@deltarobotics/uhd-viewer 0.0.0-stage → 0.2.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 +74 -0
- package/LICENSE +202 -0
- package/NOTICE +22 -0
- package/README.md +168 -2
- package/bin/serve.d.mts +62 -0
- package/bin/serve.mjs +257 -0
- package/bin/uhd-viewer.mjs +75 -0
- package/dist/app/assets/KodeMono-Bold-DdIivhPN.woff2 +0 -0
- package/dist/app/assets/KodeMono-Medium-CVnTuv5A.ttf +0 -0
- package/dist/app/assets/KodeMono-Regular-Bdr_-rJX.woff2 +0 -0
- package/dist/app/assets/KodeMono-SemiBold-JAfn5Dpm.ttf +0 -0
- package/dist/app/assets/index-CEJyT8hF.css +2 -0
- package/dist/app/assets/index-DWZ-48wc.js +3853 -0
- package/dist/app/index.html +16 -0
- package/dist/components/components.css +6372 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +26609 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/types/uhd-viewer/src/api.d.ts +286 -0
- package/dist/components/types/uhd-viewer/src/cad/CadView.d.ts +52 -0
- package/dist/components/types/uhd-viewer/src/cad/ModelView.d.ts +30 -0
- package/dist/components/types/uhd-viewer/src/cad/explode.d.ts +110 -0
- package/dist/components/types/uhd-viewer/src/cad/explodeSample.d.ts +25 -0
- package/dist/components/types/uhd-viewer/src/cad/findings.d.ts +41 -0
- package/dist/components/types/uhd-viewer/src/cad/labels.d.ts +76 -0
- package/dist/components/types/uhd-viewer/src/cad/model.d.ts +132 -0
- package/dist/components/types/uhd-viewer/src/cad/row.d.ts +15 -0
- package/dist/components/types/uhd-viewer/src/cad/scene.d.ts +95 -0
- package/dist/components/types/uhd-viewer/src/cad/scope.d.ts +89 -0
- package/dist/components/types/uhd-viewer/src/canvas/Canvas.d.ts +90 -0
- package/dist/components/types/uhd-viewer/src/canvas/Icon.d.ts +5 -0
- package/dist/components/types/uhd-viewer/src/canvas/LinkEdge.d.ts +40 -0
- package/dist/components/types/uhd-viewer/src/canvas/Port.d.ts +18 -0
- package/dist/components/types/uhd-viewer/src/canvas/actions.d.ts +5 -0
- package/dist/components/types/uhd-viewer/src/canvas/geometry.d.ts +72 -0
- package/dist/components/types/uhd-viewer/src/canvas/interaction.d.ts +162 -0
- package/dist/components/types/uhd-viewer/src/canvas/nodes.d.ts +43 -0
- package/dist/components/types/uhd-viewer/src/canvas/reveal.d.ts +55 -0
- package/dist/components/types/uhd-viewer/src/canvas/rightDrag.d.ts +51 -0
- package/dist/components/types/uhd-viewer/src/canvas/routing.d.ts +28 -0
- package/dist/components/types/uhd-viewer/src/chrome/ArtifactCards.d.ts +30 -0
- package/dist/components/types/uhd-viewer/src/chrome/Artifacts.d.ts +44 -0
- package/dist/components/types/uhd-viewer/src/chrome/BoardInspector.d.ts +53 -0
- package/dist/components/types/uhd-viewer/src/chrome/Chrome.d.ts +126 -0
- package/dist/components/types/uhd-viewer/src/chrome/Evidence.d.ts +20 -0
- package/dist/components/types/uhd-viewer/src/chrome/InterfaceCards.d.ts +66 -0
- package/dist/components/types/uhd-viewer/src/chrome/PairView.d.ts +37 -0
- package/dist/components/types/uhd-viewer/src/chrome/Sections.d.ts +128 -0
- package/dist/components/types/uhd-viewer/src/components.d.ts +60 -0
- package/dist/components/types/uhd-viewer/src/host/channel.d.ts +49 -0
- package/dist/components/types/uhd-viewer/src/host/context.d.ts +29 -0
- package/dist/components/types/uhd-viewer/src/host/protocol.d.ts +22 -0
- package/dist/components/types/uhd-viewer/src/host/targets.d.ts +101 -0
- package/dist/components/types/uhd-viewer/src/layout/board-layout.d.ts +69 -0
- package/dist/components/types/uhd-viewer/src/layout/card-body.d.ts +64 -0
- package/dist/components/types/uhd-viewer/src/layout/layout.d.ts +94 -0
- package/dist/components/types/uhd-viewer/src/layout/module-layout.d.ts +32 -0
- package/dist/components/types/uhd-viewer/src/layout/router.d.ts +55 -0
- package/dist/components/types/uhd-viewer/src/layout/taps.d.ts +20 -0
- package/dist/components/types/uhd-viewer/src/model/artifacts.d.ts +129 -0
- package/dist/components/types/uhd-viewer/src/model/classification.d.ts +41 -0
- package/dist/components/types/uhd-viewer/src/model/diagnostics.d.ts +27 -0
- package/dist/components/types/uhd-viewer/src/model/evidence.d.ts +63 -0
- package/dist/components/types/uhd-viewer/src/model/from-uhd.d.ts +58 -0
- package/dist/components/types/uhd-viewer/src/model/hierarchy.d.ts +173 -0
- package/dist/components/types/uhd-viewer/src/model/isolate.d.ts +33 -0
- package/dist/components/types/uhd-viewer/src/model/level.d.ts +7 -0
- package/dist/components/types/uhd-viewer/src/model/nets.d.ts +53 -0
- package/dist/components/types/uhd-viewer/src/model/outputs.d.ts +79 -0
- package/dist/components/types/uhd-viewer/src/model/slots.d.ts +184 -0
- package/dist/components/types/uhd-viewer/src/model/types.d.ts +213 -0
- package/dist/components/types/uhd-viewer/src/options.d.ts +27 -0
- package/dist/components/types/uhd-viewer/src/source/source.d.ts +109 -0
- package/dist/components/types/uhd-viewer/src/source/tolerant.d.ts +45 -0
- package/dist/components/types/uhd-viewer/src/theme/ThemeProvider.d.ts +33 -0
- package/dist/components/types/uhd-viewer/src/theme/adapters.d.ts +28 -0
- package/dist/components/types/uhd-viewer/src/theme/resolve.d.ts +19 -0
- package/dist/components/types/uhd-viewer/src/theme/themes.d.ts +67 -0
- package/dist/components/types/uhd-viewer/src/theme/tokens.d.ts +175 -0
- package/dist/components/types/uhd-viewer/src/ui/Inspector.d.ts +45 -0
- package/dist/components/types/uhd-viewer/src/ui/Legend.d.ts +59 -0
- package/dist/components/types/uhd-viewer/src/ui/ScrollArea.d.ts +16 -0
- package/dist/components/types/uhd-viewer/src/ui/Toolbar.d.ts +49 -0
- package/dist/components/types/uhd-viewer/src/ui/Tooltips.d.ts +3 -0
- package/dist/components/types/uhd-viewer/src/ui/cx.d.ts +2 -0
- package/dist/components/types/uhd-viewer/src/ui/legendFit.d.ts +55 -0
- package/dist/components/types/uhd-viewer/src/viewer/controller.d.ts +219 -0
- package/dist/components/types/uhd-viewer/src/viewer/details.d.ts +81 -0
- package/dist/components/types/uhd-viewer/src/viewer/hierarchy.d.ts +82 -0
- package/dist/components/types/uhd-viewer/src/viewer/mount.d.ts +12 -0
- package/dist/components/types/uhd-viewer/src/viewer/views.d.ts +187 -0
- package/dist/headless/index.d.ts +71 -0
- package/dist/headless/index.js +7425 -0
- package/dist/headless/index.js.map +1 -0
- package/dist/lib/api.d.ts +252 -0
- package/dist/lib/uhd-viewer.css +3 -0
- package/dist/lib/uhd-viewer.d.ts +4 -0
- package/dist/lib/uhd-viewer.js +97787 -0
- package/package.json +110 -4
- package/src/styles/fonts/OFL.txt +93 -0
- package/src/styles/tailwind-theme.css +48 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The viewer's public contract for hosts (docs: protoboard-cli/docs/specs/uhd-boundary.md,
|
|
3
|
+
* "The viewer contract"). A host gives the viewer a UHD bundle, may replace or
|
|
4
|
+
* extend how artifact files resolve, may add diagnostics of its own, and may
|
|
5
|
+
* take over saving the layout the user edits. Nothing here names a host.
|
|
6
|
+
*
|
|
7
|
+
* This file holds types only, so the published declarations stay small.
|
|
8
|
+
*/
|
|
9
|
+
import type { UhdBundle, UhdBundleDiagnostic } from "@deltarobotics/uhd";
|
|
10
|
+
/** URL for a definition's artifact file path, or undefined if it cannot be resolved. */
|
|
11
|
+
export type ArtifactResolver = (definitionId: string, filePath: string) => string | undefined;
|
|
12
|
+
/** A link's hand layout on one drill level. */
|
|
13
|
+
export interface LinkLayout {
|
|
14
|
+
/** Pinned waypoints [x, y] in absolute canvas coordinates (the router passes through them in order). */
|
|
15
|
+
waypoints?: [number, number][];
|
|
16
|
+
/** Label + toggle position as a fraction along the route (0..1). */
|
|
17
|
+
labelT?: number;
|
|
18
|
+
/** Label on the other side of the line (below a horizontal stretch, left of a vertical one). */
|
|
19
|
+
labelFlip?: boolean;
|
|
20
|
+
/** Harness name tab position as a fraction along the route (absent: placed automatically). */
|
|
21
|
+
tabT?: number;
|
|
22
|
+
/** Tab on the same side as the label (it defaults to the opposite side). */
|
|
23
|
+
tabFlip?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Layout edits made in the viewer, on top of the bundle's display hints.
|
|
27
|
+
* Both maps are keyed by drill level (instance path joined with "/", "" = top).
|
|
28
|
+
*/
|
|
29
|
+
export interface ViewerLayout {
|
|
30
|
+
/** Dragged node positions: top-level nodes absolute, nodes inside a container relative to it. */
|
|
31
|
+
positions: Record<string, Record<string, {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
}>>;
|
|
35
|
+
/** Pinned waypoints and label/tab positions by link id. */
|
|
36
|
+
links: Record<string, Record<string, LinkLayout>>;
|
|
37
|
+
}
|
|
38
|
+
/** A view of the diagram: the pan offset in pixels and the zoom factor. */
|
|
39
|
+
export interface ViewerViewport {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
zoom: number;
|
|
43
|
+
}
|
|
44
|
+
/** The tones a mark can take; each is a token of the theme (`--uhd-mark-<tone>`). */
|
|
45
|
+
export type NodeMarkTone = "neutral" | "info" | "ok" | "warning" | "error";
|
|
46
|
+
/** A host's mark on a module (`ViewerOptions.nodeMarks`). */
|
|
47
|
+
export interface NodeMark {
|
|
48
|
+
tone: NodeMarkTone;
|
|
49
|
+
/** What the mark means, for the tooltip. */
|
|
50
|
+
label?: string;
|
|
51
|
+
/** `"ring"`: the tone as a dashed ring instead of a filled dot, for a module that stands for something not chosen yet. Default: a dot. */
|
|
52
|
+
shape?: "dot" | "ring";
|
|
53
|
+
}
|
|
54
|
+
export interface ViewerOptions {
|
|
55
|
+
/** The bundle itself. Relative artifact paths resolve against `bundleUrl` when both are given. */
|
|
56
|
+
bundle?: UhdBundle;
|
|
57
|
+
/** Where to fetch the bundle from (used when `bundle` is not given). */
|
|
58
|
+
bundleUrl?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Replaces or extends artifact resolution. `fallback` is the default
|
|
61
|
+
* resolver (the bundle's per-definition artifact bases).
|
|
62
|
+
*/
|
|
63
|
+
resolveArtifact?: (definitionId: string, filePath: string, fallback: ArtifactResolver) => string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Findings from the host, listed in the checks panel next to the bundle's
|
|
66
|
+
* own. Like the bundle's, they describe the nominal build and are shown for
|
|
67
|
+
* the nominal scenario only.
|
|
68
|
+
*/
|
|
69
|
+
diagnostics?: UhdBundleDiagnostic[];
|
|
70
|
+
/**
|
|
71
|
+
* A mark per definition, by definition id: the dot in the corner of every
|
|
72
|
+
* module card drawn from that definition takes the mark's tone and says its
|
|
73
|
+
* label on hover. For what the host knows about a part and the bundle does
|
|
74
|
+
* not (where it comes from, whether it was reviewed). A module with no mark
|
|
75
|
+
* keeps the dot of its links' state.
|
|
76
|
+
*/
|
|
77
|
+
nodeMarks?: Record<string, NodeMark>;
|
|
78
|
+
/**
|
|
79
|
+
* The layout edits to show (only used together with `onLayoutChange`). A
|
|
80
|
+
* different layout passed later through `update` replaces the viewer's
|
|
81
|
+
* current edits, for example after the host's saved layout changed on disk.
|
|
82
|
+
* Passing back what `onLayoutChange` sent changes nothing.
|
|
83
|
+
*/
|
|
84
|
+
layout?: ViewerLayout;
|
|
85
|
+
/**
|
|
86
|
+
* Called (debounced) with the whole edited layout after every change. When
|
|
87
|
+
* given, the host owns the layout and the viewer stops keeping it in local
|
|
88
|
+
* storage.
|
|
89
|
+
*/
|
|
90
|
+
onLayoutChange?: (layout: ViewerLayout) => void;
|
|
91
|
+
/** The viewer has a model on screen and takes host messages from now on. */
|
|
92
|
+
onReady?: () => void;
|
|
93
|
+
/** Every change of the selection, in the diagram or the 3D view (null: nothing selected). */
|
|
94
|
+
onSelectionChanged?: (selection: ViewerSelection | null) => void;
|
|
95
|
+
/**
|
|
96
|
+
* The user asked to see where a module is defined (the inspector's OPEN
|
|
97
|
+
* SOURCE, a node's context menu). Given: the viewer offers these actions.
|
|
98
|
+
*/
|
|
99
|
+
onOpenSource?: (request: {
|
|
100
|
+
defId: string;
|
|
101
|
+
instancePath?: string;
|
|
102
|
+
}) => void;
|
|
103
|
+
/**
|
|
104
|
+
* The user picked an artifact file in the inspector. Given: the host opens
|
|
105
|
+
* it; otherwise the viewer opens the file's URL in a new tab.
|
|
106
|
+
*/
|
|
107
|
+
onOpenArtifact?: (request: {
|
|
108
|
+
defId: string;
|
|
109
|
+
artifactId: string;
|
|
110
|
+
}) => void;
|
|
111
|
+
/**
|
|
112
|
+
* The user asked to open a module on its own (a double-click on it, OPEN IN
|
|
113
|
+
* NEW TAB in its context menu): `path` is its instance path from the root.
|
|
114
|
+
* Given: double-click opens the module there (a host's new tab or window),
|
|
115
|
+
* and expanding it in place moves to the context menu. Not given:
|
|
116
|
+
* double-click expands the module in place.
|
|
117
|
+
*/
|
|
118
|
+
onOpenModule?: (request: {
|
|
119
|
+
path: string;
|
|
120
|
+
}) => void;
|
|
121
|
+
/**
|
|
122
|
+
* The user asked for the pair view of a link (the inspector's pair
|
|
123
|
+
* preview). Given: the host shows it (its own tab or window); otherwise
|
|
124
|
+
* the viewer opens it over the diagram. `linkId` is the link as a
|
|
125
|
+
* selection names it, `a` and `b` the instance paths of the two modules it
|
|
126
|
+
* joins.
|
|
127
|
+
*/
|
|
128
|
+
onOpenPair?: (request: {
|
|
129
|
+
linkId: string;
|
|
130
|
+
a: string;
|
|
131
|
+
b: string;
|
|
132
|
+
}) => void;
|
|
133
|
+
/**
|
|
134
|
+
* The user asked to inspect a module (INSPECT in its context menu): `path`
|
|
135
|
+
* is its instance path from the root. Given: the host shows it in an
|
|
136
|
+
* inspector of its own; otherwise the viewer opens its own panel. The
|
|
137
|
+
* module is selected either way (`onSelectionChanged` says so first).
|
|
138
|
+
*/
|
|
139
|
+
onInspect?: (request: {
|
|
140
|
+
path: string;
|
|
141
|
+
defId?: string;
|
|
142
|
+
}) => void;
|
|
143
|
+
/**
|
|
144
|
+
* Where the user left the view of each drill level (keyed like the layout;
|
|
145
|
+
* only used together with `onViewportChange`). A level with a viewport
|
|
146
|
+
* opens on it; a level with none is fitted to the diagram.
|
|
147
|
+
*/
|
|
148
|
+
viewports?: Record<string, ViewerViewport>;
|
|
149
|
+
/**
|
|
150
|
+
* Called with every level's viewport after the user pans or zooms (a fit
|
|
151
|
+
* the user asks for drops that level's entry). When given, the host keeps
|
|
152
|
+
* the viewports and the viewer stops keeping them in local storage.
|
|
153
|
+
*/
|
|
154
|
+
onViewportChange?: (viewports: Record<string, ViewerViewport>) => void;
|
|
155
|
+
/**
|
|
156
|
+
* Talk to the parent window over `postMessage` (the viewer in a frame):
|
|
157
|
+
* host messages are accepted from `window.parent` only, and only from the
|
|
158
|
+
* listed origins (exact origins, `scheme://*`, or `*`; default: the page's
|
|
159
|
+
* own origin); events are posted to the parent, at its origin. Open actions
|
|
160
|
+
* then go to the parent. `true` uses the defaults. Off unless given.
|
|
161
|
+
*/
|
|
162
|
+
parentWindow?: boolean | {
|
|
163
|
+
origins?: string[];
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* How the viewer looks: its own `"dark"` (default) or `"light"` theme,
|
|
167
|
+
* `"system"` (whichever the system prefers), `"vscode"` (follows the VS Code
|
|
168
|
+
* colour theme of the webview it runs in, high contrast included), a theme
|
|
169
|
+
* of the host's (tokens it leaves out come from the default), or an adapter
|
|
170
|
+
* that tells the viewer when the host's theme changes.
|
|
171
|
+
*/
|
|
172
|
+
theme?: ViewerThemeOption;
|
|
173
|
+
}
|
|
174
|
+
/** A theme: values for the viewer's semantic tokens (docs: README, "Theming"). */
|
|
175
|
+
export interface ViewerTheme {
|
|
176
|
+
id: string;
|
|
177
|
+
label: string;
|
|
178
|
+
scheme: "dark" | "light" | "high-contrast" | "high-contrast-light";
|
|
179
|
+
/** Token values by token name (`accent`, `surface`, `textMuted`, …): any CSS value, `var(...)` included. */
|
|
180
|
+
tokens: Partial<Record<string, string>>;
|
|
181
|
+
}
|
|
182
|
+
/** Where a host's theme comes from, when it can change while the viewer is open. */
|
|
183
|
+
export interface ViewerThemeAdapter {
|
|
184
|
+
id: string;
|
|
185
|
+
current(): ViewerTheme;
|
|
186
|
+
subscribe?(onChange: (theme: ViewerTheme) => void): () => void;
|
|
187
|
+
}
|
|
188
|
+
export type ViewerThemeOption = "dark" | "light" | "system" | "vscode" | ViewerTheme | ViewerThemeAdapter;
|
|
189
|
+
/** What a view shows: the diagram or the assembled 3D model. */
|
|
190
|
+
export type ViewerView = "diagram" | "3d";
|
|
191
|
+
/**
|
|
192
|
+
* A selection as the host sees it. Ids are canonical paths from the root
|
|
193
|
+
* system: a module's instance path (`arm_fl/motor`), a link's owner path and
|
|
194
|
+
* id (`stack/fc_esc`, as UHD's system checks name links).
|
|
195
|
+
*/
|
|
196
|
+
export interface ViewerSelection {
|
|
197
|
+
kind: "node" | "link";
|
|
198
|
+
id: string;
|
|
199
|
+
/** The selected module's definition id (a link's: the module that declares it). */
|
|
200
|
+
defId?: string;
|
|
201
|
+
/**
|
|
202
|
+
* Modules selected together (a selection box, Shift- or Ctrl-click, select all), two or more: every one, by
|
|
203
|
+
* path from the root. `id` is the first of them.
|
|
204
|
+
*/
|
|
205
|
+
ids?: string[];
|
|
206
|
+
}
|
|
207
|
+
/** Host → viewer messages (`window.postMessage`, or the handle's methods). */
|
|
208
|
+
export type ViewerHostMessage =
|
|
209
|
+
/** Drill to the level that holds the module, select it and centre it. */
|
|
210
|
+
{
|
|
211
|
+
type: "uhd-viewer:focus";
|
|
212
|
+
path: string;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Select on the current level (the first id that is there; none: clear the
|
|
216
|
+
* selection). The diagram pans (zooming out only if it must) to bring the
|
|
217
|
+
* selection into view when it is not; `reveal: false` leaves the view where it is.
|
|
218
|
+
*/
|
|
219
|
+
| {
|
|
220
|
+
type: "uhd-viewer:select";
|
|
221
|
+
ids: string[];
|
|
222
|
+
reveal?: boolean;
|
|
223
|
+
}
|
|
224
|
+
/** Show the level of this instance path ("" is the root system). */
|
|
225
|
+
| {
|
|
226
|
+
type: "uhd-viewer:drill";
|
|
227
|
+
path: string;
|
|
228
|
+
} | {
|
|
229
|
+
type: "uhd-viewer:setView";
|
|
230
|
+
view: ViewerView;
|
|
231
|
+
};
|
|
232
|
+
/** Viewer → host events (posted to the parent window, or the options' callbacks). */
|
|
233
|
+
export type ViewerEvent = {
|
|
234
|
+
type: "uhd-viewer:ready";
|
|
235
|
+
} | {
|
|
236
|
+
type: "uhd-viewer:selectionChanged";
|
|
237
|
+
selection: ViewerSelection | null;
|
|
238
|
+
} | {
|
|
239
|
+
type: "uhd-viewer:openSource";
|
|
240
|
+
defId: string;
|
|
241
|
+
instancePath?: string;
|
|
242
|
+
} | {
|
|
243
|
+
type: "uhd-viewer:openArtifact";
|
|
244
|
+
defId: string;
|
|
245
|
+
artifactId: string;
|
|
246
|
+
} | {
|
|
247
|
+
type: "uhd-viewer:openModule";
|
|
248
|
+
path: string;
|
|
249
|
+
} | {
|
|
250
|
+
type: "uhd-viewer:openPair";
|
|
251
|
+
linkId: string;
|
|
252
|
+
a: string;
|
|
253
|
+
b: string;
|
|
254
|
+
} | {
|
|
255
|
+
type: "uhd-viewer:inspect";
|
|
256
|
+
path: string;
|
|
257
|
+
defId?: string;
|
|
258
|
+
};
|
|
259
|
+
export interface ViewerHandle {
|
|
260
|
+
/**
|
|
261
|
+
* Re-renders with changed options (a rebuilt bundle, new host diagnostics, a
|
|
262
|
+
* changed layout). The change is merged into the current options; giving
|
|
263
|
+
* `bundleUrl` without `bundle` drops a bundle given earlier and loads from
|
|
264
|
+
* the URL.
|
|
265
|
+
*/
|
|
266
|
+
update(options: Partial<ViewerOptions>): void;
|
|
267
|
+
unmount(): void;
|
|
268
|
+
/** Drill to the level that holds the module at `path`, select it and centre it. */
|
|
269
|
+
focus(path: string): void;
|
|
270
|
+
/**
|
|
271
|
+
* Select on the current level: the first of `ids` drawn there (an empty list
|
|
272
|
+
* clears the selection), brought into view unless `reveal` is false.
|
|
273
|
+
*/
|
|
274
|
+
select(ids: string[], options?: {
|
|
275
|
+
reveal?: boolean;
|
|
276
|
+
}): void;
|
|
277
|
+
/** Show the level of an instance path ("" is the root system). */
|
|
278
|
+
drill(path: string): void;
|
|
279
|
+
setView(view: ViewerView): void;
|
|
280
|
+
/**
|
|
281
|
+
* Apply a host message object, as it arrives over `postMessage`. Returns
|
|
282
|
+
* false when it is not a valid viewer message. Messages sent before the
|
|
283
|
+
* model is on screen wait for it.
|
|
284
|
+
*/
|
|
285
|
+
post(message: unknown): boolean;
|
|
286
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 3D view (PB-775): the quadcopter assembled from UHD links, with interface
|
|
3
|
+
* geometry resolved through three binding directions, mounting hardware
|
|
4
|
+
* placed from the harnesses' fastener stacks, wire harnesses drawn from their
|
|
5
|
+
* own routed artifacts (placed by the parts they connect), and callout labels.
|
|
6
|
+
*/
|
|
7
|
+
import { type ReactNode } from "react";
|
|
8
|
+
import type { ModuleDef } from "@deltarobotics/uhd";
|
|
9
|
+
import type { ModuleLookup } from "@deltarobotics/uhd";
|
|
10
|
+
import type { UhdBundleDiagnostic } from "@deltarobotics/uhd";
|
|
11
|
+
import type { LoadedSource } from "../source/source";
|
|
12
|
+
import type { ScopedDiagnostic } from "../model/diagnostics";
|
|
13
|
+
export interface CadSelection {
|
|
14
|
+
body?: string;
|
|
15
|
+
iface?: string;
|
|
16
|
+
/** A component drawn inside the selected body (a board's part in board.glb), by instance path. */
|
|
17
|
+
component?: string;
|
|
18
|
+
mate?: string;
|
|
19
|
+
/** A routed wire harness, by instance path. */
|
|
20
|
+
harness?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function CadView({ system, lookup, source, diagnostics, hostDiagnostics, focusOn, onSelect, inspector, scope, emptyHint, }: {
|
|
23
|
+
/** The root system: every path a host gives or hears is from it. */
|
|
24
|
+
system: ModuleDef;
|
|
25
|
+
lookup: ModuleLookup;
|
|
26
|
+
source: LoadedSource;
|
|
27
|
+
/** The viewer's own checks (root and boards) and the host's findings, pinned to the model where they name something placed. */
|
|
28
|
+
diagnostics?: ScopedDiagnostic[];
|
|
29
|
+
hostDiagnostics?: UhdBundleDiagnostic[];
|
|
30
|
+
/**
|
|
31
|
+
* A host's focus or select, by instance path; `seq` tells repeats apart. A
|
|
32
|
+
* node selects the module's body and (with `look`, the default) looks at it;
|
|
33
|
+
* a link selects the mate of that path (`owner/link`); `none` clears.
|
|
34
|
+
*/
|
|
35
|
+
focusOn?: {
|
|
36
|
+
path: string;
|
|
37
|
+
seq: number;
|
|
38
|
+
kind?: "node" | "link" | "none";
|
|
39
|
+
look?: boolean;
|
|
40
|
+
};
|
|
41
|
+
/** Show the inspector card (the app's panel toggle; closed by default on a narrow view, PB-861). */
|
|
42
|
+
inspector?: boolean;
|
|
43
|
+
/** Every selection change (for the host), by paths from the root. */
|
|
44
|
+
onSelect?: (sel: CadSelection) => void;
|
|
45
|
+
/**
|
|
46
|
+
* The module to show, by instance path from the root (PB-897): that module
|
|
47
|
+
* alone, in its own coordinates. Absent or empty: the whole assembly.
|
|
48
|
+
*/
|
|
49
|
+
scope?: string[];
|
|
50
|
+
/** What a host adds where a module has nothing to draw (how its model is made). */
|
|
51
|
+
emptyHint?: ReactNode;
|
|
52
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
export interface ModelViewProps {
|
|
3
|
+
/** A GLB or glTF file. */
|
|
4
|
+
url?: string;
|
|
5
|
+
/**
|
|
6
|
+
* A model a host built (a converted STEP), in millimetres with Z up (the
|
|
7
|
+
* 3D view's CAD coordinates). Used instead of `url`.
|
|
8
|
+
*/
|
|
9
|
+
object?: THREE.Object3D | null;
|
|
10
|
+
/** The file's name, for the card. */
|
|
11
|
+
name?: string;
|
|
12
|
+
/** A line under the name ("STEP · converted in the viewer"). */
|
|
13
|
+
detail?: string;
|
|
14
|
+
/** Why the model could not be shown (the host's conversion failed). */
|
|
15
|
+
error?: string;
|
|
16
|
+
/** Called once the model is on screen, with what the card shows. */
|
|
17
|
+
onReady?: (info: ModelInfo) => void;
|
|
18
|
+
/** Show the card with the name and size from the start (default: folded into the toolbar's Model info button). */
|
|
19
|
+
infoOpen?: boolean;
|
|
20
|
+
/** Extra buttons at the toolbar's end (a host's "open in the system viewer"). */
|
|
21
|
+
actions?: React.ReactNode;
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ModelInfo {
|
|
25
|
+
/** Size along x, y, z (mm). */
|
|
26
|
+
size: [number, number, number];
|
|
27
|
+
meshes: number;
|
|
28
|
+
triangles: number;
|
|
29
|
+
}
|
|
30
|
+
export declare function ModelView({ url, object, name, detail, error, onReady, actions, infoOpen, className }: ModelViewProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exploded view by assembly order (PB-899). Parts that go together along the
|
|
3
|
+
* model's up direction come apart along it in the order they are stacked:
|
|
4
|
+
* the part at the bottom of a column stays, and each part above it rises
|
|
5
|
+
* until it clears everything it sits on, plus a gap. Two parts interact only
|
|
6
|
+
* where one lies over the other (their outlines, seen along the axis,
|
|
7
|
+
* overlap), so four motors on four arms each lift off their own arm, not
|
|
8
|
+
* off each other.
|
|
9
|
+
*
|
|
10
|
+
* The order is geometric, never the lowest coordinate alone (a cap whose
|
|
11
|
+
* skirt reaches below the bottle's lip still comes after the bottle):
|
|
12
|
+
*
|
|
13
|
+
* - a part held inside another (the other surrounds it on every side across
|
|
14
|
+
* the axis: a cell in a case) comes out through the side the holder leaves
|
|
15
|
+
* open: up when less of the holder is above it than below, down the other
|
|
16
|
+
* way (a seal seated under a cap's ceiling), up when the holder closes
|
|
17
|
+
* round it (parts go in before the lid), and clears the holder entirely;
|
|
18
|
+
* - otherwise the lower part, by its middle, comes first, and the upper one
|
|
19
|
+
* only has to clear the surface right under it.
|
|
20
|
+
*
|
|
21
|
+
* Pure: the scene samples each part's surfaces into height grids (raycasts in
|
|
22
|
+
* CadView) and this module orders and spaces them.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* A part's top and bottom surfaces over its outline, sampled on a grid in the
|
|
26
|
+
* plane across the axis: `top[i]` is the highest point of the part above
|
|
27
|
+
* cell i, `bottom[i]` the lowest (NaN where the part has nothing there).
|
|
28
|
+
*/
|
|
29
|
+
export interface HeightGrid {
|
|
30
|
+
/** Lower corner (u, v) of the grid and the cell size along u and v. */
|
|
31
|
+
u0: number;
|
|
32
|
+
v0: number;
|
|
33
|
+
du: number;
|
|
34
|
+
dv: number;
|
|
35
|
+
/** Cells along u and v. */
|
|
36
|
+
nu: number;
|
|
37
|
+
nv: number;
|
|
38
|
+
top: Float64Array;
|
|
39
|
+
bottom: Float64Array;
|
|
40
|
+
}
|
|
41
|
+
export interface StackPart {
|
|
42
|
+
key: string;
|
|
43
|
+
/** Extent along the axis (assembled). */
|
|
44
|
+
lo: number;
|
|
45
|
+
hi: number;
|
|
46
|
+
/** Middle along the axis. */
|
|
47
|
+
mid: number;
|
|
48
|
+
/** Outline across the axis: [uMin, uMax, vMin, vMax]. */
|
|
49
|
+
outline: [number, number, number, number];
|
|
50
|
+
grid: HeightGrid;
|
|
51
|
+
}
|
|
52
|
+
export interface StackOptions {
|
|
53
|
+
/** Space left between parts once apart (mm). */
|
|
54
|
+
gap: number;
|
|
55
|
+
/** Whether `holder` surrounds `part` across the axis (rays from the part's middle hit it on every side). */
|
|
56
|
+
surrounds?: (holder: StackPart, part: StackPart) => boolean;
|
|
57
|
+
/** Which way `part` leaves `holder` (default: `exitOf`, from the height grids). */
|
|
58
|
+
exit?: (holder: StackPart, part: StackPart) => "up" | "down" | undefined;
|
|
59
|
+
}
|
|
60
|
+
/** The grid cell holding (u, v), or -1 outside. */
|
|
61
|
+
export declare function cellAt(g: HeightGrid, u: number, v: number): number;
|
|
62
|
+
/** Centre of cell k. */
|
|
63
|
+
export declare const cellCentre: (g: HeightGrid, k: number) => [number, number];
|
|
64
|
+
/** A grid for a part made of axis-aligned boxes (tests, and parts without a mesh to sample). */
|
|
65
|
+
export declare function boxGrid(outline: [number, number, number, number], boxes: {
|
|
66
|
+
u: [number, number];
|
|
67
|
+
v: [number, number];
|
|
68
|
+
z: [number, number];
|
|
69
|
+
}[], n?: number): HeightGrid;
|
|
70
|
+
/**
|
|
71
|
+
* How far `upper` must rise to clear `lower` where they lie over each other:
|
|
72
|
+
* from the lowest point of `upper` over `lower`'s outline to the highest
|
|
73
|
+
* point of `lower` under `upper`'s outline (a cap's skirt clears the bottle's
|
|
74
|
+
* lip it reaches around; a motor clears only the arm's top under it).
|
|
75
|
+
* Undefined when neither lies over the other.
|
|
76
|
+
*/
|
|
77
|
+
export declare function clearance(lower: StackPart, upper: StackPart): number | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Which way a part held by `holder` comes out: `up` when the holder has
|
|
80
|
+
* material above the part at fewer points than below it, `down` the other
|
|
81
|
+
* way. When that ties, the side the part sticks out of the holder further (a
|
|
82
|
+
* probe through a cap's floor hangs below it); undefined when nothing
|
|
83
|
+
* decides.
|
|
84
|
+
*/
|
|
85
|
+
export declare function exitOf(holder: StackPart, part: StackPart, eps?: number): "up" | "down" | undefined;
|
|
86
|
+
/** What a holder has along one line through a part, parallel to the axis. */
|
|
87
|
+
export interface ExitLine {
|
|
88
|
+
partTop: number;
|
|
89
|
+
partBottom: number;
|
|
90
|
+
/** The holder has material above the part's top, below its bottom. */
|
|
91
|
+
above: boolean;
|
|
92
|
+
below: boolean;
|
|
93
|
+
/** The holder's highest and lowest points on the line. */
|
|
94
|
+
holderTop: number;
|
|
95
|
+
holderBottom: number;
|
|
96
|
+
}
|
|
97
|
+
/** The exit decided from lines through the part (see `exitOf`). */
|
|
98
|
+
export declare function exitFrom(lines: readonly ExitLine[], eps?: number): "up" | "down" | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Whether `p` is the lower of the two where they lie over each other,
|
|
101
|
+
* comparing the middles of their material there (a frame's tall centre stack
|
|
102
|
+
* does not put it above a motor on the arm's tip); undefined when they never
|
|
103
|
+
* lie over each other.
|
|
104
|
+
*/
|
|
105
|
+
export declare function lowerWhereTheyMeet(p: StackPart, q: StackPart): boolean | undefined;
|
|
106
|
+
/**
|
|
107
|
+
* Each part's rise along the axis when fully exploded (mm), by key. Parts
|
|
108
|
+
* that lie over nothing stay where they are.
|
|
109
|
+
*/
|
|
110
|
+
export declare function planStack(parts: readonly StackPart[], options: StackOptions): Map<string, number>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Samples a part's surfaces for the exploded stack (./explode.ts): rays along
|
|
3
|
+
* the up axis over the part's outline give its top and bottom heights, and
|
|
4
|
+
* rays across the axis say whether one part surrounds another.
|
|
5
|
+
*/
|
|
6
|
+
import * as THREE from "three";
|
|
7
|
+
import { type StackPart } from "./explode";
|
|
8
|
+
/** A sampled part, with the meshes it was sampled from (for the surround test). */
|
|
9
|
+
export interface SampledPart extends StackPart {
|
|
10
|
+
meshes: THREE.Mesh[];
|
|
11
|
+
/** Middle across the axis (u, v). */
|
|
12
|
+
centre: [number, number];
|
|
13
|
+
}
|
|
14
|
+
/** Two directions across the axis, completing a right-handed frame with it. */
|
|
15
|
+
export declare function acrossAxis(axis: THREE.Vector3): [THREE.Vector3, THREE.Vector3];
|
|
16
|
+
/** The part's top and bottom over a grid on its outline (world space, the assembled pose). */
|
|
17
|
+
export declare function sampleStackPart(key: string, objects: THREE.Object3D[], skip: Set<THREE.Object3D>, axis: THREE.Vector3, n?: number): SampledPart | undefined;
|
|
18
|
+
/** Whether `holder` surrounds `part` across the axis: rays from the part's middle meet it on at least three of four sides. */
|
|
19
|
+
export declare function surrounded(holder: SampledPart, part: SampledPart, axis: THREE.Vector3): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Which way `part` leaves `holder`, from rays along the axis through every
|
|
22
|
+
* sampled point of the part against the holder's own meshes (finer than the
|
|
23
|
+
* holder's height grid: a probe's few millimetres through a cap's floor).
|
|
24
|
+
*/
|
|
25
|
+
export declare function exitByRays(holder: SampledPart, part: SampledPart, axis: THREE.Vector3, eps?: number): "up" | "down" | undefined;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Findings on the 3D model (PB-823): where a diagnostic is, in the body that
|
|
3
|
+
* holds what it names. Fit and route findings from a host (a cad provider's
|
|
4
|
+
* check: "swd_window is 0.8 mm off the pads") and the viewer's own system
|
|
5
|
+
* checks both name canonical paths, so both can be pinned to the model:
|
|
6
|
+
*
|
|
7
|
+
* `enclosure:swd_window` the interface's frame origin, else the manifest
|
|
8
|
+
* feature of that name on the body
|
|
9
|
+
* `board/l1`, `board/u2:pin_14`
|
|
10
|
+
* inside a body that draws its components (a
|
|
11
|
+
* board): the manifest feature named by the
|
|
12
|
+
* component's instance path
|
|
13
|
+
* `board` the body's centre
|
|
14
|
+
* `link:<id>` a mate's frame on its first end
|
|
15
|
+
*/
|
|
16
|
+
import type { UhdBundleDiagnostic } from "@deltarobotics/uhd";
|
|
17
|
+
import type { Vec3 } from "@deltarobotics/uhd";
|
|
18
|
+
import type { ScopedDiagnostic } from "../model/diagnostics";
|
|
19
|
+
import type { CadModel } from "./model";
|
|
20
|
+
export interface CadTarget {
|
|
21
|
+
body?: string;
|
|
22
|
+
iface?: string;
|
|
23
|
+
/** A component drawn inside the body (its instance path relative to the body). */
|
|
24
|
+
component?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface FindingAnchor {
|
|
27
|
+
id: string;
|
|
28
|
+
severity: "error" | "warning";
|
|
29
|
+
title: string;
|
|
30
|
+
message: string;
|
|
31
|
+
/** Body key the point is in, and the point in the body's own coordinates (undefined: the body's centre). */
|
|
32
|
+
body: string;
|
|
33
|
+
local?: Vec3;
|
|
34
|
+
select: CadTarget;
|
|
35
|
+
/** From the host (a provider's fit or route check) rather than the viewer's own checks. */
|
|
36
|
+
host?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** Where a canonical path is on the model, if any placed body holds it. */
|
|
39
|
+
export declare function anchorOf(model: CadModel, ref: string): Omit<FindingAnchor, "id" | "severity" | "title" | "message"> | undefined;
|
|
40
|
+
/** Errors and warnings that can be pinned to the model, the system's first, then the host's. */
|
|
41
|
+
export declare function findingAnchors(model: CadModel, system: ScopedDiagnostic[], host: UhdBundleDiagnostic[]): FindingAnchor[];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callout labels for the 3D view (PB-775): screen-space cards with leader
|
|
3
|
+
* lines to 3D anchors, laid out in two columns like a product manual's
|
|
4
|
+
* exploded-view callouts. HTML stays upright and camera-facing by
|
|
5
|
+
* construction; positions are recomputed every frame.
|
|
6
|
+
*/
|
|
7
|
+
import * as THREE from "three";
|
|
8
|
+
export type LabelKind = "module" | "harness" | "interface" | "finding";
|
|
9
|
+
export interface LabelItem {
|
|
10
|
+
id: string;
|
|
11
|
+
kind: LabelKind;
|
|
12
|
+
title: string;
|
|
13
|
+
sub?: string;
|
|
14
|
+
/** A second class for the card, leader and dot (a finding's severity: "error", "warning"). */
|
|
15
|
+
tone?: string;
|
|
16
|
+
/** World-space anchor (read every frame, so it follows explode and animation). */
|
|
17
|
+
anchor: () => THREE.Vector3 | null;
|
|
18
|
+
onClick?: () => void;
|
|
19
|
+
}
|
|
20
|
+
/** Vertical gap between cards in a column, and the clearance between the model's anchors and a column. */
|
|
21
|
+
export declare const LABEL_GAP = 6;
|
|
22
|
+
/** The leader's short level run into a card's inner edge. */
|
|
23
|
+
export declare const LEADER_RUN = 16;
|
|
24
|
+
/** Whether segments p1–p2 and q1–q2 cross (touching at an end does not count). */
|
|
25
|
+
export declare function segmentsCross(p1: [number, number], p2: [number, number], q1: [number, number], q2: [number, number]): boolean;
|
|
26
|
+
/** A card to place: its anchor on screen (x, y) and its size. */
|
|
27
|
+
export interface LabelBox {
|
|
28
|
+
id: string;
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
w: number;
|
|
32
|
+
h: number;
|
|
33
|
+
}
|
|
34
|
+
/** Where a card goes: its top-left corner, the side of the model, and which column on that side (0 nearest). */
|
|
35
|
+
export interface SolvedLabel {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
side: -1 | 1;
|
|
39
|
+
column: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Callout placement (PB-873): cards in columns either side of the model,
|
|
43
|
+
* each column ordered by its anchors' screen y (then untangled, where two
|
|
44
|
+
* anchors at much the same height would still cross) so leader lines do not
|
|
45
|
+
* cross, and stacked so no two cards overlap. Sides are chosen by anchor x and then
|
|
46
|
+
* balanced by height; a side taller than the view spills into a second,
|
|
47
|
+
* outer column. Pure: re-solved every frame as the camera moves.
|
|
48
|
+
*/
|
|
49
|
+
export declare function solveLabels(items: LabelBox[], width: number, height: number, m: {
|
|
50
|
+
top: number;
|
|
51
|
+
bottom: number;
|
|
52
|
+
left: number;
|
|
53
|
+
right: number;
|
|
54
|
+
}): Map<string, SolvedLabel>;
|
|
55
|
+
export declare class LabelLayer {
|
|
56
|
+
readonly root: HTMLDivElement;
|
|
57
|
+
private svg;
|
|
58
|
+
private cards;
|
|
59
|
+
/** Place the next frame's cards at their solved positions without easing (stills). */
|
|
60
|
+
snap: boolean;
|
|
61
|
+
private items;
|
|
62
|
+
opacity: number;
|
|
63
|
+
target: number;
|
|
64
|
+
/** Screen margins kept clear (floating chrome). */
|
|
65
|
+
margins: {
|
|
66
|
+
top: number;
|
|
67
|
+
bottom: number;
|
|
68
|
+
left: number;
|
|
69
|
+
right: number;
|
|
70
|
+
};
|
|
71
|
+
constructor(host: HTMLElement);
|
|
72
|
+
setItems(items: LabelItem[]): void;
|
|
73
|
+
/** Lay out and draw; call every frame. */
|
|
74
|
+
update(camera: THREE.Camera, width: number, height: number, dt?: number): void;
|
|
75
|
+
dispose(): void;
|
|
76
|
+
}
|