feat: Replace SDF kernel with OpenCASCADE, VTK with pygfx

Major architecture migration:

- Remove SDF-based geometry kernel (sdf/)
- Remove VTK renderer (drawing_modules/)
- Remove old mesh modules (mesh_modules/)

New components:
- geometry/base.py: Abstract geometry kernel interface
- geometry_occ/kernel.py: OpenCASCADE implementation via CadQuery/OCP
- geometry_occ/sketch.py: 2D sketching with constraint solving
- rendering/base.py: Abstract renderer interface
- rendering/pygfx_renderer.py: WebGPU-based renderer
- models/data_model.py: Project, Component, Sketch, Body classes
- main.py: New Qt-based application

Features:
- STEP/IGES import/export
- Exact BRep geometry (vs approximate SDF mesh)
- Parametric sketching with constraints
- Boolean operations (union, difference, intersection)
- Fillet and chamfer operations
- Modern pygfx renderer (~30MB vs VTK ~200MB)

Dependencies:
- cadquery >= 2.4
- ocp >= 7.9.3
- pygfx >= 0.7.0
- wgpu >= 0.19.0
- PySide6 >= 6.9.0
This commit is contained in:
bklronin
2026-03-14 08:45:07 +01:00
parent d6044e551a
commit fe23ca610c
90 changed files with 3737 additions and 14523 deletions
+187
View File
@@ -0,0 +1,187 @@
"""Tests for Fluency CAD geometry kernel."""
import pytest
import numpy as np
from fluency.geometry_occ.kernel import OCGeometryKernel, OCCGeometryObject
from fluency.geometry_occ.sketch import OCCSketch
from fluency.geometry.base import Point2D
class TestOCGeometryKernel:
"""Tests for the OpenCASCADE geometry kernel."""
def test_kernel_creation(self):
"""Test kernel can be created."""
kernel = OCGeometryKernel()
assert kernel is not None
def test_create_point(self):
"""Test point creation."""
kernel = OCGeometryKernel()
point = kernel.create_point(10.0, 20.0)
assert point is not None
def test_create_line(self):
"""Test line creation."""
kernel = OCGeometryKernel()
start = Point2D(0, 0)
end = Point2D(10, 10)
line = kernel.create_line(start, end)
assert line is not None
def test_create_circle(self):
"""Test circle creation."""
kernel = OCGeometryKernel()
center = Point2D(0, 0)
circle = kernel.create_circle(center, 5.0)
assert circle is not None
def test_create_rectangle(self):
"""Test rectangle creation."""
kernel = OCGeometryKernel()
rect = kernel.create_rectangle(10.0, 20.0)
assert rect is not None
def test_create_polygon(self):
"""Test polygon creation."""
kernel = OCGeometryKernel()
points = [
Point2D(0, 0),
Point2D(10, 0),
Point2D(10, 10),
Point2D(0, 10),
]
polygon = kernel.create_polygon(points)
assert polygon is not None
def test_extrude_polygon(self):
"""Test extruding a polygon."""
kernel = OCGeometryKernel()
points = [
Point2D(0, 0),
Point2D(10, 0),
Point2D(10, 10),
Point2D(0, 10),
]
polygon = kernel.create_polygon(points)
extruded = kernel.extrude(polygon, 20.0)
assert extruded is not None
def test_get_mesh(self):
"""Test mesh generation."""
kernel = OCGeometryKernel()
points = [
Point2D(0, 0),
Point2D(10, 0),
Point2D(10, 10),
Point2D(0, 10),
]
polygon = kernel.create_polygon(points)
extruded = kernel.extrude(polygon, 20.0)
vertices, faces = kernel.get_mesh(extruded)
assert len(vertices) > 0
assert len(faces) > 0
assert vertices.shape[1] == 3
assert faces.shape[1] == 3
def test_get_bounding_box(self):
"""Test bounding box calculation."""
kernel = OCGeometryKernel()
points = [
Point2D(0, 0),
Point2D(10, 0),
Point2D(10, 10),
Point2D(0, 10),
]
polygon = kernel.create_polygon(points)
extruded = kernel.extrude(polygon, 20.0)
min_pt, max_pt = kernel.get_bounding_box(extruded)
assert min_pt.x <= max_pt.x
assert min_pt.y <= max_pt.y
assert min_pt.z <= max_pt.z
def test_get_volume(self):
"""Test volume calculation."""
kernel = OCGeometryKernel()
points = [
Point2D(0, 0),
Point2D(10, 0),
Point2D(10, 10),
Point2D(0, 10),
]
polygon = kernel.create_polygon(points)
extruded = kernel.extrude(polygon, 20.0)
volume = kernel.get_volume(extruded)
assert volume > 0
assert abs(volume - 2000.0) < 0.1
class TestOCCSketch:
"""Tests for the OpenCASCADE sketch."""
def test_sketch_creation(self):
"""Test sketch can be created."""
sketch = OCCSketch()
assert sketch is not None
def test_add_point(self):
"""Test adding a point."""
sketch = OCCSketch()
point = sketch.add_point(10.0, 20.0)
assert point is not None
assert point.entity_type == "point"
def test_add_line(self):
"""Test adding a line."""
sketch = OCCSketch()
p1 = sketch.add_point(0, 0)
p2 = sketch.add_point(10, 10)
line = sketch.add_line(p1, p2)
assert line is not None
assert line.entity_type == "line"
def test_add_circle(self):
"""Test adding a circle."""
sketch = OCCSketch()
center = sketch.add_point(0, 0)
circle = sketch.add_circle(center, 5.0)
assert circle is not None
assert circle.entity_type == "circle"
def test_add_rectangle(self):
"""Test adding a rectangle."""
sketch = OCCSketch()
entities = sketch.add_rectangle((0, 0), (10, 10))
assert len(entities) == 8
def test_get_points(self):
"""Test getting points."""
sketch = OCCSketch()
sketch.add_point(0, 0)
sketch.add_point(10, 10)
sketch.add_point(20, 20)
points = sketch.get_points()
assert len(points) == 3
def test_clear(self):
"""Test clearing sketch."""
sketch = OCCSketch()
sketch.add_point(0, 0)
sketch.add_point(10, 10)
sketch.clear()
points = sketch.get_points()
assert len(points) == 0
if __name__ == "__main__":
pytest.main([__file__, "-v"])