poly-extrude 0.18.1 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/poly-extrude.js +4538 -4365
- package/dist/poly-extrude.js.map +1 -1
- package/dist/poly-extrude.min.js +2 -2
- package/dist/poly-extrude.mjs +347 -174
- package/dist/poly-extrude.mjs.map +1 -1
- package/dist/polyline.d.ts +18 -7
- package/dist/polyline.js +141 -21
- package/dist/polyline.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +1 -0
- package/src/polyline.ts +168 -31
package/src/polyline.ts
CHANGED
@@ -7,13 +7,23 @@ function checkOptions(options) {
|
|
7
7
|
options.sideDepth = Math.max(0, options.sideDepth);
|
8
8
|
}
|
9
9
|
|
10
|
-
type
|
11
|
-
depth?: number;
|
10
|
+
type ExpandLineOptions = {
|
12
11
|
lineWidth?: number;
|
12
|
+
cutCorner?: boolean;
|
13
|
+
}
|
14
|
+
|
15
|
+
type PolylinesOptions = ExpandLineOptions & {
|
16
|
+
depth?: number;
|
13
17
|
bottomStickGround?: boolean;
|
14
18
|
pathUV?: boolean;
|
15
19
|
}
|
16
20
|
|
21
|
+
type SlopesOptions = PolylinesOptions & {
|
22
|
+
side?: 'left' | 'right',
|
23
|
+
sideDepth?: number
|
24
|
+
}
|
25
|
+
|
26
|
+
|
17
27
|
type PolylinesResult = ResultType & {
|
18
28
|
lines: Array<PolylineType>;
|
19
29
|
}
|
@@ -37,11 +47,6 @@ export function extrudePolylines(lines: Array<PolylineType>, opts?: PolylinesOpt
|
|
37
47
|
return result;
|
38
48
|
}
|
39
49
|
|
40
|
-
type SlopesOptions = PolylinesOptions & {
|
41
|
-
side?: 'left' | 'right',
|
42
|
-
sideDepth?: number
|
43
|
-
}
|
44
|
-
|
45
50
|
export function extrudeSlopes(lines: Array<PolylineType>, opts?: SlopesOptions): PolylinesResult {
|
46
51
|
const options = Object.assign({}, { depth: 2, lineWidth: 1, side: 'left', sideDepth: 0, bottomStickGround: false, pathUV: false, isSlope: true }, opts);
|
47
52
|
checkOptions(options);
|
@@ -90,10 +95,15 @@ function generateTopAndBottom(result, options) {
|
|
90
95
|
const { leftPoints, rightPoints } = result;
|
91
96
|
const line = result.line;
|
92
97
|
const pathUV = options.pathUV;
|
98
|
+
let uvCalPath = line;
|
99
|
+
// let needCalUV = false;
|
100
|
+
if (leftPoints.length > uvCalPath.length) {
|
101
|
+
uvCalPath = leftPoints;
|
102
|
+
}
|
93
103
|
if (pathUV) {
|
94
|
-
calLineDistance(
|
95
|
-
for (let i = 0, len =
|
96
|
-
leftPoints[i].distance = rightPoints[i].distance =
|
104
|
+
calLineDistance(uvCalPath);
|
105
|
+
for (let i = 0, len = uvCalPath.length; i < len; i++) {
|
106
|
+
leftPoints[i].distance = rightPoints[i].distance = uvCalPath[i].distance;
|
97
107
|
}
|
98
108
|
}
|
99
109
|
let i = 0, len = leftPoints.length;
|
@@ -106,6 +116,8 @@ function generateTopAndBottom(result, options) {
|
|
106
116
|
points[idx0 + 1] = y1;
|
107
117
|
points[idx0 + 2] = lz + z1;
|
108
118
|
|
119
|
+
// const p1 = leftPoints[i];
|
120
|
+
|
109
121
|
// top right
|
110
122
|
const [x2, y2, z2] = rightPoints[i];
|
111
123
|
const idx1 = len * 3 + idx0;
|
@@ -113,6 +125,8 @@ function generateTopAndBottom(result, options) {
|
|
113
125
|
points[idx1 + 1] = y2;
|
114
126
|
points[idx1 + 2] = rz + z2;
|
115
127
|
|
128
|
+
// const p2 = rightPoints[i];
|
129
|
+
|
116
130
|
// bottom left
|
117
131
|
const idx2 = (len * 2) * 3 + idx0;
|
118
132
|
points[idx2] = x1;
|
@@ -133,8 +147,8 @@ function generateTopAndBottom(result, options) {
|
|
133
147
|
|
134
148
|
// generate path uv
|
135
149
|
if (pathUV) {
|
136
|
-
const p =
|
137
|
-
|
150
|
+
const p = uvCalPath[i];
|
151
|
+
let uvx = p.distance;
|
138
152
|
|
139
153
|
const uIndex0 = i * 2;
|
140
154
|
uv[uIndex0] = uvx;
|
@@ -307,56 +321,118 @@ function generateSides(result, options) {
|
|
307
321
|
|
308
322
|
const TEMPV1 = { x: 0, y: 0 }, TEMPV2 = { x: 0, y: 0 };
|
309
323
|
|
310
|
-
export function expandLine(line, options) {
|
311
|
-
|
324
|
+
export function expandLine(line, options?: ExpandLineOptions) {
|
325
|
+
options = Object.assign({}, { lineWidth: 1, cutCorner: false }, options);
|
312
326
|
let radius = options.lineWidth / 2;
|
313
|
-
if (options.isSlope) {
|
327
|
+
if ((options as any).isSlope) {
|
314
328
|
radius *= 2;
|
315
329
|
}
|
330
|
+
const { cutCorner } = options;
|
316
331
|
const points: Array<number[]> = [], leftPoints: Array<number[]> = [], rightPoints: Array<number[]> = [];
|
317
332
|
const len = line.length;
|
333
|
+
|
334
|
+
const repeatVertex = () => {
|
335
|
+
const len1 = leftPoints.length;
|
336
|
+
if (len1) {
|
337
|
+
leftPoints.push(leftPoints[len1 - 1]);
|
338
|
+
rightPoints.push(rightPoints[len1 - 1]);
|
339
|
+
const len2 = points.length;
|
340
|
+
points.push(points[len2 - 2], points[len2 - 1]);
|
341
|
+
}
|
342
|
+
};
|
343
|
+
|
344
|
+
const equal = (p1, p2) => {
|
345
|
+
return p1[0] === p2[0] && p1[1] === p2[1];
|
346
|
+
};
|
347
|
+
|
318
348
|
let i = 0;
|
349
|
+
let preleftline, prerightline;
|
319
350
|
while (i < len) {
|
351
|
+
let p0;
|
320
352
|
let p1 = line[i],
|
321
353
|
p2 = line[i + 1];
|
322
|
-
const currentp =
|
354
|
+
const currentp = p1;
|
323
355
|
// last vertex
|
324
356
|
if (i === len - 1) {
|
325
357
|
p1 = line[len - 2];
|
326
358
|
p2 = line[len - 1];
|
359
|
+
if (equal(p1, p2)) {
|
360
|
+
for (let j = line.indexOf(p1); j >= 0; j--) {
|
361
|
+
const p = line[j];
|
362
|
+
if (!equal(p, currentp)) {
|
363
|
+
p1 = p;
|
364
|
+
break;
|
365
|
+
}
|
366
|
+
}
|
367
|
+
}
|
368
|
+
} else {
|
369
|
+
if (equal(p1, p2)) {
|
370
|
+
for (let j = line.indexOf(p2); j < len; j++) {
|
371
|
+
const p = line[j];
|
372
|
+
if (!equal(p, currentp)) {
|
373
|
+
p2 = p;
|
374
|
+
break;
|
375
|
+
}
|
376
|
+
}
|
377
|
+
}
|
378
|
+
}
|
379
|
+
if (equal(p1, p2)) {
|
380
|
+
repeatVertex();
|
381
|
+
i++;
|
382
|
+
continue;
|
327
383
|
}
|
328
|
-
|
384
|
+
|
385
|
+
let dy = p2[1] - p1[1],
|
329
386
|
dx = p2[0] - p1[0];
|
387
|
+
|
330
388
|
let rAngle = 0;
|
331
|
-
const rad = Math.
|
389
|
+
const rad = Math.atan2(dy, dx);
|
332
390
|
const angle = radToDeg(rad);
|
333
|
-
// preAngle = angle;
|
334
391
|
if (i === 0 || i === len - 1) {
|
335
392
|
rAngle = angle;
|
336
393
|
rAngle -= 90;
|
337
394
|
} else {
|
338
395
|
// 至少3个顶点才会触发
|
339
|
-
|
396
|
+
p0 = line[i - 1];
|
397
|
+
if (equal(p0, p2) || equal(p0, p1)) {
|
398
|
+
for (let j = line.indexOf(p2); j >= 0; j--) {
|
399
|
+
const p = line[j];
|
400
|
+
if (!equal(p, p2) && (!equal(p, p1))) {
|
401
|
+
p0 = p;
|
402
|
+
break;
|
403
|
+
}
|
404
|
+
}
|
405
|
+
}
|
406
|
+
if (equal(p0, p2) || equal(p0, p1) || equal(p1, p2)) {
|
407
|
+
repeatVertex();
|
408
|
+
i++;
|
409
|
+
continue;
|
410
|
+
}
|
340
411
|
TEMPV1.x = p0[0] - p1[0];
|
341
412
|
TEMPV1.y = p0[1] - p1[1];
|
342
413
|
TEMPV2.x = p2[0] - p1[0];
|
343
414
|
TEMPV2.y = p2[1] - p1[1];
|
415
|
+
if ((TEMPV1.x === 0 && TEMPV1.y === 0) || (TEMPV2.x === 0 && TEMPV2.y === 0)) {
|
416
|
+
console.error('has repeat vertex,the index:', i);
|
417
|
+
}
|
344
418
|
const vAngle = getAngle(TEMPV1, TEMPV2);
|
345
419
|
rAngle = angle - vAngle / 2;
|
346
420
|
}
|
421
|
+
|
347
422
|
const rRad = degToRad(rAngle);
|
348
423
|
const p3 = currentp;
|
349
424
|
const x = Math.cos(rRad) + p3[0], y = Math.sin(rRad) + p3[1];
|
350
425
|
const p4 = [x, y];
|
351
|
-
const [
|
352
|
-
let op1 = lineIntersection(
|
353
|
-
let op2 = lineIntersection(
|
426
|
+
const [leftline, rightline] = translateLine(p1, p2, radius);
|
427
|
+
let op1 = lineIntersection(leftline[0], leftline[1], p3, p4);
|
428
|
+
let op2 = lineIntersection(rightline[0], rightline[1], p3, p4);
|
354
429
|
// 平行,回头路
|
355
430
|
if (!op1 || !op2) {
|
356
431
|
const len1 = points.length;
|
357
432
|
const point1 = points[len1 - 2];
|
358
433
|
const point2 = points[len1 - 1];
|
359
434
|
if (!point1 || !point2) {
|
435
|
+
i++;
|
360
436
|
continue;
|
361
437
|
}
|
362
438
|
op1 = [point1[0], point1[1]];
|
@@ -366,13 +442,64 @@ export function expandLine(line, options) {
|
|
366
442
|
op2[2] = currentp[2] || 0;
|
367
443
|
// const [op1, op2] = calOffsetPoint(rRad, radius, p1);
|
368
444
|
points.push(op1, op2);
|
369
|
-
|
370
|
-
|
371
|
-
|
445
|
+
let needCut = false;
|
446
|
+
if (cutCorner) {
|
447
|
+
const bufferRadius = radius * 2;
|
448
|
+
if (distance(currentp, op1) > bufferRadius || distance(currentp, op2) > bufferRadius) {
|
449
|
+
needCut = true;
|
450
|
+
}
|
451
|
+
}
|
452
|
+
if (needCut && p0 && preleftline && prerightline) {
|
453
|
+
let cutPoint = op1;
|
454
|
+
if (distance(op1, p0) < distance(op2, p0)) {
|
455
|
+
cutPoint = op2;
|
456
|
+
}
|
457
|
+
const dy = cutPoint[1] - currentp[1], dx = cutPoint[0] - currentp[0];
|
458
|
+
const cutAngle = Math.atan2(dy, dx) / Math.PI * 180;
|
459
|
+
const cutRad = degToRad(cutAngle);
|
460
|
+
const x1 = Math.cos(cutRad) * radius + currentp[0];
|
461
|
+
const y1 = Math.sin(cutRad) * radius + currentp[1];
|
462
|
+
const v1 = [x1, y1];
|
463
|
+
|
464
|
+
const hcutangle = cutAngle + 90;
|
465
|
+
// console.log(i, cutAngle, hcutangle);
|
466
|
+
const hcutRad = degToRad(hcutangle);
|
467
|
+
const x2 = Math.cos(hcutRad) + x1;
|
468
|
+
const y2 = Math.sin(hcutRad) + y1;
|
469
|
+
const v2 = [x2, y2];
|
470
|
+
|
471
|
+
let preline = preleftline;
|
472
|
+
let currentLine = leftline;
|
473
|
+
let appendArray = leftPoints;
|
474
|
+
let repeatArray = rightPoints;
|
475
|
+
if (!leftOnLine(cutPoint, p1, p2)) {
|
476
|
+
preline = prerightline;
|
477
|
+
currentLine = rightline;
|
478
|
+
appendArray = rightPoints;
|
479
|
+
repeatArray = leftPoints;
|
480
|
+
}
|
481
|
+
|
482
|
+
let cross1 = lineIntersection(preline[0], preline[1], v1, v2);
|
483
|
+
let cross2 = lineIntersection(currentLine[0], currentLine[1], v1, v2);
|
484
|
+
|
485
|
+
cross1[2] = currentp[2] || 0;
|
486
|
+
cross2[2] = currentp[2] || 0;
|
487
|
+
const repeatPoint = cutPoint === op1 ? op2 : op1;
|
488
|
+
appendArray.push(cross1, cross2);
|
489
|
+
repeatArray.push(repeatPoint, [...repeatPoint]);
|
372
490
|
} else {
|
373
|
-
|
374
|
-
|
491
|
+
if (leftOnLine(op1, p1, p2)) {
|
492
|
+
leftPoints.push(op1);
|
493
|
+
rightPoints.push(op2);
|
494
|
+
} else {
|
495
|
+
leftPoints.push(op2);
|
496
|
+
rightPoints.push(op1);
|
497
|
+
}
|
375
498
|
}
|
499
|
+
|
500
|
+
preleftline = leftline;
|
501
|
+
prerightline = rightline;
|
502
|
+
|
376
503
|
i++;
|
377
504
|
}
|
378
505
|
|
@@ -395,9 +522,15 @@ const getAngle = ({ x: x1, y: y1 }, { x: x2, y: y2 }) => {
|
|
395
522
|
const dot = x1 * x2 + y1 * y2;
|
396
523
|
const det = x1 * y2 - y1 * x2;
|
397
524
|
const angle = Math.atan2(det, dot) / Math.PI * 180;
|
398
|
-
return (angle
|
525
|
+
return (angle);
|
526
|
+
// return (angle + 360) % 360;
|
399
527
|
};
|
400
528
|
|
529
|
+
function distance(p1, p2) {
|
530
|
+
const dx = p2[0] - p1[0], dy = p2[1] - p1[1];
|
531
|
+
return Math.sqrt(dx * dx + dy * dy);
|
532
|
+
}
|
533
|
+
|
401
534
|
export function leftOnLine(p, p1, p2) {
|
402
535
|
const [x1, y1] = p1;
|
403
536
|
const [x2, y2] = p2;
|
@@ -412,8 +545,11 @@ export function leftOnLine(p, p1, p2) {
|
|
412
545
|
* @param {*} distance
|
413
546
|
* @returns
|
414
547
|
*/
|
415
|
-
function translateLine(p1, p2, distance) {
|
548
|
+
export function translateLine(p1, p2, distance) {
|
416
549
|
const dy = p2[1] - p1[1], dx = p2[0] - p1[0];
|
550
|
+
if (dy === 0 && dx === 0) {
|
551
|
+
return null;
|
552
|
+
}
|
417
553
|
const rad = Math.atan2(dy, dx);
|
418
554
|
const rad1 = rad + Math.PI / 2;
|
419
555
|
let offsetX = Math.cos(rad1) * distance, offsetY = Math.sin(rad1) * distance;
|
@@ -438,13 +574,14 @@ function translateLine(p1, p2, distance) {
|
|
438
574
|
function lineIntersection(p1, p2, p3, p4): Array<number> | null {
|
439
575
|
const dx1 = p2[0] - p1[0], dy1 = p2[1] - p1[1];
|
440
576
|
const dx2 = p4[0] - p3[0], dy2 = p4[1] - p3[1];
|
577
|
+
//vertical
|
441
578
|
if (dx1 === 0 && dx2 === 0) {
|
442
579
|
return null;
|
443
580
|
}
|
581
|
+
//horizontal
|
444
582
|
if (dy1 === 0 && dy2 === 0) {
|
445
583
|
return null;
|
446
584
|
}
|
447
|
-
|
448
585
|
const k1 = dy1 / dx1;
|
449
586
|
const k2 = dy2 / dx2;
|
450
587
|
|