diff -r 3930916a2e0d -r 18d743e517b5 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);