Example #1
0
def out_Location(rv3d, orig, vector):
    view_matrix = rv3d.view_matrix
    v1 = (int(view_matrix[0][0]*1.5), int(view_matrix[0][1]*1.5), int(view_matrix[0][2]*1.5))
    v2 = (int(view_matrix[1][0]*1.5), int(view_matrix[1][1]*1.5), int(view_matrix[1][2]*1.5))

    hit = intersect_ray_tri((1,0,0), (0,1,0), (0,0,0), (vector), (orig), False)
    if hit is None:
        hit = intersect_ray_tri(v1, v2, (0,0,0), (vector), (orig), False)
    if hit is None:
        hit = intersect_ray_tri(v1, v2, (0,0,0), (-vector), (orig), False)
    if hit is None:
        hit = Vector()
    return hit
def out_Location(rv3d, region, orig, vector):
    view_matrix = rv3d.view_matrix
    v1 = Vector((int(view_matrix[0][0]*1.5),int(view_matrix[0][1]*1.5),int(view_matrix[0][2]*1.5)))
    v2 = Vector((int(view_matrix[1][0]*1.5),int(view_matrix[1][1]*1.5),int(view_matrix[1][2]*1.5)))

    hit = intersect_ray_tri(Vector((1,0,0)), Vector((0,1,0)), Vector((0,0,0)), (vector), (orig), False)
    if hit == None:
        hit = intersect_ray_tri(v1, v2, Vector((0,0,0)), (vector), (orig), False)        
    if hit == None:
        hit = intersect_ray_tri(v1, v2, Vector((0,0,0)), (-vector), (orig), False)
    if hit == None:
        hit = Vector((0,0,0))
    return hit
Example #3
0
def get_click_point_info(x, y, ctx):
    cp = ClickPoint()
    view_orient, view_type = get_view_orientation(ctx)
    region = ctx.region
    region_data = ctx.space_data.region_3d
    if view_type in ['PERSP', 'CAMERA']:
        view_matrix = region_data.view_matrix.inverted()
        ray_start = view_matrix.to_translation()
        ray_depth = view_matrix @ Vector((0, 0, -100000))  #TODO from view
        ray_end = region_2d_to_location_3d(region, region_data, (x, y),
                                           ray_depth)
        p = get_triface_from_orient(view_orient)
        # cp.view = mathutils.geometry.intersect_ray_tri(p[0],p[1],p[2],ray_end,ray_start,False)
        cp.view = geometry.intersect_ray_tri(p[0], p[1], p[2], ray_end,
                                             ray_start, False)
        if cp.view == None:
            cp.view = Vector((0, 0, 0))
    else:
        cp.view = region_2d_to_location_3d(region, region_data, (x, y),
                                           (0, 0, 0))
    cp.screen = region_2d_to_location_3d(region, region_data, (x, y),
                                         (0, 0, 0))
    cp.local = switch_axis_by_orient(view_orient, cp.view)
    cp.orient = Vector(get_rotation_from_orient(view_orient))
    if view_type == 'ORTHO' and view_orient == 'USER':
        pass
        # TODO in orthografic user view not work correctly
        # r3d = ctx.area.spaces.active.region_3d
        # view_rot = r3d.view_matrix.to_euler()
    cp.view_name = view_orient
    return cp
def rayCastingObject(ob, ray_origin, ray_direction, culling=False):
    bm = bmesh.new()
    bm.from_object(ob, bpy.context.view_layer.depsgraph)
    bm.transform(ob.matrix_world)
    bmesh.ops.triangulate(bm, faces=bm.faces)

    ray_direction.normalize()

    points = {}

    for face in bm.faces:
        if (culling and ray_direction.dot(face.normal) > 0.0):
            continue

        intersection = geometry.intersect_ray_tri(face.verts[0].co,
                                                  face.verts[1].co,
                                                  face.verts[2].co,
                                                  ray_direction, ray_origin)
        if (intersection):
            distance = (ray_origin - intersection).length_squared
            points[distance] = intersection

    if (points):
        min_distance = min(points)
        return min_distance, points[min_distance]
    else:
        return None, None
def face_intersect(face, pts):
    ray = Vector((0,0,1))
    nrIntersections = np.zeros((np.shape(pts)[0],1), dtype=bool)
    for i in range(np.shape(pts)[0]):
        isect = intersect_ray_tri(face[0], face[1], face[2], ray, pts[i,:], 1)

        nrIntersections[i] = bool( isect and isect.z > pts[i, 2])

    return np.array(nrIntersections, dtype=int)
 def testTri3D(self, coords):
     inter = intersect_ray_tri(coords[0],
                               coords[1],
                               coords[2],
                               self.ray_direction,
                               self.ray_origin)
     if inter is None:
         return None
     return (inter - self.ray_origin).length
 def testTri3D(self, coords):
     inter = intersect_ray_tri(coords[0],
                               coords[1],
                               coords[2],
                               self.ray_direction,
                               self.ray_origin)
     if inter is None:
         return None
     return (inter - self.ray_origin).length
Example #8
0
def calculateRay(laserAngle, mirrorAngle, laserMirrorDistance):
    reusable_ray.xyz = [0.0,-1.0,0.0] # A ray pointing down towards the mirror
    reusable_ray.rotate( Matrix.Rotation(laserAngle, 4, rotation_axis) )
    [v1,v2,v3,n] = createMirror(mirrorAngle)
    incomingAngle = n.angle(reusable_ray)%(math.pi/2.0)
    reflectionAxis = n.cross(reusable_ray) # Calculate the axis around which the ray needs
                                           # to be rotated to created the reflection
    ray_to_mirror.xyz = [0.0,laserMirrorDistance,0.0]
    reflectionPoint = geometry.intersect_ray_tri(v1,v2,v3,reusable_ray,ray_to_mirror,False)
    reusable_ray.rotate( Matrix.Rotation(2*incomingAngle, 4,reflectionAxis) )
    return [reusable_ray, reflectionPoint, math.pi/4-incomingAngle]
Example #9
0
def calculateRay(laserAngle, mirrorAngle, laserMirrorDistance):
    reusable_ray.xyz = [0.0,-1.0,0.0] # A ray pointing down towards the mirror
    reusable_ray.rotate( Matrix.Rotation(laserAngle, 4, rotation_axis) )
    [v1,v2,v3,n] = createMirror(mirrorAngle)
    incomingAngle = n.angle(reusable_ray)%(math.pi/2.0)
    reflectionAxis = n.cross(reusable_ray) # Calculate the axis around which the ray needs
                                           # to be rotated to created the reflection
    ray_to_mirror.xyz = [0.0,laserMirrorDistance,0.0]
    reflectionPoint = geometry.intersect_ray_tri(v1,v2,v3,reusable_ray,ray_to_mirror,False)
    reusable_ray.rotate( Matrix.Rotation(2*incomingAngle, 4,reflectionAxis) )
    return [reusable_ray, reflectionPoint, math.pi/4-incomingAngle]
