def concatenate():
    trans1 = [1, 2, 3]
    angle1 = [-2.142, 1.141, -0.142]
    T1 = Translation(trans1)
    R1 = Rotation.from_euler_angles(angle1)
    M1 = T1.concatenate(R1)
    assert np.allclose(M1, T1 * R1)
Example #2
0
 def node_xyz(self):
     """dict : The view coordinates of the mesh object."""
     origin = Point(0, 0, 0)
     stack = []
     if self.scale != 1.0:
         S = Scale.from_factors([self.scale] * 3)
         stack.append(S)
     if self.rotation != [0, 0, 0]:
         R = Rotation.from_euler_angles(self.rotation)
         stack.append(R)
     if self.location != origin:
         if self.anchor is not None:
             xyz = self.network.vertex_attributes(self.anchor, 'xyz')
             point = Point(*xyz)
             T1 = Translation.from_vector(origin - point)
             stack.insert(0, T1)
         T2 = Translation.from_vector(self.location)
         stack.append(T2)
     if stack:
         X = reduce(mul, stack[::-1])
         network = self.network.transformed(X)
     else:
         network = self.network
     node_xyz = {
         network: network.node_attributes(node, 'xyz')
         for node in network.nodes()
     }
     return node_xyz
Example #3
0
 def vertex_xyz(self):
     """dict : The view coordinates of the volmesh object."""
     origin = Point(0, 0, 0)
     stack = []
     if self.scale != 1.0:
         S = Scale.from_factors([self.scale] * 3)
         stack.append(S)
     if self.rotation != [0, 0, 0]:
         R = Rotation.from_euler_angles(self.rotation)
         stack.append(R)
     if self.location != origin:
         if self.anchor is not None:
             xyz = self.volmesh.vertex_attributes(self.anchor, 'xyz')
             point = Point(*xyz)
             T1 = Translation.from_vector(origin - point)
             stack.insert(0, T1)
         T2 = Translation.from_vector(self.location)
         stack.append(T2)
     if stack:
         X = reduce(mul, stack[::-1])
         volmesh = self.volmesh.transformed(X)
     else:
         volmesh = self.volmesh
     vertex_xyz = {
         vertex: volmesh.vertex_attributes(vertex, 'xyz')
         for vertex in volmesh.vertices()
     }
     return vertex_xyz
Example #4
0
    def create_group_action(self, objs, transformations, times):
        # TODO: how to start multiple animations at once
        if len(transformations) != len(times):
            raise ValueError("Pass equal amount of transformations and times")
        x, y, z, w = objs[0].quaternion
        Tinit = Rotation.from_quaternion([w, x, y, z]) * Translation(
            objs[0].position)
        positions = []
        quaternions = []
        for M in transformations:
            Sc, Sh, R, T, P = (M * Tinit).decomposed()
            positions.append(list(T.translation))
            quaternions.append(R.quaternion.xyzw)
        position_track = p3js.VectorKeyframeTrack(name='.position',
                                                  times=times,
                                                  values=list(
                                                      flatten(positions)))
        rotation_track = p3js.QuaternionKeyframeTrack(
            name='.quaternion', times=times, values=list(flatten(quaternions)))

        animation_group = p3js.AnimationObjectGroup()
        animation_group.exec_three_obj_method('add',
                                              objs[0])  # this is not working

        obj_clip = p3js.AnimationClip(tracks=[position_track, rotation_track])
        mixer = p3js.AnimationMixer(animation_group)
        obj_action = p3js.AnimationAction(mixer, obj_clip, animation_group)
        return obj_action
Example #5
0
def test_remeshing():

    FILE = os.path.join(HERE, '../..', 'data', 'Bunny.ply')

    # ==============================================================================
    # Get the bunny and construct a mesh
    # ==============================================================================

    bunny = TriMesh.from_ply(FILE)

    bunny.cull_vertices()

    # ==============================================================================
    # Move the bunny to the origin and rotate it upright.
    # ==============================================================================

    vector = scale_vector(bunny.centroid, -1)

    T = Translation.from_vector(vector)
    S = Scale.from_factors([100, 100, 100])
    R = Rotation.from_axis_and_angle(Vector(1, 0, 0), math.radians(90))

    bunny.transform(R * S * T)

    # ==============================================================================
    # Remesh
    # ==============================================================================

    length = bunny.average_edge_length

    bunny.remesh(4 * length)

    bunny.to_mesh()
