wt-huatu 1.3.4

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.
Files changed (110) hide show
  1. package/Readme.md +223 -0
  2. package/component/HuatuComponentCache.js +89 -0
  3. package/component/HuatuComponentDef.js +249 -0
  4. package/component/HuatuComponentPlacement.js +172 -0
  5. package/component/HuatuComponentPort.js +58 -0
  6. package/component/IHuatuComponent.js +2 -0
  7. package/for-node/node-cli-funcs.js +36 -0
  8. package/geometry/AngDeg.js +68 -0
  9. package/geometry/Angle.js +77 -0
  10. package/geometry/Bezier.js +623 -0
  11. package/geometry/Circle.js +143 -0
  12. package/geometry/Ellipse.js +546 -0
  13. package/geometry/EllipticalArc.js +337 -0
  14. package/geometry/IGeometry.js +1 -0
  15. package/geometry/Intersection.js +601 -0
  16. package/geometry/Line.js +136 -0
  17. package/geometry/LineSeg.js +179 -0
  18. package/geometry/Point.js +88 -0
  19. package/geometry/PolyLine.js +97 -0
  20. package/geometry/Polygon.js +122 -0
  21. package/geometry/Rect.js +149 -0
  22. package/geometry/Ring.js +91 -0
  23. package/geometry/SegmentModel.js +206 -0
  24. package/geometry/Triangle.js +168 -0
  25. package/geometry/Vector.js +92 -0
  26. package/huatu/Huatu.js +4279 -0
  27. package/huatu/HuatuAngleMark.js +400 -0
  28. package/huatu/HuatuBlock.js +26 -0
  29. package/huatu/HuatuClipPath.js +29 -0
  30. package/huatu/HuatuCompDrawing.js +77 -0
  31. package/huatu/HuatuDimMark.js +579 -0
  32. package/huatu/HuatuDrawing.js +185 -0
  33. package/huatu/HuatuDrawingGroup.js +52 -0
  34. package/huatu/HuatuJob.js +347 -0
  35. package/huatu/HuatuLabel.js +311 -0
  36. package/huatu/HuatuLabelStyle.js +190 -0
  37. package/huatu/HuatuLayer.js +10 -0
  38. package/huatu/HuatuMarker.js +209 -0
  39. package/huatu/HuatuParser.js +435 -0
  40. package/huatu/HuatuPath.js +131 -0
  41. package/huatu/HuatuPathSeries.js +51 -0
  42. package/huatu/HuatuPattern.js +54 -0
  43. package/huatu/HuatuPoint.js +20 -0
  44. package/huatu/HuatuPointSeries.js +67 -0
  45. package/huatu/HuatuStyle.js +394 -0
  46. package/huatu/HuatuSymbol.js +81 -0
  47. package/huatu/HuatuSymbolArray.js +235 -0
  48. package/huatu/HuatuTransform.js +113 -0
  49. package/huatu/HuatuVar.js +17 -0
  50. package/huatu/HuatuYml.js +271 -0
  51. package/huatu/IHuatu.js +2 -0
  52. package/huatu/IHuatuYml.js +1 -0
  53. package/huatu/IndentParsingStack.js +82 -0
  54. package/index.d.ts +1747 -0
  55. package/index.js +247 -0
  56. package/math/Complex.js +72 -0
  57. package/math/Matrix.js +31 -0
  58. package/math/NumberRange.js +38 -0
  59. package/math/VarFuncs.js +24 -0
  60. package/math/WtMath.js +217 -0
  61. package/package.json +34 -0
  62. package/path/PathBezier2.js +75 -0
  63. package/path/PathBezier3.js +89 -0
  64. package/path/PathCircle.js +82 -0
  65. package/path/PathConfig.js +81 -0
  66. package/path/PathContinuousSegmentedPath.js +390 -0
  67. package/path/PathEllipse.js +99 -0
  68. package/path/PathEllipticalArc.js +111 -0
  69. package/path/PathGeneric.js +59 -0
  70. package/path/PathLine.js +75 -0
  71. package/path/PathLines.js +46 -0
  72. package/path/PathPolygon.js +142 -0
  73. package/path/PathPolyline.js +266 -0
  74. package/path/PathRect.js +125 -0
  75. package/path/PathRing.js +51 -0
  76. package/path/PathSegmentedPath.js +199 -0
  77. package/path/PathTriangle.js +90 -0
  78. package/presets/marker.js +37 -0
  79. package/presets/pattern.js +85 -0
  80. package/shape/SvgShapeArrowHead.js +92 -0
  81. package/shape/SvgShapeCircle.js +26 -0
  82. package/shape/SvgShapeCross.js +43 -0
  83. package/shape/SvgShapeEllipse.js +28 -0
  84. package/shape/SvgShapeGeneric.js +50 -0
  85. package/shape/SvgShapeHeart.js +40 -0
  86. package/shape/SvgShapeIsoscelesTriangle.js +52 -0
  87. package/shape/SvgShapePolygon.js +39 -0
  88. package/shape/SvgShapeRect.js +26 -0
  89. package/shape/SvgShapeRhombus.js +27 -0
  90. package/shape/SvgShapeStar.js +103 -0
  91. package/svg/ISvg.js +1 -0
  92. package/svg/SvgBBox.js +149 -0
  93. package/svg/SvgConstants.js +14 -0
  94. package/svg/SvgGradient.js +97 -0
  95. package/svg/SvgMarker.js +221 -0
  96. package/svg/SvgPattern.js +228 -0
  97. package/svg/SvgPoint.js +33 -0
  98. package/svg/SvgPointSet.js +41 -0
  99. package/templates/lines.js +35 -0
  100. package/tools/gen-id.js +2 -0
  101. package/tools/html.js +2 -0
  102. package/tools/katex.js +75 -0
  103. package/tools/rand-str.js +122 -0
  104. package/tools/regex.js +15 -0
  105. package/tools/utils.js +438 -0
  106. package/tools/webColor.js +700 -0
  107. package/wire/HuatuNet.js +22 -0
  108. package/wire/HuatuWire.js +415 -0
  109. package/wire/HuatuWireDrawing.js +17 -0
  110. package/wire/IHuatuWire.js +1 -0
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The class implementation of SVG Line.
3
+ */
4
+ import { PathGeneric } from "./PathGeneric.js";
5
+ import { SvgPoint } from "../svg/SvgPoint.js";
6
+ import { SvgBBox } from "../svg/SvgBBox.js";
7
+ import { SvgCsts } from "../svg/SvgConstants.js";
8
+ import LineSeg from "../geometry/LineSeg.js";
9
+ export class PathLine extends PathGeneric {
10
+ pStart;
11
+ pEnd;
12
+ auxModel;
13
+ isStartSeg = false;
14
+ closePath = false;
15
+ constructor(x1, y1, x2, y2) {
16
+ super();
17
+ this.pathTag = "line";
18
+ this.pStart = new SvgPoint(x1, y1);
19
+ this.pEnd = new SvgPoint(x2, y2);
20
+ this.realPoints.push(this.pStart, this.pEnd);
21
+ this.bbox = SvgBBox.fromMaxMins(x1, x2, y1, y2);
22
+ this.auxModel = new LineSeg(this.pStart, this.pEnd);
23
+ this.models = [this.auxModel];
24
+ }
25
+ getPathAttr() {
26
+ return [
27
+ `x1="${this.pStart.x.toFixed(SvgCsts.COORS_DIGITS)}"`,
28
+ `y1="${this.pStart.y.toFixed(SvgCsts.COORS_DIGITS)}"`,
29
+ `x2="${this.pEnd.x.toFixed(SvgCsts.COORS_DIGITS)}"`,
30
+ `y2="${this.pEnd.y.toFixed(SvgCsts.COORS_DIGITS)}"`
31
+ ];
32
+ }
33
+ buildPathData() {
34
+ return `M${this.pStart.toCoors()} L${this.pEnd.toCoors()}`;
35
+ }
36
+ getLength() {
37
+ return Math.sqrt(this.bbox.w ** 2 + this.bbox.h ** 2);
38
+ }
39
+ /* Functions for the role as Path Segment */
40
+ buildSegPathData() {
41
+ return (this.isStartSeg ? `M${this.pStart.toCoors()}` : "")
42
+ + ` L${this.pEnd.toCoors()}`
43
+ + (this.closePath ? " z" : "");
44
+ }
45
+ moveBy(dx, dy) {
46
+ const ls = new LineSeg(this.pStart, this.pEnd).moveBy(dx, dy);
47
+ return PathLine.fromLineSeg(ls);
48
+ }
49
+ rotateBy(rotDeg, at) {
50
+ const ls = new LineSeg(this.pStart, this.pEnd).rotateBy(rotDeg, at);
51
+ return PathLine.fromLineSeg(ls);
52
+ }
53
+ reverse() {
54
+ return PathLine.fromEnds(this.pEnd, this.pStart);
55
+ }
56
+ duplicateTo(distance, dir) {
57
+ dir = dir === "in" ? "right"
58
+ : dir === "out" ? "left"
59
+ : dir;
60
+ return PathLine.fromLineSeg(this.auxModel.duplicateTo(distance, dir));
61
+ }
62
+ makeSubPath(pStart, pEnd) {
63
+ let ls = this.auxModel.makeSubSeg(pStart, pEnd);
64
+ if (!ls)
65
+ return undefined;
66
+ return PathLine.fromLineSeg(ls);
67
+ }
68
+ /** Static methods */
69
+ static fromEnds(pStart, pEnd) {
70
+ return new PathLine(pStart.x, pStart.y, pEnd.x, pEnd.y);
71
+ }
72
+ static fromLineSeg(seg) {
73
+ return new PathLine(seg.pStart.x, seg.pStart.y, seg.pEnd.x, seg.pEnd.y);
74
+ }
75
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The class implementation of SVG Line.
3
+ */
4
+ import { PathGeneric } from "./PathGeneric.js";
5
+ import { SvgPoint } from "../svg/SvgPoint.js";
6
+ import LineSeg from "../geometry/LineSeg.js";
7
+ export class PathLines extends PathGeneric {
8
+ lines = [];
9
+ constructor() {
10
+ super();
11
+ }
12
+ addLine(pStart, pEnd) {
13
+ const ls = new LineSeg(pStart, pEnd);
14
+ this.lines.push(ls);
15
+ this.models.push(ls);
16
+ }
17
+ addLineSegs(...segs) {
18
+ this.lines.push(...segs);
19
+ this.models.push(...segs);
20
+ }
21
+ getPathAttr() {
22
+ return [`d="${this.buildPathData()}"`];
23
+ }
24
+ buildPathData() {
25
+ if (this.lines.length < 1)
26
+ return "";
27
+ return this.lines
28
+ .map(l => `M${SvgPoint.toCoors(l.pStart)} L${SvgPoint.toCoors(l.pEnd)}`)
29
+ .join(" ");
30
+ ;
31
+ }
32
+ getLength() {
33
+ return this.lines.map(l => l.len)
34
+ .reduce((sum, len) => sum + len, 0);
35
+ }
36
+ /** Static methods */
37
+ static fromLineSegments(...lines) {
38
+ const path = new PathLines();
39
+ if (lines.length < 1)
40
+ return path;
41
+ lines.forEach(l => {
42
+ path.addLine(l.pStart, l.pEnd);
43
+ });
44
+ return path;
45
+ }
46
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * The class implementation of SVG Line.
3
+ */
4
+ import Polygon from "../geometry/Polygon.js";
5
+ import { PathGeneric } from "./PathGeneric.js";
6
+ import { SvgPoint } from "../svg/SvgPoint.js";
7
+ import { PathEllipticalArc } from "./PathEllipticalArc.js";
8
+ import { SvgBBox } from "../svg/SvgBBox.js";
9
+ import { WtMath } from "../math/WtMath.js";
10
+ import { PathLine } from "./PathLine.js";
11
+ export class PathPolygon extends PathGeneric {
12
+ points;
13
+ polygon;
14
+ angArcRadius = 0;
15
+ constructor(...points) {
16
+ super();
17
+ this.points = points.map(p => new SvgPoint(p.x, p.y));
18
+ this.polygon = new Polygon(...this.points);
19
+ this.realPoints.push(...this.points);
20
+ const xs = points.map(p => p.x);
21
+ const ys = points.map(p => p.y);
22
+ this.bbox = SvgBBox.fromMaxMins(Math.min(...xs), Math.max(...xs), Math.min(...ys), Math.max(...ys));
23
+ this.models = [this.polygon];
24
+ }
25
+ setAngArcRadius(radius) {
26
+ this.angArcRadius = radius;
27
+ this.pathTag = "path";
28
+ }
29
+ removeAngArcRadius() {
30
+ this.angArcRadius = 0;
31
+ this.pathTag = "polygon";
32
+ }
33
+ getPathAttr() {
34
+ const attr = (this.pathTag === "polygon")
35
+ ? `points="${this.points.map(p => p.toCoors()).join(" ")}"`
36
+ : `d="${this.buildPathData()}"`;
37
+ return [attr];
38
+ }
39
+ buildPathData(angArcRadius) {
40
+ if (!this.polygon.isValid)
41
+ return "";
42
+ const radius = angArcRadius || this.angArcRadius;
43
+ // No radius
44
+ if (typeof radius === "number" && WtMath.isZero(radius)) {
45
+ return this.points.map((p, idx) => `${idx === 0 ? 'M' : 'L'}${p.toCoors()}`).join(" ") + ' z';
46
+ }
47
+ const angRadius = WtMath.expandRadius(this.points.length, radius);
48
+ return this.polygon.getAngles(true)
49
+ .map((a, idx) => {
50
+ const aps = a.getArcPoints(angRadius[idx]);
51
+ const mOrL = idx === 0 ? "M" : "L";
52
+ const ellArc = PathEllipticalArc.fromStartEndPoints(aps.pArcStart, aps.pArcEnd, aps.r, aps.r, false, aps.cw);
53
+ return aps.hasArc
54
+ ? `${mOrL}${SvgPoint.toCoors(aps.pArcStart)} ${ellArc.buildPureArc()}`
55
+ : `${mOrL}${SvgPoint.toCoors(aps.pStart)}`;
56
+ }).join(" ") + " z";
57
+ }
58
+ getLength() {
59
+ return this.polygon.angles.map(a => a.lineOA.len).reduce((sum, l) => sum + l, 0);
60
+ }
61
+ toAngleArced(radius) {
62
+ if (!radius)
63
+ return this;
64
+ // Copy the polygon to make a new Path
65
+ const path = PathPolygon.fromPolygon(this.polygon);
66
+ path.setAngArcRadius(radius);
67
+ return path;
68
+ }
69
+ toPathSegs(radius) {
70
+ if (!this.polygon.isValid)
71
+ return [];
72
+ const segments = [];
73
+ // No radius
74
+ if (!radius ||
75
+ (typeof radius === "number" && WtMath.isZero(radius)) ||
76
+ (Array.isArray(radius) && radius.length < 1)) {
77
+ this.points.forEach((p, idx) => {
78
+ if (idx < this.points.length - 1) {
79
+ segments.push(PathLine.fromEnds(this.points[idx], this.points[idx + 1]));
80
+ }
81
+ else {
82
+ const seg = PathLine.fromEnds(this.points[idx], this.points[0]);
83
+ seg.closePath = true;
84
+ segments.push(seg);
85
+ }
86
+ });
87
+ return segments;
88
+ }
89
+ // Has radius
90
+ const angRadius = WtMath.expandRadius(this.points.length, radius);
91
+ const angles = this.polygon.getAngles(true);
92
+ let lastPoint = angles[0].pA;
93
+ angles.forEach((a, idx) => {
94
+ const aps = a.getArcPoints(angRadius[idx]);
95
+ const ellArc = PathEllipticalArc.fromStartEndPoints(aps.pArcStart, aps.pArcEnd, aps.r, aps.r, false, aps.cw);
96
+ if (aps.hasArc) {
97
+ segments.push(PathLine.fromEnds(lastPoint, aps.pArcStart), ellArc);
98
+ lastPoint = aps.pArcEnd;
99
+ }
100
+ else {
101
+ segments.push(PathLine.fromEnds(lastPoint, aps.pStart));
102
+ lastPoint = aps.pStart;
103
+ }
104
+ });
105
+ const lastSeg = PathLine.fromEnds(lastPoint, angles[0].pA);
106
+ lastSeg.closePath = true;
107
+ segments.push(lastSeg);
108
+ return segments;
109
+ }
110
+ toCwOrCcw(cw) {
111
+ // If it is invalid, then return itself, because it will not make anything since it is invalid
112
+ // And if cw/ccw not specified, it will use the default case
113
+ if (!this.polygon.isValid || !cw)
114
+ return this;
115
+ // Judge if the original polygon is cw or ccw, and reverse points order accordingly
116
+ let isCw = this.polygon.isCw(), pts = (cw === "cw" && isCw) || (cw === "ccw" && !isCw)
117
+ ? this.points
118
+ : this.points.map((p, i) => this.points[i === 0 ? 0 : this.points.length - i]);
119
+ return new PathPolygon(...pts);
120
+ }
121
+ toPathSegments(radius, cw) {
122
+ if (!this.polygon.isValid)
123
+ return [];
124
+ return this.toCwOrCcw(cw).toPathSegs(radius);
125
+ }
126
+ duplicateTo(distance, dir) {
127
+ if (!this.polygon.isValid)
128
+ return undefined;
129
+ let isCw = this.polygon.isCw();
130
+ dir = dir === "in" ? (isCw ? "right" : "left")
131
+ : dir === "out" ? (isCw ? "left" : "right")
132
+ : dir;
133
+ const plg = this.polygon.duplicateTo(distance, dir);
134
+ return plg ? PathPolygon.fromPolygon(plg) : undefined;
135
+ }
136
+ // Static methods
137
+ static fromPolygon(plg) {
138
+ if (plg.isValid)
139
+ return new PathPolygon(...plg.points);
140
+ return undefined;
141
+ }
142
+ }
@@ -0,0 +1,266 @@
1
+ /**
2
+ * The class implementation of SVG Polyline.
3
+ */
4
+ import { PathGeneric } from "./PathGeneric.js";
5
+ import { SvgPoint } from "../svg/SvgPoint.js";
6
+ import { PathEllipticalArc } from "./PathEllipticalArc.js";
7
+ import { SvgBBox } from "../svg/SvgBBox.js";
8
+ import Line from "../geometry/Line.js";
9
+ import LineSeg from "../geometry/LineSeg.js";
10
+ import { WtMath } from "../math/WtMath.js";
11
+ import { PathLine } from "./PathLine.js";
12
+ import { AngDeg } from "../geometry/AngDeg.js";
13
+ import { fitNumInRange } from "../tools/utils.js";
14
+ import { PathBezier3 } from "./PathBezier3.js";
15
+ import { PathSegmentedPath } from "./PathSegmentedPath.js";
16
+ import Angle from "../geometry/Angle.js";
17
+ import { PathPolygon } from "./PathPolygon.js";
18
+ import PolyLine from "../geometry/PolyLine.js";
19
+ import Point from "../geometry/Point.js";
20
+ import { SegmentModel } from "../geometry/SegmentModel.js";
21
+ export class PathPolyline extends PathGeneric {
22
+ static SMOOTH_FACTOR_DEFAULT = 0.44;
23
+ static SMOOTH_FACTOR_MIN = 0.02;
24
+ static SMOOTH_FACTOR_MAX = 0.88;
25
+ points;
26
+ close = false;
27
+ angArcRadius = 0;
28
+ constructor(...points) {
29
+ super();
30
+ this.pathTag = "polyline";
31
+ this.points = points.map(p => new SvgPoint(p.x, p.y));
32
+ this.realPoints.push(...this.points);
33
+ const xs = points.map(p => p.x);
34
+ const ys = points.map(p => p.y);
35
+ this.bbox = SvgBBox.fromMaxMins(Math.min(...xs), Math.max(...xs), Math.min(...ys), Math.max(...ys));
36
+ this.models = this.toPathSegments().map(s => s.auxModel);
37
+ }
38
+ makeClose() {
39
+ this.close = true;
40
+ this.pathTag = "path";
41
+ }
42
+ getPathAttr() {
43
+ if (this.pathTag === "polyline" && !this.close) {
44
+ return [`points="${this.points.map(p => p.toCoors()).join(" ")}"`];
45
+ }
46
+ else {
47
+ return [`d="${this.buildPathData()}"`];
48
+ }
49
+ }
50
+ buildPathData(angArcRadius) {
51
+ const radius = angArcRadius || this.angArcRadius;
52
+ // No radius
53
+ if (typeof radius === "number" && WtMath.isZero(radius)) {
54
+ return this.points.map((p, idx) => `${idx === 0 ? "M" : "L"}${p.toCoors()}`).join(" ")
55
+ + (this.close ? " z" : "");
56
+ }
57
+ if (this.close) {
58
+ return new PathPolygon(...this.points).toAngleArced().buildPathData(radius);
59
+ }
60
+ const dedupPoints = Point.deDupRepeated(this.points, SegmentModel.DE_DUP_THRESHOLD);
61
+ const angRadius = WtMath.expandRadius(dedupPoints.length - 2, radius);
62
+ const middleArcs = dedupPoints.reduce((a, p, i) => {
63
+ if (i < dedupPoints.length - 2) {
64
+ const pStart = new LineSeg(dedupPoints[i], dedupPoints[i + 1]).middle;
65
+ const pEnd = new LineSeg(dedupPoints[i + 1], dedupPoints[i + 2]).middle;
66
+ return [...a, new Angle(pStart, dedupPoints[i + 1], pEnd)];
67
+ }
68
+ else {
69
+ return a;
70
+ }
71
+ }, [])
72
+ .map((a, idx) => {
73
+ const aps = a.getArcPoints(angRadius[idx]);
74
+ const ellArc = PathEllipticalArc.fromStartEndPoints(aps.pArcStart, aps.pArcEnd, aps.r, aps.r, false, aps.cw);
75
+ return aps.hasArc
76
+ ? `L${SvgPoint.toCoors(aps.pArcStart)} ${ellArc.buildPureArc()}`
77
+ : `L${SvgPoint.toCoors(aps.pStart)}`;
78
+ })
79
+ .join(" ");
80
+ return `M${SvgPoint.toCoors(dedupPoints[0])} ${middleArcs} L${SvgPoint.toCoors(dedupPoints[dedupPoints.length - 1])}`;
81
+ }
82
+ getLength() {
83
+ return this.points
84
+ .map((p, idx) => idx === 0 ? 0 : new LineSeg(this.points[idx - 1], p).len)
85
+ .reduce((sum, l) => sum + l, 0);
86
+ }
87
+ toAngleArced(radius) {
88
+ if (!radius)
89
+ return this;
90
+ // Re-build the polyline and add radius
91
+ const path = new PathPolyline(...this.points);
92
+ path.angArcRadius = radius;
93
+ path.pathTag = "path";
94
+ return path;
95
+ }
96
+ toSmoothed(factor = 3, mode = "normal") {
97
+ const path = new PathSegmentedPath();
98
+ if (this.points.length < 2)
99
+ return path;
100
+ if (this.points.length < 3) {
101
+ path.addSeg(PathLine.fromEnds(this.points[0], this.points[1]));
102
+ return path;
103
+ }
104
+ ;
105
+ // Judge Factor
106
+ factor = PathPolyline.calcSmoothFactor(factor);
107
+ // Translate the polyline points into line segments
108
+ const segs = [];
109
+ let i = 0;
110
+ let pStart = undefined;
111
+ while (i < this.points.length - 1) {
112
+ pStart = pStart ?? this.points[i];
113
+ const seg = new LineSeg(pStart, this.points[i + 1]);
114
+ if (!WtMath.isZero(seg.len)) {
115
+ segs.push(seg);
116
+ pStart = undefined;
117
+ }
118
+ i++;
119
+ }
120
+ if (segs.length < 2) {
121
+ path.addSeg(PathLine.fromLineSeg(segs[0]));
122
+ return path;
123
+ }
124
+ const pathSegs = [];
125
+ i = 0;
126
+ let angAvg = 0;
127
+ while (i < segs.length) {
128
+ const seg = segs[i];
129
+ let angStart = seg.ang, angEnd = AngDeg.fitAng(seg.ang - AngDeg.DEG180);
130
+ if (i === 0) {
131
+ angAvg = AngDeg.fitAng(seg.ang + segs[i + 1].ang) / 2;
132
+ if (AngDeg.absAng(angEnd, angAvg) > AngDeg.DEG90) { // Make acute angle
133
+ angEnd = AngDeg.fitAng(angAvg - AngDeg.DEG180);
134
+ }
135
+ else {
136
+ angEnd = angAvg;
137
+ }
138
+ }
139
+ else if (i === segs.length - 1) {
140
+ if (AngDeg.absAng(angStart, angAvg) > AngDeg.DEG90) {
141
+ angStart = AngDeg.fitAng(angAvg - AngDeg.DEG180);
142
+ }
143
+ else {
144
+ angStart = angAvg;
145
+ }
146
+ }
147
+ else {
148
+ if (AngDeg.absAng(angStart, angAvg) > AngDeg.DEG90) {
149
+ angStart = AngDeg.fitAng(angAvg - AngDeg.DEG180);
150
+ }
151
+ else {
152
+ angStart = angAvg;
153
+ }
154
+ // Update Average Angle
155
+ angAvg = AngDeg.fitAng(seg.ang + segs[i + 1].ang) / 2;
156
+ if (AngDeg.absAng(angEnd, angAvg) > AngDeg.DEG90) { // Make acute angle
157
+ angEnd = AngDeg.fitAng(angAvg - AngDeg.DEG180);
158
+ }
159
+ else {
160
+ angEnd = angAvg;
161
+ }
162
+ }
163
+ pathSegs.push({
164
+ pStart: seg.pStart,
165
+ pEnd: seg.pEnd,
166
+ angStart,
167
+ angEnd,
168
+ angLine: seg.ang,
169
+ isVert: WtMath.isZero(seg.pStart.x - seg.pEnd.x),
170
+ isHori: WtMath.isZero(seg.pStart.y - seg.pEnd.y),
171
+ segLen: seg.len
172
+ });
173
+ i++;
174
+ }
175
+ if (mode === "normal") {
176
+ path.addSeg(...pathSegs.map(seg => new PathBezier3(seg.pStart, new Line(seg.pStart, seg.angStart).getPointByLen(factor * seg.segLen / 2), new Line(seg.pEnd, seg.angEnd).getPointByLen(factor * seg.segLen / 2), seg.pEnd)));
177
+ }
178
+ else { // "xy mode"
179
+ i = 0;
180
+ while (i < pathSegs.length) {
181
+ let seg = pathSegs[i];
182
+ if (seg.isHori || seg.isVert) {
183
+ path.addSeg(PathLine.fromEnds(seg.pStart, seg.pEnd));
184
+ }
185
+ else {
186
+ let angStartUsed = i > 0 && (pathSegs[i - 1].isVert || pathSegs[i - 1].isHori)
187
+ ? pathSegs[i - 1].angLine
188
+ : seg.angStart;
189
+ let angEndUsed = i < pathSegs.length - 1 && (pathSegs[i + 1].isVert || pathSegs[i + 1].isHori)
190
+ ? AngDeg.fitAng(pathSegs[i + 1].angLine - AngDeg.DEG180)
191
+ : seg.angEnd;
192
+ let smoothLen = Math.min(Math.abs(seg.pStart.x - seg.pEnd.x), Math.abs(seg.pStart.y - seg.pEnd.y));
193
+ path.addSeg(new PathBezier3(seg.pStart, new Line(seg.pStart, angStartUsed).getPointByLen(factor * smoothLen), new Line(seg.pEnd, angEndUsed).getPointByLen(factor * smoothLen), seg.pEnd));
194
+ }
195
+ i++;
196
+ }
197
+ }
198
+ return path;
199
+ }
200
+ toPathSegments(radius) {
201
+ const segments = [];
202
+ // No radius
203
+ if (!radius ||
204
+ (typeof radius === "number" && WtMath.isZero(radius)) ||
205
+ (Array.isArray(radius) && radius.length < 1)) {
206
+ this.points.forEach((p, idx) => {
207
+ if (idx < this.points.length - 1) {
208
+ segments.push(PathLine.fromEnds(this.points[idx], this.points[idx + 1]));
209
+ }
210
+ });
211
+ if (this.close) {
212
+ segments.push(PathLine.fromEnds(this.points[this.points.length - 1], this.points[0]));
213
+ }
214
+ return segments;
215
+ }
216
+ const dedupPoints = Point.deDupRepeated(this.points, SegmentModel.DE_DUP_THRESHOLD);
217
+ const angRadius = WtMath.expandRadius(dedupPoints.length - 2, radius);
218
+ let lastPoint = dedupPoints[0];
219
+ dedupPoints.reduce((a, p, i) => {
220
+ if (i < dedupPoints.length - 2) {
221
+ const pStart = new LineSeg(dedupPoints[i], dedupPoints[i + 1]).middle;
222
+ const pEnd = new LineSeg(dedupPoints[i + 1], dedupPoints[i + 2]).middle;
223
+ return [...a, new Angle(pStart, dedupPoints[i + 1], pEnd)];
224
+ }
225
+ else {
226
+ return a;
227
+ }
228
+ }, [])
229
+ .forEach((a, idx) => {
230
+ const aps = a.getArcPoints(angRadius[idx]);
231
+ const ellArc = PathEllipticalArc.fromStartEndPoints(aps.pArcStart, aps.pArcEnd, aps.r, aps.r, false, aps.cw);
232
+ if (aps.hasArc) {
233
+ segments.push(PathLine.fromEnds(lastPoint, aps.pArcStart), ellArc);
234
+ lastPoint = aps.pArcEnd;
235
+ }
236
+ else {
237
+ segments.push(PathLine.fromEnds(lastPoint, aps.pStart));
238
+ lastPoint = aps.pStart;
239
+ }
240
+ });
241
+ segments.push(PathLine.fromEnds(lastPoint, dedupPoints[dedupPoints.length - 1]));
242
+ return segments;
243
+ }
244
+ duplicateTo(distance, dir) {
245
+ const pln = new PolyLine(...this.points);
246
+ dir = this.points.length < 3
247
+ ? (dir === "in" ? "right"
248
+ : dir === "out" ? "left"
249
+ : dir)
250
+ : (dir === "in" ? (pln.isCw() ? "right" : "left")
251
+ : dir === "out" ? (pln.isCw() ? "left" : "right")
252
+ : dir);
253
+ const pln2 = pln.duplicateTo(distance, dir);
254
+ return pln2 ? PathPolyline.fromPolyLine(pln2) : undefined;
255
+ }
256
+ // Static Methods
257
+ static fromPolyLine(line) {
258
+ if (!line.isValid)
259
+ return undefined;
260
+ return new PathPolyline(...line.points);
261
+ }
262
+ static calcSmoothFactor(val) {
263
+ const input = fitNumInRange(val, 1, 10);
264
+ return (input - 1) * (PathPolyline.SMOOTH_FACTOR_MAX - PathPolyline.SMOOTH_FACTOR_MIN) / (10 - 1) + PathPolyline.SMOOTH_FACTOR_MIN;
265
+ }
266
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * The class implementation of SVG Line.
3
+ */
4
+ import { WtMath } from "../math/WtMath.js";
5
+ import { SvgCsts } from "../svg/SvgConstants.js";
6
+ import { SvgPoint } from "../svg/SvgPoint.js";
7
+ import { SvgBBox } from "../svg/SvgBBox.js";
8
+ import Rect from "../geometry/Rect.js";
9
+ import { Bezier3 } from "../geometry/Bezier.js";
10
+ import { PathGeneric } from "./PathGeneric.js";
11
+ import { PathSegmentedPath } from "./PathSegmentedPath.js";
12
+ import { PathBezier3 } from "./PathBezier3.js";
13
+ import { PathLine } from "./PathLine.js";
14
+ import { PathPolygon } from "./PathPolygon.js";
15
+ export class PathRect extends PathGeneric {
16
+ width = 0;
17
+ height = 0;
18
+ anchor = new SvgPoint(0, 0);
19
+ radius = 0;
20
+ auxRect;
21
+ constructor() {
22
+ // Please Note that the constructor will not be used directly, please use the static fromXXXX methods
23
+ super();
24
+ this.pathTag = "rect";
25
+ }
26
+ getPathAttr() {
27
+ const attrs = [
28
+ `x="${this.anchor.x.toFixed(SvgCsts.COORS_DIGITS)}"`,
29
+ `y="${this.anchor.y.toFixed(SvgCsts.COORS_DIGITS)}"`,
30
+ `width="${this.width.toFixed(SvgCsts.COORS_DIGITS)}"`,
31
+ `height="${this.height.toFixed(SvgCsts.COORS_DIGITS)}"`
32
+ ];
33
+ const r = this.fitRadius();
34
+ if (!WtMath.isZero(r))
35
+ attrs.push(`rx="${r.toFixed(SvgCsts.COORS_DIGITS)}" ry="${r.toFixed(SvgCsts.COORS_DIGITS)}"`);
36
+ return attrs;
37
+ }
38
+ fitRadius() {
39
+ if (this.radius > 0) {
40
+ const rMax = Math.min(this.width / 2, this.height / 2);
41
+ return this.radius >= rMax ? rMax : this.radius;
42
+ }
43
+ return 0;
44
+ }
45
+ buildPathData() {
46
+ if (!this.auxRect)
47
+ return "";
48
+ if (!this.radius)
49
+ return `M${SvgPoint.toCoors(this.auxRect.pNW)}`
50
+ + ` L${SvgPoint.toCoors(this.auxRect.pNE)}`
51
+ + ` L${SvgPoint.toCoors(this.auxRect.pSE)}`
52
+ + ` L${SvgPoint.toCoors(this.auxRect.pSW)} z`;
53
+ const r = this.fitRadius();
54
+ const pathWithR = new PathSegmentedPath();
55
+ let pNW = this.auxRect.pNW, pSW = this.auxRect.pSW, pSE = this.auxRect.pSE, pNE = this.auxRect.pNE;
56
+ let e = r * (1 - Bezier3.K);
57
+ const pArcNW = new PathBezier3(new SvgPoint(pNW.x + r, pNW.y), new SvgPoint(pNW.x + e, pNW.y), new SvgPoint(pNW.x, pNW.y + e), new SvgPoint(pNW.x, pNW.y + r));
58
+ const lW = new PathLine(pNW.x, pNW.y + r, pSW.x, pSW.y - r);
59
+ const pArcSW = new PathBezier3(new SvgPoint(pSW.x, pSW.y - r), new SvgPoint(pSW.x, pSW.y - e), new SvgPoint(pSW.x + e, pSW.y), new SvgPoint(pSW.x + r, pSW.y));
60
+ const lS = new PathLine(pSW.x + r, pSW.y, pSE.x - r, pSE.y);
61
+ const pArcSE = new PathBezier3(new SvgPoint(pSE.x - r, pSE.y), new SvgPoint(pSE.x - e, pSE.y), new SvgPoint(pSE.x, pSE.y - e), new SvgPoint(pSE.x, pSE.y - r));
62
+ const lE = new PathLine(pSE.x, pSE.y - r, pNE.x, pNE.y + r);
63
+ const pArcNE = new PathBezier3(new SvgPoint(pNE.x, pNE.y + r), new SvgPoint(pNE.x, pNE.y + e), new SvgPoint(pNE.x - e, pNE.y), new SvgPoint(pNE.x - r, pNE.y));
64
+ const lN = new PathLine(pNE.x - r, pNE.y, pNW.x + r, pNW.y);
65
+ pathWithR.addSeg(pArcNW, lW, pArcSW, lS, pArcSE, lE, pArcNE, lN);
66
+ pathWithR.closePath();
67
+ return pathWithR.buildPathData();
68
+ }
69
+ getLength() {
70
+ return 2 * (this.width + this.height);
71
+ }
72
+ toAngleArced(radius) {
73
+ if (!radius)
74
+ return this;
75
+ // Copy the polygon to make a new Path
76
+ const path = PathPolygon.fromPolygon(this.auxRect.toPolygon());
77
+ path.setAngArcRadius(radius);
78
+ return path;
79
+ }
80
+ toPathSegments(radius, cw) {
81
+ if (!this.auxRect)
82
+ return [];
83
+ return PathPolygon.fromPolygon(this.auxRect.toPolygon(cw)).toPathSegs(radius);
84
+ }
85
+ duplicateTo(distance, dir) {
86
+ if (!this.auxRect)
87
+ return undefined;
88
+ dir = dir === "right" ? "in"
89
+ : dir === "left" ? "out"
90
+ : dir;
91
+ let rect = this.auxRect.duplicateTo(distance, dir);
92
+ return rect ? PathRect.fromRect(rect) : undefined;
93
+ }
94
+ // Static Methods
95
+ static fromXYWH(x, y, width, height, radius) {
96
+ const path = new PathRect();
97
+ const rect = new Rect(x, y, width, height);
98
+ path.auxRect = rect;
99
+ path.models = [rect];
100
+ path.width = rect.width;
101
+ path.height = rect.height;
102
+ path.anchor = SvgPoint.from(rect.pNW);
103
+ // Here, the order is important, it will be used to build the path data, please note
104
+ path.realPoints.push(SvgPoint.from(rect.pNW), SvgPoint.from(rect.pNE), SvgPoint.from(rect.pSE), SvgPoint.from(rect.pSW));
105
+ path.centers.push(SvgPoint.from(rect.center));
106
+ path.bbox = SvgBBox.fromMaxMins(rect.xMin, rect.xMax, rect.yMin, rect.yMax);
107
+ path.radius = radius ?? 0;
108
+ return path;
109
+ }
110
+ static fromRect(rect, radius) {
111
+ const path = new PathRect();
112
+ path.auxRect = rect;
113
+ path.models = [rect];
114
+ path.width = rect.width;
115
+ path.height = rect.height;
116
+ // path.center = SvgPoint.from(rect.center);
117
+ path.anchor = SvgPoint.from(rect.pNW);
118
+ // Here, the order is important, it will be used to build the path data, please note
119
+ path.realPoints.push(SvgPoint.from(rect.pNW), SvgPoint.from(rect.pNE), SvgPoint.from(rect.pSE), SvgPoint.from(rect.pSW));
120
+ path.centers.push(SvgPoint.from(rect.center));
121
+ path.bbox = SvgBBox.fromMaxMins(rect.xMin, rect.xMax, rect.yMin, rect.yMax);
122
+ path.radius = radius ?? 0;
123
+ return path;
124
+ }
125
+ }