Esempio n. 1
0
	def drawD2(self):
		if self.vtkViewer.reader == None:
			return

		num = self.inputD2.text()
		try:
			num = list(map(int, num.split(' ')))
		except Exception:
			return
		if len(num) != 2 or num[0] <= 1 or num[1] <= 1:
			return

		self.clear()

		histogram, scale = self.vtkViewer.d2Sample(num[0], num[1])

		d2Chart = QChart()
		d2Chart.legend().hide()
		polyline = QPolygonF(num[0])
		pointer = polyline.data()
		dtype, tinfo = np.float, np.finfo
		pointer.setsize(2*polyline.size()*tinfo(dtype).dtype.itemsize)
		memory = np.frombuffer(pointer, dtype)
		memory[:(num[0]-1)*2+1:2] = scale
		memory[1:(num[0]-1)*2+2:2] = histogram
		curve = QLineSeries()
		curve.append(polyline)

		d2Chart.addSeries(curve)
		d2Chart.createDefaultAxes()

		ChartView = QChartView(d2Chart)
		self.d2ChartLayout.addWidget(ChartView)
Esempio n. 2
0
class SideRoi:
    support: List[QRectF]
    polygon: QPolygonF

    def __init__(self):
        self.support = list()
        self.polygon = QPolygonF()

    def in_support(self, point: QPointF) -> bool:
        for s in self.support:
            if s.contains(point):
                return True

        return False

    def contains(self, point: QPointF) -> bool:
        return (self.polygon.size() >= 3) and \
            self.polygon.containsPoint(point, Qt.OddEvenFill)

    def to_dict(self):
        o = dict()
        o['support'] = list()

        for s in self.support:
            r = dict()
            r['x'] = s.x()
            r['y'] = s.y()
            r['width'] = s.width()
            r['height'] = s.height()
            o['support'].append(r)

        o['polygon'] = list()
        for point in self.polygon:
            p = dict()
            p['x'] = point.x()
            p['y'] = point.y()
            o['polygon'].append(p)

        return o

    def from_dict(self, o: dict):
        self.support = list()
        self.polygon = QPolygonF()

        for r in o['support']:
            x = r['x']
            y = r['y']
            w = r['width']
            h = r['height']
            s = QRectF(x, y, w, h)
            self.support.append(s)

        for p in o['polygon']:
            x = p['x']
            y = p['y']
            point = QPointF(x, y)
            self.polygon.append(point)

        return self
Esempio n. 3
0
def series_to_polyline(xdata, ydata):
    size = len(xdata)
    polyline = QPolygonF(size)
    pointer = polyline.data()
    dtype, tinfo = np.float, np.finfo  # integers: = np.int, np.iinfo
    pointer.setsize(2 * polyline.size() * tinfo(dtype).dtype.itemsize)
    memory = np.frombuffer(pointer, dtype)
    memory[:(size - 1) * 2 + 1:2] = xdata
    memory[1:(size - 1) * 2 + 2:2] = ydata
    return polyline
Esempio n. 4
0
def series_to_polyline(xdata, ydata):

    #inspired from https://github.com/PierreRaybaut/plotpy/wiki/Using-Qt-Charts-(PyQtChart)-to-plot-curves-efficiently-in-Python!
    size = len(xdata)
    polyline = QPolygonF(size)
    pointer = polyline.data()
    dtype, tinfo = np.float, np.finfo  # integers: = np.int, np.iinfo
    pointer.setsize(2 * polyline.size() * tinfo(dtype).dtype.itemsize)
    memory = np.frombuffer(pointer, dtype)
    memory[:(size - 1) * 2 + 1:2] = xdata
    memory[1:(size - 1) * 2 + 2:2] = ydata
    return polyline
Esempio n. 5
0
def series_to_polyline(xdata, ydata):
    """Convert series data to QPolygon(F) polyline
    
    This code is derived from PythonQwt's function named 
    `qwt.plot_curve.series_to_polyline`"""
    size = len(xdata)
    polyline = QPolygonF(size)
    pointer = polyline.data()
    dtype, tinfo = np.float, np.finfo  # integers: = np.int, np.iinfo
    pointer.setsize(2 * polyline.size() * tinfo(dtype).dtype.itemsize)
    memory = np.frombuffer(pointer, dtype)
    memory[:(size - 1) * 2 + 1:2] = xdata
    memory[1:(size - 1) * 2 + 2:2] = ydata
    return polyline
