use libtess-jni in ExtrusionLayer, remove Triangle

This commit is contained in:
Hannes Janetzek 2013-09-21 12:27:17 +02:00
parent bc3885cd54
commit da4d1c1ee7
20 changed files with 1491 additions and 11099 deletions

View File

@ -1,4 +1,4 @@
package org.oscim.utils.geom;
package org.oscim.utils;
import org.oscim.backend.Log;
import org.oscim.renderer.elements.VertexItem;
@ -9,7 +9,7 @@ import com.google.gwt.core.client.JsArrayNumber;
import com.google.gwt.core.client.JsArrayUtils;
import com.google.gwt.typedarrays.shared.Int32Array;
public class Triangulator {
public class Tessellator {
public static synchronized int triangulate(float[] points, int ppos, int plen, short[] index,
int ipos, int rings, int vertexOffset, VertexItem outTris) {

View File

@ -1,5 +1,4 @@
import com.badlogic.gdx.jnigen.AntScriptGenerator;
import com.badlogic.gdx.jnigen.BuildConfig;
import com.badlogic.gdx.jnigen.BuildTarget;
@ -9,30 +8,28 @@ public class JniBuilder {
public static void main(String[] args) {
String[] headers = { "." };
String[] sources = {
// Matrix stuff
"gl/utils.c",
// Triangle
"triangle/TriangleJni.c",
"triangle/triangle.c",
"triangle/triangle_dbg.c",
// libtessellate
"tessellate/dict.c",
"tessellate/mesh.c",
"tessellate/render.c",
"tessellate/tess.c",
"tessellate/geom.c",
"tessellate/memalloc.c",
"tessellate/normal.c",
"tessellate/priorityq.c",
"tessellate/sweep.c",
"tessellate/tessmono.c",
"tessellate/tessellate.c"
};
// Matrix stuff
"gl/utils.c",
String cflags = " -Wall -std=c99 -O2 -ffast-math -DTRILIBRARY -DREDUCED -DCDT_ONLY -DNO_TIMER";
// libtessellate
"tessellate/dict.c",
"tessellate/mesh.c",
"tessellate/render.c",
"tessellate/tess.c",
"tessellate/geom.c",
"tessellate/memalloc.c",
"tessellate/normal.c",
"tessellate/priorityq.c",
"tessellate/sweep.c",
"tessellate/tessmono.c",
"tessellate/tessellate.c",
"tessellate/TessellateJni.c"
};
String cflags = " -Wall -std=c99 -O2 -ffast-math";
BuildTarget win32home = BuildTarget.newDefaultTarget(TargetOs.Windows,
false);
false);
win32home.compilerPrefix = "";
win32home.buildFileName = "build-windows32home.xml";
win32home.excludeFromMasterBuildFile = true;
@ -42,14 +39,14 @@ public class JniBuilder {
win32home.cppFlags += cflags;
BuildTarget win32 = BuildTarget.newDefaultTarget(TargetOs.Windows,
false);
false);
win32.headerDirs = headers;
win32.cIncludes = sources;
win32.cFlags += cflags;
win32.cppFlags += cflags;
BuildTarget win64 = BuildTarget
.newDefaultTarget(TargetOs.Windows, true);
.newDefaultTarget(TargetOs.Windows, true);
win64.headerDirs = headers;
win64.cIncludes = sources;
win64.cFlags += cflags;
@ -76,7 +73,7 @@ public class JniBuilder {
// mac.linkerFlags += " -framework CoreServices -framework Carbon";
BuildTarget android = BuildTarget.newDefaultTarget(TargetOs.Android,
false);
false);
android.headerDirs = headers;
android.cIncludes = sources;
android.cFlags += cflags;
@ -90,15 +87,11 @@ public class JniBuilder {
//new NativeCodeGenerator().generate();
new AntScriptGenerator().generate(new BuildConfig("vtm-jni"),
//win32home, win32, win64, lin32,
lin64, android);
// BuildExecutor.executeAnt("jni/build-windows32home.xml", "-v clean");
// BuildExecutor.executeAnt("jni/build-windows32home.xml", "-v");
// BuildExecutor.executeAnt("jni/build.xml", "pack-natives -v");
//win32home, win32, win64, lin32,
lin64, android);
// BuildExecutor.executeAnt("jni/build-windows32home.xml", "-v clean");
// BuildExecutor.executeAnt("jni/build-windows32home.xml", "-v");
// BuildExecutor.executeAnt("jni/build.xml", "pack-natives -v");
}
}

View File

@ -1,2 +1,2 @@
all:
gcc dict.c mesh.c render.c tess.c geom.c memalloc.c normal.c priorityq.c sweep.c tessmono.c tessellate.c main.c -o tessellate
gcc -g -DTEST dict.c mesh.c render.c tess.c geom.c memalloc.c normal.c priorityq.c sweep.c tessmono.c tessellate.c main.c -o tessellate

View File

@ -0,0 +1,225 @@
#include "tessellate.h"
#include <stdlib.h>
#include <stdint.h>
#include <jni.h>
#ifdef __ANDROID__
#include <android/log.h>
#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "Tesselate", __VA_ARGS__)
#endif
#define CAST_CTX(x) (TessContext *)(uintptr_t) x
void Java_org_oscim_utils_Tessellator_tessFinish(JNIEnv *env, jclass c, jlong ptr_context) {
TessContext *ctx = CAST_CTX(ptr_context);
while (ctx->latest_v) {
Vertex *prev = ctx->latest_v->prev;
free(ctx->latest_v);
ctx->latest_v = prev;
}
while (ctx->latest_t) {
Triangle *prev = ctx->latest_t->prev;
free(ctx->latest_t);
ctx->latest_t = prev;
}
//destroy_tess_context(ctx);
free(ctx);
}
jint Java_org_oscim_utils_Tessellator_tessGetCoordinates(JNIEnv *env, jclass c,
jlong ptr_context, jshortArray obj_coords, jfloat scale) {
TessContext *ctx = CAST_CTX(ptr_context);
int length = (*env)->GetArrayLength(env, obj_coords);
jshort* coords = (jshort*) (*env)->GetPrimitiveArrayCritical(env, obj_coords, 0);
if (coords == NULL) {
return 0;
}
//int n_verts = 1 + ctx->latest_v->index;
//int n_tris_copy = ctx->n_tris;
int cnt = 0;
for (; ctx->latest_v && cnt < length; cnt += 2) {
coords[cnt + 0] = (ctx->latest_v->pt[0] * scale) + 0.5f;
coords[cnt + 1] = (ctx->latest_v->pt[1] * scale) + 0.5f;
Vertex *prev = ctx->latest_v->prev;
free(ctx->latest_v);
ctx->latest_v = prev;
}
(*env)->ReleasePrimitiveArrayCritical(env, obj_coords, coords, JNI_ABORT);
return cnt;
}
jint Java_org_oscim_utils_Tessellator_tessGetCoordinatesD(JNIEnv *env, jclass c,
jlong ptr_context, jdoubleArray obj_coords) {
TessContext *ctx = CAST_CTX(ptr_context);
int length = (*env)->GetArrayLength(env, obj_coords);
jdouble* coords = (jdouble*) (*env)->GetPrimitiveArrayCritical(env, obj_coords, 0);
if (coords == NULL) {
return 0;
}
//int n_verts = 1 + ctx->latest_v->index;
//int n_tris_copy = ctx->n_tris;
int cnt = 0;
for (; ctx->latest_v && cnt < length; cnt += 2) {
coords[cnt + 0] = ctx->latest_v->pt[0];
coords[cnt + 1] = ctx->latest_v->pt[1];
Vertex *prev = ctx->latest_v->prev;
free(ctx->latest_v);
ctx->latest_v = prev;
}
(*env)->ReleasePrimitiveArrayCritical(env, obj_coords, coords, JNI_ABORT);
return cnt;
}
jint Java_org_oscim_utils_Tessellator_tessGetIndices(JNIEnv *env, jclass c,
jlong ptr_context, jshortArray obj_indices) {
TessContext *ctx = CAST_CTX(ptr_context);
int length = (*env)->GetArrayLength(env, obj_indices);
jshort* tris = (jshort*) (*env)->GetPrimitiveArrayCritical(env, obj_indices, 0);
if (tris == NULL) {
return 0;
}
int n_tris_copy = ctx->n_tris;
int cnt = 0;
for (; ctx->latest_t && cnt < length; cnt += 3) {
tris[cnt + 0] = ctx->latest_t->v[0];
tris[cnt + 1] = ctx->latest_t->v[1];
tris[cnt + 2] = ctx->latest_t->v[2];
Triangle *prev = ctx->latest_t->prev;
free(ctx->latest_t);
ctx->latest_t = prev;
n_tris_copy--;
}
ctx->n_tris = n_tris_copy;
(*env)->ReleasePrimitiveArrayCritical(env, obj_indices, tris, JNI_ABORT);
return cnt;
}
jlong Java_org_oscim_utils_Tessellator_tessellate(JNIEnv *env, jclass c,
jfloatArray obj_points, jint pos,
jshortArray obj_index, jint ipos,
jint num_rings, jintArray obj_out) {
//printf("add %d %d %d\n", pos, ipos, num_rings);
jboolean isCopy;
float* orig_points = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_points, &isCopy);
if (orig_points == NULL)
return 0;
const float *points = orig_points + pos;
jshort* orig_indices = (jshort*) (*env)->GetPrimitiveArrayCritical(env, obj_index, &isCopy);
if (orig_indices == NULL) {
(*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT);
return 0;
}
jshort* indices = orig_indices + ipos;
const float **rings = malloc(sizeof(float*) * (num_rings + 1));
int offset = 0;
for (int i = 0; i < num_rings; i++) {
rings[i] = points + offset;
offset += indices[i];
}
(*env)->ReleasePrimitiveArrayCritical(env, obj_index, orig_indices, JNI_ABORT);
(*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT);
rings[num_rings] = points + offset;
int nverts, ntris;
TessContext *ctx = tessellate(NULL, &nverts, NULL, &ntris,
rings, rings + (num_rings + 1));
free(rings);
nverts = 1 + ctx->latest_v->index;
ntris = ctx->n_tris;
jint* out = (jint*) (*env)->GetPrimitiveArrayCritical(env, obj_out, &isCopy);
if (out == NULL) {
return 0;
}
out[0] = nverts;
out[1] = ntris;
(*env)->ReleasePrimitiveArrayCritical(env, obj_out, out, JNI_ABORT);
return (long) ctx;
}
jlong Java_org_oscim_renderer_sublayers_MeshLayer_tessellateD(JNIEnv *env, jclass c,
jdoubleArray obj_points, jint pos,
jshortArray obj_index, jint ipos,
jint num_rings) { //, jintArray obj_out) {
jboolean isCopy;
//printf("add %d %d %d\n", pos, ipos, num_rings);
double* orig_points = (double*) (*env)->GetPrimitiveArrayCritical(env, obj_points, &isCopy);
if (orig_points == NULL)
return 0;
const double *points = orig_points + pos;
jshort* orig_indices = (jshort*) (*env)->GetPrimitiveArrayCritical(env, obj_index, &isCopy);
if (orig_indices == NULL) {
(*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT);
return 0;
}
jshort* indices = orig_indices + ipos;
const double **rings = malloc(sizeof(double*) * (num_rings + 1));
int offset = 0;
for (int i = 0; i < num_rings; i++) {
rings[i] = points + offset;
offset += indices[i];
}
rings[num_rings] = points + offset;
int nverts, ntris;
TessContext *ctx = tessellateD(NULL, &nverts, NULL, &ntris,
rings, rings + (num_rings + 1));
free(rings);
(*env)->ReleasePrimitiveArrayCritical(env, obj_index, orig_indices, JNI_ABORT);
(*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT);
return (long) ctx;
}

View File

