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
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ configure_project_target(ecs_engine)
add_library(safecrowd_domain STATIC
src/domain/SafeCrowdDomain.h
src/domain/SafeCrowdDomain.cpp
src/domain/DemoLayouts.h
src/domain/DemoLayouts.cpp
src/domain/Geometry2D.h
src/domain/PopulationSpec.h
src/domain/ScenarioAuthoring.h
Expand Down
25 changes: 17 additions & 8 deletions src/application/LayoutPreviewWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,20 @@ void drawLine(QPainter& painter, const safecrowd::domain::LineSegment2D& line, c
painter.drawLine(transform.map(line.start), transform.map(line.end));
}

void drawPolyline(QPainter& painter, const safecrowd::domain::Polyline2D& polyline, const LayoutTransform& transform) {
const auto path = polylinePath(polyline, transform);
if (path.size() <= 1) {
return;
}

if (polyline.closed && path.size() > 2) {
painter.drawPolygon(path);
return;
}

painter.drawPolyline(path);
}

bool stringListContains(const std::vector<std::string>& values, const QString& target) {
return std::any_of(values.begin(), values.end(), [&](const std::string& value) {
return QString::fromStdString(value) == target;
Expand Down Expand Up @@ -380,11 +394,9 @@ void LayoutPreviewWidget::paintEvent(QPaintEvent* event) {
}

painter.setPen(QPen(QColor(70, 70, 70), 3));
painter.setBrush(Qt::NoBrush);
for (const auto& barrier : importResult_.layout->barriers) {
const auto path = polylinePath(barrier.geometry, transform);
if (path.size() > 1) {
painter.drawPolyline(path);
}
drawPolyline(painter, barrier.geometry, transform);
}
}

Expand All @@ -405,10 +417,7 @@ void LayoutPreviewWidget::paintEvent(QPaintEvent* event) {
}
for (const auto& barrier : importResult_.layout->barriers) {
if (QString::fromStdString(barrier.id) == focusedTargetId_ || traceMatches(barrier.provenance, focusedTargetId_)) {
const auto path = polylinePath(barrier.geometry, transform);
if (path.size() > 1) {
painter.drawPolyline(path);
}
drawPolyline(painter, barrier.geometry, transform);
}
}
}
Expand Down
43 changes: 35 additions & 8 deletions src/application/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,29 @@
#include "application/NewProjectWidget.h"
#include "application/ProjectPersistence.h"
#include "application/ProjectNavigatorWidget.h"
#include "domain/DemoLayouts.h"
#include "domain/DxfImportService.h"
#include "domain/ImportIssue.h"
#include "domain/ImportOrchestrator.h"
#include "domain/ImportValidationService.h"
#include "domain/SafeCrowdDomain.h"

namespace safecrowd::application {
namespace {

safecrowd::domain::ImportResult makeDemoImportResult() {
safecrowd::domain::ImportResult result;
result.layout = safecrowd::domain::DemoLayouts::demoFacility();

safecrowd::domain::ImportValidationService validator;
result.issues = validator.validate(*result.layout);
result.reviewStatus = safecrowd::domain::hasBlockingImportIssue(result.issues)
? safecrowd::domain::ImportReviewStatus::Pending
: safecrowd::domain::ImportReviewStatus::NotRequired;
return result;
}

} // namespace

MainWindow::MainWindow(safecrowd::domain::SafeCrowdDomain& domain, QWidget* parent)
: QMainWindow(parent),
Expand Down Expand Up @@ -84,6 +102,11 @@ void MainWindow::saveCurrentProject() {
return;
}

if (currentProject_.isBuiltInDemo()) {
QMessageBox::information(this, "Save Project", "Built-in demo projects do not need to be saved.");
return;
}

QString errorMessage;
if (!ProjectPersistence::saveProject(currentProject_, &errorMessage)) {
QMessageBox::warning(this, "Save Project", errorMessage);
Expand All @@ -98,14 +121,18 @@ void MainWindow::showLayoutReview(const ProjectMetadata& metadata) {
currentProject_ = metadata;
hasCurrentProject_ = true;

safecrowd::domain::DxfImportService importer;
const safecrowd::domain::ImportRequest importRequest{
.sourcePath = std::filesystem::path(metadata.layoutPath.toStdWString()),
.requestedFormat = safecrowd::domain::ImportedFileFormat::Dxf,
.preserveRawModel = true,
.runValidation = true,
};
auto importResult = importer.importFile(importRequest);
auto importResult = metadata.isBuiltInDemo()
? makeDemoImportResult()
: [&metadata]() {
safecrowd::domain::DxfImportService importer;
const safecrowd::domain::ImportRequest importRequest{
.sourcePath = std::filesystem::path(metadata.layoutPath.toStdWString()),
.requestedFormat = safecrowd::domain::ImportedFileFormat::Dxf,
.preserveRawModel = true,
.runValidation = true,
};
return importer.importFile(importRequest);
}();

setCentralWidget(new LayoutReviewWidget(
metadata.name,
Expand Down
18 changes: 18 additions & 0 deletions src/application/ProjectMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,33 @@

namespace safecrowd::application {

inline QString builtInDemoLayoutPath() {
return QStringLiteral("safecrowd://demo/sprint1-facility");
}

struct ProjectMetadata {
QString name{};
QString folderPath{};
QString layoutPath{};
QString savedAt{};

bool isBuiltInDemo() const noexcept {
return layoutPath == builtInDemoLayoutPath();
}

bool isValid() const noexcept {
if (isBuiltInDemo()) {
return !name.isEmpty();
}
return !name.isEmpty() && !folderPath.isEmpty() && !layoutPath.isEmpty();
}
};

inline ProjectMetadata makeBuiltInDemoProject() {
return {
.name = QStringLiteral("Demo"),
.layoutPath = builtInDemoLayoutPath(),
};
}

} // namespace safecrowd::application
11 changes: 11 additions & 0 deletions src/application/ProjectPersistence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ bool copyLayoutIntoProject(ProjectMetadata& metadata, QString* errorMessage) {

QList<ProjectMetadata> ProjectPersistence::loadRecentProjects() {
QList<ProjectMetadata> projects;
projects.push_back(makeBuiltInDemoProject());

const auto document = readJsonDocument(recentProjectsPath());
if (!document.isObject()) {
Expand All @@ -123,6 +124,9 @@ QList<ProjectMetadata> ProjectPersistence::loadRecentProjects() {

for (const auto& value : document.object().value("projects").toArray()) {
const auto indexed = fromJson(value.toObject());
if (indexed.isBuiltInDemo()) {
continue;
}
const auto loaded = loadProject(indexed.folderPath);
if (loaded.isValid()) {
projects.push_back(loaded);
Expand All @@ -142,6 +146,13 @@ ProjectMetadata ProjectPersistence::loadProject(const QString& folderPath) {
}

bool ProjectPersistence::saveProject(ProjectMetadata metadata, QString* errorMessage) {
if (metadata.isBuiltInDemo()) {
if (errorMessage != nullptr) {
*errorMessage = "Built-in demo projects do not need to be saved.";
}
return false;
}

if (!metadata.isValid()) {
if (errorMessage != nullptr) {
*errorMessage = "Project name, folder, and layout path are required.";
Expand Down
68 changes: 7 additions & 61 deletions src/domain/DemoFixtureService.cpp
Original file line number Diff line number Diff line change
@@ -1,76 +1,22 @@
#include "domain/DemoFixtureService.h"

#include "domain/DemoLayouts.h"

namespace safecrowd::domain {

DemoFixture DemoFixtureService::createSprint1DemoFixture() const {
DemoFixture fixture;
auto& layout = fixture.layout;
layout.id = "demo-fixture-01";
layout.name = "Sprint 1 Demo Layout";
layout.levelId = "L1";

Zone2D mainRoom;
mainRoom.id = "zone-room-1";
mainRoom.kind = ZoneKind::Room;
mainRoom.label = "Main Demo Room";
mainRoom.area = Polygon2D{
.outline = {
{0.0, 0.0},
{20.0, 0.0},
{20.0, 20.0},
{0.0, 20.0},
},
};
mainRoom.defaultCapacity = 200;
layout.zones.push_back(mainRoom);

Zone2D exitZone;
exitZone.id = "zone-exit-1";
exitZone.kind = ZoneKind::Exit;
exitZone.label = "Main Exit";
exitZone.area = Polygon2D{
.outline = {
{18.0, 20.0},
{20.0, 20.0},
{20.0, 22.0},
{18.0, 22.0},
},
};
exitZone.defaultCapacity = 20;
layout.zones.push_back(exitZone);

Connection2D exitConnection;
exitConnection.id = "conn-exit-1";
exitConnection.kind = ConnectionKind::Exit;
exitConnection.fromZoneId = mainRoom.id;
exitConnection.toZoneId = exitZone.id;
exitConnection.effectiveWidth = 2.0;
exitConnection.centerSpan = LineSegment2D{{18.0, 20.0}, {20.0, 20.0}};
layout.connections.push_back(exitConnection);

Barrier2D centerObstacle;
centerObstacle.id = "barrier-1";
centerObstacle.blocksMovement = true;
centerObstacle.geometry = Polyline2D{
.vertices = {
{8.0, 10.0},
{12.0, 10.0},
{12.0, 11.0},
{8.0, 11.0},
},
.closed = true,
};
layout.barriers.push_back(centerObstacle);
fixture.layout = DemoLayouts::demoFacility();

fixture.population.initialPlacements.push_back({
.id = "placement-1",
.zoneId = mainRoom.id,
.zoneId = DemoLayouts::Sprint1FacilityIds::MainRoomZoneId,
.area = {
.outline = {
{1.0, 1.0},
{5.0, 1.0},
{5.0, 5.0},
{1.0, 5.0},
{4.0, 1.0},
{4.0, 4.0},
{1.0, 4.0},
},
},
.targetAgentCount = 100,
Expand Down
Loading
Loading