Exemplo n.º 1
0
    def svg(self, filename, rect=None, resolution=72.0, paperColor=None):
        """Create a SVG file for the selected rect or the whole page.

        The filename may be a string or a QIODevice object. The rectangle is
        relative to our top-left position. Normally vector graphics are
        rendered, but in cases where that is not possible, the resolution will
        be used to determine the DPI for the generated rendering.

        """
        # map to the original page
        source = self.pageRect() if rect is None else self.mapFromPage().rect(
            rect)
        # scale to target size
        w = source.width() * self.scaleX
        h = source.height() * self.scaleY
        if self.computedRotation & 1:
            w, h = h, w
        targetSize = QSizeF(w, h) * resolution / self.dpi

        svg = QSvgGenerator()
        if isinstance(filename, str):
            svg.setFileName(filename)
        else:
            svg.setOutputDevice(filename)
        svg.setResolution(int(resolution))
        svg.setSize(targetSize.toSize())
        svg.setViewBox(QRectF(0, 0, targetSize.width(), targetSize.height()))
        return self.output(svg, source, paperColor)
Exemplo n.º 2
0
    def max_size(self):
        size = QSizeF(0, 0)

        for cluster in self.__clusters:
            for obj in cluster.objects:
                if obj.x() > size.width():
                    size.setWidth(obj.x())

                if obj.y() > size.height():
                    size.setHeight(obj.y())
        return size
Exemplo n.º 3
0
    def boundingRect(self, arg):
        tp = type(arg)
        if tp == QRect:
            rect = arg
            tileWidth = self.map().tileWidth()
            tileHeight = self.map().tileHeight()
            return QRect(rect.x() * tileWidth,
                         rect.y() * tileHeight,
                         rect.width() * tileWidth,
                         rect.height() * tileHeight)
        elif tp == MapObject:
            object = arg
            bounds = object.bounds()
            boundingRect = QRectF()
            if (not object.cell().isEmpty()):
                bottomLeft = bounds.topLeft()
                tile = object.cell().tile
                imgSize = tile.image().size()
                tileOffset = tile.offset()
                objectSize = object.size()
                scale = QSizeF(objectSize.width() / imgSize.width(),
                               objectSize.height() / imgSize.height())
                boundingRect = QRectF(
                    bottomLeft.x() + (tileOffset.x() * scale.width()),
                    bottomLeft.y() +
                    (tileOffset.y() * scale.height() - objectSize.height()),
                    objectSize.width(),
                    objectSize.height()).adjusted(-1, -1, 1, 1)
            else:
                extraSpace = max(self.objectLineWidth(), 1.0)
                x = object.shape()
                if x == MapObject.Ellipse or x == MapObject.Rectangle:
                    if (bounds.isNull()):
                        boundingRect = bounds.adjusted(-10 - extraSpace,
                                                       -10 - extraSpace,
                                                       10 + extraSpace + 1,
                                                       10 + extraSpace + 1)
                    else:
                        boundingRect = bounds.adjusted(-extraSpace,
                                                       -extraSpace,
                                                       extraSpace + 1,
                                                       extraSpace + 1)
                elif x == MapObject.Polygon or x == MapObject.Polyline:
                    # Make some more room for the starting dot
                    extraSpace += self.objectLineWidth() * 4

                    pos = object.position()
                    polygon = object.polygon().translated(pos)
                    screenPolygon = self.pixelToScreenCoords_(polygon)
                    boundingRect = screenPolygon.boundingRect().adjusted(
                        -extraSpace, -extraSpace, extraSpace + 1,
                        extraSpace + 1)
            return boundingRect
Exemplo n.º 4
0
 def boundingRect(self, arg):
     tp = type(arg)
     if tp==QRect:
         rect = arg
         tileWidth = self.map().tileWidth()
         tileHeight = self.map().tileHeight()
         return QRect(rect.x() * tileWidth,
                      rect.y() * tileHeight,
                      rect.width() * tileWidth,
                      rect.height() * tileHeight)
     elif tp==MapObject:
         object = arg
         bounds = object.bounds()
         boundingRect = QRectF()
         if (not object.cell().isEmpty()):
             bottomLeft = bounds.topLeft()
             tile = object.cell().tile
             imgSize = tile.image().size()
             tileOffset = tile.offset()
             objectSize = object.size()
             scale = QSizeF(objectSize.width() / imgSize.width(), objectSize.height() / imgSize.height())
             boundingRect = QRectF(bottomLeft.x() + (tileOffset.x() * scale.width()),
                                   bottomLeft.y() + (tileOffset.y() * scale.height() - objectSize.height()),
                                   objectSize.width(),
                                   objectSize.height()).adjusted(-1, -1, 1, 1)
         else:
             extraSpace = max(self.objectLineWidth(), 1.0)
             x = object.shape()
             if x==MapObject.Ellipse or x==MapObject.Rectangle:
                 if (bounds.isNull()):
                     boundingRect = bounds.adjusted(-10 - extraSpace,
                                                    -10 - extraSpace,
                                                    10 + extraSpace + 1,
                                                    10 + extraSpace + 1)
                 else:
                     boundingRect = bounds.adjusted(-extraSpace,
                                                    -extraSpace,
                                                    extraSpace + 1,
                                                    extraSpace + 1)
             elif x==MapObject.Polygon or x==MapObject.Polyline:
                 # Make some more room for the starting dot
                 extraSpace += self.objectLineWidth() * 4
                 
                 pos = object.position()
                 polygon = object.polygon().translated(pos)
                 screenPolygon = self.pixelToScreenCoords_(polygon)
                 boundingRect = screenPolygon.boundingRect().adjusted(-extraSpace,
                                                                      -extraSpace,
                                                                      extraSpace + 1,
                                                                      extraSpace + 1)
         return boundingRect
Exemplo n.º 5
0
def paint_screenshot_image(
        painter: QPainter,
        scene: QGraphicsScene,
        image_size: QSize,
        scene_rect: QRectF = QRectF(),
        transparent_background=False,
) -> QImage:
    # with elapsed_timer() as elapsed:
    painter.fillRect(QRect(QPoint(0, 0), image_size),
                     painter.background().color())
    scene_items = scene.items(scene_rect, Qt.IntersectsItemBoundingRect)
    only_leaf_items = [
        item for item in scene_items if len(item.childItems()) == 0
    ]
    item_parents = {}
    item_groups = {}
    for item in only_leaf_items:
        if item.group():
            item_groups[item] = item.group()
            item.group().setVisible(False)
        elif item.parentItem():
            item_parents[item] = item.parentItem()
            item.parentItem().setVisible(False)
    group_for_screenshot = QGraphicsItemGroup()
    for item in only_leaf_items:
        group_for_screenshot.addToGroup(item)
    scene.addItem(group_for_screenshot)
    group_for_screenshot.setVisible(True)
    # scene_items2 = scene.items(scene_rect, Qt.IntersectsItemBoundingRect)
    # print(scene_items2)
    # print("before render==========================================")
    rendered_size = scene_rect.size().scaled(QSizeF(image_size),
                                             Qt.KeepAspectRatio)
    dsize = QSizeF(image_size) - rendered_size
    top_left = QPointF(dsize.width() / 2, dsize.height() / 2)
    scene.render(painter, QRectF(top_left, rendered_size), scene_rect,
                 Qt.KeepAspectRatio)
    scene.destroyItemGroup(group_for_screenshot)

    for item in item_parents:
        parent = item_parents[item]
        item.setParentItem(parent)
        parent.setVisible(True)
    for item in item_groups:
        group = item_groups[item]
        group.addToGroup(item)
        group.setVisible(True)
