Example #1
0
    def read(self, file_name, **kwargs):
        try:
            for id, reader in self._mesh_readers.items():
                result = reader.read(file_name)
                if result is not None:
                    if kwargs.get("center", True):
                        # If the result has a mesh and no children it needs to be centered
                        if result.getMeshData() and len(result.getChildren()) == 0:
                            extents = result.getMeshData().getExtents()
                            move_vector = Vector()
                            move_vector.setX(extents.center.x)
                            move_vector.setY(extents.center.y) # Ensure that bottom is on 0 (above plate)
                            move_vector.setZ(extents.center.z)
                            result.setCenterPosition(move_vector)

                            if result.getMeshData().getExtents().bottom != 0:
                               result.translate(Vector(0,-result.getMeshData().getExtents().bottom ,0))

                        # Move all the meshes of children so that toolhandles are shown in the correct place.
                        for node in result.getChildren():
                            if node.getMeshData():
                                extents = node.getMeshData().getExtents()
                                m = Matrix()
                                m.translate(-extents.center)
                                node.setMeshData(node.getMeshData().getTransformed(m))
                                node.translate(extents.center)
                    return result

        except OSError as e:
            Logger.log("e", str(e))

        Logger.log("w", "Unable to read file %s", file_name)
        return None #unable to read
Example #2
0
    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()
Example #3
0
    def event(self, event):
        super().event(event)

        if event.type == Event.MousePressEvent and self._controller.getToolsEnabled(
        ):
            # Initialise a mirror operation
            if MouseEvent.LeftButton not in event.buttons:
                return False

            id = self._selection_pass.getIdAtPosition(event.x, event.y)
            if not id:
                return False

            if ToolHandle.isAxis(id):
                self.setLockedAxis(id)
                return True

        if event.type == Event.MouseReleaseEvent:
            # Perform a mirror operation
            if self.getLockedAxis():
                op = None
                if Selection.getCount() == 1:
                    node = Selection.getSelectedObject(0)
                    mirror = Vector(1, 1, 1)
                    if self.getLockedAxis() == ToolHandle.XAxis:
                        mirror.setX(-1)
                    elif self.getLockedAxis() == ToolHandle.YAxis:
                        mirror.setY(-1)
                    elif self.getLockedAxis() == ToolHandle.ZAxis:
                        mirror.setZ(-1)

                    op = MirrorOperation(node,
                                         mirror,
                                         mirror_around_center=True)
                else:
                    op = GroupedOperation()

                    for node in Selection.getAllSelectedObjects():
                        mirror = Vector(1, 1, 1)
                        if self.getLockedAxis() == ToolHandle.XAxis:
                            mirror.setX(-1)
                        elif self.getLockedAxis() == ToolHandle.YAxis:
                            mirror.setY(-1)
                        elif self.getLockedAxis() == ToolHandle.ZAxis:
                            mirror.setZ(-1)

                        op.addOperation(
                            MirrorOperation(node,
                                            mirror,
                                            mirror_around_center=True))

                op.push()

                self.setLockedAxis(None)
                return True

        return False
Example #4
0
    def event(self, event):
        super().event(event)

        if event.type == Event.MousePressEvent and self._controller.getToolsEnabled():
            if MouseEvent.LeftButton not in event.buttons:
                return False

            id = self._selection_pass.getIdAtPosition(event.x, event.y)
            if not id:
                return False

            if ToolHandle.isAxis(id):
                self.setLockedAxis(id)
                return True

        if event.type == Event.MouseReleaseEvent:
            if self.getLockedAxis():
                op = None
                if Selection.getCount() == 1:
                    node = Selection.getSelectedObject(0)
                    mirror = Vector(1,1,1)
                    if self.getLockedAxis() == ToolHandle.XAxis:
                        mirror.setX(-1)
                    elif self.getLockedAxis() == ToolHandle.YAxis:
                        mirror.setY(-1)
                    elif self.getLockedAxis() == ToolHandle.ZAxis:
                        mirror.setZ(-1)

                    op = MirrorOperation(node, mirror, mirror_around_center = True)
                else:
                    op = GroupedOperation()

                    for node in Selection.getAllSelectedObjects():
                        mirror = node.getMirror()
                        if self.getLockedAxis() == ToolHandle.XAxis:
                            mirror.setX(-1)
                        elif self.getLockedAxis() == ToolHandle.YAxis:
                            mirror.setY(-1)
                        elif self.getLockedAxis() == ToolHandle.ZAxis:
                            mirror.setZ(-1)

                        op.addOperation(MirrorOperation(node, mirror, mirror_around_center = True))

                op.push()

                self.setLockedAxis(None)
                return True

        return False
Example #5
0
    def __iadd__(self, other):
        if not other.isValid():
            return self

        newMin = Vector()
        newMin.setX(min(self._min.x, other.left))
        newMin.setY(min(self._min.y, other.bottom))
        newMin.setZ(min(self._min.z, other.back))

        newMax = Vector()
        newMax.setX(max(self._max.x, other.right))
        newMax.setY(max(self._max.y, other.top))
        newMax.setZ(max(self._max.z, other.front))

        self._min = newMin
        self._max = newMax

        return self
Example #6
0
    def __iadd__(self, other):
        if not other.isValid():
            return self

        new_min = Vector()
        new_min.setX(min(self._min.x, other.left))
        new_min.setY(min(self._min.y, other.bottom))
        new_min.setZ(min(self._min.z, other.back))

        new_max = Vector()
        new_max.setX(max(self._max.x, other.right))
        new_max.setY(max(self._max.y, other.top))
        new_max.setZ(max(self._max.z, other.front))

        self._min = new_min
        self._max = new_max

        return self