Example #10
0
def isect_point_tri_persp_to_world(perspective_matrix, point, p1, p2, p3,
                                   clip=True):
    """
    point:      透視投影座標。2d。3dの場合、zは無視される。
    p1, p2, p3: 透視東映座標。3d
    pointから画面に垂直に伸びる直線との交差判定をworld座標で行う。
    返り値はworld座標。
    """
    pmat = perspective_matrix
    pimat = pmat.inverted()
    
    orig = mul_persp(pimat, Vector((point[0], point[1], -1.0)))
    tag = mul_persp(pimat, Vector((point[0], point[1], 0.5)))
    ray = tag - orig
    v1 = mul_persp(pimat, p1)
    v2 = mul_persp(pimat, p2)
    v3 = mul_persp(pimat, p3)
    ivec = geo.intersect_ray_tri(v1, v2, v3, ray, orig, clip)
    return ivec
Example #11
0
def isect_point_tri_persp_to_world(perspective_matrix, point, p1, p2, p3,
                                   clip=True):
    """
    point:      透視投影座標。2d。3dの場合、zは無視される。
    p1, p2, p3: 透視東映座標。3d
    pointから画面に垂直に伸びる直線との交差判定をworld座標で行う。
    返り値はworld座標。
    """
    pmat = perspective_matrix
    pimat = pmat.inverted()
    
    orig = mul_persp(pimat, Vector((point[0], point[1], -1.0)))
    tag = mul_persp(pimat, Vector((point[0], point[1], 0.5)))
    ray = tag - orig
    v1 = mul_persp(pimat, p1)
    v2 = mul_persp(pimat, p2)
    v3 = mul_persp(pimat, p3)
    ivec = geo.intersect_ray_tri(v1, v2, v3, ray, orig, clip)
    return ivec
Example #12
0
    def _intersect_edge_face(self, edge, face, ix_points):
        """ Compute intersection for one edge and one face.
        Returns None if no intersection occured, and a IntersectionPoint
        if one is found.
        """
        if (edge, face) in self._saved_results:
            return self._saved_results[(edge, face)]

        vec_t_vert = Vector((edge.t_vert.pos))
        vec_b_vert = Vector((edge.b_vert.pos))
        vec_dir = vec_t_vert - vec_b_vert
        
        point = intersect_ray_tri(Vector((face.verts[0].pos)),
                                  Vector((face.verts[1].pos)),
                                  Vector((face.verts[2].pos)),
                                  vec_dir,
                                  vec_b_vert,
                                  True) # restrict ix to tri face instead of plane
        # point = intersect_ray_tri(face.verts[0].get_blender_pos(),
        #                           face.verts[1].get_blender_pos(),
        #                           face.verts[2].get_blender_pos(),
        #                           edge.b_vert.get_blender_pos(),
        #                           dir_vector)

        if not point:
            ix_point = None
        elif (self._get_norm_squared(point - vec_b_vert) >
              self._get_norm_squared(vec_dir)):
            ix_point = None
        elif (point - vec_b_vert) * vec_dir < 0:
            ix_point = None
        else:
            # print ("Found ixpoint! " + str(point))
            ix_point = IntersectionPoint(edge, face, point)
            ix_points.append(ix_point)
            
        self._saved_results[(edge, face)] = ix_point

        return ix_point
