From df25824820672c656f1f175dc5e208150024aaee Mon Sep 17 00:00:00 2001 From: David Vierra Date: Wed, 7 Oct 2015 15:32:00 -1000 Subject: [PATCH] Move PendingImport and PendingImportNode to imports.py --- src/mcedit2/editorsession.py | 140 +----------- src/mcedit2/editortools/clone.py | 2 +- src/mcedit2/editortools/move.py | 208 +----------------- src/mcedit2/imports.py | 352 +++++++++++++++++++++++++++++++ 4 files changed, 357 insertions(+), 345 deletions(-) create mode 100644 src/mcedit2/imports.py diff --git a/src/mcedit2/editorsession.py b/src/mcedit2/editorsession.py index 2b88699..88fd52b 100644 --- a/src/mcedit2/editorsession.py +++ b/src/mcedit2/editorsession.py @@ -4,7 +4,6 @@ import os from PySide import QtGui, QtCore from PySide.QtCore import Qt - from mcedit2.rendering.blockmodels import BlockModels from mcedit2 import editortools @@ -13,6 +12,7 @@ from mcedit2.editorcommands.fill import fillCommand from mcedit2.editorcommands.find_replace import FindReplaceDialog from mcedit2.editorcommands.analyze import AnalyzeOutputDialog from mcedit2.editortools.select import SelectCommand +from mcedit2.imports import PendingImport from mcedit2.panels.player import PlayerPanel from mcedit2.panels.map import MapPanel from mcedit2.panels.worldinfo import WorldInfoPanel @@ -23,7 +23,6 @@ from mcedit2.util.mimeformats import MimeFormats from mcedit2.util.resources import resourcePath from mcedit2.widgets.mcedockwidget import MCEDockWidget from mcedit2.widgets.spinslider import SpinSlider -from mceditlib.transform import DimensionTransform, SelectionTransform from mceditlib.util import exhaust from mceditlib.util.lazyprop import weakrefprop from mcedit2.util.raycast import rayCastInBounds @@ -57,143 +56,6 @@ log = logging.getLogger(__name__) sessionSettings = Settings().getNamespace("editorsession") currentViewSetting = sessionSettings.getOption("currentview", unicode, "cam") -class PendingImport(object): - """ - An object representing a schematic, etc that is currently being imported and can be - moved/rotated/scaled by the user. - - Parameters - ---------- - - sourceDim: WorldEditorDimension - The object that will be imported. - pos: Vector - The position in the currently edited world where the object will be imported. - selection: SelectionBox - Defines the portion of sourceDim that will be imported. For importing - .schematic files, this is usually the schematic's bounds. For importing/moving - a selected part of a world, this is the shaped selection created by the - Select tool. - text: unicode - A user-readable name for this import to be displayed in the "pending imports" - list, when multiple-importing is enabled. - isMove: bool - A flag that tells whether the imported object is being moved or copied. If it is - being moved, the previous position of the object is filled with air and cleared - of entities. - - Attributes - ---------- - - importPos: Vector - The effective position where the object is to be imported. When the - PendingImport's rotation or scale is the default, this will be the - same as `self.pos`, otherwise it will be the position where the transformed - object will be imported. Changing this attribute will also change `self.pos` - to the pre-transform position accordingly. - - importDim: WorldEditorDimension - The effective dimension to be imported. When the rotation or scale is the - default, this will be the same as `self.sourceDim`; otherwise, it will be a - TransformedDimension, which is a read-only proxy that acts as a scaled and - rotated form of `self.sourceDim`. - - rotation: tuple of float - The rotation transform to be applied during import, in the form - (rotX, rotY, rotZ). Rotation is applied around the center point given - by `self.rotateAnchor` - - scale: tuple of float - The scale transform to be applied during import, in the form - (rotX, rotY, rotZ). Scaling is applied around the center point given - by `self.rotateAnchor` - - rotateAnchor: Vector - The anchor point that acts as the "center" when applying rotation - and scale transforms, in source coordinates. By default, - this is the center of `self.selection`. - - bounds: BoundingBox - The axis-aligned bounding box that completely encloses `self.selection`, moved - to the position given by `self.pos`, in destination coordinates. - - importBounds: BoundingBox - The axis-aligned bounding box that completely encloses the transformed dimension - `self.transformedDim` in destination coordinates. - - """ - def __init__(self, sourceDim, pos, selection, text, isMove=False): - self.selection = selection - self.text = text - self.pos = pos - self.sourceDim = sourceDim - self.isMove = isMove - self._rotation = (0, 0, 0) - self._scale = (0, 0, 0) - self.transformedDim = None - - bounds = self.selection - self.rotateAnchor = bounds.origin + bounds.size * 0.5 - - @property - def importPos(self): - if self.transformedDim is None: - return self.pos - return self.pos + self.transformedDim.bounds.origin - self.selection.origin - - @importPos.setter - def importPos(self, pos): - if self.transformedDim is None: - self.pos = pos - else: - self.pos = pos - self.transformedDim.bounds.origin + self.selection.origin - - @property - def importDim(self): - if self.transformedDim is not None: - return self.transformedDim - else: - return self.sourceDim - - @property - def rotation(self): - return self._rotation - - @rotation.setter - def rotation(self, value): - self._rotation = value - self.updateTransform() - - @property - def scale(self): - return self._rotation - - @scale.setter - def scale(self, value): - self._rotation = value - self.updateTransform() - - def updateTransform(self): - if self.rotation == (0, 0, 0) and self.scale == (0, 0, 0): - self.transformedDim = None - else: - selectionDim = SelectionTransform(self.sourceDim, self.selection) - self.transformedDim = DimensionTransform(selectionDim, self.rotateAnchor, *self.rotation) - - def __repr__(self): - return "%s(%r, %r, %r)" % (self.__class__.__name__, self.sourceDim, self.selection, self.pos) - - @property - def bounds(self): - return BoundingBox(self.pos, self.selection.size) - - @property - def importBounds(self): - if self.transformedDim is not None: - size = self.transformedDim.bounds.size - else: - size = self.selection.size - return BoundingBox(self.importPos, size) class PasteImportCommand(QtGui.QUndoCommand): def __init__(self, editorSession, pendingImport, text, *args, **kwargs): diff --git a/src/mcedit2/editortools/clone.py b/src/mcedit2/editortools/clone.py index 04cafcc..435f4a1 100644 --- a/src/mcedit2/editortools/clone.py +++ b/src/mcedit2/editortools/clone.py @@ -6,7 +6,7 @@ import logging from mcedit2.command import SimpleRevisionCommand from mcedit2.editorsession import PendingImport from mcedit2.editortools import EditorTool -from mcedit2.editortools.move import PendingImportNode +from mcedit2.imports import PendingImportNode, PendingImport from mcedit2.rendering.scenegraph import scenenode from PySide import QtGui from mcedit2.rendering.selection import boxFaceUnderCursor diff --git a/src/mcedit2/editortools/move.py b/src/mcedit2/editortools/move.py index 248f135..0d8c633 100644 --- a/src/mcedit2/editortools/move.py +++ b/src/mcedit2/editortools/move.py @@ -5,21 +5,14 @@ from __future__ import absolute_import, division, print_function import logging from PySide import QtGui, QtCore - from PySide.QtCore import Qt -from mcedit2.editorsession import PendingImport + from mcedit2.editortools import EditorTool from mcedit2.command import SimpleRevisionCommand -from mcedit2.handles.boxhandle import BoxHandle -from mcedit2.rendering.scenegraph.matrix import TranslateNode, RotateNode -from mcedit2.rendering.scenegraph.scenenode import Node -from mcedit2.rendering.selection import SelectionBoxNode, SelectionFaceNode, boxFaceUnderCursor +from mcedit2.imports import PendingImportNode, PendingImport from mcedit2.rendering.scenegraph import scenenode -from mcedit2.rendering.depths import DepthOffset -from mcedit2.rendering.worldscene import WorldScene from mcedit2.util.load_ui import load_ui from mcedit2.util.showprogress import showProgress -from mcedit2.util.worldloader import WorldLoader from mcedit2.widgets.coord_widget import CoordinateWidget from mcedit2.widgets.layout import Column from mceditlib.export import extractSchematicFromIter @@ -64,6 +57,7 @@ class MoveOffsetCommand(QtGui.QUndoCommand): def redo(self): self.moveTool.movePosition = self.newPoint + class MoveFinishCommand(SimpleRevisionCommand): def __init__(self, moveTool, pendingImport, *args, **kwargs): super(MoveFinishCommand, self).__init__(moveTool.editorSession, moveTool.tr("Finish Move"), *args, **kwargs) @@ -83,202 +77,6 @@ class MoveFinishCommand(SimpleRevisionCommand): self.moveTool.removePendingImport(self.pendingImport) -class Rotate3DNode(Node): - def __init__(self): - super(Rotate3DNode, self).__init__() - self.anchorNode = TranslateNode() - self.rotXNode = RotateNode(axis=(1, 0, 0)) - self.rotYNode = RotateNode(axis=(0, 1, 0)) - self.rotZNode = RotateNode(axis=(0, 0, 1)) - self.recenterNode = TranslateNode() - - super(Rotate3DNode, self).addChild(self.anchorNode) - self.anchorNode.addChild(self.rotXNode) - self.rotXNode.addChild(self.rotYNode) - self.rotYNode.addChild(self.rotZNode) - self.rotZNode.addChild(self.recenterNode) - - def addChild(self, node): - self.recenterNode.addChild(node) - - def removeChild(self, node): - self.recenterNode.removeChild(node) - - def setRotation(self, rots): - rx, ry, rz = rots - self.rotXNode.degrees = rx - self.rotYNode.degrees = ry - self.rotZNode.degrees = rz - - def setAnchor(self, point): - self.anchorNode.translateOffset = point - self.recenterNode.translateOffset = -point - - -class PendingImportNode(Node, QtCore.QObject): - __node_id_counter = 0 - - def __init__(self, pendingImport, textureAtlas): - super(PendingImportNode, self).__init__() - PendingImportNode.__node_id_counter += 1 - self.id = PendingImportNode.__node_id_counter - - self.textureAtlas = textureAtlas - self.pendingImport = pendingImport - dim = pendingImport.sourceDim - - # positionTranslateNode contains the non-transformed preview of the imported - # object, including its world scene. This preview will be rotated model-wise - # while the user is dragging the rotate controls. - - self.positionTranslateNode = TranslateNode() - self.rotateNode = Rotate3DNode() - self.addChild(self.positionTranslateNode) - self.positionTranslateNode.addChild(self.rotateNode) - - self.rotateNode.setAnchor(self.pendingImport.bounds.size * 0.5) - - # worldSceneTranslateNode is contained by positionTranslateNode, and - # serves to translate the world scene back to 0, 0, 0 so the positionTranslateNode - # can translate by the current position. - - self.worldSceneTranslateNode = TranslateNode() - self.worldScene = WorldScene(dim, textureAtlas, bounds=pendingImport.selection) - self.worldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer - - # transformedWorldTranslateNode contains the transformed preview of the imported - # object, including a world scene that displays the object wrapped by a - # DimensionTransform. - - self.transformedWorldTranslateNode = TranslateNode() - self.transformedWorldScene = None - self.addChild(self.transformedWorldTranslateNode) - - self.worldSceneTranslateNode.translateOffset = -self.pendingImport.selection.origin - self.worldSceneTranslateNode.addChild(self.worldScene) - self.rotateNode.addChild(self.worldSceneTranslateNode) - - # handleNode displays a bounding box that can be moved around, and responds - # to mouse events. - - box = BoundingBox(pendingImport.importPos, pendingImport.importBounds.size) - - self.handleNode = BoxHandle() - self.handleNode.bounds = box - self.handleNode.resizable = False - - self.updateTransformedScene() - self.pos = pendingImport.pos - - self.handleNode.boundsChanged.connect(self.handleBoundsChanged) - self.handleNode.boundsChangedDone.connect(self.handleBoundsChangedDone) - - self.addChild(self.handleNode) - - # loads the non-transformed world scene asynchronously. - self.loader = WorldLoader(self.worldScene, - list(pendingImport.selection.chunkPositions())) - self.loader.startLoader() - - # Emitted when the user finishes dragging the box handle and releases the mouse - # button. Arguments are (newPosition, oldPosition). - importMoved = QtCore.Signal(object, object) - - def handleBoundsChangedDone(self, bounds, oldBounds): - point = self.getPosFromBox(bounds.origin) - oldPoint = self.getPosFromBox(oldBounds.origin) - if point != oldPoint: - self.importMoved.emit(point, oldPoint) - - def handleBoundsChanged(self, bounds): - point = self.getPosFromBox(bounds.origin) - if self.pos != point: - self.pos = point - - def getPosFromBox(self, point): - offset = self.pendingImport.pos - self.pendingImport.importPos - return point + offset - - def setPreviewRotation(self, rots): - self.rotateNode.visible = True - self.worldSceneTranslateNode.visible = True - self.transformedWorldTranslateNode.visible = False - self.rotateNode.setRotation(rots) - - def setRotation(self, rots): - self.pendingImport.rotation = rots - self.updateTransformedScene() - self.updateBoxHandle() - - def updateTransformedScene(self): - if self.pendingImport.transformedDim is not None: - log.info("Showing transformed scene") - self.rotateNode.visible = False - self.worldSceneTranslateNode.visible = False - self.transformedWorldTranslateNode.visible = True - - if self.transformedWorldScene: - self.transformedWorldTranslateNode.removeChild(self.transformedWorldScene) - - self.transformedWorldScene = WorldScene(self.pendingImport.transformedDim, - self.textureAtlas) - self.transformedWorldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer - self.transformedWorldTranslateNode.addChild(self.transformedWorldScene) - - self.updateTransformedSceneOffset() - - cPos = list(self.pendingImport.transformedDim.chunkPositions()) - self.loader = WorldLoader(self.transformedWorldScene, - cPos) - self.loader.startLoader() - - else: - log.info("Hiding transformed scene") - self.rotateNode.visible = True - self.worldSceneTranslateNode.visible = True - self.transformedWorldTranslateNode.visible = False - if self.transformedWorldScene: - self.transformedWorldTranslateNode.removeChild(self.transformedWorldScene) - self.transformedWorldScene = None - - def updateTransformedSceneOffset(self): - self.transformedWorldTranslateNode.translateOffset = self.pos - self.pendingImport.rotateAnchor + self.pendingImport.bounds.size * 0.5 - - @property - def pos(self): - return self.positionTranslateNode.translateOffset - - @pos.setter - def pos(self, value): - if value == self.positionTranslateNode.translateOffset: - return - - self.positionTranslateNode.translateOffset = Vector(*value) - self.updateTransformedSceneOffset() - self.updateBoxHandle() - - def updateBoxHandle(self): - if self.transformedWorldScene is None: - bounds = BoundingBox(self.pos, self.pendingImport.bounds.size) - else: - origin = self.pos - self.pendingImport.pos + self.pendingImport.importPos - bounds = BoundingBox(origin, self.pendingImport.importBounds.size) - #if self.handleNode.bounds.size != bounds.size: - self.handleNode.bounds = bounds - - # --- Mouse events --- - - # inherit from BoxHandle? - def mouseMove(self, event): - self.handleNode.mouseMove(event) - - def mousePress(self, event): - self.handleNode.mousePress(event) - - def mouseRelease(self, event): - self.handleNode.mouseRelease(event) - - class RotationWidget(QtGui.QWidget): def __init__(self): super(RotationWidget, self).__init__() diff --git a/src/mcedit2/imports.py b/src/mcedit2/imports.py new file mode 100644 index 0000000..6aa826b --- /dev/null +++ b/src/mcedit2/imports.py @@ -0,0 +1,352 @@ +""" + imports +""" +from __future__ import absolute_import, division, print_function, unicode_literals +import logging +from PySide import QtCore +from mcedit2.handles.boxhandle import BoxHandle +from mcedit2.rendering.depths import DepthOffset +from mcedit2.rendering.scenegraph.matrix import TranslateNode, RotateNode +from mcedit2.rendering.scenegraph.scenenode import Node +from mcedit2.rendering.worldscene import WorldScene +from mcedit2.util.worldloader import WorldLoader +from mceditlib.geometry import Vector +from mceditlib.selection import BoundingBox +from mceditlib.transform import SelectionTransform, DimensionTransform + +log = logging.getLogger(__name__) + + +class PendingImportNode(Node, QtCore.QObject): + __node_id_counter = 0 + + def __init__(self, pendingImport, textureAtlas): + super(PendingImportNode, self).__init__() + PendingImportNode.__node_id_counter += 1 + self.id = PendingImportNode.__node_id_counter + + self.textureAtlas = textureAtlas + self.pendingImport = pendingImport + dim = pendingImport.sourceDim + + # positionTranslateNode contains the non-transformed preview of the imported + # object, including its world scene. This preview will be rotated model-wise + # while the user is dragging the rotate controls. + + self.positionTranslateNode = TranslateNode() + self.rotateNode = Rotate3DNode() + self.addChild(self.positionTranslateNode) + self.positionTranslateNode.addChild(self.rotateNode) + + self.rotateNode.setAnchor(self.pendingImport.bounds.size * 0.5) + + # worldSceneTranslateNode is contained by positionTranslateNode, and + # serves to translate the world scene back to 0, 0, 0 so the positionTranslateNode + # can translate by the current position. + + self.worldSceneTranslateNode = TranslateNode() + self.worldScene = WorldScene(dim, textureAtlas, bounds=pendingImport.selection) + self.worldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer + + # transformedWorldTranslateNode contains the transformed preview of the imported + # object, including a world scene that displays the object wrapped by a + # DimensionTransform. + + self.transformedWorldTranslateNode = TranslateNode() + self.transformedWorldScene = None + self.addChild(self.transformedWorldTranslateNode) + + self.worldSceneTranslateNode.translateOffset = -self.pendingImport.selection.origin + self.worldSceneTranslateNode.addChild(self.worldScene) + self.rotateNode.addChild(self.worldSceneTranslateNode) + + # handleNode displays a bounding box that can be moved around, and responds + # to mouse events. + + box = BoundingBox(pendingImport.importPos, pendingImport.importBounds.size) + + self.handleNode = BoxHandle() + self.handleNode.bounds = box + self.handleNode.resizable = False + + self.updateTransformedScene() + self.pos = pendingImport.pos + + self.handleNode.boundsChanged.connect(self.handleBoundsChanged) + self.handleNode.boundsChangedDone.connect(self.handleBoundsChangedDone) + + self.addChild(self.handleNode) + + # loads the non-transformed world scene asynchronously. + self.loader = WorldLoader(self.worldScene, + list(pendingImport.selection.chunkPositions())) + self.loader.startLoader() + + # Emitted when the user finishes dragging the box handle and releases the mouse + # button. Arguments are (newPosition, oldPosition). + importMoved = QtCore.Signal(object, object) + + def handleBoundsChangedDone(self, bounds, oldBounds): + point = self.getPosFromBox(bounds.origin) + oldPoint = self.getPosFromBox(oldBounds.origin) + if point != oldPoint: + self.importMoved.emit(point, oldPoint) + + def handleBoundsChanged(self, bounds): + point = self.getPosFromBox(bounds.origin) + if self.pos != point: + self.pos = point + + def getPosFromBox(self, point): + offset = self.pendingImport.pos - self.pendingImport.importPos + return point + offset + + def setPreviewRotation(self, rots): + self.rotateNode.visible = True + self.worldSceneTranslateNode.visible = True + self.transformedWorldTranslateNode.visible = False + self.rotateNode.setRotation(rots) + + def setRotation(self, rots): + self.pendingImport.rotation = rots + self.updateTransformedScene() + self.updateBoxHandle() + + def updateTransformedScene(self): + if self.pendingImport.transformedDim is not None: + log.info("Showing transformed scene") + self.rotateNode.visible = False + self.worldSceneTranslateNode.visible = False + self.transformedWorldTranslateNode.visible = True + + if self.transformedWorldScene: + self.transformedWorldTranslateNode.removeChild(self.transformedWorldScene) + + self.transformedWorldScene = WorldScene(self.pendingImport.transformedDim, + self.textureAtlas) + self.transformedWorldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer + self.transformedWorldTranslateNode.addChild(self.transformedWorldScene) + + self.updateTransformedSceneOffset() + + cPos = list(self.pendingImport.transformedDim.chunkPositions()) + self.loader = WorldLoader(self.transformedWorldScene, + cPos) + self.loader.startLoader() + + else: + log.info("Hiding transformed scene") + self.rotateNode.visible = True + self.worldSceneTranslateNode.visible = True + self.transformedWorldTranslateNode.visible = False + if self.transformedWorldScene: + self.transformedWorldTranslateNode.removeChild(self.transformedWorldScene) + self.transformedWorldScene = None + + def updateTransformedSceneOffset(self): + self.transformedWorldTranslateNode.translateOffset = self.pos - self.pendingImport.rotateAnchor + self.pendingImport.bounds.size * 0.5 + + @property + def pos(self): + return self.positionTranslateNode.translateOffset + + @pos.setter + def pos(self, value): + if value == self.positionTranslateNode.translateOffset: + return + + self.positionTranslateNode.translateOffset = Vector(*value) + self.updateTransformedSceneOffset() + self.updateBoxHandle() + + def updateBoxHandle(self): + if self.transformedWorldScene is None: + bounds = BoundingBox(self.pos, self.pendingImport.bounds.size) + else: + origin = self.pos - self.pendingImport.pos + self.pendingImport.importPos + bounds = BoundingBox(origin, self.pendingImport.importBounds.size) + #if self.handleNode.bounds.size != bounds.size: + self.handleNode.bounds = bounds + + # --- Mouse events --- + + # inherit from BoxHandle? + def mouseMove(self, event): + self.handleNode.mouseMove(event) + + def mousePress(self, event): + self.handleNode.mousePress(event) + + def mouseRelease(self, event): + self.handleNode.mouseRelease(event) + + +class PendingImport(object): + """ + An object representing a schematic, etc that is currently being imported and can be + moved/rotated/scaled by the user. + + Parameters + ---------- + + sourceDim: WorldEditorDimension + The object that will be imported. + pos: Vector + The position in the currently edited world where the object will be imported. + selection: SelectionBox + Defines the portion of sourceDim that will be imported. For importing + .schematic files, this is usually the schematic's bounds. For importing/moving + a selected part of a world, this is the shaped selection created by the + Select tool. + text: unicode + A user-readable name for this import to be displayed in the "pending imports" + list, when multiple-importing is enabled. + isMove: bool + A flag that tells whether the imported object is being moved or copied. If it is + being moved, the previous position of the object is filled with air and cleared + of entities. + + Attributes + ---------- + + importPos: Vector + The effective position where the object is to be imported. When the + PendingImport's rotation or scale is the default, this will be the + same as `self.pos`, otherwise it will be the position where the transformed + object will be imported. Changing this attribute will also change `self.pos` + to the pre-transform position accordingly. + + importDim: WorldEditorDimension + The effective dimension to be imported. When the rotation or scale is the + default, this will be the same as `self.sourceDim`; otherwise, it will be a + TransformedDimension, which is a read-only proxy that acts as a scaled and + rotated form of `self.sourceDim`. + + rotation: tuple of float + The rotation transform to be applied during import, in the form + (rotX, rotY, rotZ). Rotation is applied around the center point given + by `self.rotateAnchor` + + scale: tuple of float + The scale transform to be applied during import, in the form + (rotX, rotY, rotZ). Scaling is applied around the center point given + by `self.rotateAnchor` + + rotateAnchor: Vector + The anchor point that acts as the "center" when applying rotation + and scale transforms, in source coordinates. By default, + this is the center of `self.selection`. + + bounds: BoundingBox + The axis-aligned bounding box that completely encloses `self.selection`, moved + to the position given by `self.pos`, in destination coordinates. + + importBounds: BoundingBox + The axis-aligned bounding box that completely encloses the transformed dimension + `self.transformedDim` in destination coordinates. + + """ + def __init__(self, sourceDim, pos, selection, text, isMove=False): + self.selection = selection + self.text = text + self.pos = pos + self.sourceDim = sourceDim + self.isMove = isMove + self._rotation = (0, 0, 0) + self._scale = (0, 0, 0) + self.transformedDim = None + + bounds = self.selection + self.rotateAnchor = bounds.origin + bounds.size * 0.5 + + @property + def importPos(self): + if self.transformedDim is None: + return self.pos + return self.pos + self.transformedDim.bounds.origin - self.selection.origin + + @importPos.setter + def importPos(self, pos): + if self.transformedDim is None: + self.pos = pos + else: + self.pos = pos - self.transformedDim.bounds.origin + self.selection.origin + + @property + def importDim(self): + if self.transformedDim is not None: + return self.transformedDim + else: + return self.sourceDim + + @property + def rotation(self): + return self._rotation + + @rotation.setter + def rotation(self, value): + self._rotation = value + self.updateTransform() + + @property + def scale(self): + return self._rotation + + @scale.setter + def scale(self, value): + self._rotation = value + self.updateTransform() + + def updateTransform(self): + if self.rotation == (0, 0, 0) and self.scale == (0, 0, 0): + self.transformedDim = None + else: + selectionDim = SelectionTransform(self.sourceDim, self.selection) + self.transformedDim = DimensionTransform(selectionDim, self.rotateAnchor, *self.rotation) + + def __repr__(self): + return "%s(%r, %r, %r)" % (self.__class__.__name__, self.sourceDim, self.selection, self.pos) + + @property + def bounds(self): + return BoundingBox(self.pos, self.selection.size) + + @property + def importBounds(self): + if self.transformedDim is not None: + size = self.transformedDim.bounds.size + else: + size = self.selection.size + return BoundingBox(self.importPos, size) + + +class Rotate3DNode(Node): + def __init__(self): + super(Rotate3DNode, self).__init__() + self.anchorNode = TranslateNode() + self.rotXNode = RotateNode(axis=(1, 0, 0)) + self.rotYNode = RotateNode(axis=(0, 1, 0)) + self.rotZNode = RotateNode(axis=(0, 0, 1)) + self.recenterNode = TranslateNode() + + super(Rotate3DNode, self).addChild(self.anchorNode) + self.anchorNode.addChild(self.rotXNode) + self.rotXNode.addChild(self.rotYNode) + self.rotYNode.addChild(self.rotZNode) + self.rotZNode.addChild(self.recenterNode) + + def addChild(self, node): + self.recenterNode.addChild(node) + + def removeChild(self, node): + self.recenterNode.removeChild(node) + + def setRotation(self, rots): + rx, ry, rz = rots + self.rotXNode.degrees = rx + self.rotYNode.degrees = ry + self.rotZNode.degrees = rz + + def setAnchor(self, point): + self.anchorNode.translateOffset = point + self.recenterNode.translateOffset = -point \ No newline at end of file