mirror of
https://github.com/blender/blender-addons.git
synced 2025-08-20 13:22:58 +00:00

An issue occurred if you opened Blender with a new file where the Parts Library path pointed relative to the Blender App, not the working directory, so was not found. The Parts Library file is now moved to the Parts Library Menu, so must be set for new .blend files. Existing .blend files are not affected by this change. Error message altered to reflect the option of having this Parts Library live anywhere. New Feature: Add Trig Waves Functions Requires the user to select an existing object in the Trig menu.
795 lines
27 KiB
Python
795 lines
27 KiB
Python
# ***** BEGIN GPL LICENSE BLOCK *****
|
|
#
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# ***** END GPL LICENCE BLOCK *****
|
|
#
|
|
# -----------------------------------------------------------------------
|
|
# Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
|
|
# -----------------------------------------------------------------------
|
|
#
|
|
import bpy
|
|
import bmesh
|
|
from math import sqrt, floor, asin, sin, cos, pi
|
|
from mathutils import Vector
|
|
from bpy.types import Operator
|
|
|
|
from .pdt_functions import (
|
|
oops,
|
|
arc_centre,
|
|
set_mode,
|
|
view_coords,
|
|
view_coords_i,
|
|
)
|
|
|
|
from .pdt_msg_strings import (
|
|
PDT_OBJ_MODE_ERROR,
|
|
PDT_ERR_NO_ACT_OBJ,
|
|
PDT_ERR_SEL_3_VERTS,
|
|
PDT_ERR_SEL_1_VERT,
|
|
PDT_ERR_BADDISTANCE,
|
|
PDT_ERR_MATHSERROR,
|
|
PDT_ERR_SAMERADII,
|
|
PDT_ERR_VERT_MODE,
|
|
)
|
|
|
|
from . import pdt_exception
|
|
|
|
PDT_ObjectModeError = pdt_exception.ObjectModeError
|
|
PDT_SelectionError = pdt_exception.SelectionError
|
|
|
|
|
|
def get_tangent_intersect_outer(hloc_0, vloc_0, hloc_1, vloc_1, radius_0, radius_1):
|
|
"""Return Location in 2 Dimensions of the Intersect Point for Outer Tangents.
|
|
|
|
Args:
|
|
hloc_0: Horizontal Coordinate of Centre of First Arc
|
|
vloc_0: Vertical Coordinate of Centre of First Arc
|
|
hloc_1: Horizontal Coordinate of Centre of Second Arc
|
|
vloc_1: Vertical Coordinate of Centre of Second Arc
|
|
radius_0: Radius of First Arc
|
|
radius_1: Radius of Second Arc
|
|
|
|
Returns:
|
|
hloc_p: Horizontal Coordinate of Centre of Intersection
|
|
vloc_p: Vertical Coordinate of Centre of Intersection.
|
|
"""
|
|
|
|
hloc_p = ((hloc_1 * radius_0) - (hloc_0 * radius_1)) / (radius_0 - radius_1)
|
|
vloc_p = ((vloc_1 * radius_0) - (vloc_0 * radius_1)) / (radius_0 - radius_1)
|
|
|
|
return hloc_p, vloc_p
|
|
|
|
|
|
def get_tangent_intersect_inner(hloc_0, vloc_0, hloc_1, vloc_1, radius_0, radius_1):
|
|
"""Return Location in 2 Dimensions of the Intersect Point for Inner Tangents.
|
|
|
|
Args:
|
|
hloc_0: Horizontal Coordinate of Centre of First Arc
|
|
vloc_0: Vertical Coordinate of Centre of First Arc
|
|
hloc_1: Horizontal Coordinate of Centre of Second Arc
|
|
vloc_1: Vertical Coordinate of Centre of Second Arc
|
|
radius_0: Radius of First Arc
|
|
radius_1: Radius of Second Arc
|
|
|
|
Returns:
|
|
hloc_p: Horizontal Coordinate of Centre of Intersection
|
|
vloc_p: Vertical Coordinate of Centre of Intersection.
|
|
"""
|
|
|
|
hloc_p = ((hloc_1 * radius_0) + (hloc_0 * radius_1)) / (radius_0 + radius_1)
|
|
vloc_p = ((vloc_1 * radius_0) + (vloc_0 * radius_1)) / (radius_0 + radius_1)
|
|
|
|
return hloc_p, vloc_p
|
|
|
|
|
|
def get_tangent_points(context, hloc_0, vloc_0, radius_0, hloc_p, vloc_p):
|
|
"""Return Location in 2 Dimensions of the Tangent Points.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance
|
|
hloc_0: Horizontal Coordinate of Centre of First Arc
|
|
vloc_0: Vertical Coordinate of Centre of First Arc
|
|
radius_0: Radius of First Arc
|
|
hloc_p: Horizontal Coordinate of Intersection
|
|
vloc_p: Vertical Coordinate of Intersection
|
|
|
|
Returns:
|
|
hloc_t1: Horizontal Location of First Tangent Point
|
|
hloc_t2: Horizontal Location of Second Tangent Point
|
|
vloc_t1: Vertical Location of First Tangent Point
|
|
vloc_t2: Vertical Location of Second Tangent Point
|
|
"""
|
|
|
|
# Uses basic Pythagorus' theorem to compute locations
|
|
#
|
|
numerator = (radius_0 ** 2 * (hloc_p - hloc_0)) + (
|
|
radius_0
|
|
* (vloc_p - vloc_0)
|
|
* sqrt((hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2 - radius_0 ** 2)
|
|
)
|
|
denominator = (hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2
|
|
hloc_t1 = round((numerator / denominator) + hloc_0, 5)
|
|
|
|
numerator = (radius_0 ** 2 * (hloc_p - hloc_0)) - (
|
|
radius_0
|
|
* (vloc_p - vloc_0)
|
|
* sqrt((hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2 - radius_0 ** 2)
|
|
)
|
|
denominator = (hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2
|
|
hloc_t2 = round((numerator / denominator) + hloc_0, 5)
|
|
|
|
# Get Y values
|
|
numerator = (radius_0 ** 2 * (vloc_p - vloc_0)) - (
|
|
radius_0
|
|
* (hloc_p - hloc_0)
|
|
* sqrt((hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2 - radius_0 ** 2)
|
|
)
|
|
denominator = (hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2
|
|
vloc_t1 = round((numerator / denominator) + vloc_0, 5)
|
|
|
|
numerator = (radius_0 ** 2 * (vloc_p - vloc_0)) + (
|
|
radius_0
|
|
* (hloc_p - hloc_0)
|
|
* sqrt((hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2 - radius_0 ** 2)
|
|
)
|
|
denominator = (hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2
|
|
vloc_t2 = round((numerator / denominator) + vloc_0, 5)
|
|
|
|
return hloc_t1, hloc_t2, vloc_t1, vloc_t2
|
|
|
|
|
|
def make_vectors(coords, a1, a2, a3, pg):
|
|
"""Return Vectors of the Tangent Points.
|
|
|
|
Args:
|
|
coords: A List of Coordinates in 2D space of the tangent points
|
|
& a third dimension for the vectors
|
|
a1: Index of horizontal axis
|
|
a2: Index of vertical axis
|
|
a3: Index of depth axis
|
|
pg: PDT Parameters Group - our variables
|
|
|
|
Returns:
|
|
tangent_vector_o1: Location of First Tangent Point
|
|
tangent_vector_o2: Location of Second Tangent Point
|
|
tangent_vector_o3: Location of First Tangent Point
|
|
tangent_vector_o4: Location of Second Tangent Point
|
|
"""
|
|
|
|
tangent_vector_o1 = Vector((0, 0, 0))
|
|
tangent_vector_o1[a1] = coords[0]
|
|
tangent_vector_o1[a2] = coords[1]
|
|
tangent_vector_o1[a3] = coords[8]
|
|
tangent_vector_o2 = Vector((0, 0, 0))
|
|
tangent_vector_o2[a1] = coords[2]
|
|
tangent_vector_o2[a2] = coords[3]
|
|
tangent_vector_o2[a3] = coords[8]
|
|
tangent_vector_o3 = Vector((0, 0, 0))
|
|
tangent_vector_o3[a1] = coords[4]
|
|
tangent_vector_o3[a2] = coords[5]
|
|
tangent_vector_o3[a3] = coords[8]
|
|
tangent_vector_o4 = Vector((0, 0, 0))
|
|
tangent_vector_o4[a1] = coords[6]
|
|
tangent_vector_o4[a2] = coords[7]
|
|
tangent_vector_o4[a3] = coords[8]
|
|
|
|
if pg.plane == "LO":
|
|
# Reset coordinates from view local (Horiz, Vert, depth) to World XYZ.
|
|
#
|
|
tangent_vector_o1 = view_coords(
|
|
tangent_vector_o1[a1], tangent_vector_o1[a2], tangent_vector_o1[a3]
|
|
)
|
|
tangent_vector_o2 = view_coords(
|
|
tangent_vector_o2[a1], tangent_vector_o2[a2], tangent_vector_o2[a3]
|
|
)
|
|
tangent_vector_o3 = view_coords(
|
|
tangent_vector_o3[a1], tangent_vector_o3[a2], tangent_vector_o3[a3]
|
|
)
|
|
tangent_vector_o4 = view_coords(
|
|
tangent_vector_o4[a1], tangent_vector_o4[a2], tangent_vector_o4[a3]
|
|
)
|
|
|
|
return (tangent_vector_o1, tangent_vector_o2, tangent_vector_o3, tangent_vector_o4)
|
|
|
|
|
|
def tangent_setup(context, pg, plane, obj_data, centre_0, centre_1, centre_2, radius_0, radius_1):
|
|
"""This section sets up all the variables required for the tangent functions.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance
|
|
pg: PDT Parameter Group of variables
|
|
plane: Working plane
|
|
obj_data: All the data of the chosen object
|
|
centre_0: Centre coordinates of the first arc
|
|
centre_1: Centre coordinates of the second arc
|
|
centre_2: Coordinates fo the point
|
|
radius_0: Radius if the first Arc
|
|
radius_1: Radius of the second Arc
|
|
|
|
Returns:
|
|
Status Set.
|
|
"""
|
|
|
|
a1, a2, a3 = set_mode(plane)
|
|
mode = pg.tangent_mode
|
|
if plane == "LO":
|
|
# Translate world cordinates into view local (horiz, vert, depth)
|
|
#
|
|
centre_0 = view_coords_i(centre_0[a1], centre_0[a2], centre_0[a3])
|
|
centre_1 = view_coords_i(centre_1[a1], centre_1[a2], centre_1[a3])
|
|
centre_2 = view_coords_i(centre_2[a1], centre_2[a2], centre_2[a3])
|
|
if pg.tangent_mode == "point":
|
|
vector_difference = centre_2 - centre_0
|
|
distance = sqrt(vector_difference[a1] ** 2 + vector_difference[a2] ** 2)
|
|
else:
|
|
vector_difference = centre_1 - centre_0
|
|
distance = sqrt(vector_difference[a1] ** 2 + vector_difference[a2] ** 2)
|
|
|
|
if (
|
|
(distance <= radius_0 and mode in {"point"}) or
|
|
(distance <= (radius_0 + radius_1) and mode in {"inner", "both"}) or
|
|
(distance <= radius_0 or distance <= radius_1 and mode in {"outer", "both"})
|
|
):
|
|
# Cannot execute, centres are too close.
|
|
#
|
|
pg.error = f"{PDT_ERR_BADDISTANCE}"
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
|
|
"""This next section will draw Point based Tangents.
|
|
|
|
These are drawn from a point to an Arc
|
|
"""
|
|
|
|
if mode == "point":
|
|
if (
|
|
(centre_2[a1] - centre_0[a1]) ** 2 + (centre_2[a2] - centre_0[a2]) ** 2 - radius_0 ** 2
|
|
) > 0:
|
|
hloc_to1, hloc_to2, vloc_to1, vloc_to2 = get_tangent_points(
|
|
context, centre_0[a1], centre_0[a2], radius_0, centre_2[a1], centre_2[a2]
|
|
)
|
|
else:
|
|
pg.error = PDT_ERR_MATHSERROR
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
# Point Tangents
|
|
#
|
|
tangent_vector_o1 = Vector((0, 0, 0))
|
|
tangent_vector_o1[a1] = hloc_to1
|
|
tangent_vector_o1[a2] = vloc_to1
|
|
tangent_vector_o1[a3] = centre_2[a3]
|
|
tangent_vector_o2 = Vector((0, 0, 0))
|
|
tangent_vector_o2[a1] = hloc_to2
|
|
tangent_vector_o2[a2] = vloc_to2
|
|
tangent_vector_o2[a3] = centre_2[a3]
|
|
if pg.plane == "LO":
|
|
# Translate view local coordinates (horiz, vert, depth) into World XYZ
|
|
#
|
|
centre_2 = view_coords(centre_2[a1], centre_2[a2], centre_2[a3])
|
|
tangent_vector_o1 = view_coords(
|
|
tangent_vector_o1[a1], tangent_vector_o1[a2], tangent_vector_o1[a3]
|
|
)
|
|
tangent_vector_o2 = view_coords(
|
|
tangent_vector_o2[a1], tangent_vector_o2[a2], tangent_vector_o2[a3]
|
|
)
|
|
tangent_vectors = (centre_2, tangent_vector_o1, tangent_vector_o2)
|
|
draw_tangents(tangent_vectors, obj_data)
|
|
|
|
return {"FINISHED"}
|
|
|
|
"""This next section will draw Arc based Outer Tangents.
|
|
|
|
These are drawn from an Arc to another Arc
|
|
"""
|
|
|
|
if mode in {"outer", "both"}:
|
|
# Uses basic trigonometry and Pythagorus' theorem to compute locations
|
|
#
|
|
if radius_0 == radius_1:
|
|
# No intersection point for outer tangents
|
|
#
|
|
sin_angle = (centre_1[a2] - centre_0[a2]) / distance
|
|
cos_angle = (centre_1[a1] - centre_0[a1]) / distance
|
|
hloc_to1 = centre_0[a1] + (radius_0 * sin_angle)
|
|
hloc_to2 = centre_0[a1] - (radius_0 * sin_angle)
|
|
hloc_to3 = centre_1[a1] + (radius_0 * sin_angle)
|
|
hloc_to4 = centre_1[a1] - (radius_0 * sin_angle)
|
|
vloc_to1 = centre_0[a2] - (radius_0 * cos_angle)
|
|
vloc_to2 = centre_0[a2] + (radius_0 * cos_angle)
|
|
vloc_to3 = centre_1[a2] - (radius_0 * cos_angle)
|
|
vloc_to4 = centre_1[a2] + (radius_0 * cos_angle)
|
|
else:
|
|
hloc_po, vloc_po = get_tangent_intersect_outer(
|
|
centre_0[a1], centre_0[a2], centre_1[a1], centre_1[a2], radius_0, radius_1
|
|
)
|
|
|
|
if ((hloc_po - centre_0[a1]) ** 2 + (vloc_po - centre_0[a2]) ** 2 - radius_0 ** 2) > 0:
|
|
hloc_to1, hloc_to2, vloc_to1, vloc_to2 = get_tangent_points(
|
|
context, centre_0[a1], centre_0[a2], radius_0, hloc_po, vloc_po
|
|
)
|
|
else:
|
|
pg.error = PDT_ERR_MATHSERROR
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
if ((hloc_po - centre_0[a1]) ** 2 + (vloc_po - centre_0[a2]) ** 2 - radius_1 ** 2) > 0:
|
|
hloc_to3, hloc_to4, vloc_to3, vloc_to4 = get_tangent_points(
|
|
context, centre_1[a1], centre_1[a2], radius_1, hloc_po, vloc_po
|
|
)
|
|
else:
|
|
pg.error = PDT_ERR_MATHSERROR
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
|
|
dloc_p = centre_0[a3]
|
|
coords_in = (
|
|
hloc_to1,
|
|
vloc_to1,
|
|
hloc_to2,
|
|
vloc_to2,
|
|
hloc_to3,
|
|
vloc_to3,
|
|
hloc_to4,
|
|
vloc_to4,
|
|
dloc_p,
|
|
)
|
|
tangent_vectors = make_vectors(coords_in, a1, a2, a3, pg)
|
|
draw_tangents(tangent_vectors, obj_data)
|
|
|
|
"""This next section will draw Arc based Inner Tangents.
|
|
|
|
These are drawn from an Arc to another Arc
|
|
"""
|
|
|
|
if mode in {"inner", "both"}:
|
|
# Uses basic trigonometry and Pythagorus' theorem to compute locations
|
|
#
|
|
hloc_pi, vloc_pi = get_tangent_intersect_inner(
|
|
centre_0[a1], centre_0[a2], centre_1[a1], centre_1[a2], radius_0, radius_1
|
|
)
|
|
if ((hloc_pi - centre_0[a1]) ** 2 + (vloc_pi - centre_0[a2]) ** 2 - radius_0 ** 2) > 0:
|
|
hloc_to1, hloc_to2, vloc_to1, vloc_to2 = get_tangent_points(
|
|
context, centre_0[a1], centre_0[a2], radius_0, hloc_pi, vloc_pi
|
|
)
|
|
else:
|
|
pg.error = PDT_ERR_MATHSERROR
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
if ((hloc_pi - centre_0[a1]) ** 2 + (vloc_pi - centre_0[a2]) ** 2 - radius_0 ** 2) > 0:
|
|
hloc_to3, hloc_to4, vloc_to3, vloc_to4 = get_tangent_points(
|
|
context, centre_1[a1], centre_1[a2], radius_1, hloc_pi, vloc_pi
|
|
)
|
|
else:
|
|
pg.error = PDT_ERR_MATHSERROR
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
|
|
dloc_p = centre_0[a3]
|
|
coords_in = (
|
|
hloc_to1,
|
|
vloc_to1,
|
|
hloc_to2,
|
|
vloc_to2,
|
|
hloc_to3,
|
|
vloc_to3,
|
|
hloc_to4,
|
|
vloc_to4,
|
|
dloc_p,
|
|
)
|
|
tangent_vectors = make_vectors(coords_in, a1, a2, a3, pg)
|
|
draw_tangents(tangent_vectors, obj_data)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
def draw_tangents(tangent_vectors, obj_data):
|
|
"""Add Edges Representing the Tangents.
|
|
|
|
Note:
|
|
The length of the tanget_vectors determins whcih tangents will be
|
|
drawn, 3 gives Point Tangents, 4 gives Inner/Outer tangents
|
|
|
|
Args:
|
|
tangent_vectors: A list of vectores representing the tangents
|
|
obj_data: A list giving Object, Object Location and Object Bmesh
|
|
|
|
Returns:
|
|
Nothing.
|
|
"""
|
|
obj = obj_data[0]
|
|
obj_loc = obj_data[1]
|
|
bm = obj_data[2]
|
|
if len(tangent_vectors) == 3:
|
|
point_vertex_outer = bm.verts.new(tangent_vectors[0] - obj_loc)
|
|
tangent_vertex_o1 = bm.verts.new(tangent_vectors[1] - obj_loc)
|
|
tangent_vertex_o2 = bm.verts.new(tangent_vectors[2] - obj_loc)
|
|
bm.edges.new([tangent_vertex_o1, point_vertex_outer])
|
|
bm.edges.new([tangent_vertex_o2, point_vertex_outer])
|
|
else:
|
|
tangent_vertex_o1 = bm.verts.new(tangent_vectors[0] - obj_loc)
|
|
tangent_vertex_o2 = bm.verts.new(tangent_vectors[2] - obj_loc)
|
|
tangent_vertex_o3 = bm.verts.new(tangent_vectors[1] - obj_loc)
|
|
tangent_vertex_o4 = bm.verts.new(tangent_vectors[3] - obj_loc)
|
|
bm.edges.new([tangent_vertex_o1, tangent_vertex_o2])
|
|
bm.edges.new([tangent_vertex_o3, tangent_vertex_o4])
|
|
bmesh.update_edit_mesh(obj.data)
|
|
|
|
|
|
def analyse_arc(context, pg):
|
|
"""Analyses an Arc inferred from Selected Vertices.
|
|
|
|
Note:
|
|
Will work if more than 3 vertices are selected, taking the
|
|
first, the nearest to the middle and the last.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance
|
|
pg: PDT Parameters Group - our variables
|
|
|
|
Returns:
|
|
vector_delta: Location of Arc Centre
|
|
radius: Radius of Arc.
|
|
"""
|
|
obj = context.view_layer.objects.active
|
|
if obj is None:
|
|
pg.error = PDT_ERR_NO_ACT_OBJ
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
raise PDT_ObjectModeError
|
|
if obj.mode == "EDIT":
|
|
obj_loc = obj.matrix_world.decompose()[0]
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
|
verts = [v for v in bm.verts if v.select]
|
|
if len(verts) < 3:
|
|
pg.error = f"{PDT_ERR_SEL_3_VERTS} {len(verts)})"
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
raise PDT_SelectionError
|
|
vector_a = verts[0].co
|
|
# Get the nearest to middle vertex of the arc
|
|
#
|
|
vector_b = verts[int(floor(len(verts) / 2))].co
|
|
vector_c = verts[-1].co
|
|
vector_delta, radius = arc_centre(vector_a, vector_b, vector_c)
|
|
|
|
return vector_delta, radius
|
|
|
|
|
|
class PDT_OT_TangentOperate(Operator):
|
|
"""Calculate Tangents from Inputs."""
|
|
|
|
bl_idname = "pdt.tangentoperate"
|
|
bl_label = "Calculate Tangents"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_description = "Calculate Tangents to Arcs from Points or Other Arcs"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
if ob is None:
|
|
return False
|
|
return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
|
|
|
|
def execute(self, context):
|
|
"""Calculate Tangents from Inputs.
|
|
|
|
Note:
|
|
Uses pg.plane, pg.tangent_point0, pg.tangent_radius0, pg.tangent_point1
|
|
pg.tangent_radius1, pg.tangent_point2 to place tangents.
|
|
|
|
Analyses distance between arc centres, or arc centre and tangent point
|
|
to determine which mode is possible (Inner, Outer, or Point). If centres are
|
|
both contianed within 1 inferred circle, Inner tangents are not possible.
|
|
|
|
Arcs of same radius will have no intersection for outer tangents so these
|
|
are calculated differently.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Nothing.
|
|
"""
|
|
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
plane = pg.plane
|
|
# Get Object
|
|
obj = context.view_layer.objects.active
|
|
if obj is not None:
|
|
if obj.mode not in {"EDIT"} or obj.type != "MESH":
|
|
pg.error = PDT_OBJ_MODE_ERROR
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
else:
|
|
pg.error = PDT_ERR_NO_ACT_OBJ
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
|
obj_loc = obj.matrix_world.decompose()[0]
|
|
obj_data = (obj, obj_loc, bm)
|
|
|
|
radius_0 = pg.tangent_radius0
|
|
radius_1 = pg.tangent_radius1
|
|
centre_0 = pg.tangent_point0
|
|
centre_1 = pg.tangent_point1
|
|
centre_2 = pg.tangent_point2
|
|
|
|
tangent_setup(
|
|
context, pg, plane, obj_data, centre_0, centre_1, centre_2, radius_0, radius_1
|
|
)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_TangentOperateSel(Operator):
|
|
"""Calculate Tangents from Selection."""
|
|
|
|
bl_idname = "pdt.tangentoperatesel"
|
|
bl_label = "Calculate Tangents"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_description = "Calculate Tangents to Arcs from 2 Selected Vertices, or 1 & Point in Menu"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
if ob is None:
|
|
return False
|
|
return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
|
|
|
|
def execute(self, context):
|
|
"""Calculate Tangents from Selection.
|
|
|
|
Note:
|
|
Uses pg.plane & 2 or more selected Vertices to place tangents.
|
|
One vertex must be on each arc.
|
|
|
|
Analyses distance between arc centres, or arc centre and tangent point
|
|
to determine which mode is possible (Inner, Outer, or Point). If centres are
|
|
both contianed within 1 inferred circle, Inner tangents are not possible.
|
|
|
|
Arcs of same radius will have no intersection for outer tangents so these
|
|
are calculated differently.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Nothing.
|
|
"""
|
|
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
plane = pg.plane
|
|
# Get Object
|
|
obj = context.view_layer.objects.active
|
|
if obj is not None:
|
|
if obj.mode not in {"EDIT"} or obj.type != "MESH":
|
|
pg.error = PDT_OBJ_MODE_ERROR
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
else:
|
|
pg.error = PDT_ERR_NO_ACT_OBJ
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
|
obj_loc = obj.matrix_world.decompose()[0]
|
|
obj_data = (obj, obj_loc, bm)
|
|
|
|
# Get All Values from Selected Vertices
|
|
verts = [v for v in bm.verts if v.select]
|
|
if len(verts) <= 0:
|
|
pg.error = f"{PDT_ERR_SEL_1_VERT} 0"
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
v1 = verts[0]
|
|
vn = verts[-1]
|
|
for v in bm.verts:
|
|
v.select_set(False)
|
|
for e in bm.edges:
|
|
e.select_set(False)
|
|
v1.select_set(True)
|
|
bpy.ops.mesh.select_linked()
|
|
verts1 = [v for v in bm.verts if v.select].copy()
|
|
if len(verts1) < 3:
|
|
pg.error = f"{PDT_ERR_VERT_MODE} or Less than 3 vertices in your Arc(s)"
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
return {"FINISHED"}
|
|
for v in bm.verts:
|
|
v.select_set(False)
|
|
for e in bm.edges:
|
|
e.select_set(False)
|
|
vn.select_set(True)
|
|
bpy.ops.mesh.select_linked()
|
|
vertsn = [v for v in bm.verts if v.select].copy()
|
|
for v in bm.verts:
|
|
v.select_set(False)
|
|
for e in bm.edges:
|
|
e.select_set(False)
|
|
bmesh.update_edit_mesh(obj.data)
|
|
bm.select_history.clear()
|
|
# Select the nearest to middle vertex in the arc
|
|
#
|
|
verts1 = [verts1[0].co, verts1[int(floor(len(verts1) / 2))].co, verts1[-1].co]
|
|
vertsn = [vertsn[0].co, vertsn[int(floor(len(vertsn) / 2))].co, vertsn[-1].co]
|
|
centre_0, radius_0 = arc_centre(verts1[0], verts1[1], verts1[2])
|
|
centre_1, radius_1 = arc_centre(vertsn[0], vertsn[1], vertsn[2])
|
|
centre_2 = pg.tangent_point2
|
|
|
|
tangent_setup(
|
|
context, pg, plane, obj_data, centre_0, centre_1, centre_2, radius_0, radius_1
|
|
)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_TangentSet1(Operator):
|
|
"""Calculates Centres & Radii from 3 Vectors."""
|
|
|
|
bl_idname = "pdt.tangentset1"
|
|
bl_label = "Calculate Centres & Radii"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_description = "Calculate Centres & Radii from Selected Vertices"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
if ob is None:
|
|
return False
|
|
return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
|
|
|
|
def execute(self, context):
|
|
"""Sets Input Tangent Point 1 to analysis of Arc.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Nothing.
|
|
"""
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
vector_delta, radius = analyse_arc(context, pg)
|
|
pg.tangent_point0 = vector_delta
|
|
pg.tangent_radius0 = radius
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_TangentSet2(Operator):
|
|
"""Calculates Centres & Radii from 3 Vectors."""
|
|
|
|
bl_idname = "pdt.tangentset2"
|
|
bl_label = "Calculate Centres & Radii"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_description = "Calculate Centres & Radii from Selected Vertices"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
obj = context.object
|
|
if obj is None:
|
|
return False
|
|
return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
|
|
|
|
def execute(self, context):
|
|
"""Sets Input Tangent Point 2 to analysis of Arc.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Nothing.
|
|
"""
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
vector_delta, radius = analyse_arc(context, pg)
|
|
pg.tangent_point1 = vector_delta
|
|
pg.tangent_radius1 = radius
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_TangentSet3(Operator):
|
|
"""Set Tangent Origin Point from Cursor."""
|
|
|
|
bl_idname = "pdt.tangentset3"
|
|
bl_label = "Set Tangent Origin Point from Cursor"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_description = "Set Tangent Origin Point from Cursor"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
obj = context.object
|
|
if obj is None:
|
|
return False
|
|
return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
|
|
|
|
def execute(self, context):
|
|
"""Sets Input Tangent Point 3 to analysis of Arc.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Nothing.
|
|
"""
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
pg.tangent_point2 = scene.cursor.location
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_TangentSet4(Operator):
|
|
"""Set Tangent Origin Point from Cursor."""
|
|
|
|
bl_idname = "pdt.tangentset4"
|
|
bl_label = "Set Tangent Origin Point from Vertex"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_description = "Set Tangent Origin Point from Vertex"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
obj = context.object
|
|
if obj is None:
|
|
return False
|
|
return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
|
|
|
|
def execute(self, context):
|
|
"""Sets Input Tangent Point 2 to selected Vertex.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Nothing.
|
|
"""
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
obj = context.object
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
|
verts = [v for v in bm.verts if v.select]
|
|
if len(verts) != 1:
|
|
pg.error = f"{PDT_ERR_SEL_1_VERT} {len(verts)})"
|
|
context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
|
|
raise PDT_SelectionError
|
|
pg.tangent_point2 = verts[0].co
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_TangentExpandMenu(Operator):
|
|
"""Expand/Collapse Tangent Menu."""
|
|
|
|
bl_idname = "pdt.tangentexpandmenu"
|
|
bl_label = "Expand/Collapse Tangent Menu"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_description = "Expand/Collapse Tangent Menu to Show/Hide Input Options"
|
|
|
|
def execute(self, context):
|
|
"""Expand/Collapse Tangent Menu.
|
|
|
|
Note:
|
|
This is used to add further options to the menu.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Nothing.
|
|
"""
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
if pg.menu_expand:
|
|
pg.menu_expand = False
|
|
else:
|
|
pg.menu_expand = True
|
|
return {"FINISHED"}
|