svghmi/widget_xygraph.ysl2
changeset 3490 4f252e8d6759
parent 3488 6ef4ffcf9761
child 3505 a27b5862e363
--- a/svghmi/widget_xygraph.ysl2	Thu May 26 12:11:42 2022 +0200
+++ b/svghmi/widget_xygraph.ysl2	Mon May 30 12:44:04 2022 +0200
@@ -72,6 +72,15 @@
                 [this.x_axis_line_elt, this.y_axis_line_elt],
                 [this.x_format, this.y_format]);
 
+            let max_stroke_width = 0;
+            for(let curve of this.curves){
+                if(curve.style.strokeWidth > max_stroke_width){
+                    max_stroke_width = curve.style.strokeWidth;
+                }
+            }
+
+            this.Margins=this.reference.getLengths().map(length => max_stroke_width/length);
+
             // create <clipPath> path and attach it to widget
             let clipPath = document.createElementNS(xmlns,"clipPath");
             let clipPathPath = document.createElementNS(xmlns,"path");
@@ -79,6 +88,7 @@
             clipPathPathDattr.value = this.reference.getClipPathPathDattr();
             clipPathPath.setAttributeNode(clipPathPathDattr);
             clipPath.appendChild(clipPathPath);
+            clipPath.id = randomId();
             this.element.appendChild(clipPath);
 
             // assign created clipPath to clip-path property of curves
@@ -115,7 +125,7 @@
             }
 
             this.xmax = time;
-            let Xlength = this.xmax - this.xmin;
+            let Xrange = this.xmax - this.xmin;
 
             if(!this.fixed_y_range){
                 ymin_damaged = overflow <= this.ymin;
@@ -129,7 +139,14 @@
                     this.ymin = value;
                 }
             }
-            let Ylength = this.ymax - this.ymin;
+            let Yrange = this.ymax - this.ymin;
+
+            let [xMargin,yMargin] = zip(this.Margins, [Xrange, Yrange]).map(([m,l]) => m*l);
+            [[this.dxmin, this.dxmax],[this.dymin,this.dymax]] =
+                [[this.xmin-xMargin, this.xmax+xMargin],
+                 [this.ymin-yMargin, this.ymax+yMargin]];
+            Xrange += 2*xMargin;
+            Yrange += 2*yMargin;
 
             // recompute curves "d" attribute
             // FIXME: use SVG getPathData and setPathData when available.