Example #7
0
    def read(self, file_name, **kwargs):
        try:
            for id, reader in self._mesh_readers.items():
                result = reader.read(file_name)
                if result is not None:
                    if kwargs.get("center", True):
                        # If the result has a mesh and no children it needs to be centered
                        if result.getMeshData() and len(
                                result.getChildren()) == 0:
                            extents = result.getMeshData().getExtents()
                            move_vector = Vector()
                            move_vector.setX(extents.center.x)
                            move_vector.setY(
                                extents.center.y
                            )  # Ensure that bottom is on 0 (above plate)
                            move_vector.setZ(extents.center.z)
                            result.setCenterPosition(move_vector)

                            if result.getMeshData().getExtents().bottom != 0:
                                result.translate(
                                    Vector(
                                        0, -result.getMeshData().getExtents().
                                        bottom, 0))

                        # Move all the meshes of children so that toolhandles are shown in the correct place.
                        for node in result.getChildren():
                            if node.getMeshData():
                                extents = node.getMeshData().getExtents()
                                m = Matrix()
                                m.translate(-extents.center)
                                node.setMeshData(
                                    node.getMeshData().getTransformed(m))
                                node.translate(extents.center)
                    return result

        except OSError as e:
            Logger.log("e", str(e))

        Logger.log("w", "Unable to read file %s", file_name)
        return None  #unable to read
Example #8
0
    def redo(self):
        if self._set_scale:
            self._node.setScale(self._scale)
        elif self._add_scale:
            self._node.setScale(self._node.getScale() + self._scale)
        elif self._relative_scale:
            scale_factor = Vector()

            ## Ensure that the direction is correctly applied (it can be flipped due to mirror)
            if self._node.getScale().x > 0:
                scale_factor.setX(self._node.getScale().x + self._scale.x)
            else:
                scale_factor.setX(self._node.getScale().x - self._scale.x)
            if self._node.getScale().y > 0:
                scale_factor.setY(self._node.getScale().y + self._scale.y)
            else:
                scale_factor.setY(self._node.getScale().y - self._scale.y)
            if self._node.getScale().z > 0:
                scale_factor.setZ(self._node.getScale().z + self._scale.z)
            else:
                scale_factor.setZ(self._node.getScale().z - self._scale.z)

            current_scale = copy.deepcopy(self._node.getScale())
            scale_factor.setX(scale_factor.x / current_scale.x)
            scale_factor.setY(scale_factor.y / current_scale.y)
            scale_factor.setZ(scale_factor.z / current_scale.z)
            self._node.scale(scale_factor, SceneNode.TransformSpace.Parent)

            new_scale = copy.deepcopy(self._node.getScale())
            if self._snap:
                if(scale_factor.x != 1.0):
                    new_scale.setX(round(new_scale.x, 1))
                if(scale_factor.y != 1.0):
                    new_scale.setY(round(new_scale.y, 1))
                if(scale_factor.z != 1.0):
                    new_scale.setZ(round(new_scale.z, 1))

            # Enforce min size.
            if new_scale.x < self._min_scale and new_scale.x >=0:
                new_scale.setX(self._min_scale)
            if new_scale.y < self._min_scale and new_scale.y >=0:
                new_scale.setY(self._min_scale)
            if new_scale.z < self._min_scale and new_scale.z >=0:
                new_scale.setZ(self._min_scale)

            # Enforce min size (when mirrored)
            if new_scale.x > -self._min_scale and new_scale.x <=0:
                new_scale.setX(-self._min_scale)
            if new_scale.y > -self._min_scale and new_scale.y <=0:
                new_scale.setY(-self._min_scale)
            if new_scale.z > -self._min_scale and new_scale.z <=0:
                new_scale.setZ(-self._min_scale)
            self._node.setScale(new_scale, SceneNode.TransformSpace.World)
        else:
            self._node.scale(self._scale, SceneNode.TransformSpace.World)
Example #9
0
    def redo(self):
        if self._set_scale:
            self._node.setScale(self._scale)
        elif self._add_scale:
            self._node.setScale(self._node.getScale() + self._scale)
        elif self._relative_scale:
            scale_factor = Vector()

            ## Ensure that the direction is correctly applied (it can be flipped due to mirror)
            if self._node.getScale().x > 0:
                scale_factor.setX(self._node.getScale().x + self._scale.x)
            else:
                scale_factor.setX(self._node.getScale().x - self._scale.x)
            if self._node.getScale().y > 0:
                scale_factor.setY(self._node.getScale().y + self._scale.y)
            else:
                scale_factor.setY(self._node.getScale().y - self._scale.y)
            if self._node.getScale().z > 0:
                scale_factor.setZ(self._node.getScale().z + self._scale.z)
            else:
                scale_factor.setZ(self._node.getScale().z - self._scale.z)

            current_scale = copy.deepcopy(self._node.getScale())
            scale_factor.setX(scale_factor.x / current_scale.x)
            scale_factor.setY(scale_factor.y / current_scale.y)
            scale_factor.setZ(scale_factor.z / current_scale.z)
            self._node.scale(scale_factor, SceneNode.TransformSpace.Parent)

            new_scale = copy.deepcopy(self._node.getScale())
            if self._snap:
                if (scale_factor.x != 1.0):
                    new_scale.setX(round(new_scale.x, 1))
                if (scale_factor.y != 1.0):
                    new_scale.setY(round(new_scale.y, 1))
                if (scale_factor.z != 1.0):
                    new_scale.setZ(round(new_scale.z, 1))

            # Enforce min size.
            if new_scale.x < self._min_scale and new_scale.x >= 0:
                new_scale.setX(self._min_scale)
            if new_scale.y < self._min_scale and new_scale.y >= 0:
                new_scale.setY(self._min_scale)
            if new_scale.z < self._min_scale and new_scale.z >= 0:
                new_scale.setZ(self._min_scale)

            # Enforce min size (when mirrored)
            if new_scale.x > -self._min_scale and new_scale.x <= 0:
                new_scale.setX(-self._min_scale)
            if new_scale.y > -self._min_scale and new_scale.y <= 0:
                new_scale.setY(-self._min_scale)
            if new_scale.z > -self._min_scale and new_scale.z <= 0:
                new_scale.setZ(-self._min_scale)
            self._node.setScale(new_scale, SceneNode.TransformSpace.World)
        else:
            self._node.scale(self._scale, SceneNode.TransformSpace.World)