Example #6
0
def test_concatenated():
    trans1 = [1, 2, 3]
    angle1 = [-2.142, 1.141, -0.142]
    T1 = Translation.from_vector(trans1)
    R1 = Rotation.from_euler_angles(angle1)
    M1 = T1.concatenated(R1)
    assert allclose(M1, T1 * R1)
Example #7
0
    def correct_location_in_z(self, cylinder, dist_diff):
        corr_vector = cylinder.get_normal() * dist_diff
        T = Translation(corr_vector)

        cylinder.location.transform(T)

        cylinder.attrs["location_correction"] = corr_vector
Example #8
0
def test___eq__():
    i1 = Transformation()
    i2 = Transformation()
    t = Translation.from_vector([1, 0, 0])
    assert i1 == i2
    assert not (i1 != i2)
    assert i1 != t
    assert not (i1 == t)
Example #9
0
 def _to_occ(self) -> BRepPrimAPI_MakeBox:
     xaxis = self.frame.xaxis.scaled(-0.5 * self.xsize)
     yaxis = self.frame.yaxis.scaled(-0.5 * self.ysize)
     zaxis = self.frame.zaxis.scaled(-0.5 * self.zsize)
     frame = self.frame.transformed(
         Translation.from_vector(xaxis + yaxis + zaxis))
     ax2 = gp_Ax2(gp_Pnt(*frame.point), gp_Dir(*frame.zaxis),
                  gp_Dir(*frame.xaxis))
     return BRepPrimAPI_MakeBox(ax2, self.xsize, self.ysize, self.zsize)
Example #10
0
 def vertex_xyz(self):
     bbox = self.mesh.bounding_box()
     xmin, ymin, zmin = bbox[0]
     if not self.binary and (xmin < 0 or ymin < 0 or zmin < 0):
         T = Translation.from_vector([-xmin, -ymin, -zmin])
         mesh = self.mesh.transformed(T)
     else:
         mesh = self.mesh
     return {vertex: mesh.vertex_attributes(vertex, 'xyz') for vertex in mesh.vertices()}
    def board_geometry_setup():
        for my_element in self.elements():
            my_board = my_element[1]
            if my_board.layer % 2 == 0:
                my_frame = self.origin_fr
                layer_standard_length = self.primary_length
            else:
                my_frame = self.sec_fr
                layer_standard_length = self.secondary_length

            my_dir1 = normalize_vector(my_frame[1])
            my_dir2 = normalize_vector(my_frame[2])

            dist = my_board.grid_position

            if my_board.location == "high":
                if not my_board.supporter:
                    length_attribute_1 = layer_standard_length - my_board.length / 2
                else:
                    length_attribute_1 = layer_standard_length - my_board.width / 2
            elif my_board.location == "low":
                if not my_board.supporter:
                    length_attribute_1 = my_board.length / 2
                else:
                    length_attribute_1 = my_board.width / 2
            else:
                length_attribute_1 = layer_standard_length / 2

            # position parallel to the boards (if not sup)
            my_vec1 = scale_vector(my_dir1, length_attribute_1)
            # position perpendicular to board direction (if not sup)
            my_vec2 = scale_vector(my_dir2, dist)
            # height vector
            my_vec3 = Vector(0, 0, my_board.z_drop - my_board.height / 2)
            my_centre = self.origin_pt + my_vec1 + my_vec2 + my_vec3
            my_board.centre_point = my_centre

            my_board.drop_point = my_centre + Vector(0, 0, my_board.height / 2)
            if not my_board.perp:
                my_board.length_vector = normalize_vector(my_vec1)
                my_board.width_vector = normalize_vector(my_vec2)
            else:
                my_board.length_vector = normalize_vector(my_vec2)
                my_board.width_vector = normalize_vector(my_vec1)

            old_centre = my_board.center
            T = Translation(my_centre - old_centre)

            self.network.node[my_board.global_count]['x'] = my_centre[0]
            self.network.node[my_board.global_count]['y'] = my_centre[1]
            self.network.node[my_board.global_count]['z'] = my_centre[2]

            my_board.transform(T)
            my_board.board_frame = Frame(my_board.centre_point, my_board.length_vector, my_board.width_vector)
            my_board.tool_frame = Frame(my_board.drop_point, my_board.length_vector, my_board.width_vector)
            my_board.box = Box(my_board.board_frame, my_board.length, my_board.width, my_board.height)
