def __init__( self, tags: Tuple[str, ...], args: Tuple[Edge, ...], kind: ConstraintKind, param: Any = None, ): # validate based on the solver provided spec if kind not in ConstraintInvariants: raise ValueError(f"Unknown constraint {kind}.") arity, types, param_type, converter = ConstraintInvariants[kind] if arity != len(tags): raise ValueError( f"Invalid number of entities for constraint {kind}. Provided {len(tags)}, required {arity}." ) if any(e.geomType() not in types for e in args): raise ValueError( f"Unsupported geometry types {[e.geomType() for e in args]} for constraint {kind}." ) if not instance_of(param, param_type): raise ValueError( f"Unsupported argument types {get_type(param)}, required {param_type}." ) # if all is fine store everything and possibly convert the params self.tags = tags self.args = args self.kind = kind self.param = tcast(Any, converter)(param) if converter else param
def face( self: T, b: Union[Wire, Iterable[Edge], Compound, T], angle: Real = 0, mode: Modes = "a", tag: Optional[str] = None, ignore_selection: bool = False, ) -> T: """ Construct a face from a wire or edges. """ res: Union[Face, Sketch, Compound] if isinstance(b, Wire): res = Face.makeFromWires(b) elif isinstance(b, (Sketch, Compound)): res = b elif isinstance(b, Iterable): wires = edgesToWires(tcast(Iterable[Edge], b)) res = Face.makeFromWires(*(wires[0], wires[1:])) else: raise ValueError(f"Unsupported argument {b}") if angle != 0: res = res.moved(Location(Vector(), Vector(0, 0, 1), angle)) return self.each(lambda l: res.moved(l), mode, tag, ignore_selection)
def test_component_destroy_destroys_child_components(): component = Component(view=Mock(), presenter=Mock()) mock_cfactories = [MockComponentFactory() for _ in range(3)] mock_children = [tcast(Mock, component.new_component(mock_cfactory)) for mock_cfactory in mock_cfactories] component.destroy() for mock_child in mock_children: mock_child.destroy.assert_called_once_with()
def test_component_remove_component(): component = Component(view=Mock(), presenter=Mock()) mock_cfactory = MockComponentFactory() mock_child = tcast(Mock, component.new_component(mock_cfactory)) component.remove_component(mock_child) mock_child.destroy.assert_called_once_with() # Should raise an error if we try to remove again. with pytest.raises(ValueError): component.remove_component(mock_child)
def test_component_removes_child_if_child_is_destroyed(): component = Component(view=Mock(), presenter=Mock()) mock_cfactory = MockComponentFactory() new_child = tcast(MockComponent, component.new_component(mock_cfactory)) new_child.sim_destroy() # Should raise an error since child should have been automatically removed after it is destroyed. with pytest.raises(ValueError): component.remove_component(new_child) # Child should not be destroyed again by previous call to `component.remove_component()` new_child.destroy.assert_not_called()
def _validate(self, args: Tuple[Shape, ...], kind: ConstraintKind, param: Any): arity, marker_types, param_type, converter = ConstraintInvariants[kind] # check arity if arity != len(args): raise ValueError( f"Invalid number of entities for constraint {kind}. Provided {len(args)}, required {arity}." ) # check arguments arg_check: Dict[Any, Callable[[Shape], Any]] = { gp_Pnt: self._getPnt, gp_Dir: self._getAxis, gp_Pln: self._getPln, gp_Lin: self._getLin, None: lambda x: True, # dummy check for None marker } for a, t in zip(args, tcast(Tuple[Type[ConstraintMarker], ...], marker_types)): try: arg_check[t](a) except ValueError: raise ValueError(f"Unsupported entity {a} for constraint {kind}.") # check parameter if not instance_of(param, param_type) and param is not None: raise ValueError( f"Unsupported argument types {get_type(param)}, required {param_type}." ) # check parameter conversion try: if param is not None and converter: converter(param) except Exception as e: raise ValueError(f"Exception {e} occured in the parameter conversion")
def solve(self: T) -> T: """ Solve current constraints and update edge positions. """ entities = [] # list with all degrees of freedom e2i = {} # mapping from tags to indices of entities geoms = [] # geometry types # fill entities, e2i and geoms for i, (k, v) in enumerate( filter(lambda kv: isinstance(kv[1][0], Edge), self._tags.items())): v0 = tcast(Edge, v[0]) # dispatch on geom type if v0.geomType() == "LINE": p1 = v0.startPoint() p2 = v0.endPoint() ent: DOF = (p1.x, p1.y, p2.x, p2.y) elif v0.geomType() == "CIRCLE": p = v0.arcCenter() p1 = v0.startPoint() - p p2 = v0.endPoint() - p pm = v0.positionAt(0.5) - p a1 = Vector(0, 1).getSignedAngle(p1) a2 = p1.getSignedAngle(p2) a3 = p1.getSignedAngle(pm) if a3 > 0 and a2 < 0: a2 += 2 * pi elif a3 < 0 and a2 > 0: a2 -= 2 * pi radius = v0.radius() ent = (p.x, p.y, radius, a1, a2) else: continue entities.append(ent) e2i[k] = i geoms.append(v0.geomType()) # build the POD constraint list constraints = [] for c in self._constraints: ix = (e2i[c.tags[0]], e2i[c.tags[1]] if len(c.tags) == 2 else None) constraints.append((ix, c.kind, c.param)) # optimize solver = SketchConstraintSolver(entities, constraints, geoms) res, self._solve_status = solver.solve() self._solve_status["x"] = res # translate back the solution - update edges for g, (k, i) in zip(geoms, e2i.items()): el = res[i] # dispatch on geom type if g == "LINE": p1 = Vector(el[0], el[1]) p2 = Vector(el[2], el[3]) e = Edge.makeLine(p1, p2) elif g == "CIRCLE": p1 = Vector(*arc_first(el)) p2 = Vector(*arc_point(el, 0.5)) p3 = Vector(*arc_last(el)) e = Edge.makeThreePointArc(p1, p2, p3) # overwrite the low level object self._tags[k][0].wrapped = e.wrapped return self