@toolpath/dfm 0.3.1 → 0.5.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/README.md +47 -5
- package/dist/{chunk-5MPHT7BC.js → chunk-4IRNLDE6.js} +548 -142
- package/dist/{feature-record-BUoFFjN6.d.ts → feature-record-CfjgDCJU.d.ts} +195 -99
- package/dist/index.d.ts +18 -2
- package/dist/index.js +97 -76
- package/dist/model/index.d.ts +61 -15
- package/dist/model/index.js +575 -166
- package/package.json +2 -2
package/dist/model/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CRITICAL_COLOR,
|
|
3
3
|
FILE_KIND,
|
|
4
|
+
ID_KINDS,
|
|
4
5
|
LENGTH_PLACES,
|
|
6
|
+
MILLING_RULES,
|
|
5
7
|
RULE_COLORS,
|
|
8
|
+
TURNING_RULES,
|
|
6
9
|
TYPE_SUBJECT,
|
|
7
10
|
WARNING_COLOR,
|
|
8
11
|
breaks,
|
|
@@ -26,10 +29,12 @@ import {
|
|
|
26
29
|
runStart,
|
|
27
30
|
sameDepth,
|
|
28
31
|
serializeRuleSet,
|
|
32
|
+
subjectInfo,
|
|
29
33
|
trimmedCurve,
|
|
34
|
+
turnedTypeLabel,
|
|
30
35
|
unitLabel,
|
|
31
36
|
worseUpward
|
|
32
|
-
} from "../chunk-
|
|
37
|
+
} from "../chunk-4IRNLDE6.js";
|
|
33
38
|
|
|
34
39
|
// src/model/broken-rules.ts
|
|
35
40
|
var byColor = (hits) => [...hits].sort((a, b) => compareColors(a.rule.color, b.rule.color));
|
|
@@ -41,12 +46,12 @@ var worstHits = (hits) => {
|
|
|
41
46
|
return true;
|
|
42
47
|
});
|
|
43
48
|
};
|
|
44
|
-
var brokenRules = (check, tag, units) => worstHits(check.hitsByTag.get(tag.toLowerCase()) ?? []).map((hit) => {
|
|
49
|
+
var brokenRules = (check, tag, units, vocabulary = MILLING_RULES) => worstHits(check.hitsByTag.get(tag.toLowerCase()) ?? []).map((hit) => {
|
|
45
50
|
const figure = hitFigure(hit, units);
|
|
46
51
|
return {
|
|
47
52
|
key: hit.rule.id,
|
|
48
53
|
color: hit.rule.color,
|
|
49
|
-
text: describeRule(hit.rule, units),
|
|
54
|
+
text: describeRule(hit.rule, units, vocabulary),
|
|
50
55
|
...figure ? { figure } : {}
|
|
51
56
|
};
|
|
52
57
|
});
|
|
@@ -106,9 +111,9 @@ var featureIs = (subject, featureType, sheet) => {
|
|
|
106
111
|
return false;
|
|
107
112
|
}
|
|
108
113
|
};
|
|
109
|
-
var at = (
|
|
114
|
+
var at = (record2, path) => path.split(".").reduce(
|
|
110
115
|
(value, key) => typeof value === "object" && value !== null ? value[key] : void 0,
|
|
111
|
-
|
|
116
|
+
record2
|
|
112
117
|
);
|
|
113
118
|
var readPath = (datasheet, metric) => {
|
|
114
119
|
for (const path of metric.path ?? []) {
|
|
@@ -217,6 +222,22 @@ var requiredTags = (features) => {
|
|
|
217
222
|
features.filter((feature) => feature.regionIdxs.some((region) => owners.get(region) === 1)).map((feature) => feature.tag.toLowerCase())
|
|
218
223
|
);
|
|
219
224
|
};
|
|
225
|
+
var worstColor = (hits) => hits?.map((hit) => hit.rule.color).sort(compareColors)[0];
|
|
226
|
+
var ruleResults = (rules, features, hitsByTag, required) => rules.map((rule) => {
|
|
227
|
+
const hits = features.flatMap((feature) => {
|
|
228
|
+
const tag = feature.tag.toLowerCase();
|
|
229
|
+
const hit = hitsByTag.get(tag)?.find((each) => each.rule.id === rule.id);
|
|
230
|
+
return hit ? [{ tag: feature.tag, value: hit.value, metric: hit.metric, required: required.has(tag) }] : [];
|
|
231
|
+
});
|
|
232
|
+
const [first] = hits;
|
|
233
|
+
const up = first ? worseUpward(rule, first.metric) : true;
|
|
234
|
+
hits.sort((a, b) => up ? b.value - a.value : a.value - b.value);
|
|
235
|
+
return {
|
|
236
|
+
rule,
|
|
237
|
+
tags: hits.filter((hit) => hit.required).map((hit) => hit.tag),
|
|
238
|
+
otherTags: hits.filter((hit) => !hit.required).map((hit) => hit.tag)
|
|
239
|
+
};
|
|
240
|
+
});
|
|
220
241
|
var QUALIFIERS = ["undercut", "filleted", "tslot", "dovetail"];
|
|
221
242
|
var EDGE_TYPES = ["fillet", "chamfer", "sink"];
|
|
222
243
|
var featureComplexity = (featureType) => {
|
|
@@ -226,7 +247,6 @@ var featureComplexity = (featureType) => {
|
|
|
226
247
|
};
|
|
227
248
|
var CONTOUR = "contour_surface";
|
|
228
249
|
var unavoidableFaces = (features, hitsByTag, regionAreas) => {
|
|
229
|
-
const worstColor = (feature) => hitsByTag.get(feature.tag.toLowerCase())?.map((hit) => hit.rule.color).sort(compareColors)[0];
|
|
230
250
|
const areaOf = (feature) => [...new Set(feature.regionIdxs)].reduce(
|
|
231
251
|
(sum, region) => sum + (regionAreas.get(region) ?? 0),
|
|
232
252
|
0
|
|
@@ -242,7 +262,7 @@ var unavoidableFaces = (features, hitsByTag, regionAreas) => {
|
|
|
242
262
|
for (const [region, candidates] of owners) {
|
|
243
263
|
const colored = candidates.map((feature) => ({
|
|
244
264
|
feature,
|
|
245
|
-
color: worstColor(feature),
|
|
265
|
+
color: worstColor(hitsByTag.get(feature.tag.toLowerCase())),
|
|
246
266
|
area: areaOf(feature)
|
|
247
267
|
}));
|
|
248
268
|
const isContour = ({ feature }) => feature.featureType === CONTOUR;
|
|
@@ -269,10 +289,10 @@ var unavoidableFaces = (features, hitsByTag, regionAreas) => {
|
|
|
269
289
|
};
|
|
270
290
|
var withSurroundedFaces = (check, features, adjacency) => {
|
|
271
291
|
const paintedFor = new Map(check.paintedFaces);
|
|
272
|
-
const
|
|
292
|
+
const regionsOf2 = /* @__PURE__ */ new Map();
|
|
273
293
|
for (const feature of features) {
|
|
274
294
|
const key = feature.tag.toLowerCase();
|
|
275
|
-
|
|
295
|
+
regionsOf2.set(key, new Set(feature.regionIdxs));
|
|
276
296
|
const whole = check.paintedWhole.get(key);
|
|
277
297
|
if (whole) {
|
|
278
298
|
for (const region of feature.regionIdxs)
|
|
@@ -290,7 +310,7 @@ var withSurroundedFaces = (check, features, adjacency) => {
|
|
|
290
310
|
counts.set(key, { paint: paint2, count: (counts.get(key)?.count ?? 0) + 1 });
|
|
291
311
|
}
|
|
292
312
|
const most = Math.max(0, ...[...counts.values()].map(({ count }) => count));
|
|
293
|
-
const [paint] = [...counts].filter(([key, { count }]) => count === most &&
|
|
313
|
+
const [paint] = [...counts].filter(([key, { count }]) => count === most && regionsOf2.get(key)?.has(region)).map(([, { paint: paint2 }]) => paint2).sort((a, b) => compareColors(a.color, b.color));
|
|
294
314
|
if (paint) surrounded.set(region, paint);
|
|
295
315
|
}
|
|
296
316
|
if (surrounded.size === 0) return check;
|
|
@@ -304,29 +324,172 @@ var checkPart = (features, sources, rules) => {
|
|
|
304
324
|
featureHits(features, feature, sources, rules)
|
|
305
325
|
])
|
|
306
326
|
);
|
|
307
|
-
const byRule = rules.map((rule) => {
|
|
308
|
-
const hits = features.flatMap((feature) => {
|
|
309
|
-
const tag = feature.tag.toLowerCase();
|
|
310
|
-
const hit = hitsByTag.get(tag)?.find((each) => each.rule.id === rule.id);
|
|
311
|
-
return hit ? [{ tag: feature.tag, value: hit.value, metric: hit.metric, required: required.has(tag) }] : [];
|
|
312
|
-
});
|
|
313
|
-
const [first] = hits;
|
|
314
|
-
const up = first ? worseUpward(rule, first.metric) : true;
|
|
315
|
-
hits.sort((a, b) => up ? b.value - a.value : a.value - b.value);
|
|
316
|
-
return {
|
|
317
|
-
rule,
|
|
318
|
-
tags: hits.filter((hit) => hit.required).map((hit) => hit.tag),
|
|
319
|
-
otherTags: hits.filter((hit) => !hit.required).map((hit) => hit.tag)
|
|
320
|
-
};
|
|
321
|
-
});
|
|
322
327
|
return {
|
|
323
328
|
hitsByTag,
|
|
324
329
|
required,
|
|
325
|
-
byRule,
|
|
330
|
+
byRule: ruleResults(rules, features, hitsByTag, required),
|
|
326
331
|
...unavoidableFaces(features, hitsByTag, sources.regionAreas)
|
|
327
332
|
};
|
|
328
333
|
};
|
|
329
334
|
|
|
335
|
+
// src/model/pinch.ts
|
|
336
|
+
var asNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
337
|
+
var recordOf = (value) => typeof value === "object" && value !== null ? value : {};
|
|
338
|
+
var clearanceFit = (datasheet) => {
|
|
339
|
+
const sheet = recordOf(datasheet);
|
|
340
|
+
const facts = recordOf(sheet.facts);
|
|
341
|
+
const chamfer = facts.kind === "Chamfer";
|
|
342
|
+
const band = recordOf(chamfer ? recordOf(facts.three).cd : facts.cd);
|
|
343
|
+
const tolerance = recordOf(sheet.toleranceBand);
|
|
344
|
+
const strict = asNumber(recordOf(band.ignore).min);
|
|
345
|
+
if (strict === void 0) return void 0;
|
|
346
|
+
const bandPath = chamfer ? "facts.three.cd" : "facts.cd";
|
|
347
|
+
const loose = asNumber(recordOf(band.deviate).min);
|
|
348
|
+
const atStrict = asNumber(tolerance.atolIgnore);
|
|
349
|
+
const atLoose = asNumber(tolerance.atolDeviate);
|
|
350
|
+
const source = { bandPath, strict, loose, atStrict, atLoose };
|
|
351
|
+
if (loose === void 0 || atStrict === void 0 || atLoose === void 0 || atLoose <= atStrict)
|
|
352
|
+
return { ...source, slack: 0, resolved: false };
|
|
353
|
+
const growth = (loose - strict) / (atLoose - atStrict);
|
|
354
|
+
const slack = growth > 0 ? Math.min(growth * atStrict, strict) : 0;
|
|
355
|
+
return { ...source, slack, resolved: true };
|
|
356
|
+
};
|
|
357
|
+
var readPinch = (datasheet) => {
|
|
358
|
+
const sheet = recordOf(datasheet);
|
|
359
|
+
const zMin = asNumber(sheet.zMin);
|
|
360
|
+
const zMax = asNumber(sheet.zMax);
|
|
361
|
+
if (zMin === void 0 || zMax === void 0 || !Array.isArray(sheet.pinchPoints)) return null;
|
|
362
|
+
const fit = clearanceFit(datasheet);
|
|
363
|
+
const slack = fit?.slack ?? 0;
|
|
364
|
+
const discs = sheet.pinchPoints.flatMap((point) => {
|
|
365
|
+
const centre = recordOf(recordOf(point).center);
|
|
366
|
+
const x = asNumber(centre.x);
|
|
367
|
+
const y = asNumber(centre.y);
|
|
368
|
+
const diameter = asNumber(recordOf(point).diameter);
|
|
369
|
+
return x === void 0 || y === void 0 || diameter === void 0 || diameter < 0 ? [] : [{ x, y, diameter: Math.max(diameter - slack, 0) }];
|
|
370
|
+
});
|
|
371
|
+
if (discs.length === 0) return null;
|
|
372
|
+
return { discs, zMin, zMax, ...fit && !fit.resolved ? { unresolved: true } : {} };
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// src/model/feature-sheet.ts
|
|
376
|
+
var SHARP_MM = 0.1;
|
|
377
|
+
var SHARP_RADIUS_MM = 0.01;
|
|
378
|
+
var PINCH_TOOL_MM = 0.01;
|
|
379
|
+
var asNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
380
|
+
var recordOf2 = (value) => typeof value === "object" && value !== null ? value : {};
|
|
381
|
+
var definedOnly = (fields) => Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== void 0));
|
|
382
|
+
var undercutOf = (facts) => {
|
|
383
|
+
const unmeasured = facts.isInvalidGeometry === true || recordOf2(facts.cd).measurementFailed === true ? true : void 0;
|
|
384
|
+
if (facts.kind === "Tslot")
|
|
385
|
+
return definedOnly({
|
|
386
|
+
undercutDepth: asNumber2(facts.undercutDepth),
|
|
387
|
+
maxEntry: asNumber2(facts.maxEntryCd),
|
|
388
|
+
isClosed: facts.isClosed === true ? true : void 0,
|
|
389
|
+
unmeasured
|
|
390
|
+
});
|
|
391
|
+
if (facts.kind === "Dovetail")
|
|
392
|
+
return definedOnly({
|
|
393
|
+
taperDeg: asNumber2(facts.taperDeg),
|
|
394
|
+
floorWidth: asNumber2(facts.floorWidth),
|
|
395
|
+
topOpeningWidth: asNumber2(facts.topOpeningWidth),
|
|
396
|
+
isExternal: facts.isExternal === true ? true : void 0,
|
|
397
|
+
unmeasured
|
|
398
|
+
});
|
|
399
|
+
return void 0;
|
|
400
|
+
};
|
|
401
|
+
var threadingOf = (facts) => {
|
|
402
|
+
const spec = recordOf2(recordOf2(facts.threading).spec);
|
|
403
|
+
const basicDiameter = asNumber2(spec.basicDiameter);
|
|
404
|
+
const threadPitch = asNumber2(spec.threadPitch);
|
|
405
|
+
if (basicDiameter === void 0 || threadPitch === void 0 || threadPitch <= 0) return void 0;
|
|
406
|
+
return { basicDiameter, threadPitch };
|
|
407
|
+
};
|
|
408
|
+
var noToolFits = (band) => {
|
|
409
|
+
const { max } = band;
|
|
410
|
+
if (typeof max === "string") return /^-inf/i.test(max.trim());
|
|
411
|
+
return typeof max === "number" && (max === Number.NEGATIVE_INFINITY || Number.isFinite(max) && max < SHARP_MM);
|
|
412
|
+
};
|
|
413
|
+
var insideCornerRadius = (fit) => Math.max((fit.strict - fit.slack) / 2, 0);
|
|
414
|
+
var pinchCentres = (points) => Array.isArray(points) ? points.flatMap((point) => {
|
|
415
|
+
const centre = recordOf2(recordOf2(point).center);
|
|
416
|
+
const x = asNumber2(centre.x);
|
|
417
|
+
const y = asNumber2(centre.y);
|
|
418
|
+
return x === void 0 || y === void 0 ? [] : [{ x, y }];
|
|
419
|
+
}) : [];
|
|
420
|
+
var ZERO_MM = 5e-5;
|
|
421
|
+
var isCutterlessTslot = (datasheet) => {
|
|
422
|
+
const facts = recordOf2(recordOf2(datasheet).facts);
|
|
423
|
+
if (facts.kind !== "Tslot") return false;
|
|
424
|
+
const leaves = (value) => typeof value === "object" && value !== null ? Object.values(value).flatMap(leaves) : [value];
|
|
425
|
+
const band = leaves(facts.cd);
|
|
426
|
+
return band.length > 0 && band.every((each) => {
|
|
427
|
+
const value = typeof each === "string" && each.trim() !== "" ? Number(each) : each;
|
|
428
|
+
return typeof value === "number" && Math.abs(value) < ZERO_MM;
|
|
429
|
+
});
|
|
430
|
+
};
|
|
431
|
+
var isSharpCorneredFillet = (featureType, datasheet) => {
|
|
432
|
+
if (typeof featureType !== "string" || !featureType.toLowerCase().split("_").includes("fillet"))
|
|
433
|
+
return false;
|
|
434
|
+
const facts = recordOf2(recordOf2(datasheet).facts);
|
|
435
|
+
return facts.hasSharpCorner === true || recordOf2(facts.three).hasSharpCorner === true;
|
|
436
|
+
};
|
|
437
|
+
var isNoReading = (featureType, datasheet) => isCutterlessTslot(datasheet) || isSharpCorneredFillet(featureType, datasheet);
|
|
438
|
+
var featureSheet = (datasheet) => {
|
|
439
|
+
const sheet = recordOf2(datasheet);
|
|
440
|
+
const facts = recordOf2(sheet.facts);
|
|
441
|
+
const kind = typeof facts.kind === "string" ? facts.kind : void 0;
|
|
442
|
+
const band = recordOf2(kind === "Chamfer" ? recordOf2(facts.three).cd : facts.cd);
|
|
443
|
+
const ignoreBand = recordOf2(band.ignore);
|
|
444
|
+
const fit = clearanceFit(datasheet);
|
|
445
|
+
const fields = {
|
|
446
|
+
kind,
|
|
447
|
+
zMin: asNumber2(sheet.zMin),
|
|
448
|
+
zMax: asNumber2(sheet.zMax),
|
|
449
|
+
extendedZMax: asNumber2(sheet.extendedZMax),
|
|
450
|
+
maxTool: asNumber2(ignoreBand.min),
|
|
451
|
+
diameter: kind === "Hole" ? asNumber2(facts.diameter) : void 0,
|
|
452
|
+
tipAngle: kind === "Hole" ? asNumber2(facts.fullConeDeg) : void 0,
|
|
453
|
+
filletRadius: asNumber2(facts.filletRadius),
|
|
454
|
+
// A bevel's angle wherever the facts carry one: a chamfer's, and any other kind that bevels.
|
|
455
|
+
chamferAngle: asNumber2(recordOf2(facts.bevel).angleDeg)
|
|
456
|
+
};
|
|
457
|
+
const toolFitPath = facts.toolFit !== void 0 ? "facts.toolFit" : "facts.three.toolFit";
|
|
458
|
+
const toolFit = recordOf2(facts.toolFit ?? recordOf2(facts.three).toolFit);
|
|
459
|
+
fields.fitToolDiameter = asNumber2(toolFit.toolDiameter);
|
|
460
|
+
fields.fitCornerRadius = asNumber2(toolFit.cornerRadius);
|
|
461
|
+
const ballOnly = (facts.useOnlyBallToolsForFinish ?? recordOf2(facts.three).useOnlyBallToolsForFinish) === true;
|
|
462
|
+
const measured = Object.fromEntries(
|
|
463
|
+
Object.entries(fields).filter(([, value]) => value !== void 0)
|
|
464
|
+
);
|
|
465
|
+
const cramped = Object.keys(ignoreBand).length > 0 && noToolFits(ignoreBand);
|
|
466
|
+
const walled = kind !== "Hole" && sheet.hasWall === true;
|
|
467
|
+
const clearance = walled ? fit : void 0;
|
|
468
|
+
const cornerRadius = clearance ? insideCornerRadius(clearance) : void 0;
|
|
469
|
+
const sharp = cornerRadius !== void 0 && cornerRadius < SHARP_RADIUS_MM && !cramped;
|
|
470
|
+
const threading = kind === "Hole" ? threadingOf(facts) : void 0;
|
|
471
|
+
const undercut = undercutOf(facts);
|
|
472
|
+
const pinched = (readPinch(datasheet)?.discs ?? []).filter(
|
|
473
|
+
(disc) => disc.diameter >= PINCH_TOOL_MM
|
|
474
|
+
);
|
|
475
|
+
return {
|
|
476
|
+
...measured,
|
|
477
|
+
...pinched.length > 0 ? {
|
|
478
|
+
pinchDiameter: Math.min(...pinched.map((disc) => disc.diameter)),
|
|
479
|
+
pinchPlaces: pinched.length
|
|
480
|
+
} : {},
|
|
481
|
+
...cornerRadius !== void 0 ? { cornerRadius } : {},
|
|
482
|
+
...clearance && !clearance.resolved ? { cornerRadiusUnresolved: true } : {},
|
|
483
|
+
...clearance ? { clearance } : {},
|
|
484
|
+
...Object.keys(toolFit).length > 0 ? { toolFitPath } : {},
|
|
485
|
+
...sharp ? { sharpCorners: pinchCentres(sheet.pinchPoints) } : {},
|
|
486
|
+
...cramped ? { noToolFits: true } : {},
|
|
487
|
+
...ballOnly ? { ballOnly: true } : {},
|
|
488
|
+
...threading ? { threading } : {},
|
|
489
|
+
...undercut ? { undercut } : {}
|
|
490
|
+
};
|
|
491
|
+
};
|
|
492
|
+
|
|
330
493
|
// src/model/feature-details.ts
|
|
331
494
|
import { MM_PER_INCH } from "@toolpath/tool-support";
|
|
332
495
|
var mm = (value) => `${trim(value, 4)} mm`;
|
|
@@ -375,6 +538,11 @@ var maxBallDiameter = (sheet) => {
|
|
|
375
538
|
};
|
|
376
539
|
var ROW_ORDER = [
|
|
377
540
|
"featureDepth",
|
|
541
|
+
"undercutWidth",
|
|
542
|
+
"floorWidth",
|
|
543
|
+
"openingWidth",
|
|
544
|
+
"undercutDepth",
|
|
545
|
+
"taper",
|
|
378
546
|
"diameter",
|
|
379
547
|
"cornerRadius",
|
|
380
548
|
"filletRadius",
|
|
@@ -383,6 +551,8 @@ var ROW_ORDER = [
|
|
|
383
551
|
"thread",
|
|
384
552
|
"bevelAngle",
|
|
385
553
|
"finishTool",
|
|
554
|
+
"undercutCutter",
|
|
555
|
+
"maxShaft",
|
|
386
556
|
"pinch",
|
|
387
557
|
"fitCornerRadius",
|
|
388
558
|
"maxBull",
|
|
@@ -406,6 +576,7 @@ var featureMeasurements = ({
|
|
|
406
576
|
});
|
|
407
577
|
const sheet = sheets[feature.tag.toLowerCase()];
|
|
408
578
|
const rows = [];
|
|
579
|
+
const undercut = sheet?.undercut;
|
|
409
580
|
const top = sheet ? partTop(features, sheets, feature.machiningDirection) : null;
|
|
410
581
|
const bottom = sheet?.zMin;
|
|
411
582
|
const topFrom = top === null ? void 0 : features.find((each) => sheets[each.tag.toLowerCase()]?.extendedZMax === top);
|
|
@@ -419,7 +590,7 @@ var featureMeasurements = ({
|
|
|
419
590
|
derivation: [topLine, `Top \u2212 zMin = ${mm(top)} \u2212 ${mm(bottom)} = ${mm(top - bottom)}`]
|
|
420
591
|
});
|
|
421
592
|
}
|
|
422
|
-
if (sheet?.zMax !== void 0 && bottom !== void 0) {
|
|
593
|
+
if (sheet?.zMax !== void 0 && bottom !== void 0 && !(undercut && sheet?.kind === "Tslot")) {
|
|
423
594
|
rows.push({
|
|
424
595
|
key: "featureDepth",
|
|
425
596
|
label: "Feature depth",
|
|
@@ -435,7 +606,7 @@ var featureMeasurements = ({
|
|
|
435
606
|
derivation: [`facts.diameter = ${mm(sheet.diameter)}`]
|
|
436
607
|
});
|
|
437
608
|
}
|
|
438
|
-
if (sheet?.cornerRadius !== void 0) {
|
|
609
|
+
if (sheet?.cornerRadius !== void 0 && !undercut) {
|
|
439
610
|
const radius = length(sheet.cornerRadius);
|
|
440
611
|
rows.push({
|
|
441
612
|
key: "cornerRadius",
|
|
@@ -449,7 +620,7 @@ var featureMeasurements = ({
|
|
|
449
620
|
]
|
|
450
621
|
});
|
|
451
622
|
}
|
|
452
|
-
if (!sheet?.sharpCorners) {
|
|
623
|
+
if (!sheet?.sharpCorners && !undercut) {
|
|
453
624
|
const clearance = sheet?.pinchDiameter ?? (sheet?.cornerRadius !== void 0 ? 2 * sheet.cornerRadius : void 0);
|
|
454
625
|
if (clearance !== void 0) {
|
|
455
626
|
const width = length(clearance);
|
|
@@ -479,6 +650,7 @@ var featureMeasurements = ({
|
|
|
479
650
|
});
|
|
480
651
|
}
|
|
481
652
|
}
|
|
653
|
+
if (sheet && undercut) rows.push(...undercutRows(sheet, undercut, top, length));
|
|
482
654
|
const surface = SURFACE_TYPES.has(feature.featureType);
|
|
483
655
|
if (surface && sheet) {
|
|
484
656
|
const diameter = sheet.fitToolDiameter;
|
|
@@ -544,12 +716,12 @@ var featureMeasurements = ({
|
|
|
544
716
|
});
|
|
545
717
|
}
|
|
546
718
|
}
|
|
547
|
-
const ld = featureLd(features, feature, sheets);
|
|
719
|
+
const ld = undercut ? null : featureLd(features, feature, sheets);
|
|
548
720
|
if (ld && top !== null && bottom !== void 0) {
|
|
549
721
|
const across = ld.basis === "bore" ? `D = facts.diameter = ${mm(ld.across)}` : ld.basis === "pinch" ? `D = widest tool (the tightest pinch disc, tolerance taken out) = ${mm(ld.across)}${ld.atLeast ? " (at most, so L/D is at least)" : ""}` : `D = widest tool = 2 \xD7 inside corner radius = ${mm(ld.across)}${ld.atLeast ? " (at most, so L/D is at least)" : ""} (no pinch points reported)`;
|
|
550
722
|
const ratioOf = (depth) => {
|
|
551
|
-
const
|
|
552
|
-
return ld.atLeast ? `\u2265 ${
|
|
723
|
+
const ratio2 = (depth / ld.across).toFixed(3);
|
|
724
|
+
return ld.atLeast ? `\u2265 ${ratio2}` : ratio2;
|
|
553
725
|
};
|
|
554
726
|
if (ld.drilling) {
|
|
555
727
|
rows.push({
|
|
@@ -641,141 +813,173 @@ var featureMeasurements = ({
|
|
|
641
813
|
}
|
|
642
814
|
return rows.sort((a, b) => ROW_ORDER.indexOf(a.key) - ROW_ORDER.indexOf(b.key));
|
|
643
815
|
};
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
if (
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
};
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
const leaves = (value) => typeof value === "object" && value !== null ? Object.values(value).flatMap(leaves) : [value];
|
|
715
|
-
const band = leaves(facts.cd);
|
|
716
|
-
return band.length > 0 && band.every((each) => {
|
|
717
|
-
const value = typeof each === "string" && each.trim() !== "" ? Number(each) : each;
|
|
718
|
-
return typeof value === "number" && Math.abs(value) < ZERO_MM;
|
|
816
|
+
var undercutRows = (sheet, undercut, top, length) => {
|
|
817
|
+
const rows = [];
|
|
818
|
+
const tslot = sheet.kind === "Tslot";
|
|
819
|
+
const { zMin, zMax } = sheet;
|
|
820
|
+
const { floorWidth: floor, topOpeningWidth: opening } = undercut;
|
|
821
|
+
const angle = undercut.taperDeg !== void 0 ? `${trim(undercut.taperDeg, 3)}\xB0` : void 0;
|
|
822
|
+
if (tslot && zMin !== void 0 && zMax !== void 0) {
|
|
823
|
+
rows.push({
|
|
824
|
+
key: "undercutWidth",
|
|
825
|
+
label: "Undercut width",
|
|
826
|
+
...length(zMax - zMin),
|
|
827
|
+
derivation: [
|
|
828
|
+
"The groove\u2019s height along the tool: the widest cutter it takes in one pass.",
|
|
829
|
+
`zMax \u2212 zMin = ${mm(zMax)} \u2212 ${mm(zMin)} = ${mm(zMax - zMin)}`
|
|
830
|
+
]
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
if (floor !== void 0) {
|
|
834
|
+
rows.push({
|
|
835
|
+
key: "floorWidth",
|
|
836
|
+
label: "Floor width",
|
|
837
|
+
...length(floor),
|
|
838
|
+
derivation: ["The widest clearance over the floor", `facts.floorWidth = ${mm(floor)}`]
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
if (opening !== void 0) {
|
|
842
|
+
rows.push({
|
|
843
|
+
key: "openingWidth",
|
|
844
|
+
label: "Opening width",
|
|
845
|
+
...length(opening),
|
|
846
|
+
derivation: [
|
|
847
|
+
"The clearance through the opening at the top of the groove",
|
|
848
|
+
`facts.topOpeningWidth = ${mm(opening)}`
|
|
849
|
+
]
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
let depth = undercut.undercutDepth;
|
|
853
|
+
let depthLine = depth !== void 0 ? `facts.undercutDepth = ${mm(depth)}` : "";
|
|
854
|
+
if (depth === void 0 && floor !== void 0 && opening !== void 0) {
|
|
855
|
+
if (floor > opening && !undercut.isExternal) {
|
|
856
|
+
depth = (floor - opening) / 2;
|
|
857
|
+
depthLine = `(facts.floorWidth \u2212 facts.topOpeningWidth) / 2 = (${mm(floor)} \u2212 ${mm(opening)}) / 2 = ${mm(depth)}`;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
if (depth !== void 0) {
|
|
861
|
+
rows.push({
|
|
862
|
+
key: "undercutDepth",
|
|
863
|
+
label: "Undercut depth",
|
|
864
|
+
...length(depth),
|
|
865
|
+
derivation: [
|
|
866
|
+
"How far the groove runs back from its opening: what the cutter\u2019s head reaches past its shaft.",
|
|
867
|
+
depthLine
|
|
868
|
+
]
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
if (angle) {
|
|
872
|
+
rows.push({
|
|
873
|
+
key: "taper",
|
|
874
|
+
label: "Dovetail angle",
|
|
875
|
+
value: `${angle} from the tool axis`,
|
|
876
|
+
derivation: [`facts.taperDeg = ${angle}, between the overhanging wall and the tool axis`]
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
const cutter = (value, derivation, alt) => ({
|
|
880
|
+
key: "undercutCutter",
|
|
881
|
+
label: "Max cutting diameter",
|
|
882
|
+
milling: true,
|
|
883
|
+
value,
|
|
884
|
+
...alt !== void 0 ? { alt } : {},
|
|
885
|
+
derivation
|
|
719
886
|
});
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
887
|
+
if (undercut.unmeasured) {
|
|
888
|
+
return [
|
|
889
|
+
...rows,
|
|
890
|
+
cutter("Not measured", [
|
|
891
|
+
"The Engine could not measure the groove (facts.isInvalidGeometry or facts.cd.measurementFailed), so its figures offer no cutter."
|
|
892
|
+
])
|
|
893
|
+
];
|
|
894
|
+
}
|
|
895
|
+
if (sheet.noToolFits) {
|
|
896
|
+
return [
|
|
897
|
+
...rows,
|
|
898
|
+
cutter("None fits", [
|
|
899
|
+
"facts.cd.ignore.max is next to nothing: no cutter fits anywhere in the groove."
|
|
900
|
+
])
|
|
901
|
+
];
|
|
902
|
+
}
|
|
903
|
+
const band = sheet.maxTool !== void 0 && sheet.maxTool > 0 ? sheet.maxTool : void 0;
|
|
904
|
+
const dropIn = undercut.isClosed ? undercut.maxEntry : void 0;
|
|
905
|
+
const head = band === void 0 ? void 0 : Math.min(band, dropIn ?? Infinity);
|
|
906
|
+
const opensTo = undercut.maxEntry ?? opening;
|
|
907
|
+
const behind = head !== void 0 && depth !== void 0 ? head - 2 * depth : void 0;
|
|
908
|
+
const limits = [opensTo, behind].filter((each) => each !== void 0);
|
|
909
|
+
const shaft = limits.length > 0 ? Math.max(Math.min(...limits), 0) : void 0;
|
|
910
|
+
const shaftLines = [
|
|
911
|
+
"The most the cutter\u2019s shaft can be: what comes down through the opening above the groove, and leaves the head reaching the back of it.",
|
|
912
|
+
...opensTo !== void 0 ? [`Opening: ${tslot ? "facts.maxEntryCd" : "facts.topOpeningWidth"} = ${mm(opensTo)}`] : [],
|
|
913
|
+
...behind !== void 0 && head !== void 0 && depth !== void 0 ? [
|
|
914
|
+
`Reach: max cutting diameter \u2212 2 \xD7 undercut depth = ${mm(head)} \u2212 2 \xD7 ${mm(depth)} = ${mm(behind)}`
|
|
915
|
+
] : [],
|
|
916
|
+
...shaft !== void 0 ? [`Max shaft = ${mm(shaft)}`] : []
|
|
917
|
+
];
|
|
918
|
+
if (head !== void 0 && band !== void 0) {
|
|
919
|
+
const headLines = [
|
|
920
|
+
"The biggest head that reaches every point of the groove.",
|
|
921
|
+
`facts.cd.ignore.min = ${mm(band)}`,
|
|
922
|
+
...dropIn !== void 0 ? [
|
|
923
|
+
`A closed slot: the head comes down through the opening above it, facts.maxEntryCd = ${mm(dropIn)}`
|
|
924
|
+
] : [],
|
|
925
|
+
`\u2300 = ${mm(head)}`
|
|
926
|
+
];
|
|
927
|
+
if (head < SHARP_MM || shaft !== void 0 && shaft < SHARP_MM) {
|
|
928
|
+
return [
|
|
929
|
+
...rows,
|
|
930
|
+
cutter("None fits", [
|
|
931
|
+
...headLines,
|
|
932
|
+
...shaft !== void 0 ? shaftLines : [],
|
|
933
|
+
"No head that fits the groove leaves room for a shaft to carry it."
|
|
934
|
+
])
|
|
935
|
+
];
|
|
936
|
+
}
|
|
937
|
+
const size = length(head);
|
|
938
|
+
const filletRadius = sheet.filletRadius !== void 0 && sheet.filletRadius > 0 ? sheet.filletRadius : void 0;
|
|
939
|
+
const fillet = filletRadius !== void 0 ? length(filletRadius) : void 0;
|
|
940
|
+
const describe = (unit2) => [`\u2300 ${size[unit2]}`, ...angle ? [angle] : [], ...fillet ? [`R ${fillet[unit2]}`] : []].join(
|
|
941
|
+
" \xB7 "
|
|
942
|
+
);
|
|
943
|
+
rows.push(
|
|
944
|
+
cutter(
|
|
945
|
+
describe("value"),
|
|
946
|
+
[
|
|
947
|
+
...headLines,
|
|
948
|
+
...angle ? [`Angle = facts.taperDeg = ${angle}, from the tool axis`] : [],
|
|
949
|
+
...filletRadius !== void 0 ? [`R = facts.filletRadius = ${mm(filletRadius)}, the blend the head\u2019s corners cut`] : []
|
|
950
|
+
],
|
|
951
|
+
describe("alt")
|
|
952
|
+
)
|
|
953
|
+
);
|
|
954
|
+
if (top !== null && zMin !== void 0) {
|
|
955
|
+
const reach = top - zMin;
|
|
956
|
+
const ratio2 = (reach / head).toFixed(3);
|
|
957
|
+
rows.push({
|
|
958
|
+
key: "topLd",
|
|
959
|
+
label: "L/D to top of part",
|
|
960
|
+
milling: true,
|
|
961
|
+
value: ratio2,
|
|
962
|
+
derivation: [
|
|
963
|
+
`L = depth below top of part = ${mm(reach)}`,
|
|
964
|
+
`D = max cutting diameter = ${mm(head)}`,
|
|
965
|
+
`L / D = ${mm(reach)} / ${mm(head)} = ${ratio2}`
|
|
966
|
+
]
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
if (shaft !== void 0 || tslot) {
|
|
971
|
+
const size = shaft !== void 0 ? length(shaft) : void 0;
|
|
972
|
+
rows.push({
|
|
973
|
+
key: "maxShaft",
|
|
974
|
+
label: "Max shaft diameter",
|
|
975
|
+
milling: true,
|
|
976
|
+
...size ? { value: `\u2300 ${size.value}`, alt: `\u2300 ${size.alt}` } : { value: "No limit" },
|
|
977
|
+
derivation: size ? shaftLines : [
|
|
978
|
+
"facts.maxEntryCd is not reported and the head or undercut depth is not known: nothing limits the shaft."
|
|
979
|
+
]
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
return rows;
|
|
779
983
|
};
|
|
780
984
|
|
|
781
985
|
// src/model/pinch-mark.ts
|
|
@@ -1009,14 +1213,217 @@ var placePinchTool = (mark, triangles, direction, origin = { x: 0, y: 0, z: 0 })
|
|
|
1009
1213
|
across: asVec(across)
|
|
1010
1214
|
};
|
|
1011
1215
|
};
|
|
1216
|
+
|
|
1217
|
+
// src/model/turned-check.ts
|
|
1218
|
+
var regionsOf = (feature) => Array.isArray(feature.regionIdxs) ? feature.regionIdxs : [];
|
|
1219
|
+
var stretchKey = (feature) => {
|
|
1220
|
+
const regions = regionsOf(feature);
|
|
1221
|
+
return regions.length === 0 ? `tag:${feature.featureTag}` : `${feature.featureType}:${[...regions].sort((a, b) => a - b).join(",")}`;
|
|
1222
|
+
};
|
|
1223
|
+
var isApproach = (value) => value === "PlusZ" || value === "MinusZ";
|
|
1224
|
+
var turnedStretches = (features) => {
|
|
1225
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1226
|
+
for (const feature of features) {
|
|
1227
|
+
const key = stretchKey(feature);
|
|
1228
|
+
groups.set(key, [...groups.get(key) ?? [], feature]);
|
|
1229
|
+
}
|
|
1230
|
+
const merged = [...groups.values()].flatMap((copies) => {
|
|
1231
|
+
const [first] = copies;
|
|
1232
|
+
if (!first) return [];
|
|
1233
|
+
const approaches = [...new Set(copies.map((each) => each.approach).filter(isApproach))];
|
|
1234
|
+
const only = approaches.length === 1 ? approaches[0] : void 0;
|
|
1235
|
+
const z = only === "PlusZ" ? 1 : only === "MinusZ" ? -1 : 0;
|
|
1236
|
+
return [{ first, copies, approaches, machiningDirection: { x: 0, y: 0, z } }];
|
|
1237
|
+
});
|
|
1238
|
+
const numbered = dfmFeatures(
|
|
1239
|
+
merged.map(({ first, machiningDirection }) => ({
|
|
1240
|
+
featureTag: first.featureTag,
|
|
1241
|
+
featureType: first.featureType,
|
|
1242
|
+
machiningDirection,
|
|
1243
|
+
regionIdxs: regionsOf(first)
|
|
1244
|
+
}))
|
|
1245
|
+
);
|
|
1246
|
+
return merged.flatMap(({ copies, approaches }, at2) => {
|
|
1247
|
+
const feature = numbered[at2];
|
|
1248
|
+
return feature ? [{ ...feature, copies, approaches }] : [];
|
|
1249
|
+
});
|
|
1250
|
+
};
|
|
1251
|
+
var record = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
1252
|
+
var finite = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1253
|
+
var ratio = (top, bottom) => top !== null && bottom !== null && bottom > 0 ? top / bottom : null;
|
|
1254
|
+
var narrowestStep = (facts) => {
|
|
1255
|
+
const steps = Array.isArray(facts.steps) ? facts.steps : [];
|
|
1256
|
+
const diameters = steps.map((step) => finite(record(step)?.diameter)).filter((each) => each !== null && each > 0);
|
|
1257
|
+
return diameters.length > 0 ? Math.min(...diameters) : null;
|
|
1258
|
+
};
|
|
1259
|
+
var threadSpec = (facts, field) => finite(record(facts.threadSpec)?.[field]) ?? finite(record(record(facts.thread)?.threadSpec)?.[field]);
|
|
1260
|
+
var partEnds = (stretches) => {
|
|
1261
|
+
const zs = stretches.flatMap(
|
|
1262
|
+
(stretch) => stretch.copies.flatMap((copy) => {
|
|
1263
|
+
const sheet = record(copy.datasheet);
|
|
1264
|
+
return [finite(sheet?.zMin), finite(sheet?.zMax)].filter(
|
|
1265
|
+
(each) => each !== null
|
|
1266
|
+
);
|
|
1267
|
+
})
|
|
1268
|
+
);
|
|
1269
|
+
return zs.length > 0 ? { low: Math.min(...zs), high: Math.max(...zs) } : null;
|
|
1270
|
+
};
|
|
1271
|
+
var turnedLd = (sheet, facts, kind, approach, ends) => {
|
|
1272
|
+
if (kind === "AxialHole") return ratio(finite(facts.depth), narrowestStep(facts));
|
|
1273
|
+
const inside = ID_KINDS;
|
|
1274
|
+
if (!kind || !inside.includes(kind) || !ends || !approach) return null;
|
|
1275
|
+
const zMin = finite(sheet.zMin);
|
|
1276
|
+
const zMax = finite(sheet.zMax);
|
|
1277
|
+
const reach = approach === "PlusZ" ? zMin === null ? null : ends.high - zMin : zMax === null ? null : zMax - ends.low;
|
|
1278
|
+
const rMin = finite(sheet.rMin);
|
|
1279
|
+
const bore = finite(sheet.entryDiameter) ?? (rMin === null ? null : 2 * rMin);
|
|
1280
|
+
return ratio(reach, bore);
|
|
1281
|
+
};
|
|
1282
|
+
var HAIR = 1e-3;
|
|
1283
|
+
var FLAT_CONE = 179.5;
|
|
1284
|
+
var copyMeasure = (metric, copy, ends) => {
|
|
1285
|
+
const sheet = record(copy.datasheet);
|
|
1286
|
+
if (!sheet) return null;
|
|
1287
|
+
const facts = record(sheet.facts) ?? {};
|
|
1288
|
+
const kind = typeof facts.kind === "string" ? facts.kind : kindOfType(copy.featureType);
|
|
1289
|
+
const kinds = metric.kinds;
|
|
1290
|
+
if (kinds && !(kind && kinds.includes(kind))) return null;
|
|
1291
|
+
const doubled = (value) => {
|
|
1292
|
+
const radius = finite(value);
|
|
1293
|
+
return radius === null ? null : 2 * radius;
|
|
1294
|
+
};
|
|
1295
|
+
switch (metric.key) {
|
|
1296
|
+
case "maxDiameter":
|
|
1297
|
+
return doubled(sheet.rMax);
|
|
1298
|
+
case "minDiameter":
|
|
1299
|
+
return doubled(sheet.rMin);
|
|
1300
|
+
case "length": {
|
|
1301
|
+
const zMin = finite(sheet.zMin);
|
|
1302
|
+
const zMax = finite(sheet.zMax);
|
|
1303
|
+
return zMin === null || zMax === null || zMax - zMin < HAIR ? null : zMax - zMin;
|
|
1304
|
+
}
|
|
1305
|
+
case "ld":
|
|
1306
|
+
return turnedLd(sheet, facts, kind, copy.approach ?? null, ends);
|
|
1307
|
+
case "grooveWidth":
|
|
1308
|
+
return finite(facts.width);
|
|
1309
|
+
case "grooveDepth":
|
|
1310
|
+
case "threadDepth":
|
|
1311
|
+
case "holeDepth":
|
|
1312
|
+
return finite(facts.depth);
|
|
1313
|
+
case "grooveAspect":
|
|
1314
|
+
return ratio(finite(facts.depth), finite(facts.width));
|
|
1315
|
+
case "threadPitch":
|
|
1316
|
+
return finite(facts.pitch) ?? threadSpec(facts, "threadPitch");
|
|
1317
|
+
case "threadDiameter":
|
|
1318
|
+
return threadSpec(facts, "basicDiameter");
|
|
1319
|
+
case "holeDiameter":
|
|
1320
|
+
return narrowestStep(facts);
|
|
1321
|
+
case "holeSteps":
|
|
1322
|
+
return Array.isArray(facts.steps) ? facts.steps.length : null;
|
|
1323
|
+
case "flatBottom": {
|
|
1324
|
+
if (facts.bottom === void 0) return 0;
|
|
1325
|
+
const cone = finite(record(facts.bottom)?.coneDeg);
|
|
1326
|
+
return cone === null ? null : cone >= FLAT_CONE ? 1 : 0;
|
|
1327
|
+
}
|
|
1328
|
+
// Zero is a figure here: a sharp corner, which a "radius ≤" rule is most of all about.
|
|
1329
|
+
case "cornerRadius":
|
|
1330
|
+
return finite(sheet.minConcaveRadius);
|
|
1331
|
+
case "sharpCorner": {
|
|
1332
|
+
const radius = finite(sheet.minConcaveRadius);
|
|
1333
|
+
return radius === null ? 0 : radius <= HAIR ? 1 : 0;
|
|
1334
|
+
}
|
|
1335
|
+
// Absent where nothing stands between the feature and its end: nothing to pass, so clean.
|
|
1336
|
+
case "entryDiameter":
|
|
1337
|
+
return finite(sheet.entryDiameter);
|
|
1338
|
+
case "axialTool":
|
|
1339
|
+
return typeof sheet.mounting === "string" ? sheet.mounting === "Axial" ? 1 : 0 : null;
|
|
1340
|
+
case "tolerance":
|
|
1341
|
+
return finite(record(sheet.toleranceBand)?.atolMax);
|
|
1342
|
+
default:
|
|
1343
|
+
return null;
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
var kindOfType = (featureType) => featureType.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
1347
|
+
var stretchFacts = (stretch) => stretch.copies.map((copy) => record(record(copy.datasheet)?.facts)).find((each) => each !== void 0);
|
|
1348
|
+
var stretchIs = (subject, stretch) => {
|
|
1349
|
+
const type = stretch.featureType.toLowerCase();
|
|
1350
|
+
if (subject.startsWith(TYPE_SUBJECT)) return type === subject.slice(TYPE_SUBJECT.length);
|
|
1351
|
+
const facts = stretchFacts(stretch);
|
|
1352
|
+
const kind = typeof facts?.kind === "string" ? facts.kind : kindOfType(type);
|
|
1353
|
+
const hole = kind === "AxialHole";
|
|
1354
|
+
switch (subject) {
|
|
1355
|
+
case "any":
|
|
1356
|
+
return true;
|
|
1357
|
+
case "thread":
|
|
1358
|
+
return kind === "OdThread" || kind === "IdThread" || hole && record(facts?.thread) !== void 0;
|
|
1359
|
+
case "throughHole":
|
|
1360
|
+
return hole && facts?.through === true;
|
|
1361
|
+
case "blindHole":
|
|
1362
|
+
return hole && facts?.through === false;
|
|
1363
|
+
case "threadedHole":
|
|
1364
|
+
return hole && record(facts?.thread) !== void 0;
|
|
1365
|
+
default: {
|
|
1366
|
+
const kinds = subjectInfo(subject, TURNING_RULES)?.kinds;
|
|
1367
|
+
return kinds !== void 0 && kinds.includes(kind);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
};
|
|
1371
|
+
var stretchHit = (stretch, rule, metric, ends) => {
|
|
1372
|
+
if (metric.quantity === "presence") return { rule, metric, value: 1 };
|
|
1373
|
+
const measured = stretch.copies.filter((copy) => record(copy.datasheet) !== void 0);
|
|
1374
|
+
if (measured.length === 0) return null;
|
|
1375
|
+
const values = [];
|
|
1376
|
+
for (const copy of measured) {
|
|
1377
|
+
const value2 = copyMeasure(metric, copy, ends);
|
|
1378
|
+
if (value2 === null || !breaks(rule, metric.quantity, value2)) return null;
|
|
1379
|
+
values.push(value2);
|
|
1380
|
+
}
|
|
1381
|
+
const value = worseUpward(rule, metric) ? Math.min(...values) : Math.max(...values);
|
|
1382
|
+
return { rule, metric, value };
|
|
1383
|
+
};
|
|
1384
|
+
var checkTurnedPart = (stretches, rules) => {
|
|
1385
|
+
const ends = partEnds(stretches);
|
|
1386
|
+
const hitsByTag = new Map(
|
|
1387
|
+
stretches.map((stretch) => [
|
|
1388
|
+
stretch.tag.toLowerCase(),
|
|
1389
|
+
rules.flatMap((rule) => {
|
|
1390
|
+
const metric = metricInfo(rule.metric, TURNING_RULES);
|
|
1391
|
+
if (!metric || !stretchIs(rule.subject, stretch)) return [];
|
|
1392
|
+
const hit = stretchHit(stretch, rule, metric, ends);
|
|
1393
|
+
return hit ? [hit] : [];
|
|
1394
|
+
})
|
|
1395
|
+
])
|
|
1396
|
+
);
|
|
1397
|
+
const required = new Set(stretches.map((stretch) => stretch.tag.toLowerCase()));
|
|
1398
|
+
const paintedFaces = /* @__PURE__ */ new Map();
|
|
1399
|
+
for (const stretch of stretches) {
|
|
1400
|
+
const color = worstColor(hitsByTag.get(stretch.tag.toLowerCase()));
|
|
1401
|
+
if (!color) continue;
|
|
1402
|
+
for (const region of stretch.regionIdxs) {
|
|
1403
|
+
const painted = paintedFaces.get(region);
|
|
1404
|
+
if (!painted || compareColors(color, painted.color) < 0)
|
|
1405
|
+
paintedFaces.set(region, { tag: stretch.tag, color });
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return {
|
|
1409
|
+
hitsByTag,
|
|
1410
|
+
required,
|
|
1411
|
+
byRule: ruleResults(rules, stretches, hitsByTag, required),
|
|
1412
|
+
paintedFaces,
|
|
1413
|
+
paintedWhole: /* @__PURE__ */ new Map()
|
|
1414
|
+
};
|
|
1415
|
+
};
|
|
1012
1416
|
export {
|
|
1013
1417
|
CRITICAL_COLOR,
|
|
1014
1418
|
FILE_KIND,
|
|
1419
|
+
MILLING_RULES,
|
|
1015
1420
|
PINCH_TOOL_MM,
|
|
1016
1421
|
RULE_COLORS,
|
|
1422
|
+
TURNING_RULES,
|
|
1017
1423
|
WARNING_COLOR,
|
|
1018
1424
|
brokenRules,
|
|
1019
1425
|
checkPart,
|
|
1426
|
+
checkTurnedPart,
|
|
1020
1427
|
compareColors,
|
|
1021
1428
|
describeRule,
|
|
1022
1429
|
dfmFeatures,
|
|
@@ -1043,5 +1450,7 @@ export {
|
|
|
1043
1450
|
sameDepth,
|
|
1044
1451
|
serializeRuleSet,
|
|
1045
1452
|
trimmedCurve,
|
|
1453
|
+
turnedStretches,
|
|
1454
|
+
turnedTypeLabel,
|
|
1046
1455
|
withSurroundedFaces
|
|
1047
1456
|
};
|