Added repeat count to Clone tool
This commit is contained in:
parent
10ce7746a9
commit
e1a6c51a2b
@ -12,7 +12,7 @@ from PySide import QtGui
|
|||||||
from mcedit2.rendering.selection import boxFaceUnderCursor
|
from mcedit2.rendering.selection import boxFaceUnderCursor
|
||||||
from mcedit2.util.showprogress import showProgress
|
from mcedit2.util.showprogress import showProgress
|
||||||
from mcedit2.widgets.coord_widget import CoordinateWidget
|
from mcedit2.widgets.coord_widget import CoordinateWidget
|
||||||
from mcedit2.widgets.layout import Column
|
from mcedit2.widgets.layout import Column, Row
|
||||||
from mceditlib.selection import BoundingBox
|
from mceditlib.selection import BoundingBox
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -27,11 +27,11 @@ class CloneSelectionCommand(SimpleRevisionCommand):
|
|||||||
|
|
||||||
def undo(self):
|
def undo(self):
|
||||||
super(CloneSelectionCommand, self).undo()
|
super(CloneSelectionCommand, self).undo()
|
||||||
self.cloneTool.currentClone = None
|
self.cloneTool.pendingClone = None
|
||||||
self.cloneTool.editorSession.chooseTool("Select")
|
self.cloneTool.editorSession.chooseTool("Select")
|
||||||
|
|
||||||
def redo(self):
|
def redo(self):
|
||||||
self.cloneTool.currentClone = self.pendingImport
|
self.cloneTool.pendingClone = self.pendingImport
|
||||||
self.cloneTool.editorSession.chooseTool("Clone")
|
self.cloneTool.editorSession.chooseTool("Clone")
|
||||||
super(CloneSelectionCommand, self).redo()
|
super(CloneSelectionCommand, self).redo()
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class CloneFinishCommand(SimpleRevisionCommand):
|
|||||||
|
|
||||||
def undo(self):
|
def undo(self):
|
||||||
super(CloneFinishCommand, self).undo()
|
super(CloneFinishCommand, self).undo()
|
||||||
self.cloneTool.currentClone = self.pendingImport
|
self.cloneTool.pendingClone = self.pendingImport
|
||||||
self.editorSession.currentSelection = self.previousSelection
|
self.editorSession.currentSelection = self.previousSelection
|
||||||
self.editorSession.chooseTool("Clone")
|
self.editorSession.chooseTool("Clone")
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ class CloneFinishCommand(SimpleRevisionCommand):
|
|||||||
super(CloneFinishCommand, self).redo()
|
super(CloneFinishCommand, self).redo()
|
||||||
self.previousSelection = self.editorSession.currentSelection
|
self.previousSelection = self.editorSession.currentSelection
|
||||||
self.editorSession.currentSelection = self.pendingImport.bounds
|
self.editorSession.currentSelection = self.pendingImport.bounds
|
||||||
self.cloneTool.currentClone = None
|
self.cloneTool.pendingClone = None
|
||||||
|
|
||||||
class CloneTool(EditorTool):
|
class CloneTool(EditorTool):
|
||||||
iconName = "clone"
|
iconName = "clone"
|
||||||
@ -79,90 +79,164 @@ class CloneTool(EditorTool):
|
|||||||
self.dragStartPoint = None
|
self.dragStartPoint = None
|
||||||
self.dragStartClonePosition = None
|
self.dragStartClonePosition = None
|
||||||
|
|
||||||
self.pendingCloneNode = None
|
self.originPoint = None
|
||||||
|
self.offsetPoint = None
|
||||||
|
|
||||||
|
self.pendingCloneNodes = []
|
||||||
|
self.mainCloneNode = None
|
||||||
self.overlayNode = scenenode.Node()
|
self.overlayNode = scenenode.Node()
|
||||||
self.overlayNode.name = "Clone Overlay"
|
self.overlayNode.name = "Clone Overlay"
|
||||||
|
|
||||||
self.toolWidget = QtGui.QWidget()
|
self.toolWidget = QtGui.QWidget()
|
||||||
self.pointInput = CoordinateWidget()
|
self.pointInput = CoordinateWidget()
|
||||||
self.pointInput.pointChanged.connect(self.pointInputChanged)
|
self.pointInput.pointChanged.connect(self.pointInputChanged)
|
||||||
confirmButton = QtGui.QPushButton("Confirm") # xxxx should be in worldview
|
confirmButton = QtGui.QPushButton(self.tr("Confirm")) # xxxx should be in worldview
|
||||||
confirmButton.clicked.connect(self.confirmClone)
|
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,
|
self.toolWidget.setLayout(Column(self.pointInput,
|
||||||
|
Row(QtGui.QLabel(self.tr("Repeat count: ")), self.repeatCountInput),
|
||||||
|
Row(self.tileXCheckbox,
|
||||||
|
self.tileYCheckbox,
|
||||||
|
self.tileZCheckbox),
|
||||||
confirmButton,
|
confirmButton,
|
||||||
None))
|
None))
|
||||||
|
|
||||||
self.currentClone = None # Do this after creating pointInput to disable inputs
|
self.pendingClone = None # Do this after creating pointInput to disable inputs
|
||||||
|
|
||||||
def pointInputChanged(self, value):
|
def pointInputChanged(self, value):
|
||||||
if value is not None:
|
self.offsetPoint = value
|
||||||
self.currentClone.pos = value
|
self.pendingClone.pos = value
|
||||||
self.pendingCloneNode.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)
|
||||||
|
|
||||||
|
if len(self.pendingCloneNodes):
|
||||||
|
self.mainCloneNode = self.pendingCloneNodes[0]
|
||||||
|
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
|
@property
|
||||||
def currentClone(self):
|
def pendingClone(self):
|
||||||
return self._currentClone
|
return self._pendingClone
|
||||||
|
|
||||||
@currentClone.setter
|
@pendingClone.setter
|
||||||
def currentClone(self, pendingImport):
|
def pendingClone(self, pendingImport):
|
||||||
log.info("Begin clone: %s", pendingImport)
|
log.info("Begin clone: %s", pendingImport)
|
||||||
self._currentClone = pendingImport
|
self._pendingClone = pendingImport
|
||||||
self.pointInput.setEnabled(pendingImport is not None)
|
self.pointInput.setEnabled(pendingImport is not None)
|
||||||
if pendingImport is not None:
|
self.updateTiling()
|
||||||
node = self.pendingCloneNode = PendingImportNode(pendingImport, self.editorSession.textureAtlas)
|
|
||||||
self.overlayNode.addChild(node)
|
|
||||||
else:
|
|
||||||
if self.pendingCloneNode:
|
|
||||||
self.overlayNode.removeChild(self.pendingCloneNode)
|
|
||||||
self.pendingCloneNode = None
|
|
||||||
|
|
||||||
def toolActive(self):
|
def toolActive(self):
|
||||||
self.editorSession.selectionTool.hideSelectionWalls = True
|
self.editorSession.selectionTool.hideSelectionWalls = True
|
||||||
if self.currentClone is None:
|
if self.pendingClone is not None:
|
||||||
if self.editorSession.currentSelection is None:
|
self.pendingClone = None
|
||||||
return
|
|
||||||
|
|
||||||
# This makes a reference to the latest revision in the editor.
|
if self.editorSession.currentSelection is None:
|
||||||
# If the cloned area is changed between "Clone" and "Confirm", the changed
|
return
|
||||||
# blocks will be moved.
|
|
||||||
pos = self.editorSession.currentSelection.origin
|
|
||||||
pendingImport = PendingImport(self.editorSession.currentDimension, pos,
|
|
||||||
self.editorSession.currentSelection,
|
|
||||||
self.tr("<Cloned Object>"))
|
|
||||||
moveCommand = CloneSelectionCommand(self, pendingImport)
|
|
||||||
|
|
||||||
self.editorSession.pushCommand(moveCommand)
|
# 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
|
||||||
|
pendingImport = PendingImport(self.editorSession.currentDimension, pos,
|
||||||
|
self.editorSession.currentSelection,
|
||||||
|
self.tr("<Cloned Object>"))
|
||||||
|
moveCommand = CloneSelectionCommand(self, pendingImport)
|
||||||
|
|
||||||
|
self.editorSession.pushCommand(moveCommand)
|
||||||
|
|
||||||
def toolInactive(self):
|
def toolInactive(self):
|
||||||
self.editorSession.selectionTool.hideSelectionWalls = False
|
self.editorSession.selectionTool.hideSelectionWalls = False
|
||||||
|
if self.mainCloneNode:
|
||||||
|
self.mainCloneNode.hoverFace(None)
|
||||||
|
|
||||||
self.pendingCloneNode.hoverFace(None)
|
|
||||||
|
|
||||||
self.confirmClone()
|
self.confirmClone()
|
||||||
|
|
||||||
def confirmClone(self):
|
def confirmClone(self):
|
||||||
if self.currentClone is None:
|
if self.pendingClone is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
command = CloneFinishCommand(self, self.currentClone)
|
command = CloneFinishCommand(self, self.pendingClone)
|
||||||
|
|
||||||
with command.begin():
|
with command.begin():
|
||||||
# TODO don't use intermediate schematic...
|
# TODO don't use intermediate schematic...
|
||||||
export = self.currentClone.sourceDim.exportSchematicIter(self.currentClone.selection)
|
export = self.pendingClone.sourceDim.exportSchematicIter(self.pendingClone.selection)
|
||||||
schematic = showProgress("Copying...", export)
|
schematic = showProgress("Copying...", export)
|
||||||
dim = schematic.getDimension()
|
dim = schematic.getDimension()
|
||||||
|
|
||||||
task = self.editorSession.currentDimension.copyBlocksIter(dim, dim.bounds,
|
tasks = []
|
||||||
self.currentClone.pos,
|
for pos in self.getTilingPositions():
|
||||||
biomes=True, create=True)
|
task = self.editorSession.currentDimension.copyBlocksIter(dim, dim.bounds, pos,
|
||||||
showProgress(self.tr("Pasting..."), task)
|
biomes=True, create=True)
|
||||||
|
tasks.append(task)
|
||||||
|
|
||||||
|
showProgress(self.tr("Pasting..."), *tasks)
|
||||||
|
|
||||||
self.editorSession.pushCommand(command)
|
self.editorSession.pushCommand(command)
|
||||||
|
self.originPoint = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def clonePosition(self):
|
def clonePosition(self):
|
||||||
return None if self.currentClone is None else self.currentClone.pos
|
return None if self.pendingClone is None else self.pendingClone.pos
|
||||||
|
|
||||||
@clonePosition.setter
|
@clonePosition.setter
|
||||||
def clonePosition(self, value):
|
def clonePosition(self, value):
|
||||||
@ -186,38 +260,20 @@ class CloneTool(EditorTool):
|
|||||||
dim = self.dragStartFace.dimension
|
dim = self.dragStartFace.dimension
|
||||||
return ray.intersectPlane(dim, self.dragStartPoint[dim])
|
return ray.intersectPlane(dim, self.dragStartPoint[dim])
|
||||||
|
|
||||||
@property
|
|
||||||
def schematicBox(self):
|
|
||||||
box = self.currentClone.selection
|
|
||||||
return BoundingBox(self.clonePosition, box.size)
|
|
||||||
|
|
||||||
def dragMovePoint(self, ray):
|
|
||||||
"""
|
|
||||||
Return a point representing the intersection between the mouse ray
|
|
||||||
and an imaginary plane coplanar to the dragged face
|
|
||||||
|
|
||||||
:type ray: Ray
|
|
||||||
:rtype: Vector
|
|
||||||
"""
|
|
||||||
dim = self.dragStartFace.dimension
|
|
||||||
return ray.intersectPlane(dim, self.dragStartPoint[dim])
|
|
||||||
|
|
||||||
def mouseMove(self, event):
|
def mouseMove(self, event):
|
||||||
# Hilite face cursor is over
|
# Hilite face cursor is over
|
||||||
if self.currentClone is None:
|
if self.pendingClone is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
node = self.pendingCloneNode
|
point, face = boxFaceUnderCursor(self.pendingClone.bounds, event.ray)
|
||||||
if node:
|
self.mainCloneNode.hoverFace(face)
|
||||||
point, face = boxFaceUnderCursor(self.schematicBox, event.ray)
|
|
||||||
node.hoverFace(face)
|
|
||||||
|
|
||||||
# Highlight face of box to move along, or else axis pointers to grab and drag?
|
# Highlight face of box to move along, or else axis pointers to grab and drag?
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def mouseDrag(self, event):
|
def mouseDrag(self, event):
|
||||||
# Clone box using face or axis pointers
|
# Clone box using face or axis pointers
|
||||||
if self.currentClone is None:
|
if self.pendingClone is None:
|
||||||
return
|
return
|
||||||
if self.dragStartFace is None:
|
if self.dragStartFace is None:
|
||||||
return
|
return
|
||||||
@ -226,17 +282,18 @@ class CloneTool(EditorTool):
|
|||||||
self.clonePosition = self.dragStartClonePosition + map(int, delta)
|
self.clonePosition = self.dragStartClonePosition + map(int, delta)
|
||||||
|
|
||||||
def mousePress(self, event):
|
def mousePress(self, event):
|
||||||
if self.currentClone is not None:
|
if self.pendingClone is not None:
|
||||||
point, face = boxFaceUnderCursor(self.schematicBox, event.ray)
|
point, face = boxFaceUnderCursor(self.pendingClone.bounds, event.ray)
|
||||||
self.dragStartFace = face
|
self.dragStartFace = face
|
||||||
self.dragStartPoint = point
|
self.dragStartPoint = point
|
||||||
self.dragStartClonePosition = self.clonePosition
|
self.dragStartClonePosition = self.clonePosition
|
||||||
|
|
||||||
def mouseRelease(self, event):
|
def mouseRelease(self, event):
|
||||||
if self.currentClone is not None:
|
if self.pendingClone is not None:
|
||||||
self.doCloneOffsetCommand(self.dragStartClonePosition, self.clonePosition)
|
self.doCloneOffsetCommand(self.dragStartClonePosition, self.clonePosition)
|
||||||
|
|
||||||
def doCloneOffsetCommand(self, oldPoint, newPoint):
|
def doCloneOffsetCommand(self, oldPoint, newPoint):
|
||||||
|
log.info("clone offset command: %s %s", oldPoint, newPoint)
|
||||||
if newPoint != oldPoint:
|
if newPoint != oldPoint:
|
||||||
command = CloneOffsetCommand(self, oldPoint, newPoint)
|
command = CloneOffsetCommand(self, oldPoint, newPoint)
|
||||||
self.editorSession.pushCommand(command)
|
self.editorSession.pushCommand(command)
|
||||||
|
Reference in New Issue
Block a user