Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
/**
* @RDE_HEADER@
* Some helper classes / functions for AES
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_helper.h"
#include <QHash>
#include <QSet>
namespace AreaEffectSimulation
{
//
// class HandledImpactsStorage::Private
//
class HandledImpactsStorage::Private
{
public:
Private() = default;
~Private() = default;
// data:
QHash<int, QSet<int>> m_aesItemId2Index; // Key = aesItemId ; processed/calculated index
};
//
// class HandledImpactsStorage
//
HandledImpactsStorage::HandledImpactsStorage()
: d(new Private)
{}
HandledImpactsStorage::~HandledImpactsStorage()
{
delete d;
}
void HandledImpactsStorage::setIndexProcessed(int aesItemId, int index)
{
d->m_aesItemId2Index[aesItemId].insert(index);
}
bool HandledImpactsStorage::isIndexProcessed(int aesItemId, int index)
{
return d->m_aesItemId2Index[aesItemId].contains(index);
}
void HandledImpactsStorage::removeAesItem(int aesItemId)
{
d->m_aesItemId2Index.remove(aesItemId);
}
//------------------------------------------------------------------------------
} // namespace AreaEffectSimulation
+150
View File
@@ -0,0 +1,150 @@
/**
* @RDE_HEADER@
* Some helper classes / functions for AES
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AreaEffectSimulation/aes_export.h>
#include <math.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <QPointF>
#include <QString>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <LtsUtilsCore/dds_bitarray.h>
#include <LtsUtilsCore/qpoint3d.h>
#include "aes_db.h"
namespace AreaEffectSimulation
{
inline bool chargeTypeIdIsVulcano(int chargeTypeId) { return /*(chargeTypeId == 376) ||*/ (chargeTypeId == 393) ; } // 376=Artillery ; 393=Rocket
inline Core::GeoPosition toCoreGeoPosition(const LtsUtils::QPoint3D &point)
{
return Core::GeoPosition(point.x(), point.y(), point.z());
}
//------------------------------------------------------------------------------
// Helper namespace TrajectoryRelative (ADLER / SAA_Trajectory)
//------------------------------------------------------------------------------
namespace TrajectoryRelative
{
// keyword used for encoding/decoding, or in other words: for toString/fromString
static const QString TRAJECTORY_RELATIVE_KEYWORD = QStringLiteral("trajectoryRelative");
inline std::string toString(const std::vector<Core::RelativePositionRelativeTime> &trajectoryRelative)
{
if (trajectoryRelative.empty())
return {};
QJsonObject obj;
QJsonArray arr;
for (const auto &coordinate : trajectoryRelative)
{
QJsonObject coordObj;
coordObj[QStringLiteral("offsetX")] = coordinate.offsetX();
coordObj[QStringLiteral("offsetY")] = coordinate.offsetY();
coordObj[QStringLiteral("offsetZ")] = coordinate.offsetZ();
coordObj[QStringLiteral("relativeTime")] = coordinate.relativeTime();
arr.append(coordObj);
}
obj[TRAJECTORY_RELATIVE_KEYWORD] = arr;
return QJsonDocument(obj).toJson(QJsonDocument::JsonFormat::Compact).toStdString();
}
inline std::vector<Core::RelativePositionRelativeTime> fromString(const QString &trajectoryRelativeJson)
{
if (trajectoryRelativeJson.isEmpty())
return {};
QJsonObject obj = QJsonDocument::fromJson(trajectoryRelativeJson.toUtf8()).object();
if (!obj.contains(TRAJECTORY_RELATIVE_KEYWORD) || !obj[TRAJECTORY_RELATIVE_KEYWORD].isArray() )
return {};
std::vector<Core::RelativePositionRelativeTime> ret;
QJsonArray arr = obj[TRAJECTORY_RELATIVE_KEYWORD].toArray();
for (const auto arrRef: arr)
{
QJsonObject coordinate = arrRef.toObject();
if (!coordinate.contains(QStringLiteral("offsetX")) ||
!coordinate.contains(QStringLiteral("offsetY")) ||
!coordinate.contains(QStringLiteral("offsetZ")) ||
!coordinate.contains(QStringLiteral("relativeTime"))
)
return {};
Core::RelativePositionRelativeTime coord;
coord.offsetX() = coordinate[QStringLiteral("offsetX")].toDouble();
coord.offsetY() = coordinate[QStringLiteral("offsetY")].toDouble();
coord.offsetZ() = coordinate[QStringLiteral("offsetZ")].toDouble();
coord.relativeTime() = coordinate[QStringLiteral("relativeTime")].toInt();
ret.push_back(coord);
}
return ret;
}
} // namespace TrajectoryRelative
inline double getAngleDegreeNorth(const QPointF &p1, const QPointF &p2)
{
double x = p2.x() - p1.x();
double y = p2.y() - p1.y();
// Winkel der Geraden bestimmen
double alphaRad = atan2(y, x);
// // Fuer Winkel > PI liefert atan2 math. rechts drehende (also negative) Werte.
// // Um Werte von 0-2PI zu bekommen muss deshalb hier von 2Pi abgezogen werden.
alphaRad -= M_PI / 2.0;
alphaRad *= -1.0;
if (alphaRad < 0.0)
alphaRad = 2 * M_PI + alphaRad;
return (alphaRad * 180.0 / M_PI);
}
inline bool isPointSimilar(AreaEffectSimulation::AesExplosionGeometry geometry)
{
return (geometry.underlying() == AreaEffectSimulation::AesExplosionGeometry_def::EgAimPoint) ||
(geometry.underlying() == AreaEffectSimulation::AesExplosionGeometry_def::EgPoint);
}
//------------------------------------------------------------------------------
// helper class HandledImpactsStorage
//------------------------------------------------------------------------------
class AES_COMMON_EXPORT HandledImpactsStorage final
{
public:
Q_DISABLE_COPY(HandledImpactsStorage)
HandledImpactsStorage();
~HandledImpactsStorage();
void setIndexProcessed(int aesItemId, int index);
bool isIndexProcessed(int aesItemId, int index);
void removeAesItem(int aesItemId);
private:
class Private;
/**
* The private d->pointer to not break BC when changing internals of this class
*/
Private *d = nullptr;
};
} // namespace AreaEffectSimulation
+159
View File
@@ -0,0 +1,159 @@
/**
* @RDE_HEADER@
* Wandelt DDS-Strukturen in Database-Strukturen
* $Id$
* $HeadURL$
* $Author$
*/
#include "ddsdbconvert.h"
#include <aes.hpp>
namespace AreaEffectSimulation
{
namespace DdsDbConvert
{
Database::AesNContamination nContamination(const AreaEffectSimulation::NContamination &src)
{
Database::AesNContamination dst;
dst.id = src.id();
dst.chargeTypeId = src.chargeStruct().chargeTypeId();
dst.effectStateId = src.effectStateId();
dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0;
dst.participantId = src.participantId().is_set() ? src.participantId().get() : 0;
dst.effectDuration = src.effectDuration();
dst.effectStart = src.effectStart().is_set() ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime();
dst.name = QString::fromStdString(src.name());
dst.impactPosition.rx() = src.impactPosition().lon();
dst.impactPosition.ry() = src.impactPosition().lat();
dst.borderLengthInner = src.borderLengthInner();
dst.borderLengthOuter = src.borderLengthOuter();
dst.impactRadius = src.impactRadius();
dst.spanAngle = src.spanAngle();
dst.rotationAngle = src.rotationAngle();
dst.detonationValue = src.detonationValue();
// dst.priv_entityId;
// dst.privNContaminationId;
return dst;
}
Database::AesBCContamination bcContamination(const BCContamination &src)
{
Database::AesBCContamination dst;
dst.id = src.id();
dst.effectStateId = src.effectStateId();
dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0;
dst.participantId = src.participantId().is_set() ? src.participantId().get() : 0;
dst.effectDuration = src.effectDuration();
dst.effectStart = src.effectStart().is_set() ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime();
dst.name = QString::fromStdString(src.name());
dst.impactPosition.rx() = src.impactPosition().lon();
dst.impactPosition.ry() = src.impactPosition().lat();
dst.impactRadius = src.impactRadius();
dst.chargeTypeId = src.chargeStruct().chargeTypeId();
dst.windSpeed = src.windSpeed();
dst.weatherTimestamp = QDateTime::fromMSecsSinceEpoch(src.weatherTimestamp());
dst.propagationDirection = src.propagationDirection();
dst.weatherPosition = dst.impactPosition; // initial, is not used during update therefore it's ok here
dst.upperOffset = src.upperOffset();
dst.lowerOffset = src.lowerOffset();
// dst.expansionFactor;
// dst.isDynamicWeather;
// dst.priv_entityId;
// dst.privBCContaminationId;
return dst;
}
Database::AesExplosionArea explosionArea(const ExplosionArea &src)
{
Database::AesExplosionArea dst;
dst.id = src.id();
dst.effectStateId = src.effectStateId();
dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0;
dst.participantId = src.participantId().is_set() ? src.participantId().get() : 0;
dst.effectDuration = src.effectDuration();
dst.effectStart = src.effectStart().is_set() && src.effectStart().get() > 0 ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime();
dst.name = QString::fromStdString(src.name());
dst.impactPosition.rx() = src.area().cornerBottomLeft().lon();
dst.impactPosition.ry() = src.area().cornerBottomLeft().lat();
dst.impactLength = src.area().height();
dst.impactWidth = src.area().width();
dst.rotationAngle = src.area().rotation();
dst.chargeTypeId = src.chargeStruct().chargeTypeId();
dst.cannonCount = src.cannonCount();
dst.cannonRounds = src.cannonRounds();
dst.originalEffectStart = src.originalEffectStart() > 0 ? QDateTime::fromMSecsSinceEpoch(src.originalEffectStart()) : QDateTime();
dst.originalEffectDuration = src.originalEffectDuration();
dst.remainExplosionNumber = src.remainExplosionNumber();
// dst.firePurpose;
// dst.fireRequestTime;
// dst.priv_entityId;
// dst.priv_explosionId;
// dst.privExplosionAreaId;
return dst;
}
Database::AesExplosionSingle explosionSingle(const ExplosionSingle &src)
{
Database::AesExplosionSingle dst;
dst.id = src.id();
dst.effectStateId = src.effectStateId();
dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0;
dst.participantId = src.participantId().is_set() ? src.participantId().get() : 0;
dst.effectDuration = src.effectDuration();
dst.effectStart = src.effectStart().is_set() && src.effectStart().get() > 0 ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime();
dst.name = QString::fromStdString(src.name());
dst.impactPosition.rx() = src.impactPosition().lon();
dst.impactPosition.ry() = src.impactPosition().lat();
dst.chargeTypeId = src.chargeStruct().chargeTypeId();
// dst.priv_entityId;
// dst.priv_explosionId;
// dst.privExplosionSingleId;
return dst;
}
Database::AesMineArea mineArea(const MineArea &src)
{
Database::AesMineArea dst;
dst.id = src.id();
dst.name = QString::fromStdString(src.name());
dst.effectStart = src.effectStart().is_set() && src.effectStart().get() > 0 ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime();
dst.effectDuration = src.effectDuration();
dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0;
dst.chargeTypeId = src.chargeStruct().chargeTypeId();
dst.participantId = src.participantId().is_set() ? src.participantId().get() : 0;
dst.density = src.density();
dst.width = src.width();
dst.layingProcedureTypeId = src.layingProcedureType();
dst.isLaidHidden = src.isLaidHidden();
dst.effectStateId = src.effectStateId();
dst.isCenterLine = src.isCenterLine();
// qint32 priv_entityId;
// qint32 priv_mineId;
// qint32 privMineAreaId;
return dst;
}
QVector<Database::AesMineAreaSegment> mineAreaSegments(const MineArea &src)
{
const std::vector<AreaEffectSimulation::MineSegment> &segments = src.segments();
QVector<Database::AesMineAreaSegment> dbSegments;
dbSegments.reserve(segments.size());
for (size_t i = 0; i < segments.size(); ++i)
{
Database::AesMineAreaSegment newSegment;
newSegment.elementNumber = i;
newSegment.mineAreaId = src.id(); // Todo: korrekt?
newSegment.id = 0;
newSegment.startPosition = QPointF(segments[i].positionStart().lon(), segments[i].positionStart().lat());
newSegment.endPosition = QPointF(segments[i].positionEnd().lon(), segments[i].positionEnd().lat());
dbSegments << newSegment;
}
return dbSegments;
}
} // namespace DdsDbConvert
} // namespace AreaEffectSimulation
+31
View File
@@ -0,0 +1,31 @@
/**
* @RDE_HEADER@
* Wandelt DDS-Strukturen in Database-Strukturen
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AreaEffectSimulation/aes_db_structs.h>
namespace AreaEffectSimulation
{
class BCContamination;
class ExplosionArea;
class ExplosionSingle;
class MineArea;
class NContamination;
namespace DdsDbConvert
{
AES_COMMON_EXPORT Database::AesBCContamination bcContamination(const BCContamination &src);
AES_COMMON_EXPORT Database::AesExplosionArea explosionArea(const ExplosionArea &src);
AES_COMMON_EXPORT Database::AesExplosionSingle explosionSingle(const ExplosionSingle &src);
AES_COMMON_EXPORT Database::AesMineArea mineArea(const MineArea &src);
AES_COMMON_EXPORT QVector<Database::AesMineAreaSegment> mineAreaSegments(const MineArea &src);
AES_COMMON_EXPORT Database::AesNContamination nContamination(const NContamination &src);
} // namespace DdsDbConvert
} // namespace AreaEffectSimulation
+423
View File
@@ -0,0 +1,423 @@
/**
* AES fire automaton - scheduling fire patterns for multiple effectors
* $Id$
* $HeadURL$
* $Author$
*/
#include <QtGlobal>
#include <QTimer>
#include <QStringList>
#include <QVector>
#include <AreaEffectSimulation/logger.h>
#include <AreaEffectSimulation/settings.h>
#include "fire_automaton.h"
namespace AreaEffectSimulation
{
FireAutomaton::FireAutomaton(QObject *parent)
: QObject(parent)
, m_nextIdx(0)
, m_tick(0)
, m_durationMSecs(0)
, m_intervalMSecs(0)
, m_remainingTickMSecs(0)
, m_state(State::Uninitialized)
, m_logger(getLogger("Utils.FireAutomaton"))
{
m_timer.setSingleShot(true);
connect(&m_timer, &QTimer::timeout, this, &FireAutomaton::nextTick, Qt::UniqueConnection);
}
/**
* @brief Initializes the fire automaton and derives a fire sequence from given parameters
*
* @param time Time used for the fire sequence calculation in milliseconds
* @param timeType Type of the given time
* @param rounds Number of rounds to be fired
* @param effectorIds Ids of the effector that shall be used to fire rounds
* @param pattern Fire pattern to be used
* @return true when the automaton is succesfully initialized
*/
bool FireAutomaton::init(int32_t time, TimeType type, int32_t rounds, const QVector<int32_t>& effectorIds, const QString& pattern)
{
if(m_state != State::Stopped && m_state != State::Uninitialized)
{
ERROR_LOG(m_logger) << " init failed - fire automaton must be in state unitialized or stopped.";
return false;
}
if(time < 0)
{
ERROR_LOG(m_logger) << " init failed - time invalid.";
return false;
}
if(effectorIds.isEmpty())
{
ERROR_LOG(m_logger) << " init failed - effector Ids invalid.";
return false;
}
if(rounds <= 0)
{
ERROR_LOG(m_logger) << " init failed - number of rounds invalid.";
return false;
}
if(pattern.isEmpty())
{
ERROR_LOG(m_logger) << " init failed - invalid fire pattern.";
return false;
}
m_effectorIds = effectorIds;
m_tick = 0;
m_remainingTickMSecs = 0;
m_nextIdx = 0;
if(!readPattern(pattern))
{
ERROR_LOG(m_logger) << " init failed - firing pattern invalid.";
return false;
}
if(!calculateSchedule(rounds))
{
ERROR_LOG(m_logger) << " init failed - firing schedule invalid.";
return false;
}
const int32_t maxTick = m_schedule.size();
switch(type)
{
case TimeType::Cadence:
m_intervalMSecs = time;
m_durationMSecs = (maxTick - 1) * time;
break;
case TimeType::Duration:
m_durationMSecs = time;
if(maxTick == 1)
{
m_intervalMSecs = 0;
}
else
{
m_intervalMSecs = m_durationMSecs / (maxTick - 1);
// ensure atleast 1 ms spacing between ticks
if(m_intervalMSecs <= 0)
{
m_intervalMSecs = 1;
}
}
break;
}
setState(State::Stopped);
return true;
}
void FireAutomaton::setState(State state)
{
m_state = state;
}
void FireAutomaton::nextTick()
{
QVector<int32_t> fireIds = getEffectorIds(m_tick);
for(auto id : fireIds)
{
emit fire(id);
}
++m_tick;
if(m_tick >= m_schedule.size())
{
stop();
emit finished();
return;
}
if(m_state != State::Stepping)
{
m_timer.start(m_intervalMSecs);
}
}
/**
* @brief Starts the fire automaton's sequence derived from the fire pattern and parameters
*
* The sequence can only be started in state stopped and sets the state to running. After the sequence is finished, the state will be set to stopped.
*
* @return True when the automaton is succesfully resumed
*/
bool FireAutomaton::start()
{
if(m_state != State::Stopped)
{
ERROR_LOG(m_logger) << " start failed - fire automaton must be in state stopped.";
return false;
}
setState(State::Running);
m_timer.start(0);
return true;
}
/**
* @brief Resumes a previously stopped fire automaton
*
* In the state paused the fire automaton sequence execution will be resumed and the state set to running.
*
* @return True when the automaton is succesfully resumed
*/
bool FireAutomaton::resume()
{
if(m_state != State::Paused)
{
ERROR_LOG(m_logger) << " resume failed - fire automaton must be in state paused.";
return false;
}
setState(State::Running);
m_timer.start(m_remainingTickMSecs);
return true;
}
/**
* @brief Pauses a running fire automaton sequence
*
* In the state running the fire automaton sequence execution will be halted and the state set to paused.
*
* @return True when the automaton is succesfully paused
*/
bool FireAutomaton::pause()
{
if(m_state != State::Running)
{
ERROR_LOG(m_logger) << " pause failed - fire automaton must be in state running.";
return false;
}
m_remainingTickMSecs = m_timer.remainingTime();
m_timer.stop();
setState(State::Paused);
return true;
}
/**
* @brief Stops and resets the fire automaton
*
* Forces the state stopped and resets the automaton sequence execution.
*/
void FireAutomaton::stop()
{
m_tick = 0;
m_remainingTickMSecs = 0;
m_timer.stop();
setState(State::Stopped);
}
/**
* @brief Steps through the derived fire automaton sequence
*
* Stepping can only be started from the state stopped and sets the state to stepping. After stepping through the whole sequence, the state will be set to stopped.
*
* @return True when step is succesfully performed
*/
bool FireAutomaton::performStep()
{
if(m_state != State::Stopped && m_state != State::Stepping)
{
ERROR_LOG(m_logger) << " performStep failed - fire automaton must be in state stepping or stopped.";
return false;
}
m_state = State::Stepping;
nextTick();
return true;
}
/**
* @brief Returns the current state of the fire automaton
*
* @return Fire automaton state
*/
FireAutomaton::State FireAutomaton::getState() const
{
return m_state;
}
bool FireAutomaton::readPattern(const QString& pattern)
{
bool fill = false;
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QStringList elements = pattern.split(QStringLiteral(","), Qt::SkipEmptyParts);
#else
QStringList elements = pattern.split(QStringLiteral(","), QString::SkipEmptyParts);
#endif
for(const QString& element : elements)
{
FireAutomaton::PatternElement patternElement;
if(!patternElementFromString(element, patternElement))
{
WARNING_LOG(m_logger) << " skipped invalid firing pattern elements.";
continue;
}
if(patternElement == FireAutomaton::PatternElement::NextFill || patternElement == FireAutomaton::PatternElement::AllFill)
{
if(!fill)
{
fill = true;
}
else
{
ERROR_LOG(m_logger) << " a pattern may contain at most one fill element.";
return false;
}
}
m_pattern.append(patternElement);
}
if(m_pattern.empty())
{
ERROR_LOG(m_logger) << " error occured reading firing pattern.";
return false;
}
return true;
}
bool FireAutomaton::patternElementFromString(const QString& str, FireAutomaton::PatternElement& out)
{
const QString key = str.trimmed().toLower();
if(key == QStringLiteral("all"))
out = FireAutomaton::PatternElement::All;
else if(key == QStringLiteral("all*"))
out = FireAutomaton::PatternElement::AllFill;
else if(key == QStringLiteral("next"))
out = FireAutomaton::PatternElement::Next;
else if(key == QStringLiteral("next*"))
out = FireAutomaton::PatternElement::NextFill;
else if(key == QStringLiteral("idle"))
out = FireAutomaton::PatternElement::Idle;
else
return false;
return true;
}
QVector<int32_t> FireAutomaton::getEffectorIds(int32_t tick) const
{
return m_schedule[tick];
}
int32_t FireAutomaton::getMaxTick() const
{
return m_schedule.size();
}
bool FireAutomaton::calculateSchedule(int32_t rounds)
{
int32_t minCounter = 0;
// count needed rounds
for(const FireAutomaton::PatternElement& element : m_pattern)
{
if(element == FireAutomaton::PatternElement::All)
minCounter += m_effectorIds.size();
else if(element == FireAutomaton::PatternElement::Next)
++minCounter;
}
int32_t fillCounter = rounds - minCounter;
m_schedule.clear();
for(const FireAutomaton::PatternElement& element : m_pattern)
{
// stop when all rounds used
if(rounds <= 0)
break;
if(element == FireAutomaton::PatternElement::All)
{
if(rounds >= m_effectorIds.size())
{
m_schedule.append(m_effectorIds);
rounds -= m_effectorIds.size();
}
else
{
m_schedule.append(m_effectorIds.mid(0,rounds));
rounds = 0;
}
}
else if(element == FireAutomaton::PatternElement::AllFill && fillCounter > 0)
{
const int32_t times = fillCounter / m_effectorIds.size();
const int32_t remainder = fillCounter % m_effectorIds.size();
for(int32_t i=0; i < times; i++)
{
m_schedule.append(m_effectorIds);
rounds -= m_effectorIds.size();
}
if(remainder > 0)
{
m_schedule.append(m_effectorIds.mid(0, remainder));
rounds -= remainder;
}
}
else if(element == FireAutomaton::PatternElement::Idle)
{
m_schedule.append(QVector<int32_t>{});
}
else if(element == FireAutomaton::PatternElement::Next)
{
m_schedule.append(QVector<int32_t>{m_effectorIds[m_nextIdx]});
m_nextIdx = (m_nextIdx + 1) % m_effectorIds.size();
rounds--;
}
else if(element == FireAutomaton::PatternElement::NextFill && fillCounter > 0)
{
for(int32_t i = 0; i < fillCounter; i++)
{
m_schedule.append(QVector<int32_t>{m_effectorIds[i % m_effectorIds.size()]});
rounds--;
}
}
}
if(rounds > 0)
{
ERROR_LOG(m_logger) << " scheduling error - fire pattern does not provide sufficient slots to schedule all rounds. " << rounds;
return false;
}
return true;
}
} // namespace AreaEffectSimulation
+106
View File
@@ -0,0 +1,106 @@
/**
* AES fire automaton - scheduling fire patterns for multiple effectors
* $Id$
* $HeadURL$
* $Author$
*/
#include <QtCore>
#include <QObject>
#include <QTimer>
#include <QString>
#include <QVector>
#include "../logger.h"
#pragma once
namespace NGL {
class INGLLogger;
}
namespace AreaEffectSimulation {
class AES_COMMON_EXPORT FireAutomaton : public QObject
{
Q_OBJECT
public:
FireAutomaton(QObject *parent = nullptr);
enum class State {
Uninitialized,
Stopped,
Running,
Paused,
Stepping
};
/*
Allowed state transitions:
Uninitialized -> Stopped
Running -> Stopped
Stepping -> Stopped
Paused -> Stopped
Stopped -> Running
Running -> Paused
Paused -> Running
Stopped -> Stepping
*/
enum class TimeType {
Duration,
Cadence
};
bool init(int32_t time, TimeType type, int32_t rounds, const QVector<int32_t>& effectorIds, const QString& pattern);
State getState() const;
signals:
void fire(int32_t effectorId);
void finished();
public slots:
bool start();
void stop();
bool pause();
bool resume();
bool performStep();
private slots:
void nextTick();
private:
enum class PatternElement {
All,
AllFill,
Next,
NextFill,
Idle
};
static bool patternElementFromString(const QString& str, FireAutomaton::PatternElement& out);
bool readPattern(const QString& pattern);
bool calculateSchedule(int32_t rounds);
void setState(State state);
int32_t getMaxTick() const;
QVector<int32_t> getEffectorIds(int32_t tick) const;
private:
QVector<int32_t> m_effectorIds;
QVector<FireAutomaton::PatternElement> m_pattern;
QVector<QVector<int32_t>> m_schedule;
QTimer m_timer;
int32_t m_nextIdx;
int32_t m_tick;
int32_t m_durationMSecs;
int32_t m_intervalMSecs;
int32_t m_remainingTickMSecs;
State m_state;
LOGGER_T m_logger;
};
} // namespace AreaEffectSimulation
+505
View File
@@ -0,0 +1,505 @@
/**
* @RDE_HEADER@
* Firing Timer, a helperclass for ExplosionCluster
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, ESVG, 2024-09-03
*/
#include "firing_timer.h"
#include "../logger.h"
#include <LtsUtilsCore/qpoint3d.h>
#include <QString>
#include <QTimer>
#include <QElapsedTimer>
#include <QDateTime>
#include <QDebug>
#include <iostream>
#include <cmath>
namespace AreaEffectSimulation
{
// To be able to aesily differentiate between different instances:
#define MY_LOGFUNCTION "debugInfo='" << d->m_debugInfo << "' " << Q_FUNC_INFO
static long s_counter{0};
//------------------------------------------------------------------------------
// class
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// class FiringTimer::Private
//------------------------------------------------------------------------------
class FiringTimer::Private
{
public:
Q_DISABLE_COPY(Private);
Private()
{
m_etActive = new QElapsedTimer();
m_etPaused = new QElapsedTimer();
m_firingTimer = new QTimer();
reset();
}
~Private()
{
delete m_etActive;
delete m_etPaused;
}
QString m_debugInfo;
double m_effectDurationMsec{-1};
int m_ammoCount;
long m_intervallMsec{-1};
long m_ammoFlightDurationMsec{-1};
FiringMode m_currentFiringMode {FiringMode::FM_Undefined};
QElapsedTimer *m_etActive{nullptr};
qint64 m_activeMsec{0};
QElapsedTimer *m_etPaused{nullptr};
qint64 m_pausedMsec{0};
QTimer *m_firingTimer{nullptr};
int m_firedAmmoCount{0};
FiringTimer::CMD m_lastDelayedStateCmd {CMD::CMD_UNDEFINED};
QStringList m_logHistorie;
void reset()
{
m_effectDurationMsec = -1;
m_ammoCount = -1;
m_intervallMsec = -1;
m_ammoFlightDurationMsec = -1;
m_currentFiringMode = FiringMode::FM_Undefined;
m_etActive->invalidate();
m_activeMsec = 0;
m_etPaused->invalidate();
m_pausedMsec = 0;
m_firingTimer->stop();
m_firedAmmoCount = 0;
m_lastDelayedStateCmd = CMD::CMD_UNDEFINED;
m_logHistorie.clear();
}
void dump()
{
static LOGGER_T logger = getLogger("Utils.FiringTimer");
INFO_LOG(logger) << " DUMP m_effectDurationMsec=" << m_effectDurationMsec
<< ", m_ammoCount=" << m_ammoCount
<< "-> m_intervallMsec=" << m_intervallMsec
<< ", m_ammoFlightDurationMsec=" << m_ammoFlightDurationMsec
<< ", m_currentFiringMode=" << (int)m_currentFiringMode
<< ", m_lastDelayedStateCmd=" << m_lastDelayedStateCmd
<< ", m_etActive=" << m_etActive
<< ", m_activeMsec=" << m_activeMsec
<< ", m_etPaused=" << m_etPaused
<< ", m_pausedMsec=" << m_pausedMsec
<< ", m_firingTimer=" << m_firingTimer
<< ", m_firedAmmoCount=" << m_firedAmmoCount;
}
};
//------------------------------------------------------------------------------
// class FiringTimer
//------------------------------------------------------------------------------
FiringTimer::FiringTimer(const QString &debugInfo)
: BaseClass(nullptr)
, d(new Private)
, m_logger(getLogger("Utils.FiringTimer"))
{
++s_counter;
d->m_debugInfo = debugInfo;
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " START, s_counter=" << s_counter << " this=" << this;
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this;
}
FiringTimer::~FiringTimer()
{
--s_counter;
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " START, s_counter=" << s_counter << " this=" << this;
QString debugInfoSave = d->m_debugInfo;
delete d;
//DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this;
DEBUG_LOG(m_logger) << "debugInfo='" << debugInfoSave << "'" << LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this;
}
void FiringTimer::setDebugInfo(const QString &debugInfo)
{
d->m_debugInfo = debugInfo;
}
void FiringTimer::reset()
{
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START";
d->reset();
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END";
}
bool FiringTimer::init(long effectDurationMsec, int ammoCount, long ammoFlightDurationMsec)
{
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START,"
<< ", effectDurationMsec=" << effectDurationMsec
<< ", ammoCount=" << ammoCount
<< ", ammoFlightDurationMsec=" << ammoFlightDurationMsec;
reset();
d->m_effectDurationMsec = effectDurationMsec;
d->m_ammoCount = ammoCount;
d->m_ammoFlightDurationMsec = ammoFlightDurationMsec;
d->m_intervallMsec = static_cast<double>(d->m_effectDurationMsec) / d->m_ammoCount; // ensure float calculation
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " m_intervallMsec " << d->m_intervallMsec;
d->m_firingTimer->stop();
d->m_firingTimer->setSingleShot(false);
d->m_firingTimer->setInterval(d->m_intervallMsec);
connect(d->m_firingTimer, &QTimer::timeout,
this, &FiringTimer::onFiringTimout, Qt::UniqueConnection);
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END";
return true;
}
bool FiringTimer::startFiring()
{
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START";
if ((d->m_currentFiringMode != FiringMode::FM_Undefined) &&
(d->m_currentFiringMode != FiringMode::FM_Paused))
{
ERROR_LOG(m_logger) << MY_LOGFUNCTION << " wrong firingMode, cur = " << (int)d->m_currentFiringMode;
return false;
}
if (remainingAmmoCount() <= 0)
{
ERROR_LOG(m_logger) << MY_LOGFUNCTION << " no ammo left"
<< " (total ammoCount was: " << d->m_ammoCount
<< " cur firingMode = " << (int)d->m_currentFiringMode;
return false;
}
#ifdef SUPPORT_STATE_CHANGE_DELAYED
if (d->m_currentFiringMode == FiringMode::FM_Paused)
{
int firedAmmoCountCopy = d->m_firedAmmoCount;
QTimer::singleShot(d->m_ammoFlightDurationMsec, this, [this, firedAmmoCountCopy](){
emitStateChangeDelayed(CMD::CMD_RESUME, firedAmmoCountCopy);
});
}
#endif // SUPPORT_STATE_CHANGE_DELAYED
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " start firing with m_firedAmmoCount = " << d->m_firedAmmoCount;
d->m_firingTimer->start();
d->m_currentFiringMode = FiringMode::FM_Active;
d->m_etActive->start();
// maybe m_etPaused is active, maybe not:
qint64 pausedElapsedMsec = 0;
if (d->m_etPaused->isValid())
{
pausedElapsedMsec = d->m_etPaused->elapsed();
d->m_pausedMsec += pausedElapsedMsec;
d->m_etPaused->invalidate();
}
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " start firing after [ms]: " << pausedElapsedMsec
<< ", m_activeMsec in total = " << d->m_activeMsec
<< ", m_pausedMsec in total = " << d->m_pausedMsec;
QString cmd = QString(QStringLiteral("StartFiring_at_%1")).arg(QDateTime::currentMSecsSinceEpoch());
d->m_logHistorie.push_back(cmd);
// ammoCount : 2 ammo
// effect Duration : 10000 msec
// => interval : 5000 msec
// _____________
// | | |
// _____________
// ^ ^
// | |_ fire 2
// |_ fire 1
//
// ^ ^
// |_ signal firing(1, 2)
// |_ signal firing(2,2)
//
// ^ ^
// |_ signal firingFinishedPlus
// |_ signal firingFinished
// to fire at the beginning of the interval:
onFiringTimout();
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END";
return true;
}
bool FiringTimer::startFiringDelay(qint64 delayMsec)
{
if (delayMsec <= 0)
return false;
QTimer::singleShot(delayMsec, this, &FiringTimer::startFiring);
return true;
}
bool FiringTimer::pauseFiring()
{
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START";
if (d->m_currentFiringMode != FiringMode::FM_Active)
{
ERROR_LOG(m_logger) << MY_LOGFUNCTION << " wrong firingMode";
return false;
}
if (remainingAmmoCount() <= 0)
{
ERROR_LOG(m_logger) << MY_LOGFUNCTION << " no ammo left"
<< " (total ammoCount was: " << d->m_ammoCount;
return false;
}
#ifdef SUPPORT_STATE_CHANGE_DELAYED
int curAmmoCountCopy = d->m_firedAmmoCount;
QTimer::singleShot(d->m_ammoFlightDurationMsec, this, [this, curAmmoCountCopy](){
emitStateChangeDelayed(CMD::CMD_PAUSE, curAmmoCountCopy);
});
#endif // SUPPORT_STATE_CHANGE_DELAYED
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " stop firing at m_firedAmmoCount = " << d->m_firedAmmoCount;
d->m_firingTimer->stop();
d->m_currentFiringMode = FiringMode::FM_Paused;
d->m_etPaused->start();
Q_ASSERT_X(d->m_etActive->isValid(), "FiringTimer", "d->m_etActive not valid");
const qint64 activeElapsedMsec = d->m_etActive->elapsed();
d->m_activeMsec += activeElapsedMsec;
d->m_etActive->invalidate();
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " stop firing after [ms]: " << activeElapsedMsec
<< ", m_activeMsec in total = " << d->m_activeMsec
<< ", m_pausedMsec in total = " << d->m_pausedMsec;
QString log = QString(QStringLiteral("pauseFiring_at_%1")).arg(QDateTime::currentMSecsSinceEpoch()) ;
d->m_logHistorie.append(log);
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END";
return true;
}
long FiringTimer::ammoFlightDurationMsec() const
{
return d->m_ammoFlightDurationMsec;
}
int FiringTimer::firedAmmoCount() const
{
return d->m_firedAmmoCount;
}
int FiringTimer::nextAmmoToFire() const
{
if (remainingAmmoCount() <= 0)
return -1;
return firedAmmoCount() + 1;
}
int FiringTimer::remainingAmmoCount() const
{
return d->m_ammoCount - d->m_firedAmmoCount;
}
long FiringTimer::intervallMsec() const
{
return d->m_intervallMsec;
}
#ifdef SUPPORT_STATE_CHANGE_DELAYED
void FiringTimer::emitStateChangeDelayed(AreaEffectSimulation::FiringTimer::CMD cmd, int firedAmmoCount)
{
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " SUPPORT_STATE_CHANGE_DELAYED: " << cmd
<< ", firedAmmoCount = " << firedAmmoCount;
emit stateChangeDelayedAsInt(static_cast<int>(cmd), firedAmmoCount);
}
#endif // SUPPORT_STATE_CHANGE_DELAYED
QStringList FiringTimer::getLogHistorie() const
{
return d->m_logHistorie;
}
QString FiringTimer::toString(const CMD cmd)
{
switch(cmd)
{
case CMD::CMD_UNDEFINED:
return QStringLiteral("CMD_UNDEFINED");
case CMD::CMD_START:
return QStringLiteral("CMD_START");
case CMD::CMD_RESUME:
return QStringLiteral("CMD_RESUME");
case CMD::CMD_PAUSE:
return QStringLiteral("CMD_PAUSE");
case CMD::CMD_EXPIRED:
return QStringLiteral("CMD_EXPIRED");
case CMD::CMD_EXPIRED_PLUS:
return QStringLiteral("CMD_EXPIRED_PLUS");
default:
return QStringLiteral("CMD_Error");
}
}
void FiringTimer::onFiringTimout()
{
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START";
++d->m_firedAmmoCount;
INFO_LOG(m_logger) << MY_LOGFUNCTION << " emit firing with: " << d->m_firedAmmoCount
<< " of total ammoCount = " << d->m_ammoCount;
emit firing(d->m_firedAmmoCount, d->m_ammoCount);
// prepared signal for impact, not interruptable
if (d->m_ammoFlightDurationMsec >= 0)
{
// copy values:
// i am not sure, if '=' would work here, so copy to local vars:
int currentFiringNumber = d->m_firedAmmoCount;
int totalFiringNumber = d->m_ammoCount;
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " XXXXXXXXX schedule 'emit impact' " << currentFiringNumber;
QTimer::singleShot(d->m_ammoFlightDurationMsec, this, [this, currentFiringNumber, totalFiringNumber]()
{
#ifdef SUPPORT_STATE_CHANGE_DELAYED
if (currentFiringNumber == 1) // Number -> start counting at '1'
{
emitStateChangeDelayed(CMD::CMD_START, currentFiringNumber);
}
#endif // SUPPORT_STATE_CHANGE_DELAYED
INFO_LOG(m_logger) << MY_LOGFUNCTION << " emit impact " << currentFiringNumber
<< " of total = " << totalFiringNumber;
emit impact(currentFiringNumber, totalFiringNumber);
#ifdef SUPPORT_STATE_CHANGE_DELAYED
if (currentFiringNumber >= totalFiringNumber)
{
emitStateChangeDelayed(CMD::CMD_EXPIRED, currentFiringNumber);
// for ..PLUS we need a delay:
QTimer::singleShot(d->m_intervallMsec, this, [this, currentFiringNumber]()
{
emitStateChangeDelayed(CMD::CMD_EXPIRED_PLUS, currentFiringNumber);
});
}
#endif // SUPPORT_STATE_CHANGE_DELAYED
});
}
if (d->m_firedAmmoCount >= d->m_ammoCount)
{
INFO_LOG(m_logger) << MY_LOGFUNCTION << " no ammo left, finishing firing"
<< ", " << d->m_firedAmmoCount << " of total ammoCount = " << d->m_ammoCount << " are fired";
d->m_firingTimer->stop();
d->m_currentFiringMode = FiringMode::FM_Expired;
const qint64 activeElapsedMsec = d->m_etActive->elapsed();
// !! Attention: do pauseFiring() WITHIN the intervall will reduce amount of time the firing is active !!
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " stop firing after [ms]: " << activeElapsedMsec
<< ", m_activeMsec in total = " << d->m_activeMsec
<< ", m_pausedMsec in total = " << d->m_pausedMsec
<< " now FiringTimoutExpiredPlus ...";
// !! Attention: do pauseFiring() WITHIN the intervall will reduce amount of time the firing is active !!
emit firingFinished();
d->dump();
// FM_ExpiredPlus
// maybe use disconnect via QMetaObject :
delete d->m_firingTimer;
d->m_firingTimer = new QTimer();
d->m_firingTimer->setInterval(d->m_intervallMsec);
// : maybe use disconnect via QMetaObject
connect(d->m_firingTimer, &QTimer::timeout,
this, &FiringTimer::onFiringTimoutExpiredPlus, Qt::UniqueConnection);
d->m_firingTimer->setSingleShot(true);
d->m_firingTimer->start();
}
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END" << ", m_firedAmmoCount " << d->m_firedAmmoCount;
}
void FiringTimer::onFiringTimoutExpiredPlus()
{
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START";
d->m_currentFiringMode = FiringMode::FM_ExpiredPlus;
const qint64 activeElapsedMsec = d->m_etActive->elapsed();
d->m_activeMsec += activeElapsedMsec;
d->m_etActive->invalidate();
// !! Attention: do pauseFiring() WITHIN the intervall will reduce amount of time the firing is active !!
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " stop firing2 after [ms]: " << activeElapsedMsec
<< ", m_activeMsec in total = " << d->m_activeMsec
<< ", m_pausedMsec in total = " << d->m_pausedMsec;
// !! Attention: do pauseFiring() WITHIN the intervall will reduce amount of time the firing is active !!
emit firingFinishedPlus();
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END";
}
AES_COMMON_EXPORT std::ostream &operator<< (std::ostream &stream, const FiringTimer::CMD value)
{
stream << FiringTimer::toString(value);
return stream;
}
} // namespace AreaEffectSimulation
+119
View File
@@ -0,0 +1,119 @@
/**
* @RDE_HEADER@
* Firing Timer, a helperclass for ExplosionCluster
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, ESVG, 2024-09-03
*/
#pragma once
#include "aes_export.h"
#include "../logger.h"
#include <QObject>
#include <QString>
#include <iosfwd>
#define SUPPORT_STATE_CHANGE_DELAYED
namespace AreaEffectSimulation
{
class AES_COMMON_EXPORT FiringTimer : public QObject
{
Q_OBJECT
using BaseClass = QObject;
public:
enum class CMD
{
CMD_UNDEFINED,
CMD_START,
CMD_RESUME,
CMD_PAUSE,
CMD_EXPIRED,
CMD_EXPIRED_PLUS,
};
Q_ENUM(CMD);
enum class FiringMode
{
FM_Undefined = 0, //
FM_Prepared = 1, //
FM_Active = 2, //
FM_Paused = 3, //
FM_Expired = 4, //
FM_ExpiredPlus = 6 //
};
// ctor/dtor
explicit FiringTimer(const QString &debugInfo = QStringLiteral("FiringTimer"));
~FiringTimer();
void setDebugInfo(const QString &debugInfo);
// initialisation
bool init(long effectDurationMsec, int ammoCount, long ammoFlightDurationMsec);
void reset();
//
bool startFiring();
bool pauseFiring();
bool resumeFiring();
FiringMode currentFiringMode() const;
long ammoFlightDurationMsec() const;
int firedAmmoCount() const;
int nextAmmoToFire() const;
int remainingAmmoCount() const;
long intervallMsec() const;
QStringList getLogHistorie() const;
static QString toString(const CMD cmd);
public Q_SLOTS:
bool startFiringDelay(qint64 delayMsec);
Q_SIGNALS:
void firing(int currentFiringNumber, int totalAmmoCount);
void impact(int currentImpactNumber, int totalAmmoCount);
void firingFinished();
void firingFinishedPlus();
#ifdef SUPPORT_STATE_CHANGE_DELAYED
// Q_SIGNALS:
// void stateChangeDelayed(AreaEffectSimulation::FiringTimer::CMD cmd); // problems: Q_DECLARE_METATYPE and/or qRegisterMetatType... - grrrr
void stateChangeDelayedAsInt(int cmdAsInt, int firedAmmoCount);
public Q_SLOTS:
void emitStateChangeDelayed(FiringTimer::CMD cmd, int firedAmmoCount);
#endif // SUPPORT_STATE_CHANGE_DELAYED
protected Q_SLOTS:
void onFiringTimout();
void onFiringTimoutExpiredPlus();
private:
class Private;
Private *d;
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
AES_COMMON_EXPORT std::ostream &operator<< (std::ostream &stream, const FiringTimer::CMD value);
} // namespace AreaEffectSimulation
// at global scope
Q_DECLARE_METATYPE(AreaEffectSimulation::FiringTimer::CMD);
+208
View File
@@ -0,0 +1,208 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#include "icons.h"
#include "polygons.h"
#include <AreaEffectSimulation/aes.hpp>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <QGuiApplication>
#include <QIcon>
#include <QPainter>
#include <QPixmap>
#include <QTransform>
namespace AreaEffectSimulation
{
namespace Icons
{
// Todo: Remove
const int NEBEL = 1;
struct PolygonData
{
QPolygonF poly;
QColor color;
Qt::BrushStyle fillPattern = Qt::NoBrush;
PolygonData() = default;
PolygonData(const QPolygonF &_poly,
const QColor &_color,
const Qt::BrushStyle &_fillPattern)
: poly(_poly)
, color(_color)
, fillPattern(_fillPattern)
{}
};
static QPixmap pixmapFromPolygons(const QVector<PolygonData> &datas,
int width,
const QColor &bgcolor = Qt::transparent)
{
if (datas.isEmpty() || datas.first().poly.isEmpty())
return {};
QPolygonF polyUtm;
QVector<QPolygonF> polysUtm;
polysUtm.reserve(datas.size() * 10);
int zone = LtsCoordTrans::UtmCoordinate::fromGeographic(datas.first().poly.first()).zone;
for (const PolygonData &data : datas)
{
polyUtm.clear();
for (const QPointF &point : qAsConst(data.poly))
{
LtsCoordTrans::UtmCoordinate pointUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(point, zone);
polyUtm << QPointF(pointUtm.x, pointUtm.y);
}
polysUtm << polyUtm;
}
QPolygonF bounding;
bounding.reserve(4 * datas.size());
// Does not matter, how the total polygon looks as we just need the bounding rect.
// << is cheaper than .united()
for (const QPolygonF &polyUtmLocal : qAsConst(polysUtm))
bounding << polyUtmLocal;
// center to center / Todo:: Does it always work?
const QTransform mTrans(QTransform::fromTranslate(-bounding.boundingRect().bottomLeft().x(), -bounding.boundingRect().bottomLeft().y()));
const auto dpr = qApp->devicePixelRatio();
width *= dpr;
qreal w = bounding.boundingRect().width();
qreal h = bounding.boundingRect().height();
double scale = w > h ? width / w : width / h;
w *= scale;
h *= scale;
scale *= 0.96; // Leave 4% border (i.e. 2% each side)
const QTransform mScale(QTransform::fromScale(scale, -scale));
const QTransform mTransSmall(QTransform::fromTranslate(0.02 * width + (width - w) / 2, 0.02 * width + (width - h) / 2));
QPixmap pix(width, width);
pix.setDevicePixelRatio(dpr);
pix.fill(bgcolor);
QPainter p(&pix);
int i = 0;
for (const PolygonData &d : qAsConst(datas))
{
QPolygonF mPoly = mTrans.map(polysUtm.at(i++));
mPoly = mScale.map(mPoly);
mPoly = mTransSmall.map(mPoly);
p.setBrush(QBrush(d.color, d.fillPattern));
p.setPen(QPen(p.brush(), 2));
p.drawPolygon(mPoly);
}
p.end();
return pix;
}
static QPixmap pixmapFromPolygon(const QPolygonF &poly,
const QColor &color,
const Qt::BrushStyle &fillPattern,
const int width,
const QColor &bgcolor = Qt::transparent)
{
return pixmapFromPolygons({PolygonData(poly, color, fillPattern)}, width, bgcolor);
}
QIcon nContamination(const Database::AesNContamination &awf, int size)
{
const QPolygonF poly = Polygons::nContamination(awf);
return QIcon(pixmapFromPolygon(poly, Qt::yellow, Qt::Dense4Pattern, size, QColor(0, 0, 0, 128)));
}
QIcon bcContamination(const Database::AesBCContamination &cwf,
ProjectDataManagement::VolatilityType volatilityType,
double expansionFactor,
const QDateTime &currentTime,
int size)
{
const QPolygonF poly = Polygons::bcContamination(cwf, volatilityType, expansionFactor, currentTime);
return QIcon(pixmapFromPolygon(poly, Qt::yellow, Qt::Dense4Pattern, size, QColor(0, 0, 0, 128)));
}
QIcon explosionSingle(const Database::AesExplosionSingle &es, double chargeRadius, int size)
{
QPolygonF poly = Polygons::explosionSingle(es, chargeRadius);
// Todo!
QColor color = (es.chargeTypeId == NEBEL) ? Qt::lightGray : Qt::yellow;
QColor bgrnd = Qt::transparent;
if (es.chargeTypeId != NEBEL) // Nebel / Smoke
{
color = Qt::red; // Todo: Teamcolor! teams->color(ef.partei_id);
bgrnd = QColor(0, 0, 0, 128);
}
return QIcon(pixmapFromPolygon(poly, color, Qt::Dense4Pattern, size, bgrnd));
}
QIcon explosionArea(const Database::AesExplosionArea &ea, int size)
{
// Todo!
QColor color = (ea.chargeTypeId == NEBEL) ? Qt::lightGray : Qt::yellow;
QColor bgrnd = Qt::transparent;
if (ea.chargeTypeId != NEBEL) // Nebel / Smoke
{
color = Qt::red; // Todo: Teamcolor! teams->color(ff.partei_id);
bgrnd = QColor(0, 0, 0, 128);
}
QPolygonF area = Polygons::explosionArea(ea);
return QIcon(pixmapFromPolygon(area, color, Qt::Dense4Pattern, size, bgrnd));
}
QIcon explosionAreaSingleExplosions(const Database::AesExplosionArea &ea, int size)
{
// Todo!
QColor color = (ea.chargeTypeId == NEBEL) ? Qt::lightGray : Qt::yellow;
if (ea.chargeTypeId != NEBEL) // Nebel / Smoke
color = Qt::red; // Todo: Teamcolor! teams->color(ea.partei_id);
const QVector<QPointF> singleExplosions = Polygons::explosionAreaSingleExplosions(ea);
QVector<PolygonData> datas;
datas.reserve(singleExplosions.size() + 1);
datas << PolygonData(Polygons::explosionArea(ea), color, Qt::Dense4Pattern);
for (const QPointF &explosion : singleExplosions)
{
/* TODO
if (explosion.time() < QDateTime::currentMSecsSinceEpoch())
datas << PolygonData(Polygons::circle(explosion.position(), 5), Qt::yellow, Qt::Dense4Pattern);
else
*/
datas << PolygonData(Polygons::circle(explosion, 5), Qt::green, Qt::Dense4Pattern);
}
return QIcon(pixmapFromPolygons(datas, size));
}
QIcon mineArea(const Database::AesMineArea &area, const QVector<Database::AesMineAreaSegment> &segments, int size)
{
QPolygonF gesamtPolygon = Polygons::mineArea(segments, area.width, area.density, area.isCenterLine);
const QVector<QPointF> minePositions = Polygons::minePositions(area, segments);
QVector<PolygonData> datas;
datas.reserve(minePositions.size() + 1);
datas << PolygonData(gesamtPolygon, Qt::green, Qt::Dense4Pattern);
for (const QPointF minePos : minePositions)
datas << PolygonData(Polygons::circle(minePos, 5), Qt::red, Qt::Dense4Pattern);
return QIcon(pixmapFromPolygons(datas, size));
}
QIcon mineCorridor(const QVector<Database::AesMineCorridorSegment> &segments, int width, int size)
{
const QPolygonF poly = Polygons::mineCorridor(segments, width);
return QIcon(pixmapFromPolygon(poly, Qt::yellow, Qt::Dense4Pattern, size, QColor(0, 0, 0, 128)));
}
} // Namespace
} // Namespace
+31
View File
@@ -0,0 +1,31 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AreaEffectSimulation/aes_db_structs.h>
#include <ProjectDataManagement/pdm_enums.hpp>
class QIcon;
namespace AreaEffectSimulation
{
namespace Icons
{
AES_COMMON_EXPORT QIcon nContamination(const Database::AesNContamination &awf, int size = 256);
AES_COMMON_EXPORT QIcon bcContamination(const Database::AesBCContamination &cwf,
ProjectDataManagement::VolatilityType volatilityType,
double expansionFactor,
const QDateTime &currentTime,
int size = 256);
AES_COMMON_EXPORT QIcon explosionSingle(const Database::AesExplosionSingle &es, double chargeRadius, int size = 256);
AES_COMMON_EXPORT QIcon explosionArea(const Database::AesExplosionArea &ea, int size = 256);
AES_COMMON_EXPORT QIcon explosionAreaSingleExplosions(const Database::AesExplosionArea &ea, int size = 256);
AES_COMMON_EXPORT QIcon mineArea(const Database::AesMineArea &area, const QVector<Database::AesMineAreaSegment> &segments, int size = 256);
AES_COMMON_EXPORT QIcon mineCorridor(const QVector<Database::AesMineCorridorSegment> &segments, int width, int size = 256);
}
}
+89
View File
@@ -0,0 +1,89 @@
/**
* @RDE_HEADER@
* Test ob sich ein Rechteck mit Wirkfeldformen überschneidet
* $Id$
* $HeadURL$
* $Author$
*/
#include "intersections.h"
#include "polygons.h"
#include <QtGui/QPolygonF>
#include <QtGui/QPainterPath>
namespace AreaEffectSimulation
{
namespace Intersections
{
static inline bool intersects(const QPolygonF &p1, const QPolygonF &p2)
{
QPainterPath pp1;
pp1.addPolygon(p1);
QPainterPath pp2;
pp2.addPolygon(p2);
return pp1.intersects(pp2);
}
bool nContamination(const Database::AesNContamination &awf,
const QPolygonF &poly)
{
return (intersects(Polygons::nContamination(awf), poly));
}
bool bcContamination(const Database::AesBCContamination &cwf,
ProjectDataManagement::VolatilityType volatilityType,
double expansionFactor,
const QDateTime &currentTime,
const QPolygonF &poly)
{
return (intersects(Polygons::bcContamination(cwf, volatilityType, expansionFactor, currentTime), poly));
}
bool explosionSingle(const Database::AesExplosionSingle &es,
double chargeRadius,
const QPolygonF &poly)
{
return (intersects(Polygons::explosionSingle(es, chargeRadius), poly));
}
bool explosionArea(const Database::AesExplosionArea &ea,
const QPolygonF &poly)
{
return (intersects(Polygons::explosionArea(ea), poly));
}
bool mineArea(const QVector<Database::AesMineAreaSegment> &segments,
double width,
double density,
bool bIsCenterLine,
const QPolygonF &poly)
{
// if(!area.boundingRect.isEmpty() && !area.boundingRect.intersects(rect))
// return false;
return (intersects(Polygons::mineArea(segments, width, density, bIsCenterLine), poly));
}
QVector<int> mineIdsInCorridor(const Database::AesMineArea &area,
const QVector<Database::AesMineAreaSegment> &segments,
const QVector<QPolygonF> &corridorSegments)
{
QVector<int> minesIds;
const QVector<QPointF> singleMines = Polygons::minePositions(area, segments);
// Other possibility: Create total polygon by uniting corridor segments. But that may be less efficient.
for (const QPolygonF &corridorSegment : corridorSegments)
{
for (int i = 0; i < singleMines.size(); ++i)
{
if (corridorSegment.contains(singleMines[i]))
minesIds << i;
}
}
return minesIds;
}
} // Namespace
}
+42
View File
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* Test ob sich ein Rechteck mit Wirkfeldformen überschneidet
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AreaEffectSimulation/aes_db_structs.h>
#include <ProjectDataManagement/pdm_enums.hpp>
namespace AreaEffectSimulation
{
namespace Intersections
{
AES_COMMON_EXPORT bool nContamination(const Database::AesNContamination &awf,
const QPolygonF &poly);
AES_COMMON_EXPORT bool bcContamination(const Database::AesBCContamination &cwf,
ProjectDataManagement::VolatilityType volatilityType,
double expansionFactor,
const QDateTime &currentTime,
const QPolygonF &poly);
AES_COMMON_EXPORT bool explosionSingle(const Database::AesExplosionSingle &es,
double chargeRadius,
const QPolygonF &poly);
AES_COMMON_EXPORT bool explosionArea(const Database::AesExplosionArea &ea,
const QPolygonF &poly);
AES_COMMON_EXPORT bool mineArea(const QVector<Database::AesMineAreaSegment> &segments,
double width,
double density,
bool bIsCenterLine,
const QPolygonF &poly);
// keep area (instead of width/density) as also random number is needed!
AES_COMMON_EXPORT QVector<int> mineIdsInCorridor(const Database::AesMineArea &area,
const QVector<Database::AesMineAreaSegment> &segments,
const QVector<QPolygonF> &corridorSegments);
}
}
File diff suppressed because it is too large. Load diff
+176
View File
@@ -0,0 +1,176 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AreaEffectSimulation/aes_db_structs.h>
#include <ProjectDataManagement/pdm_enums.hpp>
#include <QColor>
#include <QPolygonF>
#include <QRectF>
class QPointF;
namespace LtsCoordTrans
{
struct UtmCoordinate;
}
namespace SituationMap
{
class ElevationMap;
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
} // namespace ParticipantManagement
namespace NGL
{
class INGLLogger;
}
namespace AreaEffectSimulation
{
// N. Thoden, 18. jan 2024:
// I can´t find a file like aes_types.h, aes_constants.h.
// So i use polygons.h to provide following for service as well as gui:
static const int s_maxTrajectoriesPerExplosionCluster = 900;
namespace Polygons
{
using Trajectory = QPair<QPolygonF, QVector<double>>;
struct AES_COMMON_EXPORT AreaBottomLeft
{
explicit AreaBottomLeft(const QPointF &cornerBottomLeftMeter, double widthMeter, double heightMeter, double rotation_deg);
explicit AreaBottomLeft(const LtsCoordTrans::UtmCoordinate &cornerBottomLeftMeter, double widthMeter, double heightMeter, double rotation_deg);
bool isValid() const;
QPointF m_cornerBottomLeft; // meter
double m_width{0}; // x; meter,
double m_height{0}; // y; meter, aka length
double m_rotation_deg{0};
};
AES_COMMON_EXPORT QPixmap pixmapFromPolygon(const QPolygonF &poly,
const QColor &color,
const int &width,
const Qt::BrushStyle &fillPattern = Qt::BrushStyle::NoBrush,
bool isLine = false,
const QColor &bgcolor = Qt::transparent);
AES_COMMON_EXPORT QPolygonF circle(const QPointF &center, qreal radius, int steps = 72);
AES_COMMON_EXPORT QVector<double> trajectory(const double &distance, const double &height, double angle, int steps = 72);
AES_COMMON_EXPORT QPolygonF nContamination(const Database::AesNContamination &awf);
AES_COMMON_EXPORT void bcCurrentCenterPoint(const Database::AesBCContamination &cwf,
ProjectDataManagement::VolatilityType volatilityType,
const QDateTime &currentTime,
QPointF &newCenterPointGeographic,
double &distance);
AES_COMMON_EXPORT QPolygonF bcContamination(const Database::AesBCContamination &cwf,
ProjectDataManagement::VolatilityType volatilityType,
double expansionFactor,
const QDateTime &currentTime);
AES_COMMON_EXPORT QPolygonF airSupportImpact(const Database::AesAirSupportImpact &as, double chargeRadius);
AES_COMMON_EXPORT QPolygonF explosionSingle(const Database::AesExplosionSingle &ef, double chargeRadius);
AES_COMMON_EXPORT QPolygonF explosionArea(const Database::AesExplosionArea &ea);
AES_COMMON_EXPORT QVector<QPointF> explosionAreaSingleExplosions(const Database::AesExplosionArea &explosionArea);
// ExplosionCluster (3D-Wirksim)
AES_COMMON_EXPORT AreaBottomLeft explosionClusterAddTla(const AreaBottomLeft &areaBottomLeftIn, int tla);
AES_COMMON_EXPORT QPolygonF explosionClusterArea(const Database::AesExplosionCluster &explosionCluster); // geometryType-aware
AES_COMMON_EXPORT QVector<QPointF> explosionClusterSingleExplosionsGAUSS(const Database::AesExplosionCluster &explosionCluster);
AES_COMMON_EXPORT QVector<QPointF> explosionClusterSingleExplosionsGAUSS_Mtp(const Database::AesExplosionCluster &explosionCluster, double mtpRotationNorthDegree);
AES_COMMON_EXPORT LtsUtils::QPoint3D calculateFiringUnitCenterNonRecursive(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
int unitParticipantId,
std::vector<Core::GeoPosition> &singlePositionsOut,
NGL::INGLLogger &logger);
// MTP = Mittlerer TrefferPunkt
AES_COMMON_EXPORT QVector<QPointF> explosionClusterAreaToMtps(const AreaBottomLeft &boundary,
int numberOfMTPs,
double *singleWidthOut = nullptr,
double *singleHeightOut = nullptr);
AES_COMMON_EXPORT QVector<QPointF> explosionClusterRadialToMtps(const AreaBottomLeft &boundary,
int numberOfMTPs,
const LtsCoordTrans::UtmCoordinate &firingPos,
const LtsCoordTrans::UtmCoordinate &circleCenter,
double *singleWidthOut = nullptr,
double *singleHeightOut = nullptr);
AES_COMMON_EXPORT QVector<QPointF> explosionClusterAreaToMtpsRelativeToBottomLeft(const AreaBottomLeft &boundary,
int numberOfMTPs,
double *singleWidthOut = nullptr,
double *singleHeightOut = nullptr);
AES_COMMON_EXPORT QVector<QPointF> explosionClusterRadialToMtpsRelativeToBottomLeft(const AreaBottomLeft &boundary,
int numberOfMTPs,
const LtsCoordTrans::UtmCoordinate &firingPos,
const LtsCoordTrans::UtmCoordinate &circleCenter,
double *singleWidthOut = nullptr,
double *singleHeightOut = nullptr);
AES_COMMON_EXPORT qint64 explosionClusterCalculatedFlightDuration2DMSec(const Database::AesExplosionCluster &explosionCluster, int timeOffsetMSec = 2000);
AES_COMMON_EXPORT qint64 explosionClusterEstimatedMaxFlightDuration2DMSec(qint64 calculatedFlightDuration, int calculationOffsetMSec = 10000);
// EffectCluster (3D-Wirksim/Adler)
AES_COMMON_EXPORT QPolygonF effectClusterArea(const Database::AesEffectCluster &effectCluster); // geometryType-aware
AES_COMMON_EXPORT QVector<QPointF> effectClusterSingleExplosionsGAUSS(const Database::AesEffectCluster &effectCluster);
AES_COMMON_EXPORT QVector<QPointF> effectClusterSingleExplosionsGAUSS_Mtp(const Database::AesEffectCluster &effectCluster,
const QHash<int, Database::AesEffectorData> &effectorData);
// offRouteMine
AES_COMMON_EXPORT QPolygonF offRouteMineArea(const Database::AesOffRouteMine &ea);
AES_COMMON_EXPORT QPolygonF offRouteMineAreaImpact(const Database::AesOffRouteMine &ea, int steps = 72);
AES_COMMON_EXPORT QPolygonF boobyTrapCake(const Database::AesBoobyTrap &boobyTrap, qreal radius);
AES_COMMON_EXPORT QPointF bottomLeft2CenterPoint(QPointF bottomLeft, double width, double height, double rotation);
AES_COMMON_EXPORT QPointF centerPoint2BottomLeft(QPointF centerPoint, double width, double height, double rotation);
AES_COMMON_EXPORT QPolygonF functionalArea(const Database::AesFunctionalArea &fa);
AES_COMMON_EXPORT QPolygonF technicalArea(const Database::AesTechnicalArea &ta);
AES_COMMON_EXPORT QPolygonF reconArea(const Database::AesReconArea &ra);
AES_COMMON_EXPORT QPolygonF dangerArea(const QVector<QPointF> &segments);
AES_COMMON_EXPORT std::vector<Core::GeoPosition2D> dangerAreaDefaultSegments(const QPointF &centerPos, const int &radius);
// AES_COMMON_EXPORT QPolygonF dangerArea(const shm_gefahrenbereich_rt& gb);
AES_COMMON_EXPORT QPolygonF borderline(const QVector<QPointF> &segments);
AES_COMMON_EXPORT QPolygonF gasPipe(const QVector<Database::AesGasPipeSegment> &segments, double width);
AES_COMMON_EXPORT QPolygonF gasPipeSegment(const Database::AesGasPipeSegment &start, const Database::AesGasPipeSegment &end, double width);
AES_COMMON_EXPORT QPolygonF mineArea(const QVector<Database::AesMineAreaSegment> &segments, double width, double density, bool bIsCenterLine);
AES_COMMON_EXPORT QVector<QPolygonF> mineAreaSingleSegments(const QVector<Database::AesMineAreaSegment> &segments, double width, bool bIsCenterLine);
AES_COMMON_EXPORT QPolygonF mineAreaSegment(const Database::AesMineAreaSegment &segment, double width, bool bIsCenterLine);
AES_COMMON_EXPORT QPolygonF mineCorridor(const QVector<Database::AesMineCorridorSegment> &segments, double width);
AES_COMMON_EXPORT QVector<QPolygonF> mineCorridorSingleSegments(const QVector<Database::AesMineCorridorSegment> &segments, double width);
AES_COMMON_EXPORT QPolygonF mineCorridorSegment(const Database::AesMineCorridorSegment &start, const Database::AesMineCorridorSegment &end, double width);
// keep area (instead of width/density) as also random number is needed!
AES_COMMON_EXPORT QVector<QPointF> minePositions(const Database::AesMineArea &area, const QVector<Database::AesMineAreaSegment> &segments);
AES_COMMON_EXPORT int getSequelNumber(const Database::AesExplosionArea &explosionArea);
AES_COMMON_EXPORT int getSequelNumber(int startSequelNumber, int impactCount, qint64 originalEffectDurationMs, qint64 effectDurationMs);
// calculates the minimum Area Enclosing Rectangle, bottom left is OUR Bottom Left, outangleDregrees is already counter clockwise
AES_COMMON_EXPORT QRectF getMinAreaRect(const QVector<QPointF> &points, double &outAngleDegrees);
// Hilfsfunktion für die konvexe Hülle (Monotone Chain Algorithmus)
QVector<QPointF> calculateConvexHull(const QVector<QPointF> &points);
AES_COMMON_EXPORT QVector<LtsCoordTrans::UtmCoordinate> geoVecToUtm(const std::vector<Core::GeoPosition2D> &vecPos, int utmZone = -1);
AES_COMMON_EXPORT QVector<QPointF> geoVecToUtmPointF(const std::vector<Core::GeoPosition2D> &vecPos, int utmZone = -1);
} // namespace Polygons
} // namespace AreaEffectSimulation
+194
View File
@@ -0,0 +1,194 @@
/**
* @RDE_HEADER@
* main for AreaEffectSimulation
* $Id$
* $HeadURL$
* $Author$
*/
#include <nglapplication/nglapplication.h>
#include <NGLLogger/INGLLogger.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include "icons.h"
#include "polygons.h"
#include <QWidget>
#include <QLabel>
#include <QIcon>
#include <LtsCoordTrans/lts_coord_trans.h>
QIcon iconExplosionArea()
{
AreaEffectSimulation::Database::AesExplosionArea ff;
ff.impactPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
ff.impactWidth = 50;
ff.impactLength = 10;
ff.rotationAngle = 30;
return AreaEffectSimulation::Icons::explosionArea(ff);
}
QIcon iconExplosionAreaSingleExplosions()
{
AreaEffectSimulation::Database::AesExplosionArea ff;
ff.impactPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
ff.impactWidth = 50;
ff.id = 42;
ff.impactLength = 500;
ff.rotationAngle = 30;
ff.effectStart = QDateTime::currentDateTimeUtc().addSecs(-300);
ff.effectDuration = 600000;
ff.cannonCount = 5;
ff.cannonRounds = 5;
return AreaEffectSimulation::Icons::explosionAreaSingleExplosions(ff);
}
QIcon iconNContamination()
{
AreaEffectSimulation::Database::AesNContamination ff;
ff.impactPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
ff.rotationAngle = 30;
ff.borderLengthInner = 50;
ff.borderLengthOuter = 100;
ff.impactRadius = 50;
ff.spanAngle = 60;
return AreaEffectSimulation::Icons::nContamination(ff);
}
QIcon iconBcContamination()
{
AreaEffectSimulation::Database::AesBCContamination ff;
ff.impactPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
ff.impactRadius = 500;
ff.weatherPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7200062000"));
ff.weatherTimestamp = QDateTime::currentDateTimeUtc().addSecs(-3600);
ff.effectStart = ff.weatherTimestamp.addSecs(360);
ff.effectDuration = 360000; // msecs
ff.propagationDirection = 20;
ff.windSpeed = 2;
ff.chargeTypeId = 1;
return AreaEffectSimulation::Icons::bcContamination(ff,
ProjectDataManagement::VolatilityType::Floating,
1.25,
QDateTime::currentDateTimeUtc());
}
QIcon iconExplosionSingle()
{
AreaEffectSimulation::Database::AesExplosionSingle ff;
ff.impactPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
ff.chargeTypeId = 5;
return AreaEffectSimulation::Icons::explosionSingle(ff, 7.43);
}
QIcon IconMineArea()
{
AreaEffectSimulation::Database::AesMineArea ff;
ff.id = 42;
ff.density = 7;
ff.width = 100;
QVector<AreaEffectSimulation::Database::AesMineAreaSegment> vec;
AreaEffectSimulation::Database::AesMineAreaSegment seg1;
seg1.elementNumber = 1;
seg1.mineAreaId = 42;
seg1.startPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
seg1.endPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7050061000"));
// AreaEffectSimulation::Database::AesMineAreaSegment seg2;
// seg2.elementNumber = 2;
// seg2.density = 300;
// seg2.mineAreaId = 42;
// seg2.mineCount = 10;
// seg2.rotationAngle = 10;
// seg2.startPosition = QPointF(190,170);
// seg2.width = 100;
vec << seg1 ; //<< seg2;
return AreaEffectSimulation::Icons::mineArea(ff, vec);
}
QIcon IconMineAreaPositions()
{
AreaEffectSimulation::Database::AesMineArea ff;
ff.id = 42;
ff.density = 0.07;
ff.width = 100;
QVector<AreaEffectSimulation::Database::AesMineAreaSegment> vec;
AreaEffectSimulation::Database::AesMineAreaSegment seg1, seg2;
seg1.elementNumber = 1;
seg1.mineAreaId = 42;
seg1.startPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
seg1.endPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7050061000"));
seg2.elementNumber = 2;
seg2.mineAreaId = 42;
seg2.startPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7027260421"));
seg2.endPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7060060732"));
vec << seg1 << seg2;
return AreaEffectSimulation::Icons::mineArea(ff, vec);
}
QIcon IconMineCorridor()
{
QVector<AreaEffectSimulation::Database::AesMineCorridorSegment> vec;
vec.reserve(3);
AreaEffectSimulation::Database::AesMineCorridorSegment seg1, seg2, seg3;
seg1.elementNumber = 1;
seg1.mineCorridorId = 43;
seg1.position = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7010060100"));
seg2 = seg1;
seg2.elementNumber++;
seg2.position = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7020060100"));
seg3 = seg2;
seg3.elementNumber++;
seg3.position = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7030060200"));
vec << seg1;
vec << seg2;
vec << seg3;
return AreaEffectSimulation::Icons::mineCorridor(vec, 30);
}
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
NGLApplication app(argc, argv);
app.createLogger("AES", "log4cxx_aesutilstest.props");
int iRet = -1;
{
STARTUP_LOG(app.baseLogger());
QIcon i;
// i = iconExplosionArea();
// i = iconExplosionAreaSingleExplosions();
// i = iconBcContamination();
// i = iconExplosionSingle();
// i = iconNContamination();
// i = IconMineCorridor();
// i = IconMineAreaPositions();
// i = IconMineAreaPositions();
QWidget *p = new QWidget();
QLabel *l = new QLabel(p);
l->setPixmap(i.pixmap(256, 256));
p->show();
iRet = app.exec();
SHUTDOWN_LOG(app.baseLogger());
}
return iRet;
}
+139
View File
@@ -0,0 +1,139 @@
/**
* @RDE_HEADER@
* TNE_Algorithemn zur Nachbildung von TNE-Funktionalität, hier Berechnung der Einschlagkoordinaten eines Flächenfeuers
* $Id$
* $HeadURL$
* $Author$
*/
#include "tne_algorithmen.h"
#include <iostream>
namespace AreaEffectSimulation
{
namespace TNE_ALGORITHMEN
{
QVector<QPointF> areaExlosionsGAUSS(int wirkfeldKennung, int breite_m, int tiefe_m, short anzahl_einschlaege)
{
if (anzahl_einschlaege<=0)
return {};
// breite, tiefe sollte ein Vielfaches von 5m sein, vergleiche ICD
// const int breite_m = breite_5m * 5;
// const int tiefe_m = tiefe_5m * 5;
QVector<QPointF> impacts;
impacts.reserve(anzahl_einschlaege);
ULONG xor32 = wirkfeldKennung; // ZZ-Generator initialisieren:
for (int i = 0; i < anzahl_einschlaege; ++i)
{
QPointF point;
// p1 bestimmen (Breite/Ostwert)
point.rx() = breite_m / 2 + xorshift32_nv_limited(&xor32, breite_m / 2, 3);
// ^^^
// p2 bestimmen (Tiefe/Nordwert)
point.ry() = tiefe_m / 2 + xorshift32_nv_limited(&xor32, tiefe_m / 2, 3);
// ^^^
// std::cout << " i=" << i
// << ", p1=" << point.x() << ", p2=" << point.y()
// << std::endl;
impacts.append(point);
} // for
return impacts;
}
QVector<QPointF> areaExlosionsGAUSS_aroundCenter(int wirkfeldKennung, int breite_m, int tiefe_m, short anzahl_einschlaege)
{
if (anzahl_einschlaege<=0)
return {};
// breite, tiefe sollte ein Vielfaches von 5m sein, vergleiche ICD
// const int breite_m = breite_5m * 5;
// const int tiefe_m = tiefe_5m * 5;
QVector<QPointF> impacts;
impacts.reserve(anzahl_einschlaege);
ULONG xor32 = wirkfeldKennung; // ZZ-Generator initialisieren:
for (int i = 0; i < anzahl_einschlaege; ++i)
{
QPointF point;
// p1 bestimmen (Breite/Ostwert)
point.rx() = /*breite_m / 2 + */ xorshift32_nv_limited(&xor32, breite_m / 2, 3);
// p2 bestimmen (Tiefe/Nordwert)
point.ry() = /*tiefe_m / 2 + */ xorshift32_nv_limited(&xor32, tiefe_m / 2, 3);
// std::cout << " i=" << i
// << ", p1=" << point.x() << ", p2=" << point.y()
// << std::endl;
impacts.append(point);
} // for
return impacts;
}
USHORT xorshift32(ULONG *_px)
{
ULONG x32 = *_px;
x32 ^= x32 << 13;
x32 ^= x32 >> 17;
x32 ^= x32 << 5;
*_px = x32;
return x32 % 32768; // für x % n mit n = 2^b; b natürliche Zahl, gilt: x % n = x & (n - 1)
}
SHORT xorshift32_nv(ULONG *_generator, SHORT _bereich, INT_AS_CHAR _sigmaGrenze)
{
//suffix _nv steht für 'normal verteilt'
//DBG_ASSERT(false, "ZZ-generator eingangs=%lu\r", *_generator);
LONG sum = 0;
int i;
for (i = 0; i < 12; i++)
sum += (ULONG)xorshift32(_generator);
// INTEGER-Implementierung 32bit, vgl. GUEZSB60__TN-0006_Wirksimulationsmodelle.docx, Abschnitt 3.3.2.2
short m = 32768 - 1;
LONG zentrierteSumme = sum - 6 * m;
LONG nenner = _sigmaGrenze * m;
if (zentrierteSumme >= 0)
return (SHORT)((zentrierteSumme * _bereich + nenner / 2) / nenner);
else
return (SHORT)((zentrierteSumme * _bereich - nenner / 2) / nenner);
}
SHORT xorshift32_nv_limited(ULONG *_generator, SHORT _bereich, INT_AS_CHAR _sigmaGrenze)
{
SHORT zz = xorshift32_nv(_generator, _bereich, _sigmaGrenze);
if (zz > _bereich)
return _bereich;
if (zz < (-1 * _bereich))
return (-1 * _bereich);
return zz;
}
} // namespace TNE_ALGORITHMEN
} // namespace AreaEffectSimulation
+83
View File
@@ -0,0 +1,83 @@
/**
* @RDE_HEADER@
* TNE_Algorithemn zur Nachbildung von TNE-Funktionalität, hier Berechnung der Einschlagkoordinaten eines Flächenfeuers
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AreaEffectSimulation/aes_export.h>
#include <QVector>
#include <QPointF>
namespace AreaEffectSimulation
{
/**
* @brief This namespace contains some algorithms, which must behave identical on both sides!!!
* Therefore the is an UnitTest with values taken from the TNE: ../UnitTests/UnitTests_TneAlgo
* That was created while Michael Moetje was implementing that code at the TNE (PlayerUnit)
*/
namespace TNE_ALGORITHMEN
{
//
// Hinweise für die Übernahme/Updates des wesentlichen Codes (AWES_FF_IteriereEinschlagkoordinaten2)
//
/* moetje:
"long" -> 32Bit
MTP -> MittlerTrefferPunkt
*/
// some using/typedef to ease updates
using SHORT = qint16;
using USHORT = quint16;
using ULONG = quint32; // TNE ist auf 32Bit Plattform
using LONG = qint32; // TNE ist auf 32Bit Plattform
using INT_AS_CHAR = int;
//
// basic TNE wrapper:
//
/**
* @brief areaExlosionsGAUSS berechnet die Einschläge eines Flächenfeuers entsprechend der Normal-Verteilung, siehe auch TNGUEZSB60__TN-0006_Wirksimulationsmodelle.docx
* @param wirkfeldKennung dient als Startwert für den Zufallzahlen-Generator.
* @param breite in Meter, sollte aber ein Vielfaches von 5m sein, vergleiche ICD.
* @param tiefe in Meter, sollte aber ein Vielfaches von 5m sein, vergleiche ICD.
* @param anzahl_einschlaege Die Anzahl der gewuenschten Einschläge
* @return Einschläge entsprechend der Normal-Verteilung
*/
AES_COMMON_EXPORT QVector<QPointF> areaExlosionsGAUSS(int wirkfeldKennung, int breite_m, int tiefe_m, short anzahl_einschlaege);
/**
* @brief areaExlosionsGAUSS berechnet die Einschläge eines Flächenfeuers entsprechend der Normal-Verteilung, siehe auch TNGUEZSB60__TN-0006_Wirksimulationsmodelle.docx
* @param wirkfeldKennung dient als Startwert für den Zufallzahlen-Generator.
* @param breite in Meter, sollte aber ein Vielfaches von 5m sein, vergleiche ICD.
* @param tiefe in Meter, sollte aber ein Vielfaches von 5m sein, vergleiche ICD.
* @param anzahl_einschlaege Die Anzahl der gewuenschten Einschläge
* @return Einschläge entsprechend der Normal-Verteilung
*/
AES_COMMON_EXPORT QVector<QPointF> areaExlosionsGAUSS_aroundCenter(int wirkfeldKennung, int breite_m, int tiefe_m, short anzahl_einschlaege);
AES_COMMON_EXPORT USHORT xorshift32(ULONG *_px);
AES_COMMON_EXPORT SHORT xorshift32_nv(ULONG *_generator, SHORT _bereich, INT_AS_CHAR _sigmaGrenze);
AES_COMMON_EXPORT SHORT xorshift32_nv_limited(ULONG *_generator, SHORT _bereich, INT_AS_CHAR _sigmaGrenze);
} // namespace TNE_ALGORITHMEN
} // namespace AreaEffectSimulation