@toolpath/tool-drawing 0.1.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 +117 -0
- package/dist/chunk-7ZID4QRO.js +34 -0
- package/dist/chunk-OUMNS4RI.js +225 -0
- package/dist/clearance/index.d.ts +205 -0
- package/dist/clearance/index.js +487 -0
- package/dist/geometry/index.d.ts +102 -0
- package/dist/geometry/index.js +6 -0
- package/dist/index.d.ts +353 -0
- package/dist/index.js +846 -0
- package/dist/sheet-D0LSO7qP.d.ts +183 -0
- package/package.json +67 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage 2 of the drawing: where the millimetres land on the sheet.
|
|
3
|
+
*
|
|
4
|
+
* Pure arithmetic over a content extent and a measured box. No React, no DOM,
|
|
5
|
+
* no SVG — {@link frameFor} returns numbers and two mapping functions, and the
|
|
6
|
+
* renderer does nothing but place things with them.
|
|
7
|
+
*
|
|
8
|
+
* ## The inversion
|
|
9
|
+
*
|
|
10
|
+
* **The frame is content plus margins, and the scale absorbs the panel's
|
|
11
|
+
* shape.** Never the other way round. The component this replaces derived the
|
|
12
|
+
* *frame* from the panel's aspect ratio while pinning the scale to the tool's
|
|
13
|
+
* length, so a ⌀1 drill 58 mm long got a 312 mm-wide sheet and then had
|
|
14
|
+
* `preserveAspectRatio` shrink it to fit — about 85% of the panel rendered
|
|
15
|
+
* empty. Here `scale` is the smaller of the two px-per-mm ratios that fit the
|
|
16
|
+
* content in the box, and the viewBox describes the content and its margins.
|
|
17
|
+
* A wide panel now buys a bigger drawing rather than a wider sheet.
|
|
18
|
+
*
|
|
19
|
+
* ## The contract with the renderer
|
|
20
|
+
*
|
|
21
|
+
* The `<svg>` must carry `preserveAspectRatio="xMidYMid meet"`, which is the
|
|
22
|
+
* default. That is not decoration: `meet` fits the viewBox by the smaller of
|
|
23
|
+
* its own two ratios, and the algebra below chooses `scale` so that the
|
|
24
|
+
* binding ratio is exactly `scale`. The reported number and the rendered
|
|
25
|
+
* number are then the same, which is what lets {@link Frame.fontSize} be
|
|
26
|
+
* trusted. Under `preserveAspectRatio="none"` the drawing would stretch and
|
|
27
|
+
* `scale` would be a lie on one axis.
|
|
28
|
+
*
|
|
29
|
+
* ## Orientation
|
|
30
|
+
*
|
|
31
|
+
* {@link Frame.toX} and {@link Frame.toY} are the **only** things in this
|
|
32
|
+
* package that know which way the tool axis runs. Everything downstream — the
|
|
33
|
+
* silhouette, the joins, the centreline, the dimensions, the clearance overlay
|
|
34
|
+
* — is written once against them and is orientation-agnostic. Orientation
|
|
35
|
+
* being baked into a pair of closures inside the render pass, and assumed by
|
|
36
|
+
* every renderer below them, is the reason the old component could not be
|
|
37
|
+
* patched into shape.
|
|
38
|
+
*/
|
|
39
|
+
/** What framing needs from an outline: how far it reaches, in millimetres. */
|
|
40
|
+
interface Extent {
|
|
41
|
+
/** The tallest point drawn, above the tip. */
|
|
42
|
+
readonly height: number;
|
|
43
|
+
/** The widest radius drawn. The drawing is mirrored, so it spans twice this. */
|
|
44
|
+
readonly radius: number;
|
|
45
|
+
}
|
|
46
|
+
/** A measured panel, in CSS pixels. Zero on the server and before first paint. */
|
|
47
|
+
interface Box {
|
|
48
|
+
readonly width: number;
|
|
49
|
+
readonly height: number;
|
|
50
|
+
}
|
|
51
|
+
type Orientation = 'vertical' | 'horizontal';
|
|
52
|
+
interface Frame {
|
|
53
|
+
readonly orientation: Orientation;
|
|
54
|
+
/** Pixels per millimetre, as the drawing will actually render. */
|
|
55
|
+
readonly scale: number;
|
|
56
|
+
/** `minX minY width height`, in millimetres: the content and its margins. */
|
|
57
|
+
readonly viewBox: string;
|
|
58
|
+
/** A point's horizontal place in viewBox coordinates, from radius and height. */
|
|
59
|
+
readonly toX: (r: number, z: number) => number;
|
|
60
|
+
/** A point's vertical place in viewBox coordinates, from radius and height. */
|
|
61
|
+
readonly toY: (r: number, z: number) => number;
|
|
62
|
+
/** Type size in **millimetres**, back-derived from a target in pixels. */
|
|
63
|
+
readonly fontSize: number;
|
|
64
|
+
/**
|
|
65
|
+
* The chrome actually applied, in pixels — what was asked for, or less.
|
|
66
|
+
*
|
|
67
|
+
* The frame reports what it did with the request, the way it reports the
|
|
68
|
+
* scale it settled on. A caller placing something in the margin has to place
|
|
69
|
+
* it against the room the margin really has: ask for more than
|
|
70
|
+
* {@link MOST_OF_A_PANEL} and the request is scaled back rather than
|
|
71
|
+
* granted, and anything drawn at the full request would then hang off the
|
|
72
|
+
* sheet.
|
|
73
|
+
*/
|
|
74
|
+
readonly padding: Padding;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Room for chrome around the content, in **pixels**, flank by flank.
|
|
78
|
+
*
|
|
79
|
+
* **Pixels rather than millimetres because it is chrome**: it should not grow
|
|
80
|
+
* because the tool is long, and it has to stay independent of `scale` or the
|
|
81
|
+
* arithmetic closes a loop — more padding shrinks the scale, which changes the
|
|
82
|
+
* type size, which changes the band widths, which changes the padding.
|
|
83
|
+
*
|
|
84
|
+
* **Named by flank rather than by side of the screen.** `minus` is the `-r`
|
|
85
|
+
* flank and `plus` the `+r` flank; which of them is the screen's left, right,
|
|
86
|
+
* top or bottom is `toX`/`toY`'s business and nothing else's.
|
|
87
|
+
*
|
|
88
|
+
* A plain number is the same room on every side, and that is what a drawing
|
|
89
|
+
* with no dimensions on it wants.
|
|
90
|
+
*/
|
|
91
|
+
interface Padding {
|
|
92
|
+
/** Outside the `-r` flank. */
|
|
93
|
+
readonly minus: number;
|
|
94
|
+
/** Outside the `+r` flank. */
|
|
95
|
+
readonly plus: number;
|
|
96
|
+
/** Past each end of the tool, along its axis. */
|
|
97
|
+
readonly along: number;
|
|
98
|
+
}
|
|
99
|
+
interface FrameOptions {
|
|
100
|
+
/**
|
|
101
|
+
* Room for chrome around the content, in **pixels** — dimension bands, the
|
|
102
|
+
* arrowheads, the figures.
|
|
103
|
+
*
|
|
104
|
+
* **Widened from a plain number in phase 4, deliberately.** The dimension
|
|
105
|
+
* bands are genuinely asymmetric: with figures on both flanks each side
|
|
106
|
+
* needs as much room as its own bands take, and those differ. Forcing that
|
|
107
|
+
* through one scalar would mean padding both flanks by the wider of the two
|
|
108
|
+
* and throwing away the difference — real drawing area, on the axis where a
|
|
109
|
+
* long thin tool has least of it. A number still means what it always did.
|
|
110
|
+
*/
|
|
111
|
+
readonly padding?: number | Partial<Padding>;
|
|
112
|
+
/**
|
|
113
|
+
* The box to frame against before the panel has been measured. A
|
|
114
|
+
* `ResizeObserver` reports nothing on the server or on first paint, and
|
|
115
|
+
* framing against a zero box would paint at the stack's own width and then
|
|
116
|
+
* visibly jump. This is a plain landscape sheet instead.
|
|
117
|
+
*/
|
|
118
|
+
readonly defaultBox?: Box;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The type size a drawing in this box is set in, in **pixels**.
|
|
122
|
+
*
|
|
123
|
+
* Exported because the dimension model has to know it before there is a frame:
|
|
124
|
+
* its band widths are measured in type, the frame's padding is the total of
|
|
125
|
+
* those bands, and a frame cannot be built until the padding is known. It
|
|
126
|
+
* depends only on the panel, so there is no circle to close.
|
|
127
|
+
*/
|
|
128
|
+
declare const typeSizeFor: (box: Box, options?: FrameOptions) => number;
|
|
129
|
+
/**
|
|
130
|
+
* Which way the tool axis runs in this box: along its long side.
|
|
131
|
+
*
|
|
132
|
+
* Exported for the same reason as {@link typeSizeFor} — the dimension model
|
|
133
|
+
* has to size its bands before there is a frame, and one of its measures
|
|
134
|
+
* depends on which way the type runs relative to the axis. It reads the panel
|
|
135
|
+
* only, so there is no circle to close.
|
|
136
|
+
*/
|
|
137
|
+
declare const orientationFor: (box: Box, options?: FrameOptions) => Orientation;
|
|
138
|
+
declare const frameFor: (outline: Extent, box: Box, options?: FrameOptions) => Frame;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The sheet the tool is drawn on, and the ink it is drawn in.
|
|
142
|
+
*
|
|
143
|
+
* **Hard colours rather than the application's ramp** (Paul, 2026-09-01): a
|
|
144
|
+
* drawing is a drawing, and a "light grey" written as a zinc utility comes out
|
|
145
|
+
* dark under the flipped ramp. So the sheet, the linework and the two shades a
|
|
146
|
+
* tool has — gold flutes, steel body — are stated here.
|
|
147
|
+
*
|
|
148
|
+
* **One set per theme** (Paul, 2026-09-01: "2d tool visualization can't have
|
|
149
|
+
* the white background in dark mode — make it just barely lighter than any of
|
|
150
|
+
* the other backgrounds"). A white sheet in a dark application is a torch. In
|
|
151
|
+
* dark it is a shade above the card it sits on, and the ink turns over with
|
|
152
|
+
* it: light lines on a dark ground rather than dark lines nobody can see.
|
|
153
|
+
*
|
|
154
|
+
* Stated as literals in this package for a second reason as well: it has no
|
|
155
|
+
* Tailwind, no design-token import and no access to the consumer's ramp, so
|
|
156
|
+
* every colour the drawing uses has to be a colour rather than a class name.
|
|
157
|
+
*/
|
|
158
|
+
declare const SHEETS: {
|
|
159
|
+
readonly light: {
|
|
160
|
+
readonly ground: "#ffffff";
|
|
161
|
+
readonly ink: "#3f4650";
|
|
162
|
+
readonly centre: "#15181c";
|
|
163
|
+
readonly dimension: "#606a76";
|
|
164
|
+
readonly body: "#c4c8ce";
|
|
165
|
+
readonly flutes: "#e6bf59";
|
|
166
|
+
readonly holder: "#9aa2ad";
|
|
167
|
+
readonly connection: "#78818d";
|
|
168
|
+
};
|
|
169
|
+
readonly dark: {
|
|
170
|
+
readonly ground: "#22252b";
|
|
171
|
+
readonly ink: "#c7cdd6";
|
|
172
|
+
readonly centre: "#e8ebef";
|
|
173
|
+
readonly dimension: "#8d97a4";
|
|
174
|
+
readonly body: "#5b626c";
|
|
175
|
+
readonly flutes: "#c9a44b";
|
|
176
|
+
readonly holder: "#474d57";
|
|
177
|
+
readonly connection: "#3a4048";
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
type Theme = keyof typeof SHEETS;
|
|
181
|
+
type Sheet = (typeof SHEETS)[Theme];
|
|
182
|
+
|
|
183
|
+
export { type Box as B, type Extent as E, type Frame as F, type Orientation as O, type Padding as P, type Sheet as S, type Theme as T, type FrameOptions as a, SHEETS as b, frameFor as f, orientationFor as o, typeSizeFor as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toolpath/tool-drawing",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "2D elevation drawing of a cutting tool and its holder",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/toolpath/ui-packages.git",
|
|
12
|
+
"directory": "packages/tool-drawing"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://developers.toolpath.com",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/toolpath/ui-packages/issues"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public",
|
|
20
|
+
"registry": "https://registry.npmjs.org"
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./geometry": {
|
|
31
|
+
"types": "./dist/geometry/index.d.ts",
|
|
32
|
+
"import": "./dist/geometry/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./clearance": {
|
|
35
|
+
"types": "./dist/clearance/index.d.ts",
|
|
36
|
+
"import": "./dist/clearance/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./package.json": "./package.json"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"LICENSE",
|
|
43
|
+
"README.md"
|
|
44
|
+
],
|
|
45
|
+
"sideEffects": false,
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsup src/index.ts src/geometry/index.ts src/clearance/index.ts --format esm --dts --clean --external react --external react-dom",
|
|
48
|
+
"check-types": "tsc --noEmit",
|
|
49
|
+
"test": "vitest run"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"react": "^19.0.0",
|
|
53
|
+
"react-dom": "^19.0.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@testing-library/react": "^16.3.2",
|
|
57
|
+
"@types/node": "24.10.1",
|
|
58
|
+
"@types/react": "^19.0.0",
|
|
59
|
+
"@types/react-dom": "^19.0.0",
|
|
60
|
+
"jsdom": "^30.0.1",
|
|
61
|
+
"react": "19.2.0",
|
|
62
|
+
"react-dom": "19.2.0",
|
|
63
|
+
"tsup": "8.5.1",
|
|
64
|
+
"typescript": "5.9.3",
|
|
65
|
+
"vitest": "4.1.10"
|
|
66
|
+
}
|
|
67
|
+
}
|