Skip to content

Instantly share code, notes, and snippets.

@Dante83
Created January 8, 2017 21:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Dante83/1f5431b0d1e220b0429c92909669e787 to your computer and use it in GitHub Desktop.
Save Dante83/1f5431b0d1e220b0429c92909669e787 to your computer and use it in GitHub Desktop.
import bpy
from mathutils import Vector
import bmesh
import numpy as np
from math import *
import random as rand
import os, sys
import operator
from operator import itemgetter
_INCHES_TO_METERS = 1 / 39.3701
_FEET_TO_METERS = 12.0 * _INCHES_TO_METERS
class Spline:
def __init__(self, name, x_data, y_data, z_data, g_x_data, g_y_data, g_z_data, lh_x_data, lh_y_data, lh_z_data, rh_x_data, rh_y_data, rh_z_data):
self.name = name
self.coordinate_count = len(x_data)
self.local_x_coordinates = x_data
self.local_y_coordinates = y_data
self.local_z_coordinates = z_data
self.left_handle_x = lh_x_data
self.left_handle_y = lh_y_data
self.left_handle_z = lh_z_data
self.right_handle_x = rh_x_data
self.right_handle_y = rh_y_data
self.right_handle_z = rh_z_data
self.highest_local_x = None
self.highest_local_y = None
self.highest_local_z = None
self.lowest_local_x = None
self.lowest_local_y = None
self.lowest_local_z = None
self.local_node_coordinates = [[0,0,0] for c in range(len(x_data))]
for i, x in enumerate(x_data):
self.local_node_coordinates[i][0] = x
if self.highest_local_x == None or self.highest_local_x > x:
self.highest_local_x = x
if self.lowest_local_x == None or self.lowest_local_x < x:
self.lowest_local_x = x
for i, y in enumerate(y_data):
self.local_node_coordinates[i][1] = y
if self.highest_local_y == None or self.highest_local_y > y:
self.highest_local_y = y
if self.lowest_local_y == None or self.lowest_local_y < y:
self.lowest_local_y = y
for i, z in enumerate(z_data):
self.local_node_coordinates[i][2] = z
if self.highest_local_z == None or self.highest_local_z > z:
self.highest_local_z = z
if self.lowest_local_z == None or self.lowest_local_z < z:
self.lowest_local_z = z
self.local_left_handle_offset_x = [lh_x_data[i] - x_data[i] for i in range(len(x_data))]
self.local_left_handle_offset_y = [lh_y_data[i] - y_data[i] for i in range(len(y_data))]
self.local_left_handle_offset_z = [lh_z_data[i] - z_data[i] for i in range(len(z_data))]
self.local_right_handle_offset_x = [rh_x_data[i] - x_data[i] for i in range(len(x_data))]
self.local_right_handle_offset_y = [rh_y_data[i] - y_data[i] for i in range(len(y_data))]
self.local_right_handle_offset_z = [rh_z_data[i] - z_data[i] for i in range(len(z_data))]
self.local_delta_x = self.highest_local_x - self.lowest_local_x
self.local_delta_y = self.highest_local_y - self.lowest_local_y
self.local_delta_z = self.highest_local_z - self.lowest_local_z
#Now... about those global coordinates
self.highest_global_x = None
self.highest_global_y = None
self.highest_global_z = None
self.lowest_global_x = None
self.lowest_global_y = None
self.lowest_global_z = None
self.global_node_coordinates = [[0,0,0] for c in range(len(g_x_data))]
for i, x in enumerate(g_x_data):
self.global_node_coordinates[i][0] = x
if self.highest_global_x == None or self.highest_global_x > x:
self.highest_global_x = x
if self.lowest_global_x == None or self.lowest_global_x < x:
self.lowest_global_x = x
for i, y in enumerate(g_y_data):
self.global_node_coordinates[i][1] = y
if self.highest_global_y == None or self.highest_global_y > y:
self.highest_global_y = y
if self.lowest_global_y == None or self.lowest_global_y < y:
self.lowest_global_y = y
for i, z in enumerate(g_z_data):
self.global_node_coordinates[i][2] = z
if self.highest_global_z == None or self.highest_global_z > z:
self.highest_global_z = z
if self.lowest_global_z == None or self.lowest_global_z < z:
self.lowest_global_z = z
self.global_x_coordinates = g_x_data
self.global_y_coordinates = g_y_data
self.global_z_coordinates = g_z_data
self.global_delta_x = self.highest_global_x - self.lowest_global_x
self.global_delta_y = self.highest_global_y - self.lowest_global_y
self.global_delta_z = self.highest_global_z - self.lowest_global_z
def setupObjectParameters(self, location, rotation, scale):
self.x_0 = location[0]
self.y_0 = location[1]
self.z_0 = location[2]
self.rot_x = rotation[0]
self.rot_y = rotation[1]
self.rot_z = rotation[2]
self.scale_x = scale[0]
self.scale_y = scale[1]
self.scale_z = scale[2]
class TriangularVoxel:
def __init__(self, p1, p2, p3, global_matrix, id_val=None):
self.p1 = p1
self.p2 = p2
self.p3 = p3
self.points = [p1, p2, p3]
self.gp1 = global_matrix * p1
self.gp2 = global_matrix * p2
self.gp3 = global_matrix * p3
self.global_matrix = global_matrix
self.id_val = id_val
class Plane:
def __init__(self, p1, p2, p3, origin, id):
# Each plane must be made up of 3 points, which is why we must triangulate our matrix first,
#we also prefer to take on numpy arrays, but we should also convert regular lists
if type(p1).__module__ == np.__name__:
self.p1 = p1
else:
self.p1 = np.asarray(p1)
if type(p2).__module__ == np.__name__:
self.p2 = p2
else:
self.p2 = np.asarray(p2)
if type(p3).__module__ == np.__name__:
self.p3 = p3
else:
self.p3 = np.asarray(p3)
if type(origin).__module__ == np.__name__:
self.origin = origin
else:
self.origin = np.asarray(origin)
#Get the normal vector for this equation via the cross product of p1 anp2
normal_vector = np.cross(self.p1, self.p2)
#Now normalize that normal vector from two of the points
normalization_constant = (normal_vector[0]**2 + normal_vector[1]**2 + normal_vector[2]**2)**(0.5)
self.normal_vector = normal_vector / normalization_constant
#Get our coordinates relative to the origin
p1_rel_to_origin = self.p1 - origin
p2_rel_to_origin = self.p2 - origin
p3_rel_to_origin = self.p3 - origin
pnts_rel_to_origin = [p1_rel_to_origin, p2_rel_to_origin, p3_rel_to_origin]
#
#From some of the delicious help, over at http://stackoverflow.com/questions/9423621/3d-rotations-of-a-plane
#
#The normal vector to our x-y plane is, of course, [0,0,1], so
normal_to_xy = np.asarray([0.0,0.0,1.0])
#Now determine our rotation angles via...
cosTheta = np.dot(self.normal_vector, normal_to_xy)
#The rotation axis should then be given by,
axis = np.cross(self.normal_vector, normal_to_xy)
axis_list = axis.tolist()
if( any(a != 0.0 for a in axis_list) ):
axis_normalized = axis / ((axis[0])**2 + (axis[1])**2 + (axis[2])**2)**0.5
s = (1.0 - cosTheta**2)**0.5
c_inv = (1.0 - cosTheta)
x = axis_normalized[0]
y = axis_normalized[1]
z = axis_normalized[2]
r_00 = c_inv * x**2 + cosTheta
r_01 = x * y * c_inv - z * s
r_02 = x * z * c_inv + y * s
r_10 = y * x * c_inv + z * s
r_11 = c_inv * y**2 + cosTheta
r_12 = y * z * c_inv - x * s
r_20 = z * x * c_inv - y * s
r_21 = z * y * c_inv + x * s
r_22 = c_inv * z**2 + cosTheta
rot_matrix = [[r_00, r_01, r_02],[r_10, r_11, r_12],[r_20, r_21, r_22]]
temp_p1 = np.asmatrix(self.p1[np.newaxis, :].T)
temp_p2 = np.asmatrix(self.p2[np.newaxis, :].T)
temp_p3 = np.asmatrix(self.p3[np.newaxis, :].T)
self.x_y_plane_transformation_matrix = np.asarray(rot_matrix)
self.rotated_p1 = np.asmatrix(self.x_y_plane_transformation_matrix) * temp_p1
self.rotated_p2 = np.asmatrix(self.x_y_plane_transformation_matrix) * temp_p2
self.rotated_p3 = np.asmatrix(self.x_y_plane_transformation_matrix) * temp_p3
#Now find the inverse matrix which is critical for transforming coordinates in the x-y plane back to the native plane of this field
self.transform_x_y_plane_into_this_plane = np.linalg.inv(np.asmatrix(self.x_y_plane_transformation_matrix))
else:
#Hey, it turns out that this plane is already aligned with the x-y plane... so guess what?
#that means our x_y transformation matrix is just the identity matrix,
#as is (by default) it's inverse
self.x_y_plane_transformation_matrix = np.identity(3)
self.transform_xy_plane_into_this_plane = np.identity(3)
self.rotated_p1 = self.p1
self.rotated_p2 = self.p2
self.rotated_p3 = self.p3
def point_from_x_y(self, x, y):
z = self.p3[2] - (self.normal_vector[0] * (x - self.p3[0]) + self.normal_vector[1] * (y - self.p3[1])) / (self.normal_vector[2])
return z
def point_from_x_z(self, x, z):
y = self.p3[1] - (self.normal_vector[0] * (x - self.p3[0]) + self.normal_vector[2] * (z - self.p3[2])) / (self.normal_vector[1])
return y
def point_from_y_z(self, y, z):
x = self.p3[0] - (self.normal_vector[1] * (y - self.p3[1]) + self.normal_vector[2] * (z - self.p3[2])) / (self.normal_vector[0])
return x
def rotate_about_x_axis(self, theta):
ST = math.sin(theta)
CT = math.cos(theta)
rot_matrix = [[1.0, 0.0, 0.0], [0.0, CT, -ST], [0.0, ST, CT]]
return np.array(rot_matrix)
def rotate_about_y_axis(self, theta):
ST = math.sin(theta)
CT = math.cos(theta)
rot_matrix = [[CT, 0.0, ST], [0.0, 1.0, 0.0], [-ST, 0.0, CT]]
return np.array(rot_matrix)
def rotate_about_z_axis(self, theta):
ST = math.sin(theta)
CT = math.cos(theta)
rot_matrix = [[CT, -ST, 0.0], [ST, CT, 0.0], [0.0, 0.0, 1.0]]
return np.array(rot_matrix)
def calculate_plane_travelling_distance(self, point):
#Find out the x, y and z coordinates where our line crosses the plane (It can't avoid crossing the plane because it is orthoganl, and hence must cross at some point)
# x = point[0] + t * self.normal_vector[0]
# y = point[1] + t * self.normal_vector[1]
# z = point[2] + t * self.normal_vector[2]
#Solve this via gaussian elimination
eqn_1 = [self.normal_vector[0], self.normal_vector[1], self.normal_vector[2], 0.0]
eqn_2 = [1.0, 0.0, 0.0, -1.0 * self.normal_vector[0]]
eqn_3 = [0.0, 1.0, 0.0, -1.0 * self.normal_vector[0]]
eqn_4 = [0.0, 0.0, 1.0, -1.0 * self.normal_vector[0]]
first_constant = self.normal_vector[0] * self.p3[0] + self.normal_vector[1] * self.p3[1] + self.normal_vector[2] * self.p3[2]
dependent_variables = [first_constant, point[0], point[1], point[2]]
gaussian_array = np.asarray([eqn_1, eqn_2, eqn_3, eqn_4])
#NOTE: If you're getting singular matrix errors here, make sure that your mesh is properly triangulated before running this code
intersection_point = np.linalg.solve(gaussian_array, dependent_variables)
intersection_coordinates = np.asarray([intersection_point[p] for p in range(3)])
#Now that we have the intersection point, let's rotate that into the x-y plane along with our other variables
intersection_point = np.matrix(self.x_y_plane_transformation_matrix) * np.matrix(intersection_coordinates).T
intersection_point = np.array(intersection_point.T)[0]
#Now that these points have been rotated, use them to determine whether the intersection point is inside of our triangle
#Thanks to http://stackoverflow.com/questions/13300904/determine-whether-point-lies-inside-triangle
intersection_point_is_in_voxel = False
#To make the below equation a little less cumbersome (and fit on a single screen...)
rp1 = self.rotated_p1
rp2 = self.rotated_p2
rp3 = self.rotated_p3
alpha_numerator = ((rp2[1] - rp3[1])*(intersection_point[0] - rp3[0]) + (rp3[0] - rp2[0])*(intersection_point[1] - rp3[1]))
alpha_denominator = ((rp2[1] - rp3[1])*(rp1[0] - rp3[0]) + (rp3[0] - rp2[0])*(rp1[1] - rp3[1]))
alpha = alpha_numerator / alpha_denominator
beta_numerator = ((rp3[1] - rp1[1])*(intersection_point[0] - rp3[0]) + (rp1[0] - rp3[0])*(intersection_point[1] - rp3[1]))
beta_denominator = ((rp2[1] - rp3[1])*(rp1[0] - rp3[0]) + (rp3[0] - rp2[0])*(rp1[1] - rp3[1]))
beta = beta_numerator / beta_denominator
gamma = 1.0 - alpha - beta
if( all(x > 0.0 for x in [alpha, beta, gamma]) ):
#If it does, return a true value and the distance between the point and the point on the plane where the plane and the line intersect
intersection_distance = ((point[0] - intersection_point[0])**2+(point[1] - intersection_point[1])**2+(point[2] - intersection_point[2])**2)**(0.5)
self.distance_to_plane = intersection_distance
self.intersects_plane = True
return {'intersects_triangle': True, 'distance_to_plane':intersection_distance}
else:
#If it does NOT, return false, and the distance to the plane, but the nearest distance to the border where the plane actually begins
distance_to_outer_plane = ((point[0] - intersection_point[0])**2+(point[1] - intersection_point[1])**2+(point[2] - intersection_point[2])**2)**(0.5)
#Now find the distances to each of the edges
distances = []
rot_points = [self.rotated_p1, self.rotated_p2, self.rotated_p3]
rot_point_T = [np.array(rot_point.T)[0] for rot_point in rot_points]
for i in range(len(rot_points)):
#Create a line between the intersection point and the line created by the points i and i -1
m_triangle_side = (rot_point_T[i][1] - rot_point_T[i - 1][1]) / (rot_point_T[i][0] - rot_point_T[i - 1][0])
b_triangle_side = rot_point_T[i][1] - m_triangle_side * rot_point_T[i][0]
#The shortest path between the intersection point and this line will then be perpendicular to this line and
#go through the intersection point itself
m_perpendicular = -1.0 * m_triangle_side
b_perpendicular = intersection_point[1] - m_perpendicular * intersection_point[0]
#Now find out where the two lines cross
left_matrix = np.asarray([[-m_triangle_side, 1.0],[-m_perpendicular, 1.0]])
right_vector = np.asarray([b_triangle_side, b_perpendicular])
x_y_crossing_point = np.linalg.solve(left_matrix, right_vector)
x_crossing = x_y_crossing_point[0]
#Finally decide if it crosses in between the two points, before the first point, or after the last point.
#If it falls in between them, use the distance between that intersection point and the original point as our addition
#To the the final distance, otherwise use the distance to either of the respective end points
points = [rot_point_T[i], rot_point_T[i - 1]]
larger_point = rot_point_T[0]
smaller_point = rot_point_T[0]
for point in rot_point_T:
if(point[0] > larger_point[0]):
larger_point = point
if(point[0] < smaller_point[0]):
smaller_point = point
if(x_crossing >= larger_point[0]):
distances.append(((intersection_point[0] - larger_point[0])**2 + (intersection_point[1] - larger_point[1])**2)**0.5)
elif(x_crossing <= smaller_point[0]):
distances.append(((intersection_point[0] - smaller_point[0])**2 + (intersection_point[1] - smaller_point[1])**2)**0.5)
else:
distances.append(((intersection_point[0] - x_y_crossing_point[0])**2 + (intersection_point[1] - x_y_crossing_point[1])**2)**0.5)
#Determine the smallest of the distances acquired from the above work
smallest_distance_to_side = min(distances)
final_distance = smallest_distance_to_side + distance_to_outer_plane
self.distance_to_plane = final_distance
self.intersects_plane = False
return {'intersects_plane': False, 'distance_to_plane': final_distance}
class ExtrapolatedSpline:
def __init__(self, loop_iterator):
self.x_data = []
self.y_data = []
self.z_data = []
self.lh_x_data = []
self.lh_y_data = []
self.lh_z_data = []
self.rh_x_data = []
self.rh_y_data = []
self.rh_z_data = []
self.coordinates = []
self.point_coordinates = []
self.left_handle_coordinates = []
self.right_handle_coordinates = []
self.x_0 = 0.0
self.y_0 = 0.0
self.z_0 = 0.0
self.r_x = 0.0
self.r_y = 0.0
self.r_z = 0.0
self.s_x = 0.0
self.s_y = 0.0
self.s_z = 0.0
self.original_thread_name = None
self.loop_iterator = loop_iterator
def set_global_origin(self, x_0, y_0, z_0, r_x, r_y, r_z, s_x, s_y, s_z):
self.x_0 = x_0
self.y_0 = y_0
self.z_0 = z_0
self.r_x = r_x
self.r_y = r_y
self.r_z = r_z
self.s_x = s_x
self.s_y = s_y
self.s_z = s_z
def add_cordinate_point(self, x, y, z, lh_x, lh_y, lh_z, rh_x, rh_y, rh_z):
self.x_data.append(x)
self.y_data.append(y)
self.z_data.append(z)
self.lh_x_data.append(lh_x)
self.lh_y_data.append(lh_y)
self.lh_z_data.append(lh_z)
self.rh_x_data.append(rh_x)
self.rh_y_data.append(rh_y)
self.rh_z_data.append(rh_z)
self.point_coordinates.append([x,y,z])
self.left_handle_coordinates.append([lh_x, lh_y, lh_z])
self.right_handle_coordinates.append([rh_x, rh_y, rh_z])
coordinate_dictionary = {'co': [x,y,z], 'lh': [lh_x, lh_y, lh_z], 'rh': [rh_x, rh_y, rh_z]}
self.coordinates.append(coordinate_dictionary)
class AttachmentPoint:
def __init__(self, row, column):
self.row = row
self.column = column
def setOriginPoint(self, x, y ,z):
self.origin_x = x
self.origin_y = y
self.origin_z = z
def setNextOriginPoint(self, x, y, z):
self.next_origin_x = x
self.next_origin_y = y
self.next_origin_z = z
def setRegionAboveOriginPoint(self, x, y, z):
self.region_above_origin_x = x
self.region_above_origin_y = y
self.region_above_origin_z = z
def Main():
#----------------------------------------
#Cheap user variables
#----------------------------------------
horizontal_loops = 14
vertical_loops = 5
#thread_radius = 0.0163 * _INCHES_TO_METERS / 2.0
thread_radius = 0.27
thread_extrapolation_offset = [0.0, 0.0, 0.0] #The starting x and ending x should all be the same
#vertical_distance_to_thread_repeat = [0.0, -thread_radius * 2.0, 0.0]
#vertical_distance_to_thread_repeat = [0.0,-4.0,0.0]
output_curve_name = 'output_thread_'
#----------------------------------------
#Get all the triangular voxel data from our active object
context = bpy.context
active = context.active_object
selected_but_not_active = [obj for obj in context.selected_objects if not (obj == active)]
bm_active_obj = bmesh.new()
bm_active_obj.from_mesh(active.data)
active_object_faces = []
id_val = 1
for face in bm_active_obj.faces:
face_vertices = []
id_val += 1
for v in face.verts:
face_vertices.append(v.co)
active_object_face = TriangularVoxel(face_vertices[0], face_vertices[1], face_vertices[2], active.matrix_world, id_val)
active_object_faces.append(active_object_face)
#Get all the selected splines and convert them into a set of coordinates based on their control points, where everything selected but the active object is a spline
object_names = []
threads = []
for obj in selected_but_not_active:
object_names.append(obj.name)
splines = obj.data.splines
x_data = []
y_data = []
z_data = []
lh_x_data = []
lh_y_data = []
lh_z_data = []
rh_x_data = []
rh_y_data = []
rh_z_data = []
g_x_data = []
g_y_data = []
g_z_data = []
matrix_world = obj.matrix_world
for spline in obj.data.splines:
for point in spline.bezier_points:
# angle_right = degrees(Vector((1,0,0)).angle((point.handle_right - point.co)))
# if (Vector((1,0,0)).cross((point.handle_right - point.co)).z < 0):
# angle_right = angle_right + 180
# angle_left = degrees(Vector((1,0,0)).angle((point.handle_left - point.co)))
# if (Vector((1,0,0)).cross((point.handle_left - point.co)).z < 0):
# angle_left = angle_left + 180
#
# angle_left_type = point.handle_left_type
# angle_right_type = point.handle_right_type
lh_x_data.append(point.handle_left[0])
lh_y_data.append(point.handle_left[1])
lh_z_data.append(point.handle_left[2])
x_data.append(point.co.x)
y_data.append(point.co.y)
z_data.append(point.co.z)
rh_x_data.append(point.handle_right[0])
rh_y_data.append(point.handle_right[1])
rh_z_data.append(point.handle_right[2])
#And for that same point in global coordinates
g_coordinates = matrix_world * point.co
g_x_data.append(g_coordinates[0])
g_y_data.append(g_coordinates[1])
g_z_data.append(g_coordinates[2])
thread = Spline(obj.name, x_data, y_data, z_data, g_x_data, g_y_data, g_z_data, lh_x_data, lh_y_data, lh_z_data, rh_x_data, rh_y_data, rh_z_data)
threads.append(thread)
#Now let's go through this again, so that we can get those glorious global coordinates of the object itself
for name in object_names:
bpy.ops.object.select_all( action = 'DESELECT' )
selected_object = bpy.data.objects[name]
for thread in threads:
if(thread.name == name):
thread.setupObjectParameters(selected_object.location, selected_object.rotation_euler, selected_object.scale)
bpy.ops.object.select_all( action = 'DESELECT' )
#Organize these results by their spline name
threads.sort(key=lambda x: x.name)
#Find the width, height and depth of this first component
grand_lowest_x = None
grand_lowest_y = None
grand_lowest_z = None
grand_highest_x = None
grand_highest_y = None
grand_highest_z = None
for thread in threads:
#Get the grand lowest x coordinate in local coordinates
if(grand_lowest_x == None or abs(thread.lowest_global_x) < abs(grand_lowest_x) ):
grand_lowest_x = thread.lowest_global_x
if(grand_lowest_y == None or abs(thread.lowest_global_y) < abs(grand_lowest_y) ):
grand_lowest_y = thread.lowest_global_y
if(grand_lowest_z == None or abs(thread.lowest_global_z) < abs(grand_lowest_z) ):
grand_lowest_z = thread.lowest_global_z
if(grand_highest_x == None or abs(thread.highest_global_x) > abs(grand_highest_x) ):
grand_highest_x = thread.highest_global_x
if(grand_highest_y == None or abs(thread.highest_global_y) > abs(grand_highest_y) ):
grand_highest_y = thread.highest_global_y
if(grand_highest_z == None or abs(thread.highest_global_z) > abs(grand_highest_z) ):
grand_highest_z = thread.highest_global_z
weave_delta_x = grand_highest_x - grand_lowest_x
weave_delta_y = grand_highest_y - grand_lowest_y
weave_delta_z = grand_highest_z - grand_lowest_z
#Now that we have our thread data, we repeat each of our threads the number of times specified
extrapolated_threads = []
previousThread = None
#This will record our last ending position for each thread as we create it, so that we can use this as an offset
#For starting new segments of the thread in the next component.
#We re-write over each component as we go along the threads, but then as we reach the next vertical loop, we jump
#to the new row of this table and start a-new, using the previous thread data from below to guide our offset data
thread_construction_data = []
for vl in range(vertical_loops):
thread_construction_data.append([])
for thread in threads:
#A new thread to hold all this delicious data
extrapolated_thread = ExtrapolatedSpline(vl)
#
#TODO: Hey!!! This is where things start going a little crazy. Let's fix this first.
#
#Create all of our local coordinates for each thread
for vert in range(horizontal_loops * thread.coordinate_count):
if(vert % thread.coordinate_count == 0):
#this is a new thread segment! Use this opportunity to determine
# a new offset vertically and horizontally that aligns it with the previous
#thread behind it and below it.
newAttachmentPoint = AttachmentPoint(vl, int(vert / thread.coordinate_count))
if( len(thread_construction_data[-1]) - 1 <= floor(vert / thread.coordinate_count)):
thread_construction_data[-1].append(newAttachmentPoint) #Append this to the highest one in the list, this new chunk of thread is going to rotate around this point
#If there is a thread before it, we use that thread's origin offset to set the location for this threads origin offset
#Otherwise, we check for coordinates below it
#And else we just stick with the current coordinate and go with things
row_index = len(thread_construction_data) - 1 #We're always working with the final row
column_index = len(thread_construction_data[-1]) - 1 #And the final column that we've so far added to that column
if(row_index != 0):
if(column_index != 0):
#Just use the previous element's data to set the origin
previous_thread = thread_construction_data[row_index][column_index - 1]
thread_segment_rotational_origin = np.asarray([previous_thread.next_origin_x, previous_thread.next_origin_y, previous_thread.next_origin_z])
else:
#Just use the information from the thread below to set the starting location for this thread
previous_thread = thread_construction_data[row_index - 1][0]
#We're still using the far right lower index data for this - we need the upper bound of this new data
thread_segment_rotational_origin = np.asarray([previous_thread.region_above_origin_x, previous_thread.region_above_origin_y, previous_thread.region_above_origin_z])
else:
if(column_index != 0):
#Just use the previous element's data to set the origin
previous_thread = thread_construction_data[0][column_index - 1]
thread_segment_rotational_origin = np.asarray([previous_thread.next_origin_x, previous_thread.next_origin_y, previous_thread.next_origin_z])
else:
#Hey, this is the very first element we're adding! Let's use origin as our starting point of interest...
thread_segment_rotational_origin = np.asarray([0.0, 0.0, 0.0])
#this origin point to build off our thread segment in the x-y plane is also used to determine the rotation that will move it in line with
#The the coordinates of our surface...
#Implement this origin and use it to orient all of our planes\
face_planes = []
fp_id = 0
for face in active_object_faces:
fp_id += 1
#Use center of mass for these coordinates to determine the origin of rotation
sum_of_coordinates = [0.0,0.0,0.0]
coordinates = [np.asarray(face.gp1), np.asarray(face.gp2), np.asarray(face.gp3)]
for coordinate in coordinates:
sum_of_coordinates[0] += coordinates[0]
sum_of_coordinates[1] += coordinates[1]
sum_of_coordinates[2] += coordinates[2]
origin_co = np.asarray(sum_of_coordinates) / 3.0
#Now create our facial plane (Using the same origin as our 2D plane implements)
face_plane = Plane(face.gp1, face.gp2, face.gp3, origin_co, fp_id)
#Determine the distance between our starting point for our mesh weave and every other plane in the mesh
traveling_distance = face_plane.calculate_plane_travelling_distance(thread_segment_rotational_origin)
#And add this to the list of our potential planes for rotation
face_planes.append(face_plane)
#Now that we have the distances between our point and each of the planes, determine the plane with the smallest distances
closest_plane = None
for plane in face_planes:
if closest_plane == None or plane.distance_to_plane < closest_plane.distance_to_plane:
closest_plane = plane
#
#TODO: I'm thinking our offset component is located here - we need to get it below for multiple levels of our thread.
#
#
#TODO: Just what are we rotating about? The center of mass of our element, or the origin? If the cent of mass, these just won't do.
#Although it does seem that our rotation matrix is slightly off on these off axis components. Perhaps this is the first order element
#to correct?
#
#
#TODO: Most important question! This seems to be dependent upon our rotation matrix (it is) and so, we should fix the bugs associated with
#that before we can hope to have it move our components in the right direction "vertically".
#
next_thread_segment_starting_point = closest_plane.transform_x_y_plane_into_this_plane * np.matrix(np.asarray([thread.global_delta_x,0.0,0.0])).T + np.matrix(thread_segment_rotational_origin).T
next_thread_starting_point = closest_plane.transform_x_y_plane_into_this_plane * np.matrix(np.asarray([0.0, weave_delta_y, 0.0])).T# + np.matrix(thread_segment_rotational_origin).T
next_thread_segment_starting_point = np.array(next_thread_segment_starting_point.T)[0]
next_thread_starting_point = np.array(next_thread_starting_point.T)[0]
if(( (len(thread_construction_data) - 1) >= row_index) and ( (len(thread_construction_data[row_index]) - 1) >= column_index)):
thread_construction_data[row_index][column_index].setNextOriginPoint(next_thread_segment_starting_point[0], next_thread_segment_starting_point[1], next_thread_segment_starting_point[2])
thread_construction_data[row_index][column_index].setRegionAboveOriginPoint(next_thread_starting_point[0], next_thread_starting_point[1], next_thread_starting_point[2])
if((vert % thread.coordinate_count) != 0):
v_x_offset = floor(vert / thread.coordinate_count) * (thread_extrapolation_offset[0] - thread.local_delta_x)
v_y_offset = floor(vert / thread.coordinate_count) * thread_extrapolation_offset[1]
v_z_offset = floor(vert / thread.coordinate_count) * thread_extrapolation_offset[2]
v_i = vert % thread.coordinate_count
v_x = thread.local_node_coordinates[v_i][0] + v_x_offset
v_y = thread.local_node_coordinates[v_i][1] + v_y_offset
v_z = thread.local_node_coordinates[v_i][2] + v_z_offset
left_hand_x = thread.local_left_handle_offset_x[v_i] + v_x
left_hand_y = thread.local_left_handle_offset_y[v_i] + v_y
left_hand_z = thread.local_left_handle_offset_z[v_i] + v_z
right_hand_x = thread.local_right_handle_offset_x[v_i] + v_x
right_hand_y = thread.local_right_handle_offset_y[v_i] + v_y
right_hand_z = thread.local_right_handle_offset_z[v_i] + v_z
#Create vectors from each of the above and multiply they by inverse matrix that rotates
#x-y coordinates into the coordinates of the plane of our closest plane
#so that each one is in the right locoation
#And while we're at this creation thing, let's move these points into the local coordinates of our plane
v_l_coordinates = np.asarray([v_x, v_y, v_z]) - thread_segment_rotational_origin
l_l_coordinates = np.asarray([left_hand_x, left_hand_y, left_hand_z]) - thread_segment_rotational_origin
r_l_coordinates = np.asarray([right_hand_x, right_hand_y, right_hand_z]) - thread_segment_rotational_origin
#Luckily, because of how I built things, we can keep both of these boogers so that their local/global coordinates are all aligned...
#Even though I thought I'd have to deal with that, I decided not dealing with it would be a good thing.
#Consequently, I should just need to keep track of my planar offsets in their local coordinates, as opposed to worrying about all
#Those coordinate systems. But, I still need to get these coordinates in the coordinate system of the center of mass of our plane
#So I can apply a proper transformation, then I gotta get it BACK out of those coordinates.
rot_v_l_coordinates = closest_plane.transform_x_y_plane_into_this_plane * np.matrix(v_l_coordinates).T
rot_l_l_coordinates = closest_plane.transform_x_y_plane_into_this_plane * np.matrix(l_l_coordinates).T
rot_r_l_coordinates = closest_plane.transform_x_y_plane_into_this_plane * np.matrix(r_l_coordinates).T
#And move these coordinates back into their original coordiante systems
rot_v_coordinates = rot_v_l_coordinates + np.matrix(thread_segment_rotational_origin).T
rot_l_coordinates = rot_l_l_coordinates + np.matrix(thread_segment_rotational_origin).T
rot_r_coordinates = rot_r_l_coordinates + np.matrix(thread_segment_rotational_origin).T
#And turn them back into numpy arrays when we're done
rot_v_coordinates = np.array(rot_v_coordinates.T)[0]
rot_l_coordinates = np.array(rot_l_coordinates.T)[0]
rot_r_coordinates = np.array(rot_r_coordinates.T)[0]
#Now split up those values into values we can pack into our final extrapolated thread
r_v_x = rot_v_coordinates[0]
r_v_y = rot_v_coordinates[1]
r_v_z = rot_v_coordinates[2]
r_l_x = rot_l_coordinates[0]
r_l_y = rot_l_coordinates[1]
r_l_z = rot_l_coordinates[2]
r_r_x = rot_r_coordinates[0]
r_r_y = rot_r_coordinates[1]
r_r_z = rot_r_coordinates[2]
#And add these rotated and translated coordinates to the extrapolated thread
extrapolated_thread.add_cordinate_point(r_v_x, r_v_y, r_v_z, r_l_x, r_l_y, r_l_z, r_r_x, r_r_y, r_r_z)
extrapolated_thread.original_thread_name = thread.name
extrapolated_threads.append(extrapolated_thread)
#Now take each of those threads and give them the same position, rotation and scale of their original object
for vl in range(vertical_loops):
for thread in threads:
for extrapolated_thread in extrapolated_threads:
if extrapolated_thread.original_thread_name == thread.name and extrapolated_thread.loop_iterator == vl:
extrapolated_thread.set_global_origin(thread.x_0, thread.y_0, thread.z_0, thread.rot_x, thread.rot_y, thread.rot_z, thread.scale_x, thread.scale_y, thread.scale_z)
#Now that we have a set of new points, turn these into threads on the screen and bevel them out according to our thread radius
#Implement a bezier curve along these points
for thread_id, thread in enumerate(extrapolated_threads):
#Deselect, just for good measure
bpy.ops.object.select_all(action='DESELECT')
#Now build one of these boogers
curveData = bpy.data.curves.new('data_' + output_curve_name + str(thread_id), type='CURVE')
curveData.dimensions = '3D'
curveData.resolution_u = 1
curveData.fill_mode = 'FULL'
curveData.bevel_depth = thread_radius
curveData.bevel_resolution = 1
bezier_curve = bpy.data.objects.new(output_curve_name + str(thread_id), curveData)
bezier_curve.location = Vector((thread.x_0,thread.y_0,thread.z_0)) #object origin
bezier_curve.rotation_euler = Vector((thread.r_x, thread.r_y, thread.r_z)) #object rotation
bezier_curve.scale = Vector((thread.s_x, thread.s_y, thread.s_z))
bpy.context.scene.objects.link(bezier_curve)
bezier_curve = curveData.splines.new('BEZIER')
bezier_curve.bezier_points.add(len(thread.coordinates)-1)
for i, c in enumerate(thread.coordinates):
bezier_curve.bezier_points[i].handle_left = (c['lh'][0], c['lh'][1], c['lh'][2])
bezier_curve.bezier_points[i].co = (c['co'][0], c['co'][1], c['co'][2])
bezier_curve.bezier_points[i].handle_right = (c['rh'][0], c['rh'][1], c['rh'][2])
#TODO: Copy the main materials for fibres
#TODO: Apply this material to the fibre
Main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment