diff options
Diffstat (limited to 'release/scripts/modules/retopo.py')
-rw-r--r-- | release/scripts/modules/retopo.py | 506 |
1 files changed, 0 insertions, 506 deletions
diff --git a/release/scripts/modules/retopo.py b/release/scripts/modules/retopo.py deleted file mode 100644 index 6fba34b7b58..00000000000 --- a/release/scripts/modules/retopo.py +++ /dev/null @@ -1,506 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -import bpy - -EPS_SPLINE_DIV = 15.0 # remove doubles is ~15th the length of the spline -ANGLE_JOIN_LIMIT = 25.0 # limit for joining splines into 1. - -def get_hub(co, _hubs, EPS_SPLINE): - - if 1: - for hub in _hubs.values(): - if (hub.co - co).length < EPS_SPLINE: - return hub - - key = co.toTuple(3) - hub = _hubs[key] = Hub(co, key, len(_hubs)) - return hub - else: - pass - - ''' - key = co.toTuple(3) - try: - return _hubs[key] - except: - hub = _hubs[key] = Hub(co, key, len(_hubs)) - return hub - ''' - - -class Hub(object): - __slots__ = "co", "key", "index", "links" - - def __init__(self, co, key, index): - self.co = co.copy() - self.key = key - self.index = index - self.links = [] - - def get_weight(self): - f = 0.0 - - for hub_other in self.links: - f += (self.co - hub_other.co).length - - def replace(self, other): - for hub in self.links: - try: - hub.links.remove(self) - except: - pass - if other not in hub.links: - hub.links.append(other) - - def dist(self, other): - return (self.co - other.co).length - - def calc_faces(self, hub_ls): - faces = [] - # first tris - for l_a in self.links: - for l_b in l_a.links: - if l_b is not self and l_b in self.links: - # will give duplicates - faces.append((self.index, l_a.index, l_b.index)) - - # now quads, check which links share 2 different verts directly - def validate_quad(face): - if len(set(face)) != len(face): - return False - if hub_ls[face[0]] in hub_ls[face[2]].links: - return False - if hub_ls[face[2]] in hub_ls[face[0]].links: - return False - - if hub_ls[face[1]] in hub_ls[face[3]].links: - return False - if hub_ls[face[3]] in hub_ls[face[1]].links: - return False - - return True - - for i, l_a in enumerate(self.links): - links_a = {l.index for l in l_a.links} - for j in range(i): - l_b = self.links[j] - - links_b = {l.index for l in l_b.links} - - isect = links_a.intersection(links_b) - if len(isect) == 2: - isect = list(isect) - - # check there are no diagonal lines - face = (isect[0], l_a.index, isect[1], l_b.index) - if validate_quad(face): - - faces.append(face) - - return faces - - -class BBox(object): - __slots__ = "xmin", "ymin", "zmin", "xmax", "ymax", "zmax" - - def __init__(self): - self.xmin = self.ymin = self.zmin = 100000000.0 - self.xmax = self.ymax = self.zmax = -100000000.0 - - @property - def xdim(self): - return self.xmax - self.xmin - - @property - def ydim(self): - return self.ymax - self.ymin - - @property - def zdim(self): - return self.zmax - self.zmin - - def calc(self, points): - xmin = ymin = zmin = 100000000.0 - xmax = ymax = zmax = -100000000.0 - - for pt in points: - x, y, z = pt - if x < xmin: - xmin = x - if y < ymin: - ymin = y - if z < zmin: - zmin = z - - if x > xmax: - xmax = x - if y > ymax: - ymax = y - if z > zmax: - zmax = z - - self.xmin, self.ymin, self.zmin = xmin, ymin, zmin - self.xmax, self.ymax, self.zmax = xmax, ymax, zmax - - def xsect(self, other, margin=0.0): - if margin == 0.0: - if self.xmax < other.xmin: - return False - if self.ymax < other.ymin: - return False - if self.zmax < other.zmin: - return False - - if self.xmin > other.xmax: - return False - if self.ymin > other.ymax: - return False - if self.zmin > other.zmax: - return False - - else: - xmargin = ((self.xdim + other.xdim) / 2.0) * margin - ymargin = ((self.ydim + other.ydim) / 2.0) * margin - zmargin = ((self.zdim + other.zdim) / 2.0) * margin - - if self.xmax < other.xmin - xmargin: - return False - if self.ymax < other.ymin - ymargin: - return False - if self.zmax < other.zmin - zmargin: - return False - - if self.xmin > other.xmax + xmargin: - return False - if self.ymin > other.ymax + ymargin: - return False - if self.zmin > other.zmax + zmargin: - return False - return True - - def __iadd__(self, other): - self.xmin = min(self.xmin, other.xmin) - self.ymin = min(self.ymin, other.ymin) - self.zmin = min(self.zmin, other.zmin) - - self.xmax = max(self.xmax, other.xmax) - self.ymax = max(self.ymax, other.ymax) - self.zmax = max(self.zmax, other.zmax) - return self - -class Spline(object): - __slots__ = "points", "hubs", "length", "bb" - - def __init__(self, points): - self.points = points - self.hubs = [] - self.calc_length() - self.bb = BBox() - self.bb.calc(points) - - def calc_length(self): - # calc length - f = 0.0 - co_prev = self.points[0] - for co in self.points[1:]: - f += (co - co_prev).length - co_prev = co - self.length = f - - def link(self): - if len(self.hubs) < 2: - return - - edges = list(set([i for i, hub in self.hubs])) - edges.sort() - - edges_order = {} - for i in edges: - edges_order[i] = [] - - - # self.hubs.sort() - for i, hub in self.hubs: - edges_order[i].append(hub) - - hubs_order = [] - for i in edges: - ls = edges_order[i] - edge_start = self.points[i] - ls.sort(key=lambda hub: (hub.co - edge_start).length) - hubs_order.extend(ls) - - # Now we have the order, connect the hubs - hub_prev = hubs_order[0] - - for hub in hubs_order[1:]: - hub.links.append(hub_prev) - hub_prev.links.append(hub) - hub_prev = hub - - -def get_points(stroke): - return [point.co.copy() for point in stroke.points] - - -def get_splines(gp): - l = None - for l in gp.layers: - if l.active: # XXX - should be layers.active - break - if l: - frame = l.active_frame - return [Spline(get_points(stroke)) for stroke in frame.strokes] - else: - return [] - - -def xsect_spline(sp_a, sp_b, _hubs): - from Mathutils import LineIntersect - from Mathutils import MidpointVecs - from Geometry import ClosestPointOnLine - pt_a_prev = pt_b_prev = None - EPS_SPLINE = (sp_a.length + sp_b.length) / (EPS_SPLINE_DIV * 2) - pt_a_prev = sp_a.points[0] - for a, pt_a in enumerate(sp_a.points[1:]): - pt_b_prev = sp_b.points[0] - for b, pt_b in enumerate(sp_b.points[1:]): - - # Now we have 2 edges - # print(pt_a, pt_a_prev, pt_b, pt_b_prev) - xsect = LineIntersect(pt_a, pt_a_prev, pt_b, pt_b_prev) - if xsect is not None: - if (xsect[0] - xsect[1]).length <= EPS_SPLINE: - f = ClosestPointOnLine(xsect[1], pt_a, pt_a_prev)[1] - # if f >= 0.0-EPS_SPLINE and f <= 1.0+EPS_SPLINE: # for some reason doesnt work so well, same below - if f >= 0.0 and f <= 1.0: - f = ClosestPointOnLine(xsect[0], pt_b, pt_b_prev)[1] - # if f >= 0.0-EPS_SPLINE and f <= 1.0+EPS_SPLINE: - if f >= 0.0 and f <= 1.0: - # This wont happen often - co = MidpointVecs(xsect[0], xsect[1]) - hub = get_hub(co, _hubs, EPS_SPLINE) - - sp_a.hubs.append((a, hub)) - sp_b.hubs.append((b, hub)) - - pt_b_prev = pt_b - - pt_a_prev = pt_a - - -def connect_splines(splines): - HASH_PREC = 8 - from Mathutils import AngleBetweenVecs - from math import radians - ANG_LIMIT = radians(ANGLE_JOIN_LIMIT) - def sort_pair(a, b): - if a < b: - return a, b - else: - return b, a - - #def test_join(p1a, p1b, p2a, p2b, length_average): - def test_join(s1, s2, dir1, dir2, length_average): - if dir1 is False: - p1a = s1.points[0] - p1b = s1.points[1] - else: - p1a = s1.points[-1] - p1b = s1.points[-2] - - if dir2 is False: - p2a = s2.points[0] - p2b = s2.points[1] - else: - p2a = s2.points[-1] - p2b = s2.points[-2] - - # compare length between tips - if (p1a - p2a).length > (length_average / EPS_SPLINE_DIV): - return False - - v1 = p1a - p1b - v2 = p2b - p2a - - if AngleBetweenVecs(v1, v2) > ANG_LIMIT: - return False - - # print("joining!") - return True - - # lazy, hash the points that have been compared. - comparisons = set() - - do_join = True - while do_join: - do_join = False - for i, s1 in enumerate(splines): - key1a = s1.points[0].toTuple(HASH_PREC) - key1b = s1.points[-1].toTuple(HASH_PREC) - - for j, s2 in enumerate(splines): - if s1 is s2: - continue - - length_average = min(s1.length, s2.length) - - key2a = s2.points[0].toTuple(HASH_PREC) - key2b = s2.points[-1].toTuple(HASH_PREC) - - # there are 4 ways this may be joined - key_pair = sort_pair(key1a, key2a) - if key_pair not in comparisons: - comparisons.add(key_pair) - if test_join(s1, s2, False, False, length_average): - s1.points[:0] = reversed(s2.points) - s1.bb += s2.bb - s1.calc_length() - del splines[j] - do_join = True - break - - key_pair = sort_pair(key1a, key2b) - if key_pair not in comparisons: - comparisons.add(key_pair) - if test_join(s1, s2, False, True, length_average): - s1.points[:0] = s2.points - s1.bb += s2.bb - s1.calc_length() - del splines[j] - do_join = True - break - - key_pair = sort_pair(key1b, key2b) - if key_pair not in comparisons: - comparisons.add(key_pair) - if test_join(s1, s2, True, True, length_average): - s1.points += list(reversed(s2.points)) - s1.bb += s2.bb - s1.calc_length() - del splines[j] - do_join = True - break - - key_pair = sort_pair(key1b, key2a) - if key_pair not in comparisons: - comparisons.add(key_pair) - if test_join(s1, s2, True, False, length_average): - s1.points += s2.points - s1.bb += s2.bb - s1.calc_length() - del splines[j] - do_join = True - break - - if do_join: - break - - -def calculate(gp): - splines = get_splines(gp) - - # spline endpoints may be co-linear, join these into single splines - connect_splines(splines) - - _hubs = {} - - for i, sp in enumerate(splines): - for j, sp_other in enumerate(splines): - if j <= i: - continue - - if sp.bb.xsect(sp_other.bb, margin=0.1): - xsect_spline(sp, sp_other, _hubs) - - for sp in splines: - sp.link() - - # remove these - hubs_ls = [hub for hub in _hubs.values() if hub.index != -1] - - _hubs.clear() - _hubs = None - - for i, hub in enumerate(hubs_ls): - hub.index = i - - # Now we have connected hubs, write all edges! - def order(i1, i2): - if i1 > i2: - return i2, i1 - return i1, i2 - - edges = {} - - for hub in hubs_ls: - i1 = hub.index - for hub_other in hub.links: - i2 = hub_other.index - edges[order(i1, i2)] = None - - verts = [] - edges = edges.keys() - faces = [] - - for hub in hubs_ls: - verts.append(hub.co) - faces.extend(hub.calc_faces(hubs_ls)) - - # remove double faces - faces = dict([(tuple(sorted(f)), f) for f in faces]).values() - - mesh = bpy.data.meshes.new("Retopo") - mesh.from_pydata(verts, [], faces) - - scene = bpy.context.scene - mesh.update() - obj_new = bpy.data.objects.new("Torus", 'MESH') - obj_new.data = mesh - scene.objects.link(obj_new) - - return obj_new - - -def main(): - scene = bpy.context.scene - obj = bpy.context.object - - gp = None - - if obj: - gp = obj.grease_pencil - - if not gp: - gp = scene.grease_pencil - - if not gp: - raise Exception("no active grease pencil") - - obj_new = calculate(gp) - - scene.objects.active = obj_new - obj_new.selected = True - - # nasty, recalc normals - bpy.ops.object.mode_set(mode='EDIT', toggle=False) - bpy.ops.mesh.normals_make_consistent(inside=False) - bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |