Add "Hollow" option to brush tool

This commit is contained in:
David Vierra 2016-06-22 18:48:25 -10:00
parent 279c04e9ca
commit f8828fc5f1
4 changed files with 109 additions and 26 deletions

View File

@ -23,6 +23,7 @@ from mcedit2.util.showprogress import showProgress
from mcedit2.util.worldloader import WorldLoader from mcedit2.util.worldloader import WorldLoader
from mceditlib.geometry import Vector from mceditlib.geometry import Vector
from mceditlib.selection import UnionBox from mceditlib.selection import UnionBox
from mceditlib.selection.hollow import HollowSelection
from mceditlib.util import exhaust from mceditlib.util import exhaust
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -74,7 +75,18 @@ class BrushCommand(SimplePerformCommand):
selections = [self.brushShape.createShapedSelection(self.brushMode.brushBoxForPoint(point, self.options), selections = [self.brushShape.createShapedSelection(self.brushMode.brushBoxForPoint(point, self.options),
self.editorSession.currentDimension) self.editorSession.currentDimension)
for point in self.points] for point in self.points]
if len(selections) > 1:
selection = UnionBox(*selections) 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): for i in self.brushMode.applyToSelection(self, selection):
yield i yield i
except NotImplementedError: except NotImplementedError:
@ -209,7 +221,8 @@ class BrushTool(EditorTool):
def options(self): def options(self):
options = {'brushSize': self.brushSize, options = {'brushSize': self.brushSize,
'brushShape': self.brushShape, 'brushShape': self.brushShape,
'brushMode': self.brushMode} 'brushMode': self.brushMode,
'brushHollow': self.brushHollow}
options.update(self.brushMode.getOptions()) options.update(self.brushMode.getOptions())
return options return options
@ -225,6 +238,10 @@ class BrushTool(EditorTool):
def brushShape(self): def brushShape(self):
return self.toolWidget.brushShapeInput.currentShape return self.toolWidget.brushShapeInput.currentShape
@property
def brushHollow(self):
return self.toolWidget.hollowCheckBox.isChecked()
def updateCursor(self): def updateCursor(self):
log.info("Updating brush cursor") log.info("Updating brush cursor")
if self.cursorWorldScene: if self.cursorWorldScene:

View File

@ -114,6 +114,10 @@ class Round(BrushShape):
radius = shape / 2.0 radius = shape / 2.0
offset = radius - 0.5 offset = radius - 0.5
if 0 in radius:
log.warn("Zero volume shape: %s", shape)
return None
blockPositions -= offset[:, None, None, None] blockPositions -= offset[:, None, None, None]
blockPositions *= blockPositions blockPositions *= blockPositions
@ -132,22 +136,9 @@ class Square(BrushShape):
super(Square, self).__init__() super(Square, self).__init__()
self.displayName = self.tr("Square") 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): def createShapedSelection(self, box, dimension):
return BoundingBox(box.origin, box.size) return BoundingBox(box.origin, box.size)
def getOptionsWidget(self):
return self.optionsWidget
class Diamond(BrushShape): class Diamond(BrushShape):
ID = "Diamond" ID = "Diamond"

View File

@ -13,7 +13,7 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2"> <item row="0" column="0" colspan="2">
@ -32,14 +32,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>Width:</string> <string>Width:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="2" column="1">
<widget class="SpinSlider" name="xSpinSlider" native="true"> <widget class="SpinSlider" name="xSpinSlider" native="true">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
@ -49,14 +49,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
<string>Height:</string> <string>Height:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="3" column="1">
<widget class="SpinSlider" name="ySpinSlider" native="true"> <widget class="SpinSlider" name="ySpinSlider" native="true">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
@ -66,14 +66,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="4" column="0">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>Length:</string> <string>Length:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="4" column="1">
<widget class="SpinSlider" name="zSpinSlider" native="true"> <widget class="SpinSlider" name="zSpinSlider" native="true">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
@ -83,14 +83,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="5" column="0">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_7">
<property name="text"> <property name="text">
<string>Hover:</string> <string>Hover:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="5" column="1">
<widget class="SpinSlider" name="hoverSpinSlider" native="true"> <widget class="SpinSlider" name="hoverSpinSlider" native="true">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
@ -100,14 +100,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="6" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>Mode:</string> <string>Mode:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<widget class="BrushModeWidget" name="brushModeInput" native="true"> <widget class="BrushModeWidget" name="brushModeInput" native="true">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
@ -117,6 +117,30 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QCheckBox" name="hollowCheckBox">
<property name="text">
<string>Hollow</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout> </layout>
</item> </item>
<item> <item>

View File

@ -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