TazGraph Project v0.1.0
Loading...
Searching...
No Matches
GECSEntityTypesLink.h
1#pragma once
2
3#include "./GECSEntityTypes.h"
4
5class Link : public LinkEntity {
6private:
7
8
9
10public:
11
12 Link(Manager& mManager) : LinkEntity(mManager) {
13 }
14
15 Link(Manager& mManager, EntityID mfromId, EntityID mtoId)
16 : LinkEntity(mManager, mfromId, mtoId)
17 {
18 }
19
20 Link(
21 Manager& mManager,
22 EntityID mfromId, EntityID mtoId,
23 EntityID m_fromPort, EntityID m_toPort,
24 int m_fromSlot, int m_toSlot
25 )
26 : LinkEntity(mManager,
27 mfromId, mtoId,
28 m_fromPort, m_toPort,
29 m_fromSlot, m_toSlot)
30 {
31
32 }
33
34 Link(
35 Manager& mManager,
36 glm::vec3 mfromPos, glm::vec3 mtoPos
37 )
38 : LinkEntity(mManager,
39 mfromPos, mtoPos)
40 {
41
42 }
43
44 Link(
45 Manager& mManager,
46 ConnectionType m_type
47 )
48 : LinkEntity(mManager)
49 {
50 type = m_type;
51 }
52
53 void onCreation() override {
54 // Now getId() is valid
55 if (type != ConnectionType::DIRECT_POSITIONS) {
56 dynamic_cast<NodeEntity*>(manager.getEntityFromId(fromId))->addOutLink(getId());
57 dynamic_cast<NodeEntity*>(manager.getEntityFromId(toId))->addInLink(getId());
58 }
59 }
60
61 void addToGroup(Group mGroup) override {
62 Entity::addToGroup(mGroup);
63 manager.AddLinkToGroup(this, mGroup);
64 }
65
66 void removeGroup(Group mGroup) override {
67 Entity::removeGroup(mGroup);
68 manager.aboutTo_updateActiveEntities();
69 }
70
71 virtual ~Link() {
72
73 }
74
75 void update(float deltaTime) override
76 {
77 Entity::update(deltaTime);
78
79
80 }
81
82 void cellUpdate() override {
83 // if cell(or position) of fromNode or cell(or position) of toNode is different than
84 // the saved cells in ownerCells then update it
85 if (!ownerCells.empty()) {
86 auto level = manager.grid->getGridLevel();
87 NodeEntity* fromNode = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getFromNode()));
88 NodeEntity* toNode = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getToNode()));
89
90 const auto& fromCell = manager.grid->getCell(*fromNode, level);
91 const auto& toCell = manager.grid->getCell(*toNode, level);
92
93 const auto& ownerFront = ownerCells.front();
94 const auto& ownerBack = ownerCells.back();
95
96 if (fromCell != ownerFront
97 || toCell != ownerBack)
98 {
99 removeFromCells();
100
101 manager.grid->addLink(this, manager.grid->getGridLevel());
102 }
103 }
104
105 }
106
107 void updateArrowHeads() override {
108 if (children.contains(LinkChildren_ToString(ARROWHEAD))) {
109 NodeEntity* fromNode = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getFromNode()));
110 NodeEntity* toNode = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getToNode()));
111 if (!fromNode || !toNode)
112 return;
113
114 Entity* fromPortEntity = nullptr;
115 Entity* toPortEntity = nullptr;
116
117 if (fromNode->children.contains(fromPort))
118 fromPortEntity = getManager()->getEntityFromId(fromNode->children[fromPort]);
119 if (toNode->children.contains(toPort))
120 toPortEntity = getManager()->getEntityFromId(toNode->children[toPort]);
121
122 if (!fromPortEntity || !toPortEntity)
123 return;
124
125 // --- Get slot entities safely ---
126 if (fromSlotIndex >= fromPortEntity->children.size() ||
127 toSlotIndex >= toPortEntity->children.size())
128 return;
129
130 Entity* fromSlotEntity = getManager()->getEntityFromId(fromPortEntity->children[fromSlotIndex]);
131 Entity* toSlotEntity = getManager()->getEntityFromId(toPortEntity->children[toSlotIndex]);
132 if (!fromSlotEntity || !toSlotEntity)
133 return;
134
135 // --- Retrieve positions ---
136 auto& fromTransform = fromSlotEntity->GetComponent<TransformComponent>();
137 auto& toTransform = toSlotEntity->GetComponent<TransformComponent>();
138
139 glm::vec3 fromConnectionPoint = fromTransform.getPosition();
140 glm::vec3 toConnectionPoint = toTransform.getPosition();
141
142 glm::vec3 direction = toConnectionPoint - fromConnectionPoint;
143 float distance = glm::length(direction);
144
145 if (distance < 0.001f) {
146 // Points are too close, skip update to avoid division by zero
147 return;
148 }
149
150 glm::vec3 unitDirection = direction / distance;
151
152 // Calculate arrowhead position with offset (same as in addArrowHead)
153 float offset = 10.0f;
154 glm::vec3 arrowHeadPos = toConnectionPoint - unitDirection * offset;
155
156 // Calculate rotation based on the direction between slots
157 float angleRadians = -atan2(direction.y, direction.x);
158
159 // Update the arrowhead position and rotation
160 getManager()->getEntityFromId(children[LinkChildren_ToString(ARROWHEAD)])->GetComponent<TransformComponent>().position = arrowHeadPos;
161 getManager()->getEntityFromId(children[LinkChildren_ToString(ARROWHEAD)])->GetComponent<TransformComponent>().setRotation(glm::vec3(0.0f, 0.0f, angleRadians + glm::half_pi<float>()));
162
163 // Update the arrowhead entity
164 getManager()->getEntityFromId(children[LinkChildren_ToString(ARROWHEAD)])->update(0.0f);
165 }
166 }
167
168 void setConnectionType(ConnectionType setType) override {
169 type = setType;
170 }
171
172 // ========== HELPER: Find next available slot index ==========
173
174 int findNextAvailableSlotIndex(Entity* portEntity) {
175 if (!portEntity || portEntity->children.empty()) {
176 return 0;
177 }
178
179 // Find the highest existing index
180 int maxIndex = -1;
181 for (const auto& [slotIndex, entityId] : portEntity->children) {
182 if (std::get<int>(slotIndex) > maxIndex) {
183 maxIndex = std::get<int>(slotIndex);
184 }
185 }
186
187 return maxIndex + 1;
188 }
189
190 // ========== HELPER: Remove slot and reindex remaining slots ==========
191
192 void removeSlotAndReindex(Entity* portEntity, int slotIndexToRemove) {
193 if (!portEntity) return;
194
195 // Find and remove the slot
196 auto it = portEntity->children.find(slotIndexToRemove);
197 if (it == portEntity->children.end()) {
198 return; // Slot doesn't exist
199 }
200
201 // Delete the slot entity
202 Entity* slotEntity = getManager()->getEntityFromId(it->second);
203 if (slotEntity) {
204 slotEntity->destroy();
205 }
206
207 // Remove from map
208 portEntity->children.erase(it);
209
210 // Reindex all slots with higher indices
211 std::map<EntityID, EntityID> updatedChildren;
212
213 for (auto& [slotIndex, entityId] : portEntity->children) {
214 Entity* slot = portEntity->getManager()->getEntityFromId(entityId);
215
216 if (std::get<int>(slotIndex) > slotIndexToRemove) {
217 // Shift index down by 1
218 int newIndex = std::get<int>(slotIndex) - 1;
219
220 if (slot->hasComponent<PortSlotComponent>()) {
221 slot->GetComponent<PortSlotComponent>().index = newIndex;
222 }
223
224 updatedChildren[newIndex] = entityId;
225 }
226 else {
227 // Keep same index
228 updatedChildren[slotIndex] = entityId;
229 }
230 }
231
232 portEntity->children = std::move(updatedChildren);
233 }
234
235 int assignSlotIndex(NodeEntity* node, EntityID newPort, EntityID oldPort, int oldSlotIndex) {
236 // If port changed, remove link from old port's slots
237 if (std::holds_alternative<std::string>(oldPort) &&
238 !std::get<std::string>(oldPort).empty() &&
239 oldPort != newPort) {
240
241 Entity* oldPortEntity = getManager()->getEntityFromId(node->children[oldPort]);
242
243 if (oldPortEntity && oldPortEntity->hasComponent<PortComponent>()) {
244 removeSlotAndReindex(oldPortEntity, oldSlotIndex);
245 updateLinksSlotIndices(node, oldPort, oldSlotIndex, true);
246 updateLinksSlotIndices(node, oldPort, oldSlotIndex, false);
247 }
248 }
249
250 // If port changed or first assignment, create new slot
251 if (oldPort != newPort) {
252 if (std::get<std::string>(newPort).empty() || !node->children.contains(newPort)) {
253 return -1;
254 }
255
256 Entity* newPortEntity = getManager()->getEntityFromId(node->children[newPort]);
257 if (!newPortEntity || !newPortEntity->hasComponent<PortComponent>()) {
258 return -1;
259 }
260
261 // Find the next available slot index
262 int newSlotIndex = findNextAvailableSlotIndex(newPortEntity);
263
264 // Create new slot
265 auto& newSlot = node->getManager()->addEntityFromParent<Empty>(newPortEntity);
266 newSlot.addToGroup(Manager::groupPortSlots);
267
268 TransformComponent& portTransform = newPortEntity->GetComponent<TransformComponent>();
269 newSlot.addComponent<TransformComponent>(
270 portTransform.position,
271 glm::vec3(1),
272 1.0f
273 );
274
275 newSlot.addComponent<Rectangle_w_Color>();
276 newSlot.GetComponent<Rectangle_w_Color>().setColor(TazColor(0, 250, 0, 255));
277 newSlot.setParentEntity(newPortEntity);
278
279 // Add to port with explicit index
280 newPortEntity->children[newSlotIndex] = newSlot.getId();
281
282 auto& slotComp = newSlot.addComponent<PortSlotComponent>();
283 slotComp.index = newSlotIndex;
284
285 return newSlotIndex;
286 }
287
288 return oldSlotIndex;
289 }
290
291 void updateConnection() override {
292
293 if (type == ConnectionType::NODE_TO_NODE) {
294 fromPort = -1;
295 toPort = -1;
296 }
297 else if (type == ConnectionType::PORT_TO_PORT) {
298 NodeEntity* from = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getFromNode()));
299 NodeEntity* to = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getToNode()));
300
301 TransformComponent* fromTR = &from->GetComponent<TransformComponent>();
302 TransformComponent* toTR = &to->GetComponent<TransformComponent>();
303
304 EntityID newFromPort = getBestPortForConnection(fromTR->getPosition(), toTR->getPosition());
305 EntityID newToPort = getBestPortForConnection(toTR->getPosition(), fromTR->getPosition());
306
307 fromSlotIndex = assignSlotIndex(from, newFromPort, fromPort, fromSlotIndex);
308 toSlotIndex = assignSlotIndex(to, newToPort, toPort, toSlotIndex);
309
310 fromPort = newFromPort;
311 toPort = newToPort;
312 }
313 else if (type == ConnectionType::DIRECT_POSITIONS) {
314 // it is as is
315 }
316 else if (type == ConnectionType::GHOST_PORT_TO_PORT) {
317 // get adjacen path links ports and slots
318 InnerLink* iL = &GetComponent<InnerLink>();
319
320 LinkEntity* iL_1 = dynamic_cast<LinkEntity*>(manager.getEntityFromId(iL->first_pathLink));
321 LinkEntity* iL_2 = dynamic_cast<LinkEntity*>(manager.getEntityFromId(iL->second_pathLink));
322
323 fromPort = iL_1->toPort;
324 toPort = iL_2->fromPort;
325
326 fromSlotIndex = iL_1->toSlotIndex;
327 toSlotIndex = iL_2->fromSlotIndex;
328 }
329 else {
330 TAZ_ERROR("type doesn't exist for link");
331 }
332
333 updateConnectionPositions();
334 }
335
336 void updatePortSlots() override {
337 NodeEntity* from = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getFromNode()));
338 NodeEntity* to = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getToNode()));
339
340 Entity* from_port = getManager()->getEntityFromId(from->children[fromPort]);
341 from_port->update(0.0f);
342 Entity* to_port = getManager()->getEntityFromId(to->children[toPort]);
343 to_port->update(0.0f);
344
345
346 Entity* from_port_slot = getManager()->getEntityFromId(from_port->children[fromSlotIndex]);
347 from_port_slot->update(0.0f);
348 Entity* to_port_slot = getManager()->getEntityFromId(to_port->children[toSlotIndex]);
349 to_port_slot->update(0.0f);
350 }
351
352
353 void updateConnectionPositions() {
354 if (type == ConnectionType::NODE_TO_NODE) {
355 NodeEntity* from = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getFromNode()));
356 NodeEntity* to = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getToNode()));
357
358 fromPos = from->
359 GetComponent<TransformComponent>()
360 .getPosition();
361 toPos = to->
362 GetComponent<TransformComponent>()
363 .getPosition();
364 }
365 else if (
366 type == ConnectionType::PORT_TO_PORT ||
367 type == ConnectionType::GHOST_PORT_TO_PORT
368 ) {
369 NodeEntity* from = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getFromNode()));
370 NodeEntity* to = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getToNode()));
371
372 Entity* fromPortEntity = getManager()->getEntityFromId(from->children[fromPort]);
373 Entity* toPortEntity = getManager()->getEntityFromId(to->children[toPort]);
374
375 if (!fromPortEntity || !toPortEntity ||
376 fromSlotIndex >= fromPortEntity->children.size() ||
377 toSlotIndex >= toPortEntity->children.size()) {
378 TAZ_ERROR(type == ConnectionType::PORT_TO_PORT ?
379 "updateConnectionPositions port-port"
380 : "updateConnectionPositions Ghost port-port");
381 }
382
383 fromPos = getManager()->getEntityFromId(fromPortEntity->children[fromSlotIndex])
384 ->GetComponent<TransformComponent>().getPosition();
385 toPos = getManager()->getEntityFromId(toPortEntity->children[toSlotIndex])
386 ->GetComponent<TransformComponent>().getPosition();
387 }
388 else if (type == ConnectionType::DIRECT_POSITIONS) {
389 // it is as is
390 }
391 }
392
393 void addArrowHead() override {
394 NodeEntity* fromNode = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getFromNode()));
395 NodeEntity* toNode = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getToNode()));
396
397
398 Entity* fromPortEntity = getManager()->getEntityFromId(fromNode->children[fromPort]);
399 Entity* toPortEntity = getManager()->getEntityFromId(toNode->children[toPort]);
400
401 // Check if slot indices are valid
402 if ((fromSlotIndex >= fromPortEntity->children.size()) ||
403 (toSlotIndex >= toPortEntity->children.size())) {
404 TAZ_ERROR("Invalid slot indices!");
405 return;
406 }
407
408 // Get the actual connection points from the port slots
409 glm::vec3 fromConnectionPoint = getManager()->getEntityFromId(fromPortEntity->children[fromSlotIndex])->GetComponent<TransformComponent>().getPosition();
410 glm::vec3 toConnectionPoint = getManager()->getEntityFromId(toPortEntity->children[toSlotIndex])->GetComponent<TransformComponent>().getPosition();
411
412 glm::vec3 direction = toConnectionPoint - fromConnectionPoint;
413 glm::vec3 unitDirection = glm::normalize(direction);
414
415 float linkWidth = this->GetComponent<Line_w_Color>().width;
416
417 float arrowheadWidth = linkWidth * 2.0f; // Arrowhead width = 2x link width
418 float arrowheadHeight = arrowheadWidth * 2.0f; // Height = 2x width for good proportion
419
420 // Calculate arrowhead position (slightly offset from the target slot)
421 float offset = arrowheadHeight * 0.6f;
422 glm::vec3 arrowHeadPos = toConnectionPoint - unitDirection * offset;
423
424 auto& temp_arrowHead = getManager()->addEntityFromParent<Empty>(this, LinkChildren_ToString(ARROWHEAD));
425
426 // Calculate rotation based on the direction between slots
427 float angleRadians = -atan2(direction.y, direction.x);
428 glm::vec3 arrowSize(arrowheadWidth, arrowheadHeight, 0.0f);
429
430 temp_arrowHead.addComponent<TransformComponent>(arrowHeadPos, arrowSize, 1);
431 temp_arrowHead.addComponent<Triangle_w_Color>();
432 temp_arrowHead.GetComponent<Triangle_w_Color>().color = TazColor(0, 0, 0, 255);
433
434 // Rotate the arrowhead to point in the direction of the connection
435 temp_arrowHead.GetComponent<TransformComponent>().setRotation(glm::vec3(0.0f, 0.0f, angleRadians + glm::half_pi<float>()));
436
437 temp_arrowHead.addToGroup(Manager::groupArrowHeads_0);
438 temp_arrowHead.setParentEntity(this);
439 children[LinkChildren_ToString(ARROWHEAD)] = temp_arrowHead.getId();
440 }
441
442 void removeArrowHead() override {
443 if (children.contains(LinkChildren_ToString(ARROWHEAD))) {
444 getManager()->getEntityFromId(children[LinkChildren_ToString(ARROWHEAD)])->removeFromCell();
445 getManager()->getEntityFromId(children[LinkChildren_ToString(ARROWHEAD)])->destroy();
446 children.erase(LinkChildren_ToString(ARROWHEAD));
447 }
448 }
449
450 EntityID getBestPortForConnection(const glm::vec3& fromPos, const glm::vec3& toPos) {
451 // Simple logic to determine the port based on relative position
452 float deltaX = toPos.x - fromPos.x;
453 float deltaY = toPos.y - fromPos.y;
454
455 if (abs(deltaX) > abs(deltaY)) { // Horizontal distance is greater
456 return deltaX > 0 ? NodePorts_ToString(RIGHT) : NodePorts_ToString(LEFT);
457 }
458 else { // Vertical distance is greater
459 return deltaY > 0 ? NodePorts_ToString(BOTTOM) : NodePorts_ToString(TOP);
460 }
461 }
462
463
464 void updateLinksSlotIndices(NodeEntity* node, EntityID portIndex, int removedSlotIndex, bool isFrom) {
465
466 // reassign slots to links
467 if (isFrom) {
468 for (auto& linkEntityId : node->getOutLinks()) {
469 auto* linkEntity = dynamic_cast<LinkEntity*>(manager.getEntityFromId(linkEntityId));
470
471 if (EntityIDUtils::areEqual(linkEntity->fromPort, portIndex) &&
472 linkEntity->fromSlotIndex > removedSlotIndex &&
473 linkEntity->type != ConnectionType::GHOST_PORT_TO_PORT) {
474 linkEntity->fromSlotIndex--;
475 }
476 }
477 }
478 else {
479 for (auto& linkEntityId : node->getInLinks()) {
480 auto* linkEntity = dynamic_cast<LinkEntity*>(manager.getEntityFromId(linkEntityId));
481
482 if (EntityIDUtils::areEqual(linkEntity->toPort, portIndex) &&
483 linkEntity->toSlotIndex > removedSlotIndex &&
484 linkEntity->type != ConnectionType::GHOST_PORT_TO_PORT) {
485 linkEntity->toSlotIndex--;
486 }
487 }
488 }
489 }
490
491 void removeSlotFromNode(NodeEntity* node, EntityID port, int slotIndex, bool isFrom) {
492 if (std::holds_alternative<int>(port) || slotIndex == -1) return;
493
494 Entity* portEntity = getManager()->getEntityFromId(node->children[port]);
495 if (portEntity && portEntity->hasComponent<PortComponent>()) {
496 if (removeSlot(portEntity, slotIndex)) {
497 updateLinksSlotIndices(node, port, slotIndex, isFrom);
498 }
499 }
500 }
501
502 void imgui_print() override {
503
504 NodeEntity* fromNode = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getFromNode()));
505 NodeEntity* toNode = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getToNode()));
506
507
508 glm::vec2 fromNodePosition = fromNode->GetComponent<TransformComponent>().getPosition();
509 glm::vec2 toNodePosition = toNode->GetComponent<TransformComponent>().getPosition();
510
511 ImGui::Text("From Node TazPosition: (%.2f, %.2f)", fromNodePosition.x, fromNodePosition.y);
512 ImGui::Text("To Node TazPosition: (%.2f, %.2f)", toNodePosition.x, toNodePosition.y);
513
514 ImGui::Text("Bounding boxes of intercepted cells:");
515
516 for (auto cell : ownerCells) {
517 ImGui::Text("- %.2f , %.2f , %.2f", cell->boundingBox_origin.x, cell->boundingBox_origin.y, cell->boundingBox_origin.z);
518 }
519 }
520
521 void imgui_display() override {
522 ImGui::Text("Display Info Here Link");
523 }
524
525 void destroy() {
526 Entity::destroy();
527
528 NodeEntity* from = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getFromNode()));
529 NodeEntity* to = dynamic_cast<NodeEntity*>(manager.getEntityFromId(getToNode()));
530
531
532 if (from) {
533 from->removeOutLink(getId());
534 removeSlotFromNode(from, fromPort, fromSlotIndex, true);
535 }
536 if (to) {
537 to->removeInLink(getId());
538 removeSlotFromNode(to, toPort, toSlotIndex, false);
539 }
540
541 removeArrowHead();
542 setConnectionType(LinkEntity::ConnectionType::NODE_TO_NODE);
543 updateConnection();
544
545 manager.aboutTo_updateActiveEntities();//? cant have it at destroy in baseclass
546 }
547
548 std::vector<EntityID> getSlots(Entity* portEntity) const {
549 std::vector<EntityID> slots;
550 for (const auto& [id, child] : portEntity->children) {
551 auto* ent = portEntity->getManager()->getEntityFromId(child);
552
553 if (ent->hasComponent<PortSlotComponent>()) {
554 slots.push_back(child);
555 }
556 }
557 return slots;
558 }
559
560 // Remove slot by index
561 bool removeSlot(Entity* portEntity, int index) {
562 for (auto it = portEntity->children.begin(); it != portEntity->children.end(); ++it) {
563 EntityID child = it->second;
564 auto* childEnt = portEntity->getManager()->getEntityFromId(child);
565
566 if (childEnt->hasComponent<PortSlotComponent>() &&
567 childEnt->GetComponent<PortSlotComponent>().index == index) {
568 childEnt->destroy();
569 portEntity->children.erase(it);
570 return true;
571 }
572 }
573 return false;
574 }
575
576 // Get number of slots
577 size_t getSlotCount(Entity* portEntity) const {
578 size_t count = 0;
579 for (const auto& [id, child] : portEntity->children) {
580 auto* childEnt = portEntity->getManager()->getEntityFromId(child);
581
582 if (childEnt->hasComponent<PortSlotComponent>()) {
583 count++;
584 }
585 }
586 return count;
587 }
588
589};
Definition GECSEntityTypesEmpty.h:5
Definition GECS.h:224
std::map< EntityID, EntityID > children
child_index(id) -> real_entity_id
Definition GECS.h:248
Manager & manager
the object doesnt move, so we can have pointer
Definition GECS.h:241
Definition GECSEntity.h:108
EntityID fromId
When Node_to_Node.
Definition GECSEntity.h:111
glm::vec3 fromPos
When DirectPosition.
Definition GECSEntity.h:124
EntityID fromPort
When Port_to_Port.
Definition GECSEntity.h:128
Definition GECSManager.h:20
Definition GECSEntity.h:40
Definition PortComponent.h:8
Definition PortSlotComponent.h:7
Definition Rectangle_w_Color.h:6
Definition TransformComponent.h:7
Definition Triangle_w_Color.h:6
Definition Vertex.h:44