aboutsummaryrefslogtreecommitdiff
path: root/Core/Scripts/System/mathUtils.py
diff options
context:
space:
mode:
authorMobileMachine\jeremy <[email protected]>2017-06-06 22:59:03 -0400
committerMobileMachine\jeremy <[email protected]>2017-06-06 22:59:03 -0400
commit24725fa8681f906ab44d80687c09fecc171a2896 (patch)
tree312a601df29aca7f8db9f44082d96ebc7a679138 /Core/Scripts/System/mathUtils.py
parentInitial commit (diff)
downloadartv2-24725fa8681f906ab44d80687c09fecc171a2896.tar.xz
artv2-24725fa8681f906ab44d80687c09fecc171a2896.zip
Initial Submission
First submission of current state of ARTv2. Currently considered to be in Alpha. There are a couple of animation tools not implemented yet, and one module not implemented yet, as well as incomplete documentation.
Diffstat (limited to 'Core/Scripts/System/mathUtils.py')
-rw-r--r--Core/Scripts/System/mathUtils.py207
1 files changed, 207 insertions, 0 deletions
diff --git a/Core/Scripts/System/mathUtils.py b/Core/Scripts/System/mathUtils.py
new file mode 100644
index 0000000..d2230ae
--- /dev/null
+++ b/Core/Scripts/System/mathUtils.py
@@ -0,0 +1,207 @@
+"""
+Math utilities
+2015, Epic Games
+"""
+
+import math
+
+import maya.api.OpenMaya as om
+import maya.cmds as cmds
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# CLASSES
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+class KDTreeNode():
+ def __init__(self, point, left, right):
+ self.point = point
+ self.left = left
+ self.right = right
+
+ def is_leaf(self):
+ return (self.left is None and self.right is None)
+
+
+class KDTreeNeighbours():
+ """ Internal structure used in nearest-neighbours search.
+ """
+
+ def __init__(self, query_point, t):
+ self.query_point = query_point
+ self.t = t # neighbours wanted
+ self.largest_distance = 0 # squared
+ self.current_best = []
+
+ def calculate_largest(self):
+ if self.t >= len(self.current_best):
+ self.largest_distance = self.current_best[-1][1]
+ else:
+ self.largest_distance = self.current_best[self.t - 1][1]
+
+ def add(self, point):
+ sd = square_distance(point, self.query_point)
+ # run through current_best, try to find appropriate place
+ for i, e in enumerate(self.current_best):
+ if i == self.t:
+ return # enough neighbours, this one is farther, let's forget it
+ if e[1] > sd:
+ self.current_best.insert(i, [point, sd])
+ self.calculate_largest()
+ return
+ # append it to the end otherwise
+ self.current_best.append([point, sd])
+ self.calculate_largest()
+
+ def get_best(self):
+ return [element[0] for element in self.current_best[:self.t]]
+
+
+class KDTree():
+ """ KDTree implementation built from http://en.wikipedia.org/wiki/K-d_tree as a starting point
+
+ Example usage:
+ from kdtree import KDTree
+
+ tree = KDTree.construct_from_data(data)
+ nearest = tree.query(point, t=4) # find nearest 4 points
+ """
+
+ def __init__(self, data):
+ def build_kdtree(point_list, depth):
+ if not point_list:
+ return None
+
+ # check that all points share the same dimensions
+ dim = len(point_list[0])
+ for point in point_list:
+ if len(point) != dim:
+ print 'KDTREE: point', point, 'does not have', dim, 'dimensions.'
+
+ # select axis based on depth modulo tested dimension
+ axis = depth % dim
+
+ # sort point list
+ point_list.sort(key=lambda point: point[axis])
+ # choose the median
+ median = len(point_list) / 2
+
+ # create node and recursively construct subtrees
+ node = KDTreeNode(point=point_list[median],
+ left=build_kdtree(point_list[0:median], depth + 1),
+ right=build_kdtree(point_list[median + 1:], depth + 1))
+ return node
+
+ self.root_node = build_kdtree(data, depth=0)
+
+ @staticmethod
+ def construct_from_data(data):
+ tree = KDTree(data)
+ return tree
+
+ def query(self, query_point, t=1, debug=1):
+ stats = {'nodes_visited': 0, 'far_search': 0, 'leafs_reached': 0}
+
+ def nn_search(node, query_point, t, depth, best_neighbours):
+ if node is None:
+ return
+
+ stats['nodes_visited'] += 1
+
+ # if we have reached a leaf, let's add to current best neighbours,
+ # (if it's better than the worst one or if there is not enough neighbours)
+ if node.is_leaf():
+ # statistics['leafs_reached'] += 1
+ best_neighbours.add(node.point)
+ return
+
+ # this node is no leaf
+
+ # select dimension for comparison (based on current depth)
+ axis = depth % len(query_point)
+
+ # figure out which subtree to search
+ near_subtree = None # near subtree
+ far_subtree = None # far subtree (perhaps we'll have to traverse it as well)
+
+ # compare query_point and point of current node in selected dimension
+ # and figure out which subtree is farther than the other
+ if query_point[axis] < node.point[axis]:
+ near_subtree = node.left
+ far_subtree = node.right
+ else:
+ near_subtree = node.right
+ far_subtree = node.left
+
+ # recursively search through the tree until a leaf is found
+ nn_search(near_subtree, query_point, t, depth + 1, best_neighbours)
+
+ # while unwinding the recursion, check if the current node
+ # is closer to query point than the current best,
+ # also, until t points have been found, search radius is infinity
+ best_neighbours.add(node.point)
+
+ # check whether there could be any points on the other side of the
+ # splitting plane that are closer to the query point than the current best
+ if (node.point[axis] - query_point[axis]) ** 2 < best_neighbours.largest_distance:
+ # statistics['far_search'] += 1
+ nn_search(far_subtree, query_point, t, depth + 1, best_neighbours)
+
+ return
+
+ # if there's no tree, there's no neighbors
+ if self.root_node is not None:
+ neighbours = KDTreeNeighbours(query_point, t)
+ nn_search(self.root_node, query_point, t, depth=0, best_neighbours=neighbours)
+ result = neighbours.get_best()
+ else:
+ result = []
+
+ # print statistics
+ return result
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# METHODS
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+def square_distance(self, pointA, pointB):
+ # squared euclidean distance
+ distance = 0
+ dimensions = len(pointA) # assumes both points have the same dimensions
+ for dimension in range(dimensions):
+ distance += (pointA[dimension] - pointB[dimension]) ** 2
+ return distance
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+def getAngleBetween(object1, object2):
+ point1 = cmds.xform(object1, t=True, q=True, ws=True)
+ vector1 = om.MVector(point1)
+
+ point2 = cmds.xform(object2, t=True, q=True, ws=True)
+ vector2 = om.MVector(point2)
+
+ dotProduct = vector1.normal() * vector2.normal()
+ angle = math.acos(dotProduct) * 180 / math.pi
+ return angle
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+def returnPercentile(incomingRange, percent, key=lambda x: x):
+ floor = math.floor(percent)
+ ceil = math.ceil(percent)
+
+ if percent == 1:
+ return incomingRange[1]
+
+ if percent == 0:
+ return incomingRange[0]
+
+ d0 = key(incomingRange[int(floor)] * (ceil - percent))
+ d1 = key(incomingRange[int(ceil)] * (percent - floor))
+
+ return d0 + d1