KiCad PCB EDA Suite
ifsg_api.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2017 Cirilo Bernardo <[email protected]>
5  * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <iostream>
26 #include <sstream>
27 #include <fstream>
28 #include <memory>
29 #include <wx/filename.h>
30 #include <wx/log.h>
31 #include "plugins/3dapi/ifsg_api.h"
33 #include "streamwrapper.h"
34 #include "3d_cache/sg/sg_node.h"
35 #include "3d_cache/sg/scenegraph.h"
37 #include "3d_cache/sg/sg_shape.h"
38 #include "3d_cache/sg/sg_helpers.h"
39 
40 
41 // version format of the cache file
42 #define SG_VERSION_TAG "VERSION:2"
43 
44 
45 static void formatMaterial( SMATERIAL& mat, SGAPPEARANCE const* app )
46 {
47  float v0, v1, v2;
48 
49  app->ambient.GetColor( v0, v1, v2 );
50  mat.m_Ambient.x = v0;
51  mat.m_Ambient.y = v1;
52  mat.m_Ambient.z = v2;
53 
54  app->diffuse.GetColor( v0, v1, v2 );
55  mat.m_Diffuse.x = v0;
56  mat.m_Diffuse.y = v1;
57  mat.m_Diffuse.z = v2;
58  mat.m_Ambient.x *= v0;
59  mat.m_Ambient.y *= v1;
60  mat.m_Ambient.z *= v2;
61 
62  app->emissive.GetColor( v0, v1, v2 );
63  mat.m_Emissive.x = v0;
64  mat.m_Emissive.y = v1;
65  mat.m_Emissive.z = v2;
66 
67  app->specular.GetColor( v0, v1, v2 );
68  mat.m_Specular.x = v0;
69  mat.m_Specular.y = v1;
70  mat.m_Specular.z = v2;
71 
72  mat.m_Shininess = app->shininess;
73  mat.m_Transparency = app->transparency;
74 }
75 
76 
77 bool S3D::WriteVRML( const char* filename, bool overwrite, SGNODE* aTopNode,
78  bool reuse, bool renameNodes )
79 {
80  if( nullptr == filename || filename[0] == 0 )
81  return false;
82 
83  wxString ofile = wxString::FromUTF8Unchecked( filename );
84 
85  if( wxFileName::Exists( ofile ) )
86  {
87  if( !overwrite )
88  return false;
89 
90  // make sure we make no attempt to write a directory
91  if( !wxFileName::FileExists( ofile ) )
92  return false;
93  }
94 
95  wxCHECK( aTopNode && aTopNode->GetNodeType() == S3D::SGTYPE_TRANSFORM, false );
96 
97  OPEN_OSTREAM( op, filename );
98 
99  if( op.fail() )
100  {
101  wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] failed to open file '%s'" ),
102  __FILE__, __FUNCTION__, __LINE__, filename );
103 
104  return false;
105  }
106 
107  op.imbue( std::locale::classic() );
108  op << "#VRML V2.0 utf8\n";
109 
110  if( renameNodes )
111  {
112  aTopNode->ResetNodeIndex();
113  aTopNode->ReNameNodes();
114  }
115 
116  aTopNode->WriteVRML( op, reuse );
117 
118  if( !op.fail() )
119  {
120  CLOSE_STREAM( op );
121  return true;
122  }
123 
124  CLOSE_STREAM( op );
125 
126  wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] problems encountered writing file '%s'" ),
127  __FILE__, __FUNCTION__, __LINE__, filename );
128 
129  return false;
130 }
131 
132 
134 {
135  wxCHECK( aNode, /* void */ );
136 
137  aNode->ResetNodeIndex();
138 }
139 
140 
141 void S3D::RenameNodes( SGNODE* aNode )
142 {
143  wxCHECK( aNode, /* void */ );
144 
145  aNode->ReNameNodes();
146 }
147 
148 
149 void S3D::DestroyNode( SGNODE* aNode ) noexcept
150 {
151  wxCHECK( aNode, /* void */ );
152 
153  delete aNode;
154 }
155 
156 
157 bool S3D::WriteCache( const char* aFileName, bool overwrite, SGNODE* aNode,
158  const char* aPluginInfo )
159 {
160  if( nullptr == aFileName || aFileName[0] == 0 )
161  return false;
162 
163  wxString ofile = wxString::FromUTF8Unchecked( aFileName );
164 
165  wxCHECK( aNode, false );
166 
167  if( wxFileName::Exists( ofile ) )
168  {
169  if( !overwrite )
170  {
171  wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] file exists not overwriting '%s'" ),
172  __FILE__, __FUNCTION__, __LINE__, aFileName );
173 
174  return false;
175  }
176 
177  // make sure we make no attempt to write a directory
178  if( !wxFileName::FileExists( aFileName ) )
179  {
180  wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] specified path is a directory '%s'" ),
181  __FILE__, __FUNCTION__, __LINE__, aFileName );
182 
183  return false;
184  }
185  }
186 
187  OPEN_OSTREAM( output, aFileName );
188 
189  if( output.fail() )
190  {
191  wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] failed to open file '%s'" ),
192  __FILE__, __FUNCTION__, __LINE__, aFileName );
193 
194  return false;
195  }
196 
197  output << "(" << SG_VERSION_TAG << ")";
198 
199  if( nullptr != aPluginInfo && aPluginInfo[0] != 0 )
200  output << "(" << aPluginInfo << ")";
201  else
202  output << "(INTERNAL:0.0.0.0)";
203 
204  bool rval = aNode->WriteCache( output, nullptr );
205  CLOSE_STREAM( output );
206 
207  if( !rval )
208  {
209  wxLogTrace( MASK_3D_SG,
210  wxT( "%s:%s:%d * [INFO] problems encountered writing cache file '%s'" ),
211  __FILE__, __FUNCTION__, __LINE__, aFileName );
212 
213  // delete the defective file
214  wxRemoveFile( ofile );
215  }
216 
217  return rval;
218 }
219 
220 
221 SGNODE* S3D::ReadCache( const char* aFileName, void* aPluginMgr,
222  bool (*aTagCheck)( const char*, void* ) )
223 {
224  if( nullptr == aFileName || aFileName[0] == 0 )
225  return nullptr;
226 
227  wxString ofile = wxString::FromUTF8Unchecked( aFileName );
228 
229  if( !wxFileName::FileExists( aFileName ) )
230  {
231  wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] no such file '%s'" ),
232  __FILE__, __FUNCTION__, __LINE__, aFileName );
233 
234  return nullptr;
235  }
236 
237  std::unique_ptr<SGNODE> np = std::make_unique<SCENEGRAPH>( nullptr );
238 
239  OPEN_ISTREAM( file, aFileName );
240 
241  if( file.fail() )
242  {
243  wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] failed to open file '%s'" ),
244  __FILE__, __FUNCTION__, __LINE__, aFileName );
245 
246  return nullptr;
247  }
248 
249  // from SG_VERSION_TAG 1, read the version tag; if it's not the expected tag
250  // then we fail to read the cache file
251  do
252  {
253  std::string name;
254  char schar;
255  file.get( schar );
256 
257  if( '(' != schar )
258  {
259  wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; missing left parenthesis"
260  " at position '%d'" ),
261  __FILE__, __FUNCTION__, __LINE__,
262  static_cast<int>( file.tellg() ) );
263 
264  CLOSE_STREAM( file );
265  return nullptr;
266  }
267 
268  file.get( schar );
269 
270  while( ')' != schar && file.good() )
271  {
272  name.push_back( schar );
273  file.get( schar );
274  }
275 
276  if( name.compare( SG_VERSION_TAG ) )
277  {
278  CLOSE_STREAM( file );
279  return nullptr;
280  }
281 
282  } while( 0 );
283 
284  // from SG_VERSION_TAG 2, read the PluginInfo string and check that it matches
285  // version tag; if it's not the expected tag then we fail to read the file
286  do
287  {
288  std::string name;
289  char schar;
290  file.get( schar );
291 
292  if( '(' != schar )
293  {
294  wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; missing left parenthesis"
295  " at position '%d'" ),
296  __FILE__, __FUNCTION__, __LINE__,
297  static_cast<int>( file.tellg() ) );
298 
299  CLOSE_STREAM( file );
300  return nullptr;
301  }
302 
303  file.get( schar );
304 
305  while( ')' != schar && file.good() )
306  {
307  name.push_back( schar );
308  file.get( schar );
309  }
310 
311  // check the plugin tag
312  if( nullptr != aTagCheck && nullptr != aPluginMgr
313  && !aTagCheck( name.c_str(), aPluginMgr ) )
314  {
315  CLOSE_STREAM( file );
316  return nullptr;
317  }
318 
319  } while( 0 );
320 
321  bool rval = np->ReadCache( file, nullptr );
322  CLOSE_STREAM( file );
323 
324  if( !rval )
325  {
326  wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] problems encountered reading cache file "
327  "'%s'" ),
328  __FILE__, __FUNCTION__, __LINE__,
329  aFileName );
330 
331  return nullptr;
332  }
333 
334  return np.release();
335 }
336 
337 
339 {
340  if( nullptr == aNode )
341  return nullptr;
342 
343  if( aNode->GetNodeType() != S3D::SGTYPE_TRANSFORM )
344  return nullptr;
345 
346  S3D::MATLIST materials;
347  std::vector< SMESH > meshes;
348 
349  // the materials list shall have a default color; although the VRML
350  // default is an opaque black, the default used here shall be a median
351  // gray in hopes that it may help highlight faulty models; this color is
352  // also typical of MCAD applications. When a model has no associated
353  // material color it shall be assigned the index 0.
354  SGAPPEARANCE app( nullptr );
355  app.ambient = SGCOLOR( 0.6f, 0.6f, 0.6f );
356  app.diffuse = SGCOLOR( 0.6f, 0.6f, 0.6f );
357  app.specular = app.diffuse;
358  app.shininess = 0.05f;
359  app.transparency = 0.0f;
360 
361  materials.matorder.push_back( &app );
362  materials.matmap.insert( std::pair< SGAPPEARANCE const*, int >( &app, 0 ) );
363 
364  if( aNode->Prepare( nullptr, materials, meshes ) )
365  {
366  if( meshes.empty() )
367  return nullptr;
368 
369  S3DMODEL* model = S3D::New3DModel();
370 
371  // add all the materials
372  size_t j = materials.matorder.size();
373  SMATERIAL* lmat = new SMATERIAL[j];
374 
375  for( size_t i = 0; i < j; ++i )
376  formatMaterial( lmat[i], materials.matorder[i] );
377 
378  model->m_Materials = lmat;
379  model->m_MaterialsSize = j;
380 
381  // add all the meshes
382  j = meshes.size();
383  SMESH* lmesh = new SMESH[j];
384 
385  for( size_t i = 0; i < j; ++i )
386  lmesh[i] = meshes[i];
387 
388  model->m_Meshes = lmesh;
389  model->m_MeshesSize = j;
390 
391  return model;
392  }
393 
394  size_t j = meshes.size();
395 
396  for( size_t i = 0; i < j; ++i )
397  S3D::Free3DMesh( meshes[i] );
398 
399  return nullptr;
400 }
401 
402 
404 {
405  if( nullptr == aModel || nullptr == *aModel )
406  return;
407 
408  S3DMODEL* m = *aModel;
409  S3D::FREE_S3DMODEL( *m );
410  delete m;
411  *aModel = nullptr;
412 }
413 
414 
415 void Free3DModel( S3DMODEL& aModel )
416 {
417  S3D::FREE_S3DMODEL( aModel );
418 }
419 
420 
421 void S3D::Free3DMesh( SMESH& aMesh )
422 {
423  S3D::FREE_SMESH( aMesh );
424 }
425 
426 
428 {
429  S3DMODEL* mp = new S3DMODEL;
430  S3D::INIT_S3DMODEL( *mp );
431  return mp;
432 }
433 
434 
436 {
437  S3D::INIT_SMATERIAL( aMat );
438 }
439 
440 
441 void S3D::Init3DMesh( SMESH& aMesh )
442 {
443  S3D::INIT_SMESH( aMesh );
444 }
445 
446 
447 void S3D::GetLibVersion( unsigned char* Major, unsigned char* Minor, unsigned char* Patch,
448  unsigned char* Revision ) noexcept
449 {
450  if( Major )
451  *Major = KICADSG_VERSION_MAJOR;
452 
453  if( Minor )
454  *Minor = KICADSG_VERSION_MINOR;
455 
456  if( Revision )
457  *Revision = KICADSG_VERSION_REVISION;
458 
459  if( Patch )
460  *Patch = KICADSG_VERSION_PATCH;
461 }
462 
463 
464 SGVECTOR S3D::CalcTriNorm( const SGPOINT& p1, const SGPOINT& p2, const SGPOINT& p3 )
465 {
466  glm::dvec3 tri = glm::dvec3( 0.0, 0.0, 0.0 );
467  glm::dvec3 pts[3];
468 
469  pts[0] = glm::dvec3( p1.x, p1.y, p1.z );
470  pts[1] = glm::dvec3( p2.x, p2.y, p2.z );
471  pts[2] = glm::dvec3( p3.x, p3.y, p3.z );
472 
473  // degenerate points are given a default 0, 0, 1 normal
474  if( S3D::degenerate( pts ) )
475  return SGVECTOR( 0.0, 0.0, 1.0 );
476 
477  // normal
478  tri = glm::cross( pts[1] - pts[0], pts[2] - pts[0] );
479  glm::normalize( tri );
480 
481  return SGVECTOR( tri.x, tri.y, tri.z );
482 }
483 
484 
486 {
487  if( nullptr == aNode )
488  return SGTYPE_END;
489 
490  return aNode->GetNodeType();
491 }
492 
493 
495 {
496  if( nullptr == aNode )
497  return nullptr;
498 
499  return aNode->GetParent();
500 }
501 
502 
503 bool S3D::AddSGNodeRef( SGNODE* aParent, SGNODE* aChild )
504 {
505  if( nullptr == aParent || nullptr == aChild )
506  return false;
507 
508  return aParent->AddRefNode( aChild );
509 }
510 
511 
512 bool S3D::AddSGNodeChild( SGNODE* aParent, SGNODE* aChild )
513 {
514  if( nullptr == aParent || nullptr == aChild )
515  return false;
516 
517  return aParent->AddChildNode( aChild );
518 }
519 
520 
521 void S3D::AssociateSGNodeWrapper( SGNODE* aObject, SGNODE** aRefPtr )
522 {
523  if( nullptr == aObject || nullptr == aRefPtr || aObject != *aRefPtr )
524  return;
525 
526  aObject->AssociateWrapper( aRefPtr );
527 }
#define KICADSG_VERSION_MAJOR
Definition: sg_version.h:33
virtual void ReNameNodes(void)=0
Rename a node and all its child nodes in preparation for write operations.
SGCOLOR diffuse
Definition: sg_appearance.h:77
#define KICADSG_VERSION_REVISION
Definition: sg_version.h:36
SGLIB_API void RenameNodes(SGNODE *aNode)
Function RenameNodes renames a node and all children nodes based on the current values of the global ...
Definition: ifsg_api.cpp:141
SFVEC3F m_Ambient
Definition: c3dmodel.h:39
VECTOR2I v2(1, 0)
Test suite for KiCad math code.
SGLIB_API S3DMODEL * New3DModel(void)
Function New3DModel creates and initializes an S3DMODEL struct.
Definition: ifsg_api.cpp:427
double x
Definition: sg_base.h:70
bool degenerate(glm::dvec3 *pts) noexcept
Definition: sg_helpers.cpp:310
#define KICADSG_VERSION_PATCH
Definition: sg_version.h:35
SGLIB_API void Init3DMaterial(SMATERIAL &aMat)
Function Init3DMaterial initializes an SMATERIAL struct.
Definition: ifsg_api.cpp:435
virtual bool AddRefNode(SGNODE *aNode)=0
void ResetNodeIndex(void) noexcept
Reset the global SG* node indices in preparation for write operations.
Definition: sg_node.cpp:238
SGLIB_API SGNODE * ReadCache(const char *aFileName, void *aPluginMgr, bool(*aTagCheck)(const char *, void *))
Function ReadCache reads a binary cache file and creates an SGNODE tree.
Definition: ifsg_api.cpp:221
SGLIB_API void ResetNodeIndex(SGNODE *aNode)
Function ResetNodeIndex resets the global SG* class indices.
Definition: ifsg_api.cpp:133
SGLIB_API void AssociateSGNodeWrapper(SGNODE *aObject, SGNODE **aRefPtr)
Definition: ifsg_api.cpp:521
void FREE_S3DMODEL(S3DMODEL &aModel)
Definition: sg_node.cpp:326
SGLIB_API void Destroy3DModel(S3DMODEL **aModel)
Function Destroy3DModel frees memory used by an S3DMODEL structure and sets the pointer to the struct...
Definition: ifsg_api.cpp:403
SGLIB_API SGNODE * GetSGNodeParent(SGNODE *aNode)
Definition: ifsg_api.cpp:494
void AssociateWrapper(SGNODE **aWrapperRef) noexcept
Associate this object with a handle to itself.
Definition: sg_node.cpp:207
float m_Transparency
1.0 is completely transparent, 0.0 completely opaque
Definition: c3dmodel.h:44
#define KICADSG_VERSION_MINOR
Definition: sg_version.h:34
double y
Definition: sg_base.h:71
std::map< SGAPPEARANCE const *, int > matmap
Definition: sg_node.h:57
The base class of all Scene Graph nodes.
Definition: sg_node.h:74
SGCOLOR emissive
Definition: sg_appearance.h:78
Per-vertex normal/color/texcoors structure.
Definition: c3dmodel.h:76
void GetColor(float &aRedVal, float &aGreenVal, float &aBlueVal) const noexcept
Definition: sg_base.cpp:59
SGLIB_API bool AddSGNodeRef(SGNODE *aParent, SGNODE *aChild)
Definition: ifsg_api.cpp:503
SMESH * m_Meshes
The meshes list of this model.
Definition: c3dmodel.h:93
SGLIB_API bool WriteVRML(const char *filename, bool overwrite, SGNODE *aTopNode, bool reuse, bool renameNodes)
Function WriteVRML writes out the given node and its subnodes to a VRML2 file.
Definition: ifsg_api.cpp:77
SGLIB_API void DestroyNode(SGNODE *aNode) noexcept
Function DestroyNode deletes the given SG* class node.
Definition: ifsg_api.cpp:149
SGCOLOR ambient
Definition: sg_appearance.h:76
static void formatMaterial(SMATERIAL &mat, SGAPPEARANCE const *app)
Definition: ifsg_api.cpp:45
float transparency
Definition: sg_appearance.h:75
#define OPEN_OSTREAM(var, name)
SGLIB_API bool AddSGNodeChild(SGNODE *aParent, SGNODE *aChild)
Definition: ifsg_api.cpp:512
void INIT_SMESH(SMESH &aMesh) noexcept
Definition: sg_node.cpp:276
void INIT_S3DMODEL(S3DMODEL &aModel) noexcept
Definition: sg_node.cpp:282
defines the library version of the intermediate scenegraph (SG) implementation
float m_Shininess
Definition: c3dmodel.h:43
SGLIB_API void Init3DMesh(SMESH &aMesh)
Function Init3DMesh creates and initializes an SMESH struct.
Definition: ifsg_api.cpp:441
unsigned int m_MaterialsSize
Number of materials in the material array.
Definition: c3dmodel.h:95
defines the API calls for the manipulation of SG* classes
#define SG_VERSION_TAG
Definition: ifsg_api.cpp:42
SGNODE * GetParent(void) const noexcept
Returns a pointer to the parent SGNODE of this object or NULL if the object has no parent (ie.
Definition: sg_node.cpp:110
SGTYPES
Definition: sg_types.h:34
virtual bool WriteCache(std::ostream &aFile, SGNODE *parentNode)=0
Write this node's data to a binary cache file.
SGLIB_API SGVECTOR CalcTriNorm(const SGPOINT &p1, const SGPOINT &p2, const SGPOINT &p3)
Function CalcTriNorm returns the normal vector of a triangle described by vertices p1,...
Definition: ifsg_api.cpp:464
SGLIB_API S3D::SGTYPES GetSGNodeType(SGNODE *aNode)
Definition: ifsg_api.cpp:485
void Free3DModel(S3DMODEL &aModel)
Function Free3DModel frees memory used internally by an S3DMODEL structure.
Definition: ifsg_api.cpp:415
const char * name
Definition: DXF_plotter.cpp:56
#define OPEN_ISTREAM(var, name)
SFVEC3F m_Emissive
Definition: c3dmodel.h:41
std::vector< SGAPPEARANCE const * > matorder
Definition: sg_node.h:56
SMATERIAL * m_Materials
The materials list of this model.
Definition: c3dmodel.h:96
double z
Definition: sg_base.h:72
SGCOLOR specular
Definition: sg_appearance.h:79
void INIT_SMATERIAL(SMATERIAL &aMaterial)
Definition: sg_node.cpp:270
virtual bool AddChildNode(SGNODE *aNode)=0
Define the basic data set required to represent a 3D model.
Definition: scenegraph.h:44
SGLIB_API bool WriteCache(const char *aFileName, bool overwrite, SGNODE *aNode, const char *aPluginInfo)
Function WriteCache writes the SGNODE tree to a binary cache file.
Definition: ifsg_api.cpp:157
SFVEC3F m_Diffuse
Default diffuse color if m_Color is NULL.
Definition: c3dmodel.h:40
Store the a model based on meshes and materials.
Definition: c3dmodel.h:90
Define a number of macros to aid in repetitious code which is probably best expressed as a preprocess...
#define CLOSE_STREAM(var)
void FREE_SMESH(SMESH &aMesh) noexcept
Definition: sg_node.cpp:288
SGLIB_API S3DMODEL * GetModel(SCENEGRAPH *aNode)
Function GetModel creates an S3DMODEL representation of aNode (raw data, no transforms)
Definition: ifsg_api.cpp:338
unsigned int m_MeshesSize
Number of meshes in the array.
Definition: c3dmodel.h:92
SGLIB_API void GetLibVersion(unsigned char *Major, unsigned char *Minor, unsigned char *Patch, unsigned char *Revision) noexcept
Function GetLibVersion retrieves version information of the kicad_3dsg library.
Definition: ifsg_api.cpp:447
Defines the generic material appearance of a scenegraph object.
Definition: sg_appearance.h:37
SFVEC3F m_Specular
Definition: c3dmodel.h:42
virtual bool WriteVRML(std::ostream &aFile, bool aReuseFlag)=0
Writes this node's data to a VRML file.
S3D::SGTYPES GetNodeType(void) const noexcept
Return the type of this node instance.
Definition: sg_node.cpp:104
bool Prepare(const glm::dmat4 *aTransform, S3D::MATLIST &materials, std::vector< SMESH > &meshes)
Definition: scenegraph.cpp:647
SGLIB_API void Free3DMesh(SMESH &aMesh)
Function Free3DMesh frees memory used internally by an SMESH structure.
Definition: ifsg_api.cpp:421