예제 #1
0
    def _sort_faces(self):
        """
        sort the vertex indices for every face such that they are listed clockwise as seen from the inside

        since the faces of a convex polyhedron are convex polygons, the center of a face is always inside this polygon.
        therefor the angles between the vectors connecting each vertex to the face center are unique
        """
        # print(self.faces)
        # self.show_faces()
        for face_com, face_indices in zip(self.face_centers, self.faces):
            start_vec = self.vertices[face_indices[0]].pos - face_com
            face_normal = vpy.cross(
                start_vec, self.vertices[face_indices[1]].pos - face_com)
            if vpy.diff_angle(face_normal, face_com - self.pos) > vpy.pi / 2:
                face_normal *= -1
            # if self.debug:
            #     vpy.arrow(axis=face_normal/face_normal.mag*5,
            #             pos=face_com, color=vpy.vec(0, 0.8, 0))
            def sort_face(vertex_index):
                """
                return the angle between the starting vector and 'self.vertices[vertex_index] - face_com'
                """
                current_vec = self.vertices[vertex_index].pos - face_com
                angle = vpy.diff_angle(start_vec, current_vec) * \
                    sign(vpy.dot(vpy.cross(start_vec, current_vec), face_normal))
                return angle

            face_indices.sort(key=sort_face)
            del (sort_face)  # clean up the internal function
def calc_rotate_pair(point_A, point_B, com, PUZZLE_COM=vpy.vec(0, 0, 0)):
    """
    calculate everything necessary to rotate 'point_A' to 'point_B' around
        the point 'com'

    inputs:
    -------
        point_A - (vpython 3d object) - a vpython object with .pos attribute
            this object will be moved
        point_B - (vpython 3d object) - a vpython object with .pos attribute
            this is the position of 'point_A' after the rotation
        com - (vpy.vector) - the point around which 'point_A' will be rotated

    returns:
    --------
        (float) - angle of rotation in radians
        (vpy.vector) - axis of rotation
        (vpy.vector) - origin for the rotation
    """
    v_A = point_A.pos - com
    v_B = point_B.pos - com
    # check for linear dependence
    if abs(abs(vpy.dot(v_A, v_B)) - v_A.mag * v_B.mag) <= 1e-12:
        mid_point = (point_A.pos + point_B.pos) / 2
        axis = mid_point - PUZZLE_COM
        angle = vpy.pi
        return angle, axis, mid_point

    axis = vpy.cross(v_A, v_B)
    angle = vpy.diff_angle(v_A, v_B)
    return angle, axis, com
        def sort_face(point_index):
            """
            return three values for sorting (in that order):
                1. angle between the starting vector and `point_coordinates[point_index] - piece_com`
                2. angle between `piece_com` and `point_coordinates[point_index] - piece_com`
                3. magnitude of `point_coordinates[point_index] - piece_com`

            inputs:
            -------
                index of an element in piece
            """
            current_vec = point_coordinates[point_index] - piece_com
            angle = vpy.diff_angle(start_vec, current_vec) * \
                sign(vpy.dot(vpy.cross(start_vec, current_vec), piece_com))
            return (angle, vpy.diff_angle(piece_com,
                                          current_vec), current_vec.mag)
예제 #4
0
 def sort_face(vec):
     """
     return the angle between the starting vector and 'self.vertices[vertex_index] - face_com'
     """
     current_vec = vec - face_com
     angle = vpy.diff_angle(start_vec, current_vec) * \
         sign(vpy.dot(vpy.cross(start_vec, current_vec), face_normal))
     return angle
def vertex_is_inside(vertex, plane_normal, plane_anchor):
    """
        Checks whether or not a given vertex is below (True) or above (False) a given plane.
        The planes normal vector points towards the 'above' side of the plane.
        If the point is on the plane, also returns True

        inputs:
        -------
            vertex - (vpy.vector) - any 3D point as a vpython vector
            plane_normal - (vpy.vector) - the normal vector of the plane of interest
            plane_anchor - (vpy.vector) - any point on the plane

        returns:
        --------
            (bool) - True if vertex is below or on the plane, 
                False if it is above the plane
        """
    if vpy.diff_angle(vertex - plane_anchor, plane_normal) >= vpy.pi / 2:
        return True
    return False
