Esempio n. 1
0
class MoveTool(EditorTool):
    iconName = "move"
    name = "Move"
    modifiesWorld = True

    def __init__(self, editorSession, *args, **kwargs):
        super(MoveTool, self).__init__(editorSession, *args, **kwargs)
        self.overlayNode = scenenode.Node("moveOverlay")
        self._currentImport = None
        self._currentImportNode = None

        self.toolWidget = QtGui.QWidget()

        self.pointInput = CoordinateWidget()
        self.pointInput.pointChanged.connect(self.pointInputChanged)

        self.rotationInput = RotationWidget()
        self.rotationInput.rotationChanged.connect(self.rotationChanged)

        self.copyOptionsWidget = QtGui.QGroupBox(self.tr("Options"))

        self.copyAirCheckbox = QtGui.QCheckBox(self.tr("Copy Air"))
        self.copyOptionsWidget.setLayout(Column(self.copyAirCheckbox))

        confirmButton = QtGui.QPushButton(
            "Confirm")  # xxxx should be in worldview
        confirmButton.clicked.connect(self.confirmImport)
        self.toolWidget.setLayout(
            Column(self.pointInput, self.rotationInput, self.copyOptionsWidget,
                   confirmButton, None))

    def rotationChanged(self, rots, live):
        if self.currentImport:
            if live:
                self.currentImportNode.setPreviewRotation(rots)
            elif rots != self.currentImport.rotation:
                command = MoveRotateCommand(self.currentImport.rotation, rots,
                                            self.currentImport)
                self.editorSession.pushCommand(command)

            self.editorSession.updateView()

    def pointInputChanged(self, value):
        if value is not None:
            self.importDidMove(value, self.currentImport.basePosition)

    # --- Pending imports ---

    def importDidMove(self, newPoint, oldPoint):
        if self.currentImport is None:
            return

        if newPoint != oldPoint:
            command = MoveOffsetCommand(oldPoint, newPoint, self.currentImport)
            self.editorSession.pushCommand(command)
            self.pointInput.point = newPoint

    @property
    def currentImport(self):
        return self._currentImport

    @currentImport.setter
    def currentImport(self, pendingImport):
        self._currentImport = pendingImport
        self.pointInput.setEnabled(pendingImport is not None)
        if pendingImport is not None:
            pendingImport.rotationChanged.connect(self.setRotationInput)
            pendingImport.positionChanged.connect(self.setPositionInput)

        # Set current import to different color?
        # for node in self.pendingImportNodes.itervalues():
        #     node.outlineNode.wireColor = (.2, 1., .2, .5) if node.pendingImport is value else (1, 1, 1, .3)
        if self._currentImportNode is not None:
            self.overlayNode.removeChild(self._currentImportNode)
            self._currentImportNode = None

        if pendingImport is not None:
            node = PendingImportNode(pendingImport,
                                     self.editorSession.textureAtlas)
            node.importMoved.connect(self.importDidMove)
            self._currentImportNode = node
            self.overlayNode.addChild(node)
            self.rotationInput.rotation = pendingImport.rotation
            self.pointInput.point = pendingImport.basePosition
            self.pointInput.origin = pendingImport.selection.origin

    @property
    def currentImportNode(self):
        return self._currentImportNode

    def setRotationInput(self, rots):
        self.rotationInput.rotation = rots

    def setPositionInput(self, pos):
        self.pointInput.point = pos

    # --- Mouse events ---

    def mouseMove(self, event):
        # Hilite face cursor is over
        if self.currentImport is not None:
            self.currentImportNode.mouseMove(event)

        # Highlight face of box to move along, or else axis pointers to grab and drag?

    def mouseDrag(self, event):
        self.mouseMove(event)

    def mousePress(self, event):
        if self.currentImport is not None:
            self.currentImportNode.mousePress(event)

    def mouseRelease(self, event):
        if self.currentImport is not None:
            self.currentImportNode.mouseRelease(event)

    # --- Editor events ---

    def toolActive(self):
        self.editorSession.selectionTool.hideSelectionWalls = True
        if self.currentImport is None:
            self.rotationInput.rotation = (0, 0, 0)
            if self.editorSession.currentSelection is None:
                return

            # This makes a reference to the latest revision in the editor.
            # If the moved area is changed between "Move" and "Confirm", the changed
            # blocks will be moved.
            pos = self.editorSession.currentSelection.origin
            pendingImport = PendingImport(self.editorSession.currentDimension,
                                          pos,
                                          self.editorSession.currentSelection,
                                          self.tr("<Moved Object>"),
                                          isMove=True)
            moveCommand = MoveSelectionCommand(self, pendingImport)

            self.editorSession.pushCommand(moveCommand)

    def toolInactive(self):
        self.editorSession.selectionTool.hideSelectionWalls = False
        # hide hovers for box handles?

    def confirmImport(self):
        if self.currentImport is None:
            return

        command = MoveFinishCommand(self, self.currentImport)
        destDim = self.editorSession.currentDimension
        with command.begin():
            log.info("Move: starting")
            sourceDim, selection = self.currentImport.getSourceForDim(destDim)

            def _copy():
                # Copy to destination
                log.info("Move: copying")
                task = destDim.copyBlocksIter(
                    sourceDim,
                    selection,
                    self.currentImport.importPos,
                    biomes=True,
                    create=True,
                    copyAir=self.copyAirCheckbox.isChecked())

                showProgress(self.tr("Pasting..."), task)

            def _clear():
                log.info("Move: clearing")
                # Clear source
                if self.currentImport.isMove:
                    fill = destDim.fillBlocksIter(self.currentImport.selection,
                                                  "air")
                    showProgress(self.tr("Clearing..."), fill)

            # XXX PendingImport knows about this, defer copy/clear to it?
            if sourceDim is self.currentImport.importDim:
                # copying from original source, copy before clear
                _copy()
                _clear()
            else:
                # temp schematic used, source and dest overlap, clear before copy
                _clear()
                _copy()

        self.editorSession.pushCommand(command)
