@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/README.md
CHANGED
|
@@ -4,6 +4,12 @@ Drawing construction guides plugin for [genart.dev](https://genart.dev) — 3D f
|
|
|
4
4
|
|
|
5
5
|
Part of [genart.dev](https://genart.dev) — a generative art platform with an MCP server, desktop app, and IDE extensions.
|
|
6
6
|
|
|
7
|
+
## Examples
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
Source file: [construction-guides.genart](test-renders/construction-guides.genart)
|
|
12
|
+
|
|
7
13
|
## Install
|
|
8
14
|
|
|
9
15
|
```bash
|
|
@@ -152,18 +158,6 @@ Intersection lines between two or more overlapping 3D forms, computed by surface
|
|
|
152
158
|
| `showFormLabels` | boolean | `false` | Show A/B/C labels |
|
|
153
159
|
| `transitionType` | select | `"hard"` | hard / soft / mixed |
|
|
154
160
|
|
|
155
|
-
## Test Render
|
|
156
|
-
|
|
157
|
-

|
|
158
|
-
|
|
159
|
-
16-panel montage showing all layer types with variations:
|
|
160
|
-
- **Row 1**: Box, cylinder, sphere, cone — default rotations with cross-contours and axes
|
|
161
|
-
- **Row 2**: Wedge, egg/ovoid, box (extreme rotation), cylinder (weak-perspective foreshortening)
|
|
162
|
-
- **Row 3**: Cross-contour lines on organic shape, 3-value study, 5-value study (full shadow anatomy), envelope block-in with angles and plumb line
|
|
163
|
-
- **Row 4**: Box+sphere intersection, cylinder+cone intersection (soft), multi-form scene, construction exercise
|
|
164
|
-
|
|
165
|
-
Regenerate with `node render-test-construction.cjs` (requires `canvas` dev dependency).
|
|
166
|
-
|
|
167
161
|
## MCP Tools (8)
|
|
168
162
|
|
|
169
163
|
| Tool | Description |
|
package/dist/index.cjs
CHANGED
|
@@ -20,10 +20,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
COMPOUND_PRESETS: () => COMPOUND_PRESETS,
|
|
23
24
|
approximateIntersection: () => approximateIntersection,
|
|
24
25
|
castShadow: () => castShadow,
|
|
25
26
|
clamp: () => clamp,
|
|
26
27
|
comparativeMeasure: () => comparativeMeasure,
|
|
28
|
+
compoundFormLayerType: () => compoundFormLayerType,
|
|
27
29
|
computeEnvelope: () => computeEnvelope,
|
|
28
30
|
constructionMcpTools: () => constructionMcpTools,
|
|
29
31
|
cross3: () => cross3,
|
|
@@ -2404,6 +2406,169 @@ var intersectionLayerType = {
|
|
|
2404
2406
|
}
|
|
2405
2407
|
};
|
|
2406
2408
|
|
|
2409
|
+
// src/compound-form-layer.ts
|
|
2410
|
+
var COMPOUND_PRESETS = {
|
|
2411
|
+
snowman: [
|
|
2412
|
+
{ type: "sphere", offsetX: 0, offsetY: 0.35, offsetZ: 0, scaleX: 1, scaleY: 1, scaleZ: 1 },
|
|
2413
|
+
{ type: "sphere", offsetX: 0, offsetY: -0.05, offsetZ: 0, scaleX: 0.75, scaleY: 0.75, scaleZ: 0.75 },
|
|
2414
|
+
{ type: "sphere", offsetX: 0, offsetY: -0.35, offsetZ: 0, scaleX: 0.5, scaleY: 0.5, scaleZ: 0.5 }
|
|
2415
|
+
],
|
|
2416
|
+
bottle: [
|
|
2417
|
+
{ type: "cylinder", offsetX: 0, offsetY: 0.15, offsetZ: 0, scaleX: 1, scaleY: 0.7, scaleZ: 1 },
|
|
2418
|
+
{ type: "cone", offsetX: 0, offsetY: -0.25, offsetZ: 0, scaleX: 0.6, scaleY: 0.3, scaleZ: 0.6 },
|
|
2419
|
+
{ type: "cylinder", offsetX: 0, offsetY: -0.42, offsetZ: 0, scaleX: 0.3, scaleY: 0.15, scaleZ: 0.3 }
|
|
2420
|
+
],
|
|
2421
|
+
mushroom: [
|
|
2422
|
+
{ type: "sphere", offsetX: 0, offsetY: -0.2, offsetZ: 0, scaleX: 1.2, scaleY: 0.5, scaleZ: 1.2 },
|
|
2423
|
+
{ type: "cylinder", offsetX: 0, offsetY: 0.2, offsetZ: 0, scaleX: 0.3, scaleY: 0.5, scaleZ: 0.3 }
|
|
2424
|
+
],
|
|
2425
|
+
lamp: [
|
|
2426
|
+
{ type: "cone", offsetX: 0, offsetY: -0.25, offsetZ: 0, scaleX: 1, scaleY: 0.5, scaleZ: 1 },
|
|
2427
|
+
{ type: "cylinder", offsetX: 0, offsetY: 0.1, offsetZ: 0, scaleX: 0.15, scaleY: 0.5, scaleZ: 0.15 },
|
|
2428
|
+
{ type: "cylinder", offsetX: 0, offsetY: 0.4, offsetZ: 0, scaleX: 0.5, scaleY: 0.05, scaleZ: 0.5 }
|
|
2429
|
+
],
|
|
2430
|
+
tree: [
|
|
2431
|
+
{ type: "cylinder", offsetX: 0, offsetY: 0.25, offsetZ: 0, scaleX: 0.2, scaleY: 0.5, scaleZ: 0.2 },
|
|
2432
|
+
{ type: "egg", offsetX: 0, offsetY: -0.2, offsetZ: 0, scaleX: 0.8, scaleY: 0.6, scaleZ: 0.8 }
|
|
2433
|
+
]
|
|
2434
|
+
};
|
|
2435
|
+
var COMPOUND_PROPERTIES = [
|
|
2436
|
+
{
|
|
2437
|
+
key: "preset",
|
|
2438
|
+
label: "Preset",
|
|
2439
|
+
type: "select",
|
|
2440
|
+
default: "snowman",
|
|
2441
|
+
options: Object.keys(COMPOUND_PRESETS).map((k) => ({ value: k, label: k.charAt(0).toUpperCase() + k.slice(1) })),
|
|
2442
|
+
group: "compound"
|
|
2443
|
+
},
|
|
2444
|
+
{
|
|
2445
|
+
key: "components",
|
|
2446
|
+
label: "Components (JSON)",
|
|
2447
|
+
type: "string",
|
|
2448
|
+
default: "",
|
|
2449
|
+
group: "compound"
|
|
2450
|
+
},
|
|
2451
|
+
{
|
|
2452
|
+
key: "position",
|
|
2453
|
+
label: "Position",
|
|
2454
|
+
type: "point",
|
|
2455
|
+
default: { x: 0.5, y: 0.5 },
|
|
2456
|
+
group: "compound"
|
|
2457
|
+
},
|
|
2458
|
+
{
|
|
2459
|
+
key: "formSize",
|
|
2460
|
+
label: "Size",
|
|
2461
|
+
type: "number",
|
|
2462
|
+
default: 0.25,
|
|
2463
|
+
min: 0.05,
|
|
2464
|
+
max: 0.6,
|
|
2465
|
+
step: 0.01,
|
|
2466
|
+
group: "compound"
|
|
2467
|
+
},
|
|
2468
|
+
{
|
|
2469
|
+
key: "rotX",
|
|
2470
|
+
label: "Rotation X (deg)",
|
|
2471
|
+
type: "number",
|
|
2472
|
+
default: 15,
|
|
2473
|
+
min: -90,
|
|
2474
|
+
max: 90,
|
|
2475
|
+
step: 5,
|
|
2476
|
+
group: "compound"
|
|
2477
|
+
},
|
|
2478
|
+
{
|
|
2479
|
+
key: "rotY",
|
|
2480
|
+
label: "Rotation Y (deg)",
|
|
2481
|
+
type: "number",
|
|
2482
|
+
default: 25,
|
|
2483
|
+
min: -180,
|
|
2484
|
+
max: 180,
|
|
2485
|
+
step: 5,
|
|
2486
|
+
group: "compound"
|
|
2487
|
+
},
|
|
2488
|
+
...COMMON_GUIDE_PROPERTIES
|
|
2489
|
+
];
|
|
2490
|
+
var compoundFormLayerType = {
|
|
2491
|
+
typeId: "construction:compound-form",
|
|
2492
|
+
displayName: "Compound Form",
|
|
2493
|
+
icon: "compound",
|
|
2494
|
+
category: "guide",
|
|
2495
|
+
properties: COMPOUND_PROPERTIES,
|
|
2496
|
+
propertyEditorId: "construction:compound-form-editor",
|
|
2497
|
+
createDefault() {
|
|
2498
|
+
const props = {};
|
|
2499
|
+
for (const schema of COMPOUND_PROPERTIES) {
|
|
2500
|
+
props[schema.key] = schema.default;
|
|
2501
|
+
}
|
|
2502
|
+
return props;
|
|
2503
|
+
},
|
|
2504
|
+
render(properties, ctx, bounds, _resources) {
|
|
2505
|
+
const preset = properties.preset ?? "snowman";
|
|
2506
|
+
const componentsJson = properties.components ?? "";
|
|
2507
|
+
const position = properties.position ?? { x: 0.5, y: 0.5 };
|
|
2508
|
+
const formSize = properties.formSize ?? 0.25;
|
|
2509
|
+
const rotX = properties.rotX ?? 15;
|
|
2510
|
+
const rotY = properties.rotY ?? 25;
|
|
2511
|
+
let components;
|
|
2512
|
+
if (componentsJson.trim()) {
|
|
2513
|
+
try {
|
|
2514
|
+
components = JSON.parse(componentsJson);
|
|
2515
|
+
} catch {
|
|
2516
|
+
components = COMPOUND_PRESETS[preset] ?? COMPOUND_PRESETS["snowman"];
|
|
2517
|
+
}
|
|
2518
|
+
} else {
|
|
2519
|
+
components = COMPOUND_PRESETS[preset] ?? COMPOUND_PRESETS["snowman"];
|
|
2520
|
+
}
|
|
2521
|
+
if (components.length === 0) return;
|
|
2522
|
+
const w = bounds.width;
|
|
2523
|
+
const h = bounds.height;
|
|
2524
|
+
const baseSize = Math.min(w, h) * formSize;
|
|
2525
|
+
const cx = bounds.x + w * position.x;
|
|
2526
|
+
const cy = bounds.y + h * position.y;
|
|
2527
|
+
ctx.save();
|
|
2528
|
+
const guideColor = properties.guideColor ?? "rgba(0,200,255,0.5)";
|
|
2529
|
+
const lineWidth = properties.lineWidth ?? 1;
|
|
2530
|
+
const dashPattern = properties.dashPattern ?? "";
|
|
2531
|
+
setupGuideStyle(ctx, guideColor, lineWidth, dashPattern);
|
|
2532
|
+
const mat = rotationMatrix(rotX, rotY, 0);
|
|
2533
|
+
const sorted = components.map((comp, i) => {
|
|
2534
|
+
const p3d = rotate3D({ x: comp.offsetX * baseSize, y: comp.offsetY * baseSize, z: comp.offsetZ * baseSize }, mat);
|
|
2535
|
+
return { comp, z: p3d.z, index: i };
|
|
2536
|
+
}).sort((a, b) => b.z - a.z);
|
|
2537
|
+
for (const { comp } of sorted) {
|
|
2538
|
+
const offset = rotate3D(
|
|
2539
|
+
{ x: comp.offsetX * baseSize, y: comp.offsetY * baseSize, z: comp.offsetZ * baseSize },
|
|
2540
|
+
mat
|
|
2541
|
+
);
|
|
2542
|
+
const compCx = cx + offset.x;
|
|
2543
|
+
const compCy = cy + offset.y;
|
|
2544
|
+
const compSize = baseSize * Math.max(comp.scaleX, comp.scaleY);
|
|
2545
|
+
ctx.beginPath();
|
|
2546
|
+
const rx = compSize * comp.scaleX * 0.5;
|
|
2547
|
+
const ry = compSize * comp.scaleY * 0.5;
|
|
2548
|
+
ctx.ellipse(compCx, compCy, Math.max(1, rx), Math.max(1, ry), 0, 0, Math.PI * 2);
|
|
2549
|
+
ctx.stroke();
|
|
2550
|
+
ctx.beginPath();
|
|
2551
|
+
ctx.arc(compCx, compCy, 2, 0, Math.PI * 2);
|
|
2552
|
+
ctx.fill();
|
|
2553
|
+
}
|
|
2554
|
+
ctx.restore();
|
|
2555
|
+
},
|
|
2556
|
+
validate(properties) {
|
|
2557
|
+
const componentsJson = properties.components;
|
|
2558
|
+
if (componentsJson && componentsJson.trim()) {
|
|
2559
|
+
try {
|
|
2560
|
+
const parsed = JSON.parse(componentsJson);
|
|
2561
|
+
if (!Array.isArray(parsed)) {
|
|
2562
|
+
return [{ property: "components", message: "Components must be a JSON array" }];
|
|
2563
|
+
}
|
|
2564
|
+
} catch {
|
|
2565
|
+
return [{ property: "components", message: "Invalid JSON for components" }];
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
return null;
|
|
2569
|
+
}
|
|
2570
|
+
};
|
|
2571
|
+
|
|
2407
2572
|
// src/construction-tools.ts
|
|
2408
2573
|
function textResult(text) {
|
|
2409
2574
|
return { content: [{ type: "text", text }] };
|
|
@@ -2843,7 +3008,7 @@ var constructionMcpTools = [
|
|
|
2843
3008
|
var constructionPlugin = {
|
|
2844
3009
|
id: "construction",
|
|
2845
3010
|
name: "Construction Guides",
|
|
2846
|
-
version: "0.
|
|
3011
|
+
version: "0.2.0",
|
|
2847
3012
|
tier: "free",
|
|
2848
3013
|
description: "Drawing construction guides: 3D form primitives, cross-contour lines, value/shadow studies, envelope block-ins, and form intersections.",
|
|
2849
3014
|
layerTypes: [
|
|
@@ -2851,7 +3016,8 @@ var constructionPlugin = {
|
|
|
2851
3016
|
crossContourLayerType,
|
|
2852
3017
|
valueShapesLayerType,
|
|
2853
3018
|
envelopeLayerType,
|
|
2854
|
-
intersectionLayerType
|
|
3019
|
+
intersectionLayerType,
|
|
3020
|
+
compoundFormLayerType
|
|
2855
3021
|
],
|
|
2856
3022
|
tools: [],
|
|
2857
3023
|
exportHandlers: [],
|
|
@@ -2864,10 +3030,12 @@ var constructionPlugin = {
|
|
|
2864
3030
|
var index_default = constructionPlugin;
|
|
2865
3031
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2866
3032
|
0 && (module.exports = {
|
|
3033
|
+
COMPOUND_PRESETS,
|
|
2867
3034
|
approximateIntersection,
|
|
2868
3035
|
castShadow,
|
|
2869
3036
|
clamp,
|
|
2870
3037
|
comparativeMeasure,
|
|
3038
|
+
compoundFormLayerType,
|
|
2871
3039
|
computeEnvelope,
|
|
2872
3040
|
constructionMcpTools,
|
|
2873
3041
|
cross3,
|