Exemplo n.º 6
0
    def boundingRect(self, arg):
        tp = type(arg)
        if tp == QRect:
            rect = arg
            tileWidth = self.map().tileWidth()
            tileHeight = self.map().tileHeight()
            originX = int(self.map().height() * tileWidth / 2)
            pos = QPoint(
                (rect.x() - (rect.y() + rect.height())) * tileWidth / 2 +
                originX, (rect.x() + rect.y()) * tileHeight / 2)
            side = rect.height() + rect.width()
            size = QSize(side * tileWidth / 2, side * tileHeight / 2)
            return QRect(pos, size)
        elif tp == MapObject:
            object = arg
            if (not object.cell().isEmpty()):
                bottomCenter = self.pixelToScreenCoords_(object.position())
                tile = object.cell().tile
                imgSize = tile.image().size()
                tileOffset = tile.offset()
                objectSize = object.size()
                scale = QSizeF(objectSize.width() / imgSize.width(),
                               objectSize.height() / imgSize.height())
                return QRectF(
                    bottomCenter.x() + (tileOffset.x() * scale.width()) -
                    objectSize.width() / 2,
                    bottomCenter.y() + (tileOffset.y() * scale.height()) -
                    objectSize.height(), objectSize.width(),
                    objectSize.height()).adjusted(-1, -1, 1, 1)
            elif (not object.polygon().isEmpty()):
                extraSpace = max(self.objectLineWidth(), 1.0)

                # Make some more room for the starting dot
                extraSpace += self.objectLineWidth() * 4
                pos = object.position()
                polygon = object.polygon().translated(pos)
                screenPolygon = self.pixelToScreenCoords_(polygon)
                return screenPolygon.boundingRect().adjusted(
                    -extraSpace, -extraSpace - 1, extraSpace, extraSpace)
            else:
                # Take the bounding rect of the projected object, and then add a few
                # pixels on all sides to correct for the line width.
                base = self.pixelRectToScreenPolygon(
                    object.bounds()).boundingRect()
                extraSpace = max(self.objectLineWidth() / 2, 1.0)
                return base.adjusted(-extraSpace, -extraSpace - 1, extraSpace,
                                     extraSpace)
Exemplo n.º 7
0
 def boundingRect(self, arg):
     tp = type(arg)
     if tp == QRect:
         rect = arg
         tileWidth = self.map().tileWidth()
         tileHeight = self.map().tileHeight()
         originX = int(self.map().height() * tileWidth / 2)
         pos = QPoint((rect.x() - (rect.y() + rect.height())) * tileWidth / 2 + originX, (rect.x() + rect.y()) * tileHeight / 2)
         side = rect.height() + rect.width()
         size = QSize(side * tileWidth / 2, side * tileHeight / 2)
         return QRect(pos, size)
     elif tp == MapObject:
         object = arg
         if (not object.cell().isEmpty()):
             bottomCenter = self.pixelToScreenCoords_(object.position())
             tile = object.cell().tile
             imgSize = tile.image().size()
             tileOffset = tile.offset()
             objectSize = object.size()
             scale = QSizeF(objectSize.width() / imgSize.width(), objectSize.height() / imgSize.height())
             return QRectF(bottomCenter.x() + (tileOffset.x() * scale.width()) - objectSize.width() / 2,
                           bottomCenter.y() + (tileOffset.y() * scale.height()) - objectSize.height(),
                           objectSize.width(),
                           objectSize.height()).adjusted(-1, -1, 1, 1)
         elif (not object.polygon().isEmpty()):
             extraSpace = max(self.objectLineWidth(), 1.0)
             
             # Make some more room for the starting dot
             extraSpace += self.objectLineWidth() * 4
             pos = object.position()
             polygon = object.polygon().translated(pos)
             screenPolygon = self.pixelToScreenCoords_(polygon)
             return screenPolygon.boundingRect().adjusted(-extraSpace,
                                                          -extraSpace - 1,
                                                          extraSpace,
                                                          extraSpace)
         else:
             # Take the bounding rect of the projected object, and then add a few
             # pixels on all sides to correct for the line width.
             base = self.pixelRectToScreenPolygon(object.bounds()).boundingRect()
             extraSpace = max(self.objectLineWidth() / 2, 1.0)
             return base.adjusted(-extraSpace,
                                  -extraSpace - 1,
                                  extraSpace, extraSpace)
Exemplo n.º 8
0
    def render(self, cell, pos, cellSize, origin):
        if (self.mTile != cell.tile):
            self.flush()
        image = cell.tile.currentFrameImage()
        size = image.size()
        if cellSize == QSizeF(0, 0):
            objectSize = size
        else:
            objectSize = cellSize
        scale = QSizeF(objectSize.width() / size.width(),
                       objectSize.height() / size.height())
        offset = cell.tile.offset()
        sizeHalf = QPointF(objectSize.width() / 2, objectSize.height() / 2)
        fragment = QPainter.PixmapFragment()
        fragment.x = pos.x() + (offset.x() * scale.width()) + sizeHalf.x()
        fragment.y = pos.y() + (
            offset.y() * scale.height()) + sizeHalf.y() - objectSize.height()
        fragment.sourceLeft = 0
        fragment.sourceTop = 0
        fragment.width = size.width()
        fragment.height = size.height()

        if cell.flippedHorizontally:
            fragment.scaleX = -1
        else:
            fragment.scaleX = 1
        if cell.flippedVertically:
            fragment.scaleY = -1
        else:
            fragment.scaleY = 1

        fragment.rotation = 0
        fragment.opacity = 1
        flippedHorizontally = cell.flippedHorizontally
        flippedVertically = cell.flippedVertically
        if (origin == CellRenderer.BottomCenter):
            fragment.x -= sizeHalf.x()
        if (cell.flippedAntiDiagonally):
            fragment.rotation = 90
            flippedHorizontally = cell.flippedVertically
            flippedVertically = not cell.flippedHorizontally
            # Compensate for the swap of image dimensions
            halfDiff = sizeHalf.y() - sizeHalf.x()
            fragment.y += halfDiff
            if (origin != CellRenderer.BottomCenter):
                fragment.x += halfDiff

        if flippedHorizontally:
            x = -1
        else:
            x = 1
        fragment.scaleX = scale.width() * x

        if flippedVertically:
            x = -1
        else:
            x = 1
        fragment.scaleY = scale.height() * x
        if (self.mIsOpenGL or (fragment.scaleX > 0 and fragment.scaleY > 0)):
            self.mTile = cell.tile
            self.mFragments.append(fragment)
            return

        # The Raster paint engine as of Qt 4.8.4 / 5.0.2 does not support
        # drawing fragments with a negative scaling factor.
        self.flush()  # make sure we drew all tiles so far
        oldTransform = self.mPainter.transform()
        transform = oldTransform
        transform.translate(fragment.x, fragment.y)
        transform.rotate(fragment.rotation)
        transform.scale(fragment.scaleX, fragment.scaleY)
        target = QRectF(fragment.width * -0.5, fragment.height * -0.5,
                        fragment.width, fragment.height)
        source = QRectF(0, 0, fragment.width, fragment.height)
        self.mPainter.setTransform(transform)
        self.mPainter.drawPixmap(target, image, source)
        self.mPainter.setTransform(oldTransform)
