@toolpath/dfm 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +213 -2
- package/dfm.css +10 -0
- package/dist/chunk-5MPHT7BC.js +1599 -0
- package/dist/feature-record-BUoFFjN6.d.ts +503 -0
- package/dist/index.d.ts +368 -0
- package/dist/index.js +2486 -0
- package/dist/model/index.d.ts +128 -0
- package/dist/model/index.js +1047 -0
- package/package.json +72 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Toolpath
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,214 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Toolpath DFM
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@toolpath/dfm` is the DFM (design for manufacturing) rule list: each rule
|
|
4
|
+
written as the sentence it is — "Holes with L/D to top of part ≥ 8" — whose
|
|
5
|
+
words are its fields, beside its colour and how many of a part's features
|
|
6
|
+
break it. Under the list, Import, Export and Copy LLM prompt share a rule set
|
|
7
|
+
as one JSON file.
|
|
8
|
+
|
|
9
|
+
It also draws a feature's reach chart: the walls beside the feature, how high
|
|
10
|
+
they stand and how far out.
|
|
11
|
+
|
|
12
|
+
And it shows a feature as the Engine read it — the feature panel: the features
|
|
13
|
+
a clicked face could mean, the rules the chosen one breaks, what was measured
|
|
14
|
+
of it, its reach, and its datasheet as sent. Its pinch points, the widest tool
|
|
15
|
+
standing where the feature is tightest, are placed here and drawn by
|
|
16
|
+
`@toolpath/viewer`.
|
|
17
|
+
|
|
18
|
+
The rules and the checker that counts them ship too, with no React, under
|
|
19
|
+
`@toolpath/dfm/model`.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm install @toolpath/dfm @toolpath/ui react react-dom
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Add the styles after Tailwind and the `@toolpath/ui` theme:
|
|
28
|
+
|
|
29
|
+
```css
|
|
30
|
+
@import 'tailwindcss';
|
|
31
|
+
@import '@toolpath/ui/theme.css';
|
|
32
|
+
@import '@toolpath/dfm/dfm.css';
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Exports
|
|
36
|
+
|
|
37
|
+
| Entry point | What it is |
|
|
38
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
39
|
+
| `@toolpath/dfm` | `RuleList`, `Swatch`, the list's keyboard helpers, `ReachChart`, `ReachDrawing`, the feature panel (`FeaturePanel`, `FeatureDetails`, `CandidateList`, `ComparisonTable`, `storageFolds`), their types |
|
|
40
|
+
| `@toolpath/dfm/model` | The rules, `checkPart`, the rule-set file, the feature panel's rows, pinch points — no React, no DOM |
|
|
41
|
+
| `@toolpath/dfm/dfm.css` | Lets the app's Tailwind see the list's and chart's classes |
|
|
42
|
+
|
|
43
|
+
## The rules are an input
|
|
44
|
+
|
|
45
|
+
The package ships no default rules and stores nothing — not the rules, nor
|
|
46
|
+
which of the feature panel's sections are open. The app keeps the rules
|
|
47
|
+
— in memory, `localStorage`, a server — and passes them in with the functions
|
|
48
|
+
that change them:
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import { RuleList, type DfmRules } from '@toolpath/dfm'
|
|
52
|
+
import { checkPart, dfmFeatures } from '@toolpath/dfm/model'
|
|
53
|
+
|
|
54
|
+
const rules: DfmRules = { rules, add, update, remove, replace }
|
|
55
|
+
|
|
56
|
+
// From a part analysis: the Engine's report, and each feature's datasheet.
|
|
57
|
+
const features = dfmFeatures(report.features)
|
|
58
|
+
const check = checkPart(features, { sheets, datasheets, regionAreas }, rules.rules)
|
|
59
|
+
|
|
60
|
+
<RuleList
|
|
61
|
+
rules={rules}
|
|
62
|
+
check={check} // null while the part is still being analysed
|
|
63
|
+
features={features}
|
|
64
|
+
sheets={sheets}
|
|
65
|
+
units="mm"
|
|
66
|
+
selected={selectedTag}
|
|
67
|
+
selectedGroup={selectedGroup}
|
|
68
|
+
onInspect={(tag, group) => inspect(tag, group)}
|
|
69
|
+
onHover={(hovered) => highlight(hovered)}
|
|
70
|
+
/>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`sheets` is each feature's measurements, built with `featureSheet` from its
|
|
74
|
+
datasheet; `datasheets` is the datasheets as the API sent them, both by
|
|
75
|
+
lower-cased feature tag; `regionAreas` is each face's area by region index.
|
|
76
|
+
|
|
77
|
+
Put `ruleListKeys` on the panel that holds the list, so the arrow keys walk the
|
|
78
|
+
rules from anywhere in it.
|
|
79
|
+
|
|
80
|
+
## Reach chart
|
|
81
|
+
|
|
82
|
+
`ReachChart` draws a feature's reach curve: the section through the part at the
|
|
83
|
+
feature's wall, to scale. The feature is on the left, in blue; the walls step up
|
|
84
|
+
on the right, with their heights and their distances from the feature written
|
|
85
|
+
on. Pointing at the walls reads the curve at that distance.
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { ReachChart, ReachDrawing } from '@toolpath/dfm'
|
|
89
|
+
import { featureProfile, readReachCurve } from '@toolpath/dfm/model'
|
|
90
|
+
|
|
91
|
+
const curve = readReachCurve(datasheet) // null where the feature has none
|
|
92
|
+
const feature = featureProfile(sheets[tag], featureType)
|
|
93
|
+
|
|
94
|
+
export const Reach = () =>
|
|
95
|
+
curve ? <ReachChart curve={curve} units="mm" feature={feature} /> : null
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`ReachChart` is as wide as its container. `ReachDrawing` is the same section
|
|
99
|
+
with a caption and a note on how to read it, for a larger window of its own.
|
|
100
|
+
Both stop drawing the curve half an inch past where the walls reach full
|
|
101
|
+
height; `ReachDrawing` says so in its note.
|
|
102
|
+
|
|
103
|
+
This is not the clearance overlay in `@toolpath/tool-drawing/clearance`: that
|
|
104
|
+
draws the walls beside a cutting tool, to show whether the tool fits. This
|
|
105
|
+
draws the walls alone.
|
|
106
|
+
|
|
107
|
+
## Feature panel
|
|
108
|
+
|
|
109
|
+
`FeaturePanel` is the card a click on the part opens: every feature the face
|
|
110
|
+
belongs to, then the details of the one being read. With several faces
|
|
111
|
+
shift-clicked, each face's candidates sit under a letter, above a table of
|
|
112
|
+
what each was read as. It fills what it is put in — where it sits, and
|
|
113
|
+
whether it can be dragged, is the app's — and the app decides what a click
|
|
114
|
+
means: which faces, which candidates in which order, which is read.
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
import { FeatureDetails, FeaturePanel, storageFolds } from '@toolpath/dfm'
|
|
118
|
+
import { brokenRules, featureMeasurements, featureProfile } from '@toolpath/dfm/model'
|
|
119
|
+
|
|
120
|
+
const folds = storageFolds(localStorage, 'toolpath.fold.')
|
|
121
|
+
|
|
122
|
+
export const Inspector = () => (
|
|
123
|
+
<FeaturePanel
|
|
124
|
+
faces={[{ region, rows, selected }]}
|
|
125
|
+
active={0}
|
|
126
|
+
onSelect={(tag) => setSelected(tag)}
|
|
127
|
+
onHover={setHovered}
|
|
128
|
+
onClose={close}
|
|
129
|
+
folds={folds}
|
|
130
|
+
>
|
|
131
|
+
<FeatureDetails
|
|
132
|
+
feature={feature}
|
|
133
|
+
units="mm"
|
|
134
|
+
directionColor={directionHex}
|
|
135
|
+
profile={featureProfile(sheets[key], feature.featureType)}
|
|
136
|
+
required={check.required.has(key)}
|
|
137
|
+
onFrame={() => frame(feature.tag)}
|
|
138
|
+
rules={brokenRules(check, feature.tag, 'mm')}
|
|
139
|
+
measurements={{
|
|
140
|
+
status: 'ready',
|
|
141
|
+
value: featureMeasurements({ features, feature, sheets, units: 'mm' }),
|
|
142
|
+
}}
|
|
143
|
+
record={{ status: 'ready', value: records[key] ?? null }}
|
|
144
|
+
PopOut={AppWindow}
|
|
145
|
+
folds={folds}
|
|
146
|
+
/>
|
|
147
|
+
</FeaturePanel>
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The parts work alone too. An app with an inspect panel of its own puts
|
|
152
|
+
`CandidateList` and `FeatureDetails` in it and fills their slots with its own
|
|
153
|
+
controls — a folder in a row's `detail`, a Pin button in its `action`, Zoom to
|
|
154
|
+
in the details' `actions`, a folder badge in `status`.
|
|
155
|
+
|
|
156
|
+
- **Rules broken** are `BrokenRule` rows: a colour, the rule's text, the figure
|
|
157
|
+
that broke it, and optionally the limit and a note on hover. `brokenRules`
|
|
158
|
+
builds them from this package's check; an app with a checker of its own
|
|
159
|
+
builds them from its hits.
|
|
160
|
+
- **Measurements** are `featureMeasurements` rows, each with how it was worked
|
|
161
|
+
out behind an ⓘ. `inchMark` writes inches as `0.46"`.
|
|
162
|
+
- **Reach, the datasheet fields and the raw record** read the feature's
|
|
163
|
+
`FeatureRecord`: its report entry and datasheet as the API sent them.
|
|
164
|
+
- **Reads take time.** `measurements` and `record` are `Loadable`. While
|
|
165
|
+
`loading`, a section says what it is reading; on `error` it shows the app's
|
|
166
|
+
message and a Retry, held until `retryAt`.
|
|
167
|
+
- **The look** differs where apps want it to: `look.rules`, `look.selected` and
|
|
168
|
+
`look.muted`. Left out, it is the CAD viewer's.
|
|
169
|
+
- **Folds** are kept in the app's `FoldStore`; `storageFolds(storage, prefix)`
|
|
170
|
+
builds one over a `Storage`. Without one, a section remembers only while it
|
|
171
|
+
is mounted.
|
|
172
|
+
- **Pop-outs** need `PopOut`, the app's window component: the panel decides
|
|
173
|
+
what is open and fills it, and the app draws the frame. Define it at module
|
|
174
|
+
level, and render it through a portal to the page's body: the panel's blur
|
|
175
|
+
makes it the box a `fixed` window inside it would be placed in.
|
|
176
|
+
- **Keys:** Escape closes the panel and the arrow keys walk the candidates. On
|
|
177
|
+
a `CandidateList` used alone, put `candidateListKeys` on whatever holds it.
|
|
178
|
+
|
|
179
|
+
## Pinch points
|
|
180
|
+
|
|
181
|
+
The widest tool a feature admits, standing where the feature is tightest. The
|
|
182
|
+
data is here; the drawing is `@toolpath/viewer`'s `<ToolMarks>`, which knows
|
|
183
|
+
nothing of datasheets:
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { useMemo } from 'react'
|
|
187
|
+
import { pinchLabel, pinchMark, placePinchTool } from '@toolpath/dfm/model'
|
|
188
|
+
import { featureTriangles, ToolMarks, usePartContext } from '@toolpath/viewer'
|
|
189
|
+
|
|
190
|
+
// A child of <EnginePart> or <PartMesh>, which gives it the part.
|
|
191
|
+
export const PinchPoints = ({ datasheet, sheet, feature, units, shown }) => {
|
|
192
|
+
const { model, geometry } = usePartContext()
|
|
193
|
+
// Placing the tool searches the feature's faces: once per feature, not once per render.
|
|
194
|
+
const marks = useMemo(() => {
|
|
195
|
+
const mark = pinchMark(datasheet, sheet, feature.featureType)
|
|
196
|
+
const triangles = featureTriangles(model, geometry, feature.tag)
|
|
197
|
+
const tool = mark ? placePinchTool(mark, triangles, feature.machiningDirection) : null
|
|
198
|
+
return tool && mark ? [{ ...tool, ...pinchLabel(mark, units) }] : []
|
|
199
|
+
}, [datasheet, sheet, feature, model, geometry, units])
|
|
200
|
+
return <ToolMarks marks={marks} visible={shown} />
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`placePinchTool` finds the datasheet's tool frame against the feature's faces —
|
|
205
|
+
the API gives its discs across the tool without saying which way x and y lie —
|
|
206
|
+
and stands the tool on the tightest disc, turned toward the wall that pinches
|
|
207
|
+
it. If the app moved the geometry to centre it, pass `origin`: where the CAD
|
|
208
|
+
file's zero now sits.
|
|
209
|
+
|
|
210
|
+
Show and hide it from the viewer's toolbar — `ViewerToolbar.ToolsButton` on a
|
|
211
|
+
`tools` control — with the state held by the app; start it shown. The tool is
|
|
212
|
+
an end mill made from its diameter and corner; to draw it as the app's other
|
|
213
|
+
tools are drawn, give the mark a `profile` from `@toolpath/tool-drawing`'s
|
|
214
|
+
outline of a flat or bull nose end mill that size.
|
package/dfm.css
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Lets the app's Tailwind see the classes the rule list and the feature panel
|
|
3
|
+
* use. Import it after Tailwind and `@toolpath/ui/theme.css`, whose colours
|
|
4
|
+
* they are drawn in:
|
|
5
|
+
*
|
|
6
|
+
* @import 'tailwindcss';
|
|
7
|
+
* @import '@toolpath/ui/theme.css';
|
|
8
|
+
* @import '@toolpath/dfm/dfm.css';
|
|
9
|
+
*/
|
|
10
|
+
@source './dist';
|