예제 #6
0
    def intersect(self, other, debug=False, color=None, **poly_properties):
        """
        define intersection of two polyhedra 
        equal to the intersection of the sets of all points inside the polyhedra

        edge cases are treated as empty intersections:
            if the intersection has no volume (i.e. if it is just a point or line),
            returns None

        inputs:
        -------
            other - (Polyhedron) - another 3D polyhedron

        returns:
        --------
            (NoneType) or (Polyhedron) - None if the intersection is empty,
                otherwise return a new Polyhedron object
        
        raises:
        -------
            TypeError - if 'other' is not a Polyhedron object.
        """
        if not isinstance(other, Polyhedron):
            raise TypeError(
                f"second object should be of type 'Polyhedron' but is of type {type(other)}."
            )
        # # check for bounding box overlap. This is very quick and can eliminate many cases with empty intersections
        # dist_vec = other.obj.pos - self.obj.pos
        # if abs(dist_vec.x) > other.obj.size.x/2 + self.obj.size.x/2 \
        #         and abs(dist_vec.y) > other.obj.size.y/2 + self.obj.size.y/2 \
        #         and abs(dist_vec.z) > other.obj.size.z/2 + self.obj.size.z/2:
        #     return None
        # del(dist_vec)
        changed_polyhedron = False  # variable to check if the polyhedron was changed
        # We will work on the list of faces but the faces are lists of vpython vectors,
        #   not just refrences to self.vertices
        new_poly_faces = [[r_vec(self.vertices[i].pos) for i in face]
                          for face in self.faces]
        for clip_center, clip_face in zip(other.face_centers, other.faces):
            # calculate normal vector of the clip plane
            clip_vec_0 = other.vertices[clip_face[0]].pos - clip_center
            clip_vec_1 = other.vertices[clip_face[1]].pos - clip_center
            clip_normal_vec = vpy.cross(clip_vec_0, clip_vec_1)
            del (clip_vec_0, clip_vec_1, clip_face)  #cleanup
            # calculate for each point whether it's above or below the clip plane
            relation_dict = get_vertex_plane_relations(new_poly_faces,
                                                       clip_normal_vec,
                                                       clip_center,
                                                       eps=self.eps)
            if debug:
                debug_list = [
                    vpy.arrow(axis=clip_normal_vec / clip_normal_vec.mag,
                              pos=clip_center,
                              color=color,
                              shaftwidth=0.05,
                              headlength=0.2,
                              headwidth=0.2)
                ]
                debug_list += [
                    vpy.sphere(pos=vpy.vec(*key),
                               radius=0.05,
                               color=vpy.vec(1 - val, val, 0))
                    for key, val in relation_dict.items()
                ]
            # skip calculation if this plane does not intersect the polyhedron
            relation_set = set(relation_dict.values())
            if relation_set == {False}:  # all points are above the clip plane
                if debug:
                    for obj in debug_list:
                        obj.visible = False
                    del (debug_list)
                return None
            if len(relation_set) == 1:  # all point are below the clip plane
                if debug:
                    for obj in debug_list:
                        obj.visible = False
                    del (debug_list)
                continue
            del (relation_set)
            changed_polyhedron = True
            intersected_vertices = list()
            for face in new_poly_faces:  # loop over all faces of the polyhedron
                point_1 = face[-1]
                new_face = list()
                for point_2 in face:  # loop over all edges of each face
                    point_1_rel = relation_dict[vpy_vec_tuple(point_1)]
                    point_2_rel = relation_dict[vpy_vec_tuple(point_2)]

                    if debug:
                        edge = point_2 - point_1
                        debug_list.append(
                            vpy.arrow(
                                pos=point_1,
                                axis=edge,
                                shaftwidth=0.05,
                                headlength=0.2,
                                headwidth=0.2,
                                color=vpy.vec(1, 0, 1),
                                opacity=0.75))  #show directional debug edge
                        print(
                            f"calc intersection: {bool(len(set((point_1_rel, point_2_rel)))-1)}"
                        )

                    if point_1_rel:  #point_1 is below the clip face -> possibly still in the clip polyhedron
                        if not point_1 in new_face:
                            new_face.append(point_1)
                    if point_1_rel != point_2_rel:
                        # if one point is above and the other below, calculate the intersection point:
                        edge_vec = point_2 - point_1
                        # counteract numerical errors:
                        if abs(
                                vpy.diff_angle(clip_normal_vec, edge_vec) -
                                vpy.pi / 2) < 1e-5:
                            # edge on clip plane
                            continue
                        div = vpy.dot(edge_vec, clip_normal_vec)
                        if div != 0:
                            t = vpy.dot(clip_center - point_1,
                                        clip_normal_vec) / div
                            # try to prevent numerical errors:
                            if abs(t) < 1e-5:  # point_1 on clip plane
                                intersect_point = point_1
                            elif abs(t - 1) < 1e-5:  # point 2 on clip plane
                                intersect_point = point_2
                            else:  # normal intersection calculation
                                intersect_point = r_vec(point_1 + t * edge_vec)
                            # process intersection point
                            if not intersect_point in new_face:
                                new_face.append(intersect_point)
                            if not intersect_point in intersected_vertices:
                                intersected_vertices.append(intersect_point)
                            relation_dict[vpy_vec_tuple(
                                intersect_point)] = True
                            if debug:
                                debug_list.append(
                                    vpy.sphere(pos=intersect_point,
                                               color=vpy.vec(1, 0, 1),
                                               radius=0.06))

                    point_1 = point_2  # go to next edge
                    if debug:
                        while not isinstance(debug_list[-1], vpy.arrow):
                            debug_list[-1].visible = False  # hide debug points
                            del (debug_list[-1])
                        debug_list[-1].visible = False  # hide debug edge

                if len(new_face) > 2:
                    face[:] = new_face
                else:
                    face.clear()
            if debug:
                for obj in debug_list:
                    obj.visible = False
                del (debug_list)

            if len(intersected_vertices) > 2:
                sort_new_face(intersected_vertices)
                new_poly_faces.append(
                    intersected_vertices)  # add a single new face

            del_empties(new_poly_faces)  # delete empty faces
            if debug:
                self.toggle_visible(False)
                try:
                    temp.toggle_visible(False)
                except UnboundLocalError:
                    pass
                temp = poly_from_faces(
                    new_poly_faces,
                    debug=debug,
                    color=color,
                    opacity=0.5,
                    show_faces=True,
                    show_edges=True,
                    show_corners=False,
                    sort_faces=True,
                    edge_color=poly_properties["edge_color"],
                    corner_color=poly_properties["corner_color"],
                    edge_radius=poly_properties["edge_radius"],
                    corner_radius=poly_properties["corner_radius"],
                    pos=poly_properties["pos"])
                print("next")
        # if not changed_polyhedron: # no faces of 'self' and 'other intersected'
        #     return self # 'self' is completely inside of 'other'
        # the intersection is a new polyhedron
        if len(new_poly_faces) == 0:
            return None
        return poly_from_faces(new_poly_faces,
                               debug=debug,
                               color=color,
                               **poly_properties)