Exemplo n.º 9
0
class MapObject(Object):
    ##
    # Enumerates the different object shapes. Rectangle is the default shape.
    # When a polygon is set, the shape determines whether it should be
    # interpreted as a filled polygon or a line.
    ##
    Rectangle, Polygon, Polyline, Ellipse = range(4)

    def __init__(self, *args):
        super().__init__(Object.MapObjectType)

        self.mPolygon = QPolygonF()
        self.mName = QString()
        self.mPos = QPointF()
        self.mCell = Cell()
        self.mType = QString()
        self.mId = 0
        self.mShape = MapObject.Rectangle
        self.mObjectGroup = None
        self.mRotation = 0.0
        self.mVisible = True

        l = len(args)
        if l == 0:
            self.mSize = QSizeF(0, 0)
        elif l == 4:
            name, _type, pos, size = args

            self.mName = name
            self.mType = _type
            self.mPos = pos
            self.mSize = QSizeF(size)

    ##
    # Returns the id of this object. Each object gets an id assigned that is
    # unique for the map the object is on.
    ##
    def id(self):
        return self.mId

    ##
    # Sets the id of this object.
    ##
    def setId(self, id):
        self.mId = id

    ##
    # Returns the name of this object. The name is usually just used for
    # identification of the object in the editor.
    ##
    def name(self):
        return self.mName

    ##
    # Sets the name of this object.
    ##
    def setName(self, name):
        self.mName = name

    ##
    # Returns the type of this object. The type usually says something about
    # how the object is meant to be interpreted by the engine.
    ##
    def type(self):
        return self.mType

    ##
    # Sets the type of this object.
    ##
    def setType(self, type):
        self.mType = type

    ##
    # Returns the position of this object.
    ##
    def position(self):
        return QPointF(self.mPos)

    ##
    # Sets the position of this object.
    ##
    def setPosition(self, pos):
        self.mPos = pos

    ##
    # Returns the x position of this object.
    ##
    def x(self):
        return self.mPos.x()

    ##
    # Sets the x position of this object.
    ##
    def setX(self, x):
        self.mPos.setX(x)

    ##
    # Returns the y position of this object.
    ##
    def y(self):
        return self.mPos.y()

    ##
    # Sets the x position of this object.
    ##
    def setY(self, y):
        self.mPos.setY(y)

    ##
    # Returns the size of this object.
    ##
    def size(self):
        return self.mSize

    ##
    # Sets the size of this object.
    ##
    def setSize(self, *args):
        l = len(args)
        if l == 1:
            size = args[0]
            self.mSize = QSizeF(size)
        elif l == 2:
            width, height = args
            self.setSize(QSizeF(width, height))

    ##
    # Returns the width of this object.
    ##
    def width(self):
        return self.mSize.width()

    ##
    # Sets the width of this object.
    ##
    def setWidth(self, width):
        self.mSize.setWidth(width)

    ##
    # Returns the height of this object.
    ##
    def height(self):
        return self.mSize.height()

    ##
    # Sets the height of this object.
    ##
    def setHeight(self, height):
        self.mSize.setHeight(height)

    ##
    # Sets the polygon associated with this object. The polygon is only used
    # when the object shape is set to either Polygon or Polyline.
    #
    # \sa setShape()
    ##
    def setPolygon(self, polygon):
        self.mPolygon = polygon

    ##
    # Returns the polygon associated with this object. Returns an empty
    # polygon when no polygon is associated with this object.
    ##
    def polygon(self):
        return QPolygonF(self.mPolygon)

    ##
    # Sets the shape of the object.
    ##
    def setShape(self, shape):
        self.mShape = shape

    ##
    # Returns the shape of the object.
    ##
    def shape(self):
        return self.mShape

    ##
    # Shortcut to getting a QRectF from position() and size().
    ##
    def bounds(self):
        return QRectF(self.mPos, self.mSize)

    ##
    # Shortcut to getting a QRectF from position() and size() that uses cell tile if present.
    ##
    def boundsUseTile(self):
        if (self.mCell.isEmpty()):
            # No tile so just use regular bounds
            return self.bounds()

        # Using the tile for determing boundary
        # Note the position given is the bottom-left corner so correct for that
        return QRectF(
            QPointF(self.mPos.x(),
                    self.mPos.y() - self.mCell.tile.height()),
            self.mCell.tile.size())

    ##
    # Sets the tile that is associated with this object. The object will
    # display as the tile image.
    #
    # \warning The object shape is ignored for tile objects!
    ##
    def setCell(self, cell):
        self.mCell = cell

    ##
    # Returns the tile associated with this object.
    ##
    def cell(self):
        return self.mCell

    ##
    # Returns the object group this object belongs to.
    ##
    def objectGroup(self):
        return self.mObjectGroup

    ##
    # Sets the object group this object belongs to. Should only be called
    # from the ObjectGroup class.
    ##
    def setObjectGroup(self, objectGroup):
        self.mObjectGroup = objectGroup

    ##
    # Returns the rotation of the object in degrees.
    ##
    def rotation(self):
        return self.mRotation

    ##
    # Sets the rotation of the object in degrees.
    ##
    def setRotation(self, rotation):
        self.mRotation = rotation

    ##
    # This is somewhat of a workaround for dealing with the ways different objects
    # align.
    #
    # Traditional rectangle objects have top-left alignment.
    # Tile objects have bottom-left alignment on orthogonal maps, but
    # bottom-center alignment on isometric maps.
    #
    # Eventually, the object alignment should probably be configurable. For
    # backwards compatibility, it will need to be configurable on a per-object
    # level.
    ##
    def alignment(self):
        if (self.mCell.isEmpty()):
            return Alignment.TopLeft
        elif (self.mObjectGroup):
            map = self.mObjectGroup.map()
            if map:
                if (map.orientation() == Map.Orientation.Isometric):
                    return Alignment.Bottom

        return Alignment.BottomLeft

    def isVisible(self):
        return self.mVisible

    def setVisible(self, visible):
        self.mVisible = visible

    ##
    # Flip this object in the given \a direction. This doesn't change the size
    # of the object.
    ##
    def flip(self, direction):
        if (not self.mCell.isEmpty()):
            if (direction == FlipDirection.FlipHorizontally):
                self.mCell.flippedHorizontally = not self.mCell.flippedHorizontally
            elif (direction == FlipDirection.FlipVertically):
                self.mCell.flippedVertically = not self.mCell.flippedVertically

        if (not self.mPolygon.isEmpty()):
            center2 = self.mPolygon.boundingRect().center() * 2
            if (direction == FlipDirection.FlipHorizontally):
                for i in range(self.mPolygon.size()):
                    # oh, QPointF mPolygon returned is a copy of internal object
                    self.mPolygon[i] = QPointF(
                        center2.x() - self.mPolygon[i].x(),
                        self.mPolygon[i].y())
            elif (direction == FlipDirection.FlipVertically):
                for i in range(self.mPolygon.size()):
                    self.mPolygon[i] = QPointF(
                        self.mPolygon[i].x(),
                        center2.y() - self.mPolygon[i].y())

    ##
    # Returns a duplicate of this object. The caller is responsible for the
    # ownership of this newly created object.
    ##
    def clone(self):
        o = MapObject(self.mName, self.mType, self.mPos, self.mSize)
        o.setProperties(self.properties())
        o.setPolygon(self.mPolygon)
        o.setShape(self.mShape)
        o.setCell(self.mCell)
        o.setRotation(self.mRotation)
        return o
Exemplo n.º 10
0
def denormalize_rect(area: QSizeF, rect: QRectF) -> QRectF:
    return QRectF(rect.x() * area.width(),
                  rect.y() * area.height(),
                  rect.width() * area.width(),
                  rect.height() * area.height())
