mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 02:41:17 +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:
@@ -112,6 +112,9 @@ DesignCanvas::DesignCanvas(wxWindow* parent)
|
||||
m_sketch_tool.on_inline_dismiss = [this]() {
|
||||
if (m_inline_editor) m_inline_editor->cancel();
|
||||
};
|
||||
m_sketch_tool.on_inline_commit = [this]() {
|
||||
if (m_inline_editor) m_inline_editor->commit();
|
||||
};
|
||||
|
||||
// Bottom-right viewport HUD: a borderless, non-focusable float label showing the active
|
||||
// tool's current values. Top-level (a child widget is hidden by the GL surface, same as
|
||||
@@ -236,6 +239,18 @@ void DesignCanvas::request_repaint()
|
||||
return;
|
||||
m_canvas->set_as_dirty();
|
||||
|
||||
// NOTHING may touch GL before the canvas has initialised it. The backend probe below calls
|
||||
// OpenGLManager::get_gl_info().get_renderer(), which runs GLInfo::detect() -> glGetString
|
||||
// with no context current and, before init_opengl(), no loaded function pointers — a
|
||||
// segfault at startup with no window and nothing in the log. Anything that asks for a
|
||||
// repaint while the panel is still being built lands here, so the guard belongs at the top
|
||||
// rather than around the render() call: the crash was in the PROBE, not in the paint.
|
||||
if (!m_canvas->is_initialized()) {
|
||||
if (m_canvas_widget)
|
||||
m_canvas_widget->Refresh(); // the first real paint draws the current state anyway
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_sw_gl < 0) {
|
||||
// Cache the backend once it's known; the renderer string is empty until
|
||||
// GL is initialised, so stay "unknown" and take the safe direct path till then.
|
||||
@@ -274,7 +289,7 @@ void DesignCanvas::reload(bool keep_view)
|
||||
for (int i = 0; i < (int)m_model.objects.size(); ++i)
|
||||
m_canvas->load_object(m_model, i);
|
||||
|
||||
const ColorRGBA sel_gold(0.40f, 0.82f, 1.0f, 1.0f); // cyan tint = solid selected
|
||||
const ColorRGBA sel_gold = design_selection_color(); // same colour as every other selection
|
||||
const ColorRGBA ghost(0.26f, 0.66f, 1.0f, 0.45f);
|
||||
|
||||
const auto& volumes = m_canvas->get_volumes().volumes;
|
||||
@@ -553,7 +568,7 @@ void DesignCanvas::set_on_sketch_selection_changed(std::function<void(int)> cb)
|
||||
m_sketch_tool.on_selection_changed = std::move(cb);
|
||||
}
|
||||
|
||||
void DesignCanvas::set_on_sketch_face_selected(std::function<void()> cb)
|
||||
void DesignCanvas::set_on_sketch_face_selected(std::function<void(int)> cb)
|
||||
{
|
||||
m_sketch_tool.on_face_selected = std::move(cb);
|
||||
}
|
||||
@@ -583,6 +598,12 @@ void DesignCanvas::clear_loop_pick()
|
||||
m_sketch_tool.clear_display_pick();
|
||||
}
|
||||
|
||||
void DesignCanvas::set_loop_pick(int feature, int region)
|
||||
{
|
||||
m_sketch_tool.set_display_pick(feature, region);
|
||||
request_repaint();
|
||||
}
|
||||
|
||||
void DesignCanvas::set_solid_pick(const std::vector<CadBody>* bodies, const TriangleMesh* mesh,
|
||||
const std::vector<int>* tri_face, const std::vector<int>* tri_body,
|
||||
const std::vector<bool>* visible,
|
||||
@@ -1146,6 +1167,11 @@ bool DesignCanvas::inline_busy() const
|
||||
return m_sketch_tool.inline_busy();
|
||||
}
|
||||
|
||||
bool DesignCanvas::live_sketch_has_work() const
|
||||
{
|
||||
return m_sketch_tool.live_sketch_has_work();
|
||||
}
|
||||
|
||||
bool DesignCanvas::undo_last_sketch_entity()
|
||||
{
|
||||
const bool did = m_sketch_tool.undo_last_entity();
|
||||
|
||||
Reference in New Issue
Block a user