Esempio n. 2
0
class MoveTool(EditorTool):
    iconName = "move"
    name = "Move"

    def __init__(self, editorSession, *args, **kwargs):
        super(MoveTool, self).__init__(editorSession, *args, **kwargs)
        self.overlayNode = scenenode.Node("moveOverlay")
        self._currentImport = None
        self._currentImportNode = None

        self.toolWidget = QtGui.QWidget()

        self.pointInput = CoordinateWidget()
        self.pointInput.pointChanged.connect(self.pointInputChanged)

        self.rotationInput = RotationWidget()
        self.rotationInput.rotationChanged.connect(self.rotationChanged)

        self.copyOptionsWidget = QtGui.QGroupBox(self.tr("Options"))

        self.copyAirCheckbox = QtGui.QCheckBox(self.tr("Copy Air"))
        self.copyOptionsWidget.setLayout(Column(self.copyAirCheckbox))

        confirmButton = QtGui.QPushButton("Confirm")  # xxxx should be in worldview
        confirmButton.clicked.connect(self.confirmImport)
        self.toolWidget.setLayout(Column(self.pointInput,
                                         self.rotationInput,
                                         self.copyOptionsWidget,
                                         confirmButton,
                                         None))

    def rotationChanged(self, rots, live):
        if self.currentImport:
            if live:
                self.currentImportNode.setPreviewRotation(rots)
            else:
                command = MoveRotateCommand(self.currentImport.rotation, rots, self.currentImport)
                self.editorSession.pushCommand(command)

            self.editorSession.updateView()

    def pointInputChanged(self, value):
        if value is not None:
            self.importDidMove(value, self.currentImport.basePosition)

    # --- Pending imports ---

    def importDidMove(self, newPoint, oldPoint):
        if newPoint != oldPoint:
            command = MoveOffsetCommand(oldPoint, newPoint, self.currentImport)
            self.editorSession.pushCommand(command)
            self.pointInput.point = newPoint

    @property
    def currentImport(self):
        return self._currentImport

    @currentImport.setter
    def currentImport(self, pendingImport):
        self._currentImport = pendingImport
        self.pointInput.setEnabled(pendingImport is not None)
        # Set current import to different color?
        # for node in self.pendingImportNodes.itervalues():
        #     node.outlineNode.wireColor = (.2, 1., .2, .5) if node.pendingImport is value else (1, 1, 1, .3)
        if self._currentImportNode is not None:
            self.overlayNode.removeChild(self._currentImportNode)
            self._currentImportNode = None

        if pendingImport is not None:
            node = PendingImportNode(pendingImport, self.editorSession.textureAtlas)
            node.importMoved.connect(self.importDidMove)
            self._currentImportNode = node
            self.overlayNode.addChild(node)
            self.rotationInput.rotation = pendingImport.rotation
            self.pointInput.point = pendingImport.basePosition
            self.pointInput.origin = pendingImport.selection.origin

    @property
    def currentImportNode(self):
        return self._currentImportNode

    # --- Mouse events ---

    def mouseMove(self, event):
        # Hilite face cursor is over
        if self.currentImport is not None:
            self.currentImportNode.mouseMove(event)

        # Highlight face of box to move along, or else axis pointers to grab and drag?

    def mouseDrag(self, event):
        self.mouseMove(event)

    def mousePress(self, event):
        if self.currentImport is not None:
            self.currentImportNode.mousePress(event)

    def mouseRelease(self, event):
        if self.currentImport is not None:
            self.currentImportNode.mouseRelease(event)

    # --- Editor events ---

    def toolActive(self):
        self.editorSession.selectionTool.hideSelectionWalls = True
        if self.currentImport is None:
            self.rotationInput.rotation = (0, 0, 0)
            if self.editorSession.currentSelection is None:
                return

            # This makes a reference to the latest revision in the editor.
            # If the moved area is changed between "Move" and "Confirm", the changed
            # blocks will be moved.
            pos = self.editorSession.currentSelection.origin
            pendingImport = PendingImport(self.editorSession.currentDimension, pos,
                                          self.editorSession.currentSelection,
                                          self.tr("<Moved Object>"),
                                          isMove=True)
            moveCommand = MoveSelectionCommand(self, pendingImport)

            self.editorSession.pushCommand(moveCommand)

    def toolInactive(self):
        self.editorSession.selectionTool.hideSelectionWalls = False
        # hide hovers for box handles?

    def confirmImport(self):
        if self.currentImport is None:
            return

        command = MoveFinishCommand(self, self.currentImport)
        destDim = self.editorSession.currentDimension
        with command.begin():
            log.info("Move: starting")
            sourceDim, selection = self.currentImport.getSourceForDim(destDim)

            # Copy to destination
            log.info("Move: copying")
            task = destDim.copyBlocksIter(sourceDim, selection,
                                          self.currentImport.importPos,
                                          biomes=True, create=True,
                                          copyAir=self.copyAirCheckbox.isChecked())

            showProgress(self.tr("Pasting..."), task)

            log.info("Move: clearing")
            # Clear source
            if self.currentImport.isMove:
                fill = destDim.fillBlocksIter(self.currentImport.selection, "air")
                showProgress(self.tr("Clearing..."), fill)

        self.editorSession.pushCommand(command)