Exemplo n.º 11
0
    def render(self, cell, pos, cellSize, origin):
        if (self.mTile != cell.tile):
            self.flush()
        image = cell.tile.currentFrameImage()
        size = image.size()
        if cellSize == QSizeF(0,0):
            objectSize = size
        else:
            objectSize = cellSize
        scale = QSizeF(objectSize.width() / size.width(), objectSize.height() / size.height())
        offset = cell.tile.offset()
        sizeHalf = QPointF(objectSize.width() / 2, objectSize.height() / 2)
        fragment = QPainter.PixmapFragment()
        fragment.x = pos.x() + (offset.x() * scale.width()) + sizeHalf.x()
        fragment.y = pos.y() + (offset.y() * scale.height()) + sizeHalf.y() - objectSize.height()
        fragment.sourceLeft = 0
        fragment.sourceTop = 0
        fragment.width = size.width()
        fragment.height = size.height()
        
        if cell.flippedHorizontally:
            fragment.scaleX = -1
        else:
            fragment.scaleX = 1
        if cell.flippedVertically:
            fragment.scaleY = -1
        else:
            fragment.scaleY = 1

        fragment.rotation = 0
        fragment.opacity = 1
        flippedHorizontally = cell.flippedHorizontally
        flippedVertically = cell.flippedVertically
        if (origin == CellRenderer.BottomCenter):
            fragment.x -= sizeHalf.x()
        if (cell.flippedAntiDiagonally):
            fragment.rotation = 90
            flippedHorizontally = cell.flippedVertically
            flippedVertically = not cell.flippedHorizontally
            # Compensate for the swap of image dimensions
            halfDiff = sizeHalf.y() - sizeHalf.x()
            fragment.y += halfDiff
            if (origin != CellRenderer.BottomCenter):
                fragment.x += halfDiff

        if flippedHorizontally:
            x = -1
        else:
            x = 1
        fragment.scaleX = scale.width() * x

        if flippedVertically:
            x = -1
        else:
            x = 1
        fragment.scaleY = scale.height() * x
        if (self.mIsOpenGL or (fragment.scaleX > 0 and fragment.scaleY > 0)):
            self.mTile = cell.tile
            self.mFragments.append(fragment)
            return

        # The Raster paint engine as of Qt 4.8.4 / 5.0.2 does not support
        # drawing fragments with a negative scaling factor.
        self.flush() # make sure we drew all tiles so far
        oldTransform = self.mPainter.transform()
        transform = oldTransform
        transform.translate(fragment.x, fragment.y)
        transform.rotate(fragment.rotation)
        transform.scale(fragment.scaleX, fragment.scaleY)
        target = QRectF(fragment.width * -0.5, fragment.height * -0.5, fragment.width, fragment.height)
        source = QRectF(0, 0, fragment.width, fragment.height)
        self.mPainter.setTransform(transform)
        self.mPainter.drawPixmap(target, image, source)
        self.mPainter.setTransform(oldTransform)
Exemplo n.º 12
0
    def drawMapObject(self, painter, object, color):
        painter.save()
        pen = QPen(Qt.black)
        pen.setCosmetic(True)
        cell = object.cell()
        if (not cell.isEmpty()):
            tile = cell.tile
            imgSize = tile.size()
            pos = self.pixelToScreenCoords_(object.position())
            tileOffset = tile.offset()
            CellRenderer(painter).render(cell, pos, object.size(), CellRenderer.BottomCenter)
            if (self.testFlag(RenderFlag.ShowTileObjectOutlines)):
                rect = QRectF(QPointF(pos.x() - imgSize.width() / 2 + tileOffset.x(),
                                    pos.y() - imgSize.height() + tileOffset.y()),
                            QSizeF(imgSize))
                pen.setStyle(Qt.SolidLine)
                painter.setPen(pen)
                painter.drawRect(rect)
                pen.setStyle(Qt.DotLine)
                pen.setColor(color)
                painter.setPen(pen)
                painter.drawRect(rect)
        else:
            lineWidth = self.objectLineWidth()
            scale = self.painterScale()
            x = 1
            if lineWidth != 0:
                x = lineWidth
            shadowOffset = x / scale
            brushColor = QColor(color)
            brushColor.setAlpha(50)
            brush = QBrush(brushColor)
            pen.setJoinStyle(Qt.RoundJoin)
            pen.setCapStyle(Qt.RoundCap)
            pen.setWidth(lineWidth)
            
            colorPen = QPen(pen)
            colorPen.setColor(color)
        
            painter.setPen(pen)
            painter.setRenderHint(QPainter.Antialiasing)
            # TODO: Do something sensible to make null-sized objects usable
            x = object.shape()
            if x==MapObject.Ellipse:
                polygon = self.pixelRectToScreenPolygon(object.bounds())
                tw = self.map().tileWidth()
                th = self.map().tileHeight()
                transformScale = QPointF(1, 1)
                if (tw > th):
                    transformScale = QPointF(1, th/tw)
                else:
                    transformScale = QPointF(tw/th, 1)
                l1 = polygon.at(1) - polygon.at(0)
                l2 = polygon.at(3) - polygon.at(0)
                trans = QTransform()
                trans.scale(transformScale.x(), transformScale.y())
                trans.rotate(45)
                iTrans, ok = trans.inverted()
                l1x = iTrans.map(l1)
                l2x = iTrans.map(l2)
                ellipseSize = QSizeF(l1x.manhattanLength(), l2x.manhattanLength())
                if (ellipseSize.width() > 0 and ellipseSize.height() > 0):
                    painter.save()
                    painter.setPen(pen)
                    painter.translate(polygon.at(0))
                    painter.scale(transformScale.x(), transformScale.y())
                    painter.rotate(45)
                    painter.drawEllipse(QRectF(QPointF(0, 0), ellipseSize))
                    painter.restore()

                painter.setBrush(Qt.NoBrush)
                painter.drawPolygon(polygon)
                
                painter.setPen(colorPen)
                painter.setBrush(Qt.NoBrush)
                painter.translate(QPointF(0, -shadowOffset))
                painter.drawPolygon(polygon)
                painter.setBrush(brush)
                if (ellipseSize.width() > 0 and ellipseSize.height() > 0):
                    painter.save()
                    painter.translate(polygon.at(0))
                    painter.scale(transformScale.x(), transformScale.y())
                    painter.rotate(45)
                    painter.drawEllipse(QRectF(QPointF(0, 0), ellipseSize))
                    painter.restore()
            elif x==MapObject.Rectangle:
                polygon = self.pixelRectToScreenPolygon(object.bounds())
                painter.drawPolygon(polygon)
                painter.setPen(colorPen)
                painter.setBrush(brush)
                polygon.translate(0, -shadowOffset)
                painter.drawPolygon(polygon)
            elif x==MapObject.Polygon:
                pos = object.position()
                polygon = object.polygon().translated(pos)
                screenPolygon = self.pixelToScreenCoords_(polygon)
                thickPen = QPen(pen)
                thickColorPen = QPen(colorPen)
                thickPen.setWidthF(thickPen.widthF() * 4)
                thickColorPen.setWidthF(thickColorPen.widthF() * 4)
            
                painter.drawPolygon(screenPolygon)
                
                painter.setPen(thickPen)
                painter.drawPoint(screenPolygon.first())

                painter.setPen(colorPen)
                painter.setBrush(brush)
                screenPolygon.translate(0, -shadowOffset)
                painter.drawPolygon(screenPolygon)
                painter.setPen(thickColorPen)
                painter.drawPoint(screenPolygon.first())
                
            elif x==MapObject.Polyline:
                pos = object.position()
                polygon = object.polygon().translated(pos)
                screenPolygon = self.pixelToScreenCoords_(polygon)
                thickPen = QPen(pen)
                thickColorPen = QPen(colorPen)
                thickPen.setWidthF(thickPen.widthF() * 4)
                thickColorPen.setWidthF(thickColorPen.widthF() * 4)
                
                painter.drawPolyline(screenPolygon)
                painter.setPen(thickPen)
                painter.drawPoint(screenPolygon.first())
                
                painter.setPen(colorPen)
                screenPolygon.translate(0, -shadowOffset)
                painter.drawPolyline(screenPolygon)
                
                painter.setPen(thickColorPen)
                painter.drawPoint(screenPolygon.first())

        painter.restore()
