Beispiel #1
0
def normal(*vecs):
    # 3~4個のVectorのNormalを求める
    if len(vecs) == 3:
        return geom.normal(*vecs)
    elif len(vecs) == 4:
        n1 = geom.normal(vecs[0], vecs[1], vecs[3])
        n2 = geom.normal(vecs[1], vecs[2], vecs[3])
        if n1.dot(n2) < 0:
            n2.negate()
        return (n1 + n2).normalized()
def get_color_from_normal(dvk, pol, num_verts, vectorlight, colo):
    if num_verts <= 4:
        normal_no = normal(dvk[pol[0]], dvk[pol[1]], dvk[pol[2]])
    else:
        normal_no = normal(dvk[pol[0]], dvk[pol[1]], dvk[pol[2]], dvk[pol[3]])

    normal_no = (normal_no.angle(vectorlight, 0)) / pi

    r = (normal_no * colo[0]) - 0.1
    g = (normal_no * colo[1]) - 0.1
    b = (normal_no * colo[2]) - 0.1
    return (r+0.2, g+0.2, b+0.2)
Beispiel #3
0
 def __init__(self, v1, v2, v3):
     self.verts = [v1, v2, v3]
     self.normal = geom.normal(v1.co, v2.co, v3.co)
     self.edge_keys = [tuple(sorted((self.verts[i - 1], self.verts[i]),
                                    key=lambda v: v.index))
                       for i in range(3)]
     self.outer_verts = []
Beispiel #4
0
def generate_3PT(pts, obj, nv, mode=1):
    mw = obj.matrix_world
    V = Vector
    nv = max(3, nv)

    # construction
    v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2])
    edge1_mid = v1.lerp(v2, 0.5)
    edge2_mid = v3.lerp(v4, 0.5)
    axis = geometry.normal(v1, v2, v4)
    mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis)

    # triangle edges
    v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid
    v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid
    v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid
    v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid

    r = geometry.intersect_line_line(v1_, v2_, v3_, v4_)
    if r:
        p1, _ = r
        cp = mw * p1
        bpy.context.scene.cursor_location = cp

        if mode == 0:
            pass

        elif mode == 1:
            generate_bmesh_repr(p1, v1, axis, nv)

    else:
        print('not on a circle')
Beispiel #5
0
def _binary_write(filepath, faces):
    with open(filepath, 'wb') as data:
        fw = data.write
        # header
        # we write padding at header beginning to avoid to
        # call len(list(faces)) which may be expensive
        fw(struct.calcsize('<80sI') * b'\0')

        # 3 vertex == 9f
        pack = struct.Struct('<9f').pack

        # number of vertices written
        nb = 0

        for face in faces:
            # calculate face normal
            # write normal + vertexes + pad as attributes
            fw(struct.pack('<3f', *normal(*face)) + pack(*itertools.chain.from_iterable(face)))
            # attribute byte count (unused)
            fw(b'\0\0') 
            nb += 1

        # header, with correct value now
        data.seek(0)
        fw(struct.pack('<80sI', _header_version().encode('ascii'), nb))
Beispiel #6
0
 def calc_vert_normal(self, vert, looptris, fallback=Vector((0, 0, 0))):
     normal = Vector()
     num = 0
     for tri in looptris:
         normal += geom.normal(*[loop.vert.co for loop in tri])
         num += 1
     return normal / num
Beispiel #7
0
def generate_3PT_mode_1_(pts, obj):
    origin = obj.location
    transform_matrix = obj.matrix_local
    V = Vector

    # construction
    v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2])
    edge1_mid = v1.lerp(v2, 0.5)
    edge2_mid = v3.lerp(v4, 0.5)
    axis = geometry.normal(v1, v2, v4)
    mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis)

    # triangle edges
    v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid
    v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid
    v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid
    v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid

    r = geometry.intersect_line_line(v1_, v2_, v3_, v4_)
    if r:
        p1, _ = r
        # cp = transform_matrix * (p1 + origin)
        cp = transform_matrix * p1
        bpy.context.scene.cursor_location = cp
        # generate_gp3d_stroke(cp, axis, obj, radius=(p1-v1).length)
    else:
        print('not on a circle')
Beispiel #8
0
def createMirror(angle):
    mirror[0].xyz = [0,-1,-1]
    mirror[1].xyz = [-1,1,1]
    mirror[2].xyz = [1,1,1]
    mirror[0].rotate( Matrix.Rotation( angle, 4, norm_mirror) )
    mirror[1].rotate( Matrix.Rotation( angle, 4, norm_mirror)  )
    mirror[2].rotate( Matrix.Rotation( angle, 4, norm_mirror)  )
    n = geometry.normal(mirror[2],mirror[1],mirror[0])
    return [mirror[2],mirror[1],mirror[0],n]
Beispiel #9
0
def getPolyNormal(poly):
	""" Returns the normal of poligon based on the position of their vertex. It calculates the normal, it doesn't return manually modified normals.

	:param poly: The poligon.
	:type poly: |KX_PolyProxy|
	"""

	mesh = poly.getMesh()
	s = poly.getNumVertex()
	v1 = mesh.getVertex(0, poly.v1)
	v2 = mesh.getVertex(0, poly.v2)
	v3 = mesh.getVertex(0, poly.v3)
	if s == 4: v4v = mesh.getVertex(0, poly.v4).XYZ
	else: v4v = None

	if v4v: normal = geometry.normal(v1.XYZ, v2.XYZ, v3.XYZ, v4v)
	else: normal = geometry.normal(v1.XYZ, v2.XYZ, v3.XYZ)
	return normal
Beispiel #10
0
def flatFace(verts):
    #print("    Flatface: original vertices %s %s %s"%(verts[0],verts[1],verts[2]))
    quatr = mg.normal(verts).rotation_difference(Vector((0, 0, 1)))
    eul = quatr.to_euler()
    eul.z = 0.0
    #print("    Flatface called Euler is x=%2.2g deg y=%2.2g deg"%(degrees(eul.x),degrees(eul.y)))
    for v in verts:
        v.rotate(eul)
        v.z = 0
Beispiel #11
0
    def execute(self, context):
        obj = context.edit_object
        me = obj.data
        bm = bmesh.from_edit_mesh(me)
        bm.select_history.validate()
        vertCount = 0
        selectedVerts = []
        for vert in (vert for vert in bm.verts if vert.select):  #count loop verts
            vertCount+=1
            if len(selectedVerts)<3:
                selectedVerts.append(vert)

        if vertCount < 1:
            self.report({'INFO'}, 'Select at least one vertex')
            return {'CANCELLED'}


        vertsMedianLoc = mathutils.Vector((0.0 ,0.0 ,0.0))
        vertsMedianNorm = mathutils.Vector((0.0 ,0.0 ,0.0))
        vertsCoordList = []
        vectOffset = mathutils.Vector((0.0, 0.0, self.Depth))
        offsetMatrix = mathutils.Matrix.Translation(vectOffset)
        yawMatrix = mathutils.Matrix.Rotation(radians(self.TrimRoll), 4, 'X')
        pitchMatrix = mathutils.Matrix.Rotation(radians(self.TripPitch), 4, 'Y')

        for vert in selectedVerts:
            vertsMedianLoc+=vert.co
            vertsCoordList.append(vert.co)
            vertsMedianNorm+=vert.normal
        vertsMedianLoc /= len(selectedVerts)
        if vertCount>2:
            trisNorm=normal(vertsCoordList)
            if degrees(vertsMedianNorm.angle(trisNorm))>90:
                vertsMedianNorm= trisNorm*(-1)
            else:
                vertsMedianNorm = trisNorm
        if vertCount==2:  #fixes normal for 2 verts
            vectorV1_V2=selectedVerts[0].co-selectedVerts[1].co
            perpendicular= vectorV1_V2.cross(vertsMedianNorm)
            vertsMedianNorm = perpendicular.cross(vectorV1_V2)


        normToRotation = vertsMedianNorm.to_track_quat('Z', 'X').to_euler()
        selectionLocalMatrix = mathutils.Matrix.Translation(vertsMedianLoc)
        selectionLocalMatrix *= selectionLocalMatrix.Rotation(normToRotation[2], 4, 'Z')
        selectionLocalMatrix *= selectionLocalMatrix.Rotation(normToRotation[1], 4, 'Y')
        selectionLocalMatrix *= selectionLocalMatrix.Rotation(normToRotation[0], 4, 'X')

        spaceMatrixTransform = context.object.matrix_world*selectionLocalMatrix*offsetMatrix*yawMatrix*pitchMatrix

        loc = spaceMatrixTransform.to_translation()
        norm = spaceMatrixTransform.to_3x3()*mathutils.Vector((0.0, 0.0, 1.0))
        bpy.ops.mesh.select_all(action='SELECT')
        bpy.ops.mesh.bisect(plane_co=loc, plane_no=norm, use_fill=True, clear_inner= not self.clearOut, clear_outer=self.clearOut)
        bpy.ops.mesh.select_all(action='DESELECT')
        bmesh.update_edit_mesh(me, True)
        return {"FINISHED"}
Beispiel #12
0
def _ascii_write(filepath, faces):
    with open(filepath, 'w') as data:
        fw = data.write
        header = _header_version()
        fw('solid %s\n' % header)

        for face in faces:
            # calculate face normal
            fw('facet normal %f %f %f\nouter loop\n' % normal(*face)[:])
            for vert in face:
                fw('vertex %f %f %f\n' % vert[:])
            fw('endloop\nendfacet\n')

        fw('endsolid %s\n' % header)
def build_mirror_pivot(f,verts,ob):
  v0 = mathutils.Vector(verts[f[0]])
  v1 = mathutils.Vector(verts[f[1]])
  v2 = mathutils.Vector(verts[f[2]])
  if len(f) == 4:
    v3 = mathutils.Vector(verts[f[3]])
    norm = normal(v0,v1,v2,v3)
  else:
    v3 = None
    norm = normal(v0,v1,v2)
  rot = norm.to_track_quat('X','Z')
  r = rot.to_euler('XYZ')
  p0 = v0.lerp(v1,0.5)
  if v3 == None: p1 = v2
  else: p1 = v2.lerp(v3,0.5)
  center = p0.lerp(p1,0.5)

  dummy = bpy.data.objects.new(ob.name + "_mirror_pivot",None)
  dummy.location = center.to_tuple()
  print("ROTATING TO ",r.x," ",r.y," ",r.z)
  dummy.rotation_euler = (r.x,r.y,r.z)
  bpy.context.scene.objects.link(dummy)
  return dummy
Beispiel #14
0
def generate_3PT_mode_1(pts=None, num_verts=20, make_edges=False):
    '''
    Arc from start - throught - Eend
    - call this function only if you have 3 pts,
    - do your error checking before passing to it.
    '''
    num_verts -= 1
    verts, edges = [], []
    V = Vector

    # construction
    v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2])
    edge1_mid = v1.lerp(v2, 0.5)
    edge2_mid = v3.lerp(v4, 0.5)
    axis = geometry.normal(v1, v2, v4)
    mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis)

    # triangle edges
    v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid
    v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid
    v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid
    v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid

    r = geometry.intersect_line_line(v1_, v2_, v3_, v4_)
    if r:
        # do arc
        p1, _ = r

        # find arc angle.
        a = (v1 - p1).angle((v4 - p1), 0)
        s = (2 * math.pi) - a

        interior_angle = (v1 - v2).angle(v4 - v3, 0)
        if interior_angle > 0.5 * math.pi:
            s = math.pi + 2 * (0.5 * math.pi - interior_angle)

        for i in range(num_verts + 1):
            mat_rot = mathutils.Matrix.Rotation(((s / num_verts) * i), 4, axis)
            vec = ((v4 - p1) * mat_rot) + p1
            verts.append(vec[:])
    else:
        # do straight line
        step_size = 1 / num_verts
        verts = [v1_.lerp(v4_, i * step_size)[:] for i in range(num_verts + 1)]

    if make_edges:
        edges = [(n, n + 1) for n in range(len(verts) - 1)]

    return verts, edges
def compute_distances_mu(plane, pts, result, gates, tolerance):
    plane_origin = V(plane[0])
    plane_a, plane_b = V(plane[1]), V(plane[2])
    norm = normal([plane_origin, plane_a, plane_b])
    if norm.length == 0:
        print("Error: the three points of the plane are aligned. Not valid plane")
    local_result = [[] for res in result]
    for p in pts:
        data = compute_point_tri_dist(V(p), plane_origin, plane_a, plane_b, norm, tolerance)
        for i, r in enumerate(local_result):
            r.append(data[i])

    for i, res in enumerate(result):
        if gates[i]:
            res.append(local_result[i])
Beispiel #16
0
def generate_3PT_mode_1(pts, obj, nv):
    origin = obj.location
    mw = obj.matrix_world
    V = Vector

    nv = max(3, nv)

    # construction
    v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2])
    edge1_mid = v1.lerp(v2, 0.5)
    edge2_mid = v3.lerp(v4, 0.5)
    axis = geometry.normal(v1, v2, v4)
    mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis)

    # triangle edges
    v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid
    v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid
    v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid
    v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid

    r = geometry.intersect_line_line(v1_, v2_, v3_, v4_)
    if r:
        p1, _ = r
        cp = mw * p1
        bpy.context.scene.cursor_location = cp
        layer = get_layer()
        generate_gp3d_stroke(layer, p1, v1, axis, mw, origin, nv)

        ''' 
        # f = [i for i in dir(bpy.context) if 'gpencil' in i]
        active_gpencil_frame
        active_gpencil_layer
        editable_gpencil_layers
        editable_gpencil_strokes
        gpencil_data
        gpencil_data_owner
        visible_gpencil_layers
        '''
        
        #bpy.context.active_gpencil_layer = layer
        #print(bpy.context.gpencil_data)
        scn = bpy.context.scene
        scn.grease_pencil = bpy.data.grease_pencil['tc_circle_000']

    else:
        print('not on a circle')
Beispiel #17
0
def generate_3PT(pts, obj, nv, mode=0):
    origin = obj.location
    mw = obj.matrix_world
    V = Vector

    nv = max(3, nv)

    # construction
    v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2])
    edge1_mid = v1.lerp(v2, 0.5)
    edge2_mid = v3.lerp(v4, 0.5)
    axis = geometry.normal(v1, v2, v4)
    mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis)

    # triangle edges
    v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid
    v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid
    v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid
    v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid

    r = geometry.intersect_line_line(v1_, v2_, v3_, v4_)
    if r:
        p1, _ = r
        cp = mw * p1
        bpy.context.scene.cursor_location = cp

        if mode == 0:
            layer = get_layer()
            generate_gp3d_stroke(layer, p1, v1, axis, mw, origin, nv)

            scn = bpy.context.scene
            scn.grease_pencil = bpy.data.grease_pencil['tc_circle_000']

        elif mode == 1:
            generate_bmesh_repr(p1, v1, axis, nv)

    else:
        print('not on a circle')
    def process(self):
        if self.outputs['Centers'].is_linked or self.outputs['Normals'].is_linked or \
                self.outputs['Origins'].is_linked or self.outputs['Norm_abs'].is_linked:
            if 'Polygons' in self.inputs and 'Vertices' in self.inputs \
                and self.inputs['Polygons'].is_linked and self.inputs['Vertices'].is_linked:

                pols_ = SvGetSocketAnyType(self, self.inputs['Polygons'])
                vers_tupls = SvGetSocketAnyType(self, self.inputs['Vertices'])
                vers_vects = Vector_generate(vers_tupls)
                
                # make mesh temp утилитарно - удалить в конце
                mat_collect = []
                normals_out = []
                origins = []
                norm_abs_out = []
                for verst, versv, pols in zip(vers_tupls, vers_vects, pols_):
                    normals = []
                    centrs = []
                    norm_abs = []
                    p0_xdirs = []
                    for p in pols:
                        v0 = versv[p[0]]
                        v1 = versv[p[1]]
                        v2 = versv[p[2]]
                        # save direction of 1st point in polygon
                        p0_xdirs.append(v0)
                        # normals
                        norm = geometry.normal(v0, v1, v2)
                        normals.append(norm)                       
                        # centrs
                        x,y,z = zip(*[verst[poi] for poi in p])
                        x,y,z = sum(x)/len(x), sum(y)/len(y), sum(z)/len(z)
                        current_center = Vector((x,y,z))
                        centrs.append(current_center)
                        # normal absolute !!!
                        # это совершенно нормально!!! ;-)
                        norm_abs.append(current_center+norm)
                        
                        if self.Separate:
                            norm_abs_out.append(norm_abs)    
                            origins.append(centrs)
                            normals_out.append(normals)
                        else:
                            norm_abs_out.extend(norm_abs)    
                            origins.extend(centrs)
                            normals_out.extend(normals)
                    mat_collect_ = []

                    for cen, nor, p0 in zip(centrs, normals, p0_xdirs):
                        zdir = nor
                        xdir = (Vector(p0) - cen).normalized()
                        ydir = zdir.cross(xdir)
                        lM = [(xdir[0], ydir[0], zdir[0], cen[0]),
                              (xdir[1], ydir[1], zdir[1], cen[1]),
                              (xdir[2], ydir[2], zdir[2], cen[2]),
                              (0.0, 0.0, 0.0, 1.0)]
                        mat_collect_.append(lM)
                    mat_collect.extend(mat_collect_)

                if not self.Separate:
                    SvSetSocketAnyType(self, 'Centers', mat_collect)
                    SvSetSocketAnyType(self, 'Norm_abs', Vector_degenerate([norm_abs_out]))
                    SvSetSocketAnyType(self, 'Origins', Vector_degenerate([origins]))
                    SvSetSocketAnyType(self, 'Normals', Vector_degenerate([normals_out]))
                else:
                    SvSetSocketAnyType(self, 'Centers', mat_collect)
                    SvSetSocketAnyType(self, 'Norm_abs', Vector_degenerate(norm_abs_out))
                    SvSetSocketAnyType(self, 'Origins', Vector_degenerate(origins))
                    SvSetSocketAnyType(self, 'Normals', Vector_degenerate(normals_out))
Beispiel #19
0
 def calc_normal(self):
     """:rtype: Vector"""
     return geom.normal(*self.coords)
    def process(self):
        verts_socket, poly_socket = self.inputs
        norm_socket, norm_abs_socket, origins_socket, centers_socket = self.outputs

        if not any([s.is_linked for s in self.outputs]):
            return

        if not (verts_socket.is_linked and poly_socket.is_linked):
            return

        pols_ = poly_socket.sv_get()
        vers_tupls = verts_socket.sv_get()
        vers_vects = Vector_generate(vers_tupls)

        # make mesh temp утилитарно - удалить в конце
        mat_collect = []
        normals_out = []
        origins = []
        norm_abs_out = []
        for verst, versv, pols in zip(vers_tupls, vers_vects, pols_):
            normals = []
            centrs = []
            norm_abs = []
            p0_xdirs = []
            for p in pols:
                v0 = versv[p[0]]
                v1 = versv[p[1]]
                v2 = versv[p[2]]
                # save direction of 1st point in polygon
                p0_xdirs.append(v0)
                # normals
                norm = geometry.normal(v0, v1, v2)
                normals.append(norm)
                # centrs
                x,y,z = zip(*[verst[poi] for poi in p])
                x,y,z = sum(x)/len(x), sum(y)/len(y), sum(z)/len(z)
                current_center = Vector((x,y,z))
                centrs.append(current_center)
                # normal absolute !!!
                # это совершенно нормально!!! ;-)
                norm_abs.append(current_center+norm)

            if self.Separate:
                norm_abs_out.append(norm_abs)
                origins.append(centrs)
                normals_out.append(normals)
            else:
                norm_abs_out.extend(norm_abs)
                origins.extend(centrs)
                normals_out.extend(normals)
                
            mat_collect_ = []

            for cen, nor, p0 in zip(centrs, normals, p0_xdirs):
                zdir = nor
                xdir = (Vector(p0) - cen).normalized()
                ydir = zdir.cross(xdir)
                lM = [(xdir[0], ydir[0], zdir[0], cen[0]),
                      (xdir[1], ydir[1], zdir[1], cen[1]),
                      (xdir[2], ydir[2], zdir[2], cen[2]),
                      (0.0, 0.0, 0.0, 1.0)]
                mat_collect_.append(Matrix(lM))
            mat_collect.extend(mat_collect_)

        if not self.Separate:
            norm_abs_out = [norm_abs_out]
            origins = [origins]
            normals_out = [normals_out]

        centers_socket.sv_set(mat_collect)
        norm_abs_socket.sv_set(Vector_degenerate(norm_abs_out))
        origins_socket.sv_set(Vector_degenerate(origins))
        norm_socket.sv_set(Vector_degenerate(normals_out))
Beispiel #21
0
 def normal(self):
     return geometry.normal(*[x._position for x in self._vertices])
Beispiel #22
0
def offset_edges(verts_in, edges_in, shift_in):
    # take an input mesh (verts + edges ) and an offset property and generate the resulting geometry

    verts_out = []
    faces_out = []
    
    verts_z = verts_in[:]
    verts_in = [Vector(v).to_2d() for v in verts_in]

    diff_shift = len(verts_in) - len(shift_in)
    if diff_shift >= 0:
        shift_in.extend([shift_in[-1] for _ in range(diff_shift)])
    else:
        shift_in = shift_in[:diff_shift]
    
    #Searching neighbours for each point
    neighbours = [[] for _ in verts_in]
    for edg in edges_in:
        neighbours[edg[0]].append(edg)
        neighbours[edg[1]].append(edg)

    #Sorting neighbours by hour hand
    for i, p_neighbs in enumerate(neighbours):
        if len(p_neighbs) != 1:
            angles = [0]
            first_item = list(set(p_neighbs[0]) - set([i]))[0]
            for another_neighb in p_neighbs[1:]:            
                second_item = list(set(another_neighb) - set([i]))[0]
                vector1 = verts_in[first_item] - verts_in[i]
                vector2 = verts_in[second_item] - verts_in[i]
                angles.append(calc_angle(vector1, vector2))
            sorted_ = list(zip(angles, p_neighbs))
            sorted_.sort()
            neighbours[i] = [e[1] for e in sorted_]
            
    def get_end_points(item_point, shift):
        second_item = list(set(neighbours[item_point][0]) - set([item_point]))[0]
        vert_edg = verts_in[item_point] - verts_in[second_item]
        mat_r1 = Matrix.Rotation(radians(-45),2,'X')
        mat_r2 = Matrix.Rotation(radians(45),2,'X')
        shift_end_points = shift/sin(radians(45))
        vert_new1 = (vert_edg * mat_r1).normalized() * shift_end_points + verts_in[item_point]
        vert_new2 = (vert_edg * mat_r2).normalized() * shift_end_points + verts_in[item_point]
        return [vert_new1,vert_new2]

    def get_middle_points(item_point, shift):
        points = []
        for i in range(len(neighbours[item_point])):
            current_edges = (neighbours[item_point][i:] + neighbours[item_point][:i])[:2]
            second_item1 = list(set(current_edges[0]) - set([item_point]))[0]
            second_item2 = list(set(current_edges[1]) - set([item_point]))[0]
            vert_edg1 = verts_in[second_item1] - verts_in[item_point]
            vert_edg2 = verts_in[second_item2] - verts_in[item_point]
            angle = calc_angle(vert_edg1, vert_edg2) / 2
            mat_r = Matrix.Rotation(angle, 2, 'X')        
            points.append((vert_edg1 * mat_r).normalized() * shift/sin(angle) + verts_in[item_point])
        return points

    #Seting points
    findex_new_points = [0]
    for i,sh in enumerate(shift_in):
        #avoid zero offset
        if not sh:
            sh = 0.001
        
        if len(neighbours[i]) == 1:
            verts_out.extend(get_end_points(i,sh))
            findex_new_points.append(findex_new_points[-1] + 2)
        else:
            p = get_middle_points(i,sh)
            verts_out.extend(p)
            findex_new_points.append(findex_new_points[-1] + len(p))
        
    # Preparing Z coordinate
    z_co = []
    for c,(i1,i2) in enumerate(zip(findex_new_points[:-1], findex_new_points[1:])):
        z_co.extend([verts_z[c][2] for _ in range(i2-i1)])
        
    #Creating faces and mark outer edges and central points
    outer_edges = []
    current_index = len(verts_out) - 1
    vers_mask = [0 for _ in verts_out]
    nomber_outer_points = current_index
    position_old_points = [0 for _ in verts_out]
    for edg in edges_in:
        need_points = []
        for i in edg:
            if len(neighbours[i]) <= 2:
                need_points.extend([findex_new_points[i], findex_new_points[i] + 1])
            else:
                position = neighbours[i].index(edg)
                nomber_points = len(neighbours[i])
                variants_positions = list(range(findex_new_points[i], findex_new_points[i] + nomber_points))
                need_points.extend((variants_positions[position - 1:] + variants_positions[:position - 1])[:2])
    
        vec_edg = verts_in[edg[0]] - verts_in[edg[1]]
        vec_1 = verts_out[need_points[0]] - verts_in[edg[0]]
        vec_2 = verts_out[need_points[1]] - verts_in[edg[0]]
        vec_3 = verts_out[need_points[2]] - verts_in[edg[1]]
        vec_4 = verts_out[need_points[3]] - verts_in[edg[1]]
        new_vecs = [vec_1, vec_2, vec_3, vec_4]

        angles = [vec_edg.angle_signed(vec) for vec in new_vecs]

        if position_old_points[edg[0]] == 0:
            verts_out.append(verts_in[edg[0]])
            z_co.append(verts_z[edg[0]][2]) 
            vers_mask.append(1)
            current_index += 1
            position_old_points[edg[0]] = current_index
    
        if position_old_points[edg[1]] == 0:
            verts_out.append(verts_in[edg[1]])
            z_co.append(verts_z[edg[1]][2])
            vers_mask.append(1)
            current_index += 1
            position_old_points[edg[1]] = current_index
            
        n_p = need_points
        if angles[0] < 0 and angles[2] < 0 or angles[0] >= 0 and angles[2] >= 0:
            new_edges = [[n_p[1], position_old_points[edg[0]], position_old_points[edg[1]], n_p[3]],
                         [n_p[0], position_old_points[edg[0]], position_old_points[edg[1]], n_p[2]]]
                             
            outer_edges.extend([[n_p[1],n_p[3]],[n_p[0],n_p[2]]])
        else:
            new_edges = [[n_p[0], position_old_points[edg[0]], position_old_points[edg[1]], n_p[3]],
                         [n_p[1], position_old_points[edg[0]], position_old_points[edg[1]], n_p[2]]]
                         
            outer_edges.extend([[n_p[0],n_p[3]],[n_p[1],n_p[2]]])

        if len(neighbours[edg[0]]) == 1:
            new_edges.append([need_points[1], position_old_points[edg[0]], need_points[0]])
            outer_edges.append([need_points[1],need_points[0]])
        if len(neighbours[edg[1]]) == 1:
            new_edges.append([need_points[2], position_old_points[edg[1]], need_points[3]])
            outer_edges.append([need_points[2],need_points[3]])
    
        for c,face in enumerate(new_edges):
            if normal(*[verts_out[i].to_3d() for i in face])[2] < 0:
                new_edges[c] = new_edges[c][::-1]
        
        faces_out.extend(new_edges)

    verts_out = [(v.x, v.y, z) for v, z in zip(verts_out, z_co)]
    return verts_out, faces_out, outer_edges, vers_mask
Beispiel #23
0
 def calc_normal(self):
     return geom.normal(*self.coords)
    def process(self):
        if self.outputs['Centers'].is_linked or self.outputs['Normals'].is_linked or \
                self.outputs['Origins'].is_linked or self.outputs['Norm_abs'].is_linked:
            if 'Polygons' in self.inputs and 'Vertices' in self.inputs \
                and self.inputs['Polygons'].is_linked and self.inputs['Vertices'].is_linked:

                pols_ = SvGetSocketAnyType(self, self.inputs['Polygons'])
                vers_tupls = SvGetSocketAnyType(self, self.inputs['Vertices'])
                vers_vects = Vector_generate(vers_tupls)
                
                # make mesh temp утилитарно - удалить в конце
                mat_collect = []
                normals_out = []
                origins = []
                norm_abs_out = []
                for verst, versv, pols in zip(vers_tupls, vers_vects, pols_):
                    # medians в векторах
                    medians = []
                    normals = []
                    centrs = []
                    norm_abs = []
                    for p in pols:
                        # medians
                        # it calcs middle point of opposite edges, 
                        # than finds length vector between this two points
                        v0 = versv[p[0]]
                        v1 = versv[p[1]]
                        v2 = versv[p[2]]
                        lp=len(p)
                        if lp >= 4:
                            l = ((lp-2)//2) + 2
                            v3 = versv[p[l]]
                            poi_2 = (v2+v3)/2
                            # normals
                            norm = geometry.normal(v0, v1, v2, v3)
                            normals.append(norm)
                        else:
                            poi_2 = v2
                            # normals
                            norm = geometry.normal(v0, v1, v2)
                            normals.append(norm)
                        poi_1 = (v0+v1)/2
                        vm = poi_2 - poi_1
                        medians.append(vm)
                        # centrs
                        x,y,z = zip(*[verst[poi] for poi in p])
                        x,y,z = sum(x)/len(x), sum(y)/len(y), sum(z)/len(z)
                        current_center = Vector((x,y,z))
                        centrs.append(current_center)
                        # normal absolute !!!
                        # это совершенно нормально!!! ;-)
                        norm_abs.append(current_center+norm)
                        
                    norm_abs_out.append(norm_abs)    
                    origins.append(centrs)
                    normals_out.extend(normals)
                    mat_collect_ = []
                    for cen, med, nor in zip(centrs, medians, normals):
                        loc = Matrix.Translation(cen)
                        # need better solution for Z,Y vectors + may be X vector correction
                        vecz = Vector((0, 1e-6, 1))
                        q_rot0 = vecz.rotation_difference(nor).to_matrix().to_4x4()
                        q_rot2 = nor.rotation_difference(vecz).to_matrix().to_4x4()
                        vecy = Vector((1e-6, 1, 0)) * q_rot2
                        q_rot1 = vecy.rotation_difference(med).to_matrix().to_4x4()
                        # loc is matrix * rot vector * rot vector
                        M = loc*q_rot1*q_rot0
                        lM = [ j[:] for j in M ]
                        mat_collect_.append(lM)
                    mat_collect.extend(mat_collect_)
                
                SvSetSocketAnyType(self, 'Centers', mat_collect)
                SvSetSocketAnyType(self, 'Norm_abs', Vector_degenerate(norm_abs_out))
                SvSetSocketAnyType(self, 'Origins', Vector_degenerate(origins))
                SvSetSocketAnyType(self, 'Normals', Vector_degenerate([normals_out]))
Beispiel #25
0
 def execute(self, context):
     nor = normal(p.co for p in self.points[:3])
     co = Vector(self.points[-1].co) + nor * self.offset
     bpy.ops.mesh.bisect(plane_co=co, plane_no=nor, use_fill=self.use_fill, clear_inner=self.clear_inner, clear_outer=self.clear_outer)
     return {'FINISHED'}
Beispiel #26
0
def f_(me, list_0, dict_0, opp, list_fl, in_):

    if len(list_fl) == 0:      # loop
        loop = True
        path = False
        frst = list_0[0][0]
        list_1 = f_2(frst, list_0)
        del list_1[-1]

    else:      # path
        loop = False
        path = True
        frst = list_fl[0]
        last = list_fl[1]
        list_1 = f_1(frst, list_0, last)

    n = len(list_1)
    for ii in range(n):
        p_ = (me.vertices[list_1[ii]].co).copy()
        p1_ = (me.vertices[list_1[(ii - 1) % n]].co).copy()
        p2_ = (me.vertices[list_1[(ii + 1) % n]].co).copy()
        vec1_ = p_ - p1_
        vec2_ = p_ - p2_
        ang = vec1_.angle(vec2_, any)
        an = round(degrees(ang))

        if ang == 0 or ang == 180:
            continue
        elif ang != 0 or ang != 180:
            vec_no = normal(p_, p1_, p2_)
            break
        
    list_2 = []
    list_3 = []

    for i in range(n):
        p = (me.vertices[list_1[i]].co).copy()
        p1 = (me.vertices[list_1[(i - 1) % n]].co).copy()
        p2 = (me.vertices[list_1[(i + 1) % n]].co).copy()

        me.vertices[list_1[i]].select = False

        if in_ == True:
            p3 = p - (vec_no * opp)
        else:
            p3 = p + (vec_no * opp)

        me.vertices.add(1)
        me.vertices[-1].co = p3

        list_2.append(list_1[i])
        list_3.append(me.vertices[-1].index)

    n1 = len(list_2)
    if path == True:
        for j in range(n1 - 1):
            me.faces.add(1)
            me.faces[-1].vertices_raw = [ list_2[j], list_2[(j + 1) % n1], list_3[(j + 1) % n1], list_3[j] ]
    elif loop == True:
        for j in range(n1):
            me.faces.add(1)
            me.faces[-1].vertices_raw = [ list_2[j], list_2[(j + 1) % n1], list_3[(j + 1) % n1], list_3[j] ]

    # -- -- -- --
    list_4 = []
    n2 = len(list_2)
    for k in range(n2):

        q = (me.vertices[list_2[k]].co).copy()
        q1 = (me.vertices[list_2[(k - 1) % n2]].co).copy()
        q2 = (me.vertices[list_2[(k + 1) % n2]].co).copy()

        vec1 = q - q1
        vec2 = q - q2

        q3 = q - (vec1.normalized() * 0.1)
        q4 = q - (vec2.normalized() * 0.1)

        ang = vec1.angle(vec2, any)
        
        if path:
            if k == 0:
                axis = vec2
            elif k == n2 - 1:
                axis = -vec1
            else:
                axis = q3 - q4
            
            mtrx = Matrix.Rotation((pi * 0.5), 3, axis)
            q5 = (me.vertices[list_3[k]].co).copy()
            tmp = q5 - q
            tmp1 = mtrx * tmp
            tmp2 = tmp1 + q

            if k == 0:
                list_4.append(tmp2)
            elif k == n2 - 1:
                list_4.append(tmp2)
            else:
                vec3 = tmp2 - q
                adj = opp / tan(ang / 2)
                h = (adj ** 2 + opp ** 2) ** 0.5
                q6 = q + (vec3.normalized() * h)
                list_4.append(q6)

        elif loop:

            axis = q3 - q4
            ang = vec1.angle(vec2, any)

            mtrx = Matrix.Rotation((pi * 0.5), 3, axis)
            q5 = (me.vertices[list_3[k]].co).copy()
            tmp = q5 - q
            tmp1 = mtrx * tmp
            tmp2 = tmp1 + q

            vec3 = tmp2 - q
        
            # -- -- -- --
            d_ = tan(ang / 2)
            if d_ == 0:
                pass
            elif d_ != 0:
                adj = opp / tan(ang / 2)
                h = (adj ** 2 + opp ** 2) ** 0.5
                q6 = q + (vec3.normalized() * h)
                me.vertices[list_3[k]].co = q6
            # -- -- -- --

    if len(list_4) == 0:
        pass
    else:
        for kk in range(n2):
            me.vertices[list_3[kk]].co = list_4[kk]

    me.update(calc_edges = True)
Beispiel #27
0
def convex_hull_3d(vecs, eps:'距離がこれ以下なら同一平面と見做す'=1e-6):
    """三次元又は二次元の凸包を求める"""
    if len(vecs) <= 1:
        return list(range(len(vecs)))

    verts = [_Vert(i, v) for i, v in enumerate(vecs)]

    # なるべく離れている二頂点を求める
    medium = reduce(lambda a, b: a + b, vecs) / len(vecs)
    v1 = max(verts, key=lambda v: (v.co - medium).length)
    v2 = max(verts, key=lambda v: (v.co - v1.co).length)
    line = v2.co - v1.co
    if line.length <= eps:
        # 全ての頂点が重なる
        return [0]

    verts.remove(v1)
    verts.remove(v2)
    if not verts:
        return [v1.index, v2.index]

    # 三角形を構成する為の頂点を求める
    v3 = max(verts, key=lambda v: line.cross(v.co - v1.co).length)
    if line.normalized().cross(v3.co - v1.co).length <= eps:
        # 全ての頂点が同一線上にある
        return [v1.index, v2.index]
    
    verts.remove(v3)
    if not verts:
        return [v1.index, v2.index, v3.index]

    # 四面体を構成する為の頂点を求める
    normal = geom.normal(v1.co, v2.co, v3.co)
    def key_func(v):
        return abs(geom.distance_point_to_plane(v.co, v1.co, normal))
    v4 = max(verts, key=key_func)
    if key_func(v4) <= eps:
        # 全ての頂点が平面上にある
        quat = normal.rotation_difference(Vector((0, 0, 1)))
        vecs_2d = [(quat * v).to_2d() for v in vecs]
        return convex_hull_2d(vecs_2d, eps)

    verts.remove(v4)

    # 四面体作成
    #       ^ normal
    #    v3 |
    #     / |\
    # v1 /____\v2
    #    \    /
    #     \  /
    #     v4

    if geom.distance_point_to_plane(v4.co, v1.co, normal) < 0.0:
        faces = [_Face(v1, v2, v3),
                 _Face(v1, v4, v2), _Face(v2, v4, v3), _Face(v3, v4, v1)]
    else:
        faces = [_Face(v1, v3, v2),
                 _Face(v1, v2, v4), _Face(v2, v3, v4), _Face(v3, v1, v4)]

    # 残りの頂点を各面に分配
    _divide_outer_verts(faces, verts, eps)

    # edge_faces作成
    edge_faces = defaultdict(list)
    for face in faces:
        for ekey in face.edge_keys:
            edge_faces[ekey].append(face)

    while True:
        added = False
        for i in range(len(faces)):
            try:
                face = faces[i]
            except:
                break
            if not face.outer_verts:
                continue

            v1 = max(face.outer_verts, key=lambda v: face.distance(v.co))
            if face.distance(v1.co) > eps:
                # 凸包になるようにv1から放射状に面を貼る
                added = True

                # 隠れて不要となる面を求める
                remove_faces = set()
                _find_remove_faces_re(remove_faces, v1.co, face, edge_faces,
                                      eps)

                # remove_facesを多面体から除去して穴を開ける
                for f in remove_faces:
                    for ekey in f.edge_keys:
                        edge_faces[ekey].remove(f)
                    faces.remove(f)

                # 穴に面を貼る
                new_faces = []
                ekey_count = defaultdict(int)
                for f in remove_faces:
                    for ekey in f.edge_keys:
                        ekey_count[ekey] += 1
                for ekey, cnt in ekey_count.items():
                    if cnt != 1:
                        continue
                    linkface = edge_faces[ekey][0]
                    v2, v3 = ekey
                    if linkface.verts[linkface.verts.index(v2) - 1] != v3:
                        v2, v3 = v3, v2
                    new_face = _Face(v1, v2, v3)
                    for key in new_face.edge_keys:
                        edge_faces[key].append(new_face)
                    new_faces.append(new_face)
                faces.extend(new_faces)

                # 頂点の再分配
                outer_verts = reduce(lambda a, b: a + b,
                                     (f.outer_verts for f in remove_faces))
                if v1 in outer_verts:
                    outer_verts.remove(v1)
                _divide_outer_verts(new_faces, outer_verts, eps)

            else:
                face.outer_verts = []

        if not added:
            break

    return [[v.index for v in f.verts] for f in faces]
Beispiel #28
0
    def process(self):
        verts_socket, poly_socket = self.inputs
        norm_socket, norm_abs_socket, origins_socket, centers_socket = self.outputs

        if not any([s.is_linked for s in self.outputs]):
            return

        if not (verts_socket.is_linked and poly_socket.is_linked):
            return

        pols_ = poly_socket.sv_get()
        vers_tupls = verts_socket.sv_get()
        vers_vects = Vector_generate(vers_tupls)
        
        # make mesh temp утилитарно - удалить в конце
        mat_collect = []
        normals_out = []
        origins = []
        norm_abs_out = []
        for verst, versv, pols in zip(vers_tupls, vers_vects, pols_):
            # medians в векторах
            medians = []
            normals = []
            centrs = []
            norm_abs = []
            for p in pols:
                # medians
                # it calcs middle point of opposite edges, 
                # than finds length vector between this two points
                v0 = versv[p[0]]
                v1 = versv[p[1]]
                v2 = versv[p[2]]
                lp=len(p)
                if lp >= 4:
                    l = ((lp-2)//2) + 2
                    v3 = versv[p[l]]
                    poi_2 = (v2+v3)/2
                    # normals
                    norm = geometry.normal(v0, v1, v2, v3)
                    normals.append(norm)
                else:
                    poi_2 = v2
                    # normals
                    norm = geometry.normal(v0, v1, v2)
                    normals.append(norm)
                poi_1 = (v0+v1)/2
                vm = poi_2 - poi_1
                medians.append(vm)
                # centrs
                x,y,z = zip(*[verst[poi] for poi in p])
                x,y,z = sum(x)/len(x), sum(y)/len(y), sum(z)/len(z)
                current_center = Vector((x,y,z))
                centrs.append(current_center)
                # normal absolute !!!
                # это совершенно нормально!!! ;-)
                norm_abs.append(current_center+norm)
                
            if self.Separate:
                norm_abs_out.append(norm_abs)    
                origins.append(centrs)
                normals_out.append(normals)
            else:
                norm_abs_out.extend(norm_abs)    
                origins.extend(centrs)
                normals_out.extend(normals)
            mat_collect_ = []
            for cen, med, nor in zip(centrs, medians, normals):
                loc = Matrix.Translation(cen)
                # need better solution for Z,Y vectors + may be X vector correction
                vecz = Vector((0, 1e-6, 1))
                q_rot0 = vecz.rotation_difference(nor).to_matrix().to_4x4()
                q_rot2 = nor.rotation_difference(vecz).to_matrix().to_4x4()
                if med[1]>med[0]:
                    vecy = Vector((1e-6, 1, 0)) * q_rot2
                else:
                    vecy = Vector((1, 1e-6, 0)) * q_rot2
                q_rot1 = vecy.rotation_difference(med).to_matrix().to_4x4()
                # loc is matrix * rot vector * rot vector
                M = loc*q_rot1*q_rot0
                lM = [ j[:] for j in M ]
                mat_collect_.append(lM)
            mat_collect.extend(mat_collect_)

        
        if not self.Separate:
            norm_abs_out = [norm_abs_out]
            origins = [origins]
            normals_out = [normals_out]

        centers_socket.sv_set(mat_collect)
        norm_abs_socket.sv_set(Vector_degenerate(norm_abs_out))
        origins_socket.sv_set(Vector_degenerate(origins))
        norm_socket.sv_set(Vector_degenerate(normals_out))
    def execute(self, context):
        if self.mode in ('selected', 'center', 'grid', 'active'):
            self.execute_builtin(context)
            return {'FINISHED'}
        
        actob = context.active_object
        mat = actob.matrix_world
        editmode = 'EDIT' in context.mode
        if editmode:
            bpy.ops.object.mode_set(mode='OBJECT')
        
        mesh = None
        if editmode and actob.type == 'MESH':
            mesh = actob.data
        
        loc = None
        if self.mode == 'circle':
            if mesh:
                vecs = (v.co for v in mesh.vertices if v.select and not v.hide)
                vecs = [mat * v for v in vecs]
            else:
                vecs = [Vector(ob.matrix_world.col[3][:3])
                        for ob in context.selected_objects]
            if len(vecs) == 3:
                center = vam.center_of_circumscribed_circle_tri(*vecs)
                if center:
                    loc = center
                    r = (vecs[0] - loc).length
                    self.report({'INFO'}, 'r: {}'.format(r))
            else:
                self.report({'WARNING'}, 'Select 3 vectors')

        elif self.mode == 'sphere':
            if mesh:
                vecs = (v.co for v in mesh.vertices if v.select and not v.hide)
                vecs = [mat * v for v in vecs]
                if len(vecs) < 4:
                    self.report({'WARNING'}, 'Select more than 4 vertices')
                elif len(vecs) > 16:
                    # 計算に時間がかかるのでパス。
                    txt = '{} vertices is selected. Too many'.format(len(vecs))
                    self.report({'WARNING'}, txt)
                else:
                    locations = []
                    normals = []
                    for v1, v2, v3 in combinations(vecs, 3):
                        center = vam.center_of_circumscribed_circle_tri(
                                         v1, v2, v3)
                        if center:
                            locations.append(center)
                            normals.append(geom.normal(v1, v2, v3))
                    inters = []
                    for i, j in combinations(range(len(locations)), 2):
                        cross_length = normals[i].cross(normals[j]).length
                        angle = math.asin(min(cross_length, 1.0))
                        if angle < ANGLE_THRESHOLD:
                            continue
                        v1 = locations[i]
                        v2 = v1 + normals[i]
                        v3 = locations[j]
                        v4 = v3 + normals[j]
                        inter = geom.intersect_line_line(v1, v2, v3, v4)
                        if inter:
                            inters.append((inter[0] + inter[1]) / 2)
                    if inters:
                        loc = (reduce(lambda v1, v2: v1 + v2, inters) /
                               len(inters))
                        r_sum = 0
                        for v in vecs:
                            r_sum += (v - loc).length
                        r = r_sum /len(vecs)
                        self.report({'INFO'}, 'r: {}'.format(r))
                
        elif self.mode == 'median':
            if mesh:
                dm = self.get_dm(context)
                vecs = [v.co for v in dm.vertices if v.select and not v.hide]
                if vecs:
                    vecs = [mat * v for v in vecs]
                    loc = (reduce(lambda v1, v2: v1 + v2, vecs) /
                                       len(vecs))
        
        elif self.mode == 'boundbox':
            if mesh:
                dm = self.get_dm(context)
                vecs = (v.co for v in dm.vertices if v.select and not v.hide)
                vecs = [mat * v for v in vecs]
                if vecs:
                    v = vecs[0]
                    mins = [v[0], v[1], v[2]]
                    maxs = [v[0], v[1], v[2]]
                    for v in vecs:
                        for i in range(3):
                            if v[i] < mins[i]:
                                mins[i] = v[i]
                            if v[i] > maxs[i]:
                                maxs[i] = v[i]
                    loc = Vector([(mins[i] + maxs[i]) / 2 for i in range(3)])
        
        if editmode:
            bpy.ops.object.mode_set(mode='EDIT')

        if loc:
            context.scene.cursor_location = loc

        return {'FINISHED'}
Beispiel #30
0
def OBB(vecs, r_indices=None, eps=1e-6):
    """Convex hull を用いたOBBを返す。
    Z->Y->Xの順で長さが最少となる軸を求める。
    :param vecs: list of Vector
    :type vecs: list | tuple
    :param r_indices: listを渡すとconvexhullの結果を格納する
    :type r_indices: None | list
    :param eps: 種々の計算の閾値
    :return:
        (matrix, obb_size)
        matrix:
            type: Matrx
            OBBの回転と中心を表す。vecsが二次元ベクトルの場合は3x3, 三次元なら4x4。
        obb_size:
            type: Vector
            OBBの各軸の長さ。vecsと同じ次元。
    :rtype: (Matrix, Vector)
    """

    if not vecs:
        return None, None

    # 2D ----------------------------------------------------------------------
    if len(vecs[0]) == 2:
        mat = Matrix.Identity(3)
        bb_size = Vector((0, 0))

        indices = convex_hull_2d(vecs, eps)
        if r_indices:
            r_indices[:] = indices

        if len(indices) == 1:
            mat.col[2][:2] = vecs[0]
        elif len(indices) == 2:
            v1 = vecs[indices[0]]
            v2 = vecs[indices[1]]
            xaxis = (v2 - v1).normalized()
            angle = math.atan2(xaxis[1], xaxis[0])
            mat2 = Matrix.Rotation(angle, 2)
            mat.col[0][:2] = mat2.col[0]
            mat.col[1][:2] = mat2.col[1]
            mat.col[2][:2] = (v1 + v2) / 2
            bb_size[0] = (v2 - v1).length
        else:
            yaxis = _closest_axis_on_plane(vecs, indices)
            angle = math.atan2(yaxis[1], yaxis[0]) - math.pi / 2  # X軸
            mat2 = Matrix.Rotation(angle, 2)
            imat2 = Matrix.Rotation(-angle, 2)
            rotvecs = [imat2 * v for v in vecs]
            loc = Vector((0, 0))
            for i in range(2):
                rotvecs.sort(key=lambda v: v[i])
                bb_size[i] = rotvecs[-1][i] - rotvecs[0][i]
                loc[i] = (rotvecs[0][i] + rotvecs[-1][i]) / 2
            mat.col[0][:2] = mat2.col[0]
            mat.col[1][:2] = mat2.col[1]
            mat.col[2][:2] = mat2 * loc
        return mat, bb_size

    # 3D ----------------------------------------------------------------------
    mat = Matrix.Identity(4)
    bb_size = Vector((0, 0, 0))

    indices = convex_hull(vecs, eps)

    if r_indices:
        r_indices[:] = indices

    if isinstance(indices[0], int):  # 2d
        if len(indices) == 1:
            mat.col[3][:3] = vecs[0]
            return mat, bb_size
        
        elif len(indices) == 2:
            # 同一線上
            v1 = vecs[indices[0]]
            v2 = vecs[indices[1]]
            xaxis = (v2 - v1).normalized()
            quat = Vector((1, 0, 0)).rotation_difference(xaxis)
            mat = quat.to_matrix().to_4x4()
            mat.col[3][:3] = (v1 + v2) / 2
            bb_size[0] = (v2 - v1).length
            return mat, bb_size

        else:
            # 同一平面上
            medium = reduce(lambda a, b: a + b, vecs) / len(vecs)
            v1 = max(vecs, key=lambda v: (v - medium).length)
            v2 = max(vecs, key=lambda v: (v - v1).length)
            line = v2 - v1
            v3 = max(vecs, key=lambda v: line.cross(v - v1).length)
            zaxis = geom.normal(v1, v2, v3)
            if zaxis[2] < 0.0:
                zaxis.negate()

            quat = zaxis.rotation_difference(Vector((0, 0, 1)))
            rotvecs = [quat * v for v in vecs]
            indices_2d = indices

    else:  # 3d
        indices_set = set(chain(*indices))
        zaxis = None
        dist = 0.0
        # 最も距離の近い面(平面)と頂点を求める
        for tri in indices:
            v1, v2, v3 = [vecs[i] for i in tri]
            normal = geom.normal(v1, v2, v3)
            d = 0.0
            for v4 in (vecs[i] for i in indices_set if i not in tri):
                f = abs(geom.distance_point_to_plane(v4, v1, normal))
                d = max(f, d)
            if zaxis is None or d < dist:
                zaxis = -normal
                dist = d

        quat = zaxis.rotation_difference(Vector((0, 0, 1)))
        rotvecs = [(quat * v).to_2d() for v in vecs]
        indices_2d = convex_hull_2d(rotvecs, eps)

    yaxis = _closest_axis_on_plane(rotvecs, indices_2d)
    yaxis = quat.inverted() * yaxis.to_3d()

    xaxis = yaxis.cross(zaxis)
    xaxis.normalize()  # 不要?

    mat.col[0][:3] = xaxis
    mat.col[1][:3] = yaxis
    mat.col[2][:3] = zaxis

    # OBBの大きさと中心を求める
    imat = mat.inverted()
    rotvecs = [imat * v for v in vecs]
    loc = Vector()
    for i in range(3):
        rotvecs.sort(key=lambda v: v[i])
        bb_size[i] = rotvecs[-1][i] - rotvecs[0][i]
        loc[i] = (rotvecs[0][i] + rotvecs[-1][i]) / 2
    mat.col[3][:3] = mat * loc
    return mat, bb_size