Example #10
0
    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

            build_volume_bounding_box = copy.deepcopy(self._build_volume.getBoundingBox())
            build_volume_bounding_box.setBottom(-9001) # Ignore intersections with the bottom

            # Mark the node as outside the build volume if the bounding box test fails.
            if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
                node._outside_buildarea = True
            else:
                node._outside_buildarea = False

            # Move it downwards if bottom is above platform
            move_vector = Vector()
            if not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down
                if bbox.bottom > 0:
                    move_vector.setY(-bbox.bottom)
            #if not Float.fuzzyCompare(bbox.bottom, 0.0):
            #   pass#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
            elif Preferences.getInstance().getValue("physics/automatic_push_free"):
                # 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 or node.getParent().callDecoration("isGroup") is not None:
                        continue
                        #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.
                    try:
                        head_hull = node.callDecoration("getConvexHullHead")
                        if head_hull:
                            overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull"))
                            if not overlap:
                                other_head_hull = other_node.callDecoration("getConvexHullHead")
                                if other_head_hull:
                                    overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull)
                        else:
                            overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_node.callDecoration("getConvexHull"))
                    except:
                        overlap = None #It can sometimes occur that the caclulated convex hull has no size, in which case there is no overlap.

                    if overlap is None:
                        continue
                    move_vector.setX(overlap[0] * 1.01)
                    move_vector.setZ(overlap[1] * 1.01)
            convex_hull = node.callDecoration("getConvexHull")
            if convex_hull:
                if not convex_hull.isValid():
                    return
                # 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()
Example #11
0
    def event(self, event):
        super().event(event)

        if event.type == Event.ToolActivateEvent:
            self._old_scale = Selection.getSelectedObject(0).getScale()
            for node in Selection.getAllSelectedObjects():
                node.boundingBoxChanged.connect(self.propertyChanged)

        if event.type == Event.ToolDeactivateEvent:
            for node in Selection.getAllSelectedObjects():
                node.boundingBoxChanged.disconnect(self.propertyChanged)

        # Handle modifier keys: Shift toggles snap, Control toggles uniform scaling
        if event.type == Event.KeyPressEvent:
            if event.key == KeyEvent.ShiftKey:
                self._snap_scale = False
                self.propertyChanged.emit()
            elif event.key == KeyEvent.ControlKey:
                self._non_uniform_scale = True
                self.propertyChanged.emit()

        if event.type == Event.KeyReleaseEvent:
            if event.key == KeyEvent.ShiftKey:
                self._snap_scale = True
                self.propertyChanged.emit()
            elif event.key == KeyEvent.ControlKey:
                self._non_uniform_scale = False
                self.propertyChanged.emit()

        if event.type == Event.MousePressEvent and self._controller.getToolsEnabled():
            # Initialise a scale operation
            if MouseEvent.LeftButton not in event.buttons:
                return False

            id = self._selection_pass.getIdAtPosition(event.x, event.y)
            if not id:
                return False

            if ToolHandle.isAxis(id):
                self.setLockedAxis(id)

            # Save the current positions of the node, as we want to scale arround their current centres
            self._saved_node_positions = []
            for node in Selection.getAllSelectedObjects():
                self._saved_node_positions.append((node, node.getWorldPosition()))

            self._saved_handle_position = self._handle.getWorldPosition()

            if id == ToolHandle.XAxis:
                self.setDragPlane(Plane(Vector(0, 0, 1), self._saved_handle_position.z))
            elif id == ToolHandle.YAxis:
                self.setDragPlane(Plane(Vector(0, 0, 1), self._saved_handle_position.z))
            elif id == ToolHandle.ZAxis:
                self.setDragPlane(Plane(Vector(0, 1, 0), self._saved_handle_position.y))
            else:
                self.setDragPlane(Plane(Vector(0, 1, 0), self._saved_handle_position.y))

            self.setDragStart(event.x, event.y)
            self.operationStarted.emit(self)

        if event.type == Event.MouseMoveEvent:
            # Perform a scale operation
            if not self.getDragPlane():
                return False

            drag_position = self.getDragPosition(event.x, event.y)
            if drag_position:
                drag_length = (drag_position - self._saved_handle_position).length()
                if self._drag_length > 0:
                    drag_change = (drag_length - self._drag_length) / 100 * self._scale_speed
                    if self._snap_scale:
                        scale_factor = round(drag_change, 1)
                    else:
                        scale_factor = drag_change
                    if scale_factor:
                        scale_change = Vector(0.0, 0.0, 0.0)
                        if self._non_uniform_scale:
                            if self.getLockedAxis() == ToolHandle.XAxis:
                                scale_change.setX(scale_factor)
                            elif self.getLockedAxis() == ToolHandle.YAxis:
                                scale_change.setY(scale_factor)
                            elif self.getLockedAxis() == ToolHandle.ZAxis:
                                scale_change.setZ(scale_factor)
                        else:
                            scale_change.setX(scale_factor)
                            scale_change.setY(scale_factor)
                            scale_change.setZ(scale_factor)

                        # Scale around the saved centeres of all selected nodes
                        op = GroupedOperation()
                        for node, position in self._saved_node_positions:
                            op.addOperation(ScaleOperation(node, scale_change, relative_scale = True, scale_around_point = position))
                        op.push()
                        self._drag_length = (self._saved_handle_position - drag_position).length()
                else:
                    self._drag_length = (self._saved_handle_position - drag_position).length() #First move, do nothing but set right length.
                return True

        if event.type == Event.MouseReleaseEvent:
            # Finish a scale operation
            if self.getDragPlane():
                self.setDragPlane(None)
                self.setLockedAxis(None)
                self._drag_length = 0
                self.operationStopped.emit(self)
                return True