Exemplo n.º 13
0
    def drawMapObject(self, painter, object, color):
        painter.save()
        pen = QPen(Qt.black)
        pen.setCosmetic(True)
        cell = object.cell()
        if (not cell.isEmpty()):
            tile = cell.tile
            imgSize = tile.size()
            pos = self.pixelToScreenCoords_(object.position())
            tileOffset = tile.offset()
            CellRenderer(painter).render(cell, pos, object.size(),
                                         CellRenderer.BottomCenter)
            if (self.testFlag(RenderFlag.ShowTileObjectOutlines)):
                rect = QRectF(
                    QPointF(pos.x() - imgSize.width() / 2 + tileOffset.x(),
                            pos.y() - imgSize.height() + tileOffset.y()),
                    QSizeF(imgSize))
                pen.setStyle(Qt.SolidLine)
                painter.setPen(pen)
                painter.drawRect(rect)
                pen.setStyle(Qt.DotLine)
                pen.setColor(color)
                painter.setPen(pen)
                painter.drawRect(rect)
        else:
            lineWidth = self.objectLineWidth()
            scale = self.painterScale()
            x = 1
            if lineWidth != 0:
                x = lineWidth
            shadowOffset = x / scale
            brushColor = QColor(color)
            brushColor.setAlpha(50)
            brush = QBrush(brushColor)
            pen.setJoinStyle(Qt.RoundJoin)
            pen.setCapStyle(Qt.RoundCap)
            pen.setWidth(lineWidth)

            colorPen = QPen(pen)
            colorPen.setColor(color)

            painter.setPen(pen)
            painter.setRenderHint(QPainter.Antialiasing)
            # TODO: Do something sensible to make null-sized objects usable
            x = object.shape()
            if x == MapObject.Ellipse:
                polygon = self.pixelRectToScreenPolygon(object.bounds())
                tw = self.map().tileWidth()
                th = self.map().tileHeight()
                transformScale = QPointF(1, 1)
                if (tw > th):
                    transformScale = QPointF(1, th / tw)
                else:
                    transformScale = QPointF(tw / th, 1)
                l1 = polygon.at(1) - polygon.at(0)
                l2 = polygon.at(3) - polygon.at(0)
                trans = QTransform()
                trans.scale(transformScale.x(), transformScale.y())
                trans.rotate(45)
                iTrans, ok = trans.inverted()
                l1x = iTrans.map(l1)
                l2x = iTrans.map(l2)
                ellipseSize = QSizeF(l1x.manhattanLength(),
                                     l2x.manhattanLength())
                if (ellipseSize.width() > 0 and ellipseSize.height() > 0):
                    painter.save()
                    painter.setPen(pen)
                    painter.translate(polygon.at(0))
                    painter.scale(transformScale.x(), transformScale.y())
                    painter.rotate(45)
                    painter.drawEllipse(QRectF(QPointF(0, 0), ellipseSize))
                    painter.restore()

                painter.setBrush(Qt.NoBrush)
                painter.drawPolygon(polygon)

                painter.setPen(colorPen)
                painter.setBrush(Qt.NoBrush)
                painter.translate(QPointF(0, -shadowOffset))
                painter.drawPolygon(polygon)
                painter.setBrush(brush)
                if (ellipseSize.width() > 0 and ellipseSize.height() > 0):
                    painter.save()
                    painter.translate(polygon.at(0))
                    painter.scale(transformScale.x(), transformScale.y())
                    painter.rotate(45)
                    painter.drawEllipse(QRectF(QPointF(0, 0), ellipseSize))
                    painter.restore()
            elif x == MapObject.Rectangle:
                polygon = self.pixelRectToScreenPolygon(object.bounds())
                painter.drawPolygon(polygon)
                painter.setPen(colorPen)
                painter.setBrush(brush)
                polygon.translate(0, -shadowOffset)
                painter.drawPolygon(polygon)
            elif x == MapObject.Polygon:
                pos = object.position()
                polygon = object.polygon().translated(pos)
                screenPolygon = self.pixelToScreenCoords_(polygon)
                thickPen = QPen(pen)
                thickColorPen = QPen(colorPen)
                thickPen.setWidthF(thickPen.widthF() * 4)
                thickColorPen.setWidthF(thickColorPen.widthF() * 4)

                painter.drawPolygon(screenPolygon)

                painter.setPen(thickPen)
                painter.drawPoint(screenPolygon.first())

                painter.setPen(colorPen)
                painter.setBrush(brush)
                screenPolygon.translate(0, -shadowOffset)
                painter.drawPolygon(screenPolygon)
                painter.setPen(thickColorPen)
                painter.drawPoint(screenPolygon.first())

            elif x == MapObject.Polyline:
                pos = object.position()
                polygon = object.polygon().translated(pos)
                screenPolygon = self.pixelToScreenCoords_(polygon)
                thickPen = QPen(pen)
                thickColorPen = QPen(colorPen)
                thickPen.setWidthF(thickPen.widthF() * 4)
                thickColorPen.setWidthF(thickColorPen.widthF() * 4)

                painter.drawPolyline(screenPolygon)
                painter.setPen(thickPen)
                painter.drawPoint(screenPolygon.first())

                painter.setPen(colorPen)
                screenPolygon.translate(0, -shadowOffset)
                painter.drawPolyline(screenPolygon)

                painter.setPen(thickColorPen)
                painter.drawPoint(screenPolygon.first())

        painter.restore()
Exemplo n.º 14
0
    def updateResizingSingleItem(self, resizingOrigin, screenPos, modifiers):
        renderer = self.mapDocument().renderer()
        object = self.mMovingObjects.first()
        mapObject = object.item.mapObject()
        
        ## The resizingOrigin, screenPos and mStart are affected by the ObjectGroup
        # offset. We will un-apply it to these variables since the resize for
        # single items happens in local coordinate space.
        ##
        offset = mapObject.objectGroup().offset()
    
        ## These transformations undo and redo the object rotation, which is always
        # applied in screen space.
        ##
        unrotate = rotateAt(object.oldItemPosition, -object.oldRotation)
        rotate = rotateAt(object.oldItemPosition, object.oldRotation)
        origin = (resizingOrigin - offset) * unrotate
        pos = (screenPos - offset) * unrotate
        start = (self.mStart - offset) * unrotate
        oldPos = object.oldItemPosition
        ## In order for the resizing to work somewhat sanely in isometric mode,
        # the resizing is performed in pixel space except for tile objects, which
        # are not affected by isometric projection apart from their position.
        ##
        pixelSpace = resizeInPixelSpace(mapObject)
        preserveAspect = modifiers & Qt.ControlModifier
        if (pixelSpace):
            origin = renderer.screenToPixelCoords_(origin)
            pos = renderer.screenToPixelCoords_(pos)
            start = renderer.screenToPixelCoords_(start)
            oldPos = object.oldPosition

        newPos = oldPos
        newSize = object.oldSize
        ## In case one of the anchors was used as-is, the desired size can be
        # derived directly from the distance from the origin for rectangle
        # and ellipse objects. This allows scaling up a 0-sized object without
        # dealing with infinite scaling factor issues.
        #
        # For obvious reasons this can't work on polygons or polylines, nor when
        # preserving the aspect ratio.
        ##
        if (self.mClickedResizeHandle.resizingOrigin() == resizingOrigin and (mapObject.shape() == MapObject.Rectangle or
                 mapObject.shape() == MapObject.Ellipse) and not preserveAspect):
            newBounds = QRectF(newPos, newSize)
            newBounds = align(newBounds, mapObject.alignment())
            x = self.mClickedResizeHandle.anchorPosition()
            if x==AnchorPosition.LeftAnchor or x==AnchorPosition.TopLeftAnchor or x==AnchorPosition.BottomLeftAnchor:
                newBounds.setLeft(min(pos.x(), origin.x()))
            elif x==AnchorPosition.RightAnchor or x==AnchorPosition.TopRightAnchor or x==AnchorPosition.BottomRightAnchor:
                newBounds.setRight(max(pos.x(), origin.x()))
            else:
                # nothing to do on this axis
                pass

            x = self.mClickedResizeHandle.anchorPosition()
            if x==AnchorPosition.TopAnchor or x==AnchorPosition.TopLeftAnchor or x==AnchorPosition.TopRightAnchor:
                newBounds.setTop(min(pos.y(), origin.y()))
            elif x==AnchorPosition.BottomAnchor or x==AnchorPosition.BottomLeftAnchor or x==AnchorPosition.BottomRightAnchor:
                newBounds.setBottom(max(pos.y(), origin.y()))
            else:
                # nothing to do on this axis
                pass

            newBounds = unalign(newBounds, mapObject.alignment())
            newSize = newBounds.size()
            newPos = newBounds.topLeft()
        else:
            relPos = pos - origin
            startDiff = start - origin
            try:
                newx = relPos.x() / startDiff.x()
            except:
                newx = 0
            try:
                newy = relPos.y() / startDiff.y()
            except:
                newy = 0
            scalingFactor = QSizeF(max(0.01, newx), max(0.01, newy))
            if not math.isfinite(scalingFactor.width()):
                scalingFactor.setWidth(1)
            if not math.isfinite(scalingFactor.height()):
                scalingFactor.setHeight(1)
            
            if (self.mResizingLimitHorizontal):
                if preserveAspect:
                    scalingFactor.setWidth(scalingFactor.height())
                else:
                    scalingFactor.setWidth(1)
            elif (self.mResizingLimitVertical):
                if preserveAspect:
                    scalingFactor.setHeight(scalingFactor.width())
                else:
                    scalingFactor.setHeight(1)
            elif (preserveAspect):
                scale = min(scalingFactor.width(), scalingFactor.height())
                scalingFactor.setWidth(scale)
                scalingFactor.setHeight(scale)

            oldRelPos = oldPos - origin
            newPos = origin + QPointF(oldRelPos.x() * scalingFactor.width(), oldRelPos.y() * scalingFactor.height())
            newSize.setWidth(newSize.width() * scalingFactor.width())
            newSize.setHeight(newSize.height() * scalingFactor.height())
            if (not object.oldPolygon.isEmpty()):
                newPolygon = QPolygonF(object.oldPolygon.size())
                for n in range(object.oldPolygon.size()):
                    point = object.oldPolygon[n]
                    newPolygon[n] = QPointF(point.x() * scalingFactor.width(), point.y() * scalingFactor.height())

                mapObject.setPolygon(newPolygon)

        if (pixelSpace):
            newPos = renderer.pixelToScreenCoords_(newPos)
        newPos = renderer.screenToPixelCoords_(newPos * rotate)
        mapObject.setSize(newSize)
        mapObject.setPosition(newPos)
        self.mapDocument().mapObjectModel().emitObjectsChanged(self.changingObjects())
