pathfinding change: the engine uses Triangulation and A* on triangles now. dcdt package added. premake.lua changed to include the dcdt code. it needs to run update-workspaces.bat (flag -showOverlay will draw the triangulation and a single unit paths)

This was SVN commit r5393.
This commit is contained in:
kai 2007-10-09 07:27:45 +00:00
parent 4b7713fd94
commit e595dbc88e
210 changed files with 44266 additions and 22 deletions

View File

@ -304,7 +304,7 @@ function setup_all_libs ()
-- names of external libraries used (see libraries_dir comment)
local extern_libs = {}
source_dirs = {
"network",
}
@ -326,7 +326,8 @@ function setup_all_libs ()
"sound",
"scripting",
"maths",
"maths/scripting"
"maths/scripting",
"dcdt/se"
}
extern_libs = {
"spidermonkey",
@ -343,12 +344,14 @@ function setup_all_libs ()
"graphics",
"graphics/scripting",
"renderer"
}
extern_libs = {
"opengl",
"sdl", -- key definitions
"spidermonkey", -- for graphics/scripting
"boost"
}
setup_static_lib_package("graphics", source_dirs, extern_libs, {})
@ -369,12 +372,14 @@ function setup_all_libs ()
source_dirs = {
"tools/atlas/GameInterface",
"tools/atlas/GameInterface/Handlers"
}
extern_libs = {
"boost",
"sdl", -- key definitions
"opengl",
"spidermonkey"
}
setup_static_lib_package("atlas", source_dirs, extern_libs, {})

722
source/dcdt/se/Abstract.cpp Normal file
View File

@ -0,0 +1,722 @@
//Abstract.cpp
//DJD: Abstract function definitions {
#include "precompiled.h"
#include <math.h>
#include <fstream>
#include "se_dcdt.h"
//the degree for an unabstracted triangle
#define UNABSTRACTED -1
template <class T>
SrArray<SeLinkFace<T> *> SeLinkFace<T>::processing;
void SeDcdt::Abstract()
{
#if defined EXPERIMENT
//count the number of degree-3 nodes in the abstract graph
int num = 0;
#endif
//Degree-1 and Degree-0 faces
//keep track of the connected component
int component = 0;
//possible degree-1 triangles
SrArray<SeDcdtFace *> degree1;
//possible degree-2 or 3 triangles
SrArray<SeDcdtFace *> processing;
//get first face and record it
SeDcdtFace *currentFace = _mesh->first()->fac();
SeDcdtFace *firstFace = currentFace;
if (outside == NULL)
{
//first determine the "outside" face
// for (int n = 0; n < SeDcdtFace::Faces(); n++)
do
{
// SeDcdtFace *currentFace = SeDcdtFace::Face(n);
if (currentFace == NULL)
{
continue;
}
float x1, y1, x2, y2, x3, y3;
TriangleVertices(currentFace, x1, y1, x2, y2, x3, y3);
//outside face is any whose vertices aren't in counterclockwise order
if (Orientation(x1, y1, x2, y2, x3, y3) <= 0.0f)
{
//record this face, reset, and break
outside = currentFace;
currentFace = firstFace;
break;
}
//if it wasn't found, try the next face
currentFace = currentFace->nxt();
}
while (currentFace != firstFace);
}
//go through all faces in the triangulation
// for (int n = 0; n < SeDcdtFace::Faces(); n++)
do
{
// SeDcdtFace *currentFace = SeDcdtFace::Face(n);
//don't process the outside face
if ((currentFace == NULL) || (currentFace == outside))
{
currentFace = currentFace->nxt();
continue;
}
//keep track of how many edges are constrained
int numConstrained = 0;
//this is the adjacent face to a degree-1 triangle
SeDcdtFace *tempFace = NULL;
//access the parts of the face
SeBase *s = currentFace->se();
//count the number of constrained edges around this face
for (int i = 0; i < 3; i++)
{
//consider a constrained edge or one bordering the outside face to be blocked
if (Blocked(s))
{
numConstrained++;
}
//if there's an unconstrained edge, record the face across it
else
{
tempFace = (SeDcdtFace *)(s->sym()->fac());
}
//go to the next element of the triangle
s = s->nxt();
}
//if there are two constrained edges, this is a degree-1 triangle
if (numConstrained == 2)
{
//abstract the face and calculate its widths
currentFace->link = new Abstraction(1);
CalculateWidths(currentFace);
//if the face across the unconstrained edge is already degree-1,
if (Degree(tempFace) == 1)
{
//this is a tree component
TreeAbstract(tempFace, component++);
}
//if it's not abstracted, it might now be a degree-1 node
else if (Degree(tempFace) == UNABSTRACTED)
{
degree1.push() = tempFace;
}
}
//if all edges are constrained, this is a degree-0 triangle
else if (numConstrained == 3)
{
//abstract it and set its component
currentFace->link = new Abstraction(0);
currentFace->link->Component(component++);
}
//if it's not degree-0 or 1, it might be degree-2 or 3
else
{
//enqueue it for later processing
processing.push() = currentFace;
}
//move to the next face in the mesh
currentFace = currentFace->nxt();
}
//continue until reaching the first face again
while (currentFace != firstFace);
//keep track of which faces we have considered for degree-1 nodes
_mesh->begin_marking();
//go through the possible degree-1 nodes
while (!degree1.empty())
{
//get the next possible degree-1 node
SeDcdtFace *currentFace = degree1.pop();
//if this face has already been dealt with, skip it
if ((_mesh->marked(currentFace)) || (Degree(currentFace) != UNABSTRACTED))
{
continue;
}
//keep track of the number of adjacent triangles
int numAdjacent = 0;
//the face across an unconstrained edge
SeDcdtFace *tempFace = NULL;
//access the elements of the triangle
SeBase *s = currentFace->se();
//calculate the number of unabstracted faces across unconstrained edges
for (int i = 0; i < 3; i++)
{
//only consider adjacent, unabstracted edges
if (!Blocked(s) && (Degree(s->sym()) == UNABSTRACTED))
{
numAdjacent++;
tempFace = (SeDcdtFace *)(s->sym()->fac());
}
//move to the next edge of the triangle
s = s->nxt();
}
//if there is only one, this is a degree-1 node
if (numAdjacent == 1)
{
//abstract this node and calculate its widths
currentFace->link = new Abstraction(1);
CalculateWidths(currentFace);
//the adjacent triangle is now possibly degree-1
degree1.push() = tempFace;
//mark this face as dealt with
_mesh->mark(currentFace);
}
//if there aren't any, this is a tree component
else if (numAdjacent == 0)
{
TreeAbstract(currentFace, component++);
//mark this face as dealt with
_mesh->mark(currentFace);
}
}
//done with degree-1 nodes
_mesh->end_marking();
//Degree-2 and Degree-3 faces
//go through all possible degree-3 triangles
for (int n = 0; n < processing.size(); n++)
{
//gets the current face to check
SeDcdtFace *currentFace = processing[n];
//we only care about unabstracted nodes at this point
if (Degree(currentFace) != UNABSTRACTED)
{
continue;
}
//the number of non-degree-1 triangles across unconstrained edges
int numAdjacent = 0;
//access the elements of the triangle
SeBase *s = currentFace->se();
//go through the edges of the triangle
for (int i = 0; i < 3; i++)
{
//get the degree of the triangle across that edge
int degree = Degree(s->sym());
//keep track of non-degree-1 edges across unconstrained edges
if (!Blocked(s) && (degree != 1))
{
numAdjacent++;
}
//move to the next edge
s = s->nxt();
}
//if this is not a degree-3 triangle, skip it
if (numAdjacent < 3)
{
continue;
}
//stack of degree-3 nodes in this connected component
SrArray<SeDcdtFace *> degree3;
//put the current triangle on the stack
degree3.push() = currentFace;
//continue through all degree-3 nodes in this component
while (!degree3.empty())
{
//get one of the degree-3 nodes from the stack
SeDcdtFace *stackFace = degree3.pop();
//abstract it and calculate its widths and set its connected component
stackFace->link = new Abstraction(3);
#if defined EXPERIMENT
//keep track of the extra degree-3 node
num++;
#endif
CalculateWidths(stackFace);
stackFace->link->Component(component);
//access the elements of the triangle
s = stackFace->se();
//go in all directions from this node
for (int i = 0; i < 3; i++)
{
//get the face across this particular edge
SeDcdtFace *tempFace = (SeDcdtFace *)(s->sym()->fac());
//mark the first face and the previous face
SeDcdtFace *firstFace = stackFace;
SeDcdtFace *lastFace = stackFace;
//keep track of the cumulative angle since the last degree-3 node
float angle = 0;
//follow this chain until another degree-3 node is encountered
while (true)
{
//the number of adjacent faces across unconstrained edges not abstracted as degree-1
numAdjacent = 0;
//access the elements of this triangle
SeBase *s1 = tempFace->se();
//go through the edges of this triangle
for (int j = 0; j < 3; j++)
{
//get the degree of the face across this edge
int degree = Degree(s1->sym());
//if it's not blocked or degree-1, it's adjacent
if (!Blocked(s1) && (degree != 1))
{
numAdjacent++;
}
//move to the next edge
s1 = s1->nxt();
}
//if the current triangle is degree-3,
if (numAdjacent == 3)
{
//if it hasn't been abstracted yet, put it on the stack to deal with
if (Degree(tempFace) == UNABSTRACTED)
{
degree3.push() = tempFace;
}
//sets the original degree-3 node adjacent to this one
stackFace->link->Adjacent(i, tempFace);
//and sets the total angle between them
stackFace->link->Angle(i, angle);
//if there were no degree-2 nodes in between these,
if (lastFace == stackFace)
{
//get the elements of this triangle
SeBase *s1 = tempFace->se();
//go through the edges
for (int j = 0; j < 3; j++)
{
//find the edge between this triangle and the last one
if (s1->sym()->fac() == lastFace)
{
//the choke point is the length of the edge between these faces
float x1, y1, x2, y2, x3, y3;
TriangleVertices(s1, x1, y1, x2, y2, x3, y3);
stackFace->link->Choke(i, Length(x1, y1, x2, y2));
break;
}
//move to the next edge
s1 = s1->nxt();
}
}
//if there were degree-2 nodes between them,
else
{
//access the elements of the previous triangle
SeBase *s1 = lastFace->se();
float choke, width;
//go through its edges
for (int j = 0; j < 3; j++)
{
//find the direction of the original degree-3 node
if (lastFace->link->Adjacent(j) == stackFace)
{
//get the choke point to this node
choke = lastFace->link->Choke(j);
//get the width through this triangle between the two degree-3 nodes
width = lastFace->link->Width((s1->nxt()->sym()->fac() == tempFace) ? j : (j + 2) % 3);
//the choke point of the original degree-3 node is the lesser of the two
stackFace->link->Choke(i, Minimum(choke, width));
break;
}
//move to the next edge
s1 = s1->nxt();
}
}
//we've reached the other degree-3 node - stop following this chain
break;
}
//if the triangle is degree-2
else if (numAdjacent == 2)
{
//if the triangle wasn't abstracted before,
if (Degree(tempFace) == UNABSTRACTED)
{
//abstract it and set its widths and connected component
tempFace->link = new Abstraction(2);
CalculateWidths(tempFace);
tempFace->link->Component(component);
}
//access the elements of this triangle
SeBase *s1 = tempFace->se();
//go through the edges of the triangle
for (int j = 0; j < 3; j++)
{
//find the edge across which is the last triangle
if (s1->sym()->fac() == lastFace)
{
//set the angle back towards the original degree-3 node
tempFace->link->Angle(j, angle);
//and set the adjacent degree-3 node in that direction
tempFace->link->Adjacent(j, firstFace);
//if there are no nodes between this one and the degree-3 node,
if (lastFace == firstFace)
{
//the choke point is simply the length of the edge between them
float x1, y1, x2, y2, x3, y3;
TriangleVertices(s1, x1, y1, x2, y2, x3, y3);
tempFace->link->Choke(j, Length(x1, y1, x2, y2));
}
else
{
float choke, width;
//otherwise, access the elements of the last triangle
SeBase *s2 = lastFace->se();
//and go through the edges
for (int k = 0; k < 3; k++)
{
//we want the edge which leads back to the original degree-3 node
if ((s2->sym()->fac() == tempFace) || (lastFace->link->Adjacent(k) == NULL))
{
s2 = s2->nxt();
continue;
}
//get the associated choke point width
choke = lastFace->link->Choke(k);
//get the width between the two degree-3 nodes
if (s2->nxt()->sym()->fac() == tempFace)
{
width = lastFace->link->Width(k);
}
else
{
width = lastFace->link->Width((k + 2) % 3);
}
break;
}
//the choke point value is the lesser of these
tempFace->link->Choke(j, Minimum(choke, width));
}
break;
}
//move to the next edge
s1 = s1->nxt();
}
//calculate the angle between edges leading to degree-3 nodes
float currentAngle = 0;
//access the elements of this triangle again
s1 = tempFace->se();
//record the next triangle to move to
SeDcdtFace *nextFace = NULL;
//go through the edges of the triangle
for (int j = 0; j < 3; j++)
{
//get the degree of the adjacent triangle across this edge
int degree = Degree(s1->sym());
//if there is a tree component off this node, collapse it
if (!Blocked(s1) && (degree == 1)
&& (((SeDcdtFace *)s1->sym()->fac())->link->Component() == INVALID))
{
TreeCollapse(tempFace, (SeDcdtFace *)(s1->sym()->fac()), component);
}
//when finding the next face, record it
if (!Blocked(s1) && (s1->sym()->fac() != lastFace) && (degree != 1))
{
nextFace = (SeDcdtFace *)s1->sym()->fac();
}
//determine the degree of the triangle across the next edge
int degree2 = Degree(s1->nxt()->sym());
//if this edge and the next are the connecting edges, record the angle between them
if (!((degree == 0) || (degree == 1) || (degree2 == 0) || (degree2 == 1))
&& !Blocked(s1) && !Blocked(s1->nxt()))
{
float x1, y1, x2, y2, x3, y3;
TriangleVertices(s1, x1, y1, x2, y2, x3, y3);
currentAngle = abs(AngleBetween(x1, y1, x2, y2, x3, y3));
}
//move to the next edge
s1 = s1->nxt();
}
//increment the cumulative angle so far
angle += currentAngle;
//move to the next edge
lastFace = tempFace;
tempFace = nextFace;
}
//in other cases, report an error
else
{
sr_out.warning("ERROR: should only encounter degree-2 and degree-3 faces this way\n");
}
}
//follow another chain
s = s->nxt();
}
}
//get the next component
component++;
}
//Degree-2 faces (in a ring)
//go through the processing queue again
for (int i = 0; i < processing.size(); i++)
{
//get the current triangle
SeDcdtFace *currentFace = processing[i];
//only continue if it hasn't been abstracted yet
if (Degree(currentFace) != UNABSTRACTED)
{
continue;
}
//mark it as the first triangle in the ring
SeDcdtFace *firstFace = currentFace;
//the next triangle to visit
SeDcdtFace *nextFace = NULL;
//follow the ring of unabstracted faces
while (true)
{
//access the elements of the current triangle
SeBase *s = currentFace->se();
//go through its edges
for (int i = 0; i < 3; i++)
{
//record the degree of the triangle across this edge
int degree = Degree(s->sym());
//if there's a tree component off this face, collapse it
if (!Blocked(s) && (degree == 1))
{
TreeCollapse(currentFace, (SeDcdtFace *)s->sym()->fac(), component);
}
//when the next face is found in the ring, record it
if (!Blocked(s) && (degree == UNABSTRACTED))
{
nextFace = (SeDcdtFace *)s->sym()->fac();
}
//move to the next edge
s = s->nxt();
}
//abstract the current face, calculate its widths, and set its connected component
currentFace->link = new Abstraction(2);
CalculateWidths(currentFace);
currentFace->link->Component(component);
//access the triangle's elements again
s = currentFace->se();
//go through the edges again
for (int i = 0; i < 3; i++)
{
//if the adjacent triangle is part of the ring
if (!Blocked(s) && Degree(s->sym()) != 1)
{
//set the angle and choke values accordingly
currentFace->link->Angle(i, INFINITY);
currentFace->link->Choke(i, INFINITY);
}
//move to the next edge
s = s->nxt();
}
//if the start of the ring has been reencountered, stop following it
if (currentFace == nextFace)
{
break;
}
//move to the next face in the ring
currentFace = nextFace;
}
//move to the next ring (connected component)
component++;
}
#if defined EXPERIMENT
//output the number of degree-3 nodes to the output file
std::ofstream outFile("Data.txt", std::ios_base::app);
outFile << "Degree3: " << num << "\t";
outFile.close();
#endif
}
//Abstract into a tree
void SeDcdt::TreeAbstract(SeDcdtFace *first, int component)
{
//create a stack of the nodes in the tree
SrArray<SeDcdtFace *> tree;
//put the initial node on the tree
tree.push() = first;
//continue until the tree can't be expanded anymore
while (!tree.empty())
{
//get the next face off of the stack
SeDcdtFace *currentFace = tree.pop();
//get the elements of this triangle
SeBase *s = currentFace->se();
//go through its edges
for (int i = 0; i < 3; i++)
{
//ignore triangles across contrained edges
if (Blocked(s))
{
s = s->nxt();
continue;
}
//gets the face across that edge
SeDcdtFace *tempFace = (SeDcdtFace *)s->sym()->fac();
//if the adjacent triangle hasn't been dealt with, put it on the stack
if ((tempFace->link->Angle(0) == INVALID) && (tempFace->link->Angle(1) == INVALID)
&& (tempFace->link->Angle(2) == INVALID))
{
tree.push() = tempFace;
}
//move to the next edge
s = s->nxt();
}
//abstract the triangle if it hasn't already been
if (Degree(currentFace) == UNABSTRACTED)
{
//if the triangle hasn't been abstracted yet, abstract it and calculate its widths
currentFace->link = new Abstraction(1);
CalculateWidths(currentFace);
}
//access the triangle's elements again
s = currentFace->se();
//move through its edges
for (int i = 0; i < 3; i++)
{
//for each unconstrained edge,
if (!Blocked(s))
{
//set the angle and choke values accordingly
currentFace->link->Angle(i, INFINITY);
currentFace->link->Choke(i, INFINITY);
}
//move to the next edge
s = s->nxt();
}
//set the triangle's connected component
currentFace->link->Component(component);
}
}
//Collapse a tree onto a degree-2 node
void SeDcdt::TreeCollapse(SeDcdtFace *root, SeDcdtFace *currentFace, int component)
{
//mark edges we have dealt with
_mesh->begin_marking();
//starting with the root of the tree
_mesh->mark(root);
//create a stack of triangles to be processed
SrArray<SeDcdtFace *> tree;
//and a parallel one for angles of those triangles
SrArray<float> angles;
//put the current triangle on the stack to be processed
tree.push() = currentFace;
//and record that it is at angle 0 from the root
angles.push() = 0.0f;
//continue until all faces in the tree have been dealt with
while (!tree.empty())
{
//get the top face off the stack
currentFace = tree.pop();
//set its connected component
currentFace->link->Component(component);
//mark this face as dealt with
_mesh->mark(currentFace);
//access this face's elements
SeBase *s = currentFace->se();
//the angles to the other adjacent triangles
float angle[2];
int i;
//go through the edges
for (i = 0; i < 3; i++)
{
//the face across that edge
SeDcdtFace *tempFace = (SeDcdtFace *)s->sym()->fac();
//if this is the entry edge,
if (!Blocked(s) && _mesh->marked(tempFace))
{
//set the angle
currentFace->link->Angle(i, angles.pop());
//calculate the angle to the other adjacent triangles
float x1, y1, x2, y2, x3, y3;
TriangleVertices(s, x1, y1, x2, y2, x3, y3);
angle[0] = abs(AngleBetween(x1, y1, x2, y2, x3, y3)) + currentFace->link->Angle(i);
angle[1] = abs(AngleBetween(x2, y2, x3, y3, x1, y1)) + currentFace->link->Angle(i);
//set the adjacent triangle in that direction to the root
currentFace->link->Adjacent(i, root);
//if this is the triangle next to the root,
if (tempFace == root)
{
//the choke value is the length of the edge between them
currentFace->link->Choke(i, Length(x1, y1, x2, y2));
}
//if there are triangles between this and the root,
else
{
//get the elements of the current triangle
SeBase *s1 = tempFace->se();
float width, choke;
//go through its edges
for (int j = 0; j < 3; j++)
{
//get the choke value in that direction
float temp = tempFace->link->Choke(j);
//if it's a valid value,
if ((temp != INVALID) && (temp != INFINITY))
{
//record it
choke = temp;
//gets the width through that triangle between the root and current triangles
if (s1->nxt()->sym()->fac() == currentFace)
{
width = tempFace->link->Width(j);
}
else
{
width = tempFace->link->Width((j + 2) % 3);
}
break;
}
//move to the next edge
s1 = s1->nxt();
}
//the choke value of this triangle is the lesser of these values
currentFace->link->Choke(i, Minimum(width, choke));
}
break;
}
//move to the next edge
s = s->nxt();
}
//record where we left off
int base = i + 1;
//visit the other two adjacent triangles
for (int i = 0; i < 2; i++)
{
//move to the next edge
s = s->nxt();
//only deal with ones not across a constrained edge
if (Blocked(s))
{
continue;
}
//set the angle and choke values acoordingly
currentFace->link->Angle((i + base) % 3, INFINITY);
currentFace->link->Choke((i + base) % 3, INFINITY);
//put them on the tree for processing
tree.push() = (SeDcdtFace *)s->sym()->fac();
//also put the appropriate angle on the other tree
angles.push() = angle[i];
}
}
//finished collapsing the tree
_mesh->end_marking();
}
//deletes the information from the abstraction
void SeDcdt::DeleteAbstraction()
{
//get the first face in the mesh
SeDcdtFace *currentFace = _mesh->first()->fac();
//and record it
SeDcdtFace *firstFace = currentFace;
//go through the faces
do
{
//if the triangle is abstracted,
if (currentFace->link != NULL)
{
//free the abstraction information
delete currentFace->link;
currentFace->link = NULL;
}
//move to the next triangle
currentFace = currentFace->nxt();
}
//until reaching the first face again
while (currentFace != firstFace);
//mark the outside face as being invalid
outside = NULL;
}
//Abstract function definitions }

View File

@ -0,0 +1,157 @@
//Abstraction.h
//DJD: definition of Abstraction class {
#ifndef ABSTRACTION_H
#define ABSTRACTION_H
#include <float.h>
#include "FunnelDeque.h"
//defines an invalid entry, and "infinity", for our purposes
#define INVALID -1
#define INFINITY FLT_MAX
//prototypes for classes defined later
template <class T> class SeLinkFace;
class Abstraction;
typedef SeLinkFace<Abstraction> SeDcdtFace;
//class which contains information about our topological abstraction
class Abstraction
{
protected:
//list of adjacent nodes
//if this node is degree-1 in a tree, all will be NULL
//if it is degree-1 otherwise, one will be the root of the tree, the others will be NULL
//if it is degree-2 in a ring, all will be NULL
//if it is degree-2 otherwise, two will be degree-3 nodes on the ends of the corridor, the other will be NULL
//if it is degree-3, all will be degree-3 nodes on the end of the corridors
SeDcdtFace *m_CAdjacent[3];
//sum of interior angles of triangles between this one and the corresponding adjacent one
//INVALID if the corresponding adjacent node is NULL
float m_dAngle[3];
//width between the corresponding edge and the one counterclockwise to it, through the triangle
float m_dWidth[3];
//smallest width through the triangles between this one and the corresponding adjacent one
//INVALID if the corresponding adjacent node is NULL
float m_dChoke[3];
//degree of this node
int m_nDegree;
//connected component to which this triangle belongs
int m_nComponent;
//makes sure the index is in the range [0, 2]
bool CheckBounds(int n)
{
return ((n >= 0) && (n < 3));
}
public:
//creates the abstraction with given degree
Abstraction(int nDegree)
{
//starts all adjacents as NULL and other information as INVALID
for (int i = 0; i < 3; i++)
{
m_CAdjacent[i] = NULL;
m_dAngle[i] = INVALID;
m_dWidth[i] = INVALID;
m_dChoke[i] = INVALID;
}
m_nDegree = nDegree;
m_nComponent = INVALID;
}
//default destructor
~Abstraction()
{
}
//accessor for the degree of the node
int Degree()
{
return m_nDegree;
}
//mutator for the degree of the node
void Degree(int nDegree)
{
m_nDegree = nDegree;
}
//accessor for the connected component of the node
int Component()
{
return m_nComponent;
}
//mutator for the connected component of the node
void Component(int nComponent)
{
m_nComponent = nComponent;
}
//accessor for the adjacent node with index given
SeDcdtFace *Adjacent(int n)
{
return (CheckBounds(n)) ? m_CAdjacent[n] : NULL;
}
//mutator for the adjacent node with index given
void Adjacent(int n, SeDcdtFace *CAdjacent)
{
if (CheckBounds(n))
{
m_CAdjacent[n] = CAdjacent;
}
}
//accessor for the angle with index given
float Angle(int n)
{
return (CheckBounds(n)) ? m_dAngle[n] : INVALID;
}
//mutator for the angle with index given
void Angle(int n, float dAngle)
{
if (CheckBounds(n))
{
m_dAngle[n] = dAngle;
}
}
//accessor for the width with index given
float Width(int n)
{
return (CheckBounds(n)) ? m_dWidth[n] : INVALID;
}
//mutator for the width with index given
void Width(int n, float dWidth)
{
if (CheckBounds(n))
{
m_dWidth[n] = dWidth;
}
}
//accessor for the choke point width with index given
float Choke(int n)
{
return (CheckBounds(n)) ? m_dChoke[n] : INVALID;
}
//mutator for the choke point width with index given
void Choke(int n, float dChoke)
{
if (CheckBounds(n))
{
m_dChoke[n] = dChoke;
}
}
};
#endif
//definition of Abstraction class }

View File

@ -0,0 +1,100 @@
//Experiments.h
//DJD: definitions used in running experiments {
#ifndef EXPERIMENTS_H
#define EXPERIMENTS_H
//windows-dependent timing mechanism
#include <windows.h>
//for accurate timing of paths
class Timer
{
protected:
//time at which the timer was started
LARGE_INTEGER StartTime;
// LARGE_INTEGER StopTime;
//frequency of the counter
LONGLONG Frequency;
//calibration component - time between lines
LONGLONG Correction;
public:
//constructor
Timer()
{
//retrieves the frequency of the counter
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
//gets the relevant portion of that data
Frequency = freq.QuadPart;
LARGE_INTEGER StopTime;
//starts the timer
Start();
// Stop();
//and measures the time between these lines
QueryPerformanceCounter(&StopTime);
//stores the relevant part of that figure
Correction = StopTime.QuadPart - StartTime.QuadPart;
}
//start timing
void Start()
{
// Sleep(0);
//record the start time
QueryPerformanceCounter(&StartTime);
}
// void Stop()
// {
// QueryPerformanceCounter(&StopTime);
// }
//return the time since the start time in milliseconds
float GetDuration()
{
//get the current time from the timer
LARGE_INTEGER StopTime;
QueryPerformanceCounter(&StopTime);
//return the calculated duration since the timer was started
return (float)(StopTime.QuadPart - StartTime.QuadPart - Correction) * 1000.0f / Frequency;
}
};
//structure for holding data from the experiments
struct Data
{
//the total time the algorithm ran
float TotalTime;
//the time taken to construct channels, run funnel algorithms, etc.
float ConstructionTime;
//the length of the best path found to this point
float Length;
//the number of nodes searched at this point
int SearchNodes;
//the number of triangles used in funnel algorithms, etc.
int ConstructionNodes;
//the number of paths found so far
int Paths;
//constructor
Data()
{
//initializes all variables
Reset();
}
//initializes all variables to default values
void Reset()
{
TotalTime = 0.0f;
ConstructionTime = 0.0f;
Length = 0.0f;
SearchNodes = 0;
ConstructionNodes = 0;
Paths = 1;
}
};
#endif
//definitions used in running experiments }

View File

@ -0,0 +1,557 @@
//FunnelDeque.cpp
//DJD: definition of FunnelNode functions {
#include "precompiled.h"
#include "FunnelDeque.h"
#include <float.h>
#include <math.h>
//default constructor - initializes all
//member variables to default values
FunnelNode::FunnelNode()
{
m_CLeft = NULL;
m_CRight = NULL;
m_CPoint.set(FLT_MIN, FLT_MIN);
}
//constructor - initializes the point member
//variable to the coordinates passed
FunnelNode::FunnelNode(float x, float y)
{
m_CLeft = NULL;
m_CRight = NULL;
m_CPoint.set(x, y);
}
//constructor - initializes the point member
//variable to the one passed
FunnelNode::FunnelNode(const SrPnt2& point)
{
m_CLeft = NULL;
m_CRight = NULL;
m_CPoint.set(point.x, point.y);
}
//mutator for the left pointer
void FunnelNode::Left(FunnelNode *left)
{
m_CLeft = left;
}
//mutator for the right pointer
void FunnelNode::Right(FunnelNode *right)
{
m_CRight = right;
}
//accessor for the left pointer
FunnelNode *FunnelNode::Left()
{
return m_CLeft;
}
//accessor for the right pointer
FunnelNode *FunnelNode::Right()
{
return m_CRight;
}
//mutator for the point
void FunnelNode::Point(const SrPnt2& point)
{
m_CPoint.set(point.x, point.y);
}
//accessor for the point
SrPnt2 FunnelNode::Point()
{
SrPnt2 copy;
copy.set(m_CPoint.x, m_CPoint.y);
return copy;
}
//mutator for the point's coordinates
void FunnelNode::Point(float x, float y)
{
m_CPoint.set(x, y);
}
//accessor for the point's X coordinate
float FunnelNode::X()
{
return m_CPoint.x;
}
//accessor for the point's Y coordinate
float FunnelNode::Y()
{
return m_CPoint.y;
}
//definition of FunnelNode functions }
//DJD: definition of FunnelDeque functions {
//constructor - initializes the apex of the funnel
//to the point passed, and the unit radius
FunnelDeque::FunnelDeque(float x, float y, float r)
{
m_CApex = new FunnelNode(x, y);
m_CLeft = m_CApex;
m_CRight = m_CApex;
m_tApexType = Point;
m_dRadius = r;
}
//constructor - initializes the apex of the funnel
//to the coordinates passed, and the unit radius
FunnelDeque::FunnelDeque(const SrPnt2& point, float r)
{
m_CApex = new FunnelNode(point);
m_CLeft = m_CApex;
m_CRight = m_CApex;
m_tApexType = Point;
m_dRadius = r;
}
//destructor - deletes any funnel nodes in the deque
FunnelDeque::~FunnelDeque()
{
FunnelNode *current = m_CLeft;
while (current != NULL)
{
FunnelNode *temp = current;
current = current->Right();
delete temp;
}
}
//Adds a new point to the funnel, adds to the existing path if the apex moves
//type = RightTangent => point is being added on left side
//type = LeftTangent => point is being added on right side
//type = Point => point is the end of the channel
void FunnelDeque::Add(const SrPnt2& p, CornerType type, SrPolygon& path)
{
//the new funnel node containing the new point
FunnelNode *next = new FunnelNode(p);
//if it's being added on the left side
//(operations are just reversed for the other side)
if (type == RightTangent)
{
//loop until the point is added to the funnel
while (true)
{
//if the apex is the only point in the funnel,
//simply add the point to the appropriate side
if (m_CLeft == m_CRight)
{
next->Right(m_CApex);
m_CApex->Left(next);
m_CLeft = next;
break;
}
float x_1, y_1, x_2, y_2;
CornerType type_1, type_2;
//if there are no points on the left side of the funnel,
//get the wedge going to the opposite side of the apex
if (m_CLeft == m_CApex)
{
x_1 = m_CLeft->X();
y_1 = m_CLeft->Y();
x_2 = m_CLeft->Right()->X();
y_2 = m_CLeft->Right()->Y();
type_1 = m_tApexType;
type_2 = LeftTangent;
}
//otherwise get the wedge on this side of the apex
else
{
x_2 = m_CLeft->X();
y_2 = m_CLeft->Y();
x_1 = m_CLeft->Right()->X();
y_1 = m_CLeft->Right()->Y();
type_2 = RightTangent;
if (m_CLeft->Right() == m_CApex)
{
type_1 = m_tApexType;
}
else
{
type_1 = RightTangent;
}
}
//calculate the angle associated with this wedge
float wedge = Angle(x_1, y_1, type_1, x_2, y_2, type_2);
//also calculate the distance between these two points
float length1 = Distance(x_1, y_1, x_2, y_2);
//and that to the new point (special case for the modofied funnel algorithm)
float length2 = Distance(x_1, y_1, p.x, p.y);
//now get the wedge leading to the new point
x_1 = m_CLeft->X();
y_1 = m_CLeft->Y();
if (m_CApex == m_CLeft)
{
type_1 = m_tApexType;
}
else
{
type_1 = RightTangent;
}
x_2 = p.x;
y_2 = p.y;
type_2 = type;
//and the angle associated with it
float toPoint = Angle(x_1, y_1, type_1, x_2, y_2, type_2);
//compare the two angles
float diff = wedge - toPoint;
diff += (diff <= -PI) ? 2.0f * PI : (diff > PI) ? -2.0f * PI : 0.0f;
//if the existing wedge is counterclockwise of that to the point
if (diff < 0.0f)
{
//add the new point on this end of the funnel
m_CLeft->Left(next);
next->Right(m_CLeft);
m_CLeft = next;
break;
}
//if it is clockwise,
else
{
//check if we are popping the apex
if (m_CLeft == m_CApex)
{
//if the point being added is closer than the one being popped,
if (length2 < length1)
{
//make the new point the apex instead of the one on the opposite side
//(special case for the modified funnel algorithm)
float angle = Angle(m_CApex->X(), m_CApex->Y(), m_tApexType, p.x, p.y, type);
AddPoint(m_CApex->X(), m_CApex->Y(), m_tApexType, angle, path);
AddPoint(p.x, p.y, type, angle, path);
next->Right(m_CApex->Right());
m_CApex->Right()->Left(next);
delete m_CApex;
m_CApex = next;
m_CLeft = next;
m_tApexType = type;
break;
}
//otherwise,
else
{
//move the apex to the right and add the segment between
//the old and new apexes to the path so far
float angle = Angle(m_CApex->X(), m_CApex->Y(), m_tApexType,
m_CApex->Right()->X(), m_CApex->Right()->Y(), CornerType::LeftTangent);
AddPoint(m_CApex->X(), m_CApex->Y(), m_tApexType, angle, path);
AddPoint(m_CApex->Right()->X(), m_CApex->Right()->Y(), CornerType::LeftTangent, angle, path);
m_CApex = m_CApex->Right();
m_tApexType = LeftTangent;
}
}
//pop the leftmost point off of the funnel
FunnelNode *temp = m_CLeft;
m_CLeft = m_CLeft->Right();
m_CLeft->Left(NULL);
delete temp;
}
}
}
//otherwise, if adding a point on the right side,
//continue as before, with sides reversed
//adding a point also ends up here -
//we do this to pop off all the necessary points off of
//the right side of the funnel, so at the end we can just
//add to the path the points between the apex and the
//right side of the funnel (done at the end)
else
{
while (true)
{
if (m_CLeft == m_CRight)
{
next->Left(m_CApex);
m_CApex->Right(next);
m_CRight = next;
break;
}
float x_1, y_1, x_2, y_2;
CornerType type_1, type_2;
if (m_CRight == m_CApex)
{
x_1 = m_CRight->X();
y_1 = m_CRight->Y();
x_2 = m_CRight->Left()->X();
y_2 = m_CRight->Left()->Y();
type_1 = m_tApexType;
type_2 = RightTangent;
}
else
{
x_2 = m_CRight->X();
y_2 = m_CRight->Y();
x_1 = m_CRight->Left()->X();
y_1 = m_CRight->Left()->Y();
type_2 = LeftTangent;
if (m_CRight->Left() == m_CApex)
{
type_1 = m_tApexType;
}
else
{
type_1 = LeftTangent;
}
}
float wedge = Angle(x_1, y_1, type_1, x_2, y_2, type_2);
float length1 = Distance(x_1, y_1, x_2, y_2);
float length2 = Distance(x_1, y_1, p.x, p.y);
x_1 = m_CRight->X();
y_1 = m_CRight->Y();
if (m_CApex == m_CRight)
{
type_1 = m_tApexType;
}
else
{
type_1 = LeftTangent;
}
x_2 = p.x;
y_2 = p.y;
type_2 = type;
float toPoint = Angle(x_1, y_1, type_1, x_2, y_2, type_2);
float diff = wedge - toPoint;
diff += (diff <= -PI) ? 2.0f * PI : (diff > PI) ? -2.0f * PI : 0.0f;
if (diff < 0.0f)
{
if (m_CRight == m_CApex)
{
if (length2 < length1)
{
float angle = Angle(m_CApex->X(), m_CApex->Y(), m_tApexType, p.x, p.y, type);
AddPoint(m_CApex->X(), m_CApex->Y(), m_tApexType, angle, path);
AddPoint(p.x, p.y, type, angle, path);
next->Left(m_CApex->Left());
m_CApex->Left()->Right(next);
delete m_CApex;
m_CApex = next;
m_CRight = next;
m_tApexType = type;
break;
}
else
{
float angle = Angle(m_CApex->X(), m_CApex->Y(), m_tApexType,
m_CApex->Left()->X(), m_CApex->Left()->Y(), CornerType::RightTangent);
AddPoint(m_CApex->X(), m_CApex->Y(), m_tApexType, angle, path);
AddPoint(m_CApex->Left()->X(), m_CApex->Left()->Y(), CornerType::RightTangent, angle, path);
m_CApex = m_CApex->Left();
m_tApexType = RightTangent;
}
}
FunnelNode *temp = m_CRight;
m_CRight = m_CRight->Left();
m_CRight->Right(NULL);
delete temp;
}
else
{
m_CRight->Right(next);
next->Left(m_CRight);
m_CRight = next;
break;
}
}
}
//this is where the points between the apex
//and the right side o fthe funnel are added to the path
//when adding the final point to the channel
if (type == Point)
{
FunnelNode *Current = m_CApex;
while (Current != m_CRight)
{
float x_1, y_1, x_2, y_2, toPoint;
CornerType type_1, type_2;
type_1 = (Current == m_CApex) ? m_tApexType : CornerType::LeftTangent;
type_2 = (Current->Right() == m_CRight) ? CornerType::Point : CornerType::LeftTangent;
x_1 = Current->X();
y_1 = Current->Y();
Current = Current->Right();
x_2 = Current->X();
y_2 = Current->Y();
toPoint = Angle(x_1, y_1, type_1, x_2, y_2, type_2);
AddPoint(x_1, y_1, type_1, toPoint, path);
AddPoint(x_2, y_2, type_2, toPoint, path);
}
}
}
//returns the length of a path found by this funnel algorithm with the current radius
float FunnelDeque::Length(SrPolygon path)
{
//only paths with an even number of points are valid
if ((path.size() % 2) != 0)
{
return 0.0f;
}
float length = 0.0f;
//go through the straight segments and total their lengths
for (int i = 0; i < path.size(); i += 2)
{
float xDiff = path[i + 1].x - path[i].x;
float yDiff = path[i + 1].y - path[i].y;
length += sqrt(xDiff * xDiff + yDiff * yDiff);
}
//go through the curved segments and add their length
//(the difference of the angles of the
// surrounding segments, times the unit radius)
for (int i = 1; i < path.size() - 3; i += 2)
{
float angle1 = atan2(path[i + 1].y - path[i].y, path[i + 1].x - path[i].x);
float angle2 = atan2(path[i + 3].y - path[i + 2].y, path[i + 3].x - path[i + 2].x);
float diff = angle1 - angle2;
diff += (diff <= -PI) ? 2.0f * PI : (diff > PI) ? -2.0f * PI : 0.0f;
length += m_dRadius * abs(diff);
}
return length;
}
//returns the angle from x_1, y_1 to x_2, y_2, given the types
float FunnelDeque::Angle(float x_1, float y_1, CornerType type_1, float x_2, float y_2, CornerType type_2)
{
//simply calls the appropriate function based
//on the types of the two points
if (type_1 == Point)
{
return PointToTangent(x_1, y_1, x_2, y_2, (type_2 == RightTangent));
}
else if (type_2 == Point)
{
float angle = PointToTangent(x_2, y_2, x_1, y_1, (type_1 == LeftTangent));
return AngleRange(angle + PI);
}
else if (type_1 != type_2)
{
return ToTangentAlt(x_1, y_1, x_2, y_2, (type_1 == LeftTangent));
}
else
{
return ToTangent(x_1, y_1, x_2, y_2);
}
}
//adds a point to the path given a coordinate, its type,
//and the angle of the segment attached
void FunnelDeque::AddPoint(float x, float y, CornerType type, float angle, SrPolygon &path)
{
//if it's a point type, just add it unchanged
if (type == Point)
{
path.push().set(x, y);
}
//otherwise,
else
{
//move the unit radius from the point to one side or the other
float theta = AngleRange(angle + PI / ((type == LeftTangent) ? 2.0f : -2.0f));
path.push().set(x + cos(theta) * m_dRadius, y + sin(theta) * m_dRadius);
}
}
//takes an angle value and restricts it to the range (-PI, PI]
float FunnelDeque::AngleRange(float theta)
{
return ((theta > PI) ? (theta - 2.0f * PI) :
(theta <= -PI) ? (theta + 2.0f * PI) : theta);
}
//gets the distance between the points (x_1, y_1) and (x_2, y_2)
float FunnelDeque::Distance(float x_1, float y_1, float x_2, float y_2)
{
float x = x_2 - x_1;
float y = y_2 - y_1;
return sqrt(x * x + y * y);
}
//gets the angle of the line tangent to two points of the same type
float FunnelDeque::ToTangent(float x_1, float y_1, float x_2, float y_2)
{
return atan2(y_2 - y_1, x_2 - x_1);
}
//gets the angle of the line tangent to a point
//of type LeftTangent and one of type RightTangent
float FunnelDeque::ToTangentAlt(float x_1, float y_1, float x_2, float y_2, bool LeftRight)
{
float h = Distance(x_1, y_1, x_2, y_2) / 2.0f;
float l = sqrt(h * h - m_dRadius * m_dRadius);
float interior = atan(m_dRadius / l);
float absolute = atan2(y_2 - y_1, x_2 - x_1);
if (LeftRight)
{
interior = -interior;
}
return AngleRange(absolute + interior);
}
//gets the angle of the line going through one point
//and running tangent to another
float FunnelDeque::PointToTangent(float x_1, float y_1, float x_2, float y_2, bool Right)
{
float h = Distance(x_1, y_1, x_2, y_2);
float l = sqrt(h * h - m_dRadius * m_dRadius);
float interior = atan(m_dRadius / l);
float absolute = atan2(y_2 - y_1, x_2 - x_1);
if (Right)
{
interior = -interior;
}
return AngleRange(absolute + interior);
}
//prints the funnel (used for debugging)
void FunnelDeque::Print()
{
FunnelNode *Current = m_CLeft;
while (Current != NULL)
{
sr_out << "\t(" << Current->X() << ", " << Current->Y() << ")";
if (Current == m_CLeft)
{
sr_out << " <-- Left";
}
if (Current == m_CApex)
{
sr_out << " <-- Apex";
}
if (Current == m_CRight)
{
sr_out << " <-- Right";
}
sr_out << srnl;
Current = Current->Right();
}
}
//prints the path and then the funnel (used for debugging)
void FunnelDeque::Print(SrPolygon path)
{
sr_out << "Path = {";
for (int i = 0; i < path.size() - 1; i++)
{
SrPnt2 p = path[i];
sr_out << "(" << p.x << ", " << p.y << "), ";
}
if (path.size() > 0)
{
SrPnt2 p = path[path.size() - 1];
sr_out << "(" << p.x << ", " << p.y << ")";
}
sr_out << "}\n";
Print();
}
//definition of FunnelDeque functions }

View File

@ -0,0 +1,107 @@
//FunnelDeque.h
//DJD: definition of FunnelNode class {
#ifndef FUNNELDEQUE_H
#define FUNNELDEQUE_H
#include "sr_vec2.h"
#include "sr_polygon.h"
//define a floating point value for pi
#define PI 3.1415926535897932384626433832795f
//a node in the funnel deque
class FunnelNode
{
protected:
//left and right pointers (doubly-connected)
FunnelNode *m_CLeft;
FunnelNode *m_CRight;
//the vertex represented by this funnel node
SrPnt2 m_CPoint;
public:
//default constructor
FunnelNode();
//constructor - initializes the vertex coordinates
FunnelNode(float x, float y);
//constructor - initializes the vertex
FunnelNode(const SrPnt2& point);
//mutator for the left pointer
void Left(FunnelNode *left);
//mutator for the right pointer
void Right(FunnelNode *right);
//accessor for the left pointer
FunnelNode *Left();
//accessor for the right pointer
FunnelNode *Right();
//mutator for the vertex
void Point(const SrPnt2& point);
//accessor for the vertex
SrPnt2 Point();
//mutator for the vertex coordinates
void Point(float x, float y);
//accessor for the vertex x coordinate
float X();
//accessor for the vertex y coordinate
float Y();
};
//definition of FunnelNode class }
//DJD: definition of FunnelDeque class {
//the funnel deque used in the funnel algorithm
class FunnelDeque
{
public:
//different kinds of corners - point, left and right tangent
//(tangent to a circle around the corner, running along the side specified)
enum CornerType {Point, LeftTangent, RightTangent};
protected:
//left end, right end, and apex pointers for the deque
FunnelNode *m_CLeft;
FunnelNode *m_CRight;
FunnelNode *m_CApex;
//the corner type of the node at the apex
//(ones on the left side are all right tangent, ones on the right are all left tangent)
CornerType m_tApexType;
//radius of the unit for which we are finding the path
float m_dRadius;
//angle between the first and second points given their corner types
float Angle(float x_1, float y_1, CornerType type_1, float x_2, float y_2, CornerType type_2);
//constrains an angfle to the range (-PI, PI]
float AngleRange(float theta);
//returns the (Euclidean) distance between two points
float Distance(float x_1, float y_1, float x_2, float y_2);
//returns the angle between the given points
//(same as the angle to their tangents if they're the same type)
float ToTangent(float x_1, float y_1, float x_2, float y_2);
//returns the angle between tangents of the given points
//given whether the first is left tangent type, assuming the second is the opposite
float ToTangentAlt(float x_1, float y_1, float x_2, float y_2, bool LeftRight);
//returns the angle between the first point and a tangent to the second
//(the type of the second is specified)
float PointToTangent(float x_1, float y_1, float x_2, float y_2, bool Right);
//adds a given point to the path
void AddPoint(float x, float y, CornerType type, float angle, SrPolygon &path);
public:
//creates the funnel deque based on a starting point and radius
FunnelDeque(float x, float y, float r);
//copy constructor
FunnelDeque(const SrPnt2& point, float r);
//destructor
~FunnelDeque();
//adds a given point of a given type to the funnel deque, adding to the path if necessary
void Add(const SrPnt2& p, CornerType type, SrPolygon& path);
//prints the contents of the funnel deque
void Print();
//prints the contents of the path
void Print(SrPolygon path);
//calculates the length of the path for a unit of given radius
float Length(SrPolygon path);
};
#endif
//definition of FunnelDeque class }

159
source/dcdt/se/Location.cpp Normal file
View File

@ -0,0 +1,159 @@
//Location.cpp
#include "precompiled.h"
#include <cstdlib>
#include <math.h>
#include "se_dcdt.h"
#include <fstream>
//the list of unprocessed triangles
template <class T>
SrArray<SeLinkFace<T> *> SeLinkFace<T>::processing;
//DJD: Point location function definitions {
//initializes each sector midpoint to point to the triangle in which it is contained
void SeDcdt::InitializeSectors()
{
#if defined EXPERIMENT
//count the number of triangles
int num = 0;
#endif
//calculate the width and height of the triangulation
float width = _xmax - _xmin;
float height = _ymax - _ymin;
//calculate the width and height of each sector
sectorWidth = width / (float)xSectors;
sectorHeight = height / (float)ySectors;
//initialize all sectors to null
for (int i = 0; i < ySectors; i++)
{
for (int j = 0; j < xSectors; j++)
{
sectors[i][j] = NULL;
}
}
float x, y;
float x1, y1, x2, y2, x3, y3;
//go through the unprocessed triangles
for (int k = 0; k < SeDcdtFace::Faces(); k++)
{
//retrieve the next valid triangle
SeDcdtFace *currentFace = SeDcdtFace::Face(k);
if (currentFace == NULL)
{
continue;
}
#if defined EXPERIMENT
//keep track of the number of triangles
num++;
#endif
//get the minimum and maximum x and y values
TriangleVertices(currentFace, x1, y1, x2, y2, x3, y3);
float left = Min(x1, x2, x3);
float right = Max(x1, x2, x3);
float top = Min(y1, y2, y3);
float bottom = Max(y1, y2, y3);
//calculate the minimum and maximum x and y sector indices covered
int xIndexMin = (int)((left - _xmin + 0.5f * sectorWidth) / sectorWidth);
int xIndexMax = (int)((right - _xmin - 0.5f * sectorWidth) / sectorWidth);
int yIndexMin = (int)((top - _ymin + 0.5f * sectorHeight) / sectorHeight);
int yIndexMax = (int)((bottom - _ymin - 0.5f * sectorHeight) / sectorHeight);
//go through the covered sector midpoints
for (int i = yIndexMin; i <= yIndexMax; i++)
{
for (int j = xIndexMin; j <= xIndexMax; j++)
{
//calculate the location of the midpoint
x = ((float)j + 0.5f) * sectorWidth + _xmin;
y = ((float)i + 0.5f) * sectorHeight + _ymin;
//if the midpoint is in the current triangle,
if (InTriangle(currentFace, x, y))
{
//point the sector to that triangle
sectors[i][j] = currentFace;
}
}
}
}
#if defined EXPERIMENT
//if we are running an experiment, output the number of triangles to a data file
std::ofstream outFile("Data.txt", std::ios_base::app);
outFile << "Triangles: " << num << "\t";
outFile.close();
#endif
}
//check if the given point is within a given triangle
bool SeDcdt::InTriangle(SeDcdtFace *face, float x, float y)
{
//get the triangle's vertices
float x1, y1, x2, y2, x3, y3;
TriangleVertices(face, x1, y1, x2, y2, x3, y3);
//check that the point is counterclockwise of all edges
return ((Orientation(x1, y1, x2, y2, x, y) >= 0)
&& (Orientation(x2, y2, x3, y3, x, y) >= 0)
&& (Orientation(x3, y3, x1, y1, x, y) >= 0));
}
//uses the sectors to locate in which triangle the given point lies
SeTriangulator::LocateResult SeDcdt::LocatePoint(float x, float y, SeBase* &result)
{
//calculates the indices of the sector in which the point lies
int xIndex = (int)((x - _xmin) / sectorWidth);
int yIndex = (int)((y - _ymin) / sectorHeight);
//makes sure the indices are within the proper bounds
xIndex = (xIndex < 0) ? 0 : (xIndex >= xSectors) ? xSectors - 1 : xIndex;
yIndex = (yIndex < 0) ? 0 : (yIndex >= ySectors) ? ySectors - 1 : yIndex;
//retrieve the triangle in which that sector midpoint is located
SeFace *iniface = sectors[yIndex][xIndex];
//if it was invalid, start at the usual place
if (iniface == NULL)
{
iniface = _search_face();
}
//use this starting triangle to start point location
return _triangulator->locate_point(iniface, x, y, result);
}
//locate the given point starting at the usual place
SeTriangulator::LocateResult SeDcdt::LocatePointOld(float x, float y, SeBase* &result)
{
return _triangulator->locate_point(_search_face(), x, y, result);
}
//returns the maximum of the 3 values passed
float SeDcdt::Max(float a, float b, float c)
{
return ((a >= b) && (a >= c)) ? a :
((b >= a) && (b >= c)) ? b : c;
}
//returns the minimum of the 3 values passed
float SeDcdt::Min(float a, float b, float c)
{
return ((a <= b) && (a <= c)) ? a :
((b <= a) && (b <= c)) ? b : c;
}
//return a random value in the range of x values (used for testing)
float SeDcdt::RandomX()
{
return RandomBetween(_xmin, _xmax);
}
//return a random value in the range of y values (used for testing)
float SeDcdt::RandomY()
{
return RandomBetween(_ymin, _ymax);
}
//returns a random number between the values given
float SeDcdt::RandomBetween(float min, float max)
{
float base = (float)rand() / (float)RAND_MAX;
return (base * (max - min) + min);
}
//Point location function definitions }

2328
source/dcdt/se/Search.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

135
source/dcdt/se/SearchNode.h Normal file
View File

@ -0,0 +1,135 @@
//SearchNode.h
//DJD: definition of the SearchNode class {
#ifndef SEARCHNODE_H
#define SEARCHNODE_H
#include "se_dcdt.h"
//class used to store a state in the search functions
class SearchNode
{
protected:
//the (minimum) distance travelled since the start node
float m_dMinDistance;
//the (admissible and consistent) estimate of the distance to the goal
float m_dHeuristic;
//the closest point in this triangle to the goal
SrPnt2 m_CClosestPoint;
//the triangle (face) associated with this node
SeDcdtFace *m_CTriangle;
//the parent search node of this one
SearchNode *m_CBack;
//the number of open children
int m_nOpenChildren;
//the direction taken from the last node to this one
int m_nDirection;
public:
//constructor - initializes the member variables
SearchNode(float dMinDistance, float dHeuristic, SrPnt2 CClosestPoint, SeDcdtFace *CTriangle, SearchNode *CBack, int nDirection = INVALID)
{
m_dMinDistance = dMinDistance;
m_dHeuristic = dHeuristic;
m_CClosestPoint.x = CClosestPoint.x;
m_CClosestPoint.y = CClosestPoint.y;
m_CTriangle = CTriangle;
m_CBack = CBack;
m_nOpenChildren = 0;
m_nDirection = nDirection;
}
//returns the estimated minimum cost of a path from the start to the goal through this node
float f()
{
return (m_dMinDistance + m_dHeuristic);
}
//returns the estimated minimum cost of a path from the start to this node
float g()
{
return m_dMinDistance;
}
//returns the estimated minimum cost of a path from this node to the goal
float h()
{
return m_dHeuristic;
}
//returns (a copy of) the closest point in this triangle to the goal
SrPnt2 Point()
{
SrPnt2 temp;
temp.x = m_CClosestPoint.x;
temp.y = m_CClosestPoint.y;
return temp;
}
//returns the triangle associated with this node
SeDcdtFace *Triangle()
{
return m_CTriangle;
}
//returns the parent search node of this one
SearchNode *Back()
{
return m_CBack;
}
//returns the direction taken from the last node to this one
int Direction()
{
return m_nDirection;
}
//sets the number of open children
void OpenChild()
{
m_nOpenChildren++;
}
//closes a child
bool CloseChild()
{
return ((--m_nOpenChildren) <= 0);
}
//closes the current search node
//(deleting its parent if it has no other open children)
void Close()
{
if ((m_CBack != NULL) && m_CBack->CloseChild())
{
m_CBack->Close();
delete m_CBack;
m_CBack = NULL;
}
}
//returns the number of open children this node has
int OpenChildren()
{
return m_nOpenChildren;
}
//checks the ancestors of this node to see if a given triangle was already searched
bool Searched(SeDcdtFace *triangle)
{
SearchNode *current = m_CBack;
while (current != NULL)
{
if (current->Triangle() == triangle)
{
return true;
}
current = current->Back();
}
return false;
}
};
#endif
//definition of the SearchNode class }

248
source/dcdt/se/Utility.cpp Normal file
View File

@ -0,0 +1,248 @@
//Utility.cpp
//DJD: Helper function definitions {
#include "precompiled.h"
#include <math.h>
#include "se_dcdt.h"
//degree value for an unabstracted triangle
#define UNABSTRACTED -1
//the list of unprocessed triangles
template <class T>
SrArray<SeLinkFace<T> *> SeLinkFace<T>::processing;
//gets the vertex coordinates and whether or not the edges are blocked, for a face
void SeDcdt::TriangleInfo(SeDcdtFace *face, float &x1, float &y1, bool &e1,
float &x2, float &y2, bool &e2, float &x3, float &y3, bool &e3)
{
TriangleInfo(face->se(), x1, y1, e1, x2, y2, e2, x3, y3, e3);
}
//gets the vertex coordinates and whether or not the edges are blocked, in a certain order
void SeDcdt::TriangleInfo(SeBase *s, float &x1, float &y1, bool &e1,
float &x2, float &y2, bool &e2, float &x3, float &y3, bool &e3)
{
TriangleVertices(s, x1, y1, x2, y2, x3, y3);
TriangleEdges(s, e1, e2, e3);
}
//gets the vertex coordinates of a triangle, for a face
void SeDcdt::TriangleVertices(SeDcdtFace *face, float &x1, float &y1,
float &x2, float &y2, float &x3, float &y3)
{
TriangleVertices(face->se(), x1, y1, x2, y2, x3, y3);
}
//gets the vertex coordinates of a triangle, in a certain order
void SeDcdt::TriangleVertices(SeBase *s, float &x1, float &y1,
float &x2, float &y2, float &x3, float &y3)
{
x1 = ((SeDcdtVertex *)s->vtx())->p.x;
y1 = ((SeDcdtVertex *)s->vtx())->p.y;
s = s->nxt();
x2 = ((SeDcdtVertex *)s->vtx())->p.x;
y2 = ((SeDcdtVertex *)s->vtx())->p.y;
s = s->nxt();
x3 = ((SeDcdtVertex *)s->vtx())->p.x;
y3 = ((SeDcdtVertex *)s->vtx())->p.y;
}
//gets whether or not the edges of a triangle are blocked, for a face
void SeDcdt::TriangleEdges(SeDcdtFace *face, bool &e1, bool &e2, bool &e3)
{
TriangleEdges(face->se(), e1, e2, e3);
}
//gets whether or not the edges of a triangle are blocked, in a certain order
void SeDcdt::TriangleEdges(SeBase *s, bool &e1, bool &e2, bool &e3)
{
e1 = Blocked(s);
s = s->nxt();
e2 = Blocked(s);
s = s->nxt();
e3 = Blocked(s);
}
//gets the midpoint of a triangle
void SeDcdt::TriangleMidpoint(SeDcdtFace *face, float &x, float &y)
{
float x1, y1, x2, y2, x3, y3;
TriangleVertices(face, x1, y1, x2, y2, x3, y3);
x = (x1 + x2 + x3) / 3.0f;
y = (y1 + y2 + y3) / 3.0f;
}
//gets the midpoint of a triangle
SrPnt2 SeDcdt::TriangleMidpoint(SeDcdtFace *face)
{
float x1, y1, x2, y2, x3, y3;
TriangleVertices(face, x1, y1, x2, y2, x3, y3);
SrPnt2 p;
p.x = (x1 + x2 + x3) / 3.0f;
p.y = (y1 + y2 + y3) / 3.0f;
return p;
}
//gets whether or not an edge is constrained or bordering the outside face
bool SeDcdt::Blocked(SeBase *s)
{
return (((SeDcdtEdge *)s->edg())->is_constrained() || (s->sym()->fac() == outside));
}
//gets the degree of node a face has been abstracted to
int SeDcdt::Degree(SeBase *s)
{
return Degree(s->fac());
}
//gets the degree of node a face has been abstracted to
int SeDcdt::Degree(SeFace *face)
{
if (((SeDcdtFace *)face)->link == NULL)
{
return UNABSTRACTED;
}
return ((SeDcdtFace *)face)->link->Degree();
}
//gets the angle between the given vertex coordinates
float SeDcdt::AngleBetween(float x1, float y1, float x2, float y2, float x3, float y3)
{
float angle1 = (float)atan2(y1 - y2, x1 - x2);
float angle2 = (float)atan2(y3 - y2, x3 - x2);
float diff = angle1 - angle2;
diff += (diff <= -PI) ? 2.0f * PI : (diff > PI) ? -2.0f * PI : 0.0f;
return diff;
}
//gets the orientation of the vertices with coordinates given
//(0 for collinear, <0 for clockwise, >0 for counterclockwise)
float SeDcdt::Orientation(float x1, float y1, float x2, float y2, float x3, float y3)
{
return ((x1 * y2) + (x2 * y3) + (x3 * y1) - (x1 * y3) - (x2 * y1) - (x3 * y2));
}
//determines if an angle is accute
bool SeDcdt::IsAccute(float theta)
{
return (abs(theta) < (PI / 2.0f));
}
//determines if an angle is obtuse
bool SeDcdt::IsObtuse(float theta)
{
return (abs(theta) > (PI / 2.0f));
}
//calculates the distance between two points
float SeDcdt::Length(float x1, float y1, float x2, float y2)
{
float xDiff = x1 - x2;
float yDiff = y1 - y2;
return (sqrt(xDiff * xDiff + yDiff * yDiff));
}
//returns the smaller of the two arguments
float SeDcdt::Minimum(float a, float b)
{
return ((a < b) ? a : b);
}
//returns the larger of the two arguments
float SeDcdt::Maximum(float a, float b)
{
return ((a > b) ? a : b);
}
//returns the minimum distance between the two segments given
float SeDcdt::SegmentDistance(float A1x, float A1y, float A2x, float A2y,
float B1x, float B1y, float B2x, float B2y)
{
float d1 = PointSegmentDistance(A1x, A1y, B1x, B1y, B2x, B2y);
float d2 = PointSegmentDistance(A2x, A2y, B1x, B1y, B2x, B2y);
float d3 = PointSegmentDistance(B1x, B1y, A1x, A1y, A2x, A2y);
float d4 = PointSegmentDistance(B2x, B2y, A1x, A1y, A2x, A2y);
float min = Minimum(Minimum(d1, d2), Minimum(d3, d4));
return min;
}
//returns the minimum distance between the two segments given, at least r from the endpoints
float SeDcdt::SegmentDistance(float ls1x, float ls1y, float ls2x, float ls2y,
float ll1x, float ll1y, float ll2x, float ll2y, float r)
{
float theta = atan2(ls2y - ls1y, ls2x - ls1x);
float A1x = ls1x + cos(theta) * r;
float A1y = ls1y + sin(theta) * r;
float A2x = ls2x - cos(theta) * r;
float A2y = ls2y - sin(theta) * r;
theta = atan2(ll2y - ll1y, ll2x - ll1x);
float B1x = ll1x + cos(theta) * r;
float B1y = ll1y + sin(theta) * r;
float B2x = ll2x - cos(theta) * r;
float B2y = ll2y - sin(theta) * r;
return SegmentDistance(A1x, A1y, A2x, A2y, B1x, B1y, B2x, B2y);
}
//returns the minimum distance between the point and the line segment given
float SeDcdt::PointSegmentDistance(float x, float y, float x1, float y1, float x2, float y2)
{
if (!IsAccute(AngleBetween(x1, y1, x2, y2, x, y)))
{
return Length(x2, y2, x, y);
}
else if (!IsAccute(AngleBetween(x2, y2, x1, y1, x, y)))
{
return Length(x1, y1, x, y);
}
else
{
return PointLineDistance(x, y, x1, y1, x2, y2);
}
}
//returns the closest point on the segment given, at least r in from the endpoints, to the point given
SrPnt2 SeDcdt::ClosestPointOn(float x1, float y1, float x2, float y2, float x, float y, float r)
{
SrPnt2 point;
float distance;
//checks if the angles along this edge and to the point are accute
bool accute1 = IsAccute(AngleBetween(x1, y1, x2, y2, x, y));
bool accute2 = IsAccute(AngleBetween(x2, y2, x1, y1, x, y));
if (accute1 && accute2)
{
//if both are, get closest point on this line
distance = PointLineDistance(x, y, x1, y1, x2, y2);
float theta = atan2(y2 - y1, x2 - x1);
theta += (Orientation(x, y, x1, y1, x2, y2) > 0.0f) ? PI / -2.0f : PI / 2.0f;
theta += (theta <= -PI) ? 2.0f * PI : (theta > PI) ? -2.0f * PI : 0.0f;
point.set(x + cos(theta) * distance, y + sin(theta) * distance);
//if it's < r from either end, move it out to distance r
if (Length(point.x, point.y, x1, y1) < r)
{
accute2 = false;
}
else if (Length(point.x, point.y, x2, y2) < r)
{
accute1 = false;
}
}
if (!accute1 && accute2)
{
//get point distance r from (x2, y2) in the direction of (x1, y1)
float theta = atan2(y1 - y2, x1 - x2);
point.set(x2 + cos(theta) * r, y2 + sin(theta) * r);
}
else if (accute1 && !accute2)
{
//get point distance r from (x1, y1) in the direction of (x2, y2)
float theta = atan2(y2 - y1, x2 - x1);
point.set(x1 + cos(theta) * r, y1 + sin(theta) * r);
}
return point;
}
//Helper function definitions }

149
source/dcdt/se/Width.cpp Normal file
View File

@ -0,0 +1,149 @@
//Width.cpp
//DJD: Width function definitions {
#include "precompiled.h"
#include "se_dcdt.h"
#include <math.h>
//list of unprocessed triangles
template <class T>
SrArray<SeLinkFace<T> *> SeLinkFace<T>::processing;
//determines the distance between a point and the closest point to it on a line
float SeDcdt::PointLineDistance(float x, float y, float x1, float y1, float x2, float y2)
{
if (x1 == x2)
{
return abs(x - x1);
}
float rise = y2 - y1;
float run = x2 - x1;
float intercept = y1 - (rise / run) * x1;
float a = rise;
float b = -run;
float c = run * intercept;
return (abs(a * x + b * y + c) / sqrt(a * a + b * b));
}
//determines if an edge should be considered when calculating the width
//(if the base angles of the triangle formed by this edge joined with the "origin" point are both accute)
bool SeDcdt::Consider(float x, float y, float x1, float y1, float x2, float y2)
{
return ((IsAccute(AngleBetween(x, y, x1, y1, x2, y2)))
&& (IsAccute(AngleBetween(x, y, x2, y2, x1, y1))));
}
//Calculates the meaningful widths of a triangle
void SeDcdt::CalculateWidths(SeDcdtFace *face)
{
//if the triangle hasn't been abstracted yet, return
if (face->link == NULL)
{
return;
}
//goes through the vertices of the triangle
SeBase *s = face->se();
for (int i = 0; i < 3; i++)
{
//calculate and set the width between those edges
face->link->Width(i, TriangleWidth(s->nxt()));
s = s->nxt();
}
float x1, y1, x2, y2, x3, y3;
TriangleVertices(face, x1, y1, x2, y2, x3, y3);
}
//determine one width through a triangle
float SeDcdt::TriangleWidth(SeBase *s)
{
//get the coordinates of the triangle
float x, y, x1, y1, x2, y2;
TriangleVertices(s, x, y, x1, y1, x2, y2);
//if either base angle isn't accute, the width of that
//triangle is the length of the shorter edge
if (!IsAccute(AngleBetween(x, y, x1, y1, x2, y2)))
{
return Length(x, y, x1, y1);
}
else if (!IsAccute(AngleBetween(x, y, x2, y2, x1, y1)))
{
return Length(x, y, x2, y2);
}
else
{
//otherwise, calculate the upper bound on the width of the triangle
float CurrentWidth = Minimum(Length(x, y, x1, y1), Length(x, y, x2, y2));
//and calculate the actual value of the width
return SearchWidth(x, y, s->nxt()->sym(), CurrentWidth);
}
}
//checks the current triangle for bounds on the triangle width
float SeDcdt::SearchWidth(float x, float y, SeBase *s, float CurrentWidth)
{
//get the coordinates of the triangle
float x1, y1, x2, y2, x3, y3;
TriangleVertices(s, x1, y1, x2, y2, x3, y3);
//checks if the entrance edge should be considered
if (!Consider(x, y, x1, y1, x2, y2))
{
//if not, returns the width unmodified
return CurrentWidth;
}
//calculates the distance between this edge and the "origin" point
float Distance = PointLineDistance(x, y, x1, y1, x2, y2);
//if it's farther than the upper bound
if (Distance >= CurrentWidth)
{
return CurrentWidth;
}
//if the edge is constrained,
else if (Blocked(s->sym()))
{
//return this distance as the new upper bound
return Distance;
}
//gets the distance to the vertex opposite the entrance edge
Distance = Length(x, y, x3, y3);
//if this distance is less than the current upper bound,
if (Distance <= CurrentWidth)
{
//return this distance as the new upper bound
return Distance;
}
//otherwise, searches across the other two edges for bounds
CurrentWidth = SearchWidth(x, y, s->nxt()->sym(), CurrentWidth);
return SearchWidth(x, y, s->nxt()->nxt()->sym(), CurrentWidth);
}
//determines if a certain point (x, y) in face is at least distance r from any constraints
bool SeDcdt::ValidPosition(float x, float y, SeDcdtFace *face, float r)
{
//get the triangle's vertices
float x1, y1, x2, y2, x3, y3;
TriangleVertices(face, x1, y1, x2, y2, x3, y3);
//check if any are closer than r to (x, y)
if ((Length(x, y, x1, y1) < r) || (Length(x, y, x2, y2) < r) || (Length(x, y, x3, y3) < r))
{
return false;
}
//get the edges of the triangle
SeBase *s = face->se();
//go through them one-by-one
for (int i = 0; i < 3; i++)
{
//if there are any constraints within r of (x, y) across the current edge
if (SearchWidth(x, y, s->sym(), r) < r)
{
//return that this in an invalid position
return false;
}
//move to the next edge
s = s->nxt();
}
//if no constraints were found within r of (x, y), it is a valid position
return true;
}
//Width function definitions }

163
source/dcdt/se/data/ecol.se Normal file
View File

@ -0,0 +1,163 @@
SYMEDGE MESH DESCRIPTION
SymEdges 76
37 54 0 0 0
24 3 14 0 8
1 5 1 1 8
35 65 14 1 18
2 7 2 2 8
67 62 1 2 11
4 9 3 3 8
39 67 2 3 19
6 11 4 4 8
69 39 3 4 13
8 13 5 5 8
75 58 4 5 12
10 15 6 6 8
57 32 5 6 14
12 17 7 7 8
49 47 6 7 4
14 19 8 8 8
26 49 7 8 5
16 21 9 9 8
42 26 8 9 1
18 23 10 10 8
23 44 9 10 3
20 25 11 11 8
45 20 10 11 3
22 0 0 12 8
51 40 11 12 9
48 16 8 13 5
19 43 12 13 1
31 51 0 14 10
54 61 13 14 17
52 46 12 15 6
50 29 13 15 10
71 75 5 16 16
13 56 15 16 14
62 72 17 17 7
64 2 1 17 18
65 71 15 18 20
55 1 14 18 0
9 68 16 19 13
66 6 3 19 19
43 45 11 20 2
25 50 12 20 9
27 18 9 21 1
44 41 12 21 2
40 42 9 22 2
21 22 11 22 3
15 48 12 23 4
30 53 6 23 6
17 27 12 24 5
46 14 7 24 4
28 30 12 25 10
41 24 0 25 9
47 31 13 26 6
61 57 6 26 15
60 28 0 27 17
0 36 15 27 0
53 60 15 28 15
33 12 6 28 14
72 69 4 29 21
11 74 17 29 12
29 55 15 30 17
56 52 13 30 15
73 35 1 31 7
5 66 16 31 11
3 34 17 32 18
70 37 14 32 20
7 38 16 33 19
63 4 2 33 11
58 73 16 34 21
38 8 4 34 13
36 64 17 35 20
74 33 15 35 16
68 59 17 36 21
34 63 16 36 7
32 70 17 37 16
59 10 5 37 12
Vertices 18
51 -0.471118 -0.471118 0.000000 127 127 127 255
62 1.006899 -0.406455 0.000000 127 127 127 255
67 1.256315 0.027713 0.000000 127 127 127 255
9 1.265552 0.489593 0.000000 127 127 127 255
69 0.914523 0.748246 0.000000 127 127 127 255
75 0.572732 0.859097 0.000000 127 127 127 255
47 -0.147802 0.840622 0.000000 127 127 127 255
17 -0.840622 0.840622 0.000000 127 127 127 255
19 -1.182414 0.655870 0.000000 127 127 127 255
18 -1.487255 0.129327 0.000000 127 127 127 255
23 -1.441067 -0.286366 0.000000 127 127 127 255
40 -0.932998 -0.434168 0.000000 127 127 127 255
50 -0.907948 0.177278 0.000000 127 127 127 255
61 -0.390490 0.162904 0.000000 127 127 127 255
3 0.498831 -0.498831 0.000000 127 127 127 255
36 0.122178 0.138947 0.000000 127 127 127 255
73 1.135253 0.143426 -0.000000 127 127 127 255
70 0.702544 0.153150 0.000000 127 127 127 255
Edges 38
0 127 127 127 255
2 127 127 127 255
4 127 127 127 255
6 127 127 127 255
8 127 127 127 255
10 127 127 127 255
12 127 127 127 255
14 127 127 127 255
16 127 127 127 255
18 127 127 127 255
20 127 127 127 255
22 127 127 127 255
24 127 127 127 255
26 127 127 127 255
28 255 14 238 255
30 127 127 127 255
32 127 127 127 255
34 127 127 127 255
36 127 127 127 255
38 127 127 127 255
40 127 127 127 255
42 127 127 127 255
44 127 127 127 255
46 255 72 8 255
48 127 127 127 255
50 78 100 255 255
52 120 255 98 255
54 127 127 127 255
56 127 127 127 255
58 127 127 127 255
60 255 17 78 255
62 127 127 127 255
64 127 127 127 255
66 127 127 127 255
68 127 127 127 255
70 255 22 71 255
72 127 127 127 255
74 55 8 255 255
Faces 22
37 127 127 127 255
19 127 127 127 255
40 127 127 127 255
23 127 127 127 255
49 127 127 127 255
17 127 127 127 255
47 127 127 127 255
73 127 127 127 255
24 127 127 127 255
51 127 127 127 255
50 127 127 127 255
67 127 127 127 255
75 127 127 127 255
69 127 127 127 255
13 127 127 127 255
61 127 127 127 255
74 127 127 127 255
29 127 127 127 255
3 127 127 127 255
7 127 127 127 255
36 127 127 127 255
68 127 127 127 255

View File

@ -0,0 +1,24 @@
export ROOT = ../..
export INCLUDEDIR = -I $(ROOT)/ -I $(ROOT)/../sr/ -I $(ROOT)/../fltk/fltk-1.1.5rc34
export LIBDIR = -L $(ROOT)/lib -L $(ROOT)/../sr/lib -L $(ROOT)/../fltk/fltk-1.1.5rc3/lib
export LIBS = -lse -lsrfl -lsrgl -lsr -lfltk -lfltk_gl -lGL
DIRS = se semesh setut
export CC = g++
export CFLAGS = $(INCLUDEDIR)
export LFLAGS = $(LIBDIR) $(LIBS)
all:
@for dir in $(DIRS); do\
mkdir $$dir; cd $$dir; $(MAKE) -f ../makefile.$$dir; cd ..; \
done
clean:
-$(RM) core *.o *~ ../lib/* ../bin/*
@for dir in $(DIRS); do \
rm -r $$dir; \
done

View File

@ -0,0 +1,14 @@
SRCDIR = $(ROOT)/src/se/
LIB = $(ROOT)/lib/libse.a
CPPFILES := $(shell echo $(SRCDIR)*.cpp)
OBJFILES = $(CPPFILES:.cpp=.o)
OBJECTS = $(notdir $(OBJFILES))
$(LIB): $(OBJECTS)
ar -r $(LIB) $(OBJECTS)
%.o: $(SRCDIR)%.cpp
$(CC) -c $(CFLAGS) $< -o $@

View File

@ -0,0 +1,14 @@
SRCDIR = $(ROOT)/src/semesh/
BIN = $(ROOT)/bin/semesh
CPPFILES := $(shell echo $(SRCDIR)*.cpp)
OBJFILES = $(CPPFILES:.cpp=.o)
OBJECTS = $(notdir $(OBJFILES))
$(BIN): $(OBJECTS)
$(CC) $(OBJECTS) $(LFLAGS) -o $(BIN)
%.o: $(SRCDIR)%.cpp
$(CC) -c $(CFLAGS) $< -o $@

View File

@ -0,0 +1,14 @@
SRCDIR = $(ROOT)/src/setut/
BIN = $(ROOT)/bin/setut
CPPFILES := $(shell echo $(SRCDIR)*.cpp)
OBJFILES = $(CPPFILES:.cpp=.o)
OBJECTS = $(notdir $(OBJFILES))
$(BIN): $(OBJECTS)
$(CC) $(OBJECTS) $(LFLAGS) -o $(BIN)
%.o: $(SRCDIR)%.cpp
$(CC) -c $(CFLAGS) $< -o $@

70
source/dcdt/se/readme.txt Normal file
View File

@ -0,0 +1,70 @@
===============================================================
SR - Simulation and Representation Toolkit
(also known as the small scene graph toolkit)
Marcelo Kallmann 1995-2004
See file sr.h for compilation options and
important notes about programming conventions
===============================================================
==== LIBRARIES ====
- sr: stand alone SR library with all main classes
- srgl: OpenGL rendering of the SR scene nodes (www.opengl.org)
- srfl: FLTK-OpenGL viewers and other UI tools (www.fltk.org)
==== EXECUTABLES ====
- srmodel: demonstration and processing of SrModel
- srtest: several small programs to test/demonstrate
the toolkit. All tests are compiled in the
same executable, but only one test is called
from srtest_main.cpp.
- srxapp: Example application to start a project containing
a FLTK user interface and the 3D viewer SrViewer,
which uses FLTK and OpenGL.
==== VISUALC ====
- Configurations Debug and Release are the usual ones, and
configuration Compact is meant to build executables
without any dependencies on DLLs.
- Generated libs are: sr, srfl, srgl (release)
src, srflc, srglc (compact), and
srd, srfld, srgld (debug)
- Folder visualc6 contains projects for Visual C++ 6
- Folder visualc7 contains projects for Visual C++ .NET
- FLTK include and libs are set to: ..\..\fltk
==== LINUX ====
- the linux folder contains makefiles for compilation using
gnu g++ and gmake tools.
- Some makefiles will look for FLTK as specified in the
main makefile linux/makefile
- gmake must be called from inside the linux/ directory
==== TESTED PLATFORMS ====
- Windows 98 with Visual C++ 6.0
- Windows XP with Visual C++ .NET
- Linux with gmake and g++
==== KNOWN BUGS ====
- check if SrSnEditor::~SrSnEditor() bug is really fixed
- OpenGL: Transparency is currently disabled in SrViewer,
because it had side-effects with "solid" polygons.
- SrViewer: Need to review/fix camera control and view_all()
==== WISH LIST ====
- Compare performance of using double or floats in sr_bv_math.h
- Finish texture support in SrModel
- SrPolygon: make a better grow() method
- Make a good trackball manipulator scene node

31
source/dcdt/se/se.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "precompiled.h"
# include "se.h"
//================================= SeElement ====================================
SeElement::SeElement ()
{
_index=0;
_symedge=0;
_next=_prior=this;
}
SeElement* SeElement::_remove ()
{
_next->_prior = _prior;
_prior->_next = _next;
return this;
}
// same insert_prior() implementation as in SrListNode
SeElement* SeElement::_insert ( SeElement* n )
{
n->_prior = _prior;
_prior->_next = n;
n->_next = this;
_prior = n;
return n;
}
//=== End of File ===================================================================

199
source/dcdt/se/se.h Normal file
View File

@ -0,0 +1,199 @@
# ifndef SE_H
# define SE_H
/** \file se.h
* Sym Edge definitions
\code
******************************************************************************************
*
* SymEdge Mesh Data Structure
* Marcelo Kallmann 1996 - 2004
*
* SymEdge joins Guibas & Stolfi structure ideas with Mäntylä Euler operators.
* Several triangulation algorithms were implemented in the se toolkit
*
* References:
* 1. M. Kallmann, H. Bieri, and D. Thalmann, "Fully Dynamic Constrained Delaunay
* Triangulations", In Geometric Modelling for Scientific Visualization,
* G. Brunnett, B. Hamann, H. Mueller, L. Linsen (Eds.), ISBN 3-540-40116-4,
* Springer-Verlag, Heidelberg, Germany, pp. 241-257, 2003.
* 2. M. Kallmann, and D. Thalmann, "Star-Vertices: A Compact Representation
* for Planar Meshes with Adjacency Information", Journal of Graphics Tools,
* 2001. (In the comparisons table, the symedge was used and called as the
* "simplified quad-edge structure" )
* 3. M. Mäntylä, "An Introduction to Solid Modeling", Computer Science Press,
* Maryland, 1988.
* 4. L. Guibas and J. Stolfi, "Primitives for the Manipulation of General
* Subdivisions and the Computation of Voronoi Diagrams", ACM Transaction
* on Graphics, 4, 75-123, 1985.
*
* The structure is based on circular lists of SeElement for vertex/edge/face nodes,
* plus the connectivity information stored with SeBase class.
*
* Implementation History:
*
* 10/2004 - Becomes again SE lib, but with dependendy on the SR lib.
*
* 10/2003 - Integration to the SR library, which is the newer verion of FG.
*
* 08/2002 - SeGeo becomes abstract, and SeGeoPoint, SeGeoFloat, SeGeoPointFloat appear.
* - SeTriangulator now covers both constrained and conforming triangulations
* - SrClassManagerBase signature is no longer used
*
* 06/2001 - The library becomes again a standalone library.
*
* 09/2000 - Changed to always keep v/e/f information lists
* - New names starting with Se
*
* 05/2000 - Finally operator mg was done to convert triangle soups with genus>0
*
* 10/1999 - Adaptation to the FG library, using FgArray, FgListNode, FgTree, I/O, etc.
*
* 06/1998 - Abandoned the idea of using a base algorithm class MeshAlg
*
* 07/1997 - Automatic calculation of MaxindexValue
* - Import() and related virtual callbacks created
* - Triangulation now is done with virtual callbacks
*
* 07/1996 - First implementation
*
******************************************************************************************
\endcode */
//================================ Types and Constants ===============================
class SeMeshBase;
class SeBase;
class SeElement;
typedef unsigned int semeshindex; //!< internally used to mark elements (must be unsigned)
typedef SeElement SeVertex; //!< a vertex is an element
typedef SeElement SeEdge; //!< an edge is an element
typedef SeElement SeFace; //!< a face is an element
//=================================== SeElement =======================================
/*! SeElement contains the required information to be attached
to all vertices, edges and faces:
1. a reference to one (any) adjacent SeBase
2. pointers maintaining a circular list of all elements of
the same type (vertices, edges or faces)
3. an index that can be used by SeMeshBase to mark elements
To attach user-related information to an element:
1. SeElement must be derived and all user data is declared
inside the derived class.
2. A corresponding SrClassManager must be derived and
the required virtual methods must be re-written in order to
manage the derived SeElement class. The compare method
is not used. See also sr_class_manager.h. */
class SeElement
{ protected :
SeElement ();
public :
SeBase* se () const { return _symedge; }
SeElement* nxt() const { return _next; }
SeElement* pri() const { return _prior; }
private :
friend class SeMeshBase;
friend class SrClassManagerBase;
SeElement* _next;
SeElement* _prior;
SeBase* _symedge;
semeshindex _index;
SeElement* _remove ();
SeElement* _insert ( SeElement* n );
};
/*! The following define can be called in a user derived class of SeElement
to easily redefine public SeElement methods with correct type casts.
E stands for the element type, and S for the sym edge type. */
# define SE_ELEMENT_CASTED_METHODS(E,S) \
S* se() const { return (S*)SeElement::se(); } \
E* nxt() const { return (E*)SeElement::nxt(); } \
E* pri() const { return (E*)SeElement::pri(); }
/*! The following define can be used to fully declare a default user derived
class of SeElement, which contains no user data but correctly redefines
public SeElement methods with type casts. */
# define SE_DEFINE_DEFAULT_ELEMENT(E,S) \
class E : public SeElement \
{ public : \
SE_ELEMENT_CASTED_METHODS(E,S); \
E () {} \
E ( const E& e ) {} \
friend SrOutput& operator<< ( SrOutput& o, const E& e ) { return o; } \
friend SrInput& operator>> ( SrInput& i, E& e ) { return i; } \
friend int sr_compare ( const E* e1, const E* e2 ) { return 0; } \
};
//================================== SeBase ========================================
/*! Used to describe all adjacency relations of the mesh topology. The mesh itself is
composed of a net of SeBase elements linked together reflecting the vertex and
face loops. SymEdge is a short name for symetrical edge, as each SeBase has a
symetrical one incident to the same edge on the opposite face, and is given by
sym(). SeBase has local traverse operators permitting to change to any adjacent
symedge so to access any information stored on a vertex, edge or face. Symedges
are also used as parameters to the topological operators of SeMeshBase, which allow
modifying the mesh. */
class SeBase
{ public :
/*! Returns the next symedge adjacent to the same face. */
SeBase* nxt() const { return _next; }
/*! Returns the prior symedge adjacent to the same face. */
SeBase* pri() const { return _rotate->_next->_rotate; }
/*! Returns the next symedge adjacent to the same vertex. */
SeBase* rot() const { return _rotate; }
/*! Returns the prior symedge adjacent to the same vertex. */
SeBase* ret() const { return _next->_rotate->_next; }
/*! Returns the symmetrical symedge, sharing the same edge. */
SeBase* sym() const { return _next->_rotate; }
/*! Returns the element attached to the incident vertex. */
SeVertex* vtx() const { return _vertex; }
/*! Returns the element attached to the incident edge. */
SeEdge* edg() const { return _edge; }
/*! Returns the element attached to the incident face. */
SeFace* fac() const { return _face; }
private :
friend class SeMeshBase;
friend class SeElement;
SeBase* _next;
SeBase* _rotate;
SeVertex* _vertex;
SeEdge* _edge;
SeFace* _face;
};
/*! This is the template version of the SeBase class, that redefines the methods
of SeBase including all needed type casts to the user defined classes.
All methods are implemented inline just calling the corresponding method of
the base class but correctly applying type casts to convert default types
to user types.
Important Note: no user data can be stored in sym edges. This template class
is only used as a technique to correctly perform type casts. */
template <class V, class E, class F>
class Se : public SeBase
{ public :
Se* nxt() const { return (Se*)SeBase::nxt(); }
Se* pri() const { return (Se*)SeBase::pri(); }
Se* rot() const { return (Se*)SeBase::rot(); }
Se* ret() const { return (Se*)SeBase::ret(); }
Se* sym() const { return (Se*)SeBase::sym(); }
V* vtx() const { return (V*)SeBase::vtx(); }
E* edg() const { return (E*)SeBase::edg(); }
F* fac() const { return (F*)SeBase::fac(); }
};
//============================ End of File ===============================
# endif // SE_H

709
source/dcdt/se/se_dcdt.cpp Normal file
View File

@ -0,0 +1,709 @@
#include "precompiled.h"
# include <math.h>
# include <stdlib.h>
# include <string.h>
# include "sr_box.h"
# include "sr_geo2.h"
# include "se_dcdt.h"
//# define SR_USE_TRACE1 // insert polygon
//# define SR_USE_TRACE2 // init
//# define SR_USE_TRACE3 // insert polygon
//# define SR_USE_TRACE4 // remove polygon
//# define SR_USE_TRACE5 // funnel
//# define SR_USE_TRACE6 // translate
//# define SR_USE_TRACE7 // ray
//# define SR_USE_TRACE8 // tri manager
//# define SR_USE_TRACE9 // extract contours
# include "sr_trace.h"
//DJD: declaring static member variables {
template <class T>
SrArray<SeLinkFace<T> *> SeLinkFace<T>::processing;
template <class T>
int SeLinkFace<T>::current = 0;
//declaring static member variables }
//=============================== SeDcdt ==================================
SeDcdt::SeDcdt ()
{
_mesh = new SeDcdtMesh;
_triangulator = new SeTriangulator
( SeTriangulator::ModeConstrained,
_mesh,
new SeDcdtTriManager,
0.000001 /*epsilon*/ );
_first_symedge = 0;
_cur_search_face = 0;
_xmin = _xmax = _ymin = _ymax = 0;
_radius = -1;
}
SeDcdt::~SeDcdt ()
{
delete _triangulator;
delete _mesh;
}
void SeDcdt::get_mesh_edges ( SrArray<SrPnt2>* constr, SrArray<SrPnt2>* unconstr )
{
SeDcdtEdge *e, *ei;
SeDcdtSymEdge *s;
SrArray<SrPnt2>* pa;
if ( constr ) constr->size(0);
if ( unconstr ) unconstr->size(0);
e = ei = _mesh->first()->edg();
do { if ( e->is_constrained() ) pa=constr; else pa=unconstr;
if ( pa )
{ s = e->se(); pa->push() = s->vtx()->p;
s = s->nxt(); pa->push() = s->vtx()->p;
}
e = e->nxt();
} while ( e!=ei );
}
//================================================================================
//================================ save ==========================================
//================================================================================
bool SeDcdt::save ( SrOutput& out )
{
int elems = _polygons.elements();
out << "SeDcdt\n\n";
out << "# domain:" << (elems>0?1:0) << " polygons:" << (elems-1) << srnl << srnl;
out << "epsilon " << _triangulator->epsilon() << srnl << srnl;
if ( _radius!=-1 )
out << "init_radius " << _radius << srnl << srnl;
if ( elems==0 ) return true;
int i, id, psize;
int maxid = _polygons.maxid();
for ( id=0; id<=maxid; id++ )
{ if ( !_polygons[id] ) continue;
SeDcdtInsPol& p = *_polygons[id];
if ( id==0 )
{ out << "domain\n"; }
else
{ out << "polygon " << id;
if ( p.open ) out << " open";
out << srnl;
}
psize = p.size();
for ( i=0; i<psize; i++ )
{ out << p[i]->p;
if ( i==psize-1 ) out<<';'; else out<<srspc;
}
out << srnl << srnl;
}
return true;
}
//================================================================================
//================================ load ==========================================
//================================================================================
static void _read_pol ( SrInput& inp, SrPolygon& pol )
{
pol.size ( 0 );
while ( 1 )
{ inp.get_token();
if ( inp.last_token()==';' ) break;
inp.unget_token ();
inp >> pol.push();
}
}
bool SeDcdt::load ( SrInput& inp )
{
SrPolygon pol;
pol.capacity ( 64 );
SrArray<int> invids;
float epsilon = 0.00001f;
float radius = -1.0f;
int id, nextid=1;
// signature:
inp.comment_style ( '#' );
inp.get_token ();
if ( inp.last_token()!="SeDcdt") return false;
while ( 1 )
{ inp.get_token ();
if ( inp.finished() ) break;
SrString& s = inp.last_token();
if ( s=="epsilon" )
{ inp >> epsilon;
}
if ( s=="init_radius" )
{ inp >> radius;
}
else if ( s=="domain" )
{ _read_pol ( inp, pol );
pol.open ( false );
init ( pol, epsilon, radius );
}
else if ( s=="polygon" )
{ if ( num_polygons()==0 ) return false;
inp >> id;
while ( id>nextid ) { invids.push()=_polygons.insert(); nextid++; }
inp.get_token();
if ( inp.last_token()=="open" )
{ pol.open(true); }
else
{ inp.unget_token();
pol.open(false);
}
_read_pol ( inp, pol );
insert_polygon ( pol );
nextid++;
}
}
while ( invids.size() ) _polygons.remove(invids.pop());
return true;
}
//================================================================================
//================================ init ==========================================
//================================================================================
void SeDcdt::init ( const SrPolygon& domain , float epsilon, float radius )
{
SR_TRACE2 ( "Starting init()..." );
// Reset cur searched face:
_cur_search_face = 0;
// Clear structures if needed:
if ( _first_symedge )
{ _mesh->destroy(); _first_symedge=0; _polygons.init(); }
// Checks parameters:
if ( domain.size()<3 || domain.open() )
sr_out.fatal_error ( "SeDcdt::init(): domain polygon must be simple and closed!");
// Calculates an external polygon (the border) :
SR_TRACE2 ( "Calculating External Polygon for domain with "<<domain.size()<<" points..." );
SrBox b;
domain.get_bounding_box ( b );
_xmin=b.a.x; _xmax=b.b.x; _ymin=b.a.y; _ymax=b.b.y;
float dx = (_xmax-_xmin)/5.0f;
float dy = (_ymax-_ymin)/5.0f;
if ( radius>0 )
{ if ( dx<radius ) dx=radius;
if ( dy<radius ) dy=radius;
}
if ( radius!=0 )
{ _xmax+=dx; _xmin-=dx; _ymax+=dy; _ymin-=dy; }
// Creates the triangulated external square (the border):
SR_TRACE2 ( "Creating External Polygon..." );
_triangulator->epsilon ( epsilon );
_first_symedge = (SeDcdtSymEdge*)_triangulator->init_as_triangulated_square
( _xmin, _ymin, _xmax, _ymin, _xmax, _ymax, _xmin, _ymax );
_radius = radius;
// Inserts the vertices of the domain according to radius:
if ( radius==0 )
{ _using_domain = false;
}
else
{ _using_domain = true;
insert_polygon ( domain );
}
}
void SeDcdt::get_bounds ( float& xmin, float& xmax, float& ymin, float& ymax ) const
{
xmin=_xmin; xmax=_xmax; ymin=_ymin; ymax=_ymax;
}
//================================================================================
//=========================== insert polygon ====================================
//================================================================================
int SeDcdt::insert_polygon ( const SrPolygon& pol )
{
int i, i1, id;
SeVertex* v;
SeFace* sface;
SR_TRACE1 ( "Inserting entry in the polygon set..." ); // put in _polygons
id = _polygons.insert();
SeDcdtInsPol& ip = *_polygons[id];
ip.open = pol.open();
SR_TRACE1 ( "Inserting polygon points..." ); // insert vertices
sface = _search_face();
for ( i=0; i<pol.size(); i++ )
{ v = _triangulator->insert_point ( pol[i].x, pol[i].y, sface );
if ( !v ) sr_out.fatal_error ( "Search failure in _insert_polygon()." );
ip.push() = (SeDcdtVertex*)v;
sface = v->se()->fac();
}
// should we keep here collinear vertices (which are not corners)?
// they can be removed as Steiner vertices when removing another intersecting polygon
_cur_search_face=0; // Needed because edge constraint may call kef
SR_TRACE1 ( "Inserting polygon edges constraints..." ); // insert edges
for ( i=0; i<ip.size(); i++ )
{ i1 = (i+1)%ip.size();
if ( i1==0 && ip.open ) break; // do not close the polygon
if ( !_triangulator->insert_line_constraint ( ip[i], ip[i1], id ) )
sr_out.fatal_error ( "Unable to insert constraint in _insert_polygon()." );
}
return id;
}
int SeDcdt::polygon_max_id () const
{
return _polygons.maxid();
}
int SeDcdt::num_polygons () const
{
return _polygons.elements();
}
//================================================================================
//=========================== remove polygon ====================================
//================================================================================
void SeDcdt::_find_intermediate_vertices_and_edges ( SeDcdtVertex* vini, int oid )
{
SeDcdtEdge *e;
SeDcdtVertex *v;
SeDcdtSymEdge *s, *si;
_mesh->begin_marking ();
_varray.size(0); _earray.size(0); _stack.size(0);
// initialize stack with all constrained edges from v:
_varray.push() = vini;
_mesh->mark ( vini );
s = si = vini->se();
do { e = s->edg();
if ( e->has_id(oid) )
{ _stack.push() = s;
_earray.push() = s;
_mesh->mark ( e );
}
s = s->rot();
} while ( s!=si );
// advances untill all edges are reached:
while ( _stack.size() )
{ s = si = _stack.pop()->nxt();
v = s->vtx();
if ( !_mesh->marked(v) )
{ _varray.push() = v;
_mesh->mark ( v );
}
do { e = s->edg();
if ( !_mesh->marked(e) && e->has_id(oid) )
{ _stack.push() = s;
_earray.push() = s;
_mesh->mark ( e );
}
s = s->rot();
} while ( s!=si );
}
_mesh->end_marking ();
}
bool SeDcdt::_is_intersection_vertex
( SeDcdtVertex* v, int id, SeDcdtVertex*& v1, SeDcdtVertex*& v2 )
{
SeDcdtSymEdge *si, *s;
v1 = v2 = 0;
// at this point, it is guaranteed that only two constraints with index id
// are adjacent to v, because there is a test that ensures that only two
// constrained edges are incident to v in _remove_vertex_if_possible().
// (however these two constrained edges may have more than one id)
si = s = v->se();
do { if ( s->edg()->has_id(id) )
{ if ( v1==0 ) v1=s->nxt()->vtx();
else if ( v2==0 ) { v2=s->nxt()->vtx(); break; }
}
s = s->rot();
} while ( s!=si );
if ( v1==0 || v2==0 ) return false; // v is an extremity of an open constraint
double d = sr_point_segment_dist ( v->p.x, v->p.y, v1->p.x, v1->p.y, v2->p.x, v2->p.y );
return d<=_triangulator->epsilon()? true:false;
}
void SeDcdt::_remove_vertex_if_possible ( SeDcdtVertex* v, const SrArray<int>& vids )
{
SR_TRACE4 ( "Try to remove vertex with ref="<<vids.size()<<"..." );
// Easier case, vertex is no more used:
if ( vids.size()==0 ) // dangling vertex
{ SR_TRACE4 ( "Removing dangling vertex" );
_triangulator->remove_vertex(v);
return;
}
int i;
int num_of_incident_constrained_edges=0;
SeDcdtSymEdge *s;
s = v->se();
do { if ( s->edg()->is_constrained() ) num_of_incident_constrained_edges++;
s = s->rot();
} while ( s!=v->se() );
if ( num_of_incident_constrained_edges!=2 ) return;
// test if all polygons using the vertex no more need it:
SeDcdtVertex *v1, *v2;
SrArray<SeDcdtVertex*>& va = _varray2; // internal buffer
va.size ( 0 );
// (note that we can have vids.size()>1 at this point)
for ( i=0; i<vids.size(); i++ )
{ if ( !_is_intersection_vertex(v,vids[i],v1,v2) )
{ SR_TRACE4 ( "Not an intersection vertex!" );
return;
}
va.push()=v1; va.push()=v2;
}
SR_TRACE4 ( "Removing one intersection vertex..." );
_triangulator->remove_vertex(v);
// and recover all deleted constraints:
for ( i=0; i<vids.size(); i++ )
{ _triangulator->insert_line_constraint ( va[i*2], va[i*2+1], vids[i] );
}
}
void SeDcdt::remove_polygon ( int polygonid )
{
int i;
SR_TRACE4 ( "Entering remove_polygon..." );
if ( polygonid==0 )
sr_out.fatal_error("Domain cannot be removed by SeDcdt::remove_polygon()");
if ( polygonid<0 || polygonid>_polygons.maxid() )
sr_out.fatal_error("Invalid id sent to SeDcdt::remove_polygon()");
if ( !_polygons[polygonid] )
sr_out.fatal_error("SeDcdt::remove_polygon(): polygon already removed");
// _search_face can be invalidated so make it unavailable:
_cur_search_face = 0;
// Recuperate intermediate vertices inserted as Steiner points:
SR_TRACE4 ( "Recuperating full polygon..." );
_find_intermediate_vertices_and_edges ( _polygons[polygonid]->get(0), polygonid );
SR_TRACE4 ( "polygon has "<<_varray.size()<<" vertices " << "and "<<_earray.size()<<" edges." );
// Remove all ids which are equal to polygonid in the edge constraints
SR_TRACE4 ( "Updating edge constraints..." );
for ( i=0; i<_earray.size(); i++ )
{ _earray[i]->edg()->remove_id ( polygonid ); // remove all occurences
}
SR_TRACE4 ( "Removing free vertices..." );
for ( i=0; i<_varray.size(); i++ )
{ _varray[i]->get_references ( _ibuffer ); //vobs
SR_TRACE4 ( "Vertex "<<i<<": (" << _varray[i]->p << ")" );
_remove_vertex_if_possible ( _varray[i], _ibuffer );
}
_polygons.remove ( polygonid );
SR_TRACE4 ( "Finished." );
}
//================================================================================
//============================= get polygon ======================================
//================================================================================
void SeDcdt::get_polygon ( int polygonid, SrPolygon& polygon )
{
polygon.size(0);
if ( polygonid<0 || polygonid>_polygons.maxid() ) return;
if ( !_polygons[polygonid] ) return;
SeDcdtInsPol& p = *_polygons[polygonid];
int i;
polygon.size ( p.size() );
polygon.open ( p.open? true:false );
for ( i=0; i<p.size(); i++ )
polygon[i] = p[i]->p;
}
void SeDcdt::get_triangulated_polygon ( int polygonid, SrArray<SeDcdtVertex*>* vtxs, SrArray<SeDcdtEdge*>* edgs )
{
if ( polygonid<0 || polygonid>_polygons.maxid() ) return;
if ( !_polygons[polygonid] ) return;
_find_intermediate_vertices_and_edges ( _polygons[polygonid]->get(0), polygonid );
if ( vtxs ) *vtxs = _varray;
if ( edgs ) // need to convert symedges to edges
{ int i;
edgs->size ( _earray.size() );
for ( i=0; i<edgs->size(); i++ ) (*edgs)[i] = _earray[i]->edg();
}
}
//================================================================================
//=========================== ray intersection ===================================
//================================================================================
static bool _intersects ( double p1x, double p1y, double p2x, double p2y, SeDcdtSymEdge* s )
{
SeDcdtVertex* v1 = s->vtx();
SeDcdtVertex* v2 = s->nxt()->vtx();
return sr_segments_intersect ( p1x, p1y, p2x, p2y,
v1->p.x, v1->p.y, v2->p.x, v2->p.y );
}
void SeDcdt::ray_intersection ( float x1, float y1, float x2, float y2,
SrArray<int>& polygons, int depth )
{
SeTriangulator::LocateResult res;
SeBase *ses;
SeDcdtSymEdge *s;
polygons.size(0);
if ( depth==0 ) return;
double p1x=x1, p1y=y1, p2x=x2, p2y=y2;
res = _triangulator->locate_point ( _search_face(), p1x, p1y, ses );
if ( res==SeTriangulator::NotFound )
{ SR_TRACE7 ( "First point not found!" );
return;
}
s = (SeDcdtSymEdge*)ses;
_cur_search_face = s->fac();
// Find first intersection with the first triangle:
int i=0;
while ( !_intersects(p1x,p1y,p2x,p2y,s) )
{ s=s->nxt();
if ( i++==3 )
{ SR_TRACE7 ( "None intersections, ray in triangle!" );
return;
}
}
// Continues until end:
while ( true )
{ if ( s->edg()->is_constrained() )
{ //int r = rand() % e->ids_size;
SR_TRACE7 ( "found one!" );
SrArray<int>& a = s->edg()->ids;
for ( i=0; i<a.size(); i++ )
if ( polygons.size()<depth ) polygons.push()=a[i];
if ( depth>0 && polygons.size()>=depth )
{ SR_TRACE7 ( "depth reached!" ); return; }
}
s = s->sym();
if ( _intersects(p1x,p1y,p2x,p2y,s->nxt()) ) s=s->nxt();
else
if ( _intersects(p1x,p1y,p2x,p2y,s->pri()) ) s=s->pri();
else
{ SR_TRACE7 ( "Finished Ray!" ); return; }
}
}
void SeDcdt::_add_contour ( SeDcdtSymEdge* s, SrPolygon& vertices, SrArray<int>& pindices )
{
SeDcdtSymEdge* si=s;
pindices.push() = vertices.size();
SR_TRACE9 ( "Begin contour: "<<pindices.top() );
do { vertices.push() = s->vtx()->p;
_mesh->mark ( s->edg() );
do { s=s->rot(); } while ( !s->edg()->is_constrained() );
s = s->sym();
} while ( s!=si );
pindices.push() = vertices.size()-1;
SR_TRACE9 ( "End contour: "<<pindices.top() );
}
SeDcdtSymEdge* SeDcdt::_find_one_obstacle ()
{
SeDcdtSymEdge *s;
while ( 1 )
{ if ( _earray.empty() ) break;
s = _earray.pop();
if ( _mesh->marked(s->edg()) ) continue;
if ( s->edg()->is_constrained() ) return s;
_mesh->mark(s->edg());
s = s->sym()->nxt();
if ( !_mesh->marked(s->edg()) ) { _earray.push()=s; }
s = s->nxt();
if ( !_mesh->marked(s->edg()) ) { _earray.push()=s; }
}
return 0;
}
void SeDcdt::extract_contours ( SrPolygon& vertices, SrArray<int>& pindices, float x, float y )
{
SeTriangulator::LocateResult res;
SeBase *ses;
vertices.size(0);
pindices.size(0);
SR_TRACE9 ( "Begin Extract" );
res = _triangulator->locate_point ( _search_face(), x, y, ses );
if ( res==SeTriangulator::NotFound ) return;
_mesh->begin_marking();
_earray.size(0);
SeDcdtSymEdge *s, *si;
s = si = (SeDcdtSymEdge*)ses;
do { _earray.push() = s;
s = s->nxt();
} while ( s!=si );
while (1)
{ s = _find_one_obstacle ();
if ( !s ) break;
_add_contour ( s, vertices, pindices );
}
_mesh->end_marking();
SR_TRACE9 ( "End Extract" );
}
//================================================================================
//=========================== inside polygon ====================================
//================================================================================
static int interhoriz ( float px, float py, float p1x, float p1y, float p2x, float p2y )
{
if ( p1y>p2y ) { float tmp; SR_SWAP(p1x,p2x); SR_SWAP(p1y,p2y); } // swap
if ( p1y>=py ) return false; // not intercepting
if ( p2y<py ) return false; // not intercepting or = max
float x2 = p1x + (py-p1y) * (p2x-p1x) / (p2y-p1y);
return (px<x2)? true:false;
}
int SeDcdt::inside_polygon ( float x, float y , SrArray<int>* allpolys )
{
if ( _polygons.elements()<=1 ) return -1; // if there is only the domain, return.
int cont=0, i, size;
SeDcdtInsPol* pol;
SeDcdtVertex *v1, *v2;
if ( allpolys ) allpolys->size(0);
int polid=0;
if ( _using_domain ) polid++;
for ( ; polid<=_polygons.maxid(); polid++ )
{ pol = _polygons[polid];
if ( !pol ) continue;
size = pol->size();
for ( i=0; i<size; i++ )
{ v1 = pol->get(i);
v2 = pol->get( (i+1)%size );
cont ^= interhoriz ( x, y, v1->p.x, v1->p.y, v2->p.x, v2->p.y );
}
if (cont)
{ if ( allpolys ) allpolys->push()=polid;
else return polid;
}
}
if ( allpolys ) if ( allpolys->size()>0 ) return allpolys->get(0);
return -1;
}
int SeDcdt::pick_polygon ( float x, float y )
{
if ( _polygons.elements()==0 ) return -1;
if ( _using_domain && _polygons.elements()==1 ) return -1; // if there is only the domain, return.
SeTriangulator::LocateResult res;
SeBase *sse;
res = _triangulator->locate_point ( _search_face(), x, y, sse );
if ( res==SeTriangulator::NotFound ) return -1;
SeDcdtSymEdge *s = (SeDcdtSymEdge*)sse;
SrPnt2 pt(x,y);
float d1 = dist2 ( pt, s->vtx()->p );
float d2 = dist2 ( pt, s->nxt()->vtx()->p );
float d3 = dist2 ( pt, s->nxt()->nxt()->vtx()->p );
if ( d2<=d1 && d2<=d3 ) s=s->nxt();
if ( d3<=d1 && d3<=d2 ) s=s->nxt()->nxt();
SeDcdtSymEdge* k=s;
do { SrArray<int>& ids = k->edg()->ids;
if ( ids.size()>0 ) { if ( !_using_domain || ids[0]>0 ) return ids[0]; } // 0 is the domain
k=k->rot();
} while ( k!=s );
return -1;
}
//================================================================================
//=============================== search path ======================================
//================================================================================
bool SeDcdt::search_path ( float x1, float y1, float x2, float y2,
const SeDcdtFace* iniface, bool vistest )
{
// fast security test to ensure at least that points are not outside the border limits:
if ( x1<_xmin || x1>_xmax || x2<_xmin || x2>_xmax ) return false;
if ( !iniface ) iniface = _search_face();
bool found = _triangulator->search_path ( x1, y1, x2, y2, iniface, vistest );
// to optimize searching for next queries around the same point,
// set the next starting search face to the first channel face:
if ( _triangulator->get_channel_interior_edges().size()>0 )
_cur_search_face = (SeDcdtFace*)_triangulator->get_channel_interior_edges()[0]->fac();
return found;
}
//============================ End of File ===============================

493
source/dcdt/se/se_dcdt.h Normal file
View File

@ -0,0 +1,493 @@
# ifndef SE_DCDT_H
# define SE_DCDT_H
/** \file se_dcdt.h
* Dynamic Constrained Delaunay Triangulaiton
*/
# include "sr_set.h"
# include "sr_vec2.h"
# include "sr_polygon.h"
# include "se_triangulator.h"
//DJD: Including definition of abstraction and search node classes {
//#define EXPERIMENT
#define DEBUG
#define FIRST_PATH_FAST
#include "Abstraction.h"
#include "SearchNode.h"
#if defined EXPERIMENT
#include "Experiments.h"
#endif
//Including definition of abstraction and search node classes }
//============================ DCDT Mesh definitions ==================================
class SeDcdtVertex; // contains 2d coordinates
class SeDcdtEdge; // contains constraints ids
//DJD: changing definition of SeDcdtFace {
//class SeDcdtFace; // a default element
//changing definition of SeDcdtFace }
typedef Se < SeDcdtVertex, SeDcdtEdge, SeDcdtFace > SeDcdtSymEdge;
typedef SeMesh < SeDcdtVertex, SeDcdtEdge, SeDcdtFace > SeDcdtMesh;
//DJD: defining SeLinkFace {
//SE_DEFINE_DEFAULT_ELEMENT(SeDcdtFace,SeDcdtSymEdge);
template <class T>
class SeLinkFace : public SeElement
{
public:
//the number of new faces that haven't been dealt with
static int Faces() {return processing.size();}
//retrieve a particular unprocessed face
static SeLinkFace<T> *Face(int n) {return ((n < 0) || (n >= processing.size())) ? NULL : processing[n];}
//clear unprocessed face list, increment the global update
static void Clear() {while (!processing.empty()) processing.pop(); current++;}
//link to external information
T *link;
SE_ELEMENT_CASTED_METHODS(SeLinkFace, SeDcdtSymEdge);
//default constructor - set the link to null and initialize the face
SeLinkFace() : link (NULL) {Initialize();}
//copy constructor - initialize the link to that of the other face, and initialize it
SeLinkFace(const SeLinkFace& f) : link (f.link) {Initialize();}
//constructor - initialize the link to the value passed and initialize the face
SeLinkFace(T *l) : link (l) {Initialize();}
//destructor - deletes a non-null link, and nulls its entry in the unprocessed list if it's in there
~SeLinkFace() {if (link != NULL) delete link; if (update == current) processing[index] = NULL;}
friend SrOutput& operator<<(SrOutput& out, const SeLinkFace<T>& f) {return out;}
friend SrInput& operator>>(SrInput& inp, SeLinkFace<T>& f) {return inp;}
friend int sr_compare(const SeLinkFace<T>* f1, const SeLinkFace<T>* f2) {return 0;}
protected:
//the index of this face in the unprocessed list
int index;
//the update when this face was inserted in the unprocessed list
int update;
//initializes the face - puts it in the unprocessed list and sets its index and update
void Initialize() {index = processing.size(); processing.push() = this; update = current;}
//the unprocessed list
static SrArray<SeLinkFace<T> *> processing;
//the global update
static int current;
};
//defining SeLinkFace }
class SeDcdtVertex : public SeElement
{ public :
SrPnt2 p; // 2d coordinates
public :
SE_ELEMENT_CASTED_METHODS(SeDcdtVertex,SeDcdtSymEdge);
SeDcdtVertex () {}
SeDcdtVertex ( const SeDcdtVertex& v ) : p(v.p) {}
SeDcdtVertex ( const SrPnt2& pnt ) : p(pnt) {}
void set ( float x, float y ) { p.x=x; p.y=y; }
void get_references ( SrArray<int>& ids ); // get all constr edges referencing this vertex
friend SrOutput& operator<< ( SrOutput& out, const SeDcdtVertex& v );
friend SrInput& operator>> ( SrInput& inp, SeDcdtVertex& v );
friend int sr_compare ( const SeDcdtVertex* v1, const SeDcdtVertex* v2 ) { return 0; } // not used
};
class SeDcdtEdge : public SeElement
{ public :
SrArray<int> ids; // ids of all constraints sharing this edge
public :
SE_ELEMENT_CASTED_METHODS(SeDcdtEdge,SeDcdtSymEdge);
SeDcdtEdge () { }
SeDcdtEdge ( const SeDcdtEdge& e ) { ids=e.ids; }
bool is_constrained() const { return ids.size()>0? true:false; }
void set_unconstrained () { ids.size(0); }
bool has_id ( int id ) const;
bool has_other_id_than ( int id ) const;
bool remove_id ( int id );
void add_constraints ( const SrArray<int>& ids );
friend SrOutput& operator<< ( SrOutput& out, const SeDcdtEdge& e );
friend SrInput& operator>> ( SrInput& inp, SeDcdtEdge& e );
friend int sr_compare ( const SeDcdtEdge* e1, const SeDcdtEdge* e2 ) { return 0; } // not used
};
//================================ DCDT internal classes =====================================
class SeDcdtTriManager: public SeTriangulatorManager
{ public :
virtual void get_vertex_coordinates ( const SeVertex* v, double& x, double & y );
virtual void set_vertex_coordinates ( SeVertex* v, double x, double y );
virtual bool is_constrained ( SeEdge* e );
virtual void set_unconstrained ( SeEdge* e );
virtual void get_constraints ( SeEdge* e, SrArray<int>& ids );
virtual void add_constraints ( SeEdge* e, const SrArray<int>& ids );
};
class SeDcdtInsPol: public SrArray<SeDcdtVertex*> // keeps one "inserted polygon"
{ public :
char open;
public :
SeDcdtInsPol () { open=0; }
SeDcdtInsPol ( const SeDcdtInsPol& ob ) : SrArray<SeDcdtVertex*>((const SrArray<SeDcdtVertex*>&)ob) { open=0; }
friend SrOutput& operator<< ( SrOutput& o, const SeDcdtInsPol& ob );
friend SrInput& operator>> ( SrInput& in, SeDcdtInsPol& ob );
friend int sr_compare ( const SeDcdtInsPol* c1, const SeDcdtInsPol* c2 ) { return 0; } // not used
};
//=================================== DCDT class ========================================
/*! Mantains a dynamic constrained Delaunay triangulation of given polygons
for the purpose of path planning and other queries. */
class SeDcdt
{ private :
SeDcdtMesh* _mesh; // the mesh
SeTriangulator* _triangulator; // the used triangulator
SeDcdtSymEdge* _first_symedge; // symedge at the border, but not at the back face
SeDcdtFace* _cur_search_face; // a face near the last change in the mesh, 0 if not valid
SrSet<SeDcdtInsPol> _polygons; // inserted polygons, polygon 0 is always the domain
SrArray<SeDcdtVertex*> _varray; // internal buffer
SrArray<SeDcdtSymEdge*> _earray; // internal buffer
SrArray<SeDcdtSymEdge*> _stack; // internal buffer
SrArray<SeDcdtVertex*> _varray2; // internal buffer
SrArray<int> _ibuffer;
float _radius;
bool _using_domain;
float _xmin, _xmax, _ymin, _ymax;
public :
/*! Default constructor */
SeDcdt ();
/*! Destructor */
~SeDcdt ();
/*! Returns a pointer to the internal maintained mesh. The user can then
use low level methods of SeMesh for additional computation. However it
is the user responsability to not conflict with SeDcdt methods. */
const SeDcdtMesh* mesh() const { return _mesh; }
/*! Put in the given arrays the coordinates of the constrained and
unconstrained edges endpoints. Each two consecutive points in the
returned arrays give the first and end points of one dge.
Parameters can be null indicating that no data of that type is requested.
The two parameters are also allowed to point to the same array */
void get_mesh_edges ( SrArray<SrPnt2>* constr, SrArray<SrPnt2>* unconstr );
/*! Save the current dcdt by saving the list of all inserted obstacles.
Note that the polygons ids are preserved. */
bool save ( SrOutput& out );
/*! Destructs the current map and loads a new one */
bool load ( SrInput& inp );
/*! Initializes the triangulation with a domain polygon.
The domain is considered to be the constraint polygon with id 0; and can
be retrieved again by calling get_polygon(0).
An external box is automatically computed as an expanded bounding box of
the domain, where the expansion vector length is 1/5 of the bounding box sides.
This external box defines the border of the triangulation and its coordinates
can be retrieved with get_bounds().
Note that all polygons inserted afterwards must be inside the external box.
Parameter radius can be used, for instance, to have a security margin in order
to allow growing polygons without intersecting with the external box.
If parameter radius is >0, it is used as minimum length for the
expansion vector used to compute the external box.
Special Case: If radius is 0, the domain polygon is not inserted, and the
triangulation is initialized with border equal to the bounding box of domain.
Parameter epsilon is simply passed to the triangulator. */
void init ( const SrPolygon& domain, float epsilon, float radius=-1 );
/*! Internally, the border is generated containing the domain polygon.
This method allows to retrieve the coordinates of the border rectangle. */
void get_bounds ( float& xmin, float& xmax, float& ymin, float& ymax ) const;
/*! Inserts a polygon as a constraint in the CDT, returning its id.
In case of error, -1 is returned. Polygons can be open.
The returned id can be used to remove the polygon later on.
All kinds of intersections and overllapings are handled.
Collinear vertices are inserted. If not desired, method
SrPolygon::remove_collinear_vertices() should be called prior insertion. */
int insert_polygon ( const SrPolygon& polygon );
/*! Returns the max id currently being used. Note that a SrSet controls
the ids, so that the max id may not correspond to the number of
polygons inserted; ids values are not changed with polygon removal */
int polygon_max_id () const;
/*! Returns the number of inserted polygons, including the domain (if inserted). */
int num_polygons () const;
/*! Remove a previoulsy inserted polygon. false may be returned if the id is
not valid or if some internal error occurs. The domain cannot be removed with
this method. Steiner points may stay as part of other polygons if the
triangulation is in conforming mode. */
void remove_polygon ( int polygonid );
/*! Retrieves the original vertices of the given polygon (without collinear vertices).
If the returned polygon is empty, it means that polygonid is invalid. */
void get_polygon ( int polygonid, SrPolygon& polygon );
/*! Returns the vertices and edges used by the polygon in the triangulation.
Elements may be out of order, specially when they have intersections.
If an argument is a null pointer, nothing is done with it. */
void get_triangulated_polygon ( int polygonid, SrArray<SeDcdtVertex*>* vtxs, SrArray<SeDcdtEdge*>* edgs );
/*! Returns a list with the ids of the polygons having some edge traversed by the segment
[(x1,y1),(x2,y2)]. The length of the returned array will not be more than depth,
corresponding to 'depth' edges being crossed. Note: the id of a polygon will
appear both when the ray enters the polygon and when it leaves the polygon.
If depth is <0, no depth control is used.
This routine can also be used to fastly determine if a point is inside a polygon
by looking if the number of intersections is odd or even. */
void ray_intersection ( float x1, float y1, float x2, float y2, SrArray<int>& polygons, int depth );
/*! Returns all polygons describing the contours of an "eating virus" starting at x,y.
Array pindices contains, for each contour, the starting and ending vertex index,
which are sequentially stored in array vertices. */
void extract_contours ( SrPolygon& vertices, SrArray<int>& pindices, float x, float y );
/*! Returns the id of the first found polygon containing the given point (x,y),
or -1 if no polygons are found. The domain polygon, if used in init(), will not
be considered. The optional parameter allpolys can be sent to return all polygons
containing the point, and only the first one.
Note: this method does a linear search over each polygon, alternativelly, the
ray_intersection() method might also be used to detect polygon containement. */
int inside_polygon ( float x, float y, SrArray<int>* allpolys=0 );
/*! Returns the id of one polygon close to the given point (x,y), or -1 otherwise.
This method locates the point (x,y) in the triangulation and then takes the
nearest polygon touching that triangle.
The domain polygon, if used in init(), will not be considered. */
int pick_polygon ( float x, float y );
/*! Search for the channel connecting x1,y1 and x2,y2.
It simply calls SeTriangulator::search_path(), however here parameter iniface is optional */
bool search_path ( float x1, float y1, float x2, float y2,
const SeDcdtFace* iniface=0, bool vistest=false );
/*! Returns a reference to the list with the interior edges of the last channel
determined by a sussesfull call to search_path */
const SrArray<SeBase*>& get_channel_interior_edges () const
{ return _triangulator->get_channel_interior_edges(); }
/*! Returns a polygon describing the current channel, and thus, method find_path must
be succesfully called before to determine the channel to consider. */
void get_channel_boundary ( SrPolygon& channel ) { _triangulator->get_channel_boundary(channel); }
/*! Returns the canonical path, which is the path passing through the midpoint of
the channel interior edges. Method search_path must be succesfully called before
in order to determine the channel. The path is returned as an open polygon. */
void get_canonical_path ( SrPolygon& path ) { _triangulator->get_canonical_path(path); }
/*! Returns the shortest path inside the current channel using the funnel algorithm. */
void get_shortest_path ( SrPolygon& path ) { _triangulator->get_shortest_path(path); }
//DJD: prototypes for functions used in abstraction {
public:
#if defined DEBUG
//the triangle on which the mouse pointer currently resides
SeDcdtFace *cursor;
#endif
//adds abstraction information to the triangulation
void Abstract();
//deletes all abstraction information from the triangulation
void DeleteAbstraction();
private:
//the "outside" face of the triangulation
SeDcdtFace *outside;
//abstract an acyclic component of the triangulation graph
void TreeAbstract(SeDcdtFace *first, int component);
//collapse an acyclic portion into the degree-2 root given
void TreeCollapse(SeDcdtFace *root, SeDcdtFace *currentFace, int component);
//prototypes for functions used in abstraction }
//DJD: prototypes for functions used in width calculation {
private:
//calculates all widths through a triangle
void CalculateWidths(SeDcdtFace *face);
//calculates a particular width through a triangle
float TriangleWidth(SeBase *s);
//search across an edge for the width through the triangle
float SearchWidth(float x, float y, SeBase *s, float CurrentWidth);
//checks if the position given in the face given is at least r from any obstacle
bool ValidPosition(float x, float y, SeDcdtFace *face, float r);
//determines if angles (x, y)-(x1, y1)-(x2, y2) and (x, y)-(x2, y2)-(x1, y1) are accute
bool Consider(float x, float y, float x1, float y1, float x2, float y2);
//determines the minimum distance between the point (x, y) and the line going through (x1, y1)-(x2, y2)
float PointLineDistance(float x, float y, float x1, float y1, float x2, float y2);
//prototypes for functions used in width calculation }
//DJD: protptypes for functions used in point location {
public:
//perform initialization for sector-based point location on the unprocessed triangles
void InitializeSectors();
//checks if the given point is inside the given triangle
bool InTriangle(SeDcdtFace *face, float x, float y);
//use sector-based point location to find the face in which the given point resides
SeTriangulator::LocateResult LocatePoint(float x, float y, SeBase* &result);
//use regular point location to perform the above
SeTriangulator::LocateResult LocatePointOld(float x, float y, SeBase* &result);
//generate a random valid x value for testing point location
float RandomX();
//generate a random valid y value for testing point location
float RandomY();
private:
//the number of sectors in the vertical dimension
const static int ySectors = 10;
//the number of sectors in the horizontal dimension
const static int xSectors = 10;
//the width of a single sector
float sectorWidth;
//the height of a single sector
float sectorHeight;
//the array of sectors
SeDcdtFace *sectors[ySectors][xSectors];
//return the maximum of the 3 numbers passed
float Max(float a, float b, float c);
//return the maximum of the 3 numbers passed
float Min(float a, float b, float c);
//return a random value between those given
float RandomBetween(float min, float max);
//protptypes for functions used in point location }
//DJD: prototypes for functions used in abstracted space searching {
public:
#if defined EXPERIMENT
//find given path with TRA* and return experimental data
bool SearchPathFast(float x1, float y1, float x2, float y2, float r, Data *data, int numData);
//find given path with TA* and return experimental data
bool SearchPathBaseFast(float x1, float y1, float x2, float y2, float r, Data *data, int numData);
#else
//find given path with TRA*
bool SearchPathFast(float x1, float y1, float x2, float y2, float r);
//find given path with TA*
bool SearchPathBaseFast(float x1, float y1, float x2, float y2, float r);
#endif
//get the shortest path between the given points within the given channel of triangles
float GetShortestPath(SrPolygon& path, SrArray<SeBase *> Channel, float x1, float y1, float x2, float y2, float r);
//return the polygon around the current channel
SrPolygon GetChannelBoundary();
//return the current path
SrPolygon &GetPath();
private:
#if defined EXPERIMENT
//search for a path in an acyclic portion of the triangulation graph and count the triangles searched
bool Degree1Path(SrArray<SeBase *>& path, float x1, float y1, SeDcdtFace *startFace,
float x2, float y2, SeDcdtFace *goalFace, float r, int &nodes);
#else
//search for a path in an acyclic portion of the triangulation graph
bool Degree1Path(SrArray<SeBase *>& path, float x1, float y1, SeDcdtFace *startFace,
float x2, float y2, SeDcdtFace *goalFace, float r);
#endif
//search for a path along a degree-2 corridor
bool Degree2Path(SrArray<SeBase *>& path, SeDcdtFace *startFace, SeDcdtFace *nextFace,
SeDcdtFace *goalFace, float r);
//search for a path along a degree-2 corridor, crossing a degree-3 node
bool FollowLoop(SrArray<SeBase *>& path, SeDcdtFace *startFace, SeDcdtFace *nextFace,
SeDcdtFace *goalFace, SeDcdtFace *degree3, float r);
//walk from one face to a given adjacent face, in a direction specified
bool WalkBetween(SrArray<SeBase *>& path, SeDcdtFace *sourceFace, SeDcdtFace *destinationFace, float r, int direction = INVALID);
//determine if an object of given radius can cross the middle triangle between the adjacent ones given
bool CanCross(SeBase *first, SeBase *second, SeBase *third, float r);
bool CanCross(SeDcdtFace *middle, SeBase *end1, SeBase *end2, float r);
//checks if a unit of radius r can enter the path from the start and goal
bool ValidEndpoints(SrArray<SeBase *>& path, float x1, float y1, float x2, float y2, float r);
//checks if a unit has to cross the middle of the triangle to enter the path from the point given
bool ValidEndpoint(SeBase *s, float x, float y, bool left);
//determines if a unit can fit through the middle of the given triangle to get to the path
bool ValidEndpoint(SeDcdtFace *first, SeDcdtFace *second, float x, float y, float r);
//checks if a given path is valid for a unit of radius r
bool ValidPath(SrArray<SeBase *>& path, float x1, float y1, float x2, float y2, float r);
//returns the closest point in a triangle to a given point for a unit of given radius
SrPnt2 ClosestPointTo(SeDcdtFace *face, float x, float y, float r);
//given a search node, construct the channel it represents
SrArray<SeBase *> ConstructBaseChannel(SearchNode *goal);
//given a search node, construct a channel, filling in degree-1 and 2 nodes between those searched
void ConstructBasePath(SrArray<SeBase *> &path, SearchNode *goalNode, SeDcdtFace *start, SeDcdtFace *goal, int direction = INVALID);
//the current channel
SrArray<SeBase *> currentChannel;
//the current path
SrPolygon currentPath;
//the current start position
SrPnt2 startPoint;
//the current goal position
SrPnt2 goalPoint;
//prototypes for functions used in abstracted space searching }
//DJD: prototypes for utility functions {
public:
//retrieves the vertices and whether the edges are constrained, for a given triangle
void TriangleInfo(SeDcdtFace *face, float &x1, float &y1, bool &e1,
float &x2, float &y2, bool &e2, float &x3, float &y3, bool &e3);
void TriangleInfo(SeBase *s, float &x1, float &y1, bool &e1,
float &x2, float &y2, bool &e2, float &x3, float &y3, bool &e3);
//revireves the vertices for a given triangle
void TriangleVertices(SeDcdtFace *face, float &x1, float &y1,
float &x2, float &y2, float &x3, float &y3);
void TriangleVertices(SeBase *s, float &x1, float &y1,
float &x2, float &y2, float &x3, float &y3);
//retrieves whether the edges are constrained, for a given triangle
void TriangleEdges(SeDcdtFace *face, bool &e1, bool &e2, bool &e3);
void TriangleEdges(SeBase *s, bool &e1, bool &e2, bool &e3);
//returns the midpoint of a given triangle
void TriangleMidpoint(SeDcdtFace *face, float &x, float &y);
SrPnt2 TriangleMidpoint(SeDcdtFace *face);
//returns whether a given edge is either constrained, or borders the "outside" polygon
bool Blocked(SeBase *s);
//returns the degree of a triangle's node (UNABSTRACTED if it has not been mapped)
int Degree(SeBase *s);
int Degree(SeFace *face);
private:
//returns whether the given angle is accute
bool IsAccute(float theta);
//returns whether the given angle is obtuse
bool IsObtuse(float theta);
//returns the length of the line segment (x1, y1)-(x2, y2)
float Length(float x1, float y1, float x2, float y2);
//returns the smaller of the two values passed
float Minimum(float a, float b);
//returns the larger of the two values passed
float Maximum(float a, float b);
//determines the angle between (x1, y1)-(x2, y2)-(x3, y3)
float AngleBetween(float x1, float y1, float x2, float y2, float x3, float y3);
//returns 0 if the points are colinear, <0 if they are in clockwise order, and >0 for counterclockwise
float Orientation(float x1, float y1, float x2, float y2, float x3, float y3);
//returns the minimum distance between the line segments (A1x, A1y)-(A2x, A2y) and (B1x, B1y)-(B2x, B2y)
float SegmentDistance(float A1x, float A1y, float A2x, float A2y, float B1x, float B1y, float B2x, float B2y);
//returns the minimum distance between the line segments (ls1x, ls1y)-(ls2x, ls2y) and (ll1x, ll1y)-(ll2x, ll2y),
//at least r away from the segments' end points
float SegmentDistance(float ls1x, float ls1y, float ls2x, float ls2y, float ll1x, float ll1y, float ll2x, float ll2y, float r);
//returns the minimum distance between point (x, y) and segment (x1, y1)-(x2, y2)
float PointSegmentDistance(float x, float y, float x1, float y1, float x2, float y2);
//returns the closest point on the segment (x1, y1)-(x2, y2) to point (x, y),
//at least r away from th segments' midpoints
SrPnt2 ClosestPointOn(float x1, float y1, float x2, float y2, float x, float y, float r);
//prototypes for utility functions }
private :
SeDcdtFace* _search_face() { return _cur_search_face? _cur_search_face:_first_symedge->fac(); }
void _find_intermediate_vertices_and_edges ( SeDcdtVertex* vini, int oid );
bool _is_intersection_vertex ( SeDcdtVertex* v, int oid, SeDcdtVertex*& v1, SeDcdtVertex*& v2 );
void _remove_vertex_if_possible ( SeDcdtVertex* v, const SrArray<int>& vids );
void _add_contour ( SeDcdtSymEdge* s, SrPolygon& vertices, SrArray<int>& pindices );
SeDcdtSymEdge* _find_one_obstacle ();
};
//================================== End of File =========================================
# endif // SE_DCDT_H

View File

@ -0,0 +1,148 @@
#include "precompiled.h"
# include "se_dcdt.h"
//============================== SeDcdtVertex ==================================
static void insert_if_not_there ( SrArray<int>& a, int id )
{
int i=0;
for ( i=0; i<a.size(); i++ )
if ( a[i]==id ) return;
a.push() = id;
}
static void insert_ids ( SrArray<int>& a, const SrArray<int>& ids )
{
int i;
for ( i=0; i<ids.size(); i++ ) insert_if_not_there ( a, ids[i] );
}
void SeDcdtVertex::get_references ( SrArray<int>& ids )
{
SeDcdtEdge *e;
SeDcdtSymEdge *si, *s;
ids.size ( 0 );
si = s = se();
do { e = s->edg();
if ( e->is_constrained() ) insert_ids ( ids, e->ids );
s = s->rot();
} while ( s!=si );
}
SrOutput& operator<< ( SrOutput& out, const SeDcdtVertex& v )
{
return out << v.p;
}
SrInput& operator>> ( SrInput& inp, SeDcdtVertex& v )
{
return inp >> v.p;
}
//=============================== SeDcdtEdge ==================================
bool SeDcdtEdge::has_id ( int id ) const
{
for ( int i=0; i<ids.size(); i++ )
if ( ids[i]==id ) return true;
return false;
}
bool SeDcdtEdge::has_other_id_than ( int id ) const
{
for ( int i=0; i<ids.size(); i++ )
if ( ids[i]!=id ) return true;
return false;
}
bool SeDcdtEdge::remove_id ( int id ) // remove all occurences
{
bool removed=false;
int i=0;
while ( i<ids.size() )
{ if ( ids[i]==id )
{ ids[i]=ids.pop(); removed=true; }
else
i++;
}
return removed;
}
void SeDcdtEdge::add_constraints ( const SrArray<int>& cids )
{
int i;
for ( i=0; i<cids.size(); i++ ) ids.push() = cids[i];
}
SrOutput& operator<< ( SrOutput& out, const SeDcdtEdge& e )
{
return out << e.ids;
}
SrInput& operator>> ( SrInput& inp, SeDcdtEdge& e )
{
return inp >> e.ids;
}
//============================== SeDcdtTriManager ==================================
void SeDcdtTriManager::get_vertex_coordinates ( const SeVertex* v, double& x, double & y )
{
x = (double) ((SeDcdtVertex*)v)->p.x;
y = (double) ((SeDcdtVertex*)v)->p.y;
}
void SeDcdtTriManager::set_vertex_coordinates ( SeVertex* v, double x, double y )
{
((SeDcdtVertex*)v)->p.x = (float) x;
((SeDcdtVertex*)v)->p.y = (float) y;
}
bool SeDcdtTriManager::is_constrained ( SeEdge* e )
{
return ((SeDcdtEdge*)e)->is_constrained();
}
void SeDcdtTriManager::set_unconstrained ( SeEdge* e )
{
((SeDcdtEdge*)e)->set_unconstrained();
}
void SeDcdtTriManager::get_constraints ( SeEdge* e, SrArray<int>& ids )
{
ids = ((SeDcdtEdge*)e)->ids;
}
void SeDcdtTriManager::add_constraints ( SeEdge* e, const SrArray<int>& ids )
{
((SeDcdtEdge*)e)->add_constraints ( ids );
}
//============================== SeDcdtTriManager ==================================
SrOutput& operator<< ( SrOutput& o, const SeDcdtInsPol& ob )
{
if (ob.open) o << "(open) ";
return o;// << (const SrArray<SeDcdtVertex*>)ob;
}
SrInput& operator>> ( SrInput& in, SeDcdtInsPol& ob )
{
in.get_token();
/*
if ( in.last_token()[0]=='(' )
{ ob.open = true;
in.get_token();
in.get_token();
}
else
{ ob.open = false;
in.unget_token();
}
*/
return in;// >> (SrArray<SeDcdtVertex*>)ob;
}
//=============================== End of File ===============================

237
source/dcdt/se/se_mesh.cpp Normal file
View File

@ -0,0 +1,237 @@
#include "precompiled.h"
# include "sr_array.h"
# include "se_mesh.h"
# define IFTYPE(t,v,e,f) t==TypeVertex? (v) : t==TypeEdge? (e) : (f)
# define GETINFO(t,x) t==TypeVertex? (SeElement*)x->vertex : t==TypeEdge? (SeElement*)x->edge : (SeElement*)x->face
//================================= static functions =====================================
static void fatal_error ( char* msg )
{
sr_out.fatal_error ( msg );
}
//============================= SeMeshBase Private Methods =====================================
void SeMeshBase::_defaults ()
{
_first = 0;
_op_last_msg = OpNoErrors;
_curmark = 0;
_marking = false;
_indexing = false;
_vertices = _edges = _faces = 0;
}
SeBase* SeMeshBase::_op_error ( OpMsg m )
{
_op_last_msg = (OpMsg)m;
if ( _output_op_errors )
{ sr_out << "Error in SeMeshBase operation: "
<< translate_op_msg(m) << srnl;
}
return (SeBase*) 0;
}
//============================ SeMeshBase general =====================================
semeshindex se_index_max_value = ((semeshindex)0)-1; // This is the unsigned greatest value;
SeMeshBase::SeMeshBase ( SrClassManagerBase* vtxman, SrClassManagerBase* edgman, SrClassManagerBase* facman )
{
_defaults ();
_output_op_errors = 0;
if ( !vtxman || !edgman || !facman )
fatal_error ( "SeMeshBase::SeMeshBase(): null pointer received!" );
_vtxman = vtxman;
_edgman = edgman;
_facman = facman;
_op_check_parms = 0;
}
SeMeshBase::~SeMeshBase ()
{
destroy ();
_vtxman->unref();
_edgman->unref();
_facman->unref();
}
/* from [mantyla] :
v-e+f = h0-h1+h2 ( Betti numbers )
h0 = shells
h1 = 2 * genus ( every closed curve cut a part of the surface away )
h2 = orientability = h0
==> v-e+f = s-2g+s = 2s-2g = 2(s-g), if s==1, => v-e+f=2-2g */
int SeMeshBase::euler () const
{
return _vertices - _edges + _faces;
}
int SeMeshBase::genus () const
{
return (_vertices-_edges+_faces-2)/(-2);
}
int SeMeshBase::elems ( ElemType type ) const
{
switch ( type )
{ case TypeVertex : return _vertices;
case TypeEdge : return _edges;
case TypeFace : return _faces;
}
return 0;
}
void SeMeshBase::invert_faces ()
{
SeBase *se;
SeElement *edg, *edgi;
int i, j, symedges = _edges*2;
if ( !_first ) return;
SrArray<SeBase*> nse(symedges,symedges);
SrArray<SeBase*> nxt(symedges,symedges);
SrArray<SeBase*> rot(symedges,symedges);
SrArray<SeVertex*> vtx(symedges,symedges);
i=0;
edg = edgi = _first->edg();
do { se = edg->se();
for ( j=0; j<2; j++ )
{ if ( j==1 ) se = se->sym();
vtx[i] = se->nxt()->vtx();
nxt[i] = se->pri();
rot[i] = se->nxt()->sym();
nse[i] = se;
i++;
}
edg = edg->nxt();
} while ( edg!=edgi );
for ( i=0; i<symedges; i++ )
{ //fg_out<<i<<" "<<(int)nse[i]<<fgnl;
nse[i]->_next = nxt[i];
nse[i]->_rotate = rot[i];
nse[i]->_vertex = vtx[i];
vtx[i]->_symedge = nse[i];
}
}
int SeMeshBase::vertices_in_face ( SeBase *e ) const
{
int i;
SeBase *x=e->nxt();
for ( i=1; x!=e; i++ ) x=x->nxt();
return i;
}
int SeMeshBase::vertex_degree ( SeBase *e ) const
{
int i=1;
SeBase *x;
for ( x=e->rot(); x!=e; i++ ) x=x->rot();
return i;
}
float SeMeshBase::mean_vertex_degree () const
{
if ( !_vertices ) return 0;
SeVertex* vi = _first->vtx();
SeVertex* v = vi;
float deg=0;
do { deg += vertex_degree(v->se());
v = v->nxt();
} while ( v!=vi );
return deg / (float)_vertices;
}
//============================ SeMeshBase marking =====================================
// replace _marking and _indexing by _mark_status !!
void SeMeshBase::_normalize_mark () // private method
{
SeElement *ei, *e;
_curmark=1;
if ( !_first ) return;
ei=e=_first->vtx(); do { e->_index=0; e=e->nxt(); } while ( e!=ei );
ei=e=_first->edg(); do { e->_index=0; e=e->nxt(); } while ( e!=ei );
ei=e=_first->fac(); do { e->_index=0; e=e->nxt(); } while ( e!=ei );
}
void SeMeshBase::begin_marking ()
{
if ( _indexing || _marking )
fatal_error("SeMeshBase::begin_marking() not allowed as marking or indexing is already in use!");
_marking = true;
if ( _curmark==se_index_max_value )
{ _normalize_mark ();
}
else _curmark++;
}
void SeMeshBase::end_marking ()
{
_marking = false;
}
bool SeMeshBase::marked ( SeElement* e )
{
if ( !_marking ) fatal_error ( "SeMeshBase::marked(e): marking is not active!\n" );
return e->_index==_curmark? true:false;
}
void SeMeshBase::mark ( SeElement* e )
{
if ( !_marking ) fatal_error ( "SeMeshBase::mark(e): marking is not active!\n" );
e->_index = _curmark;
}
void SeMeshBase::unmark ( SeElement* e )
{
if ( !_marking ) fatal_error ( "SeMeshBase::unmark(e): marking is not active!\n");
e->_index = _curmark-1;
}
//============================ SeMeshBase indexing =====================================
void SeMeshBase::begin_indexing ()
{
if ( _marking || _indexing )
fatal_error("SeMeshBase::begin_indexing() not allowed as marking or indexing is already in use!");
_indexing = true;
}
void SeMeshBase::end_indexing ()
{
_indexing = false;
_normalize_mark ();
}
semeshindex SeMeshBase::index ( SeElement* e )
{
if ( !_indexing ) fatal_error ("SeMeshBase::index(e): indexing is not active!");
return e->_index;
}
void SeMeshBase::index ( SeElement* e, semeshindex i )
{
if ( !_indexing ) fatal_error ("SeMeshBase::index(e,i): indexing is not active!");
e->_index = i;
}
//=== End of File ===================================================================

419
source/dcdt/se/se_mesh.h Normal file
View File

@ -0,0 +1,419 @@
# ifndef SE_MESH_H
# define SE_MESH_H
/** \file se_mesh.h
* Symmetrical Edge Mesh Data Structure */
# include "sr_array.h"
# include "sr_input.h"
# include "sr_output.h"
# include "sr_class_manager.h"
# include "se.h"
// ====================================== SeMeshBase ======================================
// SeMeshBase documentation in the end of this file
class SeMeshBase
{ public :
/*! Enumerator with the possible results of check_all(). */
enum ChkMsg { ChkOk, //!< The check_all() method had success
ChkNxtError, //!< Some face loop defined by the nxt() operators is wrong
ChkRotError, //!< Some vertex loop defined by the rot() operators is wrong
ChkVtxError, //!< Vertex referencing is wrong
ChkEdgError, //!< Edge referencing is wrong
ChkFacError //!< Face referencing is wrong
};
/*! Enumerator with possible errors raised during the use of the topological operators. */
enum OpMsg { OpNoErrors, //!< No Errors occured with operators
OpMevParmsEqual, //!< Parms in mev are equal
OpMevNotSameVtx, //!< Parms in mev not in the same vertex
OpMefNotSameFace, //!< Parms in mef not in the same face
OpKevLastEdge, //!< Cannot delete the last edge with kev
OpKevManyEdges, //!< More then one edge in parm vertex in kev
OpKevOneEdge, //!< Vertex of parm has only one edge in kev
OpKefSameFace, //!< Parm in kef must be between two different faces
OpMgUncompatibleFaces,//!< Parms are adjacent to faces with diferent number of vertices
OpMgSameFace, //!< Parm in mg must be between two different faces
OpFlipOneFace, //!< Parm in flip must be between two different faces
OpEdgColOneFace, //!< Parm in EdgCol must be between two different faces
OpVtxSplitNotSameVtx, //!< Parms in vtxsplit are not in the same vertex
OpVtxSplitParmsEqual //!< Parms in VtxSplit are equal
};
/*! Enumerator with the type of elements used in many methods arguments. */
enum ElemType { TypeVertex,
TypeEdge,
TypeFace
};
private : // Data ===================================================================================
SeBase* _first; // A pointer to the symedge of the mesh that is considered the first
char _op_check_parms; // Flag to tell if must check operators parameters (default false)
char _op_last_msg; // Keeps the last result of an operator (OpMsg type)
char _output_op_errors; // Flag that outputs all errors ocurred during operators (default true)
int _vertices, _edges, _faces; // Elem counters
semeshindex _curmark; // Current values for marking
char _marking, _indexing; // flags to indicate that marking or indexing is on
SrClassManagerBase *_vtxman, *_edgman, *_facman; // keep used managers
private : // Private methods ==========================================================================
void _defaults ();
SeBase* _op_error ( OpMsg m );
public : // General mesh functions ===================================================================
/*! Inits an empty mesh. It requires to receive three manager classes
dynamically allocated with operator new. These classes will manage data
stored in vertices, edges and faces. If no associated data is required,
the user can simply pass as argument "new SrClassManagerMeshElementManager", otherwise
an allocated user-derived object must be created and passed to this
constructor. SeMeshBase destructor will automatically delete them. */
SeMeshBase ( SrClassManagerBase* vtxman, SrClassManagerBase* edgman, SrClassManagerBase* facman );
/*! Deletes all associated data: internal buffer, all mesh and its infos. */
virtual ~SeMeshBase ();
/*! Returns the initial symedge of the mesh, can be 0. */
SeBase* first () const { return _first; }
/*! Returns true if the mesh is empty, what happens when first() is null. */
bool empty () const { return _first? false:true; }
/*! Returns v-e+f. */
int euler () const;
/*! Returns (v-e+f-2)/(-2). */
int genus () const;
/*! Returns the number of elements of the given type. */
int elems ( ElemType type ) const;
/*! Returns the number of vertices in the mesh. */
int vertices () const { return _vertices; }
/*! Returns the number of edges in the mesh. */
int edges () const { return _edges; }
/*! Returns the number of faces in the mesh. */
int faces () const { return _faces; }
/*! Invert orientation of all faces. */
void invert_faces ();
/*! Returns the number of vertices in the face adjacent of the given symedge e. */
int vertices_in_face ( SeBase* e ) const;
/*! Returns the number of edges incident to the vertex adjacent of the given symedge e,
ie, the degree of the vertex. */
int vertex_degree ( SeBase* e ) const;
/*! Returns the mean vertex degree of the whole mesh */
float mean_vertex_degree () const;
/*! Returns true if the face incident to the given symedge e has exactly three edges. */
bool is_triangle ( SeBase* e ) const { return e->nxt()->nxt()->nxt()==e? true:false; }
private : // Marking and Indexing =====================================================
void _normalize_mark ();
public :
/*! Allows the user to safely mark elements with mark(), marked() and unmark()
methods. Marking elements is necessary for many algorithms.
SeMeshBase mantains a current marker value, so that each time begin_marking()
is called, the marker value is incremented, guaranteing that old marked
elements have a different mark value.
When the max marking value is reached, a global normalization is done.
Attention: Marking and indexing can not be used at the same time. Also,
end_marking() must be called when marking operations are finished! */
void begin_marking ();
/*! Must be called after a begin_marking() call, when the user finishes using the
marking methods. Note: There is no problem if end_marking() is called without
a previously call to begin_marking() */
void end_marking ();
/*! See if an element is marked (begin_marking() should be called first) */
bool marked ( SeElement* e );
/*! Unmark an element (begin_marking() should be called first) */
void unmark ( SeElement* e );
/*! Mark an element (begin_marking() should be called first) */
void mark ( SeElement* e );
/*! Indexing methods can be used only after a call to begin_indexing(), which permits
the user to associate any index to any element. Note however that after a call to
begin_indexing() the existant indices will have undefined values. This happens
because indices and markers share the same variables, so that both can not be used
at a same time. Method end_marking() must always be called after */
void begin_indexing ();
/*! To finish indexing mode, end_indexing() must be called. The method will do a global
normalization of the internal indices values, preparing them to be used for a later
marking or indexing session. It can be called without a previous begin_marking()
call, but note that it will always peform the global normalization. */
void end_indexing ();
/*! Retrieves the index of an element (begin_indexing() should be called first) */
semeshindex index ( SeElement* e );
/*! Sets the index of an element (begin_indexing() should be called first) */
void index ( SeEdge* e, semeshindex i );
public : // Operators Control ===========================================================================
/*! Get safe mode status. When it is true, error msgs are generated, but
operations become a little slower because some minor checkings are
performed. The default mode is true only for the
debug version of the library (compiled with _DEBUG macro defined).*/
bool safe_mode () { return _op_check_parms? true:false; }
/*! Change the safe mode status. The default mode is false. */
void safe_mode ( bool b ) { _op_check_parms=(char)b; }
/*! Say if error messages should be printed or not (using sr_out), default is false. */
void output_errors ( bool b ) { _output_op_errors=(char)b; }
/*! Whenever an operator returns null, an error message is generated and
can be retrieved here. Each time an operator is called, a new message
is generated. And whenever the operator finishes succesfully OpNoErrors
is generated. */
OpMsg last_op_msg () { return (OpMsg)_op_last_msg; }
/*! Get a string description of the given message. */
static char *translate_op_msg ( OpMsg m );
/*! Makes a global consistency check, for debug purposes, should always return ChkOk. */
ChkMsg check_all () const;
private :
void _new_vtx(SeBase* s); void _new_edg(SeBase* s); void _new_fac(SeBase* s);
void _del_vtx(SeElement* e); void _del_edg(SeElement* e); void _del_fac(SeElement* e);
public : // Operators ==================================================================================
/*! Destroy all the mesh and associated data. */
void destroy ();
/*! Initialize the mesh. See also SeMeshBase documentation. */
SeBase* init ();
/*! Make edge and vertex operator. See also SeMeshBase documentation. */
SeBase* mev ( SeBase* s );
/*! Make edge and vertex operator, splitting a vertex loop. See also SeMeshBase documentation. */
SeBase* mev ( SeBase* s1, SeBase* s2 );
/*! Make edge and face operator. See also SeMeshBase documentation. */
SeBase* mef ( SeBase* s1, SeBase* s2 );
/*! Kill edge and vertex operator. See also SeMeshBase documentation. */
SeBase* kev ( SeBase* x );
/*! Kill edge and vertex operator that can join two vertex loops. See also SeMeshBase documentation. */
SeBase* kev ( SeBase* x, SeBase* *s );
/*! Kill edge and face operator. See also SeMeshBase documentation. */
SeBase* kef ( SeBase* x, SeBase* *s );
/*! Make genus by joining two face contours and deleting two faces,
vertices and edges of s2 face. s1 and s2 must be symmetrically
placed, each one on its contour to join. (not fully tested) */
SeBase* mg ( SeBase* s1, SeBase* s2 );
/*! Flip the edge of a face. See also SeMeshBase documentation. */
SeBase* flip ( SeBase* x );
/*! Edge collide operator. See also SeMeshBase documentation. */
SeBase* ecol ( SeBase* x, SeBase* *s );
/*! Vertex split operator. See also SeMeshBase documentation. */
SeBase* vsplit ( SeBase* s1, SeBase* s2 );
/*! Add vertex operator. See also SeMeshBase documentation. */
SeBase* addv ( SeBase* x );
/*! Delete vertex operator. See also SeMeshBase documentation. */
SeBase* delv ( SeBase* y );
private : // IO ========================================================================================
void _elemsave ( SrOutput& out, ElemType type, SeElement* first );
void _elemload ( SrInput& inp, SrArray<SeElement*>& e, const SrArray<SeBase*>& s, SrClassManagerBase* man );
public :
/*! Writes the mesh to the given output. save() will invoke the
virtual methods output() of the associated element managers.
Note: indexing is used during save(). */
bool save ( SrOutput& out );
/*! Reads the mesh from the given input. load() will invoke the
virtual methods input() of the associated element managers.
Note: indexing is used during load(). */
bool load ( SrInput& inp );
};
/*! This is the template version of the SeMeshBase class, that redefines some methods
of SeBase only for the purpose of including type casts to user defined classes.
All methods are implemented inline just calling the corresponding method of
the base class but correctly applying type casts to convert default types
to user types. It must be used together with SeTpl. */
template <class V, class E, class F> //, class VM, class EM, class FM>
class SeMesh : public SeMeshBase
{ public :
typedef Se<V,E,F> S;
typedef SrClassManager<V> VM;
typedef SrClassManager<E> EM;
typedef SrClassManager<F> FM;
SeMesh () : SeMeshBase ( new VM, new EM, new FM ) {}
S* first () const { return (S*)SeMeshBase::first(); }
S* init () { return (S*)SeMeshBase::init(); }
S* mev ( S* s ) { return (S*)SeMeshBase::mev(s); }
S* mev ( S* s1, S* s2 ) { return (S*)SeMeshBase::mev(s1,s2); }
S* mef ( S* s1, S* s2 ) { return (S*)SeMeshBase::mef(s1,s2); }
S* kev ( S* x ) { return (S*)SeMeshBase::kev(x); }
S* kev ( S* x, S* *s ) { return (S*)SeMeshBase::kev(x,(SeBase**)s); }
S* kef ( S* x, S* *s ) { return (S*)SeMeshBase::kef(x,(SeBase**)s); }
S* mg ( S* s1, S* s2 ) { return (S*)SeMeshBase::mg(s1,s2); }
S* flip ( S* x ) { return (S*)SeMeshBase::flip(x); }
S* ecol ( S* x, S* *s ) { return (S*)SeMeshBase::ecol(x,(SeBase**)s); }
S* vsplit ( S* s1, S* s2 ) { return (S*)SeMeshBase::vsplit(s1,s2); }
S* addv ( S* x ) { return (S*)SeMeshBase::addv(x); }
S* delv ( S* y ) { return (S*)SeMeshBase::delv(y); }
};
/*! \class SeMeshBase se_mesh.h
\brief manages topology and attached information of a symedge mesh.
SeMeshBase uses SeBase as argument for many methods, specially for the
topological operators. Each SeBase instance has only one vertex, edge
and face adjacent (or associated) and so it can be used to reference any
of these elements. SeMeshBase destructor calls destroy(), that will call also
each free() method of registered SeElementManagers in the mesh. To attach info
to a mesh, the user must derive SrClassManagerBase, suply the required virtual
methods, and push it in SeMeshBase with push_info() method.
Operators error handling is performed when the checking mode is true (the
default), all operators may then return a 0 (null) pointer when their
parameters are illegal. In such cases, a call to last_op_msg() will return
the error occured. Next is a schema explaining each operator: \code
*========================================================================
* Operator init()
*
* x=init() v1 o o v2 -> v1 o------o v2
* destroy() <-----
* destroy all the previous structure. x
*
*========================================================================
* Operator mev(s)
*
* s x
* <----- <-----
* x=mev(s) o------o o v -> o------o------o v
* s=kev(x) / | / |
* / | / |
* To kev(x), only o o o o
* one edge in v;
*
*========================================================================
* Operator mev(s1,s2)
* o o
* | \
* | s1 \
* | ----> \
* x=mev(s1,s2) o------o---------o -> o------o------o--o
* s1=kev(x,&s2) <---- | o | <--- v
* s2 | v | x
* To mev, s1 & s2 on o o
* the same vertex
* and s1!=s2
*
* To kev(x), more than one edge of x must be incident to v
*
*=========================================================================
* Operator mef(s1,s2)
*
* x=mef(s1,s2) o-------o-------o o-------o-------o
* s1=kef(x,&s2) | <---- | | | |
* | s1 | | | |
* | | | || |
* s1 & s2 on | | -> | e||x |
* same face | | | |V |
* | s2 | | | f |
* in kef(x), x->sym | ----> | | | |
* must be in a diff o-------o-------o o-------o-------o
* face than x
*
*========================================================================
* Operator flip(x)
*
* y=flip(x) o---o---o o---o---o
* / | | / |
* / | | / |
* x & x->sym must / || | / |
* be in diff faces o ||x o -> o---------------o
* and share the | |V / | <------ /
* same edge | | / | y /
* | | / | /
* o---o---o o---o---o
*
* This operator acts as an "edge rotation"; that in triangulations is the
* same as the well known flip (or swap) operator.
*
*========================================================================
* Operator ecol(x,&s2)
*
* s1=ecol(x,&s2) o o
* x=vsplit(s1,s2) / | s1|
* / | ^|
* \ / x | / \ || /
* x and x->sym must \ / --> |/ \ |/
* be in diff faces o---------o v -> o v
* and share the / \ |\ / |\
* same edge / \ | \ / | \
* / \ | / / |
* / \ | / /s2 |
* o V o
*
* In the process, 3 edges, 2 faces and 1 vertex are created/destroyed.
* This operator acts as an "edge collapse", and has
* the "vertex split" operator as inverse.
* The three destroyed edges are incident to x->vtx(), and not v.
*
*========================================================================
* Operator addv()
*
* y=addv(x) ----------- -----------
* x=delv(y) | <-- | |\ /|
* | x | | \ y^/ |
* | | | \ // |
* | | | \ / |
* | o | -> | o |
* | | | / \ |
* | | | / \ |
* | | | / \ |
* | | |/ \|
* ----------- -----------
*
*
\endcode */
//============================ End of File ===============================
# endif // SE_MESH_H

View File

@ -0,0 +1,535 @@
#include "precompiled.h"
# include "sr_tree.h"
# include "se_mesh_import.h"
//# define SR_USE_TRACE1 // basic messages
//# define SR_USE_TRACE2 // more messages
//# define SR_USE_TRACE4 // active contour
//# define SR_USE_TRACE5 // tree
# include "sr_trace.h"
//======================= static data =============================================
// Question: Could I substitute EdgeTree by an EdgeHashTable ...?
class Edge : public SrTreeNode
{ public :
int a, b; // Edge vertices, not oriented
int v; // The other vertex of the adjacent face
int f; // The index of the edge's face
Edge *sym; // The other edge, belonging to the other face sharing this edge
Edge *e2, *e3; // The other two edges of this face
public :
Edge ( int i1=0, int i2=0, int i3=0 ) : f(0), sym(0), e2(0), e3(0) { a=i1; b=i2; v=i3; }
void set_fac_elems ( Edge *b, Edge *c, int fac ) { e2=b; e3=c; f=fac; }
void swap() { int tmp=a; a=b; b=tmp; }
friend SrOutput& operator<< ( SrOutput& o, const Edge& e )
{ return o<<'['<<e.a<<' '<<e.b<<' '<<e.v<<"]"; }
friend SrInput& operator>> ( SrInput& i, Edge& e ) { return i; }
friend int sr_compare ( const Edge* e1, const Edge* e2 );
};
int sr_compare ( const Edge *e1, const Edge *e2 )
{
if ( e1->a<e2->a ) return -1;
if ( e1->a>e2->a ) return 1;
if ( e1->b<e2->b ) return -1;
if ( e1->b>e2->b ) return 1;
return 0;
}
/*
# ifdef SE_USR_TRACE4
struct PFace { SeBase *e; SeMeshBase *m; } PFaceInst;
FgOutput &operator << ( FgOutput &o, PFace &f )
{
o << '[';
SeBase *e=f.e;
while ( true )
{ o<<(int)f.m->index(e->vtx());
e = e->nxt();
if ( e!=f.e ) o<<' '; else break;
}
return o<<']';
}
# define SR_TRACE_CONTOUR(x,s) { PFaceInst.e=x; PFaceInst.m=s; SR_TRACE4 ( "Active Contour = "<< PFaceInst ); }
# else
# define SR_TRACE_CONTOUR(x,s)
# endif
*/
# define SR_TRACE_CONTOUR(x,s)
//=========================== EdgeTree ========================================
class EdgeTree : public SrTree<Edge>
{ public :
// EdgeTree() {}
public :
Edge *insert_edge ( int a, int b, int v );
bool insert_face ( int a, int b, int c, int f );
Edge* search_edge ( int a, int b );
void remove_face ( Edge *e );
};
Edge *EdgeTree::insert_edge ( int a, int b, int v )
{
Edge *e = new Edge ( a, b, v );
if ( !insert(e) )
{ Edge *x = cur();
e->swap();
if ( !insert(e) )
{ SR_TRACE2 ( "Could not insert edge ("<<a<<','<<b<<")!!" );
delete e;
return 0;
}
x->sym = e;
e->sym = x;
}
else // search if the sym edge is there and set sym pointers
{ // (to try: sort (a,b) to avoid checking if the sym exists...)
Edge eba ( b, a, v );
Edge *esym = search ( &eba );
if ( esym )
{ esym->sym = e;
e->sym = esym;
}
}
return e;
}
bool EdgeTree::insert_face ( int a, int b, int c, int f )
{
Edge *e1 = insert_edge ( a, b, c );
Edge *e2 = insert_edge ( b, c, a );
Edge *e3 = insert_edge ( c, a, b );
if (e1) e1->set_fac_elems ( e2, e3, f );
if (e2) e2->set_fac_elems ( e1, e3, f );
if (e3) e3->set_fac_elems ( e1, e2, f );
return e1&&e2&&e3? true:false;
}
Edge* EdgeTree::search_edge ( int a, int b )
{
Edge key(a,b,0);
Edge *e = search(&key);
if ( !e )
{ key.swap();
e=search(&key);
}
return e;
}
void EdgeTree::remove_face ( Edge *e )
{
if (!e) return;
// e2 and/or e3 can be null in case non-manifold edges were found during the
// construction of the edge tree with start()
Edge *e2 = e->e2;
Edge *e3 = e->e3;
extract ( e );
if ( e->sym ) e->sym->sym=0;
if (e2)
{ if ( e2->sym ) e2->sym->sym=0;
remove ( e2 );
}
if (e3)
{ if ( e3->sym ) e3->sym->sym=0;
remove ( e3 );
}
}
//======================= internal data ==================================
static SeBase* init ( SeMeshImport *self, SeMeshBase* m, int a, int b )
{
SeBase *s = m->init ();
m->index(s->vtx(),(semeshindex)b);
m->index(s->nxt()->vtx(),(semeshindex)a);
self->attach_vtx_info ( s->vtx(), b );
self->attach_vtx_info ( s->nxt()->vtx(), a );
self->attach_edg_info ( s->edg(), b, a );
return s;
}
static SeBase* mev ( SeMeshImport *self, SeMeshBase* m, SeBase* s, int ev, int v )
{
s = m->mev ( s );
self->attach_edg_info ( s->edg(), ev, v );
self->attach_vtx_info ( s->vtx(), v );
m->index ( s->vtx(), (semeshindex)v );
return s;
}
static SeBase* mef ( SeMeshImport *self, SeMeshBase* m, SeBase* s1, SeBase* s2, int a, int b, int f )
{
s2 = m->mef ( s1, s2 );
self->attach_edg_info ( s2->edg(), a, b );
self->attach_fac_info ( s2->fac(), f );
return s2;
}
class SeMeshImport::Data
{ public :
SeMeshImport *self;
EdgeTree tree;
SrArray<SeBase*> active_contours; // stack of contours
SrArray<SeBase*> contours_to_solve; // keep contours that will result in genus or holes
SrArray<SeBase*> vtxsymedges; // keep pointer for vertices already inserted
public :
Data ( SeMeshImport *x );
SeMeshBase *init_shell ();
int start ( const int *triangles, int numtris, int numvtx );
void solve_lasting_contours ( SeMeshBase *m );
void glue_face_on_one_edge ( SeMeshBase *m, SeBase *&s, Edge *e );
void glue_face_on_two_edges ( SeMeshBase *m, SeBase *&s, Edge *e );
void glue_face_on_three_edges ( SeMeshBase *m, SeBase *s, Edge *e );
};
SeMeshImport::Data::Data ( SeMeshImport *x )
{
vtxsymedges.size(0);
vtxsymedges.capacity(256);
active_contours.size(0);
active_contours.capacity(32);
self = x;
}
SeMeshBase *SeMeshImport::Data::init_shell ()
{
SR_TRACE1 ( "Getting new shell..." );
SeMeshBase *m = self->get_new_shell ();
if ( !m ) return 0;
SR_TRACE1 ( "Getting seed face from the root of the tree..." );
Edge *e = tree.root();
int a=e->a; int b=e->b; int c=e->v; // the order of the vertices is random
int f=e->f;
tree.remove_face ( e );
delete e;
SR_TRACE1 ( "Creating seed face = ["<<a<<" "<<b<<" "<<c<<"]" );
m->begin_indexing ();
SeBase *s;
s = init ( self, m, a, b );
s = mev ( self, m, s, b, c );
mef ( self, m, s, s->nxt()->nxt(), a, c, f );
vtxsymedges[c] = s;
vtxsymedges[b] = s->nxt();
vtxsymedges[a] = s->nxt()->nxt();
active_contours.push() = s;
SR_TRACE2 ("seed face info: "<<(int)s->sym()->fac()<<", active face info: "<<(int)s->fac() );
return m;
}
int SeMeshImport::Data::start ( const int *triangles, int numtris, int numvtx )
{
int i;
active_contours.size(0);
SR_TRACE1 ( "Initializing vertices table with size "<<numvtx<<"..." );
vtxsymedges.size(numvtx);
for ( i=0; i<vtxsymedges.size(); i++ ) vtxsymedges[i]=0;
SR_TRACE1 ( "Creating edge tree..." );
const int *tri=triangles;
int faces_with_edges_not_inserted=0;
for ( i=0; i<numtris; i++ )
{ if ( !tree.insert_face(tri[0],tri[1],tri[2],i) )
faces_with_edges_not_inserted++;
tri += 3;
}
return faces_with_edges_not_inserted;
}
static SeBase* coincident_contour ( SeBase *s1, SeBase *s2 )
{
SeBase *x = s2;
while ( s1->vtx() != x->nxt()->vtx() )
{ x = x->nxt();
if ( x==s2 ) return 0; // not coincident
}
// a common starting vertex was found, so that the contours will be coincident
// or we'll have non-manifold vertices. Let's verify :
s2 = x;
do { if ( s1->vtx() != s2->nxt()->vtx() )
{ printf("Non-manifold vertex detected!\n"); return 0; }
s1 = s1->nxt();
s2 = s2->pri();
} while ( s2!=x );
return x;
}
// should finished the contours by detecting loops to join or holes info to attach
void SeMeshImport::Data::solve_lasting_contours ( SeMeshBase *m )
{
int i;
bool genus_increased;
SeBase *s, *c;
while ( !contours_to_solve.empty() )
{ genus_increased = false;
s = contours_to_solve.pop();
SR_TRACE_CONTOUR(s,m);
for ( i=0; i<contours_to_solve.size(); i++ )
{ c = coincident_contour(s,contours_to_solve[i]);
if ( c )
{ SR_TRACE1 ( "Making one genus..." );
SR_TRACE_CONTOUR(c,m);
contours_to_solve.remove(i);//[i]=contours_to_solve.pop();
genus_increased = true;
m->mg ( s, c );
break;
}
}
if ( !genus_increased )
{ SR_TRACE1 ( "Closing last contour as a hole..." );
self->attach_hole_info ( s->fac() );
}
}
}
void SeMeshImport::Data::glue_face_on_one_edge ( SeMeshBase *m, SeBase *&s, Edge *e )
{
tree.remove_face ( e );
SeBase *snxt = s->nxt();
s = mev ( self, m, s, e->a, e->v );
s = mef ( self, m, snxt, s, e->b, e->v, e->f );
vtxsymedges[e->a] = s->nxt()->sym();
vtxsymedges[e->b] = snxt;
vtxsymedges[e->v] = snxt->pri();
s = snxt;
delete e;
}
void SeMeshImport::Data::glue_face_on_two_edges ( SeMeshBase* m, SeBase*& s, Edge* e )
{
tree.remove_face ( e );
SeElement *ve = vtxsymedges[e->v]->vtx();
SeBase *s1, *s2;
if ( s->pri()->vtx()==ve )
{ s1=s->nxt(); s2=s->pri(); }
else
{ s1=s->nxt()->nxt(); s2=s; }
mef ( self, m, s1, s2, m->index(s1->vtx()), m->index(s2->vtx()), e->f );
s = s1->pri();
vtxsymedges[(int)m->index(s->vtx())] = s;
vtxsymedges[(int)m->index(s1->vtx())] = s1;
delete e;
}
void SeMeshImport::Data::glue_face_on_three_edges ( SeMeshBase* /*m*/, SeBase* s, Edge* e )
{
tree.remove_face ( e );
self->attach_fac_info ( s->fac(), e->f );
delete e;
}
//======================== SeMeshImport ========================================
SeMeshImport::SeMeshImport ()
{
_data = new Data ( this );
}
SeMeshImport::~SeMeshImport ()
{
for ( int i=0; i<shells.size(); i++ ) delete shells[i];
delete _data;
}
void SeMeshImport::compress_buffers ()
{
shells.compress();
_data->vtxsymedges.capacity(0);
_data->active_contours.capacity(0);
}
SeMeshImport::Msg SeMeshImport::start ( const int *triangles, int numtris, int numvtxs )
{
SR_TRACE1 ( "Start :" );
int not_inserted = _data->start ( triangles, numtris, numvtxs );
shells.size(0);
SeMeshBase *m = _data->init_shell (); // will push something in active_contours
if ( !m ) return MsgNullShell;
shells.push()=m;
SR_TRACE_CONTOUR(_data->active_contours.top(),m);
SR_TRACE4 ( "Face info of active contour = " << (int)(_data->active_contours.top()->fac()) );
SR_TRACE5 ( "Tree = "<<_data->tree );
SR_TRACE1 ( "Start finished !" );
SR_TRACE1 ( "Faces Lost: "<<not_inserted );
return not_inserted>0? MsgNonManifoldFacesLost : MsgOk;
}
static SeBase* in_same_face ( SeBase *e, SeElement *v )
{
SeBase *x = e;
do { if ( x->vtx()==v ) return x;
x = x->nxt();
} while ( x!=e );
return 0;
}
SeMeshImport::Msg SeMeshImport::next_step ()
{
int a, b;
Edge *e;
SeBase *ini, *s, *sv;
int edges_found = 0;
SR_TRACE2 ( " " );
SR_TRACE2 ( "Next step :" );
EdgeTree &tree = _data->tree;
SrArray<SeBase*> &active_contours = _data->active_contours;
SrArray<SeBase*> &contours_to_solve = _data->contours_to_solve;
SrArray<SeBase*> &vtxsymedges = _data->vtxsymedges;
SR_TRACE2 ( "Active contours : "<<active_contours.size() );
if ( tree.empty() )
{ SR_TRACE1 ( "Empty tree: no more faces to glue." );
if ( !active_contours.empty() )
{ while ( !active_contours.empty() ) contours_to_solve.push()=active_contours.pop();
SR_TRACE1 ( "Solving "<<contours_to_solve.size()<<" open contour(s)..." );
_data->solve_lasting_contours ( shells.top() );
}
for ( int i=0; i<shells.size(); i++ ) shells[i]->end_indexing();
SR_TRACE1 ( "Finished : "<<shells.size()<<" shell(s), last shell V-E+F="<<shells.top()->euler() );
return MsgFinished;
}
else if ( active_contours.empty() )
{ SR_TRACE1 ( "Starting new shell "<<shells.size()<<"... " );
shells.push() = _data->init_shell();
return MsgOk; // return now to cope with degenerated data (a shell with a single triangle).
}
s = ini = active_contours.top();
while ( true )
{
a = (int) shells.top()->index(s->vtx());
b = (int) shells.top()->index(s->nxt()->vtx());
SR_TRACE2 ( "Searching edge "<<a<<" "<<b<<"... " );
e = tree.search_edge ( a, b ); // will search {a,b} or {b,a}
if ( e )
{ edges_found++;
sv = 0;
if ( vtxsymedges[e->v] ) // the other vertex is already in the mesh
sv = in_same_face ( s, vtxsymedges[e->v]->vtx() );
if ( !vtxsymedges[e->v] || !sv ) // if the other vertex is free or is in another contour
{ SR_TRACE1 ( "Gluing face "<<a<<" "<<b<<" "<<e->v<<" on one edge... " );
_data->glue_face_on_one_edge ( shells.top(), s, e );
active_contours.top() = s;
SR_TRACE_CONTOUR(s,shells.top());
SR_TRACE5 ( "Tree = "<<tree );
return MsgOk;
}
else if ( s->nxt()->nxt()->nxt()==s ) // trying to close the active contour that is a triangle
{ SR_TRACE1 ( "Gluing last face "<<a<<" "<<b<<" "<<e->v<<" of current active contour..." );
_data->glue_face_on_three_edges ( shells.top(), s, e );
active_contours.pop();
SR_TRACE1 ( "Active contour popped... " );
SR_TRACE5 ( "Tree = "<<tree );
return MsgOk;
}
else if ( s->nxt()->nxt()->vtx()==vtxsymedges[e->v]->vtx() ||
s->pri()->vtx()==vtxsymedges[e->v]->vtx() )
{ SR_TRACE1 ( "Gluing face "<<a<<" "<<b<<" "<<e->v<<" on two edges... " );
_data->glue_face_on_two_edges ( shells.top(), s, e );
active_contours.top() = s;
SR_TRACE_CONTOUR(s,shells.top());
SR_TRACE5 ( "Tree = "<<tree );
return MsgOk;
}
else if ( sv )
{ SR_TRACE1 ( "Connecting one edge, and pushing one active contour... " );
shells.top()->mef ( s, sv );
attach_edg_info ( s->edg(), shells.top()->index(s->vtx()), shells.top()->index(sv->vtx()) );
active_contours.push()=sv;
return MsgOk;
}
else
{ SR_TRACE1 ( "XXXXXXXXXXXXXXXXXX undef situation!! XXXXXXXXXXXXXXXXXXXXXXXXXXXX" );
SR_TRACE_CONTOUR(active_contours.top(),shells.top());
SR_TRACE1 ( "s = ["<<a<<","<<b<<"] v="<<e->v);
SR_TRACE1 ( "s.fac="<<(int)s->fac()<<", vfac="<<(int)vtxsymedges[e->v]->fac() );
}
}
else
{ // e not in the edge tree: input data is not fully connected!
}
s = s->nxt();
if ( s==ini ) break;// already passed all edges of the active contour
}
// At this point we looked into all edges of the active_contour,
// but no faces were glued and the tree is not empty.
SR_TRACE1 ( "No more faces could be glued to the actual contour!" );
contours_to_solve.push() = active_contours.pop();
return MsgOk;
}
//============================== virtuals ==================================
void SeMeshImport::attach_vtx_info ( SeVertex* /*v*/, int /*vi*/ )
{
}
void SeMeshImport::attach_edg_info ( SeEdge* /*e*/, int /*a*/, int /*b*/ )
{
}
void SeMeshImport::attach_fac_info ( SeFace* /*f*/, int /*fi*/ )
{
}
void SeMeshImport::attach_hole_info ( SeFace* /*f*/ )
{
}
//============================== end of file ===============================

View File

@ -0,0 +1,81 @@
# ifndef SE_MESH_IMPORT_H
# define SE_MESH_IMPORT_H
/** \file sr_mesh_import.h
* Topology recovery from a list of triangles
*/
# include "se_mesh.h"
/*! \class SeMeshImport sr_mesh_import.h
\brief Recovers adjacencies from a triangle list
Note: SeMeshImport uses the indexing methods of SeMesh. */
class SeMeshImport
{ public :
enum Msg { MsgOk,
MsgNullShell,
MsgNonManifoldFacesLost,
MsgFinished
};
/*! This array will contain the recovered shells, and the user is reuired to
get and take control of the stored pointers and after that, putting the size
of the array to 0. If the array size is not 0, the destructor of SeMeshImport
will delete each shell in the array. */
SrArray<SeMeshBase*> shells;
private :
class Data;
Data *_data;
public :
/*! Default constructor */
SeMeshImport ();
/*! The destructor will delete all meshes stored in the shells array.
So that if the user wants to become responsible to maintain the
imported meshes, he/she must take the pointers in the shell array
and set the array size to 0. */
virtual ~SeMeshImport ();
/*! Compress some used internal buffers */
void compress_buffers ();
/*! Defines the information describing the triangle-based description to
be imported. The list of triangles contains indices to the vertices.
Geometrical information can be attached to the mesh by re-writing
the availble virtual methods, which allow transforming indices
into any kind of user-related data. */
Msg start ( const int *triangles, int numtris, int numvtx );
/*! Join another face to the mesh, expanding the current active contour.
This method should be called until MsgFinished or errors occur */
Msg next_step ();
public :
/* This virtual method must be derived to return a valid SeMesh for
each new shell encountered in the data being analysed. */
virtual SeMeshBase* get_new_shell ()=0;
/*! Optional virtual method to attach vertex information.
The default implementation does nothing. */
virtual void attach_vtx_info ( SeVertex* v, int vi );
/*! Optional virtual method to attach edge information.
The default implementation does nothing. */
virtual void attach_edg_info ( SeEdge* e, int a, int b );
/*! Optional virtual method to attach face information.
The default implementation does nothing. */
virtual void attach_fac_info ( SeFace* f, int fi );
/*! Optional virtual method to attach face information of holes found.
The default implementation does nothing. */
virtual void attach_hole_info ( SeFace* f );
};
//============================== end of file ===============================
# endif // SE_MESH_IMPORT_H

View File

@ -0,0 +1,219 @@
#include "precompiled.h"
# include <string.h>
# include "se_mesh.h"
//# define SR_USE_TRACE1 // IO trace
# include "sr_trace.h"
//================================ IO =========================================
# define ID(se) int(se->_edge)
void SeMeshBase::_elemsave ( SrOutput& out, SeMeshBase::ElemType type, SeElement* first )
{
SrClassManagerBase* man;
SeElement *e;
int esize;
char* st;
if ( type==TypeVertex )
{ esize=_vertices; man=_vtxman; st="Vertices"; }
else if ( type==TypeEdge )
{ esize=_edges; man=_edgman; st="Edges"; }
else
{ esize=_faces; man=_facman; st="Faces"; }
out << srnl << st << srspc << esize << srnl;
//fprintf ( f, "\n%s %d\n", st, esize );
e = first;
do { e->_index = 0;
out << ID(e->_symedge) <<srspc;
//fprintf ( f, "%d ", ID(e->_symedge) );
man->output(out,e);
out << srnl;
//fprintf ( f, "\n" );
e = e->nxt();
} while (e!=first);
SR_TRACE1 ( esize << " elements written !" );
}
bool SeMeshBase::save ( SrOutput& out )
{
SeBase *se;
SeElement *el, *eli;
int symedges, i, j;
out << "SYMEDGE MESH DESCRIPTION\n\n";
//fprintf ( f, "SYMEDGE MESH DESCRIPTION\n\n" );
SR_TRACE1("Header written.");
symedges = _edges*2;
out << "SymEdges " << symedges << srnl;
//fprintf ( f, "SymEdges %d\n", symedges );
if ( empty() ) return true;
SrArray<SeBase*> ses(symedges);
SrArray<SeEdge*> edg(symedges);
// get adjacency information of symedges to save:
SR_TRACE1("Mounting S array...");
i=0;
el = eli = _first->edg();
do { se = el->se();
el->_index = i/2;
for ( j=0; j<2; j++ )
{ if ( j==1 ) se = se->sym();
ses[i] = se;
edg[i] = se->_edge; // save edge pointer
se->_edge = (SeEdge*)i; // and use it to keep indices
i++;
}
el = el->nxt();
} while ( el!=eli );
// adjust vtx and fac indices:
SR_TRACE1("Adjusting indexes...");
i = 0;
el = eli = _first->vtx();
do { el->_index=i++; el=el->nxt(); } while (el!=eli);
i = 0;
el = eli = _first->fac();
do { el->_index=i++; el=el->nxt(); } while (el!=eli);
for ( i=0; i<ses.size(); i++ )
{ out << ID(ses[i]->_next) << srspc
<< ID(ses[i]->_rotate) << srspc
<< ses[i]->_vertex->_index << srspc
<< edg[i]->_index << srspc
<< ses[i]->_face->_index << srspc
<< srnl;
/*fprintf ( f, "%d %d %d %d %d\n",
ID(ses[i]->_next),
ID(ses[i]->_rotate),
ses[i]->_vertex->_index,
edg[i]->_index,
ses[i]->_face->_index );*/
}
SR_TRACE1("Symedges written.");
_elemsave ( out, TypeVertex, _first->vtx() );
_elemsave ( out, TypeEdge, edg[0] );
_elemsave ( out, TypeFace, _first->fac() );
_curmark = 1;
_marking = _indexing = false;
for ( i=0; i<ses.size(); i++ ) ses[i]->_edge = edg[i];
SR_TRACE1 ( "save OK !" );
return true;
}
# undef ID
//---------------------------------- load --------------------------------
/*
static int load_int ( FILE* f )
{
int i;
fscanf ( f, "%d", &i );
return i;
}
static void skip ( FILE* f, int n )
{
char s[60];
while ( n-- ) fscanf ( f, "%s", s );
}
*/
void SeMeshBase::_elemload ( SrInput& inp, SrArray<SeElement*>& E,
const SrArray<SeBase*>& S, SrClassManagerBase* man )
{
int i, x;
inp.get_token(); // skip elem type label
//skip ( f, 1 ); // skip elem type label
inp >> x; E.size(x);
//x = load_int(f); E.size(x);
for ( i=0; i<E.size(); i++ )
{ inp >> x; //x = load_int(f);
E[i] = (SeElement*)man->alloc();
E[i]->_symedge = S.get(x);
man->input ( inp, E[i] );
//man->read ( E[i], f );
if ( i>0 ) E[0]->_insert(E[i]);
}
SR_TRACE1 ( e.size() << " elements loaded !" );
}
bool SeMeshBase::load ( SrInput& inp )
{
//char buf[64];
int i, x;
//fscanf ( f, "%s", buf ); if ( strcmp(buf,"SYMEDGE") ) return false;
//fscanf ( f, "%s", buf ); if ( strcmp(buf,"MESH") ) return false;
//fscanf ( f, "%s", buf ); if ( strcmp(buf,"DESCRIPTION") ) return false;
inp.get_token(); if ( inp.last_token()!="SYMEDGE" ) return false;
inp.get_token(); if ( inp.last_token()!="MESH" ) return false;
inp.get_token(); if ( inp.last_token()!="DESCRIPTION" ) return false;
SR_TRACE1 ( "Signature ok." );
// destroy actual structure to load the new one:
destroy ();
// load indices and store them in an array:
inp.get_token(); // skip SymEdges label
//skip ( f, 1 ); // skip SymEdges label
inp >> i; //i = load_int ( f );
SrArray<SeBase*> S(i);
for ( i=0; i<S.size(); i++ )
{ S[i] = new SeBase;
inp>>x; S[i]->_next = (SeBase*)x;
inp>>x; S[i]->_rotate = (SeBase*)x;
inp>>x; S[i]->_vertex = (SeVertex*)x;
inp>>x; S[i]->_edge = (SeEdge*)x;
inp>>x; S[i]->_face = (SeFace*)x;
}
SR_TRACE1 ( "Symedges loaded." );
// load infos:
SrArray<SeElement*> V;
SrArray<SeElement*> E;
SrArray<SeElement*> F;
_elemload ( inp, V, S, _vtxman );
_elemload ( inp, E, S, _edgman );
_elemload ( inp, F, S, _facman );
// convert indices to pointers:
for ( i=0; i<S.size(); i++ )
{ S[i]->_next = S[(int)(S[i]->_next)];
S[i]->_rotate = S[(int)(S[i]->_rotate)];
S[i]->_vertex = V[(int)(S[i]->_vertex)];
S[i]->_edge = E[(int)(S[i]->_edge)];
S[i]->_face = F[(int)(S[i]->_face)];
}
// adjust internal variables:
_first = S[0];
_vertices = V.size();
_edges = E.size();
_faces = F.size();
_curmark = 1;
_marking = _indexing = false;
SR_TRACE1 ( "load OK !" );
return true;
}
//=== End of File ===================================================================

View File

@ -0,0 +1,571 @@
#include "precompiled.h"
# include <stdlib.h>
# include "se_mesh.h"
//# define CHECKALL_OPS // Will call check_all() after each operator
//# define SR_USE_TRACE1 // Operators trace
# include "sr_trace.h"
//===================================== Some Macros =======================================
# ifdef CHECKALL_OPS
# include <stdlib.h>
# define CHECKALL { int err=check_all(); if(err!=0) { printf("CHECK ERROR: %d\n",err ); exit(1); } }
# else
# define CHECKALL
# endif
# define SE_MAX3(a,b,c) (a>b? (a>c?(a):(c)):(b>c?(b):(c)))
# define EDG_CREATE(x,y) x=new SeBase; y=new SeBase; \
x->_next=y; x->_rotate=x; \
y->_next=x; y->_rotate=y;
# define DEF_SPLICE_VARS SeBase *sp_xnxt, *sp_ynxt, *sp_xsym
// the effect is: swap(x.nxt,y.nxt); swap(x.nxt.rot,y.nxt.rot)
# define SPLICE(x,y) sp_xnxt = x->_next; \
sp_ynxt = y->_next; \
sp_xsym = sp_xnxt->_rotate; \
x->_next = sp_ynxt; \
y->_next = sp_xnxt; \
sp_xnxt->_rotate = sp_ynxt->_rotate; \
sp_ynxt->_rotate = sp_xsym
//======================================== private members ========================================
void SeMeshBase::_new_vtx ( SeBase *s )
{
_vertices++;
SeVertex* v = (SeVertex*)_vtxman->alloc();
v->_symedge = s;
if ( _first ) _first->vtx()->_insert(v);
s->_vertex = v;
}
void SeMeshBase::_new_edg ( SeBase *s )
{
_edges++;
SeEdge* e = (SeEdge*)_edgman->alloc();
e->_symedge = s;
if ( _first ) _first->edg()->_insert(e);
s->_edge = e;
}
void SeMeshBase::_new_fac ( SeBase *s )
{
_faces++;
SeFace* f = (SeFace*)_facman->alloc();
f->_symedge = s;
if ( _first ) _first->fac()->_insert(f);
s->_face = f;
}
void SeMeshBase::_del_vtx ( SeElement *e )
{
_vertices--;
if ( e==_first->vtx() ) _first = e->nxt()==e? 0:e->nxt()->se();
e->_remove();
_vtxman->free ( e );
}
void SeMeshBase::_del_edg ( SeElement *e )
{
_edges--;
if ( e==_first->edg() ) _first = e->nxt()==e? 0:e->nxt()->se();
e->_remove();
_edgman->free ( e );
}
void SeMeshBase::_del_fac ( SeElement *e )
{
_faces--;
if ( e==_first->fac() ) _first = e->nxt()==e? 0:e->nxt()->se();
e->_remove();
_facman->free ( e );
}
//==================================== operator related funcs ======================================
char* SeMeshBase::translate_op_msg ( OpMsg m )
{
switch (m)
{ case OpNoErrors : return "No Errors occured";
case OpMevParmsEqual : return "Parms in mev are equal";
case OpMevNotSameVtx : return "Parms in mev are not in the same vertex";
case OpMefNotSameFace : return "Parms in mef are not in the same face";
case OpKevLastEdge : return "Cannot delete the last edge with kev";
case OpKevManyEdges : return "More then one edge in parm vertex in kev";
case OpKevOneEdge : return "Vertex of parm has only one edge in kev";
case OpKefSameFace : return "Parm in kef must be between two different faces";
case OpMgUncompatibleFaces : return "Parms in mg must be adjacent to faces with the same number of vertices";
case OpMgSameFace : return "Parms in mg must be, each one, adjacent to a different face";
case OpFlipOneFace : return "Parm in flip must be between two different faces";
case OpEdgColOneFace : return "Parm in edgcol must be between two different faces";
case OpVtxSplitParmsEqual : return "Parms in vtxsplit are equal";
case OpVtxSplitNotSameVtx : return "Parms in vtxsplit not in the same vertex";
}
return "Undefined error code";
}
static SeMeshBase::ChkMsg check ( SeBase *x, int max )
{
int i=0;
SeBase *xi = x;
x = xi->nxt();
while ( x!=xi ) { x=x->nxt(); i++; if(i>max) return SeMeshBase::ChkNxtError; }
i=0;
x = xi->rot();
while ( x!=xi ) { x=x->rot(); i++; if(i>max) return SeMeshBase::ChkRotError; }
return SeMeshBase::ChkOk;
}
SeMeshBase::ChkMsg SeMeshBase::check_all () const
{
ChkMsg m;
int i, max = 2*SE_MAX3(_vertices,_edges,_faces); // A very high roof to avoid infinite loop
SeElement *e, *ei;
i = 0;
e = ei = _first->vtx();
do { if ( e->se()->vtx()!=e || i++>max ) return ChkVtxError;
e = e->nxt();
} while ( e!=ei );
i = 0;
e = ei = _first->edg();
do { if ( e->se()->edg()!=e || i++>max ) return ChkEdgError;
e = e->nxt();
} while ( e!=ei );
i = 0;
e = ei = _first->fac();
do { if ( e->se()->fac()!=e || i++>max ) return ChkFacError;
e = e->nxt();
} while ( e!=ei );
e = ei = _first->edg();
do { m = check ( e->se(), max );
if ( m!=ChkOk ) return m;
m = check ( e->se()->sym(), max );
if ( m!=ChkOk ) return m;
e = e->nxt();
} while ( e!=ei );
return ChkOk;
}
//======================================== SeMeshBase Operators ========================================
void SeMeshBase::destroy ()
{
if (!_first) return;
SR_TRACE1("destroy...");
int i;
SeElement *ini, *cur, *curn;
// Save all symedges in a list:
SrArray<SeBase*> S(0,_edges*2);
cur = ini = _first->edg();
do { S.push() = cur->se();
S.push() = cur->se()->sym();
cur = cur->nxt();
} while ( cur!=ini );
// delete element lists :
SrClassManagerBase* man;
for ( i=0; i<3; i++ ) // for each element type
{ if ( i==0 )
{ ini=_first->vtx(); man=_vtxman; }
else if ( i==1 )
{ ini=_first->edg(); man=_edgman; }
else
{ ini=_first->fac(); man=_facman; }
cur=ini;
do { curn = cur->nxt(); // for each element
man->free ( cur );
cur = curn;
} while ( cur!=ini );
}
// delete all symedges:
for ( i=0; i<S.size(); i++ ) delete S[i];
_defaults ();
SR_TRACE1("Ok.");
}
SeBase *SeMeshBase::init ()
{
SR_TRACE1("init...");
destroy ();
SeBase *ne1, *ne2;
EDG_CREATE ( ne1, ne2 );
_new_vtx(ne1);
_new_vtx(ne2); ne2->vtx()->_insert(ne1->vtx());
_new_edg(ne1); ne2->_edge=ne1->edg();
_new_fac(ne1); ne2->_face=ne1->fac();
_first = ne2; // _first must be set only here!
CHECKALL;
SR_TRACE1("Ok.");
return ne2;
}
SeBase *SeMeshBase::mev ( SeBase *s )
{
SR_TRACE1("mev...");
SeBase *ne1, *ne2;
EDG_CREATE ( ne1, ne2 );
ne1->_vertex = s->vtx();
_new_vtx(ne2); ne1->_vertex=s->vtx();
_new_edg(ne2); ne1->_edge=ne2->edg();
ne1->_face = ne2->_face = s->fac();
DEF_SPLICE_VARS;
SPLICE ( s->pri(), ne2 );
_op_last_msg = OpNoErrors;
CHECKALL;
SR_TRACE1("Ok.");
return ne2;
}
SeBase *SeMeshBase::mev ( SeBase *s1, SeBase *s2 )
{
SR_TRACE1("mev2...");
if ( _op_check_parms )
{ if ( s1==s2 ) return _op_error(OpMevParmsEqual);
if ( s1->vtx()!=s2->vtx() ) return _op_error(OpMevNotSameVtx);
_op_last_msg = OpNoErrors;
}
SeBase *ne1, *ne2;
EDG_CREATE ( ne1, ne2 );
_new_vtx(ne1); ne2->_vertex=s2->vtx();
_new_edg(ne1); ne2->_edge = ne1->edg();
ne1->_face = s1->sym()->fac();
ne2->_face = s2->sym()->fac();
s2->_vertex->_symedge = s2;
DEF_SPLICE_VARS;
SPLICE ( s1->sym(), ne2 );
SPLICE ( s2->sym(), ne1 );
SPLICE ( ne1, ne2 );
s2=s1;
while ( s1!=ne1 )
{ s1->_vertex = ne1->vtx();
s1=s1->rot();
if(s1==s2) { printf("MEV2 INFINITE LOOP FOUND!\n"); break; }
// Prevents infinite loop by incoherent args
}
CHECKALL;
SR_TRACE1("Ok.");
return ne1;
}
SeBase *SeMeshBase::mef ( SeBase *s1, SeBase *s2 )
{
SR_TRACE1("mef...");
if ( _op_check_parms )
{ if ( s1->fac()!=s2->fac() ) return _op_error (OpMefNotSameFace);
_op_last_msg = OpNoErrors;
}
SeBase *ne1, *ne2;
EDG_CREATE ( ne1, ne2 );
ne1->_vertex = s2->vtx();
ne2->_vertex = s1->vtx();
_new_edg(ne1); ne2->_edge=ne1->edg();
_new_fac(ne2);
ne1->_face=s1->fac(); s1->_face->_symedge=ne1;
DEF_SPLICE_VARS;
SPLICE ( s1->pri(), ne1 );
SPLICE ( s2->pri(), ne2 );
// s2 = ne2->nxt();
while ( s2!=ne2 ) { s2->_face=ne2->fac(); s2=s2->nxt(); }
CHECKALL;
SR_TRACE1("Ok.");
return ne2;
}
SeBase *SeMeshBase::kev ( SeBase *x )
{
SR_TRACE1("kev...");
if ( _op_check_parms )
{ if ( _edges==1 ) return _op_error(OpKevLastEdge);
if ( x->rot()!=x ) return _op_error(OpKevManyEdges);
_op_last_msg = OpNoErrors;
}
SeBase *xn = x->nxt();
xn->_vertex->_symedge = xn;
xn->_face->_symedge = xn;
_del_vtx ( x->vtx() );
_del_edg ( x->edg() );
SeBase *xsp = x->sym()->pri();
DEF_SPLICE_VARS;
SPLICE ( xsp, x );
delete x->nxt(); delete x;
CHECKALL;
SR_TRACE1("Ok.");
return xsp->nxt();
}
SeBase *SeMeshBase::kev ( SeBase *x, SeBase **s )
{
SR_TRACE1("kev2...");
SeBase *xs = x->sym();
SeBase *xp = x->pri();
SeBase *xsp = xs->pri();
if ( _op_check_parms )
{ if ( _edges==1 ) return _op_error(OpKevLastEdge);
if ( x->rot()==x ) return _op_error(OpKevOneEdge);
_op_last_msg = OpNoErrors;
}
x->nxt()->vtx()->_symedge = x->nxt();
x->fac()->_symedge = xp;
x->sym()->fac()->_symedge = xsp;
_del_vtx ( x->vtx() );
_del_edg ( x->edg() );
DEF_SPLICE_VARS;
SPLICE ( x, xs );
SPLICE ( xsp, x );
SPLICE ( xp, xs );
delete x->nxt(); delete x;
x=xp=xp->sym(); xs=xsp->sym();
while ( x!=xs )
{ x->_vertex=xs->vtx();
x=x->rot();
if(x==xp) break; // Prevents infinite loop by incoherent args
}
*s=xs;
CHECKALL;
SR_TRACE1("Ok.");
return xp;
}
SeBase *SeMeshBase::kef ( SeBase *x, SeBase **s )
{
SR_TRACE1("kef...");
SeBase *xs = x->sym();
SeBase *xp = x->pri();
SeBase *xsp = xs->pri();
if ( _op_check_parms )
{ if ( x->fac()==xs->fac() ) return _op_error(OpKefSameFace);
_op_last_msg = OpNoErrors;
}
x->fac()->_symedge = xp;
xs->fac()->_symedge = xsp;
x->vtx()->_symedge = xs->nxt();
xs->vtx()->_symedge = x->nxt();
_del_edg ( x->edg() );
_del_fac ( x->fac() );
DEF_SPLICE_VARS;
SPLICE ( xsp, x );
SPLICE ( xp, xs );
delete x->nxt(); delete x;
xp=xp->nxt(); x=xs=xsp->nxt();
while ( x!=xp ) { x->_face=xp->fac(); x=x->nxt(); }
if (s) *s=xs;
CHECKALL;
SR_TRACE1("Ok.");
return xp;
}
SeBase *SeMeshBase::mg ( SeBase *s1, SeBase *s2 ) // elems of s2 will be deleted
{
SR_TRACE1("mg...");
if ( _op_check_parms )
{ if ( vertices_in_face(s1)!=vertices_in_face(s2) ) return _op_error(OpMgUncompatibleFaces);
if ( s1->fac()==s2->fac() ) return _op_error(OpMgSameFace);
_op_last_msg = OpNoErrors;
}
_del_fac ( s1->fac() );
_del_fac ( s2->fac() );
SrArray<SeBase*> contour(0,256);
SeBase *x1, *x2, *s;
// Not fully tested: it seems that some fixes will be required like vtx->_symedge=vtx, etc...
x1=s1; x2=s2;
do { _del_edg ( x2->edg() );
_del_vtx ( x2->vtx() );
x2->sym()->_edge = x1->edg();
contour.push()=x1; contour.push()=x2;
contour.push()=x1->sym(); contour.push()=x2->sym();
x1 = x1->nxt();
s=x2; do { s->_vertex=x1->vtx(); s=s->rot(); } while (s!=x2);
x2 = x2->pri();
} while ( x1!=s1 );
for ( int i=0; i<contour.size(); i+=4 )
{ contour[i+2]->_next->_rotate=contour[i+3];
contour[i+3]->_next->_rotate=contour[i+2];
delete contour[i];
delete contour[i+1];
}
CHECKALL;
return s;
}
SeBase *SeMeshBase::flip ( SeBase *x )
{
SR_TRACE1("flip...");
SeBase *xs = x->sym();
SeBase *xp = x->pri();
SeBase *xsp = xs->pri();
if ( _op_check_parms )
{ if ( x->fac()==xs->fac() ) return _op_error(OpFlipOneFace);
_op_last_msg = OpNoErrors;
}
DEF_SPLICE_VARS;
SPLICE ( xsp, x );
SPLICE ( xp, xs );
xp = xp->nxt();
xsp = xsp->nxt();
x->_vertex = xp->nxt()->vtx();
xs->_vertex = xsp->nxt()->vtx();
xsp->_face = xs->fac();
xp->_face = x->fac();
xp->vtx()->_symedge = xp;
xsp->vtx()->_symedge = xsp;
xsp->fac()->_symedge = xsp;
xp->fac()->_symedge = xp;
SPLICE ( xp, xs );
SPLICE ( xsp, x );
CHECKALL;
SR_TRACE1("Ok.");
return xs;
}
SeBase *SeMeshBase::ecol ( SeBase *x, SeBase **s )
{
SR_TRACE1("ecol...");
SeBase *s1, *s2, *z;
if ( _op_check_parms )
{ if ( x->fac()==x->sym()->fac() ) return _op_error(OpEdgColOneFace);
_op_last_msg = OpNoErrors;
}
s1 = kev ( x, &s2 );
s1 = kef ( s1, &z );
s2 = kef ( s2->sym()->nxt(), &z );
*s = s2->rot();
return s1;
}
SeBase *SeMeshBase::vsplit ( SeBase *s1, SeBase *s2 )
{
SR_TRACE1("vtxsplit using mev...");
if ( _op_check_parms )
{ if ( s1==s2 ) return _op_error(OpVtxSplitParmsEqual);
if ( s1->vtx()!=s2->vtx() ) return _op_error(OpVtxSplitNotSameVtx);
_op_last_msg = OpNoErrors;
}
SeBase* s2s = s2->sym();
mef ( s2s->nxt(), s2s );
s1 = mef ( s1, s1->nxt() );
return mev ( s1, s2 );
}
SeBase* SeMeshBase::addv ( SeBase* x )
{
SR_TRACE1("addv...");
if ( _op_check_parms )
{ _op_last_msg = OpNoErrors;
}
SeBase *y = mev ( x );
x=x->nxt();
do { y = mef ( y, x );
x=x->nxt();
} while ( x->nxt()!=y );
return y;
}
SeBase* SeMeshBase::delv ( SeBase* y )
{
SR_TRACE1("delv...");
if ( _op_check_parms )
{ _op_last_msg = OpNoErrors;
}
SeBase *s2;
while ( y!=y->rot() ) y = kef ( y, &s2 );
return kev ( y );
}
//============================ End of File ===============================

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,366 @@
# ifndef SE_TRIANGULATOR_H
# define SE_TRIANGULATOR_H
/** \file se_triangulator.h
* A triangulator based on symedges
*/
# include "sr_polygon.h"
# include "se_mesh.h"
/*! SeTriangulatorManager contains virtual methods that the user needs to provide
to the triangulator, allowing the triangulator to:
1.access the coordinates of a vertex, which is a user-denided type.
2.notify the user of some events: vertices, edges created, etc.
3.access the constrained information of edges. This information should be
stored as an array with the constraints ids sharing the constrained edge.
However, if constraints are guaranteed to not overllap, the user can
simply maintain a boolean variable.
Other utility methods are available in the class.
Note that the required virtual methods to be implemented are only those
related to the triangulator functions being used.
*/
class SeTriangulatorManager : public SrSharedClass
{ public :
/*! Allows retrieving the coordinates of a vertex. This method is pure
virtual, so it needs to be implemented in all cases. */
virtual void get_vertex_coordinates ( const SeVertex* v, double& x, double& y )=0;
/*! Allows setting the coordinates of a vertex. This method is pure
virtual, so it needs to be implemented in all cases. */
virtual void set_vertex_coordinates ( SeVertex* v, double x, double y )=0;
/*! This method is called by SeTriangulator::triangulate_face() to notify
new edges created during the triangulation process, before the optimization
phase, which is based on edge swap. The default implementation simply does nothing.*/
virtual void triangulate_face_created_edge ( SeEdge* e );
/*! This method is called by the triangulator to notify when vertices which
are intersection of two constrained edges are inserted in the triangulation.
The default implementation simply does nothing. */
virtual void new_intersection_vertex_created ( SeVertex* v );
/*! This method is called by the triangulator to notify when steiner vertices are
inserted when recovering conforming line constraints. The default implementation
simply does nothing. */
virtual void new_steiner_vertex_created ( SeVertex* v );
/*! This method is called to track vertices which are found when inserting
edge constraints with insert_line_constraint(). Meaning that the found
vertices v which are in the segment v1, v2 are passed here. The default
implementation simply does nothing. */
virtual void vertex_found_in_constrained_edge ( SeVertex* v );
/*! Should return true if the passed edge is constrained. The default
implementation always returns false. */
virtual bool is_constrained ( SeEdge* e );
/*! Requires that the edge becomes unconstrained. This is called during
manipulation of the triangulation, when edges need to be temporary
unconstrained for consistent edge flipping. The default implementation
does nothing. */
virtual void set_unconstrained ( SeEdge* e );
/*! Used to see if an edge is constrained. The passed array of ids is empty.
If it is returned as empty, it means that the edge is not constrained;
and this is the case of the default implementation. */
virtual void get_constraints ( SeEdge* e, SrArray<int>& ids );
/*! This method is called when the edge needs to be set as constrained.
The passed array contains the ids to be added to the edge. So that
the user can keep track of many constraints sharing the same constrained
edge. The passed array will never be empty. The default implementation
does nothing. */
virtual void add_constraints ( SeEdge* e, const SrArray<int>& ids );
/*! Retrieves the coordinates and call sr_ccw() */
bool ccw ( SeVertex* v1, SeVertex* v2, SeVertex* v3 );
/*! Retrieves the coordinates and call sr_in_triangle() */
bool in_triangle ( SeVertex* v1, SeVertex* v2, SeVertex* v3, SeVertex* v );
/*! Retrieves the coordinates and call sr_in_triangle() */
bool in_triangle ( SeVertex* v1, SeVertex* v2, SeVertex* v3, double x, double y );
/*! Retrieves the coordinates and call sr_in_segment() */
bool in_segment ( SeVertex* v1, SeVertex* v2, SeVertex* v, double eps );
/*! Retrieves the coordinates and call sr_in_circle() */
bool is_delaunay ( SeEdge* e );
/*! Uses sr_ccw() and sr_in_circle() */
bool is_flippable_and_not_delaunay ( SeEdge* e );
/*! Check if (x,y) lies in the interior, edge, or vertex of (v1,v2,v3).
A SeTriangulator::LocateResult enum is returned, and s will be adjacent to
the found element, if any (sr_next() and sr_in_segment() are used). */
int test_boundary ( SeVertex* v1, SeVertex* v2, SeVertex* v3,
double x, double y, double eps, SeBase*& s );
/*! Retrieves the coordinates and call sr_segments_intersect() */
bool segments_intersect ( SeVertex* v1, SeVertex* v2,
SeVertex* v3, SeVertex* v4, double& x, double& y );
/*! Retrieves the coordinates and call sr_segments_intersect() */
bool segments_intersect ( SeVertex* v1, SeVertex* v2, SeVertex* v3, SeVertex* v4 );
/*! Retrieves the coordinates and call sr_segments_intersect() */
bool segments_intersect ( double x1, double y1, double x2, double y2,
SeVertex* v3, SeVertex* v4 );
/*! Returns the midpoint of segment (v1,v2) */
void segment_midpoint ( SeVertex* v1, SeVertex* v2, double& x, double& y );
};
/*! \class SeTriangulator sr_triangulator.h
\brief Delaunay triangulation methods
SeTriangulator provides several methods to construct and update
triangulations, including Delaunay triangulations with support
to constrained and conforming versions.
To use it, a SeTriangulatorManager class is required in order
to tell how the triangulator can access the data in the user mesh.
The triangulator methods were made to be of flexible use for different
applications, so that they only perform the basic algorithms work,
leaving many decisions to the user. In this way it is left as user
responsability to correctly use them. For instance, the associated mesh
must be in fact a triangulation with counter-clockwise triangles, etc,
otherwise several algorithms will fail.
Most of the used techniques are described in the following paper:
M. Kallmann, H. Bieri, and D. Thalmann, "Fully Dynamic Constrained
Delaunay Triangulations", In Geometric Modelling for Scientific
Visualization, G. Brunnett, B. Hamann, H. Mueller, L. Linsen (Eds.),
ISBN 3-540-40116-4, Springer-Verlag, Heidelberg, Germany, pp. 241-257, 2003.
However, some solutions presented in the paper may not be available in the current
version of the code, which favoured to maintain simpler approaches.
All used geometric primitives are implemented in sr_geo2.cpp */
class SeTriangulator
{ public :
/*! Note: ModeConforming is not robust when several constraints intersect */
enum Mode { ModeUnconstrained, ModeConforming, ModeConstrained };
/*! Used by locate_point() method */
enum LocateResult { NotFound, TriangleFound, EdgeFound, VertexFound };
private :
Mode _mode;
double _epsilon;
SeMeshBase* _mesh;
SeTriangulatorManager* _man;
SrArray<SeBase*> _buffer;
SrArray<int> _ibuffer;
struct ConstrElem { SeVertex* v; SeBase* e;
void set(SeVertex* a, SeBase* b) {v=a; e=b;}
};
SrArray<ConstrElem> _elem_buffer;
class PathNode;
class PathTree;
PathTree* _ptree;
bool _path_found;
SrArray<SeBase*> _channel;
double _xi, _yi, _xg, _yg;
class FunnelDeque;
FunnelDeque* _fdeque;
public :
/*! Three modes of triangulations can be created: unconstrained, conforming,
or constrained. Depending on the mode some internal methods will
behave differently. In particular, for unconstrained triangulations
it is not required to reimplement the methods of SeTriangulatorManager
dealing with is/set/get/add edge constraints.
The documentation of each method should tell which methods are used from
both the associated manager and geometric primitives.
The required manager should be allocated with operator new and can be shared.
The epsilon is used in the geometric primitives. */
SeTriangulator ( Mode mode, SeMeshBase* m, SeTriangulatorManager* man, double epsilon );
/*! The destructor unreferences the associated SeTriangulatorManager,
but it does not delete the associated mesh. */
~SeTriangulator ();
/*! Set a new epsilon */
void epsilon ( double eps ) { _epsilon=eps; }
/*! Get the currently used epsilon */
double epsilon () const { return _epsilon; }
/*! Returns the associated mesh pointer */
SeMeshBase* mesh () const { return _mesh; }
/*! Returns the associated manager pointer */
SeTriangulatorManager* manager () const { return _man; }
/*! Set the desired mode */
void mode ( Mode m ) { _mode=m; }
/*! Get the current triangulator mode */
Mode mode () const { return _mode; }
public :
/*! This method destroys the associated mesh and initializes it as
a triangulated square with the given coordinates. Points must
be passed in counter clockwise order. The returned SeBase
is adjacent to vertex 1, edge (1,2) and triangle (1,2,4).
To construct a square with given maximum and minimum coordinates,
points should be set as follows (respectivelly):
xmin, ymin, xmax, ymin, xmax, ymax, xmin, ymax.
Alternatively, the user may initialize the associated mesh directly
by calling the construction operators of SeMesh (such as mev and mef).
Normally the mesh should be initialized as a triangulated convex polygon. */
SeBase* init_as_triangulated_square
( double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4 );
/*! Triangulates a counter-clockwise (ccw) oriented face. It is the
user responsability to provide a face in the correct ccw orientation.
False is returned in case the face cannot be triangulated. However,
this migth only happen if the face is not simple.
It uses the so called "ear" algorithm, that has worst case complexity
of O(n^2), but has a very simple implementation.
It uses the geometric primitives sr_ccw() and sr_in_triangle() and
each time an edge is inserted during the triangulation construction,
the method new_edge_created() of the associated manager is called.
If optimization is set to true (the default), the algorithm will
flip the created edges to satisfy the Delaunay criterium, and for
this, the geometric primitive sr_in_circle() is also used. */
bool triangulate_face ( SeFace* f, bool optimize=true );
/*! This method searches for the location containing the given point.
The enumerator LocateResult is returned with the result of the
query. If the point is coincident to an existing vertex, VertexFound
is returned, and result is incident to the vertex.
If the point is found on an edge, EdgeFound is returned, and result
is incident to the edge. If the point is inside a triangle, result
is incident to the found triangle. In the case that the point is
not found to be inside the triangulation, NotFound is returned.
The algorithm starts with the given iniface, and continously
skips to the neighbour triangle which shares an edge separating the
current triangle and the point to reach in two different semi spaces.
To avoid possible loops, triangle marking is used.
The "distance" of the given iniface and the point to search
dictates the performance of the algorithm. The agorithm is O(n)
(n = number of triangles). In general the pratical performance
is much better than O(n). However serious loop problems will occur
if topological inconsistencies are detected.
This algorithm may fail to find the point if the border of the
triangulation is not convex or if non triangular faces exist.
It is mainly based on the geometric primitive sr_ccw(). But it also
calls methods sr_points_are_equal() and sr_in_segment(), in order to
decide if the point is in an existing edge or vertex. */
LocateResult locate_point ( const SeFace* iniface,
double x, double y, SeBase*& result );
/*! Insert a point in the given triangle, with the given coordinates,
and then continously flips its edges to ensure the Delaunay criterium.
The geometric primitive sr_in_circle() is called during this process.
The new vertex inserted is returned and will be never 0 (null).
Methods new_vertex_created() and new_edge_created() of the associated
manager are called for the new three edges and one vertex created.
If the triangulator is of type conforming, methods for managing the
constraints ids of edges are called, and to maintain the correctness
of the triangulation, new vertices might be automatically inserted
in a recursive mid-point subdivision way.
If the triangulator is of constrained type, points are inserted only
at eventual intersection points. */
SeVertex* insert_point_in_face ( SeFace* f, double x, double y );
/*! Insert a point in the given edge, with the given coordinates.
It might be a good practice to project the point to the segment
defined by the edge before calling this method.
After insertion, it continously flips edges to ensure the Delaunay
criterium, as in insert_point_in_face */
SeVertex* insert_point_in_edge ( SeEdge* e, double x, double y );
/*! Insert point searchs the location of (x,y) and correclty insert it in case it
is located in an edge or face. If a coincident vertex is found it is returned.
If iniface is null, the search starts from mesh()->first()->fac().
Null is returned in case the point cannot be located. */
SeVertex* insert_point ( double x, double y, const SeFace* inifac=0 );
/*! Removes a vertex from the Delaunay triangulation. It is the user responsibility
to guarantee that the vertex being removed is inside a triangulation, and not
at the border of a face which is not triangular.
This method simply calls SeMesh::delv() and then retriangulates the created
non-triangular polygon, so that no special actions are taken concerning
constrained edges. */
bool remove_vertex ( SeVertex* v );
/*! Inserts a line constraint to the current Delaunay triangulation. The line
is defined by two existing vertices. This method has a different behavior
depending on the actual mode of the triangulator:
If the triangulator is in unconstrained mode, nothing is done.
If the mode is conforming, Steiner points are inserted starting with the
middle point of the missing constraint, and recursively inserted by binary
partition until the line becomes present in the triangulation.
If the triangulator is in constrained mode, Steiner points are only inserted
at the intersection of existing constraints, if there are any. Hence,
constrained edges do not respect the Delaunay criterium.
False is returned if the algorithm fails, what can occur if non ccw or non
triangular cells are found, or in unconstrained mode.
The id parameter allows the user to keep track of the created constraints,
and it is passed to the corresponding manager method.
Note that edges may be referenced by several
constrained lines in the case overllap occurs, and thats why each edge
of the triangulation should maintain an array of constraints ids.
Methods new_vertex_created() and vertex_found_in_constrained_edge() of
the manager are called to allow tracking the insertion of Steiner points. */
bool insert_line_constraint ( SeVertex *v1, SeVertex *v2, int id );
/*! Inserts the two points with insert_point() and then call insert_line_constraint().
Returns the success or failure of the operation */
bool insert_segment ( double x1, double y1, double x2, double y2, int id, const SeFace* inifac=0 );
/*! Search for a sequence of triangles (e.g. a channel) connecting x1,y1 and x2,y2,
without crossing edges marked as constrained.
If true is returned, the channel and paths inside the channel can be retrieved
with methods get_channel(), get_canonical_path(), get_shortest_path().
Note that the channel may not be the shortest one available.
Parameter iniface is required in order to feed the process of finding
the triangle containing the initial point p1. If it is already the triangle
containing p1, the search will be solved trivially.
If vistest is true (the default if false) a direct line test is performed prior
to the path search.
The A* algorithm is used, with the simple heuristic "dist(cur_node,goal_node)" */
bool search_path ( double x1, double y1, double x2, double y2,
const SeFace* iniface, bool vistest=false );
/*! Returns a reference to the list with the interior edges of the last channel
determined by a sussesfull call to search_path */
const SrArray<SeBase*>& get_channel_interior_edges () const { return _channel; }
/*! Returns a polygon describing the current channel, and thus, method find_path must
be succesfully called before to determine the channel to consider. */
void get_channel_boundary ( SrPolygon& channel );
/*! Returns the canonical path, which is the path passing through the midpoint of
the channel interior edges. Method search_path must be succesfully called before
in order to determine the channel. The path is returned as an open polygon. */
void get_canonical_path ( SrPolygon& path );
/*! Returns the shortest path inside the current channel using the funnel algorithm. */
void get_shortest_path ( SrPolygon& path );
private :
void _propagate_delaunay ();
bool _conform_line ( SeVertex*, SeVertex*, const SrArray<int>& );
bool _constrain_line ( SeVertex*, SeVertex*, const SrArray<int>& );
void _v_next_step ( SeBase* s, SeVertex* v1, SeVertex* v2, SeVertex*& v, SeBase*& e );
void _e_next_step ( SeBase* s, SeVertex* v1, SeVertex* v2, SeVertex*& v, SeBase*& e );
bool _can_connect ( SeBase* se, SeBase* sv );
bool _blocked ( SeBase* s );
void _ptree_init ( LocateResult res, SeBase* s, double xi, double yi, double xg, double yg );
int _expand_lowest_cost_leaf ();
void _funnel_add ( bool intop, SrPolygon& path, const SrPnt2& p );
};
//============================ End of File =================================
# endif // SE_TRIANGULATOR_H

219
source/dcdt/se/sr.cpp Normal file
View File

@ -0,0 +1,219 @@
#include "precompiled.h"
# include <stdio.h>
# include <string.h>
# include "sr.h"
//================================ sr.cpp ============================================
float sr_todeg ( float radians )
{
return 180.0f * radians / float(SR_PI);
}
double sr_todeg ( double radians )
{
return 180.0 * radians / double(SR_PI);
}
float sr_torad ( float degrees )
{
return float(SR_PI) * degrees / 180.0f;
}
double sr_torad ( double degrees )
{
return double(SR_PI) * degrees / 180.0;
}
float sr_trunc ( float x )
{
return (float) (int) (x);
}
double sr_trunc ( double x )
{
return (double) (int) (x);
}
float sr_round ( float x )
{
return (float) (int) ((x>0.0)? (x+0.5f) : (x-0.5f));
}
double sr_round ( double x )
{
return (double) (int) ((x>0.0)? (x+0.5) : (x-0.5));
}
float sr_floor ( float x )
{
return (float) (int) ((x>0.0)? x : (x-1.0f));
}
double sr_floor ( double x )
{
return (double) (int) ((x>0.0)? x : (x-1.0));
}
float sr_ceil ( float x )
{
return (float) (int) ((x>0.0)? (x+1.0f) : (x));
}
double sr_ceil ( double x )
{
return (double) (int) ((x>0.0)? (x+1.0) : (x));
}
int sr_sqrt ( int n )
{
register int i, s=0, t;
for ( i=15; i>=0; i-- )
{ t = ( s | (1<<i) );
if (t*t<=n) s=t;
}
return s;
}
int sr_fact ( int x )
{
if ( x<2 ) return 1;
int m = x;
while ( --x>1 ) m *= x;
return m;
}
int sr_pow ( int b, int e )
{
if ( e<=0 ) return 1;
int pow=b;
while ( --e>0 ) pow*=b;
return pow;
}
int sr_compare ( const char *s1, const char *s2 )
{
int c1, c2; // ANSI definition of toupper() uses int types
while ( *s1 && *s2 )
{ c1 = SR_UPPER(*s1);
c2 = SR_UPPER(*s2);
if ( c1!=c2 ) return c1-c2;
s1++; s2++;
}
if ( !*s1 && !*s2 ) return 0;
return !*s1? -1:1;
}
int sr_compare_cs ( const char *s1, const char *s2 )
{
int c1, c2; // ANSI definition of toupper() uses int types
while ( *s1 && *s2 )
{ c1 = *s1;
c2 = *s2;
if ( c1!=c2 ) return c1-c2;
s1++; s2++;
}
if ( !*s1 && !*s2 ) return 0;
return !*s1? -1:1;
}
int sr_compare ( const char *s1, const char *s2, int n )
{
int c1, c2; // ANSI definition of toupper() uses int types
// printf("[%s]<>[%s] (%d)\n",s1,s2,n);
while ( *s1 && *s2 )
{ c1 = SR_UPPER(*s1);
c2 = SR_UPPER(*s2);
if ( c1!=c2 ) return c1-c2;
s1++; s2++; n--;
if ( n==0 ) return n; // are equal
}
if ( !*s1 && !*s2 ) return 0;
return !*s1? -1:1;
}
int sr_compare_cs ( const char *s1, const char *s2, int n )
{
int c1, c2; // ANSI definition of toupper() uses int types
// printf("[%s]<>[%s] (%d)\n",s1,s2,n);
while ( *s1 && *s2 )
{ c1 = *s1;
c2 = *s2;
if ( c1!=c2 ) return c1-c2;
s1++; s2++; n--;
if ( n==0 ) return n; // are equal
}
if ( !*s1 && !*s2 ) return 0;
return !*s1? -1:1;
}
int sr_compare ( const int *i1, const int *i2 )
{
return *i1-*i2;
}
int sr_compare ( const float *f1, const float *f2 )
{
return SR_COMPARE(*f1,*f2);
}
int sr_compare ( const double *d1, const double *d2 )
{
return SR_COMPARE(*d1,*d2);
}
char* sr_string_new ( const char *tocopy )
{
if ( !tocopy ) return 0;
char *s = new char [ strlen(tocopy)+1 ];
strcpy ( s, tocopy );
return s;
}
char* sr_string_set ( char*& s, const char *tocopy )
{
delete[] s;
if ( !tocopy ) { s=0; return s; }
s = new char [ strlen(tocopy)+1 ];
strcpy ( s, tocopy );
return s;
}
char* sr_string_realloc ( char*& s, int size )
{
char *news = 0;
if ( size>0 )
{ news = new char[size];
news[0] = 0;
if ( s )
{ int i;
for ( i=0; i<size; i++ )
{ news[i]=s[i]; if(!s[i]) break; }
}
news[size-1] = 0;
}
delete []s;
s = news;
return s;
}
void sr_stdout_to_file ()
{
freopen ( "stdout.txt", "w", stdout );
setbuf ( stdout, NULL );
freopen ( "stderr.txt", "w", stderr );
setbuf ( stderr, NULL );
}
//=== End of File =======================================================================

367
source/dcdt/se/sr.h Normal file
View File

@ -0,0 +1,367 @@
# ifndef SR_H
# define SR_H
/** \file sr.h
* Main header file of the SR toolkit
*
\code
*************************************************************************
*
* SR - Simulation and Representation Toolkit
* (also called the small scene graph toolkit)
* Marcelo Kallmann 1995-2004
*
* This toolkit was develloped as support for research on modelling,
* computational geometry, animation and robotics.
*
* Main features are classes for:
* data structures, math, scene graph, meshes,
* dynamic constrained delaunay triangulations and articulated characters.
*
* Support for OpenGL and FLTK libraries is provided in libs srgl and srfl.
*
* Main design goals are to be simple and flexible when linking with other tools.
* Design choices:
* - Small number of classes, with clear and independent functionality
* - Uses old c standard library dependency, for most portability and efficiency
* - Simple scene graph nodes for small scenes, this is not a scene library
* - Transformation matrices follow column major OpenGL format
* - Angle parameters are in radians, but degrees are used in data files
* - float types are normally preferred than double types, but both are used
* - Multiple inheritance is avoided when possible
* - Templates are used mainly as type-casting to generic classes, when possible
* - Most geometric classes are 3D; otherwise a letter is used to indicate
* the dimension, e.g.: SrVec2, SrMatn, etc
*
*************************************************************************
*
* Nomenclature conventions/examples :
*
* global functions : sr_time()
* global variables : sr_out
* global defines : SR_BIT0, SR_USE_TRACE1
* global consts : sre, srpi
* global typedefs : srbyte, sruint
* global enums : srMyEnum
* global classes : SrList, SrVec, SrVec2
* class public members : a.size(), a.size(3), v.normalize()
* class private members : _num_vertices, _size
* class enums : class SrClass { enum Msg { MsgOk, MsgError } };
* class friends (no prefix) : dist ( const SrVec& x, const SrVec& y )
*
* If compiling in windows, make sure the _WIN32 macro is defined.
*
* Macros for the library compilation (which are not defined by default):
* - SR_DEF_BOOL : If the compiler does not have 'bool true false' as keywords,
* defining this macro will make sr to define them.
* - SR_BV_MATH_FLOAT : To change default type to float in sr_bv_math.h
*
*************************************************************************
\endcode */
// ==================================== Types =================================
# ifdef _WIN32
# define SR_TARGET_WINDOWS //!< Defined if compiled for windows
# else
# define SR_TARGET_LINUX //!< Defined if not compiled in windows
# endif
# ifdef SR_DEF_BOOL
enum bool { false, true }; //!< for old compilers without bool/true/false keywords
# endif
// The following types should be adjusted according to the used system
typedef void* srvoidpt; //!< a pointer to a char
typedef char* srcharpt; //!< a pointer to a char
typedef signed char srchar; //!< 1 byte signed int, from -127 to 128
typedef unsigned char srbyte; //!< 1 byte unsigned int, from 0 to 255
typedef unsigned short srword; //!< 2 bytes unsigned int, from 0 to 65,535
typedef short int srsint; //!< 2 bytes integer, from -32,768 to 32,767
typedef unsigned int sruint; //!< 4 bytes unsigned int, from 0 to 4294967295
typedef signed int srint; //!< 4 bytes signed integer, from -2147483648 to 2147483647
/*! Defines a typedef for a generic comparison function in the form:
int srcompare ( const void*, const void* ), that is used by data structure classes. */
typedef int (*srcompare) ( const void*, const void* );
/*! Defines a generic comparison function for the type X, to be used as argument
for template based classes. Same as: int (*sr_compare) (const X*,const X*) */
# define SR_COMPARE_FUNC int (*sr_compare_func) (const X*,const X*)
// ================================ Some Constants ======================
const char srnl = '\n'; //!< Contains the newline '\n' char
const char srtab = '\t'; //!< Contains the tab '\t' char
const char srspc = ' '; //!< Contains the space char
const float srtiny = 1.0E-6f; //!< 1E-6
const float sre = 2.71828182f; //!< 2.7182818
const float srpi = 3.14159265f; //!< 3.1415926
const float srpidiv2 = 1.57079632f; //!< 1.57079632
const float sr2pi = 6.28318530f; //!< 2*pi
const float srsqrt2 = 1.41421356f; //!< sqrt(2) = 1.4142135
const float srsqrt3 = 1.73205080f; //!< sqrt(3) = 1.7320508
const float srsqrt6 = 2.44948974f; //!< sqrt(6) = 2.4494897
const sruint sruintmax = ((sruint)0)-1; //!< the unsigned int maximum value
/* floats and doubles have precision of 7 and 15 decimal digits */
# define SR_E 2.7182818284590452 //!< 2.71828...
# define SR_PI 3.1415926535897932 //!< 3.141592...
# define SR_PIDIV2 1.5707963267948966 //!< 1.570796...
# define SR_2PI 6.2831853071795864 //!< 2*pi
# define SR_SQRT2 1.4142135623730950 //!< sqrt(2) = 1.4142...
# define SR_SQRT3 1.7320508075688772 //!< sqrt(3) = 1.7320...
# define SR_SQRT6 2.4494897427831780 //!< sqrt(6) = 2.4494...
// ================================= Macros ==================================
/*! \def SR_ASSERT
The macro SR_ASSERT(exp) is expanded to a code that sends an error message
to sr_out and exist the application, using sr_out.fatal_error()). */
# define SR_ASSERT(exp) if ( !(exp) ) sr_out.fatal_error("SR_ASSERT failure in %s::%d !\n",__FILE__,__LINE__);
/*! Macro that puts c in lower case if c is a valid upper case letter. */
# define SR_LOWER(c) ( (c)>='A'&&(c)<='Z'? (c)-'A'+'a':(c) )
/*! Macro that puts c in upper case if c is a valid lower case letter. */
# define SR_UPPER(c) ( (c)>='a'&&(c)<='z'? (c)-'a'+'A':(c) )
/*! Macro that returns 0 if a is equal to b, 1 if a>b, and -1 otherwise. */
# define SR_COMPARE(a,b) (a==b)? 0: (a>b)? 1: -1
/*! Macro that swaps the value of a boolean type. */
# define SR_SWAPB(b) b = !b // B from bool
/*! Macro that swaps the values of a and b using three xor logical operations. */
# define SR_SWAPX(a,b) { a^=b; b^=a; a^=b; } // x from xor
/*! Macro that swaps the values of a and b, using tmp as temporary variable. */
# define SR_SWAPT(a,b,tmp) { tmp=a; a=b; b=tmp; }
/*! Macro that swaps the values of a and b, given that a temporary
variable named tmp, of the same type as a and b exists. */
# define SR_SWAP(a,b) { tmp=a; a=b; b=tmp; }
/*! Macro that returns a number multiple of gap, but that is greater or equal to size. */
# define SR_SIZE_WITH_GAP(size,gap) ( (gap) * ( ((size)/(gap)) + ((size)%(gap)==0?0:1) ) )
/*! Macro that makes m to be x, if x is greater than m. */
# define SR_UPDMAX(m,x) if((x)>(m)) m=x
/*! Macro that makes m to be x, if x is smaller than m. */
# define SR_UPDMIN(m,x) if((x)<(m)) m=x
/*! Macro that tests if x is inside the interval [i,s]. */
# define SR_BOUNDED(x,i,s) ((i)<=(x) && (x)<=(s))
/*! Macro that returns x clipped by the interval [i,s]. */
# define SR_BOUND(x,i,s) (x)<(i)? (i): (x)>(s)? (s): (x)
/*! Macro that forces a to be positive by negating it if it is negative. */
# define SR_POS(a) if((a)<0) a=-(a)
/*! Macro that forces a to be negative by negating it if it is positive. */
# define SR_NEG(a) if((a)>0) a=-(a)
/*! Macro that returns x, so that x = a(1-t) + bt. */
# define SR_LERP(a,b,t) ((a)*(1-(t))+(b)*(t)) // return x = a(1-t) + bt
/*! Macro that returns t, so that x = a(1-t) + bt. */
# define SR_PARAM(a,b,x) ((x)-(a))/((b)-(a)) // return t : x = a(1-t) + bt
/*! Macro that truncates x, with an int typecast. */
# define SR_TRUNC(x) ( (int) (x) )
/*! Macro that returns x rounded to the nearest integer. */
# define SR_ROUND(x) ( (int) ((x>0)? (x+0.5f):(x-0.5f)) )
/*! Macro that rounds x to the nearest integer, but to be
applied only when x is positive. This macro adds 0.5
and does an int typecast. */
# define SR_ROUNDPOS(x) ( (int) (x+0.5) )
/*! Macro that returns the lowest integer of x. */
# define SR_FLOOR(x) ( int( ((x)>0)? (x):((x)-1) ) )
/*! Macro that returns the highest integer of x. */
# define SR_CEIL(x) ( int( ((x)>0)? ((x)+1):(x) ) )
/*! Macro that returns the maximum value of the two arguments. */
# define SR_MAX(a,b) ((a)>(b)? (a):(b))
/*! Macro that returns the maximum value of the three arguments. */
# define SR_MAX3(a,b,c) ((a)>(b)? (a>c?(a):(c)):((b)>(c)?(b):(c)))
/*! Macro that returns the minimum value of the two arguments. */
# define SR_MIN(a,b) ((a)<(b)? (a):(b))
/*! Macro that returns the minimum value of the three arguments. */
# define SR_MIN3(a,b,c) ((a)<(b)? ((a)<(c)?(a):(c)):((b)<(c)?(b):(c)))
/*! Macro that returns the absolute value of x. */
# define SR_ABS(x) ((x)>0? (x):-(x))
/*! Macro that returns |a-b|, that is the distance of two points in the line. */
# define SR_DIST(a,b) ( (a)>(b)? ((a)-(b)):((b)-(a)) )
/*! Macro that tests if the distance between a and b is closer or equal to ds. */
# define SR_NEXT(a,b,ds) ( ( (a)>(b)? ((a)-(b)):((b)-(a)) )<=(ds) )
/*! Macro that tests if the distance between a and 0 is closer or equal to ds. */
# define SR_NEXTZ(a,eps) ( (a)>-(eps) && (a)<(eps) ) // z from zero
/*! Macro that returns -1 if x is negative, 1 if x is positive and 0 if x is zero. */
# define SR_SIGN(x) ((x)<0)? -1: ((x)>0)? 1: 0
/*! Returns the converted angle, from radians to degrees (float version). */
# define SR_TODEG(r) (180.0f*float(r)/srpi)
/*! Returns the converted angle, from degrees to radians (float version). */
# define SR_TORAD(d) (srpi*float(d)/180.0f)
/*! Returns the converted angle, from radians to degrees (double version). */
# define SR_TODEGd(r) (180.0*double(r)/SR_PI)
/*! Returns the converted angle, from degrees to radians (double version). */
# define SR_TORADd(d) (SR_PI*double(d)/180.0)
// ============================== Math Utilities ===========================
/*! Returns the convertion from radians to degrees (float version). */
float sr_todeg ( float radians );
/*! Returns the convertion from radians to degrees (double version). */
double sr_todeg ( double radians );
/*! Returns the convertion from degrees to radians (float version). */
float sr_torad ( float degrees );
/*! Returns the convertion from degrees to radians (double version). */
double sr_torad ( double degrees );
/*! Returns the integer part of x by using a sequence of type casts (float version). */
float sr_trunc ( float x );
/*! Returns the integer part of x by using a sequence of type casts (double version). */
double sr_trunc ( double x );
/*! Returns the closest integer of x (float version). */
float sr_round ( float x );
/*! Returns the closest integer of x (double version). */
double sr_round ( double x );
/*! Returns the lowest rounded value of x (float version). */
float sr_floor ( float x );
/*! Returns the lowest rounded value of x (double version). */
double sr_floor ( double x );
/*! Returns the highest rounded value of x (float version). */
float sr_ceil ( float x );
/*! Returns the highest rounded value of x (double version). */
double sr_ceil ( double x );
/*! Returns the square root for integer values, with no use of floating point. */
int sr_sqrt ( int x );
/*! sr_fact returns the factorial of x. */
int sr_fact ( int x );
/*! returns "b^e", e must be >=0 */
int sr_pow ( int b, int e );
// ============================= Compare Functions ============================
/*! Case insensitive comparison of strings in the C style
This function follows the C style of compare functions where 0 is returned if
s1==s2, <0 if s1<s2, and >0 otherwise. Comparisons are case-insensitive.
s1 and s2 must be non-null pointers, otherwise unpredictable results will arise.
If two strings have the first n characters equal, where one has lenght n, and
the other has length >n, the smaller one is considered to come first. */
int sr_compare ( const char *s1, const char *s2 );
/*! Case sensitive comparison of strings in the C style */
int sr_compare_cs ( const char *s1, const char *s2 );
/*! Case insensitive compare strings, but compares a maximum of n characters. */
int sr_compare ( const char *s1, const char *s2, int n );
/*! Case sensitive compare strings, but compares a maximum of n characters. */
int sr_compare_cs ( const char *s1, const char *s2, int n );
/*! Compares two integers, returning 0 if they're equal, <0 if i1<i2, and >0 otherwise. */
int sr_compare ( const int *i1, const int *i2 );
/*! Compares two floats, returning 0 if they're equal, <0 if f1<f2, and >0 otherwise. */
int sr_compare ( const float *f1, const float *f2 );
/*! Compares two doubles, returning 0 if they're equal, <0 if d1<d2, and >0 otherwise. */
int sr_compare ( const double *d1, const double *d2 );
// ============================== C String Utilities ============================
/*! Allocates a string with sufficient size to copy 'tocopy' in it.
The allocation is simply done with operator new, and the allocated
memory pointer is returned. If tocopy==0, the value 0 is returned. */
char* sr_string_new ( const char* tocopy );
/*! Deletes s, and reallocates s with sufficient size to copy 'tocopy'
in it. If tocopy==0, s will be a null pointer, and 0 is returned.
Otherwise the allocation is simply done with operator new, the
allocated memory pointer is returned and s is changed to point to
this new memory allocated so that the returned value will be the
same as s. */
char* sr_string_set ( char*& s, const char *tocopy );
/*! Deletes s, and reallocates it with the given size also copying its
contents to the new allocated position. If size<=0, s is simply
deleted. If the new size is smaller then the original s length,
the contents will be truncated. In all cases, the new s is returned
and will be a valid string, having the ending null char. This
function is similar to the C standard realloc function, but using
C++ operators new/delete. */
char* sr_string_realloc ( char*& s, int size );
// ============================== Standard IO ============================
/*! Redirects the C streams stdout and stderr to the text files
stdout.txt and stderr.txt in the current folder */
void sr_stdout_to_file ();
// ============================== Bit Operation ============================
/*! Tests if flg has the given bit set. */
# define SR_FLAG_TEST(flg,bit) ((flg)&(bit))
/*! Sets on the given bit of the flag. */
# define SR_FLAG_ON(flg,bit) flg|=bit
/*! Swaps the given bit of the flag. */
# define SR_FLAG_SWAP(flg,bit) flg^=bit
/*! Sets off the given bit of the flag. This requires two instructions, so
that SR_FLAG_SWAP is faster. */
# define SR_FLAG_OFF(flg,bit) if((flg)&(bit)) flg^=bit
/*! Sets the given bit of the flag to be on or off, according to the boolean value of val. */
# define SR_FLAG_SET(flg,bit,val) flg = (val)? (flg)|(bit) : ((flg)&(bit)? (flg)^(bit):flg)
# define SR_BIT0 1 //!< Same as 1
# define SR_BIT1 2 //!< Same as 2
# define SR_BIT2 4 //!< Same as 4
# define SR_BIT3 8 //!< Same as 8
# define SR_BIT4 16 //!< Same as 16
# define SR_BIT5 32 //!< Same as 32
# define SR_BIT6 64 //!< Same as 64
# define SR_BIT7 128 //!< Same as 128
# define SR_ALLBITS 255 //!< Same as 255
# define SR_NOBITS 0 //!< Same as 0
//============================== end of file ===============================
# endif // SR_H

224
source/dcdt/se/sr_alg.cpp Normal file
View File

@ -0,0 +1,224 @@
#include "precompiled.h"
# include <math.h>
# include "sr.h"
# include "sr_alg.h"
/*-------------------------------------------------------------------*/
/* Functions to solve polynomials of 2nd, 3rt and 4th degree. */
/* Source: graphics gems */
/*-------------------------------------------------------------------*/
//change kai:
//# define aMAXFLOAT 3.40282347E+38F
# define aMAXFLOAT 3.40282347E+28F
# define aEPSILON 1e-9
# define aEPSILON2 0.00001
# define aISZERO(x) ((x) > -aEPSILON && (x) < aEPSILON)
# define aCBRT(x) ((x) > 0.0 ? pow((double)(x), 1.0/3.0) : \
((x) < 0.0 ? -pow((double)-(x), 1.0/3.0) : 0.0))
int sr_solve_quadric_polynomial ( double c[3], double s[2] )
{
double p, q, D;
// normal form: x^2 + px + q = 0
p = c[1] / (2*c[2]);
q = c[0] / c[2];
D = p*p - q;
if ( aISZERO(D) )
{ s[0] = -p;
return 1;
}
else if ( D<0 )
{ return 0;
}
else // if (D > 0)
{ double sqrt_D = sqrt(D);
s[0] = sqrt_D - p;
s[1] = - sqrt_D - p;
return 2;
}
}
int sr_solve_cubic_polynomial ( double c[4], double s[3] )
{
int i, num;
double sub;
double A, B, C;
double sq_A, p, q;
double cb_p, D;
// normal form: x^3 + Ax^2 + Bx + C = 0
A = c[2] / c[3];
B = c[1] / c[3];
C = c[0] / c[3];
// substitute x = y - A/3 to eliminate quadric term:
// x^3 +px + q = 0
sq_A = A * A;
p = 1.0/3 * (- 1.0/3 * sq_A + B);
q = 1.0/2 * (A * (2.0/27 * sq_A - 1.0/3 * B) + C);
// use Cardano's formula
cb_p = p * p * p;
D = q * q + cb_p;
if ( aISZERO(D) )
{ if ( aISZERO(q) ) // one triple solution
{ s[0] = 0;
num = 1;
}
else // one single and one double solution
{ double u = aCBRT(-q);
s[0] = 2 * u;
s[1] = - u;
num = 2;
}
}
else if ( D<0 ) // Casus irreducibilis: three real solutions
{ double phi = 1.0/3 * acos ( -q/sqrt(-cb_p) );
double t = 2 * sqrt(-p);
s[0] = t * cos(phi);
s[1] = - t * cos(phi + SR_PI / 3);
s[2] = - t * cos(phi - SR_PI / 3);
num = 3;
}
else /* one real solution */
{ double sqrt_D = sqrt(D);
double u = aCBRT(sqrt_D - q);
double v = - aCBRT(sqrt_D + q);
s[0] = u + v;
num = 1;
}
// resubstitute
sub = 1.0/3 * A;
for ( i=0; i<num; ++i ) s[i] -= sub;
return num;
}
int sr_solve_quartic_polynomial ( double c[5], double s[4] )
{
double coeffs[4];
double z, u, v, sub;
double A, B, C, D;
double sq_A, p, q, r;
int i, num;
// normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0
A = c[3] / c[4];
B = c[2] / c[4];
C = c[1] / c[4];
D = c[0] / c[4];
// substitute x = y - A/4 to eliminate cubic term:
// x^4 + px^2 + qx + r = 0
sq_A = A * A;
p = - 3.0/8 * sq_A + B;
q = A * (1.0/8 * sq_A - 1.0/2 * B) + C;
r = sq_A * (- 3.0/256*sq_A + 1.0/16*B) - 1.0/4*A*C + D;
if ( aISZERO(r) ) // no absolute term: y(y^3 + py + q) = 0
{ coeffs[0] = q;
coeffs[1] = p;
coeffs[2] = 0;
coeffs[3] = 1;
num = sr_solve_cubic_polynomial ( coeffs, s );
s[num++] = 0;
}
else // solve the resolvent cubic ...
{ coeffs[0] = 1.0/2 * r * p - 1.0/8 * q * q;
coeffs[1] = - r;
coeffs[2] = - 1.0/2 * p;
coeffs[3] = 1;
sr_solve_cubic_polynomial ( coeffs, s );
// ... and take the one real solution ...
z = s[ 0 ];
// ... to build two quadric equations
u = z * z - r;
v = 2 * z - p;
if (aISZERO(u)) u = 0;
else if (u > 0) u = sqrt(u);
else return 0;
if (aISZERO(v)) v = 0;
else if (v > 0) v = sqrt(v);
else return 0;
coeffs[0] = z - u;
coeffs[1] = q < 0 ? -v : v;
coeffs[2] = 1;
num = sr_solve_quadric_polynomial ( coeffs, s );
coeffs[0]= z + u;
coeffs[1] = q < 0 ? v : -v;
coeffs[2] = 1;
num += sr_solve_quadric_polynomial ( coeffs, s+num );
}
// resubstitute
sub = 1.0/4 * A;
for ( i=0; i<num; ++i ) s[i] -= sub;
return num;
}
float sr_in_ellipse ( float x, float y, float a, float b )
{
float cx = x/a;
float cy = y/b;
return cx*cx + cy*cy - 1;
}
// replaces (x,y) by its closest point on the ellipse (a,b)
void sr_get_closest_on_ellipse ( float a, float b, float& x, float& y )
{
double c[5], s[4];
double r = a / b,
e = 2. * (b - a*r),
f = 2. * (x * r);
if ( fabs(y)<aEPSILON2 )
{ x = x>0 ? a : -a;
y = 0;
return;
}
c [4] = y;
c [3] = f - e;
c [2] = 0;
c [1] = f + e;
c [0] = -y;
int nb = sr_solve_quartic_polynomial ( c, s );
double denom, s_theta, c_theta, dist;
double test_x [4], test_y [4];
double min_dist = aMAXFLOAT;
int i, winner=0;
// Find the closest point
for ( i=0; i<nb; i++ )
{ denom = 1. + s [i] * s [i];
s_theta = 2. * s [i] / denom;
c_theta = (1. - s [i] * s [i]) / denom;
test_x[i] = a * c_theta;
test_y[i] = b * s_theta;
dist = (test_x[i]-x) * (test_x[i]-x) + (test_y[i]-y) * (test_y[i]-y);
if ( dist<min_dist )
{ min_dist = dist;
winner = i;
}
}
x = (float)test_x[winner];
y = (float)test_y[winner];
}
//============================== end of file ===============================

34
source/dcdt/se/sr_alg.h Normal file
View File

@ -0,0 +1,34 @@
/** \file sr_alg.h
* few algebra functions */
# ifndef SR_ALG_H
# define SR_ALG_H
/*! Solve polynomials of 2nd degree. The coefficients are given in c,
where c[i] is the coefficient of the i-th power of the unknown variable.
The roots are stored in s, and the number of stored roots is returned. */
int sr_solve_quadric_polynomial ( double c[3], double s[2] );
/*! Solve polynomials of 3rd degree. The coefficients are given in c,
where c[i] is the coefficient of the i-th power of the unknown variable.
The roots are stored in s, and the number of stored roots is returned. */
int sr_solve_cubic_polynomial ( double c[4], double s[3] );
/*! Solve polynomials of 4th degree. The coefficients are given in c,
where c[i] is the coefficient of the i-th power of the unknown variable.
The roots are stored in s, and the number of stored roots is returned. */
int sr_solve_quartic_polynomial ( double c[5], double s[4] );
/*! Returns <0 if (x,y) is inside the ellipse with radius (a,b), returns
0 if it is on the ellipse, and returns >0 if it is outside */
float sr_in_ellipse ( float x, float y, float a, float b );
/*! This function replaces the given point (x,y) by the closest point
on the ellipse whose semi-axes are of length a and b. The solution
is derived analyticaly, solving a quartic polynomial */
void sr_get_closest_on_ellipse ( float a, float b, float& x, float& y );
//============================== end of file ===============================
# endif // SR_ALG_H

189
source/dcdt/se/sr_array.cpp Normal file
View File

@ -0,0 +1,189 @@
#include "precompiled.h"
# include <stdlib.h>
# include <string.h>
# include "sr_array.h"
//# define SR_USE_MEM_CONTROL
# include "sr_mem_control.h"
# define DATA(i) ((char*)_data)+(sizeofx*(i))
# define NEWDATA(i) ((char*)newdata)+(sizeofx*(i))
//================================= methods =======================================================
SrArrayBase::SrArrayBase ( sruint sizeofx, int s, int c )
: _size(s), _capacity(c)
{
if ( _capacity<_size ) _capacity=_size;
_data = _capacity>0? SR_MALLOC(sizeofx*_capacity) : 0;
}
SrArrayBase::SrArrayBase ( sruint sizeofx, const SrArrayBase& a )
: _size(a._size), _capacity(a._size)
{
if ( _capacity>0 )
{ _data = SR_MALLOC ( sizeofx*_capacity );
if ( _size>0 ) memcpy ( _data, a._data, sizeofx*_size );
}
else _data = 0;
}
SrArrayBase::SrArrayBase ( void* pt, int s, int c )
: _data(pt), _size(s), _capacity(c)
{
}
void SrArrayBase::free_data ()
{
if (_data) SR_FREE(_data);
}
void SrArrayBase::size ( unsigned sizeofx, int ns )
{
_size = ns;
if ( _size<0 ) _size=0;
if ( _size>_capacity )
{ _capacity = _size;
_data = realloc ( _data, sizeofx*_capacity ); // if _data==0, realloc reacts as malloc.
}
}
void SrArrayBase::capacity ( unsigned sizeofx, int nc )
{
if ( nc<0 ) nc = 0;
if ( nc==_capacity ) return;
if ( nc==0 ) { if(_data)free(_data); _data=0; _capacity=_size=0; return; }
_capacity = nc;
if ( _size>_capacity ) _size=_capacity;
_data = realloc ( _data, sizeofx*_capacity ); // if _data==0, realloc reacts as malloc.
}
int SrArrayBase::validate ( int index ) const
{
if ( index<0 ) index += _size * ( -index/_size + 1 );
SR_ASSERT ( index>=0 );
return index%_size;
}
void SrArrayBase::compress ( sruint sizeofx )
{
if ( _size==_capacity ) return;
if ( !_size ) { SR_FREE ( _data ); _data=0; }
else _data = SR_REALLOC ( _data, sizeofx*_size );
_capacity = _size;
}
void SrArrayBase::remove ( sruint sizeofx, int i, int dp )
{
if ( i<_size-dp ) memmove ( DATA(i), DATA(i+dp), sizeofx*(_size-(i+dp)) );
_size-=dp;
}
void SrArrayBase::insert ( sruint sizeofx, int i, int dp )
{
SR_ASSERT ( i>=0 && i<=_size);
SR_ASSERT ( dp>0 );
_size += dp;
if ( _size>_capacity )
{ _capacity = _size*2;
_data = SR_REALLOC ( _data, sizeofx*_capacity );
}
if ( i<_size-dp )
{ memmove ( DATA(i+dp), DATA(i), sizeofx*(_size-dp-i) ); // ok with overlap
}
}
void SrArrayBase::move ( sruint sizeofx, int dest, int src, int n )
{
memmove ( DATA(dest), DATA(src), sizeofx*(n) );
}
void SrArrayBase::sort ( sruint sizeofx, srcompare cmp )
{
::qsort ( _data, (size_t)_size, (size_t)sizeofx, cmp );
}
int SrArrayBase::lsearch ( sruint sizeofx, const void *x, srcompare cmp ) const
{
char *pt = (char*)_data;
for ( int i=0; i<_size; i++ ) { if ( cmp(pt,x)==0 ) return i; pt+=sizeofx; }
return -1;
}
int SrArrayBase::bsearch ( sruint sizeofx, const void *x, srcompare cmp, int *pos ) const
{
int comp;
int i, e, p;
comp=1; i=0; e=_size; p=(e-i)/2;
while ( comp!=0 && i!=e )
{ comp = cmp ( x, DATA(p) );
if ( comp<0 ) e = p;
else if ( comp>0 ) i = p+1;
p = i + (e-i)/2;
}
if (pos) *pos=p;
return comp==0? p : -1;
}
int SrArrayBase::insort ( sruint sizeofx, const void *x, srcompare cmp, bool allowdup )
{
int result, pos;
result = bsearch ( sizeofx, x, cmp, &pos );
if ( result!=-1 && !allowdup ) return -1;
insert ( sizeofx, pos, 1 );
return pos;
}
void SrArrayBase::copy ( sruint sizeofx, const SrArrayBase& a )
{
if ( _data==a._data ) return;
if ( _data ) { SR_FREE(_data); _data=0; }
_capacity = a._capacity;
_size = a._size;
if ( _capacity>0 )
{ _data = SR_MALLOC ( sizeofx*_capacity );
if ( _size>0 ) memcpy ( _data, a._data, sizeofx*_size );
}
}
void* SrArrayBase::leave_data ()
{
void *pt = _data;
_data=0;
_size=0;
_capacity=0;
return pt;
}
void SrArrayBase::take_data ( SrArrayBase& a )
{
if ( _data ) SR_FREE ( _data );
_data = a._data; a._data=0;
_size = a._size; a._size=0;
_capacity = a._capacity; a._capacity=0;
}
void SrArrayBase::take_data ( void* pt, int s, int c )
{
if ( _data ) SR_FREE ( _data );
_data = pt;
_size = s;
_capacity = c;
}
//============================== end of file ===============================

341
source/dcdt/se/sr_array.h Normal file
View File

@ -0,0 +1,341 @@
# ifndef SR_ARRAY_H
# define SR_ARRAY_H
/** \file sr_array.h
* fast resizeable array template */
# include "sr.h"
# include "sr_input.h"
# include "sr_output.h"
/*! \class SrArrayBase sr_array.h
\brief Fast resizeable array base class
This class is to be derived, and not to be directly used. See SrArray for
a user-ready class. All memory management functions of SrArrayBase were
written using quick memory block functions. In this way, malloc() and free()
functions are used. So that void pointers are used to refer to user's data.
Most methods need to know the size of each element that is the parameter
sizeofx appearing several times. */
class SrArrayBase
{ protected :
void* _data; //!< Array pointer used for storage
int _size; //!< Number of elements being used in array
int _capacity; //!< Number of allocated elements (>=size)
protected :
/*! Init with the sizeof of each element, size, and capacity. If the given
capacity is smaller than the size, capacity is set to be equal to size */
SrArrayBase ( sruint sizeofx, int s, int c );
/*! Copy constructor. Allocates and copies size elements. */
SrArrayBase ( sruint sizeofx, const SrArrayBase& a );
/*! Constructor from a given buffer. No checkings are done,
its the user responsability to give consistent parameters. */
SrArrayBase ( void* pt, int s, int c );
/*! Will free the internal buffer if needed. The internal size and capacity
are not adjusted, so that this method should be called only by the
destructor of the derived class, as SrArrayBase has not a destructor. */
void free_data ();
/*! Changes the size of the array. Reallocation is done only when the size
requested is greater than the current capacity, and in this case,
capacity becomes equal to the size. */
void size ( unsigned sizeofx, int ns );
/*! Changes the capacity of the array. Reallocation is done whenever a new
capacity is requested. Internal memory is freed in case of 0 capacity.
The size is always kept inside [0,nc]. Parameter nc is considered 0
if it is negative. */
void capacity ( unsigned sizeofx, int nc );
/*! Returns a valid index as if the given index references a circular
array, ie, it returns index%size() for positive numbers. Negative
numbers are also correctly mapped. */
int validate ( int index ) const;
/*! Makes size==capacity, freeing all extra capacity if any. */
void compress ( sruint sizeofx );
/*! Removes positions starting with pos i, and length n, moving all data
correctly. The parameters must be valid, no checkings are done. No
reallocation is done. */
void remove ( sruint sizeofx, int i, int n );
/*! Inserts dp positions, starting at pos i, moving all data correctly.
Parameter i can be between 0 and size(), if i==size(), n positions
are appended. If reallocation is needed, the array capacity is
reallocated to contain two times the new size (after insertion). */
void insert ( sruint sizeofx, int i, int n );
/*! Copies n entries from src position to dest position. Regions are
allowed to overlap. Uses the C function memmove. */
void move ( sruint sizeofx, int dest, int src, int n );
/*! Sorts the array, with the compare function cmp, by calling the
system function qsort(). */
void sort ( sruint sizeofx, srcompare cmp );
/*! Linear search, returns index of the element found, or -1 if not found */
int lsearch ( sruint sizeofx, const void *x, srcompare cmp ) const;
/*! Binary search for sorted arrays. Returns index of the element found,
or -1 if not found. If not found and pos is not null, pos will have
the position to insert the element keeping the array sorted. Faster
than the standard C library function bsearch() for large arrays. */
int bsearch ( sruint sizeofx, const void *x, srcompare cmp, int *pos ) const;
/*! Returns the position of the insertion, or -1 if not inserted. The
space of sizeofx is created and the index of the position is returned,
but get attention to the fact that the contents of x are not moved to
the inserted position. In this way, x can be only a key to the data
stored in the array. In case of duplication, the insertion is not done
if parameter allowdup is given as false. To insert the element position,
insert() method is called. */
int insort ( sruint sizeofx, const void *x, srcompare cmp, bool allowdup );
/*! Copy from another array. SrArrayBase will be an exact copy of the given array,
allocating the same capacity, but copying only size elements. */
void copy ( sruint sizeofx, const SrArrayBase& a );
/*! Returns the internal buffer pointer that will be null or contain the address of
the memory used and that was allocated with malloc(). The user will then be
responsible to free this allocated memory with free(). After this call, the
array becomes an empty valid array. */
void* leave_data ();
/*! Takes the data of the given array a, that will become an empty array.
SrArrayBase will have the same data that a had before. This is done
without reallocation. */
void take_data ( SrArrayBase& a );
/*! Frees the current data of SrArrayBase, and then makes SrArrayBase to control
the given buffer pt, with size and capacity as given. */
void take_data ( void* pt, int s, int c );
};
/*! \class SrArray sr_array.h
\brief Fast resizeable dynamic array
All memory management functions of SrArray use quick memory block functions
and so be aware that constructors and destructors of class X are not called.
SrArray can be used only with classes or structs that do not have any internal
allocated data, as SrArray will not respect them when resizing. Internally,
malloc(), realloc() and free() functions are used throught SrArrayBase methods.
Note that the array size is automatically reallocated when needed (with a double
size strategey), and so take care to not reference internal memory of SrArray
that can be reallocated. For example, the following code is wrong: a.push()=a[x],
because a[x] referentiates a memory space that can be reallocated by push() */
template <class X>
class SrArray : protected SrArrayBase
{ public:
/*! Constructs with the given size and capacity. If the given capacity
is smaller than the size, capacity is set to be equal to size. */
SrArray ( int s=0, int c=0 ) : SrArrayBase ( sizeof(X), s, c ) {}
/*! For compatibility with prior versions we provide a constructor from
3 ints, the last one is simply not considered. */
SrArray ( int s, int c, int g ) : SrArrayBase ( sizeof(X), s, c ) {}
/*! Copy constructor. SrArray will be an exact copy of the given array,
but allocating as capacity only the size of a.
Attention: the operator= that X might have is not called ! */
SrArray ( const SrArray& a ) : SrArrayBase ( sizeof(X), a ) {}
/*! Constructor from a given buffer. No checkings are done, its the user
responsability to give consistent parameters. */
SrArray ( X* pt, int s, int c ) : SrArrayBase ( (void*)pt, s, c ) {}
/*! Destructor frees the array calling the base class free_data() method.
Attention: elements' destructors are not called ! */
~SrArray () { SrArrayBase::free_data(); }
/*! Returns true if the array has no elements, ie, size()==0; and false otherwise. */
bool empty () const { return _size==0? true:false; }
/*! Returns the capacity of the array. Capacity is used to be able to have a
larger storage buffer than the current size used. The method capacity()
will always return a value not smaller than size(). */
int capacity () const { return _capacity; }
/*! Returns the current size of the array. */
int size () const { return _size; }
/*! Changes the size of the array. Reallocation is done only when the size
requested is greater than the current capacity, and in this case, capacity
becomes equal to the size. */
void size ( int ns ) { SrArrayBase::size(sizeof(X),ns); }
/*! Changes the capacity of the array. Reallocation is done whenever a new
capacity is requested. Internal memory is freed in case of 0 capacity.
The size is always kept inside [0,nc]. Parameter nc is considered 0
if it is negative. */
void capacity ( int nc ) { SrArrayBase::capacity(sizeof(X),nc); }
/*! Defines a minimum capacity to use, ie, sets the capacity to be c
iff the current capacity is lower than c */
void ensure_capacity ( int c ) { if ( capacity()<c ) capacity(c); }
/*! Sets all elements as x, copying each element using operator = */
void setall ( const X& x )
{ int i; for ( i=0; i<_size; i++ ) ((X*)_data)[i]=x; }
/*! Makes capacity to be equal to size, freeing all extra capacity if any. */
void compress () { SrArrayBase::compress ( sizeof(X) ); }
/*! Returns a valid index as if the given index references a circular
array, ie, it returns index%size() for positive numbers. Negative
numbers are also correctly mapped. */
int validate ( int index ) const { return SrArrayBase::validate(index); }
/*! Gets a const reference to the element of index i. Indices start from 0 and must
be smaller than size(). No checkings are done to ensure that i is valid. */
const X& const_get ( int i ) const { return ((X*)_data)[i]; }
/*! Gets a reference to the element of index i. Indices start from 0 and must
be smaller than size(). No checkings are done to ensure that i is valid. */
X& get ( int i ) const { return ((X*)_data)[i]; }
/*! Sets an element. Operator = is used here. Indices start from 0 and must
be smaller than size(). No checkings are done to ensure that i is valid. */
void set ( int i, const X& x ) { ((X*)_data)[i]=x; }
/*! Operator version of X& get(int i), but returning a non const reference.
No checkings are done to ensure that i is valid. */
X& operator[] ( int i ) { return ((X*)_data)[i]; }
/*! Returns a const pointer of the internal buffer. The internal buffer
will always contain a contigous storage space of capacity() elements.
See also take_data() and leave_data() methods. */
operator const X* () const { return (X*)_data; }
/*! Returns a reference to the last element, ie, with index size()-1.
The array must not be empty when calling this method. */
X& top () { return ((X*)_data)[_size-1]; }
/*! Returns a reference to the last element, ie, with index size()-1, and
then reduces the size of the array by one with no reallocation.
The array must not be empty when calling this method. */
X& pop () { return ((X*)_data)[--_size]; }
/*! Method to append positions. If reallocation is needed, capacity is set
to two times the new size. The first new element appended is returned
as a reference. */
X& push () { SrArrayBase::insert(sizeof(X),_size,1); return top(); }
/*! Pushes one position at the end of the array using the insert() method, and
then copies the content of x using operator=(). */
void push ( const X& x ) { SrArrayBase::insert(sizeof(X),_size,1); top()=x; }
/*! Inserts dp positions, starting at pos i, moving all data correctly.
Parameter i can be between 0 and size(), if i==size(), dp positions are
appended. If reallocation is required, capacity is set to two times the
new size. The first new element inserted (i) is returned as a reference.
The quantity of appended positions (dp) has a default value of 1. */
X& insert ( int i, int dp=1 ) { SrArrayBase::insert(sizeof(X),i,dp); return ((X*)_data)[i]; }
/*! Removes dp positions starting from pos i, moving all data correctly;
dp has a default value of 1. Attention: elements' destructors are not called! */
void remove ( int i, int dp=1 ) { SrArrayBase::remove(sizeof(X),i,dp); }
/*! Copies n entries from src position to dest position. Regions are
allowed to overlap. Uses the C function memmove. */
void move ( int dest, int src, int n ) { SrArrayBase::move(sizeof(X),dest,src,n); }
/*! Copies all internal data of a to SrArray, with fast memcpy() functions,
so that the operator=() that X might have is not used. This method has
no effect if a "self copy" is called. */
void operator = ( const SrArray<X>& a )
{ SrArrayBase::copy ( sizeof(X), a ); }
/*! Revert the order of the elements in the array. Copy operator of X is used. */
void revert ()
{ int i, max=size()-1, mid=size()/2; X tmp;
for ( i=0; i<mid; i++ ) { SR_SWAP(get(i),get(max-i)); }
}
/*! Inserts the element x in the sorted array, moving all data correctly, and
returning the position of the element inserted. When allowdup is false,
the element will not be inserted in case of duplication, and in this case,
-1 is returned. A compare function int sr_compare(const X*,const X*) is
required as argument. The method insert() is called to open the required
space, but the operator=() of X will be used to copy element contents in
the open position. Parameter allowdup has a default value of true. */
int insort ( const X& x, SR_COMPARE_FUNC, bool allowdup=true )
{ int pos = SrArrayBase::insort ( sizeof(X), (void*)&x, (srcompare)sr_compare_func, allowdup );
if ( pos>=0 ) ((X*)_data)[pos]=x;
return pos;
}
/*! Standard library qsort() wrapper call. The compare function is required
as argument: int sr_compare(const X*,const X*) */
void sort ( SR_COMPARE_FUNC ) { SrArrayBase::sort ( sizeof(X), (srcompare)sr_compare_func ); }
/*! Linear search, returns the index of the element found, or -1 if not
found. A compare function is required as argument:
int sr_compare(const X*,const X*) */
int lsearch ( const X& x, SR_COMPARE_FUNC ) const { return SrArrayBase::lsearch ( sizeof(X), (void*)&x, (srcompare)sr_compare_func ); }
/*! Binary search for sorted arrays. Returns index of the element found,
or -1 if not found. If not found and pos is not null, pos will have the
position to insert the element keeping the array sorted. Faster than
the standard C library bsearch() for large arrays. A compare function
is required as argument: int sr_compare(const X*,const X*) */
int bsearch ( const X& x, SR_COMPARE_FUNC, int *pos=NULL ) const
{ return SrArrayBase::bsearch ( sizeof(X), (void*)&x, (srcompare)sr_compare_func, pos ); }
/*! Returns the internal buffer pointer that will be null or contain the address of
the memory used and that was allocated with malloc(). The user will then be
responsible to free this allocated memory with free(). After this call, the
array becomes an empty valid array. */
X* leave_data () { return (X*) SrArrayBase::leave_data(); }
/*! Frees the data of SrArray, and then makes SrArray be the given array a.
After this, a is set to be a valid empty array. The data is moved without
reallocation. */
void take_data ( SrArray<X>& a ) { SrArrayBase::take_data ( (SrArrayBase&)a ); }
/*! Frees the data of SrArray, and then makes SrArray to control the given
buffer pt, with size and capacity as given. Its the user reponsibility to
pass correct values. Note also that the memory menagement of SrArray is
done with malloc/realloc/free functions. */
void take_data ( X* pt, int s, int c ) { SrArrayBase::take_data ( pt, s, c ); }
/*! Output all elements of the array. Element type X must have its ouput operator <<
available. The output format is [e0 e1 ... en]. */
friend SrOutput& operator<< ( SrOutput& o, const SrArray<X>& a )
{ int i;
o << '[';
for ( i=0; i<a.size(); i++ )
{ o << a[i];
if ( i<a.size()-1 ) o<<srspc;
}
return o << ']';
}
/*! Input all elements of the array. Element type X must have its input operator <<
available. */
friend SrInput& operator>> ( SrInput& in, SrArray<X>& a )
{ a.size(0);
in.get_token();
while (true)
{ in.get_token();
if ( in.last_token()[0]==']' ) break;
in.unget_token();
a.push();
in >> a.top();
}
return in;
}
};
//============================== end of file ===============================
#endif // SR_ARRAY_H

View File

@ -0,0 +1,202 @@
#include "precompiled.h"
# include "sr_array_pt.h"
//====================== SrArrayPtBase ==========================
SrArrayPtBase::SrArrayPtBase ( SrClassManagerBase* m )
{
_man = m;
_man->ref();
}
SrArrayPtBase::SrArrayPtBase ( const SrArrayPtBase& a )
{
_man = a._man;
_man->ref();
*this = a; // calls copy operator
}
SrArrayPtBase::~SrArrayPtBase ()
{
init ();
_man->unref(); // must be called after init()!
}
void SrArrayPtBase::init ()
{
while ( SrArray<void*>::size()>0 ) _man->free ( SrArray<void*>::pop() );
}
void SrArrayPtBase::size ( int ns )
{
int i, s = SrArray<void*>::size();
if ( ns>s )
{ SrArray<void*>::size(ns);
for ( i=s; i<ns; i++ ) SrArray<void*>::set ( i, _man->alloc() );
}
else if ( ns<s )
{ for ( i=ns; i<s; i++ ) _man->free ( SrArray<void*>::get(i) );
SrArray<void*>::size(ns);
}
}
void SrArrayPtBase::capacity ( int nc )
{
int i, s = SrArray<void*>::size();
if ( nc<0 ) nc=0;
if ( nc<s )
{ for ( i=nc; i<s; i++ ) _man->free ( SrArray<void*>::get(i) );
}
SrArray<void*>::capacity(nc);
}
void SrArrayPtBase::compress ()
{
capacity ( size() );
}
void SrArrayPtBase::swap ( int i, int j )
{
void *pti = SrArray<void*>::get(i);
void *ptj = SrArray<void*>::get(j);
SrArray<void*>::set ( i, ptj );
SrArray<void*>::set ( j, pti );
}
void SrArrayPtBase::set ( int i, const void* pt )
{
SR_ASSERT ( i>=0 && i<size() );
_man->free ( SrArray<void*>::get(i) );
SrArray<void*>::set ( i, _man->alloc(pt) );
}
void* SrArrayPtBase::get ( int i ) const
{
SR_ASSERT ( i>=0 && i<size() );
return SrArray<void*>::get(i);
}
const void* SrArrayPtBase::const_get ( int i ) const
{
SR_ASSERT ( i>=0 && i<size() );
return SrArray<void*>::const_get(i);
}
void* SrArrayPtBase::top () const
{
if ( size()==0 ) return 0;
return SrArray<void*>::get ( size()-1 );
}
void SrArrayPtBase::pop ()
{
if ( size()>0 ) _man->free ( SrArray<void*>::pop() );
}
void SrArrayPtBase::push ()
{
SrArray<void*>::push() = _man->alloc ();
}
void SrArrayPtBase::insert ( int i, int dp )
{
SrArray<void*>::insert ( i, dp );
int j;
for ( j=0; j<dp; j++ )
SrArray<void*>::set ( i+j, _man->alloc() );
}
void SrArrayPtBase::remove ( int i, int dp )
{
int j;
for ( j=0; j<dp; j++ )
_man->free( SrArray<void*>::get(i+j) );
SrArray<void*>::remove ( i, dp );
}
void* SrArrayPtBase::extract ( int i )
{
void *pt = SrArray<void*>::get(i);
SrArray<void*>::remove ( i );
return pt;
}
void SrArrayPtBase::operator = ( const SrArrayPtBase& a )
{
init (); // deletes all data
SrArray<void*>::size ( a.size() );
SrArray<void*>::compress();
int i;
for ( i=0; i<a.size(); i++ ) SrArray<void*>::set ( i, _man->alloc(a[i]) );
}
static SrClassManagerBase* StaticManager = 0; // This is not thread safe...
static int fcmp ( const void* pt1, const void* pt2 )
{
typedef const int* cint;
return StaticManager->compare( (const void*)*cint(pt1), (const void*)*cint(pt2) );
}
int SrArrayPtBase::insort ( const void* pt, bool allowdup )
{
int pos;
pos = SrArray<void*>::insort ( (void *const&)pt,
(int(*)(void *const *,void *const *))fcmp,
allowdup );
if ( pos>=0 ) SrArray<void*>::get(pos) = _man->alloc(pt);
return pos;
}
void SrArrayPtBase::sort ()
{
StaticManager = _man;
SrArray<void*>::sort ( (int(*)(void *const *,void *const *))fcmp );
}
int SrArrayPtBase::lsearch ( const void* pt ) const
{
StaticManager = _man;
return SrArray<void*>::lsearch ( (void *const&)pt, (int(*)(void *const *,void *const *))fcmp );
}
int SrArrayPtBase::bsearch ( const void* pt, int *pos )
{
StaticManager = _man;
return SrArray<void*>::bsearch ( (void *const&)pt, (int(*)(void *const *,void *const *))fcmp, pos );
}
void SrArrayPtBase::take_data ( SrArrayPtBase& a )
{
size(0);
capacity(0);
SrArray<void*>::take_data(a);
}
SrOutput& operator<< ( SrOutput& o, const SrArrayPtBase& a )
{
int i, m;
m = a.size()-1;
o << '[';
for ( i=0; i<=m; i++ )
{ a._man->output ( o, a[i] );
if ( i<m ) o<<srspc;
}
return o << ']';
}
SrInput& operator>> ( SrInput& in, SrArrayPtBase& a )
{
a.size(0);
in.get_token();
while (true)
{ in.get_token();
if ( in.last_token()[0]==']' ) break;
in.unget_token ();
a.push ();
a._man->input ( in, a.top() );
}
return in;
}
//=========================== EOF ===============================

View File

@ -0,0 +1,169 @@
# ifndef SR_ARRAY_PT_H
# define SR_ARRAY_PT_H
/** \file sr_array_pt.h
* resizeable array of class pointers */
# include "sr_class_manager.h"
# include "sr_array.h"
/*! \class SrArrayPtBase sr_array_pt.h
\brief resizeable array of class pointers
SrArrayPtBase implements methods for managing a resizeable array
of pointers. The user should however use the template
class SrArrayPt for an implementation that includes automatic
type casts for the user types. A manager to the user
data is required, see sr_class_manager.h */
class SrArrayPtBase : private SrArray<void*>
{ private :
SrClassManagerBase* _man;
public :
/*! Initiates an empty array. The class manager is required. */
SrArrayPtBase ( SrClassManagerBase* m );
/*! Copy constructor. The class manager of a is shared. */
SrArrayPtBase ( const SrArrayPtBase& a );
/*! Destructor */
~SrArrayPtBase ();
/*! Returns true if the array has no elements, and false otherwise. */
bool empty () const { return SrArray<void*>::empty(); }
/*! Returns the capacity of the array. */
int capacity () const { return SrArray<void*>::capacity(); }
/*! Returns the current size of the array. */
int size () const { return SrArray<void*>::size(); }
/*! Makes the array empty; equivalent to size(0) */
void init ();
/*! Changes the size of the array, filling new objects in the new positions. */
void size ( int ns );
/*! Changes the capacity of the array. */
void capacity ( int nc );
/*! Makes capacity to be equal to size. */
void compress ();
/*! Swaps the pointers of position i and j, that must be valid positions. */
void swap ( int i, int j );
/*! Returns a valid index as if the given index references a circular
array, ie, it returns index%size() for positive numbers. Negative
numbers are also correctly mapped. */
int validate ( int index ) const { SrArray<void*>::validate(index); }
/*! deletes element i and reallocates a new one as a copy of pt */
void set ( int i, const void* pt );
/*! returns a pointer to the object in position i. */
void* get ( int i ) const;
/*! Returns a const pointer to the object in position i. */
const void* const_get ( int i ) const;
/*! Returns a pointer to the last element or 0 if the array is empty*/
void* top () const;
/*! Pop and frees element size-1 if the array is not empty */
void pop ();
/*! Allocates and appends one empty element */
void push ();
/*! Inserts n positions, starting at pos i, and putting a new element in
each new position created. */
void insert ( int i, int n=1 );
/*! Removes n positions starting from pos i */
void remove ( int i, int n=1 );
/*! Extract (without deletion) and returns the pointer at position i */
void* extract ( int i );
/*! Copy operator */
void operator = ( const SrArrayPtBase& a );
/*! Inserts one object, considering the array is sorted. Returns the
inserted position, or -1 if duplication occurs and allowdup is false.
(Note: all methods using sr_compare functions are not thread safe, as
they use a static pointer to set the current comparison function) */
int insort ( const void* pt, bool allowdup=true );
/*! Sort array */
void sort ();
/*! Linear search. Returns index of the element found,
or -1 if not found. */
int lsearch ( const void* pt ) const;
/*! Binary search for sorted arrays. Returns index of the element found,
or -1 if not found. If not found and pos is not 0, pos will have the
position to insert the element keeping the array sorted. */
int bsearch ( const void* pt, int *pos=NULL );
/*! Frees all data, and then makes SrArrayPtBase be the given array a.
After this, a is set to be a valid empty array. The data is moved without
reallocation. */
void take_data ( SrArrayPtBase& a );
/*! Outputs all elements of the array in format is [e0 e1 ... en]. */
friend SrOutput& operator<< ( SrOutput& o, const SrArrayPtBase& a );
/*! Inputs elements in format is [e0 e1 ... en]. */
friend SrInput& operator>> ( SrInput& in, SrArrayPtBase& a );
};
/*! \class SrArrayPt sr_array_pt.h
\brief resizeable array of class pointers
SrArrayPtBase implements methods for managing a resizeable array
of pointers, which are managed by SrClassManager object. */
template <class X>
class SrArrayPt : public SrArrayPtBase
{ public :
/*! Default constructor that automatically creates a SrClassManager<X>. */
SrArrayPt () : SrArrayPtBase ( new SrClassManager<X> ) {}
/*! Constructor with a given class manager. */
SrArrayPt ( SrClassManagerBase* m ) : SrArrayPtBase ( m ) {}
/*! Copy constructor sharing class manager. */
SrArrayPt ( const SrArrayPt& a ) : SrArrayPtBase ( a ) {}
void set ( int i, const X& x ) { SrArrayPtBase::set(i,(const void*)&x); }
X* get ( int i ) { return (X*)SrArrayPtBase::get(i); }
const X* const_get ( int i ) const { return (const X*)SrArrayPtBase::const_get(i); }
X* operator[] ( int i ) { return get(i); }
/*! Returns a pointer to the last element or 0 if the array is empty*/
X* top () const { return (X*)SrArrayPtBase::top(); }
/*! Pop and frees element size-1 if the array is not empty */
void pop () { SrArrayPtBase::pop(); }
/*! Allocates and appends one empty element */
void push () { SrArrayPtBase::push(); }
/*! Allocates and appends one element using copy operator */
void push ( const X& x ) { push(); *top()=x; }
/*! Allocates and insert one element using copy operator */
void insert ( int i, const X& x ) { insert(i,1); *get(i)=x; }
/*! Extract (without deletion) and returns the pointer at position i */
X* extract ( int i ) { return (X*) SrArrayPtBase::extract(i); }
};
//============================== end of file ===============================
#endif // SR_ARRAY_PT_H

193
source/dcdt/se/sr_box.cpp Normal file
View File

@ -0,0 +1,193 @@
#include "precompiled.h"
# include "sr_box.h"
# include "sr_mat.h"
//======================================== SrBox =======================================
const char* SrBox::class_name = "Box";
SrBox::SrBox ( const SrBox& x, const SrBox& y )
: a ( SR_MIN(x.a.x,y.a.x), SR_MIN(x.a.y,y.a.y), SR_MIN(x.a.z,y.a.z) ),
b ( SR_MAX(x.b.x,y.b.x), SR_MAX(x.b.y,y.b.y), SR_MAX(x.b.z,y.b.z) )
{
}
bool SrBox::empty () const
{
return a.x>b.x || a.y>b.y || a.z>b.z ? true:false;
}
float SrBox::volume () const
{
return (b.x-a.x) * (b.y-a.y) * (b.z-a.z);
}
SrVec SrBox::center () const
{
return (a+b)/2.0f; // == a + (b-a)/2 == a + b/2 - a/2
}
void SrBox::center ( const SrVec& p )
{
(*this) += p-center();
}
void SrBox::size ( const SrVec& v )
{
b = a+v;
}
SrVec SrBox::size () const
{
return b-a;
}
float SrBox::max_size () const
{
SrVec s = b-a;
return SR_MAX3(s.x,s.y,s.z);
}
float SrBox::min_size () const
{
SrVec s = b-a;
return SR_MIN3(s.x,s.y,s.z);
}
void SrBox::extend ( const SrPnt &p )
{
if ( empty() ) { a=p; b=p; }
SR_UPDMIN ( a.x, p.x ); SR_UPDMAX ( b.x, p.x );
SR_UPDMIN ( a.y, p.y ); SR_UPDMAX ( b.y, p.y );
SR_UPDMIN ( a.z, p.z ); SR_UPDMAX ( b.z, p.z );
}
void SrBox::extend ( const SrBox &box )
{
if ( empty() ) *this=box;
if ( box.empty() ) return;
SR_UPDMIN ( a.x, box.a.x ); SR_UPDMAX ( b.x, box.b.x );
SR_UPDMIN ( a.y, box.a.y ); SR_UPDMAX ( b.y, box.b.y );
SR_UPDMIN ( a.z, box.a.z ); SR_UPDMAX ( b.z, box.b.z );
}
void SrBox::grows ( float dx, float dy, float dz )
{
a.x-=dx; a.y-=dy; a.z-=dz;
b.x+=dx; b.y+=dy; b.z+=dz;
}
bool SrBox::contains ( const SrVec& p ) const
{
return p.x<a.x || p.y<a.y || p.z<a.z || p.x>b.x || p.y>b.y || p.z>b.z ? false : true;
}
bool SrBox::intersects ( const SrBox& box ) const
{
if ( box.contains(a) ) return true;
if ( box.contains(b) ) return true;
SrVec x(a.x,a.y,b.z); if ( box.contains(x) ) return true;
x.set (a.x,b.y,a.z); if ( box.contains(x) ) return true;
x.set (b.x,a.y,a.z); if ( box.contains(x) ) return true;
x.set (b.x,b.y,a.z); if ( box.contains(x) ) return true;
x.set (b.x,a.y,b.z); if ( box.contains(x) ) return true;
x.set (a.x,b.y,b.z); if ( box.contains(x) ) return true;
return false;
}
void SrBox::get_side ( SrPnt& p1, SrPnt& p2, SrPnt& p3, SrPnt& p4, int s ) const
{
switch (s)
{ case 0 : p1.set ( a.x, a.y, a.z );
p2.set ( a.x, a.y, b.z );
p3.set ( a.x, b.y, b.z );
p4.set ( a.x, b.y, a.z );
break;
case 1 : p1.set ( b.x, a.y, a.z );
p2.set ( b.x, b.y, a.z );
p3.set ( b.x, b.y, b.z );
p4.set ( b.x, a.y, b.z );
break;
case 2 : p1.set ( a.x, a.y, a.z );
p2.set ( b.x, a.y, a.z );
p3.set ( b.x, a.y, b.z );
p4.set ( a.x, a.y, b.z );
break;
case 3 : p1.set ( a.x, b.y, a.z );
p2.set ( a.x, b.y, b.z );
p3.set ( b.x, b.y, b.z );
p4.set ( b.x, b.y, a.z );
break;
case 4 : p1.set ( a.x, a.y, a.z );
p2.set ( a.x, b.y, a.z );
p3.set ( b.x, b.y, a.z );
p4.set ( b.x, a.y, a.z );
break;
case 5 : p1.set ( a.x, a.y, b.z );
p2.set ( b.x, a.y, b.z );
p3.set ( b.x, b.y, b.z );
p4.set ( a.x, b.y, b.z );
break;
}
}
void SrBox::operator += ( const SrVec& v )
{
a += v;
b += v;
}
void SrBox::operator *= ( float s )
{
a *= s;
b *= s;
}
//============================== friends ========================================
SrBox operator * ( const SrBox& b, const SrMat& m )
{
SrBox x; // init as an empty box
if ( b.empty() ) return x;
SrVec v(b.a); x.extend(v*m);
v.x=b.b.x; x.extend(v*m);
v.y=b.b.y; x.extend(v*m);
v.x=b.a.x; x.extend(v*m);
v.z=b.b.z; x.extend(v*m);
v.x=b.b.x; x.extend(v*m);
v.y=b.a.y; x.extend(v*m);
v.x=b.a.x; x.extend(v*m);
return x;
}
SrBox operator * ( const SrMat& m, const SrBox& b )
{
SrBox x; // init as an empty box
if ( b.empty() ) return x;
SrVec v(b.a); x.extend(m*v);
v.x=b.b.x; x.extend(m*v);
v.y=b.b.y; x.extend(m*v);
v.x=b.a.x; x.extend(m*v);
v.z=b.b.z; x.extend(m*v);
v.x=b.b.x; x.extend(m*v);
v.y=b.a.y; x.extend(m*v);
v.x=b.a.x; x.extend(m*v);
return x;
}
SrOutput& operator<< ( SrOutput& o, const SrBox& box )
{
return o << box.a << ' ' << box.b;
}
SrInput& operator>> ( SrInput& in, SrBox& box )
{
return in >> box.a >> box.b;
}
//================================ End of File =================================================

132
source/dcdt/se/sr_box.h Normal file
View File

@ -0,0 +1,132 @@
# ifndef SR_BOX_H
# define SR_BOX_H
/** \file sr_box.h
* 3d axis-aligned box
*/
# include "sr_vec.h"
class SrMat;
/*! \class SrBox sr_box.h
\brief 3d axis-aligned box
SrBox describes a 3d axis-aligned box. The box is described by
two 3d vertices a,b; one with the minimum coordinates(a), and the
other with the maximum coordinates(b). It is used to describe
bounding boxes, which have their sides parallel to the axes.
If any of the coordinates of a are greater than any coordinates
of b, the box is said to be empty, ie, not valid. */
class SrBox
{ public :
SrPnt a; //!< Contains the minimum coordinates of the box
SrPnt b; //!< Contains the maximum coordinates of the box
static const char* class_name;
public :
/*! Default constructor initializes the box as the empty box (1,1,1)(0,0,0). */
SrBox () : a(SrPnt::one), b(SrPnt::null) {}
/*! Constructs a box with all vertices the same. This degenerated
box is identical to a single point and is not considered an
empty box. */
SrBox ( const SrPnt& p ) : a(p), b(p) {}
/*! Constructs the box from the given min and max points. */
SrBox ( const SrPnt& min, const SrPnt& max ) : a(min), b(max) {}
/*! Copy constructor. */
SrBox ( const SrBox& box ) : a(box.a), b(box.b) {}
/* Constructs SrBox containing the two given boxes. */
SrBox ( const SrBox& x, const SrBox& y );
/*! Init the box as (0,0,0)(0,0,0). */
void set_null () { a=SrPnt::null; b=SrPnt::null; }
/*! Sets the minimum and maximum vertices of the box. */
void set ( const SrPnt& min, const SrPnt& max ) { a=min; b=max; }
/*! Sets the box to be empty, ie, invalid, just by putting
the x coordinate of the minimum vertex (a) greater than
the x coordinate of the maximum vertex (b). */
void set_empty () { a.x=1.0; b.x=0.0; }
/*! Returns true if the box is empty (or invalid), ie, when
some coordinate of a is greater than b. */
bool empty () const;
/*! Returns the volume of the box. */
float volume () const;
/*! Returns the center point of the box (b+a)/2. */
SrPnt center () const;
/*! Translates SrBox to have its center in p. */
void center ( const SrPnt& p );
/*! Changes the position of the maximum vertex b of the box in order to
achieve the desired dimensions given in v (b=a+v). */
void size ( const SrVec& v );
/*! Returns the dimensions in each axis (b-a). */
SrVec size () const;
/*! Returns the maximum dimension of the box. */
float max_size () const;
/*! Returns the minimum dimension of the box. */
float min_size () const;
/*! Extends SrBox (if needed) to contain the given point. If SrBox
is empty, SrBox min and max vertices become the given point. */
void extend ( const SrPnt& p );
/*! Extends SrBox (if needed) to contain the given box, if the given
box is not empty(). If SrBox is empty, SrBox becomes the given box. */
void extend ( const SrBox& box );
/*! Adds (dx,dy,dz) to b, and diminish it from a. */
void grows ( float dx, float dy, float dz );
/*! Returns true if SrBox contains the given point. */
bool contains ( const SrPnt& p ) const;
/*! Returns true if SrBox intersects with the given box. */
bool intersects ( const SrBox& box ) const;
/*! Returns the four corners of side s={0,..,5} of the box.
Side 0 has all x coordinates equal to a.x, side 1 equal to b.x.
Side 2 has all y coordinates equal to a.y, side 3 equal to b.y.
Side 4 has all z coordinates equal to a.z, side 5 equal to b.z.
Order is ccw, starting with the point with more SrBox::a coordinates */
void get_side ( SrPnt& p1, SrPnt& p2, SrPnt& p3, SrPnt& p4, int s ) const;
/*! The bounding box is identical to SrBox (needed by SrSceneShapeTpl). */
void get_bounding_box ( SrBox &box ) const { box=*this; }
/* Translates SrBox by v. */
void operator += ( const SrVec& v );
/* Scales SrBox by the factor s. */
void operator *= ( float s );
/* Returns the bounding box of the transformed vertices vM of b. */
friend SrBox operator * ( const SrBox& b, const SrMat& m );
/* Returns the bounding box of the transformed vertices Mv of b. */
friend SrBox operator * ( const SrMat& m, const SrBox& b );
/*! Outputs in format: "x y z a b c". */
friend SrOutput& operator<< ( SrOutput& o, const SrBox& box );
/*! Inputs from format: "x y z a b c". */
friend SrInput& operator>> ( SrInput& in, SrBox& box );
};
//================================ End of File =================================================
# endif // SR_BOX_H

View File

@ -0,0 +1,63 @@
#include "precompiled.h"
# include <stdlib.h>
# include <string.h>
# include "sr_buffer.h"
//=== SrBuffer =======================================================================
# define BUF(i) ((char*)buffer)+(sizeofx*(i))
void* sr_buffer_size ( void* buffer, int sizeofx, int& size, int newsize )
{
if ( size==newsize ) return buffer;
size = newsize;
if ( size==0 )
{ if (buffer) { free(buffer); buffer=0; } }
else
{ buffer = realloc ( buffer, (size_t)(sizeofx*size) ); }
return buffer;
}
void* sr_buffer_insert ( void* buffer, int sizeofx, int& size, int i, int dp )
{
size += dp;
buffer = realloc ( buffer, (size_t)(sizeofx*size) );
if ( i<size-dp )
memmove ( BUF(i+dp), BUF(i), sizeofx*(size-dp-i) ); // ok with overlap
return buffer;
}
void* sr_buffer_remove ( void* buffer, int sizeofx, int& size, int i, int dp )
{
if ( i<size-dp ) memmove ( BUF(i), BUF(i+dp), sizeofx*(size-(i+dp)) );
return sr_buffer_size ( buffer, sizeofx, size, size-dp );
}
void* sr_buffer_copy ( void* buffer, int sizeofx, int& size, const void* buffertocp, int sizetocp )
{
if ( buffer==buffertocp ) return buffer;
buffer = sr_buffer_size ( buffer, sizeofx, size, sizetocp );
if ( buffer ) memcpy ( buffer, buffertocp, sizeofx*size ); // no overlap
return buffer;
}
# undef BUF
//=== End of File =====================================================================
/* Note:
void *memmove( void *dest, const void *src, size_t count );
If some regions of the source area and the destination overlap,
memmove ensures that the original source bytes in the overlapping
region are copied before being overwritten
void *memcpy( void *dest, const void *src, size_t count );
If the source and destination overlap, memcpy function does not
ensure that the original source bytes in the overlapping region
are copied before being overwritten. Use memmove to handle
overlapping regions. */

170
source/dcdt/se/sr_buffer.h Normal file
View File

@ -0,0 +1,170 @@
# ifndef SR_BUFFER_H
# define SR_BUFFER_H
/** \file sr_buffer.h
* fast buffer memory management template */
# include "sr.h"
# include "sr_input.h"
# include "sr_output.h"
/*! Allocates memory with sizeofx*newsize bytes using the C function realloc.
If newsize is zero, buffer is freed and both buffer and size becomes zero.
After the function call, size has the value of newsize. No effect if
size==newsize. */
void* sr_buffer_size ( void* buffer, int sizeofx, int& size, int newsize );
/*! Inserts dp*sizeofx bytes at i position, moving correctly the buffer contents. */
void* sr_buffer_insert ( void* buffer, int sizeofx, int& size, int i, int dp );
/*! Inserts dp*sizeofx bytes at i position, moving correctly the buffer contents. */
void* sr_buffer_remove ( void* buffer, int sizeofx, int& size, int i, int dp );
/*! Put buffers with same size and copy them. No effect if buffers pointers are equal. */
void* sr_buffer_copy ( void* buffer, int sizeofx, int& size, const void* buffertocp, int sizetocp );
/*! \class SrBuffer sr_buffer.h
\brief fast buffer memory management template
All memory management functions of SrBuffer are written using the four buffer
functions available in this header. Be aware that constructors and destructors
of class X are not called. SrBuffer can thus be used only with classes or structs
that do not have any internal allocated data.
Internally, SrBuffer keeps only a pointer to the allocated
buffer memory and the buffer size. For a more featured class see SrArray. */
template <class X>
class SrBuffer
{ private:
void* _data;
int _size;
public:
/*! Default constructor. */
SrBuffer () : _data(0), _size(0) {}
/*! Copy constructor. */
SrBuffer ( const SrBuffer& b ) : _data(0), _size(0)
{ _data=sr_buffer_copy(_data,sizeof(X),_size,b._data,b._size); }
/*! Constructor with a given size. */
SrBuffer ( int s ) : _data(0), _size(0) { _data=sr_buffer_size(_data,sizeof(X),_size,s); }
/*! Constructor from a user allocated buffer. See also leave_data(). */
SrBuffer ( X* pt, int s ) : _data(pt), _size(s) {}
/*! Destructor frees the buffer. Elements' destructors are not called ! */
~SrBuffer () { sr_buffer_size ( _data, sizeof(X), _size, 0 ); }
/*! Returns true if size()==0; and false otherwise. */
bool empty () const { return _size==0? true:false; }
/*! Returns the current size of the Buffer. */
int size () const { return _size; }
/*! Allows to change the size of the buffer. */
void size ( int ns )
{ _data=sr_buffer_size(_data,sizeof(X),_size,ns); }
/*! Sets all elements as x, copying each element using operator = */
void setall ( const X& x )
{ for ( int i=0; i<_size; i++ ) ((X*)_data)[i]=x; }
/*! Get a const reference to the element of index i. Indices start from 0
and must be smaller than size(). No checkings are done to ensure that
i is in a valid range. */
const X& const_get ( int i ) const { return ((X*)_data)[i]; }
/*! Get a reference to the element of index i. Indices start from 0
and must be smaller than size(). No checkings are done to ensure that
i is in a valid range. */
X& get ( int i ) { return ((X*)_data)[i]; }
/*! Sets an element. Operator = is used here. Indices start from 0 and must
be smaller than size(). No checkings are done to ensure that i is valid. */
void set ( int i, const X& x ) { ((X*)_data)[i]=x; }
/*! Operator version of X& get(int i), but returning a non const reference.
No checkings are done to ensure that i is valid. */
X& operator[] ( int i ) { return ((X*)_data)[i]; }
/*! Returns a const pointer of the internal buffer. The internal buffer
will always contain a contigous storage space of size() elements.
See also take_data() and leave_data() methods. */
operator const X* () const { return (X*)_data; }
/*! Returns a reference to the last element, ie, with index size()-1.
The Buffer must not be empty when calling this method. */
X& top () { return ((X*)_data)[_size-1]; }
/* Reduces the size of the buffer by one. */
void pop () { _data=sr_buffer_size(_data,sizeof(X),_size,_size-1); }
/* Increases the size of the buffer by one. */
void push ( int dp=1 ) { _data=sr_buffer_size(_data,sizeof(X),_size,_size+1); }
/*! Inserts dp positions, starting at pos i, moving all data correctly.
Parameter i can be between 0 and size(), if i==size(), dp positions are
appended. */
void insert ( int i, int dp=1 ) { _data=sr_buffer_insert(_data,sizeof(X),_size,i,dp); }
/*! Removes dp positions starting from pos i, moving all data correctly;
dp has a default value of 1. Attention: elements' destructors are not
called ! */
void remove ( int i, int dp=1 ) { _data=sr_buffer_remove(_data,sizeof(X),_size,i,dp); }
/*! Copies all internal data of a to SrBuffer, with fast memcpy() functions,
so that the operator=() that X might have is not used. This method has no
effect if a "self copy" is called. */
void operator = ( const SrBuffer<X>& b )
{ _data=sr_buffer_copy(_data,sizeof(X),_size,b._data,b._size); }
/*! Makes the given xpt pointer to point to the internal buffer, without
reallocation; xpt will then be null or contain the address of the memory
used and that was allocated with malloc(). The user will then be responsible
to free this allocated memory with free(). After this call, SrBuffer will
become an empty but valid buffer. */
void leave_data ( X*& xpt, int& size )
{ xpt=_data; size=_size; _data=0; _size=0; }
/*! Frees the data of SrBuffer, and then makes SrBuffer be the given buffer b.
After this, b is set to be a valid empty buffer. The data is moved without
reallocation. */
void take_data ( SrBuffer<X>& b ) { size(0); b.leave_data(_data,_size); }
/*! Outputs all elements of the Buffer. Element type X must have its ouput operator <<
available. This is the only method of SrBuffer that is not inline. */
friend SrOutput& operator<< ( SrOutput& o, const SrBuffer<X>& b )
{ o << '[';
int i;
for ( i=0; i<b.size()-1; i++ ) o << b.const_get(i) << srspc;
if ( i<b.size() ) o << b.const_get(i);
return o << ']';
}
/*! Inputs all elements of the buffer. Element type X must have its input operator <<
available. */
friend SrInput& operator>> ( SrInput& in, SrBuffer<X>& b )
{ int s=0;
b.size(128);
in.get_token();
while (true)
{ in.get_token();
if ( in.last_token()[0]==']' ) break;
in.unget_token();
in >> b[s];
s++;
if ( s>=b.size() ) b.size ( b.size()+128 );
}
b.size ( s );
return in;
}
};
//============================== end of file ===============================
#endif // SR_BUFFER_H

1176
source/dcdt/se/sr_bv.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,230 @@
#include "precompiled.h"
# include "sr_bv_coldet.h"
//============================= SrBvColdet::Object ===========================
/*! Contains required data to be stored in each BvTree. */
class SrBvColdet::Object
{ public :
bool active;
srbvmat R;
srbvvec T;
SrBvTree tree;
public :
Object ()
{ SrBvMath::Midentity ( R ); // set to id matrix
SrBvMath::Videntity ( T ); // set to a null vector
active = true;
}
~Object () {};
void set_transformation ( const SrMat& srm )
{
# define SRM(i) (srbvreal)srm.get(i)
// here we pass from column-major to line-major order:
R[0][0]=SRM(0); R[1][0]=SRM(1); R[2][0]=SRM(2);
R[0][1]=SRM(4); R[1][1]=SRM(5); R[2][1]=SRM(6);
R[0][2]=SRM(8); R[1][2]=SRM(9); R[2][2]=SRM(10);
T[0]=SRM(12); T[1]=SRM(13); T[2]=SRM(14);
# undef SRM
}
void make_tree ( const SrModel& model )
{ tree.make ( model );
}
};
class SrBvColdetObjectManager : public SrClassManagerBase
{ public :
virtual void* alloc () { return (void*) new SrBvColdet::Object; }
virtual void* alloc ( const void* obj ) { return (void*) 0; } // copy not used/supported
virtual void free ( void* obj ) { delete (SrBvColdet::Object*) obj; }
};
//================================ SrBvColdet =====================================
SrBvColdet::SrBvColdet ()
{
_set = new SrSet<Object> ( new SrBvColdetObjectManager );
}
SrBvColdet::~SrBvColdet ()
{
delete _set;
}
void SrBvColdet::init ()
{
_set->init ();
_nbody.init ();
_disabled.init ();
_pairs.size (0);
}
int SrBvColdet::insert_object ( const SrModel& m )
{
int id = _set->insert ();
Object* o = _set->get ( id );
o->make_tree ( m );
_nbody.insert_object ( id, m );
return id;
}
void SrBvColdet::remove_object ( int id )
{
Object* o = _getobj ( id );
if ( !o ) return;
_set->remove(id); // will also delete o
_disabled.del_pairs_with_id ( id );
_nbody.remove_object ( id );
}
void SrBvColdet::update_transformation ( int id, const SrMat& m )
{
Object* o = _getobj ( id );
if ( !o ) return;
o->set_transformation ( m );
_nbody.update_transformation ( id, m );
}
void SrBvColdet::activate_object ( int id )
{
Object* o = _getobj ( id );
if ( !o ) return;
o->active = true;
}
void SrBvColdet::deactivate_object ( int id )
{
Object* o = _getobj ( id );
if ( !o ) return;
o->active = false;
}
void SrBvColdet::activate_pair ( int id1, int id2 )
{
_disabled.del_pair ( id1, id2 );
}
void SrBvColdet::deactivate_pair ( int id1, int id2 )
{
_disabled.add_pair ( id1, id2 );
}
//============================= private method ================================
bool SrBvColdet::_collide ( SrBvTreeQuery::CollideFlag flag )
{
_pairs.size ( 0 );
const SrArray<SrBvIdPairs::Elem*>& overlap = _nbody.overlapping_pairs.elements();
const SrArray<SrBvIdPairs::Elem*>& disabled = _disabled.elements();
int osize = overlap.size();
int dsize = disabled.size();
bool call_collide;
Object* o1;
Object* o2;
const SrBvIdPairs::Elem* curr_overlap;
const SrBvIdPairs::Elem* curr_disabled;
// Simultaneously traverse overlap and disabled,
// and make collision queries only when required.
int i;
for ( i=0; i<osize; i++ )
{ o1 = _set->get(i); //overlap[i]->id);
if ( !o1 ) continue;
if ( !o1->active ) continue; // this objects is not active
curr_disabled = i<dsize? disabled[i]:0;
curr_overlap = overlap[i];
while ( curr_overlap )
{ o2 = _set->get(curr_overlap->id);
// update curr_disabled position:
while ( curr_disabled )
{ if ( curr_disabled->id<curr_overlap->id )
curr_disabled = curr_disabled->next;
else break;
}
call_collide = false;
if ( !curr_disabled ) call_collide=true;
else
if ( curr_disabled->id>curr_overlap->id ) call_collide=true;
else
curr_disabled=curr_disabled->next;
//sr_out<<"COLLIDING "<<i<<" and "<<curr_overlap->id<<srnl;
if ( call_collide && o2->active )
{
_query.collide ( o1->R, o1->T, &(o1->tree), o2->R, o2->T, &(o2->tree), flag );
_query.colliding_pairs().size();
if ( _query.colliding_pairs().size()>0 ) // had collisions
{ _pairs.push()=i;
_pairs.push()=curr_overlap->id;
if ( flag==SrBvTreeQuery::CollideFirstContact ) return true;
}
}
curr_overlap = curr_overlap->next;
}
}
return _pairs.size()>0? true:false;
}
bool SrBvColdet::_collide_tolerance ( float toler )
{
const SrArray<SrBvIdPairs::Elem*>& overlap = _nbody.overlapping_pairs.elements();
const SrArray<SrBvIdPairs::Elem*>& disabled = _disabled.elements();
int osize = overlap.size();
int dsize = disabled.size();
bool call_tolerance;
Object* o1;
Object* o2;
const SrBvIdPairs::Elem* curr_overlap;
const SrBvIdPairs::Elem* curr_disabled;
// Simultaneously traverse overlap and disabled,
// and make collision queries only when required.
int i;
for ( i=0; i<osize; i++ )
{ o1 = _set->get(i); //overlap[i]->id);
if ( !o1 ) continue;
if ( !o1->active ) continue; // this objects is not active
curr_disabled = i<dsize? disabled[i]:0;
curr_overlap = overlap[i];
while ( curr_overlap )
{ o2 = _set->get(curr_overlap->id);
// update curr_disabled position:
while ( curr_disabled )
{ if ( curr_disabled->id<curr_overlap->id )
curr_disabled = curr_disabled->next;
else break;
}
call_tolerance = false;
if ( !curr_disabled ) call_tolerance=true;
else
if ( curr_disabled->id>curr_overlap->id ) call_tolerance=true;
else
curr_disabled=curr_disabled->next;
if ( call_tolerance && o2->active )
{ if ( _query.tolerance( o1->R, o1->T, &(o1->tree), o2->R, o2->T, &(o2->tree), toler ) )
return true;
}
curr_overlap = curr_overlap->next;
}
}
return false;
}
//=================================== EOF =====================================

View File

@ -0,0 +1,95 @@
/** \file sr_bv_coldet.h
* multi model collision test */
# ifndef SR_BV_COLDET_H
# define SR_BV_COLDET_H
# include "precompiled.h"
# include "sr_set.h"
# include "sr_bv_nbody.h"
# include "sr_bv_tree_query.h"
/* SrBvColdet uses SrBvNBody in order to efficiently determine
collisions between several dynamic objects.
Adapted from the VCollide package. Their copyright is in the source file. */
class SrBvColdet
{ private:
class Object;
friend class SrBvColdetObjectManager;
SrSet<Object>* _set;
SrBvNBody _nbody;
SrBvIdPairs _disabled;
SrBvTreeQuery _query;
SrArray<int> _pairs;
public:
/*! Constructor */
SrBvColdet ();
/*! Destructor (calls init()) */
~SrBvColdet ();
/*! Clears everything */
void init ();
/*! Create a new object and returns its id. */
int insert_object ( const SrModel& m );
/*! Removes (and deletes) the object from the database. */
void remove_object ( int id );
/*! Returns true if id represents a valid id in the database, and false otherwise */
bool id_valid ( int id ) { return _getobj(id)? true:false; }
/*! Update the transformation applied to an object.
Only rotations and translations are supported. */
void update_transformation ( int id, const SrMat& m );
/*! Turn on collision detection for an object. */
void activate_object ( int id );
/*! Turn off collision detection for an object. */
void deactivate_object ( int id );
/*! Turn on collision detection between a specific pair of objects. */
void activate_pair ( int id1, int id2 );
/*! Turn off collision detection between a specific pair of objects. */
void deactivate_pair ( int id1, int id2 );
/*! Returns true if the given pair of objects is deactivated and
false otherwise (-1 ids return false). */
bool pair_deactivated ( int id1, int id2 ) { return _disabled.pair_exists(id1,id2); }
/*! Counts and returns the number of deactivated pairs */
int count_deactivated_pairs () { return _disabled.count_pairs(); }
/*! Returns the array with the ids of the colliding objects in
the last collide_all() or has_collisions() query. The id
pairs are sequentially stored in the array. Therefore, the number
of collisions = colliding_pairs.size()/2. */
const SrArray<int>& colliding_pairs () const { return _pairs; }
/*! Perform collision detection only untill finding a first collision.
True is returned if a collision was found; false is returned otherwise. */
bool collide ()
{ return _collide ( SrBvTreeQuery::CollideFirstContact ); }
/*! Perform collision detection among all pairs of objects. The results can
be retrieved with colliding_pairs() */
bool collide_all ()
{ return _collide ( SrBvTreeQuery::CollideAllContacts ); }
/*! Returns true as soon as the distance between one pair of models
is found to be smaller than the given tolerance.
False is returned when all pairs respects the minimum clearance. */
bool collide_tolerance ( float toler ) { return _collide_tolerance(toler); }
private :
bool _collide ( SrBvTreeQuery::CollideFlag flag );
bool _collide_tolerance ( float toler );
Object* _getobj ( int id ) { return ( id<0 || id>_set->maxid() )? 0:_set->get(id); }
};
#endif // SR_BV_COLDET_H

View File

@ -0,0 +1,216 @@
# include "precompiled.h"
# include "sr_bv_id_pairs.h"
# define DEFAULT_SIZE 10
//============================== SrBvIdPairs ====================================
# define ORDER_IDS(id1,id2) { if (id1>id2) { SR_SWAPX(id1,id2); } } // make id1<id2
SrBvIdPairs::SrBvIdPairs ()
{
}
SrBvIdPairs::~SrBvIdPairs ()
{
init ();
}
void SrBvIdPairs::add_pair ( int id1, int id2 ) //add a pair to the set.
{
ORDER_IDS ( id1, id2 ); // order the ids
if ( id1<0 ) return;
while ( id1>=_arr.size() ) // increase the size of "_arr"
{ _arr.push() = 0; // reallocation is efficiently done when needed
}
Elem *current = _arr[id1]; //select the right list from "_arr".
if ( !current ) //if the list is empty, insert the element in the front.
{ current = new Elem;
current->id = id2;
current->next = NULL;
_arr[id1] = current;
}
else if (current->id > id2) //if the list is not empty but all
{ //elements are greater than id2, then
current = new Elem; //insert id2 in the front.
current->id = id2;
current->next = _arr[id1];
_arr[id1] = current;
}
else
{ while (current->next != NULL) // otherwise, find the correct location
{ // in the sorted list (ascending order)
if (current->next->id > id2) //and insert id2 there.
break;
current = current->next;
}
if (current->id == id2) // already there
{ return;
}
else
{ Elem *temp = new Elem;
temp->id = id2;
temp->next = current->next;
current->next = temp;
}
}
}
void SrBvIdPairs::del_pair ( int id1, int id2 ) //delete a pair from the set.
{
ORDER_IDS(id1, id2); //order the ids.
if ( id1<0 || id1>=_arr.size() ) return; //the pair does not exist in the set, so return
Elem *current = _arr[id1]; //otherwise, select the correct list.
if ( !current ) return; //if this list is empty, the pair doesn't exist, so return
if (current->id == id2) //otherwise, if id2 is the first element, delete it
{ _arr[id1] = current->next;
delete current;
return;
}
else
{ while (current->next != NULL) //if id2 is not the first element,
{ //start traversing the sorted list.
if (current->next->id > id2) //if you have moved too far away
{ //without hitting id2, then the pair
return; //pair doesn't exist. So, return.
}
else if (current->next->id == id2) //otherwise, delete id2.
{ Elem *temp = current->next;
current->next = current->next->next;
delete temp;
return;
}
current = current->next;
}
}
}
void SrBvIdPairs:: del_pairs_with_id ( int id ) //delete all pairs containing id.
{
int i;
Elem *t;
Elem *temp;
if ( id<_arr.size() )
{ temp = _arr[id];
while ( temp )
{ t = temp;
temp = temp->next;
delete t;
}
_arr[id] = 0;
for ( i=0; i<id; i++ ) del_pair(i,id);
}
else
{ for ( i=0; i<_arr.size(); i++ ) del_pair(i,id);
}
}
void SrBvIdPairs::init () //delete all pairs from the set.
{
int i;
int size = _arr.size();
Elem *current;
for ( i=0; i<size; i++ )
{ while ( _arr[i] )
{ current = _arr[i];
_arr[i] = current->next;
delete current;
}
}
_arr.size(0);
};
bool SrBvIdPairs::pair_exists ( int id1, int id2 )
{
ORDER_IDS ( id1, id2 );
if ( id1>=_arr.size() || id1<0 || id2<0 ) return false; // the pair cannot exist
Elem *current = _arr[id1]; //otherwise, get the correct list and look for id2
while ( current )
{ if ( current->id==id2) return true;
if ( current->id>id2) return false;
current = current->next;
}
return false;
}
int SrBvIdPairs::count_pairs ()
{
int i, n = 0;
Elem *current;
for ( i=0; i<_arr.size(); i++ )
{ current = _arr[i];
while ( current )
{ n++; current = current->next; }
}
return n;
}
/************************************************************************\
Copyright 1997 The University of North Carolina at Chapel Hill.
All Rights Reserved.
Permission to use, copy, modify and distribute this software
and its documentation for educational, research and non-profit
purposes, without fee, and without a written agreement is
hereby granted, provided that the above copyright notice and
the following three paragraphs appear in all copies.
IN NO EVENT SHALL THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL
HILL BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
EVEN IF THE UNIVERSITY OF NORTH CAROLINA HAVE BEEN ADVISED OF
THE POSSIBILITY OF SUCH DAMAGES.
Permission to use, copy, modify and distribute this software
and its documentation for educational, research and non-profit
purposes, without fee, and without a written agreement is
hereby granted, provided that the above copyright notice and
the following three paragraphs appear in all copies.
THE UNIVERSITY OF NORTH CAROLINA SPECIFICALLY DISCLAIM ANY
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
BASIS, AND THE UNIVERSITY OF NORTH CAROLINA HAS NO OBLIGATION
TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS.
---------------------------------
|Please send all BUG REPORTS to: |
| |
| geom@cs.unc.edu |
| |
---------------------------------
The authors may be contacted via:
US Mail: A. Pattekar/J. Cohen/T. Hudson/S. Gottschalk/M. Lin/D. Manocha
Department of Computer Science
Sitterson Hall, CB #3175
University of N. Carolina
Chapel Hill, NC 27599-3175
Phone: (919)962-1749
EMail: geom@cs.unc.edu
\************************************************************************/

View File

@ -0,0 +1,55 @@
/** \file sr_bv_id_pairs.h
* maintains a set of id pairs */
# ifndef SR_BV_ID_PAIRS_H
# define SR_BV_ID_PAIRS_H
# include "sr_array.h"
/*! Stores a set of pairs of integers. It is assumed that the data is sparse
(i.e. the size of this set is small as compared to the n*(n-1)/2 possible pairs).
A pair (id1, id2) exists <==> max(id1, id2) exists in the linked list pointed
to by elements[min(id1, id2)].
Adapted from the VCollide package. Their copyright is in the source file. */
class SrBvIdPairs
{ public:
/*! The node of the linked list containing the 2nd ids of all pairs
starting with the same id. It is a single connected list, with
the last pointer as null */
struct Elem { int id; Elem *next; };
private :
SrArray<Elem*> _arr;
public :
/*! Constructor */
SrBvIdPairs ();
/*! Destructor */
~SrBvIdPairs ();
/*! Returns the array of elements. Each element[i] points to the sorted list
with all pairs (i,x) in the structure, i<x. If no pairs start with i,
element[i] will be NULL, therefore some entries may have null pointers. */
const SrArray<Elem*>& elements() const { return _arr; }
/*! Add a pair of ids to the set */
void add_pair ( int id1, int id2 );
/*! Delete a pair from the set */
void del_pair ( int id1, int id2 );
/*! Delete all pairs containing id */
void del_pairs_with_id ( int id );
/*! Empty the set */
void init ();
/*! Check if a pair of ids exists (-1 ids return false) */
bool pair_exists ( int id1, int id2 );
/*! Counts and returns the number of existing pairs */
int count_pairs ();
};
#endif // SR_BV_ID_PAIRS_H

View File

@ -0,0 +1,902 @@
#include "precompiled.h"/* Note: this code was adapted from the PQP library;
their copyright notice can be found at the end of this file. */
# include <math.h>
# include <stdio.h>
# include "sr_bv_math.h"
//====================== namespace SrBvMath =======================
/* void Mprint ( const srbvmat M );
void SrBvMath::Mprint ( const srbvmat M )
{
printf("%g %g %g\n%g %g %g\n%g %g %g\n",
M[0][0], M[0][1], M[0][2],
M[1][0], M[1][1], M[1][2],
M[2][0], M[2][1], M[2][2]);
}*/
/* void Vprint ( const srbvvec V );
void SrBvMath::Vprint ( const srbvvec V )
{
printf("%g %g %g\n", V[0], V[1], V[2]);
}*/
/* void Vset ( srbvvec V, srbvreal x, srbvreal y, srbvreal z );
void SrBvMath::Vset ( srbvvec V, srbvreal x, srbvreal y, srbvreal z )
{
V[0]=x; V[1]=y; V[2]=z;
}*/
void SrBvMath::Vset ( srbvvec V, float* fp )
{
V[0]=fp[0]; V[1]=fp[1]; V[2]=fp[2];
}
void SrBvMath::Midentity ( srbvmat M )
{
M[0][0] = M[1][1] = M[2][2] = 1;
M[0][1] = M[1][2] = M[2][0] = 0;
M[0][2] = M[1][0] = M[2][1] = 0;
}
void SrBvMath::Videntity ( srbvvec T )
{
T[0] = T[1] = T[2] = 0.0;
}
void SrBvMath::McM ( srbvmat Mr, const srbvmat M )
{
Mr[0][0] = M[0][0]; Mr[0][1] = M[0][1]; Mr[0][2] = M[0][2];
Mr[1][0] = M[1][0]; Mr[1][1] = M[1][1]; Mr[1][2] = M[1][2];
Mr[2][0] = M[2][0]; Mr[2][1] = M[2][1]; Mr[2][2] = M[2][2];
}
/*void MTcM ( srbvmat Mr, const srbvmat M ); // Mr=M.transpose()
void SrBvMath::MTcM ( srbvmat Mr, const srbvmat M )
{
Mr[0][0] = M[0][0]; Mr[1][0] = M[0][1]; Mr[2][0] = M[0][2];
Mr[0][1] = M[1][0]; Mr[1][1] = M[1][1]; Mr[2][1] = M[1][2];
Mr[0][2] = M[2][0]; Mr[1][2] = M[2][1]; Mr[2][2] = M[2][2];
}*/
void SrBvMath::VcV ( srbvvec Vr, const srbvvec V )
{
Vr[0] = V[0]; Vr[1] = V[1]; Vr[2] = V[2];
}
void SrBvMath::McolcV ( srbvvec Vr, const srbvmat M, int c )
{
Vr[0] = M[0][c];
Vr[1] = M[1][c];
Vr[2] = M[2][c];
}
void SrBvMath::McolcMcol ( srbvmat Mr, int cr, const srbvmat M, int c )
{
Mr[0][cr] = M[0][c];
Mr[1][cr] = M[1][c];
Mr[2][cr] = M[2][c];
}
/*
void MxMpV ( srbvmat Mr, const srbvmat M1, const srbvmat M2, const srbvvec T ); // Mr=M1*M2+T
void SrBvMath::MxMpV ( srbvmat Mr, const srbvmat M1, const srbvmat M2, const srbvvec T )
{
Mr[0][0] = (M1[0][0] * M2[0][0] +
M1[0][1] * M2[1][0] +
M1[0][2] * M2[2][0] +
T[0]);
Mr[1][0] = (M1[1][0] * M2[0][0] +
M1[1][1] * M2[1][0] +
M1[1][2] * M2[2][0] +
T[1]);
Mr[2][0] = (M1[2][0] * M2[0][0] +
M1[2][1] * M2[1][0] +
M1[2][2] * M2[2][0] +
T[2]);
Mr[0][1] = (M1[0][0] * M2[0][1] +
M1[0][1] * M2[1][1] +
M1[0][2] * M2[2][1] +
T[0]);
Mr[1][1] = (M1[1][0] * M2[0][1] +
M1[1][1] * M2[1][1] +
M1[1][2] * M2[2][1] +
T[1]);
Mr[2][1] = (M1[2][0] * M2[0][1] +
M1[2][1] * M2[1][1] +
M1[2][2] * M2[2][1] +
T[2]);
Mr[0][2] = (M1[0][0] * M2[0][2] +
M1[0][1] * M2[1][2] +
M1[0][2] * M2[2][2] +
T[0]);
Mr[1][2] = (M1[1][0] * M2[0][2] +
M1[1][1] * M2[1][2] +
M1[1][2] * M2[2][2] +
T[1]);
Mr[2][2] = (M1[2][0] * M2[0][2] +
M1[2][1] * M2[1][2] +
M1[2][2] * M2[2][2] +
T[2]);
}*/
void SrBvMath::MxM ( srbvmat Mr, const srbvmat M1, const srbvmat M2 )
{
Mr[0][0] = (M1[0][0] * M2[0][0] +
M1[0][1] * M2[1][0] +
M1[0][2] * M2[2][0]);
Mr[1][0] = (M1[1][0] * M2[0][0] +
M1[1][1] * M2[1][0] +
M1[1][2] * M2[2][0]);
Mr[2][0] = (M1[2][0] * M2[0][0] +
M1[2][1] * M2[1][0] +
M1[2][2] * M2[2][0]);
Mr[0][1] = (M1[0][0] * M2[0][1] +
M1[0][1] * M2[1][1] +
M1[0][2] * M2[2][1]);
Mr[1][1] = (M1[1][0] * M2[0][1] +
M1[1][1] * M2[1][1] +
M1[1][2] * M2[2][1]);
Mr[2][1] = (M1[2][0] * M2[0][1] +
M1[2][1] * M2[1][1] +
M1[2][2] * M2[2][1]);
Mr[0][2] = (M1[0][0] * M2[0][2] +
M1[0][1] * M2[1][2] +
M1[0][2] * M2[2][2]);
Mr[1][2] = (M1[1][0] * M2[0][2] +
M1[1][1] * M2[1][2] +
M1[1][2] * M2[2][2]);
Mr[2][2] = (M1[2][0] * M2[0][2] +
M1[2][1] * M2[1][2] +
M1[2][2] * M2[2][2]);
}
/* void MxMT ( srbvmat Mr, const srbvmat M1, const srbvmat M2 ); // Mr=M1*M2.transpose()
void SrBvMath::MxMT ( srbvmat Mr, const srbvmat M1, const srbvmat M2 )
{
Mr[0][0] = (M1[0][0] * M2[0][0] +
M1[0][1] * M2[0][1] +
M1[0][2] * M2[0][2]);
Mr[1][0] = (M1[1][0] * M2[0][0] +
M1[1][1] * M2[0][1] +
M1[1][2] * M2[0][2]);
Mr[2][0] = (M1[2][0] * M2[0][0] +
M1[2][1] * M2[0][1] +
M1[2][2] * M2[0][2]);
Mr[0][1] = (M1[0][0] * M2[1][0] +
M1[0][1] * M2[1][1] +
M1[0][2] * M2[1][2]);
Mr[1][1] = (M1[1][0] * M2[1][0] +
M1[1][1] * M2[1][1] +
M1[1][2] * M2[1][2]);
Mr[2][1] = (M1[2][0] * M2[1][0] +
M1[2][1] * M2[1][1] +
M1[2][2] * M2[1][2]);
Mr[0][2] = (M1[0][0] * M2[2][0] +
M1[0][1] * M2[2][1] +
M1[0][2] * M2[2][2]);
Mr[1][2] = (M1[1][0] * M2[2][0] +
M1[1][1] * M2[2][1] +
M1[1][2] * M2[2][2]);
Mr[2][2] = (M1[2][0] * M2[2][0] +
M1[2][1] * M2[2][1] +
M1[2][2] * M2[2][2]);
}*/
void SrBvMath::MTxM ( srbvmat Mr, const srbvmat M1, const srbvmat M2 )
{
Mr[0][0] = (M1[0][0] * M2[0][0] +
M1[1][0] * M2[1][0] +
M1[2][0] * M2[2][0]);
Mr[1][0] = (M1[0][1] * M2[0][0] +
M1[1][1] * M2[1][0] +
M1[2][1] * M2[2][0]);
Mr[2][0] = (M1[0][2] * M2[0][0] +
M1[1][2] * M2[1][0] +
M1[2][2] * M2[2][0]);
Mr[0][1] = (M1[0][0] * M2[0][1] +
M1[1][0] * M2[1][1] +
M1[2][0] * M2[2][1]);
Mr[1][1] = (M1[0][1] * M2[0][1] +
M1[1][1] * M2[1][1] +
M1[2][1] * M2[2][1]);
Mr[2][1] = (M1[0][2] * M2[0][1] +
M1[1][2] * M2[1][1] +
M1[2][2] * M2[2][1]);
Mr[0][2] = (M1[0][0] * M2[0][2] +
M1[1][0] * M2[1][2] +
M1[2][0] * M2[2][2]);
Mr[1][2] = (M1[0][1] * M2[0][2] +
M1[1][1] * M2[1][2] +
M1[2][1] * M2[2][2]);
Mr[2][2] = (M1[0][2] * M2[0][2] +
M1[1][2] * M2[1][2] +
M1[2][2] * M2[2][2]);
}
void SrBvMath::MxV ( srbvvec Vr, const srbvmat M1, const srbvvec V1 )
{
Vr[0] = (M1[0][0] * V1[0] +
M1[0][1] * V1[1] +
M1[0][2] * V1[2]);
Vr[1] = (M1[1][0] * V1[0] +
M1[1][1] * V1[1] +
M1[1][2] * V1[2]);
Vr[2] = (M1[2][0] * V1[0] +
M1[2][1] * V1[1] +
M1[2][2] * V1[2]);
}
void SrBvMath::MxVpV ( srbvvec Vr, const srbvmat M1, const srbvvec V1, const srbvvec V2)
{
Vr[0] = (M1[0][0] * V1[0] +
M1[0][1] * V1[1] +
M1[0][2] * V1[2] +
V2[0]);
Vr[1] = (M1[1][0] * V1[0] +
M1[1][1] * V1[1] +
M1[1][2] * V1[2] +
V2[1]);
Vr[2] = (M1[2][0] * V1[0] +
M1[2][1] * V1[1] +
M1[2][2] * V1[2] +
V2[2]);
}
/* void sMxVpV ( srbvvec Vr, srbvreal s1, const srbvmat M1, const srbvvec V1, const srbvvec V2 );
void SrBvMath::sMxVpV ( srbvvec Vr, srbvreal s1, const srbvmat M1, const srbvvec V1, const srbvvec V2 )
{
Vr[0] = s1 * (M1[0][0] * V1[0] +
M1[0][1] * V1[1] +
M1[0][2] * V1[2]) +
V2[0];
Vr[1] = s1 * (M1[1][0] * V1[0] +
M1[1][1] * V1[1] +
M1[1][2] * V1[2]) +
V2[1];
Vr[2] = s1 * (M1[2][0] * V1[0] +
M1[2][1] * V1[1] +
M1[2][2] * V1[2]) +
V2[2];
}*/
void SrBvMath::MTxV ( srbvvec Vr, const srbvmat M1, const srbvvec V1 )
{
Vr[0] = (M1[0][0] * V1[0] +
M1[1][0] * V1[1] +
M1[2][0] * V1[2]);
Vr[1] = (M1[0][1] * V1[0] +
M1[1][1] * V1[1] +
M1[2][1] * V1[2]);
Vr[2] = (M1[0][2] * V1[0] +
M1[1][2] * V1[1] +
M1[2][2] * V1[2]);
}
/* void sMTxV ( srbvvec Vr, srbvreal s1, const srbvmat M1, const srbvvec V1 );
void SrBvMath::sMTxV ( srbvvec Vr, srbvreal s1, const srbvmat M1, const srbvvec V1 )
{
Vr[0] = s1*(M1[0][0] * V1[0] +
M1[1][0] * V1[1] +
M1[2][0] * V1[2]);
Vr[1] = s1*(M1[0][1] * V1[0] +
M1[1][1] * V1[1] +
M1[2][1] * V1[2]);
Vr[2] = s1*(M1[0][2] * V1[0] +
M1[1][2] * V1[1] +
M1[2][2] * V1[2]);
}*/
/* void sMxV ( srbvvec Vr, srbvreal s1, const srbvmat M1, const srbvvec V1 );
void SrBvMath::sMxV ( srbvvec Vr, srbvreal s1, const srbvmat M1, const srbvvec V1 )
{
Vr[0] = s1*(M1[0][0] * V1[0] +
M1[0][1] * V1[1] +
M1[0][2] * V1[2]);
Vr[1] = s1*(M1[1][0] * V1[0] +
M1[1][1] * V1[1] +
M1[1][2] * V1[2]);
Vr[2] = s1*(M1[2][0] * V1[0] +
M1[2][1] * V1[1] +
M1[2][2] * V1[2]);
}*/
void SrBvMath::VmV ( srbvvec Vr, const srbvvec V1, const srbvvec V2 )
{
Vr[0] = V1[0] - V2[0];
Vr[1] = V1[1] - V2[1];
Vr[2] = V1[2] - V2[2];
}
void SrBvMath::VpV ( srbvvec Vr, const srbvvec V1, const srbvvec V2 )
{
Vr[0] = V1[0] + V2[0];
Vr[1] = V1[1] + V2[1];
Vr[2] = V1[2] + V2[2];
}
void SrBvMath::VpVxS ( srbvvec Vr, const srbvvec V1, const srbvvec V2, srbvreal s )
{
Vr[0] = V1[0] + V2[0] * s;
Vr[1] = V1[1] + V2[1] * s;
Vr[2] = V1[2] + V2[2] * s;
}
/* void MskewV ( srbvmat M, const srbvvec v );
void SrBvMath::MskewV ( srbvmat M, const srbvvec v )
{
M[0][0] = M[1][1] = M[2][2] = 0.0;
M[1][0] = v[2];
M[0][1] = -v[2];
M[0][2] = v[1];
M[2][0] = -v[1];
M[1][2] = -v[0];
M[2][1] = v[0];
}*/
void SrBvMath::VcrossV ( srbvvec Vr, const srbvvec V1, const srbvvec V2 )
{
Vr[0] = V1[1]*V2[2] - V1[2]*V2[1];
Vr[1] = V1[2]*V2[0] - V1[0]*V2[2];
Vr[2] = V1[0]*V2[1] - V1[1]*V2[0];
}
/* srbvreal Vlength ( srbvvec V );
srbvreal SrBvMath::Vlength ( srbvvec V )
{
return sqrt(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]);
}*/
/* void Vnormalize ( srbvvec V );
void SrBvMath::Vnormalize ( srbvvec V )
{
srbvreal d = srbvreal(1.0) / sqrt(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]);
V[0] *= d;
V[1] *= d;
V[2] *= d;
}*/
srbvreal SrBvMath::VdotV ( const srbvvec V1, const srbvvec V2 )
{
return (V1[0]*V2[0] + V1[1]*V2[1] + V1[2]*V2[2]);
}
srbvreal SrBvMath::VdistV2 ( const srbvvec V1, const srbvvec V2 )
{
return ( (V1[0]-V2[0]) * (V1[0]-V2[0]) +
(V1[1]-V2[1]) * (V1[1]-V2[1]) +
(V1[2]-V2[2]) * (V1[2]-V2[2]));
}
void SrBvMath::VxS ( srbvvec Vr, const srbvvec V, srbvreal s )
{
Vr[0] = V[0] * s;
Vr[1] = V[1] * s;
Vr[2] = V[2] * s;
}
/* void MRotZ ( srbvmat Mr, srbvreal t );
void MRotX ( srbvmat Mr, srbvreal t );
void MRotY ( srbvmat Mr, srbvreal t );
void Mqinverse ( srbvmat Mr, srbvmat M );*/
/*
void SrBvMath::MRotZ ( srbvmat Mr, srbvreal t)
{
Mr[0][0] = cos(t);
Mr[1][0] = sin(t);
Mr[0][1] = -Mr[1][0];
Mr[1][1] = Mr[0][0];
Mr[2][0] = Mr[2][1] = 0.0;
Mr[0][2] = Mr[1][2] = 0.0;
Mr[2][2] = 1.0;
}
void SrBvMath::MRotX ( srbvmat Mr, srbvreal t )
{
Mr[1][1] = cos(t);
Mr[2][1] = sin(t);
Mr[1][2] = -Mr[2][1];
Mr[2][2] = Mr[1][1];
Mr[0][1] = Mr[0][2] = 0.0;
Mr[1][0] = Mr[2][0] = 0.0;
Mr[0][0] = 1.0;
}
void SrBvMath::MRotY ( srbvmat Mr, srbvreal t )
{
Mr[2][2] = cos(t);
Mr[0][2] = sin(t);
Mr[2][0] = -Mr[0][2];
Mr[0][0] = Mr[2][2];
Mr[1][2] = Mr[1][0] = 0.0;
Mr[2][1] = Mr[0][1] = 0.0;
Mr[1][1] = 1.0;
}
void SrBvMath::Mqinverse ( srbvmat Mr, srbvmat M )
{
int i,j;
for(i=0; i<3; i++)
for(j=0; j<3; j++)
{
int i1 = (i+1)%3;
int i2 = (i+2)%3;
int j1 = (j+1)%3;
int j2 = (j+2)%3;
Mr[i][j] = (M[j1][i1]*M[j2][i2] - M[j1][i2]*M[j2][i1]);
}
}*/
// Meigen from Numerical Recipes in C
#define ROTATE(a,i,j,k,l) g=a[i][j]; h=a[k][l]; a[i][j]=g-s*(h+g*tau); a[k][l]=h+s*(g-h*tau)
void SrBvMath::Meigen ( srbvmat vout, srbvvec dout, srbvmat a )
{
int n = 3;
int j,iq,ip,i;
srbvreal tresh,theta,tau,t,sm,s,h,g,c;
int nrot;
srbvvec b, z, d;
srbvmat v;
Midentity(v);
for(ip=0; ip<n; ip++)
{
b[ip] = a[ip][ip];
d[ip] = a[ip][ip];
z[ip] = 0.0;
}
nrot = 0;
for(i=0; i<50; i++)
{ sm=0.0;
for(ip=0;ip<n;ip++) for(iq=ip+1;iq<n;iq++) sm+=fabs(a[ip][iq]);
if (sm == 0.0)
{ McM(vout, v);
VcV(dout, d);
return;
}
if (i < 3)
tresh=srbvreal(0.2)*sm/(n*n);
else
tresh=0.0;
for(ip=0; ip<n; ip++) for(iq=ip+1; iq<n; iq++)
{ g = srbvreal(100.0)*fabs(a[ip][iq]);
if (i>3 &&
fabs(d[ip])+g==fabs(d[ip]) &&
fabs(d[iq])+g==fabs(d[iq]))
{ a[ip][iq]=0.0; }
else if (fabs(a[ip][iq])>tresh)
{ h = d[iq]-d[ip];
if (fabs(h)+g == fabs(h)) t=(a[ip][iq])/h;
else
{ theta=(srbvreal)0.5*h/(a[ip][iq]);
t=(srbvreal)(1.0/(fabs(theta)+sqrt(1.0+theta*theta)));
if (theta < 0.0) t = -t;
}
c=(srbvreal)1.0/sqrt(1+t*t);
s=t*c;
tau=s/((srbvreal)1.0+c);
h=t*a[ip][iq];
z[ip] -= h;
z[iq] += h;
d[ip] -= h;
d[iq] += h;
a[ip][iq]=0.0;
for(j=0;j<ip;j++) { ROTATE(a,j,ip,j,iq); }
for(j=ip+1;j<iq;j++) { ROTATE(a,ip,j,j,iq); }
for(j=iq+1;j<n;j++) { ROTATE(a,ip,j,iq,j); }
for(j=0;j<n;j++) { ROTATE(v,j,ip,j,iq); }
nrot++;
}
}
for(ip=0;ip<n;ip++)
{ b[ip] += z[ip];
d[ip] = b[ip];
z[ip] = 0.0;
}
}
fprintf(stderr, "eigen: too many iterations in Jacobi transform.\n");
return;
}
#ifdef _WIN32
#include <float.h>
#define isnan _isnan
#endif
//--------------------------------------------------------------------------
// SegPoints()
//
// Returns closest points between an segment pair.
// Implemented from an algorithm described in
//
// Vladimir J. Lumelsky,
// On fast computation of distance between line segments.
// In Information Processing Letters, no. 21, pages 55-61, 1985.
//--------------------------------------------------------------------------
void SrBvMath::SegPoints ( srbvvec VEC, srbvvec X, srbvvec Y,
const srbvvec P, const srbvvec A,
const srbvvec Q, const srbvvec B )
{
srbvreal T[3], A_dot_A, B_dot_B, A_dot_B, A_dot_T, B_dot_T;
srbvreal TMP[3];
VmV(T,Q,P);
A_dot_A = VdotV(A,A);
B_dot_B = VdotV(B,B);
A_dot_B = VdotV(A,B);
A_dot_T = VdotV(A,T);
B_dot_T = VdotV(B,T);
// t parameterizes ray P,A
// u parameterizes ray Q,B
srbvreal t,u;
// compute t for the closest point on ray P,A to
// ray Q,B
srbvreal denom = A_dot_A*B_dot_B - A_dot_B*A_dot_B;
t = (A_dot_T*B_dot_B - B_dot_T*A_dot_B) / denom;
// clamp result so t is on the segment P,A
if ((t < 0) || isnan(t)) t = 0; else if (t > 1) t = 1;
// find u for point on ray Q,B closest to point at t
u = (t*A_dot_B - B_dot_T) / B_dot_B;
// if u is on segment Q,B, t and u correspond to
// closest points, otherwise, clamp u, recompute and
// clamp t
if ((u <= 0) || isnan(u)) {
VcV(Y, Q);
t = A_dot_T / A_dot_A;
if ((t <= 0) || isnan(t)) {
VcV(X, P);
VmV(VEC, Q, P);
}
else if (t >= 1) {
VpV(X, P, A);
VmV(VEC, Q, X);
}
else {
VpVxS(X, P, A, t);
VcrossV(TMP, T, A);
VcrossV(VEC, A, TMP);
}
}
else if (u >= 1) {
VpV(Y, Q, B);
t = (A_dot_B + A_dot_T) / A_dot_A;
if ((t <= 0) || isnan(t)) {
VcV(X, P);
VmV(VEC, Y, P);
}
else if (t >= 1) {
VpV(X, P, A);
VmV(VEC, Y, X);
}
else {
VpVxS(X, P, A, t);
VmV(T, Y, P);
VcrossV(TMP, T, A);
VcrossV(VEC, A, TMP);
}
}
else {
VpVxS(Y, Q, B, u);
if ((t <= 0) || isnan(t)) {
VcV(X, P);
VcrossV(TMP, T, B);
VcrossV(VEC, B, TMP);
}
else if (t >= 1) {
VpV(X, P, A);
VmV(T, Q, X);
VcrossV(TMP, T, B);
VcrossV(VEC, B, TMP);
}
else {
VpVxS(X, P, A, t);
VcrossV(VEC, A, B);
if (VdotV(VEC, T) < 0) {
VxS(VEC, VEC, -1);
}
}
}
}
//--------------------------------------------------------------------------
// TriDist()
//
// Computes the closest points on two triangles, and returns the
// distance between them.
//
// S and T are the triangles, stored tri[point][dimension].
//
// If the triangles are disjoint, P and Q give the closest points of
// S and T respectively. However, if the triangles overlap, P and Q
// are basically a random pair of points from the triangles, not
// coincident points on the intersection of the triangles, as might
// be expected.
//--------------------------------------------------------------------------
srbvreal
SrBvMath::TriDist( srbvvec P, srbvvec Q,
const srbvmat S, const srbvmat T )
{
// Compute vectors along the 6 sides
srbvreal Sv[3][3], Tv[3][3];
srbvreal VEC[3];
VmV(Sv[0],S[1],S[0]);
VmV(Sv[1],S[2],S[1]);
VmV(Sv[2],S[0],S[2]);
VmV(Tv[0],T[1],T[0]);
VmV(Tv[1],T[2],T[1]);
VmV(Tv[2],T[0],T[2]);
// For each edge pair, the vector connecting the closest points
// of the edges defines a slab (parallel planes at head and tail
// enclose the slab). If we can show that the off-edge vertex of
// each triangle is outside of the slab, then the closest points
// of the edges are the closest points for the triangles.
// Even if these tests fail, it may be helpful to know the closest
// points found, and whether the triangles were shown disjoint
srbvreal V[3];
srbvreal Z[3];
srbvreal minP[3], minQ[3], mindd;
int shown_disjoint = 0;
mindd = VdistV2(S[0],T[0]) + 1; // Set first minimum safely high
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
// Find closest points on edges i & j, plus the
// vector (and distance squared) between these points
SegPoints(VEC,P,Q,S[i],Sv[i],T[j],Tv[j]);
VmV(V,Q,P);
srbvreal dd = VdotV(V,V);
// Verify this closest point pair only if the distance
// squared is less than the minimum found thus far.
if (dd <= mindd)
{
VcV(minP,P);
VcV(minQ,Q);
mindd = dd;
VmV(Z,S[(i+2)%3],P);
srbvreal a = VdotV(Z,VEC);
VmV(Z,T[(j+2)%3],Q);
srbvreal b = VdotV(Z,VEC);
if ((a <= 0) && (b >= 0)) return sqrt(dd);
srbvreal p = VdotV(V, VEC);
if (a < 0) a = 0;
if (b > 0) b = 0;
if ((p - a + b) > 0) shown_disjoint = 1;
}
}
}
// No edge pairs contained the closest points.
// either:
// 1. one of the closest points is a vertex, and the
// other point is interior to a face.
// 2. the triangles are overlapping.
// 3. an edge of one triangle is parallel to the other's face. If
// cases 1 and 2 are not true, then the closest points from the 9
// edge pairs checks above can be taken as closest points for the
// triangles.
// 4. possibly, the triangles were degenerate. When the
// triangle points are nearly colinear or coincident, one
// of above tests might fail even though the edges tested
// contain the closest points.
// First check for case 1
srbvreal Sn[3], Snl;
VcrossV(Sn,Sv[0],Sv[1]); // Compute normal to S triangle
Snl = VdotV(Sn,Sn); // Compute square of length of normal
// If cross product is long enough,
if (Snl > 1e-15)
{
// Get projection lengths of T points
srbvreal Tp[3];
VmV(V,S[0],T[0]);
Tp[0] = VdotV(V,Sn);
VmV(V,S[0],T[1]);
Tp[1] = VdotV(V,Sn);
VmV(V,S[0],T[2]);
Tp[2] = VdotV(V,Sn);
// If Sn is a separating direction,
// find point with smallest projection
int point = -1;
if ((Tp[0] > 0) && (Tp[1] > 0) && (Tp[2] > 0))
{
if (Tp[0] < Tp[1]) point = 0; else point = 1;
if (Tp[2] < Tp[point]) point = 2;
}
else if ((Tp[0] < 0) && (Tp[1] < 0) && (Tp[2] < 0))
{
if (Tp[0] > Tp[1]) point = 0; else point = 1;
if (Tp[2] > Tp[point]) point = 2;
}
// If Sn is a separating direction,
if (point >= 0)
{
shown_disjoint = 1;
// Test whether the point found, when projected onto the
// other triangle, lies within the face.
VmV(V,T[point],S[0]);
VcrossV(Z,Sn,Sv[0]);
if (VdotV(V,Z) > 0)
{
VmV(V,T[point],S[1]);
VcrossV(Z,Sn,Sv[1]);
if (VdotV(V,Z) > 0)
{
VmV(V,T[point],S[2]);
VcrossV(Z,Sn,Sv[2]);
if (VdotV(V,Z) > 0)
{
// T[point] passed the test - it's a closest point for
// the T triangle; the other point is on the face of S
VpVxS(P,T[point],Sn,Tp[point]/Snl);
VcV(Q,T[point]);
return sqrt(VdistV2(P,Q));
}
}
}
}
}
srbvreal Tn[3], Tnl;
VcrossV(Tn,Tv[0],Tv[1]);
Tnl = VdotV(Tn,Tn);
if (Tnl > 1e-15)
{
srbvreal Sp[3];
VmV(V,T[0],S[0]);
Sp[0] = VdotV(V,Tn);
VmV(V,T[0],S[1]);
Sp[1] = VdotV(V,Tn);
VmV(V,T[0],S[2]);
Sp[2] = VdotV(V,Tn);
int point = -1;
if ((Sp[0] > 0) && (Sp[1] > 0) && (Sp[2] > 0))
{
if (Sp[0] < Sp[1]) point = 0; else point = 1;
if (Sp[2] < Sp[point]) point = 2;
}
else if ((Sp[0] < 0) && (Sp[1] < 0) && (Sp[2] < 0))
{
if (Sp[0] > Sp[1]) point = 0; else point = 1;
if (Sp[2] > Sp[point]) point = 2;
}
if (point >= 0)
{
shown_disjoint = 1;
VmV(V,S[point],T[0]);
VcrossV(Z,Tn,Tv[0]);
if (VdotV(V,Z) > 0)
{
VmV(V,S[point],T[1]);
VcrossV(Z,Tn,Tv[1]);
if (VdotV(V,Z) > 0)
{
VmV(V,S[point],T[2]);
VcrossV(Z,Tn,Tv[2]);
if (VdotV(V,Z) > 0)
{
VcV(P,S[point]);
VpVxS(Q,S[point],Tn,Sp[point]/Tnl);
return sqrt(VdistV2(P,Q));
}
}
}
}
}
// Case 1 can't be shown.
// If one of these tests showed the triangles disjoint,
// we assume case 3 or 4, otherwise we conclude case 2,
// that the triangles overlap.
if (shown_disjoint)
{
VcV(P,minP);
VcV(Q,minQ);
return sqrt(mindd);
}
else return 0;
}
/*************************************************************************\
Copyright 1999 The University of North Carolina at Chapel Hill.
All Rights Reserved.
Permission to use, copy, modify and distribute this software and its
documentation for educational, research and non-profit purposes, without
fee, and without a written agreement is hereby granted, provided that the
above copyright notice and the following three paragraphs appear in all
copies.
IN NO EVENT SHALL THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL BE
LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE
USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
OF NORTH CAROLINA HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
THE UNIVERSITY OF NORTH CAROLINA SPECIFICALLY DISCLAIM ANY
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
NORTH CAROLINA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
The authors may be contacted via:
US Mail: S. Gottschalk, E. Larsen
Department of Computer Science
Sitterson Hall, CB #3175
University of N. Carolina
Chapel Hill, NC 27599-3175
Phone: (919)962-1749
EMail: geom@cs.unc.edu
\**************************************************************************/

View File

@ -0,0 +1,72 @@
/** \file sr_bv_math.h
* BV tree math routines */
# ifndef SR_BV_MATH_H
# define SR_BV_MATH_H
# include "sr.h"
/*! if the following macro is defined, will use floats instead of doubles
Default is doubles (macro not defined) */
# ifdef SR_BV_MATH_FLOAT
typedef float srbvreal;
# else
typedef double srbvreal;
# endif
typedef srbvreal srbvmat[3][3];
typedef srbvreal srbvvec[3];
const srbvreal srbvpi = (srbvreal)SR_PI;
//============================== SrBvMath ===============================
/*! This namespace encapsulates a (row-major) matrix/vector code
adapted from the PQP package. Their copyright notice
is displayed in the source file */
namespace SrBvMath
{
void Vset ( srbvvec V, float* fp );
void Midentity ( srbvmat M ); // set to id matrix
void Videntity ( srbvvec T ); // set to a null vector
void McM ( srbvmat Mr, const srbvmat M ); // Mr=M
void VcV ( srbvvec Vr, const srbvvec V ); // Vr=V
void McolcV ( srbvvec Vr, const srbvmat M, int c ); // Vr=M.column(c)
void McolcMcol ( srbvmat Mr, int cr, const srbvmat M, int c ); // Vr.column(cr)=M.column(c)
void MxM ( srbvmat Mr, const srbvmat M1, const srbvmat M2 ); // Mr=M1*M2
void MTxM ( srbvmat Mr, const srbvmat M1, const srbvmat M2 ); // Mr=M1.transpose()*M2
void MxV ( srbvvec Vr, const srbvmat M1, const srbvvec V1 ); // Vr=M1*V1
void MxVpV ( srbvvec Vr, const srbvmat M1, const srbvvec V1, const srbvvec V2 );
void MTxV ( srbvvec Vr, const srbvmat M1, const srbvvec V1 );
void VmV ( srbvvec Vr, const srbvvec V1, const srbvvec V2 );
void VpV ( srbvvec Vr, const srbvvec V1, const srbvvec V2 );
void VpVxS ( srbvvec Vr, const srbvvec V1, const srbvvec V2, srbvreal s );
void VcrossV ( srbvvec Vr, const srbvvec V1, const srbvvec V2 );
srbvreal VdotV ( const srbvvec V1, const srbvvec V2 );
srbvreal VdistV2 ( const srbvvec V1, const srbvvec V2 );
void VxS ( srbvvec Vr, const srbvvec V, srbvreal s );
void Meigen ( srbvmat vout, srbvvec dout, srbvmat a );
/*! SegPoints returns closest points between a segment pair.
Points x and y are the found closest points.
Parameters p and a are segment 1 origin and vector.
Parameters q and b are segment 2 origin and vector. */
void SegPoints( srbvvec vec, srbvvec x, srbvvec y,
const srbvvec p, const srbvvec a,
const srbvvec q, const srbvvec b );
/*! TriDist computes the closest points on two triangles, and
returns the distance between them.
s and t are the triangles, stored tri[point][dimension].
If the triangles are disjoint, p and q give the closest
points of s and t respectively. However, if the triangles
overlap, p and q are basically a random pair of points from
the triangles, not coincident points on the intersection of
the triangles, as might be expected. */
srbvreal TriDist ( srbvvec p, srbvvec q,
const srbvmat s, const srbvmat t );
}
//============================== end of file ===============================
# endif // SR_BV_MATH_H

View File

@ -0,0 +1,471 @@
#include "precompiled.h"
# include <math.h>
# include "sr_bv_nbody.h"
# include "sr_model.h"
# include "sr_box.h"
# include "sr_mat.h"
# include "sr_trace.h"
# define SR_USE_TRACE1 // function calls trace
//============================== SrBvNBody::EndPoint ====================================
/* EndPoint stores either the (min_x, min_y, min_z)
or the (max_x, max_y, max_z) end-point of an AABB.
Each instance of EndPoint appears simultaneously in three sorted linked
lists, one for each dimension, as required by the sweep and prune algorithm.
A "min" and the correspoding "max" end-point give us three intervals, one
for each axis, which are actually the projections of the AABB on each of the
three co-ordinate axii. */
enum MinMax { MIN=1, MAX=2 };
struct SrBvNBody::EndPoint
{ char minmax; //whether it represents a "MIN" or a "MAX" end-point
srbvreal val[3]; //the coordinates of the EndPoint.
EndPoint* prev[3]; //for maintaining the three linked
EndPoint* next[3]; //lists.
AABB *aabb; //back pointer to the parent AABB.
};
//============================== SrBvNBody::AABB ====================================
/* Class AABB is used to store information about an axis aligned bounding box.
The AABB here has the same "radius" along all the three axii. As a result
of this, even if the object rotates about some arbitrary point and axis,
the radius of the enclosing AABB need not be changed.
Computing the center and radius of the AABBs:
Let P1, P2, ... , Pn be the position vectors of the end-points of the given
object. Then, the center of the enclosing AABB is the centroid of the object:
center = (P1 + P2 + ... + Pn) / n
The radius is given by: radius = max_i (dist(Pi, center)) */
struct SrBvNBody::AABB
{ int id; // the id of the enclosed object
srbvreal center[3]; // center of the AABB
srbvreal radius; // radius of the AABB
EndPoint *lo; // the (min_x, min_y, min_z) and
EndPoint *hi; // (max_x, max_y, max_z) corners of the AABB. These point to
// the corresponding nodes in the NBody linked lists.
AABB ( EndPoint* l, EndPoint* h )
{ lo = l;
lo->minmax = MIN;
lo->aabb = this;
hi = h;
hi->minmax = MAX;
hi->aabb = this;
}
AABB ()
{ lo = new EndPoint;
lo->minmax = MIN;
lo->aabb = this;
hi = new EndPoint;
hi->minmax = MAX;
hi->aabb = this;
}
~AABB ()
{ delete lo;
delete hi;
}
/* min/max holds the min/max endpoints of the box, but considering the
maximum radius as the size in all directions; this allows rotation
of the models w/o the need to recompute the boxes */
void compute_min_max ()
{ lo->val[0] = center[0] - radius;
lo->val[1] = center[1] - radius;
lo->val[2] = center[2] - radius;
hi->val[0] = center[0] + radius;
hi->val[1] = center[1] + radius;
hi->val[2] = center[2] + radius;
}
friend bool overlaps ( AABB* obj1, AABB* obj2 )//check it the two AABBs overlap
{
int coord;
for (coord=0; coord<3; coord++)
{ if (obj1->lo->val[coord] < obj2->lo->val[coord])
{ if (obj2->lo->val[coord] > obj1->hi->val[coord]) return false; }
else
{ if (obj1->lo->val[coord] > obj2->hi->val[coord]) return false; }
}
return true;
}
};
//============================== SrBvNBody ====================================
const int REVERSE = 1; //whether the direction of movement of the interval
const int FORWARD = 2; //along the list is forward (FORWARD), reverse (REVERSE)
const int NOCHANGE = 3; //or there is no movement at all (NOCHANGE).
SrBvNBody::SrBvNBody() //constructor.
{
_elist[0] = NULL;
_elist[1] = NULL;
_elist[2] = NULL;
}
SrBvNBody::~SrBvNBody() //destructor.
{
init ();
}
void SrBvNBody::init()
{
while ( _aabb.size()>0 )
{ delete _aabb.pop();
}
_elist[0] = NULL;
_elist[1] = NULL;
_elist[2] = NULL;
overlapping_pairs.init ();
}
bool SrBvNBody::insert_object ( int id, const SrModel& m )
{
SR_TRACE1 ( "insert_object" );
// Increase the dynamic array if necessary, and store the new aabb
while ( id>=_aabb.size() ) _aabb.push()=0;
if ( _aabb[id] ) return false; // error: id already exists, so just return
AABB* aabb = new AABB;
_aabb[id] = aabb;
// Set the id to the given value
aabb->id = id;
// Get the center of the AABB, instead of the centroid as vcollide does
SrBox box;
m.get_bounding_box(box);
SrPnt boxc = box.center();
aabb->center[0] = boxc.x;
aabb->center[1] = boxc.y;
aabb->center[2] = boxc.z;
// The "radius" of the AABB is computed as the maximum distance of the AABB
// center from any of the vertices of the object.
float dist2, distmax=0;
int i, mvsize=m.V.size();
for ( i=0; i<mvsize; i++ )
{ dist2 = ::dist2 ( boxc, m.V[i] );
if ( dist2>distmax ) distmax=dist2;
}
aabb->radius = (srbvreal) sqrt(double(distmax));
aabb->radius *= (srbvreal)1.0001; //add a 0.01% buffer.
aabb->compute_min_max ();
// Now, check the overlap of this AABB with with all other AABBs and
// add the pair to the set of overlapping pairs if reqd.
int size = _aabb.size();
for ( i=0; i<size; i++ )
{ if ( _aabb[i] )
if ( overlaps(aabb,_aabb[i]) )
_add_pair ( aabb->id, i );
}
// Now, for each of the three co-ordinates, insert the interval
// in the correspoding list.
int coord;
for ( coord=0; coord<3; coord++ )
{ //first insert the "hi" endpoint.
if ( _elist[coord]==NULL ) // if the list is empty, insert in front.
{ _elist[coord] = aabb->hi;
aabb->hi->prev[coord] = aabb->hi->next[coord] = NULL;
}
else //otherwise insert in the correct location (the list is sorted)
{ _list_insert ( coord, aabb->hi ); }
// Now, insert the "lo" endpoint. The list cannot be empty since we
// have already inserted the "hi" endpoint
_list_insert ( coord, aabb->lo );
}
SR_TRACE1 ( "insert_object end" );
return true;
}
bool SrBvNBody::update_transformation ( int id, const SrMat& m )
{
SR_TRACE1 ( "update_transformation" );
if ( id>=_aabb.size() ) return false;
if ( !_aabb[id] ) return false;
AABB *aabb = _aabb[id]; // the given object exists
// compute the new position of the AABB center
SrPnt c ( aabb->center );
srbvvec new_center;
new_center[0] = m.get(0)*c.x + m.get(4)*c.y + m.get(8)*c.z + m.get(12);
new_center[1] = m.get(1)*c.x + m.get(5)*c.y + m.get(9)*c.z + m.get(13);
new_center[2] = m.get(2)*c.x + m.get(6)*c.y + m.get(10)*c.z + m.get(14);
// vcollide version:
//double new_center[3]; // multiply: [trans] * [center]
//new_center[0] = aabb->center[0] * trans[0][0] + aabb->center[1] * trans[0][1] + aabb->center[2] * trans[0][2] + trans[0][3];
//new_center[1] = aabb->center[0] * trans[1][0] + aabb->center[1] * trans[1][1] + aabb->center[2] * trans[1][2] + trans[1][3];
//new_center[2] = aabb->center[0] * trans[2][0] + aabb->center[1] * trans[2][1] + aabb->center[2] * trans[2][2] + trans[2][3];
// compute the new min and max endpoints
// (aabb min/max values are updated later on)
srbvreal min[3], max[3];
min[0] = new_center[0] - aabb->radius;
min[1] = new_center[1] - aabb->radius;
min[2] = new_center[2] - aabb->radius;
max[0] = new_center[0] + aabb->radius;
max[1] = new_center[1] + aabb->radius;
max[2] = new_center[2] + aabb->radius;
// we need these so that we can use the same function overlaps(AABB *, AABB *)
// to check overlap of the newly transformed object with other objects
EndPoint lo, hi;
AABB dummy(&lo,&hi);
lo.val[0]=min[0]; lo.val[1]=min[1]; lo.val[2]=min[2];
hi.val[0]=max[0]; hi.val[1]=max[1]; hi.val[2]=max[2];
//update all the three lists by moving the endpoint to correct position.
EndPoint *temp;
int direction;
int coord;
for ( coord=0; coord<3; coord++ )
{ // set the direction of motion of the endpoint along the list
if ( aabb->lo->val[coord] > min[coord] )
direction = REVERSE;
else if ( aabb->lo->val[coord] < min[coord] )
direction = FORWARD;
else
direction = NOCHANGE;
if ( direction==REVERSE ) // backward motion
{ // first update the "lo" endpoint of the interval
if ( aabb->lo->prev[coord]!=NULL )
{ temp = aabb->lo;
while ( (temp != NULL) && (temp->val[coord] > min[coord]))
{ if (temp->minmax == MAX)
if (overlaps(temp->aabb, &dummy))
_add_pair(temp->aabb->id, aabb->id);
temp = temp->prev[coord];
}
if (temp == NULL)
{ aabb->lo->prev[coord]->next[coord] = aabb->lo->next[coord];
aabb->lo->next[coord]->prev[coord] = aabb->lo->prev[coord];
aabb->lo->prev[coord] = NULL;
aabb->lo->next[coord] = _elist[coord];
_elist[coord]->prev[coord] = aabb->lo;
_elist[coord] = aabb->lo;
}
else
{ aabb->lo->prev[coord]->next[coord] = aabb->lo->next[coord];
aabb->lo->next[coord]->prev[coord] = aabb->lo->prev[coord];
aabb->lo->prev[coord] = temp;
aabb->lo->next[coord] = temp->next[coord];
temp->next[coord]->prev[coord] = aabb->lo;
temp->next[coord] = aabb->lo;
}
}
aabb->lo->val[coord] = min[coord];
//then update the "hi" endpoint of the interval.
if (aabb->hi->val[coord] != max[coord])
{ temp = aabb->hi;
while (temp->val[coord] > max[coord])
{ if ( (temp->minmax == MIN) && (overlaps(temp->aabb, aabb)) )
_del_pair(temp->aabb->id, aabb->id);
temp = temp->prev[coord];
}
aabb->hi->prev[coord]->next[coord] = aabb->hi->next[coord];
if (aabb->hi->next[coord] != NULL)
aabb->hi->next[coord]->prev[coord] = aabb->hi->prev[coord];
aabb->hi->prev[coord] = temp;
aabb->hi->next[coord] = temp->next[coord];
if (temp->next[coord] != NULL)
temp->next[coord]->prev[coord] = aabb->hi;
temp->next[coord] = aabb->hi;
aabb->hi->val[coord] = max[coord];
}
}
else if (direction == FORWARD) // forward motion
{ // here, we first update the "hi" endpoint
if ( aabb->hi->next[coord] != NULL )
{ temp = aabb->hi;
while ( (temp->next[coord] != NULL) && (temp->val[coord] < max[coord]) )
{ if (temp->minmax == MIN)
if (overlaps(temp->aabb, &dummy))
_add_pair(temp->aabb->id, aabb->id);
temp = temp->next[coord];
}
if (temp->val[coord] < max[coord])
{ aabb->hi->prev[coord]->next[coord] = aabb->hi->next[coord];
aabb->hi->next[coord]->prev[coord] = aabb->hi->prev[coord];
aabb->hi->prev[coord] = temp;
aabb->hi->next[coord] = NULL;
temp->next[coord] = aabb->hi;
}
else if (aabb->hi->val[coord] != max[coord])
{ aabb->hi->prev[coord]->next[coord] = aabb->hi->next[coord];
aabb->hi->next[coord]->prev[coord] = aabb->hi->prev[coord];
aabb->hi->prev[coord] = temp->prev[coord];
aabb->hi->next[coord] = temp;
temp->prev[coord]->next[coord] = aabb->hi;
temp->prev[coord] = aabb->hi;
}
}
aabb->hi->val[coord] = max[coord];
//then, update the "lo" endpoint of the interval.
temp = aabb->lo;
while (temp->val[coord] < min[coord])
{ if ( (temp->minmax == MAX) && (overlaps(temp->aabb, aabb)) )
_del_pair(temp->aabb->id, aabb->id);
temp = temp->next[coord];
}
if (aabb->lo->prev[coord] != NULL)
{ aabb->lo->prev[coord]->next[coord] = aabb->lo->next[coord]; }
else
{ _elist[coord] = aabb->lo->next[coord]; }
aabb->lo->next[coord]->prev[coord] = aabb->lo->prev[coord];
aabb->lo->prev[coord] = temp->prev[coord];
aabb->lo->next[coord] = temp;
if (temp->prev[coord] != NULL)
{ temp->prev[coord]->next[coord] = aabb->lo; }
else
{ _elist[coord] = aabb->lo; }
temp->prev[coord] = aabb->lo;
aabb->lo->val[coord] = min[coord];
}
}
// make sure AABB destructor does not delete the used static vars:
dummy.lo = 0;
dummy.hi = 0;
SR_TRACE1 ( "update_transformation end" );
return true;
}
bool SrBvNBody::remove_object ( int id )
{
SR_TRACE1 ( "remove_object" );
if ( id>=_aabb.size() ) return false;
if ( !_aabb[id] ) return false;
// 1st get the AABB to be deleted
AABB *aabb = _aabb[id];
// Now "remove it" from the AABB array
if ( id+1==_aabb.size() )
_aabb.pop();
else
_aabb[id] = NULL;
// Now we delete all the three intervals from the corresponding lists
int coord;
for ( coord=0; coord<3; coord++ )
{ // first delete the "lo" endpoint of the interval
if ( aabb->lo->prev[coord]==NULL )
_elist[coord] = aabb->lo->next[coord];
else
aabb->lo->prev[coord]->next[coord] = aabb->lo->next[coord];
aabb->lo->next[coord]->prev[coord] = aabb->lo->prev[coord];
// then, delete the "hi" endpoint
if ( aabb->hi->prev[coord]==NULL )
_elist[coord] = aabb->hi->next[coord];
else
aabb->hi->prev[coord]->next[coord] = aabb->hi->next[coord];
if ( aabb->hi->next[coord]!=NULL )
aabb->hi->next[coord]->prev[coord] = aabb->hi->prev[coord];
}
// Delete all entries involving this id from the set of overlapping pairs
overlapping_pairs.del_pairs_with_id ( id );
// de-allocate the memory
delete aabb;
SR_TRACE1 ( "remove_object end" );
return true;
}
//=========================== private methods ===================================
void SrBvNBody::_list_insert ( int coord, EndPoint* newpt )
{
// Find the correct location in the list and insert there (the list is sorted)
EndPoint* current = _elist[coord];
while ( current->next[coord] && current->val[coord]<newpt->val[coord] )
current = current->next[coord];
if ( current->val[coord]>=newpt->val[coord] ) // insert before
{ newpt->prev[coord] = current->prev[coord];
newpt->next[coord] = current;
if ( !current->prev[coord] )
_elist[coord] = newpt;
else
current->prev[coord]->next[coord] = newpt;
current->prev[coord] = newpt;
}
else // insert at the end
{ newpt->prev[coord] = current;
newpt->next[coord] = NULL;
current->next[coord] = newpt;
}
}
/************************************************************************\
Copyright 1997 The University of North Carolina at Chapel Hill.
All Rights Reserved.
Permission to use, copy, modify and distribute this software
and its documentation for educational, research and non-profit
purposes, without fee, and without a written agreement is
hereby granted, provided that the above copyright notice and
the following three paragraphs appear in all copies.
IN NO EVENT SHALL THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL
HILL BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
EVEN IF THE UNIVERSITY OF NORTH CAROLINA HAVE BEEN ADVISED OF
THE POSSIBILITY OF SUCH DAMAGES.
Permission to use, copy, modify and distribute this software
and its documentation for educational, research and non-profit
purposes, without fee, and without a written agreement is
hereby granted, provided that the above copyright notice and
the following three paragraphs appear in all copies.
THE UNIVERSITY OF NORTH CAROLINA SPECIFICALLY DISCLAIM ANY
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
BASIS, AND THE UNIVERSITY OF NORTH CAROLINA HAS NO OBLIGATION
TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS.
---------------------------------
|Please send all BUG REPORTS to: |
| |
| geom@cs.unc.edu |
| |
---------------------------------
The authors may be contacted via:
US Mail: A. Pattekar/J. Cohen/T. Hudson/S. Gottschalk/M. Lin/D. Manocha
Department of Computer Science
Sitterson Hall, CB #3175
University of N. Carolina
Chapel Hill, NC 27599-3175
Phone: (919)962-1749
EMail: geom@cs.unc.edu
\************************************************************************/

View File

@ -0,0 +1,65 @@
/** \file sr_bv_nbody.h
* multibody AABB seep and prune */
# ifndef SR_BV_NBODY_H
# define SR_BV_NBODY_H
# include "sr_bv_math.h"
# include "sr_bv_id_pairs.h"
class SrModel;
class SrMat;
/*! Maintains a set of three linked lists and keeps updating them using the
sweep and prune algorithm. At any point in execution, the class maintains
a set of pairs of objects whose AABBs overlap. In general, this set would
be very small compared to the n(n-1)/2 possible pairs.
Adapted from the VCollide package. Their copyright is in the source file. */
class SrBvNBody
{ private:
struct AABB;
struct EndPoint;
EndPoint *_elist[3]; //Pointers to the three linked lists, one per axis
SrArray<AABB*> _aabb; //Element "i" points to the AABB of the object with id=i. If
// this object doesn't exist, then the pointer is NULL.
public:
/*! at any instance during execution, overlapping_pairs contains the set
of pairs of ids whose AABBs overlap. */
SrBvIdPairs overlapping_pairs;
public:
/*! Constructor */
SrBvNBody();
/*! Destructor (calls init()) */
~SrBvNBody();
/*! Clears everything */
void init ();
/*! Add an object with the given id to the NBody data structure.
If the id already exists, nothing is done and false is returned.
Otherwise, the object is added and true is returned.
Note: to change a model associated to an existing id, delete_object()
must be called prior to add_object() */
bool insert_object ( int id, const SrModel& m );
/*! Update position and orientation of the associated object, and
the 3 per-axis linked lists maintaining the ordered AABB endpoints.
False is returned if the id is invalid; true is returned otherwise. */
bool update_transformation ( int id, const SrMat& m );
/*! Delete the object from the data structure. Returns true if the
object could be deleted, and false otherwise. False may only
occur if the id is invalid or represents an already deleted object */
bool remove_object ( int id );
private:
void _list_insert ( int coord, EndPoint* newpt );
void _add_pair(int id1, int id2)
{ if (id1 != id2) overlapping_pairs.add_pair(id1, id2); }
void _del_pair(int id1, int id2)
{ if (id1 != id2) overlapping_pairs.del_pair(id1, id2); }
};
#endif // SR_BV_NBODY_H

View File

@ -0,0 +1,331 @@
#include "precompiled.h"/* Note: this code was adapted from the PQP library;
their copyright notice can be found at the end of this file. */
# include "sr_bv_tree.h"
# include "sr_model.h"
//====================================
//======= SrBvTree::SrBvTree =========
//====================================
SrBvTree::SrBvTree ()
{
_last_tri=0;
}
//=====================================
//======= SrBvTree::~SrBvTree =========
//=====================================
SrBvTree::~SrBvTree ()
{
}
//================================
//======= SrBvTree::init =========
//================================
void SrBvTree::init ()
{
_tris.size(0); _tris.compress();
_bvs.size(0); _bvs.compress();
_last_tri=0;
}
//====================================
//======= SrBvTree::make =========
//====================================
void SrBvTree::make ( const SrModel& m )
{
using namespace SrBvMath;
if ( m.F.size()==0 ) { init(); return; }
// get the geometry of the model:
_tris.size ( m.F.size() );
int i;
for ( i=0; i<_tris.size(); i++ )
{ Vset ( _tris[i].p1, m.V[m.F[i].a] );
Vset ( _tris[i].p2, m.V[m.F[i].b] );
Vset ( _tris[i].p3, m.V[m.F[i].c] );
_tris[i].id = i;
}
_last_tri = &_tris[0];
// create an array of BVs for the model:
_bvs.ensure_capacity ( 2*_tris.size()-1 );
_bvs.size ( 1 );
// build recursively the tree:
_build_recurse ( 0/*node*/, 0/*first*/, _tris.size()/*num*/ );
// change BV orientations from world-relative to parent-relative:
srbvmat R; srbvvec T;
Midentity(R);
Videntity(T);
_make_parent_relative(0,R,T,T);
// make sure the sizes were well defined:
_tris.compress();
_bvs.compress();
}
static void get_centroid_triverts ( srbvvec c, SrBvTri *tris, int num_tris )
{
int i;
c[0] = c[1] = c[2] = 0.0;
// get center of mass
for(i=0; i<num_tris; i++)
{
srbvreal *p1 = tris[i].p1;
srbvreal *p2 = tris[i].p2;
srbvreal *p3 = tris[i].p3;
c[0] += p1[0] + p2[0] + p3[0];
c[1] += p1[1] + p2[1] + p3[1];
c[2] += p1[2] + p2[2] + p3[2];
}
srbvreal n = (srbvreal)(3 * num_tris);
c[0] /= n;
c[1] /= n;
c[2] /= n;
}
static void get_covariance_triverts ( srbvmat M, SrBvTri *tris, int num_tris )
{
int i;
srbvreal S1[3];
srbvreal S2[3][3];
S1[0] = S1[1] = S1[2] = 0.0;
S2[0][0] = S2[1][0] = S2[2][0] = 0.0;
S2[0][1] = S2[1][1] = S2[2][1] = 0.0;
S2[0][2] = S2[1][2] = S2[2][2] = 0.0;
// get center of mass
for(i=0; i<num_tris; i++)
{
srbvreal *p1 = tris[i].p1;
srbvreal *p2 = tris[i].p2;
srbvreal *p3 = tris[i].p3;
S1[0] += p1[0] + p2[0] + p3[0];
S1[1] += p1[1] + p2[1] + p3[1];
S1[2] += p1[2] + p2[2] + p3[2];
S2[0][0] += (p1[0] * p1[0] +
p2[0] * p2[0] +
p3[0] * p3[0]);
S2[1][1] += (p1[1] * p1[1] +
p2[1] * p2[1] +
p3[1] * p3[1]);
S2[2][2] += (p1[2] * p1[2] +
p2[2] * p2[2] +
p3[2] * p3[2]);
S2[0][1] += (p1[0] * p1[1] +
p2[0] * p2[1] +
p3[0] * p3[1]);
S2[0][2] += (p1[0] * p1[2] +
p2[0] * p2[2] +
p3[0] * p3[2]);
S2[1][2] += (p1[1] * p1[2] +
p2[1] * p2[2] +
p3[1] * p3[2]);
}
srbvreal n = (srbvreal)(3 * num_tris);
// now get covariances
M[0][0] = S2[0][0] - S1[0]*S1[0] / n;
M[1][1] = S2[1][1] - S1[1]*S1[1] / n;
M[2][2] = S2[2][2] - S1[2]*S1[2] / n;
M[0][1] = S2[0][1] - S1[0]*S1[1] / n;
M[1][2] = S2[1][2] - S1[1]*S1[2] / n;
M[0][2] = S2[0][2] - S1[0]*S1[2] / n;
M[1][0] = M[0][1];
M[2][0] = M[0][2];
M[2][1] = M[1][2];
}
// given a list of triangles, a splitting axis, and a coordinate on
// that axis, partition the triangles into two groups according to
// where their centroids fall on the axis (under axial projection).
// Returns the number of tris in the first half
static int split_tris ( SrBvTri* tris, int num_tris, srbvvec a, srbvreal c )
{
int i;
int c1 = 0;
srbvvec p;
srbvreal x;
SrBvTri temp;
using namespace SrBvMath;
for(i = 0; i < num_tris; i++)
{
// loop invariant: up to (but not including) index c1 in group 1,
// then up to (but not including) index i in group 2
//
// [1] [1] [1] [1] [2] [2] [2] [x] [x] ... [x]
// c1 i
//
VcV(p, tris[i].p1);
VpV(p, p, tris[i].p2);
VpV(p, p, tris[i].p3);
x = VdotV(p, a);
x /= 3.0;
if (x <= c)
{
// group 1
temp = tris[i];
tris[i] = tris[c1];
tris[c1] = temp;
c1++;
}
else
{
// group 2 -- do nothing
}
}
// split arbitrarily if one group empty
if ((c1 == 0) || (c1 == num_tris)) c1 = num_tris/2;
return c1;
}
// Fits m->child(bn) to the num_tris triangles starting at first_tri
// Then, if num_tris is greater than one, partitions the tris into two
// sets, and recursively builds two children of m->child(bn)
void SrBvTree::_build_recurse ( int bn, int first_tri, int num_tris )
{
using namespace SrBvMath;
SrBv* b = child ( bn );
// compute a rotation matrix
srbvreal C[3][3], E[3][3], R[3][3], s[3], axis[3], mean[3], coord;
get_covariance_triverts(C,&_tris[first_tri],num_tris);
Meigen(E, s, C);
// place axes of E in order of increasing s
int min, mid, max;
if (s[0] > s[1]) { max = 0; min = 1; }
else { min = 0; max = 1; }
if (s[2] < s[min]) { mid = min; min = 2; }
else if (s[2] > s[max]) { mid = max; max = 2; }
else { mid = 2; }
McolcMcol(R,0,E,max);
McolcMcol(R,1,E,mid);
R[0][2] = E[1][max]*E[2][mid] - E[1][mid]*E[2][max];
R[1][2] = E[0][mid]*E[2][max] - E[0][max]*E[2][mid];
R[2][2] = E[0][max]*E[1][mid] - E[0][mid]*E[1][max];
// fit the BV
b->fit ( R, &_tris[first_tri], num_tris );
if (num_tris == 1)
{
// BV is a leaf BV - first_child will index a triangle
b->first_child = -(first_tri + 1);
}
else if (num_tris > 1)
{
// BV not a leaf - first_child will index a BV
b->first_child = _bvs.size();
_bvs.push();
_bvs.push();
// choose splitting axis and splitting coord
McolcV(axis,R,0);
get_centroid_triverts(mean,&_tris[first_tri],num_tris);
coord = VdotV(axis, mean);
// now split
int num_first_half = split_tris(&_tris[first_tri], num_tris,
axis, coord);
// recursively build the children
_build_recurse( child(bn)->first_child, first_tri, num_first_half);
_build_recurse( child(bn)->first_child + 1,
first_tri + num_first_half, num_tris - num_first_half);
}
}
// this descends the hierarchy, converting world-relative
// transforms to parent-relative transforms
void SrBvTree::_make_parent_relative ( int bn,
const srbvmat parentR,
const srbvvec parentTr,
const srbvvec parentTo )
{
srbvmat Rpc;
srbvvec Tpc;
if ( !child(bn)->leaf() )
{ // make children parent-relative
_make_parent_relative ( child(bn)->first_child,
child(bn)->R,
child(bn)->Tr,
child(bn)->To
);
_make_parent_relative ( child(bn)->first_child+1,
child(bn)->R,
child(bn)->Tr,
child(bn)->To
);
}
// make self parent relative
using namespace SrBvMath;
MTxM(Rpc,parentR,child(bn)->R);
McM(child(bn)->R,Rpc);
VmV(Tpc,child(bn)->Tr,parentTr);
MTxV(child(bn)->Tr,parentR,Tpc);
VmV(Tpc,child(bn)->To,parentTo);
MTxV(child(bn)->To,parentR,Tpc);
}
/*************************************************************************\
Copyright 1999 The University of North Carolina at Chapel Hill.
All Rights Reserved.
Permission to use, copy, modify and distribute this software and its
documentation for educational, research and non-profit purposes, without
fee, and without a written agreement is hereby granted, provided that the
above copyright notice and the following three paragraphs appear in all
copies.
IN NO EVENT SHALL THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL BE
LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE
USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
OF NORTH CAROLINA HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
THE UNIVERSITY OF NORTH CAROLINA SPECIFICALLY DISCLAIM ANY
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
NORTH CAROLINA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
The authors may be contacted via:
US Mail: S. Gottschalk, E. Larsen
Department of Computer Science
Sitterson Hall, CB #3175
University of N. Carolina
Chapel Hill, NC 27599-3175
Phone: (919)962-1749
EMail: geom@cs.unc.edu
\**************************************************************************/

View File

@ -0,0 +1,87 @@
/** \file sr_bv_tree.h
* bounding volume tree */
# ifndef SR_BV_TREE_H
# define SR_BV_TREE_H
# include "sr_array.h"
# include "sr_bv_math.h"
# include "sr_class_manager.h"
class SrModel;
class SrBvTreeQuery;
/*! Encapsulates the coordinates of a triangle. */
struct SrBvTri
{ srbvvec p1, p2, p3; // the three coordinates
int id; // the id of the face of the original SrModel
};
/*! Encapsulates a bounding volume node.
Adapted from PQP, see the copyright notice in the source file. */
struct SrBv
{ srbvmat R; // orientation of RSS & OBB
srbvvec Tr; // RSS: position of rectangle
srbvreal l[2]; // RSS: side lengths of rectangle
srbvreal r; // RSS: radius of sphere summed with rectangle to form RSS
srbvvec To; // OBB: position of obb
srbvvec d; // OBB: (half) dimensions of obb
int first_child; // positive value is index of first_child bv
// negative value is -(index + 1) of triangle
SrBv() { first_child=0; }
bool leaf() const { return first_child<0? true:false; }
srbvreal size() const;
void fit ( srbvmat O, SrBvTri* tris, int num_tris );
static bool overlap ( srbvmat R, srbvvec T, const SrBv* b1, const SrBv* b2 );
static srbvreal distance ( srbvmat R, srbvvec T, const SrBv* b1, const SrBv* b2 );
};
/*! \class SrBvTree sr_bv_tree.h
\brief bounding volume tree
Manages a hierarchy of bounding volumes, both OBB and RSS are
maintained. OBBs are used for collision detection and RSSs are
used for distance computation.
Adapted from PQP, see the copyright notice in the source file. */
class SrBvTree
{ private:
SrArray<SrBvTri> _tris;
SrArray<SrBv> _bvs;
SrBvTri* _last_tri; // closest tri on this model in last distance test
friend class SrBvTreeQuery;
public :
/*! Constructor */
SrBvTree ();
/*! Destructor */
~SrBvTree ();
/*! Returns the number of bounding volumes in the current tree */
int nodes () const { return _bvs.size(); }
/*! Returns the triangle with index i */
const SrBvTri* tri ( int i ) const { return &_tris[i]; }
/*! Returns a const copy of the list of triangles */
const SrArray<SrBvTri>& tris () const { return _tris; }
/*! Returns the bounding volume node with index i */
SrBv* child ( int i ) { return &_bvs[i]; }
const SrBv* const_child ( int i ) const { return &_bvs[i]; }
/*! Empty the tree and frees all used memory */
void init ();
/*! Constructs the tree for the given model */
void make ( const SrModel& m );
private :
void _build_recurse ( int bn, int first_tri, int num_tris );
void _make_parent_relative ( int bn, const srbvmat parentR,
const srbvvec parentTr,
const srbvvec parentTo );
};
//============================== end of file ===============================
# endif // SR_BV_TREE_H

View File

@ -0,0 +1,839 @@
#include "precompiled.h"/* Note: this code was adapted from the PQP library;
their copyright notice can be found at the end of this file. */
# include "sr_bv_tree_query.h"
//====================== SrBvTreeQuery ======================
SrBvTreeQuery::SrBvTreeQuery ()
{
init ();
}
void SrBvTreeQuery::init ()
{
_num_bv_tests = 0;
_num_tri_tests = 0;
_pairs.size(0);
_pairs.compress();
_dist = 0;
_rel_err = _abs_err = 0;
_closer_than_tolerance = false;
_toler = 0;
}
inline void setmv ( srbvmat m, srbvvec v, const SrMat& srm )
{
# define SRM(i) (srbvreal)(srm[i])
// here we pass from column-major to line-major order:
m[0][0]=SRM(0); m[1][0]=SRM(1); m[2][0]=SRM(2);
m[0][1]=SRM(4); m[1][1]=SRM(5); m[2][1]=SRM(6);
m[0][2]=SRM(8); m[1][2]=SRM(9); m[2][2]=SRM(10);
v[0]=SRM(12); v[1]=SRM(13); v[2]=SRM(14);
# undef SRM
}
bool SrBvTreeQuery::collide ( const SrMat& m1, const SrBvTree* t1,
const SrMat& m2, const SrBvTree* t2 )
{
srbvmat R1, R2;
srbvvec T1, T2;
setmv ( R1, T1, m1 );
setmv ( R2, T2, m2 );
collide ( R1, T1, t1, R2, T2, t2, CollideFirstContact );
return _pairs.size()>0? true:false;
}
int SrBvTreeQuery::collide_all ( const SrMat& m1, const SrBvTree* t1,
const SrMat& m2, const SrBvTree* t2 )
{
srbvmat R1, R2;
srbvvec T1, T2;
setmv ( R1, T1, m1 );
setmv ( R2, T2, m2 );
collide ( R1, T1, t1, R2, T2, t2, CollideAllContacts );
return _pairs.size();
}
float SrBvTreeQuery::distance ( const SrMat& m1, SrBvTree* t1,
const SrMat& m2, SrBvTree* t2,
float rel_err, float abs_err )
{
srbvmat R1, R2;
srbvvec T1, T2;
setmv ( R1, T1, m1 );
setmv ( R2, T2, m2 );
_distance ( R1, T1, t1, R2, T2, t2, rel_err, abs_err );
_srp1.set ( _p1 );
_srp2.set ( _p2 );
return distance();
}
bool SrBvTreeQuery::tolerance ( const SrMat& m1, SrBvTree* t1,
const SrMat& m2, SrBvTree* t2,
float tolerance )
{
srbvmat R1, R2;
srbvvec T1, T2;
setmv ( R1, T1, m1 );
setmv ( R2, T2, m2 );
return SrBvTreeQuery::tolerance ( R1, T1, t1, R2, T2, t2, tolerance );
}
float SrBvTreeQuery::greedy_distance ( const SrMat& m1, SrBvTree* t1,
const SrMat& m2, SrBvTree* t2, float delta )
{
srbvmat R1, R2;
srbvvec T1, T2;
setmv ( R1, T1, m1 );
setmv ( R2, T2, m2 );
return SrBvTreeQuery::greedy_distance ( R1, T1, t1, R2, T2, t2, delta );
}
//====================== static functions ======================
inline srbvreal max ( srbvreal a, srbvreal b, srbvreal c )
{
srbvreal t = a;
if (b > t) t = b;
if (c > t) t = c;
return t;
}
inline srbvreal min ( srbvreal a, srbvreal b, srbvreal c )
{
srbvreal t = a;
if (b < t) t = b;
if (c < t) t = c;
return t;
}
static int project6 ( srbvreal *ax,
srbvreal *p1, srbvreal *p2, srbvreal *p3,
srbvreal *q1, srbvreal *q2, srbvreal *q3 )
{
using namespace SrBvMath;
srbvreal P1 = VdotV(ax, p1);
srbvreal P2 = VdotV(ax, p2);
srbvreal P3 = VdotV(ax, p3);
srbvreal Q1 = VdotV(ax, q1);
srbvreal Q2 = VdotV(ax, q2);
srbvreal Q3 = VdotV(ax, q3);
srbvreal mx1 = max(P1, P2, P3);
srbvreal mn1 = min(P1, P2, P3);
srbvreal mx2 = max(Q1, Q2, Q3);
srbvreal mn2 = min(Q1, Q2, Q3);
if (mn1 > mx2) return 0;
if (mn2 > mx1) return 0;
return 1;
}
// very robust triangle intersection test
// uses no divisions
// works on coplanar triangles
static int TriContact(
const srbvreal *P1, const srbvreal *P2, const srbvreal *P3,
const srbvreal *Q1, const srbvreal *Q2, const srbvreal *Q3 )
{
// One triangle is (p1,p2,p3). Other is (q1,q2,q3).
// Edges are (e1,e2,e3) and (f1,f2,f3).
// Normals are n1 and m1
// Outwards are (g1,g2,g3) and (h1,h2,h3).
//
// We assume that the triangle vertices are in the same coordinate system.
//
// First thing we do is establish a new c.s. so that p1 is at (0,0,0).
srbvreal p1[3], p2[3], p3[3];
srbvreal q1[3], q2[3], q3[3];
srbvreal e1[3], e2[3], e3[3];
srbvreal f1[3], f2[3], f3[3];
srbvreal g1[3], g2[3], g3[3];
srbvreal h1[3], h2[3], h3[3];
srbvreal n1[3], m1[3];
srbvreal ef11[3], ef12[3], ef13[3];
srbvreal ef21[3], ef22[3], ef23[3];
srbvreal ef31[3], ef32[3], ef33[3];
p1[0] = P1[0] - P1[0]; p1[1] = P1[1] - P1[1]; p1[2] = P1[2] - P1[2];
p2[0] = P2[0] - P1[0]; p2[1] = P2[1] - P1[1]; p2[2] = P2[2] - P1[2];
p3[0] = P3[0] - P1[0]; p3[1] = P3[1] - P1[1]; p3[2] = P3[2] - P1[2];
q1[0] = Q1[0] - P1[0]; q1[1] = Q1[1] - P1[1]; q1[2] = Q1[2] - P1[2];
q2[0] = Q2[0] - P1[0]; q2[1] = Q2[1] - P1[1]; q2[2] = Q2[2] - P1[2];
q3[0] = Q3[0] - P1[0]; q3[1] = Q3[1] - P1[1]; q3[2] = Q3[2] - P1[2];
e1[0] = p2[0] - p1[0]; e1[1] = p2[1] - p1[1]; e1[2] = p2[2] - p1[2];
e2[0] = p3[0] - p2[0]; e2[1] = p3[1] - p2[1]; e2[2] = p3[2] - p2[2];
e3[0] = p1[0] - p3[0]; e3[1] = p1[1] - p3[1]; e3[2] = p1[2] - p3[2];
f1[0] = q2[0] - q1[0]; f1[1] = q2[1] - q1[1]; f1[2] = q2[2] - q1[2];
f2[0] = q3[0] - q2[0]; f2[1] = q3[1] - q2[1]; f2[2] = q3[2] - q2[2];
f3[0] = q1[0] - q3[0]; f3[1] = q1[1] - q3[1]; f3[2] = q1[2] - q3[2];
using namespace SrBvMath;
VcrossV(n1, e1, e2);
VcrossV(m1, f1, f2);
VcrossV(g1, e1, n1);
VcrossV(g2, e2, n1);
VcrossV(g3, e3, n1);
VcrossV(h1, f1, m1);
VcrossV(h2, f2, m1);
VcrossV(h3, f3, m1);
VcrossV(ef11, e1, f1);
VcrossV(ef12, e1, f2);
VcrossV(ef13, e1, f3);
VcrossV(ef21, e2, f1);
VcrossV(ef22, e2, f2);
VcrossV(ef23, e2, f3);
VcrossV(ef31, e3, f1);
VcrossV(ef32, e3, f2);
VcrossV(ef33, e3, f3);
// now begin the series of tests
if (!project6(n1, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(m1, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(ef11, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(ef12, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(ef13, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(ef21, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(ef22, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(ef23, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(ef31, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(ef32, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(ef33, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(g1, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(g2, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(g3, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(h1, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(h2, p1, p2, p3, q1, q2, q3)) return 0;
if (!project6(h3, p1, p2, p3, q1, q2, q3)) return 0;
return 1;
}
/**************************/
/******* COLLIDE **********/
/**************************/
void SrBvTreeQuery::collide (
srbvmat R1, srbvvec T1, const SrBvTree* o1,
srbvmat R2, srbvvec T2, const SrBvTree* o2,
CollideFlag flag )
{
// clear the data:
_num_bv_tests = 0;
_num_tri_tests = 0;
_pairs.size(0);
// make sure that the models are built:
if ( o1->nodes()==0 || o2->nodes()==0 ) return;
// Okay, compute what transform [R,T] that takes us from cs1 to cs2.
// [R,T] = [R1,T1]'[R2,T2] = [R1',-R1'T][R2,T2] = [R1'R2, R1'(T2-T1)]
// First compute the rotation part, then translation part
using namespace SrBvMath;
MTxM(_R,R1,R2);
srbvvec Ttemp;
VmV(Ttemp, T2, T1);
MTxV(_T, R1, Ttemp);
// compute the transform from o1->child(0) to o2->child(0)
srbvmat Rtemp, R;
srbvvec T;
MxM(Rtemp,_R,o2->const_child(0)->R);
MTxM(R,o1->const_child(0)->R,Rtemp);
MxVpV(Ttemp,_R,o2->const_child(0)->To,_T);
VmV(Ttemp,Ttemp,o1->const_child(0)->To);
MTxV(T,o1->const_child(0)->R,Ttemp);
// now start with both top level BVs
_collide_recurse ( R, T, o1, 0, o2, 0, flag );
}
float SrBvTreeQuery::greedy_distance ( srbvmat R1, srbvvec T1, SrBvTree* o1,
srbvmat R2, srbvvec T2, SrBvTree* o2, float delta )
{
using namespace SrBvMath;
MTxM(_R,R1,R2);
double Ttemp[3];
VmV(Ttemp, T2, T1);
MTxV(_T, R1, Ttemp);
_num_bv_tests = 0;
_num_tri_tests = 0;
srbvmat Rtemp, R;
srbvvec T;
MxM(Rtemp,_R,o2->child(0)->R);
MTxM(R,o1->child(0)->R,Rtemp);
MxVpV(Ttemp,_R,o2->child(0)->Tr,_T);
VmV(Ttemp,Ttemp,o1->child(0)->Tr);
MTxV(T,o1->child(0)->R,Ttemp);
// first, see if top-level BVs are separated
_num_bv_tests += 1;
srbvreal d = SrBv::distance(R, T, o1->child(0), o2->child(0));
// if we already found separation don't bother recursing to refine it
if ( float(d) > delta )
return (float)d;
else // if top-level BVs are not separated, recurse
return (float)_greedy_distance_recurse(R,T,o1,0,o2,0,delta);
}
//====================== private methods ======================
inline
srbvreal
TriDistance(srbvreal R[3][3], srbvreal T[3], const SrBvTri *t1, const SrBvTri *t2,
srbvreal p[3], srbvreal q[3])
{
// transform tri 2 into same space as tri 1
srbvreal tri1[3][3], tri2[3][3];
using namespace SrBvMath;
VcV(tri1[0], t1->p1);
VcV(tri1[1], t1->p2);
VcV(tri1[2], t1->p3);
MxVpV(tri2[0], R, t2->p1, T);
MxVpV(tri2[1], R, t2->p2, T);
MxVpV(tri2[2], R, t2->p3, T);
return TriDist(p,q,tri1,tri2);
}
void SrBvTreeQuery::_collide_recurse (
srbvmat R, srbvvec T, // b2 relative to b1
const SrBvTree* o1, int b1,
const SrBvTree* o2, int b2, int flag)
{
// first thing, see if we're overlapping
_num_bv_tests++;
if ( !SrBv::overlap(R,T,o1->const_child(b1), o2->const_child(b2))) return;
// if we are, see if we test triangles next
bool l1 = o1->const_child(b1)->leaf();
bool l2 = o2->const_child(b2)->leaf();
using namespace SrBvMath;
if (l1 && l2)
{
_num_tri_tests++;
// transform the points in b2 into space of b1, then compare
const SrBvTri *t1 = o1->tri(-o1->const_child(b1)->first_child - 1);
const SrBvTri *t2 = o2->tri(-o2->const_child(b2)->first_child - 1);
srbvreal q1[3], q2[3], q3[3];
const srbvreal *p1 = t1->p1;
const srbvreal *p2 = t1->p2;
const srbvreal *p3 = t1->p3;
MxVpV(q1, _R, t2->p1, _T);
MxVpV(q2, _R, t2->p2, _T);
MxVpV(q3, _R, t2->p3, _T);
if (TriContact(p1, p2, p3, q1, q2, q3))
{ // add this to result
_pairs.push() = t1->id;
_pairs.push() = t2->id;
}
return;
}
// we dont, so decide whose children to visit next
srbvreal sz1 = o1->const_child(b1)->size();
srbvreal sz2 = o2->const_child(b2)->size();
srbvreal Rc[3][3],Tc[3],Ttemp[3];
if (l2 || (!l1 && (sz1 > sz2)))
{
int c1 = o1->const_child(b1)->first_child;
int c2 = c1 + 1;
MTxM(Rc,o1->const_child(c1)->R,R);
VmV(Ttemp,T,o1->const_child(c1)->To);
MTxV(Tc,o1->const_child(c1)->R,Ttemp);
_collide_recurse(Rc,Tc,o1,c1,o2,b2,flag);
if ( flag==CollideFirstContact && _pairs.size()>0 ) return;
MTxM(Rc,o1->const_child(c2)->R,R);
VmV(Ttemp,T,o1->const_child(c2)->To);
MTxV(Tc,o1->const_child(c2)->R,Ttemp);
_collide_recurse(Rc,Tc,o1,c2,o2,b2,flag);
}
else
{
int c1 = o2->const_child(b2)->first_child;
int c2 = c1 + 1;
MxM(Rc,R,o2->const_child(c1)->R);
MxVpV(Tc,R,o2->const_child(c1)->To,T);
_collide_recurse(Rc,Tc,o1,b1,o2,c1,flag);
if ( flag==CollideFirstContact && _pairs.size()>0 ) return;
MxM(Rc,R,o2->const_child(c2)->R);
MxVpV(Tc,R,o2->const_child(c2)->To,T);
_collide_recurse(Rc,Tc,o1,b1,o2,c2,flag);
}
}
/***************************/
/******* DISTANCE **********/
/***************************/
void SrBvTreeQuery::_distance ( srbvmat R1, srbvvec T1, SrBvTree* o1,
srbvmat R2, srbvvec T2, SrBvTree* o2,
srbvreal rel_err, srbvreal abs_err )
{
// init data:
_dist = 0;
_num_bv_tests = 0;
_num_tri_tests = 0;
_abs_err = abs_err;
_rel_err = rel_err;
// make sure that the models are built:
if ( o1->nodes()==0 || o2->nodes()==0 ) return;
// Okay, compute what transform [R,T] that takes us from cs2 to cs1.
// [R,T] = [R1,T1]'[R2,T2] = [R1',-R1'T][R2,T2] = [R1'R2, R1'(T2-T1)]
// First compute the rotation part, then translation part
using namespace SrBvMath;
MTxM(_R,R1,R2);
srbvreal Ttemp[3];
VmV(Ttemp, T2, T1);
MTxV(_T, R1, Ttemp);
// establish initial upper bound using last triangles which
// provided the minimum distance
srbvvec p,q;
_dist = TriDistance(_R,_T,o1->_last_tri,o2->_last_tri,p,q);
VcV(_p1,p);
VcV(_p2,q);
// compute the transform from o1->child(0) to o2->child(0)
srbvreal Rtemp[3][3], R[3][3], T[3];
MxM(Rtemp,_R,o2->const_child(0)->R);
MTxM(R,o1->const_child(0)->R,Rtemp);
MxVpV(Ttemp,_R,o2->const_child(0)->Tr,_T);
VmV(Ttemp,Ttemp,o1->const_child(0)->Tr);
MTxV(T,o1->const_child(0)->R,Ttemp);
_distance_recurse(R,T,o1,0,o2,0);
// res->p2 is in cs 1 ; transform it to cs 2
srbvvec u;
VmV(u, _p2, _T);
MTxV(_p2, _R, u);
}
void SrBvTreeQuery::_distance_recurse (
srbvmat R, srbvvec T, // b2 relative to b1
SrBvTree* o1, int b1,
SrBvTree* o2, int b2 )
{
using namespace SrBvMath;
srbvreal sz1 = o1->const_child(b1)->size();
srbvreal sz2 = o2->const_child(b2)->size();
bool l1 = o1->const_child(b1)->leaf();
bool l2 = o2->const_child(b2)->leaf();
if ( l1 && l2 )
{ // both leaves. Test the triangles beneath them.
_num_tri_tests++;
srbvreal p[3], q[3];
SrBvTri *t1 = &o1->_tris[-o1->child(b1)->first_child - 1];
SrBvTri *t2 = &o2->_tris[-o2->child(b2)->first_child - 1];
srbvreal d = TriDistance(_R,_T,t1,t2,p,q);
if ( d<_dist )
{
_dist = d;
VcV(_p1, p); // p already in c.s. 1
VcV(_p2, q); // q must be transformed
// into c.s. 2 later
o1->_last_tri = t1;
o2->_last_tri = t2;
}
return;
}
// First, perform distance tests on the children. Then traverse
// them recursively, but test the closer pair first, the further
// pair second.
int a1,a2,c1,c2; // new bv tests 'a' and 'c'
srbvreal R1[3][3], T1[3], R2[3][3], T2[3], Ttemp[3];
if (l2 || (!l1 && (sz1 > sz2)))
{
// visit the children of b1
a1 = o1->child(b1)->first_child;
a2 = b2;
c1 = o1->child(b1)->first_child+1;
c2 = b2;
MTxM(R1,o1->const_child(a1)->R,R);
VmV(Ttemp,T,o1->const_child(a1)->Tr);
MTxV(T1,o1->const_child(a1)->R,Ttemp);
MTxM(R2,o1->const_child(c1)->R,R);
VmV(Ttemp,T,o1->const_child(c1)->Tr);
MTxV(T2,o1->const_child(c1)->R,Ttemp);
}
else
{
// visit the children of b2
a1 = b1;
a2 = o2->child(b2)->first_child;
c1 = b1;
c2 = o2->child(b2)->first_child+1;
MxM(R1,R,o2->const_child(a2)->R);
MxVpV(T1,R,o2->const_child(a2)->Tr,T);
MxM(R2,R,o2->const_child(c2)->R);
MxVpV(T2,R,o2->const_child(c2)->Tr,T);
}
_num_bv_tests += 2;
srbvreal d1 = SrBv::distance(R1, T1, o1->const_child(a1), o2->const_child(a2));
srbvreal d2 = SrBv::distance(R2, T2, o1->const_child(c1), o2->const_child(c2));
if (d2 < d1)
{
if ((d2 < (_dist - _abs_err)) ||
(d2*(1 + _rel_err) < _dist))
{
_distance_recurse(R2, T2, o1, c1, o2, c2);
}
if ((d1 < (_dist - _abs_err)) ||
(d1*(1 + _rel_err) < _dist))
{
_distance_recurse(R1, T1, o1, a1, o2, a2);
}
}
else
{
if ((d1 < (_dist - _abs_err)) ||
(d1*(1 + _rel_err) < _dist))
{
_distance_recurse(R1, T1, o1, a1, o2, a2);
}
if ((d2 < (_dist - _abs_err)) ||
(d2*(1 + _rel_err) < _dist))
{
_distance_recurse(R2, T2, o1, c1, o2, c2);
}
}
}
/***************************/
/******* TOLERANCE *********/
/***************************/
void SrBvTreeQuery::_tolerance ( srbvmat R1, srbvvec T1, SrBvTree* o1,
srbvmat R2, srbvvec T2, SrBvTree* o2,
srbvreal tolerance )
{
using namespace SrBvMath;
// init data:
// _dist = 0;
_num_bv_tests = 0;
_num_tri_tests = 0;
if ( tolerance<0 ) tolerance=0.0;
_toler = tolerance;
_closer_than_tolerance = false;
// make sure that the models are built:
if ( o1->nodes()==0 || o2->nodes()==0 ) return;
// Compute the transform [R,T] that takes us from cs2 to cs1.
// [R,T] = [R1,T1]'[R2,T2] = [R1',-R1'T][R2,T2] = [R1'R2, R1'(T2-T1)]
MTxM(_R,R1,R2);
srbvreal Ttemp[3];
VmV(Ttemp, T2, T1);
MTxV(_T, R1, Ttemp);
// compute the transform from o1->child(0) to o2->child(0)
srbvreal Rtemp[3][3], R[3][3], T[3];
MxM(Rtemp,_R,o2->child(0)->R);
MTxM(R,o1->child(0)->R,Rtemp);
MxVpV(Ttemp,_R,o2->child(0)->Tr,_T);
VmV(Ttemp,Ttemp,o1->child(0)->Tr);
MTxV(T,o1->child(0)->R,Ttemp);
// find a distance lower bound for trivial reject
srbvreal d = SrBv::distance(R, T, o1->child(0), o2->child(0));
if ( d <= _toler )
{ _tolerance_recurse ( R, T, o1, 0, o2, 0 );
}
// res->p2 is in cs 1 ; transform it to cs 2
srbvreal u[3];
VmV(u, _p2, _T);
MTxV(_p2, _R, u);
}
void SrBvTreeQuery::_tolerance_recurse
( srbvmat R, srbvvec T,
SrBvTree* o1, int b1,
SrBvTree* o2, int b2 )
{
using namespace SrBvMath;
srbvreal sz1 = o1->child(b1)->size();
srbvreal sz2 = o2->child(b2)->size();
int l1 = o1->child(b1)->leaf();
int l2 = o2->child(b2)->leaf();
if (l1 && l2)
{
// both leaves - find if tri pair within tolerance
_num_tri_tests++;
srbvreal p[3], q[3];
SrBvTri *t1 = &o1->_tris[-o1->child(b1)->first_child - 1];
SrBvTri *t2 = &o2->_tris[-o2->child(b2)->first_child - 1];
srbvreal d = TriDistance(_R,_T,t1,t2,p,q);
if ( d<=_toler )
{
// triangle pair distance less than tolerance
_closer_than_tolerance = true;
_dist = d;
VcV(_p1, p); // p already in c.s. 1
VcV(_p2, q); // q must be transformed into c.s. 2 later
}
return;
}
int a1,a2,c1,c2; // new bv tests 'a' and 'c'
srbvreal R1[3][3], T1[3], R2[3][3], T2[3], Ttemp[3];
if (l2 || (!l1 && (sz1 > sz2)))
{
// visit the children of b1
a1 = o1->child(b1)->first_child;
a2 = b2;
c1 = o1->child(b1)->first_child+1;
c2 = b2;
MTxM(R1,o1->child(a1)->R,R);
VmV(Ttemp,T,o1->child(a1)->Tr);
MTxV(T1,o1->child(a1)->R,Ttemp);
MTxM(R2,o1->child(c1)->R,R);
VmV(Ttemp,T,o1->child(c1)->Tr);
MTxV(T2,o1->child(c1)->R,Ttemp);
}
else
{
// visit the children of b2
a1 = b1;
a2 = o2->child(b2)->first_child;
c1 = b1;
c2 = o2->child(b2)->first_child+1;
MxM(R1,R,o2->child(a2)->R);
MxVpV(T1,R,o2->child(a2)->Tr,T);
MxM(R2,R,o2->child(c2)->R);
MxVpV(T2,R,o2->child(c2)->Tr,T);
}
_num_bv_tests += 2;
srbvreal d1 = SrBv::distance(R1, T1, o1->child(a1), o2->child(a2));
srbvreal d2 = SrBv::distance(R2, T2, o1->child(c1), o2->child(c2));
if (d2 < d1)
{
if (d2 <= _toler) _tolerance_recurse( R2, T2, o1, c1, o2, c2);
if (_closer_than_tolerance) return;
if (d1 <= _toler) _tolerance_recurse( R1, T1, o1, a1, o2, a2);
}
else
{
if (d1 <= _toler) _tolerance_recurse( R1, T1, o1, a1, o2, a2);
if (_closer_than_tolerance) return;
if (d2 <= _toler) _tolerance_recurse( R2, T2, o1, c1, o2, c2);
}
}
srbvreal SrBvTreeQuery::_greedy_distance_recurse (
srbvmat R, srbvvec T, SrBvTree* o1, int b1,
SrBvTree* o2, int b2, srbvreal delta )
{
using namespace SrBvMath;
int l1 = o1->child(b1)->leaf();
int l2 = o2->child(b2)->leaf();
if (l1 && l2)
{
// both leaves. return their distance
_num_tri_tests++;
double p[3], q[3];
const SrBvTri *t1 = o1->tri(-o1->child(b1)->first_child - 1);
const SrBvTri *t2 = o2->tri(-o2->child(b2)->first_child - 1);
return TriDistance(_R,_T,t1,t2,p,q);
}
// First, perform distance tests on the children. Then traverse
// them recursively, but test the closer pair first, the further
// pair second.
int a1,a2,c1,c2; // new bv tests 'a' and 'c'
double R1[3][3], T1[3], R2[3][3], T2[3], Ttemp[3];
srbvreal sz1 = o1->child(b1)->size();
srbvreal sz2 = o2->child(b2)->size();
if (l2 || (!l1 && (sz1 > sz2)))
{
// visit the children of b1
a1 = o1->child(b1)->first_child;
a2 = b2;
c1 = o1->child(b1)->first_child+1;
c2 = b2;
MTxM(R1,o1->child(a1)->R,R);
VmV(Ttemp,T,o1->child(a1)->Tr);
MTxV(T1,o1->child(a1)->R,Ttemp);
MTxM(R2,o1->child(c1)->R,R);
VmV(Ttemp,T,o1->child(c1)->Tr);
MTxV(T2,o1->child(c1)->R,Ttemp);
}
else
{
// visit the children of b2
a1 = b1;
a2 = o2->child(b2)->first_child;
c1 = b1;
c2 = o2->child(b2)->first_child+1;
MxM(R1,R,o2->child(a2)->R);
MxVpV(T1,R,o2->child(a2)->Tr,T);
MxM(R2,R,o2->child(c2)->R);
MxVpV(T2,R,o2->child(c2)->Tr,T);
}
double d1 = SrBv::distance(R1, T1, o1->child(a1), o2->child(a2));
double d2 = SrBv::distance(R2, T2, o1->child(c1), o2->child(c2));
_num_bv_tests += 2;
// if we already found separation, don't further recurse to refine it
double min_d1d2 = (d1 < d2) ? d1 : d2;
if ( min_d1d2 > delta )
return min_d1d2;
// else recurse
if (d2 < d1) // and thus at least d2 <= delta (d1 maybe too)
{
srbvreal alpha = _greedy_distance_recurse(R2, T2, o1, c1, o2, c2, delta);
if ( alpha > delta ) {
if ( d1 > delta )
return (alpha < d1)? alpha: d1;
srbvreal beta = _greedy_distance_recurse(R1, T1, o1, a1, o2, a2, delta);
if ( beta > delta )
return (alpha < beta)? alpha: beta;
}
return 0;
}
else // at least d1 <= delta (d2 maybe too)
{
srbvreal alpha = _greedy_distance_recurse(R1, T1, o1, a1, o2, a2, delta);
if ( alpha > delta ) {
if ( d2 > delta )
return (alpha < d2)? alpha: d2;
srbvreal beta = _greedy_distance_recurse(R2, T2, o1, c1, o2, c2, delta);
if ( beta > delta )
return (alpha < beta)? alpha: beta;
}
return 0;
}
}
/*************************************************************************\
Copyright 1999 The University of North Carolina at Chapel Hill.
All Rights Reserved.
Permission to use, copy, modify and distribute this software and its
documentation for educational, research and non-profit purposes, without
fee, and without a written agreement is hereby granted, provided that the
above copyright notice and the following three paragraphs appear in all
copies.
IN NO EVENT SHALL THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL BE
LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE
USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
OF NORTH CAROLINA HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
THE UNIVERSITY OF NORTH CAROLINA SPECIFICALLY DISCLAIM ANY
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
NORTH CAROLINA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
The authors may be contacted via:
US Mail: S. Gottschalk, E. Larsen
Department of Computer Science
Sitterson Hall, CB #3175
University of N. Carolina
Chapel Hill, NC 27599-3175
Phone: (919)962-1749
EMail: geom@cs.unc.edu
\**************************************************************************/

View File

@ -0,0 +1,173 @@
/** \file sr_bv_tree_query.h
* bounding volume tree */
// TO TEST:
// 1. use of double vs. float
// 2. use inline in SrBvMath functions
# ifndef SR_BV_TREE_QUERY_H
# define SR_BV_TREE_QUERY_H
# include "sr_mat.h"
# include "sr_bv_tree.h"
/*! Encapsulates data for collision and proximity queries between
trees of bounding volumes.
Adapted from PQP, see the copyright notice in the source file. */
class SrBvTreeQuery
{ private :
// common data for all tests:
int _num_bv_tests;
int _num_tri_tests;
srbvmat _R; // xform from model 1 to model 2
srbvvec _T;
// for collision queries only:
SrArray<int> _pairs;
// for both distance and tolerance queries:
srbvreal _dist;
SrPnt _srp1, _srp2;
srbvvec _p1, _p2;
// for distance queries only:
srbvreal _rel_err;
srbvreal _abs_err;
// for proximity queries only:
bool _closer_than_tolerance;
srbvreal _toler;
public :
/*! Constructor */
SrBvTreeQuery ();
/*! Initialize all values and frees all used memory */
void init ();
/*! Returns the number of bounding volume tests in last query */
int num_bv_tests() const { return _num_bv_tests; }
/*! Returns the number of triangle intersection tests in last query */
int num_tri_tests() const { return _num_tri_tests; }
/*! Returns the array with the indices of the colliding faces in
the last collide() query. The indices pairs are sequentially
stored in the array. */
const SrArray<int>& colliding_pairs () const { return _pairs; }
/*! Returns the points computed in the last proximity or distance
query. For a proximity query, these are the points that
stablished the distance smaller than the tolerance (otherwise
these points are not meaningful).
For a distance query, these points stablished the minimum
distance, within the relative and absolute error bounds specified.*/
const SrPnt& p1 () const { return _srp1; }
const SrPnt& p2 () const { return _srp2; }
/*! Returns the distance computed in the last proximity or distance
query. For a proximity query, this value is only meaningful
if the distance is smaller than the tolerance specified */
float distance() const { return (float)_dist; }
/*! The boolean says whether the models in the last proximity query
are closer than tolerance distance specified */
bool closer_than_tolerance() const { return _closer_than_tolerance; }
/*! Find collisions between two models given as bv trees.
m1 is the placement of model 1 in the world &
m2 is the placement of model 2 in the world.
Matrices are in column-major order (as OpenGL) and must specify
only a rigid transformation (rotation and/or translation).
In this version, the collision test stops when the first
contact is found; which can be retrieved with colliding_pairs();
True is returned if a collision was found, and false otherwise. */
bool collide ( const SrMat& m1, const SrBvTree* t1,
const SrMat& m2, const SrBvTree* t2 );
/*! Similar to collide(), but it finds all collisions between the
two given models. Two times the number of collisions is
returned, ie, the size of the colliding_pairs() array. */
int collide_all ( const SrMat& m1, const SrBvTree* t1,
const SrMat& m2, const SrBvTree* t2 );
/*! Computes the distance between two models given as bv trees.
"rel_err" is the relative error margin from actual distance.
"abs_err" is the absolute error margin from actual distance.
The smaller of the two will be satisfied, so set one large
to nullify its effect.
Returns the distance between the two models.
Methods distance(), p1() and p2() will return the distance and
points computed during the query.
Note: pointers t1 and t2 are not const only because the _last_tri
member variable of SrBvTree is updated during the query. */
float distance ( const SrMat& m1, SrBvTree* t1,
const SrMat& m2, SrBvTree* t2,
float rel_err, float abs_err );
/*! Checks if distance between two models (given as bv trees) is <= tolerance
The algorithm returns whether the true distance is <= or >
"tolerance". This routine does not simply compute true distance
and compare to the tolerance - models can often be shown closer or
farther than the tolerance more trivially. In most cases this
query should run faster than a distance query would on the same
models and configurations.
The returned parameter returns the result of the query, which can
be also retrieved with method closer_than_tolerance().
If the models are closer than ( <= ) tolerance, the points that
established this can be retrieved with methods p1(), and p2(),
and the distance can be retrieved with method distance() */
bool tolerance ( const SrMat& m1, SrBvTree* t1,
const SrMat& m2, SrBvTree* t2, float tolerance );
float greedy_distance ( const SrMat& m1, SrBvTree* t1,
const SrMat& m2, SrBvTree* t2, float delta );
public : // lower level methods
enum CollideFlag { CollideAllContacts=1, CollideFirstContact=2 };
/*! Lower level call used by the two main collide methods.
Collision results, if any, are stored in colliding_pairs(). */
void collide ( srbvmat R1, srbvvec T1, const SrBvTree* o1,
srbvmat R2, srbvvec T2, const SrBvTree* o2, CollideFlag flag );
/*! Lower level call called by the other tolerance() method */
bool tolerance ( srbvmat R1, srbvvec T1, SrBvTree* o1,
srbvmat R2, srbvvec T2, SrBvTree* o2, float toler )
{ _tolerance ( R1, T1, o1, R2, T2, o2, toler );
_srp1.set ( _p1 );
_srp2.set ( _p2 );
return _closer_than_tolerance;
}
/*! Computes a simple but fast lower bound on the distance between two models */
float greedy_distance ( srbvmat R1, srbvvec T1, SrBvTree* o1,
srbvmat R2, srbvvec T2, SrBvTree* o2, float delta );
private :
void _collide_recurse ( srbvmat R, srbvvec T,
const SrBvTree* o1, int b1,
const SrBvTree* o2, int b2, int flag );
void _distance ( srbvmat R1, srbvvec T1, SrBvTree* o1,
srbvmat R2, srbvvec T2, SrBvTree* o2,
srbvreal rel_err, srbvreal abs_err );
void _distance_recurse ( srbvmat R, srbvvec T,
SrBvTree* o1, int b1,
SrBvTree* o2, int b2 );
void _tolerance ( srbvmat R1, srbvvec T1, SrBvTree* o1,
srbvmat R2, srbvvec T2, SrBvTree* o2,
srbvreal tolerance );
void _tolerance_recurse ( srbvmat R, srbvvec T,
SrBvTree* o1, int b1,
SrBvTree* o2, int b2 );
srbvreal _greedy_distance_recurse ( srbvmat R, srbvvec T,
SrBvTree* o1, int b1,
SrBvTree* o2, int b2, srbvreal delta );
};
//============================== end of file ===============================
# endif // SR_BV_TREE_QUERY_H

View File

@ -0,0 +1,202 @@
#include "precompiled.h"
# include <math.h>
# include "sr_box.h"
# include "sr_plane.h"
# include "sr_camera.h"
//# define SR_USE_TRACE1 // ray
# include "sr_trace.h"
//=================================== SrCamera ===================================
SrCamera::SrCamera ()
{
init ();
}
SrCamera::SrCamera ( const SrCamera &c )
:eye(c.eye), center(c.center), up(c.up)
{
fovy = c.fovy;
znear = c.znear;
zfar = c.zfar;
aspect = c.aspect;
scale = c.scale;
}
SrCamera::SrCamera ( const SrPnt& e, const SrPnt& c, const SrVec& u )
: eye(e), center(c), up(u)
{
fovy = SR_TORAD(60);
znear = 0.1f;
zfar = 1000.0f;
aspect = 1.0f;
}
void SrCamera::init ()
{
eye.set ( 0, 0, 2.0f );
center = SrVec::null;
up = SrVec::j;
fovy = SR_TORAD(60);
znear = 0.1f;
zfar = 1000.0f;
aspect = 1.0f;
scale = 1.0f;
}
SrMat& SrCamera::get_view_mat ( SrMat &m ) const
{
m.look_at ( eye, center, up );
return m;
}
SrMat& SrCamera::get_perspective_mat ( SrMat &m ) const
{
m.perspective ( fovy, aspect, znear, zfar );
return m;
}
// screenpt coords range in [-1,1]
void SrCamera::get_ray ( float winx, float winy, SrVec &p1, SrVec &p2 ) const
{
p1.set ( winx, winy, znear ); // p1 is in the near clip plane
SrMat M(SrMat::NotInitialized), V(SrMat::NotInitialized), P(SrMat::NotInitialized);
V.look_at ( eye, center, up );
P.perspective ( fovy, aspect, znear, zfar );
M.mult ( V, P ); // equiv to M = V * P
M.invert();
p1 = p1 * M;
p2 = p1-eye; // ray is in object coordinates, but before the scaling
p2.normalize();
p2 *= (zfar-znear);
p2 += p1;
float inv_scale = 1.0f/scale;
p1*= inv_scale;
p2*= inv_scale;
SR_TRACE1 ( "Ray: "<< p1 <<" : "<< p2 );
}
/* - -------- \
| | | \
h | bbox |--------------.eye
| | | dist /
- -------- / tan(viewang/2)=(h/2)/dist
*/
void SrCamera::view_all ( const SrBox &box, float fovy_radians )
{
SrVec size = box.size();
float h = SR_MAX(size.x,size.y);
fovy = fovy_radians;
up = SrVec::j;
center = box.center();
eye = center;
float dist = (h/2)/tanf(fovy/2);
eye.z = box.b.z + dist;
float delta = box.max_size() + 0.0001f;
zfar = SR_ABS(eye.z)+delta;
scale = 1.0f;
}
void SrCamera::apply_translation_from_mouse_motion ( float lwinx, float lwiny, float winx, float winy )
{
SrVec p1, p2, x, inc;
SrPlane plane ( center, eye-center );
get_ray ( lwinx, lwiny, p1, x );
p1 = plane.intersect ( p1, x );
get_ray ( winx, winy, p2, x );
p2 = plane.intersect ( p2, x );
inc = p1-p2;
inc *= scale;
*this += inc;
}
void SrCamera::operator*= ( const SrQuat& q )
{
eye -= center;
eye = eye * q;
eye += center;
up -= center;
up = up * q;
up += center;
}
void SrCamera::operator+= ( const SrVec& v )
{
eye += v;
center += v;
}
void SrCamera::operator-= ( const SrVec& v )
{
eye -= v;
center -= v;
}
//=============================== friends ==========================================
SrCamera operator* ( const SrCamera& c, const SrQuat& q )
{
SrCamera cam(c);
cam *= q;
return cam;
}
SrCamera operator+ ( const SrCamera& c, const SrVec& v )
{
SrCamera cam(c);
cam += v;
return cam;
}
SrOutput& operator<< ( SrOutput& out, const SrCamera& c )
{
// out << "eye:" << c.eye << " center:" << c.center << " up:" << c.up << srnl;
out << "eye " << c.eye << srnl <<
"center " << c.center << srnl <<
"up " << c.up << srnl <<
"fovy " << c.fovy << srnl <<
"znear " << c.znear << srnl <<
"zfar " << c.zfar << srnl <<
"aspect " << c.aspect << srnl <<
"scale " << c.scale << srnl;
return out;
}
SrInput& operator>> ( SrInput& inp, SrCamera& c )
{
while ( 1 )
{ if ( inp.get_token()==SrInput::EndOfFile ) break;
if ( inp.last_token()=="eye" ) inp>>c.eye;
else if ( inp.last_token()=="center" ) inp>>c.center;
else if ( inp.last_token()=="up" ) inp>>c.up;
else if ( inp.last_token()=="fovy" ) inp>>c.fovy;
else if ( inp.last_token()=="znear" ) inp>>c.znear;
else if ( inp.last_token()=="zfar" ) inp>>c.zfar;
else if ( inp.last_token()=="aspect" ) inp>>c.aspect;
else if ( inp.last_token()=="scale" ) inp>>c.scale;
else { inp.unget_token(); break; }
}
return inp;
}
//================================ End of File =========================================

108
source/dcdt/se/sr_camera.h Normal file
View File

@ -0,0 +1,108 @@
# ifndef SR_CAMERA_H
# define SR_CAMERA_H
/** \file sr_camera.h
* Keeps camera parameters
*/
# include "sr_vec.h"
# include "sr_quat.h"
class SrMat;
class SrBox;
/*! \class SrCamera sr_camera.h
\brief Keeps camera parameters
SrCamera contains the parameters to define a camera.
Attention: if znear is too small inconsistencies in the rendering may appear;
a minimal value of 0.1 should be considered. */
class SrCamera
{ public :
SrPnt eye; //!< position of the eye, default is (0,0,2).
SrPnt center; //!< position where the eye is looking to, default is (0,0,0).
SrVec up; //!< the up vector orients the camera around the eye-center vector, default is (0,1,0)
float fovy; //!< the y field of view in radians. Default is pi/3 (60deg), range is [0.01,pi].
float znear; //!< must be >0, default is 0.1.
float zfar; //!< must be >0, default is 1000.
float aspect; //!< normally is set to the screen width/heigh, default is 1.
float scale; //!< a scale factor to be applied between the view matrix and the scene
public :
/*! Initialize the camera with the default parameters, see init(). */
SrCamera ();
/*! Copy constructor. */
SrCamera ( const SrCamera &c );
/*! Initialize the camera with the main parameters eye, center and up. */
SrCamera ( const SrPnt& e, const SrPnt& c, const SrVec& u );
/*! Set the parameters to their default values, which are :
eye=(0,0,2), center=(0,0,0), up=(0,1,0), fovy=60, znear=0.1, zfar=1000, aspect=1. */
void init ();
/*! Set m to be the transformation matrix generated by the parameters
eye, center, and up. A reference to m is also returned.
Note: the scale factor is not included in this matrix. */
SrMat& get_view_mat ( SrMat &m ) const;
/*! Set m to be the transformation projection matrix generated by the parameters
fovy, znear, zfar, aspect. A reference to m is also returned. */
SrMat& get_perspective_mat ( SrMat &m ) const;
/*! Gets the 3d ray (p1,p2) which projects exactly in the given window point
according to the camera current parameters. Points p1 and p2 lye in the
near and far planes respectively. Window points are considered to be
in normalized coordinates, ranging between [-1,1]. */
void get_ray ( float winx, float winy, SrVec& p1, SrVec& p2 ) const;
/*! Sets center at the center of the box, and put the eye in the semi-line
rooted at center and with direction z, in a distance from the center
that is sufficient to visualize all the box with the given fov_y parm.
After this call, variable SrCamera::fovy will have the same value as fov_y.
Note: the scale factor is set to one in this method. */
void view_all ( const SrBox& box, float fov_y );
/*! Apply a trackball translation induced from the mouse motion. First, the ray
passing through each window position and the intersection point with the
projection plane is determined. Then, the two intersection points determine the
displacement to be applied to the trackball, after a multiplication with the
current spin rotation. Mouse coordinates must be normalized in [-1,1]x[-1,1]. */
void apply_translation_from_mouse_motion ( float lwinx, float lwiny, float winx, float winy );
/*! Transforms the camera position with the given rotation. The rotation is
applied to the up vector, and to the eye vector in the following way by
going: x-=center; x=x*q; x+=center (x represents eye or up vector). */
void operator*= ( const SrQuat& q );
/*! Adds the vector v to the eye and center points. */
void operator+= ( const SrVec& v );
/*! Subtracts the vector v to the eye and center points. */
void operator-= ( const SrVec& v );
/*! Returns a camera that is the same as the given camera c, but with the
rotation q applied. See the operator *= for a description of how the
rotation is applied to the camera. */
friend SrCamera operator* ( const SrCamera& c, const SrQuat& q );
/*! Returns a camera that is the same as the given camera c, but with the
the translation vector v added to the eye and center points. */
friend SrCamera operator+ ( const SrCamera& c, const SrVec& v );
/*! Output camera data values in format keyword1 value \n keyword2 value ...
(keywords are: eye, center, up, etc*/
friend SrOutput& operator<< ( SrOutput& out, const SrCamera& c );
/*! Input camera data. Not all keywords are required to exist. The routine
returns when a non-keyword entry is found (which is 'ungetted' in inp). */
friend SrInput& operator>> ( SrInput& inp, SrCamera& c );
};
//================================ End of File =================================================
# endif // SR_CAMERA_H

View File

@ -0,0 +1,34 @@
#include "precompiled.h"
# include "sr_cfg_manager.h"
//=========================== SrCfgManagerBase ========================================
// ps: could try each "level check" in a new thread
bool SrCfgManagerBase::visible ( const srcfg* ct0, const srcfg* ct1, srcfg* ct, float prec )
{
float segs, dseg, dt, t;
float len = dist ( ct0, ct1 );
int k = 1; // k is the current level being tested
while ( true )
{ // test the 2^(k-1) tests of level k:
segs = (float) sr_pow ( 2, k );
dseg = 1.0f / segs;
dt = dseg*2.0f;
//sr_out<<"level="<<k<<srnl;
//sr_out<<"segs="<<segs<<srnl;
for ( t=dseg; t<1.0f; t+=dt )
{ interp ( ct0, ct1, t, ct );
//sr_out<<"t="<<t<<srnl;
if ( !valid(ct) ) return false;
}
//sr_out<<"len/segs="<<(len/segs)<<" prec="<<prec<<srnl;
if ( len/segs<=prec ) return true; // safe edge up to prec
k++; // increment level
}
}
//============================= End of File ===========================================

View File

@ -0,0 +1,148 @@
# ifndef SR_CFG_MANAGER_H
# define SR_CFG_MANAGER_H
# include "sr_input.h"
# include "sr_output.h"
# include "sr_class_manager.h"
typedef void srcfg;
class SrCfgManagerBase : protected SrClassManagerBase
{
public : // accesible members from SrSharedClass
/*! Returns the current reference counter value. */
int getref () const { return SrSharedClass::getref(); }
/*! Increments the reference counter. */
void ref () { SrSharedClass::ref(); }
/*! Decrements the reference counter (if >0), and if the
counter becomes 0, the class is automatically self deleted. */
void unref() { SrSharedClass::unref(); }
public : // virtual callbacks
/*! Returns a new configuration */
virtual srcfg* alloc ()=0;
/*! Returns a new configuration as a copy of c */
virtual srcfg* alloc ( const srcfg* c )=0;
/*! Deletes a configuration */
virtual void free ( srcfg* c )=0;
/*! Copy the contents of c2 into c1. Note: c1 and c2 will be equal
in several calls, so a test if(c1==c2) should be included here. */
virtual void copy ( srcfg* c1, const srcfg* c2 )=0;
/*! Outputs a configuration */
virtual void output ( SrOutput& o, const srcfg* c )=0;
/*! Inputs a configuration */
virtual void input ( SrInput& i, srcfg* c )=0;
/*! Returns a random configuration, not necessarily valid. */
virtual void random ( srcfg* c )=0;
/*! Returns if the configuration is valid. */
virtual bool valid ( const srcfg* c )=0;
/*! Returns a distance between the two configurations. */
virtual float dist ( const srcfg* c1, const srcfg* c2 )=0;
/*! Returns the interpolated configuration c between c1 and c2,
according to t in [0,1]. */
virtual void interp ( const srcfg* c1, const srcfg* c2, float t, srcfg* c )=0;
/*! Returns true if all nodes between the interpolation of ct0 and ct1 are
valid according to the given precision prec. Configuration ct is to be
used as a temporary variable. Note: ct0 and ct1 are already valid.
The default implementation for method visible performs recursive binary
subdivision untill reaching precision prec. */
virtual bool visible ( const srcfg* ct0, const srcfg* ct1, srcfg* ct, float prec );
/*! Returns true if the time encoded in c1 is smaller than in c2. This will be
only relevant to planning in time-varying conditions, if this is not the
case, true must always be returned */
virtual bool monotone ( const srcfg* c1, const srcfg* c2 )=0;
/*! This is called just to notify that node child has been added
as a child of node parent */
virtual void child_added ( srcfg* parent, srcfg* child )=0;
/*! The time() method returns the time associated with the given configuration c.
This method is only called when solving a time-varying problem */
virtual float time ( srcfg* c )=0;
};
/*! Template SrCfgManager makes automatic type casts to user-defined classes.
If needed in special cases, it can be further derived to rewrite/extend
method SrCfgManagerBase::visible().
Here is an example of implementations of a configuration class and a class manager that
can be directly used with template SrCfgManager<MyCfg,MyManager> : \code
class MyCfg
{ public :
MyCfg ();
MyCfg ( const MyCfg& c );
~MyCfg ();
void operator = ( const MyCfg& c );
void random ();
bool valid () const;
friend SrOutput& operator<< ( SrOutput& out, const MyCfg& c );
friend SrInput& operator>> ( SrInput& inp, MyCfg& c );
friend float dist ( const MyCfg& c1, const MyCfg& c2 );
friend float interp ( const MyCfg& c1, const MyCfg& c2, float t, MyCfg& c );
};
class MyManager
{ public :
MyCfg* alloc () { return new MyCfg; }
MyCfg* alloc ( const MyCfg* c ) { return new MyCfg(*c); }
void free ( MyCfg* c ) { delete c; }
void copy ( MyCfg* c1, const MyCfg* c2 ) { *c1=*c2; }
void output ( SrOutput& o, const MyCfg* c ) { o<<(*c); }
void input ( SrInput& i, MyCfg* c ) { i>>(*c); }
void random ( MyCfg* c ) { c->random(); }
bool valid ( const MyCfg* c ) { return c->valid(); }
float dist ( const MyCfg* c1, const MyCfg* c2 ) { return ::dist(*c1,*c2); }
void interp ( const MyCfg* c1, const MyCfg* c2, float t, MyCfg* c )
{ return ::interp(*c1,*c2,t,*c); }
void child_added ( MyCfg* parent, MyCfg* child ) {}
}; \endcode*/
template <class C, class M=SrCfgManagerBase>
class SrCfgManager : public SrCfgManagerBase, public M
{ public :
virtual srcfg* alloc ()
{ return (srcfg*) M::alloc(); }
virtual srcfg* alloc ( const srcfg* c )
{ return (srcfg*) M::alloc((const C*)c); }
virtual void free ( srcfg* c )
{ M::free ( (C*)c ); }
virtual void copy ( srcfg* c1, const srcfg* c2 )
{ M::copy ( (C*)c1, (const C*)c2 ); }
virtual void output ( SrOutput& o, const srcfg* c )
{ M::output ( o, (const C*)c ); }
virtual void input ( SrInput& i, srcfg* c )
{ M::input ( i, (C*)c ); }
virtual void random ( srcfg* c )
{ M::random ( (C*)c ); }
virtual bool valid ( const srcfg* c )
{ return M::valid ( (const C*)c ); }
virtual float dist ( const srcfg* c1, const srcfg* c2 )
{ return M::dist ( (const C*)c1, (const C*)c2 ); }
virtual void interp ( const srcfg* c1, const srcfg* c2, float t, srcfg* c )
{ M::interp ( (const C*)c1, (const C*)c2, t, (C*)c ); }
virtual bool monotone ( const srcfg* c1, const srcfg* c2 )
{ return M::monotone ( (const C*)c1, (const C*)c2 ); }
virtual void child_added ( srcfg* child, srcfg* parent )
{ M::child_added ( (C*)parent, (C*)child ); }
virtual float time ( srcfg* c )
{ return M::time ( (C*)c ); }
};
//============================== end of file ===============================
# endif // SR_CFG_MANAGER_H

View File

@ -0,0 +1,442 @@
#include "precompiled.h"
# include "sr_cfg_path.h"
# include "sr_random.h"
//# define SR_USE_TRACE1 //
# include "sr_trace.h"
//================================ SrCfgPathBase ========================================
SrCfgPathBase::SrCfgPathBase ( SrCfgManagerBase* cman )
{
_cman = cman;
_cman->ref();
_size = 0;
_interp_start = 0;
_interp_startdist = 0;
}
SrCfgPathBase::SrCfgPathBase ( const SrCfgPathBase& p )
{
_cman = p._cman;
_cman->ref();
_size = 0;
insert_path ( 0, p );
_interp_start = 0;
_interp_startdist = 0;
}
SrCfgPathBase::~SrCfgPathBase ()
{
init ();
compress ();
_cman->unref();
}
void SrCfgPathBase::init ()
{
_size = 0;
_interp_start = 0;
_interp_startdist = 0;
}
void SrCfgPathBase::compress ()
{
while ( _buffer.size()>_size ) _cman->free ( _buffer.pop() );
}
void SrCfgPathBase::push ( const srcfg* c )
{
if ( _buffer.size()==_size ) // add new buffer entry
{ _buffer.push() = _cman->alloc();
}
if ( c ) _cman->copy ( _buffer[_size], c );
_size++;
}
void SrCfgPathBase::pop ()
{
if ( _size>0 ) _size--;
_interp_start = 0;
_interp_startdist = 0;
}
void SrCfgPathBase::remove ( int i )
{
srcfg* cfg = _buffer[i];
_buffer.move ( i/*dest*/, i+1/*src*/, _size-(i+1)/*n*/ );
_size--;
_buffer[_size] = cfg;
_interp_start = 0;
_interp_startdist = 0;
}
void SrCfgPathBase::remove ( int i, int dp )
{
int bsize = _buffer.size();
int idp = i+dp;
_buffer.size ( bsize+dp );
_buffer.move ( bsize/*dest*/, i/*src*/, dp/*n*/ ); // copy part to remove to the buffer end
_buffer.move ( i/*dest*/, idp/*src*/, _size-idp/*n*/ ); // remove
_buffer.move ( _size-dp/*dest*/, bsize/*src*/, dp/*n*/ ); // keep the removed part
_buffer.size ( bsize );
_size -= dp;
_interp_start = 0;
_interp_startdist = 0;
}
void SrCfgPathBase::insert ( int i, const srcfg* c )
{
if ( i>=_size ) { push(c); return; }
push ( c );
srcfg* newcfg = top();
_buffer.move ( i+1/*dest*/, i/*src*/, _size-(i+1)/*n*/ );
_buffer[i] = newcfg;
_interp_start = 0;
_interp_startdist = 0;
}
void SrCfgPathBase::insert_path ( int i, const SrCfgPathBase& p )
{
if ( p.size()<1 ) return;
if ( i>_size ) i=_size;
// open space in the end:
int oldsize = _size;
int newsize = _size+p.size();
while ( _size!=newsize ) push(0);
// move new space to the middle:
int n, n2=newsize-(oldsize-i);
srcfg* tmp;
for ( n=i; n<oldsize; n++ )
{ SR_SWAP(_buffer[n],_buffer[n2]);
}
// copy contents:
for ( n=0; n<p.size(); n++ )
{ _cman->copy ( _buffer[i+n], p._buffer[n] );
}
_interp_start = 0;
_interp_startdist = 0;
}
void SrCfgPathBase::append_path ( SrCfgPathBase& p )
{
// open space here:
_buffer.insert ( _size, p.size() );
// transfer nodes from p:
int i;
for ( i=0; i<p.size(); i++ ) _buffer[_size+i] = p._buffer[i];
_size += p.size();
// close space there:
p._buffer.remove ( 0, p.size() );
p._size=0;
}
void SrCfgPathBase::swap ( int i, int j )
{
srcfg* tmp;
SR_SWAP(_buffer[i],_buffer[j]);
_interp_start = 0;
_interp_startdist = 0;
}
void SrCfgPathBase::revert ()
{
int i;
int end = _size-1;
int mid = _size/2;
for ( i=0; i<mid; i++ )
{ swap ( i, end );
end--;
}
_interp_start = 0;
_interp_startdist = 0;
}
void SrCfgPathBase::size ( int s )
{
while ( size()<s ) push(0);
while ( size()>s ) pop();
_interp_start = 0;
_interp_startdist = 0;
}
float SrCfgPathBase::len ( int i1, int i2 ) const
{
float l=0;
int i;
if ( _size<2 ) return l;
for ( i=i1; i<i2; i++ ) l += _cman->dist(_buffer[i],_buffer[i+1]);
return l;
}
void SrCfgPathBase::interp ( float t, srcfg* c )
{
if ( _buffer.size()<2 ) { _interp_start=0; _interp_startdist=0; return; }
// parameters _interp_start and _interp_startdist are used to optimize
// the time search during sequential play, here we check if they can
// be used or if we should recount the distance form the first node
if ( t<_interp_startdist )
{ _interp_start=0; _interp_startdist=0; }
float dt;
float d = _interp_startdist;
int i;
for ( i=_interp_start+1; i<_buffer.size(); i++ )
{ dt = _cman->dist ( _buffer.const_get(i-1), _buffer.const_get(i) );
if ( d+dt>=t ) break;
d += dt;
}
if ( i==_buffer.size() ) // may happen because of imprecisions
{ i--; t=1; }
else
{ t -= d; t /= dt; if ( t>1 ) t=1; }
_interp_start = i-1;
_interp_startdist = d;
_cman->interp ( _buffer.const_get(i-1), _buffer.const_get(i), t, c );
}
void SrCfgPathBase::temporal_interp ( float t, srcfg* c )
{
if ( _buffer.size()<2 ) { _interp_start=0; _interp_startdist=0; return; }
// here _interp_startdist is in fact the "start time"
if ( t<_interp_startdist )
{ _interp_start=0; _interp_startdist=0; }
float dt; // delta time
float nt; // next time
float ct = _interp_startdist; // current time
int i;
for ( i=_interp_start+1; i<_buffer.size(); i++ )
{ nt = _cman->time ( _buffer.const_get(i) );
dt = nt-ct;
if ( nt>=t ) break;
ct = nt;
}
if ( i==_buffer.size() ) // may happen because of imprecisions
{ i--; t=1; }
else
{ t -= ct; t /= dt; if ( t>1 ) t=1; }
_interp_start = i-1;
_interp_startdist = ct;
_cman->interp ( _buffer.const_get(i-1), _buffer.const_get(i), t, c );
}
void SrCfgPathBase::smooth_random ( float prec, float& len )
{
if ( len<0 ) len = SrCfgPathBase::len(0,_size-1);
float t1 = SrRandom::randf()*len;
float t2 = SrRandom::randf()*len;
linearize ( prec, len, t1, t2 );
}
int SrCfgPathBase::linearize ( float prec, float& len, float t1, float t2 )
{
srcfg* cfg1; // 1st random cfg along the path
srcfg* cfg2; // 2nd random cfg along the path
srcfg* citp; // cfg used during interpolation
// get buffer space for cfg1 and cfg2:
if ( _buffer.size()<_size+3 ) // add new buffer entries
{ push(0); push(0); push(0); pop(); pop(); pop(); }
citp = _buffer[_size];
cfg1 = _buffer[_size+1];
cfg2 = _buffer[_size+2];
// ensure t1<t2:
float tmp;
if ( t1>len ) t1=len;
if ( t2>len ) t2=len;
if ( t1>t2 ) SR_SWAP(t1,t2);
// get configurations cfg1 and cfg2 at t1 and t2:
_interp_start = 0;
_interp_startdist = 0;
interp ( t1, cfg1 );
int i1 = _interp_start+1; // node after t1
interp ( t2, cfg2 );
int i2 = _interp_start; // node prior t2
if ( i1>i2 ) return 0; // samples t1 and t2 are in the same edge
if ( !_cman->visible(cfg1,cfg2,citp,prec) ) return -1; // cannot smooth
len -= SrCfgPathBase::len(i1-1,i2+1); // remove the older part length from len
if ( i1==i2 ) // only 1 vertex between cfg1 and cfg2: insert 1 space
{ insert ( i1+1, 0 );
i2 = i1+1;
}
// positions i1 and i2 become cfg1 and cfg2:
_cman->copy ( _buffer[i1], cfg1 );
_cman->copy ( _buffer[i2], cfg2 );
// delete non-used intermediate vertices:
remove ( i1+1, (i2-i1)-1 );
len += SrCfgPathBase::len(i1-1,i1+2); // add the new part length to len
return 1;
}
void SrCfgPathBase::smooth_ends ( float prec )
{
if ( _size<5 ) return; // not possible if <5
srcfg* citp; // cfg used during interpolation
// get buffer space for citp:
if ( _buffer.size()<_size+1 ) { push(0); pop(); }
citp = _buffer[_size];
// specify mid node and border nodes i1 and i2:
int mid = _size/2;
int i1 = mid;
int i2 = mid;
int posmax = _size-1;
int max = _size-3;
int min = 2;
// check second half:
while ( i2<=max )
{ if ( _cman->visible(_buffer[i2],_buffer[posmax],citp,prec) )
{ remove ( i2+1, (posmax-i2)-1 );
break;
}
i2++;
}
// check first half:
while ( i1>=min )
{ if ( _cman->visible(_buffer[0],_buffer[i1],citp,prec) )
{ remove ( 1, i1-1 );
break;
}
i1--;
}
}
float SrCfgPathBase::_diff ( int i, float prec )
{
if ( i<=0 || i>=size()-1 ) return -1; // protection
/* srcfg* cfg1;
srcfg* cfg2;
// get buffer space for cfg1 and cfg2:
if ( _buffer.size()<_size+2 ) // add new buffer entries
{ push(0); push(0); pop(); pop(); }
cfg1 = _buffer[_size];
cfg2 = _buffer[_size+1];
*/
float d1 = _cman->dist(_buffer.const_get(i-1),_buffer.const_get(i));
float d2 = _cman->dist(_buffer.const_get(i),_buffer.const_get(i+1));
float d = _cman->dist(_buffer.const_get(i-1),_buffer.const_get(i+1));
float diff = (d1+d2)-d;
diff /= d;
/*
prec*=5.0f;
d = _cman->dist ( _buffer.const_get(i-1), _buffer.const_get(i) );
t = prec/d;
if ( t>1 ) t=1;
_cman->interp ( _buffer.const_get(i-1), _buffer.const_get(i), t, cfg1 );
d = _cman->dist ( _buffer.const_get(i), _buffer.const_get(i+1) );
t = prec/d;
if ( t>1 ) t=1;
_cman->interp ( _buffer.const_get(i), _buffer.const_get(i+1), t, cfg2 );
float diff = (_cman->dist(cfg1,_buffer.const_get(i))+_cman->dist(_buffer.const_get(i),cfg2))
-_cman->dist(cfg1,cfg2);*/
return diff;
}
void SrCfgPathBase::smooth_init ( float prec )
{
_sprec = prec;
//smooth_ends ( prec );
_slen = len();
_sbads=0;
_slastangmax=9999999999.0f;
}
bool SrCfgPathBase::smooth_step ()
{
smooth_random ( _sprec, _slen );
return false;
int i, imax=1;
int lasti = size()-2;
float d;
float angmax=0;
float t=0, tmax=0;
for ( i=1; i<=lasti; i++ )
{ t += _cman->dist ( _buffer.const_get(i-1), _buffer.const_get(i) );
d = _diff ( i, _sprec );
if ( d>angmax )
{ angmax=d; imax=i; tmax=t; }
}
//sr_out<<"angmax => "<<angmax<<srnl;
//sr_out<<"prec => "<<_sprec<<srnl;
float maxradius = _slen/10.0f;
float r, t1, t2;
int result;
int times=0;
r = maxradius;
while ( r>_sprec )
{ //r = SrRandom::randf()*maxradius;
r/=2.0f;
//sr_out<<times<<": "<<r<<srnl;
t1 = tmax-r; if ( t1<0 ) t1=0;
t2 = tmax+r; if ( t2>_slen ) t2=_slen;
result = linearize ( _sprec, _slen, t1, t2 );
times++;
if ( result==0 ) break;
}
if ( angmax>_slastangmax )
{ _sbads++;
}
else
{ _sbads=0;
_slastangmax=angmax;
}
//sr_out<<"BADS: "<<_bads<<srnl;
//float freedom = z.freedom / float(times);
if ( _sbads>6 ) return true; // no more easy improvements
return false;
}
SrOutput& operator<< ( SrOutput& o, const SrCfgPathBase& p )
{
int i;
for ( i=0; i<p.size(); i++ )
{ o<<"path node "<<i<<":\n";
p._cman->output ( o, p._buffer[i] );
o<<srnl;
}
return o;
}
//================================ End of File =================================================

View File

@ -0,0 +1,148 @@
# ifndef SR_CFG_PATH_H
# define SR_CFG_PATH_H
# include "sr_output.h"
# include "sr_array.h"
//# include "sr_cfg_manager.h"
# include "sr_cfg_manager.h"
/*! SrCfgPathBase manages a sequence of configurations, describing
a path in configuration space. */
class SrCfgPathBase
{ private :
SrArray<srcfg*> _buffer;
SrCfgManagerBase* _cman;
int _size;
int _interp_start;
float _interp_startdist;
float _sprec, _slen;
int _sbads;
float _slastangmax;
public :
/*! Constructor requires a pointer to the configuration manager */
SrCfgPathBase ( SrCfgManagerBase* cman );
/*! Copy constructor sharing the configuration manager */
SrCfgPathBase ( const SrCfgPathBase& p );
/*! Destructor */
~SrCfgPathBase ();
/*! Makes the path become empty */
void init ();
/*! Deletes all non-used internal buffers */
void compress ();
/*! Appends a configuration at the end of the path.
Even if c is null, a new valid configuration is pushed. */
void push ( const srcfg* c );
/*! Removes the last configuration in the path */
void pop ();
/*! Removes the position i, which must be a valid position */
void remove ( int i );
/*! Removes dp positions at position i (parameters must be valid) */
void remove ( int i, int dp );
/*! Inserts a configuraton at position i; if i>=size() a push() is done. */
void insert ( int i, const srcfg* c );
/*! Inserts a copy of path p at position i; if i>=size() p is appended. */
void insert_path ( int i, const SrCfgPathBase& p );
/*! Appends p to the path; p will become an empty path after this call */
void append_path ( SrCfgPathBase& p );
/*! Swap the positions of the two nodes */
void swap ( int i, int j );
/*! Reverts the order of the nodes in the path */
void revert ();
/*! pushes or pops entries until reaching size s */
void size ( int s );
/*! Returns the number of configurations in the path */
int size () const { return _size; }
/*! Returns the length of the path from node i1 to node i2.
The length is calculated by adding the distances
of adjacent configurations in the path. */
float len ( int i1, int i2 ) const;
/*! Returns the length of the full path. */
float len () const { return len(0,_size-1); }
/*! Returns in c the interpolated configuration in the path according to parameter t,
which must be in the closed interval [0,len]. */
void interp ( float t, srcfg* c );
/*! Returns in c the interpolated configuration in the path according to the time
parameter t, which must be a valid time between the times associated with the
first and last nodes of the path. The configuration manager method time() is
used to retrieve the time associated with each configuration. */
void temporal_interp ( float t, srcfg* c );
/*! The smooth routine takes two random configurations interpolated along the path
and replaces the portion between them by a direct interpolation if no collisions
appear (up to precision prec). Parameter len should contain the current lenght of
the path. A <0 value can be given if the length is not known in advance. In any
case, after the smooth, len will contain the updated path length. */
void smooth_random ( float prec, float& len );
/*! tries to replace the subpath(t1,t2) by a "straight interpolation".
Returns: updated len, 0:same edge, -1:collision, 1:done */
int linearize ( float prec, float& len, float t1, float t2 );
/*! Make one pass in all nodes, trying first to smooth nodes closer to the first and
last nodes. Usefull because most often several nodes are created near the root
of the two expanding trees. Usually is called once before the random smooths. */
void smooth_ends ( float prec );
void smooth_init ( float prec );
bool smooth_step ();
/*! Copy operator */
void operator= ( const SrCfgPathBase& p ) { init(); insert_path(0,p); }
/*! Returns the configuration of the last node in the path */
srcfg* top () { return _buffer[_size-1]; }
/*! Returns configuration index i */
srcfg* get ( int i ) { return _buffer[i]; }
/*! Const version of get() */
const srcfg* const_get ( int i ) const { return _buffer.const_get(i); }
/*! Outputs the path nodes for inspection */
friend SrOutput& operator<< ( SrOutput& o, const SrCfgPathBase& p );
private:
float _diff ( int i, float prec );
};
/*! This template provides automatic type casts for the user-defined
configuration class C. */
template <class C>
class SrCfgPath : public SrCfgPathBase
{ public :
SrCfgPath ( SrCfgManagerBase* cman ) : SrCfgPathBase(cman) {}
SrCfgPath ( const SrCfgPath& p ) : SrCfgPathBase(p) {}
C* operator[] ( int i ) { return get(i); }
C* top () { return (C*)SrCfgPathBase::top(); }
C* get ( int i ) { return (C*)SrCfgPathBase::get(i); }
const C* const_get ( int i ) const { return (const C*)SrCfgPathBase::const_get(i); }
void operator= ( const SrCfgPath<C>& p ) { SrCfgPathBase::init(); SrCfgPathBase::insert_path(0,p); }
};
//================================ End of File =================================================
# endif // SR_PATH_H

View File

@ -0,0 +1,128 @@
#include "precompiled.h"
# include "sr_cfg_planner.h"
//# define SR_USE_TRACE1 // start
//# define SR_USE_TRACE2 // update
//# define SR_USE_TRACE3 // bridge
# include "sr_trace.h"
//============================= SrCfgPlannerBase ========================================
SrCfgPlannerBase::SrCfgPlannerBase ( SrCfgManagerBase* cman )
:_tree1 ( cman ), _tree2 ( cman ), _path ( cman )
{
_cman = cman;
_tmpc1 = _cman->alloc();
_tmpc2 = _cman->alloc();
_solved = false;
_juststarted = false;
_curtree = 1;
}
SrCfgPlannerBase::~SrCfgPlannerBase ()
{
init ();
_cman->free ( _tmpc1 );
_cman->free ( _tmpc2 );
}
void SrCfgPlannerBase::init ()
{
_tree1.init ();
_tree2.init ();
_path.init ();
_solved = false;
_juststarted = false;
_curtree = 1;
}
void SrCfgPlannerBase::start ( const srcfg* c1, const srcfg* c2 )
{
SR_TRACE1 ( "Start...");
init ();
SR_TRACE1 ( "Tree Init...");
_tree1.init ( c1 );
_tree2.init ( c2 );
_curtree = 1; // could be 1 or 2
_juststarted = true;
SR_TRACE1 ( "Start OK.");
}
bool SrCfgPlannerBase::update_rrt ( float step, int tries, float prec )
{
SrCfgNode* n; // new node added to the current tree
SrCfgNode* nearest1; // nearest node in tree1
SrCfgNode* nearest2; // nearest node in tree2
float dist1; // distance from crand to nearest1
float dist2; // distance from crand to nearest2
srcfg* crand = _tmpc1; // the random configuration
SR_TRACE2 ( "UPDT: expanding tree " << _curtree );
if ( _juststarted )
{ _juststarted = false;
if ( _try_to_join(_tree1.root(),_tree2.root(),prec) ) return true; // FOUND
}
_cman->random ( crand );
nearest1 = _tree1.search_nearest ( crand, &dist1 );
nearest2 = _tree2.search_nearest ( crand, &dist2 );
float dist = _cman->dist(nearest1->cfg(),nearest2->cfg());
if ( dist<=step )
{ if ( _try_to_join(nearest1,nearest2,prec) ) return true; // FOUND
SR_TRACE2 ( "UPDT: not found." );
return false; // not found
}
SR_TRACE2 ( "UPDT: nearest1="<<nearest1->id()<<" nearest2="<<nearest2->id() );
SR_TRACE2 ( "UPDT: expanding..." );
if ( _curtree==1 )
{ if ( _cman->monotone ( nearest1->cfg(), crand ) )
{ n = _tree1.expand_node_safe ( nearest1, crand, step, tries, prec, dist1 );
if ( n )
{ if ( _try_to_join(n,nearest2,prec) ) return true; // FOUND
}
}
}
else
{ if ( _cman->monotone ( crand, nearest2->cfg() ) )
{ n = _tree2.expand_node_safe ( nearest2, crand, step, tries, prec, dist2 );
if ( n )
{ if ( _try_to_join(nearest1,n,prec) ) return true; // FOUND
}
}
}
_curtree = _curtree==1? 2:1;
SR_TRACE2 ( "UPDT: not found." );
return false; // not found
}
bool SrCfgPlannerBase::_try_to_join ( SrCfgNode* n1, SrCfgNode* n2, float prec )
{
bool b;
b = _cman->monotone ( n1->cfg(), n2->cfg() );
if ( !b ) return false;
b = _cman->visible ( n1->cfg(), n2->cfg(), _tmpc2, prec );
if ( !b ) return false;
_path.init ();
_tree1.get_branch ( n1, _path );
_path.revert();
_tree2.get_branch ( n2, _path );
_solved = true;
return _solved;
}
//============================= End of File ===========================================

View File

@ -0,0 +1,88 @@
# ifndef SR_CFG_PLANNER_H
# define SR_CFG_PLANNER_H
//# include <SR/sr_cfg_path.h>
//# include <SR/sr_cfg_tree.h>
# include "sr_heap.h"
# include "sr_cfg_path.h"
# include "sr_cfg_tree.h"
/*! A single-query, bidirectional, lazy and sampling-based planner */
class SrCfgPlannerBase : public SrSharedClass
{ private :
SrCfgTreeBase _tree1, _tree2;
SrCfgPathBase _path;
SrCfgManagerBase* _cman;
srcfg* _tmpc1;
srcfg* _tmpc2;
int _curtree;
bool _solved;
bool _juststarted;
struct HeapEdge { SrCfgTreeBase* tree; SrCfgNode* n1; SrCfgNode* n2; };
SrHeap<HeapEdge,int> _heap;
public :
/*! The constructor requires a configuration manager. */
SrCfgPlannerBase ( SrCfgManagerBase* cman );
/*! Destructor frees all used internal data, and unref the associated
configuration managers */
~SrCfgPlannerBase ();
/*! Returns the roadmap tree rooted at the source configuration */
SrCfgTreeBase& tree1 () { return _tree1; }
/*! Returns the roadmap tree rooted at the destination configuration */
SrCfgTreeBase& tree2 () { return _tree2; }
/*! Returns the number of nodes in both trees */
int nodes () const { return _tree1.nodes()+_tree2.nodes(); }
/*! Clears everything */
void init ();
/*! Clears everything and define the source and goal configurations.
Configurations c1 and c2 must be valid.
If a time-varying problem will be solved, configuration c1 must correspond
to the start and c2 to the goal, ie c1 happens before c2 */
void start ( const srcfg* c1, const srcfg* c2 );
/*! Returns true if a path to the goal was found, and false otherwise. */
bool solved () const { return _solved; }
/*! Returns the last path found by the planner */
SrCfgPathBase& path () { return _path; }
/*! Update one of the trees, returning true if a path was found
Parameter step is the incremental step distance, and
tries is the number of bisections to try in case of expantion failure */
bool update_rrt ( float step, int tries, float prec );
/*! Lazy version of the update method. */
bool update_lazy ( float step, int tries, float prec );
private :
bool _test_bridge ( SrCfgNode* n1, SrCfgNode* n2, float prec );
bool _try_to_join ( SrCfgNode* n1, SrCfgNode* n2, float prec );
void _heap_add_branch ( SrCfgTreeBase* tree, SrCfgNode* n );
};
/*! Planner template for user-defined configurations */
template <class C>
class SrCfgPlanner : public SrCfgPlannerBase
{ public :
SrCfgPlanner ( SrCfgManagerBase* cman )
: SrCfgPlannerBase(cman) { }
SrCfgTree<C>& tree1 () { return (SrCfgTree<C>&) SrCfgPlannerBase::tree1(); }
SrCfgTree<C>& tree2 () { return (SrCfgTree<C>&) SrCfgPlannerBase::tree2(); }
SrCfgPath<C>& path () { return (SrCfgPath<C>&) SrCfgPlannerBase::path(); }
};
//================================ End of File =================================================
# endif // SR_CFG_PLANNER_H

View File

@ -0,0 +1,137 @@
#include "precompiled.h"
# include "sr_cfg_planner.h"
//# define SR_USE_TRACE1 // not used
//# define SR_USE_TRACE2 // update
//# define SR_USE_TRACE3 // bridge
# include "sr_trace.h"
//============================= SrCfgPlannerBase ========================================
bool SrCfgPlannerBase::update_lazy ( float step, int tries, float prec )
{
SrCfgNode* n; // new node added to the current tree
SrCfgNode* nearest1; // nearest node in tree1
SrCfgNode* nearest2; // nearest node in tree2
float dist1; // distance from crand to nearest1
float dist2; // distance from crand to nearest2
srcfg* crand = _tmpc1; // the random configuration
SR_TRACE2 ( "UPDT: expanding tree " << _curtree );
if ( _juststarted )
{ _juststarted = false;
if ( _test_bridge(_tree1.root(),_tree2.root(),prec) ) return true; // FOUND
}
_cman->random ( crand );
nearest1 = _tree1.search_nearest ( crand, &dist1 );
nearest2 = _tree2.search_nearest ( crand, &dist2 );
float dist = _cman->dist(nearest1->cfg(),nearest2->cfg());
if ( dist<=step )
{ if ( _test_bridge(nearest1,nearest2,prec) ) return true; // FOUND
}
SR_TRACE2 ( "UPDT: nearest1="<<nearest1->id()<<" nearest2="<<nearest2->id() );
SR_TRACE2 ( "UPDT: expanding..." );
if ( _curtree==1 )
{ n = _tree1.expand_node ( nearest1, crand, step, tries, dist1 );
if ( n )
{ if ( _test_bridge(n,nearest2,prec) ) return true; // FOUND
}
}
else
{ n = _tree2.expand_node ( nearest2, crand, step, tries, dist2 );
if ( n )
{ if ( _test_bridge(nearest1,n,prec) ) return true; // FOUND
}
}
_curtree = _curtree==1? 2:1;
SR_TRACE2 ( "UPDT: not found." );
return false; // not found
}
void SrCfgPlannerBase::_heap_add_branch ( SrCfgTreeBase* tree, SrCfgNode* n )
{
HeapEdge e;
SrCfgNode *parent;
while ( n->parent() )
{ parent = n->parent();
if ( !parent->safe(n->parentlink()) )
{ e.tree = tree;
e.n1 = parent; // convention: e.n1 is parent of e.n2
e.n2 = n;
_heap.insert ( e, parent->level(n->parentlink()) );
}
n = parent; // move to the parent
}
}
/*! Test if the path formed by connecting node index n1 of tree 1 with
node index n2 of tree2 is a valid path. The test performs collision
detection in the edges of the path incrementing their levels. A
priority queue is used to first test edges in lower levels.
If all edges in the path become safe, a path is formed and true is returned.
Otherwise, the edge found to be invalid is deleted, the two trees are
updated to keep the remaining edges, and false is returned */
bool SrCfgPlannerBase::_test_bridge ( SrCfgNode* n1, SrCfgNode* n2, float prec )
{
// add the cfg of n2 to tree1:
SrCfgNode* n12 = _tree1.add_node ( n1, n2->cfg() );
// make priority heap where the cost is the edge level
// and add all non-safe path edges of tree 1 and tree 2 to the heap
SR_TRACE3 ( "BRIDGE: building heap..." );
_heap.init();
_heap_add_branch ( &_tree1, n12 );
_heap_add_branch ( &_tree2, n2 );
// test and increment the level of the edges in the heap
int level;
HeapEdge e;
while ( _heap.size()>0 )
{ level = _heap.lowest_cost();
e = _heap.top();
SR_TRACE3 ( "BRIDGE: heap size="<<_heap.size()<<" level="<<level<<" ..." );
if ( !e.tree->increment_edge_level ( e.n1, e.n2, prec ) ) break; // collision found
// remove and reinsert edge if not yet safe:
_heap.remove();
if ( !e.n1->safe(e.n2->parentlink()) )
_heap.insert ( e, level+1 );
}
if ( _heap.size()==0 ) // all edges were safe: path found
{ SR_TRACE3 ( "BRIDGE: path found!" );
_solved = true;
_path.init ();
_tree1.get_branch ( n1, _path );
_path.revert();
_tree2.get_branch ( n2, _path );
SR_TRACE3 ( "BRIDGE: path done." );
}
else // failed, reorganize trees
{ SR_TRACE3 ( "BRIDGE: failed, transferring subtrees..." );
if ( e.tree==&_tree1 )
{ //sr_out<<"\nTRANSFER 1: "<<e.n1->id()<<srspc<< e.n2->id()<<srspc<< n12->id()<<srspc<< n2->id()<<srnl;
_tree1.transfer_subtree ( e.n1, e.n2, n12/*joint1*/, _tree2, n2/*joint2*/ );
}
else
{ //sr_out<<"\nTRANSFER 2: "<<e.n1->id()<<srspc<< e.n2->id()<<srspc<< n2->id()<<srspc<< n12->id()<<srnl;
_tree2.transfer_subtree ( e.n1, e.n2, n2/*joint1*/, _tree1, n12/*joint2*/ );
}
SR_TRACE3 ( "BRIDGE: transfer done." );
_solved = false;
}
return _solved;
}

View File

@ -0,0 +1,558 @@
#include "precompiled.h"
# include "sr_cfg_tree.h"
//# define SR_USE_TRACE1 // expand node
# include "sr_trace.h"
//=============================== SrCfgNode ========================================
float SrCfgNode::prec ( int i ) const
{
int lev = level ( i );
return dist(i)/(float)sr_pow(2,SR_ABS(lev));
}
void SrCfgNode::get_subtree ( SrArray<SrCfgNode*>& nodes )
{
int i;
nodes.push() = this;
for ( i=0; i<_children.size(); i++ ) _children[i].node->get_subtree ( nodes );
}
void SrCfgNode::_deledge ( int e )
{
_children.remove ( e );
int i, size = _children.size();
for ( i=e; i<size; i++ )
_children[i].node->_parentlink = i;
}
void SrCfgNode::_fixplinks ()
{
int i;
for ( i=0; i<_children.size(); i++ )
_children[i].node->_parentlink = i;
for ( i=0; i<_children.size(); i++ )
_children[i].node->_fixplinks();
}
void SrCfgNode::_reroot ()
{
if ( !_parent ) return;
// new parent will have its old parent as new child:
SrCfgNode* curnode = this;
SrCfgNode* curparent = _parent;
int newparentlink = _children.size();
int oldparentlink = _parentlink;
_children.push() = _parent->_children[oldparentlink];
_children.top().node = _parent;
_parent = 0;
_parentlink = -1;
// walk towards the old root, swaping old/new parents:
int tmp;
SrCfgNode* newparent = this;
curnode = curparent;
curparent = curnode->_parent; // move to parent
while ( curparent )
{ // modify curnode:
curnode->_children[oldparentlink] = curparent->_children[curnode->_parentlink];
curnode->_children[oldparentlink].node = curparent; // parent becomes child
tmp = oldparentlink;
oldparentlink = curnode->_parentlink;
curnode->_parentlink = newparentlink;
newparentlink = tmp;
curnode->_parent = newparent;
// move to parent:
newparent = curnode;
curnode = curparent;
curparent = curparent->_parent;
}
// delete extra child of the old root:
curnode->_deledge ( oldparentlink );
curnode->_parentlink = newparentlink;
curnode->_parent = newparent;
}
//============================= SrCfgTreeBase ======================================
SrCfgTreeBase::SrCfgTreeBase ( SrCfgManagerBase* cman )
{
_cman = cman;
_cman->ref ();
_root = 0;
}
SrCfgTreeBase::~SrCfgTreeBase ()
{
int i;
for ( i=0; i<_buffer.size(); i++ )
{ _cman->free ( _buffer[i]->_cfg );
delete _buffer[i];
}
_cman->unref();
}
bool SrCfgTreeBase::check_all ( SrOutput& o )
{
int i, j;
o << "Starting check:\n";
if ( !_root ) { o<<"Check ok, but empty.\n"; return true; }
o << "Buffer size...\n";
_nodes.size ( 0 );
_root->get_subtree ( _nodes );
if ( _nodes.size()!=_buffer.size()-_freepos.size() ) goto error;
o << "Buffer indices...\n";
for ( i=0; i<_buffer.size(); i++ )
if ( _buffer[i]->_bufferid!=i ) goto error;
o << "Parent-child pointers...\n";
for ( i=0; i<_nodes.size(); i++ )
{ if ( !_nodes[i]->_parent )
{ if ( _nodes[i]!=_root ) { o<<"wrong root "; goto error; } }
else
{ if ( _nodes[i]->_parent->child(_nodes[i]->_parentlink)!=_nodes[i] )
{ o<< "wrong parent link n:" <<
_nodes[i]->id()<<" p:"<<_nodes[i]->_parent->id()<<srspc; goto error; }
}
}
o << "Children pointers...\n";
for ( i=0; i<_nodes.size(); i++ )
{ for ( j=0; j<_nodes[i]->children(); j++ )
{ if ( _nodes[i]->_children[j].node->_parent!=_nodes[i] )
{ o<<"wrong child->_parent pointer "; goto error; }
if ( _nodes[i]->_children[j].node->_parentlink!=j )
{ o<<"wrong child->_parentlink index "<<
_nodes[i]->id()<<"/"<<j<<srspc; goto error; }
}
}
o << "Check ok.\n";
return true;
error:
o << "error!\n";
return false;
}
void SrCfgTreeBase::init ()
{
_root = 0;
_freepos.size ( _buffer.size() );
int i, max = _buffer.size()-1;
for ( i=0; i<=max; i++ ) _freepos[i] = max-i;
}
void SrCfgTreeBase::init ( const srcfg* cfg )
{
init ();
add_node ( 0, cfg, 0 );
}
SrCfgNode* SrCfgTreeBase::_newnode ()
{
if ( _freepos.size()>0 )
{ return _buffer[_freepos.pop()];
}
else
{ int id = _buffer.size();
_buffer.push() = new SrCfgNode;
_buffer[id]->_cfg = _cman->alloc();
_buffer[id]->_bufferid = id;
return _buffer[id];
}
}
void SrCfgTreeBase::_delnode ( SrCfgNode* n )
{
_freepos.push() = n->_bufferid;
}
SrCfgNode* SrCfgTreeBase::add_node ( SrCfgNode* parent, const srcfg* cfg, float dist )
{
SrCfgNode* newn = _newnode ();
if ( parent ) // set parent data
{ newn->_parentlink = parent->children();
SrCfgNode::Link& l = parent->_children.push();
if ( dist<0 ) dist = _cman->dist ( parent->_cfg, cfg );
l.node = newn;
l.dist = dist;
l.level = 0;
}
else // this is the root node
{ _root = newn;
newn->_parentlink = -1;
}
_cman->copy ( newn->_cfg, cfg );
newn->_parent = parent;
newn->_children.size(0);
return newn;
}
void SrCfgTreeBase::_nearest ( SrCfgNode* n, const srcfg* c, SrCfgNode*& nearest, float& mindist )
{
// check distance:
float dist = _cman->dist ( n->cfg(), c );
if ( dist<mindist )
{ nearest = n;
mindist = dist;
}
// recurse:
int i, chsize=n->children();
for ( i=0; i<chsize; i++ )
_nearest ( n->child(i), c, nearest, mindist );
}
SrCfgNode* SrCfgTreeBase::search_nearest ( const srcfg* c, float* d )
{
if ( !_root ) return _root;
SrCfgNode* nearest;
float mindist = 1E+30f; // float range in visualc is: 3.4E +/- 38
_nearest ( _root, c, nearest, mindist );
if (d) *d = mindist;
return nearest;
}
SrCfgNode* SrCfgTreeBase::expand_node ( SrCfgNode* source, const srcfg* direction,
float step, int maxtries, float dist )
{
srcfg* csource = source->cfg();
if ( dist<0 ) dist = _cman->dist ( csource, direction );
if ( dist<0.00001f ) return 0; // too close
SrCfgNode* nnew = _newnode();
srcfg* cnew = nnew->cfg();
float t = step/dist;
if ( t>1.0f ) t = 1.0f;
while ( maxtries-->0 )
{ SR_TRACE1 ( "INS: trying to insert...");
_cman->interp ( csource, direction, t, cnew );
t /= 2;
if ( _cman->valid(cnew) )
{ SR_TRACE1 ( "INS: inserting 1 node.");
_delnode ( nnew );
return add_node ( source, cnew, -1 ); // instead of -1, could use aprox distance dist*t...
}
}
SR_TRACE1 ( "INS: no nodes inserted.");
_delnode ( nnew );
return 0;
}
SrCfgNode* SrCfgTreeBase::expand_node_safe ( SrCfgNode* source, const srcfg* direction,
float step, int maxtries, float prec, float dist )
{
srcfg* csource = source->cfg();
if ( dist<0 ) dist = _cman->dist ( csource, direction );
if ( dist<0.00001f ) return 0; // too close
SrCfgNode* nnew = _newnode();
srcfg* cnew = nnew->cfg();
SrCfgNode* ntmp = _newnode();
srcfg* ctmp = ntmp->cfg();
float t = step/dist;
if ( t>1.0f ) t = 1.0f;
if ( 0 ) // test "long" expansion
{ float dt = t;
maxtries=5;
while ( t<=1 && maxtries-->0 )
{ SR_TRACE1 ( "INS: trying to insert...");
_cman->interp ( csource, direction, t, cnew );
t += dt;
if ( _cman->valid(cnew) )
if ( _cman->visible(source->cfg(),cnew,ctmp,prec) )
{ SR_TRACE1 ( "INS: inserting 1 node.");
_delnode ( nnew );
_delnode ( ntmp );
nnew = add_node ( source, cnew, -1 ); // instead of -1, could use aprox distance dist*t...
source->_children.top().level = -1; // mark as safe (can be any <0 number)
_cman->child_added ( source->cfg(), cnew ); // notify configuration manager
SR_TRACE1 ( "INS: Ok.");
source = nnew;
if ( t>1 ) return nnew; // end
nnew = _newnode();
cnew = nnew->cfg();
ntmp = _newnode();
ctmp = ntmp->cfg();
}
}
}
else
while ( maxtries-->0 )
{ SR_TRACE1 ( "INS: trying to insert...");
_cman->interp ( csource, direction, t, cnew );
t /= 2;
if ( _cman->valid(cnew) )
if ( _cman->visible(source->cfg(),cnew,ctmp,prec) )
{ SR_TRACE1 ( "INS: inserting 1 node.");
_delnode ( nnew );
_delnode ( ntmp );
nnew = add_node ( source, cnew, -1 ); // instead of -1, could use aprox distance dist*t...
source->_children.top().level = -1; // mark as safe (can be any <0 number)
_cman->child_added ( source->cfg(), cnew ); // notify configuration manager
SR_TRACE1 ( "INS: Ok.");
return nnew;
//following test not ok:
//return expand_node_safe ( nnew, direction, step, maxtries, prec, -1 );
}
}
SR_TRACE1 ( "INS: no nodes inserted.");
_delnode ( nnew );
_delnode ( ntmp );
return 0;
}
void SrCfgTreeBase::get_branch ( SrCfgNode* n, SrArray<SrCfgNode*>& nodes )
{
while ( n )
{ nodes.push() = n;
n = n->parent();
}
}
void SrCfgTreeBase::get_branch ( SrCfgNode* n, SrCfgPathBase& path )
{
while ( n )
{ path.push ( n->cfg() );
n = n->parent();
}
}
void SrCfgTreeBase::get_nodes ( SrArray<SrCfgNode*>& nodes )
{
nodes.size(0);
if ( !_root ) return;
_root->get_subtree ( nodes );
}
// level k : (2^k)+1 tot tests, 2^(k-1) new tests, 2^k segments
// safe edge : dist/segments < collision_precision
bool SrCfgTreeBase::increment_edge_level ( SrCfgNode* n1, SrCfgNode* n2, float prec )
{
SrCfgNode::Link& l = n1->_children[n2->parentlink()];
SrCfgNode* tmpnode = _newnode();
srcfg* ct = tmpnode->cfg();
srcfg* ct0 = n1->cfg();
srcfg* ct1 = n2->cfg();
// we will test the next level k:
int k = 1 + l.level;
//sr_out<<"K: "<<k<<srnl;
// test the 2^(k-1) new tests of the new level k:
float segs = (float) sr_pow ( 2, k );
//float dseg = (l.dist/segs<=prec)? prec : 1.0f/segs; // test if last pass
float dseg = 1.0f/segs;
float dt = dseg*2.0f;
float t;
for ( t=dseg; t<1.0f; t+=dt )
{ //sr_out<<"t: "<<t<<srnl;
_cman->interp ( ct0, ct1, t, ct );
if ( !_cman->valid(ct) ) { _delnode(tmpnode); return false; }
}
// ok, promote edge to level k and check if it can be marked as safe:
l.level = k;
//sr_out<<"dist:"<<l.dist<<" segs:"<<segs<<" prec:"<<prec<<srnl;
if ( l.dist/segs<=prec ) l.level=-l.level; // mark as safe
_delnode(tmpnode);
return true;
}
void SrCfgTreeBase::transfer_subtree ( SrCfgNode* n1, SrCfgNode* n2, SrCfgNode* joint1,
SrCfgTreeBase& tree2, SrCfgNode* joint2 )
{
// delete edge [n1,n2]:
n1->_deledge ( n2->parentlink() );
n2->_parent=0; // n2 is now the root of a disconnected subtree, still using _buffer
n2->_parentlink=-1;
// save the nodes of subtree n2:
_nodes.size ( 0 );
n2->get_subtree ( _nodes );
// reorder subtree to make joint1 the new root:
joint1->_reroot();
// attach joint1 children as children of joint2:
while ( joint1->_children.size() )
{ SrCfgNode::Link& l = joint1->_children.pop();
l.node->_parent = joint2;
l.node->_parentlink = joint2->_children.size();
joint2->_children.push() = l;
}
// finally reorganize buffers; all in _nodes change buffer, except joint1 deleted:
int tmpi;
SrCfgNode *n, *newn, *tmpn;
while ( _nodes.size() )
{ n = _nodes.pop();
_delnode (n);
if ( n!=joint1 ) // "swap" with a new entry in tree2.buffer
{ newn = tree2._newnode();
SR_SWAPT(_buffer[n->_bufferid],tree2._buffer[newn->_bufferid],tmpn);
SR_SWAPT(n->_bufferid,newn->_bufferid,tmpi);
}
}
}
void SrCfgTreeBase::output ( SrOutput& o, bool printcfg, SrCfgNode* n )
{
int i, j;
_nodes.size(0);
if ( !n ) n = _root;
n->get_subtree ( _nodes );
o << "\nSrCfgTree " << _nodes.size() << srnl;
for ( i=0; i<_nodes.size(); i++ )
{
n = _nodes[i];
//t._cman->output ( o, t.cfg(i) );
o << "Node:" << n->id();
if ( n->parent() ) o << " parent:"<<n->parent()->id();
else o<<" root ";
o << " Children:";
for ( j=0; j<n->children(); j++ )
{ o << srspc << n->child(j)->id()
<< ":" << j
<< "/" << n->child(j)->parentlink();
}
if ( printcfg )
{ o << "\nData: ";
_cman->output(o,n->cfg());
o <<"\n";
}
o << srnl;
}
}
//================================ friends =================================================
/*
void _inpnode ( SrInput& i, SrCfgNode* n );
void SrCfgTreeBase::_inpnode ( SrInput& in, SrCfgNode* n )
{
int i, size;
_cman->input ( in, n->_cfg );
in.get_token(); // "p"
in >> n->_parent;
in.get_token(); // "c"
in >> size;
in.get_token(); // ":"
n->_children.size(size);
for ( i=0; i<size; i++ )
{ in >> n->_children[i].dist;
in >> n->_children[i].node;
in >> n->_children[i].level;
}
}*/
/*! read the roadmap */
/*friend SrInput& operator>> ( SrInput& in, SrCfgTreeBase& t );
SrInput& operator>> ( SrInput& in, SrCfgTreeBase& t )
{
int i, size;
t.init();
in.get_token();
if ( in.last_token()!="SrCfgTree" ) return in;
in >> size;
for ( i=0; i<size; i++ )
{
// make sure next entry is valid
if ( i==t._nodes.size() ) t._pushnode();
// read node
t._cman->input ( in, t.cfg(i) );
t._inpnode ( in, t.node(i) );
}
t._nsize = size;
return in;
}
*/
//================================ End of File =================================================
/*
This method takes O(k*n) time. It looks into all nodes of the graph,
updating the sorted list with the k closest nodes, which is returned.
SrArray<SrCfgTreeBase::NodeDist>& SrCfgTreeBase::search_k_nearests ( const srcfg* c, int k )
{
_snodes.ensure_capacity(k);
_snodes.size(0);
SrRoadmapNode* n = _graph.first_node();
if ( !n ) return _snodes;
NodeDist nd;
int i, worse=-1;
SrListIterator<SrRoadmapNode> it(n);
for ( it.first(); it.inrange(); it.next() )
{ if ( it.get()->blocked() ) continue;
nd.n = it.get();
nd.d = _cman->get_distance ( it.get()->c, c );
if ( _snodes.size()<k )
{ if ( worse<0 )
worse=_snodes.size();
else
if ( nd.d < _snodes[worse].d ) worse=_snodes.size();
_snodes.push() = nd;
}
else if ( nd.d < _snodes[worse].d )
{ _snodes[worse] = nd;
worse=0;
for ( i=1; i<k; i++ )
if ( _snodes[i].d > _snodes[worse].d ) worse=i;
}
}
if ( _snodes.size()>1 ) _snodes.sort(nd_compare);
return _snodes;
}
*/

View File

@ -0,0 +1,169 @@
# ifndef SR_CFG_TREE_H
# define SR_CFG_TREE_H
# include "sr_set.h"
//# include <SR/sr_cfg_manager.h>
# include "sr_cfg_manager.h"
# include "sr_cfg_path.h"
class SrCfgTreeBase;
/*! SrCfgNode is a node of SrCfgTree. Each edge has a
level based on the number of collision checks performed:
Level TotTests NewTests Segments
0 2 0 1
1 3 1 2
2 5 2 4
3 9 4 8
k (2^k)+1 2^(k-1) 2^k
Safe edge is achieved if dist/(2^k) < collision precision,
and level is marked as safe when this occurs */
class SrCfgNode
{ private :
int _bufferid;
srcfg* _cfg;
SrCfgNode* _parent;
int _parentlink;
struct Link { SrCfgNode* node; float dist; int level; };
SrArray<Link> _children;
friend SrCfgTreeBase;
public :
SrCfgNode* parent () const { return _parent; }
int parentlink () const { return _parentlink; } // -1 if root node
int children () const { return _children.size(); }
srcfg* cfg () const { return _cfg; }
SrCfgNode* child ( int i ) const { return _children[i].node; }
float prec ( int i ) const; // dist/2^abs(level)
float dist ( int i ) const { return _children[i].dist; }
int level ( int i ) const { return _children[i].level; } // <0 if safe
bool safe ( int i ) const { return level(i)<0?true:false; }
void get_subtree ( SrArray<SrCfgNode*>& nodes ); // add the node itself and all subtree
int id () const { return _bufferid; }
private :
void _deledge ( int e );
void _fixplinks ();
void _reroot ();
};
/*! The tree-based roadmap */
class SrCfgTreeBase
{ private :
SrCfgManagerBase* _cman; // configuration manager
SrCfgNode* _root; // the root of the tree
SrArray<SrCfgNode*> _buffer; // buffer of nodes
SrArray<int> _freepos; // free positions in buffer
SrArray<SrCfgNode*> _nodes; // for temporary use
public :
/*! The constructor requires a valid configuration manager for
dealing with the user-defined configuration */
SrCfgTreeBase ( SrCfgManagerBase* cman );
/*! Destructor frees all used internal data, and unref the
associated configuration manager */
~SrCfgTreeBase ();
/* Returns a pointer to the used configuration manager */
SrCfgManagerBase* cman () const { return _cman; }
/*! Debug tool to test all internal pointers, should always return true */
bool check_all ( SrOutput& o );
/*! Set the tree as an empty tree */
void init ();
/*! Set the current tree to be a tree containing
only the given node as the root of the tree. */
void init ( const srcfg* cfg );
/*! Add a node to the tree, as a child to the provided parent.
If dist<0 (the default) the parent-cfg distance is retrieved
by using the associated configuration manager.
The level of the parent-cfg edge is set to 0.
Returns the new node created. */
SrCfgNode* add_node ( SrCfgNode* parent, const srcfg* cfg, float dist=-1 );
/*! Returns the number of nodes in the tree */
int nodes () const { return _buffer.size()-_freepos.size(); }
/*! Returns the root node or null if tree empty */
SrCfgNode* root () const { return _root; }
/*! Performs a O(n) search over all nodes and returns the node
with the closest configuration to c. Null is returned if the
tree is empty. If given pointer d is not null, the nearest
distance is returned in d */
SrCfgNode* search_nearest ( const srcfg* c, float* d=0 );
/*! Add a child node from node source, walking a distance of step in direction
to configuration direction. If the node is not valid, step is divided
by 2 until the node becomes valid, or until max_tries tentatives are performed.
Note that here edges are not tested for validity, only nodes (lazy evaluation).
The new node is returned, or null in case no node could be added.
If parameter dist>0, it will be used as being dist(source->cfg(),direction). */
SrCfgNode* expand_node ( SrCfgNode* source, const srcfg* direction, float step, int maxtries, float dist=-1 );
/*! Same as expand node, but here the expansion is done only if the new edge is
valid, therefore the edge visibility tests is called */
SrCfgNode* expand_node_safe ( SrCfgNode* source, const srcfg* direction,
float step, int maxtries, float prec, float dist=-1 );
/*! Returns a list of nodes forming the tree branch joining node n to the root.
The first element of the array is always n, and the last is always the root node.
The array size is not set to zero, ie, the indices are apended to the array. */
void get_branch ( SrCfgNode* n, SrArray<SrCfgNode*>& nodes );
/*! Same as the other get_branch() method, but the result goes to a path object */
void get_branch ( SrCfgNode* n, SrCfgPathBase& path );
/*! Returns an unordered list with all nodes in the tree */
void get_nodes ( SrArray<SrCfgNode*>& nodes );
/*! Performs 2^(k-1) collision tests to check if the edge [n1,n2] can be promoted
to level k = n1->level(n2->parentlink())+1.
False is returned in case the level could not be promoted due to a collision.
True is returned if the level could be promoted, and in this case, the edge
will be marked as safe if the new level achieves the required precision
with the following test: n1->dist(n2->parentlink())/2^k < prec */
bool increment_edge_level ( SrCfgNode* n1, SrCfgNode* n2, float prec );
/*! Removes the edge [n1,n2] and transfers the disconnected subtree to tree2,
by "identifying" joint1 with joint2 of tree2 and reorganizing the subtree
so as to make joint2 the subtree root.
Important: n1 must be parent of n2, and joint1 must be in n2 subtree */
void transfer_subtree ( SrCfgNode* n1, SrCfgNode* n2, SrCfgNode* joint1,
SrCfgTreeBase& tree2, SrCfgNode* joint2 );
/*! Output of the roadmap tree for inspection:
- if printcfg is true, node data is also sent to output
- n is the root of the subgraph to print (if 0, the real root is taken)*/
void output ( SrOutput& o, bool printcfg=true, SrCfgNode* n=0 );
private :
SrCfgNode* _newnode ();
void _delnode ( SrCfgNode* n );
srcfg* _tmpnode ();
void _nearest ( SrCfgNode* n, const srcfg* c, SrCfgNode*& nearest, float& mindist );
};
/*! This template version performs all required type casts to bind the
tree to the user-defined configuration class. Class C must be managed
by the used configuration manager */
template <class C>
class SrCfgTree : public SrCfgTreeBase
{ public :
/*! Constructor receiving a user-defined manager */
SrCfgTree ( SrCfgManagerBase* cman ) : SrCfgTreeBase(cman) { }
/*! Automatically allocates a manager using SrCfgManager<C> template */
SrCfgTree () : SrCfgTreeBase ( new SrCfgManager<C> ) { }
/*! Returns the configuration at node index n */
C* cfg ( SrCfgNode* n ) const { return (C*)n->cfg(); }
};
//================================ End of File =================================================
# endif // SR_CFG_TREE_H

View File

@ -0,0 +1,59 @@
# ifndef SR_CLASS_MANAGER_H
# define SR_CLASS_MANAGER_H
/** \file sr_class_manager.h
* Generic way to allocate, io and compare classes */
# include "sr_input.h"
# include "sr_output.h"
# include "sr_shared_class.h"
class SrClassManagerBase : public SrSharedClass
{ protected :
virtual ~SrClassManagerBase() {};
public : // callbacks
virtual void* alloc ()=0;
virtual void* alloc ( const void* obj )=0;
virtual void free ( void* obj )=0;
virtual void output ( SrOutput& o, const void* obj ) { }
virtual void input ( SrInput& i, void* obj ) { }
virtual int compare ( const void* obj1, const void* obj2 ) { return 0; }
};
/*! Example of an implementation of a class to be automatically managed
with SrClassManager<MyData> :
class MyData
{ public :
MyData ();
MyData ( const MyData& d );
~MyData ();
friend SrOutput& operator<< ( SrOutput& out, const MyData& d );
friend SrInput& operator>> ( SrInput& inp, MyData& d );
friend int sr_compare ( const MyData* d1, const MyData* d2 );
};*/
template <class X>
class SrClassManager : public SrClassManagerBase
{ protected :
virtual ~SrClassManager<X> () {}
public :
virtual void* alloc () { return (void*) new X; }
virtual void* alloc ( const void* obj ) { return (void*) new X(*((X*)obj)); }
virtual void free ( void* obj ) { delete (X*) obj; }
virtual void output ( SrOutput& o, const void* obj ) { o<<*((const X*)obj); }
virtual void input ( SrInput& i, void* obj ) { i>>*((X*)obj); }
virtual int compare ( const void* obj1, const void* obj2 )
{ return sr_compare ( (const X*)obj1, (const X*)obj2 ); }
};
//============================== end of file ===============================
# endif // SR_CLASS_MANAGER_H

108
source/dcdt/se/sr_color.cpp Normal file
View File

@ -0,0 +1,108 @@
#include "precompiled.h"
# include "sr_color.h"
//========================================= static =======================================
const SrColor SrColor::black (0,0,0);
const SrColor SrColor::red (255,0,0);
const SrColor SrColor::green (0,255,0);
const SrColor SrColor::yellow (255,255,0);
const SrColor SrColor::blue (0,0,255);
const SrColor SrColor::magenta(255,0,255);
const SrColor SrColor::cyan (0,255,255);
const SrColor SrColor::white (255,255,255);
const SrColor SrColor::gray (127,127,127);
//========================================= SrColor =======================================
void SrColor::set ( srbyte x, srbyte y, srbyte z, srbyte w )
{
r=x; g=y; b=z; a=w;
}
void SrColor::set ( int x, int y, int z, int w )
{
r=(srbyte)x;
g=(srbyte)y;
b=(srbyte)z;
a=(srbyte)w;
}
void SrColor::set ( float x, float y, float z, float w )
{
r = (srbyte) ( x*255.0f );
g = (srbyte) ( y*255.0f );
b = (srbyte) ( z*255.0f );
a = (srbyte) ( w*255.0f );
}
void SrColor::set ( const char* s )
{
switch ( s[0] )
{ case 'b' : *this = s[2]=='a'? black:blue; break;
case 'r' : *this = red; break;
case 'g' : *this = s[2]=='e'? green:gray; break;
case 'y' : *this = yellow; break;
case 'm' : *this = magenta; break;
case 'c' : *this = cyan; break;
case 'w' : *this = white; break;
}
}
// we dont do double versions to avoid automatic typecasts complications...
void SrColor::get ( float f[4] ) const
{
f[0] = ((float)r) / 255.0f;
f[1] = ((float)g) / 255.0f;
f[2] = ((float)b) / 255.0f;
f[3] = ((float)a) / 255.0f;
}
void SrColor::get ( int i[4] ) const
{
i[0] = (int)r;
i[1] = (int)g;
i[2] = (int)b;
i[3] = (int)a;
}
void SrColor::get ( srbyte x[4] ) const
{
x[0] = r;
x[1] = g;
x[2] = b;
x[3] = a;
}
bool operator == ( const SrColor &c1, const SrColor &c2 )
{
return c1.r==c2.r && c1.g==c2.g &&c1.b==c2.b && c1.a==c2.a? true:false;
}
bool operator != ( const SrColor &c1, const SrColor &c2 )
{
return c1.r==c2.r && c1.g==c2.g &&c1.b==c2.b && c1.a==c2.a? false:true;
}
SrColor lerp ( const SrColor &c1, const SrColor &c2, float t )
{
SrColor c;
c.r = (srbyte) (SR_LERP ( float(c1.r), float(c2.r), t ) + 0.5f);
c.g = (srbyte) (SR_LERP ( float(c1.g), float(c2.g), t ) + 0.5f);
c.b = (srbyte) (SR_LERP ( float(c1.b), float(c2.b), t ) + 0.5f);
c.a = (srbyte) (SR_LERP ( float(c1.a), float(c2.a), t ) + 0.5f);
return c;
}
SrOutput& operator<< ( SrOutput& o, const SrColor& c )
{
return o << c.r <<' '<< c.g <<' '<< c.b <<' '<< c.a;
}
SrInput& operator>> ( SrInput& in, SrColor& c )
{
return in >> c.r >> c.g >> c.b >> c.a;
}
//=================================== End of File ==========================================

99
source/dcdt/se/sr_color.h Normal file
View File

@ -0,0 +1,99 @@
# ifndef SR_COLOR_H
# define SR_COLOR_H
/** \file sr_color.h
* A color definition
*/
# include "sr_input.h"
# include "sr_output.h"
/*! \class SrColor sr_color.h
\brief specifies a color
SrColor specifies a color using 8 bits for each basic color (red,green,blue)
and more 8 bits for the alpha (the opacity). In this way, each component can
have a value from 0 to 255 and the total class has a sizeof of 4 bytes.
The default constructor initializes with values (r,g,b,a)=(127,127,127,255). */
class SrColor
{ public :
static const SrColor black, //!< black color (0,0,0)
red, //!< red color (255,0,0)
green, //!< green color (0,255,0)
yellow, //!< yellow color (255,255,0)
blue, //!< blue color (0,0,255)
magenta, //!< magenta color (255,0,255)
cyan, //!< cyan color (0,255,255)
white, //!< white color (255,255,255)
gray; //!< gray color (127,127,127)
srbyte r; //!< r component, in {0,...,255}, default is 127
srbyte g; //!< g component, in {0,...,255}, default is 127
srbyte b; //!< b component, in {0,...,255}, default is 127
srbyte a; //!< a component, in {0,...,255}, default is 255, that is full opacity
public :
/*! Default constructor. Initializes with color gray. */
SrColor () { *this=gray; }
/*! Constructor setting all components. */
SrColor ( srbyte x, srbyte y, srbyte z, srbyte w=255 ) { set(x,y,z,w); }
/*! Constructor setting all components. */
SrColor ( int x, int y, int z, int w=255 ) { set(x,y,z,w); }
/*! Constructor setting all components with float types. */
SrColor ( float x, float y, float z, float w=1.0f ) { set(x,y,z,w); }
/*! Constructor from a 4 dimension float pointer. */
SrColor ( const float v[4] ) { set(v); }
/*! Sets the components of the color, the alpha value has a default parameter of 255. */
void set ( srbyte x, srbyte y, srbyte z, srbyte w=255 );
/*! Sets the components of the color with integer values also betwenn 1 and 255,
the alpha value has a default parameter of 255. */
void set ( int x, int y, int z, int w=255 );
/*! Sets the components of the color with float values, each one inside [0.0,1.0],
the alpha value has a default parameter of 1.0. */
void set ( float x, float y, float z, float w=1.0f );
/*! Sets the components from and array of four floats. */
void set ( const float v[4] ) { set(v[0],v[1],v[2],v[3]); }
/*! Sets the color with a string containing one of the following:
black, red, green, yellow, blue, magenta, cyan, white, gray */
void set ( const char* s );
/*! Put the four components in f[], translating each one to range [0.0,1.0] */
void get ( float f[4] ) const;
/*! Put the four components in i[], each component varying from 0 to 255. */
void get ( int i[4] ) const;
/*! Put the four components in b[], each component varying from 0 to 255. */
void get ( srbyte b[4] ) const;
/*! Comparison equal operator. */
friend bool operator == ( const SrColor &c1, const SrColor &c2 );
/*! Comparison difference operator. */
friend bool operator != ( const SrColor &c1, const SrColor &c2 );
/*! Interpolates two colors. */
friend SrColor lerp ( const SrColor &c1, const SrColor &c2, float t );
/*! Outputs in format 'r g b a'. */
friend SrOutput& operator<< ( SrOutput& o, const SrColor& v );
/*! Reads from format 'r g b a'. */
friend SrInput& operator>> ( SrInput& in, SrColor& v );
};
//================================ End of File =================================================
# endif // SR_COLOR_H

View File

@ -0,0 +1,55 @@
#include "precompiled.h"
# include "sr_cylinder.h"
# include "sr_box.h"
//================================== SrCylinder ====================================
const char* SrCylinder::class_name = "Cylinder";
SrCylinder::SrCylinder () : a(SrVec::null), b(SrVec::i)
{
radius = 0.1f;
}
SrCylinder::SrCylinder ( const SrCylinder& c ) : a(c.a), b(c.b)
{
radius = c.radius;
}
void SrCylinder::get_bounding_box ( SrBox& box ) const
{
SrVec va = b-a;
va.normalize();
SrVec vr1;
if ( angle(SrVec::i,va)<0.1f )
vr1 = cross ( SrVec::j, va );
else
vr1 = cross ( SrVec::i, va );
SrVec vr2 = cross ( vr1, va );
vr1.len ( radius );
vr2.len ( radius );
box.set_empty();
box.extend ( a+vr1 );
box.extend ( a-vr1 );
box.extend ( a+vr2 );
box.extend ( a-vr2 );
box.extend ( b+vr1 );
box.extend ( b-vr1 );
box.extend ( b+vr2 );
box.extend ( b-vr2 );
}
SrOutput& operator<< ( SrOutput& o, const SrCylinder& c )
{
return o << c.a << srspc << c.b << srspc << c.radius;
}
SrInput& operator>> ( SrInput& in, SrCylinder& c )
{
return in >> c.a >> c.b >> c.radius;
}
//================================ EOF =================================================

View File

@ -0,0 +1,46 @@
# ifndef SR_CYLINDER_H
# define SR_CYLINDER_H
/** \file sr_cylinder.h
* a cylinder
*/
# include "sr_vec.h"
class SrBox;
/*! \class SrCylinder sr_cylinder.h
\brief a cylinder
SrCylinder represents a cylinder based on its endpoints and radius.
By default, the cylinder has endpoints (0,0,0) and (1,0,0) and radius 0.1*/
class SrCylinder
{ public :
SrPnt a, b;
float radius;
static const char* class_name; //!< constain the static string "Cylinder"
public :
/*! Constructs a cylinder with endpoints (0,0,0) and (1,0,0) and radius 1 */
SrCylinder ();
/*! Copy constructor */
SrCylinder ( const SrCylinder& c );
/*! Returns the bounding box of all vertices used. The returned box can be empty. */
void get_bounding_box ( SrBox &b ) const;
/*! Outputs in format "p1 p2 radius " */
friend SrOutput& operator<< ( SrOutput& o, const SrCylinder& c );
/*! Input from format "p1 p2 radius " */
friend SrInput& operator>> ( SrInput& in, SrCylinder& c );
};
//================================ End of File =================================================
# endif // SR_SCENE_CYLINDER_H

55
source/dcdt/se/sr_deque.h Normal file
View File

@ -0,0 +1,55 @@
# ifndef SR_DEQUE_H
# define SR_DEQUE_H
/** \file sr_deque.h
* double-ended queue */
# include "sr_array.h"
/*! \class SrDeque sr_deque.h
\brief double-ended queue
A double-ended queue templete based on SrArray. */
template <class X>
class SrDeque // used by the funnel algorithm
{ private :
SrArray<X> _array;
int _base;
bool _topmode;
public :
SrDeque () { _base=0; _topmode=true; }
int size () { return _array.size()-_base; }
void init ( int cap ) { _array.ensure_capacity(cap); _array.size(cap/2); _base=_array.size(); }
void init () { _array.size(_array.capacity()/2); _base=_array.size(); }
void compress () { _array.remove(0,_base); _base=0; _array.compress(); }
X& top ( int i ) { return _array[_array.size()-i-1]; }
X& top () { return _array.top(); }
X& popt () { return _array.pop(); }
X& pusht () { return _array.push(); }
X& bottom ( int i ) { return _array[_base+i]; }
X& bottom () { return _array[_base]; }
X& popb () { return _array[_base++]; }
X& pushb ()
{ if (_base==0) { _base=_array.size(); _array.insert(0,_base); }
return _array[--_base];
}
X& operator[] ( int i ) { return _array[_base+i]; }
public :
bool top_mode () const { return _topmode; }
void top_mode ( bool b ) { _topmode=b; }
X& get ( int i ) { return _topmode? top(i):bottom(i); }
X& get () { return _topmode? top():bottom(); }
X& pop () { return _topmode? popt():popb(); }
X& push () { return _topmode? pusht():pushb(); }
};
//============================== end of file ===============================
#endif // SR_DEQUE_H

188
source/dcdt/se/sr_euler.cpp Normal file
View File

@ -0,0 +1,188 @@
#include "precompiled.h"
# include "sr_euler.h"
# include "sr_mat.h"
# include <math.h>
//# define SR_USE_TRACE1
# include "sr_trace.h"
# define ISZERO(a) ( (a)>-(srtiny) && (a)<(srtiny) )
# define EQUAL(a,b) ( ( (a)>(b)? ((a)-(b)):((b)-(a)) )<=(srtiny) )
# define GETSINCOS double cx=cos(rx); double cy=cos(ry); double cz=cos(rz); \
double sx=sin(rx); double sy=sin(ry); double sz=sin(rz)
# define ATAN2(x,y) (float) atan2 ( (double)x, (double)y )
# define NORM(x,y) sqrt(double(x)*double(x)+double(y)*double(y))
//============================ Get Angles ================================
void sr_euler_angles ( int order, const SrMat& m, float& rx, float& ry, float& rz )
{
switch ( order )
{ case 123: sr_euler_angles_xyz(m,rx,ry,rz); break;
case 132: sr_euler_angles_xzy(m,rx,ry,rz); break;
case 213: sr_euler_angles_yxz(m,rx,ry,rz); break;
case 231: sr_euler_angles_yzx(m,rx,ry,rz); break;
case 312: sr_euler_angles_zxy(m,rx,ry,rz); break;
case 321: sr_euler_angles_zyx(m,rx,ry,rz); break;
default: rx=ry=rz=0;
}
}
void sr_euler_angles_xyz ( const SrMat& m, float& rx, float& ry, float& rz )
{
ry = ATAN2 ( -m[2], NORM(m[0],m[1]) );
rx = ATAN2 ( m[6], m[10] );
rz = ATAN2 ( m[1], m[0] );
}
void sr_euler_angles_xzy ( const SrMat& m, float& rx, float& ry, float& rz )
{
rx = ATAN2 ( -m[9], m[5] );
ry = ATAN2 ( -m[2], m[0] );
rz = ATAN2 ( m[1], NORM(m[5],m[9]) );
}
void sr_euler_angles_yxz ( const SrMat& m, float& rx, float& ry, float& rz )
{
rx = ATAN2 ( m[6], NORM(m[2],m[10]) );
ry = ATAN2 ( -m[2], m[10] );
rz = ATAN2 ( -m[4], m[5] );
}
void sr_euler_angles_yzx ( const SrMat& m, float& rx, float& ry, float& rz )
{
rz = ATAN2 ( -m[4], NORM(m[0],m[8]) );
rx = ATAN2 ( m[6], m[5] );
ry = ATAN2 ( m[8], m[0] );
}
void sr_euler_angles_zxy ( const SrMat& m, float& rx, float& ry, float& rz )
{
rx = ATAN2 ( -m[9], NORM(m[1],m[5]) );
rz = ATAN2 ( m[1], m[5] );
ry = ATAN2 ( m[8], m[10] );
}
void sr_euler_angles_zyx ( const SrMat& m, float& rx, float& ry, float& rz )
{
ry = ATAN2 ( m[8], NORM(m[0],m[4]) );
rx = ATAN2 ( -m[9], m[10] );
rz = ATAN2 ( -m[4], m[0] );
}
//============================ Get Mat ================================
void sr_euler_mat ( int order, SrMat& m, float rx, float ry, float rz )
{
switch ( order )
{ case 123: sr_euler_mat_xyz(m,rx,ry,rz); break;
case 132: sr_euler_mat_xzy(m,rx,ry,rz); break;
case 213: sr_euler_mat_yxz(m,rx,ry,rz); break;
case 231: sr_euler_mat_yzx(m,rx,ry,rz); break;
case 312: sr_euler_mat_zxy(m,rx,ry,rz); break;
case 321: sr_euler_mat_zyx(m,rx,ry,rz); break;
default: m.identity();
}
}
void sr_euler_mat_xyz ( SrMat& m, float rx, float ry, float rz )
{
GETSINCOS;
// the following is the same as: R=Rx*Ry*Rz (in column-major format)
m[0]=float(cy*cz); m[1]=float(cy*sz); m[2]=float(-sy);
m[4]=float(-cx*sz+sx*sy*cz); m[5]=float(cx*cz+sx*sy*sz); m[6]=float(sx*cy);
m[8]=float(sx*sz+cx*sy*cz); m[9]=float(-sx*cz+cx*sy*sz); m[10]=float(cx*cy);
}
void sr_euler_mat_xzy ( SrMat& m, float rx, float ry, float rz )
{
GETSINCOS;
// the following is the same as: R=Rx*Rz*Ry (in column-major format)
m[0]=float(cy*cz); m[1]=float(sz); m[2]=float(-cz*sy);
m[4]=float(-cx*sz*cy+sx*sy); m[5]=float(cx*cz); m[6]=float(cx*sy*sz+sx*cy);
m[8]=float(sx*sz*cy+cx*sy); m[9]=float(-sx*cz); m[10]=float(-sx*sy*sz+cx*cy);
}
void sr_euler_mat_yxz ( SrMat& m, float rx, float ry, float rz )
{
GETSINCOS;
// the following is the same as: R=Ry*Rx*Rz (in column-major format)
m[0]=float(cy*cz-sx*sy*sz); m[1]=float(cy*sz+sx*sy*cz); m[2]=float(-cx*sy);
m[4]=float(-cx*sz); m[5]=float(cx*cz); m[6]=float(sx);
m[8]=float(cz*sy+sx*cy*sz); m[9]=float(sy*sz-sx*cy*cz); m[10]=float(cx*cy);
}
void sr_euler_mat_yzx ( SrMat& m, float rx, float ry, float rz )
{
GETSINCOS;
// the following is the same as: R=Ry*Rz*Rx (in column-major format)
m[0]=float(cy*cz); m[1]=float(cx*cy*sz+sx*sy); m[2]=float(cy*sz*sx-cx*sy);
m[4]=float(-sz); m[5]=float(cx*cz); m[6]=float(cz*sx);
m[8]=float(sy*cz); m[9]=float(cx*sy*sz-cy*sx); m[10]=float(sx*sy*sz+cx*cy);
}
void sr_euler_mat_zxy ( SrMat& m, float rx, float ry, float rz )
{
GETSINCOS;
// the following is the same as: R=Rz*Rx*Ry (in column-major format)
m[0]=float(cy*cz+sx*sy*sz); m[1]=float(cx*sz); m[2]=float(-sy*cz+sx*cy*sz);
m[4]=float(-sz*cy+sx*sy*cz); m[5]=float(cx*cz); m[6]=float(sy*sz+sx*cy*cz);
m[8]=float(cx*sy); m[9]=float(-sx); m[10]=float(cx*cy);
}
void sr_euler_mat_zyx ( SrMat& m, float rx, float ry, float rz )
{
GETSINCOS;
// the following is the same as: R=Rz*Ry*Rx (in column-major format)
m[0]=float(cy*cz); m[1]=float(cx*sz+cz*sy*sx); m[2]=float(sx*sz-cx*cz*sy);
m[4]=float(-sz*cy); m[5]=float(cx*cz-sx*sy*sz); m[6]=float(sx*cz+cx*sy*sz);
m[8]=float(sy); m[9]=float(-sx*cy); m[10]=float(cx*cy);
}
//================================ EOF ===================================
/* Math:
|1 0 0| |cy 0 -sy| | cz sz 0|
Rx=|0 cx sx| Ry=| 0 1 0| Rz=|-sz cz 0|
|0 -sx cx| |sy 0 cy| | 0 0 1|
| cy 0 -sy| |cycz cysz -sy| | cycz sz -czsy|
RxRy = |sxsy cx sxcy| RyRz = | -sz cz 0| RzRy = |-szcy cz sysz|
|cxsy -sx cxcy| |sycz sysz cy| | sy 0 cy|
| cycz cysz -sy | | m0 m1 m2 |
RxRyRz = |-cxsz+sxsycz cxcz+sxsysz sx*cy | = | m4 m5 m6 |
| sxsz+cxsycz -sxcz+cxsysz cx*cy | | m8 m9 m10|
X: sx/cx = m6/m10
Z: sz/cz = m1/m0
Y: cy^2*cz^2 + cy^2*sz^2 = m0^2+m1^2 => cy = norm(m0,m1) => sy/cy = -m2/norm(m0,m1)
| cycz sz -czsy | | m0 m1 m2 |
RxRzRy = | -cxszcy+sxsy cxcz cxsysz+sxcy | = | m4 m5 m6 |
| sxszcy+cxsy -sxcz -sxsysz+cxcy | | m8 m9 m10|
X: -sx/cx = m9/m5
Y: -sy/cy = m2/m0
Z: cx^2*cz^2 + cz^2*sx^2 = m5^2+m9^2 => cz = norm(m5,m9) => sz/cz = m1/norm(m5,m9)
| cycz+sxsysz cxsz -sycz+sxcysz | | m0 m1 m2 |
RzRxRy = | -szcy+sxsycz cxcz sysz+sxcycz | = | m4 m5 m6 |
| cxsy -sx cxcy | | m8 m9 m10|
Z: sz/cz = m1/m5
Y: sy/cy = m8/m10
X: cx^2*sz^2 + cx^2*cz^2 = m1^2+m5^2 => cx = norm(m1,m5) => sx/cx = -m9/norm(m1,m5)
| cycz cxsz+czsysx sxsz-cxczsy | | m0 m1 m2 |
RzRyRx = | -szcy cxcz-sxsysz sxcz+cxsysz | = | m4 m5 m6 |
| sy -sxcy cxcy | | m8 m9 m10|
X: -sx/cx = m9/m10
Z: -sz/cz = m4/m0
Y: cy^2*cz^2 + sz^2*cy^2 = m0^2+m4^2 => cy = norm(m0,m4) => sy/cy = m8/norm(m0,m4)
(Euler is fixed axis, the "inver order" becomes moving axis)
*/
//================================ EOF ===================================

41
source/dcdt/se/sr_euler.h Normal file
View File

@ -0,0 +1,41 @@
/** \file sr_euler.h
* euler angles tools */
# ifndef SR_EULER_H
# define SR_EULER_H
//================================ Angles from Mat ================================
class SrMat;
/*! Extract from the rotation matrix m the x,y,z Euler angles,
assuming they were encoded in m in the given order.
Parameter order should be: 123 for XYZ, 132 for XZY, etc.
The other sr_euler_angles* functions are similar, but specific to the desired order. */
void sr_euler_angles ( int order, const SrMat& m, float& rx, float& ry, float& rz );
void sr_euler_angles_xyz ( const SrMat& m, float& rx, float& ry, float& rz );
void sr_euler_angles_xzy ( const SrMat& m, float& rx, float& ry, float& rz );
void sr_euler_angles_yxz ( const SrMat& m, float& rx, float& ry, float& rz );
void sr_euler_angles_yzx ( const SrMat& m, float& rx, float& ry, float& rz );
void sr_euler_angles_zxy ( const SrMat& m, float& rx, float& ry, float& rz );
void sr_euler_angles_zyx ( const SrMat& m, float& rx, float& ry, float& rz );
//================================ Mat from Angles ================================
/*! Set the rotation matrix m according to the given x,y,z Euler angles.
Parameter order should be: 123 for XYZ, 132 for XZY, etc.
Note: the 4th line and column of m are not changed.
The other sr_euler_mat* functions are similar, but specific to the desired order. */
void sr_euler_mat ( int order, SrMat& m, float rx, float ry, float rz );
void sr_euler_mat_xyz ( SrMat& m, float rx, float ry, float rz );
void sr_euler_mat_xzy ( SrMat& m, float rx, float ry, float rz );
void sr_euler_mat_yxz ( SrMat& m, float rx, float ry, float rz );
void sr_euler_mat_yzx ( SrMat& m, float rx, float ry, float rz );
void sr_euler_mat_zxy ( SrMat& m, float rx, float ry, float rz );
void sr_euler_mat_zyx ( SrMat& m, float rx, float ry, float rz );
//============================== end of file ===============================
# endif // SR_EULER_H

View File

@ -0,0 +1,62 @@
#include "precompiled.h"
# include "sr_event.h"
//# define SR_USE_TRACE1
# include "sr_trace.h"
//===================================== SrEvent =================================
SrEvent::SrEvent ()
{
init ();
}
void SrEvent::init ()
{
type = None;
key = 0;
button = 0;
button1 = button2 = button3 = 0;
ctrl = shift = alt = key = 0;
width = heigth = 0;
pixel_size = 0.05f;
}
void SrEvent::init_lmouse ()
{
type = None;
key = 0;
button = 0;
button1 = button2 = button3 = 0;
ctrl = shift = alt = key = 0;
lmouse = mouse;
lmouse = mouse;
mouse.x = mouse.y = 0;
}
const char *SrEvent::type_name () const
{
switch ( type )
{ case None : return "none";
case Push : return "push";
case Drag : return "drag";
case Release : return "release";
case Keyboard : return "keyboard";
}
return "undefined?!";
}
SrOutput& operator<< ( SrOutput& out, const SrEvent& e )
{
out << e.type_name();
if ( e.type==SrEvent::Keyboard )
out << " [" << (e.key? e.key:' ') << ':' << (int)e.key << ']';
out << " POS:" << e.mouse <<
" BUTS:" << (int)e.button1<<(int)e.button2<<(int)e.button3<<
" ACS:" << (int)e.alt<<(int)e.ctrl<<(int)e.shift;
return out;
}

95
source/dcdt/se/sr_event.h Normal file
View File

@ -0,0 +1,95 @@
# ifndef SR_EVENT_H
# define SR_EVENT_H
/** \file sr_event.h
* A user-generated window-event
*/
# include "sr_vec2.h"
# include "sr_line.h"
# include "sr_output.h"
/*! \class SrEvent sr_event.h
\brief Keeps a window event
SrEvent is used to describe a mouse or keyboard event, in a
system-independent way. */
class SrEvent
{ public :
/*! Enumerators for the type of event. */
enum Type { None, //!< No event occured.
Push, //!< A mouse button was pushed.
Drag, //!< The mouse moved with a button down.
Release, //!< A mouse button was released.
Keyboard //!< A key was pressed.
};
/*! Enumerators with codes for special keys. */
enum KeyCodes { KeyEsc=65307, KeyDel=65535, KeyIns=65379, KeyBack=65288,
KeyUp=65362, KeyDown=65364, KeyLeft=65361, KeyRigth=65363,
KeyEnd=65367, KeyPgDn=65366, KeyPgUp=65365, KeyHome=65360,
KeyShift=653505, KeyCtrl=65307, KeyAlt=65313, KeySpace=32,
KeyEnter=65293 };
public : //--- event occured :
Type type; //!< The type of the occured event
char button; //!< The button number 1, 2 or 3 if event type was Push or Release, 0 otherwise
int key; //!< The ascii code / code of the key pressed (uppercase) if it was a keyboard event, 0 otherwise
int width; //!< The width of the screen when the event occured
int heigth; //!< The heigth of the screen when the event occured
// float scenew; //!< Width in the units of the scene
// float sceneh; //!< Heigth in the units of the scene
public : //--- states at event time :
char button1; //!< Contains 1 if the left mouse button state is pressed, 0 otherwise
char button2; //!< Contains 1 if the middle mouse button state is pressed, 0 otherwise
char button3; //!< Contains 1 if the right mouse button state is pressed, 0 otherwise
char alt; //!< Contains 1 if the Alt modifier key state is pressed, 0 otherwise
char ctrl; //!< Contains 1 if the Ctrl modifier key state is pressed, 0 otherwise
char shift; //!< Contains 1 if the Shift modifier key state is pressed, 0 otherwise
public : //--- mouse coordinates :
SrVec2 mouse; //!< Current mouse push position in normalized coords [-1,1]
SrVec2 lmouse; //!< Last mouse x push position in normalized coords [-1,1]
public : //--- scene information :
SrLine ray;
SrLine lray;
SrPnt mousep; //!< mouse point in scene coordinates at plane z=0
SrPnt lmousep; //!< last mouse point in scene coordinates at plane z=0
float pixel_size; //!< 0.05 by default but should be updated according to the camera
public : //--- methods :
/*! Initialize as a None event type, by calling init(). */
SrEvent ();
/*! Makes the event as the None type, and puts all data with their default values of zero. */
void init ();
/*! Puts mouse keyboard information to their default value, but saves the mouse
values in the lmouse variables. */
void init_lmouse ();
/*! Returns a string with the name of the event type. */
const char *type_name () const;
/*! Returns the difference: mousex-lmousex. */
float mousedx () const { return mouse.x-lmouse.x; }
/*! Returns the difference: mousey-lmousey. */
float mousedy () const { return mouse.y-lmouse.y; }
/*! Returns true if the event type is push, drag, or release; and false otherwise. */
bool mouse_event () const { return type==Push||type==Drag||type==Release? true:false; }
/*! Outputs data of this event for data inspection. */
friend SrOutput& operator<< ( SrOutput& out, const SrEvent& e );
};
//================================ End of File =================================================
# endif // SR_EVENT_H

View File

@ -0,0 +1,511 @@
#include "precompiled.h"//***************************************************************************
//
// SrExpTABLE.H
// By Marcelo Kallmann 08/98 - Brazil
//
//***************************************************************************
# include <math.h>
# include <string.h>
# include <stdlib.h>
# include "sr_array.h"
# include "sr_string.h"
# include "sr_exp_table.h"
//# define SR_USE_TRACE1 // Parse
//# define SR_USE_TRACE2 // Eval
//# define SR_USE_TRACE3 // Table
# include "sr_trace.h"
// ============================= EToken ========================================
typedef SrExpTable::Token EToken;
inline void setNumb ( EToken& t, double d ) { t.type=(char)EToken::Numb; t.data.realval=d; }
inline void setVar ( EToken& t, int id ) { t.type=(char)EToken::Var; t.data.index=id; }
inline void setFunc ( EToken& t, int id ) { t.type=(char)EToken::Func; t.data.index=id; }
inline void setBinOp ( EToken& t, char op ) { t.type=(char)EToken::BinOp; t.data.code=op; }
inline void setUnaOp ( EToken& t, char op ) { t.type=(char)EToken::UnaOp; t.data.code=op; }
inline void setLeftPar ( EToken& t ) { t.type=(char)EToken::LeftPar; }
inline void setRightPar ( EToken& t ) { t.type=(char)EToken::RightPar; }
//======================= Modes while parsing =================================
enum srParMode { srParModeUnary, srParModeBinary, srParModeFunction };
//======================== functions methods ===================================
enum srFunc { srFuncAbs, srFuncAcos, srFuncAsin, srFuncAtan, srFuncCeil,
srFuncCos, srFuncExp, srFuncFact, srFuncFloor, srFuncLn,
srFuncLog, srFuncRound, srFuncSign, srFuncSin, srFuncSqrt,
srFuncTan, srFuncUndefined }; // Alphabetical order !
static char *Functions[] = { "abs", "acos", "asin", "atan", "ceil",
"cos", "exp", "fact", "floor", "ln",
"log", "round", "sign", "sin", "sqrt",
"tan" }; // Alphabetical order !
static int compfunc ( const void *a, const void *b )
{
return strcmp ( ((char*)a), *((char**)b) );
}
static srFunc func_code ( const char *st )
{
void *result = bsearch ( (const void*)st, // to search
(const void*)Functions, // table
(size_t)srFuncUndefined, // num elems in table
(size_t)sizeof(char*), // size of each elem in table
compfunc // compare function
);
if (!result) return srFuncUndefined;
return (srFunc) ( ((int)result-(int)Functions)/sizeof(int) );
}
const char *SrExpTable::function_name ( int index )
{
if ( index<0 || index>=(int)srFuncUndefined ) return 0;
return Functions[index];
}
static int oprank ( char op )
{
switch ( op )
{ case '[' : return 8;
case '^' : case '%' : return 7;
case '/' : case '*' : case '|' : return 6;
case '+' : case '-' : return 5;
case '>' : case '<' :
case ')' : case '(' : // >= <=
case '~' : case '=' : return 4;
case 'a' : case 'o' : return 3; // and or
}
return 0;
}
//===================================== messages ==============================
// Global functions :
SrExpTable::Msg SrExpTable::translate_error ( SrInput::ErrorType e )
{
switch ( e )
{ case SrInput::UndefChar : return MsgUndefChar;
case SrInput::TooBig : return MsgNameTooBig;
case SrInput::OpenString : return MsgStringNotClosed;
case SrInput::InvalidPoint : return MsgMisplacedDecPoint;
default : return MsgOk;
}
}
const char* SrExpTable::msg_desc ( SrExpTable::Msg m )
{
switch ( m )
{ // messages translated from sr_input.h :
case MsgUndefChar : return SrInput::error_desc ( SrInput::UndefChar );
case MsgNameTooBig : return SrInput::error_desc ( SrInput::TooBig );
case MsgStringNotClosed : return SrInput::error_desc ( SrInput::OpenString );
case MsgMisplacedDecPoint : return SrInput::error_desc ( SrInput::InvalidPoint );
// new messages :
case MsgOk : return "Ok";
case MsgUnexpectedEndOfFile : return "Unexpected end of file encountered";
case MsgUndefinedName : return "Undefined variable";
case MsgUndefBinOp : return "Undefined binary operator: internal error";
case MsgUndefUnaOp : return "Undefined unary operator: internal error";
case MsgUndefPreDefFunc : return "Undefined pre-defined function: internal error";
case MsgOperandExpected : return "Operand expected in expression";
case MsgOperatorExpected : return "Operator expected in expression";
case MsgFuncWithoutPar : return "Function name must be followed by a left parenthesis";
case MsgDivideByZero : return "Divide by zero";
case MsgRaisedZeroToNeg : return "Cannot raise zero to a negative power";
case MsgRaisedNegToReal : return "Cannot raise a negative number to a non-integer power";
case MsgUnmatchedParentesis : return "Unmatched parentesis";
case MsgAcosArgsOutOfRange : return "Arc cos arguments out of range";
case MsgAsinArgsOutOfRange : return "Arc sin arguments out of range";
case MsgStackEmpty : return "Stack is empty";
case MsgSqrtArgIsNeg : return "Sqrt argument is negative";
default : return NULL;
}
}
//============================ internal ======================================
# define ISBINARY(c) strchr("+-*/^%|><=[",c)
# define ISUNARY(c) strchr("+-!",c)
static bool is_binary ( const SrString& token )
{
if ( strchr("+-*/^%|><~=[",token[0]) ) return true;
if ( token=="and" || token=="or" ) return true;
return false;
}
static SrExpTable::Msg get_translated_token ( SrInput& in,
int& curline,
EToken& etok,
srParMode mode,
const char* delimeters,
SrExpTableVarManager* varman )
{
SrInput::TokenType toktype;
SrString token;
toktype = in.get_token(token);
curline = in.curline();
if ( toktype==SrInput::Error ) return SrExpTable::translate_error ( in.last_error() );
if ( toktype==SrInput::EndOfFile ) return SrExpTable::MsgUnexpectedEndOfFile;
if ( delimeters && toktype==SrInput::Delimiter && strchr(delimeters,token[0]) )
{ in.unget_token ( token, toktype );
return SrExpTable::MsgUnexpectedEndOfFile; // permits to stop the parser before end of file
}
if ( mode==srParModeUnary && ISUNARY(token[0]) )
{ setUnaOp ( etok, token[0] );
SR_TRACE1 ( "Got a unary op: ["<<token<<"]" );
return SrExpTable::MsgOk;
}
if ( (mode==srParModeBinary || mode==srParModeFunction) && is_binary(token) )
{ char op = token[0];
if ( op=='>' || op=='<' )
{ toktype = in.get_token(token);
if ( toktype==SrInput::Error ) return SrExpTable::translate_error ( in.last_error() );
curline = in.curline();
if ( op=='>' && token[0]=='=' ) op = ')'; // that will mean >=
else
if ( op=='<' && token[0]=='=' ) op = '('; // that will mean <=
else
if ( op=='<' && token[0]=='>' ) op = '~'; // <> mean difference op ~
else
in.unget_token ( token, toktype );
}
else if ( op=='=' )
{ toktype = in.get_token(token);
if ( toktype==SrInput::Error ) return SrExpTable::translate_error ( in.last_error() );
if ( token[0]!='=' ) return SrExpTable::MsgWrongEqualOperator;
}
else if ( op=='[' )
{
in.unget_token ( "(", SrInput::Delimiter ); // so that arrays indices may form an expression
}
setBinOp ( etok, op );
SR_TRACE1 ( "Got a binary op: ["<<op<<"]");
return SrExpTable::MsgOk;
}
if ( token[0]=='(' ) { setLeftPar(etok); SR_TRACE1("Got a left par.\n"); return SrExpTable::MsgOk; }
if ( token[0]==')' ) { setRightPar(etok); SR_TRACE1("Got a right par.\n"); return SrExpTable::MsgOk; }
if ( token[0]==']' ) { setRightPar(etok); SR_TRACE1("Got array right par.\n"); return SrExpTable::MsgOk; }
if ( toktype==SrInput::Name ) // bool(int), func or var
{ if ( strcmp(token,"true")==0 ) { setNumb(etok,1.0); SR_TRACE1("Got a true.\n"); return SrExpTable::MsgOk; }
if ( strcmp(token,"false")==0 ) { setNumb(etok,0.0); SR_TRACE1("Got a false.\n"); return SrExpTable::MsgOk; }
srFunc f = func_code ( token ); // found a func?
if ( f!=srFuncUndefined )
{ setFunc(etok,f); SR_TRACE1("Got func ["<<(Functions[(int)f])<<"]"); return SrExpTable::MsgOk; }
if ( !varman ) return SrExpTable::MsgUndefinedName; // found a name that is not a function or variable
setVar ( etok, varman->get_variable_index(token) ); // get the user index for the variable
SR_TRACE1("Got a var ["<<token<<"], index "<<etok.data.index );
if ( etok.data.index<0 ) return SrExpTable::MsgUndefinedName;
return SrExpTable::MsgOk;
}
if ( toktype==SrInput::Integer || toktype==SrInput::Real )
{ setNumb(etok,(double)atof(token)); SR_TRACE1("Got a number: "<<(atof(token))); return SrExpTable::MsgOk; }
return SrExpTable::MsgUndefChar;
}
//===================================== SrExpTable ===================================================
SrExpTable::SrExpTable ()
{
_varman = 0;
_delimiters = 0;
}
SrExpTable::~SrExpTable ()
{
if ( _varman ) _varman->unref();
delete _delimiters;
}
void SrExpTable::set_var_manager ( SrExpTableVarManager* var_man )
{
if ( _varman ) _varman->unref();
_varman = var_man;
_varman->ref();
}
void SrExpTable::set_parser_delimiters ( const char *delimiters )
{
sr_string_set ( _delimiters, delimiters );
}
SrExpTable::Msg SrExpTable::parse ( SrInput& in, int &curline )
{
Msg msg;
Token etok, t;
srParMode mode = srParModeUnary;
_table.size ( 0 );
_stack.size ( 0 );
while ( true )
{
msg = get_translated_token ( in, curline, etok, mode, _delimiters, _varman );
if ( msg==MsgUnexpectedEndOfFile ) break;
if ( msg!=MsgOk ) return msg;
if ( mode==srParModeUnary && (etok.type==Token::BinOp||etok.type==Token::RightPar) )
{ return MsgOperandExpected; }
if ( mode==srParModeBinary && etok.type!=Token::BinOp && etok.type!=Token::RightPar )
{ return MsgOperatorExpected; }
if ( mode==srParModeFunction && etok.type!=Token::LeftPar )
{ return MsgFuncWithoutPar; }
switch ( etok.type )
{ case Token::Numb :
case Token::Var : _stack.push(etok); mode=srParModeBinary; break;
case Token::LeftPar :
case Token::UnaOp : _stack.push(etok); mode=srParModeUnary; break;
case Token::Func : _stack.push(etok); mode=srParModeFunction; break;
case Token::BinOp :
while ( !_stack.empty() )
{ t=_stack.top ();
if ( t.type==Token::BinOp && oprank(etok.data.code)>oprank(t.data.code) ) break;
if ( t.type==Token::LeftPar ) break;
_stack.pop(); _table.push(t);
}
_stack.push(etok); mode=srParModeUnary; break;
case Token::RightPar :
if ( _stack.empty() ) return MsgStackEmpty;
while ( !_stack.empty() )
{ t=_stack.pop();
if (t.type==Token::LeftPar) break;
_table.push(t);
}
if ( t.type!=Token::LeftPar ) return MsgOk; // here the stack will be empty
if ( !_stack.empty() && _stack.top().type==Token::Func )
_table.push( _stack.pop() );
mode=srParModeBinary;
break;
}
}
if ( mode==srParModeUnary) return MsgOperandExpected;
while ( !_stack.empty() )
{ if ( _stack.top().type==Token::LeftPar ) { return MsgUnmatchedParentesis; }
_table.push( _stack.pop() );
}
_table.push().type=Token::EndMark; // mark the end
# ifdef SR_USE_TRACE3
sr_out << "ExpTable: " << *this << srnl;
# endif
return MsgOk;
}
static SrExpTable::Msg operateBinOp ( double x, double y, char op,
SrArray<EToken>& stack,
SrExpTableVarManager* varman )
{
double r;
switch ( op )
{ case '[' : r = varman->get_variable_value(int(x),int(y)); break;
case '+' : r = x+y; break;
case '-' : r = x-y; break;
case '/' : if (!y) return SrExpTable::MsgDivideByZero; r=x/y; break;
case '|' : if (!y) return SrExpTable::MsgDivideByZero; r=(double)(((int)x)|((int)y)); break;
case '*' : r=x*y; break;
case '%' : r = (x/100.0)*y; break;
case '^' : if ( x==0.0 && y<0.0 ) return SrExpTable::MsgRaisedZeroToNeg;
if ( x<0.0 && y!=(int)y ) return SrExpTable::MsgRaisedNegToReal;
r=pow(x,y); break;
case 'a' : r = x!=0 && y!=0? 1.0:0.0; break;
case 'o' : r = x!=0 || y!=0? 1.0:0.0; break;
case '>' : r = x>y? 1.0:0.0; break;
case '<' : r = x<y? 1.0:0.0; break;
case ')' : r = x>=y? 1.0:0.0; break;
case '(' : r = x<=y? 1.0:0.0; break;
case '~' : r = x!=y? 1.0:0.0; break;
case '=' : r = x==y? 1.0:0.0; break;
default : return SrExpTable::MsgUndefBinOp;
}
SR_TRACE2 ( "Operating: "<< x << " " << op << " " << y << " = " << r );
setNumb ( stack.push(), r );
return SrExpTable::MsgOk;
}
static SrExpTable::Msg operateUnaOp ( double x, char op, SrArray<EToken>& stack )
{
double r;
switch ( op )
{ case '+' : r=x; break;
case '-' : r=-x; break;
case '!' : r = x==0? 1.0:0.0; break;
default : return SrExpTable::MsgUndefUnaOp;
}
SR_TRACE2 ( "Operating: " << op << " " << x << " = " << r );
setNumb ( stack.push(), r );
return SrExpTable::MsgOk;
}
static double dround ( double x ) { return (x>0.0)? (double)(long int)(x+0.5) : (double)(long int)(x-0.5); }
static double dsign ( double x ) { return (x<0.0)? -1: (x>0.0)? 1:0; }
static double dfact ( int x ) { if(x<2)return 1l; long m=(long)x; while(--x>1)m*=(long)x; return(double)m; }
static SrExpTable::Msg operateFunc ( double x, srFunc f, SrArray<EToken>& stack )
{
double r;
switch ( f )
{ case srFuncAbs : r = x<0.0? -x:x; break;
case srFuncAcos : if ( x<-1.0 || x>1.0 ) return SrExpTable::MsgAcosArgsOutOfRange;
r=acos(x); break;
case srFuncAsin : if ( x<-1.0 || x>1.0 ) return SrExpTable::MsgAsinArgsOutOfRange;
r=asin(x); break;
case srFuncAtan : r=atan(x); break;
case srFuncCeil : r=ceil(x); break;
case srFuncCos : r=cos(x); break;
case srFuncExp : r=pow(SR_E,x); break;
case srFuncFact : r=dfact((int)x); break;
case srFuncFloor : r=floor(x); break;
case srFuncLn : r=log(x); break;
case srFuncLog : r=log10(x); break;
case srFuncSign : r=dsign(x); break;
case srFuncSin : r=sin(x); break;
case srFuncSqrt : if ( x<-0.0 ) return SrExpTable::MsgSqrtArgIsNeg;
r=sqrt(x); break;
case srFuncTan : r=tan(x); break;
case srFuncRound : r=dround(x); break;
default : return SrExpTable::MsgUndefPreDefFunc;
}
SR_TRACE2 ( "Operating: " << Functions[f] << "(" << x << ") = " << r );
setNumb ( stack.push(), r );
return SrExpTable::MsgOk;
}
static SrExpTable::Msg pop_value ( double &v, SrArray<EToken>& stack,
SrExpTableVarManager* varman, bool isarrayop )
{
if ( stack.empty() ) return SrExpTable::MsgStackEmpty;
EToken t = stack.pop();
if ( t.type==EToken::Var )
{ v = isarrayop? (double)t.data.index : varman->get_variable_value(t.data.index,-1);
return SrExpTable::MsgOk;
}
else if ( t.type==EToken::Numb )
{ v = t.data.realval;
return SrExpTable::MsgOk;
}
else
return SrExpTable::MsgOperandExpected;
}
SrExpTable::Msg SrExpTable::evaluate ( double &result )
{
int i;
double u, v;
Token::Type t;
Msg msg;
_stack.size ( 0 );
for ( i=0; i<_table.size(); i++ )
{ t = (Token::Type) _table[i].type;
if ( t==Token::EndMark ) break; // Found a mark for the end (only when not compressed)
if ( t==Token::Numb || t==Token::Var ) // a value
{ _stack.push ( _table[i] );
}
else
{ msg = pop_value ( v, _stack, _varman, false );
if ( msg!=MsgOk ) return msg;
switch ( t )
{ case Token::Func :
msg = operateFunc ( v, (srFunc)_table[i].data.index, _stack );
break;
case Token::UnaOp :
msg = operateUnaOp ( v, _table[i].data.code, _stack );
break;
case Token::BinOp :
msg = pop_value ( u, _stack, _varman, _table[i].data.code=='['?true:false );
if ( msg!=MsgOk ) break;
msg = operateBinOp ( u, v, _table[i].data.code, _stack, _varman );
break;
}
if ( msg!=MsgOk ) return msg;
}
}
if ( _stack.size()!=1 ) return MsgOperatorExpected;
return pop_value ( result, _stack, _varman, false );
}
void SrExpTable::compress ()
{
_table.compress ();
_stack.compress();
}
/*! Takes the internal data of t, and leave t as an empty table. */
// void take_data ( SrExpTable& t );
// void take_data ( SrArray<SrExpTableToken>& tokens );
// void leave_data ( SrArray<SrExpTableToken>& tokens );
/*
void SrExpTable::take_data ( SrExpTable& t )
{
if ( _tokens ) free ( _tokens );
_tokens = t._tokens; t._tokens = 0;
_size = t._size; t._size = 0;
_capacity = t._capacity; t._capacity = 0;
}
*/
//=============================== friends ============================
SrOutput& operator << ( SrOutput& o, const SrExpTable& t )
{
EToken tk;
for ( int i=0; i<t._table.size(); i++ )
{ tk = t._table[i];
switch ( tk.type )
{ case EToken::Numb : o<<tk.data.realval<<srspc; break;
case EToken::Var : o<<"var:"<<tk.data.index<<srspc; break;
case EToken::Func : o<<Functions[tk.data.index]<<srspc; break;
case EToken::UnaOp :
case EToken::BinOp : o<<tk.data.code<<srspc; break;
case EToken::EndMark : return o;
}
}
return o;
}
//============================ End of File ==============================

View File

@ -0,0 +1,134 @@
/** \file sr_exp_table.h
* Expression Table Evaluation. */
# ifndef SR_EXP_TABLE_H
# define SR_EXP_TABLE_H
# include "sr_array.h"
# include "sr_input.h"
# include "sr_output.h"
# include "sr_shared_class.h"
/*! \class SrExpTableVarManager sr_exp_table.h
\brief Allows the use of variables in expressions
This class should be derived and its two virtual methods
implemented in order to make SrExpTable working with user-
managed variables. Variables can be indexed like "var[n]". */
class SrExpTableVarManager : public SrSharedClass
{ public :
/*! virtual destructor will call the destructor of the derived class, if any. */
virtual ~SrExpTableVarManager () {};
/*! Translates found user-variables into unique indices to be internally stored.
Whenever a name is encountered during parsing, it is first checked if it
corresponds to some internal function, if not, it is considered to be a
user-variable. If -1 is returned, the variable is not defined and an
error is generated. */
virtual int get_variable_index ( const char* var_name )=0;
/*! Used to retrieve a variable value during evaluation. The
parameter var_id is the variable index registered at parsing time, and
if the parameter array_id is >=0, array_id will contain the array index
requested for the variable. */
virtual double get_variable_value ( int var_id, int array_id )=0;
};
/*! \class SrExpTable sr_exp_table.h
\brief Expression Table Evaluation
SrExpTable is used to parse and keep an expression table for later evaluation.
Recognized operators and their precedence are:
(1) !x (logical unary operator that negates a value 0<->1)
(2) x^y, x%y (x raised to y, x percent of y)
(3) x*y, x/y, x|y (x multiplied by y, x divided by y, x modulus y)
(4) x+y, x-y (x plus y, x minus y)
(5) x>y, x<y, x>=y, x<=y, x==y, x<>y (comparison logical operators)
(6) x and y, x or y (logical operators)
Names true and false are converted to 1 and 0.
All calculations are done using double types.
Suported functions are: abs, acos, asin, atan, ceil, cos, exp, fact, floor,
ln, log, round, sign, sin, sqrt, tan */
class SrExpTable
{ public :
enum Msg { MsgOk, MsgUndefChar, MsgNameTooBig,
MsgStringNotClosed, MsgMisplacedDecPoint, MsgUnexpectedEndOfFile,
MsgUndefinedName, MsgUndefBinOp, MsgUndefUnaOp,
MsgUndefPreDefFunc, MsgOperandExpected, MsgOperatorExpected,
MsgFuncWithoutPar, MsgDivideByZero, MsgRaisedZeroToNeg,
MsgRaisedNegToReal, MsgUnmatchedParentesis, MsgAcosArgsOutOfRange,
MsgAsinArgsOutOfRange, MsgStackEmpty, MsgSqrtArgIsNeg,
MsgWrongEqualOperator
};
struct Token
{ enum Type { Numb, Var, Func, BinOp, UnaOp, LeftPar, RightPar, EndMark };
char type;
union { double realval; int index; char code; } data;
};
private :
SrArray<Token> _stack;
SrArray<Token> _table;
SrExpTableVarManager* _varman;
char* _delimiters;
public : // static methods :
/*! This method can be used to get the name of all supported pre-defined
functions. It returns the name of a given function index, that starts
from zero. When index is out of range, null is returned. */
static const char* function_name ( int index );
/*! This method returns the corresponding SrExpTable::Msg from the given
SrInput::Error, the returned Msg can be one of : MsgOk, MsgUndefChar,
MsgNameTooBig, MsgStringNotClosed, MsgMisplacedDecPoint. */
static Msg translate_error ( SrInput::ErrorType e );
/*! Returns a string description of the given msg. Descriptions start with
an upper case letter, and have no ending period. */
static const char* msg_desc ( Msg m );
public :
/*! Constructor. */
SrExpTable ();
/*! Destructor. */
~SrExpTable ();
/*! A derived class of SrExpTableVarManager is needed in order to enable
the use of variables. The passed manager must be allocated with operator
new and its deletion is controlled by SrExpTable. A null pointer can
be passed to indicate that variables are not used. */
void set_var_manager ( SrExpTableVarManager* var_man );
/*! Make the parsing stop when any special delimeter is found.
The parsing stops when one of the
specified delimiters is read, and then,
after ungetting the read delimiter, MsgOk is returned when parse() is called.
If the passed string is null or "", delimiters will not be taken into account. */
void set_parser_delimiters ( const char *delimiters );
/*! Parses an expression from the given input. The current line of the
parsed input is updated in curline. */
Msg parse ( SrInput &in, int &curline );
/*! Evaluates the expression and put the value in result if no error occured. */
Msg evaluate ( double &result );
/*! Compress the internal arrays. */
void compress ();
/*! Print the internal table. */
friend SrOutput& operator << ( SrOutput& o, const SrExpTable& t );
};
# endif // SR_EXP_TABLE_H

54
source/dcdt/se/sr_fl.h Normal file
View File

@ -0,0 +1,54 @@
/** \file sr_fl.h
* fltk utilities integrated with sr
*/
# ifndef SR_FL_H
# define SR_FL_H
# include <FL/Fl_Gl_Window.H>
# include <SR/sr_color.h>
# include <SR/sr_string_array.h>
# include <SR/sr_mat.h>
/*! Fltk color chooser utility receiving a sr parameter SrColor */
bool fl_color_chooser ( const char *title, SrColor& c );
/*! Returns true if ok was pressed and false otherwise. Parameter value
is displayed and returns the chosen value*/
bool fl_value_input ( const char* title, const char* label, float& value );
/*! Returns true if ok was pressed and false otherwise. Parameter s
is displayed for edition and returns the entered string */
bool fl_string_input ( const char* title, const char* label, SrString& s );
/*! Uses an internal instance of SrOutputWindow to display a text.
The internal instance is only allocated when used for the first time. */
void fl_text_output ( const char* title, const char* text );
/*! Opens a matrix edition window, returning true if ok was pressed,
and false otherwise. If title is null, the window title is not changed. */
bool fl_matrix_input ( const char* title, SrMat& m );
/*! Scan the directory dir, and put all found directories and files in
dirs and files. String array ext may contain file extensions to
filter the read files (e.g "cpp" "txt", etc).
String dir must finish with a slash '/' character.
File names are returned in their full path names */
void fl_scan_dir ( const SrString& dir,
SrStringArray& dirs,
SrStringArray& files,
const SrStringArray& ext );
/*! Recursively scans and put in file all files starting at basedir.
String array ext may contain file extensions to filter the read
files; extensions are set without the "dot".
File names are returned in their full path names */
void fl_scan_files ( const SrString& basedir,
SrStringArray& files,
const SrStringArray& ext );
//================================ End of File =================================================
# endif // SR_VIEWER_H

262
source/dcdt/se/sr_geo2.cpp Normal file
View File

@ -0,0 +1,262 @@
#include "precompiled.h"
# include <stdio.h>
# include <math.h>
# include "sr_geo2.h"
// This file is designed to be as most as possible independent of other sr types
//================================= Macros =========================================
# define gABS(x) (x>0? (x):-(x))
# define gMAX(a,b) (a>b? (a):(b))
# define gSWAP(a,b) { tmp=a; a=b; b=tmp; }
# define gOUTXY(a,b) printf("(%+6.4f,%+6.4f) ", a, b )
# define gOUTL printf("\n");
# define gEPS 1.0E-14 // doubles have 15 decimals
# define gNEXTZERO(a) ( (a)>-(gEPS) && (a)<(gEPS) )
// The following macro can be defined to activate some extra operations that
// try to get more precise results in the functions of this file. Normally
// these extra operations are not worth to activate
// # define gMAXPREC
//================================= funcs =====================================
bool sr_segments_intersect ( double p1x, double p1y, double p2x, double p2y,
double p3x, double p3y, double p4x, double p4y )
{
double d = (p4y-p3y)*(p1x-p2x)-(p1y-p2y)*(p4x-p3x);
if ( gNEXTZERO(d) ) return false; // they are parallel
double t = ((p4y-p3y)*(p4x-p2x)-(p4x-p3x)*(p4y-p2y)) / d;
if ( t<0.0 || t>1.0 ) return false; // outside [p1,p2]
double s = ((p4y-p2y)*(p1x-p2x)-(p1y-p2y)*(p4x-p2x)) / d;
if ( s<0.0 || s>1.0 ) return false; // outside [p3,p4]
return true;
}
bool sr_segments_intersect ( double p1x, double p1y, double p2x, double p2y,
double p3x, double p3y, double p4x, double p4y,
double& x, double &y )
{
double d = (p4y-p3y)*(p1x-p2x)-(p1y-p2y)*(p4x-p3x);
if ( gNEXTZERO(d) ) return false; // they are parallel
double t = ((p4y-p3y)*(p4x-p2x)-(p4x-p3x)*(p4y-p2y)) / d;
if ( t<0.0 || t>1.0 ) return false; // outside [p1,p2]
double s = ((p4y-p2y)*(p1x-p2x)-(p1y-p2y)*(p4x-p2x)) / d;
if ( s<0.0 || s>1.0 ) return false; // outside [p3,p4]
x = t*p1x+(1-t)*p2x;
y = t*p1y+(1-t)*p2y;
# ifdef gMAXPREC
x += s*p3x+(1-s)*p4x;
y += s*p3y+(1-s)*p4y;
x /= 2;
y /= 2;
# endif
return true;
}
bool sr_lines_intersect ( double p1x, double p1y, double p2x, double p2y,
double p3x, double p3y, double p4x, double p4y )
{
double d = (p4y-p3y)*(p1x-p2x)-(p1y-p2y)*(p4x-p3x);
if ( gNEXTZERO(d) ) return false; // they are parallel
return true;
}
bool sr_lines_intersect ( double p1x, double p1y, double p2x, double p2y,
double p3x, double p3y, double p4x, double p4y,
double& x, double &y )
{
double d = (p4y-p3y)*(p1x-p2x)-(p1y-p2y)*(p4x-p3x);
if ( gNEXTZERO(d) ) return false; // they are parallel
double t = ((p4y-p3y)*(p4x-p2x)-(p4x-p3x)*(p4y-p2y)) / d;
x = t*p1x+(1-t)*p2x;
y = t*p1y+(1-t)*p2y;
# ifdef gMAXPREC // 1000 random tests reveal E-10 order error between t and s results
double s = ((p4y-p2y)*(p1x-p2x)-(p1y-p2y)*(p4x-p2x)) / d;
x += s*p3x+(1-s)*p4x;
y += s*p3y+(1-s)*p4y;
x /= 2;
y /= 2;
# endif
return true;
}
void sr_line_projection ( double p1x, double p1y, double p2x, double p2y, double px, double py, double& qx, double& qy )
{
qx = px;
qy = py;
double vx = -(p2y-p1y);
double vy = p2x-p1x; // v = (p2-p1).ortho(), ortho==(-y,x)
sr_lines_intersect ( p1x, p1y, p2x, p2y, px, py, px+vx, py+vy, qx, qy );
}
bool sr_segment_projection ( double p1x, double p1y, double p2x, double p2y, double px, double py, double& qx, double& qy, double epsilon )
{
double tmp;
sr_line_projection ( p1x, p1y, p2x, p2y, px, py, qx, qy );
if ( epsilon==0 ) epsilon=gEPS; // if 0 the inequalities dont work with collinear inputs
/* if ( p1x>p2x ) gSWAP(p1x,p2x);
if ( qx+epsilon<=p1x || qx-epsilon>=p2x ) return false;
if ( p1y>p2y ) gSWAP(p1y,p2y);
if ( qy+epsilon<=p1y || qy-epsilon>=p2y ) return false;*/
if ( p1x>p2x ) gSWAP(p1x,p2x);
if ( qx+epsilon<p1x || qx-epsilon>p2x ) return false;
if ( p1y>p2y ) gSWAP(p1y,p2y);
if ( qy+epsilon<p1y || qy-epsilon>p2y ) return false;
return true;
}
double sr_dist2 ( double p1x, double p1y, double p2x, double p2y )
{
p1x = p2x-p1x;
p1y = p2y-p1y;
return p1x*p1x + p1y*p1y;
}
double sr_point_segment_dist ( double px, double py, double p1x, double p1y, double p2x, double p2y )
{
double dist, qx, qy;
if ( sr_segment_projection(p1x,p1y,p2x,p2y,px,py,qx,qy,0) )
{ dist = sr_dist2(px,py,qx,qy);
}
else
{ dist = sr_dist2(px,py,p1x,p1y);
double d = sr_dist2(px,py,p2x,p2y);
if (d<dist) dist=d;
}
return sqrt(dist);
}
double sr_point_line_dist ( double px, double py, double p1x, double p1y, double p2x, double p2y )
{
double qx, qy;
sr_line_projection (p1x,p1y,p2x,p2y,px,py,qx,qy);
return sqrt( sr_dist2(px,py,qx,qy) );
}
bool sr_next ( double p1x, double p1y, double p2x, double p2y, double epsilon )
{
return sr_dist2(p1x,p1y,p2x,p2y)<=epsilon*epsilon? true:false;
}
double sr_ccw ( double p1x, double p1y, double p2x, double p2y, double p3x, double p3y )
{
return SR_CCW(p1x,p1y,p2x,p2y,p3x,p3y);
}
bool sr_in_segment ( double p1x, double p1y, double p2x, double p2y, double px, double py, double epsilon )
{
double qx, qy;
if ( epsilon==0 ) epsilon=gEPS;
if ( !sr_segment_projection ( p1x, p1y, p2x, p2y, px, py, qx, qy, epsilon ) ) return false;
return sr_dist2(px,py,qx,qy)<=epsilon*epsilon? true:false;
}
bool sr_in_segment ( double p1x, double p1y, double p2x, double p2y, double px, double py,
double epsilon, double& dist2 )
{
double qx, qy;
if ( epsilon==0 ) epsilon=gEPS;
if ( !sr_segment_projection ( p1x, p1y, p2x, p2y, px, py, qx, qy, epsilon ) ) return false;
dist2 = sr_dist2(px,py,qx,qy);
//sr_out<<dist2<<srspc<<(epsilon*epsilon)<<srnl;
return dist2<=epsilon*epsilon? true:false;
}
bool sr_in_triangle ( double p1x, double p1y, double p2x, double p2y, double p3x, double p3y, double px, double py )
{
return SR_CCW(px,py,p1x,p1y,p2x,p2y)>=0 &&
SR_CCW(px,py,p2x,p2y,p3x,p3y)>=0 &&
SR_CCW(px,py,p3x,p3y,p1x,p1y)>=0 ? true:false;
}
// circle_test :
// p1, p2, p3 must be in ccw order
// Calculates the following determinant :
// | p1x p1y p1x*p1x + p1y*p1y 1.0 |
// | p2x p2y p2x*p2x + p2y*p2y 1.0 |
// | p3x p3y p3x*p3x + p3y*p3y 1.0 |
// | px py px*px + py*py 1.0 |
// This is not the most accurate calculation, but is the fastest.
bool sr_in_circle ( double p1x, double p1y, double p2x, double p2y, double p3x, double p3y, double px, double py )
{
double p1z = p1x*p1x + p1y*p1y;
double p2z = p2x*p2x + p2y*p2y;
double p3z = p3x*p3x + p3y*p3y;
double pz = px*px + py*py;
double m12 = p2x*p3y - p2y*p3x;
double m13 = p2x*p3z - p2z*p3x;
double m14 = p2x - p3x;
double m23 = p2y*p3z - p2z*p3y;
double m24 = p2y - p3y;
double m34 = p2z - p3z;
double d1 = p1y*m34 - p1z*m24 + m23;
double d2 = p1x*m34 - p1z*m14 + m13;
double d3 = p1x*m24 - p1y*m14 + m12;
double d4 = p1x*m23 - p1y*m13 + p1z*m12;
double det = d4 - px*d1 + py*d2 - pz*d3;
const double prec = gEPS; // I have encountered cases working only with this tiny epsilon
return det>prec? true:false;
}
void sr_barycentric ( double p1x, double p1y, double p2x, double p2y, double p3x, double p3y,
double px, double py, double& u, double& v, double& w )
{
# define DET3(a,b,c,d,e,f,g,h,i) a*e*i +b*f*g +d*h*c -c*e*g -b*d*i -a*f*h
double A = DET3 ( p1x, p2x, p3x, p1y, p2y, p3y, 1, 1, 1 );
double A1 = DET3 ( px, p2x, p3x, py, p2y, p3y, 1, 1, 1 );
double A2 = DET3 ( p1x, px, p3x, p1y, py, p3y, 1, 1, 1 );
//double A3 = DET3 ( p1x, p2x, px, p1y, p2y, py, 1, 1, 1 );
# undef DET3
u = A1/A;
v = A2/A;
w = 1.0-u-v; // == A3/A;
}
//=============================== Documentation ======================================
/*
Intersection of segments math:
t p1 + (1-t)p2 = p (1)
s p3 + (1-s)p4 = p (2)
=> Making (1)=(2) :
t p1 + (1-t)p2 = s p3 + (1-s)p4
t(p1-p2) + s(p4-p3) = p4-p2
t(p1x-p2x) + s(p4x-p3x) = p4x-p2x (3)
t(p1y-p2y) + s(p4y-p3y) = p4y-p2y (4)
=> Putting t from (3) to (4) :
t = [(p4x-p2x) - s(p4x-p3x)] / (p1x-p2x)
[(p4x-p2x) - s(p4x-p3x)] / (p1x-p2x) = [(p4y-p2y) - s(p4y-p3y)] / (p1y-p2y)
(p1y-p2y)(p4x-p2x) - s(p1y-p2y)(p4x-p3x) = (p4y-p2y)(p1x-p2x) - s(p4y-p3y)(p1x-p2x)
s(p4y-p3y)(p1x-p2x) - s(p1y-p2y)(p4x-p3x) = (p4y-p2y)(p1x-p2x) - (p1y-p2y)(p4x-p2x)
s = [(p4y-p2y)(p1x-p2x)-(p1y-p2y)(p4x-p2x)] / [(p4y-p3y)(p1x-p2x)-(p1y-p2y)(p4x-p3x)]
Let d = (p4y-p3y)(p1x-p2x)-(p1y-p2y)(p4x-p3x) (5)
s = [(p4y-p2y)(p1x-p2x)-(p1y-p2y)(p4x-p2x)] / d (6)
=> Putting s from (3) to (4) :
s = [(p4x-p2x) - t(p1x-p2x)] / (p4x-p3x)
[(p4x-p2x) - t(p1x-p2x)] / (p4x-p3x) = [(p4y-p2y) - t(p1y-p2y)] / (p4y-p3y)
(p4y-p3y)(p4x-p2x) - t(p4y-p3y)(p1x-p2x) = (p4x-p3x)(p4y-p2y) - t(p4x-p3x)(p1y-p2y)
t(p4x-p3x)(p1y-p2y) - t(p4y-p3y)(p1x-p2x) = (p4x-p3x)(p4y-p2y) - (p4y-p3y)(p4x-p2x)
t = [(p4x-p3x)(p4y-p2y)-(p4y-p3y)(p4x-p2x)] / [(p4x-p3x)(p1y-p2y)-(p4y-p3y)(p1x-p2x)]
t = -1*[(p4x-p3x)(p4y-p2y)-(p4y-p3y)(p4x-p2x)] / -1*[(p4x-p3x)(p1y-p2y)-(p4y-p3y)(p1x-p2x)]
t = [(p4y-p3y)(p4x-p2x)-(p4x-p3x)(p4y-p2y)] / [(p4y-p3y)(p1x-p2x)-(p4x-p3x)(p1y-p2y)]
Using (5) :
t = [(p4y-p3y)(p4x-p2x)-(p4x-p3x)(p4y-p2y)] / d (7)
=> From (6) and (7), t and s determines p:
p = t p1 + (1-t)p2 = s p3 + (1-s)p4 */
//=============================== End of File ======================================

90
source/dcdt/se/sr_geo2.h Normal file
View File

@ -0,0 +1,90 @@
/** \file sr_geo2.h
* 2d geometric primitives */
# ifndef SR_GEO2_H
# define SR_GEO2_H
# define SR_CCW(ax,ay,bx,by,cx,cy) ((ax*by)-(bx*ay)+(bx*cy)-(cx*by)+(cx*ay)-(ax*cy))
/*! Returns true if segments (p1,p2) and (p3,p4) intersect, and false otherwise. */
bool sr_segments_intersect ( double p1x, double p1y, double p2x, double p2y,
double p3x, double p3y, double p4x, double p4y );
/*! Returns true if segments (p1,p2) and (p3,p4) intersect, and false otherwise.
In case of intersection, p will be the intersection point. */
bool sr_segments_intersect ( double p1x, double p1y, double p2x, double p2y,
double p3x, double p3y, double p4x, double p4y,
double& x, double &y );
/*! Returns true if lines (p1,p2) and (p3,p4) intersect, and false otherwise. */
bool sr_lines_intersect ( double p1x, double p1y, double p2x, double p2y,
double p3x, double p3y, double p4x, double p4y );
/*! Returns true if lines (p1,p2) and (p3,p4) intersect, and false otherwise.
In case of intersection, p will be the intersection point. */
bool sr_lines_intersect ( double p1x, double p1y, double p2x, double p2y,
double p3x, double p3y, double p4x, double p4y,
double& x, double &y );
/*! Orthogonal projection of p in the line (p1,p2). The projected point becomes q. */
void sr_line_projection ( double p1x, double p1y, double p2x, double p2y,
double px, double py,
double& qx, double& qy );
/*! Returns true if the orthogonal projection of p is inside (within epsilon distance)
the segment (p1,p2). In such a case, the projected point becomes q, otherwise
false is returned. */
bool sr_segment_projection ( double p1x, double p1y, double p2x, double p2y,
double px, double py,
double& qx, double& qy, double epsilon );
/*! Returns the square of the distance between p1 and p2 */
double sr_dist2 ( double p1x, double p1y, double p2x, double p2y );
/*! Returns the minimum distance between p and segment (p1,p2) */
double sr_point_segment_dist ( double px, double py,
double p1x, double p1y, double p2x, double p2y );
/*! Returns the minimum distance between p and line (p1,p2) */
double sr_point_line_dist ( double px, double py, double p1x, double p1y, double p2x, double p2y );
/*! Returns true if the distance between p1 and p2 is smaller (or equal) than epsilon */
bool sr_next ( double p1x, double p1y, double p2x, double p2y, double epsilon );
/*! Returns >0 if the three points are in counter-clockwise order, <0 if
the order is clockwise and 0 if points are collinear. */
double sr_ccw ( double p1x, double p1y, double p2x, double p2y, double p3x, double p3y );
/*! Returns true if p is in the segment (p1,p2), within precision epsilon, and false
otherwise. More precisely, true is returned if the projection of p in the segment (p1,p2)
has a distance to p less or equal than epsilon. */
bool sr_in_segment ( double p1x, double p1y, double p2x, double p2y,
double px, double py, double epsilon );
/*! Returns true if p is in the segment (p1,p2), within precision epsilon, and false
otherwise. More precisely, true is returned if the projection of p in the segment
p1,p2) has a distance to p less or equal than epsilon. Parameter dist2 contains
the square of that distance. */
bool sr_in_segment ( double p1x, double p1y, double p2x, double p2y, double px, double py,
double epsilon, double& dist2 );
/*! Returns true if p is inside (or in the border) of triangle (p1,p2,p3), otherwise false
is returned. The test is based on 3 CCW>=0 tests and no epsilons are used. */
bool sr_in_triangle ( double p1x, double p1y, double p2x, double p2y, double p3x, double p3y,
double px, double py );
/*! Returns true if p is inside the circle passing at p1, p2, and p3, otherwise false
is returned. Points p1, p2 and p3 must be in ccw orientation.
This is a fast 4x4 determinant evaluation that will return true only
if p is strictly inside the circle, testing if determinant>1.0E-14 */
bool sr_in_circle ( double p1x, double p1y, double p2x, double p2y, double p3x, double p3y,
double px, double py );
/*! Returns (u,v,w), w==1-u-v, u+v+w==1, such that p1*u + p2*v + p3w == p */
void sr_barycentric ( double p1x, double p1y, double p2x, double p2y, double p3x, double p3y,
double px, double py, double& u, double& v, double& w );
//============================== end of file ===============================
# endif // SR_GEO2_H

73
source/dcdt/se/sr_gl.h Normal file
View File

@ -0,0 +1,73 @@
# ifndef SR_GL_H
# define SR_GL_H
/** \file sr_gl.h
* Sr wrapper and extensions for OpenGL
*
* Overload of most used OpenGL functions to directly work with SR types,
* and some extra utilities.
*/
# include <SR/sr.h>
# ifdef SR_TARGET_WINDOWS // defined in sr.h
# include <Windows.h>
# endif
# include <GL/gl.h>
class SrVec;
class SrMat;
class SrQuat;
class SrColor;
class SrLight;
class SrImage;
class SrOutput;
class SrMaterial;
//======================================= geometry ================================
void glNormal ( const SrVec &v );
void glVertex ( const SrVec &v );
void glVertex ( const SrVec &v1, const SrVec &v2 );
void glVertex ( const SrVec &v1, const SrVec &v2, const SrVec &v3 );
void glVertex ( const SrVec &v1, const SrVec &v2, const SrVec &v3, const SrVec &v4 );
void glVertex ( float x, float y, float z );
void glVertex ( float x, float y, float z, float a, float b, float c );
void glVertex ( float x, float y );
void glDrawBox ( const SrVec& a, const SrVec& b ); //!< Send quads with normals forming the box
//====================================== appearance ================================
void glClearColor ( const SrColor& c );
void glColor ( const SrColor& c );
void glLight ( int id, const SrLight& l ); //!< id = x E {0,...,7}, from GL_LIGHTx
void glMaterial ( const SrMaterial &m ); //!< Sets material for GL_FRONT_AND_BACK
void glMaterialFront ( const SrMaterial &m );
void glMaterialBack ( const SrMaterial &m );
//==================================== matrices ==============================
void glMultMatrix ( const SrMat &m );
void glLoadMatrix ( const SrMat &m );
void glTranslate ( const SrVec &v );
void glScale ( float s );
void glRotate ( const SrQuat &q );
void glLookAt ( const SrVec &eye, const SrVec &center, const SrVec &up );
void glPerspective ( float fovy, float aspect, float znear, float zfar );
void glGetViewMatrix ( SrMat &m );
void glGetProjectionMatrix ( SrMat &m );
//==================================== extras ==============================
void glSnapshot ( SrImage& img );
//==================================== info ==============================
void glPrintInfo ( SrOutput &o );
//================================ End of File ==================================
# endif // SR_GL_H

View File

@ -0,0 +1,48 @@
# ifndef SR_GL_RENDER_FUNCS_H
# define SR_GL_RENDER_FUNCS_H
/** \file sr_gl_render_funcs.h
* OpenGL render functions of SR shapes
*/
class SrSnShapeBase;
/*! All render functions used to render SR shapes are static methods
of class SrGlRenderFuncs. They are automatically registered to
the ogl render action. See SrGlRenderAction class description. */
class SrGlRenderFuncs
{ public:
static void render_model ( SrSnShapeBase* shape );
static void render_lines ( SrSnShapeBase* shape );
static void render_points ( SrSnShapeBase* shape );
/*! The default render mode is smooth, and flat has the same
effect of smooth shading. */
static void render_box ( SrSnShapeBase* shape );
/*! The resolution value stored in SrSnShapeBase
indicates how many triangles is used to
draw the sphere. A value lower or equal to 0.2 defines
the first level (8 triangles), then levels are increased
each time 0.2 is added to the resolution. Resolution 1.0
represents a well discretized sphere. */
static void render_sphere ( SrSnShapeBase* shape );
/*! The resolution value stored in SrSnShapeBase
represents 1/10th of the number of edges discretizing the base
circle of the cylinder. For ex., resolution 1.0 results in
10 edges, and gives 40 triangles to render the cylinder. */
static void render_cylinder ( SrSnShapeBase* shape );
static void render_polygon ( SrSnShapeBase* shape );
static void render_polygons ( SrSnShapeBase* shape );
};
//================================ End of File =================================================
# endif // SR_GL_RENDER_FUNCS_H

663
source/dcdt/se/sr_graph.cpp Normal file
View File

@ -0,0 +1,663 @@
#include "precompiled.h"
# include <stdlib.h>
# include "sr_graph.h"
# include "sr_heap.h"
//============================== SrGraphLink ===============================================
//============================== SrGraphNode ===============================================
SrGraphNode::~SrGraphNode ()
{
SrClassManagerBase* lman = _graph->link_class_manager();
if ( _links.size()>0 )
lman->free ( _links.pop() );
}
SrGraphLink* SrGraphNode::linkto ( SrGraphNode* n, float cost )
{
SrGraphLink* l = (SrGraphLink*) _graph->link_class_manager()->alloc();
l->_node = n;
l->_index = 0;
l->_cost = cost;
_links.push () = l;
return l;
}
void SrGraphNode::unlink ( int ni )
{
SrClassManagerBase* lman = _graph->link_class_manager();
lman->free ( _links[ni] );
_links[ni] = _links.pop();
}
int SrGraphNode::search_link ( SrGraphNode* n ) const
{
int i;
for ( i=0; i<_links.size(); i++ )
if ( _links[i]->node()==n ) return i;
return -1;
}
void SrGraphNode::output ( SrOutput& o ) const
{
float cost;
int i, max;
max = _links.size()-1;
if ( max<0 ) return;
SrClassManagerBase* lman = _graph->link_class_manager();
o<<'(';
for ( i=0; i<=max; i++ )
{ // output blocked status
o << _links[i]->_blocked << srspc;
// output index to target node
o << _links[i]->_node->_index << srspc;
// output link cost
cost = _links[i]->_cost;
if ( cost==SR_TRUNC(cost) ) o << int(cost);
else o << cost;
// output user data
o << srspc;
lman->output(o,_links[i]);
if ( i<max ) o<<srspc;
}
o<<')';
}
//============================== SrGraphPathTree ===============================================
class SrGraphPathTree
{ public :
struct Node { int parent; float cost; SrGraphNode* node; };
struct Leaf { int l; int d; }; // the leaf index in nodes array, and its depth
SrArray<Node> nodes;
SrHeap<Leaf,float> leafs;
SrGraphBase* graph;
SrGraphNode* closest;
int iclosest;
float cdist;
float (*distfunc) ( const SrGraphNode*, const SrGraphNode*, void* udata );
void *udata;
bool bidirectional_block;
public :
SrGraphPathTree ()
{ bidirectional_block = false;
}
void init ( SrGraphBase* g, SrGraphNode* n )
{ nodes.size(1);
nodes[0].parent = -1;
nodes[0].cost = 0;
nodes[0].node = n;
Leaf l;
l.l = l.d = 0;
leafs.insert ( l, 0 );
graph = g;
distfunc=0;
udata = 0;
closest = 0;
iclosest = 0;
cdist = 0;
}
bool has_leaf ()
{ return leafs.size()==0? false:true;
}
bool expand_lowest_cost_leaf ( SrGraphNode* goalnode )
{ int i;
int n = leafs.top().l;
int d = leafs.top().d;
leafs.remove ();
SrGraphNode* node = nodes[n].node;
Leaf leaf;
const SrArray<SrGraphLink*>& a = node->links();
for ( i=0; i<a.size(); i++ )
{ //sr_out<<a.size()<<srnl;
if ( graph->marked(a[i]) ||
a[i]->blocked() ||
a[i]->node()->blocked() ) continue;
if ( bidirectional_block )
{ if ( a[i]->node()->link(node)->blocked() ) continue; }
nodes.push();
nodes.top().parent = n;
nodes.top().cost = nodes[n].cost + a[i]->cost();
nodes.top().node = a[i]->node();
leaf.l = nodes.size()-1;
leaf.d = d+1;
leafs.insert ( leaf, nodes.top().cost );
graph->mark ( a[i] );
if ( distfunc )
{ float d = distfunc ( nodes.top().node, goalnode, udata );
if ( !closest || d<cdist )
{ closest=nodes.top().node; iclosest=nodes.size()-1; cdist=d; }
}
if ( a[i]->node()==goalnode ) return true;
}
return false;
}
};
//============================== SrGraphBase ===============================================
# define MARKFREE 0
# define MARKING 1
# define INDEXING 2
SrGraphBase::SrGraphBase ( SrClassManagerBase* nm, SrClassManagerBase* lm )
:_nodes(nm)
{
_curmark = 1;
_mark_status = MARKFREE;
_pt = 0; // allocated only if shortest path is called
_lman = lm;
_lman->ref(); // nm is managed by the list _nodes
_leave_indices_after_save = 0;
}
SrGraphBase::~SrGraphBase ()
{
_nodes.init (); // Important: this ensures that _lman is used before _lman->unref()
delete _pt;
_lman->unref();
}
void SrGraphBase::init ()
{
_nodes.init();
_curmark = 1;
_mark_status = MARKFREE;
}
void SrGraphBase::compress ()
{
if ( _nodes.empty() ) return;
_nodes.gofirst();
do { _nodes.cur()->compress();
} while ( _nodes.notlast() );
}
int SrGraphBase::num_links () const
{
if ( _nodes.empty() ) return 0;
int n=0;
SrGraphNode* first = _nodes.first();
SrGraphNode* cur = first;
do { n += cur->num_links();
cur = cur->next();
} while ( cur!=first );
return n;
}
//----------------------------------- marking --------------------------------
void SrGraphBase::begin_marking ()
{
if ( _mark_status!=MARKFREE ) sr_out.fatal_error("SrGraphBase::begin_mark() is locked!");
_mark_status = MARKING;
if ( _curmark==sruintmax ) _normalize_mark ();
else _curmark++;
}
void SrGraphBase::end_marking ()
{
_mark_status=MARKFREE;
}
bool SrGraphBase::marked ( SrGraphNode* n )
{
if ( _mark_status!=MARKING ) sr_out.fatal_error ( "SrGraphBase::marked(n): marking is not active!\n" );
return n->_index==_curmark? true:false;
}
void SrGraphBase::mark ( SrGraphNode* n )
{
if ( _mark_status!=MARKING ) sr_out.fatal_error ( "SrGraphBase::mark(n): marking is not active!\n" );
n->_index = _curmark;
}
void SrGraphBase::unmark ( SrGraphNode* n )
{
if ( _mark_status!=MARKING ) sr_out.fatal_error ( "SrGraphBase::unmark(n): marking is not active!\n");
n->_index = _curmark-1;
}
bool SrGraphBase::marked ( SrGraphLink* l )
{
if ( _mark_status!=MARKING ) sr_out.fatal_error ( "SrGraphBase::marked(l): marking is not active!\n" );
return l->_index==_curmark? true:false;
}
void SrGraphBase::mark ( SrGraphLink* l )
{
if ( _mark_status!=MARKING ) sr_out.fatal_error ( "SrGraphBase::mark(l): marking is not active!\n" );
l->_index = _curmark;
}
void SrGraphBase::unmark ( SrGraphLink* l )
{
if ( _mark_status!=MARKING ) sr_out.fatal_error ( "SrGraphBase::unmark(l): marking is not active!\n");
l->_index = _curmark-1;
}
//----------------------------------- indexing --------------------------------
void SrGraphBase::begin_indexing ()
{
if ( _mark_status!=MARKFREE ) sr_out.fatal_error("SrGraphBase::begin_indexing() is locked!");
_mark_status = INDEXING;
}
void SrGraphBase::end_indexing ()
{
_normalize_mark ();
_mark_status=MARKFREE;
}
sruint SrGraphBase::index ( SrGraphNode* n )
{
if ( _mark_status!=INDEXING ) sr_out.fatal_error ("SrGraphBase::index(n): indexing is not active!");
return n->_index;
}
void SrGraphBase::index ( SrGraphNode* n, sruint i )
{
if ( _mark_status!=INDEXING ) sr_out.fatal_error ("SrGraphBase::index(n,i): indexing is not active!");
n->_index = i;
}
sruint SrGraphBase::index ( SrGraphLink* l )
{
if ( _mark_status!=INDEXING ) sr_out.fatal_error ("SrGraphBase::index(l): indexing is not active!");
return l->_index;
}
void SrGraphBase::index ( SrGraphLink* l, sruint i )
{
if ( _mark_status!=INDEXING ) sr_out.fatal_error ("SrGraphBase::index(l,i): indexing is not active!");
l->_index = i;
}
//----------------------------------- construction --------------------------------
SrGraphNode* SrGraphBase::insert ( SrGraphNode* n )
{
_nodes.insert_next ( n );
n->_graph = this;
return n;
}
SrGraphNode* SrGraphBase::extract ( SrGraphNode* n )
{
_nodes.cur(n);
return _nodes.extract();
}
void SrGraphBase::remove_node ( SrGraphNode* n )
{
_nodes.cur(n);
_nodes.remove();
}
int SrGraphBase::remove_link ( SrGraphNode* n1, SrGraphNode* n2 )
{
int i;
int n=0;
while ( true )
{ i = n1->search_link(n2);
if ( i<0 ) break;
n++;
n1->unlink(i);
}
while ( true )
{ i = n2->search_link(n1);
if ( i<0 ) break;
n++;
n2->unlink(i);
}
return n;
}
void SrGraphBase::link ( SrGraphNode* n1, SrGraphNode* n2, float c )
{
n1->linkto(n2,c);
n2->linkto(n1,c);
}
//----------------------------------- get edges ----------------------------------
void SrGraphBase::get_directed_edges ( SrArray<SrGraphNode*>& edges )
{
edges.size ( num_nodes() );
edges.size ( 0 );
int i;
SrGraphNode* n;
SrListIterator<SrGraphNode> it(_nodes);
for ( it.first(); it.inrange(); it.next() )
{ n = it.get();
for ( i=0; i<n->num_links(); i++ )
{ edges.push() = n;
edges.push() = n->link(i)->node();
}
}
}
void SrGraphBase::get_undirected_edges ( SrArray<SrGraphNode*>& edges )
{
edges.size ( num_nodes() );
edges.size ( 0 );
int i, li;
SrGraphNode* n;
SrListIterator<SrGraphNode> it(_nodes);
begin_marking();
for ( it.first(); it.inrange(); it.next() )
{ n = it.get();
for ( i=0; i<n->links().size(); i++ )
{ if ( !marked(n->link(i)) )
{ edges.push() = n;
edges.push() = n->link(i)->node();
mark ( n->link(i) );
li = edges.top()->search_link(n);
if ( li>=0 ) mark ( edges.top()->link(li) );
}
}
}
end_marking();
}
//----------------------------------- components ----------------------------------
static void _traverse ( SrGraphBase* graph,
SrArray<SrGraphNode*>& stack,
SrArray<SrGraphNode*>& nodes )
{
int i;
SrGraphNode *n, *ln;
while ( stack.size()>0 )
{ n = stack.pop();
graph->mark ( n );
nodes.push() = n;
for ( i=0; i<n->num_links(); i++ )
{ ln = n->link(i)->node();
if ( !graph->marked(ln) ) stack.push()=ln;
}
}
}
void SrGraphBase::get_connected_nodes ( SrGraphNode* source, SrArray<SrGraphNode*>& nodes )
{
nodes.size ( num_nodes() );
nodes.size ( 0 );
SrArray<SrGraphNode*>& stack = _buffer;
stack.size ( 0 );
begin_marking();
stack.push() = source;
_traverse ( this, stack, nodes );
end_marking();
}
void SrGraphBase::get_disconnected_components ( SrArray<int>& components, SrArray<SrGraphNode*>& nodes )
{
nodes.size ( num_nodes() );
nodes.size ( 0 );
components.size ( 0 );
SrArray<SrGraphNode*>& stack = _buffer;
stack.size ( 0 );
SrGraphNode* n;
SrListIterator<SrGraphNode> it(_nodes);
begin_marking();
for ( it.first(); it.inrange(); it.next() )
{ n = it.get();
if ( !marked(n) )
{ components.push() = nodes.size();
stack.push() = n;
_traverse ( this, stack, nodes );
components.push() = nodes.size()-1;
}
}
end_marking();
}
//----------------------------------- shortest path ----------------------------------
float SrGraphBase::get_shortest_path ( SrGraphNode* n1,
SrGraphNode* n2,
SrArray<SrGraphNode*>& path,
float (*distfunc) ( const SrGraphNode*, const SrGraphNode*, void* udata ),
void* udata )
{
path.size(0);
if ( n1==n2 ) { path.push()=n1; return 0; }
begin_marking ();
if ( !_pt ) _pt = new SrGraphPathTree;
_pt->init ( this, n2 );
if ( distfunc )
{ _pt->distfunc = distfunc;
_pt->udata = udata;
}
int i;
bool end = false;
while ( !end )
{ if ( !_pt->has_leaf() )
{ end_marking();
if ( !distfunc ) return 0;
i = _pt->iclosest;
float cost = _pt->nodes[i].cost;
while ( i>=0 )
{ path.push () = _pt->nodes[i].node;
i = _pt->nodes[i].parent;
}
return cost;
}
end = _pt->expand_lowest_cost_leaf ( n1 );
}
end_marking ();
i = _pt->nodes.size()-1; // last element is n1
float cost = _pt->nodes[i].cost;
while ( i>=0 )
{ path.push () = _pt->nodes[i].node;
i = _pt->nodes[i].parent;
}
return cost;
}
bool SrGraphBase::local_search
( SrGraphNode* startn,
SrGraphNode* endn,
int maxdepth,
float maxdist,
int& depth,
float& dist )
{
depth=0;
dist=0;
if ( startn==endn ) return true;
begin_marking ();
if ( !_pt ) _pt = new SrGraphPathTree;
_pt->init ( this, startn );
int i;
bool not_found = false;
bool end = false;
while ( !end )
{ if ( !_pt->has_leaf() ) { not_found=true; break; } // not found!
dist = _pt->nodes[_pt->leafs.top().l].cost;
depth = _pt->leafs.top().d;
if ( maxdepth>0 && depth>maxdepth ) { i=-1; break; } // max depth reached
if ( maxdist>0 && dist>maxdist ) { i=-1; break; } // max dist reached
end = _pt->expand_lowest_cost_leaf ( endn );
}
end_marking ();
if ( not_found ) return false; // not found!
return true;
}
void SrGraphBase::bidirectional_block_test ( bool b )
{
if ( !_pt ) _pt = new SrGraphPathTree;
_pt->bidirectional_block = b;
}
//------------------------------------- I/O --------------------------------
void SrGraphBase::output ( SrOutput& o )
{
SrListIterator<SrGraphNode> it(_nodes);
SrClassManagerBase* nman = node_class_manager();
// set indices
if ( _mark_status!=MARKFREE ) sr_out.fatal_error("SrGraphBase::operator<<(): begin_indexing() is locked!");
sruint i=0;
begin_indexing();
for ( it.first(); it.inrange(); it.next() ) index(it.get(),i++);
// print
o<<'[';
for ( it.first(); it.inrange(); it.next() )
{ o << it->index() << srspc; // output node index
o << it->_blocked << srspc; // output node blocked status
nman->output ( o, it.get() ); // output user data
if ( it.get()->num_links()>0 ) // output node links (blocked, ids, cost, and udata)
{ o<<srspc;
it->SrGraphNode::output ( o );
}
if ( !it.inlast() ) o << srnl;
}
o<<']';
if ( !_leave_indices_after_save ) end_indexing();
_leave_indices_after_save = 0;
}
static void set_blocked ( int& blocked, const char* last_token )
{
if ( last_token[0]=='b' ) // back-compatibility
blocked = 1;
else if ( last_token[0]=='f' ) // back-compatibility
blocked = 0;
else // should be an integer
blocked = atoi(last_token);
}
void SrGraphBase::input ( SrInput& inp )
{
SrArray<SrGraphNode*>& nodes = _buffer;
nodes.size(128);
nodes.size(0);
SrClassManagerBase* nman = node_class_manager();
SrClassManagerBase* lman = link_class_manager();
init ();
inp.getd(); // [
inp.get_token(); // get node counter (which is not needed)
while ( inp.last_token()[0]!=']' )
{
nodes.push() = _nodes.insert_next(); // allocate one node
nodes.top()->_graph = this;
inp.get_token(); // get node blocked status
set_blocked ( nodes.top()->_blocked, inp.last_token() );
nman->input ( inp, nodes.top() ); // read node user data
inp.get_token(); // new node counter, or '(', or ']'
if ( inp.last_token()[0]=='(')
while ( true )
{ inp.get_token();
if ( inp.last_token()[0]==')' ) { inp.get_token(); break; }
SrArray<SrGraphLink*>& la = nodes.top()->_links;
la.push() = (SrGraphLink*) lman->alloc();
set_blocked ( la.top()->_blocked, inp.last_token() ); // get link blocked status
inp.get_token(); // get id
la.top()->_index = atoi(inp.last_token()); // store id
inp.getn(); // get cost
la.top()->_cost = (float) atof(inp.last_token()); // store cost
lman->input ( inp, la.top() );
}
}
// now convert indices to pointers:
int i, j;
for ( i=0; i<nodes.size(); i++ )
{ SrArray<SrGraphLink*>& la = nodes[i]->_links;
for ( j=0; j<la.size(); j++ )
{ la[j]->_node = nodes[ int(la[j]->_index) ];
}
}
}
//---------------------------- private methods --------------------------------
// set all indices (nodes and links) to 0 and curmark to 1
void SrGraphBase::_normalize_mark()
{
int i;
SrGraphNode* n;
if ( _nodes.empty() ) return;
_nodes.gofirst();
do { n = _nodes.cur();
n->_index = 0;
for ( i=0; i<n->num_links(); i++ ) n->link(i)->_index=0;
} while ( _nodes.notlast() );
_curmark = 1;
}
//============================== end of file ===============================

320
source/dcdt/se/sr_graph.h Normal file
View File

@ -0,0 +1,320 @@
# ifndef SR_GRAPH_H
# define SR_GRAPH_H
/** \file sr_graph.h
* Graph maintenance classes
*/
# include "sr_array.h"
# include "sr_list.h"
class SrGraphNode;
class SrGraphBase;
/*! SrGraphLink contains the minimal needed information for
a link. To attach user-related information, SrGraphLink
must be derived and as well a corresponding
SrClassManagerBased, which will manage only the user data. */
class SrGraphLink
{ private :
SrGraphNode* _node; // node pointing to
sruint _index; // index for traversing
float _cost; // cost for minimum paths
int _blocked; // used as boolean or as a ref counter
friend class SrGraphNode;
friend class SrGraphBase;
protected :
SrGraphLink () { _index=0; _cost=0; _node=0; _blocked=0; }
~SrGraphLink () {}
public :
void cost ( float c ) { _cost=c; }
float cost () const { return _cost; }
SrGraphNode* node () const { return _node; }
sruint index () const { return _index; }
int blocked () const { return _blocked; }
void blocked ( bool b ) { _blocked = b? 1:0; }
void blocked ( int b ) { _blocked = b; }
};
/*! SrGraphNode contains the minimal needed information for
a node. To attach user-related information, SrGraphLink
must be derived and as well a corresponding
SrClassManagerBased, which will manage only the user data. */
class SrGraphNode : public SrListNode
{ private :
SrArray<SrGraphLink*> _links;
sruint _index;
int _blocked; // used as boolean or as a ref counter
SrGraphBase* _graph;
friend class SrGraphBase;
protected :
SrGraphNode () { _index=0; _graph=0; _blocked=0; }
~SrGraphNode ();
public :
/*! Returns the next node in the global list of nodes */
SrGraphNode* next() const { return (SrGraphNode*) SrListNode::next(); }
/*! Returns the prior node in the global list of nodes */
SrGraphNode* prior() const { return (SrGraphNode*) SrListNode::prior(); }
/*! Returns the index of this node */
sruint index () { return _index; }
int blocked () const { return _blocked; }
void blocked ( bool b ) { _blocked = b? 1:0; }
void blocked ( int b ) { _blocked = b; }
/*! Compress internal links array */
void compress () { _links.compress(); }
/*! Returns the new link created */
SrGraphLink* linkto ( SrGraphNode* n, float cost=0 );
/*! Remove link of index ni, with a fast remove: the order in the
links array is not mantained */
void unlink ( int ni );
/*! Remove the link pointing to n. Should be called ONLY when it is
guaranteed that the link to n really exists! Uses method unlink(ni) */
void unlink ( SrGraphNode* n ) { unlink ( search_link(n) ); }
/*! Returns the index in the links array, or -1 if not linked to n */
int search_link ( SrGraphNode* n ) const;
/*! Returns the link pointing to n. Should be called ONLY when it is
guaranteed that the link to n really exists! */
SrGraphLink* link ( SrGraphNode*n ) { return _links[search_link(n)]; }
/*! Returns the link index i, which must be a valid index */
SrGraphLink* link ( int i ) { return _links[i]; }
/*! Returns the last link */
SrGraphLink* last_link () { return _links.top(); }
/*! Returns the number of links in this node */
int num_links () const { return _links.size(); }
/*! Returns the links array */
const SrArray<SrGraphLink*>& links() const { return _links; }
private :
void output ( SrOutput& o ) const;
};
/*! The following define is to be used inside a user derived class
of SrGraphLink, in order to easily redefine public SrGraphLink
methods with correct type casts. */
# define SR_GRAPH_LINK_CASTED_METHODS(N,L) \
N* node() const { return (N*)SrGraphLink::node(); }
/*! The following define is to be used inside a user derived class
of SrGraphNode, in order to easily redefine public SrGraphNode
methods with correct type casts. */
# define SR_GRAPH_NODE_CASTED_METHODS(N,L) \
N* next() const { return (N*)SrListNode::next(); } \
N* prior() const { return (N*)SrListNode::prior(); } \
L* linkto ( N* n, float c ) { return (L*)SrGraphNode::linkto(n,c); } \
L* link ( N* n ) { return (L*)SrGraphNode::link(n); } \
L* link ( int i ) { return (L*)SrGraphNode::link(i); } \
const SrArray<L*>& links() const { return (const SrArray<L*>&) SrGraphNode::links(); }
class SrGraphPathTree;
/*! SrGraphBase maintains a directed graph with nodes and links. Links around
a node do not have any order meaning.
Note also that the user should avoid redundant links, as no tests are done
(for speed purposes) in several methods.
SrGraphBase is the base class, the user should use SrGraph template instead. */
class SrGraphBase
{ private :
SrList<SrGraphNode> _nodes;
SrArray<SrGraphNode*> _buffer;
sruint _curmark;
char _mark_status;
SrGraphPathTree* _pt;
SrClassManagerBase* _lman; // link manager for a class deriving SrGraphLink
char _leave_indices_after_save;
public :
/*! Constructor requires managers for nodes and links */
SrGraphBase ( SrClassManagerBase* nm, SrClassManagerBase* lm );
/*! Destructor deletes all data in the graph */
~SrGraphBase ();
/*! Returns a pointer to the node manager */
SrClassManagerBase* node_class_manager() const { return _nodes.class_manager(); }
/*! Returns a pointer to the link manager */
SrClassManagerBase* link_class_manager() const { return _lman; }
/*! Make the graph empty */
void init ();
/*! Compress all link arrays in the nodes */
void compress ();
/*! Returns the number of nodes in the graph */
int num_nodes () const { return _nodes.elements(); }
/*! Counts and returns the number of (directional) links in the graph */
int num_links () const;
/*! Methods for marking nodes and links */
void begin_marking ();
void end_marking ();
bool marked ( SrGraphNode* n );
void mark ( SrGraphNode* n );
void unmark ( SrGraphNode* n );
bool marked ( SrGraphLink* l );
void mark ( SrGraphLink* l );
void unmark ( SrGraphLink* l );
/*! Methods for indexing nodes and links */
void begin_indexing ();
void end_indexing ();
sruint index ( SrGraphNode* n );
void index ( SrGraphNode* n, sruint i );
sruint index ( SrGraphLink* l );
void index ( SrGraphLink* l, sruint i );
/*! Returns the list of nodes, that should be used with
care to not invalidate some SrGraph operations. */
SrList<SrGraphNode>& nodes () { return _nodes; }
/*! Inserts node n in the list of nodes, as a new
unconnected component. n is returned. */
SrGraphNode* insert ( SrGraphNode* n );
/*! Extract (without deleting) the node from the graph. Nothing
is done concerning possible links between the graph and
the node being extracted */
SrGraphNode* extract ( SrGraphNode* n );
/*! Removes and deletes the node from the graph. Its the user
responsibility to ensure that the graph has no links with
the node being deleted */
void remove_node ( SrGraphNode* n );
/*! Searches and removes the edge(s) linking n1 with n2 if any.
Links are removed with a "fast remove process" so that indices
in the links array of the involved nodes may be modified.
Return the number of (directed) links removed. */
int remove_link ( SrGraphNode* n1, SrGraphNode* n2 );
/*! Links n1 to n2 and n2 to n1, with cost c in both directions */
void link ( SrGraphNode* n1, SrGraphNode* n2, float c=0 );
/*! Returns the first node of the list of nodes */
SrGraphNode* first_node () const { return _nodes.first(); }
/*! Get all directed edges of the graph. Note that if n1 is linked
to n2 and n2 is linked to n1, both edges (n1,n2) and (n2,n1) will
appear in the edges array */
void get_directed_edges ( SrArray<SrGraphNode*>& edges );
/*! Get all edges of the graph without duplications from the directional
point of view. */
void get_undirected_edges ( SrArray<SrGraphNode*>& edges );
/*! Get all nodes which are in the same connected component of source */
void get_connected_nodes ( SrGraphNode* source, SrArray<SrGraphNode*>& nodes );
/*! Organize nodes by connected components. The indices in array components say each
start and end position in array nodes for each component */
void get_disconnected_components ( SrArray<int>& components, SrArray<SrGraphNode*>& nodes );
/*! The returned path contains pointers to existing nodes in the graph.
In the case the two nodes are in two different disconnected components
an empty path is returned. If n1==n2 a path with the single node n1
is returned. In all cases, returns the distance (cost) of path.
In case no path is found, the optional parameters distfunc and udata
can be used to return the path to the closest processed node to the goal. */
float get_shortest_path ( SrGraphNode* n1, SrGraphNode* n2, SrArray<SrGraphNode*>& path,
float (*distfunc) ( const SrGraphNode*, const SrGraphNode*, void* udata )=0,
void* udata=0 );
/*! Performs a A* search from startn, until finding endn. The search is
stopped if either maxnodes or maxdist is reached. If these parameters
are <0 they are not taken into account. True is returned if endn could
be reached, and parameters depth and dist will contain the final
higher depth and distance (along the shortest path) reached. */
bool local_search ( SrGraphNode* startn, SrGraphNode* endn,
int maxnodes, float maxdepth, int& depth, float& dist );
/*! When set to true, graph search will consider a link blocked if any of its
directions is blocked, i.e., blocking only one direction of a link will
block both the directions. Afftected methods are get_shortest_path() and
local_search(). Default is false. */
void bidirectional_block_test ( bool b );
bool are_near_in_graph ( SrGraphNode* startn, SrGraphNode* endn, int maxnodes );
/*! If this is set to true, it will be the user responsibility to call
end_indexing() after the next graph save. It is used to retrieve the indices
used during saving to reference additional data to be saved in derived classes. */
void leave_indices_after_save ( bool b ) { _leave_indices_after_save = b? 1:0; }
/*! Returns the internal buffer, which might be usefull for some specific needs;
see for instance the description of the input() method. */
SrArray<SrGraphNode*>& buffer () { return _buffer; }
/*! Outputs the graph in the format: [ (l1..lk)e1 (..)e2 (..)en ].
Nodes are indexed (starting from 0), and after the output
all indices of the nodes are set to 0. */
void output ( SrOutput& o );
/*! Initializes the current graph and load another one from the given input.
Method buffer() can be used to retrieve an array with all nodes loaded,
indexed by the indices used in the loaded file */
void input ( SrInput& i );
friend SrOutput& operator<< ( SrOutput& o, SrGraphBase& g ) { g.output(o); return o; }
friend SrInput& operator>> ( SrInput& i, SrGraphBase& g ) { g.input(i); return i; }
private :
void _normalize_mark();
};
/*! SrGraph is a template that includes type casts for the user
derived types (of SrGraphNode and SrGraphLink) to correctly
call SrGraphBase methods. */
template <class N, class L>
class SrGraph : public SrGraphBase
{ public :
SrGraph () : SrGraphBase ( new SrClassManager<N>, new SrClassManager<L> ) {}
SrGraph ( SrClassManagerBase* nm, SrClassManagerBase* lm ) : SrGraphBase ( nm, lm ) {}
const SrList<N>& nodes () { return (SrList<N>&) SrGraphBase::nodes(); }
N* insert ( N* n ) { return (N*) SrGraphBase::insert((SrGraphNode*)n); }
N* extract ( N* n ) { return (N*) SrGraphBase::extract((SrGraphNode*)n); }
N* first_node () const { return (N*) SrGraphBase::first_node(); }
void get_undirected_edges ( SrArray<N*>& edges )
{ SrGraphBase::get_undirected_edges( (SrArray<SrGraphNode*>&)edges ); }
void get_disconnected_components ( SrArray<int>& components, SrArray<N*>& nodes )
{ SrGraphBase::get_disconnected_components( components, (SrArray<SrGraphNode*>&)nodes ); }
float get_shortest_path ( N* n1, N* n2, SrArray<N*>& path,
float (*distfunc) ( const SrGraphNode*, const SrGraphNode*, void* udata )=0,
void* udata=0 )
{ return SrGraphBase::get_shortest_path((SrGraphNode*)n1,(SrGraphNode*)n2,(SrArray<SrGraphNode*>&)path,distfunc,udata); }
SrArray<N*>& buffer () { return (SrArray<N*>&) SrGraphBase::buffer(); }
friend SrOutput& operator<< ( SrOutput& o, SrGraph& g ) { return o<<(SrGraphBase&)g; }
friend SrInput& operator>> ( SrInput& i, SrGraph& g ) { return i>>(SrGraphBase&)g; }
};
//================================ End of File =================================================
# endif // SR_GRAPH_H

249
source/dcdt/se/sr_grid.cpp Normal file
View File

@ -0,0 +1,249 @@
#include "precompiled.h"
# include "sr_grid.h"
//============================ SrGridBase =================================
SrGridBase::SrGridBase ( int dim, int ns )
{
_cells = 0;
if ( dim*ns>0 ) init ( dim, ns );
}
void SrGridBase::init ( int dim, int ns )
{
int i;
_axis.size ( dim );
for ( i=0; i<dim; i++ )
{ _axis[i].min = 0.0f;
_axis[i].max = 1.0f;
_axis[i].segs = ns;
}
init ( _axis );
}
void SrGridBase::init ( const SrArray<SrGridAxis>& axis_desc )
{
if ( &axis_desc != &_axis ) _axis=axis_desc;
if ( _axis.size()==0 ) { _cells=0; _size.size(0); _seglen.size(0); return; }
int i, j;
_cells = _axis[0].segs;
int dim = _axis.size();
for ( i=1; i<dim; i++ )
{ _cells *= _axis[i].segs;
}
_size.size ( dim );
for ( i=0; i<dim; i++ )
{ _size[i] = 1;
for ( j=0; j<i; j++ ) _size[i]*=_axis[j].segs;
}
_seglen.size ( dim );
for ( i=0; i<dim; i++ )
_seglen[i] = (_axis[i].max-_axis[i].min)/float(_axis[i].segs);
}
int SrGridBase::cell_index ( int i, int j ) const
{
if ( dimensions()!=2 ) return 0;
return _size[1]*j + i;
}
int SrGridBase::cell_index ( int i, int j, int k ) const
{
if ( dimensions()!=3 ) return 0;
return _size[2]*k + _size[1]*j + i;
}
int SrGridBase::cell_index ( const SrArray<int>& coords ) const
{
int dim = _axis.size();
if ( dim!=coords.size() || dim<=0 ) return 0;
int i, cell = coords[0];
for ( i=1; i<dim; i++ ) cell += coords[i]*_size[i];
return cell;
}
void SrGridBase::cell_coords ( int index, int& i, int& j ) const
{
if ( dimensions()!=2 ) { i=j=0; return; }
j = index/_size[1]; index=index%_size[1];
i = index;
}
void SrGridBase::cell_coords ( int index, int& i, int& j, int& k ) const
{
if ( dimensions()!=3 ) { i=j=k=0; return; }
k = index/_size[2]; index=index%_size[2];
j = index/_size[1]; index=index%_size[1];
i = index;
}
void SrGridBase::cell_coords ( int index, SrArray<int>& coords ) const
{
int d = dimensions();
if ( coords.size()!=d ) coords.size(d);
d--;
while ( d>0 )
{ coords[d] = index/_size[d];
index = index%_size[d];
d--;
}
coords[0] = index;
}
void SrGridBase::cell_boundary ( int i, int j, SrPnt2& a, SrPnt2& b ) const
{
if ( dimensions()!=2 ) return;
a.x = _axis[0].min + float(i)*_seglen[0];
a.y = _axis[1].min + float(j)*_seglen[1];
b.x = a.x + _seglen[0];
b.y = a.y + _seglen[1];
}
void SrGridBase::cell_boundary ( int i, int j, int k, SrPnt& a, SrPnt& b ) const
{
if ( dimensions()!=3 ) return;
a.x = _axis[0].min + float(i)*_seglen[0];
a.y = _axis[1].min + float(j)*_seglen[1];
a.z = _axis[2].min + float(k)*_seglen[2];
b.x = a.x + _seglen[0];
b.y = a.y + _seglen[1];
b.z = a.z + _seglen[2];
}
void SrGridBase::get_intersection ( SrPnt2 a, SrPnt2 b, SrArray<int>& cells ) const
{
if ( dimensions()!=2 ) return;
if ( a.x>b.x || a.y>b.y ) return;
if ( a.x>=_axis[0].max || a.y>=_axis[1].max ) return;
if ( b.x<=_axis[0].min || b.y<=_axis[1].min ) return;
float f = _seglen[0]/2;
a.x = SR_BOUND ( a.x, _axis[0].min, _axis[0].max-f );
b.x = SR_BOUND ( b.x, _axis[0].min, _axis[0].max-f );
f = _seglen[1]/2;
a.y = SR_BOUND ( a.y, _axis[1].min, _axis[1].max-f );
b.y = SR_BOUND ( b.y, _axis[1].min, _axis[1].max-f );
int i1 = (int) ((a.x-_axis[0].min) / _seglen[0]);
int j1 = (int) ((a.y-_axis[1].min) / _seglen[1]);
f = (b.x-_axis[0].min) / _seglen[0];
int i2 = int(f); if ( float(i2)==f ) i2--;
f = (b.y-_axis[1].min) / _seglen[1];
int j2 = int(f); if ( float(j2)==f ) j2--;
int i, j, index;
for ( j=j1; j<=j2; j++ )
{ index = _size[1]*j + i1; // == cell_index(i1,j);
for ( i=i1; i<=i2; i++ )
{ cells.push() = index;
index++;
}
}
}
void SrGridBase::get_intersection ( SrPnt a, SrPnt b, SrArray<int>& cells ) const
{
if ( dimensions()!=3 ) return;
if ( a.x>b.x || a.y>b.y || a.z>b.z ) return;
if ( a.x>=_axis[0].max || a.y>=_axis[1].max || a.z>=_axis[2].max ) return;
if ( b.x<=_axis[0].min || b.y<=_axis[1].min || b.z<=_axis[2].min ) return;
float f = _seglen[0]/2;
a.x = SR_BOUND ( a.x, _axis[0].min, _axis[0].max-f );
b.x = SR_BOUND ( b.x, _axis[0].min, _axis[0].max-f );
f = _seglen[1]/2;
a.y = SR_BOUND ( a.y, _axis[1].min, _axis[1].max-f );
b.y = SR_BOUND ( b.y, _axis[1].min, _axis[1].max-f );
f = _seglen[2]/2;
a.z = SR_BOUND ( a.z, _axis[2].min, _axis[2].max-f );
b.z = SR_BOUND ( b.z, _axis[2].min, _axis[2].max-f );
int i1 = (int) ((a.x-_axis[0].min) / _seglen[0]);
int j1 = (int) ((a.y-_axis[1].min) / _seglen[1]);
int k1 = (int) ((a.z-_axis[2].min) / _seglen[2]);
f = (b.x-_axis[0].min) / _seglen[0];
int i2 = int(f); if ( float(i2)==f ) i2--;
f = (b.y-_axis[1].min) / _seglen[1];
int j2 = int(f); if ( float(j2)==f ) j2--;
f = (b.z-_axis[2].min) / _seglen[2];
int k2 = int(f); if ( float(k2)==f ) k2--;
int i, j, k, index;
for ( k=k1; k<=k2; k++ )
for ( j=j1; j<=j2; j++ )
{ index = _size[2]*k + _size[1]*j + i1; // == cell_index(i1,j,k);
for ( i=i1; i<=i2; i++ )
{ cells.push() = index;
index++;
}
}
}
int SrGridBase::get_point_location ( SrPnt2 a ) const
{
if ( dimensions()!=2 ) return -1;
if ( a.x>=_axis[0].max || a.y>=_axis[1].max ) return -1;
if ( a.x<=_axis[0].min || a.y<=_axis[1].min ) return -1;
a.x = SR_BOUND ( a.x, _axis[0].min, _axis[0].max-(_seglen[0]/2) );
a.y = SR_BOUND ( a.y, _axis[1].min, _axis[1].max-(_seglen[1]/2) );
int i = (int) ((a.x-_axis[0].min) / _seglen[0]);
int j = (int) ((a.y-_axis[1].min) / _seglen[1]);
return _size[1]*j + i;
}
int SrGridBase::get_point_location ( SrPnt a ) const
{
if ( dimensions()!=3 ) return -1;
if ( a.x>=_axis[0].max || a.y>=_axis[1].max || a.z>=_axis[2].max ) return -1;
if ( a.x<=_axis[0].min || a.y<=_axis[1].min || a.z<=_axis[2].min ) return -1;
a.x = SR_BOUND ( a.x, _axis[0].min, _axis[0].max-(_seglen[0]/2) );
a.y = SR_BOUND ( a.y, _axis[1].min, _axis[1].max-(_seglen[1]/2) );
a.z = SR_BOUND ( a.z, _axis[2].min, _axis[2].max-(_seglen[2]/2) );
int i = (int) ((a.x-_axis[0].min) / _seglen[0]);
int j = (int) ((a.y-_axis[1].min) / _seglen[1]);
int k = (int) ((a.z-_axis[2].min) / _seglen[2]);
return _size[2]*k + _size[1]*j + i;
}
//============================== end of file ===============================

167
source/dcdt/se/sr_grid.h Normal file
View File

@ -0,0 +1,167 @@
# ifndef SR_GRID_H
# define SR_GRID_H
/** \file sr_grid.h
* Manages data in a n-D regular grid */
# include "sr_vec.h"
# include "sr_vec2.h"
# include "sr_array.h"
# include "sr_output.h"
# include "sr_class_manager.h"
/*! Describes how to subdivide one dimensional axis.
This class is required to set up the decomposition in SrGrid */
class SrGridAxis
{ public :
int segs; // number of segments to divide this axis
float min; // minimum coordinate
float max; // maximum coordinate
public :
SrGridAxis ( int se, float mi, float ma )
{ segs=se; min=mi; max=ma; }
void set ( int se, float mi, float ma )
{ segs=se; min=mi; max=ma; }
friend SrOutput& operator<< ( SrOutput& o, const SrGridAxis& a )
{ return o << a.segs << srspc << a.min << srspc << a.max; }
friend SrInput& operator>> ( SrInput& i, SrGridAxis& a )
{ return i >> a.segs >> a.min >> a.max; }
};
/*! \class SrGridBase sr_grid.h
\brief n-D regular grid base class
SrGridBase has methods to translate n dimensional grid coordinates
into a one dimension bucket array coordinate. Use the derived SrGrid
template class to associate user data to the grid. */
class SrGridBase
{ private :
SrArray<SrGridAxis> _axis;
SrArray<int> _size; // subspaces sizes for fast bucket determination
SrArray<float> _seglen; // axis segments lengths for fast cell determination
int _cells;
public :
/*! Constructs a grid of given dimension (dim) and number of
segments in each axis (ns). The number of cells will then be
ns^dim. Axis are normalized in [0,1] */
SrGridBase ( int dim=0, int ns=0 );
/*! Init the grid with given dimension and number of
segments in each axis. The number of cells will then be
ns^dim. Axis are normalized in [0,1] */
void init ( int dim, int ns );
/*! Destroy the existing grid and initializes one according to
the descriptions sent in the desc array, which size will
define the dimension of the grid and thus the dimension
of the index tuple to specify a grid */
void init ( const SrArray<SrGridAxis>& axis_desc );
const SrArray<SrGridAxis>& axis_desc() const { return _axis; }
float max_coord ( int axis ) const { return _axis[axis].max; }
float min_coord ( int axis ) const { return _axis[axis].min; }
int segments ( int axis ) const { return _axis[axis].segs; }
float seglen ( int axis ) const { return _seglen[axis]; }
/*! Returns the number of dimensions of the grid */
int dimensions () const { return _axis.size(); }
/*! Returns the total number of cells in the grid */
int cells () const { return _cells; }
/*! Get the index of a cell from its coordinates. 2D version. */
int cell_index ( int i, int j ) const;
/*! Get the index of a cell from its coordinates. 3D version */
int cell_index ( int i, int j, int k ) const;
/*! Get the index of a cell from its coordinates. Multidimensional version */
int cell_index ( const SrArray<int>& coords ) const;
/*! Get the cell coordinates from its index. 2D version. */
void cell_coords ( int index, int& i, int& j ) const;
/*! Get the cell coordinates from its index. 3D version. */
void cell_coords ( int index, int& i, int& j, int& k ) const;
/*! Get the cell coordinates from its index. Multidimensional version. */
void cell_coords ( int index, SrArray<int>& coords ) const;
/*! Get the lower and upper coordinates of the Euclidian 2D box of cell (i,j). */
void cell_boundary ( int i, int j, SrPnt2& a, SrPnt2& b ) const;
/*! Get the lower and upper coordinates of the Euclidian 3D box of cell (i,j,k). */
void cell_boundary ( int i, int j, int k, SrPnt& a, SrPnt& b ) const;
/*! Get the indices of all cells intersecting with the given box.
If a box boundary is exactly at a grid separator, only the cell
with greater index is considered. Indices are just pushed to
array cells, which is not emptied before being used */
void get_intersection ( SrPnt2 a, SrPnt2 b, SrArray<int>& cells ) const;
/*! 3D version of get_intersection() */
void get_intersection ( SrPnt a, SrPnt b, SrArray<int>& cells ) const;
/*! Returns the index of the cell containing a, or -1 if a is outside the grid */
int get_point_location ( SrPnt2 a ) const;
/*! Returns the index of the cell containing a, or -1 if a is outside the grid */
int get_point_location ( SrPnt a ) const;
};
/*! \class SrGrid sr_grid.h
\brief n-D regular grid template class
SrGrid defines automatic type casts to the user type.
WARNING: SrGrid is designed to efficiently store large
amount of cells and it uses SrArray<X>, which implies
that no constructors or destructors of type X are called.
The user must initialize and delete data properly. */
template <class X>
class SrGrid : public SrGridBase
{ private :
SrArray<X> _data;
public :
SrGrid ( int dim=0, int ns=0 ) : SrGridBase ( dim, ns )
{ _data.size(SrGridBase::cells()); }
void init ( int dim, int ns )
{ SrGridBase::init ( dim, ns );
_data.size(SrGridBase::cells());
}
void init ( const SrArray<SrGridAxis>& axis_desc )
{ SrGridBase::init ( axis_desc );
_data.size(SrGridBase::cells());
}
/*! Returns the cell of given index, that should be in 0<=i<cells() */
X& operator[] ( int index ) { return _data[index]; }
/*! Returns the cell of given 2D coordinates */
X& operator() ( int i, int j )
{ return _data[SrGridBase::cell_index(i,j)]; }
/*! Returns the cell of given 3D coordinates */
X& operator() ( int i, int j, int k )
{ return _data[SrGridBase::cell_index(i,j,k)]; }
/*! Returns the cell of given n-D coordinates */
X& operator() ( const SrArray<int>& coords )
{ return _data[SrGridBase::cell_index(i,j)]; }
};
//============================== end of file ===============================
# endif // SR_LIST_H

View File

@ -0,0 +1,242 @@
#include "precompiled.h"
# include "sr_hash_table.h"
//================================ hash function =============================
static int hash ( const char* s, int size )
{
int h = 0;
while ( *s )
{ h = 31*h + SR_LOWER(*s);
s++;
}
h = SR_ABS(h);
h = h%size;
return h;
}
/*static int hash ( const char *string, int limit ) // worse
{
unsigned int i = 0;
unsigned int accum = 0;
while( string[ i ] != 0 ) {
accum = string[ i ]
^ ( accum << ( sizeof( char ) * 8 + 1 ) )
^ ( accum >> ( ( sizeof( accum ) - sizeof( char ) ) * 8 - 1 ) );
i++;
}
return (accum % limit);
}*/
//================================ SrHashTableBase ===============================
SrHashTableBase::SrHashTableBase ( int hsize )
{
init ( hsize );
}
SrHashTableBase::~SrHashTableBase ()
{
init ( 0 );
}
void SrHashTableBase::init ( int hsize )
{
if ( hsize==0 && _hash_size==0 ) return; // already initialized to 0
// destroys actual table:
while ( _table.size() )
{ if ( _table.top().dynamic ) sr_string_set ( _table.top().st, 0 ); // delete
_table.pop();
}
// builds new table:
_free.size ( 0 );
_free.capacity ( 0 );
_last_id = -1;
_elements = 0;
_hash_size = hsize;
_table.capacity ( hsize );
_table.size ( hsize );
int i;
for ( i=0; i<hsize; i++ )
_set_entry ( i, 0/*st*/, 0/*data*/, 0/*dynamic*/ );
}
void SrHashTableBase::rehash ( int new_hsize )
{
SrArray<Entry> a;
a.capacity ( _elements );
a.size ( 0 );
// 1. store current valid elements:
int i;
for ( i=0; i<_table.size(); i++ )
{ if ( _table[i].st )
{ a.push() = _table[i];
_table[i].st=0; // ensures st pointer will not be deleted
}
}
// 2. init table with new hash size:
init ( new_hsize );
// 3. put back original data in new table:
for ( i=0; i<a.size(); i++ )
{ _insert ( a[i].st, a[i].data, a[i].dynamic );
}
}
int SrHashTableBase::longest_entry () const
{
int i, j, len;
int longest=0;
for ( i=0; i<_hash_size; i++ )
{
if ( _table[i].st==0 ) continue;
len = 1;
j=_table[i].next;
while ( j>=0 )
{ len++;
j = _table[j].next;
}
if ( len>longest ) longest=len;
}
return longest;
}
int SrHashTableBase::lookup_index ( const char *st ) const
{
if ( !st ) return -1;
int id = ::hash ( st, _hash_size );
if ( _table[id].st==0 ) return -1; // empty entry, not found
while ( true )
{ if ( sr_compare(_table[id].st,st)==0 ) return id; // already there
// else check next colliding entry:
if ( _table[id].next<0 ) return -1; // no more entries, not found
id = _table[id].next;
}
}
void* SrHashTableBase::lookup ( const char* st ) const
{
int id = lookup_index ( st );
return id<0? 0: _table[id].data;
}
bool SrHashTableBase::_insert ( const char *st, void* data, char dynamic )
{
if ( !st ) { _last_id=-1; return false; }
if ( _hash_size<=0 ) init ( 256 ); // automatic initialization to avoid problems
int id = ::hash ( st, _hash_size );
if ( _table[id].st==0 ) // empty entry, just take it
{ _set_entry ( id, st, data, dynamic );
_elements++;
_last_id = id;
return true;
}
while ( true )
{ if ( sr_compare(_table[id].st,st)==0 ) // already there
{ _last_id = id;
return false;
}
// else check next colliding entry:
if ( _table[id].next<0 ) // no more entries, add one:
{ int newid;
if ( _free.size()>0 )
{ newid = _free.pop();
}
else
{ newid = _table.size();
_table.push();
}
_table[id].next = newid;
_set_entry ( newid, st, data, dynamic );
_elements++;
_last_id = newid;
return true;
}
id = _table[id].next;
}
}
void* SrHashTableBase::remove ( const char *st )
{
if ( !st ) { _last_id=-1; return false; }
int id = ::hash ( st, _hash_size );
if ( _table[id].st==0 ) return 0; // already empty entry
int priorid=id;
while ( true )
{ if ( sr_compare(_table[id].st,st)==0 ) // found: remove it
{
void* data = _table[id].data;
int next = _table[id].next;
_clear_entry ( id );
_elements--;
// fix links:
if ( priorid==id ) // removing first entry
{ if ( next>=0 )
{ _table[id] = _table[next];
_set_entry ( next, 0 /*st*/, 0 /*data*/, 0 /*dynamic*/ );
_free.push() = next;
}
else
{ } // nothing to do
}
else // removing entry in the "linked list"
{ _table[priorid].next = next;
_free.push() = id;
}
return data;
}
priorid = id;
id = _table[id].next;
}
}
void SrHashTableBase::_set_entry ( int id, const char *st, void* data, char dynamic )
{
if ( dynamic )
_table[id].st = sr_string_new ( st ); // can return null if st==0
else
_table[id].st = (char*)st;
_table[id].data = data;
_table[id].next = -1;
_table[id].dynamic = dynamic;
}
void SrHashTableBase::_clear_entry ( int id )
{
if ( _table[id].dynamic ) delete _table[id].st;
_table[id].st = 0;
_table[id].data = 0;
_table[id].next = -1;
_table[id].dynamic = 0;
}
//============================== end of file ===============================

View File

@ -0,0 +1,155 @@
/** \file sr_hash_table.h
* Hash table functions */
# ifndef SR_HASH_TABLE_H
# define SR_HASH_TABLE_H
# include "sr_array.h"
//================================ SrHashTableBase ===============================
/*! \class SrHashTableBase sr_hash_table.h
Stores user data associated with string keys in a hash table.
Note1: this is a rather specific implementation: colliding elements
are appended to the same array of entries, this is very efficient for inserting
elements but less practical for removing elements (removal was not implemented).
Note2: the user is responsible for allocation/deallocation of the appended user data,
which is merely stored as given void pointers, more info about that in the SrHashTable class. */
class SrHashTableBase
{ protected:
struct Entry { char* st; // the string id of this entry, or null if empty entry
void* data; // the user data associated or null if none
int next; // the index of the next colliding item
char dynamic; // 1 if st was created with new operator, 0 if st is static
};
SrArray<Entry> _table;
SrArray<int> _free;
int _hash_size;
int _elements;
int _last_id;
public:
/*! Default constructor creates a table of given hash size. If the hash size
is 0 (the default value), init() must be properly called aftwerwards. */
SrHashTableBase ( int hsize=0 );
/*! Destructor */
~SrHashTableBase ();
/*! Destroy actual table and builds a new empty one with the given hash size.
A value of zero will make the hash table empty and unusable. */
void init ( int hsize );
/*! Rebuilds the table with a different hash size */
void rehash ( int new_hsize );
/*! Returns the table size, which can be greater than the initial
given hash size in case there were collisions */
int size () const { return _table.size(); }
/*! Returns the initial (minimum) size of the table */
int hash_size () const { return _hash_size; }
/*! Returns the number of colliding elements in the table */
int collisions () const { return _table.size()-_free.size()-_hash_size; }
/*! Calculates and returns the maximum number of comparisons
that can happen when searching for a string in the table,
ie, the longest list size associated with a same entry in
the hash table. If there are no collisions, 1 is returned. */
int longest_entry () const;
/*! Total number of elements inserted in the table */
int elements () const { return _elements; }
/*! Returns the next index of an entry colliding with index id.
-1 is returned in case there is no further "colliding index" */
int collidingid ( int id ) const { return _table[id].next; }
/*! Returns the string key associated with the given id (can be null).
No validity checkings in the index are done! */
const char* key ( int id ) const { return _table[id].st; }
/*! Returns the user data associated with the given id (can be null).
No validity checkings in the index are done! */
void* data ( int id ) const { return _table[id].data; }
/*! Returns the valid index entry (>=0) relative to the given string key,
or -1 if the string key does not exist in the table (or if st==0)*/
int lookup_index ( const char *st ) const;
/*! Returns the user data associated with the given string key,
or null if the string was not found */
void* lookup ( const char* st ) const;
/*! Inserts a string key and user data to the table and returns true in case of success.
False is returned in case the string key already exists (or if st==0) meaning
that the entry was not added to the table.
If the string already exists, its index can be retrieved with lastid().
The given st pointer is copied in an internal allocated string.
(note: if not set, the hash size is automatically initialized with 256) */
bool insert ( const char *st, void* data ) { return _insert(st,data,1); }
/*! Same as the insert() method, however the given st pointer is considered to
be a pointer to a static string and is not internally allocated. */
bool insertstat ( const char *st, void* data ) { return _insert(st,data,0); }
/*! Returns the id involved during the last call to the insert methods:
it will be 1) the index of the new added entry, or 2) the index of the found
duplicated entry, or 3) -1 is the string key was null. */
int lastid () const { return _last_id; }
/*! Removes and returns the data associated with key st. Returns 0 if
the key was not found. */
void* remove ( const char *st );
private :
bool _insert ( const char *st, void* data, char dynamic );
void _set_entry ( int id, const char *st, void* data, char dynamic );
void _clear_entry ( int id );
};
//================================ SrHasTable ===============================
/*! \class SrHasTable sr_hash_table.h
Template version for usefull typecasts to a user type X.
For generality, there is no destructor and the user is responsible to
delete any attached pointers, using or not methods delete_data() and unref_data(). */
template <class X>
class SrHashTable : public SrHashTableBase
{ public:
/*! This constructor simply calls the constructor of the base class */
SrHashTable ( int hsize ) : SrHashTableBase(hsize) {}
/*! Simple type cast to the base class method */
X data ( int id ) const { return (X)SrHashTableBase::data(id); }
/*! Simple type cast to the base class method */
X lookup ( const char* st ) const { return (X)SrHashTableBase::lookup(st); }
/*! Will delete and set to zero all attached data in the hash table using
the delete operator after a type cast to (X*). */
void delete_data ()
{ int i;
for (i=0; i<_table.size(); i++)
{ delete (X*)_table[i].data; _table[i].data=0; } // delete 0 is ok
}
/*! Will call unref() and set to zero all attached data in the hash table. It assumes
that X derives SrSharedClass, and therefore X* is type-casted to SrSharedClass*. */
void unref_data ()
{ int i;
for (i=0; i<_table.size(); i++)
{ if (_table[i].data) { ((SrSharedClass*)_table[i].data)->unref(); _table[i].data=0; } }
}
/*! Simple type cast to the base class method */
X remove ( const char *st ) { return (X)SrHashTableBase::remove(st); }
};
//============================== end of file ===============================
# endif // SR_HASH_TABLE_H

119
source/dcdt/se/sr_heap.h Normal file
View File

@ -0,0 +1,119 @@
# ifndef SR_HEAP_H
# define SR_HEAP_H
/** \file sr_heap.h
* Template for a heap based on SrArray. */
# include "sr_array.h"
/*! \class SrHeap sr_heap.h
\brief Heap based on a SrArray
SrHeap implements a heap ordered binary tree based on a SrArray object.
Because of performance issues, SrHeap does not honor constructors or
destructors of its elements X, so user-managed pointers should be used
in case of more complex classes, in the same design line as SrArray.
The heap is ordered according to its associated cost. The remove()
method will always remove the minimum cost element in the heap.
class X is the element type, and Y is the cost type. Y will usually
be int, float or double. */
template <class X, class Y>
class SrHeap
{ private :
struct Elem { X e; Y c; };
SrArray<Elem> _heap;
public :
/*! Default constructor. */
SrHeap () {}
/*! Copy constructor. */
SrHeap ( const SrHeap& h ) : _heap(h._heap) {}
/*! Set the capacity of the internal array */
void capacity ( int c ) { _heap.capacity(c); }
/*! Returns true if the heap is empty, false otherwise. */
bool empty () const { return _heap.empty(); }
/*! Returns the number of elements in the queue. */
int size () const { return _heap.size(); }
/*! Initializes as an empty heap */
void init () { _heap.size(0); }
/*! Compress the internal heap array */
void compress () { _heap.compress(0); }
/*! Make the heap have the given size, by removing the worst elements.
Only applicable when s < size(). */
void size ( int s )
{ if ( s<=0 ) { init(); return; }
if ( s>=size() ) return;
SrArray<Elem> tmp(s,s);
while ( tmp.size()<s )
{ tmp.push() = _heap.top();
remove();
}
init();
while ( tmp.size()>0 )
{ insert ( tmp.top().e, tmp.top().c );
tmp.pop();
}
}
/*! Insert a new element with the given cost */
void insert ( const X& elem, Y cost )
{ // insert at the end:
_heap.push();
_heap.top().e = elem;
_heap.top().c = cost;
// swim up: (parent of elem k is k/2)
Elem tmp;
int k=_heap.size();
while ( k>1 && _heap[k/2-1].c>_heap[k-1].c )
{ SR_SWAP ( _heap[k/2-1], _heap[k-1] );
k = k/2;
}
}
/*! Removes the element in the top of the heap, which is always
the element with lowest cost. */
void remove ()
{ // put last element in top:
_heap[0] = _heap.pop();
// sink down: (children of node k are 2k and 2k+1)
int j;
int k=1;
int n=_heap.size();
Elem tmp;
while ( 2*k<=n )
{ j=2*k;
if ( j<n && _heap[j-1].c>_heap[j].c ) j++;
if ( !(_heap[k-1].c>_heap[j-1].c) ) break;
SR_SWAP ( _heap[k-1], _heap[j-1] );
k=j;
}
}
/*! Get a reference to the top element of the the heap,
which is always the element with lowest cost. */
const X& top () const { return _heap[0].e; }
/*! Get the lowest cost in the heap,
which is always the cost of the top element. */
Y lowest_cost () const { return _heap[0].c; }
/*! Returns elem i (0<=i<size) for inspection */
const X& elem ( int i ) const { return _heap[i].e; }
/*! Returns the cost of elem i (0<=i<size) for inspection */
Y cost ( int i ) const { return _heap[i].c; }
};
//============================== end of file ===============================
#endif // SR_HEAP_H

116
source/dcdt/se/sr_image.cpp Normal file
View File

@ -0,0 +1,116 @@
#include "precompiled.h"
# include <string.h>
# include "sr_image.h"
//=========================== SrImage ============================
SrImage::SrImage ()
{
_w = _h = _tw = 0;
_data = 0;
}
SrImage::~SrImage ()
{
delete _data;
}
void SrImage::alloc ( int w, int h )
{
int newsize = w*h*3;
if ( newsize!=_tw*_h )
{
delete _data;
if ( w<=0 || h<=0 ) w=h=0;
if ( w )
_data = new srbyte [ newsize ]; // to hold rgb values
else
_data = 0;
}
_tw = 3*w;
_w = w;
_h = h;
}
void SrImage::vertical_mirror ()
{
int i, ie, mid;
mid = _h/2;
srbyte* buf = new srbyte [ _tw ];
for ( i=0,ie=_h-1; i<mid; i++,ie-- )
{ memcpy ( buf, line(i), _tw );
memcpy ( line(i), line(ie), _tw );
memcpy ( line(ie), buf, _tw );
}
delete buf;
}
# define PutInt(i) fwrite(&i,4/*bytes*/,1/*num items*/,f)
# define PutShort(s) fwrite(&s,2/*bytes*/,1/*num items*/,f)
bool SrImage::save_as_bmp ( const char* filename )
{
FILE* f = fopen ( filename, "wb" );
if ( !f ) return false;
int i = 0;
int offset = 14+40;
//int dw = 4-(_w%4); if ( dw==4 ) dw=0;
int dw = (_w%4);
int filesize = 14 /*header*/ + 40 /*info*/ + (_w*_h*3) +(_h*dw);
// 14 bytes of header:
fprintf ( f, "BM" ); // 2 bytes : signature
PutInt ( filesize ); // file size
PutInt ( i ); // reserved (zeros)
PutInt ( offset ); // offset to the data
// 40 bytes of info header:
int infosize = 40;
short planes = 1;
short bits = 24;
int compression = 0; // no compression
int compsize = 0; // no compression
int hres = 600;
int vres = 600;
int colors = 0;
int impcolors = 0; // important colors: all
PutInt ( infosize ); // size of info header
PutInt ( _w ); // width
PutInt ( _h ); // height
PutShort ( planes );
PutShort ( bits );
PutInt ( compression );
PutInt ( compsize );
PutInt ( hres );
PutInt ( vres );
PutInt ( colors );
PutInt ( impcolors );
int w, h;
srbyte* scanline;
for ( h=_h-1; h>=0; h-- )
{ scanline = line(h);
for ( w=0; w<_tw; w+=3 )
{ fputc ( scanline[w+2], f ); // B
fputc ( scanline[w+1], f ); // G
fputc ( scanline[w], f ); // R
}
for ( w=0; w<dw; w++ ) fputc ( 0, f );
}
fclose ( f );
return true;
}
//============================= end of file ==========================

67
source/dcdt/se/sr_image.h Normal file
View File

@ -0,0 +1,67 @@
# ifndef SR_IMAGE_H
# define SR_IMAGE_H
/** \file sr_image.h
* A 24 bit image.*/
# include "sr_color.h"
//=========================== SrImage ============================
/*! \class SrImage sr_image.h
\brief Non compressed 24 bit image
SrImage stores pixel data as a sequence of rgb data. */
class SrImage
{ private :
int _w, _h; // total data size is 3*_w*_h
int _tw; // _tw = 3*_w
srbyte* _data; // array of rgb values
public :
/*! Constructs an empty image */
SrImage ();
/*! Destructor */
~SrImage ();
/*! Alloc the desired size in pixels of the image.
A total of w*h*3 elements are allocated.
Invalid dimensions deletes the image data */
void alloc ( int w, int h );
/*! Changes the image by appying a vertical mirroring */
void vertical_mirror ();
/*! Returns the width in pixels of the image */
int w () const { return _w; }
/*! Returns the height in pixels of the image */
int h () const { return _h; }
/*! Returns a pointer to the base image data */
const srbyte* data () { return _data; }
/*! Returns a pointer to the pixel color (3 bytes) at position (l,c) */
srbyte* data ( int l, int c ) { return _data+(l*_tw)+(c*3); }
/*! Returns a reference to the red component of the pixel at position (l,c) */
srbyte& r ( int l, int c ) { return data(l,c)[0]; }
/*! Returns a reference to the green component of the pixel at position (l,c) */
srbyte& g ( int l, int c ) { return data(l,c)[1]; }
/*! Returns a reference to the blue component of the pixel at position (l,c) */
srbyte& b ( int l, int c ) { return data(l,c)[2]; }
/*! Returns the base pointer of the line l of the image */
srbyte* line ( int l ) { return _data+(l*_tw); }
/*! Saves the image in a bmp file. Returns true if could write file and false otherwise. */
bool save_as_bmp ( const char* filename );
};
//============================= end of file ==========================
# endif // SR_IMAGE_H

606
source/dcdt/se/sr_input.cpp Normal file
View File

@ -0,0 +1,606 @@
#include "precompiled.h"
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
# include "sr_input.h"
# include "sr_string.h"
# include "sr_array.h"
//# define SR_USE_TRACE1 //Parser
//# define SR_USE_TRACE2 //Init
# include "sr_trace.h"
# define ISNULL _type==(srbyte)TypeNull
# define ISFILE _type==(srbyte)TypeFile
# define ISSTRING _type==(srbyte)TypeString
//=============================== SrInput =================================
struct SrInput::UngetData
{ struct Token { char* string; srbyte type; };
SrArray<int> character; // unget buffer for char reading
SrArray<Token> token; // unget buffer for token reading
void init ();
};
void SrInput::UngetData::init ()
{
character.size(0);
while ( token.size() ) delete token.pop().string;
}
void SrInput::_init ( char c )
{
_size = 0;
_curline = 0;
_comment_style = c;
_type = (srbyte) TypeNull;
_last_error = (srbyte) NoError;
_last_token_type = 0;
_max_token_size = 256;
_lowercase_tokens = 1; // true
_skipped_spaces = 0;
_unget = new UngetData;
_filename = 0;
}
SrInput::SrInput ( char com )
{
SR_TRACE2 ("Default Constructor");
_init ( com );
}
SrInput::SrInput ( const char *buff, char com )
{
SR_TRACE2 ("String Constructor");
_init ( com );
if ( buff )
{ _cur.s = buff;
_ini.s = buff;
_size = strlen ( buff );
_curline = 1;
_type = (srbyte) TypeString;
}
}
// static utility function:
static void get_size ( FILE *fp, int &size, int &start )
{
start = (int)ftell(fp);
fseek ( fp, 0, SEEK_END );
size = (int)ftell(fp) - start;
fseek ( fp, start, SEEK_SET );
}
SrInput::SrInput ( FILE *file, char com )
{
SR_TRACE2 ("File Constructor");
_init ( com );
if ( file )
{ _cur.f = file;
_curline = 1;
_type = (srbyte) TypeFile;
get_size ( file, _size, _ini.f );
}
}
SrInput::SrInput ( const char* filename, const char* mode, char com )
{
SR_TRACE2 ("File2 Constructor");
_init ( com );
FILE* file = fopen ( filename, mode );
sr_string_set ( _filename, filename );
if ( file )
{ _cur.f = file;
_curline = 1;
_type = (srbyte) TypeFile;
get_size ( file, _size, _ini.f );
}
}
SrInput::~SrInput ()
{
close (); // close frees all data inside _unget, and frees _filename
delete _unget;
}
void SrInput::init ( const char *buff )
{
SR_TRACE2 ("Init with string");
close ();
if ( buff )
{ _cur.s = buff;
_ini.s = buff;
_size = strlen ( buff );
_curline = 1;
_type = (srbyte) TypeString;
}
}
void SrInput::init ( FILE *file )
{
SR_TRACE2 ("Init with file");
close ();
if ( file )
{ _cur.f = file;
_curline = 1;
_type = (srbyte) TypeFile;
get_size ( file, _size, _ini.f );
}
}
void SrInput::init ( const char* filename, const char* mode )
{
SR_TRACE2 ("Init with file2");
FILE* file = fopen ( filename, mode );
init ( file );
sr_string_set ( _filename, filename );
}
void SrInput::close ()
{
if ( ISFILE ) fclose ( _cur.f );
_size = 0;
_curline = 0;
_type = (srbyte) TypeNull;
_last_error = (srbyte) NoError;
_last_token = "";
_last_token_type = 0;
_unget->init ();
sr_string_set ( _filename, 0 );
}
void SrInput::leave_file ()
{
_type = (srbyte) TypeNull;
close ();
}
FILE* SrInput::filept ()
{
return _type==TypeFile? _cur.f:0;
}
bool SrInput::valid () const
{
return (ISNULL)? false : true;
}
bool SrInput::finished ()
{
if ( _unget->character.size()>0 || _unget->token.size()>0 ) return false;
else if ( ISFILE ) return pos()>=_size? true:false;
else if ( ISSTRING ) return *(_cur.s)? false:true;
else return true;
}
void SrInput::getall ( SrString& buf )
{
if ( ISFILE )
{ int s = size()-pos();
buf.capacity ( s+2 ); // need +2 to cope with pc text files
fread ( (void*)(const char*)buf, sizeof(char), (size_t)s, _cur.f );
buf [ s+1 ] = 0;
}
else if ( ISSTRING )
{
buf.set ( _cur.s );
_cur.s = _ini.s+_size;
}
}
int SrInput::getline ( SrString& buf )
{
int c;
buf.len(0);
do { c = get();
buf << (char)c;
} while ( c!='\n' && c!=EOF );
return c;
}
int SrInput::getchar ()
{
int c = EOF;
if ( _unget->character.size()>0 )
{ c = _unget->character.pop();
}
else
{ if ( ISFILE ) c=fgetc(_cur.f);
else if ( ISSTRING ) c = *_cur.s? *_cur.s++:EOF;
}
if ( c=='\n' ) _curline++;
return c;
}
static void skip_c_comment ( SrInput *p )
{
int c, d;
while ( true )
{ c = p->getchar();
if ( c=='/' ) // for nested comments
{ d = p->getchar();
if ( d=='*' ) skip_c_comment(p); else p->unget(d);
}
else if ( c=='*' )
{ d = p->getchar();
if ( d=='/' ) return; else p->unget(d);
}
else if ( c<0 ) return; // EOF found in the middle of a comment will not cause an error.
}
}
int SrInput::get ()
{
int c = getchar();
if ( _comment_style==0 )
{ return c;
}
else if ( _comment_style=='C' && c=='/' )
{ int d = getchar();
if ( d=='*' )
{ skip_c_comment(this);
return get();
}
else if ( d=='/' )
{ skip_line ();
return get();
}
else unget(d);
}
else if ( c==_comment_style )
{ skip_line ();
return get();
}
return c;
}
void SrInput::unget ( char c )
{
if ( c=='\n' && _curline>0 ) _curline--;
_unget->character.push() = c;
}
void SrInput::advance ( int n )
{
while ( n-->0 ) if ( getchar()<0 ) break;
}
void SrInput::rewind ()
{
if ( ISNULL ) return;
_unget->init();
_curline = 1;
if ( ISSTRING ) _cur.s = _ini.s;
else fseek ( _cur.f, _ini.f, SEEK_SET );
}
int SrInput::pos ()
{
if ( ISFILE ) return ((int)ftell(_cur.f)) - _ini.f;
else if ( ISSTRING ) return ((sruint)_cur.s) - ((sruint)_ini.s);
else return 0;
}
void SrInput::pos ( int pos )
{
_unget->init();
if ( ISFILE ) fseek ( _cur.f, pos+_ini.f, SEEK_SET );
else if ( ISSTRING ) _cur.s = _ini.s+pos;
}
void SrInput::set_pos_and_update_cur_line ( int pos )
{
_unget->init();
rewind ();
advance ( pos );
}
void SrInput::skip_line ()
{
int c;
do { c = getchar();
} while ( c!=EOF && c!='\n' );
}
void SrInput::unget_token ( const char *token, SrInput::TokenType type )
{
UngetData::Token& t = _unget->token.push();
t.string = 0;
t.type = (srbyte) type;
sr_string_set ( t.string, token );
}
void SrInput::unget_token ()
{
if ( _last_token_type!=EndOfFile )
unget_token ( _last_token, (TokenType)_last_token_type );
}
bool SrInput::has_unget_data () const
{
return _unget->token.size()==0 && _unget->character.size()==0? false:true;
}
static char get_escape_char ( char c )
{
switch ( c )
{ case 'n' : return '\n';
case 't' : return '\t';
case '\n': return 0; // Just skip line
default : return c;
}
}
SrInput::TokenType SrInput::get_token ( SrString &buf )
{
# define UNGET(c) if(c>0)unget(c)
int i;
int c = ' ';
int size = _max_token_size;
TokenType ret;
_last_error = (srbyte) NoError;
buf.capacity(size);
if ( _unget->token.size()>0 )
{ UngetData::Token& t = _unget->token.pop();
buf.set ( t.string );
delete t.string;
return (TokenType) t.type;
}
buf[0] = buf[1] = 0;
_skipped_spaces = 0;
while ( c && isspace(c) ) // skip initial spaces;
{ c=get(); _skipped_spaces++; }
if ( c==EOF )
{ SR_TRACE1 ( "Got the End Of File!" );
ret = EndOfFile;
}
else if ( strchr(SR_INPUT_DELIMITERS,c) && c!='.' ) // '.' will be detected after checking a real
{ buf[0]=c; ret=Delimiter;
SR_TRACE1 ( "Got a Delimiter: "<<buf );
}
else if ( c=='"' )
{ SR_TRACE1 ( "Quote found..." );
i = 0;
while ( true )
{ c = getchar(); // Comments inside a string are not considered
if ( !c || c=='"' ) break;
if ( c=='\\' ) c = get_escape_char ( getchar() );
if ( c ) buf[i++]=c;
if ( i+1==size ) break;
}
buf[i]=0;
if ( i+1==size ) { SR_TRACE1("Got an Error TooBig!"); ret=Error; _last_error=TooBig; }
else if ( c ) { SR_TRACE1("Got a String: ["<<buf<<"] size="<<strlen(buf)); ret=String; }
else { SR_TRACE1("Got an Error OpenString!"); ret=Error; _last_error=OpenString; }
}
else if ( c=='.' || isdigit(c) )
{ SR_TRACE1 ( "Digit found..." );
bool pnt=false, exp=false;
i = 0;
while ( true )
{ if ( c=='e' ) c='E';
if ( !pnt && c=='.' ) pnt=true;
else if ( pnt && c=='.' ) break;
else if ( !exp && c=='E' ) exp=pnt=true;
else if ( (c=='+'||c=='-') && buf[i-1]=='E' );
else if ( !isdigit(c) ) break;
buf[i++]=c;
if ( i+1==size ) break;
c = getchar();
}
buf[i]=0;
if ( buf[0]=='.' && i==1 && strchr(SR_INPUT_DELIMITERS,'.') )
{ ret=Delimiter; UNGET(c); SR_TRACE1("Got a Delimiter: ["<<buf<<"]"); }
else if ( i+1==size ) { ret=Error; _last_error=TooBig; SR_TRACE1("Got an Error TooBig!"); }
else if ( pnt && c=='.' ) { ret=Error; _last_error=InvalidPoint; SR_TRACE1("Got an Error InvalidPoint!"); }
else if ( pnt || exp ) { ret=Real; UNGET(c); SR_TRACE1("Got a Real: ["<<buf<<"] => "<<atof(buf)); }
else { ret=Integer; UNGET(c); SR_TRACE1("Got an Integer: ["<<buf<<"] => "<<atoi(buf)); }
}
else if ( c=='_' || isalpha(c) )
{ SR_TRACE1 ( "Alpha found..." );
i = 0;
while ( true )
{ if ( !isalnum(c) && c!='_' ) break;
buf[i++]=c;
if ( i+1==size ) break;
c = getchar();
}
buf[i]=0;
if ( i+1==size ) { SR_TRACE1("Got an Error TooBig!"); ret=Error; _last_error=TooBig; }
else { SR_TRACE1("Got a Name: ["<<buf<<']'); ret=Name; UNGET(c); }
if ( _lowercase_tokens ) buf.lower();
}
else
{ SR_TRACE1("Got an Error Undef, code: "<<(int)c<<" ["<<(char)c<<']');
buf[0]=c;
ret = Error;
_last_error = UndefChar;
}
return ret;
# undef UNGET
}
SrInput::TokenType SrInput::get_token ()
{
_last_token_type = get_token ( _last_token );
return (TokenType)_last_token_type;
}
char* SrInput::error_desc ( SrInput::ErrorType t )
{
switch ( t )
{ case UndefChar : return "Undefined character found";
case UnexpectedToken: return "Parsed token is of an unexpected type";
case TooBig : return "Name too big";
case OpenString : return "EOF found before end of a string";
case InvalidPoint : return "Misplaced decimal point";
default : return 0;
}
}
SrString& SrInput::gets ()
{
get_token();
if ( _last_token_type!=String && _last_token_type!=Name )
{ _last_error=UnexpectedToken; _last_token=""; }
return _last_token;
}
SrString& SrInput::gets ( SrString& buf )
{
TokenType t = get_token ( buf );
if ( t!=String && t!=Name ) _last_error=UnexpectedToken;
return buf;
}
SrString& SrInput::getn ()
{
int signal=1;
get_token();
// accumulate +- delimiter if any:
while ( _last_token_type==Delimiter )
{ if ( _last_token[0]=='-' )
signal*=-1;
else if ( _last_token[1]!='+' )
break; // will then return ""
get_token();
}
if ( _last_token_type!=Integer && _last_token_type!=Real )
{ _last_error=UnexpectedToken; _last_token=""; return _last_token; }
if ( signal==-1 ) _last_token.insert(0,"-");
return _last_token;
}
char SrInput::getd ()
{
get_token();
if ( _last_token_type!=Delimiter )
{ _last_error = UnexpectedToken;
return 0;
}
return _last_token[0];
}
bool SrInput::skip ( int n )
{
while ( n-- )
{ get_token();
if ( _last_token_type==Error || _last_token_type==EndOfFile ) return false;
}
return true;
}
bool SrInput::skipto ( const char *name )
{
while ( true )
{ get_token();
if ( _last_token_type==Error || _last_token_type==EndOfFile ) return false;
if ( _last_token_type==Name )
{ if ( _last_token==name ) return true; }
}
}
bool SrInput::has_field ()
{
if ( getd()!='<' ) { unget_token(); return false; }
if ( gets()=="" ) { unget_token(); unget_token(); return false; }
char d = getd();
unget_token();
unget_token();
unget_token();
return d=='>'? true:false;
}
bool SrInput::read_field ( SrString& name )
{
name = "";
if ( getd()!='<' ) return false;
name = gets();
if ( name=="" ) return false;
if ( getd()!='>' ) return false;
return true;
}
bool SrInput::close_field ( const SrString& name )
{
if ( getd()!='<' ) return false;
if ( getd()!='/' ) return false;
if ( gets()!=name ) return false;
if ( getd()!='>' ) return false;
return true;
}
bool SrInput::skip_field ( const SrString& name )
{
while ( true )
{ if ( close_field(name) ) return true;
if ( _last_token_type==EndOfFile ) return false;
}
}
//================================= operators ==================================
SrInput& operator>> ( SrInput& in, int& i )
{
SrString& s = in.getn();
i = s.atoi();
return in;
}
SrInput& operator>> ( SrInput& in, sruint& i )
{
SrString& s = in.getn();
i = (sruint)s.atoi();
return in;
}
SrInput& operator>> ( SrInput& in, srbyte& c )
{
SrString& s = in.getn();
c = (srbyte)s.atoi();
return in;
}
SrInput& operator>> ( SrInput& in, float& f )
{
SrString& s = in.getn();
f = s.atof();
return in;
}
SrInput& operator>> ( SrInput& in, double& d )
{
SrString& s = in.getn();
d = s.atod();
return in;
}
SrInput& operator>> ( SrInput& in, char* st )
{
SrString& s = in.gets();
strcpy ( st, s );
return in;
}
//============================ End of File ==============================

369
source/dcdt/se/sr_input.h Normal file
View File

@ -0,0 +1,369 @@
# ifndef SR_INPUT_H
# define SR_INPUT_H
/** \file sr_input.h
* parses input file or string */
// sr_array.h cannot be included here because of circular references
# include <stdio.h>
# include "sr_string.h"
/*! Defines which characters are considered as delimiters. */
# define SR_INPUT_DELIMITERS "{}[]();,.=+-/^*%:&|!~<>#?@\\\'"
/*! \class SrInput sr_input.h
\brief Parses a file or string buffer
SrInput reads data from a string buffer or from an open file. It can
be used to read data byte per byte, or by parsing tokens that are
recognized as names, delimiters, integers, reals or strings.
Comments can be specified to be skiped and error messages are returned. */
class SrInput
{ public :
/*! Indicates the type of the current input. It can be TypeFile, TypeString,
or TypeNull. TypeNull indicates that the SrInput is not valid, and need
to be connected to a file or string to become valid (by calling init()). */
enum Type { TypeFile, //!< Input from a file
TypeString, //!< Input from a string buffer
TypeNull //!< Input not initialized, valid() will return false
};
/*! Indicates the type of the token returned in get_token() */
enum TokenType { Name, //!< A sequence of chars without spaces and delimiters
Delimiter, //!< Any delimiter defined in macro SR_INPUT_DELIMITERS
Integer, //!< An int type number
Real, //!< A real type number
String, //!< A seq inside "". Returns content converting c-like escape chars.
EndOfFile, //!< End of file encountered
Error //!< There was an error when parsing the last token.
};
/*! Indicates the error occured in the last get operation. See the comments in each
function to know the types of error that can be generated. */
enum ErrorType { NoError, //!< No error occured in the last operation
UndefChar, //!< Unrecognized char was found
UnexpectedToken, //!< Parsed token is of an unexpected type
TooBig, //!< token is bigger than the given buffer
OpenString, //!< EOF found before end of a string
InvalidPoint //!< Misplaced decimal point found in a real number
};
private :
struct UngetData;
union { FILE *f; const char *s; } _cur; // the current input position
union { int f; const char *s; } _ini; // the beginning of the buffer
int _size; // the size of the input in bytes
int _curline; // keeps track of the current line
char _comment_style; // 0:none, 'C':C/C++ like, otherwise skip line
srbyte _type; // the enumerator Type
srbyte _last_error; // last error, if any.
SrString _last_token; // buffer with the last token read
srbyte _last_token_type; // buffer with the last token type read
int _max_token_size; // max size allowed for parsed tokens
char _lowercase_tokens; // if tokens should be converted to lowercase
int _skipped_spaces; // number of spaces skipped during last get_token
UngetData* _unget; // unget char and token information
char* _filename; // optional file name of the open file
void _init ( char c );
public :
/*! Construct an input of type SrInput::TypeNull.
SrInput will only be operational when linked to a file or string by calling
an init() function later. While SrInput is of type Null, the valid() method will
return false. The parameter com is the comment style for this input and is 0 by
default. See comment_style() for a description of possible styles. */
SrInput ( char com=0 ); // init as a null input
/*! Construct an input of type SrInput::TypeString.
In this way SrInput will read bytes from the given null-terminated string buffer.
If buff is null, SrInput will be initialized as TypeNull, and not as a String type.
Com is the comment style for this input and is 0 by default.
See comment_style() for a description of possible styles. */
SrInput ( const char *buff, char com=0 ); // init as a string input
/*! Construct an input of type SrInput::File
In this way, SrInput will read bytes from the given C-style file stream. If the file pointer
is null, SrInput will be initialized as TypeNull, and not as a File type. The current
position pointed by file is considered to be the start, and so the size is measured
since this given position. The parameter com is the comment style for this input and
is 0 by default. See comment_style() for a description of possible styles */
SrInput ( FILE *file, char com=0 ); // init as a file input
/*! Construct an input of type SrInput::File, from a given filename.
This is similar to the previous constructor, but it receives the fopen() parameters
directly (file name and mode strings) and stores internally the given file name. */
SrInput ( const char* filename, const char* mode, char com=0 ); // init as a file input
/*! Closes the associated file if it is a TypeFile input.
If it is required to leave the associated input file open, call leave_file() before. */
~SrInput ();
/*! Closes actual input, and init it as TypeString.
If buff is null, SrInput will be initialized as TypeNull, and not as a String type.
The comment style is not changed, but unget data are freed. */
void init ( const char *buff );
/*! Closes actual input, and init it as a File type.
If file is null, SrInput will be initialized as TypeNull, and not as a File type.
The comment style is not changed, but unget data are freed. */
void init ( FILE *file );
/*! Closes actual input, and init it as a File type, opening given file name in given mode.
If file could not be open, SrInput is initialized as TypeNull, and not as a File type.
Filename is stored, the comment style is not changed, but unget data are freed. */
void init ( const char* filename, const char* mode );
/*! Closes actual input and set it as TypeNull.
If SrInput is of type file, the associated file is closed. In all cases,
unget data is freed, filename is set to null, the last error is cleaned,
and size and curline are set to zero. */
void close ();
/*! Puts SrInput into TypeNull mode but without closing the current file.
If SrInput is not of File type, the effect is the same as close(). */
void leave_file ();
/*! If the input is done from a file, return the FILE pointer associated, otherwise
will return 0 */
FILE* filept ();
/*! Returns the file name used for opening a file input, or null if not available */
const char* filename () const { return _filename; }
/*! Associates with the input a file name. The string is stored but not used by SrInput. */
void filename ( const char* s ) { sr_string_set(_filename,s); }
/*! Returns true if the input is not of TypeNull type. */
bool valid () const;
/*! Returns the type of the SrInput. */
Type type () const { return (Type) _type; }
/*! Returns the size of the current input. Size is calculated as the number in bytes from
the beginning of the input to the end of the input. The beginning is considered to be
the pointer passed to the constructor or init() method, when it is calculated. */
int size () const { return _size; }
/*! Returns the current line of the input. When SrInput is created or initialized, the
current line is set to 1. Afterwards, the current line is updated internally each
time a newline char is read. Note that when pos(int) method is used, the current
line is no more valid. */
int curline () const { return _curline; }
/*! Defines the comment style used for all "get methods", with exception to get_byte().
The style can be set to 0 if no comments are allowed, can be set to 'C' to have
standard C and C++ nested comments parsed, or otherwise when the specified
character is encountered the whole line is skiped. */
void comment_style ( char style ) { _comment_style=style; } // style can be 0,'C','anychar'
/*! Returns the current comment style being used. */
char comment_style () const { return _comment_style; }
/*! Returns true if the current input is pointing after the end of the file. If the
input source is empty but there is data to unget, the input is not considered finished.*/
bool finished ();
/*! Gets all bytes of the input, starting from the current position, and put them
in the given buffer. The input will point to its end. If input is of TypeNull,
nothing is done. Note: the input is not parsed, just copied, therefore comments
are also copied. */
void getall ( SrString& buf );
/*! Gets all bytes of the current line, i.e. untill a '\n' is read,
and put them in the given buffer (inlcuding the '\n').
The input will point to the first byte after the new line character.
The last character read is returned and will be either '\n' or EOF.
Uses method get(), and therefore comments are parsed. */
int getline ( SrString& buf );
/*! Get current character and advances pointer, will return -1 if end was reached.
Will read next char from the associated string or file. If SrInput is of
TypeNull, -1 is always returned. Comments are not considered, but unget
chars are correctly handled. */
int getchar (); // comments not handled
/*! Get current byte and advance, will return 0 if finish is reached.
This is for ascii inputs where comments are skipped according to
the current style. */
int get (); // comments handled
/*! Puts a byte in the unget stack. */
void unget ( char c );
/*! Reads the next n bytes of the current input.
Note that unget data are considered, but comments are not. */
void advance ( int n=1 );
/*! puts the pointer in the beginning of the input.
Any unget data is cleared. Nothing is done if SrInput is of type Null. */
void rewind ();
/*! Offset in bytes from the begining of the input. */
int pos ();
/*! Puts the input pointer to the position pos, clearing all unget data.
After calling it, curline() becomes invalid. Alternatively, method
set_pos_and_update_cur_line() can be used */
void pos ( int pos );
/*! Puts the input pointer to the position pos, clears all unget data and
determines the line number using rewind() and advance(pos). */
void set_pos_and_update_cur_line ( int pos );
/*! Reads all the current line (with getchar), stopping one byte after the
first newline encountered. */
void skip_line ();
/*! Sets the maximum allowed size for parsed tokens. Default is 256. */
void max_token_size ( int s ) { _max_token_size=s; }
/*! Returns the current maximum allowed size for parsed tokens. */
int max_token_size () const { return _max_token_size; }
/*! Determines if parsed tokens are transformed to lowercase or not. Default is true. */
void lowercase_tokens ( bool b ) { _lowercase_tokens = b; }
/*! Retrieves the state if parsed tokens are transformed to lowercase or not. */
bool lowercase_tokens () const { return _lowercase_tokens? true:false; }
/*! Returns the number of leading spaces skipped during the
last call to get_token() */
int skipped_spaces () const { return _skipped_spaces; }
/*! Affects the next call to all methods using get_token().
The token is copied and stored within SrInput, leaving the user buffer untouched. */
void unget_token ( const char *token, TokenType type );
/*! Affects the next call to all methods using get_token().
The last token parsed is ungetted, if its type is not EndOfFile */
void unget_token ();
/*! Returns true if any data (token or byte) was put in the unget stacks */
bool has_unget_data () const;
/*! Puts a token in buf, returning its type, skipping comments, and considering
unget() and unget_token() data. When EOF is reached, buf[0] is set to 0.
Names are converted to lower case according to the state set by lowercase_tokens().
The maximum length permitted for buf is determined by max_token_size().
When SrInput::Error is returned, last_error() will return the description of
the error occured.
Note: get_token() will parse an input like "-3" as having two tokens, a
delimiter and an integer (use getn() for reading numbers). */
TokenType get_token ( SrString& buf );
/* Does the same as get_token(SrString&), but the parsed token and token type
are kept in an internal buffer, to be later retrieved by last_token() and
last_token_type() */
TokenType get_token ();
/* Returns a reference to the internal buffer containing the last token parsed
with get_token(void). */
SrString& last_token() { return _last_token; }
/* Returns the type of the last token parsed with get_token(void). */
TokenType last_token_type() const { return (TokenType)_last_token_type; }
/*! Returns the last error occured, if any. Errors are generated by methods
using get_token() methods. */
ErrorType last_error () const { return (ErrorType)_last_error; }
/*! Returns true if there an error occured, and false otherwise. */
bool had_error () const { return ((ErrorType)_last_error)==NoError? false:true; }
/*! Sets the last error to NoError. */
void reset_error () { _last_error=(srbyte)NoError; }
/*! This static method will return a describing error message if t is one
of the following: UndefChar, UnexpectedToken, TooBig, OpenString, or InvalidPoint.
Otherwise it will return 0 : */
static char* error_desc ( ErrorType t );
/*! Gets the next token of type string or name using get_token(void),
and returns the token, which is kept in the internal last_token() string.
If the returned string has length 0 (ie equal to ""), it means that
the parsed token is not a string, neithert a name, or EOF was reached.
In any of these cases, last_error() will return UnexpectedToken. */
SrString& gets ();
/*! Same as gets(void), but without using the internal buffer of last_token(),
the given buffer buf is used instead. A reference to buf is retorned. */
SrString& gets ( SrString& buf );
/*! Gets the next token, considering it is a number (type int or real)
preceeded or not by delimiters + or -, and returns the token, which
is kept in the internal last_token() string.
The type in last_token_type() will tell if it was an integer or real.
If the returned string has length 0 (ie equal to ""), it means that
the parsed token is not a number or EOF was reached.
In any of these cases, last_error() will return UnexpectedToken. */
SrString& getn ();
/*! Reads the next token using get_token(), and tests if it is a delimiter.
Returns the delimiter char if a delimiter was found, otherwise returns 0.
When 0 is returned, last_error() will be set to UnexpectedToken. */
char getd ();
/*! Skips next n tokens, by calling get_token(). n has a default value of 1.
False is returned if any error occurred or EOF is encountered.
Otherwise true is returned. */
bool skip ( int n=1 );
/*! Reads tokens using get_token() until the given name is read as type Name.
skipto() uses the sr_compare() function that is case-insensitive.
Returns false if EOF was reached before, otherwise returns true. */
bool skipto ( const char *name );
/*! Returns true if there is a field like <nnn> in the current position,
or otherwise false is returned. All parsed tokens are ungetted, so that
the effect is as the current input position is not changed. */
bool has_field ();
/*! Parse the input expecting to encounter a field like <name>, and puts
field_name in the string name. If unexpected tokens are read, an error occurs
and false is returned. If EOF is encountered, false is returned. */
bool read_field ( SrString& name );
/*! Checks if the following tokens in input is </name>, returning true or false. */
bool close_field ( const SrString& name );
/*! Reads the input until reading </name>. If the field is not found, false is
returned. Otherwise true is returned and the input will point after </name>. */
bool skip_field ( const SrString& name );
/*! Operator to read an integer using method getn() and function atoi().
Errors can be tracked using last_error(). */
friend SrInput& operator>> ( SrInput& in, int& i );
/*! Operator to read an unsigned integer using method getn() and function atoi().
Errors can be tracked using last_error(). */
friend SrInput& operator>> ( SrInput& in, sruint& i );
/*! Operator to read an unsigned char using method getn() and function atoi().
Errors can be tracked using last_error(). */
friend SrInput& operator>> ( SrInput& in, srbyte& c );
/*! Operator to read a float using method getn() and function atof().
Errors can be tracked using last_error(). */
friend SrInput& operator>> ( SrInput& in, float& f );
/*! Operator to read a double using method getn() and function atof().
Errors can be tracked using last_error(). */
friend SrInput& operator>> ( SrInput& in, double& d );
/*! Operator to read a string using method gets(). st must point to a buffer
with enought space to receive the input. The input is read using the
internal buffer, and the result is copied to st using simply strcpy().
Errors can be tracked using last_error(). */
friend SrInput& operator>> ( SrInput& in, char* st );
/*! Operator to read a SrString using method gets(SrString&).
Errors can be tracked using last_error(). Implemented inline. */
friend SrInput& operator>> ( SrInput& in, SrString& s ) { in.gets(s); return in; }
};
//============================== end of file ===============================
# endif // SR_INPUT_H

View File

@ -0,0 +1,29 @@
#include "precompiled.h"
# include "sr_light.h"
//===================================== SrLight ================================
SrLight::SrLight ()
{
init ();
}
void SrLight::init ()
{
spot_exponent = 0;
spot_direction.set ( 0, 0, -1.0f );
spot_cutoff = 180;
constant_attenuation = 1.0f;
linear_attenuation = 0;
quadratic_attenuation = 0;
ambient = SrColor::black;
diffuse = SrColor::white;
specular = SrColor::black;
position = SrVec::k;
directional = true;
}
//================================ End of File =================================================

82
source/dcdt/se/sr_light.h Normal file
View File

@ -0,0 +1,82 @@
# ifndef SR_LIGHT_H
# define SR_LIGHT_H
/** \file sr_light.h
* Keeps light parameters
*/
# include "sr_vec.h"
# include "sr_color.h"
/*! \class SrLight sr_light.h
\brief Keeps light parameters
SrLight Keeps light parameters following the OpenGL specifications.
Note however that this class only stores data and has no dependency on
OpenGL functions. */
class SrLight
{ public :
/*! Higher spot exponents result in a more focused light source. The default
spot exponent is 0, resulting in uniform light distribution. Values must
be in the range of [0,128]. */
float spot_exponent;
/*! The direction of the light in homogeneous object coordinates. The spot
direction is transformed by the inverse of the modelview matrix when
glLight is called (just as if it was a normal), and it is stored in
eye coordinates. It is significant only when GL_SPOT_CUTOFF is not
180, which it is by default. The default direction is (0,0,-1). */
SrVec spot_direction;
/*! Specifies the maximum spread angle. Only values in the range [0,90],
and the special value 180, are accepted. If the angle between the
direction of the light and the direction from the light to the vertex
being lighted is greater than the spot cutoff angle, then the light
is completely masked. Otherwise, its intensity is controlled by the
spot exponent and the attenuation factors. The default spot cutoff
is 180, resulting in uniform light distribution. */
float spot_cutoff;
/*! If the light is positional, rather than directional, its intensity
is attenuated by the reciprocal of the sum of: the constant factor,
the linear factor multiplied by the distance between the light and
the vertex being lighted, and the quadratic factor multiplied by
the square of the same distance. The default attenuation factors
are cte=1, linear=0, quad=0, resulting in no attenuation. Only
nonnegative values are accepted. */
float constant_attenuation;
float linear_attenuation; //!< See constant_attenuation.
float quadratic_attenuation; //!< See constant_attenuation.
SrColor ambient; //!< Default is black
SrColor diffuse; //!< Default is white
SrColor specular; //!< Default is black
/*! The position is transformed by the modelview matrix when glLight is
called (just as if it was a point), and it is stored in eye
coordinates. If directional, diffuse and specular lighting calculations
take the lights direction, but not its actual position, into account,
and attenuation is disabled. Otherwise, diffuse and specular lighting
calculations are based on the actual location of the light in eye
coordinates, and attenuation is enabled. The default position is (0,0,1). */
SrVec position;
/*! When true means that the position w coord is 0, otherwise 1. Default is true. */
bool directional;
public :
/*! Initialize the camera with the default parameters. */
SrLight ();
/*! Sets the default parameters. */
void init ();
};
//================================ End of File =================================================
# endif // SR_LIGHT_H

177
source/dcdt/se/sr_line.cpp Normal file
View File

@ -0,0 +1,177 @@
#include "precompiled.h"
# include <math.h>
# include "sr_box.h"
# include "sr_line.h"
# include "sr_input.h"
# include "sr_output.h"
//===========================================================================
// Important: Static initializations cannot use other static initialized
// variables ( as SrVec::i, ... ), as we cannot know the order that they
// will be initialized by the compiler.
const SrLine SrLine::x ( SrVec(0,0,0), SrVec(1.0f,0,0) );
const SrLine SrLine::y ( SrVec(0,0,0), SrVec(0,1.0f,0) );
const SrLine SrLine::z ( SrVec(0,0,0), SrVec(0,0,1.0f) );
//============================== SrLine ====================================
#define EPSILON 0.00001 // floats have 7 decimals
// Original code from : http://www.acm.org/jgt/papers/MollerTrumbore97/
bool SrLine::intersects_triangle ( const SrVec &v0, const SrVec &v1, const SrVec &v2,
float &t, float &u, float &v ) const
{
SrVec dir, edge1, edge2, tvec, pvec, qvec;
float det, inv_det;
dir = p2 - p1;
edge1 = v1 - v0; // find vectors for two edges sharing v0
edge2 = v2 - v0;
pvec = cross ( dir, edge2 ); // begin calculating determinant - also used to calculate U parameter
det = dot ( edge1, pvec ); // if determinant is near zero, ray lies in plane of triangle
// printf("det=%f\n",det);
if ( SR_NEXTZ(det,EPSILON) )
{ //sr_out.warning("det in ray_triangle fails => %f",(float)det);
return false;
}
inv_det = 1.0f / det;
tvec = p1 - v0; // calculate distance from v0 to ray origin
u = dot(tvec, pvec) * inv_det; // calculate U parameter and test bounds
if ( u<0.0 || u>1.0 ) return false;
qvec = cross ( tvec, edge1 ); // prepare to test V parameter
v = dot(dir, qvec) * inv_det; // calculate V parameter and test bounds
if ( v<0.0 || u+v>1.0 ) return false;
t = dot(edge2,qvec) * inv_det; // calculate t, ray intersects triangle
return true;
}
bool SrLine::intersects_square ( const SrVec &v1, const SrVec &v2,
const SrVec &v3, const SrVec &v4, float& t ) const
{
float u, v;
if ( intersects_triangle ( v1, v2, v3, t, u, v ) ) return true;
if ( intersects_triangle ( v1, v3, v4, t, u, v ) ) return true;
return false;
}
int SrLine::intersects_box ( const SrBox& box, float& t1, float& t2, SrVec* vp ) const
{
SrVec p1, p2, p3, p4, p;
float t[6];
int side[6];
int tsize=0;
# define INTERSECT(s) if ( intersects_square(p1,p2,p3,p4,t[tsize]) ) { side[tsize]=s; tsize++; }
box.get_side ( p1, p2, p3, p4, 0 );
INTERSECT(0);
box.get_side ( p1, p2, p3, p4, 1 );
INTERSECT(1);
box.get_side ( p1, p2, p3, p4, 2 );
INTERSECT(2);
box.get_side ( p1, p2, p3, p4, 3 );
INTERSECT(3);
box.get_side ( p1, p2, p3, p4, 4 );
INTERSECT(4);
box.get_side ( p1, p2, p3, p4, 5 );
INTERSECT(5);
# undef INTERSECT
if ( tsize==0 )
{ t1=t2=0; }
else if ( tsize==1 )
{ t1=t2=t[0]; }
else if ( tsize==2 )
{ float tmpf;
int tmpi;
if ( t[1]<t[0] ) { SR_SWAPT(t[0],t[1],tmpf); SR_SWAPT(side[0],side[1],tmpi); }
t1 = t[0];
t2 = t[1];
}
else // sort according to t and take the two extremes
{ int i, j, tmpi;
float tmpf;
for ( i=0; i<tsize; i++ )
for ( j=i; j<tsize; j++ )
if ( t[j]<t[i] ) { SR_SWAPT(t[i],t[j],tmpf); SR_SWAPT(side[i],side[j],tmpi); }
t1 = t[0];
t2 = t[tsize-1];
tsize = 2;
}
if (tsize>0 && vp) box.get_side ( vp[0], vp[1], vp[2], vp[3], side[0] );
return tsize;
}
int SrLine::intersects_sphere ( const SrPnt& center, float radius, SrPnt* vp ) const
{
// set up quadratic Q(t) = a*t^2 + 2*b*t + c
SrVec dir = p2-p1;
SrVec kdiff = p1 - center;
float a = dir.norm2();
float b = dot ( kdiff, dir );
float c = kdiff.norm2() - radius*radius;
float aft[2];
float discr = b*b - a*c;
if ( discr < 0.0f )
{ return 0;
}
else if ( discr > 0.0f )
{ float root = sqrtf(discr);
float inva = 1.0f/a;
aft[0] = (-b - root)*inva;
aft[1] = (-b + root)*inva;
if ( vp )
{ vp[0] = p1 + aft[0]*dir;
vp[1] = p1 + aft[1]*dir;
if ( dist2(vp[1],p1)<dist2(vp[0],p1) )
{ SrPnt tmp;
SR_SWAP(vp[0],vp[1]);
}
}
return 2;
}
else
{ aft[0] = -b/a;
if ( vp ) vp[0] = p1 + aft[0]*dir;
return 1;
}
}
//http://astronomy.swin.edu.au/~pbourke/geometry/pointline/source.c
SrPnt SrLine::closestpt ( SrPnt p, float* k ) const
{
SrVec v (p2-p1);
float u = ( ( (p.x-p1.x) * (v.x) ) +
( (p.y-p1.y) * (v.y) ) +
( (p.z-p1.z) * (v.z) ) ) / ( v.norm2() );
//if( u<0.0f || u>1.0f ) // closest point does not fall within the line segment
if ( k ) *k=u;
return p1 + u*v;
}
//============================== friends ====================================
SrOutput& operator<< ( SrOutput& o, const SrLine& l )
{
return o << l.p1 <<" "<< l.p2;
}
SrInput& operator>> ( SrInput& in, SrLine& l )
{
return in >> l.p1 >> l.p2;
}
//============================= End of File ===========================================

95
source/dcdt/se/sr_line.h Normal file
View File

@ -0,0 +1,95 @@
/** \file sr_line.h
* Three dimensional line */
# ifndef SR_LINE_H
# define SR_LINE_H
# include "sr_vec.h"
class SrBox;
class SrInput;
class SrOutput;
/*! \class SrLine sr_line.h
\brief Three dimensional line
SrLine defines a line by keeping two points in the three dimensional
space. These two points are p1 and p2 of type SrVec(==SrPnt). When the line
is considered as a segement, p1 and p2 will delimit the segment, and
when the line is considered as a ray, the source of the ray is p1 and
the ray direction is defined as p2-p1. */
class SrLine
{ public :
SrPnt p1, p2;
public :
static const SrLine x; //!< (0,0,0)--(1,0,0) line
static const SrLine y; //!< (0,0,0)--(0,1,0) line
static const SrLine z; //!< (0,0,0)--(0,0,1) line
public :
/*! Initializes SrLine as the x axis (0,0,0)--(1,0,0) line. */
SrLine () : p1(SrPnt::null), p2(SrPnt::i) {}
/*! Copy constructor. */
SrLine ( const SrLine &l ) : p1(l.p1), p2(l.p2) {}
/*! Initializes with the given endpoints. */
SrLine ( const SrPnt &v1, const SrPnt &v2 ) : p1(v1), p2(v2) {}
/*! Set endpoints. */
void set ( const SrPnt &v1, const SrPnt &v2 ) { p1=v1; p2=v2; }
/*! Same as copy operator. */
void set ( const SrLine &l ) { p1=l.p1; p2=l.p2; }
/*! Copy operator from another SrLine. */
void operator = ( const SrLine &l ) { p1=l.p1; p2=l.p2; }
/*! Calculates the intersection of SrLine with the triangle [v0,v1,v2].
If the line intercepts the triangle, true is returned, otherwise
false is returned. The triangle can be given in any orientation.
When true is returned the return values t,u,v will satisfy:
(1-u-v)v0 + uv1 + vv2 == (1-t)p1 + (t)p2 == interception point.
In this way, u and v indicate a parametric distance from the vertices
and t is a parametric distance that can be used to determine if only
the segment [p1,p2] intersects in fact the triangle. */
bool intersects_triangle ( const SrPnt &v0, const SrPnt &v1, const SrPnt &v2,
float &t, float &u, float &v ) const;
/*! Returns the number of intersections between the line and the square (v1,v2,v3,v4).
In case true is returned, the intersection point is
defined by (1-t)p1 + (t)p2, so that if t is between 0 and 1, the point
is also inside the segment determined by SrLine. */
bool intersects_square ( const SrPnt& v1, const SrPnt& v2,
const SrPnt& v3, const SrPnt& v4, float& t ) const;
/*! Returns 1 or 2 if the line intersects the box, otherwise 0 is returned.
In case 2 is returned, there are two intersection points defined by
(1-t1)p1 + (t1)p2, and (1-t2)p1 + (t2)p2 (t1<t2).
In case 1 is returned, there is one intersection point (1-t1)p1 + (t1)p2,
and t1 is equal to t2. Parameter vp is a pointer to SrPnt[4], and will contain
the corners of the traversed side of the box, if vp is not null. */
int intersects_box ( const SrBox& box, float& t1, float& t2, SrPnt* vp=0 ) const;
/*! Returns 1 or 2 if the line intersects the sphere (center,radius), otherwise
0 is returned. In case 1 or 2 is returned, there are one or two intersection
points, which are put in the vp array, if the vp pointer is not null.
If two intersection points exist they are ordered according to the proximity
to SrLine::p1 */
int intersects_sphere ( const SrPnt& center, float radius, SrPnt* vp=0 ) const;
/*! Returns the closest point in the line to p. Parameter k, if given,
will be such that: closestpt == p1+k*(p2-p1) */
SrPnt closestpt ( SrPnt p, float* k=0 ) const;
/*! Outputs in format: "p1 p2". */
friend SrOutput& operator<< ( SrOutput& o, const SrLine& l );
/*! Inputs from format: "p1 p2". */
friend SrInput& operator>> ( SrInput& in, SrLine& l );
};
//============================== end of file ===============================
# endif // SR_LINE_H

265
source/dcdt/se/sr_lines.cpp Normal file
View File

@ -0,0 +1,265 @@
#include "precompiled.h"
# include "sr_box.h"
# include "sr_mat.h"
# include "sr_vec2.h"
# include "sr_lines.h"
//# define SR_USE_TRACE1 // Constructor and Destructor
# include "sr_trace.h"
//======================================= SrLines ====================================
const char* SrLines::class_name = "Lines";
SrLines::SrLines ()
{
SR_TRACE1 ( "Constructor" );
}
SrLines::~SrLines ()
{
SR_TRACE1 ( "Destructor" );
}
void SrLines::init ()
{
V.size(0);
C.size(0);
I.size(0);
}
void SrLines::compress ()
{
V.compress();
C.compress();
I.compress();
}
void SrLines::push_line ( const SrVec &p1, const SrVec &p2 )
{
V.push()=p1;
V.push()=p2;
}
void SrLines::push_line ( const SrVec2 &p1, const SrVec2 &p2 )
{
V.push().set(p1.x,p1.y,0);
V.push().set(p2.x,p2.y,0);
}
void SrLines::push_line ( float ax, float ay, float az, float bx, float by, float bz )
{
V.push().set(ax,ay,az);
V.push().set(bx,by,bz);
}
void SrLines::push_line ( float ax, float ay, float bx, float by )
{
V.push().set(ax,ay,0);
V.push().set(bx,by,0);
}
void SrLines::begin_polyline ()
{
I.push() = V.size();
}
void SrLines::end_polyline ()
{
I.push() = V.size()-1;
}
void SrLines::push_vertex ( const SrVec& p )
{
V.push() = p;
}
void SrLines::push_vertex ( const SrVec2& p )
{
V.push().set(p.x,p.y,0);
}
void SrLines::push_vertex ( float x, float y, float z )
{
V.push().set(x,y,z);
}
void SrLines::push_color ( const SrColor &c )
{
I.push() = V.size();
C.push() = c;
I.push() = -C.size();
}
void SrLines::push_cross ( SrVec2 c, float r )
{
SrVec2 p(r,r);
push_line ( c-p, c+p ); p.x*=-1;
push_line ( c-p, c+p );
}
void SrLines::push_axis ( const SrPnt& orig, float len, int dim, const char* let,
bool rule, SrBox* box )
{
float r, mr;
float a, b, c, k;
const float z = 0.0f;
r = box? box->max_size()/2.0f : len;
mr = -r;
a=r/25.0f; b=a/2.0f; c=a*3.0f; k=a/3.0f;
bool letx=false, lety=false, letz=false;
int vi = V.size();
if ( let )
while ( *let )
{ char c = SR_UPPER(*let);
if ( c=='X' ) letx=true;
else if ( c=='Y' ) lety=true;
else if ( c=='Z' ) letz=true;
let++;
}
if ( dim>=1 )
{ if ( box ) { mr=box->a.x; r=box->b.x; }
push_color ( SrColor::red );
push_line ( mr, z, z, r, z, z ); // X axis
if ( letx && r>0 )
{ push_line ( r, -a, z, r-a, -c, z ); // Letter X
push_line ( r-a, -a, z, r, -c, z );
}
if ( rule && r>1.0 )
{ int i; int mini=SR_CEIL(mr); int maxi=SR_FLOOR(r);
push_color ( SrColor::red );
for ( i=mini; i<maxi; i++ )
{ if ( i==0 ) continue;
push_line ( (float)i, z, z, (float)i, k, z );
}
}
}
if ( dim>=2 )
{ if ( box ) { mr=box->a.y; r=box->b.y; }
push_color ( SrColor::green );
push_line ( z, mr, z, z, r, z ); // Y axis
if ( lety && r>0 )
{ push_line ( a, r, z, a+b, r-a, z ); // Letter Y
push_line ( a+a, r, z, a, r-a-a, z );
}
if ( rule && r>1.0 )
{ int i; int mini=SR_CEIL(mr); int maxi=SR_FLOOR(r);
push_color ( SrColor::green );
for ( i=mini; i<maxi; i++ )
{ if ( i==0 ) continue;
push_line ( z, (float)i, z, -k, (float)i, z );
}
}
}
if ( dim>=3 )
{ if ( box ) { mr=box->a.z; r=box->b.z; }
push_color ( SrColor::blue );
push_line ( z, z, mr, z, z, r ); // Z axis
if ( letz && r>0 )
{ begin_polyline ();
push_vertex ( z, -a, r-a ); // Letter Z
push_vertex ( z, -a, r );
push_vertex ( z, -c, r-a );
push_vertex ( z, -c, r );
end_polyline ();
}
if ( rule && r>1.0 )
{ int i; int mini=SR_CEIL(mr); int maxi=SR_FLOOR(r);
push_color ( SrColor::blue );
for ( i=mini; i<maxi; i++ )
{ if ( i==0 ) continue;
push_line ( z, z, (float)i, z, k, (float)i );
}
}
}
if ( orig!=SrPnt::null )
{ int i;
for ( i=vi; i<V.size(); i++ ) V[i]+=orig;
}
}
void SrLines::push_box ( const SrBox& box, bool multicolor )
{
const SrPnt& a = box.a;
const SrPnt& b = box.b;
if ( multicolor ) push_color ( SrColor::red );
push_line ( a.x, a.y, a.z, b.x, a.y, a.z );
push_line ( a.x, a.y, b.z, b.x, a.y, b.z );
push_line ( a.x, b.y, a.z, b.x, b.y, a.z );
push_line ( a.x, b.y, b.z, b.x, b.y, b.z );
if ( multicolor ) push_color ( SrColor::green );
push_line ( a.x, a.y, a.z, a.x, b.y, a.z );
push_line ( a.x, a.y, b.z, a.x, b.y, b.z );
push_line ( b.x, a.y, b.z, b.x, b.y, b.z );
push_line ( b.x, a.y, a.z, b.x, b.y, a.z );
if ( multicolor ) push_color ( SrColor::blue );
push_line ( a.x, a.y, a.z, a.x, a.y, b.z );
push_line ( a.x, b.y, a.z, a.x, b.y, b.z );
push_line ( b.x, b.y, a.z, b.x, b.y, b.z );
push_line ( b.x, a.y, a.z, b.x, a.y, b.z );
}
void SrLines::push_polyline ( const SrArray<SrVec2>& a )
{
int i;
if ( a.size()<2 ) return;
I.push() = V.size();
for ( i=0; i<a.size(); i++ ) V.push().set(a[i].x,a[i].y,0);
I.push() = V.size()-1;
}
void SrLines::push_polygon ( const SrArray<SrVec2>& a )
{
int i;
if ( a.size()<2 ) return;
I.push() = V.size();
for ( i=0; i<a.size(); i++ ) V.push().set(a[i].x,a[i].y,0);
V.push().set(a[0].x,a[0].y,0);
I.push() = V.size()-1;
}
void SrLines::push_lines ( const SrArray<SrVec2>& a )
{
int i;
for ( i=0; i<a.size(); i++ ) V.push().set(a[i].x,a[i].y,0);
}
void SrLines::push_circle_approximation ( const SrPnt& center, const SrVec& radius,
const SrVec& normal, int nvertices )
{
SrVec x = radius; // rotating vec to draw the circle
SrVec x1st = x;
SrMat mr;
float dr = sr2pi / (float)nvertices;
mr.rot ( normal, dr );
begin_polyline ();
while ( nvertices-->0 )
{ push_vertex ( center + x );
x = x*mr;
}
push_vertex ( center+x1st ); // to make it close exactly
end_polyline ();
}
void SrLines::get_bounding_box ( SrBox& b ) const
{
int i;
b.set_empty ();
for ( i=0; i<V.size(); i++ )
{ b.extend ( V[i] );
}
}
//================================ EOF =================================================

Some files were not shown because too many files have changed in this diff Show More