svghmi/widget_xygraph.ysl2
changeset 3490 4f252e8d6759
parent 3488 6ef4ffcf9761
child 3505 a27b5862e363
equal deleted inserted replaced
3489:5335895ce526 3490:4f252e8d6759
    70                  [this.y_interval_minor_mark_elt, this.y_interval_major_mark_elt]],
    70                  [this.y_interval_minor_mark_elt, this.y_interval_major_mark_elt]],
    71                 [this.x_axis_label_elt, this.y_axis_label_elt],
    71                 [this.x_axis_label_elt, this.y_axis_label_elt],
    72                 [this.x_axis_line_elt, this.y_axis_line_elt],
    72                 [this.x_axis_line_elt, this.y_axis_line_elt],
    73                 [this.x_format, this.y_format]);
    73                 [this.x_format, this.y_format]);
    74 
    74 
       
    75             let max_stroke_width = 0;
       
    76             for(let curve of this.curves){
       
    77                 if(curve.style.strokeWidth > max_stroke_width){
       
    78                     max_stroke_width = curve.style.strokeWidth;
       
    79                 }
       
    80             }
       
    81 
       
    82             this.Margins=this.reference.getLengths().map(length => max_stroke_width/length);
       
    83 
    75             // create <clipPath> path and attach it to widget
    84             // create <clipPath> path and attach it to widget
    76             let clipPath = document.createElementNS(xmlns,"clipPath");
    85             let clipPath = document.createElementNS(xmlns,"clipPath");
    77             let clipPathPath = document.createElementNS(xmlns,"path");
    86             let clipPathPath = document.createElementNS(xmlns,"path");
    78             let clipPathPathDattr = document.createAttribute("d");
    87             let clipPathPathDattr = document.createAttribute("d");
    79             clipPathPathDattr.value = this.reference.getClipPathPathDattr();
    88             clipPathPathDattr.value = this.reference.getClipPathPathDattr();
    80             clipPathPath.setAttributeNode(clipPathPathDattr);
    89             clipPathPath.setAttributeNode(clipPathPathDattr);
    81             clipPath.appendChild(clipPathPath);
    90             clipPath.appendChild(clipPathPath);
       
    91             clipPath.id = randomId();
    82             this.element.appendChild(clipPath);
    92             this.element.appendChild(clipPath);
    83 
    93 
    84             // assign created clipPath to clip-path property of curves
    94             // assign created clipPath to clip-path property of curves
    85             for(let curve of this.curves){
    95             for(let curve of this.curves){
    86                 curve.setAttribute("clip-path", "url(#" + clipPath.id + ")");
    96                 curve.setAttribute("clip-path", "url(#" + clipPath.id + ")");
   113                     this.xmin = time;
   123                     this.xmin = time;
   114                 }
   124                 }
   115             }
   125             }
   116 
   126 
   117             this.xmax = time;
   127             this.xmax = time;
   118             let Xlength = this.xmax - this.xmin;
   128             let Xrange = this.xmax - this.xmin;
   119 
   129 
   120             if(!this.fixed_y_range){
   130             if(!this.fixed_y_range){
   121                 ymin_damaged = overflow <= this.ymin;
   131                 ymin_damaged = overflow <= this.ymin;
   122                 ymax_damaged = overflow >= this.ymax;
   132                 ymax_damaged = overflow >= this.ymax;
   123                 if(value > this.ymax){
   133                 if(value > this.ymax){
   127                 if(value < this.ymin){
   137                 if(value < this.ymin){
   128                     ymin_damaged = false;
   138                     ymin_damaged = false;
   129                     this.ymin = value;
   139                     this.ymin = value;
   130                 }
   140                 }
   131             }
   141             }
   132             let Ylength = this.ymax - this.ymin;
   142             let Yrange = this.ymax - this.ymin;
       
   143 
       
   144             let [xMargin,yMargin] = zip(this.Margins, [Xrange, Yrange]).map(([m,l]) => m*l);
       
   145             [[this.dxmin, this.dxmax],[this.dymin,this.dymax]] =
       
   146                 [[this.xmin-xMargin, this.xmax+xMargin],
       
   147                  [this.ymin-yMargin, this.ymax+yMargin]];
       
   148             Xrange += 2*xMargin;
       
   149             Yrange += 2*yMargin;
   133 
   150 
   134             // recompute curves "d" attribute
   151             // recompute curves "d" attribute
   135             // FIXME: use SVG getPathData and setPathData when available.
   152             // FIXME: use SVG getPathData and setPathData when available.
   136             //        https://svgwg.org/specs/paths/#InterfaceSVGPathData
   153             //        https://svgwg.org/specs/paths/#InterfaceSVGPathData
   137             //        https://github.com/jarek-foksa/path-data-polyfill
   154             //        https://github.com/jarek-foksa/path-data-polyfill
   139             let [base_point, xvect, yvect] = this.reference.getBaseRef();
   156             let [base_point, xvect, yvect] = this.reference.getBaseRef();
   140             this.curves_d_attr =
   157             this.curves_d_attr =
   141                 zip(this.curves_data, this.curves).map(([data,curve]) => {
   158                 zip(this.curves_data, this.curves).map(([data,curve]) => {
   142                     let new_d = data.map(([x,y], i) => {
   159                     let new_d = data.map(([x,y], i) => {
   143                         // compute curve point from data, ranges, and base_ref
   160                         // compute curve point from data, ranges, and base_ref
   144                         let xv = vectorscale(xvect, (x - this.xmin) / Xlength);
   161                         let xv = vectorscale(xvect, (x - this.dxmin) / Xrange);
   145                         let yv = vectorscale(yvect, (y - this.ymin) / Ylength);
   162                         let yv = vectorscale(yvect, (y - this.dymin) / Yrange);
   146                         let px = base_point.x + xv.x + yv.x;
   163                         let px = base_point.x + xv.x + yv.x;
   147                         let py = base_point.y + xv.y + yv.y;
   164                         let py = base_point.y + xv.y + yv.y;
   148                         if(!this.fixed_y_range){
   165                         if(!this.fixed_y_range){
   149                             if(ymin_damaged && y < this.ymin) this.ymin = y;
   166                             if(ymin_damaged && y < this.ymin) this.ymin = y;
   150                             if(ymax_damaged && y > this.ymax) this.ymax = y;
   167                             if(ymax_damaged && y > this.ymax) this.ymax = y;
   167 
   184 
   168             // move elements only if enough data
   185             // move elements only if enough data
   169             if(this.curves_data.some(data => data.length > 1)){
   186             if(this.curves_data.some(data => data.length > 1)){
   170 
   187 
   171                 // move marks and update labels
   188                 // move marks and update labels
   172                 this.reference.applyRanges([[this.xmin, this.xmax],
   189                 this.reference.applyRanges([[this.dxmin, this.dxmax],
   173                                             [this.ymin, this.ymax]]);
   190                                             [this.dymin, this.dymax]]);
   174 
   191 
   175                 // apply computed curves "d" attributes
   192                 // apply computed curves "d" attributes
   176                 for(let [curve, d_attr] of zip(this.curves, this.curves_d_attr)){
   193                 for(let [curve, d_attr] of zip(this.curves, this.curves_d_attr)){
   177                     curve.setAttribute("d", d_attr);
   194                     curve.setAttribute("d", d_attr);
   178                 }
   195                 }
   218 
   235 
   219 function vectorscale(p1, p2) {
   236 function vectorscale(p1, p2) {
   220     return new DOMPoint(p2 * p1.x , p2 * p1.y);
   237     return new DOMPoint(p2 * p1.x , p2 * p1.y);
   221 };
   238 };
   222 
   239 
       
   240 function vectorLength(p1) {
       
   241     return Math.sqrt(p1.x*p1.x + p1.y*p1.y);
       
   242 };
       
   243 
       
   244 function randomId(){
       
   245     return Date.now().toString(36) + Math.random().toString(36).substr(2);
       
   246 }
       
   247 
   223 function move_elements_to_group(elements) {
   248 function move_elements_to_group(elements) {
   224     let newgroup = document.createElementNS(xmlns,"g");
   249     let newgroup = document.createElementNS(xmlns,"g");
   225     newgroup.id = Date.now().toString(36) + Math.random().toString(36).substr(2);
   250     newgroup.id = randomId();
   226 
   251 
   227     for(let element of elements){
   252     for(let element of elements){
   228         let parent = element.parentElement;
   253         let parent = element.parentElement;
   229         if(parent !== null)
   254         if(parent !== null)
   230             parent.removeChild(element);
   255             parent.removeChild(element);
   306                  + -xvect.x + "," + -xvect.y + " "
   331                  + -xvect.x + "," + -xvect.y + " "
   307                  + -yvect.x + "," + -yvect.y + " z";
   332                  + -yvect.x + "," + -yvect.y + " z";
   308 
   333 
   309         this.base_ref = [base_point, xvect, yvect];
   334         this.base_ref = [base_point, xvect, yvect];
   310 
   335 
       
   336         this.lengths = [xvect,yvect].map(v => vectorLength(v));
       
   337 
   311         for(let axis of this.axes){
   338         for(let axis of this.axes){
   312             axis.setBasePoint(base_point);
   339             axis.setBasePoint(base_point);
   313         }
   340         }
       
   341     }
       
   342 
       
   343     getLengths(){
       
   344         return this.lengths;
   314     }
   345     }
   315 
   346 
   316 	getBaseRef(){
   347 	getBaseRef(){
   317         return this.base_ref;
   348         return this.base_ref;
   318 	}
   349 	}
   370 
   401 
   371         // add transforms for elements sliding along the axis line
   402         // add transforms for elements sliding along the axis line
   372         for(let [elementname,element] of zip(["minor", "major", "label"],[...marks,label])){
   403         for(let [elementname,element] of zip(["minor", "major", "label"],[...marks,label])){
   373             for(let name of ["base","slide"]){
   404             for(let name of ["base","slide"]){
   374                 let transform = svg_root.createSVGTransform();
   405                 let transform = svg_root.createSVGTransform();
   375                 element.transform.baseVal.appendItem(transform);
   406                 element.transform.baseVal.insertItemBefore(transform,0);
   376                 this[elementname+"_"+name+"_transform"]=transform;
   407                 this[elementname+"_"+name+"_transform"]=transform;
   377             };
   408             };
   378         };
   409         };
   379 
   410 
   380         // group marks an labels together
   411         // group marks an labels together
   393 
   424 
   394         this.marks_and_label_group_transform = svg_root.createSVGTransform();
   425         this.marks_and_label_group_transform = svg_root.createSVGTransform();
   395         this.marks_and_label_group.transform.baseVal.appendItem(this.marks_and_label_group_transform);
   426         this.marks_and_label_group.transform.baseVal.appendItem(this.marks_and_label_group_transform);
   396 
   427 
   397         this.duplicates = [];
   428         this.duplicates = [];
   398         this.last_mark_count = 0;
   429         this.last_duplicate_index = 0;
   399     }
   430     }
   400 
   431 
   401     setBasePoint(base_point){
   432     setBasePoint(base_point){
   402         // move Axis to base point
   433         // move Axis to base point
   403         let [start, _vect] = this.line;
   434         let [start, _vect] = this.line;
   443         //   Unit is pow(10, integer_number )
   474         //   Unit is pow(10, integer_number )
   444         //
   475         //
   445         // - To transform order of magnitude to an integer, floor() is used.
   476         // - To transform order of magnitude to an integer, floor() is used.
   446         //   This results in a count of mark fluctuating in between 10 and 100.
   477         //   This results in a count of mark fluctuating in between 10 and 100.
   447         //
   478         //
   448         // - To spare resources result is better in between 5 and 50,
   479         // - To spare resources result is better in between 3 and 30,
   449         //   and log10(5) is substracted to order of magnitude to obtain this
   480         //   and log10(3) is substracted to order of magnitude to obtain this
   450         //   log10(5) ~= 0.69897
   481         let unit = Math.pow(10, Math.floor(Math.log10(range)-Math.log10(3)));
   451 
       
   452 
       
   453         let unit = Math.pow(10, Math.floor(Math.log10(range)-0.69897));
       
   454 
   482 
   455         // TODO: for time values (ms), units may be :
   483         // TODO: for time values (ms), units may be :
   456         //       1       -> ms
   484         //       1       -> ms
   457         //       10      -> s/100
   485         //       10      -> s/100
   458         //       100     -> s/10
   486         //       100     -> s/10
   506         //     v xoffset|
   534         //     v xoffset|
   507         //     X<------>|
   535         //     X<------>|
   508         // base_point
   536         // base_point
   509 
   537 
   510         // move major marks and label to first positive mark position
   538         // move major marks and label to first positive mark position
   511         let v = vectorscale(unit_vect, unit);
   539         // let v = vectorscale(unit_vect, unit);
   512         this.label_slide_transform.setTranslate(v.x, v.y);
   540         // this.label_slide_transform.setTranslate(v.x, v.y);
   513         this.major_slide_transform.setTranslate(v.x, v.y);
   541         // this.major_slide_transform.setTranslate(v.x, v.y);
   514         // move minor mark to first half positive mark position
   542         // move minor mark to first half positive mark position
   515         v = vectorscale(unit_vect, unit/2);
   543         let v = vectorscale(unit_vect, unit/2);
   516         this.minor_slide_transform.setTranslate(v.x, v.y);
   544         this.minor_slide_transform.setTranslate(v.x, v.y);
   517 
   545 
   518         // duplicate marks and labels as needed
   546         // duplicate marks and labels as needed
   519         let current_mark_count = this.duplicates.length;
   547         let current_mark_count = this.duplicates.length;
   520         for(let i = current_mark_count; i <= mark_count; i++){
   548         for(let i = current_mark_count; i <= mark_count; i++){
   521             // cloneNode() label and add a svg:use of marks in a new group
   549             // cloneNode() label and add a svg:use of marks in a new group
   522             let newgroup = document.createElementNS(xmlns,"g");
   550             let newgroup = document.createElementNS(xmlns,"g");
   523             let transform = svg_root.createSVGTransform();
   551             let transform = svg_root.createSVGTransform();
   524             let newlabel = this.label.cloneNode(true);
   552             let newlabel = this.label.cloneNode(true);
   525             let newuse = document.createElementNS(xmlns,"use");
   553             let newuse = document.createElementNS(xmlns,"use");
   526             let newuseAttr = document.createAttribute("xlink:href");
   554             let newuseAttr = document.createAttribute("href");
   527             newuseAttr.value = "#"+this.marks_group.id;
   555             newuseAttr.value = "#"+this.marks_group.id;
   528             newuse.setAttributeNode(newuseAttr);
   556             newuse.setAttributeNode(newuseAttr);
   529             newgroup.transform.baseVal.appendItem(transform);
   557             newgroup.transform.baseVal.appendItem(transform);
   530             newgroup.appendChild(newlabel);
   558             newgroup.appendChild(newlabel);
   531             newgroup.appendChild(newuse);
   559             newgroup.appendChild(newuse);
   558         //        -5    -4 |
   586         //        -5    -4 |
   559         //         offset  |
   587         //         offset  |
   560         //     X<--------->|
   588         //     X<--------->|
   561         // base_point
   589         // base_point
   562 
   590 
       
   591         let duplicate_index = 0;
   563         for(let mark_index = 0; mark_index <= mark_count; mark_index++){
   592         for(let mark_index = 0; mark_index <= mark_count; mark_index++){
   564             let i = 0;
       
   565             let val = (mark_min + mark_index) * unit;
   593             let val = (mark_min + mark_index) * unit;
   566             let vec = vectorscale(unit_vect, offset + val - min);
   594             let vec = vectorscale(unit_vect, val - min);
   567             let text = this.format ? sprintf(this.format, val) : val.toString();
   595             let text = this.format ? sprintf(this.format, val) : val.toString();
   568             if(mark_index == mark_offset){
   596             if(mark_index == mark_offset){
   569                 // apply offset to original marks and label groups
   597                 // apply offset to original marks and label groups
   570                 this.marks_and_label_group_transform.setTranslate(vec.x, vec.y);
   598                 this.marks_and_label_group_transform.setTranslate(vec.x, vec.y);
   571 
   599 
   572                 // update original label text
   600                 // update original label text
   573                 this.label.getElementsByTagName("tspan")[0].textContent = text;
   601                 this.label.getElementsByTagName("tspan")[0].textContent = text;
   574             } else {
   602             } else {
   575                 let [transform,element] = this.duplicates[i++];
   603                 let [transform,element] = this.duplicates[duplicate_index++];
   576 
   604 
   577                 // apply unit vector*N to marks and label groups
   605                 // apply unit vector*N to marks and label groups
   578                 transform.setTranslate(vec.x, vec.y);
   606                 transform.setTranslate(vec.x, vec.y);
   579 
   607 
   580                 // update label text
   608                 // update label text
   581                 element.getElementsByTagName("tspan")[0].textContent = text;
   609                 element.getElementsByTagName("tspan")[0].textContent = text;
   582 
   610 
   583                 // Attach to group if not already
   611                 // Attach to group if not already
   584                 if(i >= this.last_mark_count){
   612                 if(element.parentElement == null){
   585                     this.group.appendChild(element);
   613                     this.group.appendChild(element);
   586                 }
   614                 }
   587             }
   615             }
   588         }
   616         }
   589 
   617 
       
   618         let save_duplicate_index = duplicate_index;
   590         // dettach marks and label from group if not anymore visible
   619         // dettach marks and label from group if not anymore visible
   591         for(let i = current_mark_count; i < this.last_mark_count; i++){
   620         for(;duplicate_index < this.last_duplicate_index; duplicate_index++){
   592             let [transform,element] = this.duplicates[i];
   621             let [transform,element] = this.duplicates[duplicate_index];
   593             this.group.removeChild(element);
   622             this.group.removeChild(element);
   594         }
   623         }
   595 
   624 
   596         this.last_mark_count = current_mark_count;
   625         this.last_duplicate_index = save_duplicate_index;
   597 
   626 
   598 		return vectorscale(unit_vect, offset);
   627 		return vectorscale(unit_vect, offset);
   599     }
   628     }
   600 }
   629 }
   601 ||
   630 ||