# HG changeset patch # User Edouard Tisserant # Date 1633095369 -7200 # Node ID 7478d0c0dc1cb739b38e19c9974b01611d16b260 # Parent 595603d80b5b320a22e56ef49a07df768ea6bc68 SVGHMI: Update generated XSLT (PathSlider widget) diff -r 595603d80b5b -r 7478d0c0dc1c svghmi/analyse_widget.xslt --- a/svghmi/analyse_widget.xslt Fri Oct 01 15:34:04 2021 +0200 +++ b/svghmi/analyse_widget.xslt Fri Oct 01 15:36:09 2021 +0200 @@ -601,12 +601,39 @@ Value to display + + + + + + PathSlider - + + + + Slide an SVG element along a path by dragging it + + + value + + + min + + + max + + + minimum value + + + maximum value + + - ScrollBar - documentation to be written + ScrollBar - svg:rect based scrollbar diff -r 595603d80b5b -r 7478d0c0dc1c svghmi/gen_index_xhtml.xslt --- a/svghmi/gen_index_xhtml.xslt Fri Oct 01 15:34:04 2021 +0200 +++ b/svghmi/gen_index_xhtml.xslt Fri Oct 01 15:36:09 2021 +0200 @@ -1904,17 +1904,6 @@ - - - - - - - - - - - class AnimateWidget @@ -6395,12 +6384,352 @@ ], + + + + + + PathSlider - + + + + Slide an SVG element along a path by dragging it + + + value + + + min + + + max + + + minimum value + + + maximum value + + + + class + PathSliderWidget + extends Widget{ + + frequency = 10; + + position = undefined; + + min = 0; + + max = 100; + + scannedPoints = []; + + pathLength = undefined; + + precision = undefined; + + origPt = undefined; + + + + + + scanPath() { + + this.pathLength = this.path_elt.getTotalLength(); + + this.precision = Math.floor(this.pathLength / 10); + + + + // save linear scan for coarse approximation + + for (var scanLength = 0; scanLength <= this.pathLength; scanLength += this.precision) { + + this.scannedPoints.push([this.path_elt.getPointAtLength(scanLength), scanLength]); + + } + + [this.origPt,] = this.scannedPoints[0]; + + } + + + + closestPoint(point) { + + var bestPoint, + + bestLength, + + bestDistance = Infinity, + + scanDistance; + + + + // use linear scan for coarse approximation + + for (let [scanPoint, scanLength] of this.scannedPoints){ + + if ((scanDistance = distance2(scanPoint)) < bestDistance) { + + bestPoint = scanPoint, + + bestLength = scanLength, + + bestDistance = scanDistance; + + } + + } + + + + // binary search for more precise estimate + + let precision = this.precision / 2; + + while (precision > 0.5) { + + var beforePoint, + + afterPoint, + + beforeLength, + + afterLength, + + beforeDistance, + + afterDistance; + + if ((beforeLength = bestLength - precision) >= 0 && + + (beforeDistance = distance2(beforePoint = this.path_elt.getPointAtLength(beforeLength))) < bestDistance) { + + bestPoint = beforePoint, + + bestLength = beforeLength, + + bestDistance = beforeDistance; + + } else if ((afterLength = bestLength + precision) <= this.pathLength && + + (afterDistance = distance2(afterPoint = this.path_elt.getPointAtLength(afterLength))) < bestDistance) { + + bestPoint = afterPoint, + + bestLength = afterLength, + + bestDistance = afterDistance; + + } + + precision /= 2; + + } + + + + return [bestPoint, bestLength]; + + + + function distance2(p) { + + var dx = p.x - point.x, + + dy = p.y - point.y; + + return dx * dx + dy * dy; + + } + + } + + + + dispatch(value,oldval, index) { + + switch(index) { + + case 0: + + this.position = value; + + break; + + case 1: + + this.min = value; + + break; + + case 2: + + this.max = value; + + break; + + } + + + + this.request_animate(); + + } + + + + get_current_point(){ + + let currLength = this.pathLength * (this.position - this.min) / (this.max - this.min) + + return this.path_elt.getPointAtLength(currLength); + + } + + + + animate(){ + + if(this.position == undefined) + + return; + + + + let currPt = this.get_current_point(); + + this.cursor_transform.setTranslate(currPt.x - this.origPt.x, currPt.y - this.origPt.y); + + } + + + + init() { + + if(this.args.length == 2) + + [this.min, this.max]=this.args; + + + + this.scanPath(); + + + + this.cursor_transform = svg_root.createSVGTransform(); + + + + this.cursor_elt.transform.baseVal.appendItem(this.cursor_transform); + + + + this.cursor_elt.onpointerdown = (e) => this.on_cursor_down(e); + + + + this.bound_drag = this.drag.bind(this); + + this.bound_drop = this.drop.bind(this); + + } + + + + start_dragging_from_event(e){ + + let clientPoint = new DOMPoint(e.clientX, e.clientY); + + let point = clientPoint.matrixTransform(this.invctm); + + let currPt = this.get_current_point(); + + this.draggingOffset = new DOMPoint(point.x - currPt.x , point.y - currPt.y); + + } + + + + apply_position_from_event(e){ + + let clientPoint = new DOMPoint(e.clientX, e.clientY); + + let rawPoint = clientPoint.matrixTransform(this.invctm); + + let point = new DOMPoint(rawPoint.x - this.draggingOffset.x , rawPoint.y - this.draggingOffset.y); + + let [closestPoint, closestLength] = this.closestPoint(point); + + let new_position = this.min + (this.max - this.min) * closestLength / this.pathLength; + + this.position = Math.round(Math.max(Math.min(new_position, this.max), this.min)); + + this.apply_hmi_value(0, this.position); + + } + + + + on_cursor_down(e){ + + // get scrollbar -> root transform + + let ctm = this.path_elt.getCTM(); + + // root -> path transform + + this.invctm = ctm.inverse(); + + this.start_dragging_from_event(e); + + svg_root.addEventListener("pointerup", this.bound_drop, true); + + svg_root.addEventListener("pointermove", this.bound_drag, true); + + } + + + + drop(e) { + + svg_root.removeEventListener("pointerup", this.bound_drop, true); + + svg_root.removeEventListener("pointermove", this.bound_drag, true); + + } + + + + drag(e) { + + this.apply_position_from_event(e); + + } + + } + + + + + + + + cursor path + + + - ScrollBar - documentation to be written + ScrollBar - svg:rect based scrollbar @@ -6469,15 +6798,13 @@ let range = this.range; - let size = Math.max(this.range * this.mincursize, Math.min(this.size, range)); + let size = Math.max(range * this.mincursize, Math.min(this.size, range)); let maxh = this.range_elt.height.baseVal.value; let pixels = maxh; - let units = range; - - return [size, maxh, range, pixels, units]; + return [size, maxh, range, pixels]; } @@ -6489,11 +6816,11 @@ return; - let [size, maxh, range, pixels, units] = this.get_ratios(); - - - - let new_y = this.range_elt.y.baseVal.value + Math.round(Math.min(this.position,range-size) * pixels / units); + let [size, maxh, range, pixels] = this.get_ratios(); + + + + let new_y = this.range_elt.y.baseVal.value + Math.round(Math.min(this.position,range-size) * pixels / range); let new_height = Math.round(maxh * size/range); @@ -6579,7 +6906,7 @@ drag(e) { - let [size, maxh, range, pixels, units] = this.get_ratios(); + let [size, maxh, range, pixels] = this.get_ratios(); if(pixels == 0) return; @@ -6587,7 +6914,7 @@ let movement = point.matrixTransform(this.invctm).y; - this.dragpos += movement * units / pixels; + this.dragpos += movement * range / pixels; this.apply_position(this.dragpos);