Files
qt5-legatus-legacy/Lts/Viewer/SituationMap/SymbologyXML/primitive.cpp
T
2026-08-13 18:33:10 +02:00

637 lines
17 KiB
C++
Executable File

/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#include "primitive.h"
#include <cmath>
#include <cstdlib>
#include "symbol_reader.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace SymbologyXML
{
unsigned int Primitive::_idCount = 0;
Primitive::Primitive(t_primitive type)
: logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("AdvSymbologyXML.Primitive"))
, _id(++_idCount)
, _type(type)
{
TRACE_LOG(logger) << LOGFUNCTION ;
TRACE_LOG(logger) << "END " << LOGFUNCTION ;
} // Primitive::Primitive(t_primitive type)
Primitive::Primitive(const Primitive& other)
: logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("AdvSymbologyXML.Primitive"))
{
TRACE_LOG(logger) << LOGFUNCTION ;
++_idCount;
_id = _idCount; // _id is NOT copied ( see operator= )
*this = other; // _id is NOT copied ( see operator= )
TRACE_LOG(logger) << "END " << LOGFUNCTION ;
} // Primitive::Primitive(const Primitive& other)
Primitive& Primitive::operator=(const Primitive& other)
{
DEBUG_LOG(logger) << LOGFUNCTION ;
//// _id = other._id; Do NOT have identical _id
_type = other._type;
_valid = other._valid;
_points = other._points;
_fill = other._fill;
_lineWidth = other._lineWidth;
_lineStyle = other._lineStyle;
_closed = other._closed;
_startAngle = other._startAngle;
_extent = other._extent;
_rotation = other._rotation;
_transform = other._transform;
_color = other._color;
_iconId = other._iconId;
_iconType = other._iconType;
_text = other._text;
_position = other._position;
_rectpos = other._rectpos;
_scalemode = other._scalemode;
_font = other._font;
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
return *this;
} // Primitive& Primitive::operator=(const Primitive& other)
Primitive::~Primitive() {
DEBUG_LOG(logger) << LOGFUNCTION ;
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // Primitive::~Primitive()
bool Primitive::operator==(const Primitive& other) const {
DEBUG_LOG(logger) << LOGFUNCTION ;
if ( _id == other._id ) {
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
return true;
}
bool result = true;
result &= _type == other._type;
result &= _valid == other._valid;
result &= _points == other._points;
result &= _fill == other._fill;
result &= _lineWidth == other._lineWidth;
result &= _lineStyle == other._lineStyle;
result &= _closed == other._closed;
result &= _startAngle == other._startAngle;
result &= _extent == other._extent;
result &= _rotation == other._rotation;
result &= _transform == other._transform;
result &= _color == other._color;
result &= _iconId == other._iconId;
result &= _iconType == other._iconType;
result &= _position == other._position;
result &= _rectpos == other._rectpos;
result &= _scalemode == other._scalemode;
result &= _text == other._text;
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
return result;
} // bool Primitive::operator==(const Primitive& other)
bool Primitive::operator!=(const Primitive& other) const {
DEBUG_LOG(logger) << LOGFUNCTION ;
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
return !(*this == other);
} // bool Primitive::operator!=(const Primitive& other)
void Primitive::setFill(bool flag) {
DEBUG_LOG(logger) << LOGFUNCTION ;
_fill = flag & (_type != LINE);
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::setFill(bool flag)
void Primitive::setClosed(bool flag) {
DEBUG_LOG(logger) << LOGFUNCTION ;
_closed = flag;
if ( (_type == ELLIPSE) && _closed ) {
_extent = 360.0;
}
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::setClosed(bool flag)
bool Primitive::setPoints(const QPolygonF &pointList) {
DEBUG_LOG(logger) << LOGFUNCTION ;
bool ok = true;
switch (_type) {
case ELLIPSE:
case LINE:
case RECT:
case ICON:
case TEXT:
if ( pointList.size() != 2 ) {
ok = false;
}
break;
case POLYGON:
if ( pointList.size() < 3 ) {
ok = false;
}
break;
default: _valid = false;
}
_points.clear();
_points = pointList;
_valid = ok;
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
return _valid;
} // bool Primitive::setPoints(QList<Point> pointList)
void Primitive::setLineWidth(double width) {
DEBUG_LOG(logger) << LOGFUNCTION ;
_lineWidth = width;
_valid = (width > 0.0);
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::setLineWidth(double width)
void Primitive::setLineStyle(t_linestyle style) {
DEBUG_LOG(logger) << LOGFUNCTION ;
_lineStyle = style;
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::setLineStyle(t_linestyle style)
void Primitive::setStartAngle(double start) {
DEBUG_LOG(logger) << LOGFUNCTION ;
if (_type == ELLIPSE) {
_startAngle = fmod(start, 360.0);
}
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::setStartAngle(double start)
void Primitive::setExtent(double extent) {
DEBUG_LOG(logger) << LOGFUNCTION ;
if (_type == ELLIPSE) {
if ( extent < -360.0 ) {
_extent = -360.0;
} else if ( extent > 360.0 ) {
_extent = 360.0;
} else {
_extent = extent;
}
}
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::setExtent(double extent)
bool Primitive::getBoundingBox(double &x0, double &y0, double &x1, double &y1, bool ignoreEllipseType) const
{
DEBUG_LOG(logger) << LOGFUNCTION ;
if ( !isValid() ) {
WARNING_LOG(logger) << "Primitive::getBoundingBox - primitive not valid";
return false;
}
const QRectF br = _points.boundingRect();
// Check for open Ellipse
if ( (_type == ELLIPSE) && !_closed && !ignoreEllipseType) {
return getBoundingBoxEllipse(x0, y0,
x1, y1,
br.x(), br.y(),
br.x() + br.width(), br.y() + br.height());
}
x0 = br.x();
y0 = br.y();
x1 = br.x() + br.width();
y1 = br.y() + br.height();
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
return true;
} // void Primitive::getBoundingBox(double* x0, ...) const
void Primitive::getCenter(double &x, double &y) const {
DEBUG_LOG(logger) << LOGFUNCTION ;
double x0, y0, x1, y1;
if (getBoundingBox(x0, y0, x1, y1, true))
{
x = (x0+x1)/2.0;
y = (y0+y1)/2.0;
}
else
{
x = y = 0;
}
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::getCenter(double* x, double* y) const
void Primitive::move(const QPointF &diff) {
_points.translate(diff);
}
void Primitive::scale(double factorX, double factorY) {
DEBUG_LOG(logger) << LOGFUNCTION ;
if ( (factorX <= 0.0) || (factorY <= 0.0) ) {
// Factor too small, ignore
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
return;
}
// Get bounding box center
double x0, x1, y0, y1;
if (!getBoundingBox(x0, y0, x1, y1))
return;
double cx = (x0+x1)/2.0;
double cy = (y0+y1)/2.0;
// Transform all points to origin and multiply with factor
for (int i = 0; i < _points.size(); ++i) {
double tmpx = (_points.at(i)).x() - cx;
double tmpy = (_points.at(i)).y() - cy;
tmpx = (tmpx*factorX)+cx;
tmpy = (tmpy*factorY)+cy;
_points[i].setX(tmpx);
_points[i].setY(tmpy);
}
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::scale(double factorX, double factorY)
void Primitive::setText(const QString &text) {
_text = text;
}
QString Primitive::text() const {
return _text;
}
void Primitive::setIcon(int id) {
_iconId = id;
}
int Primitive::icon() const {
return _iconId;
}
void Primitive::setIconType(SymbolTypes::t_icon_type iconType) {
_iconType = iconType;
}
SymbolTypes::t_icon_type Primitive::iconType() const {
return _iconType;
}
void Primitive::setPosition(SymbolTypes::t_sector_position position) {
_position = position;
}
SymbolTypes::t_sector_position Primitive::position() const {
return _position;
}
void Primitive::setRectpos(SymbolTypes::t_sector_rectpos rectpos) {
_rectpos = rectpos;
}
SymbolTypes::t_sector_rectpos Primitive::rectpos() const {
return _rectpos;
}
void Primitive::setScalemode(Primitive::t_scalemode scalemode)
{
_scalemode = scalemode;
}
Primitive::t_scalemode Primitive::scalemode() const
{
return _scalemode;
}
QString Primitive::scalemode(Primitive::t_scalemode scalemode)
{
QString ret;
switch(scalemode) {
case Scalemode_Undefined: ret = QStringLiteral("undefined"); break;
case Scalemode_Proport: ret = QStringLiteral("proport"); break;
case Scalemode_Unproport: ret = QStringLiteral("unproport"); break;
default: ret = QStringLiteral("undefined"); break;
}
return ret;
}
Primitive::t_scalemode Primitive::scalemode(const QString &scalemode)
{
Primitive::t_scalemode ret = Scalemode_Undefined;
if(scalemode == QStringLiteral("proport")) {
ret = Scalemode_Proport;
} else if(scalemode == QStringLiteral("unproport")) {
ret = Scalemode_Unproport;
}
return ret;
}
void Primitive::setRotation(double angle) {
DEBUG_LOG(logger) << LOGFUNCTION ;
_rotation = fmod(angle, 360.0);
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::setRotation(double angle)
bool Primitive::getBoundingBoxEllipse(double &x0, double &y0, double &x1, double &y1,
double tx0, double ty0, double tx1, double ty1) const
{
DEBUG_LOG(logger) << LOGFUNCTION ;
if ( !isValid() || (_type != ELLIPSE) ) {
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
return false;
}
double extent = closed()?360.0:_extent;
QList<double> pointsX;
QList<double> pointsY;
double end = _startAngle+extent;
// Add start/end-point
double centerX = (tx0+tx1)/2.0;
double centerY = (ty0+ty1)/2.0;
double startX = cos(-_startAngle*2*M_PI/360.0);
startX = centerX+(startX*(tx1-tx0)/2.0);
double startY = sin(_startAngle*2*M_PI/360.0);
startY = centerY+(startY*(ty1-ty0)/2.0);
double endX = cos(-end*2*M_PI/360.0);
endX = centerX+(endX*(tx1-tx0)/2.0);
double endY = sin(end*2*M_PI/360.0);
endY = centerY+(endY*(ty1-ty0)/2.0);
pointsX.append(startX);
pointsX.append(endX);
pointsY.append(startY);
pointsY.append(endY);
// Check 0°, 90°, 180°, 270°
if ( extent >= 0.0 ) {
// Counter-clockwise
if ( ((_startAngle <= 0.0) && (end >= 0.0)) ||
((_startAngle <= -360.0) && (end >= -360.0)) ||
((_startAngle <= 360.0) && (end >= 360.0)) ||
((_startAngle <= -720.0) && (end >= -720.0)) ) {
// 0°/-360°/360° is in
pointsX.append(tx1);
}
// Counter-clockwise
if ( ((_startAngle <= 90.0) && (end >= 90.0)) ||
((_startAngle <= -270.0) && (end >= -270.0)) ||
((_startAngle <= 450.0) && (end >= 450.0)) ||
((_startAngle <= -630.0) && (end >= -630.0)) ) {
// 90°/-270°/450° is in
pointsY.append(ty1);
}
if ( ((_startAngle <= 180.0) && (end >= 180.0)) ||
((_startAngle <= -180.0) && (end >= -180.0)) ||
((_startAngle <= 540.0) && (end >= 540.0)) ||
((_startAngle <= -540.0) && (end >= -540.0)) ) {
// 180°/-180°/540° is in
pointsX.append(tx0);
}
if ( ((_startAngle <= 270.0) && (end >= 270.0)) ||
((_startAngle <= -90.0) && (end >= -90.0)) ||
((_startAngle <= 630.0) && (end >= 630.0)) ||
((_startAngle <= -450.0) && (end >= -450.0)) ) {
// 270°/-90°/630° is in
pointsY.append(ty0);
}
} else {
// Clockwise
if ( ((_startAngle >= 0.0) && (end <= 0.0)) ||
((_startAngle >= -360.0) && (end <= -360.0)) ||
((_startAngle >= 360.0) && (end <= 360.0)) ||
((_startAngle >= -720.0) && (end <= -720.0)) ) {
// 0°/-360°/360° is in
pointsX.append(tx1);
}
if ( ((_startAngle >= 90.0) && (end <= 90.0)) ||
((_startAngle >= -270.0) && (end <= -270.0)) ||
((_startAngle >= 450.0) && (end <= 450.0)) ||
((_startAngle >= -630.0) && (end <= -630.0)) ) {
// 90°/-270°/450° is in
pointsY.append(ty1);
}
if ( ((_startAngle >= 180.0) && (end <= 180.0)) ||
((_startAngle >= -180.0) && (end <= -180.0)) ||
((_startAngle >= 540.0) && (end <= 540.0)) ||
((_startAngle >= -540.0) && (end <= -540.0)) ) {
// 180°/-180°/540° is in
pointsX.append(tx0);
}
if ( ((_startAngle >= 270.0) && (end <= 270.0)) ||
((_startAngle >= -90.0) && (end <= -90.0)) ||
((_startAngle >= 630.0) && (end <= 630.0)) ||
((_startAngle >= -450.0) && (end <= -450.0)) ) {
// 270°/-90°/630° is in
pointsY.append(ty0);
}
}
double lx0 = pointsX.at(0);
double ly0 = pointsY.at(0);
double lx1 = lx0;
double ly1 = ly0;
// Find min/max for X/Y
for (int i = 1; i < pointsX.size(); ++i) {
if ( pointsX.at(i) < lx0 ) {
lx0 = pointsX.at(i);
} else if ( pointsX.at(i) > lx1 ) {
lx1 = pointsX.at(i);
}
}
for (int i = 1; i < pointsY.size(); ++i) {
if ( pointsY.at(i) < ly0 ) {
ly0 = pointsY.at(i);
} else if ( pointsY.at(i) > ly1 ) {
ly1 = pointsY.at(i);
}
}
x0 = lx0;
y0 = ly0;
x1 = lx1;
y1 = ly1;
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
return true;
} // void Primitive::getBoundingBoxEllipse(double* x0, ...) const
void Primitive::rotate() {
DEBUG_LOG(logger) << LOGFUNCTION ;
// Ellipse needs special handling and temporary hiding of its type
bool isEllipseType = (_type == ELLIPSE);
// Get center of bounding box
double x0, y0, x1, y1, cx, cy;
if ( isEllipseType ) { _type = LINE; }
if (!getBoundingBox(x0, y0, x1, y1))
return;
if ( isEllipseType ) { _type = ELLIPSE; }
cx = (x0+x1)/2.0;
cy = (y0+y1)/2.0;
if ( (_type == RECT) && ( fmod(_rotation, 90.0) != 0.0 ) ) {
// Convert Rectangle to Polygon, because we need to rotate all four points
// This does not change the bbox, so its okay, to do this here
rectToPolygon();
} else if ( _type == ELLIPSE ) {
if ( fmod(_rotation, 90.0) == 0.0 ) {
// Rotate ELLIPSE in multiple of 90°: Set the _startAngle to rotated
// value and then rotate the defining points.
// This does not change the bbox, so its okay, to do this here
_startAngle = _startAngle+(fmod(_rotation, 360.0));
} else {
const double temp = std::abs(_extent);
ellipseToPolygon(lrint(temp/2));
}
}
// Rotate all points
for (int i = 0; i < _points.size(); ++i) {
double px = _points.at(i).x();
double py = _points.at(i).y();
rotatePoint(px, py, cx, cy, &px, &py, _rotation);
_points[i] = QPointF(px, py);
}
// Reset the rotation-property
_rotation = 0.0;
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::rotate()
void Primitive::ellipseToPolygon(unsigned int approxSteps) {
DEBUG_LOG(logger) << LOGFUNCTION ;
if ( _type != ELLIPSE ) {
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
return;
}
// Get the full Bounding Box
_type = POLYGON;
double x0, y0, x1, y1;
if (!getBoundingBox(x0, y0, x1,y1))
return;
// Get center for translation of points
double cx = (x0+x1)/2.0;
double cy = (y0+y1)/2.0;
// Get Bounding Box size for scaling
double sx = (x1-x0)/2.0;
double sy = (y1-y0)/2.0;
_points.clear();
unsigned int steps = (approxSteps>10)?approxSteps:10;
if (steps > 20)
steps = 20;
double stepSize = _extent/(double)steps;
for (unsigned int i = 0; i <= steps; ++i) {
double stepAngle = _startAngle+(double)i*stepSize;
double px, py;
rotatePoint(1.0, 0.0, 0.0, 0.0, &px, &py, stepAngle);
_points.append(QPointF(px*sx+cx, py*sy+cy));
}
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::ellipseToPolygon(unsigned int approxSteps)
void Primitive::rectToPolygon() {
DEBUG_LOG(logger) << LOGFUNCTION ;
if ( _type != RECT ) {
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
return;
}
QPolygonF points;
points.append(_points.at(0));
points.append(QPointF(_points.at(0).x(), _points.at(1).y()));
points.append(_points.at(1));
points.append(QPointF(_points.at(1).x(), _points.at(0).y()));
_points = points;
_type = POLYGON;
_closed = true;
DEBUG_LOG(logger) << "END " << LOGFUNCTION ;
} // void Primitive::rectToPolygon()
void Primitive::rotatePoint(double px, double py,
double cx, double cy,
double *rx, double *ry,
double angle) {
// This is a static method, so no logging is used here
// angle to rad
double radAngle = ((2*M_PI)/360.0)*angle;
// Translate
double tx = px-cx;
double ty = py-cy;
// Rotate
double nx = tx*cos(radAngle)-ty*sin(radAngle);
double ny = tx*sin(radAngle)+ty*cos(radAngle);
// Translate back and write result
*rx = nx+cx;
*ry = ny+cy;
} // void Primitive::rotatePoint(double px...)
}; // namespace SymbologyXML