Example #12
0
    def event(self, event):
        super().event(event)

        if event.type == Event.ToolActivateEvent:
            self._old_scale = Selection.getSelectedObject(0).getScale()
            for node in Selection.getAllSelectedObjects():
                node.boundingBoxChanged.connect(self.propertyChanged)

        if event.type == Event.ToolDeactivateEvent:
            for node in Selection.getAllSelectedObjects():
                node.boundingBoxChanged.disconnect(self.propertyChanged)

        if event.type == Event.KeyPressEvent:
            if event.key == KeyEvent.ShiftKey:
                self._snap_scale = False
                self.propertyChanged.emit()
            elif event.key == KeyEvent.ControlKey:
                self._non_uniform_scale = True
                self.propertyChanged.emit()

        if event.type == Event.KeyReleaseEvent:
            if event.key == KeyEvent.ShiftKey:
                self._snap_scale = True
                self.propertyChanged.emit()
            elif event.key == KeyEvent.ControlKey:
                self._non_uniform_scale = False
                self.propertyChanged.emit()

        if event.type == Event.MousePressEvent and self._controller.getToolsEnabled():
            if MouseEvent.LeftButton not in event.buttons:
                return False

            id = self._selection_pass.getIdAtPosition(event.x, event.y)
            if not id:
                return False

            if ToolHandle.isAxis(id):
                self.setLockedAxis(id)

            self._saved_handle_position = self._handle.getWorldPosition()

            if id == ToolHandle.XAxis:
                self.setDragPlane(Plane(Vector(0, 0, 1), self._saved_handle_position.z))
            elif id == ToolHandle.YAxis:
                self.setDragPlane(Plane(Vector(0, 0, 1), self._saved_handle_position.z))
            elif id == ToolHandle.ZAxis:
                self.setDragPlane(Plane(Vector(0, 1, 0), self._saved_handle_position.y))
            else:
                self.setDragPlane(Plane(Vector(0, 1, 0), self._saved_handle_position.y))

            self.setDragStart(event.x, event.y)
            self.operationStarted.emit(self)

        if event.type == Event.MouseMoveEvent:
            if not self.getDragPlane():
                return False

            #handle_position = self._handle.getWorldPosition()
            drag_position = self.getDragPosition(event.x, event.y)
            if drag_position:
                drag_length = (drag_position - self._saved_handle_position).length()
                if self._drag_length > 0:
                    drag_change = (drag_length - self._drag_length) / 100 * self._scale_speed

                    scale_factor = drag_change

                    scale_change = Vector(0.0, 0.0, 0.0)
                    if self._non_uniform_scale:
                        if self.getLockedAxis() == ToolHandle.XAxis:
                            scale_change.setX(scale_factor)
                        elif self.getLockedAxis() == ToolHandle.YAxis:
                            scale_change.setY(scale_factor)
                        elif self.getLockedAxis() == ToolHandle.ZAxis:
                            scale_change.setZ(scale_factor)
                    else:
                        scale_change.setX(scale_factor)
                        scale_change.setY(scale_factor)
                        scale_change.setZ(scale_factor)

                    Selection.applyOperation(ScaleOperation, scale_change, relative_scale = True, snap = self._snap_scale)

                self._drag_length = (self._saved_handle_position - drag_position).length()
                return True

        if event.type == Event.MouseReleaseEvent:
            if self.getDragPlane():
                self.setDragPlane(None)
                self.setLockedAxis(None)
                self._drag_length = 0
                self.operationStopped.emit(self)
                return True
Example #13
0
class AxisAlignedBox:
    class IntersectionResult:
        NoIntersection = 1
        PartialIntersection = 2
        FullIntersection = 3

    def __init__(self, *args, **kwargs):
        super().__init__()

        self._min = Vector()
        self._max = Vector()

        if len(args) == 3:
            self.setLeft(-args[0] / 2)
            self.setRight(args[0] / 2)

            self.setTop(args[1] / 2)
            self.setBottom(-args[1] / 2)

            self.setBack(-args[2] / 2)
            self.setFront(args[2] / 2)

        if "minimum" in kwargs:
            self._min = kwargs["minimum"]

        if "maximum" in kwargs:
            self._max = kwargs["maximum"]
            
        ## Sometimes the min and max are not correctly set. 
        if not self._max:
            self._max = Vector()
            
        if not self._min:
            self._min = Vector()    

        self._ensureMinMax()

    def __add__(self, other):
        b = AxisAlignedBox()
        b += self
        b += other
        return b

    def __iadd__(self, other):
        if not other.isValid():
            return self

        newMin = Vector()
        newMin.setX(min(self._min.x, other.left))
        newMin.setY(min(self._min.y, other.bottom))
        newMin.setZ(min(self._min.z, other.back))

        newMax = Vector()
        newMax.setX(max(self._max.x, other.right))
        newMax.setY(max(self._max.y, other.top))
        newMax.setZ(max(self._max.z, other.front))

        self._min = newMin
        self._max = newMax

        return self

    #def __sub__(self, other):
        #b = AxisAlignedBox()
        #b += self
        #b -= other
        #return b

    #def __isub__(self, other):
        #self._dimensions -= other._dimensions
        #self._center = self._dimensions / 2.0
        #return self

    @property
    def width(self):
        return self._max.x - self._min.x

    @property
    def height(self):
        return self._max.y - self._min.y

    @property
    def depth(self):
        return self._max.z - self._min.z

    @property
    def center(self):
        return self._min + ((self._max - self._min) / 2.0)

    @property
    def left(self):
        return self._min.x

    def setLeft(self, value):
        self._min.setX(value)
        self._ensureMinMax()

    @property
    def right(self):
        return self._max.x

    def setRight(self, value):
        self._max.setX(value)
        self._ensureMinMax()

    @property
    def bottom(self):
        return self._min.y

    def setBottom(self, value):
        self._min.setY(value)
        self._ensureMinMax()

    @property
    def top(self):
        return self._max.y

    def setTop(self, value):
        self._max.setY(value)
        self._ensureMinMax()

    @property
    def back(self):
        return self._min.z

    def setBack(self, value):
        self._min.setZ(value)
        self._ensureMinMax()

    @property
    def front(self):
        return self._max.z

    def setFront(self, value):
        self._max.setZ(value)
        self._ensureMinMax()

    @property
    def minimum(self):
        return self._min

    def setMinimum(self, m):
        self._min = m
        self._ensureMinMax()

    @property
    def maximum(self):
        return self._max

    def setMaximum(self, m):
        self._max = m
        self._ensureMinMax()

    ##  Check if the bounding box is valid.
    #   Uses fuzzycompare to validate.
    #   \sa Float::fuzzyCompare()
    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))

    ##  Intersect the bounding box with a ray 
    #   \param ray \type{Ray}
    #   \sa Ray
    def intersectsRay(self, ray):
        inv = ray.inverseDirection

        t = numpy.empty((2,3), dtype=numpy.float32)
        t[0, 0] = inv.x * (self._min.x - ray.origin.x)
        t[0, 1] = inv.y * (self._min.y - ray.origin.y)
        t[0, 2] = inv.z * (self._min.z - ray.origin.z)
        t[1, 0] = inv.x * (self._max.x - ray.origin.x)
        t[1, 1] = inv.y * (self._max.y - ray.origin.y)
        t[1, 2] = inv.z * (self._max.z - ray.origin.z)

        tmin = numpy.min(t, axis=0)
        tmax = numpy.max(t, axis=0)

        largest_min = numpy.max(tmin)
        smallest_max = numpy.min(tmax)

        if smallest_max > largest_min:
            return (largest_min, smallest_max)
        else:
            return False

    ##  Check to see if this box intersects another box.
    #
    #   \param box \type{AxisAlignedBox} The box to check for intersection.
    #   \return \type{IntersectionResult} NoIntersection when no intersection occurs, PartialIntersection when partially intersected, FullIntersection when box is fully contained inside this box.
    def intersectsBox(self, box):
        if self._min.x > box._max.x or box._min.x > self._max.x:
            return self.IntersectionResult.NoIntersection

        if self._min.y > box._max.y or box._min.y > self._max.y:
            return self.IntersectionResult.NoIntersection

        if self._min.z > box._max.z or box._min.z > self._max.z:
            return self.IntersectionResult.NoIntersection

        if box._min >= self._min and box._max <= self._max:
            return self.IntersectionResult.FullIntersection

        return self.IntersectionResult.PartialIntersection

    ##  private:

    #   Ensure min contains the minimum values and max contains the maximum values
    def _ensureMinMax(self):
        if self._max.x < self._min.x:
            x = self._min.x
            self._min.setX(self._max.x)
            self._max.setX(x)

        if self._max.y < self._min.y:
            y = self._min.y
            self._min.setY(self._max.y)
            self._max.setY(y)

        if self._max.z < self._min.z:
            z = self._min.z
            self._min.setZ(self._max.z)
            self._max.setZ(z)

    def __repr__(self):
        return "AxisAlignedBox(min = {0}, max = {1})".format(self._min, self._max)