@@ -141,8 +158,8 @@
                 zip(this.curves_data, this.curves).map(([data,curve]) => {
                     let new_d = data.map(([x,y], i) => {
                         // compute curve point from data, ranges, and base_ref
-                        let xv = vectorscale(xvect, (x - this.xmin) / Xlength);
-                        let yv = vectorscale(yvect, (y - this.ymin) / Ylength);
+                        let xv = vectorscale(xvect, (x - this.dxmin) / Xrange);
+                        let yv = vectorscale(yvect, (y - this.dymin) / Yrange);
                         let px = base_point.x + xv.x + yv.x;
                         let py = base_point.y + xv.y + yv.y;
                         if(!this.fixed_y_range){
@@ -169,8 +186,8 @@
             if(this.curves_data.some(data => data.length > 1)){
 
                 // move marks and update labels
-                this.reference.applyRanges([[this.xmin, this.xmax],
-                                            [this.ymin, this.ymax]]);
+                this.reference.applyRanges([[this.dxmin, this.dxmax],
+                                            [this.dymin, this.dymax]]);
 
                 // apply computed curves "d" attributes
                 for(let [curve, d_attr] of zip(this.curves, this.curves_d_attr)){
@@ -220,9 +237,17 @@
     return new DOMPoint(p2 * p1.x , p2 * p1.y);
 };
 
+function vectorLength(p1) {
+    return Math.sqrt(p1.x*p1.x + p1.y*p1.y);
+};
+
+function randomId(){
+    return Date.now().toString(36) + Math.random().toString(36).substr(2);
+}
+
 function move_elements_to_group(elements) {
     let newgroup = document.createElementNS(xmlns,"g");
-    newgroup.id = Date.now().toString(36) + Math.random().toString(36).substr(2);
+    newgroup.id = randomId();
 
     for(let element of elements){
         let parent = element.parentElement;
@@ -308,11 +333,17 @@
 
         this.base_ref = [base_point, xvect, yvect];
 
+        this.lengths = [xvect,yvect].map(v => vectorLength(v));
+
         for(let axis of this.axes){
             axis.setBasePoint(base_point);
         }
     }
 
+    getLengths(){
+        return this.lengths;
+    }
+
 	getBaseRef(){
         return this.base_ref;
 	}
@@ -372,7 +403,7 @@
         for(let [elementname,element] of zip(["minor", "major", "label"],[...marks,label])){
             for(let name of ["base","slide"]){
                 let transform = svg_root.createSVGTransform();
-                element.transform.baseVal.appendItem(transform);
+                element.transform.baseVal.insertItemBefore(transform,0);
                 this[elementname+"_"+name+"_transform"]=transform;
             };
         };
@@ -395,7 +426,7 @@
         this.marks_and_label_group.transform.baseVal.appendItem(this.marks_and_label_group_transform);
 
         this.duplicates = [];
-        this.last_mark_count = 0;
+        this.last_duplicate_index = 0;
     }
 
     setBasePoint(base_point){
@@ -445,12 +476,9 @@
         // - To transform order of magnitude to an integer, floor() is used.
         //   This results in a count of mark fluctuating in between 10 and 100.
         //
-        // - To spare resources result is better in between 5 and 50,
-        //   and log10(5) is substracted to order of magnitude to obtain this
-        //   log10(5) ~= 0.69897
-
-
-        let unit = Math.pow(10, Math.floor(Math.log10(range)-0.69897));
+        // - To spare resources result is better in between 3 and 30,
+        //   and log10(3) is substracted to order of magnitude to obtain this
+        let unit = Math.pow(10, Math.floor(Math.log10(range)-Math.log10(3)));
 
         // TODO: for time values (ms), units may be :
         //       1       -> ms
@@ -508,11 +536,11 @@
         // base_point
 
         // move major marks and label to first positive mark position
-        let v = vectorscale(unit_vect, unit);
-        this.label_slide_transform.setTranslate(v.x, v.y);
-        this.major_slide_transform.setTranslate(v.x, v.y);
+        // let v = vectorscale(unit_vect, unit);
+        // this.label_slide_transform.setTranslate(v.x, v.y);
+        // this.major_slide_transform.setTranslate(v.x, v.y);
         // move minor mark to first half positive mark position
-        v = vectorscale(unit_vect, unit/2);
+        let v = vectorscale(unit_vect, unit/2);
         this.minor_slide_transform.setTranslate(v.x, v.y);
 
         // duplicate marks and labels as needed
@@ -523,7 +551,7 @@
             let transform = svg_root.createSVGTransform();
             let newlabel = this.label.cloneNode(true);
             let newuse = document.createElementNS(xmlns,"use");
-            let newuseAttr = document.createAttribute("xlink:href");
+            let newuseAttr = document.createAttribute("href");
             newuseAttr.value = "#"+this.marks_group.id;
             newuse.setAttributeNode(newuseAttr);
             newgroup.transform.baseVal.appendItem(transform);
@@ -560,10 +588,10 @@
         //     X<--------->|
         // base_point
 
+        let duplicate_index = 0;
         for(let mark_index = 0; mark_index <= mark_count; mark_index++){
-            let i = 0;
             let val = (mark_min + mark_index) * unit;
-            let vec = vectorscale(unit_vect, offset + val - min);
+            let vec = vectorscale(unit_vect, val - min);
             let text = this.format ? sprintf(this.format, val) : val.toString();
             if(mark_index == mark_offset){
                 // apply offset to original marks and label groups
@@ -572,7 +600,7 @@
                 // update original label text
                 this.label.getElementsByTagName("tspan")[0].textContent = text;
             } else {
-                let [transform,element] = this.duplicates[i++];
+                let [transform,element] = this.duplicates[duplicate_index++];
 
                 // apply unit vector*N to marks and label groups
                 transform.setTranslate(vec.x, vec.y);
@@ -581,19 +609,20 @@
                 element.getElementsByTagName("tspan")[0].textContent = text;
 
                 // Attach to group if not already
-                if(i >= this.last_mark_count){
+                if(element.parentElement == null){
                     this.group.appendChild(element);
                 }
             }
         }
 
+        let save_duplicate_index = duplicate_index;
         // dettach marks and label from group if not anymore visible
-        for(let i = current_mark_count; i < this.last_mark_count; i++){
-            let [transform,element] = this.duplicates[i];
+        for(;duplicate_index < this.last_duplicate_index; duplicate_index++){
+            let [transform,element] = this.duplicates[duplicate_index];
             this.group.removeChild(element);
         }
 
-        this.last_mark_count = current_mark_count;
+        this.last_duplicate_index = save_duplicate_index;
 
 		return vectorscale(unit_vect, offset);
     }