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.
- package/Readme.md +223 -0
- package/component/HuatuComponentCache.js +89 -0
- package/component/HuatuComponentDef.js +249 -0
- package/component/HuatuComponentPlacement.js +172 -0
- package/component/HuatuComponentPort.js +58 -0
- package/component/IHuatuComponent.js +2 -0
- package/for-node/node-cli-funcs.js +36 -0
- package/geometry/AngDeg.js +68 -0
- package/geometry/Angle.js +77 -0
- package/geometry/Bezier.js +623 -0
- package/geometry/Circle.js +143 -0
- package/geometry/Ellipse.js +546 -0
- package/geometry/EllipticalArc.js +337 -0
- package/geometry/IGeometry.js +1 -0
- package/geometry/Intersection.js +601 -0
- package/geometry/Line.js +136 -0
- package/geometry/LineSeg.js +179 -0
- package/geometry/Point.js +88 -0
- package/geometry/PolyLine.js +97 -0
- package/geometry/Polygon.js +122 -0
- package/geometry/Rect.js +149 -0
- package/geometry/Ring.js +91 -0
- package/geometry/SegmentModel.js +206 -0
- package/geometry/Triangle.js +168 -0
- package/geometry/Vector.js +92 -0
- package/huatu/Huatu.js +4279 -0
- package/huatu/HuatuAngleMark.js +400 -0
- package/huatu/HuatuBlock.js +26 -0
- package/huatu/HuatuClipPath.js +29 -0
- package/huatu/HuatuCompDrawing.js +77 -0
- package/huatu/HuatuDimMark.js +579 -0
- package/huatu/HuatuDrawing.js +185 -0
- package/huatu/HuatuDrawingGroup.js +52 -0
- package/huatu/HuatuJob.js +347 -0
- package/huatu/HuatuLabel.js +311 -0
- package/huatu/HuatuLabelStyle.js +190 -0
- package/huatu/HuatuLayer.js +10 -0
- package/huatu/HuatuMarker.js +209 -0
- package/huatu/HuatuParser.js +435 -0
- package/huatu/HuatuPath.js +131 -0
- package/huatu/HuatuPathSeries.js +51 -0
- package/huatu/HuatuPattern.js +54 -0
- package/huatu/HuatuPoint.js +20 -0
- package/huatu/HuatuPointSeries.js +67 -0
- package/huatu/HuatuStyle.js +394 -0
- package/huatu/HuatuSymbol.js +81 -0
- package/huatu/HuatuSymbolArray.js +235 -0
- package/huatu/HuatuTransform.js +113 -0
- package/huatu/HuatuVar.js +17 -0
- package/huatu/HuatuYml.js +271 -0
- package/huatu/IHuatu.js +2 -0
- package/huatu/IHuatuYml.js +1 -0
- package/huatu/IndentParsingStack.js +82 -0
- package/index.d.ts +1747 -0
- package/index.js +247 -0
- package/math/Complex.js +72 -0
- package/math/Matrix.js +31 -0
- package/math/NumberRange.js +38 -0
- package/math/VarFuncs.js +24 -0
- package/math/WtMath.js +217 -0
- package/package.json +34 -0
- package/path/PathBezier2.js +75 -0
- package/path/PathBezier3.js +89 -0
- package/path/PathCircle.js +82 -0
- package/path/PathConfig.js +81 -0
- package/path/PathContinuousSegmentedPath.js +390 -0
- package/path/PathEllipse.js +99 -0
- package/path/PathEllipticalArc.js +111 -0
- package/path/PathGeneric.js +59 -0
- package/path/PathLine.js +75 -0
- package/path/PathLines.js +46 -0
- package/path/PathPolygon.js +142 -0
- package/path/PathPolyline.js +266 -0
- package/path/PathRect.js +125 -0
- package/path/PathRing.js +51 -0
- package/path/PathSegmentedPath.js +199 -0
- package/path/PathTriangle.js +90 -0
- package/presets/marker.js +37 -0
- package/presets/pattern.js +85 -0
- package/shape/SvgShapeArrowHead.js +92 -0
- package/shape/SvgShapeCircle.js +26 -0
- package/shape/SvgShapeCross.js +43 -0
- package/shape/SvgShapeEllipse.js +28 -0
- package/shape/SvgShapeGeneric.js +50 -0
- package/shape/SvgShapeHeart.js +40 -0
- package/shape/SvgShapeIsoscelesTriangle.js +52 -0
- package/shape/SvgShapePolygon.js +39 -0
- package/shape/SvgShapeRect.js +26 -0
- package/shape/SvgShapeRhombus.js +27 -0
- package/shape/SvgShapeStar.js +103 -0
- package/svg/ISvg.js +1 -0
- package/svg/SvgBBox.js +149 -0
- package/svg/SvgConstants.js +14 -0
- package/svg/SvgGradient.js +97 -0
- package/svg/SvgMarker.js +221 -0
- package/svg/SvgPattern.js +228 -0
- package/svg/SvgPoint.js +33 -0
- package/svg/SvgPointSet.js +41 -0
- package/templates/lines.js +35 -0
- package/tools/gen-id.js +2 -0
- package/tools/html.js +2 -0
- package/tools/katex.js +75 -0
- package/tools/rand-str.js +122 -0
- package/tools/regex.js +15 -0
- package/tools/utils.js +438 -0
- package/tools/webColor.js +700 -0
- package/wire/HuatuNet.js +22 -0
- package/wire/HuatuWire.js +415 -0
- package/wire/HuatuWireDrawing.js +17 -0
- package/wire/IHuatuWire.js +1 -0
package/svg/SvgMarker.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The class implementation of SVG Line.
|
|
3
|
+
*/
|
|
4
|
+
import { SvgShapeArrowHeadSimpleAngle, SvgShapeArrowHeadSolidTriangle, SvgShapeArrowHeadSolidTwoTriangle } from "../shape/SvgShapeArrowHead.js";
|
|
5
|
+
import { SvgShapeCross, SvgShapePlus } from "../shape/SvgShapeCross.js";
|
|
6
|
+
import { SvgShapeStar, SvgShapeStar5, SvgShapeStarUnified } from "../shape/SvgShapeStar.js";
|
|
7
|
+
import { SvgShapeCircle } from "../shape/SvgShapeCircle.js";
|
|
8
|
+
import { SvgShapeEllipse } from "../shape/SvgShapeEllipse.js";
|
|
9
|
+
import { SvgShapeHeart } from "../shape/SvgShapeHeart.js";
|
|
10
|
+
import { SvgShapeIsoscelesTriangle } from "../shape/SvgShapeIsoscelesTriangle.js";
|
|
11
|
+
import { SvgShapePolygon } from "../shape/SvgShapePolygon.js";
|
|
12
|
+
import { SvgShapeRect } from "../shape/SvgShapeRect.js";
|
|
13
|
+
import { SvgShapeRhombus } from "../shape/SvgShapeRhombus.js";
|
|
14
|
+
export class SvgMarker {
|
|
15
|
+
id;
|
|
16
|
+
units = "strokeWidth";
|
|
17
|
+
refX;
|
|
18
|
+
refY;
|
|
19
|
+
orient; // If use number, it means degrees
|
|
20
|
+
viewBox;
|
|
21
|
+
preserveAspectRatio;
|
|
22
|
+
stroke;
|
|
23
|
+
fill;
|
|
24
|
+
shape;
|
|
25
|
+
constructor(id, shape, orient, refX, refY, stroke, fill) {
|
|
26
|
+
this.id = `svg-mkr-${id}`;
|
|
27
|
+
this.orient = orient;
|
|
28
|
+
this.shape = shape;
|
|
29
|
+
this.refX = refX ?? 0;
|
|
30
|
+
this.refY = refY ?? 0;
|
|
31
|
+
if (stroke && this.shape.hasStroke) {
|
|
32
|
+
this.shape.setStroke(stroke);
|
|
33
|
+
this.stroke = { ...this.shape.stroke };
|
|
34
|
+
}
|
|
35
|
+
if (fill && this.shape.hasFill) {
|
|
36
|
+
this.shape.setFill(fill);
|
|
37
|
+
this.fill = { ...this.shape.fill };
|
|
38
|
+
}
|
|
39
|
+
this.viewBox = this.shape.getBBox(this.stroke?.width);
|
|
40
|
+
}
|
|
41
|
+
buildMarkerSvg(id, primaryColor, secondaryColor) {
|
|
42
|
+
id = id ?? this.id;
|
|
43
|
+
const fillColor = (this.shape.primaryColor === "fill" ? primaryColor : secondaryColor) ?? this.fill?.color;
|
|
44
|
+
const strokeColor = (this.shape.primaryColor === "stroke" ? primaryColor : secondaryColor) ?? this.stroke?.color;
|
|
45
|
+
const attrs = [
|
|
46
|
+
`id="${id}"`,
|
|
47
|
+
`viewBox="${this.viewBox.buildString()}"`,
|
|
48
|
+
`refX="${this.refX}"`,
|
|
49
|
+
`refY="${this.refY}"`,
|
|
50
|
+
`markerWidth="${this.viewBox.w}"`,
|
|
51
|
+
`markerHeight="${this.viewBox.h}"`,
|
|
52
|
+
`orient="${this.orient}"`,
|
|
53
|
+
];
|
|
54
|
+
return `<marker ${attrs.join(" ")} >\n`
|
|
55
|
+
/** Here, the fill opacity and stoke width are not able to be set while building SVG */
|
|
56
|
+
+ this.shape.buildHtml(fillColor, undefined, strokeColor, undefined)
|
|
57
|
+
+ `\n</marker>`;
|
|
58
|
+
}
|
|
59
|
+
/** Static methods, and shortcut methods of build different markers, the only variant is the
|
|
60
|
+
* shape which is determined by different input parameters such as size, etc.
|
|
61
|
+
*/
|
|
62
|
+
/** --- Circle --- */
|
|
63
|
+
static buildMarkerCircle(radius, fillColor, fillOpacity, strokeColor, strokeWidth) {
|
|
64
|
+
const shape = new SvgShapeCircle(radius);
|
|
65
|
+
return new SvgMarker("circ", shape, 0, 0, 0, {
|
|
66
|
+
color: strokeColor,
|
|
67
|
+
width: strokeWidth,
|
|
68
|
+
}, {
|
|
69
|
+
color: fillColor,
|
|
70
|
+
opacity: fillOpacity
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/** --- Ellipse --- */
|
|
74
|
+
static buildMarkerEllipse(rx, ry, fillColor, fillOpacity, strokeColor, strokeWidth) {
|
|
75
|
+
const shape = new SvgShapeEllipse(rx, ry);
|
|
76
|
+
return new SvgMarker("ell", shape, 0, 0, 0, {
|
|
77
|
+
color: strokeColor,
|
|
78
|
+
width: strokeWidth,
|
|
79
|
+
}, {
|
|
80
|
+
color: fillColor,
|
|
81
|
+
opacity: fillOpacity
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/** --- Rectangle --- */
|
|
85
|
+
static buildMarkerRect(w, h, fillColor, fillOpacity, strokeColor, strokeWidth) {
|
|
86
|
+
const shape = new SvgShapeRect(w, h);
|
|
87
|
+
return new SvgMarker("rect", shape, 0, 0, 0, {
|
|
88
|
+
color: strokeColor,
|
|
89
|
+
width: strokeWidth,
|
|
90
|
+
}, {
|
|
91
|
+
color: fillColor,
|
|
92
|
+
opacity: fillOpacity
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** --- Square --- */
|
|
96
|
+
static buildMarkerSquare(w, fillColor, fillOpacity, strokeColor, strokeWidth) {
|
|
97
|
+
const shape = new SvgShapeRect(w, w);
|
|
98
|
+
return new SvgMarker("square", shape, 0, 0, 0, {
|
|
99
|
+
color: strokeColor,
|
|
100
|
+
width: strokeWidth,
|
|
101
|
+
}, {
|
|
102
|
+
color: fillColor,
|
|
103
|
+
opacity: fillOpacity
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** --- Cross --- */
|
|
107
|
+
static buildMarkerCross(w, strokeColor, strokeWidth) {
|
|
108
|
+
const shape = new SvgShapeCross(w);
|
|
109
|
+
return new SvgMarker("cross", shape, 0, 0, 0, {
|
|
110
|
+
color: strokeColor,
|
|
111
|
+
width: strokeWidth,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/** --- Plus --- */
|
|
115
|
+
static buildMarkerPlus(w, strokeColor, strokeWidth) {
|
|
116
|
+
const shape = new SvgShapePlus(w);
|
|
117
|
+
return new SvgMarker("plus", shape, 0, 0, 0, {
|
|
118
|
+
color: strokeColor,
|
|
119
|
+
width: strokeWidth,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/** --- Polygon --- */
|
|
123
|
+
static buildMarkerPolygon(radius, edgeCount, fillColor, fillOpacity, strokeColor, strokeWidth) {
|
|
124
|
+
const shape = new SvgShapePolygon(radius, edgeCount);
|
|
125
|
+
return new SvgMarker("plg", shape, 0, 0, 0, {
|
|
126
|
+
color: strokeColor,
|
|
127
|
+
width: strokeWidth,
|
|
128
|
+
}, {
|
|
129
|
+
color: fillColor,
|
|
130
|
+
opacity: fillOpacity
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/** --- Rhombus --- */
|
|
134
|
+
static buildMarkerRhombus(width, height, fillColor, fillOpacity, strokeColor, strokeWidth) {
|
|
135
|
+
const shape = new SvgShapeRhombus(width, height);
|
|
136
|
+
return new SvgMarker("rhombus", shape, 0, 0, 0, {
|
|
137
|
+
color: strokeColor,
|
|
138
|
+
width: strokeWidth,
|
|
139
|
+
}, {
|
|
140
|
+
color: fillColor,
|
|
141
|
+
opacity: fillOpacity
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/** --- Isosceles Triangle --- */
|
|
145
|
+
static buildMarkerIsoscelesTriangle(width, height, direction, fillColor, fillOpacity, strokeColor, strokeWidth) {
|
|
146
|
+
const shape = new SvgShapeIsoscelesTriangle(width, height, direction);
|
|
147
|
+
return new SvgMarker("isotriang", shape, 0, 0, 0, {
|
|
148
|
+
color: strokeColor,
|
|
149
|
+
width: strokeWidth,
|
|
150
|
+
}, {
|
|
151
|
+
color: fillColor,
|
|
152
|
+
opacity: fillOpacity
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/** --- Star made by simple axises --- */
|
|
156
|
+
static buildMarkerStar(size, axisCount, strokeColor, strokeWidth) {
|
|
157
|
+
const shape = new SvgShapeStar(size / 2, axisCount);
|
|
158
|
+
return new SvgMarker("star", shape, 0, 0, 0, {
|
|
159
|
+
color: strokeColor,
|
|
160
|
+
width: strokeWidth,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/** --- Standard 5-angle star --- */
|
|
164
|
+
static buildMarkerStar5(size, centerEmpty, fillColor, fillOpacity, strokeColor, strokeWidth) {
|
|
165
|
+
const shape = new SvgShapeStar5(size / 2, centerEmpty);
|
|
166
|
+
return new SvgMarker("star5", shape, 0, 0, 0, {
|
|
167
|
+
color: strokeColor,
|
|
168
|
+
width: strokeWidth,
|
|
169
|
+
}, {
|
|
170
|
+
color: fillColor,
|
|
171
|
+
opacity: fillOpacity
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/** --- Unified Star-Like Markers --- */
|
|
175
|
+
static buildMarkerStarUnified(axisCount, rIn, rOut, degOffset, fillColor, fillOpacity, strokeColor, strokeWidth, roundRadius) {
|
|
176
|
+
const shape = new SvgShapeStarUnified(axisCount, rIn, rOut, degOffset, roundRadius);
|
|
177
|
+
return new SvgMarker("staru", shape, 0, 0, 0, {
|
|
178
|
+
color: strokeColor,
|
|
179
|
+
width: strokeWidth,
|
|
180
|
+
}, {
|
|
181
|
+
color: fillColor,
|
|
182
|
+
opacity: fillOpacity
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/** --- Heart --- */
|
|
186
|
+
static buildMarkerHeart(h1, ctrlLen1, deg1, farPointX, farPointY, deg2, ctrlLen2, ctrlLen3, h2, deg3, ctrlLen4, fillColor, fillOpacity, strokeColor, strokeWidth) {
|
|
187
|
+
const shape = new SvgShapeHeart(h1, ctrlLen1, deg1, farPointX, farPointY, deg2, ctrlLen2, ctrlLen3, h2, deg3, ctrlLen4);
|
|
188
|
+
return new SvgMarker("heart", shape, 0, 0, 0, {
|
|
189
|
+
color: strokeColor,
|
|
190
|
+
width: strokeWidth,
|
|
191
|
+
}, {
|
|
192
|
+
color: fillColor,
|
|
193
|
+
opacity: fillOpacity
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/** --- Arrow Head --- */
|
|
197
|
+
static buildArrowHead(type, length, openDeg, len2Ratio, makeSmooth, makeTriangle, color) {
|
|
198
|
+
if (type === "solid-angle") {
|
|
199
|
+
const shape = new SvgShapeArrowHeadSolidTriangle(length, openDeg, makeSmooth ? 0.4 : undefined);
|
|
200
|
+
return new SvgMarker("arrow-solid", shape, "auto-start-reverse", -0.5 / Math.tan(shape.angHalf), 0, undefined, color ? { color } : undefined /** Fill Color */);
|
|
201
|
+
}
|
|
202
|
+
else if (type === "solid-two-angle") {
|
|
203
|
+
const shape = new SvgShapeArrowHeadSolidTwoTriangle(length, openDeg, len2Ratio, makeSmooth ? 0.4 : undefined);
|
|
204
|
+
return new SvgMarker("arrow-solid-2", shape, "auto-start-reverse", -0.5 / Math.tan(shape.angHalf), 0, undefined, color ? { color } : undefined /** Fill Color */);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
const shape = new SvgShapeArrowHeadSimpleAngle(length, openDeg, makeTriangle, makeSmooth);
|
|
208
|
+
/** Here, the second 0.5 (half of 1) is derived from the fact that the default (and should always be)
|
|
209
|
+
* stroke width is 1 (same as line stroke) */
|
|
210
|
+
const refX = -0.5 / Math.tan(shape.angHalf) + 0.5 / Math.sin(shape.angHalf);
|
|
211
|
+
return new SvgMarker("arrow-simple", shape, "auto-start-reverse", refX, 0, color ? { color } : undefined);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/** The predefined markers */
|
|
215
|
+
static preDefines = {
|
|
216
|
+
"arrow-std": SvgMarker.buildArrowHead("solid-angle", 5.2, 42),
|
|
217
|
+
"arrow1": SvgMarker.buildArrowHead("solid-angle", 7, 38),
|
|
218
|
+
"arrow2": SvgMarker.buildArrowHead("solid-angle", 5, 30),
|
|
219
|
+
// TODO: add more as required
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The class implementation of SVG Point.
|
|
3
|
+
*/
|
|
4
|
+
import { fitNumInRange } from "../tools/utils.js";
|
|
5
|
+
import { AngDeg } from "../geometry/AngDeg.js";
|
|
6
|
+
import Line from "../geometry/Line.js";
|
|
7
|
+
import LineSeg from "../geometry/LineSeg.js";
|
|
8
|
+
import Point from "../geometry/Point.js";
|
|
9
|
+
import { SvgBBox } from "./SvgBBox.js";
|
|
10
|
+
import { PathRect } from "../path/PathRect.js";
|
|
11
|
+
import { PathLines } from "../path/PathLines.js";
|
|
12
|
+
import { PathCircle } from "../path/PathCircle.js";
|
|
13
|
+
export class SvgPattern {
|
|
14
|
+
id;
|
|
15
|
+
units = "userSpaceOnUse";
|
|
16
|
+
contentUnits;
|
|
17
|
+
x = 0;
|
|
18
|
+
y = 0;
|
|
19
|
+
width = 0;
|
|
20
|
+
height = 0;
|
|
21
|
+
paths = [];
|
|
22
|
+
viewBox;
|
|
23
|
+
constructor(id, x, y, width, height) {
|
|
24
|
+
this.id = id;
|
|
25
|
+
if (x)
|
|
26
|
+
this.x = x;
|
|
27
|
+
if (y)
|
|
28
|
+
this.y = y;
|
|
29
|
+
if (width)
|
|
30
|
+
this.width = width;
|
|
31
|
+
if (height)
|
|
32
|
+
this.height = height;
|
|
33
|
+
}
|
|
34
|
+
setUnits(units) {
|
|
35
|
+
this.units = units;
|
|
36
|
+
}
|
|
37
|
+
setContentUnits(units) {
|
|
38
|
+
// If there is a View Box defined, then the content unit should not be defined
|
|
39
|
+
if (this.viewBox)
|
|
40
|
+
return;
|
|
41
|
+
this.units = units;
|
|
42
|
+
}
|
|
43
|
+
setViewBox(box) {
|
|
44
|
+
this.viewBox = box;
|
|
45
|
+
}
|
|
46
|
+
addPaths(...paths) {
|
|
47
|
+
this.paths.push(...paths);
|
|
48
|
+
}
|
|
49
|
+
buildHtml(id) {
|
|
50
|
+
const attrs = [
|
|
51
|
+
`id="${id ?? this.id}"`,
|
|
52
|
+
`x="${this.x}"`,
|
|
53
|
+
`y="${this.y}"`,
|
|
54
|
+
`width="${this.width}"`,
|
|
55
|
+
`height="${this.height}"`
|
|
56
|
+
];
|
|
57
|
+
if (this.units === "userSpaceOnUse")
|
|
58
|
+
attrs.push(`patternUnits="${this.units}"`);
|
|
59
|
+
if (this.contentUnits && this.viewBox)
|
|
60
|
+
attrs.push(`patternContentUnits="${this.contentUnits}"`);
|
|
61
|
+
if (this.viewBox)
|
|
62
|
+
attrs.push(`viewBox="${this.viewBox.buildString()}"`);
|
|
63
|
+
return `<pattern ${attrs.join(" ")} >\n`
|
|
64
|
+
+ this.paths.map(p => p.buildHtml()).join("\n")
|
|
65
|
+
+ `\n</pattern>`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const buildRectFromViewBox = (vb, bg, bgOpacity) => {
|
|
69
|
+
const rect = PathRect.fromXYWH(vb.x, vb.y, vb.w, vb.h);
|
|
70
|
+
rect.fill = { color: bg };
|
|
71
|
+
if (bgOpacity) {
|
|
72
|
+
rect.fill.opacity = fitNumInRange(bgOpacity, 0, 1);
|
|
73
|
+
}
|
|
74
|
+
return rect;
|
|
75
|
+
};
|
|
76
|
+
export const buildLinePattern = (name, lineDensity, rotDeg, lineWeight, lineStyle, dashDensity, color, background, bgOpacity, opacity) => {
|
|
77
|
+
const buildLineSegAndViewBox = (lineDistance, rotDeg) => {
|
|
78
|
+
let wBox, hBox;
|
|
79
|
+
let path = new PathLines();
|
|
80
|
+
let deg = AngDeg.fitDeg(rotDeg);
|
|
81
|
+
deg = AngDeg.ang2Deg(deg);
|
|
82
|
+
if (deg > 180)
|
|
83
|
+
deg -= 180;
|
|
84
|
+
const degUsed = deg < 5
|
|
85
|
+
? 0
|
|
86
|
+
: deg > 85 && deg < 95
|
|
87
|
+
? 90
|
|
88
|
+
: deg > 175
|
|
89
|
+
? 0
|
|
90
|
+
: deg;
|
|
91
|
+
let w, h;
|
|
92
|
+
let N = 8; // Here N should be odd integer
|
|
93
|
+
const idx = [...Array(N).keys()];
|
|
94
|
+
if (degUsed === 0) { // Horizontal Line
|
|
95
|
+
wBox = N * lineDistance;
|
|
96
|
+
hBox = N * lineDistance;
|
|
97
|
+
h = lineDistance / 2;
|
|
98
|
+
path.addLineSegs(...idx.map(i => LineSeg.fromXYs(-wBox / 2, (2 * i - N + 1) * h, wBox / 2, (2 * i - N + 1) * h)));
|
|
99
|
+
}
|
|
100
|
+
else if (degUsed === 90) { // Vertical Line
|
|
101
|
+
wBox = N * lineDistance;
|
|
102
|
+
hBox = N * lineDistance;
|
|
103
|
+
w = lineDistance / 2;
|
|
104
|
+
path.addLineSegs(...idx.map(i => LineSeg.fromXYs((2 * i - N + 1) * w, -hBox / 2, (2 * i - N + 1) * w, hBox / 2)));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
let angUsed = AngDeg.deg2Ang(degUsed);
|
|
108
|
+
w = lineDistance / Math.abs(Math.sin(angUsed)) / 2;
|
|
109
|
+
h = lineDistance / Math.abs(Math.cos(angUsed)) / 2;
|
|
110
|
+
let d = Math.sqrt(w ** 2 + h ** 2);
|
|
111
|
+
wBox = N * 2 * w;
|
|
112
|
+
hBox = N * 2 * h;
|
|
113
|
+
let lines = [...Array(2 * N).keys()]
|
|
114
|
+
.map(i => degUsed < 90
|
|
115
|
+
? new Point(-wBox / 2, (N - 1 - 2 * i) * h)
|
|
116
|
+
: new Point(wBox / 2, (N - 1 - 2 * i) * h))
|
|
117
|
+
.map((p, i) => {
|
|
118
|
+
let len = (i >= N ? (2 * N) : (2 * i + 1)) * d;
|
|
119
|
+
return new Line(p, angUsed).buildSeg(-strokeWidth, len + strokeWidth);
|
|
120
|
+
});
|
|
121
|
+
path.addLineSegs(...lines);
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
path,
|
|
125
|
+
viewBox: new SvgBBox(-wBox / 2, -hBox / 2, wBox, hBox)
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
// Fit Line Weight, it should be in 1 and 100
|
|
129
|
+
lineWeight = fitNumInRange(lineWeight, 1, 100);
|
|
130
|
+
let strokeWidth = lineWeight ** 1.4 * 0.02;
|
|
131
|
+
// Fit Density, it should be in 1 and 100
|
|
132
|
+
lineDensity = fitNumInRange(lineDensity, 1, 100);
|
|
133
|
+
let lineDistance = 40 / lineDensity + strokeWidth * 2;
|
|
134
|
+
// Fit DashDensity, it should be in 1 and 100
|
|
135
|
+
dashDensity = fitNumInRange(dashDensity, 1, 100);
|
|
136
|
+
let dashArray = [];
|
|
137
|
+
let lineCap;
|
|
138
|
+
if (lineStyle === "dotted") {
|
|
139
|
+
lineCap = "round";
|
|
140
|
+
let k = 100 / dashDensity;
|
|
141
|
+
dashArray.push(0, k * strokeWidth);
|
|
142
|
+
}
|
|
143
|
+
else if (lineStyle === "dashed") {
|
|
144
|
+
let k = 100 / dashDensity;
|
|
145
|
+
dashArray.push(k * 1.6 * strokeWidth, k ** 1.5 * 1.6 * strokeWidth);
|
|
146
|
+
}
|
|
147
|
+
const lines = buildLineSegAndViewBox(lineDistance, rotDeg);
|
|
148
|
+
const pattern = new SvgPattern(name, 0, 0, lines.viewBox.w, lines.viewBox.h);
|
|
149
|
+
if (background) {
|
|
150
|
+
pattern.addPaths(buildRectFromViewBox(lines.viewBox, background, bgOpacity));
|
|
151
|
+
}
|
|
152
|
+
lines.path.stroke = {
|
|
153
|
+
color: color ? color : "currentColor",
|
|
154
|
+
width: strokeWidth,
|
|
155
|
+
};
|
|
156
|
+
if (lineCap) {
|
|
157
|
+
lines.path.stroke.lineCap = lineCap;
|
|
158
|
+
}
|
|
159
|
+
if (dashArray.length > 0) {
|
|
160
|
+
lines.path.stroke.dashArray = dashArray;
|
|
161
|
+
}
|
|
162
|
+
if (opacity) {
|
|
163
|
+
lines.path.stroke.opacity = fitNumInRange(opacity, 0, 1);
|
|
164
|
+
}
|
|
165
|
+
pattern.viewBox = lines.viewBox;
|
|
166
|
+
pattern.addPaths(lines.path);
|
|
167
|
+
return pattern;
|
|
168
|
+
};
|
|
169
|
+
export const buildDotPattern = (name, dotDensity, dotSize, color, opacity, background, bgOpacity) => {
|
|
170
|
+
dotDensity = fitNumInRange(dotDensity, 1, 100);
|
|
171
|
+
dotSize = fitNumInRange(dotSize, 1, 100);
|
|
172
|
+
let r = dotSize * 0.2, d = r * (40 / dotDensity ** 0.56247); // Max density: 3r, min Density: 40r, this is half of the viewBox square side length
|
|
173
|
+
const vb = new SvgBBox(-d, -d, 2 * d, 2 * d);
|
|
174
|
+
const pattern = new SvgPattern(name, 0, 0, vb.w, vb.h);
|
|
175
|
+
const circ = new PathCircle(0, 0, r);
|
|
176
|
+
circ.fill = { color: color };
|
|
177
|
+
if (opacity) {
|
|
178
|
+
circ.fill.opacity = fitNumInRange(opacity, 0, 1);
|
|
179
|
+
}
|
|
180
|
+
pattern.viewBox = vb;
|
|
181
|
+
if (background) {
|
|
182
|
+
pattern.addPaths(buildRectFromViewBox(vb, background, bgOpacity));
|
|
183
|
+
}
|
|
184
|
+
pattern.addPaths(circ);
|
|
185
|
+
return pattern;
|
|
186
|
+
};
|
|
187
|
+
export const buildSquarePattern = (name, sqDensity, sqSize, color, opacity, background, bgOpacity) => {
|
|
188
|
+
sqDensity = fitNumInRange(sqDensity, 1, 100);
|
|
189
|
+
sqSize = fitNumInRange(sqSize, 1, 100);
|
|
190
|
+
let l = sqSize * 0.2, d = l * (40 / sqDensity ** 0.56247); // Max density: 3r, min Density: 40r, this is half of the viewBox square side length
|
|
191
|
+
const vb = new SvgBBox(-d, -d, 2 * d, 2 * d);
|
|
192
|
+
const pattern = new SvgPattern(name, 0, 0, vb.w, vb.h);
|
|
193
|
+
const sq = PathRect.fromXYWH(-l, -l, 2 * l, 2 * l);
|
|
194
|
+
sq.fill = { color: color };
|
|
195
|
+
if (opacity) {
|
|
196
|
+
sq.fill.opacity = fitNumInRange(opacity, 0, 1);
|
|
197
|
+
}
|
|
198
|
+
pattern.viewBox = vb;
|
|
199
|
+
if (background) {
|
|
200
|
+
pattern.addPaths(buildRectFromViewBox(vb, background, bgOpacity));
|
|
201
|
+
}
|
|
202
|
+
pattern.addPaths(sq);
|
|
203
|
+
return pattern;
|
|
204
|
+
};
|
|
205
|
+
export const buildGridPattern = (name, gridSize, lineWidth, color, bgColor, bgOpacity, opacity, sparseK) => {
|
|
206
|
+
gridSize = fitNumInRange(gridSize, 1, 100);
|
|
207
|
+
let d = Math.ceil(gridSize);
|
|
208
|
+
sparseK = fitNumInRange(sparseK ?? 1, 0.3, 30);
|
|
209
|
+
let len = d * (1 + sparseK);
|
|
210
|
+
const vb = new SvgBBox(-len / 2, -len / 2, len, len);
|
|
211
|
+
const lines = new PathLines();
|
|
212
|
+
lines.addLineSegs(LineSeg.fromXYs(-len / 2, -d / 2, len / 2, -d / 2), LineSeg.fromXYs(-len / 2, d / 2, len / 2, d / 2), LineSeg.fromXYs(-d / 2, -len / 2, -d / 2, len / 2), LineSeg.fromXYs(d / 2, -len / 2, d / 2, len / 2));
|
|
213
|
+
lineWidth = fitNumInRange(lineWidth, 1, 100);
|
|
214
|
+
lines.stroke = {
|
|
215
|
+
color: color,
|
|
216
|
+
width: lineWidth ** 1.4 * 0.02
|
|
217
|
+
};
|
|
218
|
+
if (opacity) {
|
|
219
|
+
lines.stroke.opacity = fitNumInRange(opacity, 0, 1);
|
|
220
|
+
}
|
|
221
|
+
const pattern = new SvgPattern(name, 0, 0, vb.w, vb.h);
|
|
222
|
+
pattern.viewBox = vb;
|
|
223
|
+
if (bgColor) {
|
|
224
|
+
pattern.addPaths(buildRectFromViewBox(vb, bgColor, bgOpacity));
|
|
225
|
+
}
|
|
226
|
+
pattern.addPaths(lines);
|
|
227
|
+
return pattern;
|
|
228
|
+
};
|
package/svg/SvgPoint.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The class implementation of SVG Point.
|
|
3
|
+
*/
|
|
4
|
+
import { SvgCsts } from "./SvgConstants.js";
|
|
5
|
+
export class SvgPoint {
|
|
6
|
+
x;
|
|
7
|
+
y;
|
|
8
|
+
constructor(x, y) {
|
|
9
|
+
this.x = x;
|
|
10
|
+
this.y = y;
|
|
11
|
+
}
|
|
12
|
+
toCoors(digits) {
|
|
13
|
+
const digitPlace = digits
|
|
14
|
+
? (digits < SvgCsts.COORS_DIGITS_MIN ? SvgCsts.COORS_DIGITS_MIN : Math.round(digits))
|
|
15
|
+
: SvgCsts.COORS_DIGITS;
|
|
16
|
+
return `${this.x.toFixed(digitPlace)},${this.y.toFixed(digitPlace)}`;
|
|
17
|
+
}
|
|
18
|
+
moveBy(dx, dy) {
|
|
19
|
+
return new SvgPoint(this.x + dx, this.y + dy);
|
|
20
|
+
}
|
|
21
|
+
//======= Static Methods =====================================================
|
|
22
|
+
static from(p) {
|
|
23
|
+
return new SvgPoint(p.x, p.y);
|
|
24
|
+
}
|
|
25
|
+
static toCoors(p, digits) {
|
|
26
|
+
const digitPlace = digits
|
|
27
|
+
? (digits < SvgCsts.COORS_DIGITS_MIN ? SvgCsts.COORS_DIGITS_MIN : Math.round(digits))
|
|
28
|
+
: SvgCsts.COORS_DIGITS;
|
|
29
|
+
return `${p.x.toFixed(digitPlace)},${p.y.toFixed(digitPlace)}`;
|
|
30
|
+
}
|
|
31
|
+
static ORIGIN = new SvgPoint(0, 0);
|
|
32
|
+
static ZERO = new SvgPoint(0, 0);
|
|
33
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The class implementation of SVG Point.
|
|
3
|
+
*/
|
|
4
|
+
import { SvgPoint } from "./SvgPoint.js";
|
|
5
|
+
export class SvgPointSet {
|
|
6
|
+
points = [];
|
|
7
|
+
constructor() {
|
|
8
|
+
}
|
|
9
|
+
add(...points) {
|
|
10
|
+
this.points.push(...points.map(p => SvgPoint.from(p)));
|
|
11
|
+
}
|
|
12
|
+
concat(set) {
|
|
13
|
+
this.add(...set.points);
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
//======= Static Methods =====================================================
|
|
17
|
+
static from(points) {
|
|
18
|
+
const set = new SvgPointSet();
|
|
19
|
+
set.points = points.map(p => SvgPoint.from(p));
|
|
20
|
+
return set;
|
|
21
|
+
}
|
|
22
|
+
static zipTwo(set1, set2) {
|
|
23
|
+
const set = new SvgPointSet();
|
|
24
|
+
const lenLonger = Math.max(set1.points.length, set2.points.length);
|
|
25
|
+
for (let i = 0; i < lenLonger; i++) {
|
|
26
|
+
if (i < set1.points.length) {
|
|
27
|
+
set.add(set1.points[i]);
|
|
28
|
+
}
|
|
29
|
+
if (i < set2.points.length) {
|
|
30
|
+
set.add(set2.points[i]);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return set;
|
|
34
|
+
}
|
|
35
|
+
static concatTwo(set1, set2) {
|
|
36
|
+
const set = new SvgPointSet();
|
|
37
|
+
set.add(...set1.points);
|
|
38
|
+
set.add(...set2.points);
|
|
39
|
+
return set;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import Point from "../geometry/Point.js";
|
|
2
|
+
import { SvgBBox } from "../svg/SvgBBox.js";
|
|
3
|
+
export class HuatuTemplateLines {
|
|
4
|
+
name = "lines";
|
|
5
|
+
chartHeight = 600;
|
|
6
|
+
chartWidth = 1000;
|
|
7
|
+
data = [];
|
|
8
|
+
constructor() {
|
|
9
|
+
}
|
|
10
|
+
buildHuatuYml() {
|
|
11
|
+
return "";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export const data2Points = (data, vbChart, xRange, yRange) => {
|
|
15
|
+
const xs = data.map(x => x.x);
|
|
16
|
+
const ys = data.map(x => x.y);
|
|
17
|
+
const xr = xRange ?? { min: Math.min(...xs), max: Math.max(...xs) };
|
|
18
|
+
const yr = yRange ?? { min: Math.min(...ys), max: Math.max(...ys) };
|
|
19
|
+
return data.map((d, i) => {
|
|
20
|
+
let dx = d.x >= xr.max ? xr.max : d.x <= xr.min ? xr.min : d.x, dy = d.y >= yr.max ? yr.max : d.y <= yr.min ? yr.min : d.y, x = vbChart.x + vbChart.w * (dx - xr.min) / (xr.max - xr.min), y = vbChart.y + vbChart.h - vbChart.h * (dy - yr.min) / (yr.max - yr.min);
|
|
21
|
+
return new Point(x, y);
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const dat = [...Array(30).keys()].map(i => {
|
|
25
|
+
const theta = i * Math.PI / 12;
|
|
26
|
+
const r = 6 * theta;
|
|
27
|
+
return {
|
|
28
|
+
x: r * Math.cos(theta),
|
|
29
|
+
y: r * Math.sin(theta),
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
const points = data2Points(dat, new SvgBBox(0, -800, 1000, 800), undefined, undefined)
|
|
33
|
+
.map(p => `${p.x.toFixed(6)},${p.y.toFixed(6)}`)
|
|
34
|
+
.join(";");
|
|
35
|
+
console.log(points);
|
package/tools/gen-id.js
ADDED
package/tools/html.js
ADDED
package/tools/katex.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import katex from "katex";
|
|
2
|
+
import "katex/contrib/mhchem";
|
|
3
|
+
export const renderKatexInline = (math) => {
|
|
4
|
+
try {
|
|
5
|
+
return katex.renderToString(math, {
|
|
6
|
+
displayMode: false,
|
|
7
|
+
output: "html",
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
return `<span class='txt-error'>公式书写错误: ${math}</span>`;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
export const renderKatexBlock = (math) => {
|
|
15
|
+
try {
|
|
16
|
+
const katexHtml = katex.renderToString(math, {
|
|
17
|
+
displayMode: true,
|
|
18
|
+
output: "html",
|
|
19
|
+
});
|
|
20
|
+
return `<div class="katex-block">${katexHtml}</div>`;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return `<span class='txt-error'>公式书写错误: ${math}</span>`;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export const renderKatexInlineWithBlockForm = (math) => {
|
|
27
|
+
try {
|
|
28
|
+
const renderedHtml = katex.renderToString(math, {
|
|
29
|
+
displayMode: true,
|
|
30
|
+
output: "html",
|
|
31
|
+
});
|
|
32
|
+
const matched = /<span class="katex-display">([\s\S]+)<\/span>/i.exec(renderedHtml);
|
|
33
|
+
return matched ? matched[1] : renderedHtml;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return `<span class='txt-error'>公式书写错误: ${math}</span>`;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
export const renderKatexMixedText = (text) => {
|
|
40
|
+
const rawSegments = text.split(/(?<!\\)\$/); // Split label text with just the $ sign, but not escaped one \$
|
|
41
|
+
const rendered = [];
|
|
42
|
+
if (rawSegments[0].trim()) {
|
|
43
|
+
rendered.push(`<span>${rawSegments[0]}</span>`);
|
|
44
|
+
}
|
|
45
|
+
let dsStarted = false;
|
|
46
|
+
let textPending = '';
|
|
47
|
+
if (rawSegments.length >= 2) {
|
|
48
|
+
for (let i = 1; i < rawSegments.length; i++) {
|
|
49
|
+
if (!dsStarted) {
|
|
50
|
+
dsStarted = true;
|
|
51
|
+
if (textPending.trim()) {
|
|
52
|
+
rendered.push(`<span>${textPending}</span>`);
|
|
53
|
+
}
|
|
54
|
+
textPending = rawSegments[i];
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
rendered.push(renderKatexInlineWithBlockForm(textPending));
|
|
58
|
+
dsStarted = false;
|
|
59
|
+
textPending = rawSegments[i];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (dsStarted) {
|
|
63
|
+
textPending = '$' + textPending;
|
|
64
|
+
}
|
|
65
|
+
if (textPending) {
|
|
66
|
+
rendered.push(`<span>${textPending}</span>`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return rendered.join("");
|
|
70
|
+
};
|
|
71
|
+
export const renderKatexMixedTextConditional = (text, renderKatex) => {
|
|
72
|
+
return renderKatex
|
|
73
|
+
? renderKatexMixedText(text)
|
|
74
|
+
: `<span>${text}</span>`;
|
|
75
|
+
};
|