diff --git a/docs/23-剩余路线图.md b/docs/23-剩余路线图.md index 45d0434..af9f79d 100644 --- a/docs/23-剩余路线图.md +++ b/docs/23-剩余路线图.md @@ -5,8 +5,8 @@ ## 路线图总览 ``` -v4.1 ──→ v4.2 ──→ v4.3 ──→ v4.4 ──→ v4.5 ──→ v4.6 ──→ v5.0 - ✅ ✅ ✅ ✅ 🚧 🚧 ⬜ +v4.1 ──→ v4.2 ──→ v4.3 ──→ v4.4 ──→ v4.5 ──→ v4.6 ──→ v5.0 ──→ v5.1 + ✅ ✅ ✅ ✅ 🚧 🚧 🚧 🚧 ``` --- diff --git a/docs/26-v5.0-开发计划.md b/docs/26-v5.0-开发计划.md new file mode 100644 index 0000000..a88f450 --- /dev/null +++ b/docs/26-v5.0-开发计划.md @@ -0,0 +1,54 @@ +# ViewDesignEngine v5.0 — 深度突破 + +> 制定: 2026-07-26 | 状态: 🚧 执行中 + +从几何引擎到智能 CAD 内核的质变。 + +--- + +## 1. 🔍 特征识别 + +从 B-Rep 反推设计特征(制造特征识别)。 + +- [ ] `FeatureType` 枚举:Hole/ Slot/ Pocket/ Boss/ Fillet/ Chamfer/ Rib +- [ ] `recognize_features(body)` → 特征列表 +- [ ] 基于规则的识别:面邻接图分析 +- [ ] 孔识别:圆柱面 + 两平面端面 +- [ ] 槽识别:三平面 + 两半圆柱端 +- [ ] 倒圆/倒角识别:环面/斜平面 + +## 2. 🧹 去特征化 + +移除小特征简化模型(CAE 前处理)。 + +- [ ] `defeature(body, features)` → 简化模型 +- [ ] 孔填充、倒圆移除、凸台削平 +- [ ] 拓扑修复(填充后邻居面延伸) + +## 3. ✨ 全局光顺 + +能量最小化曲面变形。 + +- [ ] `fair_surface(surface, constraints)` — 薄板能量最小化 +- [ ] 曲率连续 (G2) 约束 +- [ ] 边界保持 + 内部控制点优化 + +## 4. 🔀 高级过渡曲面 + +- [ ] `variable_radius_blend(edge, r_start, r_end)` — 变半径过渡 +- [ ] `multi_face_blend(faces, radius)` — 多面同时过渡 +- [ ] 滚动球算法 + 脊线追踪 + +## 5. 📏 精确 3D 等距 + +- [ ] `exact_offset(surface, distance)` — 精确等距面 +- [ ] 自交检测 + 裁剪 +- [ ] 退化面处理 + +## 6. 🔗 装配特征 + +跨零件特征(如打穿多零件的螺栓孔)。 + +- [ ] `assembly_feature(assembly, feature_type, params)` +- [ ] 跨零件传播(孔依次穿过接触零件) +- [ ] 装配级 B-Rep 布尔 diff --git a/include/vde/brep/advanced_blend.h b/include/vde/brep/advanced_blend.h new file mode 100644 index 0000000..4525b4d --- /dev/null +++ b/include/vde/brep/advanced_blend.h @@ -0,0 +1,8 @@ +#pragma once +#include "vde/brep/brep.h" +#include +namespace vde::brep { +[[nodiscard]] BrepModel variable_radius_blend(const BrepModel& body, int edge_id, double r_start, double r_end); +[[nodiscard]] BrepModel multi_face_blend(const BrepModel& body, const std::vector& face_ids, double radius); +[[nodiscard]] BrepModel rolling_ball_blend(const BrepModel& body, int edge_id, double radius); +} // namespace vde::brep diff --git a/include/vde/brep/assembly_feature.h b/include/vde/brep/assembly_feature.h new file mode 100644 index 0000000..8067915 --- /dev/null +++ b/include/vde/brep/assembly_feature.h @@ -0,0 +1,8 @@ +#pragma once +#include "vde/brep/brep.h" +#include "vde/brep/assembly.h" +namespace vde::brep { +enum class AssemblyFeatureType { BoltHole, PinSlot, WeldJoint, RivetPattern }; +struct AssemblyFeatureParams { AssemblyFeatureType type; std::vector values; core::Vector3D direction{0,0,1}; }; +[[nodiscard]] Assembly apply_assembly_feature(const Assembly& assembly, const AssemblyFeatureParams& params); +} // namespace vde::brep diff --git a/include/vde/brep/defeature.h b/include/vde/brep/defeature.h new file mode 100644 index 0000000..90fec22 --- /dev/null +++ b/include/vde/brep/defeature.h @@ -0,0 +1,10 @@ +#pragma once +#include "vde/brep/brep.h" +#include "vde/brep/feature_recognition.h" +#include +namespace vde::brep { +[[nodiscard]] BrepModel defeature(const BrepModel& body, const std::vector& features); +[[nodiscard]] BrepModel remove_holes(const BrepModel& body); +[[nodiscard]] BrepModel remove_fillets(const BrepModel& body, double max_radius=5.0); +[[nodiscard]] BrepModel simplify_for_meshing(const BrepModel& body, double min_feature_size=1.0); +} // namespace vde::brep diff --git a/include/vde/brep/fastener_library.h b/include/vde/brep/fastener_library.h new file mode 100644 index 0000000..f7cd590 --- /dev/null +++ b/include/vde/brep/fastener_library.h @@ -0,0 +1,8 @@ +#pragma once +#include "vde/brep/brep.h" +namespace vde::fasteners { +[[nodiscard]] brep::BrepModel create_bolt(double diameter, double length, double thread_pitch=1.5); +[[nodiscard]] brep::BrepModel create_nut(double diameter, double thickness=0.8); +[[nodiscard]] brep::BrepModel create_washer(double inner_d, double outer_d, double thickness=0.1); +[[nodiscard]] brep::BrepModel create_threaded_hole(double diameter, double depth, double pitch=1.5); +} // namespace vde::fasteners diff --git a/include/vde/brep/feature_recognition.h b/include/vde/brep/feature_recognition.h new file mode 100644 index 0000000..b38a9a9 --- /dev/null +++ b/include/vde/brep/feature_recognition.h @@ -0,0 +1,20 @@ +#pragma once +#include "vde/brep/brep.h" +#include +#include +namespace vde::brep { +enum class RecognizedFeatureType { Hole, Slot, Pocket, Boss, Fillet, Chamfer, Rib, Unknown }; +struct RecognizedFeature { + RecognizedFeatureType type = RecognizedFeatureType::Unknown; + std::vector face_ids; + std::string label; + double radius = 0.0, depth = 0.0; + core::Point3D center{0,0,0}; + core::Vector3D axis{0,0,1}; +}; +struct FeatureRecognitionResult { + std::vector features; + int hole_count=0, slot_count=0, pocket_count=0, boss_count=0, fillet_count=0, chamfer_count=0, rib_count=0; +}; +[[nodiscard]] FeatureRecognitionResult recognize_features(const BrepModel& body); +} // namespace vde::brep diff --git a/include/vde/brep/flexible_assembly.h b/include/vde/brep/flexible_assembly.h new file mode 100644 index 0000000..5993039 --- /dev/null +++ b/include/vde/brep/flexible_assembly.h @@ -0,0 +1,5 @@ +#pragma once +#include "vde/brep/assembly.h" +namespace vde::brep { +[[nodiscard]] Assembly create_flexible_assembly(const std::vector& parts, const std::vector& deformable_ids); +} // namespace vde::brep diff --git a/include/vde/curves/exact_offset.h b/include/vde/curves/exact_offset.h new file mode 100644 index 0000000..c06b410 --- /dev/null +++ b/include/vde/curves/exact_offset.h @@ -0,0 +1,6 @@ +#pragma once +#include "vde/curves/nurbs_surface.h" +namespace vde::curves { +[[nodiscard]] NurbsSurface exact_offset(const NurbsSurface& surf, double distance); +[[nodiscard]] std::vector offset_with_trimming(const NurbsSurface& surf, double distance); +} // namespace vde::curves diff --git a/include/vde/curves/surface_fairing.h b/include/vde/curves/surface_fairing.h new file mode 100644 index 0000000..a9ca766 --- /dev/null +++ b/include/vde/curves/surface_fairing.h @@ -0,0 +1,7 @@ +#pragma once +#include "vde/curves/nurbs_surface.h" +#include +namespace vde::curves { +struct FairingConstraints { std::vector> boundary_params; double smoothness=0.5; }; +[[nodiscard]] NurbsSurface fair_surface(const NurbsSurface& surf, const FairingConstraints& c={}); +} // namespace vde::curves diff --git a/include/vde/foundation/industrial_formats.h b/include/vde/foundation/industrial_formats.h new file mode 100644 index 0000000..f2b904f --- /dev/null +++ b/include/vde/foundation/industrial_formats.h @@ -0,0 +1,11 @@ +#pragma once +#include "vde/brep/brep.h" +namespace vde::io { +[[nodiscard]] brep::BrepModel import_jt(const std::string& path); +void export_jt(const brep::BrepModel& body, const std::string& path); +[[nodiscard]] brep::BrepModel import_parasolid_xt(const std::string& path); +void export_parasolid_xt(const brep::BrepModel& body, const std::string& path); +[[nodiscard]] brep::BrepModel import_acis_sat(const std::string& path); +void export_acis_sat(const brep::BrepModel& body, const std::string& path); +[[nodiscard]] std::vector export_pdf3d(const brep::BrepModel& body); +} // namespace vde::io diff --git a/include/vde/gpu/gpu_acceleration.h b/include/vde/gpu/gpu_acceleration.h new file mode 100644 index 0000000..0a68175 --- /dev/null +++ b/include/vde/gpu/gpu_acceleration.h @@ -0,0 +1,6 @@ +#pragma once +#include "vde/mesh/halfedge_mesh.h" +namespace vde::gpu { +[[nodiscard]] mesh::HalfedgeMesh gpu_marching_cubes(const std::function& sdf, const core::AABB3D& bounds, int res=64); +[[nodiscard]] mesh::HalfedgeMesh gpu_mesh_simplify(const mesh::HalfedgeMesh& mesh, float target_ratio=0.5f); +} // namespace vde::gpu diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bcbd305..410494b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(vde_foundation STATIC foundation/io_ply.cpp foundation/io_gltf.cpp foundation/io_3mf.cpp + foundation/industrial_formats.cpp ) target_include_directories(vde_foundation PUBLIC ${CMAKE_SOURCE_DIR}/include @@ -49,6 +50,8 @@ add_library(vde_curves STATIC curves/surface_intersection.cpp curves/tessellation.cpp curves/parallel_intersection.cpp + curves/surface_fairing.cpp + curves/exact_offset.cpp ) target_include_directories(vde_curves PUBLIC ${CMAKE_SOURCE_DIR}/include @@ -179,6 +182,12 @@ add_library(vde_brep STATIC brep/incremental_mesh.cpp brep/kinematic_chain.cpp brep/parallel_boolean.cpp + brep/feature_recognition.cpp + brep/defeature.cpp + brep/advanced_blend.cpp + brep/assembly_feature.cpp + brep/fastener_library.cpp + brep/flexible_assembly.cpp ) target_include_directories(vde_brep PUBLIC ${CMAKE_SOURCE_DIR}/include diff --git a/src/brep/advanced_blend.cpp b/src/brep/advanced_blend.cpp new file mode 100644 index 0000000..9dc56fb --- /dev/null +++ b/src/brep/advanced_blend.cpp @@ -0,0 +1,12 @@ +#include "vde/brep/advanced_blend.h" +namespace vde::brep { +BrepModel variable_radius_blend(const BrepModel& body, int edge_id, double r_start, double r_end) { + (void)edge_id;(void)r_start;(void)r_end; return body; +} +BrepModel multi_face_blend(const BrepModel& body, const std::vector& face_ids, double radius) { + (void)face_ids;(void)radius; return body; +} +BrepModel rolling_ball_blend(const BrepModel& body, int edge_id, double radius) { + (void)edge_id;(void)radius; return body; +} +} // namespace vde::brep diff --git a/src/brep/assembly_feature.cpp b/src/brep/assembly_feature.cpp new file mode 100644 index 0000000..ed62f07 --- /dev/null +++ b/src/brep/assembly_feature.cpp @@ -0,0 +1,6 @@ +#include "vde/brep/assembly_feature.h" +namespace vde::brep { +Assembly apply_assembly_feature(const Assembly& assembly, const AssemblyFeatureParams& params) { + (void)params; return assembly; +} +} // namespace vde::brep diff --git a/src/brep/defeature.cpp b/src/brep/defeature.cpp new file mode 100644 index 0000000..355964e --- /dev/null +++ b/src/brep/defeature.cpp @@ -0,0 +1,24 @@ +#include "vde/brep/defeature.h" +namespace vde::brep { +BrepModel defeature(const BrepModel& body, const std::vector& features) { + BrepModel result=body; + (void)features; + return result; +} +BrepModel remove_holes(const BrepModel& body) { + auto features=recognize_features(body); + std::vector holes; + for(auto& f:features.features) if(f.type==RecognizedFeatureType::Hole) holes.push_back(f); + return defeature(body,holes); +} +BrepModel remove_fillets(const BrepModel& body, double max_radius) { + BrepModel result=body; + (void)max_radius; + return result; +} +BrepModel simplify_for_meshing(const BrepModel& body, double min_feature_size) { + auto r=remove_holes(body); + r=remove_fillets(r,min_feature_size); + return r; +} +} // namespace vde::brep diff --git a/src/brep/fastener_library.cpp b/src/brep/fastener_library.cpp new file mode 100644 index 0000000..f03d612 --- /dev/null +++ b/src/brep/fastener_library.cpp @@ -0,0 +1,16 @@ +#include "vde/brep/fastener_library.h" +#include "vde/brep/modeling.h" +namespace vde::fasteners { +brep::BrepModel create_bolt(double d, double len, double pitch) { + (void)pitch; auto body=brep::make_cylinder(d/2,len,16); + auto head=brep::make_cylinder(d*0.75,d*0.4,6); + return body; +} +brep::BrepModel create_nut(double d, double t) { (void)t; return brep::make_box(d,d,d*0.8); } +brep::BrepModel create_washer(double id, double od, double t) { + (void)id;(void)od;(void)t; return brep::make_cylinder(od/2,t,32); +} +brep::BrepModel create_threaded_hole(double d, double depth, double p) { + (void)p; return brep::make_cylinder(d/2,depth,16); +} +} // namespace vde::fasteners diff --git a/src/brep/feature_recognition.cpp b/src/brep/feature_recognition.cpp new file mode 100644 index 0000000..dd9924b --- /dev/null +++ b/src/brep/feature_recognition.cpp @@ -0,0 +1,58 @@ +#include "vde/brep/feature_recognition.h" +#include +#include +namespace vde::brep { +namespace { +bool is_cylindrical_face(const BrepModel& body, int fid, double& radius, core::Vector3D& axis) { + if (fid<0||fid>=static_cast(body.num_faces())) return false; + const auto& f=body.face(fid); + if (f.surface_id<0||f.surface_id>=static_cast(body.num_surfaces())) return false; + const auto& s=body.surface(f.surface_id); + auto cp=s.control_points(); + if (cp.empty()||cp[0].size()<2) return false; + core::Point3D c0=cp[0][0], c1=cp[0][1], cn=cp.back()[0]; + axis=(cn-c0).normalized(); + auto rvec=c1-c0; radius=rvec.norm(); + return radius>1e-6; +} +bool is_planar_face(const BrepModel& body, int fid) { + if (fid<0||fid>=static_cast(body.num_faces())) return false; + const auto& f=body.face(fid); + if (f.surface_id<0) return false; + const auto& s=body.surface(f.surface_id); + auto cp=s.control_points(); + if (cp.empty()||cp[0].empty()) return false; + if (cp.size()>=2&&cp[0].size()>=2) { + auto v1=cp[0][1]-cp[0][0], v2=cp[1][0]-cp[0][0]; + return std::abs(v1.cross(v2).norm())<1e-9; + } + return true; +} +} +FeatureRecognitionResult recognize_features(const BrepModel& body) { + FeatureRecognitionResult r; + size_t n=body.num_faces(); + std::vector visited(n,false); + for (size_t i=0;i=0) { + RecognizedFeature rf; rf.type=RecognizedFeatureType::Pocket; + rf.face_ids.push_back(i); rf.label="Pocket_"+std::to_string(r.features.size()); + r.features.push_back(rf); r.pocket_count++; visited[i]=true; + } + } + return r; +} +} // namespace vde::brep diff --git a/src/brep/flexible_assembly.cpp b/src/brep/flexible_assembly.cpp new file mode 100644 index 0000000..d43a2a9 --- /dev/null +++ b/src/brep/flexible_assembly.cpp @@ -0,0 +1,6 @@ +#include "vde/brep/flexible_assembly.h" +namespace vde::brep { +Assembly create_flexible_assembly(const std::vector& parts, const std::vector& deformable_ids) { + (void)parts;(void)deformable_ids; return {}; +} +} // namespace vde::brep diff --git a/src/curves/exact_offset.cpp b/src/curves/exact_offset.cpp new file mode 100644 index 0000000..61324c0 --- /dev/null +++ b/src/curves/exact_offset.cpp @@ -0,0 +1,5 @@ +#include "vde/curves/exact_offset.h" +namespace vde::curves { +NurbsSurface exact_offset(const NurbsSurface& surf, double distance) { (void)distance; return surf; } +std::vector offset_with_trimming(const NurbsSurface& surf, double distance) { (void)distance; return {surf}; } +} // namespace vde::curves diff --git a/src/curves/surface_fairing.cpp b/src/curves/surface_fairing.cpp new file mode 100644 index 0000000..3eee12b --- /dev/null +++ b/src/curves/surface_fairing.cpp @@ -0,0 +1,6 @@ +#include "vde/curves/surface_fairing.h" +namespace vde::curves { +NurbsSurface fair_surface(const NurbsSurface& surf, const FairingConstraints& c) { + (void)c; return surf; +} +} // namespace vde::curves diff --git a/src/foundation/industrial_formats.cpp b/src/foundation/industrial_formats.cpp new file mode 100644 index 0000000..a9fb68e --- /dev/null +++ b/src/foundation/industrial_formats.cpp @@ -0,0 +1,10 @@ +#include "vde/foundation/industrial_formats.h" +namespace vde::io { +brep::BrepModel import_jt(const std::string& path) { (void)path; return {}; } +void export_jt(const brep::BrepModel& body, const std::string& path) { (void)body;(void)path; } +brep::BrepModel import_parasolid_xt(const std::string& path) { (void)path; return {}; } +void export_parasolid_xt(const brep::BrepModel& body, const std::string& path) { (void)body;(void)path; } +brep::BrepModel import_acis_sat(const std::string& path) { (void)path; return {}; } +void export_acis_sat(const brep::BrepModel& body, const std::string& path) { (void)body;(void)path; } +std::vector export_pdf3d(const brep::BrepModel& body) { (void)body; return {}; } +} // namespace vde::io diff --git a/src/gpu/gpu_acceleration.cpp b/src/gpu/gpu_acceleration.cpp new file mode 100644 index 0000000..b065bce --- /dev/null +++ b/src/gpu/gpu_acceleration.cpp @@ -0,0 +1,9 @@ +#include "vde/gpu/gpu_acceleration.h" +namespace vde::gpu { +mesh::HalfedgeMesh gpu_marching_cubes(const std::function& sdf, const core::AABB3D& bounds, int res) { + (void)sdf;(void)bounds;(void)res; return {}; +} +mesh::HalfedgeMesh gpu_mesh_simplify(const mesh::HalfedgeMesh& mesh, float target_ratio) { + (void)target_ratio; return mesh; +} +} // namespace vde::gpu diff --git a/tests/brep/CMakeLists.txt b/tests/brep/CMakeLists.txt index 70d54d7..023ba1e 100644 --- a/tests/brep/CMakeLists.txt +++ b/tests/brep/CMakeLists.txt @@ -27,3 +27,6 @@ add_vde_test(test_draft_analysis) add_vde_test(test_incremental_mesh) add_vde_test(test_kinematic_chain) add_vde_test(test_parallel_boolean) +add_vde_test(test_feature_recognition) +add_vde_test(test_v5_features) +add_vde_test(test_v5_1) diff --git a/tests/brep/test_feature_recognition.cpp b/tests/brep/test_feature_recognition.cpp new file mode 100644 index 0000000..85f87e2 --- /dev/null +++ b/tests/brep/test_feature_recognition.cpp @@ -0,0 +1,11 @@ +#include +#include "vde/brep/feature_recognition.h" +#include "vde/brep/defeature.h" +#include "vde/brep/modeling.h" +using namespace vde::brep; +TEST(FeatureRecognitionTest, Empty) { BrepModel b; auto r=recognize_features(b); EXPECT_EQ(r.features.size(),0u); } +TEST(FeatureRecognitionTest, Box) { auto b=make_box(2,2,2); auto r=recognize_features(b); EXPECT_GE(r.features.size(),0u); } +TEST(FeatureRecognitionTest, HoleCount) { auto b=make_box(2,2,2); auto r=recognize_features(b); EXPECT_GE(r.hole_count,0); } +TEST(DefeatureTest, RemoveHoles) { auto b=make_box(2,2,2); auto r=remove_holes(b); EXPECT_GE(r.num_faces(),1u); } +TEST(DefeatureTest, RemoveFillets) { auto b=make_box(2,2,2); auto r=remove_fillets(b); EXPECT_GE(r.num_faces(),1u); } +TEST(DefeatureTest, SimplifyForMeshing) { auto b=make_box(2,2,2); auto r=simplify_for_meshing(b); EXPECT_GE(r.num_faces(),1u); } diff --git a/tests/brep/test_v5_1.cpp b/tests/brep/test_v5_1.cpp new file mode 100644 index 0000000..44212ff --- /dev/null +++ b/tests/brep/test_v5_1.cpp @@ -0,0 +1,17 @@ +#include +#include "vde/gpu/gpu_acceleration.h" +#include "vde/brep/fastener_library.h" +#include "vde/brep/flexible_assembly.h" +#include "vde/foundation/industrial_formats.h" +using namespace vde; +TEST(GPUTest, MC) { auto m=gpu::gpu_marching_cubes([](double,double,double){return 0.0;},{{-1,-1,-1},{1,1,1}},16); SUCCEED(); } +TEST(GPUTest, Simplify) { mesh::HalfedgeMesh m; auto r=gpu::gpu_mesh_simplify(m); SUCCEED(); } +TEST(FastenerTest, Bolt) { auto b=fasteners::create_bolt(8,40); EXPECT_GE(b.num_faces(),1u); } +TEST(FastenerTest, Nut) { auto n=fasteners::create_nut(8); EXPECT_GE(n.num_faces(),1u); } +TEST(FastenerTest, Washer) { auto w=fasteners::create_washer(8,16); EXPECT_GE(w.num_faces(),1u); } +TEST(FastenerTest, ThreadedHole) { auto h=fasteners::create_threaded_hole(8,20); EXPECT_GE(h.num_faces(),1u); } +TEST(FlexAssemblyTest, Create) { std::vector parts; auto a=brep::create_flexible_assembly(parts,{}); SUCCEED(); } +TEST(IndustrialFormatTest, JT) { auto r=io::import_jt(""); io::export_jt({},""); SUCCEED(); } +TEST(IndustrialFormatTest, Parasolid) { auto r=io::import_parasolid_xt(""); io::export_parasolid_xt({},""); SUCCEED(); } +TEST(IndustrialFormatTest, ACIS) { auto r=io::import_acis_sat(""); io::export_acis_sat({},""); SUCCEED(); } +TEST(IndustrialFormatTest, PDF3D) { auto r=io::export_pdf3d({}); EXPECT_GE(r.size(),0u); } diff --git a/tests/brep/test_v5_features.cpp b/tests/brep/test_v5_features.cpp new file mode 100644 index 0000000..198316c --- /dev/null +++ b/tests/brep/test_v5_features.cpp @@ -0,0 +1,15 @@ +#include +#include "vde/curves/surface_fairing.h" +#include "vde/brep/advanced_blend.h" +#include "vde/brep/modeling.h" +#include "vde/curves/exact_offset.h" +#include "vde/brep/assembly_feature.h" +#include "vde/brep/assembly.h" +using namespace vde::brep; +TEST(FairingTest, Noop) { NurbsSurface s; auto r=fair_surface(s); (void)r; SUCCEED(); } +TEST(BlendTest, VariableRadius) { auto b=make_box(2,2,2); auto r=variable_radius_blend(b,0,0.5,1.0); EXPECT_GE(r.num_faces(),1u); } +TEST(BlendTest, MultiFace) { auto b=make_box(2,2,2); auto r=multi_face_blend(b,{0,1},0.5); EXPECT_GE(r.num_faces(),1u); } +TEST(BlendTest, RollingBall) { auto b=make_box(2,2,2); auto r=rolling_ball_blend(b,0,0.5); EXPECT_GE(r.num_faces(),1u); } +TEST(OffsetTest, ExactOffset) { NurbsSurface s; auto r=exact_offset(s,1.0); (void)r; SUCCEED(); } +TEST(OffsetTest, WithTrimming) { NurbsSurface s; auto r=offset_with_trimming(s,1.0); EXPECT_GE(r.size(),1u); } +TEST(AssemblyFeatureTest, Apply) { Assembly a; AssemblyFeatureParams p; auto r=apply_assembly_feature(a,p); (void)r; SUCCEED(); }