Esempio n. 3
0
class CloneTool(EditorTool):
    iconName = "clone"
    name = "Clone"

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

        self.originPoint = None
        self.offsetPoint = None

        self.pendingCloneNodes = []
        self.mainCloneNode = None
        self.overlayNode = scenenode.Node()
        self.overlayNode.name = "Clone Overlay"

        self.toolWidget = QtGui.QWidget()
        self.pointInput = CoordinateWidget()
        self.pointInput.pointChanged.connect(self.pointInputChanged)
        confirmButton = QtGui.QPushButton(self.tr("Confirm"))  # xxxx should be in worldview
        confirmButton.clicked.connect(self.confirmClone)

        self.repeatCount = 1
        self.repeatCountInput = QtGui.QSpinBox(minimum=1, maximum=100, value=1)
        self.repeatCountInput.valueChanged.connect(self.setRepeatCount)

        self.tileX = self.tileY = self.tileZ = False

        self.tileXCheckbox = QtGui.QCheckBox(self.tr("Tile X"))
        self.tileXCheckbox.toggled.connect(self.setTileX)

        self.tileYCheckbox = QtGui.QCheckBox(self.tr("Tile Y"))
        self.tileYCheckbox.toggled.connect(self.setTileY)

        self.tileZCheckbox = QtGui.QCheckBox(self.tr("Tile Z"))
        self.tileZCheckbox.toggled.connect(self.setTileZ)

        self.toolWidget.setLayout(Column(self.pointInput,
                                         Row(QtGui.QLabel(self.tr("Repeat count: ")), self.repeatCountInput),
                                         Row(self.tileXCheckbox,
                                             self.tileYCheckbox,
                                             self.tileZCheckbox),
                                         confirmButton,
                                         None))

        self.pendingClone = None  # Do this after creating pointInput to disable inputs

    def pointInputChanged(self, value):
        if self.offsetPoint != value:
            self.offsetPoint = value
            self.pendingClone.pos = value
            self.updateTiling()

    def setTileX(self, value):
        self.tileX = value
        self.updateTiling()

    def setTileY(self, value):
        self.tileY = value
        self.updateTiling()

    def setTileZ(self, value):
        self.tileZ = value
        self.updateTiling()

    def setRepeatCount(self, value):
        self.repeatCount = value
        self.updateTiling()

    def updateTiling(self):
        if self.pendingClone is None:
            repeatCount = 0
        else:
            repeatCount = self.repeatCount

        while len(self.pendingCloneNodes) > repeatCount:
            node = self.pendingCloneNodes.pop()
            self.overlayNode.removeChild(node)
        while len(self.pendingCloneNodes) < repeatCount:
            node = PendingImportNode(self.pendingClone, self.editorSession.textureAtlas)
            self.pendingCloneNodes.append(node)
            self.overlayNode.addChild(node)

        # This is stupid.
        if self.mainCloneNode:
            self.mainCloneNode.importMoved.disconnect(self.cloneDidMove)

        if repeatCount > 0:
            self.mainCloneNode = self.pendingCloneNodes[0]
            self.mainCloneNode.importMoved.connect(self.cloneDidMove)
        else:
            self.mainCloneNode = None

        if None not in (self.offsetPoint, self.originPoint):
            for node, pos in zip(self.pendingCloneNodes, self.getTilingPositions()):
                node.pos = pos

        self.editorSession.updateView()

    def getTilingPositions(self):
        if None not in (self.offsetPoint, self.originPoint):
            pos = self.originPoint
            offset = self.offsetPoint - self.originPoint
            for i in range(self.repeatCount):
                pos = pos + offset
                yield pos

    @property
    def pendingClone(self):
        return self._pendingClone

    @pendingClone.setter
    def pendingClone(self, pendingImport):
        log.info("Begin clone: %s", pendingImport)
        self._pendingClone = pendingImport
        self.pointInput.setEnabled(pendingImport is not None)
        self.updateTiling()

    def toolActive(self):
        self.editorSession.selectionTool.hideSelectionWalls = True
        if self.pendingClone is not None:
            self.pendingClone = None

        if self.editorSession.currentSelection is None:
            return

        # This makes a reference to the latest revision in the editor.
        # If the cloned area is changed between "Clone" and "Confirm", the changed
        # blocks will be moved.
        pos = self.editorSession.currentSelection.origin
        self.originPoint = pos
        self.offsetPoint = pos
        pendingImport = PendingImport(self.editorSession.currentDimension, pos,
                                      self.editorSession.currentSelection,
                                      self.tr("<Cloned Object>"))
        moveCommand = CloneSelectionCommand(self, pendingImport)

        self.editorSession.pushCommand(moveCommand)

    def toolInactive(self):
        self.editorSession.selectionTool.hideSelectionWalls = False
        # if self.mainCloneNode:
        #     self.mainCloneNode.hoverFace(None)

        self.confirmClone()
        
    def confirmClone(self):
        if self.pendingClone is None:
            return

        command = CloneFinishCommand(self, self.pendingClone)

        with command.begin():
            # TODO don't use intermediate schematic...
            export = self.pendingClone.sourceDim.exportSchematicIter(self.pendingClone.selection)
            schematic = showProgress("Copying...", export)
            dim = schematic.getDimension()

            tasks = []
            for pos in self.getTilingPositions():
                task = self.editorSession.currentDimension.copyBlocksIter(dim, dim.bounds, pos,
                                                                          biomes=True, create=True)
                tasks.append(task)

            showProgress(self.tr("Pasting..."), *tasks)

        self.editorSession.pushCommand(command)
        self.originPoint = None

    @property
    def clonePosition(self):
        return None if self.pendingClone is None else self.pendingClone.pos

    @clonePosition.setter
    def clonePosition(self, value):
        """

        :type value: Vector
        """
        self.pointInput.point = value
        self.pointInputChanged(value)

    # --- Mouse events ---

    def mouseMove(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mouseMove(event)

    def mouseDrag(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mouseMove(event)

    def mousePress(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mousePress(event)

    def mouseRelease(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mouseRelease(event)

    # --- Box handle events ---

    def cloneDidMove(self, newPoint, oldPoint):
        log.info("clone offset command: %s %s", oldPoint, newPoint)
        if newPoint != oldPoint:
            command = CloneOffsetCommand(self, oldPoint, newPoint)
            self.editorSession.pushCommand(command)
Esempio n. 4
0
class CloneTool(EditorTool):
    """
    Make multiple copies of the selected area. When selected, displays a preview of the
     copies and allows the position, repeat count, and transforms to be changed.


    Attributes
    ----------
    mainPendingClone : PendingImport
        The object currently being cloned.

    pendingClones : list of PendingImport
        Repeated imports of the object being cloned

    """
    iconName = "clone"
    name = "Clone"
    modifiesWorld = True

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

        self.originPoint = None

        self.pendingClones = []
        self.pendingCloneNodes = []
        self.mainCloneNode = None

        self.overlayNode = scenenode.Node("cloneOverlay")

        self.toolWidget = QtGui.QWidget()
        self.pointInput = CoordinateWidget()
        self.pointInput.pointChanged.connect(self.pointInputChanged)

        self.rotationInput = RotationWidget()
        self.rotationInput.rotationChanged.connect(self.rotationChanged)

        self.scaleInput = ScaleWidget()
        self.scaleInput.scaleChanged.connect(self.scaleChanged)
        
        confirmButton = QtGui.QPushButton(self.tr("Confirm"))  # xxxx should be in worldview
        confirmButton.clicked.connect(self.confirmClone)

        self.repeatCount = 1
        self.repeatCountInput = QtGui.QSpinBox(minimum=1, maximum=10000, value=1)
        self.repeatCountInput.valueChanged.connect(self.setRepeatCount)

        self.rotateRepeatsCheckbox = QtGui.QCheckBox(self.tr("Rotate Repeats"))
        self.rotateRepeatsCheckbox.toggled.connect(self.updateTiling)

        self.rotateOffsetCheckbox = QtGui.QCheckBox(self.tr("Rotate Offset"))
        self.rotateOffsetCheckbox.toggled.connect(self.updateTiling)

        self.toolWidget.setLayout(Column(self.pointInput,
                                         self.rotationInput,
                                         Row(self.rotateRepeatsCheckbox,
                                             self.rotateOffsetCheckbox),
                                         self.scaleInput,
                                         Row(QtGui.QLabel(self.tr("Repeat count: ")), self.repeatCountInput),
                                         confirmButton,
                                         None))

        self.mainPendingClone = None  # Do this after creating pointInput to disable inputs

    def pointInputChanged(self, value):
        if self.mainPendingClone.basePosition != value:
            self.mainPendingClone.basePosition = value
            self.updateTiling()

    def rotationChanged(self, rots, live):
        scale = self.scaleInput.scale
        if live:
            for node, (nodePos, nodeRots, nodeScale) in zip(self.pendingCloneNodes, self.getTilingPositions(None, rots, scale)):
                node.setPreviewRotation(nodeRots)
                node.setPreviewScale(nodeScale)
                node.setPreviewBasePosition(nodePos + node.pendingImport.transformOffset)
            self.editorSession.updateView()
        else:
            if self.mainPendingClone and self.mainPendingClone.rotation != rots:
                command = CloneRotateCommand(self.mainPendingClone.rotation, rots, self)
                self.editorSession.pushCommand(command)
                self.updateTiling()

    def scaleChanged(self, scale, live):
        rots = self.rotationInput.rotation
        if live:
            for node, (nodePos, nodeRots, nodeScale) in zip(self.pendingCloneNodes, self.getTilingPositions(None, rots, scale)):
                node.setPreviewRotation(nodeRots)
                node.setPreviewScale(nodeScale)
                node.setPreviewBasePosition(nodePos + node.pendingImport.transformOffset)
            self.editorSession.updateView()
        else:
            if self.mainPendingClone and self.mainPendingClone.scale != scale:
                command = CloneScaleCommand(self.mainPendingClone.scale, scale, self)
                self.editorSession.pushCommand(command)
                self.updateTiling()

    def setRepeatCount(self, value):
        self.repeatCount = value
        self.updateTiling()

    def setRotation(self, rots):
        if self.mainPendingClone is None:
            return
        else:
            self.mainPendingClone.rotation = rots
            self.updateTiling()

    def setScale(self, scale):
        if self.mainPendingClone is None:
            return
        else:
            self.mainPendingClone.scale = scale
            self.updateTiling()

    def updateTiling(self):
        if self.mainPendingClone is None:
            repeatCount = 0
        else:
            repeatCount = self.repeatCount

        while len(self.pendingClones) > repeatCount:
            node = self.pendingCloneNodes.pop()
            self.overlayNode.removeChild(node)
            self.pendingClones.pop()

        while len(self.pendingClones) < repeatCount:
            clone = PendingImport(self.mainPendingClone.sourceDim,
                                  self.mainPendingClone.basePosition,
                                  self.mainPendingClone.selection,
                                  self.mainPendingClone.text + " %d" % len(self.pendingClones))
            node = PendingImportNode(clone,
                                     self.editorSession.textureAtlas,
                                     hasHandle=len(self.pendingClones) == 0)

            self.pendingClones.append(clone)
            self.pendingCloneNodes.append(node)
            self.overlayNode.addChild(node)

        # This is stupid.
        if self.mainCloneNode:
            self.mainCloneNode.importMoved.disconnect(self.cloneDidMove)
            self.mainCloneNode.importIsMoving.disconnect(self.cloneIsMoving)

        if repeatCount > 0:
            self.mainCloneNode = self.pendingCloneNodes[0]
            self.mainCloneNode.importMoved.connect(self.cloneDidMove)
            self.mainCloneNode.importIsMoving.connect(self.cloneIsMoving)
        else:
            self.mainCloneNode = None

        self.updateTilingPositions()

    def updateTilingPositions(self, offsetPoint=None):
        if self.originPoint is not None:
            for clone, (pos, rots, scale) in zip(self.pendingClones, self.getTilingPositions(offsetPoint)):
                clone.basePosition = pos
                clone.rotation = rots
                clone.scale = scale

        self.editorSession.updateView()

    def getTilingPositions(self, offsetPoint=None, rotations=None, scale=None):
        rotateRepeats = self.rotateRepeatsCheckbox.isChecked()
        rotateOffsets = self.rotateOffsetCheckbox.isChecked()
        baseRotations = rotations or self.mainPendingClone.rotation
        rotations = baseRotations
        scale = scale or self.mainPendingClone.scale

        matrix = transform.transformationMatrix((0, 0, 0), rotations, scale)
        matrix = numpy.linalg.inv(matrix)[:3, :3]
    
        # TODO: Use scales here
        if offsetPoint is None:
            offsetPoint = self.mainPendingClone.basePosition
        if None not in (offsetPoint, self.originPoint):
            pos = self.originPoint
            offset = offsetPoint - self.originPoint
            for i in range(self.repeatCount):
                pos = pos + offset
                yield pos.intfloor(), rotations, scale
                if rotateRepeats:
                    rotations = [a+b for a,b in zip(rotations, baseRotations)]
                if rotateOffsets:
                    # Convert to 4-element column and back
                    offset = (offset * matrix).T
                    offset = tuple(float(x) for x in offset)

    @property
    def mainPendingClone(self):
        return self._pendingClone

    @mainPendingClone.setter
    def mainPendingClone(self, pendingImport):
        log.info("Begin clone: %s", pendingImport)
        self._pendingClone = pendingImport
        self.pointInput.setEnabled(pendingImport is not None)
        if pendingImport:
            self.pointInput.point = pendingImport.basePosition
        self.updateTiling()

    def toolActive(self):
        self.editorSession.selectionTool.hideSelectionWalls = True
        if self.mainPendingClone is None:
            if self.editorSession.currentSelection is None:
                return

            # This makes a reference to the latest revision in the editor.
            # If the cloned area is changed between "Clone" and "Confirm", the changed
            # blocks will be cloned.
            pos = self.editorSession.currentSelection.origin
            self.pointInput.origin = self.originPoint = pos
            pendingImport = PendingImport(self.editorSession.currentDimension, pos,
                                          self.editorSession.currentSelection,
                                          self.tr("<Cloned Object>"))
            moveCommand = CloneSelectionCommand(self, pendingImport)

            self.editorSession.pushCommand(moveCommand)

        self.updateTiling()

    def toolInactive(self):
        self.editorSession.selectionTool.hideSelectionWalls = False
        # if self.mainCloneNode:
        #     self.mainCloneNode.hoverFace(None)

        self.confirmClone()
        
    def confirmClone(self):
        if self.mainPendingClone is None:
            return

        command = CloneFinishCommand(self, self.mainPendingClone, self.originPoint)

        with command.begin():
            tasks = []
            for clone in self.pendingClones:
                # TODO don't use intermediate schematic...
                destDim = self.editorSession.currentDimension
                dim, selection = clone.getSourceForDim(destDim)

                task = destDim.copyBlocksIter(dim, selection, clone.importPos,
                                              biomes=True, create=True, copyAir=False)
                tasks.append(task)

            showProgress(self.tr("Pasting..."), *tasks)

        self.editorSession.pushCommand(command)

    @property
    def clonePosition(self):
        return None if self.mainPendingClone is None else self.mainPendingClone.basePosition

    @clonePosition.setter
    def clonePosition(self, value):
        """

        :type value: Vector
        """
        self.pointInput.point = value
        self.pointInputChanged(value)


    # --- Mouse events ---

    def mouseMove(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mouseMove(event)

    def mouseDrag(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mouseMove(event)

    def mousePress(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mousePress(event)

    def mouseRelease(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mouseRelease(event)

    # --- Box handle events ---

    def cloneDidMove(self, newPoint, oldPoint):
        log.info("clone offset command: %s %s", oldPoint, newPoint)
        if newPoint != oldPoint:
            command = CloneOffsetCommand(self, oldPoint, newPoint)
            self.editorSession.pushCommand(command)

    def cloneIsMoving(self, newPoint):
        self.updateTilingPositions(newPoint)
Esempio n. 5
0
class CloneTool(EditorTool):
    iconName = "clone"
    name = "Clone"

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

        self.originPoint = None
        self.offsetPoint = None

        self.pendingCloneNodes = []
        self.mainCloneNode = None
        self.overlayNode = scenenode.Node()
        self.overlayNode.name = "Clone Overlay"

        self.toolWidget = QtGui.QWidget()
        self.pointInput = CoordinateWidget()
        self.pointInput.pointChanged.connect(self.pointInputChanged)
        confirmButton = QtGui.QPushButton(
            self.tr("Confirm"))  # xxxx should be in worldview
        confirmButton.clicked.connect(self.confirmClone)

        self.repeatCount = 1
        self.repeatCountInput = QtGui.QSpinBox(minimum=1, maximum=100, value=1)
        self.repeatCountInput.valueChanged.connect(self.setRepeatCount)

        self.tileX = self.tileY = self.tileZ = False

        self.tileXCheckbox = QtGui.QCheckBox(self.tr("Tile X"))
        self.tileXCheckbox.toggled.connect(self.setTileX)

        self.tileYCheckbox = QtGui.QCheckBox(self.tr("Tile Y"))
        self.tileYCheckbox.toggled.connect(self.setTileY)

        self.tileZCheckbox = QtGui.QCheckBox(self.tr("Tile Z"))
        self.tileZCheckbox.toggled.connect(self.setTileZ)

        self.toolWidget.setLayout(
            Column(
                self.pointInput,
                Row(QtGui.QLabel(self.tr("Repeat count: ")),
                    self.repeatCountInput),
                Row(self.tileXCheckbox, self.tileYCheckbox,
                    self.tileZCheckbox), confirmButton, None))

        self.pendingClone = None  # Do this after creating pointInput to disable inputs

    def pointInputChanged(self, value):
        if self.offsetPoint != value:
            self.offsetPoint = value
            self.pendingClone.pos = value
            self.updateTiling()

    def setTileX(self, value):
        self.tileX = value
        self.updateTiling()

    def setTileY(self, value):
        self.tileY = value
        self.updateTiling()

    def setTileZ(self, value):
        self.tileZ = value
        self.updateTiling()

    def setRepeatCount(self, value):
        self.repeatCount = value
        self.updateTiling()

    def updateTiling(self):
        if self.pendingClone is None:
            repeatCount = 0
        else:
            repeatCount = self.repeatCount

        while len(self.pendingCloneNodes) > repeatCount:
            node = self.pendingCloneNodes.pop()
            self.overlayNode.removeChild(node)
        while len(self.pendingCloneNodes) < repeatCount:
            node = PendingImportNode(self.pendingClone,
                                     self.editorSession.textureAtlas)
            self.pendingCloneNodes.append(node)
            self.overlayNode.addChild(node)

        # This is stupid.
        if self.mainCloneNode:
            self.mainCloneNode.importMoved.disconnect(self.cloneDidMove)

        if repeatCount > 0:
            self.mainCloneNode = self.pendingCloneNodes[0]
            self.mainCloneNode.importMoved.connect(self.cloneDidMove)
        else:
            self.mainCloneNode = None

        if None not in (self.offsetPoint, self.originPoint):
            for node, pos in zip(self.pendingCloneNodes,
                                 self.getTilingPositions()):
                node.pos = pos

        self.editorSession.updateView()

    def getTilingPositions(self):
        if None not in (self.offsetPoint, self.originPoint):
            pos = self.originPoint
            offset = self.offsetPoint - self.originPoint
            for i in range(self.repeatCount):
                pos = pos + offset
                yield pos

    @property
    def pendingClone(self):
        return self._pendingClone

    @pendingClone.setter
    def pendingClone(self, pendingImport):
        log.info("Begin clone: %s", pendingImport)
        self._pendingClone = pendingImport
        self.pointInput.setEnabled(pendingImport is not None)
        self.updateTiling()

    def toolActive(self):
        self.editorSession.selectionTool.hideSelectionWalls = True
        if self.pendingClone is not None:
            self.pendingClone = None

        if self.editorSession.currentSelection is None:
            return

        # This makes a reference to the latest revision in the editor.
        # If the cloned area is changed between "Clone" and "Confirm", the changed
        # blocks will be moved.
        pos = self.editorSession.currentSelection.origin
        self.originPoint = pos
        self.offsetPoint = pos
        pendingImport = PendingImport(self.editorSession.currentDimension, pos,
                                      self.editorSession.currentSelection,
                                      self.tr("<Cloned Object>"))
        moveCommand = CloneSelectionCommand(self, pendingImport)

        self.editorSession.pushCommand(moveCommand)

    def toolInactive(self):
        self.editorSession.selectionTool.hideSelectionWalls = False
        # if self.mainCloneNode:
        #     self.mainCloneNode.hoverFace(None)

        self.confirmClone()

    def confirmClone(self):
        if self.pendingClone is None:
            return

        command = CloneFinishCommand(self, self.pendingClone)

        with command.begin():
            # TODO don't use intermediate schematic...
            export = self.pendingClone.sourceDim.exportSchematicIter(
                self.pendingClone.selection)
            schematic = showProgress("Copying...", export)
            dim = schematic.getDimension()

            tasks = []
            for pos in self.getTilingPositions():
                task = self.editorSession.currentDimension.copyBlocksIter(
                    dim, dim.bounds, pos, biomes=True, create=True)
                tasks.append(task)

            showProgress(self.tr("Pasting..."), *tasks)

        self.editorSession.pushCommand(command)
        self.originPoint = None

    @property
    def clonePosition(self):
        return None if self.pendingClone is None else self.pendingClone.pos

    @clonePosition.setter
    def clonePosition(self, value):
        """

        :type value: Vector
        """
        self.pointInput.point = value
        self.pointInputChanged(value)

    # --- Mouse events ---

    def mouseMove(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mouseMove(event)

    def mouseDrag(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mouseMove(event)

    def mousePress(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mousePress(event)

    def mouseRelease(self, event):
        if self.mainCloneNode is not None:
            self.mainCloneNode.mouseRelease(event)

    # --- Box handle events ---

    def cloneDidMove(self, newPoint, oldPoint):
        log.info("clone offset command: %s %s", oldPoint, newPoint)
        if newPoint != oldPoint:
            command = CloneOffsetCommand(self, oldPoint, newPoint)
            self.editorSession.pushCommand(command)