@ -7,22 +7,25 @@ void run_example(const double vertices_array[],
int contours_size)
{
double *coordinates_out;
int *tris_out;
int nverts, ntris, i;
const double *p = vertices_array;
/* const double **contours = contours_array; */
//const double **contours = contours_array;
tessellate(&coordinates_out, &nverts,
tessellateD(&coordinates_out, &nverts,
&tris_out, &ntris,
contours_array, contours_array + contours_size);
for (i=0; i<2 * nverts; ++i) {
fprintf(stdout, "%g ", coordinates_out[i]);
for (i=0; i< nverts; i += 1) {
fprintf(stdout, "%g %g, ", coordinates_out[i*2], coordinates_out[i*2+1]);
}
fprintf(stdout, "\n");
for (i=0; i<3 * ntris; ++i) {
fprintf(stdout, "%d ", tris_out[i]);
for (i=0; i< ntris; i += 1) {
fprintf(stdout, "%d %d %d\n", tris_out[i*3], tris_out[i*3+1], tris_out[i*3+2]);
}
fprintf(stdout, "\n");
free(coordinates_out);
@ -30,15 +33,23 @@ void run_example(const double vertices_array[],
free(tris_out);
}
int main()
{
double a1[] = { 0, 0, 1, 5, 2, 0, -1, 3, 3, 3 };
const double *c1[] = {a1, a1+10};
int s1 = 2;
int main() {
double a1[] = {
0, 0,
1, 0,
1, 1,
0, 1,
};
const double *c1[] = {a1, a1 + 8};
run_example(a1, c1, 2);
printf("\n");
double a2[] = { 0, 0, 3, 0, 3, 3, 0, 3,
1, 1, 2, 1, 2, 2, 1, 2 };
const double *c2[] = {a2, a2+8, a2+16};
int s2 = 3;
run_example(a2, c2, s2);

View File

@ -28,9 +28,9 @@
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include "mesh.h"
@ -38,6 +38,7 @@
#include "normal.h"
#include <math.h>
#include <assert.h>
#include <stdio.h>
#ifndef TRUE
#define TRUE 1
@ -51,121 +52,139 @@
#if 0
static void Normalize( GLdouble v[3] )
{
GLdouble len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
GLdouble len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
assert( len > 0 );
len = sqrt( len );
v[0] /= len;
v[1] /= len;
v[2] /= len;
assert( len > 0 );
len = sqrt( len );
v[0] /= len;
v[1] /= len;
v[2] /= len;
}
#endif
#undef ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
static int LongAxis( GLdouble v[3] )
static int LongAxis(GLdouble v[3])
{
int i = 0;
int i = 0;
if( ABS(v[1]) > ABS(v[0]) ) { i = 1; }
if( ABS(v[2]) > ABS(v[i]) ) { i = 2; }
return i;
if (ABS(v[1]) > ABS(v[0])) {
i = 1;
}
if (ABS(v[2]) > ABS(v[i])) {
i = 2;
}
return i;
}
static void ComputeNormal( GLUtesselator *tess, GLdouble norm[3] )
static void ComputeNormal(GLUtesselator *tess, GLdouble norm[3])
{
GLUvertex *v, *v1, *v2;
GLdouble c, tLen2, maxLen2;
GLdouble maxVal[3], minVal[3], d1[3], d2[3], tNorm[3];
GLUvertex *maxVert[3], *minVert[3];
GLUvertex *vHead = &tess->mesh->vHead;
int i;
GLUvertex *v, *v1, *v2;
GLdouble c, tLen2, maxLen2;
GLdouble maxVal[3], minVal[3], d1[3], d2[3], tNorm[3];
GLUvertex *maxVert[3], *minVert[3];
GLUvertex *vHead = &tess->mesh->vHead;
int i;
maxVal[0] = maxVal[1] = maxVal[2] = -2 * GLU_TESS_MAX_COORD;
minVal[0] = minVal[1] = minVal[2] = 2 * GLU_TESS_MAX_COORD;
maxVal[0] = maxVal[1] = maxVal[2] = -2 * GLU_TESS_MAX_COORD;
minVal[0] = minVal[1] = minVal[2] = 2 * GLU_TESS_MAX_COORD;
for( v = vHead->next; v != vHead; v = v->next ) {
for( i = 0; i < 3; ++i ) {
c = v->coords[i];
if( c < minVal[i] ) { minVal[i] = c; minVert[i] = v; }
if( c > maxVal[i] ) { maxVal[i] = c; maxVert[i] = v; }
}
}
for (v = vHead->next; v != vHead; v = v->next) {
for (i = 0; i < 3; ++i) {
c = v->coords[i];
if (c < minVal[i]) {
minVal[i] = c;
minVert[i] = v;
}
if (c > maxVal[i]) {
maxVal[i] = c;
maxVert[i] = v;
}
}
}
/* Find two vertices separated by at least 1/sqrt(3) of the maximum
* distance between any two vertices
*/
i = 0;
if( maxVal[1] - minVal[1] > maxVal[0] - minVal[0] ) { i = 1; }
if( maxVal[2] - minVal[2] > maxVal[i] - minVal[i] ) { i = 2; }
if( minVal[i] >= maxVal[i] ) {
/* All vertices are the same -- normal doesn't matter */
norm[0] = 0; norm[1] = 0; norm[2] = 1;
return;
}
/* Find two vertices separated by at least 1/sqrt(3) of the maximum
* distance between any two vertices
*/
i = 0;
if (maxVal[1] - minVal[1] > maxVal[0] - minVal[0]) {
i = 1;
}
if (maxVal[2] - minVal[2] > maxVal[i] - minVal[i]) {
i = 2;
}
if (minVal[i] >= maxVal[i]) {
/* All vertices are the same -- normal doesn't matter */
norm[0] = 0;
norm[1] = 0;
norm[2] = 1;
return;
}
/* Look for a third vertex which forms the triangle with maximum area
* (Length of normal == twice the triangle area)
*/
maxLen2 = 0;
v1 = minVert[i];
v2 = maxVert[i];
d1[0] = v1->coords[0] - v2->coords[0];
d1[1] = v1->coords[1] - v2->coords[1];
d1[2] = v1->coords[2] - v2->coords[2];
for( v = vHead->next; v != vHead; v = v->next ) {
d2[0] = v->coords[0] - v2->coords[0];
d2[1] = v->coords[1] - v2->coords[1];
d2[2] = v->coords[2] - v2->coords[2];
tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1];
tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2];
tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0];
tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2];
if( tLen2 > maxLen2 ) {
maxLen2 = tLen2;
norm[0] = tNorm[0];
norm[1] = tNorm[1];
norm[2] = tNorm[2];
}
}
/* Look for a third vertex which forms the triangle with maximum area
* (Length of normal == twice the triangle area)
*/
maxLen2 = 0;
v1 = minVert[i];
v2 = maxVert[i];
d1[0] = v1->coords[0] - v2->coords[0];
d1[1] = v1->coords[1] - v2->coords[1];
d1[2] = v1->coords[2] - v2->coords[2];
for (v = vHead->next; v != vHead; v = v->next) {
d2[0] = v->coords[0] - v2->coords[0];
d2[1] = v->coords[1] - v2->coords[1];
d2[2] = v->coords[2] - v2->coords[2];
tNorm[0] = d1[1] * d2[2] - d1[2] * d2[1];
tNorm[1] = d1[2] * d2[0] - d1[0] * d2[2];
tNorm[2] = d1[0] * d2[1] - d1[1] * d2[0];
tLen2 = tNorm[0] * tNorm[0] + tNorm[1] * tNorm[1] + tNorm[2] * tNorm[2];
if (tLen2 > maxLen2) {
maxLen2 = tLen2;
norm[0] = tNorm[0];
norm[1] = tNorm[1];
norm[2] = tNorm[2];
}
}
if( maxLen2 <= 0 ) {
/* All points lie on a single line -- any decent normal will do */
norm[0] = norm[1] = norm[2] = 0;
norm[LongAxis(d1)] = 1;
}
if (maxLen2 <= 0) {
/* All points lie on a single line -- any decent normal will do */
norm[0] = norm[1] = norm[2] = 0;
norm[LongAxis(d1)] = 1;
}
//printf("compute normal %f %f %f\n", norm[0], norm[1], norm[2]);
}
static void CheckOrientation( GLUtesselator *tess )
static void CheckOrientation(GLUtesselator *tess)
{
GLdouble area;
GLUface *f, *fHead = &tess->mesh->fHead;
GLUvertex *v, *vHead = &tess->mesh->vHead;
GLUhalfEdge *e;
GLdouble area;
GLUface *f, *fHead = &tess->mesh->fHead;
GLUvertex *v, *vHead = &tess->mesh->vHead;
GLUhalfEdge *e;
/* When we compute the normal automatically, we choose the orientation
* so that the sum of the signed areas of all contours is non-negative.
*/
area = 0;
for( f = fHead->next; f != fHead; f = f->next ) {
e = f->anEdge;
if( e->winding <= 0 ) continue;
do {
area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t);
e = e->Lnext;
} while( e != f->anEdge );
}
if( area < 0 ) {
/* Reverse the orientation by flipping all the t-coordinates */
for( v = vHead->next; v != vHead; v = v->next ) {
v->t = - v->t;
}
tess->tUnit[0] = - tess->tUnit[0];
tess->tUnit[1] = - tess->tUnit[1];
tess->tUnit[2] = - tess->tUnit[2];
}
/* When we compute the normal automatically, we choose the orientation
* so that the sum of the signed areas of all contours is non-negative.
*/
area = 0;
for (f = fHead->next; f != fHead; f = f->next) {
e = f->anEdge;
if (e->winding <= 0)
continue;
do {
area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t);
e = e->Lnext;
} while (e != f->anEdge);
}
if (area < 0) {
/* Reverse the orientation by flipping all the t-coordinates */
for (v = vHead->next; v != vHead; v = v->next) {
v->t = -v->t;
}
tess->tUnit[0] = -tess->tUnit[0];
tess->tUnit[1] = -tess->tUnit[1];
tess->tUnit[2] = -tess->tUnit[2];
}
}
#ifdef FOR_TRITE_TEST_PROGRAM
@ -195,63 +214,63 @@ extern int RandomSweep;
/* Determine the polygon normal and project vertices onto the plane
* of the polygon.
*/
void __gl_projectPolygon( GLUtesselator *tess )
void __gl_projectPolygon(GLUtesselator *tess)
{
GLUvertex *v, *vHead = &tess->mesh->vHead;
GLdouble norm[3];
GLdouble *sUnit, *tUnit;
int i, computedNormal = FALSE;
GLUvertex *v, *vHead = &tess->mesh->vHead;
GLdouble norm[3];
GLdouble *sUnit, *tUnit;
int i, computedNormal = FALSE;
norm[0] = tess->normal[0];
norm[1] = tess->normal[1];
norm[2] = tess->normal[2];
if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
ComputeNormal( tess, norm );
computedNormal = TRUE;
}
sUnit = tess->sUnit;
tUnit = tess->tUnit;
i = LongAxis( norm );
norm[0] = tess->normal[0];
norm[1] = tess->normal[1];
norm[2] = tess->normal[2];
if (norm[0] == 0 && norm[1] == 0 && norm[2] == 0) {
ComputeNormal(tess, norm);
computedNormal = TRUE;
}
sUnit = tess->sUnit;
tUnit = tess->tUnit;
i = LongAxis(norm);
#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT)
/* Choose the initial sUnit vector to be approximately perpendicular
* to the normal.
*/
Normalize( norm );
/* Choose the initial sUnit vector to be approximately perpendicular
* to the normal.
*/
Normalize( norm );
sUnit[i] = 0;
sUnit[(i+1)%3] = S_UNIT_X;
sUnit[(i+2)%3] = S_UNIT_Y;
sUnit[i] = 0;
sUnit[(i+1)%3] = S_UNIT_X;
sUnit[(i+2)%3] = S_UNIT_Y;
/* Now make it exactly perpendicular */
w = Dot( sUnit, norm );
sUnit[0] -= w * norm[0];
sUnit[1] -= w * norm[1];
sUnit[2] -= w * norm[2];
Normalize( sUnit );
/* Now make it exactly perpendicular */
w = Dot( sUnit, norm );
sUnit[0] -= w * norm[0];
sUnit[1] -= w * norm[1];
sUnit[2] -= w * norm[2];
Normalize( sUnit );
/* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */
tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1];
tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2];
tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0];
Normalize( tUnit );
/* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */
tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1];
tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2];
tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0];
Normalize( tUnit );
#else
/* Project perpendicular to a coordinate axis -- better numerically */
sUnit[i] = 0;
sUnit[(i+1)%3] = S_UNIT_X;
sUnit[(i+2)%3] = S_UNIT_Y;
/* Project perpendicular to a coordinate axis -- better numerically */
sUnit[i] = 0;
sUnit[(i + 1) % 3] = S_UNIT_X;
sUnit[(i + 2) % 3] = S_UNIT_Y;
tUnit[i] = 0;
tUnit[(i+1)%3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y;
tUnit[(i+2)%3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X;
tUnit[i] = 0;
tUnit[(i + 1) % 3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y;
tUnit[(i + 2) % 3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X;
#endif
/* Project the vertices onto the sweep plane */
for( v = vHead->next; v != vHead; v = v->next ) {
v->s = Dot( v->coords, sUnit );
v->t = Dot( v->coords, tUnit );
}
if( computedNormal ) {
CheckOrientation( tess );
}
/* Project the vertices onto the sweep plane */
for (v = vHead->next; v != vHead; v = v->next) {
v->s = Dot( v->coords, sUnit );
v->t = Dot( v->coords, tUnit );
}
if (computedNormal) {
CheckOrientation(tess);
}
}

View File

@ -28,13 +28,14 @@
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "mesh.h"
#include "tess.h"
#include "render.h"
@ -51,24 +52,22 @@
* primitive is able to use the most triangles.
*/
struct FaceCount {
long size; /* number of triangles used */
GLUhalfEdge *eStart; /* edge where this primitive starts */
void (*render)(GLUtesselator *, GLUhalfEdge *, long);
/* routine to render this primitive */
long size; /* number of triangles used */
GLUhalfEdge *eStart; /* edge where this primitive starts */
void (*render)(GLUtesselator *, GLUhalfEdge *, long);
/* routine to render this primitive */
};
static struct FaceCount MaximumFan( GLUhalfEdge *eOrig );
static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig );
static void RenderFan( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *eStart,
long size );
static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig );
static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head );
static struct FaceCount MaximumFan(GLUhalfEdge *eOrig);
static struct FaceCount MaximumStrip(GLUhalfEdge *eOrig);
static void RenderFan(GLUtesselator *tess, GLUhalfEdge *eStart, long size);
static void RenderStrip(GLUtesselator *tess, GLUhalfEdge *eStart, long size);
static void RenderTriangle(GLUtesselator *tess, GLUhalfEdge *eStart,
long size);
static void RenderMaximumFaceGroup(GLUtesselator *tess, GLUface *fOrig);
static void RenderLonelyTriangles(GLUtesselator *tess, GLUface *head);
/************************ Strips and Fans decomposition ******************/
@ -79,63 +78,79 @@ static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head );
*
* The rendering output is provided as callbacks (see the api).
*/
void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh )
void __gl_renderMesh(GLUtesselator *tess, GLUmesh *mesh)
{
GLUface *f;
GLUface *f;
/* Make a list of separate triangles so we can render them all at once */
tess->lonelyTriList = NULL;
/* Make a list of separate triangles so we can render them all at once */
tess->lonelyTriList = NULL;
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
f->marked = FALSE;
}
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
for (f = mesh->fHead.next; f != &mesh->fHead; f = f->next) {
f->marked = FALSE;
}
for (f = mesh->fHead.next; f != &mesh->fHead; f = f->next) {
/* We examine all faces in an arbitrary order. Whenever we find
* an unprocessed face F, we output a group of faces including F
* whose size is maximum.
*/
if( f->inside && ! f->marked ) {
RenderMaximumFaceGroup( tess, f );
assert( f->marked );
}
}
if( tess->lonelyTriList != NULL ) {
RenderLonelyTriangles( tess, tess->lonelyTriList );
tess->lonelyTriList = NULL;
}
/* We examine all faces in an arbitrary order. Whenever we find
* an unprocessed face F, we output a group of faces including F
* whose size is maximum.
*/
if (f->inside && !f->marked) {
RenderMaximumFaceGroup(tess, f);
assert( f->marked);
}
}
if (tess->lonelyTriList != NULL) {
RenderLonelyTriangles(tess, tess->lonelyTriList);
tess->lonelyTriList = NULL;
}
}
static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig )
static void RenderMaximumFaceGroup(GLUtesselator *tess, GLUface *fOrig)
{
/* We want to find the largest triangle fan or strip of unmarked faces
* which includes the given face fOrig. There are 3 possible fans
* passing through fOrig (one centered at each vertex), and 3 possible
* strips (one for each CCW permutation of the vertices). Our strategy
* is to try all of these, and take the primitive which uses the most
* triangles (a greedy approach).
*/
GLUhalfEdge *e = fOrig->anEdge;
struct FaceCount max, newFace;
/* We want to find the largest triangle fan or strip of unmarked faces
* which includes the given face fOrig. There are 3 possible fans
* passing through fOrig (one centered at each vertex), and 3 possible
* strips (one for each CCW permutation of the vertices). Our strategy
* is to try all of these, and take the primitive which uses the most
* triangles (a greedy approach).
*/
GLUhalfEdge *e = fOrig->anEdge;
struct FaceCount max, newFace;
max.size = 1;
max.eStart = e;
max.render = &RenderTriangle;
max.size = 1;
max.eStart = e;
max.render = &RenderTriangle;
if( ! tess->flagBoundary ) {
newFace = MaximumFan( e ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumFan( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumFan( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
if (!tess->flagBoundary) {
newFace = MaximumFan(e);
if (newFace.size > max.size) {
max = newFace;
}
newFace = MaximumFan(e->Lnext);
if (newFace.size > max.size) {
max = newFace;
}
newFace = MaximumFan(e->Lprev);
if (newFace.size > max.size) {
max = newFace;
}
newFace = MaximumStrip( e ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumStrip( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumStrip( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
}
(*(max.render))( tess, max.eStart, max.size );
newFace = MaximumStrip(e);
if (newFace.size > max.size) {
max = newFace;
}
newFace = MaximumStrip(e->Lnext);
if (newFace.size > max.size) {
max = newFace;
}
newFace = MaximumStrip(e->Lprev);
if (newFace.size > max.size) {
max = newFace;
}
}
(*(max.render))(tess, max.eStart, max.size);
}
/* Macros which keep track of faces we have marked temporarily, and allow
* us to backtrack when necessary. With triangle fans, this is not
* really necessary, since the only awkward case is a loop of triangles
@ -153,213 +168,209 @@ static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig )
} \
} while(0) /* absorb trailing semicolon */
static struct FaceCount MaximumFan( GLUhalfEdge *eOrig )
static struct FaceCount MaximumFan(GLUhalfEdge *eOrig)
{
/* eOrig->Lface is the face we want to render. We want to find the size
* of a maximal fan around eOrig->Org. To do this we just walk around
* the origin vertex as far as possible in both directions.
*/
struct FaceCount newFace = { 0, NULL, &RenderFan };
GLUface *trail = NULL;
GLUhalfEdge *e;
/* eOrig->Lface is the face we want to render. We want to find the size
* of a maximal fan around eOrig->Org. To do this we just walk around
* the origin vertex as far as possible in both directions.
*/
struct FaceCount newFace = { 0, NULL, &RenderFan };
GLUface *trail = NULL;
GLUhalfEdge *e;
for( e = eOrig; ! Marked( e->Lface ); e = e->Onext ) {
AddToTrail( e->Lface, trail );
++newFace.size;
}
for( e = eOrig; ! Marked( e->Rface ); e = e->Oprev ) {
AddToTrail( e->Rface, trail );
++newFace.size;
}
newFace.eStart = e;
/*LINTED*/
FreeTrail( trail );
return newFace;
for (e = eOrig; !Marked( e->Lface ); e = e->Onext) {
AddToTrail( e->Lface, trail);
++newFace.size;
}
for (e = eOrig; !Marked( e->Rface ); e = e->Oprev) {
AddToTrail( e->Rface, trail);
++newFace.size;
}
newFace.eStart = e;
/*LINTED*/
FreeTrail( trail);
return newFace;
}
#define IsEven(n) (((n) & 1) == 0)
static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig )
static struct FaceCount MaximumStrip(GLUhalfEdge *eOrig)
{
/* Here we are looking for a maximal strip that contains the vertices
* eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the
* reverse, such that all triangles are oriented CCW).
*
* Again we walk forward and backward as far as possible. However for
* strips there is a twist: to get CCW orientations, there must be
* an *even* number of triangles in the strip on one side of eOrig.
* We walk the strip starting on a side with an even number of triangles;
* if both side have an odd number, we are forced to shorten one side.
*/
struct FaceCount newFace = { 0, NULL, &RenderStrip };
long headSize = 0, tailSize = 0;
GLUface *trail = NULL;
GLUhalfEdge *e, *eTail, *eHead;
/* Here we are looking for a maximal strip that contains the vertices
* eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the
* reverse, such that all triangles are oriented CCW).
*
* Again we walk forward and backward as far as possible. However for
* strips there is a twist: to get CCW orientations, there must be
* an *even* number of triangles in the strip on one side of eOrig.
* We walk the strip starting on a side with an even number of triangles;
* if both side have an odd number, we are forced to shorten one side.
*/
struct FaceCount newFace = { 0, NULL, &RenderStrip };
long headSize = 0, tailSize = 0;
GLUface *trail = NULL;
GLUhalfEdge *e, *eTail, *eHead;
for( e = eOrig; ! Marked( e->Lface ); ++tailSize, e = e->Onext ) {
AddToTrail( e->Lface, trail );
++tailSize;
e = e->Dprev;
if( Marked( e->Lface )) break;
AddToTrail( e->Lface, trail );
}
eTail = e;
for (e = eOrig; !Marked( e->Lface ); ++tailSize, e = e->Onext) {
AddToTrail( e->Lface, trail);
++tailSize;
e = e->Dprev;
if (Marked( e->Lface ))
break;
AddToTrail( e->Lface, trail);
}
eTail = e;
for( e = eOrig; ! Marked( e->Rface ); ++headSize, e = e->Dnext ) {
AddToTrail( e->Rface, trail );
++headSize;
e = e->Oprev;
if( Marked( e->Rface )) break;
AddToTrail( e->Rface, trail );
}
eHead = e;
for (e = eOrig; !Marked( e->Rface ); ++headSize, e = e->Dnext) {
AddToTrail( e->Rface, trail);
++headSize;
e = e->Oprev;
if (Marked( e->Rface ))
break;
AddToTrail( e->Rface, trail);
}
eHead = e;
newFace.size = tailSize + headSize;
if( IsEven( tailSize )) {
newFace.eStart = eTail->Sym;
} else if( IsEven( headSize )) {
newFace.eStart = eHead;
} else {
/* Both sides have odd length, we must shorten one of them. In fact,
* we must start from eHead to guarantee inclusion of eOrig->Lface.
*/
--newFace.size;
newFace.eStart = eHead->Onext;
}
/*LINTED*/
FreeTrail( trail );
return newFace;
newFace.size = tailSize + headSize;
if (IsEven( tailSize )) {
newFace.eStart = eTail->Sym;
}
else if (IsEven( headSize )) {
newFace.eStart = eHead;
}
else {
/* Both sides have odd length, we must shorten one of them. In fact,
* we must start from eHead to guarantee inclusion of eOrig->Lface.
*/
--newFace.size;
newFace.eStart = eHead->Onext;
}
/*LINTED*/
FreeTrail( trail);
return newFace;
}
static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *e, long size )
static void RenderTriangle(GLUtesselator *tess, GLUhalfEdge *e, long size)
{
/* Just add the triangle to a triangle list, so we can render all
* the separate triangles at once.
*/
assert( size == 1 );
AddToTrail( e->Lface, tess->lonelyTriList );
/* Just add the triangle to a triangle list, so we can render all
* the separate triangles at once.
*/
assert( size == 1);
AddToTrail( e->Lface, tess->lonelyTriList);
}
static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *f )
static void RenderLonelyTriangles(GLUtesselator *tess, GLUface *f)
{
/* Now we render all the separate triangles which could not be
* grouped into a triangle fan or strip.
*/
GLUhalfEdge *e;
int newState;
int edgeState = -1; /* force edge state output for first vertex */
/* Now we render all the separate triangles which could not be
* grouped into a triangle fan or strip.
*/
GLUhalfEdge *e;
int newState;
int edgeState = -1; /* force edge state output for first vertex */
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES );
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES);
for( ; f != NULL; f = f->trail ) {
/* Loop once for each edge (there will always be 3 edges) */
for (; f != NULL; f = f->trail) {
/* Loop once for each edge (there will always be 3 edges) */
e = f->anEdge;
do {
if( tess->flagBoundary ) {
/* Set the "edge state" to TRUE just before we output the
* first vertex of each edge on the polygon boundary.
*/
newState = ! e->Rface->inside;
if( edgeState != newState ) {
edgeState = newState;
CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState );
}
}
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
e = f->anEdge;
do {
if (tess->flagBoundary) {
/* Set the "edge state" to TRUE just before we output the
* first vertex of each edge on the polygon boundary.
*/
newState = !e->Rface->inside;
if (edgeState != newState) {
edgeState = newState;
CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState);
}
}
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data);
e = e->Lnext;
} while( e != f->anEdge );
}
CALL_END_OR_END_DATA();
e = e->Lnext;
} while (e != f->anEdge);
}
CALL_END_OR_END_DATA();
}
static void RenderFan( GLUtesselator *tess, GLUhalfEdge *e, long size )
static void RenderFan(GLUtesselator *tess, GLUhalfEdge *e, long size)
{
/* Render as many CCW triangles as possible in a fan starting from
* edge "e". The fan *should* contain exactly "size" triangles
* (otherwise we've goofed up somewhere).
*/
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN );
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
/* Render as many CCW triangles as possible in a fan starting from
* edge "e". The fan *should* contain exactly "size" triangles
* (otherwise we've goofed up somewhere).
*/
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN);
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data);
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data);
while( ! Marked( e->Lface )) {
e->Lface->marked = TRUE;
--size;
e = e->Onext;
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
}
while (!Marked( e->Lface )) {
e->Lface->marked = TRUE;
--size;
e = e->Onext;
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data);
}
assert( size == 0 );
CALL_END_OR_END_DATA();
assert( size == 0);
CALL_END_OR_END_DATA();
}
static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *e, long size )
static void RenderStrip(GLUtesselator *tess, GLUhalfEdge *e, long size)
{
/* Render as many CCW triangles as possible in a strip starting from
* edge "e". The strip *should* contain exactly "size" triangles
* (otherwise we've goofed up somewhere).
*/
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP );
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
/* Render as many CCW triangles as possible in a strip starting from
* edge "e". The strip *should* contain exactly "size" triangles
* (otherwise we've goofed up somewhere).
*/
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP);
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data);
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data);
while( ! Marked( e->Lface )) {
e->Lface->marked = TRUE;
--size;
e = e->Dprev;
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
if( Marked( e->Lface )) break;
while (!Marked( e->Lface )) {
e->Lface->marked = TRUE;
--size;
e = e->Dprev;
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data);
if (Marked( e->Lface ))
break;
e->Lface->marked = TRUE;
--size;
e = e->Onext;
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
}
e->Lface->marked = TRUE;
--size;
e = e->Onext;
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data);
}
assert( size == 0 );
CALL_END_OR_END_DATA();
assert( size == 0);
CALL_END_OR_END_DATA();
}
/************************ Boundary contour decomposition ******************/
/* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one
* contour for each face marked "inside". The rendering output is
* provided as callbacks (see the api).
*/
void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh )
void __gl_renderBoundary(GLUtesselator *tess, GLUmesh *mesh)
{
GLUface *f;
GLUhalfEdge *e;
GLUface *f;
GLUhalfEdge *e;
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
if( f->inside ) {
CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP );
e = f->anEdge;
do {
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
e = e->Lnext;
} while( e != f->anEdge );
CALL_END_OR_END_DATA();
}
}
for (f = mesh->fHead.next; f != &mesh->fHead; f = f->next) {
if (f->inside) {
CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP);
e = f->anEdge;
do {
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data);
e = e->Lnext;
} while (e != f->anEdge);
CALL_END_OR_END_DATA();
}
}
}
/************************ Quick-and-dirty decomposition ******************/
#define SIGN_INCONSISTENT 2
static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check )
static int ComputeNormal(GLUtesselator *tess, GLdouble norm[3], int check)
/*
* If check==FALSE, we compute the polygon normal and place it in norm[].
* If check==TRUE, we check that each triangle in the fan from v0 has a
@ -369,66 +380,79 @@ static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check )
* SIGN_INCONSISTENT.
*/
{
CachedVertex *v0 = tess->cache;
CachedVertex *vn = v0 + tess->cacheCount;
CachedVertex *vc;
GLdouble dot, xc, yc, zc, xp, yp, zp, n[3];
int sign = 0;
CachedVertex *v0 = tess->cache;
CachedVertex *vn = v0 + tess->cacheCount;
CachedVertex *vc;
GLdouble dot, xc, yc, zc, xp, yp, zp, n[3];
int sign = 0;
/* Find the polygon normal. It is important to get a reasonable
* normal even when the polygon is self-intersecting (eg. a bowtie).
* Otherwise, the computed normal could be very tiny, but perpendicular
* to the true plane of the polygon due to numerical noise. Then all
* the triangles would appear to be degenerate and we would incorrectly
* decompose the polygon as a fan (or simply not render it at all).
*
* We use a sum-of-triangles normal algorithm rather than the more
* efficient sum-of-trapezoids method (used in CheckOrientation()
* in normal.c). This lets us explicitly reverse the signed area
* of some triangles to get a reasonable normal in the self-intersecting
* case.
*/
if( ! check ) {
norm[0] = norm[1] = norm[2] = 0.0;
}
/* Find the polygon normal. It is important to get a reasonable
* normal even when the polygon is self-intersecting (eg. a bowtie).
* Otherwise, the computed normal could be very tiny, but perpendicular
* to the true plane of the polygon due to numerical noise. Then all
* the triangles would appear to be degenerate and we would incorrectly
* decompose the polygon as a fan (or simply not render it at all).
*
* We use a sum-of-triangles normal algorithm rather than the more
* efficient sum-of-trapezoids method (used in CheckOrientation()
* in normal.c). This lets us explicitly reverse the signed area
* of some triangles to get a reasonable normal in the self-intersecting
* case.
*/
if (!check) {
norm[0] = norm[1] = norm[2] = 0.0;
}
vc = v0 + 1;
xc = vc->coords[0] - v0->coords[0];
yc = vc->coords[1] - v0->coords[1];
zc = vc->coords[2] - v0->coords[2];
while( ++vc < vn ) {
xp = xc; yp = yc; zp = zc;
xc = vc->coords[0] - v0->coords[0];
yc = vc->coords[1] - v0->coords[1];
zc = vc->coords[2] - v0->coords[2];
vc = v0 + 1;
xc = vc->coords[0] - v0->coords[0];
yc = vc->coords[1] - v0->coords[1];
zc = vc->coords[2] - v0->coords[2];
while (++vc < vn) {
xp = xc;
yp = yc;
zp = zc;
xc = vc->coords[0] - v0->coords[0];
yc = vc->coords[1] - v0->coords[1];
zc = vc->coords[2] - v0->coords[2];
/* Compute (vp - v0) cross (vc - v0) */
n[0] = yp*zc - zp*yc;
n[1] = zp*xc - xp*zc;
n[2] = xp*yc - yp*xc;
/* Compute (vp - v0) cross (vc - v0) */
n[0] = yp * zc - zp * yc;
n[1] = zp * xc - xp * zc;
n[2] = xp * yc - yp * xc;
dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2];
if( ! check ) {
/* Reverse the contribution of back-facing triangles to get
* a reasonable normal for self-intersecting polygons (see above)
*/
if( dot >= 0 ) {
norm[0] += n[0]; norm[1] += n[1]; norm[2] += n[2];
} else {
norm[0] -= n[0]; norm[1] -= n[1]; norm[2] -= n[2];
dot = n[0] * norm[0] + n[1] * norm[1] + n[2] * norm[2];
if (!check) {
/* Reverse the contribution of back-facing triangles to get
* a reasonable normal for self-intersecting polygons (see above)
*/
if (dot >= 0) {
norm[0] += n[0];
norm[1] += n[1];
norm[2] += n[2];
}
else {
norm[0] -= n[0];
norm[1] -= n[1];
norm[2] -= n[2];
}
}
} else if( dot != 0 ) {
/* Check the new orientation for consistency with previous triangles */
if( dot > 0 ) {
if( sign < 0 ) return SIGN_INCONSISTENT;
sign = 1;
} else {
if( sign > 0 ) return SIGN_INCONSISTENT;
sign = -1;
else if (dot != 0) {
/* Check the new orientation for consistency with previous triangles */
if (dot > 0) {
if (sign < 0)
return SIGN_INCONSISTENT;
sign = 1;
}
else {
if (sign > 0)
return SIGN_INCONSISTENT;
sign = -1;
}
}
}
}
return sign;
}
//printf("%f, %f, %f -- %d\n", norm[0], norm[1], norm[2], sign);
return sign;
}
/* __gl_renderCache( tess ) takes a single contour and tries to render it
@ -438,65 +462,68 @@ static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check )
* Returns TRUE if the polygon was successfully rendered. The rendering
* output is provided as callbacks (see the api).
*/
GLboolean __gl_renderCache( GLUtesselator *tess )
GLboolean __gl_renderCache(GLUtesselator *tess)
{
CachedVertex *v0 = tess->cache;
CachedVertex *vn = v0 + tess->cacheCount;
CachedVertex *vc;
GLdouble norm[3];
int sign;
CachedVertex *v0 = tess->cache;
CachedVertex *vn = v0 + tess->cacheCount;
CachedVertex *vc;
GLdouble norm[3];
int sign;
if( tess->cacheCount < 3 ) {
/* Degenerate contour -- no output */
return TRUE;
}
if (tess->cacheCount < 3) {
/* Degenerate contour -- no output */
return TRUE;
}
norm[0] = tess->normal[0];
norm[1] = tess->normal[1];
norm[2] = tess->normal[2];
if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
ComputeNormal( tess, norm, FALSE );
}
norm[0] = tess->normal[0];
norm[1] = tess->normal[1];
norm[2] = tess->normal[2];
if (norm[0] == 0 && norm[1] == 0 && norm[2] == 0) {
ComputeNormal(tess, norm, FALSE);
}
sign = ComputeNormal( tess, norm, TRUE );
if( sign == SIGN_INCONSISTENT ) {
/* Fan triangles did not have a consistent orientation */
return FALSE;
}
if( sign == 0 ) {
/* All triangles were degenerate */
return TRUE;
}
sign = ComputeNormal(tess, norm, TRUE);
if (sign == SIGN_INCONSISTENT) {
/* Fan triangles did not have a consistent orientation */
return FALSE;
}
if (sign == 0) {
/* All triangles were degenerate */
return TRUE;
}
/* Make sure we do the right thing for each winding rule */
switch( tess->windingRule ) {
case GLU_TESS_WINDING_ODD:
case GLU_TESS_WINDING_NONZERO:
break;
case GLU_TESS_WINDING_POSITIVE:
if( sign < 0 ) return TRUE;
break;
case GLU_TESS_WINDING_NEGATIVE:
if( sign > 0 ) return TRUE;
break;
case GLU_TESS_WINDING_ABS_GEQ_TWO:
return TRUE;
}
/* Make sure we do the right thing for each winding rule */
switch (tess->windingRule) {
case GLU_TESS_WINDING_ODD:
case GLU_TESS_WINDING_NONZERO:
break;
case GLU_TESS_WINDING_POSITIVE:
if (sign < 0)
return TRUE;
break;
case GLU_TESS_WINDING_NEGATIVE:
if (sign > 0)
return TRUE;
break;
case GLU_TESS_WINDING_ABS_GEQ_TWO:
return TRUE;
}
CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP
: (tess->cacheCount > 3) ? GL_TRIANGLE_FAN
: GL_TRIANGLES );
CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP
: (tess->cacheCount > 3) ? GL_TRIANGLE_FAN
: GL_TRIANGLES);
CALL_VERTEX_OR_VERTEX_DATA( v0->data );
if( sign > 0 ) {
for( vc = v0+1; vc < vn; ++vc ) {
CALL_VERTEX_OR_VERTEX_DATA( vc->data );
}
} else {
for( vc = vn-1; vc > v0; --vc ) {
CALL_VERTEX_OR_VERTEX_DATA( vc->data );
}
}
CALL_END_OR_END_DATA();
return TRUE;
CALL_VERTEX_OR_VERTEX_DATA( v0->data);
if (sign > 0) {
for (vc = v0 + 1; vc < vn; ++vc) {
CALL_VERTEX_OR_VERTEX_DATA( vc->data);
}
}
else {
for (vc = vn - 1; vc > v0; --vc) {
CALL_VERTEX_OR_VERTEX_DATA( vc->data);
}
}
CALL_END_OR_END_DATA();
return TRUE;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,44 +1,12 @@
#include <jni.h>
#include <string.h>
#include "glu.h"
#include "tess.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#ifdef __ANDROID__
#include <android/log.h>
#endif
#include "tessellate.h"
//#ifndef uintptr_t
//typedef unsigned long uintptr_t;
//#endif
/******************************************************************************/
typedef struct Triangle {
int v[3];
struct Triangle *prev;
} Triangle;
typedef struct Vertex {
double pt[3];
int index;
struct Vertex *prev;
} Vertex;
typedef struct TessContext {
Triangle *latest_t;
int n_tris;
Vertex *v_prev;
Vertex *v_prevprev;
Vertex *latest_v;
GLenum current_mode;
int odd_even_strip;
void (*vertex_cb)(Vertex *, struct TessContext *);
} TessContext;
void skip_vertex(Vertex *v, TessContext *ctx);
@ -212,8 +180,37 @@ void write_output(TessContext *ctx, float **coordinates_out, int **tris_out, int
}
}
void write_outputD(TessContext *ctx, double **coordinates_out, int **tris_out, int *vc, int *tc)
{
int n_verts = 1 + ctx->latest_v->index;
*vc = n_verts;
int n_tris_copy = ctx->n_tris;
*tc = ctx->n_tris;
*coordinates_out = malloc(n_verts * sizeof(double) * 2);
*tris_out = (ctx->n_tris ? malloc(ctx->n_tris * sizeof(int) * 3) : NULL);
while (ctx->latest_v) {
(*coordinates_out)[2 * ctx->latest_v->index] = ctx->latest_v->pt[0];
(*coordinates_out)[2 * ctx->latest_v->index + 1] = ctx->latest_v->pt[1];
Vertex *prev = ctx->latest_v->prev;
free(ctx->latest_v);
ctx->latest_v = prev;
}
while (ctx->latest_t) {
(*tris_out)[3 * (n_tris_copy - 1)] = ctx->latest_t->v[0];
(*tris_out)[3 * (n_tris_copy - 1) + 1] = ctx->latest_t->v[1];
(*tris_out)[3 * (n_tris_copy - 1) + 2] = ctx->latest_t->v[2];
Triangle *prev = ctx->latest_t->prev;
free(ctx->latest_t);
ctx->latest_t = prev;
n_tris_copy--;
}
}
TessContext *tessellate(
float **verts,
int *nverts,
int **tris,
int *ntris,
const float **contoursbegin,
const float **contoursend)
@ -231,6 +228,7 @@ TessContext *tessellate(
gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (GLvoid (*)()) &combine);
gluTessBeginPolygon(tess, ctx);
do {
contourbegin = *contoursbegin++;
contourend = *contoursbegin;
@ -244,16 +242,20 @@ TessContext *tessellate(
} while (contoursbegin != (contoursend - 1));
gluTessEndPolygon(tess);
//write_output(ctx, verts, tris, nverts, ntris);
//destroy_tess_context(ctx);
#ifdef TEST
write_output(ctx, verts, tris, nverts, ntris);
destroy_tess_context(ctx);
#else
gluDeleteTess(tess);
#endif
return ctx;
}
TessContext *tessellateD(
double **verts,
int *nverts,
int **tris,
int *ntris,
const double **contoursbegin,
const double **contoursend)
@ -266,231 +268,40 @@ TessContext *tessellateD(
tess = gluNewTess();
ctx = new_tess_context();
//tess->normal[2] = -1;
gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (GLvoid (*)()) &vertex);
gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (GLvoid (*)()) &begin);
gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (GLvoid (*)()) &combine);
gluTessBeginPolygon(tess, ctx);
do {
//printf("begin contour\n");
contourbegin = *contoursbegin++;
contourend = *contoursbegin;
gluTessBeginContour(tess);
while (contourbegin != contourend) {
//printf("add point %f %f \n", contourbegin[0], contourbegin[1]);
current_vertex = new_vertex(ctx, contourbegin[0], contourbegin[1]);
contourbegin += 2;
gluTessVertex(tess, current_vertex->pt, current_vertex);
}
gluTessEndContour(tess);
} while (contoursbegin != (contoursend - 1));
gluTessEndPolygon(tess);
//write_output(ctx, verts, tris, nverts, ntris);
//destroy_tess_context(ctx);
#ifdef TEST
write_outputD(ctx, verts, tris, nverts, ntris);
destroy_tess_context(ctx);
#else
gluDeleteTess(tess);
#endif
return ctx;
}
#ifdef __ANDROID__
#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "Tesselate", __VA_ARGS__)
#endif
#define CAST_CTX(x) (TessContext *)(uintptr_t) x
void Java_org_oscim_renderer_sublayers_MeshLayer_tessFinish(JNIEnv *env, jclass c,
jlong ptr_context) {
TessContext *ctx = CAST_CTX(ptr_context);
while (ctx->latest_v) {
Vertex *prev = ctx->latest_v->prev;
free(ctx->latest_v);
ctx->latest_v = prev;
}
while (ctx->latest_t) {
Triangle *prev = ctx->latest_t->prev;
free(ctx->latest_t);
ctx->latest_t = prev;
}
destroy_tess_context(ctx);
}
jint Java_org_oscim_renderer_sublayers_MeshLayer_tessGetCoordinates(JNIEnv *env, jclass c,
jlong ptr_context, jshortArray obj_coords, jfloat scale) {
TessContext *ctx = CAST_CTX(ptr_context);
int length = (*env)->GetArrayLength(env, obj_coords);
jshort* coords = (jshort*) (*env)->GetPrimitiveArrayCritical(env, obj_coords, 0);
if (coords == NULL) {
return 0;
}
//int n_verts = 1 + ctx->latest_v->index;
//int n_tris_copy = ctx->n_tris;
int cnt = 0;
for (; ctx->latest_v && cnt < length; cnt += 2) {
coords[cnt + 0] = (ctx->latest_v->pt[0] * scale) + 0.5f;
coords[cnt + 1] = (ctx->latest_v->pt[1] * scale) + 0.5f;
Vertex *prev = ctx->latest_v->prev;
free(ctx->latest_v);
ctx->latest_v = prev;
}
(*env)->ReleasePrimitiveArrayCritical(env, obj_coords, coords, JNI_ABORT);
return cnt;
}
jint Java_org_oscim_renderer_sublayers_MeshLayer_tessGetCoordinatesD(JNIEnv *env, jclass c,
jlong ptr_context, jdoubleArray obj_coords) {
TessContext *ctx = CAST_CTX(ptr_context);
int length = (*env)->GetArrayLength(env, obj_coords);
jdouble* coords = (jdouble*) (*env)->GetPrimitiveArrayCritical(env, obj_coords, 0);
if (coords == NULL) {
return 0;
}
//int n_verts = 1 + ctx->latest_v->index;
//int n_tris_copy = ctx->n_tris;
int cnt = 0;
for (; ctx->latest_v && cnt < length; cnt += 2) {
coords[cnt + 0] = ctx->latest_v->pt[0];
coords[cnt + 1] = ctx->latest_v->pt[1];
Vertex *prev = ctx->latest_v->prev;
free(ctx->latest_v);
ctx->latest_v = prev;
}
(*env)->ReleasePrimitiveArrayCritical(env, obj_coords, coords, JNI_ABORT);
return cnt;
}
jint Java_org_oscim_renderer_sublayers_MeshLayer_tessGetIndices(JNIEnv *env, jclass c,
jlong ptr_context, jshortArray obj_indices) {
TessContext *ctx = CAST_CTX(ptr_context);
int length = (*env)->GetArrayLength(env, obj_indices);
jshort* tris = (jshort*) (*env)->GetPrimitiveArrayCritical(env, obj_indices, 0);
if (tris == NULL) {
return 0;
}
int n_tris_copy = ctx->n_tris;
int cnt = 0;
for (; ctx->latest_t && cnt < length; cnt += 3) {
tris[cnt + 0] = ctx->latest_t->v[0];
tris[cnt + 1] = ctx->latest_t->v[1];
tris[cnt + 2] = ctx->latest_t->v[2];
Triangle *prev = ctx->latest_t->prev;
free(ctx->latest_t);
ctx->latest_t = prev;
n_tris_copy--;
}
ctx->n_tris = n_tris_copy;
(*env)->ReleasePrimitiveArrayCritical(env, obj_indices, tris, JNI_ABORT);
return cnt;
}
jlong Java_org_oscim_renderer_sublayers_MeshLayer_tessellate(JNIEnv *env, jclass c,
jfloatArray obj_points, jint pos,
jshortArray obj_index, jint ipos,
jint num_rings) { //, jintArray obj_out) {
jboolean isCopy;
printf("add %d %d %d\n", pos, ipos, num_rings);
float* orig_points = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_points, &isCopy);
if (orig_points == NULL)
return 0;
const float *points = orig_points + pos;
jshort* orig_indices = (jshort*) (*env)->GetPrimitiveArrayCritical(env, obj_index, &isCopy);
if (orig_indices == NULL) {
(*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT);
return 0;
}
jshort* indices = orig_indices + ipos;
const float **rings = malloc(sizeof(float*) * (num_rings + 1));
int offset = 0;
for (int i = 0; i < num_rings; i++) {
rings[i] = points + offset;
offset += indices[i];
}
rings[num_rings] = points + offset;
int nverts, ntris;
TessContext *ctx = tessellate(&nverts, &ntris,
rings, rings + (num_rings + 1));
free(rings);
(*env)->ReleasePrimitiveArrayCritical(env, obj_index, orig_indices, JNI_ABORT);
(*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT);
return (long) ctx;
}
jlong Java_org_oscim_renderer_sublayers_MeshLayer_tessellateD(JNIEnv *env, jclass c,
jdoubleArray obj_points, jint pos,
jshortArray obj_index, jint ipos,
jint num_rings) { //, jintArray obj_out) {
jboolean isCopy;
printf("add %d %d %d\n", pos, ipos, num_rings);
double* orig_points = (double*) (*env)->GetPrimitiveArrayCritical(env, obj_points, &isCopy);
if (orig_points == NULL)
return 0;
const double *points = orig_points + pos;
jshort* orig_indices = (jshort*) (*env)->GetPrimitiveArrayCritical(env, obj_index, &isCopy);
if (orig_indices == NULL) {
(*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT);
return 0;
}
jshort* indices = orig_indices + ipos;
const double **rings = malloc(sizeof(double*) * (num_rings + 1));
int offset = 0;
for (int i = 0; i < num_rings; i++) {
rings[i] = points + offset;
offset += indices[i];
}
rings[num_rings] = points + offset;
int nverts, ntris;
TessContext *ctx = tessellateD(&nverts, &ntris,
rings, rings + (num_rings + 1));
free(rings);
(*env)->ReleasePrimitiveArrayCritical(env, obj_index, orig_indices, JNI_ABORT);
(*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT);
return (long) ctx;
}

