| 
									
										
										
										
											2009-12-16 21:27:07 +00:00
										 |  |  | # ##### 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, | 
					
						
							| 
									
										
										
										
											2010-02-12 13:34:04 +00:00
										 |  |  | #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
					
						
							| 
									
										
										
										
											2009-12-16 21:27:07 +00:00
										 |  |  | # | 
					
						
							|  |  |  | # ##### END GPL LICENSE BLOCK ##### | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # <pep8 compliant> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #for full docs see... | 
					
						
							|  |  |  | # http://mediawiki.blender.org/index.php/Scripts/Manual/UV_Calculate/Follow_active_quads | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import bpy | 
					
						
							| 
									
										
										
										
											2011-08-12 06:57:00 +00:00
										 |  |  | from bpy.types import Operator | 
					
						
							| 
									
										
										
										
											2009-12-16 21:27:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-16 21:27:07 +00:00
										 |  |  | def extend(obj, operator, EXTEND_MODE): | 
					
						
							| 
									
										
										
										
											2013-01-04 07:57:33 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |     import bmesh | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  |     me = obj.data | 
					
						
							|  |  |  |     # script will fail without UVs | 
					
						
							| 
									
										
										
										
											2010-08-23 22:16:45 +00:00
										 |  |  |     if not me.uv_textures: | 
					
						
							|  |  |  |         me.uv_textures.new() | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |      | 
					
						
							|  |  |  |     bm = bmesh.from_edit_mesh(me) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     f_act = bm.faces.active | 
					
						
							|  |  |  |     uv_act = bm.loops.layers.uv.active | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     if f_act is None: | 
					
						
							|  |  |  |         operator.report({'ERROR'}, "No active face") | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     elif len(f_act.verts) != 4: | 
					
						
							|  |  |  |         operator.report({'ERROR'}, "Active face must be a quad") | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |     faces = [f for f in bm.faces if f.select and len(f.verts) == 4] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # our own local walker | 
					
						
							| 
									
										
										
										
											2013-01-04 07:57:33 +00:00
										 |  |  |     def walk_face_init(faces, f_act): | 
					
						
							| 
									
										
										
										
											2013-01-08 12:10:53 +00:00
										 |  |  |         # first tag all faces True (so we dont uvmap them) | 
					
						
							|  |  |  |         for f in bm.faces: | 
					
						
							|  |  |  |             f.tag = True | 
					
						
							|  |  |  |         # then tag faces arg False | 
					
						
							| 
									
										
										
										
											2013-01-04 07:57:33 +00:00
										 |  |  |         for f in faces: | 
					
						
							|  |  |  |             f.tag = False | 
					
						
							| 
									
										
										
										
											2013-01-08 12:10:53 +00:00
										 |  |  |         # tag the active face True since we begin there | 
					
						
							| 
									
										
										
										
											2013-01-04 07:57:33 +00:00
										 |  |  |         f_act.tag = True | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |     def walk_face(f): | 
					
						
							|  |  |  |         # all faces in this list must be tagged | 
					
						
							|  |  |  |         f.tag = True | 
					
						
							|  |  |  |         faces_a = [f] | 
					
						
							|  |  |  |         faces_b = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         while faces_a: | 
					
						
							|  |  |  |             for f in faces_a: | 
					
						
							|  |  |  |                 for l in f.loops: | 
					
						
							|  |  |  |                     l_edge = l.edge | 
					
						
							|  |  |  |                     if (l_edge.is_manifold is True) and (l_edge.seam is False): | 
					
						
							|  |  |  |                         l_other = l.link_loop_radial_next | 
					
						
							|  |  |  |                         f_other = l_other.face | 
					
						
							|  |  |  |                         if not f_other.tag: | 
					
						
							|  |  |  |                             yield (f, l, f_other) | 
					
						
							|  |  |  |                             f_other.tag = True | 
					
						
							|  |  |  |                             faces_b.append(f_other) | 
					
						
							|  |  |  |             # swap | 
					
						
							|  |  |  |             faces_a, faces_b = faces_b, faces_a | 
					
						
							|  |  |  |             faces_b.clear() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-04 07:57:33 +00:00
										 |  |  |     def walk_edgeloop(l): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Could make this a generic function | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         e_first = l.edge | 
					
						
							|  |  |  |         e = None | 
					
						
							|  |  |  |         while True: | 
					
						
							|  |  |  |             e = l.edge | 
					
						
							|  |  |  |             yield e | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # don't step past non-manifold edges | 
					
						
							|  |  |  |             if e.is_manifold: | 
					
						
							|  |  |  |                 # welk around the quad and then onto the next face | 
					
						
							|  |  |  |                 l = l.link_loop_radial_next | 
					
						
							|  |  |  |                 if len(l.face.verts) == 4: | 
					
						
							|  |  |  |                     l = l.link_loop_next.link_loop_next | 
					
						
							|  |  |  |                     if l.edge is e_first: | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |     def extrapolate_uv(fac, | 
					
						
							|  |  |  |                        l_a_outer, l_a_inner, | 
					
						
							|  |  |  |                        l_b_outer, l_b_inner): | 
					
						
							|  |  |  |         l_b_inner[:] = l_a_inner | 
					
						
							|  |  |  |         l_b_outer[:] = l_a_inner + ((l_a_inner - l_a_outer) * fac) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def apply_uv(f_prev, l_prev, f_next): | 
					
						
							|  |  |  |         l_a = [None, None, None, None] | 
					
						
							|  |  |  |         l_b = [None, None, None, None] | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         l_a[0] = l_prev | 
					
						
							|  |  |  |         l_a[1] = l_a[0].link_loop_next | 
					
						
							|  |  |  |         l_a[2] = l_a[1].link_loop_next | 
					
						
							|  |  |  |         l_a[3] = l_a[2].link_loop_next | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         #  l_b | 
					
						
							|  |  |  |         #  +-----------+ | 
					
						
							|  |  |  |         #  |(3)        |(2) | 
					
						
							|  |  |  |         #  |           | | 
					
						
							|  |  |  |         #  |l_next(0)  |(1) | 
					
						
							|  |  |  |         #  +-----------+ | 
					
						
							|  |  |  |         #        ^ | 
					
						
							|  |  |  |         #  l_a   | | 
					
						
							|  |  |  |         #  +-----------+ | 
					
						
							|  |  |  |         #  |l_prev(0)  |(1) | 
					
						
							|  |  |  |         #  |    (f)    | | 
					
						
							|  |  |  |         #  |(3)        |(2) | 
					
						
							|  |  |  |         #  +-----------+ | 
					
						
							|  |  |  |         #  copy from this face to the one above. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # get the other loops  | 
					
						
							|  |  |  |         l_next = l_prev.link_loop_radial_next | 
					
						
							|  |  |  |         if l_next.vert != l_prev.vert: | 
					
						
							|  |  |  |             l_b[1] = l_next | 
					
						
							|  |  |  |             l_b[0] = l_b[1].link_loop_next | 
					
						
							|  |  |  |             l_b[3] = l_b[0].link_loop_next | 
					
						
							|  |  |  |             l_b[2] = l_b[3].link_loop_next | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             l_b[0] = l_next | 
					
						
							|  |  |  |             l_b[1] = l_b[0].link_loop_next | 
					
						
							|  |  |  |             l_b[2] = l_b[1].link_loop_next | 
					
						
							|  |  |  |             l_b[3] = l_b[2].link_loop_next | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |         l_a_uv = [l[uv_act].uv for l in l_a] | 
					
						
							|  |  |  |         l_b_uv = [l[uv_act].uv for l in l_b] | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-04 07:57:33 +00:00
										 |  |  |         if EXTEND_MODE == 'LENGTH_AVERAGE': | 
					
						
							|  |  |  |             fac = edge_lengths[l_b[2].edge.index][0] / edge_lengths[l_a[1].edge.index][0] | 
					
						
							|  |  |  |         elif EXTEND_MODE == 'LENGTH': | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |             a0, b0, c0 = l_a[3].vert.co, l_a[0].vert.co, l_b[3].vert.co | 
					
						
							|  |  |  |             a1, b1, c1 = l_a[2].vert.co, l_a[1].vert.co, l_b[2].vert.co | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |             d1 = (a0 - b0).length + (a1 - b1).length | 
					
						
							|  |  |  |             d2 = (b0 - c0).length + (b1 - c1).length | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 fac = d2 / d1 | 
					
						
							|  |  |  |             except ZeroDivisionError: | 
					
						
							|  |  |  |                 fac = 1.0 | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |             fac = 1.0 | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |         extrapolate_uv(fac, | 
					
						
							|  |  |  |                        l_a_uv[3], l_a_uv[0], | 
					
						
							|  |  |  |                        l_b_uv[3], l_b_uv[0]) | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |         extrapolate_uv(fac, | 
					
						
							|  |  |  |                        l_a_uv[2], l_a_uv[1], | 
					
						
							|  |  |  |                        l_b_uv[2], l_b_uv[1]) | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-04 07:57:33 +00:00
										 |  |  |     # ------------------------------------------- | 
					
						
							|  |  |  |     # Calculate average length per loop if needed | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if EXTEND_MODE == 'LENGTH_AVERAGE': | 
					
						
							|  |  |  |         bm.edges.index_update() | 
					
						
							|  |  |  |         edge_lengths = [None] * len(bm.edges) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for f in faces: | 
					
						
							|  |  |  |             # we know its a quad | 
					
						
							|  |  |  |             l_quad = f.loops[:] | 
					
						
							|  |  |  |             l_pair_a = (l_quad[0], l_quad[2]) | 
					
						
							|  |  |  |             l_pair_b = (l_quad[1], l_quad[3]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for l_pair in (l_pair_a, l_pair_b): | 
					
						
							|  |  |  |                 if edge_lengths[l_pair[0].edge.index] is None: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     edge_length_store = [-1.0] | 
					
						
							|  |  |  |                     edge_length_accum = 0.0 | 
					
						
							|  |  |  |                     edge_length_total = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     for l in l_pair: | 
					
						
							|  |  |  |                         if edge_lengths[l.edge.index] is None: | 
					
						
							|  |  |  |                             for e in walk_edgeloop(l): | 
					
						
							|  |  |  |                                 if edge_lengths[e.index] is None: | 
					
						
							|  |  |  |                                     edge_lengths[e.index] = edge_length_store | 
					
						
							|  |  |  |                                     edge_length_accum += e.calc_length() | 
					
						
							|  |  |  |                                     edge_length_total += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     edge_length_store[0] = edge_length_accum / edge_length_total | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # done with average length | 
					
						
							|  |  |  |     # ------------------------ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     walk_face_init(faces, f_act) | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |     for f_triple in walk_face(f_act): | 
					
						
							|  |  |  |         apply_uv(*f_triple) | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-29 14:02:28 +00:00
										 |  |  |     bmesh.update_edit_mesh(me, False) | 
					
						
							| 
									
										
										
										
											2009-12-16 21:27:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(context, operator): | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  |     obj = context.active_object | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     extend(obj, operator, operator.properties.mode) | 
					
						
							| 
									
										
										
										
											2009-12-16 21:27:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-12 06:57:00 +00:00
										 |  |  | class FollowActiveQuads(Operator): | 
					
						
							| 
									
										
										
										
											2012-07-03 09:02:41 +00:00
										 |  |  |     """Follow UVs from active quads along continuous face loops""" | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  |     bl_idname = "uv.follow_active_quads" | 
					
						
							|  |  |  |     bl_label = "Follow Active Quads" | 
					
						
							| 
									
										
										
										
											2010-03-01 00:03:51 +00:00
										 |  |  |     bl_options = {'REGISTER', 'UNDO'} | 
					
						
							| 
									
										
										
										
											2009-12-16 21:27:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-11 03:28:46 +00:00
										 |  |  |     mode = bpy.props.EnumProperty( | 
					
						
							|  |  |  |             name="Edge Length Mode", | 
					
						
							|  |  |  |             description="Method to space UV edge loops", | 
					
						
							| 
									
										
										
										
											2012-07-04 15:52:07 +00:00
										 |  |  |             items=(('EVEN', "Even", "Space all UVs evenly"), | 
					
						
							| 
									
										
										
										
											2013-01-04 07:57:33 +00:00
										 |  |  |                    ('LENGTH', "Length", "Average space UVs edge length of each loop"), | 
					
						
							|  |  |  |                    ('LENGTH_AVERAGE', "Length Average", "Average space UVs edge length of each loop"), | 
					
						
							|  |  |  |                    ), | 
					
						
							|  |  |  |             default='LENGTH_AVERAGE', | 
					
						
							| 
									
										
										
										
											2011-11-11 03:28:46 +00:00
										 |  |  |             ) | 
					
						
							| 
									
										
										
										
											2009-12-16 21:27:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-09 01:37:09 +00:00
										 |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  |         obj = context.active_object | 
					
						
							|  |  |  |         return (obj is not None and obj.type == 'MESH') | 
					
						
							| 
									
										
										
										
											2009-12-16 21:27:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-17 01:21:55 +00:00
										 |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         main(context, self) | 
					
						
							| 
									
										
										
										
											2009-12-24 19:50:43 +00:00
										 |  |  |         return {'FINISHED'} | 
					
						
							| 
									
										
										
										
											2009-12-16 21:27:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-09 11:01:44 +00:00
										 |  |  |     def invoke(self, context, event): | 
					
						
							|  |  |  |         wm = context.window_manager | 
					
						
							|  |  |  |         return wm.invoke_props_dialog(self) |