Created
July 13, 2016 08:46
-
-
Save Farfarer/4f3de49ffc41be8bf6063d804def1329 to your computer and use it in GitHub Desktop.
AABB and AACyl creation for MODO.
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
#!/usr/bin/env python | |
# Uses Smallest Enclosing Circle code which is: | |
# Copyright (c) 2014 Nayuki Minase | |
# http://nayuki.eigenstate.org/page/smallest-enclosing-circle | |
import lx | |
import lxifc | |
import lxu.command | |
import lxu.select | |
import math | |
import sys | |
import random | |
import traceback | |
minFloat = float('-inf') | |
maxFloat = float('inf') | |
_EPSILON = sys.float_info.epsilon | |
options_bbox_from = [('group', 'layer', 'all'), | |
('Per Selected Group', 'Per Layer', 'All Layers')] | |
options_bbox_to = [('each', 'primary'), | |
('Each Layer', 'Primary Layer')] | |
options_bbox_type = [('aabb', 'aacyl'), | |
('Axis Aligned Bounding Box', 'Axis Aligned Bounding Cylinder')] | |
options_bbox_axis = [('x', 'y', 'z', 'shortest', 'longest'), | |
('X', 'Y', 'Z', 'Auto Shortest', 'Auto Longest')] | |
options_bbox_accuracy = [('inside', 'on', 'outside'), | |
('Inside Radius', 'On Radius', 'Enclosing Radius')] | |
class OptionPopup(lxifc.UIValueHints): | |
def __init__(self, items): | |
self._items = items | |
def uiv_Flags(self): | |
return lx.symbol.fVALHINT_POPUPS | |
def uiv_PopCount(self): | |
return len(self._items[0]) | |
def uiv_PopUserName(self,index): | |
return self._items[1][index] | |
def uiv_PopInternalName(self,index): | |
return self._items[0][index] | |
class MarkElements(lxifc.Visitor): | |
def __init__(self, element, mark_mode): | |
self.element = element | |
self.mark_mode = mark_mode | |
def vis_Evaluate(self): | |
self.element.SetMarks(self.mark_mode) | |
class BBOXVerts(lxifc.Visitor): | |
def __init__(self, point): | |
self.point = point | |
# BBOX = Max X Y Z, Min X Y Z | |
self.points = [[],] | |
def get_points(self): | |
return list (set (self.points[0]),) | |
def vis_Evaluate(self): | |
try: | |
self.points[0].append (self.point.Pos ()) | |
except: | |
lx.out(traceback.format_exc()) | |
class BBOXVertsConnected(lxifc.Visitor): | |
def __init__(self, point, mode_selected_unhideunlockunchecked, mode_checked): | |
self.point = point | |
self.mode_selected_unhideunlockunchecked = mode_selected_unhideunlockunchecked | |
self.mode_checked = mode_checked | |
self.points = [] | |
def get_points(self): | |
return self.points | |
def vis_Evaluate(self): | |
try: | |
inner_list = [] | |
outer_list = [] | |
outer_list.append (self.point.ID ()) | |
while len(outer_list) > 0: | |
point_ID = outer_list[0] | |
self.point.Select (point_ID) | |
self.point.SetMarks (self.mode_checked) | |
inner_list.append (point_ID) | |
outer_list.remove (point_ID) | |
num_points = self.point.PointCount () | |
point_points = [] | |
for p in xrange(num_points): | |
point_points.append(self.point.PointByIndex (p)) | |
for next_point_ID in point_points: | |
if next_point_ID not in outer_list and next_point_ID not in inner_list: | |
self.point.Select (next_point_ID) | |
if self.point.TestMarks (self.mode_selected_unhideunlockunchecked): | |
outer_list.append (next_point_ID) | |
if len(inner_list) > 1: | |
# BBOX = Max X Y Z, Min X Y Z | |
points = [] | |
for point_ID in inner_list: | |
self.point.Select (point_ID) | |
points.append (self.point.Pos ()) | |
self.points.append (set (points)) | |
except: | |
lx.out(traceback.format_exc()) | |
class BBOXEdges(lxifc.Visitor): | |
def __init__(self, edge, point): | |
self.edge = edge | |
self.point = point | |
# BBOX = Max X Y Z, Min X Y Z | |
self.points = [] | |
def get_points(self): | |
return list (set (self.points[0]),) | |
def vis_Evaluate(self): | |
try: | |
p1, p2 = self.edge.Endpoints () | |
self.point.Select (p1) | |
self.points.append (self.point.Pos ()) | |
self.point.Select (p2) | |
self.points.append (self.point.Pos ()) | |
except: | |
lx.out(traceback.format_exc()) | |
class BBOXEdgesConnected(lxifc.Visitor): | |
def __init__(self, edge, point, mode_selected_unhideunlockunchecked, mode_checked): | |
self.edge = edge | |
self.point = point | |
self.mode_selected_unhideunlockunchecked = mode_selected_unhideunlockunchecked | |
self.mode_checked = mode_checked | |
# BBOX = Max X Y Z, Min X Y Z | |
self.points = [] | |
def get_points(self): | |
return self.points | |
def vis_Evaluate(self): | |
try: | |
inner_list = [] | |
outer_list = [] | |
outer_list.append (self.edge.ID ()) | |
while len(outer_list) > 0: | |
edge_ID = outer_list[0] | |
self.edge.Select (edge_ID) | |
self.edge.SetMarks (self.mode_checked) | |
inner_list.append (edge_ID) | |
outer_list.remove (edge_ID) | |
endpoints = self.edge.Endpoints () | |
for endpoint in endpoints: | |
self.point.Select (endpoint) | |
num_edges = self.point.EdgeCount () | |
for e in xrange(num_edges): | |
next_edge_ID = self.point.EdgeByIndex (e) | |
if next_edge_ID not in outer_list and next_edge_ID not in inner_list: | |
self.edge.Select (next_edge_ID) | |
if self.edge.TestMarks (self.mode_selected_unhideunlockunchecked): | |
outer_list.append (next_edge_ID) | |
if len(inner_list) > 0: | |
# BBOX = Max X Y Z, Min X Y Z | |
points = [] | |
for edge_ID in inner_list: | |
self.edge.Select (edge_ID) | |
p1, p2 = self.edge.Endpoints () | |
self.point.Select (p1) | |
points.append (self.point.Pos ()) | |
self.point.Select (p2) | |
points.append (self.point.Pos ()) | |
self.points.append (set (points)) | |
except: | |
lx.out(traceback.format_exc()) | |
class BBOXPolys(lxifc.Visitor): | |
def __init__(self, polygon, point): | |
self.polygon = polygon | |
self.point = point | |
self.points = [[],] | |
def get_points(self): | |
return list (set (points[0]),) | |
def vis_Evaluate(self): | |
numPoints = self.polygon.VertexCount () | |
for pp in xrange(numPoints): | |
self.point.Select (self.polygon.VertexByIndex(pp)) | |
self.points[0].append (self.point.Pos ()) | |
class BBOXPolysConnected(lxifc.Visitor): | |
def __init__(self, polygon, point, mode_selected_unhideunlockunchecked, mode_checked): | |
self.polygon = polygon | |
self.point = point | |
self.mode_selected_unhideunlockunchecked = mode_selected_unhideunlockunchecked | |
self.mode_checked = mode_checked | |
self.points = [] | |
def get_points(self): | |
return self.points | |
def vis_Evaluate(self): | |
try: | |
inner_list = [] | |
outer_list = [] | |
outer_list.append (self.polygon.ID ()) | |
while len(outer_list) > 0: | |
polygon_ID = outer_list[0] | |
self.polygon.Select (polygon_ID) | |
self.polygon.SetMarks (self.mode_checked) | |
inner_list.append (polygon_ID) | |
outer_list.remove (polygon_ID) | |
num_points = self.polygon.VertexCount () | |
for p in xrange(num_points): | |
self.polygon.Select (polygon_ID) | |
point_ID = self.polygon.VertexByIndex (p) | |
self.point.Select (point_ID) | |
num_polys = self.point.PolygonCount () | |
for pp in xrange(num_polys): | |
next_polygon_ID = self.point.PolygonByIndex (pp) | |
if next_polygon_ID not in outer_list and next_polygon_ID not in inner_list: | |
self.polygon.Select (next_polygon_ID) | |
if self.polygon.TestMarks (self.mode_selected_unhideunlockunchecked): | |
outer_list.append (next_polygon_ID) | |
if len(inner_list) > 0: | |
# BBOX = Max X Y Z, Min X Y Z | |
points = [] | |
for polygon_ID in inner_list: | |
self.polygon.Select (polygon_ID) | |
num_points = self.polygon.VertexCount () | |
for p in xrange(num_points): | |
point_ID = self.polygon.VertexByIndex (p) | |
self.point.Select (point_ID) | |
points.append (self.point.Pos ()) | |
self.points.append (set (points)) | |
except: | |
lx.out(traceback.format_exc()) | |
class CreateBBOX_Cmd(lxu.command.BasicCommand): | |
def __init__(self): | |
lxu.command.BasicCommand.__init__ (self) | |
self.dyna_Add ('type', lx.symbol.sTYPE_STRING) | |
self.basic_SetFlags(0, lx.symbol.fCMDARG_QUERY) | |
self.dyna_Add ('from', lx.symbol.sTYPE_STRING) | |
self.basic_SetFlags(1, lx.symbol.fCMDARG_QUERY) | |
self.dyna_Add ('to', lx.symbol.sTYPE_STRING) | |
self.basic_SetFlags(2, lx.symbol.fCMDARG_QUERY) | |
self.dyna_Add ('sides', lx.symbol.sTYPE_INTEGER) | |
self.basic_SetFlags(3, lx.symbol.fCMDARG_QUERY) | |
self.dyna_Add ('axis', lx.symbol.sTYPE_STRING) | |
self.basic_SetFlags(4, lx.symbol.fCMDARG_QUERY) | |
self.dyna_Add ('accuracy', lx.symbol.sTYPE_STRING) | |
self.basic_SetFlags(5, lx.symbol.fCMDARG_OPTIONAL | lx.symbol.fCMDARG_QUERY) | |
self.dyna_Add ('select', lx.symbol.sTYPE_BOOLEAN) | |
self.basic_SetFlags (6, lx.symbol.fCMDARG_OPTIONAL | lx.symbol.fCMDARG_QUERY) | |
def arg_UIValueHints(self, index): | |
if index == 0: | |
return OptionPopup(options_bbox_type) | |
elif index == 1: | |
return OptionPopup(options_bbox_from) | |
elif index == 2: | |
return OptionPopup(options_bbox_to) | |
elif index == 4: | |
return OptionPopup(options_bbox_axis) | |
elif index == 5: | |
return OptionPopup(options_bbox_accuracy) | |
def cmd_Query(self,index,vaQuery): | |
va = lx.object.ValueArray() | |
va.set(vaQuery) | |
if index == 0: | |
va.AddString(options_bbox_type[0][0]) | |
elif index == 1: | |
va.AddString(options_bbox_from[0][0]) | |
elif index == 2: | |
va.AddString(options_bbox_to[0][0]) | |
elif index == 3: | |
va.AddInt(8) | |
elif index == 4: | |
va.AddString(options_bbox_axis[0][1]) | |
elif index == 5: | |
va.AddString(options_bbox_accuracy[0][1]) | |
elif index == 6: | |
va.AddInt(1) | |
return lx.result.OK | |
def cmd_Interact(self): | |
# Stop modo complaining. | |
pass | |
def cmd_UserName(self): | |
return 'Create AABB' | |
def cmd_Desc(self): | |
return 'Creates an AABB box mesh around the current selection.' | |
def cmd_Tooltip(self): | |
return 'Creates an AABB box mesh around the current selection.' | |
def cmd_Help(self): | |
return 'http://www.farfarer.com/' | |
def basic_ButtonName(self): | |
return 'Create AABB' | |
def cmd_Flags(self): | |
return lx.symbol.fCMD_SELECT | lx.symbol.fCMD_MODEL | lx.symbol.fCMD_UNDO | |
def basic_Enable(self, msg): | |
return True | |
def arg_UIHints (self, index, hints): | |
if index == 0: | |
hints.Label ('Type') | |
elif index == 1: | |
hints.Label ('Create From') | |
elif index == 2: | |
hints.Label ('Create To') | |
elif index == 3: | |
hints.Label ('Sides') | |
elif index == 4: | |
hints.Label ('Axis') | |
elif index == 5: | |
hints.Label ('Circumference Accuracy') | |
elif index == 6: | |
hints.Label ('Select Results') | |
# Disable input to certain fields in the options dialog depending on the current choices. | |
def cmd_ArgEnable (self, index): | |
if index == 1: | |
if self.dyna_IsSet (0): | |
if self.dyna_String(0) == options_bbox_from[0][2]: | |
lx.throw (lx.symbol.e_CMD_DISABLED) | |
elif index == 2: | |
if self.dyna_IsSet (1): | |
if self.dyna_String(1) == options_bbox_from[0][2]: | |
lx.throw (lx.symbol.e_CMD_DISABLED) | |
elif index == 3: | |
if self.dyna_IsSet (0): | |
if self.dyna_String(0) != options_bbox_type[0][1]: | |
lx.throw (lx.symbol.e_CMD_DISABLED) | |
elif index == 4: | |
if self.dyna_IsSet (0): | |
if self.dyna_String(0) != options_bbox_type[0][1]: | |
lx.throw (lx.symbol.e_CMD_DISABLED) | |
elif index == 5: | |
if self.dyna_IsSet (0): | |
if self.dyna_String(0) != options_bbox_type[0][1]: | |
lx.throw (lx.symbol.e_CMD_DISABLED) | |
return lx.symbol.e_OK | |
# ############################################################################################################################################ | |
# SED STUFF | |
# ############################################################################################################################################ | |
# Data conventions: A point is a pair of floats (x, y). A circle is a triple of floats (center x, center y, radius). | |
# | |
# Returns the smallest circle that encloses all the given points. Runs in expected O(n) time, randomized. | |
# Input: A sequence of pairs of floats or ints, e.g. [(0,5), (3.1,-2.7)]. | |
# Output: A triple of floats representing a circle. | |
# Note: If 0 points are given, None is returned. If 1 point is given, a circle of radius 0 is returned. | |
# | |
def make_circle (self, points): | |
# Convert to float and randomize order | |
shuffled = [(float (p[0]), float (p[1])) for p in points] | |
random.shuffle(shuffled) | |
# Progressively add points to circle or recompute circle | |
c = None | |
for (i, p) in enumerate (shuffled): | |
if c is None or not self._is_in_circle (c, p): | |
c = self._make_circle_one_point (shuffled[0 : i + 1], p) | |
return c | |
# One boundary point known | |
def _make_circle_one_point (self, points, p): | |
c = (p[0], p[1], 0.0) | |
for (i, q) in enumerate (points): | |
if not self._is_in_circle (c, q): | |
if c[2] == 0.0: | |
c = self._make_diameter (p, q) | |
else: | |
c = self._make_circle_two_points (points[0 : i + 1], p, q) | |
return c | |
# Two boundary points known | |
def _make_circle_two_points (self, points, p, q): | |
diameter = self._make_diameter (p, q) | |
if all (self._is_in_circle (diameter, r) for r in points): | |
return diameter | |
left = None | |
right = None | |
for r in points: | |
cross = self._cross_product (p[0], p[1], q[0], q[1], r[0], r[1]) | |
c = self._make_circumcircle (p, q, r) | |
if c is None: | |
continue | |
elif cross > 0.0 and (left is None or self._cross_product (p[0], p[1], q[0], q[1], c[0], c[1]) > self._cross_product (p[0], p[1], q[0], q[1], left[0], left[1])): | |
left = c | |
elif cross < 0.0 and (right is None or self._cross_product (p[0], p[1], q[0], q[1], c[0], c[1]) < self._cross_product (p[0], p[1], q[0], q[1], right[0], right[1])): | |
right = c | |
return left if (right is None or (left is not None and left[2] <= right[2])) else right | |
def _make_circumcircle (self, p0, p1, p2): | |
# Mathematical algorithm from Wikipedia: Circumscribed circle | |
ax = p0[0]; ay = p0[1] | |
bx = p1[0]; by = p1[1] | |
cx = p2[0]; cy = p2[1] | |
d = (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)) * 2.0 | |
if d == 0.0: | |
return None | |
x = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d | |
y = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d | |
return (x, y, math.hypot (x - ax, y - ay)) | |
def _make_diameter (self, p0, p1): | |
return ((p0[0] + p1[0]) / 2.0, (p0[1] + p1[1]) / 2.0, math.hypot (p0[0] - p1[0], p0[1] - p1[1]) / 2.0) | |
def _is_in_circle (self, c, p): | |
return c is not None and math.hypot (p[0] - c[0], p[1] - c[1]) < c[2] + _EPSILON | |
# Returns twice the signed area of the triangle defined by (x0, y0), (x1, y1), (x2, y2) | |
def _cross_product (self, x0, y0, x1, y1, x2, y2): | |
return (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0) | |
# ############################################################################################################################################ | |
def make_BBOX (self, mesh, points): | |
try: | |
# Check for silly single verts. | |
if len (points) < 2: | |
return [] | |
# Make bbox. | |
bbox = [minFloat, minFloat, minFloat, maxFloat, maxFloat, maxFloat] | |
for pointPos in points: | |
bbox[0] = max (bbox[0], pointPos[0]) | |
bbox[1] = max (bbox[1], pointPos[1]) | |
bbox[2] = max (bbox[2], pointPos[2]) | |
bbox[3] = min (bbox[3], pointPos[0]) | |
bbox[4] = min (bbox[4], pointPos[1]) | |
bbox[5] = min (bbox[5], pointPos[2]) | |
point = lx.object.Point (mesh.PointAccessor ()) | |
polygon = lx.object.Polygon (mesh.PolygonAccessor ()) | |
# 0(-X +Y +Z) 1(+X +Y +Z) 2(+X +Y -Z) 3(-X +Y -Z) | |
# 4(-X -Y +Z) 5(+X -Y +Z) 6(+X -Y -Z) 7(-X -Y -Z) | |
new_points = ( | |
point.New ((bbox[3], bbox[1], bbox[2])), | |
point.New ((bbox[0], bbox[1], bbox[2])), | |
point.New ((bbox[0], bbox[1], bbox[5])), | |
point.New ((bbox[3], bbox[1], bbox[5])), | |
point.New ((bbox[3], bbox[4], bbox[2])), | |
point.New ((bbox[0], bbox[4], bbox[2])), | |
point.New ((bbox[0], bbox[4], bbox[5])), | |
point.New ((bbox[3], bbox[4], bbox[5])) | |
) | |
polygon_IDs = [] | |
poly_storage = lx.object.storage ('p', 4) | |
# Front | |
poly_vert_list = (new_points[4], new_points[5], new_points[1], new_points[0]) | |
poly_storage.set (poly_vert_list) | |
polygon_IDs.append (polygon.New (lx.symbol.iPTYP_FACE, poly_storage, 4, 0)) | |
# Back | |
poly_vert_list = (new_points[6], new_points[7], new_points[3], new_points[2]) | |
poly_storage.set (poly_vert_list) | |
polygon_IDs.append (polygon.New (lx.symbol.iPTYP_FACE, poly_storage, 4, 0)) | |
# Left | |
poly_vert_list = (new_points[5], new_points[6], new_points[2], new_points[1]) | |
poly_storage.set (poly_vert_list) | |
polygon_IDs.append (polygon.New (lx.symbol.iPTYP_FACE, poly_storage, 4, 0)) | |
# Right | |
poly_vert_list = (new_points[3], new_points[7], new_points[4], new_points[0]) | |
poly_storage.set (poly_vert_list) | |
polygon_IDs.append (polygon.New (lx.symbol.iPTYP_FACE, poly_storage, 4, 0)) | |
# Top | |
poly_vert_list = (new_points[0], new_points[1], new_points[2], new_points[3]) | |
poly_storage.set (poly_vert_list) | |
polygon_IDs.append (polygon.New (lx.symbol.iPTYP_FACE, poly_storage, 4, 0)) | |
# Bottom | |
poly_vert_list = (new_points[7], new_points[6], new_points[5], new_points[4]) | |
poly_storage.set (poly_vert_list) | |
polygon_IDs.append (polygon.New (lx.symbol.iPTYP_FACE, poly_storage, 4, 0)) | |
return polygon_IDs | |
except: | |
lx.out(traceback.format_exc()) | |
return [] | |
def make_AACYL (self, mesh, points, axis_val, sides, accuracy): | |
if len (points) < 2: | |
return [] | |
axes = [0, 1, 2] | |
point = lx.object.Point (mesh.PointAccessor ()) | |
polygon = lx.object.Polygon (mesh.PolygonAccessor ()) | |
# Make bbox. | |
bbox = [minFloat, minFloat, minFloat, maxFloat, maxFloat, maxFloat] | |
for pointPos in points: | |
bbox[0] = max (bbox[0], pointPos[0]) | |
bbox[1] = max (bbox[1], pointPos[1]) | |
bbox[2] = max (bbox[2], pointPos[2]) | |
bbox[3] = min (bbox[3], pointPos[0]) | |
bbox[4] = min (bbox[4], pointPos[1]) | |
bbox[5] = min (bbox[5], pointPos[2]) | |
# Work out auto axes. | |
# Try and default to Y - upright - for square bboxes. | |
axis = 1 | |
if axis_val == -1 or axis_val == -2: | |
x_size = abs(bbox[0] - bbox[3]) | |
y_size = abs(bbox[1] - bbox[4]) | |
z_size = abs(bbox[2] - bbox[5]) | |
if axis_val == -1: | |
# Longest. | |
if x_size > y_size: | |
if z_size > y_size: | |
axis = 2 | |
else: | |
if x_size > z_size: | |
axis = 0 | |
if axis_val == -2: | |
# Shortest. | |
if x_size < y_size: | |
if z_size < y_size: | |
axis = 2 | |
else: | |
if x_size < z_size: | |
axis = 0 | |
elif axis_val in axes: | |
axis = axis_val | |
else: | |
return [] | |
axes.remove (axis) | |
planar_points = [(p[axes[0]], p[axes[1]]) for p in points] | |
circle = self.make_circle (planar_points) | |
if accuracy == 'on' or accuracy == 'outside': | |
mid_side = (1.0 / sides) * math.pi * 2 | |
mid_point = ((math.sin (mid_side) * circle[2]) * 0.5, ((math.cos(mid_side) * circle[2]) + circle[2]) * 0.5) | |
if accuracy == 'on': | |
mid_point = (mid_point[0] * 0.5, (mid_point[1] + circle[2]) * 0.5) | |
mid_point_distance = math.sqrt(mid_point[0]**2 + mid_point[1]**2) | |
rad_difference_ratio = circle[2] / mid_point_distance | |
if accuracy == 'on': | |
circle = (circle[0], circle[1], (circle[2] * rad_difference_ratio)) | |
elif accuracy == 'outside': | |
circle = (circle[0], circle[1], (circle[2] * rad_difference_ratio)) | |
# Work out axis extents. | |
axisExtents = (bbox[axis], bbox[(axis+3)]) | |
# Build the coords for the cylinder. | |
circle_points = [] | |
for s in xrange(sides): | |
side = (float (s) / float (sides)) * math.pi * 2 | |
circle_point = ((math.sin (side) * circle[2]) + circle[0], (math.cos(side) * circle[2]) + circle[1]) | |
circle_points.append (circle_point) | |
# Build the points for the top and bottom of the cylinder. | |
cylinder_points = [] | |
for axisExtent in axisExtents: | |
for circle_point in circle_points: | |
point_pos = [0.0, 0.0, 0.0] | |
point_pos [axes[0]] = circle_point[0] | |
point_pos [axes[1]] = circle_point[1] | |
point_pos [axis] = axisExtent | |
cylinder_points.append (point.New (point_pos)) | |
# Build the polygons. | |
polygon_IDs = [] | |
# Top & bottom polygons. | |
poly_storage = lx.object.storage ('p', sides) | |
polygon_points = [] | |
for s in xrange(sides): | |
polygon_points.append (cylinder_points[s]) | |
poly_storage.set (polygon_points) | |
polygon_IDs.append (polygon.New (lx.symbol.iPTYP_FACE, poly_storage, sides, 0)) | |
polygon_points = [] | |
for s in xrange(sides): | |
polygon_points.append (cylinder_points[(sides+s)]) | |
polygon_points.reverse () | |
poly_storage.set (polygon_points) | |
polygon_IDs.append (polygon.New (lx.symbol.iPTYP_FACE, poly_storage, sides, 0)) | |
# Side polygons. | |
poly_storage = lx.object.storage ('p', 4) | |
for s in xrange(sides): | |
polygon_points = [] | |
polygon_points.append (cylinder_points[((s+1)%sides)]) | |
polygon_points.append (cylinder_points[(s%sides)]) | |
polygon_points.append (cylinder_points[((s%sides)+sides)]) | |
polygon_points.append (cylinder_points[(((s+1)%sides)+sides)]) | |
poly_storage.set (polygon_points) | |
polygon_IDs.append (polygon.New (lx.symbol.iPTYP_FACE, poly_storage, 4, 0)) | |
return polygon_IDs | |
def basic_Execute(self, msg, flags): | |
try: | |
# Get settings. | |
type_aabb = False | |
type_aacyl = False | |
if self.dyna_IsSet(0): | |
type_val = self.dyna_String(0) | |
if type_val == options_bbox_type[0][0]: | |
type_aabb = True | |
elif type_val == options_bbox_type[0][1]: | |
type_aacyl = True | |
per_element = True | |
per_layer = True | |
if self.dyna_IsSet(1): | |
from_val = self.dyna_String(1) | |
if from_val == options_bbox_from[0][1]: | |
per_element = False | |
elif from_val == options_bbox_from[0][2]: | |
per_element = False | |
per_layer = False | |
to_primary = False | |
if self.dyna_IsSet(2): | |
to_primary = (self.dyna_String(2) == options_bbox_to[0][1]) | |
sides = 8 | |
if self.dyna_IsSet(3): | |
sides = self.dyna_Int(3) | |
sides = max (sides, 3) | |
axis = -1 | |
axes = ('x', 'y', 'z') | |
if self.dyna_IsSet(4): | |
axis_val = self.dyna_String(4) | |
for a in xrange(len(axes)): | |
if axis_val == axes[a]: | |
axis = a | |
break | |
if axis < -2 or axis > 2: | |
return | |
accuracy = 'on' | |
if self.dyna_IsSet(5): | |
accuracy_val = self.dyna_String(5) | |
if accuracy_val in options_bbox_accuracy[0]: | |
accuracy = accuracy_val | |
select_after = True | |
if self.dyna_IsSet(6): | |
select_after = self.dyna_Bool(6) | |
# Grab the active and background layers. | |
layer_svc = lx.service.Layer () | |
layer_scan = lx.object.LayerScan (layer_svc.ScanAllocate (lx.symbol.f_LAYERSCAN_EDIT)) | |
if not layer_scan.test (): | |
return | |
# Early out if there are no active layers. | |
layer_count = layer_scan.Count () | |
if layer_count == 0: | |
return | |
# Drop polgyon selection. | |
selType = lx.eval1('query layerservice selmode ?') | |
if select_after: | |
sel_svc = lx.service.Selection () | |
polygon_pkts = [] | |
# Only deal with visible and unlocked verts. | |
mesh_svc = lx.service.Mesh () | |
mode = mesh_svc.ModeCompose ('select', 'hide lock') | |
mode_selected_unhideunlock = mesh_svc.ModeCompose ('select', 'hide lock') | |
mode_selected_unhideunlockunchecked = mesh_svc.ModeCompose ('select', 'hide lock user4') | |
mode_checked = mesh_svc.ModeCompose ('user4', None) | |
mode_unchecked = mesh_svc.ModeCompose (None, 'user4') | |
# Stuff for treating all layers as one. | |
master_points = [] | |
for layer_idx in xrange(layer_count): | |
# Grab the meshes and their point and meshmap accessors. | |
mesh = lx.object.Mesh (layer_scan.MeshEdit (layer_idx)) | |
mesh_base = lx.object.Mesh (layer_scan.MeshBase (layer_idx)) | |
if not mesh.test () or not mesh_base.test (): | |
continue | |
# Early out if there are no points in the active layer. | |
point_count = mesh.PointCount () | |
if point_count == 0: | |
continue | |
point = lx.object.Point (mesh.PointAccessor ()) | |
edge = lx.object.Edge (mesh.EdgeAccessor ()) | |
polygon = lx.object.Polygon (mesh.PolygonAccessor ()) | |
if not point.test () or not edge.test () or not polygon.test (): | |
continue | |
# Find the BBOX of selected verts. | |
if selType == 'vertex': | |
if per_element: | |
clearElements = MarkElements (point, mode_unchecked) | |
point.Enumerate (mode_checked, clearElements, 0) | |
visitor = BBOXVertsConnected (point, mode_selected_unhideunlockunchecked, mode_checked) | |
point.Enumerate (mode_selected_unhideunlockunchecked, visitor, 0) | |
point.Enumerate (mode_checked, clearElements, 0) | |
else: | |
visitor = BBOXVerts (point) | |
point.Enumerate (mode, visitor, 0) | |
# Find the BBOX of selected edges. | |
elif selType == 'edge': | |
if per_element: | |
clearElements = MarkElements (edge, mode_unchecked) | |
edge.Enumerate (mode_checked, clearElements, 0) | |
visitor = BBOXEdgesConnected (edge, point, mode_selected_unhideunlockunchecked, mode_checked) | |
edge.Enumerate (mode_selected_unhideunlockunchecked, visitor, 0) | |
edge.Enumerate (mode_checked, clearElements, 0) | |
else: | |
visitor = BBOXEdges (edge, point) | |
edge.Enumerate (mode, visitor, 0) | |
# Find the BBOX of selected polygons. | |
elif selType == 'polygon': | |
if per_element: | |
clearElements = MarkElements (polygon, mode_unchecked) | |
polygon.Enumerate (mode_checked, clearElements, 0) | |
visitor = BBOXPolysConnected (polygon, point, mode_selected_unhideunlockunchecked, mode_checked) | |
polygon.Enumerate (mode_selected_unhideunlockunchecked, visitor, 0) | |
polygon.Enumerate (mode_checked, clearElements, 0) | |
else: | |
visitor = BBOXPolys (polygon, point) | |
polygon.Enumerate (mode, visitor, 0) | |
for points in visitor.points: | |
if to_primary: | |
master_points.append (points) | |
else: | |
polygon_IDs = [] | |
if type_aabb: | |
polygon_IDs += self.make_BBOX (mesh, points) | |
elif type_aacyl: | |
polygon_IDs += self.make_AACYL (mesh, points, axis, sides, accuracy) | |
if select_after: | |
for polygon_ID in polygon_IDs: | |
polygon_pkts.append ((polygon_ID, mesh_base)) | |
layer_scan.SetMeshChange (layer_idx, lx.symbol.f_MESHEDIT_GEOMETRY) | |
layer_scan.Apply() | |
if to_primary: | |
# Not applying per layer, so create one master bbox on the primary layer. | |
layer_scan_primary = lx.object.LayerScan (layer_svc.ScanAllocate (lx.symbol.f_LAYERSCAN_PRIMARY | lx.symbol.f_LAYERSCAN_WRITEMESH)) | |
if layer_scan_primary.test (): | |
layer_count_primary = layer_scan_primary.Count () | |
if layer_count_primary > 0: | |
mesh_primary = lx.object.Mesh (layer_scan_primary.MeshEdit (0)) | |
mesh_base_primary = lx.object.Mesh (layer_scan_primary.MeshBase (0)) | |
polygon_IDs = [] | |
if per_element: | |
# Creating one bbox for each element layers. | |
for points in master_points: | |
if type_aabb: | |
polygon_IDs += self.make_BBOX (mesh_primary, points) | |
elif type_aacyl: | |
polygon_IDs += self.make_AACYL (mesh_primary, points, axis, sides, accuracy) | |
else: | |
# Creating one bbox for all elements. | |
points = [point for points in master_points for point in points] | |
if type_aabb: | |
polygon_IDs += self.make_BBOX (mesh_primary, points) | |
elif type_aacyl: | |
polygon_IDs += self.make_AACYL (mesh_primary, points, axis, sides, accuracy) | |
if select_after: | |
for polygon_ID in polygon_IDs: | |
polygon_pkts.append ((polygon_ID, mesh_base_primary)) | |
layer_scan_primary.SetMeshChange (0, lx.symbol.f_MESHEDIT_GEOMETRY) | |
layer_scan_primary.Apply() | |
if select_after and len(polygon_pkts) > 0: | |
sel_type_polygon = sel_svc.LookupType (lx.symbol.sSELTYP_POLYGON) | |
sel_svc.Drop (sel_type_polygon) | |
poly_pkt_trans = lx.object.PolygonPacketTranslation (sel_svc.Allocate(lx.symbol.sSELTYP_POLYGON)) | |
sel_svc.StartBatch () | |
for polygon_pkt in polygon_pkts: | |
sel_svc.Select (sel_type_polygon, poly_pkt_trans.Packet (polygon_pkt[0], polygon_pkt[1])) | |
sel_svc.EndBatch () | |
except: | |
lx.out(traceback.format_exc()) | |
lx.bless(CreateBBOX_Cmd, 'ffr.createAABB') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Awesome by it's simple result, useful for game dev