Files
fluencyCAD/.test_relocate_fallback.py
T
bklronin 9abeb6266a - Assembly instaiated operations
- assembly forward proagation
2026-08-19 20:14:14 +02:00

84 lines
3.1 KiB
Python

"""Headless smoke: the manual re-pick fallback path (prompt -> frame -> pick mode)."""
import os
import sys
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
import numpy as np
from OCP.gp import gp_Pnt, gp_Dir, gp_Ax2
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder
from OCP.BRepAlgoAPI import BRepAlgoAPI_Cut
from PySide6.QtWidgets import QApplication, QMessageBox
from fluency.ui.main_window import MainWindow
from fluency.models.data_model import Body
from fluency.geometry_occ.kernel import OCCGeometryObject
app = QApplication.instance() or QApplication([])
w = MainWindow()
comp = w._current_component
body = Body(name="plate")
comp.bodies[body.id] = body
box = BRepPrimAPI_MakeBox(60.0, 40.0, 5.0).Shape()
ax = gp_Ax2(gp_Pnt(10.0, 5.0, 0.0), gp_Dir(0, 0, 1))
box = BRepAlgoAPI_Cut(box, BRepPrimAPI_MakeCylinder(ax, 3.0, 6.0).Shape()).Shape()
body.geometry = OCCGeometryObject(box)
partner = w._project.add_component()
pbody = Body(name="partner")
partner.bodies[pbody.id] = pbody
pbody.geometry = OCCGeometryObject(BRepPrimAPI_MakeBox(30.0, 30.0, 10.0).Shape())
asm = w._project.get_active_assembly()
ac1 = asm.add_component_instance(comp.id, name="A")
ac2 = asm.add_component_instance(partner.id, name="B")
ac1.geom_cache[body.id] = body.geometry
c1 = ac1.add_connector(
position=(10.0, 5.0, 2.5), normal=(0, 0, 1), x_dir=(1, 0, 0),
source_obj_id=f"asm_{ac1.id}_{body.id}",
name="Conn hole", entity_type="cylindrical_face",
)
c1.partner_ac_id = ac2.id
# Activate the assembly view so the prompt path is taken.
w._assembly_view_active = True
w._selected_assembly_component_id = ac1.id
# Force a Yes from the question dialog, and record that it actually fired.
asked = {}
def fake_question(parent, title, text, buttons, default):
asked["title"] = title
return QMessageBox.StandardButton.Yes
QMessageBox.question = staticmethod(fake_question)
def fake_warning(*a, **k):
return QMessageBox.StandardButton.Ok
QMessageBox.warning = staticmethod(fake_warning)
w._prompt_relocate_unresolved([(asm, ac1, c1)])
assert asked.get("title") == "Connector Position Needed", asked
assert w._relocate_pending == [(asm, ac1, c1)], w._relocate_pending
assert w._viewer_3d._connector_pick_mode, "pick mode must be active"
# A wrong-part click must not consume the pending entry.
w._on_relocate_picked((0, 0, 0), (0, 0, 1), (1, 0, 0), "planar_face", f"asm_{ac2.id}_{pbody.id}")
assert w._relocate_pending == [(asm, ac1, c1)], "wrong part must not consume the pick"
# The right click re-homes and drains.
w._on_relocate_picked((40.0, 30.0, 2.5), (0, 0, 1), (1, 0, 0), "cylindrical_face", f"asm_{ac1.id}_{body.id}")
assert w._relocate_pending is None
assert np.allclose(c1.position, (40.0, 30.0, 2.5)), c1.position
assert not w._viewer_3d._connector_pick_mode, "pick mode must be off after completion"
# Esc cancel mid-flight clears the state.
w._relocate_pending = [(asm, ac1, c1)]
w._start_relocate_pick_next()
w._on_connector_pick_cancelled()
assert w._relocate_pending is None
assert not w._viewer_3d._connector_pick_mode
print("RELOCATE_PICK_FALLBACK_OK")