plotboilerplate 1.20.1 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/dist/index.esm.js +2065 -1397
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/dist/plotboilerplate.browser.min.js +1 -1
- package/dist/plotboilerplate.browser.min.js.map +1 -1
- package/dist/plotboilerplate.js +1265 -584
- package/dist/plotboilerplate.min.js +1 -1
- package/package.json +2 -1
- package/src/cjs/BezierPath.d.ts +27 -49
- package/src/cjs/BezierPath.js +69 -237
- package/src/cjs/BezierPath.js.map +1 -1
- package/src/cjs/Bounds.d.ts +14 -1
- package/src/cjs/Bounds.js +21 -1
- package/src/cjs/Bounds.js.map +1 -1
- package/src/cjs/CubicBezierCurve.d.ts +70 -1
- package/src/cjs/CubicBezierCurve.js +130 -12
- package/src/cjs/CubicBezierCurve.js.map +1 -1
- package/src/cjs/Line.d.ts +2 -1
- package/src/cjs/Line.js +1 -0
- package/src/cjs/Line.js.map +1 -1
- package/src/cjs/PBText.d.ts +2 -1
- package/src/cjs/PBText.js +10 -9
- package/src/cjs/PBText.js.map +1 -1
- package/src/cjs/PlotBoilerplate.d.ts +10 -13
- package/src/cjs/PlotBoilerplate.js +136 -47
- package/src/cjs/PlotBoilerplate.js.map +1 -1
- package/src/cjs/Polygon.d.ts +35 -1
- package/src/cjs/Polygon.js +87 -1
- package/src/cjs/Polygon.js.map +1 -1
- package/src/cjs/Triangle.js +1 -0
- package/src/cjs/Triangle.js.map +1 -1
- package/src/cjs/VEllipse.js +1 -1
- package/src/cjs/VEllipse.js.map +1 -1
- package/src/cjs/Vector.d.ts +4 -4
- package/src/cjs/Vector.js +2 -2
- package/src/cjs/Vector.js.map +1 -1
- package/src/cjs/VertTuple.d.ts +2 -1
- package/src/cjs/VertTuple.js +4 -3
- package/src/cjs/VertTuple.js.map +1 -1
- package/src/cjs/Vertex.d.ts +14 -3
- package/src/cjs/Vertex.js +18 -3
- package/src/cjs/Vertex.js.map +1 -1
- package/src/cjs/draw.d.ts +159 -79
- package/src/cjs/draw.js +311 -139
- package/src/cjs/draw.js.map +1 -1
- package/src/cjs/drawgl.d.ts +93 -55
- package/src/cjs/drawgl.js +90 -34
- package/src/cjs/drawgl.js.map +1 -1
- package/src/cjs/drawutilssvg.d.ts +233 -58
- package/src/cjs/drawutilssvg.js +384 -95
- package/src/cjs/drawutilssvg.js.map +1 -1
- package/src/cjs/interfaces/DrawLib.d.ts +161 -82
- package/src/cjs/interfaces/DrawLib.js +7 -0
- package/src/cjs/interfaces/DrawLib.js.map +1 -1
- package/src/cjs/interfaces/core.d.ts +46 -32
- package/src/cjs/interfaces/core.js +2 -0
- package/src/cjs/interfaces/core.js.map +1 -1
- package/src/cjs/utils/creategui.js +16 -10
- package/src/cjs/utils/datastructures/Color.d.ts +3 -1
- package/src/cjs/utils/datastructures/Color.js +12 -1
- package/src/cjs/utils/datastructures/Color.js.map +1 -1
- package/src/cjs/utils/detectDarkMode.d.ts +10 -0
- package/src/cjs/utils/detectDarkMode.js +32 -0
- package/src/cjs/utils/detectDarkMode.js.map +1 -0
- package/src/cjs/utils/module.js +4 -2
- package/src/esm/BezierPath.d.ts +27 -49
- package/src/esm/BezierPath.js +69 -237
- package/src/esm/BezierPath.js.map +1 -1
- package/src/esm/Bounds.d.ts +14 -1
- package/src/esm/Bounds.js +19 -1
- package/src/esm/Bounds.js.map +1 -1
- package/src/esm/CubicBezierCurve.d.ts +70 -1
- package/src/esm/CubicBezierCurve.js +130 -12
- package/src/esm/CubicBezierCurve.js.map +1 -1
- package/src/esm/Line.d.ts +2 -1
- package/src/esm/Line.js +1 -0
- package/src/esm/Line.js.map +1 -1
- package/src/esm/PBText.d.ts +2 -1
- package/src/esm/PBText.js +10 -9
- package/src/esm/PBText.js.map +1 -1
- package/src/esm/PlotBoilerplate.d.ts +10 -13
- package/src/esm/PlotBoilerplate.js +136 -48
- package/src/esm/PlotBoilerplate.js.map +1 -1
- package/src/esm/Polygon.d.ts +35 -1
- package/src/esm/Polygon.js +85 -1
- package/src/esm/Polygon.js.map +1 -1
- package/src/esm/Triangle.js +1 -0
- package/src/esm/Triangle.js.map +1 -1
- package/src/esm/VEllipse.js +1 -1
- package/src/esm/VEllipse.js.map +1 -1
- package/src/esm/Vector.d.ts +4 -4
- package/src/esm/Vector.js +2 -2
- package/src/esm/Vector.js.map +1 -1
- package/src/esm/VertTuple.d.ts +2 -1
- package/src/esm/VertTuple.js +4 -3
- package/src/esm/VertTuple.js.map +1 -1
- package/src/esm/Vertex.d.ts +14 -3
- package/src/esm/Vertex.js +18 -3
- package/src/esm/Vertex.js.map +1 -1
- package/src/esm/draw.d.ts +159 -79
- package/src/esm/draw.js +307 -137
- package/src/esm/draw.js.map +1 -1
- package/src/esm/drawgl.d.ts +93 -55
- package/src/esm/drawgl.js +90 -34
- package/src/esm/drawgl.js.map +1 -1
- package/src/esm/drawutilssvg.d.ts +233 -58
- package/src/esm/drawutilssvg.js +379 -95
- package/src/esm/drawutilssvg.js.map +1 -1
- package/src/esm/interfaces/DrawLib.d.ts +161 -82
- package/src/esm/interfaces/DrawLib.js +7 -0
- package/src/esm/interfaces/DrawLib.js.map +1 -1
- package/src/esm/interfaces/core.d.ts +46 -32
- package/src/esm/interfaces/core.js +2 -0
- package/src/esm/interfaces/core.js.map +1 -1
- package/src/esm/utils/datastructures/Color.d.ts +3 -1
- package/src/esm/utils/datastructures/Color.js +12 -1
- package/src/esm/utils/datastructures/Color.js.map +1 -1
- package/src/esm/utils/detectDarkMode.d.ts +10 -0
- package/src/esm/utils/detectDarkMode.js +28 -0
- package/src/esm/utils/detectDarkMode.js.map +1 -0
- package/src/ts/BezierPath.ts +74 -244
- package/src/ts/Bounds.ts +25 -1
- package/src/ts/CubicBezierCurve.ts +139 -12
- package/src/ts/Line.ts +2 -1
- package/src/ts/PBText.ts +10 -9
- package/src/ts/PlotBoilerplate.ts +130 -50
- package/src/ts/Polygon.ts +91 -1
- package/src/ts/Triangle.ts +2 -1
- package/src/ts/VEllipse.ts +2 -2
- package/src/ts/Vector.ts +4 -4
- package/src/ts/VertTuple.ts +4 -3
- package/src/ts/Vertex.ts +20 -4
- package/src/ts/draw.ts +316 -119
- package/src/ts/drawgl.ts +141 -78
- package/src/ts/drawutilssvg.ts +562 -133
- package/src/ts/interfaces/DrawLib.ts +223 -86
- package/src/ts/interfaces/core.ts +47 -32
- package/src/ts/utils/datastructures/Color.ts +13 -1
- package/src/ts/utils/detectDarkMode.ts +30 -0
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
* @modified 2022-02-02 Added the `destroy` method.
|
|
22
22
|
* @modified 2022-02-02 Cleared the `toSVGPathData` function (deprecated). Use `drawutilssvg` instead.
|
|
23
23
|
* @modified 2022-10-17 The `CubicBezierCurve` class now implements the new `PathSegment` interface.
|
|
24
|
-
* @
|
|
24
|
+
* @modified 2023-09-30 Added the function `CubicbezierCurve.getSubCurve(number,number)` – similar to `getSubCurveAt(...)` but with absolute position parameters.
|
|
25
|
+
* @modified 2023-10-07 Added the `trimEnd`, `trimEndAt`, `trimStart`, `trimStartAt` methods.
|
|
26
|
+
* @version 2.8.0
|
|
25
27
|
*
|
|
26
28
|
* @file CubicBezierCurve
|
|
27
29
|
* @public
|
|
@@ -523,6 +525,96 @@ export class CubicBezierCurve implements PathSegment {
|
|
|
523
525
|
return new Vertex(tX, tY);
|
|
524
526
|
}
|
|
525
527
|
|
|
528
|
+
/**
|
|
529
|
+
* Trim off a start section of this curve. The position parameter `uValue` is the absolute position on the
|
|
530
|
+
* curve in `[0...arcLength]`.
|
|
531
|
+
* The remaining curve will be the one in the bounds `[uValue,1]` (so `[0.0,uValue]` is cut off).
|
|
532
|
+
*
|
|
533
|
+
* Note this function just converts the absolute parameter to a relative one and call `trimStartAt`.
|
|
534
|
+
*
|
|
535
|
+
* @method trimStart
|
|
536
|
+
* @instance
|
|
537
|
+
* @memberof CubicBezierCurve
|
|
538
|
+
* @param {number} uValue - The absolute position parameter where to cut off the head curve.
|
|
539
|
+
* @returns {CubicBezierCurve} `this` for chanining.
|
|
540
|
+
*/
|
|
541
|
+
trimStart(uValue: number): CubicBezierCurve {
|
|
542
|
+
return this.trimStartAt(this.convertU2T(uValue));
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Trim off a start section of this curve. The position parameter `t` is the relative position in [0..1].
|
|
547
|
+
* The remaining curve will be the one in the bounds `[uValue,1]` (so `[0.0,uValue]` is cut off).
|
|
548
|
+
*
|
|
549
|
+
* @method trimStartAt
|
|
550
|
+
* @instance
|
|
551
|
+
* @memberof CubicBezierCurve
|
|
552
|
+
* @param {number} t - The relative position parameter where to cut off the head curve.
|
|
553
|
+
* @returns {CubicBezierCurve} `this` for chanining.
|
|
554
|
+
*/
|
|
555
|
+
trimStartAt(t: number): CubicBezierCurve {
|
|
556
|
+
const subCurbePoints = CubicBezierCurve.utils.getSubCurvePointsAt(this, t, 1.0);
|
|
557
|
+
this.startPoint.set(subCurbePoints[0]);
|
|
558
|
+
this.startControlPoint.set(subCurbePoints[2]);
|
|
559
|
+
this.endPoint.set(subCurbePoints[1]);
|
|
560
|
+
this.endControlPoint.set(subCurbePoints[3]);
|
|
561
|
+
this.updateArcLengths();
|
|
562
|
+
return this;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Trim off the end of this curve. The position parameter `uValue` is the absolute position on the
|
|
567
|
+
* curve in `[0...arcLength]`.
|
|
568
|
+
* The remaining curve will be the one in the bounds `[0,uValue]` (so `[1.0-uValue,1.0]` is cut off).
|
|
569
|
+
*
|
|
570
|
+
* Note this function just converts the absolute parameter to a relative one and call `trimEndAt`.
|
|
571
|
+
*
|
|
572
|
+
* @method trimEnd
|
|
573
|
+
* @instance
|
|
574
|
+
* @memberof CubicBezierCurve
|
|
575
|
+
* @param {number} uValue - The absolute position parameter where to cut off the tail curve.
|
|
576
|
+
* @returns {CubicBezierCurve} `this` for chanining.
|
|
577
|
+
*/
|
|
578
|
+
trimEnd(uValue: number): CubicBezierCurve {
|
|
579
|
+
return this.trimEndAt(this.convertU2T(uValue));
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Trim off the end of this curve. The position parameter `t` is the relative position in [0..1].
|
|
584
|
+
* The remaining curve will be the one in the bounds `[0,t]` (so `[1.0-t,1.0]` is cut off).
|
|
585
|
+
*
|
|
586
|
+
* @method trimEndAt
|
|
587
|
+
* @instance
|
|
588
|
+
* @memberof CubicBezierCurve
|
|
589
|
+
* @param {number} t - The relative position parameter where to cut off the tail curve.
|
|
590
|
+
* @returns {CubicBezierCurve} `this` for chanining.
|
|
591
|
+
*/
|
|
592
|
+
trimEndAt(t: number): CubicBezierCurve {
|
|
593
|
+
const subCurbePoints = CubicBezierCurve.utils.getSubCurvePointsAt(this, 0.0, t);
|
|
594
|
+
this.startPoint.set(subCurbePoints[0]);
|
|
595
|
+
this.startControlPoint.set(subCurbePoints[2]);
|
|
596
|
+
this.endPoint.set(subCurbePoints[1]);
|
|
597
|
+
this.endControlPoint.set(subCurbePoints[3]);
|
|
598
|
+
this.updateArcLengths();
|
|
599
|
+
return this;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Get a sub curve at the given start end end positions (values on the curve's length, between 0 and curve.arcLength).
|
|
604
|
+
*
|
|
605
|
+
* tStart >= tEnd is allowed, you will get a reversed sub curve then.
|
|
606
|
+
*
|
|
607
|
+
* @method getSubCurve
|
|
608
|
+
* @param {number} tStart – The start position of the desired sub curve (must be in [0..arcLength]).
|
|
609
|
+
* @param {number} tEnd – The end position if the desired cub curve (must be in [0..arcLength]).
|
|
610
|
+
* @instance
|
|
611
|
+
* @memberof CubicBezierCurve
|
|
612
|
+
* @return {CubicBezierCurve} The sub curve as a new curve.
|
|
613
|
+
**/
|
|
614
|
+
getSubCurve(uStart: number, uEnd: number): CubicBezierCurve {
|
|
615
|
+
return this.getSubCurveAt(this.convertU2T(uStart), this.convertU2T(uEnd));
|
|
616
|
+
}
|
|
617
|
+
|
|
526
618
|
/**
|
|
527
619
|
* Get a sub curve at the given start end end offsets (values between 0.0 and 1.0).
|
|
528
620
|
*
|
|
@@ -536,20 +628,22 @@ export class CubicBezierCurve implements PathSegment {
|
|
|
536
628
|
* @return {CubicBezierCurve} The sub curve as a new curve.
|
|
537
629
|
**/
|
|
538
630
|
getSubCurveAt(tStart: number, tEnd: number): CubicBezierCurve {
|
|
539
|
-
const startVec: Vector = new Vector(this.getPointAt(tStart), this.getTangentAt(tStart));
|
|
540
|
-
const endVec: Vector = new Vector(this.getPointAt(tEnd), this.getTangentAt(tEnd).inv());
|
|
631
|
+
// const startVec: Vector = new Vector(this.getPointAt(tStart), this.getTangentAt(tStart));
|
|
632
|
+
// const endVec: Vector = new Vector(this.getPointAt(tEnd), this.getTangentAt(tEnd).inv());
|
|
541
633
|
|
|
542
|
-
// Tangents are relative. Make absolute.
|
|
543
|
-
startVec.b.add(startVec.a);
|
|
544
|
-
endVec.b.add(endVec.a);
|
|
634
|
+
// // Tangents are relative. Make absolute.
|
|
635
|
+
// startVec.b.add(startVec.a);
|
|
636
|
+
// endVec.b.add(endVec.a);
|
|
545
637
|
|
|
546
|
-
// This 'splits' the curve at the given point at t.
|
|
547
|
-
startVec.scale(0.33333333 * (tEnd - tStart));
|
|
548
|
-
endVec.scale(0.33333333 * (tEnd - tStart));
|
|
638
|
+
// // This 'splits' the curve at the given point at t.
|
|
639
|
+
// startVec.scale(0.33333333 * (tEnd - tStart));
|
|
640
|
+
// endVec.scale(0.33333333 * (tEnd - tStart));
|
|
549
641
|
|
|
550
|
-
// Draw the bezier curve
|
|
551
|
-
// pb.draw.cubicBezier( startVec.a, endVec.a, startVec.b, endVec.b, '#8800ff', 2 );
|
|
552
|
-
return new CubicBezierCurve(startVec.a, endVec.a, startVec.b, endVec.b);
|
|
642
|
+
// // Draw the bezier curve
|
|
643
|
+
// // pb.draw.cubicBezier( startVec.a, endVec.a, startVec.b, endVec.b, '#8800ff', 2 );
|
|
644
|
+
// return new CubicBezierCurve(startVec.a, endVec.a, startVec.b, endVec.b);
|
|
645
|
+
const subCurbePoints = CubicBezierCurve.utils.getSubCurvePointsAt(this, tStart, tEnd);
|
|
646
|
+
return new CubicBezierCurve(subCurbePoints[0], subCurbePoints[1], subCurbePoints[2], subCurbePoints[3]);
|
|
553
647
|
}
|
|
554
648
|
|
|
555
649
|
/**
|
|
@@ -813,4 +907,37 @@ export class CubicBezierCurve implements PathSegment {
|
|
|
813
907
|
if (arr.length != 4) throw "Can only build from array with four elements.";
|
|
814
908
|
return new CubicBezierCurve(arr[0], arr[1], arr[2], arr[3]);
|
|
815
909
|
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Helper utils.
|
|
913
|
+
*/
|
|
914
|
+
private static utils = {
|
|
915
|
+
/**
|
|
916
|
+
* Get the points of a sub curve at the given start end end offsets (values between 0.0 and 1.0).
|
|
917
|
+
*
|
|
918
|
+
* tStart >= tEnd is allowed, you will get a reversed sub curve then.
|
|
919
|
+
*
|
|
920
|
+
* @method getSubCurvePointsAt
|
|
921
|
+
* @param {CubicBezierCurve} curve – The curve to get the sub curve points from.
|
|
922
|
+
* @param {number} tStart – The start offset of the desired sub curve (must be in [0..1]).
|
|
923
|
+
* @param {number} tEnd – The end offset if the desired cub curve (must be in [0..1]).
|
|
924
|
+
* @instance
|
|
925
|
+
* @memberof CubicBezierCurve
|
|
926
|
+
* @return {CubicBezierCurve} The sub curve as a new curve.
|
|
927
|
+
**/
|
|
928
|
+
getSubCurvePointsAt: (curve: CubicBezierCurve, tStart: number, tEnd: number): [Vertex, Vertex, Vertex, Vertex] => {
|
|
929
|
+
const startVec: Vector = new Vector(curve.getPointAt(tStart), curve.getTangentAt(tStart));
|
|
930
|
+
const endVec: Vector = new Vector(curve.getPointAt(tEnd), curve.getTangentAt(tEnd).inv());
|
|
931
|
+
|
|
932
|
+
// Tangents are relative. Make absolute.
|
|
933
|
+
startVec.b.add(startVec.a);
|
|
934
|
+
endVec.b.add(endVec.a);
|
|
935
|
+
|
|
936
|
+
// This 'splits' the curve at the given point at t.
|
|
937
|
+
startVec.scale(0.33333333 * (tEnd - tStart));
|
|
938
|
+
endVec.scale(0.33333333 * (tEnd - tStart));
|
|
939
|
+
|
|
940
|
+
return [startVec.a, endVec.a, startVec.b, endVec.b];
|
|
941
|
+
}
|
|
942
|
+
};
|
|
816
943
|
}
|
package/src/ts/Line.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* @modified 2022-02-02 Added the `destroy` method.
|
|
16
16
|
* @modified 2022-10-09 Changed the actual return value of the `intersection` function to null (was undefined before).
|
|
17
17
|
* @modified 2022-10-17 Adding these methods from the `PathSegment` interface: getStartPoint, getEndPoint, revert.
|
|
18
|
+
* @modified 2023-09-25 Changed param type of `intersection()` from Line to VertTuple.
|
|
18
19
|
* @version 2.3.0
|
|
19
20
|
*
|
|
20
21
|
* @file Line
|
|
@@ -61,7 +62,7 @@ export class Line extends VertTuple<Line> implements SVGSerializable, PathSegmen
|
|
|
61
62
|
* @memberof Line
|
|
62
63
|
**/
|
|
63
64
|
// !!! DO NOT MOVE TO VertTuple
|
|
64
|
-
intersection(line:
|
|
65
|
+
intersection(line: VertTuple<any>): Vertex | null {
|
|
65
66
|
const denominator: number = this.denominator(line);
|
|
66
67
|
if (denominator == 0) {
|
|
67
68
|
return null;
|
package/src/ts/PBText.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* @author Ikaros Kappler
|
|
3
3
|
* @date 2021-11-16
|
|
4
4
|
* @modified 2022-02-02 Added the `destroy` method.
|
|
5
|
-
* @
|
|
5
|
+
* @modified 2023-09-25 Fixed a type error in the constructor. Nothing vital.
|
|
6
|
+
* @version 1.1.1
|
|
6
7
|
**/
|
|
7
8
|
|
|
8
9
|
import { UIDGenerator } from "./UIDGenerator";
|
|
@@ -127,14 +128,14 @@ export class PBText implements SVGSerializable, FontOptions {
|
|
|
127
128
|
this.uid = UIDGenerator.next();
|
|
128
129
|
this.text = text;
|
|
129
130
|
this.anchor = anchor ?? new Vertex();
|
|
130
|
-
this.color = options
|
|
131
|
-
this.fontFamily = options
|
|
132
|
-
this.fontSize = options
|
|
133
|
-
this.fontStyle = options
|
|
134
|
-
this.fontWeight = options
|
|
135
|
-
this.lineHeight = options
|
|
136
|
-
this.textAlign = options
|
|
137
|
-
this.rotation = options
|
|
131
|
+
this.color = options?.color;
|
|
132
|
+
this.fontFamily = options?.fontFamily;
|
|
133
|
+
this.fontSize = options?.fontSize;
|
|
134
|
+
this.fontStyle = options?.fontStyle;
|
|
135
|
+
this.fontWeight = options?.fontWeight;
|
|
136
|
+
this.lineHeight = options?.lineHeight;
|
|
137
|
+
this.textAlign = options?.textAlign;
|
|
138
|
+
this.rotation = options?.rotation;
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
/**
|
|
@@ -78,7 +78,12 @@
|
|
|
78
78
|
* @modified 2022-10-25 Added the `origin` to the default draw config.
|
|
79
79
|
* @modified 2022-11-06 Adding an XML declaration to the SVG export routine.
|
|
80
80
|
* @modified 2022-11-23 Added the `drawRaster` (default=true) option to the config/drawconfig.
|
|
81
|
-
* @
|
|
81
|
+
* @modified 2023-02-04 Fixed a bug in the `drawDrawable` function; fill's current classname was not set.
|
|
82
|
+
* @modified 2023-02-10 Fixing an issue of the `style.position` setting when `fitToParent=true` from `absolute` to `static` (default).
|
|
83
|
+
* @modified 2023-02-10 Cleaning up most type errors in the main class (mostly null checks).
|
|
84
|
+
* @modified 2023-02-10 Adding `enableZoom` and `enablePan` (both default true) to have the option to disable these functions.
|
|
85
|
+
* @modified 2023-09-29 Adding proper dicionary key and value types to the params of `PlotBoilerplate.utils.safeMergeByKeys` (was `object` before).
|
|
86
|
+
* @version 1.17.3
|
|
82
87
|
*
|
|
83
88
|
* @file PlotBoilerplate
|
|
84
89
|
* @fileoverview The main class.
|
|
@@ -377,6 +382,8 @@ export class PlotBoilerplate {
|
|
|
377
382
|
* @param {boolean=} [config.enableTouch=true] - Indicates if the application should handle touch events for you.
|
|
378
383
|
* @param {boolean=} [config.enableKeys=true] - Indicates if the application should handle key events for you.
|
|
379
384
|
* @param {boolean=} [config.enableMouseWheel=true] - Indicates if the application should handle mouse wheel events for you.
|
|
385
|
+
* @param {boolean=} [config.enablePan=true] - (default true) Set to false if you want to disable panning completely.
|
|
386
|
+
* @param {boolean=} [config.enableZoom=true] - (default true) Set to false if you want to disable zooming completely.
|
|
380
387
|
* @param {boolean=} [config.enableGL=false] - Indicates if the application should use the experimental WebGL features (not recommended).
|
|
381
388
|
* @param {boolean=} [config.enableSVGExport=true] - Indicates if the SVG export should be enabled (default is true).
|
|
382
389
|
* Note that changes from the postDraw hook might not be visible in the export.
|
|
@@ -393,7 +400,7 @@ export class PlotBoilerplate {
|
|
|
393
400
|
visible: true
|
|
394
401
|
};
|
|
395
402
|
|
|
396
|
-
if (typeof config.canvas
|
|
403
|
+
if (typeof config.canvas === "undefined") {
|
|
397
404
|
throw "No canvas specified.";
|
|
398
405
|
}
|
|
399
406
|
|
|
@@ -450,6 +457,8 @@ export class PlotBoilerplate {
|
|
|
450
457
|
enableTouch: f.bool(config, "enableTouch", true),
|
|
451
458
|
enableKeys: f.bool(config, "enableKeys", true),
|
|
452
459
|
enableMouseWheel: f.bool(config, "enableMouseWheel", true),
|
|
460
|
+
enableZoom: f.bool(config, "enableZoom", true), // default=true
|
|
461
|
+
enablePan: f.bool(config, "enablePan", true), // default=true
|
|
453
462
|
|
|
454
463
|
// Experimental (and unfinished)
|
|
455
464
|
enableGL: f.bool(config, "enableGL", false)
|
|
@@ -488,6 +497,10 @@ export class PlotBoilerplate {
|
|
|
488
497
|
fill: true
|
|
489
498
|
}
|
|
490
499
|
},
|
|
500
|
+
// bezierPath: {
|
|
501
|
+
// color: "#0022a8",
|
|
502
|
+
// lineWidth: 1
|
|
503
|
+
// },
|
|
491
504
|
polygon: {
|
|
492
505
|
color: "#0022a8",
|
|
493
506
|
lineWidth: 1
|
|
@@ -549,7 +562,10 @@ export class PlotBoilerplate {
|
|
|
549
562
|
this.grid = new Grid(new Vertex(0, 0), new Vertex(50, 50));
|
|
550
563
|
this.canvasSize = { width: PlotBoilerplate.DEFAULT_CANVAS_WIDTH, height: PlotBoilerplate.DEFAULT_CANVAS_HEIGHT };
|
|
551
564
|
const canvasElement: Element =
|
|
552
|
-
typeof config.canvas
|
|
565
|
+
typeof config.canvas === "string" ? (document.querySelector(config.canvas) as Element) : config.canvas;
|
|
566
|
+
if (typeof canvasElement === "undefined") {
|
|
567
|
+
throw `Cannot initialize PlotBoilerplate with a null canvas (element "${config.canvas} not found).`;
|
|
568
|
+
}
|
|
553
569
|
// Which renderer to use: Canvas2D, WebGL (experimental) or SVG?
|
|
554
570
|
if (canvasElement.tagName.toLowerCase() === "canvas") {
|
|
555
571
|
this.canvas = canvasElement as HTMLCanvasElement;
|
|
@@ -1140,7 +1156,7 @@ export class PlotBoilerplate {
|
|
|
1140
1156
|
this.draw.setCurrentId(d.uid);
|
|
1141
1157
|
this.fill.setCurrentId(d.uid);
|
|
1142
1158
|
this.draw.setCurrentClassName(d.className);
|
|
1143
|
-
this.
|
|
1159
|
+
this.fill.setCurrentClassName(d.className);
|
|
1144
1160
|
this.drawDrawable(d, renderTime, draw, fill);
|
|
1145
1161
|
}
|
|
1146
1162
|
}
|
|
@@ -1162,7 +1178,13 @@ export class PlotBoilerplate {
|
|
|
1162
1178
|
**/
|
|
1163
1179
|
drawDrawable(d: Drawable, renderTime: number, draw: DrawLib<any>, fill: DrawLib<any>) {
|
|
1164
1180
|
if (d instanceof BezierPath) {
|
|
1181
|
+
var curveIndex = 0;
|
|
1165
1182
|
for (var c in d.bezierCurves) {
|
|
1183
|
+
// Restore these settings again in each loop (will be overwritten)
|
|
1184
|
+
this.draw.setCurrentId(`${d.uid}-${curveIndex}`);
|
|
1185
|
+
this.fill.setCurrentId(`${d.uid}-${curveIndex}`);
|
|
1186
|
+
this.draw.setCurrentClassName(d.className);
|
|
1187
|
+
this.fill.setCurrentClassName(d.className);
|
|
1166
1188
|
draw.cubicBezier(
|
|
1167
1189
|
d.bezierCurves[c].startPoint,
|
|
1168
1190
|
d.bezierCurves[c].endPoint,
|
|
@@ -1245,22 +1267,13 @@ export class PlotBoilerplate {
|
|
|
1245
1267
|
if (this.drawConfig.drawBezierHandleLines && this.drawConfig.drawHandleLines) {
|
|
1246
1268
|
draw.setCurrentId(`${d.uid}_l0`);
|
|
1247
1269
|
draw.setCurrentClassName(`${d.className}-start-line`);
|
|
1248
|
-
draw.
|
|
1249
|
-
d.bezierCurves[c].startPoint,
|
|
1250
|
-
d.bezierCurves[c].startControlPoint,
|
|
1251
|
-
this.drawConfig.bezier.handleLine.color,
|
|
1252
|
-
this.drawConfig.bezier.handleLine.lineWidth
|
|
1253
|
-
);
|
|
1270
|
+
draw.handleLine(d.bezierCurves[c].startPoint, d.bezierCurves[c].startControlPoint);
|
|
1254
1271
|
draw.setCurrentId(`${d.uid}_l1`);
|
|
1255
1272
|
draw.setCurrentClassName(`${d.className}-end-line`);
|
|
1256
|
-
draw.
|
|
1257
|
-
d.bezierCurves[c].endPoint,
|
|
1258
|
-
d.bezierCurves[c].endControlPoint,
|
|
1259
|
-
this.drawConfig.bezier.handleLine.color,
|
|
1260
|
-
this.drawConfig.bezier.handleLine.lineWidth
|
|
1261
|
-
);
|
|
1273
|
+
draw.handleLine(d.bezierCurves[c].endPoint, d.bezierCurves[c].endControlPoint);
|
|
1262
1274
|
}
|
|
1263
|
-
|
|
1275
|
+
curveIndex++;
|
|
1276
|
+
} // END for
|
|
1264
1277
|
} else if (d instanceof Polygon) {
|
|
1265
1278
|
draw.polygon(d, this.drawConfig.polygon.color, this.drawConfig.polygon.lineWidth);
|
|
1266
1279
|
if (!this.drawConfig.drawHandlePoints) {
|
|
@@ -1276,11 +1289,11 @@ export class PlotBoilerplate {
|
|
|
1276
1289
|
draw.setCurrentId(`${d.uid}_e0`);
|
|
1277
1290
|
draw.setCurrentClassName(`${d.className}-v-line`);
|
|
1278
1291
|
// draw.line( d.center.clone().add(0,d.axis.y-d.center.y), d.axis, '#c8c8c8' );
|
|
1279
|
-
draw.
|
|
1292
|
+
draw.handleLine(d.center.clone().add(0, d.signedRadiusV()).rotate(d.rotation, d.center), d.axis); // , "#c8c8c8");
|
|
1280
1293
|
draw.setCurrentId(`${d.uid}_e1`);
|
|
1281
1294
|
draw.setCurrentClassName(`${d.className}-h-line`);
|
|
1282
1295
|
// draw.line( d.center.clone().add(d.axis.x-d.center.x,0), d.axis, '#c8c8c8' );
|
|
1283
|
-
draw.
|
|
1296
|
+
draw.handleLine(d.center.clone().add(d.signedRadiusH(), 0).rotate(d.rotation, d.center), d.axis); // , "#c8c8c8");
|
|
1284
1297
|
}
|
|
1285
1298
|
draw.setCurrentId(d.uid);
|
|
1286
1299
|
draw.setCurrentClassName(`${d.className}`);
|
|
@@ -1375,10 +1388,10 @@ export class PlotBoilerplate {
|
|
|
1375
1388
|
} else {
|
|
1376
1389
|
console.error("Cannot draw object. Unknown class.");
|
|
1377
1390
|
}
|
|
1378
|
-
draw.setCurrentClassName(
|
|
1379
|
-
draw.setCurrentId(
|
|
1380
|
-
fill.setCurrentClassName(
|
|
1381
|
-
fill.setCurrentId(
|
|
1391
|
+
draw.setCurrentClassName(null);
|
|
1392
|
+
draw.setCurrentId(null);
|
|
1393
|
+
fill.setCurrentClassName(null);
|
|
1394
|
+
fill.setCurrentId(null);
|
|
1382
1395
|
}
|
|
1383
1396
|
|
|
1384
1397
|
/**
|
|
@@ -1475,8 +1488,8 @@ export class PlotBoilerplate {
|
|
|
1475
1488
|
|
|
1476
1489
|
// Clear IDs and classnames (postDraw hook might draw somthing and the do not want
|
|
1477
1490
|
// to interfered with that).
|
|
1478
|
-
draw.setCurrentId(
|
|
1479
|
-
draw.setCurrentClassName(
|
|
1491
|
+
draw.setCurrentId(null);
|
|
1492
|
+
draw.setCurrentClassName(null);
|
|
1480
1493
|
} // END redraw
|
|
1481
1494
|
|
|
1482
1495
|
/**
|
|
@@ -1626,7 +1639,7 @@ export class PlotBoilerplate {
|
|
|
1626
1639
|
_setSize(width, height);
|
|
1627
1640
|
} else if (_self.config.fitToParent) {
|
|
1628
1641
|
// Set editor size
|
|
1629
|
-
_self.canvas.style.position = "
|
|
1642
|
+
_self.canvas.style.position = "static";
|
|
1630
1643
|
const space: XYDimension = this.getAvailableContainerSpace();
|
|
1631
1644
|
_self.canvas.style.width = (_self.config.canvasWidthFactor ?? 1.0) * space.width + "px";
|
|
1632
1645
|
_self.canvas.style.height = (_self.config.canvasHeightFactor ?? 1.0) * space.height + "px";
|
|
@@ -1697,23 +1710,23 @@ export class PlotBoilerplate {
|
|
|
1697
1710
|
**/
|
|
1698
1711
|
private handleClick(e: XMouseEvent) {
|
|
1699
1712
|
const _self: PlotBoilerplate = this;
|
|
1700
|
-
var
|
|
1713
|
+
var point: IDraggable | null = this.locatePointNear(
|
|
1701
1714
|
_self.transformMousePosition(e.params.pos.x, e.params.pos.y),
|
|
1702
|
-
PlotBoilerplate.DEFAULT_CLICK_TOLERANCE / Math.min(_self.config.cssScaleX, _self.config.cssScaleY)
|
|
1715
|
+
PlotBoilerplate.DEFAULT_CLICK_TOLERANCE / Math.min(_self.config.cssScaleX || 1.0, _self.config.cssScaleY || 1.0)
|
|
1703
1716
|
);
|
|
1704
|
-
if (
|
|
1705
|
-
_self.vertices[
|
|
1717
|
+
if (point) {
|
|
1718
|
+
_self.vertices[point.vindex].listeners.fireClickEvent(e);
|
|
1706
1719
|
if (this.keyHandler && this.keyHandler.isDown("shift")) {
|
|
1707
|
-
if (
|
|
1708
|
-
let vert: Vertex = _self.paths[
|
|
1720
|
+
if (point.typeName == "bpath") {
|
|
1721
|
+
let vert: Vertex = _self.paths[point.pindex].bezierCurves[point.cindex].getPointByID(point.pid);
|
|
1709
1722
|
if (vert.attr.selectable) vert.attr.isSelected = !vert.attr.isSelected;
|
|
1710
|
-
} else if (
|
|
1711
|
-
let vert: Vertex = _self.vertices[
|
|
1723
|
+
} else if (point.typeName == "vertex") {
|
|
1724
|
+
let vert: Vertex = _self.vertices[point.vindex];
|
|
1712
1725
|
if (vert.attr.selectable) vert.attr.isSelected = !vert.attr.isSelected;
|
|
1713
1726
|
}
|
|
1714
1727
|
_self.redraw();
|
|
1715
|
-
} else if (this.keyHandler.isDown("y")) {
|
|
1716
|
-
_self.vertices[
|
|
1728
|
+
} else if (this.keyHandler && this.keyHandler.isDown("y")) {
|
|
1729
|
+
_self.vertices[point.vindex].attr.bezierAutoAdjust = !_self.vertices[point.vindex].attr.bezierAutoAdjust;
|
|
1717
1730
|
_self.redraw();
|
|
1718
1731
|
}
|
|
1719
1732
|
} else if (_self.selectPolygon != null) {
|
|
@@ -1782,13 +1795,13 @@ export class PlotBoilerplate {
|
|
|
1782
1795
|
private mouseDownHandler(e: XMouseEvent) {
|
|
1783
1796
|
const _self = this;
|
|
1784
1797
|
if (e.button != 0) return; // Only react on left mouse or touch events
|
|
1785
|
-
var
|
|
1798
|
+
var draggablePoint: IDraggable | null = _self.locatePointNear(
|
|
1786
1799
|
_self.transformMousePosition(e.params.pos.x, e.params.pos.y),
|
|
1787
1800
|
PlotBoilerplate.DEFAULT_CLICK_TOLERANCE / Math.min(_self.config.cssScaleX, _self.config.cssScaleY)
|
|
1788
1801
|
);
|
|
1789
|
-
if (!
|
|
1802
|
+
if (!draggablePoint) return;
|
|
1790
1803
|
// Drag all selected elements?
|
|
1791
|
-
if (
|
|
1804
|
+
if (draggablePoint.typeName == "vertex" && _self.vertices[draggablePoint.vindex].attr.isSelected) {
|
|
1792
1805
|
// Multi drag
|
|
1793
1806
|
// for( var i in _self.vertices ) {
|
|
1794
1807
|
for (var i = 0; i < _self.vertices.length; i++) {
|
|
@@ -1801,10 +1814,13 @@ export class PlotBoilerplate {
|
|
|
1801
1814
|
}
|
|
1802
1815
|
} else {
|
|
1803
1816
|
// Single drag
|
|
1804
|
-
if (!_self.vertices[
|
|
1805
|
-
_self.draggedElements.push(
|
|
1806
|
-
if (
|
|
1807
|
-
|
|
1817
|
+
if (!_self.vertices[draggablePoint.vindex].attr.draggable) return;
|
|
1818
|
+
_self.draggedElements.push(draggablePoint);
|
|
1819
|
+
if (draggablePoint.typeName == "bpath")
|
|
1820
|
+
_self.paths[draggablePoint.pindex].bezierCurves[draggablePoint.cindex]
|
|
1821
|
+
.getPointByID(draggablePoint.pid)
|
|
1822
|
+
.listeners.fireDragStartEvent(e);
|
|
1823
|
+
else if (draggablePoint.typeName == "vertex") _self.vertices[draggablePoint.vindex].listeners.fireDragStartEvent(e);
|
|
1808
1824
|
}
|
|
1809
1825
|
_self.redraw();
|
|
1810
1826
|
}
|
|
@@ -1832,7 +1848,10 @@ export class PlotBoilerplate {
|
|
|
1832
1848
|
// not this one. So this tab will never receive any [Ctrl-down] events
|
|
1833
1849
|
// until next keypress; the implication is, that [Ctrl] would still
|
|
1834
1850
|
// considered to be pressed which is not true.
|
|
1835
|
-
if (this.keyHandler.isDown("alt") || this.keyHandler.isDown("spacebar")) {
|
|
1851
|
+
if (this.keyHandler && (this.keyHandler.isDown("alt") || this.keyHandler.isDown("spacebar"))) {
|
|
1852
|
+
if (!this.config.enablePan) {
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1836
1855
|
_self.setOffset(_self.draw.offset.clone().add(e.params.dragAmount));
|
|
1837
1856
|
_self.redraw();
|
|
1838
1857
|
} else {
|
|
@@ -1898,6 +1917,9 @@ export class PlotBoilerplate {
|
|
|
1898
1917
|
* @return {void}
|
|
1899
1918
|
**/
|
|
1900
1919
|
private mouseWheelHandler(e: XMouseEvent) {
|
|
1920
|
+
if (!this.config.enableZoom) {
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1901
1923
|
var zoomStep: number = 1.25; // Make configurable?
|
|
1902
1924
|
// CHANGED replaced _self by this
|
|
1903
1925
|
const _self: PlotBoilerplate = this;
|
|
@@ -2045,6 +2067,9 @@ export class PlotBoilerplate {
|
|
|
2045
2067
|
if (evt.touches.length == 1 && draggedElement) {
|
|
2046
2068
|
evt.preventDefault();
|
|
2047
2069
|
evt.stopPropagation();
|
|
2070
|
+
if (!touchDownPos || !touchMovePos) {
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
2048
2073
|
var rel: XYCoords = relPos({ x: evt.touches[0].clientX, y: evt.touches[0].clientY });
|
|
2049
2074
|
var trans: XYCoords = _self.transformMousePosition(rel.x, rel.y);
|
|
2050
2075
|
var diff: Vertex = new Vertex(_self.transformMousePosition(touchMovePos.x, touchMovePos.y)).difference(trans);
|
|
@@ -2067,6 +2092,9 @@ export class PlotBoilerplate {
|
|
|
2067
2092
|
}
|
|
2068
2093
|
touchMovePos = new Vertex(rel);
|
|
2069
2094
|
} else if (evt.touches.length == 2) {
|
|
2095
|
+
if (!this.config.enablePan) {
|
|
2096
|
+
return;
|
|
2097
|
+
}
|
|
2070
2098
|
// If at least two fingers touch and move, then change the draw offset (panning).
|
|
2071
2099
|
evt.preventDefault();
|
|
2072
2100
|
evt.stopPropagation();
|
|
@@ -2081,6 +2109,9 @@ export class PlotBoilerplate {
|
|
|
2081
2109
|
touchEnd: (evt: TouchEvent) => {
|
|
2082
2110
|
// Note: e.touches.length is 0 here
|
|
2083
2111
|
if (draggedElement && draggedElement.typeName == "vertex") {
|
|
2112
|
+
if (!touchDownPos) {
|
|
2113
|
+
return;
|
|
2114
|
+
}
|
|
2084
2115
|
var draggingVertex: Vertex = _self.vertices[draggedElement.vindex];
|
|
2085
2116
|
var fakeEvent: VertEvent = {
|
|
2086
2117
|
isTouchEvent: true,
|
|
@@ -2112,9 +2143,17 @@ export class PlotBoilerplate {
|
|
|
2112
2143
|
multiTouchStartScale = null;
|
|
2113
2144
|
},
|
|
2114
2145
|
pinch: (evt: TouchPinchEvent) => {
|
|
2146
|
+
if (!this.config.enableZoom) {
|
|
2147
|
+
return;
|
|
2148
|
+
}
|
|
2149
|
+
const touchItem0 = evt.touches.item(0);
|
|
2150
|
+
const touchItem1 = evt.touches.item(1);
|
|
2151
|
+
if (!evt.touches || !multiTouchStartScale || !touchItem0 || !touchItem1) {
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2115
2154
|
// For pinching there must be at least two touch items
|
|
2116
|
-
const fingerA: Vertex = new Vertex(
|
|
2117
|
-
const fingerB: Vertex = new Vertex(
|
|
2155
|
+
const fingerA: Vertex = new Vertex(touchItem0.clientX, touchItem0.clientY);
|
|
2156
|
+
const fingerB: Vertex = new Vertex(touchItem1.clientX, touchItem1.clientY);
|
|
2118
2157
|
const center: Vertex = new Line(fingerA, fingerB).vertAt(0.5);
|
|
2119
2158
|
_self.setZoom(multiTouchStartScale.x * evt.zoom, multiTouchStartScale.y * evt.zoom, center);
|
|
2120
2159
|
_self.redraw();
|
|
@@ -2192,7 +2231,45 @@ export class PlotBoilerplate {
|
|
|
2192
2231
|
* @param {Object} extension
|
|
2193
2232
|
* @return {Object} base extended by the new attributes.
|
|
2194
2233
|
**/
|
|
2195
|
-
safeMergeByKeys:
|
|
2234
|
+
safeMergeByKeys: <KeyType extends string | number | symbol, ValueType>(
|
|
2235
|
+
base: Record<KeyType, ValueType>,
|
|
2236
|
+
extension: Record<KeyType, ValueType>
|
|
2237
|
+
): Record<KeyType, ValueType> => {
|
|
2238
|
+
for (var k in extension) {
|
|
2239
|
+
if (!extension.hasOwnProperty(k)) {
|
|
2240
|
+
continue;
|
|
2241
|
+
}
|
|
2242
|
+
if (base.hasOwnProperty(k)) {
|
|
2243
|
+
const typ = typeof base[k];
|
|
2244
|
+
const extVal = extension[k];
|
|
2245
|
+
try {
|
|
2246
|
+
if (typ == "boolean") {
|
|
2247
|
+
if (typeof extVal === "string") base[k] = Boolean(!!JSON.parse(extVal)) as unknown as ValueType;
|
|
2248
|
+
else base[k] = extVal as ValueType;
|
|
2249
|
+
} else if (typ == "number") {
|
|
2250
|
+
if (typeof extVal === "string") base[k] = Number(JSON.parse(extVal) * 1) as unknown as ValueType;
|
|
2251
|
+
else base[k] = extension[k] as ValueType;
|
|
2252
|
+
} else if (typ == "function" && typeof extVal == "function") {
|
|
2253
|
+
base[k] = extension[k];
|
|
2254
|
+
} else {
|
|
2255
|
+
// Probably a sting
|
|
2256
|
+
base[k] = extension[k];
|
|
2257
|
+
}
|
|
2258
|
+
} catch (e) {
|
|
2259
|
+
console.error("error in key ", k, extVal, e);
|
|
2260
|
+
}
|
|
2261
|
+
} else {
|
|
2262
|
+
base[k] = extension[k];
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
return base;
|
|
2266
|
+
},
|
|
2267
|
+
|
|
2268
|
+
/*
|
|
2269
|
+
__safeMergeByKeys: <KeyType extends string | number | symbol, ValueType extends boolean | number | string | Function>(
|
|
2270
|
+
base: Record<KeyType, ValueType>,
|
|
2271
|
+
extension: Record<KeyType, string>
|
|
2272
|
+
): Record<KeyType, ValueType> => {
|
|
2196
2273
|
for (var k in extension) {
|
|
2197
2274
|
if (!extension.hasOwnProperty(k)) continue;
|
|
2198
2275
|
if (base.hasOwnProperty(k)) {
|
|
@@ -2211,6 +2288,7 @@ export class PlotBoilerplate {
|
|
|
2211
2288
|
}
|
|
2212
2289
|
return base;
|
|
2213
2290
|
},
|
|
2291
|
+
*()
|
|
2214
2292
|
|
|
2215
2293
|
/**
|
|
2216
2294
|
* A helper function to scale elements (usually the canvas) using CSS.
|
|
@@ -2224,8 +2302,10 @@ export class PlotBoilerplate {
|
|
|
2224
2302
|
**/
|
|
2225
2303
|
setCSSscale: (element: HTMLElement | SVGElement, scaleX: number, scaleY: number): void => {
|
|
2226
2304
|
element.style["transform-origin"] = "0 0";
|
|
2227
|
-
if (scaleX == 1.0 && scaleY == 1.0)
|
|
2228
|
-
|
|
2305
|
+
if (scaleX == 1.0 && scaleY == 1.0) {
|
|
2306
|
+
// element.style.transform = null;
|
|
2307
|
+
element.style.removeProperty("transform");
|
|
2308
|
+
} else element.style.transform = "scale(" + scaleX + "," + scaleY + ")";
|
|
2229
2309
|
},
|
|
2230
2310
|
|
|
2231
2311
|
// A helper for fetching data from objects.
|
|
@@ -2271,7 +2351,7 @@ export class PlotBoilerplate {
|
|
|
2271
2351
|
* @param {boolean} fallback - A default value if the key does not exist.
|
|
2272
2352
|
* @return {boolean}
|
|
2273
2353
|
**/
|
|
2274
|
-
bool: (obj: any, key: string, fallback: boolean) => {
|
|
2354
|
+
bool: (obj: any, key: string, fallback: boolean | null) => {
|
|
2275
2355
|
if (!obj.hasOwnProperty(key)) return fallback;
|
|
2276
2356
|
if (typeof obj[key] == "boolean") return obj[key];
|
|
2277
2357
|
else {
|