Skip to content

feat(wall): add endHeightOffset for sloped top edge - #640

Open
vducasse wants to merge 5 commits into
pascalorg:mainfrom
vducasse:feat/wall-end-height
Open

feat(wall): add endHeightOffset for sloped top edge#640
vducasse wants to merge 5 commits into
pascalorg:mainfrom
vducasse:feat/wall-end-height

Conversation

@vducasse

@vducasse vducasse commented Aug 12, 2026

Copy link
Copy Markdown

What does this PR do?

Adds an endHeightOffset property to the WallNode schema to allow wall extremities to have different heights (e.g., for creating a knee wall following a single-pitch roof slope). This adds an "End height offset" slider to the wall properties panel and updates the geometry generation to apply the slope along the top edge of the wall.

How to test

  1. Open a scene in the editor and draw a standard wall.
  2. Select the wall to open the properties panel.
  3. Under the dimensions/properties, look for the new End height offset slider.
  4. Adjust the slider and verify that the top edge of the wall tilts smoothly between the start and end points.

Screenshots / screen recording

image image image image

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

Note

Medium Risk
Changes wall height resolution, 3D mesh generation, and wall-item placement together; additive optional field but mismatches could affect doors/windows on sloped walls.

Overview
Walls can now have a sloped top edge via a new optional endHeightOffset on WallNode (extra height at the end point only; start unchanged).

The wall properties panel adds an End height offset slider (bounded so the end cannot drop below a minimal top height). resolveWallTop / resolveWallEffectiveHeight take an optional parametric t along the wall and add endHeightOffset * t to the resolved top. SpatialGridManager uses that local height when validating wall-mounted placement (canPlaceOnWall passes t from position along the wall). The viewer runs applyWallEndHeightSlope after extrusion to lift top vertices from start→end, including curved walls (arc-based t instead of chord X).

Reviewed by Cursor Bugbot for commit 83d0854. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread packages/nodes/src/wall/panel.tsx
Fixes a bug where negative offset values caused wall geometry to self-intersect and drop below the base. The value is now strictly clamped to 0 at the Zod schema, UI, and geometry generation layers.
Comment thread packages/viewer/src/systems/wall/wall-system.tsx Outdated
# Conflicts:
#	packages/nodes/src/wall/panel.tsx
Comment thread packages/viewer/src/systems/wall/wall-system.tsx
Comment thread packages/viewer/src/systems/wall/wall-system.tsx
Ducasse, Vincent (external) added 2 commits August 13, 2026 11:24
    curved wall sloped geometry

- **Geometry Generation**:
  - `applyWallEndHeightSlope`: Replaced naive X-axis interpolation with exact
  radial angle-based parameterization (`t`) when the wall footprint is curved.
  Implemented robust `getSignedAngleDiff` and angle-unwrapping based on
  expected linear distance to prevent severe Y-axis warping on the wall end
  caps when `Math.atan2` boundaries are crossed on steep semicircular curves.
  - `generateExtrudedWall`: Added `getWallArcData` injection, mapping the arc
  center into wall-local coordinate space, and passing `localArc` down into
  the height slope generator.

- **Height Resolution Core**:
  - `resolveWallTop`: Added an optional `t?: number` (parametric position
  along the chord) to correctly account for the sloped top. It injects
  `endHeightOffset * t` directly into the final top boundary calculation.
  - `resolveWallEffectiveHeight`: Cascaded the optional `t` parameter from
  the caller down into `resolveWallTop` and adjusted elevation offsets.

- **Spatial Grid Placement**:
  - `getWallHeight`: Now accepts an optional parametric position and passes
  it directly to `resolveWallTop`.
  - `canPlaceOnWall`: Computes the exact parametric `tCenter` for wall-hosted
  items based on the projection of the item's position onto the wall chord.
  This allows the placement bounding box constraints to accurately read the
  sloped height of the wall at the item's insertion point, fixing the bug
  where placement validators treated the entire wall as completely flat.

- **UI and Constraints**:
  - `panel.tsx`: Refactored the `SliderControl` minimum constraint for the
  `endHeightOffset` input to explicitly prevent the wall top from dropping
  below a safe minimum height threshold (`0.01m`).

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 83d0854. Configure here.

}
const wallHeight = wallNode.height ?? 2.5
const minEndHeight = 0.01
const endHeightOffset = Math.max(rawOffset, -(wallHeight - minEndHeight))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plane-bound slope clamp uses wrong height

Medium Severity

applyWallEndHeightSlope clamps negative endHeightOffset against wallNode.height ?? 2.5. Plane-bound walls have no stored height, so the clamp ignores the real extruded body height (topY / storey minus base). On taller storeys the UI can request a lower end than geometry applies; on shorter bodies a large negative offset can pull the end below the wall base.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 83d0854. Configure here.

): number {
const levelId = resolveNodeLevelId(wall, nodes)
const baseElevation = getWallBaseElevationForNodes(wall, nodes)
return resolveWallEffectiveHeight(wall, getWallPlaneTop(wall, levelId, nodes), baseElevation)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Openings ignore sloped wall top

Medium Severity

getWallEffectiveHeightForNodes still resolves a single flat height and never passes parametric t, so resolveWallOpeningCeiling caps doors and windows at the start-side top. With a negative endHeightOffset, openings near the end can be taller than the rendered wall there and cut through the sloped crest.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 83d0854. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant