def calc_rotation(self, curve, src, dst, plane=None): # Some hacks. # Problem: if src and/or dst are exactly parallel to # one of coordinate axes, then Vector.rotation_difference # sometimes returns a matrix that rotates our vector in # completely different plane. # For example, if whole figure lays in XY plane, and # we are trying to rotate src = (0, 1, 0) into dst = (1, 0, 0), # then rotation_difference might return us a matrix, which # rotates (-1, 0, 0) into (0, 0, -1), which is out of XY plane # ("because why no? it still rotates src into dst"). # Solution (hack): if whole figure lays in XY plane, then do # not use general rotation_difference method, calculate # rotation along Z axis only. if plane == 'XY': # Another unobvious hack: Vector.angle_signed method # works with 2D vectors only (this is not stated in # it's documentation!). Fortunately, in this particular # case our vectors are actually 2D. dst = Vector((dst[0], dst[1])) src = Vector((src[0], src[1])) angle = dst.angle_signed(src) return Matrix.Rotation(angle, 4, 'Z') elif plane == 'YZ': dst = Vector((dst[1], dst[2])) src = Vector((src[1], src[2])) angle = dst.angle_signed(src) return Matrix.Rotation(angle, 4, 'X') elif plane == 'XZ': dst = Vector((dst[2], dst[0])) src = Vector((src[2], src[0])) angle = dst.angle_signed(src) return Matrix.Rotation(angle, 4, 'Y') else: return autorotate_diff(dst, src)
def calc_rotation(self, curve, src, dst, plane=None): # Some hacks. # Problem: if src and/or dst are exactly parallel to # one of coordinate axes, then Vector.rotation_difference # sometimes returns a matrix that rotates our vector in # completely different plane. # For example, if whole figure lays in XY plane, and # we are trying to rotate src = (0, 1, 0) into dst = (1, 0, 0), # then rotation_difference might return us a matrix, which # rotates (-1, 0, 0) into (0, 0, -1), which is out of XY plane # ("because why no? it still rotates src into dst"). # Solution (hack): if whole figure lays in XY plane, then do # not use general rotation_difference method, calculate # rotation along Z axis only. if plane == 'XY': # Another unobvious hack: Vector.angle_signed method # works with 2D vectors only. Fortunately, in this particular # case our vectors are actually 2D. dst = Vector((dst[0], dst[1])) src = Vector((src[0], src[1])) angle = dst.angle_signed(src) return Matrix.Rotation(angle, 4, 'Z') elif plane == 'YZ': dst = Vector((dst[1], dst[2])) src = Vector((src[1], src[2])) angle = dst.angle_signed(src) return Matrix.Rotation(angle, 4, 'X') elif plane == 'XZ': dst = Vector((dst[2], dst[0])) src = Vector((src[2], src[0])) angle = dst.angle_signed(src) return Matrix.Rotation(angle, 4, 'Y') else: return autorotate_diff(dst, src)
def rounded_primitive(cls, verts, radius, resolution=2.0): if not verts: return if len(verts) == 1: yield from cls.arc(verts[0], radius, resolution, skip_end=1) elif len(verts) == 2: v0, v1 = verts dv = v1 - v0 angle = Vector((0, 1)).angle_signed(Vector((-dv.y, dv.x)), 0.0) yield from cls.arc(v0, radius, resolution, angle - math.pi, angle) yield from cls.arc(v1, radius, resolution, angle, angle + math.pi) elif radius == 0: yield from verts # exactly the same else: vref = Vector((0, 1)) count = len(verts) for i0 in range(count): v0 = verts[i0] v1 = verts[(i0 + 1) % count] v2 = verts[(i0 + 2) % count] dv10 = v1 - v0 dv21 = v2 - v1 angle10 = vref.angle_signed(Vector((-dv10.y, dv10.x)), 0.0) angle21 = vref.angle_signed(Vector((-dv21.y, dv21.x)), 0.0) angle21 = angle10 + clamp_angle(angle21 - angle10) yield from cls.arc(v1, radius, resolution, angle10, angle21)
def svg_text_shader(item, text, mid, textCard, color,svg,parent=None): text_position = get_render_location(mid) svgColor = svgwrite.rgb(color[0]*255, color[1]*255, color[2]*255, '%') ssp1 = get_render_location(textCard[0]) ssp2 = get_render_location(textCard[3]) ssp3 = get_render_location(textCard[2]) dirVec = Vector(ssp1) - Vector(ssp2) if dirVec.length == 0: return text_position = (Vector(ssp1) + Vector(ssp3)) / 2 rotation = math.degrees(dirVec.angle_signed(Vector((1, 0)))) parent.add(svg.text(text, insert=tuple(text_position), fill=svgColor, **{ 'transform': 'rotate({} {} {})'.format( rotation, text_position[0], text_position[1] ), 'font-size': 12, 'font-family': 'OpenGost Type B TT', 'text-anchor': 'middle' }))
def autoscale(self): symmetry = nmv.physics.hex_symmetry_space if self.hex_mode else nmv.physics.symmetry_space for vert in self.bm.verts: u = Vector(self.field[vert.index]) v = u.cross(vert.normal) ang = 0 last_vec = u for loop in vert.link_loops: vert1 = loop.link_loop_next.vert vert2 = loop.link_loop_next.link_loop_next.vert if not last_vec: vert1_vec = Vector(self.field[vert1.index]) else: vert1_vec = last_vec vert2_vec = nmv.physics.best_matching_vector( symmetry(self.field[vert2.index], vert2.normal), vert1_vec) vert1_vec = Vector((vert1_vec.dot(u), vert1_vec.dot(v))) vert2_vec = Vector((vert2_vec.dot(u), vert2_vec.dot(v))) ang += vert1_vec.angle_signed(vert2_vec) self.scale[vert.index] = ang for i in range(20): self.scale += self.scale[self.walk_edges(0)] self.scale /= 2 self.scale -= self.scale.min() self.scale /= self.scale.max()
def execute(self,bgeCar): #vector between car and guide car_position = bgeCar.getPosition() guide_position = bgeCar.bgeGuide.getPosition() vec_to_guide = guide_position - car_position #car norm vector car_dir_vec = bgeCar.getVelocity() #in 2D vec_to_guide = Vector([vec_to_guide[0],vec_to_guide[1]]) car_dir_vec = Vector([car_dir_vec[0],car_dir_vec[1]]) #angle between the two angle = vec_to_guide.angle_signed(car_dir_vec,None) if angle==None: angleFormated = 0.0 else: angleFormated = angle * 180/math.pi angle_treshold = 2.0 if angleFormated > angle_treshold: bge.logic.sendMessage(bgeCar.carname + '_Left') elif angleFormated < -angle_treshold: bge.logic.sendMessage(bgeCar.carname + '_Right')
def rounded_primitive(cls, verts, radius, resolution=2.0): if not verts: return if len(verts) == 1: yield from cls.arc(verts[0], radius, resolution, skip_end=1) elif len(verts) == 2: v0, v1 = verts dv = v1 - v0 angle = Vector((0,1)).angle_signed(Vector((-dv.y, dv.x)), 0.0) yield from cls.arc(v0, radius, resolution, angle-math.pi, angle) yield from cls.arc(v1, radius, resolution, angle, angle+math.pi) elif radius == 0: yield from verts # exactly the same else: vref = Vector((0,1)) count = len(verts) for i0 in range(count): v0 = verts[i0] v1 = verts[(i0 + 1) % count] v2 = verts[(i0 + 2) % count] dv10 = v1 - v0 dv21 = v2 - v1 angle10 = vref.angle_signed(Vector((-dv10.y, dv10.x)), 0.0) angle21 = vref.angle_signed(Vector((-dv21.y, dv21.x)), 0.0) angle21 = angle10 + clamp_angle(angle21 - angle10) yield from cls.arc(v1, radius, resolution, angle10, angle21)
def angle_signed( a: Vector, b: Vector, c: Vector, ): v1 = a - b v2 = c - b return Vector.angle_signed(v1, v2)
def ang_2d_sort(x): pt1 = bm.verts[x].co vec = vm * wm * pt1 - vm * wm * pt0 vec = Vector(vec[0:2]) ang = vec.angle_signed(Vector((1, 0))) if ang < 0: return ang + 3.1415926 * 2 else: return ang
def ang_2d_sort(x): pt1=bm.verts[x].co vec=vm*wm*pt1-vm*wm*pt0 vec=Vector(vec[0:2]) ang=vec.angle_signed(Vector((1,0))) if ang<0: return ang+3.1415926*2 else: return ang
def vector_rotation(vec): """ Find vector rotation according to X axis. :arg vec: Input vector. :type vec: :class:'mathutils.Vector' :return: Angle in radians. :rtype: float """ v0 = Vector((1, 0)) ang = Vector.angle_signed(vec, v0) if ang < 0: ang = 2 * pi + ang return ang
def tractorAimError(self): ''' Return the angle between the vector the animal is facing, and the vector from the animal to the tractor. ''' tractor = GameLogic.getCurrentScene().objects['tractor'] p1 = self.worldPosition.copy() p1 = Vector((p1.x,p1.y)) p2 = tractor.worldPosition.copy() p2 = Vector((p2.x,p2.y)) v1 = self.orientation[:][1].copy() v1 = Vector((v1.x,v1.y*-1)) v2 = p2 - p1 return abs(v1.angle_signed(v2))
def detect_singularities(self): symmetry = nmv.physics.hex_symmetry_space if self.hex_mode else nmv.physics.symmetry_space cache = {} def symmetry_cached(vert): if vert in cache: return cache[vert] else: s = symmetry(self.field[vert.index], vert.normal) cache[vert] = s return s singularities = [] if not self.hex_mode: for face in self.bm.faces: v0 = face.verts[0] v1 = face.verts[1] v2 = face.verts[2] vec0 = self.field[v0.index] vec1 = nmv.physics.best_matching_vector( symmetry_cached(v1), vec0) v2_symmetry = symmetry_cached(v2) match0 = nmv.physics.best_matching_vector(v2_symmetry, vec0) match1 = nmv.physics.best_matching_vector(v2_symmetry, vec1) if match0.dot(match1) < 0.5: singularities.append(face.calc_center_median()) else: for vert in self.bm.verts: ang = 0 u = nmv.physics.random_tangent_vector(vert.normal) v = u.cross(vert.normal) last_vec = None for loop in vert.link_loops: vert1 = loop.link_loop_next.vert vert2 = loop.link_loop_next.link_loop_next.vert if not last_vec: vert1_vec = symmetry_cached(vert1)[0] else: vert1_vec = last_vec vert2_vec = nmv.physics.best_matching_vector( symmetry_cached(vert2), vert1_vec) last_vec = vert2_vec vert1_vec = Vector((vert1_vec.dot(u), vert1_vec.dot(v))) vert2_vec = Vector((vert2_vec.dot(u), vert2_vec.dot(v))) ang += vert1_vec.angle_signed(vert2_vec) if ang > 0.9: singularities.append(vert.co) self.singularities = singularities
def bulge_to_arc(point, next, bulge): """ point: start point of segment in lwpolyline next: end point of segment in lwpolyline bulge: number between 0 and 1 Converts a bulge of lwpolyline to an arc with a bulge describing the amount of how much a straight segment should be bended to an arc. With the bulge one can find the center point of the arc that replaces the segment. """ rot = Matrix(((0, -1, 0), (1, 0, 0), (0, 0, 1))) section = next - point section_length = section.length / 2 direction = -bulge / abs(bulge) correction = 1 sagitta_len = section_length * abs(bulge) radius = (sagitta_len**2 + section_length**2) / (2 * sagitta_len) if sagitta_len < radius: cosagitta_len = radius - sagitta_len else: cosagitta_len = sagitta_len - radius direction *= -1 correction *= -1 center = point + section / 2 + section.normalized( ) * cosagitta_len * direction @ rot cp = point - center cn = next - center cr = cp.to_3d().cross(cn.to_3d()) * correction start = Vector((1, 0)) if cr[2] > 0: angdir = 0 startangle = -start.angle_signed(cp.to_2d()) endangle = -start.angle_signed(cn.to_2d()) else: angdir = 1 startangle = start.angle_signed(cp.to_2d()) endangle = start.angle_signed(cn.to_2d()) return ArcEntity(startangle, endangle, center.to_3d(), radius, angdir)
def bulge_to_arc(point, next, bulge): """ point: start point of segment in lwpolyline next: end point of segment in lwpolyline bulge: number between 0 and 1 Converts a bulge of lwpolyline to an arc with a bulge describing the amount of how much a straight segment should be bended to an arc. With the bulge one can find the center point of the arc that replaces the segment. """ rot = Matrix(((0, -1, 0), (1, 0, 0), (0, 0, 1))) section = next - point section_length = section.length / 2 direction = -bulge / abs(bulge) correction = 1 sagitta_len = section_length * abs(bulge) radius = (sagitta_len**2 + section_length**2) / (2 * sagitta_len) if sagitta_len < radius: cosagitta_len = radius - sagitta_len else: cosagitta_len = sagitta_len - radius direction *= -1 correction *= -1 center = point + section / 2 + section.normalized() * cosagitta_len * rot * direction cp = point - center cn = next - center cr = cp.to_3d().cross(cn.to_3d()) * correction start = Vector((1, 0)) if cr[2] > 0: angdir = 0 startangle = -start.angle_signed(cp.to_2d()) endangle = -start.angle_signed(cn.to_2d()) else: angdir = 1 startangle = start.angle_signed(cp.to_2d()) endangle = start.angle_signed(cn.to_2d()) return ArcEntity(startangle, endangle, center.to_3d(), radius, angdir)
def get_ninety_angle_from(a, b): '''Get angle from horizon bewteen 0 (->) and 90 up : -90 horizon : 0 down : 90 ''' H = Vector((1, 0)) #res = degrees( H.angle(b-a) ) V = b - a if V == Vector((0, 0)): return angle = H.angle_signed(b - a) res = math.degrees(angle) if res > 90: res = res - 180 if res < -90: res = res + 180 return res
def rotate_norms_mouse(modal, context, event, func_data): center = np.array( view3d_utils.location_3d_to_region_2d(modal.act_reg, modal.act_rv3d, modal._mode_cache[0]).to_3d()) start_vec = Vector(modal._mouse_init - center).xy mouse_vec = Vector(modal._mouse_reg_loc - center).xy ang = mouse_vec.angle_signed(start_vec) if event.shift: ang *= 0.1 if ang != 0.0: modal._mode_cache[ 1] = modal._mode_cache[1] + ang * modal._mode_cache[2] rotate_vectors(modal, modal._mode_cache[1]) modal._mouse_init[:] = modal._mouse_reg_loc modal.redraw_active = True return
def modal(self, context, event): context.area.tag_redraw() myobj = context.selected_objects[self.objIndex] annotation = myobj.AnnotationGenerator[0].annotations[self.idx] center = annotation.gizLoc region = bpy.context.region rv3d = bpy.context.space_data.region_3d center = view3d_utils.location_3d_to_region_2d(region, rv3d, center) #For some reason the value returned by view3d utils is 100px off in the y axis center += Vector((0, 100)) vecLast = Vector((self.init_mouse_x, self.init_mouse_y)) vecLast -= center delta = 0 # Set Tweak Flags if event.ctrl: tweak_snap = True else: tweak_snap = False if event.shift: tweak_precise = True else: tweak_precise = False if event.type == 'MOUSEMOVE': sensitivity = 1 vecDelta = Vector((event.mouse_x, event.mouse_y)) vecDelta -= center delta += vecDelta.angle_signed(vecLast) * sensitivity delta = math.degrees(delta) if tweak_snap: delta = 5 * round(delta / 5) if self.constrainAxis[0]: annotation.annotationRotation[0] = self.init_x - math.radians( delta) axisText = 'X: ' if self.constrainAxis[1]: annotation.annotationRotation[1] = self.init_y + math.radians( delta) axisText = 'Y: ' if self.constrainAxis[2]: annotation.annotationRotation[2] = self.init_z - math.radians( delta) axisText = 'Z: ' vecLast = vecDelta context.area.header_text_set("Rotate " + axisText + "%.4f" % delta + "\u00b0") elif event.type == 'LEFTMOUSE': #Setting hide_viewport is a stupid hack to force Gizmos to update after operator completes context.area.header_text_set(None) bpy.context.window.cursor_modal_restore() return {'FINISHED'} elif event.type in {'RIGHTMOUSE', 'ESC'}: #Setting hide_viewport is a stupid hack to force Gizmos to update after operator completes context.area.header_text_set(None) bpy.context.window.cursor_modal_restore() annotation.annotationRotation[0] = self.init_x annotation.annotationRotation[1] = self.init_y annotation.annotationRotation[2] = self.init_z return {'CANCELLED'} return {'RUNNING_MODAL'}
def svg_text_shader(item, text, mid, textCard, color, svg, parent=None): # Card Indicies: # # 1----------------2 # | | # | | # 0----------------3 text_position = get_render_location(mid) svgColor = svgwrite.rgb(color[0] * 100, color[1] * 100, color[2] * 100, '%') ssp0 = get_render_location(textCard[0]) ssp1 = get_render_location(textCard[1]) ssp2 = get_render_location(textCard[2]) ssp3 = get_render_location(textCard[3]) cardHeight = Vector(ssp1) - Vector(ssp0) dirVec = Vector(ssp3) - Vector(ssp0) heightOffsetAmount = 1 / 8 * cardHeight.length heightOffset = Vector((dirVec[1], -dirVec[0])).normalized() heightOffset *= heightOffsetAmount leftVec = Vector(ssp0) rightVec = Vector(ssp3) midVec = (leftVec + rightVec) / 2 if dirVec.length == 0: return text_position = (0, 0) text_anchor = 'start' if item.textAlignment == 'L': text_position = leftVec text_anchor = 'start' position_flip = rightVec anchor_flip = 'end' if item.textAlignment == 'C': text_position = midVec text_anchor = 'middle' position_flip = midVec anchor_flip = 'middle' if item.textAlignment == 'R': text_position = rightVec text_anchor = 'end' position_flip = leftVec anchor_flip = 'start' rotation = math.degrees(dirVec.angle_signed(Vector((1, 0)))) if rotation > 90 or rotation < -90: rotation += 180 #text_position = position_flip text_anchor = anchor_flip heightOffset = -heightOffset print('did flip') print(heightOffset) text_position += heightOffset view = get_view() res = bpy.context.scene.MeasureItArchProps.default_resolution if view is not None: res = view.res parent.add( svg.text( text, insert=tuple(text_position), fill=svgColor, **{ 'transform': 'rotate({} {} {})'.format(rotation, text_position[0], text_position[1]), # I wish i could tell you why this fudge factor is necessary, but for some reason # spec-ing svg units in inches and using this factor for text size is the only way to get # sensible imports in both inkscape and illustrator 'font-size': round(item.fontSize * 4.166666667 / (300 / res), 2), 'font-family': 'OpenSans Regular', 'text-anchor': text_anchor, 'text-align': text_anchor }))
def vector_rotation(vec): v0 = Vector((1,0)) ang = Vector.angle_signed(vec, v0) if ang < 0: ang = 2*pi + ang return ang
def square_fit(context): obj = bpy.context.view_layer.objects.active bm = bmesh.from_edit_mesh(obj.data) uv_layer = bm.loops.layers.uv.verify() faces = list() #MAKE FACE LIST for face in bm.faces: if face.select: faces.append(face) #TEST IF QUADS OR NOT quadmethod = True #EXPERIMENTAL! TO MUCH SKEWING TEST: distorted = False for face in faces: if len(face.loops) is not 4 : quadmethod = False #print('no quad!') #SLOW HERE, find faster way to test if selection is ring shaped #Unwrap and get the edge verts bmesh.update_edit_mesh(obj.data) bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001) bpy.ops.mesh.region_to_loop() obj = bpy.context.edit_object me = obj.data bm = bmesh.from_edit_mesh(me) edge_list = list() for e in bm.edges: if e.select is True: edge_list.append(e) #print(e) #select faces again for f in faces: f.select = True #get start loop (this makes sure we loop in the right direction) startloop = None if(len(edge_list) == 0): print("weird! - means no mesh was sent?") return distorted for l in edge_list[0].link_loops: if l.face.select is True: startloop = l #create sorted verts from start loop sorted_vert_list = list() for f in faces: f.select = False for e in edge_list: e.select = True sorted_vert_list.append(startloop.vert) startloop.edge.select = False sorted_vert_list.append(startloop.link_loop_next.vert) for i in range(1,len(edge_list)-1): #catch if a patch is donut shaped: if i >= len(sorted_vert_list): for f in faces: f.select = True bmesh.update_edit_mesh(obj.data) return False for e in sorted_vert_list[i].link_edges: if e.select is True: sorted_vert_list.append(e.other_vert(sorted_vert_list[i])) e.select = False #select faces again for f in faces: f.select = True #get UV sorted_uv_list = list() uv_layer = bm.loops.layers.uv.active for v in sorted_vert_list: for l in v.link_loops: if l.face.select is True: sorted_uv_list.append(l[uv_layer]) break #get all angles sorted_angle_list = list() for i in range(len(sorted_uv_list)): prev = (i-1)%len(sorted_uv_list) next = (i+1)%len(sorted_uv_list) vector1 = Vector((sorted_uv_list[prev].uv.y-sorted_uv_list[i].uv.y,sorted_uv_list[prev].uv.x-sorted_uv_list[i].uv.x)) vector2 = Vector((sorted_uv_list[next].uv.y-sorted_uv_list[i].uv.y,sorted_uv_list[next].uv.x-sorted_uv_list[i].uv.x)) #check failcase of zero length vector: if vector1.length == 0 or vector2.length == 0: bmesh.update_edit_mesh(obj.data) return False angle = -math.degrees(vector1.angle_signed(vector2)) if angle < 0: angle += 360 sorted_angle_list.append(angle) #find concaves: for i in range(len(sorted_angle_list)): #print(sorted_angle_list[i]) if sorted_angle_list[i] > 230: distorted = True bmesh.update_edit_mesh(obj.data) return False #angle test: #print("angles") #test if more than 4 90 degrees: NCount = 0 for i in range(len(sorted_angle_list)): #print(sorted_angle_list[i]) if sorted_angle_list[i] < 100: NCount += 1 if NCount > 4: distorted = True if quadmethod: #MAP FIRST QUAD edge1 = (faces[0].loops[0].vert.co.xyz - faces[0].loops[1].vert.co.xyz).length edge2 = (faces[0].loops[1].vert.co.xyz - faces[0].loops[2].vert.co.xyz).length faces[0].loops[0][uv_layer].uv.x = 0 faces[0].loops[0][uv_layer].uv.y = 0 faces[0].loops[1][uv_layer].uv.x = edge1 faces[0].loops[1][uv_layer].uv.y = 0 faces[0].loops[2][uv_layer].uv.x = edge1 faces[0].loops[2][uv_layer].uv.y = edge2 faces[0].loops[3][uv_layer].uv.x = 0 faces[0].loops[3][uv_layer].uv.y = edge2 bm.faces.active = faces[0] #UNWRAP ADJACENT bmesh.update_edit_mesh(obj.data) bpy.ops.uv.follow_active_quads() #print("quading it") if quadmethod is False: #now find top 4 angles topangles = list() for o in range(4): top = 360 topindex = -1 for i in range(len(sorted_angle_list)): if sorted_angle_list[i] < top: top = sorted_angle_list[i] topindex = i #print(sorted_angle_list[topindex]) if o is 3: if sorted_angle_list[topindex] > 120: distorted = True topangles.append(topindex) sorted_angle_list[topindex] = 999 #lol sorted_corner_list = list() for i in range(len(sorted_uv_list)): sorted_corner_list.append(False) sorted_corner_list[topangles[0]] = True sorted_corner_list[topangles[1]] = True sorted_corner_list[topangles[2]] = True sorted_corner_list[topangles[3]] = True #find bottom left corner (using distance method seems to work well) distance = 2 closest = 0 for t in topangles: l = sorted_uv_list[t].uv.length if l < distance: distance = l closest = t #rotate lists to get clostest corner at start: for i in range(closest): sorted_corner_list.append(sorted_corner_list.pop(0)) sorted_uv_list.append(sorted_uv_list.pop(0)) sorted_vert_list.append(sorted_vert_list.pop(0)) sorted_edge_ratios = list() #get edge lenghts edge = list() for i in range(len(sorted_vert_list)): if sorted_corner_list[i] is True: sorted_edge_ratios.append(0) if i is not 0: l = (sorted_vert_list[i-1].co.xyz - sorted_vert_list[i].co.xyz).length edge.append(sorted_edge_ratios[i-1] + l) if sorted_corner_list[i] is False: l = (sorted_vert_list[i-1].co.xyz - sorted_vert_list[i].co.xyz).length sorted_edge_ratios.append(sorted_edge_ratios[i-1] + l) if i is (len(sorted_vert_list)-1): l = (sorted_vert_list[i].co.xyz - sorted_vert_list[0].co.xyz).length edge.append(sorted_edge_ratios[i] + l) if distorted is False: #NOW LAY OUT ALL EDGE UVs i = 0 #EDGE 1 for l in sorted_vert_list[i].link_loops: if l.face.select is True: l[uv_layer].uv = Vector((0,0)) i += 1 while sorted_corner_list[i] is False: for l in sorted_vert_list[i].link_loops: if l.face.select is True: l[uv_layer].uv = Vector((sorted_edge_ratios[i]/edge[0],0)) i += 1 #EDGE 2 for l in sorted_vert_list[i].link_loops: if l.face.select is True: l[uv_layer].uv = Vector((1,0)) i += 1 while sorted_corner_list[i] is False: for l in sorted_vert_list[i].link_loops: if l.face.select is True: l[uv_layer].uv = Vector((1,sorted_edge_ratios[i]/edge[1])) i += 1 #EDGE 3 for l in sorted_vert_list[i].link_loops: if l.face.select is True: l[uv_layer].uv = Vector((1,1)) i += 1 while sorted_corner_list[i] is False: for l in sorted_vert_list[i].link_loops: if l.face.select is True: l[uv_layer].uv = Vector((1-(sorted_edge_ratios[i]/edge[2]),1)) i += 1 #EDGE 4 for l in sorted_vert_list[i].link_loops: if l.face.select is True: l[uv_layer].uv = Vector((0,1)) i += 1 for o in range(i,len(sorted_vert_list)): for l in sorted_vert_list[o].link_loops: if l.face.select is True: l[uv_layer].uv = Vector((0,1-(sorted_edge_ratios[o]/edge[3]))) #set proper aspect ratio for f in bm.faces: if f.select is True: for loop in f.loops: loop_uv = loop[uv_layer] loop_uv.uv.x *= edge[0] loop_uv.uv.y *= edge[1] bmesh.update_edit_mesh(me, True) bpy.ops.uv.select_all(action='SELECT') bpy.ops.uv.minimize_stretch(iterations=100) #return true if rect fit was succesful return not distorted