diff --git a/src/slic3r/GUI/DesignCanvas.cpp b/src/slic3r/GUI/DesignCanvas.cpp index e5a1800ff3..e04f0d1b5f 100644 --- a/src/slic3r/GUI/DesignCanvas.cpp +++ b/src/slic3r/GUI/DesignCanvas.cpp @@ -1001,6 +1001,12 @@ void DesignCanvas::set_mate_connectors(std::vector> l) +{ + m_sketch_tool.set_mate_links(std::move(l)); + request_repaint(); +} + bool DesignCanvas::toggle_planes() { const bool on = m_sketch_tool.toggle_show_planes(); diff --git a/src/slic3r/GUI/DesignCanvas.hpp b/src/slic3r/GUI/DesignCanvas.hpp index d4bca46e29..3acb03f20e 100644 --- a/src/slic3r/GUI/DesignCanvas.hpp +++ b/src/slic3r/GUI/DesignCanvas.hpp @@ -197,6 +197,7 @@ public: std::vector sizes = {}); // draw datum/reference planes (u/v extents) // Mate connectors, drawn as frames so their verse and polarity are visible (snaporca-wgsc). void set_mate_connectors(std::vector g); + void set_mate_links(std::vector> l); void set_body_highlight(bool on); // tint the solid when its feature is tree-selected // The status line, shown along the BASE OF THE VIEWPORT rather than in the side panel: // the panel clips it at ~73 characters with no warning (snaporca-8cc), the viewport's diff --git a/src/slic3r/GUI/DesignPanel.cpp b/src/slic3r/GUI/DesignPanel.cpp index 1a31a27643..a2d65fa891 100644 --- a/src/slic3r/GUI/DesignPanel.cpp +++ b/src/slic3r/GUI/DesignPanel.cpp @@ -9728,6 +9728,10 @@ void DesignPanel::refresh_mate_connectors() const int cs_b = (m_active == Tool::Mate && m_mate_cs_b) ? m_mate_cs_b->GetSelection() : -1; std::vector out; + // Origins, keyed both ways: a committed mate names its connectors by FEATURE index, the open + // card's combos by ordinal. Only resolved frames land here, so an unresolved end simply draws + // no pair line rather than a line to the origin of the world. + std::map origin_by_feature, origin_by_ordinal; const auto frames = m_doc.resolve_datum_coordsys(); size_t k = 0; for (size_t i = 0; i < m_doc.features.size() && k < frames.size(); ++i) { @@ -9748,9 +9752,24 @@ void DesignPanel::refresh_mate_connectors() // is the case the glyph has to confess rather than absorb. g.roll_undefined = (f.coordsys_type == CoordSysType::FaceAndDirection && f.coordsys_edge < 0); + origin_by_feature[int(i)] = g.origin; + origin_by_ordinal[ordinal] = g.origin; out.push_back(g); } + + std::vector> links; + auto add_link = [&links](const std::map& m, int a, int b) { + const auto ia = m.find(a), ib = m.find(b); + if (ia != m.end() && ib != m.end()) links.emplace_back(ia->second, ib->second); + }; + for (const CadFeature& mf : m_doc.features) + if (mf.type == CadFeatureType::Mate && mf.enabled) + add_link(origin_by_feature, mf.mate_cs_a, mf.mate_cs_b); + if (cs_a >= 0 && cs_b >= 0 && cs_a != cs_b) + add_link(origin_by_ordinal, cs_a, cs_b); // the live pick, not yet committed + m_viewport->set_mate_connectors(std::move(out)); + m_viewport->set_mate_links(std::move(links)); } void DesignPanel::update_datum_gizmo() diff --git a/src/slic3r/GUI/DesignSketchTool.cpp b/src/slic3r/GUI/DesignSketchTool.cpp index 4fe6e038ec..a290336cbd 100644 --- a/src/slic3r/GUI/DesignSketchTool.cpp +++ b/src/slic3r/GUI/DesignSketchTool.cpp @@ -3680,6 +3680,35 @@ void DesignSketchTool::render_mate_connectors() arrow(Z, R * 2.10, body, g.role == 2); // +Z only. Nothing is ever drawn on -Z. } } + + // ---- the pair line. Dashed, drawn IN WORLD along the segment joining the two origins, so it + // foreshortens with the model and its length is the gap the mate has still to close. Screen- + // constant dashes like everything else here; the dash pitch opens up on a long span so a mate + // across a large assembly cannot emit thousands of segments. + for (const auto& lnk : m_mate_links) { + const Vec3d d = lnk.second - lnk.first; + const double L = d.norm(); + if (L < 1e-9) continue; + SketchPlane lp; + lp.origin = lnk.first; + lp.x_axis = d / L; + lp.y_axis = lp.x_axis.cross(cam.get_dir_forward().normalized()); + if (lp.y_axis.norm() < 1e-6) lp.y_axis = lp.x_axis.cross(up); // link along the view axis + lp.y_axis.normalize(); + lp.normal = lp.x_axis.cross(lp.y_axis); + m_plane = lp; + + std::vector> segs; + const double dash = 6.0 * upp; + const double step = std::max(dash + 4.0 * upp, L / 400.0); + for (double t = 0.0; t < L; t += step) + segs.emplace_back(Vec2d(t, 0.0), Vec2d(std::min(t + dash, L), 0.0)); + // Depth off: the line's job is to say "these two belong together", and it has to say it + // even when the parts it spans are between the camera and one of the ends. + glsafe(::glDisable(GL_DEPTH_TEST)); + draw_strokes(m_mc_stroke_model, segs, lw, grey); + } + m_plane = saved; } diff --git a/src/slic3r/GUI/DesignSketchTool.hpp b/src/slic3r/GUI/DesignSketchTool.hpp index c236a10085..df05085a8e 100644 --- a/src/slic3r/GUI/DesignSketchTool.hpp +++ b/src/slic3r/GUI/DesignSketchTool.hpp @@ -337,7 +337,11 @@ public: bool roll_undefined{false}; }; void set_mate_connectors(std::vector g) { m_mate_connectors = std::move(g); } - void clear_mate_connectors() { m_mate_connectors.clear(); } + // The pair line: the two origins of every mate — committed ones, plus the live pick of an open + // Mate card. Two connectors a mate binds are ONE object with a gap still in it; drawn as two + // separate frames they read as unrelated. + void set_mate_links(std::vector> l) { m_mate_links = std::move(l); } + void clear_mate_connectors() { m_mate_connectors.clear(); m_mate_links.clear(); } // Visual Revolve gizmo. The panel feeds the sketch plane + profile centroid + axis (0=plane X, // 1=plane Y) + angle + flip while its Revolve card is open; an angle-arc is drawn in the @@ -1085,6 +1089,7 @@ private: void render_mate_face(const Vec3d& origin, const Vec3d& X, const Vec3d& Y, const Vec3d& Z, double R, const ColorRGBA& body); std::vector m_mate_connectors; + std::vector> m_mate_links; GLModel m_mc_stroke_model; GLModel m_mc_fill_model; // the face treatment's shaded facets GLModel m_solid_face_model;