Esempio n. 1
0
    def edges(self):
        """Return the four edge curves in (parametric) order: umin, umax, vmin, vmax

        :return: Edge curves
        :rtype: (Curve)
        """
        return tuple(self.section(*args) for args in sections(2, 1))
Esempio n. 2
0
    def faces(self):
        """Return the six faces of this volume in order: umin, umax, vmin, vmax, wmin, wmax.

        :return: Boundary faces
        :rtype: (Surface)
        """
        return tuple(self.section(*args) for args in sections(3, 2))
Esempio n. 3
0
    def edges(self):
        """Return the four edge curves in (parametric) order: umin, umax, vmin, vmax

        :return: Edge curves
        :rtype: (Curve)
        """
        return tuple(self.section(*args) for args in sections(2, 1))
Esempio n. 4
0
    def faces(self):
        """Return the six faces of this volume in order: umin, umax, vmin, vmax, wmin, wmax.

        :return: Boundary faces
        :rtype: (Surface)
        """
        return tuple(self.section(*args) for args in sections(3, 2))
Esempio n. 5
0
    def lookup(self, obj, add=False):
        """Obtain the `NodeView` object corresponding to a given object.

        If the keyword argument `add` is true, this function may generate one
        or more new nodes to accommodate the object.

        :param SplineObject obj: The object to look up
        :param bool add: Whether to allow adding new objects
        :return: A corresponding view
        :rtype: NodeView

        .. warning:: The object *must* be a `SplineObject`, even for points.
        """
        # Pass lower-dimensional objects through to the lower levels
        if self.pardim > obj.pardim:
            return self.lower.lookup(obj, add=add)

        # Special case for points: self.lower is a mapping from array to node
        if self.pardim == 0:
            if add:
                node = TopologicalNode(obj, [])
                return self.lower.setdefault(obj.controlpoints, node).view()
            return self.lower[obj.controlpoints].view()

        # Get all nodes of lower dimension (points, vertices, etc.)
        # This involves a recursive call to self.lower.__call__
        lower_nodes = []
        for i in range(0, self.pardim):
            nodes = tuple(
                self.lower.lookup(obj.section(*args, unwrap_points=False),
                                  add=add).node
                for args in sections(self.pardim, i))
            lower_nodes.append(nodes)

        # Try looking up the lower-order nodes in the internal dictionary,
        # which maps tuples of nodes to lists of nodes. E.g. for volumes we
        # look up faces, for faces we look up edges, etc. Return the first one
        # we find for which the .view() function succeeds. This can throw a
        # KeyError (if this particular combination of lower-order nodes is new)
        # or an OrientationError (if it is not new, but the objects don't
        # match). If that happens, we generate a new node and return the
        # identity view on it.
        try:
            for candidate_node in self.internal[lower_nodes[-1]]:
                return candidate_node.view(obj)
        # FIXME: It might be useful to optionally not silence OrientationError,
        # since that more often than not indicates a real error
        except (KeyError, OrientationError):
            if not add:
                raise KeyError("No such object found")
            node = TopologicalNode(obj, lower_nodes)
            # Assign the new node to each possible permutation of lower-order
            # nodes. This is slight overkill since some of these permutations
            # are invalid, but c'est la vie.
            for p in permutations(lower_nodes[-1]):
                self.internal.setdefault(p, []).append(node)
            return node.view()
Esempio n. 6
0
    def read_cp_numbers(self):
        """Read control point numbers for unowned control points from child nodes."""
        for node, section in zip(self.lower_nodes[-1], sections(self.pardim, self.pardim-1)):
            if node.owner is not self:
                # The two sections may not agree on orientation, so we fix this here.
                ori = Orientation.compute(self.obj.section(*section), node.obj)
                self.cp_numbers[_section_to_index(section)] = ori.map_array(node.cp_numbers)

        assert (self.cp_numbers != -1).all()
Esempio n. 7
0
    def assign_cp_numbers(self, numbers):
        """Directly assign control point numbers."""
        self.cp_numbers = numbers

        # Control point numbers for owned children must be communicated to them
        if self.pardim > 0:
            for node, section in zip(self.lower_nodes[-1], sections(self.pardim, self.pardim-1)):
                if node.owner is self or node.owner is self.owner:
                    # Since this runs in a direct line of ownership, we don't need to be concerned with
                    # orientations not matching up.
                    node.assign_cp_numbers(numbers[_section_to_index(section)])
Esempio n. 8
0
    def edges(self):
        """Return the twelve edges of this volume in order:

        - umin, vmin
        - umax, vmin
        - umin, vmax
        - umax, vmax
        - umin, wmin
        - umax, wmin
        - umin, wmax
        - umax, wmax
        - vmin, wmin
        - vmax, wmin
        - vmin, wmax
        - vmax, wmax

        :return: Edges
        :rtype: (Curve)
        """
        return tuple(self.section(*args) for args in sections(3, 1))
