1+ #include " domain/CompressionSystem.h"
2+ #include " domain/FacilityLayout2D.h"
3+ #include " domain/AgentComponents.h"
4+ #include " domain/metrics.h"
5+ #include < algorithm>
6+ #include < cmath>
7+
8+ namespace safecrowd ::domain {
9+
10+ static float distanceBetween (const Point2D& p1, const Point2D& p2) {
11+ float dx = static_cast <float >(p1.x - p2.x );
12+ float dy = static_cast <float >(p1.y - p2.y );
13+ return std::sqrt (dx * dx + dy * dy);
14+ }
15+
16+ static float distancePointToSegment (const Point2D& p, const Point2D& a, const Point2D& b) {
17+ float l2 = static_cast <float >(std::pow (b.x - a.x , 2 ) + std::pow (b.y - a.y , 2 ));
18+ if (l2 == 0 .0f ) return distanceBetween (p, a);
19+
20+ float t = std::clamp (static_cast <float >(((p.x - a.x ) * (b.x - a.x ) + (p.y - a.y ) * (b.y - a.y )) / l2), 0 .0f , 1 .0f );
21+ Point2D projection = { a.x + t * (b.x - a.x ), a.y + t * (b.y - a.y ) };
22+ return distanceBetween (p, projection);
23+ }
24+
25+ void CompressionSystem::update (engine::ComponentRegistry& registry, float dt) {
26+ // 필요한 스토리지들을 로드
27+ auto & posStorage = registry.storageFor <Position>();
28+ auto & agentStorage = registry.storageFor <Agent>();
29+ auto & compStorage = registry.storageFor <CompressionData>();
30+
31+ // Barrier2D 스토리지가 있는지 확인
32+ if (!registry.isRegistered <Barrier2D>()) return ;
33+ auto & barrierStorage = registry.storageFor <Barrier2D>();
34+
35+ // Position을 가진 모든 엔티티를 순회
36+ for (const auto & entity : posStorage.getEntities ()) {
37+ // Agent와 CompressionData 컴포넌트가 모두 있는지 확인
38+ if (!agentStorage.contains (entity) || !compStorage.contains (entity)) continue ;
39+
40+ const auto & pos = posStorage.get (entity);
41+ const auto & agent = agentStorage.get (entity);
42+ auto & compression = compStorage.get (entity);
43+
44+ float currentForce = 0 .0f ;
45+
46+ // [군중 간 압박]
47+ for (const auto & otherEntity : posStorage.getEntities ()) {
48+ if (entity.index == otherEntity.index && entity.generation == otherEntity.generation ) continue ;
49+ if (!agentStorage.contains (otherEntity)) continue ;
50+
51+ const auto & otherPos = posStorage.get (otherEntity);
52+ const auto & otherAgent = agentStorage.get (otherEntity);
53+
54+ float dist = distanceBetween (pos.value , otherPos.value );
55+ float combinedRadius = agent.radius + otherAgent.radius ;
56+
57+ if (dist < combinedRadius) {
58+ currentForce += (combinedRadius - dist);
59+ }
60+ }
61+
62+ // [벽/장애물 압박]
63+ for (const auto & barrierEntity : barrierStorage.getEntities ()) {
64+ const auto & barrier = barrierStorage.get (barrierEntity);
65+ const auto & vertices = barrier.geometry .vertices ;
66+ if (vertices.size () < 2 ) continue ;
67+
68+ for (size_t i = 0 ; i < vertices.size () - 1 ; ++i) {
69+ float distToWall = distancePointToSegment (pos.value , vertices[i], vertices[i + 1 ]);
70+ if (distToWall < agent.radius ) {
71+ currentForce += (agent.radius - distToWall);
72+ }
73+ }
74+ if (barrier.geometry .closed ) {
75+ float distToWall = distancePointToSegment (pos.value , vertices.back (), vertices.front ());
76+ if (distToWall < agent.radius ) {
77+ currentForce += (agent.radius - distToWall);
78+ }
79+ }
80+ }
81+
82+ compression.force = currentForce;
83+
84+ // [고위험 상태 업데이트]
85+ const float FORCE_THRESHOLD = 0 .5f ;
86+ if (compression.force > FORCE_THRESHOLD ) {
87+ compression.exposure += dt;
88+ }
89+ else {
90+ compression.exposure = std::max (0 .0f , compression.exposure - dt * 0 .5f );
91+ }
92+
93+ const float EXPOSURE_THRESHOLD = 2 .0f ;
94+ compression.isCritical = (compression.force > FORCE_THRESHOLD ) &&
95+ (compression.exposure > EXPOSURE_THRESHOLD );
96+ }
97+ }
98+
99+ } // namespace safecrowd::domain
0 commit comments