Example #14
0
class AxisAlignedBox:
    class IntersectionResult:
        NoIntersection = 1
        PartialIntersection = 2
        FullIntersection = 3

    def __init__(self, *args, **kwargs):
        super().__init__()

        self._min = Vector()
        self._max = Vector()

        if len(args) == 3:
            self.setLeft(-args[0] / 2)
            self.setRight(args[0] / 2)

            self.setTop(args[1] / 2)
            self.setBottom(-args[1] / 2)

            self.setBack(-args[2] / 2)
            self.setFront(args[2] / 2)

        if "minimum" in kwargs:
            self._min = kwargs["minimum"]

        if "maximum" in kwargs:
            self._max = kwargs["maximum"]
            
        ## Sometimes the min and max are not correctly set. 
        if not self._max:
            self._max = Vector()
            
        if not self._min:
            self._min = Vector()    

        self._ensureMinMax()

    def __add__(self, other):
        b = AxisAlignedBox()
        b += self
        b += other
        return b

    def __iadd__(self, other):
        if not other.isValid():
            return self

        new_min = Vector()
        new_min.setX(min(self._min.x, other.left))
        new_min.setY(min(self._min.y, other.bottom))
        new_min.setZ(min(self._min.z, other.back))

        new_max = Vector()
        new_max.setX(max(self._max.x, other.right))
        new_max.setY(max(self._max.y, other.top))
        new_max.setZ(max(self._max.z, other.front))

        self._min = new_min
        self._max = new_max

        return self

    #def __sub__(self, other):
        #b = AxisAlignedBox()
        #b += self
        #b -= other
        #return b

    #def __isub__(self, other):
        #self._dimensions -= other._dimensions
        #self._center = self._dimensions / 2.0
        #return self

    @property
    def width(self):
        return self._max.x - self._min.x

    @property
    def height(self):
        return self._max.y - self._min.y

    @property
    def depth(self):
        return self._max.z - self._min.z

    @property
    def center(self):
        return self._min + ((self._max - self._min) / 2.0)

    @property
    def left(self):
        return self._min.x

    def setLeft(self, value):
        self._min.setX(value)
        self._ensureMinMax()

    @property
    def right(self):
        return self._max.x

    def setRight(self, value):
        self._max.setX(value)
        self._ensureMinMax()

    @property
    def bottom(self):
        return self._min.y

    def setBottom(self, value):
        self._min.setY(value)
        self._ensureMinMax()

    @property
    def top(self):
        return self._max.y

    def setTop(self, value):
        self._max.setY(value)
        self._ensureMinMax()

    @property
    def back(self):
        return self._min.z

    def setBack(self, value):
        self._min.setZ(value)
        self._ensureMinMax()

    @property
    def front(self):
        return self._max.z

    def setFront(self, value):
        self._max.setZ(value)
        self._ensureMinMax()

    @property
    def minimum(self):
        return self._min

    def setMinimum(self, m):
        self._min = m
        self._ensureMinMax()

    @property
    def maximum(self):
        return self._max

    def setMaximum(self, m):
        self._max = m
        self._ensureMinMax()

    ##  Check if the bounding box is valid.
    #   Uses fuzzycompare to validate.
    #   \sa Float::fuzzyCompare()
    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))

    ##  Intersect the bounding box with a ray 
    #   \param ray \type{Ray}
    #   \sa Ray
    def intersectsRay(self, ray):
        inv = ray.inverseDirection

        t = numpy.empty((2,3), dtype=numpy.float32)
        t[0, 0] = inv.x * (self._min.x - ray.origin.x)
        t[0, 1] = inv.y * (self._min.y - ray.origin.y)
        t[0, 2] = inv.z * (self._min.z - ray.origin.z)
        t[1, 0] = inv.x * (self._max.x - ray.origin.x)
        t[1, 1] = inv.y * (self._max.y - ray.origin.y)
        t[1, 2] = inv.z * (self._max.z - ray.origin.z)

        tmin = numpy.min(t, axis=0)
        tmax = numpy.max(t, axis=0)

        largest_min = numpy.max(tmin)
        smallest_max = numpy.min(tmax)

        if smallest_max > largest_min:
            return (largest_min, smallest_max)
        else:
            return False

    ##  Check to see if this box intersects another box.
    #
    #   \param box \type{AxisAlignedBox} The box to check for intersection.
    #   \return \type{IntersectionResult} NoIntersection when no intersection occurs, PartialIntersection when partially intersected, FullIntersection when box is fully contained inside this box.
    def intersectsBox(self, box):
        if self._min.x > box._max.x or box._min.x > self._max.x:
            return self.IntersectionResult.NoIntersection

        if self._min.y > box._max.y or box._min.y > self._max.y:
            return self.IntersectionResult.NoIntersection

        if self._min.z > box._max.z or box._min.z > self._max.z:
            return self.IntersectionResult.NoIntersection

        if box._min >= self._min and box._max <= self._max:
            return self.IntersectionResult.FullIntersection

        return self.IntersectionResult.PartialIntersection

    ##  private:

    #   Ensure min contains the minimum values and max contains the maximum values
    def _ensureMinMax(self):
        if self._max.x < self._min.x:
            x = self._min.x
            self._min.setX(self._max.x)
            self._max.setX(x)

        if self._max.y < self._min.y:
            y = self._min.y
            self._min.setY(self._max.y)
            self._max.setY(y)

        if self._max.z < self._min.z:
            z = self._min.z
            self._min.setZ(self._max.z)
            self._max.setZ(z)

    def __repr__(self):
        return "AxisAlignedBox(min = {0}, max = {1})".format(self._min, self._max)
Example #15
0
    def event(self, event):
        super().event(event)

        if event.type == Event.ToolActivateEvent:
            for node in Selection.getAllSelectedObjects():
                node.boundingBoxChanged.connect(self.propertyChanged)

        if event.type == Event.ToolDeactivateEvent:
            for node in Selection.getAllSelectedObjects():
                node.boundingBoxChanged.disconnect(self.propertyChanged)

        if event.type == Event.KeyPressEvent:
            if event.key == KeyEvent.ShiftKey:
                self._lock_steps = False
                self.propertyChanged.emit()
            elif event.key == KeyEvent.ControlKey:
                self._non_uniform_scale = True
                self.propertyChanged.emit()

        if event.type == Event.KeyReleaseEvent:
            if event.key == KeyEvent.ShiftKey:
                self._lock_steps = True
                self.propertyChanged.emit()
            elif event.key == KeyEvent.ControlKey:
                self._non_uniform_scale = False
                self.propertyChanged.emit()

        if event.type == Event.MousePressEvent:
            if MouseEvent.LeftButton not in event.buttons:
                return False

            id = self._renderer.getIdAtCoordinate(event.x, event.y)
            if not id:
                return False

            if ToolHandle.isAxis(id):
                self.setLockedAxis(id)

            handle_position = self._handle.getWorldPosition()

            if id == ToolHandle.XAxis:
                self.setDragPlane(Plane(Vector(0, 0, 1), handle_position.z))
            elif id == ToolHandle.YAxis:
                self.setDragPlane(Plane(Vector(0, 0, 1), handle_position.z))
            elif id == ToolHandle.ZAxis:
                self.setDragPlane(Plane(Vector(0, 1, 0), handle_position.y))
            else:
                self.setDragPlane(Plane(Vector(0, 1, 0), handle_position.y))

            self.setDragStart(event.x, event.y)
            self.operationStarted.emit(self)

        if event.type == Event.MouseMoveEvent:
            if not self.getDragPlane():
                return False

            handle_position = self._handle.getWorldPosition()
            drag_position = self.getDragPosition(event.x, event.y)
            if drag_position:
                drag_length = (drag_position - handle_position).length()
                if self._drag_length > 0:
                    drag_change = (drag_length - self._drag_length) / 100

                    if self._snap_scale and abs(drag_change) < self._snap_amount:
                        return False

                    scale = Vector(1.0, 1.0, 1.0)
                    if self._non_uniform_scale:
                        if self.getLockedAxis() == ToolHandle.XAxis:
                            scale.setX(1.0 + drag_change)
                        elif self.getLockedAxis() == ToolHandle.YAxis:
                            scale.setY(1.0 + drag_change)
                        elif self.getLockedAxis() == ToolHandle.ZAxis:
                            scale.setZ(1.0 + drag_change)

                    if scale == Vector(1.0, 1.0, 1.0):
                        scale.setX(1.0 + drag_change)
                        scale.setY(1.0 + drag_change)
                        scale.setZ(1.0 + drag_change)

                    Selection.applyOperation(ScaleOperation, scale)

                self._drag_length = (handle_position - drag_position).length()
                return True

        if event.type == Event.MouseReleaseEvent:
            if self.getDragPlane():
                self.setDragPlane(None)
                self.setLockedAxis(None)
                self._drag_length = 0
                self.operationStopped.emit(self)
                return True