예제 #7
0
             1 / 2 * (L - 2 * R) * cos(alpha + theta2)) - meu_2 * cos(alpha) * (
                                m + M) * g * R) / (I_sys + (m + M) * R ** 2)) * dt
     time += dt
     capsule.pos += vec(R * dtheta2 * cos(alpha), - R * dtheta2 * sin(alpha), 0)
     origin = vec(capsule.pos.x + (0.5 * L - R) * cos(alpha + theta2),
                  capsule.pos.y - (0.5 * L - R) * sin(alpha + theta2),
                  capsule.pos.z)
     # white_mark = sphere(pos=origin, radius=0.5, color=color.white)
     capsule.rotate(angle=dtheta2, axis=vec(0, 0, -1), origin=origin)
     ball.pos += vec(R * dtheta2 * cos(alpha), - R * dtheta2 * sin(alpha), 0)
     if ball.pos.y < 0:
         break
 if ball.pos.y < 0:
     break
 check_vec = capsule.pos - vec(0.5 * B, 0, 0)
 if diff_angle(check_vec, - vec(0.5 * B, 0, 0)) != alpha:
     print("Simulation and upgradation of positions and numerical values with dt = 1 micro sec ",
           "resulted in an angle of inclination of the simulated capsule to be: ", diff_angle(check_vec, - vec(0.5 * B, 0, 0)))
     print("The true angle of inclination is: ", alpha)
     print("Adjusting the position of capsule for the simulation to visually seem smooth...")
     """
     This is python's numerical computation fault :(. Further refer to:
     https://www-uxsup.csx.cam.ac.uk/courses/moved.NumericalPython/paper_1.pdf
     to see how python creates this gap for numerical calculation errors.
     """
     # the following lines added to keep simulation outlook smooth
     capsule.pos = vec(cap_ref.x + R * pi * cos(alpha), cap_ref.y - R * pi * sin(alpha), cap_ref.z)
     origin = vec(capsule.pos.x + (0.5 * L - R) * cos(alpha),
                  capsule.pos.y - (0.5 * L - R) * sin(alpha),
                  capsule.pos.z)
     capsule.rotate(angle=pi, axis=vec(0, 0, -1), origin=origin)