changeset 1536:c883ee76e1a7

measure plugin: first step towards info window
author hertzhaft
date Thu, 13 Oct 2016 17:33:49 +0200
parents f129468a9ab8
children dc63c24c59be
files webapp/src/main/webapp/jquery/jquery.digilib.measure.css webapp/src/main/webapp/jquery/jquery.digilib.measure.js
diffstat 2 files changed, 178 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/webapp/src/main/webapp/jquery/jquery.digilib.measure.css	Mon Oct 10 13:31:27 2016 +0200
+++ b/webapp/src/main/webapp/jquery/jquery.digilib.measure.css	Thu Oct 13 17:33:49 2016 +0200
@@ -15,6 +15,25 @@
     z-index: 20;
 }
 
+div#dl-measure-info {
+    font-size: 90%;
+    display: none;
+	border: 1px solid grey;
+	border-radius: 5px;
+	background-color:rgba(255, 255, 255, 0.3);
+    padding: 5px;
+    position: absolute;
+    z-index: 20;
+}
+
+div#dl-measure-info div.head {
+    font-weight: bold;
+}
+
+div#dl-measure-info em {
+    font-weight: bold;
+}
+
 input#dl-measure-value1 {
     font-size: 100%;
     width: 8em;
@@ -117,16 +136,19 @@
     fill: none;
 }
 
-span#dl-measure-shapecolor,
-span#dl-measure-constrcolor,
-span#dl-measure-guidecolor,
-span#dl-measure-selectedcolor {
+.dl-measure-handle {
+    stroke: blue;
+    fill: none;
+}
+
+
+span.dl-measure-color {
     display: inline-block;
-    width: 8px;
-    height: 16px;
+    width: 12px;
+    height: 12px;
     vertical-align: middle;
-    margin: 0px 0px 2px 2px;
-    border: 1px solid darkgray;
+    margin: 1px;
+    border: 1px solid black;
     background-color: darkgray;
 }
 
--- a/webapp/src/main/webapp/jquery/jquery.digilib.measure.js	Mon Oct 10 13:31:27 2016 +0200
+++ b/webapp/src/main/webapp/jquery/jquery.digilib.measure.js	Thu Oct 13 17:33:49 2016 +0200
@@ -28,8 +28,8 @@
 */
 
 /* TODO:
-    - infowindow for shapes
-        - display fractions
+    - infowindow for shapes (partially done)
+        - display fractions (1/3 etc.)
         - display angles
         - display Vitruvian intercolumnium types
     - display shapes overlay? (angles, distances?)
@@ -717,36 +717,36 @@
         // styles for shapes
         styles : {
             shape : {
-                stroke : 'red',
-                strokewidth : 1,
+                stroke : 'lightgreen',
+                'stroke-width' : 2,
                 fill : 'none'
                 },
             constr : {
                 stroke : 'cornsilk',
-                strokewidth : 1,
+                'stroke-width' : 1,
                 fill : 'none'
                 },
             guide : {
                 stroke : 'blue',
-                strokewidth : 1,
+                'stroke-width' : 1,
                 fill : 'none'
                 },
             selected : {
                 stroke : 'cyan',
-                strokewidth : 1,
+                'stroke-width' : 1,
                 fill : 'none'
                 },
             handle : {
                 stroke : 'blue',
-                strokewidth : 1,
+                'stroke-width' : 1,
                 fill : 'none',
                 hover : {
-                    fill : 'red',
+                    fill : 'yellow',
                     }
                 }
             },
         // implemented styles
-        implementedStyles : ['shape', 'constr', 'guide', 'selected'],
+        implementedStyles : ['shape', 'constr', 'guide', 'selected', 'handle'],
         // implemented measuring shape types, for select widget
         implementedShapes : ['Line', 'LineString', 'Proportion', 'Rect', 'Rectangle', 'Polygon', 'Circle', 'Ellipse', 'Oval', 'Grid'],
         // all measuring shape types
@@ -754,7 +754,7 @@
             Line :       { name : 'line',           display : 'length', },
             LineString : { name : 'linestring',     display : 'length'  },
             Proportion : { name : 'proportion',     display : 'length'  },
-            Rectangle :  { name : 'box',            display : 'area'    },
+            Rectangle :  { name : 'box',            display : 'diagonal'    },
             Rect :       { name : 'rectangle',      display : 'area'    },
             Square :     { name : 'square',         display : 'length'  },
             Polygon :    { name : 'polygon',        display : 'area'    },
@@ -804,7 +804,9 @@
         // keep original object when moving/scaling/rotating
         keepOriginal : false,
         // number of copies when drawing grids
-        gridCopies : 10
+        gridCopies : 10,
+        // info window
+        infoDiv : null
         };
 
     // debug routine
@@ -830,7 +832,8 @@
             var shape = newShape(data);
             var layer = data.measureLayer;
             $(data).trigger('createShape', shape);
-            digilib.actions.addShape(data, shape, shapeCompleted, layer);
+            digilib.actions.addShape(data, shape, shapeCompleted, layer);
+            console.debug('drawshape', shape);
             _debug_shape('action drawshape', shape);
             }
         };
@@ -879,6 +882,22 @@
     // event handler for changeShape
     var onChangeShape = function(event, shape) {
         var data = this;
+        // event handler for updating shape info
+        var select = function(event) {
+            selectShape(data, shape);
+            updateInfo(data, shape);
+            _debug_shape('onClick', shape);
+            };
+        var info = function(event) {
+            showInfoDiv(event, data, shape);
+            _debug_shape('onMouseover', shape);
+            };
+        var $elem = shape.geometry.type === 'Oval'
+            ? shape.$elem.children('path')
+            : shape.$elem;
+        console.debug('measure: onChangeShape', $elem);
+        $elem.on('mouseover.measure', info);
+        $elem.on('click.measure', select);
         updateInfo(data, shape);
         currentShape = null;
         _debug_shape('onChangeShape', shape);
@@ -886,15 +905,6 @@
 
     // event handler for renderShape
     var onRenderShape = function(event, shape) {
-        // event handler for updating shape info
-        var info = function(event) {
-            selectShape(data, shape);
-            updateInfo(data, shape);
-            _debug_shape('onClick', shape);
-            };
-        var data = this;
-        var $elem = shape.$elem;
-        $elem.on('click.measure', info);
         _debug_shape('onRenderShape', shape);
         };
 
@@ -962,47 +972,75 @@
         var val = parseFloat(widgets.value1.val());
         var fac = val / data.lastMeasuredValue;
         data.measureFactor = fac;
-        convertUnits(data);
+        updateUnits(data);
     };
 
     // convert measured value to second unit and display
     var updateMeasures = function(data, val, type) {
-        var info = data.settings.shapeInfo[type]
         var widgets = data.measureWidgets;
-        var display = info.display;
-        var u1 = parseFloat(widgets.unit1.val());
-        var u2 = parseFloat(widgets.unit2.val());
-        var ratio = u1 / u2;
-        var result = (display === 'area') // TODO: display unit²
-            ? val * ratio * ratio
-            : val * ratio;
+        var unit1 = parseFloat(widgets.unit1.val());
+        var unit2 = parseFloat(widgets.unit2.val());
+        var ratio = unit1 / unit2;
+        var result = scaleValue(data, type, val, ratio);
         widgets.shape.val(type);
         widgets.value1.val(fn.cropFloatStr(mRound(val)));
         widgets.value2.text(fn.cropFloatStr(mRound(result)));
         };
 
-    // convert measured pixel values to new units
-    var convertUnits = function(data) {
-        var type = getActiveShapeType(data);
+    // scale
+    var scaleValue = function(data, type, val, factor) {
+        var scaleArea = data.settings.shapeInfo[type].display === 'area';
+        var result = scaleArea
+            ? val * factor * factor
+            : val * factor;
+        return result;
+        };
+
+    // convert pixel values to other units
+    var pixelToUnit = function(data, type, px) {
+        var ratio = data.measureFactor;
+        var result = scaleValue(data, type, px, ratio);
+        return result;
+        };
+
+    // rectify pixel values according to digilib aspect ratio
+    var rectifiedPixel = function(data, shape) {
+        var type = shape.geometry.type;
         var display = data.settings.shapeInfo[type].display;
-        var val = data.lastMeasuredValue;
-        var fac = data.measureFactor;
-        var result = (display === 'area')
-            ? val * fac * fac
-            : val * fac;
+        var px = (display === 'area')
+            ? rectifiedArea(data, shape)
+            : rectifiedDist(data, shape);
+        return px;
+        };
+
+    // update last measured pixel values, display as converted to new units
+    var updateUnits = function(data) {
+        var type = getActiveShapeType(data);
+        var px = data.lastMeasuredValue;
+        var result = pixelToUnit(data, type, px);
         updateMeasures(data, result, type);
         };
 
     // display info for shape
     var updateInfo = function(data, shape) {
+        data.lastMeasuredValue = rectifiedPixel(data, shape);
+        setActiveShapeType(data, shape);
+        updateUnits(data);
+        };
+
+    // info data for shape
+    var getInfo = function(data, shape) {
+        var s = data.settings;
         var type = shape.geometry.type;
-        var display = data.settings.shapeInfo[type].display;
-        var val = (display === 'area')
+        var display = s.shapeInfo[type].display;
+        var name = s.shapeInfo[type].name;
+        var px = (display === 'area')
             ? rectifiedArea(data, shape)
             : rectifiedDist(data, shape);
-        data.lastMeasuredValue = val;
-        setActiveShapeType(data, type);
-        convertUnits(data);
+        var len = fn.cropFloat(pixelToUnit(data, type, px), 2);
+        var unit = data.measureWidgets.unit1.find('option:selected').text();
+        var html = '<div class="head">'+name+'</div><div><em>'+display+'</em>: '+len+' '+unit+'</div>';
+        return html;
         };
 
     // select/unselect shape (or toggle)
@@ -1101,26 +1139,28 @@
         };
 
     // set the current shape type (from shape select widget)
-    var changeShapeType = function(data) {
+    var changeActiveShapeType = function(data) {
         data.settings.activeShapeType = data.measureWidgets.shape.val();
         setCalibrationInputState(data);
         };
 
     // set the current shape type
-    var setActiveShapeType = function(data, type) {
-        data.settings.activeShapeType = type;
+    var setActiveShapeType = function(data, shape) {
+        data.settings.activeShapeType = shape.geometry.type;
         setCalibrationInputState(data);
         };
 
     // update Line Style classes (overwrite CSS)
     var updateLineStyles = function(data) {
         var s = data.settings;
+        var DL = s.cssPrefix;
         var $lineStyles = s.$lineStyles;
         var style = s.styles;
         $lineStyles.html(
             '.'+CSS+'guide {stroke: '+style.guide.stroke+'} '+
             '.'+CSS+'constr {stroke: '+style.constr.stroke+'} '+
-            '.'+CSS+'selected {stroke: '+style.selected.stroke+'}'
+            '.'+CSS+'selected {stroke: '+style.selected.stroke+'} '+
+            'div.'+DL+'digilib .'+DL+'svg-handle {stroke: '+style.handle.stroke+'}'
             );
         var widget = data.measureWidgets;
         var styleName = s.implementedStyles;
@@ -1201,6 +1241,35 @@
         return false;
         };
 
+    var createInfoDiv = function() {
+        var options = { id: CSS+'info', class: 'dl-keep '+CSS+'info' };
+        return $('<div>', options);
+        };
+
+    // show shape info
+    var showInfoDiv = function(event, data, shape) {
+        var settings = data.settings;
+        var $info = settings.infoDiv;
+        $info.offset({
+            left : event.pageX,
+            top  : event.pageY
+            });
+        var timer;
+        $info.html(getInfo(data, shape));
+        console.debug('Info', shape);
+        $info.on('mouseout', function() { timer = setTimeout(hideInfoDiv, 300) });
+        $info.on('mouseover', function() { clearTimeout(timer) });
+        $info.fadeIn();
+        return false;
+        };
+
+    // hide shape info
+    var hideInfoDiv = function() {
+        var $info = $('#'+CSS+'info');
+        $info.off('mouseout').off('mouseover');
+        $info.fadeOut();
+        };
+
     // remove selected shapes - or the most recent one, if none was selected
     var removeSelectedShapes = function(data) {
         var layer = data.measureLayer;
@@ -1319,19 +1388,22 @@
                 },
                 'svg' : function (shape) {
                     var trafo = data.imgTrafo;
+                    var styles = data.settings.styles;
+                    var props = shape.properties;
+                    props['stroke-width'] = styles.guide['stroke-width']; // draw a rectangle in guides style
                     var $s = factory['Rect'].svg(shape);
-                    var props = shape.properties;
                     var place = $s.place;
                     var guide = CSS+'guide';
+                    var shapeClass = CSS+'shape';
                     var constr = CSS+'constr';
                     $s.attr({'class' : guide});
                     var $g = $(fn.svgElement('g', {'id': shape.id + '-oval'}));
                     var $c1 = $(fn.svgElement('circle', {'id': shape.id + '-circ1', 'class': guide }));
                     var $c2 = $(fn.svgElement('circle', {'id': shape.id + '-circ2', 'class': guide }));
                     var $p1 = $(fn.svgElement('path',   {'id': shape.id + '-lines', 'class': guide }));
-                    var $p2 = $(fn.svgElement('path',   {'id': shape.id + '-arc', stroke: props.stroke, fill: 'none' }));
-                    var $p3 = $(fn.svgElement('path',   {'id': shape.id + '-constr', 'class': constr })); // debug construction
-                    $g.append($s).append($c1).append($c2).append($p1).append($p2).append($p3);
+                    var $p2 = $(fn.svgElement('path',   {'id': shape.id + '-constr', 'class': constr })); // debug construction
+                    var $arc = $(fn.svgElement('path',   {'id': shape.id + '-arc', 'class': shapeClass, stroke: props.stroke, 'stroke-width': styles.shape['stroke-width'], fill: 'none' }));
+                    $g.append($s).append($c1).append($c2).append($p1).append($p2).append($arc);
                     $g.place = function () {
                         var p = props.screenpos;
                         place.call($s); // place the framing rectangle (polygon)
@@ -1344,24 +1416,24 @@
                             var mid1 = side1.mid();
                             var mid2 = side2.mid();
                             var mid3 = side3.mid();
-                            var axis0 = side0.parallel(mid3); // short axis
-                            var axis1 = side1.parallel(mid0); // long axis
-                            var maxDiam = axis0.length()-1; // maximal diameter for small circles
-                            var handle = axis1.perpendicularPoint(p[3]); // drag point projected on long axis
-                            if (handle.distance(mid0) > axis1.length()) { // constrain handle
+                            var axis1 = side0.parallel(mid3); // short axis
+                            var axis2 = side1.parallel(mid0); // long axis
+                            var maxDiam = axis1.length()-1; // maximal diameter for small circles
+                            var handle = axis2.perpendicularPoint(p[3]); // drag point projected on long axis
+                            if (handle.distance(mid0) > axis2.length()) { // constrain handle
                                 handle.moveTo(mid2);
                             } else if (handle.distance(mid2) > maxDiam) {
                                 handle.moveTo(geom.line(mid2, handle).length(maxDiam).point());
                                 }
                             var m1 = handle.mid(mid2); // midpoints of the small circles
-                            var m2 = axis0.mirror(m1);
+                            var m2 = axis1.mirror(m1);
                             var rad1 = m1.distance(mid2); // radius of the small circles
-                            var rd = axis0.copy().length(rad1).point(); // radius distance from short axis
+                            var rd = axis1.copy().length(rad1).point(); // radius distance from short axis
                             var ld = geom.line(rd, m1);
                             var md = rd.mid(m1);
                             var bi = ld.perpendicular(md); // perpendicular bisector
-                            var m3 = axis0.intersection(bi); // midpoints of the big circles
-                            var m4 = axis1.mirror(m3);
+                            var m3 = axis1.intersection(bi); // midpoints of the big circles
+                            var m4 = axis2.mirror(m3);
                             var fp1 = geom.line(m3, m1).addEnd(rad1); // the four fitting points
                             var fp2 = geom.line(m3, m2).addEnd(rad1);
                             var fp3 = geom.line(m4, m1).addEnd(rad1);
@@ -1370,19 +1442,20 @@
                             // place the SVG shapes
                             $c1.attr({cx: m1.x, cy: m1.y, r: rad1});
                             $c2.attr({cx: m2.x, cy: m2.y, r: rad1});
-                            $p1.attr({d: // the guides
+                            $p1.attr({d: // the guidelines
                                 'M'+fp1+' L'+m3+' '+fp2+
                                 'M'+fp3+' L'+m4+' '+fp4+
                                 'M'+mid0+' L'+mid2+
                                 'M'+mid1+' L'+mid3});
-                            $p2.attr({d: 'M'+fp2+ // the arcs
+                            $p2.attr({d: 'M'+rd+' L'+m1+' M'+md+' '+m3}); // debug construction
+                            $arc.attr({d: 'M'+fp2+ // the arcs of the oval
                                 ' A'+rad2+','+rad2+' 0 0,1 '+fp1+
                                 ' A'+rad1+','+rad1+' 0 0,1 '+fp3+
                                 ' A'+rad2+','+rad2+' 0 0,1 '+fp4+
                                 ' A'+rad1+','+rad1+' 0 0,1 '+fp2});
-                            $p3.attr({d: 'M'+rd+' L'+m1+' M'+md+' '+m3}); // debug construction
                             p[3] = handle;
                             shape.geometry.coordinates[3] = trafo.invtransform(handle).toArray();
+                            props.measures = { rad1: rad1, rad2: rad2, axis1: axis1.length(), axis2: axis2.length() }; // use for info
                             }
                         };
                     return $g;
@@ -1456,7 +1529,7 @@
                 'type',
                 'value1', 'unit1', 'eq',
                 'value2', 'unit2',
-                'shapecolor', 'guidecolor', 'constrcolor', 'selectedcolor',
+                'shapecolor', 'guidecolor', 'constrcolor', 'selectedcolor', 'handlecolor',
                 'move'
                 ],
             info :       $('<img id="dl-measure-info" src="img/info.png" title="display info window for shapes"></img>'),
@@ -1470,10 +1543,11 @@
 			unit1 :      $('<select id="dl-measure-unit1" title="current measuring unit - click to change" />'),
 			unit2 :      $('<select id="dl-measure-unit2" title="secondary measuring unit - click to change" />'),
 			angle :      $('<span id="dl-measure-angle" class="dl-measure-number" title="last measured angle" />'),
-            shapecolor : $('<span id="dl-measure-shapecolor" title="select line color for shapes"></span>'),
-            guidecolor : $('<span id="dl-measure-guidecolor" title="select guide line color for shapes"></span>'),
-            constrcolor :$('<span id="dl-measure-constrcolor" title="select construction line color for shapes"></span>'),
-            selectedcolor :$('<span id="dl-measure-selectedcolor" title="select line color for selected shapes"></span>'),
+            shapecolor : $('<span id="dl-measure-shapecolor" class="dl-measure-color" title="select line color for shapes"></span>'),
+            guidecolor : $('<span id="dl-measure-guidecolor" class="dl-measure-color" title="select guide line color for shapes"></span>'),
+            constrcolor :$('<span id="dl-measure-constrcolor" class="dl-measure-color" title="select construction line color for shapes"></span>'),
+            selectedcolor :$('<span id="dl-measure-selectedcolor" class="dl-measure-color" title="select line color for selected shapes"></span>'),
+            handlecolor :$('<span id="dl-measure-handlecolor" class="dl-measure-color" title="select color for shape handles"></span>'),
             move :       $('<img id="dl-measure-move" src="img/move.png" title="move measuring bar around the screen"></img>')
 		    };
         var $measureBar = $('<div id="dl-measure-toolbar" />');
@@ -1512,10 +1586,10 @@
             $elem.digilib(action);
             return false;
             });
-        widgets.shape.on('change.measure',  function(evt) { changeShapeType(data) });
+        widgets.shape.on('change.measure',  function(evt) { changeActiveShapeType(data) });
         widgets.value1.on('change.measure', function(evt) { changeFactor(data) });
-        widgets.unit1.on('change.measure',  function(evt) { convertUnits(data) });
-        widgets.unit2.on('change.measure',  function(evt) { convertUnits(data) });
+        widgets.unit1.on('change.measure',  function(evt) { updateUnits(data) });
+        widgets.unit2.on('change.measure',  function(evt) { updateUnits(data) });
         widgets.unit1.attr('tabindex', -1);
         widgets.unit2.attr('tabindex', -1);
         widgets.value1.attr('tabindex', -1);
@@ -1583,6 +1657,8 @@
         $data.on('changeShape', onChangeShape);
         $data.on('positionShape', onPositionShape);
         $data.on('dragShape', onDragShape);
+        settings.infoDiv = createInfoDiv();
+        data.$elem.append(settings.infoDiv);
         };
 
     // plugin object with name and init