Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
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()
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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()
Ejemplo n.º 6
0
    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")
Ejemplo n.º 7
0
    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