Fix incorrect identity comparisons in power_sequencer
[blender-addons.git] / measureit / measureit_geometry.py
blob1f314f7f2e66401c26ea321e7407e8acbbf2864d
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8 compliant>
21 # ----------------------------------------------------------
22 # support routines for OpenGL
23 # Author: Antonio Vazquez (antonioya)
25 # ----------------------------------------------------------
26 # noinspection PyUnresolvedReferences
27 import bpy
28 # noinspection PyUnresolvedReferences
29 import blf
30 from blf import ROTATION
31 from math import fabs, degrees, radians, sqrt, cos, sin, pi
32 from mathutils import Vector, Matrix
33 from bmesh import from_edit_mesh
34 from bpy_extras import view3d_utils, mesh_utils
35 import bpy_extras.object_utils as object_utils
36 from sys import exc_info
37 # GPU
38 import bgl
39 import gpu
40 from gpu_extras.batch import batch_for_shader
42 shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') if not bpy.app.background else None
44 # -------------------------------------------------------------
45 # Draw segments
47 # -------------------------------------------------------------
48 # noinspection PyUnresolvedReferences,PyUnboundLocalVariable
49 def draw_segments(context, myobj, op, region, rv3d):
50 if op.measureit_num > 0:
51 a_code = "\u00b0" # degree
52 scale = bpy.context.scene.unit_settings.scale_length
53 scene = bpy.context.scene
54 pr = scene.measureit_gl_precision
55 fmt = "%1." + str(pr) + "f"
56 ovr = scene.measureit_ovr
57 ovrcolor = scene.measureit_ovr_color
58 ovrfsize = scene.measureit_ovr_font
59 ovrfang = get_angle_in_rad(scene.measureit_ovr_font_rotation)
60 ovrfaln = scene.measureit_ovr_font_align
61 ovrline = scene.measureit_ovr_width
62 units = scene.measureit_units
63 fang = get_angle_in_rad(scene.measureit_font_rotation)
65 # --------------------
66 # Scene Scale
67 # --------------------
68 if scene.measureit_scale is True:
69 prs = scene.measureit_scale_precision
70 fmts = "%1." + str(prs) + "f"
71 pos_2d = get_scale_txt_location(context)
72 tx_dsp = fmts % scene.measureit_scale_factor
73 tx_scale = scene.measureit_gl_scaletxt + " 1:" + tx_dsp
74 draw_text(myobj, pos_2d,
75 tx_scale, scene.measureit_scale_color, scene.measureit_scale_font,
76 text_rot=fang)
78 # --------------------
79 # Loop
80 # --------------------
81 for idx in range(op.measureit_num):
82 ms = op.measureit_segments[idx]
83 if ovr is False:
84 fsize = ms.glfont_size
85 fang = get_angle_in_rad(ms.glfont_rotat)
86 faln = ms.glfont_align
87 else:
88 fsize = ovrfsize
89 fang = ovrfang
90 faln = ovrfaln
91 # ------------------------------
92 # only active and visible
93 # ------------------------------
94 if ms.glview is True and ms.glfree is False:
95 # Arrow data
96 a_size = ms.glarrow_s
97 a_type = ms.glarrow_a
98 b_type = ms.glarrow_b
99 # noinspection PyBroadException
100 try:
101 if ovr is False:
102 rgba = ms.glcolor
103 else:
104 rgba = ovrcolor
106 # ----------------------
107 # Segment or Label
108 # ----------------------
109 if ms.gltype == 1 or ms.gltype == 2:
110 obverts = get_mesh_vertices(myobj)
112 if ms.glpointa <= len(obverts) and ms.glpointb <= len(obverts):
113 a_p1 = get_point(obverts[ms.glpointa].co, myobj)
114 b_p1 = get_point(obverts[ms.glpointb].co, myobj)
116 # ----------------------
117 # Segment or Label
118 # ----------------------
119 if ms.gltype == 12 or ms.gltype == 13 or ms.gltype == 14:
120 obverts = get_mesh_vertices(myobj)
121 if ms.glpointa <= len(obverts):
122 a_p1 = get_point(obverts[ms.glpointa].co, myobj)
123 if ms.gltype == 12: # X
124 b_p1 = get_point((0.0,
125 obverts[ms.glpointa].co[1],
126 obverts[ms.glpointa].co[2]), myobj)
127 elif ms.gltype == 13: # Y
128 b_p1 = get_point((obverts[ms.glpointa].co[0],
129 0.0,
130 obverts[ms.glpointa].co[2]), myobj)
131 else: # Z
132 b_p1 = get_point((obverts[ms.glpointa].co[0],
133 obverts[ms.glpointa].co[1],
134 0.0), myobj)
135 # ----------------------
136 # Vertex to Vertex (link)
137 # ----------------------
138 if ms.gltype == 3:
139 obverts = get_mesh_vertices(myobj)
140 linkverts = bpy.data.objects[ms.gllink].data.vertices
141 a_p1 = get_point(obverts[ms.glpointa].co, myobj)
142 b_p1 = get_point(linkverts[ms.glpointb].co, bpy.data.objects[ms.gllink])
143 # ----------------------
144 # Vertex to Object (link)
145 # ----------------------
146 if ms.gltype == 4:
147 obverts = get_mesh_vertices(myobj)
148 a_p1 = get_point(obverts[ms.glpointa].co, myobj)
149 b_p1 = get_location(bpy.data.objects[ms.gllink])
150 # ----------------------
151 # Object to Vertex (link)
152 # ----------------------
153 if ms.gltype == 5:
154 linkverts = bpy.data.objects[ms.gllink].data.vertices
155 a_p1 = get_location(myobj)
156 b_p1 = get_point(linkverts[ms.glpointb].co, bpy.data.objects[ms.gllink])
157 # ----------------------
158 # Object to Object (link)
159 # ----------------------
160 if ms.gltype == 8:
161 a_p1 = get_location(myobj)
162 b_p1 = get_location(bpy.data.objects[ms.gllink])
163 # ----------------------
164 # Vertex to origin
165 # ----------------------
166 if ms.gltype == 6:
167 obverts = get_mesh_vertices(myobj)
168 a_p1 = (0, 0, 0)
169 b_p1 = get_point(obverts[ms.glpointa].co, myobj)
170 # ----------------------
171 # Object to origin
172 # ----------------------
173 if ms.gltype == 7:
174 a_p1 = (0, 0, 0)
175 b_p1 = get_location(myobj)
176 # ----------------------
177 # Angle
178 # ----------------------
179 if ms.gltype == 9:
180 obverts = get_mesh_vertices(myobj)
181 if ms.glpointa <= len(obverts) and ms.glpointb <= len(obverts) and ms.glpointc <= len(obverts):
182 an_p1 = get_point(obverts[ms.glpointa].co, myobj)
183 an_p2 = get_point(obverts[ms.glpointb].co, myobj)
184 an_p3 = get_point(obverts[ms.glpointc].co, myobj)
186 ang_1 = Vector((an_p1[0] - an_p2[0], an_p1[1] - an_p2[1], an_p1[2] - an_p2[2]))
187 ang_2 = Vector((an_p3[0] - an_p2[0], an_p3[1] - an_p2[1], an_p3[2] - an_p2[2]))
189 ang_3 = ang_1 + ang_2 # Result vector
191 a_p1 = (an_p2[0], an_p2[1], an_p2[2])
192 b_p1 = (0, 0, 0)
193 # ----------------------
194 # Annotation
195 # ----------------------
196 if ms.gltype == 10:
197 a_p1 = get_location(myobj)
198 b_p1 = get_location(myobj)
200 # ----------------------
201 # Arc
202 # ----------------------
203 if ms.gltype == 11:
204 obverts = get_mesh_vertices(myobj)
205 if ms.glpointa <= len(obverts) and ms.glpointb <= len(obverts) and ms.glpointc <= len(obverts):
206 an_p1 = get_point(obverts[ms.glpointa].co, myobj)
207 an_p2 = get_point(obverts[ms.glpointb].co, myobj)
208 an_p3 = get_point(obverts[ms.glpointc].co, myobj)
209 # reference for maths: http://en.wikipedia.org/wiki/Circumscribed_circle
210 an_p12 = Vector((an_p1[0] - an_p2[0], an_p1[1] - an_p2[1], an_p1[2] - an_p2[2]))
211 an_p13 = Vector((an_p1[0] - an_p3[0], an_p1[1] - an_p3[1], an_p1[2] - an_p3[2]))
212 an_p21 = Vector((an_p2[0] - an_p1[0], an_p2[1] - an_p1[1], an_p2[2] - an_p1[2]))
213 an_p23 = Vector((an_p2[0] - an_p3[0], an_p2[1] - an_p3[1], an_p2[2] - an_p3[2]))
214 an_p31 = Vector((an_p3[0] - an_p1[0], an_p3[1] - an_p1[1], an_p3[2] - an_p1[2]))
215 an_p32 = Vector((an_p3[0] - an_p2[0], an_p3[1] - an_p2[1], an_p3[2] - an_p2[2]))
216 an_p12xp23 = an_p12.copy().cross(an_p23)
218 # radius = an_p12.length * an_p23.length * an_p31.length / (2 * an_p12xp23.length)
220 alpha = pow(an_p23.length, 2) * an_p12.dot(an_p13) / (2 * pow(an_p12xp23.length, 2))
221 beta = pow(an_p13.length, 2) * an_p21.dot(an_p23) / (2 * pow(an_p12xp23.length, 2))
222 gamma = pow(an_p12.length, 2) * an_p31.dot(an_p32) / (2 * pow(an_p12xp23.length, 2))
224 a_p1 = (alpha * an_p1[0] + beta * an_p2[0] + gamma * an_p3[0],
225 alpha * an_p1[1] + beta * an_p2[1] + gamma * an_p3[1],
226 alpha * an_p1[2] + beta * an_p2[2] + gamma * an_p3[2])
228 b_p1 = (an_p2[0], an_p2[1], an_p2[2])
229 a_n = an_p12.cross(an_p23)
230 a_n.normalize() # normal vector
231 arc_angle, arc_length = get_arc_data(an_p1, a_p1, an_p2, an_p3)
232 # Apply scale to arc_length
233 arc_length *= scene.measureit_scale_factor
235 # ----------------------
236 # Area
237 # ----------------------
238 if ms.gltype == 20:
239 a_p1 = get_location(myobj) # Not used
240 b_p1 = get_location(myobj) # Not used
242 # Calculate distance
243 dist, distloc = distance(a_p1, b_p1, ms.glocx, ms.glocy, ms.glocz)
244 # ------------------------------------
245 # get normal vector
246 # ------------------------------------
247 if ms.gldefault is True:
248 if ms.gltype == 9:
249 vn = ang_3 # if angle, vector is angle position
250 elif ms.gltype == 11:
251 vn = a_n # if arc, vector is perpendicular to surface of the three vertices
252 else:
253 loc = get_location(myobj)
254 midpoint3d = interpolate3d(a_p1, b_p1, fabs(dist / 2))
255 vn = Vector((midpoint3d[0] - loc[0],
256 midpoint3d[1] - loc[1],
257 midpoint3d[2] - loc[2]))
258 else:
259 vn = Vector((ms.glnormalx, ms.glnormaly, ms.glnormalz))
261 vn.normalize()
262 # ------------------------------------
263 # position vector
264 # ------------------------------------
265 vi = vn * ms.glspace
266 s = (14 / 200)
267 if s > ms.glspace:
268 s = ms.glspace / 5
269 vi2 = vn * (ms.glspace + s)
270 # ------------------------------------
271 # apply vector
272 # ------------------------------------
273 v1 = [a_p1[0] + vi[0], a_p1[1] + vi[1], a_p1[2] + vi[2]]
274 v2 = [b_p1[0] + vi[0], b_p1[1] + vi[1], b_p1[2] + vi[2]]
276 # Segment extreme
277 v11 = [a_p1[0] + vi2[0], a_p1[1] + vi2[1], a_p1[2] + vi2[2]]
278 v22 = [b_p1[0] + vi2[0], b_p1[1] + vi2[1], b_p1[2] + vi2[2]]
280 # Labeling
281 v11a = (a_p1[0] + vi2[0], a_p1[1] + vi2[1], a_p1[2] + vi2[2] + s / 30)
282 v11b = (a_p1[0] + vi2[0], a_p1[1] + vi2[1], a_p1[2] + vi2[2] - s / 40)
284 # Annotation
285 vn1 = (a_p1[0], a_p1[1], a_p1[2])
287 # -------------------------------------------
288 # Orthogonal
289 # -------------------------------------------
290 if ms.gltype == 1 and ms.glorto != "99":
291 if ms.glorto == "0": # A
292 if ms.glorto_x is True:
293 v1[0] = v2[0]
294 v11[0] = v22[0]
295 if ms.glorto_y is True:
296 v1[1] = v2[1]
297 v11[1] = v22[1]
298 if ms.glorto_z is True:
299 v1[2] = v2[2]
300 v11[2] = v22[2]
302 if ms.glorto == "1": # B
303 if ms.glorto_x is True:
304 v2[0] = v1[0]
305 v22[0] = v11[0]
306 if ms.glorto_y is True:
307 v2[1] = v1[1]
308 v22[1] = v11[1]
309 if ms.glorto_z is True:
310 v2[2] = v1[2]
311 v22[2] = v11[2]
313 # ------------------------------------
314 # converting to screen coordinates
315 # ------------------------------------
316 screen_point_ap1 = get_2d_point(region, rv3d, a_p1)
317 screen_point_bp1 = get_2d_point(region, rv3d, b_p1)
319 screen_point_v1 = get_2d_point(region, rv3d, v1)
320 screen_point_v2 = get_2d_point(region, rv3d, v2)
321 screen_point_v11 = get_2d_point(region, rv3d, v11)
322 screen_point_v22 = get_2d_point(region, rv3d, v22)
323 screen_point_v11a = get_2d_point(region, rv3d, v11a)
324 screen_point_v11b = get_2d_point(region, rv3d, v11b)
326 # ------------------------------------
327 # colour + line setup
328 # ------------------------------------
329 if ovr is False:
330 bgl.glLineWidth(ms.glwidth)
331 else:
332 bgl.glLineWidth(ovrline)
334 # ------------------------------------
335 # Text (distance)
336 # ------------------------------------
337 # noinspection PyBroadException
338 if ms.gltype != 2 and ms.gltype != 9 and ms.gltype != 10 and ms.gltype != 11 and ms.gltype != 20:
339 # noinspection PyBroadException
340 try:
341 midpoint3d = interpolate3d(v1, v2, fabs(dist / 2))
342 gap3d = (midpoint3d[0], midpoint3d[1], midpoint3d[2] + s / 2)
343 tmp_point = get_2d_point(region, rv3d, gap3d)
344 if tmp_point is None:
345 pass
346 txtpoint2d = tmp_point[0] + ms.glfontx, tmp_point[1] + ms.glfonty
347 # Scale
348 if scene.measureit_scale is True:
349 dist = dist * scene.measureit_scale_factor
350 distloc = distloc * scene.measureit_scale_factor
352 # decide dist to use
353 if dist == distloc:
354 locflag = False
355 usedist = dist
356 else:
357 usedist = distloc
358 locflag = True
359 # Apply scene scale
360 usedist *= scale
361 tx_dist = str(format_distance(fmt, units, usedist))
362 # -----------------------------------
363 # Draw text
364 # -----------------------------------
365 if scene.measureit_gl_show_d is True and ms.gldist is True:
366 msg = tx_dist + " "
367 else:
368 msg = " "
369 if scene.measureit_gl_show_n is True and ms.glnames is True:
370 msg += ms.gltxt
371 if scene.measureit_gl_show_d is True or scene.measureit_gl_show_n is True:
372 draw_text(myobj, txtpoint2d, msg, rgba, fsize, faln, fang)
374 # ------------------------------
375 # if axis loc, show a indicator
376 # ------------------------------
377 if locflag is True and ms.glocwarning is True:
378 txtpoint2d = get_2d_point(region, rv3d, (v2[0], v2[1], v2[2]))
379 txt = "["
380 if ms.glocx is True:
381 txt += "X"
382 if ms.glocy is True:
383 txt += "Y"
384 if ms.glocz is True:
385 txt += "Z"
386 txt += "]"
387 draw_text(myobj, txtpoint2d, txt, rgba, fsize - 1, text_rot=fang)
389 except:
390 pass
391 # ------------------------------------
392 # Text (label) and Angles
393 # ------------------------------------
394 # noinspection PyBroadException
395 if ms.gltype == 2 or ms.gltype == 9 or ms.gltype == 11:
396 tx_dist = ""
397 # noinspection PyBroadException
398 try:
399 if ms.gltype == 2:
400 tx_dist = ms.gltxt
401 if ms.gltype == 9: # Angles
402 ang = ang_1.angle(ang_2)
403 if bpy.context.scene.unit_settings.system_rotation == "DEGREES":
404 ang = degrees(ang)
406 tx_dist = " " + fmt % ang
407 # Add degree symbol
408 if bpy.context.scene.unit_settings.system_rotation == "DEGREES":
409 tx_dist += a_code
411 if scene.measureit_gl_show_n is True:
412 tx_dist += " " + ms.gltxt
413 if ms.gltype == 11: # arc
414 # print length or arc and angle
415 if ms.glarc_len is True:
416 tx_dist = ms.glarc_txlen + format_distance(fmt, units, arc_length)
417 else:
418 tx_dist = " "
420 if bpy.context.scene.unit_settings.system_rotation == "DEGREES":
421 arc_d = degrees(arc_angle)
422 else:
423 arc_d = arc_angle
425 if ms.glarc_ang is True:
426 tx_dist += " " + ms.glarc_txang + format_distance(fmt, 9, arc_d)
427 # Add degree symbol
428 if bpy.context.scene.unit_settings.system_rotation == "DEGREES":
429 tx_dist += a_code
431 if scene.measureit_gl_show_d is True and ms.gldist is True:
432 msg = tx_dist + " "
433 else:
434 msg = " "
436 if scene.measureit_gl_show_n is True and ms.glnames is True:
437 msg += ms.gltxt
439 if scene.measureit_gl_show_d is True or scene.measureit_gl_show_n is True:
440 # Normal vector
441 vna = Vector((b_p1[0] - a_p1[0],
442 b_p1[1] - a_p1[1],
443 b_p1[2] - a_p1[2]))
444 vna.normalize()
445 via = vna * ms.glspace
447 gap3d = (b_p1[0] + via[0], b_p1[1] + via[1], b_p1[2] + via[2])
448 tmp_point = get_2d_point(region, rv3d, gap3d)
449 if tmp_point is not None:
450 txtpoint2d = tmp_point[0] + ms.glfontx, tmp_point[1] + ms.glfonty
451 draw_text(myobj, txtpoint2d, msg, rgba, fsize, faln, fang)
452 # Radius
453 if scene.measureit_gl_show_d is True and ms.gldist is True and \
454 ms.glarc_rad is True:
455 tx_dist = ms.glarc_txradio + format_distance(fmt, units,
456 dist * scene.measureit_scale_factor)
457 else:
458 tx_dist = " "
459 if ms.gltype == 2:
460 gap3d = (v11a[0], v11a[1], v11a[2])
461 else:
462 gap3d = (a_p1[0], a_p1[1], a_p1[2])
464 tmp_point = get_2d_point(region, rv3d, gap3d)
465 if tmp_point is not None:
466 txtpoint2d = tmp_point[0] + ms.glfontx, tmp_point[1] + ms.glfonty
467 draw_text(myobj, txtpoint2d, tx_dist, rgba, fsize, faln, fang)
468 except:
469 pass
470 # ------------------------------------
471 # Annotation
472 # ------------------------------------
473 # noinspection PyBroadException
474 if ms.gltype == 10:
475 # noinspection PyBroadException
476 tx_dist = ms.gltxt
477 gap3d = (vn1[0], vn1[1], vn1[2])
478 tmp_point = get_2d_point(region, rv3d, gap3d)
479 if tmp_point is not None:
480 txtpoint2d = tmp_point[0] + ms.glfontx, tmp_point[1] + ms.glfonty
481 draw_text(myobj, txtpoint2d, tx_dist, rgba, fsize, faln, fang)
483 # ------------------------------------
484 # Draw lines
485 # ------------------------------------
486 bgl.glEnable(bgl.GL_BLEND)
488 if ms.gltype == 1: # Segment
489 draw_line(screen_point_ap1, screen_point_v11, rgba)
490 draw_line(screen_point_bp1, screen_point_v22, rgba)
491 draw_arrow(screen_point_v1, screen_point_v2, rgba, a_size, a_type, b_type)
493 if ms.gltype == 12 or ms.gltype == 13 or ms.gltype == 14: # Segment to origin
494 draw_line(screen_point_ap1, screen_point_v11, rgba)
495 draw_line(screen_point_bp1, screen_point_v22, rgba)
496 draw_arrow(screen_point_v1, screen_point_v2, rgba, a_size, a_type, b_type)
498 if ms.gltype == 2: # Label
499 draw_line(screen_point_v11a, screen_point_v11b, rgba)
500 draw_arrow(screen_point_ap1, screen_point_v11, rgba, a_size, a_type, b_type)
502 if ms.gltype == 3 or ms.gltype == 4 or ms.gltype == 5 or ms.gltype == 8 \
503 or ms.gltype == 6 or ms.gltype == 7: # Origin and Links
504 draw_arrow(screen_point_ap1, screen_point_bp1, rgba, a_size, a_type, b_type)
506 if ms.gltype == 9: # Angle
507 dist, distloc = distance(an_p1, an_p2)
508 mp1 = interpolate3d(an_p1, an_p2, fabs(dist / 1.1))
510 dist, distloc = distance(an_p3, an_p2)
511 mp2 = interpolate3d(an_p3, an_p2, fabs(dist / 1.1))
513 screen_point_an_p1 = get_2d_point(region, rv3d, mp1)
514 screen_point_an_p2 = get_2d_point(region, rv3d, an_p2)
515 screen_point_an_p3 = get_2d_point(region, rv3d, mp2)
517 draw_line(screen_point_an_p1, screen_point_an_p2, rgba)
518 draw_line(screen_point_an_p2, screen_point_an_p3, rgba)
519 draw_line(screen_point_an_p1, screen_point_an_p3, rgba)
521 if ms.gltype == 11: # arc
522 # draw line from center of arc second point
523 c = Vector(a_p1)
524 if ms.glarc_rad is True:
525 if ms.glarc_extrad is False:
526 draw_arrow(screen_point_ap1, screen_point_bp1, rgba, a_size, a_type, b_type)
527 else:
528 vne = Vector((b_p1[0] - a_p1[0],
529 b_p1[1] - a_p1[1],
530 b_p1[2] - a_p1[2]))
531 vne.normalize()
532 vie = vne * ms.glspace
533 pe = (b_p1[0] + vie[0], b_p1[1] + vie[1], b_p1[2] + vie[2])
534 screen_point_pe = get_2d_point(region, rv3d, pe)
535 draw_arrow(screen_point_ap1, screen_point_pe, rgba, a_size, a_type, b_type)
537 # create arc around the centerpoint
538 # rotation matrix around normal vector at center point
539 mat_trans1 = Matrix.Translation(-c)
540 # get step
541 n_step = 36.0
542 if ms.glarc_full is False:
543 step = arc_angle / n_step
544 else:
545 step = radians(360.0) / n_step
547 mat_rot1 = Matrix.Rotation(step, 4, vn)
548 mat_trans2 = Matrix.Translation(c)
549 p1 = Vector(an_p1) # first point of arc
550 # Normal vector
551 vn = Vector((p1[0] - a_p1[0],
552 p1[1] - a_p1[1],
553 p1[2] - a_p1[2]))
554 vn.normalize()
555 vi = vn * ms.glspace
557 p_01a = None
558 p_01b = None
559 p_02a = None
560 p_02b = None
561 # draw the arc
562 for i in range(int(n_step)):
563 p2 = mat_trans2 @ mat_rot1 @ mat_trans1 @ p1
564 p1_ = (p1[0] + vi[0], p1[1] + vi[1], p1[2] + vi[2])
565 # First Point
566 if i == 0:
567 p_01a = (p1_[0], p1_[1], p1_[2])
568 p_01b = (p1[0], p1[1], p1[2])
570 # Normal vector
571 vn = Vector((p2[0] - a_p1[0],
572 p2[1] - a_p1[1],
573 p2[2] - a_p1[2]))
574 vn.normalize()
575 vi = vn * ms.glspace
577 p2_ = (p2[0] + vi[0], p2[1] + vi[1], p2[2] + vi[2])
578 # convert to screen coordinates
579 screen_point_p1 = get_2d_point(region, rv3d, p1_)
580 screen_point_p2 = get_2d_point(region, rv3d, p2_)
581 if i == 0:
582 draw_arrow(screen_point_p1, screen_point_p2, rgba, ms.glarc_s, ms.glarc_a, "99")
583 elif i == int(n_step) - 1:
584 draw_arrow(screen_point_p1, screen_point_p2, rgba, ms.glarc_s, "99", ms.glarc_b)
585 else:
586 draw_line(screen_point_p1, screen_point_p2, rgba)
588 p1 = p2.copy()
590 # Last Point
591 if i == int(n_step) - 1:
592 p_02a = (p2_[0], p2_[1], p2_[2])
593 p_02b = (p2[0], p2[1], p2[2])
595 # Draw close lines
596 if ms.glarc_full is False:
597 screen_point_p1a = get_2d_point(region, rv3d, p_01a)
598 screen_point_p1b = get_2d_point(region, rv3d, p_01b)
599 screen_point_p2a = get_2d_point(region, rv3d, p_02a)
600 screen_point_p2b = get_2d_point(region, rv3d, p_02b)
602 draw_line(screen_point_p1a, screen_point_p1b, rgba)
603 draw_line(screen_point_p2a, screen_point_p2b, rgba)
605 if ms.gltype == 20: # Area
606 obverts = get_mesh_vertices(myobj)
607 tot = 0
608 if scene.measureit_scale is True:
609 ms_scale = scene.measureit_scale_factor
610 else:
611 ms_scale = 1.0
613 for face in ms.measureit_faces:
614 myvertices = []
615 for v in face.measureit_index:
616 myvertices.append(v.glidx)
618 area = get_area_and_paint(myvertices, myobj, obverts, region, rv3d, rgba, ms_scale)
619 tot += area
620 # Draw Area number over first face
621 if len(ms.measureit_faces) > 0:
622 face = ms.measureit_faces[0]
623 a = face.measureit_index[0].glidx
624 b = face.measureit_index[2].glidx
626 p1 = get_point(obverts[a].co, myobj)
627 p2 = get_point(obverts[b].co, myobj)
629 d1, dn = distance(p1, p2)
630 midpoint3d = interpolate3d(p1, p2, fabs(d1 / 2))
632 # mult by world scale
633 tot *= scale
634 tx_dist = str(format_distance(fmt, units, tot, 2))
635 # -----------------------------------
636 # Draw text
637 # -----------------------------------
638 if scene.measureit_gl_show_d is True and ms.gldist is True:
639 msg = tx_dist + " "
640 else:
641 msg = " "
642 if scene.measureit_gl_show_n is True and ms.glnames is True:
643 msg += ms.gltxt
644 if scene.measureit_gl_show_d is True or scene.measureit_gl_show_n is True:
645 tmp_point = get_2d_point(region, rv3d, midpoint3d)
646 if tmp_point is not None:
647 txtpoint2d = tmp_point[0] + ms.glfontx, tmp_point[1] + ms.glfonty
648 # todo: swap ms.glcolorarea with ms.glcolor ?
649 draw_text(myobj, txtpoint2d, msg, ms.glcolorarea, fsize, faln, fang)
651 except IndexError:
652 ms.glfree = True
653 except:
654 print("Unexpected error:" + str(exc_info()))
655 pass
657 return
660 # ------------------------------------------
661 # Get polygon area and paint area
663 # ------------------------------------------
664 def get_area_and_paint(myvertices, myobj, obverts, region, rv3d, rgba, ms_scale):
665 mymesh = myobj.data
666 totarea = 0
667 if len(myvertices) > 3:
668 # Tessellate the polygon
669 if myobj.mode != 'EDIT':
670 tris = mesh_utils.ngon_tessellate(mymesh, myvertices)
671 else:
672 bm = from_edit_mesh(myobj.data)
673 myv = []
674 for v in bm.verts:
675 myv.append(v.co)
676 tris = mesh_utils.ngon_tessellate(myv, myvertices)
678 for t in tris:
679 v1, v2, v3 = t
680 p1 = get_point(obverts[myvertices[v1]].co, myobj)
681 p2 = get_point(obverts[myvertices[v2]].co, myobj)
682 p3 = get_point(obverts[myvertices[v3]].co, myobj)
684 screen_point_p1 = get_2d_point(region, rv3d, p1)
685 screen_point_p2 = get_2d_point(region, rv3d, p2)
686 screen_point_p3 = get_2d_point(region, rv3d, p3)
688 draw_triangle(screen_point_p1, screen_point_p2, screen_point_p3, rgba)
690 # Area
692 area = get_triangle_area(p1, p2, p3, ms_scale)
694 totarea += area
695 elif len(myvertices) == 3:
696 v1, v2, v3 = myvertices
697 p1 = get_point(obverts[v1].co, myobj)
698 p2 = get_point(obverts[v2].co, myobj)
699 p3 = get_point(obverts[v3].co, myobj)
701 screen_point_p1 = get_2d_point(region, rv3d, p1)
702 screen_point_p2 = get_2d_point(region, rv3d, p2)
703 screen_point_p3 = get_2d_point(region, rv3d, p3)
704 draw_triangle(screen_point_p1, screen_point_p2, screen_point_p3, rgba)
706 # Area
707 area = get_triangle_area(p1, p2, p3, ms_scale)
708 totarea += area
709 else:
710 return 0.0
712 # Apply world scale
713 totarea *= bpy.context.scene.unit_settings.scale_length
715 return totarea
718 # ------------------------------------------
719 # Get area using Heron formula
721 # ------------------------------------------
722 def get_triangle_area(p1, p2, p3, scale=1.0):
723 d1, dn = distance(p1, p2)
724 d2, dn = distance(p2, p3)
725 d3, dn = distance(p1, p3)
727 d1 *= scale
728 d2 *= scale
729 d3 *= scale
731 per = (d1 + d2 + d3) / 2.0
732 area = sqrt(per * (per - d1) * (per - d2) * (per - d3))
733 return area
736 # ------------------------------------------
737 # Get point in 2d space
739 # ------------------------------------------
740 def get_2d_point(region, rv3d, point3d):
741 if rv3d is not None and region is not None:
742 return view3d_utils.location_3d_to_region_2d(region, rv3d, point3d)
743 else:
744 return get_render_location(point3d)
747 # -------------------------------------------------------------
748 # Get sum of a group
750 # myobj: Current object
751 # Tag: group
752 # -------------------------------------------------------------
753 def get_group_sum(myobj, tag):
754 # noinspection PyBroadException
755 try:
756 tx = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
757 "T", "U", "V", "W", "X", "Y", "Z"]
758 g = tag[2:3]
759 mp = myobj.MeasureGenerator[0]
760 flag = False
761 # -----------------
762 # Sum loop segments
763 # -----------------
764 scale = bpy.context.scene.unit_settings.scale_length
765 tot = 0.0
766 obverts = get_mesh_vertices(myobj)
767 for idx in range(mp.measureit_num):
768 ms = mp.measureit_segments[idx]
769 if (ms.gltype == 1 or ms.gltype == 12 or
770 ms.gltype == 13 or ms.gltype == 14) and ms.gltot != '99' \
771 and ms.glfree is False and g == tx[int(ms.gltot)]: # only segments
772 if ms.glpointa <= len(obverts) and ms.glpointb <= len(obverts):
773 p1 = get_point(obverts[ms.glpointa].co, myobj)
774 if ms.gltype == 1:
775 p2 = get_point(obverts[ms.glpointb].co, myobj)
776 elif ms.gltype == 12:
777 p2 = get_point((0.0,
778 obverts[ms.glpointa].co[1],
779 obverts[ms.glpointa].co[2]), myobj)
780 elif ms.gltype == 13:
781 p2 = get_point((obverts[ms.glpointa].co[0],
782 0.0,
783 obverts[ms.glpointa].co[2]), myobj)
784 else:
785 p2 = get_point((obverts[ms.glpointa].co[0],
786 obverts[ms.glpointa].co[1],
787 0.0), myobj)
789 dist, distloc = distance(p1, p2, ms.glocx, ms.glocy, ms.glocz)
790 if dist == distloc:
791 usedist = dist
792 else:
793 usedist = distloc
794 usedist *= scale
795 tot += usedist
796 flag = True
798 if flag is True:
799 # Return value
800 pr = bpy.context.scene.measureit_gl_precision
801 fmt = "%1." + str(pr) + "f"
802 units = bpy.context.scene.measureit_units
804 return format_distance(fmt, units, tot)
805 else:
806 return " "
807 except:
808 return " "
811 # -------------------------------------------------------------
812 # Create OpenGL text
814 # -------------------------------------------------------------
815 def draw_text(myobj, pos2d, display_text, rgba, fsize, align='L', text_rot=0.0):
816 if pos2d is None:
817 return
819 # dpi = bpy.context.preferences.system.dpi
820 gap = 12
821 x_pos, y_pos = pos2d
822 font_id = 0
823 ui_scale = bpy.context.preferences.system.ui_scale
824 blf.size(font_id, round(fsize * ui_scale), 72)
825 # blf.size(font_id, fsize, dpi)
826 # height of one line
827 mwidth, mheight = blf.dimensions(font_id, "Tp") # uses high/low letters
829 # Calculate sum groups
830 m = 0
831 while "<#" in display_text:
832 m += 1
833 if m > 10: # limit loop
834 break
835 i = display_text.index("<#")
836 tag = display_text[i:i + 4]
837 display_text = display_text.replace(tag, get_group_sum(myobj, tag.upper()))
839 # split lines
840 mylines = display_text.split("|")
841 idx = len(mylines) - 1
842 maxwidth = 0
843 maxheight = len(mylines) * mheight
844 # -------------------
845 # Draw all lines
846 # -------------------
847 for line in mylines:
848 text_width, text_height = blf.dimensions(font_id, line)
849 if align == 'C':
850 newx = x_pos - text_width / 2
851 elif align == 'R':
852 newx = x_pos - text_width - gap
853 else:
854 newx = x_pos
855 blf.enable(font_id, ROTATION)
856 blf.rotation(font_id, text_rot)
857 # calculate new Y position
858 new_y = y_pos + (mheight * idx)
859 # Draw
860 blf.position(font_id, newx, new_y, 0)
861 blf.color(font_id, rgba[0], rgba[1], rgba[2], rgba[3])
862 blf.draw(font_id, " " + line)
863 # sub line
864 idx -= 1
865 # saves max width
866 if maxwidth < text_width:
867 maxwidth = text_width
869 if align == 'L':
870 blf.disable(font_id, ROTATION)
872 return maxwidth, maxheight
875 # -------------------------------------------------------------
876 # Draw an OpenGL line
878 # -------------------------------------------------------------
879 def draw_line(v1, v2, rgba):
880 coords = [(v1[0], v1[1]), (v2[0], v2[1])]
881 batch = batch_for_shader(shader, 'LINES', {"pos": coords})
883 # noinspection PyBroadException
884 try:
885 if v1 is not None and v2 is not None:
886 shader.bind()
887 shader.uniform_float("color", rgba)
888 batch.draw(shader)
889 except:
890 pass
893 # -------------------------------------------------------------
894 # Draw an OpenGL triangle
896 # -------------------------------------------------------------
897 def draw_triangle(v1, v2, v3, rgba):
898 coords = [(v1[0], v1[1]), (v2[0], v2[1]), (v3[0], v3[1])]
899 batch = batch_for_shader(shader, 'TRIS', {"pos": coords})
901 # noinspection PyBroadException
902 try:
903 if v1 is not None and v2 is not None and v3 is not None:
904 shader.bind()
905 shader.uniform_float("color", rgba)
906 batch.draw(shader)
907 except:
908 pass
911 # -------------------------------------------------------------
912 # Draw an Arrow
914 # -------------------------------------------------------------
915 def draw_arrow(v1, v2, rgba, size=20, a_typ="1", b_typ="1"):
916 if v1 is None or v2 is None:
917 return
919 rad45 = radians(45)
920 rad315 = radians(315)
921 rad90 = radians(90)
922 rad270 = radians(270)
924 v = interpolate3d((v1[0], v1[1], 0.0), (v2[0], v2[1], 0.0), size)
926 v1i = (v[0] - v1[0], v[1] - v1[1])
928 v = interpolate3d((v2[0], v2[1], 0.0), (v1[0], v1[1], 0.0), size)
929 v2i = (v[0] - v2[0], v[1] - v2[1])
931 # Point A
932 if a_typ == "3":
933 rad_a = rad90
934 rad_b = rad270
935 else:
936 rad_a = rad45
937 rad_b = rad315
939 v1a = (int(v1i[0] * cos(rad_a) - v1i[1] * sin(rad_a) + v1[0]),
940 int(v1i[1] * cos(rad_a) + v1i[0] * sin(rad_a)) + v1[1])
941 v1b = (int(v1i[0] * cos(rad_b) - v1i[1] * sin(rad_b) + v1[0]),
942 int(v1i[1] * cos(rad_b) + v1i[0] * sin(rad_b) + v1[1]))
944 # Point B
945 if b_typ == "3":
946 rad_a = rad90
947 rad_b = rad270
948 else:
949 rad_a = rad45
950 rad_b = rad315
952 v2a = (int(v2i[0] * cos(rad_a) - v2i[1] * sin(rad_a) + v2[0]),
953 int(v2i[1] * cos(rad_a) + v2i[0] * sin(rad_a)) + v2[1])
954 v2b = (int(v2i[0] * cos(rad_b) - v2i[1] * sin(rad_b) + v2[0]),
955 int(v2i[1] * cos(rad_b) + v2i[0] * sin(rad_b) + v2[1]))
957 # Triangle o Lines
958 if a_typ == "1" or a_typ == "3":
959 draw_line(v1, v1a, rgba)
960 draw_line(v1, v1b, rgba)
962 if b_typ == "1" or b_typ == "3":
963 draw_line(v2, v2a, rgba)
964 draw_line(v2, v2b, rgba)
966 if a_typ == "2":
967 draw_triangle(v1, v1a, v1b, rgba)
968 if b_typ == "2":
969 draw_triangle(v2, v2a, v2b, rgba)
971 draw_line(v1, v2, rgba)
974 # -------------------------------------------------------------
975 # Draw an OpenGL Rectangle
977 # v1, v2 are corners (bottom left / top right)
978 # -------------------------------------------------------------
979 def draw_rectangle(v1, v2, rgba):
980 # noinspection PyBroadException
981 try:
982 if v1 is not None and v2 is not None:
983 v1b = (v2[0], v1[1])
984 v2b = (v1[0], v2[1])
985 draw_line(v1, v1b, rgba)
986 draw_line(v1b, v2, rgba)
987 draw_line(v2, v2b, rgba)
988 draw_line(v2b, v1, rgba)
989 except:
990 pass
993 # -------------------------------------------------------------
994 # format a point as (x, y, z) for display
996 # -------------------------------------------------------------
997 def format_point(mypoint, pr):
998 pf = "%1." + str(pr) + "f"
999 fmt = " ("
1000 fmt += pf % mypoint[0]
1001 fmt += ", "
1002 fmt += pf % mypoint[1]
1003 fmt += ", "
1004 fmt += pf % mypoint[2]
1005 fmt += ")"
1007 return fmt
1010 # -------------------------------------------------------------
1011 # Draw object num for debug
1013 # -------------------------------------------------------------
1014 # noinspection PyUnresolvedReferences,PyUnboundLocalVariable,PyUnusedLocal
1015 def draw_object(context, myobj, region, rv3d):
1016 scene = bpy.context.scene
1017 rgba = scene.measureit_debug_obj_color
1018 fsize = scene.measureit_debug_font
1019 precision = scene.measureit_debug_precision
1020 # --------------------
1021 # object Loop
1022 # --------------------
1023 objs = bpy.context.scene.objects
1024 obidxs = list(range(len(bpy.context.scene.objects)))
1025 for o in obidxs:
1026 # Display only selected
1027 if scene.measureit_debug_select is True:
1028 if objs[o].select_get() is False:
1029 continue
1030 a_p1 = Vector(get_location(objs[o]))
1031 # Text
1032 txt = ''
1033 if scene.measureit_debug_objects is True:
1034 txt += str(o)
1035 if scene.measureit_debug_object_loc is True:
1036 txt += format_point(a_p1, precision)
1037 # converting to screen coordinates
1038 txtpoint2d = get_2d_point(region, rv3d, a_p1)
1039 draw_text(myobj, txtpoint2d, txt, rgba, fsize)
1040 return
1043 # -------------------------------------------------------------
1044 # Draw vertex num for debug
1046 # -------------------------------------------------------------
1047 # noinspection PyUnresolvedReferences,PyUnboundLocalVariable,PyUnusedLocal
1048 def draw_vertices(context, myobj, region, rv3d):
1049 # Only meshes
1050 if myobj.type != "MESH":
1051 return
1053 scene = bpy.context.scene
1054 rgba = scene.measureit_debug_vert_color
1055 fsize = scene.measureit_debug_font
1056 precision = scene.measureit_debug_precision
1057 # --------------------
1058 # vertex Loop
1059 # --------------------
1060 if scene.measureit_debug_vert_loc_toggle == '1':
1061 co_mult = lambda c: c
1062 else: # if global, convert local c to global
1063 co_mult = lambda c: myobj.matrix_world @ c
1065 if myobj.mode == 'EDIT':
1066 bm = from_edit_mesh(myobj.data)
1067 obverts = bm.verts
1068 else:
1069 obverts = myobj.data.vertices
1071 for v in obverts:
1072 # Display only selected
1073 if scene.measureit_debug_select is True:
1074 if v.select is False:
1075 continue
1076 # noinspection PyBroadException
1077 # try:
1078 a_p1 = get_point(v.co, myobj)
1079 # converting to screen coordinates
1080 txtpoint2d = get_2d_point(region, rv3d, a_p1)
1081 # Text
1082 txt = ''
1083 if scene.measureit_debug_vertices is True:
1084 txt += str(v.index)
1085 if scene.measureit_debug_vert_loc is True:
1086 txt += format_point(co_mult(v.co), precision)
1087 draw_text(myobj, txtpoint2d, txt, rgba, fsize)
1088 # except:
1089 # print("Unexpected error:" + str(exc_info()))
1090 # pass
1092 return
1095 # -------------------------------------------------------------
1096 # Draw edge num for debug
1098 # -------------------------------------------------------------
1099 # noinspection PyUnresolvedReferences,PyUnboundLocalVariable,PyUnusedLocal
1100 def draw_edges(context, myobj, region, rv3d):
1101 # Only meshes
1102 if myobj.type != "MESH":
1103 return
1105 scene = bpy.context.scene
1106 rgba = scene.measureit_debug_edge_color
1107 fsize = scene.measureit_debug_font
1108 precision = scene.measureit_debug_precision
1109 # --------------------
1110 # edge Loop
1112 # uses lambda for edge midpoint finder (midf) because edit mode
1113 # edge vert coordinate is not stored in same places as in obj mode
1114 # --------------------
1115 if myobj.mode == 'EDIT':
1116 bm = from_edit_mesh(myobj.data)
1117 obedges = bm.edges
1118 obverts = None # dummy value to avoid duplicating for loop
1119 midf = lambda e, v: e.verts[0].co.lerp(e.verts[1].co, 0.5)
1120 else:
1121 obedges = myobj.data.edges
1122 obverts = myobj.data.vertices
1123 midf = lambda e, v: v[e.vertices[0]].co.lerp(v[e.vertices[1]].co, 0.5)
1125 for e in obedges:
1126 # Display only selected
1127 if scene.measureit_debug_select is True:
1128 if e.select is False:
1129 continue
1130 a_mp = midf(e, obverts)
1131 a_p1 = get_point(a_mp, myobj)
1132 # converting to screen coordinates
1133 txtpoint2d = get_2d_point(region, rv3d, a_p1)
1134 draw_text(myobj, txtpoint2d, str(e.index), rgba, fsize)
1135 return
1138 # -------------------------------------------------------------
1139 # Draw face num for debug
1141 # -------------------------------------------------------------
1142 # noinspection PyUnresolvedReferences,PyUnboundLocalVariable,PyUnusedLocal
1143 def draw_faces(context, myobj, region, rv3d):
1144 # Only meshes
1145 if myobj.type != "MESH":
1146 return
1148 scene = bpy.context.scene
1149 rgba = scene.measureit_debug_face_color
1150 rgba2 = scene.measureit_debug_norm_color
1151 fsize = scene.measureit_debug_font
1152 ln = scene.measureit_debug_normal_size
1153 th = scene.measureit_debug_width
1154 precision = scene.measureit_debug_precision
1156 # --------------------
1157 # face Loop
1158 # --------------------
1159 if myobj.mode == 'EDIT':
1160 bm = from_edit_mesh(myobj.data)
1161 obverts = bm.verts
1162 myfaces = bm.faces
1163 else:
1164 obverts = myobj.data.vertices
1165 myfaces = myobj.data.polygons
1167 for f in myfaces:
1168 normal = f.normal
1169 # Display only selected
1170 if scene.measureit_debug_select is True:
1171 if f.select is False:
1172 continue
1173 # noinspection PyBroadException
1174 try:
1175 if myobj.mode == 'EDIT':
1176 a_p1 = get_point(f.calc_center_median(), myobj)
1177 else:
1178 a_p1 = get_point(f.center, myobj)
1180 a_p2 = (a_p1[0] + normal[0] * ln, a_p1[1] + normal[1] * ln, a_p1[2] + normal[2] * ln)
1181 # line setup
1182 bgl.glEnable(bgl.GL_BLEND)
1183 bgl.glLineWidth(th)
1184 # converting to screen coordinates
1185 txtpoint2d = get_2d_point(region, rv3d, a_p1)
1186 point2 = get_2d_point(region, rv3d, a_p2)
1187 # Text
1188 if scene.measureit_debug_faces is True:
1189 draw_text(myobj, txtpoint2d, str(f.index), rgba, fsize)
1190 # Draw Normal
1191 if scene.measureit_debug_normals is True:
1192 bgl.glEnable(bgl.GL_BLEND)
1193 draw_arrow(txtpoint2d, point2, rgba, 10, "99", "1")
1195 if len(obverts) > 2 and scene.measureit_debug_normal_details is True:
1196 if myobj.mode == 'EDIT':
1197 i1 = f.verts[0].index
1198 i2 = f.verts[1].index
1199 i3 = f.verts[2].index
1200 else:
1201 i1 = f.vertices[0]
1202 i2 = f.vertices[1]
1203 i3 = f.vertices[2]
1205 a_p1 = get_point(obverts[i1].co, myobj)
1206 a_p2 = get_point(obverts[i2].co, myobj)
1207 a_p3 = get_point(obverts[i3].co, myobj)
1208 # converting to screen coordinates
1209 a2d = get_2d_point(region, rv3d, a_p1)
1210 b2d = get_2d_point(region, rv3d, a_p2)
1211 c2d = get_2d_point(region, rv3d, a_p3)
1212 # draw vectors
1213 draw_arrow(a2d, b2d, rgba, 10, "99", "1")
1214 draw_arrow(b2d, c2d, rgba, 10, "99", "1")
1215 # Normal vector data
1216 txt = format_point(normal, precision)
1217 draw_text(myobj, point2, txt, rgba2, fsize)
1219 except:
1220 print("Unexpected error:" + str(exc_info()))
1221 pass
1223 return
1226 # --------------------------------------------------------------------
1227 # Distance between 2 points in 3D space
1228 # v1: first point
1229 # v2: second point
1230 # locx/y/z: Use this axis
1231 # return: distance
1232 # --------------------------------------------------------------------
1233 def distance(v1, v2, locx=True, locy=True, locz=True):
1234 x = sqrt((v2[0] - v1[0]) ** 2 + (v2[1] - v1[1]) ** 2 + (v2[2] - v1[2]) ** 2)
1236 # If axis is not used, make equal both (no distance)
1237 v1b = [v1[0], v1[1], v1[2]]
1238 v2b = [v2[0], v2[1], v2[2]]
1239 if locx is False:
1240 v2b[0] = v1b[0]
1241 if locy is False:
1242 v2b[1] = v1b[1]
1243 if locz is False:
1244 v2b[2] = v1b[2]
1246 xloc = sqrt((v2b[0] - v1b[0]) ** 2 + (v2b[1] - v1b[1]) ** 2 + (v2b[2] - v1b[2]) ** 2)
1248 return x, xloc
1251 # --------------------------------------------------------------------
1252 # Interpolate 2 points in 3D space
1253 # v1: first point
1254 # v2: second point
1255 # d1: distance
1256 # return: interpolate point
1257 # --------------------------------------------------------------------
1258 def interpolate3d(v1, v2, d1):
1259 # calculate vector
1260 v = (v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2])
1261 # calculate distance between points
1262 d0, dloc = distance(v1, v2)
1264 # calculate interpolate factor (distance from origin / distance total)
1265 # if d1 > d0, the point is projected in 3D space
1266 if d0 > 0:
1267 x = d1 / d0
1268 else:
1269 x = d1
1271 final = (v1[0] + (v[0] * x), v1[1] + (v[1] * x), v1[2] + (v[2] * x))
1272 return final
1275 # --------------------------------------------------------------------
1276 # Get point rotated and relative to parent
1277 # v1: point
1278 # mainobject
1279 # --------------------------------------------------------------------
1280 def get_point(v1, mainobject):
1281 # Using World Matrix
1282 vt = Vector((v1[0], v1[1], v1[2], 1))
1283 m4 = mainobject.matrix_world
1284 vt2 = m4 @ vt
1285 v2 = [vt2[0], vt2[1], vt2[2]]
1287 return v2
1290 # --------------------------------------------------------------------
1291 # Get location in world space
1292 # v1: point
1293 # mainobject
1294 # --------------------------------------------------------------------
1295 def get_location(mainobject):
1296 # Using World Matrix
1297 m4 = mainobject.matrix_world
1299 return [m4[0][3], m4[1][3], m4[2][3]]
1302 # --------------------------------------------------------------------
1303 # Get vertex data
1304 # mainobject
1305 # --------------------------------------------------------------------
1306 def get_mesh_vertices(myobj):
1307 try:
1308 if myobj.mode == 'EDIT':
1309 bm = from_edit_mesh(myobj.data)
1310 obverts = bm.verts
1311 else:
1312 obverts = myobj.data.vertices
1314 return obverts
1315 except AttributeError:
1316 return None
1319 # --------------------------------------------------------------------
1320 # Get position for scale text
1322 # --------------------------------------------------------------------
1323 def get_scale_txt_location(context):
1324 scene = context.scene
1325 pos_x = int(context.region.width * scene.measureit_scale_pos_x / 100)
1326 pos_y = int(context.region.height * scene.measureit_scale_pos_y / 100)
1328 return pos_x, pos_y
1331 # --------------------------------------------------------------------
1332 # Get position in final render image
1333 # (Z < 0 out of camera)
1334 # return 2d position
1335 # --------------------------------------------------------------------
1336 def get_render_location(mypoint):
1338 v1 = Vector(mypoint)
1339 scene = bpy.context.scene
1340 co_2d = object_utils.world_to_camera_view(scene, scene.camera, v1)
1341 # Get pixel coords
1342 render_scale = scene.render.resolution_percentage / 100
1343 render_size = (int(scene.render.resolution_x * render_scale),
1344 int(scene.render.resolution_y * render_scale))
1346 return [round(co_2d.x * render_size[0]), round(co_2d.y * render_size[1])]
1349 # ---------------------------------------------------------
1350 # Get center of circle base on 3 points
1352 # Point a: (x,y,z) arc start
1353 # Point b: (x,y,z) center
1354 # Point c: (x,y,z) midle point in the arc
1355 # Point d: (x,y,z) arc end
1356 # Return:
1357 # ang: angle (radians)
1358 # len: len of arc
1360 # ---------------------------------------------------------
1361 def get_arc_data(pointa, pointb, pointc, pointd):
1362 v1 = Vector((pointa[0] - pointb[0], pointa[1] - pointb[1], pointa[2] - pointb[2]))
1363 v2 = Vector((pointc[0] - pointb[0], pointc[1] - pointb[1], pointc[2] - pointb[2]))
1364 v3 = Vector((pointd[0] - pointb[0], pointd[1] - pointb[1], pointd[2] - pointb[2]))
1366 angle = v1.angle(v2) + v2.angle(v3)
1368 rclength = pi * 2 * v2.length * (angle / (pi * 2))
1370 return angle, rclength
1373 # -------------------------------------------------------------
1374 # Format a number to the right unit
1376 # -------------------------------------------------------------
1377 def format_distance(fmt, units, value, factor=1):
1378 s_code = "\u00b2" # Superscript two
1379 hide_units = bpy.context.scene.measureit_hide_units
1380 # ------------------------
1381 # Units automatic
1382 # ------------------------
1383 if units == "1":
1384 # Units
1385 if bpy.context.scene.unit_settings.system == "IMPERIAL":
1386 feet = value * (3.2808399 ** factor)
1387 if round(feet, 2) >= 1.0:
1388 if hide_units is False:
1389 fmt += " ft"
1390 if factor == 2:
1391 fmt += s_code
1392 tx_dist = fmt % feet
1393 else:
1394 inches = value * (39.3700787 ** factor)
1395 if hide_units is False:
1396 fmt += " in"
1397 if factor == 2:
1398 fmt += s_code
1399 tx_dist = fmt % inches
1400 elif bpy.context.scene.unit_settings.system == "METRIC":
1401 if round(value, 2) >= 1.0:
1402 if hide_units is False:
1403 fmt += " m"
1404 if factor == 2:
1405 fmt += s_code
1406 tx_dist = fmt % value
1407 else:
1408 if round(value, 2) >= 0.01:
1409 if hide_units is False:
1410 fmt += " cm"
1411 if factor == 2:
1412 fmt += s_code
1413 d_cm = value * (100 ** factor)
1414 tx_dist = fmt % d_cm
1415 else:
1416 if hide_units is False:
1417 fmt += " mm"
1418 if factor == 2:
1419 fmt += s_code
1420 d_mm = value * (1000 ** factor)
1421 tx_dist = fmt % d_mm
1422 else:
1423 tx_dist = fmt % value
1424 # ------------------------
1425 # Units meters
1426 # ------------------------
1427 elif units == "2":
1428 if hide_units is False:
1429 fmt += " m"
1430 if factor == 2:
1431 fmt += s_code
1432 tx_dist = fmt % value
1433 # ------------------------
1434 # Units centimeters
1435 # ------------------------
1436 elif units == "3":
1437 if hide_units is False:
1438 fmt += " cm"
1439 if factor == 2:
1440 fmt += s_code
1441 d_cm = value * (100 ** factor)
1442 tx_dist = fmt % d_cm
1443 # ------------------------
1444 # Units millimeters
1445 # ------------------------
1446 elif units == "4":
1447 if hide_units is False:
1448 fmt += " mm"
1449 if factor == 2:
1450 fmt += s_code
1451 d_mm = value * (1000 ** factor)
1452 tx_dist = fmt % d_mm
1453 # ------------------------
1454 # Units feet
1455 # ------------------------
1456 elif units == "5":
1457 if hide_units is False:
1458 fmt += " ft"
1459 if factor == 2:
1460 fmt += s_code
1461 feet = value * (3.2808399 ** factor)
1462 tx_dist = fmt % feet
1463 # ------------------------
1464 # Units inches
1465 # ------------------------
1466 elif units == "6":
1467 if hide_units is False:
1468 fmt += " in"
1469 if factor == 2:
1470 fmt += s_code
1471 inches = value * (39.3700787 ** factor)
1472 tx_dist = fmt % inches
1473 # ------------------------
1474 # Default
1475 # ------------------------
1476 else:
1477 tx_dist = fmt % value
1479 return tx_dist
1482 # -------------------------------------------------------------
1483 # Get radian float based on angle choice
1485 # -------------------------------------------------------------
1486 def get_angle_in_rad(fangle):
1487 if fangle == 0:
1488 return 0.0
1489 else:
1490 return radians(fangle)