Example #12
0
 def vertex_xyz(self):
     """dict : The view coordinates of the mesh object."""
     origin = Point(0, 0, 0)
     if self.anchor is not None:
         xyz = self.mesh.vertex_attributes(self.anchor, 'xyz')
         point = Point(* xyz)
         T1 = Translation.from_vector(origin - point)
         S = Scale.from_factors([self.scale] * 3)
         R = Rotation.from_euler_angles(self.rotation)
         T2 = Translation.from_vector(self.location)
         X = T2 * R * S * T1
     else:
         S = Scale.from_factors([self.scale] * 3)
         R = Rotation.from_euler_angles(self.rotation)
         T = Translation.from_vector(self.location)
         X = T * R * S
     mesh = self.mesh.transformed(X)
     vertex_xyz = {vertex: mesh.vertex_attributes(vertex, 'xy') + [0.0] for vertex in mesh.vertices()}
     return vertex_xyz
Example #13
0
    def draw_blocks(self, keys=None, show_faces=False, show_vertices=False, show_edges=True):
        """Draw the blocks of the assembly.

        Parameters
        ----------
        show_faces : bool, optional
            Draw the faces of the blocks.
            Default is ``False``.
        show_vertices : bool, optional
            Draw the vertices of the blocks.
            Default is ``False``.
        show_edges : bool, optional
            Draw the edges of the blocks.
            Default is ``True``.

        Notes
        -----
        * By default, blocks are drawn as wireframes.
        * By default, blocks are drawn on a sublayer of the base layer, if a base layer was specified.
        * Block names have the following pattern: ``"{assembly_name}.block.{block_id}"``
        * Faces and vertices can be drawn using the corresponding flags.
        * Block components have the following pattern:

          * face: ``"{assembly_name}.block.{block_id}.face.{face_id}"``
          * edge: ``"{assembly_name}.block.{block_id}.edge.{edge_id}"``
          * vertex: ``"{assembly_name}.block.{block_id}.vertex.{vertex_id}"``

        Examples
        --------
        >>>
        """
        blocks = []
        # compas_blender.clear_collection()
        for key in self.assembly.nodes():
            block = self.assembly.blocks[key]
            centroid = block.centroid()
            T = Translation(subtract_vectors([0, 0, 0], centroid))
            name = "Assembly.block.{}".format(key)
            vertices = transform_points(block.vertices_attributes('xyz'), T)
            edges = list(block.edges())
            faces = [block.face_vertices(fkey) for fkey in block.faces()]
            mesh = compas_blender.bpy.data.meshes.new(name)
            mesh.from_pydata(vertices, edges, faces)
            obj = compas_blender.bpy.data.objects.new(name, mesh)
            obj.show_wire = True
            obj.location = centroid
            if self.assembly.node_attribute(key, 'is_support'):
                compas_blender.drawing.set_object_color(obj, [1.0, 0.0, 0.0])
            else:
                compas_blender.drawing.set_object_color(obj, [1.0, 1.0, 1.0])
            blocks.append(obj)
        for obj in blocks:
            for col in obj.users_collection:
                col.objects.unlink(obj)
            self.block_collection.objects.link(obj)
Example #14
0
    def get_compressed_centroid_frame(self):
        """Get frame at middle of compressed bullet.

        Returns
        -------
        :class:`compas.geometry.Frame`
        """
        vector = self.get_normal() * self.get_compressed_height() / 2
        T = Translation(vector)

        return self.location.transformed(T)
Example #15
0
    def get_compressed_top_frame(self):
        """Top of compressed cylinder.

        Returns
        -------
        :class:`compas.geometry.Frame`
        """
        vector = self.get_normal() * self.get_compressed_height()
        T = Translation(vector)

        return self.location.transformed(T)
def test_basis_vectors():
    trans1 = [1, 2, 3]
    angle1 = [-2.142, 1.141, -0.142]
    scale1 = [0.123, 2, 0.5]
    T1 = Translation(trans1)
    R1 = Rotation.from_euler_angles(angle1)
    S1 = Scale(scale1)
    M = (T1 * R1) * S1
    x, y = M.basis_vectors
    assert np.allclose(x, Vector(0.41249169135312663, -0.05897071585157175, -0.9090506362335324))
    assert np.allclose(y, Vector(-0.8335562904208867, -0.4269749553355485, -0.35053715668381935))
Example #17
0
    def get_egress_frame(self):
        """Get Frame at end and start of trajectory to and from.

        Returns
        -------
        :class:`compas.geometry.Frame`
        """
        vector = self.get_normal() * self.egress_frame_distance
        T = Translation(vector)

        return self.get_uncompressed_top_frame().transformed(T)
Example #18
0
    def pick_element(self, element):
        """Send movement and IO instructions to pick up fabrication element.

        Parameter
        ----------
        element : :class:`~rapid_clay_formations_fab.fab_data.FabricationElement`
            Representation of fabrication element to pick up.
        """
        self.send(compas_rrc.SetTool(self.pick_place_tool.name))
        log.debug("Tool {self.pick_place_tool.name} set.")
        self.send(compas_rrc.SetWorkObject(self.wobjs.pick))
        log.debug(f"Work object {self.wobjs.pick} set.")

        # start watch
        self.send(compas_rrc.StartWatch())

        # TODO: Use separate obj for pick elems?
        vector = element.get_normal(
        ) * self.pick_place_tool.elem_pick_egress_dist
        T = Translation(vector)
        egress_frame = element.get_uncompressed_top_frame().transformed(T)

        self.send(
            MoveToRobtarget(
                egress_frame,
                self.EXTERNAL_AXES_DUMMY,
                self.speed.travel,
                self.zone.travel,
            ))

        self.send(
            MoveToRobtarget(
                element.get_uncompressed_top_frame(),
                self.EXTERNAL_AXES_DUMMY,
                self.speed.pick_place,
                self.zone.pick,
            ))

        self.send(compas_rrc.WaitTime(self.pick_place_tool.needles_pause))
        self.extend_needles()
        self.send(compas_rrc.WaitTime(self.pick_place_tool.needles_pause))

        self.send(
            MoveToRobtarget(
                egress_frame,
                self.EXTERNAL_AXES_DUMMY,
                self.speed.pick_place,
                self.zone.pick,
                motion_type=Motion.LINEAR,
            ))

        self.send(compas_rrc.StopWatch())

        return self.send(compas_rrc.ReadWatch())
Example #19
0
def hexagongrid():
    polygon = Polygon.from_sides_and_radius_xy(6, 1)
    vertices = polygon.points
    vertices.append(polygon.centroid)
    x, y, z = zip(*vertices)
    xmin = min(x)
    xmax = max(x)
    ymin = min(y)
    ymax = max(y)
    faces = [[0, 1, 6], [1, 2, 6], [2, 3, 6], [3, 4, 6], [4, 5, 6], [5, 0, 6]]
    mesh = Mesh.from_vertices_and_faces(vertices, faces)
    meshes = []
    for i in range(2):
        T = Translation.from_vector([i * (xmax - xmin), 0, 0])
        meshes.append(mesh.transformed(T))
    for i in range(2):
        T = Translation.from_vector([i * (xmax - xmin), ymax - ymin, 0])
        meshes.append(mesh.transformed(T))
    mesh = meshes_join_and_weld(meshes)
    return mesh
Example #20
0
    def calculate_prismatic_transformation(self, position):
        """Returns a transformation of a prismatic joint.

        A prismatic joint slides along the axis and has a limited range
        specified by the upper and lower limits.

        Args:
            position (float): the movement in meters.
        """
        position = max(min(position, self.limit.upper), self.limit.lower)
        return Translation(self.axis.vector * position)
