Add procedural support column variants
This commit is contained in:
@@ -39,6 +39,7 @@ export {
|
|||||||
ColumnShaftDetail,
|
ColumnShaftDetail,
|
||||||
ColumnShaftProfile,
|
ColumnShaftProfile,
|
||||||
ColumnStyle,
|
ColumnStyle,
|
||||||
|
ColumnSupportStyle,
|
||||||
} from './nodes/column'
|
} from './nodes/column'
|
||||||
export { DoorNode, DoorSegment } from './nodes/door'
|
export { DoorNode, DoorSegment } from './nodes/door'
|
||||||
export { FenceBaseStyle, FenceNode, FenceStyle } from './nodes/fence'
|
export { FenceBaseStyle, FenceNode, FenceStyle } from './nodes/fence'
|
||||||
|
|||||||
@@ -56,6 +56,20 @@ export const ColumnRingPlacement = z.enum(['ends', 'even', 'top', 'bottom'])
|
|||||||
|
|
||||||
export const ColumnCarvingPlacement = z.enum(['shaft', 'base', 'capital', 'all'])
|
export const ColumnCarvingPlacement = z.enum(['shaft', 'base', 'capital', 'all'])
|
||||||
|
|
||||||
|
export const ColumnSupportStyle = z.enum([
|
||||||
|
'vertical',
|
||||||
|
'a-frame',
|
||||||
|
'y-frame',
|
||||||
|
'v-frame',
|
||||||
|
'x-brace',
|
||||||
|
'k-brace',
|
||||||
|
'single-strut',
|
||||||
|
'tripod',
|
||||||
|
'trestle',
|
||||||
|
'portal-frame',
|
||||||
|
'box-frame',
|
||||||
|
])
|
||||||
|
|
||||||
export type ColumnStyle = z.infer<typeof ColumnStyle>
|
export type ColumnStyle = z.infer<typeof ColumnStyle>
|
||||||
export type ColumnCrossSection = z.infer<typeof ColumnCrossSection>
|
export type ColumnCrossSection = z.infer<typeof ColumnCrossSection>
|
||||||
export type ColumnShaftProfile = z.infer<typeof ColumnShaftProfile>
|
export type ColumnShaftProfile = z.infer<typeof ColumnShaftProfile>
|
||||||
@@ -65,6 +79,7 @@ export type ColumnBaseStyle = z.infer<typeof ColumnBaseStyle>
|
|||||||
export type ColumnCapitalStyle = z.infer<typeof ColumnCapitalStyle>
|
export type ColumnCapitalStyle = z.infer<typeof ColumnCapitalStyle>
|
||||||
export type ColumnRingPlacement = z.infer<typeof ColumnRingPlacement>
|
export type ColumnRingPlacement = z.infer<typeof ColumnRingPlacement>
|
||||||
export type ColumnCarvingPlacement = z.infer<typeof ColumnCarvingPlacement>
|
export type ColumnCarvingPlacement = z.infer<typeof ColumnCarvingPlacement>
|
||||||
|
export type ColumnSupportStyle = z.infer<typeof ColumnSupportStyle>
|
||||||
|
|
||||||
export const ColumnNode = BaseNode.extend({
|
export const ColumnNode = BaseNode.extend({
|
||||||
id: objectId('column'),
|
id: objectId('column'),
|
||||||
@@ -136,6 +151,12 @@ export const ColumnNode = BaseNode.extend({
|
|||||||
lowerBandCarvingLevel: z.number().int().min(0).max(4).default(0),
|
lowerBandCarvingLevel: z.number().int().min(0).max(4).default(0),
|
||||||
dentilCount: z.number().int().min(0).max(48).default(0),
|
dentilCount: z.number().int().min(0).max(48).default(0),
|
||||||
beadCount: z.number().int().min(0).max(64).default(0),
|
beadCount: z.number().int().min(0).max(64).default(0),
|
||||||
|
supportStyle: ColumnSupportStyle.default('vertical'),
|
||||||
|
braceWidth: z.number().positive().default(0.16),
|
||||||
|
braceDepth: z.number().positive().default(0.16),
|
||||||
|
braceBottomSpread: z.number().min(0.2).default(1.2),
|
||||||
|
braceTopSpread: z.number().min(0).default(0.12),
|
||||||
|
bracePlateEnabled: z.boolean().default(true),
|
||||||
material: MaterialSchema.optional(),
|
material: MaterialSchema.optional(),
|
||||||
materialPreset: z.string().optional(),
|
materialPreset: z.string().optional(),
|
||||||
}).describe(dedent`
|
}).describe(dedent`
|
||||||
@@ -150,6 +171,7 @@ export const ColumnNode = BaseNode.extend({
|
|||||||
- baseStyle/capitalStyle: procedural base and top treatment with tier/detail controls
|
- baseStyle/capitalStyle: procedural base and top treatment with tier/detail controls
|
||||||
- baseHeight/capitalHeight: bottom and top block proportions
|
- baseHeight/capitalHeight: bottom and top block proportions
|
||||||
- ring/flute/spiral/panel/lathe/carving fields: procedural detail controls
|
- ring/flute/spiral/panel/lathe/carving fields: procedural detail controls
|
||||||
|
- supportStyle/brace fields: vertical column or procedural support assembly
|
||||||
`)
|
`)
|
||||||
|
|
||||||
export const COLUMN_PRESETS = {
|
export const COLUMN_PRESETS = {
|
||||||
@@ -403,6 +425,566 @@ export const COLUMN_PRESETS = {
|
|||||||
dentilCount: 0,
|
dentilCount: 0,
|
||||||
beadCount: 0,
|
beadCount: 0,
|
||||||
},
|
},
|
||||||
|
aFrameSupport: {
|
||||||
|
label: 'A-Frame Support',
|
||||||
|
supportStyle: 'a-frame',
|
||||||
|
style: 'faceted',
|
||||||
|
crossSection: 'rectangular',
|
||||||
|
height: 2.6,
|
||||||
|
radius: 0.08,
|
||||||
|
width: 0.16,
|
||||||
|
depth: 0.16,
|
||||||
|
edgeSoftness: 0.012,
|
||||||
|
baseHeight: 0,
|
||||||
|
capitalHeight: 0,
|
||||||
|
shaftProfile: 'straight',
|
||||||
|
shaftTaper: 0,
|
||||||
|
shaftBulge: 0,
|
||||||
|
shaftStartScale: 1,
|
||||||
|
shaftEndScale: 1,
|
||||||
|
shaftSegmentCount: 1,
|
||||||
|
shaftTwistStep: 0,
|
||||||
|
shaftCornerRadius: 0.012,
|
||||||
|
shaftDetail: 'none',
|
||||||
|
baseStyle: 'none',
|
||||||
|
baseWidthScale: 1,
|
||||||
|
baseDepthScale: 1,
|
||||||
|
baseTierCount: 1,
|
||||||
|
baseStepSpread: 0.34,
|
||||||
|
basePlinthHeightRatio: 0.44,
|
||||||
|
baseRoundBandScale: 0.92,
|
||||||
|
baseNeckScale: 0.72,
|
||||||
|
baseRoundBandCount: 0,
|
||||||
|
baseRibCount: 0,
|
||||||
|
baseCarvingLevel: 0,
|
||||||
|
capitalStyle: 'none',
|
||||||
|
capitalWidthScale: 1,
|
||||||
|
capitalDepthScale: 1,
|
||||||
|
capitalTierCount: 1,
|
||||||
|
capitalStepSpread: 0.34,
|
||||||
|
capitalBandCount: 0,
|
||||||
|
capitalCarvingLevel: 0,
|
||||||
|
ringCount: 0,
|
||||||
|
ringSpread: 0.16,
|
||||||
|
fluteCount: 0,
|
||||||
|
spiralTwist: 0,
|
||||||
|
spiralRibCount: 0,
|
||||||
|
panelCount: 0,
|
||||||
|
latheRingCount: 0,
|
||||||
|
carvingLevel: 0,
|
||||||
|
lowerBandEnabled: false,
|
||||||
|
dentilCount: 0,
|
||||||
|
beadCount: 0,
|
||||||
|
braceWidth: 0.16,
|
||||||
|
braceDepth: 0.16,
|
||||||
|
braceBottomSpread: 1.2,
|
||||||
|
braceTopSpread: 0.06,
|
||||||
|
bracePlateEnabled: false,
|
||||||
|
},
|
||||||
|
yFrameSupport: {
|
||||||
|
label: 'Y Support',
|
||||||
|
supportStyle: 'y-frame',
|
||||||
|
style: 'faceted',
|
||||||
|
crossSection: 'rectangular',
|
||||||
|
height: 2.7,
|
||||||
|
radius: 0.08,
|
||||||
|
width: 0.16,
|
||||||
|
depth: 0.16,
|
||||||
|
edgeSoftness: 0.012,
|
||||||
|
baseHeight: 0,
|
||||||
|
capitalHeight: 0,
|
||||||
|
shaftProfile: 'straight',
|
||||||
|
shaftTaper: 0,
|
||||||
|
shaftBulge: 0,
|
||||||
|
shaftStartScale: 1,
|
||||||
|
shaftEndScale: 1,
|
||||||
|
shaftSegmentCount: 1,
|
||||||
|
shaftTwistStep: 0,
|
||||||
|
shaftCornerRadius: 0.012,
|
||||||
|
shaftDetail: 'none',
|
||||||
|
baseStyle: 'none',
|
||||||
|
baseWidthScale: 1,
|
||||||
|
baseDepthScale: 1,
|
||||||
|
baseTierCount: 1,
|
||||||
|
baseStepSpread: 0.34,
|
||||||
|
basePlinthHeightRatio: 0.44,
|
||||||
|
baseRoundBandScale: 0.92,
|
||||||
|
baseNeckScale: 0.72,
|
||||||
|
baseRoundBandCount: 0,
|
||||||
|
baseRibCount: 0,
|
||||||
|
baseCarvingLevel: 0,
|
||||||
|
capitalStyle: 'none',
|
||||||
|
capitalWidthScale: 1,
|
||||||
|
capitalDepthScale: 1,
|
||||||
|
capitalTierCount: 1,
|
||||||
|
capitalStepSpread: 0.34,
|
||||||
|
capitalBandCount: 0,
|
||||||
|
capitalCarvingLevel: 0,
|
||||||
|
ringCount: 0,
|
||||||
|
ringSpread: 0.16,
|
||||||
|
fluteCount: 0,
|
||||||
|
spiralTwist: 0,
|
||||||
|
spiralRibCount: 0,
|
||||||
|
panelCount: 0,
|
||||||
|
latheRingCount: 0,
|
||||||
|
carvingLevel: 0,
|
||||||
|
lowerBandEnabled: false,
|
||||||
|
dentilCount: 0,
|
||||||
|
beadCount: 0,
|
||||||
|
braceWidth: 0.16,
|
||||||
|
braceDepth: 0.16,
|
||||||
|
braceBottomSpread: 0.2,
|
||||||
|
braceTopSpread: 1,
|
||||||
|
bracePlateEnabled: false,
|
||||||
|
},
|
||||||
|
vFrameSupport: {
|
||||||
|
label: 'V Support',
|
||||||
|
supportStyle: 'v-frame',
|
||||||
|
style: 'faceted',
|
||||||
|
crossSection: 'rectangular',
|
||||||
|
height: 2.6,
|
||||||
|
radius: 0.08,
|
||||||
|
width: 0.16,
|
||||||
|
depth: 0.16,
|
||||||
|
edgeSoftness: 0.012,
|
||||||
|
baseHeight: 0,
|
||||||
|
capitalHeight: 0,
|
||||||
|
shaftProfile: 'straight',
|
||||||
|
shaftTaper: 0,
|
||||||
|
shaftBulge: 0,
|
||||||
|
shaftStartScale: 1,
|
||||||
|
shaftEndScale: 1,
|
||||||
|
shaftSegmentCount: 1,
|
||||||
|
shaftTwistStep: 0,
|
||||||
|
shaftCornerRadius: 0.012,
|
||||||
|
shaftDetail: 'none',
|
||||||
|
baseStyle: 'none',
|
||||||
|
baseWidthScale: 1,
|
||||||
|
baseDepthScale: 1,
|
||||||
|
baseTierCount: 1,
|
||||||
|
baseStepSpread: 0.34,
|
||||||
|
basePlinthHeightRatio: 0.44,
|
||||||
|
baseRoundBandScale: 0.92,
|
||||||
|
baseNeckScale: 0.72,
|
||||||
|
baseRoundBandCount: 0,
|
||||||
|
baseRibCount: 0,
|
||||||
|
baseCarvingLevel: 0,
|
||||||
|
capitalStyle: 'none',
|
||||||
|
capitalWidthScale: 1,
|
||||||
|
capitalDepthScale: 1,
|
||||||
|
capitalTierCount: 1,
|
||||||
|
capitalStepSpread: 0.34,
|
||||||
|
capitalBandCount: 0,
|
||||||
|
capitalCarvingLevel: 0,
|
||||||
|
ringCount: 0,
|
||||||
|
ringSpread: 0.16,
|
||||||
|
fluteCount: 0,
|
||||||
|
spiralTwist: 0,
|
||||||
|
spiralRibCount: 0,
|
||||||
|
panelCount: 0,
|
||||||
|
latheRingCount: 0,
|
||||||
|
carvingLevel: 0,
|
||||||
|
lowerBandEnabled: false,
|
||||||
|
dentilCount: 0,
|
||||||
|
beadCount: 0,
|
||||||
|
braceWidth: 0.16,
|
||||||
|
braceDepth: 0.16,
|
||||||
|
braceBottomSpread: 0.2,
|
||||||
|
braceTopSpread: 1,
|
||||||
|
bracePlateEnabled: false,
|
||||||
|
},
|
||||||
|
xBraceSupport: {
|
||||||
|
label: 'X Brace',
|
||||||
|
supportStyle: 'x-brace',
|
||||||
|
style: 'faceted',
|
||||||
|
crossSection: 'rectangular',
|
||||||
|
height: 2.6,
|
||||||
|
radius: 0.07,
|
||||||
|
width: 0.14,
|
||||||
|
depth: 0.14,
|
||||||
|
edgeSoftness: 0.01,
|
||||||
|
baseHeight: 0,
|
||||||
|
capitalHeight: 0,
|
||||||
|
shaftProfile: 'straight',
|
||||||
|
shaftTaper: 0,
|
||||||
|
shaftBulge: 0,
|
||||||
|
shaftStartScale: 1,
|
||||||
|
shaftEndScale: 1,
|
||||||
|
shaftSegmentCount: 1,
|
||||||
|
shaftTwistStep: 0,
|
||||||
|
shaftCornerRadius: 0.01,
|
||||||
|
shaftDetail: 'none',
|
||||||
|
baseStyle: 'none',
|
||||||
|
baseWidthScale: 1,
|
||||||
|
baseDepthScale: 1,
|
||||||
|
baseTierCount: 1,
|
||||||
|
baseStepSpread: 0.34,
|
||||||
|
basePlinthHeightRatio: 0.44,
|
||||||
|
baseRoundBandScale: 0.92,
|
||||||
|
baseNeckScale: 0.72,
|
||||||
|
baseRoundBandCount: 0,
|
||||||
|
baseRibCount: 0,
|
||||||
|
baseCarvingLevel: 0,
|
||||||
|
capitalStyle: 'none',
|
||||||
|
capitalWidthScale: 1,
|
||||||
|
capitalDepthScale: 1,
|
||||||
|
capitalTierCount: 1,
|
||||||
|
capitalStepSpread: 0.34,
|
||||||
|
capitalBandCount: 0,
|
||||||
|
capitalCarvingLevel: 0,
|
||||||
|
ringCount: 0,
|
||||||
|
ringSpread: 0.16,
|
||||||
|
fluteCount: 0,
|
||||||
|
spiralTwist: 0,
|
||||||
|
spiralRibCount: 0,
|
||||||
|
panelCount: 0,
|
||||||
|
latheRingCount: 0,
|
||||||
|
carvingLevel: 0,
|
||||||
|
lowerBandEnabled: false,
|
||||||
|
dentilCount: 0,
|
||||||
|
beadCount: 0,
|
||||||
|
braceWidth: 0.14,
|
||||||
|
braceDepth: 0.14,
|
||||||
|
braceBottomSpread: 1,
|
||||||
|
braceTopSpread: 1,
|
||||||
|
bracePlateEnabled: false,
|
||||||
|
},
|
||||||
|
kBraceSupport: {
|
||||||
|
label: 'K Brace',
|
||||||
|
supportStyle: 'k-brace',
|
||||||
|
style: 'faceted',
|
||||||
|
crossSection: 'rectangular',
|
||||||
|
height: 2.6,
|
||||||
|
radius: 0.07,
|
||||||
|
width: 0.14,
|
||||||
|
depth: 0.14,
|
||||||
|
edgeSoftness: 0.01,
|
||||||
|
baseHeight: 0,
|
||||||
|
capitalHeight: 0,
|
||||||
|
shaftProfile: 'straight',
|
||||||
|
shaftTaper: 0,
|
||||||
|
shaftBulge: 0,
|
||||||
|
shaftStartScale: 1,
|
||||||
|
shaftEndScale: 1,
|
||||||
|
shaftSegmentCount: 1,
|
||||||
|
shaftTwistStep: 0,
|
||||||
|
shaftCornerRadius: 0.01,
|
||||||
|
shaftDetail: 'none',
|
||||||
|
baseStyle: 'none',
|
||||||
|
baseWidthScale: 1,
|
||||||
|
baseDepthScale: 1,
|
||||||
|
baseTierCount: 1,
|
||||||
|
baseStepSpread: 0.34,
|
||||||
|
basePlinthHeightRatio: 0.44,
|
||||||
|
baseRoundBandScale: 0.92,
|
||||||
|
baseNeckScale: 0.72,
|
||||||
|
baseRoundBandCount: 0,
|
||||||
|
baseRibCount: 0,
|
||||||
|
baseCarvingLevel: 0,
|
||||||
|
capitalStyle: 'none',
|
||||||
|
capitalWidthScale: 1,
|
||||||
|
capitalDepthScale: 1,
|
||||||
|
capitalTierCount: 1,
|
||||||
|
capitalStepSpread: 0.34,
|
||||||
|
capitalBandCount: 0,
|
||||||
|
capitalCarvingLevel: 0,
|
||||||
|
ringCount: 0,
|
||||||
|
ringSpread: 0.16,
|
||||||
|
fluteCount: 0,
|
||||||
|
spiralTwist: 0,
|
||||||
|
spiralRibCount: 0,
|
||||||
|
panelCount: 0,
|
||||||
|
latheRingCount: 0,
|
||||||
|
carvingLevel: 0,
|
||||||
|
lowerBandEnabled: false,
|
||||||
|
dentilCount: 0,
|
||||||
|
beadCount: 0,
|
||||||
|
braceWidth: 0.14,
|
||||||
|
braceDepth: 0.14,
|
||||||
|
braceBottomSpread: 1,
|
||||||
|
braceTopSpread: 1,
|
||||||
|
bracePlateEnabled: false,
|
||||||
|
},
|
||||||
|
singleStrutSupport: {
|
||||||
|
label: 'Single Strut',
|
||||||
|
supportStyle: 'single-strut',
|
||||||
|
style: 'faceted',
|
||||||
|
crossSection: 'rectangular',
|
||||||
|
height: 2.6,
|
||||||
|
radius: 0.07,
|
||||||
|
width: 0.14,
|
||||||
|
depth: 0.14,
|
||||||
|
edgeSoftness: 0.01,
|
||||||
|
baseHeight: 0,
|
||||||
|
capitalHeight: 0,
|
||||||
|
shaftProfile: 'straight',
|
||||||
|
shaftTaper: 0,
|
||||||
|
shaftBulge: 0,
|
||||||
|
shaftStartScale: 1,
|
||||||
|
shaftEndScale: 1,
|
||||||
|
shaftSegmentCount: 1,
|
||||||
|
shaftTwistStep: 0,
|
||||||
|
shaftCornerRadius: 0.01,
|
||||||
|
shaftDetail: 'none',
|
||||||
|
baseStyle: 'none',
|
||||||
|
baseWidthScale: 1,
|
||||||
|
baseDepthScale: 1,
|
||||||
|
baseTierCount: 1,
|
||||||
|
baseStepSpread: 0.34,
|
||||||
|
basePlinthHeightRatio: 0.44,
|
||||||
|
baseRoundBandScale: 0.92,
|
||||||
|
baseNeckScale: 0.72,
|
||||||
|
baseRoundBandCount: 0,
|
||||||
|
baseRibCount: 0,
|
||||||
|
baseCarvingLevel: 0,
|
||||||
|
capitalStyle: 'none',
|
||||||
|
capitalWidthScale: 1,
|
||||||
|
capitalDepthScale: 1,
|
||||||
|
capitalTierCount: 1,
|
||||||
|
capitalStepSpread: 0.34,
|
||||||
|
capitalBandCount: 0,
|
||||||
|
capitalCarvingLevel: 0,
|
||||||
|
ringCount: 0,
|
||||||
|
ringSpread: 0.16,
|
||||||
|
fluteCount: 0,
|
||||||
|
spiralTwist: 0,
|
||||||
|
spiralRibCount: 0,
|
||||||
|
panelCount: 0,
|
||||||
|
latheRingCount: 0,
|
||||||
|
carvingLevel: 0,
|
||||||
|
lowerBandEnabled: false,
|
||||||
|
dentilCount: 0,
|
||||||
|
beadCount: 0,
|
||||||
|
braceWidth: 0.14,
|
||||||
|
braceDepth: 0.14,
|
||||||
|
braceBottomSpread: 1,
|
||||||
|
braceTopSpread: 1,
|
||||||
|
bracePlateEnabled: false,
|
||||||
|
},
|
||||||
|
tripodSupport: {
|
||||||
|
label: 'Tripod Support',
|
||||||
|
supportStyle: 'tripod',
|
||||||
|
style: 'faceted',
|
||||||
|
crossSection: 'rectangular',
|
||||||
|
height: 2.6,
|
||||||
|
radius: 0.07,
|
||||||
|
width: 0.14,
|
||||||
|
depth: 0.14,
|
||||||
|
edgeSoftness: 0.01,
|
||||||
|
baseHeight: 0,
|
||||||
|
capitalHeight: 0,
|
||||||
|
shaftProfile: 'straight',
|
||||||
|
shaftTaper: 0,
|
||||||
|
shaftBulge: 0,
|
||||||
|
shaftStartScale: 1,
|
||||||
|
shaftEndScale: 1,
|
||||||
|
shaftSegmentCount: 1,
|
||||||
|
shaftTwistStep: 0,
|
||||||
|
shaftCornerRadius: 0.01,
|
||||||
|
shaftDetail: 'none',
|
||||||
|
baseStyle: 'none',
|
||||||
|
baseWidthScale: 1,
|
||||||
|
baseDepthScale: 1,
|
||||||
|
baseTierCount: 1,
|
||||||
|
baseStepSpread: 0.34,
|
||||||
|
basePlinthHeightRatio: 0.44,
|
||||||
|
baseRoundBandScale: 0.92,
|
||||||
|
baseNeckScale: 0.72,
|
||||||
|
baseRoundBandCount: 0,
|
||||||
|
baseRibCount: 0,
|
||||||
|
baseCarvingLevel: 0,
|
||||||
|
capitalStyle: 'none',
|
||||||
|
capitalWidthScale: 1,
|
||||||
|
capitalDepthScale: 1,
|
||||||
|
capitalTierCount: 1,
|
||||||
|
capitalStepSpread: 0.34,
|
||||||
|
capitalBandCount: 0,
|
||||||
|
capitalCarvingLevel: 0,
|
||||||
|
ringCount: 0,
|
||||||
|
ringSpread: 0.16,
|
||||||
|
fluteCount: 0,
|
||||||
|
spiralTwist: 0,
|
||||||
|
spiralRibCount: 0,
|
||||||
|
panelCount: 0,
|
||||||
|
latheRingCount: 0,
|
||||||
|
carvingLevel: 0,
|
||||||
|
lowerBandEnabled: false,
|
||||||
|
dentilCount: 0,
|
||||||
|
beadCount: 0,
|
||||||
|
braceWidth: 0.14,
|
||||||
|
braceDepth: 0.14,
|
||||||
|
braceBottomSpread: 1.1,
|
||||||
|
braceTopSpread: 1.1,
|
||||||
|
bracePlateEnabled: false,
|
||||||
|
},
|
||||||
|
trestleSupport: {
|
||||||
|
label: 'Trestle Frame',
|
||||||
|
supportStyle: 'trestle',
|
||||||
|
style: 'faceted',
|
||||||
|
crossSection: 'rectangular',
|
||||||
|
height: 2.6,
|
||||||
|
radius: 0.07,
|
||||||
|
width: 0.14,
|
||||||
|
depth: 0.14,
|
||||||
|
edgeSoftness: 0.01,
|
||||||
|
baseHeight: 0,
|
||||||
|
capitalHeight: 0,
|
||||||
|
shaftProfile: 'straight',
|
||||||
|
shaftTaper: 0,
|
||||||
|
shaftBulge: 0,
|
||||||
|
shaftStartScale: 1,
|
||||||
|
shaftEndScale: 1,
|
||||||
|
shaftSegmentCount: 1,
|
||||||
|
shaftTwistStep: 0,
|
||||||
|
shaftCornerRadius: 0.01,
|
||||||
|
shaftDetail: 'none',
|
||||||
|
baseStyle: 'none',
|
||||||
|
baseWidthScale: 1,
|
||||||
|
baseDepthScale: 1,
|
||||||
|
baseTierCount: 1,
|
||||||
|
baseStepSpread: 0.34,
|
||||||
|
basePlinthHeightRatio: 0.44,
|
||||||
|
baseRoundBandScale: 0.92,
|
||||||
|
baseNeckScale: 0.72,
|
||||||
|
baseRoundBandCount: 0,
|
||||||
|
baseRibCount: 0,
|
||||||
|
baseCarvingLevel: 0,
|
||||||
|
capitalStyle: 'none',
|
||||||
|
capitalWidthScale: 1,
|
||||||
|
capitalDepthScale: 1,
|
||||||
|
capitalTierCount: 1,
|
||||||
|
capitalStepSpread: 0.34,
|
||||||
|
capitalBandCount: 0,
|
||||||
|
capitalCarvingLevel: 0,
|
||||||
|
ringCount: 0,
|
||||||
|
ringSpread: 0.16,
|
||||||
|
fluteCount: 0,
|
||||||
|
spiralTwist: 0,
|
||||||
|
spiralRibCount: 0,
|
||||||
|
panelCount: 0,
|
||||||
|
latheRingCount: 0,
|
||||||
|
carvingLevel: 0,
|
||||||
|
lowerBandEnabled: false,
|
||||||
|
dentilCount: 0,
|
||||||
|
beadCount: 0,
|
||||||
|
braceWidth: 0.14,
|
||||||
|
braceDepth: 0.14,
|
||||||
|
braceBottomSpread: 1.2,
|
||||||
|
braceTopSpread: 1,
|
||||||
|
bracePlateEnabled: false,
|
||||||
|
},
|
||||||
|
portalFrameSupport: {
|
||||||
|
label: 'Portal Frame',
|
||||||
|
supportStyle: 'portal-frame',
|
||||||
|
style: 'faceted',
|
||||||
|
crossSection: 'rectangular',
|
||||||
|
height: 2.6,
|
||||||
|
radius: 0.08,
|
||||||
|
width: 0.16,
|
||||||
|
depth: 0.16,
|
||||||
|
edgeSoftness: 0.012,
|
||||||
|
baseHeight: 0,
|
||||||
|
capitalHeight: 0,
|
||||||
|
shaftProfile: 'straight',
|
||||||
|
shaftTaper: 0,
|
||||||
|
shaftBulge: 0,
|
||||||
|
shaftStartScale: 1,
|
||||||
|
shaftEndScale: 1,
|
||||||
|
shaftSegmentCount: 1,
|
||||||
|
shaftTwistStep: 0,
|
||||||
|
shaftCornerRadius: 0.012,
|
||||||
|
shaftDetail: 'none',
|
||||||
|
baseStyle: 'none',
|
||||||
|
baseWidthScale: 1,
|
||||||
|
baseDepthScale: 1,
|
||||||
|
baseTierCount: 1,
|
||||||
|
baseStepSpread: 0.34,
|
||||||
|
basePlinthHeightRatio: 0.44,
|
||||||
|
baseRoundBandScale: 0.92,
|
||||||
|
baseNeckScale: 0.72,
|
||||||
|
baseRoundBandCount: 0,
|
||||||
|
baseRibCount: 0,
|
||||||
|
baseCarvingLevel: 0,
|
||||||
|
capitalStyle: 'none',
|
||||||
|
capitalWidthScale: 1,
|
||||||
|
capitalDepthScale: 1,
|
||||||
|
capitalTierCount: 1,
|
||||||
|
capitalStepSpread: 0.34,
|
||||||
|
capitalBandCount: 0,
|
||||||
|
capitalCarvingLevel: 0,
|
||||||
|
ringCount: 0,
|
||||||
|
ringSpread: 0.16,
|
||||||
|
fluteCount: 0,
|
||||||
|
spiralTwist: 0,
|
||||||
|
spiralRibCount: 0,
|
||||||
|
panelCount: 0,
|
||||||
|
latheRingCount: 0,
|
||||||
|
carvingLevel: 0,
|
||||||
|
lowerBandEnabled: false,
|
||||||
|
dentilCount: 0,
|
||||||
|
beadCount: 0,
|
||||||
|
braceWidth: 0.16,
|
||||||
|
braceDepth: 0.16,
|
||||||
|
braceBottomSpread: 1.4,
|
||||||
|
braceTopSpread: 0.2,
|
||||||
|
bracePlateEnabled: false,
|
||||||
|
},
|
||||||
|
boxFrameSupport: {
|
||||||
|
label: 'Box Frame',
|
||||||
|
supportStyle: 'box-frame',
|
||||||
|
style: 'faceted',
|
||||||
|
crossSection: 'rectangular',
|
||||||
|
height: 2.6,
|
||||||
|
radius: 0.07,
|
||||||
|
width: 0.14,
|
||||||
|
depth: 0.14,
|
||||||
|
edgeSoftness: 0.01,
|
||||||
|
baseHeight: 0,
|
||||||
|
capitalHeight: 0,
|
||||||
|
shaftProfile: 'straight',
|
||||||
|
shaftTaper: 0,
|
||||||
|
shaftBulge: 0,
|
||||||
|
shaftStartScale: 1,
|
||||||
|
shaftEndScale: 1,
|
||||||
|
shaftSegmentCount: 1,
|
||||||
|
shaftTwistStep: 0,
|
||||||
|
shaftCornerRadius: 0.01,
|
||||||
|
shaftDetail: 'none',
|
||||||
|
baseStyle: 'none',
|
||||||
|
baseWidthScale: 1,
|
||||||
|
baseDepthScale: 1,
|
||||||
|
baseTierCount: 1,
|
||||||
|
baseStepSpread: 0.34,
|
||||||
|
basePlinthHeightRatio: 0.44,
|
||||||
|
baseRoundBandScale: 0.92,
|
||||||
|
baseNeckScale: 0.72,
|
||||||
|
baseRoundBandCount: 0,
|
||||||
|
baseRibCount: 0,
|
||||||
|
baseCarvingLevel: 0,
|
||||||
|
capitalStyle: 'none',
|
||||||
|
capitalWidthScale: 1,
|
||||||
|
capitalDepthScale: 1,
|
||||||
|
capitalTierCount: 1,
|
||||||
|
capitalStepSpread: 0.34,
|
||||||
|
capitalBandCount: 0,
|
||||||
|
capitalCarvingLevel: 0,
|
||||||
|
ringCount: 0,
|
||||||
|
ringSpread: 0.16,
|
||||||
|
fluteCount: 0,
|
||||||
|
spiralTwist: 0,
|
||||||
|
spiralRibCount: 0,
|
||||||
|
panelCount: 0,
|
||||||
|
latheRingCount: 0,
|
||||||
|
carvingLevel: 0,
|
||||||
|
lowerBandEnabled: false,
|
||||||
|
dentilCount: 0,
|
||||||
|
beadCount: 0,
|
||||||
|
braceWidth: 0.14,
|
||||||
|
braceDepth: 0.14,
|
||||||
|
braceBottomSpread: 1.4,
|
||||||
|
braceTopSpread: 1,
|
||||||
|
bracePlateEnabled: false,
|
||||||
|
},
|
||||||
} as const satisfies Record<string, { label: string } & Partial<z.input<typeof ColumnNode>>>
|
} as const satisfies Record<string, { label: string } & Partial<z.input<typeof ColumnNode>>>
|
||||||
|
|
||||||
export type ColumnPresetId = keyof typeof COLUMN_PRESETS
|
export type ColumnPresetId = keyof typeof COLUMN_PRESETS
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export const FenceNode = BaseNode.extend({
|
|||||||
groundClearance: z.number().default(0),
|
groundClearance: z.number().default(0),
|
||||||
edgeInset: z.number().default(0.015),
|
edgeInset: z.number().default(0.015),
|
||||||
baseStyle: FenceBaseStyle.default('grounded'),
|
baseStyle: FenceBaseStyle.default('grounded'),
|
||||||
|
showInfill: z.boolean().default(true),
|
||||||
color: z.string().default('#ffffff'),
|
color: z.string().default('#ffffff'),
|
||||||
style: FenceStyle.default('slat'),
|
style: FenceStyle.default('slat'),
|
||||||
}).describe(
|
}).describe(
|
||||||
@@ -33,6 +34,7 @@ export const FenceNode = BaseNode.extend({
|
|||||||
- height/thickness: overall fence dimensions in meters
|
- height/thickness: overall fence dimensions in meters
|
||||||
- baseHeight/postSpacing/postSize/topRailHeight: exact geometric controls from the plan3D fence model
|
- baseHeight/postSpacing/postSize/topRailHeight: exact geometric controls from the plan3D fence model
|
||||||
- groundClearance/edgeInset/baseStyle: fence support and inset configuration
|
- groundClearance/edgeInset/baseStyle: fence support and inset configuration
|
||||||
|
- showInfill: whether to draw intermediate posts/slats between end posts
|
||||||
- color/style: visual appearance options
|
- color/style: visual appearance options
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1768,6 +1768,56 @@ function getRotatedRectanglePolygon(
|
|||||||
|
|
||||||
function getColumnPlanFootprint(column: ColumnNode): Point2D[] {
|
function getColumnPlanFootprint(column: ColumnNode): Point2D[] {
|
||||||
const center = { x: column.position[0], y: column.position[2] }
|
const center = { x: column.position[0], y: column.position[2] }
|
||||||
|
|
||||||
|
if (
|
||||||
|
column.supportStyle === 'a-frame' ||
|
||||||
|
column.supportStyle === 'y-frame' ||
|
||||||
|
column.supportStyle === 'v-frame' ||
|
||||||
|
column.supportStyle === 'x-brace' ||
|
||||||
|
column.supportStyle === 'k-brace' ||
|
||||||
|
column.supportStyle === 'single-strut' ||
|
||||||
|
column.supportStyle === 'tripod' ||
|
||||||
|
column.supportStyle === 'trestle' ||
|
||||||
|
column.supportStyle === 'portal-frame' ||
|
||||||
|
column.supportStyle === 'box-frame'
|
||||||
|
) {
|
||||||
|
const width = Math.max(
|
||||||
|
column.supportStyle === 'a-frame' ||
|
||||||
|
column.supportStyle === 'x-brace' ||
|
||||||
|
column.supportStyle === 'k-brace' ||
|
||||||
|
column.supportStyle === 'single-strut' ||
|
||||||
|
column.supportStyle === 'tripod' ||
|
||||||
|
column.supportStyle === 'trestle' ||
|
||||||
|
column.supportStyle === 'portal-frame' ||
|
||||||
|
column.supportStyle === 'box-frame'
|
||||||
|
? (column.braceBottomSpread ?? 1.2)
|
||||||
|
: 0,
|
||||||
|
column.braceTopSpread ??
|
||||||
|
(column.supportStyle === 'y-frame' ||
|
||||||
|
column.supportStyle === 'v-frame' ||
|
||||||
|
column.supportStyle === 'x-brace' ||
|
||||||
|
column.supportStyle === 'k-brace' ||
|
||||||
|
column.supportStyle === 'single-strut' ||
|
||||||
|
column.supportStyle === 'tripod' ||
|
||||||
|
column.supportStyle === 'trestle' ||
|
||||||
|
column.supportStyle === 'portal-frame' ||
|
||||||
|
column.supportStyle === 'box-frame'
|
||||||
|
? 1
|
||||||
|
: 0),
|
||||||
|
(column.braceWidth ?? column.width) * 2,
|
||||||
|
)
|
||||||
|
const depth = Math.max(
|
||||||
|
column.supportStyle === 'tripod' ||
|
||||||
|
column.supportStyle === 'trestle' ||
|
||||||
|
column.supportStyle === 'box-frame'
|
||||||
|
? (column.braceTopSpread ?? 1)
|
||||||
|
: 0,
|
||||||
|
column.braceDepth ?? column.depth,
|
||||||
|
0.08,
|
||||||
|
)
|
||||||
|
return getRotatedRectanglePolygon(center, width, depth, column.rotation)
|
||||||
|
}
|
||||||
|
|
||||||
const shaftWidth =
|
const shaftWidth =
|
||||||
column.crossSection === 'round' ||
|
column.crossSection === 'round' ||
|
||||||
column.crossSection === 'octagonal' ||
|
column.crossSection === 'octagonal' ||
|
||||||
@@ -5742,6 +5792,12 @@ const FloorplanFenceLayer = memo(function FloorplanFenceLayer({
|
|||||||
const fenceGlowOpacity = isDeleteHovered ? 0.18 : isActive ? 0.22 : isHovered ? 0.14 : 0
|
const fenceGlowOpacity = isDeleteHovered ? 0.18 : isActive ? 0.22 : isHovered ? 0.14 : 0
|
||||||
const fenceUnderlayWidth = isActive ? '6.5' : isHovered ? '6' : '5.2'
|
const fenceUnderlayWidth = isActive ? '6.5' : isHovered ? '6' : '5.2'
|
||||||
const fenceStrokeWidth = isActive ? '2.6' : isHovered ? '2.35' : '2.05'
|
const fenceStrokeWidth = isActive ? '2.6' : isHovered ? '2.35' : '2.05'
|
||||||
|
const showFenceInfill = fence.showInfill ?? true
|
||||||
|
const visibleMarkerFrames = showFenceInfill
|
||||||
|
? markerFrames
|
||||||
|
: markerFrames.filter(
|
||||||
|
(_, markerIndex) => markerIndex === 0 || markerIndex === markerFrames.length - 1,
|
||||||
|
)
|
||||||
const privacyMarkerWidth = clamp(fence.postSize * 0.58, 0.038, 0.068)
|
const privacyMarkerWidth = clamp(fence.postSize * 0.58, 0.038, 0.068)
|
||||||
const privacyMarkerHeight = clamp(
|
const privacyMarkerHeight = clamp(
|
||||||
Math.max(fence.baseHeight * 0.5, fence.postSize * 1.4),
|
Math.max(fence.baseHeight * 0.5, fence.postSize * 1.4),
|
||||||
@@ -5792,7 +5848,7 @@ const FloorplanFenceLayer = memo(function FloorplanFenceLayer({
|
|||||||
strokeWidth={fenceStrokeWidth}
|
strokeWidth={fenceStrokeWidth}
|
||||||
vectorEffect="non-scaling-stroke"
|
vectorEffect="non-scaling-stroke"
|
||||||
/>
|
/>
|
||||||
{markerFrames.map(({ angleDeg, point }, markerIndex) => {
|
{visibleMarkerFrames.map(({ angleDeg, point }, markerIndex) => {
|
||||||
const svgPoint = toSvgPoint(point)
|
const svgPoint = toSvgPoint(point)
|
||||||
|
|
||||||
if (fence.style === 'privacy') {
|
if (fence.style === 'privacy') {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import useEditor from '../../../store/use-editor'
|
|||||||
import { ActionButton, ActionGroup } from '../controls/action-button'
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SliderControl } from '../controls/slider-control'
|
import { SliderControl } from '../controls/slider-control'
|
||||||
|
import { ToggleControl } from '../controls/toggle-control'
|
||||||
import { PanelWrapper } from './panel-wrapper'
|
import { PanelWrapper } from './panel-wrapper'
|
||||||
|
|
||||||
const SELECT_CLASS =
|
const SELECT_CLASS =
|
||||||
@@ -83,6 +84,7 @@ function presetUpdates(presetId: ColumnPresetId): Partial<ColumnNode> {
|
|||||||
const { label, ...preset } = COLUMN_PRESETS[presetId]
|
const { label, ...preset } = COLUMN_PRESETS[presetId]
|
||||||
return {
|
return {
|
||||||
name: label,
|
name: label,
|
||||||
|
supportStyle: 'supportStyle' in preset ? preset.supportStyle : 'vertical',
|
||||||
...preset,
|
...preset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,6 +201,18 @@ export function ColumnPanel() {
|
|||||||
|
|
||||||
if (!(node && node.type === 'column' && selectedId && selectedCount === 1)) return null
|
if (!(node && node.type === 'column' && selectedId && selectedCount === 1)) return null
|
||||||
const shaftProfile = node.shaftProfile ?? 'straight'
|
const shaftProfile = node.shaftProfile ?? 'straight'
|
||||||
|
const supportStyle = node.supportStyle ?? 'vertical'
|
||||||
|
const isBraceSupport =
|
||||||
|
supportStyle === 'a-frame' ||
|
||||||
|
supportStyle === 'y-frame' ||
|
||||||
|
supportStyle === 'v-frame' ||
|
||||||
|
supportStyle === 'x-brace' ||
|
||||||
|
supportStyle === 'k-brace' ||
|
||||||
|
supportStyle === 'single-strut' ||
|
||||||
|
supportStyle === 'tripod' ||
|
||||||
|
supportStyle === 'trestle' ||
|
||||||
|
supportStyle === 'portal-frame' ||
|
||||||
|
supportStyle === 'box-frame'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelWrapper icon="/icons/column.png" onClose={handleClose} title={node.name || 'Column'} width={300}>
|
<PanelWrapper icon="/icons/column.png" onClose={handleClose} title={node.name || 'Column'} width={300}>
|
||||||
@@ -223,7 +237,65 @@ export function ColumnPanel() {
|
|||||||
<PanelSection title="Shape">
|
<PanelSection title="Shape">
|
||||||
<select
|
<select
|
||||||
className={SELECT_CLASS}
|
className={SELECT_CLASS}
|
||||||
onChange={(event) => handleUpdate({ crossSection: event.target.value as ColumnNode['crossSection'] })}
|
onChange={(event) => {
|
||||||
|
const nextSupportStyle = event.target.value as ColumnNode['supportStyle']
|
||||||
|
handleUpdate({
|
||||||
|
supportStyle: nextSupportStyle,
|
||||||
|
...(nextSupportStyle !== 'vertical'
|
||||||
|
? {
|
||||||
|
crossSection: 'rectangular',
|
||||||
|
width: node.braceWidth ?? node.width,
|
||||||
|
depth: node.braceDepth ?? node.depth,
|
||||||
|
baseStyle: 'none',
|
||||||
|
capitalStyle: 'none',
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
value={supportStyle}
|
||||||
|
>
|
||||||
|
<option value="vertical">Vertical</option>
|
||||||
|
<option value="a-frame">A-Frame</option>
|
||||||
|
<option value="y-frame">Y Support</option>
|
||||||
|
<option value="v-frame">V Support</option>
|
||||||
|
<option value="x-brace">X Brace</option>
|
||||||
|
<option value="k-brace">K Brace</option>
|
||||||
|
<option value="single-strut">Single Strut</option>
|
||||||
|
<option value="tripod">Tripod Support</option>
|
||||||
|
<option value="trestle">Trestle Frame</option>
|
||||||
|
<option value="portal-frame">Portal Frame</option>
|
||||||
|
<option value="box-frame">Box Frame</option>
|
||||||
|
</select>
|
||||||
|
{isBraceSupport ? (
|
||||||
|
<>
|
||||||
|
<SliderControl
|
||||||
|
label="Brace Width"
|
||||||
|
max={0.8}
|
||||||
|
min={0.04}
|
||||||
|
onChange={(value) => handleUpdate({ braceWidth: value, width: value })}
|
||||||
|
precision={2}
|
||||||
|
step={0.01}
|
||||||
|
unit="m"
|
||||||
|
value={node.braceWidth ?? node.width}
|
||||||
|
/>
|
||||||
|
<SliderControl
|
||||||
|
label="Brace Depth"
|
||||||
|
max={0.8}
|
||||||
|
min={0.04}
|
||||||
|
onChange={(value) => handleUpdate({ braceDepth: value, depth: value })}
|
||||||
|
precision={2}
|
||||||
|
step={0.01}
|
||||||
|
unit="m"
|
||||||
|
value={node.braceDepth ?? node.depth}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<select
|
||||||
|
className={SELECT_CLASS}
|
||||||
|
onChange={(event) =>
|
||||||
|
handleUpdate({ crossSection: event.target.value as ColumnNode['crossSection'] })
|
||||||
|
}
|
||||||
value={node.crossSection}
|
value={node.crossSection}
|
||||||
>
|
>
|
||||||
<option value="round">Round</option>
|
<option value="round">Round</option>
|
||||||
@@ -252,9 +324,12 @@ export function ColumnPanel() {
|
|||||||
value={node.shaftCornerRadius ?? 0.035}
|
value={node.shaftCornerRadius ?? 0.035}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
|
||||||
<PanelSection title="Dimensions">
|
<PanelSection title="Dimensions">
|
||||||
|
{!isBraceSupport && (
|
||||||
<select
|
<select
|
||||||
className={SELECT_CLASS}
|
className={SELECT_CLASS}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
@@ -270,6 +345,7 @@ export function ColumnPanel() {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
)}
|
||||||
<SliderControl
|
<SliderControl
|
||||||
label="Height"
|
label="Height"
|
||||||
max={6}
|
max={6}
|
||||||
@@ -280,6 +356,77 @@ export function ColumnPanel() {
|
|||||||
unit="m"
|
unit="m"
|
||||||
value={node.height}
|
value={node.height}
|
||||||
/>
|
/>
|
||||||
|
{isBraceSupport ? (
|
||||||
|
<>
|
||||||
|
{(supportStyle === 'a-frame' ||
|
||||||
|
supportStyle === 'x-brace' ||
|
||||||
|
supportStyle === 'k-brace' ||
|
||||||
|
supportStyle === 'single-strut' ||
|
||||||
|
supportStyle === 'tripod' ||
|
||||||
|
supportStyle === 'trestle' ||
|
||||||
|
supportStyle === 'portal-frame' ||
|
||||||
|
supportStyle === 'box-frame') && (
|
||||||
|
<SliderControl
|
||||||
|
label="Bottom Spread"
|
||||||
|
max={4}
|
||||||
|
min={0.2}
|
||||||
|
onChange={(value) =>
|
||||||
|
handleUpdate({
|
||||||
|
braceBottomSpread: value,
|
||||||
|
braceTopSpread:
|
||||||
|
supportStyle === 'a-frame'
|
||||||
|
? Math.min(node.braceTopSpread ?? 0.12, value)
|
||||||
|
: (node.braceTopSpread ?? 1),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
precision={2}
|
||||||
|
step={0.05}
|
||||||
|
unit="m"
|
||||||
|
value={node.braceBottomSpread ?? 1.2}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<SliderControl
|
||||||
|
label={supportStyle === 'y-frame' ? 'Fork Spread' : 'Top Spread'}
|
||||||
|
max={
|
||||||
|
supportStyle === 'y-frame' ||
|
||||||
|
supportStyle === 'v-frame' ||
|
||||||
|
supportStyle === 'x-brace' ||
|
||||||
|
supportStyle === 'k-brace' ||
|
||||||
|
supportStyle === 'single-strut' ||
|
||||||
|
supportStyle === 'tripod' ||
|
||||||
|
supportStyle === 'trestle' ||
|
||||||
|
supportStyle === 'box-frame'
|
||||||
|
? 4
|
||||||
|
: Math.max(0.2, node.braceBottomSpread ?? 1.2)
|
||||||
|
}
|
||||||
|
min={0}
|
||||||
|
onChange={(value) => handleUpdate({ braceTopSpread: value })}
|
||||||
|
precision={2}
|
||||||
|
step={0.02}
|
||||||
|
unit="m"
|
||||||
|
value={
|
||||||
|
node.braceTopSpread ??
|
||||||
|
(supportStyle === 'y-frame' ||
|
||||||
|
supportStyle === 'v-frame' ||
|
||||||
|
supportStyle === 'x-brace' ||
|
||||||
|
supportStyle === 'k-brace' ||
|
||||||
|
supportStyle === 'single-strut' ||
|
||||||
|
supportStyle === 'tripod' ||
|
||||||
|
supportStyle === 'trestle' ||
|
||||||
|
supportStyle === 'portal-frame' ||
|
||||||
|
supportStyle === 'box-frame'
|
||||||
|
? 1
|
||||||
|
: 0.12)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ToggleControl
|
||||||
|
checked={node.bracePlateEnabled ?? true}
|
||||||
|
label="Connector Plates"
|
||||||
|
onChange={(checked) => handleUpdate({ bracePlateEnabled: checked })}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<SliderControl
|
<SliderControl
|
||||||
label="Width"
|
label="Width"
|
||||||
max={1.6}
|
max={1.6}
|
||||||
@@ -308,12 +455,19 @@ export function ColumnPanel() {
|
|||||||
value={node.depth}
|
value={node.depth}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
|
||||||
|
{!isBraceSupport && (
|
||||||
<PanelSection title="Shaft">
|
<PanelSection title="Shaft">
|
||||||
<select
|
<select
|
||||||
className={SELECT_CLASS}
|
className={SELECT_CLASS}
|
||||||
onChange={(event) => handleUpdate(shaftProfileUpdates(event.target.value as ColumnNode['shaftProfile']))}
|
onChange={(event) =>
|
||||||
|
handleUpdate(
|
||||||
|
shaftProfileUpdates(event.target.value as ColumnNode['shaftProfile']),
|
||||||
|
)
|
||||||
|
}
|
||||||
value={shaftProfile}
|
value={shaftProfile}
|
||||||
>
|
>
|
||||||
<option value="straight">Straight</option>
|
<option value="straight">Straight</option>
|
||||||
@@ -369,7 +523,9 @@ export function ColumnPanel() {
|
|||||||
label="End Width"
|
label="End Width"
|
||||||
max={1.2}
|
max={1.2}
|
||||||
min={0.3}
|
min={0.3}
|
||||||
onChange={(value) => handleUpdate({ shaftStartScale: value, shaftEndScale: value })}
|
onChange={(value) =>
|
||||||
|
handleUpdate({ shaftStartScale: value, shaftEndScale: value })
|
||||||
|
}
|
||||||
precision={2}
|
precision={2}
|
||||||
step={0.02}
|
step={0.02}
|
||||||
value={node.shaftStartScale ?? 0.68}
|
value={node.shaftStartScale ?? 0.68}
|
||||||
@@ -391,7 +547,9 @@ export function ColumnPanel() {
|
|||||||
label="End Width"
|
label="End Width"
|
||||||
max={1.2}
|
max={1.2}
|
||||||
min={0.3}
|
min={0.3}
|
||||||
onChange={(value) => handleUpdate({ shaftStartScale: value, shaftEndScale: value })}
|
onChange={(value) =>
|
||||||
|
handleUpdate({ shaftStartScale: value, shaftEndScale: value })
|
||||||
|
}
|
||||||
precision={2}
|
precision={2}
|
||||||
step={0.02}
|
step={0.02}
|
||||||
value={node.shaftStartScale ?? 0.84}
|
value={node.shaftStartScale ?? 0.84}
|
||||||
@@ -475,7 +633,9 @@ export function ColumnPanel() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isBraceSupport && (
|
||||||
<PanelSection title="Ends">
|
<PanelSection title="Ends">
|
||||||
<select
|
<select
|
||||||
className={SELECT_CLASS}
|
className={SELECT_CLASS}
|
||||||
@@ -685,6 +845,7 @@ export function ColumnPanel() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
)}
|
||||||
|
|
||||||
<PanelSection title="Transform">
|
<PanelSection title="Transform">
|
||||||
<SliderControl
|
<SliderControl
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { MaterialPicker } from '../controls/material-picker'
|
|||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SegmentedControl } from '../controls/segmented-control'
|
import { SegmentedControl } from '../controls/segmented-control'
|
||||||
import { SliderControl } from '../controls/slider-control'
|
import { SliderControl } from '../controls/slider-control'
|
||||||
|
import { ToggleControl } from '../controls/toggle-control'
|
||||||
import { PanelWrapper } from './panel-wrapper'
|
import { PanelWrapper } from './panel-wrapper'
|
||||||
|
|
||||||
type FenceStyleValue = 'slat' | 'rail' | 'privacy'
|
type FenceStyleValue = 'slat' | 'rail' | 'privacy'
|
||||||
@@ -115,6 +116,12 @@ export function FencePanel() {
|
|||||||
options={FENCE_BASE_STYLE_OPTIONS}
|
options={FENCE_BASE_STYLE_OPTIONS}
|
||||||
value={node.baseStyle}
|
value={node.baseStyle}
|
||||||
/>
|
/>
|
||||||
|
<ToggleControl
|
||||||
|
checked={node.showInfill ?? true}
|
||||||
|
className="mt-2"
|
||||||
|
label="Fence Infill"
|
||||||
|
onChange={(checked) => handleUpdate({ showInfill: checked })}
|
||||||
|
/>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
|
||||||
<PanelSection title="Dimensions">
|
<PanelSection title="Dimensions">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
|||||||
import { useFrame } from '@react-three/fiber'
|
|
||||||
import {
|
import {
|
||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
type FenceNode,
|
type FenceNode,
|
||||||
@@ -7,6 +6,7 @@ import {
|
|||||||
sceneRegistry,
|
sceneRegistry,
|
||||||
useScene,
|
useScene,
|
||||||
} from '@pascal-app/core'
|
} from '@pascal-app/core'
|
||||||
|
import { useFrame } from '@react-three/fiber'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
||||||
|
|
||||||
@@ -69,7 +69,9 @@ function createFenceCurveSpanParts(
|
|||||||
const parts: FencePart[] = []
|
const parts: FencePart[] = []
|
||||||
const frameCount = Math.max(
|
const frameCount = Math.max(
|
||||||
1,
|
1,
|
||||||
Math.ceil((getWallCurveLength(fence) * Math.max(1e-4, endT - startT)) / MIN_CURVE_SEGMENT_LENGTH),
|
Math.ceil(
|
||||||
|
(getWallCurveLength(fence) * Math.max(1e-4, endT - startT)) / MIN_CURVE_SEGMENT_LENGTH,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
let previous = getFencePointAt(fence, startT)
|
let previous = getFencePointAt(fence, startT)
|
||||||
@@ -164,6 +166,7 @@ function createFenceParts(fence: FenceNode): FencePart[] {
|
|||||||
const spacing = Math.max(fence.postSpacing * styleDefaults.spacingFactor, postWidth * 1.2)
|
const spacing = Math.max(fence.postSpacing * styleDefaults.spacingFactor, postWidth * 1.2)
|
||||||
const edgeInset = Math.max(fence.edgeInset ?? 0.015, 0.005)
|
const edgeInset = Math.max(fence.edgeInset ?? 0.015, 0.005)
|
||||||
const isFloating = fence.baseStyle === 'floating'
|
const isFloating = fence.baseStyle === 'floating'
|
||||||
|
const showInfill = fence.showInfill ?? true
|
||||||
const baseY = isFloating ? clearance : 0
|
const baseY = isFloating ? clearance : 0
|
||||||
const effectiveBaseHeight = baseHeight
|
const effectiveBaseHeight = baseHeight
|
||||||
const startInsetT = Math.min(0.499, edgeInset / length)
|
const startInsetT = Math.min(0.499, edgeInset / length)
|
||||||
@@ -192,18 +195,18 @@ function createFenceParts(fence: FenceNode): FencePart[] {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const count = Math.max(2, Math.floor((length - edgeInset * 2) / spacing) + 1)
|
const count = showInfill ? Math.max(2, Math.floor((length - edgeInset * 2) / spacing) + 1) : 2
|
||||||
const verticalY = baseY + effectiveBaseHeight + verticalHeight / 2
|
const verticalY = baseY + effectiveBaseHeight + verticalHeight / 2
|
||||||
|
|
||||||
for (let index = 0; index < count; index += 1) {
|
for (let index = 0; index < count; index += 1) {
|
||||||
const t = count === 1 ? 0.5 : startInsetT + (endInsetT - startInsetT) * (index / (count - 1))
|
const t = count === 1 ? 0.5 : startInsetT + (endInsetT - startInsetT) * (index / (count - 1))
|
||||||
const frame = getFencePointAt(fence, t)
|
const frame = getFencePointAt(fence, t)
|
||||||
const isEdgePost = index === 0 || index === count - 1
|
const isEdgePost = index === 0 || index === count - 1
|
||||||
const postHeight =
|
const fullHeightPost = !showInfill || (isFloating && isEdgePost)
|
||||||
isFloating && isEdgePost
|
const postHeight = fullHeightPost
|
||||||
? effectiveBaseHeight + verticalHeight + topRailHeight + clearance
|
? effectiveBaseHeight + verticalHeight + topRailHeight + clearance
|
||||||
: verticalHeight
|
: verticalHeight
|
||||||
const postY = isFloating && isEdgePost ? postHeight / 2 : verticalY
|
const postY = fullHeightPost ? postHeight / 2 : verticalY
|
||||||
|
|
||||||
parts.push({
|
parts.push({
|
||||||
position: [frame.point.x, postY, frame.point.y],
|
position: [frame.point.x, postY, frame.point.y],
|
||||||
@@ -244,7 +247,9 @@ function generateFenceGeometry(fence: FenceNode) {
|
|||||||
const geometries = parts.map(createFencePartGeometry)
|
const geometries = parts.map(createFencePartGeometry)
|
||||||
|
|
||||||
const merged = mergeGeometries(geometries, false) ?? new THREE.BufferGeometry()
|
const merged = mergeGeometries(geometries, false) ?? new THREE.BufferGeometry()
|
||||||
geometries.forEach((geometry) => geometry.dispose())
|
geometries.forEach((geometry) => {
|
||||||
|
geometry.dispose()
|
||||||
|
})
|
||||||
const mergedUv = merged.getAttribute('uv')
|
const mergedUv = merged.getAttribute('uv')
|
||||||
if (mergedUv) {
|
if (mergedUv) {
|
||||||
merged.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(mergedUv.array), 2))
|
merged.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(mergedUv.array), 2))
|
||||||
|
|||||||
Reference in New Issue
Block a user