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