Developer Documentation

BaseObjectData.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4 * Copyright (c) 2001-2015, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openflipper.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenFlipper. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41
42
43 //=============================================================================
44 //
45 // MyTypes
46 //
47 //=============================================================================
48
49 //== INCLUDES =================================================================
50
51 #include "BaseObjectData.hh"
52 #include "Types.hh"
53 #include <ACG/Scenegraph/SceneGraph.hh>
54 #include <OpenFlipper/common/GlobalOptions.hh>
55 #include <OpenFlipper/BasePlugin/PluginFunctions.hh>
56
57
58 #include <ACG/Scenegraph/SeparatorNode.hh>
59 #include <ACG/QtScenegraph/QtTranslationManipulatorNode.hh>
60 #include <ACG/Scenegraph/BoundingBoxNode.hh>
61 #include <ACG/Scenegraph/StencilRefNode.hh>
62 #include <ACG/Scenegraph/StatusNodesT.hh>
63
64 //== TYPEDEFS =================================================================
65
66 //== CLASS DEFINITION =========================================================
67
68 BaseObjectData::BaseObjectData(const BaseObjectData& _object)
69 : BaseObject(_object),
70 manipPlaced_(false),
71 rootNode_(_object.rootNode_),
72 separatorNode_(0),
73 manipulatorNode_(0),
74 materialNode_(0),
75 boundingBoxNode_(0),
76 stencilRefNode_(0)
77 {
79 initializeScenegraphNodes();
80 }
81
82 BaseObjectData::BaseObjectData() :
83 BaseObject(),
84 manipPlaced_(false),
85 rootNode_( dynamic_cast< ACG::SceneGraph::SeparatorNode* > (PluginFunctions::getRootNode()) ),
86 separatorNode_(0),
87 manipulatorNode_(0),
88 materialNode_(0),
89 boundingBoxNode_(0),
90 stencilRefNode_(0)
91 {
92 initializeScenegraphNodes();
93 }
94
95 BaseObjectData::~BaseObjectData() {
96 if ( separatorNode_ != 0 ) {
97 separatorNode_->delete_subtree();
98 }
99
100 }
101
102 void BaseObjectData::cleanup() {
103
104 // Delete everything below the seperator node on top of the object. This will remove the complete subtree.
105 if ( separatorNode_ == 0 )
106 std::cerr << "cleanup : separatorNode_ is already 0" << std::endl;
107 else {
108 separatorNode_->delete_subtree();
109 separatorNode_ = 0;
110 manipulatorNode_ = 0;
111 materialNode_ = 0;
112 boundingBoxNode_ = 0;
113 additionalNodes_.clear();
114 }
115
116 BaseObject::cleanup();
117
118 initializeScenegraphNodes();
119 }
120
121 void BaseObjectData::initializeScenegraphNodes() {
122 // Create seperatorNode for Object only if it does not exist.
123 if ( separatorNode_ == 0 )
124 separatorNode_ = new SeparatorNode((BaseNode*)rootNode_,"NEW Object");
125 else
126 std::cerr << "Separator Node already exists. this should not happen!" << std::endl;
127
128 if ( manipulatorNode_ == 0 ) {
129 manipulatorNode_ = new QtTranslationManipulatorNode(baseNode(),"NEW ManipulatorNode");
130
131 // Bind this manipulator to the current object
132 manipulatorNode_->setIdentifier(id());
133 manipulatorNode_->set_status( ACG::SceneGraph::TranslationManipulatorNode::Active );
134 }
135 else
136 std::cerr << "Manipulator Node already exists. this should not happen!" << std::endl;
137 if ( boundingBoxNode_ == 0)
138 {
139 boundingBoxNode_ = new BoundingBoxNode(manipulatorNode(), "New Bounding Box");
140 boundingBoxNode_->set_status( ACG::SceneGraph::BaseNode::HideNode );
141 }
142 if ( stencilRefNode_ == 0 )
143 {
144 stencilRefNode_ = new StencilRefNode(boundingBoxNode(), "New Stencil Reference");
145 stencilRefNode_->set_status( ACG::SceneGraph::BaseNode::HideNode );
146 }
147 if ( materialNode_ == 0 ) {
148 materialNode_ = new MaterialNode(stencilRefNode(), "New Material");
149
150 QColor color;
151
152 if ( OpenFlipper::Options::randomDefaultColor() ){
153
154 QColor bckgrnd = OpenFlipperSettings().value("Core/Gui/glViewer/defaultBackgroundColor").value<QColor>();
155 int diff;
156
157 do{
158 color.setRgb(rand()%255, rand()%255, rand()%255);
159
160 diff = (bckgrnd.red() - color.red()) *(bckgrnd.red() - color.red())
161 +(bckgrnd.green() - color.green())*(bckgrnd.green() - color.green())
162 +(bckgrnd.blue() - color.blue()) *(bckgrnd.blue() - color.blue());
163 } while (diff < 70000);
164 }
165 else{
166 color = OpenFlipper::Options::defaultColor();
167 }
168
169 ACG::Vec4f colorV;
170 colorV[0] = color.redF();
171 colorV[1] = color.greenF();
172 colorV[2] = color.blueF();
173 colorV[3] = color.alphaF();
174
175 materialNode_->set_base_color(colorV);
176 materialNode_->set_color(colorV);
177
178 // We take the brightest color component for overlays to get best contrast
179 materialNode_->set_overlay_color(materialNode_->specular_color());
180 }
181 }
182
183
184 // ===============================================================================
185 // Object visualization
186 // ===============================================================================
187
188 void BaseObjectData::show() {
189 if ( !visible_ ) {
190 separatorNode_->set_status( ACG::SceneGraph::BaseNode::Active );
191 visible_ = true;
192
193 emit visibilityChanged( id() );
194 }
195 }
196
197 void BaseObjectData::hide() {
198 if ( visible_ ) {
199 separatorNode_->set_status( ACG::SceneGraph::BaseNode::HideSubtree );
200 visible_ = false;
201
202 emit visibilityChanged( id() );
203 }
204 }
205
206 bool BaseObjectData::visible(){
207 return visible_;
208 }
209
210 void BaseObjectData::visible(bool _visible) {
211
212 if ( visible_ != _visible ) {
213
214 if (_visible)
215 separatorNode_->set_status( ACG::SceneGraph::BaseNode::Active );
216 else
217 separatorNode_->set_status( ACG::SceneGraph::BaseNode::HideSubtree );
218
219 visible_ = _visible;
220
221 emit visibilityChanged( id() );
222 }
223 }
224
225 SeparatorNode* BaseObjectData::baseNode() {
226 return separatorNode_;
227 }
228
229 bool BaseObjectData::hasNode(BaseNode* _node) {
230
231 QList< BaseNode* > list;
232 list.push_back(separatorNode_);
233
234 BaseNode* currentNode;
235 while (!list.empty()) {
236 currentNode = list.front();
237
238 if ( currentNode->find(_node) == currentNode->childrenEnd() ) {
239 for ( BaseNode::ChildIter child = currentNode->childrenBegin() ; child != currentNode->childrenEnd(); ++child )
240 list.push_back(*child);
241 } else
242 return true;
243
244 list.pop_front();
245 }
246
247 return false;
248
249 }
250
251 BaseNode* BaseObjectData::primaryNode() {
252 return separatorNode_;
253 }
254
255 QtTranslationManipulatorNode* BaseObjectData::manipulatorNode() {
256 return manipulatorNode_;
257 }
258
259 ACG::SceneGraph::ShaderNode* BaseObjectData::shaderNode() {
260 return 0;
261 }
262
263 MaterialNode* BaseObjectData::materialNode() {
264 return materialNode_;
265 }
266
267 const MaterialNode* BaseObjectData::materialNode() const {
268 return materialNode_;
269 }
270
271 BoundingBoxNode* BaseObjectData::boundingBoxNode() {
272 return boundingBoxNode_;
273 }
274
275 StencilRefNode* BaseObjectData::stencilRefNode() {
276 return stencilRefNode_;
277 }
278
279 bool BaseObjectData::manipPlaced() {
280 return manipPlaced_;
281 }
282
283 void BaseObjectData::manipPlaced( bool _placed ) {
284 manipPlaced_ = _placed;
285 }
286
287 void BaseObjectData::getBoundingBox(ACG::Vec3d& bbmin, ACG::Vec3d& bbmax){
288
289 // Single pass action, as the bounding box is not influenced by multipass traversal
290 ACG::SceneGraph::BoundingBoxAction act;
291 ACG::SceneGraph::traverse(separatorNode_, act);
292
293
294 bbmin = (ACG::Vec3d) act.bbMin();
295 bbmax = (ACG::Vec3d) act.bbMax();
296 }
297
298 void BaseObjectData::setObjectDrawMode(const ACG::SceneGraph::DrawModes::DrawMode& _mode, const bool& _force) {
299
300 // Set draw mode for this object
301 ACG::SceneGraph::SetDrawModesAction actionActive(_mode, _force);
302
303 // Traverse scenegraph in order to set new draw mode
304 ACG::SceneGraph::traverse(primaryNode(), actionActive);
305 }
306
307 // ===============================================================================
308 // Picking
309 // ===============================================================================
310
311 bool BaseObjectData::picked( uint /* _node_idx */ ) {
312 return false;
313 }
314
315 void BaseObjectData::enablePicking( bool /*_enable*/ ) {
316 }
317
318 bool BaseObjectData::pickingEnabled() {
319 return true;
320 }
321
322 ACG::Vec3d BaseObjectData::refinePick(const ACG::SceneGraph::PickTarget _pickTarget, const ACG::Vec3d _hitPoint, const ACG::Vec3d _start , const ACG::Vec3d _dir, const unsigned int _targetIdx ) {
323 return _hitPoint;
324 }
325
326 // ===============================================================================
327 // Content Nodes
328 // ===============================================================================
329 void BaseObjectData::update(UpdateType _type )
330 {
331
332 }
333
334 // ===============================================================================
335 // Additional Nodes
336 // ===============================================================================
337
338 bool BaseObjectData::hasAdditionalNode(QString _pluginName, QString _nodeName , int _id )
339 {
340 QString index;
341 index.setNum(_id);
342 QString searchname = _pluginName+"#"+_nodeName+"#"+index;
343
344 for ( uint i =0 ; i < additionalNodes_.size() ; ++i ) {
345 if (additionalNodes_[i].second == searchname )
346 return true;
347 }
348
349 return false;
350 }
351
352
353 //=============================================================================
354
355 void BaseObjectData::setName(QString _name ) {
356 BaseObject::setName(_name);
357
358 std::string nodename = std::string("SeparatorNode for object " + _name.toUtf8());
359 separatorNode_->name( nodename );
360
361 nodename = std::string("ManipulatorNode for object " + _name.toUtf8());
362 manipulatorNode_->name( nodename );
363
364 nodename = std::string("BoundingBoxNode for object " + _name.toUtf8());
365 boundingBoxNode_->name( nodename );
366
367 nodename = std::string(_name.toUtf8() + "'s Material" );
368 materialNode_->name( nodename );
369
370 nodename = std::string("StencilRefNode for object " + _name.toUtf8());
371 stencilRefNode_->name( nodename );
372 }
373
ACG::SceneGraph::BoundingBoxAction::bbMax
const Vec3d & bbMax() const
Returns maximum point of the bounding box.
Definition: SceneGraph.hh:403
BaseObject::visibilityChanged
void visibilityChanged(int _objectId)
BaseObjectData::materialNode_
MaterialNode * materialNode_
Scenegraph Material Node for the object.
Definition: BaseObjectData.hh:233
BaseObjectData::boundingBoxNode_
BoundingBoxNode * boundingBoxNode_
Bounding box node for the object.
Definition: BaseObjectData.hh:236
BaseObject::child
BaseObject * child(int row)
return a child
Definition: BaseObject.cc:506
BaseObjectData::show
virtual void show()
Sets the whole Scenegraph subtree of this node to visible.
Definition: BaseObjectData.cc:188
BaseObjectData::hasAdditionalNode
bool hasAdditionalNode(QString _pluginName, QString _nodeName, int _id=0)
check if an object has the additional node
Definition: BaseObjectData.cc:338
BaseObjectData::picked
virtual bool picked(uint _node_idx)
detect if the node has been picked
Definition: BaseObjectData.cc:311
ACG::SceneGraph::QtTranslationManipulatorNode
Definition: QtTranslationManipulatorNode.hh:81
BaseObjectData::setName
virtual void setName(QString _name) override
path to the file from which the object is loaded ( defaults to "." )
Definition: BaseObjectData.cc:355
BaseObjectData::~BaseObjectData
virtual ~BaseObjectData()
destructor
Definition: BaseObjectData.cc:95
BaseObjectData::baseNode
SeparatorNode * baseNode()
Definition: BaseObjectData.cc:225
ACG
Namespace providing different geometric functions concerning angles.
Definition: BaseObjectData.hh:68
ACG::SceneGraph::MaterialNode::set_overlay_color
void set_overlay_color(const Vec4f &_s)
set the overlay color
Definition: MaterialNode.hh:446
ACG::SceneGraph::ACG::SceneGraph::BaseNode::Active
Draw node & children.
Definition: MeshNode2T_impl.hh:392
BaseObjectData::stencilRefNode_
StencilRefNode * stencilRefNode_
Stencil reference node for the object.
Definition: BaseObjectData.hh:239
BaseObject
Definition: BaseObject.hh:82
BaseObjectData::hasNode
virtual bool hasNode(BaseNode *_node)
Check if the given node is owned by this object.
Definition: BaseObjectData.cc:229
ACG::SceneGraph::MaterialNode::specular_color
const Vec4f & specular_color() const
get the specular color
Definition: MaterialNode.hh:443
GlobalOptions.hh
ACG::SceneGraph::StencilRefNode
Definition: StencilRefNode.hh:74
BaseObjectData::BaseObjectData
BaseObjectData()
constructor
Definition: BaseObjectData.cc:82
ACG::SceneGraph::BaseNode::push_back
void push_back(BaseNode *_node)
Insert _node at the end of the list of children.
Definition: BaseNode.hh:318
OpenMesh::VectorT
Definition: Vector11T.hh:83
ACG::SceneGraph::ShaderNode
Definition: ShaderNode.hh:81
ACG::SceneGraph::BaseNode::HideSubtree
Hide this node and its children.
Definition: BaseNode.hh:398
PluginFunctions.hh
BaseObjectData::refinePick
virtual ACG::Vec3d refinePick(ACG::SceneGraph::PickTarget _pickTarget, const ACG::Vec3d _hitPoint, const ACG::Vec3d _start, const ACG::Vec3d _dir, const unsigned int _targetIdx)
Refine picking.
Definition: BaseObjectData.cc:322
ACG::SceneGraph::MaterialNode
Definition: MaterialNode.hh:338
ACG::SceneGraph::QtTranslationManipulatorNode::setIdentifier
void setIdentifier(int _id)
Set an identifier for that manipulator.
Definition: QtTranslationManipulatorNode.hh:127
ACG::SceneGraph::BaseNode::Active
Draw node & children.
Definition: BaseNode.hh:392
SceneGraph
Definition: QtPrincipalAxisDialog.hh:67
BaseObjectData::enablePicking
virtual void enablePicking(bool _enable)
Definition: BaseObjectData.cc:315
BaseObjectData::primaryNode
virtual BaseNode * primaryNode()
Definition: BaseObjectData.cc:251
BaseObjectData::manipulatorNode
QtTranslationManipulatorNode * manipulatorNode()
Definition: BaseObjectData.cc:255
BaseObject::visible_
bool visible_
Definition: BaseObject.hh:274
ACG::SceneGraph::PickTarget
PickTarget
What target to use for picking.
Definition: PickTarget.hh:73
ACG::SceneGraph::BaseNode::childrenBegin
ChildIter childrenBegin()
Returns: begin-iterator of children.
Definition: BaseNode.hh:294
SeparatorNode
ACG::SceneGraph::SeparatorNode SeparatorNode
Seperator Node.
Definition: BaseObjectData.hh:89
BaseObjectData::cleanup
virtual void cleanup() override
Definition: BaseObjectData.cc:102
ACG::SceneGraph::BoundingBoxNode
Definition: BoundingBoxNode.hh:79
BaseObjectData.hh
BaseObjectData::manipPlaced_
bool manipPlaced_
rootNode of global Scenegraph structure
Definition: BaseObjectData.hh:221
BaseObjectData::boundingBoxNode
BoundingBoxNode * boundingBoxNode()
get a pointer to the bounding box node
Definition: BaseObjectData.cc:271
ACG::SceneGraph::SetDrawModesAction
Definition: SceneGraph.hh:632
BaseObject::setName
virtual void setName(QString _name)
path to the file from which the object is loaded ( defaults to "." )
Definition: BaseObject.cc:723
OpenFlipperQSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: OpenFlipperQSettings.cc:46
BaseObjectData::setObjectDrawMode
void setObjectDrawMode(const ACG::SceneGraph::DrawModes::DrawMode &_mode, const bool &_force=false)
Set the draw mode for the object.
Definition: BaseObjectData.cc:298
BaseObjectData::update
virtual void update(UpdateType _type=UPDATE_ALL) override
This function is called to update the object.
Definition: BaseObjectData.cc:329
ACG::SceneGraph::SeparatorNode
Definition: SeparatorNode.hh:78
BaseObjectData::hide
virtual void hide()
Sets the whole Scenegraph subtree of this node to invisible.
Definition: BaseObjectData.cc:197
ACG::SceneGraph::BaseNode
Definition: BaseNode.hh:104
BaseObjectData
Definition: BaseObjectData.hh:102
UpdateType
Update type class.
Definition: UpdateType.hh:60
ACG::SceneGraph::BaseNode::ChildIter
std::vector< BaseNode * >::iterator ChildIter
allows to iterate over children
Definition: BaseNode.hh:286
ACG::SceneGraph::ACG::SceneGraph::BaseNode::set_status
void set_status(StatusMode _s)
Set the status of this node.
Definition: MeshNode2T_impl.hh:403
QtTranslationManipulatorNode
ACG::SceneGraph::QtTranslationManipulatorNode QtTranslationManipulatorNode
ManipulatorNode.
Definition: BaseObjectData.hh:87
BaseObjectData::materialNode
MaterialNode * materialNode()
get a pointer to the materialnode
Definition: BaseObjectData.cc:263
ACG::SceneGraph::BaseNode::childrenEnd
ChildIter childrenEnd()
Returns: end-iterator of children.
Definition: BaseNode.hh:298
BoundingBoxNode
ACG::SceneGraph::BoundingBoxNode BoundingBoxNode
Bounding box Node.
Definition: BaseObjectData.hh:93
ACG::SceneGraph::DrawModes::DrawMode
Specifies a DrawMode.
Definition: DrawModes.hh:288
BaseObjectData::rootNode_
SeparatorNode * rootNode_
rootNode of global Scenegraph structure
Definition: BaseObjectData.hh:224
BaseObjectData::manipPlaced
bool manipPlaced()
Check if the manipulator has been placed.
Definition: BaseObjectData.cc:279
ACG::SceneGraph::BoundingBoxAction::bbMin
const Vec3d & bbMin() const
Returns minimum point of the bounding box.
Definition: SceneGraph.hh:401
SceneGraph.hh
BaseObjectData::stencilRefNode
StencilRefNode * stencilRefNode()
get a pointer to the stencil reference node
Definition: BaseObjectData.cc:275
ACG::SceneGraph::MaterialNode::set_base_color
void set_base_color(const Vec4f &_c)
set the base color ( Same as set_emission(const Vec4f& _c) )
Definition: MaterialNode.hh:421
StencilRefNode
ACG::SceneGraph::StencilRefNode StencilRefNode
Stencil reference Node.
Definition: BaseObjectData.hh:95
MaterialNode
ACG::SceneGraph::MaterialNode MaterialNode
Materialnode.
Definition: BaseObjectData.hh:85
ACG::SceneGraph::ACG::SceneGraph::BaseNode::delete_subtree
void delete_subtree()
Delete the whole subtree of this node.
Definition: BaseNode.cc:170
BaseObjectData::shaderNode
virtual ACG::SceneGraph::ShaderNode * shaderNode()
Definition: BaseObjectData.cc:259
BaseObjectData::separatorNode_
SeparatorNode * separatorNode_
Separator at top of Scenegraph structure used for this Object.
Definition: BaseObjectData.hh:227
BaseObjectData::initializeScenegraphNodes
void initializeScenegraphNodes()
Definition: BaseObjectData.cc:121
ACG::SceneGraph::BaseNode::find
ChildIter find(BaseNode *_node)
Definition: BaseNode.hh:346
Types.hh
PluginFunctions
Definition: PluginFunctions.cc:57
OpenFlipperSettings
DLLEXPORT OpenFlipperQSettings & OpenFlipperSettings()
QSettings object containing all program settings of OpenFlipper.
Definition: GlobalOptions.cc:1038
ACG::SceneGraph::BaseNode::HideNode
Hide this node, but draw children.
Definition: BaseNode.hh:394
ACG::SceneGraph::BoundingBoxAction
Definition: SceneGraph.hh:362
ACG::SceneGraph::ACG::SceneGraph::BaseNode::name
std::string name() const
Returns: name of node (needs not be unique)
Definition: MeshNode2T_impl.hh:415
BaseObjectData::getBoundingBox
void getBoundingBox(ACG::Vec3d &bbmin, ACG::Vec3d &bbmax)
get the bounding box of the object
Definition: BaseObjectData.cc:287
BaseObject::cleanup
virtual void cleanup()
Definition: BaseObject.cc:207
BaseObjectData::additionalNodes_
std::vector< std::pair< BaseNode *, QString > > additionalNodes_
Definition: BaseObjectData.hh:375
BaseObjectData::pickingEnabled
virtual bool pickingEnabled()
Definition: BaseObjectData.cc:318
ACG::SceneGraph::traverse
void traverse(BaseNode *_node, Action &_action)
Definition: SceneGraph.hh:137
BaseObjectData::manipulatorNode_
QtTranslationManipulatorNode * manipulatorNode_
Manipulator used for this Object.
Definition: BaseObjectData.hh:230
BaseObjectData::visible
virtual bool visible() override
return visiblity
Definition: BaseObjectData.cc:206
ACG::Vec3d
VectorT< double, 3 > Vec3d
Definition: VectorT.hh:121
ACG::SceneGraph::MaterialNode::set_color
void set_color(const Vec4f &_c)
set color (base, ambient, diffuse, specular) based on _c
Definition: MaterialNode.hh:411