Fix T52833: OBJ triangulate doesn't match viewport
[blender-addons.git] / add_advanced_objects_menu / arrange_on_curve.py
blobc292e341da92293accb13d16d49513aad7da6dc2
1 # gpl author: Mano-Wii
3 bl_info = {
4 "name": "Arrange on Curve",
5 "author": "Mano-Wii",
6 "version": (6, 3, 0),
7 "blender": (2, 7, 7),
8 "location": "3D View > Toolshelf > Create > Arrange on Curve",
9 "description": "Arrange objects along a curve",
10 "warning": "Select curve",
11 "wiki_url": "",
12 "category": "3D View"
15 # Note: scene properties are moved into __init__
16 # search for patterns advanced_objects and adv_obj
18 import bpy
19 import mathutils
20 from bpy.types import (
21 Operator,
22 Panel,
24 from bpy.props import (
25 EnumProperty,
26 FloatProperty,
27 IntProperty,
30 FLT_MIN = 0.004
33 class PanelDupliCurve(Panel):
34 bl_idname = "VIEW3D_PT_arranjar_numa_curva"
35 bl_space_type = "VIEW_3D"
36 bl_region_type = "TOOLS"
37 bl_context = "objectmode"
38 bl_category = "Create"
39 bl_label = "Arrange on Curve"
40 bl_options = {'DEFAULT_CLOSED'}
42 @classmethod
43 def poll(cls, context):
44 return context.object and context.mode == 'OBJECT' and context.object.type == 'CURVE'
46 def draw(self, context):
47 layout = self.layout
48 adv_obj = context.scene.advanced_objects
50 layout.prop(adv_obj, "arrange_c_use_selected")
52 if not adv_obj.arrange_c_use_selected:
53 layout.prop(adv_obj, "arrange_c_select_type", expand=True)
54 if adv_obj.arrange_c_select_type == 'O':
55 layout.column(align=True).prop_search(
56 adv_obj, "arrange_c_obj_arranjar",
57 bpy.data, "objects"
59 elif adv_obj.arrange_c_select_type == 'G':
60 layout.column(align=True).prop_search(
61 adv_obj, "arrange_c_obj_arranjar",
62 bpy.data, "groups"
64 if context.object.type == 'CURVE':
65 layout.operator("object.arranjar_numa_curva", text="Arrange Objects")
68 class DupliCurve(Operator):
69 bl_idname = "object.arranjar_numa_curva"
70 bl_label = "Arrange Objects along a Curve"
71 bl_description = "Arange chosen / selected objects along the Active Curve"
72 bl_options = {'REGISTER', 'UNDO'}
74 use_distance = EnumProperty(
75 name="Arrangement",
76 items=[
77 ("D", "Distance", "Objects are arranged depending on the distance", 0),
78 ("Q", "Quantity", "Objects are arranged depending on the quantity", 1),
79 ("R", "Range", "Objects are arranged uniformly between the corners", 2)
82 distance = FloatProperty(
83 name="Distance",
84 description="Distance between Objects",
85 default=1.0,
86 min=FLT_MIN,
87 soft_min=0.1,
88 unit='LENGTH',
90 object_qt = IntProperty(
91 name="Quantity",
92 description="Object amount",
93 default=2,
94 min=0,
96 scale = FloatProperty(
97 name="Scale",
98 description="Object Scale",
99 default=1.0,
100 min=FLT_MIN,
101 unit='LENGTH',
103 Yaw = FloatProperty(
104 name="X",
105 description="Rotate around the X axis (Yaw)",
106 default=0.0,
107 unit='ROTATION'
109 Pitch = FloatProperty(
110 default=0.0,
111 description="Rotate around the Y axis (Pitch)",
112 name="Y",
113 unit='ROTATION'
115 Roll = FloatProperty(
116 default=0.0,
117 description="Rotate around the Z axis (Roll)",
118 name="Z",
119 unit='ROTATION'
121 max_angle = FloatProperty(
122 default=1.57079,
123 max=3.141592,
124 name="Angle",
125 unit='ROTATION'
127 offset = FloatProperty(
128 default=0.0,
129 name="Offset",
130 unit='LENGTH'
133 @classmethod
134 def poll(cls, context):
135 return context.mode == 'OBJECT'
137 def draw(self, context):
138 layout = self.layout
139 col = layout.column()
140 col.prop(self, "use_distance", text="")
141 col = layout.column(align=True)
142 if self.use_distance == "D":
143 col.prop(self, "distance")
144 elif self.use_distance == "Q":
145 col.prop(self, "object_qt")
146 else:
147 col.prop(self, "distance")
148 col.prop(self, "max_angle")
149 col.prop(self, "offset")
151 col = layout.column(align=True)
152 col.prop(self, "scale")
153 col.prop(self, "Yaw")
154 col.prop(self, "Pitch")
155 col.prop(self, "Roll")
157 def Glpoints(self, curve):
158 Gpoints = []
159 for i, spline in enumerate(curve.data.splines):
160 segments = len(spline.bezier_points)
161 if segments >= 2:
162 r = spline.resolution_u + 1
164 points = []
165 for j in range(segments):
166 bp1 = spline.bezier_points[j]
167 inext = (j + 1)
168 if inext == segments:
169 if not spline.use_cyclic_u:
170 break
171 inext = 0
172 bp2 = spline.bezier_points[inext]
173 if bp1.handle_right_type == bp2.handle_left_type == 'VECTOR':
174 _points = (bp1.co, bp2.co) if j == 0 else (bp2.co,)
175 else:
176 knot1 = bp1.co
177 handle1 = bp1.handle_right
178 handle2 = bp2.handle_left
179 knot2 = bp2.co
180 _points = mathutils.geometry.interpolate_bezier(knot1, handle1, handle2, knot2, r)
181 points.extend(_points)
182 Gpoints.append(tuple((curve.matrix_world * p for p in points)))
183 elif len(spline.points) >= 2:
184 l = [curve.matrix_world * p.co.xyz for p in spline.points]
185 if spline.use_cyclic_u:
186 l.append(l[0])
187 Gpoints.append(tuple(l))
189 if self.use_distance == "R":
190 max_angle = self.max_angle
191 tmp_Gpoints = []
192 sp = Gpoints[i]
193 sp2 = [sp[0], sp[1]]
194 lp = sp[1]
195 v1 = lp - sp[0]
196 for p in sp[2:]:
197 v2 = p - lp
198 try:
199 if (3.14158 - v1.angle(v2)) < max_angle:
200 tmp_Gpoints.append(tuple(sp2))
201 sp2 = [lp]
202 except Exception as e:
203 print("\n[Add Advanced Objects]\nOperator: "
204 "object.arranjar_numa_curva\nError: {}".format(e))
205 pass
206 sp2.append(p)
207 v1 = v2
208 lp = p
209 tmp_Gpoints.append(tuple(sp2))
210 Gpoints = Gpoints[:i] + tmp_Gpoints
212 lengths = []
213 if self.use_distance != "D":
214 for sp in Gpoints:
215 lp = sp[1]
216 leng = (lp - sp[0]).length
217 for p in sp[2:]:
218 leng += (p - lp).length
219 lp = p
220 lengths.append(leng)
221 return Gpoints, lengths
223 def execute(self, context):
224 if context.object.type != 'CURVE':
225 return {'CANCELLED'}
227 curve = context.active_object
228 Gpoints, lengs = self.Glpoints(curve)
229 adv_obj = context.scene.advanced_objects
231 if adv_obj.arrange_c_use_selected:
232 G_Objeto = context.selected_objects
233 G_Objeto.remove(curve)
235 if not G_Objeto:
236 return {'CANCELLED'}
238 elif adv_obj.arrange_c_select_type == 'O':
239 G_Objeto = bpy.data.objects[adv_obj.arrange_c_obj_arranjar],
240 elif adv_obj.arrange_c_select_type == 'G':
241 G_Objeto = bpy.data.groups[adv_obj.arrange_c_obj_arranjar].objects
243 yawMatrix = mathutils.Matrix.Rotation(self.Yaw, 4, 'X')
244 pitchMatrix = mathutils.Matrix.Rotation(self.Pitch, 4, 'Y')
245 rollMatrix = mathutils.Matrix.Rotation(self.Roll, 4, 'Z')
247 max_angle = self.max_angle # max_angle is called in Glpoints
249 if self.use_distance == "D":
250 dist = self.distance
251 for sp_points in Gpoints:
252 dx = 0.0 # Length of initial calculation of section
253 last_point = sp_points[0]
254 j = 0
255 for point in sp_points[1:]:
256 vetorx = point - last_point # Vector spline section
257 quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z') # Tracking the selected objects
258 quat = quat.to_matrix().to_4x4()
260 v_len = vetorx.length
261 if v_len > 0.0:
262 dx += v_len # Defined length calculation equal total length of the spline section
263 v_norm = vetorx / v_len
264 while dx > dist:
265 object = G_Objeto[j % len(G_Objeto)]
266 j += 1
267 dx -= dist # Calculating the remaining length of the section
268 obj = object.copy()
269 context.scene.objects.link(obj)
270 obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
271 # Placing in the correct position
272 obj.matrix_world.translation = point - v_norm * dx
273 obj.scale *= self.scale
274 last_point = point
276 elif self.use_distance == "Q":
277 object_qt = self.object_qt + 1
278 for i, sp_points in enumerate(Gpoints):
279 dx = 0.0 # Length of initial calculation of section
280 dist = lengs[i] / object_qt
281 last_point = sp_points[0]
282 j = 0
283 for point in sp_points[1:]:
284 vetorx = point - last_point # Vector spline section
285 # Tracking the selected objects
286 quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z')
287 quat = quat.to_matrix().to_4x4()
289 v_len = vetorx.length
290 if v_len > 0.0:
291 # Defined length calculation equal total length of the spline section
292 dx += v_len
293 v_norm = vetorx / v_len
294 while dx > dist:
295 object = G_Objeto[j % len(G_Objeto)]
296 j += 1
297 dx -= dist # Calculating the remaining length of the section
298 obj = object.copy()
299 context.scene.objects.link(obj)
300 obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
301 # Placing in the correct position
302 obj.matrix_world.translation = point - v_norm * dx
303 obj.scale *= self.scale
304 last_point = point
306 else:
307 dist = self.distance
308 offset2 = 2 * self.offset
309 for i, sp_points in enumerate(Gpoints):
310 leng = lengs[i] - offset2
311 rest = leng % dist
312 offset = offset2 + rest
313 leng -= rest
314 offset /= 2
315 last_point = sp_points[0]
317 dx = dist - offset # Length of initial calculation of section
318 j = 0
319 for point in sp_points[1:]:
320 vetorx = point - last_point # Vector spline section
321 # Tracking the selected objects
322 quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z')
323 quat = quat.to_matrix().to_4x4()
325 v_len = vetorx.length
326 if v_len > 0.0:
327 dx += v_len
328 v_norm = vetorx / v_len
329 while dx >= dist and leng >= 0.0:
330 leng -= dist
331 dx -= dist # Calculating the remaining length of the section
332 object = G_Objeto[j % len(G_Objeto)]
333 j += 1
334 obj = object.copy()
335 context.scene.objects.link(obj)
336 obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
337 # Placing in the correct position
338 obj.matrix_world.translation = point - v_norm * dx
339 obj.scale *= self.scale
340 last_point = point
342 return {"FINISHED"}
345 def register():
346 bpy.utils.register_class(PanelDupliCurve)
347 bpy.utils.register_class(DupliCurve)
350 def unregister():
351 bpy.utils.unregister_class(PanelDupliCurve)
352 bpy.utils.unregister_class(DupliCurve)
355 if __name__ == "__main__":
356 register()