feat(v3.8): 2D projection views + section + DXF + dimensions + G2 curvature
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user