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