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