@genart-dev/plugin-construction 0.1.0 → 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/README.md +6 -12
- package/dist/index.cjs +170 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -11
- package/dist/index.d.ts +27 -11
- package/dist/index.js +168 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
package/dist/index.d.cts
CHANGED
|
@@ -10,8 +10,6 @@ declare const envelopeLayerType: LayerTypeDefinition;
|
|
|
10
10
|
|
|
11
11
|
declare const intersectionLayerType: LayerTypeDefinition;
|
|
12
12
|
|
|
13
|
-
declare const constructionMcpTools: McpToolDefinition[];
|
|
14
|
-
|
|
15
13
|
/** 3D vector. */
|
|
16
14
|
interface Vec3 {
|
|
17
15
|
x: number;
|
|
@@ -72,6 +70,32 @@ declare function dot3(a: Vec3, b: Vec3): number;
|
|
|
72
70
|
/** Cross product of two Vec3. */
|
|
73
71
|
declare function cross3(a: Vec3, b: Vec3): Vec3;
|
|
74
72
|
|
|
73
|
+
type FormType = "box" | "cylinder" | "sphere" | "cone" | "wedge" | "egg";
|
|
74
|
+
interface FormDefinition {
|
|
75
|
+
type: FormType;
|
|
76
|
+
position: Vec3;
|
|
77
|
+
size: Vec3;
|
|
78
|
+
rotation: Vec3;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A single component in a compound form. */
|
|
82
|
+
interface CompoundComponent {
|
|
83
|
+
type: FormType;
|
|
84
|
+
/** Offset from parent center (in form-size units). */
|
|
85
|
+
offsetX: number;
|
|
86
|
+
offsetY: number;
|
|
87
|
+
offsetZ: number;
|
|
88
|
+
/** Scale relative to parent form size. */
|
|
89
|
+
scaleX: number;
|
|
90
|
+
scaleY: number;
|
|
91
|
+
scaleZ: number;
|
|
92
|
+
}
|
|
93
|
+
/** Named compound form presets. */
|
|
94
|
+
declare const COMPOUND_PRESETS: Record<string, CompoundComponent[]>;
|
|
95
|
+
declare const compoundFormLayerType: LayerTypeDefinition;
|
|
96
|
+
|
|
97
|
+
declare const constructionMcpTools: McpToolDefinition[];
|
|
98
|
+
|
|
75
99
|
interface EllipseParams {
|
|
76
100
|
cx: number;
|
|
77
101
|
cy: number;
|
|
@@ -190,14 +214,6 @@ declare function comparativeMeasure(segment1: [Vec2, Vec2], segment2: [Vec2, Vec
|
|
|
190
214
|
label: string;
|
|
191
215
|
};
|
|
192
216
|
|
|
193
|
-
type FormType = "box" | "cylinder" | "sphere" | "cone" | "wedge" | "egg";
|
|
194
|
-
interface FormDefinition {
|
|
195
|
-
type: FormType;
|
|
196
|
-
position: Vec3;
|
|
197
|
-
size: Vec3;
|
|
198
|
-
rotation: Vec3;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
217
|
/**
|
|
202
218
|
* Approximate form intersection by sampling both surfaces and finding
|
|
203
219
|
* points where they are equidistant from the camera (same Z depth).
|
|
@@ -208,4 +224,4 @@ declare function approximateIntersection(form1: FormDefinition, form2: FormDefin
|
|
|
208
224
|
|
|
209
225
|
declare const constructionPlugin: DesignPlugin;
|
|
210
226
|
|
|
211
|
-
export { approximateIntersection, castShadow, clamp, comparativeMeasure, computeEnvelope, constructionMcpTools, cross3, crossContourLayerType, constructionPlugin as default, dot3, drawEllipse, drawEllipseWithHidden, ellipsePoints, envelopeAngles, envelopeLayerType, formLayerType, identityMatrix, intersectionLayerType, levelLine, lightDirection, lightDirection2D, multiplyMat3, normalize3, plumbLine, project, projectedEllipse, rotate3D, rotationMatrix, sphereTerminator, sphereValueZones, transformPoint, transformedNormalZ, valueShapesLayerType };
|
|
227
|
+
export { COMPOUND_PRESETS, type CompoundComponent, approximateIntersection, castShadow, clamp, comparativeMeasure, compoundFormLayerType, computeEnvelope, constructionMcpTools, cross3, crossContourLayerType, constructionPlugin as default, dot3, drawEllipse, drawEllipseWithHidden, ellipsePoints, envelopeAngles, envelopeLayerType, formLayerType, identityMatrix, intersectionLayerType, levelLine, lightDirection, lightDirection2D, multiplyMat3, normalize3, plumbLine, project, projectedEllipse, rotate3D, rotationMatrix, sphereTerminator, sphereValueZones, transformPoint, transformedNormalZ, valueShapesLayerType };
|
package/dist/index.d.ts
CHANGED
|
@@ -10,8 +10,6 @@ declare const envelopeLayerType: LayerTypeDefinition;
|
|
|
10
10
|
|
|
11
11
|
declare const intersectionLayerType: LayerTypeDefinition;
|
|
12
12
|
|
|
13
|
-
declare const constructionMcpTools: McpToolDefinition[];
|
|
14
|
-
|
|
15
13
|
/** 3D vector. */
|
|
16
14
|
interface Vec3 {
|
|
17
15
|
x: number;
|
|
@@ -72,6 +70,32 @@ declare function dot3(a: Vec3, b: Vec3): number;
|
|
|
72
70
|
/** Cross product of two Vec3. */
|
|
73
71
|
declare function cross3(a: Vec3, b: Vec3): Vec3;
|
|
74
72
|
|
|
73
|
+
type FormType = "box" | "cylinder" | "sphere" | "cone" | "wedge" | "egg";
|
|
74
|
+
interface FormDefinition {
|
|
75
|
+
type: FormType;
|
|
76
|
+
position: Vec3;
|
|
77
|
+
size: Vec3;
|
|
78
|
+
rotation: Vec3;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A single component in a compound form. */
|
|
82
|
+
interface CompoundComponent {
|
|
83
|
+
type: FormType;
|
|
84
|
+
/** Offset from parent center (in form-size units). */
|
|
85
|
+
offsetX: number;
|
|
86
|
+
offsetY: number;
|
|
87
|
+
offsetZ: number;
|
|
88
|
+
/** Scale relative to parent form size. */
|
|
89
|
+
scaleX: number;
|
|
90
|
+
scaleY: number;
|
|
91
|
+
scaleZ: number;
|
|
92
|
+
}
|
|
93
|
+
/** Named compound form presets. */
|
|
94
|
+
declare const COMPOUND_PRESETS: Record<string, CompoundComponent[]>;
|
|
95
|
+
declare const compoundFormLayerType: LayerTypeDefinition;
|
|
96
|
+
|
|
97
|
+
declare const constructionMcpTools: McpToolDefinition[];
|
|
98
|
+
|
|
75
99
|
interface EllipseParams {
|
|
76
100
|
cx: number;
|
|
77
101
|
cy: number;
|
|
@@ -190,14 +214,6 @@ declare function comparativeMeasure(segment1: [Vec2, Vec2], segment2: [Vec2, Vec
|
|
|
190
214
|
label: string;
|
|
191
215
|
};
|
|
192
216
|
|
|
193
|
-
type FormType = "box" | "cylinder" | "sphere" | "cone" | "wedge" | "egg";
|
|
194
|
-
interface FormDefinition {
|
|
195
|
-
type: FormType;
|
|
196
|
-
position: Vec3;
|
|
197
|
-
size: Vec3;
|
|
198
|
-
rotation: Vec3;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
217
|
/**
|
|
202
218
|
* Approximate form intersection by sampling both surfaces and finding
|
|
203
219
|
* points where they are equidistant from the camera (same Z depth).
|
|
@@ -208,4 +224,4 @@ declare function approximateIntersection(form1: FormDefinition, form2: FormDefin
|
|
|
208
224
|
|
|
209
225
|
declare const constructionPlugin: DesignPlugin;
|
|
210
226
|
|
|
211
|
-
export { approximateIntersection, castShadow, clamp, comparativeMeasure, computeEnvelope, constructionMcpTools, cross3, crossContourLayerType, constructionPlugin as default, dot3, drawEllipse, drawEllipseWithHidden, ellipsePoints, envelopeAngles, envelopeLayerType, formLayerType, identityMatrix, intersectionLayerType, levelLine, lightDirection, lightDirection2D, multiplyMat3, normalize3, plumbLine, project, projectedEllipse, rotate3D, rotationMatrix, sphereTerminator, sphereValueZones, transformPoint, transformedNormalZ, valueShapesLayerType };
|
|
227
|
+
export { COMPOUND_PRESETS, type CompoundComponent, approximateIntersection, castShadow, clamp, comparativeMeasure, compoundFormLayerType, computeEnvelope, constructionMcpTools, cross3, crossContourLayerType, constructionPlugin as default, dot3, drawEllipse, drawEllipseWithHidden, ellipsePoints, envelopeAngles, envelopeLayerType, formLayerType, identityMatrix, intersectionLayerType, levelLine, lightDirection, lightDirection2D, multiplyMat3, normalize3, plumbLine, project, projectedEllipse, rotate3D, rotationMatrix, sphereTerminator, sphereValueZones, transformPoint, transformedNormalZ, valueShapesLayerType };
|
package/dist/index.js
CHANGED
|
@@ -2346,6 +2346,169 @@ var intersectionLayerType = {
|
|
|
2346
2346
|
}
|
|
2347
2347
|
};
|
|
2348
2348
|
|
|
2349
|
+
// src/compound-form-layer.ts
|
|
2350
|
+
var COMPOUND_PRESETS = {
|
|
2351
|
+
snowman: [
|
|
2352
|
+
{ type: "sphere", offsetX: 0, offsetY: 0.35, offsetZ: 0, scaleX: 1, scaleY: 1, scaleZ: 1 },
|
|
2353
|
+
{ type: "sphere", offsetX: 0, offsetY: -0.05, offsetZ: 0, scaleX: 0.75, scaleY: 0.75, scaleZ: 0.75 },
|
|
2354
|
+
{ type: "sphere", offsetX: 0, offsetY: -0.35, offsetZ: 0, scaleX: 0.5, scaleY: 0.5, scaleZ: 0.5 }
|
|
2355
|
+
],
|
|
2356
|
+
bottle: [
|
|
2357
|
+
{ type: "cylinder", offsetX: 0, offsetY: 0.15, offsetZ: 0, scaleX: 1, scaleY: 0.7, scaleZ: 1 },
|
|
2358
|
+
{ type: "cone", offsetX: 0, offsetY: -0.25, offsetZ: 0, scaleX: 0.6, scaleY: 0.3, scaleZ: 0.6 },
|
|
2359
|
+
{ type: "cylinder", offsetX: 0, offsetY: -0.42, offsetZ: 0, scaleX: 0.3, scaleY: 0.15, scaleZ: 0.3 }
|
|
2360
|
+
],
|
|
2361
|
+
mushroom: [
|
|
2362
|
+
{ type: "sphere", offsetX: 0, offsetY: -0.2, offsetZ: 0, scaleX: 1.2, scaleY: 0.5, scaleZ: 1.2 },
|
|
2363
|
+
{ type: "cylinder", offsetX: 0, offsetY: 0.2, offsetZ: 0, scaleX: 0.3, scaleY: 0.5, scaleZ: 0.3 }
|
|
2364
|
+
],
|
|
2365
|
+
lamp: [
|
|
2366
|
+
{ type: "cone", offsetX: 0, offsetY: -0.25, offsetZ: 0, scaleX: 1, scaleY: 0.5, scaleZ: 1 },
|
|
2367
|
+
{ type: "cylinder", offsetX: 0, offsetY: 0.1, offsetZ: 0, scaleX: 0.15, scaleY: 0.5, scaleZ: 0.15 },
|
|
2368
|
+
{ type: "cylinder", offsetX: 0, offsetY: 0.4, offsetZ: 0, scaleX: 0.5, scaleY: 0.05, scaleZ: 0.5 }
|
|
2369
|
+
],
|
|
2370
|
+
tree: [
|
|
2371
|
+
{ type: "cylinder", offsetX: 0, offsetY: 0.25, offsetZ: 0, scaleX: 0.2, scaleY: 0.5, scaleZ: 0.2 },
|
|
2372
|
+
{ type: "egg", offsetX: 0, offsetY: -0.2, offsetZ: 0, scaleX: 0.8, scaleY: 0.6, scaleZ: 0.8 }
|
|
2373
|
+
]
|
|
2374
|
+
};
|
|
2375
|
+
var COMPOUND_PROPERTIES = [
|
|
2376
|
+
{
|
|
2377
|
+
key: "preset",
|
|
2378
|
+
label: "Preset",
|
|
2379
|
+
type: "select",
|
|
2380
|
+
default: "snowman",
|
|
2381
|
+
options: Object.keys(COMPOUND_PRESETS).map((k) => ({ value: k, label: k.charAt(0).toUpperCase() + k.slice(1) })),
|
|
2382
|
+
group: "compound"
|
|
2383
|
+
},
|
|
2384
|
+
{
|
|
2385
|
+
key: "components",
|
|
2386
|
+
label: "Components (JSON)",
|
|
2387
|
+
type: "string",
|
|
2388
|
+
default: "",
|
|
2389
|
+
group: "compound"
|
|
2390
|
+
},
|
|
2391
|
+
{
|
|
2392
|
+
key: "position",
|
|
2393
|
+
label: "Position",
|
|
2394
|
+
type: "point",
|
|
2395
|
+
default: { x: 0.5, y: 0.5 },
|
|
2396
|
+
group: "compound"
|
|
2397
|
+
},
|
|
2398
|
+
{
|
|
2399
|
+
key: "formSize",
|
|
2400
|
+
label: "Size",
|
|
2401
|
+
type: "number",
|
|
2402
|
+
default: 0.25,
|
|
2403
|
+
min: 0.05,
|
|
2404
|
+
max: 0.6,
|
|
2405
|
+
step: 0.01,
|
|
2406
|
+
group: "compound"
|
|
2407
|
+
},
|
|
2408
|
+
{
|
|
2409
|
+
key: "rotX",
|
|
2410
|
+
label: "Rotation X (deg)",
|
|
2411
|
+
type: "number",
|
|
2412
|
+
default: 15,
|
|
2413
|
+
min: -90,
|
|
2414
|
+
max: 90,
|
|
2415
|
+
step: 5,
|
|
2416
|
+
group: "compound"
|
|
2417
|
+
},
|
|
2418
|
+
{
|
|
2419
|
+
key: "rotY",
|
|
2420
|
+
label: "Rotation Y (deg)",
|
|
2421
|
+
type: "number",
|
|
2422
|
+
default: 25,
|
|
2423
|
+
min: -180,
|
|
2424
|
+
max: 180,
|
|
2425
|
+
step: 5,
|
|
2426
|
+
group: "compound"
|
|
2427
|
+
},
|
|
2428
|
+
...COMMON_GUIDE_PROPERTIES
|
|
2429
|
+
];
|
|
2430
|
+
var compoundFormLayerType = {
|
|
2431
|
+
typeId: "construction:compound-form",
|
|
2432
|
+
displayName: "Compound Form",
|
|
2433
|
+
icon: "compound",
|
|
2434
|
+
category: "guide",
|
|
2435
|
+
properties: COMPOUND_PROPERTIES,
|
|
2436
|
+
propertyEditorId: "construction:compound-form-editor",
|
|
2437
|
+
createDefault() {
|
|
2438
|
+
const props = {};
|
|
2439
|
+
for (const schema of COMPOUND_PROPERTIES) {
|
|
2440
|
+
props[schema.key] = schema.default;
|
|
2441
|
+
}
|
|
2442
|
+
return props;
|
|
2443
|
+
},
|
|
2444
|
+
render(properties, ctx, bounds, _resources) {
|
|
2445
|
+
const preset = properties.preset ?? "snowman";
|
|
2446
|
+
const componentsJson = properties.components ?? "";
|
|
2447
|
+
const position = properties.position ?? { x: 0.5, y: 0.5 };
|
|
2448
|
+
const formSize = properties.formSize ?? 0.25;
|
|
2449
|
+
const rotX = properties.rotX ?? 15;
|
|
2450
|
+
const rotY = properties.rotY ?? 25;
|
|
2451
|
+
let components;
|
|
2452
|
+
if (componentsJson.trim()) {
|
|
2453
|
+
try {
|
|
2454
|
+
components = JSON.parse(componentsJson);
|
|
2455
|
+
} catch {
|
|
2456
|
+
components = COMPOUND_PRESETS[preset] ?? COMPOUND_PRESETS["snowman"];
|
|
2457
|
+
}
|
|
2458
|
+
} else {
|
|
2459
|
+
components = COMPOUND_PRESETS[preset] ?? COMPOUND_PRESETS["snowman"];
|
|
2460
|
+
}
|
|
2461
|
+
if (components.length === 0) return;
|
|
2462
|
+
const w = bounds.width;
|
|
2463
|
+
const h = bounds.height;
|
|
2464
|
+
const baseSize = Math.min(w, h) * formSize;
|
|
2465
|
+
const cx = bounds.x + w * position.x;
|
|
2466
|
+
const cy = bounds.y + h * position.y;
|
|
2467
|
+
ctx.save();
|
|
2468
|
+
const guideColor = properties.guideColor ?? "rgba(0,200,255,0.5)";
|
|
2469
|
+
const lineWidth = properties.lineWidth ?? 1;
|
|
2470
|
+
const dashPattern = properties.dashPattern ?? "";
|
|
2471
|
+
setupGuideStyle(ctx, guideColor, lineWidth, dashPattern);
|
|
2472
|
+
const mat = rotationMatrix(rotX, rotY, 0);
|
|
2473
|
+
const sorted = components.map((comp, i) => {
|
|
2474
|
+
const p3d = rotate3D({ x: comp.offsetX * baseSize, y: comp.offsetY * baseSize, z: comp.offsetZ * baseSize }, mat);
|
|
2475
|
+
return { comp, z: p3d.z, index: i };
|
|
2476
|
+
}).sort((a, b) => b.z - a.z);
|
|
2477
|
+
for (const { comp } of sorted) {
|
|
2478
|
+
const offset = rotate3D(
|
|
2479
|
+
{ x: comp.offsetX * baseSize, y: comp.offsetY * baseSize, z: comp.offsetZ * baseSize },
|
|
2480
|
+
mat
|
|
2481
|
+
);
|
|
2482
|
+
const compCx = cx + offset.x;
|
|
2483
|
+
const compCy = cy + offset.y;
|
|
2484
|
+
const compSize = baseSize * Math.max(comp.scaleX, comp.scaleY);
|
|
2485
|
+
ctx.beginPath();
|
|
2486
|
+
const rx = compSize * comp.scaleX * 0.5;
|
|
2487
|
+
const ry = compSize * comp.scaleY * 0.5;
|
|
2488
|
+
ctx.ellipse(compCx, compCy, Math.max(1, rx), Math.max(1, ry), 0, 0, Math.PI * 2);
|
|
2489
|
+
ctx.stroke();
|
|
2490
|
+
ctx.beginPath();
|
|
2491
|
+
ctx.arc(compCx, compCy, 2, 0, Math.PI * 2);
|
|
2492
|
+
ctx.fill();
|
|
2493
|
+
}
|
|
2494
|
+
ctx.restore();
|
|
2495
|
+
},
|
|
2496
|
+
validate(properties) {
|
|
2497
|
+
const componentsJson = properties.components;
|
|
2498
|
+
if (componentsJson && componentsJson.trim()) {
|
|
2499
|
+
try {
|
|
2500
|
+
const parsed = JSON.parse(componentsJson);
|
|
2501
|
+
if (!Array.isArray(parsed)) {
|
|
2502
|
+
return [{ property: "components", message: "Components must be a JSON array" }];
|
|
2503
|
+
}
|
|
2504
|
+
} catch {
|
|
2505
|
+
return [{ property: "components", message: "Invalid JSON for components" }];
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
return null;
|
|
2509
|
+
}
|
|
2510
|
+
};
|
|
2511
|
+
|
|
2349
2512
|
// src/construction-tools.ts
|
|
2350
2513
|
function textResult(text) {
|
|
2351
2514
|
return { content: [{ type: "text", text }] };
|
|
@@ -2785,7 +2948,7 @@ var constructionMcpTools = [
|
|
|
2785
2948
|
var constructionPlugin = {
|
|
2786
2949
|
id: "construction",
|
|
2787
2950
|
name: "Construction Guides",
|
|
2788
|
-
version: "0.
|
|
2951
|
+
version: "0.2.0",
|
|
2789
2952
|
tier: "free",
|
|
2790
2953
|
description: "Drawing construction guides: 3D form primitives, cross-contour lines, value/shadow studies, envelope block-ins, and form intersections.",
|
|
2791
2954
|
layerTypes: [
|
|
@@ -2793,7 +2956,8 @@ var constructionPlugin = {
|
|
|
2793
2956
|
crossContourLayerType,
|
|
2794
2957
|
valueShapesLayerType,
|
|
2795
2958
|
envelopeLayerType,
|
|
2796
|
-
intersectionLayerType
|
|
2959
|
+
intersectionLayerType,
|
|
2960
|
+
compoundFormLayerType
|
|
2797
2961
|
],
|
|
2798
2962
|
tools: [],
|
|
2799
2963
|
exportHandlers: [],
|
|
@@ -2805,10 +2969,12 @@ var constructionPlugin = {
|
|
|
2805
2969
|
};
|
|
2806
2970
|
var index_default = constructionPlugin;
|
|
2807
2971
|
export {
|
|
2972
|
+
COMPOUND_PRESETS,
|
|
2808
2973
|
approximateIntersection,
|
|
2809
2974
|
castShadow,
|
|
2810
2975
|
clamp,
|
|
2811
2976
|
comparativeMeasure,
|
|
2977
|
+
compoundFormLayerType,
|
|
2812
2978
|
computeEnvelope,
|
|
2813
2979
|
constructionMcpTools,
|
|
2814
2980
|
cross3,
|