Changeset View
Changeset View
Standalone View
Standalone View
mesh_looptools.py
| Context not available. | |||||
| def cache_read(tool, object, bm, input_method, boundaries): | def cache_read(tool, object, bm, input_method, boundaries): | ||||
| # current tool not cached yet | # current tool not cached yet | ||||
| if tool not in looptools_cache: | if tool not in looptools_cache: | ||||
| return(False, False, False, False, False) | return False, False, False, False, False | ||||
| # check if selected object didn't change | # check if selected object didn't change | ||||
| if object.name != looptools_cache[tool]["object"]: | if object.name != looptools_cache[tool]["object"]: | ||||
| return(False, False, False, False, False) | return False, False, False, False, False | ||||
| # check if input didn't change | # check if input didn't change | ||||
| if input_method != looptools_cache[tool]["input_method"]: | if input_method != looptools_cache[tool]["input_method"]: | ||||
| return(False, False, False, False, False) | return False, False, False, False, False | ||||
| if boundaries != looptools_cache[tool]["boundaries"]: | if boundaries != looptools_cache[tool]["boundaries"]: | ||||
| return(False, False, False, False, False) | return False, False, False, False, False | ||||
| modifiers = [mod.name for mod in object.modifiers if mod.show_viewport \ | modifiers = [mod.name for mod in object.modifiers if mod.show_viewport \ | ||||
| and mod.type == 'MIRROR'] | and mod.type == 'MIRROR'] | ||||
| if modifiers != looptools_cache[tool]["modifiers"]: | if modifiers != looptools_cache[tool]["modifiers"]: | ||||
| return(False, False, False, False, False) | return False, False, False, False, False | ||||
| input = [v.index for v in bm.verts if v.select and not v.hide] | input = [v.index for v in bm.verts if v.select and not v.hide] | ||||
| if input != looptools_cache[tool]["input"]: | if input != looptools_cache[tool]["input"]: | ||||
| return(False, False, False, False, False) | return False, False, False, False, False | ||||
| # reading values | # reading values | ||||
| single_loops = looptools_cache[tool]["single_loops"] | single_loops = looptools_cache[tool]["single_loops"] | ||||
| loops = looptools_cache[tool]["loops"] | loops = looptools_cache[tool]["loops"] | ||||
| derived = looptools_cache[tool]["derived"] | derived = looptools_cache[tool]["derived"] | ||||
| mapping = looptools_cache[tool]["mapping"] | mapping = looptools_cache[tool]["mapping"] | ||||
| return(True, single_loops, loops, derived, mapping) | return True, single_loops, loops, derived, mapping | ||||
| # store information in the cache | # store information in the cache | ||||
| Context not available. | |||||
| knots = knots[4:-4] | knots = knots[4:-4] | ||||
| tknots = tknots[4:-4] | tknots = tknots[4:-4] | ||||
| return(splines) | return splines | ||||
| # calculates linear splines through all given knots | # calculates linear splines through all given knots | ||||
| Context not available. | |||||
| u = tknots[i+1]-t | u = tknots[i+1]-t | ||||
| splines.append([a, d, t, u]) # [locStart, locDif, tStart, tDif] | splines.append([a, d, t, u]) # [locStart, locDif, tStart, tDif] | ||||
| return(splines) | return splines | ||||
| # calculate a best-fit plane to the given vertices | # calculate a best-fit plane to the given vertices | ||||
| Context not available. | |||||
| normal = object.matrix_world.inverted().to_euler().to_matrix() * \ | normal = object.matrix_world.inverted().to_euler().to_matrix() * \ | ||||
| normal | normal | ||||
| return(com, normal) | return com, normal | ||||
| # calculate splines based on given interpolation method (controller function) | # calculate splines based on given interpolation method (controller function) | ||||
| Context not available. | |||||
| else: # interpolations == 'linear' | else: # interpolations == 'linear' | ||||
| splines = calculate_linear_splines(bm_mod, tknots, knots[:]) | splines = calculate_linear_splines(bm_mod, tknots, knots[:]) | ||||
| return(splines) | return splines | ||||
| # check loops and only return valid ones | # check loops and only return valid ones | ||||
| Context not available. | |||||
| # passed all tests, loop is valid | # passed all tests, loop is valid | ||||
| valid_loops.append([loop, circular]) | valid_loops.append([loop, circular]) | ||||
| return(valid_loops) | return valid_loops | ||||
| # input: bmesh, output: dict with the edge-key as key and face-index as value | # input: bmesh, output: dict with the edge-key as key and face-index as value | ||||
| Context not available. | |||||
| for key in face_edgekeys(face): | for key in face_edgekeys(face): | ||||
| edge_faces[key].append(face.index) | edge_faces[key].append(face.index) | ||||
| return(edge_faces) | return edge_faces | ||||
| # input: bmesh (edge-faces optional), output: dict with face-face connections | # input: bmesh (edge-faces optional), output: dict with face-face connections | ||||
| Context not available. | |||||
| continue | continue | ||||
| connected_faces[face.index].append(connected_face) | connected_faces[face.index].append(connected_face) | ||||
| return(connected_faces) | return connected_faces | ||||
| # input: bmesh, output: dict with the vert index as key and edge-keys as value | # input: bmesh, output: dict with the vert index as key and edge-keys as value | ||||
| Context not available. | |||||
| for vert in ek: | for vert in ek: | ||||
| vert_edges[vert].append(ek) | vert_edges[vert].append(ek) | ||||
| return(vert_edges) | return vert_edges | ||||
| # input: bmesh, output: dict with the vert index as key and face index as value | # input: bmesh, output: dict with the vert index as key and face index as value | ||||
| Context not available. | |||||
| for vert in face.verts: | for vert in face.verts: | ||||
| vert_faces[vert.index].append(face.index) | vert_faces[vert.index].append(face.index) | ||||
| return(vert_faces) | return vert_faces | ||||
| # input: list of edge-keys, output: dictionary with vertex-vertex connections | # input: list of edge-keys, output: dictionary with vertex-vertex connections | ||||
| Context not available. | |||||
| else: | else: | ||||
| vert_verts[ek[i]] = [ek[1-i]] | vert_verts[ek[i]] = [ek[1-i]] | ||||
| return(vert_verts) | return vert_verts | ||||
| # return the edgekey ([v1.index, v2.index]) of a bmesh edge | # return the edgekey ([v1.index, v2.index]) of a bmesh edge | ||||
| def edgekey(edge): | def edgekey(edge): | ||||
| return(tuple(sorted([edge.verts[0].index, edge.verts[1].index]))) | return tuple(sorted([edge.verts[0].index, edge.verts[1].index])) | ||||
| # returns the edgekeys of a bmesh face | # returns the edgekeys of a bmesh face | ||||
| Context not available. | |||||
| # if only selected loops are needed, we're done | # if only selected loops are needed, we're done | ||||
| if input == 'selected': | if input == 'selected': | ||||
| return(derived, bm_mod, loops) | return derived, bm_mod, loops | ||||
| # elif input == 'all': | # elif input == 'all': | ||||
| loops = get_parallel_loops(bm_mod, loops) | loops = get_parallel_loops(bm_mod, loops) | ||||
| return(derived, bm_mod, loops) | return derived, bm_mod, loops | ||||
| # sorts all edge-keys into a list of loops | # sorts all edge-keys into a list of loops | ||||
| Context not available. | |||||
| loops.append(loop) | loops.append(loop) | ||||
| return(loops) | return loops | ||||
| # get the derived mesh data, if there is a mirror modifier | # get the derived mesh data, if there is a mirror modifier | ||||
| Context not available. | |||||
| bm_mod.edges.ensure_lookup_table() | bm_mod.edges.ensure_lookup_table() | ||||
| bm_mod.faces.ensure_lookup_table() | bm_mod.faces.ensure_lookup_table() | ||||
| return(derived, bm_mod) | return derived, bm_mod | ||||
| # return a mapping of derived indices to indices | # return a mapping of derived indices to indices | ||||
| def get_mapping(derived, bm, bm_mod, single_vertices, full_search, loops): | def get_mapping(derived, bm, bm_mod, single_vertices, full_search, loops): | ||||
| if not derived: | if not derived: | ||||
| return(False) | return False | ||||
| if full_search: | if full_search: | ||||
| verts = [v for v in bm.verts if not v.hide] | verts = [v for v in bm.verts if not v.hide] | ||||
| Context not available. | |||||
| verts_mod.remove(v_mod) | verts_mod.remove(v_mod) | ||||
| break | break | ||||
| return(mapping) | return mapping | ||||
| # calculate the determinant of a matrix | # calculate the determinant of a matrix | ||||
| Context not available. | |||||
| + m[0][2] * m[1][0] * m[2][1] - m[0][2] * m[1][1] * m[2][0] \ | + m[0][2] * m[1][0] * m[2][1] - m[0][2] * m[1][1] * m[2][0] \ | ||||
| - m[0][1] * m[1][0] * m[2][2] - m[0][0] * m[1][2] * m[2][1] | - m[0][1] * m[1][0] * m[2][2] - m[0][0] * m[1][2] * m[2][1] | ||||
| return(determinant) | return determinant | ||||
| # custom matrix inversion, to provide higher precision than the built-in one | # custom matrix inversion, to provide higher precision than the built-in one | ||||
| Context not available. | |||||
| (m[1][0]*m[2][1] - m[1][1]*m[2][0], m[0][1]*m[2][0] - m[0][0]*m[2][1], | (m[1][0]*m[2][1] - m[1][1]*m[2][0], m[0][1]*m[2][0] - m[0][0]*m[2][1], | ||||
| m[0][0]*m[1][1] - m[0][1]*m[1][0]))) | m[0][0]*m[1][1] - m[0][1]*m[1][0]))) | ||||
| return (r * (1 / matrix_determinant(m))) | return r * (1 / matrix_determinant(m)) | ||||
| # returns a list of all loops parallel to the input, input included | # returns a list of all loops parallel to the input, input included | ||||
| Context not available. | |||||
| # input contains branches, only return selected loop | # input contains branches, only return selected loop | ||||
| if has_branches: | if has_branches: | ||||
| return(loops) | return loops | ||||
| # change edgeloops into normal loops | # change edgeloops into normal loops | ||||
| loops = [] | loops = [] | ||||
| Context not available. | |||||
| circular = False | circular = False | ||||
| loops.append([loop, circular]) | loops.append([loop, circular]) | ||||
| return(loops) | return loops | ||||
| # gather initial data | # gather initial data | ||||
| Context not available. | |||||
| bm.edges.ensure_lookup_table() | bm.edges.ensure_lookup_table() | ||||
| bm.faces.ensure_lookup_table() | bm.faces.ensure_lookup_table() | ||||
| return(global_undo, object, bm) | return global_undo, object, bm | ||||
| # move the vertices to their new locations | # move the vertices to their new locations | ||||
| Context not available. | |||||
| result.append([a[i], b[i], c[i], d[i], x[i]]) | result.append([a[i], b[i], c[i], d[i], x[i]]) | ||||
| spline = [result[1], result[4], result[7]] | spline = [result[1], result[4], result[7]] | ||||
| return(spline) | return spline | ||||
| # return a list with new vertex location vectors, a list with face vertex | # return a list with new vertex location vectors, a list with face vertex | ||||
| Context not available. | |||||
| next_verts = [] | next_verts = [] | ||||
| next_vert_indices = [] | next_vert_indices = [] | ||||
| return(new_verts, faces, max_vert_index) | return new_verts, faces, max_vert_index | ||||
| # calculate lines (list of lists, vertex indices) that are used for bridging | # calculate lines (list of lists, vertex indices) that are used for bridging | ||||
| Context not available. | |||||
| if loop1_circular and loop2_circular: | if loop1_circular and loop2_circular: | ||||
| lines.append([loop1[0], loop2[0]]) | lines.append([loop1[0], loop2[0]]) | ||||
| return(lines) | return lines | ||||
| # calculate number of segments needed | # calculate number of segments needed | ||||
| Context not available. | |||||
| segments = max(1, round(average_bridge_length / average_edge_length)) | segments = max(1, round(average_bridge_length / average_edge_length)) | ||||
| return(segments) | return segments | ||||
| # return dictionary with vertex index as key, and the normal vector as value | # return dictionary with vertex index as key, and the normal vector as value | ||||
| Context not available. | |||||
| vertex_normals = dict([[vertex, vector[0]] for vertex, vector in \ | vertex_normals = dict([[vertex, vector[0]] for vertex, vector in \ | ||||
| vertex_normals.items()]) | vertex_normals.items()]) | ||||
| return(vertex_normals) | return vertex_normals | ||||
| # add vertices to mesh | # add vertices to mesh | ||||
| Context not available. | |||||
| bm.edges.ensure_lookup_table() | bm.edges.ensure_lookup_table() | ||||
| bm.faces.ensure_lookup_table() | bm.faces.ensure_lookup_table() | ||||
| return(new_faces) | return new_faces | ||||
| # calculate input loops | # calculate input loops | ||||
| Context not available. | |||||
| and not edge.hide and edgekey(edge) not in internal_edges] | and not edge.hide and edgekey(edge) not in internal_edges] | ||||
| loops = get_connected_selections(selected_edges) | loops = get_connected_selections(selected_edges) | ||||
| return(loops) | return loops | ||||
| # return values needed by the bridge operator | # return values needed by the bridge operator | ||||
| Context not available. | |||||
| >= 0.5: | >= 0.5: | ||||
| smooth = True | smooth = True | ||||
| return(edge_faces, edgekey_to_edge, old_selected_faces, smooth) | return edge_faces, edgekey_to_edge, old_selected_faces, smooth | ||||
| # return a string with the input method | # return a string with the input method | ||||
| Context not available. | |||||
| else: | else: | ||||
| method = "Bridge" | method = "Bridge" | ||||
| return(method) | return method | ||||
| # match up loops in pairs, used for multi-input bridging | # match up loops in pairs, used for multi-input bridging | ||||
| Context not available. | |||||
| if len(new_order) >= 2: | if len(new_order) >= 2: | ||||
| loops = [loops[i] for i in new_order] | loops = [loops[i] for i in new_order] | ||||
| return(loops) | return loops | ||||
| # remove old_selected_faces | # remove old_selected_faces | ||||
| Context not available. | |||||
| if loft_loop: | if loft_loop: | ||||
| loops = loops + [loops[0]] | loops = loops + [loops[0]] | ||||
| return(loops) | return loops | ||||
| # remapping old indices to new position in list | # remapping old indices to new position in list | ||||
| Context not available. | |||||
| old_selected_faces = [i for i, face in enumerate(bm.faces) if face.index \ | old_selected_faces = [i for i, face in enumerate(bm.faces) if face.index \ | ||||
| in old_selected_faces] | in old_selected_faces] | ||||
| return(old_selected_faces) | return old_selected_faces | ||||
| ########################################## | ########################################## | ||||
| Context not available. | |||||
| y = q.dot(vloc) / q.dot(q) | y = q.dot(vloc) / q.dot(q) | ||||
| locs_2d.append([x, y, vert]) | locs_2d.append([x, y, vert]) | ||||
| return(locs_2d, p, q) | return locs_2d, p, q | ||||
| # calculate a best-fit circle to the 2d locations on the plane | # calculate a best-fit circle to the 2d locations on the plane | ||||
| Context not available. | |||||
| break | break | ||||
| # return center of circle and radius | # return center of circle and radius | ||||
| return(x0, y0, r) | return x0, y0, r | ||||
| # calculate circle so no vertices have to be moved away from the center | # calculate circle so no vertices have to be moved away from the center | ||||
| Context not available. | |||||
| r = min([(mathutils.Vector([i[0], i[1]])-center).length for i in locs_2d]) | r = min([(mathutils.Vector([i[0], i[1]])-center).length for i in locs_2d]) | ||||
| # return center of circle and radius | # return center of circle and radius | ||||
| return(x0, y0, r) | return x0, y0, r | ||||
| # calculate the new locations of the vertices that need to be moved | # calculate the new locations of the vertices that need to be moved | ||||
| Context not available. | |||||
| locs_3d.append([loc[2], loc[0]*p + loc[1]*q + com]) | locs_3d.append([loc[2], loc[0]*p + loc[1]*q + com]) | ||||
| if flatten: # flat circle | if flatten: # flat circle | ||||
| return(locs_3d) | return locs_3d | ||||
| else: # project the locations on the existing mesh | else: # project the locations on the existing mesh | ||||
| vert_edges = dict_vert_edges(bm_mod) | vert_edges = dict_vert_edges(bm_mod) | ||||
| Context not available. | |||||
| new_locs.append([loc[0], projection]) | new_locs.append([loc[0], projection]) | ||||
| # return new positions of projected circle | # return new positions of projected circle | ||||
| return(new_locs) | return new_locs | ||||
| # check loops and only return valid ones | # check loops and only return valid ones | ||||
| Context not available. | |||||
| valid_loops.append([loop, circular]) | valid_loops.append([loop, circular]) | ||||
| valid_single_loops[len(valid_loops)-1] = single_loops[i] | valid_single_loops[len(valid_loops)-1] = single_loops[i] | ||||
| return(valid_single_loops, valid_loops) | return valid_single_loops, valid_loops | ||||
| # calculate the location of single input vertices that need to be flattened | # calculate the location of single input vertices that need to be flattened | ||||
| Context not available. | |||||
| loc = mathutils.Vector(bm_mod.verts[vert].co[:]) | loc = mathutils.Vector(bm_mod.verts[vert].co[:]) | ||||
| new_locs.append([vert, loc - (loc-com).dot(normal)*normal]) | new_locs.append([vert, loc - (loc-com).dot(normal)*normal]) | ||||
| return(new_locs) | return new_locs | ||||
| # calculate input loops | # calculate input loops | ||||
| Context not available. | |||||
| if single not in single_loops[i]: | if single not in single_loops[i]: | ||||
| single_loops[i].append(single) | single_loops[i].append(single) | ||||
| return(derived, bm_mod, single_vertices, single_loops, loops) | return derived, bm_mod, single_vertices, single_loops, loops | ||||
| # recalculate positions based on the influence of the circle shape | # recalculate positions based on the influence of the circle shape | ||||
| Context not available. | |||||
| alty = newy*(influence/100)+ oldy*((100-influence)/100) | alty = newy*(influence/100)+ oldy*((100-influence)/100) | ||||
| locs_2d[i] = [altx, alty, j] | locs_2d[i] = [altx, alty, j] | ||||
| return(locs_2d) | return locs_2d | ||||
| # project 2d locations on circle, respecting distance relations between verts | # project 2d locations on circle, respecting distance relations between verts | ||||
| Context not available. | |||||
| loc.length = r | loc.length = r | ||||
| locs_2d[i] = [loc[0], loc[1], j] | locs_2d[i] = [loc[0], loc[1], j] | ||||
| return(locs_2d) | return locs_2d | ||||
| # project 2d locations on circle, with equal distance between all vertices | # project 2d locations on circle, with equal distance between all vertices | ||||
| Context not available. | |||||
| y = math.sin(t) * r | y = math.sin(t) * r | ||||
| locs_2d[i] = [x, y, locs_2d[i][2]] | locs_2d[i] = [x, y, locs_2d[i][2]] | ||||
| return(locs_2d) | return locs_2d | ||||
| # shift loop, so the first vertex is closest to the center | # shift loop, so the first vertex is closest to the center | ||||
| Context not available. | |||||
| shift = distances[0][1] | shift = distances[0][1] | ||||
| loop = [verts[shift:] + verts[:shift], circular] | loop = [verts[shift:] + verts[:shift], circular] | ||||
| return(loop) | return loop | ||||
| ########################################## | ########################################## | ||||
| Context not available. | |||||
| if loop[0][-1] not in knots: | if loop[0][-1] not in knots: | ||||
| knots.append(loop[0][-1]) | knots.append(loop[0][-1]) | ||||
| return(knots, points) | return knots, points | ||||
| # calculate relative positions compared to first knot | # calculate relative positions compared to first knot | ||||
| Context not available. | |||||
| if circular: | if circular: | ||||
| tknots[-1] = tpoints[-1] | tknots[-1] = tpoints[-1] | ||||
| return(tknots, tpoints) | return tknots, tpoints | ||||
| # change the location of non-selected points to their place on the spline | # change the location of non-selected points to their place on the spline | ||||
| Context not available. | |||||
| if dloc.angle(normal) > 0.5 * math.pi - 1e-6: | if dloc.angle(normal) > 0.5 * math.pi - 1e-6: | ||||
| move.append([p, newloc]) | move.append([p, newloc]) | ||||
| return(move) | return move | ||||
| # trim loops to part between first and last selected vertices (including) | # trim loops to part between first and last selected vertices (including) | ||||
| Context not available. | |||||
| else: | else: | ||||
| cut_loops.append([loop[first:last], circular]) | cut_loops.append([loop[first:last], circular]) | ||||
| return(cut_loops) | return cut_loops | ||||
| # calculate input loops | # calculate input loops | ||||
| Context not available. | |||||
| if boundaries: | if boundaries: | ||||
| correct_loops = curve_cut_boundaries(bm_mod, correct_loops) | correct_loops = curve_cut_boundaries(bm_mod, correct_loops) | ||||
| return(derived, bm_mod, correct_loops) | return derived, bm_mod, correct_loops | ||||
| # return all loops that are perpendicular to the given one | # return all loops that are perpendicular to the given one | ||||
| Context not available. | |||||
| if not loop[1]] | if not loop[1]] | ||||
| if not shortest: | if not shortest: | ||||
| # all loops are circular, not trimming | # all loops are circular, not trimming | ||||
| return([[loop[0], loop[1]] for loop in perp_loops]) | return [[loop[0], loop[1]] for loop in perp_loops] | ||||
| else: | else: | ||||
| shortest = min(shortest) | shortest = min(shortest) | ||||
| shortest_start = perp_loops[shortest[1]][2] | shortest_start = perp_loops[shortest[1]][2] | ||||
| Context not available. | |||||
| end = min(len(loop[0]), loop[2] + after_start + 1) | end = min(len(loop[0]), loop[2] + after_start + 1) | ||||
| trimmed_loops.append([loop[0][start:end], False]) | trimmed_loops.append([loop[0][start:end], False]) | ||||
| return(trimmed_loops) | return trimmed_loops | ||||
| # project knots on non-selected geometry | # project knots on non-selected geometry | ||||
| Context not available. | |||||
| v2 -= v1 | v2 -= v1 | ||||
| v3 -= v1 | v3 -= v1 | ||||
| p = v3.project(v2) | p = v3.project(v2) | ||||
| return(p + v1) | return p + v1 | ||||
| if circular: # project all knots | if circular: # project all knots | ||||
| start = 0 | start = 0 | ||||
| Context not available. | |||||
| if not circular: | if not circular: | ||||
| pknots.append(mathutils.Vector(bm_mod.verts[knots[-1]].co[:])) | pknots.append(mathutils.Vector(bm_mod.verts[knots[-1]].co[:])) | ||||
| return(pknots) | return pknots | ||||
| # find all loops through a given vertex | # find all loops through a given vertex | ||||
| Context not available. | |||||
| loop.reverse() | loop.reverse() | ||||
| loops.append([loop, circular]) | loops.append([loop, circular]) | ||||
| return(loops) | return loops | ||||
| ########################################## | ########################################## | ||||
| Context not available. | |||||
| # no connected verts, consider all selected verts as a single input | # no connected verts, consider all selected verts as a single input | ||||
| if not vert_verts: | if not vert_verts: | ||||
| return([[verts, False]]) | return [[verts, False]] | ||||
| loops = [] | loops = [] | ||||
| while len(verts) > 0: | while len(verts) > 0: | ||||
| Context not available. | |||||
| # add loop to loops | # add loop to loops | ||||
| loops.append([loop, False]) | loops.append([loop, False]) | ||||
| return(loops) | return loops | ||||
| # calculate position of vertex projections on plane | # calculate position of vertex projections on plane | ||||
| Context not available. | |||||
| verts_projected = [[v.index, mathutils.Vector(v.co[:]) - \ | verts_projected = [[v.index, mathutils.Vector(v.co[:]) - \ | ||||
| (mathutils.Vector(v.co[:])-com).dot(normal)*normal] for v in verts] | (mathutils.Vector(v.co[:])-com).dot(normal)*normal] for v in verts] | ||||
| return(verts_projected) | return verts_projected | ||||
| ########################################## | ########################################## | ||||
| Context not available. | |||||
| ########################################## | ########################################## | ||||
| # fake stroke class, used to create custom strokes if no GP data is found | # fake stroke class, used to create custom strokes if no GP data is found | ||||
| class gstretch_fake_stroke(): | class gstretch_fake_stroke: | ||||
| def __init__(self, points): | def __init__(self, points): | ||||
| self.points = [gstretch_fake_stroke_point(p) for p in points] | self.points = [gstretch_fake_stroke_point(p) for p in points] | ||||
| # fake stroke point class, used in fake strokes | # fake stroke point class, used in fake strokes | ||||
| class gstretch_fake_stroke_point(): | class gstretch_fake_stroke_point: | ||||
| def __init__(self, loc): | def __init__(self, loc): | ||||
| self.co = loc | self.co = loc | ||||
| Context not available. | |||||
| relative_distance, stroke_lengths_cache) | relative_distance, stroke_lengths_cache) | ||||
| total_distance += (loc2 - loc1).length | total_distance += (loc2 - loc1).length | ||||
| return(total_distance) | return total_distance | ||||
| if ls_pairs: | if ls_pairs: | ||||
| for (loop, stroke) in ls_pairs: | for (loop, stroke) in ls_pairs: | ||||
| Context not available. | |||||
| if total_dist_rev > total_dist: | if total_dist_rev > total_dist: | ||||
| loop[0].reverse() | loop[0].reverse() | ||||
| return(ls_pairs) | return ls_pairs | ||||
| # calculate vertex positions on stroke | # calculate vertex positions on stroke | ||||
| Context not available. | |||||
| x, dist = mathutils.geometry.intersect_point_line( | x, dist = mathutils.geometry.intersect_point_line( | ||||
| intersections[0], p.co, stroke.points[i].co) | intersections[0], p.co, stroke.points[i].co) | ||||
| if -1 < dist < 1: | if -1 < dist < 1: | ||||
| return(intersections[0]) | return intersections[0] | ||||
| return(None) | return None | ||||
| if method == 'project': | if method == 'project': | ||||
| projection_vectors = [] | projection_vectors = [] | ||||
| Context not available. | |||||
| loc = matrix_inverse * loc | loc = matrix_inverse * loc | ||||
| move.append([v_index, loc]) | move.append([v_index, loc]) | ||||
| return(move) | return move | ||||
| # create new vertices, based on GP strokes | # create new vertices, based on GP strokes | ||||
| Context not available. | |||||
| bm_mod.edges.ensure_lookup_table() | bm_mod.edges.ensure_lookup_table() | ||||
| bmesh.update_edit_mesh(object.data) | bmesh.update_edit_mesh(object.data) | ||||
| return(move) | return move | ||||
| # erases the grease pencil stroke | # erases the grease pencil stroke | ||||
| Context not available. | |||||
| 'pressure': 1, | 'pressure': 1, | ||||
| 'size': 0, | 'size': 0, | ||||
| 'time': 0} | 'time': 0} | ||||
| return(lib) | return lib | ||||
| if type(stroke) != bpy.types.GPencilStroke: | if type(stroke) != bpy.types.GPencilStroke: | ||||
| # fake stroke, there is nothing to delete | # fake stroke, there is nothing to delete | ||||
| Context not available. | |||||
| loc = stroke.points[stroke_index-1].co + \ | loc = stroke.points[stroke_index-1].co + \ | ||||
| distance_relative * interval_vector | distance_relative * interval_vector | ||||
| return(loc, stroke_lengths_cache) | return loc, stroke_lengths_cache | ||||
| # create fake grease pencil strokes for the active object | # create fake grease pencil strokes for the active object | ||||
| Context not available. | |||||
| p2 = object.matrix_world * bm_mod.verts[loop[0][-1]].co | p2 = object.matrix_world * bm_mod.verts[loop[0][-1]].co | ||||
| strokes.append(gstretch_fake_stroke([p1, p2])) | strokes.append(gstretch_fake_stroke([p1, p2])) | ||||
| return(strokes) | return strokes | ||||
| # get grease pencil strokes for the active object | # get grease pencil strokes for the active object | ||||
| def gstretch_get_strokes(object, context): | def gstretch_get_strokes(object, context): | ||||
| gp = get_grease_pencil(object, context) | gp = get_grease_pencil(object, context) | ||||
| if not gp: | if not gp: | ||||
| return(None) | return None | ||||
| layer = gp.layers.active | layer = gp.layers.active | ||||
| if not layer: | if not layer: | ||||
| return(None) | return None | ||||
| frame = layer.active_frame | frame = layer.active_frame | ||||
| if not frame: | if not frame: | ||||
| return(None) | return None | ||||
| strokes = frame.strokes | strokes = frame.strokes | ||||
| if len(strokes) < 1: | if len(strokes) < 1: | ||||
| return(None) | return None | ||||
| return(strokes) | return strokes | ||||
| # returns a list with loop-stroke pairs | # returns a list with loop-stroke pairs | ||||
| def gstretch_match_loops_strokes(loops, strokes, object, bm_mod): | def gstretch_match_loops_strokes(loops, strokes, object, bm_mod): | ||||
| if not loops or not strokes: | if not loops or not strokes: | ||||
| return(None) | return None | ||||
| # calculate loop centers | # calculate loop centers | ||||
| loop_centers = [] | loop_centers = [] | ||||
| Context not available. | |||||
| ls_pairs.append([lc[1], stroke_centers[best_stroke][1]]) | ls_pairs.append([lc[1], stroke_centers[best_stroke][1]]) | ||||
| stroke_centers[best_stroke][2] += 1 # increase stroke use count | stroke_centers[best_stroke][2] += 1 # increase stroke use count | ||||
| return(ls_pairs) | return ls_pairs | ||||
| # match single selected vertices to the closest stroke endpoint | # match single selected vertices to the closest stroke endpoint | ||||
| Context not available. | |||||
| distances_new.append(distance_new[0]) | distances_new.append(distance_new[0]) | ||||
| distances = distances_new | distances = distances_new | ||||
| return(singles) | return singles | ||||
| # returns list with a relative distance (0.0 - 1.0) of each vertex on the loop | # returns list with a relative distance (0.0 - 1.0) of each vertex on the loop | ||||
| Context not available. | |||||
| relative_lengths = [length / total_length for length in | relative_lengths = [length / total_length for length in | ||||
| lengths] | lengths] | ||||
| return(relative_lengths) | return relative_lengths | ||||
| # convert cache-stored strokes into usable (fake) GP strokes | # convert cache-stored strokes into usable (fake) GP strokes | ||||
| Context not available. | |||||
| for safe_stroke in safe_strokes: | for safe_stroke in safe_strokes: | ||||
| strokes.append(gstretch_fake_stroke(safe_stroke)) | strokes.append(gstretch_fake_stroke(safe_stroke)) | ||||
| return(strokes) | return strokes | ||||
| # convert a GP stroke into a list of points which can be stored in cache | # convert a GP stroke into a list of points which can be stored in cache | ||||
| Context not available. | |||||
| for stroke in strokes: | for stroke in strokes: | ||||
| safe_strokes.append([p.co.copy() for p in stroke.points]) | safe_strokes.append([p.co.copy() for p in stroke.points]) | ||||
| return(safe_strokes) | return safe_strokes | ||||
| # force consistency in GUI, max value can never be lower than min value | # force consistency in GUI, max value can never be lower than min value | ||||
| Context not available. | |||||
| for p in points: | for p in points: | ||||
| all_points.append(p) | all_points.append(p) | ||||
| return(all_knots, all_points) | return all_knots, all_points | ||||
| # calculate relative positions compared to first knot | # calculate relative positions compared to first knot | ||||
| Context not available. | |||||
| all_tknots.append(tknots) | all_tknots.append(tknots) | ||||
| all_tpoints.append(tpoints) | all_tpoints.append(tpoints) | ||||
| return(all_tknots, all_tpoints) | return all_tknots, all_tpoints | ||||
| # change the location of the points to their place on the spline | # change the location of the points to their place on the spline | ||||
| Context not available. | |||||
| for c in change: | for c in change: | ||||
| move.append([c[0], (bm_mod.verts[c[0]].co + c[1]) / 2]) | move.append([c[0], (bm_mod.verts[c[0]].co + c[1]) / 2]) | ||||
| return(move) | return move | ||||
| ########################################## | ########################################## | ||||
| Context not available. | |||||
| t_per_segment = len_total / (amount - 1) | t_per_segment = len_total / (amount - 1) | ||||
| tpoints = [i * t_per_segment for i in range(amount)] | tpoints = [i * t_per_segment for i in range(amount)] | ||||
| return(tknots, tpoints) | return tknots, tpoints | ||||
| # change the location of the points to their place on the spline | # change the location of the points to their place on the spline | ||||
| Context not available. | |||||
| a, d, t, u = splines[n] | a, d, t, u = splines[n] | ||||
| move.append([p, ((m-t)/u)*d + a]) | move.append([p, ((m-t)/u)*d + a]) | ||||
| return(move) | return move | ||||
| ########################################## | ########################################## | ||||
| Context not available. | |||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| ob = context.active_object | ob = context.active_object | ||||
| return (ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH') | return ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH' | ||||
| def draw(self, context): | def draw(self, context): | ||||
| layout = self.layout | layout = self.layout | ||||
| Context not available. | |||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| ob = context.active_object | ob = context.active_object | ||||
| return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH') | return ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH' | ||||
| def draw(self, context): | def draw(self, context): | ||||
| layout = self.layout | layout = self.layout | ||||
| Context not available. | |||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| ob = context.active_object | ob = context.active_object | ||||
| return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH') | return ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH' | ||||
| def draw(self, context): | def draw(self, context): | ||||
| layout = self.layout | layout = self.layout | ||||
| Context not available. | |||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| ob = context.active_object | ob = context.active_object | ||||
| return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH') | return ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH' | ||||
| def draw(self, context): | def draw(self, context): | ||||
| layout = self.layout | layout = self.layout | ||||
| Context not available. | |||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| ob = context.active_object | ob = context.active_object | ||||
| return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH') | return ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH' | ||||
| def draw(self, context): | def draw(self, context): | ||||
| layout = self.layout | layout = self.layout | ||||
| Context not available. | |||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| ob = context.active_object | ob = context.active_object | ||||
| return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH') | return ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH' | ||||
| def draw(self, context): | def draw(self, context): | ||||
| layout = self.layout | layout = self.layout | ||||
| Context not available. | |||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| ob = context.active_object | ob = context.active_object | ||||
| return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH') | return ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH' | ||||
| def draw(self, context): | def draw(self, context): | ||||
| layout = self.layout | layout = self.layout | ||||
| Context not available. | |||||