Example #16
0
    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()
Example #17
0
    def event(self, event):
        super().event(event)

        if event.type == Event.ToolActivateEvent:
            self._old_scale = Selection.getSelectedObject(0).getScale()
            for node in Selection.getAllSelectedObjects():
                node.boundingBoxChanged.connect(self.propertyChanged)

        if event.type == Event.ToolDeactivateEvent:
            for node in Selection.getAllSelectedObjects():
                node.boundingBoxChanged.disconnect(self.propertyChanged)

        if event.type == Event.KeyPressEvent:
            if event.key == KeyEvent.ShiftKey:
                self._snap_scale = False
                self.propertyChanged.emit()
            elif event.key == KeyEvent.ControlKey:
                self._non_uniform_scale = True
                self.propertyChanged.emit()

        if event.type == Event.KeyReleaseEvent:
            if event.key == KeyEvent.ShiftKey:
                self._snap_scale = True
                self.propertyChanged.emit()
            elif event.key == KeyEvent.ControlKey:
                self._non_uniform_scale = False
                self.propertyChanged.emit()

        if event.type == Event.MousePressEvent:
            if MouseEvent.LeftButton not in event.buttons:
                return False

            id = self._selection_pass.getIdAtPosition(event.x, event.y)
            if not id:
                return False

            if ToolHandle.isAxis(id):
                self.setLockedAxis(id)

            handle_position = self._handle.getWorldPosition()

            if id == ToolHandle.XAxis:
                self.setDragPlane(Plane(Vector(0, 0, 1), handle_position.z))
            elif id == ToolHandle.YAxis:
                self.setDragPlane(Plane(Vector(0, 0, 1), handle_position.z))
            elif id == ToolHandle.ZAxis:
                self.setDragPlane(Plane(Vector(0, 1, 0), handle_position.y))
            else:
                self.setDragPlane(Plane(Vector(0, 1, 0), handle_position.y))

            self.setDragStart(event.x, event.y)
            self.operationStarted.emit(self)

        if event.type == Event.MouseMoveEvent:
            if not self.getDragPlane():
                return False

            handle_position = self._handle.getWorldPosition()
            drag_position = self.getDragPosition(event.x, event.y)
            if drag_position:
                drag_length = (drag_position - handle_position).length()
                if self._drag_length > 0:
                    drag_change = (drag_length - self._drag_length) / 100 * self._scale_speed

                    if self._snap_scale:
                        scaleFactor = round(drag_change, 1)
                    else:
                        scaleFactor = drag_change

                    scale = Vector(0.0, 0.0, 0.0)
                    if self._non_uniform_scale:
                        if self.getLockedAxis() == ToolHandle.XAxis:
                            scale.setX(scaleFactor)
                        elif self.getLockedAxis() == ToolHandle.YAxis:
                            scale.setY(scaleFactor)
                        elif self.getLockedAxis() == ToolHandle.ZAxis:
                            scale.setZ(scaleFactor)

                    else:
                        scale.setX(scaleFactor)
                        scale.setY(scaleFactor)
                        scale.setZ(scaleFactor)

                    Selection.applyOperation(ScaleOperation, scale, add_scale=True)

                    #this part prevents the mesh being scaled to a size < 0.
                    #This cannot be done before the operation (even though that would be more efficient)
                    #because then the operation can distract more of the mesh then is remaining of its size
                    realWorldMeshScale = Selection.getSelectedObject(0).getScale()
                    if realWorldMeshScale.x <= 0 or realWorldMeshScale.y <= 0 or realWorldMeshScale.z <= 0:
                        minimumScale = 0.01 #1% so the mesh never completely disapears for the user
                        if self._snap_scale == True:
                            minimumScale = 0.1 #10% same reason as above
                        if realWorldMeshScale.x <= 0:
                            realWorldMeshScale.setX(minimumScale)
                        if realWorldMeshScale.y <= 0:
                            realWorldMeshScale.setY(minimumScale)
                        if realWorldMeshScale.z <= 0:
                            realWorldMeshScale.setZ(minimumScale)
                        Selection.applyOperation(SetTransformOperation, None, None, realWorldMeshScale)

                self._drag_length = (handle_position - drag_position).length()
                return True

        if event.type == Event.MouseReleaseEvent:
            if self.getDragPlane():
                self.setDragPlane(None)
                self.setLockedAxis(None)
                self._drag_length = 0
                self.operationStopped.emit(self)
                return True
    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

            build_volume_bounding_box = copy.deepcopy(
                self._build_volume.getBoundingBox())
            build_volume_bounding_box.setBottom(
                -9001)  # Ignore intersections with the bottom
            node._outside_buildarea = False

            # Mark the node as outside the build volume if the bounding box test fails.
            if build_volume_bounding_box.intersectsBox(
                    bbox
            ) != AxisAlignedBox.IntersectionResult.FullIntersection:
                node._outside_buildarea = True

            # Move it downwards if bottom is above platform
            move_vector = Vector()
            if not (node.getParent()
                    and node.getParent().callDecoration("isGroup")
                    ):  #If an object is grouped, don't move it down
                z_offset = node.callDecoration(
                    "getZOffset") if node.getDecorator(
                        ZOffsetDecorator.ZOffsetDecorator) else 0
                if bbox.bottom > 0:
                    move_vector.setY(-bbox.bottom + z_offset)
                elif bbox.bottom < z_offset:
                    move_vector.setY((-bbox.bottom) - z_offset)

            #if not Float.fuzzyCompare(bbox.bottom, 0.0):
            #   pass#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 Preferences.getInstance().getValue(
                    "physics/automatic_push_free"):
                # 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 or node.getParent(
                            ).callDecoration("isGroup") is not None:
                        continue
                        #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.
                    try:
                        head_hull = node.callDecoration("getConvexHullHead")
                        if head_hull:
                            overlap = head_hull.intersectsPolygon(
                                other_node.callDecoration("getConvexHullHead"))
                            if not overlap:
                                other_head_hull = other_node.callDecoration(
                                    "getConvexHullHead")
                                if other_head_hull:
                                    overlap = node.callDecoration(
                                        "getConvexHullHead").intersectsPolygon(
                                            other_head_hull)
                        else:
                            overlap = node.callDecoration(
                                "getConvexHull").intersectsPolygon(
                                    other_node.callDecoration("getConvexHull"))
                    except:
                        overlap = None  #It can sometimes occur that the caclulated convex hull has no size, in which case there is no overlap.

                    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:
                if not convex_hull.isValid():
                    return
                # 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()