Esempio n. 6
0
 def rotatePolygonClockWiseRad(self, p: QPolygonF, angle, origin):
     '''
     Rotate a polygon around a point
     :param p: polygon to rotate
     :param origin: the rotation center
     :param angle: rotation angle, in radians
     :return p: Polygon p rotated by angle around the origin point
     '''
     for i in range(p.size()):
         curr = p.at(i)
         x = curr.x()
         y = curr.y()
         curr.setX(((cos(angle) * (x-origin.x())) + (-sin(angle) * (y-origin.y()))) + origin.x())
         curr.setY(((sin(angle) * (x-origin.x())) + (cos(angle) * (y-origin.y()))) + origin.y())
         p.replace(i, curr)
Esempio n. 7
0
 def series_to_polyline(self, xdata, ydata): #convert plot data for Qt
     """Convert series data to QPolygon(F) polyline
     This code is derived from PythonQwt's function named 
     `qwt.plot_curve.series_to_polyline`"""
     xsize = len(xdata)
     ysize = len(ydata)
     if xsize != ysize:
         root = Tk()
         root.withdraw()
         messagebox.showinfo("Live Plot Error!", "Check force file/video file\n" + \
                             "Exception: x axis and y axis array sizes don't match")
         root.destroy()
         self.playStatus = False
         
     polyline = QPolygonF(xsize)
     pointer = polyline.data()
     dtype, tinfo = np.float, np.finfo  # integers: = np.int, np.iinfo
     pointer.setsize(2*polyline.size()*tinfo(dtype).dtype.itemsize)
     memory = np.frombuffer(pointer, dtype)
     memory[:(xsize-1)*2+1:2] = xdata
     memory[1:(ysize-1)*2+2:2] = ydata
     return polyline   
Esempio n. 8
0
def series_to_polyline(xdata, ydata):
    """Convert series data to QPolygon(F) polyline

    This code is derived from PythonQwt's function named
    `qwt.plot_curve.series_to_polyline`"""
    size = len(xdata)
    dtype = np.float
    tinfo = np.finfo
    dtypesize = tinfo(dtype).dtype.itemsize

    polyline = QPolygonF(size)
    polyline_size = polyline.size()

    pointer = polyline.data()
    pointer_size = 2 * polyline_size * dtypesize
    pointer.setsize(pointer_size)

    memory = np.frombuffer(pointer, dtype)
    memory[:(size - 1) * 2 + 1:2] = xdata.T
    memory[1:(size - 1) * 2 + 2:2] = ydata.T

    return polyline
