From e9cb69001087161cbae0cf2e179335c5e394ee6b Mon Sep 17 00:00:00 2001 From: 95x8x9 Date: Thu, 9 Apr 2026 02:29:21 +0900 Subject: [PATCH 1/4] [Domain] implement demo layout fixture for Sprint 1 (#14) --- src/domain/DemoFixtureService.cpp | 73 +++++++++++++++++++++++++++++++ src/domain/DomoFixtureService.h | 13 ++++++ src/domain/FacilityLayout2D.h | 8 ++++ 3 files changed, 94 insertions(+) create mode 100644 src/domain/DemoFixtureService.cpp create mode 100644 src/domain/DomoFixtureService.h diff --git a/src/domain/DemoFixtureService.cpp b/src/domain/DemoFixtureService.cpp new file mode 100644 index 0000000..dd8b213 --- /dev/null +++ b/src/domain/DemoFixtureService.cpp @@ -0,0 +1,73 @@ +#include "domain/DemoFixtureService.h" + +namespace safecrowd::domain { + + FacilityLayout2D DemoFixtureService::createSprint1DemoLayout() const { + FacilityLayout2D layout; + layout.id = "demo-fixture-01"; + layout.name = "Sprint 1 Demo Layout"; + layout.levelId = "L1"; + + // 1. (20m x 20m 簢 ) + 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); + + // 2. ⱸ ( 18~20 ) + 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); + + // 3. + 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); + + // 4. ֹ ( Ѱ ġ 4x1 ũ . Ʈ ȸ ׽Ʈ) + 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); + + // 5. ( ϴ, 100 ) + SpawnZone2D spawnArea; + spawnArea.id = "spawn-1"; + spawnArea.targetAgentCount = 100; // Sprint 1 䱸 + spawnArea.area = Polygon2D{ + .outline = { + {1.0, 1.0}, {5.0, 1.0}, {5.0, 5.0}, {1.0, 5.0} + } + }; + layout.spawnZones.push_back(spawnArea); + + return layout; + } + +} // namespace safecrowd::domain \ No newline at end of file diff --git a/src/domain/DomoFixtureService.h b/src/domain/DomoFixtureService.h new file mode 100644 index 0000000..12f9ea8 --- /dev/null +++ b/src/domain/DomoFixtureService.h @@ -0,0 +1,13 @@ +#pragma once + +#include "domain/FacilityLayout2D.h" + +namespace safecrowd::domain { + + class DemoFixtureService { + public: + // Sprint 1 20x20 ũ + FacilityLayout2D createSprint1DemoLayout() const; + }; + +} // namespace safecrowd::domain \ No newline at end of file diff --git a/src/domain/FacilityLayout2D.h b/src/domain/FacilityLayout2D.h index dba2268..fbbf19e 100644 --- a/src/domain/FacilityLayout2D.h +++ b/src/domain/FacilityLayout2D.h @@ -84,6 +84,13 @@ struct ControlPoint2D { ElementProvenance provenance{}; }; +struct SpawnZone2D { + std::string id{}; + Polygon2D area{}; + std::size_t targetAgentCount{ 0 }; // Ʈ + ElementProvenance provenance{}; +}; + struct FacilityLayout2D { std::string id{}; std::string name{}; @@ -92,6 +99,7 @@ struct FacilityLayout2D { std::vector connections{}; std::vector barriers{}; std::vector controls{}; + std::vector spawnXones{}; }; } // namespace safecrowd::domain From 948517637d5d03c4b2f2df487190c37a75b8de77 Mon Sep 17 00:00:00 2001 From: gsh <144871327+95x8x9@users.noreply.github.com> Date: Thu, 9 Apr 2026 03:06:45 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EB=AA=85=20=EC=98=A4?= =?UTF-8?q?=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/{DomoFixtureService.h => DemoFixtureService.h} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/domain/{DomoFixtureService.h => DemoFixtureService.h} (64%) diff --git a/src/domain/DomoFixtureService.h b/src/domain/DemoFixtureService.h similarity index 64% rename from src/domain/DomoFixtureService.h rename to src/domain/DemoFixtureService.h index 12f9ea8..0a731d8 100644 --- a/src/domain/DomoFixtureService.h +++ b/src/domain/DemoFixtureService.h @@ -6,8 +6,8 @@ namespace safecrowd::domain { class DemoFixtureService { public: - // Sprint 1 20x20 ũ + // Sprint 1 데모를 위한 20x20 크기의 고정 맵을 생성 FacilityLayout2D createSprint1DemoLayout() const; }; -} // namespace safecrowd::domain \ No newline at end of file +} // namespace safecrowd::domain From c5d01a0e4ceac662e82e8f5708ea4c8174843743 Mon Sep 17 00:00:00 2001 From: gsh <144871327+95x8x9@users.noreply.github.com> Date: Thu, 9 Apr 2026 03:16:57 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/FacilityLayout2D.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domain/FacilityLayout2D.h b/src/domain/FacilityLayout2D.h index fbbf19e..82ff92e 100644 --- a/src/domain/FacilityLayout2D.h +++ b/src/domain/FacilityLayout2D.h @@ -87,7 +87,7 @@ struct ControlPoint2D { struct SpawnZone2D { std::string id{}; Polygon2D area{}; - std::size_t targetAgentCount{ 0 }; // Ʈ + std::size_t targetAgentCount{ 0 }; // 이 구역에 생성할 에이전트 수 ElementProvenance provenance{}; }; @@ -99,7 +99,7 @@ struct FacilityLayout2D { std::vector connections{}; std::vector barriers{}; std::vector controls{}; - std::vector spawnXones{}; + std::vector spawnZones{}; }; } // namespace safecrowd::domain From 780818832634af30dbe4284d94be14a511052afc Mon Sep 17 00:00:00 2001 From: learncold Date: Thu, 9 Apr 2026 16:45:30 +0900 Subject: [PATCH 4/4] [Domain] align demo fixture with population contract --- CMakeLists.txt | 4 + src/domain/DemoFixtureService.cpp | 131 ++++++++++++++++-------------- src/domain/DemoFixtureService.h | 15 ++-- src/domain/FacilityLayout2D.h | 8 -- src/domain/PopulationSpec.h | 22 +++++ tests/DemoFixtureServiceTests.cpp | 36 ++++++++ 6 files changed, 142 insertions(+), 74 deletions(-) create mode 100644 src/domain/PopulationSpec.h create mode 100644 tests/DemoFixtureServiceTests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0094216..8c51af4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,8 +72,11 @@ add_library(safecrowd_domain STATIC src/domain/SafeCrowdDomain.h src/domain/SafeCrowdDomain.cpp src/domain/Geometry2D.h + src/domain/PopulationSpec.h src/domain/RawImportModel.h src/domain/CanonicalGeometry.h + src/domain/DemoFixtureService.h + src/domain/DemoFixtureService.cpp src/domain/DxfImportService.h src/domain/DxfImportService.cpp src/domain/FacilityLayout2D.h @@ -114,6 +117,7 @@ if (BUILD_TESTING) tests/EcsCoreTests.cpp tests/ImportContractsTests.cpp tests/DxfImportServiceTests.cpp + tests/DemoFixtureServiceTests.cpp tests/FacilityLayoutBuilderTests.cpp tests/WorldQueryTests.cpp tests/CommandBufferTests.cpp diff --git a/src/domain/DemoFixtureService.cpp b/src/domain/DemoFixtureService.cpp index dd8b213..0bde4f6 100644 --- a/src/domain/DemoFixtureService.cpp +++ b/src/domain/DemoFixtureService.cpp @@ -2,72 +2,81 @@ namespace safecrowd::domain { - FacilityLayout2D DemoFixtureService::createSprint1DemoLayout() const { - FacilityLayout2D layout; - layout.id = "demo-fixture-01"; - layout.name = "Sprint 1 Demo Layout"; - layout.levelId = "L1"; +DemoFixture DemoFixtureService::createSprint1DemoFixture() const { + DemoFixture fixture; + auto& layout = fixture.layout; + layout.id = "demo-fixture-01"; + layout.name = "Sprint 1 Demo Layout"; + layout.levelId = "L1"; - // 1. (20m x 20m 簢 ) - 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 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); - // 2. ⱸ ( 18~20 ) - 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); + 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); - // 3. - 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); + 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); - // 4. ֹ ( Ѱ ġ 4x1 ũ . Ʈ ȸ ׽Ʈ) - 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); + 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); - // 5. ( ϴ, 100 ) - SpawnZone2D spawnArea; - spawnArea.id = "spawn-1"; - spawnArea.targetAgentCount = 100; // Sprint 1 䱸 - spawnArea.area = Polygon2D{ + fixture.population.initialPlacements.push_back({ + .id = "placement-1", + .zoneId = mainRoom.id, + .area = { .outline = { - {1.0, 1.0}, {5.0, 1.0}, {5.0, 5.0}, {1.0, 5.0} - } - }; - layout.spawnZones.push_back(spawnArea); + {1.0, 1.0}, + {5.0, 1.0}, + {5.0, 5.0}, + {1.0, 5.0}, + }, + }, + .targetAgentCount = 100, + }); - return layout; - } + return fixture; +} -} // namespace safecrowd::domain \ No newline at end of file +} // namespace safecrowd::domain diff --git a/src/domain/DemoFixtureService.h b/src/domain/DemoFixtureService.h index 0a731d8..228da40 100644 --- a/src/domain/DemoFixtureService.h +++ b/src/domain/DemoFixtureService.h @@ -1,13 +1,18 @@ #pragma once #include "domain/FacilityLayout2D.h" +#include "domain/PopulationSpec.h" namespace safecrowd::domain { - class DemoFixtureService { - public: - // Sprint 1 데모를 위한 20x20 크기의 고정 맵을 생성 - FacilityLayout2D createSprint1DemoLayout() const; - }; +struct DemoFixture { + FacilityLayout2D layout{}; + PopulationSpec population{}; +}; + +class DemoFixtureService { +public: + DemoFixture createSprint1DemoFixture() const; +}; } // namespace safecrowd::domain diff --git a/src/domain/FacilityLayout2D.h b/src/domain/FacilityLayout2D.h index 82ff92e..dba2268 100644 --- a/src/domain/FacilityLayout2D.h +++ b/src/domain/FacilityLayout2D.h @@ -84,13 +84,6 @@ struct ControlPoint2D { ElementProvenance provenance{}; }; -struct SpawnZone2D { - std::string id{}; - Polygon2D area{}; - std::size_t targetAgentCount{ 0 }; // 이 구역에 생성할 에이전트 수 - ElementProvenance provenance{}; -}; - struct FacilityLayout2D { std::string id{}; std::string name{}; @@ -99,7 +92,6 @@ struct FacilityLayout2D { std::vector connections{}; std::vector barriers{}; std::vector controls{}; - std::vector spawnZones{}; }; } // namespace safecrowd::domain diff --git a/src/domain/PopulationSpec.h b/src/domain/PopulationSpec.h new file mode 100644 index 0000000..953f58e --- /dev/null +++ b/src/domain/PopulationSpec.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +#include "domain/Geometry2D.h" + +namespace safecrowd::domain { + +struct InitialPlacement2D { + std::string id{}; + std::string zoneId{}; + Polygon2D area{}; + std::size_t targetAgentCount{0}; +}; + +struct PopulationSpec { + std::vector initialPlacements{}; +}; + +} // namespace safecrowd::domain diff --git a/tests/DemoFixtureServiceTests.cpp b/tests/DemoFixtureServiceTests.cpp new file mode 100644 index 0000000..d156b14 --- /dev/null +++ b/tests/DemoFixtureServiceTests.cpp @@ -0,0 +1,36 @@ +#include + +#include "TestSupport.h" + +#include "domain/DemoFixtureService.h" +#include "domain/ImportIssue.h" +#include "domain/ImportValidationService.h" + +SC_TEST(DemoFixtureServiceBuildsSprint1Fixture) { + safecrowd::domain::DemoFixtureService service; + const auto fixture = service.createSprint1DemoFixture(); + const auto& layout = fixture.layout; + const auto& population = fixture.population; + + SC_EXPECT_EQ(layout.id, std::string("demo-fixture-01")); + SC_EXPECT_EQ(layout.name, std::string("Sprint 1 Demo Layout")); + SC_EXPECT_EQ(layout.levelId, std::string("L1")); + SC_EXPECT_EQ(layout.zones.size(), std::size_t{2}); + SC_EXPECT_EQ(layout.zones.front().kind, safecrowd::domain::ZoneKind::Room); + SC_EXPECT_EQ(layout.zones.back().kind, safecrowd::domain::ZoneKind::Exit); + + SC_EXPECT_EQ(layout.connections.size(), std::size_t{1}); + SC_EXPECT_EQ(layout.connections.front().kind, safecrowd::domain::ConnectionKind::Exit); + SC_EXPECT_NEAR(layout.connections.front().effectiveWidth, 2.0, 1e-9); + SC_EXPECT_EQ(layout.barriers.size(), std::size_t{1}); + SC_EXPECT_TRUE(layout.barriers.front().geometry.closed); + + SC_EXPECT_EQ(population.initialPlacements.size(), std::size_t{1}); + SC_EXPECT_EQ(population.initialPlacements.front().zoneId, std::string("zone-room-1")); + SC_EXPECT_EQ(population.initialPlacements.front().targetAgentCount, std::size_t{100}); + SC_EXPECT_EQ(population.initialPlacements.front().area.outline.size(), std::size_t{4}); + + safecrowd::domain::ImportValidationService validator; + const auto issues = validator.validate(layout); + SC_EXPECT_TRUE(!safecrowd::domain::hasBlockingImportIssue(issues)); +}