Exemplo n.º 15
0
class PandaWidget(QWidget):
    def __init__(self,
                 panda3DWorld,
                 label_info,
                 parent=None,
                 FPS=60,
                 debug=False):
        QWidget.__init__(self, parent)

        # set fixed geometry
        self.panda3DWorld = panda3DWorld
        self.panda3DWorld.set_parent(self)

        self.label_info = label_info

        self.setFocusPolicy(Qt.StrongFocus)
        self.paintSurface = QPainter()
        self.rotate = QTransform()
        self.rotate.rotate(180)
        self.out_image = QImage()

        size = self.panda3DWorld.cam.node().get_lens().get_film_size()
        self.initial_film_size = QSizeF(size.x, size.y)
        self.initial_size = self.size()

        self.synchronizer = QPanda3DSynchronizer(self, FPS)
        self.synchronizer.start()

        self.debug = debug

        self.update_sgam()

    def get_scene_graph_analyzer_meter(self):
        return self.panda3DWorld.get_scene_graph_analyzer_meter()

    def update_sgam(self):
        text = sceneGraphAnalyzerMeterParser(
            self.get_scene_graph_analyzer_meter()).toText()
        self.label_info.setText(text)

    def mousePressEvent(self, event):
        self.panda3DWorld.camera_controller.mouse_press(
            event.button(), event.x(), event.y())

    def mouseReleaseEvent(self, event):
        self.panda3DWorld.camera_controller.mouse_release(
            event.button(), event.x(), event.y())

    def mouseMoveEvent(self, event):
        self.panda3DWorld.camera_controller.mouse_move(event.button(),
                                                       event.x(), event.y())

    def wheelEvent(self, evt):
        delta = -evt.angleDelta().y()
        self.panda3DWorld.camera_controller.zoom_event(delta)

    def keyPressEvent(self, evt):
        key = evt.key()
        try:
            k = "{}{}".format(get_panda_key_modifiers_prefix(evt),
                              QPanda3D_Key_translation[key])
            if self.debug:
                print(k)
            messenger.send(k)
        except:
            print(
                "Unimplemented key. Please send an issue on github to fix this problem"
            )

    def keyReleaseEvent(self, evt):
        key = evt.key()
        try:
            k = "{}{}-up".format(get_panda_key_modifiers_prefix(evt),
                                 QPanda3D_Key_translation[key])
            if self.debug:
                print(k)
            messenger.send(k)
        except:
            print(
                "Unimplemented key. Please send an issue on github to fix this problem"
            )

    def resizeEvent(self, evt):
        lens = self.panda3DWorld.cam.node().get_lens()
        lens.set_film_size(
            self.initial_film_size.width() * evt.size().width() /
            self.initial_size.width(),
            self.initial_film_size.height() * evt.size().height() /
            self.initial_size.height())
        self.panda3DWorld.buff.setSize(evt.size().width(), evt.size().height())

    def minimumSizeHint(self):
        return QSize(400, 300)

    # Use the paint event to pull the contents of the panda texture to the widget
    def paintEvent(self, event):
        if self.panda3DWorld.screenTexture.mightHaveRamImage():
            self.panda3DWorld.screenTexture.setFormat(Texture.FRgba32)
            data = self.panda3DWorld.screenTexture.getRamImage().getData()
            img = QImage(data, self.panda3DWorld.screenTexture.getXSize(),
                         self.panda3DWorld.screenTexture.getYSize(),
                         QImage.Format_ARGB32).mirrored()
            self.paintSurface.begin(self)
            self.paintSurface.drawImage(0, 0, img)

            self.paintSurface.end()

    def rotateX(self, value):
        self.panda3DWorld.camera_controller.rotateTheta(value)

    def rotateY(self, value):
        self.panda3DWorld.camera_controller.rotatePhi(value)
