JD2022-TU1/main/extern/Camcam/LIBS/Mixer/svg/triangulate.cpp

1183 lines
28 KiB
C++

#ifdef USE_SVG
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "triangulate.h"
/** déclarations : **/
extern char CamcamCCM[];
tsVertex* vertices = NULL;
int nvertices = 0;
/** déclarations externes : **/
//chemin du répertoire image à générer pour les textures du MAD
extern char svgTexturesDir[512];
//id de l'objet SVG courant
extern char svgObjectID[260];
//nouveaux points à créer
extern char** textureFilenames;
//nouveaux points à créer
extern std::vector<PointF> pointsList;
//noeud SVG courant
extern TiXmlElement* cNode;
//liste des textures SVG du document SVG
extern SvgTexture* svgTextures;
//nombre de textures SVG du document SVG
extern int numberOfSvgTextures;
//limites de la forme SVG courante
extern float svgxmin, svgymin, svgxmax, svgymax, svgz;
//centre de l'objet SVG en cours
extern PointF objectCenter;
//nombre de points composant l'objet SVG
extern int svgNbPoints;
//nombre d'allocations mémoire
extern int nbAlloc;
//tableau des tailles des zones mémoire allouées
extern unsigned int* allocSizes;
//tableau des pointeurs vers les zones mémoire allouées
extern void** allocStack;
//bounding box du monde SVG à créer
extern float svgWorld_Xmin, svgWorld_Ymin, svgWorld_Xmax, svgWorld_Ymax;
/** définition des fonctions : **/
/** triangulation de la forme SVG courante en utilisant les coordonnées des points de son contour **/
void triangulatePath(MAD_Simple3DVertex* madPointsList, MAD_GeometricObjectElement* madTrianglesList)
{
//retournement des coordonnées des vertices en Y en gardant l'ordre trigonométrique des vertices
{
float swp;
int n = pointsList.size();
for(int i=0; i<=(n-1)/2; i++)
{
//pour les Y
pointsList.at(i).y = -pointsList.at(i).y;
if(i!=(n-1)/2 || (n+1)%2)
{
pointsList.at(n-1-i).y = -pointsList.at(n-1-i).y;
swp = pointsList.at(i).y;
pointsList.at(i).y = pointsList.at(n-1-i).y;
pointsList.at(n-1-i).y = swp;
}
//pour les X
swp = pointsList.at(i).x;
pointsList.at(i).x = pointsList.at(n-1-i).x;
pointsList.at(n-1-i).x = swp;
}
//retournement en Y des coordonnées du dégradé
char fillValue[256] = "";
SvgFillType fillType = svgFillType(cNode->Attribute("fill"));
if(fillType!=FILL_EMPTY)
strcpy(fillValue, cNode->Attribute("fill"));
if(fillType==FILL_URL)
{
//récupération de l'ID de la texture
memmove(fillValue, fillValue+strlen("url(#"), strlen(fillValue)-strlen("url(#")-1);
*(fillValue+strlen(fillValue)-strlen("url(#")-1) = 0;
//récupération des caractéristiques de la texture
for(int i=0; i<numberOfSvgTextures; i++)
{
if(strcmp(svgTextures[i].name, fillValue)==0)
{
if(svgTextures[i].type== LINEAR_GRADIENT)
{
svgTextures[i].y1 = -svgTextures[i].y1;
svgTextures[i].y2 = -svgTextures[i].y2;
}
}
}
}
}
//passage des vertices au trianguleur
read_vertices(madPointsList);
//récupération des frontières et points significatifs de l'objet
scale_data();
//mémorisation du nombre de points de l'objet
svgNbPoints = pointsList.size();
//triangulation de l'objet
triangulate(madTrianglesList);
}
/*
Purpose:
AREA_POLY2 returns the area of a polygon.
Parameters:
Output, int AREA_POLY2, the area of the polygon.
*/
/*
Purpose:
READ_VERTICES reads the polygon vertice from standard input.
Discussion:
After reading the vertices, the function links them into a circular
list with MAKE_NULL_VERTEX. There is no need for the # of vertices
to be the first line: the function looks for EOF instead.
*/
void read_vertices(MAD_Simple3DVertex* madPointsList)
{
tsVertex* v;
vertices = NULL;
nvertices = 0;
float x;
float y;
int vnum = 0;
for(int i=0; i<svgNbPoints; i++)
{
//récupération des coordonnées du point à rajouter
x = pointsList.at(i).x;
y = pointsList.at(i).y;
//remplissage de la structure Mad
madPointsList[i].Point.x = x;
madPointsList[i].Point.y = y;
madPointsList[i].Point.z = svgz;
//remplissage de la structure tsVertex
v = make_null_vertex();
v->v[0] = x;
v->v[1] = y;
v->vnum = vnum++;
}
nvertices = vnum;
}
/*
Purpose:
SCALE_DATA determines the scale for the polygonal data.
Parameters:
Output, int *XMIN, *XMAX, *YMIN, *YMAX, the minimum and maximum
0 and 1 values of the coordinates of the vertices of the polygon.
*/
void scale_data()
{
tsVertex* v;
//Compute bounding box for Encapsulated PostScript.
v = vertices;
svgxmin = v->v[0];
svgxmax = v->v[0];
svgymin = v->v[1];
svgymax = v->v[1];
do
{
if(v->v[0] > svgxmax) svgxmax = v->v[0];
else if(v->v[0] < svgxmin) svgxmin = v->v[0];
if(v->v[1] > svgymax) svgymax = v->v[1];
else if(v->v[1] < svgymin) svgymin = v->v[1];
v = v->next;
} while (v != vertices);
//calcul des coordonnées du point de pivot
objectCenter = PointF((svgxmax+svgxmin)/2, (svgymax+svgymin)/2);
//mise à jour de la bounding box du monde SVG à créer
if(svgxmin<svgWorld_Xmin) svgWorld_Xmin = svgxmin;
if(svgymin<svgWorld_Ymin) svgWorld_Ymin = svgymin;
if(svgxmax>svgWorld_Xmax) svgWorld_Xmax = svgxmax;
if(svgymax>svgWorld_Ymax) svgWorld_Ymax = svgymax;
}
/*
Index a face in the Mad Meshes List
*/
void indexFace(MAD_GeometricObjectElement* madTrianglesList, tsVertex* v1, tsVertex* v2, tsVertex* v3, int trgInd)
{
madTrianglesList->Faces[trgInd].Index[2] = v3->vnum;
madTrianglesList->Faces[trgInd].Index[1] = v2->vnum;
madTrianglesList->Faces[trgInd].Index[0] = v1->vnum;
madTrianglesList->Faces[trgInd].UVIndex[2] = v3->vnum;
madTrianglesList->Faces[trgInd].UVIndex[1] = v2->vnum;
madTrianglesList->Faces[trgInd].UVIndex[0] = v1->vnum;
madTrianglesList->Faces[trgInd].SmoothingGroup = MAD_NULL_INDEX; //TO TRY
madTrianglesList->Faces[trgInd].MAXflags = 0; //7 = all //TO CHECK : effect under MAX and Camcam
}
/*
Purpose:
TRIANGULATE prints N-3 diagonals which triangulate the polygon.
Parameters:
Input, int XMIN, XMAX, YMIN, YMAX, the minimum and maximum
0 and 1 values of the coordinates of the vertices of the polygon.
*/
void triangulate(MAD_GeometricObjectElement* madTrianglesList)
{
//initialisations
tsVertex* v0, *v1, *v2, *v3, *v4; /* five consecutive vertices */
int n = nvertices; /* number of vertices; shrinks to 3. */
int trgInd = 0;
//remplissage des triangles de la structure Mad
madTrianglesList->MaterialID = 0;
madTrianglesList->NumberOfTriangles = nvertices-2;
madTrianglesList->Faces = (MAD_Face*)malloc2(madTrianglesList->NumberOfTriangles*sizeof(MAD_Face));
memset(madTrianglesList->Faces, 0, madTrianglesList->NumberOfTriangles*sizeof(MAD_Face));
madTrianglesList->NumberOfUsedIndex = svgNbPoints;
madTrianglesList->UsedIndex = (unsigned long*)malloc2(svgNbPoints*sizeof(unsigned long));
for(int i=0; i<svgNbPoints; i++) madTrianglesList->UsedIndex[i] = i;
madTrianglesList->OneUVPerVertexPerElementBase = 0;
//triangulation
ear_init();
//each step of outer loop removes one ear.
while(n > 3)
{
//inner loop searches for an ear.
v2 = vertices;
do
{
if(v2->ear)
{
//ear found. Fill variables.
v3 = v2->next;
v4 = v3->next;
v1 = v2->prev;
v0 = v1->prev;
//(v1,v3) is a diagonal
//ajout du triangle (v1, v2, v3) à la structure MAD
indexFace(madTrianglesList, v1, v2, v3, trgInd);
trgInd++;
//update earity of diagonal endpoints.
v1->ear = diagonal(v0, v3);
v3->ear = diagonal(v1, v4);
//cut off the ear v2.
v1->next = v3;
v3->prev = v1;
vertices = v3;
//in case the head was v2.
n--;
break;
}
//End if ear found
v2 = v2->next;
} while(v2 != vertices);
}
//ajout du dernier triangle (v1, v2, v3) à la structure MAD
v2 = v3->next; v3 = v2->next; v1 = v2->prev;
indexFace(madTrianglesList, v1, v2, v3, trgInd);
trgInd++;
}
/*
Purpose:
Calculates the UV coords for texturing the vertices created
Parameters:
Input, int XMIN, XMAX, YMIN, YMAX, the minimum and maximum
0 and 1 values of the coordinates of the vertices of the polygon.
*/
void createUVCoords(MAD_Vertex* madUVList)
{
bool isImage = svgTextures[numberOfSvgTextures-1].type == PNG_IMAGE
|| svgTextures[numberOfSvgTextures-1].type == JPEG_IMAGE;
//on doit assigner les UV pour une image (<-> rectangle)
if(isImage)
{
//remplissage des uv des vertices
madUVList[0].x = 1;
madUVList[0].y = 1;
madUVList[0].z = 1;
madUVList[1].x = 1;
madUVList[1].y = 0;
madUVList[1].z = 1;
madUVList[2].x = 0;
madUVList[2].y = 0;
madUVList[2].z = 1;
madUVList[3].x = 0;
madUVList[3].y = 1;
madUVList[3].z = 1;
//retournement verticale de la texture d'une image pour MAX
if(TheScene->GetIniValue(CamcamCCM, "svgFlipTexturesFor3DSMAX"))
{
MAD_Vertex tmp = madUVList[0];
madUVList[0] = madUVList[1];
madUVList[1] = tmp;
tmp = madUVList[3];
madUVList[3] = madUVList[2];
madUVList[2] = tmp;
}
return;
}
char fillValue[256] = "0";
SvgFillType fillType = svgFillType(cNode->Attribute("fill"));
//lecture de la valeur de la donnée de remplissage de la forme
if(fillType!=FILL_EMPTY)
strcpy(fillValue, cNode->Attribute("fill"));
//cas de la texture unicolor
if(fillType==FILL_COLOR)
{
//récupération de la couleur de remplissage
int fillColor = (int)atoh(fillValue+1);
//remplissage des uv des vertices
for(int i=0; i<svgNbPoints; i++)
{
madUVList[i].x = 0;
madUVList[i].y = 0;
madUVList[i].z = 1;
}
}
//cas de la texture multicolor
if(fillType==FILL_URL)
{
//récupération de l'ID de la texture
memmove(fillValue, fillValue+strlen("url(#"), strlen(fillValue)-strlen("url(#")-1);
*(fillValue+strlen(fillValue)-strlen("url(#")-1) = 0;
//récupération des caractéristiques de la texture
for(int i=0; i<numberOfSvgTextures; i++)
{
if(strcmp(svgTextures[i].name, fillValue)==0)
{
//TO FINISH:
switch(svgTextures[i].type)
{
case LINEAR_GRADIENT :
{
//calcul de l'équation de la droite directrice du dégradé
float x1 = (float)svgTextures[i].x1;
float y1 = (float)svgTextures[i].y1;
float x2 = (float)svgTextures[i].x2;
float y2 = (float)svgTextures[i].y2;
//calcul des paramètres de la droite directrice (P1,P2) du dégradé
float c = 0; float d = 0;
if(x1!=x2)
{
c = (y1-y2)/(x1-x2);
d = y1 - c*x1;
}
//norme au carré du vecteur P1P2
float nc12 = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
//remplissage des uv des vertices
for(int i=0; i<svgNbPoints; i++)
{
//position du vertex courant (M) de la forme SVG (en coordonnées flottantes absolues)
float xm = pointsList.at(i).x;
float ym = pointsList.at(i).y;
//projeté P de M sur la droite (P1,P2)
float xp = x1;
float yp = ym;
if(x1!=x2)
{
xp = (c*ym + xm - c*d)/(c*c+1);
yp = c*xp + d;
}
//produit scalaire des vecteurs P1P et P1P2
float s1p12 = (xp-x1)*(x2-x1) + (yp-y1)*(y2-y1);
//place de P sur la droite P1P2
float prop = 0;
if(s1p12!=0 && nc12!=0) prop = s1p12/nc12;
//pour le mode de dégradé "PAD"
if(prop<0) prop = 0.001;
if(prop>1) prop = 0.999;
//affectation des uv pour le vertex M
madUVList[i].x = prop;
madUVList[i].y = 0;
madUVList[i].z = 1;
}
} break;
//TO FINISH:
case RADIAL_GRADIENT :
{
if(svgTextures[i].cx==1e-30) svgTextures[i].cx = objectCenter.x; //TO CHANGE to 50%
if(svgTextures[i].cy==1e-30) svgTextures[i].cy = objectCenter.y; //TO CHANGE to 50%
if(svgTextures[i].r==1e-30) svgTextures[i].r = 0; //TO CHANGE to 50%
if(svgTextures[i].fx==1e-30) svgTextures[i].fx = svgTextures[i].cx;
if(svgTextures[i].fy==1e-30) svgTextures[i].fy = svgTextures[i].cy;
//calcul de l'équation de la droite directrice du dégradé
float x1 = (float)svgTextures[i].cx;
float y1 = (float)svgTextures[i].cy;
float x2 = (float)svgTextures[i].fx;
float y2 = (float)svgTextures[i].fy;
//calcul des paramètres de la droite directrice (P1,P2) du dégradé
float c = 0; float d = 0;
if(x1!=x2)
{
c = (y1-y2)/(x1-x2);
d = y1 - c*x1;
}
//norme au carré du vecteur P1P2
float nc12 = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
//remplissage des uv des vertices
for(int i=0; i<svgNbPoints; i++)
{
//position du vertex courant (M) de la forme SVG (en coordonnées flottantes absolues)
float xm = pointsList.at(i).x;
float ym = pointsList.at(i).y;
//projeté P de M sur la droite (P1,P2)
float xp = x1;
float yp = ym;
if(x1!=x2)
{
xp = (c*ym + xm - c*d)/(c*c+1);
yp = c*xp + d;
}
//produit scalaire des vecteurs P1P et P1P2
float s1p12 = (xp-x1)*(x2-x1) + (yp-y1)*(y2-y1);
//place de P sur la droite P1P2
float prop = 1.0f - 1.0/OFFSET_PRECISION;
if(s1p12!=0 && nc12!=0)
prop = s1p12/nc12;
//pour le mode de dégradé "PAD"
if(prop<0) prop = 0;
if(prop>1) prop = 1;
//affectation des uv pour le vertex M
madUVList[i].x = prop;
madUVList[i].y = 0;
madUVList[i].z = 1;
}
} break;
case PNG_IMAGE :
{
} break;
}
//sortie de la boucle
i = numberOfSvgTextures;
}
}
}
}
/*
création de la texture "path" correspondant à l'image SVG courante
*/
void createImageTexture(char *textureFilename)
{
//création du nom du fichier image temporaire PNG ou JPEG
char path[260] = "";
if(svgTextures[numberOfSvgTextures-1].type == PNG_IMAGE) sprintf(path, "%s\\%s - texture.png", svgTexturesDir, svgObjectID);
else sprintf(path, "%s\\%s - texture.jpeg", svgTexturesDir, svgObjectID);
//récupération des données images en base 64 dans le node SVG courant
char* data = svgTextures[numberOfSvgTextures-1].data;
data = strstr(data, ",") + 1;
//décodage des données image de base 64 vers ascii
char* cleanedData = getCleanedString(data);
char* decodedData = (char*)malloc2(1+strlen(data));
int dataLength = decodeB64(cleanedData, decodedData);
free2(cleanedData);
free2(svgTextures[numberOfSvgTextures-1].data);
svgTextures[numberOfSvgTextures-1].data = decodedData;
//écriture du fichier temporaire
FILE* outFile = CC_fopen(path, "wb");
fwrite(decodedData, 1, dataLength, outFile);
CC_fclose(outFile);
//***** à supprimer dès que camcam supportera les fichiers PNG comme texture *****
//vérification de la présence de "nconvert.exe" dans le répertoire "/SVG"
char myShareDirPath[] = "\\\\mtp-mauboussin\\Share";
extern HWND hWnd;
char Text[2048];
sprintf(Text,"NCONVERT.EXE is missing in the \"/SVG\" folder of the application and is necessary to convert SVG files to MAD files. \nYou should find it in the folder \"%s\".", myShareDirPath);
while(lengthOfFile("SVG\\nconvert.exe")==-1)
{
int userAnswer = MessageBox(GetDesktopWindow(),Text,"Camcam message",MB_RETRYCANCEL|MB_SYSTEMMODAL);
if(userAnswer == IDCANCEL)
{
char svgTexturesDir[] = "SVG\\TMP";
CleanDirectory(svgTexturesDir);
RemoveDirectory(svgTexturesDir);
exit(-1);
}
}
//écriture de la texture TGA
remove(textureFilename);
char convertLine[200] = "";
sprintf(convertLine, "\"SVG\\nconvert.exe\" -out tga -o \"%s\" \"%s\"", textureFilename, path);
WinExec(convertLine, SW_HIDE);
//TO IMPROVE:
FILE* filetmp;
while(!(filetmp = CC_fopen(textureFilename, "r")))
Sleep(10);
CC_fclose(filetmp);
//suppression du fichier temporaire
remove(path);
//*/
}
void createShapeTexture(char *path)
{
//définition de la MAP de texture
MAP * sourceMap = (MAP*)malloc2(sizeof(MAP));
//récupération du type de remplissage de la forme
SvgFillType fillType = svgFillType(cNode->Attribute("fill"));
//cas de la texture unicolor : FILL_EMPTY or FILL_NONE or FILL_COLOR
if(fillType!=FILL_URL)
{
//récupération de la couleur de remplissage
int fillColor = 0;
if(fillType==FILL_COLOR)
{
//lecture de la valeur de la donnée de remplissage de la forme
char fillValue[256] = "";
strcpy(fillValue, cNode->Attribute("fill"));
//transformation en entier de la couleur en hexadécimal
fillColor = (int)atoh(fillValue+1);
}
//création de la MAP de texture
sourceMap->SX = sourceMap->SY = sourceMap->PITCH = 8;
sourceMap->BASE = (s32*)malloc2(sourceMap->SX*sourceMap->SY*sizeof(s32));
//remplissage de la MAP de texture
int color = 255<<24 | fillColor;
if(fillType==FILL_NONE) color = 0;
for(int pixel=0; pixel<sourceMap->SX*sourceMap->SY; pixel++)
*(sourceMap->BASE+pixel) = color;
}
//cas de la texture multicolor
else
{
//lecture de la valeur de la donnée de remplissage de la forme
char fillValue[256] = "";
strcpy(fillValue, cNode->Attribute("fill"));
//récupération de l'ID de la texture
memmove(fillValue, fillValue+strlen("url(#"), strlen(fillValue)-strlen("url(#")-1);
*(fillValue+strlen(fillValue)-strlen("url(#")-1) = 0;
//récupération des caractéristiques de la texture
for(int i=0; i<numberOfSvgTextures; i++)
{
if(strcmp(svgTextures[i].name, fillValue)==0)
{
//TO FINISH:
switch(svgTextures[i].type)
{
case LINEAR_GRADIENT :
{
//création de la MAP de texture
sourceMap->SY = 8;
sourceMap->SX = sourceMap->PITCH = OFFSET_PRECISION;
sourceMap->BASE = (s32*)malloc2(sourceMap->SX*sourceMap->SY*sizeof(s32));
//remplissage de la MAP de texture
int indOffset = 0;
for(int px=0; px<sourceMap->SX; px++)
{
float offsetInf = (float)(svgTextures[i].stopColors[indOffset])/OFFSET_PRECISION;
float offsetSup = (float)(svgTextures[i].stopColors[indOffset+2])/OFFSET_PRECISION;
float x = ((float)px/OFFSET_PRECISION);
float prop = (float)(x-offsetInf)/(float)(offsetSup-offsetInf);
int colorInf = svgTextures[i].stopColors[indOffset+1];
int colorSup = svgTextures[i].stopColors[indOffset+3];
int red = prop*(float)(getR(colorSup)-getR(colorInf))+getR(colorInf);
int green = prop*(float)(getV(colorSup)-getV(colorInf))+getV(colorInf);
int blue = prop*(float)(getB(colorSup)-getB(colorInf))+getB(colorInf);
int color = makeRGB(red, green, blue);
for(int py=0; py<sourceMap->SY; py++)
{
*(sourceMap->BASE+px+py*sourceMap->PITCH) = 255<<24 | color;
}
if(x > offsetSup)
indOffset+=2;
}
} break;
case RADIAL_GRADIENT : //TO FINISH:
break;
}
//sortie de la boucle
i = numberOfSvgTextures;
}
}
}
//remplissage de la structure TGA
MyTGAHeader header;
memset(&header, 0, sizeof(header));
header.uc_ImageTypeCode = 2;
header.uw_Width = sourceMap->SX;
header.uw_Height = sourceMap->SY;
header.uc_BPP = 32;
header.ucDescriptorByte = 0x8;
//écriture du fichier TGA
FILE* f = CC_fopen(path, "wb");
if(f)
{
fwrite(&header, sizeof(header), 1, f);
fwrite(sourceMap->BASE, sizeof(char), sourceMap->SX * sourceMap->SY * 4, f);
CC_fclose(f);
}
//libération des ressources utilisées
free2(sourceMap->BASE);
free2(sourceMap);
}
/************************************************************************************/
/*
Purpose:
DIAGONAL returns TRUE iff (A,B) is a proper internal diagonal of the polygon.
Parameters:
Input, tsVertex* A, B, two vertices of the polygon.
Output, bool DIAGONAL, is TRUE if the line connecting A and B is a
proper internal diagonal of the polygon.
*/
bool diagonal ( tsVertex* a, tsVertex* b )
{
return in_cone ( a, b ) && in_cone ( b, a ) && diagonalie ( a, b );
}
/*
Purpose:
MAKE_NULL_VERTEX makes a vertex.
Parameters:
Output, tsVertex* MAKE_NULL_VERTEX, the new vertex.
*/
tsVertex* make_null_vertex ( void )
{
tsVertex* v;
v = ( tsVertex * ) malloc2 ( sizeof ( tsVertex ) );
if(vertices)
{
v->next = vertices;
v->prev = vertices->prev;
vertices->prev = v;
v->prev->next = v;
}
else
{
vertices = v;
vertices->next = v;
vertices->prev = v;
}
return v;
}
/*
Purpose:
EAR_INIT initializes the data structures, and calls Triangulate2 to clip ears.
Local Parameters:
Local, tsVertex* V0, V1, V2, three consecutive vertices of the polygon.
*/
void ear_init ( void )
{
tsVertex* v0;
tsVertex* v1;
tsVertex* v2;
/*
Initialize v1->ear for all vertices.
*/
v1 = vertices;
do
{
v2 = v1->next;
v0 = v1->prev;
v1->ear = diagonal ( v0, v2 );
v1 = v1->next;
} while ( v1 != vertices );
return;
}
/*
Purpose:
DIAGONALIE returns TRUE iff (A,B) is a proper diagonal of a polygon.
Discussion:
(A,B) may be an internal or external diagonal of the polygon, ignoring edges
incident to A and B.
Parameters:
Input, tsVertex* A, B, two vertices of the polygon.
Output, bool DIAGONALIE, is TRUE if the line connecting A and B is a
proper diagonal of the polygon.
*/
bool diagonalie ( tsVertex* a, tsVertex* b )
{
tsVertex* c;
tsVertex* c1;
/*
For each edge (C,C1) of P.
*/
c = vertices;
do
{
c1 = c->next;
/*
Skip edges incident to A or B.
*/
if ( ( c != a ) && ( c1 != a ) && ( c != b ) && ( c1 != b ) &&
intersect( a->v, b->v, c->v, c1->v ) )
{
return false;
}
c = c->next;
} while ( c != vertices );
return true;
}
/*
Purpose:
IN_CONE returns TRUE iff the diagonal (A,B) is strictly internal.
Discussion:
More correctly, the diagonal (A,B) must be strictly internal to the
polygon in the neighborhood of the A endpoint.
Parameters:
Input, tsVertex* A, B, two vertices of the polygon.
Output, bool IN_CONE, is TRUE if the line connecting A and B is
strictly internal to the polygon in the neighborhood of A.
Local Parameters:
Local, tsVertex* A0, A1, are the vertices before and after A.
*/
bool in_cone ( tsVertex* a, tsVertex* b )
{
tsVertex* a0;
tsVertex* a1;
a1 = a->next;
a0 = a->prev;
/*
If A is a convex vertex ...
*/
if ( left_on ( a->v, a1->v, a0->v ) )
{
return left ( a->v, b->v, a0->v )
&& left ( b->v, a->v, a1->v );
}
/*
Else A is reflex vertex:
*/
else
{
return !( left_on ( a->v, b->v, a1->v )
&& left_on ( b->v, a->v, a0->v ) );
}
}
float area_poly2 ( void )
{
tsVertex* a;
tsVertex* p;
float sum = 0;
p = vertices; /* Fixed. */
a = p->next; /* Moving. */
do
{
sum = sum + area2 ( p->v, a->v, a->next->v );
a = a->next;
} while ( a->next != vertices );
return sum;
}
/*
Purpose:
AREA_SIGN returns the sign of the area defined by three points.
Parameters:
Input, tPointi A, B, C, three points that define a triangle.
Output, int AREA_SIGN, the sign of the area of the triangle.
*/
int area_sign ( tPointi a, tPointi b, tPointi c )
{
float area;
area = ( b[0] - a[0] ) * ( c[1] - a[1] ) - ( c[0] - a[0] ) * ( b[1] - a[1] );
/*
The area should be an integer.
*/
if ( area > 0 )
{
return 1;
}
else if ( area < 0 )
{
return -1;
}
else
{
return 0;
}
}
/*
Purpose:
AREA2 returns twice the signed area of a triangle.
Discussion:
The area is positive if points A, B, and C are oriented counter clockwise,
negative if clockwise, and zero if the points are collinear.
Parameters:
Input, tPointi A, B, C, three points that define a triangle.
Output, int AREA2, twice the signed area of the triangle.
*/
float area2 ( tPointi a, tPointi b, tPointi c )
{
return (( b[0] - a[0] ) * ( c[1] - a[1] ) - ( c[0] - a[0] ) * ( b[1] - a[1] ));
}
/*
Purpose:
BETWEEN returns TRUE iff point C lies on the closed segement AB.
Discussion:
The function first checks that C is collinear with A and B.
Parameters:
Input, tPointi A, B, C, three points to be tested.
Output, bool BETWEEN is TRUE if point C lies on the closed
segment between A and B.
*/
bool between ( tPointi a, tPointi b, tPointi c )
{
if ( ! collinear ( a, b, c ) )
{
return false;
}
/*
If AB not vertical, check betweenness on x; else on y.
*/
if ( a[0] != b[0] )
{
return ( (a[0] <= c[0] ) && ( c[0] <= b[0] ) ) ||
( (a[0] >= c[0] ) && ( c[0] >= b[0] ) );
}
else
{
return ( ( a[1] <= c[1] ) && ( c[1] <= b[1] ) ) ||
( ( a[1] >= c[1] ) && ( c[1] >= b[1] ) );
}
}
/*
Purpose:
COLLINEAR is TRUE if the points A, B and C are collinear.
Parameters:
Input, tPointi A, B, C, three points to be tested.
Output, bool COLLINEAR, is TRUE if the points are collinear.
*/
bool collinear ( tPointi a, tPointi b, tPointi c )
{
return ( area_sign ( a, b, c ) == 0 );
}
/*
Purpose:
INTERSECT returns TRUE iff segments AB and CD intersect.
Discussion:
The intersection may be proper or improper.
Parameters:
Input, tPointi A, B, C, D, points that define the segments.
Output, bool INTERSECT, is TRUE if segments AB and CD intersect.
*/
bool intersect ( tPointi a, tPointi b, tPointi c, tPointi d )
{
if ( intersect_prop ( a, b, c, d ) )
{
return true;
}
else if ( between ( a, b, c )
|| between ( a, b, d )
|| between ( c, d, a )
|| between ( c, d, b ) )
{
return true;
}
else
{
return false;
}
}
/*
Purpose:
INTERSECT_PROP returns true if and only if AB properly intersects CD.
Discussion:
AB and CD must share a point interior to both segments. The properness of
the intersection is ensured by using strict leftness.
Parameters:
Input, tPointi A, B, C, D, points that define segments AB and CD.
Output, bool INTERSECT_PROP, is TRUE if AB properly intersects CD.
*/
bool intersect_prop ( tPointi a, tPointi b, tPointi c, tPointi d )
{
/*
Eliminate improper cases.
*/
if ( collinear ( a, b, c ) ||
collinear ( a, b, d ) ||
collinear ( c, d, a ) ||
collinear ( c, d, b ) )
{
return false;
}
return
xor ( left ( a, b, c ), left ( a, b, d ) )
&& xor ( left ( c, d, a ), left ( c, d, b ) );
}
/*
Purpose:
LEFT is TRUE if C is on the left side of the line from A to B.
Discussion:
More correctly, the function returns true if and only if C is strictly
to the left of the directed line through A to B.
Parameters:
Input, tPointi A, B, C, three points to be tested.
Output, bool LEFT, is TRUE if C is strictly to the left of the directed
line from A to B.
*/
bool left ( tPointi a, tPointi b, tPointi c )
{
return ( area_sign ( a, b, c ) > 0 );
}
/*
Purpose:
LEFT_ON is TRUE if C is to the left side, or on, the line from A to B.
Parameters:
Input, tPointi A, B, C, three points to be tested.
Output, bool LEFT_ON, is TRUE if C is strictly to the left of, or on,
the directed line from A to B.
*/
bool left_on ( tPointi a, tPointi b, tPointi c )
{
return ( area_sign ( a, b, c ) >= 0 );
}
/*
Purpose:
PRINT_POLY prints the polygon data.
*/
void print_poly ( void )
{
tsVertex* v;
printf ( "%%\n" );
printf ( "%%Polygon circular list:\n" );
printf ( "%%\n" );
v = vertices;
do
{
printf( "%% vnum=%5d: ear=%d\n", v->vnum, v->ear );
v = v->next;
} while ( v != vertices );
printf ( "%%\n" );
return;
}
/*
Purpose:
PRINT_VERTICES prints the vertices.
Discussion:
This function uses the VNUM indices corresponding to the order in which
the vertices were input. The output is in PostScript format.
Parameters:
Input, int XMIN, XMAX, YMIN, YMAX, the minimum and maximum
0 and 1 values of the coordinates of the vertices of the polygon.
Input, int SCALE, an appropriate scaling for the data.
*/
void print_vertices ( float xmin, float xmax, float ymin, float ymax, int scale )
{
/* Pointers to vertices, edges, faces. */
tsVertex* v;
int x;
int y;
/*
PostScript header
*/
printf ( "%%!PS\n" );
printf ( "%%%%Creator: triangulate.c (Joseph O'Rourke)\n" );
printf ( "%%%%BoundingBox: %d %d %d %d\n",
0, 0, 72 + scale * ( xmax - xmin ), 72 + scale * ( ymax - ymin ) );
printf ( "%%%%EndComments\n" );
printf ( "1 1 setlinewidth\n" );
/*
Output vertex information as a PostScript comment.
*/
printf ( "\n" );
printf ( "%% number of vertices = %d\n", nvertices );
v = vertices;
do
{
printf ( "%% vnum=%5d:\tx=%5d\ty=%5d\n", v->vnum, v->v[0], v->v[1] );
v = v->next;
} while ( v != vertices );
/*
Draw the polygon.
*/
printf ( "\n%%Polygon:\n" );
printf ( "newpath\n" );
v = vertices;
x = 36 + scale * ( v->v[0] - xmin );
y = 36 + scale * ( v->v[1] - ymin );
printf ( "%d\t%d\tmoveto\n", x, y );
v = v->next;
do
{
x = 36 + scale * ( v->v[0] - xmin );
y = 36 + scale * ( v->v[1] - ymin );
printf ( "%d\t%d\tlineto\n", x, y );
v = v->next;
} while ( v != vertices );
printf ( "closepath stroke\n" );
}
bool xor ( bool x, bool y )
{
return ( !x ^ !y );
}
#endif //USE_SVG