64 |
64 |
65 this.curves = []; |
65 this.curves = []; |
66 this.init_specific(); |
66 this.init_specific(); |
67 |
67 |
68 this.reference = new ReferenceFrame( |
68 this.reference = new ReferenceFrame( |
69 [[this.x_interval_minor_mark, this.x_interval_major_mark], |
69 [[this.x_interval_minor_mark_elt, this.x_interval_major_mark_elt], |
70 [this.y_interval_minor_mark, this.y_interval_major_mark]], |
70 [this.y_interval_minor_mark_elt, this.y_interval_major_mark_elt]], |
71 [this.y_axis_label, this.x_axis_label], |
71 [this.x_axis_label_elt, this.y_axis_label_elt], |
72 [this.x_axis_line, this.y_axis_line], |
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 // create <clipPath> path and attach it to widget |
75 // create <clipPath> path and attach it to widget |
76 clipPath = document.createElementNS(xmlns,"clipPath"); |
76 let clipPath = document.createElementNS(xmlns,"clipPath"); |
77 clipPathPath = document.createElementNS(xmlns,"path"); |
77 let clipPathPath = document.createElementNS(xmlns,"path"); |
78 clipPathPathDattr = document.createAttribute("d"); |
78 let clipPathPathDattr = document.createAttribute("d"); |
79 clipPathPathDattr.value = this.reference.getClipPathPathDattr(); |
79 clipPathPathDattr.value = this.reference.getClipPathPathDattr(); |
80 clipPathPath.setAttributeNode(clipPathPathDattr); |
80 clipPathPath.setAttributeNode(clipPathPathDattr); |
81 clipPath.appendChild(clipPathPath); |
81 clipPath.appendChild(clipPathPath); |
82 this.element.appendChild(clipPath); |
82 this.element.appendChild(clipPath); |
83 |
83 |
84 // assign created clipPath to clip-path property of curves |
84 // assign created clipPath to clip-path property of curves |
85 for(let curve of this.curves){ |
85 for(let curve of this.curves){ |
86 curve.setAttribute("clip-path", "url(#" + clipPath.id + ")"); |
86 curve.setAttribute("clip-path", "url(#" + clipPath.id + ")"); |
87 } |
87 } |
88 |
88 |
89 this.curves_data = []; |
89 this.curves_data = this.curves.map(_unused => []); |
90 this.max_data_length = this.args[0]; |
90 this.max_data_length = this.args[0]; |
91 } |
91 } |
92 |
92 |
93 dispatch(value,oldval, index) { |
93 dispatch(value,oldval, index) { |
94 // naive local buffer impl. |
94 // naive local buffer impl. |
132 |
133 |
133 // recompute curves "d" attribute |
134 // recompute curves "d" attribute |
134 // FIXME: use SVG getPathData and setPathData when available. |
135 // FIXME: use SVG getPathData and setPathData when available. |
135 // https://svgwg.org/specs/paths/#InterfaceSVGPathData |
136 // https://svgwg.org/specs/paths/#InterfaceSVGPathData |
136 // https://github.com/jarek-foksa/path-data-polyfill |
137 // https://github.com/jarek-foksa/path-data-polyfill |
137 |
138 |
138 let [base_point, xvect, yvect] = this.reference.getBaseRef(); |
139 let [base_point, xvect, yvect] = this.reference.getBaseRef(); |
139 this.curves_d_attr = |
140 this.curves_d_attr = |
140 zip(this.curves_data, this.curves).map(function([data,curve]){ |
141 zip(this.curves_data, this.curves).map(([data,curve]) => { |
141 let new_d = data.map(function([y, i]){ |
142 let new_d = data.map((y, i) => { |
142 // compute curve point from data, ranges, and base_ref |
143 // compute curve point from data, ranges, and base_ref |
143 let x = Xmin + i * Xlength / data_length; |
144 let x = this.xmin + i * Xlength / data_length; |
144 let xv = vectorscale(xvect, (x - Xmin) / Xlength); |
145 let xv = vectorscale(xvect, (x - this.xmin) / Xlength); |
145 let yv = vectorscale(yvect, (y - Ymin) / Ylength); |
146 let yv = vectorscale(yvect, (y - this.ymin) / Ylength); |
146 let px = base_point.x + xv.x + yv.x; |
147 let px = base_point.x + xv.x + yv.x; |
147 let py = base_point.y + xv.y + yv.y; |
148 let py = base_point.y + xv.y + yv.y; |
148 if(!this.fixed_y_range){ |
149 if(!this.fixed_y_range){ |
149 if(ymin_damaged && y > this.ymin) this.ymin = y; |
150 if(ymin_damaged && y < this.ymin) this.ymin = y; |
150 if(xmin_damaged && x > this.xmin) this.xmin = x; |
151 if(ymax_damaged && y > this.ymax) this.ymax = y; |
151 } |
152 } |
152 |
153 |
153 return " " + px + "," + py; |
154 return " " + px + "," + py; |
|
155 }); |
|
156 |
|
157 new_d.unshift("M "); |
|
158 |
|
159 return new_d.join(''); |
154 }); |
160 }); |
155 |
161 |
156 new_d.unshift("M "); |
|
157 new_d.push(" z"); |
|
158 |
|
159 return new_d.join(''); |
|
160 } |
|
161 |
|
162 // computed curves "d" attr is applied to svg curve during animate(); |
162 // computed curves "d" attr is applied to svg curve during animate(); |
163 |
163 |
164 this.request_animate(); |
164 this.request_animate(); |
165 } |
165 } |
166 |
166 |
167 animate(){ |
167 animate(){ |
168 |
168 |
169 // move marks and update labels |
169 // move elements only if enough data |
170 this.reference.applyRanges([this.XRange, this.YRange]); |
170 if(this.curves_data.some(data => data.length > 1)){ |
171 |
171 |
172 // apply computed curves "d" attributes |
172 // move marks and update labels |
173 for(let [curve, d_attr] of zip(this.curves, this.curves_d_attr)){ |
173 this.reference.applyRanges([[this.xmin, this.xmax], |
174 curve.setAttribute("d", d_attr); |
174 [this.ymin, this.ymax]]); |
|
175 |
|
176 // apply computed curves "d" attributes |
|
177 for(let [curve, d_attr] of zip(this.curves, this.curves_d_attr)){ |
|
178 curve.setAttribute("d", d_attr); |
|
179 } |
175 } |
180 } |
176 } |
181 } |
177 |
182 |
178 || |
183 || |
179 } |
184 } |
180 |
185 |
181 widget_defs("XYGraph") { |
186 widget_defs("XYGraph") { |
182 labels("/x_interval_minor_mark /x_axis_line /x_interval_major_mark /x_axis_label") |
187 labels("/x_interval_minor_mark /x_axis_line /x_interval_major_mark /x_axis_label"); |
183 labels("/y_interval_minor_mark /y_axis_line /y_interval_major_mark /y_axis_label") |
188 labels("/y_interval_minor_mark /y_axis_line /y_interval_major_mark /y_axis_label"); |
184 |
189 |
185 | init_specific() { |
190 | init_specific() { |
186 |
191 |
187 // collect all curve_n labelled children |
192 // collect all curve_n labelled children |
188 foreach "$hmi_element/*[regexp:test(@inkscape:label,'^curve_[0-9]+$')]" { |
193 foreach "$hmi_element/*[regexp:test(@inkscape:label,'^curve_[0-9]+$')]" { |
217 }; |
222 }; |
218 |
223 |
219 function move_elements_to_group(elements) { |
224 function move_elements_to_group(elements) { |
220 let newgroup = document.createElementNS(xmlns,"g"); |
225 let newgroup = document.createElementNS(xmlns,"g"); |
221 for(let element of elements){ |
226 for(let element of elements){ |
222 element.parentElement().removeChild(element); |
227 let parent = element.parentElement; |
|
228 if(parent !== null) |
|
229 parent.removeChild(element); |
223 newgroup.appendChild(element); |
230 newgroup.appendChild(element); |
224 } |
231 } |
225 return newgroup; |
232 return newgroup; |
226 } |
233 } |
227 function getLinesIntesection(l1, l2) { |
234 function getLinesIntesection(l1, l2) { |
|
235 let [l1start, l1vect] = l1; |
|
236 let [l2start, l2vect] = l2; |
|
237 |
|
238 let b; |
228 /* |
239 /* |
229 Compute intersection of two lines |
240 Compute intersection of two lines |
230 ================================= |
241 ================================= |
231 |
242 |
232 ^ l2vect |
243 ^ l2vect |
241 |
252 |
242 intersection = l1start + l1vect * a |
253 intersection = l1start + l1vect * a |
243 intersection = l2start + l2vect * b |
254 intersection = l2start + l2vect * b |
244 ==> solve : "l1start + l1vect * a = l2start + l2vect * b" to find a and b and then intersection |
255 ==> solve : "l1start + l1vect * a = l2start + l2vect * b" to find a and b and then intersection |
245 |
256 |
246 (1) l1start.x + l1vect.x * a = l2start.x + l2vect.x * b |
257 (1) l1start.y + l1vect.y * a = l2start.y + l2vect.y * b |
247 (2) l1start.y + l1vect.y * a = l2start.y + l2vect.y * b |
258 (2) l1start.x + l1vect.x * a = l2start.x + l2vect.x * b |
248 |
259 |
249 // express a |
260 // express a |
250 (1) a = (l2start.x + l2vect.x * b) / (l1start.x + l1vect.x) |
261 (1) a = (l2start.y + l2vect.y * b - l1start.y) / l1vect.y |
251 |
262 |
252 // substitute a to have only b |
263 // substitute a to have only b |
253 (1+2) l1start.y + l1vect.y * (l2start.x + l2vect.x * b) / (l1start.x + l1vect.x) = l2start.y + l2vect.y * b |
264 (1+2) l1start.x + l1vect.x * (l2start.y + l2vect.y * b - l1start.y) / l1vect.y = l2start.x + l2vect.x * b |
254 |
265 |
255 // expand to isolate b |
266 // expand to isolate b |
256 (2) l1start.y + (l1vect.y * l2start.x) / (l1start.x + l1vect.x) + (l1vect.y * l2vect.x * b) / (l1start.x + l1vect.x) = l2start.y + l2vect.y * b |
267 l1start.x + l1vect.x * l2start.y / l1vect.y + l2vect.y / l1vect.y * b - l1start.y / l1vect.y = l2start.x + l2vect.x * b |
257 (2) l1start.y + (l1vect.y * l2start.x) / (l1start.x + l1vect.x) - l2start.y = l2vect.y * b - (l1vect.y * l2vect.x * b) / (l1start.x + l1vect.x) |
|
258 |
268 |
259 // factorize b |
269 // factorize b |
260 (2) l1start.y + (l1vect.y * l2start.x) / (l1start.x + l1vect.x) - l2start.y = b * (l2vect.y - (l1vect.y * l2vect.x ) / (l1start.x + l1vect.x)) |
270 l1start.x + l1vect.x * l2start.y / l1vect.y - l1start.y / l1vect.y - l2start.x = b * ( l2vect.x - l2vect.y / l1vect.y) |
261 |
271 |
262 // extract b |
272 // extract b |
263 (2) b = (l1start.y + (l1vect.y * l2start.x) / (l1start.x + l1vect.x) - l2start.y) / ((l2vect.y - (l1vect.y * l2vect.x ) / (l1start.x + l1vect.x))) |
273 b = (l1start.x + l1vect.x * l2start.y / l1vect.y - l1start.y / l1vect.y - l2start.x) / ( l2vect.x - l2vect.y / l1vect.y) |
264 */ |
274 */ |
265 |
275 |
266 let [l1start, l1vect] = l1; |
276 /* avoid divison by zero by swapping (1) and (2) */ |
267 let [l2start, l2vect] = l2; |
277 if(l1vect.y == 0 ){ |
268 |
278 b = (l1start.y + l1vect.y * l2start.x / l1vect.x - l1start.x / l1vect.x - l2start.y) / ( l2vect.y - l2vect.x / l1vect.x); |
269 let b = (l1start.y + (l1vect.y * l2start.x) / (l1start.x + l1vect.x) - l2start.y) / ((l2vect.y - (l1vect.y * l2vect.x ) / (l1start.x + l1vect.x))); |
279 }else{ |
|
280 b = (l1start.x + l1vect.x * l2start.y / l1vect.y - l1start.y / l1vect.y - l2start.x) / ( l2vect.x - l2vect.y / l1vect.y); |
|
281 } |
270 |
282 |
271 return new DOMPoint(l2start.x + l2vect.x * b, l2start.y + l2vect.y * b); |
283 return new DOMPoint(l2start.x + l2vect.x * b, l2start.y + l2vect.y * b); |
272 }; |
284 }; |
273 |
|
274 |
|
275 // From https://stackoverflow.com/a/48293566 |
|
276 function *zip (...iterables){ |
|
277 let iterators = iterables.map(i => i[Symbol.iterator]() ) |
|
278 while (true) { |
|
279 let results = iterators.map(iter => iter.next() ) |
|
280 if (results.some(res => res.done) ) return |
|
281 else yield results.map(res => res.value ) |
|
282 } |
|
283 } |
|
284 |
285 |
285 class ReferenceFrame { |
286 class ReferenceFrame { |
286 constructor( |
287 constructor( |
287 // [[Xminor,Xmajor], [Yminor,Ymajor]] |
288 // [[Xminor,Xmajor], [Yminor,Ymajor]] |
288 marks, |
289 marks, |
338 Clipping region is a parallelogram containing axes lines, |
339 Clipping region is a parallelogram containing axes lines, |
339 and whose sides are parallel to axes line respectively. |
340 and whose sides are parallel to axes line respectively. |
340 Given axes lines are not starting at the same point, hereafter is |
341 Given axes lines are not starting at the same point, hereafter is |
341 calculus of parallelogram base point. |
342 calculus of parallelogram base point. |
342 |
343 |
343 ^ given Y axis |
344 ^ given Y axis (yvect) |
344 / / |
345 / / |
345 / / |
346 / / |
346 / / |
347 / / |
347 xstart /---------/--------------> given X axis |
348 xstart *---------*--------------> given X axis (xvect) |
348 / / |
349 / /origin |
349 / / |
350 / / |
350 /---------/-------------- |
351 *---------*-------------- |
351 base_point ystart |
352 base_point ystart |
352 |
353 |
353 base_point = xstart + yvect * a |
|
354 base_point = ystart + xvect * b |
|
355 ==> solve : "xstart + yvect * a = ystart + xvect * b" to find a and b and then base_point |
|
356 |
|
357 (1) xstart.x + yvect.x * a = ystart.x + xvect.x * b |
|
358 (2) xstart.y + yvect.y * a = ystart.y + xvect.y * b |
|
359 |
|
360 // express a |
|
361 (1) a = (ystart.x + xvect.x * b) / (xstart.x + yvect.x) |
|
362 |
|
363 // substitute a to have only b |
|
364 (1+2) xstart.y + yvect.y * (ystart.x + xvect.x * b) / (xstart.x + yvect.x) = ystart.y + xvect.y * b |
|
365 |
|
366 // expand to isolate b |
|
367 (2) xstart.y + (yvect.y * ystart.x) / (xstart.x + yvect.x) + (yvect.y * xvect.x * b) / (xstart.x + yvect.x) = ystart.y + xvect.y * b |
|
368 (2) xstart.y + (yvect.y * ystart.x) / (xstart.x + yvect.x) - ystart.y = xvect.y * b - (yvect.y * xvect.x * b) / (xstart.x + yvect.x) |
|
369 |
|
370 // factorize b |
|
371 (2) xstart.y + (yvect.y * ystart.x) / (xstart.x + yvect.x) - ystart.y = b * (xvect.y - (yvect.y * xvect.x ) / (xstart.x + yvect.x)) |
|
372 |
|
373 // extract b |
|
374 (2) b = (xstart.y + (yvect.y * ystart.x) / (xstart.x + yvect.x) - ystart.y) / ((xvect.y - (yvect.y * xvect.x ) / (xstart.x + yvect.x))) |
|
375 */ |
354 */ |
376 |
355 |
377 let b = (xstart.y + (yvect.y * ystart.x) / (xstart.x + yvect.x) - ystart.y) / ((xvect.y - (yvect.y * xvect.x ) / (xstart.x + yvect.x))); |
356 let base_point = getLinesIntesection([xstart,yvect],[ystart,xvect]); |
378 let base_point = new DOMPoint(ystart.x + xvect.x * b, ystart.y + xvect.y * b); |
|
379 |
|
380 // // compute given origin |
|
381 // // from drawing : given_origin = xstart - xvect * b |
|
382 // let given_origin = new DOMPoint(xstart.x - xvect.x * b, xstart.y - xvect.y * b); |
|
383 |
357 |
384 return base_point; |
358 return base_point; |
385 |
359 |
386 } |
360 } |
387 |
361 |
405 this[elementname+"_"+name+"_transform"]=transform; |
379 this[elementname+"_"+name+"_transform"]=transform; |
406 }; |
380 }; |
407 }; |
381 }; |
408 |
382 |
409 // group marks an labels together |
383 // group marks an labels together |
410 let parent = line.parentElement() |
384 let parent = line.parentElement; |
411 marks_group = move_elements_to_group(marks); |
385 let marks_group = move_elements_to_group(marks); |
412 marks_and_label_group = move_elements_to_group([marks_group_use, label]); |
386 let marks_and_label_group = move_elements_to_group([marks_group, label]); |
413 group = move_elements_to_group([marks_and_label_group,line]); |
387 let group = move_elements_to_group([marks_and_label_group,line]); |
414 parent.appendChild(group); |
388 parent.appendChild(group); |
415 |
389 |
416 // Add transforms to group |
390 // Add transforms to group |
417 for(let name of ["base","origin"]){ |
391 for(let name of ["base","origin"]){ |
418 let transform = svg_root.createSVGTransform(); |
392 let transform = svg_root.createSVGTransform(); |
419 group.transform.baseVal.appendItem(transform); |
393 group.transform.baseVal.appendItem(transform); |
420 this[name+"_transform"]=transform; |
394 this[name+"_transform"]=transform; |
421 }; |
395 }; |
427 this.mlg_clones = []; |
401 this.mlg_clones = []; |
428 this.last_mark_count = 0; |
402 this.last_mark_count = 0; |
429 } |
403 } |
430 |
404 |
431 setBasePoint(base_point){ |
405 setBasePoint(base_point){ |
432 // move Axis to base point |
406 // move Axis to base point |
433 let [start, _vect] = this.lineElement; |
407 let [start, _vect] = this.line; |
434 let v = vector(start, base_point); |
408 let v = vector(start, base_point); |
435 this.base_transform.setTranslate(v.x, v.y); |
409 this.base_transform.setTranslate(v.x, v.y); |
436 |
410 |
437 // Move marks and label to base point. |
411 // Move marks and label to base point. |
438 // _|_______ _|________ |
412 // _|_______ _|________ |
439 // | ' | ==> ' |
413 // | ' | ==> ' |
440 // | 0 0 |
414 // | 0 0 |
441 // | | |
415 // | | |
442 |
416 |
443 for(let [markname,mark] of zip(["minor", "major"],this.marks)){ |
417 for(let [markname,mark] of zip(["minor", "major"],this.marks)){ |
444 let transform = this[markname+"_base_transform"]; |
418 let transform = this[markname+"_base_transform"]; |
445 let pos = vector( |
419 let pos = vector( |
446 // Marks are expected to be paths |
420 // Marks are expected to be paths |
452 this[markname+"_base_transform"].setTranslate(-pos.x, -pos.x); |
426 this[markname+"_base_transform"].setTranslate(-pos.x, -pos.x); |
453 if(markname == "major"){ // label follow major mark |
427 if(markname == "major"){ // label follow major mark |
454 this.label_base_transform.setTranslate(-pos.x, -pos.x); |
428 this.label_base_transform.setTranslate(-pos.x, -pos.x); |
455 } |
429 } |
456 } |
430 } |
457 } |
|
458 |
|
459 applyOriginAndUnitVector(offset, unit_vect){ |
|
460 // offset is a representing position of an |
|
461 // axis along the opposit axis line, expressed in major marks units |
|
462 // unit_vect is the translation in between to major marks |
|
463 |
|
464 // ^ |
|
465 // | unit_vect |
|
466 // |<---> |
|
467 // _________|__________> |
|
468 // ^ | ' | ' | ' |
|
469 // |yoffset | 1 |
|
470 // | | |
|
471 // v xoffset| |
|
472 // X<------>| |
|
473 // base_point |
|
474 |
|
475 // move major marks and label to first positive mark position |
|
476 let v = vectorscale(unit_vect, offset+1); |
|
477 this.label_slide_transform.setTranslate(v.x, v.x); |
|
478 this.major_slide_transform.setTranslate(v.x, v.x); |
|
479 // move minor mark to first half positive mark position |
|
480 let h = vectorscale(unit_vect, offset+0.5); |
|
481 this.minor_slide_transform.setTranslate(h.x, h.x); |
|
482 } |
431 } |
483 |
432 |
484 applyRange(min, max){ |
433 applyRange(min, max){ |
485 let range = max - min; |
434 let range = max - min; |
486 |
435 |
542 let unit_vect = vectorscale(vect, unit/range); |
491 let unit_vect = vectorscale(vect, unit/range); |
543 let [umin, umax, uoffset] = [min,max,offset].map(val => Math.round(val/unit)); |
492 let [umin, umax, uoffset] = [min,max,offset].map(val => Math.round(val/unit)); |
544 let mark_count = umax-umin; |
493 let mark_count = umax-umin; |
545 |
494 |
546 // apply unit vector to marks and label |
495 // apply unit vector to marks and label |
547 this.label_and_marks.applyOriginAndUnitVector(offset, unit_vect); |
496 // offset is a representing position of an |
|
497 // axis along the opposit axis line, expressed in major marks units |
|
498 // unit_vect is the translation in between to major marks |
|
499 |
|
500 // ^ |
|
501 // | unit_vect |
|
502 // |<---> |
|
503 // _________|__________> |
|
504 // ^ | ' | ' | ' |
|
505 // |yoffset | 1 |
|
506 // | | |
|
507 // v xoffset| |
|
508 // X<------>| |
|
509 // base_point |
|
510 |
|
511 // move major marks and label to first positive mark position |
|
512 let v = vectorscale(unit_vect, offset+unit); |
|
513 this.label_slide_transform.setTranslate(v.x, v.y); |
|
514 this.major_slide_transform.setTranslate(v.x, v.y); |
|
515 // move minor mark to first half positive mark position |
|
516 let h = vectorscale(unit_vect, offset+(unit/2)); |
|
517 this.minor_slide_transform.setTranslate(h.x, h.y); |
548 |
518 |
549 // duplicate marks and labels as needed |
519 // duplicate marks and labels as needed |
550 let current_mark_count = this.mlg_clones.length; |
520 let current_mark_count = this.mlg_clones.length; |
551 for(let i = current_mark_count; i <= mark_count; i++){ |
521 for(let i = current_mark_count; i <= mark_count; i++){ |
552 // cloneNode() label and add a svg:use of marks in a new group |
522 // cloneNode() label and add a svg:use of marks in a new group |
553 let newgroup = document.createElementNS(xmlns,"g"); |
523 let newgroup = document.createElementNS(xmlns,"g"); |
554 let transform = svg_root.createSVGTransform(); |
524 let transform = svg_root.createSVGTransform(); |
555 let newlabel = cloneNode(this.label); |
525 let newlabel = this.label.cloneNode(true); |
556 let newuse = document.createElementNS(xmlns,"use"); |
526 let newuse = document.createElementNS(xmlns,"use"); |
557 let newuseAttr = document.createAttribute("xlink:href"); |
527 let newuseAttr = document.createAttribute("xlink:href"); |
558 newuseAttr.value = "#"+this.marks_group.id; |
528 newuseAttr.value = "#"+this.marks_group.id; |
559 newuse.setAttributeNode(newuseAttr.value); |
529 newuse.setAttributeNode(newuseAttr); |
560 newgroup.transform.baseVal.appendItem(transform); |
530 newgroup.transform.baseVal.appendItem(transform); |
561 newgroup.appendChild(newlabel); |
531 newgroup.appendChild(newlabel); |
562 newgroup.appendChild(newuse); |
532 newgroup.appendChild(newuse); |
563 this.mlg_clones.push([tranform,newgroup]); |
533 this.mlg_clones.push([transform,newgroup]); |
564 } |
534 } |
565 |
535 |
566 // move marks and labels, set labels |
536 // move marks and labels, set labels |
567 for(let u = 0; u <= mark_count; u++){ |
537 for(let u = 0; u <= mark_count; u++){ |
568 let i = 0; |
538 let i = 0; |
569 let val = (umin + u) * unit; |
539 let val = (umin + u) * unit; |
570 let vec = vectorscale(unit_vect, offset + val); |
540 let vec = vectorscale(unit_vect, offset + val); |
571 let text = this.format ? sprintf(this.format, val) : val.toString(); |
541 let text = this.format ? sprintf(this.format, val) : val.toString(); |
572 if(u == uoffset){ |
542 if(u == uoffset){ |
573 // apply offset to original marks and label groups |
543 // apply offset to original marks and label groups |
574 this.origin_transform.setTranslate(vec.x, vec.x); |
544 this.origin_transform.setTranslate(vec.x, vec.y); |
575 |
545 |
576 // update original label text |
546 // update original label text |
577 this.label_and_mark.label.textContent = text; |
547 this.label.getElementsByTagName("tspan")[0].textContent = text; |
578 } else { |
548 } else { |
579 let [transform,element] = this.mlg_clones[i++]; |
549 let [transform,element] = this.mlg_clones[i++]; |
580 |
550 |
581 // apply unit vector*N to marks and label groups |
551 // apply unit vector*N to marks and label groups |
582 transform.setTranslate(vec.x, vec.x); |
552 transform.setTranslate(vec.x, vec.y); |
583 |
553 |
584 // update label text |
554 // update label text |
585 element.getElementsByTagName("tspan")[0].textContent = text; |
555 element.getElementsByTagName("tspan")[0].textContent = text; |
586 |
556 |
587 // Attach to group if not already |
557 // Attach to group if not already |