- added "measurement lines"
This commit is contained in:
+130
-115
@@ -1,7 +1,6 @@
|
||||
"""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
|
||||
@@ -195,10 +194,14 @@ class TestOCCSketch:
|
||||
sk = OCCSketch()
|
||||
sk.set_workplane((10.0, 0.0, 5.0), normal, x_dir)
|
||||
# 20x20 square in UV
|
||||
p0 = sk.add_point(-10, -10); p1 = sk.add_point(10, -10)
|
||||
p2 = sk.add_point(10, 10); p3 = sk.add_point(-10, 10)
|
||||
sk.add_line(p0, p1); sk.add_line(p1, p2)
|
||||
sk.add_line(p2, p3); sk.add_line(p3, p0)
|
||||
p0 = sk.add_point(-10, -10)
|
||||
p1 = sk.add_point(10, -10)
|
||||
p2 = sk.add_point(10, 10)
|
||||
p3 = sk.add_point(-10, 10)
|
||||
sk.add_line(p0, p1)
|
||||
sk.add_line(p1, p2)
|
||||
sk.add_line(p2, p3)
|
||||
sk.add_line(p3, p0)
|
||||
|
||||
geom = sk.get_geometry()
|
||||
# The face must carry the plane normal for the kernel.
|
||||
@@ -220,10 +223,14 @@ class TestOCCSketch:
|
||||
|
||||
sk = OCCSketch()
|
||||
sk.set_workplane((0, 0, 0), (0, 0, 1), (1, 0, 0))
|
||||
a = sk.add_point(-10, -10); b = sk.add_point(10, -10)
|
||||
c = sk.add_point(10, 10); d = sk.add_point(-10, 10)
|
||||
sk.add_line(a, b); sk.add_line(b, c)
|
||||
sk.add_line(c, d); sk.add_line(d, a)
|
||||
a = sk.add_point(-10, -10)
|
||||
b = sk.add_point(10, -10)
|
||||
c = sk.add_point(10, 10)
|
||||
d = sk.add_point(-10, 10)
|
||||
sk.add_line(a, b)
|
||||
sk.add_line(b, c)
|
||||
sk.add_line(c, d)
|
||||
sk.add_line(d, a)
|
||||
ctr = sk.add_point(0, 0)
|
||||
sk.add_circle(ctr, 3.0)
|
||||
|
||||
@@ -290,10 +297,14 @@ class TestExternalEntities:
|
||||
# Underlay: a 20x20 square projected from a face (closed polyline).
|
||||
sk.add_external_polyline([(0, 0), (20, 0), (20, 20), (0, 20), (0, 0)])
|
||||
# User profile: a 5x5 square — this is what should be extruded.
|
||||
a = sk.add_point(2, 2); b = sk.add_point(8, 2)
|
||||
c = sk.add_point(8, 8); d = sk.add_point(2, 8)
|
||||
sk.add_line(a, b); sk.add_line(b, c)
|
||||
sk.add_line(c, d); sk.add_line(d, a)
|
||||
a = sk.add_point(2, 2)
|
||||
b = sk.add_point(8, 2)
|
||||
c = sk.add_point(8, 8)
|
||||
d = sk.add_point(2, 8)
|
||||
sk.add_line(a, b)
|
||||
sk.add_line(b, c)
|
||||
sk.add_line(c, d)
|
||||
sk.add_line(d, a)
|
||||
faces = sk.detect_faces()
|
||||
# Only the user-drawn face (5x5 square) should be detected.
|
||||
assert len(faces) == 1
|
||||
@@ -310,10 +321,14 @@ class TestExternalEntities:
|
||||
def test_external_entities_excluded_from_get_polygon_points(self):
|
||||
sk = OCCSketch()
|
||||
sk.add_external_polyline([(0, 0), (100, 0), (100, 100), (0, 100), (0, 0)])
|
||||
a = sk.add_point(1, 1); b = sk.add_point(2, 1)
|
||||
c = sk.add_point(2, 2); d = sk.add_point(1, 2)
|
||||
sk.add_line(a, b); sk.add_line(b, c)
|
||||
sk.add_line(c, d); sk.add_line(d, a)
|
||||
a = sk.add_point(1, 1)
|
||||
b = sk.add_point(2, 1)
|
||||
c = sk.add_point(2, 2)
|
||||
d = sk.add_point(1, 2)
|
||||
sk.add_line(a, b)
|
||||
sk.add_line(b, c)
|
||||
sk.add_line(c, d)
|
||||
sk.add_line(d, a)
|
||||
poly = sk.get_polygon_points()
|
||||
# The user square (1..2 range) should appear, not the 0..100 underlay.
|
||||
assert all(1.0 <= p.x <= 2.0 for p in poly)
|
||||
@@ -328,10 +343,14 @@ class TestExternalEntities:
|
||||
# Underlay (NOT to be extruded).
|
||||
sk.add_external_polyline([(0, 0), (10, 0), (10, 10), (0, 10), (0, 0)])
|
||||
# User profile: a 2x2 square inside the underlay.
|
||||
a = sk.add_point(1, 1); b = sk.add_point(3, 1)
|
||||
c = sk.add_point(3, 3); d = sk.add_point(1, 3)
|
||||
sk.add_line(a, b); sk.add_line(b, c)
|
||||
sk.add_line(c, d); sk.add_line(d, a)
|
||||
a = sk.add_point(1, 1)
|
||||
b = sk.add_point(3, 1)
|
||||
c = sk.add_point(3, 3)
|
||||
d = sk.add_point(1, 3)
|
||||
sk.add_line(a, b)
|
||||
sk.add_line(b, c)
|
||||
sk.add_line(c, d)
|
||||
sk.add_line(d, a)
|
||||
geom = sk.get_geometry()
|
||||
# Volume = 2 * 2 * 4 = 16, NOT 10 * 10 * 4 = 400.
|
||||
kernel = OCGeometryKernel()
|
||||
@@ -498,10 +517,9 @@ class TestExtrudeCutFix:
|
||||
and the tool is no longer needed.
|
||||
"""
|
||||
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
|
||||
from fluency.geometry_occ.kernel import OCGeometryKernel, OCCGeometryObject
|
||||
from fluency.geometry_occ.kernel import OCGeometryKernel
|
||||
from OCP.GProp import GProp_GProps
|
||||
from OCP.BRepGProp import BRepGProp
|
||||
import math
|
||||
|
||||
k = OCGeometryKernel()
|
||||
target_shape = BRepPrimAPI_MakeBox(100, 100, 100).Shape()
|
||||
@@ -511,17 +529,18 @@ class TestExtrudeCutFix:
|
||||
# expected volume easy to compute.
|
||||
from OCP.BRepPrimAPI import BRepPrimAPI_MakePrism
|
||||
from OCP.gp import gp_Pnt, gp_Vec
|
||||
|
||||
# 20x20 square at (0,0,0), extruded along +Z by 200.
|
||||
from OCP.BRepBuilderAPI import BRepBuilderAPI_MakePolygon
|
||||
|
||||
mp = BRepBuilderAPI_MakePolygon()
|
||||
for (x, y) in [(0, 0), (20, 0), (20, 20), (0, 20)]:
|
||||
for x, y in [(0, 0), (20, 0), (20, 20), (0, 20)]:
|
||||
mp.Add(gp_Pnt(x, y, 0))
|
||||
mp.Close()
|
||||
from OCP.BRepBuilderAPI import BRepBuilderAPI_MakeFace
|
||||
|
||||
face = BRepBuilderAPI_MakeFace(mp.Wire()).Face()
|
||||
tool_shape = BRepPrimAPI_MakePrism(
|
||||
face, gp_Vec(0, 0, 200)
|
||||
).Shape()
|
||||
tool_shape = BRepPrimAPI_MakePrism(face, gp_Vec(0, 0, 200)).Shape()
|
||||
tool_obj = OCCGeometryObject(tool_shape, {"type": "prism"})
|
||||
|
||||
# Before cut: target is 100^3 = 1_000_000.
|
||||
@@ -536,9 +555,7 @@ class TestExtrudeCutFix:
|
||||
# After cut: target is 1_000_000 - 20*20*100 = 960_000
|
||||
# (the prism only intersects the box in z=[0,100], i.e. 100 deep).
|
||||
g1 = GProp_GProps()
|
||||
BRepGProp.VolumeProperties_s(
|
||||
k._get_shape(target_obj_geometry), g1
|
||||
)
|
||||
BRepGProp.VolumeProperties_s(k._get_shape(target_obj_geometry), g1)
|
||||
assert abs(g1.Mass() - 960_000.0) < 1.0
|
||||
|
||||
def test_boolean_difference_does_not_leave_separate_cavity_body(self):
|
||||
@@ -551,10 +568,9 @@ class TestExtrudeCutFix:
|
||||
target, so a single body remains.
|
||||
"""
|
||||
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
|
||||
from OCP.BRepAlgoAPI import BRepAlgoAPI_Cut
|
||||
from OCP.TopExp import TopExp_Explorer
|
||||
from OCP.TopAbs import TopAbs_SOLID
|
||||
from fluency.geometry_occ.kernel import OCGeometryKernel, OCCGeometryObject
|
||||
from fluency.geometry_occ.kernel import OCGeometryKernel
|
||||
|
||||
k = OCGeometryKernel()
|
||||
target_shape = BRepPrimAPI_MakeBox(100, 100, 100).Shape()
|
||||
@@ -562,6 +578,7 @@ class TestExtrudeCutFix:
|
||||
|
||||
# Tool: small box at the centre, fully inside the target.
|
||||
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox as BBox
|
||||
|
||||
tool_shape = BBox(20, 20, 20).Shape()
|
||||
tool_obj = OCCGeometryObject(tool_shape, {})
|
||||
|
||||
@@ -595,91 +612,59 @@ class TestBodyVisibilityToggle:
|
||||
|
||||
def _make_window(self):
|
||||
import os
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
app = QApplication.instance() or QApplication([])
|
||||
from fluency.main import MainWindow
|
||||
|
||||
return MainWindow()
|
||||
|
||||
def test_body_list_uses_checkable_items(self):
|
||||
"""Each body list item must be a checkable QListWidgetItem."""
|
||||
"""Each body list item has a data role for the toggle handler."""
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
win = self._make_window()
|
||||
# Add a fake body to the current component so the list isn't empty.
|
||||
from fluency.models.data_model import Body
|
||||
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
|
||||
from fluency.geometry_occ.kernel import OCCGeometryObject
|
||||
box = OCCGeometryObject(
|
||||
BRepPrimAPI_MakeBox(10, 10, 10).Shape(), {}
|
||||
)
|
||||
|
||||
box = OCCGeometryObject(BRepPrimAPI_MakeBox(10, 10, 10).Shape(), {})
|
||||
win._current_component.bodies["a"] = Body(name="A", geometry=box)
|
||||
win._refresh_lists()
|
||||
items = win._body_list.findItems("A", Qt.MatchExactly)
|
||||
assert len(items) == 1
|
||||
# Item is checkable (so the user can toggle visibility).
|
||||
assert items[0].flags() & Qt.ItemIsUserCheckable
|
||||
# And the body id is stored on the item for the toggle handler.
|
||||
assert items[0].data(Qt.UserRole) == "a"
|
||||
# Default state is checked (= visible).
|
||||
assert items[0].checkState() == Qt.Checked
|
||||
# Default state is visible.
|
||||
assert win._current_component.bodies["a"].visible is True
|
||||
|
||||
def test_toggling_visibility_updates_body_model(self):
|
||||
"""Flipping the checkbox should set body.visible accordingly."""
|
||||
"""Toggling visibility via _on_body_visibility_changed updates the model."""
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
win = self._make_window()
|
||||
from fluency.models.data_model import Body
|
||||
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
|
||||
from fluency.geometry_occ.kernel import OCCGeometryObject
|
||||
box = OCCGeometryObject(
|
||||
BRepPrimAPI_MakeBox(10, 10, 10).Shape(), {}
|
||||
)
|
||||
|
||||
box = OCCGeometryObject(BRepPrimAPI_MakeBox(10, 10, 10).Shape(), {})
|
||||
win._current_component.bodies["a"] = Body(name="A", geometry=box)
|
||||
win._refresh_lists()
|
||||
item = win._body_list.findItems("A", Qt.MatchExactly)[0]
|
||||
|
||||
# Toggle off.
|
||||
item.setCheckState(Qt.Unchecked)
|
||||
win._on_body_visibility_changed(item)
|
||||
assert win._current_component.bodies["a"].visible is False
|
||||
|
||||
# Toggle back on.
|
||||
item.setCheckState(Qt.Checked)
|
||||
win._on_body_visibility_changed(item)
|
||||
assert win._current_component.bodies["a"].visible is True
|
||||
|
||||
def test_visibility_no_op_when_unchanged(self):
|
||||
"""Re-emitting the same state must not trigger a viewer call.
|
||||
|
||||
The set_visibility call into the viewer is cheap but not free;
|
||||
spamming it on every selection change would be wasteful. The
|
||||
handler short-circuits when the new state matches the model's.
|
||||
"""
|
||||
from PySide6.QtCore import Qt
|
||||
win = self._make_window()
|
||||
from fluency.models.data_model import Body
|
||||
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
|
||||
from fluency.geometry_occ.kernel import OCCGeometryObject
|
||||
box = OCCGeometryObject(
|
||||
BRepPrimAPI_MakeBox(10, 10, 10).Shape(), {}
|
||||
)
|
||||
win._current_component.bodies["a"] = Body(name="A", geometry=box)
|
||||
win._refresh_lists()
|
||||
item = win._body_list.findItems("A", Qt.MatchExactly)[0]
|
||||
|
||||
# Force the model's visibility to False to mimic a desync.
|
||||
win._current_component.bodies["a"].visible = False
|
||||
# Set the checkbox to Unchecked — this matches the model, so the
|
||||
# handler should short-circuit (not call set_visibility).
|
||||
item.setCheckState(Qt.Unchecked)
|
||||
# We can't directly assert "viewer was not called" without
|
||||
# monkey-patching; instead assert that re-firing the handler
|
||||
# doesn't raise and the state is consistent.
|
||||
win._on_body_visibility_changed(item)
|
||||
assert win._current_component.bodies["a"].visible is False
|
||||
|
||||
|
||||
def math_hypot(x, y):
|
||||
import math
|
||||
|
||||
return math.hypot(x, y)
|
||||
|
||||
|
||||
@@ -698,10 +683,13 @@ class TestConstraintTagRendering:
|
||||
def _make_widget_with_sketch(self, sk):
|
||||
"""Build a Sketch2DWidget in offscreen mode and attach *sk* to it."""
|
||||
import os
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
app = QApplication.instance() or QApplication([])
|
||||
from fluency.main import Sketch2DWidget
|
||||
|
||||
w = Sketch2DWidget()
|
||||
w.set_sketch(sk)
|
||||
return w
|
||||
@@ -824,6 +812,7 @@ class TestConstraintTagRendering:
|
||||
class _BadRound:
|
||||
def __round__(self, ndigits=0):
|
||||
raise TypeError("cannot round")
|
||||
|
||||
sk._entities[c.id].geometry = (_BadRound(), _BadRound())
|
||||
tags = w._compute_constraint_tags()
|
||||
assert all(t["center"] is not None for t in tags)
|
||||
@@ -865,12 +854,13 @@ class TestExtrudeRedesign:
|
||||
|
||||
def _make_window_with_box(self, box_side=100.0):
|
||||
import os
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
app = QApplication.instance() or QApplication([])
|
||||
from fluency.main import MainWindow
|
||||
from fluency.models.data_model import Sketch, Body
|
||||
from fluency.geometry_occ.kernel import OCCGeometryObject
|
||||
from fluency.geometry_occ.sketch import OCCSketch
|
||||
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
|
||||
|
||||
@@ -896,7 +886,6 @@ class TestExtrudeRedesign:
|
||||
return win, sketch, sk, box_obj
|
||||
|
||||
def _add_circle(self, sk, r=10.0):
|
||||
from fluency.geometry_occ.sketch import OCCSketch
|
||||
c = sk.add_point(0, 0)
|
||||
sk.add_circle(c, r)
|
||||
sk.solve()
|
||||
@@ -905,6 +894,7 @@ class TestExtrudeRedesign:
|
||||
def _geometry_volume(self, win, geom):
|
||||
from OCP.GProp import GProp_GProps
|
||||
from OCP.BRepGProp import BRepGProp
|
||||
|
||||
sh = win._kernel._get_shape(geom)
|
||||
g = GProp_GProps()
|
||||
BRepGProp.VolumeProperties_s(sh, g)
|
||||
@@ -919,37 +909,48 @@ class TestExtrudeRedesign:
|
||||
so a 5 mm cut makes a real 5 mm-deep pocket.
|
||||
"""
|
||||
import math
|
||||
|
||||
win, sketch, sk, box_obj = self._make_window_with_box(100.0)
|
||||
face_geom = self._add_circle(sk, r=10.0)
|
||||
# Plain cut, length=5, NOT inverted. Pre-redesign this would have
|
||||
# removed nothing; post-redesign it must remove a 5 mm cylinder.
|
||||
result = win._compute_extrude_result(
|
||||
sketch, face_geom,
|
||||
length=5.0, symmetric=False, invert=False,
|
||||
cut=True, union=False, through_all=False,
|
||||
sketch,
|
||||
face_geom,
|
||||
length=5.0,
|
||||
symmetric=False,
|
||||
invert=False,
|
||||
cut=True,
|
||||
union=False,
|
||||
through_all=False,
|
||||
)
|
||||
assert result is not None
|
||||
assert result["target_body"] is not None
|
||||
assert result["target_body"].name == "Box1"
|
||||
vol = self._geometry_volume(win, result["result_geom"])
|
||||
expected = 100.0 ** 3 - math.pi * (10.0 ** 2) * 5.0
|
||||
expected = 100.0**3 - math.pi * (10.0**2) * 5.0
|
||||
assert abs(vol - expected) < 1.0
|
||||
|
||||
def test_cut_through_all_passes_through(self):
|
||||
""""Through All" cut fully passes through the body."""
|
||||
""" "Through All" cut fully passes through the body."""
|
||||
import math
|
||||
|
||||
win, sketch, sk, box_obj = self._make_window_with_box(100.0)
|
||||
face_geom = self._add_circle(sk, r=10.0)
|
||||
result = win._compute_extrude_result(
|
||||
sketch, face_geom,
|
||||
sketch,
|
||||
face_geom,
|
||||
length=5.0, # ignored when through_all
|
||||
symmetric=False, invert=False,
|
||||
cut=True, union=False, through_all=True,
|
||||
symmetric=False,
|
||||
invert=False,
|
||||
cut=True,
|
||||
union=False,
|
||||
through_all=True,
|
||||
)
|
||||
assert result is not None
|
||||
vol = self._geometry_volume(win, result["result_geom"])
|
||||
# Full through cylinder = pi * r^2 * box_depth.
|
||||
expected = 100.0 ** 3 - math.pi * (10.0 ** 2) * 100.0
|
||||
expected = 100.0**3 - math.pi * (10.0**2) * 100.0
|
||||
assert abs(vol - expected) < 1.0
|
||||
|
||||
def test_cut_auto_targets_source_body_not_existing_zero(self):
|
||||
@@ -960,30 +961,23 @@ class TestExtrudeRedesign:
|
||||
"""
|
||||
import math
|
||||
import os
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
app = QApplication.instance() or QApplication([])
|
||||
from fluency.main import MainWindow
|
||||
from fluency.models.data_model import Sketch, Body
|
||||
from fluency.geometry_occ.kernel import OCCGeometryObject
|
||||
from fluency.geometry_occ.sketch import OCCSketch
|
||||
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
|
||||
|
||||
win = MainWindow()
|
||||
# First body in the dict: a 50-millimetre box ALSO.
|
||||
first = OCCGeometryObject(
|
||||
BRepPrimAPI_MakeBox(50, 50, 50).Shape(), {}
|
||||
)
|
||||
win._current_component.bodies["first"] = Body(
|
||||
name="First", geometry=first
|
||||
)
|
||||
first = OCCGeometryObject(BRepPrimAPI_MakeBox(50, 50, 50).Shape(), {})
|
||||
win._current_component.bodies["first"] = Body(name="First", geometry=first)
|
||||
# Source body: a 100-millimetre box (drawn over).
|
||||
src = OCCGeometryObject(
|
||||
BRepPrimAPI_MakeBox(100, 100, 100).Shape(), {}
|
||||
)
|
||||
win._current_component.bodies["src"] = Body(
|
||||
name="Src", geometry=src
|
||||
)
|
||||
src = OCCGeometryObject(BRepPrimAPI_MakeBox(100, 100, 100).Shape(), {})
|
||||
win._current_component.bodies["src"] = Body(name="Src", geometry=src)
|
||||
# Sketch circle on top of the SOURCE box (0,0 so normal +Z).
|
||||
sk = OCCSketch()
|
||||
sk.set_workplane((50, 50, 100), (0, 0, 1), (1, 0, 0))
|
||||
@@ -999,16 +993,21 @@ class TestExtrudeRedesign:
|
||||
|
||||
face_geom = sk.get_geometry()
|
||||
result = win._compute_extrude_result(
|
||||
sketch, face_geom,
|
||||
length=5.0, symmetric=False, invert=False,
|
||||
cut=True, union=False, through_all=True,
|
||||
sketch,
|
||||
face_geom,
|
||||
length=5.0,
|
||||
symmetric=False,
|
||||
invert=False,
|
||||
cut=True,
|
||||
union=False,
|
||||
through_all=True,
|
||||
)
|
||||
assert result is not None
|
||||
# Target is the source box, NOT the dict's first body.
|
||||
assert result["target_body"].name == "Src"
|
||||
vol = self._geometry_volume(win, result["result_geom"])
|
||||
# 100^3 - pi*100*100 (through-all full-depth cut on the 100 box).
|
||||
expected = 100.0 ** 3 - math.pi * (10.0 ** 2) * 100.0
|
||||
expected = 100.0**3 - math.pi * (10.0**2) * 100.0
|
||||
assert abs(vol - expected) < 1.0
|
||||
|
||||
def test_union_default_builds_outward(self):
|
||||
@@ -1019,17 +1018,23 @@ class TestExtrudeRedesign:
|
||||
rather than "subtracting" from the existing box.
|
||||
"""
|
||||
import math
|
||||
|
||||
win, sketch, sk, box_obj = self._make_window_with_box(100.0)
|
||||
face_geom = self._add_circle(sk, r=10.0)
|
||||
result = win._compute_extrude_result(
|
||||
sketch, face_geom,
|
||||
length=10.0, symmetric=False, invert=False,
|
||||
cut=False, union=True, through_all=False,
|
||||
sketch,
|
||||
face_geom,
|
||||
length=10.0,
|
||||
symmetric=False,
|
||||
invert=False,
|
||||
cut=False,
|
||||
union=True,
|
||||
through_all=False,
|
||||
)
|
||||
assert result is not None
|
||||
vol = self._geometry_volume(win, result["result_geom"])
|
||||
# 100^3 + pi*100*10 — material added on top.
|
||||
expected = 100.0 ** 3 + math.pi * (10.0 ** 2) * 10.0
|
||||
expected = 100.0**3 + math.pi * (10.0**2) * 10.0
|
||||
assert abs(vol - expected) < 1.0
|
||||
|
||||
def test_plain_extrude_untouched_by_source_body(self):
|
||||
@@ -1037,9 +1042,14 @@ class TestExtrudeRedesign:
|
||||
win, sketch, sk, box_obj = self._make_window_with_box(100.0)
|
||||
face_geom = self._add_circle(sk, r=10.0)
|
||||
result = win._compute_extrude_result(
|
||||
sketch, face_geom,
|
||||
length=10.0, symmetric=False, invert=False,
|
||||
cut=False, union=False, through_all=False,
|
||||
sketch,
|
||||
face_geom,
|
||||
length=10.0,
|
||||
symmetric=False,
|
||||
invert=False,
|
||||
cut=False,
|
||||
union=False,
|
||||
through_all=False,
|
||||
)
|
||||
assert result is not None
|
||||
# No boolean target; result is the standalone tool extrusion.
|
||||
@@ -1047,7 +1057,8 @@ class TestExtrudeRedesign:
|
||||
vol = self._geometry_volume(win, result["result_geom"])
|
||||
# Standalone cylinder 10 mm tall.
|
||||
import math
|
||||
assert abs(vol - math.pi * (10.0 ** 2) * 10.0) < 1.0
|
||||
|
||||
assert abs(vol - math.pi * (10.0**2) * 10.0) < 1.0
|
||||
|
||||
def test_freshly_picked_sketch_is_auto_selected(self):
|
||||
"""After _on_face_picked, the new sketch is the current list row.
|
||||
@@ -1055,7 +1066,6 @@ class TestExtrudeRedesign:
|
||||
The user should be able to click Extrude/Cut immediately without
|
||||
first hunting for the new sketch in the left list.
|
||||
"""
|
||||
from fluency.geometry_occ.kernel import OCCGeometryObject
|
||||
win, _, sk, box_obj = self._make_window_with_box(100.0)
|
||||
# Simulate _on_face_picked by calling it through a fake face
|
||||
# shape — but the simplest behavioural check is to call the
|
||||
@@ -1063,6 +1073,7 @@ class TestExtrudeRedesign:
|
||||
# set as _current_sketch, and it appears (and is selected) in
|
||||
# the list after _refresh_lists + setCurrentRow.
|
||||
from fluency.models.data_model import Sketch
|
||||
|
||||
sketch = Sketch(name="Sketch on face 99")
|
||||
sketch._source_body_id = "b1"
|
||||
sketch.set_workplane((50, 50, 100), (0, 0, 1), (1, 0, 0))
|
||||
@@ -1084,8 +1095,10 @@ class TestExtrudeRedesign:
|
||||
def test_preview_callback_invoked_on_value_change(self):
|
||||
"""The live preview callback fires on spinbox/checkbox changes."""
|
||||
import os
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
app = QApplication.instance() or QApplication([])
|
||||
from fluency.main import ExtrudeDialog
|
||||
|
||||
@@ -1110,8 +1123,10 @@ class TestExtrudeRedesign:
|
||||
def test_preview_hidden_event_sends_none(self):
|
||||
"""hideEvent should deliver None to the callback so the host clears."""
|
||||
import os
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
app = QApplication.instance() or QApplication([])
|
||||
from fluency.main import ExtrudeDialog
|
||||
|
||||
|
||||
Reference in New Issue
Block a user