class Triangluation(CoordWidget):
    def __init__(self):
        super(Triangluation, self).__init__()
        self.setGeometry(300, 300, 800, 600)
        self.setWindowTitle('Triangluation ')
        self.points = []  # screen pos
        self.polygon = QPolygonF()

        self.holePoints = []  # screen pos
        self.holePolygon = QPolygonF()

        self.lineSeg = []
        self.indenpandentPoints = []  # screen

    def mouseMoveEvent(self, e):
        newPos = e.pos()
        if (self.lastPos - newPos).manhattanLength() < 1:
            self.lastPos = newPos
            return
        if e.buttons() & Qt.RightButton:
            translation = self.screenToWorld(newPos) - self.screenToWorld(
                self.lastPos)
            self.scene_translation_ = self.scene_translation_ + translation
        elif e.buttons() & Qt.LeftButton:
            pass
        self.lastPos = e.pos()
        self.update()

    def mousePressEvent(self, qMousePressEvent):
        e = qMousePressEvent
        modifiers = QtWidgets.QApplication.keyboardModifiers()
        if e.buttons() & Qt.LeftButton:
            self.take_screenshot()
            if modifiers == QtCore.Qt.ControlModifier:  # record a hole
                self.holePoints.append(e.pos())
                self.makeConvexHull(self.holePoints, self.holePolygon)
            elif modifiers == QtCore.Qt.ShiftModifier:  # record a line seg
                if len(self.lineSeg) < 3:
                    self.lineSeg.append(self.screenToWorld(e.pos()))
            elif modifiers == QtCore.Qt.AltModifier:
                self.indenpandentPoints.append(e.pos())
            else:
                self.points.append(e.pos())
                self.makeConvexHull(self.points, self.polygon)
        self.lastPos = e.pos()
        self.update()

    def isRightTurn(self, p0, p1, p2):
        v1x = p1.x() - p0.x()
        v1y = p1.y() - p0.y()
        v2x = p2.x() - p1.x()
        v2y = p2.y() - p1.y()
        if v1x * v2y - v1y * v2x > 0.0:
            return False
        else:
            return True

    def makeConvexHull(self, points, polygon):
        verticesIter = map(lambda p: self.screenToWorld(p), points)
        vertices = list(verticesIter)
        vertices.sort(key=lambda p: (p.x(), p.y()))
        if len(vertices) < 3:
            return
        upper = [vertices[0], vertices[1]]
        for v in vertices[2:len(vertices)]:
            upper.append(v)
            while len(upper) > 2 and self.isRightTurn(upper[-3], upper[-2],
                                                      upper[-1]):
                del upper[-2]
        lower = [vertices[-1], vertices[-2]]
        for v in reversed(vertices[0:-3]):
            lower.append(v)
            while len(lower) > 2 and self.isRightTurn(lower[-3], lower[-2],
                                                      lower[-1]):
                del lower[-2]
        del lower[0]
        upper.extend(lower)
        polygon.clear()
        for v in upper:
            polygon.append(v)

    # http://www.cs.cmu.edu/~quake/triangle.html
    def triangulate(self):
        vertices = []
        segments = []
        outBoundarySegments = []
        for i in range(self.polygon.size() - 1):
            v = self.polygon.at(i)
            vertices.append((v.x(), v.y()))
            if i == (self.polygon.size() - 2):
                outBoundarySegments.append((i, 0))
            else:
                outBoundarySegments.append((i, i + 1))
        outVertexNum = len(vertices)
        for i in range(self.holePolygon.size() - 1):
            v = self.holePolygon.at(i)
            vertices.append((v.x(), v.y()))
            n = i + outVertexNum
            if i == (self.holePolygon.size() - 2):
                segments.append((n, outVertexNum))
            else:
                segments.append((n, n + 1))
        v = self.lineSeg[0]
        vertices.append((v.x(), v.y()))
        v = self.lineSeg[1]
        vertices.append((v.x(), v.y()))
        segments.append((len(vertices) - 2, len(vertices) - 1))
        for p in self.indenpandentPoints:
            v = self.screenToWorld(p)
            vertices.append((v.x(), v.y()))
        holeMarkerPos = []
        center = self.holePolygon.boundingRect().center()
        holeMarkerPos.append((center.x(), center.y()))
        segments = segments + outBoundarySegments
        # A1 = triangle.get_data('face.1')
        A = dict(vertices=np.array(vertices),
                 segments=np.array(segments),
                 holes=np.array(holeMarkerPos))
        B = triangle.triangulate(A, 'pqa0.01c')
        triangle.plot.compare(plt, A, B)  #
        plt.show()

    def keyPressEvent(self, keyEvent):
        e = keyEvent
        if e.key() == Qt.Key_C:
            self.makeConvexHull(self.vertexs, self.polygon)
            self.makeConvexHull(self.holeVertexs, self.holePolygon)
        elif e.key() == Qt.Key_S:
            self.saveGIF()
        elif e.key() == Qt.Key_T:
            self.triangulate()
        super(Triangluation, self).keyPressEvent(keyEvent)

    def drawInWorld(self, qPainter):
        pen = qPainter.pen()
        pen.setColor(QColor.fromRgb(255, 0, 0))
        qPainter.setPen(pen)
        if None is not self.polygon:
            qPainter.drawPolyline(self.polygon)
        pen.setColor(QColor.fromRgb(0, 255, 0))
        qPainter.setPen(pen)
        if None is not self.holePolygon:
            qPainter.drawPolyline(self.holePolygon)
        if len(self.lineSeg) == 2:
            qPainter.drawLine(QLineF(self.lineSeg[0], self.lineSeg[1]))
        pen.setColor(QColor.fromRgb(0, 0, 255))
        qPainter.setPen(pen)

    def drawInScreen(self, qPainter):
        pen = qPainter.pen()
        pen.setWidth(5)
        pen.setColor(QColor.fromRgb(0, 0, 0))
        qPainter.setPen(pen)
        qPainter.resetTransform()
        # draw selected points in screen
        for v in self.points:
            qPainter.drawPoint(v)
        for v in self.indenpandentPoints:
            qPainter.drawPoint(v)

        for i in range(self.polygon.size() - 1):
            qPainter.drawText(self.worldToScreen(self.polygon.at(i)), str(i))
        for i in range(self.holePolygon.size() - 1):
            n = self.polygon.size() + i - 1
            qPainter.drawText(self.worldToScreen(self.holePolygon.at(i)),
                              str(n))
Esempio n. 10
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
Esempio n. 11
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