feat(v3.8): 2D projection views + section + DXF + dimensions + G2 curvature
CI / Build & Test (push) Failing after 41s
CI / Release Build (push) Failing after 35s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled

This commit is contained in:
茂之钳
2026-07-24 15:23:30 +00:00
parent 6779b45f27
commit d2c628215d
5 changed files with 446 additions and 0 deletions
+1
View File
@@ -145,6 +145,7 @@ add_library(vde::engine ALIAS vde)
add_library(vde_brep STATIC
brep/brep.cpp
brep/modeling.cpp
brep/brep_drawing.cpp
brep/step_export.cpp
brep/step_import.cpp
brep/iges_import.cpp
+152
View File
@@ -0,0 +1,152 @@
#include "vde/brep/brep_drawing.h"
#include <fstream>
#include <algorithm>
#include <cmath>
namespace vde::brep {
using core::Point3D;
using core::Vector3D;
namespace {
// Project a 3D point to 2D based on view direction
std::pair<double,double> project(const Point3D& p, const Vector3D& dir) {
// Front view (look down -Z): project to XY
if (std::abs(dir.z() + 1.0) < 0.1) return {p.x(), p.y()};
// Top view (look down -Y): project to XZ, map Z→Y
if (std::abs(dir.y() + 1.0) < 0.1) return {p.x(), -p.z()};
// Right view (look from +X): project to YZ, map Z→X, Y→Y
if (std::abs(dir.x() - 1.0) < 0.1) return {p.z(), p.y()};
// Default: XY plane
return {p.x(), p.y()};
}
void add_edge_segments(const BrepModel& body, ProjectionView& view, const Vector3D& dir) {
for (size_t ei = 0; ei < body.num_edges(); ++ei) {
const auto& e = body.edge(static_cast<int>(ei));
// Get vertex positions
Point3D p0(0,0,0), p1(0,0,0);
bool found0 = false, found1 = false;
for (size_t vi = 0; vi < body.num_vertices(); ++vi) {
const auto& v = body.vertex(static_cast<int>(vi));
if (v.id == e.v_start) { p0 = v.point; found0 = true; }
if (v.id == e.v_end) { p1 = v.point; found1 = true; }
}
if (!found0 || !found1) continue;
auto [x1, y1] = project(p0, dir);
auto [x2, y2] = project(p1, dir);
view.segments.push_back({x1, y1, x2, y2, false});
}
}
} // namespace
std::vector<ProjectionView> generate_views(const BrepModel& body) {
std::vector<ProjectionView> views;
// Front view: looking from -Z
ProjectionView front;
front.name = "Front";
front.view_direction = Vector3D(0, 0, -1);
add_edge_segments(body, front, front.view_direction);
views.push_back(std::move(front));
// Top view: looking from -Y
ProjectionView top;
top.name = "Top";
top.view_direction = Vector3D(0, -1, 0);
add_edge_segments(body, top, top.view_direction);
views.push_back(std::move(top));
// Right view: looking from +X
ProjectionView right;
right.name = "Right";
right.view_direction = Vector3D(1, 0, 0);
add_edge_segments(body, right, right.view_direction);
views.push_back(std::move(right));
return views;
}
ProjectionView section_view(const BrepModel& body,
const Point3D& plane_pt, const Vector3D& plane_n) {
ProjectionView view;
view.name = "Section";
view.view_direction = plane_n;
Vector3D n = plane_n.normalized();
for (size_t ei = 0; ei < body.num_edges(); ++ei) {
const auto& e = body.edge(static_cast<int>(ei));
Point3D p0(0,0,0), p1(0,0,0);
bool f0 = false, f1 = false;
for (size_t vi = 0; vi < body.num_vertices(); ++vi) {
const auto& v = body.vertex(static_cast<int>(vi));
if (v.id == e.v_start) { p0 = v.point; f0 = true; }
if (v.id == e.v_end) { p1 = v.point; f1 = true; }
}
if (!f0 || !f1) continue;
double d0 = (p0 - plane_pt).dot(n);
double d1 = (p1 - plane_pt).dot(n);
// Edge crosses the section plane
if (d0 * d1 < 0 || std::abs(d0) < 1e-9 || std::abs(d1) < 1e-9) {
double t = (std::abs(d0) < 1e-9) ? 0.0 : d0 / (d0 - d1);
t = std::clamp(t, 0.0, 1.0);
Point3D isect = p0 + t * (p1 - p0);
auto [sx, sy] = project(isect, plane_n);
// Store as point (degenerate segment marking intersection location)
view.segments.push_back({sx, sy, sx, sy, false});
}
}
return view;
}
bool export_dxf(const std::string& filepath, const std::vector<ProjectionView>& views) {
std::ofstream f(filepath);
if (!f) return false;
f << "0\nSECTION\n2\nENTITIES\n";
for (const auto& view : views) {
for (const auto& seg : view.segments) {
f << "0\nLINE\n8\n" << view.name << "\n";
f << "10\n" << seg.x1 << "\n20\n" << seg.y1 << "\n";
f << "11\n" << seg.x2 << "\n21\n" << seg.y2 << "\n";
}
}
f << "0\nENDSEC\n0\nEOF\n";
return true;
}
std::vector<Dimension> auto_dimension(const ProjectionView& view) {
if (view.segments.empty()) return {};
// Find bounding box
double xmin = view.segments[0].x1, xmax = view.segments[0].x1;
double ymin = view.segments[0].y1, ymax = view.segments[0].y1;
for (auto& s : view.segments) {
xmin = std::min({xmin, s.x1, s.x2});
xmax = std::max({xmax, s.x1, s.x2});
ymin = std::min({ymin, s.y1, s.y2});
ymax = std::max({ymax, s.y1, s.y2});
}
std::vector<Dimension> dims;
double offset = 10.0;
// Overall width
char buf[64];
snprintf(buf, sizeof(buf), "%.1f", xmax - xmin);
dims.push_back({DimType::Linear, xmin, ymin - offset, xmax, ymin - offset, buf});
// Overall height
snprintf(buf, sizeof(buf), "%.1f", ymax - ymin);
dims.push_back({DimType::Linear, xmin - offset, ymin, xmin - offset, ymax, buf});
return dims;
}
} // namespace vde::brep