xl-public-utils 1.0.11 → 1.0.13
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/index.d.ts +52 -0
- package/index.js +2 -1
- package/package.json +4 -2
- package/src/drcUtils.js +1 -0
- package/src/threeFont/Curve/Curve.js +349 -0
- package/src/threeFont/Curve/CurvePath.js +202 -0
- package/src/threeFont/Curve/Interpolations.js +57 -0
- package/src/threeFont/Curve/Shape.js +77 -0
- package/src/threeFont/Math/MathUtils.js +526 -0
- package/src/threeFont/Math/Matrix4.js +863 -0
- package/src/threeFont/Math/Quaternion.js +584 -0
- package/src/threeFont/Math/Vector2.js +373 -0
- package/src/threeFont/Math/Vector3.js +617 -0
- package/src/threeFont/TextGeometry/Earcut.js +634 -0
- package/src/threeFont/TextGeometry/ExtrudeGeometry.js +647 -0
- package/src/threeFont/TextGeometry/FontLoader.js +114 -0
- package/src/threeFont/TextGeometry/Path.js +146 -0
- package/src/threeFont/TextGeometry/ShapePath.js +222 -0
- package/src/threeFont/TextGeometry/ShapeUtils.js +70 -0
- package/src/threeFont/curves/CubicBezierCurve.js +66 -0
- package/src/threeFont/curves/Curves.js +10 -0
- package/src/threeFont/curves/EllipseCurve.js +130 -0
- package/src/threeFont/curves/LineCurve.js +70 -0
- package/src/threeFont/curves/QuadraticBezierCurve.js +61 -0
- package/src/threeFont/curves/SplineCurve.js +76 -0
- package/src/threeFont/index.js +166 -0
- package/src/vtkUtils.js +55 -28
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { ShapePath } from './ShapePath.js';
|
|
2
|
+
|
|
3
|
+
class Font {
|
|
4
|
+
constructor(data) {
|
|
5
|
+
this.isFont = true;
|
|
6
|
+
|
|
7
|
+
this.type = 'Font';
|
|
8
|
+
|
|
9
|
+
this.data = data;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
generateShapes(text, size = 100) {
|
|
13
|
+
const shapes = [];
|
|
14
|
+
const paths = createPaths(text, size, this.data);
|
|
15
|
+
|
|
16
|
+
for (let p = 0, pl = paths.length; p < pl; p++) {
|
|
17
|
+
shapes.push(...paths[p].toShapes());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return shapes;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function createPaths(text, size, data) {
|
|
25
|
+
const chars = Array.from(text);
|
|
26
|
+
const scale = size / data.resolution;
|
|
27
|
+
const line_height = (data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness) * scale;
|
|
28
|
+
|
|
29
|
+
const paths = [];
|
|
30
|
+
|
|
31
|
+
let offsetX = 0,
|
|
32
|
+
offsetY = 0;
|
|
33
|
+
|
|
34
|
+
for (let i = 0; i < chars.length; i++) {
|
|
35
|
+
const char = chars[i];
|
|
36
|
+
|
|
37
|
+
if (char === '\n') {
|
|
38
|
+
offsetX = 0;
|
|
39
|
+
offsetY -= line_height;
|
|
40
|
+
} else {
|
|
41
|
+
const ret = createPath(char, scale, offsetX, offsetY, data);
|
|
42
|
+
offsetX += ret.offsetX;
|
|
43
|
+
paths.push(ret.path);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return paths;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function createPath(char, scale, offsetX, offsetY, data) {
|
|
51
|
+
const glyph = data.glyphs[char] || data.glyphs['?'];
|
|
52
|
+
|
|
53
|
+
if (!glyph) {
|
|
54
|
+
console.error('THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.');
|
|
55
|
+
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const path = new ShapePath();
|
|
60
|
+
|
|
61
|
+
let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2;
|
|
62
|
+
|
|
63
|
+
if (glyph.o) {
|
|
64
|
+
const outline = glyph._cachedOutline || (glyph._cachedOutline = glyph.o.split(' '));
|
|
65
|
+
|
|
66
|
+
for (let i = 0, l = outline.length; i < l; ) {
|
|
67
|
+
const action = outline[i++];
|
|
68
|
+
|
|
69
|
+
switch (action) {
|
|
70
|
+
case 'm': // moveTo
|
|
71
|
+
x = outline[i++] * scale + offsetX;
|
|
72
|
+
y = outline[i++] * scale + offsetY;
|
|
73
|
+
|
|
74
|
+
path.moveTo(x, y);
|
|
75
|
+
|
|
76
|
+
break;
|
|
77
|
+
|
|
78
|
+
case 'l': // lineTo
|
|
79
|
+
x = outline[i++] * scale + offsetX;
|
|
80
|
+
y = outline[i++] * scale + offsetY;
|
|
81
|
+
|
|
82
|
+
path.lineTo(x, y);
|
|
83
|
+
|
|
84
|
+
break;
|
|
85
|
+
|
|
86
|
+
case 'q': // quadraticCurveTo
|
|
87
|
+
cpx = outline[i++] * scale + offsetX;
|
|
88
|
+
cpy = outline[i++] * scale + offsetY;
|
|
89
|
+
cpx1 = outline[i++] * scale + offsetX;
|
|
90
|
+
cpy1 = outline[i++] * scale + offsetY;
|
|
91
|
+
|
|
92
|
+
path.quadraticCurveTo(cpx1, cpy1, cpx, cpy);
|
|
93
|
+
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
case 'b': // bezierCurveTo
|
|
97
|
+
cpx = outline[i++] * scale + offsetX;
|
|
98
|
+
cpy = outline[i++] * scale + offsetY;
|
|
99
|
+
cpx1 = outline[i++] * scale + offsetX;
|
|
100
|
+
cpy1 = outline[i++] * scale + offsetY;
|
|
101
|
+
cpx2 = outline[i++] * scale + offsetX;
|
|
102
|
+
cpy2 = outline[i++] * scale + offsetY;
|
|
103
|
+
|
|
104
|
+
path.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, cpx, cpy);
|
|
105
|
+
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { offsetX: glyph.ha * scale, path: path };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export { Font };
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Vector2 } from '../Math/Vector2.js';
|
|
2
|
+
import { CurvePath } from '../Curve/CurvePath.js';
|
|
3
|
+
import { EllipseCurve } from '../curves/EllipseCurve.js';
|
|
4
|
+
import { SplineCurve } from '../curves/SplineCurve.js';
|
|
5
|
+
import { CubicBezierCurve } from '../curves/CubicBezierCurve.js';
|
|
6
|
+
import { QuadraticBezierCurve } from '../curves/QuadraticBezierCurve.js';
|
|
7
|
+
import { LineCurve } from '../curves/LineCurve.js';
|
|
8
|
+
|
|
9
|
+
class Path extends CurvePath {
|
|
10
|
+
constructor(points) {
|
|
11
|
+
super();
|
|
12
|
+
|
|
13
|
+
this.type = 'Path';
|
|
14
|
+
this.currentPoint = new Vector2();
|
|
15
|
+
|
|
16
|
+
if (points) {
|
|
17
|
+
this.setFromPoints(points);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setFromPoints(points) {
|
|
22
|
+
this.moveTo(points[0].x, points[0].y);
|
|
23
|
+
|
|
24
|
+
for (let i = 1, l = points.length; i < l; i++) {
|
|
25
|
+
this.lineTo(points[i].x, points[i].y);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
moveTo(x, y) {
|
|
32
|
+
this.currentPoint.set(x, y); // TODO consider referencing vectors instead of copying?
|
|
33
|
+
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
lineTo(x, y) {
|
|
38
|
+
const curve = new LineCurve(this.currentPoint.clone(), new Vector2(x, y));
|
|
39
|
+
this.curves.push(curve);
|
|
40
|
+
|
|
41
|
+
this.currentPoint.set(x, y);
|
|
42
|
+
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
quadraticCurveTo(aCPx, aCPy, aX, aY) {
|
|
47
|
+
const curve = new QuadraticBezierCurve(this.currentPoint.clone(), new Vector2(aCPx, aCPy), new Vector2(aX, aY));
|
|
48
|
+
|
|
49
|
+
this.curves.push(curve);
|
|
50
|
+
|
|
51
|
+
this.currentPoint.set(aX, aY);
|
|
52
|
+
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) {
|
|
57
|
+
const curve = new CubicBezierCurve(this.currentPoint.clone(), new Vector2(aCP1x, aCP1y), new Vector2(aCP2x, aCP2y), new Vector2(aX, aY));
|
|
58
|
+
|
|
59
|
+
this.curves.push(curve);
|
|
60
|
+
|
|
61
|
+
this.currentPoint.set(aX, aY);
|
|
62
|
+
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
splineThru(pts /*Array of Vector*/) {
|
|
67
|
+
const npts = [this.currentPoint.clone()].concat(pts);
|
|
68
|
+
|
|
69
|
+
const curve = new SplineCurve(npts);
|
|
70
|
+
this.curves.push(curve);
|
|
71
|
+
|
|
72
|
+
this.currentPoint.copy(pts[pts.length - 1]);
|
|
73
|
+
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) {
|
|
78
|
+
const x0 = this.currentPoint.x;
|
|
79
|
+
const y0 = this.currentPoint.y;
|
|
80
|
+
|
|
81
|
+
this.absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise);
|
|
82
|
+
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) {
|
|
87
|
+
this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
|
|
88
|
+
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
ellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) {
|
|
93
|
+
const x0 = this.currentPoint.x;
|
|
94
|
+
const y0 = this.currentPoint.y;
|
|
95
|
+
|
|
96
|
+
this.absellipse(aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation);
|
|
97
|
+
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
absellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) {
|
|
102
|
+
const curve = new EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation);
|
|
103
|
+
|
|
104
|
+
if (this.curves.length > 0) {
|
|
105
|
+
// if a previous curve is present, attempt to join
|
|
106
|
+
const firstPoint = curve.getPoint(0);
|
|
107
|
+
|
|
108
|
+
if (!firstPoint.equals(this.currentPoint)) {
|
|
109
|
+
this.lineTo(firstPoint.x, firstPoint.y);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this.curves.push(curve);
|
|
114
|
+
|
|
115
|
+
const lastPoint = curve.getPoint(1);
|
|
116
|
+
this.currentPoint.copy(lastPoint);
|
|
117
|
+
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
copy(source) {
|
|
122
|
+
super.copy(source);
|
|
123
|
+
|
|
124
|
+
this.currentPoint.copy(source.currentPoint);
|
|
125
|
+
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
toJSON() {
|
|
130
|
+
const data = super.toJSON();
|
|
131
|
+
|
|
132
|
+
data.currentPoint = this.currentPoint.toArray();
|
|
133
|
+
|
|
134
|
+
return data;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
fromJSON(json) {
|
|
138
|
+
super.fromJSON(json);
|
|
139
|
+
|
|
140
|
+
this.currentPoint.fromArray(json.currentPoint);
|
|
141
|
+
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { Path };
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { Path } from './Path.js';
|
|
2
|
+
import { Shape } from '../Curve/Shape.js';
|
|
3
|
+
import { ShapeUtils } from '../TextGeometry/ShapeUtils.js';
|
|
4
|
+
|
|
5
|
+
class ShapePath {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.type = 'ShapePath';
|
|
8
|
+
this.color = [1.0, 1.0, 1.0];
|
|
9
|
+
|
|
10
|
+
this.subPaths = [];
|
|
11
|
+
this.currentPath = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
moveTo(x, y) {
|
|
15
|
+
this.currentPath = new Path();
|
|
16
|
+
this.subPaths.push(this.currentPath);
|
|
17
|
+
this.currentPath.moveTo(x, y);
|
|
18
|
+
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
lineTo(x, y) {
|
|
23
|
+
this.currentPath.lineTo(x, y);
|
|
24
|
+
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
quadraticCurveTo(aCPx, aCPy, aX, aY) {
|
|
29
|
+
this.currentPath.quadraticCurveTo(aCPx, aCPy, aX, aY);
|
|
30
|
+
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) {
|
|
35
|
+
this.currentPath.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY);
|
|
36
|
+
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
splineThru(pts) {
|
|
41
|
+
this.currentPath.splineThru(pts);
|
|
42
|
+
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
toShapes(isCCW) {
|
|
47
|
+
function toShapesNoHoles(inSubpaths) {
|
|
48
|
+
const shapes = [];
|
|
49
|
+
|
|
50
|
+
for (let i = 0, l = inSubpaths.length; i < l; i++) {
|
|
51
|
+
const tmpPath = inSubpaths[i];
|
|
52
|
+
|
|
53
|
+
const tmpShape = new Shape();
|
|
54
|
+
tmpShape.curves = tmpPath.curves;
|
|
55
|
+
|
|
56
|
+
shapes.push(tmpShape);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return shapes;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isPointInsidePolygon(inPt, inPolygon) {
|
|
63
|
+
const polyLen = inPolygon.length;
|
|
64
|
+
|
|
65
|
+
// inPt on polygon contour => immediate success or
|
|
66
|
+
// toggling of inside/outside at every single! intersection point of an edge
|
|
67
|
+
// with the horizontal line through inPt, left of inPt
|
|
68
|
+
// not counting lowerY endpoints of edges and whole edges on that line
|
|
69
|
+
let inside = false;
|
|
70
|
+
for (let p = polyLen - 1, q = 0; q < polyLen; p = q++) {
|
|
71
|
+
let edgeLowPt = inPolygon[p];
|
|
72
|
+
let edgeHighPt = inPolygon[q];
|
|
73
|
+
|
|
74
|
+
let edgeDx = edgeHighPt.x - edgeLowPt.x;
|
|
75
|
+
let edgeDy = edgeHighPt.y - edgeLowPt.y;
|
|
76
|
+
|
|
77
|
+
if (Math.abs(edgeDy) > Number.EPSILON) {
|
|
78
|
+
// not parallel
|
|
79
|
+
if (edgeDy < 0) {
|
|
80
|
+
edgeLowPt = inPolygon[q];
|
|
81
|
+
edgeDx = -edgeDx;
|
|
82
|
+
edgeHighPt = inPolygon[p];
|
|
83
|
+
edgeDy = -edgeDy;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y) continue;
|
|
87
|
+
|
|
88
|
+
if (inPt.y === edgeLowPt.y) {
|
|
89
|
+
if (inPt.x === edgeLowPt.x) return true; // inPt is on contour ?
|
|
90
|
+
// continue; // no intersection or edgeLowPt => doesn't count !!!
|
|
91
|
+
} else {
|
|
92
|
+
const perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y);
|
|
93
|
+
if (perpEdge === 0) return true; // inPt is on contour ?
|
|
94
|
+
if (perpEdge < 0) continue;
|
|
95
|
+
inside = !inside; // true intersection left of inPt
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
// parallel or collinear
|
|
99
|
+
if (inPt.y !== edgeLowPt.y) continue; // parallel
|
|
100
|
+
// edge lies on the same horizontal line as inPt
|
|
101
|
+
if ((edgeHighPt.x <= inPt.x && inPt.x <= edgeLowPt.x) || (edgeLowPt.x <= inPt.x && inPt.x <= edgeHighPt.x)) return true; // inPt: Point on contour !
|
|
102
|
+
// continue;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return inside;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const isClockWise = ShapeUtils.isClockWise;
|
|
110
|
+
|
|
111
|
+
const subPaths = this.subPaths;
|
|
112
|
+
if (subPaths.length === 0) return [];
|
|
113
|
+
|
|
114
|
+
let solid, tmpPath, tmpShape;
|
|
115
|
+
const shapes = [];
|
|
116
|
+
|
|
117
|
+
if (subPaths.length === 1) {
|
|
118
|
+
tmpPath = subPaths[0];
|
|
119
|
+
tmpShape = new Shape();
|
|
120
|
+
tmpShape.curves = tmpPath.curves;
|
|
121
|
+
shapes.push(tmpShape);
|
|
122
|
+
return shapes;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let holesFirst = !isClockWise(subPaths[0].getPoints());
|
|
126
|
+
holesFirst = isCCW ? !holesFirst : holesFirst;
|
|
127
|
+
|
|
128
|
+
// console.log("Holes first", holesFirst);
|
|
129
|
+
|
|
130
|
+
const betterShapeHoles = [];
|
|
131
|
+
const newShapes = [];
|
|
132
|
+
let newShapeHoles = [];
|
|
133
|
+
let mainIdx = 0;
|
|
134
|
+
let tmpPoints;
|
|
135
|
+
|
|
136
|
+
newShapes[mainIdx] = undefined;
|
|
137
|
+
newShapeHoles[mainIdx] = [];
|
|
138
|
+
|
|
139
|
+
for (let i = 0, l = subPaths.length; i < l; i++) {
|
|
140
|
+
tmpPath = subPaths[i];
|
|
141
|
+
tmpPoints = tmpPath.getPoints();
|
|
142
|
+
solid = isClockWise(tmpPoints);
|
|
143
|
+
solid = isCCW ? !solid : solid;
|
|
144
|
+
|
|
145
|
+
if (solid) {
|
|
146
|
+
if (!holesFirst && newShapes[mainIdx]) mainIdx++;
|
|
147
|
+
|
|
148
|
+
newShapes[mainIdx] = { s: new Shape(), p: tmpPoints };
|
|
149
|
+
newShapes[mainIdx].s.curves = tmpPath.curves;
|
|
150
|
+
|
|
151
|
+
if (holesFirst) mainIdx++;
|
|
152
|
+
newShapeHoles[mainIdx] = [];
|
|
153
|
+
|
|
154
|
+
//console.log('cw', i);
|
|
155
|
+
} else {
|
|
156
|
+
newShapeHoles[mainIdx].push({ h: tmpPath, p: tmpPoints[0] });
|
|
157
|
+
|
|
158
|
+
//console.log('ccw', i);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// only Holes? -> probably all Shapes with wrong orientation
|
|
163
|
+
if (!newShapes[0]) return toShapesNoHoles(subPaths);
|
|
164
|
+
|
|
165
|
+
if (newShapes.length > 1) {
|
|
166
|
+
let ambiguous = false;
|
|
167
|
+
let toChange = 0;
|
|
168
|
+
|
|
169
|
+
for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) {
|
|
170
|
+
betterShapeHoles[sIdx] = [];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) {
|
|
174
|
+
const sho = newShapeHoles[sIdx];
|
|
175
|
+
|
|
176
|
+
for (let hIdx = 0; hIdx < sho.length; hIdx++) {
|
|
177
|
+
const ho = sho[hIdx];
|
|
178
|
+
let hole_unassigned = true;
|
|
179
|
+
|
|
180
|
+
for (let s2Idx = 0; s2Idx < newShapes.length; s2Idx++) {
|
|
181
|
+
if (isPointInsidePolygon(ho.p, newShapes[s2Idx].p)) {
|
|
182
|
+
if (sIdx !== s2Idx) toChange++;
|
|
183
|
+
|
|
184
|
+
if (hole_unassigned) {
|
|
185
|
+
hole_unassigned = false;
|
|
186
|
+
betterShapeHoles[s2Idx].push(ho);
|
|
187
|
+
} else {
|
|
188
|
+
ambiguous = true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (hole_unassigned) {
|
|
194
|
+
betterShapeHoles[sIdx].push(ho);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (toChange > 0 && ambiguous === false) {
|
|
200
|
+
newShapeHoles = betterShapeHoles;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let tmpHoles;
|
|
205
|
+
|
|
206
|
+
for (let i = 0, il = newShapes.length; i < il; i++) {
|
|
207
|
+
tmpShape = newShapes[i].s;
|
|
208
|
+
shapes.push(tmpShape);
|
|
209
|
+
tmpHoles = newShapeHoles[i];
|
|
210
|
+
|
|
211
|
+
for (let j = 0, jl = tmpHoles.length; j < jl; j++) {
|
|
212
|
+
tmpShape.holes.push(tmpHoles[j].h);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
//console.log("shape", shapes);
|
|
217
|
+
|
|
218
|
+
return shapes;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export { ShapePath };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Earcut } from './Earcut.js';
|
|
2
|
+
|
|
3
|
+
class ShapeUtils {
|
|
4
|
+
// calculate area of the contour polygon
|
|
5
|
+
|
|
6
|
+
static area(contour) {
|
|
7
|
+
const n = contour.length;
|
|
8
|
+
let a = 0.0;
|
|
9
|
+
|
|
10
|
+
for (let p = n - 1, q = 0; q < n; p = q++) {
|
|
11
|
+
a += contour[p].x * contour[q].y - contour[q].x * contour[p].y;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return a * 0.5;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static isClockWise(pts) {
|
|
18
|
+
return ShapeUtils.area(pts) < 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static triangulateShape(contour, holes) {
|
|
22
|
+
const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ]
|
|
23
|
+
const holeIndices = []; // array of hole indices
|
|
24
|
+
const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ]
|
|
25
|
+
|
|
26
|
+
removeDupEndPts(contour);
|
|
27
|
+
addContour(vertices, contour);
|
|
28
|
+
|
|
29
|
+
//
|
|
30
|
+
|
|
31
|
+
let holeIndex = contour.length;
|
|
32
|
+
|
|
33
|
+
holes.forEach(removeDupEndPts);
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < holes.length; i++) {
|
|
36
|
+
holeIndices.push(holeIndex);
|
|
37
|
+
holeIndex += holes[i].length;
|
|
38
|
+
addContour(vertices, holes[i]);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//
|
|
42
|
+
|
|
43
|
+
const triangles = Earcut.triangulate(vertices, holeIndices);
|
|
44
|
+
|
|
45
|
+
//
|
|
46
|
+
|
|
47
|
+
for (let i = 0; i < triangles.length; i += 3) {
|
|
48
|
+
faces.push(triangles.slice(i, i + 3));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return faces;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function removeDupEndPts(points) {
|
|
56
|
+
const l = points.length;
|
|
57
|
+
|
|
58
|
+
if (l > 2 && points[l - 1].equals(points[0])) {
|
|
59
|
+
points.pop();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function addContour(vertices, contour) {
|
|
64
|
+
for (let i = 0; i < contour.length; i++) {
|
|
65
|
+
vertices.push(contour[i].x);
|
|
66
|
+
vertices.push(contour[i].y);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { ShapeUtils };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Curve } from '../Curve/Curve.js';
|
|
2
|
+
import { Vector2 } from '../Math/Vector2.js';
|
|
3
|
+
import { CubicBezier } from '../Curve/Interpolations.js';
|
|
4
|
+
|
|
5
|
+
class CubicBezierCurve extends Curve {
|
|
6
|
+
constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2()) {
|
|
7
|
+
super();
|
|
8
|
+
|
|
9
|
+
this.isCubicBezierCurve = true;
|
|
10
|
+
|
|
11
|
+
this.type = 'CubicBezierCurve';
|
|
12
|
+
|
|
13
|
+
this.v0 = v0;
|
|
14
|
+
this.v1 = v1;
|
|
15
|
+
this.v2 = v2;
|
|
16
|
+
this.v3 = v3;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getPoint(t, optionalTarget = new Vector2()) {
|
|
20
|
+
const point = optionalTarget;
|
|
21
|
+
|
|
22
|
+
const v0 = this.v0,
|
|
23
|
+
v1 = this.v1,
|
|
24
|
+
v2 = this.v2,
|
|
25
|
+
v3 = this.v3;
|
|
26
|
+
|
|
27
|
+
point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y));
|
|
28
|
+
|
|
29
|
+
return point;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
copy(source) {
|
|
33
|
+
super.copy(source);
|
|
34
|
+
|
|
35
|
+
this.v0.copy(source.v0);
|
|
36
|
+
this.v1.copy(source.v1);
|
|
37
|
+
this.v2.copy(source.v2);
|
|
38
|
+
this.v3.copy(source.v3);
|
|
39
|
+
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
toJSON() {
|
|
44
|
+
const data = super.toJSON();
|
|
45
|
+
|
|
46
|
+
data.v0 = this.v0.toArray();
|
|
47
|
+
data.v1 = this.v1.toArray();
|
|
48
|
+
data.v2 = this.v2.toArray();
|
|
49
|
+
data.v3 = this.v3.toArray();
|
|
50
|
+
|
|
51
|
+
return data;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fromJSON(json) {
|
|
55
|
+
super.fromJSON(json);
|
|
56
|
+
|
|
57
|
+
this.v0.fromArray(json.v0);
|
|
58
|
+
this.v1.fromArray(json.v1);
|
|
59
|
+
this.v2.fromArray(json.v2);
|
|
60
|
+
this.v3.fromArray(json.v3);
|
|
61
|
+
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { CubicBezierCurve };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// export { ArcCurve } from './ArcCurve.js';
|
|
2
|
+
// export { CatmullRomCurve3 } from './CatmullRomCurve3.js';
|
|
3
|
+
// export { CubicBezierCurve } from './CubicBezierCurve.js';
|
|
4
|
+
// export { CubicBezierCurve3 } from './CubicBezierCurve3.js';
|
|
5
|
+
// export { EllipseCurve } from './EllipseCurve.js';
|
|
6
|
+
export { LineCurve } from './LineCurve.js';
|
|
7
|
+
// export { LineCurve3 } from './LineCurve3.js';
|
|
8
|
+
// export { QuadraticBezierCurve } from './QuadraticBezierCurve.js';
|
|
9
|
+
// export { QuadraticBezierCurve3 } from './QuadraticBezierCurve3.js';
|
|
10
|
+
// export { SplineCurve } from './SplineCurve.js';
|