khanat-opennel-code/code/nel/src/ligo/primitive.cpp

2757 lines
68 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdligo.h"
#include "nel/misc/types_nl.h"
#include "nel/misc/hierarchical_timer.h"
#include "nel/ligo/primitive.h"
#include "nel/ligo/ligo_config.h"
#include "nel/ligo/primitive_class.h"
#include "nel/misc/i_xml.h"
#include "nel/misc/path.h"
using namespace NLMISC;
using namespace std;
const uint32 NLLIGO_PRIMITIVE_VERSION = 1;
namespace NLLIGO
{
CPrimitiveContext *CPrimitiveContext::_Instance = NULL;
// ***************************************************************************
// XML helpers
// ***************************************************************************
void Error (const char *filename, const char *format, ...)
{
va_list args;
va_start( args, format );
char buffer[1024];
vsnprintf( buffer, 1024, format, args );
va_end( args );
nlwarning ("In File (%s) %s", filename, buffer);
}
// ***************************************************************************
void XMLError (xmlNodePtr xmlNode, const char *filename, const char *format, ... )
{
va_list args;
va_start( args, format );
char buffer[1024];
vsnprintf( buffer, 1024, format, args );
va_end( args );
Error (filename, "node (%s), line (%p) : %s", xmlNode->name, xmlNode->content, buffer);
}
// ***************************************************************************
xmlNodePtr GetFirstChildNode (xmlNodePtr xmlNode, const char *filename, const char *childName)
{
// Call the CIXml version
xmlNodePtr result = CIXml::getFirstChildNode (xmlNode, childName);
if (result) return result;
// Output a formated error
XMLError (xmlNode, filename, "Can't find XML node named (%s)", childName);
return NULL;
}
// ***************************************************************************
bool GetPropertyString (string &result, const char *filename, xmlNodePtr xmlNode, const char *propName)
{
// Call the CIXml version
if (!CIXml::getPropertyString (result, xmlNode, propName))
{
// Output a formated error
XMLError (xmlNode, filename, "Can't find XML node property (%s)", propName);
return false;
}
return true;
}
// ***************************************************************************
bool ReadInt (const char *propName, int &result, const char *filename, xmlNodePtr xmlNode)
{
string value;
if (GetPropertyString (value, filename, xmlNode, propName))
{
result = atoi (value.c_str ());
return true;
}
return false;
}
// ***************************************************************************
void WriteInt (const char *propName, int value, xmlNodePtr xmlNode)
{
// Set properties
xmlSetProp (xmlNode, (const xmlChar*)propName, (const xmlChar*)(toString (value).c_str ()));
}
// ***************************************************************************
bool ReadUInt (const char *propName, uint &result, const char *filename, xmlNodePtr xmlNode)
{
string value;
if (GetPropertyString (value, filename, xmlNode, propName))
{
result = strtoul (value.c_str (), NULL, 10);
return true;
}
return false;
}
// ***************************************************************************
void WriteUInt (const char *propName, uint value, xmlNodePtr xmlNode)
{
// Set properties
xmlSetProp (xmlNode, (const xmlChar*)propName, (const xmlChar*)(toString (value).c_str ()));
}
// ***************************************************************************
bool ReadFloat (const char *propName, float &result, const char *filename, xmlNodePtr xmlNode)
{
string value;
if (GetPropertyString (value, filename, xmlNode, propName))
{
result = (float)atof (value.c_str ());
return true;
}
return false;
}
// ***************************************************************************
void WriteFloat (const char *propName, float value, xmlNodePtr xmlNode)
{
// Set properties
xmlSetProp (xmlNode, (const xmlChar*)propName, (const xmlChar*)(toString (value).c_str ()));
}
// ***************************************************************************
bool ReadVector (CPrimVector &point, const char *filename, xmlNodePtr xmlNode)
{
CPrimVector pos;
if (ReadFloat ("X", pos.x, filename, xmlNode))
{
if (ReadFloat ("Y", pos.y, filename, xmlNode))
{
if (ReadFloat ("Z", pos.z, filename, xmlNode))
{
pos.Selected = false;
string result;
if (CIXml::getPropertyString (result, xmlNode, "SELECTED"))
{
if (result == "true")
pos.Selected = true;
}
point = pos;
return true;
}
}
}
return false;
}
// ***************************************************************************
void WriteVector (const CPrimVector &point, xmlNodePtr xmlNode)
{
// Set properties
xmlSetProp (xmlNode, (const xmlChar*)"X", (const xmlChar*)(toString (point.x).c_str ()));
xmlSetProp (xmlNode, (const xmlChar*)"Y", (const xmlChar*)(toString (point.y).c_str ()));
xmlSetProp (xmlNode, (const xmlChar*)"Z", (const xmlChar*)(toString (point.z).c_str ()));
if (point.Selected)
xmlSetProp (xmlNode, (const xmlChar*)"SELECTED", (const xmlChar*)"true");
}
// ***************************************************************************
bool GetNodeString (string &result, const char *filename, xmlNodePtr xmlNode, const char *nodeName)
{
// Look for the node
xmlNodePtr node = CIXml::getFirstChildNode (xmlNode, nodeName);
if (!node)
{
XMLError (xmlNode, filename, "Can't find XML node named (%s)", nodeName);
return false;
}
// Get the node string
if (!CIXml::getContentString (result, node))
{
XMLError (xmlNode, filename, "Can't find any text in the node named (%s)", nodeName);
return false;
}
return true;
}
// ***************************************************************************
bool GetContentString (string &result, const char *filename, xmlNodePtr xmlNode)
{
// Get the node string
if (!CIXml::getContentString (result, xmlNode))
{
XMLError (xmlNode, filename, "Can't find any text in the node");
return false;
}
return true;
}
// ***************************************************************************
// CPropertyString
// ***************************************************************************
CPropertyString::CPropertyString (const char *str)
{
String = str;
}
CPropertyString::CPropertyString (const std::string &str)
{
String = str;
}
// ***************************************************************************
CPropertyString::CPropertyString (const char *str, bool _default)
{
String = str;
Default = _default;
}
// ***************************************************************************
// CPropertyStringArray
// ***************************************************************************
CPropertyStringArray::CPropertyStringArray (const std::vector<std::string> &stringArray)
{
StringArray = stringArray;
}
// ***************************************************************************
CPropertyStringArray::CPropertyStringArray (const std::vector<std::string> &stringArray, bool _default)
{
StringArray = stringArray;
Default = _default;
}
// ***************************************************************************
void CPrimPoint::serial (NLMISC::IStream &f)
{
// serial base info
IPrimitive::serial(f);
f.serial(Point);
f.serial(Angle);
}
// ***************************************************************************
void CPrimPath::serial (NLMISC::IStream &f)
{
IPrimitive::serial(f);
f.serialCont(VPoints);
}
// ***************************************************************************
bool CPrimZone::contains (const NLMISC::CVector &v, const std::vector<CPrimVector> &points)
{
uint32 i;
CVector vMin, vMax;
// Point or line can't contains !
if (points.size() < 3)
return false;
// Get the bounding rectangle of the zone
vMax = vMin = points[0];
for (i = 0; i < points.size(); ++i)
{
if (vMin.x > points[i].x)
vMin.x = points[i].x;
if (vMin.y > points[i].y)
vMin.y = points[i].y;
if (vMax.x < points[i].x)
vMax.x = points[i].x;
if (vMax.y < points[i].y)
vMax.y = points[i].y;
}
if ((v.x < vMin.x) || (v.y < vMin.y) || (v.x > vMax.x) || (v.y > vMax.y))
return false;
uint32 nNbIntersection = 0;
for (i = 0; i < points.size(); ++i)
{
const CVector &p1 = points[i];
const CVector &p2 = points[(i+1)%points.size()];
if (((p1.y-v.y) <= 0.0)&&((p2.y-v.y) <= 0.0))
continue;
if (((p1.y-v.y) > 0.0)&&((p2.y-v.y) > 0.0))
continue;
float xinter = p1.x + (p2.x-p1.x) * ((v.y-p1.y)/(p2.y-p1.y));
if (xinter > v.x)
++nNbIntersection;
}
if ((nNbIntersection&1) == 1) // odd intersections so the vertex is inside
return true;
else
return false;
}
// ***************************************************************************
bool CPrimZone::contains (const NLMISC::CVector &v, const std::vector<NLMISC::CVector> &points)
{
uint32 i;
CVector vMin, vMax;
// Point or line can't contains !
if (points.size() < 3)
return false;
// Get the bounding rectangle of the zone
vMax = vMin = points[0];
for (i = 0; i < points.size(); ++i)
{
if (vMin.x > points[i].x)
vMin.x = points[i].x;
if (vMin.y > points[i].y)
vMin.y = points[i].y;
if (vMax.x < points[i].x)
vMax.x = points[i].x;
if (vMax.y < points[i].y)
vMax.y = points[i].y;
}
if ((v.x < vMin.x) || (v.y < vMin.y) || (v.x > vMax.x) || (v.y > vMax.y))
return false;
uint32 nNbIntersection = 0;
for (i = 0; i < points.size(); ++i)
{
const CVector &p1 = points[i];
const CVector &p2 = points[(i+1)%points.size()];
if (((p1.y-v.y) <= 0.0)&&((p2.y-v.y) <= 0.0))
continue;
if (((p1.y-v.y) > 0.0)&&((p2.y-v.y) > 0.0))
continue;
float xinter = p1.x + (p2.x-p1.x) * ((v.y-p1.y)/(p2.y-p1.y));
if (xinter > v.x)
++nNbIntersection;
}
if ((nNbIntersection&1) == 1) // odd intersections so the vertex is inside
return true;
else
return false;
}
// ***************************************************************************
// CPrimNode
// ***************************************************************************
bool CPrimNode::read (xmlNodePtr xmlNode, const char *filename, uint version, CLigoConfig &config)
{
return IPrimitive::read (xmlNode, filename, version, config);
}
// ***************************************************************************
uint CPrimNode::getNumVector () const
{
return 0;
}
// ***************************************************************************
const CPrimVector *CPrimNode::getPrimVector () const
{
return NULL;
}
// ***************************************************************************
CPrimVector *CPrimNode::getPrimVector ()
{
return NULL;
}
// ***************************************************************************
NLLIGO::IPrimitive *CPrimNode::copy () const
{
return new CPrimNode (*this);
}
// ***************************************************************************
// CPrimNode
// ***************************************************************************
/*void CPrimNode::operator= (const CPrimNode &node)
{
// Copy the IPrimitive
IPrimitive::operator= (node);
}
// ***************************************************************************
void CPrimPoint::operator= (const CPrimPoint &node)
{
// Copy the IPrimitive
IPrimitive::operator= (node);
}
// ***************************************************************************
void CPrimPath::operator= (const CPrimPath &node)
{
// Copy the IPrimitive
}
// ***************************************************************************
void CPrimZone::operator= (const CPrimZone &node)
{
// Copy the IPrimitive
IPrimitive::operator= (node);
}
*/
// ***************************************************************************
uint CPrimPoint::getNumVector () const
{
return 1;
}
// ***************************************************************************
const CPrimVector *CPrimPoint::getPrimVector () const
{
return &Point;
}
// ***************************************************************************
CPrimVector *CPrimPoint::getPrimVector ()
{
return &Point;
}
// ***************************************************************************
NLLIGO::IPrimitive *CPrimPoint::copy () const
{
return new CPrimPoint (*this);
}
// ***************************************************************************
bool CPrimPoint::read (xmlNodePtr xmlNode, const char *filename, uint version, CLigoConfig &config)
{
// Read points
xmlNodePtr ptNode = GetFirstChildNode (xmlNode, filename, "PT");
if (ptNode)
{
// Read a vector
if (!ReadVector (Point, filename, ptNode))
return false;
ptNode = CIXml::getFirstChildNode (xmlNode, "ANGLE");
if (ptNode)
{
// Read a float
if (!ReadFloat ("VALUE", Angle, filename, ptNode))
return false;
}
else
Angle = 0;
}
else
{
return false;
}
return IPrimitive::read (xmlNode, filename, version, config);
}
// ***************************************************************************
void CPrimPoint::write (xmlNodePtr xmlNode, const char *filename) const
{
// Save the point
xmlNodePtr ptNode = xmlNewChild ( xmlNode, NULL, (const xmlChar*)"PT", NULL);
WriteVector (Point, ptNode);
// Save the angle
if (Angle != 0)
{
xmlNodePtr ptNode = xmlNewChild ( xmlNode, NULL, (const xmlChar*)"ANGLE", NULL);
WriteFloat ("VALUE", Angle, ptNode);
}
IPrimitive::write (xmlNode, filename);
}
// ***************************************************************************
// CPrimPath
// ***************************************************************************
uint CPrimPath::getNumVector () const
{
return (uint)VPoints.size ();
}
// ***************************************************************************
const CPrimVector *CPrimPath::getPrimVector () const
{
if (VPoints.empty())
return NULL;
return &(VPoints[0]);
}
// ***************************************************************************
NLLIGO::IPrimitive *CPrimPath::copy () const
{
return new CPrimPath (*this);
}
// ***************************************************************************
CPrimVector *CPrimPath::getPrimVector ()
{
if (VPoints.empty())
return NULL;
return &(VPoints[0]);
}
// ***************************************************************************
bool CPrimPath::read (xmlNodePtr xmlNode, const char *filename, uint version, CLigoConfig &config)
{
// Read points
VPoints.clear ();
VPoints.reserve (CIXml::countChildren (xmlNode, "PT"));
xmlNodePtr ptNode = CIXml::getFirstChildNode (xmlNode, "PT");
if (ptNode)
{
do
{
// Read a vector
VPoints.push_back (CPrimVector ());
if (!ReadVector (VPoints.back (), filename, ptNode))
return false;
ptNode = CIXml::getNextChildNode (ptNode, "PT");
}
while (ptNode);
}
return IPrimitive::read (xmlNode, filename, version, config);
}
// ***************************************************************************
void CPrimPath::write (xmlNodePtr xmlNode, const char *filename) const
{
// Save the points
for (uint i=0; i<VPoints.size (); i++)
{
xmlNodePtr ptNode = xmlNewChild ( xmlNode, NULL, (const xmlChar*)"PT", NULL);
WriteVector (VPoints[i], ptNode);
}
IPrimitive::write (xmlNode, filename);
}
// ***************************************************************************
// CPrimZone
// ***************************************************************************
uint CPrimZone::getNumVector () const
{
return (uint)VPoints.size ();
}
// ***************************************************************************
const CPrimVector *CPrimZone::getPrimVector () const
{
if (VPoints.empty())
return NULL;
return &(VPoints[0]);
}
// ***************************************************************************
NLLIGO::IPrimitive *CPrimZone::copy () const
{
return new CPrimZone (*this);
}
// ***************************************************************************
CPrimVector *CPrimZone::getPrimVector ()
{
if(VPoints.empty()) return 0;
return &(VPoints[0]);
}
// ***************************************************************************
bool CPrimZone::read (xmlNodePtr xmlNode, const char *filename, uint version, CLigoConfig &config)
{
// Read points
VPoints.clear ();
VPoints.reserve (CIXml::countChildren (xmlNode, "PT"));
xmlNodePtr ptNode = CIXml::getFirstChildNode (xmlNode, "PT");
if (ptNode)
{
do
{
// Read a vector
VPoints.push_back (CPrimVector ());
if (!ReadVector (VPoints.back (), filename, ptNode))
return false;
ptNode = CIXml::getNextChildNode (ptNode, "PT");
}
while (ptNode);
}
return IPrimitive::read (xmlNode, filename, version, config);
}
// ***************************************************************************
void CPrimZone::write (xmlNodePtr xmlNode, const char *filename) const
{
// Save the points
for (uint i=0; i<VPoints.size (); i++)
{
xmlNodePtr ptNode = xmlNewChild ( xmlNode, NULL, (const xmlChar*)"PT", NULL);
WriteVector (VPoints[i], ptNode);
}
IPrimitive::write (xmlNode, filename);
}
// ***************************************************************************
bool CPrimZone::contains (const NLMISC::CVector &v, const std::vector<CPrimVector> &points, float &distance, NLMISC::CVector &nearPos, bool isPath)
{
H_AUTO(NLLIGO_Contains1)
uint32 i;
CVector vMin, vMax;
float nearest = FLT_MAX;
CVector pos;
// Point or line can't contains !
if (points.size() < 3 || isPath)
{
// only compute the distance.
if (points.size() == 1)
{
distance = (points[0] - v).norm();
nearPos = points[0];
}
else if (points.size() == 2)
{
distance = getSegmentDist(v, points[0], points[1], nearPos);
}
else
{
// compute nearest segment
for (i = 0; i < points.size()-1; ++i)
{
const CVector &p1 = points[i];
const CVector &p2 = points[i+1];
float dist = getSegmentDist(v, p1, p2, pos);
if( dist < nearest)
{
nearest = dist;
nearPos = pos;
}
}
distance = nearest;
}
return false;
}
// Get the bounding rectangle of the zone
vMax = vMin = points[0];
for (i = 0; i < points.size(); ++i)
{
vMin.x = min(vMin.x, points[i].x);
vMin.y = min(vMin.y, points[i].y);
vMax.x = max(vMax.x, points[i].x);
vMax.y = max(vMax.y, points[i].y);
}
if ((v.x < vMin.x) || (v.y < vMin.y) || (v.x > vMax.x) || (v.y > vMax.y))
{
// find the nearest distance of all segment
for (uint i=0; i<points.size(); ++i)
{
float dist = getSegmentDist(v, points[i], points[(i+1) % points.size()], pos);
if (dist < nearest)
{
nearest = dist;
nearPos = pos;
}
}
distance = nearest;
return false;
}
uint32 nNbIntersection = 0;
for (i = 0; i < points.size(); ++i)
{
const CVector &p1 = points[i];
const CVector &p2 = points[(i+1)%points.size()];
float dist = getSegmentDist(v, p1, p2, pos);
if( dist < nearest)
{
nearest = dist;
nearPos = pos;
}
if (((p1.y-v.y) <= 0.0)&&((p2.y-v.y) <= 0.0))
continue;
if (((p1.y-v.y) > 0.0)&&((p2.y-v.y) > 0.0))
continue;
float xinter = p1.x + (p2.x-p1.x) * ((v.y-p1.y)/(p2.y-p1.y));
if (xinter > v.x)
++nNbIntersection;
}
distance = nearest;
if ((nNbIntersection&1) == 1) // odd intersections so the vertex is inside
return true;
else
return false;
}
// ***************************************************************************
bool CPrimZone::contains (const NLMISC::CVector &v, const std::vector<CVector> &points, float &distance, NLMISC::CVector &nearPos, bool isPath)
{
H_AUTO(NLLIGO_Contains2)
uint32 i;
CVector vMin, vMax;
float nearest = FLT_MAX;
CVector pos;
// Point or line can't contains !
if (points.size() < 3 || isPath)
{
// only compute the distance.
if (points.size() == 1)
{
distance = (points[0] - v).norm();
nearPos = points[0];
}
else if (points.size() == 2)
{
distance = getSegmentDist(v, points[0], points[1], nearPos);
}
else
{
// compute nearest segment
for (i = 0; i < points.size()-1; ++i)
{
const CVector &p1 = points[i];
const CVector &p2 = points[i+1];
float dist = getSegmentDist(v, p1, p2, pos);
if( dist < nearest)
{
nearest = dist;
nearPos = pos;
}
}
distance = nearest;
}
return false;
}
// Get the bounding rectangle of the zone
vMax = vMin = points[0];
for (i = 0; i < points.size(); ++i)
{
vMin.x = min(vMin.x, points[i].x);
vMin.y = min(vMin.y, points[i].y);
vMax.x = max(vMax.x, points[i].x);
vMax.y = max(vMax.y, points[i].y);
}
if ((v.x < vMin.x) || (v.y < vMin.y) || (v.x > vMax.x) || (v.y > vMax.y))
{
// find the nearest distance of all segment
for (uint i=0; i<points.size(); ++i)
{
float dist = getSegmentDist(v, points[i], points[(i+1) % points.size()], pos);
if (dist < nearest)
{
nearest = dist;
nearPos = pos;
}
}
distance = nearest;
return false;
}
uint32 nNbIntersection = 0;
for (i = 0; i < points.size(); ++i)
{
const CVector &p1 = points[i];
const CVector &p2 = points[(i+1)%points.size()];
float dist = getSegmentDist(v, p1, p2, pos);
if( dist < nearest)
{
nearest = dist;
nearPos = pos;
}
if (((p1.y-v.y) <= 0.0)&&((p2.y-v.y) <= 0.0))
continue;
if (((p1.y-v.y) > 0.0)&&((p2.y-v.y) > 0.0))
continue;
float xinter = p1.x + (p2.x-p1.x) * ((v.y-p1.y)/(p2.y-p1.y));
if (xinter > v.x)
++nNbIntersection;
}
distance = nearest;
if ((nNbIntersection&1) == 1) // odd intersections so the vertex is inside
return true;
else
return false;
}
// ***************************************************************************
float CPrimZone::getSegmentDist(const NLMISC::CVector v, const NLMISC::CVector &p1, const NLMISC::CVector &p2, NLMISC::CVector &nearPos)
{
// two points, compute distance to the segment.
CVector V = (p2-p1).normed();
double length= (p2-p1).norm();
float distance;
// case where p1==p2
if(length==0.0)
{
nearPos= p1;
distance = (p1-v).norm();
}
// standard case
else
{
float t = (float)((double)((v-p1)*V)/length);
if (t < 0.0f)
{
nearPos = p1;
distance = (p1-v).norm();
}
else if (t > 1.0f)
{
nearPos = p2;
distance = (p2-v).norm();
}
else
{
nearPos = p1 + t*(p2-p1);
distance = (v-nearPos).norm();
}
}
return distance;
}
// ***************************************************************************
NLMISC::CVector CPrimZone::getBarycentre() const
{
CVector sum( CVector::Null );
uint n = (uint)VPoints.size();
if ( n != 0 )
{
for ( uint i=0; i!=n; ++i )
sum += VPoints[i];
return sum / (float)n;
}
else
return sum;
}
// ***************************************************************************
void CPrimZone::getAABox( NLMISC::CVector& cornerMin, NLMISC::CVector& cornerMax ) const
{
cornerMin.x = FLT_MAX;
cornerMin.y = FLT_MAX;
cornerMin.z = 0;
cornerMax.x = -FLT_MAX;
cornerMax.y = -FLT_MAX;
cornerMax.z = 0;
for ( uint i=0; i!=VPoints.size(); ++i )
{
const CVector& p = VPoints[i];
if ( p.x < cornerMin.x )
cornerMin.x = p.x;
if ( p.x > cornerMax.x )
cornerMax.x = p.x;
if ( p.y < cornerMin.y )
cornerMin.y = p.y;
if ( p.y > cornerMax.y )
cornerMax.y = p.y;
}
}
// ***************************************************************************
float CPrimZone::getAreaOfAABox() const
{
CVector cornerMin, cornerMax;
getAABox( cornerMin, cornerMax );
return (cornerMax.x-cornerMin.x) * (cornerMax.y-cornerMin.y);
}
// ***************************************************************************
void CPrimZone::serial (NLMISC::IStream &f)
{
IPrimitive::serial(f);
f.serialCont(VPoints);
}
// ***************************************************************************
void CPrimRegion::serial (NLMISC::IStream &f)
{
f.xmlPushBegin ("REGION");
f.xmlSetAttrib ("NAME");
f.serial (Name);
f.xmlPushEnd();
sint version = 2;
version = f.serialVersion (version);
string check = "REGION";
f.serialCheck (check);
f.xmlPush ("POINTS");
f.serialCont (VPoints);
f.xmlPop ();
f.xmlPush ("PATHES");
f.serialCont (VPaths);
f.xmlPop ();
f.xmlPush ("ZONES");
f.serialCont (VZones);
f.xmlPop ();
if (version > 1)
{
f.xmlPush ("HIDEPOINTS");
f.serialCont (VHidePoints);
f.xmlPop ();
f.xmlPush ("HIDEZONES");
f.serialCont (VHideZones);
f.xmlPop ();
f.xmlPush ("HIDEPATHS");
f.serialCont (VHidePaths);
f.xmlPop ();
}
else
{
VHidePoints.resize (VPoints.size(), false);
VHideZones.resize (VZones.size(), false);
VHidePaths.resize (VPaths.size(), false);
}
}
// ***************************************************************************
// IPrimitive
// ***************************************************************************
IPrimitive::IPrimitive ()
{
_Parent = NULL;
}
IPrimitive::IPrimitive (const IPrimitive &node) : IStreamable()
{
_Parent = NULL;
IPrimitive::operator= (node);
}
// ***************************************************************************
void IPrimitive::serial (NLMISC::IStream &f)
{
// NB : unparsed parameters are not binary serialized !
// serialize the property container
if (f.isReading())
{
uint32 size;
f.serial(size);
for (uint i=0; i<size; ++i)
{
std::string s;
f.serial(s);
IProperty *&pp = _Properties[s];
f.serialPolyPtr(pp);
}
}
else
{
uint32 size = (uint32)_Properties.size();
f.serial(size);
std::map<std::string, IProperty*>::iterator first(_Properties.begin()), last(_Properties.end());
for (; first != last; ++first)
{
std::string &s = const_cast<std::string&>(first->first);
f.serial(s);
f.serialPolyPtr(first->second);
}
}
f.serial(_ChildId);
// f.serial(Layer);
// f.serial(Name);
// f.serial(Expanded);
// serial the childrens
if (f.isReading())
{
std::vector<IPrimitive*> children;
f.serialContPolyPtr(children);
uint index = 0;
for(std::vector<IPrimitive*>::iterator it = children.begin(); it != children.end(); ++it, ++index)
{
insertChild(*it, index);
}
}
else
{
f.serialContPolyPtr(_Children);
}
if (f.isReading())
{
// reloc child link
vector<IPrimitive*>::iterator first(_Children.begin()), last(_Children.end());
for (; first != last; ++first)
{
if (*first)
(*first)->_Parent = this;
}
}
}
// ***************************************************************************
void IPrimitive::updateChildId (uint index)
{
uint i;
uint count = (uint)_Children.size ();
for (i=index; i<count; i++)
_Children[i]->_ChildId = i;
}
// ***************************************************************************
void IPrimitive::branchLink()
{
onBranchLink();
std::vector<IPrimitive*>::iterator first(_Children.begin()), last(_Children.end());
for (; first != last; ++first)
{
(*first)->branchLink();
}
}
// ***************************************************************************
void IPrimitive::branchUnlink()
{
onBranchUnlink();
std::vector<IPrimitive*>::iterator first(_Children.begin()), last(_Children.end());
for (; first != last; ++first)
{
(*first)->branchUnlink();
}
}
// ***************************************************************************
void IPrimitive::operator= (const IPrimitive &node)
{
// Clean dest
removeChildren ();
removeProperties ();
// copy deprecated param
// Layer = node.Layer;
// Name = node.Name;
// copy unparsed properties
_UnparsedProperties = node._UnparsedProperties;
// Copy the flags
// Expanded = node.Expanded;
_ChildId = node._ChildId;
// Copy children
_Children.resize (node._Children.size ());
for (uint child = 0; child < node._Children.size (); child++)
{
// Copy the child
_Children[child] = node._Children[child]->copy ();
// Set the parent
_Children[child]->_Parent = this;
}
// Copy properties
std::map<std::string, IProperty*>::const_iterator ite = node._Properties.begin ();
while (ite != node._Properties.end ())
{
// Get the property
CPropertyString *propString = dynamic_cast<CPropertyString *>(ite->second);
if (propString)
{
// New property
CPropertyString *newProp = new CPropertyString ();
*newProp = *propString;
_Properties.insert (std::map<std::string, IProperty*>::value_type (ite->first, newProp));
}
else
{
CPropertyStringArray *propStringArray = dynamic_cast<CPropertyStringArray *>(ite->second);
if (propStringArray)
{
// New property
CPropertyStringArray *newProp = new CPropertyStringArray ();
*newProp = *propStringArray;
_Properties.insert (std::map<std::string, IProperty*>::value_type (ite->first, newProp));
}
else
{
CPropertyColor *propColor = dynamic_cast<CPropertyColor *>(ite->second);
nlverify (propColor);
// New property
CPropertyColor *newProp = new CPropertyColor ();
*newProp = *propColor;
_Properties.insert (std::map<std::string, IProperty*>::value_type (ite->first, newProp));
}
}
ite++;
}
#ifdef NLLIGO_DEBUG
_DebugClassName = node._DebugClassName;
_DebugPrimitiveName = node._DebugPrimitiveName;
#endif
}
const IPrimitive *IPrimitive::getPrimitive (const std::string &absoluteOrRelativePath) const
{
const IPrimitive *cursor=this;
string path=absoluteOrRelativePath;
if (path.find("//")==0) // an absolute path.
{
while (cursor->getParent())
cursor=cursor->getParent();
path.erase(0,2);
}
while (path.size()>0)
{
if (path.find("/")==0)
{
path.erase(0,1);
continue;
}
if (path.find("..")==0)
{
cursor=cursor->getParent();
if (!cursor)
return NULL;
path.erase(0,2);
continue;
}
string::size_type indexStr=path.find("/");
string childName;
if (indexStr==string::npos)
{
childName=path;
path="";
}
else
{
childName=path.substr(0,indexStr);
path.erase(0, indexStr);
}
childName=toUpper(childName);
const IPrimitive*child=NULL;
uint childIndex;
for (childIndex=0;childIndex<cursor->getNumChildren();childIndex++)
{
cursor->getChild(child,childIndex);
string name;
if ( child->getPropertyByName("class", name)
&& toUpper(name)==childName )
break;
}
if (childIndex>=cursor->getNumChildren())
return NULL;
cursor=child;
}
return cursor;
}
// ***************************************************************************
bool IPrimitive::getProperty (uint index, std::string &property_name, const IProperty *&result) const
{
// Look for the property
std::map<std::string, IProperty*>::const_iterator ite = _Properties.begin ();
while (ite != _Properties.end ())
{
if (index == 0)
{
property_name = ite->first;
result = ite->second;
return true;
}
index--;
ite ++;
}
nlwarning ("NLLIGO::IPrimitive::getProperty : invalid index (index : %d, size : %d).", index, _Properties.size ());
return false;
}
// ***************************************************************************
bool IPrimitive::getProperty (uint index, std::string &property_name, IProperty *&result)
{
// Look for the property
std::map<std::string, IProperty*>::iterator ite = _Properties.begin ();
while (ite != _Properties.end ())
{
if (index == 0)
{
property_name = ite->first;
result = ite->second;
return true;
}
index--;
ite ++;
}
nlwarning ("NLLIGO::IPrimitive::getProperty : invalid index (index : %d, size : %d).", index, _Properties.size ());
return false;
}
// ***************************************************************************
bool IPrimitive::getPropertyByName (const char *property_name, const IProperty *&result) const
{
// Look for the property
std::map<std::string, IProperty*>::const_iterator ite = _Properties.find (property_name);
if (ite != _Properties.end ())
{
result = ite->second;
return true;
}
return false;
}
// ***************************************************************************
bool IPrimitive::getPropertyByName (const char *property_name, IProperty *&result) const
{
// Look for the property
std::map<std::string, IProperty*>::const_iterator ite = _Properties.find (property_name);
if (ite != _Properties.end ())
{
result = ite->second;
return true;
}
return false;
}
// ***************************************************************************
bool IPrimitive::getPropertyByName (const char *property_name, std::string *&result) const
{
// Get the property
IProperty *prop;
if (getPropertyByName (property_name, prop))
{
CPropertyString *strProp = dynamic_cast<CPropertyString *> (prop);
if (strProp)
{
result = &(strProp->String);
return true;
}
else
{
nlwarning ("NLLIGO::IPrimitive::getPropertyByName : property (%s) in not a string.", property_name);
}
}
return false;
}
// ***************************************************************************
bool IPrimitive::getPropertyByName (const char *property_name, std::string &result) const
{
// Get the property
const IProperty *prop;
if (getPropertyByName (property_name, prop))
{
const CPropertyString *strProp = dynamic_cast<const CPropertyString *> (prop);
if (strProp)
{
result = strProp->String;
return true;
}
else
{
nlwarning ("NLLIGO::IPrimitive::getPropertyByName : property (%s) in not a string.", property_name);
}
}
return false;
}
// ***************************************************************************
bool IPrimitive::getPropertyByName (const char *property_name, std::vector<std::string> *&result) const
{
// Get the property
IProperty *prop;
if (getPropertyByName (property_name, prop))
{
CPropertyStringArray *strProp = dynamic_cast<CPropertyStringArray *> (prop);
if (strProp)
{
result = &(strProp->StringArray);
return true;
}
else
{
nlwarning ("NLLIGO::IPrimitive::getPropertyByName : property (%s) in not a string.", property_name);
}
}
return false;
}
// ***************************************************************************
bool IPrimitive::getPropertyByName (const char *property_name, const std::vector<std::string> *&result) const
{
// Get the property
IProperty *prop;
if (getPropertyByName (property_name, prop))
{
const CPropertyStringArray *strProp = dynamic_cast<const CPropertyStringArray *> (prop);
if (strProp)
{
result = &(strProp->StringArray);
return true;
}
else
{
nlwarning ("NLLIGO::IPrimitive::getPropertyByName : property (%s) in not a string.", property_name);
}
}
return false;
}
// ***************************************************************************
bool IPrimitive::getPropertyByName (const char *property_name, NLMISC::CRGBA &result) const
{
// Get the property
IProperty *prop;
if (getPropertyByName (property_name, prop))
{
const CPropertyColor *colorProp = dynamic_cast<const CPropertyColor *> (prop);
if (colorProp)
{
result = colorProp->Color;
return true;
}
else
{
nlwarning ("NLLIGO::IPrimitive::getPropertyByName : property (%s) in not a color.", property_name);
}
}
return false;
}
// ***************************************************************************
bool IPrimitive::removeProperty (uint index)
{
// Look for the property
std::map<std::string, IProperty*>::iterator ite = _Properties.begin ();
while (ite != _Properties.end ())
{
if (index == 0)
{
_Properties.erase (ite);
return true;
}
index--;
ite ++;
}
nlwarning ("NLLIGO::IPrimitive::removeProperty : invalid index (index : %d, size : %d).", index, _Properties.size ());
return false;
}
// ***************************************************************************
bool IPrimitive::removePropertyByName (const char *property_name)
{
// Look for the property
std::map<std::string, IProperty*>::iterator ite = _Properties.find (property_name);
if (ite != _Properties.end ())
{
_Properties.erase (ite);
return true;
}
return false;
}
// ***************************************************************************
void IPrimitive::removeProperties ()
{
std::map<std::string, IProperty*>::iterator ite = _Properties.begin ();
while (ite != _Properties.end ())
{
delete ite->second;
ite++;
}
_Properties.clear ();
}
// ***************************************************************************
bool IPrimitive::getChild (const IPrimitive *&result, uint childId) const
{
if (childId < _Children.size ())
{
result = _Children[childId];
return true;
}
else
{
nlwarning ("NLLIGO::IPrimitive::getChild : invalid index (index : %d, size %d).", childId, _Children.size ());
}
return false;
}
// ***************************************************************************
bool IPrimitive::getChild (IPrimitive *&result, uint childId)
{
if (childId < _Children.size ())
{
result = _Children[childId];
return true;
}
else
{
nlwarning ("NLLIGO::IPrimitive::getChild : invalid index (index : %d, size %d).", childId, _Children.size ());
}
return false;
}
// ***************************************************************************
bool IPrimitive::removeChild (IPrimitive *child)
{
uint childId;
if (getChildId(childId, child))
{
return removeChild(childId);
}
else
{
nlwarning("NLLIGO::IPrimitive::removeChild : invalid child, can't remove (child : %p)", child);
}
return false;
}
// ***************************************************************************
bool IPrimitive::removeChild (uint childId)
{
if (childId < _Children.size ())
{
delete _Children[childId];
_Children.erase (_Children.begin()+childId);
updateChildId (childId);
return true;
}
else
{
nlwarning ("NLLIGO::IPrimitive::removeChild : invalid index (index : %d, size %d).", childId, _Children.size ());
}
return false;
}
// ***************************************************************************
void IPrimitive::removeChildren ()
{
// Erase children
for (uint i=0; i<_Children.size (); i++)
{
delete _Children[i];
}
_Children.clear ();
}
// ***************************************************************************
bool IPrimitive::unlinkChild(IPrimitive *child)
{
uint childId;
if (getChildId(childId, child))
{
child->onUnlinkFromParent();
child->branchUnlink();
_Children.erase (_Children.begin()+childId);
updateChildId (childId);
child->_Parent = NULL;
child->_ChildId = 0;
return true;
}
else
{
nlwarning("NLLIGO::IPrimitive::unlinkChild : invalid child, can't unlink (child : %p)", child);
}
return false;
}
// ***************************************************************************
bool IPrimitive::insertChild (IPrimitive *primitive, uint index)
{
// At the end ?
if (index == AtTheEnd)
index = (uint)_Children.size ();
// Index valid ?
if (index>_Children.size ())
return false;
// Insert
_Children.insert (_Children.begin () + index, primitive);
// Update child id
updateChildId (index);
// Link to the parent
primitive->_Parent = this;
// signaling
primitive->onLinkToParent();
primitive->branchLink();
return true;
}
// ***************************************************************************
IPrimitive::~IPrimitive ()
{
// Remove children
removeChildren ();
// Erase properties
removeProperties ();
}
// ***************************************************************************
bool IPrimitive::checkProperty(const std::string &property_name) const
{
if (_Properties.find(property_name) == _Properties.end())
return false;
return true;
}
// ***************************************************************************
bool IPrimitive::addPropertyByName (const char *property_name, IProperty *result)
{
bool inserted = _Properties.insert (std::map<std::string, IProperty*>::value_type (property_name, result)).second;
if (inserted)
{
return true;
}
return false;
}
// ***************************************************************************
bool IPrimitive::read (xmlNodePtr xmlNode, const char *filename, uint version, CLigoConfig &config)
{
// Erase old properties
_Properties.clear ();
// Read the unparsed properties (for editor view)
xmlNodePtr commentNode = CIXml::getFirstChildNode(xmlNode, XML_COMMENT_NODE);
if (commentNode)
{
if (!CIXml::getContentString(_UnparsedProperties, commentNode))
_UnparsedProperties = "";
}
// Read the expanded flag
// string expanded;
// Expanded = true;
// if (CIXml::getPropertyString (expanded, xmlNode, "EXPANDED"))
// Expanded = (expanded != "false");
// Read the properties
xmlNodePtr propNode;
propNode = CIXml::getFirstChildNode (xmlNode, "PROPERTY");
if (propNode)
{
do
{
// Read the name
string name;
if (GetNodeString (name, filename, propNode, "NAME"))
{
// Get the property type
string type;
if (GetPropertyString (type, filename, propNode, "TYPE"))
{
// The property
IProperty *property = NULL;
// Check the type
if (type == "string")
{
// Create a new property
CPropertyString *propertyString = new CPropertyString;
property = propertyString;
// Read it
if (!GetNodeString (propertyString->String, filename, propNode, "STRING"))
{
return false;
}
}
else if (type == "string_array")
{
// Create a new property
CPropertyStringArray *propertyStringArray = new CPropertyStringArray;
property = propertyStringArray;
// Read strings
xmlNodePtr stringNode;
propertyStringArray->StringArray.reserve (CIXml::countChildren (propNode, "STRING"));
stringNode = CIXml::getFirstChildNode (propNode, "STRING");
if (stringNode)
{
do
{
// Add the string
string content;
GetContentString (content, filename, stringNode);
propertyStringArray->StringArray.push_back (content);
stringNode = CIXml::getNextChildNode (stringNode, "STRING");
}
while (stringNode);
}
}
else if (type == "color")
{
// Create a new property
CPropertyColor *propertyColor= new CPropertyColor;
property = propertyColor;
// Read strings
xmlNodePtr colorNode;
colorNode = CIXml::getFirstChildNode (xmlNode, "COLOR");
string R, G, B, A;
if (GetPropertyString (R, filename, colorNode, "R") &&
GetPropertyString (G, filename, colorNode, "G") &&
GetPropertyString (B, filename, colorNode, "B") &&
GetPropertyString (A, filename, colorNode, "A"))
{
sint32 sR=0, sG=0, sB=0, sA=255;
sR = atoi (R.c_str ());
clamp (sR, 0, 255);
sG = atoi (G.c_str ());
clamp (sG, 0, 255);
sB = atoi (B.c_str ());
clamp (sB, 0, 255);
sA = atoi (A.c_str ());
clamp (sR, 0, 255);
propertyColor->Color.R = (uint8)sR;
propertyColor->Color.G = (uint8)sG;
propertyColor->Color.B = (uint8)sB;
propertyColor->Color.A = (uint8)sA;
}
else
return false;
}
// Property found ?
if (property == NULL)
{
XMLError (propNode, filename, "IPrimitive::read : Unknown property type (%s)", type.c_str ());
return false;
}
// Add it
_Properties.insert (std::map<std::string, IProperty*>::value_type (name, property));
}
else
{
return false;
}
}
else
{
return false;
}
propNode = CIXml::getNextChildNode (propNode, "PROPERTY");
}
while (propNode);
}
// Initialise default value
initDefaultValues (config);
// Read children
xmlNodePtr childNode;
childNode = CIXml::getFirstChildNode (xmlNode, "CHILD");
if (childNode)
{
do
{
// Get the property class
string type;
if (GetPropertyString (type, filename, childNode, "TYPE"))
{
// Primitive
if (type=="node")
type="CPrimNode";
if (type=="point")
type="CPrimPoint";
if (type=="path")
type="CPrimPath";
if (type=="zone")
type="CPrimZone";
if (type=="alias")
type="CPrimAlias";
IPrimitive *primitive = static_cast<IPrimitive *> (CClassRegistry::create (type));
// Primitive type not found ?
if (primitive == NULL)
{
XMLError (childNode, filename, "IPrimitive::read : Unknown primitive type (%s)", type.c_str ());
return false;
}
// Read it
primitive->read (childNode, filename, version, config);
// Add it
insertChild (primitive);
}
else
{
return false;
}
childNode = CIXml::getNextChildNode (childNode, "CHILD");
}
while (childNode);
}
#ifdef NLLIGO_DEBUG
// store debug data
getPropertyByName("class", _DebugClassName);
getPropertyByName("name", _DebugPrimitiveName);
#endif
// Done
return true;
}
// ***************************************************************************
void IPrimitive::initDefaultValues (CLigoConfig &config)
{
// Get the primitive class
const CPrimitiveClass *primitiveClass = config.getPrimitiveClass (*this);
if (primitiveClass)
{
// For each properties
uint count = (uint)primitiveClass->Parameters.size ();
uint i;
for (i=0; i<count; i++)
{
const CPrimitiveClass::CParameter &parameter = primitiveClass->Parameters[i];
// Get the property
IProperty *result;
if (!getPropertyByName (parameter.Name.c_str(), result))
{
// Create the property
if ((parameter.Type == CPrimitiveClass::CParameter::StringArray) || (parameter.Type == CPrimitiveClass::CParameter::ConstStringArray))
result = new CPropertyStringArray();
else
result = new CPropertyString();
nlverify (addPropertyByName (parameter.Name.c_str(), result));
}
}
// Set the default values
for (i=0; i<count; i++)
{
const CPrimitiveClass::CParameter &parameter = primitiveClass->Parameters[i];
CPropertyString *pString = NULL;
CPropertyStringArray *pStringArray = NULL;
IProperty *result;
nlverify (getPropertyByName (parameter.Name.c_str(), result));
pString = dynamic_cast<CPropertyString*>(result);
if (!pString)
pStringArray = dynamic_cast<CPropertyStringArray*>(result);
// Property have default values ?
if (pString)
{
// Empty string ?
if (pString->String.empty())
{
// Set as default
pString->Default = true;
parameter.getDefaultValue (pString->String, *this, *primitiveClass);
}
}
else if (pStringArray)
{
// Empty string array ?
if (pStringArray->StringArray.empty())
{
// Set as default
pStringArray->Default = true;
parameter.getDefaultValue (pStringArray->StringArray, *this, *primitiveClass);
}
}
}
}
}
// ***************************************************************************
void IPrimitive::write (xmlNodePtr xmlNode, const char *filename) const
{
// Save the expanded flag
// if (!Expanded)
// xmlSetProp (xmlNode, (const xmlChar*)"EXPANDED", (const xmlChar*)"false");
// Set the type
xmlSetProp (xmlNode, (const xmlChar*)"TYPE", (const xmlChar*)(const_cast<IPrimitive*> (this)->getClassName ().c_str ()));
// Save the unparsed property
if (!_UnparsedProperties.empty())
{
xmlNodePtr commentNode = xmlNewComment((const xmlChar*)(_UnparsedProperties.c_str()));
nlverify(commentNode);
xmlAddChild(xmlNode, commentNode);
}
// Save the properties
std::map<std::string, IProperty*>::const_iterator ite = _Properties.begin ();
while (ite != _Properties.end ())
{
// Not a default property ?
if (!ite->second->Default)
{
// Create new nodes
xmlNodePtr propNode = xmlNewChild ( xmlNode, NULL, (const xmlChar*)"PROPERTY", NULL);
xmlNodePtr nameNode = xmlNewChild ( propNode, NULL, (const xmlChar*)"NAME", NULL);
xmlNodePtr textNode = xmlNewText ((const xmlChar *)(ite->first.c_str ()));
xmlAddChild (nameNode, textNode);
// Type
const CPropertyString *str = dynamic_cast<const CPropertyString *> (ite->second);
if (str)
{
// Set the type
xmlSetProp (propNode, (const xmlChar*)"TYPE", (const xmlChar*)"string");
// Create new nodes
xmlNodePtr stringNode = xmlNewChild ( propNode, NULL, (const xmlChar*)"STRING", NULL);
xmlNodePtr textNode = xmlNewText ((const xmlChar *)(str->String.c_str ()));
xmlAddChild (stringNode, textNode);
}
else
{
// Should be an array
const CPropertyStringArray *array = dynamic_cast<const CPropertyStringArray *> (ite->second);
if (array)
{
// Set the type
xmlSetProp (propNode, (const xmlChar*)"TYPE", (const xmlChar*)"string_array");
// For each strings in the array
for (uint i=0; i<array->StringArray.size (); i++)
{
// Create new nodes
xmlNodePtr stringNode = xmlNewChild ( propNode, NULL, (const xmlChar*)"STRING", NULL);
xmlNodePtr textNode = xmlNewText ((const xmlChar *)(array->StringArray[i].c_str ()));
xmlAddChild (stringNode, textNode);
}
}
else
{
// Should be a color
const CPropertyColor *color = safe_cast<const CPropertyColor *> (ite->second);
// Set the type
xmlSetProp (propNode, (const xmlChar*)"TYPE", (const xmlChar*)"color");
// Create new nodes
xmlNodePtr colorNode = xmlNewChild ( propNode, NULL, (const xmlChar*)"COLOR", NULL);
xmlSetProp (colorNode, (const xmlChar*)"R", (const xmlChar*)toString (color->Color.R).c_str ());
xmlSetProp (colorNode, (const xmlChar*)"G", (const xmlChar*)toString (color->Color.G).c_str ());
xmlSetProp (colorNode, (const xmlChar*)"B", (const xmlChar*)toString (color->Color.B).c_str ());
xmlSetProp (colorNode, (const xmlChar*)"A", (const xmlChar*)toString (color->Color.A).c_str ());
}
}
}
ite++;
}
// Save the children
for (uint i=0; i<_Children.size (); i++)
{
// New node
xmlNodePtr childNode = xmlNewChild ( xmlNode, NULL, (const xmlChar*)"CHILD", NULL);
// Write it
_Children[i]->write (childNode, filename);
}
}
// ***************************************************************************
bool IPrimitive::getChildId (uint &childId, const IPrimitive *child) const
{
childId = child->_ChildId;
return true;
}
// ***************************************************************************
uint IPrimitive::getNumProperty () const
{
return (uint)_Properties.size ();
}
// ***************************************************************************
std::string IPrimitive::getName() const
{
std::string ret;
getPropertyByName("name", ret);
return ret;
}
// ***************************************************************************
const std::string &IPrimitive::getUnparsedProperties() const
{
return _UnparsedProperties;
}
// ***************************************************************************
void IPrimitive::setUnparsedProperties(const std::string &unparsedProperties) const
{
_UnparsedProperties = unparsedProperties;
}
// ***************************************************************************
// CPrimAlias
// ***************************************************************************
CPrimAlias::CPrimAlias() :
_Alias(0),
_Container(NULL)
{
}
CPrimAlias::CPrimAlias(const CPrimAlias &other)
: IPrimitive(other)
{
// clear the container reference and alias
_Container = NULL;
_Alias = other._Alias;
}
CPrimAlias::~CPrimAlias()
{
if (_Container)
onBranchUnlink();
}
void CPrimAlias::onBranchLink()
{
CPrimitiveContext &ctx = CPrimitiveContext::instance();
// context must be set when handling alias
nlassert(ctx.CurrentPrimitive);
nlassert(_Container == NULL || _Container == ctx.CurrentPrimitive);
_Container = ctx.CurrentPrimitive;
// generate a new alias, eventually keeping the current one if any and if still available
_Alias = _Container->genAlias(this, _Alias);
}
void CPrimAlias::onBranchUnlink()
{
nlassert(_Container != NULL);
_Container->releaseAlias(this, _Alias);
_Container = NULL;
// NB : we keep the alias value for next linkage
}
uint32 CPrimAlias::getAlias() const
{
return _Alias;
}
uint32 CPrimAlias::getFullAlias() const
{
nlassert(_Container != NULL);
return _Container->buildFullAlias(_Alias);
}
void CPrimAlias::regenAlias()
{
// container must exist
nlassert(_Container);
// generate a new alias, eventually keeping the current one if any and if still available
_Alias = _Container->genAlias(this, _Alias);
}
// Read the primitive
bool CPrimAlias::read (xmlNodePtr xmlNode, const char *filename, uint version, CLigoConfig &config)
{
// Read alias
xmlNodePtr ptNode = CIXml::getFirstChildNode (xmlNode, "ALIAS");
if (ptNode)
{
sint val = 0;
if (ReadInt ("VALUE", val, filename, ptNode))
{
_Alias = uint32(val);
// nlassert( CPrimitiveContext::instance().CurrentPrimitive);
//// nlassert(_Container);
// CPrimitiveContext::instance().CurrentPrimitive->reserveAlias(_Alias);
// // set to null, it will be rewrited by onBranchLink callback
//// _Container = NULL;
}
else
{
// error in format !
nlwarning("CPrimAlias::read: Can't find xml property 'VALUE' in element <ALIAS>");
return false;
}
}
else
{
// error in format !
nlwarning("CPrimAlias::read: Can't find xml element <ALIAS>");
return false;
}
return IPrimitive::read (xmlNode, filename, version, config);
}
// Write the primitive
void CPrimAlias::write (xmlNodePtr xmlNode, const char *filename) const
{
// Write alias
xmlNodePtr ptNode = xmlNewChild(xmlNode, NULL, (const xmlChar*)"ALIAS", NULL);
WriteInt("VALUE", int(_Alias), ptNode);
IPrimitive::write (xmlNode, filename);
}
// Create a copy of this primitive
IPrimitive *CPrimAlias::copy () const
{
// NB : this will not call the reserveAlias on the container
CPrimAlias *pa = new CPrimAlias(*this);
// clear the alias and container reference
// pa->_Alias = 0;
// pa->_Container = 0;
return pa;
}
// serial for binary save
void CPrimAlias::serial (NLMISC::IStream &f)
{
IPrimitive::serial(f);
f.serial(_Alias);
// if (f.isReading())
// {
// nlassert(_Container);
// _Container->reserveAlias(_Alias);
// }
}
// ***************************************************************************
// CPrimitives
// ***************************************************************************
CPrimitives::CPrimitives () :
_LigoConfig(NULL)
{
// init the alias generator
_LastGeneratedAlias = 0;
_AliasStaticPart = 0;
RootNode = static_cast<CPrimNode *> (CClassRegistry::create ("CPrimNode"));
// get the current ligo context (if any)
_LigoConfig = CPrimitiveContext::instance().CurrentLigoConfig;
}
// ***************************************************************************
CPrimitives::CPrimitives (const CPrimitives &other)
{
operator =(other);
// _LastGeneratedAlias = other._LastGeneratedAlias;
// // get the current ligo context (if any)
// _LigoConfig = CPrimitiveContext::instance().CurrentLigoConfig;
//
// CPrimitives *temp = CPrimitiveContext::instance().CurrentPrimitive;
// CPrimitiveContext::instance().CurrentPrimitive = this;
// // copy the nodes
// RootNode = static_cast<CPrimNode *> (((IPrimitive*)other.RootNode)->copy ());
// RootNode->branchLink();
//
// CPrimitiveContext::instance().CurrentPrimitive = temp;
}
// ***************************************************************************
CPrimitives::~CPrimitives ()
{
delete RootNode;
}
// ***************************************************************************
uint32 CPrimitives::getAliasStaticPart()
{
return _AliasStaticPart;
}
// ***************************************************************************
void CPrimitives::setAliasStaticPart(uint32 staticPart)
{
_AliasStaticPart = staticPart;
}
// ***************************************************************************
uint32 CPrimitives::buildFullAlias(uint32 dynamicPart)
{
if (_LigoConfig)
{
return _LigoConfig->buildAlias(_AliasStaticPart, dynamicPart, true);
}
else
return dynamicPart;
}
// ***************************************************************************
uint32 CPrimitives::genAlias(IPrimitive *prim, uint32 preferedAlias)
{
nlassert(_LigoConfig);
uint32 ret;
if (preferedAlias != 0)
{
// only dynamic part allowed here
nlassert(preferedAlias == (preferedAlias & _LigoConfig->getDynamicAliasMask()));
// check is the prefered alias is not already in use
map<uint32, IPrimitive*>::iterator it(_AliasInUse.find(preferedAlias));
if (it == _AliasInUse.end())
{
// this alias is available, just use it
// nldebug("Alias: added alias %u, %u alias used", preferedAlias, _AliasInUse.size()+1);
_AliasInUse.insert(make_pair(preferedAlias, prim));
return preferedAlias;
}
else
{
// check who own the alias now
if (it->second == prim)
{
// ok, the alias is already own by this primitive
// nldebug("Alias: using alias %u, %u alias used", preferedAlias, _AliasInUse.size()+1);
return preferedAlias;
}
}
}
// make sure there are some free aliases
uint32 mask = _LigoConfig->getDynamicAliasMask();
nlassert(_AliasInUse.size() < mask);
// increment alias counter
++_LastGeneratedAlias;
// mask with the dynamic alias mask
_LastGeneratedAlias &= _LigoConfig->getDynamicAliasMask();
ret = _LastGeneratedAlias;
while (_AliasInUse.find(ret) != _AliasInUse.end())
{
// this alias is already in use ! generate a new one
// increment, mask, affect...
++_LastGeneratedAlias;
_LastGeneratedAlias &= _LigoConfig->getDynamicAliasMask();
ret = _LastGeneratedAlias;
}
// insert the alias
// nldebug("Alias: added alias %u, %u alias in use", ret, _AliasInUse.size()+1);
_AliasInUse.insert(make_pair(ret, prim));
// callback
prim->onModifyPrimitive (*this);
return ret;
}
//void CPrimitives::reserveAlias(uint32 dynamicAlias)
//{
// // need ligo config
// nlassert(_LigoConfig);
// // only dynamic part allowed here
// nlassert(dynamicAlias == (dynamicAlias & _LigoConfig->getDynamicAliasMask()));
// std::set<uint32>::iterator it(_AliasInUse.find(dynamicAlias));
// // warn if already found
// if (it != _AliasInUse.end())
// {
// const string &fileName = _LigoConfig->getFileNameForStaticAlias(_AliasStaticPart);
// if (fileName.empty())
// nlwarning("Dynamic Alias %u is already in use");
// else
// nlwarning("Dynamic Alias %u is already in use in file '%s'",
// dynamicAlias,
// fileName.c_str());
// return;
// }
//
// // insert the alias
// nldebug("Alias: added alias %u, %u alias in use", dynamicAlias, _AliasInUse.size()+1);
// _AliasInUse.insert(dynamicAlias);
//}
//
void CPrimitives::releaseAlias(IPrimitive *prim, uint32 alias)
{
// need ligo config
nlassert(_LigoConfig);
// only dynamic part allowed here
nlassert(alias == (alias & _LigoConfig->getDynamicAliasMask()));
std::map<uint32, IPrimitive*>::iterator it(_AliasInUse.find(alias));
// need to be found
nlassert(it != _AliasInUse.end());
if (it->second != prim)
{
nlwarning("CPrimitives::releaseAlias: The alias %u is own by another primitive !", alias);
return;
}
// remove this alias
// nldebug("Alias: remove alias %u, %u alias left", it->first, _AliasInUse.size()-1);
_AliasInUse.erase(it);
}
// ***************************************************************************
void CPrimitives::forceAlias(CPrimAlias *prim, uint32 alias)
{
// need ligo config
nlassert(_LigoConfig);
// only dynamic part allowed here
nlassert(alias == (alias & _LigoConfig->getDynamicAliasMask()));
// store the alias in the primitive
prim->_Alias = alias;
std::map<uint32, IPrimitive*>::iterator it(_AliasInUse.find(alias));
if (it != _AliasInUse.end() && it->second != prim)
{
// we need to alloc and set a new alias for the current alias holder
CPrimAlias *pa = static_cast<CPrimAlias*>(const_cast<IPrimitive*>(it->second));
// reserve the alias for the new primitive
it->second = prim;
// and regen an alias for the old
pa->regenAlias();
}
else
{
// just store the association
_AliasInUse.insert(make_pair(alias, prim));
}
}
// ***************************************************************************
uint32 CPrimitives::getLastGeneratedAlias()
{
return _LastGeneratedAlias;
}
// ***************************************************************************
IPrimitive *CPrimitives::getPrimitiveByAlias(uint32 primAlias)
{
// check the static part of the alias
uint32 staticAlias = _LigoConfig->getStaticAliasMask() & primAlias;
staticAlias = staticAlias >> _LigoConfig->getDynamicAliasSize();
if (staticAlias != _AliasStaticPart)
return NULL;
// clear the static part before searching
primAlias &= _LigoConfig->getDynamicAliasMask();
std::map<uint32, IPrimitive*>::const_iterator it(_AliasInUse.find(primAlias));
if (it != _AliasInUse.end())
return it->second->getParent();
else
return NULL;
}
// ***************************************************************************
void CPrimitives::buildPrimitiveWithAliasList(std::map<uint32, IPrimitive*> &result)
{
nlassert(_LigoConfig != NULL);
std::map<uint32, IPrimitive*>::iterator first(_AliasInUse.begin()), last(_AliasInUse.end());
for (; first != last; ++first)
{
result.insert(make_pair(_LigoConfig->buildAlias(_AliasStaticPart, first->first), first->second->getParent()));
}
}
// ***************************************************************************
CPrimitives& CPrimitives::operator= (const CPrimitives &other)
{
// RootNode = static_cast<CPrimNode *> (((IPrimitive*)other.RootNode)->copy ());
// return *this;
_AliasStaticPart = other._AliasStaticPart;
_LastGeneratedAlias = other._LastGeneratedAlias;
// get the current ligo context (if any)
_LigoConfig = CPrimitiveContext::instance().CurrentLigoConfig;
CPrimitives *temp = CPrimitiveContext::instance().CurrentPrimitive;
CPrimitiveContext::instance().CurrentPrimitive = this;
// copy the nodes
RootNode = static_cast<CPrimNode *> (((IPrimitive*)other.RootNode)->copy ());
RootNode->branchLink();
CPrimitiveContext::instance().CurrentPrimitive = temp;
return *this;
}
// ***************************************************************************
bool CPrimitives::read (xmlNodePtr xmlNode, const char *filename, CLigoConfig &config)
{
nlassert (xmlNode);
_Filename = CFile::getFilename(filename);
if (_LigoConfig)
{
// try to get the static alias mapping
_AliasStaticPart = _LigoConfig->getFileStaticAliasMapping(CFile::getFilename(filename));
}
// Clear the primitives
RootNode->removeChildren ();
RootNode->removeProperties ();
// Get the name
if (strcmp ((const char*)xmlNode->name, "PRIMITIVES") == 0)
{
// Get the version
string versionName = "0";
if (GetPropertyString (versionName, filename, xmlNode, "VERSION"))
{
// Get the version
uint32 version = atoi (versionName.c_str ());
// Check the version
if (version <= NLLIGO_PRIMITIVE_VERSION)
{
// Read the primitives
xmlNode = GetFirstChildNode (xmlNode, filename, "ROOT_PRIMITIVE");
if (xmlNode)
{
if (version > 0)
{
xmlNodePtr subNode = GetFirstChildNode(xmlNode, filename, "ALIAS");
if (subNode)
{
uint temp;
ReadUInt("LAST_GENERATED", temp, filename, subNode);
_LastGeneratedAlias = temp;
}
else
_LastGeneratedAlias = 0;
}
else
_LastGeneratedAlias = 0;
// Read the primitive tree
((IPrimitive*)RootNode)->read (xmlNode, filename, version, config);
}
}
else
{
Error (filename, "CPrimitives::read : Unknown file version (%d)", version);
return false;
}
}
else
{
return false;
}
}
else
{
XMLError (xmlNode, filename, "This XML document is not a NeL primitive file");
return false;
}
return true;
}
// ***************************************************************************
void CPrimitives::write (xmlDocPtr doc, const char *filename) const
{
nlassert (doc);
// Primitive node
xmlNodePtr primNode = xmlNewDocNode (doc, NULL, (const xmlChar*)"PRIMITIVES", NULL);
xmlDocSetRootElement (doc, primNode);
write (primNode, filename);
}
// ***************************************************************************
void CPrimitives::write (xmlNodePtr root, const char *filename) const
{
nlassert (root);
// Version node
xmlSetProp (root, (const xmlChar*)"VERSION", (const xmlChar*)toString (NLLIGO_PRIMITIVE_VERSION).c_str ());
// The primitive root node
xmlNodePtr nameNode = xmlNewChild ( root, NULL, (const xmlChar*)"ROOT_PRIMITIVE", NULL);
xmlNodePtr subNode = xmlNewChild ( nameNode, NULL, (const xmlChar*)"ALIAS", NULL);
WriteUInt("LAST_GENERATED", _LastGeneratedAlias, subNode);
// Write the primitive tree
((IPrimitive*)RootNode)->write (nameNode, filename);
}
// ***************************************************************************
void CPrimitives::serial(NLMISC::IStream &f)
{
uint currentVersion = NLLIGO_PRIMITIVE_VERSION;
f.serialVersion(currentVersion);
if (currentVersion == 0)
{
f.serial(_LastGeneratedAlias);
}
if (f.isReading())
{
RootNode->removeChildren ();
RootNode->removeProperties ();
}
f.serialPolyPtr(RootNode);
f.serial(_Filename);
if (f.isReading() && _LigoConfig)
{
_AliasStaticPart = _LigoConfig->getFileStaticAliasMapping(_Filename);
}
}
// ***************************************************************************
void CPrimitives::convertAddPrimitive (IPrimitive *child, const IPrimitive *prim, bool hidden)
{
// The primitve
IPrimitive *primitive = NULL;
// What kind of primitive ?
const CPrimPoint *oldPoint = dynamic_cast<const CPrimPoint *>(prim);
if (oldPoint)
{
// Create a primitive
CPrimPoint *point = static_cast<CPrimPoint *> (CClassRegistry::create ("CPrimPoint"));
primitive = point;
// Copy it
*point = *oldPoint;
}
else
{
// Path ?
const CPrimPath *oldPath = dynamic_cast<const CPrimPath *>(prim);
if (oldPath)
{
// Create a primitive
CPrimPath *path = static_cast<CPrimPath *> (CClassRegistry::create ("CPrimPath"));
primitive = path;
// Copy it
*path = *oldPath;
}
else
{
const CPrimZone *oldZone = safe_cast<const CPrimZone *>(prim);
if (oldZone)
{
// Create a primitive
CPrimZone *zone = static_cast<CPrimZone *> (CClassRegistry::create ("CPrimZone"));
primitive = zone;
// Copy it
*zone = *oldZone;
}
}
}
// Primitive has been created ?
if (primitive)
{
// Create a property for the name
CPropertyString *nameProp = new CPropertyString;
// nameProp->String = prim->Name;
// Add the property
primitive->addPropertyByName ("name", nameProp);
// The primitive is hidden ?
if (hidden)
{
// Create a property for hidden
nameProp = new CPropertyString;
// Add the property
primitive->addPropertyByName ("hidden", nameProp);
}
// Add the child
child->insertChild (primitive);
}
}
// ***************************************************************************
void CPrimitives::convertPrimitive (const IPrimitive *prim, bool hidden)
{
// Look for the group
uint numChildren = RootNode->getNumChildren ();
uint j;
for (j=0; j<numChildren; j++)
{
IPrimitive *child;
nlverify (RootNode->getChild (child, j));
const IProperty *prop;
if (child->getPropertyByName ("name", prop))
{
// Prop string
const CPropertyString *name = dynamic_cast<const CPropertyString *>(prop);
if (name)
{
// This one ?
/* if (name->String == prim->Layer)
{
convertAddPrimitive (child, prim, hidden);
break;
}
*/ }
}
}
// Not found ?
if (j==numChildren)
{
// Create a node
CPrimNode *primNode = static_cast<CPrimNode *> (CClassRegistry::create ("CPrimNode"));
// Create a property for the layer
CPropertyString *nameProp = new CPropertyString;
// nameProp->String = prim->Layer;
// Add the property
primNode->addPropertyByName ("name", nameProp);
// Add the child
RootNode->insertChild (primNode);
// Add the primitive
convertAddPrimitive (primNode, prim, hidden);
}
}
// ***************************************************************************
void CPrimitives::convert (const CPrimRegion &region)
{
// Delete
RootNode->removeChildren ();
RootNode->removeProperties ();
// For each primitives
uint i;
for (i=0; i<region.VPoints.size (); i++)
{
convertPrimitive (&(region.VPoints[i]), region.VHidePoints[i]);
}
for (i=0; i<region.VPaths.size (); i++)
{
convertPrimitive (&(region.VPaths[i]), region.VHidePaths[i]);
}
for (i=0; i<region.VZones.size (); i++)
{
convertPrimitive (&(region.VZones[i]), region.VHideZones[i]);
}
}
// ***************************************************************************
CPrimitiveContext::CPrimitiveContext():
CurrentLigoConfig(NULL),
CurrentPrimitive(NULL)
{
}
void Register ()
{
NLMISC_REGISTER_CLASS(CPropertyString);
NLMISC_REGISTER_CLASS(CPropertyStringArray);
NLMISC_REGISTER_CLASS(CPropertyColor);
NLMISC_REGISTER_CLASS(CPrimNode);
NLMISC_REGISTER_CLASS(CPrimPoint);
NLMISC_REGISTER_CLASS(CPrimPath);
NLMISC_REGISTER_CLASS(CPrimZone);
NLMISC_REGISTER_CLASS(CPrimAlias);
}
// ***************************************************************************
} // namespace NLLIGO