Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/application/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,22 @@ ScenarioAuthoringWidget::ScenarioState scenarioStateFromSaved(
uiPlacement.generatedPositions = placement.explicitPositions;
state.crowdPlacements.push_back(std::move(uiPlacement));
}
for (const auto& source : saved.draft.population.occupantSources) {
ScenarioCrowdPlacement uiPlacement;
uiPlacement.id = QString::fromStdString(source.id);
uiPlacement.name = uiPlacement.id;
uiPlacement.kind = ScenarioCrowdPlacementKind::Source;
uiPlacement.zoneId = QString::fromStdString(source.zoneId);
uiPlacement.floorId = QString::fromStdString(source.floorId);
uiPlacement.area = {source.position};
uiPlacement.occupantCount = static_cast<int>(source.targetAgentCount);
uiPlacement.velocity = source.initialVelocity;
uiPlacement.sourceAgentsPerSpawn = std::max(1, static_cast<int>(source.agentsPerSpawn));
uiPlacement.sourceStartSeconds = source.startSeconds;
uiPlacement.sourceEndSeconds = source.endSeconds;
uiPlacement.sourceIntervalSeconds = source.spawnIntervalSeconds;
state.crowdPlacements.push_back(std::move(uiPlacement));
}

return state;
}
Expand Down
38 changes: 38 additions & 0 deletions src/application/ProjectPersistence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -703,13 +703,48 @@ safecrowd::domain::InitialPlacement2D initialPlacementFromJson(const QJsonObject
return placement;
}

QJsonObject occupantSourceToJson(const safecrowd::domain::OccupantSource2D& source) {
QJsonObject object;
object["id"] = QString::fromStdString(source.id);
object["zoneId"] = QString::fromStdString(source.zoneId);
object["floorId"] = QString::fromStdString(source.floorId);
object["position"] = pointArray(source.position);
object["targetAgentCount"] = static_cast<qint64>(source.targetAgentCount);
object["agentsPerSpawn"] = static_cast<qint64>(source.agentsPerSpawn);
object["startSeconds"] = source.startSeconds;
object["endSeconds"] = source.endSeconds;
object["spawnIntervalSeconds"] = source.spawnIntervalSeconds;
object["initialVelocity"] = pointArray(source.initialVelocity);
return object;
}

safecrowd::domain::OccupantSource2D occupantSourceFromJson(const QJsonObject& object) {
safecrowd::domain::OccupantSource2D source;
source.id = object.value("id").toString().toStdString();
source.zoneId = object.value("zoneId").toString().toStdString();
source.floorId = object.value("floorId").toString().toStdString();
source.position = pointFromJson(object.value("position"));
source.targetAgentCount = static_cast<std::size_t>(object.value("targetAgentCount").toInteger());
source.agentsPerSpawn = static_cast<std::size_t>(std::max<qint64>(1, object.value("agentsPerSpawn").toInteger(1)));
source.startSeconds = object.value("startSeconds").toDouble(0.0);
source.endSeconds = object.value("endSeconds").toDouble(180.0);
source.spawnIntervalSeconds = object.value("spawnIntervalSeconds").toDouble(5.0);
source.initialVelocity = pointFromJson(object.value("initialVelocity"));
return source;
}

QJsonObject populationToJson(const safecrowd::domain::PopulationSpec& population) {
QJsonObject object;
QJsonArray placements;
for (const auto& placement : population.initialPlacements) {
placements.append(initialPlacementToJson(placement));
}
object["initialPlacements"] = placements;
QJsonArray sources;
for (const auto& source : population.occupantSources) {
sources.append(occupantSourceToJson(source));
}
object["occupantSources"] = sources;
return object;
}

Expand All @@ -718,6 +753,9 @@ safecrowd::domain::PopulationSpec populationFromJson(const QJsonObject& object)
for (const auto& value : object.value("initialPlacements").toArray()) {
population.initialPlacements.push_back(initialPlacementFromJson(value.toObject()));
}
for (const auto& value : object.value("occupantSources").toArray()) {
population.occupantSources.push_back(occupantSourceFromJson(value.toObject()));
}
return population;
}

