docs: v3.8 engineering drawing + G2 continuity plan
This commit is contained in:
@@ -556,6 +556,219 @@ BrepModel fillet(const BrepModel& body, int edge_id, double radius) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Variable-radius fillet ──
|
||||
BrepModel fillet_variable(const BrepModel& body, int edge_id,
|
||||
double r_start, double r_end, int samples) {
|
||||
int num_edges = static_cast<int>(body.num_edges());
|
||||
if (edge_id < 0 || edge_id >= num_edges) return body;
|
||||
if (r_start < 0.0 || r_end < 0.0) return body;
|
||||
if (r_start == 0.0 && r_end == 0.0) return body;
|
||||
|
||||
auto efs = body.edge_faces(edge_id);
|
||||
if (efs.empty()) return body;
|
||||
int face_a = efs[0];
|
||||
|
||||
const auto& edge = body.edge(edge_id);
|
||||
Point3D ep0 = body.vertex_by_id(edge.v_start).point;
|
||||
Point3D ep1 = body.vertex_by_id(edge.v_end).point;
|
||||
int face_b = -1;
|
||||
for (size_t fi = 0; fi < body.num_faces(); ++fi) {
|
||||
if (static_cast<int>(fi) == face_a) continue;
|
||||
auto f_edges = body.face_edges(static_cast<int>(fi));
|
||||
for (int ei : f_edges) {
|
||||
const auto& e2 = body.edge(ei);
|
||||
Point3D q0 = body.vertex_by_id(e2.v_start).point;
|
||||
Point3D q1 = body.vertex_by_id(e2.v_end).point;
|
||||
bool same_edge = ((ep0-q0).norm() < 1e-7 && (ep1-q1).norm() < 1e-7) ||
|
||||
((ep0-q1).norm() < 1e-7 && (ep1-q0).norm() < 1e-7);
|
||||
if (same_edge) { face_b = static_cast<int>(fi); break; }
|
||||
}
|
||||
if (face_b >= 0) break;
|
||||
}
|
||||
if (face_b < 0) return body;
|
||||
|
||||
const auto& curve = *edge.curve;
|
||||
Vector3D n_a = compute_face_normal(body, face_a);
|
||||
Vector3D n_b = compute_face_normal(body, face_b);
|
||||
|
||||
double cos_angle = n_a.dot(n_b);
|
||||
if (std::abs(cos_angle) > 0.9999) return body;
|
||||
|
||||
Vector3D bisector = (n_a + n_b).normalized();
|
||||
double cos_half = bisector.dot(n_a);
|
||||
if (std::abs(cos_half) < 1e-8) return body;
|
||||
|
||||
auto [t_min, t_max] = curve.domain();
|
||||
|
||||
int N = samples;
|
||||
std::vector<double> radii(N + 1);
|
||||
std::vector<Point3D> edge_pts(N + 1);
|
||||
std::vector<Point3D> t1_pts(N + 1);
|
||||
std::vector<Point3D> t2_pts(N + 1);
|
||||
std::vector<Point3D> mid_pts(N + 1);
|
||||
|
||||
for (int i = 0; i <= N; ++i) {
|
||||
double t_val = static_cast<double>(i) / N;
|
||||
double r = r_start + (r_end - r_start) * t_val;
|
||||
double t = t_min + (t_max - t_min) * t_val;
|
||||
radii[i] = r;
|
||||
edge_pts[i] = curve.evaluate(t);
|
||||
|
||||
double offset = r / cos_half;
|
||||
Point3D C = edge_pts[i] + offset * bisector;
|
||||
|
||||
t1_pts[i] = C - r * n_a;
|
||||
t2_pts[i] = C - r * n_b;
|
||||
|
||||
Vector3D to_mid = (t1_pts[i] + t2_pts[i]) * 0.5 - C;
|
||||
double mid_len = to_mid.norm();
|
||||
if (mid_len > 1e-10) {
|
||||
mid_pts[i] = C + to_mid.normalized() * r;
|
||||
} else {
|
||||
mid_pts[i] = (t1_pts[i] + t2_pts[i]) * 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
BrepModel result;
|
||||
|
||||
// ── 1. Copy non-affected faces verbatim ──
|
||||
for (size_t fi = 0; fi < body.num_faces(); ++fi) {
|
||||
if (static_cast<int>(fi) == face_a || static_cast<int>(fi) == face_b)
|
||||
continue;
|
||||
const auto& f_src = body.face(static_cast<int>(fi));
|
||||
std::vector<int> new_loop_ids;
|
||||
for (int li : f_src.loops) {
|
||||
const auto& lop = body.loop_by_id(li);
|
||||
std::vector<int> new_es;
|
||||
for (int ei : lop.edges) {
|
||||
const auto& e_src = body.edge(ei);
|
||||
Point3D p0 = body.vertex_by_id(e_src.v_start).point;
|
||||
Point3D p1 = body.vertex_by_id(e_src.v_end).point;
|
||||
int nv0 = result.add_vertex(p0);
|
||||
int nv1 = result.add_vertex(p1);
|
||||
new_es.push_back(result.add_edge(nv0, nv1));
|
||||
}
|
||||
new_loop_ids.push_back(result.add_loop(new_es, lop.is_outer));
|
||||
}
|
||||
int new_surf = result.add_surface(body.surface(f_src.surface_id));
|
||||
result.add_face(new_surf, new_loop_ids);
|
||||
}
|
||||
|
||||
// ── 2. Rebuild adjacent faces with variable tangent offset ──
|
||||
auto rebuild_face = [&](int face_id, const Vector3D& n,
|
||||
const std::vector<Point3D>& tangent_pts) {
|
||||
const auto& f_src = body.face(face_id);
|
||||
const auto& curve_copy = body.surface(f_src.surface_id);
|
||||
const auto& edge_src = body.edge(edge_id);
|
||||
|
||||
auto f_edges = body.face_edges(face_id);
|
||||
int fillet_edge_idx = -1;
|
||||
for (size_t ei = 0; ei < f_edges.size(); ++ei) {
|
||||
if (f_edges[ei] == edge_id) {
|
||||
fillet_edge_idx = static_cast<int>(ei);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fillet_edge_idx < 0) return;
|
||||
|
||||
std::vector<int> new_es;
|
||||
for (const auto& lop : body.all_loops()) {
|
||||
bool found = false;
|
||||
for (int li : f_src.loops) {
|
||||
if (lop.id == li) { found = true; break; }
|
||||
}
|
||||
if (!found) continue;
|
||||
|
||||
for (size_t ei = 0; ei < lop.edges.size(); ++ei) {
|
||||
int eidx = lop.edges[ei];
|
||||
if (eidx != edge_id) continue;
|
||||
|
||||
bool edge_reversed = (edge_src.v_start != body.edge(eidx).v_start);
|
||||
|
||||
std::vector<int> sorted_edges(lop.edges.size());
|
||||
int start_offset = static_cast<int>((ei + 1) % lop.edges.size());
|
||||
for (size_t j = 0; j < lop.edges.size(); ++j) {
|
||||
sorted_edges[j] = lop.edges[(start_offset + static_cast<int>(j)) % lop.edges.size()];
|
||||
}
|
||||
sorted_edges.insert(sorted_edges.begin(), edge_id);
|
||||
|
||||
Point3D t0 = edge_reversed ? tangent_pts.back() : tangent_pts.front();
|
||||
Point3D t1 = edge_reversed ? tangent_pts.front() : tangent_pts.back();
|
||||
new_es.push_back(result.add_edge(result.add_vertex(t0),
|
||||
result.add_vertex(t1)));
|
||||
|
||||
for (size_t j = 1; j < sorted_edges.size(); ++j) {
|
||||
int old_ei = sorted_edges[j];
|
||||
const auto& e_src = body.edge(old_ei);
|
||||
Point3D p0 = body.vertex_by_id(e_src.v_start).point;
|
||||
Point3D p1 = body.vertex_by_id(e_src.v_end).point;
|
||||
|
||||
bool v0_on = (e_src.v_start == edge_src.v_start ||
|
||||
e_src.v_start == edge_src.v_end);
|
||||
bool v1_on = (e_src.v_end == edge_src.v_start ||
|
||||
e_src.v_end == edge_src.v_end);
|
||||
|
||||
Point3D q0 = p0, q1 = p1;
|
||||
if (v0_on) q0 = (e_src.v_start == edge_src.v_start) ?
|
||||
tangent_pts.front() : tangent_pts.back();
|
||||
if (v1_on) q1 = (e_src.v_end == edge_src.v_start) ?
|
||||
tangent_pts.front() : tangent_pts.back();
|
||||
|
||||
new_es.push_back(result.add_edge(
|
||||
result.add_vertex(q0), result.add_vertex(q1)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!new_es.empty()) {
|
||||
int new_loop = result.add_loop(new_es, true);
|
||||
int new_surf = result.add_surface(curve_copy);
|
||||
result.add_face(new_surf, {new_loop});
|
||||
}
|
||||
};
|
||||
|
||||
rebuild_face(face_a, n_a, t1_pts);
|
||||
rebuild_face(face_b, n_b, t2_pts);
|
||||
|
||||
// ── 3. Build variable-radius fillet surface faces ──
|
||||
for (int i = 0; i < N; ++i) {
|
||||
Point3D q00 = t1_pts[i], q01 = t2_pts[i];
|
||||
Point3D q10 = t1_pts[i + 1], q11 = t2_pts[i + 1];
|
||||
|
||||
std::vector<std::vector<Point3D>> grid = {
|
||||
{q00, mid_pts[i], q01},
|
||||
{q10, mid_pts[i + 1], q11}
|
||||
};
|
||||
curves::NurbsSurface fs(grid,
|
||||
{0, 0, 1, 1},
|
||||
{0, 0, 0, 1, 1, 1},
|
||||
{}, 1, 2);
|
||||
|
||||
int v0 = result.add_vertex(q00), v1 = result.add_vertex(q10);
|
||||
int v2 = result.add_vertex(q11), v3 = result.add_vertex(q01);
|
||||
int e1 = result.add_edge(v0, v1);
|
||||
int e2 = result.add_edge(v1, v2);
|
||||
int e3 = result.add_edge(v2, v3);
|
||||
int e4 = result.add_edge(v3, v0);
|
||||
int surf_id = result.add_surface(fs);
|
||||
result.add_face(surf_id,
|
||||
{result.add_loop({e1, e2, e3, e4}, true)});
|
||||
}
|
||||
|
||||
// ── 4. Build shell and body ──
|
||||
std::vector<int> all_face_ids;
|
||||
for (size_t fi = 0; fi < result.num_faces(); ++fi) {
|
||||
all_face_ids.push_back(result.face(static_cast<int>(fi)).id);
|
||||
}
|
||||
if (!all_face_ids.empty()) {
|
||||
int sh = result.add_shell(all_face_ids, true);
|
||||
result.add_body({sh}, "fillet_variable");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Chamfer ──
|
||||
BrepModel chamfer(const BrepModel& body, int edge_id, double dist) {
|
||||
// Validation
|
||||
|
||||
Reference in New Issue
Block a user