# mcedit2.worldview.cutaway — mceditlib 0.1 documentation

### Navigation

- index
- modules |
- mceditlib 0.1 documentation »
- Module code »

# Source code for mcedit2.worldview.cutaway

    """
        cutaway.py
    """
    from __future__ import absolute_import, division, print_function, unicode_literals
    from math import floor
    import logging

    from PySide import QtGui

    from mcedit2.rendering import worldscene, scenegraph
    from mcedit2.util import profiler
    from mcedit2.widgets.layout import Column, Row
    from mcedit2.worldview.viewcontrols import ViewControls
    from mcedit2.worldview.worldruler import WorldViewRulerGrid
    from mcedit2.worldview.worldview import WorldView
    from mcedit2.worldview.viewaction import ViewAction, MoveViewMouseAction
    from mceditlib import faces
    from mceditlib.geometry import Vector
    from mceditlib.selection import BoundingBox

    log = logging.getLogger(__name__)

    def RecenterButton(view):
        dim = view.dimension

        def _Recenter():
            pos = dim.bounds.origin + dim.bounds.size / 2
            view.centerOnPoint(pos)

        button = QtGui.QPushButton("Recenter")
        button.clicked.connect(_Recenter)
        return button

    def CutawayWorldViewFrame(dimension, textureAtlas, geometryCache, sharedGLWidget):
        viewFrame = QtGui.QWidget()
        view = CutawayWorldView(dimension, textureAtlas, geometryCache, sharedGLWidget)

        grid = WorldViewRulerGrid(view)

        x = QtGui.QPushButton("x")
        y = QtGui.QPushButton("y")
        z = QtGui.QPushButton("z")
        buttons = dict(zip('xyz', (x, y, z)))

        def to(d):
            def _clicked():
                view.axis = d
                for b in buttons.values():
                    b.setChecked(b is buttons[d])

            return _clicked

        x.clicked.connect(to('x'))
        y.clicked.connect(to('y'))
        z.clicked.connect(to('z'))

        depthLabel = QtGui.QLabel("?")

        def updateDepthLabel():
            center = view.centerPoint
            text = "%s = %d" % (view.axis.upper(), center[view.dim])
            depthLabel.setText(text)

        view.viewportMoved.connect(updateDepthLabel)
        view.__updateDepthLabel = updateDepthLabel

        viewFrame.viewControls = ViewControls(view)

        buttonBar = Row(None, depthLabel, x, y, z, RecenterButton(view), viewFrame.viewControls.getShowHideButton())

        viewFrame.setLayout(Column(buttonBar, (grid, 1)))
        viewFrame.worldView = view

        return viewFrame

    class SlicedWorldScene(scenegraph.Node):
        def __init__(self, dimension, textureAtlas):
            super(SlicedWorldScene, self).__init__()
            self.sliceScenes = {}
            self.dimension = dimension
            self.textureAtlas = textureAtlas
            self.dim = 0

        depthLimit = 0  # Number of depths to display
        advancedDepthLimit = 4  # Additional depths to calculate and cache
        loadRadius = 320

        _pos = 0
        @property
        def pos(self):
            return self._pos

        @pos.setter
        def pos(self, value):
            self._pos = value
            for depth, scene in self.sliceScenes.iteritems():
                scene.visible = depth == value

        def discardAllChunks(self):
            for mesh in self.sliceScenes.itervalues():
                mesh.discardAllChunks()

        def invalidateChunk(self, cx, cz):
            for mesh in self.sliceScenes.itervalues():
                mesh.invalidateChunk(cx, cz)

        def clear(self):
            self.sliceScenes.clear()

        def updateMeshes(self):
            v = self.pos
            for depth in range(v - self.depthLimit - self.advancedDepthLimit,
                               v + self.depthLimit + self.advancedDepthLimit + 1):
                if depth not in self.sliceScenes:
                    scene = self.sliceScenes[depth] = worldscene.WorldScene(self.dimension, self.textureAtlas,
                                                                            bounds=self.visibleBounds(depth))
                    scene.sliceDepth = depth
                    scene.visible = depth == v
                    i = 0
                    for i, child in enumerate(self.children):
                        if child.sliceDepth > depth:
                            break
                    self.insertChild(i, scene)
            for old in self.sliceScenes.keys():
                if old < v - self.depthLimit - self.advancedDepthLimit or old > v + self.depthLimit + self.advancedDepthLimit:
                    scene = self.sliceScenes.pop(old)
                    scene.discardAllChunks()
                    self.removeChild(scene)

        def visibleBounds(self, depth=None):
            r = self.loadRadius
            d = r * 2
            # off = list(self.centerPoint)
            off = [-r, -r, -r]
            size = [d, d, d]
            if depth is None:
                depth = self.pos

            off[self.dim] = depth
            size[self.dim] = 1

            return BoundingBox(off, size)

        def wantsChunk(self, c):
            return any([mesh.wantsChunk(c) for mesh in self.sliceScenes.itervalues()])

        @profiler.iterator("SlicedMesh")
        def workOnChunk(self, c, sections=None):
            for i, mesh in self.sliceScenes.iteritems():
                for _ in mesh.workOnChunk(c, sections):
                    yield _

        def chunkInvalid(self, c):
            for mesh in self.sliceScenes.values():
                mesh.invalidateChunk(*c)

    class CutawayWorldView(WorldView):
        def __init__(self, *a, **kw):
            axis = kw.pop('axis', 'x')
            WorldView.__init__(self, *a, **kw)
            self.axis = axis
            self.viewportMoved.connect(self.updateMeshPos)
            self.viewActions.extend((
                MoveViewMouseAction(),
                CutawaySliceUpAction(),
                CutawaySliceDownAction(),
            ))

        def createWorldScene(self):
            return SlicedWorldScene(self.dimension, self.textureAtlas)

        def sizeInBlocks(self):
            w, h = self.width(), self.height()
            w *= self.scale
            h *= self.scale

            return w, h

        def updateMatrices(self):
            w, h = self.sizeInBlocks()
            projection = QtGui.QMatrix4x4()
            projection.ortho(-w / 2, w / 2, -h / 2, h / 2, -1000, 2000)
            self.matrixNode.projection = projection

            modelview = QtGui.QMatrix4x4()
            if self.axis == 'x':
                modelview.rotate(90., 0., 1., 0.)
            elif self.axis == 'y':
                modelview.rotate(90., 1., 0., 0.)

            modelview.translate(-self.centerPoint[0], -self.centerPoint[1], -self.centerPoint[2])
            self.matrixNode.modelview = modelview

        #
        # def viewBounds(self):
        #    min = self.unprojectPoint(0, 0)[0]
        #    max = self.unprojectPoint(self.width(), self.height(), 1.0)[0]
        #
        #    box = BoundingBox(min, (0, 0, 0)).union(BoundingBox(max, (0, 0, 0)))
        #    return box
        #
        #def beginPan(self, x, y):
        #    point, ray = self.unprojectPoint(x, y)
        #    self.dragStart = point
        #    log.debug("Drag start %s", self.dragStart)
        #
        #def continuePan(self, x, y):
        #    point, ray = self.unprojectPoint(x, y)
        #    d = point - self.dragStart
        #    self.centerPoint -= d
        #    log.debug("Drag continue delta %s", d)
        #
        #def endPan(self):
        #    self.dragStart = None
        #    log.debug("Drag end")

        def slicedPoint(self, x, y):
            ray = self.rayAtPosition(x, y)
            pos = list(ray.point)
            pos[self.dim] = self.centerPoint[self.dim]
            return Vector(*pos)

        def viewCenter(self):
            """Return the world position at the center of the view."""
            return self.slicedPoint(self.width() / 2, self.height() / 2)

        _axis = "x"

        @property
        def axis(self):
            return self._axis

        @axis.setter
        def axis(self, value):
            center = self.viewCenter()
            self._axis = value
            self._updateMatrices()
            self.centerOnPoint(center)
            self.worldScene.discardAllChunks()
            self.worldScene.clear()
            self.worldScene.dim = self.dim
            self.updateMeshPos()
            self.worldScene.updateMeshes()

            yp = {
                'x': (0, 90),
                'y': (0, 0),
                'z': (0, 90),
            }
            self.compassNode.yawPitch = yp[value]

        @property
        def dim(self):
            return 'xyz'.index(self.axis)

        def updateMeshPos(self):
            self.worldScene.pos = int(floor(self.centerPoint[self.dim]))
            self.worldScene.updateMeshes()

        def resizeEvent(self, event):
            super(CutawayWorldView, self).resizeEvent(event)
            self.worldScene.loadRadius = 4 * max(event.size().width(), event.size().height())

        def augmentMouseEvent(self, event):
            x, y = event.x(), event.y()
            ray = self.rayAtPosition(x, y)

            event.point = self.slicedPoint(x, y)
            blockPos = event.point.intfloor()

            self.mouseBlockPos = event.blockPosition = blockPos
            vec = [0, 0, 0]
            vec[self.dim] = 1
            self.mouseBlockFace = event.blockFace = faces.Face.fromVector(vec)
            self.mouseRay = event.ray = ray
            event.view = self

    class CutawaySliceUpAction(ViewAction):
        settingsKey = "worldview.cutaway.slice_up"
        button = ViewAction.WHEEL_UP
        labelText = "Slice Up"
        acceptsMouseWheel = True

        def buttonPressEvent(self, event):
            p = list(event.view.centerPoint)
            p[event.view.dim] += 1

            event.view.centerPoint = p

    class CutawaySliceDownAction(ViewAction):
        settingsKey = "worldview.cutaway.slice_down"
        button = ViewAction.WHEEL_DOWN
        labelText = "Slice Down"
        acceptsMouseWheel = True

        def buttonPressEvent(self, event):
            p = list(event.view.centerPoint)
            p[event.view.dim] -= 1

            event.view.centerPoint = p

### Navigation

- index
- modules |
- mceditlib 0.1 documentation »
- Module code »