Exemplo n.º 16
0
    def paint(self, painter):
        painter.setRenderHint(QPainter.Antialiasing)

        pixelRatio = self.devicePixelRatioF()
        rect = QRectF(0, 0,
                      self.width() * pixelRatio,
                      self.height() * pixelRatio)
        sz = QSizeF(self.width() * pixelRatio,
                    self.height() * pixelRatio).toSize()

        self.resizePixmap(sz)

        yOffset = rect.toRect().topLeft().y() + (100 - self.value() -
                                                 10) * sz.height() / 100

        # draw water
        waterImage = QImage(sz, QImage.Format_ARGB32_Premultiplied)
        waterPainter = QPainter()
        waterPainter.begin(waterImage)
        waterPainter.setRenderHint(QPainter.Antialiasing)
        waterPainter.setCompositionMode(QPainter.CompositionMode_Source)

        pointStart = QPointF(sz.width() / 2, 0)
        pointEnd = QPointF(sz.width() / 2, sz.height())
        linear = QLinearGradient(pointStart, pointEnd)
        startColor = QColor('#1F08FF')
        startColor.setAlphaF(1)
        endColor = QColor('#50FFF7')
        endColor.setAlphaF(0.28)
        linear.setColorAt(0, startColor)
        linear.setColorAt(1, endColor)
        linear.setSpread(QGradient.PadSpread)
        waterPainter.setPen(Qt.NoPen)
        waterPainter.setBrush(linear)
        waterPainter.drawEllipse(waterImage.rect().center(),
                                 sz.width() / 2 + 1,
                                 sz.height() / 2 + 1)

        waterPainter.setCompositionMode(QPainter.CompositionMode_SourceOver)
        waterPainter.drawImage(int(self.backXOffset), yOffset,
                               self.waterBackImage)
        waterPainter.drawImage(
            int(self.backXOffset) - self.waterBackImage.width(), yOffset,
            self.waterBackImage)
        waterPainter.drawImage(int(self.frontXOffset), yOffset,
                               self.waterFrontImage)
        waterPainter.drawImage(
            int(self.frontXOffset) - self.waterFrontImage.width(), yOffset,
            self.waterFrontImage)

        # draw pop
        if self.value() > 30:
            for pop in self.pops:
                popPath = QPainterPath()
                popPath.addEllipse(pop.xOffset * sz.width() / 100,
                                   (100 - pop.yOffset) * sz.height() / 100,
                                   pop.size * sz.width() / 100,
                                   pop.size * sz.height() / 100)
                waterPainter.fillPath(popPath, QColor(255, 255, 255,
                                                      255 * 0.3))

        if self.isTextVisible():
            font = waterPainter.font()
            rectValue = QRect()
            progressText = self.text().strip('%')

            if progressText == '100':
                font.setPixelSize(sz.height() * 35 / 100)
                waterPainter.setFont(font)

                rectValue.setWidth(sz.width() * 60 / 100)
                rectValue.setHeight(sz.height() * 35 / 100)
                rectValue.moveCenter(rect.center().toPoint())
                waterPainter.setPen(Qt.white)
                waterPainter.drawText(rectValue, Qt.AlignCenter, progressText)
            else:
                font.setPixelSize(sz.height() * 40 / 100)
                waterPainter.setFont(font)

                rectValue.setWidth(sz.width() * 45 / 100)
                rectValue.setHeight(sz.height() * 40 / 100)
                rectValue.moveCenter(rect.center().toPoint())
                rectValue.moveLeft(rect.left() + rect.width() * 0.45 * 0.5)

                waterPainter.setPen(Qt.white)
                waterPainter.drawText(rectValue, Qt.AlignCenter, progressText)
                font.setPixelSize(font.pixelSize() / 2)
                waterPainter.setFont(font)
                rectPerent = QRect(
                    QPoint(rectValue.right(),
                           rectValue.bottom() - rect.height() * 20 / 100),
                    QPoint(rectValue.right() + rect.width() * 20 / 100,
                           rectValue.bottom()))

                waterPainter.drawText(rectPerent, Qt.AlignCenter, '%')

        waterPainter.end()

        maskPixmap = QPixmap(sz)
        maskPixmap.fill(Qt.transparent)
        path = QPainterPath()
        path.addEllipse(QRectF(0, 0, sz.width(), sz.height()))
        maskPainter = QPainter()
        maskPainter.begin(maskPixmap)
        maskPainter.setRenderHint(QPainter.Antialiasing)
        maskPainter.setPen(QPen(Qt.white, 1))
        maskPainter.fillPath(path, QBrush(Qt.white))
        maskPainter.end()

        mode = QPainter.CompositionMode_SourceIn
        contentImage = QImage(sz, QImage.Format_ARGB32_Premultiplied)
        contentPainter = QPainter()
        contentPainter.begin(contentImage)
        contentPainter.setCompositionMode(QPainter.CompositionMode_Source)
        contentPainter.fillRect(contentImage.rect(), Qt.transparent)
        contentPainter.setCompositionMode(QPainter.CompositionMode_SourceOver)
        contentPainter.drawImage(0, 0, maskPixmap.toImage())
        contentPainter.setCompositionMode(mode)
        contentPainter.drawImage(0, 0, waterImage)
        contentPainter.setCompositionMode(
            QPainter.CompositionMode_DestinationOver)
        contentPainter.end()

        contentImage.setDevicePixelRatio(pixelRatio)
        painter.drawImage(self.rect(), contentImage)
Exemplo n.º 17
0
class MapObject(Object):
    ##
    # Enumerates the different object shapes. Rectangle is the default shape.
    # When a polygon is set, the shape determines whether it should be
    # interpreted as a filled polygon or a line.
    ##
    Rectangle, Polygon, Polyline, Ellipse = range(4)

    def __init__(self, *args):
        super().__init__(Object.MapObjectType)

        self.mPolygon = QPolygonF()
        self.mName = QString()
        self.mPos = QPointF()
        self.mCell = Cell()
        self.mType = QString()
        self.mId = 0
        self.mShape = MapObject.Rectangle
        self.mObjectGroup = None
        self.mRotation = 0.0
        self.mVisible = True

        l = len(args)
        if l==0:
            self.mSize = QSizeF(0, 0)
        elif l==4:
            name, _type, pos, size = args

            self.mName = name
            self.mType = _type
            self.mPos = pos
            self.mSize = QSizeF(size)

    ##
    # Returns the id of this object. Each object gets an id assigned that is
    # unique for the map the object is on.
    ##
    def id(self):
        return self.mId

    ##
    # Sets the id of this object.
    ##
    def setId(self, id):
        self.mId = id

    ##
    # Returns the name of this object. The name is usually just used for
    # identification of the object in the editor.
    ##
    def name(self):
        return self.mName

    ##
    # Sets the name of this object.
    ##
    def setName(self, name):
        self.mName = name

    ##
    # Returns the type of this object. The type usually says something about
    # how the object is meant to be interpreted by the engine.
    ##
    def type(self):
        return self.mType

    ##
    # Sets the type of this object.
    ##
    def setType(self, type):
        self.mType = type

    ##
    # Returns the position of this object.
    ##
    def position(self):
        return QPointF(self.mPos)

    ##
    # Sets the position of this object.
    ##
    def setPosition(self, pos):
        self.mPos = pos

    ##
    # Returns the x position of this object.
    ##
    def x(self):
        return self.mPos.x()

    ##
    # Sets the x position of this object.
    ##
    def setX(self, x):
        self.mPos.setX(x)

    ##
    # Returns the y position of this object.
    ##
    def y(self):
        return self.mPos.y()

    ##
    # Sets the x position of this object.
    ##
    def setY(self, y):
        self.mPos.setY(y)

    ##
    # Returns the size of this object.
    ##
    def size(self):
        return self.mSize

    ##
    # Sets the size of this object.
    ##
    def setSize(self, *args):
        l = len(args)
        if l==1:
            size = args[0]
            self.mSize = QSizeF(size)
        elif l==2:
            width, height = args
            self.setSize(QSizeF(width, height))

    ##
    # Returns the width of this object.
    ##
    def width(self):
        return self.mSize.width()

    ##
    # Sets the width of this object.
    ##
    def setWidth(self, width):
        self.mSize.setWidth(width)

    ##
    # Returns the height of this object.
    ##
    def height(self):
        return self.mSize.height()

    ##
    # Sets the height of this object.
    ##
    def setHeight(self, height):
        self.mSize.setHeight(height)

    ##
    # Sets the polygon associated with this object. The polygon is only used
    # when the object shape is set to either Polygon or Polyline.
    #
    # \sa setShape()
    ##
    def setPolygon(self, polygon):
        self.mPolygon = polygon

    ##
    # Returns the polygon associated with this object. Returns an empty
    # polygon when no polygon is associated with this object.
    ##
    def polygon(self):
        return QPolygonF(self.mPolygon)

    ##
    # Sets the shape of the object.
    ##
    def setShape(self, shape):
        self.mShape = shape

    ##
    # Returns the shape of the object.
    ##
    def shape(self):
        return self.mShape

    ##
    # Shortcut to getting a QRectF from position() and size().
    ##
    def bounds(self):
        return QRectF(self.mPos, self.mSize)

    ##
    # Shortcut to getting a QRectF from position() and size() that uses cell tile if present.
    ##
    def boundsUseTile(self):
        if (self.mCell.isEmpty()):
            # No tile so just use regular bounds
            return self.bounds()

        # Using the tile for determing boundary
        # Note the position given is the bottom-left corner so correct for that
        return QRectF(QPointF(self.mPos.x(),
                              self.mPos.y() - self.mCell.tile.height()),
                      self.mCell.tile.size())

    ##
    # Sets the tile that is associated with this object. The object will
    # display as the tile image.
    #
    # \warning The object shape is ignored for tile objects!
    ##
    def setCell(self, cell):
        self.mCell = cell

    ##
    # Returns the tile associated with this object.
    ##
    def cell(self):
        return self.mCell

    ##
    # Returns the object group this object belongs to.
    ##
    def objectGroup(self):
        return self.mObjectGroup

    ##
    # Sets the object group this object belongs to. Should only be called
    # from the ObjectGroup class.
    ##
    def setObjectGroup(self, objectGroup):
        self.mObjectGroup = objectGroup

    ##
    # Returns the rotation of the object in degrees.
    ##
    def rotation(self):
        return self.mRotation

    ##
    # Sets the rotation of the object in degrees.
    ##
    def setRotation(self, rotation):
        self.mRotation = rotation

    ##
    # This is somewhat of a workaround for dealing with the ways different objects
    # align.
    #
    # Traditional rectangle objects have top-left alignment.
    # Tile objects have bottom-left alignment on orthogonal maps, but
    # bottom-center alignment on isometric maps.
    #
    # Eventually, the object alignment should probably be configurable. For
    # backwards compatibility, it will need to be configurable on a per-object
    # level.
    ##
    def alignment(self):
        if (self.mCell.isEmpty()):
            return Alignment.TopLeft
        elif (self.mObjectGroup):
            map = self.mObjectGroup.map()
            if map:
                if (map.orientation() == Map.Orientation.Isometric):
                    return Alignment.Bottom

        return Alignment.BottomLeft

    def isVisible(self):
        return self.mVisible
    
    def setVisible(self, visible):
        self.mVisible = visible

    ##
    # Flip this object in the given \a direction. This doesn't change the size
    # of the object.
    ##
    def flip(self, direction):
        if (not self.mCell.isEmpty()):
            if (direction == FlipDirection.FlipHorizontally):
                self.mCell.flippedHorizontally = not self.mCell.flippedHorizontally
            elif (direction == FlipDirection.FlipVertically):
                self.mCell.flippedVertically = not self.mCell.flippedVertically

        if (not self.mPolygon.isEmpty()):
            center2 = self.mPolygon.boundingRect().center() * 2
            if (direction == FlipDirection.FlipHorizontally):
                for i in range(self.mPolygon.size()):
                    # oh, QPointF mPolygon returned is a copy of internal object
                    self.mPolygon[i] = QPointF(center2.x() - self.mPolygon[i].x(), self.mPolygon[i].y())
            elif (direction == FlipDirection.FlipVertically):
                for i in range(self.mPolygon.size()):
                    self.mPolygon[i] = QPointF(self.mPolygon[i].x(), center2.y() - self.mPolygon[i].y())

    ##
    # Returns a duplicate of this object. The caller is responsible for the
    # ownership of this newly created object.
    ##
    def clone(self):
        o = MapObject(self.mName, self.mType, self.mPos, self.mSize)
        o.setProperties(self.properties())
        o.setPolygon(self.mPolygon)
        o.setShape(self.mShape)
        o.setCell(self.mCell)
        o.setRotation(self.mRotation)
        return o
