@pooder/kit 4.3.0 → 4.3.1
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/CHANGELOG.md +6 -0
- package/dist/index.js +48 -37
- package/dist/index.mjs +48 -37
- package/package.json +1 -1
- package/src/dieline.ts +19 -1
- package/src/geometry.ts +76 -80
package/CHANGELOG.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -837,47 +837,43 @@ function getPerimeterShape(options) {
|
|
|
837
837
|
const bridgeTop = mainBounds.top;
|
|
838
838
|
const bridgeBottom = itemBounds.top;
|
|
839
839
|
if (bridgeBottom > bridgeTop) {
|
|
840
|
-
const
|
|
841
|
-
const
|
|
842
|
-
from: [
|
|
843
|
-
to: [
|
|
840
|
+
const centerX = itemBounds.center.x;
|
|
841
|
+
const ray = new import_paper2.default.Path.Line({
|
|
842
|
+
from: [centerX, bridgeBottom],
|
|
843
|
+
to: [centerX, bridgeTop - 10],
|
|
844
|
+
// Extend slightly past top to ensure intersection
|
|
844
845
|
insert: false
|
|
845
846
|
});
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
let
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
let bestChild = null;
|
|
856
|
-
for (const child of children) {
|
|
857
|
-
if (child.bounds.bottom > maxBottom) {
|
|
858
|
-
maxBottom = child.bounds.bottom;
|
|
859
|
-
bestChild = child;
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
if (bestChild && isBottomPart(bestChild)) {
|
|
863
|
-
bridgePart = bestChild.clone();
|
|
864
|
-
}
|
|
865
|
-
} else if (gaps instanceof import_paper2.default.Path) {
|
|
866
|
-
if (isBottomPart(gaps)) {
|
|
867
|
-
bridgePart = gaps.clone();
|
|
847
|
+
const intersections = mainShape.getIntersections(ray);
|
|
848
|
+
let targetY = bridgeTop;
|
|
849
|
+
let found = false;
|
|
850
|
+
if (intersections && intersections.length > 0) {
|
|
851
|
+
const validHits = intersections.filter((i) => i.point.y < bridgeBottom - 0.1);
|
|
852
|
+
if (validHits.length > 0) {
|
|
853
|
+
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
854
|
+
targetY = validHits[0].point.y;
|
|
855
|
+
found = true;
|
|
868
856
|
}
|
|
869
857
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
858
|
+
ray.remove();
|
|
859
|
+
const overlap = 2;
|
|
860
|
+
const rectBottom = bridgeBottom;
|
|
861
|
+
let rectTop = found ? targetY + overlap : bridgeTop;
|
|
862
|
+
if (!found) {
|
|
863
|
+
if (mainBounds.bottom < bridgeBottom) {
|
|
864
|
+
targetY = mainBounds.bottom;
|
|
865
|
+
rectTop = targetY - overlap;
|
|
877
866
|
}
|
|
878
|
-
|
|
867
|
+
}
|
|
868
|
+
if (rectTop < rectBottom) {
|
|
869
|
+
const bridgeRect = new import_paper2.default.Path.Rectangle({
|
|
870
|
+
from: [itemBounds.left, rectTop],
|
|
871
|
+
to: [itemBounds.right, rectBottom],
|
|
872
|
+
insert: false
|
|
873
|
+
});
|
|
874
|
+
const unitedItem = item.unite(bridgeRect);
|
|
879
875
|
item.remove();
|
|
880
|
-
|
|
876
|
+
bridgeRect.remove();
|
|
881
877
|
if (f.operation === "add") {
|
|
882
878
|
adds.push(unitedItem);
|
|
883
879
|
} else {
|
|
@@ -1435,7 +1431,19 @@ var DielineTool = class {
|
|
|
1435
1431
|
title: "Detect Edge from Image",
|
|
1436
1432
|
handler: async (imageUrl, options) => {
|
|
1437
1433
|
try {
|
|
1438
|
-
const
|
|
1434
|
+
const loadImage = (url) => {
|
|
1435
|
+
return new Promise((resolve, reject) => {
|
|
1436
|
+
const img2 = new Image();
|
|
1437
|
+
img2.crossOrigin = "Anonymous";
|
|
1438
|
+
img2.onload = () => resolve(img2);
|
|
1439
|
+
img2.onerror = (e) => reject(e);
|
|
1440
|
+
img2.src = url;
|
|
1441
|
+
});
|
|
1442
|
+
};
|
|
1443
|
+
const [img, pathData] = await Promise.all([
|
|
1444
|
+
loadImage(imageUrl),
|
|
1445
|
+
ImageTracer.trace(imageUrl, options)
|
|
1446
|
+
]);
|
|
1439
1447
|
const bounds = getPathBounds(pathData);
|
|
1440
1448
|
const currentMax = Math.max(s.width, s.height);
|
|
1441
1449
|
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
@@ -1444,7 +1452,10 @@ var DielineTool = class {
|
|
|
1444
1452
|
return {
|
|
1445
1453
|
pathData,
|
|
1446
1454
|
width: newWidth,
|
|
1447
|
-
height: newHeight
|
|
1455
|
+
height: newHeight,
|
|
1456
|
+
rawBounds: bounds,
|
|
1457
|
+
imageWidth: img.width,
|
|
1458
|
+
imageHeight: img.height
|
|
1448
1459
|
};
|
|
1449
1460
|
} catch (e) {
|
|
1450
1461
|
console.error("Edge detection failed", e);
|
package/dist/index.mjs
CHANGED
|
@@ -795,47 +795,43 @@ function getPerimeterShape(options) {
|
|
|
795
795
|
const bridgeTop = mainBounds.top;
|
|
796
796
|
const bridgeBottom = itemBounds.top;
|
|
797
797
|
if (bridgeBottom > bridgeTop) {
|
|
798
|
-
const
|
|
799
|
-
const
|
|
800
|
-
from: [
|
|
801
|
-
to: [
|
|
798
|
+
const centerX = itemBounds.center.x;
|
|
799
|
+
const ray = new paper2.Path.Line({
|
|
800
|
+
from: [centerX, bridgeBottom],
|
|
801
|
+
to: [centerX, bridgeTop - 10],
|
|
802
|
+
// Extend slightly past top to ensure intersection
|
|
802
803
|
insert: false
|
|
803
804
|
});
|
|
804
|
-
const
|
|
805
|
-
|
|
806
|
-
let
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
let bestChild = null;
|
|
814
|
-
for (const child of children) {
|
|
815
|
-
if (child.bounds.bottom > maxBottom) {
|
|
816
|
-
maxBottom = child.bounds.bottom;
|
|
817
|
-
bestChild = child;
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
if (bestChild && isBottomPart(bestChild)) {
|
|
821
|
-
bridgePart = bestChild.clone();
|
|
822
|
-
}
|
|
823
|
-
} else if (gaps instanceof paper2.Path) {
|
|
824
|
-
if (isBottomPart(gaps)) {
|
|
825
|
-
bridgePart = gaps.clone();
|
|
805
|
+
const intersections = mainShape.getIntersections(ray);
|
|
806
|
+
let targetY = bridgeTop;
|
|
807
|
+
let found = false;
|
|
808
|
+
if (intersections && intersections.length > 0) {
|
|
809
|
+
const validHits = intersections.filter((i) => i.point.y < bridgeBottom - 0.1);
|
|
810
|
+
if (validHits.length > 0) {
|
|
811
|
+
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
812
|
+
targetY = validHits[0].point.y;
|
|
813
|
+
found = true;
|
|
826
814
|
}
|
|
827
815
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
816
|
+
ray.remove();
|
|
817
|
+
const overlap = 2;
|
|
818
|
+
const rectBottom = bridgeBottom;
|
|
819
|
+
let rectTop = found ? targetY + overlap : bridgeTop;
|
|
820
|
+
if (!found) {
|
|
821
|
+
if (mainBounds.bottom < bridgeBottom) {
|
|
822
|
+
targetY = mainBounds.bottom;
|
|
823
|
+
rectTop = targetY - overlap;
|
|
835
824
|
}
|
|
836
|
-
|
|
825
|
+
}
|
|
826
|
+
if (rectTop < rectBottom) {
|
|
827
|
+
const bridgeRect = new paper2.Path.Rectangle({
|
|
828
|
+
from: [itemBounds.left, rectTop],
|
|
829
|
+
to: [itemBounds.right, rectBottom],
|
|
830
|
+
insert: false
|
|
831
|
+
});
|
|
832
|
+
const unitedItem = item.unite(bridgeRect);
|
|
837
833
|
item.remove();
|
|
838
|
-
|
|
834
|
+
bridgeRect.remove();
|
|
839
835
|
if (f.operation === "add") {
|
|
840
836
|
adds.push(unitedItem);
|
|
841
837
|
} else {
|
|
@@ -1393,7 +1389,19 @@ var DielineTool = class {
|
|
|
1393
1389
|
title: "Detect Edge from Image",
|
|
1394
1390
|
handler: async (imageUrl, options) => {
|
|
1395
1391
|
try {
|
|
1396
|
-
const
|
|
1392
|
+
const loadImage = (url) => {
|
|
1393
|
+
return new Promise((resolve, reject) => {
|
|
1394
|
+
const img2 = new Image();
|
|
1395
|
+
img2.crossOrigin = "Anonymous";
|
|
1396
|
+
img2.onload = () => resolve(img2);
|
|
1397
|
+
img2.onerror = (e) => reject(e);
|
|
1398
|
+
img2.src = url;
|
|
1399
|
+
});
|
|
1400
|
+
};
|
|
1401
|
+
const [img, pathData] = await Promise.all([
|
|
1402
|
+
loadImage(imageUrl),
|
|
1403
|
+
ImageTracer.trace(imageUrl, options)
|
|
1404
|
+
]);
|
|
1397
1405
|
const bounds = getPathBounds(pathData);
|
|
1398
1406
|
const currentMax = Math.max(s.width, s.height);
|
|
1399
1407
|
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
@@ -1402,7 +1410,10 @@ var DielineTool = class {
|
|
|
1402
1410
|
return {
|
|
1403
1411
|
pathData,
|
|
1404
1412
|
width: newWidth,
|
|
1405
|
-
height: newHeight
|
|
1413
|
+
height: newHeight,
|
|
1414
|
+
rawBounds: bounds,
|
|
1415
|
+
imageWidth: img.width,
|
|
1416
|
+
imageHeight: img.height
|
|
1406
1417
|
};
|
|
1407
1418
|
} catch (e) {
|
|
1408
1419
|
console.error("Edge detection failed", e);
|
package/package.json
CHANGED
package/src/dieline.ts
CHANGED
|
@@ -457,7 +457,22 @@ export class DielineTool implements Extension {
|
|
|
457
457
|
title: "Detect Edge from Image",
|
|
458
458
|
handler: async (imageUrl: string, options?: any) => {
|
|
459
459
|
try {
|
|
460
|
-
|
|
460
|
+
// Helper to get image dimensions
|
|
461
|
+
const loadImage = (url: string): Promise<HTMLImageElement> => {
|
|
462
|
+
return new Promise((resolve, reject) => {
|
|
463
|
+
const img = new Image();
|
|
464
|
+
img.crossOrigin = "Anonymous";
|
|
465
|
+
img.onload = () => resolve(img);
|
|
466
|
+
img.onerror = (e) => reject(e);
|
|
467
|
+
img.src = url;
|
|
468
|
+
});
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
const [img, pathData] = await Promise.all([
|
|
472
|
+
loadImage(imageUrl),
|
|
473
|
+
ImageTracer.trace(imageUrl, options),
|
|
474
|
+
]);
|
|
475
|
+
|
|
461
476
|
const bounds = getPathBounds(pathData);
|
|
462
477
|
|
|
463
478
|
const currentMax = Math.max(s.width, s.height);
|
|
@@ -470,6 +485,9 @@ export class DielineTool implements Extension {
|
|
|
470
485
|
pathData,
|
|
471
486
|
width: newWidth,
|
|
472
487
|
height: newHeight,
|
|
488
|
+
rawBounds: bounds,
|
|
489
|
+
imageWidth: img.width,
|
|
490
|
+
imageHeight: img.height,
|
|
473
491
|
};
|
|
474
492
|
} catch (e) {
|
|
475
493
|
console.error("Edge detection failed", e);
|
package/src/geometry.ts
CHANGED
|
@@ -182,86 +182,82 @@ function getPerimeterShape(options: GeometryOptions): paper.PathItem {
|
|
|
182
182
|
const bridgeBottom = itemBounds.top;
|
|
183
183
|
|
|
184
184
|
if (bridgeBottom > bridgeTop) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
subtracts.push(item);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
} else {
|
|
185
|
+
// Ray Casting Approach:
|
|
186
|
+
// 1. Create a vertical ray from the center of the feature upwards
|
|
187
|
+
const centerX = itemBounds.center.x;
|
|
188
|
+
const ray = new paper.Path.Line({
|
|
189
|
+
from: [centerX, bridgeBottom],
|
|
190
|
+
to: [centerX, bridgeTop - 10], // Extend slightly past top to ensure intersection
|
|
191
|
+
insert: false
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// 2. Find intersections with the main shape
|
|
195
|
+
const intersections = mainShape.getIntersections(ray);
|
|
196
|
+
|
|
197
|
+
// 3. Find the lowest intersection point (highest Y)
|
|
198
|
+
// Intersections are usually sorted by offset, but we want to be safe.
|
|
199
|
+
// We want the point with the largest Y that is still <= bridgeBottom.
|
|
200
|
+
let targetY = bridgeTop; // Default to top if no intersection (shouldn't happen if overlapping)
|
|
201
|
+
let found = false;
|
|
202
|
+
|
|
203
|
+
if (intersections && intersections.length > 0) {
|
|
204
|
+
// Filter intersections that are strictly above the feature start
|
|
205
|
+
// (allow small tolerance for touching)
|
|
206
|
+
const validHits = intersections.filter(i => i.point.y < bridgeBottom - 0.1);
|
|
207
|
+
|
|
208
|
+
if (validHits.length > 0) {
|
|
209
|
+
// We want the HIT that is CLOSEST to the feature (Largest Y)
|
|
210
|
+
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
211
|
+
targetY = validHits[0].point.y;
|
|
212
|
+
found = true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
ray.remove();
|
|
217
|
+
|
|
218
|
+
// 4. Create the bridge rect
|
|
219
|
+
// If we found a hit, targetY is the surface of the main shape.
|
|
220
|
+
// We want to overlap slightly to ensure union.
|
|
221
|
+
const overlap = 2; // Overlap by 2 units
|
|
222
|
+
const rectBottom = bridgeBottom; // Start at feature top
|
|
223
|
+
let rectTop = found ? targetY + overlap : bridgeTop; // If not found, go all the way? Or maybe fail safe.
|
|
224
|
+
|
|
225
|
+
// If we didn't find an intersection, it might mean the feature is completely below the shape (no X overlap).
|
|
226
|
+
// In that case, maybe we shouldn't bridge? Or bridge to the bounding box bottom?
|
|
227
|
+
// For now, if found, use it. If not, use mainBounds.bottom?
|
|
228
|
+
// Let's assume if !found, we try to project to mainBounds.bottom if it's above us.
|
|
229
|
+
if (!found) {
|
|
230
|
+
if (mainBounds.bottom < bridgeBottom) {
|
|
231
|
+
targetY = mainBounds.bottom;
|
|
232
|
+
rectTop = targetY - overlap; // Penetrate up
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (rectTop < rectBottom) {
|
|
237
|
+
const bridgeRect = new paper.Path.Rectangle({
|
|
238
|
+
from: [itemBounds.left, rectTop],
|
|
239
|
+
to: [itemBounds.right, rectBottom],
|
|
240
|
+
insert: false
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const unitedItem = item.unite(bridgeRect);
|
|
244
|
+
item.remove();
|
|
245
|
+
bridgeRect.remove();
|
|
246
|
+
|
|
247
|
+
if (f.operation === "add") {
|
|
248
|
+
adds.push(unitedItem);
|
|
249
|
+
} else {
|
|
250
|
+
subtracts.push(unitedItem);
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
// Bridge height is negative or zero, just use item
|
|
254
|
+
if (f.operation === "add") {
|
|
255
|
+
adds.push(item);
|
|
256
|
+
} else {
|
|
257
|
+
subtracts.push(item);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
265
261
|
if (f.operation === "add") {
|
|
266
262
|
adds.push(item);
|
|
267
263
|
} else {
|