View File

@ -1,13 +1,41 @@
#include "glu.h"
typedef struct Triangle {
int v[3];
struct Triangle *prev;
} Triangle;
typedef struct Vertex {
double pt[3];
int index;
struct Vertex *prev;
} Vertex;
//void tessellate
// (double **verts,
// int *nverts,
// int **tris,
// int *ntris,
// const float **contoursbegin,
// const float **contoursend);
typedef struct TessContext {
Triangle *latest_t;
int n_tris;
Vertex *v_prev;
Vertex *v_prevprev;
Vertex *latest_v;
GLenum current_mode;
int odd_even_strip;
void (*vertex_cb)(Vertex *, struct TessContext *);
} TessContext;
TessContext *tessellateD
(double **verts,
int *nverts,
int **tris,
int *ntris,
const double **contoursbegin,
const double **contoursend);
TessContext *tessellate
(float **verts,
int *nverts,
int **tris,
int *ntris,
const float **contoursbegin,
const float **contoursend);

View File

@ -1,198 +0,0 @@
Triangle
A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator.
Version 1.6
Show Me
A Display Program for Meshes and More.
Version 1.6
Copyright 1993, 1995, 1997, 1998, 2002, 2005 Jonathan Richard Shewchuk
2360 Woolsey #H
Berkeley, California 94705-1927
Please send bugs and comments to jrs@cs.berkeley.edu
Created as part of the Quake project (tools for earthquake simulation).
Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.
There is no warranty whatsoever. Use at your own risk.
Triangle generates exact Delaunay triangulations, constrained Delaunay
triangulations, conforming Delaunay triangulations, Voronoi diagrams, and
high-quality triangular meshes. The latter can be generated with no small
or large angles, and are thus suitable for finite element analysis.
Show Me graphically displays the contents of the geometric files used by
Triangle. Show Me can also write images in PostScript form.
Information on the algorithms used by Triangle, including complete
references, can be found in the comments at the beginning of the triangle.c
source file. Another listing of these references, with PostScript copies
of some of the papers, is available from the Web page
http://www.cs.cmu.edu/~quake/triangle.research.html
------------------------------------------------------------------------------
These programs may be freely redistributed under the condition that the
copyright notices (including the copy of this notice in the code comments
and the copyright notice printed when the `-h' switch is selected) are
not removed, and no compensation is received. Private, research, and
institutional use is free. You may distribute modified versions of this
code UNDER THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT
IN THE SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH
SOURCE AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND
CLEAR NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as
part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT
WITH THE AUTHOR. (If you are not directly supplying this code to a
customer, and you are instead telling them how they can obtain it for
free, then you are not required to make any arrangement with me.)
------------------------------------------------------------------------------
The files included in this distribution are:
README The file you're reading now.
triangle.c Complete C source code for Triangle.
showme.c Complete C source code for Show Me.
triangle.h Include file for calling Triangle from another program.
tricall.c Sample program that calls Triangle.
makefile Makefile for compiling Triangle and Show Me.
A.poly A sample input file.
Each of Triangle and Show Me is a single portable C file. The easiest way
to compile them is to edit and use the included makefile. Before
compiling, read the makefile, which describes your options, and edit it
accordingly. You should specify:
The source and binary directories.
The C compiler and level of optimization.
The "correct" directories for include files (especially X include files),
if necessary.
Do you want single precision or double? (The default is double.) Do you
want to leave out some of Triangle's features to reduce the size of the
executable file? Investigate the SINGLE, REDUCED, and CDT_ONLY symbols.
If yours is not a Unix system, define the NO_TIMER symbol to remove the
Unix-specific timing code. Also, don't try to compile Show Me; it only
works with X Windows.
If you are compiling on an Intel x86 CPU and using gcc w/Linux or
Microsoft C, be sure to define the LINUX or CPU86 (for Microsoft) symbol
during compilation so that the exact arithmetic works right.
Once you've done this, type "make" to compile the programs. Alternatively,
the files are usually easy to compile without a makefile:
cc -O -o triangle triangle.c -lm
cc -O -o showme showme.c -lX11
On some systems, the C compiler won't be able to find the X include files
or libraries, and you'll need to specify an include path or library path:
cc -O -I/usr/local/include -o showme showme.c -L/usr/local/lib -lX11
Some processors, including Intel x86 family and possibly Motorola 68xxx
family chips, are IEEE conformant but have extended length internal
floating-point registers that may defeat Triangle's exact arithmetic
routines by failing to cause enough roundoff error! Typically, there is a
way to set these internal registers so that they are rounded off to IEEE
single or double precision format. I believe (but I'm not certain) that
Triangle has the right incantations for x86 chips, if you have gcc running
under Linux (define the LINUX compiler symbol) or Microsoft C (define the
CPU86 compiler symbol).
If you have a different processor or operating system, or if I got the
incantations wrong, you should check your C compiler or system manuals to
find out how to configure these internal registers to the precision you are
using. Otherwise, the exact arithmetic routines won't be exact at all.
See http://www.cs.cmu.edu/~quake/robust.pc.html for details. Triangle's
exact arithmetic hasn't a hope of working on machines like the Cray C90 or
Y-MP, which are not IEEE conformant and have inaccurate rounding.
Triangle and Show Me have both text and HTML documentation. The latter is
illustrated. Find it on the Web at
http://www.cs.cmu.edu/~quake/triangle.html
http://www.cs.cmu.edu/~quake/showme.html
Complete text instructions are printed by invoking each program with the
`-h' switch:
triangle -h
showme -h
The instructions are long; you'll probably want to pipe the output to
`more' or `lpr' or redirect it to a file.
Both programs give a short list of command line options if they are invoked
without arguments (that is, just type `triangle' or `showme').
Try out Triangle on the enclosed sample file, A.poly:
triangle -p A
showme A.poly &
Triangle will read the Planar Straight Line Graph defined by A.poly, and
write its constrained Delaunay triangulation to A.1.node and A.1.ele.
Show Me will display the figure defined by A.poly. There are two buttons
marked "ele" in the Show Me window; click on the top one. This will cause
Show Me to load and display the triangulation.
For contrast, try running
triangle -pq A
Now, click on the same "ele" button. A new triangulation will be loaded;
this one having no angles smaller than 20 degrees.
To see a Voronoi diagram, try this:
cp A.poly A.node
triangle -v A
Click the "ele" button again. You will see the Delaunay triangulation of
the points in A.poly, without the segments. Now click the top "voro" button.
You will see the Voronoi diagram corresponding to that Delaunay triangulation.
Click the "Reset" button to see the full extent of the diagram.
------------------------------------------------------------------------------
If you wish to call Triangle from another program, instructions for doing
so are contained in the file `triangle.h' (but read Triangle's regular
instructions first!). Also look at `tricall.c', which provides an example
of how to call Triangle.
Type "make trilibrary" to create triangle.o, a callable object file.
Alternatively, the object file is usually easy to compile without a
makefile:
cc -DTRILIBRARY -O -c triangle.c
Type "make distclean" to remove all the object and executable files created
by make.
------------------------------------------------------------------------------
If you use Triangle, and especially if you use it to accomplish real work,
I would like very much to hear from you. A short letter or email (to
jrs@cs.berkeley.edu) describing how you use Triangle will mean a lot to me.
The more people I know are using this program, the more easily I can
justify spending time on improvements and on the three-dimensional
successor to Triangle, which in turn will benefit you. Also, I can put you
on a list to receive email whenever a new version of Triangle is available.
If you use a mesh generated by Triangle or plotted by Show Me in a
publication, please include an acknowledgment as well. And please spell
Triangle with a capital `T'! If you want to include a citation, use
`Jonathan Richard Shewchuk, ``Triangle: Engineering a 2D Quality Mesh
Generator and Delaunay Triangulator,'' in Applied Computational Geometry:
Towards Geometric Engineering (Ming C. Lin and Dinesh Manocha, editors),
volume 1148 of Lecture Notes in Computer Science, pages 203-222,
Springer-Verlag, Berlin, May 1996. (From the First ACM Workshop on Applied
Computational Geometry.)'
Jonathan Richard Shewchuk
July 27, 2005

View File

@ -1,308 +0,0 @@
#include <jni.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "triangle.h"
#ifdef __ANDROID__
#include <android/log.h>
#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "Triangle", __VA_ARGS__)
#endif
// from www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
#if 0
int pnpoly(int nvert, float *vert, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = (nvert-1)*2; i < nvert * 2; j = i++)
{
if ( ((vert[i*2+1] > testy) != (vert[j*j+1] > testy)) &&
(testx < (vert[j*2]-vert[i*2])
* (testy - vert[i*2+1])
/ (vert[j*2+1]-vert[i*2+1]) + vert[i*2]) )
c = !c;
}
return c;
}
int compare_dups(const void *a, const void *b) {
int da = *((const long*) a);
int db = *((const long*) b);
return (da > db) - (da < db);
}
void shiftSegment(TriangleIO *in, int *seg, int pos) {
int size = (in->numberofsegments - pos - 1) * sizeof(int) * 2;
printf("shift %d - %d %d\n", size, in->numberofsegments, pos);
if (size > 0)
memmove(seg, seg + 2, size);
in->numberofsegments -= 1;
}
struct {
int p1;
int p2;
} segment;
#endif
static void printPoly(TriangleIO *in) {
// print poly format to check with triangle/showme
printf("%d 2 0 0\n", in->numberofpoints);
for (int j = 0; j < in->numberofpoints; j++)
printf("%d %f %f\n", j, in->pointlist[j*2], in->pointlist[j*2+1]);
int *seg = in->segmentlist;
printf("%d 0\n", in->numberofsegments);
for (int j = 0; j < in->numberofsegments; j++, seg += 2)
printf("%d %d %d\n", j, *seg, *(seg+1));
printf("%d 0\n", in->numberofholes);
for (int j = 0; j < in->numberofholes; j++) {
printf("%d %f %f\n", j, in->holelist[j*2], in->holelist[j*2+1]);
}
}
jint Java_org_oscim_utils_geom_Triangulator_triangulate(JNIEnv *env, jclass c,
jfloatArray obj_points, jint pos, jint len, jint num_rings, jobject indice_buf, jint offset) {
jshort* indices = (jshort*) (*env)->GetDirectBufferAddress(env, indice_buf);
jboolean isCopy;
float* orig_points = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_points, &isCopy);
if (orig_points == NULL)
return 0;
float *points = orig_points + pos;
TriangleIO in, out;
memset(&in, 0, sizeof(TriangleIO));
in.numberofpoints = len >> 1;
in.pointlist = (float *) points;
// check if explicitly closed
if (in.pointlist[0] == in.pointlist[indices[0] - 2]
&& in.pointlist[1] == in.pointlist[indices[0] - 1]) {
int point = 0;
for (int i = 0; i < num_rings; i++) {
// remove last point in ring
indices[i] -= 2;
int last = point + (indices[i] >> 1);
if (in.numberofpoints - last > 1)
memmove(in.pointlist + (last * 2), in.pointlist + ((last + 1) * 2),
(in.numberofpoints - last - 1) * 2 * sizeof(float));
in.numberofpoints--;
point = last;
}
}
int dups = 0;
float *i_points = points;
int *skip_list = NULL;
// check for duplicate vertices and keep a list
// of dups and the first occurence
for (int i = 0; i < in.numberofpoints - 1; i++) {
float x = *i_points++;
float y = *i_points++;
float *j_points = i_points;
for (int j = i + 1; j < in.numberofpoints; j++, j_points += 2) {
if ((*j_points == x) && (*(j_points + 1) == y)) {
skip_list = realloc(skip_list, (dups + 2) * 2 * sizeof(int));
skip_list[dups * 2 + 0] = j;
skip_list[dups * 2 + 1] = i;
dups++;
}
}
}
in.segmentlist = (int *) malloc(in.numberofpoints * 2 * sizeof(int));
in.numberofsegments = in.numberofpoints;
in.numberofholes = num_rings - 1;
int *rings = NULL;
if (in.numberofholes > 0) {
in.holelist = (double *) malloc(in.numberofholes * 2 * sizeof(double));
rings = (int*) malloc(num_rings * sizeof(int));
}
int *seg = in.segmentlist;
double *hole = in.holelist;
// counter going through all points
int point;
// counter going through all rings
int ring;
// assign all points to segments for each ring
for (ring = 0, point = 0; ring < num_rings; ring++, point++) {
int len;
int num_points = indices[ring] >> 1;
if (rings)
rings[ring] = num_points;
// add holes: we need a point inside the hole...
// this is just a heuristic, assuming that two
// 'parallel' lines have a distance of at least
// 1 unit. you'll notice when things went wrong
// when the hole is rendered instead of the poly
if (ring > 0) {
int k = point * 2;
float nx = in.pointlist[k++];
float ny = in.pointlist[k++];
float cx = 0, cy = 0, vx = 0, vy = 0;
// try to find a large enough segment
for (len = (point + num_points) * 2; k < len;) {
cx = nx;
cy = ny;
nx = in.pointlist[k++];
ny = in.pointlist[k++];
vx = nx - cx;
vy = ny - cy;
if (vx > 4 || vx < -4 || vy > 4 || vy < -4)
break;
}
float a = sqrt(vx * vx + vy * vy);
float ux = -vy / a;
float uy = vx / a;
double centerx = cx + vx / 2.0 - (ux * 0.1);
double centery = cy + vy / 2.0 - (uy * 0.1);
*hole++ = centerx;
*hole++ = centery;
}
// close ring
int last = point + (num_points - 1);
*seg++ = last;
*seg++ = point;
for (len = point + num_points - 1; point < len; point++) {
*seg++ = point;
*seg++ = point + 1;
}
}
if (dups) {
for (int i = 0; i < dups; i++) {
printf("duplicate points at %d, %d: %f,%f\n",
skip_list[i*2], skip_list[i*2+1],
in.pointlist[skip_list[i*2+1]*2],
in.pointlist[skip_list[i*2+1]*2+1]);
}
printPoly(&in);
// replace duplicate positions with first occurence
for (int i = 0; i < dups; i++) {
// position of the duplicate vertex
int pos = skip_list[i * 2] - i;
// first vertex
int replacement = skip_list[i * 2 + 1];
seg = in.segmentlist;
for (int j = 0; j < in.numberofsegments * 2; j++, seg++) {
if (*seg == pos) {
printf("%d: %d <- %d", j, pos, replacement);
*seg = replacement;
}
}
}
}
memset(&out, 0, sizeof(TriangleIO));
out.trianglelist = (INDICE*) indices;
// p - use polygon input, for CDT
// z - zero offset array offsets...
// P - no poly output
// N - no node output
// B - no bound output
// Q - be quiet!
TriangleOptions opt;
memset(&opt, 0, sizeof(TriangleOptions));
opt.dwyer = 1;
opt.steiner = -1;
opt.order = 1;
opt.maxarea = -1.0;
opt.poly = 1;
opt.usesegments = 1;
opt.nopolywritten = 1;
opt.nonodewritten = 1;
opt.nobound = 1;
opt.quiet = 1;
triangulate(&opt, &in, &out, (TriangleIO *) NULL);
if (in.numberofpoints < out.numberofpoints) {
// TODO rerun with 'nonodewritten = 0'
printf( "polygon input is bad! points in:%d out%d\n", in.numberofpoints, out.numberofpoints);
out.numberoftriangles = 0;
}
else if (out.trianglelist)
{
// scale to stride and add offset
short stride = 2;
if (offset < 0)
offset = 0;
INDICE *tri = out.trianglelist;
for (int n = out.numberoftriangles * 3; n > 0; n--)
*tri++ = *tri * stride + offset;
// when a ring has an odd number of points one (or rather two)
// additional vertices will be added. so the following rings
// needs extra offset...
int start = offset;
for (int j = 0, m = in.numberofholes; j < m; j++) {
start += rings[j] * stride;
// even number of points?
if (!(rings[j] & 1))
continue;
tri = out.trianglelist;
int n = out.numberoftriangles * 3;
for (; n-- > 0; tri++)
if (*tri >= start)
*tri += stride;
start += stride;
}
}
else
{
printf( "triangle failed %d\n", out.numberofpoints);
}
(*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT);
free(in.segmentlist);
free(in.holelist);
free(rings);
free(skip_list);
return out.numberoftriangles;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,362 +0,0 @@
/*****************************************************************************/
/* */
/* (triangle.h) */
/* */
/* Include file for programs that call Triangle. */
/* */
/* Accompanies Triangle Version 1.6 */
/* July 28, 2005 */
/* */
/* Copyright 1996, 2005 */
/* Jonathan Richard Shewchuk */
/* 2360 Woolsey #H */
/* Berkeley, California 94705-1927 */
/* jrs@cs.berkeley.edu */
/* */
/*****************************************************************************/
/*****************************************************************************/
/* */
/* How to call Triangle from another program */
/* */
/* */
/* If you haven't read Triangle's instructions (run "triangle -h" to read */
/* them), you won't understand what follows. */
/* */
/* Triangle must be compiled into an object file (triangle.o) with the */
/* TRILIBRARY symbol defined (generally by using the -DTRILIBRARY compiler */
/* switch). The makefile included with Triangle will do this for you if */
/* you run "make trilibrary". The resulting object file can be called via */
/* the procedure triangulate(). */
/* */
/* If the size of the object file is important to you, you may wish to */
/* generate a reduced version of triangle.o. The REDUCED symbol gets rid */
/* of all features that are primarily of research interest. Specifically, */
/* the -DREDUCED switch eliminates Triangle's -i, -F, -s, and -C switches. */
/* The CDT_ONLY symbol gets rid of all meshing algorithms above and beyond */
/* constrained Delaunay triangulation. Specifically, the -DCDT_ONLY switch */
/* eliminates Triangle's -r, -q, -a, -u, -D, -Y, -S, and -s switches. */
/* */
/* IMPORTANT: These definitions (TRILIBRARY, REDUCED, CDT_ONLY) must be */
/* made in the makefile or in triangle.c itself. Putting these definitions */
/* in this file (triangle.h) will not create the desired effect. */
/* */
/* */
/* The calling convention for triangulate() follows. */
/* */
/* void triangulate(triswitches, in, out, vorout) */
/* char *triswitches; */
/* struct triangulateio *in; */
/* struct triangulateio *out; */
/* struct triangulateio *vorout; */
/* */
/* `triswitches' is a string containing the command line switches you wish */
/* to invoke. No initial dash is required. Some suggestions: */
/* */
/* - You'll probably find it convenient to use the `z' switch so that */
/* points (and other items) are numbered from zero. This simplifies */
/* indexing, because the first item of any type always starts at index */
/* [0] of the corresponding array, whether that item's number is zero or */
/* one. */
/* - You'll probably want to use the `Q' (quiet) switch in your final code, */
/* but you can take advantage of Triangle's printed output (including the */
/* `V' switch) while debugging. */
/* - If you are not using the `q', `a', `u', `D', `j', or `s' switches, */
/* then the output points will be identical to the input points, except */
/* possibly for the boundary markers. If you don't need the boundary */
/* markers, you should use the `N' (no nodes output) switch to save */
/* memory. (If you do need boundary markers, but need to save memory, a */
/* good nasty trick is to set out->pointlist equal to in->pointlist */
/* before calling triangulate(), so that Triangle overwrites the input */
/* points with identical copies.) */
/* - The `I' (no iteration numbers) and `g' (.off file output) switches */
/* have no effect when Triangle is compiled with TRILIBRARY defined. */
/* */
/* `in', `out', and `vorout' are descriptions of the input, the output, */
/* and the Voronoi output. If the `v' (Voronoi output) switch is not used, */
/* `vorout' may be NULL. `in' and `out' may never be NULL. */
/* */
/* Certain fields of the input and output structures must be initialized, */
/* as described below. */
/* */
/*****************************************************************************/
/*****************************************************************************/
/* */
/* The `triangulateio' structure. */
/* */
/* Used to pass data into and out of the triangulate() procedure. */
/* */
/* */
/* Arrays are used to store points, triangles, markers, and so forth. In */
/* all cases, the first item in any array is stored starting at index [0]. */
/* However, that item is item number `1' unless the `z' switch is used, in */
/* which case it is item number `0'. Hence, you may find it easier to */
/* index points (and triangles in the neighbor list) if you use the `z' */
/* switch. Unless, of course, you're calling Triangle from a Fortran */
/* program. */
/* */
/* Description of fields (except the `numberof' fields, which are obvious): */
/* */
/* `pointlist': An array of point coordinates. The first point's x */
/* coordinate is at index [0] and its y coordinate at index [1], followed */
/* by the coordinates of the remaining points. Each point occupies two */
/* REALs. */
/* `pointattributelist': An array of point attributes. Each point's */
/* attributes occupy `numberofpointattributes' REALs. */
/* `pointmarkerlist': An array of point markers; one int per point. */
/* */
/* `trianglelist': An array of triangle corners. The first triangle's */
/* first corner is at index [0], followed by its other two corners in */
/* counterclockwise order, followed by any other nodes if the triangle */
/* represents a nonlinear element. Each triangle occupies */
/* `numberofcorners' ints. */
/* `triangleattributelist': An array of triangle attributes. Each */
/* triangle's attributes occupy `numberoftriangleattributes' REALs. */
/* `trianglearealist': An array of triangle area constraints; one REAL per */
/* triangle. Input only. */
/* `neighborlist': An array of triangle neighbors; three ints per */
/* triangle. Output only. */
/* */
/* `segmentlist': An array of segment endpoints. The first segment's */
/* endpoints are at indices [0] and [1], followed by the remaining */
/* segments. Two ints per segment. */
/* `segmentmarkerlist': An array of segment markers; one int per segment. */
/* */
/* `holelist': An array of holes. The first hole's x and y coordinates */
/* are at indices [0] and [1], followed by the remaining holes. Two */
/* REALs per hole. Input only, although the pointer is copied to the */
/* output structure for your convenience. */
/* */
/* `regionlist': An array of regional attributes and area constraints. */
/* The first constraint's x and y coordinates are at indices [0] and [1], */
/* followed by the regional attribute at index [2], followed by the */
/* maximum area at index [3], followed by the remaining area constraints. */
/* Four REALs per area constraint. Note that each regional attribute is */
/* used only if you select the `A' switch, and each area constraint is */
/* used only if you select the `a' switch (with no number following), but */
/* omitting one of these switches does not change the memory layout. */
/* Input only, although the pointer is copied to the output structure for */
/* your convenience. */
/* */
/* `edgelist': An array of edge endpoints. The first edge's endpoints are */
/* at indices [0] and [1], followed by the remaining edges. Two ints per */
/* edge. Output only. */
/* `edgemarkerlist': An array of edge markers; one int per edge. Output */
/* only. */
/* `normlist': An array of normal vectors, used for infinite rays in */
/* Voronoi diagrams. The first normal vector's x and y magnitudes are */
/* at indices [0] and [1], followed by the remaining vectors. For each */
/* finite edge in a Voronoi diagram, the normal vector written is the */
/* zero vector. Two REALs per edge. Output only. */
/* */
/* */
/* Any input fields that Triangle will examine must be initialized. */
/* Furthermore, for each output array that Triangle will write to, you */
/* must either provide space by setting the appropriate pointer to point */
/* to the space you want the data written to, or you must initialize the */
/* pointer to NULL, which tells Triangle to allocate space for the results. */
/* The latter option is preferable, because Triangle always knows exactly */
/* how much space to allocate. The former option is provided mainly for */
/* people who need to call Triangle from Fortran code, though it also makes */
/* possible some nasty space-saving tricks, like writing the output to the */
/* same arrays as the input. */
/* */
/* Triangle will not free() any input or output arrays, including those it */
/* allocates itself; that's up to you. You should free arrays allocated by */
/* Triangle by calling the trifree() procedure defined below. (By default, */
/* trifree() just calls the standard free() library procedure, but */
/* applications that call triangulate() may replace trimalloc() and */
/* trifree() in triangle.c to use specialized memory allocators.) */
/* */
/* Here's a guide to help you decide which fields you must initialize */
/* before you call triangulate(). */
/* */
/* `in': */
/* */
/* - `pointlist' must always point to a list of points; `numberofpoints' */
/* and `numberofpointattributes' must be properly set. */
/* `pointmarkerlist' must either be set to NULL (in which case all */
/* markers default to zero), or must point to a list of markers. If */
/* `numberofpointattributes' is not zero, `pointattributelist' must */
/* point to a list of point attributes. */
/* - If the `r' switch is used, `trianglelist' must point to a list of */
/* triangles, and `numberoftriangles', `numberofcorners', and */
/* `numberoftriangleattributes' must be properly set. If */
/* `numberoftriangleattributes' is not zero, `triangleattributelist' */
/* must point to a list of triangle attributes. If the `a' switch is */
/* used (with no number following), `trianglearealist' must point to a */
/* list of triangle area constraints. `neighborlist' may be ignored. */
/* - If the `p' switch is used, `segmentlist' must point to a list of */
/* segments, `numberofsegments' must be properly set, and */
/* `segmentmarkerlist' must either be set to NULL (in which case all */
/* markers default to zero), or must point to a list of markers. */
/* - If the `p' switch is used without the `r' switch, then */
/* `numberofholes' and `numberofregions' must be properly set. If */
/* `numberofholes' is not zero, `holelist' must point to a list of */
/* holes. If `numberofregions' is not zero, `regionlist' must point to */
/* a list of region constraints. */
/* - If the `p' switch is used, `holelist', `numberofholes', */
/* `regionlist', and `numberofregions' is copied to `out'. (You can */
/* nonetheless get away with not initializing them if the `r' switch is */
/* used.) */
/* - `edgelist', `edgemarkerlist', `normlist', and `numberofedges' may be */
/* ignored. */
/* */
/* `out': */
/* */
/* - `pointlist' must be initialized (NULL or pointing to memory) unless */
/* the `N' switch is used. `pointmarkerlist' must be initialized */
/* unless the `N' or `B' switch is used. If `N' is not used and */
/* `in->numberofpointattributes' is not zero, `pointattributelist' must */
/* be initialized. */
/* - `trianglelist' must be initialized unless the `E' switch is used. */
/* `neighborlist' must be initialized if the `n' switch is used. If */
/* the `E' switch is not used and (`in->numberofelementattributes' is */
/* not zero or the `A' switch is used), `elementattributelist' must be */
/* initialized. `trianglearealist' may be ignored. */
/* - `segmentlist' must be initialized if the `p' or `c' switch is used, */
/* and the `P' switch is not used. `segmentmarkerlist' must also be */
/* initialized under these circumstances unless the `B' switch is used. */
/* - `edgelist' must be initialized if the `e' switch is used. */
/* `edgemarkerlist' must be initialized if the `e' switch is used and */
/* the `B' switch is not. */
/* - `holelist', `regionlist', `normlist', and all scalars may be ignored.*/
/* */
/* `vorout' (only needed if `v' switch is used): */
/* */
/* - `pointlist' must be initialized. If `in->numberofpointattributes' */
/* is not zero, `pointattributelist' must be initialized. */
/* `pointmarkerlist' may be ignored. */
/* - `edgelist' and `normlist' must both be initialized. */
/* `edgemarkerlist' may be ignored. */
/* - Everything else may be ignored. */
/* */
/* After a call to triangulate(), the valid fields of `out' and `vorout' */
/* will depend, in an obvious way, on the choice of switches used. Note */
/* that when the `p' switch is used, the pointers `holelist' and */
/* `regionlist' are copied from `in' to `out', but no new space is */
/* allocated; be careful that you don't free() the same array twice. On */
/* the other hand, Triangle will never copy the `pointlist' pointer (or any */
/* others); new space is allocated for `out->pointlist', or if the `N' */
/* switch is used, `out->pointlist' remains uninitialized. */
/* */
/* All of the meaningful `numberof' fields will be properly set; for */
/* instance, `numberofedges' will represent the number of edges in the */
/* triangulation whether or not the edges were written. If segments are */
/* not used, `numberofsegments' will indicate the number of boundary edges. */
/* */
/*****************************************************************************/
//#define SINGLE
#ifdef SINGLE
#define REAL float
#else /* not SINGLE */
#define REAL double
#endif /* not SINGLE */
#define IO_REAL float
#define INDICE unsigned short
typedef struct triangulateio TriangleIO;
struct triangulateio {
IO_REAL *pointlist; /* In / out */
IO_REAL *pointattributelist; /* In / out */
int *pointmarkerlist; /* In / out */
int numberofpoints; /* In / out */
int numberofpointattributes; /* In / out */
INDICE *trianglelist; /* In / out */
REAL *triangleattributelist; /* In / out */
REAL *trianglearealist; /* In only */
int *neighborlist; /* Out only */
int numberoftriangles; /* In / out */
int numberofcorners; /* In / out */
int numberoftriangleattributes; /* In / out */
int *segmentlist; /* In / out */
int *segmentmarkerlist; /* In / out */
int numberofsegments; /* In / out */
REAL *holelist; /* In / pointer to array copied out */
int numberofholes; /* In / copied out */
REAL *regionlist; /* In / pointer to array copied out */
int numberofregions; /* In / copied out */
int *edgelist; /* Out only */
int *edgemarkerlist; /* Not used with Voronoi diagram; out only */
REAL *normlist; /* Used only with Voronoi diagram; out only */
int numberofedges; /* Out only */
};
/* Data structure for command line switches and file names. This structure
* is used (instead of global variables) to allow reentrancy.
* Switches for the triangulator.
* poly: -p switch.
* refine: -r switch.
* quality: -q switch.
* minangle: minimum angle bound, specified after -q switch.
* goodangle: cosine squared of minangle.
* offconstant: constant used to place off-center Steiner points.
* vararea: -a switch without number.
* fixedarea: -a switch with number.
* maxarea: maximum area bound, specified after -a switch.
* usertest: -u switch.
* regionattrib: -A switch.
* convex: -c switch.
* weighted: 1 for -w switch, 2 for -W switch.
* jettison: -j switch
* firstnumber: inverse of -z switch. All items are numbered starting
* from `firstnumber'.
* edgesout: -e switch.
* voronoi: -v switch.
* neighbors: -n switch.
* geomview: -g switch.
* nobound: -B switch.
* nopolywritten: -P switch.
* nonodewritten: -N switch.
* noelewritten: -E switch.
* noiterationnum: -I switch.
* noholes: -O switch.
* noexact: -X switch.
* order: element order, specified after -o switch.
* nobisect: count of how often -Y switch is selected.
* steiner: maximum number of Steiner points, specified after -S switch.
* incremental: -i switch. sweepline: -F switch.
* dwyer: inverse of -l switch.
* splitseg: -s switch.
* conformdel: -D switch. docheck: -C switch.
* quiet: -Q switch. verbose: count of how often -V switch is selected.
* usesegments: -p, -r, -q, or -c switch; determines whether segments are
* used at all.
*
* Read the instructions to find out the meaning of these switches. */
typedef struct behavior TriangleOptions;
struct behavior {
int poly, refine, quality, vararea, fixedarea, usertest;
int regionattrib, convex, weighted, jettison;
int firstnumber;
int edgesout, voronoi, neighbors, geomview;
int nobound, nopolywritten, nonodewritten, noelewritten, noiterationnum;
int noholes, noexact, conformdel;
int incremental, sweepline, dwyer;
int splitseg;
int docheck;
int quiet, verbose;
int usesegments;
int order;
int nobisect;
int steiner;REAL minangle, goodangle, offconstant;REAL maxarea;
};
void parsecommandline(int argc, char **argv, struct behavior *b);
void triangulate(struct behavior *, struct triangulateio *, struct triangulateio *,
struct triangulateio *);

View File

@ -1,441 +0,0 @@
#include "triangle_private.h"
/*****************************************************************************/
/* */
/* quality_statistics() Print statistics about the quality of the mesh. */
/* */
/*****************************************************************************/
void quality_statistics(struct mesh *m, struct behavior *b) {
struct otri triangleloop;
vertex p[3];
REAL cossquaretable[8];
REAL ratiotable[16];
REAL dx[3], dy[3];
REAL edgelength[3];
REAL dotproduct;
REAL cossquare;
REAL triarea;
REAL shortest, longest;
REAL trilongest2;
REAL smallestarea, biggestarea;
REAL triminaltitude2;
REAL minaltitude;
REAL triaspect2;
REAL worstaspect;
REAL smallestangle, biggestangle;
REAL radconst, degconst;
int angletable[18];
int aspecttable[16];
int aspectindex;
int tendegree;
int acutebiggest;
int i, ii, j, k;
printf("Mesh quality statistics:\n\n");
radconst = PI / 18.0;
degconst = 180.0 / PI;
for (i = 0; i < 8; i++) {
cossquaretable[i] = cos(radconst * (REAL) (i + 1));
cossquaretable[i] = cossquaretable[i] * cossquaretable[i];
}
for (i = 0; i < 18; i++) {
angletable[i] = 0;
}
ratiotable[0] = 1.5;
ratiotable[1] = 2.0;
ratiotable[2] = 2.5;
ratiotable[3] = 3.0;
ratiotable[4] = 4.0;
ratiotable[5] = 6.0;
ratiotable[6] = 10.0;
ratiotable[7] = 15.0;
ratiotable[8] = 25.0;
ratiotable[9] = 50.0;
ratiotable[10] = 100.0;
ratiotable[11] = 300.0;
ratiotable[12] = 1000.0;
ratiotable[13] = 10000.0;
ratiotable[14] = 100000.0;
ratiotable[15] = 0.0;
for (i = 0; i < 16; i++) {
aspecttable[i] = 0;
}
worstaspect = 0.0;
minaltitude = m->xmax - m->xmin + m->ymax - m->ymin;
minaltitude = minaltitude * minaltitude;
shortest = minaltitude;
longest = 0.0;
smallestarea = minaltitude;
biggestarea = 0.0;
worstaspect = 0.0;
smallestangle = 0.0;
biggestangle = 2.0;
acutebiggest = 1;
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
triangleloop.orient = 0;
while (triangleloop.tri != (triangle *) NULL) {
org(triangleloop, p[0]);
dest(triangleloop, p[1]);
apex(triangleloop, p[2]);
trilongest2 = 0.0;
for (i = 0; i < 3; i++) {
j = plus1mod3[i];
k = minus1mod3[i];
dx[i] = p[j][0] - p[k][0];
dy[i] = p[j][1] - p[k][1];
edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
if (edgelength[i] > trilongest2) {
trilongest2 = edgelength[i];
}
if (edgelength[i] > longest) {
longest = edgelength[i];
}
if (edgelength[i] < shortest) {
shortest = edgelength[i];
}
}
triarea = counterclockwise(m, b, p[0], p[1], p[2]);
if (triarea < smallestarea) {
smallestarea = triarea;
}
if (triarea > biggestarea) {
biggestarea = triarea;
}
triminaltitude2 = triarea * triarea / trilongest2;
if (triminaltitude2 < minaltitude) {
minaltitude = triminaltitude2;
}
triaspect2 = trilongest2 / triminaltitude2;
if (triaspect2 > worstaspect) {
worstaspect = triaspect2;
}
aspectindex = 0;
while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex]) && (aspectindex < 15)) {
aspectindex++;
}
aspecttable[aspectindex]++;
for (i = 0; i < 3; i++) {
j = plus1mod3[i];
k = minus1mod3[i];
dotproduct = dx[j] * dx[k] + dy[j] * dy[k];
cossquare = dotproduct * dotproduct / (edgelength[j] * edgelength[k]);
tendegree = 8;
for (ii = 7; ii >= 0; ii--) {
if (cossquare > cossquaretable[ii]) {
tendegree = ii;
}
}
if (dotproduct <= 0.0) {
angletable[tendegree]++;
if (cossquare > smallestangle) {
smallestangle = cossquare;
}
if (acutebiggest && (cossquare < biggestangle)) {
biggestangle = cossquare;
}
}
else {
angletable[17 - tendegree]++;
if (acutebiggest || (cossquare > biggestangle)) {
biggestangle = cossquare;
acutebiggest = 0;
}
}
}
triangleloop.tri = triangletraverse(m);
}
shortest = sqrt(shortest);
longest = sqrt(longest);
minaltitude = sqrt(minaltitude);
worstaspect = sqrt(worstaspect);
smallestarea *= 0.5;
biggestarea *= 0.5;
if (smallestangle >= 1.0) {
smallestangle = 0.0;
}
else {
smallestangle = degconst * acos(sqrt(smallestangle));
}
if (biggestangle >= 1.0) {
biggestangle = 180.0;
}
else {
if (acutebiggest) {
biggestangle = degconst * acos(sqrt(biggestangle));
}
else {
biggestangle = 180.0 - degconst * acos(sqrt(biggestangle));
}
}
printf(" Smallest area: %16.5g | Largest area: %16.5g\n", smallestarea, biggestarea);
printf(" Shortest edge: %16.5g | Longest edge: %16.5g\n", shortest, longest);
printf(
" Shortest altitude: %12.5g | Largest aspect ratio: %8.5g\n\n", minaltitude, worstaspect);
printf(" Triangle aspect ratio histogram:\n");
printf(
" 1.1547 - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", ratiotable[0], aspecttable[0], ratiotable[7], ratiotable[8], aspecttable[8]);
for (i = 1; i < 7; i++) {
printf(
" %6.6g - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", ratiotable[i - 1], ratiotable[i], aspecttable[i], ratiotable[i + 7], ratiotable[i + 8], aspecttable[i + 8]);
}
printf(
" %6.6g - %-6.6g : %8d | %6.6g - : %8d\n", ratiotable[6], ratiotable[7], aspecttable[7], ratiotable[14], aspecttable[15]);
printf(" (Aspect ratio is longest edge divided by shortest altitude)\n\n");
printf(" Smallest angle: %15.5g | Largest angle: %15.5g\n\n", smallestangle, biggestangle);
printf(" Angle histogram:\n");
for (i = 0; i < 9; i++) {
printf(
" %3d - %3d degrees: %8d | %3d - %3d degrees: %8d\n", i * 10, i * 10 + 10, angletable[i], i * 10 + 90, i * 10 + 100, angletable[i + 9]);
}
printf("\n");
}
/*****************************************************************************/
/* */
/* statistics() Print all sorts of cool facts. */
/* */
/*****************************************************************************/
void statistics(struct mesh *m, struct behavior *b) {
printf("\nStatistics:\n\n");
printf(" Input vertices: %d\n", m->invertices);
if (b->refine) {
printf(" Input triangles: %d\n", m->inelements);
}
if (b->poly) {
printf(" Input segments: %d\n", m->insegments);
if (!b->refine) {
printf(" Input holes: %d\n", m->holes);
}
}
printf("\n Mesh vertices: %ld\n", m->vertices.items - m->undeads);
printf(" Mesh triangles: %ld\n", m->triangles.items);
printf(" Mesh edges: %ld\n", m->edges);
printf(" Mesh exterior boundary edges: %ld\n", m->hullsize);
if (b->poly || b->refine) {
printf(" Mesh interior boundary edges: %ld\n", m->subsegs.items - m->hullsize);
printf(" Mesh subsegments (constrained edges): %ld\n", m->subsegs.items);
}
printf("\n");
if (b->verbose) {
quality_statistics(m, b);
printf("Memory allocation statistics:\n\n");
printf(" Maximum number of vertices: %ld\n", m->vertices.maxitems);
printf(" Maximum number of triangles: %ld\n", m->triangles.maxitems);
if (m->subsegs.maxitems > 0) {
printf(" Maximum number of subsegments: %ld\n", m->subsegs.maxitems);
}
if (m->viri.maxitems > 0) {
printf(" Maximum number of viri: %ld\n", m->viri.maxitems);
}
if (m->badsubsegs.maxitems > 0) {
printf(" Maximum number of encroached subsegments: %ld\n", m->badsubsegs.maxitems);
}
if (m->badtriangles.maxitems > 0) {
printf(" Maximum number of bad triangles: %ld\n", m->badtriangles.maxitems);
}
if (m->flipstackers.maxitems > 0) {
printf(" Maximum number of stacked triangle flips: %ld\n", m->flipstackers.maxitems);
}
if (m->splaynodes.maxitems > 0) {
printf(" Maximum number of splay tree nodes: %ld\n", m->splaynodes.maxitems);
}
printf(
" Approximate heap memory use (bytes): %ld\n\n", m->vertices.maxitems * m->vertices.itembytes + m->triangles.maxitems * m->triangles.itembytes + m->subsegs.maxitems * m->subsegs.itembytes + m->viri.maxitems * m->viri.itembytes + m->badsubsegs.maxitems * m->badsubsegs.itembytes + m->badtriangles.maxitems * m->badtriangles.itembytes + m->flipstackers.maxitems * m->flipstackers.itembytes + m->splaynodes.maxitems * m->splaynodes.itembytes);
printf("Algorithmic statistics:\n\n");
if (!b->weighted) {
printf(" Number of incircle tests: %ld\n", m->incirclecount);
}
else {
printf(" Number of 3D orientation tests: %ld\n", m->orient3dcount);
}
printf(" Number of 2D orientation tests: %ld\n", m->counterclockcount);
if (m->hyperbolacount > 0) {
printf(" Number of right-of-hyperbola tests: %ld\n", m->hyperbolacount);
}
if (m->circletopcount > 0) {
printf(" Number of circle top computations: %ld\n", m->circletopcount);
}
if (m->circumcentercount > 0) {
printf(" Number of triangle circumcenter computations: %ld\n", m->circumcentercount);
}
printf("\n");
}
}
/********* Debugging routines begin here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* printtriangle() Print out the details of an oriented triangle. */
/* */
/* I originally wrote this procedure to simplify debugging; it can be */
/* called directly from the debugger, and presents information about an */
/* oriented triangle in digestible form. It's also used when the */
/* highest level of verbosity (`-VVV') is specified. */
/* */
/*****************************************************************************/
void printtriangle(struct mesh *m, struct behavior *b, struct otri *t) {
struct otri printtri;
struct osub printsh;
vertex printvertex;
printf("triangle x%lx with orientation %d:\n", (unsigned long) t->tri, t->orient);
decode(t->tri[0], printtri);
if (printtri.tri == m->dummytri) {
printf(" [0] = Outer space\n");
}
else {
printf(" [0] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient);
}
decode(t->tri[1], printtri);
if (printtri.tri == m->dummytri) {
printf(" [1] = Outer space\n");
}
else {
printf(" [1] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient);
}
decode(t->tri[2], printtri);
if (printtri.tri == m->dummytri) {
printf(" [2] = Outer space\n");
}
else {
printf(" [2] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient);
}
org(*t, printvertex);
if (printvertex == (vertex) NULL)
printf(" Origin[%d] = NULL\n", (t->orient + 1) % 3 + 3);
else
printf(
" Origin[%d] = x%lx (%.12g, %.12g)\n", (t->orient + 1) % 3 + 3, (unsigned long) printvertex, printvertex[0], printvertex[1]);
dest(*t, printvertex);
if (printvertex == (vertex) NULL)
printf(" Dest [%d] = NULL\n", (t->orient + 2) % 3 + 3);
else
printf(
" Dest [%d] = x%lx (%.12g, %.12g)\n", (t->orient + 2) % 3 + 3, (unsigned long) printvertex, printvertex[0], printvertex[1]);
apex(*t, printvertex);
if (printvertex == (vertex) NULL)
printf(" Apex [%d] = NULL\n", t->orient + 3);
else
printf(
" Apex [%d] = x%lx (%.12g, %.12g)\n", t->orient + 3, (unsigned long) printvertex, printvertex[0], printvertex[1]);
if (b->usesegments) {
sdecode(t->tri[6], printsh);
if (printsh.ss != m->dummysub) {
printf(" [6] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient);
}
sdecode(t->tri[7], printsh);
if (printsh.ss != m->dummysub) {
printf(" [7] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient);
}
sdecode(t->tri[8], printsh);
if (printsh.ss != m->dummysub) {
printf(" [8] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient);
}
}
if (b->vararea) {
printf(" Area constraint: %.4g\n", areabound(*t));
}
}
/*****************************************************************************/
/* */
/* printsubseg() Print out the details of an oriented subsegment. */
/* */
/* I originally wrote this procedure to simplify debugging; it can be */
/* called directly from the debugger, and presents information about an */
/* oriented subsegment in digestible form. It's also used when the highest */
/* level of verbosity (`-VVV') is specified. */
/* */
/*****************************************************************************/
void printsubseg(struct mesh *m, struct behavior *b, struct osub *s) {
struct osub printsh;
struct otri printtri;
vertex printvertex;
printf(
"subsegment x%lx with orientation %d and mark %d:\n", (unsigned long) s->ss, s->ssorient, mark(*s));
sdecode(s->ss[0], printsh);
if (printsh.ss == m->dummysub) {
printf(" [0] = No subsegment\n");
}
else {
printf(" [0] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient);
}
sdecode(s->ss[1], printsh);
if (printsh.ss == m->dummysub) {
printf(" [1] = No subsegment\n");
}
else {
printf(" [1] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient);
}
sorg(*s, printvertex);
if (printvertex == (vertex) NULL)
printf(" Origin[%d] = NULL\n", 2 + s->ssorient);
else
printf(
" Origin[%d] = x%lx (%.12g, %.12g)\n", 2 + s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]);
sdest(*s, printvertex);
if (printvertex == (vertex) NULL)
printf(" Dest [%d] = NULL\n", 3 - s->ssorient);
else
printf(
" Dest [%d] = x%lx (%.12g, %.12g)\n", 3 - s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]);
decode(s->ss[6], printtri);
if (printtri.tri == m->dummytri) {
printf(" [6] = Outer space\n");
}
else {
printf(" [6] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient);
}
decode(s->ss[7], printtri);
if (printtri.tri == m->dummytri) {
printf(" [7] = Outer space\n");
}
else {
printf(" [7] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient);
}
segorg(*s, printvertex);
if (printvertex == (vertex) NULL)
printf(" Segment origin[%d] = NULL\n", 4 + s->ssorient);
else
printf(
" Segment origin[%d] = x%lx (%.12g, %.12g)\n", 4 + s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]);
segdest(*s, printvertex);
if (printvertex == (vertex) NULL)
printf(" Segment dest [%d] = NULL\n", 5 - s->ssorient);
else
printf(
" Segment dest [%d] = x%lx (%.12g, %.12g)\n", 5 - s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]);
}
/** **/
/** **/
/********* Debugging routines end here *********/

File diff suppressed because it is too large Load Diff

View File

@ -165,8 +165,6 @@ public class ExtrusionRenderer extends LayerRenderer {
private final boolean debug = false;
//private final float[] mVPMatrix = new float[16];
@Override
protected void render(MapPosition pos, Matrices m) {
// TODO one could render in one pass to texture and then draw the texture

View File

@ -25,7 +25,7 @@ import org.oscim.core.Tile;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.MapRenderer;
import org.oscim.utils.LineClipper;
import org.oscim.utils.geom.Triangulator;
import org.oscim.utils.Tessellator;
/**
* @author Hannes Janetzek
@ -126,7 +126,7 @@ public class ExtrusionLayer extends RenderElement {
// check: drop last point from explicitly closed rings
int len = length;
if (points[ppos] == points[ppos + len - 2]
&& points[ppos + 1] == points[ppos + len - 1]) {
&& points[ppos + 1] == points[ppos + len - 1]) {
len -= 2;
Log.d(TAG, "explicit closed poly " + len);
}
@ -184,15 +184,8 @@ public class ExtrusionLayer extends RenderElement {
rings++;
}
// triangulate up to 600 points (limited only by prepared buffers)
// some buildings in paris have even more...
if (len > 1200) {
Log.d(TAG, ">>> skip building : " + len + " <<<");
return;
}
int used = Triangulator.triangulate(points, ppos, len, index, ipos, rings,
startVertex + 1, mCurIndices[IND_ROOF]);
int used = Tessellator.triangulate(points, ppos, len, index, ipos, rings,
startVertex + 1, mCurIndices[IND_ROOF]);
if (used > 0) {
// get back to the last item added..
@ -204,7 +197,7 @@ public class ExtrusionLayer extends RenderElement {
}
private boolean addOutline(float[] points, int pos, int len, float minHeight, float height,
boolean convex) {
boolean convex) {
// add two vertices for last face to make zigzag indices work
boolean addFace = (len % 4 != 0);

View File

@ -0,0 +1,103 @@
package org.oscim.utils;
import org.oscim.renderer.elements.VertexItem;
public class Tessellator {
private static final int RESULT_VERTICES = 0;
//private static final int RESULT_TRIANGLES = 1;
private static final short[] coordinates = new short[720];
public static synchronized int triangulate(float[] points, int ppos, int plen, short[] index,
int ipos, int rings, int vertexOffset, VertexItem outTris) {
int[] result = new int[2];
int numPoints = 0;
for (int i = 0; i < rings; i++)
numPoints += index[ipos + i];
long ctx = Tessellator.tessellate(points, ppos, index, ipos, rings, result);
if ((numPoints / 2) < result[RESULT_VERTICES]) {
//Log.d(TAG, "nup" + Arrays.toString(result) + " " + numPoints);
Tessellator.tessFinish(ctx);
return 0;
}
//while (Tessellator.tessGetCoordinates(ctx, coordinates, 2) > 0) {
// Log.d(TAG, Arrays.toString(coordinates));
//}
int cnt;
int numIndices = 0;
while ((cnt = Tessellator.tessGetIndices(ctx, coordinates)) > 0) {
//if (cnt > (VertexItem.SIZE - outTris.used))
// Log.d(TAG, "ok" + Arrays.toString(result));
//Log.d(TAG,Arrays.toString(coordinates));
numIndices += cnt;
for (int j = 0; j < cnt; j++)
coordinates[j] *= 2;
// when a ring has an odd number of points one (or rather two)
// additional vertices will be added. so the following rings
// needs extra offset
int shift = 0;
for (int i = 0, m = rings - 1; i < m; i++) {
shift += (index[ipos + i]);
// even number of points?
if (((index[ipos + i] >> 1) & 1) == 0)
continue;
for (int j = 0; j < cnt; j++)
if (coordinates[j] >= shift)
coordinates[j] += 2;
shift += 2;
}
for (int j = 0; j < cnt;) {
int outPos = outTris.used;
short[] v = outTris.vertices;
if (outPos == VertexItem.SIZE) {
outTris.next = VertexItem.pool.get();
outTris = outTris.next;
v = outTris.vertices;
outPos = 0;
}
// shift to vertex offset
v[outPos++] = (short) (vertexOffset + coordinates[j++]);
v[outPos++] = (short) (vertexOffset + coordinates[j++]);
v[outPos++] = (short) (vertexOffset + coordinates[j++]);
outTris.used = outPos;
}
}
Tessellator.tessFinish(ctx);
return numIndices;
}
/**
* @param points an array of x,y coordinates
* @param pos position in points array
* @param index geom indices
* @param ipos position in index array
* @param numRings number of rings in polygon == outer(1) + inner rings
* @param result contains number of vertices and number of triangles
* @return context - must be freed with tessFinish()
*/
public static native long tessellate(float[] points, int pos,
short[] index, int ipos, int numRings, int[] result);
public static native void tessFinish(long ctx);
public static native int tessGetCoordinates(long ctx, short[] coordinates, float scale);
public static native int tessGetIndices(long ctx, short[] indices);
}

View File

@ -1,67 +0,0 @@
package org.oscim.utils.geom;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import org.oscim.renderer.elements.VertexItem;
public class Triangulator {
private static boolean initialized = false;
private static ShortBuffer sBuf;
public static synchronized int triangulate(float[] points, int ppos, int plen, short[] index,
int ipos, int rings, int vertexOffset, VertexItem outTris) {
if (!initialized) {
// FIXME also cleanup on shutdown!
sBuf = ByteBuffer.allocateDirect(1800 * 2).order(ByteOrder.nativeOrder())
.asShortBuffer();
initialized = true;
}
sBuf.clear();
sBuf.put(index, ipos, rings);
int numTris = triangulate(points, ppos, plen, rings, sBuf, vertexOffset);
int numIndices = numTris * 3;
sBuf.limit(numIndices);
sBuf.position(0);
for (int k = 0, cnt = 0; k < numIndices; k += cnt) {
if (outTris.used == VertexItem.SIZE) {
outTris.next = VertexItem.pool.get();
outTris = outTris.next;
}
cnt = VertexItem.SIZE - outTris.used;
if (k + cnt > numIndices)
cnt = numIndices - k;
sBuf.get(outTris.vertices, outTris.used, cnt);
outTris.used += cnt;
}
return numIndices;
}
/**
* @param points an array of x,y coordinates
* @param pos position in points array
* @param len number of points * 2 (i.e. values to read)
* @param numRings number of rings in polygon == outer(1) + inner rings
* @param io input: number of points in rings - times 2!
* output: indices of triangles, 3 per triangle :) (indices use
* stride=2, i.e. 0,2,4...)
* @param ioffset offset used to add offset to indices
* @return number of triangles in io buffer
*/
public static native int triangulate(float[] points, int pos, int len, int numRings,
ShortBuffer io, int ioffset) /*-{
return 0;
}-*/;
}