Example #19
0
    def redo(self):
        if self._set_scale: #Simply change the scale.
            self._node.setScale(self._scale, SceneNode.TransformSpace.World)
        elif self._add_scale: #Add to the current scale.
            self._node.setScale(self._node.getScale() + self._scale)
        elif self._relative_scale: #Scale relatively to the current scale.
            scale_factor = Vector()
            ## Ensure that the direction is correctly applied (it can be flipped due to mirror)
            if self._scale.z == self._scale.y and self._scale.y == self._scale.x:
                ratio = (1 / (self._node.getScale().x + self._node.getScale().y + self._node.getScale().z)) * 3
                ratio_vector = ratio * copy.deepcopy(self._node.getScale())
                self._scale *= ratio_vector
            if self._node.getScale().x > 0:
                scale_factor.setX(abs(self._node.getScale().x + self._scale.x))
            else:
                scale_factor.setX(-abs(self._node.getScale().x - self._scale.x))
            if self._node.getScale().y > 0:
                scale_factor.setY(abs(self._node.getScale().y + self._scale.y))
            else:
                scale_factor.setY(-abs(self._node.getScale().y - self._scale.y))
            if self._node.getScale().z > 0:
                scale_factor.setZ(abs(self._node.getScale().z + self._scale.z))
            else:
                scale_factor.setZ(-abs(self._node.getScale().z - self._scale.z))

            current_scale = copy.deepcopy(self._node.getScale())

            if scale_factor.x != 0:
                scale_factor.setX(scale_factor.x / current_scale.x)
            if scale_factor.y != 0:
                scale_factor.setY(scale_factor.y / current_scale.y)
            if scale_factor.z != 0:
                scale_factor.setZ(scale_factor.z / current_scale.z)

            self._node.setPosition(-self._scale_around_point) #If scaling around a point, shift that point to the axis origin first and shift it back after performing the transformation.
            self._node.scale(scale_factor, SceneNode.TransformSpace.Parent)
            self._node.setPosition(self._scale_around_point)
            new_scale = copy.deepcopy(self._node.getScale())
            if self._snap:
                if(scale_factor.x != 1.0):
                    new_scale.setX(round(new_scale.x, 2))
                if(scale_factor.y != 1.0):
                    new_scale.setY(round(new_scale.y, 2))
                if(scale_factor.z != 1.0):
                    new_scale.setZ(round(new_scale.z, 2))

            # Enforce min size.
            if new_scale.x < self._min_scale and new_scale.x >= 0:
                new_scale.setX(self._min_scale)
            if new_scale.y < self._min_scale and new_scale.y >= 0:
                new_scale.setY(self._min_scale)
            if new_scale.z < self._min_scale and new_scale.z >= 0:
                new_scale.setZ(self._min_scale)

            # Enforce min size (when mirrored)
            if new_scale.x > -self._min_scale and new_scale.x <= 0:
                new_scale.setX(-self._min_scale)
            if new_scale.y > -self._min_scale and new_scale.y <= 0:
                new_scale.setY(-self._min_scale)
            if new_scale.z > -self._min_scale and new_scale.z <=0:
                new_scale.setZ(-self._min_scale)
            self._node.setScale(new_scale, SceneNode.TransformSpace.World)
        else:
            self._node.scale(self._scale, SceneNode.TransformSpace.World) #Default to _set_scale