2020-09-03 21:54:19 +02:00
|
|
|
import pytest
|
|
|
|
import os
|
2021-04-13 17:12:14 +09:30
|
|
|
from itertools import product
|
2020-09-03 21:54:19 +02:00
|
|
|
|
2021-07-03 05:59:10 +02:00
|
|
|
import nlopt
|
2020-09-03 21:54:19 +02:00
|
|
|
import cadquery as cq
|
2021-06-22 10:06:50 +02:00
|
|
|
from cadquery.occ_impl.exporters.assembly import (
|
|
|
|
exportAssembly,
|
|
|
|
exportCAF,
|
|
|
|
exportVTKJS,
|
|
|
|
exportVRML,
|
|
|
|
)
|
|
|
|
from cadquery.occ_impl.assembly import toJSON
|
2020-09-13 20:38:01 +02:00
|
|
|
from OCP.gp import gp_XYZ
|
|
|
|
|
2020-09-03 21:54:19 +02:00
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def simple_assy():
|
|
|
|
|
2020-09-06 17:45:32 +02:00
|
|
|
b1 = cq.Solid.makeBox(1, 1, 1)
|
2020-09-03 21:54:19 +02:00
|
|
|
b2 = cq.Workplane().box(1, 1, 2)
|
|
|
|
b3 = cq.Workplane().pushPoints([(0, 0), (-2, -5)]).box(1, 1, 3)
|
|
|
|
|
|
|
|
assy = cq.Assembly(b1, loc=cq.Location(cq.Vector(2, -5, 0)))
|
|
|
|
assy.add(b2, loc=cq.Location(cq.Vector(1, 1, 0)))
|
|
|
|
assy.add(b3, loc=cq.Location(cq.Vector(2, 3, 0)))
|
|
|
|
|
|
|
|
return assy
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def nested_assy():
|
|
|
|
|
2020-11-24 17:46:21 +01:00
|
|
|
b1 = cq.Workplane().box(1, 1, 1).faces("<Z").tag("top_face").end()
|
|
|
|
b2 = cq.Workplane().box(1, 1, 1).faces("<Z").tag("bottom_face").end()
|
|
|
|
b3 = (
|
|
|
|
cq.Workplane()
|
|
|
|
.pushPoints([(-2, 0), (2, 0)])
|
|
|
|
.tag("pts")
|
|
|
|
.box(1, 1, 0.5)
|
|
|
|
.tag("boxes")
|
|
|
|
)
|
2020-09-03 21:54:19 +02:00
|
|
|
|
2020-09-04 10:17:53 +02:00
|
|
|
assy = cq.Assembly(b1, loc=cq.Location(cq.Vector(0, 0, 0)), name="TOP")
|
|
|
|
assy2 = cq.Assembly(b2, loc=cq.Location(cq.Vector(0, 4, 0)), name="SECOND")
|
|
|
|
assy2.add(b3, loc=cq.Location(cq.Vector(0, 4, 0)), name="BOTTOM")
|
2020-09-03 21:54:19 +02:00
|
|
|
|
2020-09-06 17:45:32 +02:00
|
|
|
assy.add(assy2, color=cq.Color("green"))
|
2020-09-03 21:54:19 +02:00
|
|
|
|
|
|
|
return assy
|
|
|
|
|
2020-09-06 17:45:32 +02:00
|
|
|
|
2021-06-22 10:06:50 +02:00
|
|
|
@pytest.fixture
|
|
|
|
def empty_top_assy():
|
|
|
|
|
|
|
|
b1 = cq.Workplane().box(1, 1, 1)
|
|
|
|
|
|
|
|
assy = cq.Assembly()
|
|
|
|
assy.add(b1, color=cq.Color("green"))
|
|
|
|
|
|
|
|
return assy
|
|
|
|
|
|
|
|
|
2021-04-13 17:12:14 +09:30
|
|
|
@pytest.fixture
|
|
|
|
def box_and_vertex():
|
|
|
|
|
|
|
|
box_wp = cq.Workplane().box(1, 2, 3)
|
|
|
|
assy = cq.Assembly(box_wp, name="box")
|
|
|
|
vertex_wp = cq.Workplane().newObject([cq.Vertex.makeVertex(0, 0, 0)])
|
|
|
|
assy.add(vertex_wp, name="vertex")
|
|
|
|
return assy
|
|
|
|
|
|
|
|
|
2021-07-06 16:47:36 +09:30
|
|
|
def solve_result_check(solve_result: dict) -> bool:
|
|
|
|
checks = [
|
|
|
|
solve_result["status"] == nlopt.XTOL_REACHED,
|
|
|
|
solve_result["cost"] < 1e-9,
|
|
|
|
solve_result["iters"] > 0,
|
|
|
|
]
|
|
|
|
return all(checks)
|
|
|
|
|
|
|
|
|
2020-09-06 17:45:32 +02:00
|
|
|
def test_color():
|
|
|
|
|
|
|
|
c1 = cq.Color("red")
|
|
|
|
assert c1.wrapped.GetRGB().Red() == 1
|
|
|
|
assert c1.wrapped.Alpha() == 1
|
|
|
|
|
|
|
|
c2 = cq.Color(1, 0, 0)
|
|
|
|
assert c2.wrapped.GetRGB().Red() == 1
|
|
|
|
assert c2.wrapped.Alpha() == 1
|
|
|
|
|
|
|
|
c3 = cq.Color(1, 0, 0, 0.5)
|
|
|
|
assert c3.wrapped.GetRGB().Red() == 1
|
|
|
|
assert c3.wrapped.Alpha() == 0.5
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
cq.Color("?????")
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
cq.Color(1, 2, 3, 4, 5)
|
|
|
|
|
|
|
|
|
2020-09-03 21:54:19 +02:00
|
|
|
def test_assembly(simple_assy, nested_assy):
|
|
|
|
|
|
|
|
# basic checks
|
|
|
|
assert len(simple_assy.objects) == 3
|
|
|
|
assert len(simple_assy.children) == 2
|
|
|
|
assert len(simple_assy.shapes) == 1
|
|
|
|
|
|
|
|
assert len(nested_assy.objects) == 3
|
|
|
|
assert len(nested_assy.children) == 1
|
2020-09-13 20:38:01 +02:00
|
|
|
assert nested_assy.objects["SECOND"].parent is nested_assy
|
2020-09-03 21:54:19 +02:00
|
|
|
|
2020-10-01 08:33:03 +02:00
|
|
|
# bottom-up traversal
|
2020-09-03 21:54:19 +02:00
|
|
|
kvs = list(nested_assy.traverse())
|
|
|
|
|
|
|
|
assert kvs[0][0] == "BOTTOM"
|
|
|
|
assert len(kvs[0][1].shapes[0].Solids()) == 2
|
|
|
|
assert kvs[-1][0] == "TOP"
|
|
|
|
|
|
|
|
|
2020-09-04 10:17:53 +02:00
|
|
|
def test_step_export(nested_assy):
|
2020-09-03 21:54:19 +02:00
|
|
|
|
2020-09-04 10:17:53 +02:00
|
|
|
exportAssembly(nested_assy, "nested.step")
|
2020-09-03 21:54:19 +02:00
|
|
|
|
2020-09-04 10:17:53 +02:00
|
|
|
w = cq.importers.importStep("nested.step")
|
2020-09-03 21:54:19 +02:00
|
|
|
assert w.solids().size() == 4
|
|
|
|
|
|
|
|
# check that locations were applied correctly
|
|
|
|
c = cq.Compound.makeCompound(w.solids().vals()).Center()
|
|
|
|
c.toTuple()
|
2020-09-04 10:17:53 +02:00
|
|
|
assert pytest.approx(c.toTuple()) == (0, 4, 0)
|
2020-09-03 21:54:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_native_export(simple_assy):
|
|
|
|
|
|
|
|
exportCAF(simple_assy, "assy.xml")
|
|
|
|
|
|
|
|
# only sanity check for now
|
|
|
|
assert os.path.exists("assy.xml")
|
2020-09-04 11:48:28 +02:00
|
|
|
|
2020-09-06 17:45:32 +02:00
|
|
|
|
2021-06-22 10:06:50 +02:00
|
|
|
def test_vtkjs_export(nested_assy):
|
|
|
|
|
|
|
|
exportVTKJS(nested_assy, "assy")
|
|
|
|
|
|
|
|
# only sanity check for now
|
|
|
|
assert os.path.exists("assy.zip")
|
|
|
|
|
|
|
|
|
|
|
|
def test_vrml_export(simple_assy):
|
|
|
|
|
|
|
|
exportVRML(simple_assy, "assy.wrl")
|
|
|
|
|
|
|
|
# only sanity check for now
|
|
|
|
assert os.path.exists("assy.wrl")
|
|
|
|
|
|
|
|
|
|
|
|
def test_toJSON(simple_assy, nested_assy, empty_top_assy):
|
|
|
|
|
|
|
|
r1 = toJSON(simple_assy)
|
|
|
|
r2 = toJSON(simple_assy)
|
|
|
|
r3 = toJSON(empty_top_assy)
|
|
|
|
|
|
|
|
assert len(r1) == 3
|
|
|
|
assert len(r2) == 3
|
|
|
|
assert len(r3) == 1
|
|
|
|
|
|
|
|
|
2020-09-13 20:38:01 +02:00
|
|
|
def test_save(simple_assy, nested_assy):
|
2020-09-06 17:45:32 +02:00
|
|
|
|
|
|
|
simple_assy.save("simple.step")
|
2020-09-04 11:48:28 +02:00
|
|
|
assert os.path.exists("simple.step")
|
2020-09-06 17:45:32 +02:00
|
|
|
|
|
|
|
simple_assy.save("simple.xml")
|
2020-09-04 11:48:28 +02:00
|
|
|
assert os.path.exists("simple.xml")
|
2020-09-06 17:45:32 +02:00
|
|
|
|
|
|
|
simple_assy.save("simple.step")
|
2020-09-04 11:48:28 +02:00
|
|
|
assert os.path.exists("simple.step")
|
2020-09-06 17:45:32 +02:00
|
|
|
|
|
|
|
simple_assy.save("simple.stp", "STEP")
|
2020-09-04 11:48:28 +02:00
|
|
|
assert os.path.exists("simple.stp")
|
2020-09-06 17:45:32 +02:00
|
|
|
|
|
|
|
simple_assy.save("simple.caf", "XML")
|
2020-09-04 11:53:26 +02:00
|
|
|
assert os.path.exists("simple.caf")
|
2020-09-06 17:45:32 +02:00
|
|
|
|
2020-09-04 11:48:28 +02:00
|
|
|
with pytest.raises(ValueError):
|
2020-09-06 17:45:32 +02:00
|
|
|
simple_assy.save("simple.dxf")
|
|
|
|
|
2020-09-04 11:48:28 +02:00
|
|
|
with pytest.raises(ValueError):
|
2020-09-06 17:45:32 +02:00
|
|
|
simple_assy.save("simple.step", "DXF")
|
2020-09-13 20:38:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_constrain(simple_assy, nested_assy):
|
|
|
|
|
|
|
|
subassy1 = simple_assy.children[0]
|
|
|
|
subassy2 = simple_assy.children[1]
|
|
|
|
|
|
|
|
b1 = simple_assy.obj
|
|
|
|
b2 = subassy1.obj
|
|
|
|
b3 = subassy2.obj
|
|
|
|
|
|
|
|
simple_assy.constrain(
|
|
|
|
simple_assy.name, b1.Faces()[0], subassy1.name, b2.faces("<Z").val(), "Plane"
|
|
|
|
)
|
|
|
|
simple_assy.constrain(
|
|
|
|
simple_assy.name, b1.Faces()[0], subassy2.name, b3.faces("<Z").val(), "Axis"
|
|
|
|
)
|
|
|
|
simple_assy.constrain(
|
|
|
|
subassy1.name,
|
|
|
|
b2.faces(">Z").val(),
|
|
|
|
subassy2.name,
|
|
|
|
b3.faces("<Z").val(),
|
|
|
|
"Point",
|
|
|
|
)
|
|
|
|
|
|
|
|
assert len(simple_assy.constraints) == 3
|
|
|
|
|
2020-12-18 08:40:24 +01:00
|
|
|
nested_assy.constrain("TOP@faces@>Z", "SECOND/BOTTOM@faces@<Z", "Plane")
|
|
|
|
nested_assy.constrain("TOP@faces@>X", "SECOND/BOTTOM@faces@<X", "Axis")
|
2020-09-13 20:38:01 +02:00
|
|
|
|
2020-09-25 08:57:24 +02:00
|
|
|
assert len(nested_assy.constraints) == 2
|
2020-09-13 20:38:01 +02:00
|
|
|
|
|
|
|
constraint = nested_assy.constraints[0]
|
|
|
|
|
|
|
|
assert constraint.objects == ("TOP", "SECOND")
|
|
|
|
assert (
|
|
|
|
constraint.sublocs[0]
|
|
|
|
.wrapped.Transformation()
|
|
|
|
.TranslationPart()
|
|
|
|
.IsEqual(gp_XYZ(), 1e-9)
|
|
|
|
)
|
|
|
|
assert constraint.sublocs[1].wrapped.IsEqual(
|
2020-12-18 08:40:24 +01:00
|
|
|
nested_assy.objects["SECOND/BOTTOM"].loc.wrapped
|
2020-09-13 20:38:01 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
simple_assy.solve()
|
2020-09-25 08:57:24 +02:00
|
|
|
|
2021-07-06 16:47:36 +09:30
|
|
|
assert solve_result_check(simple_assy._solve_result)
|
2021-07-03 05:59:10 +02:00
|
|
|
|
2020-09-25 08:57:24 +02:00
|
|
|
assert (
|
|
|
|
simple_assy.loc.wrapped.Transformation()
|
|
|
|
.TranslationPart()
|
|
|
|
.IsEqual(gp_XYZ(2, -5, 0), 1e-9)
|
|
|
|
)
|
|
|
|
|
|
|
|
assert (
|
|
|
|
simple_assy.children[0]
|
|
|
|
.loc.wrapped.Transformation()
|
|
|
|
.TranslationPart()
|
2020-09-26 20:45:22 +02:00
|
|
|
.IsEqual(gp_XYZ(-1, 0.5, 0.5), 1e-6)
|
2020-09-25 08:57:24 +02:00
|
|
|
)
|
|
|
|
|
2020-09-13 20:38:01 +02:00
|
|
|
nested_assy.solve()
|
2020-09-25 08:57:24 +02:00
|
|
|
|
2021-07-06 16:47:36 +09:30
|
|
|
assert solve_result_check(nested_assy._solve_result)
|
2021-07-03 05:59:10 +02:00
|
|
|
|
2020-09-25 08:57:24 +02:00
|
|
|
assert (
|
|
|
|
nested_assy.children[0]
|
|
|
|
.loc.wrapped.Transformation()
|
|
|
|
.TranslationPart()
|
2020-09-26 20:45:22 +02:00
|
|
|
.IsEqual(gp_XYZ(2, -4, 0.75), 1e-6)
|
2020-09-25 08:57:24 +02:00
|
|
|
)
|
2020-11-24 17:46:21 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_constrain_with_tags(nested_assy):
|
|
|
|
|
|
|
|
nested_assy.add(None, name="dummy")
|
2020-12-18 08:40:24 +01:00
|
|
|
nested_assy.constrain("TOP?top_face", "SECOND/BOTTOM", "Plane")
|
2020-11-24 17:46:21 +01:00
|
|
|
|
|
|
|
assert len(nested_assy.constraints) == 1
|
|
|
|
|
|
|
|
# test selection of a non-shape object
|
|
|
|
with pytest.raises(ValueError):
|
2020-12-18 08:40:24 +01:00
|
|
|
nested_assy.constrain("SECOND/BOTTOM ? pts", "dummy", "Plane")
|
|
|
|
|
|
|
|
|
|
|
|
def test_duplicate_name(nested_assy):
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
nested_assy.add(None, name="SECOND")
|
2021-01-19 08:12:50 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_empty_solve(nested_assy):
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
nested_assy.solve()
|
2021-03-28 12:07:08 +02:00
|
|
|
|
2021-03-28 12:48:35 +02:00
|
|
|
|
2021-03-28 12:07:08 +02:00
|
|
|
def test_expression_grammar(nested_assy):
|
|
|
|
|
2021-03-28 12:22:33 +02:00
|
|
|
nested_assy.constrain(
|
2021-03-29 08:08:28 +02:00
|
|
|
"TOP@faces@>Z", "SECOND/BOTTOM@vertices@>X and >Y and >Z", "Point"
|
2021-03-28 12:22:33 +02:00
|
|
|
)
|
2021-04-13 17:12:14 +09:30
|
|
|
|
|
|
|
|
|
|
|
def test_PointInPlane_constraint(box_and_vertex):
|
|
|
|
|
|
|
|
# add first constraint
|
|
|
|
box_and_vertex.constrain(
|
|
|
|
"vertex",
|
|
|
|
box_and_vertex.children[0].obj.val(),
|
|
|
|
"box",
|
|
|
|
box_and_vertex.obj.faces(">X").val(),
|
|
|
|
"PointInPlane",
|
|
|
|
param=0,
|
|
|
|
)
|
|
|
|
box_and_vertex.solve()
|
2021-07-06 16:47:36 +09:30
|
|
|
solve_result_check(box_and_vertex._solve_result)
|
2021-04-13 17:12:14 +09:30
|
|
|
|
|
|
|
x_pos = (
|
|
|
|
box_and_vertex.children[0].loc.wrapped.Transformation().TranslationPart().X()
|
|
|
|
)
|
|
|
|
assert x_pos == pytest.approx(0.5)
|
|
|
|
|
|
|
|
# add a second PointInPlane constraint
|
|
|
|
box_and_vertex.constrain("vertex", "box@faces@>Y", "PointInPlane", param=0)
|
|
|
|
box_and_vertex.solve()
|
2021-07-06 16:47:36 +09:30
|
|
|
solve_result_check(box_and_vertex._solve_result)
|
2021-04-13 17:12:14 +09:30
|
|
|
|
|
|
|
vertex_translation_part = (
|
|
|
|
box_and_vertex.children[0].loc.wrapped.Transformation().TranslationPart()
|
|
|
|
)
|
|
|
|
# should still be on the >X face from the first constraint
|
|
|
|
assert vertex_translation_part.X() == pytest.approx(0.5)
|
|
|
|
# now should additionally be on the >Y face
|
|
|
|
assert vertex_translation_part.Y() == pytest.approx(1)
|
|
|
|
|
|
|
|
# add a third PointInPlane constraint
|
|
|
|
box_and_vertex.constrain("vertex", "box@faces@>Z", "PointInPlane", param=0)
|
|
|
|
box_and_vertex.solve()
|
2021-07-06 16:47:36 +09:30
|
|
|
solve_result_check(box_and_vertex._solve_result)
|
2021-04-13 17:12:14 +09:30
|
|
|
|
|
|
|
# should now be on the >X and >Y and >Z corner
|
|
|
|
assert (
|
|
|
|
box_and_vertex.children[0]
|
|
|
|
.loc.wrapped.Transformation()
|
|
|
|
.TranslationPart()
|
|
|
|
.IsEqual(gp_XYZ(0.5, 1, 1.5), 1e-6)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def test_PointInPlane_3_parts(box_and_vertex):
|
|
|
|
|
|
|
|
cylinder_height = 2
|
|
|
|
cylinder = cq.Workplane().circle(0.1).extrude(cylinder_height)
|
|
|
|
box_and_vertex.add(cylinder, name="cylinder")
|
|
|
|
box_and_vertex.constrain("box@faces@>Z", "cylinder@faces@<Z", "Plane")
|
|
|
|
box_and_vertex.constrain("vertex", "cylinder@faces@>Z", "PointInPlane")
|
|
|
|
box_and_vertex.constrain("vertex", "box@faces@>X", "PointInPlane")
|
|
|
|
box_and_vertex.solve()
|
2021-07-06 16:47:36 +09:30
|
|
|
solve_result_check(box_and_vertex._solve_result)
|
2021-04-13 17:12:14 +09:30
|
|
|
vertex_translation_part = (
|
|
|
|
box_and_vertex.children[0].loc.wrapped.Transformation().TranslationPart()
|
|
|
|
)
|
|
|
|
assert vertex_translation_part.Z() == pytest.approx(1.5 + cylinder_height)
|
|
|
|
assert vertex_translation_part.X() == pytest.approx(0.5)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("param1", [-1, 0, 2])
|
|
|
|
@pytest.mark.parametrize("param0", [-2, 0, 0.01])
|
|
|
|
def test_PointInPlane_param(box_and_vertex, param0, param1):
|
|
|
|
|
|
|
|
box_and_vertex.constrain("vertex", "box@faces@>Z", "PointInPlane", param=param0)
|
|
|
|
box_and_vertex.constrain("vertex", "box@faces@>X", "PointInPlane", param=param1)
|
|
|
|
box_and_vertex.solve()
|
2021-07-06 16:47:36 +09:30
|
|
|
solve_result_check(box_and_vertex._solve_result)
|
2021-04-13 17:12:14 +09:30
|
|
|
|
|
|
|
vertex_translation_part = (
|
|
|
|
box_and_vertex.children[0].loc.wrapped.Transformation().TranslationPart()
|
|
|
|
)
|
|
|
|
assert vertex_translation_part.Z() - 1.5 == pytest.approx(param0, abs=1e-6)
|
|
|
|
assert vertex_translation_part.X() - 0.5 == pytest.approx(param1, abs=1e-6)
|
|
|
|
|
|
|
|
|
2021-07-06 16:47:36 +09:30
|
|
|
def test_constraint_getPln():
|
2021-04-13 17:12:14 +09:30
|
|
|
"""
|
2021-07-06 16:47:36 +09:30
|
|
|
Test that _getPln does the right thing with different arguments
|
2021-04-13 17:12:14 +09:30
|
|
|
"""
|
|
|
|
ids = (0, 1)
|
|
|
|
sublocs = (cq.Location(), cq.Location())
|
|
|
|
|
|
|
|
def make_constraint(shape0):
|
|
|
|
return cq.Constraint(ids, (shape0, shape0), sublocs, "PointInPlane", 0)
|
|
|
|
|
|
|
|
def fail_this(shape0):
|
|
|
|
c0 = make_constraint(shape0)
|
|
|
|
with pytest.raises(ValueError):
|
2021-07-06 16:47:36 +09:30
|
|
|
c0._getPln(c0.args[0])
|
2021-04-13 17:12:14 +09:30
|
|
|
|
2021-07-06 16:47:36 +09:30
|
|
|
def resulting_pln(shape0):
|
2021-04-13 17:12:14 +09:30
|
|
|
c0 = make_constraint(shape0)
|
2021-07-06 16:47:36 +09:30
|
|
|
return c0._getPln(c0.args[0])
|
|
|
|
|
|
|
|
def resulting_plane(shape0):
|
|
|
|
p0 = resulting_pln(shape0)
|
|
|
|
return cq.Plane(
|
|
|
|
cq.Vector(p0.Location()),
|
|
|
|
cq.Vector(p0.XAxis().Direction()),
|
|
|
|
cq.Vector(p0.Axis().Direction()),
|
|
|
|
)
|
2021-04-13 17:12:14 +09:30
|
|
|
|
|
|
|
# point should fail
|
|
|
|
fail_this(cq.Vertex.makeVertex(0, 0, 0))
|
|
|
|
|
|
|
|
# line should fail
|
|
|
|
fail_this(cq.Edge.makeLine(cq.Vector(1, 0, 0), cq.Vector(0, 0, 0)))
|
|
|
|
|
|
|
|
# planar edge (circle) should succeed
|
|
|
|
origin = cq.Vector(1, 2, 3)
|
|
|
|
direction = cq.Vector(4, 5, 6).normalized()
|
|
|
|
p1 = resulting_plane(cq.Edge.makeCircle(1, pnt=origin, dir=direction))
|
|
|
|
assert p1.zDir == direction
|
|
|
|
assert p1.origin == origin
|
|
|
|
|
|
|
|
# planar edge (spline) should succeed
|
|
|
|
# it's a touch risky calling a spline a planar edge, but lets see if it's within tolerance
|
|
|
|
points0 = [cq.Vector(x) for x in [(-1, 0, 1), (0, 1, 1), (1, 0, 1), (0, -1, 1)]]
|
|
|
|
planar_spline = cq.Edge.makeSpline(points0, periodic=True)
|
|
|
|
p2 = resulting_plane(planar_spline)
|
|
|
|
assert p2.origin == planar_spline.Center()
|
|
|
|
assert p2.zDir == cq.Vector(0, 0, 1)
|
|
|
|
|
|
|
|
# non-planar edge should fail
|
|
|
|
points1 = [cq.Vector(x) for x in [(-1, 0, -1), (0, 1, 1), (1, 0, -1), (0, -1, 1)]]
|
|
|
|
nonplanar_spline = cq.Edge.makeSpline(points1, periodic=True)
|
|
|
|
fail_this(nonplanar_spline)
|
|
|
|
|
|
|
|
# planar wire should succeed
|
|
|
|
# make a triangle in the XZ plane
|
|
|
|
points2 = [cq.Vector(x) for x in [(-1, 0, -1), (0, 0, 1), (1, 0, -1)]]
|
|
|
|
points2.append(points2[0])
|
|
|
|
triangle = cq.Wire.makePolygon(points2)
|
|
|
|
p3 = resulting_plane(triangle)
|
|
|
|
assert p3.origin == triangle.Center()
|
|
|
|
assert p3.zDir == cq.Vector(0, 1, 0)
|
|
|
|
|
|
|
|
# non-planar wire should fail
|
|
|
|
points3 = [cq.Vector(x) for x in [(-1, 0, -1), (0, 1, 1), (1, 0, 0), (0, -1, 1)]]
|
|
|
|
wonky_shape = cq.Wire.makePolygon(points3)
|
|
|
|
fail_this(wonky_shape)
|
|
|
|
|
2021-07-06 16:47:36 +09:30
|
|
|
# all makePlane faces should succeed
|
2021-04-13 17:12:14 +09:30
|
|
|
for length, width in product([None, 10], [None, 11]):
|
|
|
|
f0 = cq.Face.makePlane(
|
|
|
|
length=length, width=width, basePnt=(1, 2, 3), dir=(1, 0, 0)
|
|
|
|
)
|
|
|
|
p4 = resulting_plane(f0)
|
|
|
|
if length and width:
|
|
|
|
assert p4.origin == cq.Vector(1, 2, 3)
|
|
|
|
assert p4.zDir == cq.Vector(1, 0, 0)
|
|
|
|
|
|
|
|
f1 = cq.Face.makeFromWires(triangle, [])
|
|
|
|
p5 = resulting_plane(f1)
|
|
|
|
# not sure why, but the origins only roughly line up
|
|
|
|
assert (p5.origin - triangle.Center()).Length < 0.1
|
|
|
|
assert p5.zDir == cq.Vector(0, 1, 0)
|
|
|
|
|
|
|
|
# shell... not sure?
|
|
|
|
|
|
|
|
# solid should fail
|
|
|
|
fail_this(cq.Solid.makeBox(1, 1, 1))
|
2021-04-20 03:20:31 +09:30
|
|
|
|
|
|
|
|
|
|
|
def test_toCompound(simple_assy, nested_assy):
|
|
|
|
|
|
|
|
c0 = simple_assy.toCompound()
|
|
|
|
assert isinstance(c0, cq.Compound)
|
|
|
|
assert len(c0.Solids()) == 4
|
|
|
|
|
|
|
|
c1 = nested_assy.toCompound()
|
|
|
|
assert isinstance(c1, cq.Compound)
|
|
|
|
assert len(c1.Solids()) == 4
|
|
|
|
|
|
|
|
# check nested assy location appears in compound
|
|
|
|
# create four boxes, stack them on top of each other, check highest face is in final compound
|
|
|
|
box0 = cq.Workplane().box(1, 1, 3, centered=(True, True, False))
|
|
|
|
box1 = cq.Workplane().box(1, 1, 4)
|
|
|
|
box2 = cq.Workplane().box(1, 1, 5)
|
|
|
|
box3 = cq.Workplane().box(1, 1, 6)
|
|
|
|
# top level assy
|
|
|
|
assy0 = cq.Assembly(box0, name="box0")
|
|
|
|
assy0.add(box1, name="box1")
|
|
|
|
assy0.constrain("box0@faces@>Z", "box1@faces@<Z", "Plane")
|
|
|
|
# subassy
|
|
|
|
assy1 = cq.Assembly()
|
|
|
|
assy1.add(box2, name="box2")
|
|
|
|
assy1.add(box3, name="box3")
|
|
|
|
assy1.constrain("box2@faces@>Z", "box3@faces@<Z", "Plane")
|
|
|
|
assy1.solve()
|
|
|
|
assy0.add(assy1, name="assy1")
|
|
|
|
assy0.constrain("box1@faces@>Z", "assy1/box2@faces@<Z", "Plane")
|
|
|
|
# before solving there should be no face with Center = (0, 0, 18)
|
|
|
|
c2 = assy0.toCompound()
|
|
|
|
assert not cq.Vector(0, 0, 18) in [x.Center() for x in c2.Faces()]
|
|
|
|
# after solving there should be a face with Center = (0, 0, 18)
|
|
|
|
assy0.solve()
|
|
|
|
c3 = assy0.toCompound()
|
|
|
|
assert cq.Vector(0, 0, 18) in [x.Center() for x in c3.Faces()]
|
|
|
|
# also check with bounding box
|
|
|
|
assert c3.BoundingBox().zlen == pytest.approx(18)
|
2021-07-06 16:47:36 +09:30
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("origin", [(0, 0, 0), (10, -10, 10)])
|
|
|
|
@pytest.mark.parametrize("normal", [(0, 0, 1), (-1, -1, 1)])
|
|
|
|
def test_infinite_face_constraint_PointInPlane(origin, normal):
|
|
|
|
"""
|
|
|
|
An OCCT infinite face has a center at (1e99, 1e99), but when a user uses it
|
|
|
|
in a constraint, the center should be basePnt.
|
|
|
|
"""
|
|
|
|
|
|
|
|
f0 = cq.Face.makePlane(length=None, width=None, basePnt=origin, dir=normal)
|
|
|
|
|
|
|
|
c0 = cq.assembly.Constraint(
|
|
|
|
("point", "plane"),
|
|
|
|
(cq.Vertex.makeVertex(10, 10, 10), f0),
|
|
|
|
sublocs=(cq.Location(), cq.Location()),
|
|
|
|
kind="PointInPlane",
|
|
|
|
)
|
|
|
|
p0 = c0._getPln(c0.args[1]) # a gp_Pln
|
|
|
|
derived_origin = cq.Vector(p0.Location())
|
|
|
|
assert derived_origin == cq.Vector(origin)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("kind", ["Plane", "PointInPlane", "Point"])
|
|
|
|
def test_infinite_face_constraint_Plane(kind):
|
|
|
|
|
|
|
|
assy = cq.Assembly(cq.Workplane().sphere(1), name="part0")
|
|
|
|
assy.add(cq.Workplane().sphere(1), name="part1")
|
|
|
|
assy.constrain(
|
|
|
|
"part0", cq.Face.makePlane(), "part1", cq.Face.makePlane(), kind,
|
|
|
|
)
|
|
|
|
assy.solve()
|
|
|
|
assert solve_result_check(assy._solve_result)
|