Expand Down
81 changes: 74 additions & 7 deletions src/application/ScenarioAuthoringWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,9 @@ int draftOccupantCount(const safecrowd::domain::ScenarioDraft& scenario) {
for (const auto& placement : scenario.population.initialPlacements) {
total += static_cast<int>(placement.targetAgentCount);
}
for (const auto& source : scenario.population.occupantSources) {
total += static_cast<int>(source.targetAgentCount);
}
return total;
}

Expand All @@ -411,8 +414,10 @@ QString buildChangeSummaryLine(
const safecrowd::domain::ScenarioDraft& variant,
const std::string& key) {
if (key == "population.placements") {
const auto baselinePlacements = static_cast<int>(baseline.population.initialPlacements.size());
const auto variantPlacements = static_cast<int>(variant.population.initialPlacements.size());
const auto baselinePlacements = static_cast<int>(
baseline.population.initialPlacements.size() + baseline.population.occupantSources.size());
const auto variantPlacements = static_cast<int>(
variant.population.initialPlacements.size() + variant.population.occupantSources.size());
QStringList parts;
const int occupantDelta = draftOccupantCount(variant) - draftOccupantCount(baseline);
if (occupantDelta != 0) {
Expand Down Expand Up @@ -634,6 +639,9 @@ int totalOccupantCount(const ScenarioAuthoringWidget::ScenarioState& scenario) {
for (const auto& placement : scenario.draft.population.initialPlacements) {
total += static_cast<int>(placement.targetAgentCount);
}
for (const auto& source : scenario.draft.population.occupantSources) {
total += static_cast<int>(source.targetAgentCount);
}
return total;
}

Expand Down Expand Up @@ -722,6 +730,36 @@ QIcon groupCrowdTreeIcon() {
QColor("#1f5fae"));
}

QIcon sourceCrowdTreeIcon() {
QPixmap pixmap(44, 44);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.setRenderHint(QPainter::Antialiasing, true);
const QColor color("#1f5fae");
painter.setPen(QPen(color, 2.4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
painter.setBrush(Qt::NoBrush);
QPainterPath platform;
platform.moveTo(9.0, 35.0);
platform.lineTo(30.0, 35.0);
platform.lineTo(35.0, 40.0);
platform.lineTo(4.0, 40.0);
platform.closeSubpath();
painter.drawPath(platform);

painter.drawEllipse(QPointF(18, 12), 6.0, 6.0);
QPainterPath body;
body.moveTo(10.0, 33.0);
body.lineTo(10.0, 25.0);
body.cubicTo(10.0, 19.5, 13.5, 17.0, 18.0, 17.0);
body.cubicTo(22.5, 17.0, 26.0, 19.5, 26.0, 25.0);
body.lineTo(26.0, 33.0);
painter.drawPath(body);

painter.drawLine(QPointF(32.0, 26.0), QPointF(41.0, 26.0));
painter.drawLine(QPointF(36.5, 21.5), QPointF(36.5, 30.5));
return QIcon(pixmap);
}

std::vector<NavigationTreeNode> buildCrowdTree(const ScenarioAuthoringWidget::ScenarioState* scenario) {
if (scenario == nullptr || scenario->crowdPlacements.empty()) {
return {};
Expand All @@ -730,8 +768,9 @@ std::vector<NavigationTreeNode> buildCrowdTree(const ScenarioAuthoringWidget::Sc
std::vector<NavigationTreeNode> placements;
for (const auto& placement : scenario->crowdPlacements) {
const bool group = placement.kind == ScenarioCrowdPlacementKind::Group;
const bool source = placement.kind == ScenarioCrowdPlacementKind::Source;
std::vector<NavigationTreeNode> occupants;
for (int index = 1; index <= placement.occupantCount; ++index) {
for (int index = 1; group && index <= placement.occupantCount; ++index) {
occupants.push_back({
.label = QString("Occupant %1").arg(index),
.id = QString("%1/occupant-%2").arg(placement.id).arg(index),
Expand All @@ -744,6 +783,16 @@ std::vector<NavigationTreeNode> buildCrowdTree(const ScenarioAuthoringWidget::Sc
});
}

const auto detail = source
? QString("Source schedule: %1 people every %2s for %3 min\nVelocity: (%4, %5)")
.arg(placement.sourceAgentsPerSpawn)
.arg(placement.sourceIntervalSeconds, 0, 'f', 1)
.arg(std::max(0.0, placement.sourceEndSeconds - placement.sourceStartSeconds) / 60.0, 0, 'f', 1)
.arg(placement.velocity.x, 0, 'f', 2)
.arg(placement.velocity.y, 0, 'f', 2)
: QString("Velocity: (%1, %2)")
.arg(placement.velocity.x, 0, 'f', 2)
.arg(placement.velocity.y, 0, 'f', 2);
placements.push_back({
.label = QString("%1 - %2 - %3 %4")
.arg(
Expand All @@ -752,10 +801,8 @@ std::vector<NavigationTreeNode> buildCrowdTree(const ScenarioAuthoringWidget::Sc
.arg(placement.occupantCount)
.arg(placement.occupantCount == 1 ? "occupant" : "occupants"),
.id = placement.id,
.detail = QString("Velocity: (%1, %2)")
.arg(placement.velocity.x, 0, 'f', 2)
.arg(placement.velocity.y, 0, 'f', 2),
.icon = group ? groupCrowdTreeIcon() : individualCrowdTreeIcon(),
.detail = detail,
.icon = source ? sourceCrowdTreeIcon() : (group ? groupCrowdTreeIcon() : individualCrowdTreeIcon()),
.children = group ? std::move(occupants) : std::vector<NavigationTreeNode>{},
.expanded = false,
});
Expand Down Expand Up @@ -1715,7 +1762,27 @@ void ScenarioAuthoringWidget::updateCurrentScenarioPlacements(const std::vector<

scenario->crowdPlacements = placements;
scenario->draft.population.initialPlacements.clear();
scenario->draft.population.occupantSources.clear();
for (const auto& placement : scenario->crowdPlacements) {
if (placement.kind == ScenarioCrowdPlacementKind::Source) {
if (placement.area.empty()) {
continue;
}
safecrowd::domain::OccupantSource2D source;
source.id = placement.id.toStdString();
source.zoneId = placement.zoneId.toStdString();
source.floorId = placement.floorId.toStdString();
source.position = placement.area.front();
source.targetAgentCount = static_cast<std::size_t>(std::max(0, placement.occupantCount));
source.agentsPerSpawn = static_cast<std::size_t>(std::max(1, placement.sourceAgentsPerSpawn));
source.startSeconds = std::max(0.0, placement.sourceStartSeconds);
source.endSeconds = std::max(source.startSeconds, placement.sourceEndSeconds);
source.spawnIntervalSeconds = std::max(0.1, placement.sourceIntervalSeconds);
source.initialVelocity = placement.velocity;
scenario->draft.population.occupantSources.push_back(std::move(source));
continue;
}

safecrowd::domain::InitialPlacement2D initialPlacement;
initialPlacement.id = placement.id.toStdString();
initialPlacement.zoneId = placement.zoneId.toStdString();
Expand Down
16 changes: 16 additions & 0 deletions src/application/ScenarioBatchResultWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,22 @@ ScenarioAuthoringWidget::ScenarioState scenarioStateFromDraft(
uiPlacement.generatedPositions = placement.explicitPositions;
state.crowdPlacements.push_back(std::move(uiPlacement));
}
for (const auto& source : scenario.population.occupantSources) {
ScenarioCrowdPlacement uiPlacement;
uiPlacement.id = QString::fromStdString(source.id);
uiPlacement.name = uiPlacement.id;
uiPlacement.kind = ScenarioCrowdPlacementKind::Source;
uiPlacement.zoneId = QString::fromStdString(source.zoneId);
uiPlacement.floorId = QString::fromStdString(source.floorId);
uiPlacement.area = {source.position};
uiPlacement.occupantCount = static_cast<int>(source.targetAgentCount);
uiPlacement.velocity = source.initialVelocity;
uiPlacement.sourceAgentsPerSpawn = std::max(1, static_cast<int>(source.agentsPerSpawn));
uiPlacement.sourceStartSeconds = source.startSeconds;
uiPlacement.sourceEndSeconds = source.endSeconds;
uiPlacement.sourceIntervalSeconds = source.spawnIntervalSeconds;
state.crowdPlacements.push_back(std::move(uiPlacement));
}

return state;
}
Expand Down
Loading
Loading