diff --git a/src/mcedit2/editortools/brush/__init__.py b/src/mcedit2/editortools/brush/__init__.py index 27fbd52..7428ef1 100644 --- a/src/mcedit2/editortools/brush/__init__.py +++ b/src/mcedit2/editortools/brush/__init__.py @@ -23,6 +23,7 @@ from mcedit2.util.showprogress import showProgress from mcedit2.util.worldloader import WorldLoader from mceditlib.geometry import Vector from mceditlib.selection import UnionBox +from mceditlib.selection.hollow import HollowSelection from mceditlib.util import exhaust log = logging.getLogger(__name__) @@ -74,7 +75,18 @@ class BrushCommand(SimplePerformCommand): selections = [self.brushShape.createShapedSelection(self.brushMode.brushBoxForPoint(point, self.options), self.editorSession.currentDimension) for point in self.points] - selection = UnionBox(*selections) + + if len(selections) > 1: + selection = UnionBox(*selections) + elif len(selections) == 0: + yield 0 + return + else: + selection = selections[0] + + if self.hollow: + selection = HollowSelection(selection) + for i in self.brushMode.applyToSelection(self, selection): yield i except NotImplementedError: @@ -209,7 +221,8 @@ class BrushTool(EditorTool): def options(self): options = {'brushSize': self.brushSize, 'brushShape': self.brushShape, - 'brushMode': self.brushMode} + 'brushMode': self.brushMode, + 'brushHollow': self.brushHollow} options.update(self.brushMode.getOptions()) return options @@ -225,6 +238,10 @@ class BrushTool(EditorTool): def brushShape(self): return self.toolWidget.brushShapeInput.currentShape + @property + def brushHollow(self): + return self.toolWidget.hollowCheckBox.isChecked() + def updateCursor(self): log.info("Updating brush cursor") if self.cursorWorldScene: diff --git a/src/mcedit2/editortools/brush/shapes.py b/src/mcedit2/editortools/brush/shapes.py index 65fb0e2..37a18f8 100644 --- a/src/mcedit2/editortools/brush/shapes.py +++ b/src/mcedit2/editortools/brush/shapes.py @@ -114,6 +114,10 @@ class Round(BrushShape): radius = shape / 2.0 offset = radius - 0.5 + if 0 in radius: + log.warn("Zero volume shape: %s", shape) + return None + blockPositions -= offset[:, None, None, None] blockPositions *= blockPositions @@ -132,22 +136,9 @@ class Square(BrushShape): super(Square, self).__init__() self.displayName = self.tr("Square") - self.optionsWidget = QtGui.QWidget() - self.hollowCheckbox = QtGui.QCheckBox() - self.optionsWidget.setLayout(Column(self.hollowCheckbox)) - self.hollowCheckbox.toggled.connect(self.hollowChanged) - - def hollowChanged(self): - # can't connect toggled to optionsChanged directly because toggled emits with the - # new check state as an arg, but optionsChanged takes no args. - self.optionsChanged.emit() - def createShapedSelection(self, box, dimension): return BoundingBox(box.origin, box.size) - def getOptionsWidget(self): - return self.optionsWidget - class Diamond(BrushShape): ID = "Diamond" diff --git a/src/mcedit2/ui/editortools/brush.ui b/src/mcedit2/ui/editortools/brush.ui index 8f222d8..dc6ffc8 100644 --- a/src/mcedit2/ui/editortools/brush.ui +++ b/src/mcedit2/ui/editortools/brush.ui @@ -13,7 +13,7 @@ Form - + @@ -32,14 +32,14 @@ - + Width: - + @@ -49,14 +49,14 @@ - + Height: - + @@ -66,14 +66,14 @@ - + Length: - + @@ -83,14 +83,14 @@ - + Hover: - + @@ -100,14 +100,14 @@ - + Mode: - + @@ -117,6 +117,30 @@ + + + + + + Hollow + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/src/mceditlib/selection/hollow.py b/src/mceditlib/selection/hollow.py new file mode 100644 index 0000000..38f31dc --- /dev/null +++ b/src/mceditlib/selection/hollow.py @@ -0,0 +1,51 @@ +""" + hollow +""" +from __future__ import absolute_import, division, print_function, unicode_literals +import logging + +import numpy + +from mceditlib.selection import SelectionBox + +log = logging.getLogger(__name__) + + +class HollowSelection(SelectionBox): + def __init__(self, base): + super(HollowSelection, self).__init__() + + self.base = base + self.mincx = base.mincx + self.mincy = base.mincy + self.mincz = base.mincz + self.maxcx = base.maxcx + self.maxcy = base.maxcy + self.maxcz = base.maxcz + + def box_mask(self, box): + + bigBox = box.expand(1) + + mask = self.base.box_mask(bigBox) + + # Find exposed faces + + exposedY = mask[:-1] != mask[1:] + exposedZ = mask[:, :-1] != mask[:, 1:] + exposedX = mask[:, :, :-1] != mask[:, :, 1:] + + # Any block with exposed faces is rendered + + exposed = exposedY[:-1, 1:-1, 1:-1] + exposed |= exposedY[1:, 1:-1, 1:-1] + exposed |= exposedZ[1:-1, :-1, 1:-1] + exposed |= exposedZ[1:-1, 1:, 1:-1] + exposed |= exposedX[1:-1, 1:-1, :-1] + exposed |= exposedX[1:-1, 1:-1, 1:] + + mask = mask[1:-1,1:-1,1:-1] + result = mask & exposed + log.info("%d blocks in mask, %d present after hollow", mask.sum(), result.sum()) + + return result