From 965c342546f187041b8dc5f9222b5a8fdf029165 Mon Sep 17 00:00:00 2001 From: ylzha Date: Sun, 31 May 2026 23:55:25 -0400 Subject: [PATCH 1/2] sketch_rect_model: guard against off-plane model_origin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit modelToSketchSpace flattens the origin onto the sketch plane and the out-of-plane component (sk_o.z) was silently dropped — so an off-plane model_origin created the body IN the plane, not at model_origin. This was the root cause of 3 of the 4 defects in PR #56 (back-panel-at-front). Now raises an immediate, self-explaining ValueError when the origin lies >1e-4 cm off the plane, converting a silent wrong-position defect (caught late by check_interference) into an authoring-time error. A correct call (plane at the right offset) has sk_o.z ~ 0 and is unaffected. Validated: cabinet (heavy multi-plane user, incl. the just-fixed back panel) builds clean — guard does not trip. Broader example sweep pending. Co-Authored-By: Claude Opus 4.8 (1M context) --- helpers/sp/sketch.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/helpers/sp/sketch.py b/helpers/sp/sketch.py index ef2d440..eb4d90b 100644 --- a/helpers/sp/sketch.py +++ b/helpers/sp/sketch.py @@ -158,6 +158,19 @@ def sketch_rect_model(comp, plane, model_origin, model_size, corner[a] += ev(expr) sk_o = sk.modelToSketchSpace(Point3D.create(ox, oy, oz)) + # Guard: model_origin must LIE ON the sketch plane. modelToSketchSpace + # flattens the point onto the plane, dropping the out-of-plane component + # (sk_o.z) below — so an off-plane origin would silently create the body + # IN the plane, not at model_origin (the back-panel-at-front bug class: + # PR #56). Catch it here as an immediate, self-explaining error instead of + # a late interference. A correct call (plane at the right offset) has + # sk_o.z ≈ 0; only a wrong plane trips this. + if abs(sk_o.z) > 1e-4: + raise ValueError( + f"sketch_rect_model('{name}'): model_origin lies {sk_o.z:.4f} cm off " + f"the sketch plane — the body would be created IN the plane, not at " + f"model_origin. Sketch on an off_plane at that offset, or set the " + f"out-of-plane component of model_origin to match the plane.") sk_f = sk.modelToSketchSpace( Point3D.create(corner["x"], corner["y"], corner["z"])) From 0858cb9fafea10715199f4e3e801d9f33289f742 Mon Sep 17 00:00:00 2001 From: ylzha Date: Mon, 1 Jun 2026 00:21:11 -0400 Subject: [PATCH 2/2] examples/sideboard: recess kick to kick_inset (off-plane-origin bug) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guard from this branch caught it: KickFront_Sk/KickLeft_Sk sketched on the bare xZ/yZ planes (y=0 / x=0) but their model_origin set the recess (y/x = kick_inset). modelToSketchSpace flattened that onto the case face → the kick was built FLUSH with the front/side instead of recessed for toe clearance (reanchor only fixes the in-plane x/z dims). Fix: sketch on off_planes at kick_inset (same pattern as PR #56's back panels) so the body is correctly recessed and the origin lands on-plane. Static sweep of all examples confirms sideboard is the ONLY example with origin-mode off-plane bugs; the other flagged calls (wardrobe kick, toy-box back, sideboard top) use anchored mode, which handles off-plane origins correctly (wardrobe verified clean in Fusion). NOT yet Fusion-validated — connection dropped mid-sweep. Needs: guard clean + deps/connectivity pass + recessed kick still joins the case. Co-Authored-By: Claude Opus 4.8 (1M context) --- examples/sideboard/sideboard.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/sideboard/sideboard.py b/examples/sideboard/sideboard.py index de6ccda..6e9420e 100644 --- a/examples/sideboard/sideboard.py +++ b/examples/sideboard/sideboard.py @@ -166,10 +166,14 @@ def find_body(name, comp=None): print(">>> Top: 1") # ==== KICK ==== - # Anchor to Side_Left (clean Case body). KickFront_Sk is on the xZ plane - # (normal y); project Side_Left's front face (y,-1) at corner (0,0,kick_h) - # and let reanchor retarget the x/z origin dims (signs via abs()). - sk, pr = sp.sketch_rect_model(kick_c, kick_c.xZConstructionPlane, + # Anchor to Side_Left (clean Case body). KickFront_Sk is on an xZ-parallel + # plane RECESSED to y=kick_inset (the kick is set back from the case front + # for toe clearance). Sketching on the bare xZ plane (y=0) would flatten the + # y=kick_inset origin onto y=0 and build the kick flush with the front — + # the off-plane-origin bug. reanchor then retargets the in-plane x/z dims. + kick_f_pl = sp.off_plane(kick_c, kick_c.xZConstructionPlane, + "kick_inset", "KickF_Pl") + sk, pr = sp.sketch_rect_model(kick_c, kick_f_pl, ("kick_inset", "kick_inset", "0 in"), {"x": "case_w - 2 * kick_inset", "z": "kick_h"}, "KickFront_Sk", ev) pr = (sp.reanchor(sk, ref_side_left, case_occ, "y", -1, @@ -184,9 +188,12 @@ def find_body(name, comp=None): k_ymid = sp.off_plane(kick_c, kick_c.xZConstructionPlane, "case_d / 2", "KYMid") sp.mirror_feats(kick_c, [kf_ext], k_ymid, "KickBackMir").bodies.item(0).name = "Kick_Back" - # KickLeft_Sk on the yZ plane (normal x); project Side_Left's left face - # (x,-1) at corner (0,0,kick_h) and reanchor the y/z origin dims. - sk, pr = sp.sketch_rect_model(kick_c, kick_c.yZConstructionPlane, + # KickLeft_Sk on a yZ-parallel plane RECESSED to x=kick_inset (kick set in + # from the case side). Bare yZ (x=0) would flatten the x=kick_inset origin + # onto x=0 and build the kick flush with the side — the off-plane-origin bug. + kick_l_pl = sp.off_plane(kick_c, kick_c.yZConstructionPlane, + "kick_inset", "KickL_Pl") + sk, pr = sp.sketch_rect_model(kick_c, kick_l_pl, ("kick_inset", "kick_inset + board_thick", "0 in"), {"y": "case_d - 2 * kick_inset - 2 * board_thick", "z": "kick_h"}, "KickLeft_Sk", ev)