mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 19:01:02 +00:00
CAD: extrude a sketch region with its holes, and stop crashing at startup
A rectangle with a circle inside it, drawn in ONE sketch, could not be extruded to a plate with a bore from the GUI. Five defects were in the way. Each was found by driving the app on a headless rig and measuring the result — the code reads correctly at every one of these points, which is why they survived. 1. Wire orientation (kernel). SketchEngine::wires_to_face added every hole as wires[i].Reversed(), which is only right when the sketch happens to wind both loops the same way. A circle drawn clockwise inside a counter-clockwise rectangle came out matching the outer boundary, OCCT swept it as a SECOND contour, and the prism was the plate with its bore filled and the disc's volume counted twice. Measured: bbox 67.17 x 219.67 x 10 with volume 152088 mm3 against a solid box of 147542 — a body larger than its own bounding box, which is the signature. Holes are now added as-is and ShapeFix_Face::FixOrientation() classifies them; that is winding-independent and is the idiom make_extrude_regions already used for imported glyphs, which is why holed TEXT always extruded correctly while a holed SKETCH never did. After the fix: 142996 mm3, implied bore radius 12.03 mm against the circle drawn. 2. The live-sketch click threw the picked region away. region_at() served only as a yes/no gate and on_face_selected() carried no argument, so Extrude fell back to whichever loop the resolver found first — clicking the material of a plate-with-a-hole extruded the disc. The region is now carried through, and DesignPanel also hands it to the tool with set_loop_pick(), AFTER open_tool() because that re-derives selection state, since extrude_uses_loop() reads selected_loop_entities() and that lives on the tool. 3. region_at() had no hole awareness and no innermost preference: it returned the first polygon containing the point. It now skips a region when the point lies inside one of that region's holes, and picks the smallest containing loop, so a click in the bore selects the disc and a click on the material selects the plate. 4. Startup segfault. DesignCanvas::request_repaint probed the GL backend via OpenGLManager::get_gl_info().get_renderer() before the canvas had initialised GL — glGetString with no context current and, before init_opengl(), no loaded function pointers. Anything that asked for a repaint while the panel was still being built landed there, with no window and nothing in the log. It now bails at the top on !is_initialized() and asks for a Refresh instead. Note the crash was in the PROBE, not in render(), which already guards itself. 5. A holed sketch on a plane whose normal points -Z came out as the full box PLUS a disc — 220274 mm3 where 163726 was due (192000 + 28274). wires_to_face took a SketchPlane parameter it never used and let OCCT infer a surface from the outer wire; when the inferred normal disagreed with the sketch's, the hole classification produced no hole. Every face is now built on the sketch's own gp_Pln. Also in this change, from the same rig session: - A right-click that only clears the sketch selection no longer reports itself as consumed, so it stops suppressing the offer menu. With any geometry in a live sketch there was no menu route left to add a second entity. - Escape no longer discards a live sketch that holds drawn geometry; it says so and keeps the work (live_sketch_has_work()). - The holed-region fill is an even-odd scanline instead of a keyhole bridge, so no corridor triangle leaks from the bore to the nearest corner. - Cyan is reserved for the selection: an unselected region no longer wears a shade one step off the selected one. - The origin planes follow the mode, so pressing Sketch on a document that already has a body offers them again instead of naming a plane you cannot see. Tests: three [holes] cases over add_extrude_entities asserting the plate-with-bore volume, solid and face counts on both a +Z and a -Z sketch plane, and the by-name refusal of two disjoint regions. Full [CadDocument] suite green.
This commit is contained in:
@@ -30,6 +30,23 @@ class Camera; // fwd — move_gizmo_arm() sizes the gizmo from the current z
|
||||
// accumulate. `finish` commits the whole entity list as one sketch feature;
|
||||
// `cancel` aborts. Constrain is a separate legacy mode that operates on a
|
||||
// committed profile's points (entity constraints land in a later chunk).
|
||||
|
||||
|
||||
// ONE colour means SELECTED — a face, an edge, a vertex, a whole body, a 2D sketch region.
|
||||
// Nothing else on screen may wear it. Before this there were four near-identical cyans plus a
|
||||
// constant still named sel_gold that had long since become cyan, and the UNSELECTED region fill
|
||||
// was blue (0.30,0.60,1.0) — one shade from the selected one — so an ordinary region read as
|
||||
// picked. Selection is a state, not a decoration: it gets its own colour and keeps it.
|
||||
inline ColorRGBA design_selection_color(float alpha = 1.0f)
|
||||
{
|
||||
return ColorRGBA(0.20f, 0.85f, 1.00f, alpha);
|
||||
}
|
||||
// Unselected geometry — 2D regions and faces — is neutral translucent grey, so the only
|
||||
// coloured thing in the viewport is the thing you picked.
|
||||
inline ColorRGBA design_idle_face_color()
|
||||
{
|
||||
return ColorRGBA(0.72f, 0.76f, 0.80f, 0.14f);
|
||||
}
|
||||
class DesignSketchTool {
|
||||
public:
|
||||
enum class Mode { Select, Dimension, Polyline, Line, CornerRect, CenterRect, ObliqueRect,
|
||||
@@ -70,6 +87,9 @@ public:
|
||||
// (Line's existing freeze flag) as the single "inline editor open" gate.
|
||||
void set_inline_busy(bool b) { m_awaiting_length = b; }
|
||||
bool inline_busy() const { return m_awaiting_length; } // true while a value field is open
|
||||
// Does the live session hold anything a cancel would throw away? Escape must not silently
|
||||
// destroy drawn geometry; the panel asks this before treating Escape as "discard sketch".
|
||||
bool live_sketch_has_work() const { return !m_entities.empty(); }
|
||||
bool constrain_value_anchor(wxPoint& out) const; // screen anchor over the picked constrain geometry
|
||||
|
||||
void begin(const SketchPlane& plane, Mode mode = Mode::Polyline);
|
||||
@@ -179,6 +199,11 @@ public:
|
||||
// loops from the committed-sketch overlay).
|
||||
std::vector<std::vector<int>> region_entity_indices(const std::vector<SketchEntity>& ents) const;
|
||||
void clear_display_pick() { m_display_pick = -1; m_display_pick_region = -1; }
|
||||
// Adopt a loop pick the tool did not make itself. The live-sketch path resolves the region
|
||||
// BEFORE the sketch is committed, so once finish_sketch() has turned it into a display
|
||||
// sketch there is nothing left that would set this — and selected_loop_entities(), which is
|
||||
// what Extrude consumes, reads exactly these two fields.
|
||||
void set_display_pick(int feature, int region) { m_display_pick = feature; m_display_pick_region = region; }
|
||||
|
||||
// Visual Extrude gizmo (C5b). The Extrude tool is a DesignPanel docked card, so the
|
||||
// sketch tool is NOT active during it; the panel feeds the profile plane + a 2D centroid
|
||||
@@ -465,6 +490,9 @@ public:
|
||||
// Force-close any open inline field (runs its cancel = keep-as-drawn). Used by the polyline
|
||||
// terminators (right-click / double-click) to end the chain even mid per-segment edit.
|
||||
std::function<void()> on_inline_dismiss;
|
||||
// Accept and close an open inline value field. dismiss() CANCELS; this one keeps the value,
|
||||
// which is what leaving a tool should do — see set_tool().
|
||||
std::function<void()> on_inline_commit;
|
||||
|
||||
// Bottom-right viewport readout: emitted each frame with the active tool's current
|
||||
// values (live segment length/angle while drawing a line, or the selected entity's
|
||||
@@ -485,7 +513,7 @@ public:
|
||||
std::function<void(const SketchProfile&, const SketchPlane&)> on_commit;
|
||||
// Emitted when a closed-loop face is clicked in Select mode (Onshape: a region
|
||||
// becomes a selectable face → extrude). The panel commits the sketch + extrudes.
|
||||
std::function<void()> on_face_selected;
|
||||
std::function<void(int)> on_face_selected; // region index into region_loops(m_entities)
|
||||
// Esc pressed while the tool is active: exit/cancel the session (the panel restores
|
||||
// Feature mode). Layered: an in-progress entity or a non-Select draw tool is dropped
|
||||
// first; a second Esc exits the session.
|
||||
@@ -797,6 +825,10 @@ private:
|
||||
void draw_vertices(GLModel& model, const std::vector<Vec2d>& pts, const ColorRGBA& color,
|
||||
double half_size = 1.3);
|
||||
void draw_fill(GLModel& model, const std::vector<Vec2d>& poly, const ColorRGBA& color);
|
||||
// Same, with the region's holes cut out, so a selected plate-with-a-hole is drawn as an
|
||||
// ANNULUS instead of a filled rectangle painted straight across its own bore.
|
||||
void draw_fill_holed(GLModel& model, const std::vector<Vec2d>& outer,
|
||||
const std::vector<std::vector<Vec2d>>& holes, const ColorRGBA& color);
|
||||
const ColorRGBA* sketch_hl_color(int feature) const;
|
||||
|
||||
bool m_active{false};
|
||||
|
||||
Reference in New Issue
Block a user