Example #21
0
def shift(block):
    """Shift a block along the X or Y axis by a randomly chosen amount.

    Parameters
    ----------
    block : compas_assembly.datastructures.Block
    """
    scale = choice([+0.01, -0.01, +0.05, -0.05, +0.1, -0.1])
    axis = choice([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
    vector = scale_vector(axis, scale)
    T = Translation.from_vector(vector)
    block.transform(T)
Example #22
0
def move_diagram(diagram, distance=1.5):
    bbox = diagram.bounding_box()

    a = bbox[0]
    b = bbox[1]
    ab = subtract_vectors(b, a)
    ab = scale_vector(ab, distance)

    T = Translation(ab)
    diagram.transform(T)

    return diagram
def test_decomposed():
    trans1 = [1, 2, 3]
    angle1 = [-2.142, 1.141, -0.142]
    scale1 = [0.123, 2, 0.5]
    T1 = Translation(trans1)
    R1 = Rotation.from_euler_angles(angle1)
    S1 = Scale(scale1)
    M = (T1 * R1) * S1
    Sc, Sh, R, T, P = M.decomposed()
    assert S1 == Sc
    assert R1 == R
    assert T1 == T
Example #24
0
 def node_xyz(self):
     """dict : The view coordinates of the mesh object."""
     origin = Point(0, 0, 0)
     if self.anchor is not None:
         xyz = self.network.node_attributes(self.anchor, 'xyz')
         point = Point(*xyz)
         T1 = Translation.from_vector(origin - point)
         S = Scale.from_factors([self.scale] * 3)
         R = Rotation.from_euler_angles(self.rotation)
         T2 = Translation.from_vector(self.location)
         X = T2 * R * S * T1
     else:
         S = Scale.from_factors([self.scale] * 3)
         R = Rotation.from_euler_angles(self.rotation)
         T = Translation.from_vector(self.location)
         X = T * R * S
     network = self.network.transformed(X)
     node_xyz = {
         network: network.node_attributes(node, 'xyz')
         for node in network.nodes()
     }
     return node_xyz
Example #25
0
    def placement_frame(self):
        """Last frame in placement procedure.

        Derived from location, height and compression ratio.

        Returns
        -------
        :class:`compas.geometry.Frame`
        """
        vector = self.location.zaxis * self.compressed_height * -1
        T = Translation(vector)

        return self._location.transformed(T)
Example #26
0
 def draw_box(self, box, color=None):
     geo = p3js.BoxBufferGeometry(width=box.xsize, 
                                  height=box.zsize, 
                                  depth=box.ysize,
                                  widthSegments=box.xsize, 
                                  heightSegments=box.zsize,
                                  depthSegments=box.ysize)
     mat = material_from_color(color)
     mesh = p3js.Mesh(geometry=geo, material=mat)
     Tinit = Translation([box.xsize/2, box.ysize/2, box.zsize/2])
     Sc, Sh, R, T, P = (Transformation.from_frame(box.frame) * Tinit).decompose()
     mesh.quaternion = R.quaternion.xyzw
     mesh.position = list(T.translation)
     self.geometry.append(mesh)
     return mesh
Example #27
0
 def create_action(self, obj, transformations, times):
     if len(transformations) != len(times):
         raise ValueError("Pass equal amount of transformations and times")
     x, y, z, w = obj.quaternion
     Tinit = Rotation.from_quaternion([w, x, y, z]) * Translation(obj.position)
     positions = []
     quaternions = []
     for M in transformations:
         Sc, Sh, R, T, P = (M * Tinit).decompose()
         positions.append(list(T.translation))
         quaternions.append(R.quaternion.xyzw)
     position_track = p3js.VectorKeyframeTrack(name='.position', times=times, values=list(flatten(positions)))
     rotation_track = p3js.QuaternionKeyframeTrack(name='.quaternion', times=times, values=list(flatten(quaternions)))
     obj_clip = p3js.AnimationClip(tracks=[position_track, rotation_track])
     obj_action = p3js.AnimationAction(p3js.AnimationMixer(obj), obj_clip, obj)
     return obj_action
    def pick_bullet(self, pick_elem):
        """Send movement and IO instructions to pick up a clay cylinder.

        Parameter
        ----------
        pick_elem : :class:`~rapid_clay_formations_fab.fab_data.ClayBullet`
            Representation of fabrication element to pick up.
        """
        self.send(compas_rrc.SetTool(self.pick_place_tool.name))
        log.debug("Tool {} set.".format(self.pick_place_tool.name))
        self.send(compas_rrc.SetWorkObject(self.wobjs.pick))
        log.debug("Work object {} set.".format(self.wobjs.pick))

        # start watch
        self.send(compas_rrc.StartWatch())

        # TODO: Use separate obj for pick elems?
        vector = pick_elem.get_normal(
        ) * self.pick_place_tool.elem_pick_egress_dist
        T = Translation(vector)
        egress_frame = pick_elem.get_uncompressed_top_frame().transformed(T)

        self.send(
            MoveToFrame(egress_frame, self.speed.travel, self.zone.travel))

        self.send(
            MoveToFrame(
                pick_elem.get_uncompressed_top_frame(),
                self.speed.precise,
                self.zone.pick,
            ))

        self.send(compas_rrc.WaitTime(self.pick_place_tool.needles_pause))
        self.extend_needles()
        self.send(compas_rrc.WaitTime(self.pick_place_tool.needles_pause))

        self.send(
            MoveToFrame(
                egress_frame,
                self.speed.precise,
                self.zone.pick,
                motion_type=Motion.LINEAR,
            ))

        self.send(compas_rrc.StopWatch())

        return self.send(compas_rrc.ReadWatch())
Example #29
0
    def decomposed(self):
        """Decompose the `Transformation` into its components.

        Returns
        -------
        :class:`compas.geometry.Scale`
            The scale component of the current transformation.
        :class:`compas.geometry.Shear`
            The shear component of the current transformation.
        :class:`compas.geometry.Rotation`
            The rotation component of the current transformation.
        :class:`compas.geometry.Translation`
            The translation component of the current transformation.
        :class:`compas.geometry.Projection`
            The projection component of the current transformation.

        Examples
        --------
        >>> from compas.geometry import Scale, Translation, Rotation
        >>> trans1 = [1, 2, 3]
        >>> angle1 = [-2.142, 1.141, -0.142]
        >>> scale1 = [0.123, 2, 0.5]
        >>> T1 = Translation.from_vector(trans1)
        >>> R1 = Rotation.from_euler_angles(angle1)
        >>> S1 = Scale.from_factors(scale1)
        >>> M = T1 * R1 * S1
        >>> S, H, R, T, P = M.decomposed()
        >>> S1 == S
        True
        >>> R1 == R
        True
        >>> T1 == T
        True

        """
        from compas.geometry import Scale  # noqa: F811
        from compas.geometry import Shear
        from compas.geometry import Rotation  # noqa: F811
        from compas.geometry import Translation  # noqa: F811
        from compas.geometry import Projection
        s, h, a, t, p = decompose_matrix(self.matrix)
        S = Scale.from_factors(s)
        H = Shear.from_entries(h)
        R = Rotation.from_euler_angles(a, static=True, axes='xyz')
        T = Translation.from_vector(t)
        P = Projection.from_entries(p)
        return S, H, R, T, P
Example #30
0
    def draw_interfaces(self, keys=None, color=None):
        """Draw the interfaces between the blocks.

        Parameters
        ----------
        keys : list
            A list of interface identifiers (i.e. assembly edge (u, v) tuples).
            Default is ``None``, in which case all interfaces are drawn.
        color : str, tuple, dict
            The color specififcation for the interfaces.
            Colors should be specified in the form of a string (hex colors) or
            as a tuple of RGB components.
            To apply the same color to all interfaces, provide a single color
            specification. Individual colors can be assigned using a dictionary
            of key-color pairs. Missing keys will be assigned the default interface
            color (``self.settings['color.interface']``).
            The default is ``None``, in which case all interfaces are assigned the
            default interface color.

        Notes
        -----
        * Interfaces are drawn as mesh faces.
        * Interfaces are drawn on a sub-layer *Interfaces* of the base layer, if a base layer was provided.
        * Interface names have the following pattern: ``"{assembly_name}.interface.{from block_id}-{to block_id}"``
        * Interfaces have a direction, as suggested by the naming convention.
        """
        interfaces = []
        for key, attr in self.assembly.edges(True):
            u, v = key
            name = "Assembly.interface.{}-{}".format(u, v)
            points = attr['interface_points']
            base = attr['interface_origin']
            T = Translation(subtract_vectors([0, 0, 0], base))
            vertices = transform_points(points, T)
            faces = [list(range(len(vertices)))]
            edges = list(pairwise(faces[0] + [0]))
            mesh = compas_blender.bpy.data.meshes.new(name)
            mesh.from_pydata(vertices, edges, faces)
            obj = compas_blender.bpy.data.objects.new(name, mesh)
            obj.location = base
            compas_blender.drawing.set_object_color(obj, [0, 0, 1.0])
            interfaces.append(obj)
        for obj in interfaces:
            for col in obj.users_collection:
                col.objects.unlink(obj)
            self.interface_collection.objects.link(obj)