poly-extrude 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,41 @@
1
+ /* eslint-disable no-tabs */
2
+
3
+ // code copy from https://github.com/shawn0326/three.path/blob/master/src/PathPoint.js
4
+
5
+ import { Vector3 } from '../math/Vector3';
6
+
7
+ /**
8
+ * PathPoint
9
+ */
10
+ class PathPoint {
11
+ constructor() {
12
+ this.pos = new Vector3();
13
+ this.dir = new Vector3();
14
+ this.right = new Vector3();
15
+ this.up = new Vector3(); // normal
16
+ this.dist = 0; // distance from start
17
+ this.widthScale = 1; // for corner
18
+ this.sharp = false; // marks as sharp corner
19
+ }
20
+
21
+ lerpPathPoints(p1, p2, alpha) {
22
+ this.pos.lerpVectors(p1.pos, p2.pos, alpha);
23
+ this.dir.lerpVectors(p1.dir, p2.dir, alpha);
24
+ this.up.lerpVectors(p1.up, p2.up, alpha);
25
+ this.right.lerpVectors(p1.right, p2.right, alpha);
26
+ this.dist = (p2.dist - p1.dist) * alpha + p1.dist;
27
+ this.widthScale = (p2.widthScale - p1.widthScale) * alpha + p1.widthScale;
28
+ }
29
+
30
+ copy(source) {
31
+ this.pos.copy(source.pos);
32
+ this.dir.copy(source.dir);
33
+ this.up.copy(source.up);
34
+ this.right.copy(source.right);
35
+ this.dist = source.dist;
36
+ this.widthScale = source.widthScale;
37
+ }
38
+
39
+ }
40
+
41
+ export { PathPoint };
@@ -0,0 +1,251 @@
1
+ /* eslint-disable no-tabs */
2
+ // code copy from https://github.com/shawn0326/three.path/blob/master/src/PathPointList.js
3
+ import { Matrix4 } from '../math/Matrix4.js';
4
+ import { QuadraticBezierCurve3 } from '../math/QuadraticBezierCurve3.js';
5
+ import { Vector3 } from '../math/Vector3.js';
6
+ import { PathPoint } from './PathPoint.js';
7
+
8
+ const helpVec3_1 = new Vector3();
9
+ const helpVec3_2 = new Vector3();
10
+ const helpVec3_3 = new Vector3();
11
+ const helpMat4 = new Matrix4();
12
+ const helpCurve = new QuadraticBezierCurve3();
13
+
14
+ function _getCornerBezierCurve(last, current, next, cornerRadius, firstCorner, out) {
15
+ const lastDir = helpVec3_1.subVectors(current, last);
16
+ const nextDir = helpVec3_2.subVectors(next, current);
17
+
18
+ const lastDirLength = lastDir.length();
19
+ const nextDirLength = nextDir.length();
20
+
21
+ lastDir.normalize();
22
+ nextDir.normalize();
23
+
24
+ // cornerRadius can not bigger then lineDistance / 2, auto fix this
25
+ const v0Dist = Math.min((firstCorner ? lastDirLength / 2 : lastDirLength) * 0.999999, cornerRadius);
26
+ out.v0.copy(current).sub(lastDir.multiplyScalar(v0Dist));
27
+
28
+ out.v1.copy(current);
29
+
30
+ const v2Dist = Math.min(nextDirLength / 2 * 0.999999, cornerRadius);
31
+ out.v2.copy(current).add(nextDir.multiplyScalar(v2Dist));
32
+
33
+ return out;
34
+ }
35
+
36
+ /**
37
+ * PathPointList
38
+ * input points to generate a PathPoint list
39
+ */
40
+ class PathPointList {
41
+
42
+ constructor() {
43
+ this.array = []; // path point array
44
+ this.count = 0;
45
+ }
46
+
47
+ /**
48
+ * Set points
49
+ * @param {THREE.Vector3[]} points key points array
50
+ * @param {number} cornerRadius? the corner radius. set 0 to disable round corner. default is 0.1
51
+ * @param {number} cornerSplit? the corner split. default is 10.
52
+ * @param {number} up? force up. default is auto up (calculate by tangent).
53
+ * @param {boolean} close? close path. default is false.
54
+ */
55
+ set(points, cornerRadius = 0.1, cornerSplit = 10, up = null, close = false) {
56
+ points = points.slice(0);
57
+
58
+ if (points.length < 2) {
59
+ console.warn('PathPointList: points length less than 2.');
60
+ this.count = 0;
61
+ return;
62
+ }
63
+
64
+ // Auto close
65
+ if (close && !points[0].equals(points[points.length - 1])) {
66
+ points.push(new Vector3().copy(points[0]));
67
+ }
68
+
69
+ // Generate path point list
70
+ for (let i = 0, l = points.length; i < l; i++) {
71
+ if (i === 0) {
72
+ this._start(points[i], points[i + 1], up);
73
+ } else if (i === l - 1) {
74
+ if (close) {
75
+ // Connect end point and start point
76
+ this._corner(points[i], points[1], cornerRadius, cornerSplit, up);
77
+
78
+ // Fix start point
79
+ const dist = this.array[0].dist; // should not copy dist
80
+ this.array[0].copy(this.array[this.count - 1]);
81
+ this.array[0].dist = dist;
82
+ } else {
83
+ this._end(points[i]);
84
+ }
85
+ } else {
86
+ this._corner(points[i], points[i + 1], cornerRadius, cornerSplit, up);
87
+ }
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Get distance of this path
93
+ * @return {number}
94
+ */
95
+ distance() {
96
+ if (this.count > 0) {
97
+ return this.array[this.count - 1].dist;
98
+ }
99
+ return 0;
100
+ }
101
+
102
+ _getByIndex(index) {
103
+ if (!this.array[index]) {
104
+ this.array[index] = new PathPoint();
105
+ }
106
+ return this.array[index];
107
+ }
108
+
109
+ _start(current, next, up) {
110
+ this.count = 0;
111
+
112
+ const point = this._getByIndex(this.count);
113
+
114
+ point.pos.copy(current);
115
+ point.dir.subVectors(next, current);
116
+
117
+ // init start up dir
118
+ if (up) {
119
+ point.up.copy(up);
120
+ } else {
121
+ // select an initial normal vector perpendicular to the first tangent vector
122
+ let min = Number.MAX_VALUE;
123
+ const tx = Math.abs(point.dir.x);
124
+ const ty = Math.abs(point.dir.y);
125
+ const tz = Math.abs(point.dir.z);
126
+ if (tx < min) {
127
+ min = tx;
128
+ point.up.set(1, 0, 0);
129
+ }
130
+ if (ty < min) {
131
+ min = ty;
132
+ point.up.set(0, 1, 0);
133
+ }
134
+ if (tz < min) {
135
+ point.up.set(0, 0, 1);
136
+ }
137
+ }
138
+
139
+ point.right.crossVectors(point.dir, point.up).normalize();
140
+ point.up.crossVectors(point.right, point.dir).normalize();
141
+ point.dist = 0;
142
+ point.widthScale = 1;
143
+ point.sharp = false;
144
+
145
+ point.dir.normalize();
146
+
147
+ this.count++;
148
+ }
149
+
150
+ _end(current) {
151
+ const lastPoint = this.array[this.count - 1];
152
+ const point = this._getByIndex(this.count);
153
+
154
+ point.pos.copy(current);
155
+ point.dir.subVectors(current, lastPoint.pos);
156
+ const dist = point.dir.length();
157
+ point.dir.normalize();
158
+
159
+ point.up.copy(lastPoint.up); // copy last up
160
+
161
+ const vec = helpVec3_1.crossVectors(lastPoint.dir, point.dir);
162
+ if (vec.length() > Number.EPSILON) {
163
+ vec.normalize();
164
+ const theta = Math.acos(Math.min(Math.max(lastPoint.dir.dot(point.dir), -1), 1)); // clamp for floating pt errors
165
+ point.up.applyMatrix4(helpMat4.makeRotationAxis(vec, theta));
166
+ }
167
+
168
+ point.right.crossVectors(point.dir, point.up).normalize();
169
+
170
+ point.dist = lastPoint.dist + dist;
171
+ point.widthScale = 1;
172
+ point.sharp = false;
173
+
174
+ this.count++;
175
+ }
176
+
177
+ _corner(current, next, cornerRadius, cornerSplit, up) {
178
+ if (cornerRadius > 0 && cornerSplit > 0) {
179
+ const lastPoint = this.array[this.count - 1];
180
+ const curve = _getCornerBezierCurve(lastPoint.pos, current, next, cornerRadius, (this.count - 1) === 0, helpCurve);
181
+ const samplerPoints = curve.getPoints(cornerSplit); // TODO optimize
182
+
183
+ for (let f = 0; f < cornerSplit; f++) {
184
+ this._sharpCorner(samplerPoints[f], samplerPoints[f + 1], up, f === 0 ? 1 : 0);
185
+ }
186
+
187
+ if (!samplerPoints[cornerSplit].equals(next)) {
188
+ this._sharpCorner(samplerPoints[cornerSplit], next, up, 2);
189
+ }
190
+ } else {
191
+ this._sharpCorner(current, next, up, 0, true);
192
+ }
193
+ }
194
+
195
+ // dirType: 0 - use middle dir / 1 - use last dir / 2- use next dir
196
+ _sharpCorner(current, next, up, dirType = 0, sharp = false) {
197
+ const lastPoint = this.array[this.count - 1];
198
+ const point = this._getByIndex(this.count);
199
+
200
+ const lastDir = helpVec3_1.subVectors(current, lastPoint.pos);
201
+ const nextDir = helpVec3_2.subVectors(next, current);
202
+
203
+ const lastDirLength = lastDir.length();
204
+
205
+ lastDir.normalize();
206
+ nextDir.normalize();
207
+
208
+ point.pos.copy(current);
209
+
210
+ if (dirType === 1) {
211
+ point.dir.copy(lastDir);
212
+ } else if (dirType === 2) {
213
+ point.dir.copy(nextDir);
214
+ } else {
215
+ point.dir.addVectors(lastDir, nextDir);
216
+ point.dir.normalize();
217
+ }
218
+
219
+ if (up) {
220
+ if (point.dir.dot(up) === 1) {
221
+ point.right.crossVectors(nextDir, up).normalize();
222
+ } else {
223
+ point.right.crossVectors(point.dir, up).normalize();
224
+ }
225
+
226
+ point.up.crossVectors(point.right, point.dir).normalize();
227
+ } else {
228
+ point.up.copy(lastPoint.up);
229
+
230
+ const vec = helpVec3_3.crossVectors(lastPoint.dir, point.dir);
231
+ if (vec.length() > Number.EPSILON) {
232
+ vec.normalize();
233
+ const theta = Math.acos(Math.min(Math.max(lastPoint.dir.dot(point.dir), -1), 1)); // clamp for floating pt errors
234
+ point.up.applyMatrix4(helpMat4.makeRotationAxis(vec, theta));
235
+ }
236
+
237
+ point.right.crossVectors(point.dir, point.up).normalize();
238
+ }
239
+
240
+ point.dist = lastPoint.dist + lastDirLength;
241
+
242
+ const _cos = lastDir.dot(nextDir);
243
+ point.widthScale = Math.min(1 / Math.sqrt((1 + _cos) / 2), 1.415) || 1;
244
+ point.sharp = (Math.abs(_cos - 1) > 0.05) && sharp;
245
+
246
+ this.count++;
247
+ }
248
+
249
+ }
250
+
251
+ export { PathPointList };
package/src/path.js ADDED
@@ -0,0 +1,259 @@
1
+ import { Vector3 } from './math/Vector3';
2
+ import { PathPoint } from './path/PathPoint';
3
+ import { PathPointList } from './path/PathPointList';
4
+ import { merge } from './util';
5
+ const UP = new Vector3(0, 0, 1);
6
+
7
+ export function expandPaths(lines, options) {
8
+ options = Object.assign({}, { lineWidth: 1, cornerRadius: 0, cornerSplit: 10 }, options);
9
+ const results = lines.map(line => {
10
+ const points = line.map(p => {
11
+ const [x, y, z] = p;
12
+ return new Vector3(x, y, z || 0);
13
+ });
14
+ const pathPointList = new PathPointList();
15
+ pathPointList.set(points, options.cornerRadius, options.cornerSplit, UP);
16
+ const result = generatePathVertexData(pathPointList, options);
17
+ result.line = line;
18
+ result.position = new Float32Array(result.points);
19
+ result.indices = new Uint32Array(result.index);
20
+ result.uv = new Float32Array(result.uvs);
21
+ result.normal = new Float32Array(result.normal);
22
+ return result;
23
+ });
24
+ const result = merge(results);
25
+ result.lines = lines;
26
+ return result;
27
+ }
28
+
29
+ // Vertex Data Generate Functions
30
+ // code copy from https://github.com/shawn0326/three.path/blob/master/src/PathGeometry.js
31
+ function generatePathVertexData(pathPointList, options) {
32
+ const width = options.lineWidth || 0.1;
33
+ const progress = 1;
34
+ const side = 'both';
35
+
36
+ const halfWidth = width / 2;
37
+ const sideWidth = (side !== 'both' ? width / 2 : width);
38
+ const totalDistance = pathPointList.distance();
39
+ const progressDistance = progress * totalDistance;
40
+ if (totalDistance === 0) {
41
+ return null;
42
+ }
43
+
44
+ const sharpUvOffset = halfWidth / sideWidth;
45
+ // const sharpUvOffset2 = halfWidth / totalDistance;
46
+
47
+ let count = 0;
48
+
49
+ // modify data
50
+ const position = [];
51
+ const normal = [];
52
+ const uv = [];
53
+ const indices = [];
54
+ let verticesCount = 0;
55
+
56
+ const right = new Vector3();
57
+ const left = new Vector3();
58
+
59
+ // for sharp corners
60
+ const leftOffset = new Vector3();
61
+ const rightOffset = new Vector3();
62
+ const tempPoint1 = new Vector3();
63
+ const tempPoint2 = new Vector3();
64
+
65
+ function addVertices(pathPoint) {
66
+ const first = position.length === 0;
67
+ const sharpCorner = pathPoint.sharp && !first;
68
+
69
+ const uvDist = pathPoint.dist / sideWidth;
70
+ // const uvDist2 = pathPoint.dist / totalDistance;
71
+
72
+ const dir = pathPoint.dir;
73
+ const up = pathPoint.up;
74
+ const _right = pathPoint.right;
75
+
76
+ if (side !== 'left') {
77
+ right.copy(_right).multiplyScalar(halfWidth * pathPoint.widthScale);
78
+ } else {
79
+ right.set(0, 0, 0);
80
+ }
81
+
82
+ if (side !== 'right') {
83
+ left.copy(_right).multiplyScalar(-halfWidth * pathPoint.widthScale);
84
+ } else {
85
+ left.set(0, 0, 0);
86
+ }
87
+
88
+ right.add(pathPoint.pos);
89
+ left.add(pathPoint.pos);
90
+
91
+ if (sharpCorner) {
92
+ leftOffset.fromArray(position, position.length - 6).sub(left);
93
+ rightOffset.fromArray(position, position.length - 3).sub(right);
94
+
95
+ const leftDist = leftOffset.length();
96
+ const rightDist = rightOffset.length();
97
+
98
+ const sideOffset = leftDist - rightDist;
99
+ let longerOffset, longEdge;
100
+
101
+ if (sideOffset > 0) {
102
+ longerOffset = leftOffset;
103
+ longEdge = left;
104
+ } else {
105
+ longerOffset = rightOffset;
106
+ longEdge = right;
107
+ }
108
+
109
+ tempPoint1.copy(longerOffset).setLength(Math.abs(sideOffset)).add(longEdge);
110
+
111
+ // eslint-disable-next-line prefer-const
112
+ let _cos = tempPoint2.copy(longEdge).sub(tempPoint1).normalize().dot(dir);
113
+ // eslint-disable-next-line prefer-const
114
+ let _len = tempPoint2.copy(longEdge).sub(tempPoint1).length();
115
+ // eslint-disable-next-line prefer-const
116
+ let _dist = _cos * _len * 2;
117
+
118
+ tempPoint2.copy(dir).setLength(_dist).add(tempPoint1);
119
+
120
+ if (sideOffset > 0) {
121
+ position.push(
122
+ tempPoint1.x, tempPoint1.y, tempPoint1.z, // 6
123
+ right.x, right.y, right.z, // 5
124
+ left.x, left.y, left.z, // 4
125
+ right.x, right.y, right.z, // 3
126
+ tempPoint2.x, tempPoint2.y, tempPoint2.z, // 2
127
+ right.x, right.y, right.z // 1
128
+ );
129
+
130
+ verticesCount += 6;
131
+
132
+ indices.push(
133
+ verticesCount - 6, verticesCount - 8, verticesCount - 7,
134
+ verticesCount - 6, verticesCount - 7, verticesCount - 5,
135
+
136
+ verticesCount - 4, verticesCount - 6, verticesCount - 5,
137
+ verticesCount - 2, verticesCount - 4, verticesCount - 1
138
+ );
139
+
140
+ count += 12;
141
+ } else {
142
+ position.push(
143
+ left.x, left.y, left.z, // 6
144
+ tempPoint1.x, tempPoint1.y, tempPoint1.z, // 5
145
+ left.x, left.y, left.z, // 4
146
+ right.x, right.y, right.z, // 3
147
+ left.x, left.y, left.z, // 2
148
+ tempPoint2.x, tempPoint2.y, tempPoint2.z // 1
149
+ );
150
+
151
+ verticesCount += 6;
152
+
153
+ indices.push(
154
+ verticesCount - 6, verticesCount - 8, verticesCount - 7,
155
+ verticesCount - 6, verticesCount - 7, verticesCount - 5,
156
+
157
+ verticesCount - 6, verticesCount - 5, verticesCount - 3,
158
+ verticesCount - 2, verticesCount - 3, verticesCount - 1
159
+ );
160
+
161
+ count += 12;
162
+ }
163
+
164
+ normal.push(
165
+ up.x, up.y, up.z,
166
+ up.x, up.y, up.z,
167
+ up.x, up.y, up.z,
168
+ up.x, up.y, up.z,
169
+ up.x, up.y, up.z,
170
+ up.x, up.y, up.z
171
+ );
172
+
173
+ uv.push(
174
+ uvDist - sharpUvOffset, 0,
175
+ uvDist - sharpUvOffset, 1,
176
+ uvDist, 0,
177
+ uvDist, 1,
178
+ uvDist + sharpUvOffset, 0,
179
+ uvDist + sharpUvOffset, 1
180
+ );
181
+
182
+ // if (generateUv2) {
183
+ // uv2.push(
184
+ // uvDist2 - sharpUvOffset2, 0,
185
+ // uvDist2 - sharpUvOffset2, 1,
186
+ // uvDist2, 0,
187
+ // uvDist2, 1,
188
+ // uvDist2 + sharpUvOffset2, 0,
189
+ // uvDist2 + sharpUvOffset2, 1
190
+ // );
191
+ // }
192
+ } else {
193
+ position.push(
194
+ left.x, left.y, left.z,
195
+ right.x, right.y, right.z
196
+ );
197
+
198
+ normal.push(
199
+ up.x, up.y, up.z,
200
+ up.x, up.y, up.z
201
+ );
202
+
203
+ uv.push(
204
+ uvDist, 0,
205
+ uvDist, 1
206
+ );
207
+
208
+ // if (generateUv2) {
209
+ // uv2.push(
210
+ // uvDist2, 0,
211
+ // uvDist2, 1
212
+ // );
213
+ // }
214
+
215
+ verticesCount += 2;
216
+
217
+ if (!first) {
218
+ indices.push(
219
+ verticesCount - 2, verticesCount - 4, verticesCount - 3,
220
+ verticesCount - 2, verticesCount - 3, verticesCount - 1
221
+ );
222
+
223
+ count += 6;
224
+ }
225
+ }
226
+ }
227
+
228
+ let lastPoint;
229
+
230
+ if (progressDistance > 0) {
231
+ for (let i = 0; i < pathPointList.count; i++) {
232
+ const pathPoint = pathPointList.array[i];
233
+
234
+ if (pathPoint.dist > progressDistance) {
235
+ const prevPoint = pathPointList.array[i - 1];
236
+ lastPoint = new PathPoint();
237
+
238
+ // linear lerp for progress
239
+ const alpha = (progressDistance - prevPoint.dist) / (pathPoint.dist - prevPoint.dist);
240
+ lastPoint.lerpPathPoints(prevPoint, pathPoint, alpha);
241
+
242
+ addVertices(lastPoint);
243
+ break;
244
+ } else {
245
+ addVertices(pathPoint);
246
+ }
247
+ }
248
+ } else {
249
+ lastPoint = pathPointList.array[0];
250
+ }
251
+
252
+ return {
253
+ points: position,
254
+ normal,
255
+ uvs: uv,
256
+ index: indices,
257
+ count
258
+ };
259
+ }
package/src/polyline.js CHANGED
@@ -26,29 +26,29 @@ function generateTopAndBottom(result, options) {
26
26
  while (i < len) {
27
27
  // top left
28
28
  const idx0 = i * 3;
29
- const [x1, y1] = leftPoints[i];
29
+ const [x1, y1, z1] = leftPoints[i];
30
30
  points[idx0] = x1;
31
31
  points[idx0 + 1] = y1;
32
- points[idx0 + 2] = z;
32
+ points[idx0 + 2] = z + z1;
33
33
 
34
34
  // top right
35
- const [x2, y2] = rightPoints[i];
35
+ const [x2, y2, z2] = rightPoints[i];
36
36
  const idx1 = len * 3 + idx0;
37
37
  points[idx1] = x2;
38
38
  points[idx1 + 1] = y2;
39
- points[idx1 + 2] = z;
39
+ points[idx1 + 2] = z + z2;
40
40
 
41
41
  // bottom left
42
42
  const idx2 = (len * 2) * 3 + idx0;
43
43
  points[idx2] = x1;
44
44
  points[idx2 + 1] = y1;
45
- points[idx2 + 2] = 0;
45
+ points[idx2 + 2] = z1;
46
46
 
47
47
  // bottom right
48
48
  const idx3 = (len * 2) * 3 + len * 3 + idx0;
49
49
  points[idx3] = x2;
50
50
  points[idx3 + 1] = y2;
51
- points[idx3 + 2] = 0;
51
+ points[idx3 + 2] = z2;
52
52
 
53
53
  i++;
54
54
  }
@@ -88,7 +88,7 @@ function generateSides(result, options) {
88
88
 
89
89
  function addOneSideIndex(v1, v2) {
90
90
  const idx = points.length / 3;
91
- points.push(v1[0], v1[1], z, v2[0], v2[1], z, v1[0], v1[1], 0, v2[0], v2[1], 0);
91
+ points.push(v1[0], v1[1], z + v1[2], v2[0], v2[1], z + v2[2], v1[0], v1[1], v1[2], v2[0], v2[1], v2[2]);
92
92
  const a = idx + 2, b = idx + 3, c = idx, d = idx + 1;
93
93
  index.push(a, c, b, c, d, b);
94
94
  generateSideWallUV(uvs, points, a, b, c, d);
@@ -180,11 +180,12 @@ export function expandLine(line, options) {
180
180
 
181
181
  function calOffsetPoint(rad, radius, p) {
182
182
  const [x, y] = p;
183
+ const z = p[2] || 0;
183
184
  const x1 = Math.cos(rad) * radius, y1 = Math.sin(rad) * radius;
184
- const p1 = [x + x1, y + y1];
185
+ const p1 = [x + x1, y + y1, z];
185
186
  const rad1 = rad += Math.PI;
186
187
  const x2 = Math.cos(rad1) * radius, y2 = Math.sin(rad1) * radius;
187
- const p2 = [x + x2, y + y2];
188
+ const p2 = [x + x2, y + y2, z];
188
189
  return [p1, p2];
189
190
  }
190
191