@snaptrude/plugin-core 1.0.2-plainpoint-20261026.2 → 1.0.2
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/AGENTS.md +1 -0
- package/CHANGELOG.md +12 -0
- package/api-manifest.json +60 -0
- package/dist/api/core/camera/index.d.ts +4 -12
- package/dist/api/core/camera/index.d.ts.map +1 -1
- package/dist/api/core/comment/index.d.ts +3 -7
- package/dist/api/core/comment/index.d.ts.map +1 -1
- package/dist/api/core/geom/create/index.d.ts +21 -69
- package/dist/api/core/geom/create/index.d.ts.map +1 -1
- package/dist/api/core/geom/query/circle.d.ts +11 -31
- package/dist/api/core/geom/query/circle.d.ts.map +1 -1
- package/dist/api/core/geom/query/contour.d.ts +7 -19
- package/dist/api/core/geom/query/contour.d.ts.map +1 -1
- package/dist/api/core/geom/query/curve.d.ts +24 -72
- package/dist/api/core/geom/query/curve.d.ts.map +1 -1
- package/dist/api/core/geom/query/profile.d.ts +7 -19
- package/dist/api/core/geom/query/profile.d.ts.map +1 -1
- package/dist/api/core/geom/update/contour.d.ts +12 -36
- package/dist/api/core/geom/update/contour.d.ts.map +1 -1
- package/dist/api/core/geom/update/curve.d.ts +5 -13
- package/dist/api/core/geom/update/curve.d.ts.map +1 -1
- package/dist/api/core/geom/update/profile.d.ts +12 -36
- package/dist/api/core/geom/update/profile.d.ts.map +1 -1
- package/dist/api/core/io/import/index.d.ts +3 -7
- package/dist/api/core/io/import/index.d.ts.map +1 -1
- package/dist/api/core/math/quat.d.ts +2 -2
- package/dist/api/core/math/quat.d.ts.map +1 -1
- package/dist/api/core/math/vec3.d.ts +15 -15
- package/dist/api/core/math/vec3.d.ts.map +1 -1
- package/dist/api/design/boolean/index.d.ts +4 -20
- package/dist/api/design/boolean/index.d.ts.map +1 -1
- package/dist/api/design/create/bulk-items.d.ts +4 -20
- package/dist/api/design/create/bulk-items.d.ts.map +1 -1
- package/dist/api/design/create/index.d.ts +27 -67
- package/dist/api/design/create/index.d.ts.map +1 -1
- package/dist/api/design/delete/index.d.ts +7 -14
- package/dist/api/design/delete/index.d.ts.map +1 -1
- package/dist/api/design/dimensions.d.ts +4 -12
- package/dist/api/design/dimensions.d.ts.map +1 -1
- package/dist/api/design/edit/index.d.ts +3 -7
- package/dist/api/design/edit/index.d.ts.map +1 -1
- package/dist/api/design/erase/index.d.ts +2 -10
- package/dist/api/design/erase/index.d.ts.map +1 -1
- package/dist/api/design/family.d.ts +1 -5
- package/dist/api/design/family.d.ts.map +1 -1
- package/dist/api/design/index.d.ts +5 -0
- package/dist/api/design/index.d.ts.map +1 -1
- package/dist/api/design/lock.d.ts +0 -3
- package/dist/api/design/lock.d.ts.map +1 -1
- package/dist/api/design/text.d.ts +400 -0
- package/dist/api/design/text.d.ts.map +1 -0
- package/dist/api/design/transform/index.d.ts +8 -28
- package/dist/api/design/transform/index.d.ts.map +1 -1
- package/dist/api/entity/space.d.ts +6 -30
- package/dist/api/entity/space.d.ts.map +1 -1
- package/dist/api/presentation/drawings.d.ts +46 -1
- package/dist/api/presentation/drawings.d.ts.map +1 -1
- package/dist/api/presentation/graphics.d.ts +4 -0
- package/dist/api/presentation/graphics.d.ts.map +1 -1
- package/dist/api/presentation/placedViews.d.ts +29 -1
- package/dist/api/presentation/placedViews.d.ts.map +1 -1
- package/dist/api/presentation/standards.d.ts +14 -0
- package/dist/api/presentation/standards.d.ts.map +1 -1
- package/dist/handles.d.ts +0 -8
- package/dist/handles.d.ts.map +1 -1
- package/dist/index.cjs +2281 -2188
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2269 -2187
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
- package/src/api/core/camera/index.ts +5 -5
- package/src/api/core/comment/index.ts +3 -3
- package/src/api/core/geom/create/index.ts +25 -25
- package/src/api/core/geom/query/circle.ts +11 -11
- package/src/api/core/geom/query/contour.ts +6 -7
- package/src/api/core/geom/query/curve.ts +25 -25
- package/src/api/core/geom/query/profile.ts +6 -7
- package/src/api/core/geom/update/contour.ts +11 -12
- package/src/api/core/geom/update/curve.ts +5 -5
- package/src/api/core/geom/update/profile.ts +11 -12
- package/src/api/core/io/import/index.ts +2 -3
- package/src/api/core/math/quat.ts +2 -2
- package/src/api/core/math/vec3.ts +16 -16
- package/src/api/design/boolean/index.ts +2 -2
- package/src/api/design/create/bulk-items.ts +2 -3
- package/src/api/design/create/index.ts +26 -27
- package/src/api/design/delete/index.ts +7 -14
- package/src/api/design/dimensions.ts +4 -5
- package/src/api/design/edit/index.ts +3 -3
- package/src/api/design/erase/index.ts +2 -2
- package/src/api/design/family.ts +1 -2
- package/src/api/design/index.ts +5 -0
- package/src/api/design/lock.ts +0 -6
- package/src/api/design/text.ts +394 -0
- package/src/api/design/transform/index.ts +7 -15
- package/src/api/entity/space.ts +4 -5
- package/src/api/presentation/graphics.ts +4 -1
- package/src/api/presentation/placedViews.ts +12 -0
- package/src/api/presentation/standards.ts +6 -0
- package/src/handles.ts +0 -11
- package/test/designText.test.mjs +72 -0
- package/test/paperPensContract.test.mjs +39 -0
- package/test/handles.test.mjs +0 -36
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
DimensionHandle,
|
|
6
6
|
Vec3Components,
|
|
7
7
|
Vec3Handle,
|
|
8
|
-
Vec3Input,
|
|
9
8
|
} from "../../handles"
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -95,8 +94,8 @@ export abstract class PluginDesignDimensionsApi {
|
|
|
95
94
|
* ```
|
|
96
95
|
*/
|
|
97
96
|
public abstract create(
|
|
98
|
-
from:
|
|
99
|
-
to:
|
|
97
|
+
from: Vec3Handle,
|
|
98
|
+
to: Vec3Handle,
|
|
100
99
|
anchor: ComponentHandle,
|
|
101
100
|
options?: { anchorTo?: ComponentHandle; offset?: number },
|
|
102
101
|
): PluginApiReturn<DimensionHandle>
|
|
@@ -363,8 +362,8 @@ export type PluginDimensionsChangeResult = z.infer<
|
|
|
363
362
|
* TRANSPORT: positional args — validated host-side as this object.
|
|
364
363
|
*/
|
|
365
364
|
export const PluginDesignDimensionsCreateArgs = z.object({
|
|
366
|
-
from:
|
|
367
|
-
to:
|
|
365
|
+
from: Vec3Handle,
|
|
366
|
+
to: Vec3Handle,
|
|
368
367
|
anchor: ComponentHandle,
|
|
369
368
|
anchorTo: ComponentHandle.optional(),
|
|
370
369
|
offset: z.number().finite().optional(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as z from "zod"
|
|
2
2
|
import { PluginApiReturn } from "../../../types"
|
|
3
|
-
import { ComponentHandle,
|
|
3
|
+
import { ComponentHandle, Vec3Handle } from "../../../handles"
|
|
4
4
|
import { OffsetSplitOutcome } from "../shared"
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -41,7 +41,7 @@ export abstract class PluginDesignEditApi {
|
|
|
41
41
|
public abstract offsetSplit(
|
|
42
42
|
component: ComponentHandle,
|
|
43
43
|
offsetDistance: number,
|
|
44
|
-
options?: { profilePickPoint?:
|
|
44
|
+
options?: { profilePickPoint?: Vec3Handle },
|
|
45
45
|
): PluginApiReturn<OffsetSplitOutcome>
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -49,6 +49,6 @@ export abstract class PluginDesignEditApi {
|
|
|
49
49
|
export const PluginDesignOffsetSplitArgs = z.object({
|
|
50
50
|
component: ComponentHandle,
|
|
51
51
|
offsetDistance: z.number(),
|
|
52
|
-
profilePickPoint:
|
|
52
|
+
profilePickPoint: Vec3Handle.optional(),
|
|
53
53
|
})
|
|
54
54
|
export type PluginDesignOffsetSplitArgs = z.infer<typeof PluginDesignOffsetSplitArgs>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as z from "zod"
|
|
2
2
|
import { PluginApiReturn } from "../../../types"
|
|
3
|
-
import { ComponentHandle,
|
|
3
|
+
import { ComponentHandle, Vec3Handle } from "../../../handles"
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The erase strategies exposed to plugins. `SplitAndUnion` is *enumerable* but
|
|
@@ -38,7 +38,7 @@ export const ErasableEdgeRef = z.object({
|
|
|
38
38
|
/** The components this edge is incident to — one for external (DirectDelete); two or more for internal (DirectUnion; 3+ at T-junctions where several masses share the edge). */
|
|
39
39
|
components: z.array(ComponentHandle),
|
|
40
40
|
/** A representative point on the edge (its head endpoint). Diagnostic only. */
|
|
41
|
-
snapPoint:
|
|
41
|
+
snapPoint: Vec3Handle,
|
|
42
42
|
})
|
|
43
43
|
export type ErasableEdgeRef = z.infer<typeof ErasableEdgeRef>
|
|
44
44
|
|
package/src/api/design/family.ts
CHANGED
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
ComponentHandle,
|
|
6
6
|
Vec3Components,
|
|
7
7
|
Vec3Handle,
|
|
8
|
-
Vec3Input,
|
|
9
8
|
} from "../../handles"
|
|
10
9
|
import { NetworkFamilyContract } from "./networks"
|
|
11
10
|
|
|
@@ -1320,7 +1319,7 @@ export type PluginDesignFamilyDeleteArgs = z.infer<
|
|
|
1320
1319
|
export const PluginDesignFamilyPlaceArgs = z.object({
|
|
1321
1320
|
id: z.string().optional(),
|
|
1322
1321
|
version: z.number().int().positive().optional(),
|
|
1323
|
-
position: z.union([
|
|
1322
|
+
position: z.union([Vec3Handle, Vec3Components]).optional(),
|
|
1324
1323
|
rotation: z.number().optional(),
|
|
1325
1324
|
values: z.record(z.string(), z.union([z.number(), z.string()])).optional(),
|
|
1326
1325
|
storey: z.number().int().optional(),
|
package/src/api/design/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { PluginDesignEditApi } from "./edit"
|
|
|
15
15
|
import { PluginDesignUpdateApi } from "./update"
|
|
16
16
|
import { PluginDesignVisibilityApi } from "./visibility"
|
|
17
17
|
import { PluginDesignDimensionsApi } from "./dimensions"
|
|
18
|
+
import { PluginDesignTextApi } from "./text"
|
|
18
19
|
import { PluginDesignTypesApi } from "./types"
|
|
19
20
|
import { PluginDesignFamilyApi } from "./family"
|
|
20
21
|
import { PluginDesignGridsApi } from "./grids"
|
|
@@ -41,6 +42,7 @@ import {
|
|
|
41
42
|
* - {@linkcode PluginDesignApi.delete} — hard entity removal
|
|
42
43
|
* - {@linkcode PluginDesignApi.visibility} — hide / isolate / reveal entities
|
|
43
44
|
* - {@linkcode PluginDesignApi.dimensions} — dimension lines (Measuring Tape): create / list / delete / hide
|
|
45
|
+
* - {@linkcode PluginDesignApi.text} — authored text and annotations (Text / Annotate tools): create / list / get / update / setLeader
|
|
44
46
|
* - {@linkcode PluginDesignApi.types} — read-only building type / assembly reference
|
|
45
47
|
* - {@linkcode PluginDesignApi.family} — author parametric families from a JSON spec
|
|
46
48
|
* - {@linkcode PluginDesignApi.lock} / {@linkcode PluginDesignApi.unlock} / {@linkcode PluginDesignApi.isLocked} / {@linkcode PluginDesignApi.listLocked} — lock state (top-level design verbs, §2A.1)
|
|
@@ -79,6 +81,8 @@ export abstract class PluginDesignApi {
|
|
|
79
81
|
public abstract visibility: PluginDesignVisibilityApi
|
|
80
82
|
/** Dimension lines (Measuring Tape). See {@linkcode PluginDesignDimensionsApi}. */
|
|
81
83
|
public abstract dimensions: PluginDesignDimensionsApi
|
|
84
|
+
/** Authored text and annotations (Text / Annotate tools). See {@linkcode PluginDesignTextApi}. */
|
|
85
|
+
public abstract text: PluginDesignTextApi
|
|
82
86
|
/** Read-only building type / assembly reference. See {@linkcode PluginDesignTypesApi}. */
|
|
83
87
|
public abstract types: PluginDesignTypesApi
|
|
84
88
|
/** Author parametric families from a JSON spec. See {@linkcode PluginDesignFamilyApi}. */
|
|
@@ -296,6 +300,7 @@ export * from "./edit"
|
|
|
296
300
|
export * from "./update"
|
|
297
301
|
export * from "./visibility"
|
|
298
302
|
export * from "./dimensions"
|
|
303
|
+
export * from "./text"
|
|
299
304
|
export * from "./types"
|
|
300
305
|
export * from "./family"
|
|
301
306
|
export * from "./grids"
|
package/src/api/design/lock.ts
CHANGED
|
@@ -9,12 +9,6 @@ import { ComponentHandle } from "../../handles"
|
|
|
9
9
|
*/
|
|
10
10
|
export const PluginDesignChangeResult = z.object({
|
|
11
11
|
affected: z.array(ComponentHandle),
|
|
12
|
-
/** Derived BIM (walls/floors/slabs) the parametric engine removed with its host. Delete only. */
|
|
13
|
-
cascaded: z.array(ComponentHandle).optional(),
|
|
14
|
-
/** Handles that no longer resolved when the call ran and were skipped. Delete only. */
|
|
15
|
-
missing: z.array(ComponentHandle).optional(),
|
|
16
|
-
/** Handles the engine accepted but left standing; delete them again or report them. Delete only. */
|
|
17
|
-
rejected: z.array(ComponentHandle).optional(),
|
|
18
12
|
})
|
|
19
13
|
export type PluginDesignChangeResult = z.infer<typeof PluginDesignChangeResult>
|
|
20
14
|
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import * as z from "zod"
|
|
2
|
+
import { PluginApiReturn } from "../../types"
|
|
3
|
+
import { ComponentHandle, Vec3Components, Vec3Handle } from "../../handles"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Authored text — what the Text and Annotate tools place on a drawing.
|
|
7
|
+
*
|
|
8
|
+
* A text is a single line of words drawn flat in its drawing: a **plain text**
|
|
9
|
+
* is centred on its position; an **annotation** is the same text with a leader,
|
|
10
|
+
* a line from an arrowhead (the point it labels) to the text's position, then a
|
|
11
|
+
* short landing to where the words start. Texts are scene components, so their
|
|
12
|
+
* id is a {@linkcode ComponentHandle} and the generic component APIs work on
|
|
13
|
+
* them:
|
|
14
|
+
*
|
|
15
|
+
* - delete: `design.delete.entities`
|
|
16
|
+
* - move / rotate / mirror: `design.transform.*` (the leader travels with the text)
|
|
17
|
+
* - copy: `design.create.copy`
|
|
18
|
+
* - hide / show / isolate: `design.visibility.*`
|
|
19
|
+
* - lock / unlock: `design.lock` / `design.unlock`
|
|
20
|
+
* - select: `design.selection.*`
|
|
21
|
+
*
|
|
22
|
+
* **Where a text lives.** Plan text belongs to one storey and is drawn only in
|
|
23
|
+
* that storey's plan (never in 3D). Text placed while a section or elevation is
|
|
24
|
+
* open belongs to that drawing and is drawn only there. The record's `drawing`
|
|
25
|
+
* and `drawingId` say which.
|
|
26
|
+
*
|
|
27
|
+
* **Size and pens.** A text has no size of its own: every text draws at the
|
|
28
|
+
* project's Text size (Object Styles → Text, `presentation.standards`
|
|
29
|
+
* `annotation.Text.textMm`, paper mm) at the active view scale, and the leader
|
|
30
|
+
* draws with the Text category's projection pen
|
|
31
|
+
* (`presentation.graphics` / `presentation.standards` `objectStyles.Text`).
|
|
32
|
+
* Present sheets restyle all texts of a placed view at once through the
|
|
33
|
+
* `"Text"` and `"Annotation"` categories of
|
|
34
|
+
* `presentation.placedViews.updateStyles` (colour, font, leader visibility).
|
|
35
|
+
*
|
|
36
|
+
* **Fonts.** `fontFamily` is a Google Fonts family (`"Inter"`, `"Roboto"`,
|
|
37
|
+
* `"Open Sans"`, …) or one of the built-ins `"Arial"`, `"Helvetica"`,
|
|
38
|
+
* `"Times New Roman"`, `"Courier New"`, matched case-insensitively and stored
|
|
39
|
+
* in its canonical spelling; an unknown family is refused (`VALIDATION`). A
|
|
40
|
+
* font style the family does not have falls back to `"Regular"`, as in the
|
|
41
|
+
* Properties panel, so read back the style that applied.
|
|
42
|
+
*
|
|
43
|
+
* **Units.** Positions are world points in Babylon units (1 BU = 0.254 m).
|
|
44
|
+
*
|
|
45
|
+
* Every mutator here commits through the engine's command manager: one undo
|
|
46
|
+
* entry per call, autosaved, replayed to collaborators.
|
|
47
|
+
*
|
|
48
|
+
* Accessed via `snaptrude.design.text`.
|
|
49
|
+
*/
|
|
50
|
+
export abstract class PluginDesignTextApi {
|
|
51
|
+
constructor() {}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Place a text, or an annotation when `options.arrowhead` is given.
|
|
55
|
+
*
|
|
56
|
+
* Where it goes:
|
|
57
|
+
* - `options.storey` given → plan text on that storey, at its floor level
|
|
58
|
+
* (the `y` of `position` is ignored).
|
|
59
|
+
* - otherwise, with a section or elevation open on the canvas → that
|
|
60
|
+
* drawing; `position` (and `arrowhead`) are projected onto its plane.
|
|
61
|
+
* - otherwise → plan text on the active storey.
|
|
62
|
+
*
|
|
63
|
+
* The text is created unrotated (it reads along +X in plan); rotate it with
|
|
64
|
+
* `design.transform.rotate`. The font style falls back to `"Regular"` when the
|
|
65
|
+
* family has no such style.
|
|
66
|
+
*
|
|
67
|
+
* @param position - World point the text is placed at: the centre of a plain
|
|
68
|
+
* text, or the leader's text end (where the landing starts) of an
|
|
69
|
+
* annotation.
|
|
70
|
+
* @param content - The words, one line (no line breaks), not blank.
|
|
71
|
+
* @param options - `arrowhead` (world point the leader points at; makes an
|
|
72
|
+
* annotation), `color` (`#rrggbb`, default `#000000`), `fontFamily`
|
|
73
|
+
* (see **Fonts** above, default `"Inter"`), `fontStyle` (default
|
|
74
|
+
* `"Regular"`), `storey` (plan text on this storey).
|
|
75
|
+
* @returns The new text's {@linkcode ComponentHandle}.
|
|
76
|
+
* @throws `VALIDATION` for malformed arguments, blank or multi-line
|
|
77
|
+
* `content`, an unknown `fontFamily`, or an `arrowhead` on top of
|
|
78
|
+
* `position`; `PRECONDITION_FAILED`
|
|
79
|
+
* when `storey` does not exist or the editor is not mounted;
|
|
80
|
+
* `METHOD_NOT_PERMITTED` when plugin writes are disabled.
|
|
81
|
+
*
|
|
82
|
+
* @examplePrompt Label the living room "LIVING" on the plan
|
|
83
|
+
* @examplePrompt Add an annotation pointing at this column saying "RCC 300x300"
|
|
84
|
+
* @examplePrompt Write "NORTH ENTRY" in red bold text at the entrance on level 2
|
|
85
|
+
*
|
|
86
|
+
* # Example
|
|
87
|
+
* ```ts
|
|
88
|
+
* const { design, core } = snaptrude
|
|
89
|
+
* const v = core.math.vec3
|
|
90
|
+
* // A plain label on storey 1.
|
|
91
|
+
* await design.text.create(await v.new(10, 0, 20), "LIVING", { storey: 1 })
|
|
92
|
+
* // An annotation whose leader points at a wall corner.
|
|
93
|
+
* await design.text.create(await v.new(30, 0, 40), "Shear wall", {
|
|
94
|
+
* arrowhead: await v.new(22, 0, 32),
|
|
95
|
+
* color: "#c62828",
|
|
96
|
+
* fontStyle: "Bold",
|
|
97
|
+
* })
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
public abstract create(
|
|
101
|
+
position: Vec3Handle,
|
|
102
|
+
content: string,
|
|
103
|
+
options?: {
|
|
104
|
+
arrowhead?: Vec3Handle
|
|
105
|
+
color?: string
|
|
106
|
+
fontFamily?: string
|
|
107
|
+
fontStyle?: PluginTextFontStyle
|
|
108
|
+
storey?: number
|
|
109
|
+
},
|
|
110
|
+
): PluginApiReturn<ComponentHandle>
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* List the texts and annotations of every building in the active design
|
|
114
|
+
* option as full records (each carries its `buildingId`).
|
|
115
|
+
*
|
|
116
|
+
* Filters combine with AND: `storeys` keeps texts on those storeys (plan
|
|
117
|
+
* text, and texts of a section or elevation, carry the storey they were
|
|
118
|
+
* placed from), `kind` keeps only plain texts or only annotations,
|
|
119
|
+
* `isHidden` only the ones whose user Hide flag equals the value given.
|
|
120
|
+
*
|
|
121
|
+
* @param options - `storeys`, `kind`, `isHidden` filters (all optional,
|
|
122
|
+
* ANDed).
|
|
123
|
+
* @returns The matching {@linkcode PluginText} records (`[]` when nothing
|
|
124
|
+
* matches).
|
|
125
|
+
* @throws `VALIDATION` for malformed filters.
|
|
126
|
+
*
|
|
127
|
+
* @examplePrompt List all the text labels on level 1
|
|
128
|
+
* @examplePrompt Which annotations are in this model?
|
|
129
|
+
* @examplePrompt Find the text that says "LOBBY"
|
|
130
|
+
*
|
|
131
|
+
* @performance Array read — one host round-trip returns every record.
|
|
132
|
+
* Filter in one call rather than calling `get` per text.
|
|
133
|
+
*
|
|
134
|
+
* # Example
|
|
135
|
+
* ```ts
|
|
136
|
+
* const texts = await snaptrude.design.text.list({ storeys: [1] })
|
|
137
|
+
* const lobby = texts.find((t) => t.content === "LOBBY")
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
public abstract list(options?: {
|
|
141
|
+
storeys?: number[]
|
|
142
|
+
kind?: PluginTextKind
|
|
143
|
+
isHidden?: boolean
|
|
144
|
+
}): PluginApiReturn<PluginText[]>
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Read one text by handle.
|
|
148
|
+
*
|
|
149
|
+
* Returns `null` — rather than throwing — when the id is not a live text
|
|
150
|
+
* (deleted, undone, or another kind of component), so a handle kept across a
|
|
151
|
+
* delete or an undo can be polled safely.
|
|
152
|
+
*
|
|
153
|
+
* @param text - The text to read.
|
|
154
|
+
* @returns Its {@linkcode PluginText} record, or `null`.
|
|
155
|
+
* @throws `VALIDATION` if `text` is not a non-empty id string.
|
|
156
|
+
*
|
|
157
|
+
* @examplePrompt What does this text say?
|
|
158
|
+
* @examplePrompt Read the annotation I just placed
|
|
159
|
+
*
|
|
160
|
+
* # Example
|
|
161
|
+
* ```ts
|
|
162
|
+
* const t = await snaptrude.design.text.get(handle)
|
|
163
|
+
* if (t?.leader) console.log(t.content, "points at", t.leader.arrowhead)
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
public abstract get(text: ComponentHandle): PluginApiReturn<PluginText | null>
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Change the words, colour or font of one or more texts — the same edit as
|
|
170
|
+
* the Properties panel. Fields left out keep each text's own value. A font
|
|
171
|
+
* style the (new) family does not have falls back to `"Regular"`, as in the
|
|
172
|
+
* panel. One undo entry for the whole batch.
|
|
173
|
+
*
|
|
174
|
+
* @param texts - The texts to change (at least one).
|
|
175
|
+
* @param patch - `content`, `color`, `fontFamily`, `fontStyle` (at least
|
|
176
|
+
* one).
|
|
177
|
+
* @returns The texts that actually changed, as
|
|
178
|
+
* {@linkcode PluginTextChangeResult} — texts already matching the patch
|
|
179
|
+
* are omitted.
|
|
180
|
+
* @throws `VALIDATION` for malformed arguments, blank / multi-line
|
|
181
|
+
* `content` or an unknown `fontFamily`; `HANDLE_INVALID` if any handle
|
|
182
|
+
* is not a live text (the whole call rejects before anything changes);
|
|
183
|
+
* `PRECONDITION_FAILED` if any text is locked or outside the active
|
|
184
|
+
* design option; `METHOD_NOT_PERMITTED`
|
|
185
|
+
* when plugin writes are disabled.
|
|
186
|
+
*
|
|
187
|
+
* @examplePrompt Make all the room labels bold
|
|
188
|
+
* @examplePrompt Change this annotation to read "Fire exit"
|
|
189
|
+
* @examplePrompt Use Roboto in blue for every text on level 2
|
|
190
|
+
*
|
|
191
|
+
* @performance Array API — pass the whole set in one call (one host
|
|
192
|
+
* round-trip, one undo entry). Never loop this per text.
|
|
193
|
+
*
|
|
194
|
+
* # Example
|
|
195
|
+
* ```ts
|
|
196
|
+
* const text = snaptrude.design.text
|
|
197
|
+
* const level2 = await text.list({ storeys: [2] })
|
|
198
|
+
* await text.update(level2.map((t) => t.id), { fontFamily: "Roboto", color: "#1565c0" })
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
public abstract update(
|
|
202
|
+
texts: ComponentHandle[],
|
|
203
|
+
patch: PluginTextPatch,
|
|
204
|
+
): PluginApiReturn<PluginTextChangeResult>
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Point a text's leader at a new place — the same edit as dragging an
|
|
208
|
+
* annotation's arrowhead with the Edit tool. Passing `null` removes the
|
|
209
|
+
* leader (the annotation becomes a plain text); giving a plain text an
|
|
210
|
+
* arrowhead turns it into an annotation. The text itself does not move. To
|
|
211
|
+
* move the text while its arrowhead stays put, move it with
|
|
212
|
+
* `design.transform.move`, then call this with the old `leader.arrowhead`.
|
|
213
|
+
*
|
|
214
|
+
* @param text - The text to change.
|
|
215
|
+
* @param arrowhead - World point the leader points at (projected onto the
|
|
216
|
+
* text's drawing plane), or `null` to remove the leader.
|
|
217
|
+
* @returns The updated {@linkcode PluginText} record.
|
|
218
|
+
* @throws `VALIDATION` for malformed arguments or an arrowhead on top of the
|
|
219
|
+
* text's position; `HANDLE_INVALID` if `text` is not a live text;
|
|
220
|
+
* `PRECONDITION_FAILED` if it is locked or outside the active design
|
|
221
|
+
* option; `METHOD_NOT_PERMITTED` when plugin writes are disabled.
|
|
222
|
+
*
|
|
223
|
+
* @examplePrompt Point this annotation at the door instead
|
|
224
|
+
* @examplePrompt Remove the leader from this label
|
|
225
|
+
*
|
|
226
|
+
* # Example
|
|
227
|
+
* ```ts
|
|
228
|
+
* const { design, core } = snaptrude
|
|
229
|
+
* await design.text.setLeader(handle, await core.math.vec3.new(12, 0, 8))
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
public abstract setLeader(
|
|
233
|
+
text: ComponentHandle,
|
|
234
|
+
arrowhead: Vec3Handle | null,
|
|
235
|
+
): PluginApiReturn<PluginText>
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** The four font styles a text can use (the Properties panel's Style list). */
|
|
239
|
+
export const PluginTextFontStyle = z.enum([
|
|
240
|
+
"Regular",
|
|
241
|
+
"Bold",
|
|
242
|
+
"Italic",
|
|
243
|
+
"Bold Italic",
|
|
244
|
+
])
|
|
245
|
+
export type PluginTextFontStyle = z.infer<typeof PluginTextFontStyle>
|
|
246
|
+
|
|
247
|
+
/** `"text"` — a plain text; `"annotation"` — a text with a leader. */
|
|
248
|
+
export const PluginTextKind = z.enum(["text", "annotation"])
|
|
249
|
+
export type PluginTextKind = z.infer<typeof PluginTextKind>
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The drawing a text belongs to: `"plan"` (its storey's plan), `"section"`,
|
|
253
|
+
* `"elevation"`, or `"view"` (a saved 2D view).
|
|
254
|
+
*/
|
|
255
|
+
export const PluginTextDrawing = z.enum(["plan", "section", "elevation", "view"])
|
|
256
|
+
export type PluginTextDrawing = z.infer<typeof PluginTextDrawing>
|
|
257
|
+
|
|
258
|
+
const TextContent = z
|
|
259
|
+
.string()
|
|
260
|
+
.refine((s) => s.trim().length > 0, { message: "content must not be blank" })
|
|
261
|
+
.refine((s) => !/[\r\n]/.test(s), {
|
|
262
|
+
message: "content must be one line (no line breaks)",
|
|
263
|
+
})
|
|
264
|
+
const TextColor = z
|
|
265
|
+
.string()
|
|
266
|
+
.regex(/^#[0-9a-fA-F]{6}$/, "color must be a #rrggbb hex color")
|
|
267
|
+
const TextFontFamily = z
|
|
268
|
+
.string()
|
|
269
|
+
.refine((s) => s.trim().length > 0, { message: "fontFamily must not be blank" })
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* One text or annotation. Positions are world points in Babylon units.
|
|
273
|
+
*
|
|
274
|
+
* | Property | Type | Description |
|
|
275
|
+
* |---|---|---|
|
|
276
|
+
* | `id` | {@linkcode ComponentHandle} | The text's handle |
|
|
277
|
+
* | `kind` | {@linkcode PluginTextKind} | `"text"` or `"annotation"` (has a leader) |
|
|
278
|
+
* | `content` | `string` | The words |
|
|
279
|
+
* | `color` | `string` | `#rrggbb` |
|
|
280
|
+
* | `fontFamily` | `string` | Font family |
|
|
281
|
+
* | `fontStyle` | {@linkcode PluginTextFontStyle} | Font style |
|
|
282
|
+
* | `position` | {@linkcode Vec3Components} | Centre of a plain text; the leader's text end of an annotation |
|
|
283
|
+
* | `rotation` | `number` | Rotation in its drawing in degrees, positive clockwise as seen on screen — in plan, the sense a positive `design.transform.rotate` angle turns it (0 = reads along +X) |
|
|
284
|
+
* | `leader` | `{ arrowhead: Vec3Components } \| null` | Where the leader points; `null` for plain text |
|
|
285
|
+
* | `storey` | `number` | Storey it belongs to |
|
|
286
|
+
* | `buildingId` | `string \| null` | Building it belongs to |
|
|
287
|
+
* | `drawing` | {@linkcode PluginTextDrawing} | The drawing it is drawn in |
|
|
288
|
+
* | `drawingId` | `string \| null` | The section id, elevation preset or view id; `null` for plan text |
|
|
289
|
+
* | `isHidden` | `boolean` | The user Hide flag (`design.visibility.hide` / `show`) |
|
|
290
|
+
* | `isLocked` | `boolean` | Locked against edits — its own lock (`design.lock` / `unlock`) or a locked group or design option, as `design.isLocked` reports |
|
|
291
|
+
*/
|
|
292
|
+
export const PluginText = z.object({
|
|
293
|
+
id: ComponentHandle,
|
|
294
|
+
kind: PluginTextKind,
|
|
295
|
+
content: z.string(),
|
|
296
|
+
color: z.string(),
|
|
297
|
+
fontFamily: z.string(),
|
|
298
|
+
fontStyle: PluginTextFontStyle,
|
|
299
|
+
position: Vec3Components,
|
|
300
|
+
rotation: z.number(),
|
|
301
|
+
leader: z.object({ arrowhead: Vec3Components }).nullable(),
|
|
302
|
+
storey: z.number(),
|
|
303
|
+
buildingId: z.string().nullable(),
|
|
304
|
+
drawing: PluginTextDrawing,
|
|
305
|
+
drawingId: z.string().nullable(),
|
|
306
|
+
isHidden: z.boolean(),
|
|
307
|
+
isLocked: z.boolean(),
|
|
308
|
+
})
|
|
309
|
+
export type PluginText = z.infer<typeof PluginText>
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* A sparse edit for {@linkcode PluginDesignTextApi.update} — at least one
|
|
313
|
+
* field.
|
|
314
|
+
*
|
|
315
|
+
* | Property | Type | Description |
|
|
316
|
+
* |---|---|---|
|
|
317
|
+
* | `content` | `string`? | New words (one line, not blank) |
|
|
318
|
+
* | `color` | `string`? | `#rrggbb` |
|
|
319
|
+
* | `fontFamily` | `string`? | Google Fonts family (`"Inter"`, `"Roboto"`, …) or `"Arial"` / `"Helvetica"` / `"Times New Roman"` / `"Courier New"` |
|
|
320
|
+
* | `fontStyle` | {@linkcode PluginTextFontStyle}? | Falls back to `"Regular"` when the family lacks it |
|
|
321
|
+
*/
|
|
322
|
+
export const PluginTextPatch = z
|
|
323
|
+
.object({
|
|
324
|
+
content: TextContent.optional(),
|
|
325
|
+
color: TextColor.optional(),
|
|
326
|
+
fontFamily: TextFontFamily.optional(),
|
|
327
|
+
fontStyle: PluginTextFontStyle.optional(),
|
|
328
|
+
})
|
|
329
|
+
.strict()
|
|
330
|
+
.refine((p) => Object.keys(p).length > 0, {
|
|
331
|
+
message: "patch must set at least one field",
|
|
332
|
+
})
|
|
333
|
+
export type PluginTextPatch = z.infer<typeof PluginTextPatch>
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Result of {@linkcode PluginDesignTextApi.update}: the texts the call
|
|
337
|
+
* changed.
|
|
338
|
+
*
|
|
339
|
+
* | Property | Type | Description |
|
|
340
|
+
* |---|---|---|
|
|
341
|
+
* | `affected` | {@linkcode ComponentHandle}`[]` | The texts the call changed |
|
|
342
|
+
*/
|
|
343
|
+
export const PluginTextChangeResult = z.object({
|
|
344
|
+
affected: z.array(ComponentHandle),
|
|
345
|
+
})
|
|
346
|
+
export type PluginTextChangeResult = z.infer<typeof PluginTextChangeResult>
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Arguments for {@linkcode PluginDesignTextApi.create} (options flattened).
|
|
350
|
+
*
|
|
351
|
+
* TRANSPORT: positional args — validated host-side as this object.
|
|
352
|
+
*/
|
|
353
|
+
export const PluginDesignTextCreateArgs = z.object({
|
|
354
|
+
position: Vec3Handle,
|
|
355
|
+
content: TextContent,
|
|
356
|
+
arrowhead: Vec3Handle.optional(),
|
|
357
|
+
color: TextColor.optional(),
|
|
358
|
+
fontFamily: TextFontFamily.optional(),
|
|
359
|
+
fontStyle: PluginTextFontStyle.optional(),
|
|
360
|
+
storey: z.number().int().optional(),
|
|
361
|
+
})
|
|
362
|
+
export type PluginDesignTextCreateArgs = z.infer<
|
|
363
|
+
typeof PluginDesignTextCreateArgs
|
|
364
|
+
>
|
|
365
|
+
|
|
366
|
+
/** Arguments for {@linkcode PluginDesignTextApi.list} (options flattened). */
|
|
367
|
+
export const PluginDesignTextListArgs = z.object({
|
|
368
|
+
storeys: z.array(z.number()).optional(),
|
|
369
|
+
kind: PluginTextKind.optional(),
|
|
370
|
+
isHidden: z.boolean().optional(),
|
|
371
|
+
})
|
|
372
|
+
export type PluginDesignTextListArgs = z.infer<typeof PluginDesignTextListArgs>
|
|
373
|
+
|
|
374
|
+
/** Arguments for {@linkcode PluginDesignTextApi.get}. */
|
|
375
|
+
export const PluginDesignTextGetArgs = z.object({ text: ComponentHandle })
|
|
376
|
+
export type PluginDesignTextGetArgs = z.infer<typeof PluginDesignTextGetArgs>
|
|
377
|
+
|
|
378
|
+
/** Arguments for {@linkcode PluginDesignTextApi.update}. */
|
|
379
|
+
export const PluginDesignTextUpdateArgs = z.object({
|
|
380
|
+
texts: z.array(ComponentHandle).min(1),
|
|
381
|
+
patch: PluginTextPatch,
|
|
382
|
+
})
|
|
383
|
+
export type PluginDesignTextUpdateArgs = z.infer<
|
|
384
|
+
typeof PluginDesignTextUpdateArgs
|
|
385
|
+
>
|
|
386
|
+
|
|
387
|
+
/** Arguments for {@linkcode PluginDesignTextApi.setLeader}. */
|
|
388
|
+
export const PluginDesignTextSetLeaderArgs = z.object({
|
|
389
|
+
text: ComponentHandle,
|
|
390
|
+
arrowhead: Vec3Handle.nullable(),
|
|
391
|
+
})
|
|
392
|
+
export type PluginDesignTextSetLeaderArgs = z.infer<
|
|
393
|
+
typeof PluginDesignTextSetLeaderArgs
|
|
394
|
+
>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as z from "zod"
|
|
2
2
|
import { PluginApiReturn } from "../../../types"
|
|
3
|
-
import { ComponentHandle, Vec3Handle
|
|
3
|
+
import { ComponentHandle, Vec3Handle } from "../../../handles"
|
|
4
4
|
import { PluginDesignChangeResult } from "../lock"
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -22,17 +22,9 @@ export abstract class PluginDesignTransformApi {
|
|
|
22
22
|
* component's absolute position as the anchor). Undoable.
|
|
23
23
|
*
|
|
24
24
|
* @param components - Entities to move
|
|
25
|
-
* @param displacement - Relative translation as a {@linkcode Vec3Handle}
|
|
26
|
-
* shorter than 1e-6 is a no-op that succeeds (hosts before Oct 2026 throw
|
|
27
|
-
* `OPERATION_FAILED` "No movement occurred" instead, so skip the call when nothing moves).
|
|
25
|
+
* @param displacement - Relative translation as a {@linkcode Vec3Handle}
|
|
28
26
|
* @param options - `is2D`, `trackParentChange`
|
|
29
27
|
* @returns {@linkcode PluginDesignChangeResult}
|
|
30
|
-
* @throws `OPERATION_FAILED` when the engine drops the whole move. A single connected
|
|
31
|
-
* parametric wall moves only across its own line: the vertical part and the part along
|
|
32
|
-
* the line are dropped. Move the whole connected run in one call to lift walls; to slide
|
|
33
|
-
* or lengthen one wall, delete it and create it again on the new line. Graph-bound walls
|
|
34
|
-
* made by Sketch-to-BIM refuse `move` outright (`TOPOLOGY_ERROR` / `ZERO_MOVEMENT`):
|
|
35
|
-
* adjust the room boundary that owns the wall instead.
|
|
36
28
|
*
|
|
37
29
|
* @examplePrompt Move the selected room 5 metres to the right
|
|
38
30
|
* @examplePrompt Shift these walls 3m along the x axis
|
|
@@ -51,7 +43,7 @@ export abstract class PluginDesignTransformApi {
|
|
|
51
43
|
*/
|
|
52
44
|
public abstract move(
|
|
53
45
|
components: ComponentHandle[],
|
|
54
|
-
displacement:
|
|
46
|
+
displacement: Vec3Handle,
|
|
55
47
|
options?: { is2D?: boolean; trackParentChange?: boolean },
|
|
56
48
|
): PluginApiReturn<PluginDesignChangeResult>
|
|
57
49
|
|
|
@@ -89,7 +81,7 @@ export abstract class PluginDesignTransformApi {
|
|
|
89
81
|
public abstract rotate(
|
|
90
82
|
components: ComponentHandle[],
|
|
91
83
|
angleInDegrees: number,
|
|
92
|
-
options?: { axis?:
|
|
84
|
+
options?: { axis?: Vec3Handle; pivot?: Vec3Handle; is2D?: boolean },
|
|
93
85
|
): PluginApiReturn<PluginDesignChangeResult>
|
|
94
86
|
|
|
95
87
|
/**
|
|
@@ -213,7 +205,7 @@ export type PluginAlignEdge = z.infer<typeof PluginAlignEdge>
|
|
|
213
205
|
*/
|
|
214
206
|
export const PluginDesignMoveArgs = z.object({
|
|
215
207
|
components: z.array(ComponentHandle).min(1),
|
|
216
|
-
displacement:
|
|
208
|
+
displacement: Vec3Handle,
|
|
217
209
|
is2D: z.boolean().optional(),
|
|
218
210
|
trackParentChange: z.boolean().optional(),
|
|
219
211
|
})
|
|
@@ -225,9 +217,9 @@ export type PluginDesignMoveArgs = z.infer<typeof PluginDesignMoveArgs>
|
|
|
225
217
|
export const PluginDesignRotateArgs = z.object({
|
|
226
218
|
components: z.array(ComponentHandle).min(1),
|
|
227
219
|
angleInDegrees: z.number(),
|
|
228
|
-
axis:
|
|
220
|
+
axis: Vec3Handle.optional(),
|
|
229
221
|
is2D: z.boolean().optional(),
|
|
230
|
-
pivot:
|
|
222
|
+
pivot: Vec3Handle.optional(),
|
|
231
223
|
})
|
|
232
224
|
export type PluginDesignRotateArgs = z.infer<typeof PluginDesignRotateArgs>
|
|
233
225
|
|
package/src/api/entity/space.ts
CHANGED
|
@@ -6,7 +6,6 @@ import * as z from "zod"
|
|
|
6
6
|
import {
|
|
7
7
|
ProfileHandle,
|
|
8
8
|
Vec3Handle,
|
|
9
|
-
Vec3Input,
|
|
10
9
|
Vec3Components,
|
|
11
10
|
QuatComponents,
|
|
12
11
|
CurveHandle,
|
|
@@ -22,7 +21,7 @@ import {
|
|
|
22
21
|
* | `dimensions` | `{ width, height, depth }` | Box dimensions in Babylon units |
|
|
23
22
|
*/
|
|
24
23
|
export const PluginSpaceCreateRectangularArgs = z.object({
|
|
25
|
-
position:
|
|
24
|
+
position: Vec3Handle,
|
|
26
25
|
dimensions: z.object({
|
|
27
26
|
width: z.number(),
|
|
28
27
|
height: z.number(),
|
|
@@ -63,7 +62,7 @@ export const PluginSpaceCreateFromProfileArgs = z.object({
|
|
|
63
62
|
profile: ProfileHandle,
|
|
64
63
|
innerProfiles: z.array(ProfileHandle).optional(),
|
|
65
64
|
extrudeHeight: z.number(),
|
|
66
|
-
position:
|
|
65
|
+
position: Vec3Handle,
|
|
67
66
|
})
|
|
68
67
|
|
|
69
68
|
export type PluginSpaceCreateFromProfileArgs = z.infer<
|
|
@@ -467,7 +466,7 @@ export type PluginSpaceUpdateResult = z.infer<typeof PluginSpaceUpdateResult>
|
|
|
467
466
|
export const PluginSpaceBulkCreateItem = z.discriminatedUnion("kind", [
|
|
468
467
|
z.object({
|
|
469
468
|
kind: z.literal("rectangular"),
|
|
470
|
-
position:
|
|
469
|
+
position: Vec3Handle,
|
|
471
470
|
dimensions: z.object({
|
|
472
471
|
width: z.number(),
|
|
473
472
|
height: z.number(),
|
|
@@ -483,7 +482,7 @@ export const PluginSpaceBulkCreateItem = z.discriminatedUnion("kind", [
|
|
|
483
482
|
profile: ProfileHandle,
|
|
484
483
|
innerProfiles: z.array(ProfileHandle).optional(),
|
|
485
484
|
extrudeHeight: z.number(),
|
|
486
|
-
position:
|
|
485
|
+
position: Vec3Handle,
|
|
487
486
|
room_type: z.string().optional(),
|
|
488
487
|
spaceType: PluginSpaceType.optional(),
|
|
489
488
|
massType: PluginMassType.optional(),
|
|
@@ -41,17 +41,20 @@ export const PluginGraphicsModelCategory = z.enum([
|
|
|
41
41
|
"Walls",
|
|
42
42
|
"Windows",
|
|
43
43
|
])
|
|
44
|
+
/** Text is authored text and annotations; its projection pen draws their leader lines. */
|
|
44
45
|
export const PluginGraphicsAnnotationCategory = z.enum([
|
|
45
46
|
"Dimension",
|
|
46
47
|
"Grids",
|
|
47
48
|
"Sections",
|
|
49
|
+
"Text",
|
|
48
50
|
])
|
|
49
51
|
export const PluginGraphicsCategory = z.enum([
|
|
50
52
|
...PluginGraphicsModelCategory.options,
|
|
51
53
|
...PluginGraphicsAnnotationCategory.options,
|
|
52
54
|
])
|
|
55
|
+
/** Text has no view override: its meshes are not hidden or halftoned by category. */
|
|
53
56
|
export const PluginGraphicsOverrideCategory = z.enum([
|
|
54
|
-
...PluginGraphicsCategory.options,
|
|
57
|
+
...PluginGraphicsCategory.exclude(["Text"]).options,
|
|
55
58
|
"Lines",
|
|
56
59
|
])
|
|
57
60
|
export type PluginGraphicsOverrideCategory = z.infer<
|