Created
January 8, 2017 21:20
-
-
Save Dante83/1f5431b0d1e220b0429c92909669e787 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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