Adds a `horizontal` fence style that builds composite-cladding panels — horizontal boards stacked between square intermediate posts — instead of the vertical pickets that slat/rail/privacy all produce today. - New `style: 'horizontal'` (core schema + inspector Style control). - Posts march along the whole span at `postSpacing`, each with a cap. - New `postCap` control (none/flat/pyramid, default pyramid); pyramid caps render as a 4-sided cone fence part. - New `slatGap` control for the board reveal — 0 collapses to one flush panel so the stacked-board seams don't read as lines. - `postCap` + `slatGap` are horizontal-only (visibleIf); postCap uses a labelled dropdown so its options aren't a context-free switch. - Lower the `postSpacing` inspector minimum from 0.2 to 0.05 m. - 2D floor plan reuses the solid-panel marker for horizontal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
51 lines
2.2 KiB
TypeScript
51 lines
2.2 KiB
TypeScript
import dedent from 'dedent'
|
|
import { z } from 'zod'
|
|
import { BaseNode, nodeType, objectId } from '../base'
|
|
import { MaterialSchema } from '../material'
|
|
|
|
export const FenceStyle = z.enum(['slat', 'rail', 'privacy', 'horizontal'])
|
|
export const FenceBaseStyle = z.enum(['floating', 'grounded'])
|
|
export const FencePostCap = z.enum(['none', 'flat', 'pyramid'])
|
|
|
|
export const FenceNode = BaseNode.extend({
|
|
id: objectId('fence'),
|
|
type: nodeType('fence'),
|
|
material: MaterialSchema.optional(),
|
|
materialPreset: z.string().optional(),
|
|
// Unified paint-slot refs (`scene:`/`library:` MaterialRef per slot id),
|
|
// matching the slot model items/slab/shelf use. Absent = declared default.
|
|
slots: z.record(z.string(), z.string()).optional(),
|
|
start: z.tuple([z.number(), z.number()]),
|
|
end: z.tuple([z.number(), z.number()]),
|
|
height: z.number().default(1.8),
|
|
thickness: z.number().default(0.08),
|
|
curveOffset: z.number().optional(),
|
|
baseHeight: z.number().default(0.22),
|
|
postSpacing: z.number().default(2),
|
|
postSize: z.number().default(0.1),
|
|
topRailHeight: z.number().default(0.04),
|
|
groundClearance: z.number().default(0),
|
|
edgeInset: z.number().default(0.015),
|
|
// Reveal between the boards of a `horizontal` fence (0 = flush cladding).
|
|
slatGap: z.number().default(0.01),
|
|
// Topper drawn on each `horizontal`-fence post.
|
|
postCap: FencePostCap.default('pyramid'),
|
|
baseStyle: FenceBaseStyle.default('grounded'),
|
|
showInfill: z.boolean().default(true),
|
|
color: z.string().default('#ffffff'),
|
|
style: FenceStyle.default('slat'),
|
|
}).describe(
|
|
dedent`
|
|
Fence node - used to represent a fence segment in the building/site level coordinate system
|
|
- start/end: fence endpoints in level coordinate system
|
|
- height/thickness: overall fence dimensions in meters
|
|
- curveOffset: midpoint sagitta offset used to bend the fence into an arc
|
|
- baseHeight/postSpacing/postSize/topRailHeight: exact geometric controls from the plan3D fence model
|
|
- groundClearance/edgeInset/baseStyle: fence support and inset configuration
|
|
- showInfill: whether to draw intermediate posts/slats between end posts
|
|
- color/style: visual appearance options
|
|
`,
|
|
)
|
|
|
|
export type FenceNode = z.infer<typeof FenceNode>
|