def rotationTo(v1, v2): d = v1.dot(v2) if d >= 1.0: return Quaternion() # Vectors are equal, no rotation needed. q = None if Float.fuzzyCompare(d, -1.0, 1e-6): axis = Vector.Unit_X.cross(v1) if Float.fuzzyCompare(axis.length(), 0.0): axis = Vector.Unit_Y.cross(v1) axis.normalize() q = Quaternion() q.setByAngleAxis(math.pi, axis) else: s = math.sqrt((1.0 + d) * 2.0) invs = 1.0 / s c = v1.cross(v2) q = Quaternion( c.x * invs, c.y * invs, c.z * invs, s * 0.5 ) q.normalize() return q
def slerp(start, end, amount): if Float.fuzzyCompare(amount, 0.0): return start elif Float.fuzzyCompare(amount, 1.0): return end rho = math.acos(start.dot(end)) return (start * math.sin((1 - amount) * rho) + end * math.sin(amount * rho)) / math.sin(rho)
def test_setByAxis(self): q = Quaternion() q.setByAngleAxis(math.pi / 2, Vector.Unit_Z) self.assertEqual(q.x, 0.0) self.assertEqual(q.y, 0.0) self.assertTrue(Float.fuzzyCompare(q.z, math.sqrt(2.0) / 2.0, 1e-6)) self.assertTrue(Float.fuzzyCompare(q.w, math.sqrt(2.0) / 2.0, 1e-6))
def test_rotateVector(self): q1 = Quaternion() q1.setByAngleAxis(math.pi / 2, Vector.Unit_Z) v = Vector(0, 1, 0) v = q1.rotate(v) self.assertTrue(Float.fuzzyCompare(v.x, -1.0, 1e-6)) self.assertTrue(Float.fuzzyCompare(v.y, 0.0, 1e-6)) self.assertTrue(Float.fuzzyCompare(v.z, 0.0, 1e-6))
def test_fromMatrix(self): m = Matrix() m.setByRotationAxis(math.pi / 2, Vector.Unit_Z) q1 = Quaternion.fromMatrix(m) q2 = Quaternion() q2.setByAngleAxis(math.pi / 2, Vector.Unit_Z) self.assertTrue(Float.fuzzyCompare(q1.x, q2.x, 1e-6)) self.assertTrue(Float.fuzzyCompare(q1.y, q2.y, 1e-6)) self.assertTrue(Float.fuzzyCompare(q1.z, q2.z, 1e-6)) self.assertTrue(Float.fuzzyCompare(q1.w, q2.w, 1e-6))
def _onChangeTimerFinished(self): root = self._controller.getScene().getRoot() for node in BreadthFirstIterator(root): if node is root or type(node) is not SceneNode: continue bbox = node.getBoundingBox() if not bbox or not bbox.isValid(): continue # Mark the node as outside the build volume if the bounding box test fails. if self._build_volume.getBoundingBox().intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection: node._outside_buildarea = True else: node._outside_buildarea = False # Move the node upwards if the bottom is below the build platform. move_vector = Vector() if not Float.fuzzyCompare(bbox.bottom, 0.0): move_vector.setY(-bbox.bottom) # If there is no convex hull for the node, start calculating it and continue. if not hasattr(node, "_convex_hull"): if not hasattr(node, "_convex_hull_job"): job = ConvexHullJob.ConvexHullJob(node) job.start() node._convex_hull_job = job elif Selection.isSelected(node): pass else: # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): # Ignore root, ourselves and anything that is not a normal SceneNode. if other_node is root or type(other_node) is not SceneNode or other_node is node: continue # Ignore nodes that do not have the right properties set. if not hasattr(other_node, "_convex_hull") or not other_node.getBoundingBox(): continue # Check to see if the bounding boxes intersect. If not, we can ignore the node as there is no way the hull intersects. if node.getBoundingBox().intersectsBox(other_node.getBoundingBox()) == AxisAlignedBox.IntersectionResult.NoIntersection: continue # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. overlap = node._convex_hull.intersectsPolygon(other_node._convex_hull) if overlap is None: continue move_vector.setX(overlap[0] * 1.1) move_vector.setZ(overlap[1] * 1.1) if move_vector != Vector(): op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector) op.push() if node.getBoundingBox().intersectsBox(self._build_volume.getBoundingBox()) == AxisAlignedBox.IntersectionResult.FullIntersection: op = ScaleToBoundsOperation(node, self._build_volume.getBoundingBox()) op.push()
def test_mirror(self, data): polygon = Polygon(numpy.array(data["points"], numpy.float32)) #Create a polygon with the specified points. polygon.mirror(data["axis_point"], data["axis_direction"]) #Mirror over the specified axis. points = polygon.getPoints() assert len(points) == len(data["points"]) #Must have the same amount of vertices. for point_index in range(len(points)): assert len(points[point_index]) == len(data["answer"][point_index]) #Same dimensionality (2). for dimension in range(len(points[point_index])): assert Float.fuzzyCompare(points[point_index][dimension], data["answer"][point_index][dimension]) #All points must be equal.
def test_project(self): p = Polygon(numpy.array([ [0.0, 1.0], [1.0, 1.0], [1.0, 2.0], [0.0, 2.0] ], numpy.float32)) normal = numpy.array([0.0, 1.0]) self.assertEqual((1.0, 2.0), p.project(normal)) normal = numpy.array([1.0, 0.0]) self.assertEqual((0.0, 1.0), p.project(normal)) normal = numpy.array([math.sqrt(0.5), math.sqrt(0.5)]) result = p.project(normal) self.assertTrue(Float.fuzzyCompare(result[0], 0.70710678), "{0} does not equal {1}".format(result[0], 0.70710678)) self.assertTrue(Float.fuzzyCompare(result[1], 2.12132034), "{0} does not equal {1}".format(result[1], 2.12132034))
def test_project(self, data): p = Polygon(numpy.array([ [0.0, 1.0], [1.0, 1.0], [1.0, 2.0], [0.0, 2.0] ], numpy.float32)) result = p.project(data["normal"]) #Project the polygon onto the specified normal vector. assert len(result) == len(data["answer"]) #Same dimensionality (2). for dimension in range(len(result)): assert Float.fuzzyCompare(result[dimension], data["answer"][dimension])
def setObjectWidth(self, width): obj = Selection.getSelectedObject(0) if obj: width = float(width) obj_width = obj.getBoundingBox().width if not Float.fuzzyCompare(obj_width, width, DIMENSION_TOLERANCE): scale_factor = width / obj_width if self._non_uniform_scale: scale_vector = Vector(scale_factor, 1, 1) else: scale_vector = Vector(scale_factor, scale_factor, scale_factor) Selection.applyOperation(ScaleOperation, scale_vector, scale_around_point = obj.getWorldPosition())
def setObjectHeight(self, height): obj = Selection.getSelectedObject(0) if obj: height = float(height) obj_height = obj.getBoundingBox().height if not Float.fuzzyCompare(obj_height, height, DIMENSION_TOLERANCE): scale_factor = height / obj_height if self._non_uniform_scale: scale_vector = Vector(1, scale_factor, 1) else: scale_vector = Vector(scale_factor, scale_factor, scale_factor) Selection.applyOperation(ScaleOperation, scale_vector)
def setObjectDepth(self, depth): obj = Selection.getSelectedObject(0) if obj: depth = float(depth) obj_depth = obj.getBoundingBox().depth if not Float.fuzzyCompare(obj_depth, depth, DIMENSION_TOLERANCE): scale_factor = depth / obj_depth if self._non_uniform_scale: scale_vector = Vector(1, 1, scale_factor) else: scale_vector = Vector(scale_factor, scale_factor, scale_factor) Selection.applyOperation(ScaleOperation, scale_vector)
def setObjectWidth(self, width): obj = Selection.getSelectedObject(0) if obj: width = float(width) obj_width = obj.getBoundingBox().width if not Float.fuzzyCompare(obj_width, width, DIMENSION_TOLERANCE): scale_factor = width / obj_width if self._non_uniform_scale: scale_vector = Vector(scale_factor, 1, 1) else: scale_vector = Vector(scale_factor, scale_factor, scale_factor) self._scaleSelectedNodes(scale_vector)
def setX(self, x): parsed_x = self._parseInt(x) bounding_box = Selection.getBoundingBox() op = GroupedOperation() if not Float.fuzzyCompare(parsed_x, float(bounding_box.center.x), DIMENSION_TOLERANCE): for selected_node in Selection.getAllSelectedObjects(): world_position = selected_node.getWorldPosition() new_position = world_position.set(x=parsed_x + (world_position.x - bounding_box.center.x)) node_op = TranslateOperation(selected_node, new_position, set_position = True) op.addOperation(node_op) op.push() self._controller.toolOperationStopped.emit(self)
def intersectsRay(self, ray): w = ray.origin - (self._normal * self._distance) nDotR = self._normal.dot(ray.direction) nDotW = -self._normal.dot(w) if Float.fuzzyCompare(nDotR, 0.0): return False t = nDotW / nDotR if t < 0: return False return t
def setZ(self, z): parsed_z = self._parseInt(z) bounding_box = Selection.getBoundingBox() op = GroupedOperation() if not Float.fuzzyCompare(parsed_z, float(bounding_box.center.y), DIMENSION_TOLERANCE): for selected_node in Selection.getAllSelectedObjects(): # Note: The switching of z & y is intentional. We display z as up for the user, # But store the data in openGL space. world_position = selected_node.getWorldPosition() new_position = world_position.set(y=parsed_z + (world_position.y - bounding_box.bottom)) node_op = TranslateOperation(selected_node, new_position, set_position = True) op.addOperation(node_op) op.push() self._controller.toolOperationStopped.emit(self)
def test_intersectConvexHull(self, data): p1 = Polygon(numpy.array(data["p1"])) p2 = Polygon(numpy.array(data["p2"])) result = p1.intersectionConvexHulls(p2) assert len(result.getPoints()) == len(data["answer"]) #Same amount of vertices. isCorrect = False for rotation in range(0, len(result.getPoints())): #The order of vertices doesn't matter, so rotate the result around and if any check succeeds, the answer is correct. thisCorrect = True #Is this rotation correct? for vertex in range(0, len(result.getPoints())): for dimension in range(0, len(result.getPoints()[vertex])): if not Float.fuzzyCompare(result.getPoints()[vertex][dimension], data["answer"][vertex][dimension]): thisCorrect = False break #Break out of two loops. if not thisCorrect: break if thisCorrect: #All vertices checked and it's still correct. isCorrect = True break result.setPoints(numpy.roll(result.getPoints(), 1, axis = 0)) #Perform the rotation for the next check. assert isCorrect
def setX(self, x: str) -> None: parsed_x = self._parseInt(x) bounding_box = Selection.getBoundingBox() if not Float.fuzzyCompare(parsed_x, float(bounding_box.center.x), DIMENSION_TOLERANCE): selected_nodes = self._getSelectedObjectsWithoutSelectedAncestors() if len(selected_nodes) > 1: op = GroupedOperation() for selected_node in self._getSelectedObjectsWithoutSelectedAncestors(): world_position = selected_node.getWorldPosition() new_position = world_position.set(x = parsed_x + (world_position.x - bounding_box.center.x)) node_op = TranslateOperation(selected_node, new_position, set_position = True) op.addOperation(node_op) op.push() else: for selected_node in self._getSelectedObjectsWithoutSelectedAncestors(): world_position = selected_node.getWorldPosition() new_position = world_position.set(x = parsed_x + (world_position.x - bounding_box.center.x)) TranslateOperation(selected_node, new_position, set_position = True).push() self._controller.toolOperationStopped.emit(self)
def test_intersectsPolygon(self, data): p1 = Polygon(numpy.array([ #The base polygon to intersect with. [ 0, 0], [10, 0], [10, 10], [ 0, 10] ], numpy.float32)) p2 = Polygon(numpy.array(data["polygon"])) #The parametrised polygon to intersect with. #Shift the order of vertices in both polygons around. The outcome should be independent of what the first vertex is. for n in range(0, len(p1.getPoints())): for m in range(0, len(data["polygon"])): result = p1.intersectsPolygon(p2) if not data["answer"]: #Result should be None. assert result == None else: assert result != None for i in range(0, len(data["answer"])): assert Float.fuzzyCompare(result[i], data["answer"][i]) p2.setPoints(numpy.roll(p2.getPoints(), 1, axis = 0)) #Shift p2. p1.setPoints(numpy.roll(p1.getPoints(), 1, axis = 0)) #Shift p1.
def setY(self, y: str) -> None: parsed_y = self._parseInt(y) bounding_box = Selection.getBoundingBox() if not Float.fuzzyCompare(parsed_y, float(bounding_box.center.z), DIMENSION_TOLERANCE): selected_nodes = self._getSelectedObjectsWithoutSelectedAncestors() if len(selected_nodes) > 1: op = GroupedOperation() for selected_node in selected_nodes: # Note; The switching of z & y is intentional. We display z as up for the user, # But store the data in openGL space. world_position = selected_node.getWorldPosition() new_position = world_position.set(z = parsed_y + (world_position.z - bounding_box.center.z)) node_op = TranslateOperation(selected_node, new_position, set_position = True) op.addOperation(node_op) op.push() else: for selected_node in selected_nodes: world_position = selected_node.getWorldPosition() new_position = world_position.set(z = parsed_y + (world_position.z - bounding_box.center.z)) TranslateOperation(selected_node, new_position, set_position = True).push() self._controller.toolOperationStopped.emit(self)
def equals(self, other, epsilon = 1e-6): return Float.fuzzyCompare(self.x, other.x, epsilon) and \ Float.fuzzyCompare(self.y, other.y, epsilon) and \ Float.fuzzyCompare(self.z, other.z, epsilon)
def intersectionConvexHulls(self, other): me = self.getConvexHull() him = other.getConvexHull() if len(me._points) <= 2 or len(him._points) <= 2: #If either polygon has no surface area, then the intersection is empty. return Polygon() index_me = 0 #The current vertex index. index_him = 0 advances_me = 0 #How often we've advanced. advances_him = 0 who_is_inside = "unknown" #Which of the two polygons is currently on the inside. directions_me = numpy.subtract(numpy.roll(me._points, -1, axis = 0), me._points) #Pre-compute the difference between consecutive points to get a direction for each point. directions_him = numpy.subtract(numpy.roll(him._points, -1, axis = 0), him._points) result = [] #Iterate through both polygons until we've made a loop through both polygons. while advances_me <= len(me._points) or advances_him <= len(him._points): vertex_me = me._points[index_me] vertex_him = him._points[index_him] if advances_me > len(me._points) * 2 or advances_him > len(him._points) * 2: #Also, if we've looped twice through either polygon, the boundaries of the polygons don't intersect. if len(result) > 2: return result if me.isInside(vertex_him): #Other polygon is inside this one. return him if him.isInside(vertex_me): #This polygon is inside the other. return me #Polygons are disjunct. return Polygon() me_start = Vector2(data = vertex_me) me_end = Vector2(data = vertex_me + directions_me[index_me]) him_start = Vector2(data = vertex_him) him_end = Vector2(data = vertex_him + directions_him[index_him]) me_in_him_halfplane = (me_end - him_start).cross(him_end - him_start) #Cross gives positive if him_end is to the left of me_end (as seen from him_start). him_in_me_halfplane = (him_end - me_start).cross(me_end - me_start) #Arr, I's got him in me halfplane, cap'n. intersection = LineSegment(me_start, me_end).intersection(LineSegment(him_start, him_end)) if intersection: result.append(intersection.getData()) #The intersection is always in the hull. if me_in_him_halfplane > 0: #At the intersection, who was inside changes. who_is_inside = "me" elif him_in_me_halfplane > 0: who_is_inside = "him" else: pass #Otherwise, whoever is inside remains the same (or unknown). advances_me += 1 index_me = (index_me + 1) % len(me._points) advances_him += 1 index_him = (index_him + 1) % len(him._points) continue cross = (Vector2(data = directions_me[index_me]).cross(Vector2(data = directions_him[index_him]))) #Edge case: Two exactly opposite edges facing away from each other. if Float.fuzzyCompare(cross, 0) and me_in_him_halfplane <= 0 and him_in_me_halfplane <= 0: # The polygons must be disjunct then. return Polygon() if Float.fuzzyCompare(cross, 0) and directions_me[index_me].dot(directions_him[index_him]) < 0 and me_in_him_halfplane > 0: #Just advance the inside. if who_is_inside == "him": advances_him += 1 index_him = (index_him + 1) % len(him._points) else: #him OR unknown! If it's unknown, it doesn't matter which one is advanced, since it only happens when the starting edge was already colinear. advances_me += 1 index_me = (index_me + 1) % len(me._points) continue #Edge case: Two colinear edges. if Float.fuzzyCompare(cross, 0): #Two edges overlap. #Just advance the outside. if who_is_inside == "me": advances_him += 1 index_him = (index_him + 1) % len(him._points) else: #him OR unknown! If it's unknown, it doesn't matter which one is advanced, since it only happens when the starting edge was already colinear. advances_me += 1 index_me = (index_me + 1) % len(me._points) continue #Generic case: Advance whichever polygon is on the outside. if cross >= 0: #This polygon is going faster towards the inside. if him_in_me_halfplane > 0: advances_me += 1 index_me = (index_me + 1) % len(me._points) if who_is_inside == "him": result.append(vertex_him) else: advances_him += 1 index_him = (index_him + 1) % len(him._points) if who_is_inside == "me": result.append(vertex_me) else: #The other polygon is going faster towards the inside. if me_in_him_halfplane > 0: advances_him += 1 index_him = (index_him + 1) % len(him._points) if who_is_inside == "me": result.append(vertex_me) else: advances_me += 1 index_me = (index_me + 1) % len(me._points) if who_is_inside == "him": result.append(vertex_him) return Polygon(points = result)
def __eq__(self, other): return Float.fuzzyCompare( self.x, other.x, 1e-6) and Float.fuzzyCompare( self.y, other.y, 1e-6) and Float.fuzzyCompare( self.z, other.z, 1e-6) and Float.fuzzyCompare( self.w, other.w, 1e-6)
def intersectionConvexHulls(self, other): me = self.getConvexHull() him = other.getConvexHull() if len(me._points) <= 2 or len(him._points) <= 2: #If either polygon has no surface area, then the intersection is empty. return Polygon() index_me = 0 #The current vertex index. index_him = 0 advances_me = 0 #How often we've advanced. advances_him = 0 who_is_inside = "unknown" #Which of the two polygons is currently on the inside. directions_me = numpy.subtract(numpy.roll(me._points, -1, axis = 0), me._points) #Pre-compute the difference between consecutive points to get a direction for each point. directions_him = numpy.subtract(numpy.roll(him._points, -1, axis = 0), him._points) result = [] #Iterate through both polygons to find intersections and inside vertices until we've made a loop through both polygons. while advances_me <= len(me._points) or advances_him <= len(him._points): vertex_me = me._points[index_me] vertex_him = him._points[index_him] if advances_me > len(me._points) * 2 or advances_him > len(him._points) * 2: #Also, if we've looped twice through either polygon, the boundaries of the polygons don't intersect. if len(result) > 2: return Polygon(points = result) if me.isInside(vertex_him): #Other polygon is inside this one. return him if him.isInside(vertex_me): #This polygon is inside the other. return me #Polygons are disjunct. return Polygon() me_start = Vector2(data = vertex_me) me_end = Vector2(data = vertex_me + directions_me[index_me]) him_start = Vector2(data = vertex_him) him_end = Vector2(data = vertex_him + directions_him[index_him]) me_in_him_halfplane = (me_end - him_start).cross(him_end - him_start) #Cross gives positive if him_end is to the left of me_end (as seen from him_start). him_in_me_halfplane = (him_end - me_start).cross(me_end - me_start) #Arr, I's got him in me halfplane, cap'n. intersection = LineSegment(me_start, me_end).intersection(LineSegment(him_start, him_end)) if intersection: result.append(intersection.getData()) #The intersection is always in the hull. if me_in_him_halfplane > 0: #At the intersection, who was inside changes. who_is_inside = "me" elif him_in_me_halfplane > 0: who_is_inside = "him" else: pass #Otherwise, whoever is inside remains the same (or unknown). advances_me += 1 index_me = advances_me % len(me._points) advances_him += 1 index_him = advances_him % len(him._points) continue cross = (Vector2(data = directions_me[index_me]).cross(Vector2(data = directions_him[index_him]))) #Edge case: Two exactly opposite edges facing away from each other. if Float.fuzzyCompare(cross, 0) and me_in_him_halfplane <= 0 and him_in_me_halfplane <= 0: # The polygons must be disjunct then. return Polygon() #Edge case: Two colinear edges. if Float.fuzzyCompare(cross, 0) and me_in_him_halfplane <= 0: advances_me += 1 index_me = advances_me % len(me._points) continue if Float.fuzzyCompare(cross, 0) and him_in_me_halfplane <= 0: advances_him += 1 index_him = advances_him % len(him._points) continue #Edge case: Two edges overlap. if Float.fuzzyCompare(cross, 0): #Just advance the outside. if who_is_inside == "me": advances_him += 1 index_him = advances_him % len(him._points) else: #him or unknown. If it's unknown, it doesn't matter which one is advanced, as long as it's the same polygon being advanced every time (me in this case). advances_me += 1 index_me = advances_me % len(me._points) continue #Generic case: Advance whichever polygon is on the outside. if cross >= 0: #This polygon is going faster towards the inside. if him_in_me_halfplane > 0: advances_me += 1 index_me = advances_me % len(me._points) if who_is_inside == "him": result.append(vertex_him) else: advances_him += 1 index_him = advances_him % len(him._points) if who_is_inside == "me": result.append(vertex_me) else: #The other polygon is going faster towards the inside. if me_in_him_halfplane > 0: advances_him += 1 index_him = advances_him % len(him._points) if who_is_inside == "me": result.append(vertex_me) else: advances_me += 1 index_me = advances_me % len(me._points) if who_is_inside == "him": result.append(vertex_him) if (result[0] == result[-1]).all(): #If the last two edges are parallel, the first vertex will have been added again. So if it is the same as the last element, remove it. result = result[:-1] #This also handles the case where the intersection is only one point. return Polygon(points = result)
def isValid(self): return not(Float.fuzzyCompare(self._min.x, self._max.x) or Float.fuzzyCompare(self._min.y, self._max.y) or Float.fuzzyCompare(self._min.z, self._max.z))
def intersectsWithLine(self, a, b): shifted_b = b - a #It intersects if either endpoint is on the line, or if one endpoint is on the right but the other is not. return Float.fuzzyCompare(shifted_b.cross(self._endpoint_a), 0) or Float.fuzzyCompare(shifted_b.cross(self._endpoint_b), 0) or (self._pointIsRight(self._endpoint_a, a, b) != self._pointIsRight(self._endpoint_b, a, b))
def test_translateWorld(self): node1 = SceneNode() node2 = SceneNode(node1) self.assertEqual(node2.getWorldPosition(), Vector(0, 0, 0)) node1.translate(Vector(0, 0, 10)) self.assertEqual(node1.getWorldPosition(), Vector(0, 0, 10)) self.assertEqual(node2.getWorldPosition(), Vector(0, 0, 10)) node2.translate(Vector(0, 0, 10)) self.assertEqual(node1.getWorldPosition(), Vector(0, 0, 10)) self.assertEqual(node2.getWorldPosition(), Vector(0, 0, 20)) node1.rotate(Quaternion.fromAngleAxis(math.pi / 2, Vector.Unit_Y)) self.assertEqual(node1.getWorldPosition(), Vector(0, 0, 10)) self.assertEqual(node2.getWorldPosition(), Vector(10, 0, 10)) node2.translate(Vector(0, 0, 10)) # Local translation on Z with a parent rotated 90 degrees results in movement on X axis pos = node2.getWorldPosition() #Using fuzzyCompare due to accumulation of floating point error self.assertTrue(Float.fuzzyCompare(pos.x, 20, 1e-5), "{0} does not equal {1}".format(pos, Vector(20, 0, 10))) self.assertTrue(Float.fuzzyCompare(pos.y, 0, 1e-5), "{0} does not equal {1}".format(pos, Vector(20, 0, 10))) self.assertTrue(Float.fuzzyCompare(pos.z, 10, 1e-5), "{0} does not equal {1}".format(pos, Vector(20, 0, 10))) node2.translate(Vector(0, 0, 10), SceneNode.TransformSpace.World) # World translation on Z with a parent rotated 90 degrees results in movement on Z axis pos = node2.getWorldPosition() self.assertTrue(Float.fuzzyCompare(pos.x, 20, 1e-5), "{0} does not equal {1}".format(pos, Vector(20, 0, 20))) self.assertTrue(Float.fuzzyCompare(pos.y, 0, 1e-5), "{0} does not equal {1}".format(pos, Vector(20, 0, 20))) self.assertTrue(Float.fuzzyCompare(pos.z, 20, 1e-5), "{0} does not equal {1}".format(pos, Vector(20, 0, 20))) node1.translate(Vector(0, 0, 10)) self.assertEqual(node1.getWorldPosition(), Vector(10, 0, 10)) pos = node2.getWorldPosition() self.assertTrue(Float.fuzzyCompare(pos.x, 30, 1e-5), "{0} does not equal {1}".format(pos, Vector(30, 0, 20))) self.assertTrue(Float.fuzzyCompare(pos.y, 0, 1e-5), "{0} does not equal {1}".format(pos, Vector(30, 0, 20))) self.assertTrue(Float.fuzzyCompare(pos.z, 20, 1e-5), "{0} does not equal {1}".format(pos, Vector(30, 0, 20))) node1.scale(Vector(2, 2, 2)) pos = node2.getWorldPosition() self.assertTrue(Float.fuzzyCompare(pos.x, 50, 1e-4), "{0} does not equal {1}".format(pos, Vector(50, 0, 30))) self.assertTrue(Float.fuzzyCompare(pos.y, 0, 1e-4), "{0} does not equal {1}".format(pos, Vector(50, 0, 30))) self.assertTrue(Float.fuzzyCompare(pos.z, 30, 1e-4), "{0} does not equal {1}".format(pos, Vector(50, 0, 30))) node2.translate(Vector(0, 0, 10)) pos = node2.getWorldPosition() self.assertTrue(Float.fuzzyCompare(pos.x, 70, 1e-4), "{0} does not equal {1}".format(pos, Vector(70, 0, 30))) self.assertTrue(Float.fuzzyCompare(pos.y, 0, 1e-4), "{0} does not equal {1}".format(pos, Vector(70, 0, 30))) self.assertTrue(Float.fuzzyCompare(pos.z, 30, 1e-4), "{0} does not equal {1}".format(pos, Vector(70, 0, 30))) # World space set position node1 = SceneNode() node2 = SceneNode(node1) node1.setPosition(Vector(15,15,15)) node2.setPosition(Vector(10,10,10)) self.assertEqual(node2.getWorldPosition(), Vector(25, 25, 25)) node2.setPosition(Vector(15,15,15), SceneNode.TransformSpace.World) self.assertEqual(node2.getWorldPosition(), Vector(15, 15, 15)) self.assertEqual(node2.getPosition(), Vector(0,0,0)) node1.setPosition(Vector(15,15,15)) node2.setPosition(Vector(0,0,0)) node2.rotate(Quaternion.fromAngleAxis(-math.pi / 2, Vector.Unit_Y)) node2.translate(Vector(10,0,0)) self.assertEqual(node2.getWorldPosition(), Vector(15,15,25)) node2.setPosition(Vector(15,15,25), SceneNode.TransformSpace.World) self.assertEqual(node2.getWorldPosition(), Vector(15,15,25)) self.assertEqual(node2.getPosition(), Vector(0,0,10))
def __eq__(self, other): return Float.fuzzyCompare(self.x, other.x, 1e-6) and Float.fuzzyCompare(self.y, other.y, 1e-6) and Float.fuzzyCompare(self.z, other.z, 1e-6)
def test_toMatrix(self): q1 = Quaternion() q1.setByAngleAxis(math.pi / 2, Vector.Unit_Z) m1 = q1.toMatrix() m2 = Matrix() m2.setByRotationAxis(math.pi / 2, Vector.Unit_Z) self.assertTrue(Float.fuzzyCompare(m1.at(0, 0), m2.at(0, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(0, 1), m2.at(0, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(0, 2), m2.at(0, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(0, 3), m2.at(0, 3), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 0), m2.at(1, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 1), m2.at(1, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 2), m2.at(1, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 3), m2.at(1, 3), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 0), m2.at(2, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 1), m2.at(2, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 2), m2.at(2, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 3), m2.at(2, 3), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 0), m2.at(3, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 1), m2.at(3, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 2), m2.at(3, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 3), m2.at(3, 3), 1e-6))
def _onChangeTimerFinished(self): if not self._enabled: return root = self._controller.getScene().getRoot() for node in BreadthFirstIterator(root): if node is root or type(node) is not SceneNode: continue bbox = node.getBoundingBox() if not bbox or not bbox.isValid(): self._change_timer.start() continue # Mark the node as outside the build volume if the bounding box test fails. if self._build_volume.getBoundingBox().intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection: node._outside_buildarea = True else: node._outside_buildarea = False # Move the node upwards if the bottom is below the build platform. move_vector = Vector() if not Float.fuzzyCompare(bbox.bottom, 0.0): move_vector.setY(-bbox.bottom) # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) if not node.callDecoration("getConvexHull"): if not node.callDecoration("getConvexHullJob"): job = ConvexHullJob.ConvexHullJob(node) job.start() node.callDecoration("setConvexHullJob", job) elif Selection.isSelected(node): pass else: # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): # Ignore root, ourselves and anything that is not a normal SceneNode. if other_node is root or type(other_node) is not SceneNode or other_node is node: continue # Ignore colissions of a group with it's own children if other_node in node.getAllChildren() or node in other_node.getAllChildren(): continue # Ignore colissions within a group if other_node.getParent().callDecoration("isGroup") is not None: if node.getParent().callDecoration("isGroup") is other_node.getParent().callDecoration("isGroup"): continue # Ignore nodes that do not have the right properties set. if not other_node.callDecoration("getConvexHull") or not other_node.getBoundingBox(): continue # Check to see if the bounding boxes intersect. If not, we can ignore the node as there is no way the hull intersects. if node.getBoundingBox().intersectsBox(other_node.getBoundingBox()) == AxisAlignedBox.IntersectionResult.NoIntersection: continue # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_node.callDecoration("getConvexHull")) if overlap is None: continue move_vector.setX(overlap[0] * 1.1) move_vector.setZ(overlap[1] * 1.1) convex_hull = node.callDecoration("getConvexHull") if convex_hull: # Check for collisions between disallowed areas and the object for area in self._build_volume.getDisallowedAreas(): overlap = convex_hull.intersectsPolygon(area) if overlap is None: continue node._outside_buildarea = True if move_vector != Vector(): op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector) op.push()
def equals(self, other, epsilon=1e-6): return Float.fuzzyCompare(self.x, other.x, epsilon) and \ Float.fuzzyCompare(self.y, other.y, epsilon) and \ Float.fuzzyCompare(self.z, other.z, epsilon)
def isValid(self) -> bool: return not(Float.fuzzyCompare(self._min.x, self._max.x) or Float.fuzzyCompare(self._min.y, self._max.y) or Float.fuzzyCompare(self._min.z, self._max.z))