Exemplo n.º 18
0
    def updateResizingSingleItem(self, resizingOrigin, screenPos, modifiers):
        renderer = self.mapDocument().renderer()
        object = self.mMovingObjects.first()
        mapObject = object.item.mapObject()

        ## The resizingOrigin, screenPos and mStart are affected by the ObjectGroup
        # offset. We will un-apply it to these variables since the resize for
        # single items happens in local coordinate space.
        ##
        offset = mapObject.objectGroup().offset()

        ## These transformations undo and redo the object rotation, which is always
        # applied in screen space.
        ##
        unrotate = rotateAt(object.oldItemPosition, -object.oldRotation)
        rotate = rotateAt(object.oldItemPosition, object.oldRotation)
        origin = (resizingOrigin - offset) * unrotate
        pos = (screenPos - offset) * unrotate
        start = (self.mStart - offset) * unrotate
        oldPos = object.oldItemPosition
        ## In order for the resizing to work somewhat sanely in isometric mode,
        # the resizing is performed in pixel space except for tile objects, which
        # are not affected by isometric projection apart from their position.
        ##
        pixelSpace = resizeInPixelSpace(mapObject)
        preserveAspect = modifiers & Qt.ControlModifier
        if (pixelSpace):
            origin = renderer.screenToPixelCoords_(origin)
            pos = renderer.screenToPixelCoords_(pos)
            start = renderer.screenToPixelCoords_(start)
            oldPos = object.oldPosition

        newPos = oldPos
        newSize = object.oldSize
        ## In case one of the anchors was used as-is, the desired size can be
        # derived directly from the distance from the origin for rectangle
        # and ellipse objects. This allows scaling up a 0-sized object without
        # dealing with infinite scaling factor issues.
        #
        # For obvious reasons this can't work on polygons or polylines, nor when
        # preserving the aspect ratio.
        ##
        if (self.mClickedResizeHandle.resizingOrigin() == resizingOrigin
                and (mapObject.shape() == MapObject.Rectangle
                     or mapObject.shape() == MapObject.Ellipse)
                and not preserveAspect):
            newBounds = QRectF(newPos, newSize)
            newBounds = align(newBounds, mapObject.alignment())
            x = self.mClickedResizeHandle.anchorPosition()
            if x == AnchorPosition.LeftAnchor or x == AnchorPosition.TopLeftAnchor or x == AnchorPosition.BottomLeftAnchor:
                newBounds.setLeft(min(pos.x(), origin.x()))
            elif x == AnchorPosition.RightAnchor or x == AnchorPosition.TopRightAnchor or x == AnchorPosition.BottomRightAnchor:
                newBounds.setRight(max(pos.x(), origin.x()))
            else:
                # nothing to do on this axis
                pass

            x = self.mClickedResizeHandle.anchorPosition()
            if x == AnchorPosition.TopAnchor or x == AnchorPosition.TopLeftAnchor or x == AnchorPosition.TopRightAnchor:
                newBounds.setTop(min(pos.y(), origin.y()))
            elif x == AnchorPosition.BottomAnchor or x == AnchorPosition.BottomLeftAnchor or x == AnchorPosition.BottomRightAnchor:
                newBounds.setBottom(max(pos.y(), origin.y()))
            else:
                # nothing to do on this axis
                pass

            newBounds = unalign(newBounds, mapObject.alignment())
            newSize = newBounds.size()
            newPos = newBounds.topLeft()
        else:
            relPos = pos - origin
            startDiff = start - origin
            try:
                newx = relPos.x() / startDiff.x()
            except:
                newx = 0
            try:
                newy = relPos.y() / startDiff.y()
            except:
                newy = 0
            scalingFactor = QSizeF(max(0.01, newx), max(0.01, newy))
            if not math.isfinite(scalingFactor.width()):
                scalingFactor.setWidth(1)
            if not math.isfinite(scalingFactor.height()):
                scalingFactor.setHeight(1)

            if (self.mResizingLimitHorizontal):
                if preserveAspect:
                    scalingFactor.setWidth(scalingFactor.height())
                else:
                    scalingFactor.setWidth(1)
            elif (self.mResizingLimitVertical):
                if preserveAspect:
                    scalingFactor.setHeight(scalingFactor.width())
                else:
                    scalingFactor.setHeight(1)
            elif (preserveAspect):
                scale = min(scalingFactor.width(), scalingFactor.height())
                scalingFactor.setWidth(scale)
                scalingFactor.setHeight(scale)

            oldRelPos = oldPos - origin
            newPos = origin + QPointF(oldRelPos.x() * scalingFactor.width(),
                                      oldRelPos.y() * scalingFactor.height())
            newSize.setWidth(newSize.width() * scalingFactor.width())
            newSize.setHeight(newSize.height() * scalingFactor.height())
            if (not object.oldPolygon.isEmpty()):
                newPolygon = QPolygonF(object.oldPolygon.size())
                for n in range(object.oldPolygon.size()):
                    point = object.oldPolygon[n]
                    newPolygon[n] = QPointF(point.x() * scalingFactor.width(),
                                            point.y() * scalingFactor.height())

                mapObject.setPolygon(newPolygon)

        if (pixelSpace):
            newPos = renderer.pixelToScreenCoords_(newPos)
        newPos = renderer.screenToPixelCoords_(newPos * rotate)
        mapObject.setSize(newSize)
        mapObject.setPosition(newPos)
        self.mapDocument().mapObjectModel().emitObjectsChanged(
            self.changingObjects())