← house

Pitched-L Gable Closure

v1 AI approved

Close gable edge 3 for the lower wing using the exact skeleton pitch and apex. This is non-bearing infill; roof load bears on eave plates.

Dependencies

s0_wall_e3.body

Interfaces Provided

body (component)
◉
Agent initialized (status: waiting_for_dependencies, action: check_dependencies)
◉
Provider: claude_code | Profile: balanced
◇
Received interface s0_wall_e3.corner_north_west: %{name: "corner_north_west", type: nil, value: %{}, agent_id: "s0_wall_e3"}
◇
Received interface s0_wall_e3.corner_concave: %{name: "corner_concave", type: nil, value: %{}, agent_id: "s0_wall_e3"}
◇
Received interface s0_wall_e3.body: %{name: "body", type: "component", value: %{}, agent_id: "s0_wall_e3"}
◉
All dependencies resolved, starting generation
◉
Refreshed placement from design spec (bbox %{"x" => 142.0, "y" => 3000.0, "z" => 866.0254037844388})
◉
Generating v1
◉
Using deterministic SIPRoofPanelizer design-roof generation
◆
gable_closure_3_v1.py · 46 lines
import json
import FreeCAD
import Part
from FreeCAD import Vector
from elixifree.domains.sip import SIPRoofPanelizer

doc = FreeCAD.ActiveDocument or FreeCAD.newDocument("Model")
outline = json.loads("[[0.0,3.0e3,0.0],[0.0,0.0,0.0],[0.0,1.5e3,866.0254037844388]]")
extrusion = json.loads("[142.0,0.0,0.0]")
roof_contract = json.loads("{\"lower_wing\":{\"x0\":0.0,\"x1\":1.2e4,\"y0\":3.0e3,\"y1\":6.0e3},\"pitch_degrees\":30.0,\"skeleton\":{\"apex_z\":2309.4010767585028,\"edge_roles\":[{\"edge\":0,\"role\":\"gable\",\"valley_adjacent\":false,\"wing\":\"upper\"},{\"edge\":1,\"role\":\"eave\",\"valley_adjacent\":false,\"wing\":\"upper\"},{\"edge\":2,\"role\":\"eave\",\"valley_adjacent\":false,\"wing\":\"lower\"},{\"edge\":3,\"role\":\"gable\",\"valley_adjacent\":false,\"wing\":\"lower\"},{\"edge\":4,\"role\":\"eave\",\"valley_adjacent\":true,\"wing\":\"lower\"},{\"edge\":5,\"role\":\"eave\",\"valley_adjacent\":true,\"wing\":\"upper\"}],\"footprint\":[[4.0e3,0.0],[1.2e4,0.0],[1.2e4,6.0e3],[0.0,6.0e3],[0.0,3.0e3],[4.0e3,3.0e3]],\"pitch_deg\":30.0,\"planes\":[{\"axis\":\"y\",\"direction\":1.0,\"id\":\"lower_south\",\"origin\":3.0e3,\"side\":\"south\",\"tan_pitch\":0.5773502691896257,\"wing\":\"lower\"},{\"axis\":\"y\",\"direction\":-1.0,\"id\":\"lower_north\",\"origin\":6.0e3,\"side\":\"north\",\"tan_pitch\":0.5773502691896257,\"wing\":\"lower\"},{\"axis\":\"x\",\"direction\":1.0,\"id\":\"upper_west\",\"origin\":4.0e3,\"side\":\"west\",\"tan_pitch\":0.5773502691896257,\"wing\":\"upper\"},{\"axis\":\"x\",\"direction\":-1.0,\"id\":\"upper_east\",\"origin\":1.2e4,\"side\":\"east\",\"tan_pitch\":0.5773502691896257,\"wing\":\"upper\"}],\"ridges\":[{\"from\":[0.0,4.5e3,866.0254037844386],\"id\":\"ridge_lower\",\"to\":[1.2e4,4.5e3,866.0254037844386],\"wing\":\"lower\"},{\"from\":[8.0e3,0.0,2309.4010767585028],\"id\":\"ridge_upper\",\"to\":[8.0e3,6.0e3,2309.4010767585028],\"wing\":\"upper\"}],\"type\":\"gable\",\"valley\":{\"adjacent_wings\":[\"lower\",\"upper\"],\"from\":[4.0e3,3.0e3,0.0],\"id\":\"valley\",\"to\":[5.5e3,4.5e3,866.0254037844386]},\"wings\":{\"concave\":[4.0e3,3.0e3],\"lower\":{\"x0\":0.0,\"x1\":1.2e4,\"y0\":3.0e3,\"y1\":6.0e3},\"upper\":{\"x0\":4.0e3,\"x1\":1.2e4,\"y0\":0.0,\"y1\":6.0e3}}},\"stock\":\"SIP-200\",\"upper_wing\":{\"x0\":4.0e3,\"x1\":1.2e4,\"y0\":0.0,\"y1\":6.0e3}}")
roof_offset = json.loads("[-0.0,-3.0e3]")
roof_z_offset = 192.25763964014538
if roof_contract.get("lower_wing") and roof_contract.get("upper_wing"):
    panelizer = SIPRoofPanelizer(
        roof_contract["lower_wing"],
        roof_contract["upper_wing"],
        roof_contract["pitch_degrees"],
        stock=roof_contract["stock"],
        skeleton=roof_contract.get("skeleton"),
    )
else:
    # cross_gable: no wing pair — derive the envelope from the skeleton
    # (ISSUE-018; requires the descriptor to carry roof_contract.skeleton).
    panelizer = SIPRoofPanelizer.for_cross(
        roof_contract["skeleton"],
        roof_contract["pitch_degrees"],
        stock=roof_contract["stock"],
    )
shape = panelizer.gable_transition(
    outline, extrusion,
    roof_offset=roof_offset,
    roof_z_offset=roof_z_offset,
)
if shape.isNull() or not shape.isValid():
    raise RuntimeError("Gable transition produced invalid geometry")

feature = doc.addObject("Part::Feature", "Body")
feature.Label = "Pitched-L Gable Transition Infill"
feature.addProperty("App::PropertyString", "RoofGenerator")
feature.RoofGenerator = "deterministic_graph_outline"
feature.Shape = shape
doc.recompute()

if FreeCAD.GuiUp:
    FreeCAD.Gui.ActiveDocument.ActiveView.fitAll()
●
Executing in FreeCAD...
◉
StepAnalyzer failed: FreeCAD could not read STEP file: FreeCAD exception thrown (File to load not existing or not readable) — skipping
●
FreeCAD execution succeeded
●
Exported: model.stl
◉
Generation succeeded and component was auto-approved (v1)
◉
Provider updated to claude_code / balanced
◉
Provider updated to claude_code / balanced
◉
Provider updated to claude_code / balanced
◉
Provider updated to claude_code / balanced
◉
Provider updated to claude_code / balanced
◉
Provider updated to claude_code / balanced
v0.0.985