Example #13
0
def get_intesection(ob, origin, ray_dir):
    if None not in (ob, origin, ray_dir) and ob.type == 'MESH':
        scene = bpy.context.scene
        bm = bmesh.new()
        bm.from_object(ob, scene)
        bm.transform(ob.matrix_world)
        bmesh.ops.triangulate(bm, faces=bm.faces)

        for face in bm.faces:
            intersection = intersect_ray_tri(face.verts[0].co,
                                             face.verts[1].co,
                                             face.verts[2].co, ray_dir, origin)
            if intersection is not None:
                draw_cross(intersection, "Intersection found:")
                # print("Intersection found:", intersection)
                # # drawing cross in place of intersection
                # inter = bpy.data.objects.new("Intersection", None)
                # inter.location = intersection
                # inter.empty_draw_size = 2
                # scene.objects.link(inter)

                break  # find the first intersection only

    return intersection
    def execute(self, context):

        self.hcol = context.preferences.addons[
            'kekit'].preferences.modal_color_header
        self.tcol = context.preferences.addons[
            'kekit'].preferences.modal_color_text
        self.scol = context.preferences.addons[
            'kekit'].preferences.modal_color_subtext

        if self.ke_fitprim_option == "BOX":
            self.boxmode, self.world = True, False
        elif self.ke_fitprim_option == "CYL":
            self.boxmode, self.world = False, False
        elif self.ke_fitprim_option == "SPHERE":
            self.boxmode, self.world, self.sphere = False, False, True
        elif self.ke_fitprim_option == "QUADSPHERE":
            self.boxmode, self.world, self.sphere = False, False, True
        else:
            self.boxmode, self.world, self.sphere = False, False, False

        if bpy.context.scene.kekit.fitprim_item:
            self.itemize = True
        else:
            self.itemize = self.ke_fitprim_itemize

        self.modal = bpy.context.scene.kekit.fitprim_modal
        self.cyl_sides = bpy.context.scene.kekit.fitprim_sides
        self.select = bpy.context.scene.kekit.fitprim_select
        self.unit = round(bpy.context.scene.kekit.fitprim_unit, 4)
        self.sphere_ring = bpy.context.scene.kekit.fitprim_sphere_ring
        self.sphere_seg = bpy.context.scene.kekit.fitprim_sphere_seg
        self.quadsphere_seg = bpy.context.scene.kekit.fitprim_quadsphere_seg

        self.edit_mode = bpy.context.mode
        sel_verts = []
        sel_verts2 = []
        multi_object_mode = False

        cursor = context.scene.cursor
        self.og_cloc = cursor.location.copy()
        self.og_crot = cursor.rotation_euler.copy()
        og_orientation = str(context.scene.transform_orientation_slots[0].type)

        if self.itemize or self.edit_mode == "OBJECT" and context.object is not None:
            # make sure the new object is in the same layer as context object
            objc = context.object.users_collection[0]
            layer_collection = context.view_layer.layer_collection
            layer_coll = get_layer_collection(layer_collection, objc.name)
            alc = context.view_layer.active_layer_collection

            if objc.name != alc.name and alc.name != "Master Collection":
                context.view_layer.active_layer_collection = layer_coll

            elif objc.name != "Master Collection" and objc.name != alc.name:
                context.view_layer.active_layer_collection = layer_coll

        # -----------------------------------------------------------------------------------------
        # MULTI OBJECT CHECK & SETUP
        # -----------------------------------------------------------------------------------------
        if self.edit_mode == "EDIT_MESH":
            sel_mode = [b for b in bpy.context.tool_settings.mesh_select_mode]
            other_side = []
            sel_obj = [
                o for o in bpy.context.selected_objects if o.type == "MESH"
            ]
            if len(sel_obj) == 2:
                multi_object_mode = True

            obj = bpy.context.active_object
            obj_mtx = obj.matrix_world.copy()
            second_obj = []

            bm = bmesh.from_edit_mesh(obj.data)
            bm.faces.ensure_lookup_table()
            bm.verts.ensure_lookup_table()

            sel_verts = [v for v in bm.verts if v.select]

            if sel_mode[2]:
                sel_poly = [p for p in bm.faces if p.select]
                active_face = bm.faces.active
                if active_face not in sel_poly:
                    active_face = None

            if multi_object_mode:
                second_obj = [o for o in sel_obj if o != obj][0]
                bm2 = bmesh.from_edit_mesh(second_obj.data)
                obj_mtx2 = second_obj.matrix_world.copy()

                sel_poly2 = [p for p in bm2.faces if p.select]
                if sel_poly2:
                    active_face2 = bm2.faces.active
                    if active_face2 not in sel_poly2:
                        active_face2 = None
                    if active_face2:
                        sel_verts2 = active_face2.verts
                    else:
                        sel_verts2 = sel_poly2[0].verts
                        active_face2 = sel_poly2[0]  # haxxfixxx
                else:
                    sel_verts2 = [v for v in bm2.verts if v.select]

                if not sel_verts2:
                    multi_object_mode = False

                elif sel_mode[0]:
                    # Just for 2-vert mode in multiobj mode
                    ole = sel_verts2[0].link_edges[:]
                    other_side = get_shortest(obj_mtx2, ole)

            side = None
            distance = 0
            vps = []
            normal, setpos, setrot, center = None, None, None, None
            first_island = None
            island_mode = False

        # -----------------------------------------------------------------------------------------
        # NO SELECTION MODE
        # -----------------------------------------------------------------------------------------
        if not sel_verts or self.edit_mode == "OBJECT":

            # Check mouse over target
            if not self.edit_mode == "OBJECT":
                bpy.ops.object.mode_set(mode="OBJECT")

            hit_obj, hit_wloc, hit_normal, hit_face = mouse_raycast(
                context, self.mouse_pos, evaluated=True)

            if not self.edit_mode == "OBJECT":
                bpy.ops.object.mode_set(mode="EDIT")

            if self.edit_mode != "OBJECT" and hit_obj is not None:
                if len(hit_obj.modifiers) > 0:
                    self.report({
                        "INFO"
                    }, "FitPrim: Selected elements required in Edit Mode if Modifiers exist"
                                )
                    return {"CANCELLED"}

            if hit_obj and hit_face is not None:
                # mouse over placement on face
                self.world = False
                mtx = hit_obj.matrix_world
                eks = hit_obj.data.polygons[hit_face].edge_keys
                vecs = []

                for vp in eks:
                    vc1 = mtx @ hit_obj.data.vertices[vp[0]].co
                    vc2 = mtx @ hit_obj.data.vertices[vp[1]].co
                    vecs.append(Vector(vc1 - vc2).normalized())

                evps = []
                for vp in eks:
                    v1, v2 = hit_obj.data.vertices[
                        vp[0]], hit_obj.data.vertices[vp[1]]
                    evps.append([v1, v2])

                side, center, start_vec = get_sides(mtx, vecs, evps)

                if self.ke_fitprim_option == "PLANE" and self.edit_mode == "OBJECT":
                    setpos = center
                    # setpos = mtx @ hit_obj.data.polygons[hit_face].center
                    side *= 2

                else:
                    offset = hit_normal * (side / 2)
                    setpos = center + offset
                    # setpos = mtx @ hit_obj.data.polygons[hit_face].center + offset

                setrot = rotation_from_vector(hit_normal, start_vec)

            else:
                # view placement compensation & Z0 drop
                view_vec = region_2d_to_vector_3d(context.region,
                                                  context.space_data.region_3d,
                                                  self.mouse_pos)
                view_pos = context.space_data.region_3d.view_matrix.inverted(
                ).translation

                if get_view_type() != "ORTHO":
                    ground = ((0, 0, 0), (0, 1, 0), (1, 0, 0))
                    raypos = intersect_ray_tri(ground[0], ground[1], ground[2],
                                               view_vec, view_pos, False)
                    snap_val = str(self.unit).split('.')
                    if int(snap_val[0]) > 0:
                        snap = 0
                    else:
                        snap = len(snap_val[1])

                    setpos = Vector((round(raypos[0],
                                           snap), round(raypos[1],
                                                        snap), self.unit / 2))
                else:
                    setpos = region_2d_to_location_3d(
                        context.region, context.space_data.region_3d,
                        self.mouse_pos, view_vec)

                self.world = True

                side = self.unit
                if self.ke_fitprim_option == "PLANE" and self.edit_mode == "OBJECT":
                    side *= 2

            distance = side
            sel_mode = (True, False, False)

        # -----------------------------------------------------------------------------------------
        # VERT MODE(s)
        # -----------------------------------------------------------------------------------------
        elif sel_mode[0]:

            if len(sel_verts) == 1 and not multi_object_mode:
                # 1-VERT MODE
                self.world = True
                side = get_shortest(obj_mtx, sel_verts[0].link_edges[:])
                distance = side
                setpos = obj_mtx @ sel_verts[0].co

            elif len(sel_verts) == 2 and not multi_object_mode or \
                    multi_object_mode and len(sel_verts2) == 1 and len(sel_verts) == 1:
                # 2 Vert mode
                if multi_object_mode:
                    p1 = obj_mtx @ sel_verts[0].co
                    p2 = obj_mtx2 @ sel_verts2[0].co
                    side = [other_side]
                    con_edges = sel_verts[0].link_edges[:]
                    side.append(get_shortest(obj_mtx, con_edges))
                    side = sorted(side)[0]
                else:
                    p1 = obj_mtx @ sel_verts[0].co
                    p2 = obj_mtx @ sel_verts[1].co
                    con_edges = sel_verts[0].link_edges[:] + sel_verts[
                        1].link_edges[:]
                    side = get_shortest(obj_mtx, con_edges)

                v_1 = Vector(p1 - p2).normalized()
                v_2 = Vector((0, 0, 1))
                if abs(v_1.dot(v_2)) == 1:
                    v_2 = Vector((1, 0, 0))
                n_v = v_1.cross(v_2).normalized()
                u_v = n_v.cross(v_1).normalized()
                t_v = u_v.cross(n_v).normalized()

                setrot = Matrix((u_v, n_v, t_v)).to_4x4().inverted()
                distance = get_distance(p1, p2)
                setpos = Vector(get_midpoint(p1, p2))

            elif len(sel_verts) == 4 or multi_object_mode and len(
                    sel_verts2) + len(sel_verts) <= 4:
                # 4-vert Rectangle mode
                # holy brute force batman
                if not second_obj:
                    second_obj = obj
                tri = tri_order(((obj, sel_verts), (second_obj, sel_verts2)))
                q = tri[-1]
                # just placing a unit prim with min side in the center (for this one) so disregarding other vecs
                v1 = Vector(tri[0][0].matrix_world @ tri[0][1].co -
                            tri[1][0].matrix_world @ tri[1][1].co)
                v2 = Vector(tri[0][0].matrix_world @ tri[0][1].co -
                            tri[2][0].matrix_world @ tri[2][1].co)
                # v3 = Vector(q[0].matrix_world @ q[1].co - tri[1][0].matrix_world @ tri[1][1].co)
                # v4 = Vector(q[0].matrix_world @ q[1].co - tri[2][0].matrix_world @ tri[2][1].co)

                d1 = get_distance(tri[0][0].matrix_world @ tri[0][1].co,
                                  tri[1][0].matrix_world @ tri[1][1].co)
                # d2 = get_distance(tri[0][0].matrix_world @ tri[0][1].co, tri[2][0].matrix_world @ tri[2][1].co)
                # d3 = get_distance(q[0].matrix_world @ q[1].co, tri[1][0].matrix_world @ tri[1][1].co)
                d4 = get_distance(q[0].matrix_world @ q[1].co,
                                  tri[2][0].matrix_world @ tri[2][1].co)

                if d1 < d4:
                    side = d1
                else:
                    side = d4
                distance = side

                ap1 = average_vector([obj_mtx @ v.co for v in sel_verts])
                if sel_verts2:
                    ap2 = average_vector([obj_mtx2 @ v.co for v in sel_verts2])
                    setpos = average_vector((ap1, ap2))
                else:
                    setpos = ap1

                n = v1.normalized().cross(v2.normalized())
                u = n.cross(v1.normalized())
                t = u.cross(n)
                setrot = Matrix((u, n, t)).to_4x4().inverted()

            else:
                self.report({
                    "INFO"
                }, "FitPrim: Invalid Vert Mode Selection: Select 1, 2 or 4 verts"
                            )
                return {"CANCELLED"}

        # -----------------------------------------------------------------------------------------
        # EDGE MODE
        # -----------------------------------------------------------------------------------------
        elif sel_mode[1]:

            one_line, loops, loops2 = False, [], []
            sel_edges = [e for e in bm.edges if e.select]
            vps = [e.verts[:] for e in sel_edges]

            active_edge = bm.select_history.active
            if active_edge:
                active_edge_facenormals = [
                    correct_normal(obj_mtx, p.normal)
                    for p in active_edge.link_faces
                ]
                active_edge_normal = Vector(
                    average_vector(active_edge_facenormals)).normalized()

            # GET ISLANDS
            if multi_object_mode and active_edge:
                # print("multi obj mode") # todo: limited multiobj mode, rework one-for-all?
                sel_edges2 = [e for e in bm2.edges if e.select]
                vps2 = [e.verts for e in sel_edges2]
                p1 = get_loops(vps, legacy=True)
                p2 = get_loops(vps2, legacy=True)
                if p1 and p2:
                    a1, a2 = obj_mtx @ p1[0][0].co, obj_mtx @ p1[0][-1].co
                    b1, b2 = obj_mtx2 @ p2[0][0].co, obj_mtx2 @ p2[0][-1].co
                else:
                    a1, a2 = obj_mtx @ sel_verts[0].co, obj_mtx @ sel_verts[
                        -1].co
                    b1, b2 = obj_mtx2 @ sel_verts2[
                        0].co, obj_mtx2 @ sel_verts2[-1].co

                b_avg = get_midpoint(b1, b2)
                spacing, mp = get_closest_midpoint(a1, a2, b_avg)

                u_v = Vector(a1 - b1).normalized()
                t_v = Vector(a1 - a2).normalized()
                n_v = u_v.cross(t_v).normalized()

                setrot = rotation_from_vector(n_v, t_v, rotate90=True)
                setpos = mp
                side = spacing
                distance = spacing

            elif active_edge:  # same (active) obj island selections
                if len(sel_edges) > 1:

                    if len(sel_edges) == 2 and not bool(
                            set(sel_edges[0].verts).intersection(
                                sel_edges[1].verts)):
                        a1p, a2p = sel_edges[0].verts, sel_edges[1].verts
                        a1, a2 = obj_mtx @ a1p[0].co, obj_mtx @ a1p[1].co
                        b_avg = average_vector(
                            [obj_mtx @ a2p[0].co, obj_mtx @ a2p[1].co])

                        lf1 = sel_edges[0].link_faces[:]
                        lf = [
                            f for f in sel_edges[1].link_faces[:] if f in lf1
                        ]

                        v1 = Vector((obj_mtx @ a1p[0].co -
                                     obj_mtx @ a1p[1].co)).normalized()
                        v2 = Vector((obj_mtx @ a2p[0].co -
                                     obj_mtx @ a2p[1].co)).normalized()

                        if not lf:
                            u_v = Vector(a1 -
                                         (obj_mtx @ a2p[0].co)).normalized()
                            t_v = Vector(a1 - a2).normalized()
                            n_v = u_v.cross(t_v).normalized()
                            setrot = rotation_from_vector(n_v,
                                                          t_v,
                                                          rotate90=True)
                        else:
                            n = correct_normal(obj_mtx, lf[0].normal)
                            # t = average_vector((v1, v2))
                            # if sum(t) == 0:
                            #     print("AVG FAIL")
                            t = v1
                            setrot = rotation_from_vector(n, t, rotate90=True)

                        spacing, mp = get_closest_midpoint(a1, a2, b_avg)
                        setpos = mp
                        side = spacing
                        distance = spacing
                    else:
                        loops = get_loops(vps, legacy=True)

                if len(loops) > 1:
                    # print ("single obj - multi loop", len(loops))
                    # Check for closed loops
                    a_ep1, a_ep2 = loops[0][0], loops[0][-1]
                    b_ep1, b_ep2 = loops[1][0], loops[1][-1]
                    if a_ep1 == a_ep2:
                        a_ep2 = loops[0][-2]
                    if b_ep1 == b_ep2:
                        b_ep2 = loops[1][-2]

                    # get coords & set vals
                    a1, a2 = obj_mtx @ a_ep1.co, obj_mtx @ a_ep2.co
                    b1, b2 = obj_mtx @ b_ep1.co, obj_mtx @ b_ep2.co

                    b_avg = get_midpoint(b1, b2)
                    spacing, mp = get_closest_midpoint(a1, a2, b_avg)

                    u_v = Vector((0, 0, 1))
                    t_v = Vector(a1 - a2).normalized()
                    if abs(u_v.dot(t_v)) == 1:
                        u_v = Vector((1, 0, 0))

                    n_v = u_v.cross(t_v).normalized()

                    setrot = rotation_from_vector(n_v, t_v, rotate90=False)
                    setpos = mp
                    side = spacing
                    distance = spacing

                elif len(loops) == 1 or len(sel_edges) == 1:
                    # print ("single obj - single loop")
                    single_line = False
                    if len(sel_edges) != 1:
                        if loops[0][0] != loops[0][-1]:
                            single_line = True

                    if len(sel_edges) != 1 and not single_line:
                        vecs = [
                            Vector((obj_mtx @ vp[0].co) -
                                   (obj_mtx @ vp[1].co)).normalized()
                            for vp in vps
                        ]
                        normal = average_vector([
                            correct_normal(obj_mtx, v.normal)
                            for v in flatten(vps)
                        ])

                        side, center, start_vec = get_sides(obj_mtx, vecs, vps)
                        distance = side
                        setpos = center
                        setrot = rotation_from_vector(normal, start_vec)

                    elif len(sel_edges) == 1 or single_line:
                        # print("1 edge --> one line", one_line)
                        p1, p2 = obj_mtx @ sel_verts[
                            0].co, obj_mtx @ sel_verts[1].co
                        t_v = Vector(p1 - p2).normalized()
                        n_v = active_edge_normal

                        setrot = rotation_from_vector(n_v, t_v)
                        distance = get_distance(p1, p2)
                        setpos = get_midpoint(p1, p2)
                        side = distance
                    else:
                        print("Unexpected: Aborting operation.")
                        return {'CANCELLED'}

        # -----------------------------------------------------------------------------------------
        # FACE MODE
        # -----------------------------------------------------------------------------------------
        elif sel_mode[2] and active_face and sel_poly:
            fail_island = False

            # GET ISLANDS
            if multi_object_mode and active_face:
                first_island = sel_verts
                point = obj_mtx @ active_face.calc_center_median()
                firstcos = [obj_mtx @ v.co for v in active_face.verts]
                secondcos = [obj_mtx2 @ v.co for v in active_face2.verts]

                if firstcos and secondcos:
                    island_mode = True

                    distance = point_to_plane(point, firstcos, secondcos)
                    if distance:
                        distance = abs(distance)
                    else:
                        # print("Multi obj Point to plane failed for some reason - using single island mode.")
                        # island_mode = False
                        fail_island = True

            else:  # same (active) obj island selections
                first_island, second_island = get_selection_islands(
                    sel_poly, active_face)
                # Ofc, needs correct order for point to plane, and I'll just rely on face.verts for that:
                calc_island_1 = active_face.verts[:]
                calc_island_2 = []
                for p in sel_poly:
                    verts = p.verts[:]
                    for v in verts:
                        if v in second_island:
                            calc_island_2 = verts
                            break

                if len(first_island) != 0 and len(second_island) != 0:
                    firstcos = [obj_mtx @ v.co for v in calc_island_1]
                    secondcos = [obj_mtx @ v.co for v in calc_island_2]
                    distance = point_to_plane(
                        obj_mtx @ active_face.calc_center_median(), firstcos,
                        secondcos)

                    if distance:
                        distance = abs(distance)
                        island_mode = True
                    else:
                        # print("Point to plane failed for some reason - using single island mode.")
                        fail_island = True
                    # print(distance)
                else:
                    # Ngon mode
                    first_island = sel_verts

            # GETVALUES
            if island_mode or fail_island:
                bpy.ops.mesh.select_all(action='DESELECT')

                for v in first_island:
                    bm.verts[v.index].select = True

                bmesh.update_edit_mesh(obj.data)
                bpy.ops.mesh.select_mode(type='VERT')
                bpy.ops.mesh.select_mode(type='FACE')
                bm.faces.ensure_lookup_table()

            faces = [f for f in bm.faces if f.select]
            normal = average_vector(
                [correct_normal(obj_mtx, f.normal) for f in faces])
            bpy.ops.mesh.region_to_loop()

            sel_edges = [e for e in bm.edges if e.select]
            vps = [e.verts[:] for e in sel_edges]
            vecs = [
                Vector((obj_mtx @ vp[0].co) -
                       (obj_mtx @ vp[1].co)).normalized() for vp in vps
            ]

            side, center, start_vec = get_sides(obj_mtx, vecs, vps)
            setrot = rotation_from_vector(normal, start_vec)

        # -----------------------------------------------------------------------------------------
        # PROCESS
        # -----------------------------------------------------------------------------------------
        if side:
            if self.ke_fitprim_option == "PLANE" and self.edit_mode != "OBJECT":
                if sel_mode[2]:
                    setpos = center

            elif len(sel_verts) == 0 or sel_mode[0] or sel_mode[1]:
                side *= .5
                distance = distance / 2
                if self.sphere and sel_mode[0]:
                    if not len(sel_verts) == 0:
                        side = distance

            elif sel_mode[2]:
                if island_mode:
                    side *= .5
                    offset = normal * distance * .5
                    distance *= .5
                    if self.sphere:
                        side = distance
                else:
                    side *= .5
                    distance = side
                    offset = normal * side

                setpos = center + offset
                if not island_mode and self.sphere:
                    setpos = setpos - offset

            # SET FINAL ROTATION
            if self.world:
                setrot = (0, 0, 0)
                if self.ke_fitprim_option == "PLANE" and not sel_verts:
                    setpos[2] = 0
            else:
                setrot = setrot.to_euler()

            # RUN OP
            if not self.edit_mode == "OBJECT":
                bpy.ops.mesh.select_mode(type='FACE')

            if self.itemize:
                if self.edit_mode != "OBJECT":
                    bpy.ops.object.mode_set(mode="OBJECT")
                bpy.ops.transform.select_orientation(orientation='GLOBAL')
                cursor.location = setpos
                cursor.rotation_euler = setrot

            # -----------------------------------------------------------------------------------------
            # CUBE
            # -----------------------------------------------------------------------------------------
            if self.boxmode:
                bpy.ops.mesh.primitive_box_add(width=side,
                                               depth=side,
                                               height=distance,
                                               align='WORLD',
                                               location=setpos,
                                               rotation=setrot)
                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.shade_smooth()
                    bpy.context.object.data.use_auto_smooth = True

            # -----------------------------------------------------------------------------------------
            # PLANE
            # -----------------------------------------------------------------------------------------
            elif self.ke_fitprim_option == "PLANE":

                # side *= 2
                enter = True

                if self.edit_mode == 'OBJECT' or self.itemize:
                    enter = False

                bpy.ops.mesh.primitive_plane_add(enter_editmode=enter,
                                                 align='WORLD',
                                                 location=setpos,
                                                 rotation=setrot,
                                                 size=side,
                                                 scale=(1, 1, 1))
                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.shade_smooth()
                    bpy.context.object.data.use_auto_smooth = True

            # -----------------------------------------------------------------------------------------
            # SPHERE
            # -----------------------------------------------------------------------------------------
            elif self.sphere and not self.ke_fitprim_option == "QUADSPHERE":
                bpy.ops.mesh.primitive_uv_sphere_add(
                    segments=self.sphere_seg,
                    ring_count=self.sphere_ring,
                    radius=side,
                    align='WORLD',
                    location=setpos,
                    rotation=setrot)
                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.shade_smooth()
                    bpy.context.object.data.use_auto_smooth = True

            # -----------------------------------------------------------------------------------------
            # QUADSPHERE
            # -----------------------------------------------------------------------------------------
            elif self.ke_fitprim_option == "QUADSPHERE":
                cutnr = self.quadsphere_seg

                # calc compensated subd radius from cube
                v1_pos = setpos[0] + side, setpos[1] + side, setpos[2] + side
                rad = sqrt(sum([(a - b)**2 for a, b in zip(setpos, v1_pos)]))
                diff = side / rad

                side = (side * diff)
                distance = side

                bpy.ops.mesh.primitive_box_add(width=side,
                                               depth=side,
                                               height=distance,
                                               align='WORLD',
                                               location=setpos,
                                               rotation=setrot,
                                               name="QuadSphere")

                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.mode_set(mode="EDIT")
                    bpy.ops.mesh.subdivide(number_cuts=cutnr, smoothness=1)
                    bpy.ops.mesh.faces_shade_smooth()
                    bpy.ops.object.mode_set(mode="OBJECT")
                else:
                    bpy.ops.mesh.subdivide(number_cuts=cutnr, smoothness=1)
                    bpy.ops.mesh.faces_shade_smooth()

                bpy.ops.ed.undo_push()
                # bpy.context.object.data.use_auto_smooth = as_check

                if self.itemize or self.edit_mode == "OBJECT":
                    bpy.ops.object.shade_smooth()
                    bpy.context.object.data.use_auto_smooth = True

            # -----------------------------------------------------------------------------------------
            # CYLINDER
            # -----------------------------------------------------------------------------------------
            else:
                bpy.ops.mesh.primitive_cylinder_add(vertices=self.cyl_sides,
                                                    radius=side,
                                                    depth=distance * 2,
                                                    enter_editmode=False,
                                                    align='WORLD',
                                                    location=setpos,
                                                    rotation=setrot)

                if self.modal:
                    if self.itemize or self.edit_mode == "OBJECT":
                        bpy.ops.object.mode_set(mode="EDIT")

                    self.settings = (side, distance, setpos, setrot)
                    context.window_manager.modal_handler_add(self)

                    self._timer = context.window_manager.event_timer_add(
                        0.5, window=context.window)

                    args = (self, context, self.screen_x)
                    self._handle = bpy.types.SpaceView3D.draw_handler_add(
                        draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
                    bpy.context.space_data.overlay.show_cursor = False
                    return {'RUNNING_MODAL'}

            if multi_object_mode and not self.itemize:
                second_obj.select_set(state=True)
                obj.select_set(state=True)
                bpy.ops.object.editmode_toggle()
                bpy.ops.object.editmode_toggle()
        else:
            self.report({"INFO"},
                        "FitPrim: Invalid Selection / No Active Element?")

        if not self.select and not self.itemize and not self.edit_mode == "OBJECT":
            bpy.ops.mesh.select_all(action='DESELECT')

        if self.itemize:
            bpy.ops.transform.select_orientation(orientation=og_orientation)
            cursor.location = self.og_cloc
            cursor.rotation_euler = self.og_crot
            bpy.context.active_object.select_set(state=True)

        self.ke_fitprim_itemize = False
        return {"FINISHED"}
Example #15
0
def snap_utilities(self,
                   context,
                   obj_matrix_world,
                   bm_geom,
                   bool_update,
                   mcursor,
                   outer_verts=False,
                   constrain=None,
                   previous_vert=None,
                   ignore_obj=None,
                   increment=0.0):

    rv3d = context.region_data
    region = context.region
    is_increment = False

    if not hasattr(self, 'snap_cache'):
        self.snap_cache = True
        self.type = 'OUT'
        self.bvert = None
        self.bedge = None
        self.bface = None
        self.hit = False
        self.out_obj = None

    if bool_update:
        #self.bvert = None
        self.bedge = None
        #self.bface = None

    if isinstance(bm_geom, bmesh.types.BMVert):
        self.type = 'VERT'

        if self.bvert != bm_geom:
            self.bvert = bm_geom
            self.vert = obj_matrix_world * self.bvert.co
            #self.Pvert = location_3d_to_region_2d(region, rv3d, self.vert)

        if constrain:
            #self.location = (self.vert-self.const).project(vector_constrain) + self.const
            location = intersect_point_line(self.vert, constrain[0],
                                            constrain[1])
            #factor = location[1]
            self.location = location[0]
        else:
            self.location = self.vert

    elif isinstance(bm_geom, bmesh.types.BMEdge):
        if self.bedge != bm_geom:
            self.bedge = bm_geom
            self.vert0 = obj_matrix_world * self.bedge.verts[0].co
            self.vert1 = obj_matrix_world * self.bedge.verts[1].co
            self.po_cent = (self.vert0 + self.vert1) / 2
            self.Pcent = location_3d_to_region_2d(region, rv3d, self.po_cent)
            self.Pvert0 = location_3d_to_region_2d(region, rv3d, self.vert0)
            self.Pvert1 = location_3d_to_region_2d(region, rv3d, self.vert1)

            if previous_vert and previous_vert not in self.bedge.verts:
                pvert_co = obj_matrix_world * previous_vert.co
                point_perpendicular = intersect_point_line(
                    pvert_co, self.vert0, self.vert1)
                self.po_perp = point_perpendicular[0]
                #factor = point_perpendicular[1]
                self.Pperp = location_3d_to_region_2d(region, rv3d,
                                                      self.po_perp)

        if constrain:
            location = intersect_line_line(constrain[0], constrain[1],
                                           self.vert0, self.vert1)
            if location == None:
                is_increment = True
                orig, view_vector = region_2d_to_orig_and_view_vector(
                    region, rv3d, mcursor)
                end = orig + view_vector
                location = intersect_line_line(constrain[0], constrain[1],
                                               orig, end)
            if location:
                self.location = location[0]
            else:
                self.location = constrain[0]

        elif hasattr(self,
                     'Pperp') and abs(self.Pperp[0] - mcursor[0]) < 10 and abs(
                         self.Pperp[1] - mcursor[1]) < 10:
            self.type = 'PERPENDICULAR'
            self.location = self.po_perp

        elif abs(self.Pcent[0] - mcursor[0]) < 10 and abs(self.Pcent[1] -
                                                          mcursor[1]) < 10:
            self.type = 'CENTER'
            self.location = self.po_cent

        else:
            if increment and previous_vert in self.bedge.verts:
                is_increment = True
            self.type = 'EDGE'
            orig, view_vector = region_2d_to_orig_and_view_vector(
                region, rv3d, mcursor)
            end = orig + view_vector
            location = intersect_line_line(self.vert0, self.vert1, orig, end)
            if location:
                self.location = location[0]
            else:  # Impossível, uma vez que não dá para selecionar essa edge.
                self.location = self.po_cent

    elif isinstance(bm_geom, bmesh.types.BMFace):
        is_increment = True
        self.type = 'FACE'

        if self.bface != bm_geom:
            self.bface = bm_geom
            self.face_center = obj_matrix_world * bm_geom.calc_center_median()
            self.face_normal = bm_geom.normal * obj_matrix_world.inverted()

        orig, view_vector = region_2d_to_orig_and_view_vector(
            region, rv3d, mcursor)
        end = orig + view_vector
        location = intersect_line_plane(orig, end, self.face_center,
                                        self.face_normal, False)
        if not location:
            l1 = self.bface.loops[0]
            l = l1.link_loop_next
            loc_orig = obj_matrix_world.inverted() * orig
            loc_ray = view_vector * obj_matrix_world
            while l != l1:
                location = intersect_ray_tri(l1.vert.co, l.vert.co,
                                             l.link_loop_next.vert.co, loc_ray,
                                             loc_orig, True)
                if location:
                    location = obj_matrix_world * location
                    print("intersect_ray_tri", location)
                    break
                l = l.link_loop_next
            else:  # Pelo Clip ser True as vezes location pode ser None e isso dá erro
                location = self.face_center

        if constrain:
            is_increment = False
            location = intersect_point_line(location, constrain[0],
                                            constrain[1])[0]

        self.location = location

    else:
        is_increment = True
        self.type = 'OUT'

        orig, view_vector = region_2d_to_orig_and_view_vector(
            region, rv3d, mcursor)

        if B_VERSION:
            result, self.location, normal, face_index, self.out_obj, self.out_mat = context.scene.ray_cast(
                orig, view_vector, 3.3e+38)
            if result and self.out_obj != ignore_obj:
                self.type = 'FACE'
                if outer_verts:
                    if face_index != -1:
                        try:
                            verts = self.out_obj.data.polygons[
                                face_index].vertices
                            v_dist = 100

                            for i in verts:
                                v_co = self.out_mat * self.out_obj.data.vertices[
                                    i].co
                                v_2d = location_3d_to_region_2d(
                                    region, rv3d, v_co)
                                dist = (Vector(mcursor) - v_2d).length_squared
                                if dist < v_dist:
                                    is_increment = False
                                    self.type = 'VERT'
                                    v_dist = dist
                                    self.location = v_co
                        except:
                            print('Fail')
                if constrain:
                    is_increment = False
                    self.preloc = self.location
                    self.location = intersect_point_line(
                        self.preloc, constrain[0], constrain[1])[0]
            else:
                if constrain:
                    location = intersect_line_line(constrain[0], constrain[1],
                                                   orig, orig + view_vector)
                    if location:
                        self.location = location[0]
                    else:
                        self.location = constrain[0]
                else:
                    self.location = out_Location(rv3d, region, orig,
                                                 view_vector)

        else:  ### VERSION 2.76 deprecated ###
            end = orig + view_vector * 1000

            result, self.out_obj, self.out_mat, self.location, normal = context.scene.ray_cast(
                orig, end)

            if result and self.out_obj != ignore_obj:
                self.type = 'FACE'
                if outer_verts:
                    # get the ray relative to the self.out_obj
                    self.out_mat_inv = self.out_mat.inverted()
                    ray_origin_obj = self.out_mat_inv * orig
                    ray_target_obj = self.out_mat_inv * end
                    try:
                        location, normal, face_index = self.out_obj.ray_cast(
                            ray_origin_obj, ray_target_obj)
                        if face_index == -1:
                            self.out_obj = None
                        else:
                            self.location = self.out_mat * location
                            verts = self.out_obj.data.polygons[
                                face_index].vertices
                            v_dist = 100

                            for i in verts:
                                v_co = self.out_mat * self.out_obj.data.vertices[
                                    i].co
                                v_2d = location_3d_to_region_2d(
                                    region, rv3d, v_co)
                                dist = (Vector(mcursor) - v_2d).length_squared
                                if dist < v_dist:
                                    is_increment = False
                                    self.type = 'VERT'
                                    v_dist = dist
                                    self.location = v_co
                    except Exception as e:
                        print(e)
                if constrain:
                    is_increment = False
                    self.preloc = self.location
                    self.location = intersect_point_line(
                        self.preloc, constrain[0], constrain[1])[0]
            else:
                if constrain:
                    location = intersect_line_line(constrain[0], constrain[1],
                                                   orig, end)
                    if location:
                        self.location = location[0]
                    else:
                        self.location = constrain[0]
                else:
                    self.location = out_Location(rv3d, region, orig,
                                                 view_vector)
    ### END 2.76 VERSION ###

    if previous_vert:
        pvert_co = obj_matrix_world * previous_vert.co
        vec = self.location - pvert_co
        if is_increment and increment:
            pvert_co = obj_matrix_world * previous_vert.co
            vec = self.location - pvert_co
            self.len = round((1 / increment) * vec.length) * increment
            self.location = self.len * vec.normalized() + pvert_co
        else:
            self.len = vec.length
Example #16
0
def perform_face_intersection():
    '''
    (currently) only points that are found on the face that is active, are accepted
    - In practice this means the last selected face will be used to receive
      intersection points.

    - step 2 (not implemented)
      will do the reverse... thus completing the loop.
    '''

    def rays_from_face(face):
        '''
        per edge (v1, v2) this returns the reverse edge too (v2, v1)
        in case the face intersection happens on the origin of the ray
        '''
        indices = [v.index for v in face.verts]
        indices.append(indices[0])  # makes cyclic
        edges_f = [(indices[i], indices[i + 1]) for i in range(len(indices) - 1)]  # forward
        edges_b = [(indices[i + 1], indices[i]) for i in range(len(indices) - 1)]  # backward
        return edges_f + edges_b

    def triangulated(face):
        return tessellate([[v.co for v in face.verts]])

    def get_selected_minus_active(bm_faces, active_idx):
        return [f for f in bm_faces if f.select and not (f.index == active_idx)]

    # Get the active mesh
    obj = bpy.context.edit_object
    me = obj.data

    bm = bmesh.from_edit_mesh(me)
    bm.verts.ensure_lookup_table()  # get 2.73+
    bm_verts = bm.verts
    bm_faces = bm.faces

    # Prime Face
    active = bm_faces.active

    # triangulate face to intersect
    tris_to_intersect = triangulated(active)  # list of collections of 3 coordinates
    av = active.verts
    tris = [[av[idx1].co, av[idx2].co, av[idx3].co] for idx1, idx2, idx3 in tris_to_intersect]

    # this will hold the set of edges used to raycast, using set will avoid many duplicates of touching
    # faces that share edges.
    test_rays = set()

    # check intersection with bidirectional of all edges of all faces that are selected but not active

    # Non Prime Faces
    faces_to_intersect_with = get_selected_minus_active(bm_faces, active.index)
    for face in faces_to_intersect_with:
        rays = rays_from_face(face)
        for ray in rays:
            test_rays.add(ray)

    vert_set = set()

    for v1, v2, v3 in tris:

        for ray_idx, orig_idx in test_rays:
            orig = bm_verts[orig_idx].co
            ray_original = bm_verts[ray_idx].co
            ray = (bm_verts[ray_idx].co - orig).normalized()
            pt = intersect_ray_tri(v1, v2, v3, ray, orig)
            if pt:
                # filter new verts,
                # they must lie on the line described by (origin, ray_original) then add.
                itx_res = intersect_point_line(pt, ray_original, orig)
                if itx_res:
                    v, dist = itx_res
                    if (0.0 < dist < 1.0):
                        vert_set.add(pt[:])

    print('found {0} unique new verts'.format(len(vert_set)))

    for v in vert_set:
        bm.verts.new(v)

    bmesh.update_edit_mesh(me, True)
Example #17
0
def intersect_ray_quad_3d(quad, origin, destination):
    ray = destination - origin
    p = intersect_ray_tri(quad[0], quad[1], quad[2], ray, origin)
    if p is None:
        p = intersect_ray_tri(quad[2], quad[3], quad[0], ray, origin)
    return p
def intersect_ray_quad_3d(quad, origin, destination):
	ray = destination - origin
	p = intersect_ray_tri(quad[0],quad[1],quad[2],ray,origin)
	if p is None:
		p = intersect_ray_tri(quad[2],quad[3],quad[0],ray,origin)
	return p
    def execute(self, context):
        o = context.object
        M = o.matrix_world
        if not context.mode == 'EDIT_MESH':
            self.report({'ERROR'}, 'Only works in edit mode')
            bpy.ops.object.mode_set(mode='EDIT')
            return {'FINISHED'}
        bpy.ops.object.mode_set(mode='OBJECT')
        selectedEdges = [e for e in bpy.context.object.data.edges if e.select]
        if len(selectedEdges) < 2:
            self.report({'ERROR'}, 'Insufficient number of edges selected')
            bpy.ops.object.mode_set(mode='EDIT')
            return {'FINISHED'}

        if len(selectedEdges) == 2:
            # Get the line intersection of the two edges
            Pints = intersect_line_line(
                M * o.data.vertices[selectedEdges[0].vertices[0]].co,
                M * o.data.vertices[selectedEdges[0].vertices[1]].co,
                M * o.data.vertices[selectedEdges[1].vertices[0]].co,
                M * o.data.vertices[selectedEdges[1].vertices[1]].co)

            if Pints is None:
                self.report({'ERROR'}, 'Selected edges are PARALLEL')
                bpy.ops.object.mode_set(mode='EDIT')
                return {'FINISHED'}

            bpy.context.scene.cursor_location = (Pints[0] + Pints[1]) / 2.0
            bpy.ops.object.mode_set(mode='EDIT')
            return {'FINISHED'}

        if len(selectedEdges) > 2:
            # Get the intersection of an edge and a face
            selectedEdges = selectedEdges[:3]

            # Find the edges that form a face (share a common vertex)
            commonEdges = []
            for e in selectedEdges:
                for ee in selectedEdges:
                    if e.index == ee.index:
                        continue
                    if (e.key[0] in ee.key) or (e.key[1] in ee.key):
                        commonEdges = [e, ee]

            if len(commonEdges) == 0:
                self.report({
                    'ERROR'
                }, 'No common edges of a TRIANGULAR FACE found.  Please select TWO common edges of a face and another edge.'
                            )
                bpy.ops.object.mode_set(mode='EDIT')
                return {'FINISHED'}

            lonelyEdge = set(selectedEdges).difference(set(commonEdges)).pop()
            # Get the intersection of the lonelyEdge and the face (assuming it is triangular)
            rayOrigin = o.data.vertices[lonelyEdge.vertices[0]].co
            rayDirection = o.data.vertices[
                lonelyEdge.vertices[1]].co - rayOrigin
            triVerts = []
            for e in commonEdges:
                triVerts += [v for v in e.vertices]
            triVerts = list(set(triVerts))
            if not len(triVerts) == 3:
                self.report(
                    {'ERROR'},
                    'Something is wrong with your triangle edge selection')
                bpy.ops.object.mode_set(mode='EDIT')
                return {'FINISHED'}
            Pint = intersect_ray_tri(M * o.data.vertices[triVerts[0]].co,
                                     M * o.data.vertices[triVerts[1]].co,
                                     M * o.data.vertices[triVerts[2]].co,
                                     rayDirection, rayOrigin)
            if Pint is None:
                self.report({
                    'ERROR'
                }, 'Edge and triangle do not intersect.  Try selecting another triangle.'
                            )
                bpy.ops.object.mode_set(mode='EDIT')
                return {'FINISHED'}

            bpy.context.scene.cursor_location = Pint

        bpy.ops.object.mode_set(mode='EDIT')
        return {'FINISHED'}
Example #20
0
def perform_face_intersection():
    '''
    (currently) only points that are found on the face that is active, are accepted
    - In practice this means the last selected face will be used to receive
      intersection points.

    - step 2 (not implemented)
      will do the reverse... thus completing the loop.
    '''
    def rays_from_face(face):
        '''
        per edge (v1, v2) this returns the reverse edge too (v2, v1)
        in case the face intersection happens on the origin of the ray
        '''
        indices = [v.index for v in face.verts]
        indices.append(indices[0])  # makes cyclic
        edges_f = [(indices[i], indices[i + 1])
                   for i in range(len(indices) - 1)]  # forward
        edges_b = [(indices[i + 1], indices[i])
                   for i in range(len(indices) - 1)]  # backward
        return edges_f + edges_b

    def triangulated(face):
        return tessellate([[v.co for v in face.verts]])

    def get_selected_minus_active(bm_faces, active_idx):
        return [
            f for f in bm_faces if f.select and not (f.index == active_idx)
        ]

    # Get the active mesh
    obj = bpy.context.edit_object
    me = obj.data

    bm = bmesh.from_edit_mesh(me)
    bm.verts.ensure_lookup_table()  # get 2.73+
    bm_verts = bm.verts
    bm_faces = bm.faces

    # Prime Face
    active = bm_faces.active

    # triangulate face to intersect
    tris_to_intersect = triangulated(
        active)  # list of collections of 3 coordinates
    av = active.verts
    tris = [[av[idx1].co, av[idx2].co, av[idx3].co]
            for idx1, idx2, idx3 in tris_to_intersect]

    # this will hold the set of edges used to raycast, using set will avoid many duplicates of touching
    # faces that share edges.
    test_rays = set()

    # check intersection with bidirectional of all edges of all faces that are selected but not active

    # Non Prime Faces
    faces_to_intersect_with = get_selected_minus_active(bm_faces, active.index)
    for face in faces_to_intersect_with:
        rays = rays_from_face(face)
        for ray in rays:
            test_rays.add(ray)

    vert_set = set()

    for v1, v2, v3 in tris:

        for ray_idx, orig_idx in test_rays:
            orig = bm_verts[orig_idx].co
            ray_original = bm_verts[ray_idx].co
            ray = (bm_verts[ray_idx].co - orig).normalized()
            pt = intersect_ray_tri(v1, v2, v3, ray, orig)
            if pt:
                # filter new verts,
                # they must lie on the line described by (origin, ray_original) then add.
                itx_res = intersect_point_line(pt, ray_original, orig)
                if itx_res:
                    v, dist = itx_res
                    if (0.0 < dist < 1.0):
                        vert_set.add(pt[:])

    print('found {0} unique new verts'.format(len(vert_set)))

    for v in vert_set:
        bm.verts.new(v)

    bmesh.update_edit_mesh(me, True)