1357 Zeilen
37 KiB
C++
1357 Zeilen
37 KiB
C++
/**
|
|
* client/src/board/tilepainter.cpp
|
|
*
|
|
* This file is part of the traintastic source code.
|
|
*
|
|
* Copyright (C) 2020-2025 Reinder Feenstra
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "tilepainter.hpp"
|
|
#include <cmath>
|
|
#include <QtMath>
|
|
#include <QPainterPath>
|
|
#include <traintastic/enum/blocktraindirection.hpp>
|
|
#include "boardcolorscheme.hpp"
|
|
#include "../network/object.tpp"
|
|
#include "../network/object/blockrailtile.hpp"
|
|
#include "../network/object/trainblockstatus.hpp"
|
|
#include "../network/abstractvectorproperty.hpp"
|
|
#include "../settings/boardsettings.hpp"
|
|
#include "../utils/rectf.hpp"
|
|
|
|
TilePainter::TilePainter(QPainter& painter, int tileSize, const BoardColorScheme& colorScheme) :
|
|
m_colorScheme{colorScheme},
|
|
m_showBlockSensorStates{BoardSettings::instance().showBlockSensorStates},
|
|
m_turnoutDrawState{BoardSettings::instance().turnoutDrawState},
|
|
m_trackWidth{tileSize / 5},
|
|
m_turnoutMargin{tileSize / 10},
|
|
m_blockPen{m_colorScheme.track},
|
|
m_trackPen(m_colorScheme.track, m_trackWidth, Qt::SolidLine, Qt::FlatCap),
|
|
m_trackDisabledPen(m_colorScheme.trackDisabled, m_trackWidth, Qt::SolidLine, Qt::FlatCap),
|
|
m_trackReservedPen(m_colorScheme.trackReserved, m_trackWidth, Qt::SolidLine, Qt::FlatCap),
|
|
m_trackReservedDisabledPen(m_colorScheme.trackReservedDisabled, m_trackWidth, Qt::SolidLine, Qt::FlatCap),
|
|
m_trackErasePen(m_colorScheme.background, m_trackWidth * 2, Qt::SolidLine, Qt::FlatCap),
|
|
m_turnoutStatePen(m_colorScheme.turnoutState, (m_trackWidth + 1) / 2, Qt::SolidLine, Qt::FlatCap),
|
|
m_painter{painter}
|
|
{
|
|
}
|
|
|
|
void TilePainter::draw(TileId id, const QRectF& r, TileRotate rotate, bool isReserved)
|
|
{
|
|
switch(id)
|
|
{
|
|
case TileId::RailStraight:
|
|
setTrackPen(isReserved);
|
|
drawStraight(r, rotate);
|
|
break;
|
|
|
|
case TileId::RailCurve45:
|
|
setTrackPen(isReserved);
|
|
drawCurve45(r, rotate);
|
|
break;
|
|
|
|
case TileId::RailCurve90:
|
|
setTrackPen(isReserved);
|
|
drawCurve90(r, rotate);
|
|
break;
|
|
|
|
case TileId::RailCross45:
|
|
case TileId::RailCross90:
|
|
drawCross(id, r, rotate);
|
|
break;
|
|
|
|
case TileId::RailBridge45Left:
|
|
case TileId::RailBridge45Right:
|
|
case TileId::RailBridge90:
|
|
drawBridge(id, r, rotate);
|
|
break;
|
|
|
|
case TileId::RailTurnoutLeft45:
|
|
case TileId::RailTurnoutLeft90:
|
|
case TileId::RailTurnoutLeftCurved:
|
|
case TileId::RailTurnoutRight45:
|
|
case TileId::RailTurnoutRight90:
|
|
case TileId::RailTurnoutRightCurved:
|
|
case TileId::RailTurnoutWye:
|
|
case TileId::RailTurnout3Way:
|
|
case TileId::RailTurnoutSingleSlip:
|
|
case TileId::RailTurnoutDoubleSlip:
|
|
drawTurnout(id, r, rotate);
|
|
break;
|
|
|
|
case TileId::RailSensor:
|
|
drawSensor(id, r, rotate);
|
|
break;
|
|
|
|
case TileId::RailBufferStop:
|
|
setTrackPen(isReserved);
|
|
drawBufferStop(r, rotate);
|
|
break;
|
|
|
|
case TileId::RailSignal2Aspect:
|
|
case TileId::RailSignal3Aspect:
|
|
drawSignal(id, r, rotate, isReserved);
|
|
break;
|
|
|
|
case TileId::RailBlock:
|
|
drawBlock(id, r, rotate);
|
|
break;
|
|
|
|
case TileId::RailTunnel:
|
|
{
|
|
setTrackPen(isReserved);
|
|
drawStraight(r, rotate);
|
|
|
|
// tunnel arc:
|
|
const int angle = -toDeg(rotate) * 16 - 45 * 8; // - 22.5 deg
|
|
const int angleLength = 225 * 16; // 225 deg
|
|
const qreal m = r.width() / 5;
|
|
const QRectF rArc = r.adjusted(m, m, -m, -m);
|
|
|
|
m_painter.setPen(QPen(m_colorScheme.background, m_trackWidth, Qt::SolidLine, Qt::FlatCap));
|
|
m_painter.drawArc(rArc, angle, angleLength);
|
|
m_painter.setPen(QPen(m_colorScheme.track, m_trackWidth / 2., Qt::SolidLine, Qt::FlatCap));
|
|
m_painter.drawArc(rArc, angle, angleLength);
|
|
break;
|
|
}
|
|
case TileId::RailDirectionControl:
|
|
drawDirectionControl(id, r, rotate, isReserved);
|
|
break;
|
|
|
|
case TileId::RailOneWay:
|
|
{
|
|
setTrackPen(isReserved);
|
|
drawStraight(r, rotate);
|
|
m_painter.setBrush(m_painter.pen().color());
|
|
|
|
const qreal m = r.width() / 4;
|
|
QRectF rTriangle = r.adjusted(m, m, -m, -m);
|
|
m_painter.save();
|
|
m_painter.translate(rTriangle.center());
|
|
m_painter.rotate(toDeg(rotate));
|
|
rTriangle.moveCenter(QPointF{0, 0});
|
|
QPen pen = m_painter.pen();
|
|
pen.setJoinStyle(Qt::RoundJoin);
|
|
m_painter.setPen(pen);
|
|
drawTriangle(rTriangle);
|
|
m_painter.restore();
|
|
break;
|
|
}
|
|
case TileId::PushButton:
|
|
drawPushButton(r);
|
|
break;
|
|
|
|
case TileId::RailLink:
|
|
setTrackPen(isReserved);
|
|
drawLink(r, rotate);
|
|
break;
|
|
|
|
case TileId::RailDecoupler:
|
|
drawRailDecoupler(r, rotate, isReserved);
|
|
break;
|
|
|
|
case TileId::RailNXButton:
|
|
drawRailNX(r, rotate, isReserved);
|
|
break;
|
|
|
|
case TileId::Label:
|
|
drawLabel(r, rotate);
|
|
break;
|
|
|
|
case TileId::Switch:
|
|
drawSwitch(r);
|
|
break;
|
|
|
|
case TileId::None:
|
|
case TileId::HiddenRailCrossOver:
|
|
case TileId::ReservedForFutureExpension:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawBridge(TileId id, const QRectF& r, TileRotate rotate, bool isReservedAC, bool isReservedBD)
|
|
{
|
|
switch(id)
|
|
{
|
|
case TileId::RailBridge45Left:
|
|
setTrackPen(isReservedAC);
|
|
drawStraight(r, rotate);
|
|
setTrackErasePen();
|
|
drawStraight(r, rotate - TileRotate::Deg45);
|
|
setTrackPen(isReservedBD);
|
|
drawStraight(r, rotate - TileRotate::Deg45);
|
|
break;
|
|
|
|
case TileId::RailBridge45Right:
|
|
setTrackPen(isReservedAC);
|
|
drawStraight(r, rotate);
|
|
setTrackErasePen();
|
|
drawStraight(r, rotate + TileRotate::Deg45);
|
|
setTrackPen(isReservedBD);
|
|
drawStraight(r, rotate + TileRotate::Deg45);
|
|
break;
|
|
|
|
case TileId::RailBridge90:
|
|
setTrackPen(isReservedAC);
|
|
drawStraight(r, rotate);
|
|
setTrackErasePen();
|
|
drawStraight(r, rotate + TileRotate::Deg90);
|
|
setTrackPen(isReservedBD);
|
|
drawStraight(r, rotate + TileRotate::Deg90);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawCross(TileId id, const QRectF& r, TileRotate rotate, CrossState reservedState)
|
|
{
|
|
switch(id)
|
|
{
|
|
case TileId::RailCross45:
|
|
setTrackPen();
|
|
if(reservedState != CrossState::AC)
|
|
{
|
|
drawStraight(r, rotate);
|
|
}
|
|
if(reservedState != CrossState::BD)
|
|
{
|
|
drawStraight(r, rotate - TileRotate::Deg45);
|
|
}
|
|
|
|
if(reservedState != CrossState::Unset)
|
|
{
|
|
setTrackPen(true);
|
|
switch(reservedState)
|
|
{
|
|
case CrossState::AC:
|
|
drawStraight(r, rotate);
|
|
break;
|
|
|
|
case CrossState::BD:
|
|
drawStraight(r, rotate - TileRotate::Deg45);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TileId::RailCross90:
|
|
setTrackPen();
|
|
if(reservedState != CrossState::AC)
|
|
{
|
|
drawStraight(r, rotate);
|
|
}
|
|
if(reservedState != CrossState::BD)
|
|
{
|
|
drawStraight(r, rotate - TileRotate::Deg90);
|
|
}
|
|
|
|
if(reservedState != CrossState::Unset)
|
|
{
|
|
setTrackPen(true);
|
|
switch(reservedState)
|
|
{
|
|
case CrossState::AC:
|
|
drawStraight(r, rotate);
|
|
break;
|
|
|
|
case CrossState::BD:
|
|
drawStraight(r, rotate - TileRotate::Deg90);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawSensor(TileId id, const QRectF& r, TileRotate rotate, bool isReserved, SensorState state)
|
|
{
|
|
switch(id)
|
|
{
|
|
case TileId::RailSensor:
|
|
{
|
|
setTrackPen(isReserved);
|
|
drawStraight(r, rotate);
|
|
const qreal sz = r.width() / 4;
|
|
drawLED(r.adjusted(sz, sz, -sz, -sz), sensorStateToColor(state), m_colorScheme.track);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawDirectionControl(TileId id, const QRectF& r, TileRotate rotate, bool isReserved, DirectionControlState state)
|
|
{
|
|
switch(id)
|
|
{
|
|
case TileId::RailDirectionControl:
|
|
{
|
|
setTrackPen(isReserved);
|
|
drawStraight(r, rotate);
|
|
|
|
QPen pen{m_trackPen};
|
|
const qreal w = pen.width() / 2;
|
|
pen.setWidth(w);
|
|
switch(state)
|
|
{
|
|
case DirectionControlState::None:
|
|
pen.setColor(Qt::red);
|
|
break;
|
|
|
|
case DirectionControlState::AtoB:
|
|
case DirectionControlState::BtoA:
|
|
pen.setColor(Qt::blue);
|
|
break;
|
|
|
|
|
|
case DirectionControlState::Both:
|
|
pen.setColor(Qt::darkGreen);
|
|
break;
|
|
}
|
|
m_painter.setPen(pen);
|
|
m_painter.setBrush(pen.color());
|
|
m_painter.drawEllipse(r.adjusted(w, w, -w, -w));
|
|
|
|
const qreal m = r.width() / 3;
|
|
QRectF rSign = r.adjusted(m, m, -m, -m);
|
|
m_painter.save();
|
|
m_painter.translate(rSign.center());
|
|
m_painter.rotate(toDeg(rotate + (state == DirectionControlState::BtoA ? TileRotate::Deg180 : TileRotate::Deg0)));
|
|
rSign.moveCenter(QPointF{0, 0});
|
|
pen = m_trackPen;
|
|
pen.setColor(Qt::white);
|
|
pen.setBrush(pen.color());
|
|
pen.setCapStyle(Qt::RoundCap);
|
|
pen.setJoinStyle(Qt::RoundJoin);
|
|
m_painter.setPen(pen);
|
|
|
|
switch(state)
|
|
{
|
|
case DirectionControlState::None:
|
|
m_painter.drawLine(centerLeft(rSign), centerRight(rSign));
|
|
break;
|
|
|
|
case DirectionControlState::AtoB:
|
|
case DirectionControlState::BtoA:
|
|
drawTriangle(rSign);
|
|
break;
|
|
|
|
case DirectionControlState::Both:
|
|
m_painter.drawLine(topCenter(rSign), bottomCenter(rSign));
|
|
break;
|
|
}
|
|
|
|
m_painter.restore();
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawTurnout(TileId id, const QRectF& r, TileRotate rotate, TurnoutPosition reservedPosition, TurnoutPosition position)
|
|
{
|
|
switch(id)
|
|
{
|
|
case TileId::RailTurnoutLeft45:
|
|
case TileId::RailTurnoutLeft90:
|
|
case TileId::RailTurnoutLeftCurved:
|
|
case TileId::RailTurnoutRight45:
|
|
case TileId::RailTurnoutRight90:
|
|
case TileId::RailTurnoutRightCurved:
|
|
case TileId::RailTurnoutWye:
|
|
case TileId::RailTurnout3Way:
|
|
drawTurnoutStandard(id, r, rotate, reservedPosition, position);
|
|
break;
|
|
|
|
case TileId::RailTurnoutSingleSlip:
|
|
case TileId::RailTurnoutDoubleSlip:
|
|
drawTurnoutSlip(id, r, rotate, reservedPosition, position);
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawSignal(TileId id, const QRectF& r, TileRotate rotate, bool isReserved, SignalAspect aspect)
|
|
{
|
|
switch(id)
|
|
{
|
|
case TileId::RailSignal2Aspect:
|
|
setTrackPen(isReserved);
|
|
drawStraight(r, rotate);
|
|
drawSignalDirection(r, rotate);
|
|
drawSignal2Aspect(r, rotate, aspect);
|
|
break;
|
|
|
|
case TileId::RailSignal3Aspect:
|
|
setTrackPen(isReserved);
|
|
drawStraight(r, rotate);
|
|
drawSignalDirection(r, rotate);
|
|
drawSignal3Aspect(r, rotate, aspect);
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawBlock(TileId id, const QRectF& r, TileRotate rotate, bool isReservedA, bool isReservedB, const ObjectPtr& blockTile)
|
|
{
|
|
switch(id)
|
|
{
|
|
case TileId::RailBlock:
|
|
drawRailBlock(r, rotate, isReservedA, isReservedB, blockTile);
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawPushButton(const QRectF& r, Color color, Color textColor, const QString& text)
|
|
{
|
|
const auto size = std::min(r.height(), r.width());
|
|
m_painter.setPen(QPen(Qt::gray, size / 10));
|
|
m_painter.setBrush(toQColor(color));
|
|
const qreal radius = size * 0.4;
|
|
if(r.height() == r.width())
|
|
{
|
|
m_painter.drawEllipse(r.center(), radius, radius);
|
|
}
|
|
else
|
|
{
|
|
const auto margin = size * 0.1;
|
|
m_painter.drawRoundedRect(r.adjusted(margin, margin, -margin, -margin), radius, radius);
|
|
}
|
|
|
|
if(!text.isEmpty())
|
|
{
|
|
const auto margin = size * 0.2;
|
|
m_painter.setPen(toQColor(textColor));
|
|
m_painter.drawText(r.adjusted(margin, margin, -margin, -margin), text, QTextOption(Qt::AlignCenter));
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawSwitch(const QRectF& r, bool value, Color colorOn, Color colorOff)
|
|
{
|
|
m_painter.setPen(QPen(Qt::gray, r.width() / 10));
|
|
m_painter.setBrush(toQColor(value ? colorOn : colorOff));
|
|
const qreal margin = r.width() * 0.1;
|
|
const qreal radius = r.width() * 0.15;
|
|
m_painter.drawRoundedRect(r.adjusted(margin, margin, -margin, -margin), radius, radius);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
QColor TilePainter::sensorStateToColor(SensorState value) const
|
|
{
|
|
switch(value)
|
|
{
|
|
case SensorState::Occupied:
|
|
return m_colorScheme.sensorOccupied;
|
|
|
|
case SensorState::Free:
|
|
return m_colorScheme.sensorFree;
|
|
|
|
case SensorState::Idle:
|
|
return m_colorScheme.sensorIdle;
|
|
|
|
case SensorState::Triggered:
|
|
return m_colorScheme.sensorTriggered;
|
|
|
|
case SensorState::Unknown:
|
|
return m_colorScheme.sensorUnknown;
|
|
}
|
|
assert(false);
|
|
return QColor();
|
|
}
|
|
|
|
void TilePainter::setBlockStateBrush(BlockState value)
|
|
{
|
|
switch(value)
|
|
{
|
|
case BlockState::Occupied:
|
|
m_painter.setBrush(m_colorScheme.blockOccupied);
|
|
break;
|
|
|
|
case BlockState::Free:
|
|
m_painter.setBrush(m_colorScheme.blockFree);
|
|
break;
|
|
|
|
case BlockState::Reserved:
|
|
m_painter.setBrush(m_colorScheme.blockReserved);
|
|
break;
|
|
|
|
case BlockState::Unknown:
|
|
m_painter.setBrush(m_colorScheme.blockUnknown);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawStraight(const QRectF& r, TileRotate rotate)
|
|
{
|
|
switch(rotate)
|
|
{
|
|
case TileRotate::Deg0:
|
|
case TileRotate::Deg180:
|
|
m_painter.drawLine(bottomCenter(r), topCenter(r));
|
|
break;
|
|
|
|
case TileRotate::Deg45:
|
|
case TileRotate::Deg225:
|
|
m_painter.drawLine(r.bottomLeft(), r.topRight());
|
|
break;
|
|
|
|
case TileRotate::Deg90:
|
|
case TileRotate::Deg270:
|
|
m_painter.drawLine(centerLeft(r), centerRight(r));
|
|
break;
|
|
|
|
case TileRotate::Deg135:
|
|
case TileRotate::Deg315:
|
|
m_painter.drawLine(r.bottomRight(), r.topLeft());
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawCurve45(const QRectF& r, TileRotate rotate)
|
|
{
|
|
QPainterPath path;
|
|
switch(rotate)
|
|
{
|
|
case TileRotate::Deg0:
|
|
path.moveTo(bottomCenter(r));
|
|
path.quadTo(r.center(), r.topLeft());
|
|
break;
|
|
|
|
case TileRotate::Deg45:
|
|
path.moveTo(r.bottomLeft());
|
|
path.quadTo(r.center(), topCenter(r));
|
|
break;
|
|
|
|
case TileRotate::Deg90:
|
|
path.moveTo(centerLeft(r));
|
|
path.quadTo(r.center(), r.topRight());
|
|
break;
|
|
|
|
case TileRotate::Deg135:
|
|
path.moveTo(r.topLeft());
|
|
path.quadTo(r.center(), centerRight(r));
|
|
break;
|
|
|
|
case TileRotate::Deg180:
|
|
path.moveTo(topCenter(r));
|
|
path.quadTo(r.center(), r.bottomRight());
|
|
break;
|
|
|
|
case TileRotate::Deg225:
|
|
path.moveTo(r.topRight());
|
|
path.quadTo(r.center(), bottomCenter(r));
|
|
break;
|
|
|
|
case TileRotate::Deg270:
|
|
path.moveTo(centerRight(r));
|
|
path.quadTo(r.center(), r.bottomLeft());
|
|
break;
|
|
|
|
case TileRotate::Deg315:
|
|
path.moveTo(r.bottomRight());
|
|
path.quadTo(r.center(), centerLeft(r));
|
|
break;
|
|
}
|
|
m_painter.drawPath(path);
|
|
}
|
|
|
|
void TilePainter::drawCurve90(QRectF r, TileRotate rotate)
|
|
{
|
|
const int spanAngle = 90 * 16;
|
|
const qreal size = r.width();
|
|
|
|
if(isDiagonal(rotate))
|
|
{
|
|
const qreal grow = size * 0.5 * (std::sqrt(2) - 1);
|
|
r.adjust(-grow, -grow, grow, grow);
|
|
}
|
|
|
|
switch(rotate)
|
|
{
|
|
case TileRotate::Deg0:
|
|
r.translate(-size * 0.5, size * 0.5);
|
|
m_painter.drawArc(r, 0 * 16, spanAngle);
|
|
break;
|
|
|
|
case TileRotate::Deg45:
|
|
r.translate(-size, 0);
|
|
m_painter.drawArc(r, 315 * 16, spanAngle);
|
|
break;
|
|
|
|
case TileRotate::Deg90:
|
|
r.translate(-size * 0.5, -size * 0.5);
|
|
m_painter.drawArc(r, 270 * 16, spanAngle);
|
|
break;
|
|
|
|
case TileRotate::Deg135:
|
|
r.translate(0, -size);
|
|
m_painter.drawArc(r, 225 * 16, spanAngle);
|
|
break;
|
|
|
|
case TileRotate::Deg180:
|
|
r.translate(size * 0.5, -size * 0.5);
|
|
m_painter.drawArc(r, 180 * 16, spanAngle);
|
|
break;
|
|
|
|
case TileRotate::Deg225:
|
|
r.translate(size, 0);
|
|
m_painter.drawArc(r, 135 * 16, spanAngle);
|
|
break;
|
|
|
|
case TileRotate::Deg270:
|
|
r.translate(size * 0.5, size * 0.5);
|
|
m_painter.drawArc(r, 90 * 16, spanAngle);
|
|
break;
|
|
|
|
case TileRotate::Deg315:
|
|
r.translate(0, size);
|
|
m_painter.drawArc(r, 45 * 16, spanAngle);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawBufferStop(const QRectF& r, TileRotate rotate)
|
|
{
|
|
const qreal stopSize = 0.5 + qRound(r.width() / 4);
|
|
const qreal stopSizeDiagonal = stopSize / std::sqrt(2);
|
|
const qreal cx = r.center().x();
|
|
const qreal cy = r.center().y();
|
|
|
|
switch(rotate)
|
|
{
|
|
case TileRotate::Deg0:
|
|
m_painter.drawLine(bottomCenter(r), r.center());
|
|
m_painter.drawLine(QPointF{cx - stopSize, cy}, QPointF{cx + stopSize, cy});
|
|
break;
|
|
|
|
case TileRotate::Deg45:
|
|
m_painter.drawLine(r.bottomLeft(), r.center());
|
|
m_painter.drawLine(QPointF{cx - stopSizeDiagonal, cy - stopSizeDiagonal}, QPointF{cx + stopSizeDiagonal, cy + stopSizeDiagonal});
|
|
break;
|
|
|
|
case TileRotate::Deg90:
|
|
m_painter.drawLine(centerLeft(r), r.center());
|
|
m_painter.drawLine(QPointF{cx, cy - stopSize}, QPointF{cx, cy + stopSize});
|
|
break;
|
|
|
|
case TileRotate::Deg135:
|
|
m_painter.drawLine(r.topLeft(), r.center());
|
|
m_painter.drawLine(QPointF{cx - stopSizeDiagonal, cy + stopSizeDiagonal}, QPointF{cx + stopSizeDiagonal, cy - stopSizeDiagonal});
|
|
break;
|
|
|
|
case TileRotate::Deg180:
|
|
m_painter.drawLine(topCenter(r), r.center());
|
|
m_painter.drawLine(QPointF{cx - stopSize, cy}, QPointF{cx + stopSize, cy});
|
|
break;
|
|
|
|
case TileRotate::Deg225:
|
|
m_painter.drawLine(r.topRight(), r.center());
|
|
m_painter.drawLine(QPointF{cx - stopSizeDiagonal, cy - stopSizeDiagonal}, QPointF{cx + stopSizeDiagonal, cy + stopSizeDiagonal});
|
|
break;
|
|
|
|
case TileRotate::Deg270:
|
|
m_painter.drawLine(centerRight(r), r.center());
|
|
m_painter.drawLine(QPointF{cx, cy - stopSize}, QPointF{cx, cy + stopSize});
|
|
break;
|
|
|
|
case TileRotate::Deg315:
|
|
m_painter.drawLine(r.bottomRight(), r.center());
|
|
m_painter.drawLine(QPointF{cx - stopSizeDiagonal, cy + stopSizeDiagonal}, QPointF{cx + stopSizeDiagonal, cy - stopSizeDiagonal});
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawLink(const QRectF& r, TileRotate rotate)
|
|
{
|
|
switch(rotate)
|
|
{
|
|
case TileRotate::Deg0:
|
|
m_painter.drawLine(bottomCenter(r), r.center());
|
|
break;
|
|
|
|
case TileRotate::Deg45:
|
|
m_painter.drawLine(r.bottomLeft(), r.center());
|
|
break;
|
|
|
|
case TileRotate::Deg90:
|
|
m_painter.drawLine(centerLeft(r), r.center());
|
|
break;
|
|
|
|
case TileRotate::Deg135:
|
|
m_painter.drawLine(r.topLeft(), r.center());
|
|
break;
|
|
|
|
case TileRotate::Deg180:
|
|
m_painter.drawLine(topCenter(r), r.center());
|
|
break;
|
|
|
|
case TileRotate::Deg225:
|
|
m_painter.drawLine(r.topRight(), r.center());
|
|
break;
|
|
|
|
case TileRotate::Deg270:
|
|
m_painter.drawLine(centerRight(r), r.center());
|
|
break;
|
|
|
|
case TileRotate::Deg315:
|
|
m_painter.drawLine(r.bottomRight(), r.center());
|
|
break;
|
|
}
|
|
|
|
const qreal radius = r.width() / 6;
|
|
m_painter.setBrush(m_painter.pen().color());
|
|
m_painter.drawEllipse(r.center(), radius, radius);
|
|
}
|
|
|
|
void TilePainter::drawSignal2Aspect(QRectF r, TileRotate rotate, SignalAspect aspect)
|
|
{
|
|
m_painter.save();
|
|
m_painter.translate(r.center());
|
|
m_painter.rotate(toDeg(rotate));
|
|
r.moveCenter(QPointF{0, 0});
|
|
|
|
const qreal x1 = r.left() + r.width() * 0.3;
|
|
const qreal x2 = r.right() - r.width() * 0.3;
|
|
const qreal y1 = r.top() + r.height() * 0.4;
|
|
const qreal y2 = r.bottom() - r.height() * 0.4;
|
|
const qreal w = x2 - x1;
|
|
const qreal lampRadius = w / 4;
|
|
|
|
QPainterPath path;
|
|
path.moveTo(x1, y1);
|
|
path.lineTo(x1, y2);
|
|
path.arcTo(QRectF{x1, y2 - (w / 2), w, w}, 180, 180);
|
|
path.lineTo(x2, y1);
|
|
path.arcTo(QRectF{x1, y1 - (w / 2), w, w}, 0, 180);
|
|
m_painter.setPen(QPen{Qt::white, lampRadius / 2});
|
|
m_painter.fillPath(path, Qt::black);
|
|
m_painter.drawPath(path);
|
|
|
|
m_painter.setPen(Qt::NoPen);
|
|
switch(aspect)
|
|
{
|
|
case SignalAspect::Stop:
|
|
m_painter.setBrush(signalRed);
|
|
m_painter.drawEllipse(QPointF{r.center().x(), r.center().y() - lampRadius}, lampRadius, lampRadius);
|
|
break;
|
|
|
|
case SignalAspect::Proceed:
|
|
m_painter.setBrush(signalGreen);
|
|
m_painter.drawEllipse(QPointF{r.center().x(), r.center().y() + lampRadius}, lampRadius, lampRadius);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_painter.restore();
|
|
}
|
|
|
|
void TilePainter::drawTriangle(const QRectF& r)
|
|
{
|
|
const std::array<QPointF, 3> points = {{
|
|
{r.center().x(), r.top()},
|
|
{r.right(), r.bottom()},
|
|
{r.left(), r.bottom()}}};
|
|
|
|
m_painter.drawConvexPolygon(points.data(), static_cast<int>(points.size()));
|
|
}
|
|
|
|
void TilePainter::drawLED(const QRectF& r, const QColor& color, const QColor& borderColor)
|
|
{
|
|
if(borderColor.isValid())
|
|
m_painter.setPen(borderColor);
|
|
else
|
|
m_painter.setPen(Qt::NoPen);
|
|
|
|
m_painter.setBrush(color);
|
|
|
|
m_painter.drawEllipse(r);
|
|
}
|
|
|
|
void TilePainter::drawTurnoutStandard(TileId id, const QRectF& r, TileRotate rotate, TurnoutPosition reservedPosition, TurnoutPosition position)
|
|
{
|
|
const bool hasLeft90 =
|
|
id == TileId::RailTurnoutLeft90 ||
|
|
id == TileId::RailTurnoutLeftCurved;
|
|
|
|
const bool hasLeft =
|
|
hasLeft90 ||
|
|
id == TileId::RailTurnoutLeft45 ||
|
|
id == TileId::RailTurnoutWye ||
|
|
id == TileId::RailTurnout3Way;
|
|
|
|
const bool hasStraight =
|
|
id == TileId::RailTurnoutLeft45 ||
|
|
id == TileId::RailTurnoutLeft90 ||
|
|
id == TileId::RailTurnoutLeftCurved ||
|
|
id == TileId::RailTurnoutRight45 ||
|
|
id == TileId::RailTurnoutRight90 ||
|
|
id == TileId::RailTurnoutRightCurved ||
|
|
id == TileId::RailTurnout3Way;
|
|
|
|
const bool hasRight90 =
|
|
id == TileId::RailTurnoutRight90 ||
|
|
id == TileId::RailTurnoutRightCurved;
|
|
|
|
const bool hasRight =
|
|
hasRight90 ||
|
|
id == TileId::RailTurnoutRight45 ||
|
|
id == TileId::RailTurnoutWye ||
|
|
id == TileId::RailTurnout3Way;
|
|
|
|
setTurnoutPen();
|
|
if(hasLeft && (m_turnoutDrawState || position != TurnoutPosition::Left) && reservedPosition != TurnoutPosition::Left)
|
|
{
|
|
if(hasLeft90)
|
|
{
|
|
drawCurve90(r, rotate);
|
|
}
|
|
else
|
|
{
|
|
drawCurve45(r, rotate);
|
|
}
|
|
}
|
|
if(hasStraight && (m_turnoutDrawState || position != TurnoutPosition::Straight) && reservedPosition != TurnoutPosition::Straight)
|
|
{
|
|
if(id == TileId::RailTurnoutLeftCurved)
|
|
{
|
|
drawCurve45(r, rotate);
|
|
}
|
|
else if(id == TileId::RailTurnoutRightCurved)
|
|
{
|
|
drawCurve45(r, rotate + TileRotate::Deg225);
|
|
}
|
|
else
|
|
{
|
|
drawStraight(r, rotate);
|
|
}
|
|
}
|
|
if(hasRight && (m_turnoutDrawState || position != TurnoutPosition::Right) && reservedPosition != TurnoutPosition::Right)
|
|
{
|
|
if(hasRight90)
|
|
{
|
|
drawCurve90(r, rotate + TileRotate::Deg270);
|
|
}
|
|
else
|
|
{
|
|
drawCurve45(r, rotate + TileRotate::Deg225);
|
|
}
|
|
}
|
|
|
|
if(reservedPosition != TurnoutPosition::Unknown)
|
|
{
|
|
m_painter.setPen(position == reservedPosition ? m_trackReservedPen : m_trackReservedDisabledPen);
|
|
|
|
switch(reservedPosition)
|
|
{
|
|
case TurnoutPosition::Left:
|
|
if(hasLeft90)
|
|
{
|
|
drawCurve90(r, rotate);
|
|
}
|
|
else
|
|
{
|
|
drawCurve45(r, rotate);
|
|
}
|
|
break;
|
|
|
|
case TurnoutPosition::Straight:
|
|
if(id == TileId::RailTurnoutLeftCurved)
|
|
{
|
|
drawCurve45(r, rotate);
|
|
}
|
|
else if(id == TileId::RailTurnoutRightCurved)
|
|
{
|
|
drawCurve45(r, rotate + TileRotate::Deg225);
|
|
}
|
|
else
|
|
{
|
|
drawStraight(r, rotate);
|
|
}
|
|
break;
|
|
|
|
case TurnoutPosition::Right:
|
|
if(hasRight90)
|
|
{
|
|
drawCurve90(r, rotate + TileRotate::Deg270);
|
|
}
|
|
else
|
|
{
|
|
drawCurve45(r, rotate + TileRotate::Deg225);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(m_turnoutDrawState || position != reservedPosition)
|
|
{
|
|
setTurnoutStatePen();
|
|
switch(position)
|
|
{
|
|
case TurnoutPosition::Left:
|
|
if(hasLeft90)
|
|
{
|
|
drawCurve90(turnoutStateRect(r), rotate);
|
|
}
|
|
else
|
|
{
|
|
drawCurve45(turnoutStateRect(r), rotate);
|
|
}
|
|
break;
|
|
|
|
case TurnoutPosition::Straight:
|
|
if(id == TileId::RailTurnoutLeftCurved)
|
|
{
|
|
drawCurve45(turnoutStateRect(r), rotate);
|
|
}
|
|
else if(id == TileId::RailTurnoutRightCurved)
|
|
{
|
|
drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg225);
|
|
}
|
|
else
|
|
{
|
|
drawStraight(turnoutStateRect(r), rotate);
|
|
}
|
|
break;
|
|
|
|
case TurnoutPosition::Right:
|
|
if(hasRight90)
|
|
{
|
|
drawCurve90(turnoutStateRect(r), rotate + TileRotate::Deg270);
|
|
}
|
|
else
|
|
{
|
|
drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg225);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawTurnoutSlip(TileId id, const QRectF& r, TileRotate rotate, TurnoutPosition reservedPosition, TurnoutPosition position)
|
|
{
|
|
// Double: Single:
|
|
// C C
|
|
// |\ |
|
|
// B --+-- D B --+-- D
|
|
// \| \|
|
|
// A A
|
|
|
|
const bool isDoubleSlip = id == TileId::RailTurnoutDoubleSlip;
|
|
|
|
const bool positionAB = position == TurnoutPosition::Left || position == TurnoutPosition::Diverged;
|
|
const bool positionAC = position == TurnoutPosition::DoubleSlipStraightA || position == TurnoutPosition::Crossed;
|
|
const bool positionBD = position == TurnoutPosition::DoubleSlipStraightB || position == TurnoutPosition::Crossed;
|
|
const bool positionCD = isDoubleSlip && (position == TurnoutPosition::Right || position == TurnoutPosition::Diverged);
|
|
|
|
const bool reservedPositionAB = reservedPosition == TurnoutPosition::Left || reservedPosition == TurnoutPosition::Diverged;
|
|
const bool reservedPositionAC = reservedPosition == TurnoutPosition::DoubleSlipStraightA || reservedPosition == TurnoutPosition::Crossed;
|
|
const bool reservedPositionBD = reservedPosition == TurnoutPosition::DoubleSlipStraightB || reservedPosition == TurnoutPosition::Crossed;
|
|
const bool reservedPositionCD = isDoubleSlip && (reservedPosition == TurnoutPosition::Right || reservedPosition == TurnoutPosition::Diverged);
|
|
|
|
setTurnoutPen();
|
|
if((m_turnoutDrawState || !positionAB) && !reservedPositionAB)
|
|
{
|
|
drawCurve45(r, rotate);
|
|
}
|
|
if((m_turnoutDrawState || !positionAC) && !reservedPositionAC)
|
|
{
|
|
drawStraight(r, rotate);
|
|
}
|
|
if((m_turnoutDrawState || !positionBD) && !reservedPositionBD)
|
|
{
|
|
drawStraight(r, rotate - TileRotate::Deg45);
|
|
}
|
|
if(isDoubleSlip && (m_turnoutDrawState || !positionCD) && !reservedPositionCD)
|
|
{
|
|
drawCurve45(r, rotate + TileRotate::Deg180);
|
|
}
|
|
|
|
if(!m_turnoutDrawState)
|
|
{
|
|
setTrackPen();
|
|
if(position == TurnoutPosition::Crossed)
|
|
{
|
|
if(!reservedPositionAC)
|
|
{
|
|
drawStraight(r, rotate);
|
|
}
|
|
if(!reservedPositionBD)
|
|
{
|
|
drawStraight(r, rotate - TileRotate::Deg45);
|
|
}
|
|
}
|
|
else if(position == TurnoutPosition::Diverged)
|
|
{
|
|
if(!reservedPositionAB)
|
|
{
|
|
drawCurve45(r, rotate);
|
|
}
|
|
if(isDoubleSlip && !reservedPositionCD)
|
|
{
|
|
drawCurve45(r, rotate + TileRotate::Deg180);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(reservedPosition != TurnoutPosition::Unknown)
|
|
{
|
|
if(reservedPositionAB && !positionAB)
|
|
{
|
|
m_painter.setPen(m_trackReservedDisabledPen);
|
|
drawCurve45(r, rotate);
|
|
}
|
|
if(reservedPositionAC && !positionAC)
|
|
{
|
|
m_painter.setPen(m_trackReservedDisabledPen);
|
|
drawStraight(r, rotate);
|
|
}
|
|
if(reservedPositionBD && !positionBD)
|
|
{
|
|
m_painter.setPen(m_trackReservedDisabledPen);
|
|
drawStraight(r, rotate - TileRotate::Deg45);
|
|
}
|
|
if(reservedPositionCD && !positionCD)
|
|
{
|
|
m_painter.setPen(m_trackReservedDisabledPen);
|
|
drawCurve45(r, rotate + TileRotate::Deg180);
|
|
}
|
|
|
|
if(reservedPositionAB && positionAB)
|
|
{
|
|
m_painter.setPen(m_trackReservedPen);
|
|
drawCurve45(r, rotate);
|
|
}
|
|
if(reservedPositionAC && positionAC)
|
|
{
|
|
m_painter.setPen(m_trackReservedPen);
|
|
drawStraight(r, rotate);
|
|
}
|
|
if(reservedPositionBD && positionBD)
|
|
{
|
|
m_painter.setPen(m_trackReservedPen);
|
|
drawStraight(r, rotate - TileRotate::Deg45);
|
|
}
|
|
if(reservedPositionCD && positionCD)
|
|
{
|
|
m_painter.setPen(m_trackReservedPen);
|
|
drawCurve45(r, rotate + TileRotate::Deg180);
|
|
}
|
|
}
|
|
|
|
setTurnoutStatePen();
|
|
if(positionAB && (m_turnoutDrawState || reservedPositionAC || reservedPositionBD || (!reservedPositionAB && position != TurnoutPosition::Diverged)))
|
|
{
|
|
drawCurve45(turnoutStateRect(r), rotate);
|
|
}
|
|
if(positionAC && (m_turnoutDrawState || reservedPositionAB || reservedPositionCD || (!reservedPositionAC && position != TurnoutPosition::Crossed)))
|
|
{
|
|
drawStraight(turnoutStateRect(r), rotate);
|
|
}
|
|
if(positionBD && (m_turnoutDrawState || reservedPositionAB || reservedPositionCD || (!reservedPositionBD && position != TurnoutPosition::Crossed)))
|
|
{
|
|
drawStraight(turnoutStateRect(r), rotate - TileRotate::Deg45);
|
|
}
|
|
if(positionCD && (m_turnoutDrawState || reservedPositionAC || reservedPositionBD || (!reservedPositionCD && position != TurnoutPosition::Diverged)))
|
|
{
|
|
drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg180);
|
|
}
|
|
}
|
|
|
|
void TilePainter::drawSignal3Aspect(QRectF r, TileRotate rotate, SignalAspect aspect)
|
|
{
|
|
m_painter.save();
|
|
m_painter.translate(r.center());
|
|
m_painter.rotate(toDeg(rotate));
|
|
r.moveCenter(QPointF{0, 0});
|
|
|
|
const qreal x1 = r.left() + r.width() * 0.3;
|
|
const qreal x2 = r.right() - r.width() * 0.3;
|
|
const qreal y1 = r.top() + r.height() * 0.3;
|
|
const qreal y2 = r.bottom() - r.height() * 0.3;
|
|
const qreal w = x2 - x1;
|
|
const qreal lampRadius = w / 4;
|
|
|
|
QPainterPath path;
|
|
path.moveTo(x1, y1);
|
|
path.lineTo(x1, y2);
|
|
path.arcTo(QRectF{x1, y2 - (w / 2), w, w}, 180, 180);
|
|
path.lineTo(x2, y1);
|
|
path.arcTo(QRectF{x1, y1 - (w / 2), w, w}, 0, 180);
|
|
m_painter.setPen(QPen{Qt::white, lampRadius / 2});
|
|
m_painter.fillPath(path, Qt::black);
|
|
m_painter.drawPath(path);
|
|
|
|
m_painter.setPen(Qt::NoPen);
|
|
switch(aspect)
|
|
{
|
|
case SignalAspect::Stop:
|
|
m_painter.setBrush(signalRed);
|
|
m_painter.drawEllipse(QPointF{r.center().x(), r.center().y() - 2 * lampRadius}, lampRadius, lampRadius);
|
|
break;
|
|
|
|
case SignalAspect::ProceedReducedSpeed:
|
|
m_painter.setBrush(signalYellow);
|
|
m_painter.drawEllipse(r.center(), lampRadius, lampRadius);
|
|
break;
|
|
|
|
case SignalAspect::Proceed:
|
|
m_painter.setBrush(signalGreen);
|
|
m_painter.drawEllipse(QPointF{r.center().x(), r.center().y() + 2 * lampRadius}, lampRadius, lampRadius);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_painter.restore();
|
|
}
|
|
|
|
void TilePainter::drawSignalDirection(QRectF r, TileRotate rotate)
|
|
{
|
|
m_painter.save();
|
|
m_painter.translate(r.center());
|
|
m_painter.rotate(toDeg(rotate));
|
|
r.moveCenter(QPointF{0, 0});
|
|
|
|
QPainterPath path;
|
|
qreal x;
|
|
qreal y;
|
|
if(isDiagonal(rotate))
|
|
{
|
|
x = r.width() * 0.55;
|
|
y = r.height() * 0.075;
|
|
}
|
|
else
|
|
{
|
|
x = r.width() * 0.4;
|
|
y = r.height() * 0.4;
|
|
}
|
|
path.moveTo(x, y);
|
|
path.lineTo(x - r.width() * 0.15, y);
|
|
path.lineTo(x - r.width() * 0.075, y - r.height() * 0.15);
|
|
m_painter.fillPath(path, m_colorScheme.foreground);
|
|
|
|
m_painter.restore();
|
|
}
|
|
|
|
void TilePainter::drawRailBlock(const QRectF& r, TileRotate rotate, bool isReservedA, bool isReservedB, const ObjectPtr& blockTile)
|
|
{
|
|
const BlockState state = blockTile ? blockTile->getPropertyValueEnum<BlockState>("state", BlockState::Unknown) : BlockState::Unknown;
|
|
std::vector<SensorState> subStates;
|
|
QString label;
|
|
|
|
if(blockTile)
|
|
{
|
|
if(m_showBlockSensorStates)
|
|
{
|
|
if(const auto* p = blockTile->getVectorProperty("sensor_states"))
|
|
{
|
|
const int size = p->size();
|
|
subStates.resize(static_cast<size_t>(size));
|
|
for(int i = 0; i < size; i++)
|
|
subStates[i] = p->getEnum<SensorState>(i);
|
|
}
|
|
}
|
|
|
|
if(auto* block = dynamic_cast<BlockRailTile*>(blockTile.get()))
|
|
{
|
|
if(block->trains().size() == 1)
|
|
{
|
|
if(auto* trainBlockStatus = dynamic_cast<TrainBlockStatus*>(block->trains()[0].get())) /*[[likely]]*/
|
|
{
|
|
if(const auto& train = trainBlockStatus->train())
|
|
{
|
|
if(trainBlockStatus->direction() == BlockTrainDirection::TowardsA)
|
|
label += "< ";
|
|
|
|
label += train->getPropertyValueString("name");
|
|
|
|
if(trainBlockStatus->direction() == BlockTrainDirection::TowardsB)
|
|
label += " >";
|
|
}
|
|
else if(auto identification = trainBlockStatus->identification(); !identification.isEmpty())
|
|
{
|
|
if(trainBlockStatus->direction() == BlockTrainDirection::TowardsA)
|
|
label += "< ";
|
|
|
|
label += identification;
|
|
|
|
if(trainBlockStatus->direction() == BlockTrainDirection::TowardsB)
|
|
label += " >";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(label.isEmpty())
|
|
label = blockTile->getPropertyValueString("name");
|
|
}
|
|
|
|
if(rotate == TileRotate::Deg0)
|
|
{
|
|
setTrackPen(isReservedA);
|
|
m_painter.drawLine(topCenter(r), r.center());
|
|
setTrackPen(isReservedB);
|
|
m_painter.drawLine(r.center(), bottomCenter(r));
|
|
|
|
setBlockStateBrush(state);
|
|
m_painter.setPen(m_blockPen);
|
|
const qreal m = 0.5 + qFloor(r.width() / 10);
|
|
QRectF block = r.adjusted(m, m, -m, -m);
|
|
m_painter.drawRect(block);
|
|
|
|
if(!subStates.empty())
|
|
{
|
|
const qreal height = block.height() / subStates.size();
|
|
const qreal width = qRound(block.width() / 5);
|
|
qreal top = block.top();
|
|
for(SensorState subState : subStates)
|
|
{
|
|
m_painter.setBrush(sensorStateToColor(subState));
|
|
m_painter.drawRect(QRectF(block.left(), qRound(top) - 0.5, width, qRound(top + height) - qRound(top)));
|
|
top += height;
|
|
}
|
|
block.setLeft(block.left() + width);
|
|
}
|
|
|
|
if(!label.isEmpty())
|
|
{
|
|
m_painter.save();
|
|
m_painter.translate(r.center());
|
|
m_painter.rotate(90);
|
|
m_painter.setPen(m_colorScheme.blockText);
|
|
m_painter.drawText(QRectF{-block.height() / 2, -block.width() / 2, block.height(), block.width()}, label, QTextOption(Qt::AlignCenter));
|
|
m_painter.restore();
|
|
}
|
|
}
|
|
else if(rotate == TileRotate::Deg90)
|
|
{
|
|
setTrackPen(isReservedA);
|
|
m_painter.drawLine(centerLeft(r), r.center());
|
|
setTrackPen(isReservedB);
|
|
m_painter.drawLine(r.center(), centerRight(r));
|
|
|
|
setBlockStateBrush(state);
|
|
m_painter.setPen(m_blockPen);
|
|
const qreal m = 0.5 + qFloor(r.height() / 10);
|
|
QRectF block = r.adjusted(m, m, -m, -m);
|
|
m_painter.drawRect(block);
|
|
|
|
if(!subStates.empty())
|
|
{
|
|
const qreal width = block.width() / subStates.size();
|
|
const qreal height = qRound(block.height() / 5);
|
|
const qreal top = block.bottom() - height;
|
|
double left = block.left();
|
|
for(const auto& subState : subStates)
|
|
{
|
|
m_painter.setBrush(sensorStateToColor(subState));
|
|
m_painter.drawRect(QRectF(qRound(left) - 0.5, top, qRound(left + width) - qRound(left), height));
|
|
left += width;
|
|
}
|
|
block.setHeight(block.height() - height);
|
|
}
|
|
|
|
if(!label.isEmpty())
|
|
{
|
|
m_painter.setPen(m_colorScheme.blockText);
|
|
m_painter.drawText(block, label, QTextOption(Qt::AlignCenter));
|
|
}
|
|
}
|
|
else
|
|
assert(false);
|
|
}
|
|
|
|
void TilePainter::drawRailDecoupler(const QRectF& r, TileRotate rotate, bool isReserved, DecouplerState state)
|
|
{
|
|
setTrackPen(isReserved);
|
|
drawStraight(r, rotate);
|
|
|
|
m_painter.save();
|
|
m_painter.translate(r.center());
|
|
m_painter.rotate(toDeg(rotate));
|
|
m_painter.setPen(QPen(m_colorScheme.track, r.width() / 12, Qt::SolidLine, Qt::RoundCap));
|
|
const qreal h = r.height() * 0.6;
|
|
const qreal w = r.width() * 0.3;
|
|
m_painter.setBrush(state == DecouplerState::Activated ? m_colorScheme.decouplerActivated : m_colorScheme.decouplerDeactivated);
|
|
m_painter.drawRect(QRectF(-w / 2, -h / 2, w, h));
|
|
m_painter.restore();
|
|
}
|
|
|
|
void TilePainter::drawRailNX(const QRectF& r, TileRotate rotate, bool isReserved, bool isEnabled, bool pressed)
|
|
{
|
|
setTrackPen(isReserved);
|
|
drawStraight(r, rotate);
|
|
drawPushButton(r, pressed ? Color::White : (isEnabled ? Color::Blue : Color::Gray));
|
|
}
|
|
|
|
void TilePainter::drawLabel(const QRectF& r, TileRotate rotate, const QString& text, TextAlign textAlign, Color textColor, Color backgroundColor)
|
|
{
|
|
m_painter.save();
|
|
|
|
m_painter.setPen(Qt::NoPen);
|
|
m_painter.setBrush(backgroundColor == Color::None ? m_colorScheme.background : toQColor(backgroundColor));
|
|
m_painter.drawRect(r.adjusted(1, 1, -1, -1));
|
|
|
|
if(!text.isEmpty())
|
|
{
|
|
m_painter.translate(r.center());
|
|
m_painter.rotate(toDeg(rotate));
|
|
m_painter.setPen(textColor == Color::None ? m_colorScheme.foreground : toQColor(textColor));
|
|
QRectF textRect{r};
|
|
if(rotate == TileRotate::Deg90 || rotate == TileRotate::Deg270)
|
|
{
|
|
textRect = textRect.transposed();
|
|
}
|
|
textRect.moveCenter({0., 0.});
|
|
m_painter.drawText(textRect, text, QTextOption(toAlignment(textAlign)));
|
|
}
|
|
|
|
m_painter.restore();
|
|
}
|