Esempio n. 9
0
    def edges(self):
        """Return the twelve edges of this volume in order:

        - umin, vmin
        - umax, vmin
        - umin, vmax
        - umax, vmax
        - umin, wmin
        - umax, wmin
        - umin, wmax
        - umax, wmax
        - vmin, wmin
        - vmax, wmin
        - vmin, wmax
        - vmax, wmax

        :return: Edges
        :rtype: (Curve)
        """
        return tuple(self.section(*args) for args in sections(3, 1))
Esempio n. 10
0
    def generate_cp_numbers(self, start=0):
        """Generate a control point numbering starting at `start`. Return the next unused index."""
        assert self.owner is None

        # Initialize a number array
        shape = self.obj.shape
        numbers = np.empty(shape, dtype=int)
        numbers[:] = 0

        # Flag control points owned by other top-level objects with -1
        for node, section in zip(self.lower_nodes[-1], sections(self.pardim, self.pardim-1)):
            if node.owner is not self:
                numbers[_section_to_index(section)] = -1

        # Fill in control point numbers for the ones we do own
        mask = np.where(numbers != -1)
        nowned = len(mask[0])
        numbers[mask] = np.arange(start, start + nowned, dtype=int)

        # This method takes care of communicating results to children
        self.assign_cp_numbers(numbers)
        return start + nowned
Esempio n. 11
0
 def faces(self):
     """A tuple of all faces."""
     return tuple(self.section(s) for s in sections(self.pardim, 2))
Esempio n. 12
0
 def edges(self):
     """A tuple of all edges."""
     return tuple(self.section(s) for s in sections(self.pardim, 1))
Esempio n. 13
0
 def corners(self):
     """A tuple of all corners."""
     return tuple(self.section(s) for s in sections(self.pardim, 0))
Esempio n. 14
0
    def lookup(self, obj, add=False, raise_on_twins=True):
        """Obtain the `NodeView` object corresponding to a given object.

        If the keyword argument `add` is true, this function may generate one
        or more new nodes to accommodate the object.

        :param SplineObject obj: The object to look up
        :param bool add: Whether to allow adding new objects
        :param bool raise_on_twins: If true, raise an error when
            'twins' are detected, i.e. two patches that share
            identical nodes of lower parametric dimension but which
            are different. For example, two surfaces with identical
            edges but different interior, like a 'pillow'. If false,
            such cases will be considered two genuinely different
            patches. Setting this to true (default) allows catching a
            number of typical topological problems.
        :return: A corresponding view
        :rtype: NodeView

        .. warning:: The object *must* be a `SplineObject`, even for points.
        """
        # Pass lower-dimensional objects through to the lower levels
        if self.pardim > obj.pardim:
            return self.lower.lookup(obj,
                                     add=add,
                                     raise_on_twins=raise_on_twins)

        # Special case for points: self.lower is a mapping from array to node
        if self.pardim == 0:
            cps = obj.controlpoints
            if obj.rational:
                cps = cps[..., :-1]
            if add:
                node = TopologicalNode(obj, [])
                return self.lower.setdefault(cps, node).view()
            return self.lower[cps].view()

        # Get all nodes of lower dimension (points, vertices, etc.)
        # This involves a recursive call to self.lower.__call__
        lower_nodes = []
        for i in range(0, self.pardim):
            nodes = tuple(
                self.lower.lookup(obj.section(*args, unwrap_points=False),
                                  add=add).node
                for args in sections(self.pardim, i))
            lower_nodes.append(nodes)

        # Try looking up the lower-order nodes in the internal dictionary,
        # which maps tuples of nodes to lists of nodes. E.g. for volumes we
        # look up faces, for faces we look up edges, etc. Return the first one
        # we find for which the .view() function succeeds. This can throw a
        # KeyError (if this particular combination of lower-order nodes is new)
        # or an OrientationError (if it is not new, but the objects don't
        # match). If that happens, we generate a new node and return the
        # identity view on it.
        try:
            for candidate_node in self.internal[lower_nodes[-1]]:
                return candidate_node.view(obj)

        except (KeyError, OrientationError) as err:
            if isinstance(err, OrientationError) and raise_on_twins:
                raise OrientationError(
                    "Candidate nodes found but no orientation matched. "
                    "This probably indicates an erroneous topology. "
                    "If you are sure this is not the case (that twin patches exist), "
                    "use raise_on_twins=False.")
            if not add:
                raise KeyError("No such object found")
            node = TopologicalNode(obj, lower_nodes)
            # Assign the new node to each possible permutation of lower-order
            # nodes. This is slight overkill since some of these permutations
            # are invalid, but c'est la vie.
            for p in permutations(lower_nodes[-1]):
                self.internal.setdefault(p, []).append(node)
            return node.view()