Ember
Loading...
Searching...
No Matches
Node.cpp
Go to the documentation of this file.
1#include "Core/Node.h"
2#include "Utils/Logger.h"
3#include <algorithm>
4#include <numeric>
5
6namespace EmberCore {
7
8// Static member initialization
9size_t Node::next_id_ = 1;
10
11// Constructor
15
16// Destructor
18 // Unique_ptr automatically handles cleanup of children
19 for (auto &child : children_) {
20 if (child) {
21 child->parent_ = nullptr;
22 }
23 }
24}
25
26// Move constructor
27Node::Node(Node &&other) noexcept
28 : name_(std::move(other.name_)), type_(other.type_), status_(other.status_), id_(other.id_),
29 children_(std::move(other.children_)), parent_(other.parent_), position_(other.position_),
30 visual_state_(other.visual_state_), children_visible_(other.children_visible_),
31 children_width_(other.children_width_), custom_attributes_(std::move(other.custom_attributes_)) {
32 other.parent_ = nullptr;
33 // Update parent pointers for moved children
34 for (auto &child : children_) {
35 if (child) {
36 child->parent_ = this;
37 }
38 }
39}
40
41// Move assignment operator
42Node &Node::operator=(Node &&other) noexcept {
43 if (this != &other) {
44 // Clean up current children
45 for (auto &child : children_) {
46 if (child) {
47 child->parent_ = nullptr;
48 }
49 }
50
51 // Move from other
52 name_ = std::move(other.name_);
53 type_ = other.type_;
54 status_ = other.status_;
55 id_ = other.id_;
56 children_ = std::move(other.children_);
57 parent_ = other.parent_;
58 position_ = other.position_;
59 visual_state_ = other.visual_state_;
60 children_visible_ = other.children_visible_;
61 children_width_ = other.children_width_;
62 custom_attributes_ = std::move(other.custom_attributes_);
63
64 other.parent_ = nullptr;
65
66 // Update parent pointers for moved children
67 for (auto &child : children_) {
68 if (child) {
69 child->parent_ = this;
70 }
71 }
72 }
73 return *this;
74}
75
76// Core node operations
77void Node::AddChild(std::unique_ptr<Node> child) {
78 if (!child) {
79 LOG_ERROR("Node", "Attempting to add null child to node '" + name_ + "'");
80 return;
81 }
82
83 if (child.get() == this) {
84 LOG_ERROR("Node", "Attempting to add node '" + name_ + "' as child of itself");
85 return;
86 }
87
88 // Check for cycles
89 Node *ancestor = parent_;
90 while (ancestor) {
91 if (ancestor == child.get()) {
92 LOG_ERROR("Node", "Adding child '" + child->name_ + "' to node '" + name_ + "' would create a cycle");
93 return;
94 }
95 ancestor = ancestor->parent_;
96 }
97
98 child->parent_ = this;
99 children_.push_back(std::move(child));
100 OnChildAdded(children_.back().get());
101}
102
103void Node::RemoveChild(size_t index) {
104 if (index >= children_.size()) {
105 LOG_ERROR("Node", "Child index " + std::to_string(index) + " out of bounds for node '" + name_ + "' (has " +
106 std::to_string(children_.size()) + " children)");
107 return;
108 }
109
110 Node *child = children_[index].get();
111 OnChildRemoved(child);
112 child->parent_ = nullptr;
113 children_.erase(children_.begin() + index);
114}
115
117 size_t index = FindChildIndex(child);
118 if (index != SIZE_MAX) {
119 RemoveChild(index);
120 }
121}
122
123std::unique_ptr<Node> Node::DetachChild(size_t index) {
124 if (index >= children_.size()) {
125 LOG_ERROR("Node", "Child index " + std::to_string(index) + " out of bounds for node '" + name_ + "'");
126 return nullptr;
127 }
128
129 std::unique_ptr<Node> child = std::move(children_[index]);
130 children_.erase(children_.begin() + index);
131
132 if (child) {
133 OnChildRemoved(child.get());
134 child->parent_ = nullptr;
135 }
136
137 return child;
138}
139
140std::unique_ptr<Node> Node::DetachChild(Node *child) {
141 size_t index = FindChildIndex(child);
142 if (index != SIZE_MAX) {
143 return DetachChild(index);
144 }
145 return nullptr;
146}
147
148// Deep copy functionality
149std::unique_ptr<Node> Node::DeepCopy() const {
150 auto copy = CreateCopy();
151
152 // Copy children recursively
153 for (const auto &child : children_) {
154 if (child) {
155 copy->AddChild(child->DeepCopy());
156 }
157 }
158
159 return copy;
160}
161
162// Tree traversal and queries
164 auto it = std::find_if(children_.begin(), children_.end(),
165 [&name](const std::unique_ptr<Node> &child) { return child && child->name_ == name; });
166 return it != children_.end() ? it->get() : nullptr;
167}
168
169Node *Node::FindChildById(size_t id) const {
170 auto it = std::find_if(children_.begin(), children_.end(),
171 [id](const std::unique_ptr<Node> &child) { return child && child->id_ == id; });
172 return it != children_.end() ? it->get() : nullptr;
173}
174
175Node *Node::GetChild(size_t index) const {
176 if (index >= children_.size()) {
177 return nullptr;
178 }
179 return children_[index].get();
180}
181
182// Layout and rendering support
183int Node::CalculateSubtreeWidth(int node_width, int horizontal_spacing) const {
184 if (children_.empty() || !children_visible_) {
185 return node_width;
186 }
187
188 int total_width = 0;
189 for (const auto &child : children_) {
190 if (child) {
191 total_width += child->CalculateSubtreeWidth(node_width, horizontal_spacing);
192 total_width += horizontal_spacing;
193 }
194 }
195
196 // Remove the last spacing
197 if (!children_.empty()) {
198 total_width -= horizontal_spacing;
199 }
200
201 return std::max(node_width, total_width);
202}
203
204// Tree validation and integrity
205bool Node::IsValid() const {
206 // Check for basic validity
207 if (name_.empty()) {
208 return false;
209 }
210
211 // Check for cycles
212 if (HasCycles()) {
213 return false;
214 }
215
216 // Validate children recursively
217 for (const auto &child : children_) {
218 if (!child || !child->IsValid()) {
219 return false;
220 }
221
222 // Check parent-child relationship
223 if (child->parent_ != this) {
224 return false;
225 }
226 }
227
228 return true;
229}
230
231bool Node::HasCycles() const {
232 std::vector<const Node *> visited;
233 return HasCyclesHelper(visited);
234}
235
236size_t Node::GetDepth() const {
237 size_t max_depth = 0;
238 for (const auto &child : children_) {
239 if (child) {
240 max_depth = std::max(max_depth, child->GetDepth());
241 }
242 }
243 return max_depth + 1;
244}
245
247 return 1 + std::accumulate(children_.begin(), children_.end(), size_t(0),
248 [](size_t sum, const std::unique_ptr<Node> &child) {
249 return sum + (child ? child->GetSubtreeNodeCount() : 0);
250 });
251}
252
253// Visitor pattern support
254void Node::Accept(std::function<void(Node *)> visitor) {
255 visitor(this);
256 for (auto &child : children_) {
257 if (child) {
258 child->Accept(visitor);
259 }
260 }
261}
262
263void Node::Accept(std::function<void(const Node *)> visitor) const {
264 visitor(this);
265 for (const auto &child : children_) {
266 if (child) {
267 // Explicitly call the const version to avoid ambiguity
268 static_cast<const Node *>(child.get())->Accept(visitor);
269 }
270 }
271}
272
273// Utility functions
275 switch (type_) {
276 case Type::Action:
277 return "Action";
278 case Type::Control:
279 return "Control";
280 case Type::Condition:
281 return "Condition";
282 case Type::Decorator:
283 return "Decorator";
284 case Type::None:
285 default:
286 return "None";
287 }
288}
289
291 switch (status_) {
292 case Status::Running:
293 return "Running";
294 case Status::Success:
295 return "Success";
296 case Status::Failure:
297 return "Failure";
298 case Status::Idle:
299 default:
300 return "Idle";
301 }
302}
303
304std::vector<Node *> Node::GetAllChildren() {
305 std::vector<Node *> result;
306 result.reserve(children_.size());
307 for (const auto &child : children_) {
308 if (child) {
309 result.push_back(child.get());
310 }
311 }
312 return result;
313}
314
315std::vector<const Node *> Node::GetAllChildren() const {
316 std::vector<const Node *> result;
317 result.reserve(children_.size());
318 for (const auto &child : children_) {
319 if (child) {
320 result.push_back(child.get());
321 }
322 }
323 return result;
324}
325
326// Debug and serialization support
328 return "Node[" + std::to_string(id_) + "]: '" + name_ + "' (" + GetTypeString() + ", " + GetStatusString() +
329 ") - " + std::to_string(children_.size()) + " children";
330}
331
332void Node::PrintTree(int indent) const {
333 EmberCore::String indentStr(indent * 2, ' ');
334 LOG_INFO("Node", indentStr + ToString());
335
336 for (const auto &child : children_) {
337 if (child) {
338 child->PrintTree(indent + 1);
339 }
340 }
341}
342
343// Protected virtual methods
344std::unique_ptr<Node> Node::CreateCopy() const {
345 auto copy = std::make_unique<Node>(name_, type_);
346 copy->status_ = status_;
347 copy->position_ = position_;
348 copy->visual_state_ = visual_state_;
349 copy->children_visible_ = children_visible_;
350 copy->children_width_ = children_width_;
351
352 // FIXED: Let constructor auto-assign unique ID instead of copying
353 // Each copy now gets its own unique ID from next_id_++
354 // This prevents duplicate IDs when SubTree references are expanded
355
356 // Copy custom attributes (including ID attribute for serialization)
357 copy->custom_attributes_ = custom_attributes_;
358
359 return copy;
360}
361
363 // Default implementation - can be overridden by derived classes
364 LOG_TRACE("Node", "Child '" + (child ? child->name_ : "null") + "' added to node '" + name_ + "'");
365}
366
368 // Default implementation - can be overridden by derived classes
369 LOG_TRACE("Node", "Child '" + (child ? child->name_ : "null") + "' removed from node '" + name_ + "'");
370}
371
372// Private helper methods
373bool Node::HasCyclesHelper(std::vector<const Node *> &visited) const {
374 // Check if this node is already in the visited list (cycle detected)
375 if (std::find(visited.begin(), visited.end(), this) != visited.end()) {
376 return true;
377 }
378
379 visited.push_back(this);
380
381 // Check children recursively
382 bool has_cycle = std::any_of(children_.begin(), children_.end(), [&visited](const std::unique_ptr<Node> &child) {
383 return child && child->HasCyclesHelper(visited);
384 });
385
386 visited.pop_back();
387 return has_cycle;
388}
389
390size_t Node::FindChildIndex(Node *child) const {
391 for (size_t i = 0; i < children_.size(); ++i) {
392 if (children_[i].get() == child) {
393 return i;
394 }
395 }
396 return SIZE_MAX;
397}
398
399// NodeFactory implementation
400std::unique_ptr<Node> NodeFactory::CreateActionNode(const EmberCore::String &name) {
401 return std::make_unique<Node>(name, Node::Type::Action);
402}
403
404std::unique_ptr<Node> NodeFactory::CreateControlNode(const EmberCore::String &name) {
405 return std::make_unique<Node>(name, Node::Type::Control);
406}
407
408std::unique_ptr<Node> NodeFactory::CreateConditionNode(const EmberCore::String &name) {
409 return std::make_unique<Node>(name, Node::Type::Condition);
410}
411
412std::unique_ptr<Node> NodeFactory::CreateDecoratorNode(const EmberCore::String &name) {
413 return std::make_unique<Node>(name, Node::Type::Decorator);
414}
415
416std::unique_ptr<Node> NodeFactory::CreateNode(const EmberCore::String &name, Node::Type type) {
417 return std::make_unique<Node>(name, type);
418}
419
420std::unique_ptr<Node> NodeFactory::CreateSampleTree() {
421 auto root = CreateControlNode("Root Selector");
422
423 // Create a sample behavior tree structure
424 auto patrol_sequence = CreateControlNode("Patrol Sequence");
425 patrol_sequence->AddChild(CreateConditionNode("Has Patrol Route"));
426 patrol_sequence->AddChild(CreateActionNode("Move to Next Waypoint"));
427 patrol_sequence->AddChild(CreateActionNode("Wait at Waypoint"));
428
429 auto combat_sequence = CreateControlNode("Combat Sequence");
430 combat_sequence->AddChild(CreateConditionNode("Enemy in Range"));
431 combat_sequence->AddChild(CreateActionNode("Attack Enemy"));
432
433 auto idle_action = CreateActionNode("Idle");
434
435 root->AddChild(std::move(combat_sequence));
436 root->AddChild(std::move(patrol_sequence));
437 root->AddChild(std::move(idle_action));
438
439 return root;
440}
441
442// ====== CUSTOM ATTRIBUTES IMPLEMENTATION ======
443
445 if (name.empty()) {
446 return; // Don't allow empty attribute names
447 }
448 custom_attributes_[name] = value;
449}
450
452 auto it = custom_attributes_.find(name);
453 return (it != custom_attributes_.end()) ? it->second : default_value;
454}
455
456bool Node::HasAttribute(const EmberCore::String &name) const {
457 return custom_attributes_.find(name) != custom_attributes_.end();
458}
459
461
462const std::map<EmberCore::String, EmberCore::String> &Node::GetAllAttributes() const { return custom_attributes_; }
463
464size_t Node::GetAttributeCount() const { return custom_attributes_.size(); }
465
466} // namespace EmberCore
#define LOG_ERROR(category, message)
Definition Logger.h:116
#define LOG_TRACE(category, message)
Definition Logger.h:113
#define LOG_INFO(category, message)
Definition Logger.h:114
static std::unique_ptr< Node > CreateConditionNode(const String &name)
Definition Node.cpp:408
static std::unique_ptr< Node > CreateSampleTree()
Definition Node.cpp:420
static std::unique_ptr< Node > CreateNode(const String &name, Node::Type type)
Definition Node.cpp:416
static std::unique_ptr< Node > CreateActionNode(const String &name)
Definition Node.cpp:400
static std::unique_ptr< Node > CreateControlNode(const String &name)
Definition Node.cpp:404
static std::unique_ptr< Node > CreateDecoratorNode(const String &name)
Definition Node.cpp:412
virtual std::unique_ptr< Node > CreateCopy() const
Definition Node.cpp:344
bool HasAttribute(const String &name) const
Definition Node.cpp:456
Node(const String &name, Type type=Type::None)
Definition Node.cpp:12
String GetStatusString() const
Definition Node.cpp:290
virtual ~Node()
Definition Node.cpp:17
Status
Node status for runtime execution tracking.
Definition Node.h:37
std::map< String, String > custom_attributes_
Definition Node.h:158
Node * parent_
Definition Node.h:149
virtual void OnChildAdded(Node *child)
Definition Node.cpp:362
void RemoveChild(size_t index)
Definition Node.cpp:103
void PrintTree(int indent=0) const
Definition Node.cpp:332
const std::map< String, String > & GetAllAttributes() const
Definition Node.cpp:462
VisualState
Visual state for UI rendering.
Definition Node.h:47
size_t GetSubtreeNodeCount() const
Definition Node.cpp:246
VisualState visual_state_
Definition Node.h:153
size_t GetAttributeCount() const
Definition Node.cpp:464
std::vector< std::unique_ptr< Node > > children_
Definition Node.h:148
Status status_
Definition Node.h:144
size_t id_
Definition Node.h:145
Point position_
Definition Node.h:152
String GetAttribute(const String &name, const String &default_value="") const
Definition Node.cpp:451
Node * GetChild(size_t index) const
Definition Node.cpp:175
bool HasCyclesHelper(std::vector< const Node * > &visited) const
Definition Node.cpp:373
String GetTypeString() const
Definition Node.cpp:274
Type
Node types for behavior tree classification.
Definition Node.h:25
bool HasCycles() const
Definition Node.cpp:231
virtual void OnChildRemoved(Node *child)
Definition Node.cpp:367
void Accept(std::function< void(Node *)> visitor)
Definition Node.cpp:254
static size_t next_id_
Definition Node.h:161
Node * FindChild(const String &name) const
Definition Node.cpp:163
bool children_visible_
Definition Node.h:154
String ToString() const
Definition Node.cpp:327
void SetAttribute(const String &name, const String &value)
Definition Node.cpp:444
void RemoveAttribute(const String &name)
Definition Node.cpp:460
String name_
Definition Node.h:142
std::vector< Node * > GetAllChildren()
Definition Node.cpp:304
std::unique_ptr< Node > DetachChild(size_t index)
Definition Node.cpp:123
size_t FindChildIndex(Node *child) const
Definition Node.cpp:390
bool IsValid() const
Definition Node.cpp:205
Node * FindChildById(size_t id) const
Definition Node.cpp:169
void AddChild(std::unique_ptr< Node > child)
Definition Node.cpp:77
Node & operator=(const Node &other)=delete
int CalculateSubtreeWidth(int node_width, int horizontal_spacing) const
Definition Node.cpp:183
size_t GetDepth() const
Definition Node.cpp:236
int children_width_
Definition Node.h:155
std::unique_ptr< Node > DeepCopy() const
Definition Node.cpp:149
Main types header for EmberCore.
std::string String
Framework-agnostic string type.
Definition String.h:14