diff --git a/Tools/Areas/Makefile.am b/Tools/Areas/Makefile.am new file mode 100644 index 000000000..7c6106189 --- /dev/null +++ b/Tools/Areas/Makefile.am @@ -0,0 +1,62 @@ +#--------------------------------------------------------------------------- +# Makefile +# +# Written by Curtis Olson, started January 1998. +# +# Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +bin_PROGRAMS = areas + +areas_SOURCES = area.cxx area.hxx main.cxx + +areas_LDADD = \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(base_LIBS) + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.3 1998/11/04 23:01:43 curt +# Changes to the automake/autoconf system to reduce the number of libraries +# that are unnecessarily linked into the various executables. +# +# Revision 1.2 1998/07/30 23:49:18 curt +# Removed libtool support. +# +# Revision 1.1 1998/07/20 12:54:53 curt +# Whoops, need to commit Makefile.am, not Makefile. +# +# Revision 1.2 1998/04/14 02:25:59 curt +# Code reorganizations. Added a Lib/ directory for more general libraries. +# +# Revision 1.1 1998/04/08 22:54:57 curt +# Adopted Gnu automake/autoconf system. +# +# Revision 1.2 1998/01/21 02:55:46 curt +# Incorporated new make system from Bob Kuehne <rpk@sgi.com>. +# +# Revision 1.1 1998/01/15 02:45:25 curt +# Initial revision. +# + diff --git a/Tools/Areas/area.cxx b/Tools/Areas/area.cxx new file mode 100644 index 000000000..2dabe331d --- /dev/null +++ b/Tools/Areas/area.cxx @@ -0,0 +1,161 @@ +// area.c -- routines to assist with inserting "areas" into FG terrain +// +// Written by Curtis Olson, started March 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#include <math.h> +#include <stdio.h> + +#include <Include/fg_constants.h> + +#include "area.hxx" + + +// calc new x, y for a rotation +double rot_x(double x, double y, double theta) { + return ( x * cos(theta) + y * sin(theta) ); +} + + +// calc new x, y for a rotation +double rot_y(double x, double y, double theta) { + return ( -x * sin(theta) + y * cos(theta) ); +} + + +// calc new lon/lat given starting lon/lat, and offset radial, and +// distance. NOTE: distance is specified in meters (and converted +// internally to radians) +point2d calc_lon_lat( point2d orig, point2d offset ) { + point2d result; + + offset.dist *= METER_TO_NM * NM_TO_RAD; + + result.lat = asin( sin(orig.lat) * cos(offset.dist) + + cos(orig.lat) * sin(offset.dist) * cos(offset.theta) ); + + if ( cos(result.lat) < FG_EPSILON ) { + result.lon = orig.lon; // endpoint a pole + } else { + result.lon = + fmod(orig.lon - asin( sin(offset.theta) * sin(offset.dist) / + cos(result.lat) ) + FG_PI, FG_2PI) - FG_PI; + } + + return(result); +} + + +point2d cart_to_polar_2d(point2d in) { + point2d result; + result.dist = sqrt( in.x * in.x + in.y * in.y ); + result.theta = atan2(in.y, in.x); + + return(result); +} + + +void batch_cart_to_polar_2d(point2d *in, point2d *out, int size) { + int i; + + for ( i = 0; i < size; i++ ) { + out[i] = cart_to_polar_2d( in[i] ); + } +} + + +// given a set of 2d coordinates relative to a center point, and the +// lon, lat of that center point, as well as a potential orientation +// angle, generate the corresponding lon and lat of the original 2d +// verticies. +void make_area(point2d orig, point2d *cart, point2d *result, + int size, double angle ) { + point2d rad[size]; + int i; + + // convert to polar coordinates + batch_cart_to_polar_2d(cart, rad, size); + for ( i = 0; i < size; i++ ) { + printf("(%.2f, %.2f)\n", rad[i].dist, rad[i].theta); + } + printf("\n"); + + // rotate by specified angle + for ( i = 0; i < size; i++ ) { + rad[i].theta += angle; + while ( rad[i].theta > FG_2PI ) { + rad[i].theta -= FG_2PI; + } + printf("(%.2f, %.2f)\n", rad[i].dist, rad[i].theta); + } + printf("\n"); + + for ( i = 0; i < size; i++ ) { + result[i] = calc_lon_lat(orig, rad[i]); + printf("(%.8f, %.8f)\n", result[i].lon, result[i].lat); + } + printf("\n"); +} + + +// generate an area for a runway +void gen_runway_area( double lon, double lat, double heading, + double length, double width, + point2d *result, int *count) +{ + point2d cart[4]; + point2d orig; + double l, w; + int i; + + orig.lon = lon; + orig.lat = lat; + l = (length / 2.0) + (length * 0.1); + w = (width / 2.0) + (width * 0.1); + + // generate untransformed runway area vertices + cart[0].x = l; cart[0].y = w; + cart[1].x = l; cart[1].y = -w; + cart[2].x = -l; cart[2].y = -w; + cart[3].x = -l; cart[3].y = w; + for ( i = 0; i < 4; i++ ) { + printf("(%.2f, %.2f)\n", cart[i].x, cart[i].y); + } + printf("\n"); + + make_area(orig, cart, result, 4, heading); + + for ( i = 0; i < 4; i++ ) { + printf("(%.8f, %.8f)\n", result[i].lon, result[i].lat); + } + printf("\n"); + + *count = 4; +} + + +// $Log$ +// Revision 1.1 1998/07/20 12:54:05 curt +// Initial revision. +// +// diff --git a/Tools/Areas/area.hxx b/Tools/Areas/area.hxx new file mode 100644 index 000000000..584e5f6a0 --- /dev/null +++ b/Tools/Areas/area.hxx @@ -0,0 +1,57 @@ +// area.h -- routines to assist with inserting "areas" into FG terrain +// +// Written by Curtis Olson, started February 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#ifndef _AREA_H +#define _AREA_H + + +typedef struct { + union { + double x; + double dist; + double lon; + }; + union { + double y; + double theta; + double lat; + }; +} point2d; + + +// generate an area for a runway +void gen_runway_area( double lon, double lat, double heading, + double length, double width, + point2d *result, int *count ); + + +#endif // _AREA_H + + +// $Log$ +// Revision 1.1 1998/07/20 12:54:05 curt +// Initial revision. +// +// diff --git a/Tools/Areas/main.cxx b/Tools/Areas/main.cxx new file mode 100644 index 000000000..1f6d635b8 --- /dev/null +++ b/Tools/Areas/main.cxx @@ -0,0 +1,130 @@ +// main.c -- main loop +// +// Written by Curtis Olson, started March 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include "area.hxx" + +#include <Bucket/bucketutils.h> +#include <Include/fg_constants.h> + + +int main( int argc, char **argv ) { + fgBUCKET b; + point2d nodes[4]; + FILE *fd; + char base[256], path[256], command[256], file[256], exfile[256]; + double lon, lat, elevation, heading; + double length, width; + long int index; + int i, count; + + if ( argc != 2 ) { + printf("Usage %s <work dir>\n", argv[0]); + exit(0); + } + + // P13 (Globe, AZ) + // lon = -110.6642442; + // lat = 33.3528903; + // heading = 102.0 * DEG_TO_RAD; + // length = 1769; + // width = 23; + + // KANE + lon = -93.2113889; + lat = 45.145; + elevation = 912 * FEET_TO_METER; + heading = 270.0 * DEG_TO_RAD; + length = 1220; + width = 23; + + gen_runway_area( lon * DEG_TO_RAD, lat * DEG_TO_RAD, + heading, length, width, nodes, &count ); + + fgBucketFind(lon, lat, &b); + printf( "Bucket = lon,lat = %d,%d x,y index = %d,%d\n", + b.lon, b.lat, b.x, b.y); + + index = fgBucketGenIndex(&b); + fgBucketGenBasePath(&b, base); + sprintf(path, "%s/Scenery/%s", argv[1], base); + sprintf(command, "mkdir -p %s\n", path); + system(command); + + sprintf(exfile, "%s/%ld.node.ex", path, index); + sprintf(file, "%s/%ld.poly", path, index); + printf( "extra node file = %s\n", exfile); + printf( "poly file = %s\n", file); + + // output extra nodes + if ( (fd = fopen(exfile, "w")) == NULL ) { + printf("Cannot open file: %s\n", exfile); + exit(-1); + } + + fprintf(fd, "%d 2 0 0\n", count); + for ( i = 0; i < count; i++ ) { + fprintf( fd, "%d %.2f %.2f %.2f\n", i + 1, + nodes[i].lon * RAD_TO_ARCSEC, nodes[i].lat * RAD_TO_ARCSEC, + elevation); + } + fclose(fd); + + // output poly + if ( (fd = fopen(file, "w")) == NULL ) { + printf("Cannot open file: %s\n", file); + exit(-1); + } + + // output empty node list + fprintf(fd, "0 2 0 0\n"); + + // output segments + fprintf(fd, "%d 0\n", count); + for ( i = 0; i < count - 1; i++ ) { + fprintf( fd, "%d %d %d\n", i + 1, i + 1, i + 2 ); + } + fprintf( fd, "%d %d %d\n", count, count, 1 ); + + // output hole center + fprintf( fd, "1\n"); + fprintf( fd, "1 %.2f %.2f\n", lon * 3600.0, lat * 3600); + + fclose(fd); +} + + +// $Log: main.c,v +// diff --git a/Tools/Array/Makefile.am b/Tools/Array/Makefile.am new file mode 100644 index 000000000..a62648005 --- /dev/null +++ b/Tools/Array/Makefile.am @@ -0,0 +1,25 @@ +noinst_LIBRARIES = libArray.a + +libArray_a_SOURCES = array.cxx array.hxx + +bin_PROGRAMS = testarray + +testarray_SOURCES = testarray.cxx + +testarray_LDADD = \ + $(top_builddir)/Tools/Construct/Array/libArray.a \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(top_builddir)/Lib/Math/libMath.a \ + $(top_builddir)/Lib/Misc/libMisc.a \ + $(top_builddir)/Lib/zlib/libz.a + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Tools/Construct + +# We can't build this with "-O2" (optimization) since this causes a seg fault +# I haven't found a way to strip this out of the CXXFLAGS, so I'm just +# setting it to "-g" +# CXXFLAGS = -g + diff --git a/Tools/Array/array.cxx b/Tools/Array/array.cxx new file mode 100644 index 000000000..2b7d2a625 --- /dev/null +++ b/Tools/Array/array.cxx @@ -0,0 +1,607 @@ +// array.cxx -- Array management class +// +// Written by Curtis Olson, started March 1998. +// +// Copyright (C) 1998 - 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <Include/compiler.h> + +// #include <ctype.h> // isspace() +// #include <stdlib.h> // atoi() +// #include <math.h> // rint() +// #include <stdio.h> +// #include <string.h> +// #ifdef HAVE_SYS_STAT_H +// # include <sys/stat.h> // stat() +// #endif +// #ifdef FG_HAVE_STD_INCLUDES +// # include <cerrno> +// #else +// # include <errno.h> +// #endif +// #ifdef HAVE_UNISTD_H +// # include <unistd.h> // stat() +// #endif + +#include STL_STRING + +#include <Include/fg_constants.h> +#include <Misc/fgstream.hxx> +#include <Misc/strutils.hxx> +#include <Math/leastsqs.hxx> + +#include "array.hxx" + +FG_USING_STD(string); + + +FGArray::FGArray( void ) { + // cout << "class FGArray CONstructor called." << endl; + in_data = new float[ARRAY_SIZE_1][ARRAY_SIZE_1]; + // out_data = new float[ARRAY_SIZE_1][ARRAY_SIZE_1]; +} + + +FGArray::FGArray( const string &file ) { + // cout << "class FGArray CONstructor called." << endl; + in_data = new float[ARRAY_SIZE_1][ARRAY_SIZE_1]; + // out_data = new float[ARRAY_SIZE_1][ARRAY_SIZE_1]; + + FGArray::open(file); +} + + +// open an Array file +int +FGArray::open( const string& file ) { + // open input file (or read from stdin) + if ( file == "-" ) { + cout << " Opening array data pipe from stdin" << endl; + // fd = stdin; + // fd = gzdopen(STDIN_FILENO, "r"); + cout << " Not yet ported ..." << endl; + return 0; + } else { + in = new fg_gzifstream( file ); + if ( ! in->is_open() ) { + cout << " Cannot open " << file << endl; + return 0; + } + cout << " Opening array data file: " << file << endl; + } + + return 1; +} + + +// close an Array file +int +FGArray::close() { + // the fg_gzifstream doesn't seem to have a close() + + delete in; + + return 1; +} + + +// parse Array file, pass in the bucket so we can make up values when +// the file wasn't found. +int +FGArray::parse( FGBucket& b ) { + if ( in->is_open() ) { + // file open, parse + *in >> originx >> originy; + *in >> cols >> col_step; + *in >> rows >> row_step; + + cout << " origin = " << originx << " " << originy << endl; + cout << " cols = " << cols << " rows = " << rows << endl; + cout << " col_step = " << col_step + << " row_step = " << row_step <<endl; + + for ( int i = 0; i < cols; i++ ) { + for ( int j = 0; j < rows; j++ ) { + *in >> in_data[i][j]; + } + } + + cout << " Done parsing\n"; + } else { + // file not open (not found?), fill with zero'd data + + originx = ( b.get_center_lon() - 0.5 * b.get_width() ) * 3600.0; + originy = ( b.get_center_lat() - 0.5 * b.get_height() ) * 3600.0; + + double max_x = ( b.get_center_lon() + 0.5 * b.get_width() ) * 3600.0; + double max_y = ( b.get_center_lat() + 0.5 * b.get_height() ) * 3600.0; + + cols = 3; + col_step = (max_x - originx) / (cols - 1); + rows = 3; + row_step = (max_y - originy) / (rows - 1); + + cout << " origin = " << originx << " " << originy << endl; + cout << " cols = " << cols << " rows = " << rows << endl; + cout << " col_step = " << col_step + << " row_step = " << row_step <<endl; + + for ( int i = 0; i < cols; i++ ) { + for ( int j = 0; j < rows; j++ ) { + in_data[i][j] = 0.0; + } + } + + cout << " File not open, so using zero'd data" << endl; + } + + return 1; +} + + +// add a node to the output corner node list +void FGArray::add_corner_node( int i, int j, double val ) { + + double x = (originx + i * col_step) / 3600.0; + double y = (originy + j * row_step) / 3600.0; + // cout << "originx = " << originx << " originy = " << originy << endl; + // cout << "corner = " << Point3D(x, y, val) << endl; + corner_list.push_back( Point3D(x, y, val) ); +} + + +// add a node to the output fitted node list +void FGArray::add_fit_node( int i, int j, double val ) { + double x = (originx + i * col_step) / 3600.0; + double y = (originy + j * row_step) / 3600.0; + // cout << Point3D(x, y, val) << endl; + node_list.push_back( Point3D(x, y, val) ); +} + + +// Use least squares to fit a simpler data set to dem data. Return +// the number of fitted nodes +int FGArray::fit( double error ) { + double x[ARRAY_SIZE_1], y[ARRAY_SIZE_1]; + double m, b, max_error, error_sq; + double x1, y1; + // double ave_error; + double cury, lasty; + int n, row, start, end; + int colmin, colmax, rowmin, rowmax; + bool good_fit; + // FILE *dem, *fit, *fit1; + + error_sq = error * error; + + cout << " Initializing fitted node list" << endl; + corner_list.clear(); + node_list.clear(); + + // determine dimensions + colmin = 0; + colmax = cols; + rowmin = 0; + rowmax = rows; + cout << " Fitting region = " << colmin << "," << rowmin << " to " + << colmax << "," << rowmax << endl;; + + // generate corners list + add_corner_node( colmin, rowmin, in_data[colmin][rowmin] ); + add_corner_node( colmin, rowmax-1, in_data[colmin][rowmax] ); + add_corner_node( colmax-1, rowmin, in_data[colmax][rowmin] ); + add_corner_node( colmax-1, rowmax-1, in_data[colmax][rowmax] ); + + cout << " Beginning best fit procedure" << endl; + lasty = 0; + + for ( row = rowmin; row < rowmax; row++ ) { + // fit = fopen("fit.dat", "w"); + // fit1 = fopen("fit1.dat", "w"); + + start = colmin; + + // cout << " fitting row = " << row << endl; + + while ( start < colmax - 1 ) { + end = start + 1; + good_fit = true; + + x[0] = start * col_step; + y[0] = in_data[start][row]; + + x[1] = end * col_step; + y[1] = in_data[end][row]; + + n = 2; + + // cout << "Least square of first 2 points" << endl; + least_squares(x, y, n, &m, &b); + + end++; + + while ( (end < colmax) && good_fit ) { + ++n; + // cout << "Least square of first " << n << " points" << endl; + x[n-1] = x1 = end * col_step; + y[n-1] = y1 = in_data[end][row]; + least_squares_update(x1, y1, &m, &b); + // ave_error = least_squares_error(x, y, n, m, b); + max_error = least_squares_max_error(x, y, n, m, b); + + /* + printf("%d - %d ave error = %.2f max error = %.2f y = %.2f*x + %.2f\n", + start, end, ave_error, max_error, m, b); + + f = fopen("gnuplot.dat", "w"); + for ( j = 0; j <= end; j++) { + fprintf(f, "%.2f %.2f\n", 0.0 + ( j * col_step ), + in_data[row][j]); + } + for ( j = start; j <= end; j++) { + fprintf(f, "%.2f %.2f\n", 0.0 + ( j * col_step ), + in_data[row][j]); + } + fclose(f); + + printf("Please hit return: "); gets(junk); + */ + + if ( max_error > error_sq ) { + good_fit = false; + } + + end++; + } + + if ( !good_fit ) { + // error exceeded the threshold, back up + end -= 2; // back "end" up to the last good enough fit + n--; // back "n" up appropriately too + } else { + // we popped out of the above loop while still within + // the error threshold, so we must be at the end of + // the data set + end--; + } + + least_squares(x, y, n, &m, &b); + // ave_error = least_squares_error(x, y, n, m, b); + max_error = least_squares_max_error(x, y, n, m, b); + + /* + printf("\n"); + printf("%d - %d ave error = %.2f max error = %.2f y = %.2f*x + %.2f\n", + start, end, ave_error, max_error, m, b); + printf("\n"); + + fprintf(fit1, "%.2f %.2f\n", x[0], m * x[0] + b); + fprintf(fit1, "%.2f %.2f\n", x[end-start], m * x[end-start] + b); + */ + + if ( start > colmin ) { + // skip this for the first line segment + cury = m * x[0] + b; + add_fit_node( start, row, (lasty + cury) / 2 ); + // fprintf(fit, "%.2f %.2f\n", x[0], (lasty + cury) / 2); + } + + lasty = m * x[end-start] + b; + start = end; + } + + /* + fclose(fit); + fclose(fit1); + + dem = fopen("gnuplot.dat", "w"); + for ( j = 0; j < ARRAY_SIZE_1; j++) { + fprintf(dem, "%.2f %.2f\n", 0.0 + ( j * col_step ), + in_data[j][row]); + } + fclose(dem); + */ + + // NOTICE, this is for testing only. This instance of + // output_nodes should be removed. It should be called only + // once at the end once all the nodes have been generated. + // newmesh_output_nodes(&nm, "mesh.node"); + // printf("Please hit return: "); gets(junk); + } + + // outputmesh_output_nodes(fg_root, p); + + // return fit nodes + 4 corners + return node_list.size() + 4; +} + + +// return the current altitude based on grid data. We should rewrite +// this to interpolate exact values, but for now this is good enough +double FGArray::interpolate_altitude( double lon, double lat ) const { + // we expect incoming (lon,lat) to be in arcsec for now + + double xlocal, ylocal, dx, dy, zA, zB, elev; + int x1, x2, x3, y1, y2, y3; + float z1, z2, z3; + int xindex, yindex; + + /* determine if we are in the lower triangle or the upper triangle + ______ + | /| + | / | + | / | + |/ | + ------ + + then calculate our end points + */ + + xlocal = (lon - originx) / col_step; + ylocal = (lat - originy) / row_step; + + xindex = (int)(xlocal); + yindex = (int)(ylocal); + + // printf("xindex = %d yindex = %d\n", xindex, yindex); + + if ( xindex + 1 == cols ) { + xindex--; + } + + if ( yindex + 1 == rows ) { + yindex--; + } + + if ( (xindex < 0) || (xindex + 1 >= cols) || + (yindex < 0) || (yindex + 1 >= rows) ) { + cout << "WARNING: Attempt to interpolate value outside of array!!!" + << endl; + return 0; + } + + dx = xlocal - xindex; + dy = ylocal - yindex; + + if ( dx > dy ) { + // lower triangle + // printf(" Lower triangle\n"); + + x1 = xindex; + y1 = yindex; + z1 = in_data[x1][y1]; + + x2 = xindex + 1; + y2 = yindex; + z2 = in_data[x2][y2]; + + x3 = xindex + 1; + y3 = yindex + 1; + z3 = in_data[x3][y3]; + + // printf(" dx = %.2f dy = %.2f\n", dx, dy); + // printf(" (x1,y1,z1) = (%d,%d,%d)\n", x1, y1, z1); + // printf(" (x2,y2,z2) = (%d,%d,%d)\n", x2, y2, z2); + // printf(" (x3,y3,z3) = (%d,%d,%d)\n", x3, y3, z3); + + zA = dx * (z2 - z1) + z1; + zB = dx * (z3 - z1) + z1; + + // printf(" zA = %.2f zB = %.2f\n", zA, zB); + + if ( dx > FG_EPSILON ) { + elev = dy * (zB - zA) / dx + zA; + } else { + elev = zA; + } + } else { + // upper triangle + // printf(" Upper triangle\n"); + + x1 = xindex; + y1 = yindex; + z1 = in_data[x1][y1]; + + x2 = xindex; + y2 = yindex + 1; + z2 = in_data[x2][y2]; + + x3 = xindex + 1; + y3 = yindex + 1; + z3 = in_data[x3][y3]; + + // printf(" dx = %.2f dy = %.2f\n", dx, dy); + // printf(" (x1,y1,z1) = (%d,%d,%d)\n", x1, y1, z1); + // printf(" (x2,y2,z2) = (%d,%d,%d)\n", x2, y2, z2); + // printf(" (x3,y3,z3) = (%d,%d,%d)\n", x3, y3, z3); + + zA = dy * (z2 - z1) + z1; + zB = dy * (z3 - z1) + z1; + + // printf(" zA = %.2f zB = %.2f\n", zA, zB ); + // printf(" xB - xA = %.2f\n", col_step * dy / row_step); + + if ( dy > FG_EPSILON ) { + elev = dx * (zB - zA) / dy + zA; + } else { + elev = zA; + } + } + + return(elev); +} + + +#if 0 +// Write out a node file that can be used by the "triangle" program. +// Check for an optional "index.node.ex" file in case there is a .poly +// file to go along with this node file. Include these nodes first +// since they are referenced by position from the .poly file. +void FGArray::outputmesh_output_nodes( const string& fg_root, FGBucket& p ) +{ + double exnodes[MAX_EX_NODES][3]; + struct stat stat_buf; + string dir, file; + char exfile[256]; +#ifdef WIN32 + char tmp_path[256]; +#endif + string command; + FILE *fd; + int colmin, colmax, rowmin, rowmax; + int i, j, count, excount, result; + + // determine dimensions + colmin = p.get_x() * ( (cols - 1) / 8); + colmax = colmin + ( (cols - 1) / 8); + rowmin = p.get_y() * ( (rows - 1) / 8); + rowmax = rowmin + ( (rows - 1) / 8); + cout << " dumping region = " << colmin << "," << rowmin << " to " << + colmax << "," << rowmax << "\n"; + + // generate the base directory + string base_path = p.gen_base_path(); + cout << " fg_root = " << fg_root << " Base Path = " << base_path << endl; + dir = fg_root + "/Scenery/" + base_path; + cout << " Dir = " << dir << endl; + + // stat() directory and create if needed + errno = 0; + result = stat(dir.c_str(), &stat_buf); + if ( result != 0 && errno == ENOENT ) { + cout << " Creating directory\n"; + + command = "mkdir -p " + dir + "\n"; + system( command.c_str() ); + } else { + // assume directory exists + } + + // get index and generate output file name + file = dir + "/" + p.gen_index_str() + ".node"; + + // get (optional) extra node file name (in case there is matching + // .poly file. + exfile = file + ".ex"; + + // load extra nodes if they exist + excount = 0; + if ( (fd = fopen(exfile, "r")) != NULL ) { + int junki; + fscanf(fd, "%d %d %d %d", &excount, &junki, &junki, &junki); + + if ( excount > MAX_EX_NODES - 1 ) { + printf("Error, too many 'extra' nodes, increase array size\n"); + exit(-1); + } else { + printf(" Expecting %d 'extra' nodes\n", excount); + } + + for ( i = 1; i <= excount; i++ ) { + fscanf(fd, "%d %lf %lf %lf\n", &junki, + &exnodes[i][0], &exnodes[i][1], &exnodes[i][2]); + printf("(extra) %d %.2f %.2f %.2f\n", + i, exnodes[i][0], exnodes[i][1], exnodes[i][2]); + } + fclose(fd); + } + + printf("Creating node file: %s\n", file); + fd = fopen(file, "w"); + + // first count regular nodes to generate header + count = 0; + for ( j = rowmin; j <= rowmax; j++ ) { + for ( i = colmin; i <= colmax; i++ ) { + if ( out_data[i][j] > -9000.0 ) { + count++; + } + } + // printf(" count = %d\n", count); + } + fprintf(fd, "%d 2 1 0\n", count + excount); + + // now write out extra node data + for ( i = 1; i <= excount; i++ ) { + fprintf(fd, "%d %.2f %.2f %.2f\n", + i, exnodes[i][0], exnodes[i][1], exnodes[i][2]); + } + + // write out actual node data + count = excount + 1; + for ( j = rowmin; j <= rowmax; j++ ) { + for ( i = colmin; i <= colmax; i++ ) { + if ( out_data[i][j] > -9000.0 ) { + fprintf(fd, "%d %.2f %.2f %.2f\n", + count++, + originx + (double)i * col_step, + originy + (double)j * row_step, + out_data[i][j]); + } + } + // printf(" count = %d\n", count); + } + + fclose(fd); +} +#endif + + +FGArray::~FGArray( void ) { + // printf("class FGArray DEstructor called.\n"); + delete [] in_data; + // delete [] out_data; +} + + +// $Log$ +// Revision 1.8 1999/04/05 02:15:23 curt +// Make dem fitting more robust in cases when no dem file available. +// +// Revision 1.7 1999/03/27 14:05:10 curt +// More sensible handling of the case where no dem file for this tile exists +// (or has been generated). +// +// Revision 1.6 1999/03/27 05:20:13 curt +// Handle corner nodes separately from the rest of the fitted nodes. +// Fixed some "const" related warnings. +// +// Revision 1.5 1999/03/25 19:03:50 curt +// Minor tweaks related to FGBucket usage. +// +// Revision 1.4 1999/03/20 20:32:51 curt +// First mostly successful tile triangulation works. There's plenty of tweaking +// to do, but we are marching in the right direction. +// +// Revision 1.3 1999/03/17 23:48:17 curt +// Removed forced -g compile flag. +// Fixed a couple compiler warnings. +// +// Revision 1.2 1999/03/13 23:50:26 curt +// Tweaked output formatting a bit. +// +// Revision 1.1 1999/03/13 18:45:02 curt +// Initial revision. (derived from libDEM.a code.) +// diff --git a/Tools/Array/array.hxx b/Tools/Array/array.hxx new file mode 100644 index 000000000..2ec9e2a51 --- /dev/null +++ b/Tools/Array/array.hxx @@ -0,0 +1,146 @@ +// array.hxx -- Array management class +// +// Written by Curtis Olson, started March 1998. +// +// Copyright (C) 1998 - 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _ARRAY_HXX +#define _ARRAY_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include <Include/compiler.h> + +#include <vector> + +#include <Bucket/newbucket.hxx> +#include <Math/point3d.hxx> +#include <Misc/fgstream.hxx> + +#include <Main/construct_types.hxx> + +FG_USING_STD(vector); + + +#define ARRAY_SIZE 1200 +#define ARRAY_SIZE_1 1201 + + +class FGArray { + +private: + + // file pointer for input + // gzFile fd; + fg_gzifstream *in; + + // coordinates (in arc seconds) of south west corner + double originx, originy; + + // number of columns and rows + int cols, rows; + + // Distance between column and row data points (in arc seconds) + double col_step, row_step; + + // pointers to the actual grid data allocated here + float (*in_data)[ARRAY_SIZE_1]; + // float (*out_data)[ARRAY_SIZE_1]; + + // output nodes + point_list corner_list; + point_list node_list; + +public: + + // Constructor + FGArray( void ); + FGArray( const string& file ); + + // Destructor + ~FGArray( void ); + + // open an Array file (use "-" if input is coming from stdin) + int open ( const string& file ); + + // close a Array file + int close(); + + // parse a Array file + int parse( FGBucket& b ); + + // Use least squares to fit a simpler data set to dem data. + // Return the number of fitted nodes + int fit( double error ); + + // add a node to the output corner node list + void add_corner_node( int i, int j, double val ); + + // add a node to the output fitted node list + void add_fit_node( int i, int j, double val ); + + // return the current altitude based on grid data. We should + // rewrite this to interpolate exact values, but for now this is + // good enough + double interpolate_altitude( double lon, double lat ) const; + + // Informational methods + inline double get_originx() const { return originx; } + inline double get_originy() const { return originy; } + inline int get_cols() const { return cols; } + inline int get_rows() const { return rows; } + inline double get_col_step() const { return col_step; } + inline double get_row_step() const { return row_step; } + + inline point_list get_corner_node_list() const { return corner_list; } + inline point_list get_fit_node_list() const { return node_list; } +}; + + +#endif // _ARRAY_HXX + + +// $Log$ +// Revision 1.6 1999/04/05 02:15:24 curt +// Make dem fitting more robust in cases when no dem file available. +// +// Revision 1.5 1999/03/29 13:11:02 curt +// Shuffled stl type names a bit. +// Began adding support for tri-fanning (or maybe other arrangments too.) +// +// Revision 1.4 1999/03/27 05:20:14 curt +// Handle corner nodes separately from the rest of the fitted nodes. +// Fixed some "const" related warnings. +// +// Revision 1.3 1999/03/20 20:32:52 curt +// First mostly successful tile triangulation works. There's plenty of tweaking +// to do, but we are marching in the right direction. +// +// Revision 1.2 1999/03/13 23:50:27 curt +// Tweaked output formatting a bit. +// +// Revision 1.1 1999/03/13 18:45:02 curt +// Initial revision. (derived from libDEM.a code.) +// diff --git a/Tools/Array/testarray.cxx b/Tools/Array/testarray.cxx new file mode 100644 index 000000000..1a0a4d55c --- /dev/null +++ b/Tools/Array/testarray.cxx @@ -0,0 +1,33 @@ +#include <Bucket/newbucket.hxx> + +#include "array.hxx" + +main(int argc, char **argv) { + double lon, lat; + + if ( argc != 2 ) { + cout << "Usage: " << argv[0] << " work_dir" << endl; + exit(-1); + } + + string work_dir = argv[1]; + + lon = -146.248360; lat = 61.133950; // PAVD (Valdez, AK) + lon = -110.664244; lat = 33.352890; // P13 + + FGBucket b( lon, lat ); + string base = b.gen_base_path(); + string path = work_dir + "/Scenery/" + base; + + string arrayfile = path + "/" + b.gen_index_str() + ".dem"; + cout << "arrayfile = " << arrayfile << endl; + + FGArray a(arrayfile); + a.parse( b ); + + lon *= 3600; + lat *= 3600; + cout << " " << a.interpolate_altitude(lon, lat) << endl; + + a.fit( 100 ); +} diff --git a/Tools/AssemTris/Makefile.am b/Tools/AssemTris/Makefile.am new file mode 100644 index 000000000..5e830c88b --- /dev/null +++ b/Tools/AssemTris/Makefile.am @@ -0,0 +1,62 @@ +#--------------------------------------------------------------------------- +# Makefile +# +# Written by Curtis Olson, started January 1998. +# +# Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +bin_PROGRAMS = assemtris + +assemtris_SOURCES = assemtris.cxx assemtris.hxx + +assemtris_LDADD = \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(base_LIBS) + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.5 1998/11/04 23:01:45 curt +# Changes to the automake/autoconf system to reduce the number of libraries +# that are unnecessarily linked into the various executables. +# +# Revision 1.4 1998/09/25 19:35:25 curt +# Renamed assemtris.[ch] to assemtris.[ch]xx +# +# Revision 1.3 1998/07/30 23:49:23 curt +# Removed libtool support. +# +# Revision 1.2 1998/04/14 02:25:59 curt +# Code reorganizations. Added a Lib/ directory for more general libraries. +# +# Revision 1.1 1998/04/08 22:54:57 curt +# Adopted Gnu automake/autoconf system. +# +# Revision 1.2 1998/01/21 02:55:46 curt +# Incorporated new make system from Bob Kuehne <rpk@sgi.com>. +# +# Revision 1.1 1998/01/15 02:45:25 curt +# Initial revision. +# + diff --git a/Tools/AssemTris/assemtris.cxx b/Tools/AssemTris/assemtris.cxx new file mode 100644 index 000000000..1c87851e3 --- /dev/null +++ b/Tools/AssemTris/assemtris.cxx @@ -0,0 +1,600 @@ +// assemtris.cxx -- reassemble the pieces produced by splittris +// +// Written by Curtis Olson, started January 1998. +// +// Copyright (C) 1997 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> // for atoi() +#include <string.h> +#include <sys/stat.h> // for stat() +#include <unistd.h> // for stat() + +#include "assemtris.hxx" + +#include <Include/fg_constants.h> +#include <Bucket/bucketutils.h> + + +// #define OFFSET_LON 0.1 +// #define OFFSET_LAT 0.1 + +#define OFFSET_LON 0.0 +#define OFFSET_LAT 0.0 + +int nodecount = 0; +int excount = 0; + +static double nodes[MAX_NODES][3]; +static double exnodes[MAX_NODES][3]; + + +fgBUCKET my_index; +fgBUCKET ne_index, nw_index, sw_index, se_index; +fgBUCKET north_index, south_index, east_index, west_index; + + +// return the file base name ( foo/bar/file.ext = file.ext ) +void extract_file(char *in, char *base) { + int len, i; + + len = strlen(in); + + i = len - 1; + while ( (i >= 0) && (in[i] != '/') ) { + i--; + } + + in += (i + 1); + strcpy(base, in); +} + + +// return the file path name ( foo/bar/file.ext = foo/bar ) +void extract_path(char *in, char *base) { + int len, i; + + len = strlen(in); + strcpy(base, in); + + i = len - 1; + while ( (i >= 0) && (in[i] != '/') ) { + i--; + } + + base[i] = '\0'; +} + + +// check to see if specified node is in the extra list +int is_extra_node(double *n) { + int i; + + for ( i = 1; i <= excount; i++ ) { + // we only check lon/lat in case the height got fooled with + // along the way + if ( (fabs(n[0] - exnodes[i][0]) < FG_EPSILON) && + (fabs(n[1] - exnodes[i][1]) < FG_EPSILON) ) { + return(i); + } + } + + return(0); +} + +// Read all the extra nodes. These typically define inner areas to +// exclude from triangulations. There will be a .poly file that +// refers to these by position number which assumes all the extra +// nodes come first in the generated .node file. +void read_extra_nodes(char *exfile) { + FILE *fd; + int i, junk1, junk2, junk3; + + // load extra nodes if they exist + excount = 0; + if ( (fd = fopen(exfile, "r")) != NULL ) { + printf("Found and 'extra' node file = %s\n", exfile); + fscanf(fd, "%d %d %d %d", &excount, &junk1, &junk2, &junk3); + + if ( excount > MAX_NODES - 1 ) { + printf("Error, too many 'extra' nodes, increase array size\n"); + exit(-1); + } else { + printf(" Expecting %d 'extra' nodes\n", excount); + } + + for ( i = 1; i <= excount; i++ ) { + fscanf(fd, "%d %lf %lf %lf\n", &junk1, + &exnodes[i][0], &exnodes[i][1], &exnodes[i][2]); + printf("(extra) %d %.2f %.2f %.2f\n", + i, exnodes[i][0], exnodes[i][1], exnodes[i][2]); + } + fclose(fd); + } +} + + +// check if a file exists +int file_exists(char *file) { + struct stat stat_buf; + int result; + + printf("checking %s ... ", file); + + result = stat(file, &stat_buf); + + if ( result != 0 ) { + // stat failed, no file + printf("not found.\n"); + return(0); + } else { + // stat succeeded, file exists + printf("exists.\n"); + return(1); + } +} + + +// check to see if a shared object exists +int shared_object_exists(char *basepath, char *ext, char *file) { + char scene_path[256]; + long int index; + + if ( strcmp(ext, ".sw") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&west_index, scene_path); + index = fgBucketGenIndex(&west_index); + sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&sw_index, scene_path); + index = fgBucketGenIndex(&sw_index); + sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&south_index, scene_path); + index = fgBucketGenIndex(&south_index); + sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".se") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&east_index, scene_path); + index = fgBucketGenIndex(&east_index); + sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&se_index, scene_path); + index = fgBucketGenIndex(&se_index); + sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&south_index, scene_path); + index = fgBucketGenIndex(&south_index); + sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".ne") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&east_index, scene_path); + index = fgBucketGenIndex(&east_index); + sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&ne_index, scene_path); + index = fgBucketGenIndex(&ne_index); + sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&north_index, scene_path); + index = fgBucketGenIndex(&north_index); + sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".nw") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&west_index, scene_path); + index = fgBucketGenIndex(&west_index); + sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&nw_index, scene_path); + index = fgBucketGenIndex(&nw_index); + sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&north_index, scene_path); + index = fgBucketGenIndex(&north_index); + sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".south") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.south", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&south_index, scene_path); + index = fgBucketGenIndex(&south_index); + sprintf(file, "%s/%s/%ld.1.north", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".north") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.north", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&north_index, scene_path); + index = fgBucketGenIndex(&north_index); + sprintf(file, "%s/%s/%ld.1.south", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".west") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.west", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&west_index, scene_path); + index = fgBucketGenIndex(&west_index); + sprintf(file, "%s/%s/%ld.1.east", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".east") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.east", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&east_index, scene_path); + index = fgBucketGenIndex(&east_index); + sprintf(file, "%s/%s/%ld.1.west", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".body") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.body", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + return(0); +} + + +// my custom file opening routine ... don't open if a shared edge or +// vertex alread exists +FILE *my_open(char *basename, char *basepath, char *ext) { + FILE *fp; + char filename[256]; + + // check if a shared object already exists + if ( shared_object_exists(basepath, ext, filename) ) { + // not an actual file open error, but we've already got the + // shared edge, so we don't want to create another one + fp = fopen(filename, "r"); + printf("Opening %s\n", filename); + return(fp); + } else { + // open the file + printf("not opening\n"); + return(NULL); + } +} + + +// given a file pointer, read all the gdn (geodetic nodes from it.) +// The specified offset values (in arcsec) are used to overlap the +// edges of the tile slightly to cover gaps induced by floating point +// precision problems. 1 arcsec == about 100 feet so 0.01 arcsec == +// about 1 foot +void read_nodes(FILE *fp, double offset_lon, double offset_lat) { + double n[3]; + char line[256]; + int ex_index; + + offset_lon = offset_lat = 0.0; + + while ( fgets(line, 250, fp) != NULL ) { + if ( strncmp(line, "gdn ", 4) == 0 ) { + sscanf(line, "gdn %lf %lf %lf\n", &n[0], &n[1], &n[2]); + + ex_index = is_extra_node(n); + + if ( ex_index == 0 ) { + // not an extra node + nodes[nodecount][0] = n[0] + offset_lon; + nodes[nodecount][1] = n[1] + offset_lat; + nodes[nodecount][2] = n[2]; + + // printf("read_nodes(%d) %.2f %.2f %.2f %s", nodecount, + // nodes[nodecount][0], nodes[nodecount][1], + // nodes[nodecount][2], line); + + + nodecount++; + } else { + // is an extra node + printf("found extra node %.2f %.2f %.2f\n", n[0], n[1], n[2]); + // preserve the DEM altitude for now + exnodes[ex_index][2] = n[2]; + } + } + } +} + + +// load in nodes from the various split and shared pieces to +// reconstruct a tile +void build_node_list(char *basename, char *basepath) { + char exfile[256]; + FILE *ne, *nw, *se, *sw, *north, *south, *east, *west, *body; + + // load extra nodes if they exist + strcpy(exfile, basename); + strcat(exfile, ".node.ex"); + read_extra_nodes(exfile); + + ne = my_open(basename, basepath, ".ne"); + read_nodes(ne, OFFSET_LON, OFFSET_LAT); + fclose(ne); + + nw = my_open(basename, basepath, ".nw"); + read_nodes(nw, -1.0 * OFFSET_LON, OFFSET_LAT); + fclose(nw); + + se = my_open(basename, basepath, ".se"); + read_nodes(se, OFFSET_LON, -1.0 * OFFSET_LAT); + fclose(se); + + sw = my_open(basename, basepath, ".sw"); + read_nodes(sw, -1.0 * OFFSET_LON, -1.0 * OFFSET_LAT); + fclose(sw); + + north = my_open(basename, basepath, ".north"); + read_nodes(north, 0.0, OFFSET_LAT); + fclose(north); + + south = my_open(basename, basepath, ".south"); + read_nodes(south, 0.0, -1.0 * OFFSET_LAT); + fclose(south); + + east = my_open(basename, basepath, ".east"); + read_nodes(east, OFFSET_LON, 0.0); + fclose(east); + + west = my_open(basename, basepath, ".west"); + read_nodes(west, -1.0 * OFFSET_LON, 0.0); + fclose(west); + + body = my_open(basename, basepath, ".body"); + read_nodes(body, 0.0, 0.0); + fclose(body); +} + + +// dump in WaveFront .obj format +void dump_nodes(char *basename) { + char file[256]; + FILE *fd; + int i; + + // generate output file name + strcpy(file, basename); + // len = strlen(file); + // file[len-2] = '\0'; + strcat(file, ".node"); + + // dump vertices + printf("Creating node file: %s\n", file); + printf(" writing vertices in .node format.\n"); + fd = fopen(file, "w"); + + fprintf(fd, "%d 2 1 0\n", excount + nodecount); + + // now write out extra node data + for ( i = 1; i <= excount; i++ ) { + fprintf(fd, "%d %.2f %.2f %.2f 0\n", + i, exnodes[i][0], exnodes[i][1], exnodes[i][2]); + } + + // now write out actual node data + for ( i = 0; i < nodecount; i++ ) { + fprintf(fd, "%d %.2f %.2f %.2f 0\n", excount + i + 1, + nodes[i][0], nodes[i][1], nodes[i][2]); + } + + fclose(fd); +} + + +int main(int argc, char **argv) { + char basename[256], basepath[256], temp[256]; + long int tmp_index; + int len; + + // derive base name + strcpy(basename, argv[1]); + len = strlen(basename); + + // find the base path of the file + extract_path(basename, basepath); + extract_path(basepath, basepath); + extract_path(basepath, basepath); + printf("%s\n", basepath); + + // find the index of the current file + extract_file(basename, temp); + // len = strlen(temp); + // if ( len >= 2 ) { + // temp[len-2] = '\0'; + // } + tmp_index = atoi(temp); + printf("%ld\n", tmp_index); + fgBucketParseIndex(tmp_index, &my_index); + + printf("bucket = %d %d %d %d\n", + my_index.lon, my_index.lat, my_index.x, my_index.y); + // generate the indexes of the neighbors + fgBucketOffset(&my_index, &ne_index, 1, 1); + fgBucketOffset(&my_index, &nw_index, -1, 1); + fgBucketOffset(&my_index, &se_index, 1, -1); + fgBucketOffset(&my_index, &sw_index, -1, -1); + + fgBucketOffset(&my_index, &north_index, 0, 1); + fgBucketOffset(&my_index, &south_index, 0, -1); + fgBucketOffset(&my_index, &east_index, 1, 0); + fgBucketOffset(&my_index, &west_index, -1, 0); + + // printf("Corner indexes = %ld %ld %ld %ld\n", + // ne_index, nw_index, sw_index, se_index); + // printf("Edge indexes = %ld %ld %ld %ld\n", + // north_index, south_index, east_index, west_index); + + + // load the input data files + build_node_list(basename, basepath); + + // dump in WaveFront .obj format + dump_nodes(basename); + + return(0); +} + + +// $Log$ +// Revision 1.3 1998/11/02 18:25:40 curt +// Check for __CYGWIN__ (b20) as well as __CYGWIN32__ (pre b20 compilers) +// Other misc. tweaks. +// +// Revision 1.2 1998/09/25 19:38:01 curt +// Minor tweaks so that this actually compiles. +// +// Revision 1.1 1998/09/25 19:35:29 curt +// Renamed assemtris.[ch] to assemtris.[ch]xx +// +// Revision 1.13 1998/09/21 20:56:30 curt +// Changes to avoid setting airport area nodes back to their original +// elevations if they have been changed. +// +// +// Revision 1.12 1998/09/09 16:24:51 curt +// Fixed a bug in the handling of exclude files which was causing +// a crash by calling fclose() on an invalid file handle. +// Removed overlapping offsets. +// +// Revision 1.11 1998/08/06 12:47:59 curt +// Removed overlap in tiles as a test. +// +// Revision 1.10 1998/07/21 04:34:20 curt +// Mods to handle extra nodes (i.e. preserve cutouts). +// +// Revision 1.9 1998/07/04 00:55:39 curt +// typedef'd struct fgBUCKET. +// +// Revision 1.8 1998/06/01 17:58:19 curt +// Added a slight border overlap to try to minimize pixel wide gaps between +// tiles due to round off error. This is not a perfect solution, but helps. +// +// Revision 1.7 1998/04/14 02:26:00 curt +// Code reorganizations. Added a Lib/ directory for more general libraries. +// +// Revision 1.6 1998/04/08 22:54:58 curt +// Adopted Gnu automake/autoconf system. +// +// Revision 1.5 1998/03/03 16:00:52 curt +// More c++ compile tweaks. +// +// Revision 1.4 1998/01/31 00:41:23 curt +// Made a few changes converting floats to doubles. +// +// Revision 1.3 1998/01/27 18:37:00 curt +// Lots of updates to get back in sync with changes made over in .../Src/ +// +// Revision 1.2 1998/01/15 21:33:36 curt +// Assembling triangles and building a new .node file with the proper shared +// vertices now works. Now we just have to use the shared normals and we'll +// be all set. +// +// Revision 1.1 1998/01/15 02:45:26 curt +// Initial revision. +// + diff --git a/Tools/AssemTris/assemtris.hxx b/Tools/AssemTris/assemtris.hxx new file mode 100644 index 000000000..3c96881f4 --- /dev/null +++ b/Tools/AssemTris/assemtris.hxx @@ -0,0 +1,51 @@ +// splittris.hxx -- reassemble the pieces produced by splittris +// +// Written by Curtis Olson, started January 1998. +// +// Copyright (C) 1997 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + + +#ifndef ASSEMTRIS_H +#define ASSEMTRIS_H + + +#include <stdio.h> +#include <string.h> + + +#define MAX_NODES 200000 +#define MAX_TRIS 400000 + + +#endif // SPLITTRIS_H + + +// $Log$ +// Revision 1.2 1998/09/25 19:38:03 curt +// Minor tweaks so that this actually compiles. +// +// Revision 1.1 1998/09/25 19:35:31 curt +// Renamed assemtris.[ch] to assemtris.[ch]xx +// +// Revision 1.1 1998/01/15 02:45:26 curt +// Initial revision. +// + diff --git a/Tools/CVSROOT/checkoutlist b/Tools/CVSROOT/checkoutlist new file mode 100644 index 000000000..b04b3501f --- /dev/null +++ b/Tools/CVSROOT/checkoutlist @@ -0,0 +1,13 @@ +# The "checkoutlist" file is used to support additional version controlled +# administrative files in $CVSROOT/CVSROOT, such as template files. +# +# The first entry on a line is a filename which will be checked out from +# the corresponding RCS file in the $CVSROOT/CVSROOT directory. +# The remainder of the line is an error message to use if the file cannot +# be checked out. +# +# File format: +# +# [<whitespace>]<filename><whitespace><error message><end-of-line> +# +# comment lines begin with '#' diff --git a/Tools/CVSROOT/commitinfo b/Tools/CVSROOT/commitinfo new file mode 100644 index 000000000..b19e7b7a6 --- /dev/null +++ b/Tools/CVSROOT/commitinfo @@ -0,0 +1,15 @@ +# The "commitinfo" file is used to control pre-commit checks. +# The filter on the right is invoked with the repository and a list +# of files to check. A non-zero exit of the filter program will +# cause the commit to be aborted. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/Tools/CVSROOT/cvswrappers b/Tools/CVSROOT/cvswrappers new file mode 100644 index 000000000..5047bf1c5 --- /dev/null +++ b/Tools/CVSROOT/cvswrappers @@ -0,0 +1,22 @@ +# This file describes wrappers and other binary files to CVS. +# +# Wrappers are the concept where directories of files are to be +# treated as a single file. The intended use is to wrap up a wrapper +# into a single tar such that the tar archive can be treated as a +# single binary file in CVS. +# +# To solve the problem effectively, it was also necessary to be able to +# prevent rcsmerge from merging these files. +# +# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers) +# +# wildcard [option value][option value]... +# +# where option is one of +# -f from cvs filter value: path to filter +# -t to cvs filter value: path to filter +# -m update methodology value: MERGE or COPY +# +# and value is a single-quote delimited value. +# +# For example: diff --git a/Tools/CVSROOT/editinfo b/Tools/CVSROOT/editinfo new file mode 100644 index 000000000..d78886c15 --- /dev/null +++ b/Tools/CVSROOT/editinfo @@ -0,0 +1,21 @@ +# The "editinfo" file is used to allow verification of logging +# information. It works best when a template (as specified in the +# rcsinfo file) is provided for the logging procedure. Given a +# template with locations for, a bug-id number, a list of people who +# reviewed the code before it can be checked in, and an external +# process to catalog the differences that were code reviewed, the +# following test can be applied to the code: +# +# Making sure that the entered bug-id number is correct. +# Validating that the code that was reviewed is indeed the code being +# checked in (using the bug-id number or a seperate review +# number to identify this particular code set.). +# +# If any of the above test failed, then the commit would be aborted. +# +# Actions such as mailing a copy of the report to each reviewer are +# better handled by an entry in the loginfo file. +# +# One thing that should be noted is the the ALL keyword is not +# supported. There can be only one entry that matches a given +# repository. diff --git a/Tools/CVSROOT/loginfo b/Tools/CVSROOT/loginfo new file mode 100644 index 000000000..226e93771 --- /dev/null +++ b/Tools/CVSROOT/loginfo @@ -0,0 +1,19 @@ +# The "loginfo" file is used to control where "cvs commit" log information is +# sent. The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, the remainder of the line is a +# filter program that should expect log information on its standard input +# +# If the repository name does not match any of the regular expressions in the +# first field of this file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". +# +# The filter program may use one and only one "%s" modifier (ala printf). If +# such a "%s" is specified in the filter program, a brief title is included +# (as one argument, enclosed in single quotes) showing the relative directory +# name and listing the modified file names. +# +# For example: +#DEFAULT (echo ""; who am i; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog diff --git a/Tools/CVSROOT/modules b/Tools/CVSROOT/modules new file mode 100644 index 000000000..cb9e9efc9 --- /dev/null +++ b/Tools/CVSROOT/modules @@ -0,0 +1,26 @@ +# Three different line formats are valid: +# key -a aliases... +# key [options] directory +# key [options] directory files... +# +# Where "options" are composed of: +# -i prog Run "prog" on "cvs commit" from top-level of module. +# -o prog Run "prog" on "cvs checkout" of module. +# -e prog Run "prog" on "cvs export" of module. +# -t prog Run "prog" on "cvs rtag" of module. +# -u prog Run "prog" on "cvs update" of module. +# -d dir Place module in directory "dir" instead of module name. +# -l Top-level directory only -- do not recurse. +# +# NOTE: If you change any of the "Run" options above, you'll have to +# release and re-checkout any working directories of these modules. +# +# And "directory" is a path to a directory relative to $CVSROOT. +# +# The "-a" option specifies an alias. An alias is interpreted as if +# everything on the right of the "-a" had been typed on the command line. +# +# You can encode a module within a module by using the special '&' +# character to interpose another module into the current module. This +# can be useful for creating a module that consists of many directories +# spread out over the entire source repository. diff --git a/Tools/CVSROOT/notify b/Tools/CVSROOT/notify new file mode 100644 index 000000000..34f0bc288 --- /dev/null +++ b/Tools/CVSROOT/notify @@ -0,0 +1,12 @@ +# The "notify" file controls where notifications from watches set by +# "cvs watch add" or "cvs edit" are sent. The first entry on a line is +# a regular expression which is tested against the directory that the +# change is being made to, relative to the $CVSROOT. If it matches, +# then the remainder of the line is a filter program that should contain +# one occurrence of %s for the user to notify, and information on its +# standard input. +# +# "ALL" or "DEFAULT" can be used in place of the regular expression. +# +# For example: +#ALL mail %s -s "CVS notification" diff --git a/Tools/CVSROOT/rcsinfo b/Tools/CVSROOT/rcsinfo new file mode 100644 index 000000000..49e59f4d0 --- /dev/null +++ b/Tools/CVSROOT/rcsinfo @@ -0,0 +1,13 @@ +# The "rcsinfo" file is used to control templates with which the editor +# is invoked on commit and import. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/Tools/CVSROOT/taginfo b/Tools/CVSROOT/taginfo new file mode 100644 index 000000000..274a46dd5 --- /dev/null +++ b/Tools/CVSROOT/taginfo @@ -0,0 +1,20 @@ +# The "taginfo" file is used to control pre-tag checks. +# The filter on the right is invoked with the following arguments: +# +# $1 -- tagname +# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d +# $3 -- repository +# $4-> file revision [file revision ...] +# +# A non-zero exit of the filter program will cause the tag to be aborted. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/Tools/Clipper/Makefile.am b/Tools/Clipper/Makefile.am new file mode 100644 index 000000000..87a59e28b --- /dev/null +++ b/Tools/Clipper/Makefile.am @@ -0,0 +1,16 @@ +noinst_LIBRARIES = libClipper.a + +libClipper_a_SOURCES = clipper.cxx clipper.hxx + +bin_PROGRAMS = testclipper + +testclipper_SOURCES = testclipper.cxx + +testclipper_LDADD = $(top_builddir)/Tools/Construct/Clipper/libClipper.a \ + $(top_builddir)/Tools/Lib/Polygon/libPolygon.a \ + $(top_builddir)/Lib/Debug/libDebug.a \ + $(top_builddir)/Lib/Misc/libMisc.a \ + $(top_builddir)/Lib/zlib/libz.a \ + -lgfc -lgpc + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib -I$(top_builddir)/Tools/Lib diff --git a/Tools/Clipper/clipper.cxx b/Tools/Clipper/clipper.cxx new file mode 100644 index 000000000..a088f0ffb --- /dev/null +++ b/Tools/Clipper/clipper.cxx @@ -0,0 +1,304 @@ +// clipper.cxx -- top level routines to take a series of arbitrary areas and +// produce a tight fitting puzzle pieces that combine to make a +// tile +// +// Written by Curtis Olson, started February 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + + +#include <Debug/logstream.hxx> +#include <Include/fg_constants.h> +#include <Misc/fgstream.hxx> +#include <Polygon/names.hxx> + +#include "clipper.hxx" + + +// Constructor +FGClipper::FGClipper( void ) { +} + + +// Destructor +FGClipper::~FGClipper( void ) { +} + + +// Initialize Clipper (allocate and/or connect structures) +bool FGClipper::init() { + v_list.num_vertices = 0; + v_list.vertex = new gpc_vertex[FG_MAX_VERTICES];; + + return true; +} + + +// Load a polygon definition file +bool FGClipper::load_polys(const string& path) { + string poly_name; + AreaType poly_type = DefaultArea; + int contours, count, i, j; + double startx, starty, x, y, lastx, lasty; + + FG_LOG( FG_CLIPPER, FG_INFO, "Loading " << path << " ..." ); + + fg_gzifstream in( path ); + + if ( !in ) { + FG_LOG( FG_CLIPPER, FG_ALERT, "Cannot open file: " << path ); + exit(-1); + } + + gpc_polygon *poly = new gpc_polygon; + poly->num_contours = 0; + poly->contour = NULL; + + in >> skipcomment; + while ( !in.eof() ) { + in >> poly_name; + cout << "poly name = " << poly_name << endl; + poly_type = get_area_type( poly_name ); + cout << "poly type (int) = " << (int)poly_type << endl; + in >> contours; + cout << "num contours = " << contours << endl; + + for ( i = 0; i < contours; ++i ) { + in >> count; + + if ( count < 3 ) { + FG_LOG( FG_CLIPPER, FG_ALERT, + "Polygon with less than 3 data points." ); + exit(-1); + } + + in >> startx; + in >> starty; + v_list.vertex[0].x = startx; + v_list.vertex[0].y = starty; + FG_LOG( FG_CLIPPER, FG_BULK, "0 = " + << startx << ", " << starty ); + + for ( j = 1; j < count - 1; ++j ) { + in >> x; + in >> y; + v_list.vertex[j].x = x; + v_list.vertex[j].y = y; + FG_LOG( FG_CLIPPER, FG_BULK, j << " = " << x << ", " << y ); + } + v_list.num_vertices = count - 1; + + in >> lastx; + in >> lasty; + + if ( (fabs(startx - lastx) < FG_EPSILON) + && (fabs(starty - lasty) < FG_EPSILON) ) { + // last point same as first, discard + } else { + v_list.vertex[count - 1].x = lastx; + v_list.vertex[count - 1].y = lasty; + ++v_list.num_vertices; + FG_LOG( FG_CLIPPER, FG_BULK, count - 1 << " = " + << lastx << ", " << lasty ); + } + + gpc_add_contour( poly, &v_list ); + + } + + in >> skipcomment; + } + + int area = (int)poly_type; + if ( area < FG_MAX_AREA_TYPES ) { + polys_in.polys[area].push_back(poly); + } else { + FG_LOG( FG_CLIPPER, FG_ALERT, "Polygon type out of range = " + << (int)poly_type); + exit(-1); + } + + // FILE *ofp= fopen("outfile", "w"); + // gpc_write_polygon(ofp, &polys.landuse); + + return true; +} + + +// Do actually clipping work +bool FGClipper::clip_all(const point2d& min, const point2d& max) { + gpc_polygon accum, result_union, tmp; + gpc_polygon *result_diff, *remains; + gpcpoly_iterator current, last; + + FG_LOG( FG_CLIPPER, FG_INFO, "Running master clipper" ); + + accum.num_contours = 0; + + cout << " (" << min.x << "," << min.y << ") (" + << max.x << "," << max.y << ")" << endl; + + // set up clipping tile + v_list.vertex[0].x = min.x; + v_list.vertex[0].y = min.y; + + v_list.vertex[1].x = max.x; + v_list.vertex[1].y = min.y; + + v_list.vertex[2].x = max.x; + v_list.vertex[2].y = max.y; + + v_list.vertex[3].x = min.x; + v_list.vertex[3].y = max.y; + + v_list.num_vertices = 4; + + polys_in.safety_base.num_contours = 0; + polys_in.safety_base.contour = NULL; + gpc_add_contour( &polys_in.safety_base, &v_list ); + + // process polygons in priority order + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + // cout << "num polys of this type = " + // << polys_in.polys[i].size() << endl; + current = polys_in.polys[i].begin(); + last = polys_in.polys[i].end(); + for ( ; current != last; ++current ) { + FG_LOG( FG_CLIPPER, FG_DEBUG, get_area_name( (AreaType)i ) + << " = " << (*current)->contour->num_vertices ); + +#ifdef EXTRA_SAFETY_CLIP + // clip to base tile + gpc_polygon_clip(GPC_INT, *current, &polys_in.safety_base, &tmp); +#else + tmp = *current; +#endif + + // clip current polygon against previous higher priority + // stuff + result_diff = new gpc_polygon; + result_diff->num_contours = 0; + result_diff->contour = NULL; + + if ( accum.num_contours == 0 ) { + *result_diff = tmp; + result_union = tmp; + } else { + // cout << "DIFF: tmp.num_contours = " << tmp.num_contours + // << " accum.num_contours = " << accum.num_contours + // << endl; + // tmp output accum + FILE *ofp= fopen("tmp-debug", "w"); + gpc_write_polygon(ofp, &tmp); + fclose(ofp); + + ofp= fopen("accum-debug", "w"); + gpc_write_polygon(ofp, &accum); + fclose(ofp); + + gpc_polygon_clip(GPC_DIFF, &tmp, &accum, result_diff); + gpc_polygon_clip(GPC_UNION, &tmp, &accum, &result_union); + } + + /* + cout << "original contours = " << tmp.num_contours << endl; + + for ( int j = 0; j < tmp.num_contours; j++ ) { + for (int k = 0;k < tmp.contour[j].num_vertices;k++ ) { + cout << tmp.contour[j].vertex[k].x << "," + << tmp.contour[j].vertex[k].y << endl; + } + } + + cout << "clipped contours = " << result_diff->num_contours << endl; + + for ( int j = 0; j < result_diff->num_contours; j++ ) { + for (int k = 0;k < result_diff->contour[j].num_vertices;k++ ) { + cout << result_diff->contour[j].vertex[k].x << "," + << result_diff->contour[j].vertex[k].y << endl; + } + } + */ + + // only add to output list if the clip left us with a polygon + if ( result_diff->num_contours > 0 ) { + polys_clipped.polys[i].push_back(result_diff); + } + accum = result_union; + } + } + + // finally, what ever is left over goes to base terrain + + // clip to accum against original base tile + remains = new gpc_polygon; + remains->num_contours = 0; + remains->contour = NULL; + gpc_polygon_clip(GPC_DIFF, &polys_in.safety_base, &accum, + remains); + if ( remains->num_contours > 0 ) { + polys_clipped.polys[0].push_back(remains); + } + + // tmp output accum + FILE *ofp= fopen("accum", "w"); + gpc_write_polygon(ofp, &accum); + fclose(ofp); + + // tmp output safety_base + ofp= fopen("remains", "w"); + gpc_write_polygon(ofp, remains); + fclose(ofp); + + return true; +} + + +// $Log$ +// Revision 1.9 1999/03/31 23:46:38 curt +// Debuggin output tweaks. +// +// Revision 1.8 1999/03/30 23:49:22 curt +// Added some debugging output. +// +// Revision 1.7 1999/03/30 13:41:38 curt +// Working towards better handling of multi-contoured polygons. +// +// Revision 1.6 1999/03/27 05:20:53 curt +// Pass along default area explicitely to triangulator. +// +// Revision 1.5 1999/03/19 22:28:46 curt +// Only add non-null polygons to output list. +// +// Revision 1.4 1999/03/19 00:26:18 curt +// Fixed a clipping bug (polygons specified in wrong order). +// Touched up a few compiler warnings. +// +// Revision 1.3 1999/03/17 23:48:58 curt +// minor renaming and a bit of rearranging. +// +// Revision 1.2 1999/03/13 23:51:33 curt +// Renamed main.cxx to testclipper.cxx +// Converted clipper routines to a class FGClipper. +// +// Revision 1.1 1999/03/01 15:39:39 curt +// Initial revision. +// diff --git a/Tools/Clipper/clipper.hxx b/Tools/Clipper/clipper.hxx new file mode 100644 index 000000000..469691a2a --- /dev/null +++ b/Tools/Clipper/clipper.hxx @@ -0,0 +1,129 @@ +// clipper.hxx -- top level routines to take a series of arbitrary areas and +// produce a tight fitting puzzle pieces that combine to make a +// tile +// +// Written by Curtis Olson, started February 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + + +#ifndef _CLIPPER_HXX +#define _CLIPPER_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include <Include/compiler.h> + + +// include Generic Polygon Clipping Library +// +// http://www.cs.man.ac.uk/aig/staff/alan/software/ +// +extern "C" { +#include <gpc.h> +} + +#include STL_STRING +#include <vector> + +FG_USING_STD(string); +FG_USING_STD(vector); + + +typedef vector < gpc_polygon * > gpcpoly_container; +typedef gpcpoly_container::iterator gpcpoly_iterator; +typedef gpcpoly_container::const_iterator const_gpcpoly_iterator; + + +#define FG_MAX_AREA_TYPES 20 +#define EXTRA_SAFETY_CLIP +#define FG_MAX_VERTICES 100000 + + +class point2d { +public: + double x, y; +}; + + +class FGgpcPolyList { +public: + gpcpoly_container polys[FG_MAX_AREA_TYPES]; + gpc_polygon safety_base; +}; + + +class FGClipper { + +private: + + gpc_vertex_list v_list; + // static gpc_polygon poly; + FGgpcPolyList polys_in, polys_clipped; + +public: + + // Constructor + FGClipper( void ); + + // Destructor + ~FGClipper( void ); + + // Initialize Clipper (allocate and/or connect structures) + bool init(); + + // Load a polygon definition file + bool load_polys(const string& path); + + // Do actually clipping work + bool clip_all(const point2d& min, const point2d& max); + + // return output poly list + inline FGgpcPolyList get_polys_clipped() const { return polys_clipped; } +}; + + +#endif // _CLIPPER_HXX + + +// $Log$ +// Revision 1.5 1999/03/19 00:26:19 curt +// Fixed a clipping bug (polygons specified in wrong order). +// Touched up a few compiler warnings. +// +// Revision 1.4 1999/03/18 04:31:10 curt +// Let's not pass copies of huge structures on the stack ... ye might see a +// segfault ... :-) +// +// Revision 1.3 1999/03/17 23:48:59 curt +// minor renaming and a bit of rearranging. +// +// Revision 1.2 1999/03/13 23:51:34 curt +// Renamed main.cxx to testclipper.cxx +// Converted clipper routines to a class FGClipper. +// +// Revision 1.1 1999/03/01 15:39:39 curt +// Initial revision. +// diff --git a/Tools/Clipper/testclipper.cxx b/Tools/Clipper/testclipper.cxx new file mode 100644 index 000000000..2a0477b48 --- /dev/null +++ b/Tools/Clipper/testclipper.cxx @@ -0,0 +1,119 @@ +// main.cxx -- sample use of the clipper lib +// +// Written by Curtis Olson, started February 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + + +#include <Debug/logstream.hxx> +#include <Bucket/newbucket.hxx> + +#include "clipper.hxx" + + +int main( int argc, char **argv ) { + point2d global_min, global_max; + + fglog().setLogLevels( FG_ALL, FG_DEBUG ); + + global_min.x = global_min.y = 200; + global_max.y = global_max.x = -200; + + FGClipper clipper; + clipper.init(); + + if ( argc < 2 ) { + FG_LOG( FG_CLIPPER, FG_ALERT, "Usage: " << argv[0] + << " file1 file2 ..." ); + exit(-1); + } + + // process all specified polygon files + for ( int i = 1; i < argc; i++ ) { + string full_path = argv[i]; + + // determine bucket for this polygon + int pos = full_path.rfind("/"); + string file_name = full_path.substr(pos + 1); + cout << "file name = " << file_name << endl; + + pos = file_name.find("."); + string base_name = file_name.substr(0, pos); + cout << "base_name = " << base_name << endl; + + long int index; + sscanf( base_name.c_str(), "%ld", &index); + FGBucket b(index); + cout << "bucket = " << b << endl; + + // calculate bucket dimensions + point2d c, min, max; + + c.x = b.get_center_lon(); + c.y = b.get_center_lat(); + double span = bucket_span(c.y); + + if ( (c.y >= -89.0) && (c.y < 89.0) ) { + min.x = c.x - span / 2.0; + max.x = c.x + span / 2.0; + min.y = c.y - FG_HALF_BUCKET_SPAN; + max.y = c.y + FG_HALF_BUCKET_SPAN; + } else if ( c.y < -89.0) { + min.x = -90.0; + max.x = -89.0; + min.y = -180.0; + max.y = 180.0; + } else if ( c.y >= 89.0) { + min.x = 89.0; + max.x = 90.0; + min.y = -180.0; + max.y = 180.0; + } else { + FG_LOG ( FG_GENERAL, FG_ALERT, + "Out of range latitude in clip_and_write_poly() = " + << c.y ); + } + + if ( min.x < global_min.x ) global_min.x = min.x; + if ( min.y < global_min.y ) global_min.y = min.y; + if ( max.x > global_max.x ) global_max.x = max.x; + if ( max.y > global_max.y ) global_max.y = max.y; + + // finally, load the polygon(s) from this file + clipper.load_polys( full_path ); + } + + // do the clipping + clipper.clip_all(global_min, global_max); + + FG_LOG( FG_CLIPPER, FG_INFO, "finished main" ); + + return 0; +} + +// $Log$ +// Revision 1.1 1999/03/13 23:51:36 curt +// Renamed main.cxx to testclipper.cxx +// Converted clipper routines to a class FGClipper. +// +// Revision 1.1 1999/03/01 15:39:39 curt +// Initial revision. +// diff --git a/Tools/Combine/Makefile.am b/Tools/Combine/Makefile.am new file mode 100644 index 000000000..c8c640202 --- /dev/null +++ b/Tools/Combine/Makefile.am @@ -0,0 +1,9 @@ +noinst_LIBRARIES = libCombine.a + +libCombine_a_SOURCES = genfans.cxx genfans.hxx + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Tools/Lib \ + -I$(top_builddir)/Tools/Construct diff --git a/Tools/Combine/genfans.cxx b/Tools/Combine/genfans.cxx new file mode 100644 index 000000000..856a3d79c --- /dev/null +++ b/Tools/Combine/genfans.cxx @@ -0,0 +1,266 @@ +// genfans.cxx -- Combine individual triangles into more optimal fans. +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include "genfans.hxx" + + +// make sure the list is expanded at least to hold "n" and then push +// "i" onto the back of the "n" list. +void FGGenFans::add_and_expand( reverse_list& by_node, int n, int i ) { + int_list empty; + + int size = (int)by_node.size(); + if ( size > n ) { + // ok + } else { + // cout << "capacity = " << by_node.capacity() << endl; + // cout << "size = " << size << " n = " << n + // << " need to push = " << n - size + 1 << endl; + for ( int i = 0; i < n - size + 1; ++i ) { + by_node.push_back(empty); + } + } + + by_node[n].push_back(i); +} + + +// given an input triangle, shuffle nodes so that "center" is the +// first node, but maintain winding order. +static FGTriEle canonify( const FGTriEle& t, int center ) { + if ( t.get_n1() == center ) { + // already ok + return t; + } else if ( t.get_n2() == center ) { + return FGTriEle( t.get_n2(), t.get_n3(), t.get_n1(), 0.0 ); + } else if ( t.get_n3() == center ) { + return FGTriEle( t.get_n3(), t.get_n1(), t.get_n2(), 0.0 ); + } else { + cout << "ERROR, index doesn't refer to this triangle!!!" << endl; + exit(-1); + } +} + +// returns a list of triangle indices +static int_list make_best_fan( const triele_list& master_tris, + const int center, const int_list& local_tris ) +{ + int_list best_result; + + // try starting with each of local_tris to find the best fan + // arrangement + for ( int start = 0; start < (int)local_tris.size(); ++start ) { + // cout << "trying with first triangle = " << local_tris[start] << endl; + + int_list tmp_result; + tmp_result.clear(); + + FGTriEle current_tri; + FGTriEle test; + current_tri = canonify( master_tris[local_tris[start]], center ); + tmp_result.push_back( local_tris[start] ); + + // follow the ring + int next = -1; + bool matches = true; + while ( (next != start) && matches ) { + // find next triangle in ring + matches = false; + for ( int i = 0; i < (int)local_tris.size(); ++i ) { + test = canonify( master_tris[local_tris[i]], center ); + if ( current_tri.get_n3() == test.get_n2() ) { + if ( i != start ) { + // cout << " next triangle = " << local_tris[i] << endl; + current_tri = test; + tmp_result.push_back( local_tris[i] ); + matches = true; + next = i; + break; + } + } + } + } + + if ( tmp_result.size() == local_tris.size() ) { + // we found a complete usage, no need to go on + // cout << "we found a complete usage, no need to go on" << endl; + best_result = tmp_result; + break; + } else if ( tmp_result.size() > best_result.size() ) { + // we found a better way to fan + // cout << "we found a better fan arrangement" << endl; + best_result = tmp_result; + } + } + + return best_result; +} + + +static bool in_fan(int index, const int_list& fan ) { + const_int_list_iterator current = fan.begin(); + const_int_list_iterator last = fan.end(); + + for ( ; current != last; ++current ) { + if ( index == *current ) { + return true; + } + } + + return false; +} + + +// recursive build fans from triangle list +fan_list FGGenFans::greedy_build( triele_list tris ) { + cout << "starting greedy build of fans" << endl; + + fans.clear(); + + while ( ! tris.empty() ) { + // cout << "building reverse_list" << endl; + reverse_list by_node; + by_node.clear(); + + // traverse the triangle list and for each node, build a list of + // triangles that attach to it. + + for ( int i = 0; i < (int)tris.size(); ++i ) { + int n1 = tris[i].get_n1(); + int n2 = tris[i].get_n2(); + int n3 = tris[i].get_n3(); + + add_and_expand( by_node, n1, i ); + add_and_expand( by_node, n2, i ); + add_and_expand( by_node, n3, i ); + } + + // find the node in the tris list that attaches to the most + // triangles + + // cout << "find most connected node" << endl; + + int_list biggest_group; + reverse_list_iterator r_current = by_node.begin(); + reverse_list_iterator r_last = by_node.end(); + int index = 0; + int counter = 0; + for ( ; r_current != r_last; ++r_current ) { + if ( r_current->size() > biggest_group.size() ) { + biggest_group = *r_current; + index = counter; + } + ++counter; + } + // cout << "triangle pool = " << tris.size() << endl; + // cout << "biggest_group = " << biggest_group.size() << endl; + // cout << "center node = " << index << endl; + + // make the best fan we can out of this group + // cout << "before make_best_fan()" << endl; + int_list best_fan = make_best_fan( tris, index, biggest_group ); + // cout << "after make_best_fan()" << endl; + + // generate point form of best_fan + int_list node_list; + node_list.clear(); + + int_list_iterator i_start = best_fan.begin(); + int_list_iterator i_current = i_start; + int_list_iterator i_last = best_fan.end(); + for ( ; i_current != i_last; ++i_current ) { + FGTriEle t = canonify( tris[*i_current], index ); + if ( i_start == i_current ) { + node_list.push_back( t.get_n1() ); + node_list.push_back( t.get_n2() ); + } + node_list.push_back( t.get_n3() ); + } + // cout << "best list size = " << node_list.size() << endl; + + // add this fan to the fan list + fans.push_back( node_list ); + + // delete the triangles in best_fan out of tris and repeat + triele_list_iterator t_current = tris.begin(); + triele_list_iterator t_last = tris.end(); + counter = 0; + while ( t_current != t_last ) { + if ( in_fan(counter, best_fan) ) { + // cout << "erasing " + // << t_current->get_n1() << "," + // << t_current->get_n2() << "," + // << t_current->get_n3() + // << " from master tri pool" + // << endl; + tris.erase( t_current ); + } else { + ++t_current; + } + ++counter; + } + } + + cout << "end of greedy build of fans" << endl; + cout << "average fan size = " << ave_size() << endl; + + return fans; +} + + +// report average fan size +double FGGenFans::ave_size() { + double sum = 0.0; + + fan_list_iterator current = fans.begin(); + fan_list_iterator last = fans.end(); + for ( ; current != last; ++current ) { + sum += current->size(); + } + + return sum / (double)fans.size(); +} + + +// $Log$ +// Revision 1.6 1999/04/05 02:16:02 curt +// Fixed a compiler warning. +// +// Revision 1.5 1999/03/31 23:46:49 curt +// Debugging output tweaks. +// +// Revision 1.4 1999/03/31 13:26:39 curt +// Debugging output tweeaks. +// +// Revision 1.3 1999/03/31 05:35:04 curt +// Fixed bug in genfans (deleting the wrong triangles from the available pool.) +// +// Revision 1.2 1999/03/30 23:50:15 curt +// Fannifier is clearly bugging ... working on debugging it. I suspect there +// is a problem related to deleting triangles from the triangle pool as they +// are combined into fans. +// +// Revision 1.1 1999/03/29 13:08:35 curt +// Initial revision. +// diff --git a/Tools/Combine/genfans.hxx b/Tools/Combine/genfans.hxx new file mode 100644 index 000000000..55af05d70 --- /dev/null +++ b/Tools/Combine/genfans.hxx @@ -0,0 +1,85 @@ +// genfans.hxx -- Combine individual triangles into more optimal fans. +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _GENFANS_HXX +#define _GENFANS_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include <Include/compiler.h> + +#include <vector> + +#include <Main/construct_types.hxx> +#include <Triangulate/trieles.hxx> + +FG_USING_STD(vector); + + +typedef vector < int_list > fan_list; +typedef fan_list::iterator fan_list_iterator; +typedef fan_list::const_iterator const_fan_list_iterator; + +typedef vector < int_list > reverse_list; +typedef reverse_list::iterator reverse_list_iterator; +typedef reverse_list::const_iterator const_reverse_list_iterator; + + + +class FGGenFans { + +private: + + fan_list fans; + + // make sure the list is expanded at least to hold "n" and then + // push "i" onto the back of the "n" list. + void add_and_expand( reverse_list& by_node, int n, int i ); + +public: + + // Constructor && Destructor + inline FGGenFans() { } + inline ~FGGenFans() { } + + // recursive build fans from triangle list + // fan_list greedy_build( triele_list tris ); + fan_list greedy_build( triele_list tris ); + + // report average fan size + double ave_size(); +}; + + +#endif // _GENFANS_HXX + + +// $Log$ +// Revision 1.1 1999/03/29 13:08:35 curt +// Initial revision. +// diff --git a/Tools/Construct/Makefile.am b/Tools/Construct/Makefile.am new file mode 100644 index 000000000..f19226325 --- /dev/null +++ b/Tools/Construct/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = \ + Array \ + Clipper \ + Combine \ + GenOutput \ + Triangulate \ + Main diff --git a/Tools/DEM/Makefile.am b/Tools/DEM/Makefile.am new file mode 100644 index 000000000..91bfd90d6 --- /dev/null +++ b/Tools/DEM/Makefile.am @@ -0,0 +1,11 @@ +noinst_LIBRARIES = libDEM.a + +libDEM_a_SOURCES = dem.cxx dem.hxx + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib + +# We can't build this with "-O2" (optimization) since this causes a seg fault +# I haven't found a way to strip this out of the CXXFLAGS, so I'm just +# setting it to "-g" +# CXXFLAGS = -g + diff --git a/Tools/DEM/dem.cxx b/Tools/DEM/dem.cxx new file mode 100644 index 000000000..e4661a3c7 --- /dev/null +++ b/Tools/DEM/dem.cxx @@ -0,0 +1,977 @@ +// dem.cxx -- DEM management class +// +// Written by Curtis Olson, started March 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <Include/compiler.h> + +#include <ctype.h> // isspace() +#include <stdlib.h> // atoi() +#include <math.h> // rint() +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> // stat() +#endif + +#ifdef FG_HAVE_STD_INCLUDES +# include <cerrno> +#else +# include <errno.h> +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> // stat() +#endif + +#include <Misc/fgstream.hxx> +#include <Misc/strutils.hxx> +#include <Include/fg_constants.h> + +#include "dem.hxx" + + +#define MAX_EX_NODES 10000 + +#if 0 +#ifdef WIN32 +# ifdef __BORLANDC__ +# include <dir.h> +# define MKDIR(a) mkdir(a) +# else +# define MKDIR(a) mkdir(a,S_IRWXU) // I am just guessing at this flag (NHV) +# endif // __BORLANDC__ +#endif // WIN32 +#endif //0 + + +FGDem::FGDem( void ) { + // cout << "class FGDem CONstructor called." << endl; + dem_data = new float[DEM_SIZE_1][DEM_SIZE_1]; + output_data = new float[DEM_SIZE_1][DEM_SIZE_1]; +} + + +FGDem::FGDem( const string &file ) { + // cout << "class FGDem CONstructor called." << endl; + dem_data = new float[DEM_SIZE_1][DEM_SIZE_1]; + output_data = new float[DEM_SIZE_1][DEM_SIZE_1]; + + FGDem::open(file); +} + + +// open a DEM file +int +FGDem::open ( const string& file ) { + // open input file (or read from stdin) + if ( file == "-" ) { + printf("Loading DEM data file: stdin\n"); + // fd = stdin; + // fd = gzdopen(STDIN_FILENO, "r"); + printf("Not yet ported ...\n"); + return 0; + } else { + in = new fg_gzifstream( file ); + if ( !(*in) ) { + cout << "Cannot open " << file << endl; + return 0; + } + cout << "Loading DEM data file: " << file << endl; + } + + return 1; +} + + +// close a DEM file +int +FGDem::close () { + // the fg_gzifstream doesn't seem to have a close() + + delete in; + + return 1; +} + + +// return next token from input stream +string +FGDem::next_token() { + string token; + + *in >> token; + + // cout << " returning " + token + "\n"; + + return token; +} + + +// return next integer from input stream +int +FGDem::next_int() { + int result; + + *in >> result; + + return result; +} + + +// return next double from input stream +double +FGDem::next_double() { + double result; + + *in >> result; + + return result; +} + + +// return next exponential num from input stream +double +FGDem::next_exp() { + string token; + + token = next_token(); + + const char* p = token.c_str(); + char buf[64]; + char* bp = buf; + + for ( ; *p != 0; ++p ) + { + if ( *p == 'D' ) + *bp++ = 'E'; + else + *bp++ = *p; + } + *bp = 0; + return ::atof( buf ); +} + + +// read and parse DEM "A" record +int +FGDem::read_a_record() { + int i, inum; + double dnum; + string name, token; + char c; + + // get the name field (144 characters) + for ( i = 0; i < 144; i++ ) { + in->get(c); + name += c; + } + + // clean off the trailing whitespace + name = trim(name); + cout << " Quad name field: " << name << endl; + + // DEM level code, 3 reflects processing by DMA + inum = next_int(); + cout << " DEM level code = " << inum << "\n"; + + if ( inum > 3 ) { + return 0; + } + + // Pattern code, 1 indicates a regular elevation pattern + inum = next_int(); + cout << " Pattern code = " << inum << "\n"; + + // Planimetric reference system code, 0 indicates geographic + // coordinate system. + inum = next_int(); + cout << " Planimetric reference code = " << inum << "\n"; + + // Zone code + inum = next_int(); + cout << " Zone code = " << inum << "\n"; + + // Map projection parameters (ignored) + for ( i = 0; i < 15; i++ ) { + dnum = next_exp(); + // printf("%d: %f\n",i,dnum); + } + + // Units code, 3 represents arc-seconds as the unit of measure for + // ground planimetric coordinates throughout the file. + inum = next_int(); + if ( inum != 3 ) { + cout << " Unknown (X,Y) units code = " << inum << "!\n"; + exit(-1); + } + + // Units code; 2 represents meters as the unit of measure for + // elevation coordinates throughout the file. + inum = next_int(); + if ( inum != 2 ) { + cout << " Unknown (Z) units code = " << inum << "!\n"; + exit(-1); + } + + // Number (n) of sides in the polygon which defines the coverage of + // the DEM file (usually equal to 4). + inum = next_int(); + if ( inum != 4 ) { + cout << " Unknown polygon dimension = " << inum << "!\n"; + exit(-1); + } + + // Ground coordinates of bounding box in arc-seconds + dem_x1 = originx = next_exp(); + dem_y1 = originy = next_exp(); + cout << " Origin = (" << originx << "," << originy << ")\n"; + + dem_x2 = next_exp(); + dem_y2 = next_exp(); + + dem_x3 = next_exp(); + dem_y3 = next_exp(); + + dem_x4 = next_exp(); + dem_y4 = next_exp(); + + // Minimum/maximum elevations in meters + dem_z1 = next_exp(); + dem_z2 = next_exp(); + cout << " Elevation range " << dem_z1 << " to " << dem_z2 << "\n"; + + // Counterclockwise angle from the primary axis of ground + // planimetric referenced to the primary axis of the DEM local + // reference system. + token = next_token(); + + // Accuracy code; 0 indicates that a record of accuracy does not + // exist and that no record type C will follow. + + // DEM spacial resolution. Usually (3,3,1) (3,6,1) or (3,9,1) + // depending on latitude + + // I will eventually have to do something with this for data at + // higher latitudes */ + token = next_token(); + cout << " accuracy & spacial resolution string = " << token << endl; + i = token.length(); + cout << " length = " << i << "\n"; + + inum = atoi( token.substr( 0, i - 36 ) ); + row_step = atof( token.substr( i - 24, 12 ) ); + col_step = atof( token.substr( i - 36, 12 ) ); + cout << " Accuracy code = " << inum << "\n"; + cout << " column step = " << col_step << + " row step = " << row_step << "\n"; + + // dimension of arrays to follow (1) + token = next_token(); + + // number of profiles + dem_num_profiles = cols = next_int(); + cout << " Expecting " << dem_num_profiles << " profiles\n"; + + return 1; +} + + +// read and parse DEM "B" record +void +FGDem::read_b_record( ) { + string token; + int i; + + // row / column id of this profile + prof_row = next_int(); + prof_col = next_int(); + // printf("col id = %d row id = %d\n", prof_col, prof_row); + + // Number of columns and rows (elevations) in this profile + prof_num_rows = rows = next_int(); + prof_num_cols = next_int(); + // printf(" profile num rows = %d\n", prof_num_rows); + + // Ground planimetric coordinates (arc-seconds) of the first + // elevation in the profile + prof_x1 = next_exp(); + prof_y1 = next_exp(); + // printf(" Starting at %.2f %.2f\n", prof_x1, prof_y1); + + // Elevation of local datum for the profile. Always zero for + // 1-degree DEM, the reference is mean sea level. + token = next_token(); + + // Minimum and maximum elevations for the profile. + token = next_token(); + token = next_token(); + + // One (usually) dimensional array (prof_num_cols,1) of elevations + for ( i = 0; i < prof_num_rows; i++ ) { + prof_data = next_int(); + dem_data[cur_col][i] = (float)prof_data; + } +} + + +// parse dem file +int +FGDem::parse( ) { + int i; + + cur_col = 0; + + if ( !read_a_record() ) { + return(0); + } + + for ( i = 0; i < dem_num_profiles; i++ ) { + // printf("Ready to read next b record\n"); + read_b_record(); + cur_col++; + + if ( cur_col % 100 == 0 ) { + cout << " loaded " << cur_col << " profiles of data\n"; + } + } + + cout << " Done parsing\n"; + + return 1; +} + + +// write out the area of data covered by the specified bucket. Data +// is written out column by column starting at the lower left hand +// corner. +int +FGDem::write_area( const string& root, FGBucket& b, bool compress ) { + // calculate some boundaries + double min_x = ( b.get_center_lon() - 0.5 * b.get_width() ) * 3600.0; + double max_x = ( b.get_center_lon() + 0.5 * b.get_width() ) * 3600.0; + + double min_y = ( b.get_center_lat() - 0.5 * b.get_height() ) * 3600.0; + double max_y = ( b.get_center_lat() + 0.5 * b.get_height() ) * 3600.0; + + cout << b << endl; + cout << "width = " << b.get_width() << " height = " << b.get_height() + << endl; + + int start_x = (int)((min_x - originx) / col_step); + int span_x = (int)(b.get_width() * 3600.0 / col_step); + + int start_y = (int)((min_y - originy) / row_step); + int span_y = (int)(b.get_height() * 3600.0 / row_step); + + cout << "start_x = " << start_x << " span_x = " << span_x << endl; + cout << "start_y = " << start_y << " span_y = " << span_y << endl; + + // Do a simple sanity checking. But, please, please be nice to + // this write_area() routine and feed it buckets that coincide + // well with the underlying grid structure and spacing. + + if ( ( min_x < originx ) + || ( max_x > originx + cols * col_step ) + || ( min_y < originy ) + || ( max_y > originy + rows * row_step ) ) { + cout << " ERROR: bucket at least partially outside DEM data range!" << + endl; + return 0; + } + + // generate output file name + string base = b.gen_base_path(); + string path = root + "/Scenery/" + base; + string command = "mkdir -p " + path; + system( command.c_str() ); + + string demfile = path + "/" + b.gen_index_str() + ".dem"; + cout << "demfile = " << demfile << endl; + + // write the file + FILE *fp; + if ( (fp = fopen(demfile.c_str(), "w")) == NULL ) { + cout << "cannot open " << demfile << " for writing!" << endl; + exit(-1); + } + + fprintf( fp, "%d %d\n", (int)min_x, (int)min_y ); + fprintf( fp, "%d %d %d %d\n", span_x + 1, (int)col_step, + span_y + 1, (int)row_step ); + for ( int i = start_x; i <= start_x + span_x; ++i ) { + for ( int j = start_y; j <= start_y + span_y; ++j ) { + fprintf( fp, "%d ", (int)dem_data[i][j] ); + } + fprintf( fp, "\n" ); + } + fclose(fp); + + if ( compress ) { + string command = "gzip --best -f " + demfile; + system( command.c_str() ); + } + + return 1; +} + + +#if 0 + +// return the current altitude based on grid data. We should rewrite +// this to interpolate exact values, but for now this is good enough +double FGDem::interpolate_altitude( double lon, double lat ) { + // we expect incoming (lon,lat) to be in arcsec for now + + double xlocal, ylocal, dx, dy, zA, zB, elev; + int x1, x2, x3, y1, y2, y3; + float z1, z2, z3; + int xindex, yindex; + + /* determine if we are in the lower triangle or the upper triangle + ______ + | /| + | / | + | / | + |/ | + ------ + + then calculate our end points + */ + + xlocal = (lon - originx) / col_step; + ylocal = (lat - originy) / row_step; + + xindex = (int)(xlocal); + yindex = (int)(ylocal); + + // printf("xindex = %d yindex = %d\n", xindex, yindex); + + if ( xindex + 1 == cols ) { + xindex--; + } + + if ( yindex + 1 == rows ) { + yindex--; + } + + if ( (xindex < 0) || (xindex + 1 >= cols) || + (yindex < 0) || (yindex + 1 >= rows) ) { + return(-9999); + } + + dx = xlocal - xindex; + dy = ylocal - yindex; + + if ( dx > dy ) { + // lower triangle + // printf(" Lower triangle\n"); + + x1 = xindex; + y1 = yindex; + z1 = dem_data[x1][y1]; + + x2 = xindex + 1; + y2 = yindex; + z2 = dem_data[x2][y2]; + + x3 = xindex + 1; + y3 = yindex + 1; + z3 = dem_data[x3][y3]; + + // printf(" dx = %.2f dy = %.2f\n", dx, dy); + // printf(" (x1,y1,z1) = (%d,%d,%d)\n", x1, y1, z1); + // printf(" (x2,y2,z2) = (%d,%d,%d)\n", x2, y2, z2); + // printf(" (x3,y3,z3) = (%d,%d,%d)\n", x3, y3, z3); + + zA = dx * (z2 - z1) + z1; + zB = dx * (z3 - z1) + z1; + + // printf(" zA = %.2f zB = %.2f\n", zA, zB); + + if ( dx > FG_EPSILON ) { + elev = dy * (zB - zA) / dx + zA; + } else { + elev = zA; + } + } else { + // upper triangle + // printf(" Upper triangle\n"); + + x1 = xindex; + y1 = yindex; + z1 = dem_data[x1][y1]; + + x2 = xindex; + y2 = yindex + 1; + z2 = dem_data[x2][y2]; + + x3 = xindex + 1; + y3 = yindex + 1; + z3 = dem_data[x3][y3]; + + // printf(" dx = %.2f dy = %.2f\n", dx, dy); + // printf(" (x1,y1,z1) = (%d,%d,%d)\n", x1, y1, z1); + // printf(" (x2,y2,z2) = (%d,%d,%d)\n", x2, y2, z2); + // printf(" (x3,y3,z3) = (%d,%d,%d)\n", x3, y3, z3); + + zA = dy * (z2 - z1) + z1; + zB = dy * (z3 - z1) + z1; + + // printf(" zA = %.2f zB = %.2f\n", zA, zB ); + // printf(" xB - xA = %.2f\n", col_step * dy / row_step); + + if ( dy > FG_EPSILON ) { + elev = dx * (zB - zA) / dy + zA; + } else { + elev = zA; + } + } + + return(elev); +} + + +// Use least squares to fit a simpler data set to dem data +void FGDem::fit( double error, FGBucket& p ) { + double x[DEM_SIZE_1], y[DEM_SIZE_1]; + double m, b, ave_error, max_error; + double cury, lasty; + int n, row, start, end; + int colmin, colmax, rowmin, rowmax; + bool good_fit; + // FILE *dem, *fit, *fit1; + + printf("Initializing output mesh structure\n"); + outputmesh_init(); + + // determine dimensions + colmin = p.get_x() * ( (cols - 1) / 8); + colmax = colmin + ( (cols - 1) / 8); + rowmin = p.get_y() * ( (rows - 1) / 8); + rowmax = rowmin + ( (rows - 1) / 8); + printf("Fitting region = %d,%d to %d,%d\n", colmin, rowmin, colmax, rowmax); + + // include the corners explicitly + outputmesh_set_pt(colmin, rowmin, dem_data[colmin][rowmin]); + outputmesh_set_pt(colmin, rowmax, dem_data[colmin][rowmax]); + outputmesh_set_pt(colmax, rowmax, dem_data[colmax][rowmax]); + outputmesh_set_pt(colmax, rowmin, dem_data[colmax][rowmin]); + + printf("Beginning best fit procedure\n"); + + for ( row = rowmin; row <= rowmax; row++ ) { + // fit = fopen("fit.dat", "w"); + // fit1 = fopen("fit1.dat", "w"); + + start = colmin; + + // printf(" fitting row = %d\n", row); + + while ( start < colmax ) { + end = start + 1; + good_fit = true; + + x[(end - start) - 1] = 0.0 + ( start * col_step ); + y[(end - start) - 1] = dem_data[start][row]; + + while ( (end <= colmax) && good_fit ) { + n = (end - start) + 1; + // printf("Least square of first %d points\n", n); + x[end - start] = 0.0 + ( end * col_step ); + y[end - start] = dem_data[end][row]; + least_squares(x, y, n, &m, &b); + ave_error = least_squares_error(x, y, n, m, b); + max_error = least_squares_max_error(x, y, n, m, b); + + /* + printf("%d - %d ave error = %.2f max error = %.2f y = %.2f*x + %.2f\n", + start, end, ave_error, max_error, m, b); + + f = fopen("gnuplot.dat", "w"); + for ( j = 0; j <= end; j++) { + fprintf(f, "%.2f %.2f\n", 0.0 + ( j * col_step ), + dem_data[row][j]); + } + for ( j = start; j <= end; j++) { + fprintf(f, "%.2f %.2f\n", 0.0 + ( j * col_step ), + dem_data[row][j]); + } + fclose(f); + + printf("Please hit return: "); gets(junk); + */ + + if ( max_error > error ) { + good_fit = false; + } + + end++; + } + + if ( !good_fit ) { + // error exceeded the threshold, back up + end -= 2; // back "end" up to the last good enough fit + n--; // back "n" up appropriately too + } else { + // we popped out of the above loop while still within + // the error threshold, so we must be at the end of + // the data set + end--; + } + + least_squares(x, y, n, &m, &b); + ave_error = least_squares_error(x, y, n, m, b); + max_error = least_squares_max_error(x, y, n, m, b); + + /* + printf("\n"); + printf("%d - %d ave error = %.2f max error = %.2f y = %.2f*x + %.2f\n", + start, end, ave_error, max_error, m, b); + printf("\n"); + + fprintf(fit1, "%.2f %.2f\n", x[0], m * x[0] + b); + fprintf(fit1, "%.2f %.2f\n", x[end-start], m * x[end-start] + b); + */ + + if ( start > colmin ) { + // skip this for the first line segment + cury = m * x[0] + b; + outputmesh_set_pt(start, row, (lasty + cury) / 2); + // fprintf(fit, "%.2f %.2f\n", x[0], (lasty + cury) / 2); + } + + lasty = m * x[end-start] + b; + start = end; + } + + /* + fclose(fit); + fclose(fit1); + + dem = fopen("gnuplot.dat", "w"); + for ( j = 0; j < DEM_SIZE_1; j++) { + fprintf(dem, "%.2f %.2f\n", 0.0 + ( j * col_step ), + dem_data[j][row]); + } + fclose(dem); + */ + + // NOTICE, this is for testing only. This instance of + // output_nodes should be removed. It should be called only + // once at the end once all the nodes have been generated. + // newmesh_output_nodes(&nm, "mesh.node"); + // printf("Please hit return: "); gets(junk); + } + + // outputmesh_output_nodes(fg_root, p); +} + + +// Initialize output mesh structure +void FGDem::outputmesh_init( void ) { + int i, j; + + for ( j = 0; j < DEM_SIZE_1; j++ ) { + for ( i = 0; i < DEM_SIZE_1; i++ ) { + output_data[i][j] = -9999.0; + } + } +} + + +// Get the value of a mesh node +double FGDem::outputmesh_get_pt( int i, int j ) { + return ( output_data[i][j] ); +} + + +// Set the value of a mesh node +void FGDem::outputmesh_set_pt( int i, int j, double value ) { + // printf("Setting data[%d][%d] = %.2f\n", i, j, value); + output_data[i][j] = value; +} + + +// Write out a node file that can be used by the "triangle" program. +// Check for an optional "index.node.ex" file in case there is a .poly +// file to go along with this node file. Include these nodes first +// since they are referenced by position from the .poly file. +void FGDem::outputmesh_output_nodes( const string& fg_root, FGBucket& p ) +{ + double exnodes[MAX_EX_NODES][3]; + struct stat stat_buf; + string dir; + char file[256], exfile[256]; +#ifdef WIN32 + char tmp_path[256]; +#endif + string command; + FILE *fd; + long int index; + int colmin, colmax, rowmin, rowmax; + int i, j, count, excount, result; + + // determine dimensions + colmin = p.get_x() * ( (cols - 1) / 8); + colmax = colmin + ( (cols - 1) / 8); + rowmin = p.get_y() * ( (rows - 1) / 8); + rowmax = rowmin + ( (rows - 1) / 8); + cout << " dumping region = " << colmin << "," << rowmin << " to " << + colmax << "," << rowmax << "\n"; + + // generate the base directory + string base_path = p.gen_base_path(); + cout << "fg_root = " << fg_root << " Base Path = " << base_path << endl; + dir = fg_root + "/Scenery/" + base_path; + cout << "Dir = " << dir << endl; + + // stat() directory and create if needed + errno = 0; + result = stat(dir.c_str(), &stat_buf); + if ( result != 0 && errno == ENOENT ) { + cout << "Creating directory\n"; + +// #ifndef WIN32 + + command = "mkdir -p " + dir + "\n"; + system( command.c_str() ); + +#if 0 +// #else // WIN32 + + // Cygwin crashes when trying to output to node file + // explicitly making directory structure seems OK on Win95 + + extract_path (base_path, tmp_path); + + dir = fg_root + "/Scenery"; + if (my_mkdir ( dir.c_str() )) { exit (-1); } + + dir = fg_root + "/Scenery/" + tmp_path; + if (my_mkdir ( dir.c_str() )) { exit (-1); } + + dir = fg_root + "/Scenery/" + base_path; + if (my_mkdir ( dir.c_str() )) { exit (-1); } + +// #endif // WIN32 +#endif //0 + + } else { + // assume directory exists + } + + // get index and generate output file name + index = p.gen_index(); + sprintf(file, "%s/%ld.node", dir.c_str(), index); + + // get (optional) extra node file name (in case there is matching + // .poly file. + strcpy(exfile, file); + strcat(exfile, ".ex"); + + // load extra nodes if they exist + excount = 0; + if ( (fd = fopen(exfile, "r")) != NULL ) { + int junki; + fscanf(fd, "%d %d %d %d", &excount, &junki, &junki, &junki); + + if ( excount > MAX_EX_NODES - 1 ) { + printf("Error, too many 'extra' nodes, increase array size\n"); + exit(-1); + } else { + printf(" Expecting %d 'extra' nodes\n", excount); + } + + for ( i = 1; i <= excount; i++ ) { + fscanf(fd, "%d %lf %lf %lf\n", &junki, + &exnodes[i][0], &exnodes[i][1], &exnodes[i][2]); + printf("(extra) %d %.2f %.2f %.2f\n", + i, exnodes[i][0], exnodes[i][1], exnodes[i][2]); + } + fclose(fd); + } + + printf("Creating node file: %s\n", file); + fd = fopen(file, "w"); + + // first count regular nodes to generate header + count = 0; + for ( j = rowmin; j <= rowmax; j++ ) { + for ( i = colmin; i <= colmax; i++ ) { + if ( output_data[i][j] > -9000.0 ) { + count++; + } + } + // printf(" count = %d\n", count); + } + fprintf(fd, "%d 2 1 0\n", count + excount); + + // now write out extra node data + for ( i = 1; i <= excount; i++ ) { + fprintf(fd, "%d %.2f %.2f %.2f\n", + i, exnodes[i][0], exnodes[i][1], exnodes[i][2]); + } + + // write out actual node data + count = excount + 1; + for ( j = rowmin; j <= rowmax; j++ ) { + for ( i = colmin; i <= colmax; i++ ) { + if ( output_data[i][j] > -9000.0 ) { + fprintf(fd, "%d %.2f %.2f %.2f\n", + count++, + originx + (double)i * col_step, + originy + (double)j * row_step, + output_data[i][j]); + } + } + // printf(" count = %d\n", count); + } + + fclose(fd); +} +#endif + + +FGDem::~FGDem( void ) { + // printf("class FGDem DEstructor called.\n"); + delete [] dem_data; + delete [] output_data; +} + + +// $Log$ +// Revision 1.27 1999/03/25 19:04:36 curt +// Minor tweaks related to FGBucket usage. +// +// Revision 1.26 1999/03/13 17:40:37 curt +// Moved point interpolation and least squares fitting to contruction program +// area. +// Moved leastsqs.* to Lib/Math/ +// +// Revision 1.25 1999/03/12 22:53:07 curt +// Added a routine to dump out the portion of the dem data covered by a +// specified bucket. Other changes related to needs of scenery tools overhaul. +// +// Revision 1.24 1999/03/11 23:31:56 curt +// Tweaks to use newbucket.hxx +// +// Revision 1.23 1999/03/10 01:09:12 curt +// Tweaks to go along with scenery tools overhaul. +// Added a new constructor that accepts the file name. +// +// Revision 1.22 1999/01/19 20:56:56 curt +// MacOS portability changes contributed by "Robert Puyol" <puyol@abvent.fr> +// +// Revision 1.21 1998/11/06 14:04:32 curt +// Changes due to updates in fgstream. +// +// Revision 1.20 1998/10/28 19:38:20 curt +// Elliminate some unnecessary win32 specific stuff (by Norman Vine) +// +// Revision 1.19 1998/10/22 21:59:19 curt +// Fixed a couple subtle bugs that resulted from some of my c++ conversions. +// One bug could cause a segfault on certain input, and the other bug could +// cause the whole procedure to go balistic and generate huge files (also only +// on rare input combinations.) +// +// Revision 1.18 1998/10/18 01:17:09 curt +// Point3D tweaks. +// +// Revision 1.17 1998/10/16 19:08:12 curt +// Portability updates from Bernie Bright. +// +// Revision 1.16 1998/10/02 21:41:39 curt +// Fixes for win32. +// +// Revision 1.15 1998/09/21 20:53:59 curt +// minor tweaks to clean a few additional things up after the rewrite. +// +// Revision 1.14 1998/09/19 17:59:45 curt +// Use c++ streams (fg_gzifstream). Also converted many character arrays to +// the string class. +// +// Revision 1.13 1998/09/09 16:24:04 curt +// Fixed a bug in the handling of exclude files which was causing +// a crash by calling fclose() on an invalid file handle. +// +// Revision 1.12 1998/08/24 20:03:31 curt +// Eliminated a possible memory overrun error. +// Use the proper free() rather than the incorrect delete(). +// +// Revision 1.11 1998/07/20 12:46:11 curt +// When outputing to a .node file, first check for an optional +// "index.node.ex" file in case there is a .poly file to go along with this +// node file. Include these nodes first since they are referenced by position +// from the .poly file. This is my first pass at adding an area "cutout" +// feature to the terrain generation pipeline. +// +// Revision 1.10 1998/07/13 20:58:02 curt +// . +// +// Revision 1.9 1998/07/13 15:29:49 curt +// Added #ifdef HAVE_CONFIG_H +// +// Revision 1.8 1998/07/04 00:47:18 curt +// typedef'd struct fgBUCKET. +// +// Revision 1.7 1998/06/05 18:14:39 curt +// Abort out early when reading the "A" record if it doesn't look like +// a proper DEM file. +// +// Revision 1.6 1998/05/02 01:49:21 curt +// Fixed a bug where the wrong variable was being initialized. +// +// Revision 1.5 1998/04/25 15:00:32 curt +// Changed "r" to "rb" in gzopen() options. This fixes bad behavior in win32. +// +// Revision 1.4 1998/04/22 13:14:46 curt +// Fixed a bug in zlib usage. +// +// Revision 1.3 1998/04/18 03:53:05 curt +// Added zlib support. +// +// Revision 1.2 1998/04/14 02:43:27 curt +// Used "new" to auto-allocate large DEM parsing arrays in class constructor. +// +// Revision 1.1 1998/04/08 22:57:22 curt +// Adopted Gnu automake/autoconf system. +// +// Revision 1.3 1998/04/06 21:09:41 curt +// Additional win32 support. +// Fixed a bad bug in dem file parsing that was causing the output to be +// flipped about x = y. +// +// Revision 1.2 1998/03/23 20:35:41 curt +// Updated to use FG_EPSILON +// +// Revision 1.1 1998/03/19 02:54:47 curt +// Reorganized into a class lib called fgDEM. +// +// Revision 1.1 1998/03/19 01:46:28 curt +// Initial revision. +// diff --git a/Tools/DEM/dem.hxx b/Tools/DEM/dem.hxx new file mode 100644 index 000000000..46849e3a4 --- /dev/null +++ b/Tools/DEM/dem.hxx @@ -0,0 +1,210 @@ +// dem.hxx -- DEM management class +// +// Written by Curtis Olson, started March 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _DEM_HXX +#define _DEM_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include <Bucket/newbucket.hxx> +#include <Misc/fgstream.hxx> + + +#define DEM_SIZE 1200 +#define DEM_SIZE_1 1201 + + +class FGDem { + +private: + + // file pointer for input + // gzFile fd; + fg_gzifstream *in; + + // coordinates (in arc seconds) of south west corner + double originx, originy; + + // number of columns and rows + int cols, rows; + + // Distance between column and row data points (in arc seconds) + double col_step, row_step; + + // pointers to the actual grid data allocated here + float (*dem_data)[DEM_SIZE_1]; + float (*output_data)[DEM_SIZE_1]; + + // Current "A" Record Information + char dem_description[80], dem_quadrangle[80]; + double dem_x1, dem_y1, dem_x2, dem_y2, dem_x3, dem_y3, dem_x4, dem_y4; + double dem_z1, dem_z2; + int dem_resolution, dem_num_profiles; + + // Current "B" Record Information + int prof_col, prof_row; + int prof_num_cols, prof_num_rows; + double prof_x1, prof_y1; + int prof_data; + + // temporary values for the class to use + char option_name[32]; + int do_data; + int cur_col, cur_row; + + // return next token from input stream + string next_token(); + + // return next integer from input stream + int next_int(); + + // return next double from input stream + double next_double(); + + // return next exponential num from input stream + double next_exp(); + +public: + + // Constructor + FGDem( void ); + FGDem( const string& file ); + + // Destructor + ~FGDem( void ); + + // open a DEM file (use "-" if input is coming from stdin) + int open ( const string& file ); + + // close a DEM file + int close(); + + // parse a DEM file + int parse(); + + // read and parse DEM "A" record + int read_a_record(); + + // read and parse DEM "B" record + void read_b_record(); + + // write out the area of data covered by the specified bucket. + // Data is written out column by column starting at the lower left + // hand corner. + int write_area( const string& root, FGBucket& b, bool compress ); + +#if 0 + // return the current altitude based on grid data. We should + // rewrite this to interpolate exact values, but for now this is + // good enough + double interpolate_altitude( double lon, double lat ); + + // Use least squares to fit a simpler data set to dem data + void fit( double error, FGBucket& p ); + + // Initialize output mesh structure + void outputmesh_init( void ); + + // Get the value of a mesh node + double outputmesh_get_pt( int i, int j ); + + // Set the value of a mesh node + void outputmesh_set_pt( int i, int j, double value ); + + // Write out a node file that can be used by the "triangle" program + void outputmesh_output_nodes( const string& fg_root, FGBucket& p ); +#endif + + // Informational methods + inline double get_originx() const { return originx; } + inline double get_originy() const { return originy; } + inline int get_cols() const { return cols; } + inline int get_rows() const { return rows; } + inline double get_col_step() const { return col_step; } + inline double get_row_step() const { return row_step; } +}; + + +#endif // _DEM_HXX + + +// $Log$ +// Revision 1.13 1999/03/13 17:40:39 curt +// Moved point interpolation and least squares fitting to contruction program +// area. +// Moved leastsqs.* to Lib/Math/ +// +// Revision 1.12 1999/03/12 22:53:09 curt +// Added a routine to dump out the portion of the dem data covered by a +// specified bucket. Other changes related to needs of scenery tools overhaul. +// +// Revision 1.11 1999/03/11 23:31:57 curt +// Tweaks to use newbucket.hxx +// +// Revision 1.10 1999/03/10 01:09:13 curt +// Tweaks to go along with scenery tools overhaul. +// Added a new constructor that accepts the file name. +// +// Revision 1.9 1998/10/16 19:08:14 curt +// Portability updates from Bernie Bright. +// +// Revision 1.8 1998/09/19 17:59:46 curt +// Use c++ streams (fg_gzifstream). Also converted many character arrays to +// the string class. +// +// Revision 1.7 1998/07/04 00:47:19 curt +// typedef'd struct fgBUCKET. +// +// Revision 1.6 1998/06/05 18:14:40 curt +// Abort out early when reading the "A" record if it doesn't look like +// a proper DEM file. +// +// Revision 1.5 1998/04/22 13:14:46 curt +// Fixed a bug in zlib usage. +// +// Revision 1.4 1998/04/21 17:03:41 curt +// Prepairing for C++ integration. +// +// Revision 1.3 1998/04/18 03:53:06 curt +// Added zlib support. +// +// Revision 1.2 1998/04/14 02:43:28 curt +// Used "new" to auto-allocate large DEM parsing arrays in class constructor. +// +// Revision 1.1 1998/04/08 22:57:23 curt +// Adopted Gnu automake/autoconf system. +// +// Revision 1.2 1998/03/23 20:35:42 curt +// Updated to use FG_EPSILON +// +// Revision 1.1 1998/03/19 02:54:47 curt +// Reorganized into a class lib called fgDEM. +// +// Revision 1.1 1998/03/19 01:46:29 curt +// Initial revision. +// diff --git a/Tools/DemChop/Makefile.am b/Tools/DemChop/Makefile.am new file mode 100644 index 000000000..9103e673b --- /dev/null +++ b/Tools/DemChop/Makefile.am @@ -0,0 +1,58 @@ +#--------------------------------------------------------------------------- +# Makefile +# +# Written by Curtis Olson, started March 1999. +# +# Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +bin_PROGRAMS = demchop + +demchop_SOURCES = \ + demchop.cxx point2d.hxx + +demchop_LDADD = \ + $(top_builddir)/Tools/Lib/DEM/libDEM.a \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(top_builddir)/Lib/Misc/libMisc.a \ + $(top_builddir)/Lib/Debug/libDebug.a \ + $(top_builddir)/Lib/zlib/libz.a \ + $(base_LIBS) + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib -I$(top_builddir)/Tools/Lib + +# We can't build this with "-O2" (optimization) since this causes a seg fault +# I haven't found a way to strip this out of the CXXFLAGS, so I'm just +# setting it to "-g" +# CXXFLAGS = -g + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.3 1999/03/17 23:51:07 curt +# Removed forced -g compiler flag. +# +# Revision 1.2 1999/03/12 22:53:45 curt +# First working version! +# +# Revision 1.1 1999/03/10 01:02:54 curt +# Initial revision. +# diff --git a/Tools/DemChop/demchop.cxx b/Tools/DemChop/demchop.cxx new file mode 100644 index 000000000..8182c8d8e --- /dev/null +++ b/Tools/DemChop/demchop.cxx @@ -0,0 +1,113 @@ +// demchop.cxx -- chop up a dem file into it's corresponding pieces and stuff +// them into the workspace directory +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1997 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include <Include/compiler.h> + +#include STL_STRING + +#include <Debug/logstream.hxx> +#include <Bucket/newbucket.hxx> +#include <DEM/dem.hxx> + +#include "point2d.hxx" + +FG_USING_STD(string); + + +int main(int argc, char **argv) { + /* + fgDEM dem; + FGBucket p; + string fg_root; + string filename; + double error; + int i, j; + */ + + fglog().setLogLevels( FG_ALL, FG_DEBUG ); + + if ( argc != 3 ) { + FG_LOG( FG_GENERAL, FG_ALERT, + "Usage " << argv[0] << " <dem_file> <work_dir>" ); + exit(-1); + } + + string dem_name = argv[1]; + string work_dir = argv[2]; + string command = "mkdir -p " + work_dir; + system( command.c_str() ); + + FGDem dem(dem_name); + dem.parse(); + dem.close(); + + point2d min, max; + min.x = dem.get_originx() / 3600.0 + FG_HALF_BUCKET_SPAN; + min.y = dem.get_originy() / 3600.0 + FG_HALF_BUCKET_SPAN; + FGBucket b_min( min.x, min.y ); + + max.x = (dem.get_originx() + dem.get_cols() * dem.get_col_step()) / 3600.0 + - FG_HALF_BUCKET_SPAN; + max.y = (dem.get_originy() + dem.get_rows() * dem.get_row_step()) / 3600.0 + - FG_HALF_BUCKET_SPAN; + FGBucket b_max( max.x, max.y ); + + if ( b_min == b_max ) { + dem.write_area( work_dir, b_min, true ); + } else { + FGBucket b_cur; + int dx, dy, i, j; + + fgBucketDiff(b_min, b_max, &dx, &dy); + cout << "DEM file spans tile boundaries" << endl; + cout << " dx = " << dx << " dy = " << dy << endl; + + if ( (dx > 20) || (dy > 20) ) { + cout << "somethings really wrong!!!!" << endl; + exit(-1); + } + + for ( j = 0; j <= dy; j++ ) { + for ( i = 0; i <= dx; i++ ) { + b_cur = fgBucketOffset(min.x, min.y, i, j); + dem.write_area( work_dir, b_cur, true ); + } + } + } + + return 0; +} + + +// $Log$ +// Revision 1.3 1999/03/12 22:53:46 curt +// First working version! +// +// Revision 1.2 1999/03/10 16:09:44 curt +// Hacking towards the first working version. +// +// Revision 1.1 1999/03/10 01:02:54 curt +// Initial revision. +// diff --git a/Tools/DemChop/point2d.cxx b/Tools/DemChop/point2d.cxx new file mode 100644 index 000000000..817015f8a --- /dev/null +++ b/Tools/DemChop/point2d.cxx @@ -0,0 +1,44 @@ +// point2d.cxx -- 2d coordinate routines +// +// Written by Curtis Olson, started September 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#include <math.h> + +#include "point2d.hxx" + + +// convert a point from cartesian to polar coordinates +point2d cart_to_polar_2d(point2d in) { + point2d result; + result.dist = sqrt( in.x * in.x + in.y * in.y ); + result.theta = atan2(in.y, in.x); + + return(result); +} + + +// $Log$ +// Revision 1.1 1999/03/10 01:02:54 curt +// Initial revision. +// diff --git a/Tools/DemChop/point2d.hxx b/Tools/DemChop/point2d.hxx new file mode 100644 index 000000000..e4df44488 --- /dev/null +++ b/Tools/DemChop/point2d.hxx @@ -0,0 +1,62 @@ +// point2d.hxx -- define a 2d point class +// +// Written by Curtis Olson, started February 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#ifndef _POINT2D_HXX +#define _POINT2D_HXX + + +#include <list> + + +class point2d { +public: + union { + double x; + double dist; + double lon; + }; + union { + double y; + double theta; + double lat; + }; +}; + + +// convert a point from cartesian to polar coordinates +point2d cart_to_polar_2d(point2d in); + + +#endif // _POINT2D_HXX + + +// $Log$ +// Revision 1.1 1999/03/10 16:09:45 curt +// Hacking towards the first working version. +// +// Revision 1.1 1998/09/04 23:04:53 curt +// Beginning of convex hull genereration routine. +// +// diff --git a/Tools/DemInfo/Makefile.am b/Tools/DemInfo/Makefile.am new file mode 100644 index 000000000..b3d8d3aa7 --- /dev/null +++ b/Tools/DemInfo/Makefile.am @@ -0,0 +1,70 @@ +#--------------------------------------------------------------------------- +# Makefile +# +# Written by Curtis Olson, started June 1998. +# +# Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +bin_PROGRAMS = deminfo + +deminfo_SOURCES = \ + deminfo.cxx + +deminfo_LDADD = \ + $(top_builddir)/Tools/Lib/DEM/libDEM.a \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(top_builddir)/Lib/Misc/libMisc.a \ + $(top_builddir)/Lib/zlib/libz.a \ + $(base_LIBS) + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib -I$(top_builddir)/Tools/Lib + +# We can't build this with "-O2" (optimization) since this causes a seg fault +# I haven't found a way to strip this out of the CXXFLAGS, so I'm just +# setting it to "-g" +# CXXFLAGS = -g + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.7 1999/03/17 23:51:14 curt +# Removed forced -g compiler flag. +# +# Revision 1.6 1999/03/08 22:00:46 curt +# Lots of directory layout reorganization. +# +# Revision 1.5 1999/02/01 21:09:27 curt +# Moving location of Lib/DEM/ to Tools/DEM/ +# +# Revision 1.4 1998/11/04 23:01:48 curt +# Changes to the automake/autoconf system to reduce the number of libraries +# that are unnecessarily linked into the various executables. +# +# Revision 1.3 1998/09/19 18:01:21 curt +# Support for changes to libDEM.a +# +# Revision 1.2 1998/07/30 23:49:24 curt +# Removed libtool support. +# +# Revision 1.1 1998/06/04 19:18:04 curt +# Initial revision. +# diff --git a/Tools/DemInfo/deminfo.cxx b/Tools/DemInfo/deminfo.cxx new file mode 100644 index 000000000..75f9c2f55 --- /dev/null +++ b/Tools/DemInfo/deminfo.cxx @@ -0,0 +1,78 @@ +// deminfo.cxx -- main loop +// +// Written by Curtis Olson, started June 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <string> + +#include <DEM/dem.hxx> + + +// static float dem_data[DEM_SIZE_1][DEM_SIZE_1]; +// static float output_data[DEM_SIZE_1][DEM_SIZE_1]; + + +int main(int argc, char **argv) { + // DEM data + FGDem dem; + string filename; + double error; + int i, j; + + if ( argc != 2 ) { + printf("Usage: %s <file.dem>\n", argv[0]); + exit(-1); + } + + // set input dem file name + filename = argv[1]; + + dem.open(filename); + + if ( dem.read_a_record() ) { + cout << "Results = " << filename << " " + << dem.get_originx() / 3600.0 << " " + << dem.get_originy() / 3600.0 << "\n"; + } else { + cout << "Error parsing DEM file.\n"; + } + + dem.close(); + + return(0); +} + + +// $Log$ +// Revision 1.3 1999/03/12 22:54:04 curt +// Convert fgDEM to FGDem ... +// +// Revision 1.2 1998/09/19 18:01:22 curt +// Support for changes to libDEM.a +// +// Revision 1.1 1998/06/04 19:18:05 curt +// Initial revision. +// diff --git a/Tools/DemInfo/gather-dem-info.pl b/Tools/DemInfo/gather-dem-info.pl new file mode 100755 index 000000000..746d7120e --- /dev/null +++ b/Tools/DemInfo/gather-dem-info.pl @@ -0,0 +1,61 @@ +#!/usr/bin/perl + +#--------------------------------------------------------------------------- +# script to gather DEM position info so we can associate a file name with a +# position. +# +# Written by Curtis Olson, started June 1998. +# +# Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +if ( $#ARGV < 0 ) { + die "Usage: $0 search_dir ... \n"; +} + +while ( $dir = shift(@ARGV) ) { + # print "processing $dir\n"; + + @allfiles = `find $dir -print`; + + foreach $file (@allfiles) { + chop($file); + # print "trying $file\n"; + if ( -f $file ) { + # print "really trying $file\n"; + open ( INFO, "./deminfo $file |" ); + while ( <INFO> ) { + if ( m/Results = / ) { + $_ =~ s/Results = //; + print $_; + } + } + close(INFO); + } + } +} + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.1 1998/06/04 19:18:06 curt +# Initial revision. +# diff --git a/Tools/DemRaw2ascii/Makefile.am b/Tools/DemRaw2ascii/Makefile.am new file mode 100644 index 000000000..b638e2e96 --- /dev/null +++ b/Tools/DemRaw2ascii/Makefile.am @@ -0,0 +1,47 @@ +#--------------------------------------------------------------------------- +# Makefile +# +# Written by Curtis Olson, started February 1998. +# +# Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +bin_PROGRAMS = raw2ascii + +raw2ascii_SOURCES = main.c rawdem.c rawdem.h + +raw2ascii_LDADD = $(base_LIBS) + +INCLUDES += + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.3 1998/11/04 23:01:50 curt +# Changes to the automake/autoconf system to reduce the number of libraries +# that are unnecessarily linked into the various executables. +# +# Revision 1.2 1998/04/24 00:44:04 curt +# Added zlib support. +# +# Revision 1.1 1998/04/18 03:59:44 curt +# Incorporated into gnu automake/autoconf system. +# diff --git a/Tools/DemRaw2ascii/main.c b/Tools/DemRaw2ascii/main.c new file mode 100644 index 000000000..8cc0f2ffa --- /dev/null +++ b/Tools/DemRaw2ascii/main.c @@ -0,0 +1,90 @@ +/* main.c -- main loop + * + * Written by Curtis Olson, started February 1998. + * + * Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + * (Log is kept at end of this file) + */ + + +#include <stdio.h> +#include <string.h> + +#include "rawdem.h" + + +int main(int argc, char **argv) { + fgRAWDEM raw; + char basename[256], output_dir[256], hdr_file[256], dem_file[256]; + int i, start_lat, end_lat; + + if ( argc != 3 ) { + printf("Usage: %s <input_file_basename> <output_dir>\n", argv[0]); + exit(-1); + } + + /* get basename */ + strcpy(basename, argv[1]); + + /* get output dir */ + strcpy(output_dir, argv[2]); + + /* generate header file name */ + strcpy(hdr_file, basename); + strcat(hdr_file, ".HDR"); + + /* generate input file name (raw dem) */ + strcpy(dem_file, basename); + strcat(dem_file, ".DEM"); + + printf("Header file = %s Input file = %s\n", hdr_file, dem_file); + printf("Output Directory = %s\n", output_dir); + + /* scan the header file and extract important values */ + rawReadDemHdr(&raw, hdr_file); + + /* open up the raw data file */ + rawOpenDemFile(&raw, dem_file); + + end_lat = raw.rooty / 3600; + start_lat = end_lat - ((raw.nrows * raw.ydim) / 3600); + printf("Latitude ranges from %d to %d\n", start_lat, end_lat); + + for ( i = start_lat + 1; i <= end_lat; i++ ) { + rawProcessStrip(&raw, i, output_dir); + } + + /* close the raw data file */ + rawCloseDemFile(&raw); + + return(0); +} + + +/* $Log$ +/* Revision 1.3 1998/03/03 21:54:50 curt +/* Changes to process 30 arcsec binary DEM files. +/* + * Revision 1.2 1998/03/03 13:10:28 curt + * Close to a working version. + * + * Revision 1.1 1998/03/02 23:31:01 curt + * Initial revision. + * + */ diff --git a/Tools/DemRaw2ascii/rawdem.c b/Tools/DemRaw2ascii/rawdem.c new file mode 100644 index 000000000..8411035bd --- /dev/null +++ b/Tools/DemRaw2ascii/rawdem.c @@ -0,0 +1,485 @@ +/* rawdem.c -- library of routines for processing raw dem files (30 arcsec) + * + * Written by Curtis Olson, started February 1998. + * + * Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + * (Log is kept at end of this file) + */ + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <math.h> /* rint() */ +#include <stdio.h> +#include <stdlib.h> /* atoi() atof() */ +#include <string.h> /* swab() */ + +#include <sys/types.h> /* open() */ +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> /* close() */ + +#include "rawdem.h" + + +/* Read the DEM header to determine various key parameters for this + * DEM file */ +void rawReadDemHdr( fgRAWDEM *raw, char *hdr_file ) { + FILE *hdr; + char line[256], key[256], value[256]; + int i, len, offset; + double tmp; + + if ( (hdr = fopen(hdr_file, "r")) == NULL ) { + printf("Error opening DEM header file: %s\n", hdr_file); + exit(-1); + } + + /* process each line */ + while ( (fgets(line, 256, hdr) != NULL) ) { + /* printf("%s", line); */ + len = strlen(line); + + /* extract key */ + i = 0; + while ( (line[i] != ' ') && (i < len) ) { + key[i] = line[i]; + i++; + } + key[i] = '\0'; + + /* skip middle space */ + while ( (line[i] == ' ') && (i < len) ) { + i++; + } + offset = i; + + /* extract value */ + while ( (line[i] != '\n') && (i < len) ) { + value[i-offset] = line[i]; + i++; + } + value[i-offset] = '\0'; + /* printf("key='%s' value='%s'\n", key, value); */ + + if ( strcmp(key, "NROWS") == 0 ) { + raw->nrows = atoi(value); + } else if ( strcmp(key, "NCOLS") == 0 ) { + raw->ncols = atoi(value); + } else if ( strcmp(key, "ULXMAP") == 0 ) { + tmp = atof(value); +#ifdef HAVE_RINT + raw->ulxmap = (int)rint(tmp * 3600.0); /* convert to arcsec */ +#else +# error Port me rint() +#endif + } else if ( strcmp(key, "ULYMAP") == 0 ) { + tmp = atof(value); +#ifdef HAVE_RINT + raw->ulymap = (int)rint(tmp * 3600.0); /* convert to arcsec */ +#else +# error Port me rint() +#endif + } else if ( strcmp(key, "XDIM") == 0 ) { + tmp = atof(value); +#ifdef HAVE_RINT + raw->xdim = (int)rint(tmp * 3600.0); /* convert to arcsec */ +#else +# error Port me rint() +#endif + } else if ( strcmp(key, "YDIM") == 0 ) { + tmp = atof(value); +#ifdef HAVE_RINT + raw->ydim = (int)rint(tmp * 3600.0); /* convert to arcsec */ +#else +# error Port me rint() +#endif + } else { + /* ignore for now */ + } + } + + raw->rootx = raw->ulxmap - (raw->xdim / 2); + raw->rooty = raw->ulymap + (raw->ydim / 2); + + printf("%d %d %d %d %d %d %d %d\n", raw->nrows, raw->ncols, + raw->ulxmap, raw->ulymap, raw->rootx, raw->rooty, raw->xdim, + raw->ydim); +} + + +/* Open a raw DEM file. */ +void rawOpenDemFile( fgRAWDEM *raw, char *raw_dem_file ) { + printf("Opening Raw DEM file: %s\n", raw_dem_file); + if ( (raw->fd = open(raw_dem_file ,O_RDONLY)) == -1 ) { + printf("Error opening Raw DEM file: %s\n", raw_dem_file); + exit(-1); + } +} + + +/* Close a raw DEM file. */ +void rawCloseDemFile( fgRAWDEM *raw ) { + close(raw->fd); +} + + +/* Advance file pointer position to correct latitude (row) */ +void rawAdvancePosition( fgRAWDEM *raw, int arcsec ) { + long offset, result; + + offset = 2 * raw->ncols * ( arcsec / raw->ydim ); + + if ( (result = lseek(raw->fd, offset, SEEK_SET)) == -1 ) { + printf("Error lseek filed trying to offset by %ld\n", offset); + exit(-1); + } + + printf("Successful seek ahead of %ld bytes\n", result); +} + + +/* Read the next row of data */ +void rawReadNextRow( fgRAWDEM *raw, int index ) { + char buf[MAX_COLS_X_2]; + int i, result; + + if ( raw->ncols > MAX_ROWS ) { + printf("Error, buf needs to be bigger in rawReadNextRow()\n"); + exit(-1); + } + + /* printf("Attempting to read %d bytes\n", 2 * raw->ncols); */ + result = read(raw->fd, buf, 2 * raw->ncols); + /* printf("Read %d bytes\n", result); */ + + /* reverse byte order */ + /* it would be nice to test in advance some how if we need to do + * this */ + /* swab(frombuf, tobuf, 2 * raw->ncols); */ + + for ( i = 0; i < raw->ncols; i++ ) { + /* printf("hi = %d lo = %d\n", buf[2*i], buf[2*i + 1]); */ + raw->strip[index][i] = ( (buf[2*i] + 1) << 8 ) + buf[2*i + 1]; + } +} + + +/* Convert from pixel centered values to pixel corner values. This is + accomplished by taking the average of the closes center nodes. In + the following diagram "x" marks the data point location: + + +-----+ x-----x + | | | | + | x | ===> | | + | | | | + +-----+ x-----x + + */ +void rawConvertCenter2Edge( fgRAWDEM *raw ) { + int i, j; + + /* derive corner nodes */ + raw->edge[0][0] = raw->center[0][0]; + raw->edge[120][0] = raw->center[119][0]; + raw->edge[120][120] = raw->center[119][119]; + raw->edge[0][120] = raw->center[0][119]; + + /* derive edge nodes */ + for ( i = 1; i < 120; i++ ) { + raw->edge[i][0] = (raw->center[i-1][0] + raw->center[i][0]) / 2.0; + raw->edge[i][120] = (raw->center[i-1][119] + raw->center[i][119]) / 2.0; + raw->edge[0][i] = (raw->center[0][i-1] + raw->center[0][i]) / 2.0; + raw->edge[120][i] = (raw->center[119][i-1] + raw->center[119][i]) / 2.0; + } + + /* derive internal nodes */ + for ( j = 1; j < 120; j++ ) { + for ( i = 1; i < 120; i++ ) { + raw->edge[i][j] = ( raw->center[i-1][j-1] + + raw->center[i] [j-1] + + raw->center[i] [j] + + raw->center[i-1][j] ) / 4; + } + } +} + + +/* Dump out the ascii format DEM file */ +void rawDumpAsciiDEM( fgRAWDEM *raw, char *path, int ilon, int ilat ) { + char outfile[256]; + char tmp[256]; + int lon, lat; + char lon_sign, lat_sign; + int i, j; + FILE *fd; + + /* Generate output file name */ + + if ( ilon >= 0 ) { + lon = ilon; + lon_sign = 'e'; + } else { + lon = -ilon; + lon_sign = 'w'; + } + + if ( ilat >= 0 ) { + lat = ilat; + lat_sign = 'n'; + } else { + lat = -ilat; + lat_sign = 's'; + } + + sprintf(outfile, "%s/%c%03d%c%03d.dem", path, lon_sign, lon, lat_sign, lat); + + printf("outfile = %s\n", outfile); + + if ( (fd = fopen(outfile, "w")) == NULL ) { + printf("Error opening output file = %s\n", outfile); + exit(-1); + } + + /* Dump the "A" record */ + + /* print descriptive header (144 characters) */ + sprintf(tmp, "%s - Generated from a 30 arcsec binary DEM", outfile); + fprintf(fd, "%-144s", tmp); + + /* DEM level code, 3 reflects processing by DMA */ + fprintf(fd, "%6d", 1); + + /* Pattern code, 1 indicates a regular elevation pattern */ + fprintf(fd, "%6d", 1); + + /* Planimetric reference system code, 0 indicates geographic + * coordinate system. */ + fprintf(fd, "%6d", 0); + + /* Zone code */ + fprintf(fd, "%6d", 0); + + /* Map projection parameters (ignored) */ + for ( i = 0; i < 15; i++ ) { + fprintf(fd, "%6.1f%18s", 0.0, ""); + } + + /* Units code, 3 represents arc-seconds as the unit of measure for + * ground planimetric coordinates throughout the file. */ + fprintf(fd, "%6d", 3); + + /* Units code; 2 represents meters as the unit of measure for + * elevation coordinates throughout the file. */ + fprintf(fd, "%6d", 2); + + /* Number (n) of sides in the polygon which defines the coverage of + * the DEM file (usually equal to 4). */ + fprintf(fd, "%6d", 4); + + /* Ground coordinates of bounding box in arc-seconds */ + fprintf(fd, "%20.15fD+06", ilon * 3600.0 / 1000000.0); + fprintf(fd, "%20.15fD+06", ilat * 3600.0 / 1000000.0); + + fprintf(fd, "%20.15fD+06", ilon * 3600.0 / 1000000.0); + fprintf(fd, "%20.15fD+06", (ilat+1) * 3600.0 / 1000000.0); + + fprintf(fd, "%20.15fD+06", (ilon+1) * 3600.0 / 1000000.0); + fprintf(fd, "%20.15fD+06", (ilat+1) * 3600.0 / 1000000.0); + + fprintf(fd, "%20.15fD+06", (ilon+1) * 3600.0 / 1000000.0); + fprintf(fd, "%20.15fD+06", (ilat) * 3600.0 / 1000000.0); + + /* Minimum/maximum elevations in meters */ + fprintf(fd, " %20.15E", (double)raw->tmp_min); + fprintf(fd, " %20.15E", (double)raw->tmp_max); + + /* Counterclockwise angle from the primary axis of ground + * planimetric referenced to the primary axis of the DEM local + * reference system. */ + fprintf(fd, "%6.1f", 0.0); + + /* Accuracy code; 0 indicates that a record of accuracy does not + * exist and that no record type C will follow. */ + fprintf(fd, "%24d", 0); + + /* DEM spacial resolution. Usually (3,3) (3,6) or (3,9) + * depending on latitude */ + fprintf(fd, "%12.6E", 30.0); + fprintf(fd, "%12.6E", 30.0); + + /* accuracy code */ + fprintf(fd, "%12.6E", 1.0); + + /* dimension of arrays to follow (1)*/ + fprintf(fd, "%6d", 1); + + /* number of profiles */ + fprintf(fd, "%6d", 3600 / raw->ydim + 1); + + /* pad the end */ + fprintf(fd, "%160s", ""); + + + /* Dump "B" records */ + + for ( j = 0; j <= 120; j++ ) { + /* row / column id of this profile */ + fprintf(fd, "%6d%6d", 1, j + 1); + + /* Number of rows and columns (elevation points) in this + profile */ + fprintf(fd, "%6d%6d", 3600 / raw->xdim + 1, 1); + + /* Ground planimetric coordinates (arc-seconds) of the first + * elevation in the profile */ + fprintf(fd, "%20.15fD+06", ilon * 3600.0 / 1000000.0); + fprintf(fd, "%20.15fD+06", (ilat * 3600.0 + j * raw->ydim) / 1000000.0); + + /* Elevation of local datum for the profile. Always zero for + * 1-degree DEM, the reference is mean sea level. */ + fprintf(fd, "%6.1f", 0.0); + fprintf(fd, "%18s", ""); + + /* Minimum and maximum elevations for the profile. */ + fprintf(fd, " %20.15E", 0.0); + fprintf(fd, " %20.15E", 0.0); + + /* One (usually) dimensional array (1,prof_num_cols) of + elevations */ + for ( i = 0; i <= 120; i++ ) { + fprintf(fd, "%6.0f", raw->edge[j][i]); + } + } + + fprintf(fd, "\n"); + + fclose(fd); +} + + +/* Read a horizontal strip of (1 vertical degree) from the raw DEM + * file specified by the upper latitude of the stripe specified in + * degrees. The output the individual ASCII format DEM tiles. */ +void rawProcessStrip( fgRAWDEM *raw, int lat_degrees, char *path ) { + int lat, yrange; + int i, j, index, row, col; + int min, max; + int span, num_degrees, tile_width; + int xstart, xend; + + /* convert to arcsec */ + lat = lat_degrees * 3600; + + printf("Max Latitude = %d arcsec\n", lat); + + /* validity check ... */ + if ( (lat > raw->rooty) || + (lat < (raw->rooty - raw->nrows * raw->ydim + 1)) ) { + printf("Latitude out of range for this DEM file\n"); + return; + } + + printf ("Reading strip starting at %d (top and working down)\n", lat); + + /* advance to the correct latitude */ + rawAdvancePosition(raw, (raw->rooty - lat)); + + /* printf("short = %d\n", sizeof(short)); */ + + yrange = 3600 / raw->ydim; + + for ( i = 0; i < yrange; i++ ) { + index = yrange - i - 1; + /* printf("About to read into row %d\n", index); */ + rawReadNextRow(raw, index); + + for ( j = 0; j < raw->ncols; j++ ) { + if ( raw->strip[index][j] == -9999 ) { + /* map ocean to 0 for now */ + raw->strip[index][j] = 0; + } + } + } + + /* extract individual tiles from the strip */ + span = raw->ncols * raw->xdim; + num_degrees = span / 3600; + tile_width = raw->ncols / num_degrees; + printf("span = %d num_degrees = %d width = %d\n", + span, num_degrees, tile_width); + + for ( i = 0; i < num_degrees; i++ ) { + xstart = i * tile_width; + xend = xstart + 120; + + min = 10000; max = -10000; + for ( row = 0; row < yrange; row++ ) { + for ( col = xstart; col < xend; col++ ) { + /* Copy from strip to pixel centered tile. Yep, + * row/col are reversed here. raw->strip is backwards + * for convenience. I am converting to [x,y] now. */ + raw->center[col-xstart][row] = raw->strip[row][col]; + + if ( raw->strip[row][col] < min) { + min = raw->strip[row][col]; + } + + if ( raw->strip[row][col] > max) { + max = raw->strip[row][col]; + } + } + } + + raw->tmp_min = min; + raw->tmp_max = max; + + /* Convert from pixel centered to pixel edge values */ + rawConvertCenter2Edge(raw); + + /* Dump out the ascii format DEM file */ + rawDumpAsciiDEM(raw, path, (raw->rootx / 3600) + i, lat_degrees - 1); + } +} + + +/* $Log$ +/* Revision 1.6 1998/04/27 03:32:03 curt +/* Wrapped rint()'s in #ifdef HAVE_RINT +/* + * Revision 1.5 1998/04/18 03:59:46 curt + * Incorporated into gnu automake/autoconf system. + * + * Revision 1.4 1998/04/06 21:09:43 curt + * Additional win32 support. + * Fixed a bad bug in dem file parsing that was causing the output to be + * flipped about x = y. + * + * Revision 1.3 1998/03/03 13:10:29 curt + * Close to a working version. + * + * Revision 1.2 1998/03/03 02:04:01 curt + * Starting DEM Ascii format output routine. + * + * Revision 1.1 1998/03/02 23:31:01 curt + * Initial revision. + * + */ diff --git a/Tools/DemRaw2ascii/rawdem.h b/Tools/DemRaw2ascii/rawdem.h new file mode 100644 index 000000000..8877b2eaa --- /dev/null +++ b/Tools/DemRaw2ascii/rawdem.h @@ -0,0 +1,85 @@ +/* rawdem.h -- library of routines for processing raw dem files (30 arcsec) + * + * Written by Curtis Olson, started February 1998. + * + * Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + * (Log is kept at end of this file) + */ + + +#ifndef _RAWDEM_H +#define _RAWDEM_H + + +#define MAX_ROWS 6000 +#define MAX_COLS 7200 +#define MAX_COLS_X_2 14400 + +typedef struct { + /* header info */ + int nrows; /* number of rows */ + int ncols; /* number of cols */ + int ulxmap; /* X coord of center of upper left pixel in arcsec */ + int ulymap; /* Y coord of center of upper left pixel in arcsec */ + int rootx; /* X coord of upper left *edge* of DEM region in degrees */ + int rooty; /* Y coord of upper left *edge* of DEM region in degrees */ + int xdim; /* X dimension of a pixel */ + int ydim; /* Y dimension of a pixel */ + int tmp_min; /* current 1x1 degree tile minimum */ + int tmp_max; /* current 1x1 degree tile maximum */ + + /* file ptr */ + int fd; /* Raw DEM file descriptor */ + + /* storage area for a 1 degree high strip of data. Note, for + * convenience this is in y,x order */ + short strip[120][MAX_ROWS]; + + short center[120][120]; /* tile with data taken at center of pixel */ + float edge[121][121]; /* tile with data converted to corners */ +} fgRAWDEM; + + +/* Read the DEM header to determine various key parameters for this + * DEM file */ +void rawReadDemHdr( fgRAWDEM *raw, char *hdr_file ); + +/* Open a raw DEM file. */ +void rawOpenDemFile( fgRAWDEM *raw, char *raw_dem_file ); + +/* Close a raw DEM file. */ +void rawCloseDemFile( fgRAWDEM *raw ); + +/* Read a horizontal strip of (1 vertical degree) from the raw DEM + * file specified by the upper latitude of the stripe specified in + * degrees. The output the individual ASCII format DEM tiles. */ +void rawProcessStrip( fgRAWDEM *raw, int lat_degrees, char *path ); + + +#endif /* _RAWDEM_H */ + + +/* $Log$ +/* Revision 1.2 1998/03/03 13:10:30 curt +/* Close to a working version. +/* + * Revision 1.1 1998/03/02 23:31:02 curt + * Initial revision. + * + */ diff --git a/Tools/FixNode/Makefile.am b/Tools/FixNode/Makefile.am new file mode 100644 index 000000000..a245e6ac0 --- /dev/null +++ b/Tools/FixNode/Makefile.am @@ -0,0 +1,95 @@ +#--------------------------------------------------------------------------- +# Makefile +# +# Written by Curtis Olson, started October 1997. +# +# Copyright (C) 1997 - 1998 Curtis L. Olson - curt@me.umn.edu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +bin_PROGRAMS = fixnode + +fixnode_SOURCES = \ + fixnode.cxx fixnode.hxx \ + main.cxx + +fixnode_LDADD = \ + $(top_builddir)/Tools/Lib/DEM/libDEM.a \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(top_builddir)/Lib/Misc/libMisc.a \ + $(top_builddir)/Lib/zlib/libz.a \ + $(base_LIBS) + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib -I$(top_builddir)/Tools/Lib + +# We can't build this with "-O2" (optimization) since this causes a seg fault +# I haven't found a way to strip this out of the CXXFLAGS, so I'm just +# setting it to "-g" +# CXXFLAGS = -g + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.10 1999/03/17 23:50:59 curt +# Removed forced -g compiler flag. +# +# Revision 1.9 1999/03/08 22:00:45 curt +# Lots of directory layout reorganization. +# +# Revision 1.8 1999/02/01 21:09:30 curt +# Moving location of Lib/DEM/ to Tools/DEM/ +# +# Revision 1.7 1998/11/04 23:01:51 curt +# Changes to the automake/autoconf system to reduce the number of libraries +# that are unnecessarily linked into the various executables. +# +# Revision 1.6 1998/09/19 20:43:50 curt +# C++-ified and STL-ified the code. Combined triload.* and fixnode.* into +# a single file. +# +# Revision 1.5 1998/09/19 18:01:26 curt +# Support for changes to libDEM.a +# +# Revision 1.4 1998/07/30 23:49:24 curt +# Removed libtool support. +# +# Revision 1.3 1998/04/18 04:02:54 curt +# Added zlib support in placed and other misc. tweaks. +# +# Revision 1.2 1998/04/14 02:26:02 curt +# Code reorganizations. Added a Lib/ directory for more general libraries. +# +# Revision 1.1 1998/04/08 23:05:54 curt +# Adopted Gnu automake/autoconf system. +# +# Revision 1.4 1998/04/06 21:09:44 curt +# Additional win32 support. +# Fixed a bad bug in dem file parsing that was causing the output to be +# flipped about x = y. +# +# Revision 1.3 1998/03/19 02:50:19 curt +# Updated to support -lDEM class. +# +# Revision 1.2 1998/01/21 02:55:50 curt +# Incorporated new make system from Bob Kuehne <rpk@sgi.com>. +# +# Revision 1.1 1997/11/27 00:17:32 curt +# Initial revision. +# diff --git a/Tools/FixNode/fixnode.cxx b/Tools/FixNode/fixnode.cxx new file mode 100644 index 000000000..5269ac459 --- /dev/null +++ b/Tools/FixNode/fixnode.cxx @@ -0,0 +1,161 @@ +// fixnode.cxx -- traverse the node file and fix the elevation of all the new +// interpolated points. +// +// Written by Curtis Olson, started November 1997. +// +// Copyright (C) 1997 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <string> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif // HAVE_STDLIB_H + +#include <Misc/fgstream.hxx> + +#include "fixnode.hxx" + + +// load extra nodes +void load_extra(const string& filename, container& extra_list) { +} + + +// load the node information +void load_nodes(const string& filename, container& node_list) { + Point3D node; + int dim, junk1, junk2; + int i, nodecount; + + cout << "Loading node file: " << filename << " ...\n"; + + fg_gzifstream in( filename ); + if ( !in ) { + cout << "Cannot open " + filename + "\n"; + // exit immediately assuming an airport file for this tile + // doesn't exist. + exit(-1); + } + + // Read header line + in >> nodecount >> dim >> junk1 >> junk2; + cout << " Expecting " << nodecount << " nodes\n"; + + // start with an empty list :-) + node_list.erase( node_list.begin(), node_list.end() ); + + in >> skipcomment; + while ( ! in.eof() ) { + in >> junk1 >> node >> junk2; + in >> skipcomment; + node_list.push_back(node); + } +} + + +// fix the node elevations +void fix_nodes( const string& filename, fgDEM& dem, container& node_list ) +{ + string toname; + FILE *fd; + int i; + + cout << "Fixing up node elevations\n"; + + iterator current; + iterator last = node_list.end(); + for ( current = node_list.begin() ; current != last ; ++current ) { + // printf("Current: %d %.2f %.2f %.2f\n", i, nodes[i][0], + // nodes[i][1], nodes[i][2]); + + (*current).setz( + dem.interpolate_altitude( (*current).x(), + (*current).y() ) ); + + // printf("Fixed: %d %.2f %.2f %.2f\n", i, nodes[i][0], + // nodes[i][1], nodes[i][2]); + } + + + toname = filename + ".orig"; + cout << "Moving " + filename + " to " + toname + "\n"; + rename( filename.c_str(), toname.c_str() ); + + cout << "Saving new node file: " + filename + "\n"; + + fd = fopen(filename.c_str(), "w"); + + fprintf( fd, "%d 2 1 0\n", node_list.size() ); + + i = 1; + for ( current = node_list.begin() ; current != last ; ++current ) { + fprintf( fd, "%d %.2f %.2f %.2f 0\n", i, + (*current).x(), (*current).y(), (*current).z() ); + ++i; + } + + fclose(fd); +} + + +// $Log$ +// Revision 1.7 1998/11/06 21:33:55 curt +// Updates to go along with changes in fgstream. +// +// Revision 1.6 1998/10/20 15:49:22 curt +// Converted to Point3D class. +// +// Revision 1.5 1998/09/22 23:49:10 curt +// eliminated a left over #include +// +// Revision 1.4 1998/09/19 20:43:52 curt +// C++-ified and STL-ified the code. Combined triload.* and fixnode.* into +// a single file. +// +// Revision 1.3 1998/07/22 21:46:40 curt +// Fixed a bug that was triggering a seg fault. +// +// Revision 1.2 1998/04/14 02:26:03 curt +// Code reorganizations. Added a Lib/ directory for more general libraries. +// +// Revision 1.1 1998/04/08 23:05:56 curt +// Adopted Gnu automake/autoconf system. +// +// Revision 1.5 1998/03/19 02:50:19 curt +// Updated to support -lDEM class. +// +// Revision 1.4 1998/03/03 16:00:57 curt +// More c++ compile tweaks. +// +// Revision 1.3 1998/01/09 23:03:08 curt +// Restructured to split 1deg x 1deg dem's into 64 subsections. +// +// Revision 1.2 1997/12/02 13:12:07 curt +// Updated to fix every node. +// +// Revision 1.1 1997/11/27 00:17:33 curt +// Initial revision. +// + diff --git a/Tools/FixNode/fixnode.hxx b/Tools/FixNode/fixnode.hxx new file mode 100644 index 000000000..80b6eb2b6 --- /dev/null +++ b/Tools/FixNode/fixnode.hxx @@ -0,0 +1,95 @@ +// fixnode.hxx -- traverse the node file and fix the elevation of all the new +// interpolated points. +// +// Written by Curtis Olson, started November 1997. +// +// Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#ifndef _FIXNODE_H +#define _FIXNODE_H + + +#include <stdio.h> +#include <string.h> +#include <string> + +#include <vector> +#include "Include/fg_stl_config.h" + +#ifdef NEEDNAMESPACESTD +using namespace std; +#endif + +#include <DEM/dem.hxx> +#include <Math/point3d.hxx> + + +typedef vector < Point3D > container; +typedef container::iterator iterator; +typedef container::const_iterator const_iterator; + + +// Initialize a new mesh structure +void load_nodes(const string& basename, container& node_list); + + +// load the extra nodes. These are always the first n nodes of the +// .node file. (??? These will be tagged with a code indicating what +// needs to be done with this node's elevation such as adjust to local +// DEM elevation, or massage the local DEM points to match this +// elevation point. ???) +void load_extra_nodes(const string& filename, container& node_list); + + +// fix the node elevations +void fix_nodes( const string& basename, fgDEM& dem, container& node_list ); + + +#endif // _FIXNODE_H + + +// $Log$ +// Revision 1.4 1998/10/20 15:49:23 curt +// Converted to Point3D class. +// +// Revision 1.3 1998/09/19 20:43:53 curt +// C++-ified and STL-ified the code. Combined triload.* and fixnode.* into +// a single file. +// +// Revision 1.2 1998/07/22 21:46:41 curt +// Fixed a bug that was triggering a seg fault. +// +// Revision 1.1 1998/04/08 23:05:56 curt +// Adopted Gnu automake/autoconf system. +// +// Revision 1.4 1998/03/19 02:50:19 curt +// Updated to support -lDEM class. +// +// Revision 1.3 1998/03/03 16:00:58 curt +// More c++ compile tweaks. +// +// Revision 1.2 1997/12/02 13:12:07 curt +// Updated to fix every node. +// +// Revision 1.1 1997/11/27 00:17:33 curt +// Initial revision. +// diff --git a/Tools/FixNode/main.cxx b/Tools/FixNode/main.cxx new file mode 100644 index 000000000..68e540802 --- /dev/null +++ b/Tools/FixNode/main.cxx @@ -0,0 +1,146 @@ +// main.cxx -- read in a .node file and fix the z values of the interpolated +// points +// +// Written by Curtis Olson, started November 1997. +// +// Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#include <sys/types.h> +#include <dirent.h> +// #include <stdio.h> +#include <string.h> +#include <string> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif // HAVE_STDLIB_H + +#include <DEM/dem.hxx> + +#include "fixnode.hxx" + + +// find all the matching files in the specified directory and fix them +void process_files(const string& root_path, fgDEM& dem) { + container node_list; + DIR *d; + struct dirent *de; + string file_path; + char *ptr; + int len; + + if ( (d = opendir( root_path.c_str() )) == NULL ) { + cout << "cannot open directory " + root_path + "\n"; + exit(-1); + } + + while ( (de = readdir(d)) != NULL ) { + len = strlen(de->d_name); + if ( len > 7 ) { + ptr = de->d_name; + ptr += (len - 7); + // printf("--> %s \n", ptr); + + if ( strcmp(ptr, ".1.node") == 0 ) { + file_path = root_path + "/" + de->d_name; + cout << "File = " + file_path + "\n"; + + // load the input data files + load_nodes(file_path, node_list); + + fix_nodes(file_path, dem, node_list); + } + } + } +} + + +// main +int main(int argc, char **argv) { + fgDEM dem; + string demfile, root_path; + + if ( argc != 3 ) { + cout << "Usage " << argv[0] << " demfile root_path\n"; + exit(-1); + } + + cout << "Starting fixnode\n"; + + demfile = argv[1]; + root_path = argv[2]; + + // load the corresponding dem file so we can interpolate elev values + dem.open(demfile); + dem.parse(); + dem.close(); + + // process all the *.1.node files in the specified directory + process_files(root_path, dem); + + return(0); +} + + +// $Log$ +// Revision 1.7 1998/09/19 20:43:54 curt +// C++-ified and STL-ified the code. Combined triload.* and fixnode.* into +// a single file. +// +// Revision 1.6 1998/09/19 18:01:27 curt +// Support for changes to libDEM.a +// +// Revision 1.5 1998/07/22 21:46:41 curt +// Fixed a bug that was triggering a seg fault. +// +// Revision 1.4 1998/06/27 16:55:24 curt +// Changed include order for <sys/types.h> +// +// Revision 1.3 1998/04/26 05:02:06 curt +// Added #ifdef HAVE_STDLIB_H +// +// Revision 1.2 1998/04/14 02:26:04 curt +// Code reorganizations. Added a Lib/ directory for more general libraries. +// +// Revision 1.1 1998/04/08 23:05:57 curt +// Adopted Gnu automake/autoconf system. +// +// Revision 1.6 1998/04/06 21:09:44 curt +// Additional win32 support. +// Fixed a bad bug in dem file parsing that was causing the output to be +// flipped about x = y. +// +// Revision 1.5 1998/03/19 02:50:20 curt +// Updated to support -lDEM class. +// +// Revision 1.4 1998/03/03 16:00:58 curt +// More c++ compile tweaks. +// +// Revision 1.3 1998/01/09 23:03:08 curt +// Restructured to split 1deg x 1deg dem's into 64 subsections. +// +// Revision 1.2 1997/12/02 13:12:07 curt +// Updated to fix every node. +// +// Revision 1.1 1997/11/27 00:17:34 curt +// Initial revision. +// diff --git a/Tools/FixObj/Makefile.am b/Tools/FixObj/Makefile.am new file mode 100644 index 000000000..7ced91cc9 --- /dev/null +++ b/Tools/FixObj/Makefile.am @@ -0,0 +1,69 @@ +#--------------------------------------------------------------------------- +# Makefile +# +# Written by Curtis Olson, started October 1997. +# +# Copyright (C) 1997 - 1998 Curtis L. Olson - curt@me.umn.edu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +bin_PROGRAMS = fixobj + +fixobj_SOURCES = main.cxx obj.cxx obj.hxx + +fixobj_LDADD = \ + $(top_builddir)/Lib/Math/libMath.a \ + $(top_builddir)/Lib/Debug/libDebug.a \ + $(top_builddir)/Lib/zlib/libz.a \ + $(base_LIBS) + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.7 1998/11/04 23:01:53 curt +# Changes to the automake/autoconf system to reduce the number of libraries +# that are unnecessarily linked into the various executables. +# +# Revision 1.6 1998/07/30 23:49:25 curt +# Removed libtool support. +# +# Revision 1.5 1998/06/08 17:11:44 curt +# Renamed *.[ch] to *.[ch]xx +# +# Revision 1.4 1998/04/24 00:44:05 curt +# Added zlib support. +# +# Revision 1.3 1998/04/18 04:01:02 curt +# Now use libMath rather than having local copies of math routines. +# +# Revision 1.2 1998/04/14 02:26:05 curt +# Code reorganizations. Added a Lib/ directory for more general libraries. +# +# Revision 1.1 1998/04/08 23:19:35 curt +# Adopted Gnu automake/autoconf system. +# +# Revision 1.2 1998/01/21 02:55:53 curt +# Incorporated new make system from Bob Kuehne <rpk@sgi.com>. +# +# Revision 1.1 1997/12/08 19:28:54 curt +# Initial revision. +# diff --git a/Tools/FixObj/main.cxx b/Tools/FixObj/main.cxx new file mode 100644 index 000000000..bf117a10c --- /dev/null +++ b/Tools/FixObj/main.cxx @@ -0,0 +1,57 @@ +// main.cxx -- read and fix the stripping order of a .obj file +// +// Written by Curtis Olson, started December 1997. +// +// Copyright (C) 1997 - 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include <stdio.h> + +#include "obj.hxx" + + +int main(int argc, char **argv) { + char infile[256], outfile[256]; + + if ( argc != 3 ) { + printf("Usage %s: infile outfile\n", argv[0]); + } + + strcpy(infile, argv[1]); + strcpy(outfile, argv[2]); + + // load the input data files + obj_fix(infile, outfile); + + return(0); +} + + +// $Log$ +// Revision 1.1 1998/06/08 17:11:45 curt +// Renamed *.[ch] to *.[ch]xx +// +// Revision 1.2 1998/01/09 23:03:12 curt +// Restructured to split 1deg x 1deg dem's into 64 subsections. +// +// Revision 1.1 1997/12/08 19:28:54 curt +// Initial revision. +// + diff --git a/Tools/FixObj/obj.cxx b/Tools/FixObj/obj.cxx new file mode 100644 index 000000000..4239a1891 --- /dev/null +++ b/Tools/FixObj/obj.cxx @@ -0,0 +1,647 @@ +// obj.cxx -- routines to handle WaveFront .obj format files. +// +// Written by Curtis Olson, started October 1997. +// +// Copyright (C) 1997 - 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include <stdio.h> +#include <iostream> +#include <string.h> + +#include <vector> +#include "Include/compiler.h" + +#ifdef NEEDNAMESPACESTD +using namespace std; +#endif + +#include "obj.hxx" + +#include <Math/mat3.h> +#include <Math/point3d.hxx> + + +typedef vector < Point3D > container3; +typedef container3::iterator iterator3; +typedef container3::const_iterator const_iterator3; + + +// what do ya' know, here's some global variables +container3 nodes; +container3 normals; +static int faces[MAXNODES][3]; +int vncount, fcount; + +static int ccw_list[MAXNODES]; +int ccw_list_ptr; + +static int cw_list[MAXNODES]; +int cw_list_ptr; + +FILE *in, *out; + +Point3D ref; + + +// some simple list routines + +// reset the list +void list_init(int *list_ptr) { + *list_ptr = 0; +} + + +// add to list +void list_add(int *list, int *list_ptr, int node) { + if ( *list_ptr >= MAXNODES ) { + printf("ERROR: list overflow in list_add()\n"); + exit(-1); + } + + list[*list_ptr] = node; + *list_ptr += 1; + + // printf("list pointer = %d adding %d\n", *list_ptr, node); +} + + +// fix the cw list and append to ccw_list +void fix_cw_list(int *list, int list_ptr) { + int i, j, len; + + if ( list_ptr < 3 ) { + printf("List is empty ... skipping\n"); + return; + } + + printf("Fixing cw list, size = %d\n", list_ptr); + + i = 0; + while ( i < list_ptr ) { + // do next strip + + // find length + len = 0; + // scan rest of strip (until -1) + while ( ((i+len) < list_ptr) && (list[i+len] != -1) ) { + // printf("len = %d item = %d\n", len, list[i+len] ); + len++; + } + // printf(" Final length = %d\n", len); + + if ( (len % 2) != 0 ) { + // if length is odd, just reverse order of nodes to + // reverse winding + if ( ccw_list_ptr ) { + list_add(ccw_list, &ccw_list_ptr, -1); + } + for ( j = i + len - 1; j >= i; j-- ) { + // printf(" odd -> item = %d\n", list[j] ); + list_add(ccw_list, &ccw_list_ptr, list[j]); + } + } else { + // if length is even, reverse order of (n-1) nodes to + // reverse winding, and create an orphan triangle for the + // last "nth" node + if ( ccw_list_ptr ) { + list_add(ccw_list, &ccw_list_ptr, -1); + } + for ( j = i + len - 2; j >= i; j-- ) { + // printf(" even -> item = %d\n", list[j] ); + list_add(ccw_list, &ccw_list_ptr, list[j]); + } + + // printf(" even bonus -> item = %d\n", list[i + len - 1] ); + // printf(" even bonus -> item = %d\n", list[i + len - 2] ); + // printf(" even bonus -> item = %d\n", list[i + len - 3] ); + list_add(ccw_list, &ccw_list_ptr, -1); + list_add(ccw_list, &ccw_list_ptr, list[i + len - 3]); + list_add(ccw_list, &ccw_list_ptr, list[i + len - 2]); + list_add(ccw_list, &ccw_list_ptr, list[i + len - 1]); + } + + i += len + 1; + } +} + + +void dump_global_bounds( void ) { + double dist_squared, radius, radius_squared; + + radius = 0.0; + + fprintf(out, "\n"); + + + iterator3 current = nodes.begin(); + iterator3 last = nodes.end(); + + // skip first dummy node + ++current; + + for ( ; current != last; ++current ) { + dist_squared = ref.distance3Dsquared(*current); + // cout << "node = " << *current << " dist = " << dist_squared << endl; + + if ( dist_squared > radius_squared ) { + radius_squared = dist_squared; + } + } + + radius = sqrt(radius_squared); + + fprintf( out, + "gbs %.5f %.5f %.5f %.2f\n", + ref.x(), ref.y(), ref.z(), radius); +} + + +// dump nodes +void dump_nodes( void ) { + Point3D p; + + fprintf(out, "\n"); + + iterator3 current = nodes.begin(); + iterator3 last = nodes.end(); + + // skip first dummy node + ++current; + + for ( ; current != last; ++current ) { + p = *current - ref; + fprintf( out, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() ); + } +} + + +// dump normals +void dump_normals( void ) { + Point3D p; + + fprintf(out, "\n"); + + iterator3 current = normals.begin(); + iterator3 last = normals.end(); + + // skip first dummy normal + ++current; + + for ( ; current != last; ++current ) { + p = *current; + fprintf(out, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() ); + } +} + + +// dump faces +void dump_faces( void ) { + Point3D p; + int i, n1, n2, n3; + double xmax, xmin, ymax, ymin, zmax, zmin, dist, radius; + + fprintf(out, "\n"); + for ( i = 1; i < fcount; i++ ) { + n1 = faces[i][0]; + n2 = faces[i][1]; + n3 = faces[i][2]; + + // calc center of face + xmin = xmax = nodes[n1].x(); + ymin = ymax = nodes[n1].y(); + zmin = zmax = nodes[n1].z(); + + if ( nodes[n2].x() < xmin ) { xmin = nodes[n2].x(); } + if ( nodes[n2].x() > xmax ) { xmax = nodes[n2].x(); } + if ( nodes[n2].y() < ymin ) { ymin = nodes[n2].y(); } + if ( nodes[n2].y() > ymax ) { ymax = nodes[n2].y(); } + if ( nodes[n2].z() < zmin ) { zmin = nodes[n2].z(); } + if ( nodes[n2].z() > zmax ) { zmax = nodes[n2].z(); } + + if ( nodes[n3].x() < xmin ) { xmin = nodes[n3].x(); } + if ( nodes[n3].x() > xmax ) { xmax = nodes[n3].x(); } + if ( nodes[n3].y() < ymin ) { ymin = nodes[n3].y(); } + if ( nodes[n3].y() > ymax ) { ymax = nodes[n3].y(); } + if ( nodes[n3].z() < zmin ) { zmin = nodes[n3].z(); } + if ( nodes[n3].z() > zmax ) { zmax = nodes[n3].z(); } + + p = Point3D( (xmin + xmax) / 2.0, + (ymin + ymax) / 2.0, + (zmin + zmax) / 2.0 ); + + // calc bounding radius + radius = p.distance3D(nodes[n1]); + + dist = p.distance3D(nodes[n2]); + if ( dist > radius ) { radius = dist; } + + dist = p.distance3D(nodes[n3]); + if ( dist > radius ) { radius = dist; } + + // output data + fprintf(out, "bs %.2f %.2f %.2f %.2f\n", p.x(), p.y(), p.z(), radius); + fprintf(out, "f %d %d %d\n", n1, n2, n3); + } +} + + +// dump list +void dump_list(int *list, int list_ptr) { + Point3D p; + double xmax, xmin, ymax, ymin, zmax, zmin, dist_squared, radius_squared; + double radius; + int i, j, len, n; + + if ( list_ptr < 3 ) { + printf("List is empty ... skipping\n"); + return; + } + + printf("Dumping list, size = %d\n", list_ptr); + + i = 0; + while ( i < list_ptr ) { + // do next strip + + if ( (i % 2) == 0 ) { + fprintf(out, "\nusemtl desert1\n"); + } else { + fprintf(out, "\nusemtl desert2\n"); + } + + // find length of next tri strip + len = 0; + // scan rest of strip (until -1) + while ( ((i+len) < list_ptr) && (list[i+len] != -1) ) { + // printf("len = %d item = %d\n", len, list[i+len] ); + len++; + } + // printf("strip length = %d\n", len); + + // calc center of face + n = list[i]; + xmin = xmax = nodes[n].x(); + ymin = ymax = nodes[n].y(); + zmin = zmax = nodes[n].z(); + // printf("%.2f %.2f %.2f\n", nodes[n].x(), nodes[n].y(), nodes[n].z()); + + for ( j = i + 1; j < i + len; j++ ) { + // printf("j = %d\n", j); + n = list[j]; + if ( nodes[n].x() < xmin ) { xmin = nodes[n].x(); } + if ( nodes[n].x() > xmax ) { xmax = nodes[n].x(); } + if ( nodes[n].y() < ymin ) { ymin = nodes[n].y(); } + if ( nodes[n].y() > ymax ) { ymax = nodes[n].y(); } + if ( nodes[n].z() < zmin ) { zmin = nodes[n].z(); } + if ( nodes[n].z() > zmax ) { zmax = nodes[n].z(); } + // printf("%.2f %.2f %.2f\n", nodes[n].x(), nodes[n].y(), nodes[n].z()); + } + p = Point3D( (xmin + xmax) / 2.0, + (ymin + ymax) / 2.0, + (zmin + zmax) / 2.0 ); + // printf("center = %.2f %.2f %.2f\n", p.x(), p.y(), p.z()); + + // calc bounding radius + n = list[i]; + radius_squared = p.distance3Dsquared(nodes[n]); + + for ( j = i + 1; j < i + len; j++ ) { + n = list[j]; + dist_squared = p.distance3Dsquared(nodes[n]); + if ( dist_squared > radius_squared ) { + radius_squared = dist_squared; + } + } + radius = sqrt(radius_squared); + + // printf("radius = %.2f\n", radius); + + // dump bounding sphere and header + fprintf(out, "bs %.2f %.2f %.2f %.2f\n", p.x(), p.y(), p.z(), radius); + fprintf(out, "t %d %d %d\n", list[i], list[i+1], list[i+2]); + // printf("t %d %d %d\n", list[i], list[i+1], list[i+2]); + i += 3; + + // dump rest of strip (until -1) + while ( (i < list_ptr) && (list[i] != -1) ) { + fprintf(out, "q %d", list[i]); + i++; + if ( (i < list_ptr) && (list[i] != -1) ) { + fprintf(out, " %d", list[i]); + i++; + } + fprintf(out, "\n"); + } + + i++; + } +} + + +// Check the direction the current triangle faces, compared to it's +// pregenerated normal. Returns the dot product between the target +// normal and actual normal. If the dot product is close to 1.0, they +// nearly match. If the are close to -1.0, the are nearly opposite. +double check_cur_face(int n1, int n2, int n3) { + double v1[3], v2[3], approx_normal[3], dot_prod, temp; + + // check for the proper rotation by calculating an approximate + // normal and seeing if it is close to the precalculated normal + v1[0] = nodes[n2].x() - nodes[n1].x(); + v1[1] = nodes[n2].y() - nodes[n1].y(); + v1[2] = nodes[n2].z() - nodes[n1].z(); + v2[0] = nodes[n3].x() - nodes[n1].x(); + v2[1] = nodes[n3].y() - nodes[n1].y(); + v2[2] = nodes[n3].z() - nodes[n1].z(); + + MAT3cross_product(approx_normal, v1, v2); + MAT3_NORMALIZE_VEC(approx_normal,temp); + dot_prod = MAT3_DOT_PRODUCT(normals[n1], approx_normal); + + // not first triangle + // if ( ((dot_prod < -0.5) && !is_backwards) || + // ((dot_prod > 0.5) && is_backwards) ) { + // printf(" Approx normal = %.2f %.2f %.2f\n", approx_normal[0], + // approx_normal[1], approx_normal[2]); + // printf(" Dot product = %.4f\n", dot_prod); + // } + // angle = acos(dot_prod); + // printf("Normal ANGLE = %.3f rads.\n", angle); + + return(dot_prod); +} + + +// Load a .obj file +void obj_fix(char *infile, char *outfile) { + Point3D node, normal; + char line[256]; + double dot_prod; + int first, n1, n2, n3, n4; + double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin; + int is_ccw; + + if ( (in = fopen(infile, "r")) == NULL ) { + printf("Cannot open file: %s\n", infile); + exit(-1); + } + + if ( (out = fopen(outfile, "w")) == NULL ) { + printf("Cannot open file: %s\n", outfile); + exit(-1); + } + + // push dummy records onto the lists since we start counting with "1" + node = Point3D(0.0, 0.0, 0.0); + nodes.push_back(node); + + normal = Point3D(0.0, 0.0, 0.0); + normals.push_back(normal); + + // initialize other lists + list_init(&ccw_list_ptr); + list_init(&cw_list_ptr); + + // I start counting at one because that is how the triangle + // program refers to nodes and normals + first = 1; + vncount = 1; + fcount = 1; + + printf("Reading file: %s\n", infile); + + while ( fgets(line, 250, in) != NULL ) { + if ( line[0] == '#' ) { + // pass along the comments verbatim + fprintf(out, "%s", line); + } else if ( strlen(line) <= 1 ) { + // don't pass along empty lines + // fprintf(out, "%s", line); + } else if ( strncmp(line, "v ", 2) == 0 ) { + // save vertex to memory and output to file + // printf("vertex = %s", line); + sscanf(line, "v %lf %lf %lf\n", &x, &y, &z); + + if ( nodes.size() == 1 ) { + // first time through set min's and max'es + xmin = x; + xmax = x; + ymin = y; + ymax = y; + zmin = z; + zmax = z; + } else { + // update min/max vertex values + if ( x < xmin ) xmin = x; + if ( x > xmax ) xmax = x; + if ( y < ymin ) ymin = y; + if ( y > ymax ) ymax = y; + if ( z < zmin ) zmin = z; + if ( z > zmax ) zmax = z; + } + + node = Point3D(x, y, z); + nodes.push_back(node); + // fprintf(out, "v %.2f %.2f %.2f\n", + // node.x(), node.y(), node.z()); + } else if ( strncmp(line, "vn ", 3) == 0 ) { + // save vertex normals to memory and output to file + // printf("vertex normal = %s", line); + sscanf(line, "vn %lf %lf %lf\n", &x, &y, &z); + normal = Point3D(x, y, z); + normals.push_back(normal); + } else if ( line[0] == 't' ) { + // starting a new triangle strip + + printf("Starting a new triangle strip\n"); + + n1 = n2 = n3 = n4 = 0; + + printf("new tri strip = %s", line); + sscanf(line, "t %d %d %d %d\n", &n1, &n2, &n3, &n4); + + // special cases to handle bugs in our beloved tri striper + if ( (n1 == 4) && (n2 == 2) && (n3 == 2) && (n4 == 1) ) { + n2 = 3; + } + if ( (n1 == 3) && (n2 == 1) && (n3 == 1) && (n4 == 0) ) { + n3 = 4; + } + + dot_prod = check_cur_face(n1, n2, n3); + if ( dot_prod < 0.0 ) { + // this stripe is backwards (CW) + is_ccw = 0; + printf(" -> Starting a backwards stripe\n"); + } else { + // this stripe is normal (CCW) + is_ccw = 1; + } + + if ( is_ccw ) { + if ( ccw_list_ptr ) { + list_add(ccw_list, &ccw_list_ptr, -1); + } + + list_add(ccw_list, &ccw_list_ptr, n1); + list_add(ccw_list, &ccw_list_ptr, n2); + list_add(ccw_list, &ccw_list_ptr, n3); + } else { + if ( cw_list_ptr ) { + list_add(cw_list, &cw_list_ptr, -1); + } + + list_add(cw_list, &cw_list_ptr, n1); + list_add(cw_list, &cw_list_ptr, n2); + list_add(cw_list, &cw_list_ptr, n3); + } + + if ( n4 > 0 ) { + if ( is_ccw ) { + list_add(ccw_list, &ccw_list_ptr, n4); + } else { + list_add(cw_list, &cw_list_ptr, n4); + } + } + } else if ( line[0] == 'f' ) { + if ( fcount < MAXNODES ) { + // pass along the unoptimized faces verbatim + sscanf(line, "f %d %d %d\n", &n1, &n2, &n3); + faces[fcount][0] = n1; + faces[fcount][1] = n2; + faces[fcount][2] = n3; + + fcount++; + } else { + printf("Read too many unoptimized faces ... dying :-(\n"); + exit(-1); + } + + // fprintf(out, "%s", line); + } else if ( line[0] == 'q' ) { + // continue a triangle strip + n1 = n2 = 0; + + // printf("continued tri strip = %s ", line); + sscanf(line, "q %d %d\n", &n1, &n2); + + if ( is_ccw ) { + list_add(ccw_list, &ccw_list_ptr, n1); + } else { + list_add(cw_list, &cw_list_ptr, n1); + } + + if ( n2 > 0 ) { + if ( is_ccw ) { + list_add(ccw_list, &ccw_list_ptr, n2); + } else { + list_add(cw_list, &cw_list_ptr, n2); + } + } + } else { + printf("Unknown line in %s = %s\n", infile, line); + } + } + + // reference point is the "center" + ref = Point3D( (xmin + xmax) / 2.0, + (ymin + ymax) / 2.0, + (zmin + zmax) / 2.0 ); + + // convert the cw_list to ccw add append to ccw_list + fix_cw_list(cw_list, cw_list_ptr); + + dump_global_bounds(); + dump_nodes(); + dump_normals(); + if ( fcount > 1 ) { + dump_faces(); + } + + dump_list(ccw_list, ccw_list_ptr); + + fclose(in); + fclose(out); +} + + +// $Log$ +// Revision 1.3 1999/02/01 21:09:40 curt +// Optimizations from Norman Vine. +// +// Revision 1.2 1998/10/21 14:55:55 curt +// Converted to Point3D class. +// +// Revision 1.1 1998/06/08 17:11:46 curt +// Renamed *.[ch] to *.[ch]xx +// +// Revision 1.16 1998/05/27 02:27:22 curt +// Commented out a couple of debugging messages. +// +// Revision 1.15 1998/05/24 02:47:47 curt +// For each strip, specify a default material property and calculate a center +// and bounding sphere. +// +// Revision 1.14 1998/05/23 15:19:49 curt +// Output more digits after the decimal place. +// +// Revision 1.13 1998/05/20 20:55:19 curt +// Fixed arbitrary polygon winding problem here so all tristrips are passed +// to runtime simulator with a consistant counter clockwise winding. +// +// Revision 1.12 1998/05/16 13:11:26 curt +// Fixed an off by one error in node, normal, and face counters. +// +// Revision 1.11 1998/04/27 15:59:24 curt +// Fixed an off by one error. +// +// Revision 1.10 1998/04/27 03:33:11 curt +// Code now calculates a center reference points and outputs everything +// relative to that. This is useful in the rendering engine to keep everything +// close to (0, 0, 0) where we can avoid many GLfloat precision problems. +// +// Revision 1.9 1998/04/18 04:01:03 curt +// Now use libMath rather than having local copies of math routines. +// +// Revision 1.8 1998/04/08 23:19:37 curt +// Adopted Gnu automake/autoconf system. +// +// Revision 1.7 1998/03/19 02:51:41 curt +// Added special case handling to compensate for bugs in our beloved tri striper +// +// Revision 1.6 1998/03/03 15:36:12 curt +// Tweaks for compiling with g++ +// +// Revision 1.5 1998/03/03 03:37:03 curt +// Cumulative tweaks. +// +// Revision 1.4 1998/01/31 00:41:25 curt +// Made a few changes converting floats to doubles. +// +// Revision 1.3 1998/01/19 19:51:07 curt +// A couple final pre-release tweaks. +// +// Revision 1.2 1998/01/09 23:03:12 curt +// Restructured to split 1deg x 1deg dem's into 64 subsections. +// +// Revision 1.1 1997/12/08 19:28:54 curt +// Initial revision. +// diff --git a/Tools/FixObj/obj.hxx b/Tools/FixObj/obj.hxx new file mode 100644 index 000000000..59a362c8d --- /dev/null +++ b/Tools/FixObj/obj.hxx @@ -0,0 +1,60 @@ +// obj.hxx -- routines to handle WaveFront .obj format files. +// +// Written by Curtis Olson, started October 1997. +// +// Copyright (C) 1997 - 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef OBJ_HXX +#define OBJ_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#define MAXNODES 100000 + + +// Load a .obj file +void obj_fix(char *infile, char *outfile); + + +#endif // OBJ_HXX + + +// $Log$ +// Revision 1.1 1998/06/08 17:11:46 curt +// Renamed *.[ch] to *.[ch]xx +// +// Revision 1.4 1998/03/03 15:36:13 curt +// Tweaks for compiling with g++ +// +// Revision 1.3 1998/01/31 00:41:25 curt +// Made a few changes converting floats to doubles. +// +// Revision 1.2 1998/01/09 23:03:13 curt +// Restructured to split 1deg x 1deg dem's into 64 subsections. +// +// Revision 1.1 1997/12/08 19:28:55 curt +// Initial revision. +// + diff --git a/Tools/GenAirports/Makefile.am b/Tools/GenAirports/Makefile.am new file mode 100644 index 000000000..86c8ecb16 --- /dev/null +++ b/Tools/GenAirports/Makefile.am @@ -0,0 +1,86 @@ +#--------------------------------------------------------------------------- +# Makefile +# +# Written by Curtis Olson, started January 1998. +# +# Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +bin_PROGRAMS = genapts + +genapts_SOURCES = \ + area.cxx area.hxx \ + convex_hull.cxx convex_hull.hxx \ + main.cxx \ + point2d.cxx point2d.hxx + +genapts_LDADD = \ + $(top_builddir)/Tools/Lib/Polygon/libPolygon.a \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(top_builddir)/Lib/Debug/libDebug.a \ + $(top_builddir)/Lib/Misc/libMisc.a \ + $(top_builddir)/Lib/zlib/libz.a \ + $(base_LIBS) + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib -I$(top_builddir)/Tools/Lib + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.6 1999/03/08 22:00:47 curt +# Lots of directory layout reorganization. +# +# Revision 1.5 1999/02/25 21:32:47 curt +# Modified to adhere to new polygon naming convention, and also to read the +# new Robin Peel aiport format. +# +# Revision 1.4 1999/02/11 01:10:50 curt +# Start of scenery revamp project. +# +# Revision 1.3 1998/11/04 23:01:54 curt +# Changes to the automake/autoconf system to reduce the number of libraries +# that are unnecessarily linked into the various executables. +# +# Revision 1.2 1998/09/04 23:04:47 curt +# Beginning of convex hull genereration routine. +# +# Revision 1.1 1998/09/01 19:34:32 curt +# Initial revision. +# +# Revision 1.2 1998/07/30 23:49:18 curt +# Removed libtool support. +# +# Revision 1.1 1998/07/20 12:54:53 curt +# Whoops, need to commit Makefile.am, not Makefile. +# +# Revision 1.2 1998/04/14 02:25:59 curt +# Code reorganizations. Added a Lib/ directory for more general libraries. +# +# Revision 1.1 1998/04/08 22:54:57 curt +# Adopted Gnu automake/autoconf system. +# +# Revision 1.2 1998/01/21 02:55:46 curt +# Incorporated new make system from Bob Kuehne <rpk@sgi.com>. +# +# Revision 1.1 1998/01/15 02:45:25 curt +# Initial revision. +# + diff --git a/Tools/GenAirports/area.cxx b/Tools/GenAirports/area.cxx new file mode 100644 index 000000000..75d785377 --- /dev/null +++ b/Tools/GenAirports/area.cxx @@ -0,0 +1,241 @@ +// area.c -- routines to assist with inserting "areas" into FG terrain +// +// Written by Curtis Olson, started March 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#include <math.h> +#include <stdio.h> + +#include <Include/fg_constants.h> + +#include "area.hxx" +#include "point2d.hxx" + + +// calc new x, y for a rotation +double rot_x(double x, double y, double theta) { + return ( x * cos(theta) + y * sin(theta) ); +} + + +// calc new x, y for a rotation +double rot_y(double x, double y, double theta) { + return ( -x * sin(theta) + y * cos(theta) ); +} + + +// calc new lon/lat given starting lon/lat, and offset radial, and +// distance. NOTE: distance is specified in meters (and converted +// internally to radians) +point2d calc_lon_lat( point2d orig, point2d offset ) { + point2d result; + + // printf("calc_lon_lat() offset.theta = %.2f offset.dist = %.2f\n", + // offset.theta, offset.dist); + + offset.dist *= METER_TO_NM * NM_TO_RAD; + + result.lat = asin( sin(orig.lat) * cos(offset.dist) + + cos(orig.lat) * sin(offset.dist) * cos(offset.theta) ); + + if ( cos(result.lat) < FG_EPSILON ) { + result.lon = orig.lon; // endpoint a pole + } else { + result.lon = + fmod(orig.lon - asin( sin(offset.theta) * sin(offset.dist) / + cos(result.lat) ) + FG_PI, FG_2PI) - FG_PI; + } + + return(result); +} + + +list < point2d > +batch_cart_to_polar_2d( list < point2d > in_list) +{ + list < point2d > out_list; + list < point2d > :: iterator current; + list < point2d > :: iterator last; + point2d p; + + current = in_list.begin(); + last = in_list.end(); + for ( ; current != last ; ++current ) { + p = cart_to_polar_2d( *current ); + out_list.push_back(p); + } + + return out_list; +} + + +// given a set of 2d coordinates relative to a center point, and the +// lon, lat of that center point (specified in degrees), as well as a +// potential orientation angle, generate the corresponding lon and lat +// of the original 2d verticies. +list < point2d > +gen_area(point2d origin, double angle, list < point2d > cart_list) +{ + list < point2d > rad_list; + list < point2d > result_list; + list < point2d > :: iterator current; + list < point2d > :: iterator last; + point2d origin_rad, p; + + origin_rad.lon = origin.lon * DEG_TO_RAD; + origin_rad.lat = origin.lat * DEG_TO_RAD; + + // convert to polar coordinates + rad_list = batch_cart_to_polar_2d(cart_list); + + /* + // display points + printf("converted to polar\n"); + current = rad_list.begin(); + last = rad_list.end(); + while ( current != last ) { + printf("(%.2f, %.2f)\n", current->theta, current->dist); + ++current; + } + printf("\n"); + */ + + // rotate by specified angle + // printf("Rotating points by %.2f\n", angle); + current = rad_list.begin(); + last = rad_list.end(); + for ( ; current != last ; ++current ) { + current->theta -= angle; + while ( current->theta > FG_2PI ) { + current->theta -= FG_2PI; + // (*current).theta -= angle; + // while ( (*current).theta > FG_2PI ) { + // (*current).theta -= FG_2PI; + } + // printf("(%.2f, %.2f)\n", current->theta, current->dist); + } + // printf("\n"); + + // find actual lon,lat of coordinates + // printf("convert to lon, lat relative to %.2f %.2f\n", + // origin.lon, origin.lat); + current = rad_list.begin(); + last = rad_list.end(); + for ( ; current != last ; ++current ) { + p = calc_lon_lat(origin_rad, *current); + // convert from radians to degress + p.lon *= RAD_TO_DEG; + p.lat *= RAD_TO_DEG; + // printf("(%.8f, %.8f)\n", p.lon, p.lat); + result_list.push_back(p); + } + // printf("\n"); + + return result_list; +} + + +// generate an area for a runway +list < point2d > +gen_runway_area( double lon, double lat, double heading, + double length, double width) +{ + list < point2d > result_list; + list < point2d > tmp_list; + list < point2d > :: iterator current; + list < point2d > :: iterator last; + + point2d p; + point2d origin; + double l, w; + int i; + + /* + printf("runway: lon = %.2f lat = %.2f hdg = %.2f len = %.2f width = %.2f\n", + lon, lat, heading, length, width); + */ + + origin.lon = lon; + origin.lat = lat; + l = length / 2.0; + w = width / 2.0; + + // generate untransformed runway area vertices + p.x = l; p.y = w; tmp_list.push_back(p); + p.x = l; p.y = -w; tmp_list.push_back(p); + p.x = -l; p.y = -w; tmp_list.push_back(p); + p.x = -l; p.y = w; tmp_list.push_back(p); + + /* + // display points + printf("Untransformed, unrotated runway\n"); + current = tmp_list.begin(); + last = tmp_list.end(); + while ( current != last ) { + printf("(%.2f, %.2f)\n", current->x, current->y); + ++current; + } + printf("\n"); + */ + + // rotate, transform, and convert points to lon, lat in degrees + result_list = gen_area(origin, heading, tmp_list); + + /* + // display points + printf("Results in radians.\n"); + current = result_list.begin(); + last = result_list.end(); + while ( current != last ) { + printf("(%.8f, %.8f)\n", current->lon, current->lat); + ++current; + } + printf("\n"); + */ + + return result_list; +} + + +// $Log$ +// Revision 1.5 1998/10/20 15:49:54 curt +// tweak ... +// +// Revision 1.4 1998/09/09 20:59:53 curt +// Loop construct tweaks for STL usage. +// Output airport file to be used to generate airport scenery on the fly +// by the run time sim. +// +// Revision 1.3 1998/09/09 16:26:31 curt +// Continued progress in implementing the convex hull algorithm. +// +// Revision 1.2 1998/09/04 23:04:48 curt +// Beginning of convex hull genereration routine. +// +// Revision 1.1 1998/09/01 19:34:33 curt +// Initial revision. +// +// Revision 1.1 1998/07/20 12:54:05 curt +// Initial revision. +// +// diff --git a/Tools/GenAirports/area.hxx b/Tools/GenAirports/area.hxx new file mode 100644 index 000000000..a1bbd342e --- /dev/null +++ b/Tools/GenAirports/area.hxx @@ -0,0 +1,54 @@ +// area.h -- routines to assist with inserting "areas" into FG terrain +// +// Written by Curtis Olson, started February 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#ifndef _AREA_H +#define _AREA_H + + +#include <list> + +#include "point2d.hxx" + + +// generate an area for a runway (return result points in degrees) +list < point2d > +gen_runway_area( double lon, double lat, double heading, + double length, double width); + + +#endif // _AREA_H + + +// $Log$ +// Revision 1.2 1998/09/04 23:04:49 curt +// Beginning of convex hull genereration routine. +// +// Revision 1.1 1998/09/01 19:34:33 curt +// Initial revision. +// +// Revision 1.1 1998/07/20 12:54:05 curt +// Initial revision. +// +// diff --git a/Tools/GenAirports/convex_hull.cxx b/Tools/GenAirports/convex_hull.cxx new file mode 100644 index 000000000..0cdff1dcb --- /dev/null +++ b/Tools/GenAirports/convex_hull.cxx @@ -0,0 +1,277 @@ +// convex_hull.cxx -- calculate the convex hull of a set of points +// +// Written by Curtis Olson, started September 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#include <math.h> +#include <stdio.h> + +#include <list> +#include <map> + +#ifdef NEEDNAMESPACESTD +using namespace std; +#endif + +#include <Include/fg_constants.h> + +#include "convex_hull.hxx" +#include "point2d.hxx" + + +// stl map typedefs +typedef map < double, double, less<double> > map_container; +typedef map_container::iterator map_iterator; + + +// Calculate theta of angle (a, b, c) +double calc_angle(point2d a, point2d b, point2d c) { + point2d u, v; + double udist, vdist, uv_dot, tmp; + + // u . v = ||u|| * ||v|| * cos(theta) + + u.x = b.x - a.x; + u.y = b.y - a.y; + udist = sqrt( u.x * u.x + u.y * u.y ); + // printf("udist = %.6f\n", udist); + + v.x = b.x - c.x; + v.y = b.y - c.y; + vdist = sqrt( v.x * v.x + v.y * v.y ); + // printf("vdist = %.6f\n", vdist); + + uv_dot = u.x * v.x + u.y * v.y; + // printf("uv_dot = %.6f\n", uv_dot); + + tmp = uv_dot / (udist * vdist); + // printf("tmp = %.6f\n", tmp); + + return acos(tmp); +} + + +// Test to see if angle(Pa, Pb, Pc) < 180 degrees +bool test_point(point2d Pa, point2d Pb, point2d Pc) { + point2d origin, a, b, c; + double a1, a2; + + origin.x = origin.y = 0.0; + + a.x = cos(Pa.theta) * Pa.dist; + a.y = sin(Pa.theta) * Pa.dist; + + b.x = cos(Pb.theta) * Pb.dist; + b.y = sin(Pb.theta) * Pb.dist; + + c.x = cos(Pc.theta) * Pc.dist; + c.y = sin(Pc.theta) * Pc.dist; + + // printf("a is %.6f %.6f\n", a.x, a.y); + // printf("b is %.6f %.6f\n", b.x, b.y); + // printf("c is %.6f %.6f\n", c.x, c.y); + + a1 = calc_angle(a, b, origin); + a2 = calc_angle(origin, b, c); + + // printf("a1 = %.2f a2 = %.2f\n", a1 * RAD_TO_DEG, a2 * RAD_TO_DEG); + + return ( (a1 + a2) < FG_PI ); +} + + +// calculate the convex hull of a set of points, return as a list of +// point2d. The algorithm description can be found at: +// http://riot.ieor.berkeley.edu/riot/Applications/ConvexHull/CHDetails.html +list_container convex_hull( list_container input_list ) +{ + list_iterator current, last; + map_iterator map_current, map_next, map_next_next, map_last; + + // list of translated points + list_container trans_list; + + // points sorted by radian degrees + map_container radians_map; + + // will contain the convex hull + list_container con_hull; + + point2d p, average, Pa, Pb, Pc, result; + double sum_x, sum_y; + int in_count, last_size; + + // STEP ONE: Find an average midpoint of the input set of points + current = input_list.begin(); + last = input_list.end(); + in_count = input_list.size(); + sum_x = sum_y = 0.0; + + for ( ; current != last ; ++current ) { + sum_x += (*current).x; + sum_y += (*current).y; + } + + average.x = sum_x / in_count; + average.y = sum_y / in_count; + + // printf("Average center point is %.4f %.4f\n", average.x, average.y); + + // STEP TWO: Translate input points so average is at origin + current = input_list.begin(); + last = input_list.end(); + trans_list.erase( trans_list.begin(), trans_list.end() ); + + for ( ; current != last ; ++current ) { + p.x = (*current).x - average.x; + p.y = (*current).y - average.y; + // printf("%.6f %.6f\n", p.x, p.y); + trans_list.push_back(p); + } + + // STEP THREE: convert to radians and sort by theta + current = trans_list.begin(); + last = trans_list.end(); + radians_map.erase( radians_map.begin(), radians_map.end() ); + + for ( ; current != last ; ++current) { + p = cart_to_polar_2d(*current); + if ( p.dist > radians_map[p.theta] ) { + radians_map[p.theta] = p.dist; + } + } + + // printf("Sorted list\n"); + map_current = radians_map.begin(); + map_last = radians_map.end(); + for ( ; map_current != map_last ; ++map_current ) { + p.x = (*map_current).first; + p.y = (*map_current).second; + + // printf("p is %.6f %.6f\n", p.x, p.y); + } + + // STEP FOUR: traverse the sorted list and eliminate everything + // not on the perimeter. + // printf("Traversing list\n"); + + // double check list size ... this should never fail because a + // single runway will always generate four points. + if ( radians_map.size() < 3 ) { + cout << "convex hull not possible with < 3 points" << endl; + exit(-1); + } + + // ensure that we run the while loop at least once + last_size = radians_map.size() + 1; + + while ( last_size > radians_map.size() ) { + // printf("Running an iteration of the graham scan algorithm\n"); + last_size = radians_map.size(); + + map_current = radians_map.begin(); + while ( map_current != radians_map.end() ) { + // get first element + Pa.theta = (*map_current).first; + Pa.dist = (*map_current).second; + + // get second element + map_next = map_current; + ++map_next; + if ( map_next == radians_map.end() ) { + map_next = radians_map.begin(); + } + Pb.theta = (*map_next).first; + Pb.dist = (*map_next).second; + + // get third element + map_next_next = map_next; + ++map_next_next; + if ( map_next_next == radians_map.end() ) { + map_next_next = radians_map.begin(); + } + Pc.theta = (*map_next_next).first; + Pc.dist = (*map_next_next).second; + + // printf("Pa is %.6f %.6f\n", Pa.theta, Pa.dist); + // printf("Pb is %.6f %.6f\n", Pb.theta, Pb.dist); + // printf("Pc is %.6f %.6f\n", Pc.theta, Pc.dist); + + if ( test_point(Pa, Pb, Pc) ) { + // printf("Accepted a point\n"); + // accept point, advance Pa, Pb, and Pc. + ++map_current; + } else { + // printf("REJECTED A POINT\n"); + // reject point, delete it and advance only Pb and Pc + map_next = map_current; + ++map_next; + if ( map_next == radians_map.end() ) { + map_next = radians_map.begin(); + } + radians_map.erase( map_next ); + } + } + } + + // translate back to correct lon/lat + // printf("Final sorted convex hull\n"); + con_hull.erase( con_hull.begin(), con_hull.end() ); + map_current = radians_map.begin(); + map_last = radians_map.end(); + for ( ; map_current != map_last ; ++map_current ) { + p.theta = (*map_current).first; + p.dist = (*map_current).second; + + result.x = cos(p.theta) * p.dist + average.x; + result.y = sin(p.theta) * p.dist + average.y; + + // printf("%.6f %.6f\n", result.x, result.y); + + con_hull.push_back(result); + } + + return con_hull; +} + + +// $Log$ +// Revision 1.5 1999/02/25 21:32:48 curt +// Modified to adhere to new polygon naming convention, and also to read the +// new Robin Peel aiport format. +// +// Revision 1.4 1998/09/17 18:40:42 curt +// Debug message tweaks. +// +// Revision 1.3 1998/09/09 20:59:55 curt +// Loop construct tweaks for STL usage. +// Output airport file to be used to generate airport scenery on the fly +// by the run time sim. +// +// Revision 1.2 1998/09/09 16:26:32 curt +// Continued progress in implementing the convex hull algorithm. +// +// Revision 1.1 1998/09/04 23:04:51 curt +// Beginning of convex hull genereration routine. +// +// diff --git a/Tools/GenAirports/convex_hull.hxx b/Tools/GenAirports/convex_hull.hxx new file mode 100644 index 000000000..42bf0db83 --- /dev/null +++ b/Tools/GenAirports/convex_hull.hxx @@ -0,0 +1,60 @@ +// convex_hull.hxx -- calculate the convex hull of a set of points +// +// Written by Curtis Olson, started September 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#ifndef _CONVEX_HULL_HXX +#define _CONVEX_HULL_HXX + + +#include <list> + +#ifdef NEEDNAMESPACESTD +using namespace std; +#endif + +#include "point2d.hxx" + + +// stl list typedefs +typedef list < point2d > list_container; +typedef list_container::iterator list_iterator; + + +// calculate the convex hull of a set of points, return as a list of +// point2d. The algorithm description can be found at: +// http://riot.ieor.berkeley.edu/riot/Applications/ConvexHull/CHDetails.html +list_container convex_hull( list_container input_list ); + + +#endif // _CONVEX_HULL_HXX + + +// $Log$ +// Revision 1.2 1998/09/09 16:26:33 curt +// Continued progress in implementing the convex hull algorithm. +// +// Revision 1.1 1998/09/04 23:04:51 curt +// Beginning of convex hull genereration routine. +// +// diff --git a/Tools/GenAirports/main.cxx b/Tools/GenAirports/main.cxx new file mode 100644 index 000000000..7ad07f6db --- /dev/null +++ b/Tools/GenAirports/main.cxx @@ -0,0 +1,374 @@ +// main.cxx -- main loop +// +// Written by Curtis Olson, started March 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <Include/compiler.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#include <list> +#include <stdio.h> +#include <string.h> +#include STL_STRING + +#include <Bucket/newbucket.hxx> +#include <Debug/logstream.hxx> +#include <Include/fg_constants.h> +#include <Misc/fgstream.hxx> +#include <Polygon/index.hxx> + +#include "area.hxx" +#include "convex_hull.hxx" + + +// write out airport data +void write_airport( long int p_index, list_container hull_list, FGBucket b, + const string& root, const bool cut_and_keep ) { + char tile_name[256], poly_index[256]; + + string base = b.gen_base_path(); + string path = root + "/Scenery/" + base; + string command = "mkdir -p " + path; + system( command.c_str() ); + + long int b_index = b.gen_index(); + sprintf(tile_name, "%ld", b_index); + string aptfile = path + "/" + tile_name; + + sprintf( poly_index, "%ld", p_index ); + aptfile += "."; + aptfile += poly_index; + cout << "apt file = " << aptfile << endl; + + FILE *fd; + if ( (fd = fopen(aptfile.c_str(), "a")) == NULL ) { + cout << "Cannot open file: " << aptfile << endl; + exit(-1); + } + + // polygon type + if ( cut_and_keep ) { + fprintf( fd, "AirportKeep\n" ); + } else { + fprintf( fd, "AirportIgnore\n" ); + } + + // number of contours + fprintf( fd, "1\n" ); + + // size of first contour + fprintf( fd, "%d\n", hull_list.size() ); + + // write contour (polygon) points + list_iterator current = hull_list.begin(); + list_iterator last = hull_list.end(); + for ( ; current != last ; ++current ) { + fprintf( fd, "%.7f %.7f\n", (*current).lon, (*current).lat ); + } + + fclose(fd); +} + + +// process and airport + runway list +void process_airport( string airport, list < string > & runway_list, + const string& root ) { + list_container rwy_list, apt_list, hull_list; + list_iterator current, last; + + // parse main airport information + int elev; + + cout << airport << endl; + string apt_type = airport.substr(0, 1); + string apt_code = airport.substr(2, 4); + string apt_lat = airport.substr(7, 10); + string apt_lon = airport.substr(18, 11); + string apt_elev = airport.substr(30, 5); + sscanf( apt_elev.c_str(), "%d", &elev ); + string apt_use = airport.substr(36, 1); + string apt_twr = airport.substr(37, 1); + string apt_bldg = airport.substr(38, 1); + string apt_name = airport.substr(40); + + /* + cout << " type = " << apt_type << endl; + cout << " code = " << apt_code << endl; + cout << " lat = " << apt_lat << endl; + cout << " lon = " << apt_lon << endl; + cout << " elev = " << apt_elev << " " << elev << endl; + cout << " use = " << apt_use << endl; + cout << " twr = " << apt_twr << endl; + cout << " bldg = " << apt_bldg << endl; + cout << " name = " << apt_name << endl; + */ + + // parse runways and generate the vertex list + string rwy_str; + double lon, lat, hdg; + int len, width; + + list < string >::iterator last_runway = runway_list.end(); + for ( list < string >::iterator current_runway = runway_list.begin(); + current_runway != last_runway ; ++current_runway ) { + rwy_str = (*current_runway); + + cout << rwy_str << endl; + string rwy_no = rwy_str.substr(2, 4); + string rwy_lat = rwy_str.substr(6, 10); + sscanf( rwy_lat.c_str(), "%lf", &lat); + string rwy_lon = rwy_str.substr(17, 11); + sscanf( rwy_lon.c_str(), "%lf", &lon); + string rwy_hdg = rwy_str.substr(29, 7); + sscanf( rwy_hdg.c_str(), "%lf", &hdg); + string rwy_len = rwy_str.substr(36, 7); + sscanf( rwy_len.c_str(), "%d", &len); + string rwy_width = rwy_str.substr(43, 4); + sscanf( rwy_width.c_str(), "%d", &width); + string rwy_sfc = rwy_str.substr(47, 4); + string rwy_end1 = rwy_str.substr(52, 8); + string rwy_end2 = rwy_str.substr(61, 8); + + /* + cout << " no = " << rwy_no << endl; + cout << " lat = " << rwy_lat << " " << lat << endl; + cout << " lon = " << rwy_lon << " " << lon << endl; + cout << " hdg = " << rwy_hdg << " " << hdg << endl; + cout << " len = " << rwy_len << " " << len << endl; + cout << " width = " << rwy_width << " " << width << endl; + cout << " sfc = " << rwy_sfc << endl; + cout << " end1 = " << rwy_end1 << endl; + cout << " end2 = " << rwy_end2 << endl; + */ + + rwy_list = gen_runway_area( lon, lat, hdg * DEG_TO_RAD, + (double)len * FEET_TO_METER, + (double)width * FEET_TO_METER ); + + // add rwy_list to apt_list + current = rwy_list.begin(); + last = rwy_list.end(); + for ( ; current != last ; ++current ) { + apt_list.push_back(*current); + } + } + + if ( apt_list.size() == 0 ) { + cout << "no runway points generated" << endl; + return; + } + + // printf("Runway points in degrees\n"); + // current = apt_list.begin(); + // last = apt_list.end(); + // for ( ; current != last; ++current ) { + // printf( "%.5f %.5f\n", current->lon, current->lat ); + // } + // printf("\n"); + + // generate convex hull + hull_list = convex_hull(apt_list); + + // get next polygon index + long int index = poly_index_next(); + + // find average center, min, and max point of convex hull + point2d average, min, max; + double sum_x, sum_y; + int count = hull_list.size(); + current = hull_list.begin(); + last = hull_list.end(); + sum_x = sum_y = 0.0; + min.x = min.y = 200.0; + max.x = max.y = -200.0; + for ( ; current != last; ++current ) { + // printf("return = %.6f %.6f\n", (*current).x, (*current).y); + sum_x += (*current).x; + sum_y += (*current).y; + + if ( (*current).x < min.x ) { min.x = (*current).x; } + if ( (*current).y < min.y ) { min.y = (*current).y; } + if ( (*current).x > max.x ) { max.x = (*current).x; } + if ( (*current).y > max.y ) { max.y = (*current).y; } + } + average.x = sum_x / count; + average.y = sum_y / count; + + // find buckets for center, min, and max points of convex hull. + // note to self: self, you should think about checking for runways + // that span the data line + FGBucket b(average.lon, average.lat); + FGBucket b_min(min.x, min.y); + FGBucket b_max(max.x, max.y); + cout << "Bucket center = " << b << endl; + cout << "Bucket min = " << b_min << endl; + cout << "Bucket max = " << b_max << endl; + + if ( b_min == b_max ) { + write_airport( index, hull_list, b, root, true ); + } else { + FGBucket b_cur; + int dx, dy, i, j; + + fgBucketDiff(b_min, b_max, &dx, &dy); + cout << "airport spans tile boundaries" << endl; + cout << " dx = " << dx << " dy = " << dy << endl; + + if ( (dx > 2) || (dy > 2) ) { + cout << "somethings really wrong!!!!" << endl; + exit(-1); + } + + for ( j = 0; j <= dy; j++ ) { + for ( i = 0; i <= dx; i++ ) { + b_cur = fgBucketOffset(min.x, min.y, i, j); + if ( b_cur == b ) { + write_airport( index, hull_list, b_cur, root, true ); + } else { + write_airport( index, hull_list, b_cur, root, false ); + } + } + } + // string answer; cin >> answer; + } +} + + +// reads the apt_full file and extracts and processes the individual +// airport records +int main( int argc, char **argv ) { + list < string > runway_list; + string airport, last_airport; + string line; + char tmp[256]; + + fglog().setLogLevels( FG_ALL, FG_DEBUG ); + + if ( argc != 3 ) { + FG_LOG( FG_GENERAL, FG_ALERT, + "Usage " << argv[0] << " <apt_file> <work_dir>" ); + exit(-1); + } + + // make work directory + string work_dir = argv[2]; + string command = "mkdir -p " + work_dir; + system( command.c_str() ); + + // initialize persistant polygon counter + string counter_file = work_dir + "/../work.counter"; + poly_index_init( counter_file ); + + fg_gzifstream in( argv[1] ); + if ( !in ) { + FG_LOG( FG_GENERAL, FG_ALERT, "Cannot open file: " << argv[1] ); + exit(-1); + } + + // throw away the first 3 lines + in.getline(tmp, 256); + in.getline(tmp, 256); + in.getline(tmp, 256); + + last_airport = ""; + + while ( ! in.eof() ) { + in.getline(tmp, 256); + line = tmp; + // cout << line << endl; + + if ( line.length() == 0 ) { + // empty, skip + } else if ( line[0] == '#' ) { + // comment, skip + } else if ( (line[0] == 'A') || (line[0] == 'S') ) { + // start of airport record + airport = line; + + if ( last_airport.length() ) { + // process previous record + process_airport(last_airport, runway_list, argv[2]); + } + + // clear runway list for start of next airport + runway_list.erase(runway_list.begin(), runway_list.end()); + + last_airport = airport; + } else if ( line[0] == 'R' ) { + // runway entry + runway_list.push_back(line); + } else if ( line == "[End]" ) { + // end of file + break; + } else { + FG_LOG( FG_GENERAL, FG_ALERT, + "Unknown line in file" << endl << line ); + exit(-1); + } + } + + if ( last_airport.length() ) { + // process previous record + process_airport(last_airport, runway_list, argv[2]); + } + + return 0; +} + + +// $Log$ +// Revision 1.11 1999/03/19 00:27:38 curt +// Use long int for index instead of just int. +// +// Revision 1.10 1999/03/17 23:51:25 curt +// Changed polygon index counter file. +// +// Revision 1.9 1999/03/12 22:54:19 curt +// Rearrange a bit of code ... +// +// Revision 1.8 1999/03/01 15:35:26 curt +// Fixed bug in output format generated. +// +// Revision 1.7 1999/02/25 21:32:49 curt +// Modified to adhere to new polygon naming convention, and also to read the +// new Robin Peel aiport format. +// +// Revision 1.6 1999/02/11 01:10:51 curt +// Start of scenery revamp project. +// +// Revision 1.5 1998/09/17 18:40:43 curt +// Debug message tweaks. +// +// Revision 1.4 1998/09/09 20:59:56 curt +// Loop construct tweaks for STL usage. +// Output airport file to be used to generate airport scenery on the fly +// by the run time sim. +// +// diff --git a/Tools/GenAirports/point2d.cxx b/Tools/GenAirports/point2d.cxx new file mode 100644 index 000000000..755224477 --- /dev/null +++ b/Tools/GenAirports/point2d.cxx @@ -0,0 +1,45 @@ +// point2d.cxx -- 2d coordinate routines +// +// Written by Curtis Olson, started September 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#include <math.h> + +#include "point2d.hxx" + + +// convert a point from cartesian to polar coordinates +point2d cart_to_polar_2d(point2d in) { + point2d result; + result.dist = sqrt( in.x * in.x + in.y * in.y ); + result.theta = atan2(in.y, in.x); + + return(result); +} + + +// $Log$ +// Revision 1.1 1998/09/04 23:04:53 curt +// Beginning of convex hull genereration routine. +// +// diff --git a/Tools/GenAirports/point2d.hxx b/Tools/GenAirports/point2d.hxx new file mode 100644 index 000000000..199524047 --- /dev/null +++ b/Tools/GenAirports/point2d.hxx @@ -0,0 +1,59 @@ +// point2d.hxx -- define a 2d point class +// +// Written by Curtis Olson, started February 1998. +// +// Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) +// + + +#ifndef _POINT2D_HXX +#define _POINT2D_HXX + + +#include <list> + + +class point2d { +public: + union { + double x; + double dist; + double lon; + }; + union { + double y; + double theta; + double lat; + }; +}; + + +// convert a point from cartesian to polar coordinates +point2d cart_to_polar_2d(point2d in); + + +#endif // _POINT2D_HXX + + +// $Log$ +// Revision 1.1 1998/09/04 23:04:53 curt +// Beginning of convex hull genereration routine. +// +// diff --git a/Tools/GenOutput/Makefile.am b/Tools/GenOutput/Makefile.am new file mode 100644 index 000000000..28ad535a2 --- /dev/null +++ b/Tools/GenOutput/Makefile.am @@ -0,0 +1,9 @@ +noinst_LIBRARIES = libGenOutput.a + +libGenOutput_a_SOURCES = genobj.cxx genobj.hxx + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Tools/Lib \ + -I$(top_builddir)/Tools/Construct diff --git a/Tools/GenOutput/genobj.cxx b/Tools/GenOutput/genobj.cxx new file mode 100644 index 000000000..fc3cc3b68 --- /dev/null +++ b/Tools/GenOutput/genobj.cxx @@ -0,0 +1,469 @@ +// genobj.hxx -- Generate the flight gear "obj" file format from the +// triangle output +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include <time.h> + +#include <Math/mat3.h> +#include <Polygon/names.hxx> +#include <Tools/scenery_version.hxx> + +#include "genobj.hxx" + + +// build the wgs-84 point list +void FGGenOutput::gen_wgs84_points( const FGArray& array ) { + cout << "calculating wgs84 point" << endl; + Point3D geod, radians, cart; + + const_point_list_iterator current = geod_nodes.begin(); + const_point_list_iterator last = geod_nodes.end(); + + double real_z; + + for ( ; current != last; ++current ) { + geod = *current; + + real_z = array.interpolate_altitude( geod.x() * 3600.0, + geod.y() * 3600.0 ); + + // convert to radians + radians = Point3D( geod.x() * DEG_TO_RAD, + geod.y() * DEG_TO_RAD, + real_z ); + + cart = fgGeodToCart(radians); + // cout << cart << endl; + wgs84_nodes.push_back(cart); + } +} + + +// build the node -> element (triangle) reverse lookup table. there +// is an entry for each point containing a list of all the triangles +// that share that point. +void FGGenOutput::gen_node_ele_lookup_table() { + int_list ele_list; + ele_list.erase( ele_list.begin(), ele_list.end() ); + + // initialize reverse_ele_lookup structure by creating an empty + // list for each point + const_point_list_iterator w_current = wgs84_nodes.begin(); + const_point_list_iterator w_last = wgs84_nodes.end(); + for ( ; w_current != w_last; ++w_current ) { + reverse_ele_lookup.push_back( ele_list ); + } + + // traverse triangle structure building reverse lookup table + const_triele_list_iterator current = tri_elements.begin(); + const_triele_list_iterator last = tri_elements.end(); + int counter = 0; + for ( ; current != last; ++current ) { + reverse_ele_lookup[ current->get_n1() ].push_back( counter ); + reverse_ele_lookup[ current->get_n2() ].push_back( counter ); + reverse_ele_lookup[ current->get_n3() ].push_back( counter ); + ++counter; + } +} + + +// caclulate the normal for the specified triangle face +Point3D FGGenOutput::calc_normal( int i ) { + double v1[3], v2[3], normal[3]; + double temp; + + Point3D p1 = wgs84_nodes[ tri_elements[i].get_n1() ]; + Point3D p2 = wgs84_nodes[ tri_elements[i].get_n2() ]; + Point3D p3 = wgs84_nodes[ tri_elements[i].get_n3() ]; + + v1[0] = p2.x() - p1.x(); v1[1] = p2.y() - p1.y(); v1[2] = p2.z() - p1.z(); + v2[0] = p3.x() - p1.x(); v2[1] = p3.y() - p1.y(); v2[2] = p3.z() - p1.z(); + + MAT3cross_product(normal, v1, v2); + MAT3_NORMALIZE_VEC(normal,temp); + + return Point3D( normal[0], normal[1], normal[2] ); +} + + +// build the face normal list +void FGGenOutput::gen_face_normals() { + // traverse triangle structure building the face normal table + + cout << "calculating face normals" << endl; + + for ( int i = 0; i < (int)tri_elements.size(); i++ ) { + // cout << calc_normal( i ) << endl; + face_normals.push_back( calc_normal( i ) ); + } + +} + + +// calculate the normals for each point in wgs84_nodes +void FGGenOutput::gen_normals() { + Point3D normal; + cout << "caculating node normals" << endl; + + // for each node + for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) { + int_list tri_list = reverse_ele_lookup[i]; + + int_list_iterator current = tri_list.begin(); + int_list_iterator last = tri_list.end(); + + Point3D average( 0.0 ); + + // for each triangle that shares this node + for ( ; current != last; ++current ) { + normal = face_normals[ *current ]; + average += normal; + // cout << normal << endl; + } + + average /= tri_list.size(); + // cout << "average = " << average << endl; + + point_normals.push_back( average ); + } +} + + +// calculate the global bounding sphere. Center is the average of the +// points. +void FGGenOutput::calc_gbs() { + double dist_squared; + double radius_squared = 0; + + gbs_center = Point3D( 0.0 ); + + const_point_list_iterator current = wgs84_nodes.begin(); + const_point_list_iterator last = wgs84_nodes.end(); + + for ( ; current != last; ++current ) { + gbs_center += *current; + } + + gbs_center /= wgs84_nodes.size(); + + current = wgs84_nodes.begin(); + for ( ; current != last; ++current ) { + dist_squared = gbs_center.distance3Dsquared(*current); + if ( dist_squared > radius_squared ) { + radius_squared = dist_squared; + } + } + + gbs_radius = sqrt(radius_squared); +} + + +// build the necessary output structures based on the triangulation +// data +int FGGenOutput::build( const FGArray& array, const FGTriangle& t ) { + FGTriNodes trinodes = t.get_out_nodes(); + + // copy the geodetic node list into this class + geod_nodes = trinodes.get_node_list(); + + // copy the triangle list into this class + tri_elements = t.get_elelist(); + + // build the trifan list + cout << "total triangles = " << tri_elements.size() << endl; + FGGenFans f; + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + triele_list area_tris; + area_tris.erase( area_tris.begin(), area_tris.end() ); + + const_triele_list_iterator t_current = tri_elements.begin(); + const_triele_list_iterator t_last = tri_elements.end(); + for ( ; t_current != t_last; ++t_current ) { + if ( (int)t_current->get_attribute() == i ) { + area_tris.push_back( *t_current ); + } + } + + if ( (int)area_tris.size() > 0 ) { + cout << "generating fans for area = " << i << endl; + fans[i] = f.greedy_build( area_tris ); + } + } + + // generate the point list in wgs-84 coordinates + gen_wgs84_points( array ); + + // calculate the global bounding sphere + calc_gbs(); + cout << "center = " << gbs_center << " radius = " << gbs_radius << endl; + + // build the node -> element (triangle) reverse lookup table + gen_node_ele_lookup_table(); + + // build the face normal list + gen_face_normals(); + + // calculate the normals for each point in wgs84_nodes + gen_normals(); + + return 1; +} + + +// caclulate the bounding sphere for a list of triangle faces +void FGGenOutput::calc_group_bounding_sphere( const fan_list& fans, + Point3D *center, double *radius ) +{ + cout << "calculate group bounding sphere for " << fans.size() << " fans." + << endl; + + // generate a list of unique points from the triangle list + FGTriNodes nodes; + + const_fan_list_iterator f_current = fans.begin(); + const_fan_list_iterator f_last = fans.end(); + for ( ; f_current != f_last; ++f_current ) { + const_int_list_iterator i_current = f_current->begin(); + const_int_list_iterator i_last = f_current->end(); + for ( ; i_current != i_last; ++i_current ) { + Point3D p1 = wgs84_nodes[ *i_current ]; + nodes.unique_add(p1); + } + } + + // find average of point list + Point3D c( 0.0 ); + point_list points = nodes.get_node_list(); + // cout << "found " << points.size() << " unique nodes" << endl; + point_list_iterator p_current = points.begin(); + point_list_iterator p_last = points.end(); + for ( ; p_current != p_last; ++p_current ) { + c += *p_current; + } + c /= points.size(); + + // find max radius + double dist_squared; + double max_squared = 0; + + p_current = points.begin(); + p_last = points.end(); + for ( ; p_current != p_last; ++p_current ) { + dist_squared = c.distance3Dsquared(*p_current); + if ( dist_squared > max_squared ) { + max_squared = dist_squared; + } + } + + *center = c; + *radius = sqrt(max_squared); +} + + +// caclulate the bounding sphere for the specified triangle face +void FGGenOutput::calc_bounding_sphere( const FGTriEle& t, + Point3D *center, double *radius ) +{ + Point3D c( 0.0 ); + + Point3D p1 = wgs84_nodes[ t.get_n1() ]; + Point3D p2 = wgs84_nodes[ t.get_n2() ]; + Point3D p3 = wgs84_nodes[ t.get_n3() ]; + + c = p1 + p2 + p3; + c /= 3; + + double dist_squared; + double max_squared = 0; + + dist_squared = c.distance3Dsquared(p1); + if ( dist_squared > max_squared ) { + max_squared = dist_squared; + } + + dist_squared = c.distance3Dsquared(p2); + if ( dist_squared > max_squared ) { + max_squared = dist_squared; + } + + dist_squared = c.distance3Dsquared(p3); + if ( dist_squared > max_squared ) { + max_squared = dist_squared; + } + + *center = c; + *radius = sqrt(max_squared); +} + + +// write out the fgfs scenery file +int FGGenOutput::write( const string& base, const FGBucket& b ) { + Point3D p; + + string dir = base + "/Scenery/" + b.gen_base_path(); + string command = "mkdir -p " + dir; + system(command.c_str()); + + string file = dir + "/" + b.gen_index_str(); + cout << "Output file = " << file << endl; + + FILE *fp; + if ( (fp = fopen( file.c_str(), "w" )) == NULL ) { + cout << "ERROR: opening " << file << " for writing!" << endl; + exit(-1); + } + + // write headers + fprintf(fp, "# FGFS Scenery Version %s\n", FG_SCENERY_FILE_FORMAT); + + time_t calendar_time = time(NULL); + struct tm *local_tm; + local_tm = localtime( &calendar_time ); + char time_str[256]; + strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm); + fprintf(fp, "# Created %s\n", time_str ); + fprintf(fp, "\n"); + + // write global bounding sphere + fprintf(fp, "# gbs %.5f %.5f %.5f %.2f\n", + gbs_center.x(), gbs_center.y(), gbs_center.z(), gbs_radius); + fprintf(fp, "\n"); + + // write nodes + fprintf(fp, "# vertex list\n"); + const_point_list_iterator w_current = wgs84_nodes.begin(); + const_point_list_iterator w_last = wgs84_nodes.end(); + for ( ; w_current != w_last; ++w_current ) { + p = *w_current - gbs_center; + fprintf(fp, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z()); + } + fprintf(fp, "\n"); + + // write vertex normals + fprintf(fp, "# vertex normal list\n"); + const_point_list_iterator n_current = point_normals.begin(); + const_point_list_iterator n_last = point_normals.end(); + for ( ; n_current != n_last; ++n_current ) { + p = *n_current; + fprintf(fp, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z()); + } + fprintf(fp, "\n"); + + // write triangles (grouped by type for now) + Point3D center; + double radius; + fprintf(fp, "# triangle groups\n"); + fprintf(fp, "\n"); + + int total_tris = 0; + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + if ( (int)fans[i].size() > 0 ) { + string attr_name = get_area_name( (AreaType)i ); + calc_group_bounding_sphere( fans[i], ¢er, &radius ); + cout << "writing " << (int)fans[i].size() << " fans for " + << attr_name << endl; + + fprintf(fp, "# usemtl %s\n", attr_name.c_str() ); + fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n", + center.x(), center.y(), center.z(), radius); + + fan_list_iterator f_current = fans[i].begin(); + fan_list_iterator f_last = fans[i].end(); + for ( ; f_current != f_last; ++f_current ) { + fprintf( fp, "tf" ); + total_tris += f_current->size() - 2; + int_list_iterator i_current = f_current->begin(); + int_list_iterator i_last = f_current->end(); + for ( ; i_current != i_last; ++i_current ) { + fprintf( fp, " %d", *i_current ); + } + fprintf( fp, "\n" ); + +#if 0 + { + int_list_iterator i_current = f_current->begin(); + int_list_iterator i_last = f_current->end(); + int center = *i_current; + ++i_current; + int n2 = *i_current; + ++i_current; + for ( ; i_current != i_last; ++i_current ) { + int n3 = *i_current; + fprintf( fp, "f %d %d %d\n", center, n2, n3 ); + n2 = n3; + } + } +#endif + } + + fprintf( fp, "\n" ); + } + } + cout << "wrote " << total_tris << " tris to output file" << endl; + + fclose(fp); + + command = "gzip --force --best " + file; + system(command.c_str()); + + return 1; +} + + +// $Log$ +// Revision 1.10 1999/03/31 23:46:57 curt +// Debugging output tweaks. +// +// Revision 1.9 1999/03/31 13:26:40 curt +// Debugging output tweeaks. +// +// Revision 1.8 1999/03/31 05:35:05 curt +// Fixed bug in genfans (deleting the wrong triangles from the available pool.) +// +// Revision 1.7 1999/03/30 23:50:43 curt +// Modifications to fanify by attribute. +// +// Revision 1.6 1999/03/29 13:11:03 curt +// Shuffled stl type names a bit. +// Began adding support for tri-fanning (or maybe other arrangments too.) +// +// Revision 1.5 1999/03/27 14:06:42 curt +// Tweaks to bounding sphere calculation routines. +// Group like triangles together for output to be in a single display list, +// even though they are individual, non-fanified, triangles. +// +// Revision 1.4 1999/03/27 05:23:22 curt +// Interpolate real z value of all nodes from dem data. +// Write scenery file to correct location. +// Pass along correct triangle attributes and write to output file. +// +// Revision 1.3 1999/03/25 19:04:21 curt +// Preparations for outputing scenery file to correct location. +// +// Revision 1.2 1999/03/23 22:02:03 curt +// Worked on creating data to output ... normals, bounding spheres, etc. +// +// Revision 1.1 1999/03/22 23:51:51 curt +// Initial revision. +// diff --git a/Tools/GenOutput/genobj.hxx b/Tools/GenOutput/genobj.hxx new file mode 100644 index 000000000..55f984452 --- /dev/null +++ b/Tools/GenOutput/genobj.hxx @@ -0,0 +1,165 @@ +// genobj.hxx -- Generate the flight gear "obj" file format from the +// triangle output +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _GENOBJ_HXX +#define _GENOBJ_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include <Include/compiler.h> + +#include STL_STRING + +#include <Bucket/newbucket.hxx> +#include <Math/fg_geodesy.hxx> +#include <Math/point3d.hxx> + +#include <Combine/genfans.hxx> +#include <Main/construct_types.hxx> +#include <Triangulate/triangle.hxx> + +FG_USING_STD(string); +FG_USING_STD(vector); + + +typedef vector < int_list > belongs_to_list; +typedef belongs_to_list::iterator belongs_to_list_iterator; +typedef belongs_to_list::const_iterator belongs_to_list_tripoly_iterator; + + +class FGGenOutput { + +private: + + // node list in geodetic coordinats + point_list geod_nodes; + + // node list in cartesian coords (wgs84 model) + point_list wgs84_nodes; + + // face normal list (for flat shading) + point_list face_normals; + + // normal list (for each point) in cart coords (for smooth + // shading) + point_list point_normals; + + // triangles (by index into point list) + triele_list tri_elements; + + // fan list + fan_list fans[FG_MAX_AREA_TYPES]; + + // for each node, a list of triangle indices that contain this node + belongs_to_list reverse_ele_lookup; + + // global bounding sphere + Point3D gbs_center; + double gbs_radius; + + // build the wgs-84 point list + void gen_wgs84_points( const FGArray& array ); + + // build the node -> element (triangle) reverse lookup table. + // there is an entry for each point containing a list of all the + // triangles that share that point. + void gen_node_ele_lookup_table(); + + // calculate the normals for each point in wgs84_nodes + void gen_normals(); + + // build the face normal list + void gen_face_normals(); + + // caclulate the normal for the specified triangle face + Point3D calc_normal( int i ); + + // calculate the global bounding sphere. Center is the average of + // the points. + void calc_gbs(); + + // caclulate the bounding sphere for a list of triangle faces + void calc_group_bounding_sphere( const fan_list& fans, + Point3D *center, double *radius ); + + // caclulate the bounding sphere for the specified triangle face + void calc_bounding_sphere( const FGTriEle& t, + Point3D *center, double *radius ); + +public: + + // Constructor && Destructor + inline FGGenOutput() { } + inline ~FGGenOutput() { } + + // build the necessary output structures based on the + // triangulation data + int build( const FGArray& array, const FGTriangle& t ); + + // write out the fgfs scenery file + int write( const string& base, const FGBucket& b ); +}; + + +#endif // _GENOBJ_HXX + + +// $Log$ +// Revision 1.9 1999/03/31 23:46:58 curt +// Debugging output tweaks. +// +// Revision 1.8 1999/03/30 23:50:44 curt +// Modifications to fanify by attribute. +// +// Revision 1.7 1999/03/29 13:11:04 curt +// Shuffled stl type names a bit. +// Began adding support for tri-fanning (or maybe other arrangments too.) +// +// Revision 1.6 1999/03/27 14:06:43 curt +// Tweaks to bounding sphere calculation routines. +// Group like triangles together for output to be in a single display list, +// even though they are individual, non-fanified, triangles. +// +// Revision 1.5 1999/03/27 05:23:23 curt +// Interpolate real z value of all nodes from dem data. +// Write scenery file to correct location. +// Pass along correct triangle attributes and write to output file. +// +// Revision 1.4 1999/03/25 19:04:22 curt +// Preparations for outputing scenery file to correct location. +// +// Revision 1.3 1999/03/23 22:02:04 curt +// Worked on creating data to output ... normals, bounding spheres, etc. +// +// Revision 1.2 1999/03/23 17:44:49 curt +// Beginning work on generating output scenery. +// +// Revision 1.1 1999/03/22 23:51:51 curt +// Initial revision. +// diff --git a/Tools/Lib/Makefile.am b/Tools/Lib/Makefile.am new file mode 100644 index 000000000..6c6ad9d3e --- /dev/null +++ b/Tools/Lib/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = \ + DEM \ + Polygon \ + Triangle diff --git a/Tools/Main/Makefile.am b/Tools/Main/Makefile.am new file mode 100644 index 000000000..95e1e4f14 --- /dev/null +++ b/Tools/Main/Makefile.am @@ -0,0 +1,24 @@ +bin_PROGRAMS = construct + +construct_SOURCES = construct.cxx construct_types.hxx + +construct_LDADD = \ + $(top_builddir)/Tools/Construct/Array/libArray.a \ + $(top_builddir)/Tools/Construct/Clipper/libClipper.a \ + $(top_builddir)/Tools/Construct/GenOutput/libGenOutput.a \ + $(top_builddir)/Tools/Construct/Combine/libCombine.a \ + $(top_builddir)/Tools/Construct/Triangulate/libTriangulate.a \ + $(top_builddir)/Tools/Lib/Polygon/libPolygon.a \ + $(top_builddir)/Tools/Lib/Triangle/libTriangle.a \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(top_builddir)/Lib/Math/libMath.a \ + $(top_builddir)/Lib/Misc/libMisc.a \ + $(top_builddir)/Lib/Debug/libDebug.a \ + $(top_builddir)/Lib/zlib/libz.a \ + -lgpc -lgfc + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Tools/Lib \ + -I$(top_builddir)/Tools/Construct diff --git a/Tools/Main/construct.cxx b/Tools/Main/construct.cxx new file mode 100644 index 000000000..1c5c117a9 --- /dev/null +++ b/Tools/Main/construct.cxx @@ -0,0 +1,373 @@ +// main.cxx -- top level construction routines +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include <sys/types.h> // for directory reading +#include <dirent.h> // for directory reading + +#include <Bucket/newbucket.hxx> +#include <Include/fg_constants.h> + +#include <Debug/logstream.hxx> +#include <Array/array.hxx> +#include <Clipper/clipper.hxx> +#include <GenOutput/genobj.hxx> +#include <Triangulate/triangle.hxx> + + +// load regular grid of elevation data (dem based), return list of +// fitted nodes +int load_dem(const string& work_base, FGBucket& b, FGArray& array) { + point_list result; + string base = b.gen_base_path(); + + string dem_path = work_base + ".dem" + "/Scenery/" + base + + "/" + b.gen_index_str() + ".dem"; + cout << "dem_path = " << dem_path << endl; + + if ( ! array.open(dem_path) ) { + cout << "ERROR: cannot open " << dem_path << endl; + } + + array.parse( b ); + + return 1; +} + +// fit dem nodes, return number of fitted nodes +int fit_dem(FGArray& array, int error) { + return array.fit( error ); +} + + +// do actual scan of directory and loading of files +int actual_load_polys( const string& dir, FGBucket& b, FGClipper& clipper ) { + int counter = 0; + string base = b.gen_base_path(); + string tile_str = b.gen_index_str(); + string ext; + + DIR *d; + struct dirent *de; + + if ( (d = opendir( dir.c_str() )) == NULL ) { + cout << "cannot open directory " << dir << "\n"; + return 0; + } + + // load all matching polygon files + string file, f_index, full_path; + int pos; + while ( (de = readdir(d)) != NULL ) { + file = de->d_name; + pos = file.find("."); + f_index = file.substr(0, pos); + + if ( tile_str == f_index ) { + ext = file.substr(pos + 1); + cout << file << " " << f_index << " '" << ext << "'" << endl; + full_path = dir + "/" + file; + if ( (ext == "dem") || (ext == "dem.gz") ) { + // skip + } else { + cout << "ext = '" << ext << "'" << endl; + clipper.load_polys( full_path ); + ++counter; + } + } + } + + return counter; +} + + +// load all 2d polygons matching the specified base path and clip +// against each other to resolve any overlaps +int load_polys( const string& work_base, FGBucket& b, FGClipper& clipper) { + string base = b.gen_base_path(); + int result; + + // initialize clipper + clipper.init(); + + // load airports + string poly_path = work_base + ".apt" + "/Scenery/" + base; + cout << "poly_path = " << poly_path << endl; + result = actual_load_polys( poly_path, b, clipper ); + cout << " loaded " << result << " polys" << endl; + + // load hydro + poly_path = work_base + ".hydro" + "/Scenery/" + base; + cout << "poly_path = " << poly_path << endl; + result = actual_load_polys( poly_path, b, clipper ); + cout << " loaded " << result << " polys" << endl; + + point2d min, max; + min.x = b.get_center_lon() - 0.5 * b.get_width(); + min.y = b.get_center_lat() - 0.5 * b.get_height(); + max.x = b.get_center_lon() + 0.5 * b.get_width(); + max.y = b.get_center_lat() + 0.5 * b.get_height(); + + // do clipping + cout << "clipping polygons" << endl; + clipper.clip_all(min, max); + + return 1; +} + + +// triangulate the data for each polygon +void do_triangulate( const FGArray& array, const FGClipper& clipper, + FGTriangle& t ) { + // first we need to consolidate the points of the DEM fit list and + // all the polygons into a more "Triangle" friendly format + + point_list corner_list = array.get_corner_node_list(); + point_list fit_list = array.get_fit_node_list(); + FGgpcPolyList gpc_polys = clipper.get_polys_clipped(); + + cout << "ready to build node list and polygons" << endl; + t.build( corner_list, fit_list, gpc_polys ); + cout << "done building node list and polygons" << endl; + + cout << "ready to do triangulation" << endl; + t.run_triangulate(); + cout << "finished triangulation" << endl; +} + + +// generate the flight gear scenery file +void do_output( const string& base, const FGBucket &b, const FGTriangle& t, + const FGArray& array, FGGenOutput& output ) { + output.build( array, t ); + output.write( base, b ); +} + + +void construct_tile( const string& work_base, const string& output_base, + FGBucket& b ) +{ + cout << "Construct tile, bucket = " << b << endl; + + // fit with ever increasing error tolerance until we produce <= + // 80% of max nodes. We should really have the sim end handle + // arbitrarily complex tiles. + + const int min_nodes = 50; + const int max_nodes = (int)(MAX_NODES * 0.8); + + bool acceptable = false; + double error = 200.0; + int count = 0; + + // load and fit grid of elevation data + FGArray array; + load_dem( work_base, b, array ); + + FGTriangle t; + + while ( ! acceptable ) { + // fit the data + array.fit( error ); + + // load and clip 2d polygon data + FGClipper clipper; + load_polys( work_base, b, clipper ); + + // triangulate the data for each polygon + do_triangulate( array, clipper, t ); + + acceptable = true; + + count = t.get_out_nodes_size(); + + if ( (count < min_nodes) && (error >= 25.0) ) { + // reduce error tolerance until number of points exceeds the + // minimum threshold + cout << "produced too few nodes ..." << endl; + + acceptable = false; + + error /= 1.5; + cout << "Setting error to " << error << " and retrying fit." + << endl; + } + + if ( (count > max_nodes) && (error <= 1000.0) ) { + // increase error tolerance until number of points drops below + // the maximum threshold + cout << "produced too many nodes ..." << endl; + + acceptable = false; + + error *= 1.5; + cout << "Setting error to " << error << " and retrying fit." + << endl; + } + } + + cout << "finished fit with error = " << error << " node count = " + << count << endl; + + // generate the output + FGGenOutput output; + do_output( output_base, b, t, array, output ); +} + + +main(int argc, char **argv) { + double lon, lat; + + fglog().setLogLevels( FG_ALL, FG_DEBUG ); + + if ( argc != 3 ) { + cout << "Usage: " << argv[0] << " <work_base> <output_base>" << endl; + exit(-1); + } + + string work_base = argv[1]; + string output_base = argv[2]; + + // lon = -146.248360; lat = 61.133950; // PAVD (Valdez, AK) + // lon = -110.664244; lat = 33.352890; // P13 + // lon = -93.211389; lat = 45.145000; // KANE + // lon = -92.486188; lat = 44.590190; // KRGK + // lon = -89.744682312011719; lat= 29.314495086669922; + // lon = -122.488090; lat = 42.743183; // 64S + // lon = -114.861097; lat = 35.947480; // 61B + // lon = -112.012175; lat = 41.195944; // KOGD + // lon = -90.757128; lat = 46.790212; // WI32 + // lon = -122.220717; lat = 37.721291; // KOAK + // lon = -111.721477; lat = 40.215641; // KPVU + // lon = -122.309313; lat = 47.448982; // KSEA + lon = -148.798131; lat = 63.645099; // AK06 (Danali, AK) + + double min_x = lon - 3; + double min_y = lat - 1; + FGBucket b_min( min_x, min_y ); + FGBucket b_max( lon + 3, lat + 1 ); + + FGBucket b_omit(550314L); + // FGBucket b(517745L); + // FGBucket b(-146.248360, 61.133950); + // construct_tile( work_base, output_base, b ); + // exit(0); + + if ( b_min == b_max ) { + construct_tile( work_base, output_base, b_min ); + } else { + FGBucket b_cur; + int dx, dy, i, j; + + fgBucketDiff(b_min, b_max, &dx, &dy); + cout << " construction area spans tile boundaries" << endl; + cout << " dx = " << dx << " dy = " << dy << endl; + + for ( j = 0; j <= dy; j++ ) { + for ( i = 0; i <= dx; i++ ) { + b_cur = fgBucketOffset(min_x, min_y, i, j); + + if ( b_cur != b_omit ) { + construct_tile( work_base, output_base, b_cur ); + } + } + } + // string answer; cin >> answer; + } +} + + +// $Log$ +// Revision 1.18 1999/04/05 02:16:51 curt +// Dynamically update "error" until the resulting tile data scales within +// a lower and upper bounds. +// +// Revision 1.17 1999/04/03 05:22:57 curt +// Found a bug in dividing and adding unique verticle segments which could +// cause the triangulator to end up in an infinite loop. Basically the code +// was correct, but the verticle line test was a bit to selective. +// +// Revision 1.16 1999/04/01 13:52:12 curt +// Version 0.6.0 +// Shape name tweak. +// Removing tool: FixNode +// +// Revision 1.15 1999/03/31 23:47:02 curt +// Debugging output tweaks. +// +// Revision 1.14 1999/03/31 13:26:41 curt +// Debugging output tweeaks. +// +// Revision 1.13 1999/03/31 05:35:06 curt +// Fixed bug in genfans (deleting the wrong triangles from the available pool.) +// +// Revision 1.12 1999/03/30 23:51:14 curt +// fiddling ... +// +// Revision 1.11 1999/03/29 13:11:06 curt +// Shuffled stl type names a bit. +// Began adding support for tri-fanning (or maybe other arrangments too.) +// +// Revision 1.10 1999/03/27 05:25:02 curt +// Fit with a value of 200 rather than 100. +// Handle corner nodes separately from the rest of the fitted nodes. +// Write scenery file to correct location. +// First hack at generating scenery for multiple tiles in one invocation. +// +// Revision 1.9 1999/03/25 19:04:31 curt +// Preparations for outputing scenery file to correct location. +// Minor tweaks related to FGBucket usage. +// +// Revision 1.8 1999/03/23 22:02:17 curt +// Worked on creating data to output ... normals, bounding spheres, etc. +// +// Revision 1.7 1999/03/22 23:48:29 curt +// Added GenOutput/ +// +// Revision 1.6 1999/03/21 15:48:01 curt +// Removed Dem2node from the Tools fold. +// Tweaked the triangulator options to add quality mesh refinement. +// +// Revision 1.5 1999/03/21 14:02:05 curt +// Added a mechanism to dump out the triangle structures for viewing. +// Fixed a couple bugs in first pass at triangulation. +// - needed to explicitely initialize the polygon accumulator in triangle.cxx +// before each polygon rather than depending on the default behavior. +// - Fixed a problem with region attribute propagation where I wasn't generating +// the hole points correctly. +// +// Revision 1.4 1999/03/20 20:32:54 curt +// First mostly successful tile triangulation works. There's plenty of tweaking +// to do, but we are marching in the right direction. +// +// Revision 1.3 1999/03/19 00:26:52 curt +// Minor tweaks ... +// +// Revision 1.2 1999/03/17 23:49:52 curt +// Started work on Triangulate/ section. +// +// Revision 1.1 1999/03/14 00:03:24 curt +// Initial revision. +// + + diff --git a/Tools/Main/construct_types.hxx b/Tools/Main/construct_types.hxx new file mode 100644 index 000000000..095e48c56 --- /dev/null +++ b/Tools/Main/construct_types.hxx @@ -0,0 +1,58 @@ +// construct_types.hxx -- commonly used types in the construction business. +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _CONSTRUCT_TYPES_HXX +#define _CONSTRUCT_TYPES_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include <Include/compiler.h> + +#include <vector> + +#include <Math/point3d.hxx> + +FG_USING_STD(vector); + + +typedef vector < int > int_list; +typedef int_list::iterator int_list_iterator; +typedef int_list::const_iterator const_int_list_iterator; + +typedef vector < Point3D > point_list; +typedef point_list::iterator point_list_iterator; +typedef point_list::const_iterator const_point_list_iterator; + + +#endif // _CONSTRUCT_TYPES_HXX + + +// $Log$ +// Revision 1.1 1999/03/29 13:19:44 curt +// Initial revision. +// diff --git a/Tools/Makedir/Makefile.am b/Tools/Makedir/Makefile.am new file mode 100644 index 000000000..e93857cc0 --- /dev/null +++ b/Tools/Makedir/Makefile.am @@ -0,0 +1,9 @@ +bin_PROGRAMS = makedir + +makedir_SOURCES = makedir.cxx + +makedir_LDADD = \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(base_LIBS) + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib diff --git a/Tools/Makedir/makedir.cxx b/Tools/Makedir/makedir.cxx new file mode 100644 index 000000000..1bc7734cb --- /dev/null +++ b/Tools/Makedir/makedir.cxx @@ -0,0 +1,119 @@ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#include <stdio.h> +#include <sys/stat.h> // stat() +#include <unistd.h> // stat() + +#include <string> + +#include <Bucket/bucketutils.h> + + + +#ifdef WIN32 +#ifndef TRUE + #define FALSE 0 + #define TRUE 1 +#endif + +char* PathDivider() +{ + return "\\"; +} // PathDivider + +void ReplaceDivider( char* path ) +{ + char div = PathDivider()[0]; + int i; + + if ( ! path ) + return; + if ( div == '/' ) + return; + + for ( i = 0; path[i]; i++ ) + if ( path[i] == '/' ) + path[i] = div; + +} // ReplaceDivider + +int Exists( char* path ) +{ + struct stat statbuff; + + ReplaceDivider( path ); + if ( path[strlen( path ) - 1] == ':' ) + return TRUE; + if ( _stat( path, &statbuff ) != 0 ) + return FALSE; + return TRUE; +} // Exists + + +void CreateDir( char* path ) +{ + if ( ! path || ! strlen( path ) ) + return; + ReplaceDivider( path ); + // see if the parent exists yet + int i; // looping index + string parent; // path to parent + + parent = path; + for ( i = strlen( parent.c_str() )-1; i >= 0; i-- ) + if ( parent[i] == PathDivider()[0] ) + { + parent[i] = '\0'; + break; + } + + if ( ! Exists( parent.c_str() ) ) + { + CreateDir( parent.c_str() ); + } + + if ( ! Exists( path ) ) + { + if (mkdir(path, S_IRWXU) != 0 ) + { + cout << "Could not create directory " << path << endl; + }else{ + cout << "CreateDir: " << path << endl; + } + } + +} // CreateDir + + +int main(int argc, char **argv) +{ + string root; + + if(argc != 2) + { + cout << "Makedir failed needs one argument\n"; + return(10); + } + root = argv[1]; + + CreateDir(root.c_str()); + + return(0); +} +#else + +int main(int argc, char **argv) +{ + cout << "This program is intended to work with windoze\n"; + cout << "Other platforms can use mkdir\n"; +} + +#endif // WIN32 + diff --git a/Tools/Polygon/Makefile.am b/Tools/Polygon/Makefile.am new file mode 100644 index 000000000..caa4c59be --- /dev/null +++ b/Tools/Polygon/Makefile.am @@ -0,0 +1,7 @@ +noinst_LIBRARIES = libPolygon.a + +libPolygon_a_SOURCES = \ + index.cxx index.hxx \ + names.cxx names.hxx + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib diff --git a/Tools/Polygon/index.cxx b/Tools/Polygon/index.cxx new file mode 100644 index 000000000..34352aacd --- /dev/null +++ b/Tools/Polygon/index.cxx @@ -0,0 +1,79 @@ +// index.cxx -- routines to handle a unique/persistant integer polygon index +// +// Written by Curtis Olson, started February 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + +#include <Include/compiler.h> + +#include STL_STRING + +#include <stdio.h> + +#include "index.hxx" + + +static long int poly_index; +static string poly_path; + + +// initialize the unique polygon index counter stored in path +bool poly_index_init( string path ) { + poly_path = path; + + FILE *fp = fopen( poly_path.c_str(), "r" ); + + if ( fp == NULL ) { + cout << "Error cannot open " << poly_path << endl; + poly_index = 0; + return false; + } + + fscanf( fp, "%ld", &poly_index ); + + fclose( fp ); +} + + +// increment the persistant counter and return the next poly_index +long int poly_index_next() { + ++poly_index; + + FILE *fp = fopen( poly_path.c_str(), "w" ); + + if ( fp == NULL ) { + cout << "Error cannot open " << poly_path << " for writing" << endl; + } + + fprintf( fp, "%ld\n", poly_index ); + + fclose( fp ); + + return poly_index; +} + + +// $Log$ +// Revision 1.2 1999/03/19 00:27:30 curt +// Use long int for index instead of just int. +// +// Revision 1.1 1999/02/25 21:30:24 curt +// Initial revision. +// diff --git a/Tools/Polygon/index.hxx b/Tools/Polygon/index.hxx new file mode 100644 index 000000000..0ea6b5c06 --- /dev/null +++ b/Tools/Polygon/index.hxx @@ -0,0 +1,51 @@ +// index.cxx -- routines to handle a unique/persistant integer polygon index +// +// Written by Curtis Olson, started February 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _INDEX_HXX +#define _INDEX_HXX + + +#include <Include/compiler.h> + +#include STL_STRING + + +// initialize the unique polygon index counter stored in path +bool poly_index_init( string path ); + +// increment the persistant counter and return the next poly_index +long int poly_index_next(); + + + +#endif // _INDEX_HXX + + +// $Log$ +// Revision 1.2 1999/03/19 00:27:31 curt +// Use long int for index instead of just int. +// +// Revision 1.1 1999/02/25 21:30:24 curt +// Initial revision. +// diff --git a/Tools/Polygon/names.cxx b/Tools/Polygon/names.cxx new file mode 100644 index 000000000..2633d19a6 --- /dev/null +++ b/Tools/Polygon/names.cxx @@ -0,0 +1,146 @@ +// names.cxx -- process shapefiles names +// +// Written by Curtis Olson, started February 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + +#include <Include/compiler.h> + +#include STL_STRING + +#include "names.hxx" + + +// return area type from text name +AreaType get_area_type( string area ) { + if ( area == "Default" ) { + return DefaultArea; + } else if ( area == "AirportKeep" ) { + return AirportKeepArea; + } else if ( area == "AirportIgnore" ) { + return AirportIgnoreArea; + } else if ( (area == "Swamp or Marsh") + || (area == "Marsh") ) { + return MarshArea; + } else if ( (area == "Bay Estuary or Ocean") + || (area == "Ocean") ) { + return OceanArea; + } else if ( area == "Lake" ) { + return LakeArea; + } else if ( (area == "Lake Dry") + || (area == "DryLake") ) { + return DryLakeArea; + } else if ( (area == "Lake Intermittent") + || (area == "IntermittentLake") ) { + return IntLakeArea; + } else if ( area == "Reservoir" ) { + return ReservoirArea; + } else if ( (area == "Reservoir Intermittent") + || (area == "IntermittentReservoir") ) { + return IntReservoirArea; + } else if ( area == "Stream" ) { + return StreamArea; + } else if ( area == "Canal" ) { + return CanalArea; + } else if ( area == "Glacier" ) { + return GlacierArea; + } else if ( area == "Void Area" ) { + return VoidArea; + } else if ( area == "Null" ) { + return NullArea; + } else { + cout << "unknown area = '" << area << "'" << endl; + // cout << "area = " << area << endl; + // for ( int i = 0; i < area.length(); i++ ) { + // cout << i << ") " << (int)area[i] << endl; + // } + return UnknownArea; + } +} + + +// return text from of area name +string get_area_name( AreaType area ) { + if ( area == DefaultArea ) { + return "Default"; + } else if ( area == AirportKeepArea ) { + return "AirportKeep"; + } else if ( area == AirportIgnoreArea ) { + return "AirportIgnore"; + } else if ( area == MarshArea ) { + return "Marsh"; + } else if ( area == OceanArea ) { + return "Ocean"; + } else if ( area == LakeArea ) { + return "Lake"; + } else if ( area == DryLakeArea ) { + return "DryLake"; + } else if ( area == IntLakeArea ) { + return "IntermittentLake"; + } else if ( area == ReservoirArea ) { + return "Reservoir"; + } else if ( area == IntReservoirArea ) { + return "IntermittentReservoir"; + } else if ( area == StreamArea ) { + return "Stream"; + } else if ( area == CanalArea ) { + return "Canal"; + } else if ( area == GlacierArea ) { + return "Glacier"; + } else if ( area == VoidArea ) { + return "VoidArea"; + } else if ( area == NullArea ) { + return "Null"; + } else { + cout << "unknown area code = " << (int)area << endl; + return "Unknown"; + } +} + + +// $Log$ +// Revision 1.7 1999/04/01 13:52:13 curt +// Version 0.6.0 +// Shape name tweak. +// Removing tool: FixNode +// +// Revision 1.6 1999/03/27 05:31:24 curt +// Make 0 the default area type since this corresponds well with the conventions +// used by the triangulator. +// +// Revision 1.5 1999/03/22 23:49:29 curt +// Moved AreaType get_shapefile_type(GDBFile *dbf, int rec) to where it +// belongs in ShapeFile/ +// +// Revision 1.4 1999/03/13 18:47:04 curt +// Removed an unused variable. +// +// Revision 1.3 1999/03/02 01:03:58 curt +// Added more reverse lookup support. +// +// Revision 1.2 1999/03/01 15:35:52 curt +// Generalized the routines a bit to make them more useful. +// +// Revision 1.1 1999/02/25 21:30:24 curt +// Initial revision. +// +// Revision 1.1 1999/02/23 01:29:05 curt +// Additional progress. +// diff --git a/Tools/Polygon/names.hxx b/Tools/Polygon/names.hxx new file mode 100644 index 000000000..0a919a5ed --- /dev/null +++ b/Tools/Polygon/names.hxx @@ -0,0 +1,89 @@ +// names.hxx -- process shapefiles names +// +// Written by Curtis Olson, started February 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _NAMES_HXX +#define _NAMES_HXX + + +#include <Include/compiler.h> + +#include STL_STRING + +FG_USING_STD(string); + + +// Posible shape file types. Note the order of these is important and +// defines the priority of these shapes if they should intersect. The +// smaller the number, the higher the priority. +enum AreaType { + DefaultArea = 0, + AirportKeepArea = 1, + AirportIgnoreArea = 2, + OceanArea = 3, + LakeArea = 4, + DryLakeArea = 5, + IntLakeArea = 6, + ReservoirArea = 7, + IntReservoirArea = 8, + StreamArea = 9, + CanalArea = 10, + GlacierArea = 11, + MarshArea = 12, + VoidArea = 9997, + NullArea = 9998, + UnknownArea = 9999 +}; + + +// return area type from text name +AreaType get_area_type( string area ); + +// return text form of area name +string get_area_name( AreaType area ); + + +#endif // _NAMES_HXX + + +// $Log$ +// Revision 1.5 1999/03/27 05:31:25 curt +// Make 0 the default area type since this corresponds well with the conventions +// used by the triangulator. +// +// Revision 1.4 1999/03/22 23:49:30 curt +// Moved AreaType get_shapefile_type(GDBFile *dbf, int rec) to where it +// belongs in ShapeFile/ +// +// Revision 1.3 1999/03/01 15:35:53 curt +// Generalized the routines a bit to make them more useful. +// +// Revision 1.2 1999/02/26 22:10:42 curt +// Updated names and priorities of area types. +// +// Revision 1.1 1999/02/25 21:30:24 curt +// Initial revision. +// +// Revision 1.1 1999/02/23 01:29:05 curt +// Additional progress. +// diff --git a/Tools/Prep/Makefile.am b/Tools/Prep/Makefile.am new file mode 100644 index 000000000..96c87b412 --- /dev/null +++ b/Tools/Prep/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = \ + DemChop \ + DemInfo \ + DemRaw2ascii \ + GenAirports \ + ShapeFile diff --git a/Tools/ShapeFile/Makefile.am b/Tools/ShapeFile/Makefile.am new file mode 100644 index 000000000..b3c41b82b --- /dev/null +++ b/Tools/ShapeFile/Makefile.am @@ -0,0 +1,14 @@ +bin_PROGRAMS = shape-decode + +shape_decode_SOURCES = main.cxx shape.cxx shape.hxx + +shape_decode_LDADD = \ + $(top_builddir)/Tools/Lib/Polygon/libPolygon.a \ + $(top_builddir)/Lib/Debug/libDebug.a \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(top_builddir)/Lib/Misc/libMisc.a \ + $(top_builddir)/Lib/zlib/libz.a \ + -lgfc -lgpc + + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib -I$(top_builddir)/Tools/Lib diff --git a/Tools/ShapeFile/main.cxx b/Tools/ShapeFile/main.cxx new file mode 100644 index 000000000..b0e629575 --- /dev/null +++ b/Tools/ShapeFile/main.cxx @@ -0,0 +1,333 @@ +// main.cxx -- process shapefiles and extract polygon outlines, +// clipping against and sorting them into the revelant +// tiles. +// +// Written by Curtis Olson, started February 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +// Include Geographic Foundation Classes library + +// libgfc.a includes need this bit o' strangeness +#if defined ( linux ) +# define _LINUX_ +#endif +#include <gfc/gadt_polygon.h> +#include <gfc/gdbf.h> +#include <gfc/gshapefile.h> +#undef E +#undef DEG_TO_RAD +#undef RAD_TO_DEG + +// include Generic Polygon Clipping Library +extern "C" { +#include <gpc.h> +} + +#include <Include/compiler.h> + +#include STL_STRING + +#include <Debug/logstream.hxx> + +#include <Polygon/index.hxx> +#include <Polygon/names.hxx> +#include "shape.hxx" + + +// return the type of the shapefile record +AreaType get_shapefile_type(GDBFile *dbf, int rec) { + // GDBFieldDesc *fdesc[128]; // 128 is an arbitrary number here + GDBFValue *fields; //an array of field values + char* dbf_rec; //a record containing all the fields + + // grab the meta-information for all the fields + // this applies to all the records in the DBF file. + // for ( int i = 0; i < dbf->numFields(); i++ ) { + // fdesc[i] = dbf->getFieldDesc(i); + // cout << i << ") " << fdesc[i]->name << endl; + // } + + // this is the whole name record + dbf_rec = dbf->getRecord( rec ); + + // parse it into individual fields + if ( dbf_rec ) { + fields = dbf->recordDeform( dbf_rec ); + } else { + return UnknownArea; + } + + string area = fields[4].str_v; + // strip leading spaces + while ( area[0] == ' ' ) { + area = area.substr(1, area.length() - 1); + } + // strip trailing spaces + while ( area[area.length() - 1] == ' ' ) { + area = area.substr(0, area.length() - 1); + } + // strip other junk encountered + while ( (int)area[area.length() - 1] == 9 ) { + area = area.substr(0, area.length() - 1); + } + + return get_area_type( area ); +} + + +int main( int argc, char **argv ) { + gpc_polygon gpc_shape; + int i, j; + + fglog().setLogLevels( FG_ALL, FG_DEBUG ); + + if ( argc != 3 ) { + FG_LOG( FG_GENERAL, FG_ALERT, "Usage: " << argv[0] + << " <shape_file> <work_dir>" ); + exit(-1); + } + + FG_LOG( FG_GENERAL, FG_DEBUG, "Opening " << argv[1] << " for reading." ); + + // make work directory + string work_dir = argv[2]; + string command = "mkdir -p " + work_dir; + system( command.c_str() ); + + // initialize persistant polygon counter + string counter_file = work_dir + "/../work.counter"; + poly_index_init( counter_file ); + + // initialize structure for building gpc polygons + shape_utils_init(); + + GShapeFile * sf = new GShapeFile( argv[1] ); + GDBFile *dbf = new GDBFile( argv[1] ); + string path = argv[2]; + + GPolygon shape; + double *coords; // in decimal degrees + int n_vertices; + + FG_LOG( FG_GENERAL, FG_INFO, "shape file records = " << sf->numRecords() ); + + GShapeFile::ShapeType t = sf->shapeType(); + if ( t != GShapeFile::av_Polygon ) { + FG_LOG( FG_GENERAL, FG_ALERT, "Can't handle non-polygon shape files" ); + exit(-1); + } + + for ( i = 0; i < sf->numRecords(); i++ ) { + //fetch i-th record (shape) + sf->getShapeRec(i, &shape); + FG_LOG( FG_GENERAL, FG_DEBUG, "Record = " << i << " rings = " + << shape.numRings() ); + + AreaType area = get_shapefile_type(dbf, i); + FG_LOG( FG_GENERAL, FG_DEBUG, "area type = " << get_area_name(area) + << " (" << (int)area << ")" ); + + FG_LOG( FG_GENERAL, FG_INFO, " record = " << i + << " ring = " << 0 ); + + if ( area == MarshArea ) { + // interior of polygon is marsh, holes are water + + // do main outline first + init_shape(&gpc_shape); + n_vertices = shape.getRing(0, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + + // do lakes (individually) next + for ( j = 1; j < shape.numRings(); j++ ) { + FG_LOG( FG_GENERAL, FG_INFO, " record = " << i + << " ring = " << j ); + init_shape(&gpc_shape); + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + process_shape(path, LakeArea, &gpc_shape); + free_shape(&gpc_shape); + } + } else if ( area == OceanArea ) { + // interior of polygon is ocean, holes are islands + + init_shape(&gpc_shape); + for ( j = 0; j < shape.numRings(); j++ ) { + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + } + process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + } else if ( area == LakeArea ) { + // interior of polygon is lake, holes are islands + + init_shape(&gpc_shape); + for ( j = 0; j < shape.numRings(); j++ ) { + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + } + process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + } else if ( area == DryLakeArea ) { + // interior of polygon is dry lake, holes are islands + + init_shape(&gpc_shape); + for ( j = 0; j < shape.numRings(); j++ ) { + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + } + process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + } else if ( area == IntLakeArea ) { + // interior of polygon is intermittent lake, holes are islands + + init_shape(&gpc_shape); + for ( j = 0; j < shape.numRings(); j++ ) { + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + } + process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + } else if ( area == ReservoirArea ) { + // interior of polygon is reservoir, holes are islands + + init_shape(&gpc_shape); + for ( j = 0; j < shape.numRings(); j++ ) { + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + } + process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + } else if ( area == IntReservoirArea ) { + // interior of polygon is intermittent reservoir, holes are islands + + init_shape(&gpc_shape); + for ( j = 0; j < shape.numRings(); j++ ) { + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + } + process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + } else if ( area == StreamArea ) { + // interior of polygon is stream, holes are islands + + init_shape(&gpc_shape); + for ( j = 0; j < shape.numRings(); j++ ) { + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + } + process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + } else if ( area == CanalArea ) { + // interior of polygon is canal, holes are islands + + init_shape(&gpc_shape); + for ( j = 0; j < shape.numRings(); j++ ) { + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + } + process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + } else if ( area == GlacierArea ) { + // interior of polygon is glacier, holes are dry land + + init_shape(&gpc_shape); + for ( j = 0; j < shape.numRings(); j++ ) { + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + } + process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + } else if ( area == VoidArea ) { + // interior is ???? + + // skip for now + FG_LOG( FG_GENERAL, FG_ALERT, "Void area ... SKIPPING!" ); + + if ( shape.numRings() > 1 ) { + FG_LOG( FG_GENERAL, FG_ALERT, " Void area with holes!" ); + // exit(-1); + } + + init_shape(&gpc_shape); + for ( j = 0; j < shape.numRings(); j++ ) { + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + } + // process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + } else if ( area == NullArea ) { + // interior is ???? + + // skip for now + FG_LOG( FG_GENERAL, FG_ALERT, "Null area ... SKIPPING!" ); + + if ( shape.numRings() > 1 ) { + FG_LOG( FG_GENERAL, FG_ALERT, " Null area with holes!" ); + // exit(-1); + } + + init_shape(&gpc_shape); + for ( j = 0; j < shape.numRings(); j++ ) { + n_vertices = shape.getRing(j, coords); + add_to_shape(n_vertices, coords, &gpc_shape); + } + // process_shape(path, area, &gpc_shape); + free_shape(&gpc_shape); + } else { + FG_LOG( FG_GENERAL, FG_ALERT, "Uknown area!" ); + exit(-1); + } + } + + return 0; +} + + +// $Log$ +// Revision 1.8 1999/03/22 23:49:36 curt +// Moved AreaType get_shapefile_type(GDBFile *dbf, int rec) to where it +// belongs in ShapeFile/ +// +// Revision 1.7 1999/03/17 23:51:29 curt +// Changed polygon index counter file. +// +// Revision 1.6 1999/03/02 01:04:28 curt +// Don't crash when work directory doesn't exist ... create it. +// +// Revision 1.5 1999/03/01 15:36:28 curt +// Tweaked a function call name in "names.hxx". +// +// Revision 1.4 1999/02/25 21:31:05 curt +// First working version??? +// +// Revision 1.3 1999/02/23 01:29:04 curt +// Additional progress. +// +// Revision 1.2 1999/02/19 19:05:18 curt +// Working on clipping shapes and distributing into buckets. +// +// Revision 1.1 1999/02/15 19:10:23 curt +// Initial revision. +// diff --git a/Tools/ShapeFile/shape.cxx b/Tools/ShapeFile/shape.cxx new file mode 100644 index 000000000..badef8823 --- /dev/null +++ b/Tools/ShapeFile/shape.cxx @@ -0,0 +1,255 @@ +// shape.cxx -- shape/gpc utils +// +// Written by Curtis Olson, started February 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include <Include/compiler.h> + +#include STL_STRING + +#include <Bucket/newbucket.hxx> +#include <Debug/logstream.hxx> + +#include <Polygon/index.hxx> +#include <Polygon/names.hxx> +#include "shape.hxx" + + +#define FG_MAX_VERTICES 100000 +static gpc_vertex_list v_list; + + +class point2d { +public: + double x, y; +}; + + +static void clip_and_write_poly( string root, long int p_index, AreaType area, + FGBucket b, gpc_polygon *shape ) { + point2d c, min, max; + c.x = b.get_center_lon(); + c.y = b.get_center_lat(); + double span = bucket_span(c.y); + gpc_polygon base, result; + char tile_name[256], poly_index[256]; + + // calculate bucket dimensions + if ( (c.y >= -89.0) && (c.y < 89.0) ) { + min.x = c.x - span / 2.0; + max.x = c.x + span / 2.0; + min.y = c.y - FG_HALF_BUCKET_SPAN; + max.y = c.y + FG_HALF_BUCKET_SPAN; + } else if ( c.y < -89.0) { + min.x = -90.0; + max.x = -89.0; + min.y = -180.0; + max.y = 180.0; + } else if ( c.y >= 89.0) { + min.x = 89.0; + max.x = 90.0; + min.y = -180.0; + max.y = 180.0; + } else { + FG_LOG ( FG_GENERAL, FG_ALERT, + "Out of range latitude in clip_and_write_poly() = " << c.y ); + } + + FG_LOG( FG_GENERAL, FG_INFO, " (" << min.x << "," << min.y << ") (" + << max.x << "," << max.y << ")" ); + + // set up clipping tile + v_list.vertex[0].x = min.x; + v_list.vertex[0].y = min.y; + + v_list.vertex[1].x = max.x; + v_list.vertex[1].y = min.y; + + v_list.vertex[2].x = max.x; + v_list.vertex[2].y = max.y; + + v_list.vertex[3].x = min.x; + v_list.vertex[3].y = max.y; + + v_list.num_vertices = 4; + + base.num_contours = 0; + base.contour = NULL; + gpc_add_contour( &base, &v_list ); + + // FG_LOG( FG_GENERAL, FG_DEBUG, "base = 4 vertices" ); + + /* + FILE *bfp= fopen("base", "w"); + gpc_write_polygon(bfp, &base); + fclose(bfp); + */ + + gpc_polygon_clip(GPC_INT, &base, shape, &result); + + if ( result.num_contours > 0 ) { + long int t_index = b.gen_index(); + string path = root + "/Scenery/" + b.gen_base_path(); + string command = "mkdir -p " + path; + system( command.c_str() ); + + sprintf( tile_name, "%ld", t_index ); + string polyfile = path + "/" + tile_name; + + sprintf( poly_index, "%ld", p_index ); + polyfile += "."; + polyfile += poly_index; + + string poly_type = get_area_name( area ); + if ( poly_type == "Unknown" ) { + cout << "unknown area type in clip_and_write_poly()!" << endl; + exit(-1); + } + + FILE *rfp= fopen( polyfile.c_str(), "w" ); + fprintf( rfp, "%s\n", poly_type.c_str() ); + gpc_write_polygon( rfp, &result ); + fclose( rfp ); + } + + gpc_free_polygon(&base); + gpc_free_polygon(&result); +} + + +// Initialize structure we use to create polygons for the gpc library +bool shape_utils_init() { + v_list.num_vertices = 0; + v_list.vertex = new gpc_vertex[FG_MAX_VERTICES];; + + return true; +} + + +// initialize a gpc_polygon +void init_shape(gpc_polygon *shape) { + shape->num_contours = 0; + shape->contour = NULL; +} + + +// make a gpc_polygon +void add_to_shape(int count, double *coords, gpc_polygon *shape) { + + for ( int i = 0; i < count; i++ ) { + v_list.vertex[i].x = coords[i*2+0]; + v_list.vertex[i].y = coords[i*2+1]; + } + + v_list.num_vertices = count; + gpc_add_contour( shape, &v_list ); +} + + +// process shape (write polygon to all intersecting tiles) +void process_shape(string path, AreaType area, gpc_polygon *gpc_shape) { + point2d min, max; + long int index; + int i, j; + + min.x = min.y = 200.0; + max.x = max.y = -200.0; + + // find min/max of polygon + for ( i = 0; i < gpc_shape->num_contours; i++ ) { + for ( j = 0; j < gpc_shape->contour[i].num_vertices; j++ ) { + double x = gpc_shape->contour[i].vertex[j].x; + double y = gpc_shape->contour[i].vertex[j].y; + + if ( x < min.x ) { min.x = x; } + if ( y < min.y ) { min.y = y; } + if ( x > max.x ) { max.x = x; } + if ( y > max.y ) { max.y = y; } + } + } + + /* + FILE *sfp= fopen("shape", "w"); + gpc_write_polygon(sfp, gpc_shape); + fclose(sfp); + exit(-1); + */ + + // get next polygon index + index = poly_index_next(); + + FG_LOG( FG_GENERAL, FG_INFO, " min = " << min.x << "," << min.y + << " max = " << max.x << "," << max.y ); + + // find buckets for min, and max points of convex hull. + // note to self: self, you should think about checking for + // polygons that span the date line + FGBucket b_min(min.x, min.y); + FGBucket b_max(max.x, max.y); + FG_LOG( FG_GENERAL, FG_INFO, " Bucket min = " << b_min ); + FG_LOG( FG_GENERAL, FG_INFO, " Bucket max = " << b_max ); + + if ( b_min == b_max ) { + clip_and_write_poly( path, index, area, b_min, gpc_shape ); + } else { + FGBucket b_cur; + int dx, dy, i, j; + + fgBucketDiff(b_min, b_max, &dx, &dy); + FG_LOG( FG_GENERAL, FG_INFO, + " polygon spans tile boundaries" ); + FG_LOG( FG_GENERAL, FG_INFO, " dx = " << dx + << " dy = " << dy ); + + if ( (dx > 100) || (dy > 100) ) { + FG_LOG( FG_GENERAL, FG_ALERT, + "somethings really wrong!!!!" ); + exit(-1); + } + + for ( j = 0; j <= dy; j++ ) { + for ( i = 0; i <= dx; i++ ) { + b_cur = fgBucketOffset(min.x, min.y, i, j); + clip_and_write_poly( path, index, area, b_cur, gpc_shape ); + } + } + // string answer; cin >> answer; + } +} + + +// free a gpc_polygon +void free_shape(gpc_polygon *shape) { + gpc_free_polygon(shape); +} + + +// $Log$ +// Revision 1.3 1999/03/19 00:27:41 curt +// Use long int for index instead of just int. +// +// Revision 1.2 1999/02/25 21:31:08 curt +// First working version??? +// +// Revision 1.1 1999/02/23 01:29:06 curt +// Additional progress. +// diff --git a/Tools/ShapeFile/shape.hxx b/Tools/ShapeFile/shape.hxx new file mode 100644 index 000000000..6a59b77e3 --- /dev/null +++ b/Tools/ShapeFile/shape.hxx @@ -0,0 +1,64 @@ +// shape.hxx -- shape/gpc utils +// +// Written by Curtis Olson, started February 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _SHAPE_HXX +#define _SHAPE_HXX + + +// include Generic Polygon Clipping Library +extern "C" { +#include <gpc.h> +} + +#include <Polygon/names.hxx> + + +// Initialize structure we use to create polygons for the gpc library +// this must be called once from main for any program that uses this library +bool shape_utils_init(); + + +// initialize a gpc_polygon +void init_shape(gpc_polygon *shape); + +// make a gpc_polygon +void add_to_shape(int count, double *coords, gpc_polygon *shape); + +// process shape (write polygon to all intersecting tiles) +void process_shape(string path, AreaType area, gpc_polygon *gpc_shape); + +// free a gpc_polygon +void free_shape(gpc_polygon *shape); + + +#endif // _SHAPE_HXX + + +// $Log$ +// Revision 1.2 1999/02/25 21:31:09 curt +// First working version??? +// +// Revision 1.1 1999/02/23 01:29:06 curt +// Additional progress. +// diff --git a/Tools/SplitTris/Makefile.am b/Tools/SplitTris/Makefile.am new file mode 100644 index 000000000..e8f64e5b9 --- /dev/null +++ b/Tools/SplitTris/Makefile.am @@ -0,0 +1,77 @@ +#--------------------------------------------------------------------------- +# Makefile +# +# Written by Curtis Olson, started January 1998. +# +# Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +bin_PROGRAMS = splittris + +splittris_SOURCES = splittris.cxx splittris.hxx + +splittris_LDADD = \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(top_builddir)/Lib/Math/libMath.a \ + $(top_builddir)/Lib/Debug/libDebug.a \ + $(top_builddir)/Lib/Misc/libMisc.a \ + $(top_builddir)/Lib/zlib/libz.a \ + $(base_LIBS) + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.8 1998/11/04 23:01:57 curt +# Changes to the automake/autoconf system to reduce the number of libraries +# that are unnecessarily linked into the various executables. +# +# Revision 1.7 1998/10/18 01:17:25 curt +# Point3D tweaks. +# +# Revision 1.6 1998/07/30 23:49:26 curt +# Removed libtool support. +# +# Revision 1.5 1998/07/08 14:49:13 curt +# tweaks. +# +# Revision 1.4 1998/04/24 00:44:06 curt +# Added zlib support. +# +# Revision 1.3 1998/04/18 04:01:17 curt +# Now use libMath rather than having local copies of math routines. +# +# Revision 1.2 1998/04/14 02:26:06 curt +# Code reorganizations. Added a Lib/ directory for more general libraries. +# +# Revision 1.1 1998/04/08 23:21:10 curt +# Adopted Gnu automake/autoconf system. +# +# Revision 1.3 1998/01/21 02:55:55 curt +# Incorporated new make system from Bob Kuehne <rpk@sgi.com>. +# +# Revision 1.2 1998/01/14 15:54:42 curt +# Initial revision completed. +# +# Revision 1.1 1998/01/14 02:11:30 curt +# Initial revision. +# diff --git a/Tools/SplitTris/splittris.cxx b/Tools/SplitTris/splittris.cxx new file mode 100644 index 000000000..20e26e7a9 --- /dev/null +++ b/Tools/SplitTris/splittris.cxx @@ -0,0 +1,673 @@ +// splittris.cxx -- read in a .ele/.node file pair generated by the +// triangle program and output a simple Wavefront .obj +// file for the north, south, east, and west edge +// verticies ... including the normals. +// +// Written by Curtis Olson, started January 1998. +// +// Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> // for atoi() +#include <string.h> +#include <sys/stat.h> // for stat() +#include <unistd.h> // for stat() + +#include "splittris.hxx" + +#include <Include/fg_constants.h> +#include <Bucket/bucketutils.h> +#include <Math/fg_geodesy.hxx> +#include <Math/mat3.h> +#include <Math/point3d.hxx> +#include <Math/polar3d.hxx> +#include <Misc/fgstream.hxx> + +// int nodecount, tricount; +double xmin, xmax, ymin, ymax; + +// static double nodes_orig[MAX_NODES][3]; +// static Point3D nodes_cart[MAX_NODES]; +// static int tris[MAX_TRIS][3]; + +container_3d nodes_orig; +container_3d nodes_cart; +container_tri tri_list; + +fgBUCKET ne_index, nw_index, sw_index, se_index; +fgBUCKET north_index, south_index, east_index, west_index; + + +// given three points defining a triangle, calculate the normal +void calc_normal(const Point3D& p1, const Point3D& p2, + const Point3D& p3, double normal[3]) +{ + double v1[3], v2[3]; + double temp; + + v1[0] = p2.x() - p1.x(); v1[1] = p2.y() - p1.y(); v1[2] = p2.z() - p1.z(); + v2[0] = p3.x() - p1.x(); v2[1] = p3.y() - p1.y(); v2[2] = p3.z() - p1.z(); + + MAT3cross_product(normal, v1, v2); + MAT3_NORMALIZE_VEC(normal,temp); + + // printf(" Normal = %.2f %.2f %.2f\n", normal[0], normal[1], normal[2]); +} + + +// return the file base name ( foo/bar/file.ext = file.ext ) +string extract_file(const string& input) { + int pos; + + pos = input.rfind("/"); + ++pos; + + return input.substr(pos); +} + + +// return the file path name ( foo/bar/file.ext = foo/bar ) +string extract_path(const string& input) { + int pos; + + pos = input.rfind("/"); + + return input.substr(0, pos); +} + + +// return the index of all triangles containing the specified node +void find_tris(int n, int *t1, int *t2, int *t3, int *t4, int *t5) { + int i; + + *t1 = *t2 = *t3 = *t4 = *t5 = 0; + + i = 1; + iterator_tri last = tri_list.end(); + iterator_tri current = tri_list.begin(); + + // skip first null record + ++current; + + for ( ; current != last; ++current ) + { + if ( (n == (*current).n1) || (n == (*current).n2) || + (n == (*current).n3) ) + { + if ( *t1 == 0 ) { + *t1 = i; + } else if ( *t2 == 0 ) { + *t2 = i; + } else if ( *t3 == 0 ) { + *t3 = i; + } else if ( *t4 == 0 ) { + *t4 = i; + } else { + *t5 = i; + } + } + ++i; + } +} + + +// Initialize a new mesh structure +void triload(const string& basename) { + string nodename, elename; + Point3D node1, node2, p; + triangle tri; + int nodecount, tricount, dim, junk1, junk2; + int i; + + nodename = basename + ".node"; + elename = basename + ".ele"; + + cout << "Loading node file: " + nodename + " ...\n"; + + fg_gzifstream node_in( nodename ); + if ( !node_in ) { + cout << "Cannot open file " + nodename + "\n"; + exit(-1); + } + + // the triangle program starts counting at 1 by default which is + // pretty obnoxious. Let's just push null record zero's onto our + // list to compensate + nodes_orig.push_back(node1); + nodes_cart.push_back(node1); + tri_list.push_back(tri); + + node_in >> nodecount >> dim >> junk1 >> junk2; + cout << " Expecting " << nodecount << " nodes\n"; + + for ( i = 1; i <= nodecount; i++ ) { + node_in >> junk1 >> node1 >> junk2; + nodes_orig.push_back(node1); + // printf("%d %.2f %.2f %.2f\n", junk1, node1.x, node1.y, node1.z); + + // convert to radians (before we can convert to cartesian) + p = Point3D( node1.x() * ARCSEC_TO_RAD, + node1.y() * ARCSEC_TO_RAD, + node1.z() ); + + node2 = fgGeodToCart(p); + nodes_cart.push_back(node2); + // printf("%d %.2f %.2f %.2f\n", junk1, node2.x, node2.y, node2.z); + + if ( i == 1 ) { + xmin = xmax = node1.x(); + ymin = ymax = node1.y(); + } else { + if ( node1.x() < xmin ) { + xmin = node1.x(); + } + if ( node1.x() > xmax ) { + xmax = node1.x(); + } + if ( node1.y() < ymin ) { + ymin = node1.y(); + } + if ( node1.y() > ymax ) { + ymax = node1.y(); + } + } + } + + cout << "Loading element file: " + elename + " ...\n"; + fg_gzifstream ele_in( elename ); + if ( !ele_in ) { + cout << "Cannot open file " + elename + "\n"; + exit(-1); + } + + ele_in >> tricount >> junk1 >> junk2; + cout << " Expecting " << tricount << " elements\n"; + + for ( i = 1; i <= tricount; i++ ) { + // fscanf(ele_file, "%d %d %d %d\n", &junk1, + // &(tri.n1), &(tri.n2), &(tri.n3)); + ele_in >> junk1 >> tri.n1 >> tri.n2 >> tri.n3; + // printf("%d %d %d %d\n", junk1, tri.n1, tri.n2, tri.n3); + tri_list.push_back(tri); + } +} + + +// check if a file exists +int file_exists(char *file) { + struct stat stat_buf; + int result; + + cout << "checking " << file << " ... "; + + result = stat(file, &stat_buf); + + if ( result != 0 ) { + // stat failed, no file + cout << "not found.\n"; + return 0; + } else { + // stat succeeded, file exists + cout << "exists.\n"; + return 1; + } +} + + +// check to see if a shared object exists +int shared_object_exists(const char *basepath, const string& ext) { + char file[256], scene_path[256]; + long int index; + + if ( ext == ".sw" ) { + fgBucketGenBasePath(&west_index, scene_path); + index = fgBucketGenIndex(&west_index); + sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&sw_index, scene_path); + index = fgBucketGenIndex(&sw_index); + sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&south_index, scene_path); + index = fgBucketGenIndex(&south_index); + sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( ext == ".se" ) { + fgBucketGenBasePath(&east_index, scene_path); + index = fgBucketGenIndex(&east_index); + sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&se_index, scene_path); + index = fgBucketGenIndex(&se_index); + sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&south_index, scene_path); + index = fgBucketGenIndex(&south_index); + sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( ext == ".ne" ) { + fgBucketGenBasePath(&east_index, scene_path); + index = fgBucketGenIndex(&east_index); + sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&ne_index, scene_path); + index = fgBucketGenIndex(&ne_index); + sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&north_index, scene_path); + index = fgBucketGenIndex(&north_index); + sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( ext == ".nw" ) { + fgBucketGenBasePath(&west_index, scene_path); + index = fgBucketGenIndex(&west_index); + sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&nw_index, scene_path); + index = fgBucketGenIndex(&nw_index); + sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&north_index, scene_path); + index = fgBucketGenIndex(&north_index); + sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( ext == ".south" ) { + fgBucketGenBasePath(&south_index, scene_path); + index = fgBucketGenIndex(&south_index); + sprintf(file, "%s/%s/%ld.1.north", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( ext == ".north" ) { + fgBucketGenBasePath(&north_index, scene_path); + index = fgBucketGenIndex(&north_index); + sprintf(file, "%s/%s/%ld.1.south", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( ext == ".west" ) { + fgBucketGenBasePath(&west_index, scene_path); + index = fgBucketGenIndex(&west_index); + sprintf(file, "%s/%s/%ld.1.east", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( ext == ".east" ) { + fgBucketGenBasePath(&east_index, scene_path); + index = fgBucketGenIndex(&east_index); + sprintf(file, "%s/%s/%ld.1.west", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + return(0); +} + + +// my custom file opening routine ... don't open if a shared edge or +// vertex alread exists +FILE *my_open(const string& basename, const string& basepath, + const string& ext) +{ + FILE *fp; + string filename; + + // create the output file name + filename = basename + ext; + + // check if a shared object already exist from a different tile + + if ( shared_object_exists(basepath.c_str(), ext) ) { + // not an actual file open error, but we've already got the + // shared edge, so we don't want to create another one + cout << "not opening\n"; + return(NULL); + } else { + // open the file + fp = fopen(filename.c_str(), "w"); + cout << "Opening " + filename + "\n"; + return(fp); + } +} + + +// dump in WaveFront .obj format +void dump_obj(const string& basename, const string& basepath) { + Point3D node; + double n1[3], n2[3], n3[3], n4[3], n5[3], norm[3], temp; + FILE *fp, *sw, *se, *ne, *nw, *north, *south, *east, *west, *body; + int i, t1, t2, t3, t4, t5, count, size; + double x, y, z; + + sw = my_open(basename, basepath, ".sw"); + se = my_open(basename, basepath, ".se"); + ne = my_open(basename, basepath, ".ne"); + nw = my_open(basename, basepath, ".nw"); + + north = my_open(basename, basepath, ".north"); + south = my_open(basename, basepath, ".south"); + east = my_open(basename, basepath, ".east"); + west = my_open(basename, basepath, ".west"); + + body = my_open(basename, basepath, ".body"); + + cout << "Dumping edges file basename: " + basename + " ...\n"; + + // dump vertices + cout << " writing vertices\n"; + + iterator_3d last = nodes_orig.end(); + iterator_3d current = nodes_orig.begin(); + ++current; + for ( ; current != last; ++current) { + node = *current; + + if ( (fabs(node.y() - ymin) < FG_EPSILON) && + (fabs(node.x() - xmin) < FG_EPSILON) ) { + fp = sw; + } else if ( (fabs(node.y() - ymin) < FG_EPSILON) && + (fabs(node.x() - xmax) < FG_EPSILON) ) { + fp = se; + } else if ( (fabs(node.y() - ymax) < FG_EPSILON) && + (fabs(node.x() - xmax) < FG_EPSILON)) { + fp = ne; + } else if ( (fabs(node.y() - ymax) < FG_EPSILON) && + (fabs(node.x() - xmin) < FG_EPSILON) ) { + fp = nw; + } else if ( fabs(node.x() - xmin) < FG_EPSILON ) { + fp = west; + } else if ( fabs(node.x() - xmax) < FG_EPSILON ) { + fp = east; + } else if ( fabs(node.y() - ymin) < FG_EPSILON ) { + fp = south; + } else if ( fabs(node.y() - ymax) < FG_EPSILON ) { + fp = north; + } else { + fp = body; + } + + x = node.x(); + y = node.y(); + z = node.z(); + + if ( fp != NULL ) { + fprintf(fp, "gdn %.2f %.2f %.2f\n", x, y, z); + } + } + + cout << " calculating and writing normals\n"; + + // calculate and generate normals + size = nodes_orig.size(); + for ( i = 1; i < size; i++ ) { + // printf("Finding normal\n"); + + find_tris(i, &t1, &t2, &t3, &t4, &t5); + + n1[0] = n1[1] = n1[2] = 0.0; + n2[0] = n2[1] = n2[2] = 0.0; + n3[0] = n3[1] = n3[2] = 0.0; + n4[0] = n4[1] = n4[2] = 0.0; + n5[0] = n5[1] = n5[2] = 0.0; + + count = 1; + calc_normal(nodes_cart[tri_list[t1].n1], + nodes_cart[tri_list[t1].n2], + nodes_cart[tri_list[t1].n3], + n1); + + if ( t2 > 0 ) { + calc_normal(nodes_cart[tri_list[t2].n1], + nodes_cart[tri_list[t2].n2], + nodes_cart[tri_list[t2].n3], + n2); + count = 2; + } + + if ( t3 > 0 ) { + calc_normal(nodes_cart[tri_list[t3].n1], + nodes_cart[tri_list[t3].n2], + nodes_cart[tri_list[t3].n3], + n3); + count = 3; + } + + if ( t4 > 0 ) { + calc_normal(nodes_cart[tri_list[t4].n1], + nodes_cart[tri_list[t4].n2], + nodes_cart[tri_list[t4].n3], + n4); + count = 4; + } + + if ( t5 > 0 ) { + calc_normal(nodes_cart[tri_list[t5].n1], + nodes_cart[tri_list[t5].n2], + nodes_cart[tri_list[t5].n3], + n5); + count = 5; + } + + // printf(" norm[2] = %.2f %.2f %.2f\n", n1[2], n2[2], n3[2]); + + norm[0] = ( n1[0] + n2[0] + n3[0] + n4[0] + n5[0] ) / (double)count; + norm[1] = ( n1[1] + n2[1] + n3[1] + n4[1] + n5[1] ) / (double)count; + norm[2] = ( n1[2] + n2[2] + n3[2] + n4[2] + n5[2] ) / (double)count; + + // printf(" count = %d\n", count); + // printf(" Ave. normal = %.4f %.4f %.4f\n", norm[0], norm[1], + // norm[2]); + MAT3_NORMALIZE_VEC(norm, temp); + // printf(" Normalized ave. normal = %.4f %.4f %.4f\n", + // norm[0], norm[1], norm[2]); + + fp = NULL; + + if ( (fabs(nodes_orig[i].y() - ymin) < FG_EPSILON) && + (fabs(nodes_orig[i].x() - xmin) < FG_EPSILON) ) { + fp = sw; + } else if ( (fabs(nodes_orig[i].y() - ymin) < FG_EPSILON) && + (fabs(nodes_orig[i].x() - xmax) < FG_EPSILON) ) { + fp = se; + } else if ( (fabs(nodes_orig[i].y() - ymax) < FG_EPSILON) && + (fabs(nodes_orig[i].x() - xmax) < FG_EPSILON)) { + fp = ne; + } else if ( (fabs(nodes_orig[i].y() - ymax) < FG_EPSILON) && + (fabs(nodes_orig[i].x() - xmin) < FG_EPSILON) ) { + fp = nw; + } else if ( fabs(nodes_orig[i].x() - xmin) < FG_EPSILON ) { + fp = west; + } else if ( fabs(nodes_orig[i].x() - xmax) < FG_EPSILON ) { + fp = east; + } else if ( fabs(nodes_orig[i].y() - ymin) < FG_EPSILON ) { + fp = south; + } else if ( fabs(nodes_orig[i].y() - ymax) < FG_EPSILON ) { + fp = north; + } + if ( fp != NULL ) { + fprintf(fp, "vn %.4f %.4f %.4f\n", norm[0], norm[1], norm[2]); + } + } + + if ( sw ) { fclose(sw); } + if ( se ) { fclose(se); } + if ( ne ) { fclose(ne); } + if ( nw ) { fclose(nw); } + + if ( north ) { fclose(north); } + if ( south ) { fclose(south); } + if ( east ) { fclose(east); } + if ( west ) { fclose(west); } + + if ( body ) { fclose(body); } +} + + +int main(int argc, char **argv) { + string basename, basepath, temp; + fgBUCKET p; + long int index; + int len; + + basename = argv[1]; + + // find the base path of the file + basepath = extract_path(basename); + basepath = extract_path(basepath); + basepath = extract_path(basepath); + cout << "basepath = " + basepath + "\n"; + + // find the index of the current file + temp = extract_file(basename); + len = temp.length(); + if ( len >= 2 ) { + temp = temp.substr(0, len-2); + } + index = atoi( temp.c_str() ); + cout << "index = " << index << "\n"; + fgBucketParseIndex(index, &p); + + cout << "bucket = " << p.lon << " " << p.lat << " " << + p.x << " " << p.y << "\n"; + + // generate the indexes of the neighbors + fgBucketOffset(&p, &ne_index, 1, 1); + fgBucketOffset(&p, &nw_index, -1, 1); + fgBucketOffset(&p, &se_index, 1, -1); + fgBucketOffset(&p, &sw_index, -1, -1); + + fgBucketOffset(&p, &north_index, 0, 1); + fgBucketOffset(&p, &south_index, 0, -1); + fgBucketOffset(&p, &east_index, 1, 0); + fgBucketOffset(&p, &west_index, -1, 0); + + // printf("Corner indexes = %ld %ld %ld %ld\n", + // ne_index, nw_index, sw_index, se_index); + // printf("Edge indexes = %ld %ld %ld %ld\n", + // north_index, south_index, east_index, west_index); + + + // load the input data files + triload(basename); + + // dump in WaveFront .obj format + dump_obj(basename, basepath); + + return(0); +} + + +// $Log$ +// Revision 1.7 1998/11/06 21:33:57 curt +// Updates to go along with changes in fgstream. +// +// Revision 1.6 1998/10/21 14:56:20 curt +// Fixed a units conversion bug. +// +// Revision 1.5 1998/10/20 15:50:33 curt +// whitespace tweak. +// +// Revision 1.4 1998/10/18 01:17:27 curt +// Point3D tweaks. +// +// Revision 1.3 1998/09/22 23:49:56 curt +// C++-ified, STL-ified, and string-ified. +// +// Revision 1.2 1998/09/21 23:16:23 curt +// Converted to c++ style comments. +// +// Revision 1.1 1998/07/08 14:59:13 curt +// *.[ch] renamed to *.[ch]xx +// +// Revision 1.11 1998/07/04 00:56:40 curt +// typedef'd struct fgBUCKET. +// +// Revision 1.10 1998/05/02 01:54:37 curt +// Converting to polar3d.h routines. +// +// Revision 1.9 1998/04/18 04:01:20 curt +// Now use libMath rather than having local copies of math routines. +// +// Revision 1.8 1998/04/14 02:26:08 curt +// Code reorganizations. Added a Lib/ directory for more general libraries. +// +// Revision 1.7 1998/04/08 23:21:13 curt +// Adopted Gnu automake/autoconf system. +// +// Revision 1.6 1998/03/03 15:36:13 curt +// Tweaks for compiling with g++ +// +// Revision 1.5 1998/03/03 03:37:04 curt +// Cumulative tweaks. +// +// Revision 1.4 1998/01/31 00:41:26 curt +// Made a few changes converting floats to doubles. +// +// Revision 1.3 1998/01/27 18:37:04 curt +// Lots of updates to get back in sync with changes made over in .../Src/ +// +// Revision 1.2 1998/01/14 15:54:43 curt +// Initial revision completed. +// +// Revision 1.1 1998/01/14 02:11:31 curt +// Initial revision. +// + diff --git a/Tools/SplitTris/splittris.hxx b/Tools/SplitTris/splittris.hxx new file mode 100644 index 000000000..12787872d --- /dev/null +++ b/Tools/SplitTris/splittris.hxx @@ -0,0 +1,89 @@ +// splittris.hxx -- read in a .ele/.node file pair generated by the triangle +// program and output edge vertices w/ normals. +// +// Written by Curtis Olson, started January 1998. +// +// Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + + +#ifndef SPLITTRIS_HXX +#define SPLITTRIS_HXX + + +#include <stdio.h> +#include <string.h> +#include <string> + +#include <vector> +#include "Include/fg_stl_config.h" + +#ifdef NEEDNAMESPACESTD +using namespace std; +#endif + +#include <Math/point3d.hxx> + + +// A triangle (indices of the three nodes) +typedef struct { + int n1, n2, n3; +} triangle; + + +typedef vector < Point3D > container_3d; +typedef container_3d::iterator iterator_3d; +typedef container_3d::const_iterator const_iterator_3d; + +typedef vector < triangle > container_tri; +typedef container_tri::iterator iterator_tri; +typedef container_tri::const_iterator const_iterator_tri; + + +// Initialize a new mesh structure +void triload(const string& basename); + + +#endif // SPLITTRIS_HXX + + +// $Log$ +// Revision 1.4 1998/10/18 01:17:28 curt +// Point3D tweaks. +// +// Revision 1.3 1998/09/22 23:49:58 curt +// C++-ified, STL-ified, and string-ified. +// +// Revision 1.2 1998/09/21 23:16:24 curt +// Converted to c++ style comments. +// +// Revision 1.1 1998/07/08 14:59:14 curt +// *.[ch] renamed to *.[ch]xx +// +// Revision 1.3 1998/03/03 15:36:13 curt +// Tweaks for compiling with g++ +// +// Revision 1.2 1998/01/15 02:49:25 curt +// Misc. housekeeping. +// +// Revision 1.1 1998/01/14 02:11:32 curt +// Initial revision. +// + diff --git a/Tools/Stripe_u/Makefile.am b/Tools/Stripe_u/Makefile.am new file mode 100644 index 000000000..8373ea88b --- /dev/null +++ b/Tools/Stripe_u/Makefile.am @@ -0,0 +1,30 @@ +bin_PROGRAMS = strips + +strips_SOURCES = \ + add.c add.h \ + bands.c \ + common.c common.h \ + define.h \ + extend.h \ + free.c free.h \ + global.h \ + glove.h \ + init.c init.h \ + local.c local.h \ + my_global.h \ + newpolve.c \ + options.c options.h \ + output.c output.h \ + outputex.c outputex.h \ + partial.c partial.h \ + polverts.h polvertsex.h \ + queue.c queue.h \ + sgi_triang.c sgi_triangex.c \ + struct.c struct.h \ + structex.c \ + sturcts.h sturctsex.h \ + ties.c ties.h \ + triangulate.h triangulatex.h \ + util.c util.h + +strips_LDADD = $(base_LIBS) diff --git a/Tools/Stripe_u/add.c b/Tools/Stripe_u/add.c new file mode 100644 index 000000000..7d90357fd --- /dev/null +++ b/Tools/Stripe_u/add.c @@ -0,0 +1,386 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: add.c + This file contains the procedure code that will add information + to our data structures. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include "global.h" +#include "queue.h" +#include "polverts.h" +#include "triangulate.h" +#include "ties.h" +#include "outputex.h" +#include "options.h" +#include "local.h" + +BOOL new_vertex(double difference, int id1,int id2, + struct vert_struct *n) +{ + /* Is the difference between id1 and id2 (2 normal vertices that + mapped to the same vertex) greater than the + threshold that was specified? + */ + struct vert_struct *pn1,*pn2; + double dot_product; + double distance1, distance2,distance; + double rad; + char arg1[100]; + char arg2[100]; + + pn1 = n + id1; + pn2 = n + id2; + + dot_product = ((pn1->x) * (pn2->x)) + + ((pn1->y) * (pn2->y)) + + ((pn1->z) * (pn2->z)); + /* Get the absolute value */ + if (dot_product < 0) + dot_product = dot_product * -1; + + distance1 = sqrt( (pn1->x * pn1->x) + + (pn1->y * pn1->y) + + (pn1->z * pn1->z) ); + distance2 = sqrt( (pn2->x * pn2->x) + + (pn2->y * pn2->y) + + (pn2->z * pn2->z) ); + distance = distance1 * distance2; + + rad = acos((double)dot_product/(double)distance); + /* convert to degrees */ + rad = (180 * rad)/PI; + + if ( rad <= difference) + return FALSE; + + /* double checking because of imprecision with floating + point acos function + */ + sprintf( arg1,"%.5f", rad ); + sprintf( arg2,"%.5f", difference ); + if ( strcmp( arg1, arg2 ) <=0 ) + return( FALSE ); + if ( rad <= difference) + return FALSE; + else + return TRUE; +} + +BOOL Check_VN(int vertex,int normal, struct vert_added *added) +{ + /* Check to see if we already added this vertex and normal */ + register int x,n; + + n = (added+vertex)->num; + for (x = 0; x < n; x++) + { + if (*((added+vertex)->normal+x) == normal) + return TRUE; + } + return FALSE; +} + +BOOL norm_array(int id, int vertex, double normal_difference, + struct vert_struct *n, int num_vert) +{ + static int last; + static struct vert_added *added; + register int x; + static BOOL first = TRUE; + + if (first) + { + /* This is the first time that we are in here, so we will allocate + a structure that will save the vertices that we added, so that we + do not add the same thing twice + */ + first = FALSE; + added = (struct vert_added *) malloc (sizeof (struct vert_added ) * num_vert); + /* The number of vertices added for each vertex must be initialized to + zero + */ + for (x = 0; x < num_vert; x++) + (added+x)->num = 0; + } + + if (vertex) + /* Set the pointer to the vertex, we will be calling again with the + normal to fill it with + */ + last = id; + else + { + /* Fill the pointer with the id of the normal */ + if (*(vert_norms + last) == 0) + *(vert_norms + last) = id; + else if ((*(vert_norms + last) != id) && ((int)normal_difference != 360)) + { + /* difference is big enough, we need to create a new vertex */ + if (new_vertex(normal_difference,id,*(vert_norms + last),n)) + { + /* First check to see if we added this vertex and normal already */ + if (Check_VN(last,id,added)) + return FALSE; + /* OK, create the new vertex, and have its id = the number of vertices + and its normal what we have here + */ + vert_norms = realloc(vert_norms, sizeof(int) * (num_vert + 1)); + if (!vert_norms) + { + printf("Allocation error - aborting\n"); + exit(1); + } + *(vert_norms + num_vert) = id; + /* We created a new vertex, now put it in our added structure so + we do not add the same thing twice + */ + (added+last)->num = (added+last)->num + 1; + if ((added+last)->num == 1) + { + /* First time */ + (added+last)->normal = (int *) malloc (sizeof (int ) * 1); + *((added+last)->normal) = id; + } + else + { + /* Not the first time, reallocate space */ + (added+last)->normal = realloc((added+last)->normal,sizeof(int) * (added+last)->num); + *((added+last)->normal+((added+last)->num-1)) = id; + } + return TRUE; + } + } + } + return FALSE; +} + +void add_texture(int id,BOOL vertex) +{ + /* Save the texture with its vertex for future use when outputting */ + static int last; + + if (vertex) + last = id; + else + *(vert_texture+last) = id; +} + +int add_vert_id(int id, int index_count) +{ + register int x; + + /* Test if degenerate, if so do not add degenerate vertex */ + for (x = 1; x < index_count ; x++) + { + if (ids[x] == id) + return 0; + } + ids[index_count] = id; + return 1; +} + +void add_norm_id(int id, int index_count) +{ + norms[index_count] = id; +} + +void AddNewFace(int ids[MAX1], int vert_count, int face_id, int norms[MAX1]) +{ +PF_FACES pfNode; +int *pTempInt; +int *pnorms; +F_EDGES **pTempVertptr; +int *pTempmarked, *pTempwalked; +register int y,count = 0,sum = 0; + + /* Add a new face into our face data structure */ + + pfNode = (PF_FACES) malloc(sizeof(F_FACES) ); + if ( pfNode ) + { + pfNode->pPolygon = (int*) malloc(sizeof(int) * (vert_count) ); + pfNode->pNorms = (int*) malloc(sizeof(int) * (vert_count) ); + pfNode->VertandId = (F_EDGES**)malloc(sizeof(F_EDGES*) * (vert_count)); + pfNode->marked = (int*)malloc(sizeof(int) * (vert_count)); + pfNode->walked = (int*)malloc(sizeof(int) * (vert_count)); + } + pTempInt =pfNode->pPolygon; + pnorms = pfNode->pNorms; + pTempmarked = pfNode->marked; + pTempwalked = pfNode->walked; + pTempVertptr = pfNode->VertandId; + pfNode->nPolSize = vert_count; + pfNode->seen = -1; + pfNode->seen2 = -1; + for (y=1;y<=vert_count;y++) + { + *(pTempInt + count) = ids[y]; + *(pnorms + count) = norms[y]; + *(pTempmarked + count) = FALSE; + *(pTempwalked + count) = -1; + *(pTempVertptr+count) = NULL; + count++; + } + AddHead(PolFaces[face_id-1],(PLISTINFO) pfNode); +} + + +void CopyFace(int ids[MAX1], int vert_count, int face_id, int norms[MAX1]) +{ +PF_FACES pfNode; +int *pTempInt; +int *pnorms; +F_EDGES **pTempVertptr; +int *pTempmarked, *pTempwalked; +register int y,count = 0,sum = 0; + + /* Copy a face node into a new node, used after the global algorithm + is run, so that we can save whatever is left into a new structure + */ + + pfNode = (PF_FACES) malloc(sizeof(F_FACES) ); + if ( pfNode ) + { + pfNode->pPolygon = (int*) malloc(sizeof(int) * (vert_count) ); + pfNode->pNorms = (int*) malloc(sizeof(int) * (vert_count) ); + pfNode->VertandId = (F_EDGES**)malloc(sizeof(F_EDGES*) * (vert_count)); + pfNode->marked = (int*)malloc(sizeof(int) * (vert_count)); + pfNode->walked = (int*)malloc(sizeof(int) * (vert_count)); + } + pTempInt =pfNode->pPolygon; + pnorms = pfNode->pNorms; + pTempmarked = pfNode->marked; + pTempwalked = pfNode->walked; + pTempVertptr = pfNode->VertandId; + pfNode->nPolSize = vert_count; + pfNode->seen = -1; + pfNode->seen2 = -1; + for (y=0;y<vert_count;y++) + { + *(pTempInt + count) = ids[y]; + *(pnorms + count) = norms[y]; + *(pTempmarked + count) = FALSE; + *(pTempwalked + count) = -1; + *(pTempVertptr+count) = NULL; + count++; + } + AddHead(PolFaces[face_id-1],(PLISTINFO) pfNode); +} + +void Add_Edge(int v1,int v2) +{ +PF_EDGES temp = NULL; +ListHead *pListHead; +BOOL flag = TRUE; +register int t,count = 0; + + /* Add a new edge into the edge data structure */ + if (v1 > v2) + { + t = v1; + v1 = v2; + v2 = t; + } + + pListHead = PolEdges[v1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + { + printf("Have the wrong edge \n:"); + exit(1); + } + + while (flag) + { + if (v2 == temp->edge[0]) + return; + else + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,++count); + + } +} + +void Add_AdjEdge(int v1,int v2,int fnum,int index1 ) +{ + PF_EDGES temp = NULL; + PF_FACES temp2 = NULL; + PF_EDGES pfNode; + ListHead *pListHead; + ListHead *pListFace; + BOOL flag = TRUE; + register int count = 0; + register int t,v3 = -1; + + if (v1 > v2) + { + t = v1; + v1 = v2; + v2 = t; + } + pListFace = PolFaces[fnum]; + temp2 = (PF_FACES) PeekList(pListFace,LISTHEAD,0); + pListHead = PolEdges[v1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + flag = FALSE; + count++; + while (flag) + { + if (v2 == temp->edge[0]) + { + /* If greater than 2 polygons adjacent to an edge, then we will + only save the first 2 that we found. We will have a small performance + hit, but this does not happen often. + */ + if (temp->edge[2] == -1) + temp->edge[2] = fnum; + else + v3 = temp->edge[2]; + flag = FALSE; + } + else + { + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + count++; + if (temp == NULL) + flag = FALSE; + } + } + + /* Did not find it */ + if (temp == NULL) + { + pfNode = (PF_EDGES) malloc(sizeof(F_EDGES) ); + if ( pfNode ) + { + pfNode->edge[0] = v2; + pfNode->edge[1] = fnum; + pfNode->edge[2] = v3; + AddTail( PolEdges[v1], (PLISTINFO) pfNode ); + } + else + { + printf("Out of memory!\n"); + exit(1); + } + + *(temp2->VertandId+index1) = pfNode; + } + else + *(temp2->VertandId+index1) = temp; + +} + + diff --git a/Tools/Stripe_u/add.h b/Tools/Stripe_u/add.h new file mode 100644 index 000000000..607363e5e --- /dev/null +++ b/Tools/Stripe_u/add.h @@ -0,0 +1,25 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: add.h +-----------------------------------------------------------------------*/ + +BOOL new_vertex(); +BOOL Check_VN(); +BOOL norm_array(); +void add_texture(); +int add_vert_id(); +void add_norm_id(); +void AddNewFace(); +void CopyFace(); +void Add_Edge(); +void Add_AdjEdge(); + + + diff --git a/Tools/Stripe_u/bands.c b/Tools/Stripe_u/bands.c new file mode 100644 index 000000000..14a9fe4c0 --- /dev/null +++ b/Tools/Stripe_u/bands.c @@ -0,0 +1,549 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: bands.c + This file contains the main procedure code that will read in the + object and then call the routines that produce the triangle strips. +*/ +/*---------------------------------------------------------------------*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include "global.h" +#include "polverts.h" +#include "triangulate.h" +#include "ties.h" +#include "outputex.h" +#include "options.h" +#include "local.h" +#include "init.h" +#include "free.h" +#include "add.h" + +#define MAX1 60 +/* TIMING for Windows */ +#ifdef WIN32 +#include <sys/timeb.h> +#include <time.h> +/* TIMING for UNIX */ +#else +#include <sys/types.h> +#include <sys/param.h> +#include <sys/times.h> +#include <sys/time.h> +struct timeval tm; +struct timezone tz; +double et; +#define START gettimeofday(&tm,&tz);\ + et = (tm.tv_sec)+ (0.000001* (tm.tv_usec)); + +#define STOP gettimeofday(&tm,&tz);\ + et = (tm.tv_sec)+(0.000001*(tm.tv_usec)) - et; +#endif + + +void get_time() +{ + /* For timing */ + #ifdef WIN32 + struct _timeb timebuffer; + char *timeline; + #else + long timer; + #endif + + + #ifdef WIN32 + _ftime( &timebuffer ); + timeline = ctime( & ( timebuffer.time ) ); + printf( "The time is %.19s.%hu %s", timeline, timebuffer.millitm, &timeline[20] ); + #else + printf("Time for last frame = %lf seconds\n", et); + #endif +} + +/* +** + Here the main program begins. It will start by loading in a .obj file + then it will convert the polygonal model into triangle strips. +** +*/ + +void main (int argc,char *argv[]) +{ + char *fname,*all,buff[255], *ptr, *ptr2; + FILE *file, *bands; + int face_id=0, vert_count, loop, num=0,num2; + float center[3]; + int temp[MAX1],vertex,strips, swaps,tempi,cost,triangles; + int f,t,tr,g; + char *file_open; + int num_vert = 0, + num_faces = 0, + num_nvert = 0, + num_edges = 0, + num_texture = 0, + num_tris = 0; + double fra = 0.0; + BOOL texture, normal, normal_and_texture,quads = FALSE; + + /* Options variables */ + float norm_difference; + + /* Structures for the object */ + struct vert_struct *vertices = NULL, + *nvertices = NULL, + *pvertices = NULL, + *pnvertices = NULL; + + get_time(); + START + + /* File that will contain the triangle strip data */ + bands = fopen("bands.d","w"); + + /* + Scan the file once to find out the number of vertices, + vertice normals, and faces so we can set up some memory + structures + */ + /* Interpret the options specified */ + norm_difference = get_options(argc,argv,&f,&t,&tr,&g); + if (f == BINARY) + file_open = "rb"; + else + file_open = "r"; + + fname = argv[argc-1]; + printf ("File: %s\n",fname); + /*printf ("Scanning...%s ",file_open);*/ + + + /* File can be in binary for faster reading */ + if (file = fopen (fname,file_open)) + { + while (!feof (file)) + { + /* Read a line */ + if (f == BINARY) + fread (buff,sizeof(char) * 255,1, file); + else + fgets (buff, sizeof(char) * 255, file); + num++; + /* At a vertex */ + if (*buff == 'v') + { + /* At a normal */ + if (*(buff+1)=='n') + num_nvert++; + else if (*(buff+1)=='t') + num_texture++; + /* At a regular vertex */ + else + num_vert++; + } + /* At a face */ + else if (*buff == 'f') + { + num_faces++; + strtok(buff, " "); + tempi = 0; + while (strtok(NULL, " ") != NULL) tempi++; + num_tris += tempi - 2; + } + } + fclose (file); + } + + else + { + printf("Error in the file name\n"); + exit(1); + } + + + /* Allocate structures for the information */ + Start_Face_Struct(num_faces); + vertices = (struct vert_struct *) + malloc (sizeof (struct vert_struct) * num_vert); + + if (num_nvert > 0) + { + nvertices = (struct vert_struct *) + malloc (sizeof (struct vert_struct) * num_nvert); + vert_norms = (int *) + malloc (sizeof (int) * num_vert); + /* Initialize entries to zero, in case there are 2 hits + to the same vertex we will know it - used for determining + the normal difference + */ + init_vert_norms(num_vert); + } + else + nvertices = NULL; + + if (num_texture > 0) + { + vert_texture = (int *) malloc (sizeof(int) * num_vert); + init_vert_texture(num_vert); + } + + /* Set up the temporary 'p' pointers + */ + pvertices = vertices; + pnvertices = nvertices; + + /* Load the object into memory */ + /*printf (" Loading...");*/ + + fprintf(bands,"#%s: a triangle strip representation created by STRIPE.\n#This is a .objf file\n#by Francine Evans\n",fname); + + /* File will be put in a list for faster execution if file is in binary */ + if (file = fopen(fname,file_open)) + { + if (f == BINARY) + { + all = (char *) malloc (sizeof(char) * 255 * num); + fread(all,sizeof(char) * 255 * num, 1, file); + ptr = all; + } + else + ptr = (char *) malloc (sizeof(char) * 255 * num); + } + + + while (num > 0) + { + num--; + if (f == ASCII) + fgets (ptr, sizeof(char) * 255, file); + else + ptr = ptr + 255; + + /* Load in vertices/normals */ + if (*ptr == 'v') + { + if (*(ptr+1)=='n') + { + sscanf (ptr+3,"%lf%lf%lf", + &(pnvertices->x), + &(pnvertices->y), + &(pnvertices->z)); + fprintf(bands,"vn %lf %lf %lf\n", + pnvertices->x,pnvertices->y,pnvertices->z); + ++pnvertices; + } + else if (*(ptr+1)=='t') + { + sscanf (ptr+3,"%f%f%f",¢er[0],¢er[1],¢er[2]); + fprintf(bands,"vt %f %f %f\n",center[0],center[1],center[2]); + } + else + { + sscanf (ptr+2,"%lf%lf%lf", + &(pvertices->x), + &(pvertices->y), + &(pvertices->z)); + fprintf(bands,"v %lf %lf %lf\n", + pvertices->x,pvertices->y,pvertices->z); + ++pvertices; + } + } + + else if (*ptr == 'f') + { + /* Read in faces */ + num2 = 0; + face_id++; + ptr2 = ptr+1; + normal = FALSE; texture = FALSE, normal_and_texture = FALSE; + while (*ptr2) + { + if (*ptr2 >='0' && *ptr2 <='9') + { + num2++; + ++ptr2; + while (*ptr2 && (*ptr2!=' ' && *ptr2!='/')) + ptr2++; + /* There are normals in this line */ + if (*ptr2 == '/') + { + if (*(ptr2+1) == '/') + normal = TRUE; + else + texture = TRUE; + } + else if (*ptr2 == ' ') + { + if ((num2 == 3) && (texture)) + normal_and_texture = TRUE; + } + } + else + ++ptr2; + } + + ptr2 = ptr+1; + + /* loop on the number of numbers in this line of face data + */ + vert_count = 0; + + for (loop=0;loop<num2;loop++) + { + /* skip the whitespace */ + while (*ptr2<'0' || *ptr2>'9') + { + if (*ptr2 == '-') + break; + ptr2++; + } + vertex = atoi(ptr2)-1; + if (vertex < 0) + { + vertex = num_vert + vertex; + *ptr2 = ' '; + ptr2++; + } + /* If there are either normals or textures with the vertices + in this file, the data alternates so we must read it this way + */ + if ( (normal) && (!normal_and_texture)) + { + if (loop%2) + { + add_norm_id(vertex,vert_count); + /* Test here to see if we added a new vertex, since the + vertex has more than one normal and the 2 normals are greater + than the threshold specified + */ + if (norm_array(vertex,0,norm_difference,nvertices,num_vert)) + { + /* Add a new vertex and change the + id of the vertex that we just read to the id of the new + vertex that we just added + */ + /* Put it in the output file, note the added vertices will + be after the normals and separated from the rest of the + vertices. Will not affect our viewer + */ + fprintf(bands,"v %lf %lf %lf\n", + (vertices + temp[vert_count - 1])->x, + (vertices + temp[vert_count - 1])->y, + (vertices + temp[vert_count - 1])->z); + num_vert++; + temp[vert_count - 1] = num_vert - 1; + if (!(add_vert_id(num_vert - 1,vert_count))) + vert_count--; + } + } + /* the vertex */ + else + { + temp[vert_count] = vertex ; + vert_count++; + if (!(add_vert_id(vertex,vert_count))) + vert_count--; + norm_array(vertex,1,norm_difference,nvertices,num_vert); + } + } + + /* Else there are vertices and textures with the data */ + else if (normal_and_texture) + { + if( !((loop+1)%3)) + { + add_norm_id(vertex,vert_count); + /* Test here to see if we added a new vertex, since the + vertex has more than one normal and the 2 normals are greater + than the threshold specified + */ + if (norm_array(vertex,0,norm_difference,nvertices,num_vert)) + { + /* Add a new vertex and change the + id of the vertex that we just read to the id of the new + vertex that we just added + */ + /* Put it in the output file, note the added vertices will + be after the normals and separated from the rest of the + vertices. Will not affect our viewer + */ + fprintf(bands,"v %lf %lf %lf\n", + (vertices + temp[vert_count - 1])->x, + (vertices + temp[vert_count - 1])->y, + (vertices + temp[vert_count - 1])->z); + num_vert++; + temp[vert_count - 1] = num_vert - 1; + if (!(add_vert_id(num_vert - 1,vert_count))) + vert_count--; + } + } + /* the vertex */ + else if ((loop == 0) || (*(ptr2-1) == ' ')) + { + temp[vert_count] = vertex ; + vert_count++; + if (vert_count == 4) + quads = TRUE; + if (!(add_vert_id(vertex,vert_count))) + vert_count--; + add_texture(vertex,TRUE); + norm_array(vertex,1,norm_difference,nvertices,num_vert); + } + else /* The texture */ + add_texture(vertex,FALSE); + } + + else if ( texture ) + { + /* the vertex */ + if (!(loop%2)) + { + temp[vert_count] = vertex ; + vert_count++; + if (vert_count == 4) + quads = TRUE; + add_texture(vertex,TRUE); + if (!(add_vert_id(vertex,vert_count))) + vert_count--; + norm_array(vertex,1,norm_difference,nvertices,num_vert); + } + else /* texture */ + add_texture(vertex,FALSE); + } + + else + { + /*** no nvertices ***/ + temp[vert_count] = vertex ; + vert_count++; + if (vert_count == 4) + quads = TRUE; + if (!(add_vert_id(vertex,vert_count))) + vert_count--; + } + while (*ptr2>='0' && *ptr2<='9') + ptr2++; + } + /* Done with the polygon */ + num_edges += vert_count; + /* add it to face structure */ + if (vert_count >= 3) + AddNewFace(ids,vert_count,face_id,norms); + else + face_id--; + if (vert_count == 4) + quads = TRUE; + } + else if ((g == TRUE) && (face_id > 0) + && ((*ptr == 'g') || (*ptr == 's') || (*ptr == 'm') || (*ptr == 'o'))) + { + /* The user specified that the strips will be contained in each group + from the data file, so we just finished a group and will find the + triangle strips in it. + */ + Start_Edge_Struct(num_vert); + Find_Adjacencies(face_id); + if (quads) + { + Init_Table_SGI(); + Build_SGI_Table(num_vert,face_id); + /* Code for lengths of walks in each direction */ + Save_Walks(face_id,TRUE); + + /* Code for finding the bands */ + Find_Bands(face_id,bands,&swaps,&strips,&cost,&triangles,num_nvert,vert_norms,num_texture,vert_texture); + + /* Remove the faces that we did so that we can + run the strip code on the rest of the faces that are left + */ + if (cost != 0) + { + printf("Total %d triangles with %d cost\n",triangles,cost); + Save_Rest(&face_id); + printf("We saved %d .... now doing the local algorithm\n",face_id); + fprintf(bands,"\n#local\n"); + End_Edge_Struct(num_vert); + Start_Edge_Struct(num_vert); + Find_Adjacencies(face_id); + } + } + + SGI_Strip(num_vert,face_id,bands,t,tr); + + /* Get the total cost */ + Output_TriEx(-1,-2,-3,NULL,-1,-20,cost); + + End_Face_Struct(num_faces); + End_Edge_Struct(num_vert); + cost = 0; + face_id = 0; + quads = FALSE; + Start_Face_Struct(num_faces-face_id); + num_faces = num_faces - face_id; + Free_Strips(); + } +} + + /* Done reading in all the information into data structures */ + num_faces = face_id; + fclose (file); + /*printf(" Done.\n\n");*/ + free(vertices); + free(nvertices); + + /*printf ("Vertices: %d\nNormals: %d\nFaces: %d\n",num_vert,num_nvert,num_faces);*/ + Start_Edge_Struct(num_vert); + Find_Adjacencies(num_faces); + + /* Initialize it */ + Init_Table_SGI(); + /* Build it */ + Build_SGI_Table(num_vert,num_faces); + + InitStripTable(); + + + if (quads) + { + /* Code for lengths of walks in each direction */ + Save_Walks(num_faces,TRUE); + + /* Code for finding the bands */ + Find_Bands(num_faces,bands,&swaps,&strips,&cost,&triangles,num_nvert,vert_norms,num_texture,vert_texture); + /*printf("Total %d triangles with %d cost\n",triangles,cost);*/ + + /* Remove the faces that we did so that we can + run the strip code on the rest of the faces that are left + */ + Save_Rest(&num_faces); + /*printf("We saved %d .... now doing the local algorithm\n",num_faces);*/ + fprintf(bands,"\n#local\n"); + End_Edge_Struct(num_vert); + Start_Edge_Struct(num_vert); + Find_Adjacencies(num_faces); + } + + SGI_Strip(num_vert,num_faces,bands,t,tr); + + /* Get the total cost */ + Output_TriEx(-1,-2,-3,NULL,-1,-20,cost); + + End_Face_Struct(num_faces); + End_Edge_Struct(num_vert); + fclose(bands); + STOP + + get_time(); + +} + diff --git a/Tools/Stripe_u/common.c b/Tools/Stripe_u/common.c new file mode 100644 index 000000000..6e553f149 --- /dev/null +++ b/Tools/Stripe_u/common.c @@ -0,0 +1,811 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: common.c + This file contains common code used in both the local and global algorithm +*/ +/*---------------------------------------------------------------------*/ + + +#include <stdlib.h> +#include "polverts.h" +#include "extend.h" +#include "output.h" +#include "triangulate.h" +#include "util.h" +#include "add.h" + +int Old_Adj(int face_id) +{ + /* Find the bucket that the face_id is currently in, + because maybe we will be deleting it. + */ + PF_FACES temp = NULL; + ListHead *pListHead; + int size,y; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + if ( temp == NULL ) + { + printf("The face was already deleted, there is an error\n"); + exit(0); + } + + size = temp->nPolSize; + if (Done(face_id,size,&y) == NULL) + { + printf("There is an error in finding the face\n"); + exit(0); + } + return y; +} + +int Number_Adj(int id1, int id2, int curr_id) +{ + /* Given edge whose endpoints are specified by id1 and id2, + determine how many polygons share this edge and return that + number minus one (since we do not want to include the polygon + that the caller has already). + */ + + int size,y,count=0; + PF_EDGES temp = NULL; + PF_FACES temp2 = NULL; + ListHead *pListHead; + BOOL there= FALSE; + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* new edge that was created might not be here */ + return 0; + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* This edge was not there in the original, which + mean that we created it in the partial triangulation. + So it is adjacent to nothing. + */ + return 0; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return 0; + else + { + /* It was adjacent to another polygon, but maybe we did this + polygon already, and it was done partially so that this edge + could have been done + */ + if (curr_id != temp->edge[1]) + { + /* Did we use this polygon already?and it was deleted + completely from the structure + */ + pListHead = PolFaces[temp->edge[1]]; + temp2 = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + if (Done(temp->edge[1],temp2->nPolSize,&size) == NULL) + return 0; + } + else + { + pListHead = PolFaces[temp->edge[2]]; + temp2 = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + if (Done(temp->edge[2],temp2->nPolSize,&size)== NULL) + return 0; + } + + /* Now we have to check whether it was partially done, before + we can say definitely if it is adjacent. + Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp2 != NULL ) + { + /* Size of the polygon */ + size = temp2->nPolSize; + for (y = 0; y< size; y++) + { + /* If we are doing partial triangulation, we must check + to see whether the edge is still there in the polygon, + since we might have done a portion of the polygon + and saved the rest for later. + */ + if (y != (size-1)) + { + if( ((id1 == *(temp2->pPolygon+y)) && (id2 ==*(temp2->pPolygon+y+1))) + || ((id2 == *(temp2->pPolygon+y)) && (id1 ==*(temp2->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((id1 == *(temp2->pPolygon)) && (id2 == *(temp2->pPolygon+size-1))) + || ((id2 == *(temp2->pPolygon)) && (id1 ==*(temp2->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + } + + if (there ) + return 1; + return 0; + } +} + +int Min_Adj(int id) +{ + /* Used for the lookahead to break ties. It will + return the minimum adjacency found at this face. + */ + int y,numverts,t,x=60; + PF_FACES temp=NULL; + ListHead *pListHead; + + /* If polygon was used then we can't use this face */ + if (Done(id,59,&y) == NULL) + return 60; + + /* It was not used already */ + pListHead = PolFaces[id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + if ( temp != NULL ) + { + numverts = temp->nPolSize; + for (y = 0; y< numverts; y++) + { + if (y != (numverts-1)) + t = Number_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1),id); + else + t = Number_Adj(*(temp->pPolygon),*(temp->pPolygon+(numverts-1)),id); + if (t < x) + x = t; + } + } + if (x == -1) + { + printf("Error in the look\n"); + exit(0); + } + return x; +} + + + +void Edge_Least(int *index,int *new1,int *new2,int face_id,int size) +{ + /* We had a polygon without an input edge and now we re going to pick one + of the edges with the least number of adjacencies to be the input + edge + */ + register int x,value,smallest=60; + + for (x = 0; x<size; x++) + { + if (x != (size -1) ) + value = Number_Adj(*(index+x),*(index+x+1),face_id); + else + value = Number_Adj(*(index),*(index+size-1),face_id); + if (value < smallest) + { + smallest = value; + if (x != (size -1)) + { + *new1 = *(index+x); + *new2 = *(index+x+1); + } + else + { + *new1 = *(index); + *new2 = *(index+size-1); + } + } + } + if ((smallest == 60) || (smallest < 0)) + { + printf("There is an error in getting the least edge\n"); + exit(0); + } +} + + +void Check_In_Polygon(int face_id, int *min, int size) +{ + /* Check to see the adjacencies by going into a polygon that has + greater than 4 sides. + */ + + ListHead *pListHead; + PF_FACES temp; + int y,id1,id2,id3,x=0,z=0; + int saved[2]; + int big_saved[60]; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + /* Get the input edge that we came in on */ + Last_Edge(&id1,&id2,&id3,0); + + /* Find the number of adjacencies to the edges that are adjacent + to the input edge. + */ + for (y=0; y< size; y++) + { + if (y != (size-1)) + { + if (((*(temp->pPolygon+y) == id2) && (*(temp->pPolygon+y+1) != id3)) + || ((*(temp->pPolygon+y) == id3) && (*(temp->pPolygon+y+1) != id2))) + { + saved[x++] = Number_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1),face_id); + big_saved[z++] = saved[x-1]; + } + else + big_saved[z++] = Number_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1),face_id); + } + else + { + if (((*(temp->pPolygon) == id2) && (*(temp->pPolygon+size-1) != id3)) + || ((*(temp->pPolygon) == id3) && (*(temp->pPolygon+size-1) != id2))) + { + saved[x++] = Number_Adj(*(temp->pPolygon),*(temp->pPolygon+size-1),face_id); + big_saved[z++] = saved[x-1]; + } + else + big_saved[z++] = Number_Adj(*(temp->pPolygon),*(temp->pPolygon+size-1),face_id); + } + } + /* There was an input edge */ + if (x == 2) + { + if (saved[0] < saved[1]) + /* Count the polygon that we will be cutting as another adjacency*/ + *min = saved[0] + 1; + else + *min = saved[1] + 1; + } + /* There was not an input edge */ + else + { + if (z != size) + { + printf("There is an error with the z %d %d\n",size,z); + exit(0); + } + *min = 60; + for (x = 0; x < size; x++) + { + if (*min > big_saved[x]) + *min = big_saved[x]; + } + } +} + + +void New_Face (int face_id, int v1, int v2, int v3) +{ + /* We want to change the face that was face_id, we will + change it to a triangle, since the rest of the polygon + was already outputtted + */ + ListHead *pListHead; + PF_FACES temp = NULL; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp != NULL ) + { + /* Size of the polygon */ + if (temp->nPolSize != 4) + { + printf("There is a miscalculation in the partial\n"); + exit (0); + } + temp->nPolSize = 3; + *(temp->pPolygon) = v1; + *(temp->pPolygon+1) = v2; + *(temp->pPolygon+2) = v3; + } +} + +void New_Size_Face (int face_id) +{ + /* We want to change the face that was face_id, we will + change it to a triangle, since the rest of the polygon + was already outputtted + */ + ListHead *pListHead; + PF_FACES temp = NULL; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp != NULL ) + (temp->nPolSize)--; + else + printf("There is an error in updating the size\n"); +} + + + +void Check_In_Quad(int face_id,int *min) +{ + /* Check to see what the adjacencies are for the polygons that + are inside the quad, ie the 2 triangles that we can form. + */ + ListHead *pListHead; + int y,id1,id2,id3,x=0; + int saved[4]; + PF_FACES temp; + register int size = 4; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + /* Get the input edge that we came in on */ + Last_Edge(&id1,&id2,&id3,0); + + /* Now find the adjacencies for the inside triangles */ + for (y = 0; y< size; y++) + { + /* Will not do this if the edge is the input edge */ + if (y != (size-1)) + { + if ((((*(temp->pPolygon+y) == id2) && (*(temp->pPolygon+y+1) == id3))) || + (((*(temp->pPolygon+y) == id3) && (*(temp->pPolygon+y+1) == id2)))) + saved[x++] = -1; + else + { + if (x == 4) + { + printf("There is an error in the check in quad \n"); + exit(0); + } + /* Save the number of Adjacent Polygons to this edge */ + saved[x++] = Number_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1),face_id); + } + } + else if ((((*(temp->pPolygon) == id2) && (*(temp->pPolygon+size-1) == id3))) || + (((*(temp->pPolygon) == id3) && (*(temp->pPolygon+size-1) == id2))) ) + saved[x++] = -1; + else + { + if (x == 4) + { + printf("There is an error in the check in quad \n"); + exit(0); + } + /* Save the number of Adjacent Polygons to this edge */ + saved[x++] = Number_Adj(*(temp->pPolygon),*(temp->pPolygon+size-1),face_id); + + } + } + if (x != 4) + { + printf("Did not enter all the values %d \n",x); + exit(0); + } + + *min = 10; + for (x=0; x<4; x++) + { + if (x!= 3) + { + if ((saved[x] != -1) && (saved[x+1] != -1) && + ((saved[x] + saved[x+1]) < *min)) + *min = saved[x] + saved[x+1]; + } + else + { + if ((saved[0] != -1) && (saved[x] != -1) && + ((saved[x] + saved[0]) < *min)) + *min = saved[0] + saved[x]; + } + } +} + + + +int Get_Output_Edge(int face_id, int size, int *index,int id2,int id3) +{ + /* Return the vertex adjacent to either input1 or input2 that + is adjacent to the least number of polygons on the edge that + is shared with either input1 or input2. + */ + register int x=0,y; + int saved[2]; + int edges[2][1]; + + for (y = 0; y < size; y++) + { + if (y != (size-1)) + { + if (((*(index+y) == id2) && (*(index+y+1) != id3)) + || ((*(index+y) == id3) && (*(index+y+1) != id2))) + { + saved[x++] = Number_Adj(*(index+y),*(index+y+1),face_id); + edges[x-1][0] = *(index+y+1); + } + else if (y != 0) + { + if (( (*(index+y) == id2) && (*(index+y-1) != id3) ) || + ( (*(index+y) == id3) && (*(index+y-1) != id2)) ) + { + saved[x++] = Number_Adj(*(index+y),*(index+y-1),face_id); + edges[x-1][0] = *(index+y-1); + } + } + else if (y == 0) + { + if (( (*(index) == id2) && (*(index+size-1) != id3) ) || + ( (*(index) == id3) && (*(index+size-1) != id2)) ) + { + saved[x++] = Number_Adj(*(index),*(index+size-1),face_id); + edges[x-1][0] = *(index+size-1); + } + } + + } + else + { + if (((*(index+size-1) == id2) && (*(index) != id3)) + || ((*(index+size-1) == id3) && (*(index) != id2))) + { + saved[x++] = Number_Adj(*(index),*(index+size-1),face_id); + edges[x-1][0] = *(index); + } + + if (( (*(index+size-1) == id2) && (*(index+y-1) != id3) ) || + ( (*(index+size-1) == id3) && (*(index+y-1) != id2)) ) + { + saved[x++] = Number_Adj(*(index+size-1),*(index+y-1),face_id); + edges[x-1][0] = *(index+y-1); + } + } + } + if ((x != 2)) + { + printf("There is an error in getting the input edge %d \n",x); + exit(0); + } + if (saved[0] < saved[1]) + return edges[0][0]; + else + return edges[1][0]; + +} + +void Get_Input_Edge(int *index,int id1,int id2,int id3,int *new1,int *new2,int size, + int face_id) +{ + /* We had a polygon without an input edge and now we are going to pick one + as the input edge. The last triangle was id1,id2,id3, we will try to + get an edge to have something in common with one of those vertices, otherwise + we will pick the edge with the least number of adjacencies. + */ + + register int x; + int saved[3]; + + saved[0] = -1; + saved[1] = -1; + saved[2] = -1; + + /* Go through the edges to see if there is one in common with one + of the vertices of the last triangle that we had, preferably id2 or + id3 since those are the last 2 things in the stack of size 2. + */ + for (x=0; x< size; x++) + { + if (*(index+x) == id1) + { + if (x != (size-1)) + saved[0] = *(index+x+1); + else + saved[0] = *(index); + } + + if (*(index+x) == id2) + { + if (x != (size-1)) + saved[1] = *(index+x+1); + else + saved[1] = *(index); + } + + if (*(index+x) == id3) + { + if (x != (size -1)) + saved[2] = *(index+x+1); + else + saved[2] = *(index); + } + } + /* Now see what we saved */ + if (saved[2] != -1) + { + *new1 = id3; + *new2 = saved[2]; + return; + } + else if (saved[1] != -1) + { + *new1 = id2; + *new2 = saved[1]; + return; + } + else if (saved[0] != -1) + { + *new1 = id1; + *new2 = saved[0]; + return; + } + /* We did not find anything so get the edge with the least number of adjacencies */ + Edge_Least(index,new1,new2,face_id,size); + +} + +int Find_Face(int current_face, int id1, int id2, int *bucket) +{ + /* Find the face that is adjacent to the edge and is not the + current face. + */ + register int size,each_poly=0,y,tally=0,count=0; + PF_EDGES temp = NULL; + PF_FACES temp2 = NULL; + ListHead *pListHead; + int next_face; + BOOL there = FALSE; + + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + /* The input edge was a new edge */ + if (temp == NULL) + return -1; + + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + /* The input edge was a new edge */ + if (temp == NULL) + return -1; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return -1; + else + { + if (temp->edge[2] == current_face) + next_face = temp->edge[1]; + else + next_face = temp->edge[2]; + } + /* We have the other face adjacent to this edge, it is + next_face. + */ + pListHead = PolFaces[next_face]; + temp2 = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + /* See if the face was already deleted, and where + it is if it was not + */ + + if (Done(next_face,59,bucket) == NULL) + return -1; + + /* Make sure the edge is still in this polygon, and that it is not + done + */ + /* Size of the polygon */ + size = temp2->nPolSize; + for (y = 0; y< size; y++) + { + /* Make sure that the edge is still in the + polygon and was not deleted, because if the edge was + deleted, then we used it already. + */ + if (y != (size-1)) + { + if( ((id1 == *(temp2->pPolygon+y)) && (id2 ==*(temp2->pPolygon+y+1))) + || ((id2 == *(temp2->pPolygon+y)) && (id1 ==*(temp2->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((id1 == *(temp2->pPolygon)) && (id2 ==*(temp2->pPolygon+size-1))) + || ((id2 == *(temp2->pPolygon)) && (id1 ==*(temp2->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + + if (!there) + /* Edge already used and deleted from the polygon*/ + return -1; + else + return next_face; +} + +BOOL Look_Up(int id1,int id2,int face_id) +{ + /* See if the endpoints of the edge specified by id1 and id2 + are adjacent to the face with face_id + */ + register int count = 0; + PF_EDGES temp = NULL; + ListHead *pListHead; + PF_FACES temp2 = NULL; + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* Was a new edge that we created */ + return 0; + + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* Was a new edge that we created */ + return 0; + } + /* Was not adjacent to anything else except itself */ + if ((temp->edge[2] == face_id) || (temp->edge[1] == face_id)) + { + /* Edge was adjacent to face, make sure that edge is + still there + */ + if (Exist(face_id,id1,id2)) + return 1; + else + return 0; + } + else + return 0; +} + + +void Add_Id_Strips(int id, int where) +{ + /* Just save the triangle for later */ + P_STRIPS pfNode; + + pfNode = (P_STRIPS) malloc(sizeof(Strips) ); + if ( pfNode ) + { + pfNode->face_id = id; + if (where == 1) + AddTail(strips[0],(PLISTINFO) pfNode); + /* We are backtracking in the strip */ + else + AddHead(strips[0],(PLISTINFO) pfNode); + } + else + { + printf("There is not enough memory to allocate for the strips\n"); + exit(0); + } +} + + +int Num_Adj(int id1, int id2) +{ + /* Given edge whose endpoints are specified by id1 and id2, + determine how many polygons share this edge and return that + number minus one (since we do not want to include the polygon + that the caller has already). + */ + + PF_EDGES temp = NULL; + ListHead *pListHead; + register count=-1; + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + { + printf("There is an error in the creation of the table \n"); + exit(0); + } + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + { + printf("There is an error in the creation of the table\n"); + exit(0); + } + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return 0; + return 1; +} + + +void Add_Sgi_Adj(int bucket,int face_id) +{ + /* This routine will add the face to the proper bucket, + depending on how many faces are adjacent to it (what the + value bucket should be). + */ + P_ADJACENCIES pfNode; + + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + { + pfNode->face_id = face_id; + AddHead(array[bucket],(PLISTINFO) pfNode); + } + else + { + printf("Out of memory for the SGI adj list!\n"); + exit(0); + } +} + +void Find_Adjacencies(int num_faces) +{ + register int x,y; + register int numverts; + PF_FACES temp=NULL; + ListHead *pListHead; + + /* Fill in the adjacencies data structure for all the faces */ + for (x=0;x<num_faces;x++) + { + pListHead = PolFaces[x]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + if ( temp != NULL ) + { + numverts = temp->nPolSize; + if (numverts != 1) + { + for (y = 0; y< numverts; y++) + { + if (y != (numverts-1)) + Add_AdjEdge(*(temp->pPolygon+y),*(temp->pPolygon+y+1),x,y); + + else + Add_AdjEdge(*(temp->pPolygon),*(temp->pPolygon+(numverts-1)),x,numverts-1); + + } + } + temp = NULL; + } + } +} + + diff --git a/Tools/Stripe_u/common.h b/Tools/Stripe_u/common.h new file mode 100644 index 000000000..a220b3628 --- /dev/null +++ b/Tools/Stripe_u/common.h @@ -0,0 +1,41 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: common.h +-----------------------------------------------------------------------*/ + +void Add_AdjEdge(); +void Find_Adjacencies(); +void Add_Sgi_Adj(); +int Num_Adj(); +void Add_Id_Strips(); +BOOL Look_Up(); +int Number_Adj(); +int Old_Adj(); +int Min_Adj(); +int Find_Face(); +void Edge_Least(); +void Get_Input_Edge(); +int Get_Output_Edge(); +void Check_In_Polygon(); +void Check_In_Quad(); +void New_Size_Face (); +void New_Face (); + + + + + + + + + + + + diff --git a/Tools/Stripe_u/define.h b/Tools/Stripe_u/define.h new file mode 100644 index 000000000..931e5713f --- /dev/null +++ b/Tools/Stripe_u/define.h @@ -0,0 +1,13 @@ + +#define VRDATA double +#define MAX1 60 + +#define TRUE 1 +#define FALSE 0 + +#define PI 3.1415926573 + +struct vert_struct { + VRDATA x, y, z; /* point coordinates */ +}; + diff --git a/Tools/Stripe_u/extend.h b/Tools/Stripe_u/extend.h new file mode 100644 index 000000000..78c135e3a --- /dev/null +++ b/Tools/Stripe_u/extend.h @@ -0,0 +1,17 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: extend.h +-----------------------------------------------------------------------*/ + +int Bottom_Left(); +int Top_Left(); +void Start_Edge(); + + diff --git a/Tools/Stripe_u/free.c b/Tools/Stripe_u/free.c new file mode 100644 index 000000000..9494f4fb4 --- /dev/null +++ b/Tools/Stripe_u/free.c @@ -0,0 +1,110 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: free.c + This file contains the code used to free the data structures. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include "polverts.h" + +void ParseAndFreeList( ListHead *pListHead ) +{ + PLISTINFO value; + register int c,num; + + /* Freeing a linked list */ + num = NumOnList(pListHead); + for (c = 0; c< num; c++) + value = RemHead(pListHead); +} + +void FreePolygonNode( PF_VERTS pfVerts) +{ + /* Free a vertex node */ + if ( pfVerts->pPolygon ) + free( pfVerts->pPolygon ); + free( pfVerts ); + +} + +void Free_Strips() +{ + P_STRIPS temp = NULL; + + /* Free strips data structure */ + if (strips[0] == NULL) + return; + else + ParseAndFreeList(strips[0]); +} + +void FreeFaceNode( PF_FACES pfFaces) +{ + /* Free face node */ + if ( pfFaces->pPolygon ) + free( pfFaces->pPolygon ); + free( pfFaces ); +} + + +void FreeFaceTable(int nSize) +{ + register int nIndex; + + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + if ( PolFaces[nIndex] != NULL ) + ParseAndFreeList( PolFaces[nIndex] ); + } + free( PolFaces ); +} + +void FreeEdgeTable(int nSize) +{ + register int nIndex; + + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + if ( PolEdges[nIndex] != NULL ) + ParseAndFreeList( PolEdges[nIndex] ); + } + free( PolEdges ); +} + + +void Free_All_Strips() +{ + + ListHead *pListHead; + register int y; + + for (y =0; ; y++) + { + pListHead = all_strips[y]; + if (pListHead == NULL) + return; + else + ParseAndFreeList(all_strips[y]); + } +} + +void End_Face_Struct(int numfaces) +{ + FreeFaceTable(numfaces); +} + +void End_Edge_Struct(int numverts) +{ + FreeEdgeTable(numverts); +} + + diff --git a/Tools/Stripe_u/free.h b/Tools/Stripe_u/free.h new file mode 100644 index 000000000..3303d05e1 --- /dev/null +++ b/Tools/Stripe_u/free.h @@ -0,0 +1,22 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: free.h +-----------------------------------------------------------------------*/ + +void Free_All_Strips(); +void ParseAndFreeList(); +void FreePolygonNode(); +void Free_Strips(); +void FreeFaceTable(); +void FreeEdgeTable(); +void End_Face_Struct(); +void End_Edge_Struct(); + + diff --git a/Tools/Stripe_u/global.h b/Tools/Stripe_u/global.h new file mode 100644 index 000000000..3621b25e1 --- /dev/null +++ b/Tools/Stripe_u/global.h @@ -0,0 +1,37 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: global.h +-----------------------------------------------------------------------*/ + +#define VRDATA double +#define MAX1 60 + +#define TRUE 1 +#define FALSE 0 + +#ifndef PI +# define PI 3.1415926573 +#endif /* PI */ +#define ATOI(C) (C -'0') +#define X 0 +#define Y 1 +#define Z 2 +#define EVEN(x) (((x) & 1) == 0) +#define MAX_BAND 10000 + +struct vert_struct { + VRDATA x, y, z; /* point coordinates */ +}; + +int ids[MAX1]; +int norms[MAX1]; +int *vert_norms; +int *vert_texture; + diff --git a/Tools/Stripe_u/glove.h b/Tools/Stripe_u/glove.h new file mode 100644 index 000000000..74bcd07da --- /dev/null +++ b/Tools/Stripe_u/glove.h @@ -0,0 +1,151 @@ +/* + * dg2lib.h - header file for the DG2 library libdg2.a + * + * copyright 1988-92 VPL Research Inc. + * + */ + + + +/******** error returns from the library */ + +extern int DG2_error; /* for error information */ +extern float DG2_lib_version; /* for the library version */ +extern int DG2_box_version; /* for the firmware version */ +extern int DG2_glove_sensors; /* for the number of sensors in the glove */ + +/* defines for DG2_error values */ + +#define DG2_AOK 0 +#define DG2_SETTINGS_FILE -1 +#define DG2_SERIAL_OPEN -2 +#define DG2_SERIAL_PORT -4 +#define DG2_RESET -6 +#define DG2_PARAMETER -7 +#define DG2_FILE_IO -8 +#define DG2_CALIBRATION_FILE -9 +#define DG2_GESTURE_FILE -10 +#define DG2_CAL_GEST_FILES -11 +/* defines for DG2_response() */ + +#define DATAGLOVE 1 +#define POLHEMUS 2 +#define GESTURE 8 + +#define DG2_60Hz 1 +#define DG2_30Hz 2 +#define DG2_oneShot 3 + +/* defines for DG2_DataGlove_select() */ + +#define THUMB_INNER 0x1 +#define THUMB_OUTER 0x2 +#define INDEX_INNER 0x4 +#define INDEX_OUTER 0x8 +#define MIDDLE_INNER 0x10 +#define MIDDLE_OUTER 0x20 +#define RING_INNER 0x40 +#define RING_OUTER 0x80 +#define LITTLE_INNER 0x100 +#define LITTLE_OUTER 0x200 +#define NORMAL_JOINTS 0x3ff +#define FLEX11 0x400 +#define FLEX12 0x800 +#define FLEX13 0x1000 +#define FLEX14 0x2000 +#define FLEX15 0x4000 +#define FLEX16 0x8000 + + +/* defines for DG2_DataGlove_trans_select() */ + +#define DG2_TRANSLATED 5 +#define DG2_RAW 6 + +/* defines for DG2_Polhemus_units() */ + +#define POL_RAW 0 +#define POL_INCHES 1 +#define POL_CM 2 + +/* defines for DG2_user_IRQ() */ + +#define IRQ_ON 1 +#define IRQ_OFF 2 + + +/* defines for DG2_get_data() */ + +#define DG2_report 1 +#define DG2_userport 2 + + +/* dg2 command codes*/ +#define LEADINGBYTE 0x24 +#define RPT60 0x41 /* repeat 60 */ +#define RPT30 0x42 /* repeat 30 */ +#define ONESHOT 0x43 /* one shot */ +#define SYSID 0x44 /* system ID */ +#define EPTBUF 0x45 /* empty buffer */ +#define USRRD 0x46 /* user read */ +#define USRIRQ 0x47 /* user IRQ */ +#define QBRT 0x48 /* query bright */ +#define CDRST 0x49 /* cold reset */ +#define WMRST 0x4A /* warm reset */ +#define MEMALLO 0x4B /* memory alloc */ +#define DLTSND 0x4C /* delta send */ +#define SETBRT 0x4D /* set bright */ +#define SETDIM 0x4E /* set dim */ +#define FILBUF 0x4F /* fill buffer */ +#define LDTBL 0x50 /* load table */ +#define LDPOL 0x51 /* send up to 63 bytes to Polhemus */ +#define ANGLE 0x52 /* angles */ +#define NSNSR 0x53 /* num sensors */ +#define SETFB 0x54 /* set feedback */ +#define QCUT 0X55 /* query cutoff*/ +#define SETCUT 0X56 /* set cutoff */ +#define FLXVAL 0X57 /* raw flex values */ +#define USRWR 0X58 /* user write */ +#define JNTMAP 0X59 /* joint map */ +#define ERRMESS 0XFF /* error in command input */ +#define TIMOUT 0XFE /* timed out during command */ + +/* response structure */ + +typedef struct DG2_data { + char gesture; + double location[3]; /* X,Y,Z */ + double orientation[3]; /* yaw, pitch, roll */ + short flex[16]; + char gesture_name[20]; + short reserved[16]; + /* user port data: */ + char user_nibble; + char user_analog[3]; +} DG2_data; + + +/**************function prototypes*************/ +/*NOTE: all DG2_ functions return -1 on error*/ + +extern int DG2_open(char *portname, int baud); +extern int DG2_close(int filedes); +extern int DG2_direct(int filedes,char *message,int count); +extern int DG2_response(int filedes,int devices,int rate); +extern int DG2_DataGlove_select(int filedes,int flex_sensors); +extern int DG2_DataGlove_translation(int filedes,int flex_sensors,char table[16][256]); +extern int DG2_DataGlove_trans_select(int filedes,int status); +extern int DG2_DataGlove_LED_set(int filedes,int LED); +extern int DG2_DataGlove_LED_read(int filedes); +extern int DG2_Polhemus_units(int filedes,char type); +extern int DG2_Polhemus_direct(int filedes,char *message,int count); +extern int DG2_user_write(int filedes,int nibble); +extern int DG2_user_IRQ(int filedes,int mode); +extern int DG2_user_read(int filedes,DG2_data *data); +extern int DG2_get_data(int filedes,DG2_data *data); +extern int DG2_gesture_load(int filedes,char *calib,char *gest); + +/*use this with caution since it does not return until it gets a correct + *response from the DG2 +*/ +extern int DG2U_get_reply(int filedes,char *buff,int response,int size); diff --git a/Tools/Stripe_u/init.c b/Tools/Stripe_u/init.c new file mode 100644 index 000000000..b3b218cec --- /dev/null +++ b/Tools/Stripe_u/init.c @@ -0,0 +1,217 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: init.c + This file contains the initialization of data structures. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include "global.h" +#include "polverts.h" + +void init_vert_norms(int num_vert) +{ + /* Initialize vertex/normal array to have all zeros to + start with. + */ + register int x; + + for (x = 0; x < num_vert; x++) + *(vert_norms + x) = 0; +} + +void init_vert_texture(int num_vert) +{ + /* Initialize vertex/normal array to have all zeros to + start with. + */ + register int x; + + for (x = 0; x < num_vert; x++) + *(vert_texture + x) = 0; +} + +BOOL InitVertTable( int nSize ) +{ + register int nIndex; + + /* Initialize the vertex table */ + PolVerts = (ListHead**) malloc(sizeof(ListHead*) * nSize ); + if ( PolVerts ) + { + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + PolVerts[nIndex] = NULL; + } + return( TRUE ); + } + return( FALSE ); +} + +BOOL InitFaceTable( int nSize ) +{ + register int nIndex; + + /* Initialize the face table */ + PolFaces = (ListHead**) malloc(sizeof(ListHead*) * nSize ); + if ( PolFaces ) + { + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + PolFaces[nIndex] = NULL; + } + return( TRUE ); + } + return( FALSE ); +} + +BOOL InitEdgeTable( int nSize ) +{ + register int nIndex; + + /* Initialize the edge table */ + PolEdges = (ListHead**) malloc(sizeof(ListHead*) * nSize ); + if ( PolEdges ) + { + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + PolEdges[nIndex] = NULL; + } + return( TRUE ); + } + return( FALSE ); +} + + +void InitStripTable( ) +{ + + PLISTHEAD pListHead; + + /* Initialize the strip table */ + pListHead = ( PLISTHEAD ) malloc(sizeof(ListHead)); + if ( pListHead ) + { + InitList( pListHead ); + strips[0] = pListHead; + } + else + { + printf("Out of memory !\n"); + exit(0); + } + +} + +void Init_Table_SGI() +{ + PLISTHEAD pListHead; + int max_adj = 60; + register int x; + + /* This routine will initialize the table that will + have the faces sorted by the number of adjacent polygons + to it. + */ + + for (x=0; x< max_adj; x++) + { + /* We are allowing the max number of sides of a polygon + to be max_adj. + */ + pListHead = ( PLISTHEAD ) malloc(sizeof(ListHead)); + if ( pListHead ) + { + InitList( pListHead ); + array[x] = pListHead; + } + else + { + printf("Out of memory !\n"); + exit(0); + } + } +} + +void BuildVertTable( int nSize ) +{ + register int nIndex; + PLISTHEAD pListHead; + + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + pListHead = ( PLISTHEAD ) malloc(sizeof(ListHead)); + if ( pListHead ) + { + InitList( pListHead ); + PolVerts[nIndex] = pListHead; + } + else + return; + + } +} + + +void BuildFaceTable( int nSize ) +{ + register int nIndex; + PLISTHEAD pListHead; + + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + pListHead = ( PLISTHEAD ) malloc(sizeof(ListHead)); + if ( pListHead ) + { + InitList( pListHead ); + PolFaces[nIndex] = pListHead; + } + else + return; + + } +} + +void BuildEdgeTable( int nSize ) +{ + register int nIndex; + PLISTHEAD pListHead; + + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + pListHead = ( PLISTHEAD ) malloc(sizeof(ListHead)); + if ( pListHead ) + { + InitList( pListHead ); + PolEdges[nIndex] = pListHead; + } + else + return; + } +} + +void Start_Face_Struct(int numfaces) +{ + if (InitFaceTable(numfaces)) + { + BuildFaceTable(numfaces); + } +} + +void Start_Edge_Struct(int numverts) +{ + if (InitEdgeTable(numverts)) + { + BuildEdgeTable(numverts); + } +} + + diff --git a/Tools/Stripe_u/init.h b/Tools/Stripe_u/init.h new file mode 100644 index 000000000..2faf0e838 --- /dev/null +++ b/Tools/Stripe_u/init.h @@ -0,0 +1,30 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: init.h +-----------------------------------------------------------------------*/ + +void init_vert_norms(); +void init_vert_texture(); +BOOL InitVertTable(); +BOOL InitFaceTable(); +BOOL InitEdgeTable(); +void InitStripTable(); +void Init_Table_SGI(); +void BuildVertTable(); +void BuildFaceTable(); +void BuildEdgeTable(); +void Start_Face_Struct(); +void Start_Edge_Struct(); + + + + + + diff --git a/Tools/Stripe_u/local.c b/Tools/Stripe_u/local.c new file mode 100644 index 000000000..2db94904e --- /dev/null +++ b/Tools/Stripe_u/local.c @@ -0,0 +1,123 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: local.c + This file contains the code that initializes the data structures for + the local algorithm, and starts the local algorithm going. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include "polverts.h" +#include "local.h" +#include "triangulatex.h" +#include "sturctsex.h" +#include "common.h" +#include "outputex.h" +#include "util.h" +#include "init.h" + +void Find_StripsEx(FILE *output,FILE *strip,int *ties, + int tie, int triangulate, + int swaps,int *next_id) +{ + /* This routine will peel off the strips from the model */ + + ListHead *pListHead; + P_ADJACENCIES temp = NULL; + register int max,bucket=0; + BOOL whole_flag = TRUE; + int dummy = 0; + + /* Set the last known input edge to be null */ + Last_Edge(&dummy,&dummy,&dummy,1); + + /* Search for lowest adjacency polygon and output strips */ + while (whole_flag) + { + bucket = -1; + /* Search for polygons in increasing number of adjacencies */ + while (bucket < 59) + { + bucket++; + pListHead = array[bucket]; + max = NumOnList(pListHead); + if (max > 0) + { + temp = (P_ADJACENCIES) PeekList(pListHead,LISTHEAD,0); + if (temp == NULL) + { + printf("Error in the buckets%d %d %d\n",bucket,max,0); + exit(0); + } + Polygon_OutputEx(temp,temp->face_id,bucket,pListHead, + output,strip,ties,tie,triangulate,swaps,next_id,1); + /* Try to extend backwards, if the starting polygon in the + strip had 2 or more adjacencies to begin with + */ + if (bucket >= 2) + Extend_BackwardsEx(temp->face_id,output,strip,ties,tie,triangulate, + swaps,next_id); + break; + } + } + /* Went through the whole structure, it is empty and we are done. + */ + if ((bucket == 59) && (max == 0)) + whole_flag = FALSE; + + /* We just finished a strip, send dummy data to signal the end + of the strip so that we can output it. + */ + else + { + Output_TriEx(-1,-2,-3,output,-1,-10,1); + Last_Edge(&dummy,&dummy,&dummy,1); + } + } +} + + + +void SGI_Strip(int num_verts,int num_faces,FILE *output, + int ties,int triangulate) + +{ + FILE *strip; + int next_id = -1,t=0; + + strip = fopen("output.d","w"); + /* We are going to output and find triangle strips + according the the method that SGI uses, ie always + choosing as the next triangle in our strip the triangle + that has the least number of adjacencies. We do not have + all triangles and will be triangulating on the fly those + polygons that have more than 3 sides. + */ + + /* Build a table that has all the polygons sorted by the number + of polygons adjacent to it. + */ + /* Initialize it */ + Init_Table_SGI(); + /* Build it */ + Build_SGI_Table(num_verts,num_faces); + + /* We will have a structure to hold all the strips, until + outputted. + */ + InitStripTable(); + /* Now we have the structure built to find the polygons according + to the number of adjacencies. Now use the SGI Method to find + strips according to the adjacencies + */ + Find_StripsEx(output,strip,&t,ties,triangulate,ON,&next_id); + +} diff --git a/Tools/Stripe_u/local.h b/Tools/Stripe_u/local.h new file mode 100644 index 000000000..34769ebb8 --- /dev/null +++ b/Tools/Stripe_u/local.h @@ -0,0 +1,19 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE:local.h +-----------------------------------------------------------------------*/ + +void Local_Polygon_Output(); +void Local_Output_Tri(); +int Different(); +void Local_Non_Blind_Triangulate(); +void Local_Blind_Triangulate(); +void Local_Triangulate_Polygon(); +void SGI_Strip(); diff --git a/Tools/Stripe_u/my_global.h b/Tools/Stripe_u/my_global.h new file mode 100644 index 000000000..629c12074 --- /dev/null +++ b/Tools/Stripe_u/my_global.h @@ -0,0 +1,3 @@ +int change_in_stripEx = 0; +int change_in_strip = 0; + diff --git a/Tools/Stripe_u/newpolve.c b/Tools/Stripe_u/newpolve.c new file mode 100644 index 000000000..9adbfd651 --- /dev/null +++ b/Tools/Stripe_u/newpolve.c @@ -0,0 +1,1667 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: newpolve.c + This routine contains the bulk of the code that will find the + patches of quads in the data model +*/ +/*---------------------------------------------------------------------*/ + +#include <stdlib.h> +#include "polverts.h" +#include "extend.h" +#include "output.h" +#include "triangulate.h" +#include "common.h" +#include "util.h" +#include "global.h" +#include "init.h" +#include "add.h" + +ListHead **PolVerts; +ListHead **PolFaces; +ListHead **PolEdges; +int length; +BOOL resetting = FALSE; +int ids[MAX1]; +int added_quad = 0; +BOOL reversed = FALSE; +int patch = 0; +int *vn; +int *vt; + +int Calculate_Walks(int lastvert,int y, PF_FACES temp2) +{ + /* Find the length of the walk */ + + int previous_edge1, previous_edge2; + register int nextvert,numverts,counter,walk=0; + BOOL flag; + F_EDGES *node; + ListHead *pListHead; + static int seen = 0; + + /* Find the edge that we are currently on */ + if (y != 3) + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon + y + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon); + } + + temp2->seen = seen; + counter = y; + + /*Find the adjacent face to this edge */ + node = *(temp2->VertandId+y); + if (node->edge[2] != lastvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + /* Keep walking in this direction until we cannot do so */ + while ((nextvert != lastvert) && (nextvert != -1)) + { + walk++; + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp2->nPolSize; + if ((numverts != 4) || (temp2->seen == seen)) + { + walk--; + nextvert = -1; + } + else + { + temp2->seen = seen; + /* Find edge that is not adjacent to the previous one */ + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon + counter + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + + node = *(temp2->VertandId + counter); + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + } + } + seen++; + return walk; +} + + +BOOL Check_Right(int last_seen,PF_FACES temp2,int y,int face_id) +{ + /* Check when we last saw the face to the right of the current + one. We want to have seen it just before we started this strip + */ + + F_EDGES *node; + ListHead *pListHead; + register int nextvert,oldy; + PF_FACES t; + + oldy = y; + if (y != 3) + y = y+1; + else + y = 0; + node = *(temp2->VertandId + y); + if (face_id == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + if (nextvert == -1) + return FALSE; + + pListHead = PolFaces[nextvert]; + t = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + if (t->seen != (last_seen - 1)) + { + /* maybe because of the numbering, we are not + on the right orientation, so we have to check the + opposite one to be sure + */ + if (oldy != 0) + y = oldy-1; + else + y = 3; + node = *(temp2->VertandId + y); + if (face_id == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + if (nextvert == -1) + return FALSE; + pListHead = PolFaces[nextvert]; + t = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + if (t->seen != (last_seen - 1)) + return FALSE; + } + return TRUE; +} + + +int Update_and_Test(PF_FACES temp2,int y,BOOL first,int distance,int lastvert, int val) +{ + + static int last_seen = 17; + int previous_edge1, previous_edge2; + register int original_distance,nextvert,numverts,counter; + BOOL flag; + F_EDGES *node; + ListHead *pListHead; + + original_distance = distance; + /* Find the edge that we are currently on */ + if (y != 3) + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon + y + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon); + } + + temp2->seen = val; + temp2->seen2 = val; + + node = *(temp2->VertandId+y); + if (lastvert != node->edge[2]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + /* Keep walking in this direction until we cannot do so or + we go to distance */ + while ((distance > 0) && (nextvert != lastvert) && (nextvert != -1)) + { + distance--; + + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + temp2->seen = val; + + if (temp2->seen2 == val) + { + last_seen++; + return (original_distance - distance); + } + + temp2->seen2 = val; + + numverts = temp2->nPolSize; + + if (numverts != 4) + nextvert = -1; + + else if ((!first) && (!(Check_Right(last_seen,temp2,y,nextvert)))) + { + last_seen++; + return (original_distance - distance); + } + else + { + /* Find edge that is not adjacent to the previous one */ + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon + counter + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + if ( ((*(temp2->walked+counter) == -1) && + (*(temp2->walked+counter+2) == -1))) + { + printf("There is an error in the walks!\n"); + printf("1Code %d %d \n",*(temp2->walked+counter),*(temp2->walked+counter+2)); + exit(0); + } + else + { + if ((*(temp2->walked+counter) == -1) && + (*(temp2->walked+counter-2) == -1)) + { + printf("There is an error in the walks!\n"); + printf("2Code %d %d \n",*(temp2->walked+counter),*(temp2->walked+counter-2)); + exit(0); + } + } + node = *(temp2->VertandId + counter); + y = counter; + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + } + } + + last_seen++; + + if (distance != 0) + { + if (((nextvert == -1) || (nextvert == lastvert)) && (distance != 1)) + return (original_distance - distance); + } + return original_distance; +} + + +int Test_Adj(PF_FACES temp2,int x,int north,int distance,int lastvert, int value) +{ + /* if first time, then just update the last seen field */ + if (x==1) + return(Update_and_Test(temp2,north,TRUE,distance,lastvert,value)); + /* else we have to check if we are adjacent to the last strip */ + else + return(Update_and_Test(temp2,north,FALSE,distance,lastvert,value)); +} + +void Get_Band_Walk(PF_FACES temp2,int face_id,int *dir1,int *dir2, + int orientation,int cutoff_length) +{ + int previous_edge1, previous_edge2; + F_EDGES *node; + ListHead *pListHead; + register int walk = 0, nextvert,numverts,counter; + BOOL flag; + + /* Get the largest band that will include this face, starting + from orientation. Save the values of the largest band + (either north and south together, or east and west together) + in the direction variables. + */ + /* Find the edge that we are currently on */ + if (orientation != 3) + { + previous_edge1 = *(temp2->pPolygon + orientation); + previous_edge2 = *(temp2->pPolygon + orientation + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon + orientation ); + previous_edge2 = *(temp2->pPolygon); + } + + if (orientation == 0) + { + if (*dir1 > *(temp2->walked + 1)) + *dir1 = *(temp2->walked + 1); + if (*dir2 > *(temp2->walked + 3)) + *dir2 = *(temp2->walked + 3); + } + else if (orientation == 3) + { + if (*dir1 > *(temp2->walked + orientation - 3)) + *dir1 = *(temp2->walked + orientation - 3) ; + if (*dir2 > *(temp2->walked + orientation -1 )) + *dir2 = *(temp2->walked + orientation - 1); + } + else + { + if (*dir1 > *(temp2->walked + orientation - 1)) + *dir1 = *(temp2->walked + orientation -1) ; + if (*dir2 > *(temp2->walked+ orientation + 1)) + *dir2 = *(temp2->walked + orientation + 1); + } + + /* if we know already that we can't extend the + band from this face, we do not need to do the walk + */ + if ((*dir1 != 0) && (*dir2 != 0)) + { + /* Find the adjacent face to this edge */ + node = *(temp2->VertandId+orientation); + if (face_id == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + } + else + nextvert = -1; /* leave w/o walking */ + + /* Keep walking in this direction until we cannot do so */ + while ((nextvert != face_id) && (nextvert != -1)) + { + walk++; + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp2->nPolSize; + if ((numverts != 4) || (walk > cutoff_length)) + nextvert = -1; + else + { + /* Find edge that is not adjacent to the previous one */ + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon + counter + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + + /* find out how far we can extend in the 2 directions + along this new face in the walk + */ + if (counter == 0) + { + if (*dir1 > *(temp2->walked + 1)) + *dir1 = *(temp2->walked + 1); + if (*dir2 > *(temp2->walked + 3)) + *dir2 = *(temp2->walked + 3); + } + else if (counter == 3) + { + if (*dir1 > *(temp2->walked + counter - 3)) + *dir1 = *(temp2->walked + counter - 3) ; + if (*dir2 > *(temp2->walked + counter -1 )) + *dir2 = *(temp2->walked + counter -1); + } + else + { + if (*dir1 > *(temp2->walked + counter - 1)) + *dir1 = *(temp2->walked + counter -1) ; + if (*dir2 > *(temp2->walked + counter + 1)) + *dir2 = *(temp2->walked + counter + 1); + } + + /* if we know already that we can't extend the + band from this face, we do not need to do the walk + */ + if ((*dir1 == 0) || (*dir2 == 0)) + nextvert = -1; + if (nextvert != -1) + { + node = *(temp2->VertandId + counter); + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + } + + } + } +} + + + + +int Find_Max(PF_FACES temp2,int lastvert,int north,int left, + int *lastminup,int *lastminleft) +{ + int temp,walk,counter,minup,x,band_value; + int previous_edge1, previous_edge2; + F_EDGES *node; + ListHead *pListHead; + BOOL flag; + static int last_seen = 0; + register int t,smallest_so_far,nextvert,max=-1; + + t= lastvert; + *lastminup = MAX_BAND; + *lastminleft = 1; + + if (left == 3) + { + previous_edge1 = *(temp2->pPolygon + left); + previous_edge2 = *(temp2->pPolygon); + } + + else + { + previous_edge1 = *(temp2->pPolygon + left + 1); + previous_edge2 = *(temp2->pPolygon + left); + } + + temp2->seen = last_seen; + walk = *(temp2->walked + left); + + for (x=1;x<=(walk+1); x++) + { + /* test to see if we have a true band + that is, are they adjacent to each other + */ + + minup = *(temp2->walked + north) + 1; + + /* if we are at the very first face, then we do not + have to check the adjacent faces going up + and our north distance is the distance of this face's + north direction. + */ + if (x == 1) + { + *lastminup = minup; + minup = Test_Adj(temp2,x,north,*lastminup,lastvert,last_seen); + *lastminup = minup; + smallest_so_far = minup; + } + + + /* find the largest band that we can have */ + if (minup < (*lastminup)) + { + /* see if we really can go up all the way + temp should by less than our equal to minup + if it is less, then one of the faces was not + adjacent to those next to it and the band height + will be smaller + */ + temp = Test_Adj(temp2,x,north,minup,lastvert,last_seen); + if (temp > minup) + { + printf("There is an error in the test adj\n"); + exit(0); + } + minup = temp; + band_value = x * minup; + if (minup < smallest_so_far) + { + if (band_value > max) + { + smallest_so_far = minup; + *lastminup = minup; + *lastminleft = x; + max = band_value; + } + else + smallest_so_far = minup; + } + else + { + band_value = x * smallest_so_far; + if (band_value > max) + { + *lastminup = smallest_so_far; + *lastminleft = x; + max = band_value; + } + } + } + else + { + if (x != 1) + { + temp = Test_Adj(temp2,x,north,smallest_so_far,lastvert,last_seen); + if (temp > smallest_so_far) + { + printf("There is an error in the test adj\n"); + exit(0); + } + smallest_so_far = temp; + } + band_value = x * smallest_so_far; + if (band_value > max) + { + *lastminup = smallest_so_far; + *lastminleft = x; + max = band_value; + } + } + if ( x != (walk + 1)) + { + node = *(temp2->VertandId+left); + if (lastvert == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + lastvert = nextvert; + + if (nextvert == -1) + return max; + + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead, LISTHEAD, 0); + + /* if we have visited this face before, then there is an error */ + if (((*(temp2->walked) == -1) && (*(temp2->walked+1) == -1) && + (*(temp2->walked+2) == -1) && (*(temp2->walked+3) == -1)) + || (temp2->nPolSize !=4) || (temp2->seen == last_seen)) + { + + if (lastvert == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + if (nextvert == -1) + return max; + lastvert = nextvert; + /* Last attempt to get the face ... */ + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead, LISTHEAD, 0); + if (((*(temp2->walked) == -1) && (*(temp2->walked+1) == -1) && + (*(temp2->walked+2) == -1) && (*(temp2->walked+3) == -1)) + || (temp2->nPolSize !=4) || (temp2->seen == last_seen)) + return max; /* The polygon was not saved with the edge, not + enough room. We will get the walk when we come + to that polygon later. + */ + } + else + { + counter = 0; + flag = TRUE; + temp2->seen = last_seen; + + while ((counter < 3) && (flag)) + { + + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + } + + /* Get the IDs of the next edge */ + left = counter; + north = left+1; + if (left ==3) + north = 0; + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter + 1); + previous_edge2 = *(temp2->pPolygon + counter); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + + } + +} +last_seen++; +return max; +} + +void Mark_Face(PF_FACES temp2, int color1, int color2, + int color3, FILE *output_file, BOOL end, int *edge1, int *edge2, + int *face_id, int norms, int texture) +{ + static int last_quad[4]; + register int x,y,z=0; + int saved[2]; + static int output1, output2,last_id; + BOOL cptexture; + + /* Are we done with the patch? If so return the last edge that + we will come out on, and that will be the edge that we will + start to extend upon. + */ + + cptexture = texture; + if (end) + { + *edge1 = output1; + *edge2 = output2; + *face_id = last_id; + return; + } + + last_id = *face_id; + *(temp2->walked) = -1; + *(temp2->walked+1) = -1; + *(temp2->walked+2) = -1; + *(temp2->walked+3) = -1; + added_quad++; + temp2->nPolSize = 1; + + if (patch == 0) + { + /* At the first quad in the strip -- save it */ + last_quad[0] = *(temp2->pPolygon); + last_quad[1] = *(temp2->pPolygon+1); + last_quad[2] = *(temp2->pPolygon+2); + last_quad[3] = *(temp2->pPolygon+3); + patch++; + } + else + { + /* Now we have a triangle to output, find the edge in common */ + for (x=0; x < 4 ;x++) + { + for (y=0; y< 4; y++) + { + if (last_quad[x] == *(temp2->pPolygon+y)) + { + saved[z++] = last_quad[x]; + if (z > 2) + { + /* This means that there was a non convex or + an overlapping polygon + */ + z--; + break; + } + } + } + } + + if (z != 2) + { + printf("Z is not 2 %d \n",patch); + printf("4 %d %d %d %d %d %d %d\n",*(temp2->pPolygon), + *(temp2->pPolygon+1),*(temp2->pPolygon+2),*(temp2->pPolygon+3), + color1,color2,color3); + printf("%d %d %d %d\n",last_quad[0],last_quad[1],last_quad[2],last_quad[3]); + exit(1); + } + + if (patch == 1) + { + /* First one to output, there was no output edge */ + patch++; + x = Adjacent(saved[0],saved[1],last_quad,4); + y = Adjacent(saved[1],saved[0],last_quad,4); + + /* Data might be mixed and we do not have textures for some of the vertices */ + if ((texture) && ( ((vt[x]) == 0) || ((vt[y])==0) || ((vt[saved[1]])==0))) + cptexture = FALSE; + + if ((!norms) && (!cptexture)) + { + fprintf(output_file,"\nt %d %d %d ",x+1,y+1,saved[1]+1); + fprintf(output_file,"%d ",saved[0]+1); + } + else if ((norms) && (!cptexture)) + { + fprintf(output_file,"\nt %d//%d %d//%d %d//%d ",x+1,vn[x] +1, + y+1,vn[y] +1, + saved[1]+1,vn[saved[1]]+1); + fprintf(output_file,"%d//%d ",saved[0]+1,vn[saved[0]]+1); + } + else if ((cptexture) && (!norms)) + { + fprintf(output_file,"\nt %d/%d %d/%d %d/%d ",x+1,vt[x] +1, + y+1,vt[y] +1, + saved[1]+1,vt[saved[1]]+1); + fprintf(output_file,"%d//%d ",saved[0]+1,vt[saved[0]]+1); + } + else + { + fprintf(output_file,"\nt %d/%d/%d %d/%d/%d %d/%d/%d ",x+1,vt[x]+1,vn[x] +1, + y+1,vt[y]+1,vn[y] +1, + saved[1]+1,vt[saved[1]]+1,vn[saved[1]]+1); + fprintf(output_file,"%d/%d/%d ",saved[0]+1,vt[saved[0]]+1,vn[saved[0]]+1); + } + + x = Adjacent(saved[0],saved[1],temp2->pPolygon,4); + y = Adjacent(saved[1],saved[0],temp2->pPolygon,4); + + /* Data might be mixed and we do not have textures for some of the vertices */ + if ((texture) && ( (vt[x] == 0) || (vt[y]==0))) + { + if (cptexture) + fprintf(output_file,"\nq "); + cptexture = FALSE; + } + if ((!norms) && (!cptexture)) + { + fprintf(output_file,"%d ",x+1); + fprintf(output_file,"%d ",y+1); + } + else if ((norms) && (!cptexture)) + { + fprintf(output_file,"%d//%d ",x+1,vn[x]+1); + fprintf(output_file,"%d//%d ",y+1,vn[y]+1); + } + else if ((cptexture) && (!norms)) + { + fprintf(output_file,"%d/%d ",x+1,vt[x]+1); + fprintf(output_file,"%d/%d ",y+1,vt[y]+1); + } + else + { + fprintf(output_file,"%d/%d/%d ",x+1,vt[x]+1,vn[x]+1); + fprintf(output_file,"%d/%d/%d ",y+1,vt[y]+1,vn[y]+1); + } + + output1 = x; + output2 = y; + } + + else + { + x = Adjacent(output2,output1,temp2->pPolygon,4); + y = Adjacent(output1,output2,temp2->pPolygon,4); + /* Data might be mixed and we do not have textures for some of the vertices */ + if ((texture) && ( ((vt[x]) == 0) || ((vt[y])==0) )) + texture = FALSE; + + if ((!norms) && (!texture)) + { + fprintf(output_file,"\nq %d ",x+1); + fprintf(output_file,"%d ",y+1); + } + else if ((norms) && (!texture)) + { + fprintf(output_file,"\nq %d//%d ",x+1,vn[x]+1); + fprintf(output_file,"%d//%d ",y+1,vn[y]+1); + } + else if ((texture) && (!norms)) + { + fprintf(output_file,"\nq %d/%d ",x+1,vt[x]+1); + fprintf(output_file,"%d/%d ",y+1,vt[y]+1); + } + else + { + fprintf(output_file,"\nq %d/%d/%d ",x+1,vt[x]+1,vn[x]+1); + fprintf(output_file,"%d/%d/%d ",y+1,vt[y]+1,vn[y]+1); + } + + output1 = x; + output2 = y; + } + + last_quad[0] = *(temp2->pPolygon); + last_quad[1] = *(temp2->pPolygon+1); + last_quad[2] = *(temp2->pPolygon+2); + last_quad[3] = *(temp2->pPolygon+3); + } +} + +void Fast_Reset(int x) +{ + register int y,numverts; + register int front_walk, back_walk; + ListHead *pListHead; + PF_FACES temp = NULL; + + pListHead = PolFaces[x]; + temp = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp->nPolSize; + + front_walk = 0; + back_walk = 0; + resetting = TRUE; + + /* we are doing this only for quads */ + if (numverts == 4) + { + /* for each face not seen yet, do North and South together + and East and West together + */ + for (y=0;y<2;y++) + { + /* Check if the opposite sides were seen already */ + /* Find walk for the first edge */ + front_walk = Calculate_Walks(x,y,temp); + /* Find walk in the opposite direction */ + back_walk = Calculate_Walks(x,y+2,temp); + /* Now put into the data structure the numbers that + we have found + */ + Assign_Walk(x,temp,front_walk,y,back_walk); + Assign_Walk(x,temp,back_walk,y+2,front_walk); + } + } + resetting = FALSE; +} + + +void Reset_Max(PF_FACES temp2,int face_id,int north,int last_north, int orientation, + int last_left,FILE *output_file,int color1,int color2,int color3, + BOOL start) +{ + register int walk = 0,count = 0; + int previous_edge1,previous_edge2; + int static last_seen = 1000; + F_EDGES *node; + ListHead *pListHead; + int f,t,nextvert,counter; + BOOL flag; + + + /* Reset walks on faces, since we just found a patch */ + if (orientation !=3) + { + previous_edge1 = *(temp2->pPolygon + orientation+1); + previous_edge2 = *(temp2->pPolygon + orientation ); + } + else + { + previous_edge1 = *(temp2->pPolygon + orientation ); + previous_edge2 = *(temp2->pPolygon); + } + + /* only if we are going left, otherwise there will be -1 there */ + /*Find the adjacent face to this edge */ + + for (t = 0; t <=3 ; t++) + { + node = *(temp2->VertandId+t); + + if (face_id == node->edge[1]) + f = node->edge[2]; + else + f = node->edge[1]; + + if (f != -1) + Fast_Reset(f); + } + + node = *(temp2->VertandId+orientation); + if (face_id == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + while ((last_left--) > 1) + { + + if (start) + Reset_Max(temp2,face_id,orientation,last_left,north,last_north,output_file,color1,color2,color3,FALSE); + + face_id = nextvert; + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + if ((temp2->nPolSize != 4) && (temp2->nPolSize != 1)) + { + /* There is more than 2 polygons on the edge, and we could have + gotten the wrong one + */ + if (nextvert != node->edge[1]) + nextvert = node->edge[1]; + else + nextvert = node->edge[2]; + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + node = *(temp2->VertandId+orientation); + } + + + if (!start) + { + for (t = 0; t <=3 ; t++) + { + node = *(temp2->VertandId+t); + + if (face_id == node->edge[1]) + f = node->edge[2]; + else + f = node->edge[1]; + + if (f != -1) + Fast_Reset(f); + } + } + + + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter+1); + previous_edge2 = *(temp2->pPolygon + counter); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + orientation = counter; + + node = *(temp2->VertandId + counter); + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + if (!reversed) + { + if (counter != 3) + north = counter +1; + else + north = 0; + } + else + { + if (counter != 0) + north = counter -1; + else + north = 3; + + } + } +if (start) + Reset_Max(temp2,face_id,orientation,last_left,north,last_north,output_file,color1,color2,color3,FALSE); +else if (nextvert != -1) + Fast_Reset(nextvert); + +} + + +int Peel_Max(PF_FACES temp2,int face_id,int north,int last_north, int orientation, + int last_left,FILE *output_file,int color1,int color2,int color3, + BOOL start, int *swaps_added, int norms, int texture) +{ + int end1,end2,last_id,s=0,walk = 0,count = 0; + int previous_edge1,previous_edge2; + int static last_seen = 1000; + F_EDGES *node; + ListHead *pListHead; + int nextvert,numverts,counter,dummy,tris=0; + BOOL flag; + + /* Peel the patch from the model. + We will try and extend off the end of each strip in the patch. We will return the + number of triangles completed by this extension only, and the number of swaps + in the extension only. + */ + patch = 0; + + if (orientation !=3) + { + previous_edge1 = *(temp2->pPolygon + orientation+1); + previous_edge2 = *(temp2->pPolygon + orientation ); + } + else + { + previous_edge1 = *(temp2->pPolygon + orientation ); + previous_edge2 = *(temp2->pPolygon); + } + + + walk = *(temp2->walked + orientation); + + /* only if we are going left, otherwise there will be -1 there */ + if ((start) && ((walk+1) < last_left)) + { + printf("There is an error in the left %d %d\n",walk,last_left); + exit(0); + } + + /* Find the adjacent face to this edge */ + node = *(temp2->VertandId+orientation); + if (face_id == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + temp2->seen = last_seen; + + + while ((last_left--) > 1) + { + if (start) + tris += Peel_Max(temp2,face_id,orientation,last_left,north,last_north,output_file, + color1,color2,color3,FALSE,swaps_added,norms,texture); + else + Mark_Face(temp2,color1,color2,color3,output_file,FALSE,&dummy,&dummy,&face_id,norms,texture); + + + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp2->nPolSize; + + if ((numverts != 4) || (temp2->seen == last_seen) + || (nextvert == -1)) + { + + /* There is more than 2 polygons on the edge, and we could have + gotten the wrong one + */ + if (nextvert != node->edge[1]) + nextvert = node->edge[1]; + else + nextvert = node->edge[2]; + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp2->nPolSize; + if ((numverts != 4) || (temp2->seen == last_seen) ) + { + printf("Peel 2 %d\n",numverts); + exit(1); + } + } + + face_id = nextvert; + temp2->seen = last_seen; + + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter+1); + previous_edge2 = *(temp2->pPolygon + counter); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + orientation = counter; + + node = *(temp2->VertandId + counter); + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + if (!reversed) + { + if (counter != 3) + north = counter +1; + else + north = 0; + } + else + { + if (counter != 0) + north = counter -1; + else + north = 3; + } +} + +if (start) + tris += Peel_Max(temp2,face_id,orientation,last_left,north,last_north,output_file, + color1,color2,color3,FALSE,swaps_added,norms,texture); +else + Mark_Face(temp2,color1,color2,color3,output_file,FALSE,&dummy,&dummy,&face_id,norms,texture);/* do the last face */ + +last_seen++; + +/* Get the edge that we came out on the last strip of the patch */ +Mark_Face(NULL,0,0,0,output_file,TRUE,&end1,&end2,&last_id,norms,texture); +tris += Extend_Face(last_id,end1,end2,&s,output_file,color1,color2,color3,vn,norms,vt,texture); +*swaps_added = *swaps_added + s; +return tris; +} + + + +void Find_Bands(int numfaces, FILE *output_file, int *swaps, int *bands, + int *cost, int *tri, int norms, int *vert_norms, int texture, int *vert_texture) +{ + + register int x,y,max1,max2,numverts,face_id,flag,maximum = 25; + ListHead *pListHead; + PF_FACES temp = NULL; + int color1 = 0, color2 = 100, color3 = 255; + int color = 0,larger,smaller; + int north_length1,last_north,left_length1,last_left,north_length2,left_length2; + int total_tri = 0, total_swaps = 0,last_id; + int end1, end2,s=0; + register int cutoff = 20; + + /* Code that will find the patches. "Cutoff" will be + the cutoff of the area of the patches that we will be allowing. After + we reach this cutoff length, then we will run the local algorithm on the + remaining faces. + */ + + /* For each faces that is left find the largest possible band that we can + have with the remaining faces. Note that we will only be finding patches + consisting of quads. + */ + +vn = vert_norms; +vt = vert_texture; +y=1; +*bands = 0; + +while ((maximum >= cutoff)) +{ + y++; + maximum = -1; + for (x=0; x<numfaces; x++) + { + + /* Used to produce the triangle strips */ + + /* for each face, get the face */ + pListHead = PolFaces[x]; + temp = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp->nPolSize; + + /* we are doing this only for quads */ + if (numverts == 4) + { + /* We want a face that is has not been used yet, + since we know that that face must be part of + a band. Then we will find the largest band that + the face may be contained in + */ + + /* Doing the north and the left */ + if ((*(temp->walked) != -1) && (*(temp->walked+3) != -1)) + max1 = Find_Max(temp,x,0,3,&north_length1,&left_length1); + if ((*(temp->walked+1) != -1) && (*(temp->walked+2) != -1)) + max2 = Find_Max(temp,x,2,1,&north_length2,&left_length2); + if ((max1 != (north_length1 * left_length1)) || + (max2 != (north_length2 * left_length2))) + { + printf("Max1 %d, %d %d Max2 %d, %d %d\n",max1,north_length1,left_length1,max2,north_length2,left_length2); + exit(0); + } + + + if ((max1 > max2) && (max1 > maximum)) + { + maximum = max1; + face_id = x; + flag = 1; + last_north = north_length1; + last_left = left_length1; + /* so we know we saved max1 */ + } + else if ((max2 > maximum) ) + { + maximum = max2; + face_id = x; + flag = 2; + last_north = north_length2; + last_left = left_length2; + /* so we know we saved max2 */ + } + } + } + if ((maximum < cutoff) && (*bands == 0)) + return; + pListHead = PolFaces[face_id]; + temp = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + /* There are no patches that we found in this pass */ + if (maximum == -1) + break; + /*printf("The maximum is face %d area %d: lengths %d %d\n",face_id,maximum,last_north,last_left);*/ + + if (last_north > last_left) + { + larger = last_north; + smaller = last_left; + } + else + { + larger = last_left; + smaller = last_north; + } + + length = larger; + +if (flag == 1) +{ + if (last_north > last_left) /* go north sequentially */ + { + total_tri += Peel_Max(temp,face_id,0,last_north,3,last_left,output_file,color1,color2,color3,TRUE,&s,norms,texture); + Reset_Max(temp,face_id,0,last_north,3,last_left,output_file,color1,color2,color3,TRUE); + total_swaps += s; + } + else + { + reversed = TRUE; + total_tri += Peel_Max(temp,face_id,3,last_left,0,last_north,output_file,color1,color2,color3,TRUE,&s,norms,texture); + Reset_Max(temp,face_id,3,last_left,0,last_north,output_file,color1,color2,color3,TRUE); + reversed = FALSE; + total_swaps += s; + } + + + /* Get the edge that we came out on the last strip of the patch */ + Mark_Face(NULL,0,0,0,NULL,TRUE,&end1,&end2,&last_id,norms,texture); + total_tri += Extend_Face(last_id,end1,end2,&s,output_file,color1,color2,color3,vn,norms,vt,texture); + total_swaps += s; + +} +else +{ + if (last_north > last_left) + { + total_tri += Peel_Max(temp,face_id,2,last_north,1,last_left,output_file,color1,color2,color3,TRUE,&s,norms,texture); + Reset_Max(temp,face_id,2,last_north,1,last_left,output_file,color1,color2,color3,TRUE); + total_swaps += s; + } + else + { + reversed = TRUE; + total_tri += Peel_Max(temp,face_id,1,last_left,2,last_north,output_file,color1,color2,color3,TRUE,&s,norms,texture); + Reset_Max(temp,face_id,1,last_left,2,last_north,output_file,color1,color2,color3,TRUE); + reversed = FALSE; + total_swaps += s; + } + + /* Get the edge that we came out on on the patch */ + Mark_Face(NULL,0,0,0,NULL,TRUE,&end1,&end2,&last_id,norms,texture); + total_tri += Extend_Face(last_id,end1,end2,&s,output_file,color1,color2,color3,vn,norms,vt,texture); + total_swaps += s; +} + + /* Now compute the cost of transmitting this band, is equal to + going across the larger portion sequentially, + and swapping 3 times per other dimension + */ + +total_tri += (maximum * 2); +*bands = *bands + smaller; + +} + +/*printf("We transmitted %d triangles,using %d swaps and %d strips\n",total_tri, + total_swaps, *bands); +printf("COST %d\n",total_tri + total_swaps + *bands + *bands);*/ +*cost = total_tri + total_swaps + *bands + *bands; +*tri = total_tri; +added_quad = added_quad * 4; +*swaps = total_swaps; +} + + +void Save_Rest(int *numfaces) +{ + /* Put the polygons that are left into a data structure so that we can run the + stripping code on it. + */ + register int x,y=0,numverts; + ListHead *pListHead; + PF_FACES temp=NULL; + + for (x=0; x<*numfaces; x++) + { + /* for each face, get the face */ + pListHead = PolFaces[x]; + temp = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp->nPolSize; + /* If we did not do the face before add it to data structure with new + face id number + */ + if (numverts != 1) + { + CopyFace(temp->pPolygon,numverts,y+1,temp->pNorms); + y++; + } + /* Used it, so remove it */ + else + RemoveList(pListHead,(PLISTINFO) temp); + + } + *numfaces = y; +} + +void Assign_Walk(int lastvert,PF_FACES temp2, int front_walk,int y, + int back_walk) +{ +/* Go back and do the walk again, but this time save the lengths inside + the data structure. + y was the starting edge number for the front_walk length + back_walk is the length of the walk along the opposite edge + */ + int previous_edge1, previous_edge2; + register int walk = 0,nextvert,numverts,counter; + BOOL flag; + F_EDGES *node; + ListHead *pListHead; + register int total_walk, start_back_walk; + static int seen = 0; + static BOOL first = TRUE; + int test; + BOOL f = TRUE, wrap = FALSE, set = FALSE; + test = lastvert; + + /* In the "Fast_Reset" resetting will be true */ + if ((resetting) && (first)) + { + seen = 0; + first = FALSE; + } + + seen++; + total_walk = front_walk + back_walk; + start_back_walk = back_walk; + /* Had a band who could be a cycle */ + if (front_walk == back_walk) + wrap = TRUE; + + /* Find the edge that we are currently on */ + if (y != 3) + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon + y + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon); + } + + /* Assign the lengths */ + if (y < 2) + { + *(temp2->walked+y) = front_walk--; + *(temp2->walked+y+2) = back_walk++; + } + else + { + *(temp2->walked+y) = front_walk--; + *(temp2->walked+y-2) = back_walk++; + } + + /*Find the adjacent face to this edge */ + node = *(temp2->VertandId+y); + + if (node->edge[2] != lastvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + temp2->seen3 = seen; + + /* Keep walking in this direction until we cannot do so */ + while ((nextvert != lastvert) && (nextvert != -1) && (front_walk >= 0)) + { + walk++; + pListHead = PolFaces[nextvert]; + + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp2->nPolSize; + if ((numverts != 4)) + { + nextvert = -1; + /* Don't include this face in the walk */ + walk--; + } + else + { + /* Find edge that is not adjacent to the previous one */ + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon + counter + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + + + /* Put in the walk lengths */ + if (counter < 2) + { + if (((*(temp2->walked + counter) >= 0) + || (*(temp2->walked +counter + 2) >= 0))) + { + if ((resetting == FALSE) && ((temp2->seen3) != (seen-1))) + { + /* If there are more than 2 polygons adjacent + to an edge then we can be trying to assign more than + once. We will save the smaller one + */ + temp2->seen3 = seen; + if ( (*(temp2->walked+counter) <= front_walk) && + (*(temp2->walked+counter+2) <= back_walk) ) + return; + if (*(temp2->walked+counter) > front_walk) + *(temp2->walked+counter) = front_walk--; + else + front_walk--; + if (*(temp2->walked+counter+2) > back_walk) + *(temp2->walked+counter+2) = back_walk++; + else + back_walk++; + } + else if (resetting == FALSE) + { + /* if there was a cycle then all lengths are the same */ + walk--; + back_walk--; + front_walk++; + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter+2) = back_walk++; + } + else if (((temp2->seen3 == (seen-1)) + && (wrap) && (walk == 1)) || (set)) + { + /* if there was a cycle then all lengths are the same */ + set = TRUE; + walk--; + back_walk--; + front_walk++; + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter+2) = back_walk++; + } + else + { + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter+2) = back_walk++; + } + } /* if was > 0 */ + else + { + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter+2) = back_walk++; + } + } + + else + { + if (((*(temp2->walked + counter) >= 0 ) + || (*(temp2->walked +counter - 2) >= 0)) ) + { + if ((temp2->seen3 != (seen-1)) && (resetting == FALSE)) + { + /* If there are more than 2 polygons adjacent + to an edge then we can be trying to assign more than + once. We will save the smaller one + */ + temp2->seen3 = seen; + if ( (*(temp2->walked+counter) <= front_walk) && + (*(temp2->walked+counter-2) <= back_walk) ) + return; + if (*(temp2->walked+counter) > front_walk) + *(temp2->walked+counter) = front_walk--; + else + front_walk--; + if (*(temp2->walked+counter-2) > back_walk) + *(temp2->walked+counter-2) = back_walk++; + else + back_walk++; + } + else if (resetting == FALSE) + { + walk--; + back_walk--; + front_walk++; + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter-2) = back_walk++; + } + else if (((temp2->seen3 == (seen-1)) && (walk == 1) && (wrap)) + || (set)) + { + /* if there was a cycle then all lengths are the same */ + set = TRUE; + walk--; + back_walk--; + front_walk++; + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter-2) = back_walk++; + } + else + { + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter-2) = back_walk++; + } + } + else + { + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter-2) = back_walk++; + } + + } + if (nextvert != -1) + { + node = *(temp2->VertandId + counter); + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + } + + } +} +if ((EVEN(seen)) ) + seen+=2; +} + +void Save_Walks(int numfaces) +{ + int x,y,numverts; + int front_walk, back_walk; + ListHead *pListHead; + PF_FACES temp = NULL; + + for (x=0; x<numfaces; x++) + { + /* for each face, get the face */ + pListHead = PolFaces[x]; + temp = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp->nPolSize; + front_walk = 0; + back_walk = 0; + + /* we are finding patches only for quads */ + if (numverts == 4) + { + /* for each face not seen yet, do North and South together + and East and West together + */ + for (y=0;y<2;y++) + { + /* Check if the opposite sides were seen already from another + starting face, if they were then there is no need to do the walk again + */ + + if ( ((*(temp->walked+y) == -1) && + (*(temp->walked+y+2) == -1) )) + { + /* Find walk for the first edge */ + front_walk = Calculate_Walks(x,y,temp); + /* Find walk in the opposite direction */ + back_walk = Calculate_Walks(x,y+2,temp); + /* Now put into the data structure the numbers that + we have found + */ + Assign_Walk(x,temp,front_walk,y,back_walk); + Assign_Walk(x,temp,back_walk,y+2,front_walk); + } + } + } + } +} + + diff --git a/Tools/Stripe_u/options.c b/Tools/Stripe_u/options.c new file mode 100644 index 000000000..7322d937c --- /dev/null +++ b/Tools/Stripe_u/options.c @@ -0,0 +1,181 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: options.c + This file contains routines that are used to determine the options + that were specified by the user +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include "options.h" +#include "global.h" + +int power_10(int power) +{ + /* Raise 10 to the power */ + register int i,p; + + p = 1; + for (i = 1; i <= power; ++i) + p = p * 10; + return p; +} + +float power_negative(int power) +{ + /* Raise 10 to the negative power */ + + register int i; + float p; + + p = (float)1; + for (i = 1; i<=power; i++) + p = p * (float).1; + return p; +} + +float convert_array(int num[],int stack_size) +{ + /* Convert an array of characters to an integer */ + + register int counter,c; + float temp =(float)0.0; + + for (c=(stack_size-1), counter = 0; c>=0; c--, counter++) + { + if (num[c] == -1) + /* We are at the decimal point, convert to decimal + less than 1 + */ + { + counter = -1; + temp = power_negative(stack_size - c - 1) * temp; + } + else + temp += power_10(counter) * num[c]; + } + + return(temp); +} + +float get_options(int argc, char **argv, int *f, int *t, int *tr, int *group) +{ + char c; + int count = 0; + int buffer[MAX1]; + int next = 0; + /* tie variable */ + enum tie_options tie = FIRST; + /* triangulation variable */ + enum triangulation_options triangulate = WHOLE; + /* normal difference variable (in degrees) */ + float norm_difference = (float)360.0; + /* file-type variable */ + enum file_options file_type = ASCII; + + /* User has the wrong number of options */ + if ((argc > 5) || (argc < 2)) + { + printf("Usage: bands -[file_option][ties_option][triangulation_option][normal_difference] file_name\n"); + exit(0); + } + + /* Interpret the options specified */ + while (--argc > 0 && (*++argv)[0] == '-') + { + /* At the next option that was specified */ + next = 1; + while (c = *++argv[0]) + switch (c) + { + case 'f': + /* Use the first polygon we see. */ + tie = FIRST; + break; + + case 'r': + /* Randomly choose the next polygon */ + tie = RANDOM; + break; + + case 'a': + /* Alternate direction in choosing the next polygon */ + tie = ALTERNATE; + break; + + case 'l': + /* Use lookahead to choose the next polygon */ + tie = LOOK; + break; + + case 'q': + /* Try to reduce swaps */ + tie = SEQUENTIAL; + break; + + case 'p': + /* Use partial triangulation of polygons */ + triangulate = PARTIAL; + break; + + case 'w': + /* Use whole triangulation of polygons */ + triangulate = WHOLE; + break; + + case 'b': + /* Input file is in binary */ + file_type = BINARY; + break; + + case 'g': + /* Strips will be grouped according to the groups in + the data file. We will have to restrict strips to be + in the grouping of the data file. + */ + *group = 1; + + /* Get each the value of the integer */ + /* We have an integer */ + default: + if ((c >= '0') && (c <= '9')) + { + /* More than one normal difference specified, use the last one */ + if (next == 1) + { + count = 0; + next = 0; + } + buffer[count++] = ATOI(c); + } + /* At the decimal point */ + else if (c == '.') + { + /* More than one normal difference specified, use the last one */ + if (next == 1) + { + count = 0; + next = 0; + } + buffer[count++] = -1; + } + else + break; + } + } + /* Convert the buffer of characters to a floating pt integer */ + if (count != 0) + norm_difference = convert_array(buffer,count); + *f = file_type; + *t = tie; + *tr = triangulate; + return norm_difference; +} diff --git a/Tools/Stripe_u/options.h b/Tools/Stripe_u/options.h new file mode 100644 index 000000000..055d33ea7 --- /dev/null +++ b/Tools/Stripe_u/options.h @@ -0,0 +1,17 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: options.h +-----------------------------------------------------------------------*/ + +float get_options(); +enum file_options {ASCII,BINARY}; +enum tie_options {FIRST, RANDOM, ALTERNATE, LOOK, SEQUENTIAL}; +enum triangulation_options {PARTIAL,WHOLE}; + diff --git a/Tools/Stripe_u/output.c b/Tools/Stripe_u/output.c new file mode 100644 index 000000000..d6c30f613 --- /dev/null +++ b/Tools/Stripe_u/output.c @@ -0,0 +1,582 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: output.c + This file contains routines that are finding and outputting the + strips from the local algorithm +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include "global.h" +#include "polverts.h" +#include "triangulate.h" +#include "partial.h" +#include "sturcts.h" +#include "ties.h" +#include "options.h" +#include "common.h" +#include "util.h" +#include "free.h" + +int *vn; +int *vt; +int norm; +int text; + +int Finished(int *swap, FILE *output, BOOL global) +{ + /* We have finished all the triangles, now is time to output to + the data file. In the strips data structure, every three ids + is a triangle. Now we see whether we can swap, or make a new strip + or continue the strip, and output the data accordingly to the + data file. + */ + register int start_swap = 0; + int num,x,vertex1,vertex2; + ListHead *pListHead; + int id[2],other1,other2,index = 0,a,b,c; + P_STRIPS temp1,temp2,temp3,temp4,temp5,temp6; + BOOL cptexture; + *swap =0; + + cptexture = text; + pListHead = strips[0]; + if (pListHead == NULL) + return 0; + + num = NumOnList(pListHead); + /*printf ("There are %d triangles in the extend\n",num/3);*/ + + /* Go through the list triangle by triangle */ + temp1 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 0); + temp2 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 1); + temp3 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 2); + + /* Next triangle for lookahead */ + temp4 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 3); + + + /* There is only one polygon in the strip */ + if (temp4 == NULL) + { + /* Data might be mixed and we do not have textures for some of the vertices */ + if ((text) && (vt[temp3->face_id] == 0)) + cptexture = FALSE; + if ((norm) && (!cptexture)) + fprintf(output,"%d//%d %d//%d %d//%d",temp3->face_id+1,vn[temp3->face_id]+1, + temp2->face_id+1,vn[temp2->face_id]+1, + temp1->face_id+1,vn[temp1->face_id]+1); + else if ((cptexture) && (!norm)) + fprintf(output,"%d/%d %d/%d %d/%d",temp3->face_id+1,vt[temp3->face_id]+1, + temp2->face_id+1,vt[temp2->face_id]+1, + temp1->face_id+1,vt[temp1->face_id]+1); + else if ((cptexture)&& (norm)) + fprintf(output,"%d/%d/%d %d/%d/%d %d/%d/%d",temp3->face_id+1,vt[temp3->face_id]+1,vn[temp3->face_id]+1, + temp2->face_id+1,vt[temp2->face_id]+1,vn[temp2->face_id]+1, + temp1->face_id+1,vt[temp1->face_id]+1,vn[temp1->face_id]+1); + else + fprintf(output,"%d %d %d",temp3->face_id+1,temp2->face_id+1,temp1->face_id+1); + Free_Strips(); + return 1; + } + + /* We have a real strip */ + temp5 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 4); + temp6 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 5); + + if ((temp1 == NULL) || (temp2 == NULL) || (temp3 == NULL) || (temp5 == NULL) || (temp6 == NULL)) + { + printf("There is an error in the output of the triangles\n"); + exit(0); + } + + /* Find the vertex in the first triangle that is not in the second */ + vertex1 = Different(temp1->face_id,temp2->face_id,temp3->face_id,temp4->face_id,temp5->face_id,temp6->face_id,&other1,&other2); + /* Find the vertex in the second triangle that is not in the first */ + vertex2 = Different(temp4->face_id,temp5->face_id,temp6->face_id,temp1->face_id,temp2->face_id,temp3->face_id,&other1,&other2); + + /* Lookahead for the correct order of the 2nd and 3rd vertex of the first triangle */ + temp1 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 6); + temp2 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 7); + temp3 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 8); + + if (temp1 != NULL) + other1 = Different(temp3->face_id,temp4->face_id,temp5->face_id,temp1->face_id,temp2->face_id,temp3->face_id,&other1,&a); + + id[index] = vertex1; index = !index; + id[index] = other1; index = !index; + id[index] = other2; index = !index; + + a = temp4->face_id; + b = temp5->face_id; + c = temp6->face_id; + + /* If we need to rearrange the first sequence because otherwise + there would have been a swap. + */ + + if ((temp3 != NULL) && (text) && ( vt[temp3->face_id]==0)) + cptexture = FALSE; + if ((norm) && (!cptexture)) + fprintf(output,"%d//%d %d//%d %d//%d ",vertex1+1,vn[vertex1]+1,other1+1,vn[other1]+1, + other2+1,vn[other2]+1); + else if ((cptexture) && (!norm)) + fprintf(output,"%d/%d %d/%d %d/%d ",vertex1+1,vt[vertex1]+1,other1+1,vt[other1]+1, + other2+1,vt[other2]+1); + else if ((cptexture) && (norm)) + fprintf(output,"%d/%d/%d %d/%d/%d %d/%d/%d ",vertex1+1,vt[vertex1]+1,vn[vertex1]+1, + other1+1,vt[other1]+1,vn[other1]+1, + other2+1,vt[other2]+1,vn[other2]+1); + else + fprintf(output,"%d %d %d ",vertex1+1,other1+1,other2+1); + + + for (x = 6; x < num ; x = x+3) + { + + /* Get the next triangle */ + temp1 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, x); + temp2 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, x+1); + temp3 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, x+2); + + /* Error checking */ + if (!(member(id[0],a,b,c)) || !(member(id[1],a,b,c)) || !(member(vertex2,a,b,c))) + { + /* If we used partial we might have a break in the middle of a strip */ + fprintf(output,"\nt "); + /* Find the vertex in the first triangle that is not in the second */ + vertex1 = Different(a,b,c,temp1->face_id,temp2->face_id,temp3->face_id,&other1,&other2); + /* Find the vertex in the second triangle that is not in the first */ + vertex2 = Different(temp1->face_id,temp2->face_id,temp3->face_id,a,b,c,&other1,&other2); + + id[index] = vertex1; index = !index; + id[index] = other1; index = !index; + id[index] = other2; index = !index; + } + + if ((temp1 == NULL ) || (temp2 == NULL) || (temp3 == NULL)) + { + printf("There is an error in the triangle list \n"); + exit(0); + } + + if ((id[0] == id[1]) || (id[0] == vertex2)) + continue; + + if ((member(id[index],temp1->face_id,temp2->face_id,temp3->face_id))) + { + if ((text) && ( vt[id[index]]==0)) + cptexture = FALSE; + if ((!norm) && (!cptexture)) + fprintf(output,"%d ",id[index]+1); + else if ((norm) && (!cptexture)) + fprintf(output,"%d//%d ",id[index]+1,vn[id[index]]+1); + else if ((!norm) && (cptexture)) + fprintf(output,"%d/%d ",id[index]+1,vt[id[index]]+1); + else + fprintf(output,"%d/%d/%d ",id[index]+1,vt[id[index]]+1,vn[id[index]]+1); + index = !index; + *swap = *swap + 1; + } + + if ((text) && ( vt[vertex2]==0)) + cptexture = FALSE; + if ((!norm) && (!cptexture)) + fprintf(output,"\nq %d ",vertex2+1); + else if ((norm) && (!cptexture)) + fprintf(output,"\nq %d//%d ",vertex2+1,vn[vertex2]+1); + else if ((!norm) && (cptexture)) + fprintf(output,"\nq %d/%d ",vertex2+1,vt[vertex2]+1); + else + fprintf(output,"\nq %d/%d/%d ",vertex2+1,vt[vertex2]+1,vn[vertex2]+1); + + id[index] = vertex2; index = !index; + + /* Get the next vertex not in common */ + vertex2 = Different(temp1->face_id,temp2->face_id,temp3->face_id,a,b,c,&other1,&other2); + a = temp1->face_id; + b = temp2->face_id; + c = temp3->face_id; + } + /* Do the last vertex */ + if ((text) && (vt[vertex2]==0)) + { + if (cptexture) + fprintf(output,"\nq "); + cptexture = FALSE; + } + if ((!norm) && (!cptexture)) + fprintf(output,"%d ",vertex2+1); + else if ((norm) && (!cptexture)) + fprintf(output,"%d//%d ",vertex2+1,vn[vertex2]+1); + else if ((!norm) && (cptexture)) + fprintf(output,"%d/%d ",vertex2+1,vt[vertex2]+1); + else + fprintf(output,"%d/%d/%d ",vertex2+1,vt[vertex2]+1,vn[vertex2]+1); + + + Free_Strips(); + return (num/3); +} + +void Output_Tri(int id1, int id2, int id3,FILE *bands, int color1, int color2, int color3,BOOL end) +{ + /* We will save everything into a list, rather than output at once, + as was done in the old routine. This way for future modifications + we can change the strips later on if we want to. + */ + + int temp1,temp2,temp3; + + /* Make sure we do not have an error */ + /* There are degeneracies in some of the files */ + if ( (id1 == id2) || (id1 == id3) || (id2 == id3)) + { + printf("Degenerate triangle %d %d %d\n",id1,id2,id3); + exit(0); + } + else + { + Last_Edge(&temp1,&temp2,&temp3,0); + Add_Id_Strips(id1,end); + Add_Id_Strips(id2,end); + Add_Id_Strips(id3,end); + Last_Edge(&id1,&id2,&id3,1); + } +} + + +int Polygon_Output(P_ADJACENCIES temp,int face_id,int bucket, + ListHead *pListHead, BOOL first, int *swaps, + FILE *bands,int color1,int color2,int color3,BOOL global, BOOL end) +{ + ListHead *pListFace; + PF_FACES face; + P_ADJACENCIES pfNode; + static BOOL begin = TRUE; + int old_face,next_face_id,next_bucket,e1,e2,e3,other1,other2,other3; + P_ADJACENCIES lpListInfo; + int ties=0; + int tie = SEQUENTIAL; + + /* We have a polygon to output, the id is face id, and the number + of adjacent polygons to it is bucket. This routine extends the patches from + either end to make longer triangle strips. + */ + + + /* Now get the edge */ + Last_Edge(&e1,&e2,&e3,0); + + /* Get the polygon with id face_id */ + pListFace = PolFaces[face_id]; + face = (PF_FACES) PeekList(pListFace,LISTHEAD,0); + + /* We can't go any more */ + if ((face->nPolSize == 1) || ((face->nPolSize == 4) && (global))) /* if global, then we are still doing patches */ + { + /* Remove it from the list so we do not have to waste + time visiting it in the future, or winding up in an infinite loop + if it is the first on that we are looking at for a possible strip + */ + if (face->nPolSize == 1) + RemoveList(pListHead,(PLISTINFO) temp); + if (first) + return 0; + else + return (Finished(swaps,bands,global)); + } + + if (face->nPolSize == 3) + { + /* It is already a triangle */ + if (bucket == 0) + { + /* It is not adjacent to anything so we do not have to + worry about the order of the sides or updating adjacencies + */ + + next_face_id = Different(*(face->pPolygon),*(face->pPolygon+1),*(face->pPolygon+2), + e1,e2,e3,&other1,&other2); + face->nPolSize = 1; + + /* If this is the first triangle in the strip */ + if ((e2 == 0) && (e3 ==0)) + { + e2 = other1; + e3 = other2; + } + + Output_Tri(e2,e3,next_face_id,bands,color1,color2,color3,end); + RemoveList(pListHead,(PLISTINFO) temp); + return (Finished(swaps,bands,global)); + } + + + /* It is a triangle with adjacencies. This means that we + have to: + 1. Update the adjacencies in the list, because we are + using this polygon and it will be deleted. + 2. Get the next polygon. + */ + else + { + /* Return the face_id of the next polygon we will be using, + while updating the adjacency list by decrementing the + adjacencies of everything adjacent to the current triangle. + */ + + next_face_id = Update_Adjacencies(face_id, &next_bucket, &e1,&e2,&ties); + /* Maybe we deleted something in a patch and could not find an adj polygon */ + if (next_face_id == -1) + { + Output_Tri(*(face->pPolygon),*(face->pPolygon+1),*(face->pPolygon+2),bands,color1, + color2,color3,end); + face->nPolSize = 1; + RemoveList(pListHead,(PLISTINFO) temp); + return (Finished(swaps,bands,global)); + } + + old_face = next_face_id; + /* Find the other vertex to transmit in the triangle */ + e3 = Return_Other(face->pPolygon,e1,e2); + Last_Edge(&other1,&other2,&other3,0); + + if ((other2 != 0) && (other3 != 0)) + { + /* See which vertex in the output edge is not in the input edge */ + if ((e1 != other2) && (e1 != other3)) + e3 = e1; + else if ((e2 != other2) && (e2 != other3)) + e3 = e2; + else + { + printf("There is an error in the tri with adj\n"); + exit(0); + } + + /* See which vertex of the input edge is not in the output edge */ + if ((other2 != e1) && (other2 != e2)) + { + other1 = other2; + other2 = other3; + } + else if ((other3 != e1) && (other3 != e2)) + other1 = other3; + else + { + printf("There is an error in getting the tri with adj\n"); + exit(0); + } + + } + else + { + /* We are the first triangle in the strip and the starting edge + has not been set yet + */ + /* Maybe we deleted something in a patch and could not find an adj polygon */ + if (next_face_id == -1) + { + Output_Tri(*(face->pPolygon),*(face->pPolygon+1),*(face->pPolygon+2),bands,color1, + color2,color3,end); + face->nPolSize = 1; + RemoveList(pListHead,(PLISTINFO) temp); + return (Finished(swaps,bands,global)); + } + + other1 = e3; + e3 = e2; + other2 = e1; + } + + /* At this point the adjacencies have been updated and we + have the next polygon id + */ + + Output_Tri(other1,other2,e3,bands,color1,color2,color3,end); + face->nPolSize = 1; + RemoveList(pListHead,(PLISTINFO) temp); + begin = FALSE; + + /* Maybe we deleted something in a patch and could not find an adj polygon */ + if (next_face_id == -1) + return (Finished(swaps,bands,global)); + + if (Done(next_face_id,59,&next_bucket) == NULL) + { + printf("We deleted the next face 4%d\n",next_face_id); + exit(0); + } + + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = next_face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon3 %d\n",next_face_id); + exit(0); + } + return (Polygon_Output(lpListInfo,next_face_id,next_bucket, + pListHead, FALSE, swaps,bands,color1,color2,color3,global,end)); + + } + } + + else + { + /* It is not a triangle, we have to triangulate it . + Since it is not adjacent to anything we can triangulate it + blindly + */ + if (bucket == 0) + { + /* It is the first polygon in the strip, therefore there is no + input edge to start with. + */ + if ((e2 == 0) && (e3 ==0)) + Blind_Triangulate(face->nPolSize,face->pPolygon,bands, + TRUE,1,color1,color2,color3); + + else + Blind_Triangulate(face->nPolSize,face->pPolygon,bands, + FALSE,1,color1,color2,color3); + + RemoveList(pListHead,(PLISTINFO) temp); + + /* We will be at the beginning of the next strip. */ + face->nPolSize = 1; + return (Finished(swaps,bands,global)); + } + + + else + { + + + /* WHOLE triangulation */ + /* It is not a triangle and has adjacencies. + This means that we have to: + 1. Triangulate this polygon, not blindly because + we have an edge that we want to come out on, that + is the edge that is adjacent to a polygon with the + least number of adjacencies. Also we must come in + on the last seen edge. + 2. Update the adjacencies in the list, because we are + using this polygon . + 3. Get the next polygon. + */ + /* Return the face_id of the next polygon we will be using, + while updating the adjacency list by decrementing the + adjacencies of everything adjacent to the current polygon. + */ + + next_face_id = Update_Adjacencies(face_id, &next_bucket, &e1,&e2,&ties); + + /* Maybe we deleted something in a patch and could not find an adj polygon */ + if (next_face_id == -1) + { + + /* If we are at the first polygon in the strip and there is no input + edge, then begin is TRUE + */ + if ((e2 == 0) && (e3 == 0)) + Blind_Triangulate(face->nPolSize,face->pPolygon, + bands,TRUE,1,color1,color2,color3); + + else + Blind_Triangulate(face->nPolSize,face->pPolygon, + bands,FALSE,1,color1,color2,color3); + + RemoveList(pListHead,(PLISTINFO) temp); + + /* We will be at the beginning of the next strip. */ + face->nPolSize = 1; + return (Finished(swaps,bands,global)); + } + + if (Done(next_face_id,59,&next_bucket) == NULL) + { + printf("We deleted the next face 6 %d %d\n",next_face_id,face_id); + exit(0); + } + + Non_Blind_Triangulate(face->nPolSize,face->pPolygon, + bands,next_face_id,face_id,1,color1,color2,color3); + + RemoveList(pListHead,(PLISTINFO) temp); + begin = FALSE; + face->nPolSize = 1; + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = next_face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon2 %d %d\n",next_face_id,next_bucket); + exit(0); + } + return (Polygon_Output(lpListInfo,next_face_id,next_bucket, + pListHead, FALSE, swaps,bands,color1,color2,color3,global,end)); + } + + } + Last_Edge(&e1,&e2,&e3,0); + +} + + +int Extend_Face(int face_id,int e1,int e2,int *swaps,FILE *bands, + int color1,int color2,int color3,int *vert_norm, int normals, + int *vert_texture, int texture) +{ + int dummy=0,next_bucket; + P_ADJACENCIES pfNode,lpListInfo; + ListHead *pListHead; + + /* Try to extend backwards off of the local strip that we just found */ + + vn = vert_norm; + vt = vert_texture; + norm = normals; + text = texture; + + *swaps = 0; + /* Find the face that is adjacent to the edge and is not the + current face. + */ + face_id = Find_Face(face_id, e1, e2,&next_bucket); + if (face_id == -1) + return 0; + + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon3 %d\n",face_id); + exit(0); + } + Last_Edge(&dummy,&e1,&e2,1); + + /* Find a strip extending from the patch and return the cost */ + return (Polygon_Output(lpListInfo,face_id,next_bucket,pListHead,TRUE,swaps,bands,color1,color2,color3,TRUE,TRUE)); +} + + diff --git a/Tools/Stripe_u/output.h b/Tools/Stripe_u/output.h new file mode 100644 index 000000000..d9aed50a9 --- /dev/null +++ b/Tools/Stripe_u/output.h @@ -0,0 +1,26 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: output.h +-----------------------------------------------------------------------*/ + + +#define TRIANGLE 3 +#define MAGNITUDE 1000000 + +void Output_Tri(); +void Sgi_Test(); +int Polygon_Output(); +void Last_Edge(); +void Extend_Backwards(); +int Finished(); +int Extend_Face(); +void Fast_Reset(); + + diff --git a/Tools/Stripe_u/outputex.c b/Tools/Stripe_u/outputex.c new file mode 100644 index 000000000..48d4da147 --- /dev/null +++ b/Tools/Stripe_u/outputex.c @@ -0,0 +1,518 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: outputex.c + This file contains routines that are used for various functions in + the local algorithm. +*/ +/*---------------------------------------------------------------------*/ + + +#include <stdio.h> +#include <stdlib.h> +#include "global.h" +#include "outputex.h" +#include "triangulatex.h" +#include "polverts.h" +#include "ties.h" +#include "partial.h" +#include "sturctsex.h" +#include "options.h" +#include "output.h" +#include "common.h" +#include "util.h" + + +void Output_TriEx(int id1, int id2, int id3, FILE *output, int next_face, int flag, + int where) +{ + /* We will save everything into a list, rather than output at once, + as was done in the old routine. This way for future modifications + we can change the strips later on if we want to. + */ + + int swap,temp1,temp2,temp3; + static int total=0; + static int tri=0; + static int strips = 0; + static int cost = 0; + + if (flag == -20) + { + cost = cost + where+total+tri+strips+strips; + printf("We will need to send %d vertices to the renderer\n",cost); + total = 0; + tri = 0; + strips = 0; + return ; + } + + + if (flag == -10) + /* We are finished, now is time to output the triangle list + */ + { + fprintf(output,"\nt "); + tri = tri + Finished(&swap,output,FALSE); + total = total + swap; + strips++; + /*printf("There are %d swaps %d tri %d strips\n",total,tri,strips);*/ + } + + else + { + Last_Edge(&temp1,&temp2,&temp3,0); + Add_Id_Strips(id1,where); + Add_Id_Strips(id2,where); + Add_Id_Strips(id3,where); + Last_Edge(&id1,&id2,&id3,1); + } +} + + + + +void Extend_BackwardsEx(int face_id, FILE *output, FILE *strip, int *ties, + int tie, int triangulate, + int swaps,int *next_id) +{ + /* We just made a strip, now we are going to see if we can extend + backwards from the starting face, which had 2 or more adjacencies + to start with. + */ + int bucket,next_face,num,x,y,z,c,d=1,max,f; + ListHead *pListFace; + PF_FACES face; + P_ADJACENCIES temp; + + /* Get the first triangle that we have saved the the strip data + structure, so we can see if there are any polygons adjacent + to this edge or a neighboring one + */ + First_Edge(&x,&y,&z); + + pListFace = PolFaces[face_id]; + face = (PF_FACES) PeekList(pListFace,LISTHEAD,0); + + num = face->nPolSize; + + /* Go through the edges to see if there is an adjacency + with a vertex in common to the first triangle that was + outputted in the strip. (maybe edge was deleted....) + */ + for (c=0; c<num ; c++) + { + + if ( (c != (num-1)) && + (( (*(face->pPolygon+c) == x) && (*(face->pPolygon+c+1) == y)) || + (*(face->pPolygon+c) == y) && (*(face->pPolygon+c+1) == x))) + { + /* Input edge is still there see if there is an adjacency */ + next_face = Find_Face(face_id, x, y, &bucket); + if (next_face == -1) + /* Could not find a face adjacent to the edge */ + break; + pListFace = array[bucket]; + max = NumOnList(pListFace); + for (f=0;;f++) + { + temp = (P_ADJACENCIES) PeekList(pListFace,LISTHEAD,f); + if (temp->face_id == next_face) + { + Last_Edge(&z,&y,&x,1); + Polygon_OutputEx(temp,temp->face_id,bucket,pListFace, + output,strip,ties,tie,triangulate,swaps,next_id,0); + return; + } + + if (temp == NULL) + { + printf("Error in the new buckets%d %d %d\n",bucket,max,0); + exit(0); + } + } + + } + else if ( (c == (num -1)) && + ( ((*(face->pPolygon) == x) && (*(face->pPolygon+num-1) == y)) || + (*(face->pPolygon) == y) && (*(face->pPolygon+num-1) == x))) + { + next_face = Find_Face(face_id,x,y,&bucket); + if (next_face == -1) + /* Could not find a face adjacent to the edge */ + break; + pListFace = array[bucket]; + max = NumOnList(pListFace); + for (f=0;;f++) + { + temp = (P_ADJACENCIES) PeekList(pListFace,LISTHEAD,f); + if (temp->face_id == next_face) + { + Last_Edge(&z,&y,&x,1); + Polygon_OutputEx(temp,temp->face_id,bucket,pListFace, + output,strip,ties,tie,triangulate,swaps,next_id,0); + return; + } + + if (temp == NULL) + { + printf("Error in the new buckets%d %d %d\n",bucket,max,0); + exit(0); + } + } + } + + } + +} + +void Polygon_OutputEx(P_ADJACENCIES temp,int face_id,int bucket, + ListHead *pListHead, FILE *output, FILE *strips, + int *ties, int tie, + int triangulate, int swaps, + int *next_id, int where) +{ + ListHead *pListFace; + PF_FACES face; + P_ADJACENCIES pfNode; + static BOOL begin = TRUE; + int old_face,next_face_id,next_bucket,e1,e2,e3,other1,other2,other3; + P_ADJACENCIES lpListInfo; + + /* We have a polygon to output, the id is face id, and the number + of adjacent polygons to it is bucket. + */ + + Last_Edge(&e1,&e2,&e3,0); + + /* Get the polygon with id face_id */ + pListFace = PolFaces[face_id]; + face = (PF_FACES) PeekList(pListFace,LISTHEAD,0); + + if (face->nPolSize == 3) + { + /* It is already a triangle */ + if (bucket == 0) + { + /* It is not adjacent to anything so we do not have to + worry about the order of the sides or updating adjacencies + */ + + Last_Edge(&e1,&e2,&e3,0); + next_face_id = Different(*(face->pPolygon),*(face->pPolygon+1),*(face->pPolygon+2), + e1,e2,e3,&other1,&other2); + /* No input edge, at the start */ + if ((e2 ==0) && (e3 == 0)) + { + e2 = other1; + e3 = other2; + } + + Output_TriEx(e2,e3,next_face_id,strips,-1,begin,where); + RemoveList(pListHead,(PLISTINFO) temp); + /* We will be at the beginning of the next strip. */ + begin = TRUE; + } + /* It is a triangle with adjacencies. This means that we + have to: + 1. Update the adjacencies in the list, because we are + using this polygon and it will be deleted. + 2. Get the next polygon. + */ + else + { + /* Return the face_id of the next polygon we will be using, + while updating the adjacency list by decrementing the + adjacencies of everything adjacent to the current triangle. + */ + + next_face_id = Update_AdjacenciesEx(face_id, &next_bucket, &e1,&e2,ties); + old_face = next_face_id; + + /* Break the tie, if there was one */ + if (tie != FIRST) + old_face = Get_Next_Face(tie,face_id,triangulate); + + if (next_face_id == -1) + { + Polygon_OutputEx(temp,face_id,0,pListHead,output,strips,ties,tie, + triangulate,swaps,next_id,where); + return; + } + + + /* We are using a different face */ + if ((tie != FIRST) && (old_face != next_face_id) && (swaps == ON)) + { + next_face_id = old_face; + /* Get the new output edge, since e1 and e2 are for the + original next face that we got. + */ + e3 = Get_EdgeEx(&e1,&e2,face->pPolygon,next_face_id,face->nPolSize,0,0); + } + + /* Find the other vertex to transmit in the triangle */ + e3 = Return_Other(face->pPolygon,e1,e2); + Last_Edge(&other1,&other2,&other3,0); + + if ((other1 != 0) && (other2 != 0)) + { + /* See which vertex in the output edge is not in the input edge */ + if ((e1 != other2) && (e1 != other3)) + e3 = e1; + else if ((e2 != other2) && (e2 != other3)) + e3 = e2; + /* can happen with > 2 polys on an edge but won't form a good strip so stop + the strip here + */ + else + { + Polygon_OutputEx(temp,face_id,0,pListHead,output,strips,ties,tie, + triangulate,swaps,next_id,where); + return; + } + + /* See which vertex of the input edge is not in the output edge */ + if ((other2 != e1) && (other2 != e2)) + { + other1 = other2; + other2 = other3; + } + else if ((other3 != e1) && (other3 != e2)) + other1 = other3; + else + { + /* Degenerate triangle just return*/ + Output_TriEx(other1,other2,e3,strips,next_face_id,begin,where); + RemoveList(pListHead,(PLISTINFO) temp); + begin = FALSE; + return; + } + + } + + /* There was not an input edge, we are the first triangle in a strip */ + else + { + /* Find the correct order to transmit the triangle, what is + the output edge that we want ? + */ + other1 = e3; + e3 = e2; + other2 = e1; + } + + /* At this point the adjacencies have been updated and we + have the next polygon id + */ + Output_TriEx(other1,other2,e3,strips,next_face_id,begin,where); + RemoveList(pListHead,(PLISTINFO) temp); + begin = FALSE; + + if (Done(next_face_id,59,&next_bucket) == NULL) + return; + + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = next_face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon3 %d\n",next_face_id); + exit(0); + } + Polygon_OutputEx(lpListInfo,next_face_id,next_bucket, + pListHead, output, strips,ties,tie,triangulate,swaps,next_id,where); + + } +} + + else + { + /* It is not a triangle, we have to triangulate it . + Since it is not adjacent to anything we can triangulate it + blindly + */ + if (bucket == 0) + { + /* Check to see if there is not an input edge */ + Last_Edge(&other1,&other2,&other3,0); + if ((other1 == 0) && (other2 ==0)) + Blind_TriangulateEx(face->nPolSize,face->pPolygon, strips, + output,TRUE,where); + else + Blind_TriangulateEx(face->nPolSize,face->pPolygon,strips, + output,FALSE,where); + + RemoveList(pListHead,(PLISTINFO) temp); + /* We will be at the beginning of the next strip. */ + begin = TRUE; + } + + /* If we have specified PARTIAL triangulation then + we will go to special routines that will break the + polygon and update the data structure. Else everything + below will simply triangulate the whole polygon + */ + else if (triangulate == PARTIAL) + { + + /* Return the face_id of the next polygon we will be using, + */ + next_face_id = Min_Face_AdjEx(face_id,&next_bucket,ties); + + + /* Don't do it partially, because we can go inside and get + less adjacencies, for a quad we can do the whole thing. + */ + if ((face_id == next_face_id) && (face->nPolSize == 4) && (swaps == ON)) + { + next_face_id = Update_AdjacenciesEx(face_id, &next_bucket, &e1,&e2,ties); + if (next_face_id == -1) + { + /* There is no sequential face to go to, end the strip */ + Polygon_OutputEx(temp,face_id,0,pListHead,output,strips,ties,tie, + triangulate,swaps,next_id,where); + return; + } + + /* Break the tie, if there was one */ + if (tie != FIRST) + next_face_id = Get_Next_Face(tie,face_id,triangulate); + Non_Blind_TriangulateEx(face->nPolSize,face->pPolygon, strips, + output,next_face_id,face_id,where); + RemoveList(pListHead,(PLISTINFO) temp); + } + + /* Was not a quad but we still do not want to do it partially for + now, since we want to only do one triangle at a time + */ + else if ((face_id == next_face_id) && (swaps == ON)) + Inside_Polygon(face->nPolSize,face->pPolygon,strips,output, + next_face_id,face_id,next_id,pListHead,temp,where); + + else + { + if ((tie != FIRST) && (swaps == ON)) + next_face_id = Get_Next_Face(tie,face_id,triangulate); + Partial_Triangulate(face->nPolSize,face->pPolygon,strips, + output,next_face_id,face_id,next_id,pListHead,temp,where); + /* Check the next bucket again ,maybe it changed + We calculated one less, but that might not be the case + */ + } + + if (Done(next_face_id,59,&next_bucket) == NULL) + { + /* Check to see if there is not an input edge */ + Last_Edge(&other1,&other2,&other3,0); + if ((other1 == 0) && (other2 ==0)) + Blind_TriangulateEx(face->nPolSize,face->pPolygon, strips, + output,TRUE,where); + else + Blind_TriangulateEx(face->nPolSize,face->pPolygon,strips, + output,FALSE,where); + + if (Done(face_id,59,&bucket) != NULL) + { + pListHead = array[bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + RemoveList(pListHead,(PLISTINFO)lpListInfo); + } + begin = TRUE; + return; + } + + begin = FALSE; + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = next_face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon1 %d %d\n",next_face_id,next_bucket); + exit(0); + } + Polygon_OutputEx(lpListInfo,next_face_id,next_bucket, + pListHead, output, strips,ties,tie,triangulate,swaps,next_id,where); + } + + + else + { + /* WHOLE triangulation */ + /* It is not a triangle and has adjacencies. + This means that we have to: + 1. TriangulateEx this polygon, not blindly because + we have an edge that we want to come out on, that + is the edge that is adjacent to a polygon with the + least number of adjacencies. Also we must come in + on the last seen edge. + 2. Update the adjacencies in the list, because we are + using this polygon . + 3. Get the next polygon. + */ + /* Return the face_id of the next polygon we will be using, + while updating the adjacency list by decrementing the + adjacencies of everything adjacent to the current polygon. + */ + + next_face_id = Update_AdjacenciesEx(face_id, &next_bucket, &e1,&e2,ties); + + if (Done(next_face_id,59,&next_bucket) == NULL) + { + Polygon_OutputEx(temp,face_id,0,pListHead,output,strips,ties,tie, + triangulate,swaps,next_id,where); + /* Because maybe there was more than 2 polygons on the edge */ + return; + } + + /* Break the tie, if there was one */ + else if (tie != FIRST) + next_face_id = Get_Next_Face(tie,face_id,triangulate); + + Non_Blind_TriangulateEx(face->nPolSize,face->pPolygon, strips, + output,next_face_id,face_id,where); + RemoveList(pListHead,(PLISTINFO) temp); + begin = FALSE; + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = next_face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon2 %d %d\n",next_face_id,next_bucket); + exit(0); + } + Polygon_OutputEx(lpListInfo,next_face_id,next_bucket, + pListHead, output, strips,ties,tie,triangulate,swaps,next_id,where); + } + + } + Last_Edge(&e1,&e2,&e3,0); + +} + + + + + + + + diff --git a/Tools/Stripe_u/outputex.h b/Tools/Stripe_u/outputex.h new file mode 100644 index 000000000..68cff0ca2 --- /dev/null +++ b/Tools/Stripe_u/outputex.h @@ -0,0 +1,23 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: outputex.h +-----------------------------------------------------------------------*/ + + +#define TRIANGLE 3 +#define MAGNITUDE 1000000 + +void Output_TriEx(); +void Sgi_Test(); +void Polygon_OutputEx(); +void Extend_BackwardsEx(); +void FinishedEx(); + + diff --git a/Tools/Stripe_u/partial.c b/Tools/Stripe_u/partial.c new file mode 100644 index 000000000..9afb03c19 --- /dev/null +++ b/Tools/Stripe_u/partial.c @@ -0,0 +1,665 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: partial.c + This file contains routines that are used partial triangulation of polygons +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "global.h" +#include "outputex.h" +#include "polvertsex.h" +#include "triangulatex.h" +#include "sturctsex.h" +#include "polverts.h" +#include "common.h" +#include "util.h" + +void P_Triangulate_Quad(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,FILE *fp,int reversed,int face_id, + int *next_id,ListHead *pListHead, + P_ADJACENCIES temp, + int where) +{ + int vertex4,vertex5,dummy=60; + + /* This routine will nonblindly triangulate a quad, meaning + that there is a definite input and a definite output + edge that we must adhere to. Reversed will tell the orientation + of the input edge. (Reversed is -1 is we do not have an input + edge, in other words we are at the beginning of a strip.) + Out_edge* is the output edge, and in_edge* is the input edge. + Index are the edges of the polygon + and size is the size of the polygon. Begin is whether we are + at the start of a new strip. + Note that we will not necessarily triangulate the whole quad; + maybe we will do half and leave the other half (a triangle) + for later. + */ + + + /* If we do not have an input edge, then we can make our input + edge whatever we like, therefore it will be easier to come + out on the output edge. In this case the whole quad is done. + */ + if (reversed == -1) + { + vertex4 = AdjacentEx(out_edge1,out_edge2,index,size); + vertex5 = Get_Other_Vertex(vertex4,out_edge1,out_edge2,index); + Output_TriEx(vertex5,vertex4,out_edge1,output,-1,-1,where); + Output_TriEx(vertex4,out_edge1,out_edge2,output,-1,-1,where); + dummy = Update_AdjacenciesEx(face_id, &dummy, &dummy,&dummy,&dummy); + RemoveList(pListHead,(PLISTINFO) temp); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off, but cannot use the whole quad? + */ + + if (in_edge2 == out_edge1) + { + /* Output the triangle that comes out the correct + edge. Save the other half for later. + */ + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge2,index); + Output_TriEx(in_edge1,in_edge2,out_edge2,output,-1,-1,where); + /* Now we have a triangle used, and a triangle that is + left for later. + */ + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,in_edge2,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + /* Put the new face in the proper bucket of adjacencies + There are 2 edges that need to be checked for the triangle + that was just outputted. For the output edge we definitely + will be decreasing the adjacency, but we must check for the + input edge. + */ + + dummy = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp,FALSE); + dummy = Change_FaceEx(face_id,in_edge2,out_edge2,pListHead,temp,TRUE); + + /* Update the face data structure, by deleting the old + face and putting in the triangle as the new face + */ + New_Face(face_id,in_edge1,out_edge2,vertex4); + return; + } + else if (in_edge1 == out_edge1) + { + /* We want to output the first triangle (whose output + edge is not the one that we want. + We have to find the vertex that we need, which is + the other vertex which we do not have. + */ + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge2,index); + Output_TriEx(in_edge2,in_edge1,out_edge2,output,-1,-1,where); + /* Now we have a triangle used, and a triangle that is + left for later. + */ + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + dummy = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp,FALSE); + dummy = Change_FaceEx(face_id,in_edge1,out_edge2,pListHead,temp,TRUE); + + /* Update the face data structure, by deleting the old + face and putting in the triangle as the new face + */ + New_Face(face_id,in_edge2,out_edge2,vertex4); + return; + } + + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_TriEx(in_edge2,in_edge1,out_edge1,output,-1,-1,where); + /* Now we have a triangle used, and a triangle that is + left for later. + */ + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + dummy = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp,FALSE); + dummy = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp,TRUE); + + /* Update the face data structure, by deleting the old + face and putting in the triangle as the new face + */ + New_Face(face_id,in_edge2,out_edge1,vertex4); + return; + } + else if (in_edge2 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_TriEx(in_edge1,in_edge2,out_edge1,output,-1,-1,where); + /* Now we have a triangle used, and a triangle that is + left for later. + */ + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + dummy = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp,FALSE); + dummy = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp,TRUE); + + /* Update the face data structure, by deleting the old + face and putting in the triangle as the new face + */ + New_Face(face_id,in_edge1,out_edge1,vertex4); + return; + } + + /* The final case is where we want to come out the opposite + edge. + */ + else + { + if( ((!reversed) && (out_edge1 == (AdjacentEx(in_edge1,in_edge2,index,size)))) || + ((reversed) && (out_edge2 == (AdjacentEx(in_edge2,in_edge1,index,size))))) + { + /* We need to know the orientation of the input + edge, so we know which way to put the diagonal. + And also the output edge, so that we triangulate + correctly. Does not need partial. + */ + Output_TriEx(in_edge1,in_edge2,out_edge2,output,-1,-1,where); + Output_TriEx(in_edge2,out_edge2,out_edge1,output,-1,-1,where); + dummy = Update_AdjacenciesEx(face_id, &dummy, &dummy,&dummy,&dummy); + RemoveList(pListHead,(PLISTINFO) temp); + } + else + { + /* Input and output orientation was reversed, so diagonal will + be reversed from above. + */ + Output_TriEx(in_edge1,in_edge2,out_edge1,output,-1,-1,where); + Output_TriEx(in_edge2,out_edge1,out_edge2,output,-1,-1,where); + dummy = Update_AdjacenciesEx(face_id, &dummy, &dummy,&dummy,&dummy); + RemoveList(pListHead,(PLISTINFO) temp); + } + return; + } +} + +void P_Triangulate_Polygon(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size, + int *index,FILE *output,FILE *fp, + int reversed,int face_id,int *next_id, + ListHead *pListHead, P_ADJACENCIES temp2, + int where) +{ + /* We have a polygon greater than 4 sides, which we wish + to partially triangulate + */ + int next_bucket,vertex4,dummy = 60; + int *temp; + P_ADJACENCIES pfNode; + + + /* Since we are calling this recursively, we have to check whether + we are down to the case of the quad. + */ + if (size == 4) + { + P_Triangulate_Quad(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,output,fp,reversed,face_id,next_id, + pListHead,temp2,where); + return; + } + + /* We do not have a specified input edge, and therefore we + can make it anything we like, as long as we still come out + the output edge that we want. + */ + if (reversed == -1) + { + /* Get the vertex for the last triangle, which is + the one coming out the output edge, before we do + any deletions to the list. We will be doing this + bottom up. + */ + vertex4 = AdjacentEx(out_edge1,out_edge2,index,size); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(out_edge2,index,size); + /* We do not have to partially triangulate, since + we will do the whole thing, so use the whole routine + */ + Triangulate_PolygonEx(vertex4,out_edge1,in_edge2, + vertex4,size-1,index,output,fp,reversed,face_id, + next_id,pListHead,temp2,where); + memcpy(index,temp,sizeof(int)*size); + /* Lastly do the triangle that comes out the output + edge. + */ + Output_TriEx(vertex4,out_edge1,out_edge2,output,-1,-1,where); + /* We were able to do the whole polygon, now we + can delete the whole thing from our data structure. + */ + dummy = Update_AdjacenciesEx(face_id, &dummy, &dummy,&dummy,&dummy); + RemoveList(pListHead,(PLISTINFO) temp2); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off that comes out the correct output edge, + but we cannot use the whole polygon? + */ + if (in_edge2 == out_edge1) + { + Output_TriEx(in_edge1,out_edge1,out_edge2,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp2,TRUE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge2,index,size); + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + return; + } + + /* Next case is where it is again consecutive, but the triangle + formed by the consecutive edges do not come out of the + correct output edge. (the input edge will be reversed in + the next triangle) + */ + else if (in_edge1 == out_edge1) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + Output_TriEx(in_edge2,in_edge1,out_edge2,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp2,TRUE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge1,index,size); + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + return; + } + + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + Output_TriEx(in_edge2,in_edge1,out_edge1,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge1,out_edge2,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp2,TRUE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge1,index,size); + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + return; + } + else if (in_edge2 == out_edge2) + { + Output_TriEx(in_edge1,in_edge2,out_edge1,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp2,TRUE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge2,index,size); + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + return; + } + + /* Else the edge is not consecutive, and it is sufficiently + far away, for us not to make a conclusion at this time. + So we can take off a triangle and recursively call this + function. + */ + else + { + if (!reversed) + { + vertex4 = AdjacentEx(in_edge2,in_edge1,index,size); + Output_TriEx(in_edge1,in_edge2,vertex4,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(in_edge1,vertex4,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,in_edge1,vertex4,pListHead,temp2,FALSE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge1,index,size); + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + + /* Save the info for the new bucket, we will need it on + the next pass for the variables, pListHead and temp + */ + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + temp2 = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (temp2 == NULL) + { + printf("There is an error finding the next polygon10\n",next_bucket,face_id); + exit(0); + } + + P_Triangulate_Polygon(out_edge1,out_edge2,in_edge2, + vertex4,size-1,index,output,fp,!reversed, + face_id,next_id,pListHead,temp2,where); + } + else + { + vertex4 = AdjacentEx(in_edge1,in_edge2,index,size); + Output_TriEx(in_edge2,in_edge1,vertex4,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(in_edge2,vertex4,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,in_edge2,vertex4,pListHead,temp2,FALSE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge2,index,size); + + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + + /* Save the info for the new bucket, we will need it on + the next pass for the variables, pListHead and temp + */ + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + temp2 = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (temp2 == NULL) + { + printf("There is an error finding the next polygon11 %d %d\n",face_id,next_bucket); + exit(0); + } + + P_Triangulate_Polygon(out_edge1,out_edge2,vertex4, + in_edge1,size-1,index,output,fp,!reversed, + face_id,next_id,pListHead,temp2,where); + } + return; + } +} + +void P_Triangulate(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *fp,FILE *output,int reversed,int face_id, + int *next_id,ListHead *pListHead, + P_ADJACENCIES temp,int where) +{ + + if (size == 4) + P_Triangulate_Quad(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,fp,output,reversed,face_id,next_id,pListHead, temp,where); + else + P_Triangulate_Polygon(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,fp,output,reversed,face_id,next_id,pListHead,temp,where); +} + + void Partial_Triangulate(int size,int *index, FILE *fp, + FILE *output,int next_face_id,int face_id, + int *next_id,ListHead *pListHead, + P_ADJACENCIES temp, int where) +{ + int id1,id2,id3; + int nedge1,nedge2; + int reversed; + + /* We have a polygon that has to be triangulated and we cannot + do it blindly, ie we will try to come out on the edge that + has the least number of adjacencies, But also we do not + want to triangulate the whole polygon now, so that means + we will output the least number of triangles that we can + and then update the data structures, with the polygon + that is left after we are done. + */ + Last_Edge(&id1,&id2,&id3,0); + + /* Find the edge that is adjacent to the new face , + also return whether the orientation is reversed in the + face of the input edge, which is id2 and id3. + */ + reversed = Get_EdgeEx(&nedge1,&nedge2,index,next_face_id,size,id2,id3); + + /* Input edge and output edge can be the same if there are more than + one polygon on an edge + */ + if ( ((nedge1 == id2) && (nedge2 == id3)) || + ((nedge1 == id3) && (nedge2 == id2)) ) + /* Set output edge arbitrarily but when come out of here the + next face will be on the old output edge (identical one) + */ + nedge2 = Return_Other(index,id2,id3); + + /* Do the triangulation */ + P_Triangulate(nedge1,nedge2,id2,id3,size,index,fp,output,reversed, + face_id,next_id,pListHead,temp,where); +} + + void Input_Edge(int face_id, int *index, int size, int in_edge1, int in_edge2, + FILE *fp, FILE *output,ListHead *pListHead, P_ADJACENCIES temp2, + int where) + { + /* The polygon had an input edge, specified by input1 and input2 */ + + int output1,next_bucket; + int vertex4, vertex5,dummy=60; + + output1 = Get_Output_Edge(face_id,size,index,in_edge1,in_edge2); + vertex5 = AdjacentEx(in_edge2,in_edge1,index,size); + vertex4 = AdjacentEx(in_edge1,in_edge2,index,size); + + if (vertex4 == output1) + { + Output_TriEx(in_edge2,in_edge1,output1,output,-1,-1,where); + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(in_edge2,output1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,in_edge2,output1,pListHead,temp2,FALSE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge2,index,size); + + } + else if (vertex5 == output1) + { + Output_TriEx(in_edge1,in_edge2,vertex5,output,-1,-1,where); + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(in_edge1,vertex5,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,in_edge1,vertex5,pListHead,temp2,FALSE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge1,index,size); + } + + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + return; + } + + void Inside_Polygon(int size,int *index,FILE *fp,FILE *output, + int next_face_id,int face_id,int *next_id, + ListHead *pListHead,P_ADJACENCIES temp, int where) + { + /* We know that we have a polygon that is greater than 4 sides, and + that it is better for us to go inside the polygon for the next + one, since inside will have less adjacencies than going outside. + So, we are not doing partial for a part of the polygon. + */ + int id1,id2,id3; + int new1,new2; + + Last_Edge(&id1,&id2,&id3,0); + + /* See if the input edge existed in the polygon, that will help us */ + if (Exist(face_id,id2,id3)) + Input_Edge(face_id,index,size,id2,id3,output,fp,pListHead,temp,where); + else + { + /* Make one of the input edges + We will choose it by trying to get an edge that has something + in common with the last triangle, or by getting the edge that + is adjacent to the least number of thigs, with preference given + to the first option + */ + + Get_Input_Edge(index,id1,id2,id3,&new1,&new2,size,face_id); + Input_Edge(face_id,index,size,new1,new2,output,fp,pListHead,temp,where); + } + } + + diff --git a/Tools/Stripe_u/partial.h b/Tools/Stripe_u/partial.h new file mode 100644 index 000000000..c9a9439c8 --- /dev/null +++ b/Tools/Stripe_u/partial.h @@ -0,0 +1,15 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: partial.h +-----------------------------------------------------------------------*/ + +void Partial_Triangulate(); +void Inside_Polygon(); + diff --git a/Tools/Stripe_u/polverts.h b/Tools/Stripe_u/polverts.h new file mode 100644 index 000000000..79ece86db --- /dev/null +++ b/Tools/Stripe_u/polverts.h @@ -0,0 +1,87 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: polverts.h +-----------------------------------------------------------------------*/ + +#include "queue.h" +#include <malloc.h> + + +/* external functions */ +void Find_Adjacencies(); +void Test_Adj_Struct(); +void Test_SGI_Struct(); +void Write_Edges(); +void Build_SGI_Table(); +void Save_Walks(); +void Find_Bands(); +void Save_Rest(); +void Assign_Walk(); +void Save_Walks(); + +typedef struct adjacencies +{ + Node ListNode; + int face_id; +} ADJACENCIES,*P_ADJACENCIES; + +typedef struct FVerts +{ + Node ListNode; + int *pPolygon; + int nPolSize; + int nId; +} F_VERTS, *PF_VERTS; + +/*Every time we need to use this, cast it ( ListInfo*)*/ + +typedef struct FEdges +{ + Node ListNode; + int edge[3]; +}F_EDGES,*PF_EDGES; + +typedef struct FFaces +{ + Node ListNode; + int *pPolygon; + int *pNorms; + int seen; + int seen2; + int seen3; + int nPolSize; + F_EDGES **VertandId; + int *marked; + int *walked; +} F_FACES,*PF_FACES; + + +typedef struct Strips +{ + Node ListNode; + int face_id; +} Strips,*P_STRIPS; + + + struct vert_added + { + int num; + int *normal; + }; + + +/* Globals */ +ListHead **PolVerts; +ListHead **PolFaces; +ListHead **PolEdges; +ListHead *array[60]; +int id_array[60]; +ListHead *strips[1]; +ListHead *all_strips[100000]; /* Assume max 100000 strips */ diff --git a/Tools/Stripe_u/polvertsex.h b/Tools/Stripe_u/polvertsex.h new file mode 100644 index 000000000..8e05a7dd3 --- /dev/null +++ b/Tools/Stripe_u/polvertsex.h @@ -0,0 +1,34 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: polvertsex.h +-----------------------------------------------------------------------*/ + +#include "queue.h" +#include <malloc.h> + + +/* external functions */ +void Start_Vert_Struct(); +void Start_Face_StructEx(); +void Start_Edge_StructEx(); +void AddNewNode(); +void AddNewFaceEx(); +void Find_AdjacenciesEx(); +void Test_Adj_Struct(); +void Test_SGI_Struct(); +void Write_Edges(); +void End_Verts_Struct(); +void End_Face_StructEx(); +void End_Edge_StructEx(); +void Build_SGI_TableEx(); +void Add_AdjEdgeEx(); + + + diff --git a/Tools/Stripe_u/queue.c b/Tools/Stripe_u/queue.c new file mode 100644 index 000000000..29d5e9de0 --- /dev/null +++ b/Tools/Stripe_u/queue.c @@ -0,0 +1,226 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: queue.c + This file contains the routines used in the data structures lists, which + are queues. +*/ +/*---------------------------------------------------------------------*/ + + #include "queue.h" + + + +/*---------------------------------------------------------------------------- + * InitList: + */ +BOOL InitList (PLISTHEAD LHead) + +{ + if (LHead == NULL) return(FALSE); + + LHead->LHeaders[LISTHEAD] = LHead->LHeaders[LISTTAIL] = NULL; + LHead->NumList = 0; + return(TRUE); +} + +/*---------------------------------------------------------------------------- + * AddHead: + */ +BOOL AddHead(PLISTHEAD LHead, PLISTINFO LInfo) +{ + if (LHead == NULL || LInfo == NULL) + return(FALSE); + if (EMPTYLIST(LHead)) + LHead->LHeaders[LISTTAIL] = LInfo; + else LHead->LHeaders[LISTHEAD]->ListNode.Previous = (void *) LInfo; + + LInfo->ListNode.Next = (void *) LHead->LHeaders[LISTHEAD]; + LHead->LHeaders[LISTHEAD] = LInfo; + LInfo->ListNode.Previous = NULL; + LHead->NumList++; + return(TRUE); +} + +/*---------------------------------------------------------------------------- + * AddTail + */ +BOOL AddTail(PLISTHEAD LHead, PLISTINFO LInfo) +{ + if (LHead == NULL || LInfo == NULL) + return(FALSE); + if (EMPTYLIST(LHead)) + LHead->LHeaders[LISTHEAD] = LInfo; + else LHead->LHeaders[LISTTAIL]->ListNode.Next = (void *) LInfo; + + LInfo->ListNode.Previous = (void *) LHead->LHeaders[LISTTAIL]; + LHead->LHeaders[LISTTAIL] = LInfo; + LInfo->ListNode.Next = NULL; + LHead->NumList++; + return(TRUE); +} + + +BOOL InsertNode( PLISTHEAD LHead, int nPos, PLISTINFO LInfo ) +{ +PLISTINFO LAddNode; + + if ( LHead == NULL || LInfo == NULL || nPos > NumOnList( LHead ) ) + return( FALSE ); + + if ( nPos == 0 ) + AddHead( LHead, LInfo ); + else if ( nPos == NumOnList( LHead ) ) + AddTail( LHead, LInfo ); + else + { + if ( (LAddNode = PeekList( LHead, LISTHEAD, nPos - 1 )) == NULL ) + return( FALSE ); + + ((PLISTINFO)LAddNode->ListNode.Next)->ListNode.Previous = LInfo; + LInfo->ListNode.Next = LAddNode->ListNode.Next; + LInfo->ListNode.Previous = LAddNode; + LAddNode->ListNode.Next = LInfo; + + LHead->NumList++; + } + + return( TRUE ); +} + + + + +/*---------------------------------------------------------------------------- + * RemHead: + */ +PLISTINFO RemHead(PLISTHEAD LHead) +{ + PLISTINFO t, t1; + + if ( LHead == NULL || EMPTYLIST(LHead) ) + return(NULL); + + t = LHead->LHeaders[LISTHEAD]; + LHead->LHeaders[LISTHEAD] = (PLISTINFO) t->ListNode.Next; + + if (LHead->LHeaders[LISTHEAD] != NULL) + { + t1 = (PLISTINFO) t->ListNode.Next; + t1->ListNode.Previous = NULL; + } + else + LHead->LHeaders[LISTTAIL] = NULL; + + LHead->NumList--; + + return(t); +} + +/*---------------------------------------------------------------------------- + * RemTail: + */ +PLISTINFO RemTail(PLISTHEAD LHead) +{ + PLISTINFO t, t1; + + if ( LHead == NULL || EMPTYLIST(LHead) ) + return(NULL); + + t = LHead->LHeaders[LISTTAIL]; + LHead->LHeaders[LISTTAIL] = (PLISTINFO) t->ListNode.Previous; + if (LHead->LHeaders[LISTTAIL] != NULL) + { + t1 = (PLISTINFO) t->ListNode.Previous; + t1->ListNode.Next = NULL; + } + else + LHead->LHeaders[LISTHEAD] = NULL; + + LHead->NumList--; + return(t); +} + +/*---------------------------------------------------------------------------- + * PeekList: + */ +PLISTINFO PeekList(PLISTHEAD LHead, int wch, int index ) +{ + PLISTINFO t; + + if (LHead == NULL) + return(NULL); + if ( (t = LHead->LHeaders[wch]) == NULL ) + return(NULL); + + for (; t != NULL && index > 0; index-- ) + t = (wch == LISTHEAD) ? (PLISTINFO) t->ListNode.Next : + (PLISTINFO) t->ListNode.Previous; + return(t); +} + + +/*---------------------------------------------------------------------------- + * RemoveList: + */ +PLISTINFO RemoveList( PLISTHEAD LHead, PLISTINFO LInfo ) +{ + PLISTINFO t, t1; + + t = LInfo; + if (LHead == NULL) + return(NULL); + if (LHead->LHeaders[LISTHEAD] == t) + t = (PLISTINFO) RemHead(LHead); + else if (LHead->LHeaders[LISTTAIL] == t) + t = (PLISTINFO) RemTail(LHead); + else + { + t1 = (PLISTINFO) t->ListNode.Previous; + t1->ListNode.Next = t->ListNode.Next; + t1 = (PLISTINFO) t->ListNode.Next; + t1->ListNode.Previous = t->ListNode.Previous; + LHead->NumList--; + } + + return(t); +} + +/*---------------------------------------------------------------------------- + * SearchList: + * Try to find a specific node in the queue whose key matches with + * searching key. Return the pointer to that node if found, return NULL + * otherwise + * + * Input: + * lpHashTbl => a far pointer to the hash table + * lpKey => a far poniter to searching key + * CompareCallBack => comparision function + * + * Output: a far pointer to the node to be found + * + */ +PLISTINFO SearchList( + PLISTHEAD lpListHead, + PVOID lpSKey, + int (* CompareCallBack) ( PVOID, PVOID ) ) +{ +PLISTINFO lpListInfo; + + lpListInfo = PeekList( lpListHead, LISTHEAD, 0); + while ( lpListInfo != NULL ) + { + if ( CompareCallBack( lpListInfo, lpSKey ) ) + break; + lpListInfo = GetNextNode( lpListInfo ); + } + + return( lpListInfo ); +} + diff --git a/Tools/Stripe_u/queue.h b/Tools/Stripe_u/queue.h new file mode 100644 index 000000000..0bf926e0f --- /dev/null +++ b/Tools/Stripe_u/queue.h @@ -0,0 +1,283 @@ + +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE:queue.h +-----------------------------------------------------------------------*/ + +#ifndef QUEUE_INCLUDED +#define QUEUE_INCLUDED + +/* %%s Node */ +/***************************************************************** + This structure is used to store the List linkage information of a +ListInfo structure. It contains all the necessary information for the +List functions to function properly. This structure must be the first +one defined in any block of memory to be linked with the List functions. +for an example of the used of The Node structure look in the files +ipd2dms.c and ipd2man.h +******************************************************************/ +#include <stdio.h> +#define FALSE 0 +#define TRUE 1 +typedef struct +{ + void *Next; + void *Previous; +} + Node, * PNODE; + +/***************************************************************** + Next : is a pointer to the next structure in this List. + Previous : is a pointer to the previous structure in this List. + priority : this is the priority of this structure in the List. The + highest priority is 0. This field is only used by the + functions EnQue and DeQue. +******************************************************************/ +/* %%e */ + + +/* %%s ListInfo */ + +/***************************************************************** + This is the general means of linking application defined information into +Lists and queues. All structures must begin with the Node Structure. All +other data in the structure is user definable. +******************************************************************/ + +typedef struct List +{ + Node ListNode; /* link to the next Listinfo Structure */ + /* user definable data */ +} ListInfo, *PLISTINFO; + +/***************************************************************** + ListNode : this is the required node structure for the List + mainpulation functions. This must be the first + element of a user definable structure. + + In order for an application to use the List routines, it must define +a structure with all the needed information. The first element in the +user definable structure must be a Node structure. The Node structure +contains all the necessary information for the List routines to do their +magic. For an example of a user defined List structure see the file +ipd2i.h. The User definable structure can be passed to any List function +that excepts a pointer to a ListInfo structure. + +example: + +typedef mstruct +{ + Node ListNode; + int a,b,c,d,e,f,g; +} + mystruct; + + the user definable portion of the above structure is represented by +the integers a,b,c,d,e,f,g. When passing this structure to a List +function a cast of (ListInfo *) must be made to satisify the "C" complier. +******************************************************************/ +/* %%e */ + + +/* %%s ListHead */ +/***************************************************************** + ListHead is used as a header to a List. LHeaders[0] points to the +head of the List. LHeaders[1] points the tail of the list. When +accessing these variables use the defines LISTHEAD, LISTTAIL. +******************************************************************/ + +typedef struct LHead +{ + PLISTINFO LHeaders[2]; + int NumList; +} +ListHead, *PLISTHEAD; + +/***************************************************************** + LHeaders : this is an array of two pointers to ListInfo structures. + This information is used to point to the head and tail of + a list. + NumList : this integer hold the number of structures linked into this + list. + +ListHead #define: + + LISTHEAD : when Peeking down a list this specifies you should + start at the Head of the list and search downward. + + LISTTAIL : when Peeking down a list this specifies you should + start at the tail of the list and search foward. + ******************************************************************/ + +#define LISTHEAD 0 + +#define LISTTAIL 1 +/* %%e */ + +typedef int BOOL; +typedef void * PVOID; + +#define PEEKFROMHEAD( lh, ind ) ( PeekList( (lh), LISTHEAD, (ind) ) ) +#define PEEKFROMTAIL( lh, ind ) ( PeekList( (lh), LISTTAIL, (ind) ) ) +#define EMPTYLIST( lh ) ( ( (lh)->LHeaders[LISTHEAD] == NULL ) ) + +/* General utility routines */ +/* %%s QueRoutines */ +BOOL InitList ( PLISTHEAD ); + +/***************************************************************** + InitList : Initialize a new list structure for use with the List + routines + + INPUTS : LHead : a pointer to a ListHead structure. + OUTPUT : a boolean value TRUE if no errors occured FALSE + otherwise +******************************************************************/ + + +PLISTINFO PeekList ( PLISTHEAD, int, int ); + +/***************************************************************** + PeekList : This funciton peeks down a list for the N'th element + from the HEAD or TAIL of the list + + INPUTS : LHead : a pointer to a List head structure. + from : can either search from the HEAD or TAIL + of the list + where : how many nodes from the begining should the + List routines look. + OUTPUT : a pointer to a ListInfo structure identified by + from/where or NULL if an error occurred. +******************************************************************/ + + +PLISTINFO RemoveList( PLISTHEAD LHead, PLISTINFO LInfo ); + + +/***************************************************************** + RemoveList: Remove a ListInfo structure from a List. + + INPUTS : LHead : a pointer to a ListHead structure. + LInfo : a pointer to the ListInfo structure to remove + from the list. + OUTPUT : a pointer to the ListInfo structure that was removed or + NULL if an error occurred. +******************************************************************/ + +BOOL InsertNode( PLISTHEAD LHead, int nPos, PLISTINFO LInfo ); + +/***************************************************************** + InsertNode: add a node to a list after a given node + + INPUTS : LHead : a pointer to a ListHead structure. + nPos : the position to insert the node into + LInfo : a pointer to the new node to add to the list. + OUTPUT: a boolean value TRUE if all goes well false otherwise +*****************************************************************/ + +BOOL AddHead ( PLISTHEAD, PLISTINFO ); + +/***************************************************************** + AddHead : add a ListInfo structure to the HEAD of a list. + + INPUTS : LHead : a pointer to a ListHead structure of the list + to add to. + LInfo : a pointer to the ListInfo structure to add to + the list. + OUTPUT : A boolean value TRUE if no errors occurred FALSE + otherwise. +******************************************************************/ + + +BOOL AddTail ( PLISTHEAD, PLISTINFO ); + +/***************************************************************** + AddTail : Add a ListInfo structure to the TAIL of a list. + + INPUTS : LHead : a pointer to a ListHead structure of the List + to add to. + LInfo : a pointer to the ListInfo structure to add to + the List. + OUTPUT : a boolean value TRUE if no errors occurred FALSE + otherwise. +******************************************************************/ + + +PLISTINFO RemTail ( PLISTHEAD ); + +/***************************************************************** + RemTail : Remove a ListInfo structure from the TAIL of a List. + + INPUTS : LHead : a pointer to a ListHead structure of the List + to remove from. + OUTPUT : a pointer to the ListInfo structure that was removed + or NULL if an error occurred. +******************************************************************/ + + +PLISTINFO RemHead ( PLISTHEAD ); + +/***************************************************************** + RemHead : Remove a ListInfo structure from the Head of a List. + + INPUTS : LHead : a pointer to a ListHead structure of the List + to remove from. + OUTPUT : a pointer to the ListInfo structure that was removed or + NULL if an error occurred. +******************************************************************/ + +PLISTINFO SearchList( + PLISTHEAD lpListHead, + PVOID lpSKey, + int ( * CompareCallBack) ( PVOID, PVOID ) ); + +/***************************************************************** + SearchList: + Try to find a specific node in the queue whose key matches with + searching key. Return the pointer to that node if found, return NULL + otherwise + + Input: + lpHashTbl => a far pointer to the hash table + lpKey => a far poniter to searching key + CompareCallBack => comparision function + + Output: a far pointer to the node to be found + + ******************************************************************/ + +#define NumOnList(lh) ( ((lh)->NumList) ) + +/***************************************************************** + NumOnList: Returns the number of Nodes linked to a ListHead + structure. This number is maintained by the List + routines. +******************************************************************/ + +#define GetNextNode(pli) ( ((pli)->ListNode.Next) ) + +/******************************************************** + GetNextNode: This macro returns the Next Structure in this list. + This macro will return NULL if no more structures are + in the List. +*********************************************************/ + +#define GetPrevNode(pli) ( ((pli)->ListNode.Previous) ) + +/******************************************************** + GetPrevNode: This macro returns the Previous Structure in this list. + This macro will reutrn NULL if no more structures are + in the List. +********************************************************/ +/* %%e */ + +#endif + + diff --git a/Tools/Stripe_u/sgi_triang.c b/Tools/Stripe_u/sgi_triang.c new file mode 100644 index 000000000..40dff70f6 --- /dev/null +++ b/Tools/Stripe_u/sgi_triang.c @@ -0,0 +1,631 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: sgi_triang.c + File contains the routines that do the whole triangulation + of polygons. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "global.h" +#include "output.h" +#include "polverts.h" +#include "sturcts.h" +#include "common.h" +#include "util.h" +#include "init.h" + +int Adjacent(int id2,int id1, int *list, int size) +{ + /* Return the vertex that is adjacent to id1, + but is not id2, in the list of integers. + */ + + register int x=0; + + while (x < size) + { + if (*(list+x) == id1) + { + if ((x != (size -1)) && (x != 0)) + { + if ( *(list+x+1) != id2) + return *(list+x+1); + else + return *(list+x-1); + } + else if (x == (size -1)) + { + if (*(list) != id2) + return *(list); + else + return *(list+x-1); + } + else + { + if (*(list+size-1) != id2) + return *(list+size-1); + else + return *(list+x+1); + } + } + x++; + } + /* if there are degeneracies */ + return id1; +} + + +void Rearrange_Index(int *index, int size) +{ + /* If we are in the middle of a strip we must find the + edge to start on, which is the last edge that we had + transmitted. + */ + int x,f,y,e1,e2,e3; + register int increment = 1; + int *temp; + + /* Find where the input edge is in the input list */ + Last_Edge(&e1,&e2,&e3,0); + for (y = 0; y < size; y++) + { + if (*(index+y) == e2) + { + if ((y != (size - 1)) && (*(index+y+1) == e3)) + break; + else if ((y == (size - 1)) && (*(index) == e3)) + break; + else if ((y != 0) && (*(index+y-1) == e3)) + { + increment = -1; + break; + } + else if ((y==0) && (*(index+size-1) == e3)) + { + increment = -1; + break; + } + } + if (*(index+y) == e3) + { + if ((y != (size - 1)) && (*(index+y+1) == e2)) + break; + else if ((y == (size - 1)) && (*(index) == e2)) + break; + else if ((y != 0) && (*(index+y-1) == e2)) + { + increment = -1; + break; + } + else if ((y==0) && (*(index+size-1) == e2)) + { + increment = -1; + break; + } + } + /* Edge is not here, we are at the beginning */ + if ((y == (size-1)) && (increment != -1)) + return; + } + + /* Now put the list into a new list, starting with the + input edge. Increment tells us whether we have to go + forward or backward. + */ + /* Was in good position already */ + if ((y == 0) && (increment == 1)) + return; + + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + + if (increment == 1) + { + x=0; + for (f = y ; f< size; f++) + { + *(index+x) = *(temp+f); + x++; + } + /* Finish the rest of the list */ + for(f = 0; f < y ; f++) + { + *(index+x) = *(temp+f); + x++; + } + } + else + { + x=0; + for (f = y ; f >= 0; f--) + { + *(index+x) = *(temp+f); + x++; + } + /* Finish the rest of the list */ + for(f = (size - 1); f > y ; f--) + { + *(index+x) = *(temp+f); + x++; + } + } +} + +void Delete_From_List(int id,int *list, int *size) +{ + /* Delete the occurence of id in the list. + (list has size size) + */ + + int *temp; + register int x,y=0; + + temp = (int *) malloc(sizeof(int) * (*size)); + for (x=0; x<(*size); x++) + { + if (*(list+x) != id) + { + *(temp+y) = *(list+x); + y++; + } + } + *(temp+y) = -1; + *size = *size - (*size - y - 1); + memcpy(list,temp,sizeof(int)*(*size)); +} + + +void Build_SGI_Table(int num_verts,int num_faces) +{ + /* Build a table that has the polygons sorted by the + number of adjacent polygons. + */ + int x,y,size,tally=0; + ListHead *pListHead; + PF_FACES temp = NULL; + + /* For each face....*/ + for (x=0;x < num_faces;x++) + { + pListHead = PolFaces[x]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp != NULL ) + { + /* Size of the polygon */ + size = temp->nPolSize; + if (size != 1) + { + for (y = 0; y< size; y++) + { + if (y != (size-1)) + tally += Num_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1)); + else + tally += Num_Adj(*(temp->pPolygon),*(temp->pPolygon+(size-1))); + } + + /* Tally is the number of polygons that is adjacent to + the current polygon. + */ + /* Now put the face in the proper bucket depending on tally. */ + Add_Sgi_Adj(tally,x); + temp = NULL; + tally=0; + } + } + } +} + + +void Triangulate_Quad(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,int reversed,int face_id, + int where,int color1,int color2,int color3) +{ + int vertex4,vertex5; + + /* This routine will nonblindly triangulate a quad, meaning + that there is a definite input and a definite output + edge that we must adhere to. Reversed will tell the orientation + of the input edge. (Reversed is -1 is we do not have an input + edge, in other words we are at the beginning of a strip.) + Out_edge* is the output edge, and in_edge* is the input edge. + Index are the edges of the polygon + and size is the size of the polygon. Begin is whether we are + at the start of a new strip. + */ + + /* If we do not have an input edge, then we can make our input + edge whatever we like, therefore it will be easier to come + out on the output edge. + */ + if (reversed == -1) + { + vertex4 = Adjacent(out_edge1,out_edge2,index,size); + vertex5 = Get_Other_Vertex(vertex4,out_edge1,out_edge2,index); + Output_Tri(vertex5,vertex4,out_edge1,output,color1,color2,color3,where); + Output_Tri(vertex4,out_edge1,out_edge2,output,color1,color2,color3,where); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off, but cannot use the whole quad? + */ + + if (in_edge2 == out_edge1) + { + /* Output the triangle that comes out the correct + edge last. First output the triangle that comes out + the wrong edge. + */ + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge2,index); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where); + Output_Tri(vertex4,in_edge2,out_edge2,output,color1,color2,color3,where); + return; + } + /* The next case is where it is impossible to come out the + edge that we want. So we will have to start a new strip to + come out on that edge. We will output the one triangle + that we can, and then start the new strip with the triangle + that comes out on the edge that we want to come out on. + */ + else if (in_edge1 == out_edge1) + { + /* We want to output the first triangle (whose output + edge is not the one that we want. + We have to find the vertex that we need, which is + the other vertex which we do not have. + */ + vertex4 = Get_Other_Vertex(in_edge2,in_edge1,out_edge2,index); + Output_Tri(in_edge2,in_edge1,vertex4,output,color1,color2,color3,where); + Output_Tri(vertex4,in_edge1,out_edge2,output,color1,color2,color3,where); + return; + } + + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_Tri(in_edge2,in_edge1,vertex4,output,color1,color2,color3,where); + Output_Tri(vertex4,in_edge1,out_edge1,output,color1,color2,color3,where); + return; + } + else if (in_edge2 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where); + Output_Tri(vertex4,in_edge2,out_edge1,output,color1,color2,color3,where); + return; + } + + /* The final case is where we want to come out the opposite + edge. + */ + else + { + if( ((!reversed) && (out_edge1 == (Adjacent(in_edge1,in_edge2,index,size)))) || + ((reversed) && (out_edge2 == (Adjacent(in_edge2,in_edge1,index,size))))) + { + /* We need to know the orientation of the input + edge, so we know which way to put the diagonal. + And also the output edge, so that we triangulate + correctly. + */ + Output_Tri(in_edge1,in_edge2,out_edge2,output,color1,color2,color3,where); + Output_Tri(in_edge2,out_edge2,out_edge1,output,color1,color2,color3,where); + } + else + { + /* Input and output orientation was reversed, so diagonal will + be reversed from above. + */ + Output_Tri(in_edge1,in_edge2,out_edge1,output,color1,color2,color3,where); + Output_Tri(in_edge2,out_edge1,out_edge2,output,color1,color2,color3,where); + } + return; + } +} + +void Triangulate_Polygon(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,int reversed,int face_id, + int where,int color1,int color2,int color3) +{ + /* We have a polygon that we need to nonblindly triangulate. + We will recursively try to triangulate it, until we are left + with a polygon of size 4, which can use the quad routine + from above. We will be taking off a triangle at a time + and outputting it. We will have 3 cases similar to the + cases for the quad above. The inputs to this routine + are the same as for the quad routine. + */ + + int vertex4; + int *temp; + + + /* Since we are calling this recursively, we have to check whether + we are down to the case of the quad. + */ + + if (size == 4) + { + Triangulate_Quad(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,output,reversed,face_id,where,color1,color2,color3); + return; + } + + + + /* We do not have a specified input edge, and therefore we + can make it anything we like, as long as we still come out + the output edge that we want. + */ + if (reversed == -1) + { + /* Get the vertex for the last triangle, which is + the one coming out the output edge, before we do + any deletions to the list. We will be doing this + bottom up. + */ + vertex4 = Adjacent(out_edge1,out_edge2,index,size); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(out_edge2,index,&size); + Triangulate_Polygon(out_edge1,vertex4,in_edge2, + vertex4,size-1,index,output,reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + /* Lastly do the triangle that comes out the output + edge. + */ + Output_Tri(vertex4,out_edge1,out_edge2,output,color1,color2,color3,where,where); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off that comes out the correct output edge, + but we cannot use the whole polygon? + */ + if (in_edge2 == out_edge1) + { + /* Output the triangle that comes out the correct + edge last. First recursively do the rest of the + polygon. + */ + /* Do the rest of the polygon without the triangle. + We will be doing a fan triangulation. + */ + /* Get the vertex adjacent to in_edge1, but is not + in_edge2. + */ + vertex4 = Adjacent(in_edge2,in_edge1,index,size); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where); + /* Create a new edgelist without the triangle that + was just outputted. + */ + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(in_edge1,index,&size); + Triangulate_Polygon(out_edge1,out_edge2,in_edge2, + vertex4,size-1,index,output,!reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + return; + } + + /* Next case is where it is again consecutive, but the triangle + formed by the consecutive edges do not come out of the + correct output edge. For this case, we can not do much to + keep it sequential. Try and do the fan. + */ + else if (in_edge1 == out_edge1) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = Adjacent(in_edge1,in_edge2,index,size); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where,where); + /* Since that triangle goes out of the polygon (the + output edge of it), we can make our new input edge + anything we like, so we will try to make it good for + the strip. (This will be like starting a new strip, + all so that we can go out the correct output edge.) + */ + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(in_edge2,index,&size); + Triangulate_Polygon(out_edge1,out_edge2,in_edge1, + vertex4,size-1,index,output,reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + return; + } + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = Adjacent(in_edge1,in_edge2,index,size); + Output_Tri(in_edge2,in_edge1,vertex4,output,color1,color2,color3,where,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(in_edge2,index,&size); + Triangulate_Polygon(out_edge1,out_edge2,in_edge1, + vertex4,size-1,index,output,reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + return; + } + else if (in_edge2 == out_edge2) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = Adjacent(in_edge2,in_edge1,index,size); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(in_edge1,index,&size); + Triangulate_Polygon(out_edge1,out_edge2,vertex4, + in_edge2,size-1,index,output,reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + return; + } + + /* Else the edge is not consecutive, and it is sufficiently + far away, for us not to make a conclusion at this time. + So we can take off a triangle and recursively call this + function. + */ + else + { + vertex4 = Adjacent(in_edge2,in_edge1,index,size); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(in_edge1,index,&size); + Triangulate_Polygon(out_edge1,out_edge2,in_edge2, + vertex4,size-1,index,output,!reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + return; + } +} + +void Triangulate(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,int reversed,int face_id, int where, + int color1, int color2,int color3) +{ + /* We have the info we need to triangulate a polygon */ + + if (size == 4) + Triangulate_Quad(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,output,reversed,face_id,where,color1,color2,color3); + else + Triangulate_Polygon(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,output,reversed,face_id,where,color1,color2,color3); +} + +void Non_Blind_Triangulate(int size,int *index, + FILE *output,int next_face_id,int face_id,int where, + int color1,int color2,int color3) +{ + int id1,id2,id3; + int nedge1,nedge2; + int reversed; + /* We have a polygon that has to be triangulated and we cannot + do it blindly, ie we will try to come out on the edge that + has the least number of adjacencies + */ + + Last_Edge(&id1,&id2,&id3,0); + /* Find the edge that is adjacent to the new face , + also return whether the orientation is reversed in the + face of the input edge, which is id2 and id3. + */ + if (next_face_id == -1) + { + printf("The face is -1 and the size is %d\n",size); + exit(0); + } + + reversed = Get_Edge(&nedge1,&nedge2,index,next_face_id,size,id2,id3); + /* Do the triangulation */ + + /* If reversed is -1, the input edge is not in the polygon, therefore we can have the + input edge to be anything we like, since we are at the beginning + of a strip + */ + Triangulate(nedge1,nedge2,id2,id3,size,index,output,reversed, + face_id, where,color1,color2,color3); +} + + + +void Blind_Triangulate(int size, int *index, FILE *output, + BOOL begin, int where ,int color1,int color2, + int color3) +{ + /* save sides in temp array, we need it so we know + about swaps. + */ + int mode, decreasing,increasing,e1,e2,e3; + int x = 0; + BOOL flag = FALSE; + + /* Rearrange the index list so that the input edge is first + */ + if (!begin) + Rearrange_Index(index,size); + + /* We are given a polygon of more than 3 sides + and want to triangulate it. We will output the + triangles to the output file. + */ + + /* Find where the input edge is in the input list */ + Last_Edge(&e1,&e2,&e3,0); + if (( (!begin) && (*(index) == e2) ) || (begin)) + { + Output_Tri(*(index+0),*(index+1),*(index+size-1),output,color1,color2,color3,where,where); + /* If we have a quad, (chances are yes), then we know that + we can just add one diagonal and be done. (divide the + quad into 2 triangles. + */ + if (size == 4) + { + Output_Tri(*(index+1),*(index+size-1),*(index+2),output,color1,color2,color3,where,where); + return; + } + increasing = 1; + mode = 1; + + } + else if (!begin) + { + Output_Tri(*(index+1),*(index+0),*(index+size-1),output,color1,color2,color3,where,where); + if (size == 4) + { + Output_Tri(*(index+0),*(index+size-1),*(index+2),output,color1,color2,color3,where,where); + return; + } + Output_Tri(*(index+0),*(index+size-1),*(index+2),output,color1,color2,color3,where,where); + increasing = 2; + mode = 0; + } + if (size != 4) + { + /* We do not have a quad, we have something bigger. */ + decreasing = size - 1; + do + { + /* Will be alternating diagonals, so we will be increasing + and decreasing around the polygon. + */ + if (mode) + { + Output_Tri(*(index+increasing),*(index+decreasing),*(index+increasing+1),output,color1,color2,color3,where,where); + increasing++; + } + else + { + Output_Tri(*(index+decreasing),*(index+increasing),*(index+decreasing-1),output,color1,color2,color3,where,where); + decreasing--; + } + mode = !mode; + } while ((decreasing - increasing) >= 2); + + } +} + + + + diff --git a/Tools/Stripe_u/sgi_triangex.c b/Tools/Stripe_u/sgi_triangex.c new file mode 100644 index 000000000..afa2fd7db --- /dev/null +++ b/Tools/Stripe_u/sgi_triangex.c @@ -0,0 +1,584 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: sgi_triangex.c + This file contains routines that are used for various functions in + the local algorithm. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "global.h" +#include "outputex.h" +#include "polverts.h" +#include "sturctsex.h" +#include "common.h" +#include "util.h" + + +int AdjacentEx(int id2,int id1, int *list, int size) +{ + /* Return the vertex that is adjacent to id1, + but is not id2, in the list of integers. + */ + + register int x=0; + + while (x < size) + { + if (*(list+x) == id1) + { + if ((x != (size -1)) && (x != 0)) + { + if ( *(list+x+1) != id2) + return *(list+x+1); + else + return *(list+x-1); + } + else if (x == (size -1)) + { + if (*(list) != id2) + return *(list); + else + return *(list+x-1); + } + else + { + if (*(list+size-1) != id2) + return *(list+size-1); + else + return *(list+x+1); + } + } + x++; + } + printf("Error in the list\n"); + exit(0); +} + + +void Delete_From_ListEx(int id,int *list, int size) +{ + /* Delete the occurence of id in the list. + (list has size size) + */ + + int *temp; + register int x,y=0; + + temp = (int *) malloc(sizeof(int) * size); + for (x=0; x<size; x++) + { + if (*(list+x) != id) + { + *(temp+y) = *(list+x); + y++; + } + } + if(y != (size-1)) + { + printf("There is an error in the delete\n"); + exit(0); + } + *(temp+size-1) = -1; + memcpy(list,temp,sizeof(int)*size); + +} + + +void Triangulate_QuadEx(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,FILE *fp,int reversed,int face_id, + int where) +{ + int vertex4,vertex5; + + /* This routine will nonblindly triangulate a quad, meaning + that there is a definite input and a definite output + edge that we must adhere to. Reversed will tell the orientation + of the input edge. (Reversed is -1 is we do not have an input + edge, in other words we are at the beginning of a strip.) + Out_edge* is the output edge, and in_edge* is the input edge. + Index are the edges of the polygon + and size is the size of the polygon. Begin is whether we are + at the start of a new strip. + */ + + /* If we do not have an input edge, then we can make our input + edge whatever we like, therefore it will be easier to come + out on the output edge. + */ + if (reversed == -1) + { + vertex4 = AdjacentEx(out_edge1,out_edge2,index,size); + vertex5 = Get_Other_Vertex(vertex4,out_edge1,out_edge2,index); + Output_TriEx(vertex5,vertex4,out_edge1,output,-1,-1,where); + Output_TriEx(vertex4,out_edge1,out_edge2,output,-1,-1,where); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off, but cannot use the whole quad? + */ + + if (in_edge2 == out_edge1) + { + /* Output the triangle that comes out the correct + edge last. First output the triangle that comes out + the wrong edge. + */ + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge2,index); + Output_TriEx(in_edge1,in_edge2,vertex4,output,-1,-1,where); + Output_TriEx(vertex4,in_edge2,out_edge2,output,-1,-1,where); + return; + } + /* The next case is where it is impossible to come out the + edge that we want. So we will have to start a new strip to + come out on that edge. We will output the one triangle + that we can, and then start the new strip with the triangle + that comes out on the edge that we want to come out on. + */ + else if (in_edge1 == out_edge1) + { + /* We want to output the first triangle (whose output + edge is not the one that we want. + We have to find the vertex that we need, which is + the other vertex which we do not have. + */ + vertex4 = Get_Other_Vertex(in_edge2,in_edge1,out_edge2,index); + Output_TriEx(in_edge2,in_edge1,vertex4,output,-1,-1,where); + Output_TriEx(vertex4,in_edge1,out_edge2,output,-1,-1,where); + return; + } + + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_TriEx(in_edge2,in_edge1,vertex4,output,-1,-1,where); + Output_TriEx(vertex4,in_edge1,out_edge1,output,-1,-1,where); + return; + } + else if (in_edge2 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_TriEx(in_edge1,in_edge2,vertex4,output,-1,-1,where); + Output_TriEx(vertex4,in_edge2,out_edge1,output,-1,-1,where); + return; + } + + /* The final case is where we want to come out the opposite edge.*/ + else + { + if( ((!reversed) && (out_edge1 == (AdjacentEx(in_edge1,in_edge2,index,size)))) || + ((reversed) && (out_edge2 == (AdjacentEx(in_edge2,in_edge1,index,size))))) + { + /* We need to know the orientation of the input + edge, so we know which way to put the diagonal. + And also the output edge, so that we triangulate correctly. + */ + Output_TriEx(in_edge1,in_edge2,out_edge2,output,-1,-1,where); + Output_TriEx(in_edge2,out_edge2,out_edge1,output,-1,-1,where); + } + else + { + /* Input and output orientation was reversed, so diagonal will + be reversed from above. + */ + Output_TriEx(in_edge1,in_edge2,out_edge1,output,-1,-1,where); + Output_TriEx(in_edge2,out_edge1,out_edge2,output,-1,-1,where); + } + return; + } +} + +void Triangulate_PolygonEx(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,FILE *fp,int reversed,int face_id, + int where) +{ + /* We have a polygon that we need to nonblindly triangulate. + We will recursively try to triangulate it, until we are left + with a polygon of size 4, which can use the quad routine + from above. We will be taking off a triangle at a time + and outputting it. We will have 3 cases similar to the + cases for the quad above. The inputs to this routine + are the same as for the quad routine. + */ + + int vertex4; + int *temp; + + + /* Since we are calling this recursively, we have to check whether + we are down to the case of the quad. + */ + + if (size == 4) + { + Triangulate_QuadEx(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,output,fp,reversed,face_id,where); + return; + } + + + + /* We do not have a specified input edge, and therefore we + can make it anything we like, as long as we still come out + the output edge that we want. + */ + if (reversed == -1) + { + /* Get the vertex for the last triangle, which is + the one coming out the output edge, before we do + any deletions to the list. We will be doing this + bottom up. + */ + vertex4 = AdjacentEx(out_edge1,out_edge2,index,size); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(out_edge2,index,size); + Triangulate_PolygonEx(out_edge1,vertex4,in_edge2, + vertex4,size-1,index,output,fp,reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + /* Lastly do the triangle that comes out the output + edge. + */ + Output_TriEx(vertex4,out_edge1,out_edge2,output,-1,-1,where); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off that comes out the correct output edge, + but we cannot use the whole polygon? + */ + if (in_edge2 == out_edge1) + { + /* Output the triangle that comes out the correct + edge last. First recursively do the rest of the + polygon. + */ + /* Do the rest of the polygon without the triangle. + We will be doing a fan triangulation. + */ + /* Get the vertex adjacent to in_edge1, but is not + in_edge2. + */ + vertex4 = AdjacentEx(in_edge2,in_edge1,index,size); + Output_TriEx(in_edge1,in_edge2,vertex4,output,-1,-1,where); + /* Create a new edgelist without the triangle that + was just outputted. + */ + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(in_edge1,index,size); + Triangulate_PolygonEx(out_edge1,out_edge2,in_edge2, + vertex4,size-1,index,output,fp,!reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + return; + } + + /* Next case is where it is again consecutive, but the triangle + formed by the consecutive edges do not come out of the + correct output edge. For this case, we can not do much to + keep it sequential. Try and do the fan. + */ + else if (in_edge1 == out_edge1) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = AdjacentEx(in_edge1,in_edge2,index,size); + Output_TriEx(in_edge1,in_edge2,vertex4,fp,-1,-1,where); + /* Since that triangle goes out of the polygon (the + output edge of it), we can make our new input edge + anything we like, so we will try to make it good for + the strip. (This will be like starting a new strip, + all so that we can go out the correct output edge.) + */ + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(in_edge2,index,size); + Triangulate_PolygonEx(out_edge1,out_edge2,in_edge1, + vertex4,size-1,index,output,fp,reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + return; + } + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = AdjacentEx(in_edge1,in_edge2,index,size); + Output_TriEx(in_edge2,in_edge1,vertex4,fp,-1,-1,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(in_edge2,index,size); + Triangulate_PolygonEx(out_edge1,out_edge2,in_edge1, + vertex4,size-1,index,output,fp,reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + return; + } + else if (in_edge2 == out_edge2) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = AdjacentEx(in_edge2,in_edge1,index,size); + Output_TriEx(in_edge1,in_edge2,vertex4,fp,-1,-1,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(in_edge1,index,size); + Triangulate_PolygonEx(out_edge1,out_edge2,vertex4, + in_edge2,size-1,index,output,fp,reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + return; + } + + /* Else the edge is not consecutive, and it is sufficiently + far away, for us not to make a conclusion at this time. + So we can take off a triangle and recursively call this + function. + */ + else + { + vertex4 = AdjacentEx(in_edge2,in_edge1,index,size); + Output_TriEx(in_edge1,in_edge2,vertex4,fp,-1,-1,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(in_edge1,index,size); + Triangulate_PolygonEx(out_edge1,out_edge2,in_edge2, + vertex4,size-1,index,output,fp,!reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + return; + } +} + +void TriangulateEx(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *fp,FILE *output,int reversed,int face_id, int where) +{ + /* We have the info we need to triangulate a polygon */ + + if (size == 4) + Triangulate_QuadEx(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,fp,output,reversed,face_id,where); + else + Triangulate_PolygonEx(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,fp,output,reversed,face_id,where); +} + +void Non_Blind_TriangulateEx(int size,int *index, FILE *fp, + FILE *output,int next_face_id,int face_id,int where) +{ + int id1,id2,id3; + int nedge1,nedge2; + int reversed; + /* We have a polygon that has to be triangulated and we cannot + do it blindly, ie we will try to come out on the edge that + has the least number of adjacencies + */ + + Last_Edge(&id1,&id2,&id3,0); + /* Find the edge that is adjacent to the new face , + also return whether the orientation is reversed in the + face of the input edge, which is id2 and id3. + */ + if (next_face_id == -1) + { + printf("The face is -1 and the size is %d\n",size); + exit(0); + } + + reversed = Get_EdgeEx(&nedge1,&nedge2,index,next_face_id,size,id2,id3); + /* Do the triangulation */ + + /* If reversed is -1, the input edge is not in the polygon, therefore we can have the + input edge to be anything we like, since we are at the beginning + of a strip + */ + TriangulateEx(nedge1,nedge2,id2,id3,size,index,fp,output,reversed, + face_id, where); +} + +void Rearrange_IndexEx(int *index, int size) +{ + /* If we are in the middle of a strip we must find the + edge to start on, which is the last edge that we had + transmitted. + */ + int x,f,y,e1,e2,e3; + int increment = 1; + int *temp; + + /* Find where the input edge is in the input list */ + Last_Edge(&e1,&e2,&e3,0); + for (y = 0; y < size; y++) + { + if (*(index+y) == e2) + { + if ((y != (size - 1)) && (*(index+y+1) == e3)) + break; + else if ((y == (size - 1)) && (*(index) == e3)) + break; + else if ((y != 0) && (*(index+y-1) == e3)) + { + increment = -1; + break; + } + else if ((y==0) && (*(index+size-1) == e3)) + { + increment = -1; + break; + } + } + if (*(index+y) == e3) + { + if ((y != (size - 1)) && (*(index+y+1) == e2)) + break; + else if ((y == (size - 1)) && (*(index) == e2)) + break; + else if ((y != 0) && (*(index+y-1) == e2)) + { + increment = -1; + break; + } + else if ((y==0) && (*(index+size-1) == e2)) + { + increment = -1; + break; + } + } + /* Edge is not here, we are at the beginning */ + if ((y == (size-1)) && (increment != -1)) + return; + } + + /* Now put the list into a new list, starting with the + input edge. Increment tells us whether we have to go + forward or backward. + */ + /* Was in good position already */ + if ((y == 0) && (increment == 1)) + return; + + + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + + if (increment == 1) + { + x=0; + for (f = y ; f< size; f++) + { + *(index+x) = *(temp+f); + x++; + } + /* Finish the rest of the list */ + for(f = 0; f < y ; f++) + { + *(index+x) = *(temp+f); + x++; + } + } + else + { + x=0; + for (f = y ; f >= 0; f--) + { + *(index+x) = *(temp+f); + x++; + } + /* Finish the rest of the list */ + for(f = (size - 1); f > y ; f--) + { + *(index+x) = *(temp+f); + x++; + } + } +} + +void Blind_TriangulateEx(int size, int *index, FILE *fp, + FILE *output, BOOL begin, int where ) +{ + /* save sides in temp array, we need it so we know + about swaps. + */ + int mode, decreasing,increasing,e1,e2,e3; + int x = 0; + BOOL flag = FALSE; + + /* Rearrange the index list so that the input edge is first + */ + if (!begin) + Rearrange_IndexEx(index,size); + + /* We are given a polygon of more than 3 sides + and want to triangulate it. We will output the + triangles to the output file. + */ + + /* Find where the input edge is in the input list */ + Last_Edge(&e1,&e2,&e3,0); + if (( (!begin) && (*(index) == e2) ) || (begin)) + { + Output_TriEx(*(index+0),*(index+1),*(index+size-1),fp,-1,-1,where); + /* If we have a quad, (chances are yes), then we know that + we can just add one diagonal and be done. (divide the + quad into 2 triangles. + */ + if (size == 4) + { + Output_TriEx(*(index+1),*(index+size-1),*(index+2),fp,-1,-1,where); + return; + } + increasing = 1; + mode = 1; + + } + else if (!begin) + { + Output_TriEx(*(index+1),*(index+0),*(index+size-1),fp,-1,-1,where); + if (size == 4) + { + Output_TriEx(*(index+0),*(index+size-1),*(index+2),fp,-1,-1,where); + return; + } + Output_TriEx(*(index+0),*(index+size-1),*(index+2),fp,-1,-1,where); + increasing = 2; + mode = 0; + } + if (size != 4) + { + /* We do not have a quad, we have something bigger. */ + decreasing = size - 1; + + do + { + /* Will be alternating diagonals, so we will be increasing + and decreasing around the polygon. + */ + if (mode) + { + Output_TriEx(*(index+increasing),*(index+decreasing),*(index+increasing+1),fp,-1,-1,where); + increasing++; + } + else + { + Output_TriEx(*(index+decreasing),*(index+increasing),*(index+decreasing-1),fp,-1,-1,where); + decreasing--; + } + mode = !mode; + } while ((decreasing - increasing) >= 2); + + } +} + + diff --git a/Tools/Stripe_u/struct.c b/Tools/Stripe_u/struct.c new file mode 100644 index 000000000..f822b1da7 --- /dev/null +++ b/Tools/Stripe_u/struct.c @@ -0,0 +1,549 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: struct.c + Contains routines that update structures, and micellaneous routines. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdlib.h> +#include <string.h> +#include "polverts.h" +#include "ties.h" +#include "output.h" +#include "triangulate.h" +#include "sturcts.h" +#include "options.h" +#include "common.h" +#include "util.h" + +int out1 = -1; +int out2 = -1; + +int Get_Edge(int *edge1,int *edge2,int *index,int face_id, + int size, int id1, int id2) +{ + /* Put the edge that is adjacent to face_id into edge1 + and edge2. For each edge see if it is adjacent to + face_id. Id1 and id2 is the input edge, so see if + the orientation is reversed, and save it in reversed. + */ + register int x; + int reversed = -1; + BOOL set = FALSE; + + for (x=0; x< size; x++) + { + if (x == (size-1)) + { + if ((*(index) == id1) && (*(index+size-1)==id2)) + { + if (set) + return 1; + reversed = 1; + } + else if ((*(index) == id2) && (*(index+size-1)==id1)) + { + if (set) + return 0; + reversed = 0; + } + + if (Look_Up(*(index),*(index+size-1),face_id)) + { + if ( (out1 != -1) && ( (out1 == *(index)) || (out1 == *(index+size-1)) ) && + ( (out2 == *(index)) || (out2 == *(index+size-1)) )) + { + set = TRUE; + *edge1 = *(index); + *edge2 = *(index+size-1); + } + else if (out1 == -1) + { + set = TRUE; + *edge1 = *(index); + *edge2 = *(index+size-1); + } + if ((reversed != -1) && (set)) + return reversed; + } + } + else + { + if ((*(index+x) == id1) && (*(index+x+1)==id2)) + { + if (set) + return 0; + reversed = 0; + } + else if ((*(index+x) == id2) && (*(index+x+1)==id1)) + { + if (set) + return 1; + reversed = 1; + } + + if (Look_Up(*(index+x),*(index+x+1),face_id)) + { + if ( (out1 != -1) && ( (out1 == *(index+x)) || (out1 == *(index+x+1)) ) && + ((out2 == *(index+x)) || (out2 == *(index+x+1)))) + { + set = TRUE; + *edge1 = *(index+x); + *edge2 = *(index+x+1); + } + else if (out1 == -1) + { + set = TRUE; + *edge1 = *(index+x); + *edge2 = *(index+x + 1); + } + if ((reversed != -1) && (set)) + return reversed; + } + } + } + if ((x == size) && (reversed != -1)) + { + /* Could not find the output edge */ + printf("Error in the Lookup %d %d %d %d %d %d %d %d\n",face_id,id1,id2,reversed,*edge1,*edge2,out1,out2); + exit(0); + } + return reversed; +} + + +void Update_Face(int *next_bucket, int *min_face, int face_id, int *e1, + int *e2,int temp1,int temp2,int *ties) +{ + /* We have a face id that needs to be decremented. + We have to determine where it is in the structure, + so that we can decrement it. + */ + /* The number of adjacencies may have changed, so to locate + it may be a little tricky. However we know that the number + of adjacencies is less than or equal to the original number + of adjacencies, + */ + int y,size,tally=0; + ListHead *pListHead; + PF_FACES temp = NULL; + PLISTINFO lpListInfo; + static int each_poly = 0; + BOOL there = FALSE; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp != NULL ) + { + /* Size of the polygon */ + size = temp->nPolSize; + /* We did it already */ + if (size == 1) + return; + for (y = 0; y< size; y++) + { + /* If we are doing partial triangulation, we must check + to see whether the edge is still there in the polygon, + since we might have done a portion of the polygon + and saved the rest for later. + */ + if (y != (size-1)) + { + if( ((temp1 == *(temp->pPolygon+y)) && (temp2 ==*(temp->pPolygon+y+1))) + || ((temp2 == *(temp->pPolygon+y)) && (temp1 ==*(temp->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((temp1 == *(temp->pPolygon)) && (temp2 == *(temp->pPolygon+size-1))) + || ((temp2 == *(temp->pPolygon)) && (temp1 ==*(temp->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + + if (!there) + /* Original edge was already used, we cannot use this polygon */ + return; + + /* We have a starting point to start our search to locate + this polygon. + */ + + /* Check to see if this polygon was done */ + lpListInfo = Done(face_id,59,&y); + + if (lpListInfo == NULL) + return; + + /* Was not done, but there is an error in the adjacency calculations */ + if (y == 0) + { + printf("There is an error in finding the adjacencies\n"); + exit(0); + } + + /* Now put the face in the proper bucket depending on tally. */ + /* First add it to the new bucket, then remove it from the old */ + Add_Sgi_Adj(y-1,face_id); + RemoveList(array[y],lpListInfo); + + /* Save it if it was the smallest seen so far since then + it will be the next face + Here we will have different options depending on + what we want for resolving ties: + 1) First one we see we will use + 2) Random resolving + 3) Look ahead + 4) Alternating direction + */ + /* At a new strip */ + if (*next_bucket == 60) + *ties = *ties + each_poly; + /* Have a tie */ + if (*next_bucket == (y-1)) + { + Add_Ties(face_id); + each_poly++; + } + /* At a new minimum */ + if (*next_bucket > (y-1)) + { + *next_bucket = y-1; + *min_face = face_id; + *e1 = temp1; + *e2 = temp2; + each_poly = 0; + Clear_Ties(); + Add_Ties(face_id); + } + } +} + + +void Delete_Adj(int id1, int id2,int *next_bucket,int *min_face, + int current_face,int *e1,int *e2,int *ties) +{ + /* Find the face that is adjacent to the edge and is not the + current face. Delete one adjacency from it. Save the min + adjacency seen so far. + */ + register int count=0; + PF_EDGES temp = NULL; + ListHead *pListHead; + int next_face; + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* It could be a new edge that we created. So we can + exit, since there is not a face adjacent to it. + */ + return; + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* Was a new edge that was created and therefore + does not have anything adjacent to it + */ + return; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return; + + /* Was adjacent to something */ + else + { + if (temp->edge[2] == current_face) + next_face = temp->edge[1]; + else + next_face = temp->edge[2]; + } + /* We have the other face adjacent to this edge, it is + next_face. Now we need to decrement this faces' adjacencies. + */ + Update_Face(next_bucket, min_face, next_face,e1,e2,id1,id2,ties); +} + + +int Change_Face(int face_id,int in1,int in2, + ListHead *pListHead, P_ADJACENCIES temp, BOOL no_check) +{ + /* We are doing a partial triangulation and we need to + put the new face of triangle into the correct bucket + */ + int input_adj,y; + + /* Find the old number of adjacencies to this face, + so we know where to delete it from + */ + y = Old_Adj(face_id); + + /* Do we need to change the adjacency? Maybe the edge on the triangle + that was outputted was not adjacent to anything. We know if we + have to check by "check". We came out on the output edge + that we needed, then we know that the adjacencies will decrease + by exactly one. + */ + if (!no_check) + { + input_adj = Number_Adj(in1,in2,face_id); + /* If there weren't any then don't do anything */ + if (input_adj == 0) + return y; + } + + RemoveList(pListHead,(PLISTINFO)temp); + /* Before we had a quad with y adjacencies. The in edge + did not have an adjacency, since it was just deleted, + since we came in on it. The outedge must have an adjacency + otherwise we would have a bucket 0, and would not be in this + routine. Therefore the new adjacency must be y-1 + */ + + Add_Sgi_Adj(y-1,face_id); + return (y-1); +} + +int Update_Adjacencies(int face_id, int *next_bucket, int *e1, int *e2, + int *ties) +{ + /* Give the face with id face_id, we want to decrement + all the faces that are adjacent to it, since we will + be deleting face_id from the data structure. + We will return the face that has the least number + of adjacencies. + */ + PF_FACES temp = NULL; + ListHead *pListHead; + int size,y,min_face = -1; + + *next_bucket = 60; + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + if ( temp == NULL ) + { + printf("The face was already deleted, there is an error\n"); + exit(0); + } + + /* Size of the polygon */ + size = temp->nPolSize; + for (y = 0; y< size; y++) + { + if (y != (size-1)) + Delete_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1), + next_bucket,&min_face,face_id,e1,e2,ties); + else + Delete_Adj(*(temp->pPolygon),*(temp->pPolygon+(size-1)), + next_bucket,&min_face,face_id,e1,e2,ties); + } + return (min_face); +} + + +void Find_Adj_Tally(int id1, int id2,int *next_bucket,int *min_face, + int current_face,int *ties) +{ + /* Find the face that is adjacent to the edge and is not the + current face. Save the min adjacency seen so far. + */ + int size,each_poly=0,y,tally=0,count=0; + PF_EDGES temp = NULL; + PF_FACES temp2 = NULL; + ListHead *pListHead; + int next_face; + BOOL there = FALSE; + + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* This was a new edge that was created, so it is + adjacent to nothing. + */ + return; + + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* This was a new edge that we created */ + return; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return; + else + { + if (temp->edge[2] == current_face) + next_face = temp->edge[1]; + else + next_face = temp->edge[2]; + } + /* We have the other face adjacent to this edge, it is + next_face. Find how many faces it is adjacent to. + */ + pListHead = PolFaces[next_face]; + temp2 = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. This will be the original number of + polygons adjacent to this polygon, we must then see if this + number has been decremented + */ + if ( temp2 != NULL ) + { + /* Size of the polygon */ + size = temp2->nPolSize; + /* We did it already */ + if (size == 1) + return; + for (y = 0; y< size; y++) + { + /* Make sure that the edge is still in the + polygon and was not deleted, because if the edge was + deleted, then we used it already. + */ + if (y != (size-1)) + { + if( ((id1 == *(temp2->pPolygon+y)) && (id2 ==*(temp2->pPolygon+y+1))) + || ((id2 == *(temp2->pPolygon+y)) && (id1 ==*(temp2->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((id1 == *(temp2->pPolygon)) && (id2 ==*(temp2->pPolygon+size-1))) + || ((id2 == *(temp2->pPolygon)) && (id1 ==*(temp2->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + + if (!there) + /* Edge already used and deleted from the polygon*/ + return; + + /* See if the face was already deleted, and where + it is if it was not + */ + if (Done(next_face,size,&y) == NULL) + return; + + /* Save it if it was the smallest seen so far since then + it will be the next face + Here we will have different options depending on + what we want for resolving ties: + 1) First one we see we will use + 2) Random resolving + 3) Look ahead + 4) Alternating direction + */ + + /* At a new strip */ + if (*next_bucket == 60) + *ties = *ties + each_poly; + /* Have a tie */ + if (*next_bucket == (y-1)) + { + Add_Ties(next_face); + each_poly++; + } + /* At a new minimum */ + if (*next_bucket > (y-1)) + { + *next_bucket = y-1; + *min_face = next_face; + each_poly = 0; + Clear_Ties(); + Add_Ties(next_face); + } + } +} + + +int Min_Face_Adj(int face_id, int *next_bucket, int *ties) +{ + /* Used for the Partial triangulation to find the next + face. It will return the minimum adjacency face id + found at this face. + */ + PF_FACES temp = NULL; + ListHead *pListHead; + int size,y,min_face,test_face; + + *next_bucket = 60; + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + if ( temp == NULL ) + { + printf("The face was already deleted, there is an error\n"); + exit(0); + } + + /* Size of the polygon */ + size = temp->nPolSize; + for (y = 0; y< size; y++) + { + if (y != (size-1)) + Find_Adj_Tally(*(temp->pPolygon+y),*(temp->pPolygon+y+1), + next_bucket,&min_face,face_id,ties); + else + Find_Adj_Tally(*(temp->pPolygon),*(temp->pPolygon+(size-1)), + next_bucket,&min_face,face_id,ties); + } + /* Maybe we can do better by triangulating the face, because + by triangulating the face we will go to a polygon of lesser + adjacencies + */ + if (size == 4) + { + /* Checking for a quad whether to do the whole polygon will + result in better performance because the triangles in the polygon + have less adjacencies + */ + Check_In_Quad(face_id,&test_face); + if (*next_bucket > test_face) + /* We can do better by going through the polygon */ + min_face = face_id; + } + + /* We have a polygon with greater than 4 sides, check to see if going + inside is better than going outside the polygon for the output edge. + */ + else + { + Check_In_Polygon(face_id,&test_face,size); + if (*next_bucket > test_face) + /* We can do better by going through the polygon */ + min_face = face_id; + } + + return (min_face); +} + + + diff --git a/Tools/Stripe_u/struct.h b/Tools/Stripe_u/struct.h new file mode 100644 index 000000000..6eb036cb4 --- /dev/null +++ b/Tools/Stripe_u/struct.h @@ -0,0 +1,6 @@ + +struct vert_struct { + VRDATA x, y, z; /* point coordinates */ +}; + + diff --git a/Tools/Stripe_u/structex.c b/Tools/Stripe_u/structex.c new file mode 100644 index 000000000..06adba7b5 --- /dev/null +++ b/Tools/Stripe_u/structex.c @@ -0,0 +1,553 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: structex.c + This file contains routines that are used for various functions in + the local algorithm. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdlib.h> +#include <string.h> +#include "polverts.h" +#include "ties.h" +#include "outputex.h" +#include "triangulatex.h" +#include "sturctsex.h" +#include "options.h" +#include "common.h" +#include "util.h" + +int out1Ex = -1; +int out2Ex = -1; + +int Get_EdgeEx(int *edge1,int *edge2,int *index,int face_id, + int size, int id1, int id2) +{ + /* Put the edge that is adjacent to face_id into edge1 + and edge2. For each edge see if it is adjacent to + face_id. Id1 and id2 is the input edge, so see if + the orientation is reversed, and save it in reversed. + */ + int x; + int reversed = -1; + BOOL set = FALSE; + + for (x=0; x< size; x++) + { + if (x == (size-1)) + { + if ((*(index) == id1) && (*(index+size-1)==id2)) + { + if (set) + return 1; + reversed = 1; + } + else if ((*(index) == id2) && (*(index+size-1)==id1)) + { + if (set) + return 0; + reversed = 0; + } + + if (Look_Up(*(index),*(index+size-1),face_id)) + { + if ( (out1Ex != -1) && ( (out1Ex == *(index)) || (out1Ex == *(index+size-1)) ) && + ( (out2Ex == *(index)) || (out2Ex == *(index+size-1)) )) + { + set = TRUE; + *edge1 = *(index); + *edge2 = *(index+size-1); + } + else if (out1Ex == -1) + { + set = TRUE; + *edge1 = *(index); + *edge2 = *(index+size-1); + } + if ((reversed != -1) && (set)) + return reversed; + } + } + else + { + if ((*(index+x) == id1) && (*(index+x+1)==id2)) + { + if (set) + return 0; + reversed = 0; + } + else if ((*(index+x) == id2) && (*(index+x+1)==id1)) + { + if (set) + return 1; + reversed = 1; + } + + if (Look_Up(*(index+x),*(index+x+1),face_id)) + { + if ( (out1Ex != -1) && ( (out1Ex == *(index+x)) || (out1Ex == *(index+x+1)) ) && + ((out2Ex == *(index+x)) || (out2Ex == *(index+x+1)))) + { + set = TRUE; + *edge1 = *(index+x); + *edge2 = *(index+x+1); + } + else if (out1Ex == -1) + { + set = TRUE; + *edge1 = *(index+x); + *edge2 = *(index+x + 1); + } + if ((reversed != -1) && (set)) + return reversed; + } + } + } + if ((x == size) && (reversed != -1)) + { + /* Could not find the output edge */ + printf("Error in the Lookup %d %d %d %d %d %d %d %d\n",face_id,id1,id2,reversed,*edge1,*edge2,out1Ex,out2Ex); + exit(0); + } + return reversed; +} + + +void Update_FaceEx(int *next_bucket, int *min_face, int face_id, int *e1, + int *e2,int temp1,int temp2,int *ties) +{ + /* We have a face id that needs to be decremented. + We have to determine where it is in the structure, + so that we can decrement it. + */ + /* The number of adjacencies may have changed, so to locate + it may be a little tricky. However we know that the number + of adjacencies is less than or equal to the original number + of adjacencies, + */ + int y,size,tally=0; + ListHead *pListHead; + PF_FACES temp = NULL; + PLISTINFO lpListInfo; + static int each_poly = 0; + BOOL there = FALSE; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp != NULL ) + { + /* Size of the polygon */ + size = temp->nPolSize; + for (y = 0; y< size; y++) + { + /* If we are doing partial triangulation, we must check + to see whether the edge is still there in the polygon, + since we might have done a portion of the polygon + and saved the rest for later. + */ + if (y != (size-1)) + { + if( ((temp1 == *(temp->pPolygon+y)) && (temp2 ==*(temp->pPolygon+y+1))) + || ((temp2 == *(temp->pPolygon+y)) && (temp1 ==*(temp->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((temp1 == *(temp->pPolygon)) && (temp2 == *(temp->pPolygon+size-1))) + || ((temp2 == *(temp->pPolygon)) && (temp1 ==*(temp->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + + if (!there) + /* Original edge was already used, we cannot use this polygon */ + return; + + /* We have a starting point to start our search to locate + this polygon. + */ + + /* Check to see if this polygon was done */ + lpListInfo = Done(face_id,59,&y); + + if (lpListInfo == NULL) + return; + + /* Was not done, but there is an error in the adjacency calculations */ + /* If more than one edge is adj to it then maybe it was not updated */ + if (y == 0) + return; + + /* Now put the face in the proper bucket depending on tally. */ + /* First add it to the new bucket, then remove it from the old */ + Add_Sgi_Adj(y-1,face_id); + RemoveList(array[y],lpListInfo); + + /* Save it if it was the smallest seen so far since then + it will be the next face + Here we will have different options depending on + what we want for resolving ties: + 1) First one we see we will use + 2) Random resolving + 3) Look ahead + 4) Alternating direction + */ + /* At a new strip */ + if (*next_bucket == 60) + *ties = *ties + each_poly; + /* Have a tie */ + if (*next_bucket == (y-1)) + { + Add_Ties(face_id); + each_poly++; + } + /* At a new minimum */ + if (*next_bucket > (y-1)) + { + *next_bucket = y-1; + *min_face = face_id; + *e1 = temp1; + *e2 = temp2; + each_poly = 0; + Clear_Ties(); + Add_Ties(face_id); + } + } +} + + +void Delete_AdjEx(int id1, int id2,int *next_bucket,int *min_face, + int current_face,int *e1,int *e2,int *ties) +{ + /* Find the face that is adjacent to the edge and is not the + current face. Delete one adjacency from it. Save the min + adjacency seen so far. + */ + register int count=0; + PF_EDGES temp = NULL; + ListHead *pListHead; + int next_face; + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* It could be a new edge that we created. So we can + exit, since there is not a face adjacent to it. + */ + return; + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* Was a new edge that was created and therefore + does not have anything adjacent to it + */ + return; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return; + + /* Was adjacent to something */ + else + { + if (temp->edge[2] == current_face) + next_face = temp->edge[1]; + else + next_face = temp->edge[2]; + } + /* We have the other face adjacent to this edge, it is + next_face. Now we need to decrement this faces' adjacencies. + */ + Update_FaceEx(next_bucket, min_face, next_face,e1,e2,id1,id2,ties); +} + +int Change_FaceEx(int face_id,int in1,int in2, + ListHead *pListHead, P_ADJACENCIES temp, BOOL no_check) +{ + /* We are doing a partial triangulation and we need to + put the new face of triangle into the correct bucket + */ + int input_adj,y; + P_ADJACENCIES pfNode,lpListInfo; + + /* Find the old number of adjacencies to this face, + so we know where to delete it from + */ + y = Old_Adj(face_id); + pListHead = array[y]; + + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[y], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon3 %d\n",face_id); + exit(0); + } + + /* Do we need to change the adjacency? Maybe the edge on the triangle + that was outputted was not adjacent to anything. We know if we + have to check by "check". We came out on the output edge + that we needed, then we know that the adjacencies will decrease + by exactly one. + */ + if (!no_check) + { + input_adj = Number_Adj(in1,in2,face_id); + /* If there weren't any then don't do anything */ + if (input_adj == 0) + return y; + } + + RemoveList(pListHead,(PLISTINFO)/*(temp*/lpListInfo); + /* Before we had a quad with y adjacencies. The in edge + did not have an adjacency, since it was just deleted, + since we came in on it. The outedge must have an adjacency + otherwise we would have a bucket 0, and would not be in this + routine. Therefore the new adjacency must be y-1 + */ + + Add_Sgi_Adj(y-1,face_id); + return (y-1); +} + +int Update_AdjacenciesEx(int face_id, int *next_bucket, int *e1, int *e2, + int *ties) +{ + /* Give the face with id face_id, we want to decrement + all the faces that are adjacent to it, since we will + be deleting face_id from the data structure. + We will return the face that has the least number + of adjacencies. + */ + PF_FACES temp = NULL; + ListHead *pListHead; + int size,y,min_face = -1; + + *next_bucket = 60; + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + if ( temp == NULL ) + { + printf("The face was already deleted, there is an error\n"); + exit(0); + } + + /* Size of the polygon */ + size = temp->nPolSize; + for (y = 0; y< size; y++) + { + if (y != (size-1)) + Delete_AdjEx(*(temp->pPolygon+y),*(temp->pPolygon+y+1), + next_bucket,&min_face,face_id,e1,e2,ties); + else + Delete_AdjEx(*(temp->pPolygon),*(temp->pPolygon+(size-1)), + next_bucket,&min_face,face_id,e1,e2,ties); + } + return (min_face); +} + + + +void Find_Adj_TallyEx(int id1, int id2,int *next_bucket,int *min_face, + int current_face,int *ties) +{ + /* Find the face that is adjacent to the edge and is not the + current face. Save the min adjacency seen so far. + */ + int size,each_poly=0,y,tally=0,count=0; + PF_EDGES temp = NULL; + PF_FACES temp2 = NULL; + ListHead *pListHead; + int next_face; + BOOL there = FALSE; + + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* This was a new edge that was created, so it is + adjacent to nothing. + */ + return; + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* This was a new edge that we created */ + return; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return; + else + { + if (temp->edge[2] == current_face) + next_face = temp->edge[1]; + else + next_face = temp->edge[2]; + } + /* We have the other face adjacent to this edge, it is + next_face. Find how many faces it is adjacent to. + */ + pListHead = PolFaces[next_face]; + temp2 = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. This will be the original number of + polygons adjacent to this polygon, we must then see if this + number has been decremented + */ + if ( temp2 != NULL ) + { + /* Size of the polygon */ + size = temp2->nPolSize; + for (y = 0; y< size; y++) + { + /* Make sure that the edge is still in the + polygon and was not deleted, because if the edge was + deleted, then we used it already. + */ + if (y != (size-1)) + { + if( ((id1 == *(temp2->pPolygon+y)) && (id2 ==*(temp2->pPolygon+y+1))) + || ((id2 == *(temp2->pPolygon+y)) && (id1 ==*(temp2->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((id1 == *(temp2->pPolygon)) && (id2 ==*(temp2->pPolygon+size-1))) + || ((id2 == *(temp2->pPolygon)) && (id1 ==*(temp2->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + + if (!there) + /* Edge already used and deleted from the polygon*/ + return; + + /* See if the face was already deleted, and where + it is if it was not + */ + if (Done(next_face,size,&y) == NULL) + return; + + /* Save it if it was the smallest seen so far since then + it will be the next face + Here we will have different options depending on + what we want for resolving ties: + 1) First one we see we will use + 2) Random resolving + 3) Look ahead + 4) Alternating direction + */ + + /* At a new strip */ + if (*next_bucket == 60) + *ties = *ties + each_poly; + /* Have a tie */ + if (*next_bucket == (y-1)) + { + Add_Ties(next_face); + each_poly++; + } + /* At a new minimum */ + if (*next_bucket > (y-1)) + { + *next_bucket = y-1; + *min_face = next_face; + each_poly = 0; + Clear_Ties(); + Add_Ties(next_face); + } + } +} + + +int Min_Face_AdjEx(int face_id, int *next_bucket, int *ties) +{ + /* Used for the Partial triangulation to find the next + face. It will return the minimum adjacency face id + found at this face. + */ + PF_FACES temp = NULL; + ListHead *pListHead; + int size,y,min_face,test_face; + + *next_bucket = 60; + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + if ( temp == NULL ) + { + printf("The face was already deleted, there is an error\n"); + exit(0); + } + + /* Size of the polygon */ + size = temp->nPolSize; + for (y = 0; y< size; y++) + { + if (y != (size-1)) + Find_Adj_TallyEx(*(temp->pPolygon+y),*(temp->pPolygon+y+1), + next_bucket,&min_face,face_id,ties); + else + Find_Adj_TallyEx(*(temp->pPolygon),*(temp->pPolygon+(size-1)), + next_bucket,&min_face,face_id,ties); + } + /* Maybe we can do better by triangulating the face, because + by triangulating the face we will go to a polygon of lesser + adjacencies + */ + if (size == 4) + { + /* Checking for a quad whether to do the whole polygon will + result in better performance because the triangles in the polygon + have less adjacencies + */ + Check_In_Quad(face_id,&test_face); + if (*next_bucket > test_face) + /* We can do better by going through the polygon */ + min_face = face_id; + } + + /* We have a polygon with greater than 4 sides, check to see if going + inside is better than going outside the polygon for the output edge. + */ + else + { + Check_In_Polygon(face_id,&test_face,size); + if (*next_bucket > test_face) + /* We can do better by going through the polygon */ + min_face = face_id; + } + + return (min_face); +} + + diff --git a/Tools/Stripe_u/sturcts.h b/Tools/Stripe_u/sturcts.h new file mode 100644 index 000000000..07a2bbf97 --- /dev/null +++ b/Tools/Stripe_u/sturcts.h @@ -0,0 +1,31 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: sturcts.h +-----------------------------------------------------------------------*/ + +#define EVEN(x) (((x) & 1) == 0) + +BOOL Get_Edge(); +void add_vert_id(); +void Update_Face(); +int Min_Adj(); +int Min_Face_Adj(); +int Change_Face(); +void Delete_Adj(); +int Update_Adjacencies(); +int Get_Output_Edge(); +int Find_Face(); + + + + + + + diff --git a/Tools/Stripe_u/sturctsex.h b/Tools/Stripe_u/sturctsex.h new file mode 100644 index 000000000..550228306 --- /dev/null +++ b/Tools/Stripe_u/sturctsex.h @@ -0,0 +1,28 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE:sturctsex.h +-----------------------------------------------------------------------*/ + +#define EVEN(x) (((x) & 1) == 0) + +BOOL Get_EdgeEx(); +void add_vert_idEx(); +void Update_FaceEx(); +int Min_Face_AdjEx(); +int Change_FaceEx(); +void Delete_AdjEx(); +int Number_AdjEx(); +int Update_AdjacenciesEx(); + + + + + + diff --git a/Tools/Stripe_u/ties.c b/Tools/Stripe_u/ties.c new file mode 100644 index 000000000..e1a64545c --- /dev/null +++ b/Tools/Stripe_u/ties.c @@ -0,0 +1,304 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: ties.c + This file will contain all the routines used to determine the next face if there + is a tie +*/ +/*---------------------------------------------------------------------*/ + +#include <stdlib.h> +#include "polverts.h" +#include "ties.h" +#include "sturctsex.h" +#include "triangulatex.h" +#include "options.h" +#include "common.h" +#include "util.h" + +#define MAX_TIE 60 +int ties_array[60]; +int last = 0; + +void Clear_Ties() +{ + /* Clear the buffer, because we do not have the tie + any more that we had before */ + last = 0; +} + +void Add_Ties(int id) +{ + /* We have a tie to add to the buffer */ + ties_array[last++] = id; +} + +int Alternate_Tie() +{ + /* Alternate in what we choose to break the tie + We are just alternating between the first and + second thing that we found + */ + static int x = 0; + register int t; + + t = ties_array[x]; + x++; + if (x == 2) + x = 0; + return t; +} + +int Random_Tie() +{ + /* Randomly choose the next face with which + to break the tie + */ + register int num; + + num = rand(); + while (num >= last) + num = num/20; + return (ties_array[num]); +} + +int Look_Ahead(int id) +{ + /* Look ahead at this face and save the minimum + adjacency of all the faces that are adjacent to + this face. + */ + return Min_Adj(id); +} + +int Random_Look(int id[],int count) +{ + /* We had a tie within a tie in the lookahead, + break it randomly + */ + register int num; + + num = rand(); + while (num >= count) + num = num/20; + return (id[num]); +} + + +int Look_Ahead_Tie() +{ + /* Look ahead and find the face to go to that + will give the least number of adjacencies + */ + int id[60],t,x,f=0,min = 60; + + for (x = 0; x < last; x++) + { + t = Look_Ahead(ties_array[x]); + /* We have a tie */ + if (t == min) + id[f++] = ties_array[x]; + if (t < min) + { + f = 0; + min = t; + id[f++] = ties_array[x]; + } + } + /* No tie within the tie */ + if ( f == 1) + return id[0]; + /* Or ties, but we are at the end of strips */ + if (min == 0) + return id[0]; + return (Random_Look(id,f)); +} + + +int Sequential_Tri(int *index) +{ + /* We have a triangle and need to break the ties at it. + We will choose the edge that is sequential. There + is definitely one since we know we have a triangle + and that there is a tie and there are only 2 edges + for the tie. + */ + int reversed, e1,e2,e3,output1,output2,output3,output4; + + /* e2 and e3 are the input edge to the triangle */ + Last_Edge(&e1,&e2,&e3,0); + + if ((e2 == 0) && (e3 == 0)) + /* Starting the strip, don't need to do this */ + return ties_array[0]; + + /* For the 2 ties find the edge adjacent to face id */ + reversed = Get_EdgeEx(&output1,&output2,index,ties_array[0],3,0,0); + reversed = Get_EdgeEx(&output3,&output4,index,ties_array[1],3,0,0); + + if ((output1 == e3) || (output2 == e3)) + return ties_array[0]; + if ((output3 == e3) || (output4 == e3)) + return ties_array[1]; + printf("There is an error trying to break sequential triangle \n"); +} + +int Sequential_Quad(int *index, int triangulate) +{ + /* We have a quad that need to break its ties, we will try + and choose a side that is sequential, otherwise use lookahead + */ + int reversed,output1,output2,x,e1,e2,e3; + + /* e2 and e3 are the input edge to the quad */ + Last_Edge(&e1,&e2,&e3,0); + + /* No input edge */ + if ((e2 == 0) && (e3 == 0)) + return ties_array[0]; + + /* Go through the ties and see if there is a sequential one */ + for (x = 0; x < last; x++) + { + reversed = Get_EdgeEx(&output1,&output2,index,ties_array[x],4,0,0); + /* Partial and whole triangulation will have different requirements */ + if (((output1 == e3) || (output2 == e3)) && (triangulate == PARTIAL)) + return ties_array[x]; + if (((output1 != e3) && (output1 != e2) && + (output2 != e3) && (output2 != e2))) + return ties_array[x]; + } + /* There was not a tie that was sequential */ + return Look_Ahead_Tie(); +} + +void Whole_Output(int in1,int in2, int *index, int size, int *out1, int *out2) +{ + /* Used to sequentially break ties in the whole triangulation for polygons + greater than 4 sides. We will find the output edge that is good + for sequential triangulation. + */ + + int half; + + /* Put the input edge first in the list */ + Rearrange_IndexEx(index,size); + + if (!(EVEN(size))) + { + if (*(index) == in1) + half = size/2 ; + else + half = size/2 +1; + } + else + half = size/2; + + *out1 = *(index+half); + *out2 = *(index+half+1); +} + +int Sequential_Poly(int size, int *index, int triangulate) +{ + /* We have a polygon of greater than 4 sides and wish to break the + tie in the most sequential manner. + */ + + int x,reversed,output1,output2,e1,e2,e3,saved1=-1,saved2=-1,output3,output4; + + /* e2 and e3 are the input edge to the quad */ + Last_Edge(&e1,&e2,&e3,0); + + /* If we are using whole, find the output edge that is sequential */ + if (triangulate == WHOLE) + Whole_Output(e2,e3,index,size,&output3,&output4); + + /* No input edge */ + if ((e2 == 0) && (e3 == 0)) + return ties_array[0]; + + for (x = 0; x < last ; x++) + { + reversed = Get_EdgeEx(&output1,&output2,index,ties_array[x],size,0,0); + /* Partial that can be removed in just one triangle */ + if (((output1 == e3) || (output2 == e3)) && (triangulate == PARTIAL)) + saved1 = ties_array[x]; + /* Partial removed in more than one triangle */ + if ((output1 != e3) && (output1 != e2) && (output2 != e3) && (output2 != e2) && + (triangulate == PARTIAL) && (saved2 != -1)) + saved2 = ties_array[x]; + /* Whole is not so easy, since the whole polygon must be done. Given + an input edge there is only one way to come out, approximately half + way around the polygon. + */ + if (((output1 == output3) && (output2 == output4)) || + ((output1 == output4) && (output2 == output3)) && + (triangulate == WHOLE)) + return ties_array[x]; + } + + if (saved1 != -1) + return saved1; + if (saved2 != -1) + return saved2; + + /* There was not a tie that was sequential */ + return Look_Ahead_Tie(); +} + +int Sequential_Tie(int face_id,int triangulate) +{ + /* Break the tie by choosing the face that will + not give us a swap and is sequential. If there + is not one, then do the lookahead to break the + tie. + */ + /* Separate into 3 cases for simplicity, if the current + polygon has 3 sides, 4 sides or if the sides were + greater. We can do the smaller cases faster, so that + is why I separated the cases. + */ + + ListHead *pListFace; + PF_FACES face; + + /* Get the polygon with id face_id */ + pListFace = PolFaces[face_id]; + face = (PF_FACES) PeekList(pListFace,LISTHEAD,0); + + if (face->nPolSize == 3) + return(Sequential_Tri(face->pPolygon)); + if (face->nPolSize == 4) + return(Sequential_Quad(face->pPolygon,triangulate)); + else + return(Sequential_Poly(face->nPolSize,face->pPolygon,triangulate)); + +} + +int Get_Next_Face(int t, int face_id, int triangulate) +{ + /* Get the next face depending on what + the user specified + */ + + /* Did not have a tie, don't do anything */ + if (last == 1) + return(ties_array[0]); + if (t == RANDOM) + return Random_Tie(); + if (t == ALTERNATE) + return Alternate_Tie(); + if (t == LOOK) + return Look_Ahead_Tie(); + if (t == SEQUENTIAL) + return Sequential_Tie(face_id,triangulate); + + printf("Illegal option specified for ties, using first \n"); + return (ties_array[0]); +} diff --git a/Tools/Stripe_u/ties.h b/Tools/Stripe_u/ties.h new file mode 100644 index 000000000..97e051765 --- /dev/null +++ b/Tools/Stripe_u/ties.h @@ -0,0 +1,15 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: ties.h +-----------------------------------------------------------------------*/ + +void Clear_Ties(); +void Add_Ties(); +int Get_Next_Face(); diff --git a/Tools/Stripe_u/triangulate.h b/Tools/Stripe_u/triangulate.h new file mode 100644 index 000000000..de612119f --- /dev/null +++ b/Tools/Stripe_u/triangulate.h @@ -0,0 +1,23 @@ + +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: triangulate.h +-----------------------------------------------------------------------*/ + +void Blind_Triangulate(); +void Non_Blind_Triangulate(); +int Adjacent(); +void Delete_From_List(); +void Triangulate_Polygon(); +void Rearrange_Index(); +void Find_Local_Strips(); + + + diff --git a/Tools/Stripe_u/triangulatex.h b/Tools/Stripe_u/triangulatex.h new file mode 100644 index 000000000..1710f0182 --- /dev/null +++ b/Tools/Stripe_u/triangulatex.h @@ -0,0 +1,23 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: triangulatex.h +-----------------------------------------------------------------------*/ + +enum swap_type +{ ON, OFF}; + +void SGI_StripEx(); +void Blind_TriangulateEx(); +void Non_Blind_TriangulateEx(); +int AdjacentEx(); +void Delete_From_ListEx(); +void Triangulate_PolygonEx(); +void Rearrange_IndexEx(); +void Find_StripsEx(); diff --git a/Tools/Stripe_u/util.c b/Tools/Stripe_u/util.c new file mode 100644 index 000000000..f17fe5f7c --- /dev/null +++ b/Tools/Stripe_u/util.c @@ -0,0 +1,272 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: util.c + This file contains routines that are used for various functions +*/ +/*---------------------------------------------------------------------*/ + + +#include <stdlib.h> +#include "polverts.h" + +void switch_lower (int *x, int *y) +{ + register int temp; + + /* Put lower value in x */ + if (*y < *x) + { + temp = *x; + *x = *y; + *y = temp; + } +} + +BOOL member(int x , int id1, int id2, int id3) +{ + /* Is x in the triangle specified by id1,id2,id3 */ + if ((x != id1) && (x != id2) && (x != id3)) + return FALSE; + return TRUE; +} + + +int Compare (P_ADJACENCIES node1, P_ADJACENCIES node2) +{ + /* This will only return whether 2 adjacency nodes + are equivalent. + */ + if (node1->face_id == node2->face_id) + return TRUE; + else + return FALSE; +} + + +BOOL Exist(int face_id, int id1, int id2) +{ + /* Does the edge specified by id1 and id2 exist in this + face currently? Maybe we deleted in partial triangulation + */ + ListHead *pListHead; + PF_FACES temp; + register int x,size; + BOOL a=FALSE,b =FALSE; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + size = temp->nPolSize; + for (x=0; x<size; x++) + { + if (*(temp->pPolygon+x) == id1) + a = TRUE; + if (*(temp->pPolygon+x) == id2) + b = TRUE; + if (a && b) + return TRUE; + } + return FALSE; +} + +int Get_Next_Id(int *index,int e3, int size) +{ + /* Return the id following e3 in the list of vertices */ + + register int x; + + for (x = 0; x< size; x++) + { + if ((*(index+x) == e3) && (x != (size-1))) + return *(index+x+1); + else if (*(index+x) == e3) + return *(index); + } + printf("There is an error in the next id\n"); + exit(0); +} + +int Different (int id1,int id2,int id3,int id4,int id5, int id6, int *x, int *y) +{ + /* Find the vertex in the first 3 numbers that does not exist in + the last three numbers + */ + if ((id1 != id4) && (id1 != id5) && (id1 != id6)) + { + *x = id2; + *y = id3; + return id1; + } + if ((id2 != id4) && (id2 != id5) && (id2 != id6)) + { + *x = id1; + *y = id3; + return id2; + } + if ((id3 != id4) && (id3 != id5) && (id3 != id6)) + { + *x = id1; + *y = id2; + return id3; + } + + /* Because there are degeneracies in the data, this might occur */ + *x = id5; + *y = id6; + return id4; +} + +int Return_Other(int *index,int e1,int e2) +{ + /* We have a triangle and want to know the third vertex of it */ + register int x; + + for (x=0;x<3;x++) + { + if ((*(index+x) != e1) && (*(index+x) != e2)) + return *(index+x); + } + /* If there is a degenerate triangle return arbitrary */ + return e1; +} + +int Get_Other_Vertex(int id1,int id2,int id3,int *index) +{ + /* We have a list index of 4 numbers and we wish to + return the number that is not id1,id2 or id3 + */ + register int x; + + for (x=0; x<4; x++) + { + if ((*(index+x) != id1) && (*(index+x) != id2) && + (*(index+x) != id3)) + return *(index+x); + } + /* If there is some sort of degeneracy this might occur, + return arbitrary + */ + if (x==4) + return id1; +} + + +PLISTINFO Done(int face_id, int size, int *bucket) +{ + /* Check to see whether the polygon with face_id was used + already, return NULL if it was, otherwise return a pointer to the face. + */ + P_ADJACENCIES pfNode; + register int y; + PLISTINFO lpListInfo; + + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + + for (y=size; ; y--) + { + lpListInfo = SearchList(array[y], pfNode, + (int (*)(void *,void *)) (Compare)); + if (lpListInfo != NULL) + { + *bucket = y; + return lpListInfo; + } + if (y == 0) + /* This adjacent face was done already */ + return lpListInfo; + } + free (pfNode); +} + +void Output_Edge(int *index,int e2,int e3,int *output1,int *output2) +{ + /* Given a quad and an input edge return the other 2 vertices of the + quad. + */ + + *output1 = -1; + *output2 = -1; + + if ((*(index) != e2) && (*(index) != e3)) + *output1 = *(index); + + if ((*(index+1) != e2) && (*(index+1) != e3)) + { + if (*output1 == -1) + *output1 = *(index+1); + else + { + *output2 = *(index+1); + return; + } + } + + if ((*(index+2) != e2) && (*(index+2) != e3)) + { + if (*output1 == -1) + *output1 = *(index+2); + else + { + *output2 = *(index+2); + return; + } + } + + *output2 = *(index+3); +} + + +void First_Edge(int *id1,int *id2, int *id3) +{ + /* Get the first triangle in the strip we just found, we will use this to + try to extend backwards in the strip + */ + + ListHead *pListHead; + register int num; + P_STRIPS temp1,temp2,temp3; + + pListHead = strips[0]; + num = NumOnList(pListHead); + + /* Did not have a strip */ + if (num < 3) + return; + + temp1 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 0); + temp2 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 1); + temp3 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 2); + *id1 = temp1->face_id; + *id2 = temp2->face_id; + *id3 = temp3->face_id; + +} + +void Last_Edge(int *id1, int *id2, int *id3, BOOL save) +{ + /* We need the last edge that we had */ + static int v1, v2, v3; + + if (save) + { + v1 = *id1; + v2 = *id2; + v3 = *id3; + } + else + { + *id1 = v1; + *id2 = v2; + *id3 = v3; + } +} + + diff --git a/Tools/Stripe_u/util.h b/Tools/Stripe_u/util.h new file mode 100644 index 000000000..2b43a4d35 --- /dev/null +++ b/Tools/Stripe_u/util.h @@ -0,0 +1,24 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: util.h +-----------------------------------------------------------------------*/ + +void switch_lower (); +int Compare (); +BOOL Exist(); +int Get_Next_Id(); +int Different(); +int Return_Other(); +int Get_Other_Vertex(); +PLISTINFO Done(); +void Output_Edge(); +void Last_Edge(); +void First_Edge(); +BOOL member(); diff --git a/Tools/Stripe_w/Makefile.am b/Tools/Stripe_w/Makefile.am new file mode 100644 index 000000000..ac0280b8a --- /dev/null +++ b/Tools/Stripe_w/Makefile.am @@ -0,0 +1,27 @@ +bin_PROGRAMS = strips + +strips_SOURCES = \ + add.c add.h \ + bands.c \ + common.c common.h \ + extend.h \ + free.c free.h \ + global.h \ + init.c init.h \ + local.c local.h \ + newpolve.c \ + options.c options.h \ + output.c output.h \ + outputex.c outputex.h \ + partial.c partial.h \ + polverts.h polyvertsex.h \ + queue.c queue.h \ + sgi_triang.c sgi_triangex.c \ + struct.c \ + structex.c \ + sturcts.h sturctsex.h \ + ties.c ties.h \ + triangulate.h triangulatex.h \ + util.c util.h + +strips_LDADD = $(base_LIBS) diff --git a/Tools/Stripe_w/add.c b/Tools/Stripe_w/add.c new file mode 100644 index 000000000..ef6e673d8 --- /dev/null +++ b/Tools/Stripe_w/add.c @@ -0,0 +1,384 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: add.c + This file contains the procedure code that will add information + to our data structures. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include "global.h" +#include "queue.h" +#include "polverts.h" +#include "triangulate.h" +#include "ties.h" +#include "outputex.h" +#include "options.h" +#include "local.h" + +BOOL new_vertex(double difference, int id1,int id2, + struct vert_struct *n) +{ + /* Is the difference between id1 and id2 (2 normal vertices that + mapped to the same vertex) greater than the + threshold that was specified? + */ + struct vert_struct *pn1,*pn2; + double dot_product; + double distance1, distance2,distance; + double rad; + char arg1[100]; + char arg2[100]; + + pn1 = n + id1; + pn2 = n + id2; + + dot_product = ((pn1->x) * (pn2->x)) + + ((pn1->y) * (pn2->y)) + + ((pn1->z) * (pn2->z)); + /* Get the absolute value */ + if (dot_product < 0) + dot_product = dot_product * -1; + + distance1 = sqrt( (pn1->x * pn1->x) + + (pn1->y * pn1->y) + + (pn1->z * pn1->z) ); + distance2 = sqrt( (pn2->x * pn2->x) + + (pn2->y * pn2->y) + + (pn2->z * pn2->z) ); + distance = distance1 * distance2; + + rad = acos((double)dot_product/(double)distance); + /* convert to degrees */ + rad = (180 * rad)/PI; + + if ( rad <= difference) + return FALSE; + + /* double checking because of imprecision with floating + point acos function + */ + sprintf( arg1,"%.5f", rad ); + sprintf( arg2,"%.5f", difference ); + if ( strcmp( arg1, arg2 ) <=0 ) + return( FALSE ); + if ( rad <= difference) + return FALSE; + else + return TRUE; +} + +BOOL Check_VN(int vertex,int normal, struct vert_added *added) +{ + /* Check to see if we already added this vertex and normal */ + register int x,n; + + n = (added+vertex)->num; + for (x = 0; x < n; x++) + { + if (*((added+vertex)->normal+x) == normal) + return TRUE; + } + return FALSE; +} + +BOOL norm_array(int id, int vertex, double normal_difference, + struct vert_struct *n, int num_vert) +{ + static int last; + static struct vert_added *added; + register int x; + static BOOL first = TRUE; + + if (first) + { + /* This is the first time that we are in here, so we will allocate + a structure that will save the vertices that we added, so that we + do not add the same thing twice + */ + first = FALSE; + added = (struct vert_added *) malloc (sizeof (struct vert_added ) * num_vert); + /* The number of vertices added for each vertex must be initialized to + zero + */ + for (x = 0; x < num_vert; x++) + (added+x)->num = 0; + } + + if (vertex) + /* Set the pointer to the vertex, we will be calling again with the + normal to fill it with + */ + last = id; + else + { + /* Fill the pointer with the id of the normal */ + if (*(vert_norms + last) == 0) + *(vert_norms + last) = id; + else if ((*(vert_norms + last) != id) && ((int)normal_difference != 360)) + { + /* difference is big enough, we need to create a new vertex */ + if (new_vertex(normal_difference,id,*(vert_norms + last),n)) + { + /* First check to see if we added this vertex and normal already */ + if (Check_VN(last,id,added)) + return FALSE; + /* OK, create the new vertex, and have its id = the number of vertices + and its normal what we have here + */ + vert_norms = realloc(vert_norms, sizeof(int) * (num_vert + 1)); + if (!vert_norms) + { + printf("Allocation error - aborting\n"); + exit(1); + } + *(vert_norms + num_vert) = id; + /* We created a new vertex, now put it in our added structure so + we do not add the same thing twice + */ + (added+last)->num = (added+last)->num + 1; + if ((added+last)->num == 1) + { + /* First time */ + (added+last)->normal = (int *) malloc (sizeof (int ) * 1); + *((added+last)->normal) = id; + } + else + { + /* Not the first time, reallocate space */ + (added+last)->normal = realloc((added+last)->normal,sizeof(int) * (added+last)->num); + *((added+last)->normal+((added+last)->num-1)) = id; + } + return TRUE; + } + } + } + return FALSE; +} + +void add_texture(int id,BOOL vertex) +{ + /* Save the texture with its vertex for future use when outputting */ + static int last; + + if (vertex) + last = id; + else + *(vert_texture+last) = id; +} + +int add_vert_id(int id, int index_count) +{ + register int x; + + /* Test if degenerate, if so do not add degenerate vertex */ + for (x = 1; x < index_count ; x++) + { + if (ids[x] == id) + return 0; + } + ids[index_count] = id; + return 1; +} + +void add_norm_id(int id, int index_count) +{ + norms[index_count] = id; +} + +void AddNewFace(int ids[STRIP_MAX], int vert_count, int face_id, + int norms[STRIP_MAX]) +{ + PF_FACES pfNode; + int *pTempInt; + int *pnorms; + F_EDGES **pTempVertptr; + int *pTempmarked, *pTempwalked; + register int y,count = 0; + + /* Add a new face into our face data structure */ + + pfNode = (PF_FACES) malloc(sizeof(F_FACES) ); + if ( pfNode ) + { + pfNode->pPolygon = (int*) malloc(sizeof(int) * (vert_count) ); + pfNode->pNorms = (int*) malloc(sizeof(int) * (vert_count) ); + pfNode->VertandId = (F_EDGES**)malloc(sizeof(F_EDGES*) * (vert_count)); + pfNode->marked = (int*)malloc(sizeof(int) * (vert_count)); + pfNode->walked = (int*)malloc(sizeof(int) * (vert_count)); + } + pTempInt =pfNode->pPolygon; + pnorms = pfNode->pNorms; + pTempmarked = pfNode->marked; + pTempwalked = pfNode->walked; + pTempVertptr = pfNode->VertandId; + pfNode->nPolSize = vert_count; + pfNode->seen = -1; + pfNode->seen2 = -1; + for (y=1;y<=vert_count;y++) + { + *(pTempInt + count) = ids[y]; + *(pnorms + count) = norms[y]; + *(pTempmarked + count) = FALSE; + *(pTempwalked + count) = -1; + *(pTempVertptr+count) = NULL; + count++; + } + AddHead(PolFaces[face_id-1],(PLISTINFO) pfNode); +} + + +void CopyFace(int ids[STRIP_MAX], int vert_count, int face_id, + int norms[STRIP_MAX]) +{ + PF_FACES pfNode; + int *pTempInt; + int *pnorms; + F_EDGES **pTempVertptr; + int *pTempmarked, *pTempwalked; + register int y,count = 0; + + /* Copy a face node into a new node, used after the global algorithm + is run, so that we can save whatever is left into a new structure + */ + + pfNode = (PF_FACES) malloc(sizeof(F_FACES) ); + if ( pfNode ) + { + pfNode->pPolygon = (int*) malloc(sizeof(int) * (vert_count) ); + pfNode->pNorms = (int*) malloc(sizeof(int) * (vert_count) ); + pfNode->VertandId = (F_EDGES**)malloc(sizeof(F_EDGES*) * (vert_count)); + pfNode->marked = (int*)malloc(sizeof(int) * (vert_count)); + pfNode->walked = (int*)malloc(sizeof(int) * (vert_count)); + } + pTempInt =pfNode->pPolygon; + pnorms = pfNode->pNorms; + pTempmarked = pfNode->marked; + pTempwalked = pfNode->walked; + pTempVertptr = pfNode->VertandId; + pfNode->nPolSize = vert_count; + pfNode->seen = -1; + pfNode->seen2 = -1; + for (y=0;y<vert_count;y++) + { + *(pTempInt + count) = ids[y]; + *(pnorms + count) = norms[y]; + *(pTempmarked + count) = FALSE; + *(pTempwalked + count) = -1; + *(pTempVertptr+count) = NULL; + count++; + } + AddHead(PolFaces[face_id-1],(PLISTINFO) pfNode); +} + +void Add_Edge(int v1,int v2) +{ + PF_EDGES temp = NULL; + ListHead *pListHead; + BOOL flag = TRUE; + register int t,count = 0; + + /* Add a new edge into the edge data structure */ + if (v1 > v2) + { + t = v1; + v1 = v2; + v2 = t; + } + + pListHead = PolEdges[v1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + { + printf("Have the wrong edge \n:"); + exit(1); + } + + while (flag) + { + if (v2 == temp->edge[0]) + return; + else + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,++count); + } +} + +void Add_AdjEdge(int v1,int v2,int fnum,int index1 ) +{ + PF_EDGES temp = NULL; + PF_FACES temp2 = NULL; + PF_EDGES pfNode; + ListHead *pListHead; + ListHead *pListFace; + BOOL flag = TRUE; + register int count = 0; + register int t,v3 = -1; + + if (v1 > v2) + { + t = v1; + v1 = v2; + v2 = t; + } + pListFace = PolFaces[fnum]; + temp2 = (PF_FACES) PeekList(pListFace,LISTHEAD,0); + pListHead = PolEdges[v1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + flag = FALSE; + count++; + while (flag) + { + if (v2 == temp->edge[0]) + { + /* If greater than 2 polygons adjacent to an edge, then we will + only save the first 2 that we found. We will have a small performance + hit, but this does not happen often. + */ + if (temp->edge[2] == -1) + temp->edge[2] = fnum; + else + v3 = temp->edge[2]; + flag = FALSE; + } + else + { + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + count++; + if (temp == NULL) + flag = FALSE; + } + } + + /* Did not find it */ + if (temp == NULL) + { + pfNode = (PF_EDGES) malloc(sizeof(F_EDGES) ); + if ( pfNode ) + { + pfNode->edge[0] = v2; + pfNode->edge[1] = fnum; + pfNode->edge[2] = v3; + AddTail( PolEdges[v1], (PLISTINFO) pfNode ); + } + else + { + printf("Out of memory!\n"); + exit(1); + } + + *(temp2->VertandId+index1) = pfNode; + } + else + *(temp2->VertandId+index1) = temp; +} diff --git a/Tools/Stripe_w/add.h b/Tools/Stripe_w/add.h new file mode 100644 index 000000000..fb37f729a --- /dev/null +++ b/Tools/Stripe_w/add.h @@ -0,0 +1,31 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: add.h +-----------------------------------------------------------------------*/ + +#include "global.h" + +BOOL new_vertex(double difference, int id1,int id2, + struct vert_struct *n); +BOOL Check_VN(int vertex,int normal, struct vert_added *added); +BOOL norm_array(int id, int vertex, double normal_difference, + struct vert_struct *n, int num_vert); +void add_texture(int id,BOOL vertex); +int add_vert_id(int id, int index_count); +void add_norm_id(int id, int index_count); +void AddNewFace(int ids[STRIP_MAX], int vert_count, int face_id, + int norms[STRIP_MAX]); +void CopyFace(int ids[STRIP_MAX], int vert_count, int face_id, + int norms[STRIP_MAX]); +void Add_Edge(int v1,int v2); +void Add_AdjEdge(int v1,int v2,int fnum,int index1 ); + + + diff --git a/Tools/Stripe_w/bands.c b/Tools/Stripe_w/bands.c new file mode 100644 index 000000000..161e8ccde --- /dev/null +++ b/Tools/Stripe_w/bands.c @@ -0,0 +1,569 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: bands.c + This file contains the main procedure code that will read in the + object and then call the routines that produce the triangle strips. +*/ +/*---------------------------------------------------------------------*/ + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include "global.h" +#include "polverts.h" +#include "triangulate.h" +#include "ties.h" +#include "outputex.h" +#include "options.h" +#include "local.h" +#include "init.h" +#include "free.h" +#include "add.h" + +/* TIMING for Windows */ +#ifdef WIN32 +# include <sys/timeb.h> +# include <time.h> +/* TIMING for UNIX */ +#else +# include <sys/types.h> +# include <sys/param.h> +# include <sys/times.h> +# if defined(__FreeBSD__) +# ifndef HZ +# include <time.h> +# define HZ CLK_TCK +# endif /* HZ */ +# else + extern long times( ); +# endif /* __FreeBSD__ */ + long elapsed() +{ + static long total = 0; + long cpu_time, dummy; + struct tms buffer; + times(&buffer); + dummy = buffer.tms_utime + buffer.tms_stime + + buffer.tms_cutime + buffer.tms_cstime; + cpu_time = ((dummy - total) * 1000) / HZ; + total = dummy; + return(cpu_time); +} +#endif /* WIN32 */ + + +int norms[STRIP_MAX]; +int *vert_norms; +int *vert_texture; + + +void get_time() +{ + /* For timing */ +#ifdef WIN32 + struct timeb timebuffer; + char *timeline; +#else + long timer; +#endif + + +#ifdef WIN32 + ftime( &timebuffer ); + timeline = ctime( & ( timebuffer.time ) ); + printf( "The time is %.19s.%hu %s", timeline, timebuffer.millitm, &timeline[20] ); +#else + timer = elapsed(); + printf("The time is %ld\n",timer); +#endif +} + +/* +** + Here the main program begins. It will start by loading in a .obj file + then it will convert the polygonal model into triangle strips. +** +*/ + +int main (int argc,char *argv[]) +{ + char *fname, *oname, *all,buff[255], *ptr, *ptr2; + FILE *file, *bands; + int face_id=0; + int vert_count=0; + int loop=0; + int num=0; + int num2=0; + + float center[3]; + int temp[STRIP_MAX],vertex,strips, swaps,tempi,cost,triangles; + int f,t,tr,g; + char *file_open; + int num_vert = 0, + num_faces = 0, + num_nvert = 0, + num_edges = 0, + num_texture = 0, + num_tris = 0; + double fra = 0.0; + BOOL texture, normal, normal_and_texture,quads = FALSE; + + /* Options variables */ + double norm_difference; + + /* Structures for the object */ + struct vert_struct *vertices = NULL, + *nvertices = NULL, + *pvertices = NULL, + *pnvertices = NULL; + + get_time(); + + /* + Scan the file once to find out the number of vertices, + vertice normals, and faces so we can set up some memory + structures + */ + /* Interpret the options specified */ + norm_difference = get_options(argc,argv,&f,&t,&tr,&g); + if (f == BINARY) + file_open = "rb"; + else + file_open = "r"; + + fname = argv[argc-2]; + oname = argv[argc-1]; + + printf ("Input file: %s Output file: %s\n", fname, oname); + printf ("Scanning...%s ",file_open); + + + /* File that will contain the triangle strip data */ + + bands = fopen(oname, "w"); + + /* File can be in binary for faster reading */ + if (file = fopen (fname,file_open)) + { + while (!feof (file)) + { + /* Read a line */ + if (f == BINARY) + fread (buff,sizeof(char) * 255,1, file); + else + fgets (buff, sizeof(char) * 255, file); + num++; + + printf("%d\r",num); + + + /* At a vertex */ + if (*buff == 'v') + { + /* At a normal */ + if (*(buff+1)=='n') + num_nvert++; + else if (*(buff+1)=='t') + num_texture++; + /* At a regular vertex */ + else + num_vert++; + } + /* At a face */ + else if (*buff == 'f') + { + num_faces++; + strtok(buff, " "); + tempi = 0; + while (strtok(NULL, " ") != NULL) tempi++; + num_tris += tempi - 2; + } + } + fclose (file); + } + else + { + printf("Error in the file name\n"); + exit(1); + } + + printf("%s pass 1\n",fname); + + /* Allocate structures for the information */ + Start_Face_Struct(num_faces); + vertices = (struct vert_struct *) + malloc (sizeof (struct vert_struct) * num_vert); + + if (num_nvert > 0) { + nvertices = (struct vert_struct *) + malloc (sizeof (struct vert_struct) * num_nvert); + vert_norms = (int *) malloc (sizeof (int) * num_vert); + /* + Initialize entries to zero, in case there are 2 hits + to the same vertex we will know it - used for determining + the normal difference + */ + init_vert_norms(num_vert); + } else { + nvertices = NULL; + } + + if (num_texture > 0) { + vert_texture = (int *) malloc (sizeof(int) * num_vert); + init_vert_texture(num_vert); + } + + /* + Set up the temporary 'p' pointers + */ + pvertices = vertices; + pnvertices = nvertices; + + /* Load the object into memory */ + /*printf (" Loading...");*/ + + fprintf(bands,"#%s: a triangle strip representation created by STRIPE.\n#This is a .objf file\n#by Francine Evans\n",fname); + + /* File will be put in a list for faster execution if file is in binary */ + if (file = fopen(fname,file_open)) { + if (f == BINARY) { + all = (char *) malloc (sizeof(char) * 255 * num); + fread(all,sizeof(char) * 255 * num, 1, file); + ptr = all; + } else { + ptr = (char *) malloc (sizeof(char) * 255 * num); + } + } + + + while (num > 0) { + num--; + + printf("%d\r",num); + + if (f == ASCII) { + fgets (ptr, sizeof(char) * 255, file); + } else { + ptr = ptr + 255; + } + + /* Load in vertices/normals */ + if (*ptr == 'v') { + if (*(ptr+1)=='n') { + sscanf (ptr+3,"%lf%lf%lf", + &(pnvertices->x), + &(pnvertices->y), + &(pnvertices->z)); + fprintf(bands,"vn %f %f %f\n", + pnvertices->x,pnvertices->y,pnvertices->z); + ++pnvertices; + } else if (*(ptr+1)=='t') { + sscanf (ptr+3,"%f%f%f",¢er[0],¢er[1],¢er[2]); + fprintf(bands,"vt %f %f %f\n",center[0],center[1],center[2]); + } else { + sscanf (ptr+2,"%lf%lf%lf", + &(pvertices->x), + &(pvertices->y), + &(pvertices->z)); + fprintf(bands,"v %f %f %f\n", + pvertices->x,pvertices->y,pvertices->z); + ++pvertices; + } + } else if (*ptr == 'f') { + /* Read in faces */ + num2 = 0; + face_id++; + ptr2 = ptr+1; + normal = FALSE; texture = FALSE, normal_and_texture = FALSE; + while (*ptr2) { + if (*ptr2 >='0' && *ptr2 <='9') { + num2++; + ++ptr2; + while (*ptr2 && (*ptr2!=' ' && *ptr2!='/')) { + ptr2++; + } + /* There are normals in this line */ + if (*ptr2 == '/') { + if (*(ptr2+1) == '/') { + normal = TRUE; + } else { + texture = TRUE; + } + } else if (*ptr2 == ' ') { + if ((num2 == 3) && (texture)) { + normal_and_texture = TRUE; + } + } + } else { + ++ptr2; + } + } + + ptr2 = ptr+1; + + /* + loop on the number of numbers in this line of face data + */ + vert_count = 0; + + for (loop=0;loop<num2;loop++) { + /* skip the whitespace */ + while (*ptr2<'0' || *ptr2>'9') { + if (*ptr2 == '-') { + break; + } + ptr2++; + } + vertex = atoi(ptr2)-1; + if (vertex < 0) { + vertex = num_vert + vertex; + *ptr2 = ' '; + ptr2++; + } + /* + If there are either normals or textures with the vertices + in this file, the data alternates so we must read it this way + */ + if ( (normal) && (!normal_and_texture)) { + if (loop%2) { + add_norm_id(vertex,vert_count); + /* + Test here to see if we added a new vertex, since the + vertex has more than one normal and the 2 normals are greater + than the threshold specified + */ + if (norm_array(vertex,0,norm_difference,nvertices,num_vert)) { + /* + Add a new vertex and change the + id of the vertex that we just read to the id of the new + vertex that we just added + */ + /* + Put it in the output file, note the added vertices will + be after the normals and separated from the rest of the + vertices. Will not affect our viewer + */ + fprintf(bands,"v %f %f %f\n", + (vertices + temp[vert_count - 1])->x, + (vertices + temp[vert_count - 1])->y, + (vertices + temp[vert_count - 1])->z); + num_vert++; + temp[vert_count - 1] = num_vert - 1; + if (!(add_vert_id(num_vert - 1,vert_count))) { + vert_count--; + } + } + } else { + /* the vertex */ + temp[vert_count] = vertex ; + vert_count++; + if (!(add_vert_id(vertex,vert_count))) { + vert_count--; + } + norm_array(vertex,1,norm_difference,nvertices,num_vert); + } + } else if (normal_and_texture) { + /* Else there are vertices and textures with the data */ + if( !((loop+1)%3)) { + add_norm_id(vertex,vert_count); + /* + Test here to see if we added a new vertex, since the + vertex has more than one normal and the 2 normals are greater + than the threshold specified + */ + if (norm_array(vertex,0,norm_difference,nvertices,num_vert)) { + /* + Add a new vertex and change the + id of the vertex that we just read to the id of the new + vertex that we just added + */ + /* + Put it in the output file, note the added vertices will + be after the normals and separated from the rest of the + vertices. Will not affect our viewer + */ + fprintf(bands,"v %f %f %f\n", + (vertices + temp[vert_count - 1])->x, + (vertices + temp[vert_count - 1])->y, + (vertices + temp[vert_count - 1])->z); + num_vert++; + temp[vert_count - 1] = num_vert - 1; + if (!(add_vert_id(num_vert - 1,vert_count))) { + vert_count--; + } + } + } else if ((loop == 0) || (*(ptr2-1) == ' ')) { + /* the vertex */ + temp[vert_count] = vertex ; + vert_count++; + if (vert_count == 4) { + quads = TRUE; + } + if (!(add_vert_id(vertex,vert_count))) { + vert_count--; + } + add_texture(vertex,TRUE); + norm_array(vertex,1,norm_difference,nvertices,num_vert); + } else { + /* The texture */ + add_texture(vertex,FALSE); + } + } else if ( texture ) { + /* the vertex */ + if (!(loop%2)) { + temp[vert_count] = vertex ; + vert_count++; + if (vert_count == 4) + quads = TRUE; + add_texture(vertex,TRUE); + if (!(add_vert_id(vertex,vert_count))) + vert_count--; + norm_array(vertex,1,norm_difference,nvertices,num_vert); + } else { + /* texture */ + add_texture(vertex,FALSE); + } + } else { + /*** no nvertices ***/ + temp[vert_count] = vertex ; + vert_count++; + if (vert_count == 4) + quads = TRUE; + if (!(add_vert_id(vertex,vert_count))) + vert_count--; + } + while (*ptr2>='0' && *ptr2<='9') + ptr2++; + } + /* Done with the polygon */ + num_edges += vert_count; + /* add it to face structure */ + if (vert_count >= 3) + AddNewFace(ids,vert_count,face_id,norms); + else + face_id--; + if (vert_count == 4) + quads = TRUE; + } + else if ((g == TRUE) && (face_id > 0) + && ((*ptr == 'g') || (*ptr == 's') || (*ptr == 'm') || (*ptr == 'o'))) + { + /* + The user specified that the strips will be contained in each group + from the data file, so we just finished a group and will find the + triangle strips in it. + */ + Start_Edge_Struct(num_vert); + Find_Adjacencies(face_id); + if (quads) + { + Init_Table_SGI(); + Build_SGI_Table(num_vert,face_id); + /* Code for lengths of walks in each direction */ + /* Save_Walks(face_id,TRUE); */ + Save_Walks(face_id); + + /* Code for finding the bands */ + Find_Bands(face_id,bands,&swaps,&strips,&cost,&triangles,num_nvert,vert_norms,num_texture,vert_texture); + + /* + Remove the faces that we did so that we can + run the strip code on the rest of the faces that are left + */ + if (cost != 0) + { + printf("Total %d triangles with %d cost\n",triangles,cost); + Save_Rest(&face_id); + printf("We saved %d .... now doing the local algorithm\n",face_id); + fprintf(bands,"\n#local\n"); + End_Edge_Struct(num_vert); + Start_Edge_Struct(num_vert); + Find_Adjacencies(face_id); + } + } + + SGI_Strip(num_vert,face_id,bands,t,tr); + + /* Get the total cost */ + Output_TriEx(-1,-2,-3,NULL,-1,-20,cost); + + End_Face_Struct(num_faces); + End_Edge_Struct(num_vert); + cost = 0; + face_id = 0; + quads = FALSE; + Start_Face_Struct(num_faces-face_id); + num_faces = num_faces - face_id; + Free_Strips(); + } + } + + /* Done reading in all the information into data structures */ + num_faces = face_id; + fclose (file); + + printf("Input Done.\n\n"); + + free(vertices); + free(nvertices); + + printf ("Vertices: %d\nNormals: %d\nFaces: %d\n",num_vert,num_nvert,num_faces); + + Start_Edge_Struct(num_vert); + Find_Adjacencies(num_faces); + + /* Initialize it */ + Init_Table_SGI(); + /* Build it */ + Build_SGI_Table(num_vert,num_faces); + + InitStripTable(); + + if (quads) { + /* Code for lengths of walks in each direction */ + /* Save_Walks(num_faces,TRUE); */ + Save_Walks(num_faces); + + /* Code for finding the bands */ + Find_Bands(num_faces,bands,&swaps,&strips,&cost,&triangles,num_nvert,vert_norms,num_texture,vert_texture); + /*printf("Total %d triangles with %d cost\n",triangles,cost);*/ + + /* + Remove the faces that we did so that we can + run the strip code on the rest of the faces that are left + */ + Save_Rest(&num_faces); + /*printf("We saved %d .... now doing the local algorithm\n",num_faces);*/ + fprintf(bands,"\n#local\n"); + End_Edge_Struct(num_vert); + Start_Edge_Struct(num_vert); + Find_Adjacencies(num_faces); + } + + SGI_Strip(num_vert,num_faces,bands,t,tr); + + /* Get the total cost */ + Output_TriEx(-1,-2,-3,NULL,-1,-20,cost); + + End_Face_Struct(num_faces); + End_Edge_Struct(num_vert); + fclose(bands); + + get_time(); + + return(0); +} + diff --git a/Tools/Stripe_w/common.c b/Tools/Stripe_w/common.c new file mode 100644 index 000000000..ca836e19f --- /dev/null +++ b/Tools/Stripe_w/common.c @@ -0,0 +1,810 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: common.c + This file contains common code used in both the local and global algorithm +*/ +/*---------------------------------------------------------------------*/ + + +#include <stdlib.h> +#include "polverts.h" +#include "extend.h" +#include "output.h" +#include "triangulate.h" +#include "util.h" +#include "add.h" + +int Old_Adj(int face_id) +{ + /* Find the bucket that the face_id is currently in, + because maybe we will be deleting it. + */ + PF_FACES temp = NULL; + ListHead *pListHead; + int size,y; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + if ( temp == NULL ) + { + printf("The face was already deleted, there is an error\n"); + exit(0); + } + + size = temp->nPolSize; + if (Done(face_id,size,&y) == NULL) + { + printf("There is an error in finding the face\n"); + exit(0); + } + return y; +} + +int Number_Adj(int id1, int id2, int curr_id) +{ + /* Given edge whose endpoints are specified by id1 and id2, + determine how many polygons share this edge and return that + number minus one (since we do not want to include the polygon + that the caller has already). + */ + + int size,y,count=0; + PF_EDGES temp = NULL; + PF_FACES temp2 = NULL; + ListHead *pListHead; + BOOL there= FALSE; + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* new edge that was created might not be here */ + return 0; + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* This edge was not there in the original, which + mean that we created it in the partial triangulation. + So it is adjacent to nothing. + */ + return 0; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return 0; + else + { + /* It was adjacent to another polygon, but maybe we did this + polygon already, and it was done partially so that this edge + could have been done + */ + if (curr_id != temp->edge[1]) + { + /* Did we use this polygon already?and it was deleted + completely from the structure + */ + pListHead = PolFaces[temp->edge[1]]; + temp2 = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + if (Done(temp->edge[1],temp2->nPolSize,&size) == NULL) + return 0; + } + else + { + pListHead = PolFaces[temp->edge[2]]; + temp2 = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + if (Done(temp->edge[2],temp2->nPolSize,&size)== NULL) + return 0; + } + + /* Now we have to check whether it was partially done, before + we can say definitely if it is adjacent. + Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp2 != NULL ) + { + /* Size of the polygon */ + size = temp2->nPolSize; + for (y = 0; y< size; y++) + { + /* If we are doing partial triangulation, we must check + to see whether the edge is still there in the polygon, + since we might have done a portion of the polygon + and saved the rest for later. + */ + if (y != (size-1)) + { + if( ((id1 == *(temp2->pPolygon+y)) && (id2 ==*(temp2->pPolygon+y+1))) + || ((id2 == *(temp2->pPolygon+y)) && (id1 ==*(temp2->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((id1 == *(temp2->pPolygon)) && (id2 == *(temp2->pPolygon+size-1))) + || ((id2 == *(temp2->pPolygon)) && (id1 ==*(temp2->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + } + + if (there ) + return 1; + return 0; + } +} + +int Min_Adj(int id) +{ + /* Used for the lookahead to break ties. It will + return the minimum adjacency found at this face. + */ + int y,numverts,t,x=60; + PF_FACES temp=NULL; + ListHead *pListHead; + + /* If polygon was used then we can't use this face */ + if (Done(id,59,&y) == NULL) + return 60; + + /* It was not used already */ + pListHead = PolFaces[id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + if ( temp != NULL ) + { + numverts = temp->nPolSize; + for (y = 0; y< numverts; y++) + { + if (y != (numverts-1)) + t = Number_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1),id); + else + t = Number_Adj(*(temp->pPolygon),*(temp->pPolygon+(numverts-1)),id); + if (t < x) + x = t; + } + } + if (x == -1) + { + printf("Error in the look\n"); + exit(0); + } + return x; +} + + + +void Edge_Least(int *index,int *new1,int *new2,int face_id,int size) +{ + /* We had a polygon without an input edge and now we re going to pick one + of the edges with the least number of adjacencies to be the input + edge + */ + register int x,value,smallest=60; + + for (x = 0; x<size; x++) + { + if (x != (size -1) ) + value = Number_Adj(*(index+x),*(index+x+1),face_id); + else + value = Number_Adj(*(index),*(index+size-1),face_id); + if (value < smallest) + { + smallest = value; + if (x != (size -1)) + { + *new1 = *(index+x); + *new2 = *(index+x+1); + } + else + { + *new1 = *(index); + *new2 = *(index+size-1); + } + } + } + if ((smallest == 60) || (smallest < 0)) + { + printf("There is an error in getting the least edge\n"); + exit(0); + } +} + + +void Check_In_Polygon(int face_id, int *min, int size) +{ + /* Check to see the adjacencies by going into a polygon that has + greater than 4 sides. + */ + + ListHead *pListHead; + PF_FACES temp; + int y,id1,id2,id3,x=0,z=0; + int saved[2]; + int big_saved[60]; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + /* Get the input edge that we came in on */ + Last_Edge(&id1,&id2,&id3,0); + + /* Find the number of adjacencies to the edges that are adjacent + to the input edge. + */ + for (y=0; y< size; y++) + { + if (y != (size-1)) + { + if (((*(temp->pPolygon+y) == id2) && (*(temp->pPolygon+y+1) != id3)) + || ((*(temp->pPolygon+y) == id3) && (*(temp->pPolygon+y+1) != id2))) + { + saved[x++] = Number_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1),face_id); + big_saved[z++] = saved[x-1]; + } + else + big_saved[z++] = Number_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1),face_id); + } + else + { + if (((*(temp->pPolygon) == id2) && (*(temp->pPolygon+size-1) != id3)) + || ((*(temp->pPolygon) == id3) && (*(temp->pPolygon+size-1) != id2))) + { + saved[x++] = Number_Adj(*(temp->pPolygon),*(temp->pPolygon+size-1),face_id); + big_saved[z++] = saved[x-1]; + } + else + big_saved[z++] = Number_Adj(*(temp->pPolygon),*(temp->pPolygon+size-1),face_id); + } + } + /* There was an input edge */ + if (x == 2) + { + if (saved[0] < saved[1]) + /* Count the polygon that we will be cutting as another adjacency*/ + *min = saved[0] + 1; + else + *min = saved[1] + 1; + } + /* There was not an input edge */ + else + { + if (z != size) + { + printf("There is an error with the z %d %d\n",size,z); + exit(0); + } + *min = 60; + for (x = 0; x < size; x++) + { + if (*min > big_saved[x]) + *min = big_saved[x]; + } + } +} + + +void New_Face (int face_id, int v1, int v2, int v3) +{ + /* We want to change the face that was face_id, we will + change it to a triangle, since the rest of the polygon + was already outputtted + */ + ListHead *pListHead; + PF_FACES temp = NULL; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp != NULL ) + { + /* Size of the polygon */ + if (temp->nPolSize != 4) + { + printf("There is a miscalculation in the partial\n"); + exit (0); + } + temp->nPolSize = 3; + *(temp->pPolygon) = v1; + *(temp->pPolygon+1) = v2; + *(temp->pPolygon+2) = v3; + } +} + +void New_Size_Face (int face_id) +{ + /* We want to change the face that was face_id, we will + change it to a triangle, since the rest of the polygon + was already outputtted + */ + ListHead *pListHead; + PF_FACES temp = NULL; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp != NULL ) + (temp->nPolSize)--; + else + printf("There is an error in updating the size\n"); +} + + + +void Check_In_Quad(int face_id,int *min) +{ + /* Check to see what the adjacencies are for the polygons that + are inside the quad, ie the 2 triangles that we can form. + */ + ListHead *pListHead; + int y,id1,id2,id3,x=0; + int saved[4]; + PF_FACES temp; + register int size = 4; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + /* Get the input edge that we came in on */ + Last_Edge(&id1,&id2,&id3,0); + + /* Now find the adjacencies for the inside triangles */ + for (y = 0; y< size; y++) + { + /* Will not do this if the edge is the input edge */ + if (y != (size-1)) + { + if ((((*(temp->pPolygon+y) == id2) && (*(temp->pPolygon+y+1) == id3))) || + (((*(temp->pPolygon+y) == id3) && (*(temp->pPolygon+y+1) == id2)))) + saved[x++] = -1; + else + { + if (x == 4) + { + printf("There is an error in the check in quad \n"); + exit(0); + } + /* Save the number of Adjacent Polygons to this edge */ + saved[x++] = Number_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1),face_id); + } + } + else if ((((*(temp->pPolygon) == id2) && (*(temp->pPolygon+size-1) == id3))) || + (((*(temp->pPolygon) == id3) && (*(temp->pPolygon+size-1) == id2))) ) + saved[x++] = -1; + else + { + if (x == 4) + { + printf("There is an error in the check in quad \n"); + exit(0); + } + /* Save the number of Adjacent Polygons to this edge */ + saved[x++] = Number_Adj(*(temp->pPolygon),*(temp->pPolygon+size-1),face_id); + + } + } + if (x != 4) + { + printf("Did not enter all the values %d \n",x); + exit(0); + } + + *min = 10; + for (x=0; x<4; x++) + { + if (x!= 3) + { + if ((saved[x] != -1) && (saved[x+1] != -1) && + ((saved[x] + saved[x+1]) < *min)) + *min = saved[x] + saved[x+1]; + } + else + { + if ((saved[0] != -1) && (saved[x] != -1) && + ((saved[x] + saved[0]) < *min)) + *min = saved[0] + saved[x]; + } + } +} + + + +int Get_Output_Edge(int face_id, int size, int *index,int id2,int id3) +{ + /* Return the vertex adjacent to either input1 or input2 that + is adjacent to the least number of polygons on the edge that + is shared with either input1 or input2. + */ + register int x=0,y; + int saved[2]; + int edges[2][1]; + + for (y = 0; y < size; y++) + { + if (y != (size-1)) + { + if (((*(index+y) == id2) && (*(index+y+1) != id3)) + || ((*(index+y) == id3) && (*(index+y+1) != id2))) + { + saved[x++] = Number_Adj(*(index+y),*(index+y+1),face_id); + edges[x-1][0] = *(index+y+1); + } + else if (y != 0) + { + if (( (*(index+y) == id2) && (*(index+y-1) != id3) ) || + ( (*(index+y) == id3) && (*(index+y-1) != id2)) ) + { + saved[x++] = Number_Adj(*(index+y),*(index+y-1),face_id); + edges[x-1][0] = *(index+y-1); + } + } + else if (y == 0) + { + if (( (*(index) == id2) && (*(index+size-1) != id3) ) || + ( (*(index) == id3) && (*(index+size-1) != id2)) ) + { + saved[x++] = Number_Adj(*(index),*(index+size-1),face_id); + edges[x-1][0] = *(index+size-1); + } + } + + } + else + { + if (((*(index+size-1) == id2) && (*(index) != id3)) + || ((*(index+size-1) == id3) && (*(index) != id2))) + { + saved[x++] = Number_Adj(*(index),*(index+size-1),face_id); + edges[x-1][0] = *(index); + } + + if (( (*(index+size-1) == id2) && (*(index+y-1) != id3) ) || + ( (*(index+size-1) == id3) && (*(index+y-1) != id2)) ) + { + saved[x++] = Number_Adj(*(index+size-1),*(index+y-1),face_id); + edges[x-1][0] = *(index+y-1); + } + } + } + if ((x != 2)) + { + printf("There is an error in getting the input edge %d \n",x); + exit(0); + } + if (saved[0] < saved[1]) + return edges[0][0]; + else + return edges[1][0]; + +} + +void Get_Input_Edge(int *index,int id1,int id2,int id3,int *new1,int *new2,int size, + int face_id) +{ + /* We had a polygon without an input edge and now we are going to pick one + as the input edge. The last triangle was id1,id2,id3, we will try to + get an edge to have something in common with one of those vertices, otherwise + we will pick the edge with the least number of adjacencies. + */ + + register int x; + int saved[3]; + + saved[0] = -1; + saved[1] = -1; + saved[2] = -1; + + /* Go through the edges to see if there is one in common with one + of the vertices of the last triangle that we had, preferably id2 or + id3 since those are the last 2 things in the stack of size 2. + */ + for (x=0; x< size; x++) + { + if (*(index+x) == id1) + { + if (x != (size-1)) + saved[0] = *(index+x+1); + else + saved[0] = *(index); + } + + if (*(index+x) == id2) + { + if (x != (size-1)) + saved[1] = *(index+x+1); + else + saved[1] = *(index); + } + + if (*(index+x) == id3) + { + if (x != (size -1)) + saved[2] = *(index+x+1); + else + saved[2] = *(index); + } + } + /* Now see what we saved */ + if (saved[2] != -1) + { + *new1 = id3; + *new2 = saved[2]; + return; + } + else if (saved[1] != -1) + { + *new1 = id2; + *new2 = saved[1]; + return; + } + else if (saved[0] != -1) + { + *new1 = id1; + *new2 = saved[0]; + return; + } + /* We did not find anything so get the edge with the least number of adjacencies */ + Edge_Least(index,new1,new2,face_id,size); + +} + +int Find_Face(int current_face, int id1, int id2, int *bucket) +{ + /* Find the face that is adjacent to the edge and is not the + current face. + */ + register int size,y,count=0; + PF_EDGES temp = NULL; + PF_FACES temp2 = NULL; + ListHead *pListHead; + int next_face; + BOOL there = FALSE; + + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + /* The input edge was a new edge */ + if (temp == NULL) + return -1; + + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + /* The input edge was a new edge */ + if (temp == NULL) + return -1; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return -1; + else + { + if (temp->edge[2] == current_face) + next_face = temp->edge[1]; + else + next_face = temp->edge[2]; + } + /* We have the other face adjacent to this edge, it is + next_face. + */ + pListHead = PolFaces[next_face]; + temp2 = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + /* See if the face was already deleted, and where + it is if it was not + */ + + if (Done(next_face,59,bucket) == NULL) + return -1; + + /* Make sure the edge is still in this polygon, and that it is not + done + */ + /* Size of the polygon */ + size = temp2->nPolSize; + for (y = 0; y< size; y++) + { + /* Make sure that the edge is still in the + polygon and was not deleted, because if the edge was + deleted, then we used it already. + */ + if (y != (size-1)) + { + if( ((id1 == *(temp2->pPolygon+y)) && (id2 ==*(temp2->pPolygon+y+1))) + || ((id2 == *(temp2->pPolygon+y)) && (id1 ==*(temp2->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((id1 == *(temp2->pPolygon)) && (id2 ==*(temp2->pPolygon+size-1))) + || ((id2 == *(temp2->pPolygon)) && (id1 ==*(temp2->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + + if (!there) + /* Edge already used and deleted from the polygon*/ + return -1; + else + return next_face; +} + +BOOL Look_Up(int id1,int id2,int face_id) +{ + /* See if the endpoints of the edge specified by id1 and id2 + are adjacent to the face with face_id + */ + register int count = 0; + PF_EDGES temp = NULL; + ListHead *pListHead; + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* Was a new edge that we created */ + return 0; + + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* Was a new edge that we created */ + return 0; + } + /* Was not adjacent to anything else except itself */ + if ((temp->edge[2] == face_id) || (temp->edge[1] == face_id)) + { + /* Edge was adjacent to face, make sure that edge is + still there + */ + if (Exist(face_id,id1,id2)) + return 1; + else + return 0; + } + else + return 0; +} + + +void Add_Id_Strips(int id, int where) +{ + /* Just save the triangle for later */ + P_STRIPS pfNode; + + pfNode = (P_STRIPS) malloc(sizeof(Strips) ); + if ( pfNode ) + { + pfNode->face_id = id; + if (where == 1) + AddTail(strips[0],(PLISTINFO) pfNode); + /* We are backtracking in the strip */ + else + AddHead(strips[0],(PLISTINFO) pfNode); + } + else + { + printf("There is not enough memory to allocate for the strips\n"); + exit(0); + } +} + + +int Num_Adj(int id1, int id2) +{ + /* Given edge whose endpoints are specified by id1 and id2, + determine how many polygons share this edge and return that + number minus one (since we do not want to include the polygon + that the caller has already). + */ + + PF_EDGES temp = NULL; + ListHead *pListHead; + register count=-1; + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + { + printf("There is an error in the creation of the table \n"); + exit(0); + } + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + { + printf("There is an error in the creation of the table\n"); + exit(0); + } + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return 0; + return 1; +} + + +void Add_Sgi_Adj(int bucket,int face_id) +{ + /* This routine will add the face to the proper bucket, + depending on how many faces are adjacent to it (what the + value bucket should be). + */ + P_ADJACENCIES pfNode; + + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + { + pfNode->face_id = face_id; + AddHead(array[bucket],(PLISTINFO) pfNode); + } + else + { + printf("Out of memory for the SGI adj list!\n"); + exit(0); + } +} + +void Find_Adjacencies(int num_faces) +{ + register int x,y; + register int numverts; + PF_FACES temp=NULL; + ListHead *pListHead; + + /* Fill in the adjacencies data structure for all the faces */ + for (x=0;x<num_faces;x++) + { + pListHead = PolFaces[x]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + if ( temp != NULL ) + { + numverts = temp->nPolSize; + if (numverts != 1) + { + for (y = 0; y< numverts; y++) + { + if (y != (numverts-1)) + Add_AdjEdge(*(temp->pPolygon+y),*(temp->pPolygon+y+1),x,y); + + else + Add_AdjEdge(*(temp->pPolygon),*(temp->pPolygon+(numverts-1)),x,numverts-1); + + } + } + temp = NULL; + } + } +} + + diff --git a/Tools/Stripe_w/common.h b/Tools/Stripe_w/common.h new file mode 100644 index 000000000..aca19824b --- /dev/null +++ b/Tools/Stripe_w/common.h @@ -0,0 +1,42 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: common.h +-----------------------------------------------------------------------*/ + +void Add_AdjEdge(int v1,int v2,int fnum,int index1 ); +void Find_Adjacencies(int num_faces); +void Add_Sgi_Adj(int bucket,int face_id); +int Num_Adj(int id1, int id2); +void Add_Id_Strips(int id, int where); +BOOL Look_Up(int id1,int id2,int face_id); +int Number_Adj(int id1, int id2, int curr_id); +int Old_Adj(int face_id); +int Min_Adj(int id); +int Find_Face(int current_face, int id1, int id2, int *bucket); +void Edge_Least(int *index,int *new1,int *new2,int face_id,int size); +void Get_Input_Edge(int *index,int id1,int id2,int id3,int *new1,int *new2, + int size, int face_id); +int Get_Output_Edge(int face_id, int size, int *index,int id2,int id3); +void Check_In_Polygon(int face_id, int *min, int size); +void Check_In_Quad(int face_id,int *min); +void New_Size_Face (int face_id); +void New_Face (int face_id, int v1, int v2, int v3); + + + + + + + + + + + + diff --git a/Tools/Stripe_w/extend.h b/Tools/Stripe_w/extend.h new file mode 100644 index 000000000..78c135e3a --- /dev/null +++ b/Tools/Stripe_w/extend.h @@ -0,0 +1,17 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: extend.h +-----------------------------------------------------------------------*/ + +int Bottom_Left(); +int Top_Left(); +void Start_Edge(); + + diff --git a/Tools/Stripe_w/free.c b/Tools/Stripe_w/free.c new file mode 100644 index 000000000..ad655fcf7 --- /dev/null +++ b/Tools/Stripe_w/free.c @@ -0,0 +1,112 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: free.c + This file contains the code used to free the data structures. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include "polverts.h" + +ListHead *array[60]; +int id_array[60]; +ListHead *strips[1]; +ListHead *all_strips[100000]; /* Assume max 100000 strips */ + +void ParseAndFreeList( ListHead *pListHead ) +{ + register int c,num; + + /* Freeing a linked list */ + num = NumOnList(pListHead); + for (c = 0; c< num; c++) + RemHead(pListHead); +} + +void FreePolygonNode( PF_VERTS pfVerts) +{ + /* Free a vertex node */ + if ( pfVerts->pPolygon ) + free( pfVerts->pPolygon ); + free( pfVerts ); + +} + +void Free_Strips() +{ + /* Free strips data structure */ + if (strips[0] == NULL) + return; + else + ParseAndFreeList(strips[0]); +} + +void FreeFaceNode( PF_FACES pfFaces) +{ + /* Free face node */ + if ( pfFaces->pPolygon ) + free( pfFaces->pPolygon ); + free( pfFaces ); +} + + +void FreeFaceTable(int nSize) +{ + register int nIndex; + + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + if ( PolFaces[nIndex] != NULL ) + ParseAndFreeList( PolFaces[nIndex] ); + } + free( PolFaces ); +} + +void FreeEdgeTable(int nSize) +{ + register int nIndex; + + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + if ( PolEdges[nIndex] != NULL ) + ParseAndFreeList( PolEdges[nIndex] ); + } + free( PolEdges ); +} + + +void Free_All_Strips() +{ + + ListHead *pListHead; + register int y; + + for (y =0; ; y++) + { + pListHead = all_strips[y]; + if (pListHead == NULL) + return; + else + ParseAndFreeList(all_strips[y]); + } +} + +void End_Face_Struct(int numfaces) +{ + FreeFaceTable(numfaces); +} + +void End_Edge_Struct(int numverts) +{ + FreeEdgeTable(numverts); +} + + diff --git a/Tools/Stripe_w/free.h b/Tools/Stripe_w/free.h new file mode 100644 index 000000000..4c1d055c6 --- /dev/null +++ b/Tools/Stripe_w/free.h @@ -0,0 +1,22 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: free.h +-----------------------------------------------------------------------*/ + +void Free_All_Strips(); +void ParseAndFreeList( ListHead *pListHead ); +void FreePolygonNode( PF_VERTS pfVerts); +void Free_Strips(); +void FreeFaceTable(int nSize); +void FreeEdgeTable(int nSize); +void End_Face_Struct(int numfaces); +void End_Edge_Struct(int numverts); + + diff --git a/Tools/Stripe_w/global.h b/Tools/Stripe_w/global.h new file mode 100644 index 000000000..3538f82f4 --- /dev/null +++ b/Tools/Stripe_w/global.h @@ -0,0 +1,44 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: global.h +-----------------------------------------------------------------------*/ + +#ifndef _GLOBAL_H +#define _GLOBAL_H + + +#define VRDATA double +#define STRIP_MAX 60 + +#define TRUE 1 +#define FALSE 0 + +#ifndef PI +#define PI 3.1415926573 +#endif + +#define ATOI(C) (C -'0') +#define X 0 +#define Y 1 +#define Z 2 +#define EVEN(x) (((x) & 1) == 0) +#define MAX_BAND 10000 + +struct vert_struct { + VRDATA x, y, z; /* point coordinates */ +}; + +extern int ids[STRIP_MAX]; +extern int norms[STRIP_MAX]; +extern int *vert_norms; +extern int *vert_texture; + + +#endif _GLOBAL_H diff --git a/Tools/Stripe_w/init.c b/Tools/Stripe_w/init.c new file mode 100644 index 000000000..2e0f25885 --- /dev/null +++ b/Tools/Stripe_w/init.c @@ -0,0 +1,217 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: init.c + This file contains the initialization of data structures. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include "global.h" +#include "polverts.h" + +void init_vert_norms(int num_vert) +{ + /* Initialize vertex/normal array to have all zeros to + start with. + */ + register int x; + + for (x = 0; x < num_vert; x++) + *(vert_norms + x) = 0; +} + +void init_vert_texture(int num_vert) +{ + /* Initialize vertex/normal array to have all zeros to + start with. + */ + register int x; + + for (x = 0; x < num_vert; x++) + *(vert_texture + x) = 0; +} + +BOOL InitVertTable( int nSize ) +{ + register int nIndex; + + /* Initialize the vertex table */ + PolVerts = (ListHead**) malloc(sizeof(ListHead*) * nSize ); + if ( PolVerts ) + { + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + PolVerts[nIndex] = NULL; + } + return( TRUE ); + } + return( FALSE ); +} + +BOOL InitFaceTable( int nSize ) +{ + register int nIndex; + + /* Initialize the face table */ + PolFaces = (ListHead**) malloc(sizeof(ListHead*) * nSize ); + if ( PolFaces ) + { + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + PolFaces[nIndex] = NULL; + } + return( TRUE ); + } + return( FALSE ); +} + +BOOL InitEdgeTable( int nSize ) +{ + register int nIndex; + + /* Initialize the edge table */ + PolEdges = (ListHead**) malloc(sizeof(ListHead*) * nSize ); + if ( PolEdges ) + { + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + PolEdges[nIndex] = NULL; + } + return( TRUE ); + } + return( FALSE ); +} + + +void InitStripTable( ) +{ + + PLISTHEAD pListHead; + + /* Initialize the strip table */ + pListHead = ( PLISTHEAD ) malloc(sizeof(ListHead)); + if ( pListHead ) + { + InitList( pListHead ); + strips[0] = pListHead; + } + else + { + printf("Out of memory !\n"); + exit(0); + } + +} + +void Init_Table_SGI() +{ + PLISTHEAD pListHead; + int max_adj = 60; + register int x; + + /* This routine will initialize the table that will + have the faces sorted by the number of adjacent polygons + to it. + */ + + for (x=0; x< max_adj; x++) + { + /* We are allowing the max number of sides of a polygon + to be max_adj. + */ + pListHead = ( PLISTHEAD ) malloc(sizeof(ListHead)); + if ( pListHead ) + { + InitList( pListHead ); + array[x] = pListHead; + } + else + { + printf("Out of memory !\n"); + exit(0); + } + } +} + +void BuildVertTable( int nSize ) +{ + register int nIndex; + PLISTHEAD pListHead; + + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + pListHead = ( PLISTHEAD ) malloc(sizeof(ListHead)); + if ( pListHead ) + { + InitList( pListHead ); + PolVerts[nIndex] = pListHead; + } + else + return; + + } +} + + +void BuildFaceTable( int nSize ) +{ + register int nIndex; + PLISTHEAD pListHead; + + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + pListHead = ( PLISTHEAD ) malloc(sizeof(ListHead)); + if ( pListHead ) + { + InitList( pListHead ); + PolFaces[nIndex] = pListHead; + } + else + return; + + } +} + +void BuildEdgeTable( int nSize ) +{ + register int nIndex; + PLISTHEAD pListHead; + + for ( nIndex=0; nIndex < nSize; nIndex++ ) + { + pListHead = ( PLISTHEAD ) malloc(sizeof(ListHead)); + if ( pListHead ) + { + InitList( pListHead ); + PolEdges[nIndex] = pListHead; + } + else + return; + } +} + +void Start_Face_Struct(int numfaces) +{ + if (InitFaceTable(numfaces)) + { + BuildFaceTable(numfaces); + } +} + +void Start_Edge_Struct(int numverts) +{ + if (InitEdgeTable(numverts)) + { + BuildEdgeTable(numverts); + } +} + + diff --git a/Tools/Stripe_w/init.h b/Tools/Stripe_w/init.h new file mode 100644 index 000000000..fe9a05fd7 --- /dev/null +++ b/Tools/Stripe_w/init.h @@ -0,0 +1,30 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: init.h +-----------------------------------------------------------------------*/ + +void init_vert_norms(int num_vert); +void init_vert_texture(int num_vert); +BOOL InitVertTable( int nSize ); +BOOL InitFaceTable( int nSize ); +BOOL InitEdgeTable( int nSize ); +void InitStripTable( ); +void Init_Table_SGI(); +void BuildVertTable( int nSize ); +void BuildFaceTable( int nSize ); +void BuildEdgeTable( int nSize ); +void Start_Face_Struct(int numfaces); +void Start_Edge_Struct(int numverts); + + + + + + diff --git a/Tools/Stripe_w/local.c b/Tools/Stripe_w/local.c new file mode 100644 index 000000000..3f3e69da1 --- /dev/null +++ b/Tools/Stripe_w/local.c @@ -0,0 +1,119 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: local.c + This file contains the code that initializes the data structures for + the local algorithm, and starts the local algorithm going. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include "polverts.h" +#include "local.h" +#include "triangulatex.h" +#include "sturctsex.h" +#include "common.h" +#include "outputex.h" +#include "util.h" +#include "init.h" + +void Find_StripsEx(FILE *output, FILE *strip,int *ties, int tie, + int triangulate, int swaps, int *next_id) +{ + /* This routine will peel off the strips from the model */ + + ListHead *pListHead; + P_ADJACENCIES temp = NULL; + register int max,bucket=0; + BOOL whole_flag = TRUE; + int dummy = 0; + + /* Set the last known input edge to be null */ + Last_Edge(&dummy,&dummy,&dummy,1); + + /* Search for lowest adjacency polygon and output strips */ + while (whole_flag) + { + bucket = -1; + /* Search for polygons in increasing number of adjacencies */ + while (bucket < 59) + { + bucket++; + pListHead = array[bucket]; + max = NumOnList(pListHead); + if (max > 0) + { + temp = (P_ADJACENCIES) PeekList(pListHead,LISTHEAD,0); + if (temp == NULL) + { + printf("Error in the buckets%d %d %d\n",bucket,max,0); + exit(0); + } + Polygon_OutputEx(temp,temp->face_id,bucket,pListHead, + output,strip,ties,tie,triangulate,swaps,next_id,1); + /* Try to extend backwards, if the starting polygon in the + strip had 2 or more adjacencies to begin with + */ + if (bucket >= 2) + Extend_BackwardsEx(temp->face_id,output,strip,ties,tie,triangulate,swaps,next_id); + break; + } + } + /* Went through the whole structure, it is empty and we are done. + */ + if ((bucket == 59) && (max == 0)) + whole_flag = FALSE; + + /* We just finished a strip, send dummy data to signal the end + of the strip so that we can output it. + */ + else + { + Output_TriEx(-1,-2,-3,output,-1,-10,1); + Last_Edge(&dummy,&dummy,&dummy,1); + } + } +} + + +void SGI_Strip(int num_verts,int num_faces,FILE *output, + int ties,int triangulate) + +{ + FILE *strip; + int next_id = -1,t=0; + + strip = fopen("output.d","w"); + /* We are going to output and find triangle strips + according the the method that SGI uses, ie always + choosing as the next triangle in our strip the triangle + that has the least number of adjacencies. We do not have + all triangles and will be triangulating on the fly those + polygons that have more than 3 sides. + */ + + /* Build a table that has all the polygons sorted by the number + of polygons adjacent to it. + */ + /* Initialize it */ + Init_Table_SGI(); + /* Build it */ + Build_SGI_Table(num_verts,num_faces); + + /* We will have a structure to hold all the strips, until + outputted. + */ + InitStripTable(); + /* Now we have the structure built to find the polygons according + to the number of adjacencies. Now use the SGI Method to find + strips according to the adjacencies + */ + Find_StripsEx(output,strip,&t,ties,triangulate,ON,&next_id); +} diff --git a/Tools/Stripe_w/local.h b/Tools/Stripe_w/local.h new file mode 100644 index 000000000..df5256f6a --- /dev/null +++ b/Tools/Stripe_w/local.h @@ -0,0 +1,20 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE:local.h +-----------------------------------------------------------------------*/ + +void Local_Polygon_Output(); +void Local_Output_Tri(); +int Different(); +void Local_Non_Blind_Triangulate(); +void Local_Blind_Triangulate(); +void Local_Triangulate_Polygon(); +void SGI_Strip(int num_verts,int num_faces,FILE *output, + int ties,int triangulate); diff --git a/Tools/Stripe_w/newpolve.c b/Tools/Stripe_w/newpolve.c new file mode 100644 index 000000000..6267bbc42 --- /dev/null +++ b/Tools/Stripe_w/newpolve.c @@ -0,0 +1,1659 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: newpolve.c + This routine contains the bulk of the code that will find the + patches of quads in the data model +*/ +/*---------------------------------------------------------------------*/ + +#include <stdlib.h> +#include "polverts.h" +#include "extend.h" +#include "output.h" +#include "triangulate.h" +#include "common.h" +#include "util.h" +#include "global.h" +#include "init.h" +#include "add.h" + +ListHead **PolVerts; +ListHead **PolFaces; +ListHead **PolEdges; +int length; +BOOL resetting = FALSE; +int ids[STRIP_MAX]; +int added_quad = 0; +BOOL reversed = FALSE; +int patch = 0; +extern int *vn; +extern int *vt; + +int Calculate_Walks(int lastvert,int y, PF_FACES temp2) +{ + /* Find the length of the walk */ + + int previous_edge1, previous_edge2; + register int nextvert,numverts,counter,walk=0; + BOOL flag; + F_EDGES *node; + ListHead *pListHead; + static int seen = 0; + + /* Find the edge that we are currently on */ + if (y != 3) + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon + y + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon); + } + + temp2->seen = seen; + counter = y; + + /*Find the adjacent face to this edge */ + node = *(temp2->VertandId+y); + if (node->edge[2] != lastvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + /* Keep walking in this direction until we cannot do so */ + while ((nextvert != lastvert) && (nextvert != -1)) + { + walk++; + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp2->nPolSize; + if ((numverts != 4) || (temp2->seen == seen)) + { + walk--; + nextvert = -1; + } + else + { + temp2->seen = seen; + /* Find edge that is not adjacent to the previous one */ + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon + counter + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + + node = *(temp2->VertandId + counter); + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + } + } + seen++; + return walk; +} + + +BOOL Check_Right(int last_seen,PF_FACES temp2,int y,int face_id) +{ + /* Check when we last saw the face to the right of the current + one. We want to have seen it just before we started this strip + */ + + F_EDGES *node; + ListHead *pListHead; + register int nextvert,oldy; + PF_FACES t; + + oldy = y; + if (y != 3) + y = y+1; + else + y = 0; + node = *(temp2->VertandId + y); + if (face_id == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + if (nextvert == -1) + return FALSE; + + pListHead = PolFaces[nextvert]; + t = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + if (t->seen != (last_seen - 1)) + { + /* maybe because of the numbering, we are not + on the right orientation, so we have to check the + opposite one to be sure + */ + if (oldy != 0) + y = oldy-1; + else + y = 3; + node = *(temp2->VertandId + y); + if (face_id == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + if (nextvert == -1) + return FALSE; + pListHead = PolFaces[nextvert]; + t = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + if (t->seen != (last_seen - 1)) + return FALSE; + } + return TRUE; +} + + +int Update_and_Test(PF_FACES temp2,int y,BOOL first,int distance,int lastvert, int val) +{ + + static int last_seen = 17; + int previous_edge1, previous_edge2; + register int original_distance,nextvert,numverts,counter; + BOOL flag; + F_EDGES *node; + ListHead *pListHead; + + original_distance = distance; + /* Find the edge that we are currently on */ + if (y != 3) + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon + y + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon); + } + + temp2->seen = val; + temp2->seen2 = val; + + node = *(temp2->VertandId+y); + if (lastvert != node->edge[2]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + /* Keep walking in this direction until we cannot do so or + we go to distance */ + while ((distance > 0) && (nextvert != lastvert) && (nextvert != -1)) + { + distance--; + + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + temp2->seen = val; + + if (temp2->seen2 == val) + { + last_seen++; + return (original_distance - distance); + } + + temp2->seen2 = val; + + numverts = temp2->nPolSize; + + if (numverts != 4) + nextvert = -1; + + else if ((!first) && (!(Check_Right(last_seen,temp2,y,nextvert)))) + { + last_seen++; + return (original_distance - distance); + } + else + { + /* Find edge that is not adjacent to the previous one */ + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon + counter + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + if ( ((*(temp2->walked+counter) == -1) && + (*(temp2->walked+counter+2) == -1))) + { + printf("There is an error in the walks!\n"); + printf("1Code %d %d \n",*(temp2->walked+counter),*(temp2->walked+counter+2)); + exit(0); + } + else + { + if ((*(temp2->walked+counter) == -1) && + (*(temp2->walked+counter-2) == -1)) + { + printf("There is an error in the walks!\n"); + printf("2Code %d %d \n",*(temp2->walked+counter),*(temp2->walked+counter-2)); + exit(0); + } + } + node = *(temp2->VertandId + counter); + y = counter; + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + } + } + + last_seen++; + + if (distance != 0) + { + if (((nextvert == -1) || (nextvert == lastvert)) && (distance != 1)) + return (original_distance - distance); + } + return original_distance; +} + + +int Test_Adj(PF_FACES temp2,int x,int north,int distance,int lastvert, int value) +{ + /* if first time, then just update the last seen field */ + if (x==1) + return(Update_and_Test(temp2,north,TRUE,distance,lastvert,value)); + /* else we have to check if we are adjacent to the last strip */ + else + return(Update_and_Test(temp2,north,FALSE,distance,lastvert,value)); +} + +void Get_Band_Walk(PF_FACES temp2,int face_id,int *dir1,int *dir2, + int orientation,int cutoff_length) +{ + int previous_edge1, previous_edge2; + F_EDGES *node; + ListHead *pListHead; + register int walk = 0, nextvert,numverts,counter; + BOOL flag; + + /* Get the largest band that will include this face, starting + from orientation. Save the values of the largest band + (either north and south together, or east and west together) + in the direction variables. + */ + /* Find the edge that we are currently on */ + if (orientation != 3) + { + previous_edge1 = *(temp2->pPolygon + orientation); + previous_edge2 = *(temp2->pPolygon + orientation + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon + orientation ); + previous_edge2 = *(temp2->pPolygon); + } + + if (orientation == 0) + { + if (*dir1 > *(temp2->walked + 1)) + *dir1 = *(temp2->walked + 1); + if (*dir2 > *(temp2->walked + 3)) + *dir2 = *(temp2->walked + 3); + } + else if (orientation == 3) + { + if (*dir1 > *(temp2->walked + orientation - 3)) + *dir1 = *(temp2->walked + orientation - 3) ; + if (*dir2 > *(temp2->walked + orientation -1 )) + *dir2 = *(temp2->walked + orientation - 1); + } + else + { + if (*dir1 > *(temp2->walked + orientation - 1)) + *dir1 = *(temp2->walked + orientation -1) ; + if (*dir2 > *(temp2->walked+ orientation + 1)) + *dir2 = *(temp2->walked + orientation + 1); + } + + /* if we know already that we can't extend the + band from this face, we do not need to do the walk + */ + if ((*dir1 != 0) && (*dir2 != 0)) + { + /* Find the adjacent face to this edge */ + node = *(temp2->VertandId+orientation); + if (face_id == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + } + else + nextvert = -1; /* leave w/o walking */ + + /* Keep walking in this direction until we cannot do so */ + while ((nextvert != face_id) && (nextvert != -1)) + { + walk++; + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp2->nPolSize; + if ((numverts != 4) || (walk > cutoff_length)) + nextvert = -1; + else + { + /* Find edge that is not adjacent to the previous one */ + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon + counter + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + + /* find out how far we can extend in the 2 directions + along this new face in the walk + */ + if (counter == 0) + { + if (*dir1 > *(temp2->walked + 1)) + *dir1 = *(temp2->walked + 1); + if (*dir2 > *(temp2->walked + 3)) + *dir2 = *(temp2->walked + 3); + } + else if (counter == 3) + { + if (*dir1 > *(temp2->walked + counter - 3)) + *dir1 = *(temp2->walked + counter - 3) ; + if (*dir2 > *(temp2->walked + counter -1 )) + *dir2 = *(temp2->walked + counter -1); + } + else + { + if (*dir1 > *(temp2->walked + counter - 1)) + *dir1 = *(temp2->walked + counter -1) ; + if (*dir2 > *(temp2->walked + counter + 1)) + *dir2 = *(temp2->walked + counter + 1); + } + + /* if we know already that we can't extend the + band from this face, we do not need to do the walk + */ + if ((*dir1 == 0) || (*dir2 == 0)) + nextvert = -1; + if (nextvert != -1) + { + node = *(temp2->VertandId + counter); + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + } + + } + } +} + + + + +int Find_Max(PF_FACES temp2,int lastvert,int north,int left, + int *lastminup,int *lastminleft) +{ + int temp,walk,counter,minup,x,band_value; + int previous_edge1, previous_edge2; + F_EDGES *node; + ListHead *pListHead; + BOOL flag; + static int last_seen = 0; + register int smallest_so_far,nextvert,max=-1; + + *lastminup = MAX_BAND; + *lastminleft = 1; + + if (left == 3) + { + previous_edge1 = *(temp2->pPolygon + left); + previous_edge2 = *(temp2->pPolygon); + } + + else + { + previous_edge1 = *(temp2->pPolygon + left + 1); + previous_edge2 = *(temp2->pPolygon + left); + } + + temp2->seen = last_seen; + walk = *(temp2->walked + left); + + for (x=1;x<=(walk+1); x++) + { + /* test to see if we have a true band + that is, are they adjacent to each other + */ + + minup = *(temp2->walked + north) + 1; + + /* if we are at the very first face, then we do not + have to check the adjacent faces going up + and our north distance is the distance of this face's + north direction. + */ + if (x == 1) + { + *lastminup = minup; + minup = Test_Adj(temp2,x,north,*lastminup,lastvert,last_seen); + *lastminup = minup; + smallest_so_far = minup; + } + + + /* find the largest band that we can have */ + if (minup < (*lastminup)) + { + /* see if we really can go up all the way + temp should by less than our equal to minup + if it is less, then one of the faces was not + adjacent to those next to it and the band height + will be smaller + */ + temp = Test_Adj(temp2,x,north,minup,lastvert,last_seen); + if (temp > minup) + { + printf("There is an error in the test adj\n"); + exit(0); + } + minup = temp; + band_value = x * minup; + if (minup < smallest_so_far) + { + if (band_value > max) + { + smallest_so_far = minup; + *lastminup = minup; + *lastminleft = x; + max = band_value; + } + else + smallest_so_far = minup; + } + else + { + band_value = x * smallest_so_far; + if (band_value > max) + { + *lastminup = smallest_so_far; + *lastminleft = x; + max = band_value; + } + } + } + else + { + if (x != 1) + { + temp = Test_Adj(temp2,x,north,smallest_so_far,lastvert,last_seen); + if (temp > smallest_so_far) + { + printf("There is an error in the test adj\n"); + exit(0); + } + smallest_so_far = temp; + } + band_value = x * smallest_so_far; + if (band_value > max) + { + *lastminup = smallest_so_far; + *lastminleft = x; + max = band_value; + } + } + if ( x != (walk + 1)) + { + node = *(temp2->VertandId+left); + if (lastvert == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + lastvert = nextvert; + + if (nextvert == -1) + return max; + + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead, LISTHEAD, 0); + + /* if we have visited this face before, then there is an error */ + if (((*(temp2->walked) == -1) && (*(temp2->walked+1) == -1) && + (*(temp2->walked+2) == -1) && (*(temp2->walked+3) == -1)) + || (temp2->nPolSize !=4) || (temp2->seen == last_seen)) + { + + if (lastvert == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + if (nextvert == -1) + return max; + lastvert = nextvert; + /* Last attempt to get the face ... */ + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead, LISTHEAD, 0); + if (((*(temp2->walked) == -1) && (*(temp2->walked+1) == -1) && + (*(temp2->walked+2) == -1) && (*(temp2->walked+3) == -1)) + || (temp2->nPolSize !=4) || (temp2->seen == last_seen)) + return max; /* The polygon was not saved with the edge, not + enough room. We will get the walk when we come + to that polygon later. + */ + } + /*else + {*/ + counter = 0; + flag = TRUE; + temp2->seen = last_seen; + + while ((counter < 3) && (flag)) + { + + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + /*}*/ + + /* Get the IDs of the next edge */ + left = counter; + north = left+1; + if (left ==3) + north = 0; + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter + 1); + previous_edge2 = *(temp2->pPolygon + counter); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + + } + +} +last_seen++; +return max; +} + +void Mark_Face(PF_FACES temp2, int color1, int color2, + int color3, FILE *output_file, BOOL end, int *edge1, int *edge2, + int *face_id, int norms, int texture) +{ + static int last_quad[4]; + int x,y,z=0; + int saved[2]; + static int output1, output2,last_id; + BOOL cptexture = FALSE; + + /* Are we done with the patch? If so return the last edge that + we will come out on, and that will be the edge that we will + start to extend upon. + */ + + if (end) + { + *edge1 = output1; + *edge2 = output2; + *face_id = last_id; + return; + } + + cptexture = texture; + last_id = *face_id; + *(temp2->walked) = -1; + *(temp2->walked+1) = -1; + *(temp2->walked+2) = -1; + *(temp2->walked+3) = -1; + added_quad++; + temp2->nPolSize = 1; + + if (patch == 0) + { + /* At the first quad in the strip -- save it */ + last_quad[0] = *(temp2->pPolygon); + last_quad[1] = *(temp2->pPolygon+1); + last_quad[2] = *(temp2->pPolygon+2); + last_quad[3] = *(temp2->pPolygon+3); + patch++; + } + else + { + /* Now we have a triangle to output, find the edge in common */ + for (x=0; x < 4 ;x++) + { + for (y=0; y< 4; y++) + { + if (last_quad[x] == *(temp2->pPolygon+y)) + { + saved[z++] = last_quad[x]; + if (z > 2) + { + /* This means that there was a non convex or + an overlapping polygon + */ + z--; + break; + } + } + } + } + + if (z != 2) + { + printf("Z is not 2 %d \n",patch); + printf("4 %d %d %d %d %d %d %d\n",*(temp2->pPolygon), + *(temp2->pPolygon+1),*(temp2->pPolygon+2),*(temp2->pPolygon+3), + color1,color2,color3); + printf("%d %d %d %d\n",last_quad[0],last_quad[1],last_quad[2],last_quad[3]); + exit(1); + } + + if (patch == 1) + { + /* First one to output, there was no output edge */ + patch++; + x = Adjacent(saved[0],saved[1],last_quad,4); + y = Adjacent(saved[1],saved[0],last_quad,4); + + /* Data might be mixed and we do not have textures for some of the vertices */ + if ((texture) && ( ((vt[x]) == 0) || ((vt[y])==0) || ((vt[saved[1]])==0))) + cptexture = FALSE; + + if ((!norms) && (!cptexture)) + { + fprintf(output_file,"\nt %d %d %d ",x+1,y+1,saved[1]+1); + fprintf(output_file,"%d ",saved[0]+1); + } + else if ((norms) && (!cptexture)) + { + fprintf(output_file,"\nt %d//%d %d//%d %d//%d ",x+1,vn[x] +1, + y+1,vn[y] +1, + saved[1]+1,vn[saved[1]]+1); + fprintf(output_file,"%d//%d ",saved[0]+1,vn[saved[0]]+1); + } + else if ((cptexture) && (!norms)) + { + fprintf(output_file,"\nt %d/%d %d/%d %d/%d ",x+1,vt[x] +1, + y+1,vt[y] +1, + saved[1]+1,vt[saved[1]]+1); + fprintf(output_file,"%d//%d ",saved[0]+1,vt[saved[0]]+1); + } + else + { + fprintf(output_file,"\nt %d/%d/%d %d/%d/%d %d/%d/%d ",x+1,vt[x]+1,vn[x] +1, + y+1,vt[y]+1,vn[y] +1, + saved[1]+1,vt[saved[1]]+1,vn[saved[1]]+1); + fprintf(output_file,"%d/%d/%d ",saved[0]+1,vt[saved[0]]+1,vn[saved[0]]+1); + } + + x = Adjacent(saved[0],saved[1],temp2->pPolygon,4); + y = Adjacent(saved[1],saved[0],temp2->pPolygon,4); + + /* Data might be mixed and we do not have textures for some of the vertices */ + if ((texture) && ( (vt[x] == 0) || (vt[y]==0))) + { + if (cptexture) + fprintf(output_file,"\nq "); + cptexture = FALSE; + } + if ((!norms) && (!cptexture)) + { + fprintf(output_file,"%d ",x+1); + fprintf(output_file,"%d ",y+1); + } + else if ((norms) && (!cptexture)) + { + fprintf(output_file,"%d//%d ",x+1,vn[x]+1); + fprintf(output_file,"%d//%d ",y+1,vn[y]+1); + } + else if ((cptexture) && (!norms)) + { + fprintf(output_file,"%d/%d ",x+1,vt[x]+1); + fprintf(output_file,"%d/%d ",y+1,vt[y]+1); + } + else + { + fprintf(output_file,"%d/%d/%d ",x+1,vt[x]+1,vn[x]+1); + fprintf(output_file,"%d/%d/%d ",y+1,vt[y]+1,vn[y]+1); + } + + output1 = x; + output2 = y; + } + + else + { + x = Adjacent(output2,output1,temp2->pPolygon,4); + y = Adjacent(output1,output2,temp2->pPolygon,4); + /* Data might be mixed and we do not have textures for some of the vertices */ + if ((texture) && ( ((vt[x]) == 0) || ((vt[y])==0) )) + texture = FALSE; + + if ((!norms) && (!texture)) + { + fprintf(output_file,"\nq %d ",x+1); + fprintf(output_file,"%d ",y+1); + } + else if ((norms) && (!texture)) + { + fprintf(output_file,"\nq %d//%d ",x+1,vn[x]+1); + fprintf(output_file,"%d//%d ",y+1,vn[y]+1); + } + else if ((texture) && (!norms)) + { + fprintf(output_file,"\nq %d/%d ",x+1,vt[x]+1); + fprintf(output_file,"%d/%d ",y+1,vt[y]+1); + } + else + { + fprintf(output_file,"\nq %d/%d/%d ",x+1,vt[x]+1,vn[x]+1); + fprintf(output_file,"%d/%d/%d ",y+1,vt[y]+1,vn[y]+1); + } + + output1 = x; + output2 = y; + } + + last_quad[0] = *(temp2->pPolygon); + last_quad[1] = *(temp2->pPolygon+1); + last_quad[2] = *(temp2->pPolygon+2); + last_quad[3] = *(temp2->pPolygon+3); + } +} + +void Fast_Reset(int x) +{ + register int y,numverts; + register int front_walk, back_walk; + ListHead *pListHead; + PF_FACES temp = NULL; + + pListHead = PolFaces[x]; + temp = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp->nPolSize; + + front_walk = 0; + back_walk = 0; + resetting = TRUE; + + /* we are doing this only for quads */ + if (numverts == 4) + { + /* for each face not seen yet, do North and South together + and East and West together + */ + for (y=0;y<2;y++) + { + /* Check if the opposite sides were seen already */ + /* Find walk for the first edge */ + front_walk = Calculate_Walks(x,y,temp); + /* Find walk in the opposite direction */ + back_walk = Calculate_Walks(x,y+2,temp); + /* Now put into the data structure the numbers that + we have found + */ + Assign_Walk(x,temp,front_walk,y,back_walk); + Assign_Walk(x,temp,back_walk,y+2,front_walk); + } + } + resetting = FALSE; +} + + +void Reset_Max(PF_FACES temp2,int face_id,int north,int last_north, int orientation, + int last_left,FILE *output_file,int color1,int color2,int color3, + BOOL start) +{ + int previous_edge1,previous_edge2; + F_EDGES *node; + ListHead *pListHead; + int f,t,nextvert,counter; + BOOL flag; + + + /* Reset walks on faces, since we just found a patch */ + if (orientation !=3) + { + previous_edge1 = *(temp2->pPolygon + orientation+1); + previous_edge2 = *(temp2->pPolygon + orientation ); + } + else + { + previous_edge1 = *(temp2->pPolygon + orientation ); + previous_edge2 = *(temp2->pPolygon); + } + + /* only if we are going left, otherwise there will be -1 there */ + /*Find the adjacent face to this edge */ + + for (t = 0; t <=3 ; t++) + { + node = *(temp2->VertandId+t); + + if (face_id == node->edge[1]) + f = node->edge[2]; + else + f = node->edge[1]; + + if (f != -1) + Fast_Reset(f); + } + + node = *(temp2->VertandId+orientation); + if (face_id == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + while ((last_left--) > 1) + { + + if (start) + Reset_Max(temp2,face_id,orientation,last_left,north,last_north,output_file,color1,color2,color3,FALSE); + + face_id = nextvert; + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + if ((temp2->nPolSize != 4) && (temp2->nPolSize != 1)) + { + /* There is more than 2 polygons on the edge, and we could have + gotten the wrong one + */ + if (nextvert != node->edge[1]) + nextvert = node->edge[1]; + else + nextvert = node->edge[2]; + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + node = *(temp2->VertandId+orientation); + } + + + if (!start) + { + for (t = 0; t <=3 ; t++) + { + node = *(temp2->VertandId+t); + + if (face_id == node->edge[1]) + f = node->edge[2]; + else + f = node->edge[1]; + + if (f != -1) + Fast_Reset(f); + } + } + + + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter+1); + previous_edge2 = *(temp2->pPolygon + counter); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + orientation = counter; + + node = *(temp2->VertandId + counter); + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + if (!reversed) + { + if (counter != 3) + north = counter +1; + else + north = 0; + } + else + { + if (counter != 0) + north = counter -1; + else + north = 3; + + } + } +if (start) + Reset_Max(temp2,face_id,orientation,last_left,north,last_north,output_file,color1,color2,color3,FALSE); +else if (nextvert != -1) + Fast_Reset(nextvert); + +} + + +int Peel_Max(PF_FACES temp2,int face_id,int north,int last_north, int orientation, + int last_left,FILE *output_file,int color1,int color2,int color3, + BOOL start, int *swaps_added, int norms, int texture) +{ + int end1,end2,last_id,s=0,walk = 0; + int previous_edge1,previous_edge2; + static int last_seen = 1000; + F_EDGES *node; + ListHead *pListHead; + int nextvert,numverts,counter,dummy,tris=0; + BOOL flag; + + /* Peel the patch from the model. + We will try and extend off the end of each strip in the patch. We will return the + number of triangles completed by this extension only, and the number of swaps + in the extension only. + */ + patch = 0; + + if (orientation !=3) + { + previous_edge1 = *(temp2->pPolygon + orientation+1); + previous_edge2 = *(temp2->pPolygon + orientation ); + } + else + { + previous_edge1 = *(temp2->pPolygon + orientation ); + previous_edge2 = *(temp2->pPolygon); + } + + + walk = *(temp2->walked + orientation); + + /* only if we are going left, otherwise there will be -1 there */ + if ((start) && ((walk+1) < last_left)) + { + printf("There is an error in the left %d %d\n",walk,last_left); + exit(0); + } + + /* Find the adjacent face to this edge */ + node = *(temp2->VertandId+orientation); + if (face_id == node->edge[1]) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + temp2->seen = last_seen; + + + while ((last_left--) > 1) + { + if (start) + tris += Peel_Max(temp2,face_id,orientation,last_left,north,last_north,output_file, + color1,color2,color3,FALSE,swaps_added,norms,texture); + else + Mark_Face(temp2,color1,color2,color3,output_file,FALSE,&dummy,&dummy,&face_id,norms,texture); + + + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp2->nPolSize; + + if ((numverts != 4) || (temp2->seen == last_seen) + || (nextvert == -1)) + { + + /* There is more than 2 polygons on the edge, and we could have + gotten the wrong one + */ + if (nextvert != node->edge[1]) + nextvert = node->edge[1]; + else + nextvert = node->edge[2]; + pListHead = PolFaces[nextvert]; + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp2->nPolSize; + if ((numverts != 4) || (temp2->seen == last_seen) ) + { + printf("Peel 2 %d\n",numverts); + exit(1); + } + } + + face_id = nextvert; + temp2->seen = last_seen; + + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter+1); + previous_edge2 = *(temp2->pPolygon + counter); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + orientation = counter; + + node = *(temp2->VertandId + counter); + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + if (!reversed) + { + if (counter != 3) + north = counter +1; + else + north = 0; + } + else + { + if (counter != 0) + north = counter -1; + else + north = 3; + } +} + +if (start) + tris += Peel_Max(temp2,face_id,orientation,last_left,north,last_north,output_file, + color1,color2,color3,FALSE,swaps_added,norms,texture); +else + Mark_Face(temp2,color1,color2,color3,output_file,FALSE,&dummy,&dummy,&face_id,norms,texture);/* do the last face */ + +last_seen++; + +/* Get the edge that we came out on the last strip of the patch */ +Mark_Face(NULL,0,0,0,output_file,TRUE,&end1,&end2,&last_id,norms,texture); +tris += Extend_Face(last_id,end1,end2,&s,output_file,color1,color2,color3,vn,norms,vt,texture); +*swaps_added = *swaps_added + s; +return tris; +} + + + +void Find_Bands(int numfaces, FILE *output_file, int *swaps, int *bands, + int *cost, int *tri, int norms, int *vert_norms, int texture, int *vert_texture) +{ + + register int x,y,max1,max2,numverts,face_id,flag,maximum = 25; + ListHead *pListHead; + PF_FACES temp = NULL; + int color1 = 0, color2 = 100, color3 = 255; + int larger,smaller; + int north_length1,last_north,left_length1,last_left,north_length2,left_length2; + int total_tri = 0, total_swaps = 0,last_id; + int end1, end2,s=0; + register int cutoff = 20; + + /* Code that will find the patches. "Cutoff" will be + the cutoff of the area of the patches that we will be allowing. After + we reach this cutoff length, then we will run the local algorithm on the + remaining faces. + */ + + /* For each faces that is left find the largest possible band that we can + have with the remaining faces. Note that we will only be finding patches + consisting of quads. + */ + + vn = vert_norms; + vt = vert_texture; + y=1; + *bands = 0; + + while ((maximum >= cutoff)) + { + y++; + maximum = -1; + for (x=0; x<numfaces; x++) + { + /* Used to produce the triangle strips */ + + /* for each face, get the face */ + pListHead = PolFaces[x]; + temp = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp->nPolSize; + + /* we are doing this only for quads */ + if (numverts == 4) + { + /* We want a face that is has not been used yet, + since we know that that face must be part of + a band. Then we will find the largest band that + the face may be contained in + */ + + /* Doing the north and the left */ + if ((*(temp->walked) != -1) && (*(temp->walked+3) != -1)) + max1 = Find_Max(temp,x,0,3,&north_length1,&left_length1); + if ((*(temp->walked+1) != -1) && (*(temp->walked+2) != -1)) + max2 = Find_Max(temp,x,2,1,&north_length2,&left_length2); + if ((max1 != (north_length1 * left_length1)) || + (max2 != (north_length2 * left_length2))) + { + printf("Max1 %d, %d %d Max2 %d, %d %d\n",max1,north_length1,left_length1,max2,north_length2,left_length2); + exit(0); + } + + + if ((max1 > max2) && (max1 > maximum)) + { + maximum = max1; + face_id = x; + flag = 1; + last_north = north_length1; + last_left = left_length1; + /* so we know we saved max1 */ + } + else if ((max2 > maximum) ) + { + maximum = max2; + face_id = x; + flag = 2; + last_north = north_length2; + last_left = left_length2; + /* so we know we saved max2 */ + } + } + } + if ((maximum < cutoff) && (*bands == 0)) + return; + pListHead = PolFaces[face_id]; + temp = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + /* There are no patches that we found in this pass */ + if (maximum == -1) + break; + printf("The maximum is face %d area %d: lengths %d %d\n",face_id,maximum,last_north,last_left); + if (maximum == 16) + printf("Fran"); + + if (last_north > last_left) + { + larger = last_north; + smaller = last_left; + } + else + { + larger = last_left; + smaller = last_north; + } + + length = larger; + + if (flag == 1) + { + if (last_north > last_left) /* go north sequentially */ + { + total_tri += Peel_Max(temp,face_id,0,last_north,3,last_left,output_file,color1,color2,color3,TRUE,&s,norms,texture); + Reset_Max(temp,face_id,0,last_north,3,last_left,output_file,color1,color2,color3,TRUE); + total_swaps += s; + } + else + { + reversed = TRUE; + total_tri += Peel_Max(temp,face_id,3,last_left,0,last_north,output_file,color1,color2,color3,TRUE,&s,norms,texture); + Reset_Max(temp,face_id,3,last_left,0,last_north,output_file,color1,color2,color3,TRUE); + reversed = FALSE; + total_swaps += s; + } + + + /* Get the edge that we came out on the last strip of the patch */ + Mark_Face(NULL,0,0,0,NULL,TRUE,&end1,&end2,&last_id,norms,texture); + total_tri += Extend_Face(last_id,end1,end2,&s,output_file,color1,color2,color3,vn,norms,vt,texture); + total_swaps += s; + + } + else + { + if (last_north > last_left) + { + total_tri += Peel_Max(temp,face_id,2,last_north,1,last_left,output_file,color1,color2,color3,TRUE,&s,norms,texture); + Reset_Max(temp,face_id,2,last_north,1,last_left,output_file,color1,color2,color3,TRUE); + total_swaps += s; + } + else + { + reversed = TRUE; + total_tri += Peel_Max(temp,face_id,1,last_left,2,last_north,output_file,color1,color2,color3,TRUE,&s,norms,texture); + Reset_Max(temp,face_id,1,last_left,2,last_north,output_file,color1,color2,color3,TRUE); + reversed = FALSE; + total_swaps += s; + } + + /* Get the edge that we came out on on the patch */ + Mark_Face(NULL,0,0,0,NULL,TRUE,&end1,&end2,&last_id,norms,texture); + total_tri += Extend_Face(last_id,end1,end2,&s,output_file,color1,color2,color3,vn,norms,vt,texture); + total_swaps += s; + } + + /* Now compute the cost of transmitting this band, is equal to + going across the larger portion sequentially, + and swapping 3 times per other dimension + */ + + total_tri += (maximum * 2); + *bands = *bands + smaller; + } + + printf("We transmitted %d triangles,using %d swaps and %d strips\n",total_tri,total_swaps, *bands); + printf("COST %d\n",total_tri + total_swaps + *bands + *bands); + + *cost = total_tri + total_swaps + *bands + *bands; + *tri = total_tri; + added_quad = added_quad * 4; + *swaps = total_swaps; +} + + +void Save_Rest(int *numfaces) +{ + /* Put the polygons that are left into a data structure so that we can run the + stripping code on it. + */ + register int x,y=0,numverts; + ListHead *pListHead; + PF_FACES temp=NULL; + + for (x=0; x<*numfaces; x++) + { + /* for each face, get the face */ + pListHead = PolFaces[x]; + temp = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp->nPolSize; + /* If we did not do the face before add it to data structure with new + face id number + */ + if (numverts != 1) + { + CopyFace(temp->pPolygon,numverts,y+1,temp->pNorms); + y++; + } + /* Used it, so remove it */ + else + RemoveList(pListHead,(PLISTINFO) temp); + + } + *numfaces = y; +} + +void Assign_Walk(int lastvert,PF_FACES temp2, int front_walk,int y, + int back_walk) +{ +/* Go back and do the walk again, but this time save the lengths inside + the data structure. + y was the starting edge number for the front_walk length + back_walk is the length of the walk along the opposite edge + */ + int previous_edge1, previous_edge2; + register int walk = 0,nextvert,numverts,counter; + BOOL flag; + F_EDGES *node; + ListHead *pListHead; + static int seen = 0; + static BOOL first = TRUE; + BOOL wrap = FALSE, set = FALSE; + + /* In the "Fast_Reset" resetting will be true */ + if ((resetting) && (first)) + { + seen = 0; + first = FALSE; + } + + seen++; + /* Had a band who could be a cycle */ + if (front_walk == back_walk) + wrap = TRUE; + + /* Find the edge that we are currently on */ + if (y != 3) + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon + y + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon +y); + previous_edge2 = *(temp2->pPolygon); + } + + /* Assign the lengths */ + if (y < 2) + { + *(temp2->walked+y) = front_walk--; + *(temp2->walked+y+2) = back_walk++; + } + else + { + *(temp2->walked+y) = front_walk--; + *(temp2->walked+y-2) = back_walk++; + } + + /*Find the adjacent face to this edge */ + node = *(temp2->VertandId+y); + + if (node->edge[2] != lastvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + + temp2->seen3 = seen; + + /* Keep walking in this direction until we cannot do so */ + while ((nextvert != lastvert) && (nextvert != -1) && (front_walk >= 0)) + { + walk++; + pListHead = PolFaces[nextvert]; + + temp2 = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp2->nPolSize; + if ((numverts != 4)) + { + nextvert = -1; + /* Don't include this face in the walk */ + walk--; + } + else + { + /* Find edge that is not adjacent to the previous one */ + counter = 0; + flag = TRUE; + while ((counter < 3) && (flag)) + { + if ( ((*(temp2->pPolygon+counter) == previous_edge1) || + (*(temp2->pPolygon+counter+1) == previous_edge2)) || + ((*(temp2->pPolygon+counter) == previous_edge2) || + (*(temp2->pPolygon+counter+1) == previous_edge1)) ) + counter++; + else + flag = FALSE; + } + /* Get the IDs of the next edge */ + if (counter < 3) + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon + counter + 1); + } + else + { + previous_edge1 = *(temp2->pPolygon + counter); + previous_edge2 = *(temp2->pPolygon); + } + + + /* Put in the walk lengths */ + if (counter < 2) + { + if (((*(temp2->walked + counter) >= 0) + || (*(temp2->walked +counter + 2) >= 0))) + { + if ((resetting == FALSE) && ((temp2->seen3) != (seen-1))) + { + /* If there are more than 2 polygons adjacent + to an edge then we can be trying to assign more than + once. We will save the smaller one + */ + temp2->seen3 = seen; + if ( (*(temp2->walked+counter) <= front_walk) && + (*(temp2->walked+counter+2) <= back_walk) ) + return; + if (*(temp2->walked+counter) > front_walk) + *(temp2->walked+counter) = front_walk--; + else + front_walk--; + if (*(temp2->walked+counter+2) > back_walk) + *(temp2->walked+counter+2) = back_walk++; + else + back_walk++; + } + else if (resetting == FALSE) + { + /* if there was a cycle then all lengths are the same */ + walk--; + back_walk--; + front_walk++; + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter+2) = back_walk++; + } + else if (((temp2->seen3 == (seen-1)) + && (wrap) && (walk == 1)) || (set)) + { + /* if there was a cycle then all lengths are the same */ + set = TRUE; + walk--; + back_walk--; + front_walk++; + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter+2) = back_walk++; + } + else + { + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter+2) = back_walk++; + } + } /* if was > 0 */ + else + { + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter+2) = back_walk++; + } + } + + else + { + if (((*(temp2->walked + counter) >= 0 ) + || (*(temp2->walked +counter - 2) >= 0)) ) + { + if ((temp2->seen3 != (seen-1)) && (resetting == FALSE)) + { + /* If there are more than 2 polygons adjacent + to an edge then we can be trying to assign more than + once. We will save the smaller one + */ + temp2->seen3 = seen; + if ( (*(temp2->walked+counter) <= front_walk) && + (*(temp2->walked+counter-2) <= back_walk) ) + return; + if (*(temp2->walked+counter) > front_walk) + *(temp2->walked+counter) = front_walk--; + else + front_walk--; + if (*(temp2->walked+counter-2) > back_walk) + *(temp2->walked+counter-2) = back_walk++; + else + back_walk++; + } + else if (resetting == FALSE) + { + walk--; + back_walk--; + front_walk++; + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter-2) = back_walk++; + } + else if (((temp2->seen3 == (seen-1)) && (walk == 1) && (wrap)) + || (set)) + { + /* if there was a cycle then all lengths are the same */ + set = TRUE; + walk--; + back_walk--; + front_walk++; + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter-2) = back_walk++; + } + else + { + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter-2) = back_walk++; + } + } + else + { + temp2->seen3 = seen; + *(temp2->walked+counter) = front_walk--; + *(temp2->walked+counter-2) = back_walk++; + } + + } + if (nextvert != -1) + { + node = *(temp2->VertandId + counter); + if (node->edge[1] == nextvert) + nextvert = node->edge[2]; + else + nextvert = node->edge[1]; + } + + } +} +if ((EVEN(seen)) ) + seen+=2; +} + +void Save_Walks(int numfaces) +{ + int x,y,numverts; + int front_walk, back_walk; + ListHead *pListHead; + PF_FACES temp = NULL; + + for (x=0; x<numfaces; x++) + { + /* for each face, get the face */ + pListHead = PolFaces[x]; + temp = (PF_FACES) PeekList(pListHead,LISTHEAD,0); + numverts = temp->nPolSize; + front_walk = 0; + back_walk = 0; + + /* we are finding patches only for quads */ + if (numverts == 4) + { + /* for each face not seen yet, do North and South together + and East and West together + */ + for (y=0;y<2;y++) + { + /* Check if the opposite sides were seen already from another + starting face, if they were then there is no need to do the walk again + */ + + if ( ((*(temp->walked+y) == -1) && + (*(temp->walked+y+2) == -1) )) + { + /* Find walk for the first edge */ + front_walk = Calculate_Walks(x,y,temp); + /* Find walk in the opposite direction */ + back_walk = Calculate_Walks(x,y+2,temp); + /* Now put into the data structure the numbers that + we have found + */ + Assign_Walk(x,temp,front_walk,y,back_walk); + Assign_Walk(x,temp,back_walk,y+2,front_walk); + } + } + } + } +} + + diff --git a/Tools/Stripe_w/options.c b/Tools/Stripe_w/options.c new file mode 100644 index 000000000..7e1243e24 --- /dev/null +++ b/Tools/Stripe_w/options.c @@ -0,0 +1,181 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: options.c + This file contains routines that are used to determine the options + that were specified by the user +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include "options.h" +#include "global.h" + +int power_10(int power) +{ + /* Raise 10 to the power */ + register int i,p; + + p = 1; + for (i = 1; i <= power; ++i) + p = p * 10; + return p; +} + +float power_negative(int power) +{ + /* Raise 10 to the negative power */ + + register int i; + float p; + + p = (float)1; + for (i = 1; i<=power; i++) + p = p * (float).1; + return p; +} + +float convert_array(int num[],int stack_size) +{ + /* Convert an array of characters to an integer */ + + register int counter,c; + float temp =(float)0.0; + + for (c=(stack_size-1), counter = 0; c>=0; c--, counter++) + { + if (num[c] == -1) + /* We are at the decimal point, convert to decimal + less than 1 + */ + { + counter = -1; + temp = power_negative(stack_size - c - 1) * temp; + } + else + temp += power_10(counter) * num[c]; + } + + return(temp); +} + +double get_options(int argc, char **argv, int *f, int *t, int *tr, int *group) +{ + char c; + int count = 0; + int buffer[STRIP_MAX]; + int next = 0; + /* tie variable */ + enum tie_options tie = SEQUENTIAL; + /* triangulation variable */ + enum triangulation_options triangulate = PARTIAL; + /* normal difference variable (in degrees) */ + float norm_difference = (float)360.0; + /* file-type variable */ + enum file_options file_type = ASCII; + + /* User has the wrong number of options */ + if ((argc > 5) || (argc < 2)) + { + printf("Usage: bands -[file_option][ties_option][triangulation_option][normal_difference] in_file_name out_file_name\n"); + exit(0); + } + + /* Interpret the options specified */ + while (--argc > 0 && (*++argv)[0] == '-') + { + /* At the next option that was specified */ + next = 1; + while (c = *++argv[0]) + switch (c) + { + case 'f': + /* Use the first polygon we see. */ + tie = FIRST; + break; + + case 'r': + /* Randomly choose the next polygon */ + tie = RANDOM; + break; + + case 'a': + /* Alternate direction in choosing the next polygon */ + tie = ALTERNATE; + break; + + case 'l': + /* Use lookahead to choose the next polygon */ + tie = LOOK; + break; + + case 'q': + /* Try to reduce swaps */ + tie = SEQUENTIAL; + break; + + case 'p': + /* Use partial triangulation of polygons */ + triangulate = PARTIAL; + break; + + case 'w': + /* Use whole triangulation of polygons */ + triangulate = WHOLE; + break; + + case 'b': + /* Input file is in binary */ + file_type = BINARY; + break; + + case 'g': + /* Strips will be grouped according to the groups in + the data file. We will have to restrict strips to be + in the grouping of the data file. + */ + *group = 1; + + /* Get each the value of the integer */ + /* We have an integer */ + default: + if ((c >= '0') && (c <= '9')) + { + /* More than one normal difference specified, use the last one */ + if (next == 1) + { + count = 0; + next = 0; + } + buffer[count++] = ATOI(c); + } + /* At the decimal point */ + else if (c == '.') + { + /* More than one normal difference specified, use the last one */ + if (next == 1) + { + count = 0; + next = 0; + } + buffer[count++] = -1; + } + else + break; + } + } + /* Convert the buffer of characters to a floating pt integer */ + if (count != 0) + norm_difference = convert_array(buffer,count); + *f = file_type; + *t = tie; + *tr = triangulate; + return norm_difference; +} diff --git a/Tools/Stripe_w/options.h b/Tools/Stripe_w/options.h new file mode 100644 index 000000000..34c10bcc1 --- /dev/null +++ b/Tools/Stripe_w/options.h @@ -0,0 +1,17 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: options.h +-----------------------------------------------------------------------*/ + +double get_options(int argc, char **argv, int *f, int *t, int *tr, int *group); +enum file_options {ASCII,BINARY}; +enum tie_options {FIRST, RANDOM, ALTERNATE, LOOK, SEQUENTIAL}; +enum triangulation_options {PARTIAL,WHOLE}; + diff --git a/Tools/Stripe_w/output.c b/Tools/Stripe_w/output.c new file mode 100644 index 000000000..16eb6abe6 --- /dev/null +++ b/Tools/Stripe_w/output.c @@ -0,0 +1,579 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: output.c + This file contains routines that are finding and outputting the + strips from the local algorithm +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include "global.h" +#include "polverts.h" +#include "triangulate.h" +#include "partial.h" +#include "sturcts.h" +#include "ties.h" +#include "options.h" +#include "common.h" +#include "util.h" +#include "free.h" + +int *vn; +int *vt; +int norm; +int text; + +int Finished(int *swap, FILE *output, BOOL global) +{ + /* We have finished all the triangles, now is time to output to + the data file. In the strips data structure, every three ids + is a triangle. Now we see whether we can swap, or make a new strip + or continue the strip, and output the data accordingly to the + data file. + */ + int num,x,vertex1,vertex2; + ListHead *pListHead; + int id[2],other1,other2,index = 0,a,b,c; + P_STRIPS temp1,temp2,temp3,temp4,temp5,temp6; + BOOL cptexture; + *swap =0; + + cptexture = text; + pListHead = strips[0]; + if (pListHead == NULL) + return 0; + + num = NumOnList(pListHead); + // WILBUR + // printf ("There are %d triangles in the extend\n",num/3); + + /* Go through the list triangle by triangle */ + temp1 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 0); + temp2 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 1); + temp3 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 2); + + /* Next triangle for lookahead */ + temp4 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 3); + + + /* There is only one polygon in the strip */ + if (temp4 == NULL) + { + /* Data might be mixed and we do not have textures for some of the vertices */ + if ((text) && (vt[temp3->face_id] == 0)) + cptexture = FALSE; + if ((norm) && (!cptexture)) + fprintf(output,"%d//%d %d//%d %d//%d",temp3->face_id+1,vn[temp3->face_id]+1, + temp2->face_id+1,vn[temp2->face_id]+1, + temp1->face_id+1,vn[temp1->face_id]+1); + else if ((cptexture) && (!norm)) + fprintf(output,"%d/%d %d/%d %d/%d",temp3->face_id+1,vt[temp3->face_id]+1, + temp2->face_id+1,vt[temp2->face_id]+1, + temp1->face_id+1,vt[temp1->face_id]+1); + else if ((cptexture)&& (norm)) + fprintf(output,"%d/%d/%d %d/%d/%d %d/%d/%d",temp3->face_id+1,vt[temp3->face_id]+1,vn[temp3->face_id]+1, + temp2->face_id+1,vt[temp2->face_id]+1,vn[temp2->face_id]+1, + temp1->face_id+1,vt[temp1->face_id]+1,vn[temp1->face_id]+1); + else + fprintf(output,"%d %d %d",temp3->face_id+1,temp2->face_id+1,temp1->face_id+1); + Free_Strips(); + return 1; + } + + /* We have a real strip */ + temp5 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 4); + temp6 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 5); + + if ((temp1 == NULL) || (temp2 == NULL) || (temp3 == NULL) || (temp5 == NULL) || (temp6 == NULL)) + { + printf("There is an error in the output of the triangles\n"); + exit(0); + } + + /* Find the vertex in the first triangle that is not in the second */ + vertex1 = Different(temp1->face_id,temp2->face_id,temp3->face_id,temp4->face_id,temp5->face_id,temp6->face_id,&other1,&other2); + /* Find the vertex in the second triangle that is not in the first */ + vertex2 = Different(temp4->face_id,temp5->face_id,temp6->face_id,temp1->face_id,temp2->face_id,temp3->face_id,&other1,&other2); + + /* Lookahead for the correct order of the 2nd and 3rd vertex of the first triangle */ + temp1 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 6); + temp2 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 7); + temp3 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 8); + + if (temp1 != NULL) + other1 = Different(temp3->face_id,temp4->face_id,temp5->face_id,temp1->face_id,temp2->face_id,temp3->face_id,&other1,&a); + + id[index] = vertex1; index = !index; + id[index] = other1; index = !index; + id[index] = other2; index = !index; + + a = temp4->face_id; + b = temp5->face_id; + c = temp6->face_id; + + /* If we need to rearrange the first sequence because otherwise + there would have been a swap. + */ + + if ((temp3 != NULL) && (text) && ( vt[temp3->face_id]==0)) + cptexture = FALSE; + if ((norm) && (!cptexture)) + fprintf(output,"%d//%d %d//%d %d//%d ",vertex1+1,vn[vertex1]+1, + other1+1,vn[other1]+1,other2+1,vn[other2]+1); + else if ((cptexture) && (!norm)) + fprintf(output,"%d/%d %d/%d %d/%d ",vertex1+1,vt[vertex1]+1, + other1+1,vt[other1]+1,other2+1,vt[other2]+1); + else if ((cptexture) && (norm)) + fprintf(output,"%d/%d/%d %d/%d/%d %d/%d/%d ",vertex1+1,vt[vertex1]+1,vn[vertex1]+1, + other1+1,vt[other1]+1,vn[other1]+1,other2+1,vt[other2]+1,vn[other2]+1); + else { + fprintf(output,"%d %d %d ",vertex1+1,other1+1,other2+1); + } + + // original line + // for (x = 6; x < num ; x = x+3) + // Wilbur modified line + for (x = 6; x < num ; x = x+3) + { + /* Get the next triangle */ + temp1 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, x); + temp2 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, x+1); + temp3 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, x+2); + + /* Error checking */ + if (!(member(id[0],a,b,c)) || !(member(id[1],a,b,c)) || !(member(vertex2,a,b,c))) + { + /* If we used partial we might have a break in the middle of a strip */ + fprintf(output,"\nt "); + /* Find the vertex in the first triangle that is not in the second */ + vertex1 = Different(a,b,c,temp1->face_id,temp2->face_id,temp3->face_id,&other1,&other2); + /* Find the vertex in the second triangle that is not in the first */ + vertex2 = Different(temp1->face_id,temp2->face_id,temp3->face_id,a,b,c,&other1,&other2); + + id[index] = vertex1; index = !index; + id[index] = other1; index = !index; + id[index] = other2; index = !index; + } + + if ((temp1 == NULL ) || (temp2 == NULL) || (temp3 == NULL)) + { + printf("There is an error in the triangle list \n"); + exit(0); + } + + if ((id[0] == id[1]) || (id[0] == vertex2)) + continue; + + if ( (member(id[index],temp1->face_id,temp2->face_id,temp3->face_id)) ) + { + if ((text) && ( vt[id[index]]==0)) { + cptexture = FALSE; + } + if ((!norm) && (!cptexture)) { + fprintf(output,"%d ",id[index]+1); + } else if ((norm) && (!cptexture)) { + fprintf(output,"%d//%d ",id[index]+1,vn[id[index]]+1); + } else if ((!norm) && (cptexture)) { + fprintf(output,"%d/%d ",id[index]+1,vt[id[index]]+1); + } else { + fprintf(output,"%d/%d/%d ",id[index]+1,vt[id[index]]+1,vn[id[index]]+1); + } + + index = !index; + *swap = *swap + 1; + } + + if ((text) && ( vt[vertex2]==0)) + cptexture = FALSE; + if ((!norm) && (!cptexture)) + fprintf(output,"\nq %d ",vertex2+1); + else if ((norm) && (!cptexture)) + fprintf(output,"\nq %d//%d ",vertex2+1,vn[vertex2]+1); + else if ((!norm) && (cptexture)) + fprintf(output,"\nq %d/%d ",vertex2+1,vt[vertex2]+1); + else + fprintf(output,"\nq %d/%d/%d ",vertex2+1,vt[vertex2]+1,vn[vertex2]+1); + + id[index] = vertex2; index = !index; + + /* Get the next vertex not in common */ + vertex2 = Different(temp1->face_id,temp2->face_id,temp3->face_id,a,b,c,&other1,&other2); + a = temp1->face_id; + b = temp2->face_id; + c = temp3->face_id; + } + + /* Do the last vertex */ + if ((!norm) && (!cptexture)) + fprintf(output,"\nq %d ",vertex2+1); + else if ((norm) && (!cptexture)) + fprintf(output,"\nq %d//%d ",vertex2+1,vn[vertex2]+1); + else if ((!norm) && (cptexture)) + fprintf(output,"\nq %d/%d ",vertex2+1,vt[vertex2]+1); + else + fprintf(output,"\nq %d/%d/%d ",vertex2+1,vt[vertex2]+1,vn[vertex2]+1); + + Free_Strips(); + return (num/3); +} + + + + + +void Output_Tri(int id1, int id2, int id3,FILE *bands, int color1, int color2, int color3,BOOL end) +{ + /* We will save everything into a list, rather than output at once, + as was done in the old routine. This way for future modifications + we can change the strips later on if we want to. + */ + + int temp1,temp2,temp3; + + /* Make sure we do not have an error */ + /* There are degeneracies in some of the files */ + if ( (id1 == id2) || (id1 == id3) || (id2 == id3)) + { + printf("Degenerate triangle %d %d %d\n",id1,id2,id3); + exit(0); + } + else + { + Last_Edge(&temp1,&temp2,&temp3,0); + Add_Id_Strips(id1,end); + Add_Id_Strips(id2,end); + Add_Id_Strips(id3,end); + Last_Edge(&id1,&id2,&id3,1); + } +} + + +int Polygon_Output(P_ADJACENCIES temp,int face_id,int bucket, + ListHead *pListHead, BOOL first, int *swaps, + FILE *bands,int color1,int color2,int color3,BOOL global, BOOL end) +{ + ListHead *pListFace; + PF_FACES face; + P_ADJACENCIES pfNode; + int next_face_id,next_bucket,e1,e2,e3,other1,other2,other3; + P_ADJACENCIES lpListInfo; + int ties=0; + + /* We have a polygon to output, the id is face id, and the number + of adjacent polygons to it is bucket. This routine extends the patches from + either end to make longer triangle strips. + */ + + + /* Now get the edge */ + Last_Edge(&e1,&e2,&e3,0); + + /* Get the polygon with id face_id */ + pListFace = PolFaces[face_id]; + face = (PF_FACES) PeekList(pListFace,LISTHEAD,0); + + /* We can't go any more */ + if ((face->nPolSize == 1) || ((face->nPolSize == 4) && (global))) /* if global, then we are still doing patches */ + { + /* Remove it from the list so we do not have to waste + time visiting it in the future, or winding up in an infinite loop + if it is the first on that we are looking at for a possible strip + */ + if (face->nPolSize == 1) + RemoveList(pListHead,(PLISTINFO) temp); + if (first) + return 0; + else + return (Finished(swaps,bands,global)); + } + + if (face->nPolSize == 3) + { + /* It is already a triangle */ + if (bucket == 0) + { + /* It is not adjacent to anything so we do not have to + worry about the order of the sides or updating adjacencies + */ + + next_face_id = Different(*(face->pPolygon),*(face->pPolygon+1),*(face->pPolygon+2), + e1,e2,e3,&other1,&other2); + face->nPolSize = 1; + + /* If this is the first triangle in the strip */ + if ((e2 == 0) && (e3 ==0)) + { + e2 = other1; + e3 = other2; + } + + Output_Tri(e2,e3,next_face_id,bands,color1,color2,color3,end); + RemoveList(pListHead,(PLISTINFO) temp); + return (Finished(swaps,bands,global)); + } + + + /* It is a triangle with adjacencies. This means that we + have to: + 1. Update the adjacencies in the list, because we are + using this polygon and it will be deleted. + 2. Get the next polygon. + */ + else + { + /* Return the face_id of the next polygon we will be using, + while updating the adjacency list by decrementing the + adjacencies of everything adjacent to the current triangle. + */ + + next_face_id = Update_Adjacencies(face_id, &next_bucket, &e1,&e2,&ties); + /* Maybe we deleted something in a patch and could not find an adj polygon */ + if (next_face_id == -1) + { + Output_Tri(*(face->pPolygon),*(face->pPolygon+1),*(face->pPolygon+2),bands,color1, + color2,color3,end); + face->nPolSize = 1; + RemoveList(pListHead,(PLISTINFO) temp); + return (Finished(swaps,bands,global)); + } + + /* Find the other vertex to transmit in the triangle */ + e3 = Return_Other(face->pPolygon,e1,e2); + Last_Edge(&other1,&other2,&other3,0); + + if ((other2 != 0) && (other3 != 0)) + { + /* See which vertex in the output edge is not in the input edge */ + if ((e1 != other2) && (e1 != other3)) + e3 = e1; + else if ((e2 != other2) && (e2 != other3)) + e3 = e2; + else + { + printf("There is an error in the tri with adj\n"); + exit(0); + } + + /* See which vertex of the input edge is not in the output edge */ + if ((other2 != e1) && (other2 != e2)) + { + other1 = other2; + other2 = other3; + } + else if ((other3 != e1) && (other3 != e2)) + other1 = other3; + else + { + printf("There is an error in getting the tri with adj\n"); + exit(0); + } + + } + else + { + /* We are the first triangle in the strip and the starting edge + has not been set yet + */ + /* Maybe we deleted something in a patch and could not find an adj polygon */ + if (next_face_id == -1) + { + Output_Tri(*(face->pPolygon),*(face->pPolygon+1),*(face->pPolygon+2),bands,color1, + color2,color3,end); + face->nPolSize = 1; + RemoveList(pListHead,(PLISTINFO) temp); + return (Finished(swaps,bands,global)); + } + + other1 = e3; + e3 = e2; + other2 = e1; + } + + /* At this point the adjacencies have been updated and we + have the next polygon id + */ + + Output_Tri(other1,other2,e3,bands,color1,color2,color3,end); + face->nPolSize = 1; + RemoveList(pListHead,(PLISTINFO) temp); + + /* Maybe we deleted something in a patch and could not find an adj polygon */ + if (next_face_id == -1) + return (Finished(swaps,bands,global)); + + if (Done(next_face_id,59,&next_bucket) == NULL) + { + printf("We deleted the next face 4%d\n",next_face_id); + exit(0); + } + + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = next_face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon3 %d\n",next_face_id); + exit(0); + } + return (Polygon_Output(lpListInfo,next_face_id,next_bucket, + pListHead, FALSE, swaps,bands,color1,color2,color3,global,end)); + + } + } + + else + { + /* It is not a triangle, we have to triangulate it . + Since it is not adjacent to anything we can triangulate it + blindly + */ + if (bucket == 0) + { + /* It is the first polygon in the strip, therefore there is no + input edge to start with. + */ + if ((e2 == 0) && (e3 ==0)) + Blind_Triangulate(face->nPolSize,face->pPolygon,bands, + TRUE,1,color1,color2,color3); + + else + Blind_Triangulate(face->nPolSize,face->pPolygon,bands, + FALSE,1,color1,color2,color3); + + RemoveList(pListHead,(PLISTINFO) temp); + + /* We will be at the beginning of the next strip. */ + face->nPolSize = 1; + return (Finished(swaps,bands,global)); + } + + + else + { + + + /* WHOLE triangulation */ + /* It is not a triangle and has adjacencies. + This means that we have to: + 1. Triangulate this polygon, not blindly because + we have an edge that we want to come out on, that + is the edge that is adjacent to a polygon with the + least number of adjacencies. Also we must come in + on the last seen edge. + 2. Update the adjacencies in the list, because we are + using this polygon . + 3. Get the next polygon. + */ + /* Return the face_id of the next polygon we will be using, + while updating the adjacency list by decrementing the + adjacencies of everything adjacent to the current polygon. + */ + + next_face_id = Update_Adjacencies(face_id, &next_bucket, &e1,&e2,&ties); + + /* Maybe we deleted something in a patch and could not find an adj polygon */ + if (next_face_id == -1) + { + + /* If we are at the first polygon in the strip and there is no input + edge, then begin is TRUE + */ + if ((e2 == 0) && (e3 == 0)) + Blind_Triangulate(face->nPolSize,face->pPolygon, + bands,TRUE,1,color1,color2,color3); + + else + Blind_Triangulate(face->nPolSize,face->pPolygon, + bands,FALSE,1,color1,color2,color3); + + RemoveList(pListHead,(PLISTINFO) temp); + + /* We will be at the beginning of the next strip. */ + face->nPolSize = 1; + return (Finished(swaps,bands,global)); + } + + if (Done(next_face_id,59,&next_bucket) == NULL) + { + printf("We deleted the next face 6 %d %d\n",next_face_id,face_id); + exit(0); + } + + Non_Blind_Triangulate(face->nPolSize,face->pPolygon, + bands,next_face_id,face_id,1,color1,color2,color3); + + RemoveList(pListHead,(PLISTINFO) temp); + face->nPolSize = 1; + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = next_face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon2 %d %d\n",next_face_id,next_bucket); + exit(0); + } + return (Polygon_Output(lpListInfo,next_face_id,next_bucket, + pListHead, FALSE, swaps,bands,color1,color2,color3,global,end)); + } + + } + Last_Edge(&e1,&e2,&e3,0); + +} + + +int Extend_Face(int face_id,int e1,int e2,int *swaps,FILE *bands, + int color1,int color2,int color3,int *vert_norm, int normals, + int *vert_texture, int texture) +{ + int dummy=0,next_bucket; + P_ADJACENCIES pfNode,lpListInfo; + ListHead *pListHead; + + /* Try to extend backwards off of the local strip that we just found */ + + vn = vert_norm; + vt = vert_texture; + norm = normals; + text = texture; + + *swaps = 0; + /* Find the face that is adjacent to the edge and is not the + current face. + */ + face_id = Find_Face(face_id, e1, e2,&next_bucket); + if (face_id == -1) + return 0; + + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon3 %d\n",face_id); + exit(0); + } + Last_Edge(&dummy,&e1,&e2,1); + + /* Find a strip extending from the patch and return the cost */ + return (Polygon_Output(lpListInfo,face_id,next_bucket,pListHead,TRUE,swaps,bands,color1,color2,color3,TRUE,TRUE)); +} + + diff --git a/Tools/Stripe_w/output.h b/Tools/Stripe_w/output.h new file mode 100644 index 000000000..fd4b34c27 --- /dev/null +++ b/Tools/Stripe_w/output.h @@ -0,0 +1,34 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: output.h +-----------------------------------------------------------------------*/ + + +#include "polverts.h" + +#define TRIANGLE 3 +#define MAGNITUDE 1000000 + +void Output_Tri(int id1, int id2, int id3,FILE *bands, int color1, + int color2, int color3,BOOL end); +void Sgi_Test(); +int Polygon_Output(P_ADJACENCIES temp,int face_id,int bucket, + ListHead *pListHead, BOOL first, int *swaps, + FILE *bands,int color1,int color2,int color3, + BOOL global, BOOL end); +void Last_Edge(); +void Extend_Backwards(); +int Finished(int *swap, FILE *output, BOOL global); +int Extend_Face(int face_id,int e1,int e2,int *swaps,FILE *bands, + int color1,int color2,int color3,int *vert_norm, int normals, + int *vert_texture, int texture); +void Fast_Reset(); + + diff --git a/Tools/Stripe_w/outputex.c b/Tools/Stripe_w/outputex.c new file mode 100644 index 000000000..10bfb741f --- /dev/null +++ b/Tools/Stripe_w/outputex.c @@ -0,0 +1,514 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: outputex.c + This file contains routines that are used for various functions in + the local algorithm. +*/ +/*---------------------------------------------------------------------*/ + + +#include <stdio.h> +#include <stdlib.h> +#include "global.h" +#include "outputex.h" +#include "triangulatex.h" +#include "polverts.h" +#include "ties.h" +#include "partial.h" +#include "sturctsex.h" +#include "options.h" +#include "output.h" +#include "common.h" +#include "util.h" + + +void Output_TriEx(int id1, int id2, int id3, FILE *output, int next_face, int flag, + int where) +{ + /* We will save everything into a list, rather than output at once, + as was done in the old routine. This way for future modifications + we can change the strips later on if we want to. + */ + + int swap,temp1,temp2,temp3; + static int total=0; + static int tri=0; + static int strips = 0; + static int cost = 0; + + if (flag == -20) + { + cost = cost + where+total+tri+strips+strips; + printf("We will need to send %d vertices to the renderer\n",cost); + total = 0; + tri = 0; + strips = 0; + return ; + } + + + if (flag == -10) /* We are finished, now is time to output the triangle list */ + { + fprintf(output,"\nt "); + tri = tri + Finished(&swap,output,FALSE); + total = total + swap; + strips++; + /*printf("There are %d swaps %d tri %d strips\n",total,tri,strips);*/ + } + + else + { + Last_Edge(&temp1,&temp2,&temp3,0); + Add_Id_Strips(id1,where); + Add_Id_Strips(id2,where); + Add_Id_Strips(id3,where); + Last_Edge(&id1,&id2,&id3,1); + } +} + + + + +void Extend_BackwardsEx(int face_id, FILE *output, FILE *strip, int *ties, + int tie, int triangulate, int swaps, int *next_id) +{ + /* We just made a strip, now we are going to see if we can extend + backwards from the starting face, which had 2 or more adjacencies + to start with. + */ + int bucket,next_face,num,x,y,z,c,max,f; + ListHead *pListFace; + PF_FACES face; + P_ADJACENCIES temp; + + /* Get the first triangle that we have saved the the strip data + structure, so we can see if there are any polygons adjacent + to this edge or a neighboring one + */ + First_Edge(&x,&y,&z); + + pListFace = PolFaces[face_id]; + face = (PF_FACES) PeekList(pListFace,LISTHEAD,0); + + num = face->nPolSize; + + /* Go through the edges to see if there is an adjacency + with a vertex in common to the first triangle that was + outputted in the strip. (maybe edge was deleted....) + */ + for (c=0; c<num ; c++) + { + + if ( (c != (num-1)) && + (( (*(face->pPolygon+c) == x) && (*(face->pPolygon+c+1) == y)) || + (*(face->pPolygon+c) == y) && (*(face->pPolygon+c+1) == x))) + { + /* Input edge is still there see if there is an adjacency */ + next_face = Find_Face(face_id, x, y, &bucket); + if (next_face == -1) + /* Could not find a face adjacent to the edge */ + break; + pListFace = array[bucket]; + max = NumOnList(pListFace); + for (f=0;;f++) + { + temp = (P_ADJACENCIES) PeekList(pListFace,LISTHEAD,f); + if (temp->face_id == next_face) + { + Last_Edge(&z,&y,&x,1); + Polygon_OutputEx(temp,temp->face_id,bucket,pListFace, + output,strip,ties,tie,triangulate,swaps,next_id,0); + return; + } + + if (temp == NULL) + { + printf("Error in the new buckets%d %d %d\n",bucket,max,0); + exit(0); + } + } + + } + else if ( (c == (num -1)) && + ( ((*(face->pPolygon) == x) && (*(face->pPolygon+num-1) == y)) || + (*(face->pPolygon) == y) && (*(face->pPolygon+num-1) == x))) + { + next_face = Find_Face(face_id,x,y,&bucket); + if (next_face == -1) + /* Could not find a face adjacent to the edge */ + break; + pListFace = array[bucket]; + max = NumOnList(pListFace); + for (f=0;;f++) + { + temp = (P_ADJACENCIES) PeekList(pListFace,LISTHEAD,f); + if (temp->face_id == next_face) + { + Last_Edge(&z,&y,&x,1); + Polygon_OutputEx(temp,temp->face_id,bucket,pListFace, + output,strip,ties,tie,triangulate,swaps,next_id,0); + return; + } + + if (temp == NULL) + { + printf("Error in the new buckets%d %d %d\n",bucket,max,0); + exit(0); + } + } + } + + } + +} + +void Polygon_OutputEx(P_ADJACENCIES temp,int face_id,int bucket, + ListHead *pListHead, FILE *output, FILE *strips, + int *ties, int tie, int triangulate, int swaps, + int *next_id, int where) +{ + ListHead *pListFace; + PF_FACES face; + P_ADJACENCIES pfNode; + static BOOL begin = TRUE; + int old_face,next_face_id,next_bucket,e1,e2,e3,other1,other2,other3; + P_ADJACENCIES lpListInfo; + + /* We have a polygon to output, the id is face id, and the number + of adjacent polygons to it is bucket. + */ + + Last_Edge(&e1,&e2,&e3,0); + + /* Get the polygon with id face_id */ + pListFace = PolFaces[face_id]; + face = (PF_FACES) PeekList(pListFace,LISTHEAD,0); + + if (face->nPolSize == 3) + { + /* It is already a triangle */ + if (bucket == 0) + { + /* It is not adjacent to anything so we do not have to + worry about the order of the sides or updating adjacencies + */ + + Last_Edge(&e1,&e2,&e3,0); + next_face_id = Different(*(face->pPolygon),*(face->pPolygon+1),*(face->pPolygon+2), + e1,e2,e3,&other1,&other2); + /* No input edge, at the start */ + if ((e2 ==0) && (e3 == 0)) + { + e2 = other1; + e3 = other2; + } + + Output_TriEx(e2,e3,next_face_id,strips,-1,begin,where); + RemoveList(pListHead,(PLISTINFO) temp); + /* We will be at the beginning of the next strip. */ + begin = TRUE; + } + /* It is a triangle with adjacencies. This means that we + have to: + 1. Update the adjacencies in the list, because we are + using this polygon and it will be deleted. + 2. Get the next polygon. + */ + else + { + /* Return the face_id of the next polygon we will be using, + while updating the adjacency list by decrementing the + adjacencies of everything adjacent to the current triangle. + */ + + next_face_id = Update_AdjacenciesEx(face_id, &next_bucket, &e1,&e2,ties); + old_face = next_face_id; + + /* Break the tie, if there was one */ + if (tie != FIRST) + old_face = Get_Next_Face(tie,face_id,triangulate); + + if (next_face_id == -1) + { + Polygon_OutputEx(temp,face_id,0,pListHead,output,strips,ties,tie, + triangulate,swaps,next_id,where); + return; + } + + + /* We are using a different face */ + if ((tie != FIRST) && (old_face != next_face_id) && (swaps == ON)) + { + next_face_id = old_face; + /* Get the new output edge, since e1 and e2 are for the + original next face that we got. + */ + e3 = Get_EdgeEx(&e1,&e2,face->pPolygon,next_face_id,face->nPolSize,0,0); + } + + /* Find the other vertex to transmit in the triangle */ + e3 = Return_Other(face->pPolygon,e1,e2); + Last_Edge(&other1,&other2,&other3,0); + + if ((other1 != 0) && (other2 != 0)) + { + /* See which vertex in the output edge is not in the input edge */ + if ((e1 != other2) && (e1 != other3)) + e3 = e1; + else if ((e2 != other2) && (e2 != other3)) + e3 = e2; + /* can happen with > 2 polys on an edge but won't form a good strip so stop + the strip here + */ + else + { + Polygon_OutputEx(temp,face_id,0,pListHead,output,strips,ties,tie, + triangulate,swaps,next_id,where); + return; + } + + /* See which vertex of the input edge is not in the output edge */ + if ((other2 != e1) && (other2 != e2)) + { + other1 = other2; + other2 = other3; + } + else if ((other3 != e1) && (other3 != e2)) + other1 = other3; + else + { + /* Degenerate triangle just return*/ + Output_TriEx(other1,other2,e3,strips,next_face_id,begin,where); + RemoveList(pListHead,(PLISTINFO) temp); + begin = FALSE; + return; + } + + } + + /* There was not an input edge, we are the first triangle in a strip */ + else + { + /* Find the correct order to transmit the triangle, what is + the output edge that we want ? + */ + other1 = e3; + e3 = e2; + other2 = e1; + } + + /* At this point the adjacencies have been updated and we + have the next polygon id + */ + Output_TriEx(other1,other2,e3,strips,next_face_id,begin,where); + RemoveList(pListHead,(PLISTINFO) temp); + begin = FALSE; + + if (Done(next_face_id,59,&next_bucket) == NULL) + return; + + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = next_face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon3 %d\n",next_face_id); + exit(0); + } + Polygon_OutputEx(lpListInfo,next_face_id,next_bucket, + pListHead, output, strips,ties,tie,triangulate,swaps,next_id,where); + + } +} + + else + { + /* It is not a triangle, we have to triangulate it . + Since it is not adjacent to anything we can triangulate it + blindly + */ + if (bucket == 0) + { + /* Check to see if there is not an input edge */ + Last_Edge(&other1,&other2,&other3,0); + if ((other1 == 0) && (other2 ==0)) + Blind_TriangulateEx(face->nPolSize,face->pPolygon, strips, + output,TRUE,where); + else + Blind_TriangulateEx(face->nPolSize,face->pPolygon,strips, + output,FALSE,where); + + RemoveList(pListHead,(PLISTINFO) temp); + /* We will be at the beginning of the next strip. */ + begin = TRUE; + } + + /* If we have specified PARTIAL triangulation then + we will go to special routines that will break the + polygon and update the data structure. Else everything + below will simply triangulate the whole polygon + */ + else if (triangulate == PARTIAL) + { + + /* Return the face_id of the next polygon we will be using, + */ + next_face_id = Min_Face_AdjEx(face_id,&next_bucket,ties); + + + /* Don't do it partially, because we can go inside and get + less adjacencies, for a quad we can do the whole thing. + */ + if ((face_id == next_face_id) && (face->nPolSize == 4) && (swaps == ON)) + { + next_face_id = Update_AdjacenciesEx(face_id, &next_bucket, &e1,&e2,ties); + if (next_face_id == -1) + { + /* There is no sequential face to go to, end the strip */ + Polygon_OutputEx(temp,face_id,0,pListHead,output,strips,ties,tie, + triangulate,swaps,next_id,where); + return; + } + + /* Break the tie, if there was one */ + if (tie != FIRST) + next_face_id = Get_Next_Face(tie,face_id,triangulate); + Non_Blind_TriangulateEx(face->nPolSize,face->pPolygon, strips, + output,next_face_id,face_id,where); + RemoveList(pListHead,(PLISTINFO) temp); + } + + /* Was not a quad but we still do not want to do it partially for + now, since we want to only do one triangle at a time + */ + else if ((face_id == next_face_id) && (swaps == ON)) + Inside_Polygon(face->nPolSize,face->pPolygon,strips,output, + next_face_id,face_id,next_id,pListHead,temp,where); + + else + { + if ((tie != FIRST) && (swaps == ON)) + next_face_id = Get_Next_Face(tie,face_id,triangulate); + Partial_Triangulate(face->nPolSize,face->pPolygon,strips, + output,next_face_id,face_id,next_id,pListHead,temp,where); + /* Check the next bucket again ,maybe it changed + We calculated one less, but that might not be the case + */ + } + + if (Done(next_face_id,59,&next_bucket) == NULL) + { + /* Check to see if there is not an input edge */ + Last_Edge(&other1,&other2,&other3,0); + if ((other1 == 0) && (other2 ==0)) + Blind_TriangulateEx(face->nPolSize,face->pPolygon, strips, + output,TRUE,where); + else + Blind_TriangulateEx(face->nPolSize,face->pPolygon,strips, + output,FALSE,where); + + if (Done(face_id,59,&bucket) != NULL) + { + pListHead = array[bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + RemoveList(pListHead,(PLISTINFO)lpListInfo); + } + begin = TRUE; + return; + } + + begin = FALSE; + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = next_face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon1 %d %d\n",next_face_id,next_bucket); + exit(0); + } + Polygon_OutputEx(lpListInfo,next_face_id,next_bucket, + pListHead, output, strips,ties,tie,triangulate,swaps,next_id,where); + } + + + else + { + /* WHOLE triangulation */ + /* It is not a triangle and has adjacencies. + This means that we have to: + 1. TriangulateEx this polygon, not blindly because + we have an edge that we want to come out on, that + is the edge that is adjacent to a polygon with the + least number of adjacencies. Also we must come in + on the last seen edge. + 2. Update the adjacencies in the list, because we are + using this polygon . + 3. Get the next polygon. + */ + /* Return the face_id of the next polygon we will be using, + while updating the adjacency list by decrementing the + adjacencies of everything adjacent to the current polygon. + */ + + next_face_id = Update_AdjacenciesEx(face_id, &next_bucket, &e1,&e2,ties); + + if (Done(next_face_id,59,&next_bucket) == NULL) + { + Polygon_OutputEx(temp,face_id,0,pListHead,output,strips,ties,tie, + triangulate,swaps,next_id,where); + /* Because maybe there was more than 2 polygons on the edge */ + return; + } + + /* Break the tie, if there was one */ + else if (tie != FIRST) + next_face_id = Get_Next_Face(tie,face_id,triangulate); + + Non_Blind_TriangulateEx(face->nPolSize,face->pPolygon, strips, + output,next_face_id,face_id,where); + RemoveList(pListHead,(PLISTINFO) temp); + begin = FALSE; + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = next_face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon2 %d %d\n",next_face_id,next_bucket); + exit(0); + } + Polygon_OutputEx(lpListInfo,next_face_id,next_bucket, + pListHead, output, strips,ties,tie,triangulate,swaps,next_id,where); + } + + } + Last_Edge(&e1,&e2,&e3,0); + +} + + + + + + + + diff --git a/Tools/Stripe_w/outputex.h b/Tools/Stripe_w/outputex.h new file mode 100644 index 000000000..f59f7e75c --- /dev/null +++ b/Tools/Stripe_w/outputex.h @@ -0,0 +1,31 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: outputex.h +-----------------------------------------------------------------------*/ + + +#include "polverts.h" + + +#define TRIANGLE 3 +#define MAGNITUDE 1000000 + +void Output_TriEx(int id1, int id2, int id3, FILE *output, int next_face, + int flag, int where); +void Sgi_Test(); +void Polygon_OutputEx(P_ADJACENCIES temp,int face_id,int bucket, + ListHead *pListHead, FILE *output, FILE *strips, + int *ties, int tie, int triangulate, int swaps, + int *next_id, int where); +void Extend_BackwardsEx(int face_id, FILE *output, FILE *strip, int *ties, + int tie, int triangulate, int swaps,int *next_id); +void FinishedEx(); + + diff --git a/Tools/Stripe_w/partial.c b/Tools/Stripe_w/partial.c new file mode 100644 index 000000000..847b40501 --- /dev/null +++ b/Tools/Stripe_w/partial.c @@ -0,0 +1,668 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: partial.c + This file contains routines that are used partial triangulation of polygons +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "global.h" +#include "outputex.h" +#include "polyvertsex.h" +#include "triangulatex.h" +#include "sturctsex.h" +#include "polverts.h" +#include "common.h" +#include "util.h" + +void P_Triangulate_Quad(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,FILE *fp,int reversed,int face_id, + int *next_id,ListHead *pListHead, + P_ADJACENCIES temp, + int where) +{ + int vertex4,vertex5,dummy=60; + + /* This routine will nonblindly triangulate a quad, meaning + that there is a definite input and a definite output + edge that we must adhere to. Reversed will tell the orientation + of the input edge. (Reversed is -1 is we do not have an input + edge, in other words we are at the beginning of a strip.) + Out_edge* is the output edge, and in_edge* is the input edge. + Index are the edges of the polygon + and size is the size of the polygon. Begin is whether we are + at the start of a new strip. + Note that we will not necessarily triangulate the whole quad; + maybe we will do half and leave the other half (a triangle) + for later. + */ + + + /* If we do not have an input edge, then we can make our input + edge whatever we like, therefore it will be easier to come + out on the output edge. In this case the whole quad is done. + */ + if (reversed == -1) + { + vertex4 = AdjacentEx(out_edge1,out_edge2,index,size); + vertex5 = Get_Other_Vertex(vertex4,out_edge1,out_edge2,index); + Output_TriEx(vertex5,vertex4,out_edge1,output,-1,-1,where); + Output_TriEx(vertex4,out_edge1,out_edge2,output,-1,-1,where); + dummy = Update_AdjacenciesEx(face_id, &dummy, &dummy,&dummy,&dummy); + RemoveList(pListHead,(PLISTINFO) temp); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off, but cannot use the whole quad? + */ + + if (in_edge2 == out_edge1) + { + /* Output the triangle that comes out the correct + edge. Save the other half for later. + */ + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge2,index); + Output_TriEx(in_edge1,in_edge2,out_edge2,output,-1,-1,where); + /* Now we have a triangle used, and a triangle that is + left for later. + */ + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,in_edge2,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + /* Put the new face in the proper bucket of adjacencies + There are 2 edges that need to be checked for the triangle + that was just outputted. For the output edge we definitely + will be decreasing the adjacency, but we must check for the + input edge. + */ + + dummy = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp,FALSE); + dummy = Change_FaceEx(face_id,in_edge2,out_edge2,pListHead,temp,TRUE); + + /* Update the face data structure, by deleting the old + face and putting in the triangle as the new face + */ + New_Face(face_id,in_edge1,out_edge2,vertex4); + return; + } + else if (in_edge1 == out_edge1) + { + /* We want to output the first triangle (whose output + edge is not the one that we want. + We have to find the vertex that we need, which is + the other vertex which we do not have. + */ + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge2,index); + Output_TriEx(in_edge2,in_edge1,out_edge2,output,-1,-1,where); + /* Now we have a triangle used, and a triangle that is + left for later. + */ + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + dummy = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp,FALSE); + dummy = Change_FaceEx(face_id,in_edge1,out_edge2,pListHead,temp,TRUE); + + /* Update the face data structure, by deleting the old + face and putting in the triangle as the new face + */ + New_Face(face_id,in_edge2,out_edge2,vertex4); + return; + } + + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_TriEx(in_edge2,in_edge1,out_edge1,output,-1,-1,where); + /* Now we have a triangle used, and a triangle that is + left for later. + */ + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + dummy = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp,FALSE); + dummy = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp,TRUE); + + /* Update the face data structure, by deleting the old + face and putting in the triangle as the new face + */ + New_Face(face_id,in_edge2,out_edge1,vertex4); + return; + } + else if (in_edge2 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_TriEx(in_edge1,in_edge2,out_edge1,output,-1,-1,where); + /* Now we have a triangle used, and a triangle that is + left for later. + */ + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + dummy = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp,FALSE); + dummy = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp,TRUE); + + /* Update the face data structure, by deleting the old + face and putting in the triangle as the new face + */ + New_Face(face_id,in_edge1,out_edge1,vertex4); + return; + } + + /* The final case is where we want to come out the opposite + edge. + */ + else + { + if( ((!reversed) && (out_edge1 == (AdjacentEx(in_edge1,in_edge2,index,size)))) || + ((reversed) && (out_edge2 == (AdjacentEx(in_edge2,in_edge1,index,size))))) + { + /* We need to know the orientation of the input + edge, so we know which way to put the diagonal. + And also the output edge, so that we triangulate + correctly. Does not need partial. + */ + Output_TriEx(in_edge1,in_edge2,out_edge2,output,-1,-1,where); + Output_TriEx(in_edge2,out_edge2,out_edge1,output,-1,-1,where); + dummy = Update_AdjacenciesEx(face_id, &dummy, &dummy,&dummy,&dummy); + RemoveList(pListHead,(PLISTINFO) temp); + } + else + { + /* Input and output orientation was reversed, so diagonal will + be reversed from above. + */ + Output_TriEx(in_edge1,in_edge2,out_edge1,output,-1,-1,where); + Output_TriEx(in_edge2,out_edge1,out_edge2,output,-1,-1,where); + dummy = Update_AdjacenciesEx(face_id, &dummy, &dummy,&dummy,&dummy); + RemoveList(pListHead,(PLISTINFO) temp); + } + return; + } +} + +void P_Triangulate_Polygon(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size, + int *index,FILE *output,FILE *fp, + int reversed,int face_id,int *next_id, + ListHead *pListHead, P_ADJACENCIES temp2, + int where) +{ + /* We have a polygon greater than 4 sides, which we wish + to partially triangulate + */ + int next_bucket,vertex4,dummy = 60; + int *temp; + P_ADJACENCIES pfNode; + + + /* Since we are calling this recursively, we have to check whether + we are down to the case of the quad. + */ + if (size == 4) + { + P_Triangulate_Quad(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,output,fp,reversed,face_id,next_id, + pListHead,temp2,where); + return; + } + + /* We do not have a specified input edge, and therefore we + can make it anything we like, as long as we still come out + the output edge that we want. + */ + if (reversed == -1) + { + /* Get the vertex for the last triangle, which is + the one coming out the output edge, before we do + any deletions to the list. We will be doing this + bottom up. + */ + vertex4 = AdjacentEx(out_edge1,out_edge2,index,size); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(out_edge2,index,size); + /* We do not have to partially triangulate, since + we will do the whole thing, so use the whole routine + */ + /* Triangulate_PolygonEx(vertex4,out_edge1,in_edge2, + vertex4,size-1,index,output,fp,reversed, + face_id,next_id,pListHead,temp2,where); */ + Triangulate_PolygonEx(vertex4,out_edge1,in_edge2, + vertex4,size-1,index,output,fp,reversed, + face_id,where); + memcpy(index,temp,sizeof(int)*size); + /* Lastly do the triangle that comes out the output + edge. + */ + Output_TriEx(vertex4,out_edge1,out_edge2,output,-1,-1,where); + /* We were able to do the whole polygon, now we + can delete the whole thing from our data structure. + */ + dummy = Update_AdjacenciesEx(face_id, &dummy, &dummy,&dummy,&dummy); + RemoveList(pListHead,(PLISTINFO) temp2); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off that comes out the correct output edge, + but we cannot use the whole polygon? + */ + if (in_edge2 == out_edge1) + { + Output_TriEx(in_edge1,out_edge1,out_edge2,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp2,TRUE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge2,index,size); + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + return; + } + + /* Next case is where it is again consecutive, but the triangle + formed by the consecutive edges do not come out of the + correct output edge. (the input edge will be reversed in + the next triangle) + */ + else if (in_edge1 == out_edge1) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + Output_TriEx(in_edge2,in_edge1,out_edge2,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp2,TRUE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge1,index,size); + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + return; + } + + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + Output_TriEx(in_edge2,in_edge1,out_edge1,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge1,out_edge2,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp2,TRUE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge1,index,size); + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + return; + } + else if (in_edge2 == out_edge2) + { + Output_TriEx(in_edge1,in_edge2,out_edge1,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(out_edge2,out_edge1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,out_edge1,out_edge2,pListHead,temp2,TRUE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge2,index,size); + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + return; + } + + /* Else the edge is not consecutive, and it is sufficiently + far away, for us not to make a conclusion at this time. + So we can take off a triangle and recursively call this + function. + */ + else + { + if (!reversed) + { + vertex4 = AdjacentEx(in_edge2,in_edge1,index,size); + Output_TriEx(in_edge1,in_edge2,vertex4,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(in_edge1,vertex4,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,in_edge1,vertex4,pListHead,temp2,FALSE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge1,index,size); + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + + /* Save the info for the new bucket, we will need it on + the next pass for the variables, pListHead and temp + */ + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + temp2 = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (temp2 == NULL) + { + printf("There is an error finding the next polygon10 %d %d\n",next_bucket,face_id); + exit(0); + } + + P_Triangulate_Polygon(out_edge1,out_edge2,in_edge2, + vertex4,size-1,index,output,fp,!reversed, + face_id,next_id,pListHead,temp2,where); + } + else + { + vertex4 = AdjacentEx(in_edge1,in_edge2,index,size); + Output_TriEx(in_edge2,in_edge1,vertex4,output,-1,-1,where); + + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(in_edge2,vertex4,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + + /* Put the new face in the proper bucket of adjacencies */ + next_bucket = Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + next_bucket = Change_FaceEx(face_id,in_edge2,vertex4,pListHead,temp2,FALSE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge2,index,size); + + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + + /* Save the info for the new bucket, we will need it on + the next pass for the variables, pListHead and temp + */ + pListHead = array[next_bucket]; + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + temp2 = (P_ADJACENCIES) (SearchList(array[next_bucket], pfNode, + (int (*)(void *,void *)) (Compare))); + if (temp2 == NULL) + { + printf("There is an error finding the next polygon11 %d %d\n",face_id,next_bucket); + exit(0); + } + + P_Triangulate_Polygon(out_edge1,out_edge2,vertex4, + in_edge1,size-1,index,output,fp,!reversed, + face_id,next_id,pListHead,temp2,where); + } + return; + } +} + +void P_Triangulate(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *fp,FILE *output,int reversed,int face_id, + int *next_id,ListHead *pListHead, + P_ADJACENCIES temp,int where) +{ + + if (size == 4) + P_Triangulate_Quad(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,fp,output,reversed,face_id,next_id,pListHead, temp,where); + else + P_Triangulate_Polygon(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,fp,output,reversed,face_id,next_id,pListHead,temp,where); +} + + void Partial_Triangulate(int size,int *index, FILE *fp, + FILE *output,int next_face_id,int face_id, + int *next_id,ListHead *pListHead, + P_ADJACENCIES temp, int where) +{ + int id1,id2,id3; + int nedge1,nedge2; + int reversed; + + /* We have a polygon that has to be triangulated and we cannot + do it blindly, ie we will try to come out on the edge that + has the least number of adjacencies, But also we do not + want to triangulate the whole polygon now, so that means + we will output the least number of triangles that we can + and then update the data structures, with the polygon + that is left after we are done. + */ + Last_Edge(&id1,&id2,&id3,0); + + /* Find the edge that is adjacent to the new face , + also return whether the orientation is reversed in the + face of the input edge, which is id2 and id3. + */ + reversed = Get_EdgeEx(&nedge1,&nedge2,index,next_face_id,size,id2,id3); + + /* Input edge and output edge can be the same if there are more than + one polygon on an edge + */ + if ( ((nedge1 == id2) && (nedge2 == id3)) || + ((nedge1 == id3) && (nedge2 == id2)) ) + /* Set output edge arbitrarily but when come out of here the + next face will be on the old output edge (identical one) + */ + nedge2 = Return_Other(index,id2,id3); + + /* Do the triangulation */ + P_Triangulate(nedge1,nedge2,id2,id3,size,index,fp,output,reversed, + face_id,next_id,pListHead,temp,where); +} + + void Input_Edge(int face_id, int *index, int size, int in_edge1, int in_edge2, + FILE *fp, FILE *output,ListHead *pListHead, P_ADJACENCIES temp2, + int where) + { + /* The polygon had an input edge, specified by input1 and input2 */ + + int output1; + int vertex4, vertex5,dummy=60; + + output1 = Get_Output_Edge(face_id,size,index,in_edge1,in_edge2); + vertex5 = AdjacentEx(in_edge2,in_edge1,index,size); + vertex4 = AdjacentEx(in_edge1,in_edge2,index,size); + + if (vertex4 == output1) + { + Output_TriEx(in_edge2,in_edge1,output1,output,-1,-1,where); + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(in_edge2,output1,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + /* Put the new face in the proper bucket of adjacencies */ + Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + Change_FaceEx(face_id,in_edge2,output1,pListHead,temp2,FALSE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge2,index,size); + + } + else if (vertex5 == output1) + { + Output_TriEx(in_edge1,in_edge2,vertex5,output,-1,-1,where); + /* Now delete the adjacencies by one for all the faces + that are adjacent to the triangle that we just outputted. + */ + Delete_AdjEx(in_edge1,in_edge2,&dummy,&dummy,face_id, + &dummy,&dummy,&dummy); + Delete_AdjEx(in_edge1,vertex5,&dummy,&dummy, + face_id,&dummy,&dummy,&dummy); + /* Put the new face in the proper bucket of adjacencies */ + Change_FaceEx(face_id,in_edge1,in_edge2,pListHead,temp2,FALSE); + Change_FaceEx(face_id,in_edge1,vertex5,pListHead,temp2,FALSE); + + /* Create a new edgelist without the triangle that + was just outputted. + */ + Delete_From_ListEx(in_edge1,index,size); + } + + /* Update the face data structure, by deleting the old + face and putting in the polygon minus the triangle + as the new face, here we will be decrementing the size + by one. + */ + New_Size_Face(face_id); + return; + } + + void Inside_Polygon(int size,int *index,FILE *fp,FILE *output, + int next_face_id,int face_id,int *next_id, + ListHead *pListHead,P_ADJACENCIES temp, int where) + { + /* We know that we have a polygon that is greater than 4 sides, and + that it is better for us to go inside the polygon for the next + one, since inside will have less adjacencies than going outside. + So, we are not doing partial for a part of the polygon. + */ + int id1,id2,id3; + int new1,new2; + + Last_Edge(&id1,&id2,&id3,0); + + /* See if the input edge existed in the polygon, that will help us */ + if (Exist(face_id,id2,id3)) + Input_Edge(face_id,index,size,id2,id3,output,fp,pListHead,temp,where); + else + { + /* Make one of the input edges + We will choose it by trying to get an edge that has something + in common with the last triangle, or by getting the edge that + is adjacent to the least number of thigs, with preference given + to the first option + */ + + Get_Input_Edge(index,id1,id2,id3,&new1,&new2,size,face_id); + Input_Edge(face_id,index,size,new1,new2,output,fp,pListHead,temp,where); + } + } + + diff --git a/Tools/Stripe_w/partial.h b/Tools/Stripe_w/partial.h new file mode 100644 index 000000000..6a4e3a5b1 --- /dev/null +++ b/Tools/Stripe_w/partial.h @@ -0,0 +1,20 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: partial.h +-----------------------------------------------------------------------*/ + +void Partial_Triangulate(int size,int *index, FILE *fp, + FILE *output,int next_face_id,int face_id, + int *next_id,ListHead *pListHead, + P_ADJACENCIES temp, int where); +void Inside_Polygon(int size,int *index,FILE *fp,FILE *output, + int next_face_id,int face_id,int *next_id, + ListHead *pListHead,P_ADJACENCIES temp, int where); + diff --git a/Tools/Stripe_w/polverts.h b/Tools/Stripe_w/polverts.h new file mode 100644 index 000000000..b3979eb4b --- /dev/null +++ b/Tools/Stripe_w/polverts.h @@ -0,0 +1,108 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: polverts.h +-----------------------------------------------------------------------*/ + + +#ifndef _POLVERTS_H +#define _POLVERTS_H + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "queue.h" + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#else +# include <malloc.h> +#endif + + +typedef struct adjacencies +{ + Node ListNode; + int face_id; +} ADJACENCIES,*P_ADJACENCIES; + +typedef struct FVerts +{ + Node ListNode; + int *pPolygon; + int nPolSize; + int nId; +} F_VERTS, *PF_VERTS; + +/*Every time we need to use this, cast it ( ListInfo*)*/ + +typedef struct FEdges +{ + Node ListNode; + int edge[3]; +}F_EDGES,*PF_EDGES; + +typedef struct FFaces +{ + Node ListNode; + int *pPolygon; + int *pNorms; + int seen; + int seen2; + int seen3; + int nPolSize; + F_EDGES **VertandId; + int *marked; + int *walked; +} F_FACES,*PF_FACES; + + +typedef struct Strips +{ + Node ListNode; + int face_id; +} Strips,*P_STRIPS; + + + struct vert_added + { + int num; + int *normal; + }; + + +/* external functions */ +void Find_Adjacencies(int num_faces); +void Test_Adj_Struct(); +void Test_SGI_Struct(); +void Write_Edges(); +void Build_SGI_Table(int num_verts,int num_faces); +void Save_Walks(int numfaces); +void Find_Bands(int numfaces, FILE *output_file, int *swaps, int *bands, + int *cost, int *tri, int norms, int *vert_norms, int texture, + int *vert_texture); +void Save_Rest(int *numfaces); +void Assign_Walk(int lastvert, PF_FACES temp2, int front_walk,int y, + int back_walk); +void Save_Walks(int numfaces); + + +/* Globals */ +extern ListHead **PolVerts; +extern ListHead **PolFaces; +extern ListHead **PolEdges; +extern ListHead *array[60]; +extern int id_array[60]; +extern ListHead *strips[1]; +extern ListHead *all_strips[100000]; /* Assume max 100000 strips */ + + +#endif _POLVERTS_H diff --git a/Tools/Stripe_w/polyvertsex.h b/Tools/Stripe_w/polyvertsex.h new file mode 100644 index 000000000..4c541f7bc --- /dev/null +++ b/Tools/Stripe_w/polyvertsex.h @@ -0,0 +1,42 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: polvertsex.h +-----------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#else +# include <malloc.h> +#endif + +#include "queue.h" + +/* external functions */ +void Start_Vert_Struct(); +void Start_Face_StructEx(); +void Start_Edge_StructEx(); +void AddNewNode(); +void AddNewFaceEx(); +void Find_AdjacenciesEx(); +void Test_Adj_Struct(); +void Test_SGI_Struct(); +void Write_Edges(); +void End_Verts_Struct(); +void End_Face_StructEx(); +void End_Edge_StructEx(); +void Build_SGI_TableEx(); +void Add_AdjEdgeEx(); + + + diff --git a/Tools/Stripe_w/queue.c b/Tools/Stripe_w/queue.c new file mode 100644 index 000000000..966f20379 --- /dev/null +++ b/Tools/Stripe_w/queue.c @@ -0,0 +1,226 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: queue.c + This file contains the routines used in the data structures lists, which + are queues. +*/ +/*---------------------------------------------------------------------*/ + + #include "queue.h" + + + +/*---------------------------------------------------------------------------- + * InitList: + */ +BOOL InitList (PLISTHEAD LHead) + +{ + if (LHead == NULL) return(FALSE); + + LHead->LHeaders[LISTHEAD] = LHead->LHeaders[LISTTAIL] = NULL; + LHead->NumList = 0; + return(TRUE); +} + +/*---------------------------------------------------------------------------- + * AddHead: + */ +BOOL AddHead(PLISTHEAD LHead, PLISTINFO LInfo) +{ + if (LHead == NULL || LInfo == NULL) + return(FALSE); + if (EMPTYLIST(LHead)) + LHead->LHeaders[LISTTAIL] = LInfo; + else LHead->LHeaders[LISTHEAD]->ListNode.Previous = (void *) LInfo; + + LInfo->ListNode.Next = (void *) LHead->LHeaders[LISTHEAD]; + LHead->LHeaders[LISTHEAD] = LInfo; + LInfo->ListNode.Previous = NULL; + LHead->NumList++; + return(TRUE); +} + +/*---------------------------------------------------------------------------- + * AddTail + */ +BOOL AddTail(PLISTHEAD LHead, PLISTINFO LInfo) +{ + if (LHead == NULL || LInfo == NULL) + return(FALSE); + if (EMPTYLIST(LHead)) + LHead->LHeaders[LISTHEAD] = LInfo; + else LHead->LHeaders[LISTTAIL]->ListNode.Next = (void *) LInfo; + + LInfo->ListNode.Previous = (void *) LHead->LHeaders[LISTTAIL]; + LHead->LHeaders[LISTTAIL] = LInfo; + LInfo->ListNode.Next = NULL; + LHead->NumList++; + return(TRUE); +} + + +BOOL InsertNode( PLISTHEAD LHead, int nPos, PLISTINFO LInfo ) +{ +PLISTINFO LAddNode; + + if ( LHead == NULL || LInfo == NULL || nPos > NumOnList( LHead ) ) + return( FALSE ); + + if ( nPos == 0 ) + AddHead( LHead, LInfo ); + else if ( nPos == NumOnList( LHead ) ) + AddTail( LHead, LInfo ); + else + { + if ( (LAddNode = PeekList( LHead, LISTHEAD, nPos - 1 )) == NULL ) + return( FALSE ); + + ((PLISTINFO)LAddNode->ListNode.Next)->ListNode.Previous = LInfo; + LInfo->ListNode.Next = LAddNode->ListNode.Next; + LInfo->ListNode.Previous = LAddNode; + LAddNode->ListNode.Next = LInfo; + + LHead->NumList++; + } + + return( TRUE ); +} + + + + +/*---------------------------------------------------------------------------- + * RemHead: + */ +PLISTINFO RemHead(PLISTHEAD LHead) +{ + PLISTINFO t, t1; + + if ( LHead == NULL || EMPTYLIST(LHead) ) + return(NULL); + + t = LHead->LHeaders[LISTHEAD]; + LHead->LHeaders[LISTHEAD] = (PLISTINFO) t->ListNode.Next; + + if (LHead->LHeaders[LISTHEAD] != NULL) + { + t1 = (PLISTINFO) t->ListNode.Next; + t1->ListNode.Previous = NULL; + } + else + LHead->LHeaders[LISTTAIL] = NULL; + + LHead->NumList--; + + return(t); +} + +/*---------------------------------------------------------------------------- + * RemTail: + */ +PLISTINFO RemTail(PLISTHEAD LHead) +{ + PLISTINFO t, t1; + + if ( LHead == NULL || EMPTYLIST(LHead) ) + return(NULL); + + t = LHead->LHeaders[LISTTAIL]; + LHead->LHeaders[LISTTAIL] = (PLISTINFO) t->ListNode.Previous; + if (LHead->LHeaders[LISTTAIL] != NULL) + { + t1 = (PLISTINFO) t->ListNode.Previous; + t1->ListNode.Next = NULL; + } + else + LHead->LHeaders[LISTHEAD] = NULL; + + LHead->NumList--; + return(t); +} + +/*---------------------------------------------------------------------------- + * PeekList: + */ +PLISTINFO PeekList(PLISTHEAD LHead, int wch, int index ) +{ + PLISTINFO t; + + if (LHead == NULL) + return(NULL); + if ( (t = LHead->LHeaders[wch]) == NULL ) + return(NULL); + + for (; t != NULL && index > 0; index-- ) + t = (wch == LISTHEAD) ? (PLISTINFO) t->ListNode.Next : + (PLISTINFO) t->ListNode.Previous; + return(t); +} + + +/*---------------------------------------------------------------------------- + * RemoveList: + */ +PLISTINFO RemoveList( PLISTHEAD LHead, PLISTINFO LInfo ) +{ + PLISTINFO t, t1; + + t = LInfo; + if (LHead == NULL) + return(NULL); + if (LHead->LHeaders[LISTHEAD] == t) + t = (PLISTINFO) RemHead(LHead); + else if (LHead->LHeaders[LISTTAIL] == t) + t = (PLISTINFO) RemTail(LHead); + else + { + t1 = (PLISTINFO) t->ListNode.Previous; + t1->ListNode.Next = t->ListNode.Next; + t1 = (PLISTINFO) t->ListNode.Next; + t1->ListNode.Previous = t->ListNode.Previous; + LHead->NumList--; + } + + return(t); +} + +/*---------------------------------------------------------------------------- + * SearchList: + * Try to find a specific node in the queue whose key matches with + * searching key. Return the pointer to that node if found, return NULL + * otherwise + * + * Input: + * lpHashTbl => a far pointer to the hash table + * lpKey => a far poniter to searching key + * CompareCallBack => comparision function + * + * Output: a far pointer to the node to be found + * + */ +PLISTINFO SearchList( + PLISTHEAD lpListHead, + PVOID lpSKey, + int (* CompareCallBack) ( PVOID, PVOID ) ) +{ +PLISTINFO lpListInfo; + + lpListInfo = PeekList( lpListHead, LISTHEAD, 0); + while ( lpListInfo != NULL ) + { + if ( CompareCallBack( lpListInfo, lpSKey ) ) + break; + lpListInfo = GetNextNode( lpListInfo ); + } + + return( lpListInfo ); +} + diff --git a/Tools/Stripe_w/queue.h b/Tools/Stripe_w/queue.h new file mode 100644 index 000000000..0bf926e0f --- /dev/null +++ b/Tools/Stripe_w/queue.h @@ -0,0 +1,283 @@ + +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE:queue.h +-----------------------------------------------------------------------*/ + +#ifndef QUEUE_INCLUDED +#define QUEUE_INCLUDED + +/* %%s Node */ +/***************************************************************** + This structure is used to store the List linkage information of a +ListInfo structure. It contains all the necessary information for the +List functions to function properly. This structure must be the first +one defined in any block of memory to be linked with the List functions. +for an example of the used of The Node structure look in the files +ipd2dms.c and ipd2man.h +******************************************************************/ +#include <stdio.h> +#define FALSE 0 +#define TRUE 1 +typedef struct +{ + void *Next; + void *Previous; +} + Node, * PNODE; + +/***************************************************************** + Next : is a pointer to the next structure in this List. + Previous : is a pointer to the previous structure in this List. + priority : this is the priority of this structure in the List. The + highest priority is 0. This field is only used by the + functions EnQue and DeQue. +******************************************************************/ +/* %%e */ + + +/* %%s ListInfo */ + +/***************************************************************** + This is the general means of linking application defined information into +Lists and queues. All structures must begin with the Node Structure. All +other data in the structure is user definable. +******************************************************************/ + +typedef struct List +{ + Node ListNode; /* link to the next Listinfo Structure */ + /* user definable data */ +} ListInfo, *PLISTINFO; + +/***************************************************************** + ListNode : this is the required node structure for the List + mainpulation functions. This must be the first + element of a user definable structure. + + In order for an application to use the List routines, it must define +a structure with all the needed information. The first element in the +user definable structure must be a Node structure. The Node structure +contains all the necessary information for the List routines to do their +magic. For an example of a user defined List structure see the file +ipd2i.h. The User definable structure can be passed to any List function +that excepts a pointer to a ListInfo structure. + +example: + +typedef mstruct +{ + Node ListNode; + int a,b,c,d,e,f,g; +} + mystruct; + + the user definable portion of the above structure is represented by +the integers a,b,c,d,e,f,g. When passing this structure to a List +function a cast of (ListInfo *) must be made to satisify the "C" complier. +******************************************************************/ +/* %%e */ + + +/* %%s ListHead */ +/***************************************************************** + ListHead is used as a header to a List. LHeaders[0] points to the +head of the List. LHeaders[1] points the tail of the list. When +accessing these variables use the defines LISTHEAD, LISTTAIL. +******************************************************************/ + +typedef struct LHead +{ + PLISTINFO LHeaders[2]; + int NumList; +} +ListHead, *PLISTHEAD; + +/***************************************************************** + LHeaders : this is an array of two pointers to ListInfo structures. + This information is used to point to the head and tail of + a list. + NumList : this integer hold the number of structures linked into this + list. + +ListHead #define: + + LISTHEAD : when Peeking down a list this specifies you should + start at the Head of the list and search downward. + + LISTTAIL : when Peeking down a list this specifies you should + start at the tail of the list and search foward. + ******************************************************************/ + +#define LISTHEAD 0 + +#define LISTTAIL 1 +/* %%e */ + +typedef int BOOL; +typedef void * PVOID; + +#define PEEKFROMHEAD( lh, ind ) ( PeekList( (lh), LISTHEAD, (ind) ) ) +#define PEEKFROMTAIL( lh, ind ) ( PeekList( (lh), LISTTAIL, (ind) ) ) +#define EMPTYLIST( lh ) ( ( (lh)->LHeaders[LISTHEAD] == NULL ) ) + +/* General utility routines */ +/* %%s QueRoutines */ +BOOL InitList ( PLISTHEAD ); + +/***************************************************************** + InitList : Initialize a new list structure for use with the List + routines + + INPUTS : LHead : a pointer to a ListHead structure. + OUTPUT : a boolean value TRUE if no errors occured FALSE + otherwise +******************************************************************/ + + +PLISTINFO PeekList ( PLISTHEAD, int, int ); + +/***************************************************************** + PeekList : This funciton peeks down a list for the N'th element + from the HEAD or TAIL of the list + + INPUTS : LHead : a pointer to a List head structure. + from : can either search from the HEAD or TAIL + of the list + where : how many nodes from the begining should the + List routines look. + OUTPUT : a pointer to a ListInfo structure identified by + from/where or NULL if an error occurred. +******************************************************************/ + + +PLISTINFO RemoveList( PLISTHEAD LHead, PLISTINFO LInfo ); + + +/***************************************************************** + RemoveList: Remove a ListInfo structure from a List. + + INPUTS : LHead : a pointer to a ListHead structure. + LInfo : a pointer to the ListInfo structure to remove + from the list. + OUTPUT : a pointer to the ListInfo structure that was removed or + NULL if an error occurred. +******************************************************************/ + +BOOL InsertNode( PLISTHEAD LHead, int nPos, PLISTINFO LInfo ); + +/***************************************************************** + InsertNode: add a node to a list after a given node + + INPUTS : LHead : a pointer to a ListHead structure. + nPos : the position to insert the node into + LInfo : a pointer to the new node to add to the list. + OUTPUT: a boolean value TRUE if all goes well false otherwise +*****************************************************************/ + +BOOL AddHead ( PLISTHEAD, PLISTINFO ); + +/***************************************************************** + AddHead : add a ListInfo structure to the HEAD of a list. + + INPUTS : LHead : a pointer to a ListHead structure of the list + to add to. + LInfo : a pointer to the ListInfo structure to add to + the list. + OUTPUT : A boolean value TRUE if no errors occurred FALSE + otherwise. +******************************************************************/ + + +BOOL AddTail ( PLISTHEAD, PLISTINFO ); + +/***************************************************************** + AddTail : Add a ListInfo structure to the TAIL of a list. + + INPUTS : LHead : a pointer to a ListHead structure of the List + to add to. + LInfo : a pointer to the ListInfo structure to add to + the List. + OUTPUT : a boolean value TRUE if no errors occurred FALSE + otherwise. +******************************************************************/ + + +PLISTINFO RemTail ( PLISTHEAD ); + +/***************************************************************** + RemTail : Remove a ListInfo structure from the TAIL of a List. + + INPUTS : LHead : a pointer to a ListHead structure of the List + to remove from. + OUTPUT : a pointer to the ListInfo structure that was removed + or NULL if an error occurred. +******************************************************************/ + + +PLISTINFO RemHead ( PLISTHEAD ); + +/***************************************************************** + RemHead : Remove a ListInfo structure from the Head of a List. + + INPUTS : LHead : a pointer to a ListHead structure of the List + to remove from. + OUTPUT : a pointer to the ListInfo structure that was removed or + NULL if an error occurred. +******************************************************************/ + +PLISTINFO SearchList( + PLISTHEAD lpListHead, + PVOID lpSKey, + int ( * CompareCallBack) ( PVOID, PVOID ) ); + +/***************************************************************** + SearchList: + Try to find a specific node in the queue whose key matches with + searching key. Return the pointer to that node if found, return NULL + otherwise + + Input: + lpHashTbl => a far pointer to the hash table + lpKey => a far poniter to searching key + CompareCallBack => comparision function + + Output: a far pointer to the node to be found + + ******************************************************************/ + +#define NumOnList(lh) ( ((lh)->NumList) ) + +/***************************************************************** + NumOnList: Returns the number of Nodes linked to a ListHead + structure. This number is maintained by the List + routines. +******************************************************************/ + +#define GetNextNode(pli) ( ((pli)->ListNode.Next) ) + +/******************************************************** + GetNextNode: This macro returns the Next Structure in this list. + This macro will return NULL if no more structures are + in the List. +*********************************************************/ + +#define GetPrevNode(pli) ( ((pli)->ListNode.Previous) ) + +/******************************************************** + GetPrevNode: This macro returns the Previous Structure in this list. + This macro will reutrn NULL if no more structures are + in the List. +********************************************************/ +/* %%e */ + +#endif + + diff --git a/Tools/Stripe_w/sgi_triang.c b/Tools/Stripe_w/sgi_triang.c new file mode 100644 index 000000000..1a130906e --- /dev/null +++ b/Tools/Stripe_w/sgi_triang.c @@ -0,0 +1,628 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: sgi_triang.c + File contains the routines that do the whole triangulation + of polygons. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "global.h" +#include "output.h" +#include "polverts.h" +#include "sturcts.h" +#include "common.h" +#include "util.h" +#include "init.h" + +int Adjacent(int id2,int id1, int *list, int size) +{ + /* Return the vertex that is adjacent to id1, + but is not id2, in the list of integers. + */ + + register int x=0; + + while (x < size) + { + if (*(list+x) == id1) + { + if ((x != (size -1)) && (x != 0)) + { + if ( *(list+x+1) != id2) + return *(list+x+1); + else + return *(list+x-1); + } + else if (x == (size -1)) + { + if (*(list) != id2) + return *(list); + else + return *(list+x-1); + } + else + { + if (*(list+size-1) != id2) + return *(list+size-1); + else + return *(list+x+1); + } + } + x++; + } + /* if there are degeneracies */ + return id1; +} + + +void Rearrange_Index(int *index, int size) +{ + /* If we are in the middle of a strip we must find the + edge to start on, which is the last edge that we had + transmitted. + */ + int x,f,y,e1,e2,e3; + register int increment = 1; + int *temp; + + /* Find where the input edge is in the input list */ + Last_Edge(&e1,&e2,&e3,0); + for (y = 0; y < size; y++) + { + if (*(index+y) == e2) + { + if ((y != (size - 1)) && (*(index+y+1) == e3)) + break; + else if ((y == (size - 1)) && (*(index) == e3)) + break; + else if ((y != 0) && (*(index+y-1) == e3)) + { + increment = -1; + break; + } + else if ((y==0) && (*(index+size-1) == e3)) + { + increment = -1; + break; + } + } + if (*(index+y) == e3) + { + if ((y != (size - 1)) && (*(index+y+1) == e2)) + break; + else if ((y == (size - 1)) && (*(index) == e2)) + break; + else if ((y != 0) && (*(index+y-1) == e2)) + { + increment = -1; + break; + } + else if ((y==0) && (*(index+size-1) == e2)) + { + increment = -1; + break; + } + } + /* Edge is not here, we are at the beginning */ + if ((y == (size-1)) && (increment != -1)) + return; + } + + /* Now put the list into a new list, starting with the + input edge. Increment tells us whether we have to go + forward or backward. + */ + /* Was in good position already */ + if ((y == 0) && (increment == 1)) + return; + + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + + if (increment == 1) + { + x=0; + for (f = y ; f< size; f++) + { + *(index+x) = *(temp+f); + x++; + } + /* Finish the rest of the list */ + for(f = 0; f < y ; f++) + { + *(index+x) = *(temp+f); + x++; + } + } + else + { + x=0; + for (f = y ; f >= 0; f--) + { + *(index+x) = *(temp+f); + x++; + } + /* Finish the rest of the list */ + for(f = (size - 1); f > y ; f--) + { + *(index+x) = *(temp+f); + x++; + } + } +} + +void Delete_From_List(int id,int *list, int *size) +{ + /* Delete the occurence of id in the list. + (list has size size) + */ + + int *temp; + register int x,y=0; + + temp = (int *) malloc(sizeof(int) * (*size)); + for (x=0; x<(*size); x++) + { + if (*(list+x) != id) + { + *(temp+y) = *(list+x); + y++; + } + } + *(temp+y) = -1; + *size = *size - (*size - y - 1); + memcpy(list,temp,sizeof(int)*(*size)); +} + + +void Build_SGI_Table(int num_verts,int num_faces) +{ + /* Build a table that has the polygons sorted by the + number of adjacent polygons. + */ + int x,y,size,tally=0; + ListHead *pListHead; + PF_FACES temp = NULL; + + /* For each face....*/ + for (x=0;x < num_faces;x++) + { + pListHead = PolFaces[x]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp != NULL ) + { + /* Size of the polygon */ + size = temp->nPolSize; + if (size != 1) + { + for (y = 0; y< size; y++) + { + if (y != (size-1)) + tally += Num_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1)); + else + tally += Num_Adj(*(temp->pPolygon),*(temp->pPolygon+(size-1))); + } + + /* Tally is the number of polygons that is adjacent to + the current polygon. + */ + /* Now put the face in the proper bucket depending on tally. */ + Add_Sgi_Adj(tally,x); + temp = NULL; + tally=0; + } + } + } +} + + +void Triangulate_Quad(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,int reversed,int face_id, + int where,int color1,int color2,int color3) +{ + int vertex4,vertex5; + + /* This routine will nonblindly triangulate a quad, meaning + that there is a definite input and a definite output + edge that we must adhere to. Reversed will tell the orientation + of the input edge. (Reversed is -1 is we do not have an input + edge, in other words we are at the beginning of a strip.) + Out_edge* is the output edge, and in_edge* is the input edge. + Index are the edges of the polygon + and size is the size of the polygon. Begin is whether we are + at the start of a new strip. + */ + + /* If we do not have an input edge, then we can make our input + edge whatever we like, therefore it will be easier to come + out on the output edge. + */ + if (reversed == -1) + { + vertex4 = Adjacent(out_edge1,out_edge2,index,size); + vertex5 = Get_Other_Vertex(vertex4,out_edge1,out_edge2,index); + Output_Tri(vertex5,vertex4,out_edge1,output,color1,color2,color3,where); + Output_Tri(vertex4,out_edge1,out_edge2,output,color1,color2,color3,where); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off, but cannot use the whole quad? + */ + + if (in_edge2 == out_edge1) + { + /* Output the triangle that comes out the correct + edge last. First output the triangle that comes out + the wrong edge. + */ + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge2,index); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where); + Output_Tri(vertex4,in_edge2,out_edge2,output,color1,color2,color3,where); + return; + } + /* The next case is where it is impossible to come out the + edge that we want. So we will have to start a new strip to + come out on that edge. We will output the one triangle + that we can, and then start the new strip with the triangle + that comes out on the edge that we want to come out on. + */ + else if (in_edge1 == out_edge1) + { + /* We want to output the first triangle (whose output + edge is not the one that we want. + We have to find the vertex that we need, which is + the other vertex which we do not have. + */ + vertex4 = Get_Other_Vertex(in_edge2,in_edge1,out_edge2,index); + Output_Tri(in_edge2,in_edge1,vertex4,output,color1,color2,color3,where); + Output_Tri(vertex4,in_edge1,out_edge2,output,color1,color2,color3,where); + return; + } + + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_Tri(in_edge2,in_edge1,vertex4,output,color1,color2,color3,where); + Output_Tri(vertex4,in_edge1,out_edge1,output,color1,color2,color3,where); + return; + } + else if (in_edge2 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where); + Output_Tri(vertex4,in_edge2,out_edge1,output,color1,color2,color3,where); + return; + } + + /* The final case is where we want to come out the opposite + edge. + */ + else + { + if( ((!reversed) && (out_edge1 == (Adjacent(in_edge1,in_edge2,index,size)))) || + ((reversed) && (out_edge2 == (Adjacent(in_edge2,in_edge1,index,size))))) + { + /* We need to know the orientation of the input + edge, so we know which way to put the diagonal. + And also the output edge, so that we triangulate + correctly. + */ + Output_Tri(in_edge1,in_edge2,out_edge2,output,color1,color2,color3,where); + Output_Tri(in_edge2,out_edge2,out_edge1,output,color1,color2,color3,where); + } + else + { + /* Input and output orientation was reversed, so diagonal will + be reversed from above. + */ + Output_Tri(in_edge1,in_edge2,out_edge1,output,color1,color2,color3,where); + Output_Tri(in_edge2,out_edge1,out_edge2,output,color1,color2,color3,where); + } + return; + } +} + +void Triangulate_Polygon(int out_edge1, int out_edge2, int in_edge1, + int in_edge2, int size, int *index, + FILE *output, int reversed, int face_id, + int where, int color1, int color2, int color3) +{ + /* We have a polygon that we need to nonblindly triangulate. + We will recursively try to triangulate it, until we are left + with a polygon of size 4, which can use the quad routine + from above. We will be taking off a triangle at a time + and outputting it. We will have 3 cases similar to the + cases for the quad above. The inputs to this routine + are the same as for the quad routine. + */ + + int vertex4; + int *temp; + + /* Since we are calling this recursively, we have to check whether + we are down to the case of the quad. + */ + + if (size == 4) + { + Triangulate_Quad(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,output,reversed,face_id,where,color1,color2,color3); + return; + } + + + + /* We do not have a specified input edge, and therefore we + can make it anything we like, as long as we still come out + the output edge that we want. + */ + if (reversed == -1) + { + /* Get the vertex for the last triangle, which is + the one coming out the output edge, before we do + any deletions to the list. We will be doing this + bottom up. + */ + vertex4 = Adjacent(out_edge1,out_edge2,index,size); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(out_edge2,index,&size); + Triangulate_Polygon(out_edge1,vertex4,in_edge2, + vertex4,size-1,index,output,reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + /* Lastly do the triangle that comes out the output + edge. + */ + Output_Tri(vertex4,out_edge1,out_edge2,output,color1,color2,color3,where); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off that comes out the correct output edge, + but we cannot use the whole polygon? + */ + if (in_edge2 == out_edge1) + { + /* Output the triangle that comes out the correct + edge last. First recursively do the rest of the + polygon. + */ + /* Do the rest of the polygon without the triangle. + We will be doing a fan triangulation. + */ + /* Get the vertex adjacent to in_edge1, but is not + in_edge2. + */ + vertex4 = Adjacent(in_edge2,in_edge1,index,size); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where); + /* Create a new edgelist without the triangle that + was just outputted. + */ + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(in_edge1,index,&size); + Triangulate_Polygon(out_edge1,out_edge2,in_edge2, + vertex4,size-1,index,output,!reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + return; + } + + /* Next case is where it is again consecutive, but the triangle + formed by the consecutive edges do not come out of the + correct output edge. For this case, we can not do much to + keep it sequential. Try and do the fan. + */ + else if (in_edge1 == out_edge1) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = Adjacent(in_edge1,in_edge2,index,size); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where); + /* Since that triangle goes out of the polygon (the + output edge of it), we can make our new input edge + anything we like, so we will try to make it good for + the strip. (This will be like starting a new strip, + all so that we can go out the correct output edge.) + */ + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(in_edge2,index,&size); + Triangulate_Polygon(out_edge1,out_edge2,in_edge1, + vertex4,size-1,index,output,reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + return; + } + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = Adjacent(in_edge1,in_edge2,index,size); + Output_Tri(in_edge2,in_edge1,vertex4,output,color1,color2,color3,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(in_edge2,index,&size); + Triangulate_Polygon(out_edge1,out_edge2,in_edge1, + vertex4,size-1,index,output,reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + return; + } + else if (in_edge2 == out_edge2) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = Adjacent(in_edge2,in_edge1,index,size); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(in_edge1,index,&size); + Triangulate_Polygon(out_edge1,out_edge2,vertex4, + in_edge2,size-1,index,output,reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + return; + } + + /* Else the edge is not consecutive, and it is sufficiently + far away, for us not to make a conclusion at this time. + So we can take off a triangle and recursively call this + function. + */ + else + { + vertex4 = Adjacent(in_edge2,in_edge1,index,size); + Output_Tri(in_edge1,in_edge2,vertex4,output,color1,color2,color3,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_List(in_edge1,index,&size); + Triangulate_Polygon(out_edge1,out_edge2,in_edge2, + vertex4,size-1,index,output,!reversed,face_id,where,color1,color2,color3); + memcpy(index,temp,sizeof(int)*size); + return; + } +} + +void Triangulate(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,int reversed,int face_id, int where, + int color1, int color2,int color3) +{ + /* We have the info we need to triangulate a polygon */ + + if (size == 4) + Triangulate_Quad(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,output,reversed,face_id,where,color1,color2,color3); + else + Triangulate_Polygon(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,output,reversed,face_id,where,color1,color2,color3); +} + +void Non_Blind_Triangulate(int size,int *index, + FILE *output,int next_face_id,int face_id,int where, + int color1,int color2,int color3) +{ + int id1,id2,id3; + int nedge1,nedge2; + int reversed; + /* We have a polygon that has to be triangulated and we cannot + do it blindly, ie we will try to come out on the edge that + has the least number of adjacencies + */ + + Last_Edge(&id1,&id2,&id3,0); + /* Find the edge that is adjacent to the new face , + also return whether the orientation is reversed in the + face of the input edge, which is id2 and id3. + */ + if (next_face_id == -1) + { + printf("The face is -1 and the size is %d\n",size); + exit(0); + } + + reversed = Get_Edge(&nedge1,&nedge2,index,next_face_id,size,id2,id3); + /* Do the triangulation */ + + /* If reversed is -1, the input edge is not in the polygon, therefore we can have the + input edge to be anything we like, since we are at the beginning + of a strip + */ + Triangulate(nedge1,nedge2,id2,id3,size,index,output,reversed, + face_id, where,color1,color2,color3); +} + + + +void Blind_Triangulate(int size, int *index, FILE *output, + BOOL begin, int where ,int color1,int color2, + int color3) +{ + /* save sides in temp array, we need it so we know + about swaps. + */ + int mode, decreasing,increasing,e1,e2,e3; + + /* Rearrange the index list so that the input edge is first + */ + if (!begin) + Rearrange_Index(index,size); + + /* We are given a polygon of more than 3 sides + and want to triangulate it. We will output the + triangles to the output file. + */ + + /* Find where the input edge is in the input list */ + Last_Edge(&e1,&e2,&e3,0); + if (( (!begin) && (*(index) == e2) ) || (begin)) + { + Output_Tri(*(index+0),*(index+1),*(index+size-1),output,color1,color2,color3,where); + /* If we have a quad, (chances are yes), then we know that + we can just add one diagonal and be done. (divide the + quad into 2 triangles. + */ + if (size == 4) + { + Output_Tri(*(index+1),*(index+size-1),*(index+2),output,color1,color2,color3,where); + return; + } + increasing = 1; + mode = 1; + + } + else if (!begin) + { + Output_Tri(*(index+1),*(index+0),*(index+size-1),output,color1,color2,color3,where); + if (size == 4) + { + Output_Tri(*(index+0),*(index+size-1),*(index+2),output,color1,color2,color3,where); + return; + } + Output_Tri(*(index+0),*(index+size-1),*(index+2),output,color1,color2,color3,where); + increasing = 2; + mode = 0; + } + if (size != 4) + { + /* We do not have a quad, we have something bigger. */ + decreasing = size - 1; + do + { + /* Will be alternating diagonals, so we will be increasing + and decreasing around the polygon. + */ + if (mode) + { + Output_Tri(*(index+increasing),*(index+decreasing),*(index+increasing+1),output,color1,color2,color3,where); + increasing++; + } + else + { + Output_Tri(*(index+decreasing),*(index+increasing),*(index+decreasing-1),output,color1,color2,color3,where); + decreasing--; + } + mode = !mode; + } while ((decreasing - increasing) >= 2); + + } +} + + + + diff --git a/Tools/Stripe_w/sgi_triangex.c b/Tools/Stripe_w/sgi_triangex.c new file mode 100644 index 000000000..9f153a1e8 --- /dev/null +++ b/Tools/Stripe_w/sgi_triangex.c @@ -0,0 +1,582 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: sgi_triangex.c + This file contains routines that are used for various functions in + the local algorithm. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "global.h" +#include "outputex.h" +#include "polverts.h" +#include "sturctsex.h" +#include "common.h" +#include "util.h" + + +int AdjacentEx(int id2,int id1, int *list, int size) +{ + /* Return the vertex that is adjacent to id1, + but is not id2, in the list of integers. + */ + + register int x=0; + + while (x < size) + { + if (*(list+x) == id1) + { + if ((x != (size -1)) && (x != 0)) + { + if ( *(list+x+1) != id2) + return *(list+x+1); + else + return *(list+x-1); + } + else if (x == (size -1)) + { + if (*(list) != id2) + return *(list); + else + return *(list+x-1); + } + else + { + if (*(list+size-1) != id2) + return *(list+size-1); + else + return *(list+x+1); + } + } + x++; + } + printf("Error in the list\n"); + exit(0); +} + + +void Delete_From_ListEx(int id,int *list, int size) +{ + /* Delete the occurence of id in the list. + (list has size size) + */ + + int *temp; + register int x,y=0; + + temp = (int *) malloc(sizeof(int) * size); + for (x=0; x<size; x++) + { + if (*(list+x) != id) + { + *(temp+y) = *(list+x); + y++; + } + } + if(y != (size-1)) + { + printf("There is an error in the delete\n"); + exit(0); + } + *(temp+size-1) = -1; + memcpy(list,temp,sizeof(int)*size); + +} + + +void Triangulate_QuadEx(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,FILE *fp,int reversed,int face_id, + int where) +{ + int vertex4,vertex5; + + /* This routine will nonblindly triangulate a quad, meaning + that there is a definite input and a definite output + edge that we must adhere to. Reversed will tell the orientation + of the input edge. (Reversed is -1 is we do not have an input + edge, in other words we are at the beginning of a strip.) + Out_edge* is the output edge, and in_edge* is the input edge. + Index are the edges of the polygon + and size is the size of the polygon. Begin is whether we are + at the start of a new strip. + */ + + /* If we do not have an input edge, then we can make our input + edge whatever we like, therefore it will be easier to come + out on the output edge. + */ + if (reversed == -1) + { + vertex4 = AdjacentEx(out_edge1,out_edge2,index,size); + vertex5 = Get_Other_Vertex(vertex4,out_edge1,out_edge2,index); + Output_TriEx(vertex5,vertex4,out_edge1,output,-1,-1,where); + Output_TriEx(vertex4,out_edge1,out_edge2,output,-1,-1,where); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off, but cannot use the whole quad? + */ + + if (in_edge2 == out_edge1) + { + /* Output the triangle that comes out the correct + edge last. First output the triangle that comes out + the wrong edge. + */ + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge2,index); + Output_TriEx(in_edge1,in_edge2,vertex4,output,-1,-1,where); + Output_TriEx(vertex4,in_edge2,out_edge2,output,-1,-1,where); + return; + } + /* The next case is where it is impossible to come out the + edge that we want. So we will have to start a new strip to + come out on that edge. We will output the one triangle + that we can, and then start the new strip with the triangle + that comes out on the edge that we want to come out on. + */ + else if (in_edge1 == out_edge1) + { + /* We want to output the first triangle (whose output + edge is not the one that we want. + We have to find the vertex that we need, which is + the other vertex which we do not have. + */ + vertex4 = Get_Other_Vertex(in_edge2,in_edge1,out_edge2,index); + Output_TriEx(in_edge2,in_edge1,vertex4,output,-1,-1,where); + Output_TriEx(vertex4,in_edge1,out_edge2,output,-1,-1,where); + return; + } + + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_TriEx(in_edge2,in_edge1,vertex4,output,-1,-1,where); + Output_TriEx(vertex4,in_edge1,out_edge1,output,-1,-1,where); + return; + } + else if (in_edge2 == out_edge2) + { + vertex4 = Get_Other_Vertex(in_edge1,in_edge2,out_edge1,index); + Output_TriEx(in_edge1,in_edge2,vertex4,output,-1,-1,where); + Output_TriEx(vertex4,in_edge2,out_edge1,output,-1,-1,where); + return; + } + + /* The final case is where we want to come out the opposite edge.*/ + else + { + if( ((!reversed) && (out_edge1 == (AdjacentEx(in_edge1,in_edge2,index,size)))) || + ((reversed) && (out_edge2 == (AdjacentEx(in_edge2,in_edge1,index,size))))) + { + /* We need to know the orientation of the input + edge, so we know which way to put the diagonal. + And also the output edge, so that we triangulate correctly. + */ + Output_TriEx(in_edge1,in_edge2,out_edge2,output,-1,-1,where); + Output_TriEx(in_edge2,out_edge2,out_edge1,output,-1,-1,where); + } + else + { + /* Input and output orientation was reversed, so diagonal will + be reversed from above. + */ + Output_TriEx(in_edge1,in_edge2,out_edge1,output,-1,-1,where); + Output_TriEx(in_edge2,out_edge1,out_edge2,output,-1,-1,where); + } + return; + } +} + +void Triangulate_PolygonEx(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,FILE *fp,int reversed,int face_id, + int where) +{ + /* We have a polygon that we need to nonblindly triangulate. + We will recursively try to triangulate it, until we are left + with a polygon of size 4, which can use the quad routine + from above. We will be taking off a triangle at a time + and outputting it. We will have 3 cases similar to the + cases for the quad above. The inputs to this routine + are the same as for the quad routine. + */ + + int vertex4; + int *temp; + + + /* Since we are calling this recursively, we have to check whether + we are down to the case of the quad. + */ + + if (size == 4) + { + Triangulate_QuadEx(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,output,fp,reversed,face_id,where); + return; + } + + + + /* We do not have a specified input edge, and therefore we + can make it anything we like, as long as we still come out + the output edge that we want. + */ + if (reversed == -1) + { + /* Get the vertex for the last triangle, which is + the one coming out the output edge, before we do + any deletions to the list. We will be doing this + bottom up. + */ + vertex4 = AdjacentEx(out_edge1,out_edge2,index,size); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(out_edge2,index,size); + Triangulate_PolygonEx(out_edge1,vertex4,in_edge2, + vertex4,size-1,index,output,fp,reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + /* Lastly do the triangle that comes out the output + edge. + */ + Output_TriEx(vertex4,out_edge1,out_edge2,output,-1,-1,where); + return; + } + + /* These are the 5 cases that we can have for the output edge */ + + /* Are they consecutive so that we form a triangle to + peel off that comes out the correct output edge, + but we cannot use the whole polygon? + */ + if (in_edge2 == out_edge1) + { + /* Output the triangle that comes out the correct + edge last. First recursively do the rest of the + polygon. + */ + /* Do the rest of the polygon without the triangle. + We will be doing a fan triangulation. + */ + /* Get the vertex adjacent to in_edge1, but is not + in_edge2. + */ + vertex4 = AdjacentEx(in_edge2,in_edge1,index,size); + Output_TriEx(in_edge1,in_edge2,vertex4,output,-1,-1,where); + /* Create a new edgelist without the triangle that + was just outputted. + */ + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(in_edge1,index,size); + Triangulate_PolygonEx(out_edge1,out_edge2,in_edge2, + vertex4,size-1,index,output,fp,!reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + return; + } + + /* Next case is where it is again consecutive, but the triangle + formed by the consecutive edges do not come out of the + correct output edge. For this case, we can not do much to + keep it sequential. Try and do the fan. + */ + else if (in_edge1 == out_edge1) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = AdjacentEx(in_edge1,in_edge2,index,size); + Output_TriEx(in_edge1,in_edge2,vertex4,fp,-1,-1,where); + /* Since that triangle goes out of the polygon (the + output edge of it), we can make our new input edge + anything we like, so we will try to make it good for + the strip. (This will be like starting a new strip, + all so that we can go out the correct output edge.) + */ + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(in_edge2,index,size); + Triangulate_PolygonEx(out_edge1,out_edge2,in_edge1, + vertex4,size-1,index,output,fp,reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + return; + } + /* Consecutive cases again, but with the output edge reversed */ + else if (in_edge1 == out_edge2) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = AdjacentEx(in_edge1,in_edge2,index,size); + Output_TriEx(in_edge2,in_edge1,vertex4,fp,-1,-1,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(in_edge2,index,size); + Triangulate_PolygonEx(out_edge1,out_edge2,in_edge1, + vertex4,size-1,index,output,fp,reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + return; + } + else if (in_edge2 == out_edge2) + { + /* Get vertex adjacent to in_edge2, but is not in_edge1 */ + vertex4 = AdjacentEx(in_edge2,in_edge1,index,size); + Output_TriEx(in_edge1,in_edge2,vertex4,fp,-1,-1,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(in_edge1,index,size); + Triangulate_PolygonEx(out_edge1,out_edge2,vertex4, + in_edge2,size-1,index,output,fp,reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + return; + } + + /* Else the edge is not consecutive, and it is sufficiently + far away, for us not to make a conclusion at this time. + So we can take off a triangle and recursively call this + function. + */ + else + { + vertex4 = AdjacentEx(in_edge2,in_edge1,index,size); + Output_TriEx(in_edge1,in_edge2,vertex4,fp,-1,-1,where); + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + Delete_From_ListEx(in_edge1,index,size); + Triangulate_PolygonEx(out_edge1,out_edge2,in_edge2, + vertex4,size-1,index,output,fp,!reversed,face_id,where); + memcpy(index,temp,sizeof(int)*size); + return; + } +} + +void TriangulateEx(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *fp,FILE *output,int reversed,int face_id, int where) +{ + /* We have the info we need to triangulate a polygon */ + + if (size == 4) + Triangulate_QuadEx(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,fp,output,reversed,face_id,where); + else + Triangulate_PolygonEx(out_edge1,out_edge2,in_edge1,in_edge2,size, + index,fp,output,reversed,face_id,where); +} + +void Non_Blind_TriangulateEx(int size,int *index, FILE *fp, + FILE *output,int next_face_id,int face_id,int where) +{ + int id1,id2,id3; + int nedge1,nedge2; + int reversed; + /* We have a polygon that has to be triangulated and we cannot + do it blindly, ie we will try to come out on the edge that + has the least number of adjacencies + */ + + Last_Edge(&id1,&id2,&id3,0); + /* Find the edge that is adjacent to the new face , + also return whether the orientation is reversed in the + face of the input edge, which is id2 and id3. + */ + if (next_face_id == -1) + { + printf("The face is -1 and the size is %d\n",size); + exit(0); + } + + reversed = Get_EdgeEx(&nedge1,&nedge2,index,next_face_id,size,id2,id3); + /* Do the triangulation */ + + /* If reversed is -1, the input edge is not in the polygon, therefore we can have the + input edge to be anything we like, since we are at the beginning + of a strip + */ + TriangulateEx(nedge1,nedge2,id2,id3,size,index,fp,output,reversed, + face_id, where); +} + +void Rearrange_IndexEx(int *index, int size) +{ + /* If we are in the middle of a strip we must find the + edge to start on, which is the last edge that we had + transmitted. + */ + int x,f,y,e1,e2,e3; + int increment = 1; + int *temp; + + /* Find where the input edge is in the input list */ + Last_Edge(&e1,&e2,&e3,0); + for (y = 0; y < size; y++) + { + if (*(index+y) == e2) + { + if ((y != (size - 1)) && (*(index+y+1) == e3)) + break; + else if ((y == (size - 1)) && (*(index) == e3)) + break; + else if ((y != 0) && (*(index+y-1) == e3)) + { + increment = -1; + break; + } + else if ((y==0) && (*(index+size-1) == e3)) + { + increment = -1; + break; + } + } + if (*(index+y) == e3) + { + if ((y != (size - 1)) && (*(index+y+1) == e2)) + break; + else if ((y == (size - 1)) && (*(index) == e2)) + break; + else if ((y != 0) && (*(index+y-1) == e2)) + { + increment = -1; + break; + } + else if ((y==0) && (*(index+size-1) == e2)) + { + increment = -1; + break; + } + } + /* Edge is not here, we are at the beginning */ + if ((y == (size-1)) && (increment != -1)) + return; + } + + /* Now put the list into a new list, starting with the + input edge. Increment tells us whether we have to go + forward or backward. + */ + /* Was in good position already */ + if ((y == 0) && (increment == 1)) + return; + + + temp = (int *) malloc(sizeof(int) * size); + memcpy(temp,index,sizeof(int)*size); + + if (increment == 1) + { + x=0; + for (f = y ; f< size; f++) + { + *(index+x) = *(temp+f); + x++; + } + /* Finish the rest of the list */ + for(f = 0; f < y ; f++) + { + *(index+x) = *(temp+f); + x++; + } + } + else + { + x=0; + for (f = y ; f >= 0; f--) + { + *(index+x) = *(temp+f); + x++; + } + /* Finish the rest of the list */ + for(f = (size - 1); f > y ; f--) + { + *(index+x) = *(temp+f); + x++; + } + } +} + +void Blind_TriangulateEx(int size, int *index, FILE *fp, + FILE *output, BOOL begin, int where ) +{ + /* save sides in temp array, we need it so we know + about swaps. + */ + int mode, decreasing,increasing,e1,e2,e3; + + /* Rearrange the index list so that the input edge is first + */ + if (!begin) + Rearrange_IndexEx(index,size); + + /* We are given a polygon of more than 3 sides + and want to triangulate it. We will output the + triangles to the output file. + */ + + /* Find where the input edge is in the input list */ + Last_Edge(&e1,&e2,&e3,0); + if (( (!begin) && (*(index) == e2) ) || (begin)) + { + Output_TriEx(*(index+0),*(index+1),*(index+size-1),fp,-1,-1,where); + /* If we have a quad, (chances are yes), then we know that + we can just add one diagonal and be done. (divide the + quad into 2 triangles. + */ + if (size == 4) + { + Output_TriEx(*(index+1),*(index+size-1),*(index+2),fp,-1,-1,where); + return; + } + increasing = 1; + mode = 1; + + } + else if (!begin) + { + Output_TriEx(*(index+1),*(index+0),*(index+size-1),fp,-1,-1,where); + if (size == 4) + { + Output_TriEx(*(index+0),*(index+size-1),*(index+2),fp,-1,-1,where); + return; + } + Output_TriEx(*(index+0),*(index+size-1),*(index+2),fp,-1,-1,where); + increasing = 2; + mode = 0; + } + if (size != 4) + { + /* We do not have a quad, we have something bigger. */ + decreasing = size - 1; + + do + { + /* Will be alternating diagonals, so we will be increasing + and decreasing around the polygon. + */ + if (mode) + { + Output_TriEx(*(index+increasing),*(index+decreasing),*(index+increasing+1),fp,-1,-1,where); + increasing++; + } + else + { + Output_TriEx(*(index+decreasing),*(index+increasing),*(index+decreasing-1),fp,-1,-1,where); + decreasing--; + } + mode = !mode; + } while ((decreasing - increasing) >= 2); + + } +} + + diff --git a/Tools/Stripe_w/struct.c b/Tools/Stripe_w/struct.c new file mode 100644 index 000000000..7010012b7 --- /dev/null +++ b/Tools/Stripe_w/struct.c @@ -0,0 +1,549 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: struct.c + Contains routines that update structures, and micellaneous routines. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdlib.h> +#include <string.h> +#include "polverts.h" +#include "ties.h" +#include "output.h" +#include "triangulate.h" +#include "sturcts.h" +#include "options.h" +#include "common.h" +#include "util.h" + +int out1 = -1; +int out2 = -1; + +int Get_Edge(int *edge1,int *edge2,int *index,int face_id, + int size, int id1, int id2) +{ + /* Put the edge that is adjacent to face_id into edge1 + and edge2. For each edge see if it is adjacent to + face_id. Id1 and id2 is the input edge, so see if + the orientation is reversed, and save it in reversed. + */ + register int x; + int reversed = -1; + BOOL set = FALSE; + + for (x=0; x< size; x++) + { + if (x == (size-1)) + { + if ((*(index) == id1) && (*(index+size-1)==id2)) + { + if (set) + return 1; + reversed = 1; + } + else if ((*(index) == id2) && (*(index+size-1)==id1)) + { + if (set) + return 0; + reversed = 0; + } + + if (Look_Up(*(index),*(index+size-1),face_id)) + { + if ( (out1 != -1) && ( (out1 == *(index)) || (out1 == *(index+size-1)) ) && + ( (out2 == *(index)) || (out2 == *(index+size-1)) )) + { + set = TRUE; + *edge1 = *(index); + *edge2 = *(index+size-1); + } + else if (out1 == -1) + { + set = TRUE; + *edge1 = *(index); + *edge2 = *(index+size-1); + } + if ((reversed != -1) && (set)) + return reversed; + } + } + else + { + if ((*(index+x) == id1) && (*(index+x+1)==id2)) + { + if (set) + return 0; + reversed = 0; + } + else if ((*(index+x) == id2) && (*(index+x+1)==id1)) + { + if (set) + return 1; + reversed = 1; + } + + if (Look_Up(*(index+x),*(index+x+1),face_id)) + { + if ( (out1 != -1) && ( (out1 == *(index+x)) || (out1 == *(index+x+1)) ) && + ((out2 == *(index+x)) || (out2 == *(index+x+1)))) + { + set = TRUE; + *edge1 = *(index+x); + *edge2 = *(index+x+1); + } + else if (out1 == -1) + { + set = TRUE; + *edge1 = *(index+x); + *edge2 = *(index+x + 1); + } + if ((reversed != -1) && (set)) + return reversed; + } + } + } + if ((x == size) && (reversed != -1)) + { + /* Could not find the output edge */ + printf("Error in the Lookup %d %d %d %d %d %d %d %d\n",face_id,id1,id2,reversed,*edge1,*edge2,out1,out2); + exit(0); + } + return reversed; +} + + +void Update_Face(int *next_bucket, int *min_face, int face_id, int *e1, + int *e2,int temp1,int temp2,int *ties) +{ + /* We have a face id that needs to be decremented. + We have to determine where it is in the structure, + so that we can decrement it. + */ + /* The number of adjacencies may have changed, so to locate + it may be a little tricky. However we know that the number + of adjacencies is less than or equal to the original number + of adjacencies, + */ + int y,size; + ListHead *pListHead; + PF_FACES temp = NULL; + PLISTINFO lpListInfo; + static int each_poly = 0; + BOOL there = FALSE; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp != NULL ) + { + /* Size of the polygon */ + size = temp->nPolSize; + /* We did it already */ + if (size == 1) + return; + for (y = 0; y< size; y++) + { + /* If we are doing partial triangulation, we must check + to see whether the edge is still there in the polygon, + since we might have done a portion of the polygon + and saved the rest for later. + */ + if (y != (size-1)) + { + if( ((temp1 == *(temp->pPolygon+y)) && (temp2 ==*(temp->pPolygon+y+1))) + || ((temp2 == *(temp->pPolygon+y)) && (temp1 ==*(temp->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((temp1 == *(temp->pPolygon)) && (temp2 == *(temp->pPolygon+size-1))) + || ((temp2 == *(temp->pPolygon)) && (temp1 ==*(temp->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + + if (!there) + /* Original edge was already used, we cannot use this polygon */ + return; + + /* We have a starting point to start our search to locate + this polygon. + */ + + /* Check to see if this polygon was done */ + lpListInfo = Done(face_id,59,&y); + + if (lpListInfo == NULL) + return; + + /* Was not done, but there is an error in the adjacency calculations */ + if (y == 0) + { + printf("There is an error in finding the adjacencies\n"); + exit(0); + } + + /* Now put the face in the proper bucket depending on tally. */ + /* First add it to the new bucket, then remove it from the old */ + Add_Sgi_Adj(y-1,face_id); + RemoveList(array[y],lpListInfo); + + /* Save it if it was the smallest seen so far since then + it will be the next face + Here we will have different options depending on + what we want for resolving ties: + 1) First one we see we will use + 2) Random resolving + 3) Look ahead + 4) Alternating direction + */ + /* At a new strip */ + if (*next_bucket == 60) + *ties = *ties + each_poly; + /* Have a tie */ + if (*next_bucket == (y-1)) + { + Add_Ties(face_id); + each_poly++; + } + /* At a new minimum */ + if (*next_bucket > (y-1)) + { + *next_bucket = y-1; + *min_face = face_id; + *e1 = temp1; + *e2 = temp2; + each_poly = 0; + Clear_Ties(); + Add_Ties(face_id); + } + } +} + + +void Delete_Adj(int id1, int id2,int *next_bucket,int *min_face, + int current_face,int *e1,int *e2,int *ties) +{ + /* Find the face that is adjacent to the edge and is not the + current face. Delete one adjacency from it. Save the min + adjacency seen so far. + */ + register int count=0; + PF_EDGES temp = NULL; + ListHead *pListHead; + int next_face; + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* It could be a new edge that we created. So we can + exit, since there is not a face adjacent to it. + */ + return; + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* Was a new edge that was created and therefore + does not have anything adjacent to it + */ + return; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return; + + /* Was adjacent to something */ + else + { + if (temp->edge[2] == current_face) + next_face = temp->edge[1]; + else + next_face = temp->edge[2]; + } + /* We have the other face adjacent to this edge, it is + next_face. Now we need to decrement this faces' adjacencies. + */ + Update_Face(next_bucket, min_face, next_face,e1,e2,id1,id2,ties); +} + + +int Change_Face(int face_id,int in1,int in2, + ListHead *pListHead, P_ADJACENCIES temp, BOOL no_check) +{ + /* We are doing a partial triangulation and we need to + put the new face of triangle into the correct bucket + */ + int input_adj,y; + + /* Find the old number of adjacencies to this face, + so we know where to delete it from + */ + y = Old_Adj(face_id); + + /* Do we need to change the adjacency? Maybe the edge on the triangle + that was outputted was not adjacent to anything. We know if we + have to check by "check". We came out on the output edge + that we needed, then we know that the adjacencies will decrease + by exactly one. + */ + if (!no_check) + { + input_adj = Number_Adj(in1,in2,face_id); + /* If there weren't any then don't do anything */ + if (input_adj == 0) + return y; + } + + RemoveList(pListHead,(PLISTINFO)temp); + /* Before we had a quad with y adjacencies. The in edge + did not have an adjacency, since it was just deleted, + since we came in on it. The outedge must have an adjacency + otherwise we would have a bucket 0, and would not be in this + routine. Therefore the new adjacency must be y-1 + */ + + Add_Sgi_Adj(y-1,face_id); + return (y-1); +} + +int Update_Adjacencies(int face_id, int *next_bucket, int *e1, int *e2, + int *ties) +{ + /* Give the face with id face_id, we want to decrement + all the faces that are adjacent to it, since we will + be deleting face_id from the data structure. + We will return the face that has the least number + of adjacencies. + */ + PF_FACES temp = NULL; + ListHead *pListHead; + int size,y,min_face = -1; + + *next_bucket = 60; + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + if ( temp == NULL ) + { + printf("The face was already deleted, there is an error\n"); + exit(0); + } + + /* Size of the polygon */ + size = temp->nPolSize; + for (y = 0; y< size; y++) + { + if (y != (size-1)) + Delete_Adj(*(temp->pPolygon+y),*(temp->pPolygon+y+1), + next_bucket,&min_face,face_id,e1,e2,ties); + else + Delete_Adj(*(temp->pPolygon),*(temp->pPolygon+(size-1)), + next_bucket,&min_face,face_id,e1,e2,ties); + } + return (min_face); +} + + +void Find_Adj_Tally(int id1, int id2,int *next_bucket,int *min_face, + int current_face,int *ties) +{ + /* Find the face that is adjacent to the edge and is not the + current face. Save the min adjacency seen so far. + */ + int size,each_poly=0,y,count=0; + PF_EDGES temp = NULL; + PF_FACES temp2 = NULL; + ListHead *pListHead; + int next_face; + BOOL there = FALSE; + + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* This was a new edge that was created, so it is + adjacent to nothing. + */ + return; + + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* This was a new edge that we created */ + return; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return; + else + { + if (temp->edge[2] == current_face) + next_face = temp->edge[1]; + else + next_face = temp->edge[2]; + } + /* We have the other face adjacent to this edge, it is + next_face. Find how many faces it is adjacent to. + */ + pListHead = PolFaces[next_face]; + temp2 = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. This will be the original number of + polygons adjacent to this polygon, we must then see if this + number has been decremented + */ + if ( temp2 != NULL ) + { + /* Size of the polygon */ + size = temp2->nPolSize; + /* We did it already */ + if (size == 1) + return; + for (y = 0; y< size; y++) + { + /* Make sure that the edge is still in the + polygon and was not deleted, because if the edge was + deleted, then we used it already. + */ + if (y != (size-1)) + { + if( ((id1 == *(temp2->pPolygon+y)) && (id2 ==*(temp2->pPolygon+y+1))) + || ((id2 == *(temp2->pPolygon+y)) && (id1 ==*(temp2->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((id1 == *(temp2->pPolygon)) && (id2 ==*(temp2->pPolygon+size-1))) + || ((id2 == *(temp2->pPolygon)) && (id1 ==*(temp2->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + + if (!there) + /* Edge already used and deleted from the polygon*/ + return; + + /* See if the face was already deleted, and where + it is if it was not + */ + if (Done(next_face,size,&y) == NULL) + return; + + /* Save it if it was the smallest seen so far since then + it will be the next face + Here we will have different options depending on + what we want for resolving ties: + 1) First one we see we will use + 2) Random resolving + 3) Look ahead + 4) Alternating direction + */ + + /* At a new strip */ + if (*next_bucket == 60) + *ties = *ties + each_poly; + /* Have a tie */ + if (*next_bucket == (y-1)) + { + Add_Ties(next_face); + each_poly++; + } + /* At a new minimum */ + if (*next_bucket > (y-1)) + { + *next_bucket = y-1; + *min_face = next_face; + each_poly = 0; + Clear_Ties(); + Add_Ties(next_face); + } + } +} + + +int Min_Face_Adj(int face_id, int *next_bucket, int *ties) +{ + /* Used for the Partial triangulation to find the next + face. It will return the minimum adjacency face id + found at this face. + */ + PF_FACES temp = NULL; + ListHead *pListHead; + int size,y,min_face,test_face; + + *next_bucket = 60; + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + if ( temp == NULL ) + { + printf("The face was already deleted, there is an error\n"); + exit(0); + } + + /* Size of the polygon */ + size = temp->nPolSize; + for (y = 0; y< size; y++) + { + if (y != (size-1)) + Find_Adj_Tally(*(temp->pPolygon+y),*(temp->pPolygon+y+1), + next_bucket,&min_face,face_id,ties); + else + Find_Adj_Tally(*(temp->pPolygon),*(temp->pPolygon+(size-1)), + next_bucket,&min_face,face_id,ties); + } + /* Maybe we can do better by triangulating the face, because + by triangulating the face we will go to a polygon of lesser + adjacencies + */ + if (size == 4) + { + /* Checking for a quad whether to do the whole polygon will + result in better performance because the triangles in the polygon + have less adjacencies + */ + Check_In_Quad(face_id,&test_face); + if (*next_bucket > test_face) + /* We can do better by going through the polygon */ + min_face = face_id; + } + + /* We have a polygon with greater than 4 sides, check to see if going + inside is better than going outside the polygon for the output edge. + */ + else + { + Check_In_Polygon(face_id,&test_face,size); + if (*next_bucket > test_face) + /* We can do better by going through the polygon */ + min_face = face_id; + } + + return (min_face); +} + + + diff --git a/Tools/Stripe_w/structex.c b/Tools/Stripe_w/structex.c new file mode 100644 index 000000000..70359ddc0 --- /dev/null +++ b/Tools/Stripe_w/structex.c @@ -0,0 +1,553 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: structex.c + This file contains routines that are used for various functions in + the local algorithm. +*/ +/*---------------------------------------------------------------------*/ + +#include <stdlib.h> +#include <string.h> +#include "polverts.h" +#include "ties.h" +#include "outputex.h" +#include "triangulatex.h" +#include "sturctsex.h" +#include "options.h" +#include "common.h" +#include "util.h" + +int out1Ex = -1; +int out2Ex = -1; + +int Get_EdgeEx(int *edge1,int *edge2,int *index,int face_id, + int size, int id1, int id2) +{ + /* Put the edge that is adjacent to face_id into edge1 + and edge2. For each edge see if it is adjacent to + face_id. Id1 and id2 is the input edge, so see if + the orientation is reversed, and save it in reversed. + */ + int x; + int reversed = -1; + BOOL set = FALSE; + + for (x=0; x< size; x++) + { + if (x == (size-1)) + { + if ((*(index) == id1) && (*(index+size-1)==id2)) + { + if (set) + return 1; + reversed = 1; + } + else if ((*(index) == id2) && (*(index+size-1)==id1)) + { + if (set) + return 0; + reversed = 0; + } + + if (Look_Up(*(index),*(index+size-1),face_id)) + { + if ( (out1Ex != -1) && ( (out1Ex == *(index)) || (out1Ex == *(index+size-1)) ) && + ( (out2Ex == *(index)) || (out2Ex == *(index+size-1)) )) + { + set = TRUE; + *edge1 = *(index); + *edge2 = *(index+size-1); + } + else if (out1Ex == -1) + { + set = TRUE; + *edge1 = *(index); + *edge2 = *(index+size-1); + } + if ((reversed != -1) && (set)) + return reversed; + } + } + else + { + if ((*(index+x) == id1) && (*(index+x+1)==id2)) + { + if (set) + return 0; + reversed = 0; + } + else if ((*(index+x) == id2) && (*(index+x+1)==id1)) + { + if (set) + return 1; + reversed = 1; + } + + if (Look_Up(*(index+x),*(index+x+1),face_id)) + { + if ( (out1Ex != -1) && ( (out1Ex == *(index+x)) || (out1Ex == *(index+x+1)) ) && + ((out2Ex == *(index+x)) || (out2Ex == *(index+x+1)))) + { + set = TRUE; + *edge1 = *(index+x); + *edge2 = *(index+x+1); + } + else if (out1Ex == -1) + { + set = TRUE; + *edge1 = *(index+x); + *edge2 = *(index+x + 1); + } + if ((reversed != -1) && (set)) + return reversed; + } + } + } + if ((x == size) && (reversed != -1)) + { + /* Could not find the output edge */ + printf("Error in the Lookup %d %d %d %d %d %d %d %d\n",face_id,id1,id2,reversed,*edge1,*edge2,out1Ex,out2Ex); + exit(0); + } + return reversed; +} + + +void Update_FaceEx(int *next_bucket, int *min_face, int face_id, int *e1, + int *e2,int temp1,int temp2,int *ties) +{ + /* We have a face id that needs to be decremented. + We have to determine where it is in the structure, + so that we can decrement it. + */ + /* The number of adjacencies may have changed, so to locate + it may be a little tricky. However we know that the number + of adjacencies is less than or equal to the original number + of adjacencies, + */ + int y,size; + ListHead *pListHead; + PF_FACES temp = NULL; + PLISTINFO lpListInfo; + static int each_poly = 0; + BOOL there = FALSE; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. + */ + if ( temp != NULL ) + { + /* Size of the polygon */ + size = temp->nPolSize; + for (y = 0; y< size; y++) + { + /* If we are doing partial triangulation, we must check + to see whether the edge is still there in the polygon, + since we might have done a portion of the polygon + and saved the rest for later. + */ + if (y != (size-1)) + { + if( ((temp1 == *(temp->pPolygon+y)) && (temp2 ==*(temp->pPolygon+y+1))) + || ((temp2 == *(temp->pPolygon+y)) && (temp1 ==*(temp->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((temp1 == *(temp->pPolygon)) && (temp2 == *(temp->pPolygon+size-1))) + || ((temp2 == *(temp->pPolygon)) && (temp1 ==*(temp->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + + if (!there) + /* Original edge was already used, we cannot use this polygon */ + return; + + /* We have a starting point to start our search to locate + this polygon. + */ + + /* Check to see if this polygon was done */ + lpListInfo = Done(face_id,59,&y); + + if (lpListInfo == NULL) + return; + + /* Was not done, but there is an error in the adjacency calculations */ + /* If more than one edge is adj to it then maybe it was not updated */ + if (y == 0) + return; + + /* Now put the face in the proper bucket depending on tally. */ + /* First add it to the new bucket, then remove it from the old */ + Add_Sgi_Adj(y-1,face_id); + RemoveList(array[y],lpListInfo); + + /* Save it if it was the smallest seen so far since then + it will be the next face + Here we will have different options depending on + what we want for resolving ties: + 1) First one we see we will use + 2) Random resolving + 3) Look ahead + 4) Alternating direction + */ + /* At a new strip */ + if (*next_bucket == 60) + *ties = *ties + each_poly; + /* Have a tie */ + if (*next_bucket == (y-1)) + { + Add_Ties(face_id); + each_poly++; + } + /* At a new minimum */ + if (*next_bucket > (y-1)) + { + *next_bucket = y-1; + *min_face = face_id; + *e1 = temp1; + *e2 = temp2; + each_poly = 0; + Clear_Ties(); + Add_Ties(face_id); + } + } +} + + +void Delete_AdjEx(int id1, int id2,int *next_bucket,int *min_face, + int current_face,int *e1,int *e2,int *ties) +{ + /* Find the face that is adjacent to the edge and is not the + current face. Delete one adjacency from it. Save the min + adjacency seen so far. + */ + register int count=0; + PF_EDGES temp = NULL; + ListHead *pListHead; + int next_face; + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* It could be a new edge that we created. So we can + exit, since there is not a face adjacent to it. + */ + return; + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* Was a new edge that was created and therefore + does not have anything adjacent to it + */ + return; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return; + + /* Was adjacent to something */ + else + { + if (temp->edge[2] == current_face) + next_face = temp->edge[1]; + else + next_face = temp->edge[2]; + } + /* We have the other face adjacent to this edge, it is + next_face. Now we need to decrement this faces' adjacencies. + */ + Update_FaceEx(next_bucket, min_face, next_face,e1,e2,id1,id2,ties); +} + +int Change_FaceEx(int face_id,int in1,int in2, + ListHead *pListHead, P_ADJACENCIES temp, BOOL no_check) +{ + /* We are doing a partial triangulation and we need to + put the new face of triangle into the correct bucket + */ + int input_adj,y; + P_ADJACENCIES pfNode,lpListInfo; + + /* Find the old number of adjacencies to this face, + so we know where to delete it from + */ + y = Old_Adj(face_id); + pListHead = array[y]; + + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + lpListInfo = (P_ADJACENCIES) (SearchList(array[y], pfNode, + (int (*)(void *,void *)) (Compare))); + if (lpListInfo == NULL) + { + printf("There is an error finding the next polygon3 %d\n",face_id); + exit(0); + } + + /* Do we need to change the adjacency? Maybe the edge on the triangle + that was outputted was not adjacent to anything. We know if we + have to check by "check". We came out on the output edge + that we needed, then we know that the adjacencies will decrease + by exactly one. + */ + if (!no_check) + { + input_adj = Number_Adj(in1,in2,face_id); + /* If there weren't any then don't do anything */ + if (input_adj == 0) + return y; + } + + RemoveList(pListHead,(PLISTINFO)/*(temp*/lpListInfo); + /* Before we had a quad with y adjacencies. The in edge + did not have an adjacency, since it was just deleted, + since we came in on it. The outedge must have an adjacency + otherwise we would have a bucket 0, and would not be in this + routine. Therefore the new adjacency must be y-1 + */ + + Add_Sgi_Adj(y-1,face_id); + return (y-1); +} + +int Update_AdjacenciesEx(int face_id, int *next_bucket, int *e1, int *e2, + int *ties) +{ + /* Give the face with id face_id, we want to decrement + all the faces that are adjacent to it, since we will + be deleting face_id from the data structure. + We will return the face that has the least number + of adjacencies. + */ + PF_FACES temp = NULL; + ListHead *pListHead; + int size,y,min_face = -1; + + *next_bucket = 60; + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + if ( temp == NULL ) + { + printf("The face was already deleted, there is an error\n"); + exit(0); + } + + /* Size of the polygon */ + size = temp->nPolSize; + for (y = 0; y< size; y++) + { + if (y != (size-1)) + Delete_AdjEx(*(temp->pPolygon+y),*(temp->pPolygon+y+1), + next_bucket,&min_face,face_id,e1,e2,ties); + else + Delete_AdjEx(*(temp->pPolygon),*(temp->pPolygon+(size-1)), + next_bucket,&min_face,face_id,e1,e2,ties); + } + return (min_face); +} + + + +void Find_Adj_TallyEx(int id1, int id2,int *next_bucket,int *min_face, + int current_face,int *ties) +{ + /* Find the face that is adjacent to the edge and is not the + current face. Save the min adjacency seen so far. + */ + int size,each_poly=0,y,tally=0,count=0; + PF_EDGES temp = NULL; + PF_FACES temp2 = NULL; + ListHead *pListHead; + int next_face; + BOOL there = FALSE; + + + /* Always want smaller id first */ + switch_lower(&id1,&id2); + + pListHead = PolEdges[id1]; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* This was a new edge that was created, so it is + adjacent to nothing. + */ + return; + while (temp->edge[0] != id2) + { + count++; + temp = (PF_EDGES) PeekList(pListHead,LISTHEAD,count); + if (temp == NULL) + /* This was a new edge that we created */ + return; + } + /* Was not adjacent to anything else except itself */ + if (temp->edge[2] == -1) + return; + else + { + if (temp->edge[2] == current_face) + next_face = temp->edge[1]; + else + next_face = temp->edge[2]; + } + /* We have the other face adjacent to this edge, it is + next_face. Find how many faces it is adjacent to. + */ + pListHead = PolFaces[next_face]; + temp2 = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + /* Check each edge of the face and tally the number of adjacent + polygons to this face. This will be the original number of + polygons adjacent to this polygon, we must then see if this + number has been decremented + */ + if ( temp2 != NULL ) + { + /* Size of the polygon */ + size = temp2->nPolSize; + for (y = 0; y< size; y++) + { + /* Make sure that the edge is still in the + polygon and was not deleted, because if the edge was + deleted, then we used it already. + */ + if (y != (size-1)) + { + if( ((id1 == *(temp2->pPolygon+y)) && (id2 ==*(temp2->pPolygon+y+1))) + || ((id2 == *(temp2->pPolygon+y)) && (id1 ==*(temp2->pPolygon+y+1)))) + /* edge is still there we are ok */ + there = TRUE; + } + else + { + if( ((id1 == *(temp2->pPolygon)) && (id2 ==*(temp2->pPolygon+size-1))) + || ((id2 == *(temp2->pPolygon)) && (id1 ==*(temp2->pPolygon+size-1)))) + /* edge is still there we are ok */ + there = TRUE; + } + } + + if (!there) + /* Edge already used and deleted from the polygon*/ + return; + + /* See if the face was already deleted, and where + it is if it was not + */ + if (Done(next_face,size,&y) == NULL) + return; + + /* Save it if it was the smallest seen so far since then + it will be the next face + Here we will have different options depending on + what we want for resolving ties: + 1) First one we see we will use + 2) Random resolving + 3) Look ahead + 4) Alternating direction + */ + + /* At a new strip */ + if (*next_bucket == 60) + *ties = *ties + each_poly; + /* Have a tie */ + if (*next_bucket == (y-1)) + { + Add_Ties(next_face); + each_poly++; + } + /* At a new minimum */ + if (*next_bucket > (y-1)) + { + *next_bucket = y-1; + *min_face = next_face; + each_poly = 0; + Clear_Ties(); + Add_Ties(next_face); + } + } +} + + +int Min_Face_AdjEx(int face_id, int *next_bucket, int *ties) +{ + /* Used for the Partial triangulation to find the next + face. It will return the minimum adjacency face id + found at this face. + */ + PF_FACES temp = NULL; + ListHead *pListHead; + int size,y,min_face,test_face; + + *next_bucket = 60; + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + + if ( temp == NULL ) + { + printf("The face was already deleted, there is an error\n"); + exit(0); + } + + /* Size of the polygon */ + size = temp->nPolSize; + for (y = 0; y< size; y++) + { + if (y != (size-1)) + Find_Adj_TallyEx(*(temp->pPolygon+y),*(temp->pPolygon+y+1), + next_bucket,&min_face,face_id,ties); + else + Find_Adj_TallyEx(*(temp->pPolygon),*(temp->pPolygon+(size-1)), + next_bucket,&min_face,face_id,ties); + } + /* Maybe we can do better by triangulating the face, because + by triangulating the face we will go to a polygon of lesser + adjacencies + */ + if (size == 4) + { + /* Checking for a quad whether to do the whole polygon will + result in better performance because the triangles in the polygon + have less adjacencies + */ + Check_In_Quad(face_id,&test_face); + if (*next_bucket > test_face) + /* We can do better by going through the polygon */ + min_face = face_id; + } + + /* We have a polygon with greater than 4 sides, check to see if going + inside is better than going outside the polygon for the output edge. + */ + else + { + Check_In_Polygon(face_id,&test_face,size); + if (*next_bucket > test_face) + /* We can do better by going through the polygon */ + min_face = face_id; + } + + return (min_face); +} + + diff --git a/Tools/Stripe_w/sturcts.h b/Tools/Stripe_w/sturcts.h new file mode 100644 index 000000000..57490a695 --- /dev/null +++ b/Tools/Stripe_w/sturcts.h @@ -0,0 +1,36 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: sturcts.h +-----------------------------------------------------------------------*/ + +#define EVEN(x) (((x) & 1) == 0) + +int Get_Edge(int *edge1,int *edge2,int *index,int face_id, + int size, int id1, int id2); +void add_vert_id(); +void Update_Face(int *next_bucket, int *min_face, int face_id, int *e1, + int *e2,int temp1,int temp2,int *ties); +int Min_Adj(); +int Min_Face_Adj(int face_id, int *next_bucket, int *ties); +int Change_Face(int face_id,int in1,int in2, ListHead *pListHead, + P_ADJACENCIES temp, BOOL no_check); +void Delete_Adj(int id1, int id2,int *next_bucket,int *min_face, + int current_face,int *e1,int *e2,int *ties); +int Update_Adjacencies(int face_id, int *next_bucket, int *e1, int *e2, + int *ties); +int Get_Output_Edge(); +int Find_Face(); + + + + + + + diff --git a/Tools/Stripe_w/sturctsex.h b/Tools/Stripe_w/sturctsex.h new file mode 100644 index 000000000..6a4a76dbe --- /dev/null +++ b/Tools/Stripe_w/sturctsex.h @@ -0,0 +1,33 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE:sturctsex.h +-----------------------------------------------------------------------*/ + +#define EVEN(x) (((x) & 1) == 0) + +int Get_EdgeEx(int *edge1,int *edge2,int *index,int face_id, + int size, int id1, int id2); +void add_vert_idEx(); +void Update_FaceEx(int *next_bucket, int *min_face, int face_id, int *e1, + int *e2,int temp1,int temp2,int *ties); +int Min_Face_AdjEx(int face_id, int *next_bucket, int *ties); +int Change_FaceEx(int face_id,int in1,int in2, + ListHead *pListHead, P_ADJACENCIES temp, BOOL no_check); +void Delete_AdjEx(int id1, int id2,int *next_bucket,int *min_face, + int current_face,int *e1,int *e2,int *ties); +int Number_AdjEx(); +int Update_AdjacenciesEx(int face_id, int *next_bucket, int *e1, int *e2, + int *ties); + + + + + + diff --git a/Tools/Stripe_w/ties.c b/Tools/Stripe_w/ties.c new file mode 100644 index 000000000..e3fd31f78 --- /dev/null +++ b/Tools/Stripe_w/ties.c @@ -0,0 +1,304 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: ties.c + This file will contain all the routines used to determine the next face if there + is a tie +*/ +/*---------------------------------------------------------------------*/ + +#include <stdlib.h> +#include "polverts.h" +#include "ties.h" +#include "sturctsex.h" +#include "triangulatex.h" +#include "options.h" +#include "common.h" +#include "util.h" + +#define MAX_TIE 60 +int ties_array[60]; +int last = 0; + +void Clear_Ties() +{ + /* Clear the buffer, because we do not have the tie + any more that we had before */ + last = 0; +} + +void Add_Ties(int id) +{ + /* We have a tie to add to the buffer */ + ties_array[last++] = id; +} + +int Alternate_Tie() +{ + /* Alternate in what we choose to break the tie + We are just alternating between the first and + second thing that we found + */ + static int x = 0; + register int t; + + t = ties_array[x]; + x++; + if (x == 2) + x = 0; + return t; +} + +int Random_Tie() +{ + /* Randomly choose the next face with which + to break the tie + */ + register int num; + + num = rand(); + while (num >= last) + num = num/20; + return (ties_array[num]); +} + +int Look_Ahead(int id) +{ + /* Look ahead at this face and save the minimum + adjacency of all the faces that are adjacent to + this face. + */ + return Min_Adj(id); +} + +int Random_Look(int id[],int count) +{ + /* We had a tie within a tie in the lookahead, + break it randomly + */ + register int num; + + num = rand(); + while (num >= count) + num = num/20; + return (id[num]); +} + + +int Look_Ahead_Tie() +{ + /* Look ahead and find the face to go to that + will give the least number of adjacencies + */ + int id[60],t,x,f=0,min = 60; + + for (x = 0; x < last; x++) + { + t = Look_Ahead(ties_array[x]); + /* We have a tie */ + if (t == min) + id[f++] = ties_array[x]; + if (t < min) + { + f = 0; + min = t; + id[f++] = ties_array[x]; + } + } + /* No tie within the tie */ + if ( f == 1) + return id[0]; + /* Or ties, but we are at the end of strips */ + if (min == 0) + return id[0]; + return (Random_Look(id,f)); +} + + +int Sequential_Tri(int *index) +{ + /* We have a triangle and need to break the ties at it. + We will choose the edge that is sequential. There + is definitely one since we know we have a triangle + and that there is a tie and there are only 2 edges + for the tie. + */ + int e1,e2,e3,output1,output2,output3,output4; + + /* e2 and e3 are the input edge to the triangle */ + Last_Edge(&e1,&e2,&e3,0); + + if ((e2 == 0) && (e3 == 0)) + /* Starting the strip, don't need to do this */ + return ties_array[0]; + + /* For the 2 ties find the edge adjacent to face id */ + Get_EdgeEx(&output1,&output2,index,ties_array[0],3,0,0); + Get_EdgeEx(&output3,&output4,index,ties_array[1],3,0,0); + + if ((output1 == e3) || (output2 == e3)) + return ties_array[0]; + if ((output3 == e3) || (output4 == e3)) + return ties_array[1]; + printf("There is an error trying to break sequential triangle \n"); +} + +int Sequential_Quad(int *index, int triangulate) +{ + /* We have a quad that need to break its ties, we will try + and choose a side that is sequential, otherwise use lookahead + */ + int output1,output2,x,e1,e2,e3; + + /* e2 and e3 are the input edge to the quad */ + Last_Edge(&e1,&e2,&e3,0); + + /* No input edge */ + if ((e2 == 0) && (e3 == 0)) + return ties_array[0]; + + /* Go through the ties and see if there is a sequential one */ + for (x = 0; x < last; x++) + { + Get_EdgeEx(&output1,&output2,index,ties_array[x],4,0,0); + /* Partial and whole triangulation will have different requirements */ + if (((output1 == e3) || (output2 == e3)) && (triangulate == PARTIAL)) + return ties_array[x]; + if (((output1 != e3) && (output1 != e2) && + (output2 != e3) && (output2 != e2))) + return ties_array[x]; + } + /* There was not a tie that was sequential */ + return Look_Ahead_Tie(); +} + +void Whole_Output(int in1,int in2, int *index, int size, int *out1, int *out2) +{ + /* Used to sequentially break ties in the whole triangulation for polygons + greater than 4 sides. We will find the output edge that is good + for sequential triangulation. + */ + + int half; + + /* Put the input edge first in the list */ + Rearrange_IndexEx(index,size); + + if (!(EVEN(size))) + { + if (*(index) == in1) + half = size/2 ; + else + half = size/2 +1; + } + else + half = size/2; + + *out1 = *(index+half); + *out2 = *(index+half+1); +} + +int Sequential_Poly(int size, int *index, int triangulate) +{ + /* We have a polygon of greater than 4 sides and wish to break the + tie in the most sequential manner. + */ + + int x,output1,output2,e1,e2,e3,saved1=-1,saved2=-1,output3,output4; + + /* e2 and e3 are the input edge to the quad */ + Last_Edge(&e1,&e2,&e3,0); + + /* If we are using whole, find the output edge that is sequential */ + if (triangulate == WHOLE) + Whole_Output(e2,e3,index,size,&output3,&output4); + + /* No input edge */ + if ((e2 == 0) && (e3 == 0)) + return ties_array[0]; + + for (x = 0; x < last ; x++) + { + Get_EdgeEx(&output1,&output2,index,ties_array[x],size,0,0); + /* Partial that can be removed in just one triangle */ + if (((output1 == e3) || (output2 == e3)) && (triangulate == PARTIAL)) + saved1 = ties_array[x]; + /* Partial removed in more than one triangle */ + if ((output1 != e3) && (output1 != e2) && (output2 != e3) && (output2 != e2) && + (triangulate == PARTIAL) && (saved2 != -1)) + saved2 = ties_array[x]; + /* Whole is not so easy, since the whole polygon must be done. Given + an input edge there is only one way to come out, approximately half + way around the polygon. + */ + if (((output1 == output3) && (output2 == output4)) || + ((output1 == output4) && (output2 == output3)) && + (triangulate == WHOLE)) + return ties_array[x]; + } + + if (saved1 != -1) + return saved1; + if (saved2 != -1) + return saved2; + + /* There was not a tie that was sequential */ + return Look_Ahead_Tie(); +} + +int Sequential_Tie(int face_id, int triangulate) +{ + /* Break the tie by choosing the face that will + not give us a swap and is sequential. If there + is not one, then do the lookahead to break the + tie. + */ + /* Separate into 3 cases for simplicity, if the current + polygon has 3 sides, 4 sides or if the sides were + greater. We can do the smaller cases faster, so that + is why I separated the cases. + */ + + ListHead *pListFace; + PF_FACES face; + + /* Get the polygon with id face_id */ + pListFace = PolFaces[face_id]; + face = (PF_FACES) PeekList(pListFace,LISTHEAD,0); + + if (face->nPolSize == 3) + return(Sequential_Tri(face->pPolygon)); + if (face->nPolSize == 4) + return(Sequential_Quad(face->pPolygon,triangulate)); + else + return(Sequential_Poly(face->nPolSize,face->pPolygon,triangulate)); + +} + +int Get_Next_Face(int t, int face_id, int triangulate) +{ + /* Get the next face depending on what + the user specified + */ + + /* Did not have a tie, don't do anything */ + if (last == 1) + return(ties_array[0]); + if (t == RANDOM) + return Random_Tie(); + if (t == ALTERNATE) + return Alternate_Tie(); + if (t == LOOK) + return Look_Ahead_Tie(); + if (t == SEQUENTIAL) + return Sequential_Tie(face_id,triangulate); + + printf("Illegal option specified for ties, using first \n"); + return (ties_array[0]); +} diff --git a/Tools/Stripe_w/ties.h b/Tools/Stripe_w/ties.h new file mode 100644 index 000000000..502aabf4d --- /dev/null +++ b/Tools/Stripe_w/ties.h @@ -0,0 +1,15 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: ties.h +-----------------------------------------------------------------------*/ + +void Clear_Ties(); +void Add_Ties(int id); +int Get_Next_Face(int t, int face_id, int triangulate); diff --git a/Tools/Stripe_w/triangulate.h b/Tools/Stripe_w/triangulate.h new file mode 100644 index 000000000..1f677430a --- /dev/null +++ b/Tools/Stripe_w/triangulate.h @@ -0,0 +1,27 @@ + +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: triangulate.h +-----------------------------------------------------------------------*/ + +void Blind_Triangulate(int size, int *index, FILE *output, + BOOL begin, int where ,int color1,int color2, + int color3); +void Non_Blind_Triangulate(int size,int *index, FILE *output, + int next_face_id,int face_id,int where, + int color1,int color2,int color3); +int Adjacent(int id2,int id1, int *list, int size); +void Delete_From_List(int id,int *list, int *size); +void Triangulate_Polygon(int out_edge1, int out_edge2, int in_edge1, + int in_edge2, int size, int *index, + FILE *output, int reversed, int face_id, + int where, int color1, int color2, int color3); +void Rearrange_Index(int *index, int size); +void Find_Local_Strips(); diff --git a/Tools/Stripe_w/triangulatex.h b/Tools/Stripe_w/triangulatex.h new file mode 100644 index 000000000..3b2d8fb5c --- /dev/null +++ b/Tools/Stripe_w/triangulatex.h @@ -0,0 +1,28 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: triangulatex.h +-----------------------------------------------------------------------*/ + +enum swap_type +{ ON, OFF}; + +void SGI_StripEx(); +void Blind_TriangulateEx(int size, int *index, FILE *fp, FILE *output, + BOOL begin, int where ); +void Non_Blind_TriangulateEx(int size,int *index, FILE *fp, FILE *output, + int next_face_id,int face_id,int where); +int AdjacentEx(int id2,int id1, int *list, int size); +void Delete_From_ListEx(int id,int *list, int size); +void Triangulate_PolygonEx(int out_edge1,int out_edge2,int in_edge1, + int in_edge2,int size,int *index, + FILE *output,FILE *fp,int reversed,int face_id, + int where); +void Rearrange_IndexEx(int *index, int size); +void Find_StripsEx(); diff --git a/Tools/Stripe_w/util.c b/Tools/Stripe_w/util.c new file mode 100644 index 000000000..f17fe5f7c --- /dev/null +++ b/Tools/Stripe_w/util.c @@ -0,0 +1,272 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: util.c + This file contains routines that are used for various functions +*/ +/*---------------------------------------------------------------------*/ + + +#include <stdlib.h> +#include "polverts.h" + +void switch_lower (int *x, int *y) +{ + register int temp; + + /* Put lower value in x */ + if (*y < *x) + { + temp = *x; + *x = *y; + *y = temp; + } +} + +BOOL member(int x , int id1, int id2, int id3) +{ + /* Is x in the triangle specified by id1,id2,id3 */ + if ((x != id1) && (x != id2) && (x != id3)) + return FALSE; + return TRUE; +} + + +int Compare (P_ADJACENCIES node1, P_ADJACENCIES node2) +{ + /* This will only return whether 2 adjacency nodes + are equivalent. + */ + if (node1->face_id == node2->face_id) + return TRUE; + else + return FALSE; +} + + +BOOL Exist(int face_id, int id1, int id2) +{ + /* Does the edge specified by id1 and id2 exist in this + face currently? Maybe we deleted in partial triangulation + */ + ListHead *pListHead; + PF_FACES temp; + register int x,size; + BOOL a=FALSE,b =FALSE; + + pListHead = PolFaces[face_id]; + temp = ( PF_FACES ) PeekList( pListHead, LISTHEAD, 0 ); + size = temp->nPolSize; + for (x=0; x<size; x++) + { + if (*(temp->pPolygon+x) == id1) + a = TRUE; + if (*(temp->pPolygon+x) == id2) + b = TRUE; + if (a && b) + return TRUE; + } + return FALSE; +} + +int Get_Next_Id(int *index,int e3, int size) +{ + /* Return the id following e3 in the list of vertices */ + + register int x; + + for (x = 0; x< size; x++) + { + if ((*(index+x) == e3) && (x != (size-1))) + return *(index+x+1); + else if (*(index+x) == e3) + return *(index); + } + printf("There is an error in the next id\n"); + exit(0); +} + +int Different (int id1,int id2,int id3,int id4,int id5, int id6, int *x, int *y) +{ + /* Find the vertex in the first 3 numbers that does not exist in + the last three numbers + */ + if ((id1 != id4) && (id1 != id5) && (id1 != id6)) + { + *x = id2; + *y = id3; + return id1; + } + if ((id2 != id4) && (id2 != id5) && (id2 != id6)) + { + *x = id1; + *y = id3; + return id2; + } + if ((id3 != id4) && (id3 != id5) && (id3 != id6)) + { + *x = id1; + *y = id2; + return id3; + } + + /* Because there are degeneracies in the data, this might occur */ + *x = id5; + *y = id6; + return id4; +} + +int Return_Other(int *index,int e1,int e2) +{ + /* We have a triangle and want to know the third vertex of it */ + register int x; + + for (x=0;x<3;x++) + { + if ((*(index+x) != e1) && (*(index+x) != e2)) + return *(index+x); + } + /* If there is a degenerate triangle return arbitrary */ + return e1; +} + +int Get_Other_Vertex(int id1,int id2,int id3,int *index) +{ + /* We have a list index of 4 numbers and we wish to + return the number that is not id1,id2 or id3 + */ + register int x; + + for (x=0; x<4; x++) + { + if ((*(index+x) != id1) && (*(index+x) != id2) && + (*(index+x) != id3)) + return *(index+x); + } + /* If there is some sort of degeneracy this might occur, + return arbitrary + */ + if (x==4) + return id1; +} + + +PLISTINFO Done(int face_id, int size, int *bucket) +{ + /* Check to see whether the polygon with face_id was used + already, return NULL if it was, otherwise return a pointer to the face. + */ + P_ADJACENCIES pfNode; + register int y; + PLISTINFO lpListInfo; + + pfNode = (P_ADJACENCIES) malloc(sizeof(ADJACENCIES) ); + if ( pfNode ) + pfNode->face_id = face_id; + + for (y=size; ; y--) + { + lpListInfo = SearchList(array[y], pfNode, + (int (*)(void *,void *)) (Compare)); + if (lpListInfo != NULL) + { + *bucket = y; + return lpListInfo; + } + if (y == 0) + /* This adjacent face was done already */ + return lpListInfo; + } + free (pfNode); +} + +void Output_Edge(int *index,int e2,int e3,int *output1,int *output2) +{ + /* Given a quad and an input edge return the other 2 vertices of the + quad. + */ + + *output1 = -1; + *output2 = -1; + + if ((*(index) != e2) && (*(index) != e3)) + *output1 = *(index); + + if ((*(index+1) != e2) && (*(index+1) != e3)) + { + if (*output1 == -1) + *output1 = *(index+1); + else + { + *output2 = *(index+1); + return; + } + } + + if ((*(index+2) != e2) && (*(index+2) != e3)) + { + if (*output1 == -1) + *output1 = *(index+2); + else + { + *output2 = *(index+2); + return; + } + } + + *output2 = *(index+3); +} + + +void First_Edge(int *id1,int *id2, int *id3) +{ + /* Get the first triangle in the strip we just found, we will use this to + try to extend backwards in the strip + */ + + ListHead *pListHead; + register int num; + P_STRIPS temp1,temp2,temp3; + + pListHead = strips[0]; + num = NumOnList(pListHead); + + /* Did not have a strip */ + if (num < 3) + return; + + temp1 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 0); + temp2 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 1); + temp3 = ( P_STRIPS ) PeekList( pListHead, LISTHEAD, 2); + *id1 = temp1->face_id; + *id2 = temp2->face_id; + *id3 = temp3->face_id; + +} + +void Last_Edge(int *id1, int *id2, int *id3, BOOL save) +{ + /* We need the last edge that we had */ + static int v1, v2, v3; + + if (save) + { + v1 = *id1; + v2 = *id2; + v3 = *id3; + } + else + { + *id1 = v1; + *id2 = v2; + *id3 = v3; + } +} + + diff --git a/Tools/Stripe_w/util.h b/Tools/Stripe_w/util.h new file mode 100644 index 000000000..64c2f2334 --- /dev/null +++ b/Tools/Stripe_w/util.h @@ -0,0 +1,24 @@ +/********************************************************************/ +/* STRIPE: converting a polygonal model to triangle strips + Francine Evans, 1996. + SUNY @ Stony Brook + Advisors: Steven Skiena and Amitabh Varshney +*/ +/********************************************************************/ + +/*---------------------------------------------------------------------*/ +/* STRIPE: util.h +-----------------------------------------------------------------------*/ + +void switch_lower (int *x, int *y); +int Compare (P_ADJACENCIES node1, P_ADJACENCIES node2); +BOOL Exist(int face_id, int id1, int id2); +int Get_Next_Id(int *index,int e3, int size); +int Different(int id1,int id2,int id3,int id4,int id5, int id6, int *x, int *y); +int Return_Other(int *index,int e1,int e2); +int Get_Other_Vertex(int id1,int id2,int id3,int *index); +PLISTINFO Done(int face_id, int size, int *bucket); +void Output_Edge(int *index,int e2,int e3,int *output1,int *output2); +void Last_Edge(int *id1, int *id2, int *id3, BOOL save); +void First_Edge(int *id1,int *id2, int *id3); +BOOL member(int x , int id1, int id2, int id3); diff --git a/Tools/Tools/Makefile.am b/Tools/Tools/Makefile.am new file mode 100644 index 000000000..034024ed4 --- /dev/null +++ b/Tools/Tools/Makefile.am @@ -0,0 +1,15 @@ +EXTRA_DIST = process-dem.pl scenery_version.hxx + +SUBDIRS = \ + Lib \ + Prep \ + Construct \ + Utils \ + Areas \ + AssemTris \ + FixObj \ + SplitTris \ + Stripe_w \ + Tri2obj + +bin_SCRIPTS = process-dem.pl diff --git a/Tools/Tools/README b/Tools/Tools/README new file mode 100644 index 000000000..253e47e04 --- /dev/null +++ b/Tools/Tools/README @@ -0,0 +1,75 @@ +FG Scenery Tools README +======================= + +Contained here-in are the FG scenery creation tools. These can be +used to convert 3 arcsec ASCII format DEM files and 30 arcsec binary +format DEM files into Flight Gear scenery. + +Eventually these tools will expand to support insertion of airports, +roads, rivers, lakes, etc. + + +Building the Tools +================== + +These tools are compiled and tested under Linux. I'm all for +portability, but I just haven't been as motivated to port these tools, +since scenery creation is less of a general need ... especially at +this stage. However, if anyone wants to work on porting to other +platforms, I will be happy to incorporate patches. + +The process for building these tools is very similar to building the +main FG source code. + +1. Set the FG_ROOT, FG_ROOT_SRC, and FG_ROOT_LIB environment + variables. + +2. Run ``make depend'' + +3. Run ``make clean'' + +4. Run ``make'' + + +3 Arcsec ASCII DEM files +======================== + +Data files for the USA are available in this format from: + + http://edcwww.cr.usgs.gov/doc/edchome/ndcdb/ndcdb.html + +To generate FG scenery from one of these dem files, run: + + ./process-dem.pl <error-tolerance-squared> dem-file-1 [ dem-file-2 ...] + +You can vary the error tolerance to control the level of detail (and +size) of the resulting scenery. Note, you must specify the error +tolerance squared. So, if you wish to allow up to a 10 meter error +margin (very high level of detail) you would specify a value of 100. +If you desire an error tolerance of 200 meters (medium detail level) +you would specify a value of 40000. + +The process-dem.pl script will automatically dump the resulting .obj +files in the proper directory tree. + + +30 Arcsec Binary DEM files +========================== + +These data files have world wide coverage and are available from: + + http://edcwww.cr.usgs.gov/landdaac/gtopo30/gtopo30.html + +To process these data files, you must first run: + + DemRaw2Ascii/raw2ascii <input_file_basename> <output_dir> + +For example: + + DemRaw2Ascii/raw2ascii /tmp/W020N90 asciidems/ + +This will create ASCII DEM files for each 1 degree x 1 degree area in +the specified output dir. + +Then, you can take these ascii dem files and feed them through the +same procedure you use with the 3 arcsec dem files. \ No newline at end of file diff --git a/Tools/Tools/Todo b/Tools/Tools/Todo new file mode 100644 index 000000000..d056dfc03 --- /dev/null +++ b/Tools/Tools/Todo @@ -0,0 +1,37 @@ +-------------------------------------------------------------------------- +| Done +-------------------------------------------------------------------------- + +4/6/98 - fix 30 arcsec dem file processing + +4/6/98 - incorporate autoconf/automake build system + +1/10/98 - Split areas into smaller tiles + +1/14/98 - Don't create shared corners or edges if one already exists. + +1/14/98 - Reassemble triangles using only body, shared corners, and + shared edges. + +1/19/98 - Retro-fit tri2obj to use shared normals rather than regenerating + normals for shared vertices. + + +-------------------------------------------------------------------------- +| Todo +-------------------------------------------------------------------------- + +1/12/98 - Try reversing cw-wound strips rather than calling glFrontFace() + in the display list. + + gnuplot> set label "1" at 1,1 + gnuplot> set label "2" at 2,2 + + gnuplot> plot x + +1/21/98 - Generate an elevation quad tree. + +1/12/98 - Generate a face adjacency matrix + +1/21/98 - Remove internal shared edges and corners that are not needed + after an area is generated. diff --git a/Tools/Tools/process-dem.pl b/Tools/Tools/process-dem.pl new file mode 100755 index 000000000..5a92c5bb5 --- /dev/null +++ b/Tools/Tools/process-dem.pl @@ -0,0 +1,655 @@ +#!/usr/bin/perl + +#--------------------------------------------------------------------------- +# Toplevel script to automate DEM file processing and conversion +# +# Written by Curtis Olson, started January 1998. +# +# Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +# format version number +$scenery_format_version = "0.1"; + +$max_area = 10000; # maximum triangle area +$remove_tmps = 1; + +$| = 1; # flush buffers after every write + +$do_dem2node = 1; +$do_triangle_1 = 1; +$do_fixnode = 1; +$do_splittris = 1; +$do_assemtris = 1; +$do_triangle_2 = 1; + +$do_tri2obj = 1; +$do_strips = 1; +$do_fixobj = 1; + +$do_install = 1; + + +if ( $#ARGV < 3 ) { + die "Usage: $0 <fg-root-dir> <work-dir> <error^2> dem-file(s)\n"; +} + +# Start with file.dem + +$fg_root = shift(@ARGV); +$work_dir = shift(@ARGV); +$error = shift(@ARGV); +$error += 0.0; +$system_name = `uname -s`; chop($system_name); + +while ( $dem_file = shift(@ARGV) ) { + print "Source file = $dem_file Error tolerance = $error\n"; + + if ( $error < 0.5 ) { + die "I doubt you'll be happy with an error tolerance as " . + "low as $error.\n"; + } + + + if ( $do_dem2node ) { + dem2node() ; + } else { + $subdir = "./work/Scenery/w120n030/w111n033"; + print "WARNING: Hardcoding subdir = $subdir\n"; + } + + triangle_1() if ( $do_triangle_1 ); + fixnode() if ( $do_fixnode ); + splittris() if ( $do_splittris ); + assemtris() if ( $do_assemtris ); + triangle_2() if ( $do_triangle_2); + tri2obj() if ( $do_tri2obj ); + strips() if ( $do_strips ); + fixobj() if ( $do_fixobj ); + install() if ( $do_install ); +} + + +# exit normally +exit(0); + + +# replace all unix forward slashes with windoze backwards slashes if +# running on a cygwin32 system +sub fix_slashes { my($in) = @_; + if ( $system_name =~ m/CYGWIN32/ ) { + $in =~ s/\/+/\//g; + $in =~ s/\//\\/g; + } + + return($in); +} + + +# return the file name root (ending at last ".") +sub file_root { + my($file) = @_; + my($pos); + + $pos = rindex($file, "."); + return substr($file, 0, $pos); +} + + +# 1. dem2node work_dir dem_file tolerance^2 (meters) +# +# - dem2node .. dem_file 160000 +# +# splits dem file into 64 file.node's which contain the +# irregularly fitted vertices + +sub dem2node { + $command = "Dem2node/dem2node $work_dir $dem_file $error"; + $command = fix_slashes($command); + print "Running '$command'\n"; + + open(OUT, "$command |"); + while ( <OUT> ) { + print $_; + if ( m/^Dir = / ) { + $subdir = $_; + $subdir =~ s/^Dir = //; + chop($subdir); + } + if ( m/Quad name field/ ) { + $quad_name = $_; + # strip header + $quad_name =~ s/.*Quad name field: //; + # crunch consequetive spaces + $quad_name =~ s/ +/ /g; + chop($quad_name); + print "QUAD NAME = $quad_name\n"; + } + } + close(OUT); +} + + +# 2. triangle -q file (Takes file.node and produces file.1.node and +# file.1.ele) + +print "Subdirectory for this dem file is $subdir\n"; + +sub triangle_1 { + @FILES = `ls $subdir`; + foreach $file ( @FILES ) { + # print $file; + chop($file); + if ( ($file =~ m/\.node$/) && ($file !~ m/\.\d\.node$/) ) { + # special handling is needed if .poly file exists + $fileroot = $file; + $fileroot =~ s/\.node$//; + print "$subdir/$fileroot\n"; + $command = "Triangle/triangle"; + $command = fix_slashes($command); + if ( -r "$subdir/$fileroot.poly" ) { + $command .= " -pc"; + } + $command .= " -a$max_area -q10 $subdir/$file"; + print "Running '$command'\n"; + open(OUT, "$command |"); + while ( <OUT> ) { + print $_; + } + close(OUT); + + # remove input file.node + if ( $remove_tmps ) { + $file1 = "$subdir/$file"; + $file1 = fix_slashes($file1); + unlink($file1); + } + } + } +} + + +# 3. fixnode file.dem subdir +# +# Take the original .dem file (for interpolating Z values) and the +# subdirecotry containing all the file.1.node's and replace with +# fixed file.1.node + +sub fixnode { + $command = "FixNode/fixnode"; + $command = fix_slashes($command); + $command .= " $dem_file $subdir"; + print "Running '$command'\n"; + open(OUT, "$command |") || die "cannot run command\n"; + while ( <OUT> ) { + print $_; + } + close(OUT); +} + + +# 4.1 splittris file (.1.node) (.1.ele) + +# Extract the corner, edge, and body vertices (in original +# geodetic coordinates) and normals (in cartesian coordinates) and +# save them in something very close to the .obj format as file.se, +# file.sw, file.nw, file.ne, file.north, file.south, file.east, +# file.west, and file.body. This way we can reconstruct the +# region using consistant edges and corners. + +# Arbitration rules: If an opposite edge file already exists, +# don't create our matching edge. If a corner already exists, +# don't create ours. Basically, the early bird gets the worm and +# gets to define the edge verticies and normals. All the other +# adjacent tiles must use these. + +sub splittris { + @FILES = `ls $subdir`; + foreach $file ( @FILES ) { + chop($file); + if ( $file =~ m/\.1\.node$/ ) { + $file =~ s/\.node$//; # strip off the ".node" + + $command = "SplitTris/splittris"; + $command = fix_slashes($command); + $command .= " $subdir/$file"; + print "Running '$command'\n"; + open(OUT, "$command |"); + while ( <OUT> ) { + print $_; + } + close(OUT); + + if ( $remove_tmps ) { + $file1 = "$subdir/$file.node"; + $file1 = fix_slashes($file1); + unlink($file1); + $file1 = "$subdir/$file.node.orig"; + $file1 = fix_slashes($file1); + unlink($file1); + $file1 = "$subdir/$file.ele"; + $file1 = fix_slashes($file1); + unlink($file1); + } + } + } +} + + +# 4.2 read in the split of version of the tiles, reconstruct the tile +# using the proper shared corners and edges. Save as a node file +# so we can retriangulate. + +sub assemtris { + @FILES = `ls $subdir`; + foreach $file ( @FILES ) { + chop($file); + if ( $file =~ m/\.1\.body$/ ) { + $file =~ s/\.1\.body$//; # strip off the ".body" + + $command = "AssemTris/assemtris"; + $command = fix_slashes($command); + $command .= " $subdir/$file"; + print "Running '$command'\n"; + open(OUT, "$command |"); + while ( <OUT> ) { + print $_; + } + close(OUT); + } + if ( $remove_tmps ) { + $file1 = "$subdir/$file.body"; + $file1 = fix_slashes($file1); + unlink($file1); + } + } +} + + +# 4.3 Retriangulate reassembled files (without -q option) so no new +# nodes are generated. + +sub triangle_2 { + @FILES = `ls $subdir`; + foreach $file ( @FILES ) { + # print $file; + chop($file); + if ( ($file =~ m/\.node$/) && ($file !~ m/\.\d\.node$/) ) { + $base = $file; + $base =~ s/\.node$//; + print("Test for $subdir/$base.q\n"); + + $command = "Triangle/triangle"; + $command = fix_slashes($command); + + if ( -r "$subdir/$base.q" ) { + # if triangle hangs, we can create a filebase.q for + # the file it hung on. Then, we test for that file + # here which causes the incremental algorithm to run + # (which shouldn't ever hang.) + $command .= " -i"; + } + + if ( -r "$subdir/$base.poly" ) { + $command .= " -pc $subdir/$base"; + } else { + $command .= " $subdir/$file"; + } + + print "Running '$command'\n"; + open(OUT, "$command |"); + while ( <OUT> ) { + print $_; + } + close(OUT); + + # remove input file.node + if ( $remove_tmps ) { + $file1 = "$subdir/$file"; + $file1 = fix_slashes($file1); + unlink($file1); + } + } + } +} + + +# 5. tri2obj file (.1.node) (.1.ele) +# +# Take the file.1.node and file.1.ele and produce file.1.obj +# +# Extracts normals out of the shared edge/vertex files, and uses +# the precalcuated normals for these nodes instead of calculating +# new ones. By sharing normals as well as vertices, not only are +# the gaps between tiles eliminated, but the colors and lighting +# transition smoothly across tile boundaries. + +sub tri2obj { + @FILES = `ls $subdir`; + foreach $file ( @FILES ) { + chop($file); + if ( $file =~ m/\.1\.node$/ ) { + $file =~ s/\.node$//; # strip off the ".node" + + $command = "Tri2obj/tri2obj"; + $command = fix_slashes($command); + $command .= " $subdir/$file"; + print "Running '$command'\n"; + open(OUT, "$command |"); + while ( <OUT> ) { + print $_; + } + close(OUT); + + if ( $remove_tmps ) { + $file1 = "$subdir/$file.node"; + $file1 = fix_slashes($file1); + unlink($file1); + $file1 = "$subdir/$file.node.orig"; + $file1 = fix_slashes($file1); + unlink($file1); + $file1 = "$subdir/$file.ele"; + $file1 = fix_slashes($file1); + unlink($file1); + } + } + } +} + + +# 6. strip file.1.obj +# +# Strip the file.1.obj's. Note, strips doesn't handle the minimal +# case of striping a square correctly. +# +# 7. cp stripe.objf file.2.obj +# +# strips produces a file called "stripe.objf" ... copy this to file.2.obj + +sub strips { + @FILES = `ls $subdir`; + foreach $file ( @FILES ) { + chop($file); + if ( $file =~ m/\.1\.obj$/ ) { + $newfile = $file; + $newfile =~ s/\.1\.obj$//; + $command = "Stripe_w/strips"; + $command = fix_slashes($command); + $command .= " $subdir/$file $subdir/$newfile.2.obj"; + print "Running '$command'\n"; + # $input = <STDIN>; + open(OUT, "$command |"); + while ( <OUT> ) { + print $_; + } + close(OUT); + + # copy to destination file + # $newfile = $file; + # $newfile =~ s/\.1\.obj$//; + # print "Copying to $subdir/$newfile.2.obj\n"; + # open(IN, "<bands.d"); + # open(IN, "<stripe.objf"); + # open(OUT, ">$subdir/$newfile.2.obj"); + # while ( <IN> ) { + # print OUT $_; + # } + # close(IN); + # close(OUT); + + if ( $remove_tmps ) { + $file1 = "$subdir/$file"; + $file1 = fix_slashes($file1); + unlink($file1); + } + } + } +} + + +# 8. fixobj file-new +# +# Sort file.2.obj by strip winding + +sub fixobj { + @FILES = `ls $subdir`; + foreach $file ( @FILES ) { + chop($file); + if ( $file =~ m/\.2\.obj$/ ) { + $newfile = $file; + $newfile =~ s/\.2\.obj$/.obj/; + + $command = "FixObj/fixobj"; + $command = fix_slashes($command); + $command .= " $subdir/$file $subdir/$newfile"; + print "Running '$command'\n"; + open(OUT, "$command |"); + while ( <OUT> ) { + print $_; + } + close(OUT); + + if ( $remove_tmps ) { + $file1 = "$subdir/$file"; + $file1 = fix_slashes($file1); + unlink($file1); + } + } + } +} + + +# 9. install +# +# rename, compress, and install scenery files + +sub install { + $tmp = $subdir; + $tmp =~ s/$work_dir//; + # print "Temp dir = $tmp\n"; + $install_dir = "$fg_root/$tmp"; + + # try to get rid of double // + $install_dir =~ s/\/+/\//g; + print "Install dir = $install_dir\n"; + + if ( $system_name !~ m/CYGWIN32/ ) { + $command = "mkdir -p $install_dir"; + } else { + $command = "Makedir/makedir $install_dir"; + $command = fix_slashes($command); + } + + # print "Running '$command'\n"; + open(OUT, "$command |"); + while ( <OUT> ) { + print $_; + } + close(OUT); + + # write out version and info record + $version_file = "$install_dir/VERSION"; + open(VERSION, ">$version_file") || + die "Cannot open $version_file for writing\n"; + print VERSION "FGFS Scenery Version $scenery_format_version\n"; + if ( $system_name !~ m/CYGWIN32/ ) { + $date = `date`; chop($date); + } else { + # ??? + $date = "not available"; + } + $hostname = `hostname`; chop($hostname); + print VERSION "Creator = $ENV{LOGNAME}\n"; + print VERSION "Date = $date\n"; + print VERSION "Machine = $hostname\n"; + print VERSION "\n"; + print VERSION "DEM File Name = $dem_file\n"; + print VERSION "DEM Label = $quad_name\n"; + print VERSION "Error Tolerance = $error (this value is squared)\n"; + close(VERSION); + + @FILES = `ls $subdir`; + foreach $file ( @FILES ) { + chop($file); + if ( $file =~ m/\d\d.obj$/ ) { + $new_file = file_root($file); + + $command = + "gzip -c --best -v < $subdir/$file > $install_dir/$new_file.gz"; + $command = fix_slashes($command); + + print "Running '$command'\n"; + open(OUT, "$command |"); + while ( <OUT> ) { + print $_; + } + close(OUT); + + if ( $remove_tmps ) { + $file1 = "$subdir/$file"; + $file1 = fix_slashes($file1); + unlink($file1); + } + } elsif ( $file =~ m/\d\d.apt$/ ) { + $command = "cp $subdir/$file $install_dir/$file"; + $command = fix_slashes($command); + print "Running '$command'\n"; + open(OUT, "$command |"); + while ( <OUT> ) { + print $_; + } + close(OUT); + } + } +} + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.32 1998/11/20 01:02:55 curt +# Speedups for win32. +# +# Revision 1.31 1998/10/28 19:39:06 curt +# Changes to better support win32 scenery development. +# +# Revision 1.30 1998/10/22 22:00:10 curt +# Modified the info that is put in the VERSION file. +# +# Revision 1.29 1998/10/02 21:41:56 curt +# Added Makedir + fixes for win32. +# +# Revision 1.28 1998/09/17 18:40:15 curt +# Changes to allow multiple copies of the scenery processing tools +# to be run concurrently. +# +# Revision 1.27 1998/09/09 20:58:35 curt +# Fixes and tweaks to handle area cutouts for airports. +# +# Revision 1.26 1998/08/26 22:31:29 curt +# Write out version and "meta" info into each dem's subdirectory containing +# all the tiles. +# +# Revision 1.25 1998/07/22 21:46:09 curt +# minor tweaks. +# +# Revision 1.24 1998/07/21 04:33:47 curt +# More tweaks for sub-area cutouts. +# +# Revision 1.23 1998/07/20 12:55:35 curt +# Several tweaks to start incorporating area cutouts into the pipeline. +# +# Revision 1.22 1998/07/08 14:49:13 curt +# tweaks. +# +# Revision 1.21 1998/06/08 17:18:37 curt +# Mods to test new Stripe fixes from Wilbur Streett. +# +# Revision 1.20 1998/06/05 18:20:24 curt +# Added DemInfo to dump out "A" record DEM info. +# Modified process-dem.pl to work in a temp directory and compress/copy the +# result to the final destination. +# +# Revision 1.19 1998/05/27 02:25:26 curt +# Added a flag to the first run of "triangle" to impose a maximum triangle +# size. This forces really flat areas to be subdivided a certain amount +# anyways. This makes for slightly more interesting scenery. +# +# Revision 1.18 1998/05/20 20:55:40 curt +# Makefile tweaks +# +# Revision 1.17 1998/04/28 01:23:25 curt +# Added a work around so that we can get past the "triangle" program +# hanging, by aborting and rerunning with that tile marked to use the "-i" +# option. +# +# Revision 1.16 1998/04/18 03:57:53 curt +# Added zlib library support. +# +# Revision 1.15 1998/04/08 23:24:07 curt +# Adopted Gnu automake/autoconf system. +# +# Revision 1.14 1998/04/06 21:09:38 curt +# Additional win32 support. +# Fixed a bad bug in dem file parsing that was causing the output to be +# flipped about x = y. +# +# Revision 1.13 1998/03/19 02:52:52 curt +# Updated to reflect some minor tool reorganization and the creation of class +# to handle DEM processing needs. +# +# Revision 1.12 1998/03/19 01:48:35 curt +# Added gpc-2.01 (generic polygon clipping library) +# +# Revision 1.11 1998/03/03 03:36:57 curt +# Cumulative tweaks. +# +# Revision 1.10 1998/02/01 03:42:26 curt +# Modifications to handle compressed dem files. +# +# Revision 1.9 1998/01/27 18:36:54 curt +# Lots of updates to get back in sync with changes made over in .../Src/ +# +# Revision 1.8 1998/01/21 17:59:05 curt +# Uncomment lines to remove several intermediate files. +# +# Revision 1.7 1998/01/19 19:51:06 curt +# A couple final pre-release tweaks. +# +# Revision 1.6 1998/01/15 21:33:33 curt +# Assembling triangles and building a new .node file with the proper shared +# vertices now works. Now we just have to use the shared normals and we'll +# be all set. +# +# Revision 1.5 1998/01/15 02:50:08 curt +# Tweaked to add next stage. +# +# Revision 1.4 1998/01/14 15:55:34 curt +# Finished splittris, started assemtris. +# +# Revision 1.3 1998/01/14 02:15:52 curt +# Updated front end script to keep plugging away on tile fitting. +# +# Revision 1.2 1998/01/12 20:42:08 curt +# Working on fitting tiles together in a seamless manner. +# +# Revision 1.1 1998/01/09 23:06:46 curt +# Initial revision. +# diff --git a/Tools/Tools/scenery_version.hxx b/Tools/Tools/scenery_version.hxx new file mode 100644 index 000000000..c5616a646 --- /dev/null +++ b/Tools/Tools/scenery_version.hxx @@ -0,0 +1,31 @@ +// scenery_version.hxx -- Scenery file format version +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#define FG_SCENERY_FILE_FORMAT "0.2" + + +// $Log$ +// Revision 1.1 1999/03/25 19:13:35 curt +// Initial revision. +// diff --git a/Tools/Tri2obj/Makefile.am b/Tools/Tri2obj/Makefile.am new file mode 100644 index 000000000..c27fc320f --- /dev/null +++ b/Tools/Tri2obj/Makefile.am @@ -0,0 +1,71 @@ +#--------------------------------------------------------------------------- +# Makefile +# +# Written by Curtis Olson, started January 1998. +# +# Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ +# (Log is kept at end of this file) +#--------------------------------------------------------------------------- + + +bin_PROGRAMS = tri2obj + +tri2obj_SOURCES = tri2obj.cxx tri2obj.hxx + +tri2obj_LDADD = \ + $(top_builddir)/Lib/Bucket/libBucket.a \ + $(top_builddir)/Lib/Math/libMath.a \ + $(top_builddir)/Lib/Debug/libDebug.a \ + $(top_builddir)/Lib/zlib/libz.a \ + $(base_LIBS) + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib + + +#--------------------------------------------------------------------------- +# $Log$ +# Revision 1.7 1998/11/04 23:02:02 curt +# Changes to the automake/autoconf system to reduce the number of libraries +# that are unnecessarily linked into the various executables. +# +# Revision 1.6 1998/07/30 23:49:26 curt +# Removed libtool support. +# +# Revision 1.5 1998/07/08 14:49:14 curt +# tweaks. +# +# Revision 1.4 1998/04/24 00:44:07 curt +# Added zlib support. +# +# Revision 1.3 1998/04/18 04:01:29 curt +# Now use libMath rather than having local copies of math routines. +# +# Revision 1.2 1998/04/14 02:26:09 curt +# Code reorganizations. Added a Lib/ directory for more general libraries. +# +# Revision 1.1 1998/04/08 23:22:13 curt +# Adopted Gnu automake/autoconf system. +# +# Revision 1.2 1998/01/21 02:55:46 curt +# Incorporated new make system from Bob Kuehne <rpk@sgi.com>. +# +# Revision 1.1 1998/01/15 02:45:25 curt +# Initial revision. +# + diff --git a/Tools/Tri2obj/tri2obj.cxx b/Tools/Tri2obj/tri2obj.cxx new file mode 100644 index 000000000..a3401052c --- /dev/null +++ b/Tools/Tri2obj/tri2obj.cxx @@ -0,0 +1,695 @@ +// tri2obj.cxx -- read in a .ele/.node file pair generated by the triangle +// program and output a simple Wavefront .obj file. +// +// Written by Curtis Olson, started October 1997. +// +// Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include <stdio.h> +#include <stdlib.h> // for atoi() +#include <string.h> +#include <sys/stat.h> // for stat() +#include <unistd.h> // for stat() + +#include "tri2obj.hxx" + +#include <Include/fg_constants.h> +#include <Bucket/bucketutils.h> + +#include <Math/fg_geodesy.hxx> +#include <Math/mat3.h> +#include <Math/polar3d.hxx> + + +int nodecount, tricount; +int normalcount = 0; +static Point3D nodes[MAX_NODES]; +static int tris[MAX_TRIS][3]; + +static double normals[MAX_NODES][3]; + +fgBUCKET my_index; +fgBUCKET ne_index, nw_index, sw_index, se_index; +fgBUCKET north_index, south_index, east_index, west_index; + + +// given three points defining a triangle, calculate the normal +void calc_normal(const Point3D& p1, const Point3D& p2, + const Point3D& p3, double normal[3]) +{ + double v1[3], v2[3]; + double temp; + + v1[0] = p2.x() - p1.x(); v1[1] = p2.y() - p1.y(); v1[2] = p2.z() - p1.z(); + v2[0] = p3.x() - p1.x(); v2[1] = p3.y() - p1.y(); v2[2] = p3.z() - p1.z(); + + MAT3cross_product(normal, v1, v2); + MAT3_NORMALIZE_VEC(normal, temp); + +// printf(" Normal = %.2f %.2f %.2f\n", normal[0], normal[1], normal[2]); +} + + +// return the index of all triangles containing the specified node +void find_tris(int n, int *t1, int *t2, int *t3, int *t4, int *t5) { + int i; + + *t1 = *t2 = *t3 = *t4 = *t5 = 0; + + i = 1; + while ( i <= tricount ) { + if ( (n == tris[i][0]) || (n == tris[i][1]) || (n == tris[i][2]) ) { + if ( *t1 == 0 ) { + *t1 = i; + } else if ( *t2 == 0 ) { + *t2 = i; + } else if ( *t3 == 0 ) { + *t3 = i; + } else if ( *t4 == 0 ) { + *t4 = i; + } else { + *t5 = i; + } + } + i++; + } +} + + +// return the file base name ( foo/bar/file.ext = file.ext ) +void extract_file(char *in, char *base) { + int len, i; + + len = strlen(in); + + i = len - 1; + while ( (i >= 0) && (in[i] != '/') ) { + i--; + } + + in += (i + 1); + strcpy(base, in); +} + + +// return the file path name ( foo/bar/file.ext = foo/bar ) +void extract_path(char *in, char *base) { + int len, i; + + len = strlen(in); + strcpy(base, in); + + i = len - 1; + while ( (i >= 0) && (in[i] != '/') ) { + i--; + } + + base[i] = '\0'; +} + + +// check if a file exists +int file_exists(char *file) { + struct stat stat_buf; + int result; + + printf("checking %s ... ", file); + + result = stat(file, &stat_buf); + + if ( result != 0 ) { + // stat failed, no file + printf("not found.\n"); + return(0); + } else { + // stat succeeded, file exists + printf("exists.\n"); + return(1); + } +} + + +// check to see if a shared object exists +int shared_object_exists(char *basepath, char *ext, char *file) { + char scene_path[256]; + long int index; + + if ( strcmp(ext, ".sw") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&west_index, scene_path); + index = fgBucketGenIndex(&west_index); + sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&sw_index, scene_path); + index = fgBucketGenIndex(&sw_index); + sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&south_index, scene_path); + index = fgBucketGenIndex(&south_index); + sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".se") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&east_index, scene_path); + index = fgBucketGenIndex(&east_index); + sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&se_index, scene_path); + index = fgBucketGenIndex(&se_index); + sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&south_index, scene_path); + index = fgBucketGenIndex(&south_index); + sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".ne") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&east_index, scene_path); + index = fgBucketGenIndex(&east_index); + sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&ne_index, scene_path); + index = fgBucketGenIndex(&ne_index); + sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&north_index, scene_path); + index = fgBucketGenIndex(&north_index); + sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".nw") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&west_index, scene_path); + index = fgBucketGenIndex(&west_index); + sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&nw_index, scene_path); + index = fgBucketGenIndex(&nw_index); + sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&north_index, scene_path); + index = fgBucketGenIndex(&north_index); + sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".south") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.south", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&south_index, scene_path); + index = fgBucketGenIndex(&south_index); + sprintf(file, "%s/%s/%ld.1.north", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".north") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.north", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&north_index, scene_path); + index = fgBucketGenIndex(&north_index); + sprintf(file, "%s/%s/%ld.1.south", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".west") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.west", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&west_index, scene_path); + index = fgBucketGenIndex(&west_index); + sprintf(file, "%s/%s/%ld.1.east", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".east") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.east", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + fgBucketGenBasePath(&east_index, scene_path); + index = fgBucketGenIndex(&east_index); + sprintf(file, "%s/%s/%ld.1.west", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + if ( strcmp(ext, ".body") == 0 ) { + fgBucketGenBasePath(&my_index, scene_path); + index = fgBucketGenIndex(&my_index); + sprintf(file, "%s/%s/%ld.1.body", basepath, scene_path, index); + if ( file_exists(file) ) { + return(1); + } + } + + return(0); +} + + +// given a file pointer, read all the vn (normals from it) +void read_normals(FILE *fp) { + char line[256]; + + while ( fgets(line, 250, fp) != NULL ) { + if ( strncmp(line, "vn ", 3) == 0 ) { + sscanf( line, "vn %lf %lf %lf\n", + &normals[normalcount][0], + &normals[normalcount][1], + &normals[normalcount][2] ); + /* + printf("read_normals(%d) %.2f %.2f %.2f %s", normalcount, + normals[normalcount][0], normals[normalcount][1], + normals[normalcount][2], line); + */ + normalcount++; + } + } +} + + +// my custom file opening routine ... don't open if a shared edge or +// vertex alread exists +FILE *my_open(char *basename, char *basepath, char *ext) { + FILE *fp; + char filename[256]; + + // check if a shared object already exists + if ( shared_object_exists(basepath, ext, filename) ) { + // not an actual file open error, but we've already got the + // shared edge, so we don't want to create another one + fp = fopen(filename, "r"); + printf("Opening %s\n", filename); + return(fp); + } else { + // open the file + printf("not opening\n"); + return(NULL); + } +} + + +// Initialize a new mesh structure +void triload(char *basename, char *basepath) { + char nodename[256], elename[256]; + double n[3]; + Point3D p; + FILE *ne, *nw, *se, *sw, *north, *south, *east, *west; + FILE *node, *ele; + int dim, junk1, junk2; + int i; + + ne = my_open(basename, basepath, ".ne"); + read_normals(ne); + fclose(ne); + + nw = my_open(basename, basepath, ".nw"); + read_normals(nw); + fclose(nw); + + se = my_open(basename, basepath, ".se"); + read_normals(se); + fclose(se); + + sw = my_open(basename, basepath, ".sw"); + read_normals(sw); + fclose(sw); + + north = my_open(basename, basepath, ".north"); + read_normals(north); + fclose(north); + + south = my_open(basename, basepath, ".south"); + read_normals(south); + fclose(south); + + east = my_open(basename, basepath, ".east"); + read_normals(east); + fclose(east); + + west = my_open(basename, basepath, ".west"); + read_normals(west); + fclose(west); + + strcpy(nodename, basename); + strcat(nodename, ".node"); + strcpy(elename, basename); + strcat(elename, ".ele"); + + printf("Loading node file: %s ...\n", nodename); + if ( (node = fopen(nodename, "r")) == NULL ) { + printf("Cannot open file '%s'\n", nodename); + exit(-1); + } + + fscanf(node, "%d %d %d %d", &nodecount, &dim, &junk1, &junk2); + + if ( nodecount > MAX_NODES - 1 ) { + printf("Error, too many nodes, need to increase array size\n"); + exit(-1); + } else { + printf(" Expecting %d nodes\n", nodecount); + } + + for ( i = 1; i <= nodecount; i++ ) { + fscanf(node, "%d %lf %lf %lf %d\n", &junk1, + &n[0], &n[1], &n[2], &junk2); + // printf("%d %.2f %.2f %.2f\n", junk1, n[0], n[1], n[2]); + p = Point3D( n[0] * ARCSEC_TO_RAD, + n[1] * ARCSEC_TO_RAD, + n[2] ); + nodes[i] = fgGeodToCart(p); + // printf("%d %.2f %.2f %.2f\n", + // junk1, nodes[i].x, nodes[i].y, nodes[i].z); + } + + fclose(node); + + printf("Loading element file: %s ...\n", elename); + if ( (ele = fopen(elename, "r")) == NULL ) { + printf("Cannot open file '%s'\n", elename); + exit(-1); + } + + fscanf(ele, "%d %d %d", &tricount, &junk1, &junk2); + + if ( tricount > MAX_TRIS - 1 ) { + printf("Error, too many elements, need to increase array size\n"); + exit(-1); + } else { + printf(" Expecting %d elements\n", tricount); + } + + for ( i = 1; i <= tricount; i++ ) { + fscanf(ele, "%d %d %d %d\n", &junk1, + &tris[i][0], &tris[i][1], &tris[i][2]); + // printf("%d %d %d %d\n", junk1, tris[i][0], tris[i][1], tris[i][2]);*/ + } + + fclose(ele); +} + + +// dump in WaveFront .obj format +void dump_obj(char *basename) { + char objname[256]; + double n1[3], n2[3], n3[3], n4[3], n5[3], norm[3], temp; + FILE *obj; + int i, t1, t2, t3, t4, t5, count; + double x, y, z; + + strcpy(objname, basename); + strcat(objname, ".obj"); + + printf("Dumping to file: %s ...\n", objname); + + obj = fopen(objname, "w"); + + // dump vertices + printf(" writing vertices\n"); + for ( i = 1; i <= nodecount; i++ ) { + x = nodes[i].x(); + y = nodes[i].y(); + z = nodes[i].z(); + fprintf(obj, "v %.6f %.6f %.6f\n", x, y, z); + } + + printf(" calculating and writing normals\n"); + printf(" First %d normals taken from shared files.\n", normalcount); + + // calculate and generate normals + for ( i = 1; i <= nodecount; i++ ) { + + if ( i <= normalcount ) { + // use precalculated (shared) normal + norm[0] = normals[i-1][0]; + norm[1] = normals[i-1][1]; + norm[2] = normals[i-1][2]; + } else { + // printf("Finding normal\n"); + + find_tris(i, &t1, &t2, &t3, &t4, &t5); + + n1[0] = n1[1] = n1[2] = 0.0; + n2[0] = n2[1] = n2[2] = 0.0; + n3[0] = n3[1] = n3[2] = 0.0; + n4[0] = n4[1] = n4[2] = 0.0; + n5[0] = n5[1] = n5[2] = 0.0; + + count = 1; + calc_normal(nodes[tris[t1][0]], nodes[tris[t1][1]], + nodes[tris[t1][2]], n1); + + if ( t2 > 0 ) { + calc_normal(nodes[tris[t2][0]], nodes[tris[t2][1]], + nodes[tris[t2][2]], n2); + count = 2; + } + + if ( t3 > 0 ) { + calc_normal(nodes[tris[t3][0]], nodes[tris[t3][1]], + nodes[tris[t3][2]], n3); + count = 3; + } + + if ( t4 > 0 ) { + calc_normal(nodes[tris[t4][0]], nodes[tris[t4][1]], + nodes[tris[t4][2]], n4); + count = 4; + } + + if ( t5 > 0 ) { + calc_normal(nodes[tris[t5][0]], nodes[tris[t5][1]], + nodes[tris[t5][2]], n5); + count = 5; + } + + // printf(" norm[2] = %.2f %.2f %.2f\n", n1[2], n2[2], n3[2]); + + norm[0] = ( n1[0] + n2[0] + n3[0] + n4[0] + n5[0] ) / (double)count; + norm[1] = ( n1[1] + n2[1] + n3[1] + n4[1] + n5[1] ) / (double)count; + norm[2] = ( n1[2] + n2[2] + n3[2] + n4[2] + n5[2] ) / (double)count; + + // printf(" count = %d\n", count); + // printf(" Ave. normal = %.4f %.4f %.4f\n", + // norm[0], norm[1], norm[2]);*/ + MAT3_NORMALIZE_VEC(norm, temp); + // printf(" Normalized ave. normal = %.4f %.4f %.4f\n", + // norm[0], norm[1], norm[2]); + } + // printf("%d vn %.4f %.4f %.4f\n", i, norm[0], norm[1], norm[2]); + fprintf(obj, "vn %.4f %.4f %.4f\n", norm[0], norm[1], norm[2]); + } + + // dump faces + printf(" writing faces\n"); + for ( i = 1; i <= tricount; i++ ) { + fprintf(obj, "f %d %d %d\n", tris[i][0], tris[i][1], tris[i][2]); + } + + fclose(obj); +} + +int main(int argc, char **argv) { + char basename[256], basepath[256], temp[256]; + long int tmp_index; + int len; + + strcpy(basename, argv[1]); + + // find the base path of the file + extract_path(basename, basepath); + extract_path(basepath, basepath); + extract_path(basepath, basepath); + printf("%s\n", basepath); + + // find the index of the current file + extract_file(basename, temp); + len = strlen(temp); + if ( len >= 2 ) { + temp[len-2] = '\0'; + } + tmp_index = atoi(temp); + printf("%ld\n", tmp_index); + fgBucketParseIndex(tmp_index, &my_index); + + printf("bucket = %d %d %d %d\n", + my_index.lon, my_index.lat, my_index.x, my_index.y); + // generate the indexes of the neighbors + fgBucketOffset(&my_index, &ne_index, 1, 1); + fgBucketOffset(&my_index, &nw_index, -1, 1); + fgBucketOffset(&my_index, &se_index, 1, -1); + fgBucketOffset(&my_index, &sw_index, -1, -1); + + fgBucketOffset(&my_index, &north_index, 0, 1); + fgBucketOffset(&my_index, &south_index, 0, -1); + fgBucketOffset(&my_index, &east_index, 1, 0); + fgBucketOffset(&my_index, &west_index, -1, 0); + + // load the input data files + triload(basename, basepath); + + // dump in WaveFront .obj format + dump_obj(basename); + + return(0); +} + + +// $Log$ +// Revision 1.5 1998/10/21 14:56:50 curt +// Minor parameter passing tweak. +// +// Revision 1.4 1998/10/20 15:52:46 curt +// Fixed a units conversion bug introduced when converting to Point3D class. +// +// Revision 1.3 1998/10/19 19:33:31 curt +// C++-ification. +// +// Revision 1.2 1998/10/18 01:17:29 curt +// Point3D tweaks. +// +// Revision 1.1 1998/07/08 14:54:53 curt +// renamed *.[ch] to *.[ch]xx +// +// Revision 1.17 1998/07/04 00:56:40 curt +// typedef'd struct fgBUCKET. +// +// Revision 1.16 1998/05/23 15:20:41 curt +// Output more digits after the decimal place. +// +// Revision 1.15 1998/05/02 01:54:39 curt +// Converting to polar3d.h routines. +// +// Revision 1.14 1998/04/18 04:01:32 curt +// Now use libMath rather than having local copies of math routines. +// +// Revision 1.13 1998/04/14 02:26:11 curt +// Code reorganizations. Added a Lib/ directory for more general libraries. +// +// Revision 1.12 1998/04/08 23:22:18 curt +// Adopted Gnu automake/autoconf system. +// +// Revision 1.11 1998/03/03 16:01:00 curt +// More c++ compile tweaks. +// +// Revision 1.10 1998/01/31 00:41:27 curt +// Made a few changes converting floats to doubles. +// +// Revision 1.9 1998/01/27 18:37:04 curt +// Lots of updates to get back in sync with changes made over in .../Src/ +// +// Revision 1.8 1998/01/17 01:25:39 curt +// Added support for shared normals. +// +// Revision 1.7 1998/01/12 02:42:00 curt +// Average up to five triangles per vertex instead of three. +// +// Revision 1.6 1998/01/09 23:03:15 curt +// Restructured to split 1deg x 1deg dem's into 64 subsections. +// +// Revision 1.5 1997/12/08 19:17:50 curt +// Fixed a type in the normal generation code. +// +// Revision 1.4 1997/12/02 13:13:32 curt +// Fixed problem with averaged vertex normals. +// +// Revision 1.3 1997/11/15 18:05:05 curt +// minor tweaks ... +// +// Revision 1.2 1997/11/14 00:29:13 curt +// Transform scenery coordinates at this point in pipeline when scenery is +// being translated to .obj format, not when it is being loaded into the end +// renderer. Precalculate normals for each node as average of the normals +// of each containing polygon so Garoude shading is now supportable. +// +// Revision 1.1 1997/10/29 23:05:15 curt +// Initial revision. +// + diff --git a/Tools/Tri2obj/tri2obj.hxx b/Tools/Tri2obj/tri2obj.hxx new file mode 100644 index 000000000..c7a54d159 --- /dev/null +++ b/Tools/Tri2obj/tri2obj.hxx @@ -0,0 +1,68 @@ +/* tri2obj.h -- read in a .ele/.node file pair generated by the triangle + * program and output a Wavefront .obj file. + * + * Written by Curtis Olson, started October 1997. + * + * Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + * (Log is kept at end of this file) + */ + + +#ifndef TRI2OBJ_H +#define TRI2OBJ_H + + +#include <stdio.h> +#include <string.h> + + +#define MAX_NODES 200000 +#define MAX_TRIS 400000 + + +/* Initialize a new mesh structure */ +void triload(char *basename, char *basepath); + + +#endif /* TRI2OBJ_H */ + + +/* $Log$ +/* Revision 1.1 1998/07/08 14:54:54 curt +/* renamed *.[ch] to *.[ch]xx +/* + * Revision 1.5 1998/03/03 16:01:00 curt + * More c++ compile tweaks. + * + * Revision 1.4 1998/01/17 01:25:40 curt + * Added support for shared normals. + * + * Revision 1.3 1997/11/15 18:05:06 curt + * minor tweaks ... + * + * Revision 1.2 1997/11/14 00:29:13 curt + * Transform scenery coordinates at this point in pipeline when scenery is + * being translated to .obj format, not when it is being loaded into the end + * renderer. Precalculate normals for each node as average of the normals + * of each containing polygon so Garoude shading is now supportable. + * + * Revision 1.1 1997/10/29 23:05:15 curt + * Initial revision. + * + */ diff --git a/Tools/Triangle/A.poly b/Tools/Triangle/A.poly new file mode 100644 index 000000000..166a71773 --- /dev/null +++ b/Tools/Triangle/A.poly @@ -0,0 +1,62 @@ +29 2 1 0 +1 0.200000 -0.776400 -0.57 +2 0.220000 -0.773200 -0.55 +3 0.245600 -0.756400 -0.51 +4 0.277600 -0.702000 -0.53 +5 0.488800 -0.207600 0.28 +6 0.504800 -0.207600 0.30 +7 0.740800 -0.739600 0 +8 0.756000 -0.761200 -0.01 +9 0.774400 -0.772400 0 +10 0.800000 -0.776400 0.02 +11 0.800000 -0.792400 0.01 +12 0.579200 -0.792400 -0.21 +13 0.579200 -0.776400 -0.2 +14 0.621600 -0.771600 -0.15 +15 0.633600 -0.762800 -0.13 +16 0.639200 -0.744400 -0.1 +17 0.620800 -0.684400 -0.06 +18 0.587200 -0.604400 -0.01 +19 0.360800 -0.604400 -0.24 +20 0.319200 -0.706800 -0.39 +21 0.312000 -0.739600 -0.43 +22 0.318400 -0.761200 -0.44 +23 0.334400 -0.771600 -0.44 +24 0.371200 -0.776400 -0.41 +25 0.371200 -0.792400 -0.42 +26 0.374400 -0.570000 -0.2 +27 0.574400 -0.570000 0 +28 0.473600 -0.330800 0.14 +29 0.200000 -0.792400 -0.59 +29 0 +1 29 1 +2 1 2 +3 2 3 +4 3 4 +5 4 5 +6 5 6 +7 6 7 +8 7 8 +9 8 9 +10 9 10 +11 10 11 +12 11 12 +13 12 13 +14 13 14 +15 14 15 +16 15 16 +17 16 17 +18 17 18 +19 18 19 +20 19 20 +21 20 21 +22 21 22 +23 22 23 +24 23 24 +25 24 25 +26 25 29 +27 26 27 +28 27 28 +29 28 26 +1 +1 0.47 -0.5 diff --git a/Tools/Triangle/Makefile.am b/Tools/Triangle/Makefile.am new file mode 100644 index 000000000..caa86591b --- /dev/null +++ b/Tools/Triangle/Makefile.am @@ -0,0 +1,24 @@ +# DEFS is a list of definitions used to compile an object code version +# of Triangle (triangle.o) to be called by another program. The file +# "triangle.h" contains detailed information on how to call triangle.o. +# +# The -DTRILIBRARY should always be used when compiling Triangle into an +# object file. +# +# An example DEFS line is: +# +# DEFS = -DTRILIBRARY -DREDUCED -DCDT_ONLY + +DEFS += -DTRILIBRARY + +noinst_LIBRARIES = libTriangle.a + +libTriangle_a_SOURCES = triangle.c triangle.h + +if HAVE_XWINDOWS + +bin_PROGRAMS = showme +showme_SOURCES = showme.c +showme_LDADD = -lX11 + +endif \ No newline at end of file diff --git a/Tools/Triangle/README b/Tools/Triangle/README new file mode 100644 index 000000000..571d5689f --- /dev/null +++ b/Tools/Triangle/README @@ -0,0 +1,181 @@ +Triangle +A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. +Version 1.3 + +Show Me +A Display Program for Meshes and More. +Version 1.3 + +Copyright 1996 Jonathan Richard Shewchuk +School of Computer Science +Carnegie Mellon University +5000 Forbes Avenue +Pittsburgh, Pennsylvania 15213-3891 +Please send bugs and comments to jrs@cs.cmu.edu + +Created as part of the Archimedes project (tools for parallel FEM). +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, and quality conforming Delaunay triangulations. The +latter can be generated with no small 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 data file. + +Triangle and Show Me are each 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. + + Do you want single precision or double? Do you want to leave out some of + Triangle's features to reduce the size of the executable file? + + The "correct" directories for include files (especially X include files), + if necessary. + +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 + +However, on other systems (like my workstation), the latter incantation +will cause the wrong files to be read, and the Show Me mouse buttons won't +work properly in the main window. Hence, try the "-I" and "-L" switches +ONLY if the compiler fails without it. (If you're using the makefile, you +may edit it to add this switch.) + +Some processors, possibly including Intel x86 family and 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. If you have such a processor, +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. +Unfortunately, I don't have access to any such systems, and can't give +advice on how to configure them. These problems don't occur on any +workstations I am aware of. However, 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 both produce their own documentation. Complete +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'). Alternatively, you may want to read +the instructions on the World Wide Web. The appropriate URLs are: + + http://www.cs.cmu.edu/~quake/triangle.html + http://www.cs.cmu.edu/~quake/showme.html + +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. + +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 + +------------------------------------------------------------------------------ + +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.cmu.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. + + +Jonathan Richard Shewchuk +July 20, 1996 diff --git a/Tools/Triangle/showme.c b/Tools/Triangle/showme.c new file mode 100644 index 000000000..722cba8ac --- /dev/null +++ b/Tools/Triangle/showme.c @@ -0,0 +1,3384 @@ +/*****************************************************************************/ +/* */ +/* ,d88^^o 888 o o */ +/* 8888 888o^88, o88^^o Y88b o / d8b d8b o88^^8o */ +/* "Y88b 888 888 d888 b Y88b d8b / d888bdY88b d888 88b */ +/* "Y88b, 888 888 8888 8 Y888/Y88b/ / Y88Y Y888b 8888oo888 */ +/* o 8888 888 888 q888 p Y8/ Y8/ / YY Y888b q888 */ +/* "oo88P" 888 888 "88oo" Y Y / Y888b "88oooo" */ +/* */ +/* A Display Program for Meshes and More. */ +/* (showme.c) */ +/* */ +/* Version 1.3 */ +/* July 20, 1996 */ +/* */ +/* Copyright 1996 */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/* This program may be freely redistributed under the condition that the */ +/* copyright notices (including this entire header 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.) */ +/* */ +/* Hypertext instructions for Triangle are available on the Web at */ +/* */ +/* http://www.cs.cmu.edu/~quake/showme.html */ +/* */ +/* Show Me was created as part of the Archimedes project in the School of */ +/* Computer Science at Carnegie Mellon University. Archimedes is a */ +/* system for compiling parallel finite element solvers. For further */ +/* information, see Anja Feldmann, Omar Ghattas, John R. Gilbert, Gary L. */ +/* Miller, David R. O'Hallaron, Eric J. Schwabe, Jonathan R. Shewchuk, */ +/* and Shang-Hua Teng. "Automated Parallel Solution of Unstructured PDE */ +/* Problems." To appear in Communications of the ACM, we hope. */ +/* */ +/* If you make any improvements to this code, please please please let me */ +/* know, so that I may obtain the improvements. Even if you don't change */ +/* the code, I'd still love to hear what it's being used for. */ +/* */ +/* Disclaimer: Neither I nor Carnegie Mellon warrant this code in any way */ +/* whatsoever. Use at your own risk. */ +/* */ +/*****************************************************************************/ + +/* For single precision (which will save some memory and reduce paging), */ +/* write "#define SINGLE" below. */ +/* */ +/* For double precision (which will allow you to display triangulations of */ +/* a finer resolution), leave SINGLE undefined. */ + +/* #define SINGLE */ + +#ifdef SINGLE +#define REAL float +#else +#define REAL double +#endif + +/* Maximum number of characters in a file name (including the null). */ + +#define FILENAMESIZE 1024 + +/* Maximum number of characters in a line read from a file (including the */ +/* null). */ + +#define INPUTLINESIZE 512 + +#define STARTWIDTH 414 +#define STARTHEIGHT 414 +#define MINWIDTH 50 +#define MINHEIGHT 50 +#define BUTTONHEIGHT 21 +#define BUTTONROWS 3 +#define PANELHEIGHT (BUTTONHEIGHT * BUTTONROWS) +#define MAXCOLORS 64 + +#define IMAGE_TYPES 7 +#define NOTHING -1 +#define NODE 0 +#define POLY 1 +#define ELE 2 +#define EDGE 3 +#define PART 4 +#define ADJ 5 +#define VORO 6 + +#define STARTEXPLOSION 0.5 + +#include <stdio.h> +#include <string.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +/* The following obscenity seems to be necessary to ensure that this program */ +/* will port to Dec Alphas running OSF/1, because their stdio.h file commits */ +/* the unpardonable sin of including stdlib.h. Hence, malloc(), free(), and */ +/* exit() may or may not already be defined at this point. I declare these */ +/* functions explicitly because some non-ANSI C compilers lack stdlib.h. */ + +#ifndef _STDLIB_H_ +extern char *malloc(); +extern void free(); +extern void exit(); +extern double strtod(); +extern long strtol(); +#endif + +/* A necessary forward declaration. */ + +int load_image(); + +Display *display; +int screen; +Window rootwindow; +Window mainwindow; +Window quitwin; +Window leftwin; +Window rightwin; +Window upwin; +Window downwin; +Window resetwin; +Window pswin; +Window epswin; +Window expwin; +Window exppluswin; +Window expminuswin; +Window widthpluswin; +Window widthminuswin; +Window versionpluswin; +Window versionminuswin; +Window fillwin; +Window nodewin[2]; +Window polywin[2]; +Window elewin[2]; +Window edgewin[2]; +Window partwin[2]; +Window adjwin[2]; +Window voronoiwin[2]; + +int windowdepth; +XEvent event; +Colormap rootmap; +XFontStruct *font; +int width, height; +int black, white; +int showme_foreground; +GC fontgc; +GC blackfontgc; +GC linegc; +GC trianglegc; +int colors[MAXCOLORS]; +XColor rgb[MAXCOLORS]; +int color; + +int start_image, current_image; +int start_inc, current_inc; +int loweriteration; +int line_width; +int loaded[2][IMAGE_TYPES]; +REAL xlo[2][IMAGE_TYPES], ylo[2][IMAGE_TYPES]; +REAL xhi[2][IMAGE_TYPES], yhi[2][IMAGE_TYPES]; +REAL xscale, yscale; +REAL xoffset, yoffset; +int zoom; + +int nodes[2], node_dim[2]; +REAL *nodeptr[2]; +int polynodes[2], poly_dim[2], polyedges[2], polyholes[2]; +REAL *polynodeptr[2], *polyholeptr[2]; +int *polyedgeptr[2]; +int elems[2], ele_corners[2]; +int *eleptr[2]; +int edges[2]; +int *edgeptr[2]; +REAL *normptr[2]; +int subdomains[2]; +int *partpart[2]; +REAL *partcenter[2], *partshift[2]; +int adjsubdomains[2]; +int *adjptr[2]; +int vnodes[2], vnode_dim[2]; +REAL *vnodeptr[2]; +int vedges[2]; +int *vedgeptr[2]; +REAL *vnormptr[2]; +int firstnumber[2]; + +int quiet, fillelem, bw_ps, explode; +REAL explosion; + +char filename[FILENAMESIZE]; +char nodefilename[2][FILENAMESIZE]; +char polyfilename[2][FILENAMESIZE]; +char elefilename[2][FILENAMESIZE]; +char edgefilename[2][FILENAMESIZE]; +char partfilename[2][FILENAMESIZE]; +char adjfilename[2][FILENAMESIZE]; +char vnodefilename[2][FILENAMESIZE]; +char vedgefilename[2][FILENAMESIZE]; + +char *colorname[] = {"aquamarine", "red", "green yellow", "magenta", + "yellow", "green", "orange", "blue", + "white", "sandy brown", "cyan", "moccasin", + "cadet blue", "coral", "cornflower blue", "sky blue", + "firebrick", "forest green", "gold", "goldenrod", + "gray", "hot pink", "chartreuse", "pale violet red", + "indian red", "khaki", "lavender", "light blue", + "light gray", "light steel blue", "lime green", "azure", + "maroon", "medium aquamarine", "dodger blue", "honeydew", + "medium orchid", "medium sea green", "moccasin", + "medium slate blue", "medium spring green", + "medium turquoise", "medium violet red", + "orange red", "chocolate", "light goldenrod", + "orchid", "pale green", "pink", "plum", + "purple", "salmon", "sea green", + "sienna", "slate blue", "spring green", + "steel blue", "tan", "thistle", "turquoise", + "violet", "violet red", "wheat", + "yellow green"}; + +void syntax() +{ + printf("showme [-bfw_Qh] input_file\n"); + printf(" -b Black and white PostScript (default is color).\n"); + printf(" -f Fill triangles of partitioned mesh with color.\n"); + printf(" -w Set line width to some specified number.\n"); + printf(" -Q Quiet: No terminal output except errors.\n"); + printf(" -h Help: Detailed instructions for Show Me.\n"); + exit(0); +} + +void info() +{ + printf("Show Me\n"); + printf("A Display Program for Meshes and More.\n"); + printf("Version 1.3\n\n"); + printf( +"Copyright 1996 Jonathan Richard Shewchuk (bugs/comments to jrs@cs.cmu.edu)\n" +); + printf("School of Computer Science / Carnegie Mellon University\n"); + printf("5000 Forbes Avenue / Pittsburgh, Pennsylvania 15213-3891\n"); + printf( +"Created as part of the Archimedes project (tools for parallel FEM).\n"); + printf( +"Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.\n"); + printf("There is no warranty whatsoever. Use at your own risk.\n"); +#ifdef SINGLE + printf("This executable is compiled for single precision arithmetic.\n\n\n"); +#else + printf("This executable is compiled for double precision arithmetic.\n\n\n"); +#endif + printf( +"Show Me graphically displays the contents of geometric files, especially\n"); + printf( +"those generated by Triangle, my two-dimensional quality mesh generator and\n" +); + printf( +"Delaunay triangulator. Show Me can also write images in PostScript form.\n"); + printf( +"Show Me is also useful for checking the consistency of the files you create\n" +); + printf( +"as input to Triangle; Show Me does these checks more thoroughly than\n"); + printf("Triangle does. The command syntax is:\n\n"); + printf("showme [-bfw_Qh] input_file\n\n"); + printf( +"The underscore indicates that a number should follow the -w switch.\n"); + printf( +"input_file may be one of several types of file. It must have extension\n"); + printf( +".node, .poly, .ele, .edge, .part, or .adj. If no extension is provided,\n"); + printf( +"Show Me will assume the extension .ele. A .node file represents a set of\n"); + printf( +"points; a .poly file represents a Planar Straight Line Graph; an .ele file\n" +); + printf( +"(coupled with a .node file) represents the elements of a mesh or the\n"); + printf( +"triangles of a triangulation; an .edge file (coupled with a .node file)\n"); + printf( +"represents a set of edges; a .part file specifies a partition of a mesh;\n"); + printf( +"and a .adj file represents the adjacency graph defined by a partition.\n"); + printf("\n"); + printf("Command Line Switches:\n"); + printf("\n"); + printf( +" -b Makes all PostScript output black and white. If this switch is not\n" +); + printf( +" selected, color PostScript is used for partitioned meshes and\n"); + printf(" adjacency graphs (.part and .adj files).\n"); + printf( +" -f On color displays and in color PostScript, displays partitioned\n"); + printf( +" meshes by filling triangles with color, rather than by coloring the\n" +); + printf( +" edges. This switch will result in a clearer picture if all\n"); + printf( +" triangles are reasonably large, and a less clear picture if small\n"); + printf( +" triangles are present. (There is also a button to toggle this\n"); + printf(" behavior.)\n"); + printf( +" -w Followed by an integer, specifies the line width used in all\n"); + printf( +" images. (There are also buttons to change the line width.)\n"); + printf( +" -Q Quiet: Suppresses all explanation of what Show Me is doing, unless\n" +); + printf(" an error occurs.\n"); + printf(" -h Help: Displays these instructions.\n"); + printf("\n"); + printf("Controls:\n"); + printf("\n"); + printf( +" To zoom in on an image, point at the location where you want a closer\n"); + printf( +" look, and click the left mouse button. To zoom out, click the right\n"); + printf( +" mouse button. In either case, the point you click on will be centered in\n" +); + printf( +" the window. If you want to know the coordinates of a point, click the\n"); + printf( +" middle mouse button; the coordinates will be printed on the terminal you\n" +); + printf(" invoked Show Me from.\n\n"); + printf( +" If you resize the window, the image will grow or shrink to match.\n"); + printf("\n"); + printf( +" There is a panel of control buttons at the bottom of the Show Me window:\n" +); + printf("\n"); + printf(" Quit: Shuts down Show Me.\n"); + printf(" <, >, ^, v: Moves the image in the indicated direction.\n"); + printf( +" Reset: Unzooms and centers the image in the window. When you switch from\n" +); + printf( +" one image to another, the viewing region does not change, so you may\n"); + printf( +" need to reset the new image to make it fully visible. This often is\n"); + printf( +" the case when switching between Delaunay triangulations and their\n"); + printf( +" corresponding Voronoi diagrams, as Voronoi vertices can be far from the\n" +); + printf(" initial point set.\n"); + printf( +" Width+, -: Increases or decreases the width of all lines and points.\n"); + printf( +" Exp, +, -: These buttons appear only when you are viewing a partitioned\n" +); + printf( +" mesh (.part file). `Exp' toggles between an exploded and non-exploded\n" +); + printf( +" image of the mesh. The non-exploded image will not show the partition\n" +); + printf( +" on a black and white monitor. `+' and `-' allow you to adjust the\n"); + printf( +" spacing between pieces of the mesh to better distinguish them.\n"); + printf( +" Fill: This button appears only when you are viewing a partitioned mesh\n"); + printf( +" (.part file). It toggles between color-filled triangles and colored\n"); + printf( +" edges (as the -f switch does). Filled triangles look better when all\n"); + printf( +" triangles are reasonably large; colored edges look better when there\n"); + printf(" are very small triangles present.\n"); + printf( +" PS: Creates a PostScript file containing the image you are viewing. If\n" +); + printf( +" the -b switch is selected, all PostScript output will be black and\n"); + printf( +" white; otherwise, .part.ps and .adj.ps files will be color, independent\n" +); + printf( +" of whether you are using a color monitor. Normally the output will\n"); + printf( +" preserve the properties of the image you see on the screen, including\n"); + printf( +" zoom and line width; however, if black and white output is selected (-b\n" +); + printf( +" switch), partitioned meshes will always be drawn exploded. The output\n" +); + printf( +" file name depends on the image being viewed. If you want several\n"); + printf( +" different snapshots (zooming in on different parts) of the same object,\n" +); + printf( +" you'll have to rename each file after Show Me creates it so that it\n"); + printf(" isn't overwritten by the next snapshot.\n"); + printf( +" EPS: Creates an encapsulated PostScript file, suitable for inclusion in\n" +); + printf( +" documents. Otherwise, this button is just like the PS button. (The\n"); + printf( +" main difference is that .eps files lack a `showpage' command at the\n"); + printf(" end.)\n\n"); + printf( +" There are two nearly-identical rows of buttons that load different images\n" +); + printf(" from disk. Each row contains the following buttons:\n\n"); + printf(" node: Loads a .node file.\n"); + printf( +" poly: Loads a .poly file (and possibly an associated .node file).\n"); + printf(" ele: Loads an .ele file (and associated .node file).\n"); + printf(" edge: Loads an .edge file (and associated .node file).\n"); + printf( +" part: Loads a .part file (and associated .node and .ele files).\n"); + printf( +" adj: Loads an .adj file (and associated .node, .ele, and .part files).\n"); + printf(" voro: Loads a .v.node and .v.edge file for a Voronoi diagram.\n"); + printf("\n"); + printf( +" Each row represents a different iteration number of the geometry files.\n"); + printf( +" For a full explanation of iteration numbers, read the instructions for\n"); + printf( +" Triangle. Briefly, iteration numbers are used to allow a user to easily\n" +); + printf( +" represent a sequence of related triangulations. Iteration numbers are\n"); + printf( +" used in the names of geometry files; for instance, mymesh.3.ele is a\n"); + printf( +" triangle file with iteration number three, and mymesh.ele has an implicit\n" +); + printf(" iteration number of zero.\n\n"); + printf( +" The control buttons at the right end of each row display the two\n"); + printf( +" iterations currently under view. These buttons can be clicked to\n"); + printf( +" increase or decrease the iteration numbers, and thus conveniently view\n"); + printf(" a sequence of meshes.\n\n"); + printf( +" Show Me keeps each file in memory after loading it, but you can force\n"); + printf( +" Show Me to reread a set of files (for one iteration number) by reclicking\n" +); + printf( +" the button that corresponds to the current image. This is convenient if\n" +); + printf(" you have changed a geometry file.\n\n"); + printf("File Formats:\n\n"); + printf( +" All files may contain comments prefixed by the character '#'. Points,\n"); + printf( +" segments, holes, triangles, edges, and subdomains must be numbered\n"); + printf( +" consecutively, starting from either 1 or 0. Whichever you choose, all\n"); + printf( +" input files must be consistent (for any single iteration number); if the\n" +); + printf( +" nodes are numbered from 1, so must be all other objects. Show Me\n"); + printf( +" automatically detects your choice while reading a .node (or .poly) file.\n" +); + printf(" Examples of these file formats are given below.\n\n"); + printf(" .node files:\n"); + printf( +" First line: <# of points> <dimension (must be 2)> <# of attributes>\n"); + printf( +" <# of boundary markers (0 or 1)>\n" +); + printf( +" Remaining lines: <point #> <x> <y> [attributes] [boundary marker]\n"); + printf("\n"); + printf( +" The attributes, which are typically floating-point values of physical\n"); + printf( +" quantities (such as mass or conductivity) associated with the nodes of\n" +); + printf( +" a finite element mesh, are ignored by Show Me. Show Me also ignores\n"); + printf( +" boundary markers. See the instructions for Triangle to find out what\n"); + printf(" attributes and boundary markers are.\n\n"); + printf(" .poly files:\n"); + printf( +" First line: <# of points> <dimension (must be 2)> <# of attributes>\n"); + printf( +" <# of boundary markers (0 or 1)>\n" +); + printf( +" Following lines: <point #> <x> <y> [attributes] [boundary marker]\n"); + printf(" One line: <# of segments> <# of boundary markers (0 or 1)>\n"); + printf( +" Following lines: <segment #> <endpoint> <endpoint> [boundary marker]\n"); + printf(" One line: <# of holes>\n"); + printf(" Following lines: <hole #> <x> <y>\n"); + printf(" [Optional additional lines that are ignored]\n\n"); + printf( +" A .poly file represents a Planar Straight Line Graph (PSLG), an idea\n"); + printf( +" familiar to computational geometers. By definition, a PSLG is just a\n"); + printf( +" list of points and edges. A .poly file also contains some additional\n"); + printf(" information.\n\n"); + printf( +" The first section lists all the points, and is identical to the format\n" +); + printf( +" of .node files. <# of points> may be set to zero to indicate that the\n" +); + printf( +" points are listed in a separate .node file; .poly files produced by\n"); + printf( +" Triangle always have this format. When Show Me reads such a file, it\n"); + printf(" also reads the corresponding .node file.\n\n"); + printf( +" The second section lists the segments. Segments are edges whose\n"); + printf( +" presence in a triangulation produced from the PSLG is enforced. Each\n"); + printf( +" segment is specified by listing the indices of its two endpoints. This\n" +); + printf( +" means that its endpoints must be included in the point list. Each\n"); + printf( +" segment, like each point, may have a boundary marker, which is ignored\n" +); + printf(" by Show Me.\n\n"); + printf( +" The third section lists holes and concavities that are desired in any\n"); + printf( +" triangulation generated from the PSLG. Holes are specified by\n"); + printf(" identifying a point inside each hole.\n\n"); + printf(" .ele files:\n"); + printf( +" First line: <# of triangles> <points per triangle> <# of attributes>\n"); + printf( +" Remaining lines: <triangle #> <point> <point> <point> ... [attributes]\n" +); + printf("\n"); + printf( +" Points are indices into the corresponding .node file. Show Me ignores\n" +); + printf( +" all but the first three points of each triangle; these should be the\n"); + printf( +" corners listed in counterclockwise order around the triangle. The\n"); + printf(" attributes are ignored by Show Me.\n\n"); + printf(" .edge files:\n"); + printf(" First line: <# of edges> <# of boundary markers (0 or 1)>\n"); + printf( +" Following lines: <edge #> <endpoint> <endpoint> [boundary marker]\n"); + printf("\n"); + printf( +" Endpoints are indices into the corresponding .node file. The boundary\n" +); + printf(" markers are ignored by Show Me.\n\n"); + printf( +" In Voronoi diagrams, one also finds a special kind of edge that is an\n"); + printf( +" infinite ray with only one endpoint. For these edges, a different\n"); + printf(" format is used:\n\n"); + printf(" <edge #> <endpoint> -1 <direction x> <direction y>\n\n"); + printf( +" The `direction' is a floating-point vector that indicates the direction\n" +); + printf(" of the infinite ray.\n\n"); + printf(" .part files:\n"); + printf(" First line: <# of triangles> <# of subdomains>\n"); + printf(" Remaining lines: <triangle #> <subdomain #>\n\n"); + printf( +" The set of triangles is partitioned by a .part file; each triangle is\n"); + printf(" mapped to a subdomain.\n\n"); + printf(" .adj files:\n"); + printf(" First line: <# of subdomains>\n"); + printf(" Remaining lines: <adjacency matrix entry>\n\n"); + printf( +" An .adj file represents adjacencies between subdomains (presumably\n"); + printf(" computed by a partitioner). The first line is followed by\n"); + printf( +" (subdomains X subdomains) lines, each containing one entry of the\n"); + printf( +" adjacency matrix. A nonzero entry indicates that two subdomains are\n"); + printf(" adjacent (share a point).\n\n"); + printf("Example:\n\n"); + printf( +" Here is a sample file `box.poly' describing a square with a square hole:\n" +); + printf("\n"); + printf( +" # A box with eight points in 2D, no attributes, no boundary marker.\n"); + printf(" 8 2 0 0\n"); + printf(" # Outer box has these vertices:\n"); + printf(" 1 0 0\n"); + printf(" 2 0 3\n"); + printf(" 3 3 0\n"); + printf(" 4 3 3\n"); + printf(" # Inner square has these vertices:\n"); + printf(" 5 1 1\n"); + printf(" 6 1 2\n"); + printf(" 7 2 1\n"); + printf(" 8 2 2\n"); + printf(" # Five segments without boundary markers.\n"); + printf(" 5 0\n"); + printf(" 1 1 2 # Left side of outer box.\n"); + printf(" 2 5 7 # Segments 2 through 5 enclose the hole.\n"); + printf(" 3 7 8\n"); + printf(" 4 8 6\n"); + printf(" 5 6 5\n"); + printf(" # One hole in the middle of the inner square.\n"); + printf(" 1\n"); + printf(" 1 1.5 1.5\n\n"); + printf( +" After this PSLG is triangulated by Triangle, the resulting triangulation\n" +); + printf( +" consists of a .node and .ele file. Here is the former, `box.1.node',\n"); + printf(" which duplicates the points of the PSLG:\n\n"); + printf(" 8 2 0 0\n"); + printf(" 1 0 0\n"); + printf(" 2 0 3\n"); + printf(" 3 3 0\n"); + printf(" 4 3 3\n"); + printf(" 5 1 1\n"); + printf(" 6 1 2\n"); + printf(" 7 2 1\n"); + printf(" 8 2 2\n"); + printf(" # Generated by triangle -pcBev box\n"); + printf("\n"); + printf(" Here is the triangulation file, `box.1.ele'.\n"); + printf("\n"); + printf(" 8 3 0\n"); + printf(" 1 1 5 6\n"); + printf(" 2 5 1 3\n"); + printf(" 3 2 6 8\n"); + printf(" 4 6 2 1\n"); + printf(" 5 7 3 4\n"); + printf(" 6 3 7 5\n"); + printf(" 7 8 4 2\n"); + printf(" 8 4 8 7\n"); + printf(" # Generated by triangle -pcBev box\n\n"); + printf(" Here is the edge file for the triangulation, `box.1.edge'.\n\n"); + printf(" 16 0\n"); + printf(" 1 1 5\n"); + printf(" 2 5 6\n"); + printf(" 3 6 1\n"); + printf(" 4 1 3\n"); + printf(" 5 3 5\n"); + printf(" 6 2 6\n"); + printf(" 7 6 8\n"); + printf(" 8 8 2\n"); + printf(" 9 2 1\n"); + printf(" 10 7 3\n"); + printf(" 11 3 4\n"); + printf(" 12 4 7\n"); + printf(" 13 7 5\n"); + printf(" 14 8 4\n"); + printf(" 15 4 2\n"); + printf(" 16 8 7\n"); + printf(" # Generated by triangle -pcBev box\n"); + printf("\n"); + printf( +" Here's a file `box.1.part' that partitions the mesh into four subdomains.\n" +); + printf("\n"); + printf(" 8 4\n"); + printf(" 1 3\n"); + printf(" 2 3\n"); + printf(" 3 4\n"); + printf(" 4 4\n"); + printf(" 5 1\n"); + printf(" 6 1\n"); + printf(" 7 2\n"); + printf(" 8 2\n"); + printf(" # Generated by slice -s4 box.1\n\n"); + printf( +" Here's a file `box.1.adj' that represents the resulting adjacencies.\n"); + printf("\n"); + printf(" 4\n"); + printf(" 9\n"); + printf(" 2\n"); + printf(" 2\n"); + printf(" 0\n"); + printf(" 2\n"); + printf(" 9\n"); + printf(" 0\n"); + printf(" 2\n"); + printf(" 2\n"); + printf(" 0\n"); + printf(" 9\n"); + printf(" 2\n"); + printf(" 0\n"); + printf(" 2\n"); + printf(" 2\n"); + printf(" 9\n"); + printf("\n"); + printf("Display Speed:\n"); + printf("\n"); + printf( +" It is worthwhile to note that .edge files typically plot and print twice\n" +); + printf( +" as quickly as .ele files, because .ele files cause each internal edge to\n" +); + printf( +" be drawn twice. For the same reason, PostScript files created from edge\n" +); + printf(" sets are smaller than those created from triangulations.\n\n"); + printf("Show Me on the Web:\n\n"); + printf( +" To see an illustrated, updated version of these instructions, check out\n"); + printf("\n"); + printf(" http://www.cs.cmu.edu/~quake/showme.html\n"); + printf("\n"); + printf("A Brief Plea:\n"); + printf("\n"); + printf( +" If you use Show Me (or Triangle), and especially if you use it to\n"); + printf( +" accomplish real work, I would like very much to hear from you. A short\n"); + printf( +" letter or email (to jrs@cs.cmu.edu) describing how you use Show Me (and\n"); + printf( +" its sister programs) will mean a lot to me. The more people I know\n"); + printf( +" are using my programs, the more easily I can justify spending time on\n"); + printf( +" improvements, which in turn will benefit you. Also, I can put you\n"); + printf( +" on a list to receive email whenever new versions are available.\n"); + printf("\n"); + printf( +" If you use a PostScript file generated by Show Me in a publication,\n"); + printf(" please include an acknowledgment as well.\n\n"); + exit(0); +} + +void set_filenames(filename, lowermeshnumber) +char *filename; +int lowermeshnumber; +{ + char numberstring[100]; + int i; + + for (i = 0; i < 2; i++) { + strcpy(nodefilename[i], filename); + strcpy(polyfilename[i], filename); + strcpy(elefilename[i], filename); + strcpy(edgefilename[i], filename); + strcpy(partfilename[i], filename); + strcpy(adjfilename[i], filename); + strcpy(vnodefilename[i], filename); + strcpy(vedgefilename[i], filename); + + if (lowermeshnumber + i > 0) { + sprintf(numberstring, ".%d", lowermeshnumber + i); + strcat(nodefilename[i], numberstring); + strcat(polyfilename[i], numberstring); + strcat(elefilename[i], numberstring); + strcat(edgefilename[i], numberstring); + strcat(partfilename[i], numberstring); + strcat(adjfilename[i], numberstring); + strcat(vnodefilename[i], numberstring); + strcat(vedgefilename[i], numberstring); + } + + strcat(nodefilename[i], ".node"); + strcat(polyfilename[i], ".poly"); + strcat(elefilename[i], ".ele"); + strcat(edgefilename[i], ".edge"); + strcat(partfilename[i], ".part"); + strcat(adjfilename[i], ".adj"); + strcat(vnodefilename[i], ".v.node"); + strcat(vedgefilename[i], ".v.edge"); + } +} + +void parsecommandline(argc, argv) +int argc; +char **argv; +{ + int increment; + int meshnumber; + int i, j; + + quiet = 0; + fillelem = 0; + line_width = 1; + bw_ps = 0; + start_image = ELE; + filename[0] = '\0'; + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + for (j = 1; argv[i][j] != '\0'; j++) { + if (argv[i][j] == 'f') { + fillelem = 1; + } + if (argv[i][j] == 'w') { + if ((argv[i][j + 1] >= '1') && (argv[i][j + 1] <= '9')) { + line_width = 0; + while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + j++; + line_width = line_width * 10 + (int) (argv[i][j] - '0'); + } + if (line_width > 100) { + printf("Error: Line width cannot exceed 100.\n"); + line_width = 1; + } + } + } + if (argv[i][j] == 'b') { + bw_ps = 1; + } + if (argv[i][j] == 'Q') { + quiet = 1; + } + if ((argv[i][j] == 'h') || (argv[i][j] == 'H') || + (argv[i][j] == '?')) { + info(); + } + } + } else { + strcpy(filename, argv[i]); + } + } + if (filename[0] == '\0') { + syntax(); + } + if (!strcmp(&filename[strlen(filename) - 5], ".node")) { + filename[strlen(filename) - 5] = '\0'; + start_image = NODE; + } + if (!strcmp(&filename[strlen(filename) - 5], ".poly")) { + filename[strlen(filename) - 5] = '\0'; + start_image = POLY; + } + if (!strcmp(&filename[strlen(filename) - 4], ".ele")) { + filename[strlen(filename) - 4] = '\0'; + start_image = ELE; + } + if (!strcmp(&filename[strlen(filename) - 5], ".edge")) { + filename[strlen(filename) - 5] = '\0'; + start_image = EDGE; + } + if (!strcmp(&filename[strlen(filename) - 5], ".part")) { + filename[strlen(filename) - 5] = '\0'; + start_image = PART; + } + if (!strcmp(&filename[strlen(filename) - 4], ".adj")) { + filename[strlen(filename) - 4] = '\0'; + start_image = ADJ; + } + + increment = 0; + j = 1; + while (filename[j] != '\0') { + if ((filename[j] == '.') && (filename[j + 1] != '\0')) { + increment = j + 1; + } + j++; + } + meshnumber = 0; + if (increment > 0) { + j = increment; + do { + if ((filename[j] >= '0') && (filename[j] <= '9')) { + meshnumber = meshnumber * 10 + (int) (filename[j] - '0'); + } else { + increment = 0; + } + j++; + } while (filename[j] != '\0'); + } + if (increment > 0) { + filename[increment - 1] = '\0'; + } + + if (meshnumber == 0) { + start_inc = 0; + loweriteration = 0; + } else { + start_inc = 1; + loweriteration = meshnumber - 1; + } + set_filenames(filename, loweriteration); +} + +void free_inc(inc) +int inc; +{ + if (loaded[inc][NODE]) { + free(nodeptr[inc]); + } + if (loaded[inc][POLY]) { + if (polynodes[inc] > 0) { + free(polynodeptr[inc]); + } + free(polyedgeptr[inc]); + free(polyholeptr[inc]); + } + if (loaded[inc][ELE]) { + free(eleptr[inc]); + } + if (loaded[inc][PART]) { + free(partpart[inc]); + free(partcenter[inc]); + free(partshift[inc]); + } + if (loaded[inc][EDGE]) { + free(edgeptr[inc]); + free(normptr[inc]); + } + if (loaded[inc][ADJ]) { + free(adjptr[inc]); + } + if (loaded[inc][VORO]) { + free(vnodeptr[inc]); + free(vedgeptr[inc]); + free(vnormptr[inc]); + } +} + +void move_inc(inc) +int inc; +{ + int i; + + free_inc(1 - inc); + for (i = 0; i < IMAGE_TYPES; i++) { + loaded[1 - inc][i] = loaded[inc][i]; + loaded[inc][i] = 0; + xlo[1 - inc][i] = xlo[inc][i]; + ylo[1 - inc][i] = ylo[inc][i]; + xhi[1 - inc][i] = xhi[inc][i]; + yhi[1 - inc][i] = yhi[inc][i]; + } + nodes[1 - inc] = nodes[inc]; + node_dim[1 - inc] = node_dim[inc]; + nodeptr[1 - inc] = nodeptr[inc]; + polynodes[1 - inc] = polynodes[inc]; + poly_dim[1 - inc] = poly_dim[inc]; + polyedges[1 - inc] = polyedges[inc]; + polyholes[1 - inc] = polyholes[inc]; + polynodeptr[1 - inc] = polynodeptr[inc]; + polyedgeptr[1 - inc] = polyedgeptr[inc]; + polyholeptr[1 - inc] = polyholeptr[inc]; + elems[1 - inc] = elems[inc]; + ele_corners[1 - inc] = ele_corners[inc]; + eleptr[1 - inc] = eleptr[inc]; + edges[1 - inc] = edges[inc]; + edgeptr[1 - inc] = edgeptr[inc]; + normptr[1 - inc] = normptr[inc]; + subdomains[1 - inc] = subdomains[inc]; + partpart[1 - inc] = partpart[inc]; + partcenter[1 - inc] = partcenter[inc]; + partshift[1 - inc] = partshift[inc]; + adjsubdomains[1 - inc] = adjsubdomains[inc]; + adjptr[1 - inc] = adjptr[inc]; + vnodes[1 - inc] = vnodes[inc]; + vnode_dim[1 - inc] = vnode_dim[inc]; + vnodeptr[1 - inc] = vnodeptr[inc]; + vedges[1 - inc] = vedges[inc]; + vedgeptr[1 - inc] = vedgeptr[inc]; + vnormptr[1 - inc] = vnormptr[inc]; + firstnumber[1 - inc] = firstnumber[inc]; + firstnumber[inc] = -1; +} + +void unload_inc(inc) +int inc; +{ + int i; + + current_image = NOTHING; + for (i = 0; i < IMAGE_TYPES; i++) { + loaded[inc][i] = 0; + firstnumber[inc] = -1; + } +} + +void showme_init() +{ + current_image = NOTHING; + current_inc = 0; + explosion = STARTEXPLOSION; + unload_inc(0); + unload_inc(1); +} + +char *readline(string, infile, infilename) +char *string; +FILE *infile; +char *infilename; +{ + char *result; + + do { + result = fgets(string, INPUTLINESIZE, infile); + if (result == (char *) NULL) { + printf(" Error: Unexpected end of file in %s.\n", + infilename); + exit(1); + } + while ((*result != '\0') && (*result != '#') + && (*result != '.') && (*result != '+') && (*result != '-') + && ((*result < '0') || (*result > '9'))) { + result++; + } + } while ((*result == '#') || (*result == '\0')); + return result; +} + +char *findfield(string) +char *string; +{ + char *result; + + result = string; + while ((*result != '\0') && (*result != '#') + && (*result != ' ') && (*result != '\t')) { + result++; + } + while ((*result != '\0') && (*result != '#') + && (*result != '.') && (*result != '+') && (*result != '-') + && ((*result < '0') || (*result > '9'))) { + result++; + } + if (*result == '#') { + *result = '\0'; + } + return result; +} + +int load_node(fname, firstnumber, nodes, dim, ptr, xmin, ymin, xmax, ymax) +char *fname; +int *firstnumber; +int *nodes; +int *dim; +REAL **ptr; +REAL *xmin; +REAL *ymin; +REAL *xmax; +REAL *ymax; +{ + FILE *infile; + char inputline[INPUTLINESIZE]; + char *stringptr; + int extras; + int nodemarks; + int index; + int nodenumber; + int i, j; + int smallerr; + REAL x, y; + + *xmin = *ymin = 0.0; + *xmax = *ymax = 1.0; + if (!quiet) { + printf("Opening %s.\n", fname); + } + infile = fopen(fname, "r"); + if (infile == (FILE *) NULL) { + printf(" Error: Cannot access file %s.\n", fname); + return 1; + } + stringptr = readline(inputline, infile, fname); + *nodes = (int) strtol (stringptr, &stringptr, 0); + if (*nodes < 3) { + printf(" Error: %s contains %d points.\n", fname, *nodes); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + *dim = 2; + } else { + *dim = (int) strtol (stringptr, &stringptr, 0); + } + if (*dim < 1) { + printf(" Error: %s has dimensionality %d.\n", fname, *dim); + return 1; + } + if (*dim != 2) { + printf(" I only understand two-dimensional meshes.\n"); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + extras = 0; + } else { + extras = (int) strtol (stringptr, &stringptr, 0); + } + if (extras < 0) { + printf(" Error: %s has negative value for number of attributes.\n", + fname); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + nodemarks = 0; + } else { + nodemarks = (int) strtol (stringptr, &stringptr, 0); + } + if (nodemarks < 0) { + printf(" Warning: %s has negative value for number of point markers.\n", + fname); + } + if (nodemarks > 1) { + printf( + " Warning: %s has value greater than one for number of point markers.\n", + fname); + } + *ptr = (REAL *) malloc((*nodes + 1) * *dim * sizeof(REAL)); + if (*ptr == (REAL *) NULL) { + printf(" Out of memory.\n"); + return 1; + } + index = *dim; + smallerr = 1; + for (i = 0; i < *nodes; i++) { + stringptr = readline(inputline, infile, fname); + nodenumber = (int) strtol (stringptr, &stringptr, 0); + if ((i == 0) && (*firstnumber == -1)) { + if (nodenumber == 0) { + *firstnumber = 0; + } else { + *firstnumber = 1; + } + } + if ((nodenumber != *firstnumber + i) && (smallerr)) { + printf(" Warning: Points in %s are not numbered correctly\n", fname); + printf(" (starting with point %d).\n", *firstnumber + i); + smallerr = 0; + } + for (j = 0; j < *dim; j++) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d is missing a coordinate in %s.\n", + *firstnumber + i, fname); + free(*ptr); + return 1; + } + (*ptr)[index++] = (REAL) strtod(stringptr, &stringptr); + } + } + fclose(infile); + index = *dim; + *xmin = *xmax = (*ptr)[index]; + *ymin = *ymax = (*ptr)[index + 1]; + for (i = 2; i <= *nodes; i++) { + index += *dim; + x = (*ptr)[index]; + y = (*ptr)[index + 1]; + if (x < *xmin) { + *xmin = x; + } + if (y < *ymin) { + *ymin = y; + } + if (x > *xmax) { + *xmax = x; + } + if (y > *ymax) { + *ymax = y; + } + } + if (*xmin == *xmax) { + *xmin -= 0.5; + *xmax += 0.5; + } + if (*ymin == *ymax) { + *ymin -= 0.5; + *ymax += 0.5; + } + return 0; +} + +int load_poly(inc, fname, firstnumber, pnodes, dim, edges, holes, nodeptr, + edgeptr, holeptr, xmin, ymin, xmax, ymax) +int inc; +char *fname; +int *firstnumber; +int *pnodes; +int *dim; +int *edges; +int *holes; +REAL **nodeptr; +int **edgeptr; +REAL **holeptr; +REAL *xmin; +REAL *ymin; +REAL *xmax; +REAL *ymax; +{ + FILE *infile; + char inputline[INPUTLINESIZE]; + char *stringptr; + int extras; + int nodemarks; + int segmentmarks; + int index; + int nodenumber, edgenumber, holenumber; + int maxnode; + int i, j; + int smallerr; + REAL x, y; + + if (!quiet) { + printf("Opening %s.\n", fname); + } + infile = fopen(fname, "r"); + if (infile == (FILE *) NULL) { + printf(" Error: Cannot access file %s.\n", fname); + return 1; + } + stringptr = readline(inputline, infile, fname); + *pnodes = (int) strtol (stringptr, &stringptr, 0); + if (*pnodes == 0) { + if (!loaded[inc][NODE]) { + if (load_image(inc, NODE)) { + return 1; + } + } + maxnode = nodes[inc]; + *xmin = xlo[inc][NODE]; + *ymin = ylo[inc][NODE]; + *xmax = xhi[inc][NODE]; + *ymax = yhi[inc][NODE]; + } else { + if (*pnodes < 1) { + printf(" Error: %s contains %d points.\n", fname, *pnodes); + return 1; + } + maxnode = *pnodes; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + *dim = 2; + } else { + *dim = (int) strtol (stringptr, &stringptr, 0); + } + if (*dim < 1) { + printf(" Error: %s has dimensionality %d.\n", fname, *dim); + return 1; + } + if (*dim != 2) { + printf(" I only understand two-dimensional meshes.\n"); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + extras = 0; + } else { + extras = (int) strtol (stringptr, &stringptr, 0); + } + if (extras < 0) { + printf(" Error: %s has negative value for number of attributes.\n", + fname); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + nodemarks = 0; + } else { + nodemarks = (int) strtol (stringptr, &stringptr, 0); + } + if (nodemarks < 0) { + printf(" Warning: %s has negative value for number of point markers.\n", + fname); + } + if (nodemarks > 1) { + printf( + " Warning: %s has value greater than one for number of point markers.\n", + fname); + } + if (*pnodes > 0) { + *nodeptr = (REAL *) malloc((*pnodes + 1) * *dim * sizeof(REAL)); + if (*nodeptr == (REAL *) NULL) { + printf(" Out of memory.\n"); + return 1; + } + index = *dim; + smallerr = 1; + for (i = 0; i < *pnodes; i++) { + stringptr = readline(inputline, infile, fname); + nodenumber = (int) strtol (stringptr, &stringptr, 0); + if ((i == 0) && (*firstnumber == -1)) { + if (nodenumber == 0) { + *firstnumber = 0; + } else { + *firstnumber = 1; + } + } + if ((nodenumber != *firstnumber + i) && (smallerr)) { + printf(" Warning: Points in %s are not numbered correctly.\n", + fname); + printf(" (starting with point %d).\n", *firstnumber + i); + smallerr = 0; + } + for (j = 0; j < *dim; j++) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d is missing a coordinate in %s.\n", + *firstnumber + i, fname); + free(*nodeptr); + return 1; + } + (*nodeptr)[index++] = (REAL) strtod(stringptr, &stringptr); + } + } + } + stringptr = readline(inputline, infile, fname); + *edges = (int) strtol (stringptr, &stringptr, 0); + if (*edges < 0) { + printf(" Error: %s contains %d segments.\n", fname, *edges); + free(*nodeptr); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + segmentmarks = 0; + } else { + segmentmarks = (int) strtol (stringptr, &stringptr, 0); + } + if (segmentmarks < 0) { + printf(" Error: %s has negative value for number of segment markers.\n", + fname); + free(*nodeptr); + return 1; + } + if (segmentmarks > 1) { + printf( + " Error: %s has value greater than one for number of segment markers.\n", + fname); + free(*nodeptr); + return 1; + } + *edgeptr = (int *) malloc(((*edges + 1) << 1) * sizeof(int)); + if (*edgeptr == (int *) NULL) { + printf(" Out of memory.\n"); + free(*nodeptr); + return 1; + } + index = 2; + smallerr = 1; + for (i = *firstnumber; i < *firstnumber + *edges; i++) { + stringptr = readline(inputline, infile, fname); + edgenumber = (int) strtol (stringptr, &stringptr, 0); + if ((edgenumber != i) && (smallerr)) { + printf(" Warning: Segments in %s are not numbered correctly.\n", + fname); + printf(" (starting with segment %d).\n", i); + smallerr = 0; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Segment %d is missing its endpoints in %s.\n", i, fname); + free(*nodeptr); + free(*edgeptr); + return 1; + } + (*edgeptr)[index] = (int) strtol (stringptr, &stringptr, 0) + 1 - + *firstnumber; + if (((*edgeptr)[index] < 1) || ((*edgeptr)[index] > maxnode)) { + printf("Error: Segment %d has invalid endpoint in %s.\n", i, fname); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Segment %d is missing an endpoint in %s.\n", i, fname); + free(*nodeptr); + free(*edgeptr); + return 1; + } + (*edgeptr)[index + 1] = (int) strtol (stringptr, &stringptr, 0) + 1 - + *firstnumber; + if (((*edgeptr)[index + 1] < 1) || ((*edgeptr)[index + 1] > maxnode)) { + printf("Error: Segment %d has invalid endpoint in %s.\n", i, fname); + return 1; + } + index += 2; + } + stringptr = readline(inputline, infile, fname); + *holes = (int) strtol (stringptr, &stringptr, 0); + if (*holes < 0) { + printf(" Error: %s contains %d holes.\n", fname, *holes); + free(*nodeptr); + free(*edgeptr); + return 1; + } + *holeptr = (REAL *) malloc((*holes + 1) * *dim * sizeof(REAL)); + if (*holeptr == (REAL *) NULL) { + printf(" Out of memory.\n"); + free(*nodeptr); + free(*edgeptr); + return 1; + } + index = *dim; + smallerr = 1; + for (i = *firstnumber; i < *firstnumber + *holes; i++) { + stringptr = readline(inputline, infile, fname); + holenumber = (int) strtol (stringptr, &stringptr, 0); + if ((holenumber != i) && (smallerr)) { + printf(" Warning: Holes in %s are not numbered correctly.\n", fname); + printf(" (starting with hole %d).\n", i); + smallerr = 0; + } + for (j = 0; j < *dim; j++) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Hole %d is missing a coordinate in %s.\n", i, + fname); + free(*nodeptr); + free(*edgeptr); + free(*holeptr); + return 1; + } + (*holeptr)[index++] = (REAL) strtod(stringptr, &stringptr); + } + } + fclose(infile); + if (*pnodes > 0) { + index = *dim; + *xmin = *xmax = (*nodeptr)[index]; + *ymin = *ymax = (*nodeptr)[index + 1]; + for (i = 2; i <= *pnodes; i++) { + index += *dim; + x = (*nodeptr)[index]; + y = (*nodeptr)[index + 1]; + if (x < *xmin) { + *xmin = x; + } + if (y < *ymin) { + *ymin = y; + } + if (x > *xmax) { + *xmax = x; + } + if (y > *ymax) { + *ymax = y; + } + } + } + index = *dim; + for (i = 1; i <= *holes; i++) { + x = (*holeptr)[index]; + y = (*holeptr)[index + 1]; + if (x < *xmin) { + *xmin = x; + } + if (y < *ymin) { + *ymin = y; + } + if (x > *xmax) { + *xmax = x; + } + if (y > *ymax) { + *ymax = y; + } + index += *dim; + } + return 0; +} + +int load_ele(fname, firstnumber, nodes, elems, corners, ptr) +char *fname; +int firstnumber; +int nodes; +int *elems; +int *corners; +int **ptr; +{ + FILE *infile; + char inputline[INPUTLINESIZE]; + char *stringptr; + int extras; + int index; + int elemnumber; + int i, j; + int smallerr; + + if (!quiet) { + printf("Opening %s.\n", fname); + } + infile = fopen(fname, "r"); + if (infile == (FILE *) NULL) { + printf(" Error: Cannot access file %s.\n", fname); + return 1; + } + stringptr = readline(inputline, infile, fname); + *elems = (int) strtol (stringptr, &stringptr, 0); + if (*elems < 1) { + printf(" Error: %s contains %d triangles.\n", fname, *elems); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + *corners = 3; + } else { + *corners = (int) strtol (stringptr, &stringptr, 0); + } + if (*corners < 3) { + printf(" Error: Triangles in %s have only %d corners.\n", fname, + *corners); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + extras = 0; + } else { + extras = (int) strtol (stringptr, &stringptr, 0); + } + if (extras < 0) { + printf(" Error: %s has negative value for extra fields.\n", fname); + return 1; + } + *ptr = (int *) malloc((*elems + 1) * 3 * sizeof(int)); + if (*ptr == (int *) NULL) { + printf(" Out of memory.\n"); + return 1; + } + index = 3; + smallerr = 1; + for (i = firstnumber; i < firstnumber + *elems; i++) { + stringptr = readline(inputline, infile, fname); + elemnumber = (int) strtol (stringptr, &stringptr, 0); + if ((elemnumber != i) && (smallerr)) { + printf(" Warning: Triangles in %s are not numbered correctly.\n", + fname); + printf(" (starting with triangle %d).\n", i); + smallerr = 0; + } + for (j = 0; j < 3; j++) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Triangle %d is missing a corner in %s.\n", i, fname); + free(*ptr); + return 1; + } + (*ptr)[index] = (int) strtol (stringptr, &stringptr, 0) + 1 - + firstnumber; + if (((*ptr)[index] < 1) || ((*ptr)[index] > nodes)) { + printf("Error: Triangle %d has invalid corner in %s.\n", i, fname); + return 1; + } + index++; + } + } + fclose(infile); + return 0; +} + +int load_edge(fname, firstnumber, nodes, edges, edgeptr, normptr) +char *fname; +int firstnumber; +int nodes; +int *edges; +int **edgeptr; +REAL **normptr; +{ + FILE *infile; + char inputline[INPUTLINESIZE]; + char *stringptr; + int index; + int edgenumber; + int edgemarks; + int i; + int smallerr; + + if (!quiet) { + printf("Opening %s.\n", fname); + } + infile = fopen(fname, "r"); + if (infile == (FILE *) NULL) { + printf(" Error: Cannot access file %s.\n", fname); + return 1; + } + stringptr = readline(inputline, infile, fname); + *edges = (int) strtol (stringptr, &stringptr, 0); + if (*edges < 1) { + printf(" Error: %s contains %d edges.\n", fname, *edges); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + edgemarks = 0; + } else { + edgemarks = (int) strtol (stringptr, &stringptr, 0); + } + if (edgemarks < 0) { + printf(" Error: %s has negative value for number of edge markers.\n", + fname); + return 1; + } + if (edgemarks > 1) { + printf( + " Error: %s has value greater than one for number of edge markers.\n", + fname); + return 1; + } + *edgeptr = (int *) malloc(((*edges + 1) << 1) * sizeof(int)); + if (*edgeptr == (int *) NULL) { + printf(" Out of memory.\n"); + return 1; + } + *normptr = (REAL *) malloc(((*edges + 1) << 1) * sizeof(REAL)); + if (*normptr == (REAL *) NULL) { + printf(" Out of memory.\n"); + free(*edgeptr); + return 1; + } + index = 2; + smallerr = 1; + for (i = firstnumber; i < firstnumber + *edges; i++) { + stringptr = readline(inputline, infile, fname); + edgenumber = (int) strtol (stringptr, &stringptr, 0); + if ((edgenumber != i) && (smallerr)) { + printf(" Warning: Edges in %s are not numbered correctly.\n", fname); + printf(" (starting with edge %d).\n", i); + smallerr = 0; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Edge %d is missing its endpoints in %s.\n", i, fname); + free(*edgeptr); + free(*normptr); + return 1; + } + (*edgeptr)[index] = (int) strtol (stringptr, &stringptr, 0) + 1 - + firstnumber; + if (((*edgeptr)[index] < 1) || ((*edgeptr)[index] > nodes)) { + printf("Error: Edge %d has invalid endpoint in %s.\n", i, fname); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Edge %d is missing an endpoint in %s.\n", i, fname); + free(*edgeptr); + free(*normptr); + return 1; + } + (*edgeptr)[index + 1] = (int) strtol (stringptr, &stringptr, 0); + if ((*edgeptr)[index + 1] == -1) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Edge %d is missing its direction in %s.\n", i, fname); + free(*edgeptr); + free(*normptr); + return 1; + } + (*normptr)[index] = (REAL) strtod(stringptr, &stringptr); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Edge %d is missing a direction coordinate in %s.\n", + i, fname); + free(*edgeptr); + free(*normptr); + return 1; + } + (*normptr)[index + 1] = (REAL) strtod(stringptr, &stringptr); + } else { + (*edgeptr)[index + 1] += 1 - firstnumber; + if (((*edgeptr)[index + 1] < 1) || ((*edgeptr)[index + 1] > nodes)) { + printf("Error: Edge %d has invalid endpoint in %s.\n", i, fname); + return 1; + } + } + index += 2; + } + fclose(infile); + return 0; +} + +int load_part(fname, dim, firstnumber, elems, nodeptr, eleptr, parts, + partition, partcenter, partshift) +char *fname; +int dim; +int firstnumber; +int elems; +REAL *nodeptr; +int *eleptr; +int *parts; +int **partition; +REAL **partcenter; +REAL **partshift; +{ + FILE *infile; + char inputline[INPUTLINESIZE]; + char *stringptr; + int partelems; + int index; + int elemnumber; + int i, j; + int smallerr; + int *partsize; + + if (!quiet) { + printf("Opening %s.\n", fname); + } + infile = fopen(fname, "r"); + if (infile == (FILE *) NULL) { + printf(" Error: Cannot access file %s.\n", fname); + return 1; + } + stringptr = readline(inputline, infile, fname); + partelems = (int) strtol (stringptr, &stringptr, 0); + if (partelems != elems) { + printf( + " Error: .ele and .part files do not agree on number of triangles.\n"); + return 1; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + *parts = 1; + } else { + *parts = (int) strtol (stringptr, &stringptr, 0); + } + if (*parts < 1) { + printf(" Error: %s specifies %d subdomains.\n", fname, *parts); + return 1; + } + *partition = (int *) malloc((elems + 1) * sizeof(int)); + if (*partition == (int *) NULL) { + printf(" Out of memory.\n"); + return 1; + } + smallerr = 1; + for (i = firstnumber; i < firstnumber + partelems; i++) { + stringptr = readline(inputline, infile, fname); + elemnumber = (int) strtol (stringptr, &stringptr, 0); + if ((elemnumber != i) && (smallerr)) { + printf(" Warning: Triangles in %s are not numbered correctly.\n", + fname); + printf(" (starting with triangle %d).\n", i); + smallerr = 0; + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Triangle %d has no subdomain in %s.\n", i, fname); + free(*partition); + return 1; + } + (*partition)[i] = (int) strtol (stringptr, &stringptr, 0) - firstnumber; + if (((*partition)[i] >= *parts) || ((*partition)[i] < 0)) { + printf(" Error: Triangle %d of %s has an invalid subdomain.\n", + i, fname); + free(*partition); + return 1; + } + } + fclose(infile); + *partcenter = (REAL *) malloc(((*parts + 1) << 1) * sizeof(REAL)); + if (*partcenter == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + free(*partition); + return 1; + } + *partshift = (REAL *) malloc((*parts << 1) * sizeof(REAL)); + if (*partshift == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + free(*partition); + free(*partcenter); + return 1; + } + partsize = (int *) malloc((*parts + 1) * sizeof(int)); + if (partsize == (int *) NULL) { + printf("Error: Out of memory.\n"); + free(*partition); + free(*partcenter); + free(*partshift); + return 1; + } + index = 3; + for (i = 0; i <= *parts; i++) { + partsize[i] = 0; + (*partcenter)[i << 1] = 0.0; + (*partcenter)[(i << 1) + 1] = 0.0; + } + for (i = 1; i <= elems; i++) { + partsize[(*partition)[i]] += 3; + for (j = 0; j < 3; j++) { + (*partcenter)[(*partition)[i] << 1] += + nodeptr[eleptr[index] * dim]; + (*partcenter)[((*partition)[i] << 1) + 1] += + nodeptr[eleptr[index++] * dim + 1]; + } + } + for (i = 0; i < *parts; i++) { + (*partcenter)[i << 1] /= (REAL) partsize[i]; + (*partcenter)[(i << 1) + 1] /= (REAL) partsize[i]; + (*partcenter)[*parts << 1] += (*partcenter)[i << 1]; + (*partcenter)[(*parts << 1) + 1] += (*partcenter)[(i << 1) + 1]; + } + (*partcenter)[*parts << 1] /= (REAL) *parts; + (*partcenter)[(*parts << 1) + 1] /= (REAL) *parts; + free(partsize); + return 0; +} + +int load_adj(fname, subdomains, ptr) +char *fname; +int *subdomains; +int **ptr; +{ + FILE *infile; + char inputline[INPUTLINESIZE]; + char *stringptr; + int i, j; + + if (!quiet) { + printf("Opening %s.\n", fname); + } + infile = fopen(fname, "r"); + if (infile == (FILE *) NULL) { + printf(" Error: Cannot access file %s.\n", fname); + return 1; + } + stringptr = readline(inputline, infile, fname); + *subdomains = (int) strtol (stringptr, &stringptr, 0); + if (*subdomains < 1) { + printf(" Error: %s contains %d subdomains.\n", fname, *subdomains); + return 1; + } + *ptr = (int *) malloc(*subdomains * *subdomains * sizeof(int)); + if (*ptr == (int *) NULL) { + printf(" Out of memory.\n"); + return 1; + } + for (i = 0; i < *subdomains; i++) { + for (j = 0; j < *subdomains; j++) { + stringptr = readline(inputline, infile, fname); + (*ptr)[i * *subdomains + j] = (int) strtol (stringptr, &stringptr, 0); + } + } + return 0; +} + +void findpartshift(parts, explosion, partcenter, partshift) +int parts; +REAL explosion; +REAL *partcenter; +REAL *partshift; +{ + int i; + + for (i = 0; i < parts; i++) { + partshift[i << 1] = explosion * + (partcenter[i << 1] - partcenter[parts << 1]); + partshift[(i << 1) + 1] = explosion * + (partcenter[(i << 1) + 1] - partcenter[(parts << 1) + 1]); + } +} + +int load_image(inc, image) +int inc; +int image; +{ + int error; + + switch (image) { + case NODE: + error = load_node(nodefilename[inc], &firstnumber[inc], &nodes[inc], + &node_dim[inc], &nodeptr[inc], &xlo[inc][NODE], + &ylo[inc][NODE], &xhi[inc][NODE], &yhi[inc][NODE]); + break; + case POLY: + error = load_poly(inc, polyfilename[inc], &firstnumber[inc], + &polynodes[inc], &poly_dim[inc], &polyedges[inc], + &polyholes[inc], &polynodeptr[inc], &polyedgeptr[inc], + &polyholeptr[inc], + &xlo[inc][POLY], &ylo[inc][POLY], + &xhi[inc][POLY], &yhi[inc][POLY]); + break; + case ELE: + error = load_ele(elefilename[inc], firstnumber[inc], nodes[inc], + &elems[inc], &ele_corners[inc], &eleptr[inc]); + xlo[inc][ELE] = xlo[inc][NODE]; + ylo[inc][ELE] = ylo[inc][NODE]; + xhi[inc][ELE] = xhi[inc][NODE]; + yhi[inc][ELE] = yhi[inc][NODE]; + break; + case EDGE: + error = load_edge(edgefilename[inc], firstnumber[inc], nodes[inc], + &edges[inc], &edgeptr[inc], &normptr[inc]); + xlo[inc][EDGE] = xlo[inc][NODE]; + ylo[inc][EDGE] = ylo[inc][NODE]; + xhi[inc][EDGE] = xhi[inc][NODE]; + yhi[inc][EDGE] = yhi[inc][NODE]; + break; + case PART: + error = load_part(partfilename[inc], node_dim[inc], firstnumber[inc], + elems[inc], nodeptr[inc], eleptr[inc], + &subdomains[inc], &partpart[inc], &partcenter[inc], + &partshift[inc]); + if (!error) { + findpartshift(subdomains[inc], explosion, partcenter[inc], + partshift[inc]); + } + xlo[inc][PART] = xlo[inc][NODE]; + ylo[inc][PART] = ylo[inc][NODE]; + xhi[inc][PART] = xhi[inc][NODE]; + yhi[inc][PART] = yhi[inc][NODE]; + break; + case ADJ: + error = load_adj(adjfilename[inc], &adjsubdomains[inc], &adjptr[inc]); + xlo[inc][ADJ] = xlo[inc][NODE]; + ylo[inc][ADJ] = ylo[inc][NODE]; + xhi[inc][ADJ] = xhi[inc][NODE]; + yhi[inc][ADJ] = yhi[inc][NODE]; + break; + case VORO: + error = load_node(vnodefilename[inc], &firstnumber[inc], &vnodes[inc], + &vnode_dim[inc], &vnodeptr[inc], &xlo[inc][VORO], + &ylo[inc][VORO], &xhi[inc][VORO], &yhi[inc][VORO]); + if (!error) { + error = load_edge(vedgefilename[inc], firstnumber[inc], vnodes[inc], + &vedges[inc], &vedgeptr[inc], &vnormptr[inc]); + } + break; + default: + error = 1; + } + if (!error) { + loaded[inc][image] = 1; + } + return error; +} + +void choose_image(inc, image) +int inc; +int image; +{ + if (!loaded[inc][image]) { + if ((image == ELE) || (image == EDGE) || (image == PART) + || (image == ADJ)) { + if (!loaded[inc][NODE]) { + if (load_image(inc, NODE)) { + return; + } + } + } + if ((image == PART) || (image == ADJ)) { + if (!loaded[inc][ELE]) { + if (load_image(inc, ELE)) { + return; + } + } + } + if (image == ADJ) { + if (!loaded[inc][PART]) { + if (load_image(inc, PART)) { + return; + } + } + } + if (load_image(inc, image)) { + return; + } + } + current_inc = inc; + current_image = image; +} + +Window make_button(name, x, y, width) +char *name; +int x; +int y; +int width; +{ + XSetWindowAttributes attr; + XSizeHints hints; + Window button; + + attr.background_pixel = black; + attr.border_pixel = white; + attr.backing_store = NotUseful; + attr.event_mask = ExposureMask | ButtonReleaseMask | ButtonPressMask; + attr.bit_gravity = SouthWestGravity; + attr.win_gravity = SouthWestGravity; + attr.save_under = False; + button = XCreateWindow(display, mainwindow, x, y, width, BUTTONHEIGHT - 4, + 2, 0, InputOutput, CopyFromParent, + CWBackPixel | CWBorderPixel | CWEventMask | + CWBitGravity | CWWinGravity | CWBackingStore | + CWSaveUnder, &attr); + hints.width = width; + hints.height = BUTTONHEIGHT - 4; + hints.min_width = 0; + hints.min_height = BUTTONHEIGHT - 4; + hints.max_width = width; + hints.max_height = BUTTONHEIGHT - 4; + hints.width_inc = 1; + hints.height_inc = 1; + hints.flags = PMinSize | PMaxSize | PSize | PResizeInc; + XSetStandardProperties(display, button, name, "showme", None, (char **) NULL, + 0, &hints); + return button; +} + +void make_buttons(y) +int y; +{ + int i; + + for (i = 1; i >= 0; i--) { + nodewin[i] = make_button("node", 0, y + (1 - i) * BUTTONHEIGHT, 42); + XMapWindow(display, nodewin[i]); + polywin[i] = make_button("poly", 44, y + (1 - i) * BUTTONHEIGHT, 42); + XMapWindow(display, polywin[i]); + elewin[i] = make_button("ele", 88, y + (1 - i) * BUTTONHEIGHT, 33); + XMapWindow(display, elewin[i]); + edgewin[i] = make_button("edge", 123, y + (1 - i) * BUTTONHEIGHT, 42); + XMapWindow(display, edgewin[i]); + partwin[i] = make_button("part", 167, y + (1 - i) * BUTTONHEIGHT, 42); + XMapWindow(display, partwin[i]); + adjwin[i] = make_button("adj", 211, y + (1 - i) * BUTTONHEIGHT, 33); + XMapWindow(display, adjwin[i]); + voronoiwin[i] = make_button("voro", 246, y + (1 - i) * BUTTONHEIGHT, 42); + XMapWindow(display, voronoiwin[i]); + } + versionpluswin = make_button(" +", 290, y, 52); + XMapWindow(display, versionpluswin); + versionminuswin = make_button(" -", 290, y + BUTTONHEIGHT, 52); + XMapWindow(display, versionminuswin); + + quitwin = make_button("Quit", 0, y + 2 * BUTTONHEIGHT, 42); + XMapWindow(display, quitwin); + leftwin = make_button("<", 44, y + 2 * BUTTONHEIGHT, 14); + XMapWindow(display, leftwin); + rightwin = make_button(">", 60, y + 2 * BUTTONHEIGHT, 14); + XMapWindow(display, rightwin); + upwin = make_button("^", 76, y + 2 * BUTTONHEIGHT, 14); + XMapWindow(display, upwin); + downwin = make_button("v", 92, y + 2 * BUTTONHEIGHT, 14); + XMapWindow(display, downwin); + resetwin = make_button("Reset", 108, y + 2 * BUTTONHEIGHT, 52); + XMapWindow(display, resetwin); + widthpluswin = make_button("Width+", 162, y + 2 * BUTTONHEIGHT, 61); + XMapWindow(display, widthpluswin); + widthminuswin = make_button("-", 225, y + 2 * BUTTONHEIGHT, 14); + XMapWindow(display, widthminuswin); + expwin = make_button("Exp", 241, y + 2 * BUTTONHEIGHT, 33); + XMapWindow(display, expwin); + exppluswin = make_button("+", 276, y + 2 * BUTTONHEIGHT, 14); + XMapWindow(display, exppluswin); + expminuswin = make_button("-", 292, y + 2 * BUTTONHEIGHT, 14); + XMapWindow(display, expminuswin); + fillwin = make_button("Fill", 308, y + 2 * BUTTONHEIGHT, 41); + XMapWindow(display, fillwin); + pswin = make_button("PS", 351, y + 2 * BUTTONHEIGHT, 24); + XMapWindow(display, pswin); + epswin = make_button("EPS", 377, y + 2 * BUTTONHEIGHT, 33); + XMapWindow(display, epswin); +} + +void fill_button(button) +Window button; +{ + int x, y; + unsigned int w, h, d, b; + Window rootw; + + XGetGeometry(display, button, &rootw, &x, &y, &w, &h, &d, &b); + XFillRectangle(display, button, fontgc, 0, 0, w, h); +} + +void draw_buttons() +{ + char numberstring[32]; + char buttonstring[6]; + int i; + + for (i = 1; i >= 0; i--) { + if ((current_image == NODE) && (current_inc == i)) { + fill_button(nodewin[i]); + XDrawString(display, nodewin[i], blackfontgc, 2, 13, "node", 4); + } else { + XClearWindow(display, nodewin[i]); + XDrawString(display, nodewin[i], fontgc, 2, 13, "node", 4); + } + if ((current_image == POLY) && (current_inc == i)) { + fill_button(polywin[i]); + XDrawString(display, polywin[i], blackfontgc, 2, 13, "poly", 4); + } else { + XClearWindow(display, polywin[i]); + XDrawString(display, polywin[i], fontgc, 2, 13, "poly", 4); + } + if ((current_image == ELE) && (current_inc == i)) { + fill_button(elewin[i]); + XDrawString(display, elewin[i], blackfontgc, 2, 13, "ele", 3); + } else { + XClearWindow(display, elewin[i]); + XDrawString(display, elewin[i], fontgc, 2, 13, "ele", 3); + } + if ((current_image == EDGE) && (current_inc == i)) { + fill_button(edgewin[i]); + XDrawString(display, edgewin[i], blackfontgc, 2, 13, "edge", 4); + } else { + XClearWindow(display, edgewin[i]); + XDrawString(display, edgewin[i], fontgc, 2, 13, "edge", 4); + } + if ((current_image == PART) && (current_inc == i)) { + fill_button(partwin[i]); + XDrawString(display, partwin[i], blackfontgc, 2, 13, "part", 4); + } else { + XClearWindow(display, partwin[i]); + XDrawString(display, partwin[i], fontgc, 2, 13, "part", 4); + } + if ((current_image == ADJ) && (current_inc == i)) { + fill_button(adjwin[i]); + XDrawString(display, adjwin[i], blackfontgc, 2, 13, "adj", 3); + } else { + XClearWindow(display, adjwin[i]); + XDrawString(display, adjwin[i], fontgc, 2, 13, "adj", 3); + } + if ((current_image == VORO) && (current_inc == i)) { + fill_button(voronoiwin[i]); + XDrawString(display, voronoiwin[i], blackfontgc, 2, 13, "voro", 4); + } else { + XClearWindow(display, voronoiwin[i]); + XDrawString(display, voronoiwin[i], fontgc, 2, 13, "voro", 4); + } + } + + XClearWindow(display, versionpluswin); + sprintf(numberstring, "%d", loweriteration + 1); + sprintf(buttonstring, "%-4.4s+", numberstring); + XDrawString(display, versionpluswin, fontgc, 2, 13, buttonstring, 5); + XClearWindow(display, versionminuswin); + sprintf(numberstring, "%d", loweriteration); + if (loweriteration == 0) { + sprintf(buttonstring, "%-4.4s", numberstring); + } else { + sprintf(buttonstring, "%-4.4s-", numberstring); + } + XDrawString(display, versionminuswin, fontgc, 2, 13, buttonstring, 5); + + XClearWindow(display, quitwin); + XDrawString(display, quitwin, fontgc, 2, 13, "Quit", 4); + XClearWindow(display, leftwin); + XDrawString(display, leftwin, fontgc, 2, 13, "<", 1); + XClearWindow(display, rightwin); + XDrawString(display, rightwin, fontgc, 2, 13, ">", 1); + XClearWindow(display, upwin); + XDrawString(display, upwin, fontgc, 2, 13, "^", 1); + XClearWindow(display, downwin); + XDrawString(display, downwin, fontgc, 2, 13, "v", 1); + XClearWindow(display, resetwin); + XDrawString(display, resetwin, fontgc, 2, 13, "Reset", 6); + XClearWindow(display, widthpluswin); + if (line_width < 100) { + XDrawString(display, widthpluswin, fontgc, 2, 13, "Width+", 6); + } else { + XDrawString(display, widthpluswin, fontgc, 2, 13, "Width ", 6); + } + XClearWindow(display, widthminuswin); + if (line_width > 1) { + XDrawString(display, widthminuswin, fontgc, 2, 13, "-", 1); + } + XClearWindow(display, expwin); + XClearWindow(display, exppluswin); + XClearWindow(display, expminuswin); + XClearWindow(display, fillwin); + if (current_image == PART) { + if (explode) { + fill_button(expwin); + XDrawString(display, expwin, blackfontgc, 2, 13, "Exp", 3); + } else { + XDrawString(display, expwin, fontgc, 2, 13, "Exp", 3); + } + XDrawString(display, exppluswin, fontgc, 2, 13, "+", 1); + XDrawString(display, expminuswin, fontgc, 2, 13, "-", 1); + if (fillelem) { + fill_button(fillwin); + XDrawString(display, fillwin, blackfontgc, 2, 13, "Fill", 4); + } else { + XDrawString(display, fillwin, fontgc, 2, 13, "Fill", 4); + } + } + XClearWindow(display, pswin); + XDrawString(display, pswin, fontgc, 2, 13, "PS", 2); + XClearWindow(display, epswin); + XDrawString(display, epswin, fontgc, 2, 13, "EPS", 3); +} + +void showme_window(argc, argv) +int argc; +char **argv; +{ + XSetWindowAttributes attr; + XSizeHints hints; + XGCValues fontvalues, linevalues; + XColor alloc_color, exact_color; + int i; + + display = XOpenDisplay((char *) NULL); + if (!display) { + printf("Error: Cannot open display.\n"); + exit(1); + } + screen = DefaultScreen(display); + rootwindow = DefaultRootWindow(display); + black = BlackPixel(display, screen); + white = WhitePixel(display, screen); + windowdepth = DefaultDepth(display, screen); + rootmap = DefaultColormap(display, screen); + width = STARTWIDTH; + height = STARTHEIGHT; + attr.background_pixel = black; + attr.border_pixel = white; + attr.backing_store = NotUseful; + attr.event_mask = ExposureMask | ButtonReleaseMask | ButtonPressMask | + StructureNotifyMask; + attr.bit_gravity = NorthWestGravity; + attr.win_gravity = NorthWestGravity; + attr.save_under = False; + mainwindow = XCreateWindow(display, rootwindow, 0, 0, width, + height + PANELHEIGHT, 3, 0, + InputOutput, CopyFromParent, + CWBackPixel | CWBorderPixel | CWEventMask | + CWBitGravity | CWWinGravity | CWBackingStore | + CWSaveUnder, &attr); + hints.width = width; + hints.height = height + PANELHEIGHT; + hints.min_width = MINWIDTH; + hints.min_height = MINHEIGHT + PANELHEIGHT; + hints.width_inc = 1; + hints.height_inc = 1; + hints.flags = PMinSize | PSize | PResizeInc; + XSetStandardProperties(display, mainwindow, "Show Me", "showme", None, + argv, argc, &hints); + XChangeProperty(display, mainwindow, XA_WM_CLASS, XA_STRING, 8, + PropModeReplace, "showme\0Archimedes", 18); + XClearWindow(display, mainwindow); + XMapWindow(display, mainwindow); + if ((windowdepth > 1) && + XAllocNamedColor(display, rootmap, "yellow", &alloc_color, + &exact_color)) { + color = 1; + explode = bw_ps; + fontvalues.foreground = alloc_color.pixel; + linevalues.foreground = alloc_color.pixel; + showme_foreground = alloc_color.pixel; + for (i = 0; i < 64; i++) { + if (XAllocNamedColor(display, rootmap, colorname[i], &alloc_color, + &rgb[i])) { + colors[i] = alloc_color.pixel; + } else { + colors[i] = white; + rgb[i].red = alloc_color.red; + rgb[i].green = alloc_color.green; + rgb[i].blue = alloc_color.blue; + if (!quiet) { + printf("Warning: I could not allocate %s.\n", colorname[i]); + } + } + } + } else { + color = 0; + fillelem = 0; + explode = 1; + fontvalues.foreground = white; + linevalues.foreground = white; + showme_foreground = white; + } + font = XLoadQueryFont(display, "9x15"); + fontvalues.background = black; + fontvalues.font = font->fid; + fontvalues.fill_style = FillSolid; + fontvalues.line_width = 2; + fontgc = XCreateGC(display, rootwindow, GCForeground | GCBackground | + GCFont | GCLineWidth | GCFillStyle, &fontvalues); + fontvalues.foreground = black; + blackfontgc = XCreateGC(display, rootwindow, GCForeground | GCBackground | + GCFont | GCLineWidth | GCFillStyle, &fontvalues); + linevalues.background = black; + linevalues.line_width = line_width; + linevalues.cap_style = CapRound; + linevalues.join_style = JoinRound; + linevalues.fill_style = FillSolid; + linegc = XCreateGC(display, rootwindow, GCForeground | GCBackground | + GCLineWidth | GCCapStyle | GCJoinStyle | GCFillStyle, + &linevalues); + trianglegc = XCreateGC(display, rootwindow, GCForeground | GCBackground | + GCLineWidth | GCCapStyle | GCJoinStyle | GCFillStyle, + &linevalues); + make_buttons(height); + XFlush(display); +} + +void draw_node(nodes, dim, ptr, xscale, yscale, xoffset, yoffset) +int nodes; +int dim; +REAL *ptr; +REAL xscale; +REAL yscale; +REAL xoffset; +REAL yoffset; +{ + int i; + int index; + + index = dim; + for (i = 1; i <= nodes; i++) { + XFillRectangle(display, mainwindow, linegc, + (int) (ptr[index] * xscale + xoffset) - (line_width >> 1), + (int) (ptr[index + 1] * yscale + yoffset) - + (line_width >> 1), line_width, line_width); + index += dim; + } +} + +void draw_poly(nodes, dim, edges, holes, nodeptr, edgeptr, holeptr, + xscale, yscale, xoffset, yoffset) +int nodes; +int dim; +int edges; +int holes; +REAL *nodeptr; +int *edgeptr; +REAL *holeptr; +REAL xscale; +REAL yscale; +REAL xoffset; +REAL yoffset; +{ + int i; + int index; + REAL *point1, *point2; + int x1, y1, x2, y2; + + index = dim; + for (i = 1; i <= nodes; i++) { + XFillRectangle(display, mainwindow, linegc, + (int) (nodeptr[index] * xscale + xoffset) - + (line_width >> 1), + (int) (nodeptr[index + 1] * yscale + yoffset) - + (line_width >> 1), line_width, line_width); + index += dim; + } + index = 2; + for (i = 1; i <= edges; i++) { + point1 = &nodeptr[edgeptr[index++] * dim]; + point2 = &nodeptr[edgeptr[index++] * dim]; + XDrawLine(display, mainwindow, linegc, + (int) (point1[0] * xscale + xoffset), + (int) (point1[1] * yscale + yoffset), + (int) (point2[0] * xscale + xoffset), + (int) (point2[1] * yscale + yoffset)); + } + index = dim; + if (color) { + XSetForeground(display, linegc, colors[0]); + } + for (i = 1; i <= holes; i++) { + x1 = (int) (holeptr[index] * xscale + xoffset) - 3; + y1 = (int) (holeptr[index + 1] * yscale + yoffset) - 3; + x2 = x1 + 6; + y2 = y1 + 6; + XDrawLine(display, mainwindow, linegc, x1, y1, x2, y2); + XDrawLine(display, mainwindow, linegc, x1, y2, x2, y1); + index += dim; + } + XSetForeground(display, linegc, showme_foreground); +} + +void draw_ele(inc, elems, corners, ptr, partition, shift, + xscale, yscale, xoffset, yoffset) +int inc; +int elems; +int corners; +int *ptr; +int *partition; +REAL *shift; +REAL xscale; +REAL yscale; +REAL xoffset; +REAL yoffset; +{ + int i, j; + int index; + REAL shiftx, shifty; + REAL *prevpoint, *nowpoint; + XPoint *vertices; + + if (color && fillelem && (partition != (int *) NULL)) { + vertices = (XPoint *) malloc(3 * sizeof(XPoint)); + if (vertices == (XPoint *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + index = 3; + for (i = 1; i <= elems; i++) { + if ((partition != (int *) NULL) && explode) { + shiftx = shift[partition[i] << 1]; + shifty = shift[(partition[i] << 1) + 1]; + } + if (color && (partition != (int *) NULL)) { + if (fillelem) { + XSetForeground(display, trianglegc, colors[partition[i] & 63]); + } else { + XSetForeground(display, linegc, colors[partition[i] & 63]); + } + } + if (color && fillelem && (partition != (int *) NULL)) { + if ((partition != (int *) NULL) && explode) { + for (j = 0; j < 3; j++) { + nowpoint = &nodeptr[inc][ptr[index + j] * node_dim[inc]]; + vertices[j].x = (nowpoint[0] + shiftx) * xscale + xoffset; + vertices[j].y = (nowpoint[1] + shifty) * yscale + yoffset; + } + } else { + for (j = 0; j < 3; j++) { + nowpoint = &nodeptr[inc][ptr[index + j] * node_dim[inc]]; + vertices[j].x = nowpoint[0] * xscale + xoffset; + vertices[j].y = nowpoint[1] * yscale + yoffset; + } + } + XFillPolygon(display, mainwindow, trianglegc, vertices, 3, + Convex, CoordModeOrigin); + } + prevpoint = &nodeptr[inc][ptr[index + 2] * node_dim[inc]]; + if ((partition != (int *) NULL) && explode) { + for (j = 0; j < 3; j++) { + nowpoint = &nodeptr[inc][ptr[index++] * node_dim[inc]]; + XDrawLine(display, mainwindow, linegc, + (int) ((prevpoint[0] + shiftx) * xscale + xoffset), + (int) ((prevpoint[1] + shifty) * yscale + yoffset), + (int) ((nowpoint[0] + shiftx) * xscale + xoffset), + (int) ((nowpoint[1] + shifty) * yscale + yoffset)); + prevpoint = nowpoint; + } + } else { + for (j = 0; j < 3; j++) { + nowpoint = &nodeptr[inc][ptr[index++] * node_dim[inc]]; + XDrawLine(display, mainwindow, linegc, + (int) (prevpoint[0] * xscale + xoffset), + (int) (prevpoint[1] * yscale + yoffset), + (int) (nowpoint[0] * xscale + xoffset), + (int) (nowpoint[1] * yscale + yoffset)); + prevpoint = nowpoint; + } + } + } + if (color && fillelem && (partition != (int *) NULL)) { + free(vertices); + } + XSetForeground(display, linegc, showme_foreground); +} + +void draw_edge(nodes, dim, edges, nodeptr, edgeptr, normptr, + xscale, yscale, xoffset, yoffset) +int nodes; +int dim; +int edges; +REAL *nodeptr; +int *edgeptr; +REAL *normptr; +REAL xscale; +REAL yscale; +REAL xoffset; +REAL yoffset; +{ + int i; + int index; + REAL *point1, *point2; + REAL normx, normy; + REAL normmult, normmultx, normmulty; + REAL windowxmin, windowymin, windowxmax, windowymax; + + index = 2; + for (i = 1; i <= edges; i++) { + point1 = &nodeptr[edgeptr[index++] * dim]; + if (edgeptr[index] == -1) { + normx = normptr[index - 1]; + normy = normptr[index++]; + normmultx = 0.0; + if (normx > 0) { + windowxmax = (width - 1 - xoffset) / xscale; + normmultx = (windowxmax - point1[0]) / normx; + } else if (normx < 0) { + windowxmin = -xoffset / xscale; + normmultx = (windowxmin - point1[0]) / normx; + } + normmulty = 0.0; + if (normy > 0) { + windowymax = -yoffset / yscale; + normmulty = (windowymax - point1[1]) / normy; + } else if (normy < 0) { + windowymin = (height - 1 - yoffset) / yscale; + normmulty = (windowymin - point1[1]) / normy; + } + if (normmultx == 0.0) { + normmult = normmulty; + } else if (normmulty == 0.0) { + normmult = normmultx; + } else if (normmultx < normmulty) { + normmult = normmultx; + } else { + normmult = normmulty; + } + if (normmult > 0.0) { + XDrawLine(display, mainwindow, linegc, + (int) (point1[0] * xscale + xoffset), + (int) (point1[1] * yscale + yoffset), + (int) ((point1[0] + normmult * normx) * xscale + xoffset), + (int) ((point1[1] + normmult * normy) * yscale + yoffset)); + } + } else { + point2 = &nodeptr[edgeptr[index++] * dim]; + XDrawLine(display, mainwindow, linegc, + (int) (point1[0] * xscale + xoffset), + (int) (point1[1] * yscale + yoffset), + (int) (point2[0] * xscale + xoffset), + (int) (point2[1] * yscale + yoffset)); + } + } +} + +void draw_adj(dim, subdomains, ptr, center, xscale, yscale, + xoffset, yoffset) +int dim; +int subdomains; +int *ptr; +REAL *center; +REAL xscale; +REAL yscale; +REAL xoffset; +REAL yoffset; +{ + int i, j; + REAL *point1, *point2; + + for (i = 0; i < subdomains; i++) { + for (j = i + 1; j < subdomains; j++) { + if (ptr[i * subdomains + j]) { + point1 = ¢er[i * dim]; + point2 = ¢er[j * dim]; + XDrawLine(display, mainwindow, linegc, + (int) (point1[0] * xscale + xoffset), + (int) (point1[1] * yscale + yoffset), + (int) (point2[0] * xscale + xoffset), + (int) (point2[1] * yscale + yoffset)); + } + } + } + for (i = 0; i < subdomains; i++) { + point1 = ¢er[i * dim]; + if (color) { + XSetForeground(display, linegc, colors[i & 63]); + } + XFillArc(display, mainwindow, linegc, + (int) (point1[0] * xscale + xoffset) - 5 - (line_width >> 1), + (int) (point1[1] * yscale + yoffset) - 5 - (line_width >> 1), + line_width + 10, line_width + 10, 0, 23040); + } + XSetForeground(display, linegc, showme_foreground); +} + +void draw(inc, image, xmin, ymin, xmax, ymax) +int inc; +int image; +REAL xmin; +REAL ymin; +REAL xmax; +REAL ymax; +{ + draw_buttons(); + XClearWindow(display, mainwindow); + if (image == NOTHING) { + return; + } + if (!loaded[inc][image]) { + return; + } + if ((image == PART) && explode) { + xmin += (xmin - partcenter[inc][subdomains[inc] << 1]) * explosion; + xmax += (xmax - partcenter[inc][subdomains[inc] << 1]) * explosion; + ymin += (ymin - partcenter[inc][(subdomains[inc] << 1) + 1]) * explosion; + ymax += (ymax - partcenter[inc][(subdomains[inc] << 1) + 1]) * explosion; + } + xscale = (REAL) (width - line_width - 4) / (xmax - xmin); + yscale = (REAL) (height - line_width - 4) / (ymax - ymin); + if (xscale > yscale) { + xscale = yscale; + } else { + yscale = xscale; + } + xoffset = 0.5 * ((REAL) width - xscale * (xmax - xmin)) - + xscale * xmin; + yoffset = (REAL) height - 0.5 * ((REAL) height - yscale * (ymax - ymin)) + + yscale * ymin; + yscale = - yscale; + switch(image) { + case NODE: + draw_node(nodes[inc], node_dim[inc], nodeptr[inc], + xscale, yscale, xoffset, yoffset); + break; + case POLY: + if (polynodes[inc] > 0) { + draw_poly(polynodes[inc], poly_dim[inc], polyedges[inc], + polyholes[inc], polynodeptr[inc], polyedgeptr[inc], + polyholeptr[inc], + xscale, yscale, xoffset, yoffset); + } else { + draw_poly(nodes[inc], node_dim[inc], polyedges[inc], + polyholes[inc], nodeptr[inc], polyedgeptr[inc], + polyholeptr[inc], + xscale, yscale, xoffset, yoffset); + } + break; + case ELE: + draw_ele(inc, elems[inc], ele_corners[inc], eleptr[inc], + (int *) NULL, (REAL *) NULL, + xscale, yscale, xoffset, yoffset); + break; + case EDGE: + draw_edge(nodes[inc], node_dim[inc], edges[inc], + nodeptr[inc], edgeptr[inc], normptr[inc], + xscale, yscale, xoffset, yoffset); + break; + case PART: + draw_ele(inc, elems[inc], ele_corners[inc], eleptr[inc], + partpart[inc], partshift[inc], + xscale, yscale, xoffset, yoffset); + break; + case ADJ: + draw_adj(node_dim[inc], adjsubdomains[inc], adjptr[inc], partcenter[inc], + xscale, yscale, xoffset, yoffset); + break; + case VORO: + if (loaded[inc][NODE]) { + draw_node(nodes[inc], node_dim[inc], nodeptr[inc], + xscale, yscale, xoffset, yoffset); + } + draw_edge(vnodes[inc], vnode_dim[inc], vedges[inc], + vnodeptr[inc], vedgeptr[inc], vnormptr[inc], + xscale, yscale, xoffset, yoffset); + break; + default: + break; + } +} + +void addps(instring, outstring, eps) +char *instring; +char *outstring; +int eps; +{ + strcpy(outstring, instring); + if (eps) { + strcat(outstring, ".eps"); + } else { + strcat(outstring, ".ps"); + } +} + +int print_head(fname, file, llcornerx, llcornery, eps) +char *fname; +FILE **file; +int llcornerx; +int llcornery; +int eps; +{ + if (!quiet) { + printf("Writing %s\n", fname); + } + *file = fopen(fname, "w"); + if (*file == (FILE *) NULL) { + printf(" Error: Could not open %s\n", fname); + return 1; + } + if (eps) { + fprintf(*file, "%%!PS-Adobe-2.0 EPSF-2.0\n"); + } else { + fprintf(*file, "%%!PS-Adobe-2.0\n"); + } + fprintf(*file, "%%%%BoundingBox: %d %d %d %d\n", llcornerx, llcornery, + 612 - llcornerx, 792 - llcornery); + fprintf(*file, "%%%%Creator: Show Me\n"); + fprintf(*file, "%%%%EndComments\n\n"); + fprintf(*file, "1 setlinecap\n"); + fprintf(*file, "1 setlinejoin\n"); + fprintf(*file, "%d setlinewidth\n", line_width); + fprintf(*file, "%d %d moveto\n", llcornerx, llcornery); + fprintf(*file, "%d %d lineto\n", 612 - llcornerx, llcornery); + fprintf(*file, "%d %d lineto\n", 612 - llcornerx, 792 - llcornery); + fprintf(*file, "%d %d lineto\n", llcornerx, 792 - llcornery); + fprintf(*file, "closepath\nclip\nnewpath\n"); + return 0; +} + +void print_node(nodefile, nodes, dim, ptr, xscale, yscale, + xoffset, yoffset) +FILE *nodefile; +int nodes; +int dim; +REAL *ptr; +REAL xscale; +REAL yscale; +REAL xoffset; +REAL yoffset; +{ + int i; + int index; + + index = dim; + for (i = 1; i <= nodes; i++) { + fprintf(nodefile, "%d %d %d 0 360 arc\nfill\n", + (int) (ptr[index] * xscale + xoffset), + (int) (ptr[index + 1] * yscale + yoffset), + 1 + (line_width >> 1)); + index += dim; + } +} + +void print_poly(polyfile, nodes, dim, edges, holes, nodeptr, edgeptr, holeptr, + xscale, yscale, xoffset, yoffset) +FILE *polyfile; +int nodes; +int dim; +int edges; +int holes; +REAL *nodeptr; +int *edgeptr; +REAL *holeptr; +REAL xscale; +REAL yscale; +REAL xoffset; +REAL yoffset; +{ + int i; + int index; + REAL *point1, *point2; + + index = dim; + for (i = 1; i <= nodes; i++) { + fprintf(polyfile, "%d %d %d 0 360 arc\nfill\n", + (int) (nodeptr[index] * xscale + xoffset), + (int) (nodeptr[index + 1] * yscale + yoffset), + 1 + (line_width >> 1)); + index += dim; + } + index = 2; + for (i = 1; i <= edges; i++) { + point1 = &nodeptr[edgeptr[index++] * dim]; + point2 = &nodeptr[edgeptr[index++] * dim]; + fprintf(polyfile, "%d %d moveto\n", + (int) (point1[0] * xscale + xoffset), + (int) (point1[1] * yscale + yoffset)); + fprintf(polyfile, "%d %d lineto\nstroke\n", + (int) (point2[0] * xscale + xoffset), + (int) (point2[1] * yscale + yoffset)); + } +} + +void print_ele(elefile, nodes, dim, elems, corners, nodeptr, eleptr, + partition, shift, + xscale, yscale, xoffset, yoffset, llcornerx, llcornery) +FILE *elefile; +int nodes; +int dim; +int elems; +int corners; +REAL *nodeptr; +int *eleptr; +int *partition; +REAL *shift; +REAL xscale; +REAL yscale; +REAL xoffset; +REAL yoffset; +int llcornerx; +int llcornery; +{ + int i, j; + int index, colorindex; + REAL shiftx, shifty; + REAL *nowpoint; + + index = 3; + if ((partition != (int *) NULL) && !bw_ps) { + fprintf(elefile, "0 0 0 setrgbcolor\n"); + fprintf(elefile, "%d %d moveto\n", llcornerx, llcornery); + fprintf(elefile, "%d %d lineto\n", 612 - llcornerx, llcornery); + fprintf(elefile, "%d %d lineto\n", 612 - llcornerx, 792 - llcornery); + fprintf(elefile, "%d %d lineto\n", llcornerx, 792 - llcornery); + fprintf(elefile, "fill\n"); + } + for (i = 1; i <= elems; i++) { + if ((partition != (int *) NULL) && !bw_ps) { + colorindex = partition[i] & 63; + fprintf(elefile, "%6.3f %6.3f %6.3f setrgbcolor\n", + (REAL) rgb[colorindex].red / 65535.0, + (REAL) rgb[colorindex].green / 65535.0, + (REAL) rgb[colorindex].blue / 65535.0); + } + nowpoint = &nodeptr[eleptr[index + 2] * dim]; + if ((partition != (int *) NULL) && (explode || bw_ps)) { + shiftx = shift[partition[i] << 1]; + shifty = shift[(partition[i] << 1) + 1]; + fprintf(elefile, "%d %d moveto\n", + (int) ((nowpoint[0] + shiftx) * xscale + xoffset), + (int) ((nowpoint[1] + shifty) * yscale + yoffset)); + for (j = 0; j < 3; j++) { + nowpoint = &nodeptr[eleptr[index++] * dim]; + fprintf(elefile, "%d %d lineto\n", + (int) ((nowpoint[0] + shiftx) * xscale + xoffset), + (int) ((nowpoint[1] + shifty) * yscale + yoffset)); + } + } else { + fprintf(elefile, "%d %d moveto\n", + (int) (nowpoint[0] * xscale + xoffset), + (int) (nowpoint[1] * yscale + yoffset)); + for (j = 0; j < 3; j++) { + nowpoint = &nodeptr[eleptr[index++] * dim]; + fprintf(elefile, "%d %d lineto\n", + (int) (nowpoint[0] * xscale + xoffset), + (int) (nowpoint[1] * yscale + yoffset)); + } + } + if (fillelem && !bw_ps) { + fprintf(elefile, "gsave\nfill\ngrestore\n1 1 0 setrgbcolor\n"); + } + fprintf(elefile, "stroke\n"); + } +} + +void print_edge(edgefile, nodes, dim, edges, nodeptr, edgeptr, normptr, + xscale, yscale, xoffset, yoffset, llcornerx, llcornery) +FILE *edgefile; +int nodes; +int dim; +int edges; +REAL *nodeptr; +int *edgeptr; +REAL *normptr; +REAL xscale; +REAL yscale; +REAL xoffset; +REAL yoffset; +int llcornerx; +int llcornery; +{ + int i; + int index; + REAL *point1, *point2; + REAL normx, normy; + REAL normmult, normmultx, normmulty; + REAL windowxmin, windowymin, windowxmax, windowymax; + + index = 2; + for (i = 1; i <= edges; i++) { + point1 = &nodeptr[edgeptr[index++] * dim]; + if (edgeptr[index] == -1) { + normx = normptr[index - 1]; + normy = normptr[index++]; + normmultx = 0.0; + if (normx > 0) { + windowxmax = ((REAL) (612 - llcornerx) - xoffset) / xscale; + normmultx = (windowxmax - point1[0]) / normx; + } else if (normx < 0) { + windowxmin = ((REAL) llcornerx - xoffset) / xscale; + normmultx = (windowxmin - point1[0]) / normx; + } + normmulty = 0.0; + if (normy > 0) { + windowymax = ((REAL) (792 - llcornery) - yoffset) / yscale; + normmulty = (windowymax - point1[1]) / normy; + } else if (normy < 0) { + windowymin = ((REAL) llcornery - yoffset) / yscale; + normmulty = (windowymin - point1[1]) / normy; + } + if (normmultx == 0.0) { + normmult = normmulty; + } else if (normmulty == 0.0) { + normmult = normmultx; + } else if (normmultx < normmulty) { + normmult = normmultx; + } else { + normmult = normmulty; + } + if (normmult > 0.0) { + fprintf(edgefile, "%d %d moveto\n", + (int) (point1[0] * xscale + xoffset), + (int) (point1[1] * yscale + yoffset)); + fprintf(edgefile, "%d %d lineto\nstroke\n", + (int) ((point1[0] + normmult * normx) * xscale + xoffset), + (int) ((point1[1] + normmult * normy) * yscale + yoffset)); + } + } else { + point2 = &nodeptr[edgeptr[index++] * dim]; + fprintf(edgefile, "%d %d moveto\n", + (int) (point1[0] * xscale + xoffset), + (int) (point1[1] * yscale + yoffset)); + fprintf(edgefile, "%d %d lineto\nstroke\n", + (int) (point2[0] * xscale + xoffset), + (int) (point2[1] * yscale + yoffset)); + } + } +} + +void print_adj(adjfile, dim, subdomains, ptr, center, xscale, yscale, + xoffset, yoffset, llcornerx, llcornery) +FILE *adjfile; +int dim; +int subdomains; +int *ptr; +REAL *center; +REAL xscale; +REAL yscale; +REAL xoffset; +REAL yoffset; +int llcornerx; +int llcornery; +{ + int i, j; + REAL *point1, *point2; + int colorindex; + + if (!bw_ps) { + fprintf(adjfile, "0 0 0 setrgbcolor\n"); + fprintf(adjfile, "%d %d moveto\n", llcornerx, llcornery); + fprintf(adjfile, "%d %d lineto\n", 612 - llcornerx, llcornery); + fprintf(adjfile, "%d %d lineto\n", 612 - llcornerx, 792 - llcornery); + fprintf(adjfile, "%d %d lineto\n", llcornerx, 792 - llcornery); + fprintf(adjfile, "fill\n"); + fprintf(adjfile, "1 1 0 setrgbcolor\n"); + } + for (i = 0; i < subdomains; i++) { + for (j = i + 1; j < subdomains; j++) { + if (ptr[i * subdomains + j]) { + point1 = ¢er[i * dim]; + point2 = ¢er[j * dim]; + fprintf(adjfile, "%d %d moveto\n", + (int) (point1[0] * xscale + xoffset), + (int) (point1[1] * yscale + yoffset)); + fprintf(adjfile, "%d %d lineto\nstroke\n", + (int) (point2[0] * xscale + xoffset), + (int) (point2[1] * yscale + yoffset)); + } + } + } + for (i = 0; i < subdomains; i++) { + point1 = ¢er[i * dim]; + if (!bw_ps) { + colorindex = i & 63; + fprintf(adjfile, "%6.3f %6.3f %6.3f setrgbcolor\n", + (REAL) rgb[colorindex].red / 65535.0, + (REAL) rgb[colorindex].green / 65535.0, + (REAL) rgb[colorindex].blue / 65535.0); + fprintf(adjfile, "%d %d %d 0 360 arc\nfill\n", + (int) (point1[0] * xscale + xoffset), + (int) (point1[1] * yscale + yoffset), + 5 + (line_width >> 1)); + } else { + fprintf(adjfile, "%d %d %d 0 360 arc\nfill\n", + (int) (point1[0] * xscale + xoffset), + (int) (point1[1] * yscale + yoffset), + 3 + (line_width >> 1)); + } + } +} + +void print(inc, image, xmin, ymin, xmax, ymax, eps) +int inc; +int image; +REAL xmin; +REAL ymin; +REAL xmax; +REAL ymax; +int eps; +{ + REAL xxscale, yyscale, xxoffset, yyoffset; + char psfilename[FILENAMESIZE]; + int llcornerx, llcornery; + FILE *psfile; + + if (image == NOTHING) { + return; + } + if (!loaded[inc][image]) { + return; + } + if ((image == PART) && (explode || bw_ps)) { + xmin += (xmin - partcenter[inc][subdomains[inc] << 1]) * explosion; + xmax += (xmax - partcenter[inc][subdomains[inc] << 1]) * explosion; + ymin += (ymin - partcenter[inc][(subdomains[inc] << 1) + 1]) * explosion; + ymax += (ymax - partcenter[inc][(subdomains[inc] << 1) + 1]) * explosion; + } + xxscale = (460.0 - (REAL) line_width) / (xmax - xmin); + yyscale = (640.0 - (REAL) line_width) / (ymax - ymin); + if (xxscale > yyscale) { + xxscale = yyscale; + llcornerx = (604 - (int) (yyscale * (xmax - xmin)) - line_width) >> 1; + llcornery = 72; + } else { + yyscale = xxscale; + llcornerx = 72; + llcornery = (784 - (int) (xxscale * (ymax - ymin)) - line_width) >> 1; + } + xxoffset = 0.5 * (612.0 - xxscale * (xmax - xmin)) - xxscale * xmin + + (line_width >> 1); + yyoffset = 0.5 * (792.0 - yyscale * (ymax - ymin)) - yyscale * ymin + + (line_width >> 1); + switch(image) { + case NODE: + addps(nodefilename[inc], psfilename, eps); + break; + case POLY: + addps(polyfilename[inc], psfilename, eps); + break; + case ELE: + addps(elefilename[inc], psfilename, eps); + break; + case EDGE: + addps(edgefilename[inc], psfilename, eps); + break; + case PART: + addps(partfilename[inc], psfilename, eps); + break; + case ADJ: + addps(adjfilename[inc], psfilename, eps); + break; + case VORO: + addps(vedgefilename[inc], psfilename, eps); + break; + default: + break; + } + if (print_head(psfilename, &psfile, llcornerx, llcornery, eps)) { + return; + } + switch(image) { + case NODE: + print_node(psfile, nodes[inc], node_dim[inc], nodeptr[inc], + xxscale, yyscale, xxoffset, yyoffset); + break; + case POLY: + if (polynodes[inc] > 0) { + print_poly(psfile, polynodes[inc], poly_dim[inc], polyedges[inc], + polyholes[inc], polynodeptr[inc], polyedgeptr[inc], + polyholeptr[inc], xxscale, yyscale, xxoffset, yyoffset); + } else { + print_poly(psfile, nodes[inc], node_dim[inc], polyedges[inc], + polyholes[inc], nodeptr[inc], polyedgeptr[inc], + polyholeptr[inc], xxscale, yyscale, xxoffset, yyoffset); + } + break; + case ELE: + print_ele(psfile, nodes[inc], node_dim[inc], elems[inc], + ele_corners[inc], nodeptr[inc], eleptr[inc], + (int *) NULL, (REAL *) NULL, + xxscale, yyscale, xxoffset, yyoffset, llcornerx, llcornery); + break; + case EDGE: + print_edge(psfile, nodes[inc], node_dim[inc], edges[inc], + nodeptr[inc], edgeptr[inc], normptr[inc], + xxscale, yyscale, xxoffset, yyoffset, llcornerx, llcornery); + break; + case PART: + print_ele(psfile, nodes[inc], node_dim[inc], elems[inc], + ele_corners[inc], nodeptr[inc], eleptr[inc], + partpart[inc], partshift[inc], + xxscale, yyscale, xxoffset, yyoffset, llcornerx, llcornery); + break; + case ADJ: + print_adj(psfile, node_dim[inc], adjsubdomains[inc], adjptr[inc], + partcenter[inc], + xxscale, yyscale, xxoffset, yyoffset, llcornerx, llcornery); + break; + case VORO: + print_edge(psfile, vnodes[inc], vnode_dim[inc], vedges[inc], + vnodeptr[inc], vedgeptr[inc], vnormptr[inc], + xxscale, yyscale, xxoffset, yyoffset, llcornerx, llcornery); + break; + default: + break; + } + if (!eps) { + fprintf(psfile, "showpage\n"); + } + fclose(psfile); +} + +int main(argc, argv) +int argc; +char **argv; +{ + REAL xmin, ymin, xmax, ymax; + REAL xptr, yptr, xspan, yspan; + int past_image; + int new_image; + int new_inc; + + parsecommandline(argc, argv); + showme_init(); + choose_image(start_inc, start_image); + showme_window(argc, argv); + + if (current_image != NOTHING) { + xmin = xlo[current_inc][current_image]; + ymin = ylo[current_inc][current_image]; + xmax = xhi[current_inc][current_image]; + ymax = yhi[current_inc][current_image]; + zoom = 0; + } + + XMaskEvent(display, ExposureMask, &event); + while (1) { + switch (event.type) { + case ButtonRelease: + if (event.xany.window == quitwin) { + XDestroyWindow(display, mainwindow); + XCloseDisplay(display); + return 0; + } else if (event.xany.window == leftwin) { + xspan = 0.25 * (xmax - xmin); + xmin += xspan; + xmax += xspan; + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } else if (event.xany.window == rightwin) { + xspan = 0.25 * (xmax - xmin); + xmin -= xspan; + xmax -= xspan; + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } else if (event.xany.window == upwin) { + yspan = 0.25 * (ymax - ymin); + ymin -= yspan; + ymax -= yspan; + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } else if (event.xany.window == downwin) { + yspan = 0.25 * (ymax - ymin); + ymin += yspan; + ymax += yspan; + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } else if (event.xany.window == resetwin) { + xmin = xlo[current_inc][current_image]; + ymin = ylo[current_inc][current_image]; + xmax = xhi[current_inc][current_image]; + ymax = yhi[current_inc][current_image]; + zoom = 0; + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } else if (event.xany.window == widthpluswin) { + if (line_width < 100) { + line_width++; + XSetLineAttributes(display, linegc, line_width, LineSolid, + CapRound, JoinRound); + XSetLineAttributes(display, trianglegc, line_width, LineSolid, + CapRound, JoinRound); + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } + } else if (event.xany.window == widthminuswin) { + if (line_width > 1) { + line_width--; + XSetLineAttributes(display, linegc, line_width, LineSolid, + CapRound, JoinRound); + XSetLineAttributes(display, trianglegc, line_width, LineSolid, + CapRound, JoinRound); + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } + } else if (event.xany.window == expwin) { + if ((current_image == PART) && loaded[current_inc][PART]) { + explode = !explode; + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } + } else if (event.xany.window == exppluswin) { + if ((current_image == PART) && loaded[PART] && explode) { + explosion += 0.125; + findpartshift(subdomains[current_inc], explosion, + partcenter[current_inc], partshift[current_inc]); + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } + } else if (event.xany.window == expminuswin) { + if ((current_image == PART) && loaded[PART] && explode && + (explosion >= 0.125)) { + explosion -= 0.125; + findpartshift(subdomains[current_inc], explosion, + partcenter[current_inc], partshift[current_inc]); + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } + } else if (event.xany.window == fillwin) { + if ((current_image == PART) && loaded[PART]) { + fillelem = !fillelem; + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } + } else if (event.xany.window == pswin) { + fill_button(pswin); + XFlush(display); + print(current_inc, current_image, xmin, ymin, xmax, ymax, 0); + XClearWindow(display, pswin); + XDrawString(display, pswin, fontgc, 2, 13, "PS", 2); + } else if (event.xany.window == epswin) { + fill_button(epswin); + XFlush(display); + print(current_inc, current_image, xmin, ymin, xmax, ymax, 1); + XClearWindow(display, epswin); + XDrawString(display, epswin, fontgc, 2, 13, "EPS", 3); + } else if (event.xany.window == versionpluswin) { + move_inc(1); + loweriteration++; + set_filenames(filename, loweriteration); + if (current_inc == 1) { + current_inc = 0; + } else { + current_image = NOTHING; + XClearWindow(display, mainwindow); + } + draw_buttons(); + } else if (event.xany.window == versionminuswin) { + if (loweriteration > 0) { + move_inc(0); + loweriteration--; + set_filenames(filename, loweriteration); + if (current_inc == 0) { + current_inc = 1; + } else { + current_image = NOTHING; + XClearWindow(display, mainwindow); + } + draw_buttons(); + } + } else if ((event.xany.window == nodewin[0]) || + (event.xany.window == polywin[0]) || + (event.xany.window == elewin[0]) || + (event.xany.window == edgewin[0]) || + (event.xany.window == partwin[0]) || + (event.xany.window == adjwin[0]) || + (event.xany.window == voronoiwin[0]) || + (event.xany.window == nodewin[1]) || + (event.xany.window == polywin[1]) || + (event.xany.window == elewin[1]) || + (event.xany.window == edgewin[1]) || + (event.xany.window == partwin[1]) || + (event.xany.window == adjwin[1]) || + (event.xany.window == voronoiwin[1])) { + if (event.xany.window == nodewin[0]) { + new_inc = 0; + new_image = NODE; + } + if (event.xany.window == polywin[0]) { + new_inc = 0; + new_image = POLY; + } + if (event.xany.window == elewin[0]) { + new_inc = 0; + new_image = ELE; + } + if (event.xany.window == edgewin[0]) { + new_inc = 0; + new_image = EDGE; + } + if (event.xany.window == partwin[0]) { + new_inc = 0; + new_image = PART; + } + if (event.xany.window == adjwin[0]) { + new_inc = 0; + new_image = ADJ; + } + if (event.xany.window == voronoiwin[0]) { + new_inc = 0; + new_image = VORO; + } + if (event.xany.window == nodewin[1]) { + new_inc = 1; + new_image = NODE; + } + if (event.xany.window == polywin[1]) { + new_inc = 1; + new_image = POLY; + } + if (event.xany.window == elewin[1]) { + new_inc = 1; + new_image = ELE; + } + if (event.xany.window == edgewin[1]) { + new_inc = 1; + new_image = EDGE; + } + if (event.xany.window == partwin[1]) { + new_inc = 1; + new_image = PART; + } + if (event.xany.window == adjwin[1]) { + new_inc = 1; + new_image = ADJ; + } + if (event.xany.window == voronoiwin[1]) { + new_inc = 1; + new_image = VORO; + } + past_image = current_image; + if ((current_inc == new_inc) && (current_image == new_image)) { + free_inc(new_inc); + unload_inc(new_inc); + } + choose_image(new_inc, new_image); + if ((past_image == NOTHING) && (current_image != NOTHING)) { + xmin = xlo[current_inc][current_image]; + ymin = ylo[current_inc][current_image]; + xmax = xhi[current_inc][current_image]; + ymax = yhi[current_inc][current_image]; + zoom = 0; + } + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } else { + xptr = ((REAL) event.xbutton.x - xoffset) / xscale; + yptr = ((REAL) event.xbutton.y - yoffset) / yscale; + if ((current_image == PART) && loaded[PART] && explode) { + xptr = (xptr + partcenter[current_inc] + [subdomains[current_inc] << 1] + * explosion) / (1.0 + explosion); + yptr = (yptr + partcenter[current_inc] + [(subdomains[current_inc] << 1) + 1] + * explosion) / (1.0 + explosion); + } + if ((event.xbutton.button == Button1) + || (event.xbutton.button == Button3)) { + if (event.xbutton.button == Button1) { + xspan = 0.25 * (xmax - xmin); + yspan = 0.25 * (ymax - ymin); + zoom++; + } else { + xspan = xmax - xmin; + yspan = ymax - ymin; + zoom--; + } + xmin = xptr - xspan; + ymin = yptr - yspan; + xmax = xptr + xspan; + ymax = yptr + yspan; + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + } else if (event.xbutton.button == Button2) { + printf("x = %.9f, y = %.9f\n", xptr, yptr); + } + } + break; + case DestroyNotify: + XDestroyWindow(display, mainwindow); + XCloseDisplay(display); + return 0; + case ConfigureNotify: + if ((width != event.xconfigure.width) || + (height != event.xconfigure.height - PANELHEIGHT)) { + width = event.xconfigure.width; + height = event.xconfigure.height - PANELHEIGHT; + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + while (XCheckMaskEvent(display, ExposureMask, &event)); + } + break; + case Expose: + draw(current_inc, current_image, xmin, ymin, xmax, ymax); + while (XCheckMaskEvent(display, ExposureMask, &event)); + break; + default: + break; + } + XNextEvent(display, &event); + } +} diff --git a/Tools/Triangle/triangle.c b/Tools/Triangle/triangle.c new file mode 100644 index 000000000..9af47a493 --- /dev/null +++ b/Tools/Triangle/triangle.c @@ -0,0 +1,13241 @@ +/*****************************************************************************/ +/* */ +/* 888888888 ,o, / 888 */ +/* 888 88o88o " o8888o 88o8888o o88888o 888 o88888o */ +/* 888 888 888 88b 888 888 888 888 888 d888 88b */ +/* 888 888 888 o88^o888 888 888 "88888" 888 8888oo888 */ +/* 888 888 888 C888 888 888 888 / 888 q888 */ +/* 888 888 888 "88o^888 888 888 Cb 888 "88oooo" */ +/* "8oo8D */ +/* */ +/* A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. */ +/* (triangle.c) */ +/* */ +/* Version 1.3 */ +/* July 19, 1996 */ +/* */ +/* Copyright 1996 */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/* This program may be freely redistributed under the condition that the */ +/* copyright notices (including this entire header 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.) */ +/* */ +/* Hypertext instructions for Triangle are available on the Web at */ +/* */ +/* http://www.cs.cmu.edu/~quake/triangle.html */ +/* */ +/* Some of the references listed below are marked [*]. These are available */ +/* for downloading from the Web page */ +/* */ +/* http://www.cs.cmu.edu/~quake/triangle.research.html */ +/* */ +/* A paper discussing some aspects of Triangle is available. See Jonathan */ +/* Richard Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator */ +/* and Delaunay Triangulator," First Workshop on Applied Computational */ +/* Geometry, ACM, May 1996. [*] */ +/* */ +/* Triangle was created as part of the Archimedes project in the School of */ +/* Computer Science at Carnegie Mellon University. Archimedes is a */ +/* system for compiling parallel finite element solvers. For further */ +/* information, see Anja Feldmann, Omar Ghattas, John R. Gilbert, Gary L. */ +/* Miller, David R. O'Hallaron, Eric J. Schwabe, Jonathan R. Shewchuk, */ +/* and Shang-Hua Teng, "Automated Parallel Solution of Unstructured PDE */ +/* Problems." To appear in Communications of the ACM, we hope. */ +/* */ +/* The quality mesh generation algorithm is due to Jim Ruppert, "A */ +/* Delaunay Refinement Algorithm for Quality 2-Dimensional Mesh */ +/* Generation," Journal of Algorithms 18(3):548-585, May 1995. [*] */ +/* */ +/* My implementation of the divide-and-conquer and incremental Delaunay */ +/* triangulation algorithms follows closely the presentation of Guibas */ +/* and Stolfi, even though I use a triangle-based data structure instead */ +/* of their quad-edge data structure. (In fact, I originally implemented */ +/* Triangle using the quad-edge data structure, but switching to a */ +/* triangle-based data structure sped Triangle by a factor of two.) The */ +/* mesh manipulation primitives and the two aforementioned Delaunay */ +/* triangulation algorithms are described by Leonidas J. Guibas and Jorge */ +/* Stolfi, "Primitives for the Manipulation of General Subdivisions and */ +/* the Computation of Voronoi Diagrams," ACM Transactions on Graphics */ +/* 4(2):74-123, April 1985. */ +/* */ +/* Their O(n log n) divide-and-conquer algorithm is adapted from Der-Tsai */ +/* Lee and Bruce J. Schachter, "Two Algorithms for Constructing the */ +/* Delaunay Triangulation," International Journal of Computer and */ +/* Information Science 9(3):219-242, 1980. The idea to improve the */ +/* divide-and-conquer algorithm by alternating between vertical and */ +/* horizontal cuts was introduced by Rex A. Dwyer, "A Faster Divide-and- */ +/* Conquer Algorithm for Constructing Delaunay Triangulations," */ +/* Algorithmica 2(2):137-151, 1987. */ +/* */ +/* The incremental insertion algorithm was first proposed by C. L. Lawson, */ +/* "Software for C1 Surface Interpolation," in Mathematical Software III, */ +/* John R. Rice, editor, Academic Press, New York, pp. 161-194, 1977. */ +/* For point location, I use the algorithm of Ernst P. Mucke, Isaac */ +/* Saias, and Binhai Zhu, "Fast Randomized Point Location Without */ +/* Preprocessing in Two- and Three-dimensional Delaunay Triangulations," */ +/* Proceedings of the Twelfth Annual Symposium on Computational Geometry, */ +/* ACM, May 1996. [*] If I were to randomize the order of point */ +/* insertion (I currently don't bother), their result combined with the */ +/* result of Leonidas J. Guibas, Donald E. Knuth, and Micha Sharir, */ +/* "Randomized Incremental Construction of Delaunay and Voronoi */ +/* Diagrams," Algorithmica 7(4):381-413, 1992, would yield an expected */ +/* O(n^{4/3}) bound on running time. */ +/* */ +/* The O(n log n) sweepline Delaunay triangulation algorithm is taken from */ +/* Steven Fortune, "A Sweepline Algorithm for Voronoi Diagrams", */ +/* Algorithmica 2(2):153-174, 1987. A random sample of edges on the */ +/* boundary of the triangulation are maintained in a splay tree for the */ +/* purpose of point location. Splay trees are described by Daniel */ +/* Dominic Sleator and Robert Endre Tarjan, "Self-Adjusting Binary Search */ +/* Trees," Journal of the ACM 32(3):652-686, July 1985. */ +/* */ +/* The algorithms for exact computation of the signs of determinants are */ +/* described in Jonathan Richard Shewchuk, "Adaptive Precision Floating- */ +/* Point Arithmetic and Fast Robust Geometric Predicates," Technical */ +/* Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon */ +/* University, Pittsburgh, Pennsylvania, May 1996. [*] (Submitted to */ +/* Discrete & Computational Geometry.) An abbreviated version appears as */ +/* Jonathan Richard Shewchuk, "Robust Adaptive Floating-Point Geometric */ +/* Predicates," Proceedings of the Twelfth Annual Symposium on Computa- */ +/* tional Geometry, ACM, May 1996. [*] Many of the ideas for my exact */ +/* arithmetic routines originate with Douglas M. Priest, "Algorithms for */ +/* Arbitrary Precision Floating Point Arithmetic," Tenth Symposium on */ +/* Computer Arithmetic, 132-143, IEEE Computer Society Press, 1991. [*] */ +/* Many of the ideas for the correct evaluation of the signs of */ +/* determinants are taken from Steven Fortune and Christopher J. Van Wyk, */ +/* "Efficient Exact Arithmetic for Computational Geometry," Proceedings */ +/* of the Ninth Annual Symposium on Computational Geometry, ACM, */ +/* pp. 163-172, May 1993, and from Steven Fortune, "Numerical Stability */ +/* of Algorithms for 2D Delaunay Triangulations," International Journal */ +/* of Computational Geometry & Applications 5(1-2):193-213, March-June */ +/* 1995. */ +/* */ +/* For definitions of and results involving Delaunay triangulations, */ +/* constrained and conforming versions thereof, and other aspects of */ +/* triangular mesh generation, see the excellent survey by Marshall Bern */ +/* and David Eppstein, "Mesh Generation and Optimal Triangulation," in */ +/* Computing and Euclidean Geometry, Ding-Zhu Du and Frank Hwang, */ +/* editors, World Scientific, Singapore, pp. 23-90, 1992. */ +/* */ +/* The time for incrementally adding PSLG (planar straight line graph) */ +/* segments to create a constrained Delaunay triangulation is probably */ +/* O(n^2) per segment in the worst case and O(n) per edge in the common */ +/* case, where n is the number of triangles that intersect the segment */ +/* before it is inserted. This doesn't count point location, which can */ +/* be much more expensive. (This note does not apply to conforming */ +/* Delaunay triangulations, for which a different method is used to */ +/* insert segments.) */ +/* */ +/* The time for adding segments to a conforming Delaunay triangulation is */ +/* not clear, but does not depend upon n alone. In some cases, very */ +/* small features (like a point lying next to a segment) can cause a */ +/* single segment to be split an arbitrary number of times. Of course, */ +/* floating-point precision is a practical barrier to how much this can */ +/* happen. */ +/* */ +/* The time for deleting a point from a Delaunay triangulation is O(n^2) in */ +/* the worst case and O(n) in the common case, where n is the degree of */ +/* the point being deleted. I could improve this to expected O(n) time */ +/* by "inserting" the neighboring vertices in random order, but n is */ +/* usually quite small, so it's not worth the bother. (The O(n) time */ +/* for random insertion follows from L. Paul Chew, "Building Voronoi */ +/* Diagrams for Convex Polygons in Linear Expected Time," Technical */ +/* Report PCS-TR90-147, Department of Mathematics and Computer Science, */ +/* Dartmouth College, 1990. */ +/* */ +/* Ruppert's Delaunay refinement algorithm typically generates triangles */ +/* at a linear rate (constant time per triangle) after the initial */ +/* triangulation is formed. There may be pathological cases where more */ +/* time is required, but these never arise in practice. */ +/* */ +/* The segment intersection formulae are straightforward. If you want to */ +/* see them derived, see Franklin Antonio. "Faster Line Segment */ +/* Intersection." In Graphics Gems III (David Kirk, editor), pp. 199- */ +/* 202. Academic Press, Boston, 1992. */ +/* */ +/* If you make any improvements to this code, please please please let me */ +/* know, so that I may obtain the improvements. Even if you don't change */ +/* the code, I'd still love to hear what it's being used for. */ +/* */ +/* Disclaimer: Neither I nor Carnegie Mellon warrant this code in any way */ +/* whatsoever. This code is provided "as-is". Use at your own risk. */ +/* */ +/*****************************************************************************/ + +/* For single precision (which will save some memory and reduce paging), */ +/* define the symbol SINGLE by using the -DSINGLE compiler switch or by */ +/* writing "#define SINGLE" below. */ +/* */ +/* For double precision (which will allow you to refine meshes to a smaller */ +/* edge length), leave SINGLE undefined. */ +/* */ +/* Double precision uses more memory, but improves the resolution of the */ +/* meshes you can generate with Triangle. It also reduces the likelihood */ +/* of a floating exception due to overflow. Finally, it is much faster */ +/* than single precision on 64-bit architectures like the DEC Alpha. I */ +/* recommend double precision unless you want to generate a mesh for which */ +/* you do not have enough memory. */ + +/* #define SINGLE */ + +#ifdef SINGLE +#define REAL float +#else /* not SINGLE */ +#define REAL double +#endif /* not SINGLE */ + +/* If yours is not a Unix system, define the NO_TIMER compiler switch to */ +/* remove the Unix-specific timing code. */ + +/* #define NO_TIMER */ + +/* To insert lots of self-checks for internal errors, define the SELF_CHECK */ +/* symbol. This will slow down the program significantly. It is best to */ +/* define the symbol using the -DSELF_CHECK compiler switch, but you could */ +/* write "#define SELF_CHECK" below. If you are modifying this code, I */ +/* recommend you turn self-checks on. */ + +/* #define SELF_CHECK */ + +/* To compile Triangle as a callable object library (triangle.o), define the */ +/* TRILIBRARY symbol. Read the file triangle.h for details on how to call */ +/* the procedure triangulate() that results. */ + +/* #define TRILIBRARY */ + +/* It is possible to generate a smaller version of Triangle using one or */ +/* both of the following symbols. Define the REDUCED symbol to eliminate */ +/* all features that are primarily of research interest; specifically, the */ +/* -i, -F, -s, and -C switches. Define the CDT_ONLY symbol to eliminate */ +/* all meshing algorithms above and beyond constrained Delaunay */ +/* triangulation; specifically, the -r, -q, -a, -S, and -s switches. */ +/* These reductions are most likely to be useful when generating an object */ +/* library (triangle.o) by defining the TRILIBRARY symbol. */ + +/* #define REDUCED */ +/* #define CDT_ONLY */ + +/* On some machines, the exact arithmetic routines might be defeated by the */ +/* use of internal extended precision floating-point registers. Sometimes */ +/* this problem can be fixed by defining certain values to be volatile, */ +/* thus forcing them to be stored to memory and rounded off. This isn't */ +/* a great solution, though, as it slows Triangle down. */ +/* */ +/* To try this out, write "#define INEXACT volatile" below. Normally, */ +/* however, INEXACT should be defined to be nothing. ("#define INEXACT".) */ + +#define INEXACT /* Nothing */ +/* #define INEXACT volatile */ + +/* Maximum number of characters in a file name (including the null). */ + +#define FILENAMESIZE 512 + +/* Maximum number of characters in a line read from a file (including the */ +/* null). */ + +#define INPUTLINESIZE 512 + +/* For efficiency, a variety of data structures are allocated in bulk. The */ +/* following constants determine how many of each structure is allocated */ +/* at once. */ + +#define TRIPERBLOCK 4092 /* Number of triangles allocated at once. */ +#define SHELLEPERBLOCK 508 /* Number of shell edges allocated at once. */ +#define POINTPERBLOCK 4092 /* Number of points allocated at once. */ +#define VIRUSPERBLOCK 1020 /* Number of virus triangles allocated at once. */ +/* Number of encroached segments allocated at once. */ +#define BADSEGMENTPERBLOCK 252 +/* Number of skinny triangles allocated at once. */ +#define BADTRIPERBLOCK 4092 +/* Number of splay tree nodes allocated at once. */ +#define SPLAYNODEPERBLOCK 508 + +/* The point marker DEADPOINT is an arbitrary number chosen large enough to */ +/* (hopefully) not conflict with user boundary markers. Make sure that it */ +/* is small enough to fit into your machine's integer size. */ + +#define DEADPOINT -1073741824 + +/* The next line is used to outsmart some very stupid compilers. If your */ +/* compiler is smarter, feel free to replace the "int" with "void". */ +/* Not that it matters. */ + +#define VOID int + +/* Two constants for algorithms based on random sampling. Both constants */ +/* have been chosen empirically to optimize their respective algorithms. */ + +/* Used for the point location scheme of Mucke, Saias, and Zhu, to decide */ +/* how large a random sample of triangles to inspect. */ +#define SAMPLEFACTOR 11 +/* Used in Fortune's sweepline Delaunay algorithm to determine what fraction */ +/* of boundary edges should be maintained in the splay tree for point */ +/* location on the front. */ +#define SAMPLERATE 10 + +/* A number that speaks for itself, every kissable digit. */ + +#define PI 3.141592653589793238462643383279502884197169399375105820974944592308 + +/* Another fave. */ + +#define SQUAREROOTTWO 1.4142135623730950488016887242096980785696718753769480732 + +/* And here's one for those of you who are intimidated by math. */ + +#define ONETHIRD 0.333333333333333333333333333333333333333333333333333333333333 + +#include <stdio.h> +#include <string.h> +#include <math.h> +#ifndef NO_TIMER +#include <sys/time.h> +#endif /* NO_TIMER */ +#ifdef TRILIBRARY +#include "triangle.h" +#endif /* TRILIBRARY */ + +/* The following obscenity seems to be necessary to ensure that this program */ +/* will port to Dec Alphas running OSF/1, because their stdio.h file commits */ +/* the unpardonable sin of including stdlib.h. Hence, malloc(), free(), and */ +/* exit() may or may not already be defined at this point. I declare these */ +/* functions explicitly because some non-ANSI C compilers lack stdlib.h. */ + +#ifndef _STDLIB_H_ +extern void *malloc(); +extern void free(); +extern void exit(); +extern double strtod(); +extern long strtol(); +#endif /* _STDLIB_H_ */ + +/* A few forward declarations. */ + +void poolrestart(); +#ifndef TRILIBRARY +char *readline(); +char *findfield(); +#endif /* not TRILIBRARY */ + +/* Labels that signify whether a record consists primarily of pointers or of */ +/* floating-point words. Used to make decisions about data alignment. */ + +enum wordtype {POINTER, FLOATINGPOINT}; + +/* Labels that signify the result of point location. The result of a */ +/* search indicates that the point falls in the interior of a triangle, on */ +/* an edge, on a vertex, or outside the mesh. */ + +enum locateresult {INTRIANGLE, ONEDGE, ONVERTEX, OUTSIDE}; + +/* Labels that signify the result of site insertion. The result indicates */ +/* that the point was inserted with complete success, was inserted but */ +/* encroaches on a segment, was not inserted because it lies on a segment, */ +/* or was not inserted because another point occupies the same location. */ + +enum insertsiteresult {SUCCESSFULPOINT, ENCROACHINGPOINT, VIOLATINGPOINT, + DUPLICATEPOINT}; + +/* Labels that signify the result of direction finding. The result */ +/* indicates that a segment connecting the two query points falls within */ +/* the direction triangle, along the left edge of the direction triangle, */ +/* or along the right edge of the direction triangle. */ + +enum finddirectionresult {WITHIN, LEFTCOLLINEAR, RIGHTCOLLINEAR}; + +/* Labels that signify the result of the circumcenter computation routine. */ +/* The return value indicates which edge of the triangle is shortest. */ + +enum circumcenterresult {OPPOSITEORG, OPPOSITEDEST, OPPOSITEAPEX}; + +/*****************************************************************************/ +/* */ +/* The basic mesh data structures */ +/* */ +/* There are three: points, triangles, and shell edges (abbreviated */ +/* `shelle'). These three data structures, linked by pointers, comprise */ +/* the mesh. A point simply represents a point in space and its properties.*/ +/* A triangle is a triangle. A shell edge is a special data structure used */ +/* to represent impenetrable segments in the mesh (including the outer */ +/* boundary, boundaries of holes, and internal boundaries separating two */ +/* triangulated regions). Shell edges represent boundaries defined by the */ +/* user that triangles may not lie across. */ +/* */ +/* A triangle consists of a list of three vertices, a list of three */ +/* adjoining triangles, a list of three adjoining shell edges (when shell */ +/* edges are used), an arbitrary number of optional user-defined floating- */ +/* point attributes, and an optional area constraint. The latter is an */ +/* upper bound on the permissible area of each triangle in a region, used */ +/* for mesh refinement. */ +/* */ +/* For a triangle on a boundary of the mesh, some or all of the neighboring */ +/* triangles may not be present. For a triangle in the interior of the */ +/* mesh, often no neighboring shell edges are present. Such absent */ +/* triangles and shell edges are never represented by NULL pointers; they */ +/* are represented by two special records: `dummytri', the triangle that */ +/* fills "outer space", and `dummysh', the omnipresent shell edge. */ +/* `dummytri' and `dummysh' are used for several reasons; for instance, */ +/* they can be dereferenced and their contents examined without causing the */ +/* memory protection exception that would occur if NULL were dereferenced. */ +/* */ +/* However, it is important to understand that a triangle includes other */ +/* information as well. The pointers to adjoining vertices, triangles, and */ +/* shell edges are ordered in a way that indicates their geometric relation */ +/* to each other. Furthermore, each of these pointers contains orientation */ +/* information. Each pointer to an adjoining triangle indicates which face */ +/* of that triangle is contacted. Similarly, each pointer to an adjoining */ +/* shell edge indicates which side of that shell edge is contacted, and how */ +/* the shell edge is oriented relative to the triangle. */ +/* */ +/* Shell edges are found abutting edges of triangles; either sandwiched */ +/* between two triangles, or resting against one triangle on an exterior */ +/* boundary or hole boundary. */ +/* */ +/* A shell edge consists of a list of two vertices, a list of two */ +/* adjoining shell edges, and a list of two adjoining triangles. One of */ +/* the two adjoining triangles may not be present (though there should */ +/* always be one), and neighboring shell edges might not be present. */ +/* Shell edges also store a user-defined integer "boundary marker". */ +/* Typically, this integer is used to indicate what sort of boundary */ +/* conditions are to be applied at that location in a finite element */ +/* simulation. */ +/* */ +/* Like triangles, shell edges maintain information about the relative */ +/* orientation of neighboring objects. */ +/* */ +/* Points are relatively simple. A point is a list of floating point */ +/* numbers, starting with the x, and y coordinates, followed by an */ +/* arbitrary number of optional user-defined floating-point attributes, */ +/* followed by an integer boundary marker. During the segment insertion */ +/* phase, there is also a pointer from each point to a triangle that may */ +/* contain it. Each pointer is not always correct, but when one is, it */ +/* speeds up segment insertion. These pointers are assigned values once */ +/* at the beginning of the segment insertion phase, and are not used or */ +/* updated at any other time. Edge swapping during segment insertion will */ +/* render some of them incorrect. Hence, don't rely upon them for */ +/* anything. For the most part, points do not have any information about */ +/* what triangles or shell edges they are linked to. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* Handles */ +/* */ +/* The oriented triangle (`triedge') and oriented shell edge (`edge') data */ +/* structures defined below do not themselves store any part of the mesh. */ +/* The mesh itself is made of `triangle's, `shelle's, and `point's. */ +/* */ +/* Oriented triangles and oriented shell edges will usually be referred to */ +/* as "handles". A handle is essentially a pointer into the mesh; it */ +/* allows you to "hold" one particular part of the mesh. Handles are used */ +/* to specify the regions in which one is traversing and modifying the mesh.*/ +/* A single `triangle' may be held by many handles, or none at all. (The */ +/* latter case is not a memory leak, because the triangle is still */ +/* connected to other triangles in the mesh.) */ +/* */ +/* A `triedge' is a handle that holds a triangle. It holds a specific side */ +/* of the triangle. An `edge' is a handle that holds a shell edge. It */ +/* holds either the left or right side of the edge. */ +/* */ +/* Navigation about the mesh is accomplished through a set of mesh */ +/* manipulation primitives, further below. Many of these primitives take */ +/* a handle and produce a new handle that holds the mesh near the first */ +/* handle. Other primitives take two handles and glue the corresponding */ +/* parts of the mesh together. The exact position of the handles is */ +/* important. For instance, when two triangles are glued together by the */ +/* bond() primitive, they are glued by the sides on which the handles lie. */ +/* */ +/* Because points have no information about which triangles they are */ +/* attached to, I commonly represent a point by use of a handle whose */ +/* origin is the point. A single handle can simultaneously represent a */ +/* triangle, an edge, and a point. */ +/* */ +/*****************************************************************************/ + +/* The triangle data structure. Each triangle contains three pointers to */ +/* adjoining triangles, plus three pointers to vertex points, plus three */ +/* pointers to shell edges (defined below; these pointers are usually */ +/* `dummysh'). It may or may not also contain user-defined attributes */ +/* and/or a floating-point "area constraint". It may also contain extra */ +/* pointers for nodes, when the user asks for high-order elements. */ +/* Because the size and structure of a `triangle' is not decided until */ +/* runtime, I haven't simply defined the type `triangle' to be a struct. */ + +typedef REAL **triangle; /* Really: typedef triangle *triangle */ + +/* An oriented triangle: includes a pointer to a triangle and orientation. */ +/* The orientation denotes an edge of the triangle. Hence, there are */ +/* three possible orientations. By convention, each edge is always */ +/* directed to point counterclockwise about the corresponding triangle. */ + +struct triedge { + triangle *tri; + int orient; /* Ranges from 0 to 2. */ +}; + +/* The shell data structure. Each shell edge contains two pointers to */ +/* adjoining shell edges, plus two pointers to vertex points, plus two */ +/* pointers to adjoining triangles, plus one shell marker. */ + +typedef REAL **shelle; /* Really: typedef shelle *shelle */ + +/* An oriented shell edge: includes a pointer to a shell edge and an */ +/* orientation. The orientation denotes a side of the edge. Hence, there */ +/* are two possible orientations. By convention, the edge is always */ +/* directed so that the "side" denoted is the right side of the edge. */ + +struct edge { + shelle *sh; + int shorient; /* Ranges from 0 to 1. */ +}; + +/* The point data structure. Each point is actually an array of REALs. */ +/* The number of REALs is unknown until runtime. An integer boundary */ +/* marker, and sometimes a pointer to a triangle, is appended after the */ +/* REALs. */ + +typedef REAL *point; + +/* A queue used to store encroached segments. Each segment's vertices are */ +/* stored so that one can check whether a segment is still the same. */ + +struct badsegment { + struct edge encsegment; /* An encroached segment. */ + point segorg, segdest; /* The two vertices. */ + struct badsegment *nextsegment; /* Pointer to next encroached segment. */ +}; + +/* A queue used to store bad triangles. The key is the square of the cosine */ +/* of the smallest angle of the triangle. Each triangle's vertices are */ +/* stored so that one can check whether a triangle is still the same. */ + +struct badface { + struct triedge badfacetri; /* A bad triangle. */ + REAL key; /* cos^2 of smallest (apical) angle. */ + point faceorg, facedest, faceapex; /* The three vertices. */ + struct badface *nextface; /* Pointer to next bad triangle. */ +}; + +/* A node in a heap used to store events for the sweepline Delaunay */ +/* algorithm. Nodes do not point directly to their parents or children in */ +/* the heap. Instead, each node knows its position in the heap, and can */ +/* look up its parent and children in a separate array. The `eventptr' */ +/* points either to a `point' or to a triangle (in encoded format, so that */ +/* an orientation is included). In the latter case, the origin of the */ +/* oriented triangle is the apex of a "circle event" of the sweepline */ +/* algorithm. To distinguish site events from circle events, all circle */ +/* events are given an invalid (smaller than `xmin') x-coordinate `xkey'. */ + +struct event { + REAL xkey, ykey; /* Coordinates of the event. */ + VOID *eventptr; /* Can be a point or the location of a circle event. */ + int heapposition; /* Marks this event's position in the heap. */ +}; + +/* A node in the splay tree. Each node holds an oriented ghost triangle */ +/* that represents a boundary edge of the growing triangulation. When a */ +/* circle event covers two boundary edges with a triangle, so that they */ +/* are no longer boundary edges, those edges are not immediately deleted */ +/* from the tree; rather, they are lazily deleted when they are next */ +/* encountered. (Since only a random sample of boundary edges are kept */ +/* in the tree, lazy deletion is faster.) `keydest' is used to verify */ +/* that a triangle is still the same as when it entered the splay tree; if */ +/* it has been rotated (due to a circle event), it no longer represents a */ +/* boundary edge and should be deleted. */ + +struct splaynode { + struct triedge keyedge; /* Lprev of an edge on the front. */ + point keydest; /* Used to verify that splay node is still live. */ + struct splaynode *lchild, *rchild; /* Children in splay tree. */ +}; + +/* A type used to allocate memory. firstblock is the first block of items. */ +/* nowblock is the block from which items are currently being allocated. */ +/* nextitem points to the next slab of free memory for an item. */ +/* deaditemstack is the head of a linked list (stack) of deallocated items */ +/* that can be recycled. unallocateditems is the number of items that */ +/* remain to be allocated from nowblock. */ +/* */ +/* Traversal is the process of walking through the entire list of items, and */ +/* is separate from allocation. Note that a traversal will visit items on */ +/* the "deaditemstack" stack as well as live items. pathblock points to */ +/* the block currently being traversed. pathitem points to the next item */ +/* to be traversed. pathitemsleft is the number of items that remain to */ +/* be traversed in pathblock. */ +/* */ +/* itemwordtype is set to POINTER or FLOATINGPOINT, and is used to suggest */ +/* what sort of word the record is primarily made up of. alignbytes */ +/* determines how new records should be aligned in memory. itembytes and */ +/* itemwords are the length of a record in bytes (after rounding up) and */ +/* words. itemsperblock is the number of items allocated at once in a */ +/* single block. items is the number of currently allocated items. */ +/* maxitems is the maximum number of items that have been allocated at */ +/* once; it is the current number of items plus the number of records kept */ +/* on deaditemstack. */ + +struct memorypool { + VOID **firstblock, **nowblock; + VOID *nextitem; + VOID *deaditemstack; + VOID **pathblock; + VOID *pathitem; + enum wordtype itemwordtype; + int alignbytes; + int itembytes, itemwords; + int itemsperblock; + long items, maxitems; + int unallocateditems; + int pathitemsleft; +}; + +/* Variables used to allocate memory for triangles, shell edges, points, */ +/* viri (triangles being eaten), bad (encroached) segments, bad (skinny */ +/* or too large) triangles, and splay tree nodes. */ + +struct memorypool triangles; +struct memorypool shelles; +struct memorypool points; +struct memorypool viri; +struct memorypool badsegments; +struct memorypool badtriangles; +struct memorypool splaynodes; + +/* Variables that maintain the bad triangle queues. The tails are pointers */ +/* to the pointers that have to be filled in to enqueue an item. */ + +struct badface *queuefront[64]; +struct badface **queuetail[64]; + +REAL xmin, xmax, ymin, ymax; /* x and y bounds. */ +REAL xminextreme; /* Nonexistent x value used as a flag in sweepline. */ +int inpoints; /* Number of input points. */ +int inelements; /* Number of input triangles. */ +int insegments; /* Number of input segments. */ +int holes; /* Number of input holes. */ +int regions; /* Number of input regions. */ +long edges; /* Number of output edges. */ +int mesh_dim; /* Dimension (ought to be 2). */ +int nextras; /* Number of attributes per point. */ +int eextras; /* Number of attributes per triangle. */ +long hullsize; /* Number of edges of convex hull. */ +int triwords; /* Total words per triangle. */ +int shwords; /* Total words per shell edge. */ +int pointmarkindex; /* Index to find boundary marker of a point. */ +int point2triindex; /* Index to find a triangle adjacent to a point. */ +int highorderindex; /* Index to find extra nodes for high-order elements. */ +int elemattribindex; /* Index to find attributes of a triangle. */ +int areaboundindex; /* Index to find area bound of a triangle. */ +int checksegments; /* Are there segments in the triangulation yet? */ +int readnodefile; /* Has a .node file been read? */ +long samples; /* Number of random samples for point location. */ +unsigned long randomseed; /* Current random number seed. */ + +REAL splitter; /* Used to split REAL factors for exact multiplication. */ +REAL epsilon; /* Floating-point machine epsilon. */ +REAL resulterrbound; +REAL ccwerrboundA, ccwerrboundB, ccwerrboundC; +REAL iccerrboundA, iccerrboundB, iccerrboundC; + +long incirclecount; /* Number of incircle tests performed. */ +long counterclockcount; /* Number of counterclockwise tests performed. */ +long hyperbolacount; /* Number of right-of-hyperbola tests performed. */ +long circumcentercount; /* Number of circumcenter calculations performed. */ +long circletopcount; /* Number of circle top calculations performed. */ + +/* 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. */ +/* vararea: -a switch without number. */ +/* fixedarea: -a switch with number. */ +/* maxarea: maximum area bound, specified after -a switch. */ +/* regionattrib: -A switch. convex: -c 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. */ +/* steinerleft: number of Steiner points not yet used. */ +/* incremental: -i switch. sweepline: -F switch. */ +/* dwyer: inverse of -l switch. */ +/* splitseg: -s switch. */ +/* docheck: -C switch. */ +/* quiet: -Q switch. verbose: count of how often -V switch is selected. */ +/* useshelles: -p, -r, -q, or -c switch; determines whether shell edges */ +/* are used at all. */ +/* */ +/* Read the instructions to find out the meaning of these switches. */ + +int poly, refine, quality, vararea, fixedarea, regionattrib, convex; +int firstnumber; +int edgesout, voronoi, neighbors, geomview; +int nobound, nopolywritten, nonodewritten, noelewritten, noiterationnum; +int noholes, noexact; +int incremental, sweepline, dwyer; +int splitseg; +int docheck; +int quiet, verbose; +int useshelles; +int order; +int nobisect; +int steiner, steinerleft; +REAL minangle, goodangle; +REAL maxarea; + +/* Variables for file names. */ + +#ifndef TRILIBRARY +char innodefilename[FILENAMESIZE]; +char inelefilename[FILENAMESIZE]; +char inpolyfilename[FILENAMESIZE]; +char areafilename[FILENAMESIZE]; +char outnodefilename[FILENAMESIZE]; +char outelefilename[FILENAMESIZE]; +char outpolyfilename[FILENAMESIZE]; +char edgefilename[FILENAMESIZE]; +char vnodefilename[FILENAMESIZE]; +char vedgefilename[FILENAMESIZE]; +char neighborfilename[FILENAMESIZE]; +char offfilename[FILENAMESIZE]; +#endif /* not TRILIBRARY */ + +/* Triangular bounding box points. */ + +point infpoint1, infpoint2, infpoint3; + +/* Pointer to the `triangle' that occupies all of "outer space". */ + +triangle *dummytri; +triangle *dummytribase; /* Keep base address so we can free() it later. */ + +/* Pointer to the omnipresent shell edge. Referenced by any triangle or */ +/* shell edge that isn't really connected to a shell edge at that */ +/* location. */ + +shelle *dummysh; +shelle *dummyshbase; /* Keep base address so we can free() it later. */ + +/* Pointer to a recently visited triangle. Improves point location if */ +/* proximate points are inserted sequentially. */ + +struct triedge recenttri; + +/*****************************************************************************/ +/* */ +/* Mesh manipulation primitives. Each triangle contains three pointers to */ +/* other triangles, with orientations. Each pointer points not to the */ +/* first byte of a triangle, but to one of the first three bytes of a */ +/* triangle. It is necessary to extract both the triangle itself and the */ +/* orientation. To save memory, I keep both pieces of information in one */ +/* pointer. To make this possible, I assume that all triangles are aligned */ +/* to four-byte boundaries. The `decode' routine below decodes a pointer, */ +/* extracting an orientation (in the range 0 to 2) and a pointer to the */ +/* beginning of a triangle. The `encode' routine compresses a pointer to a */ +/* triangle and an orientation into a single pointer. My assumptions that */ +/* triangles are four-byte-aligned and that the `unsigned long' type is */ +/* long enough to hold a pointer are two of the few kludges in this program.*/ +/* */ +/* Shell edges are manipulated similarly. A pointer to a shell edge */ +/* carries both an address and an orientation in the range 0 to 1. */ +/* */ +/* The other primitives take an oriented triangle or oriented shell edge, */ +/* and return an oriented triangle or oriented shell edge or point; or they */ +/* change the connections in the data structure. */ +/* */ +/*****************************************************************************/ + +/********* Mesh manipulation primitives begin here *********/ +/** **/ +/** **/ + +/* Fast lookup arrays to speed some of the mesh manipulation primitives. */ + +int plus1mod3[3] = {1, 2, 0}; +int minus1mod3[3] = {2, 0, 1}; + +/********* Primitives for triangles *********/ +/* */ +/* */ + +/* decode() converts a pointer to an oriented triangle. The orientation is */ +/* extracted from the two least significant bits of the pointer. */ + +#define decode(ptr, triedge) \ + (triedge).orient = (int) ((unsigned long) (ptr) & (unsigned long) 3l); \ + (triedge).tri = (triangle *) \ + ((unsigned long) (ptr) ^ (unsigned long) (triedge).orient) + +/* encode() compresses an oriented triangle into a single pointer. It */ +/* relies on the assumption that all triangles are aligned to four-byte */ +/* boundaries, so the two least significant bits of (triedge).tri are zero.*/ + +#define encode(triedge) \ + (triangle) ((unsigned long) (triedge).tri | (unsigned long) (triedge).orient) + +/* The following edge manipulation primitives are all described by Guibas */ +/* and Stolfi. However, they use an edge-based data structure, whereas I */ +/* am using a triangle-based data structure. */ + +/* sym() finds the abutting triangle, on the same edge. Note that the */ +/* edge direction is necessarily reversed, because triangle/edge handles */ +/* are always directed counterclockwise around the triangle. */ + +#define sym(triedge1, triedge2) \ + ptr = (triedge1).tri[(triedge1).orient]; \ + decode(ptr, triedge2); + +#define symself(triedge) \ + ptr = (triedge).tri[(triedge).orient]; \ + decode(ptr, triedge); + +/* lnext() finds the next edge (counterclockwise) of a triangle. */ + +#define lnext(triedge1, triedge2) \ + (triedge2).tri = (triedge1).tri; \ + (triedge2).orient = plus1mod3[(triedge1).orient] + +#define lnextself(triedge) \ + (triedge).orient = plus1mod3[(triedge).orient] + +/* lprev() finds the previous edge (clockwise) of a triangle. */ + +#define lprev(triedge1, triedge2) \ + (triedge2).tri = (triedge1).tri; \ + (triedge2).orient = minus1mod3[(triedge1).orient] + +#define lprevself(triedge) \ + (triedge).orient = minus1mod3[(triedge).orient] + +/* onext() spins counterclockwise around a point; that is, it finds the next */ +/* edge with the same origin in the counterclockwise direction. This edge */ +/* will be part of a different triangle. */ + +#define onext(triedge1, triedge2) \ + lprev(triedge1, triedge2); \ + symself(triedge2); + +#define onextself(triedge) \ + lprevself(triedge); \ + symself(triedge); + +/* oprev() spins clockwise around a point; that is, it finds the next edge */ +/* with the same origin in the clockwise direction. This edge will be */ +/* part of a different triangle. */ + +#define oprev(triedge1, triedge2) \ + sym(triedge1, triedge2); \ + lnextself(triedge2); + +#define oprevself(triedge) \ + symself(triedge); \ + lnextself(triedge); + +/* dnext() spins counterclockwise around a point; that is, it finds the next */ +/* edge with the same destination in the counterclockwise direction. This */ +/* edge will be part of a different triangle. */ + +#define dnext(triedge1, triedge2) \ + sym(triedge1, triedge2); \ + lprevself(triedge2); + +#define dnextself(triedge) \ + symself(triedge); \ + lprevself(triedge); + +/* dprev() spins clockwise around a point; that is, it finds the next edge */ +/* with the same destination in the clockwise direction. This edge will */ +/* be part of a different triangle. */ + +#define dprev(triedge1, triedge2) \ + lnext(triedge1, triedge2); \ + symself(triedge2); + +#define dprevself(triedge) \ + lnextself(triedge); \ + symself(triedge); + +/* rnext() moves one edge counterclockwise about the adjacent triangle. */ +/* (It's best understood by reading Guibas and Stolfi. It involves */ +/* changing triangles twice.) */ + +#define rnext(triedge1, triedge2) \ + sym(triedge1, triedge2); \ + lnextself(triedge2); \ + symself(triedge2); + +#define rnextself(triedge) \ + symself(triedge); \ + lnextself(triedge); \ + symself(triedge); + +/* rnext() moves one edge clockwise about the adjacent triangle. */ +/* (It's best understood by reading Guibas and Stolfi. It involves */ +/* changing triangles twice.) */ + +#define rprev(triedge1, triedge2) \ + sym(triedge1, triedge2); \ + lprevself(triedge2); \ + symself(triedge2); + +#define rprevself(triedge) \ + symself(triedge); \ + lprevself(triedge); \ + symself(triedge); + +/* These primitives determine or set the origin, destination, or apex of a */ +/* triangle. */ + +#define org(triedge, pointptr) \ + pointptr = (point) (triedge).tri[plus1mod3[(triedge).orient] + 3] + +#define dest(triedge, pointptr) \ + pointptr = (point) (triedge).tri[minus1mod3[(triedge).orient] + 3] + +#define apex(triedge, pointptr) \ + pointptr = (point) (triedge).tri[(triedge).orient + 3] + +#define setorg(triedge, pointptr) \ + (triedge).tri[plus1mod3[(triedge).orient] + 3] = (triangle) pointptr + +#define setdest(triedge, pointptr) \ + (triedge).tri[minus1mod3[(triedge).orient] + 3] = (triangle) pointptr + +#define setapex(triedge, pointptr) \ + (triedge).tri[(triedge).orient + 3] = (triangle) pointptr + +#define setvertices2null(triedge) \ + (triedge).tri[3] = (triangle) NULL; \ + (triedge).tri[4] = (triangle) NULL; \ + (triedge).tri[5] = (triangle) NULL; + +/* Bond two triangles together. */ + +#define bond(triedge1, triedge2) \ + (triedge1).tri[(triedge1).orient] = encode(triedge2); \ + (triedge2).tri[(triedge2).orient] = encode(triedge1) + +/* Dissolve a bond (from one side). Note that the other triangle will still */ +/* think it's connected to this triangle. Usually, however, the other */ +/* triangle is being deleted entirely, or bonded to another triangle, so */ +/* it doesn't matter. */ + +#define dissolve(triedge) \ + (triedge).tri[(triedge).orient] = (triangle) dummytri + +/* Copy a triangle/edge handle. */ + +#define triedgecopy(triedge1, triedge2) \ + (triedge2).tri = (triedge1).tri; \ + (triedge2).orient = (triedge1).orient + +/* Test for equality of triangle/edge handles. */ + +#define triedgeequal(triedge1, triedge2) \ + (((triedge1).tri == (triedge2).tri) && \ + ((triedge1).orient == (triedge2).orient)) + +/* Primitives to infect or cure a triangle with the virus. These rely on */ +/* the assumption that all shell edges are aligned to four-byte boundaries.*/ + +#define infect(triedge) \ + (triedge).tri[6] = (triangle) \ + ((unsigned long) (triedge).tri[6] | (unsigned long) 2l) + +#define uninfect(triedge) \ + (triedge).tri[6] = (triangle) \ + ((unsigned long) (triedge).tri[6] & ~ (unsigned long) 2l) + +/* Test a triangle for viral infection. */ + +#define infected(triedge) \ + (((unsigned long) (triedge).tri[6] & (unsigned long) 2l) != 0) + +/* Check or set a triangle's attributes. */ + +#define elemattribute(triedge, attnum) \ + ((REAL *) (triedge).tri)[elemattribindex + (attnum)] + +#define setelemattribute(triedge, attnum, value) \ + ((REAL *) (triedge).tri)[elemattribindex + (attnum)] = value + +/* Check or set a triangle's maximum area bound. */ + +#define areabound(triedge) ((REAL *) (triedge).tri)[areaboundindex] + +#define setareabound(triedge, value) \ + ((REAL *) (triedge).tri)[areaboundindex] = value + +/********* Primitives for shell edges *********/ +/* */ +/* */ + +/* sdecode() converts a pointer to an oriented shell edge. The orientation */ +/* is extracted from the least significant bit of the pointer. The two */ +/* least significant bits (one for orientation, one for viral infection) */ +/* are masked out to produce the real pointer. */ + +#define sdecode(sptr, edge) \ + (edge).shorient = (int) ((unsigned long) (sptr) & (unsigned long) 1l); \ + (edge).sh = (shelle *) \ + ((unsigned long) (sptr) & ~ (unsigned long) 3l) + +/* sencode() compresses an oriented shell edge into a single pointer. It */ +/* relies on the assumption that all shell edges are aligned to two-byte */ +/* boundaries, so the least significant bit of (edge).sh is zero. */ + +#define sencode(edge) \ + (shelle) ((unsigned long) (edge).sh | (unsigned long) (edge).shorient) + +/* ssym() toggles the orientation of a shell edge. */ + +#define ssym(edge1, edge2) \ + (edge2).sh = (edge1).sh; \ + (edge2).shorient = 1 - (edge1).shorient + +#define ssymself(edge) \ + (edge).shorient = 1 - (edge).shorient + +/* spivot() finds the other shell edge (from the same segment) that shares */ +/* the same origin. */ + +#define spivot(edge1, edge2) \ + sptr = (edge1).sh[(edge1).shorient]; \ + sdecode(sptr, edge2) + +#define spivotself(edge) \ + sptr = (edge).sh[(edge).shorient]; \ + sdecode(sptr, edge) + +/* snext() finds the next shell edge (from the same segment) in sequence; */ +/* one whose origin is the input shell edge's destination. */ + +#define snext(edge1, edge2) \ + sptr = (edge1).sh[1 - (edge1).shorient]; \ + sdecode(sptr, edge2) + +#define snextself(edge) \ + sptr = (edge).sh[1 - (edge).shorient]; \ + sdecode(sptr, edge) + +/* These primitives determine or set the origin or destination of a shell */ +/* edge. */ + +#define sorg(edge, pointptr) \ + pointptr = (point) (edge).sh[2 + (edge).shorient] + +#define sdest(edge, pointptr) \ + pointptr = (point) (edge).sh[3 - (edge).shorient] + +#define setsorg(edge, pointptr) \ + (edge).sh[2 + (edge).shorient] = (shelle) pointptr + +#define setsdest(edge, pointptr) \ + (edge).sh[3 - (edge).shorient] = (shelle) pointptr + +/* These primitives read or set a shell marker. Shell markers are used to */ +/* hold user boundary information. */ + +#define mark(edge) (* (int *) ((edge).sh + 6)) + +#define setmark(edge, value) \ + * (int *) ((edge).sh + 6) = value + +/* Bond two shell edges together. */ + +#define sbond(edge1, edge2) \ + (edge1).sh[(edge1).shorient] = sencode(edge2); \ + (edge2).sh[(edge2).shorient] = sencode(edge1) + +/* Dissolve a shell edge bond (from one side). Note that the other shell */ +/* edge will still think it's connected to this shell edge. */ + +#define sdissolve(edge) \ + (edge).sh[(edge).shorient] = (shelle) dummysh + +/* Copy a shell edge. */ + +#define shellecopy(edge1, edge2) \ + (edge2).sh = (edge1).sh; \ + (edge2).shorient = (edge1).shorient + +/* Test for equality of shell edges. */ + +#define shelleequal(edge1, edge2) \ + (((edge1).sh == (edge2).sh) && \ + ((edge1).shorient == (edge2).shorient)) + +/********* Primitives for interacting triangles and shell edges *********/ +/* */ +/* */ + +/* tspivot() finds a shell edge abutting a triangle. */ + +#define tspivot(triedge, edge) \ + sptr = (shelle) (triedge).tri[6 + (triedge).orient]; \ + sdecode(sptr, edge) + +/* stpivot() finds a triangle abutting a shell edge. It requires that the */ +/* variable `ptr' of type `triangle' be defined. */ + +#define stpivot(edge, triedge) \ + ptr = (triangle) (edge).sh[4 + (edge).shorient]; \ + decode(ptr, triedge) + +/* Bond a triangle to a shell edge. */ + +#define tsbond(triedge, edge) \ + (triedge).tri[6 + (triedge).orient] = (triangle) sencode(edge); \ + (edge).sh[4 + (edge).shorient] = (shelle) encode(triedge) + +/* Dissolve a bond (from the triangle side). */ + +#define tsdissolve(triedge) \ + (triedge).tri[6 + (triedge).orient] = (triangle) dummysh + +/* Dissolve a bond (from the shell edge side). */ + +#define stdissolve(edge) \ + (edge).sh[4 + (edge).shorient] = (shelle) dummytri + +/********* Primitives for points *********/ +/* */ +/* */ + +#define pointmark(pt) ((int *) (pt))[pointmarkindex] + +#define setpointmark(pt, value) \ + ((int *) (pt))[pointmarkindex] = value + +#define point2tri(pt) ((triangle *) (pt))[point2triindex] + +#define setpoint2tri(pt, value) \ + ((triangle *) (pt))[point2triindex] = value + +/** **/ +/** **/ +/********* Mesh manipulation primitives end here *********/ + +/********* User interaction routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* syntax() Print list of command line switches. */ +/* */ +/*****************************************************************************/ + +#ifndef TRILIBRARY + +void syntax() +{ +#ifdef CDT_ONLY +#ifdef REDUCED + printf("triangle [-pAcevngBPNEIOXzo_lQVh] input_file\n"); +#else /* not REDUCED */ + printf("triangle [-pAcevngBPNEIOXzo_iFlCQVh] input_file\n"); +#endif /* not REDUCED */ +#else /* not CDT_ONLY */ +#ifdef REDUCED + printf("triangle [-prq__a__AcevngBPNEIOXzo_YS__lQVh] input_file\n"); +#else /* not REDUCED */ + printf("triangle [-prq__a__AcevngBPNEIOXzo_YS__iFlsCQVh] input_file\n"); +#endif /* not REDUCED */ +#endif /* not CDT_ONLY */ + + printf(" -p Triangulates a Planar Straight Line Graph (.poly file).\n"); +#ifndef CDT_ONLY + printf(" -r Refines a previously generated mesh.\n"); + printf( + " -q Quality mesh generation. A minimum angle may be specified.\n"); + printf(" -a Applies a maximum triangle area constraint.\n"); +#endif /* not CDT_ONLY */ + printf( + " -A Applies attributes to identify elements in certain regions.\n"); + printf(" -c Encloses the convex hull with segments.\n"); + printf(" -e Generates an edge list.\n"); + printf(" -v Generates a Voronoi diagram.\n"); + printf(" -n Generates a list of triangle neighbors.\n"); + printf(" -g Generates an .off file for Geomview.\n"); + printf(" -B Suppresses output of boundary information.\n"); + printf(" -P Suppresses output of .poly file.\n"); + printf(" -N Suppresses output of .node file.\n"); + printf(" -E Suppresses output of .ele file.\n"); + printf(" -I Suppresses mesh iteration numbers.\n"); + printf(" -O Ignores holes in .poly file.\n"); + printf(" -X Suppresses use of exact arithmetic.\n"); + printf(" -z Numbers all items starting from zero (rather than one).\n"); + printf(" -o2 Generates second-order subparametric elements.\n"); +#ifndef CDT_ONLY + printf(" -Y Suppresses boundary segment splitting.\n"); + printf(" -S Specifies maximum number of added Steiner points.\n"); +#endif /* not CDT_ONLY */ +#ifndef REDUCED + printf(" -i Uses incremental method, rather than divide-and-conquer.\n"); + printf(" -F Uses Fortune's sweepline algorithm, rather than d-and-c.\n"); +#endif /* not REDUCED */ + printf(" -l Uses vertical cuts only, rather than alternating cuts.\n"); +#ifndef REDUCED +#ifndef CDT_ONLY + printf( + " -s Force segments into mesh by splitting (instead of using CDT).\n"); +#endif /* not CDT_ONLY */ + printf(" -C Check consistency of final mesh.\n"); +#endif /* not REDUCED */ + printf(" -Q Quiet: No terminal output except errors.\n"); + printf(" -V Verbose: Detailed information on what I'm doing.\n"); + printf(" -h Help: Detailed instructions for Triangle.\n"); + exit(0); +} + +#endif /* not TRILIBRARY */ + +/*****************************************************************************/ +/* */ +/* info() Print out complete instructions. */ +/* */ +/*****************************************************************************/ + +#ifndef TRILIBRARY + +void info() +{ + printf("Triangle\n"); + printf( +"A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator.\n"); + printf("Version 1.3\n\n"); + printf( +"Copyright 1996 Jonathan Richard Shewchuk (bugs/comments to jrs@cs.cmu.edu)\n" +); + printf("School of Computer Science / Carnegie Mellon University\n"); + printf("5000 Forbes Avenue / Pittsburgh, Pennsylvania 15213-3891\n"); + printf( +"Created as part of the Archimedes project (tools for parallel FEM).\n"); + printf( +"Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.\n"); + printf("There is no warranty whatsoever. Use at your own risk.\n"); +#ifdef SINGLE + printf("This executable is compiled for single precision arithmetic.\n\n\n"); +#else /* not SINGLE */ + printf("This executable is compiled for double precision arithmetic.\n\n\n"); +#endif /* not SINGLE */ + printf( +"Triangle generates exact Delaunay triangulations, constrained Delaunay\n"); + printf( +"triangulations, and quality conforming Delaunay triangulations. The latter\n" +); + printf( +"can be generated with no small angles, and are thus suitable for finite\n"); + printf( +"element analysis. If no command line switches are specified, your .node\n"); + printf( +"input file will be read, and the Delaunay triangulation will be returned in\n" +); + printf(".node and .ele output files. The command syntax is:\n\n"); +#ifdef CDT_ONLY +#ifdef REDUCED + printf("triangle [-pAcevngBPNEIOXzo_lQVh] input_file\n\n"); +#else /* not REDUCED */ + printf("triangle [-pAcevngBPNEIOXzo_iFlCQVh] input_file\n\n"); +#endif /* not REDUCED */ +#else /* not CDT_ONLY */ +#ifdef REDUCED + printf("triangle [-prq__a__AcevngBPNEIOXzo_YS__lQVh] input_file\n\n"); +#else /* not REDUCED */ + printf("triangle [-prq__a__AcevngBPNEIOXzo_YS__iFlsCQVh] input_file\n\n"); +#endif /* not REDUCED */ +#endif /* not CDT_ONLY */ + printf( +"Underscores indicate that numbers may optionally follow certain switches;\n"); + printf( +"do not leave any space between a switch and its numeric parameter.\n"); + printf( +"input_file must be a file with extension .node, or extension .poly if the\n"); + printf( +"-p switch is used. If -r is used, you must supply .node and .ele files,\n"); + printf( +"and possibly a .poly file and .area file as well. The formats of these\n"); + printf("files are described below.\n\n"); + printf("Command Line Switches:\n\n"); + printf( +" -p Reads a Planar Straight Line Graph (.poly file), which can specify\n" +); + printf( +" points, segments, holes, and regional attributes and area\n"); + printf( +" constraints. Will generate a constrained Delaunay triangulation\n"); + printf( +" fitting the input; or, if -s, -q, or -a is used, a conforming\n"); + printf( +" Delaunay triangulation. If -p is not used, Triangle reads a .node\n" +); + printf(" file by default.\n"); + printf( +" -r Refines a previously generated mesh. The mesh is read from a .node\n" +); + printf( +" file and an .ele file. If -p is also used, a .poly file is read\n"); + printf( +" and used to constrain edges in the mesh. Further details on\n"); + printf(" refinement are given below.\n"); + printf( +" -q Quality mesh generation by Jim Ruppert's Delaunay refinement\n"); + printf( +" algorithm. Adds points to the mesh to ensure that no angles\n"); + printf( +" smaller than 20 degrees occur. An alternative minimum angle may be\n" +); + printf( +" specified after the `q'. If the minimum angle is 20.7 degrees or\n"); + printf( +" smaller, the triangulation algorithm is theoretically guaranteed to\n" +); + printf( +" terminate (assuming infinite precision arithmetic - Triangle may\n"); + printf( +" fail to terminate if you run out of precision). In practice, the\n"); + printf( +" algorithm often succeeds for minimum angles up to 33.8 degrees.\n"); + printf( +" For highly refined meshes, however, it may be necessary to reduce\n"); + printf( +" the minimum angle to well below 20 to avoid problems associated\n"); + printf( +" with insufficient floating-point precision. The specified angle\n"); + printf(" may include a decimal point.\n"); + printf( +" -a Imposes a maximum triangle area. If a number follows the `a', no\n"); + printf( +" triangle will be generated whose area is larger than that number.\n"); + printf( +" If no number is specified, an .area file (if -r is used) or .poly\n"); + printf( +" file (if -r is not used) specifies a number of maximum area\n"); + printf( +" constraints. An .area file contains a separate area constraint for\n" +); + printf( +" each triangle, and is useful for refining a finite element mesh\n"); + printf( +" based on a posteriori error estimates. A .poly file can optionally\n" +); + printf( +" contain an area constraint for each segment-bounded region, thereby\n" +); + printf( +" enforcing triangle densities in a first triangulation. You can\n"); + printf( +" impose both a fixed area constraint and a varying area constraint\n"); + printf( +" by invoking the -a switch twice, once with and once without a\n"); + printf( +" number following. Each area specified may include a decimal point.\n" +); + printf( +" -A Assigns an additional attribute to each triangle that identifies\n"); + printf( +" what segment-bounded region each triangle belongs to. Attributes\n"); + printf( +" are assigned to regions by the .poly file. If a region is not\n"); + printf( +" explicitly marked by the .poly file, triangles in that region are\n"); + printf( +" assigned an attribute of zero. The -A switch has an effect only\n"); + printf(" when the -p switch is used and the -r switch is not.\n"); + printf( +" -c Creates segments on the convex hull of the triangulation. If you\n"); + printf( +" are triangulating a point set, this switch causes a .poly file to\n"); + printf( +" be written, containing all edges in the convex hull. (By default,\n" +); + printf( +" a .poly file is written only if a .poly file is read.) If you are\n" +); + printf( +" triangulating a PSLG, this switch specifies that the interior of\n"); + printf( +" the convex hull of the PSLG should be triangulated. If you do not\n" +); + printf( +" use this switch when triangulating a PSLG, it is assumed that you\n"); + printf( +" have identified the region to be triangulated by surrounding it\n"); + printf( +" with segments of the input PSLG. Beware: if you are not careful,\n" +); + printf( +" this switch can cause the introduction of an extremely thin angle\n"); + printf( +" between a PSLG segment and a convex hull segment, which can cause\n"); + printf( +" overrefinement or failure if Triangle runs out of precision. If\n"); + printf( +" you are refining a mesh, the -c switch works differently; it\n"); + printf( +" generates the set of boundary edges of the mesh, rather than the\n"); + printf(" convex hull.\n"); + printf( +" -e Outputs (to an .edge file) a list of edges of the triangulation.\n"); + printf( +" -v Outputs the Voronoi diagram associated with the triangulation.\n"); + printf(" Does not attempt to detect degeneracies.\n"); + printf( +" -n Outputs (to a .neigh file) a list of triangles neighboring each\n"); + printf(" triangle.\n"); + printf( +" -g Outputs the mesh to an Object File Format (.off) file, suitable for\n" +); + printf(" viewing with the Geometry Center's Geomview package.\n"); + printf( +" -B No boundary markers in the output .node, .poly, and .edge output\n"); + printf( +" files. See the detailed discussion of boundary markers below.\n"); + printf( +" -P No output .poly file. Saves disk space, but you lose the ability\n"); + printf( +" to impose segment constraints on later refinements of the mesh.\n"); + printf(" -N No output .node file.\n"); + printf(" -E No output .ele file.\n"); + printf( +" -I No iteration numbers. Suppresses the output of .node and .poly\n"); + printf( +" files, so your input files won't be overwritten. (If your input is\n" +); + printf( +" a .poly file only, a .node file will be written.) Cannot be used\n"); + printf( +" with the -r switch, because that would overwrite your input .ele\n"); + printf( +" file. Shouldn't be used with the -s, -q, or -a switch if you are\n"); + printf( +" using a .node file for input, because no .node file will be\n"); + printf(" written, so there will be no record of any added points.\n"); + printf(" -O No holes. Ignores the holes in the .poly file.\n"); + printf( +" -X No exact arithmetic. Normally, Triangle uses exact floating-point\n" +); + printf( +" arithmetic for certain tests if it thinks the inexact tests are not\n" +); + printf( +" accurate enough. Exact arithmetic ensures the robustness of the\n"); + printf( +" triangulation algorithms, despite floating-point roundoff error.\n"); + printf( +" Disabling exact arithmetic with the -X switch will cause a small\n"); + printf( +" improvement in speed and create the possibility (albeit small) that\n" +); + printf( +" Triangle will fail to produce a valid mesh. Not recommended.\n"); + printf( +" -z Numbers all items starting from zero (rather than one). Note that\n" +); + printf( +" this switch is normally overrided by the value used to number the\n"); + printf( +" first point of the input .node or .poly file. However, this switch\n" +); + printf(" is useful when calling Triangle from another program.\n"); + printf( +" -o2 Generates second-order subparametric elements with six nodes each.\n" +); + printf( +" -Y No new points on the boundary. This switch is useful when the mesh\n" +); + printf( +" boundary must be preserved so that it conforms to some adjacent\n"); + printf( +" mesh. Be forewarned that you will probably sacrifice some of the\n"); + printf( +" quality of the mesh; Triangle will try, but the resulting mesh may\n" +); + printf( +" contain triangles of poor aspect ratio. Works well if all the\n"); + printf( +" boundary points are closely spaced. Specify this switch twice\n"); + printf( +" (`-YY') to prevent all segment splitting, including internal\n"); + printf(" boundaries.\n"); + printf( +" -S Specifies the maximum number of Steiner points (points that are not\n" +); + printf( +" in the input, but are added to meet the constraints of minimum\n"); + printf( +" angle and maximum area). The default is to allow an unlimited\n"); + printf( +" number. If you specify this switch with no number after it,\n"); + printf( +" the limit is set to zero. Triangle always adds points at segment\n"); + printf( +" intersections, even if it needs to use more points than the limit\n"); + printf( +" you set. When Triangle inserts segments by splitting (-s), it\n"); + printf( +" always adds enough points to ensure that all the segments appear in\n" +); + printf( +" the triangulation, again ignoring the limit. Be forewarned that\n"); + printf( +" the -S switch may result in a conforming triangulation that is not\n" +); + printf( +" truly Delaunay, because Triangle may be forced to stop adding\n"); + printf( +" points when the mesh is in a state where a segment is non-Delaunay\n" +); + printf( +" and needs to be split. If so, Triangle will print a warning.\n"); + printf( +" -i Uses an incremental rather than divide-and-conquer algorithm to\n"); + printf( +" form a Delaunay triangulation. Try it if the divide-and-conquer\n"); + printf(" algorithm fails.\n"); + printf( +" -F Uses Steven Fortune's sweepline algorithm to form a Delaunay\n"); + printf( +" triangulation. Warning: does not use exact arithmetic for all\n"); + printf(" calculations. An exact result is not guaranteed.\n"); + printf( +" -l Uses only vertical cuts in the divide-and-conquer algorithm. By\n"); + printf( +" default, Triangle uses alternating vertical and horizontal cuts,\n"); + printf( +" which usually improve the speed except with point sets that are\n"); + printf( +" small or short and wide. This switch is primarily of theoretical\n"); + printf(" interest.\n"); + printf( +" -s Specifies that segments should be forced into the triangulation by\n" +); + printf( +" recursively splitting them at their midpoints, rather than by\n"); + printf( +" generating a constrained Delaunay triangulation. Segment splitting\n" +); + printf( +" is true to Ruppert's original algorithm, but can create needlessly\n" +); + printf(" small triangles near external small features.\n"); + printf( +" -C Check the consistency of the final mesh. Uses exact arithmetic for\n" +); + printf( +" checking, even if the -X switch is used. Useful if you suspect\n"); + printf(" Triangle is buggy.\n"); + printf( +" -Q Quiet: Suppresses all explanation of what Triangle is doing, unless\n" +); + printf(" an error occurs.\n"); + printf( +" -V Verbose: Gives detailed information about what Triangle is doing.\n"); + printf( +" Add more `V's for increasing amount of detail. `-V' gives\n"); + printf( +" information on algorithmic progress and more detailed statistics.\n"); + printf( +" `-VV' gives point-by-point details, and will print so much that\n"); + printf( +" Triangle will run much more slowly. `-VVV' gives information only\n" +); + printf(" a debugger could love.\n"); + printf(" -h Help: Displays these instructions.\n"); + printf("\n"); + printf("Definitions:\n"); + printf("\n"); + printf( +" A Delaunay triangulation of a point set is a triangulation whose vertices\n" +); + printf( +" are the point set, having the property that no point in the point set\n"); + printf( +" falls in the interior of the circumcircle (circle that passes through all\n" +); + printf(" three vertices) of any triangle in the triangulation.\n\n"); + printf( +" A Voronoi diagram of a point set is a subdivision of the plane into\n"); + printf( +" polygonal regions (some of which may be infinite), where each region is\n"); + printf( +" the set of points in the plane that are closer to some input point than\n"); + printf( +" to any other input point. (The Voronoi diagram is the geometric dual of\n" +); + printf(" the Delaunay triangulation.)\n\n"); + printf( +" A Planar Straight Line Graph (PSLG) is a collection of points and\n"); + printf( +" segments. Segments are simply edges, whose endpoints are points in the\n"); + printf( +" PSLG. The file format for PSLGs (.poly files) is described below.\n"); + printf("\n"); + printf( +" A constrained Delaunay triangulation of a PSLG is similar to a Delaunay\n"); + printf( +" triangulation, but each PSLG segment is present as a single edge in the\n"); + printf( +" triangulation. (A constrained Delaunay triangulation is not truly a\n"); + printf(" Delaunay triangulation.)\n\n"); + printf( +" A conforming Delaunay triangulation of a PSLG is a true Delaunay\n"); + printf( +" triangulation in which each PSLG segment may have been subdivided into\n"); + printf( +" several edges by the insertion of additional points. These inserted\n"); + printf( +" points are necessary to allow the segments to exist in the mesh while\n"); + printf(" maintaining the Delaunay property.\n\n"); + printf("File Formats:\n\n"); + printf( +" All files may contain comments prefixed by the character '#'. Points,\n"); + printf( +" triangles, edges, holes, and maximum area constraints must be numbered\n"); + printf( +" consecutively, starting from either 1 or 0. Whichever you choose, all\n"); + printf( +" input files must be consistent; if the nodes are numbered from 1, so must\n" +); + printf( +" be all other objects. Triangle automatically detects your choice while\n"); + printf( +" reading the .node (or .poly) file. (When calling Triangle from another\n"); + printf( +" program, use the -z switch if you wish to number objects from zero.)\n"); + printf(" Examples of these file formats are given below.\n\n"); + printf(" .node files:\n"); + printf( +" First line: <# of points> <dimension (must be 2)> <# of attributes>\n"); + printf( +" <# of boundary markers (0 or 1)>\n" +); + printf( +" Remaining lines: <point #> <x> <y> [attributes] [boundary marker]\n"); + printf("\n"); + printf( +" The attributes, which are typically floating-point values of physical\n"); + printf( +" quantities (such as mass or conductivity) associated with the nodes of\n" +); + printf( +" a finite element mesh, are copied unchanged to the output mesh. If -s,\n" +); + printf( +" -q, or -a is selected, each new Steiner point added to the mesh will\n"); + printf(" have attributes assigned to it by linear interpolation.\n\n"); + printf( +" If the fourth entry of the first line is `1', the last column of the\n"); + printf( +" remainder of the file is assumed to contain boundary markers. Boundary\n" +); + printf( +" markers are used to identify boundary points and points resting on PSLG\n" +); + printf( +" segments; a complete description appears in a section below. The .node\n" +); + printf( +" file produced by Triangle will contain boundary markers in the last\n"); + printf(" column unless they are suppressed by the -B switch.\n\n"); + printf(" .ele files:\n"); + printf( +" First line: <# of triangles> <points per triangle> <# of attributes>\n"); + printf( +" Remaining lines: <triangle #> <point> <point> <point> ... [attributes]\n" +); + printf("\n"); + printf( +" Points are indices into the corresponding .node file. The first three\n" +); + printf( +" points are the corners, and are listed in counterclockwise order around\n" +); + printf( +" each triangle. (The remaining points, if any, depend on the type of\n"); + printf( +" finite element used.) The attributes are just like those of .node\n"); + printf( +" files. Because there is no simple mapping from input to output\n"); + printf( +" triangles, an attempt is made to interpolate attributes, which may\n"); + printf( +" result in a good deal of diffusion of attributes among nearby triangles\n" +); + printf( +" as the triangulation is refined. Diffusion does not occur across\n"); + printf( +" segments, so attributes used to identify segment-bounded regions remain\n" +); + printf( +" intact. In output .ele files, all triangles have three points each\n"); + printf( +" unless the -o2 switch is used, in which case they have six, and the\n"); + printf( +" fourth, fifth, and sixth points lie on the midpoints of the edges\n"); + printf(" opposite the first, second, and third corners.\n\n"); + printf(" .poly files:\n"); + printf( +" First line: <# of points> <dimension (must be 2)> <# of attributes>\n"); + printf( +" <# of boundary markers (0 or 1)>\n" +); + printf( +" Following lines: <point #> <x> <y> [attributes] [boundary marker]\n"); + printf(" One line: <# of segments> <# of boundary markers (0 or 1)>\n"); + printf( +" Following lines: <segment #> <endpoint> <endpoint> [boundary marker]\n"); + printf(" One line: <# of holes>\n"); + printf(" Following lines: <hole #> <x> <y>\n"); + printf( +" Optional line: <# of regional attributes and/or area constraints>\n"); + printf( +" Optional following lines: <constraint #> <x> <y> <attrib> <max area>\n"); + printf("\n"); + printf( +" A .poly file represents a PSLG, as well as some additional information.\n" +); + printf( +" The first section lists all the points, and is identical to the format\n" +); + printf( +" of .node files. <# of points> may be set to zero to indicate that the\n" +); + printf( +" points are listed in a separate .node file; .poly files produced by\n"); + printf( +" Triangle always have this format. This has the advantage that a point\n" +); + printf( +" set may easily be triangulated with or without segments. (The same\n"); + printf( +" effect can be achieved, albeit using more disk space, by making a copy\n" +); + printf( +" of the .poly file with the extension .node; all sections of the file\n"); + printf(" but the first are ignored.)\n\n"); + printf( +" The second section lists the segments. Segments are edges whose\n"); + printf( +" presence in the triangulation is enforced. Each segment is specified\n"); + printf( +" by listing the indices of its two endpoints. This means that you must\n" +); + printf( +" include its endpoints in the point list. If -s, -q, and -a are not\n"); + printf( +" selected, Triangle will produce a constrained Delaunay triangulation,\n"); + printf( +" in which each segment appears as a single edge in the triangulation.\n"); + printf( +" If -q or -a is selected, Triangle will produce a conforming Delaunay\n"); + printf( +" triangulation, in which segments may be subdivided into smaller edges.\n" +); + printf(" Each segment, like each point, may have a boundary marker.\n\n"); + printf( +" The third section lists holes (and concavities, if -c is selected) in\n"); + printf( +" the triangulation. Holes are specified by identifying a point inside\n"); + printf( +" each hole. After the triangulation is formed, Triangle creates holes\n"); + printf( +" by eating triangles, spreading out from each hole point until its\n"); + printf( +" progress is blocked by PSLG segments; you must be careful to enclose\n"); + printf( +" each hole in segments, or your whole triangulation may be eaten away.\n"); + printf( +" If the two triangles abutting a segment are eaten, the segment itself\n"); + printf( +" is also eaten. Do not place a hole directly on a segment; if you do,\n"); + printf(" Triangle will choose one side of the segment arbitrarily.\n\n"); + printf( +" The optional fourth section lists regional attributes (to be assigned\n"); + printf( +" to all triangles in a region) and regional constraints on the maximum\n"); + printf( +" triangle area. Triangle will read this section only if the -A switch\n"); + printf( +" is used or the -a switch is used without a number following it, and the\n" +); + printf( +" -r switch is not used. Regional attributes and area constraints are\n"); + printf( +" propagated in the same manner as holes; you specify a point for each\n"); + printf( +" attribute and/or constraint, and the attribute and/or constraint will\n"); + printf( +" affect the whole region (bounded by segments) containing the point. If\n" +); + printf( +" two values are written on a line after the x and y coordinate, the\n"); + printf( +" former is assumed to be a regional attribute (but will only be applied\n" +); + printf( +" if the -A switch is selected), and the latter is assumed to be a\n"); + printf( +" regional area constraint (but will only be applied if the -a switch is\n" +); + printf( +" selected). You may also specify just one value after the coordinates,\n" +); + printf( +" which can serve as both an attribute and an area constraint, depending\n" +); + printf( +" on the choice of switches. If you are using the -A and -a switches\n"); + printf( +" simultaneously and wish to assign an attribute to some region without\n"); + printf(" imposing an area constraint, use a negative maximum area.\n\n"); + printf( +" When a triangulation is created from a .poly file, you must either\n"); + printf( +" enclose the entire region to be triangulated in PSLG segments, or\n"); + printf( +" use the -c switch, which encloses the convex hull of the input point\n"); + printf( +" set. If you do not use the -c switch, Triangle will eat all triangles\n" +); + printf( +" on the outer boundary that are not protected by segments; if you are\n"); + printf( +" not careful, your whole triangulation may be eaten away. If you do\n"); + printf( +" use the -c switch, you can still produce concavities by appropriate\n"); + printf(" placement of holes just inside the convex hull.\n\n"); + printf( +" An ideal PSLG has no intersecting segments, nor any points that lie\n"); + printf( +" upon segments (except, of course, the endpoints of each segment.) You\n" +); + printf( +" aren't required to make your .poly files ideal, but you should be aware\n" +); + printf( +" of what can go wrong. Segment intersections are relatively safe -\n"); + printf( +" Triangle will calculate the intersection points for you and add them to\n" +); + printf( +" the triangulation - as long as your machine's floating-point precision\n" +); + printf( +" doesn't become a problem. You are tempting the fates if you have three\n" +); + printf( +" segments that cross at the same location, and expect Triangle to figure\n" +); + printf( +" out where the intersection point is. Thanks to floating-point roundoff\n" +); + printf( +" error, Triangle will probably decide that the three segments intersect\n" +); + printf( +" at three different points, and you will find a minuscule triangle in\n"); + printf( +" your output - unless Triangle tries to refine the tiny triangle, uses\n"); + printf( +" up the last bit of machine precision, and fails to terminate at all.\n"); + printf( +" You're better off putting the intersection point in the input files,\n"); + printf( +" and manually breaking up each segment into two. Similarly, if you\n"); + printf( +" place a point at the middle of a segment, and hope that Triangle will\n"); + printf( +" break up the segment at that point, you might get lucky. On the other\n" +); + printf( +" hand, Triangle might decide that the point doesn't lie precisely on the\n" +); + printf( +" line, and you'll have a needle-sharp triangle in your output - or a lot\n" +); + printf(" of tiny triangles if you're generating a quality mesh.\n\n"); + printf( +" When Triangle reads a .poly file, it also writes a .poly file, which\n"); + printf( +" includes all edges that are part of input segments. If the -c switch\n"); + printf( +" is used, the output .poly file will also include all of the edges on\n"); + printf( +" the convex hull. Hence, the output .poly file is useful for finding\n"); + printf( +" edges associated with input segments and setting boundary conditions in\n" +); + printf( +" finite element simulations. More importantly, you will need it if you\n" +); + printf( +" plan to refine the output mesh, and don't want segments to be missing\n"); + printf(" in later triangulations.\n\n"); + printf(" .area files:\n"); + printf(" First line: <# of triangles>\n"); + printf(" Following lines: <triangle #> <maximum area>\n\n"); + printf( +" An .area file associates with each triangle a maximum area that is used\n" +); + printf( +" for mesh refinement. As with other file formats, every triangle must\n"); + printf( +" be represented, and they must be numbered consecutively. A triangle\n"); + printf( +" may be left unconstrained by assigning it a negative maximum area.\n"); + printf("\n"); + printf(" .edge files:\n"); + printf(" First line: <# of edges> <# of boundary markers (0 or 1)>\n"); + printf( +" Following lines: <edge #> <endpoint> <endpoint> [boundary marker]\n"); + printf("\n"); + printf( +" Endpoints are indices into the corresponding .node file. Triangle can\n" +); + printf( +" produce .edge files (use the -e switch), but cannot read them. The\n"); + printf( +" optional column of boundary markers is suppressed by the -B switch.\n"); + printf("\n"); + printf( +" In Voronoi diagrams, one also finds a special kind of edge that is an\n"); + printf( +" infinite ray with only one endpoint. For these edges, a different\n"); + printf(" format is used:\n\n"); + printf(" <edge #> <endpoint> -1 <direction x> <direction y>\n\n"); + printf( +" The `direction' is a floating-point vector that indicates the direction\n" +); + printf(" of the infinite ray.\n\n"); + printf(" .neigh files:\n"); + printf( +" First line: <# of triangles> <# of neighbors per triangle (always 3)>\n" +); + printf( +" Following lines: <triangle #> <neighbor> <neighbor> <neighbor>\n"); + printf("\n"); + printf( +" Neighbors are indices into the corresponding .ele file. An index of -1\n" +); + printf( +" indicates a mesh boundary, and therefore no neighbor. Triangle can\n"); + printf( +" produce .neigh files (use the -n switch), but cannot read them.\n"); + printf("\n"); + printf( +" The first neighbor of triangle i is opposite the first corner of\n"); + printf(" triangle i, and so on.\n\n"); + printf("Boundary Markers:\n\n"); + printf( +" Boundary markers are tags used mainly to identify which output points and\n" +); + printf( +" edges are associated with which PSLG segment, and to identify which\n"); + printf( +" points and edges occur on a boundary of the triangulation. A common use\n" +); + printf( +" is to determine where boundary conditions should be applied to a finite\n"); + printf( +" element mesh. You can prevent boundary markers from being written into\n"); + printf(" files produced by Triangle by using the -B switch.\n\n"); + printf( +" The boundary marker associated with each segment in an output .poly file\n" +); + printf(" or edge in an output .edge file is chosen as follows:\n"); + printf( +" - If an output edge is part or all of a PSLG segment with a nonzero\n"); + printf( +" boundary marker, then the edge is assigned the same marker.\n"); + printf( +" - Otherwise, if the edge occurs on a boundary of the triangulation\n"); + printf( +" (including boundaries of holes), then the edge is assigned the marker\n" +); + printf(" one (1).\n"); + printf(" - Otherwise, the edge is assigned the marker zero (0).\n"); + printf( +" The boundary marker associated with each point in an output .node file is\n" +); + printf(" chosen as follows:\n"); + printf( +" - If a point is assigned a nonzero boundary marker in the input file,\n"); + printf( +" then it is assigned the same marker in the output .node file.\n"); + printf( +" - Otherwise, if the point lies on a PSLG segment (including the\n"); + printf( +" segment's endpoints) with a nonzero boundary marker, then the point\n"); + printf( +" is assigned the same marker. If the point lies on several such\n"); + printf(" segments, one of the markers is chosen arbitrarily.\n"); + printf( +" - Otherwise, if the point occurs on a boundary of the triangulation,\n"); + printf(" then the point is assigned the marker one (1).\n"); + printf(" - Otherwise, the point is assigned the marker zero (0).\n"); + printf("\n"); + printf( +" If you want Triangle to determine for you which points and edges are on\n"); + printf( +" the boundary, assign them the boundary marker zero (or use no markers at\n" +); + printf( +" all) in your input files. Alternatively, you can mark some of them and\n"); + printf(" leave others marked zero, allowing Triangle to label them.\n\n"); + printf("Triangulation Iteration Numbers:\n\n"); + printf( +" Because Triangle can read and refine its own triangulations, input\n"); + printf( +" and output files have iteration numbers. For instance, Triangle might\n"); + printf( +" read the files mesh.3.node, mesh.3.ele, and mesh.3.poly, refine the\n"); + printf( +" triangulation, and output the files mesh.4.node, mesh.4.ele, and\n"); + printf(" mesh.4.poly. Files with no iteration number are treated as if\n"); + printf( +" their iteration number is zero; hence, Triangle might read the file\n"); + printf( +" points.node, triangulate it, and produce the files points.1.node and\n"); + printf(" points.1.ele.\n\n"); + printf( +" Iteration numbers allow you to create a sequence of successively finer\n"); + printf( +" meshes suitable for multigrid methods. They also allow you to produce a\n" +); + printf( +" sequence of meshes using error estimate-driven mesh refinement.\n"); + printf("\n"); + printf( +" If you're not using refinement or quality meshing, and you don't like\n"); + printf( +" iteration numbers, use the -I switch to disable them. This switch will\n"); + printf( +" also disable output of .node and .poly files to prevent your input files\n" +); + printf( +" from being overwritten. (If the input is a .poly file that contains its\n" +); + printf(" own points, a .node file will be written.)\n\n"); + printf("Examples of How to Use Triangle:\n\n"); + printf( +" `triangle dots' will read points from dots.node, and write their Delaunay\n" +); + printf( +" triangulation to dots.1.node and dots.1.ele. (dots.1.node will be\n"); + printf( +" identical to dots.node.) `triangle -I dots' writes the triangulation to\n" +); + printf( +" dots.ele instead. (No additional .node file is needed, so none is\n"); + printf(" written.)\n\n"); + printf( +" `triangle -pe object.1' will read a PSLG from object.1.poly (and possibly\n" +); + printf( +" object.1.node, if the points are omitted from object.1.poly) and write\n"); + printf(" their constrained Delaunay triangulation to object.2.node and\n"); + printf( +" object.2.ele. The segments will be copied to object.2.poly, and all\n"); + printf(" edges will be written to object.2.edge.\n\n"); + printf( +" `triangle -pq31.5a.1 object' will read a PSLG from object.poly (and\n"); + printf( +" possibly object.node), generate a mesh whose angles are all greater than\n" +); + printf( +" 31.5 degrees and whose triangles all have area smaller than 0.1, and\n"); + printf( +" write the mesh to object.1.node and object.1.ele. Each segment may have\n" +); + printf( +" been broken up into multiple edges; the resulting constrained edges are\n"); + printf(" written to object.1.poly.\n\n"); + printf( +" Here is a sample file `box.poly' describing a square with a square hole:\n" +); + printf("\n"); + printf( +" # A box with eight points in 2D, no attributes, one boundary marker.\n"); + printf(" 8 2 0 1\n"); + printf(" # Outer box has these vertices:\n"); + printf(" 1 0 0 0\n"); + printf(" 2 0 3 0\n"); + printf(" 3 3 0 0\n"); + printf(" 4 3 3 33 # A special marker for this point.\n"); + printf(" # Inner square has these vertices:\n"); + printf(" 5 1 1 0\n"); + printf(" 6 1 2 0\n"); + printf(" 7 2 1 0\n"); + printf(" 8 2 2 0\n"); + printf(" # Five segments with boundary markers.\n"); + printf(" 5 1\n"); + printf(" 1 1 2 5 # Left side of outer box.\n"); + printf(" 2 5 7 0 # Segments 2 through 5 enclose the hole.\n"); + printf(" 3 7 8 0\n"); + printf(" 4 8 6 10\n"); + printf(" 5 6 5 0\n"); + printf(" # One hole in the middle of the inner square.\n"); + printf(" 1\n"); + printf(" 1 1.5 1.5\n\n"); + printf( +" Note that some segments are missing from the outer square, so one must\n"); + printf( +" use the `-c' switch. After `triangle -pqc box.poly', here is the output\n" +); + printf( +" file `box.1.node', with twelve points. The last four points were added\n"); + printf( +" to meet the angle constraint. Points 1, 2, and 9 have markers from\n"); + printf( +" segment 1. Points 6 and 8 have markers from segment 4. All the other\n"); + printf( +" points but 4 have been marked to indicate that they lie on a boundary.\n"); + printf("\n"); + printf(" 12 2 0 1\n"); + printf(" 1 0 0 5\n"); + printf(" 2 0 3 5\n"); + printf(" 3 3 0 1\n"); + printf(" 4 3 3 33\n"); + printf(" 5 1 1 1\n"); + printf(" 6 1 2 10\n"); + printf(" 7 2 1 1\n"); + printf(" 8 2 2 10\n"); + printf(" 9 0 1.5 5\n"); + printf(" 10 1.5 0 1\n"); + printf(" 11 3 1.5 1\n"); + printf(" 12 1.5 3 1\n"); + printf(" # Generated by triangle -pqc box.poly\n\n"); + printf(" Here is the output file `box.1.ele', with twelve triangles.\n\n"); + printf(" 12 3 0\n"); + printf(" 1 5 6 9\n"); + printf(" 2 10 3 7\n"); + printf(" 3 6 8 12\n"); + printf(" 4 9 1 5\n"); + printf(" 5 6 2 9\n"); + printf(" 6 7 3 11\n"); + printf(" 7 11 4 8\n"); + printf(" 8 7 5 10\n"); + printf(" 9 12 2 6\n"); + printf(" 10 8 7 11\n"); + printf(" 11 5 1 10\n"); + printf(" 12 8 4 12\n"); + printf(" # Generated by triangle -pqc box.poly\n\n"); + printf( +" Here is the output file `box.1.poly'. Note that segments have been added\n" +); + printf( +" to represent the convex hull, and some segments have been split by newly\n" +); + printf( +" added points. Note also that <# of points> is set to zero to indicate\n"); + printf(" that the points should be read from the .node file.\n\n"); + printf(" 0 2 0 1\n"); + printf(" 12 1\n"); + printf(" 1 1 9 5\n"); + printf(" 2 5 7 1\n"); + printf(" 3 8 7 1\n"); + printf(" 4 6 8 10\n"); + printf(" 5 5 6 1\n"); + printf(" 6 3 10 1\n"); + printf(" 7 4 11 1\n"); + printf(" 8 2 12 1\n"); + printf(" 9 9 2 5\n"); + printf(" 10 10 1 1\n"); + printf(" 11 11 3 1\n"); + printf(" 12 12 4 1\n"); + printf(" 1\n"); + printf(" 1 1.5 1.5\n"); + printf(" # Generated by triangle -pqc box.poly\n\n"); + printf("Refinement and Area Constraints:\n\n"); + printf( +" The -r switch causes a mesh (.node and .ele files) to be read and\n"); + printf( +" refined. If the -p switch is also used, a .poly file is read and used to\n" +); + printf( +" specify edges that are constrained and cannot be eliminated (although\n"); + printf( +" they can be divided into smaller edges) by the refinement process.\n"); + printf("\n"); + printf( +" When you refine a mesh, you generally want to impose tighter quality\n"); + printf( +" constraints. One way to accomplish this is to use -q with a larger\n"); + printf( +" angle, or -a followed by a smaller area than you used to generate the\n"); + printf( +" mesh you are refining. Another way to do this is to create an .area\n"); + printf( +" file, which specifies a maximum area for each triangle, and use the -a\n"); + printf( +" switch (without a number following). Each triangle's area constraint is\n" +); + printf( +" applied to that triangle. Area constraints tend to diffuse as the mesh\n"); + printf( +" is refined, so if there are large variations in area constraint between\n"); + printf(" adjacent triangles, you may not get the results you want.\n\n"); + printf( +" If you are refining a mesh composed of linear (three-node) elements, the\n" +); + printf( +" output mesh will contain all the nodes present in the input mesh, in the\n" +); + printf( +" same order, with new nodes added at the end of the .node file. However,\n" +); + printf( +" there is no guarantee that each output element is contained in a single\n"); + printf( +" input element. Often, output elements will overlap two input elements,\n"); + printf( +" and input edges are not present in the output mesh. Hence, a sequence of\n" +); + printf( +" refined meshes will form a hierarchy of nodes, but not a hierarchy of\n"); + printf( +" elements. If you a refining a mesh of higher-order elements, the\n"); + printf( +" hierarchical property applies only to the nodes at the corners of an\n"); + printf(" element; other nodes may not be present in the refined mesh.\n\n"); + printf( +" It is important to understand that maximum area constraints in .poly\n"); + printf( +" files are handled differently from those in .area files. A maximum area\n" +); + printf( +" in a .poly file applies to the whole (segment-bounded) region in which a\n" +); + printf( +" point falls, whereas a maximum area in an .area file applies to only one\n" +); + printf( +" triangle. Area constraints in .poly files are used only when a mesh is\n"); + printf( +" first generated, whereas area constraints in .area files are used only to\n" +); + printf( +" refine an existing mesh, and are typically based on a posteriori error\n"); + printf( +" estimates resulting from a finite element simulation on that mesh.\n"); + printf("\n"); + printf( +" `triangle -rq25 object.1' will read object.1.node and object.1.ele, then\n" +); + printf( +" refine the triangulation to enforce a 25 degree minimum angle, and then\n"); + printf( +" write the refined triangulation to object.2.node and object.2.ele.\n"); + printf("\n"); + printf( +" `triangle -rpaa6.2 z.3' will read z.3.node, z.3.ele, z.3.poly, and\n"); + printf( +" z.3.area. After reconstructing the mesh and its segments, Triangle will\n" +); + printf( +" refine the mesh so that no triangle has area greater than 6.2, and\n"); + printf( +" furthermore the triangles satisfy the maximum area constraints in\n"); + printf( +" z.3.area. The output is written to z.4.node, z.4.ele, and z.4.poly.\n"); + printf("\n"); + printf( +" The sequence `triangle -qa1 x', `triangle -rqa.3 x.1', `triangle -rqa.1\n"); + printf( +" x.2' creates a sequence of successively finer meshes x.1, x.2, and x.3,\n"); + printf(" suitable for multigrid.\n\n"); + printf("Convex Hulls and Mesh Boundaries:\n\n"); + printf( +" If the input is a point set (rather than a PSLG), Triangle produces its\n"); + printf( +" convex hull as a by-product in the output .poly file if you use the -c\n"); + printf( +" switch. There are faster algorithms for finding a two-dimensional convex\n" +); + printf( +" hull than triangulation, of course, but this one comes for free. If the\n" +); + printf( +" input is an unconstrained mesh (you are using the -r switch but not the\n"); + printf( +" -p switch), Triangle produces a list of its boundary edges (including\n"); + printf(" hole boundaries) as a by-product if you use the -c switch.\n\n"); + printf("Voronoi Diagrams:\n\n"); + printf( +" The -v switch produces a Voronoi diagram, in files suffixed .v.node and\n"); + printf( +" .v.edge. For example, `triangle -v points' will read points.node,\n"); + printf( +" produce its Delaunay triangulation in points.1.node and points.1.ele,\n"); + printf( +" and produce its Voronoi diagram in points.1.v.node and points.1.v.edge.\n"); + printf( +" The .v.node file contains a list of all Voronoi vertices, and the .v.edge\n" +); + printf( +" file contains a list of all Voronoi edges, some of which may be infinite\n" +); + printf( +" rays. (The choice of filenames makes it easy to run the set of Voronoi\n"); + printf(" vertices through Triangle, if so desired.)\n\n"); + printf( +" This implementation does not use exact arithmetic to compute the Voronoi\n" +); + printf( +" vertices, and does not check whether neighboring vertices are identical.\n" +); + printf( +" Be forewarned that if the Delaunay triangulation is degenerate or\n"); + printf( +" near-degenerate, the Voronoi diagram may have duplicate points, crossing\n" +); + printf( +" edges, or infinite rays whose direction vector is zero. Also, if you\n"); + printf( +" generate a constrained (as opposed to conforming) Delaunay triangulation,\n" +); + printf( +" or if the triangulation has holes, the corresponding Voronoi diagram is\n"); + printf(" likely to have crossing edges and unlikely to make sense.\n\n"); + printf("Mesh Topology:\n\n"); + printf( +" You may wish to know which triangles are adjacent to a certain Delaunay\n"); + printf( +" edge in an .edge file, which Voronoi regions are adjacent to a certain\n"); + printf( +" Voronoi edge in a .v.edge file, or which Voronoi regions are adjacent to\n" +); + printf( +" each other. All of this information can be found by cross-referencing\n"); + printf( +" output files with the recollection that the Delaunay triangulation and\n"); + printf(" the Voronoi diagrams are planar duals.\n\n"); + printf( +" Specifically, edge i of an .edge file is the dual of Voronoi edge i of\n"); + printf( +" the corresponding .v.edge file, and is rotated 90 degrees counterclock-\n"); + printf( +" wise from the Voronoi edge. Triangle j of an .ele file is the dual of\n"); + printf( +" vertex j of the corresponding .v.node file; and Voronoi region k is the\n"); + printf(" dual of point k of the corresponding .node file.\n\n"); + printf( +" Hence, to find the triangles adjacent to a Delaunay edge, look at the\n"); + printf( +" vertices of the corresponding Voronoi edge; their dual triangles are on\n"); + printf( +" the left and right of the Delaunay edge, respectively. To find the\n"); + printf( +" Voronoi regions adjacent to a Voronoi edge, look at the endpoints of the\n" +); + printf( +" corresponding Delaunay edge; their dual regions are on the right and left\n" +); + printf( +" of the Voronoi edge, respectively. To find which Voronoi regions are\n"); + printf(" adjacent to each other, just read the list of Delaunay edges.\n"); + printf("\n"); + printf("Statistics:\n"); + printf("\n"); + printf( +" After generating a mesh, Triangle prints a count of the number of points,\n" +); + printf( +" triangles, edges, boundary edges, and segments in the output mesh. If\n"); + printf( +" you've forgotten the statistics for an existing mesh, the -rNEP switches\n" +); + printf( +" (or -rpNEP if you've got a .poly file for the existing mesh) will\n"); + printf(" regenerate these statistics without writing any output.\n\n"); + printf( +" The -V switch produces extended statistics, including a rough estimate\n"); + printf( +" of memory use and a histogram of triangle aspect ratios and angles in the\n" +); + printf(" mesh.\n\n"); + printf("Exact Arithmetic:\n\n"); + printf( +" Triangle uses adaptive exact arithmetic to perform what computational\n"); + printf( +" geometers call the `orientation' and `incircle' tests. If the floating-\n" +); + printf( +" point arithmetic of your machine conforms to the IEEE 754 standard (as\n"); + printf( +" most workstations do), and does not use extended precision internal\n"); + printf( +" registers, then your output is guaranteed to be an absolutely true\n"); + printf(" Delaunay or conforming Delaunay triangulation, roundoff error\n"); + printf( +" notwithstanding. The word `adaptive' implies that these arithmetic\n"); + printf( +" routines compute the result only to the precision necessary to guarantee\n" +); + printf( +" correctness, so they are usually nearly as fast as their approximate\n"); + printf( +" counterparts. The exact tests can be disabled with the -X switch. On\n"); + printf( +" most inputs, this switch will reduce the computation time by about eight\n" +); + printf( +" percent - it's not worth the risk. There are rare difficult inputs\n"); + printf( +" (having many collinear and cocircular points), however, for which the\n"); + printf( +" difference could be a factor of two. These are precisely the inputs most\n" +); + printf(" likely to cause errors if you use the -X switch.\n\n"); + printf( +" Unfortunately, these routines don't solve every numerical problem. Exact\n" +); + printf( +" arithmetic is not used to compute the positions of points, because the\n"); + printf( +" bit complexity of point coordinates would grow without bound. Hence,\n"); + printf( +" segment intersections aren't computed exactly; in very unusual cases,\n"); + printf( +" roundoff error in computing an intersection point might actually lead to\n" +); + printf( +" an inverted triangle and an invalid triangulation. (This is one reason\n"); + printf( +" to compute your own intersection points in your .poly files.) Similarly,\n" +); + printf( +" exact arithmetic is not used to compute the vertices of the Voronoi\n"); + printf(" diagram.\n\n"); + printf( +" Underflow and overflow can also cause difficulties; the exact arithmetic\n" +); + printf( +" routines do not ameliorate out-of-bounds exponents, which can arise\n"); + printf( +" during the orientation and incircle tests. As a rule of thumb, you\n"); + printf( +" should ensure that your input values are within a range such that their\n"); + printf( +" third powers can be taken without underflow or overflow. Underflow can\n"); + printf( +" silently prevent the tests from being performed exactly, while overflow\n"); + printf(" will typically cause a floating exception.\n\n"); + printf("Calling Triangle from Another Program:\n\n"); + printf(" Read the file triangle.h for details.\n\n"); + printf("Troubleshooting:\n\n"); + printf(" Please read this section before mailing me bugs.\n\n"); + printf(" `My output mesh has no triangles!'\n\n"); + printf( +" If you're using a PSLG, you've probably failed to specify a proper set\n" +); + printf( +" of bounding segments, or forgotten to use the -c switch. Or you may\n"); + printf( +" have placed a hole badly. To test these possibilities, try again with\n" +); + printf( +" the -c and -O switches. Alternatively, all your input points may be\n"); + printf( +" collinear, in which case you can hardly expect to triangulate them.\n"); + printf("\n"); + printf(" `Triangle doesn't terminate, or just crashes.'\n"); + printf("\n"); + printf( +" Bad things can happen when triangles get so small that the distance\n"); + printf( +" between their vertices isn't much larger than the precision of your\n"); + printf( +" machine's arithmetic. If you've compiled Triangle for single-precision\n" +); + printf( +" arithmetic, you might do better by recompiling it for double-precision.\n" +); + printf( +" Then again, you might just have to settle for more lenient constraints\n" +); + printf( +" on the minimum angle and the maximum area than you had planned.\n"); + printf("\n"); + printf( +" You can minimize precision problems by ensuring that the origin lies\n"); + printf( +" inside your point set, or even inside the densest part of your\n"); + printf( +" mesh. On the other hand, if you're triangulating an object whose x\n"); + printf( +" coordinates all fall between 6247133 and 6247134, you're not leaving\n"); + printf(" much floating-point precision for Triangle to work with.\n\n"); + printf( +" Precision problems can occur covertly if the input PSLG contains two\n"); + printf( +" segments that meet (or intersect) at a very small angle, or if such an\n" +); + printf( +" angle is introduced by the -c switch, which may occur if a point lies\n"); + printf( +" ever-so-slightly inside the convex hull, and is connected by a PSLG\n"); + printf( +" segment to a point on the convex hull. If you don't realize that a\n"); + printf( +" small angle is being formed, you might never discover why Triangle is\n"); + printf( +" crashing. To check for this possibility, use the -S switch (with an\n"); + printf( +" appropriate limit on the number of Steiner points, found by trial-and-\n" +); + printf( +" error) to stop Triangle early, and view the output .poly file with\n"); + printf( +" Show Me (described below). Look carefully for small angles between\n"); + printf( +" segments; zoom in closely, as such segments might look like a single\n"); + printf(" segment from a distance.\n\n"); + printf( +" If some of the input values are too large, Triangle may suffer a\n"); + printf( +" floating exception due to overflow when attempting to perform an\n"); + printf( +" orientation or incircle test. (Read the section on exact arithmetic\n"); + printf( +" above.) Again, I recommend compiling Triangle for double (rather\n"); + printf(" than single) precision arithmetic.\n\n"); + printf( +" `The numbering of the output points doesn't match the input points.'\n"); + printf("\n"); + printf( +" You may have eaten some of your input points with a hole, or by placing\n" +); + printf(" them outside the area enclosed by segments.\n\n"); + printf( +" `Triangle executes without incident, but when I look at the resulting\n"); + printf( +" mesh, it has overlapping triangles or other geometric inconsistencies.'\n"); + printf("\n"); + printf( +" If you select the -X switch, Triangle's divide-and-conquer Delaunay\n"); + printf( +" triangulation algorithm occasionally makes mistakes due to floating-\n"); + printf( +" point roundoff error. Although these errors are rare, don't use the -X\n" +); + printf(" switch. If you still have problems, please report the bug.\n"); + printf("\n"); + printf( +" Strange things can happen if you've taken liberties with your PSLG. Do\n"); + printf( +" you have a point lying in the middle of a segment? Triangle sometimes\n"); + printf( +" copes poorly with that sort of thing. Do you want to lay out a collinear\n" +); + printf( +" row of evenly spaced, segment-connected points? Have you simply defined\n" +); + printf( +" one long segment connecting the leftmost point to the rightmost point,\n"); + printf( +" and a bunch of points lying along it? This method occasionally works,\n"); + printf( +" especially with horizontal and vertical lines, but often it doesn't, and\n" +); + printf( +" you'll have to connect each adjacent pair of points with a separate\n"); + printf(" segment. If you don't like it, tough.\n\n"); + printf( +" Furthermore, if you have segments that intersect other than at their\n"); + printf( +" endpoints, try not to let the intersections fall extremely close to PSLG\n" +); + printf(" points or each other.\n\n"); + printf( +" If you have problems refining a triangulation not produced by Triangle:\n"); + printf( +" Are you sure the triangulation is geometrically valid? Is it formatted\n"); + printf( +" correctly for Triangle? Are the triangles all listed so the first three\n" +); + printf(" points are their corners in counterclockwise order?\n\n"); + printf("Show Me:\n\n"); + printf( +" Triangle comes with a separate program named `Show Me', whose primary\n"); + printf( +" purpose is to draw meshes on your screen or in PostScript. Its secondary\n" +); + printf( +" purpose is to check the validity of your input files, and do so more\n"); + printf( +" thoroughly than Triangle does. Show Me requires that you have the X\n"); + printf( +" Windows system. If you didn't receive Show Me with Triangle, complain to\n" +); + printf(" whomever you obtained Triangle from, then send me mail.\n\n"); + printf("Triangle on the Web:\n\n"); + printf( +" To see an illustrated, updated version of these instructions, check out\n"); + printf("\n"); + printf(" http://www.cs.cmu.edu/~quake/triangle.html\n"); + printf("\n"); + printf("A Brief Plea:\n"); + printf("\n"); + printf( +" If you use Triangle, and especially if you use it to accomplish real\n"); + printf( +" work, I would like very much to hear from you. A short letter or email\n"); + printf( +" (to jrs@cs.cmu.edu) describing how you use Triangle will mean a lot to\n"); + printf( +" me. The more people I know are using this program, the more easily I can\n" +); + printf( +" justify spending time on improvements and on the three-dimensional\n"); + printf( +" successor to Triangle, which in turn will benefit you. Also, I can put\n"); + printf( +" you on a list to receive email whenever a new version of Triangle is\n"); + printf(" available.\n\n"); + printf( +" If you use a mesh generated by Triangle in a publication, please include\n" +); + printf(" an acknowledgment as well.\n\n"); + printf("Research credit:\n\n"); + printf( +" Of course, I can take credit for only a fraction of the ideas that made\n"); + printf( +" this mesh generator possible. Triangle owes its existence to the efforts\n" +); + printf( +" of many fine computational geometers and other researchers, including\n"); + printf( +" Marshall Bern, L. Paul Chew, Boris Delaunay, Rex A. Dwyer, David\n"); + printf( +" Eppstein, Steven Fortune, Leonidas J. Guibas, Donald E. Knuth, C. L.\n"); + printf( +" Lawson, Der-Tsai Lee, Ernst P. Mucke, Douglas M. Priest, Jim Ruppert,\n"); + printf( +" Isaac Saias, Bruce J. Schachter, Micha Sharir, Jorge Stolfi, Christopher\n" +); + printf( +" J. Van Wyk, David F. Watson, and Binhai Zhu. See the comments at the\n"); + printf(" beginning of the source code for references.\n\n"); + exit(0); +} + +#endif /* not TRILIBRARY */ + +/*****************************************************************************/ +/* */ +/* internalerror() Ask the user to send me the defective product. Exit. */ +/* */ +/*****************************************************************************/ + +void internalerror() +{ + printf(" Please report this bug to jrs@cs.cmu.edu\n"); + printf(" Include the message above, your input data set, and the exact\n"); + printf(" command line you used to run Triangle.\n"); + exit(1); +} + +/*****************************************************************************/ +/* */ +/* parsecommandline() Read the command line, identify switches, and set */ +/* up options and file names. */ +/* */ +/* The effects of this routine are felt entirely through global variables. */ +/* */ +/*****************************************************************************/ + +void parsecommandline(argc, argv) +int argc; +char **argv; +{ +#ifdef TRILIBRARY +#define STARTINDEX 0 +#else /* not TRILIBRARY */ +#define STARTINDEX 1 + int increment; + int meshnumber; +#endif /* not TRILIBRARY */ + int i, j, k; + char workstring[FILENAMESIZE]; + + poly = refine = quality = vararea = fixedarea = regionattrib = convex = 0; + firstnumber = 1; + edgesout = voronoi = neighbors = geomview = 0; + nobound = nopolywritten = nonodewritten = noelewritten = noiterationnum = 0; + noholes = noexact = 0; + incremental = sweepline = 0; + dwyer = 1; + splitseg = 0; + docheck = 0; + nobisect = 0; + steiner = -1; + order = 1; + minangle = 0.0; + maxarea = -1.0; + quiet = verbose = 0; +#ifndef TRILIBRARY + innodefilename[0] = '\0'; +#endif /* not TRILIBRARY */ + + for (i = STARTINDEX; i < argc; i++) { +#ifndef TRILIBRARY + if (argv[i][0] == '-') { +#endif /* not TRILIBRARY */ + for (j = STARTINDEX; argv[i][j] != '\0'; j++) { + if (argv[i][j] == 'p') { + poly = 1; + } +#ifndef CDT_ONLY + if (argv[i][j] == 'r') { + refine = 1; + } + if (argv[i][j] == 'q') { + quality = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + minangle = (REAL) strtod(workstring, (char **) NULL); + } else { + minangle = 20.0; + } + } + if (argv[i][j] == 'a') { + quality = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + fixedarea = 1; + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + maxarea = (REAL) strtod(workstring, (char **) NULL); + if (maxarea <= 0.0) { + printf("Error: Maximum area must be greater than zero.\n"); + exit(1); + } + } else { + vararea = 1; + } + } +#endif /* not CDT_ONLY */ + if (argv[i][j] == 'A') { + regionattrib = 1; + } + if (argv[i][j] == 'c') { + convex = 1; + } + if (argv[i][j] == 'z') { + firstnumber = 0; + } + if (argv[i][j] == 'e') { + edgesout = 1; + } + if (argv[i][j] == 'v') { + voronoi = 1; + } + if (argv[i][j] == 'n') { + neighbors = 1; + } + if (argv[i][j] == 'g') { + geomview = 1; + } + if (argv[i][j] == 'B') { + nobound = 1; + } + if (argv[i][j] == 'P') { + nopolywritten = 1; + } + if (argv[i][j] == 'N') { + nonodewritten = 1; + } + if (argv[i][j] == 'E') { + noelewritten = 1; + } +#ifndef TRILIBRARY + if (argv[i][j] == 'I') { + noiterationnum = 1; + } +#endif /* not TRILIBRARY */ + if (argv[i][j] == 'O') { + noholes = 1; + } + if (argv[i][j] == 'X') { + noexact = 1; + } + if (argv[i][j] == 'o') { + if (argv[i][j + 1] == '2') { + j++; + order = 2; + } + } +#ifndef CDT_ONLY + if (argv[i][j] == 'Y') { + nobisect++; + } + if (argv[i][j] == 'S') { + steiner = 0; + while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + j++; + steiner = steiner * 10 + (int) (argv[i][j] - '0'); + } + } +#endif /* not CDT_ONLY */ +#ifndef REDUCED + if (argv[i][j] == 'i') { + incremental = 1; + } + if (argv[i][j] == 'F') { + sweepline = 1; + } +#endif /* not REDUCED */ + if (argv[i][j] == 'l') { + dwyer = 0; + } +#ifndef REDUCED +#ifndef CDT_ONLY + if (argv[i][j] == 's') { + splitseg = 1; + } +#endif /* not CDT_ONLY */ + if (argv[i][j] == 'C') { + docheck = 1; + } +#endif /* not REDUCED */ + if (argv[i][j] == 'Q') { + quiet = 1; + } + if (argv[i][j] == 'V') { + verbose++; + } +#ifndef TRILIBRARY + if ((argv[i][j] == 'h') || (argv[i][j] == 'H') || + (argv[i][j] == '?')) { + info(); + } +#endif /* not TRILIBRARY */ + } +#ifndef TRILIBRARY + } else { + strncpy(innodefilename, argv[i], FILENAMESIZE - 1); + innodefilename[FILENAMESIZE - 1] = '\0'; + } +#endif /* not TRILIBRARY */ + } +#ifndef TRILIBRARY + if (innodefilename[0] == '\0') { + syntax(); + } + if (!strcmp(&innodefilename[strlen(innodefilename) - 5], ".node")) { + innodefilename[strlen(innodefilename) - 5] = '\0'; + } + if (!strcmp(&innodefilename[strlen(innodefilename) - 5], ".poly")) { + innodefilename[strlen(innodefilename) - 5] = '\0'; + poly = 1; + } +#ifndef CDT_ONLY + if (!strcmp(&innodefilename[strlen(innodefilename) - 4], ".ele")) { + innodefilename[strlen(innodefilename) - 4] = '\0'; + refine = 1; + } + if (!strcmp(&innodefilename[strlen(innodefilename) - 5], ".area")) { + innodefilename[strlen(innodefilename) - 5] = '\0'; + refine = 1; + quality = 1; + vararea = 1; + } +#endif /* not CDT_ONLY */ +#endif /* not TRILIBRARY */ + steinerleft = steiner; + useshelles = poly || refine || quality || convex; + goodangle = cos(minangle * PI / 180.0); + goodangle *= goodangle; + if (refine && noiterationnum) { + printf( + "Error: You cannot use the -I switch when refining a triangulation.\n"); + exit(1); + } + /* Be careful not to allocate space for element area constraints that */ + /* will never be assigned any value (other than the default -1.0). */ + if (!refine && !poly) { + vararea = 0; + } + /* Be careful not to add an extra attribute to each element unless the */ + /* input supports it (PSLG in, but not refining a preexisting mesh). */ + if (refine || !poly) { + regionattrib = 0; + } + +#ifndef TRILIBRARY + strcpy(inpolyfilename, innodefilename); + strcpy(inelefilename, innodefilename); + strcpy(areafilename, innodefilename); + increment = 0; + strcpy(workstring, innodefilename); + j = 1; + while (workstring[j] != '\0') { + if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) { + increment = j + 1; + } + j++; + } + meshnumber = 0; + if (increment > 0) { + j = increment; + do { + if ((workstring[j] >= '0') && (workstring[j] <= '9')) { + meshnumber = meshnumber * 10 + (int) (workstring[j] - '0'); + } else { + increment = 0; + } + j++; + } while (workstring[j] != '\0'); + } + if (noiterationnum) { + strcpy(outnodefilename, innodefilename); + strcpy(outelefilename, innodefilename); + strcpy(edgefilename, innodefilename); + strcpy(vnodefilename, innodefilename); + strcpy(vedgefilename, innodefilename); + strcpy(neighborfilename, innodefilename); + strcpy(offfilename, innodefilename); + strcat(outnodefilename, ".node"); + strcat(outelefilename, ".ele"); + strcat(edgefilename, ".edge"); + strcat(vnodefilename, ".v.node"); + strcat(vedgefilename, ".v.edge"); + strcat(neighborfilename, ".neigh"); + strcat(offfilename, ".off"); + } else if (increment == 0) { + strcpy(outnodefilename, innodefilename); + strcpy(outpolyfilename, innodefilename); + strcpy(outelefilename, innodefilename); + strcpy(edgefilename, innodefilename); + strcpy(vnodefilename, innodefilename); + strcpy(vedgefilename, innodefilename); + strcpy(neighborfilename, innodefilename); + strcpy(offfilename, innodefilename); + strcat(outnodefilename, ".1.node"); + strcat(outpolyfilename, ".1.poly"); + strcat(outelefilename, ".1.ele"); + strcat(edgefilename, ".1.edge"); + strcat(vnodefilename, ".1.v.node"); + strcat(vedgefilename, ".1.v.edge"); + strcat(neighborfilename, ".1.neigh"); + strcat(offfilename, ".1.off"); + } else { + workstring[increment] = '%'; + workstring[increment + 1] = 'd'; + workstring[increment + 2] = '\0'; + sprintf(outnodefilename, workstring, meshnumber + 1); + strcpy(outpolyfilename, outnodefilename); + strcpy(outelefilename, outnodefilename); + strcpy(edgefilename, outnodefilename); + strcpy(vnodefilename, outnodefilename); + strcpy(vedgefilename, outnodefilename); + strcpy(neighborfilename, outnodefilename); + strcpy(offfilename, outnodefilename); + strcat(outnodefilename, ".node"); + strcat(outpolyfilename, ".poly"); + strcat(outelefilename, ".ele"); + strcat(edgefilename, ".edge"); + strcat(vnodefilename, ".v.node"); + strcat(vedgefilename, ".v.edge"); + strcat(neighborfilename, ".neigh"); + strcat(offfilename, ".off"); + } + strcat(innodefilename, ".node"); + strcat(inpolyfilename, ".poly"); + strcat(inelefilename, ".ele"); + strcat(areafilename, ".area"); +#endif /* not TRILIBRARY */ +} + +/** **/ +/** **/ +/********* User interaction routines begin here *********/ + +/********* Debugging routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* printtriangle() Print out the details of a triangle/edge handle. */ +/* */ +/* I originally wrote this procedure to simplify debugging; it can be */ +/* called directly from the debugger, and presents information about a */ +/* triangle/edge handle in digestible form. It's also used when the */ +/* highest level of verbosity (`-VVV') is specified. */ +/* */ +/*****************************************************************************/ + +void printtriangle(t) +struct triedge *t; +{ + struct triedge printtri; + struct edge printsh; + point printpoint; + + printf("triangle x%lx with orientation %d:\n", (unsigned long) t->tri, + t->orient); + decode(t->tri[0], printtri); + if (printtri.tri == 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 == 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 == dummytri) { + printf(" [2] = Outer space\n"); + } else { + printf(" [2] = x%lx %d\n", (unsigned long) printtri.tri, + printtri.orient); + } + org(*t, printpoint); + if (printpoint == (point) 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) printpoint, + printpoint[0], printpoint[1]); + dest(*t, printpoint); + if (printpoint == (point) 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) printpoint, + printpoint[0], printpoint[1]); + apex(*t, printpoint); + if (printpoint == (point) NULL) + printf(" Apex [%d] = NULL\n", t->orient + 3); + else + printf(" Apex [%d] = x%lx (%.12g, %.12g)\n", + t->orient + 3, (unsigned long) printpoint, + printpoint[0], printpoint[1]); + if (useshelles) { + sdecode(t->tri[6], printsh); + if (printsh.sh != dummysh) { + printf(" [6] = x%lx %d\n", (unsigned long) printsh.sh, + printsh.shorient); + } + sdecode(t->tri[7], printsh); + if (printsh.sh != dummysh) { + printf(" [7] = x%lx %d\n", (unsigned long) printsh.sh, + printsh.shorient); + } + sdecode(t->tri[8], printsh); + if (printsh.sh != dummysh) { + printf(" [8] = x%lx %d\n", (unsigned long) printsh.sh, + printsh.shorient); + } + } + if (vararea) { + printf(" Area constraint: %.4g\n", areabound(*t)); + } +} + +/*****************************************************************************/ +/* */ +/* printshelle() Print out the details of a shell edge handle. */ +/* */ +/* I originally wrote this procedure to simplify debugging; it can be */ +/* called directly from the debugger, and presents information about a */ +/* shell edge handle in digestible form. It's also used when the highest */ +/* level of verbosity (`-VVV') is specified. */ +/* */ +/*****************************************************************************/ + +void printshelle(s) +struct edge *s; +{ + struct edge printsh; + struct triedge printtri; + point printpoint; + + printf("shell edge x%lx with orientation %d and mark %d:\n", + (unsigned long) s->sh, s->shorient, mark(*s)); + sdecode(s->sh[0], printsh); + if (printsh.sh == dummysh) { + printf(" [0] = No shell\n"); + } else { + printf(" [0] = x%lx %d\n", (unsigned long) printsh.sh, + printsh.shorient); + } + sdecode(s->sh[1], printsh); + if (printsh.sh == dummysh) { + printf(" [1] = No shell\n"); + } else { + printf(" [1] = x%lx %d\n", (unsigned long) printsh.sh, + printsh.shorient); + } + sorg(*s, printpoint); + if (printpoint == (point) NULL) + printf(" Origin[%d] = NULL\n", 2 + s->shorient); + else + printf(" Origin[%d] = x%lx (%.12g, %.12g)\n", + 2 + s->shorient, (unsigned long) printpoint, + printpoint[0], printpoint[1]); + sdest(*s, printpoint); + if (printpoint == (point) NULL) + printf(" Dest [%d] = NULL\n", 3 - s->shorient); + else + printf(" Dest [%d] = x%lx (%.12g, %.12g)\n", + 3 - s->shorient, (unsigned long) printpoint, + printpoint[0], printpoint[1]); + decode(s->sh[4], printtri); + if (printtri.tri == dummytri) { + printf(" [4] = Outer space\n"); + } else { + printf(" [4] = x%lx %d\n", (unsigned long) printtri.tri, + printtri.orient); + } + decode(s->sh[5], printtri); + if (printtri.tri == dummytri) { + printf(" [5] = Outer space\n"); + } else { + printf(" [5] = x%lx %d\n", (unsigned long) printtri.tri, + printtri.orient); + } +} + +/** **/ +/** **/ +/********* Debugging routines end here *********/ + +/********* Memory management routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* poolinit() Initialize a pool of memory for allocation of items. */ +/* */ +/* This routine initializes the machinery for allocating items. A `pool' */ +/* is created whose records have size at least `bytecount'. Items will be */ +/* allocated in `itemcount'-item blocks. Each item is assumed to be a */ +/* collection of words, and either pointers or floating-point values are */ +/* assumed to be the "primary" word type. (The "primary" word type is used */ +/* to determine alignment of items.) If `alignment' isn't zero, all items */ +/* will be `alignment'-byte aligned in memory. `alignment' must be either */ +/* a multiple or a factor of the primary word size; powers of two are safe. */ +/* `alignment' is normally used to create a few unused bits at the bottom */ +/* of each item's pointer, in which information may be stored. */ +/* */ +/* Don't change this routine unless you understand it. */ +/* */ +/*****************************************************************************/ + +void poolinit(pool, bytecount, itemcount, wtype, alignment) +struct memorypool *pool; +int bytecount; +int itemcount; +enum wordtype wtype; +int alignment; +{ + int wordsize; + + /* Initialize values in the pool. */ + pool->itemwordtype = wtype; + wordsize = (pool->itemwordtype == POINTER) ? sizeof(VOID *) : sizeof(REAL); + /* Find the proper alignment, which must be at least as large as: */ + /* - The parameter `alignment'. */ + /* - The primary word type, to avoid unaligned accesses. */ + /* - sizeof(VOID *), so the stack of dead items can be maintained */ + /* without unaligned accesses. */ + if (alignment > wordsize) { + pool->alignbytes = alignment; + } else { + pool->alignbytes = wordsize; + } + if (sizeof(VOID *) > pool->alignbytes) { + pool->alignbytes = sizeof(VOID *); + } + pool->itemwords = ((bytecount + pool->alignbytes - 1) / pool->alignbytes) + * (pool->alignbytes / wordsize); + pool->itembytes = pool->itemwords * wordsize; + pool->itemsperblock = itemcount; + + /* Allocate a block of items. Space for `itemsperblock' items and one */ + /* pointer (to point to the next block) are allocated, as well as space */ + /* to ensure alignment of the items. */ + pool->firstblock = (VOID **) malloc(pool->itemsperblock * pool->itembytes + + sizeof(VOID *) + pool->alignbytes); + if (pool->firstblock == (VOID **) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + /* Set the next block pointer to NULL. */ + *(pool->firstblock) = (VOID *) NULL; + poolrestart(pool); +} + +/*****************************************************************************/ +/* */ +/* poolrestart() Deallocate all items in a pool. */ +/* */ +/* The pool is returned to its starting state, except that no memory is */ +/* freed to the operating system. Rather, the previously allocated blocks */ +/* are ready to be reused. */ +/* */ +/*****************************************************************************/ + +void poolrestart(pool) +struct memorypool *pool; +{ + unsigned long alignptr; + + pool->items = 0; + pool->maxitems = 0; + + /* Set the currently active block. */ + pool->nowblock = pool->firstblock; + /* Find the first item in the pool. Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->nowblock + 1); + /* Align the item on an `alignbytes'-byte boundary. */ + pool->nextitem = (VOID *) + (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* There are lots of unallocated items left in this block. */ + pool->unallocateditems = pool->itemsperblock; + /* The stack of deallocated items is empty. */ + pool->deaditemstack = (VOID *) NULL; +} + +/*****************************************************************************/ +/* */ +/* pooldeinit() Free to the operating system all memory taken by a pool. */ +/* */ +/*****************************************************************************/ + +void pooldeinit(pool) +struct memorypool *pool; +{ + while (pool->firstblock != (VOID **) NULL) { + pool->nowblock = (VOID **) *(pool->firstblock); + free(pool->firstblock); + pool->firstblock = pool->nowblock; + } +} + +/*****************************************************************************/ +/* */ +/* poolalloc() Allocate space for an item. */ +/* */ +/*****************************************************************************/ + +VOID *poolalloc(pool) +struct memorypool *pool; +{ + VOID *newitem; + VOID **newblock; + unsigned long alignptr; + + /* First check the linked list of dead items. If the list is not */ + /* empty, allocate an item from the list rather than a fresh one. */ + if (pool->deaditemstack != (VOID *) NULL) { + newitem = pool->deaditemstack; /* Take first item in list. */ + pool->deaditemstack = * (VOID **) pool->deaditemstack; + } else { + /* Check if there are any free items left in the current block. */ + if (pool->unallocateditems == 0) { + /* Check if another block must be allocated. */ + if (*(pool->nowblock) == (VOID *) NULL) { + /* Allocate a new block of items, pointed to by the previous block. */ + newblock = (VOID **) malloc(pool->itemsperblock * pool->itembytes + + sizeof(VOID *) + pool->alignbytes); + if (newblock == (VOID **) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + *(pool->nowblock) = (VOID *) newblock; + /* The next block pointer is NULL. */ + *newblock = (VOID *) NULL; + } + /* Move to the new block. */ + pool->nowblock = (VOID **) *(pool->nowblock); + /* Find the first item in the block. */ + /* Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->nowblock + 1); + /* Align the item on an `alignbytes'-byte boundary. */ + pool->nextitem = (VOID *) + (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* There are lots of unallocated items left in this block. */ + pool->unallocateditems = pool->itemsperblock; + } + /* Allocate a new item. */ + newitem = pool->nextitem; + /* Advance `nextitem' pointer to next free item in block. */ + if (pool->itemwordtype == POINTER) { + pool->nextitem = (VOID *) ((VOID **) pool->nextitem + pool->itemwords); + } else { + pool->nextitem = (VOID *) ((REAL *) pool->nextitem + pool->itemwords); + } + pool->unallocateditems--; + pool->maxitems++; + } + pool->items++; + return newitem; +} + +/*****************************************************************************/ +/* */ +/* pooldealloc() Deallocate space for an item. */ +/* */ +/* The deallocated space is stored in a queue for later reuse. */ +/* */ +/*****************************************************************************/ + +void pooldealloc(pool, dyingitem) +struct memorypool *pool; +VOID *dyingitem; +{ + /* Push freshly killed item onto stack. */ + *((VOID **) dyingitem) = pool->deaditemstack; + pool->deaditemstack = dyingitem; + pool->items--; +} + +/*****************************************************************************/ +/* */ +/* traversalinit() Prepare to traverse the entire list of items. */ +/* */ +/* This routine is used in conjunction with traverse(). */ +/* */ +/*****************************************************************************/ + +void traversalinit(pool) +struct memorypool *pool; +{ + unsigned long alignptr; + + /* Begin the traversal in the first block. */ + pool->pathblock = pool->firstblock; + /* Find the first item in the block. Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->pathblock + 1); + /* Align with item on an `alignbytes'-byte boundary. */ + pool->pathitem = (VOID *) + (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* Set the number of items left in the current block. */ + pool->pathitemsleft = pool->itemsperblock; +} + +/*****************************************************************************/ +/* */ +/* traverse() Find the next item in the list. */ +/* */ +/* This routine is used in conjunction with traversalinit(). Be forewarned */ +/* that this routine successively returns all items in the list, including */ +/* deallocated ones on the deaditemqueue. It's up to you to figure out */ +/* which ones are actually dead. Why? I don't want to allocate extra */ +/* space just to demarcate dead items. It can usually be done more */ +/* space-efficiently by a routine that knows something about the structure */ +/* of the item. */ +/* */ +/*****************************************************************************/ + +VOID *traverse(pool) +struct memorypool *pool; +{ + VOID *newitem; + unsigned long alignptr; + + /* Stop upon exhausting the list of items. */ + if (pool->pathitem == pool->nextitem) { + return (VOID *) NULL; + } + /* Check whether any untraversed items remain in the current block. */ + if (pool->pathitemsleft == 0) { + /* Find the next block. */ + pool->pathblock = (VOID **) *(pool->pathblock); + /* Find the first item in the block. Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->pathblock + 1); + /* Align with item on an `alignbytes'-byte boundary. */ + pool->pathitem = (VOID *) + (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* Set the number of items left in the current block. */ + pool->pathitemsleft = pool->itemsperblock; + } + newitem = pool->pathitem; + /* Find the next item in the block. */ + if (pool->itemwordtype == POINTER) { + pool->pathitem = (VOID *) ((VOID **) pool->pathitem + pool->itemwords); + } else { + pool->pathitem = (VOID *) ((REAL *) pool->pathitem + pool->itemwords); + } + pool->pathitemsleft--; + return newitem; +} + +/*****************************************************************************/ +/* */ +/* dummyinit() Initialize the triangle that fills "outer space" and the */ +/* omnipresent shell edge. */ +/* */ +/* The triangle that fills "outer space", called `dummytri', is pointed to */ +/* by every triangle and shell edge on a boundary (be it outer or inner) of */ +/* the triangulation. Also, `dummytri' points to one of the triangles on */ +/* the convex hull (until the holes and concavities are carved), making it */ +/* possible to find a starting triangle for point location. */ +/* */ +/* The omnipresent shell edge, `dummysh', is pointed to by every triangle */ +/* or shell edge that doesn't have a full complement of real shell edges */ +/* to point to. */ +/* */ +/*****************************************************************************/ + +void dummyinit(trianglewords, shellewords) +int trianglewords; +int shellewords; +{ + unsigned long alignptr; + + /* `triwords' and `shwords' are used by the mesh manipulation primitives */ + /* to extract orientations of triangles and shell edges from pointers. */ + triwords = trianglewords; /* Initialize `triwords' once and for all. */ + shwords = shellewords; /* Initialize `shwords' once and for all. */ + + /* Set up `dummytri', the `triangle' that occupies "outer space". */ + dummytribase = (triangle *) malloc(triwords * sizeof(triangle) + + triangles.alignbytes); + if (dummytribase == (triangle *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + /* Align `dummytri' on a `triangles.alignbytes'-byte boundary. */ + alignptr = (unsigned long) dummytribase; + dummytri = (triangle *) + (alignptr + (unsigned long) triangles.alignbytes + - (alignptr % (unsigned long) triangles.alignbytes)); + /* Initialize the three adjoining triangles to be "outer space". These */ + /* will eventually be changed by various bonding operations, but their */ + /* values don't really matter, as long as they can legally be */ + /* dereferenced. */ + dummytri[0] = (triangle) dummytri; + dummytri[1] = (triangle) dummytri; + dummytri[2] = (triangle) dummytri; + /* Three NULL vertex points. */ + dummytri[3] = (triangle) NULL; + dummytri[4] = (triangle) NULL; + dummytri[5] = (triangle) NULL; + + if (useshelles) { + /* Set up `dummysh', the omnipresent "shell edge" pointed to by any */ + /* triangle side or shell edge end that isn't attached to a real shell */ + /* edge. */ + dummyshbase = (shelle *) malloc(shwords * sizeof(shelle) + + shelles.alignbytes); + if (dummyshbase == (shelle *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + /* Align `dummysh' on a `shelles.alignbytes'-byte boundary. */ + alignptr = (unsigned long) dummyshbase; + dummysh = (shelle *) + (alignptr + (unsigned long) shelles.alignbytes + - (alignptr % (unsigned long) shelles.alignbytes)); + /* Initialize the two adjoining shell edges to be the omnipresent shell */ + /* edge. These will eventually be changed by various bonding */ + /* operations, but their values don't really matter, as long as they */ + /* can legally be dereferenced. */ + dummysh[0] = (shelle) dummysh; + dummysh[1] = (shelle) dummysh; + /* Two NULL vertex points. */ + dummysh[2] = (shelle) NULL; + dummysh[3] = (shelle) NULL; + /* Initialize the two adjoining triangles to be "outer space". */ + dummysh[4] = (shelle) dummytri; + dummysh[5] = (shelle) dummytri; + /* Set the boundary marker to zero. */ + * (int *) (dummysh + 6) = 0; + + /* Initialize the three adjoining shell edges of `dummytri' to be */ + /* the omnipresent shell edge. */ + dummytri[6] = (triangle) dummysh; + dummytri[7] = (triangle) dummysh; + dummytri[8] = (triangle) dummysh; + } +} + +/*****************************************************************************/ +/* */ +/* initializepointpool() Calculate the size of the point data structure */ +/* and initialize its memory pool. */ +/* */ +/* This routine also computes the `pointmarkindex' and `point2triindex' */ +/* indices used to find values within each point. */ +/* */ +/*****************************************************************************/ + +void initializepointpool() +{ + int pointsize; + + /* The index within each point at which the boundary marker is found. */ + /* Ensure the point marker is aligned to a sizeof(int)-byte address. */ + pointmarkindex = ((mesh_dim + nextras) * sizeof(REAL) + sizeof(int) - 1) + / sizeof(int); + pointsize = (pointmarkindex + 1) * sizeof(int); + if (poly) { + /* The index within each point at which a triangle pointer is found. */ + /* Ensure the pointer is aligned to a sizeof(triangle)-byte address. */ + point2triindex = (pointsize + sizeof(triangle) - 1) / sizeof(triangle); + pointsize = (point2triindex + 1) * sizeof(triangle); + } + /* Initialize the pool of points. */ + poolinit(&points, pointsize, POINTPERBLOCK, + (sizeof(REAL) >= sizeof(triangle)) ? FLOATINGPOINT : POINTER, 0); +} + +/*****************************************************************************/ +/* */ +/* initializetrisegpools() Calculate the sizes of the triangle and shell */ +/* edge data structures and initialize their */ +/* memory pools. */ +/* */ +/* This routine also computes the `highorderindex', `elemattribindex', and */ +/* `areaboundindex' indices used to find values within each triangle. */ +/* */ +/*****************************************************************************/ + +void initializetrisegpools() +{ + int trisize; + + /* The index within each triangle at which the extra nodes (above three) */ + /* associated with high order elements are found. There are three */ + /* pointers to other triangles, three pointers to corners, and possibly */ + /* three pointers to shell edges before the extra nodes. */ + highorderindex = 6 + (useshelles * 3); + /* The number of bytes occupied by a triangle. */ + trisize = ((order + 1) * (order + 2) / 2 + (highorderindex - 3)) * + sizeof(triangle); + /* The index within each triangle at which its attributes are found, */ + /* where the index is measured in REALs. */ + elemattribindex = (trisize + sizeof(REAL) - 1) / sizeof(REAL); + /* The index within each triangle at which the maximum area constraint */ + /* is found, where the index is measured in REALs. Note that if the */ + /* `regionattrib' flag is set, an additional attribute will be added. */ + areaboundindex = elemattribindex + eextras + regionattrib; + /* If triangle attributes or an area bound are needed, increase the number */ + /* of bytes occupied by a triangle. */ + if (vararea) { + trisize = (areaboundindex + 1) * sizeof(REAL); + } else if (eextras + regionattrib > 0) { + trisize = areaboundindex * sizeof(REAL); + } + /* If a Voronoi diagram or triangle neighbor graph is requested, make */ + /* sure there's room to store an integer index in each triangle. This */ + /* integer index can occupy the same space as the shell edges or */ + /* attributes or area constraint or extra nodes. */ + if ((voronoi || neighbors) && + (trisize < 6 * sizeof(triangle) + sizeof(int))) { + trisize = 6 * sizeof(triangle) + sizeof(int); + } + /* Having determined the memory size of a triangle, initialize the pool. */ + poolinit(&triangles, trisize, TRIPERBLOCK, POINTER, 4); + + if (useshelles) { + /* Initialize the pool of shell edges. */ + poolinit(&shelles, 6 * sizeof(triangle) + sizeof(int), SHELLEPERBLOCK, + POINTER, 4); + + /* Initialize the "outer space" triangle and omnipresent shell edge. */ + dummyinit(triangles.itemwords, shelles.itemwords); + } else { + /* Initialize the "outer space" triangle. */ + dummyinit(triangles.itemwords, 0); + } +} + +/*****************************************************************************/ +/* */ +/* triangledealloc() Deallocate space for a triangle, marking it dead. */ +/* */ +/*****************************************************************************/ + +void triangledealloc(dyingtriangle) +triangle *dyingtriangle; +{ + /* Set triangle's vertices to NULL. This makes it possible to */ + /* detect dead triangles when traversing the list of all triangles. */ + dyingtriangle[3] = (triangle) NULL; + dyingtriangle[4] = (triangle) NULL; + dyingtriangle[5] = (triangle) NULL; + pooldealloc(&triangles, (VOID *) dyingtriangle); +} + +/*****************************************************************************/ +/* */ +/* triangletraverse() Traverse the triangles, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +triangle *triangletraverse() +{ + triangle *newtriangle; + + do { + newtriangle = (triangle *) traverse(&triangles); + if (newtriangle == (triangle *) NULL) { + return (triangle *) NULL; + } + } while (newtriangle[3] == (triangle) NULL); /* Skip dead ones. */ + return newtriangle; +} + +/*****************************************************************************/ +/* */ +/* shelledealloc() Deallocate space for a shell edge, marking it dead. */ +/* */ +/*****************************************************************************/ + +void shelledealloc(dyingshelle) +shelle *dyingshelle; +{ + /* Set shell edge's vertices to NULL. This makes it possible to */ + /* detect dead shells when traversing the list of all shells. */ + dyingshelle[2] = (shelle) NULL; + dyingshelle[3] = (shelle) NULL; + pooldealloc(&shelles, (VOID *) dyingshelle); +} + +/*****************************************************************************/ +/* */ +/* shelletraverse() Traverse the shell edges, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +shelle *shelletraverse() +{ + shelle *newshelle; + + do { + newshelle = (shelle *) traverse(&shelles); + if (newshelle == (shelle *) NULL) { + return (shelle *) NULL; + } + } while (newshelle[2] == (shelle) NULL); /* Skip dead ones. */ + return newshelle; +} + +/*****************************************************************************/ +/* */ +/* pointdealloc() Deallocate space for a point, marking it dead. */ +/* */ +/*****************************************************************************/ + +void pointdealloc(dyingpoint) +point dyingpoint; +{ + /* Mark the point as dead. This makes it possible to detect dead points */ + /* when traversing the list of all points. */ + setpointmark(dyingpoint, DEADPOINT); + pooldealloc(&points, (VOID *) dyingpoint); +} + +/*****************************************************************************/ +/* */ +/* pointtraverse() Traverse the points, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +point pointtraverse() +{ + point newpoint; + + do { + newpoint = (point) traverse(&points); + if (newpoint == (point) NULL) { + return (point) NULL; + } + } while (pointmark(newpoint) == DEADPOINT); /* Skip dead ones. */ + return newpoint; +} + +/*****************************************************************************/ +/* */ +/* badsegmentdealloc() Deallocate space for a bad segment, marking it */ +/* dead. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +void badsegmentdealloc(dyingseg) +struct edge *dyingseg; +{ + /* Set segment's orientation to -1. This makes it possible to */ + /* detect dead segments when traversing the list of all segments. */ + dyingseg->shorient = -1; + pooldealloc(&badsegments, (VOID *) dyingseg); +} + +#endif /* not CDT_ONLY */ + +/*****************************************************************************/ +/* */ +/* badsegmenttraverse() Traverse the bad segments, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +struct edge *badsegmenttraverse() +{ + struct edge *newseg; + + do { + newseg = (struct edge *) traverse(&badsegments); + if (newseg == (struct edge *) NULL) { + return (struct edge *) NULL; + } + } while (newseg->shorient == -1); /* Skip dead ones. */ + return newseg; +} + +#endif /* not CDT_ONLY */ + +/*****************************************************************************/ +/* */ +/* getpoint() Get a specific point, by number, from the list. */ +/* */ +/* The first point is number 'firstnumber'. */ +/* */ +/* Note that this takes O(n) time (with a small constant, if POINTPERBLOCK */ +/* is large). I don't care to take the trouble to make it work in constant */ +/* time. */ +/* */ +/*****************************************************************************/ + +point getpoint(number) +int number; +{ + VOID **getblock; + point foundpoint; + unsigned long alignptr; + int current; + + getblock = points.firstblock; + current = firstnumber; + /* Find the right block. */ + while (current + points.itemsperblock <= number) { + getblock = (VOID **) *getblock; + current += points.itemsperblock; + } + /* Now find the right point. */ + alignptr = (unsigned long) (getblock + 1); + foundpoint = (point) (alignptr + (unsigned long) points.alignbytes + - (alignptr % (unsigned long) points.alignbytes)); + while (current < number) { + foundpoint += points.itemwords; + current++; + } + return foundpoint; +} + +/*****************************************************************************/ +/* */ +/* triangledeinit() Free all remaining allocated memory. */ +/* */ +/*****************************************************************************/ + +void triangledeinit() +{ + pooldeinit(&triangles); + free(dummytribase); + if (useshelles) { + pooldeinit(&shelles); + free(dummyshbase); + } + pooldeinit(&points); +#ifndef CDT_ONLY + if (quality) { + pooldeinit(&badsegments); + if ((minangle > 0.0) || vararea || fixedarea) { + pooldeinit(&badtriangles); + } + } +#endif /* not CDT_ONLY */ +} + +/** **/ +/** **/ +/********* Memory management routines end here *********/ + +/********* Constructors begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* maketriangle() Create a new triangle with orientation zero. */ +/* */ +/*****************************************************************************/ + +void maketriangle(newtriedge) +struct triedge *newtriedge; +{ + int i; + + newtriedge->tri = (triangle *) poolalloc(&triangles); + /* Initialize the three adjoining triangles to be "outer space". */ + newtriedge->tri[0] = (triangle) dummytri; + newtriedge->tri[1] = (triangle) dummytri; + newtriedge->tri[2] = (triangle) dummytri; + /* Three NULL vertex points. */ + newtriedge->tri[3] = (triangle) NULL; + newtriedge->tri[4] = (triangle) NULL; + newtriedge->tri[5] = (triangle) NULL; + /* Initialize the three adjoining shell edges to be the omnipresent */ + /* shell edge. */ + if (useshelles) { + newtriedge->tri[6] = (triangle) dummysh; + newtriedge->tri[7] = (triangle) dummysh; + newtriedge->tri[8] = (triangle) dummysh; + } + for (i = 0; i < eextras; i++) { + setelemattribute(*newtriedge, i, 0.0); + } + if (vararea) { + setareabound(*newtriedge, -1.0); + } + + newtriedge->orient = 0; +} + +/*****************************************************************************/ +/* */ +/* makeshelle() Create a new shell edge with orientation zero. */ +/* */ +/*****************************************************************************/ + +void makeshelle(newedge) +struct edge *newedge; +{ + newedge->sh = (shelle *) poolalloc(&shelles); + /* Initialize the two adjoining shell edges to be the omnipresent */ + /* shell edge. */ + newedge->sh[0] = (shelle) dummysh; + newedge->sh[1] = (shelle) dummysh; + /* Two NULL vertex points. */ + newedge->sh[2] = (shelle) NULL; + newedge->sh[3] = (shelle) NULL; + /* Initialize the two adjoining triangles to be "outer space". */ + newedge->sh[4] = (shelle) dummytri; + newedge->sh[5] = (shelle) dummytri; + /* Set the boundary marker to zero. */ + setmark(*newedge, 0); + + newedge->shorient = 0; +} + +/** **/ +/** **/ +/********* Constructors end here *********/ + +/********* Determinant evaluation routines begin here *********/ +/** **/ +/** **/ + +/* The adaptive exact arithmetic geometric predicates implemented herein are */ +/* described in detail in my Technical Report CMU-CS-96-140. The complete */ +/* reference is given in the header. */ + +/* Which of the following two methods of finding the absolute values is */ +/* fastest is compiler-dependent. A few compilers can inline and optimize */ +/* the fabs() call; but most will incur the overhead of a function call, */ +/* which is disastrously slow. A faster way on IEEE machines might be to */ +/* mask the appropriate bit, but that's difficult to do in C. */ + +#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) +/* #define Absolute(a) fabs(a) */ + +/* Many of the operations are broken up into two pieces, a main part that */ +/* performs an approximate operation, and a "tail" that computes the */ +/* roundoff error of that operation. */ +/* */ +/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */ +/* Split(), and Two_Product() are all implemented as described in the */ +/* reference. Each of these macros requires certain variables to be */ +/* defined in the calling routine. The variables `bvirt', `c', `abig', */ +/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because */ +/* they store the result of an operation that may incur roundoff error. */ +/* The input parameter `x' (or the highest numbered `x_' parameter) must */ +/* also be declared `INEXACT'. */ + +#define Fast_Two_Sum_Tail(a, b, x, y) \ + bvirt = x - a; \ + y = b - bvirt + +#define Fast_Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Fast_Two_Sum_Tail(a, b, x, y) + +#define Two_Sum_Tail(a, b, x, y) \ + bvirt = (REAL) (x - a); \ + avirt = x - bvirt; \ + bround = b - bvirt; \ + around = a - avirt; \ + y = around + bround + +#define Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Two_Sum_Tail(a, b, x, y) + +#define Two_Diff_Tail(a, b, x, y) \ + bvirt = (REAL) (a - x); \ + avirt = x + bvirt; \ + bround = bvirt - b; \ + around = a - avirt; \ + y = around + bround + +#define Two_Diff(a, b, x, y) \ + x = (REAL) (a - b); \ + Two_Diff_Tail(a, b, x, y) + +#define Split(a, ahi, alo) \ + c = (REAL) (splitter * a); \ + abig = (REAL) (c - a); \ + ahi = c - abig; \ + alo = a - ahi + +#define Two_Product_Tail(a, b, x, y) \ + Split(a, ahi, alo); \ + Split(b, bhi, blo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +#define Two_Product(a, b, x, y) \ + x = (REAL) (a * b); \ + Two_Product_Tail(a, b, x, y) + +/* Two_Product_Presplit() is Two_Product() where one of the inputs has */ +/* already been split. Avoids redundant splitting. */ + +#define Two_Product_Presplit(a, b, bhi, blo, x, y) \ + x = (REAL) (a * b); \ + Split(a, ahi, alo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +/* Square() can be done more quickly than Two_Product(). */ + +#define Square_Tail(a, x, y) \ + Split(a, ahi, alo); \ + err1 = x - (ahi * ahi); \ + err3 = err1 - ((ahi + ahi) * alo); \ + y = (alo * alo) - err3 + +#define Square(a, x, y) \ + x = (REAL) (a * a); \ + Square_Tail(a, x, y) + +/* Macros for summing expansions of various fixed lengths. These are all */ +/* unrolled versions of Expansion_Sum(). */ + +#define Two_One_Sum(a1, a0, b, x2, x1, x0) \ + Two_Sum(a0, b , _i, x0); \ + Two_Sum(a1, _i, x2, x1) + +#define Two_One_Diff(a1, a0, b, x2, x1, x0) \ + Two_Diff(a0, b , _i, x0); \ + Two_Sum( a1, _i, x2, x1) + +#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b0, _j, _0, x0); \ + Two_One_Sum(_j, _0, b1, x3, x2, x1) + +#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Diff(a1, a0, b0, _j, _0, x0); \ + Two_One_Diff(_j, _0, b1, x3, x2, x1) + +/*****************************************************************************/ +/* */ +/* exactinit() Initialize the variables used for exact arithmetic. */ +/* */ +/* `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in */ +/* floating-point arithmetic. `epsilon' bounds the relative roundoff */ +/* error. It is used for floating-point error analysis. */ +/* */ +/* `splitter' is used to split floating-point numbers into two half- */ +/* length significands for exact multiplication. */ +/* */ +/* I imagine that a highly optimizing compiler might be too smart for its */ +/* own good, and somehow cause this routine to fail, if it pretends that */ +/* floating-point arithmetic is too much like real arithmetic. */ +/* */ +/* Don't change this routine unless you fully understand it. */ +/* */ +/*****************************************************************************/ + +void exactinit() +{ + REAL half; + REAL check, lastcheck; + int every_other; + + every_other = 1; + half = 0.5; + epsilon = 1.0; + splitter = 1.0; + check = 1.0; + /* Repeatedly divide `epsilon' by two until it is too small to add to */ + /* one without causing roundoff. (Also check if the sum is equal to */ + /* the previous sum, for machines that round up instead of using exact */ + /* rounding. Not that these routines will work on such machines anyway. */ + do { + lastcheck = check; + epsilon *= half; + if (every_other) { + splitter *= 2.0; + } + every_other = !every_other; + check = 1.0 + epsilon; + } while ((check != 1.0) && (check != lastcheck)); + splitter += 1.0; + if (verbose > 1) { + printf("Floating point roundoff is of magnitude %.17g\n", epsilon); + printf("Floating point splitter is %.17g\n", splitter); + } + /* Error bounds for orientation and incircle tests. */ + resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; + ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; + ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; + ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; + iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; + iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; + iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; +} + +/*****************************************************************************/ +/* */ +/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */ +/* components from the output expansion. */ +/* */ +/* Sets h = e + f. See my Robust Predicates paper for details. */ +/* */ +/* If round-to-even is used (as with IEEE 754), maintains the strongly */ +/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */ +/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */ +/* properties. */ +/* */ +/*****************************************************************************/ + +int fast_expansion_sum_zeroelim(elen, e, flen, f, h) /* h cannot be e or f. */ +int elen; +REAL *e; +int flen; +REAL *f; +REAL *h; +{ + REAL Q; + INEXACT REAL Qnew; + INEXACT REAL hh; + INEXACT REAL bvirt; + REAL avirt, bround, around; + int eindex, findex, hindex; + REAL enow, fnow; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + if ((fnow > enow) == (fnow > -enow)) { + Q = enow; + enow = e[++eindex]; + } else { + Q = fnow; + fnow = f[++findex]; + } + hindex = 0; + if ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Fast_Two_Sum(enow, Q, Qnew, hh); + enow = e[++eindex]; + } else { + Fast_Two_Sum(fnow, Q, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + while ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + } else { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + } + while (eindex < elen) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + while (findex < flen) { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */ +/* eliminating zero components from the */ +/* output expansion. */ +/* */ +/* Sets h = be. See my Robust Predicates paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */ +/* properties as well. (That is, if e has one of these properties, so */ +/* will h.) */ +/* */ +/*****************************************************************************/ + +int scale_expansion_zeroelim(elen, e, b, h) /* e and h cannot be the same. */ +int elen; +REAL *e; +REAL b; +REAL *h; +{ + INEXACT REAL Q, sum; + REAL hh; + INEXACT REAL product1; + REAL product0; + int eindex, hindex; + REAL enow; + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + + Split(b, bhi, blo); + Two_Product_Presplit(e[0], b, bhi, blo, Q, hh); + hindex = 0; + if (hh != 0) { + h[hindex++] = hh; + } + for (eindex = 1; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Product_Presplit(enow, b, bhi, blo, product1, product0); + Two_Sum(Q, product0, sum, hh); + if (hh != 0) { + h[hindex++] = hh; + } + Fast_Two_Sum(product1, sum, Q, hh); + if (hh != 0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* estimate() Produce a one-word estimate of an expansion's value. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL estimate(elen, e) +int elen; +REAL *e; +{ + REAL Q; + int eindex; + + Q = e[0]; + for (eindex = 1; eindex < elen; eindex++) { + Q += e[eindex]; + } + return Q; +} + +/*****************************************************************************/ +/* */ +/* counterclockwise() Return a positive value if the points pa, pb, and */ +/* pc occur in counterclockwise order; a negative */ +/* value if they occur in clockwise order; and zero */ +/* if they are collinear. The result is also a rough */ +/* approximation of twice the signed area of the */ +/* triangle defined by the three points. */ +/* */ +/* Uses exact arithmetic if necessary to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. This determinant is */ +/* computed adaptively, in the sense that exact arithmetic is used only to */ +/* the degree it is needed to ensure that the returned value has the */ +/* correct sign. Hence, this function is usually quite fast, but will run */ +/* more slowly when the input points are collinear or nearly so. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL counterclockwiseadapt(pa, pb, pc, detsum) +point pa; +point pb; +point pc; +REAL detsum; +{ + INEXACT REAL acx, acy, bcx, bcy; + REAL acxtail, acytail, bcxtail, bcytail; + INEXACT REAL detleft, detright; + REAL detlefttail, detrighttail; + REAL det, errbound; + REAL B[4], C1[8], C2[12], D[16]; + INEXACT REAL B3; + int C1length, C2length, Dlength; + REAL u[4]; + INEXACT REAL u3; + INEXACT REAL s1, t1; + REAL s0, t0; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + acx = (REAL) (pa[0] - pc[0]); + bcx = (REAL) (pb[0] - pc[0]); + acy = (REAL) (pa[1] - pc[1]); + bcy = (REAL) (pb[1] - pc[1]); + + Two_Product(acx, bcy, detleft, detlefttail); + Two_Product(acy, bcx, detright, detrighttail); + + Two_Two_Diff(detleft, detlefttail, detright, detrighttail, + B3, B[2], B[1], B[0]); + B[3] = B3; + + det = estimate(4, B); + errbound = ccwerrboundB * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pc[0], acx, acxtail); + Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); + Two_Diff_Tail(pa[1], pc[1], acy, acytail); + Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); + + if ((acxtail == 0.0) && (acytail == 0.0) + && (bcxtail == 0.0) && (bcytail == 0.0)) { + return det; + } + + errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); + det += (acx * bcytail + bcy * acxtail) + - (acy * bcxtail + bcx * acytail); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Product(acxtail, bcy, s1, s0); + Two_Product(acytail, bcx, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); + + Two_Product(acx, bcytail, s1, s0); + Two_Product(acy, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); + + Two_Product(acxtail, bcytail, s1, s0); + Two_Product(acytail, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); + + return(D[Dlength - 1]); +} + +REAL counterclockwise(pa, pb, pc) +point pa; +point pb; +point pc; +{ + REAL detleft, detright, det; + REAL detsum, errbound; + + counterclockcount++; + + detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); + detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); + det = detleft - detright; + + if (noexact) { + return det; + } + + if (detleft > 0.0) { + if (detright <= 0.0) { + return det; + } else { + detsum = detleft + detright; + } + } else if (detleft < 0.0) { + if (detright >= 0.0) { + return det; + } else { + detsum = -detleft - detright; + } + } else { + return det; + } + + errbound = ccwerrboundA * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return counterclockwiseadapt(pa, pb, pc, detsum); +} + +/*****************************************************************************/ +/* */ +/* incircle() Return a positive value if the point pd lies inside the */ +/* circle passing through pa, pb, and pc; a negative value if */ +/* it lies outside; and zero if the four points are cocircular.*/ +/* The points pa, pb, and pc must be in counterclockwise */ +/* order, or the sign of the result will be reversed. */ +/* */ +/* Uses exact arithmetic if necessary to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. This determinant is */ +/* computed adaptively, in the sense that exact arithmetic is used only to */ +/* the degree it is needed to ensure that the returned value has the */ +/* correct sign. Hence, this function is usually quite fast, but will run */ +/* more slowly when the input points are cocircular or nearly so. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL incircleadapt(pa, pb, pc, pd, permanent) +point pa; +point pb; +point pc; +point pd; +REAL permanent; +{ + INEXACT REAL adx, bdx, cdx, ady, bdy, cdy; + REAL det, errbound; + + INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + INEXACT REAL bc3, ca3, ab3; + REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; + int axbclen, axxbclen, aybclen, ayybclen, alen; + REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; + int bxcalen, bxxcalen, bycalen, byycalen, blen; + REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; + int cxablen, cxxablen, cyablen, cyyablen, clen; + REAL abdet[64]; + int ablen; + REAL fin1[1152], fin2[1152]; + REAL *finnow, *finother, *finswap; + int finlength; + + REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; + INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; + REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; + REAL aa[4], bb[4], cc[4]; + INEXACT REAL aa3, bb3, cc3; + INEXACT REAL ti1, tj1; + REAL ti0, tj0; + REAL u[4], v[4]; + INEXACT REAL u3, v3; + REAL temp8[8], temp16a[16], temp16b[16], temp16c[16]; + REAL temp32a[32], temp32b[32], temp48[48], temp64[64]; + int temp8len, temp16alen, temp16blen, temp16clen; + int temp32alen, temp32blen, temp48len, temp64len; + REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8]; + int axtbblen, axtcclen, aytbblen, aytcclen; + REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; + int bxtaalen, bxtcclen, bytaalen, bytcclen; + REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; + int cxtaalen, cxtbblen, cytaalen, cytbblen; + REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; + int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen; + REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; + int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; + REAL axtbctt[8], aytbctt[8], bxtcatt[8]; + REAL bytcatt[8], cxtabtt[8], cytabtt[8]; + int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; + REAL abt[8], bct[8], cat[8]; + int abtlen, bctlen, catlen; + REAL abtt[4], bctt[4], catt[4]; + int abttlen, bcttlen, cattlen; + INEXACT REAL abtt3, bctt3, catt3; + REAL negate; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); + axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); + aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); + ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); + alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); + bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); + bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); + byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); + blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); + cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); + cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); + cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); + clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = iccerrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) + && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) { + return det; + } + + errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); + det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail) + - (bdy * cdxtail + cdx * bdytail)) + + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) + + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail) + - (cdy * adxtail + adx * cdytail)) + + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) + + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail) + - (ady * bdxtail + bdx * adytail)) + + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if ((bdxtail != 0.0) || (bdytail != 0.0) + || (cdxtail != 0.0) || (cdytail != 0.0)) { + Square(adx, adxadx1, adxadx0); + Square(ady, adyady1, adyady0); + Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); + aa[3] = aa3; + } + if ((cdxtail != 0.0) || (cdytail != 0.0) + || (adxtail != 0.0) || (adytail != 0.0)) { + Square(bdx, bdxbdx1, bdxbdx0); + Square(bdy, bdybdy1, bdybdy0); + Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); + bb[3] = bb3; + } + if ((adxtail != 0.0) || (adytail != 0.0) + || (bdxtail != 0.0) || (bdytail != 0.0)) { + Square(cdx, cdxcdx1, cdxcdx0); + Square(cdy, cdycdy1, cdycdy0); + Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); + cc[3] = cc3; + } + + if (adxtail != 0.0) { + axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, + temp16a); + + axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); + temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); + + axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); + temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, + temp16a); + + aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); + temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); + + aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); + temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdxtail != 0.0) { + bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, + temp16a); + + bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); + temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); + + bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); + temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, + temp16a); + + bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); + temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); + + bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); + temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdxtail != 0.0) { + cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, + temp16a); + + cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); + temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); + + cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); + temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); + temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, + temp16a); + + cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); + temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); + + cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); + temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + if ((adxtail != 0.0) || (adytail != 0.0)) { + if ((bdxtail != 0.0) || (bdytail != 0.0) + || (cdxtail != 0.0) || (cdytail != 0.0)) { + Two_Product(bdxtail, cdy, ti1, ti0); + Two_Product(bdx, cdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -bdy; + Two_Product(cdxtail, negate, ti1, ti0); + negate = -bdytail; + Two_Product(cdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); + + Two_Product(bdxtail, cdytail, ti1, ti0); + Two_Product(cdxtail, bdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); + bctt[3] = bctt3; + bcttlen = 4; + } else { + bct[0] = 0.0; + bctlen = 1; + bctt[0] = 0.0; + bcttlen = 1; + } + + if (adxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); + axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, + temp32a); + axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); + temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, + temp16a); + temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); + aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, + temp32a); + aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); + temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, + temp16a); + temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if ((bdxtail != 0.0) || (bdytail != 0.0)) { + if ((cdxtail != 0.0) || (cdytail != 0.0) + || (adxtail != 0.0) || (adytail != 0.0)) { + Two_Product(cdxtail, ady, ti1, ti0); + Two_Product(cdx, adytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -cdy; + Two_Product(adxtail, negate, ti1, ti0); + negate = -cdytail; + Two_Product(adx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); + + Two_Product(cdxtail, adytail, ti1, ti0); + Two_Product(adxtail, cdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); + catt[3] = catt3; + cattlen = 4; + } else { + cat[0] = 0.0; + catlen = 1; + catt[0] = 0.0; + cattlen = 1; + } + + if (bdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); + bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, + temp32a); + bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); + temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, + temp16a); + temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); + bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, + temp32a); + bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); + temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, + temp16a); + temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if ((cdxtail != 0.0) || (cdytail != 0.0)) { + if ((adxtail != 0.0) || (adytail != 0.0) + || (bdxtail != 0.0) || (bdytail != 0.0)) { + Two_Product(adxtail, bdy, ti1, ti0); + Two_Product(adx, bdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -ady; + Two_Product(bdxtail, negate, ti1, ti0); + negate = -adytail; + Two_Product(bdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); + + Two_Product(adxtail, bdytail, ti1, ti0); + Two_Product(bdxtail, adytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); + abtt[3] = abtt3; + abttlen = 4; + } else { + abt[0] = 0.0; + abtlen = 1; + abtt[0] = 0.0; + abttlen = 1; + } + + if (cdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); + cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, + temp32a); + cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); + temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, + temp16a); + temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); + cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, + temp32a); + cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); + temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, + temp16a); + temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + + return finnow[finlength - 1]; +} + +REAL incircle(pa, pb, pc, pd) +point pa; +point pb; +point pc; +point pd; +{ + REAL adx, bdx, cdx, ady, bdy, cdy; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL alift, blift, clift; + REAL det; + REAL permanent, errbound; + + incirclecount++; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + alift = adx * adx + ady * ady; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + blift = bdx * bdx + bdy * bdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + clift = cdx * cdx + cdy * cdy; + + det = alift * (bdxcdy - cdxbdy) + + blift * (cdxady - adxcdy) + + clift * (adxbdy - bdxady); + + if (noexact) { + return det; + } + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift + + (Absolute(cdxady) + Absolute(adxcdy)) * blift + + (Absolute(adxbdy) + Absolute(bdxady)) * clift; + errbound = iccerrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return incircleadapt(pa, pb, pc, pd, permanent); +} + +/** **/ +/** **/ +/********* Determinant evaluation routines end here *********/ + +/*****************************************************************************/ +/* */ +/* triangleinit() Initialize some variables. */ +/* */ +/*****************************************************************************/ + +void triangleinit() +{ + points.maxitems = triangles.maxitems = shelles.maxitems = viri.maxitems = + badsegments.maxitems = badtriangles.maxitems = splaynodes.maxitems = 0l; + points.itembytes = triangles.itembytes = shelles.itembytes = viri.itembytes = + badsegments.itembytes = badtriangles.itembytes = splaynodes.itembytes = 0; + recenttri.tri = (triangle *) NULL; /* No triangle has been visited yet. */ + samples = 1; /* Point location should take at least one sample. */ + checksegments = 0; /* There are no segments in the triangulation yet. */ + incirclecount = counterclockcount = hyperbolacount = 0; + circumcentercount = circletopcount = 0; + randomseed = 1; + + exactinit(); /* Initialize exact arithmetic constants. */ +} + +/*****************************************************************************/ +/* */ +/* randomnation() Generate a random number between 0 and `choices' - 1. */ +/* */ +/* This is a simple linear congruential random number generator. Hence, it */ +/* is a bad random number generator, but good enough for most randomized */ +/* geometric algorithms. */ +/* */ +/*****************************************************************************/ + +unsigned long randomnation(choices) +unsigned int choices; +{ + randomseed = (randomseed * 1366l + 150889l) % 714025l; + return randomseed / (714025l / choices + 1); +} + +/********* Mesh quality testing routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* checkmesh() Test the mesh for topological consistency. */ +/* */ +/*****************************************************************************/ + +#ifndef REDUCED + +void checkmesh() +{ + struct triedge triangleloop; + struct triedge oppotri, oppooppotri; + point triorg, tridest, triapex; + point oppoorg, oppodest; + int horrors; + int saveexact; + triangle ptr; /* Temporary variable used by sym(). */ + + /* Temporarily turn on exact arithmetic if it's off. */ + saveexact = noexact; + noexact = 0; + if (!quiet) { + printf(" Checking consistency of mesh...\n"); + } + horrors = 0; + /* Run through the list of triangles, checking each one. */ + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + while (triangleloop.tri != (triangle *) NULL) { + /* Check all three edges of the triangle. */ + for (triangleloop.orient = 0; triangleloop.orient < 3; + triangleloop.orient++) { + org(triangleloop, triorg); + dest(triangleloop, tridest); + if (triangleloop.orient == 0) { /* Only test for inversion once. */ + /* Test if the triangle is flat or inverted. */ + apex(triangleloop, triapex); + if (counterclockwise(triorg, tridest, triapex) <= 0.0) { + printf(" !! !! Inverted "); + printtriangle(&triangleloop); + horrors++; + } + } + /* Find the neighboring triangle on this edge. */ + sym(triangleloop, oppotri); + if (oppotri.tri != dummytri) { + /* Check that the triangle's neighbor knows it's a neighbor. */ + sym(oppotri, oppooppotri); + if ((triangleloop.tri != oppooppotri.tri) + || (triangleloop.orient != oppooppotri.orient)) { + printf(" !! !! Asymmetric triangle-triangle bond:\n"); + if (triangleloop.tri == oppooppotri.tri) { + printf(" (Right triangle, wrong orientation)\n"); + } + printf(" First "); + printtriangle(&triangleloop); + printf(" Second (nonreciprocating) "); + printtriangle(&oppotri); + horrors++; + } + /* Check that both triangles agree on the identities */ + /* of their shared vertices. */ + org(oppotri, oppoorg); + dest(oppotri, oppodest); + if ((triorg != oppodest) || (tridest != oppoorg)) { + printf(" !! !! Mismatched edge coordinates between two triangles:\n" + ); + printf(" First mismatched "); + printtriangle(&triangleloop); + printf(" Second mismatched "); + printtriangle(&oppotri); + horrors++; + } + } + } + triangleloop.tri = triangletraverse(); + } + if (horrors == 0) { + if (!quiet) { + printf(" In my studied opinion, the mesh appears to be consistent.\n"); + } + } else if (horrors == 1) { + printf(" !! !! !! !! Precisely one festering wound discovered.\n"); + } else { + printf(" !! !! !! !! %d abominations witnessed.\n", horrors); + } + /* Restore the status of exact arithmetic. */ + noexact = saveexact; +} + +#endif /* not REDUCED */ + +/*****************************************************************************/ +/* */ +/* checkdelaunay() Ensure that the mesh is (constrained) Delaunay. */ +/* */ +/*****************************************************************************/ + +#ifndef REDUCED + +void checkdelaunay() +{ + struct triedge triangleloop; + struct triedge oppotri; + struct edge opposhelle; + point triorg, tridest, triapex; + point oppoapex; + int shouldbedelaunay; + int horrors; + int saveexact; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + /* Temporarily turn on exact arithmetic if it's off. */ + saveexact = noexact; + noexact = 0; + if (!quiet) { + printf(" Checking Delaunay property of mesh...\n"); + } + horrors = 0; + /* Run through the list of triangles, checking each one. */ + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + while (triangleloop.tri != (triangle *) NULL) { + /* Check all three edges of the triangle. */ + for (triangleloop.orient = 0; triangleloop.orient < 3; + triangleloop.orient++) { + org(triangleloop, triorg); + dest(triangleloop, tridest); + apex(triangleloop, triapex); + sym(triangleloop, oppotri); + apex(oppotri, oppoapex); + /* Only test that the edge is locally Delaunay if there is an */ + /* adjoining triangle whose pointer is larger (to ensure that */ + /* each pair isn't tested twice). */ + shouldbedelaunay = (oppotri.tri != dummytri) + && (triapex != (point) NULL) && (oppoapex != (point) NULL) + && (triangleloop.tri < oppotri.tri); + if (checksegments && shouldbedelaunay) { + /* If a shell edge separates the triangles, then the edge is */ + /* constrained, so no local Delaunay test should be done. */ + tspivot(triangleloop, opposhelle); + if (opposhelle.sh != dummysh){ + shouldbedelaunay = 0; + } + } + if (shouldbedelaunay) { + if (incircle(triorg, tridest, triapex, oppoapex) > 0.0) { + printf(" !! !! Non-Delaunay pair of triangles:\n"); + printf(" First non-Delaunay "); + printtriangle(&triangleloop); + printf(" Second non-Delaunay "); + printtriangle(&oppotri); + horrors++; + } + } + } + triangleloop.tri = triangletraverse(); + } + if (horrors == 0) { + if (!quiet) { + printf( + " By virtue of my perceptive intelligence, I declare the mesh Delaunay.\n"); + } + } else if (horrors == 1) { + printf( + " !! !! !! !! Precisely one terrifying transgression identified.\n"); + } else { + printf(" !! !! !! !! %d obscenities viewed with horror.\n", horrors); + } + /* Restore the status of exact arithmetic. */ + noexact = saveexact; +} + +#endif /* not REDUCED */ + +/*****************************************************************************/ +/* */ +/* enqueuebadtri() Add a bad triangle to the end of a queue. */ +/* */ +/* The queue is actually a set of 64 queues. I use multiple queues to give */ +/* priority to smaller angles. I originally implemented a heap, but the */ +/* queues are (to my surprise) much faster. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +void enqueuebadtri(instri, angle, insapex, insorg, insdest) +struct triedge *instri; +REAL angle; +point insapex; +point insorg; +point insdest; +{ + struct badface *newface; + int queuenumber; + + if (verbose > 2) { + printf(" Queueing bad triangle:\n"); + printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", insorg[0], + insorg[1], insdest[0], insdest[1], insapex[0], insapex[1]); + } + /* Allocate space for the bad triangle. */ + newface = (struct badface *) poolalloc(&badtriangles); + triedgecopy(*instri, newface->badfacetri); + newface->key = angle; + newface->faceapex = insapex; + newface->faceorg = insorg; + newface->facedest = insdest; + newface->nextface = (struct badface *) NULL; + /* Determine the appropriate queue to put the bad triangle into. */ + if (angle > 0.6) { + queuenumber = (int) (160.0 * (angle - 0.6)); + if (queuenumber > 63) { + queuenumber = 63; + } + } else { + /* It's not a bad angle; put the triangle in the lowest-priority queue. */ + queuenumber = 0; + } + /* Add the triangle to the end of a queue. */ + *queuetail[queuenumber] = newface; + /* Maintain a pointer to the NULL pointer at the end of the queue. */ + queuetail[queuenumber] = &newface->nextface; +} + +#endif /* not CDT_ONLY */ + +/*****************************************************************************/ +/* */ +/* dequeuebadtri() Remove a triangle from the front of the queue. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +struct badface *dequeuebadtri() +{ + struct badface *result; + int queuenumber; + + /* Look for a nonempty queue. */ + for (queuenumber = 63; queuenumber >= 0; queuenumber--) { + result = queuefront[queuenumber]; + if (result != (struct badface *) NULL) { + /* Remove the triangle from the queue. */ + queuefront[queuenumber] = result->nextface; + /* Maintain a pointer to the NULL pointer at the end of the queue. */ + if (queuefront[queuenumber] == (struct badface *) NULL) { + queuetail[queuenumber] = &queuefront[queuenumber]; + } + return result; + } + } + return (struct badface *) NULL; +} + +#endif /* not CDT_ONLY */ + +/*****************************************************************************/ +/* */ +/* checkedge4encroach() Check a segment to see if it is encroached; add */ +/* it to the list if it is. */ +/* */ +/* An encroached segment is an unflippable edge that has a point in its */ +/* diametral circle (that is, it faces an angle greater than 90 degrees). */ +/* This definition is due to Ruppert. */ +/* */ +/* Returns a nonzero value if the edge is encroached. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +int checkedge4encroach(testedge) +struct edge *testedge; +{ + struct triedge neighbortri; + struct edge testsym; + struct edge *badedge; + int addtolist; + int sides; + point eorg, edest, eapex; + triangle ptr; /* Temporary variable used by stpivot(). */ + + addtolist = 0; + sides = 0; + + sorg(*testedge, eorg); + sdest(*testedge, edest); + /* Check one neighbor of the shell edge. */ + stpivot(*testedge, neighbortri); + /* Does the neighbor exist, or is this a boundary edge? */ + if (neighbortri.tri != dummytri) { + sides++; + /* Find a vertex opposite this edge. */ + apex(neighbortri, eapex); + /* Check whether the vertex is inside the diametral circle of the */ + /* shell edge. Pythagoras' Theorem is used to check whether the */ + /* angle at the vertex is greater than 90 degrees. */ + if (eapex[0] * (eorg[0] + edest[0]) + eapex[1] * (eorg[1] + edest[1]) > + eapex[0] * eapex[0] + eorg[0] * edest[0] + + eapex[1] * eapex[1] + eorg[1] * edest[1]) { + addtolist = 1; + } + } + /* Check the other neighbor of the shell edge. */ + ssym(*testedge, testsym); + stpivot(testsym, neighbortri); + /* Does the neighbor exist, or is this a boundary edge? */ + if (neighbortri.tri != dummytri) { + sides++; + /* Find the other vertex opposite this edge. */ + apex(neighbortri, eapex); + /* Check whether the vertex is inside the diametral circle of the */ + /* shell edge. Pythagoras' Theorem is used to check whether the */ + /* angle at the vertex is greater than 90 degrees. */ + if (eapex[0] * (eorg[0] + edest[0]) + + eapex[1] * (eorg[1] + edest[1]) > + eapex[0] * eapex[0] + eorg[0] * edest[0] + + eapex[1] * eapex[1] + eorg[1] * edest[1]) { + addtolist += 2; + } + } + + if (addtolist && (!nobisect || ((nobisect == 1) && (sides == 2)))) { + if (verbose > 2) { + printf(" Queueing encroached segment (%.12g, %.12g) (%.12g, %.12g).\n", + eorg[0], eorg[1], edest[0], edest[1]); + } + /* Add the shell edge to the list of encroached segments. */ + /* Be sure to get the orientation right. */ + badedge = (struct edge *) poolalloc(&badsegments); + if (addtolist == 1) { + shellecopy(*testedge, *badedge); + } else { + shellecopy(testsym, *badedge); + } + } + return addtolist; +} + +#endif /* not CDT_ONLY */ + +/*****************************************************************************/ +/* */ +/* testtriangle() Test a face for quality measures. */ +/* */ +/* Tests a triangle to see if it satisfies the minimum angle condition and */ +/* the maximum area condition. Triangles that aren't up to spec are added */ +/* to the bad triangle queue. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +void testtriangle(testtri) +struct triedge *testtri; +{ + struct triedge sametesttri; + struct edge edge1, edge2; + point torg, tdest, tapex; + point anglevertex; + REAL dxod, dyod, dxda, dyda, dxao, dyao; + REAL dxod2, dyod2, dxda2, dyda2, dxao2, dyao2; + REAL apexlen, orglen, destlen; + REAL angle; + REAL area; + shelle sptr; /* Temporary variable used by tspivot(). */ + + org(*testtri, torg); + dest(*testtri, tdest); + apex(*testtri, tapex); + dxod = torg[0] - tdest[0]; + dyod = torg[1] - tdest[1]; + dxda = tdest[0] - tapex[0]; + dyda = tdest[1] - tapex[1]; + dxao = tapex[0] - torg[0]; + dyao = tapex[1] - torg[1]; + dxod2 = dxod * dxod; + dyod2 = dyod * dyod; + dxda2 = dxda * dxda; + dyda2 = dyda * dyda; + dxao2 = dxao * dxao; + dyao2 = dyao * dyao; + /* Find the lengths of the triangle's three edges. */ + apexlen = dxod2 + dyod2; + orglen = dxda2 + dyda2; + destlen = dxao2 + dyao2; + if ((apexlen < orglen) && (apexlen < destlen)) { + /* The edge opposite the apex is shortest. */ + /* Find the square of the cosine of the angle at the apex. */ + angle = dxda * dxao + dyda * dyao; + angle = angle * angle / (orglen * destlen); + anglevertex = tapex; + lnext(*testtri, sametesttri); + tspivot(sametesttri, edge1); + lnextself(sametesttri); + tspivot(sametesttri, edge2); + } else if (orglen < destlen) { + /* The edge opposite the origin is shortest. */ + /* Find the square of the cosine of the angle at the origin. */ + angle = dxod * dxao + dyod * dyao; + angle = angle * angle / (apexlen * destlen); + anglevertex = torg; + tspivot(*testtri, edge1); + lprev(*testtri, sametesttri); + tspivot(sametesttri, edge2); + } else { + /* The edge opposite the destination is shortest. */ + /* Find the square of the cosine of the angle at the destination. */ + angle = dxod * dxda + dyod * dyda; + angle = angle * angle / (apexlen * orglen); + anglevertex = tdest; + tspivot(*testtri, edge1); + lnext(*testtri, sametesttri); + tspivot(sametesttri, edge2); + } + /* Check if both edges that form the angle are segments. */ + if ((edge1.sh != dummysh) && (edge2.sh != dummysh)) { + /* The angle is a segment intersection. */ + if ((angle > 0.9924) && !quiet) { /* Roughly 5 degrees. */ + if (angle > 1.0) { + /* Beware of a floating exception in acos(). */ + angle = 1.0; + } + /* Find the actual angle in degrees, for printing. */ + angle = acos(sqrt(angle)) * (180.0 / PI); + printf( + "Warning: Small angle (%.4g degrees) between segments at point\n", + angle); + printf(" (%.12g, %.12g)\n", anglevertex[0], anglevertex[1]); + } + /* Don't add this bad triangle to the list; there's nothing that */ + /* can be done about a small angle between two segments. */ + angle = 0.0; + } + /* Check whether the angle is smaller than permitted. */ + if (angle > goodangle) { + /* Add this triangle to the list of bad triangles. */ + enqueuebadtri(testtri, angle, tapex, torg, tdest); + return; + } + if (vararea || fixedarea) { + /* Check whether the area is larger than permitted. */ + area = 0.5 * (dxod * dyda - dyod * dxda); + +#if 0 + if ( area < 1.0 / (2.0 * 3600.0 * 3600.0) ) { + /* FGFS ADDITION!!! */ + /* small enough, don't add to list of bad triangles */ + printf("REJECTING TRIANGLE OF AREA %.6g\n", area); + } +#endif + + if (fixedarea && (area > maxarea)) { + /* Add this triangle to the list of bad triangles. */ + enqueuebadtri(testtri, angle, tapex, torg, tdest); + } else if (vararea) { + /* Nonpositive area constraints are treated as unconstrained. */ + if ((area > areabound(*testtri)) && (areabound(*testtri) > 0.0)) { + /* Add this triangle to the list of bad triangles. */ + enqueuebadtri(testtri, angle, tapex, torg, tdest); + } + } + } +} + +#endif /* not CDT_ONLY */ + +/** **/ +/** **/ +/********* Mesh quality testing routines end here *********/ + +/********* Point location routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* makepointmap() Construct a mapping from points to triangles to improve */ +/* the speed of point location for segment insertion. */ +/* */ +/* Traverses all the triangles, and provides each corner of each triangle */ +/* with a pointer to that triangle. Of course, pointers will be */ +/* overwritten by other pointers because (almost) each point is a corner */ +/* of several triangles, but in the end every point will point to some */ +/* triangle that contains it. */ +/* */ +/*****************************************************************************/ + +void makepointmap() +{ + struct triedge triangleloop; + point triorg; + + if (verbose) { + printf(" Constructing mapping from points to triangles.\n"); + } + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + while (triangleloop.tri != (triangle *) NULL) { + /* Check all three points of the triangle. */ + for (triangleloop.orient = 0; triangleloop.orient < 3; + triangleloop.orient++) { + org(triangleloop, triorg); + setpoint2tri(triorg, encode(triangleloop)); + } + triangleloop.tri = triangletraverse(); + } +} + +/*****************************************************************************/ +/* */ +/* preciselocate() Find a triangle or edge containing a given point. */ +/* */ +/* Begins its search from `searchtri'. It is important that `searchtri' */ +/* be a handle with the property that `searchpoint' is strictly to the left */ +/* of the edge denoted by `searchtri', or is collinear with that edge and */ +/* does not intersect that edge. (In particular, `searchpoint' should not */ +/* be the origin or destination of that edge.) */ +/* */ +/* These conditions are imposed because preciselocate() is normally used in */ +/* one of two situations: */ +/* */ +/* (1) To try to find the location to insert a new point. Normally, we */ +/* know an edge that the point is strictly to the left of. In the */ +/* incremental Delaunay algorithm, that edge is a bounding box edge. */ +/* In Ruppert's Delaunay refinement algorithm for quality meshing, */ +/* that edge is the shortest edge of the triangle whose circumcenter */ +/* is being inserted. */ +/* */ +/* (2) To try to find an existing point. In this case, any edge on the */ +/* convex hull is a good starting edge. The possibility that the */ +/* vertex one seeks is an endpoint of the starting edge must be */ +/* screened out before preciselocate() is called. */ +/* */ +/* On completion, `searchtri' is a triangle that contains `searchpoint'. */ +/* */ +/* This implementation differs from that given by Guibas and Stolfi. It */ +/* walks from triangle to triangle, crossing an edge only if `searchpoint' */ +/* is on the other side of the line containing that edge. After entering */ +/* a triangle, there are two edges by which one can leave that triangle. */ +/* If both edges are valid (`searchpoint' is on the other side of both */ +/* edges), one of the two is chosen by drawing a line perpendicular to */ +/* the entry edge (whose endpoints are `forg' and `fdest') passing through */ +/* `fapex'. Depending on which side of this perpendicular `searchpoint' */ +/* falls on, an exit edge is chosen. */ +/* */ +/* This implementation is empirically faster than the Guibas and Stolfi */ +/* point location routine (which I originally used), which tends to spiral */ +/* in toward its target. */ +/* */ +/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */ +/* is a handle whose origin is the existing vertex. */ +/* */ +/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */ +/* handle whose primary edge is the edge on which the point lies. */ +/* */ +/* Returns INTRIANGLE if the point lies strictly within a triangle. */ +/* `searchtri' is a handle on the triangle that contains the point. */ +/* */ +/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */ +/* handle whose primary edge the point is to the right of. This might */ +/* occur when the circumcenter of a triangle falls just slightly outside */ +/* the mesh due to floating-point roundoff error. It also occurs when */ +/* seeking a hole or region point that a foolish user has placed outside */ +/* the mesh. */ +/* */ +/* WARNING: This routine is designed for convex triangulations, and will */ +/* not generally work after the holes and concavities have been carved. */ +/* However, it can still be used to find the circumcenter of a triangle, as */ +/* long as the search is begun from the triangle in question. */ +/* */ +/*****************************************************************************/ + +enum locateresult preciselocate(searchpoint, searchtri) +point searchpoint; +struct triedge *searchtri; +{ + struct triedge backtracktri; + point forg, fdest, fapex; + point swappoint; + REAL orgorient, destorient; + int moveleft; + triangle ptr; /* Temporary variable used by sym(). */ + + if (verbose > 2) { + printf(" Searching for point (%.12g, %.12g).\n", + searchpoint[0], searchpoint[1]); + } + /* Where are we? */ + org(*searchtri, forg); + dest(*searchtri, fdest); + apex(*searchtri, fapex); + while (1) { + if (verbose > 2) { + printf(" At (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + forg[0], forg[1], fdest[0], fdest[1], fapex[0], fapex[1]); + } + /* Check whether the apex is the point we seek. */ + if ((fapex[0] == searchpoint[0]) && (fapex[1] == searchpoint[1])) { + lprevself(*searchtri); + return ONVERTEX; + } + /* Does the point lie on the other side of the line defined by the */ + /* triangle edge opposite the triangle's destination? */ + destorient = counterclockwise(forg, fapex, searchpoint); + /* Does the point lie on the other side of the line defined by the */ + /* triangle edge opposite the triangle's origin? */ + orgorient = counterclockwise(fapex, fdest, searchpoint); + if (destorient > 0.0) { + if (orgorient > 0.0) { + /* Move left if the inner product of (fapex - searchpoint) and */ + /* (fdest - forg) is positive. This is equivalent to drawing */ + /* a line perpendicular to the line (forg, fdest) passing */ + /* through `fapex', and determining which side of this line */ + /* `searchpoint' falls on. */ + moveleft = (fapex[0] - searchpoint[0]) * (fdest[0] - forg[0]) + + (fapex[1] - searchpoint[1]) * (fdest[1] - forg[1]) > 0.0; + } else { + moveleft = 1; + } + } else { + if (orgorient > 0.0) { + moveleft = 0; + } else { + /* The point we seek must be on the boundary of or inside this */ + /* triangle. */ + if (destorient == 0.0) { + lprevself(*searchtri); + return ONEDGE; + } + if (orgorient == 0.0) { + lnextself(*searchtri); + return ONEDGE; + } + return INTRIANGLE; + } + } + + /* Move to another triangle. Leave a trace `backtracktri' in case */ + /* floating-point roundoff or some such bogey causes us to walk */ + /* off a boundary of the triangulation. We can just bounce off */ + /* the boundary as if it were an elastic band. */ + if (moveleft) { + lprev(*searchtri, backtracktri); + fdest = fapex; + } else { + lnext(*searchtri, backtracktri); + forg = fapex; + } + sym(backtracktri, *searchtri); + + /* Check for walking off the edge. */ + if (searchtri->tri == dummytri) { + /* Turn around. */ + triedgecopy(backtracktri, *searchtri); + swappoint = forg; + forg = fdest; + fdest = swappoint; + apex(*searchtri, fapex); + /* Check if the point really is beyond the triangulation boundary. */ + destorient = counterclockwise(forg, fapex, searchpoint); + orgorient = counterclockwise(fapex, fdest, searchpoint); + if ((orgorient < 0.0) && (destorient < 0.0)) { + return OUTSIDE; + } + } else { + apex(*searchtri, fapex); + } + } +} + +/*****************************************************************************/ +/* */ +/* locate() Find a triangle or edge containing a given point. */ +/* */ +/* Searching begins from one of: the input `searchtri', a recently */ +/* encountered triangle `recenttri', or from a triangle chosen from a */ +/* random sample. The choice is made by determining which triangle's */ +/* origin is closest to the point we are searcing for. Normally, */ +/* `searchtri' should be a handle on the convex hull of the triangulation. */ +/* */ +/* Details on the random sampling method can be found in the Mucke, Saias, */ +/* and Zhu paper cited in the header of this code. */ +/* */ +/* On completion, `searchtri' is a triangle that contains `searchpoint'. */ +/* */ +/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */ +/* is a handle whose origin is the existing vertex. */ +/* */ +/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */ +/* handle whose primary edge is the edge on which the point lies. */ +/* */ +/* Returns INTRIANGLE if the point lies strictly within a triangle. */ +/* `searchtri' is a handle on the triangle that contains the point. */ +/* */ +/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */ +/* handle whose primary edge the point is to the right of. This might */ +/* occur when the circumcenter of a triangle falls just slightly outside */ +/* the mesh due to floating-point roundoff error. It also occurs when */ +/* seeking a hole or region point that a foolish user has placed outside */ +/* the mesh. */ +/* */ +/* WARNING: This routine is designed for convex triangulations, and will */ +/* not generally work after the holes and concavities have been carved. */ +/* */ +/*****************************************************************************/ + +enum locateresult locate(searchpoint, searchtri) +point searchpoint; +struct triedge *searchtri; +{ + VOID **sampleblock; + triangle *firsttri; + struct triedge sampletri; + point torg, tdest; + unsigned long alignptr; + REAL searchdist, dist; + REAL ahead; + long sampleblocks, samplesperblock, samplenum; + long triblocks; + long i, j; + triangle ptr; /* Temporary variable used by sym(). */ + + if (verbose > 2) { + printf(" Randomly sampling for a triangle near point (%.12g, %.12g).\n", + searchpoint[0], searchpoint[1]); + } + /* Record the distance from the suggested starting triangle to the */ + /* point we seek. */ + org(*searchtri, torg); + searchdist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (verbose > 2) { + printf(" Boundary triangle has origin (%.12g, %.12g).\n", + torg[0], torg[1]); + } + + /* If a recently encountered triangle has been recorded and has not been */ + /* deallocated, test it as a good starting point. */ + if (recenttri.tri != (triangle *) NULL) { + if (recenttri.tri[3] != (triangle) NULL) { + org(recenttri, torg); + if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) { + triedgecopy(recenttri, *searchtri); + return ONVERTEX; + } + dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (dist < searchdist) { + triedgecopy(recenttri, *searchtri); + searchdist = dist; + if (verbose > 2) { + printf(" Choosing recent triangle with origin (%.12g, %.12g).\n", + torg[0], torg[1]); + } + } + } + } + + /* The number of random samples taken is proportional to the cube root of */ + /* the number of triangles in the mesh. The next bit of code assumes */ + /* that the number of triangles increases monotonically. */ + while (SAMPLEFACTOR * samples * samples * samples < triangles.items) { + samples++; + } + triblocks = (triangles.maxitems + TRIPERBLOCK - 1) / TRIPERBLOCK; + samplesperblock = 1 + (samples / triblocks); + sampleblocks = samples / samplesperblock; + sampleblock = triangles.firstblock; + sampletri.orient = 0; + for (i = 0; i < sampleblocks; i++) { + alignptr = (unsigned long) (sampleblock + 1); + firsttri = (triangle *) (alignptr + (unsigned long) triangles.alignbytes + - (alignptr % (unsigned long) triangles.alignbytes)); + for (j = 0; j < samplesperblock; j++) { + if (i == triblocks - 1) { + samplenum = randomnation((int) + (triangles.maxitems - (i * TRIPERBLOCK))); + } else { + samplenum = randomnation(TRIPERBLOCK); + } + sampletri.tri = (triangle *) + (firsttri + (samplenum * triangles.itemwords)); + if (sampletri.tri[3] != (triangle) NULL) { + org(sampletri, torg); + dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (dist < searchdist) { + triedgecopy(sampletri, *searchtri); + searchdist = dist; + if (verbose > 2) { + printf(" Choosing triangle with origin (%.12g, %.12g).\n", + torg[0], torg[1]); + } + } + } + } + sampleblock = (VOID **) *sampleblock; + } + /* Where are we? */ + org(*searchtri, torg); + dest(*searchtri, tdest); + /* Check the starting triangle's vertices. */ + if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) { + return ONVERTEX; + } + if ((tdest[0] == searchpoint[0]) && (tdest[1] == searchpoint[1])) { + lnextself(*searchtri); + return ONVERTEX; + } + /* Orient `searchtri' to fit the preconditions of calling preciselocate(). */ + ahead = counterclockwise(torg, tdest, searchpoint); + if (ahead < 0.0) { + /* Turn around so that `searchpoint' is to the left of the */ + /* edge specified by `searchtri'. */ + symself(*searchtri); + } else if (ahead == 0.0) { + /* Check if `searchpoint' is between `torg' and `tdest'. */ + if (((torg[0] < searchpoint[0]) == (searchpoint[0] < tdest[0])) + && ((torg[1] < searchpoint[1]) == (searchpoint[1] < tdest[1]))) { + return ONEDGE; + } + } + return preciselocate(searchpoint, searchtri); +} + +/** **/ +/** **/ +/********* Point location routines end here *********/ + +/********* Mesh transformation routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* insertshelle() Create a new shell edge and insert it between two */ +/* triangles. */ +/* */ +/* The new shell edge is inserted at the edge described by the handle */ +/* `tri'. Its vertices are properly initialized. The marker `shellemark' */ +/* is applied to the shell edge and, if appropriate, its vertices. */ +/* */ +/*****************************************************************************/ + +void insertshelle(tri, shellemark) +struct triedge *tri; /* Edge at which to insert the new shell edge. */ +int shellemark; /* Marker for the new shell edge. */ +{ + struct triedge oppotri; + struct edge newshelle; + point triorg, tridest; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + /* Mark points if possible. */ + org(*tri, triorg); + dest(*tri, tridest); + if (pointmark(triorg) == 0) { + setpointmark(triorg, shellemark); + } + if (pointmark(tridest) == 0) { + setpointmark(tridest, shellemark); + } + /* Check if there's already a shell edge here. */ + tspivot(*tri, newshelle); + if (newshelle.sh == dummysh) { + /* Make new shell edge and initialize its vertices. */ + makeshelle(&newshelle); + setsorg(newshelle, tridest); + setsdest(newshelle, triorg); + /* Bond new shell edge to the two triangles it is sandwiched between. */ + /* Note that the facing triangle `oppotri' might be equal to */ + /* `dummytri' (outer space), but the new shell edge is bonded to it */ + /* all the same. */ + tsbond(*tri, newshelle); + sym(*tri, oppotri); + ssymself(newshelle); + tsbond(oppotri, newshelle); + setmark(newshelle, shellemark); + if (verbose > 2) { + printf(" Inserting new "); + printshelle(&newshelle); + } + } else { + if (mark(newshelle) == 0) { + setmark(newshelle, shellemark); + } + } +} + +/*****************************************************************************/ +/* */ +/* Terminology */ +/* */ +/* A "local transformation" replaces a small set of triangles with another */ +/* set of triangles. This may or may not involve inserting or deleting a */ +/* point. */ +/* */ +/* The term "casing" is used to describe the set of triangles that are */ +/* attached to the triangles being transformed, but are not transformed */ +/* themselves. Think of the casing as a fixed hollow structure inside */ +/* which all the action happens. A "casing" is only defined relative to */ +/* a single transformation; each occurrence of a transformation will */ +/* involve a different casing. */ +/* */ +/* A "shell" is similar to a "casing". The term "shell" describes the set */ +/* of shell edges (if any) that are attached to the triangles being */ +/* transformed. However, I sometimes use "shell" to refer to a single */ +/* shell edge, so don't get confused. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* flip() Transform two triangles to two different triangles by flipping */ +/* an edge within a quadrilateral. */ +/* */ +/* Imagine the original triangles, abc and bad, oriented so that the */ +/* shared edge ab lies in a horizontal plane, with the point b on the left */ +/* and the point a on the right. The point c lies below the edge, and the */ +/* point d lies above the edge. The `flipedge' handle holds the edge ab */ +/* of triangle abc, and is directed left, from vertex a to vertex b. */ +/* */ +/* The triangles abc and bad are deleted and replaced by the triangles cdb */ +/* and dca. The triangles that represent abc and bad are NOT deallocated; */ +/* they are reused for dca and cdb, respectively. Hence, any handles that */ +/* may have held the original triangles are still valid, although not */ +/* directed as they were before. */ +/* */ +/* Upon completion of this routine, the `flipedge' handle holds the edge */ +/* dc of triangle dca, and is directed down, from vertex d to vertex c. */ +/* (Hence, the two triangles have rotated counterclockwise.) */ +/* */ +/* WARNING: This transformation is geometrically valid only if the */ +/* quadrilateral adbc is convex. Furthermore, this transformation is */ +/* valid only if there is not a shell edge between the triangles abc and */ +/* bad. This routine does not check either of these preconditions, and */ +/* it is the responsibility of the calling routine to ensure that they are */ +/* met. If they are not, the streets shall be filled with wailing and */ +/* gnashing of teeth. */ +/* */ +/*****************************************************************************/ + +void flip(flipedge) +struct triedge *flipedge; /* Handle for the triangle abc. */ +{ + struct triedge botleft, botright; + struct triedge topleft, topright; + struct triedge top; + struct triedge botlcasing, botrcasing; + struct triedge toplcasing, toprcasing; + struct edge botlshelle, botrshelle; + struct edge toplshelle, toprshelle; + point leftpoint, rightpoint, botpoint; + point farpoint; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + /* Identify the vertices of the quadrilateral. */ + org(*flipedge, rightpoint); + dest(*flipedge, leftpoint); + apex(*flipedge, botpoint); + sym(*flipedge, top); +#ifdef SELF_CHECK + if (top.tri == dummytri) { + printf("Internal error in flip(): Attempt to flip on boundary.\n"); + lnextself(*flipedge); + return; + } + if (checksegments) { + tspivot(*flipedge, toplshelle); + if (toplshelle.sh != dummysh) { + printf("Internal error in flip(): Attempt to flip a segment.\n"); + lnextself(*flipedge); + return; + } + } +#endif /* SELF_CHECK */ + apex(top, farpoint); + + /* Identify the casing of the quadrilateral. */ + lprev(top, topleft); + sym(topleft, toplcasing); + lnext(top, topright); + sym(topright, toprcasing); + lnext(*flipedge, botleft); + sym(botleft, botlcasing); + lprev(*flipedge, botright); + sym(botright, botrcasing); + /* Rotate the quadrilateral one-quarter turn counterclockwise. */ + bond(topleft, botlcasing); + bond(botleft, botrcasing); + bond(botright, toprcasing); + bond(topright, toplcasing); + + if (checksegments) { + /* Check for shell edges and rebond them to the quadrilateral. */ + tspivot(topleft, toplshelle); + tspivot(botleft, botlshelle); + tspivot(botright, botrshelle); + tspivot(topright, toprshelle); + if (toplshelle.sh == dummysh) { + tsdissolve(topright); + } else { + tsbond(topright, toplshelle); + } + if (botlshelle.sh == dummysh) { + tsdissolve(topleft); + } else { + tsbond(topleft, botlshelle); + } + if (botrshelle.sh == dummysh) { + tsdissolve(botleft); + } else { + tsbond(botleft, botrshelle); + } + if (toprshelle.sh == dummysh) { + tsdissolve(botright); + } else { + tsbond(botright, toprshelle); + } + } + + /* New point assignments for the rotated quadrilateral. */ + setorg(*flipedge, farpoint); + setdest(*flipedge, botpoint); + setapex(*flipedge, rightpoint); + setorg(top, botpoint); + setdest(top, farpoint); + setapex(top, leftpoint); + if (verbose > 2) { + printf(" Edge flip results in left "); + lnextself(topleft); + printtriangle(&topleft); + printf(" and right "); + printtriangle(flipedge); + } +} + +/*****************************************************************************/ +/* */ +/* insertsite() Insert a vertex into a Delaunay triangulation, */ +/* performing flips as necessary to maintain the Delaunay */ +/* property. */ +/* */ +/* The point `insertpoint' is located. If `searchtri.tri' is not NULL, */ +/* the search for the containing triangle begins from `searchtri'. If */ +/* `searchtri.tri' is NULL, a full point location procedure is called. */ +/* If `insertpoint' is found inside a triangle, the triangle is split into */ +/* three; if `insertpoint' lies on an edge, the edge is split in two, */ +/* thereby splitting the two adjacent triangles into four. Edge flips are */ +/* used to restore the Delaunay property. If `insertpoint' lies on an */ +/* existing vertex, no action is taken, and the value DUPLICATEPOINT is */ +/* returned. On return, `searchtri' is set to a handle whose origin is the */ +/* existing vertex. */ +/* */ +/* Normally, the parameter `splitedge' is set to NULL, implying that no */ +/* segment should be split. In this case, if `insertpoint' is found to */ +/* lie on a segment, no action is taken, and the value VIOLATINGPOINT is */ +/* returned. On return, `searchtri' is set to a handle whose primary edge */ +/* is the violated segment. */ +/* */ +/* If the calling routine wishes to split a segment by inserting a point in */ +/* it, the parameter `splitedge' should be that segment. In this case, */ +/* `searchtri' MUST be the triangle handle reached by pivoting from that */ +/* segment; no point location is done. */ +/* */ +/* `segmentflaws' and `triflaws' are flags that indicate whether or not */ +/* there should be checks for the creation of encroached segments or bad */ +/* quality faces. If a newly inserted point encroaches upon segments, */ +/* these segments are added to the list of segments to be split if */ +/* `segmentflaws' is set. If bad triangles are created, these are added */ +/* to the queue if `triflaws' is set. */ +/* */ +/* If a duplicate point or violated segment does not prevent the point */ +/* from being inserted, the return value will be ENCROACHINGPOINT if the */ +/* point encroaches upon a segment (and checking is enabled), or */ +/* SUCCESSFULPOINT otherwise. In either case, `searchtri' is set to a */ +/* handle whose origin is the newly inserted vertex. */ +/* */ +/* insertsite() does not use flip() for reasons of speed; some */ +/* information can be reused from edge flip to edge flip, like the */ +/* locations of shell edges. */ +/* */ +/*****************************************************************************/ + +enum insertsiteresult insertsite(insertpoint, searchtri, splitedge, + segmentflaws, triflaws) +point insertpoint; +struct triedge *searchtri; +struct edge *splitedge; +int segmentflaws; +int triflaws; +{ + struct triedge horiz; + struct triedge top; + struct triedge botleft, botright; + struct triedge topleft, topright; + struct triedge newbotleft, newbotright; + struct triedge newtopright; + struct triedge botlcasing, botrcasing; + struct triedge toplcasing, toprcasing; + struct triedge testtri; + struct edge botlshelle, botrshelle; + struct edge toplshelle, toprshelle; + struct edge brokenshelle; + struct edge checkshelle; + struct edge rightedge; + struct edge newedge; + struct edge *encroached; + point first; + point leftpoint, rightpoint, botpoint, toppoint, farpoint; + REAL attrib; + REAL area; + enum insertsiteresult success; + enum locateresult intersect; + int doflip; + int mirrorflag; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by spivot() and tspivot(). */ + + if (verbose > 1) { + printf(" Inserting (%.12g, %.12g).\n", insertpoint[0], insertpoint[1]); + } + if (splitedge == (struct edge *) NULL) { + /* Find the location of the point to be inserted. Check if a good */ + /* starting triangle has already been provided by the caller. */ + if (searchtri->tri == (triangle *) NULL) { + /* Find a boundary triangle. */ + horiz.tri = dummytri; + horiz.orient = 0; + symself(horiz); + /* Search for a triangle containing `insertpoint'. */ + intersect = locate(insertpoint, &horiz); + } else { + /* Start searching from the triangle provided by the caller. */ + triedgecopy(*searchtri, horiz); + intersect = preciselocate(insertpoint, &horiz); + } + } else { + /* The calling routine provides the edge in which the point is inserted. */ + triedgecopy(*searchtri, horiz); + intersect = ONEDGE; + } + if (intersect == ONVERTEX) { + /* There's already a vertex there. Return in `searchtri' a triangle */ + /* whose origin is the existing vertex. */ + triedgecopy(horiz, *searchtri); + triedgecopy(horiz, recenttri); + return DUPLICATEPOINT; + } + if ((intersect == ONEDGE) || (intersect == OUTSIDE)) { + /* The vertex falls on an edge or boundary. */ + if (checksegments && (splitedge == (struct edge *) NULL)) { + /* Check whether the vertex falls on a shell edge. */ + tspivot(horiz, brokenshelle); + if (brokenshelle.sh != dummysh) { + /* The vertex falls on a shell edge. */ + if (segmentflaws) { + if (nobisect == 0) { + /* Add the shell edge to the list of encroached segments. */ + encroached = (struct edge *) poolalloc(&badsegments); + shellecopy(brokenshelle, *encroached); + } else if ((nobisect == 1) && (intersect == ONEDGE)) { + /* This segment may be split only if it is an internal boundary. */ + sym(horiz, testtri); + if (testtri.tri != dummytri) { + /* Add the shell edge to the list of encroached segments. */ + encroached = (struct edge *) poolalloc(&badsegments); + shellecopy(brokenshelle, *encroached); + } + } + } + /* Return a handle whose primary edge contains the point, */ + /* which has not been inserted. */ + triedgecopy(horiz, *searchtri); + triedgecopy(horiz, recenttri); + return VIOLATINGPOINT; + } + } + /* Insert the point on an edge, dividing one triangle into two (if */ + /* the edge lies on a boundary) or two triangles into four. */ + lprev(horiz, botright); + sym(botright, botrcasing); + sym(horiz, topright); + /* Is there a second triangle? (Or does this edge lie on a boundary?) */ + mirrorflag = topright.tri != dummytri; + if (mirrorflag) { + lnextself(topright); + sym(topright, toprcasing); + maketriangle(&newtopright); + } else { + /* Splitting the boundary edge increases the number of boundary edges. */ + hullsize++; + } + maketriangle(&newbotright); + + /* Set the vertices of changed and new triangles. */ + org(horiz, rightpoint); + dest(horiz, leftpoint); + apex(horiz, botpoint); + setorg(newbotright, botpoint); + setdest(newbotright, rightpoint); + setapex(newbotright, insertpoint); + setorg(horiz, insertpoint); + for (i = 0; i < eextras; i++) { + /* Set the element attributes of a new triangle. */ + setelemattribute(newbotright, i, elemattribute(botright, i)); + } + if (vararea) { + /* Set the area constraint of a new triangle. */ + setareabound(newbotright, areabound(botright)); + } + if (mirrorflag) { + dest(topright, toppoint); + setorg(newtopright, rightpoint); + setdest(newtopright, toppoint); + setapex(newtopright, insertpoint); + setorg(topright, insertpoint); + for (i = 0; i < eextras; i++) { + /* Set the element attributes of another new triangle. */ + setelemattribute(newtopright, i, elemattribute(topright, i)); + } + if (vararea) { + /* Set the area constraint of another new triangle. */ + setareabound(newtopright, areabound(topright)); + } + } + + /* There may be shell edges that need to be bonded */ + /* to the new triangle(s). */ + if (checksegments) { + tspivot(botright, botrshelle); + if (botrshelle.sh != dummysh) { + tsdissolve(botright); + tsbond(newbotright, botrshelle); + } + if (mirrorflag) { + tspivot(topright, toprshelle); + if (toprshelle.sh != dummysh) { + tsdissolve(topright); + tsbond(newtopright, toprshelle); + } + } + } + + /* Bond the new triangle(s) to the surrounding triangles. */ + bond(newbotright, botrcasing); + lprevself(newbotright); + bond(newbotright, botright); + lprevself(newbotright); + if (mirrorflag) { + bond(newtopright, toprcasing); + lnextself(newtopright); + bond(newtopright, topright); + lnextself(newtopright); + bond(newtopright, newbotright); + } + + if (splitedge != (struct edge *) NULL) { + /* Split the shell edge into two. */ + setsdest(*splitedge, insertpoint); + ssymself(*splitedge); + spivot(*splitedge, rightedge); + insertshelle(&newbotright, mark(*splitedge)); + tspivot(newbotright, newedge); + sbond(*splitedge, newedge); + ssymself(newedge); + sbond(newedge, rightedge); + ssymself(*splitedge); + } + +#ifdef SELF_CHECK + if (counterclockwise(rightpoint, leftpoint, botpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle prior to edge point insertion (bottom).\n"); + } + if (mirrorflag) { + if (counterclockwise(leftpoint, rightpoint, toppoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle prior to edge point insertion (top).\n"); + } + if (counterclockwise(rightpoint, toppoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after edge point insertion (top right).\n" + ); + } + if (counterclockwise(toppoint, leftpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after edge point insertion (top left).\n" + ); + } + } + if (counterclockwise(leftpoint, botpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after edge point insertion (bottom left).\n" + ); + } + if (counterclockwise(botpoint, rightpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf( + " Clockwise triangle after edge point insertion (bottom right).\n"); + } +#endif /* SELF_CHECK */ + if (verbose > 2) { + printf(" Updating bottom left "); + printtriangle(&botright); + if (mirrorflag) { + printf(" Updating top left "); + printtriangle(&topright); + printf(" Creating top right "); + printtriangle(&newtopright); + } + printf(" Creating bottom right "); + printtriangle(&newbotright); + } + + /* Position `horiz' on the first edge to check for */ + /* the Delaunay property. */ + lnextself(horiz); + } else { + /* Insert the point in a triangle, splitting it into three. */ + lnext(horiz, botleft); + lprev(horiz, botright); + sym(botleft, botlcasing); + sym(botright, botrcasing); + maketriangle(&newbotleft); + maketriangle(&newbotright); + + /* Set the vertices of changed and new triangles. */ + org(horiz, rightpoint); + dest(horiz, leftpoint); + apex(horiz, botpoint); + setorg(newbotleft, leftpoint); + setdest(newbotleft, botpoint); + setapex(newbotleft, insertpoint); + setorg(newbotright, botpoint); + setdest(newbotright, rightpoint); + setapex(newbotright, insertpoint); + setapex(horiz, insertpoint); + for (i = 0; i < eextras; i++) { + /* Set the element attributes of the new triangles. */ + attrib = elemattribute(horiz, i); + setelemattribute(newbotleft, i, attrib); + setelemattribute(newbotright, i, attrib); + } + if (vararea) { + /* Set the area constraint of the new triangles. */ + area = areabound(horiz); + setareabound(newbotleft, area); + setareabound(newbotright, area); + } + + /* There may be shell edges that need to be bonded */ + /* to the new triangles. */ + if (checksegments) { + tspivot(botleft, botlshelle); + if (botlshelle.sh != dummysh) { + tsdissolve(botleft); + tsbond(newbotleft, botlshelle); + } + tspivot(botright, botrshelle); + if (botrshelle.sh != dummysh) { + tsdissolve(botright); + tsbond(newbotright, botrshelle); + } + } + + /* Bond the new triangles to the surrounding triangles. */ + bond(newbotleft, botlcasing); + bond(newbotright, botrcasing); + lnextself(newbotleft); + lprevself(newbotright); + bond(newbotleft, newbotright); + lnextself(newbotleft); + bond(botleft, newbotleft); + lprevself(newbotright); + bond(botright, newbotright); + +#ifdef SELF_CHECK + if (counterclockwise(rightpoint, leftpoint, botpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle prior to point insertion.\n"); + } + if (counterclockwise(rightpoint, leftpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after point insertion (top).\n"); + } + if (counterclockwise(leftpoint, botpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after point insertion (left).\n"); + } + if (counterclockwise(botpoint, rightpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after point insertion (right).\n"); + } +#endif /* SELF_CHECK */ + if (verbose > 2) { + printf(" Updating top "); + printtriangle(&horiz); + printf(" Creating left "); + printtriangle(&newbotleft); + printf(" Creating right "); + printtriangle(&newbotright); + } + } + + /* The insertion is successful by default, unless an encroached */ + /* edge is found. */ + success = SUCCESSFULPOINT; + /* Circle around the newly inserted vertex, checking each edge opposite */ + /* it for the Delaunay property. Non-Delaunay edges are flipped. */ + /* `horiz' is always the edge being checked. `first' marks where to */ + /* stop circling. */ + org(horiz, first); + rightpoint = first; + dest(horiz, leftpoint); + /* Circle until finished. */ + while (1) { + /* By default, the edge will be flipped. */ + doflip = 1; + if (checksegments) { + /* Check for a segment, which cannot be flipped. */ + tspivot(horiz, checkshelle); + if (checkshelle.sh != dummysh) { + /* The edge is a segment and cannot be flipped. */ + doflip = 0; +#ifndef CDT_ONLY + if (segmentflaws) { + /* Does the new point encroach upon this segment? */ + if (checkedge4encroach(&checkshelle)) { + success = ENCROACHINGPOINT; + } + } +#endif /* not CDT_ONLY */ + } + } + if (doflip) { + /* Check if the edge is a boundary edge. */ + sym(horiz, top); + if (top.tri == dummytri) { + /* The edge is a boundary edge and cannot be flipped. */ + doflip = 0; + } else { + /* Find the point on the other side of the edge. */ + apex(top, farpoint); + /* In the incremental Delaunay triangulation algorithm, any of */ + /* `leftpoint', `rightpoint', and `farpoint' could be vertices */ + /* of the triangular bounding box. These vertices must be */ + /* treated as if they are infinitely distant, even though their */ + /* "coordinates" are not. */ + if ((leftpoint == infpoint1) || (leftpoint == infpoint2) + || (leftpoint == infpoint3)) { + /* `leftpoint' is infinitely distant. Check the convexity of */ + /* the boundary of the triangulation. 'farpoint' might be */ + /* infinite as well, but trust me, this same condition */ + /* should be applied. */ + doflip = counterclockwise(insertpoint, rightpoint, farpoint) > 0.0; + } else if ((rightpoint == infpoint1) || (rightpoint == infpoint2) + || (rightpoint == infpoint3)) { + /* `rightpoint' is infinitely distant. Check the convexity of */ + /* the boundary of the triangulation. 'farpoint' might be */ + /* infinite as well, but trust me, this same condition */ + /* should be applied. */ + doflip = counterclockwise(farpoint, leftpoint, insertpoint) > 0.0; + } else if ((farpoint == infpoint1) || (farpoint == infpoint2) + || (farpoint == infpoint3)) { + /* `farpoint' is infinitely distant and cannot be inside */ + /* the circumcircle of the triangle `horiz'. */ + doflip = 0; + } else { + /* Test whether the edge is locally Delaunay. */ + doflip = incircle(leftpoint, insertpoint, rightpoint, farpoint) + > 0.0; + } + if (doflip) { + /* We made it! Flip the edge `horiz' by rotating its containing */ + /* quadrilateral (the two triangles adjacent to `horiz'). */ + /* Identify the casing of the quadrilateral. */ + lprev(top, topleft); + sym(topleft, toplcasing); + lnext(top, topright); + sym(topright, toprcasing); + lnext(horiz, botleft); + sym(botleft, botlcasing); + lprev(horiz, botright); + sym(botright, botrcasing); + /* Rotate the quadrilateral one-quarter turn counterclockwise. */ + bond(topleft, botlcasing); + bond(botleft, botrcasing); + bond(botright, toprcasing); + bond(topright, toplcasing); + if (checksegments) { + /* Check for shell edges and rebond them to the quadrilateral. */ + tspivot(topleft, toplshelle); + tspivot(botleft, botlshelle); + tspivot(botright, botrshelle); + tspivot(topright, toprshelle); + if (toplshelle.sh == dummysh) { + tsdissolve(topright); + } else { + tsbond(topright, toplshelle); + } + if (botlshelle.sh == dummysh) { + tsdissolve(topleft); + } else { + tsbond(topleft, botlshelle); + } + if (botrshelle.sh == dummysh) { + tsdissolve(botleft); + } else { + tsbond(botleft, botrshelle); + } + if (toprshelle.sh == dummysh) { + tsdissolve(botright); + } else { + tsbond(botright, toprshelle); + } + } + /* New point assignments for the rotated quadrilateral. */ + setorg(horiz, farpoint); + setdest(horiz, insertpoint); + setapex(horiz, rightpoint); + setorg(top, insertpoint); + setdest(top, farpoint); + setapex(top, leftpoint); + for (i = 0; i < eextras; i++) { + /* Take the average of the two triangles' attributes. */ + attrib = 0.5 * (elemattribute(top, i) + elemattribute(horiz, i)); + setelemattribute(top, i, attrib); + setelemattribute(horiz, i, attrib); + } + if (vararea) { + if ((areabound(top) <= 0.0) || (areabound(horiz) <= 0.0)) { + area = -1.0; + } else { + /* Take the average of the two triangles' area constraints. */ + /* This prevents small area constraints from migrating a */ + /* long, long way from their original location due to flips. */ + area = 0.5 * (areabound(top) + areabound(horiz)); + } + setareabound(top, area); + setareabound(horiz, area); + } +#ifdef SELF_CHECK + if (insertpoint != (point) NULL) { + if (counterclockwise(leftpoint, insertpoint, rightpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle prior to edge flip (bottom).\n"); + } + /* The following test has been removed because constrainededge() */ + /* sometimes generates inverted triangles that insertsite() */ + /* removes. */ +/* + if (counterclockwise(rightpoint, farpoint, leftpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle prior to edge flip (top).\n"); + } +*/ + if (counterclockwise(farpoint, leftpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after edge flip (left).\n"); + } + if (counterclockwise(insertpoint, rightpoint, farpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after edge flip (right).\n"); + } + } +#endif /* SELF_CHECK */ + if (verbose > 2) { + printf(" Edge flip results in left "); + lnextself(topleft); + printtriangle(&topleft); + printf(" and right "); + printtriangle(&horiz); + } + /* On the next iterations, consider the two edges that were */ + /* exposed (this is, are now visible to the newly inserted */ + /* point) by the edge flip. */ + lprevself(horiz); + leftpoint = farpoint; + } + } + } + if (!doflip) { + /* The handle `horiz' is accepted as locally Delaunay. */ +#ifndef CDT_ONLY + if (triflaws) { + /* Check the triangle `horiz' for quality. */ + testtriangle(&horiz); + } +#endif /* not CDT_ONLY */ + /* Look for the next edge around the newly inserted point. */ + lnextself(horiz); + sym(horiz, testtri); + /* Check for finishing a complete revolution about the new point, or */ + /* falling off the edge of the triangulation. The latter will */ + /* happen when a point is inserted at a boundary. */ + if ((leftpoint == first) || (testtri.tri == dummytri)) { + /* We're done. Return a triangle whose origin is the new point. */ + lnext(horiz, *searchtri); + lnext(horiz, recenttri); + return success; + } + /* Finish finding the next edge around the newly inserted point. */ + lnext(testtri, horiz); + rightpoint = leftpoint; + dest(horiz, leftpoint); + } + } +} + +/*****************************************************************************/ +/* */ +/* triangulatepolygon() Find the Delaunay triangulation of a polygon that */ +/* has a certain "nice" shape. This includes the */ +/* polygons that result from deletion of a point or */ +/* insertion of a segment. */ +/* */ +/* This is a conceptually difficult routine. The starting assumption is */ +/* that we have a polygon with n sides. n - 1 of these sides are currently */ +/* represented as edges in the mesh. One side, called the "base", need not */ +/* be. */ +/* */ +/* Inside the polygon is a structure I call a "fan", consisting of n - 1 */ +/* triangles that share a common origin. For each of these triangles, the */ +/* edge opposite the origin is one of the sides of the polygon. The */ +/* primary edge of each triangle is the edge directed from the origin to */ +/* the destination; note that this is not the same edge that is a side of */ +/* the polygon. `firstedge' is the primary edge of the first triangle. */ +/* From there, the triangles follow in counterclockwise order about the */ +/* polygon, until `lastedge', the primary edge of the last triangle. */ +/* `firstedge' and `lastedge' are probably connected to other triangles */ +/* beyond the extremes of the fan, but their identity is not important, as */ +/* long as the fan remains connected to them. */ +/* */ +/* Imagine the polygon oriented so that its base is at the bottom. This */ +/* puts `firstedge' on the far right, and `lastedge' on the far left. */ +/* The right vertex of the base is the destination of `firstedge', and the */ +/* left vertex of the base is the apex of `lastedge'. */ +/* */ +/* The challenge now is to find the right sequence of edge flips to */ +/* transform the fan into a Delaunay triangulation of the polygon. Each */ +/* edge flip effectively removes one triangle from the fan, committing it */ +/* to the polygon. The resulting polygon has one fewer edge. If `doflip' */ +/* is set, the final flip will be performed, resulting in a fan of one */ +/* (useless?) triangle. If `doflip' is not set, the final flip is not */ +/* performed, resulting in a fan of two triangles, and an unfinished */ +/* triangular polygon that is not yet filled out with a single triangle. */ +/* On completion of the routine, `lastedge' is the last remaining triangle, */ +/* or the leftmost of the last two. */ +/* */ +/* Although the flips are performed in the order described above, the */ +/* decisions about what flips to perform are made in precisely the reverse */ +/* order. The recursive triangulatepolygon() procedure makes a decision, */ +/* uses up to two recursive calls to triangulate the "subproblems" */ +/* (polygons with fewer edges), and then performs an edge flip. */ +/* */ +/* The "decision" it makes is which vertex of the polygon should be */ +/* connected to the base. This decision is made by testing every possible */ +/* vertex. Once the best vertex is found, the two edges that connect this */ +/* vertex to the base become the bases for two smaller polygons. These */ +/* are triangulated recursively. Unfortunately, this approach can take */ +/* O(n^2) time not only in the worst case, but in many common cases. It's */ +/* rarely a big deal for point deletion, where n is rarely larger than ten, */ +/* but it could be a big deal for segment insertion, especially if there's */ +/* a lot of long segments that each cut many triangles. I ought to code */ +/* a faster algorithm some time. */ +/* */ +/* The `edgecount' parameter is the number of sides of the polygon, */ +/* including its base. `triflaws' is a flag that determines whether the */ +/* new triangles should be tested for quality, and enqueued if they are */ +/* bad. */ +/* */ +/*****************************************************************************/ + +void triangulatepolygon(firstedge, lastedge, edgecount, doflip, triflaws) +struct triedge *firstedge; +struct triedge *lastedge; +int edgecount; +int doflip; +int triflaws; +{ + struct triedge testtri; + struct triedge besttri; + struct triedge tempedge; + point leftbasepoint, rightbasepoint; + point testpoint; + point bestpoint; + int bestnumber; + int i; + triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */ + + /* Identify the base vertices. */ + apex(*lastedge, leftbasepoint); + dest(*firstedge, rightbasepoint); + if (verbose > 2) { + printf(" Triangulating interior polygon at edge\n"); + printf(" (%.12g, %.12g) (%.12g, %.12g)\n", leftbasepoint[0], + leftbasepoint[1], rightbasepoint[0], rightbasepoint[1]); + } + /* Find the best vertex to connect the base to. */ + onext(*firstedge, besttri); + dest(besttri, bestpoint); + triedgecopy(besttri, testtri); + bestnumber = 1; + for (i = 2; i <= edgecount - 2; i++) { + onextself(testtri); + dest(testtri, testpoint); + /* Is this a better vertex? */ + if (incircle(leftbasepoint, rightbasepoint, bestpoint, testpoint) > 0.0) { + triedgecopy(testtri, besttri); + bestpoint = testpoint; + bestnumber = i; + } + } + if (verbose > 2) { + printf(" Connecting edge to (%.12g, %.12g)\n", bestpoint[0], + bestpoint[1]); + } + if (bestnumber > 1) { + /* Recursively triangulate the smaller polygon on the right. */ + oprev(besttri, tempedge); + triangulatepolygon(firstedge, &tempedge, bestnumber + 1, 1, triflaws); + } + if (bestnumber < edgecount - 2) { + /* Recursively triangulate the smaller polygon on the left. */ + sym(besttri, tempedge); + triangulatepolygon(&besttri, lastedge, edgecount - bestnumber, 1, + triflaws); + /* Find `besttri' again; it may have been lost to edge flips. */ + sym(tempedge, besttri); + } + if (doflip) { + /* Do one final edge flip. */ + flip(&besttri); +#ifndef CDT_ONLY + if (triflaws) { + /* Check the quality of the newly committed triangle. */ + sym(besttri, testtri); + testtriangle(&testtri); + } +#endif /* not CDT_ONLY */ + } + /* Return the base triangle. */ + triedgecopy(besttri, *lastedge); +} + +/*****************************************************************************/ +/* */ +/* deletesite() Delete a vertex from a Delaunay triangulation, ensuring */ +/* that the triangulation remains Delaunay. */ +/* */ +/* The origin of `deltri' is deleted. The union of the triangles adjacent */ +/* to this point is a polygon, for which the Delaunay triangulation is */ +/* found. Two triangles are removed from the mesh. */ +/* */ +/* Only interior points that do not lie on segments (shell edges) or */ +/* boundaries may be deleted. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +void deletesite(deltri) +struct triedge *deltri; +{ + struct triedge countingtri; + struct triedge firstedge, lastedge; + struct triedge deltriright; + struct triedge lefttri, righttri; + struct triedge leftcasing, rightcasing; + struct edge leftshelle, rightshelle; + point delpoint; + point neworg; + int edgecount; + triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + org(*deltri, delpoint); + if (verbose > 1) { + printf(" Deleting (%.12g, %.12g).\n", delpoint[0], delpoint[1]); + } + pointdealloc(delpoint); + + /* Count the degree of the point being deleted. */ + onext(*deltri, countingtri); + edgecount = 1; + while (!triedgeequal(*deltri, countingtri)) { +#ifdef SELF_CHECK + if (countingtri.tri == dummytri) { + printf("Internal error in deletesite():\n"); + printf(" Attempt to delete boundary point.\n"); + internalerror(); + } +#endif /* SELF_CHECK */ + edgecount++; + onextself(countingtri); + } + +#ifdef SELF_CHECK + if (edgecount < 3) { + printf("Internal error in deletesite():\n Point has degree %d.\n", + edgecount); + internalerror(); + } +#endif /* SELF_CHECK */ + if (edgecount > 3) { + /* Triangulate the polygon defined by the union of all triangles */ + /* adjacent to the point being deleted. Check the quality of */ + /* the resulting triangles. */ + onext(*deltri, firstedge); + oprev(*deltri, lastedge); + triangulatepolygon(&firstedge, &lastedge, edgecount, 0, !nobisect); + } + /* Splice out two triangles. */ + lprev(*deltri, deltriright); + dnext(*deltri, lefttri); + sym(lefttri, leftcasing); + oprev(deltriright, righttri); + sym(righttri, rightcasing); + bond(*deltri, leftcasing); + bond(deltriright, rightcasing); + tspivot(lefttri, leftshelle); + if (leftshelle.sh != dummysh) { + tsbond(*deltri, leftshelle); + } + tspivot(righttri, rightshelle); + if (rightshelle.sh != dummysh) { + tsbond(deltriright, rightshelle); + } + + /* Set the new origin of `deltri' and check its quality. */ + org(lefttri, neworg); + setorg(*deltri, neworg); + if (!nobisect) { + testtriangle(deltri); + } + + /* Delete the two spliced-out triangles. */ + triangledealloc(lefttri.tri); + triangledealloc(righttri.tri); +} + +#endif /* not CDT_ONLY */ + +/** **/ +/** **/ +/********* Mesh transformation routines end here *********/ + +/********* Divide-and-conquer Delaunay triangulation begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* The divide-and-conquer bounding box */ +/* */ +/* I originally implemented the divide-and-conquer and incremental Delaunay */ +/* triangulations using the edge-based data structure presented by Guibas */ +/* and Stolfi. Switching to a triangle-based data structure doubled the */ +/* speed. However, I had to think of a few extra tricks to maintain the */ +/* elegance of the original algorithms. */ +/* */ +/* The "bounding box" used by my variant of the divide-and-conquer */ +/* algorithm uses one triangle for each edge of the convex hull of the */ +/* triangulation. These bounding triangles all share a common apical */ +/* vertex, which is represented by NULL and which represents nothing. */ +/* The bounding triangles are linked in a circular fan about this NULL */ +/* vertex, and the edges on the convex hull of the triangulation appear */ +/* opposite the NULL vertex. You might find it easiest to imagine that */ +/* the NULL vertex is a point in 3D space behind the center of the */ +/* triangulation, and that the bounding triangles form a sort of cone. */ +/* */ +/* This bounding box makes it easy to represent degenerate cases. For */ +/* instance, the triangulation of two vertices is a single edge. This edge */ +/* is represented by two bounding box triangles, one on each "side" of the */ +/* edge. These triangles are also linked together in a fan about the NULL */ +/* vertex. */ +/* */ +/* The bounding box also makes it easy to traverse the convex hull, as the */ +/* divide-and-conquer algorithm needs to do. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* pointsort() Sort an array of points by x-coordinate, using the */ +/* y-coordinate as a secondary key. */ +/* */ +/* Uses quicksort. Randomized O(n log n) time. No, I did not make any of */ +/* the usual quicksort mistakes. */ +/* */ +/*****************************************************************************/ + +void pointsort(sortarray, arraysize) +point *sortarray; +int arraysize; +{ + int left, right; + int pivot; + REAL pivotx, pivoty; + point temp; + + if (arraysize == 2) { + /* Recursive base case. */ + if ((sortarray[0][0] > sortarray[1][0]) || + ((sortarray[0][0] == sortarray[1][0]) && + (sortarray[0][1] > sortarray[1][1]))) { + temp = sortarray[1]; + sortarray[1] = sortarray[0]; + sortarray[0] = temp; + } + return; + } + /* Choose a random pivot to split the array. */ + pivot = (int) randomnation(arraysize); + pivotx = sortarray[pivot][0]; + pivoty = sortarray[pivot][1]; + /* Split the array. */ + left = -1; + right = arraysize; + while (left < right) { + /* Search for a point whose x-coordinate is too large for the left. */ + do { + left++; + } while ((left <= right) && ((sortarray[left][0] < pivotx) || + ((sortarray[left][0] == pivotx) && + (sortarray[left][1] < pivoty)))); + /* Search for a point whose x-coordinate is too small for the right. */ + do { + right--; + } while ((left <= right) && ((sortarray[right][0] > pivotx) || + ((sortarray[right][0] == pivotx) && + (sortarray[right][1] > pivoty)))); + if (left < right) { + /* Swap the left and right points. */ + temp = sortarray[left]; + sortarray[left] = sortarray[right]; + sortarray[right] = temp; + } + } + if (left > 1) { + /* Recursively sort the left subset. */ + pointsort(sortarray, left); + } + if (right < arraysize - 2) { + /* Recursively sort the right subset. */ + pointsort(&sortarray[right + 1], arraysize - right - 1); + } +} + +/*****************************************************************************/ +/* */ +/* pointmedian() An order statistic algorithm, almost. Shuffles an array */ +/* of points so that the first `median' points occur */ +/* lexicographically before the remaining points. */ +/* */ +/* Uses the x-coordinate as the primary key if axis == 0; the y-coordinate */ +/* if axis == 1. Very similar to the pointsort() procedure, but runs in */ +/* randomized linear time. */ +/* */ +/*****************************************************************************/ + +void pointmedian(sortarray, arraysize, median, axis) +point *sortarray; +int arraysize; +int median; +int axis; +{ + int left, right; + int pivot; + REAL pivot1, pivot2; + point temp; + + if (arraysize == 2) { + /* Recursive base case. */ + if ((sortarray[0][axis] > sortarray[1][axis]) || + ((sortarray[0][axis] == sortarray[1][axis]) && + (sortarray[0][1 - axis] > sortarray[1][1 - axis]))) { + temp = sortarray[1]; + sortarray[1] = sortarray[0]; + sortarray[0] = temp; + } + return; + } + /* Choose a random pivot to split the array. */ + pivot = (int) randomnation(arraysize); + pivot1 = sortarray[pivot][axis]; + pivot2 = sortarray[pivot][1 - axis]; + /* Split the array. */ + left = -1; + right = arraysize; + while (left < right) { + /* Search for a point whose x-coordinate is too large for the left. */ + do { + left++; + } while ((left <= right) && ((sortarray[left][axis] < pivot1) || + ((sortarray[left][axis] == pivot1) && + (sortarray[left][1 - axis] < pivot2)))); + /* Search for a point whose x-coordinate is too small for the right. */ + do { + right--; + } while ((left <= right) && ((sortarray[right][axis] > pivot1) || + ((sortarray[right][axis] == pivot1) && + (sortarray[right][1 - axis] > pivot2)))); + if (left < right) { + /* Swap the left and right points. */ + temp = sortarray[left]; + sortarray[left] = sortarray[right]; + sortarray[right] = temp; + } + } + /* Unlike in pointsort(), at most one of the following */ + /* conditionals is true. */ + if (left > median) { + /* Recursively shuffle the left subset. */ + pointmedian(sortarray, left, median, axis); + } + if (right < median - 1) { + /* Recursively shuffle the right subset. */ + pointmedian(&sortarray[right + 1], arraysize - right - 1, + median - right - 1, axis); + } +} + +/*****************************************************************************/ +/* */ +/* alternateaxes() Sorts the points as appropriate for the divide-and- */ +/* conquer algorithm with alternating cuts. */ +/* */ +/* Partitions by x-coordinate if axis == 0; by y-coordinate if axis == 1. */ +/* For the base case, subsets containing only two or three points are */ +/* always sorted by x-coordinate. */ +/* */ +/*****************************************************************************/ + +void alternateaxes(sortarray, arraysize, axis) +point *sortarray; +int arraysize; +int axis; +{ + int divider; + + divider = arraysize >> 1; + if (arraysize <= 3) { + /* Recursive base case: subsets of two or three points will be */ + /* handled specially, and should always be sorted by x-coordinate. */ + axis = 0; + } + /* Partition with a horizontal or vertical cut. */ + pointmedian(sortarray, arraysize, divider, axis); + /* Recursively partition the subsets with a cross cut. */ + if (arraysize - divider >= 2) { + if (divider >= 2) { + alternateaxes(sortarray, divider, 1 - axis); + } + alternateaxes(&sortarray[divider], arraysize - divider, 1 - axis); + } +} + +/*****************************************************************************/ +/* */ +/* mergehulls() Merge two adjacent Delaunay triangulations into a */ +/* single Delaunay triangulation. */ +/* */ +/* This is similar to the algorithm given by Guibas and Stolfi, but uses */ +/* a triangle-based, rather than edge-based, data structure. */ +/* */ +/* The algorithm walks up the gap between the two triangulations, knitting */ +/* them together. As they are merged, some of their bounding triangles */ +/* are converted into real triangles of the triangulation. The procedure */ +/* pulls each hull's bounding triangles apart, then knits them together */ +/* like the teeth of two gears. The Delaunay property determines, at each */ +/* step, whether the next "tooth" is a bounding triangle of the left hull */ +/* or the right. When a bounding triangle becomes real, its apex is */ +/* changed from NULL to a real point. */ +/* */ +/* Only two new triangles need to be allocated. These become new bounding */ +/* triangles at the top and bottom of the seam. They are used to connect */ +/* the remaining bounding triangles (those that have not been converted */ +/* into real triangles) into a single fan. */ +/* */ +/* On entry, `farleft' and `innerleft' are bounding triangles of the left */ +/* triangulation. The origin of `farleft' is the leftmost vertex, and */ +/* the destination of `innerleft' is the rightmost vertex of the */ +/* triangulation. Similarly, `innerright' and `farright' are bounding */ +/* triangles of the right triangulation. The origin of `innerright' and */ +/* destination of `farright' are the leftmost and rightmost vertices. */ +/* */ +/* On completion, the origin of `farleft' is the leftmost vertex of the */ +/* merged triangulation, and the destination of `farright' is the rightmost */ +/* vertex. */ +/* */ +/*****************************************************************************/ + +void mergehulls(farleft, innerleft, innerright, farright, axis) +struct triedge *farleft; +struct triedge *innerleft; +struct triedge *innerright; +struct triedge *farright; +int axis; +{ + struct triedge leftcand, rightcand; + struct triedge baseedge; + struct triedge nextedge; + struct triedge sidecasing, topcasing, outercasing; + struct triedge checkedge; + point innerleftdest; + point innerrightorg; + point innerleftapex, innerrightapex; + point farleftpt, farrightpt; + point farleftapex, farrightapex; + point lowerleft, lowerright; + point upperleft, upperright; + point nextapex; + point checkvertex; + int changemade; + int badedge; + int leftfinished, rightfinished; + triangle ptr; /* Temporary variable used by sym(). */ + + dest(*innerleft, innerleftdest); + apex(*innerleft, innerleftapex); + org(*innerright, innerrightorg); + apex(*innerright, innerrightapex); + /* Special treatment for horizontal cuts. */ + if (dwyer && (axis == 1)) { + org(*farleft, farleftpt); + apex(*farleft, farleftapex); + dest(*farright, farrightpt); + apex(*farright, farrightapex); + /* The pointers to the extremal points are shifted to point to the */ + /* topmost and bottommost point of each hull, rather than the */ + /* leftmost and rightmost points. */ + while (farleftapex[1] < farleftpt[1]) { + lnextself(*farleft); + symself(*farleft); + farleftpt = farleftapex; + apex(*farleft, farleftapex); + } + sym(*innerleft, checkedge); + apex(checkedge, checkvertex); + while (checkvertex[1] > innerleftdest[1]) { + lnext(checkedge, *innerleft); + innerleftapex = innerleftdest; + innerleftdest = checkvertex; + sym(*innerleft, checkedge); + apex(checkedge, checkvertex); + } + while (innerrightapex[1] < innerrightorg[1]) { + lnextself(*innerright); + symself(*innerright); + innerrightorg = innerrightapex; + apex(*innerright, innerrightapex); + } + sym(*farright, checkedge); + apex(checkedge, checkvertex); + while (checkvertex[1] > farrightpt[1]) { + lnext(checkedge, *farright); + farrightapex = farrightpt; + farrightpt = checkvertex; + sym(*farright, checkedge); + apex(checkedge, checkvertex); + } + } + /* Find a line tangent to and below both hulls. */ + do { + changemade = 0; + /* Make innerleftdest the "bottommost" point of the left hull. */ + if (counterclockwise(innerleftdest, innerleftapex, innerrightorg) > 0.0) { + lprevself(*innerleft); + symself(*innerleft); + innerleftdest = innerleftapex; + apex(*innerleft, innerleftapex); + changemade = 1; + } + /* Make innerrightorg the "bottommost" point of the right hull. */ + if (counterclockwise(innerrightapex, innerrightorg, innerleftdest) > 0.0) { + lnextself(*innerright); + symself(*innerright); + innerrightorg = innerrightapex; + apex(*innerright, innerrightapex); + changemade = 1; + } + } while (changemade); + /* Find the two candidates to be the next "gear tooth". */ + sym(*innerleft, leftcand); + sym(*innerright, rightcand); + /* Create the bottom new bounding triangle. */ + maketriangle(&baseedge); + /* Connect it to the bounding boxes of the left and right triangulations. */ + bond(baseedge, *innerleft); + lnextself(baseedge); + bond(baseedge, *innerright); + lnextself(baseedge); + setorg(baseedge, innerrightorg); + setdest(baseedge, innerleftdest); + /* Apex is intentionally left NULL. */ + if (verbose > 2) { + printf(" Creating base bounding "); + printtriangle(&baseedge); + } + /* Fix the extreme triangles if necessary. */ + org(*farleft, farleftpt); + if (innerleftdest == farleftpt) { + lnext(baseedge, *farleft); + } + dest(*farright, farrightpt); + if (innerrightorg == farrightpt) { + lprev(baseedge, *farright); + } + /* The vertices of the current knitting edge. */ + lowerleft = innerleftdest; + lowerright = innerrightorg; + /* The candidate vertices for knitting. */ + apex(leftcand, upperleft); + apex(rightcand, upperright); + /* Walk up the gap between the two triangulations, knitting them together. */ + while (1) { + /* Have we reached the top? (This isn't quite the right question, */ + /* because even though the left triangulation might seem finished now, */ + /* moving up on the right triangulation might reveal a new point of */ + /* the left triangulation. And vice-versa.) */ + leftfinished = counterclockwise(upperleft, lowerleft, lowerright) <= 0.0; + rightfinished = counterclockwise(upperright, lowerleft, lowerright) <= 0.0; + if (leftfinished && rightfinished) { + /* Create the top new bounding triangle. */ + maketriangle(&nextedge); + setorg(nextedge, lowerleft); + setdest(nextedge, lowerright); + /* Apex is intentionally left NULL. */ + /* Connect it to the bounding boxes of the two triangulations. */ + bond(nextedge, baseedge); + lnextself(nextedge); + bond(nextedge, rightcand); + lnextself(nextedge); + bond(nextedge, leftcand); + if (verbose > 2) { + printf(" Creating top bounding "); + printtriangle(&baseedge); + } + /* Special treatment for horizontal cuts. */ + if (dwyer && (axis == 1)) { + org(*farleft, farleftpt); + apex(*farleft, farleftapex); + dest(*farright, farrightpt); + apex(*farright, farrightapex); + sym(*farleft, checkedge); + apex(checkedge, checkvertex); + /* The pointers to the extremal points are restored to the leftmost */ + /* and rightmost points (rather than topmost and bottommost). */ + while (checkvertex[0] < farleftpt[0]) { + lprev(checkedge, *farleft); + farleftapex = farleftpt; + farleftpt = checkvertex; + sym(*farleft, checkedge); + apex(checkedge, checkvertex); + } + while (farrightapex[0] > farrightpt[0]) { + lprevself(*farright); + symself(*farright); + farrightpt = farrightapex; + apex(*farright, farrightapex); + } + } + return; + } + /* Consider eliminating edges from the left triangulation. */ + if (!leftfinished) { + /* What vertex would be exposed if an edge were deleted? */ + lprev(leftcand, nextedge); + symself(nextedge); + apex(nextedge, nextapex); + /* If nextapex is NULL, then no vertex would be exposed; the */ + /* triangulation would have been eaten right through. */ + if (nextapex != (point) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(lowerleft, lowerright, upperleft, nextapex) > 0.0; + while (badedge) { + /* Eliminate the edge with an edge flip. As a result, the */ + /* left triangulation will have one more boundary triangle. */ + lnextself(nextedge); + sym(nextedge, topcasing); + lnextself(nextedge); + sym(nextedge, sidecasing); + bond(nextedge, topcasing); + bond(leftcand, sidecasing); + lnextself(leftcand); + sym(leftcand, outercasing); + lprevself(nextedge); + bond(nextedge, outercasing); + /* Correct the vertices to reflect the edge flip. */ + setorg(leftcand, lowerleft); + setdest(leftcand, NULL); + setapex(leftcand, nextapex); + setorg(nextedge, NULL); + setdest(nextedge, upperleft); + setapex(nextedge, nextapex); + /* Consider the newly exposed vertex. */ + upperleft = nextapex; + /* What vertex would be exposed if another edge were deleted? */ + triedgecopy(sidecasing, nextedge); + apex(nextedge, nextapex); + if (nextapex != (point) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(lowerleft, lowerright, upperleft, nextapex) + > 0.0; + } else { + /* Avoid eating right through the triangulation. */ + badedge = 0; + } + } + } + } + /* Consider eliminating edges from the right triangulation. */ + if (!rightfinished) { + /* What vertex would be exposed if an edge were deleted? */ + lnext(rightcand, nextedge); + symself(nextedge); + apex(nextedge, nextapex); + /* If nextapex is NULL, then no vertex would be exposed; the */ + /* triangulation would have been eaten right through. */ + if (nextapex != (point) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(lowerleft, lowerright, upperright, nextapex) > 0.0; + while (badedge) { + /* Eliminate the edge with an edge flip. As a result, the */ + /* right triangulation will have one more boundary triangle. */ + lprevself(nextedge); + sym(nextedge, topcasing); + lprevself(nextedge); + sym(nextedge, sidecasing); + bond(nextedge, topcasing); + bond(rightcand, sidecasing); + lprevself(rightcand); + sym(rightcand, outercasing); + lnextself(nextedge); + bond(nextedge, outercasing); + /* Correct the vertices to reflect the edge flip. */ + setorg(rightcand, NULL); + setdest(rightcand, lowerright); + setapex(rightcand, nextapex); + setorg(nextedge, upperright); + setdest(nextedge, NULL); + setapex(nextedge, nextapex); + /* Consider the newly exposed vertex. */ + upperright = nextapex; + /* What vertex would be exposed if another edge were deleted? */ + triedgecopy(sidecasing, nextedge); + apex(nextedge, nextapex); + if (nextapex != (point) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(lowerleft, lowerright, upperright, nextapex) + > 0.0; + } else { + /* Avoid eating right through the triangulation. */ + badedge = 0; + } + } + } + } + if (leftfinished || (!rightfinished && + (incircle(upperleft, lowerleft, lowerright, upperright) > 0.0))) { + /* Knit the triangulations, adding an edge from `lowerleft' */ + /* to `upperright'. */ + bond(baseedge, rightcand); + lprev(rightcand, baseedge); + setdest(baseedge, lowerleft); + lowerright = upperright; + sym(baseedge, rightcand); + apex(rightcand, upperright); + } else { + /* Knit the triangulations, adding an edge from `upperleft' */ + /* to `lowerright'. */ + bond(baseedge, leftcand); + lnext(leftcand, baseedge); + setorg(baseedge, lowerright); + lowerleft = upperleft; + sym(baseedge, leftcand); + apex(leftcand, upperleft); + } + if (verbose > 2) { + printf(" Connecting "); + printtriangle(&baseedge); + } + } +} + +/*****************************************************************************/ +/* */ +/* divconqrecurse() Recursively form a Delaunay triangulation by the */ +/* divide-and-conquer method. */ +/* */ +/* Recursively breaks down the problem into smaller pieces, which are */ +/* knitted together by mergehulls(). The base cases (problems of two or */ +/* three points) are handled specially here. */ +/* */ +/* On completion, `farleft' and `farright' are bounding triangles such that */ +/* the origin of `farleft' is the leftmost vertex (breaking ties by */ +/* choosing the highest leftmost vertex), and the destination of */ +/* `farright' is the rightmost vertex (breaking ties by choosing the */ +/* lowest rightmost vertex). */ +/* */ +/*****************************************************************************/ + +void divconqrecurse(sortarray, vertices, axis, farleft, farright) +point *sortarray; +int vertices; +int axis; +struct triedge *farleft; +struct triedge *farright; +{ + struct triedge midtri, tri1, tri2, tri3; + struct triedge innerleft, innerright; + REAL area; + int divider; + + if (verbose > 2) { + printf(" Triangulating %d points.\n", vertices); + } + if (vertices == 2) { + /* The triangulation of two vertices is an edge. An edge is */ + /* represented by two bounding triangles. */ + maketriangle(farleft); + setorg(*farleft, sortarray[0]); + setdest(*farleft, sortarray[1]); + /* The apex is intentionally left NULL. */ + maketriangle(farright); + setorg(*farright, sortarray[1]); + setdest(*farright, sortarray[0]); + /* The apex is intentionally left NULL. */ + bond(*farleft, *farright); + lprevself(*farleft); + lnextself(*farright); + bond(*farleft, *farright); + lprevself(*farleft); + lnextself(*farright); + bond(*farleft, *farright); + if (verbose > 2) { + printf(" Creating "); + printtriangle(farleft); + printf(" Creating "); + printtriangle(farright); + } + /* Ensure that the origin of `farleft' is sortarray[0]. */ + lprev(*farright, *farleft); + return; + } else if (vertices == 3) { + /* The triangulation of three vertices is either a triangle (with */ + /* three bounding triangles) or two edges (with four bounding */ + /* triangles). In either case, four triangles are created. */ + maketriangle(&midtri); + maketriangle(&tri1); + maketriangle(&tri2); + maketriangle(&tri3); + area = counterclockwise(sortarray[0], sortarray[1], sortarray[2]); + if (area == 0.0) { + /* Three collinear points; the triangulation is two edges. */ + setorg(midtri, sortarray[0]); + setdest(midtri, sortarray[1]); + setorg(tri1, sortarray[1]); + setdest(tri1, sortarray[0]); + setorg(tri2, sortarray[2]); + setdest(tri2, sortarray[1]); + setorg(tri3, sortarray[1]); + setdest(tri3, sortarray[2]); + /* All apices are intentionally left NULL. */ + bond(midtri, tri1); + bond(tri2, tri3); + lnextself(midtri); + lprevself(tri1); + lnextself(tri2); + lprevself(tri3); + bond(midtri, tri3); + bond(tri1, tri2); + lnextself(midtri); + lprevself(tri1); + lnextself(tri2); + lprevself(tri3); + bond(midtri, tri1); + bond(tri2, tri3); + /* Ensure that the origin of `farleft' is sortarray[0]. */ + triedgecopy(tri1, *farleft); + /* Ensure that the destination of `farright' is sortarray[2]. */ + triedgecopy(tri2, *farright); + } else { + /* The three points are not collinear; the triangulation is one */ + /* triangle, namely `midtri'. */ + setorg(midtri, sortarray[0]); + setdest(tri1, sortarray[0]); + setorg(tri3, sortarray[0]); + /* Apices of tri1, tri2, and tri3 are left NULL. */ + if (area > 0.0) { + /* The vertices are in counterclockwise order. */ + setdest(midtri, sortarray[1]); + setorg(tri1, sortarray[1]); + setdest(tri2, sortarray[1]); + setapex(midtri, sortarray[2]); + setorg(tri2, sortarray[2]); + setdest(tri3, sortarray[2]); + } else { + /* The vertices are in clockwise order. */ + setdest(midtri, sortarray[2]); + setorg(tri1, sortarray[2]); + setdest(tri2, sortarray[2]); + setapex(midtri, sortarray[1]); + setorg(tri2, sortarray[1]); + setdest(tri3, sortarray[1]); + } + /* The topology does not depend on how the vertices are ordered. */ + bond(midtri, tri1); + lnextself(midtri); + bond(midtri, tri2); + lnextself(midtri); + bond(midtri, tri3); + lprevself(tri1); + lnextself(tri2); + bond(tri1, tri2); + lprevself(tri1); + lprevself(tri3); + bond(tri1, tri3); + lnextself(tri2); + lprevself(tri3); + bond(tri2, tri3); + /* Ensure that the origin of `farleft' is sortarray[0]. */ + triedgecopy(tri1, *farleft); + /* Ensure that the destination of `farright' is sortarray[2]. */ + if (area > 0.0) { + triedgecopy(tri2, *farright); + } else { + lnext(*farleft, *farright); + } + } + if (verbose > 2) { + printf(" Creating "); + printtriangle(&midtri); + printf(" Creating "); + printtriangle(&tri1); + printf(" Creating "); + printtriangle(&tri2); + printf(" Creating "); + printtriangle(&tri3); + } + return; + } else { + /* Split the vertices in half. */ + divider = vertices >> 1; + /* Recursively triangulate each half. */ + divconqrecurse(sortarray, divider, 1 - axis, farleft, &innerleft); + divconqrecurse(&sortarray[divider], vertices - divider, 1 - axis, + &innerright, farright); + if (verbose > 1) { + printf(" Joining triangulations with %d and %d vertices.\n", divider, + vertices - divider); + } + /* Merge the two triangulations into one. */ + mergehulls(farleft, &innerleft, &innerright, farright, axis); + } +} + +long removeghosts(startghost) +struct triedge *startghost; +{ + struct triedge searchedge; + struct triedge dissolveedge; + struct triedge deadtri; + point markorg; + long hullsize; + triangle ptr; /* Temporary variable used by sym(). */ + + if (verbose) { + printf(" Removing ghost triangles.\n"); + } + /* Find an edge on the convex hull to start point location from. */ + lprev(*startghost, searchedge); + symself(searchedge); + dummytri[0] = encode(searchedge); + /* Remove the bounding box and count the convex hull edges. */ + triedgecopy(*startghost, dissolveedge); + hullsize = 0; + do { + hullsize++; + lnext(dissolveedge, deadtri); + lprevself(dissolveedge); + symself(dissolveedge); + /* If no PSLG is involved, set the boundary markers of all the points */ + /* on the convex hull. If a PSLG is used, this step is done later. */ + if (!poly) { + /* Watch out for the case where all the input points are collinear. */ + if (dissolveedge.tri != dummytri) { + org(dissolveedge, markorg); + if (pointmark(markorg) == 0) { + setpointmark(markorg, 1); + } + } + } + /* Remove a bounding triangle from a convex hull triangle. */ + dissolve(dissolveedge); + /* Find the next bounding triangle. */ + sym(deadtri, dissolveedge); + /* Delete the bounding triangle. */ + triangledealloc(deadtri.tri); + } while (!triedgeequal(dissolveedge, *startghost)); + return hullsize; +} + +/*****************************************************************************/ +/* */ +/* divconqdelaunay() Form a Delaunay triangulation by the divide-and- */ +/* conquer method. */ +/* */ +/* Sorts the points, calls a recursive procedure to triangulate them, and */ +/* removes the bounding box, setting boundary markers as appropriate. */ +/* */ +/*****************************************************************************/ + +long divconqdelaunay() +{ + point *sortarray; + struct triedge hullleft, hullright; + int divider; + int i, j; + + /* Allocate an array of pointers to points for sorting. */ + sortarray = (point *) malloc(inpoints * sizeof(point)); + if (sortarray == (point *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + traversalinit(&points); + for (i = 0; i < inpoints; i++) { + sortarray[i] = pointtraverse(); + } + if (verbose) { + printf(" Sorting points.\n"); + } + /* Sort the points. */ + pointsort(sortarray, inpoints); + /* Discard duplicate points, which can really mess up the algorithm. */ + i = 0; + for (j = 1; j < inpoints; j++) { + if ((sortarray[i][0] == sortarray[j][0]) + && (sortarray[i][1] == sortarray[j][1])) { + if (!quiet) { + printf( +"Warning: A duplicate point at (%.12g, %.12g) appeared and was ignored.\n", + sortarray[j][0], sortarray[j][1]); + } +/* Commented out - would eliminate point from output .node file, but causes + a failure if some segment has this point as an endpoint. + setpointmark(sortarray[j], DEADPOINT); +*/ + } else { + i++; + sortarray[i] = sortarray[j]; + } + } + i++; + if (dwyer) { + /* Re-sort the array of points to accommodate alternating cuts. */ + divider = i >> 1; + if (i - divider >= 2) { + if (divider >= 2) { + alternateaxes(sortarray, divider, 1); + } + alternateaxes(&sortarray[divider], i - divider, 1); + } + } + if (verbose) { + printf(" Forming triangulation.\n"); + } + /* Form the Delaunay triangulation. */ + divconqrecurse(sortarray, i, 0, &hullleft, &hullright); + free(sortarray); + + return removeghosts(&hullleft); +} + +/** **/ +/** **/ +/********* Divide-and-conquer Delaunay triangulation ends here *********/ + +/********* Incremental Delaunay triangulation begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* boundingbox() Form an "infinite" bounding triangle to insert points */ +/* into. */ +/* */ +/* The points at "infinity" are assigned finite coordinates, which are used */ +/* by the point location routines, but (mostly) ignored by the Delaunay */ +/* edge flip routines. */ +/* */ +/*****************************************************************************/ + +#ifndef REDUCED + +void boundingbox() +{ + struct triedge inftri; /* Handle for the triangular bounding box. */ + REAL width; + + if (verbose) { + printf(" Creating triangular bounding box.\n"); + } + /* Find the width (or height, whichever is larger) of the triangulation. */ + width = xmax - xmin; + if (ymax - ymin > width) { + width = ymax - ymin; + } + if (width == 0.0) { + width = 1.0; + } + /* Create the vertices of the bounding box. */ + infpoint1 = (point) malloc(points.itembytes); + infpoint2 = (point) malloc(points.itembytes); + infpoint3 = (point) malloc(points.itembytes); + if ((infpoint1 == (point) NULL) || (infpoint2 == (point) NULL) + || (infpoint3 == (point) NULL)) { + printf("Error: Out of memory.\n"); + exit(1); + } + infpoint1[0] = xmin - 50.0 * width; + infpoint1[1] = ymin - 40.0 * width; + infpoint2[0] = xmax + 50.0 * width; + infpoint2[1] = ymin - 40.0 * width; + infpoint3[0] = 0.5 * (xmin + xmax); + infpoint3[1] = ymax + 60.0 * width; + + /* Create the bounding box. */ + maketriangle(&inftri); + setorg(inftri, infpoint1); + setdest(inftri, infpoint2); + setapex(inftri, infpoint3); + /* Link dummytri to the bounding box so we can always find an */ + /* edge to begin searching (point location) from. */ + dummytri[0] = (triangle) inftri.tri; + if (verbose > 2) { + printf(" Creating "); + printtriangle(&inftri); + } +} + +#endif /* not REDUCED */ + +/*****************************************************************************/ +/* */ +/* removebox() Remove the "infinite" bounding triangle, setting boundary */ +/* markers as appropriate. */ +/* */ +/* The triangular bounding box has three boundary triangles (one for each */ +/* side of the bounding box), and a bunch of triangles fanning out from */ +/* the three bounding box vertices (one triangle for each edge of the */ +/* convex hull of the inner mesh). This routine removes these triangles. */ +/* */ +/*****************************************************************************/ + +#ifndef REDUCED + +long removebox() +{ + struct triedge deadtri; + struct triedge searchedge; + struct triedge checkedge; + struct triedge nextedge, finaledge, dissolveedge; + point markorg; + long hullsize; + triangle ptr; /* Temporary variable used by sym(). */ + + if (verbose) { + printf(" Removing triangular bounding box.\n"); + } + /* Find a boundary triangle. */ + nextedge.tri = dummytri; + nextedge.orient = 0; + symself(nextedge); + /* Mark a place to stop. */ + lprev(nextedge, finaledge); + lnextself(nextedge); + symself(nextedge); + /* Find a triangle (on the boundary of the point set) that isn't */ + /* a bounding box triangle. */ + lprev(nextedge, searchedge); + symself(searchedge); + /* Check whether nextedge is another boundary triangle */ + /* adjacent to the first one. */ + lnext(nextedge, checkedge); + symself(checkedge); + if (checkedge.tri == dummytri) { + /* Go on to the next triangle. There are only three boundary */ + /* triangles, and this next triangle cannot be the third one, */ + /* so it's safe to stop here. */ + lprevself(searchedge); + symself(searchedge); + } + /* Find a new boundary edge to search from, as the current search */ + /* edge lies on a bounding box triangle and will be deleted. */ + dummytri[0] = encode(searchedge); + hullsize = -2l; + while (!triedgeequal(nextedge, finaledge)) { + hullsize++; + lprev(nextedge, dissolveedge); + symself(dissolveedge); + /* If not using a PSLG, the vertices should be marked now. */ + /* (If using a PSLG, markhull() will do the job.) */ + if (!poly) { + /* Be careful! One must check for the case where all the input */ + /* points are collinear, and thus all the triangles are part of */ + /* the bounding box. Otherwise, the setpointmark() call below */ + /* will cause a bad pointer reference. */ + if (dissolveedge.tri != dummytri) { + org(dissolveedge, markorg); + if (pointmark(markorg) == 0) { + setpointmark(markorg, 1); + } + } + } + /* Disconnect the bounding box triangle from the mesh triangle. */ + dissolve(dissolveedge); + lnext(nextedge, deadtri); + sym(deadtri, nextedge); + /* Get rid of the bounding box triangle. */ + triangledealloc(deadtri.tri); + /* Do we need to turn the corner? */ + if (nextedge.tri == dummytri) { + /* Turn the corner. */ + triedgecopy(dissolveedge, nextedge); + } + } + triangledealloc(finaledge.tri); + + free(infpoint1); /* Deallocate the bounding box vertices. */ + free(infpoint2); + free(infpoint3); + + return hullsize; +} + +#endif /* not REDUCED */ + +/*****************************************************************************/ +/* */ +/* incrementaldelaunay() Form a Delaunay triangulation by incrementally */ +/* adding vertices. */ +/* */ +/*****************************************************************************/ + +#ifndef REDUCED + +long incrementaldelaunay() +{ + struct triedge starttri; + point pointloop; + int i; + + /* Create a triangular bounding box. */ + boundingbox(); + if (verbose) { + printf(" Incrementally inserting points.\n"); + } + traversalinit(&points); + pointloop = pointtraverse(); + i = 1; + while (pointloop != (point) NULL) { + /* Find a boundary triangle to search from. */ + starttri.tri = (triangle *) NULL; + if (insertsite(pointloop, &starttri, (struct edge *) NULL, 0, 0) == + DUPLICATEPOINT) { + if (!quiet) { + printf( +"Warning: A duplicate point at (%.12g, %.12g) appeared and was ignored.\n", + pointloop[0], pointloop[1]); + } +/* Commented out - would eliminate point from output .node file. + setpointmark(pointloop, DEADPOINT); +*/ + } + pointloop = pointtraverse(); + i++; + } + /* Remove the bounding box. */ + return removebox(); +} + +#endif /* not REDUCED */ + +/** **/ +/** **/ +/********* Incremental Delaunay triangulation ends here *********/ + +/********* Sweepline Delaunay triangulation begins here *********/ +/** **/ +/** **/ + +#ifndef REDUCED + +void eventheapinsert(heap, heapsize, newevent) +struct event **heap; +int heapsize; +struct event *newevent; +{ + REAL eventx, eventy; + int eventnum; + int parent; + int notdone; + + eventx = newevent->xkey; + eventy = newevent->ykey; + eventnum = heapsize; + notdone = eventnum > 0; + while (notdone) { + parent = (eventnum - 1) >> 1; + if ((heap[parent]->ykey < eventy) || + ((heap[parent]->ykey == eventy) + && (heap[parent]->xkey <= eventx))) { + notdone = 0; + } else { + heap[eventnum] = heap[parent]; + heap[eventnum]->heapposition = eventnum; + + eventnum = parent; + notdone = eventnum > 0; + } + } + heap[eventnum] = newevent; + newevent->heapposition = eventnum; +} + +#endif /* not REDUCED */ + +#ifndef REDUCED + +void eventheapify(heap, heapsize, eventnum) +struct event **heap; +int heapsize; +int eventnum; +{ + struct event *thisevent; + REAL eventx, eventy; + int leftchild, rightchild; + int smallest; + int notdone; + + thisevent = heap[eventnum]; + eventx = thisevent->xkey; + eventy = thisevent->ykey; + leftchild = 2 * eventnum + 1; + notdone = leftchild < heapsize; + while (notdone) { + if ((heap[leftchild]->ykey < eventy) || + ((heap[leftchild]->ykey == eventy) + && (heap[leftchild]->xkey < eventx))) { + smallest = leftchild; + } else { + smallest = eventnum; + } + rightchild = leftchild + 1; + if (rightchild < heapsize) { + if ((heap[rightchild]->ykey < heap[smallest]->ykey) || + ((heap[rightchild]->ykey == heap[smallest]->ykey) + && (heap[rightchild]->xkey < heap[smallest]->xkey))) { + smallest = rightchild; + } + } + if (smallest == eventnum) { + notdone = 0; + } else { + heap[eventnum] = heap[smallest]; + heap[eventnum]->heapposition = eventnum; + heap[smallest] = thisevent; + thisevent->heapposition = smallest; + + eventnum = smallest; + leftchild = 2 * eventnum + 1; + notdone = leftchild < heapsize; + } + } +} + +#endif /* not REDUCED */ + +#ifndef REDUCED + +void eventheapdelete(heap, heapsize, eventnum) +struct event **heap; +int heapsize; +int eventnum; +{ + struct event *moveevent; + REAL eventx, eventy; + int parent; + int notdone; + + moveevent = heap[heapsize - 1]; + if (eventnum > 0) { + eventx = moveevent->xkey; + eventy = moveevent->ykey; + do { + parent = (eventnum - 1) >> 1; + if ((heap[parent]->ykey < eventy) || + ((heap[parent]->ykey == eventy) + && (heap[parent]->xkey <= eventx))) { + notdone = 0; + } else { + heap[eventnum] = heap[parent]; + heap[eventnum]->heapposition = eventnum; + + eventnum = parent; + notdone = eventnum > 0; + } + } while (notdone); + } + heap[eventnum] = moveevent; + moveevent->heapposition = eventnum; + eventheapify(heap, heapsize - 1, eventnum); +} + +#endif /* not REDUCED */ + +#ifndef REDUCED + +void createeventheap(eventheap, events, freeevents) +struct event ***eventheap; +struct event **events; +struct event **freeevents; +{ + point thispoint; + int maxevents; + int i; + + maxevents = (3 * inpoints) / 2; + *eventheap = (struct event **) malloc(maxevents * sizeof(struct event *)); + if (*eventheap == (struct event **) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + *events = (struct event *) malloc(maxevents * sizeof(struct event)); + if (*events == (struct event *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + traversalinit(&points); + for (i = 0; i < inpoints; i++) { + thispoint = pointtraverse(); + (*events)[i].eventptr = (VOID *) thispoint; + (*events)[i].xkey = thispoint[0]; + (*events)[i].ykey = thispoint[1]; + eventheapinsert(*eventheap, i, *events + i); + } + *freeevents = (struct event *) NULL; + for (i = maxevents - 1; i >= inpoints; i--) { + (*events)[i].eventptr = (VOID *) *freeevents; + *freeevents = *events + i; + } +} + +#endif /* not REDUCED */ + +#ifndef REDUCED + +int rightofhyperbola(fronttri, newsite) +struct triedge *fronttri; +point newsite; +{ + point leftpoint, rightpoint; + REAL dxa, dya, dxb, dyb; + + hyperbolacount++; + + dest(*fronttri, leftpoint); + apex(*fronttri, rightpoint); + if ((leftpoint[1] < rightpoint[1]) + || ((leftpoint[1] == rightpoint[1]) && (leftpoint[0] < rightpoint[0]))) { + if (newsite[0] >= rightpoint[0]) { + return 1; + } + } else { + if (newsite[0] <= leftpoint[0]) { + return 0; + } + } + dxa = leftpoint[0] - newsite[0]; + dya = leftpoint[1] - newsite[1]; + dxb = rightpoint[0] - newsite[0]; + dyb = rightpoint[1] - newsite[1]; + return dya * (dxb * dxb + dyb * dyb) > dyb * (dxa * dxa + dya * dya); +} + +#endif /* not REDUCED */ + +#ifndef REDUCED + +REAL circletop(pa, pb, pc, ccwabc) +point pa; +point pb; +point pc; +REAL ccwabc; +{ + REAL xac, yac, xbc, ybc, xab, yab; + REAL aclen2, bclen2, ablen2; + + circletopcount++; + + xac = pa[0] - pc[0]; + yac = pa[1] - pc[1]; + xbc = pb[0] - pc[0]; + ybc = pb[1] - pc[1]; + xab = pa[0] - pb[0]; + yab = pa[1] - pb[1]; + aclen2 = xac * xac + yac * yac; + bclen2 = xbc * xbc + ybc * ybc; + ablen2 = xab * xab + yab * yab; + return pc[1] + (xac * bclen2 - xbc * aclen2 + sqrt(aclen2 * bclen2 * ablen2)) + / (2.0 * ccwabc); +} + +#endif /* not REDUCED */ + +#ifndef REDUCED + +void check4deadevent(checktri, freeevents, eventheap, heapsize) +struct triedge *checktri; +struct event **freeevents; +struct event **eventheap; +int *heapsize; +{ + struct event *deadevent; + point eventpoint; + int eventnum; + + org(*checktri, eventpoint); + if (eventpoint != (point) NULL) { + deadevent = (struct event *) eventpoint; + eventnum = deadevent->heapposition; + deadevent->eventptr = (VOID *) *freeevents; + *freeevents = deadevent; + eventheapdelete(eventheap, *heapsize, eventnum); + (*heapsize)--; + setorg(*checktri, NULL); + } +} + +#endif /* not REDUCED */ + +#ifndef REDUCED + +struct splaynode *splay(splaytree, searchpoint, searchtri) +struct splaynode *splaytree; +point searchpoint; +struct triedge *searchtri; +{ + struct splaynode *child, *grandchild; + struct splaynode *lefttree, *righttree; + struct splaynode *leftright; + point checkpoint; + int rightofroot, rightofchild; + + if (splaytree == (struct splaynode *) NULL) { + return (struct splaynode *) NULL; + } + dest(splaytree->keyedge, checkpoint); + if (checkpoint == splaytree->keydest) { + rightofroot = rightofhyperbola(&splaytree->keyedge, searchpoint); + if (rightofroot) { + triedgecopy(splaytree->keyedge, *searchtri); + child = splaytree->rchild; + } else { + child = splaytree->lchild; + } + if (child == (struct splaynode *) NULL) { + return splaytree; + } + dest(child->keyedge, checkpoint); + if (checkpoint != child->keydest) { + child = splay(child, searchpoint, searchtri); + if (child == (struct splaynode *) NULL) { + if (rightofroot) { + splaytree->rchild = (struct splaynode *) NULL; + } else { + splaytree->lchild = (struct splaynode *) NULL; + } + return splaytree; + } + } + rightofchild = rightofhyperbola(&child->keyedge, searchpoint); + if (rightofchild) { + triedgecopy(child->keyedge, *searchtri); + grandchild = splay(child->rchild, searchpoint, searchtri); + child->rchild = grandchild; + } else { + grandchild = splay(child->lchild, searchpoint, searchtri); + child->lchild = grandchild; + } + if (grandchild == (struct splaynode *) NULL) { + if (rightofroot) { + splaytree->rchild = child->lchild; + child->lchild = splaytree; + } else { + splaytree->lchild = child->rchild; + child->rchild = splaytree; + } + return child; + } + if (rightofchild) { + if (rightofroot) { + splaytree->rchild = child->lchild; + child->lchild = splaytree; + } else { + splaytree->lchild = grandchild->rchild; + grandchild->rchild = splaytree; + } + child->rchild = grandchild->lchild; + grandchild->lchild = child; + } else { + if (rightofroot) { + splaytree->rchild = grandchild->lchild; + grandchild->lchild = splaytree; + } else { + splaytree->lchild = child->rchild; + child->rchild = splaytree; + } + child->lchild = grandchild->rchild; + grandchild->rchild = child; + } + return grandchild; + } else { + lefttree = splay(splaytree->lchild, searchpoint, searchtri); + righttree = splay(splaytree->rchild, searchpoint, searchtri); + + pooldealloc(&splaynodes, (VOID *) splaytree); + if (lefttree == (struct splaynode *) NULL) { + return righttree; + } else if (righttree == (struct splaynode *) NULL) { + return lefttree; + } else if (lefttree->rchild == (struct splaynode *) NULL) { + lefttree->rchild = righttree->lchild; + righttree->lchild = lefttree; + return righttree; + } else if (righttree->lchild == (struct splaynode *) NULL) { + righttree->lchild = lefttree->rchild; + lefttree->rchild = righttree; + return lefttree; + } else { +/* printf("Holy Toledo!!!\n"); */ + leftright = lefttree->rchild; + while (leftright->rchild != (struct splaynode *) NULL) { + leftright = leftright->rchild; + } + leftright->rchild = righttree; + return lefttree; + } + } +} + +#endif /* not REDUCED */ + +#ifndef REDUCED + +struct splaynode *splayinsert(splayroot, newkey, searchpoint) +struct splaynode *splayroot; +struct triedge *newkey; +point searchpoint; +{ + struct splaynode *newsplaynode; + + newsplaynode = (struct splaynode *) poolalloc(&splaynodes); + triedgecopy(*newkey, newsplaynode->keyedge); + dest(*newkey, newsplaynode->keydest); + if (splayroot == (struct splaynode *) NULL) { + newsplaynode->lchild = (struct splaynode *) NULL; + newsplaynode->rchild = (struct splaynode *) NULL; + } else if (rightofhyperbola(&splayroot->keyedge, searchpoint)) { + newsplaynode->lchild = splayroot; + newsplaynode->rchild = splayroot->rchild; + splayroot->rchild = (struct splaynode *) NULL; + } else { + newsplaynode->lchild = splayroot->lchild; + newsplaynode->rchild = splayroot; + splayroot->lchild = (struct splaynode *) NULL; + } + return newsplaynode; +} + +#endif /* not REDUCED */ + +#ifndef REDUCED + +struct splaynode *circletopinsert(splayroot, newkey, pa, pb, pc, topy) +struct splaynode *splayroot; +struct triedge *newkey; +point pa; +point pb; +point pc; +REAL topy; +{ + REAL ccwabc; + REAL xac, yac, xbc, ybc; + REAL aclen2, bclen2; + REAL searchpoint[2]; + struct triedge dummytri; + + ccwabc = counterclockwise(pa, pb, pc); + xac = pa[0] - pc[0]; + yac = pa[1] - pc[1]; + xbc = pb[0] - pc[0]; + ybc = pb[1] - pc[1]; + aclen2 = xac * xac + yac * yac; + bclen2 = xbc * xbc + ybc * ybc; + searchpoint[0] = pc[0] - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc); + searchpoint[1] = topy; + return splayinsert(splay(splayroot, (point) searchpoint, &dummytri), newkey, + (point) searchpoint); +} + +#endif /* not REDUCED */ + +#ifndef REDUCED + +struct splaynode *frontlocate(splayroot, bottommost, searchpoint, searchtri, + farright) +struct splaynode *splayroot; +struct triedge *bottommost; +point searchpoint; +struct triedge *searchtri; +int *farright; +{ + int farrightflag; + triangle ptr; /* Temporary variable used by onext(). */ + + triedgecopy(*bottommost, *searchtri); + splayroot = splay(splayroot, searchpoint, searchtri); + + farrightflag = 0; + while (!farrightflag && rightofhyperbola(searchtri, searchpoint)) { + onextself(*searchtri); + farrightflag = triedgeequal(*searchtri, *bottommost); + } + *farright = farrightflag; + return splayroot; +} + +#endif /* not REDUCED */ + +#ifndef REDUCED + +long sweeplinedelaunay() +{ + struct event **eventheap; + struct event *events; + struct event *freeevents; + struct event *nextevent; + struct event *newevent; + struct splaynode *splayroot; + struct triedge bottommost; + struct triedge searchtri; + struct triedge fliptri; + struct triedge lefttri, righttri, farlefttri, farrighttri; + struct triedge inserttri; + point firstpoint, secondpoint; + point nextpoint, lastpoint; + point connectpoint; + point leftpoint, midpoint, rightpoint; + REAL lefttest, righttest; + int heapsize; + int check4events, farrightflag; + triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */ + + poolinit(&splaynodes, sizeof(struct splaynode), SPLAYNODEPERBLOCK, POINTER, + 0); + splayroot = (struct splaynode *) NULL; + + if (verbose) { + printf(" Placing points in event heap.\n"); + } + createeventheap(&eventheap, &events, &freeevents); + heapsize = inpoints; + + if (verbose) { + printf(" Forming triangulation.\n"); + } + maketriangle(&lefttri); + maketriangle(&righttri); + bond(lefttri, righttri); + lnextself(lefttri); + lprevself(righttri); + bond(lefttri, righttri); + lnextself(lefttri); + lprevself(righttri); + bond(lefttri, righttri); + firstpoint = (point) eventheap[0]->eventptr; + eventheap[0]->eventptr = (VOID *) freeevents; + freeevents = eventheap[0]; + eventheapdelete(eventheap, heapsize, 0); + heapsize--; + do { + if (heapsize == 0) { + printf("Error: Input points are all identical.\n"); + exit(1); + } + secondpoint = (point) eventheap[0]->eventptr; + eventheap[0]->eventptr = (VOID *) freeevents; + freeevents = eventheap[0]; + eventheapdelete(eventheap, heapsize, 0); + heapsize--; + if ((firstpoint[0] == secondpoint[0]) + && (firstpoint[1] == secondpoint[1])) { + printf( +"Warning: A duplicate point at (%.12g, %.12g) appeared and was ignored.\n", + secondpoint[0], secondpoint[1]); +/* Commented out - would eliminate point from output .node file. + setpointmark(secondpoint, DEADPOINT); +*/ + } + } while ((firstpoint[0] == secondpoint[0]) + && (firstpoint[1] == secondpoint[1])); + setorg(lefttri, firstpoint); + setdest(lefttri, secondpoint); + setorg(righttri, secondpoint); + setdest(righttri, firstpoint); + lprev(lefttri, bottommost); + lastpoint = secondpoint; + while (heapsize > 0) { + nextevent = eventheap[0]; + eventheapdelete(eventheap, heapsize, 0); + heapsize--; + check4events = 1; + if (nextevent->xkey < xmin) { + decode(nextevent->eventptr, fliptri); + oprev(fliptri, farlefttri); + check4deadevent(&farlefttri, &freeevents, eventheap, &heapsize); + onext(fliptri, farrighttri); + check4deadevent(&farrighttri, &freeevents, eventheap, &heapsize); + + if (triedgeequal(farlefttri, bottommost)) { + lprev(fliptri, bottommost); + } + flip(&fliptri); + setapex(fliptri, NULL); + lprev(fliptri, lefttri); + lnext(fliptri, righttri); + sym(lefttri, farlefttri); + + if (randomnation(SAMPLERATE) == 0) { + symself(fliptri); + dest(fliptri, leftpoint); + apex(fliptri, midpoint); + org(fliptri, rightpoint); + splayroot = circletopinsert(splayroot, &lefttri, leftpoint, midpoint, + rightpoint, nextevent->ykey); + } + } else { + nextpoint = (point) nextevent->eventptr; + if ((nextpoint[0] == lastpoint[0]) && (nextpoint[1] == lastpoint[1])) { + printf( +"Warning: A duplicate point at (%.12g, %.12g) appeared and was ignored.\n", + nextpoint[0], nextpoint[1]); +/* Commented out - would eliminate point from output .node file. + setpointmark(nextpoint, DEADPOINT); +*/ + check4events = 0; + } else { + lastpoint = nextpoint; + + splayroot = frontlocate(splayroot, &bottommost, nextpoint, &searchtri, + &farrightflag); +/* + triedgecopy(bottommost, searchtri); + farrightflag = 0; + while (!farrightflag && rightofhyperbola(&searchtri, nextpoint)) { + onextself(searchtri); + farrightflag = triedgeequal(searchtri, bottommost); + } +*/ + + check4deadevent(&searchtri, &freeevents, eventheap, &heapsize); + + triedgecopy(searchtri, farrighttri); + sym(searchtri, farlefttri); + maketriangle(&lefttri); + maketriangle(&righttri); + dest(farrighttri, connectpoint); + setorg(lefttri, connectpoint); + setdest(lefttri, nextpoint); + setorg(righttri, nextpoint); + setdest(righttri, connectpoint); + bond(lefttri, righttri); + lnextself(lefttri); + lprevself(righttri); + bond(lefttri, righttri); + lnextself(lefttri); + lprevself(righttri); + bond(lefttri, farlefttri); + bond(righttri, farrighttri); + if (!farrightflag && triedgeequal(farrighttri, bottommost)) { + triedgecopy(lefttri, bottommost); + } + + if (randomnation(SAMPLERATE) == 0) { + splayroot = splayinsert(splayroot, &lefttri, nextpoint); + } else if (randomnation(SAMPLERATE) == 0) { + lnext(righttri, inserttri); + splayroot = splayinsert(splayroot, &inserttri, nextpoint); + } + } + } + nextevent->eventptr = (VOID *) freeevents; + freeevents = nextevent; + + if (check4events) { + apex(farlefttri, leftpoint); + dest(lefttri, midpoint); + apex(lefttri, rightpoint); + lefttest = counterclockwise(leftpoint, midpoint, rightpoint); + if (lefttest > 0.0) { + newevent = freeevents; + freeevents = (struct event *) freeevents->eventptr; + newevent->xkey = xminextreme; + newevent->ykey = circletop(leftpoint, midpoint, rightpoint, + lefttest); + newevent->eventptr = (VOID *) encode(lefttri); + eventheapinsert(eventheap, heapsize, newevent); + heapsize++; + setorg(lefttri, newevent); + } + apex(righttri, leftpoint); + org(righttri, midpoint); + apex(farrighttri, rightpoint); + righttest = counterclockwise(leftpoint, midpoint, rightpoint); + if (righttest > 0.0) { + newevent = freeevents; + freeevents = (struct event *) freeevents->eventptr; + newevent->xkey = xminextreme; + newevent->ykey = circletop(leftpoint, midpoint, rightpoint, + righttest); + newevent->eventptr = (VOID *) encode(farrighttri); + eventheapinsert(eventheap, heapsize, newevent); + heapsize++; + setorg(farrighttri, newevent); + } + } + } + + pooldeinit(&splaynodes); + lprevself(bottommost); + return removeghosts(&bottommost); +} + +#endif /* not REDUCED */ + +/** **/ +/** **/ +/********* Sweepline Delaunay triangulation ends here *********/ + +/********* General mesh construction routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* delaunay() Form a Delaunay triangulation. */ +/* */ +/*****************************************************************************/ + +long delaunay() +{ + eextras = 0; + initializetrisegpools(); + +#ifdef REDUCED + if (!quiet) { + printf( + "Constructing Delaunay triangulation by divide-and-conquer method.\n"); + } + return divconqdelaunay(); +#else /* not REDUCED */ + if (!quiet) { + printf("Constructing Delaunay triangulation "); + if (incremental) { + printf("by incremental method.\n"); + } else if (sweepline) { + printf("by sweepline method.\n"); + } else { + printf("by divide-and-conquer method.\n"); + } + } + if (incremental) { + return incrementaldelaunay(); + } else if (sweepline) { + return sweeplinedelaunay(); + } else { + return divconqdelaunay(); + } +#endif /* not REDUCED */ +} + +/*****************************************************************************/ +/* */ +/* reconstruct() Reconstruct a triangulation from its .ele (and possibly */ +/* .poly) file. Used when the -r switch is used. */ +/* */ +/* Reads an .ele file and reconstructs the original mesh. If the -p switch */ +/* is used, this procedure will also read a .poly file and reconstruct the */ +/* shell edges of the original mesh. If the -a switch is used, this */ +/* procedure will also read an .area file and set a maximum area constraint */ +/* on each triangle. */ +/* */ +/* Points that are not corners of triangles, such as nodes on edges of */ +/* subparametric elements, are discarded. */ +/* */ +/* This routine finds the adjacencies between triangles (and shell edges) */ +/* by forming one stack of triangles for each vertex. Each triangle is on */ +/* three different stacks simultaneously. Each triangle's shell edge */ +/* pointers are used to link the items in each stack. This memory-saving */ +/* feature makes the code harder to read. The most important thing to keep */ +/* in mind is that each triangle is removed from a stack precisely when */ +/* the corresponding pointer is adjusted to refer to a shell edge rather */ +/* than the next triangle of the stack. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +#ifdef TRILIBRARY + +int reconstruct(trianglelist, triangleattriblist, trianglearealist, elements, + corners, attribs, segmentlist, segmentmarkerlist, + numberofsegments) +int *trianglelist; +REAL *triangleattriblist; +REAL *trianglearealist; +int elements; +int corners; +int attribs; +int *segmentlist; +int *segmentmarkerlist; +int numberofsegments; + +#else /* not TRILIBRARY */ + +long reconstruct(elefilename, areafilename, polyfilename, polyfile) +char *elefilename; +char *areafilename; +char *polyfilename; +FILE *polyfile; + +#endif /* not TRILIBRARY */ + +{ +#ifdef TRILIBRARY + int pointindex; + int attribindex; +#else /* not TRILIBRARY */ + FILE *elefile; + FILE *areafile; + char inputline[INPUTLINESIZE]; + char *stringptr; + int areaelements; +#endif /* not TRILIBRARY */ + struct triedge triangleloop; + struct triedge triangleleft; + struct triedge checktri; + struct triedge checkleft; + struct triedge checkneighbor; + struct edge shelleloop; + triangle *vertexarray; + triangle *prevlink; + triangle nexttri; + point tdest, tapex; + point checkdest, checkapex; + point shorg; + point killpoint; + REAL area; + int corner[3]; + int end[2]; + int killpointindex; + int incorners; + int segmentmarkers; + int boundmarker; + int aroundpoint; + long hullsize; + int notfound; + int elementnumber, segmentnumber; + int i, j; + triangle ptr; /* Temporary variable used by sym(). */ + +#ifdef TRILIBRARY + inelements = elements; + incorners = corners; + if (incorners < 3) { + printf("Error: Triangles must have at least 3 points.\n"); + exit(1); + } + eextras = attribs; +#else /* not TRILIBRARY */ + /* Read the triangles from an .ele file. */ + if (!quiet) { + printf("Opening %s.\n", elefilename); + } + elefile = fopen(elefilename, "r"); + if (elefile == (FILE *) NULL) { + printf(" Error: Cannot access file %s.\n", elefilename); + exit(1); + } + /* Read number of triangles, number of points per triangle, and */ + /* number of triangle attributes from .ele file. */ + stringptr = readline(inputline, elefile, elefilename); + inelements = (int) strtol (stringptr, &stringptr, 0); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + incorners = 3; + } else { + incorners = (int) strtol (stringptr, &stringptr, 0); + if (incorners < 3) { + printf("Error: Triangles in %s must have at least 3 points.\n", + elefilename); + exit(1); + } + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + eextras = 0; + } else { + eextras = (int) strtol (stringptr, &stringptr, 0); + } +#endif /* not TRILIBRARY */ + + initializetrisegpools(); + + /* Create the triangles. */ + for (elementnumber = 1; elementnumber <= inelements; elementnumber++) { + maketriangle(&triangleloop); + /* Mark the triangle as living. */ + triangleloop.tri[3] = (triangle) triangleloop.tri; + } + + if (poly) { +#ifdef TRILIBRARY + insegments = numberofsegments; + segmentmarkers = segmentmarkerlist != (int *) NULL; +#else /* not TRILIBRARY */ + /* Read number of segments and number of segment */ + /* boundary markers from .poly file. */ + stringptr = readline(inputline, polyfile, inpolyfilename); + insegments = (int) strtol (stringptr, &stringptr, 0); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + segmentmarkers = 0; + } else { + segmentmarkers = (int) strtol (stringptr, &stringptr, 0); + } +#endif /* not TRILIBRARY */ + + /* Create the shell edges. */ + for (segmentnumber = 1; segmentnumber <= insegments; segmentnumber++) { + makeshelle(&shelleloop); + /* Mark the shell edge as living. */ + shelleloop.sh[2] = (shelle) shelleloop.sh; + } + } + +#ifdef TRILIBRARY + pointindex = 0; + attribindex = 0; +#else /* not TRILIBRARY */ + if (vararea) { + /* Open an .area file, check for consistency with the .ele file. */ + if (!quiet) { + printf("Opening %s.\n", areafilename); + } + areafile = fopen(areafilename, "r"); + if (areafile == (FILE *) NULL) { + printf(" Error: Cannot access file %s.\n", areafilename); + exit(1); + } + stringptr = readline(inputline, areafile, areafilename); + areaelements = (int) strtol (stringptr, &stringptr, 0); + if (areaelements != inelements) { + printf("Error: %s and %s disagree on number of triangles.\n", + elefilename, areafilename); + exit(1); + } + } +#endif /* not TRILIBRARY */ + + if (!quiet) { + printf("Reconstructing mesh.\n"); + } + /* Allocate a temporary array that maps each point to some adjacent */ + /* triangle. I took care to allocate all the permanent memory for */ + /* triangles and shell edges first. */ + vertexarray = (triangle *) malloc(points.items * sizeof(triangle)); + if (vertexarray == (triangle *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + /* Each point is initially unrepresented. */ + for (i = 0; i < points.items; i++) { + vertexarray[i] = (triangle) dummytri; + } + + if (verbose) { + printf(" Assembling triangles.\n"); + } + /* Read the triangles from the .ele file, and link */ + /* together those that share an edge. */ + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + elementnumber = firstnumber; + while (triangleloop.tri != (triangle *) NULL) { +#ifdef TRILIBRARY + /* Copy the triangle's three corners. */ + for (j = 0; j < 3; j++) { + corner[j] = trianglelist[pointindex++]; + if ((corner[j] < firstnumber) || (corner[j] >= firstnumber + inpoints)) { + printf("Error: Triangle %d has an invalid vertex index.\n", + elementnumber); + exit(1); + } + } +#else /* not TRILIBRARY */ + /* Read triangle number and the triangle's three corners. */ + stringptr = readline(inputline, elefile, elefilename); + for (j = 0; j < 3; j++) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Triangle %d is missing point %d in %s.\n", + elementnumber, j + 1, elefilename); + exit(1); + } else { + corner[j] = (int) strtol (stringptr, &stringptr, 0); + if ((corner[j] < firstnumber) || + (corner[j] >= firstnumber + inpoints)) { + printf("Error: Triangle %d has an invalid vertex index.\n", + elementnumber); + exit(1); + } + } + } +#endif /* not TRILIBRARY */ + + /* Find out about (and throw away) extra nodes. */ + for (j = 3; j < incorners; j++) { +#ifdef TRILIBRARY + killpointindex = trianglelist[pointindex++]; +#else /* not TRILIBRARY */ + stringptr = findfield(stringptr); + if (*stringptr != '\0') { + killpointindex = (int) strtol (stringptr, &stringptr, 0); +#endif /* not TRILIBRARY */ + if ((killpointindex >= firstnumber) && + (killpointindex < firstnumber + inpoints)) { + /* Delete the non-corner point if it's not already deleted. */ + killpoint = getpoint(killpointindex); + if (pointmark(killpoint) != DEADPOINT) { + pointdealloc(killpoint); + } + } +#ifndef TRILIBRARY + } +#endif /* not TRILIBRARY */ + } + + /* Read the triangle's attributes. */ + for (j = 0; j < eextras; j++) { +#ifdef TRILIBRARY + setelemattribute(triangleloop, j, triangleattriblist[attribindex++]); +#else /* not TRILIBRARY */ + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + setelemattribute(triangleloop, j, 0); + } else { + setelemattribute(triangleloop, j, + (REAL) strtod (stringptr, &stringptr)); + } +#endif /* not TRILIBRARY */ + } + + if (vararea) { +#ifdef TRILIBRARY + area = trianglearealist[elementnumber - firstnumber]; +#else /* not TRILIBRARY */ + /* Read an area constraint from the .area file. */ + stringptr = readline(inputline, areafile, areafilename); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + area = -1.0; /* No constraint on this triangle. */ + } else { + area = (REAL) strtod(stringptr, &stringptr); + } +#endif /* not TRILIBRARY */ + setareabound(triangleloop, area); + } + + /* Set the triangle's vertices. */ + triangleloop.orient = 0; + setorg(triangleloop, getpoint(corner[0])); + setdest(triangleloop, getpoint(corner[1])); + setapex(triangleloop, getpoint(corner[2])); + /* Try linking the triangle to others that share these vertices. */ + for (triangleloop.orient = 0; triangleloop.orient < 3; + triangleloop.orient++) { + /* Take the number for the origin of triangleloop. */ + aroundpoint = corner[triangleloop.orient]; + /* Look for other triangles having this vertex. */ + nexttri = vertexarray[aroundpoint - firstnumber]; + /* Link the current triangle to the next one in the stack. */ + triangleloop.tri[6 + triangleloop.orient] = nexttri; + /* Push the current triangle onto the stack. */ + vertexarray[aroundpoint - firstnumber] = encode(triangleloop); + decode(nexttri, checktri); + if (checktri.tri != dummytri) { + dest(triangleloop, tdest); + apex(triangleloop, tapex); + /* Look for other triangles that share an edge. */ + do { + dest(checktri, checkdest); + apex(checktri, checkapex); + if (tapex == checkdest) { + /* The two triangles share an edge; bond them together. */ + lprev(triangleloop, triangleleft); + bond(triangleleft, checktri); + } + if (tdest == checkapex) { + /* The two triangles share an edge; bond them together. */ + lprev(checktri, checkleft); + bond(triangleloop, checkleft); + } + /* Find the next triangle in the stack. */ + nexttri = checktri.tri[6 + checktri.orient]; + decode(nexttri, checktri); + } while (checktri.tri != dummytri); + } + } + triangleloop.tri = triangletraverse(); + elementnumber++; + } + +#ifdef TRILIBRARY + pointindex = 0; +#else /* not TRILIBRARY */ + fclose(elefile); + if (vararea) { + fclose(areafile); + } +#endif /* not TRILIBRARY */ + + hullsize = 0; /* Prepare to count the boundary edges. */ + if (poly) { + if (verbose) { + printf(" Marking segments in triangulation.\n"); + } + /* Read the segments from the .poly file, and link them */ + /* to their neighboring triangles. */ + boundmarker = 0; + traversalinit(&shelles); + shelleloop.sh = shelletraverse(); + segmentnumber = firstnumber; + while (shelleloop.sh != (shelle *) NULL) { +#ifdef TRILIBRARY + end[0] = segmentlist[pointindex++]; + end[1] = segmentlist[pointindex++]; + if (segmentmarkers) { + boundmarker = segmentmarkerlist[segmentnumber - firstnumber]; + } +#else /* not TRILIBRARY */ + /* Read the endpoints of each segment, and possibly a boundary marker. */ + stringptr = readline(inputline, polyfile, inpolyfilename); + /* Skip the first (segment number) field. */ + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Segment %d has no endpoints in %s.\n", segmentnumber, + polyfilename); + exit(1); + } else { + end[0] = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Segment %d is missing its second endpoint in %s.\n", + segmentnumber, polyfilename); + exit(1); + } else { + end[1] = (int) strtol (stringptr, &stringptr, 0); + } + if (segmentmarkers) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + boundmarker = 0; + } else { + boundmarker = (int) strtol (stringptr, &stringptr, 0); + } + } +#endif /* not TRILIBRARY */ + for (j = 0; j < 2; j++) { + if ((end[j] < firstnumber) || (end[j] >= firstnumber + inpoints)) { + printf("Error: Segment %d has an invalid vertex index.\n", + segmentnumber); + exit(1); + } + } + + /* set the shell edge's vertices. */ + shelleloop.shorient = 0; + setsorg(shelleloop, getpoint(end[0])); + setsdest(shelleloop, getpoint(end[1])); + setmark(shelleloop, boundmarker); + /* Try linking the shell edge to triangles that share these vertices. */ + for (shelleloop.shorient = 0; shelleloop.shorient < 2; + shelleloop.shorient++) { + /* Take the number for the destination of shelleloop. */ + aroundpoint = end[1 - shelleloop.shorient]; + /* Look for triangles having this vertex. */ + prevlink = &vertexarray[aroundpoint - firstnumber]; + nexttri = vertexarray[aroundpoint - firstnumber]; + decode(nexttri, checktri); + sorg(shelleloop, shorg); + notfound = 1; + /* Look for triangles having this edge. Note that I'm only */ + /* comparing each triangle's destination with the shell edge; */ + /* each triangle's apex is handled through a different vertex. */ + /* Because each triangle appears on three vertices' lists, each */ + /* occurrence of a triangle on a list can (and does) represent */ + /* an edge. In this way, most edges are represented twice, and */ + /* every triangle-segment bond is represented once. */ + while (notfound && (checktri.tri != dummytri)) { + dest(checktri, checkdest); + if (shorg == checkdest) { + /* We have a match. Remove this triangle from the list. */ + *prevlink = checktri.tri[6 + checktri.orient]; + /* Bond the shell edge to the triangle. */ + tsbond(checktri, shelleloop); + /* Check if this is a boundary edge. */ + sym(checktri, checkneighbor); + if (checkneighbor.tri == dummytri) { + /* The next line doesn't insert a shell edge (because there's */ + /* already one there), but it sets the boundary markers of */ + /* the existing shell edge and its vertices. */ + insertshelle(&checktri, 1); + hullsize++; + } + notfound = 0; + } + /* Find the next triangle in the stack. */ + prevlink = &checktri.tri[6 + checktri.orient]; + nexttri = checktri.tri[6 + checktri.orient]; + decode(nexttri, checktri); + } + } + shelleloop.sh = shelletraverse(); + segmentnumber++; + } + } + + /* Mark the remaining edges as not being attached to any shell edge. */ + /* Also, count the (yet uncounted) boundary edges. */ + for (i = 0; i < points.items; i++) { + /* Search the stack of triangles adjacent to a point. */ + nexttri = vertexarray[i]; + decode(nexttri, checktri); + while (checktri.tri != dummytri) { + /* Find the next triangle in the stack before this */ + /* information gets overwritten. */ + nexttri = checktri.tri[6 + checktri.orient]; + /* No adjacent shell edge. (This overwrites the stack info.) */ + tsdissolve(checktri); + sym(checktri, checkneighbor); + if (checkneighbor.tri == dummytri) { + insertshelle(&checktri, 1); + hullsize++; + } + decode(nexttri, checktri); + } + } + + free(vertexarray); + return hullsize; +} + +#endif /* not CDT_ONLY */ + +/** **/ +/** **/ +/********* General mesh construction routines end here *********/ + +/********* Segment (shell edge) insertion begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* finddirection() Find the first triangle on the path from one point */ +/* to another. */ +/* */ +/* Finds the triangle that intersects a line segment drawn from the */ +/* origin of `searchtri' to the point `endpoint', and returns the result */ +/* in `searchtri'. The origin of `searchtri' does not change, even though */ +/* the triangle returned may differ from the one passed in. This routine */ +/* is used to find the direction to move in to get from one point to */ +/* another. */ +/* */ +/* The return value notes whether the destination or apex of the found */ +/* triangle is collinear with the two points in question. */ +/* */ +/*****************************************************************************/ + +enum finddirectionresult finddirection(searchtri, endpoint) +struct triedge *searchtri; +point endpoint; +{ + struct triedge checktri; + point startpoint; + point leftpoint, rightpoint; + REAL leftccw, rightccw; + int leftflag, rightflag; + triangle ptr; /* Temporary variable used by onext() and oprev(). */ + + org(*searchtri, startpoint); + dest(*searchtri, rightpoint); + apex(*searchtri, leftpoint); + /* Is `endpoint' to the left? */ + leftccw = counterclockwise(endpoint, startpoint, leftpoint); + leftflag = leftccw > 0.0; + /* Is `endpoint' to the right? */ + rightccw = counterclockwise(startpoint, endpoint, rightpoint); + rightflag = rightccw > 0.0; + if (leftflag && rightflag) { + /* `searchtri' faces directly away from `endpoint'. We could go */ + /* left or right. Ask whether it's a triangle or a boundary */ + /* on the left. */ + onext(*searchtri, checktri); + if (checktri.tri == dummytri) { + leftflag = 0; + } else { + rightflag = 0; + } + } + while (leftflag) { + /* Turn left until satisfied. */ + onextself(*searchtri); + if (searchtri->tri == dummytri) { + printf("Internal error in finddirection(): Unable to find a\n"); + printf(" triangle leading from (%.12g, %.12g) to", startpoint[0], + startpoint[1]); + printf(" (%.12g, %.12g).\n", endpoint[0], endpoint[1]); + internalerror(); + } + apex(*searchtri, leftpoint); + rightccw = leftccw; + leftccw = counterclockwise(endpoint, startpoint, leftpoint); + leftflag = leftccw > 0.0; + } + while (rightflag) { + /* Turn right until satisfied. */ + oprevself(*searchtri); + if (searchtri->tri == dummytri) { + printf("Internal error in finddirection(): Unable to find a\n"); + printf(" triangle leading from (%.12g, %.12g) to", startpoint[0], + startpoint[1]); + printf(" (%.12g, %.12g).\n", endpoint[0], endpoint[1]); + internalerror(); + } + dest(*searchtri, rightpoint); + leftccw = rightccw; + rightccw = counterclockwise(startpoint, endpoint, rightpoint); + rightflag = rightccw > 0.0; + } + if (leftccw == 0.0) { + return LEFTCOLLINEAR; + } else if (rightccw == 0.0) { + return RIGHTCOLLINEAR; + } else { + return WITHIN; + } +} + +/*****************************************************************************/ +/* */ +/* segmentintersection() Find the intersection of an existing segment */ +/* and a segment that is being inserted. Insert */ +/* a point at the intersection, splitting an */ +/* existing shell edge. */ +/* */ +/* The segment being inserted connects the apex of splittri to endpoint2. */ +/* splitshelle is the shell edge being split, and MUST be opposite */ +/* splittri. Hence, the edge being split connects the origin and */ +/* destination of splittri. */ +/* */ +/* On completion, splittri is a handle having the newly inserted */ +/* intersection point as its origin, and endpoint1 as its destination. */ +/* */ +/*****************************************************************************/ + +void segmentintersection(splittri, splitshelle, endpoint2) +struct triedge *splittri; +struct edge *splitshelle; +point endpoint2; +{ + point endpoint1; + point torg, tdest; + point leftpoint, rightpoint; + point newpoint; + enum insertsiteresult success; + enum finddirectionresult collinear; + REAL ex, ey; + REAL tx, ty; + REAL etx, ety; + REAL split, denom; + int i; + triangle ptr; /* Temporary variable used by onext(). */ + + /* Find the other three segment endpoints. */ + apex(*splittri, endpoint1); + org(*splittri, torg); + dest(*splittri, tdest); + /* Segment intersection formulae; see the Antonio reference. */ + tx = tdest[0] - torg[0]; + ty = tdest[1] - torg[1]; + ex = endpoint2[0] - endpoint1[0]; + ey = endpoint2[1] - endpoint1[1]; + etx = torg[0] - endpoint2[0]; + ety = torg[1] - endpoint2[1]; + denom = ty * ex - tx * ey; + if (denom == 0.0) { + printf("Internal error in segmentintersection():"); + printf(" Attempt to find intersection of parallel segments.\n"); + internalerror(); + } + split = (ey * etx - ex * ety) / denom; + /* Create the new point. */ + newpoint = (point) poolalloc(&points); + /* Interpolate its coordinate and attributes. */ + for (i = 0; i < 2 + nextras; i++) { + newpoint[i] = torg[i] + split * (tdest[i] - torg[i]); + } + setpointmark(newpoint, mark(*splitshelle)); + if (verbose > 1) { + printf( + " Splitting edge (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n", + torg[0], torg[1], tdest[0], tdest[1], newpoint[0], newpoint[1]); + } + /* Insert the intersection point. This should always succeed. */ + success = insertsite(newpoint, splittri, splitshelle, 0, 0); + if (success != SUCCESSFULPOINT) { + printf("Internal error in segmentintersection():\n"); + printf(" Failure to split a segment.\n"); + internalerror(); + } + if (steinerleft > 0) { + steinerleft--; + } + /* Inserting the point may have caused edge flips. We wish to rediscover */ + /* the edge connecting endpoint1 to the new intersection point. */ + collinear = finddirection(splittri, endpoint1); + dest(*splittri, rightpoint); + apex(*splittri, leftpoint); + if ((leftpoint[0] == endpoint1[0]) && (leftpoint[1] == endpoint1[1])) { + onextself(*splittri); + } else if ((rightpoint[0] != endpoint1[0]) || + (rightpoint[1] != endpoint1[1])) { + printf("Internal error in segmentintersection():\n"); + printf(" Topological inconsistency after splitting a segment.\n"); + internalerror(); + } + /* `splittri' should have destination endpoint1. */ +} + +/*****************************************************************************/ +/* */ +/* scoutsegment() Scout the first triangle on the path from one endpoint */ +/* to another, and check for completion (reaching the */ +/* second endpoint), a collinear point, and the */ +/* intersection of two segments. */ +/* */ +/* Returns one if the entire segment is successfully inserted, and zero if */ +/* the job must be finished by conformingedge() or constrainededge(). */ +/* */ +/* If the first triangle on the path has the second endpoint as its */ +/* destination or apex, a shell edge is inserted and the job is done. */ +/* */ +/* If the first triangle on the path has a destination or apex that lies on */ +/* the segment, a shell edge is inserted connecting the first endpoint to */ +/* the collinear point, and the search is continued from the collinear */ +/* point. */ +/* */ +/* If the first triangle on the path has a shell edge opposite its origin, */ +/* then there is a segment that intersects the segment being inserted. */ +/* Their intersection point is inserted, splitting the shell edge. */ +/* */ +/* Otherwise, return zero. */ +/* */ +/*****************************************************************************/ + +int scoutsegment(searchtri, endpoint2, newmark) +struct triedge *searchtri; +point endpoint2; +int newmark; +{ + struct triedge crosstri; + struct edge crossedge; + point leftpoint, rightpoint; + point endpoint1; + enum finddirectionresult collinear; + shelle sptr; /* Temporary variable used by tspivot(). */ + + collinear = finddirection(searchtri, endpoint2); + dest(*searchtri, rightpoint); + apex(*searchtri, leftpoint); + if (((leftpoint[0] == endpoint2[0]) && (leftpoint[1] == endpoint2[1])) || + ((rightpoint[0] == endpoint2[0]) && (rightpoint[1] == endpoint2[1]))) { + /* The segment is already an edge in the mesh. */ + if ((leftpoint[0] == endpoint2[0]) && (leftpoint[1] == endpoint2[1])) { + lprevself(*searchtri); + } + /* Insert a shell edge, if there isn't already one there. */ + insertshelle(searchtri, newmark); + return 1; + } else if (collinear == LEFTCOLLINEAR) { + /* We've collided with a point between the segment's endpoints. */ + /* Make the collinear point be the triangle's origin. */ + lprevself(*searchtri); + insertshelle(searchtri, newmark); + /* Insert the remainder of the segment. */ + return scoutsegment(searchtri, endpoint2, newmark); + } else if (collinear == RIGHTCOLLINEAR) { + /* We've collided with a point between the segment's endpoints. */ + insertshelle(searchtri, newmark); + /* Make the collinear point be the triangle's origin. */ + lnextself(*searchtri); + /* Insert the remainder of the segment. */ + return scoutsegment(searchtri, endpoint2, newmark); + } else { + lnext(*searchtri, crosstri); + tspivot(crosstri, crossedge); + /* Check for a crossing segment. */ + if (crossedge.sh == dummysh) { + return 0; + } else { + org(*searchtri, endpoint1); + /* Insert a point at the intersection. */ + segmentintersection(&crosstri, &crossedge, endpoint2); + triedgecopy(crosstri, *searchtri); + insertshelle(searchtri, newmark); + /* Insert the remainder of the segment. */ + return scoutsegment(searchtri, endpoint2, newmark); + } + } +} + +/*****************************************************************************/ +/* */ +/* conformingedge() Force a segment into a conforming Delaunay */ +/* triangulation by inserting a point at its midpoint, */ +/* and recursively forcing in the two half-segments if */ +/* necessary. */ +/* */ +/* Generates a sequence of edges connecting `endpoint1' to `endpoint2'. */ +/* `newmark' is the boundary marker of the segment, assigned to each new */ +/* splitting point and shell edge. */ +/* */ +/* Note that conformingedge() does not always maintain the conforming */ +/* Delaunay property. Once inserted, segments are locked into place; */ +/* points inserted later (to force other segments in) may render these */ +/* fixed segments non-Delaunay. The conforming Delaunay property will be */ +/* restored by enforcequality() by splitting encroached segments. */ +/* */ +/*****************************************************************************/ + +#ifndef REDUCED +#ifndef CDT_ONLY + +void conformingedge(endpoint1, endpoint2, newmark) +point endpoint1; +point endpoint2; +int newmark; +{ + struct triedge searchtri1, searchtri2; + struct edge brokenshelle; + point newpoint; + point midpoint1, midpoint2; + enum insertsiteresult success; + int result1, result2; + int i; + shelle sptr; /* Temporary variable used by tspivot(). */ + + if (verbose > 2) { + printf("Forcing segment into triangulation by recursive splitting:\n"); + printf(" (%.12g, %.12g) (%.12g, %.12g)\n", endpoint1[0], endpoint1[1], + endpoint2[0], endpoint2[1]); + } + /* Create a new point to insert in the middle of the segment. */ + newpoint = (point) poolalloc(&points); + /* Interpolate coordinates and attributes. */ + for (i = 0; i < 2 + nextras; i++) { + newpoint[i] = 0.5 * (endpoint1[i] + endpoint2[i]); + } + setpointmark(newpoint, newmark); + /* Find a boundary triangle to search from. */ + searchtri1.tri = (triangle *) NULL; + /* Attempt to insert the new point. */ + success = insertsite(newpoint, &searchtri1, (struct edge *) NULL, 0, 0); + if (success == DUPLICATEPOINT) { + if (verbose > 2) { + printf(" Segment intersects existing point (%.12g, %.12g).\n", + newpoint[0], newpoint[1]); + } + /* Use the point that's already there. */ + pointdealloc(newpoint); + org(searchtri1, newpoint); + } else { + if (success == VIOLATINGPOINT) { + if (verbose > 2) { + printf(" Two segments intersect at (%.12g, %.12g).\n", + newpoint[0], newpoint[1]); + } + /* By fluke, we've landed right on another segment. Split it. */ + tspivot(searchtri1, brokenshelle); + success = insertsite(newpoint, &searchtri1, &brokenshelle, 0, 0); + if (success != SUCCESSFULPOINT) { + printf("Internal error in conformingedge():\n"); + printf(" Failure to split a segment.\n"); + internalerror(); + } + } + /* The point has been inserted successfully. */ + if (steinerleft > 0) { + steinerleft--; + } + } + triedgecopy(searchtri1, searchtri2); + result1 = scoutsegment(&searchtri1, endpoint1, newmark); + result2 = scoutsegment(&searchtri2, endpoint2, newmark); + if (!result1) { + /* The origin of searchtri1 may have changed if a collision with an */ + /* intervening vertex on the segment occurred. */ + org(searchtri1, midpoint1); + conformingedge(midpoint1, endpoint1, newmark); + } + if (!result2) { + /* The origin of searchtri2 may have changed if a collision with an */ + /* intervening vertex on the segment occurred. */ + org(searchtri2, midpoint2); + conformingedge(midpoint2, endpoint2, newmark); + } +} + +#endif /* not CDT_ONLY */ +#endif /* not REDUCED */ + +/*****************************************************************************/ +/* */ +/* delaunayfixup() Enforce the Delaunay condition at an edge, fanning out */ +/* recursively from an existing point. Pay special */ +/* attention to stacking inverted triangles. */ +/* */ +/* This is a support routine for inserting segments into a constrained */ +/* Delaunay triangulation. */ +/* */ +/* The origin of fixuptri is treated as if it has just been inserted, and */ +/* the local Delaunay condition needs to be enforced. It is only enforced */ +/* in one sector, however, that being the angular range defined by */ +/* fixuptri. */ +/* */ +/* This routine also needs to make decisions regarding the "stacking" of */ +/* triangles. (Read the description of constrainededge() below before */ +/* reading on here, so you understand the algorithm.) If the position of */ +/* the new point (the origin of fixuptri) indicates that the vertex before */ +/* it on the polygon is a reflex vertex, then "stack" the triangle by */ +/* doing nothing. (fixuptri is an inverted triangle, which is how stacked */ +/* triangles are identified.) */ +/* */ +/* Otherwise, check whether the vertex before that was a reflex vertex. */ +/* If so, perform an edge flip, thereby eliminating an inverted triangle */ +/* (popping it off the stack). The edge flip may result in the creation */ +/* of a new inverted triangle, depending on whether or not the new vertex */ +/* is visible to the vertex three edges behind on the polygon. */ +/* */ +/* If neither of the two vertices behind the new vertex are reflex */ +/* vertices, fixuptri and fartri, the triangle opposite it, are not */ +/* inverted; hence, ensure that the edge between them is locally Delaunay. */ +/* */ +/* `leftside' indicates whether or not fixuptri is to the left of the */ +/* segment being inserted. (Imagine that the segment is pointing up from */ +/* endpoint1 to endpoint2.) */ +/* */ +/*****************************************************************************/ + +void delaunayfixup(fixuptri, leftside) +struct triedge *fixuptri; +int leftside; +{ + struct triedge neartri; + struct triedge fartri; + struct edge faredge; + point nearpoint, leftpoint, rightpoint, farpoint; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + lnext(*fixuptri, neartri); + sym(neartri, fartri); + /* Check if the edge opposite the origin of fixuptri can be flipped. */ + if (fartri.tri == dummytri) { + return; + } + tspivot(neartri, faredge); + if (faredge.sh != dummysh) { + return; + } + /* Find all the relevant vertices. */ + apex(neartri, nearpoint); + org(neartri, leftpoint); + dest(neartri, rightpoint); + apex(fartri, farpoint); + /* Check whether the previous polygon vertex is a reflex vertex. */ + if (leftside) { + if (counterclockwise(nearpoint, leftpoint, farpoint) <= 0.0) { + /* leftpoint is a reflex vertex too. Nothing can */ + /* be done until a convex section is found. */ + return; + } + } else { + if (counterclockwise(farpoint, rightpoint, nearpoint) <= 0.0) { + /* rightpoint is a reflex vertex too. Nothing can */ + /* be done until a convex section is found. */ + return; + } + } + if (counterclockwise(rightpoint, leftpoint, farpoint) > 0.0) { + /* fartri is not an inverted triangle, and farpoint is not a reflex */ + /* vertex. As there are no reflex vertices, fixuptri isn't an */ + /* inverted triangle, either. Hence, test the edge between the */ + /* triangles to ensure it is locally Delaunay. */ + if (incircle(leftpoint, farpoint, rightpoint, nearpoint) <= 0.0) { + return; + } + /* Not locally Delaunay; go on to an edge flip. */ + } /* else fartri is inverted; remove it from the stack by flipping. */ + flip(&neartri); + lprevself(*fixuptri); /* Restore the origin of fixuptri after the flip. */ + /* Recursively process the two triangles that result from the flip. */ + delaunayfixup(fixuptri, leftside); + delaunayfixup(&fartri, leftside); +} + +/*****************************************************************************/ +/* */ +/* constrainededge() Force a segment into a constrained Delaunay */ +/* triangulation by deleting the triangles it */ +/* intersects, and triangulating the polygons that */ +/* form on each side of it. */ +/* */ +/* Generates a single edge connecting `endpoint1' to `endpoint2'. The */ +/* triangle `starttri' has `endpoint1' as its origin. `newmark' is the */ +/* boundary marker of the segment. */ +/* */ +/* To insert a segment, every triangle whose interior intersects the */ +/* segment is deleted. The union of these deleted triangles is a polygon */ +/* (which is not necessarily monotone, but is close enough), which is */ +/* divided into two polygons by the new segment. This routine's task is */ +/* to generate the Delaunay triangulation of these two polygons. */ +/* */ +/* You might think of this routine's behavior as a two-step process. The */ +/* first step is to walk from endpoint1 to endpoint2, flipping each edge */ +/* encountered. This step creates a fan of edges connected to endpoint1, */ +/* including the desired edge to endpoint2. The second step enforces the */ +/* Delaunay condition on each side of the segment in an incremental manner: */ +/* proceeding along the polygon from endpoint1 to endpoint2 (this is done */ +/* independently on each side of the segment), each vertex is "enforced" */ +/* as if it had just been inserted, but affecting only the previous */ +/* vertices. The result is the same as if the vertices had been inserted */ +/* in the order they appear on the polygon, so the result is Delaunay. */ +/* */ +/* In truth, constrainededge() interleaves these two steps. The procedure */ +/* walks from endpoint1 to endpoint2, and each time an edge is encountered */ +/* and flipped, the newly exposed vertex (at the far end of the flipped */ +/* edge) is "enforced" upon the previously flipped edges, usually affecting */ +/* only one side of the polygon (depending upon which side of the segment */ +/* the vertex falls on). */ +/* */ +/* The algorithm is complicated by the need to handle polygons that are not */ +/* convex. Although the polygon is not necessarily monotone, it can be */ +/* triangulated in a manner similar to the stack-based algorithms for */ +/* monotone polygons. For each reflex vertex (local concavity) of the */ +/* polygon, there will be an inverted triangle formed by one of the edge */ +/* flips. (An inverted triangle is one with negative area - that is, its */ +/* vertices are arranged in clockwise order - and is best thought of as a */ +/* wrinkle in the fabric of the mesh.) Each inverted triangle can be */ +/* thought of as a reflex vertex pushed on the stack, waiting to be fixed */ +/* later. */ +/* */ +/* A reflex vertex is popped from the stack when a vertex is inserted that */ +/* is visible to the reflex vertex. (However, if the vertex behind the */ +/* reflex vertex is not visible to the reflex vertex, a new inverted */ +/* triangle will take its place on the stack.) These details are handled */ +/* by the delaunayfixup() routine above. */ +/* */ +/*****************************************************************************/ + +void constrainededge(starttri, endpoint2, newmark) +struct triedge *starttri; +point endpoint2; +int newmark; +{ + struct triedge fixuptri, fixuptri2; + struct edge fixupedge; + point endpoint1; + point farpoint; + REAL area; + int collision; + int done; + triangle ptr; /* Temporary variable used by sym() and oprev(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + org(*starttri, endpoint1); + lnext(*starttri, fixuptri); + flip(&fixuptri); + /* `collision' indicates whether we have found a point directly */ + /* between endpoint1 and endpoint2. */ + collision = 0; + done = 0; + do { + org(fixuptri, farpoint); + /* `farpoint' is the extreme point of the polygon we are "digging" */ + /* to get from endpoint1 to endpoint2. */ + if ((farpoint[0] == endpoint2[0]) && (farpoint[1] == endpoint2[1])) { + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around endpoint2. */ + delaunayfixup(&fixuptri, 0); + delaunayfixup(&fixuptri2, 1); + done = 1; + } else { + /* Check whether farpoint is to the left or right of the segment */ + /* being inserted, to decide which edge of fixuptri to dig */ + /* through next. */ + area = counterclockwise(endpoint1, endpoint2, farpoint); + if (area == 0.0) { + /* We've collided with a point between endpoint1 and endpoint2. */ + collision = 1; + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around farpoint. */ + delaunayfixup(&fixuptri, 0); + delaunayfixup(&fixuptri2, 1); + done = 1; + } else { + if (area > 0.0) { /* farpoint is to the left of the segment. */ + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around farpoint, on the */ + /* left side of the segment only. */ + delaunayfixup(&fixuptri2, 1); + /* Flip the edge that crosses the segment. After the edge is */ + /* flipped, one of its endpoints is the fan vertex, and the */ + /* destination of fixuptri is the fan vertex. */ + lprevself(fixuptri); + } else { /* farpoint is to the right of the segment. */ + delaunayfixup(&fixuptri, 0); + /* Flip the edge that crosses the segment. After the edge is */ + /* flipped, one of its endpoints is the fan vertex, and the */ + /* destination of fixuptri is the fan vertex. */ + oprevself(fixuptri); + } + /* Check for two intersecting segments. */ + tspivot(fixuptri, fixupedge); + if (fixupedge.sh == dummysh) { + flip(&fixuptri); /* May create an inverted triangle on the left. */ + } else { + /* We've collided with a segment between endpoint1 and endpoint2. */ + collision = 1; + /* Insert a point at the intersection. */ + segmentintersection(&fixuptri, &fixupedge, endpoint2); + done = 1; + } + } + } + } while (!done); + /* Insert a shell edge to make the segment permanent. */ + insertshelle(&fixuptri, newmark); + /* If there was a collision with an interceding vertex, install another */ + /* segment connecting that vertex with endpoint2. */ + if (collision) { + /* Insert the remainder of the segment. */ + if (!scoutsegment(&fixuptri, endpoint2, newmark)) { + constrainededge(&fixuptri, endpoint2, newmark); + } + } +} + +/*****************************************************************************/ +/* */ +/* insertsegment() Insert a PSLG segment into a triangulation. */ +/* */ +/*****************************************************************************/ + +void insertsegment(endpoint1, endpoint2, newmark) +point endpoint1; +point endpoint2; +int newmark; +{ + struct triedge searchtri1, searchtri2; + triangle encodedtri; + point checkpoint; + triangle ptr; /* Temporary variable used by sym(). */ + + if (verbose > 1) { + printf(" Connecting (%.12g, %.12g) to (%.12g, %.12g).\n", + endpoint1[0], endpoint1[1], endpoint2[0], endpoint2[1]); + } + + /* Find a triangle whose origin is the segment's first endpoint. */ + checkpoint = (point) NULL; + encodedtri = point2tri(endpoint1); + if (encodedtri != (triangle) NULL) { + decode(encodedtri, searchtri1); + org(searchtri1, checkpoint); + } + if (checkpoint != endpoint1) { + /* Find a boundary triangle to search from. */ + searchtri1.tri = dummytri; + searchtri1.orient = 0; + symself(searchtri1); + /* Search for the segment's first endpoint by point location. */ + if (locate(endpoint1, &searchtri1) != ONVERTEX) { + printf( + "Internal error in insertsegment(): Unable to locate PSLG point\n"); + printf(" (%.12g, %.12g) in triangulation.\n", + endpoint1[0], endpoint1[1]); + internalerror(); + } + } + /* Remember this triangle to improve subsequent point location. */ + triedgecopy(searchtri1, recenttri); + /* Scout the beginnings of a path from the first endpoint */ + /* toward the second. */ + if (scoutsegment(&searchtri1, endpoint2, newmark)) { + /* The segment was easily inserted. */ + return; + } + /* The first endpoint may have changed if a collision with an intervening */ + /* vertex on the segment occurred. */ + org(searchtri1, endpoint1); + + /* Find a triangle whose origin is the segment's second endpoint. */ + checkpoint = (point) NULL; + encodedtri = point2tri(endpoint2); + if (encodedtri != (triangle) NULL) { + decode(encodedtri, searchtri2); + org(searchtri2, checkpoint); + } + if (checkpoint != endpoint2) { + /* Find a boundary triangle to search from. */ + searchtri2.tri = dummytri; + searchtri2.orient = 0; + symself(searchtri2); + /* Search for the segment's second endpoint by point location. */ + if (locate(endpoint2, &searchtri2) != ONVERTEX) { + printf( + "Internal error in insertsegment(): Unable to locate PSLG point\n"); + printf(" (%.12g, %.12g) in triangulation.\n", + endpoint2[0], endpoint2[1]); + internalerror(); + } + } + /* Remember this triangle to improve subsequent point location. */ + triedgecopy(searchtri2, recenttri); + /* Scout the beginnings of a path from the second endpoint */ + /* toward the first. */ + if (scoutsegment(&searchtri2, endpoint1, newmark)) { + /* The segment was easily inserted. */ + return; + } + /* The second endpoint may have changed if a collision with an intervening */ + /* vertex on the segment occurred. */ + org(searchtri2, endpoint2); + +#ifndef REDUCED +#ifndef CDT_ONLY + if (splitseg) { + /* Insert vertices to force the segment into the triangulation. */ + conformingedge(endpoint1, endpoint2, newmark); + } else { +#endif /* not CDT_ONLY */ +#endif /* not REDUCED */ + /* Insert the segment directly into the triangulation. */ + constrainededge(&searchtri1, endpoint2, newmark); +#ifndef REDUCED +#ifndef CDT_ONLY + } +#endif /* not CDT_ONLY */ +#endif /* not REDUCED */ +} + +/*****************************************************************************/ +/* */ +/* markhull() Cover the convex hull of a triangulation with shell edges. */ +/* */ +/*****************************************************************************/ + +void markhull() +{ + struct triedge hulltri; + struct triedge nexttri; + struct triedge starttri; + triangle ptr; /* Temporary variable used by sym() and oprev(). */ + + /* Find a triangle handle on the hull. */ + hulltri.tri = dummytri; + hulltri.orient = 0; + symself(hulltri); + /* Remember where we started so we know when to stop. */ + triedgecopy(hulltri, starttri); + /* Go once counterclockwise around the convex hull. */ + do { + /* Create a shell edge if there isn't already one here. */ + insertshelle(&hulltri, 1); + /* To find the next hull edge, go clockwise around the next vertex. */ + lnextself(hulltri); + oprev(hulltri, nexttri); + while (nexttri.tri != dummytri) { + triedgecopy(nexttri, hulltri); + oprev(hulltri, nexttri); + } + } while (!triedgeequal(hulltri, starttri)); +} + +/*****************************************************************************/ +/* */ +/* formskeleton() Create the shell edges of a triangulation, including */ +/* PSLG edges and edges on the convex hull. */ +/* */ +/* The PSLG edges are read from a .poly file. The return value is the */ +/* number of segments in the file. */ +/* */ +/*****************************************************************************/ + +#ifdef TRILIBRARY + +int formskeleton(segmentlist, segmentmarkerlist, numberofsegments) +int *segmentlist; +int *segmentmarkerlist; +int numberofsegments; + +#else /* not TRILIBRARY */ + +int formskeleton(polyfile, polyfilename) +FILE *polyfile; +char *polyfilename; + +#endif /* not TRILIBRARY */ + +{ +#ifdef TRILIBRARY + char polyfilename[6]; + int index; +#else /* not TRILIBRARY */ + char inputline[INPUTLINESIZE]; + char *stringptr; +#endif /* not TRILIBRARY */ + point endpoint1, endpoint2; + int segments; + int segmentmarkers; + int end1, end2; + int boundmarker; + int i; + + if (poly) { + if (!quiet) { + printf("Inserting segments into Delaunay triangulation.\n"); + } +#ifdef TRILIBRARY + strcpy(polyfilename, "input"); + segments = numberofsegments; + segmentmarkers = segmentmarkerlist != (int *) NULL; + index = 0; +#else /* not TRILIBRARY */ + /* Read the segments from a .poly file. */ + /* Read number of segments and number of boundary markers. */ + stringptr = readline(inputline, polyfile, polyfilename); + segments = (int) strtol (stringptr, &stringptr, 0); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + segmentmarkers = 0; + } else { + segmentmarkers = (int) strtol (stringptr, &stringptr, 0); + } +#endif /* not TRILIBRARY */ + /* If segments are to be inserted, compute a mapping */ + /* from points to triangles. */ + if (segments > 0) { + if (verbose) { + printf(" Inserting PSLG segments.\n"); + } + makepointmap(); + } + + boundmarker = 0; + /* Read and insert the segments. */ + for (i = 1; i <= segments; i++) { +#ifdef TRILIBRARY + end1 = segmentlist[index++]; + end2 = segmentlist[index++]; + if (segmentmarkers) { + boundmarker = segmentmarkerlist[i - 1]; + } +#else /* not TRILIBRARY */ + stringptr = readline(inputline, polyfile, inpolyfilename); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Segment %d has no endpoints in %s.\n", i, + polyfilename); + exit(1); + } else { + end1 = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Segment %d is missing its second endpoint in %s.\n", i, + polyfilename); + exit(1); + } else { + end2 = (int) strtol (stringptr, &stringptr, 0); + } + if (segmentmarkers) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + boundmarker = 0; + } else { + boundmarker = (int) strtol (stringptr, &stringptr, 0); + } + } +#endif /* not TRILIBRARY */ + if ((end1 < firstnumber) || (end1 >= firstnumber + inpoints)) { + if (!quiet) { + printf("Warning: Invalid first endpoint of segment %d in %s.\n", i, + polyfilename); + } + } else if ((end2 < firstnumber) || (end2 >= firstnumber + inpoints)) { + if (!quiet) { + printf("Warning: Invalid second endpoint of segment %d in %s.\n", i, + polyfilename); + } + } else { + endpoint1 = getpoint(end1); + endpoint2 = getpoint(end2); + if ((endpoint1[0] == endpoint2[0]) && (endpoint1[1] == endpoint2[1])) { + if (!quiet) { + printf("Warning: Endpoints of segment %d are coincident in %s.\n", + i, polyfilename); + } + } else { + insertsegment(endpoint1, endpoint2, boundmarker); + } + } + } + } else { + segments = 0; + } + if (convex || !poly) { + /* Enclose the convex hull with shell edges. */ + if (verbose) { + printf(" Enclosing convex hull with segments.\n"); + } + markhull(); + } + return segments; +} + +/** **/ +/** **/ +/********* Segment (shell edge) insertion ends here *********/ + +/********* Carving out holes and concavities begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* infecthull() Virally infect all of the triangles of the convex hull */ +/* that are not protected by shell edges. Where there are */ +/* shell edges, set boundary markers as appropriate. */ +/* */ +/*****************************************************************************/ + +void infecthull() +{ + struct triedge hulltri; + struct triedge nexttri; + struct triedge starttri; + struct edge hulledge; + triangle **deadtri; + point horg, hdest; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + if (verbose) { + printf(" Marking concavities (external triangles) for elimination.\n"); + } + /* Find a triangle handle on the hull. */ + hulltri.tri = dummytri; + hulltri.orient = 0; + symself(hulltri); + /* Remember where we started so we know when to stop. */ + triedgecopy(hulltri, starttri); + /* Go once counterclockwise around the convex hull. */ + do { + /* Ignore triangles that are already infected. */ + if (!infected(hulltri)) { + /* Is the triangle protected by a shell edge? */ + tspivot(hulltri, hulledge); + if (hulledge.sh == dummysh) { + /* The triangle is not protected; infect it. */ + infect(hulltri); + deadtri = (triangle **) poolalloc(&viri); + *deadtri = hulltri.tri; + } else { + /* The triangle is protected; set boundary markers if appropriate. */ + if (mark(hulledge) == 0) { + setmark(hulledge, 1); + org(hulltri, horg); + dest(hulltri, hdest); + if (pointmark(horg) == 0) { + setpointmark(horg, 1); + } + if (pointmark(hdest) == 0) { + setpointmark(hdest, 1); + } + } + } + } + /* To find the next hull edge, go clockwise around the next vertex. */ + lnextself(hulltri); + oprev(hulltri, nexttri); + while (nexttri.tri != dummytri) { + triedgecopy(nexttri, hulltri); + oprev(hulltri, nexttri); + } + } while (!triedgeequal(hulltri, starttri)); +} + +/*****************************************************************************/ +/* */ +/* plague() Spread the virus from all infected triangles to any neighbors */ +/* not protected by shell edges. Delete all infected triangles. */ +/* */ +/* This is the procedure that actually creates holes and concavities. */ +/* */ +/* This procedure operates in two phases. The first phase identifies all */ +/* the triangles that will die, and marks them as infected. They are */ +/* marked to ensure that each triangle is added to the virus pool only */ +/* once, so the procedure will terminate. */ +/* */ +/* The second phase actually eliminates the infected triangles. It also */ +/* eliminates orphaned points. */ +/* */ +/*****************************************************************************/ + +void plague() +{ + struct triedge testtri; + struct triedge neighbor; + triangle **virusloop; + triangle **deadtri; + struct edge neighborshelle; + point testpoint; + point norg, ndest; + point deadorg, deaddest, deadapex; + int killorg; + triangle ptr; /* Temporary variable used by sym() and onext(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + if (verbose) { + printf(" Marking neighbors of marked triangles.\n"); + } + /* Loop through all the infected triangles, spreading the virus to */ + /* their neighbors, then to their neighbors' neighbors. */ + traversalinit(&viri); + virusloop = (triangle **) traverse(&viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + /* A triangle is marked as infected by messing with one of its shell */ + /* edges, setting it to an illegal value. Hence, we have to */ + /* temporarily uninfect this triangle so that we can examine its */ + /* adjacent shell edges. */ + uninfect(testtri); + if (verbose > 2) { + /* Assign the triangle an orientation for convenience in */ + /* checking its points. */ + testtri.orient = 0; + org(testtri, deadorg); + dest(testtri, deaddest); + apex(testtri, deadapex); + printf(" Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + deadorg[0], deadorg[1], deaddest[0], deaddest[1], + deadapex[0], deadapex[1]); + } + /* Check each of the triangle's three neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + /* Find the neighbor. */ + sym(testtri, neighbor); + /* Check for a shell between the triangle and its neighbor. */ + tspivot(testtri, neighborshelle); + /* Check if the neighbor is nonexistent or already infected. */ + if ((neighbor.tri == dummytri) || infected(neighbor)) { + if (neighborshelle.sh != dummysh) { + /* There is a shell edge separating the triangle from its */ + /* neighbor, but both triangles are dying, so the shell */ + /* edge dies too. */ + shelledealloc(neighborshelle.sh); + if (neighbor.tri != dummytri) { + /* Make sure the shell edge doesn't get deallocated again */ + /* later when the infected neighbor is visited. */ + uninfect(neighbor); + tsdissolve(neighbor); + infect(neighbor); + } + } + } else { /* The neighbor exists and is not infected. */ + if (neighborshelle.sh == dummysh) { + /* There is no shell edge protecting the neighbor, so */ + /* the neighbor becomes infected. */ + if (verbose > 2) { + org(neighbor, deadorg); + dest(neighbor, deaddest); + apex(neighbor, deadapex); + printf( + " Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + deadorg[0], deadorg[1], deaddest[0], deaddest[1], + deadapex[0], deadapex[1]); + } + infect(neighbor); + /* Ensure that the neighbor's neighbors will be infected. */ + deadtri = (triangle **) poolalloc(&viri); + *deadtri = neighbor.tri; + } else { /* The neighbor is protected by a shell edge. */ + /* Remove this triangle from the shell edge. */ + stdissolve(neighborshelle); + /* The shell edge becomes a boundary. Set markers accordingly. */ + if (mark(neighborshelle) == 0) { + setmark(neighborshelle, 1); + } + org(neighbor, norg); + dest(neighbor, ndest); + if (pointmark(norg) == 0) { + setpointmark(norg, 1); + } + if (pointmark(ndest) == 0) { + setpointmark(ndest, 1); + } + } + } + } + /* Remark the triangle as infected, so it doesn't get added to the */ + /* virus pool again. */ + infect(testtri); + virusloop = (triangle **) traverse(&viri); + } + + if (verbose) { + printf(" Deleting marked triangles.\n"); + } + traversalinit(&viri); + virusloop = (triangle **) traverse(&viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + + /* Check each of the three corners of the triangle for elimination. */ + /* This is done by walking around each point, checking if it is */ + /* still connected to at least one live triangle. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + org(testtri, testpoint); + /* Check if the point has already been tested. */ + if (testpoint != (point) NULL) { + killorg = 1; + /* Mark the corner of the triangle as having been tested. */ + setorg(testtri, NULL); + /* Walk counterclockwise about the point. */ + onext(testtri, neighbor); + /* Stop upon reaching a boundary or the starting triangle. */ + while ((neighbor.tri != dummytri) + && (!triedgeequal(neighbor, testtri))) { + if (infected(neighbor)) { + /* Mark the corner of this triangle as having been tested. */ + setorg(neighbor, NULL); + } else { + /* A live triangle. The point survives. */ + killorg = 0; + } + /* Walk counterclockwise about the point. */ + onextself(neighbor); + } + /* If we reached a boundary, we must walk clockwise as well. */ + if (neighbor.tri == dummytri) { + /* Walk clockwise about the point. */ + oprev(testtri, neighbor); + /* Stop upon reaching a boundary. */ + while (neighbor.tri != dummytri) { + if (infected(neighbor)) { + /* Mark the corner of this triangle as having been tested. */ + setorg(neighbor, NULL); + } else { + /* A live triangle. The point survives. */ + killorg = 0; + } + /* Walk clockwise about the point. */ + oprevself(neighbor); + } + } + if (killorg) { + if (verbose > 1) { + printf(" Deleting point (%.12g, %.12g)\n", + testpoint[0], testpoint[1]); + } + pointdealloc(testpoint); + } + } + } + + /* Record changes in the number of boundary edges, and disconnect */ + /* dead triangles from their neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + sym(testtri, neighbor); + if (neighbor.tri == dummytri) { + /* There is no neighboring triangle on this edge, so this edge */ + /* is a boundary edge. This triangle is being deleted, so this */ + /* boundary edge is deleted. */ + hullsize--; + } else { + /* Disconnect the triangle from its neighbor. */ + dissolve(neighbor); + /* There is a neighboring triangle on this edge, so this edge */ + /* becomes a boundary edge when this triangle is deleted. */ + hullsize++; + } + } + /* Return the dead triangle to the pool of triangles. */ + triangledealloc(testtri.tri); + virusloop = (triangle **) traverse(&viri); + } + /* Empty the virus pool. */ + poolrestart(&viri); +} + +/*****************************************************************************/ +/* */ +/* regionplague() Spread regional attributes and/or area constraints */ +/* (from a .poly file) throughout the mesh. */ +/* */ +/* This procedure operates in two phases. The first phase spreads an */ +/* attribute and/or an area constraint through a (segment-bounded) region. */ +/* The triangles are marked to ensure that each triangle is added to the */ +/* virus pool only once, so the procedure will terminate. */ +/* */ +/* The second phase uninfects all infected triangles, returning them to */ +/* normal. */ +/* */ +/*****************************************************************************/ + +void regionplague(attribute, area) +REAL attribute; +REAL area; +{ + struct triedge testtri; + struct triedge neighbor; + triangle **virusloop; + triangle **regiontri; + struct edge neighborshelle; + point regionorg, regiondest, regionapex; + triangle ptr; /* Temporary variable used by sym() and onext(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + if (verbose > 1) { + printf(" Marking neighbors of marked triangles.\n"); + } + /* Loop through all the infected triangles, spreading the attribute */ + /* and/or area constraint to their neighbors, then to their neighbors' */ + /* neighbors. */ + traversalinit(&viri); + virusloop = (triangle **) traverse(&viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + /* A triangle is marked as infected by messing with one of its shell */ + /* edges, setting it to an illegal value. Hence, we have to */ + /* temporarily uninfect this triangle so that we can examine its */ + /* adjacent shell edges. */ + uninfect(testtri); + if (regionattrib) { + /* Set an attribute. */ + setelemattribute(testtri, eextras, attribute); + } + if (vararea) { + /* Set an area constraint. */ + setareabound(testtri, area); + } + if (verbose > 2) { + /* Assign the triangle an orientation for convenience in */ + /* checking its points. */ + testtri.orient = 0; + org(testtri, regionorg); + dest(testtri, regiondest); + apex(testtri, regionapex); + printf(" Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + regionorg[0], regionorg[1], regiondest[0], regiondest[1], + regionapex[0], regionapex[1]); + } + /* Check each of the triangle's three neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + /* Find the neighbor. */ + sym(testtri, neighbor); + /* Check for a shell between the triangle and its neighbor. */ + tspivot(testtri, neighborshelle); + /* Make sure the neighbor exists, is not already infected, and */ + /* isn't protected by a shell edge. */ + if ((neighbor.tri != dummytri) && !infected(neighbor) + && (neighborshelle.sh == dummysh)) { + if (verbose > 2) { + org(neighbor, regionorg); + dest(neighbor, regiondest); + apex(neighbor, regionapex); + printf(" Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + regionorg[0], regionorg[1], regiondest[0], regiondest[1], + regionapex[0], regionapex[1]); + } + /* Infect the neighbor. */ + infect(neighbor); + /* Ensure that the neighbor's neighbors will be infected. */ + regiontri = (triangle **) poolalloc(&viri); + *regiontri = neighbor.tri; + } + } + /* Remark the triangle as infected, so it doesn't get added to the */ + /* virus pool again. */ + infect(testtri); + virusloop = (triangle **) traverse(&viri); + } + + /* Uninfect all triangles. */ + if (verbose > 1) { + printf(" Unmarking marked triangles.\n"); + } + traversalinit(&viri); + virusloop = (triangle **) traverse(&viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + uninfect(testtri); + virusloop = (triangle **) traverse(&viri); + } + /* Empty the virus pool. */ + poolrestart(&viri); +} + +/*****************************************************************************/ +/* */ +/* carveholes() Find the holes and infect them. Find the area */ +/* constraints and infect them. Infect the convex hull. */ +/* Spread the infection and kill triangles. Spread the */ +/* area constraints. */ +/* */ +/* This routine mainly calls other routines to carry out all these */ +/* functions. */ +/* */ +/*****************************************************************************/ + +void carveholes(holelist, holes, regionlist, regions) +REAL *holelist; +int holes; +REAL *regionlist; +int regions; +{ + struct triedge searchtri; + struct triedge triangleloop; + struct triedge *regiontris; + triangle **holetri; + triangle **regiontri; + point searchorg, searchdest; + enum locateresult intersect; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + + if (!(quiet || (noholes && convex))) { + printf("Removing unwanted triangles.\n"); + if (verbose && (holes > 0)) { + printf(" Marking holes for elimination.\n"); + } + } + + if (regions > 0) { + /* Allocate storage for the triangles in which region points fall. */ + regiontris = (struct triedge *) malloc(regions * sizeof(struct triedge)); + if (regiontris == (struct triedge *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + + if (((holes > 0) && !noholes) || !convex || (regions > 0)) { + /* Initialize a pool of viri to be used for holes, concavities, */ + /* regional attributes, and/or regional area constraints. */ + poolinit(&viri, sizeof(triangle *), VIRUSPERBLOCK, POINTER, 0); + } + + if (!convex) { + /* Mark as infected any unprotected triangles on the boundary. */ + /* This is one way by which concavities are created. */ + infecthull(); + } + + if ((holes > 0) && !noholes) { + /* Infect each triangle in which a hole lies. */ + for (i = 0; i < 2 * holes; i += 2) { + /* Ignore holes that aren't within the bounds of the mesh. */ + if ((holelist[i] >= xmin) && (holelist[i] <= xmax) + && (holelist[i + 1] >= ymin) && (holelist[i + 1] <= ymax)) { + /* Start searching from some triangle on the outer boundary. */ + searchtri.tri = dummytri; + searchtri.orient = 0; + symself(searchtri); + /* Ensure that the hole is to the left of this boundary edge; */ + /* otherwise, locate() will falsely report that the hole */ + /* falls within the starting triangle. */ + org(searchtri, searchorg); + dest(searchtri, searchdest); + if (counterclockwise(searchorg, searchdest, &holelist[i]) > 0.0) { + /* Find a triangle that contains the hole. */ + intersect = locate(&holelist[i], &searchtri); + if ((intersect != OUTSIDE) && (!infected(searchtri))) { + /* Infect the triangle. This is done by marking the triangle */ + /* as infect and including the triangle in the virus pool. */ + infect(searchtri); + holetri = (triangle **) poolalloc(&viri); + *holetri = searchtri.tri; + } + } + } + } + } + + /* Now, we have to find all the regions BEFORE we carve the holes, because */ + /* locate() won't work when the triangulation is no longer convex. */ + /* (Incidentally, this is the reason why regional attributes and area */ + /* constraints can't be used when refining a preexisting mesh, which */ + /* might not be convex; they can only be used with a freshly */ + /* triangulated PSLG.) */ + if (regions > 0) { + /* Find the starting triangle for each region. */ + for (i = 0; i < regions; i++) { + regiontris[i].tri = dummytri; + /* Ignore region points that aren't within the bounds of the mesh. */ + if ((regionlist[4 * i] >= xmin) && (regionlist[4 * i] <= xmax) && + (regionlist[4 * i + 1] >= ymin) && (regionlist[4 * i + 1] <= ymax)) { + /* Start searching from some triangle on the outer boundary. */ + searchtri.tri = dummytri; + searchtri.orient = 0; + symself(searchtri); + /* Ensure that the region point is to the left of this boundary */ + /* edge; otherwise, locate() will falsely report that the */ + /* region point falls within the starting triangle. */ + org(searchtri, searchorg); + dest(searchtri, searchdest); + if (counterclockwise(searchorg, searchdest, ®ionlist[4 * i]) > + 0.0) { + /* Find a triangle that contains the region point. */ + intersect = locate(®ionlist[4 * i], &searchtri); + if ((intersect != OUTSIDE) && (!infected(searchtri))) { + /* Record the triangle for processing after the */ + /* holes have been carved. */ + triedgecopy(searchtri, regiontris[i]); + } + } + } + } + } + + if (viri.items > 0) { + /* Carve the holes and concavities. */ + plague(); + } + /* The virus pool should be empty now. */ + + if (regions > 0) { + if (!quiet) { + if (regionattrib) { + if (vararea) { + printf("Spreading regional attributes and area constraints.\n"); + } else { + printf("Spreading regional attributes.\n"); + } + } else { + printf("Spreading regional area constraints.\n"); + } + } + if (regionattrib && !refine) { + /* Assign every triangle a regional attribute of zero. */ + traversalinit(&triangles); + triangleloop.orient = 0; + triangleloop.tri = triangletraverse(); + while (triangleloop.tri != (triangle *) NULL) { + setelemattribute(triangleloop, eextras, 0.0); + triangleloop.tri = triangletraverse(); + } + } + for (i = 0; i < regions; i++) { + if (regiontris[i].tri != dummytri) { + /* Make sure the triangle under consideration still exists. */ + /* It may have been eaten by the virus. */ + if (regiontris[i].tri[3] != (triangle) NULL) { + /* Put one triangle in the virus pool. */ + infect(regiontris[i]); + regiontri = (triangle **) poolalloc(&viri); + *regiontri = regiontris[i].tri; + /* Apply one region's attribute and/or area constraint. */ + regionplague(regionlist[4 * i + 2], regionlist[4 * i + 3]); + /* The virus pool should be empty now. */ + } + } + } + if (regionattrib && !refine) { + /* Note the fact that each triangle has an additional attribute. */ + eextras++; + } + } + + /* Free up memory. */ + if (((holes > 0) && !noholes) || !convex || (regions > 0)) { + pooldeinit(&viri); + } + if (regions > 0) { + free(regiontris); + } +} + +/** **/ +/** **/ +/********* Carving out holes and concavities ends here *********/ + +/********* Mesh quality maintenance begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* tallyencs() Traverse the entire list of shell edges, check each edge */ +/* to see if it is encroached. If so, add it to the list. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +void tallyencs() +{ + struct edge edgeloop; + int dummy; + + traversalinit(&shelles); + edgeloop.shorient = 0; + edgeloop.sh = shelletraverse(); + while (edgeloop.sh != (shelle *) NULL) { + /* If the segment is encroached, add it to the list. */ + dummy = checkedge4encroach(&edgeloop); + edgeloop.sh = shelletraverse(); + } +} + +#endif /* not CDT_ONLY */ + +/*****************************************************************************/ +/* */ +/* precisionerror() Print an error message for precision problems. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +void precisionerror() +{ + printf("Try increasing the area criterion and/or reducing the minimum\n"); + printf(" allowable angle so that tiny triangles are not created.\n"); +#ifdef SINGLE + printf("Alternatively, try recompiling me with double precision\n"); + printf(" arithmetic (by removing \"#define SINGLE\" from the\n"); + printf(" source file or \"-DSINGLE\" from the makefile).\n"); +#endif /* SINGLE */ +} + +#endif /* not CDT_ONLY */ + +/*****************************************************************************/ +/* */ +/* repairencs() Find and repair all the encroached segments. */ +/* */ +/* Encroached segments are repaired by splitting them by inserting a point */ +/* at or near their centers. */ +/* */ +/* `flaws' is a flag that specifies whether one should take note of new */ +/* encroached segments and bad triangles that result from inserting points */ +/* to repair existing encroached segments. */ +/* */ +/* When a segment is split, the two resulting subsegments are always */ +/* tested to see if they are encroached upon, regardless of the value */ +/* of `flaws'. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +void repairencs(flaws) +int flaws; +{ + struct triedge enctri; + struct triedge testtri; + struct edge *encloop; + struct edge testsh; + point eorg, edest; + point newpoint; + enum insertsiteresult success; + REAL segmentlength, nearestpoweroftwo; + REAL split; + int acuteorg, acutedest; + int dummy; + int i; + triangle ptr; /* Temporary variable used by stpivot(). */ + shelle sptr; /* Temporary variable used by snext(). */ + + while ((badsegments.items > 0) && (steinerleft != 0)) { + traversalinit(&badsegments); + encloop = badsegmenttraverse(); + while ((encloop != (struct edge *) NULL) && (steinerleft != 0)) { + /* To decide where to split a segment, we need to know if the */ + /* segment shares an endpoint with an adjacent segment. */ + /* The concern is that, if we simply split every encroached */ + /* segment in its center, two adjacent segments with a small */ + /* angle between them might lead to an infinite loop; each */ + /* point added to split one segment will encroach upon the */ + /* other segment, which must then be split with a point that */ + /* will encroach upon the first segment, and so on forever. */ + /* To avoid this, imagine a set of concentric circles, whose */ + /* radii are powers of two, about each segment endpoint. */ + /* These concentric circles determine where the segment is */ + /* split. (If both endpoints are shared with adjacent */ + /* segments, split the segment in the middle, and apply the */ + /* concentric shells for later splittings.) */ + + /* Is the origin shared with another segment? */ + stpivot(*encloop, enctri); + lnext(enctri, testtri); + tspivot(testtri, testsh); + acuteorg = testsh.sh != dummysh; + /* Is the destination shared with another segment? */ + lnextself(testtri); + tspivot(testtri, testsh); + acutedest = testsh.sh != dummysh; + /* Now, check the other side of the segment, if there's a triangle */ + /* there. */ + sym(enctri, testtri); + if (testtri.tri != dummytri) { + /* Is the destination shared with another segment? */ + lnextself(testtri); + tspivot(testtri, testsh); + acutedest = acutedest || (testsh.sh != dummysh); + /* Is the origin shared with another segment? */ + lnextself(testtri); + tspivot(testtri, testsh); + acuteorg = acuteorg || (testsh.sh != dummysh); + } + + sorg(*encloop, eorg); + sdest(*encloop, edest); + /* Use the concentric circles if exactly one endpoint is shared */ + /* with another adjacent segment. */ + if (acuteorg ^ acutedest) { + segmentlength = sqrt((edest[0] - eorg[0]) * (edest[0] - eorg[0]) + + (edest[1] - eorg[1]) * (edest[1] - eorg[1])); + /* Find the power of two nearest the segment's length. */ + nearestpoweroftwo = 1.0; + while (segmentlength > SQUAREROOTTWO * nearestpoweroftwo) { + nearestpoweroftwo *= 2.0; + } + while (segmentlength < (0.5 * SQUAREROOTTWO) * nearestpoweroftwo) { + nearestpoweroftwo *= 0.5; + } + /* Where do we split the segment? */ + split = 0.5 * nearestpoweroftwo / segmentlength; + if (acutedest) { + split = 1.0 - split; + } + } else { + /* If we're not worried about adjacent segments, split */ + /* this segment in the middle. */ + split = 0.5; + } + + /* Create the new point. */ + newpoint = (point) poolalloc(&points); + /* Interpolate its coordinate and attributes. */ + for (i = 0; i < 2 + nextras; i++) { + newpoint[i] = (1.0 - split) * eorg[i] + split * edest[i]; + } + setpointmark(newpoint, mark(*encloop)); + if (verbose > 1) { + printf( + " Splitting edge (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n", + eorg[0], eorg[1], edest[0], edest[1], newpoint[0], newpoint[1]); + } + /* Check whether the new point lies on an endpoint. */ + if (((newpoint[0] == eorg[0]) && (newpoint[1] == eorg[1])) + || ((newpoint[0] == edest[0]) && (newpoint[1] == edest[1]))) { + printf("Error: Ran out of precision at (%.12g, %.12g).\n", + newpoint[0], newpoint[1]); + printf("I attempted to split a segment to a smaller size than can\n"); + printf(" be accommodated by the finite precision of floating point\n" + ); + printf(" arithmetic.\n"); + precisionerror(); + exit(1); + } + /* Insert the splitting point. This should always succeed. */ + success = insertsite(newpoint, &enctri, encloop, flaws, flaws); + if ((success != SUCCESSFULPOINT) && (success != ENCROACHINGPOINT)) { + printf("Internal error in repairencs():\n"); + printf(" Failure to split a segment.\n"); + internalerror(); + } + if (steinerleft > 0) { + steinerleft--; + } + /* Check the two new subsegments to see if they're encroached. */ + dummy = checkedge4encroach(encloop); + snextself(*encloop); + dummy = checkedge4encroach(encloop); + + badsegmentdealloc(encloop); + encloop = badsegmenttraverse(); + } + } +} + +#endif /* not CDT_ONLY */ + +/*****************************************************************************/ +/* */ +/* tallyfaces() Test every triangle in the mesh for quality measures. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +void tallyfaces() +{ + struct triedge triangleloop; + + if (verbose) { + printf(" Making a list of bad triangles.\n"); + } + traversalinit(&triangles); + triangleloop.orient = 0; + triangleloop.tri = triangletraverse(); + while (triangleloop.tri != (triangle *) NULL) { + /* If the triangle is bad, enqueue it. */ + testtriangle(&triangleloop); + triangleloop.tri = triangletraverse(); + } +} + +#endif /* not CDT_ONLY */ + +/*****************************************************************************/ +/* */ +/* findcircumcenter() Find the circumcenter of a triangle. */ +/* */ +/* The result is returned both in terms of x-y coordinates and xi-eta */ +/* coordinates. The xi-eta coordinate system is defined in terms of the */ +/* triangle: the origin of the triangle is the origin of the coordinate */ +/* system; the destination of the triangle is one unit along the xi axis; */ +/* and the apex of the triangle is one unit along the eta axis. */ +/* */ +/* The return value indicates which edge of the triangle is shortest. */ +/* */ +/*****************************************************************************/ + +enum circumcenterresult findcircumcenter(torg, tdest, tapex, circumcenter, + xi, eta) +point torg; +point tdest; +point tapex; +point circumcenter; +REAL *xi; +REAL *eta; +{ + REAL xdo, ydo, xao, yao, xad, yad; + REAL dodist, aodist, addist; + REAL denominator; + REAL dx, dy; + + circumcentercount++; + + /* Compute the circumcenter of the triangle. */ + xdo = tdest[0] - torg[0]; + ydo = tdest[1] - torg[1]; + xao = tapex[0] - torg[0]; + yao = tapex[1] - torg[1]; + dodist = xdo * xdo + ydo * ydo; + aodist = xao * xao + yao * yao; + if (noexact) { + denominator = 0.5 / (xdo * yao - xao * ydo); + } else { + /* Use the counterclockwise() routine to ensure a positive (and */ + /* reasonably accurate) result, avoiding any possibility of */ + /* division by zero. */ + denominator = 0.5 / counterclockwise(tdest, tapex, torg); + /* Don't count the above as an orientation test. */ + counterclockcount--; + } + circumcenter[0] = torg[0] - (ydo * aodist - yao * dodist) * denominator; + circumcenter[1] = torg[1] + (xdo * aodist - xao * dodist) * denominator; + + /* To interpolate point attributes for the new point inserted at */ + /* the circumcenter, define a coordinate system with a xi-axis, */ + /* directed from the triangle's origin to its destination, and */ + /* an eta-axis, directed from its origin to its apex. */ + /* Calculate the xi and eta coordinates of the circumcenter. */ + dx = circumcenter[0] - torg[0]; + dy = circumcenter[1] - torg[1]; + *xi = (dx * yao - xao * dy) * (2.0 * denominator); + *eta = (xdo * dy - dx * ydo) * (2.0 * denominator); + + xad = tapex[0] - tdest[0]; + yad = tapex[1] - tdest[1]; + addist = xad * xad + yad * yad; + if ((addist < dodist) && (addist < aodist)) { + return OPPOSITEORG; + } else if (dodist < aodist) { + return OPPOSITEAPEX; + } else { + return OPPOSITEDEST; + } +} + +/*****************************************************************************/ +/* */ +/* splittriangle() Inserts a point at the circumcenter of a triangle. */ +/* Deletes the newly inserted point if it encroaches upon */ +/* a segment. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +void splittriangle(badtri) +struct badface *badtri; +{ + point borg, bdest, bapex; + point newpoint; + REAL xi, eta; + enum insertsiteresult success; + enum circumcenterresult shortedge; + int errorflag; + int i; + + org(badtri->badfacetri, borg); + dest(badtri->badfacetri, bdest); + apex(badtri->badfacetri, bapex); + /* Make sure that this triangle is still the same triangle it was */ + /* when it was tested and determined to be of bad quality. */ + /* Subsequent transformations may have made it a different triangle. */ + if ((borg == badtri->faceorg) && (bdest == badtri->facedest) && + (bapex == badtri->faceapex)) { + if (verbose > 1) { + printf(" Splitting this triangle at its circumcenter:\n"); + printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", borg[0], + borg[1], bdest[0], bdest[1], bapex[0], bapex[1]); + } + errorflag = 0; + /* Create a new point at the triangle's circumcenter. */ + newpoint = (point) poolalloc(&points); + shortedge = findcircumcenter(borg, bdest, bapex, newpoint, &xi, &eta); + /* Check whether the new point lies on a triangle vertex. */ + if (((newpoint[0] == borg[0]) && (newpoint[1] == borg[1])) + || ((newpoint[0] == bdest[0]) && (newpoint[1] == bdest[1])) + || ((newpoint[0] == bapex[0]) && (newpoint[1] == bapex[1]))) { + if (!quiet) { + printf("Warning: New point (%.12g, %.12g) falls on existing vertex.\n" + , newpoint[0], newpoint[1]); + errorflag = 1; + } + pointdealloc(newpoint); + } else { + for (i = 2; i < 2 + nextras; i++) { + /* Interpolate the point attributes at the circumcenter. */ + newpoint[i] = borg[i] + xi * (bdest[i] - borg[i]) + + eta * (bapex[i] - borg[i]); + } + /* The new point must be in the interior, and have a marker of zero. */ + setpointmark(newpoint, 0); + /* Ensure that the handle `badtri->badfacetri' represents the shortest */ + /* edge of the triangle. This ensures that the circumcenter must */ + /* fall to the left of this edge, so point location will work. */ + if (shortedge == OPPOSITEORG) { + lnextself(badtri->badfacetri); + } else if (shortedge == OPPOSITEDEST) { + lprevself(badtri->badfacetri); + } + /* Insert the circumcenter, searching from the edge of the triangle, */ + /* and maintain the Delaunay property of the triangulation. */ + success = insertsite(newpoint, &(badtri->badfacetri), + (struct edge *) NULL, 1, 1); + if (success == SUCCESSFULPOINT) { + if (steinerleft > 0) { + steinerleft--; + } + } else if (success == ENCROACHINGPOINT) { + /* If the newly inserted point encroaches upon a segment, delete it. */ + deletesite(&(badtri->badfacetri)); + } else if (success == VIOLATINGPOINT) { + /* Failed to insert the new point, but some segment was */ + /* marked as being encroached. */ + pointdealloc(newpoint); + } else { /* success == DUPLICATEPOINT */ + /* Failed to insert the new point because a vertex is already there. */ + if (!quiet) { + printf( + "Warning: New point (%.12g, %.12g) falls on existing vertex.\n" + , newpoint[0], newpoint[1]); + errorflag = 1; + } + pointdealloc(newpoint); + } + } + if (errorflag) { + if (verbose) { + printf(" The new point is at the circumcenter of triangle\n"); + printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + borg[0], borg[1], bdest[0], bdest[1], bapex[0], bapex[1]); + } + printf("This probably means that I am trying to refine triangles\n"); + printf(" to a smaller size than can be accommodated by the finite\n"); + printf(" precision of floating point arithmetic. (You can be\n"); + printf(" sure of this if I fail to terminate.)\n"); + precisionerror(); + } + } + /* Return the bad triangle to the pool. */ + pooldealloc(&badtriangles, (VOID *) badtri); +} + +#endif /* not CDT_ONLY */ + +/*****************************************************************************/ +/* */ +/* enforcequality() Remove all the encroached edges and bad triangles */ +/* from the triangulation. */ +/* */ +/*****************************************************************************/ + +#ifndef CDT_ONLY + +void enforcequality() +{ + int i; + + if (!quiet) { + printf("Adding Steiner points to enforce quality.\n"); + } + /* Initialize the pool of encroached segments. */ + poolinit(&badsegments, sizeof(struct edge), BADSEGMENTPERBLOCK, POINTER, 0); + if (verbose) { + printf(" Looking for encroached segments.\n"); + } + /* Test all segments to see if they're encroached. */ + tallyencs(); + if (verbose && (badsegments.items > 0)) { + printf(" Splitting encroached segments.\n"); + } + /* Note that steinerleft == -1 if an unlimited number */ + /* of Steiner points is allowed. */ + while ((badsegments.items > 0) && (steinerleft != 0)) { + /* Fix the segments without noting newly encroached segments or */ + /* bad triangles. The reason we don't want to note newly */ + /* encroached segments is because some encroached segments are */ + /* likely to be noted multiple times, and would then be blindly */ + /* split multiple times. I should fix that some time. */ + repairencs(0); + /* Now, find all the segments that became encroached while adding */ + /* points to split encroached segments. */ + tallyencs(); + } + /* At this point, if we haven't run out of Steiner points, the */ + /* triangulation should be (conforming) Delaunay. */ + + /* Next, we worry about enforcing triangle quality. */ + if ((minangle > 0.0) || vararea || fixedarea) { + /* Initialize the pool of bad triangles. */ + poolinit(&badtriangles, sizeof(struct badface), BADTRIPERBLOCK, POINTER, + 0); + /* Initialize the queues of bad triangles. */ + for (i = 0; i < 64; i++) { + queuefront[i] = (struct badface *) NULL; + queuetail[i] = &queuefront[i]; + } + /* Test all triangles to see if they're bad. */ + tallyfaces(); + if (verbose) { + printf(" Splitting bad triangles.\n"); + } + while ((badtriangles.items > 0) && (steinerleft != 0)) { + /* Fix one bad triangle by inserting a point at its circumcenter. */ + splittriangle(dequeuebadtri()); + /* Fix any encroached segments that may have resulted. Record */ + /* any new bad triangles or encroached segments that result. */ + if (badsegments.items > 0) { + repairencs(1); + } + } + } + /* At this point, if we haven't run out of Steiner points, the */ + /* triangulation should be (conforming) Delaunay and have no */ + /* low-quality triangles. */ + + /* Might we have run out of Steiner points too soon? */ + if (!quiet && (badsegments.items > 0) && (steinerleft == 0)) { + printf("\nWarning: I ran out of Steiner points, but the mesh has\n"); + if (badsegments.items == 1) { + printf(" an encroached segment, and therefore might not be truly\n"); + } else { + printf(" %ld encroached segments, and therefore might not be truly\n", + badsegments.items); + } + printf(" Delaunay. If the Delaunay property is important to you,\n"); + printf(" try increasing the number of Steiner points (controlled by\n"); + printf(" the -S switch) slightly and try again.\n\n"); + } +} + +#endif /* not CDT_ONLY */ + +/** **/ +/** **/ +/********* Mesh quality maintenance ends here *********/ + +/*****************************************************************************/ +/* */ +/* highorder() Create extra nodes for quadratic subparametric elements. */ +/* */ +/*****************************************************************************/ + +void highorder() +{ + struct triedge triangleloop, trisym; + struct edge checkmark; + point newpoint; + point torg, tdest; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + if (!quiet) { + printf("Adding vertices for second-order triangles.\n"); + } + /* The following line ensures that dead items in the pool of nodes */ + /* cannot be allocated for the extra nodes associated with high */ + /* order elements. This ensures that the primary nodes (at the */ + /* corners of elements) will occur earlier in the output files, and */ + /* have lower indices, than the extra nodes. */ + points.deaditemstack = (VOID *) NULL; + + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + /* To loop over the set of edges, loop over all triangles, and look at */ + /* the three edges of each triangle. If there isn't another triangle */ + /* adjacent to the edge, operate on the edge. If there is another */ + /* adjacent triangle, operate on the edge only if the current triangle */ + /* has a smaller pointer than its neighbor. This way, each edge is */ + /* considered only once. */ + while (triangleloop.tri != (triangle *) NULL) { + for (triangleloop.orient = 0; triangleloop.orient < 3; + triangleloop.orient++) { + sym(triangleloop, trisym); + if ((triangleloop.tri < trisym.tri) || (trisym.tri == dummytri)) { + org(triangleloop, torg); + dest(triangleloop, tdest); + /* Create a new node in the middle of the edge. Interpolate */ + /* its attributes. */ + newpoint = (point) poolalloc(&points); + for (i = 0; i < 2 + nextras; i++) { + newpoint[i] = 0.5 * (torg[i] + tdest[i]); + } + /* Set the new node's marker to zero or one, depending on */ + /* whether it lies on a boundary. */ + setpointmark(newpoint, trisym.tri == dummytri); + if (useshelles) { + tspivot(triangleloop, checkmark); + /* If this edge is a segment, transfer the marker to the new node. */ + if (checkmark.sh != dummysh) { + setpointmark(newpoint, mark(checkmark)); + } + } + if (verbose > 1) { + printf(" Creating (%.12g, %.12g).\n", newpoint[0], newpoint[1]); + } + /* Record the new node in the (one or two) adjacent elements. */ + triangleloop.tri[highorderindex + triangleloop.orient] = + (triangle) newpoint; + if (trisym.tri != dummytri) { + trisym.tri[highorderindex + trisym.orient] = (triangle) newpoint; + } + } + } + triangleloop.tri = triangletraverse(); + } +} + +/********* File I/O routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* readline() Read a nonempty line from a file. */ +/* */ +/* A line is considered "nonempty" if it contains something that looks like */ +/* a number. */ +/* */ +/*****************************************************************************/ + +#ifndef TRILIBRARY + +char *readline(string, infile, infilename) +char *string; +FILE *infile; +char *infilename; +{ + char *result; + + /* Search for something that looks like a number. */ + do { + result = fgets(string, INPUTLINESIZE, infile); + if (result == (char *) NULL) { + printf(" Error: Unexpected end of file in %s.\n", infilename); + exit(1); + } + /* Skip anything that doesn't look like a number, a comment, */ + /* or the end of a line. */ + while ((*result != '\0') && (*result != '#') + && (*result != '.') && (*result != '+') && (*result != '-') + && ((*result < '0') || (*result > '9'))) { + result++; + } + /* If it's a comment or end of line, read another line and try again. */ + } while ((*result == '#') || (*result == '\0')); + return result; +} + +#endif /* not TRILIBRARY */ + +/*****************************************************************************/ +/* */ +/* findfield() Find the next field of a string. */ +/* */ +/* Jumps past the current field by searching for whitespace, then jumps */ +/* past the whitespace to find the next field. */ +/* */ +/*****************************************************************************/ + +#ifndef TRILIBRARY + +char *findfield(string) +char *string; +{ + char *result; + + result = string; + /* Skip the current field. Stop upon reaching whitespace. */ + while ((*result != '\0') && (*result != '#') + && (*result != ' ') && (*result != '\t')) { + result++; + } + /* Now skip the whitespace and anything else that doesn't look like a */ + /* number, a comment, or the end of a line. */ + while ((*result != '\0') && (*result != '#') + && (*result != '.') && (*result != '+') && (*result != '-') + && ((*result < '0') || (*result > '9'))) { + result++; + } + /* Check for a comment (prefixed with `#'). */ + if (*result == '#') { + *result = '\0'; + } + return result; +} + +#endif /* not TRILIBRARY */ + +/*****************************************************************************/ +/* */ +/* readnodes() Read the points from a file, which may be a .node or .poly */ +/* file. */ +/* */ +/*****************************************************************************/ + +#ifndef TRILIBRARY + +void readnodes(nodefilename, polyfilename, polyfile) +char *nodefilename; +char *polyfilename; +FILE **polyfile; +{ + FILE *infile; + point pointloop; + char inputline[INPUTLINESIZE]; + char *stringptr; + char *infilename; + REAL x, y; + int firstnode; + int nodemarkers; + int currentmarker; + int i, j; + + if (poly) { + /* Read the points from a .poly file. */ + if (!quiet) { + printf("Opening %s.\n", polyfilename); + } + *polyfile = fopen(polyfilename, "r"); + if (*polyfile == (FILE *) NULL) { + printf(" Error: Cannot access file %s.\n", polyfilename); + exit(1); + } + /* Read number of points, number of dimensions, number of point */ + /* attributes, and number of boundary markers. */ + stringptr = readline(inputline, *polyfile, polyfilename); + inpoints = (int) strtol (stringptr, &stringptr, 0); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + mesh_dim = 2; + } else { + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + nextras = 0; + } else { + nextras = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + nodemarkers = 0; + } else { + nodemarkers = (int) strtol (stringptr, &stringptr, 0); + } + if (inpoints > 0) { + infile = *polyfile; + infilename = polyfilename; + readnodefile = 0; + } else { + /* If the .poly file claims there are zero points, that means that */ + /* the points should be read from a separate .node file. */ + readnodefile = 1; + infilename = innodefilename; + } + } else { + readnodefile = 1; + infilename = innodefilename; + *polyfile = (FILE *) NULL; + } + + if (readnodefile) { + /* Read the points from a .node file. */ + if (!quiet) { + printf("Opening %s.\n", innodefilename); + } + infile = fopen(innodefilename, "r"); + if (infile == (FILE *) NULL) { + printf(" Error: Cannot access file %s.\n", innodefilename); + exit(1); + } + /* Read number of points, number of dimensions, number of point */ + /* attributes, and number of boundary markers. */ + stringptr = readline(inputline, infile, innodefilename); + inpoints = (int) strtol (stringptr, &stringptr, 0); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + mesh_dim = 2; + } else { + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + nextras = 0; + } else { + nextras = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + nodemarkers = 0; + } else { + nodemarkers = (int) strtol (stringptr, &stringptr, 0); + } + } + + if (inpoints < 3) { + printf("Error: Input must have at least three input points.\n"); + exit(1); + } + if (mesh_dim != 2) { + printf("Error: Triangle only works with two-dimensional meshes.\n"); + exit(1); + } + + initializepointpool(); + + /* Read the points. */ + for (i = 0; i < inpoints; i++) { + pointloop = (point) poolalloc(&points); + stringptr = readline(inputline, infile, infilename); + if (i == 0) { + firstnode = (int) strtol (stringptr, &stringptr, 0); + if ((firstnode == 0) || (firstnode == 1)) { + firstnumber = firstnode; + } + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no x coordinate.\n", firstnumber + i); + exit(1); + } + x = (REAL) strtod(stringptr, &stringptr); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Point %d has no y coordinate.\n", firstnumber + i); + exit(1); + } + y = (REAL) strtod(stringptr, &stringptr); + pointloop[0] = x; + pointloop[1] = y; + /* Read the point attributes. */ + for (j = 2; j < 2 + nextras; j++) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + pointloop[j] = 0.0; + } else { + pointloop[j] = (REAL) strtod(stringptr, &stringptr); + } + } + if (nodemarkers) { + /* Read a point marker. */ + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + setpointmark(pointloop, 0); + } else { + currentmarker = (int) strtol (stringptr, &stringptr, 0); + setpointmark(pointloop, currentmarker); + } + } else { + /* If no markers are specified in the file, they default to zero. */ + setpointmark(pointloop, 0); + } + /* Determine the smallest and largest x and y coordinates. */ + if (i == 0) { + xmin = xmax = x; + ymin = ymax = y; + } else { + xmin = (x < xmin) ? x : xmin; + xmax = (x > xmax) ? x : xmax; + ymin = (y < ymin) ? y : ymin; + ymax = (y > ymax) ? y : ymax; + } + } + if (readnodefile) { + fclose(infile); + } + + /* Nonexistent x value used as a flag to mark circle events in sweepline */ + /* Delaunay algorithm. */ + xminextreme = 10 * xmin - 9 * xmax; +} + +#endif /* not TRILIBRARY */ + +/*****************************************************************************/ +/* */ +/* transfernodes() Read the points from memory. */ +/* */ +/*****************************************************************************/ + +#ifdef TRILIBRARY + +void transfernodes(pointlist, pointattriblist, pointmarkerlist, numberofpoints, + numberofpointattribs) +REAL *pointlist; +REAL *pointattriblist; +int *pointmarkerlist; +int numberofpoints; +int numberofpointattribs; +{ + point pointloop; + REAL x, y; + int i, j; + int coordindex; + int attribindex; + + inpoints = numberofpoints; + mesh_dim = 2; + nextras = numberofpointattribs; + readnodefile = 0; + if (inpoints < 3) { + printf("Error: Input must have at least three input points.\n"); + exit(1); + } + + initializepointpool(); + + /* Read the points. */ + coordindex = 0; + attribindex = 0; + for (i = 0; i < inpoints; i++) { + pointloop = (point) poolalloc(&points); + /* Read the point coordinates. */ + x = pointloop[0] = pointlist[coordindex++]; + y = pointloop[1] = pointlist[coordindex++]; + /* Read the point attributes. */ + for (j = 0; j < numberofpointattribs; j++) { + pointloop[2 + j] = pointattriblist[attribindex++]; + } + if (pointmarkerlist != (int *) NULL) { + /* Read a point marker. */ + setpointmark(pointloop, pointmarkerlist[i]); + } else { + /* If no markers are specified, they default to zero. */ + setpointmark(pointloop, 0); + } + x = pointloop[0]; + y = pointloop[1]; + /* Determine the smallest and largest x and y coordinates. */ + if (i == 0) { + xmin = xmax = x; + ymin = ymax = y; + } else { + xmin = (x < xmin) ? x : xmin; + xmax = (x > xmax) ? x : xmax; + ymin = (y < ymin) ? y : ymin; + ymax = (y > ymax) ? y : ymax; + } + } + + /* Nonexistent x value used as a flag to mark circle events in sweepline */ + /* Delaunay algorithm. */ + xminextreme = 10 * xmin - 9 * xmax; +} + +#endif /* TRILIBRARY */ + +/*****************************************************************************/ +/* */ +/* readholes() Read the holes, and possibly regional attributes and area */ +/* constraints, from a .poly file. */ +/* */ +/*****************************************************************************/ + +#ifndef TRILIBRARY + +void readholes(polyfile, polyfilename, hlist, holes, rlist, regions) +FILE *polyfile; +char *polyfilename; +REAL **hlist; +int *holes; +REAL **rlist; +int *regions; +{ + REAL *holelist; + REAL *regionlist; + char inputline[INPUTLINESIZE]; + char *stringptr; + int index; + int i; + + /* Read the holes. */ + stringptr = readline(inputline, polyfile, polyfilename); + *holes = (int) strtol (stringptr, &stringptr, 0); + if (*holes > 0) { + holelist = (REAL *) malloc(2 * *holes * sizeof(REAL)); + *hlist = holelist; + if (holelist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + for (i = 0; i < 2 * *holes; i += 2) { + stringptr = readline(inputline, polyfile, polyfilename); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Hole %d has no x coordinate.\n", + firstnumber + (i >> 1)); + exit(1); + } else { + holelist[i] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Hole %d has no y coordinate.\n", + firstnumber + (i >> 1)); + exit(1); + } else { + holelist[i + 1] = (REAL) strtod(stringptr, &stringptr); + } + } + } else { + *hlist = (REAL *) NULL; + } + +#ifndef CDT_ONLY + if ((regionattrib || vararea) && !refine) { + /* Read the area constraints. */ + stringptr = readline(inputline, polyfile, polyfilename); + *regions = (int) strtol (stringptr, &stringptr, 0); + if (*regions > 0) { + regionlist = (REAL *) malloc(4 * *regions * sizeof(REAL)); + *rlist = regionlist; + if (regionlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + index = 0; + for (i = 0; i < *regions; i++) { + stringptr = readline(inputline, polyfile, polyfilename); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Region %d has no x coordinate.\n", + firstnumber + i); + exit(1); + } else { + regionlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("Error: Region %d has no y coordinate.\n", + firstnumber + i); + exit(1); + } else { + regionlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf( + "Error: Region %d has no region attribute or area constraint.\n", + firstnumber + i); + exit(1); + } else { + regionlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + regionlist[index] = regionlist[index - 1]; + } else { + regionlist[index] = (REAL) strtod(stringptr, &stringptr); + } + index++; + } + } + } else { + /* Set `*regions' to zero to avoid an accidental free() later. */ + *regions = 0; + *rlist = (REAL *) NULL; + } +#endif /* not CDT_ONLY */ + + fclose(polyfile); +} + +#endif /* not TRILIBRARY */ + +/*****************************************************************************/ +/* */ +/* finishfile() Write the command line to the output file so the user */ +/* can remember how the file was generated. Close the file. */ +/* */ +/*****************************************************************************/ + +#ifndef TRILIBRARY + +void finishfile(outfile, argc, argv) +FILE *outfile; +int argc; +char **argv; +{ + int i; + + fprintf(outfile, "# Generated by"); + for (i = 0; i < argc; i++) { + fprintf(outfile, " "); + fputs(argv[i], outfile); + } + fprintf(outfile, "\n"); + fclose(outfile); +} + +#endif /* not TRILIBRARY */ + +/*****************************************************************************/ +/* */ +/* writenodes() Number the points and write them to a .node file. */ +/* */ +/* To save memory, the point numbers are written over the shell markers */ +/* after the points are written to a file. */ +/* */ +/*****************************************************************************/ + +#ifdef TRILIBRARY + +void writenodes(pointlist, pointattriblist, pointmarkerlist) +REAL **pointlist; +REAL **pointattriblist; +int **pointmarkerlist; + +#else /* not TRILIBRARY */ + +void writenodes(nodefilename, argc, argv) +char *nodefilename; +int argc; +char **argv; + +#endif /* not TRILIBRARY */ + +{ +#ifdef TRILIBRARY + REAL *plist; + REAL *palist; + int *pmlist; + int coordindex; + int attribindex; +#else /* not TRILIBRARY */ + FILE *outfile; +#endif /* not TRILIBRARY */ + point pointloop; + int pointnumber; + int i; + +#ifdef TRILIBRARY + if (!quiet) { + printf("Writing points.\n"); + } + /* Allocate memory for output points if necessary. */ + if (*pointlist == (REAL *) NULL) { + *pointlist = (REAL *) malloc(points.items * 2 * sizeof(REAL)); + if (*pointlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + /* Allocate memory for output point attributes if necessary. */ + if ((nextras > 0) && (*pointattriblist == (REAL *) NULL)) { + *pointattriblist = (REAL *) malloc(points.items * nextras * sizeof(REAL)); + if (*pointattriblist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + /* Allocate memory for output point markers if necessary. */ + if (!nobound && (*pointmarkerlist == (int *) NULL)) { + *pointmarkerlist = (int *) malloc(points.items * sizeof(int)); + if (*pointmarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + plist = *pointlist; + palist = *pointattriblist; + pmlist = *pointmarkerlist; + coordindex = 0; + attribindex = 0; +#else /* not TRILIBRARY */ + if (!quiet) { + printf("Writing %s.\n", nodefilename); + } + outfile = fopen(nodefilename, "w"); + if (outfile == (FILE *) NULL) { + printf(" Error: Cannot create file %s.\n", nodefilename); + exit(1); + } + /* Number of points, number of dimensions, number of point attributes, */ + /* and number of boundary markers (zero or one). */ + fprintf(outfile, "%ld %d %d %d\n", points.items, mesh_dim, nextras, + 1 - nobound); +#endif /* not TRILIBRARY */ + + traversalinit(&points); + pointloop = pointtraverse(); + pointnumber = firstnumber; + while (pointloop != (point) NULL) { +#ifdef TRILIBRARY + /* X and y coordinates. */ + plist[coordindex++] = pointloop[0]; + plist[coordindex++] = pointloop[1]; + /* Point attributes. */ + for (i = 0; i < nextras; i++) { + palist[attribindex++] = pointloop[2 + i]; + } + if (!nobound) { + /* Copy the boundary marker. */ + pmlist[pointnumber - firstnumber] = pointmark(pointloop); + } +#else /* not TRILIBRARY */ + /* Point number, x and y coordinates. */ + fprintf(outfile, "%4d %.17g %.17g", pointnumber, pointloop[0], + pointloop[1]); + for (i = 0; i < nextras; i++) { + /* Write an attribute. */ + fprintf(outfile, " %.17g", pointloop[i + 2]); + } + if (nobound) { + fprintf(outfile, "\n"); + } else { + /* Write the boundary marker. */ + fprintf(outfile, " %d\n", pointmark(pointloop)); + } +#endif /* not TRILIBRARY */ + + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } + +#ifndef TRILIBRARY + finishfile(outfile, argc, argv); +#endif /* not TRILIBRARY */ +} + +/*****************************************************************************/ +/* */ +/* numbernodes() Number the points. */ +/* */ +/* Each point is assigned a marker equal to its number. */ +/* */ +/* Used when writenodes() is not called because no .node file is written. */ +/* */ +/*****************************************************************************/ + +void numbernodes() +{ + point pointloop; + int pointnumber; + + traversalinit(&points); + pointloop = pointtraverse(); + pointnumber = firstnumber; + while (pointloop != (point) NULL) { + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } +} + +/*****************************************************************************/ +/* */ +/* writeelements() Write the triangles to an .ele file. */ +/* */ +/*****************************************************************************/ + +#ifdef TRILIBRARY + +void writeelements(trianglelist, triangleattriblist) +int **trianglelist; +REAL **triangleattriblist; + +#else /* not TRILIBRARY */ + +void writeelements(elefilename, argc, argv) +char *elefilename; +int argc; +char **argv; + +#endif /* not TRILIBRARY */ + +{ +#ifdef TRILIBRARY + int *tlist; + REAL *talist; + int pointindex; + int attribindex; +#else /* not TRILIBRARY */ + FILE *outfile; +#endif /* not TRILIBRARY */ + struct triedge triangleloop; + point p1, p2, p3; + point mid1, mid2, mid3; + int elementnumber; + int i; + +#ifdef TRILIBRARY + if (!quiet) { + printf("Writing triangles.\n"); + } + /* Allocate memory for output triangles if necessary. */ + if (*trianglelist == (int *) NULL) { + *trianglelist = (int *) malloc(triangles.items * + ((order + 1) * (order + 2) / 2) * sizeof(int)); + if (*trianglelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + /* Allocate memory for output triangle attributes if necessary. */ + if ((eextras > 0) && (*triangleattriblist == (REAL *) NULL)) { + *triangleattriblist = (REAL *) malloc(triangles.items * eextras * + sizeof(REAL)); + if (*triangleattriblist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + tlist = *trianglelist; + talist = *triangleattriblist; + pointindex = 0; + attribindex = 0; +#else /* not TRILIBRARY */ + if (!quiet) { + printf("Writing %s.\n", elefilename); + } + outfile = fopen(elefilename, "w"); + if (outfile == (FILE *) NULL) { + printf(" Error: Cannot create file %s.\n", elefilename); + exit(1); + } + /* Number of triangles, points per triangle, attributes per triangle. */ + fprintf(outfile, "%ld %d %d\n", triangles.items, + (order + 1) * (order + 2) / 2, eextras); +#endif /* not TRILIBRARY */ + + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + triangleloop.orient = 0; + elementnumber = firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + org(triangleloop, p1); + dest(triangleloop, p2); + apex(triangleloop, p3); + if (order == 1) { +#ifdef TRILIBRARY + tlist[pointindex++] = pointmark(p1); + tlist[pointindex++] = pointmark(p2); + tlist[pointindex++] = pointmark(p3); +#else /* not TRILIBRARY */ + /* Triangle number, indices for three points. */ + fprintf(outfile, "%4d %4d %4d %4d", elementnumber, + pointmark(p1), pointmark(p2), pointmark(p3)); +#endif /* not TRILIBRARY */ + } else { + mid1 = (point) triangleloop.tri[highorderindex + 1]; + mid2 = (point) triangleloop.tri[highorderindex + 2]; + mid3 = (point) triangleloop.tri[highorderindex]; +#ifdef TRILIBRARY + tlist[pointindex++] = pointmark(p1); + tlist[pointindex++] = pointmark(p2); + tlist[pointindex++] = pointmark(p3); + tlist[pointindex++] = pointmark(mid1); + tlist[pointindex++] = pointmark(mid2); + tlist[pointindex++] = pointmark(mid3); +#else /* not TRILIBRARY */ + /* Triangle number, indices for six points. */ + fprintf(outfile, "%4d %4d %4d %4d %4d %4d %4d", elementnumber, + pointmark(p1), pointmark(p2), pointmark(p3), pointmark(mid1), + pointmark(mid2), pointmark(mid3)); +#endif /* not TRILIBRARY */ + } + +#ifdef TRILIBRARY + for (i = 0; i < eextras; i++) { + talist[attribindex++] = elemattribute(triangleloop, i); + } +#else /* not TRILIBRARY */ + for (i = 0; i < eextras; i++) { + fprintf(outfile, " %.17g", elemattribute(triangleloop, i)); + } + fprintf(outfile, "\n"); +#endif /* not TRILIBRARY */ + + triangleloop.tri = triangletraverse(); + elementnumber++; + } + +#ifndef TRILIBRARY + finishfile(outfile, argc, argv); +#endif /* not TRILIBRARY */ +} + +/*****************************************************************************/ +/* */ +/* writepoly() Write the segments and holes to a .poly file. */ +/* */ +/*****************************************************************************/ + +#ifdef TRILIBRARY + +void writepoly(segmentlist, segmentmarkerlist) +int **segmentlist; +int **segmentmarkerlist; + +#else /* not TRILIBRARY */ + +void writepoly(polyfilename, holelist, holes, regionlist, regions, argc, argv) +char *polyfilename; +REAL *holelist; +int holes; +REAL *regionlist; +int regions; +int argc; +char **argv; + +#endif /* not TRILIBRARY */ + +{ +#ifdef TRILIBRARY + int *slist; + int *smlist; + int index; +#else /* not TRILIBRARY */ + FILE *outfile; + int i; +#endif /* not TRILIBRARY */ + struct edge shelleloop; + point endpoint1, endpoint2; + int shellenumber; + +#ifdef TRILIBRARY + if (!quiet) { + printf("Writing segments.\n"); + } + /* Allocate memory for output segments if necessary. */ + if (*segmentlist == (int *) NULL) { + *segmentlist = (int *) malloc(shelles.items * 2 * sizeof(int)); + if (*segmentlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + /* Allocate memory for output segment markers if necessary. */ + if (!nobound && (*segmentmarkerlist == (int *) NULL)) { + *segmentmarkerlist = (int *) malloc(shelles.items * sizeof(int)); + if (*segmentmarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + slist = *segmentlist; + smlist = *segmentmarkerlist; + index = 0; +#else /* not TRILIBRARY */ + if (!quiet) { + printf("Writing %s.\n", polyfilename); + } + outfile = fopen(polyfilename, "w"); + if (outfile == (FILE *) NULL) { + printf(" Error: Cannot create file %s.\n", polyfilename); + exit(1); + } + /* The zero indicates that the points are in a separate .node file. */ + /* Followed by number of dimensions, number of point attributes, */ + /* and number of boundary markers (zero or one). */ + fprintf(outfile, "%d %d %d %d\n", 0, mesh_dim, nextras, 1 - nobound); + /* Number of segments, number of boundary markers (zero or one). */ + fprintf(outfile, "%ld %d\n", shelles.items, 1 - nobound); +#endif /* not TRILIBRARY */ + + traversalinit(&shelles); + shelleloop.sh = shelletraverse(); + shelleloop.shorient = 0; + shellenumber = firstnumber; + while (shelleloop.sh != (shelle *) NULL) { + sorg(shelleloop, endpoint1); + sdest(shelleloop, endpoint2); +#ifdef TRILIBRARY + /* Copy indices of the segment's two endpoints. */ + slist[index++] = pointmark(endpoint1); + slist[index++] = pointmark(endpoint2); + if (!nobound) { + /* Copy the boundary marker. */ + smlist[shellenumber - firstnumber] = mark(shelleloop); + } +#else /* not TRILIBRARY */ + /* Segment number, indices of its two endpoints, and possibly a marker. */ + if (nobound) { + fprintf(outfile, "%4d %4d %4d\n", shellenumber, + pointmark(endpoint1), pointmark(endpoint2)); + } else { + fprintf(outfile, "%4d %4d %4d %4d\n", shellenumber, + pointmark(endpoint1), pointmark(endpoint2), mark(shelleloop)); + } +#endif /* not TRILIBRARY */ + + shelleloop.sh = shelletraverse(); + shellenumber++; + } + +#ifndef TRILIBRARY +#ifndef CDT_ONLY + fprintf(outfile, "%d\n", holes); + if (holes > 0) { + for (i = 0; i < holes; i++) { + /* Hole number, x and y coordinates. */ + fprintf(outfile, "%4d %.17g %.17g\n", firstnumber + i, + holelist[2 * i], holelist[2 * i + 1]); + } + } + if (regions > 0) { + fprintf(outfile, "%d\n", regions); + for (i = 0; i < regions; i++) { + /* Region number, x and y coordinates, attribute, maximum area. */ + fprintf(outfile, "%4d %.17g %.17g %.17g %.17g\n", firstnumber + i, + regionlist[4 * i], regionlist[4 * i + 1], + regionlist[4 * i + 2], regionlist[4 * i + 3]); + } + } +#endif /* not CDT_ONLY */ + + finishfile(outfile, argc, argv); +#endif /* not TRILIBRARY */ +} + +/*****************************************************************************/ +/* */ +/* writeedges() Write the edges to a .edge file. */ +/* */ +/*****************************************************************************/ + +#ifdef TRILIBRARY + +void writeedges(edgelist, edgemarkerlist) +int **edgelist; +int **edgemarkerlist; + +#else /* not TRILIBRARY */ + +void writeedges(edgefilename, argc, argv) +char *edgefilename; +int argc; +char **argv; + +#endif /* not TRILIBRARY */ + +{ +#ifdef TRILIBRARY + int *elist; + int *emlist; + int index; +#else /* not TRILIBRARY */ + FILE *outfile; +#endif /* not TRILIBRARY */ + struct triedge triangleloop, trisym; + struct edge checkmark; + point p1, p2; + int edgenumber; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + +#ifdef TRILIBRARY + if (!quiet) { + printf("Writing edges.\n"); + } + /* Allocate memory for edges if necessary. */ + if (*edgelist == (int *) NULL) { + *edgelist = (int *) malloc(edges * 2 * sizeof(int)); + if (*edgelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + /* Allocate memory for edge markers if necessary. */ + if (!nobound && (*edgemarkerlist == (int *) NULL)) { + *edgemarkerlist = (int *) malloc(edges * sizeof(int)); + if (*edgemarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + elist = *edgelist; + emlist = *edgemarkerlist; + index = 0; +#else /* not TRILIBRARY */ + if (!quiet) { + printf("Writing %s.\n", edgefilename); + } + outfile = fopen(edgefilename, "w"); + if (outfile == (FILE *) NULL) { + printf(" Error: Cannot create file %s.\n", edgefilename); + exit(1); + } + /* Number of edges, number of boundary markers (zero or one). */ + fprintf(outfile, "%ld %d\n", edges, 1 - nobound); +#endif /* not TRILIBRARY */ + + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + edgenumber = firstnumber; + /* To loop over the set of edges, loop over all triangles, and look at */ + /* the three edges of each triangle. If there isn't another triangle */ + /* adjacent to the edge, operate on the edge. If there is another */ + /* adjacent triangle, operate on the edge only if the current triangle */ + /* has a smaller pointer than its neighbor. This way, each edge is */ + /* considered only once. */ + while (triangleloop.tri != (triangle *) NULL) { + for (triangleloop.orient = 0; triangleloop.orient < 3; + triangleloop.orient++) { + sym(triangleloop, trisym); + if ((triangleloop.tri < trisym.tri) || (trisym.tri == dummytri)) { + org(triangleloop, p1); + dest(triangleloop, p2); +#ifdef TRILIBRARY + elist[index++] = pointmark(p1); + elist[index++] = pointmark(p2); +#endif /* TRILIBRARY */ + if (nobound) { +#ifndef TRILIBRARY + /* Edge number, indices of two endpoints. */ + fprintf(outfile, "%4d %d %d\n", edgenumber, + pointmark(p1), pointmark(p2)); +#endif /* not TRILIBRARY */ + } else { + /* Edge number, indices of two endpoints, and a boundary marker. */ + /* If there's no shell edge, the boundary marker is zero. */ + if (useshelles) { + tspivot(triangleloop, checkmark); + if (checkmark.sh == dummysh) { +#ifdef TRILIBRARY + emlist[edgenumber - firstnumber] = 0; +#else /* not TRILIBRARY */ + fprintf(outfile, "%4d %d %d %d\n", edgenumber, + pointmark(p1), pointmark(p2), 0); +#endif /* not TRILIBRARY */ + } else { +#ifdef TRILIBRARY + emlist[edgenumber - firstnumber] = mark(checkmark); +#else /* not TRILIBRARY */ + fprintf(outfile, "%4d %d %d %d\n", edgenumber, + pointmark(p1), pointmark(p2), mark(checkmark)); +#endif /* not TRILIBRARY */ + } + } else { +#ifdef TRILIBRARY + emlist[edgenumber - firstnumber] = trisym.tri == dummytri; +#else /* not TRILIBRARY */ + fprintf(outfile, "%4d %d %d %d\n", edgenumber, + pointmark(p1), pointmark(p2), trisym.tri == dummytri); +#endif /* not TRILIBRARY */ + } + } + edgenumber++; + } + } + triangleloop.tri = triangletraverse(); + } + +#ifndef TRILIBRARY + finishfile(outfile, argc, argv); +#endif /* not TRILIBRARY */ +} + +/*****************************************************************************/ +/* */ +/* writevoronoi() Write the Voronoi diagram to a .v.node and .v.edge */ +/* file. */ +/* */ +/* The Voronoi diagram is the geometric dual of the Delaunay triangulation. */ +/* Hence, the Voronoi vertices are listed by traversing the Delaunay */ +/* triangles, and the Voronoi edges are listed by traversing the Delaunay */ +/* edges. */ +/* */ +/* WARNING: In order to assign numbers to the Voronoi vertices, this */ +/* procedure messes up the shell edges or the extra nodes of every */ +/* element. Hence, you should call this procedure last. */ +/* */ +/*****************************************************************************/ + +#ifdef TRILIBRARY + +void writevoronoi(vpointlist, vpointattriblist, vpointmarkerlist, vedgelist, + vedgemarkerlist, vnormlist) +REAL **vpointlist; +REAL **vpointattriblist; +int **vpointmarkerlist; +int **vedgelist; +int **vedgemarkerlist; +REAL **vnormlist; + +#else /* not TRILIBRARY */ + +void writevoronoi(vnodefilename, vedgefilename, argc, argv) +char *vnodefilename; +char *vedgefilename; +int argc; +char **argv; + +#endif /* not TRILIBRARY */ + +{ +#ifdef TRILIBRARY + REAL *plist; + REAL *palist; + int *elist; + REAL *normlist; + int coordindex; + int attribindex; +#else /* not TRILIBRARY */ + FILE *outfile; +#endif /* not TRILIBRARY */ + struct triedge triangleloop, trisym; + point torg, tdest, tapex; + REAL circumcenter[2]; + REAL xi, eta; + int vnodenumber, vedgenumber; + int p1, p2; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + +#ifdef TRILIBRARY + if (!quiet) { + printf("Writing Voronoi vertices.\n"); + } + /* Allocate memory for Voronoi vertices if necessary. */ + if (*vpointlist == (REAL *) NULL) { + *vpointlist = (REAL *) malloc(triangles.items * 2 * sizeof(REAL)); + if (*vpointlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + /* Allocate memory for Voronoi vertex attributes if necessary. */ + if (*vpointattriblist == (REAL *) NULL) { + *vpointattriblist = (REAL *) malloc(triangles.items * nextras * + sizeof(REAL)); + if (*vpointattriblist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + *vpointmarkerlist = (int *) NULL; + plist = *vpointlist; + palist = *vpointattriblist; + coordindex = 0; + attribindex = 0; +#else /* not TRILIBRARY */ + if (!quiet) { + printf("Writing %s.\n", vnodefilename); + } + outfile = fopen(vnodefilename, "w"); + if (outfile == (FILE *) NULL) { + printf(" Error: Cannot create file %s.\n", vnodefilename); + exit(1); + } + /* Number of triangles, two dimensions, number of point attributes, */ + /* zero markers. */ + fprintf(outfile, "%ld %d %d %d\n", triangles.items, 2, nextras, 0); +#endif /* not TRILIBRARY */ + + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + triangleloop.orient = 0; + vnodenumber = firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + org(triangleloop, torg); + dest(triangleloop, tdest); + apex(triangleloop, tapex); + findcircumcenter(torg, tdest, tapex, circumcenter, &xi, &eta); +#ifdef TRILIBRARY + /* X and y coordinates. */ + plist[coordindex++] = circumcenter[0]; + plist[coordindex++] = circumcenter[1]; + for (i = 2; i < 2 + nextras; i++) { + /* Interpolate the point attributes at the circumcenter. */ + palist[attribindex++] = torg[i] + xi * (tdest[i] - torg[i]) + + eta * (tapex[i] - torg[i]); + } +#else /* not TRILIBRARY */ + /* Voronoi vertex number, x and y coordinates. */ + fprintf(outfile, "%4d %.17g %.17g", vnodenumber, circumcenter[0], + circumcenter[1]); + for (i = 2; i < 2 + nextras; i++) { + /* Interpolate the point attributes at the circumcenter. */ + fprintf(outfile, " %.17g", torg[i] + xi * (tdest[i] - torg[i]) + + eta * (tapex[i] - torg[i])); + } + fprintf(outfile, "\n"); +#endif /* not TRILIBRARY */ + + * (int *) (triangleloop.tri + 6) = vnodenumber; + triangleloop.tri = triangletraverse(); + vnodenumber++; + } + +#ifndef TRILIBRARY + finishfile(outfile, argc, argv); +#endif /* not TRILIBRARY */ + +#ifdef TRILIBRARY + if (!quiet) { + printf("Writing Voronoi edges.\n"); + } + /* Allocate memory for output Voronoi edges if necessary. */ + if (*vedgelist == (int *) NULL) { + *vedgelist = (int *) malloc(edges * 2 * sizeof(int)); + if (*vedgelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + *vedgemarkerlist = (int *) NULL; + /* Allocate memory for output Voronoi norms if necessary. */ + if (*vnormlist == (REAL *) NULL) { + *vnormlist = (REAL *) malloc(edges * 2 * sizeof(REAL)); + if (*vnormlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + elist = *vedgelist; + normlist = *vnormlist; + coordindex = 0; +#else /* not TRILIBRARY */ + if (!quiet) { + printf("Writing %s.\n", vedgefilename); + } + outfile = fopen(vedgefilename, "w"); + if (outfile == (FILE *) NULL) { + printf(" Error: Cannot create file %s.\n", vedgefilename); + exit(1); + } + /* Number of edges, zero boundary markers. */ + fprintf(outfile, "%ld %d\n", edges, 0); +#endif /* not TRILIBRARY */ + + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + vedgenumber = firstnumber; + /* To loop over the set of edges, loop over all triangles, and look at */ + /* the three edges of each triangle. If there isn't another triangle */ + /* adjacent to the edge, operate on the edge. If there is another */ + /* adjacent triangle, operate on the edge only if the current triangle */ + /* has a smaller pointer than its neighbor. This way, each edge is */ + /* considered only once. */ + while (triangleloop.tri != (triangle *) NULL) { + for (triangleloop.orient = 0; triangleloop.orient < 3; + triangleloop.orient++) { + sym(triangleloop, trisym); + if ((triangleloop.tri < trisym.tri) || (trisym.tri == dummytri)) { + /* Find the number of this triangle (and Voronoi vertex). */ + p1 = * (int *) (triangleloop.tri + 6); + if (trisym.tri == dummytri) { + org(triangleloop, torg); + dest(triangleloop, tdest); +#ifdef TRILIBRARY + /* Copy an infinite ray. Index of one endpoint, and -1. */ + elist[coordindex] = p1; + normlist[coordindex++] = tdest[1] - torg[1]; + elist[coordindex] = -1; + normlist[coordindex++] = torg[0] - tdest[0]; +#else /* not TRILIBRARY */ + /* Write an infinite ray. Edge number, index of one endpoint, -1, */ + /* and x and y coordinates of a vector representing the */ + /* direction of the ray. */ + fprintf(outfile, "%4d %d %d %.17g %.17g\n", vedgenumber, + p1, -1, tdest[1] - torg[1], torg[0] - tdest[0]); +#endif /* not TRILIBRARY */ + } else { + /* Find the number of the adjacent triangle (and Voronoi vertex). */ + p2 = * (int *) (trisym.tri + 6); + /* Finite edge. Write indices of two endpoints. */ +#ifdef TRILIBRARY + elist[coordindex] = p1; + normlist[coordindex++] = 0.0; + elist[coordindex] = p2; + normlist[coordindex++] = 0.0; +#else /* not TRILIBRARY */ + fprintf(outfile, "%4d %d %d\n", vedgenumber, p1, p2); +#endif /* not TRILIBRARY */ + } + vedgenumber++; + } + } + triangleloop.tri = triangletraverse(); + } + +#ifndef TRILIBRARY + finishfile(outfile, argc, argv); +#endif /* not TRILIBRARY */ +} + +#ifdef TRILIBRARY + +void writeneighbors(neighborlist) +int **neighborlist; + +#else /* not TRILIBRARY */ + +void writeneighbors(neighborfilename, argc, argv) +char *neighborfilename; +int argc; +char **argv; + +#endif /* not TRILIBRARY */ + +{ +#ifdef TRILIBRARY + int *nlist; + int index; +#else /* not TRILIBRARY */ + FILE *outfile; +#endif /* not TRILIBRARY */ + struct triedge triangleloop, trisym; + int elementnumber; + int neighbor1, neighbor2, neighbor3; + triangle ptr; /* Temporary variable used by sym(). */ + +#ifdef TRILIBRARY + if (!quiet) { + printf("Writing neighbors.\n"); + } + /* Allocate memory for neighbors if necessary. */ + if (*neighborlist == (int *) NULL) { + *neighborlist = (int *) malloc(triangles.items * 3 * sizeof(int)); + if (*neighborlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + nlist = *neighborlist; + index = 0; +#else /* not TRILIBRARY */ + if (!quiet) { + printf("Writing %s.\n", neighborfilename); + } + outfile = fopen(neighborfilename, "w"); + if (outfile == (FILE *) NULL) { + printf(" Error: Cannot create file %s.\n", neighborfilename); + exit(1); + } + /* Number of triangles, three edges per triangle. */ + fprintf(outfile, "%ld %d\n", triangles.items, 3); +#endif /* not TRILIBRARY */ + + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + triangleloop.orient = 0; + elementnumber = firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + * (int *) (triangleloop.tri + 6) = elementnumber; + triangleloop.tri = triangletraverse(); + elementnumber++; + } + * (int *) (dummytri + 6) = -1; + + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + elementnumber = firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + triangleloop.orient = 1; + sym(triangleloop, trisym); + neighbor1 = * (int *) (trisym.tri + 6); + triangleloop.orient = 2; + sym(triangleloop, trisym); + neighbor2 = * (int *) (trisym.tri + 6); + triangleloop.orient = 0; + sym(triangleloop, trisym); + neighbor3 = * (int *) (trisym.tri + 6); +#ifdef TRILIBRARY + nlist[index++] = neighbor1; + nlist[index++] = neighbor2; + nlist[index++] = neighbor3; +#else /* not TRILIBRARY */ + /* Triangle number, neighboring triangle numbers. */ + fprintf(outfile, "%4d %d %d %d\n", elementnumber, + neighbor1, neighbor2, neighbor3); +#endif /* not TRILIBRARY */ + + triangleloop.tri = triangletraverse(); + elementnumber++; + } + +#ifndef TRILIBRARY + finishfile(outfile, argc, argv); +#endif /* TRILIBRARY */ +} + +/*****************************************************************************/ +/* */ +/* writeoff() Write the triangulation to an .off file. */ +/* */ +/* OFF stands for the Object File Format, a format used by the Geometry */ +/* Center's Geomview package. */ +/* */ +/*****************************************************************************/ + +#ifndef TRILIBRARY + +void writeoff(offfilename, argc, argv) +char *offfilename; +int argc; +char **argv; +{ + FILE *outfile; + struct triedge triangleloop; + point pointloop; + point p1, p2, p3; + + if (!quiet) { + printf("Writing %s.\n", offfilename); + } + outfile = fopen(offfilename, "w"); + if (outfile == (FILE *) NULL) { + printf(" Error: Cannot create file %s.\n", offfilename); + exit(1); + } + /* Number of points, triangles, and edges. */ + fprintf(outfile, "OFF\n%ld %ld %ld\n", points.items, triangles.items, + edges); + + /* Write the points. */ + traversalinit(&points); + pointloop = pointtraverse(); + while (pointloop != (point) NULL) { + /* The "0.0" is here because the OFF format uses 3D coordinates. */ + fprintf(outfile, " %.17g %.17g %.17g\n", pointloop[0], + pointloop[1], 0.0); + pointloop = pointtraverse(); + } + + /* Write the triangles. */ + traversalinit(&triangles); + triangleloop.tri = triangletraverse(); + triangleloop.orient = 0; + while (triangleloop.tri != (triangle *) NULL) { + org(triangleloop, p1); + dest(triangleloop, p2); + apex(triangleloop, p3); + /* The "3" means a three-vertex polygon. */ + fprintf(outfile, " 3 %4d %4d %4d\n", pointmark(p1) - 1, + pointmark(p2) - 1, pointmark(p3) - 1); + triangleloop.tri = triangletraverse(); + } + finishfile(outfile, argc, argv); +} + +#endif /* not TRILIBRARY */ + +/** **/ +/** **/ +/********* File I/O routines end here *********/ + +/*****************************************************************************/ +/* */ +/* quality_statistics() Print statistics about the quality of the mesh. */ +/* */ +/*****************************************************************************/ + +void quality_statistics() +{ + struct triedge triangleloop; + point 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 = xmax - xmin + ymax - 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(&triangles); + triangleloop.tri = triangletraverse(); + 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(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(); + } + + shortest = sqrt(shortest); + longest = sqrt(longest); + minaltitude = sqrt(minaltitude); + worstaspect = sqrt(worstaspect); + smallestarea *= 2.0; + biggestarea *= 2.0; + 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(" 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( +" (Triangle 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() +{ + printf("\nStatistics:\n\n"); + printf(" Input points: %d\n", inpoints); + if (refine) { + printf(" Input triangles: %d\n", inelements); + } + if (poly) { + printf(" Input segments: %d\n", insegments); + if (!refine) { + printf(" Input holes: %d\n", holes); + } + } + + printf("\n Mesh points: %ld\n", points.items); + printf(" Mesh triangles: %ld\n", triangles.items); + printf(" Mesh edges: %ld\n", edges); + if (poly || refine) { + printf(" Mesh boundary edges: %ld\n", hullsize); + printf(" Mesh segments: %ld\n\n", shelles.items); + } else { + printf(" Mesh convex hull edges: %ld\n\n", hullsize); + } + if (verbose) { + quality_statistics(); + printf("Memory allocation statistics:\n\n"); + printf(" Maximum number of points: %ld\n", points.maxitems); + printf(" Maximum number of triangles: %ld\n", triangles.maxitems); + if (shelles.maxitems > 0) { + printf(" Maximum number of segments: %ld\n", shelles.maxitems); + } + if (viri.maxitems > 0) { + printf(" Maximum number of viri: %ld\n", viri.maxitems); + } + if (badsegments.maxitems > 0) { + printf(" Maximum number of encroached segments: %ld\n", + badsegments.maxitems); + } + if (badtriangles.maxitems > 0) { + printf(" Maximum number of bad triangles: %ld\n", + badtriangles.maxitems); + } + if (splaynodes.maxitems > 0) { + printf(" Maximum number of splay tree nodes: %ld\n", + splaynodes.maxitems); + } + printf(" Approximate heap memory use (bytes): %ld\n\n", + points.maxitems * points.itembytes + + triangles.maxitems * triangles.itembytes + + shelles.maxitems * shelles.itembytes + + viri.maxitems * viri.itembytes + + badsegments.maxitems * badsegments.itembytes + + badtriangles.maxitems * badtriangles.itembytes + + splaynodes.maxitems * splaynodes.itembytes); + + printf("Algorithmic statistics:\n\n"); + printf(" Number of incircle tests: %ld\n", incirclecount); + printf(" Number of orientation tests: %ld\n", counterclockcount); + if (hyperbolacount > 0) { + printf(" Number of right-of-hyperbola tests: %ld\n", + hyperbolacount); + } + if (circumcentercount > 0) { + printf(" Number of circumcenter computations: %ld\n", + circumcentercount); + } + if (circletopcount > 0) { + printf(" Number of circle top computations: %ld\n", + circletopcount); + } + printf("\n"); + } +} + +/*****************************************************************************/ +/* */ +/* main() or triangulate() Gosh, do everything. */ +/* */ +/* The sequence is roughly as follows. Many of these steps can be skipped, */ +/* depending on the command line switches. */ +/* */ +/* - Initialize constants and parse the command line. */ +/* - Read the points from a file and either */ +/* - triangulate them (no -r), or */ +/* - read an old mesh from files and reconstruct it (-r). */ +/* - Insert the PSLG segments (-p), and possibly segments on the convex */ +/* hull (-c). */ +/* - Read the holes (-p), regional attributes (-pA), and regional area */ +/* constraints (-pa). Carve the holes and concavities, and spread the */ +/* regional attributes and area constraints. */ +/* - Enforce the constraints on minimum angle (-q) and maximum area (-a). */ +/* Also enforce the conforming Delaunay property (-q and -a). */ +/* - Compute the number of edges in the resulting mesh. */ +/* - Promote the mesh's linear triangles to higher order elements (-o). */ +/* - Write the output files and print the statistics. */ +/* - Check the consistency and Delaunay property of the mesh (-C). */ +/* */ +/*****************************************************************************/ + +#ifdef TRILIBRARY + +void triangulate(triswitches, in, out, vorout) +char *triswitches; +struct triangulateio *in; +struct triangulateio *out; +struct triangulateio *vorout; + +#else /* not TRILIBRARY */ + +int main(argc, argv) +int argc; +char **argv; + +#endif /* not TRILIBRARY */ + +{ + REAL *holearray; /* Array of holes. */ + REAL *regionarray; /* Array of regional attributes and area constraints. */ +#ifndef TRILIBRARY + FILE *polyfile; +#endif /* not TRILIBRARY */ +#ifndef NO_TIMER + /* Variables for timing the performance of Triangle. The types are */ + /* defined in sys/time.h. */ + struct timeval tv0, tv1, tv2, tv3, tv4, tv5, tv6; + struct timezone tz; +#endif /* NO_TIMER */ + +#ifndef NO_TIMER + gettimeofday(&tv0, &tz); +#endif /* NO_TIMER */ + + triangleinit(); +#ifdef TRILIBRARY + parsecommandline(1, &triswitches); +#else /* not TRILIBRARY */ + parsecommandline(argc, argv); +#endif /* not TRILIBRARY */ + +#ifdef TRILIBRARY + transfernodes(in->pointlist, in->pointattributelist, in->pointmarkerlist, + in->numberofpoints, in->numberofpointattributes); +#else /* not TRILIBRARY */ + readnodes(innodefilename, inpolyfilename, &polyfile); +#endif /* not TRILIBRARY */ + +#ifndef NO_TIMER + if (!quiet) { + gettimeofday(&tv1, &tz); + } +#endif /* NO_TIMER */ + +#ifdef CDT_ONLY + hullsize = delaunay(); /* Triangulate the points. */ +#else /* not CDT_ONLY */ + if (refine) { + /* Read and reconstruct a mesh. */ +#ifdef TRILIBRARY + hullsize = reconstruct(in->trianglelist, in->triangleattributelist, + in->trianglearealist, in->numberoftriangles, + in->numberofcorners, in->numberoftriangleattributes, + in->segmentlist, in->segmentmarkerlist, + in->numberofsegments); +#else /* not TRILIBRARY */ + hullsize = reconstruct(inelefilename, areafilename, inpolyfilename, + polyfile); +#endif /* not TRILIBRARY */ + } else { + hullsize = delaunay(); /* Triangulate the points. */ + } +#endif /* not CDT_ONLY */ + +#ifndef NO_TIMER + if (!quiet) { + gettimeofday(&tv2, &tz); + if (refine) { + printf("Mesh reconstruction"); + } else { + printf("Delaunay"); + } + printf(" milliseconds: %ld\n", 1000l * (tv2.tv_sec - tv1.tv_sec) + + (tv2.tv_usec - tv1.tv_usec) / 1000l); + } +#endif /* NO_TIMER */ + + /* Ensure that no point can be mistaken for a triangular bounding */ + /* box point in insertsite(). */ + infpoint1 = (point) NULL; + infpoint2 = (point) NULL; + infpoint3 = (point) NULL; + + if (useshelles) { + checksegments = 1; /* Segments will be introduced next. */ + if (!refine) { + /* Insert PSLG segments and/or convex hull segments. */ +#ifdef TRILIBRARY + insegments = formskeleton(in->segmentlist, in->segmentmarkerlist, + in->numberofsegments); +#else /* not TRILIBRARY */ + insegments = formskeleton(polyfile, inpolyfilename); +#endif /* not TRILIBRARY */ + } + } + +#ifndef NO_TIMER + if (!quiet) { + gettimeofday(&tv3, &tz); + if (useshelles && !refine) { + printf("Segment milliseconds: %ld\n", + 1000l * (tv3.tv_sec - tv2.tv_sec) + + (tv3.tv_usec - tv2.tv_usec) / 1000l); + } + } +#endif /* NO_TIMER */ + + if (poly) { +#ifdef TRILIBRARY + holearray = in->holelist; + holes = in->numberofholes; + regionarray = in->regionlist; + regions = in->numberofregions; +#else /* not TRILIBRARY */ + readholes(polyfile, inpolyfilename, &holearray, &holes, + ®ionarray, ®ions); +#endif /* not TRILIBRARY */ + if (!refine) { + /* Carve out holes and concavities. */ + carveholes(holearray, holes, regionarray, regions); + } + } else { + /* Without a PSLG, there can be no holes or regional attributes */ + /* or area constraints. The following are set to zero to avoid */ + /* an accidental free() later. */ + holes = 0; + regions = 0; + } + +#ifndef NO_TIMER + if (!quiet) { + gettimeofday(&tv4, &tz); + if (poly && !refine) { + printf("Hole milliseconds: %ld\n", 1000l * (tv4.tv_sec - tv3.tv_sec) + + (tv4.tv_usec - tv3.tv_usec) / 1000l); + } + } +#endif /* NO_TIMER */ + +#ifndef CDT_ONLY + if (quality) { + enforcequality(); /* Enforce angle and area constraints. */ + } +#endif /* not CDT_ONLY */ + +#ifndef NO_TIMER + if (!quiet) { + gettimeofday(&tv5, &tz); +#ifndef CDT_ONLY + if (quality) { + printf("Quality milliseconds: %ld\n", + 1000l * (tv5.tv_sec - tv4.tv_sec) + + (tv5.tv_usec - tv4.tv_usec) / 1000l); + } +#endif /* not CDT_ONLY */ + } +#endif /* NO_TIMER */ + + /* Compute the number of edges. */ + edges = (3l * triangles.items + hullsize) / 2l; + + if (order > 1) { + highorder(); /* Promote elements to higher polynomial order. */ + } + if (!quiet) { + printf("\n"); + } + +#ifdef TRILIBRARY + out->numberofpoints = points.items; + out->numberofpointattributes = nextras; + out->numberoftriangles = triangles.items; + out->numberofcorners = (order + 1) * (order + 2) / 2; + out->numberoftriangleattributes = eextras; + out->numberofedges = edges; + if (useshelles) { + out->numberofsegments = shelles.items; + } else { + out->numberofsegments = hullsize; + } + if (vorout != (struct triangulateio *) NULL) { + vorout->numberofpoints = triangles.items; + vorout->numberofpointattributes = nextras; + vorout->numberofedges = edges; + } +#endif /* TRILIBRARY */ + /* If not using iteration numbers, don't write a .node file if one was */ + /* read, because the original one would be overwritten! */ + if (nonodewritten || (noiterationnum && readnodefile)) { + if (!quiet) { +#ifdef TRILIBRARY + printf("NOT writing points.\n"); +#else /* not TRILIBRARY */ + printf("NOT writing a .node file.\n"); +#endif /* not TRILIBRARY */ + } + numbernodes(); /* We must remember to number the points. */ + } else { +#ifdef TRILIBRARY + writenodes(&out->pointlist, &out->pointattributelist, + &out->pointmarkerlist); +#else /* not TRILIBRARY */ + writenodes(outnodefilename, argc, argv); /* Numbers the points too. */ +#endif /* TRILIBRARY */ + } + if (noelewritten) { + if (!quiet) { +#ifdef TRILIBRARY + printf("NOT writing triangles.\n"); +#else /* not TRILIBRARY */ + printf("NOT writing an .ele file.\n"); +#endif /* not TRILIBRARY */ + } + } else { +#ifdef TRILIBRARY + writeelements(&out->trianglelist, &out->triangleattributelist); +#else /* not TRILIBRARY */ + writeelements(outelefilename, argc, argv); +#endif /* not TRILIBRARY */ + } + /* The -c switch (convex switch) causes a PSLG to be written */ + /* even if none was read. */ + if (poly || convex) { + /* If not using iteration numbers, don't overwrite the .poly file. */ + if (nopolywritten || noiterationnum) { + if (!quiet) { +#ifdef TRILIBRARY + printf("NOT writing segments.\n"); +#else /* not TRILIBRARY */ + printf("NOT writing a .poly file.\n"); +#endif /* not TRILIBRARY */ + } + } else { +#ifdef TRILIBRARY + writepoly(&out->segmentlist, &out->segmentmarkerlist); + out->numberofholes = holes; + out->numberofregions = regions; + if (poly) { + out->holelist = in->holelist; + out->regionlist = in->regionlist; + } else { + out->holelist = (REAL *) NULL; + out->regionlist = (REAL *) NULL; + } +#else /* not TRILIBRARY */ + writepoly(outpolyfilename, holearray, holes, regionarray, regions, + argc, argv); +#endif /* not TRILIBRARY */ + } + } +#ifndef TRILIBRARY +#ifndef CDT_ONLY + if (regions > 0) { + free(regionarray); + } +#endif /* not CDT_ONLY */ + if (holes > 0) { + free(holearray); + } + if (geomview) { + writeoff(offfilename, argc, argv); + } +#endif /* not TRILIBRARY */ + if (edgesout) { +#ifdef TRILIBRARY + writeedges(&out->edgelist, &out->edgemarkerlist); +#else /* not TRILIBRARY */ + writeedges(edgefilename, argc, argv); +#endif /* not TRILIBRARY */ + } + if (voronoi) { +#ifdef TRILIBRARY + writevoronoi(&vorout->pointlist, &vorout->pointattributelist, + &vorout->pointmarkerlist, &vorout->edgelist, + &vorout->edgemarkerlist, &vorout->normlist); +#else /* not TRILIBRARY */ + writevoronoi(vnodefilename, vedgefilename, argc, argv); +#endif /* not TRILIBRARY */ + } + if (neighbors) { +#ifdef TRILIBRARY + writeneighbors(&out->neighborlist); +#else /* not TRILIBRARY */ + writeneighbors(neighborfilename, argc, argv); +#endif /* not TRILIBRARY */ + } + + if (!quiet) { +#ifndef NO_TIMER + gettimeofday(&tv6, &tz); + printf("\nOutput milliseconds: %ld\n", + 1000l * (tv6.tv_sec - tv5.tv_sec) + + (tv6.tv_usec - tv5.tv_usec) / 1000l); + printf("Total running milliseconds: %ld\n", + 1000l * (tv6.tv_sec - tv0.tv_sec) + + (tv6.tv_usec - tv0.tv_usec) / 1000l); +#endif /* NO_TIMER */ + + statistics(); + } + +#ifndef REDUCED + if (docheck) { + checkmesh(); + checkdelaunay(); + } +#endif /* not REDUCED */ + + triangledeinit(); +#ifndef TRILIBRARY + return 0; +#endif /* not TRILIBRARY */ +} diff --git a/Tools/Triangle/triangle.doc b/Tools/Triangle/triangle.doc new file mode 100644 index 000000000..28b633978 --- /dev/null +++ b/Tools/Triangle/triangle.doc @@ -0,0 +1,817 @@ +Triangle +A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. +Version 1.3 + +Copyright 1996 Jonathan Richard Shewchuk (bugs/comments to jrs@cs.cmu.edu) +School of Computer Science / Carnegie Mellon University +5000 Forbes Avenue / Pittsburgh, Pennsylvania 15213-3891 +Created as part of the Archimedes project (tools for parallel FEM). +Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship. +There is no warranty whatsoever. Use at your own risk. +This executable is compiled for double precision arithmetic. + + +Triangle generates exact Delaunay triangulations, constrained Delaunay +triangulations, and quality conforming Delaunay triangulations. The latter +can be generated with no small angles, and are thus suitable for finite +element analysis. If no command line switches are specified, your .node +input file will be read, and the Delaunay triangulation will be returned in +.node and .ele output files. The command syntax is: + +triangle [-prq__a__AcevngBPNEIOXzo_YS__iFlsCQVh] input_file + +Underscores indicate that numbers may optionally follow certain switches; +do not leave any space between a switch and its numeric parameter. +input_file must be a file with extension .node, or extension .poly if the +-p switch is used. If -r is used, you must supply .node and .ele files, +and possibly a .poly file and .area file as well. The formats of these +files are described below. + +Command Line Switches: + + -p Reads a Planar Straight Line Graph (.poly file), which can specify + points, segments, holes, and regional attributes and area + constraints. Will generate a constrained Delaunay triangulation + fitting the input; or, if -s, -q, or -a is used, a conforming + Delaunay triangulation. If -p is not used, Triangle reads a .node + file by default. + -r Refines a previously generated mesh. The mesh is read from a .node + file and an .ele file. If -p is also used, a .poly file is read + and used to constrain edges in the mesh. Further details on + refinement are given below. + -q Quality mesh generation by Jim Ruppert's Delaunay refinement + algorithm. Adds points to the mesh to ensure that no angles + smaller than 20 degrees occur. An alternative minimum angle may be + specified after the `q'. If the minimum angle is 20.7 degrees or + smaller, the triangulation algorithm is theoretically guaranteed to + terminate (assuming infinite precision arithmetic - Triangle may + fail to terminate if you run out of precision). In practice, the + algorithm often succeeds for minimum angles up to 33.8 degrees. + For highly refined meshes, however, it may be necessary to reduce + the minimum angle to well below 20 to avoid problems associated + with insufficient floating-point precision. The specified angle + may include a decimal point. + -a Imposes a maximum triangle area. If a number follows the `a', no + triangle will be generated whose area is larger than that number. + If no number is specified, an .area file (if -r is used) or .poly + file (if -r is not used) specifies a number of maximum area + constraints. An .area file contains a separate area constraint for + each triangle, and is useful for refining a finite element mesh + based on a posteriori error estimates. A .poly file can optionally + contain an area constraint for each segment-bounded region, thereby + enforcing triangle densities in a first triangulation. You can + impose both a fixed area constraint and a varying area constraint + by invoking the -a switch twice, once with and once without a + number following. Each area specified may include a decimal point. + -A Assigns an additional attribute to each triangle that identifies + what segment-bounded region each triangle belongs to. Attributes + are assigned to regions by the .poly file. If a region is not + explicitly marked by the .poly file, triangles in that region are + assigned an attribute of zero. The -A switch has an effect only + when the -p switch is used and the -r switch is not. + -c Creates segments on the convex hull of the triangulation. If you + are triangulating a point set, this switch causes a .poly file to + be written, containing all edges in the convex hull. (By default, + a .poly file is written only if a .poly file is read.) If you are + triangulating a PSLG, this switch specifies that the interior of + the convex hull of the PSLG should be triangulated. If you do not + use this switch when triangulating a PSLG, it is assumed that you + have identified the region to be triangulated by surrounding it + with segments of the input PSLG. Beware: if you are not careful, + this switch can cause the introduction of an extremely thin angle + between a PSLG segment and a convex hull segment, which can cause + overrefinement or failure if Triangle runs out of precision. If + you are refining a mesh, the -c switch works differently; it + generates the set of boundary edges of the mesh, rather than the + convex hull. + -e Outputs (to an .edge file) a list of edges of the triangulation. + -v Outputs the Voronoi diagram associated with the triangulation. + Does not attempt to detect degeneracies. + -n Outputs (to a .neigh file) a list of triangles neighboring each + triangle. + -g Outputs the mesh to an Object File Format (.off) file, suitable for + viewing with the Geometry Center's Geomview package. + -B No boundary markers in the output .node, .poly, and .edge output + files. See the detailed discussion of boundary markers below. + -P No output .poly file. Saves disk space, but you lose the ability + to impose segment constraints on later refinements of the mesh. + -N No output .node file. + -E No output .ele file. + -I No iteration numbers. Suppresses the output of .node and .poly + files, so your input files won't be overwritten. (If your input is + a .poly file only, a .node file will be written.) Cannot be used + with the -r switch, because that would overwrite your input .ele + file. Shouldn't be used with the -s, -q, or -a switch if you are + using a .node file for input, because no .node file will be + written, so there will be no record of any added points. + -O No holes. Ignores the holes in the .poly file. + -X No exact arithmetic. Normally, Triangle uses exact floating-point + arithmetic for certain tests if it thinks the inexact tests are not + accurate enough. Exact arithmetic ensures the robustness of the + triangulation algorithms, despite floating-point roundoff error. + Disabling exact arithmetic with the -X switch will cause a small + improvement in speed and create the possibility (albeit small) that + Triangle will fail to produce a valid mesh. Not recommended. + -z Numbers all items starting from zero (rather than one). Note that + this switch is normally overrided by the value used to number the + first point of the input .node or .poly file. However, this switch + is useful when calling Triangle from another program. + -o2 Generates second-order subparametric elements with six nodes each. + -Y No new points on the boundary. This switch is useful when the mesh + boundary must be preserved so that it conforms to some adjacent + mesh. Be forewarned that you will probably sacrifice some of the + quality of the mesh; Triangle will try, but the resulting mesh may + contain triangles of poor aspect ratio. Works well if all the + boundary points are closely spaced. Specify this switch twice + (`-YY') to prevent all segment splitting, including internal + boundaries. + -S Specifies the maximum number of Steiner points (points that are not + in the input, but are added to meet the constraints of minimum + angle and maximum area). The default is to allow an unlimited + number. If you specify this switch with no number after it, + the limit is set to zero. Triangle always adds points at segment + intersections, even if it needs to use more points than the limit + you set. When Triangle inserts segments by splitting (-s), it + always adds enough points to ensure that all the segments appear in + the triangulation, again ignoring the limit. Be forewarned that + the -S switch may result in a conforming triangulation that is not + truly Delaunay, because Triangle may be forced to stop adding + points when the mesh is in a state where a segment is non-Delaunay + and needs to be split. If so, Triangle will print a warning. + -i Uses an incremental rather than divide-and-conquer algorithm to + form a Delaunay triangulation. Try it if the divide-and-conquer + algorithm fails. + -F Uses Steven Fortune's sweepline algorithm to form a Delaunay + triangulation. Warning: does not use exact arithmetic for all + calculations. An exact result is not guaranteed. + -l Uses only vertical cuts in the divide-and-conquer algorithm. By + default, Triangle uses alternating vertical and horizontal cuts, + which usually improve the speed except with point sets that are + small or short and wide. This switch is primarily of theoretical + interest. + -s Specifies that segments should be forced into the triangulation by + recursively splitting them at their midpoints, rather than by + generating a constrained Delaunay triangulation. Segment splitting + is true to Ruppert's original algorithm, but can create needlessly + small triangles near external small features. + -C Check the consistency of the final mesh. Uses exact arithmetic for + checking, even if the -X switch is used. Useful if you suspect + Triangle is buggy. + -Q Quiet: Suppresses all explanation of what Triangle is doing, unless + an error occurs. + -V Verbose: Gives detailed information about what Triangle is doing. + Add more `V's for increasing amount of detail. `-V' gives + information on algorithmic progress and more detailed statistics. + `-VV' gives point-by-point details, and will print so much that + Triangle will run much more slowly. `-VVV' gives information only + a debugger could love. + -h Help: Displays these instructions. + +Definitions: + + A Delaunay triangulation of a point set is a triangulation whose vertices + are the point set, having the property that no point in the point set + falls in the interior of the circumcircle (circle that passes through all + three vertices) of any triangle in the triangulation. + + A Voronoi diagram of a point set is a subdivision of the plane into + polygonal regions (some of which may be infinite), where each region is + the set of points in the plane that are closer to some input point than + to any other input point. (The Voronoi diagram is the geometric dual of + the Delaunay triangulation.) + + A Planar Straight Line Graph (PSLG) is a collection of points and + segments. Segments are simply edges, whose endpoints are points in the + PSLG. The file format for PSLGs (.poly files) is described below. + + A constrained Delaunay triangulation of a PSLG is similar to a Delaunay + triangulation, but each PSLG segment is present as a single edge in the + triangulation. (A constrained Delaunay triangulation is not truly a + Delaunay triangulation.) + + A conforming Delaunay triangulation of a PSLG is a true Delaunay + triangulation in which each PSLG segment may have been subdivided into + several edges by the insertion of additional points. These inserted + points are necessary to allow the segments to exist in the mesh while + maintaining the Delaunay property. + +File Formats: + + All files may contain comments prefixed by the character '#'. Points, + triangles, edges, holes, and maximum area constraints must be numbered + consecutively, starting from either 1 or 0. Whichever you choose, all + input files must be consistent; if the nodes are numbered from 1, so must + be all other objects. Triangle automatically detects your choice while + reading the .node (or .poly) file. (When calling Triangle from another + program, use the -z switch if you wish to number objects from zero.) + Examples of these file formats are given below. + + .node files: + First line: <# of points> <dimension (must be 2)> <# of attributes> + <# of boundary markers (0 or 1)> + Remaining lines: <point #> <x> <y> [attributes] [boundary marker] + + The attributes, which are typically floating-point values of physical + quantities (such as mass or conductivity) associated with the nodes of + a finite element mesh, are copied unchanged to the output mesh. If -s, + -q, or -a is selected, each new Steiner point added to the mesh will + have attributes assigned to it by linear interpolation. + + If the fourth entry of the first line is `1', the last column of the + remainder of the file is assumed to contain boundary markers. Boundary + markers are used to identify boundary points and points resting on PSLG + segments; a complete description appears in a section below. The .node + file produced by Triangle will contain boundary markers in the last + column unless they are suppressed by the -B switch. + + .ele files: + First line: <# of triangles> <points per triangle> <# of attributes> + Remaining lines: <triangle #> <point> <point> <point> ... [attributes] + + Points are indices into the corresponding .node file. The first three + points are the corners, and are listed in counterclockwise order around + each triangle. (The remaining points, if any, depend on the type of + finite element used.) The attributes are just like those of .node + files. Because there is no simple mapping from input to output + triangles, an attempt is made to interpolate attributes, which may + result in a good deal of diffusion of attributes among nearby triangles + as the triangulation is refined. Diffusion does not occur across + segments, so attributes used to identify segment-bounded regions remain + intact. In output .ele files, all triangles have three points each + unless the -o2 switch is used, in which case they have six, and the + fourth, fifth, and sixth points lie on the midpoints of the edges + opposite the first, second, and third corners. + + .poly files: + First line: <# of points> <dimension (must be 2)> <# of attributes> + <# of boundary markers (0 or 1)> + Following lines: <point #> <x> <y> [attributes] [boundary marker] + One line: <# of segments> <# of boundary markers (0 or 1)> + Following lines: <segment #> <endpoint> <endpoint> [boundary marker] + One line: <# of holes> + Following lines: <hole #> <x> <y> + Optional line: <# of regional attributes and/or area constraints> + Optional following lines: <constraint #> <x> <y> <attrib> <max area> + + A .poly file represents a PSLG, as well as some additional information. + The first section lists all the points, and is identical to the format + of .node files. <# of points> may be set to zero to indicate that the + points are listed in a separate .node file; .poly files produced by + Triangle always have this format. This has the advantage that a point + set may easily be triangulated with or without segments. (The same + effect can be achieved, albeit using more disk space, by making a copy + of the .poly file with the extension .node; all sections of the file + but the first are ignored.) + + The second section lists the segments. Segments are edges whose + presence in the triangulation is enforced. Each segment is specified + by listing the indices of its two endpoints. This means that you must + include its endpoints in the point list. If -s, -q, and -a are not + selected, Triangle will produce a constrained Delaunay triangulation, + in which each segment appears as a single edge in the triangulation. + If -q or -a is selected, Triangle will produce a conforming Delaunay + triangulation, in which segments may be subdivided into smaller edges. + Each segment, like each point, may have a boundary marker. + + The third section lists holes (and concavities, if -c is selected) in + the triangulation. Holes are specified by identifying a point inside + each hole. After the triangulation is formed, Triangle creates holes + by eating triangles, spreading out from each hole point until its + progress is blocked by PSLG segments; you must be careful to enclose + each hole in segments, or your whole triangulation may be eaten away. + If the two triangles abutting a segment are eaten, the segment itself + is also eaten. Do not place a hole directly on a segment; if you do, + Triangle will choose one side of the segment arbitrarily. + + The optional fourth section lists regional attributes (to be assigned + to all triangles in a region) and regional constraints on the maximum + triangle area. Triangle will read this section only if the -A switch + is used or the -a switch is used without a number following it, and the + -r switch is not used. Regional attributes and area constraints are + propagated in the same manner as holes; you specify a point for each + attribute and/or constraint, and the attribute and/or constraint will + affect the whole region (bounded by segments) containing the point. If + two values are written on a line after the x and y coordinate, the + former is assumed to be a regional attribute (but will only be applied + if the -A switch is selected), and the latter is assumed to be a + regional area constraint (but will only be applied if the -a switch is + selected). You may also specify just one value after the coordinates, + which can serve as both an attribute and an area constraint, depending + on the choice of switches. If you are using the -A and -a switches + simultaneously and wish to assign an attribute to some region without + imposing an area constraint, use a negative maximum area. + + When a triangulation is created from a .poly file, you must either + enclose the entire region to be triangulated in PSLG segments, or + use the -c switch, which encloses the convex hull of the input point + set. If you do not use the -c switch, Triangle will eat all triangles + on the outer boundary that are not protected by segments; if you are + not careful, your whole triangulation may be eaten away. If you do + use the -c switch, you can still produce concavities by appropriate + placement of holes just inside the convex hull. + + An ideal PSLG has no intersecting segments, nor any points that lie + upon segments (except, of course, the endpoints of each segment.) You + aren't required to make your .poly files ideal, but you should be aware + of what can go wrong. Segment intersections are relatively safe - + Triangle will calculate the intersection points for you and add them to + the triangulation - as long as your machine's floating-point precision + doesn't become a problem. You are tempting the fates if you have three + segments that cross at the same location, and expect Triangle to figure + out where the intersection point is. Thanks to floating-point roundoff + error, Triangle will probably decide that the three segments intersect + at three different points, and you will find a minuscule triangle in + your output - unless Triangle tries to refine the tiny triangle, uses + up the last bit of machine precision, and fails to terminate at all. + You're better off putting the intersection point in the input files, + and manually breaking up each segment into two. Similarly, if you + place a point at the middle of a segment, and hope that Triangle will + break up the segment at that point, you might get lucky. On the other + hand, Triangle might decide that the point doesn't lie precisely on the + line, and you'll have a needle-sharp triangle in your output - or a lot + of tiny triangles if you're generating a quality mesh. + + When Triangle reads a .poly file, it also writes a .poly file, which + includes all edges that are part of input segments. If the -c switch + is used, the output .poly file will also include all of the edges on + the convex hull. Hence, the output .poly file is useful for finding + edges associated with input segments and setting boundary conditions in + finite element simulations. More importantly, you will need it if you + plan to refine the output mesh, and don't want segments to be missing + in later triangulations. + + .area files: + First line: <# of triangles> + Following lines: <triangle #> <maximum area> + + An .area file associates with each triangle a maximum area that is used + for mesh refinement. As with other file formats, every triangle must + be represented, and they must be numbered consecutively. A triangle + may be left unconstrained by assigning it a negative maximum area. + + .edge files: + First line: <# of edges> <# of boundary markers (0 or 1)> + Following lines: <edge #> <endpoint> <endpoint> [boundary marker] + + Endpoints are indices into the corresponding .node file. Triangle can + produce .edge files (use the -e switch), but cannot read them. The + optional column of boundary markers is suppressed by the -B switch. + + In Voronoi diagrams, one also finds a special kind of edge that is an + infinite ray with only one endpoint. For these edges, a different + format is used: + + <edge #> <endpoint> -1 <direction x> <direction y> + + The `direction' is a floating-point vector that indicates the direction + of the infinite ray. + + .neigh files: + First line: <# of triangles> <# of neighbors per triangle (always 3)> + Following lines: <triangle #> <neighbor> <neighbor> <neighbor> + + Neighbors are indices into the corresponding .ele file. An index of -1 + indicates a mesh boundary, and therefore no neighbor. Triangle can + produce .neigh files (use the -n switch), but cannot read them. + + The first neighbor of triangle i is opposite the first corner of + triangle i, and so on. + +Boundary Markers: + + Boundary markers are tags used mainly to identify which output points and + edges are associated with which PSLG segment, and to identify which + points and edges occur on a boundary of the triangulation. A common use + is to determine where boundary conditions should be applied to a finite + element mesh. You can prevent boundary markers from being written into + files produced by Triangle by using the -B switch. + + The boundary marker associated with each segment in an output .poly file + or edge in an output .edge file is chosen as follows: + - If an output edge is part or all of a PSLG segment with a nonzero + boundary marker, then the edge is assigned the same marker. + - Otherwise, if the edge occurs on a boundary of the triangulation + (including boundaries of holes), then the edge is assigned the marker + one (1). + - Otherwise, the edge is assigned the marker zero (0). + The boundary marker associated with each point in an output .node file is + chosen as follows: + - If a point is assigned a nonzero boundary marker in the input file, + then it is assigned the same marker in the output .node file. + - Otherwise, if the point lies on a PSLG segment (including the + segment's endpoints) with a nonzero boundary marker, then the point + is assigned the same marker. If the point lies on several such + segments, one of the markers is chosen arbitrarily. + - Otherwise, if the point occurs on a boundary of the triangulation, + then the point is assigned the marker one (1). + - Otherwise, the point is assigned the marker zero (0). + + If you want Triangle to determine for you which points and edges are on + the boundary, assign them the boundary marker zero (or use no markers at + all) in your input files. Alternatively, you can mark some of them and + leave others marked zero, allowing Triangle to label them. + +Triangulation Iteration Numbers: + + Because Triangle can read and refine its own triangulations, input + and output files have iteration numbers. For instance, Triangle might + read the files mesh.3.node, mesh.3.ele, and mesh.3.poly, refine the + triangulation, and output the files mesh.4.node, mesh.4.ele, and + mesh.4.poly. Files with no iteration number are treated as if + their iteration number is zero; hence, Triangle might read the file + points.node, triangulate it, and produce the files points.1.node and + points.1.ele. + + Iteration numbers allow you to create a sequence of successively finer + meshes suitable for multigrid methods. They also allow you to produce a + sequence of meshes using error estimate-driven mesh refinement. + + If you're not using refinement or quality meshing, and you don't like + iteration numbers, use the -I switch to disable them. This switch will + also disable output of .node and .poly files to prevent your input files + from being overwritten. (If the input is a .poly file that contains its + own points, a .node file will be written.) + +Examples of How to Use Triangle: + + `triangle dots' will read points from dots.node, and write their Delaunay + triangulation to dots.1.node and dots.1.ele. (dots.1.node will be + identical to dots.node.) `triangle -I dots' writes the triangulation to + dots.ele instead. (No additional .node file is needed, so none is + written.) + + `triangle -pe object.1' will read a PSLG from object.1.poly (and possibly + object.1.node, if the points are omitted from object.1.poly) and write + their constrained Delaunay triangulation to object.2.node and + object.2.ele. The segments will be copied to object.2.poly, and all + edges will be written to object.2.edge. + + `triangle -pq31.5a.1 object' will read a PSLG from object.poly (and + possibly object.node), generate a mesh whose angles are all greater than + 31.5 degrees and whose triangles all have area smaller than 0.1, and + write the mesh to object.1.node and object.1.ele. Each segment may have + been broken up into multiple edges; the resulting constrained edges are + written to object.1.poly. + + Here is a sample file `box.poly' describing a square with a square hole: + + # A box with eight points in 2D, no attributes, one boundary marker. + 8 2 0 1 + # Outer box has these vertices: + 1 0 0 0 + 2 0 3 0 + 3 3 0 0 + 4 3 3 33 # A special marker for this point. + # Inner square has these vertices: + 5 1 1 0 + 6 1 2 0 + 7 2 1 0 + 8 2 2 0 + # Five segments with boundary markers. + 5 1 + 1 1 2 5 # Left side of outer box. + 2 5 7 0 # Segments 2 through 5 enclose the hole. + 3 7 8 0 + 4 8 6 10 + 5 6 5 0 + # One hole in the middle of the inner square. + 1 + 1 1.5 1.5 + + Note that some segments are missing from the outer square, so one must + use the `-c' switch. After `triangle -pqc box.poly', here is the output + file `box.1.node', with twelve points. The last four points were added + to meet the angle constraint. Points 1, 2, and 9 have markers from + segment 1. Points 6 and 8 have markers from segment 4. All the other + points but 4 have been marked to indicate that they lie on a boundary. + + 12 2 0 1 + 1 0 0 5 + 2 0 3 5 + 3 3 0 1 + 4 3 3 33 + 5 1 1 1 + 6 1 2 10 + 7 2 1 1 + 8 2 2 10 + 9 0 1.5 5 + 10 1.5 0 1 + 11 3 1.5 1 + 12 1.5 3 1 + # Generated by triangle -pqc box.poly + + Here is the output file `box.1.ele', with twelve triangles. + + 12 3 0 + 1 5 6 9 + 2 10 3 7 + 3 6 8 12 + 4 9 1 5 + 5 6 2 9 + 6 7 3 11 + 7 11 4 8 + 8 7 5 10 + 9 12 2 6 + 10 8 7 11 + 11 5 1 10 + 12 8 4 12 + # Generated by triangle -pqc box.poly + + Here is the output file `box.1.poly'. Note that segments have been added + to represent the convex hull, and some segments have been split by newly + added points. Note also that <# of points> is set to zero to indicate + that the points should be read from the .node file. + + 0 2 0 1 + 12 1 + 1 1 9 5 + 2 5 7 1 + 3 8 7 1 + 4 6 8 10 + 5 5 6 1 + 6 3 10 1 + 7 4 11 1 + 8 2 12 1 + 9 9 2 5 + 10 10 1 1 + 11 11 3 1 + 12 12 4 1 + 1 + 1 1.5 1.5 + # Generated by triangle -pqc box.poly + +Refinement and Area Constraints: + + The -r switch causes a mesh (.node and .ele files) to be read and + refined. If the -p switch is also used, a .poly file is read and used to + specify edges that are constrained and cannot be eliminated (although + they can be divided into smaller edges) by the refinement process. + + When you refine a mesh, you generally want to impose tighter quality + constraints. One way to accomplish this is to use -q with a larger + angle, or -a followed by a smaller area than you used to generate the + mesh you are refining. Another way to do this is to create an .area + file, which specifies a maximum area for each triangle, and use the -a + switch (without a number following). Each triangle's area constraint is + applied to that triangle. Area constraints tend to diffuse as the mesh + is refined, so if there are large variations in area constraint between + adjacent triangles, you may not get the results you want. + + If you are refining a mesh composed of linear (three-node) elements, the + output mesh will contain all the nodes present in the input mesh, in the + same order, with new nodes added at the end of the .node file. However, + there is no guarantee that each output element is contained in a single + input element. Often, output elements will overlap two input elements, + and input edges are not present in the output mesh. Hence, a sequence of + refined meshes will form a hierarchy of nodes, but not a hierarchy of + elements. If you a refining a mesh of higher-order elements, the + hierarchical property applies only to the nodes at the corners of an + element; other nodes may not be present in the refined mesh. + + It is important to understand that maximum area constraints in .poly + files are handled differently from those in .area files. A maximum area + in a .poly file applies to the whole (segment-bounded) region in which a + point falls, whereas a maximum area in an .area file applies to only one + triangle. Area constraints in .poly files are used only when a mesh is + first generated, whereas area constraints in .area files are used only to + refine an existing mesh, and are typically based on a posteriori error + estimates resulting from a finite element simulation on that mesh. + + `triangle -rq25 object.1' will read object.1.node and object.1.ele, then + refine the triangulation to enforce a 25 degree minimum angle, and then + write the refined triangulation to object.2.node and object.2.ele. + + `triangle -rpaa6.2 z.3' will read z.3.node, z.3.ele, z.3.poly, and + z.3.area. After reconstructing the mesh and its segments, Triangle will + refine the mesh so that no triangle has area greater than 6.2, and + furthermore the triangles satisfy the maximum area constraints in + z.3.area. The output is written to z.4.node, z.4.ele, and z.4.poly. + + The sequence `triangle -qa1 x', `triangle -rqa.3 x.1', `triangle -rqa.1 + x.2' creates a sequence of successively finer meshes x.1, x.2, and x.3, + suitable for multigrid. + +Convex Hulls and Mesh Boundaries: + + If the input is a point set (rather than a PSLG), Triangle produces its + convex hull as a by-product in the output .poly file if you use the -c + switch. There are faster algorithms for finding a two-dimensional convex + hull than triangulation, of course, but this one comes for free. If the + input is an unconstrained mesh (you are using the -r switch but not the + -p switch), Triangle produces a list of its boundary edges (including + hole boundaries) as a by-product if you use the -c switch. + +Voronoi Diagrams: + + The -v switch produces a Voronoi diagram, in files suffixed .v.node and + .v.edge. For example, `triangle -v points' will read points.node, + produce its Delaunay triangulation in points.1.node and points.1.ele, + and produce its Voronoi diagram in points.1.v.node and points.1.v.edge. + The .v.node file contains a list of all Voronoi vertices, and the .v.edge + file contains a list of all Voronoi edges, some of which may be infinite + rays. (The choice of filenames makes it easy to run the set of Voronoi + vertices through Triangle, if so desired.) + + This implementation does not use exact arithmetic to compute the Voronoi + vertices, and does not check whether neighboring vertices are identical. + Be forewarned that if the Delaunay triangulation is degenerate or + near-degenerate, the Voronoi diagram may have duplicate points, crossing + edges, or infinite rays whose direction vector is zero. Also, if you + generate a constrained (as opposed to conforming) Delaunay triangulation, + or if the triangulation has holes, the corresponding Voronoi diagram is + likely to have crossing edges and unlikely to make sense. + +Mesh Topology: + + You may wish to know which triangles are adjacent to a certain Delaunay + edge in an .edge file, which Voronoi regions are adjacent to a certain + Voronoi edge in a .v.edge file, or which Voronoi regions are adjacent to + each other. All of this information can be found by cross-referencing + output files with the recollection that the Delaunay triangulation and + the Voronoi diagrams are planar duals. + + Specifically, edge i of an .edge file is the dual of Voronoi edge i of + the corresponding .v.edge file, and is rotated 90 degrees counterclock- + wise from the Voronoi edge. Triangle j of an .ele file is the dual of + vertex j of the corresponding .v.node file; and Voronoi region k is the + dual of point k of the corresponding .node file. + + Hence, to find the triangles adjacent to a Delaunay edge, look at the + vertices of the corresponding Voronoi edge; their dual triangles are on + the left and right of the Delaunay edge, respectively. To find the + Voronoi regions adjacent to a Voronoi edge, look at the endpoints of the + corresponding Delaunay edge; their dual regions are on the right and left + of the Voronoi edge, respectively. To find which Voronoi regions are + adjacent to each other, just read the list of Delaunay edges. + +Statistics: + + After generating a mesh, Triangle prints a count of the number of points, + triangles, edges, boundary edges, and segments in the output mesh. If + you've forgotten the statistics for an existing mesh, the -rNEP switches + (or -rpNEP if you've got a .poly file for the existing mesh) will + regenerate these statistics without writing any output. + + The -V switch produces extended statistics, including a rough estimate + of memory use and a histogram of triangle aspect ratios and angles in the + mesh. + +Exact Arithmetic: + + Triangle uses adaptive exact arithmetic to perform what computational + geometers call the `orientation' and `incircle' tests. If the floating- + point arithmetic of your machine conforms to the IEEE 754 standard (as + most workstations do), and does not use extended precision internal + registers, then your output is guaranteed to be an absolutely true + Delaunay or conforming Delaunay triangulation, roundoff error + notwithstanding. The word `adaptive' implies that these arithmetic + routines compute the result only to the precision necessary to guarantee + correctness, so they are usually nearly as fast as their approximate + counterparts. The exact tests can be disabled with the -X switch. On + most inputs, this switch will reduce the computation time by about eight + percent - it's not worth the risk. There are rare difficult inputs + (having many collinear and cocircular points), however, for which the + difference could be a factor of two. These are precisely the inputs most + likely to cause errors if you use the -X switch. + + Unfortunately, these routines don't solve every numerical problem. Exact + arithmetic is not used to compute the positions of points, because the + bit complexity of point coordinates would grow without bound. Hence, + segment intersections aren't computed exactly; in very unusual cases, + roundoff error in computing an intersection point might actually lead to + an inverted triangle and an invalid triangulation. (This is one reason + to compute your own intersection points in your .poly files.) Similarly, + exact arithmetic is not used to compute the vertices of the Voronoi + diagram. + + Underflow and overflow can also cause difficulties; the exact arithmetic + routines do not ameliorate out-of-bounds exponents, which can arise + during the orientation and incircle tests. As a rule of thumb, you + should ensure that your input values are within a range such that their + third powers can be taken without underflow or overflow. Underflow can + silently prevent the tests from being performed exactly, while overflow + will typically cause a floating exception. + +Calling Triangle from Another Program: + + Read the file triangle.h for details. + +Troubleshooting: + + Please read this section before mailing me bugs. + + `My output mesh has no triangles!' + + If you're using a PSLG, you've probably failed to specify a proper set + of bounding segments, or forgotten to use the -c switch. Or you may + have placed a hole badly. To test these possibilities, try again with + the -c and -O switches. Alternatively, all your input points may be + collinear, in which case you can hardly expect to triangulate them. + + `Triangle doesn't terminate, or just crashes.' + + Bad things can happen when triangles get so small that the distance + between their vertices isn't much larger than the precision of your + machine's arithmetic. If you've compiled Triangle for single-precision + arithmetic, you might do better by recompiling it for double-precision. + Then again, you might just have to settle for more lenient constraints + on the minimum angle and the maximum area than you had planned. + + You can minimize precision problems by ensuring that the origin lies + inside your point set, or even inside the densest part of your + mesh. On the other hand, if you're triangulating an object whose x + coordinates all fall between 6247133 and 6247134, you're not leaving + much floating-point precision for Triangle to work with. + + Precision problems can occur covertly if the input PSLG contains two + segments that meet (or intersect) at a very small angle, or if such an + angle is introduced by the -c switch, which may occur if a point lies + ever-so-slightly inside the convex hull, and is connected by a PSLG + segment to a point on the convex hull. If you don't realize that a + small angle is being formed, you might never discover why Triangle is + crashing. To check for this possibility, use the -S switch (with an + appropriate limit on the number of Steiner points, found by trial-and- + error) to stop Triangle early, and view the output .poly file with + Show Me (described below). Look carefully for small angles between + segments; zoom in closely, as such segments might look like a single + segment from a distance. + + If some of the input values are too large, Triangle may suffer a + floating exception due to overflow when attempting to perform an + orientation or incircle test. (Read the section on exact arithmetic + above.) Again, I recommend compiling Triangle for double (rather + than single) precision arithmetic. + + `The numbering of the output points doesn't match the input points.' + + You may have eaten some of your input points with a hole, or by placing + them outside the area enclosed by segments. + + `Triangle executes without incident, but when I look at the resulting + mesh, it has overlapping triangles or other geometric inconsistencies.' + + If you select the -X switch, Triangle's divide-and-conquer Delaunay + triangulation algorithm occasionally makes mistakes due to floating- + point roundoff error. Although these errors are rare, don't use the -X + switch. If you still have problems, please report the bug. + + Strange things can happen if you've taken liberties with your PSLG. Do + you have a point lying in the middle of a segment? Triangle sometimes + copes poorly with that sort of thing. Do you want to lay out a collinear + row of evenly spaced, segment-connected points? Have you simply defined + one long segment connecting the leftmost point to the rightmost point, + and a bunch of points lying along it? This method occasionally works, + especially with horizontal and vertical lines, but often it doesn't, and + you'll have to connect each adjacent pair of points with a separate + segment. If you don't like it, tough. + + Furthermore, if you have segments that intersect other than at their + endpoints, try not to let the intersections fall extremely close to PSLG + points or each other. + + If you have problems refining a triangulation not produced by Triangle: + Are you sure the triangulation is geometrically valid? Is it formatted + correctly for Triangle? Are the triangles all listed so the first three + points are their corners in counterclockwise order? + +Show Me: + + Triangle comes with a separate program named `Show Me', whose primary + purpose is to draw meshes on your screen or in PostScript. Its secondary + purpose is to check the validity of your input files, and do so more + thoroughly than Triangle does. Show Me requires that you have the X + Windows system. If you didn't receive Show Me with Triangle, complain to + whomever you obtained Triangle from, then send me mail. + +Triangle on the Web: + + To see an illustrated, updated version of these instructions, check out + + http://www.cs.cmu.edu/~quake/triangle.html + +A Brief Plea: + + 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.cmu.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 in a publication, please include + an acknowledgment as well. + +Research credit: + + Of course, I can take credit for only a fraction of the ideas that made + this mesh generator possible. Triangle owes its existence to the efforts + of many fine computational geometers and other researchers, including + Marshall Bern, L. Paul Chew, Boris Delaunay, Rex A. Dwyer, David + Eppstein, Steven Fortune, Leonidas J. Guibas, Donald E. Knuth, C. L. + Lawson, Der-Tsai Lee, Ernst P. Mucke, Douglas M. Priest, Jim Ruppert, + Isaac Saias, Bruce J. Schachter, Micha Sharir, Jorge Stolfi, Christopher + J. Van Wyk, David F. Watson, and Binhai Zhu. See the comments at the + beginning of the source code for references. + diff --git a/Tools/Triangle/triangle.h b/Tools/Triangle/triangle.h new file mode 100644 index 000000000..b9be696c3 --- /dev/null +++ b/Tools/Triangle/triangle.h @@ -0,0 +1,289 @@ +/*****************************************************************************/ +/* */ +/* (triangle.h) */ +/* */ +/* Include file for programs that call Triangle. */ +/* */ +/* Accompanies Triangle Version 1.3 */ +/* July 19, 1996 */ +/* */ +/* Copyright 1996 */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.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 (preferably 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, -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 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' or `a' 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 and 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. */ +/* */ +/* 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. */ +/* */ +/*****************************************************************************/ + +/* CLO: 3/21/99 - this could be done as a compile flag, but I always want +this defined and I don't want to sprinkle extra stuff throughout the +Makefile system if I don't have to. */ +#define ANSI_DECLARATORS 1 + +struct triangulateio { + REAL *pointlist; /* In / out */ + REAL *pointattributelist; /* In / out */ + int *pointmarkerlist; /* In / out */ + int numberofpoints; /* In / out */ + int numberofpointattributes; /* In / out */ + + int *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 */ +}; + +#ifdef ANSI_DECLARATORS +void triangulate(char *, struct triangulateio *, struct triangulateio *, + struct triangulateio *); +#else /* not ANSI_DECLARATORS */ +void triangulate(); +#endif /* not ANSI_DECLARATORS */ diff --git a/Tools/Triangle/tricall.c b/Tools/Triangle/tricall.c new file mode 100644 index 000000000..6beccdc81 --- /dev/null +++ b/Tools/Triangle/tricall.c @@ -0,0 +1,279 @@ +/*****************************************************************************/ +/* */ +/* (tricall.c) */ +/* */ +/* Example program that demonstrates how to call Triangle. */ +/* */ +/* Accompanies Triangle Version 1.3 */ +/* July 19, 1996 */ +/* */ +/* This file is placed in the public domain (but the file that it calls */ +/* is still copyrighted!) by */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/*****************************************************************************/ + +/* If SINGLE is defined when triangle.o is compiled, it should also be */ +/* defined here. If not, it should not be defined here. */ + +/* #define SINGLE */ + +#ifdef SINGLE +#define REAL float +#else /* not SINGLE */ +#define REAL double +#endif /* not SINGLE */ + +#include <stdio.h> +#include "triangle.h" + +#ifndef _STDLIB_H_ +extern void *malloc(); +extern void free(); +#endif /* _STDLIB_H_ */ + +/*****************************************************************************/ +/* */ +/* report() Print the input or output. */ +/* */ +/*****************************************************************************/ + +void report(io, markers, reporttriangles, reportneighbors, reportsegments, + reportedges, reportnorms) +struct triangulateio *io; +int markers; +int reporttriangles; +int reportneighbors; +int reportsegments; +int reportedges; +int reportnorms; +{ + int i, j; + + for (i = 0; i < io->numberofpoints; i++) { + printf("Point %4d:", i); + for (j = 0; j < 2; j++) { + printf(" %.6g", io->pointlist[i * 2 + j]); + } + if (io->numberofpointattributes > 0) { + printf(" attributes"); + } + for (j = 0; j < io->numberofpointattributes; j++) { + printf(" %.6g", + io->pointattributelist[i * io->numberofpointattributes + j]); + } + if (markers) { + printf(" marker %d\n", io->pointmarkerlist[i]); + } else { + printf("\n"); + } + } + printf("\n"); + + if (reporttriangles || reportneighbors) { + for (i = 0; i < io->numberoftriangles; i++) { + if (reporttriangles) { + printf("Triangle %4d points:", i); + for (j = 0; j < io->numberofcorners; j++) { + printf(" %4d", io->trianglelist[i * io->numberofcorners + j]); + } + if (io->numberoftriangleattributes > 0) { + printf(" attributes"); + } + for (j = 0; j < io->numberoftriangleattributes; j++) { + printf(" %.6g", io->triangleattributelist[i * + io->numberoftriangleattributes + j]); + } + printf("\n"); + } + if (reportneighbors) { + printf("Triangle %4d neighbors:", i); + for (j = 0; j < 3; j++) { + printf(" %4d", io->neighborlist[i * 3 + j]); + } + printf("\n"); + } + } + printf("\n"); + } + + if (reportsegments) { + for (i = 0; i < io->numberofsegments; i++) { + printf("Segment %4d points:", i); + for (j = 0; j < 2; j++) { + printf(" %4d", io->segmentlist[i * 2 + j]); + } + if (markers) { + printf(" marker %d\n", io->segmentmarkerlist[i]); + } else { + printf("\n"); + } + } + printf("\n"); + } + + if (reportedges) { + for (i = 0; i < io->numberofedges; i++) { + printf("Edge %4d points:", i); + for (j = 0; j < 2; j++) { + printf(" %4d", io->edgelist[i * 2 + j]); + } + if (reportnorms && (io->edgelist[i * 2 + 1] == -1)) { + for (j = 0; j < 2; j++) { + printf(" %.6g", io->normlist[i * 2 + j]); + } + } + if (markers) { + printf(" marker %d\n", io->edgemarkerlist[i]); + } else { + printf("\n"); + } + } + printf("\n"); + } +} + +/*****************************************************************************/ +/* */ +/* main() Create and refine a mesh. */ +/* */ +/*****************************************************************************/ + +int main() +{ + struct triangulateio in, mid, out, vorout; + + /* Define input points. */ + + in.numberofpoints = 4; + in.numberofpointattributes = 1; + in.pointlist = (REAL *) malloc(in.numberofpoints * 2 * sizeof(REAL)); + in.pointlist[0] = 0.0; + in.pointlist[1] = 0.0; + in.pointlist[2] = 1.0; + in.pointlist[3] = 0.0; + in.pointlist[4] = 1.0; + in.pointlist[5] = 10.0; + in.pointlist[6] = 0.0; + in.pointlist[7] = 10.0; + in.pointattributelist = (REAL *) malloc(in.numberofpoints * + in.numberofpointattributes * + sizeof(REAL)); + in.pointattributelist[0] = 0.0; + in.pointattributelist[1] = 1.0; + in.pointattributelist[2] = 11.0; + in.pointattributelist[3] = 10.0; + in.pointmarkerlist = (int *) malloc(in.numberofpoints * sizeof(int)); + in.pointmarkerlist[0] = 0; + in.pointmarkerlist[1] = 2; + in.pointmarkerlist[2] = 0; + in.pointmarkerlist[3] = 0; + + in.numberofsegments = 0; + in.numberofholes = 0; + in.numberofregions = 1; + in.regionlist = (REAL *) malloc(in.numberofregions * 4 * sizeof(REAL)); + in.regionlist[0] = 0.5; + in.regionlist[1] = 5.0; + in.regionlist[2] = 7.0; /* Regional attribute (for whole mesh). */ + in.regionlist[3] = 0.1; /* Area constraint that will not be used. */ + + printf("Input point set:\n\n"); + report(&in, 1, 0, 0, 0, 0, 0); + + /* Make necessary initializations so that Triangle can return a */ + /* triangulation in `mid' and a voronoi diagram in `vorout'. */ + + mid.pointlist = (REAL *) NULL; /* Not needed if -N switch used. */ + /* Not needed if -N switch used or number of point attributes is zero: */ + mid.pointattributelist = (REAL *) NULL; + mid.pointmarkerlist = (int *) NULL; /* Not needed if -N or -B switch used. */ + mid.trianglelist = (int *) NULL; /* Not needed if -E switch used. */ + /* Not needed if -E switch used or number of triangle attributes is zero: */ + mid.triangleattributelist = (REAL *) NULL; + mid.neighborlist = (int *) NULL; /* Needed only if -n switch used. */ + /* Needed only if segments are output (-p or -c) and -P not used: */ + mid.segmentlist = (int *) NULL; + /* Needed only if segments are output (-p or -c) and -P and -B not used: */ + mid.segmentmarkerlist = (int *) NULL; + mid.edgelist = (int *) NULL; /* Needed only if -e switch used. */ + mid.edgemarkerlist = (int *) NULL; /* Needed if -e used and -B not used. */ + + vorout.pointlist = (REAL *) NULL; /* Needed only if -v switch used. */ + /* Needed only if -v switch used and number of attributes is not zero: */ + vorout.pointattributelist = (REAL *) NULL; + vorout.edgelist = (int *) NULL; /* Needed only if -v switch used. */ + vorout.normlist = (REAL *) NULL; /* Needed only if -v switch used. */ + + /* Triangulate the points. Switches are chosen to read and write a */ + /* PSLG (p), preserve the convex hull (c), number everything from */ + /* zero (z), assign a regional attribute to each element (A), and */ + /* produce an edge list (e), a Voronoi diagram (v), and a triangle */ + /* neighbor list (n). */ + + triangulate("pczAevn", &in, &mid, &vorout); + + printf("Initial triangulation:\n\n"); + report(&mid, 1, 1, 1, 1, 1, 0); + printf("Initial Voronoi diagram:\n\n"); + report(&vorout, 0, 0, 0, 0, 1, 1); + + /* Attach area constraints to the triangles in preparation for */ + /* refining the triangulation. */ + + /* Needed only if -r and -a switches used: */ + mid.trianglearealist = (REAL *) malloc(mid.numberoftriangles * sizeof(REAL)); + mid.trianglearealist[0] = 3.0; + mid.trianglearealist[1] = 1.0; + + /* Make necessary initializations so that Triangle can return a */ + /* triangulation in `out'. */ + + out.pointlist = (REAL *) NULL; /* Not needed if -N switch used. */ + /* Not needed if -N switch used or number of attributes is zero: */ + out.pointattributelist = (REAL *) NULL; + out.trianglelist = (int *) NULL; /* Not needed if -E switch used. */ + /* Not needed if -E switch used or number of triangle attributes is zero: */ + out.triangleattributelist = (REAL *) NULL; + + /* Refine the triangulation according to the attached */ + /* triangle area constraints. */ + + triangulate("prazBP", &mid, &out, (struct triangulateio *) NULL); + + printf("Refined triangulation:\n\n"); + report(&out, 0, 1, 0, 0, 0, 0); + + /* Free all allocated arrays, including those allocated by Triangle. */ + + free(in.pointlist); + free(in.pointattributelist); + free(in.pointmarkerlist); + free(in.regionlist); + free(mid.pointlist); + free(mid.pointattributelist); + free(mid.pointmarkerlist); + free(mid.trianglelist); + free(mid.triangleattributelist); + free(mid.trianglearealist); + free(mid.neighborlist); + free(mid.segmentlist); + free(mid.segmentmarkerlist); + free(mid.edgelist); + free(mid.edgemarkerlist); + free(vorout.pointlist); + free(vorout.pointattributelist); + free(vorout.edgelist); + free(vorout.normlist); + free(out.pointlist); + free(out.pointattributelist); + free(out.trianglelist); + free(out.triangleattributelist); + + return 0; +} diff --git a/Tools/Triangulate/Makefile.am b/Tools/Triangulate/Makefile.am new file mode 100644 index 000000000..34c320f91 --- /dev/null +++ b/Tools/Triangulate/Makefile.am @@ -0,0 +1,14 @@ +noinst_LIBRARIES = libTriangulate.a + +libTriangulate_a_SOURCES = \ + triangle.cxx triangle.hxx \ + trieles.cxx trieles.hxx \ + trinodes.cxx trinodes.hxx \ + tripoly.cxx tripoly.hxx \ + trisegs.cxx trisegs.hxx + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Tools/Lib \ + -I$(top_builddir)/Tools/Construct diff --git a/Tools/Triangulate/triangle.cxx b/Tools/Triangulate/triangle.cxx new file mode 100644 index 000000000..38b8bf1ac --- /dev/null +++ b/Tools/Triangulate/triangle.cxx @@ -0,0 +1,476 @@ +// triangle.cxx -- "Triangle" interface class +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include "triangle.hxx" +#include "tripoly.hxx" + +// Constructor +FGTriangle::FGTriangle( void ) { +} + + +// Destructor +FGTriangle::~FGTriangle( void ) { +} + + +// populate this class based on the specified gpc_polys list +int +FGTriangle::build( const point_list& corner_list, + const point_list& fit_list, + const FGgpcPolyList& gpc_polys ) +{ + FGTriPoly poly; + int index; + + in_nodes.clear(); + trisegs.clear(); + + // Point3D junkp; + // int junkc = 0; + // char junkn[256]; + // FILE *junkfp; + + // traverse the dem corner and fit lists and gpc_polys building a + // unified node list and converting the polygons so that they + // reference the node list by index (starting at zero) rather than + // listing the points explicitely + + // first the corners since these are important + const_point_list_iterator f_current, f_last; + f_current = corner_list.begin(); + f_last = corner_list.end(); + for ( ; f_current != f_last; ++f_current ) { + index = in_nodes.unique_add( *f_current ); + } + + // next process the polygons + gpc_polygon *gpc_poly; + const_gpcpoly_iterator current, last; + + // process polygons in priority order + cout << "prepairing node list and polygons" << endl; + + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + polylist[i].clear(); + + // cout << "area type = " << i << endl; + current = gpc_polys.polys[i].begin(); + last = gpc_polys.polys[i].end(); + for ( ; current != last; ++current ) { + gpc_poly = *current; + cout << "processing a polygon, contours = " + << gpc_poly->num_contours << endl; + + if (gpc_poly->num_contours <= 0 ) { + cout << "FATAL ERROR! no contours in this polygon" << endl; + exit(-1); + } + + if (gpc_poly->num_contours > 1 ) { + cout << "FATAL ERROR! no multi-contour support" << endl; + sleep(2); + // exit(-1); + } + + for ( int j = 0; j < gpc_poly->num_contours; j++ ) { + cout << " processing contour, nodes = " + << gpc_poly->contour[j].num_vertices << endl; + + poly.erase(); + + // sprintf(junkn, "g.%d", junkc++); + // junkfp = fopen(junkn, "w"); + + for ( int k = 0; k < gpc_poly->contour[j].num_vertices; k++ ) { + Point3D p( gpc_poly->contour[j].vertex[k].x, + gpc_poly->contour[j].vertex[k].y, + 0 ); + index = in_nodes.unique_add( p ); + // junkp = in_nodes.get_node( index ); + // fprintf(junkfp, "%.4f %.4f\n", junkp.x(), junkp.y()); + poly.add_node(index); + // cout << index << endl; + } + // fprintf(junkfp, "%.4f %.4f\n", + // gpc_poly->contour[j].vertex[0].x, + // gpc_poly->contour[j].vertex[0].y); + // fclose(junkfp); + + poly.calc_point_inside( in_nodes ); + + polylist[i].push_back(poly); + } + } + } + + // last, do the rest of the height nodes + f_current = fit_list.begin(); + f_last = fit_list.end(); + for ( ; f_current != f_last; ++f_current ) { + index = in_nodes.course_add( *f_current ); + } + + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + if ( polylist[i].size() ) { + cout << get_area_name((AreaType)i) << " = " + << polylist[i].size() << endl; + } + } + + // traverse the polygon lists and build the segment (edge) list + // that is used by the "Triangle" lib. + + int i1, i2; + point_list node_list = in_nodes.get_node_list(); + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + // cout << "area type = " << i << endl; + tripoly_list_iterator tp_current, tp_last; + tp_current = polylist[i].begin(); + tp_last = polylist[i].end(); + + // process each polygon in list + for ( ; tp_current != tp_last; ++tp_current ) { + poly = *tp_current; + + for ( int j = 0; j < (int)(poly.size()) - 1; ++j ) { + i1 = poly.get_pt_index( j ); + i2 = poly.get_pt_index( j + 1 ); + // calc_line_params(i1, i2, &m, &b); + trisegs.unique_divide_and_add( node_list, FGTriSeg(i1, i2) ); + } + i1 = poly.get_pt_index( 0 ); + i2 = poly.get_pt_index( poly.size() - 1 ); + // calc_line_params(i1, i2, &m, &b); + trisegs.unique_divide_and_add( node_list, FGTriSeg(i1, i2) ); + } + } + + return 0; +} + + +static void write_out_data(struct triangulateio *out) { + FILE *node = fopen("tile.node", "w"); + fprintf(node, "%d 2 %d 0\n", + out->numberofpoints, out->numberofpointattributes); + for (int i = 0; i < out->numberofpoints; i++) { + fprintf(node, "%d %.6f %.6f %.2f\n", + i, out->pointlist[2*i], out->pointlist[2*i + 1], 0.0); + } + fclose(node); + + FILE *ele = fopen("tile.ele", "w"); + fprintf(ele, "%d 3 0\n", out->numberoftriangles); + for (int i = 0; i < out->numberoftriangles; i++) { + fprintf(ele, "%d ", i); + for (int j = 0; j < out->numberofcorners; j++) { + fprintf(ele, "%d ", out->trianglelist[i * out->numberofcorners + j]); + } + for (int j = 0; j < out->numberoftriangleattributes; j++) { + fprintf(ele, "%.6f ", + out->triangleattributelist[i + * out->numberoftriangleattributes + + j] + ); + } + fprintf(ele, "\n"); + } + fclose(ele); + + FILE *fp = fopen("tile.poly", "w"); + fprintf(fp, "0 2 1 0\n"); + fprintf(fp, "%d 0\n", out->numberofsegments); + for (int i = 0; i < out->numberofsegments; ++i) { + fprintf(fp, "%d %d %d\n", + i, out->segmentlist[2*i], out->segmentlist[2*i + 1]); + } + fprintf(fp, "%d\n", out->numberofholes); + for (int i = 0; i < out->numberofholes; i++) { + fprintf(fp, "%d %.6f %.6f\n", + i, out->holelist[2*i], out->holelist[2*i + 1]); + } + fprintf(fp, "%d\n", out->numberofregions); + for (int i = 0; i < out->numberofregions; i++) { + fprintf(fp, "%d %.6f %.6f %.6f\n", + i, out->regionlist[4*i], out->regionlist[4*i + 1], + out->regionlist[4*i + 2]); + } + fclose(fp); +} + + +// triangulate each of the polygon areas +int FGTriangle::run_triangulate() { + FGTriPoly poly; + Point3D p; + struct triangulateio in, out, vorout; + int counter; + + // point list + point_list node_list = in_nodes.get_node_list(); + in.numberofpoints = node_list.size(); + in.pointlist = (REAL *) malloc(in.numberofpoints * 2 * sizeof(REAL)); + + point_list_iterator tn_current, tn_last; + tn_current = node_list.begin(); + tn_last = node_list.end(); + counter = 0; + for ( ; tn_current != tn_last; ++tn_current ) { + in.pointlist[counter++] = tn_current->x(); + in.pointlist[counter++] = tn_current->y(); + } + + in.numberofpointattributes = 1; + in.pointattributelist = (REAL *) malloc(in.numberofpoints * + in.numberofpointattributes * + sizeof(REAL)); + for ( int i = 0; i < in.numberofpoints * in.numberofpointattributes; i++) { + in.pointattributelist[i] = 0.0; + } + + in.pointmarkerlist = (int *) malloc(in.numberofpoints * sizeof(int)); + for ( int i = 0; i < in.numberofpoints; i++) { + in.pointmarkerlist[i] = 0; + } + + // triangle list + in.numberoftriangles = 0; + + // segment list + triseg_list seg_list = trisegs.get_seg_list(); + in.numberofsegments = seg_list.size(); + in.segmentlist = (int *) malloc(in.numberofsegments * 2 * sizeof(int)); + in.segmentmarkerlist = (int *) NULL; + + triseg_list_iterator s_current, s_last; + s_current = seg_list.begin(); + s_last = seg_list.end(); + counter = 0; + for ( ; s_current != s_last; ++s_current ) { + in.segmentlist[counter++] = s_current->get_n1(); + in.segmentlist[counter++] = s_current->get_n2(); + } + + // hole list (make holes for airport ignore areas) + in.numberofholes = polylist[(int)AirportIgnoreArea].size(); + in.holelist = (REAL *) malloc(in.numberofholes * 2 * sizeof(REAL)); + + tripoly_list_iterator h_current, h_last; + h_current = polylist[(int)AirportIgnoreArea].begin(); + h_last = polylist[(int)AirportIgnoreArea].end(); + counter = 0; + for ( ; h_current != h_last; ++h_current ) { + poly = *h_current; + p = poly.get_point_inside(); + in.holelist[counter++] = p.x(); + in.holelist[counter++] = p.y(); + } + + // region list + in.numberofregions = 0; + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + in.numberofregions += polylist[i].size(); + } + + in.regionlist = (REAL *) malloc(in.numberofregions * 4 * sizeof(REAL)); + counter = 0; + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + tripoly_list_iterator h_current, h_last; + h_current = polylist[(int)i].begin(); + h_last = polylist[(int)i].end(); + for ( ; h_current != h_last; ++h_current ) { + poly = *h_current; + p = poly.get_point_inside(); + in.regionlist[counter++] = p.x(); // x coord + in.regionlist[counter++] = p.y(); // y coord + in.regionlist[counter++] = i; // region attribute + in.regionlist[counter++] = -1.0; // area constraint (unused) + } + } + + // prep the output structures + out.pointlist = (REAL *) NULL; // Not needed if -N switch used. + // Not needed if -N switch used or number of point attributes is zero: + out.pointattributelist = (REAL *) NULL; + out.pointmarkerlist = (int *) NULL; // Not needed if -N or -B switch used. + out.trianglelist = (int *) NULL; // Not needed if -E switch used. + // Not needed if -E switch used or number of triangle attributes is zero: + out.triangleattributelist = (REAL *) NULL; + out.neighborlist = (int *) NULL; // Needed only if -n switch used. + // Needed only if segments are output (-p or -c) and -P not used: + out.segmentlist = (int *) NULL; + // Needed only if segments are output (-p or -c) and -P and -B not used: + out.segmentmarkerlist = (int *) NULL; + out.edgelist = (int *) NULL; // Needed only if -e switch used. + out.edgemarkerlist = (int *) NULL; // Needed if -e used and -B not used. + + vorout.pointlist = (REAL *) NULL; // Needed only if -v switch used. + // Needed only if -v switch used and number of attributes is not zero: + vorout.pointattributelist = (REAL *) NULL; + vorout.edgelist = (int *) NULL; // Needed only if -v switch used. + vorout.normlist = (REAL *) NULL; // Needed only if -v switch used. + + // TEMPORARY + // write_out_data(&in); + + // Triangulate the points. Switches are chosen to read and write + // a PSLG (p), preserve the convex hull (c), number everything + // from zero (z), assign a regional attribute to each element (A), + // and produce an edge list (e), and a triangle neighbor list (n). + + string tri_options = "pczq10Aen"; + // string tri_options = "pzAen"; + // string tri_options = "pczq15S400Aen"; + cout << "Triangulation with options = " << tri_options << endl; + + triangulate(tri_options.c_str(), &in, &out, &vorout); + + // TEMPORARY + write_out_data(&out); + + // now copy the results back into the corresponding FGTriangle + // structures + + // nodes + out_nodes.clear(); + for ( int i = 0; i < out.numberofpoints; i++ ) { + Point3D p( out.pointlist[2*i], out.pointlist[2*i + 1], 0.0 ); + // cout << "point = " << p << endl; + out_nodes.simple_add( p ); + } + + // triangles + elelist.clear(); + int n1, n2, n3; + double attribute; + for ( int i = 0; i < out.numberoftriangles; i++ ) { + n1 = out.trianglelist[i * 3]; + n2 = out.trianglelist[i * 3 + 1]; + n3 = out.trianglelist[i * 3 + 2]; + if ( out.numberoftriangleattributes > 0 ) { + attribute = out.triangleattributelist[i]; + } else { + attribute = 0.0; + } + // cout << "triangle = " << n1 << " " << n2 << " " << n3 << endl; + + elelist.push_back( FGTriEle( n1, n2, n3, attribute ) ); + } + + // free mem allocated to the "Triangle" structures + free(in.pointlist); + free(in.pointattributelist); + free(in.pointmarkerlist); + free(in.regionlist); + free(out.pointlist); + free(out.pointattributelist); + free(out.pointmarkerlist); + free(out.trianglelist); + free(out.triangleattributelist); + // free(out.trianglearealist); + free(out.neighborlist); + free(out.segmentlist); + free(out.segmentmarkerlist); + free(out.edgelist); + free(out.edgemarkerlist); + free(vorout.pointlist); + free(vorout.pointattributelist); + free(vorout.edgelist); + free(vorout.normlist); + + return 0; +} + + +// $Log$ +// Revision 1.16 1999/04/05 02:17:11 curt +// Dynamically update "error" until the resulting tile data scales within +// a lower and upper bounds. +// +// Revision 1.15 1999/04/03 05:22:58 curt +// Found a bug in dividing and adding unique verticle segments which could +// cause the triangulator to end up in an infinite loop. Basically the code +// was correct, but the verticle line test was a bit to selective. +// +// Revision 1.14 1999/03/31 23:47:09 curt +// Debugging output tweaks. +// +// Revision 1.13 1999/03/29 13:11:07 curt +// Shuffled stl type names a bit. +// Began adding support for tri-fanning (or maybe other arrangments too.) +// +// Revision 1.12 1999/03/27 05:30:12 curt +// Handle corner nodes separately from the rest of the fitted nodes. +// Add fitted nodes in after corners and polygon nodes since the fitted nodes +// are less important. Subsequent nodes will "snap" to previous nodes if +// they are "close enough." +// Need to manually divide segments to prevent "T" intersetions which can +// confound the triangulator. Hey, I got to use a recursive method! +// Pass along correct triangle attributes to output file generator. +// Do fine grained node snapping for corners and polygons, but course grain +// node snapping for fitted terrain nodes. +// +// Revision 1.11 1999/03/23 22:02:51 curt +// Refinements in naming and organization. +// +// Revision 1.10 1999/03/22 23:49:02 curt +// Modifications to facilitate conversion to output format. +// +// Revision 1.9 1999/03/21 15:48:02 curt +// Removed Dem2node from the Tools fold. +// Tweaked the triangulator options to add quality mesh refinement. +// +// Revision 1.8 1999/03/21 14:02:06 curt +// Added a mechanism to dump out the triangle structures for viewing. +// Fixed a couple bugs in first pass at triangulation. +// - needed to explicitely initialize the polygon accumulator in triangle.cxx +// before each polygon rather than depending on the default behavior. +// - Fixed a problem with region attribute propagation where I wasn't generating +// the hole points correctly. +// +// Revision 1.7 1999/03/20 20:32:55 curt +// First mostly successful tile triangulation works. There's plenty of tweaking +// to do, but we are marching in the right direction. +// +// Revision 1.6 1999/03/20 13:22:11 curt +// Added trisegs.[ch]xx tripoly.[ch]xx. +// +// Revision 1.5 1999/03/20 02:21:52 curt +// Continue shaping the code towards triangulation bliss. Added code to +// calculate some point guaranteed to be inside a polygon. +// +// Revision 1.4 1999/03/19 22:29:04 curt +// Working on preparationsn for triangulation. +// +// Revision 1.3 1999/03/19 00:27:10 curt +// Continued work on triangulation preparation. +// +// Revision 1.2 1999/03/18 04:31:11 curt +// Let's not pass copies of huge structures on the stack ... ye might see a +// segfault ... :-) +// +// Revision 1.1 1999/03/17 23:51:59 curt +// Initial revision. +// diff --git a/Tools/Triangulate/triangle.hxx b/Tools/Triangulate/triangle.hxx new file mode 100644 index 000000000..676f5056c --- /dev/null +++ b/Tools/Triangulate/triangle.hxx @@ -0,0 +1,141 @@ +// triangle.hxx -- "Triangle" interface class +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _TRIANGLE_HXX +#define _TRIANGLE_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include <Include/compiler.h> + +#include <Array/array.hxx> +#include <Clipper/clipper.hxx> +#include <Math/point3d.hxx> +#include <Polygon/names.hxx> + +#define REAL double +extern "C" { +#include <Triangle/triangle.h> +} + +#include "trieles.hxx" +#include "trinodes.hxx" +#include "tripoly.hxx" +#include "trisegs.hxx" + + +class FGTriangle { + +private: + + // list of nodes + FGTriNodes in_nodes; + FGTriNodes out_nodes; + + // list of segments + FGTriSegments trisegs; + + // polygon list + tripoly_list polylist[FG_MAX_AREA_TYPES]; + + // triangle list + triele_list elelist; + +public: + + // Constructor and destructor + FGTriangle( void ); + ~FGTriangle( void ); + + // add nodes from the dem fit + int add_nodes(); + + // populate this class based on the specified gpc_polys list + int build( const point_list& corner_list, + const point_list& fit_list, + const FGgpcPolyList& gpc_polys ); + + // front end triangulator for polygon list + int run_triangulate(); + + inline FGTriNodes get_out_nodes() const { return out_nodes; } + inline size_t get_out_nodes_size() const { return out_nodes.size(); } + inline triele_list get_elelist() const { return elelist; } +}; + + +#endif // _TRIANGLE_HXX + + +// $Log$ +// Revision 1.11 1999/04/05 02:17:12 curt +// Dynamically update "error" until the resulting tile data scales within +// a lower and upper bounds. +// +// Revision 1.10 1999/03/29 13:11:08 curt +// Shuffled stl type names a bit. +// Began adding support for tri-fanning (or maybe other arrangments too.) +// +// Revision 1.9 1999/03/27 05:30:13 curt +// Handle corner nodes separately from the rest of the fitted nodes. +// Add fitted nodes in after corners and polygon nodes since the fitted nodes +// are less important. Subsequent nodes will "snap" to previous nodes if +// they are "close enough." +// Need to manually divide segments to prevent "T" intersetions which can +// confound the triangulator. Hey, I got to use a recursive method! +// Pass along correct triangle attributes to output file generator. +// Do fine grained node snapping for corners and polygons, but course grain +// node snapping for fitted terrain nodes. +// +// Revision 1.8 1999/03/23 22:02:52 curt +// Refinements in naming and organization. +// +// Revision 1.7 1999/03/22 23:49:03 curt +// Modifications to facilitate conversion to output format. +// +// Revision 1.6 1999/03/20 20:32:56 curt +// First mostly successful tile triangulation works. There's plenty of tweaking +// to do, but we are marching in the right direction. +// +// Revision 1.5 1999/03/20 02:21:53 curt +// Continue shaping the code towards triangulation bliss. Added code to +// calculate some point guaranteed to be inside a polygon. +// +// Revision 1.4 1999/03/19 22:29:05 curt +// Working on preparationsn for triangulation. +// +// Revision 1.3 1999/03/19 00:27:11 curt +// Continued work on triangulation preparation. +// +// Revision 1.2 1999/03/18 04:31:12 curt +// Let's not pass copies of huge structures on the stack ... ye might see a +// segfault ... :-) +// +// Revision 1.1 1999/03/17 23:51:59 curt +// Initial revision. +// diff --git a/Tools/Triangulate/trieles.cxx b/Tools/Triangulate/trieles.cxx new file mode 100644 index 000000000..406a19a91 --- /dev/null +++ b/Tools/Triangulate/trieles.cxx @@ -0,0 +1,31 @@ +// trieles.cxx -- "Triangle" element management class +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include "trieles.hxx" + + +// $Log$ +// Revision 1.1 1999/03/22 23:58:57 curt +// Initial revision. +// diff --git a/Tools/Triangulate/trieles.hxx b/Tools/Triangulate/trieles.hxx new file mode 100644 index 000000000..2d22c7db5 --- /dev/null +++ b/Tools/Triangulate/trieles.hxx @@ -0,0 +1,94 @@ +// trieles.hxx -- "Triangle" element management class +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _TRIELES_HXX +#define _TRIELES_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include <Include/compiler.h> + +#include <vector> + +FG_USING_STD(vector); + + +// a segment is two integer pointers into the node list +class FGTriEle { + int n1, n2, n3; + + double attribute; + +public: + + // Constructor and destructor + inline FGTriEle( void ) { }; + inline FGTriEle( int i1, int i2, int i3, double a ) { + n1 = i1; n2 = i2; n3 = i3; attribute = a; + } + + inline ~FGTriEle( void ) { }; + + inline int get_n1() const { return n1; } + inline void set_n1( int i ) { n1 = i; } + inline int get_n2() const { return n2; } + inline void set_n2( int i ) { n2 = i; } + inline int get_n3() const { return n3; } + inline void set_n3( int i ) { n3 = i; } + + inline double get_attribute() const { return attribute; } + inline void set_attribute( double a ) { attribute = a; } +}; + + +typedef vector < FGTriEle > triele_list; +typedef triele_list::iterator triele_list_iterator; +typedef triele_list::const_iterator const_triele_list_iterator; + + +#endif // _TRIELES_HXX + + +// $Log$ +// Revision 1.3 1999/03/27 05:30:14 curt +// Handle corner nodes separately from the rest of the fitted nodes. +// Add fitted nodes in after corners and polygon nodes since the fitted nodes +// are less important. Subsequent nodes will "snap" to previous nodes if +// they are "close enough." +// Need to manually divide segments to prevent "T" intersetions which can +// confound the triangulator. Hey, I got to use a recursive method! +// Pass along correct triangle attributes to output file generator. +// Do fine grained node snapping for corners and polygons, but course grain +// node snapping for fitted terrain nodes. +// +// Revision 1.2 1999/03/23 22:02:53 curt +// Refinements in naming and organization. +// +// Revision 1.1 1999/03/22 23:58:57 curt +// Initial revision. +// diff --git a/Tools/Triangulate/trinodes.cxx b/Tools/Triangulate/trinodes.cxx new file mode 100644 index 000000000..50e7df7ee --- /dev/null +++ b/Tools/Triangulate/trinodes.cxx @@ -0,0 +1,130 @@ +// trinodes.cxx -- "Triangle" nodes management class +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include "trinodes.hxx" + + +// Constructor +FGTriNodes::FGTriNodes( void ) { +} + + +// Destructor +FGTriNodes::~FGTriNodes( void ) { +} + + +// Add a point to the point list if it doesn't already exist. Returns +// the index (starting at zero) of the point in the list. +int FGTriNodes::unique_add( const Point3D& p ) { + point_list_iterator current, last; + int counter = 0; + + // cout << p.x() << "," << p.y() << endl; + + // see if point already exists + current = node_list.begin(); + last = node_list.end(); + for ( ; current != last; ++current ) { + if ( close_enough(p, *current) ) { + // cout << "found an existing match!" << endl; + return counter; + } + + ++counter; + } + + // add to list + node_list.push_back( p ); + + return counter; +} + + +// Add the point with no uniqueness checking +int FGTriNodes::simple_add( const Point3D& p ) { + node_list.push_back( p ); + + return node_list.size() - 1; +} + + +// Add a point to the point list if it doesn't already exist. Returns +// the index (starting at zero) of the point in the list. Use a +// course proximity check +int FGTriNodes::course_add( const Point3D& p ) { + point_list_iterator current, last; + int counter = 0; + + // cout << p.x() << "," << p.y() << endl; + + // see if point already exists + current = node_list.begin(); + last = node_list.end(); + for ( ; current != last; ++current ) { + if ( course_close_enough(p, *current) ) { + // cout << "found an existing match!" << endl; + return counter; + } + + ++counter; + } + + // add to list + node_list.push_back( p ); + + return counter; +} + + +// $Log$ +// Revision 1.6 1999/03/27 05:30:15 curt +// Handle corner nodes separately from the rest of the fitted nodes. +// Add fitted nodes in after corners and polygon nodes since the fitted nodes +// are less important. Subsequent nodes will "snap" to previous nodes if +// they are "close enough." +// Need to manually divide segments to prevent "T" intersetions which can +// confound the triangulator. Hey, I got to use a recursive method! +// Pass along correct triangle attributes to output file generator. +// Do fine grained node snapping for corners and polygons, but course grain +// node snapping for fitted terrain nodes. +// +// Revision 1.5 1999/03/23 22:02:54 curt +// Refinements in naming and organization. +// +// Revision 1.4 1999/03/22 23:49:04 curt +// Modifications to facilitate conversion to output format. +// +// Revision 1.3 1999/03/20 02:21:54 curt +// Continue shaping the code towards triangulation bliss. Added code to +// calculate some point guaranteed to be inside a polygon. +// +// Revision 1.2 1999/03/19 00:27:12 curt +// Continued work on triangulation preparation. +// +// Revision 1.1 1999/03/17 23:52:00 curt +// Initial revision. +// + + diff --git a/Tools/Triangulate/trinodes.hxx b/Tools/Triangulate/trinodes.hxx new file mode 100644 index 000000000..f7c5fdd55 --- /dev/null +++ b/Tools/Triangulate/trinodes.hxx @@ -0,0 +1,155 @@ +// trinodes.hxx -- "Triangle" nodes management class +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _TRINODES_HXX +#define _TRINODES_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include <Include/compiler.h> + +#include <Math/point3d.hxx> + +#include <Main/construct_types.hxx> + + +#define FG_PROXIMITY_EPSILON 0.000001 +#define FG_COURSE_EPSILON 0.0003 + + +class FGTriNodes { + +private: + + point_list node_list; + + // return true of the two points are "close enough" as defined by + // FG_PROXIMITY_EPSILON + bool close_enough( const Point3D& p, const Point3D& p ); + + // return true of the two points are "close enough" as defined by + // FG_COURSE_EPSILON + bool course_close_enough( const Point3D& p1, const Point3D& p2 ); + +public: + + // Constructor and destructor + FGTriNodes( void ); + ~FGTriNodes( void ); + + // delete all the data out of node_list + inline void clear() { node_list.clear(); } + + // Add a point to the point list if it doesn't already exist. + // Returns the index (starting at zero) of the point in the list. + int unique_add( const Point3D& p ); + + // Add the point with no uniqueness checking + int simple_add( const Point3D& p ); + + // Add a point to the point list if it doesn't already exist. + // Returns the index (starting at zero) of the point in the list. + // Use a course proximity check + int course_add( const Point3D& p ); + + // return the master node list + inline point_list get_node_list() const { return node_list; } + + // return the ith point + inline Point3D get_node( int i ) const { return node_list[i]; } + + // return the size of the node list + inline size_t size() const { return node_list.size(); } +}; + + +// return true of the two points are "close enough" as defined by +// FG_PROXIMITY_EPSILON +inline bool FGTriNodes::close_enough( const Point3D& p1, const Point3D& p2 ) { + if ( ( fabs(p1.x() - p2.x()) < FG_PROXIMITY_EPSILON ) && + ( fabs(p1.y() - p2.y()) < FG_PROXIMITY_EPSILON ) ) { + return true; + } else { + return false; + } +} + + +// return true of the two points are "close enough" as defined by +// FG_COURSE_EPSILON +inline bool FGTriNodes::course_close_enough( const Point3D& p1, + const Point3D& p2 ) +{ + if ( ( fabs(p1.x() - p2.x()) < FG_COURSE_EPSILON ) && + ( fabs(p1.y() - p2.y()) < FG_COURSE_EPSILON ) ) { + return true; + } else { + return false; + } +} + + +#endif // _TRINODES_HXX + + +// $Log$ +// Revision 1.8 1999/04/05 02:17:13 curt +// Dynamically update "error" until the resulting tile data scales within +// a lower and upper bounds. +// +// Revision 1.7 1999/03/29 13:11:10 curt +// Shuffled stl type names a bit. +// Began adding support for tri-fanning (or maybe other arrangments too.) +// +// Revision 1.6 1999/03/27 05:30:16 curt +// Handle corner nodes separately from the rest of the fitted nodes. +// Add fitted nodes in after corners and polygon nodes since the fitted nodes +// are less important. Subsequent nodes will "snap" to previous nodes if +// they are "close enough." +// Need to manually divide segments to prevent "T" intersetions which can +// confound the triangulator. Hey, I got to use a recursive method! +// Pass along correct triangle attributes to output file generator. +// Do fine grained node snapping for corners and polygons, but course grain +// node snapping for fitted terrain nodes. +// +// Revision 1.5 1999/03/23 22:02:55 curt +// Refinements in naming and organization. +// +// Revision 1.4 1999/03/22 23:49:05 curt +// Modifications to facilitate conversion to output format. +// +// Revision 1.3 1999/03/20 02:21:55 curt +// Continue shaping the code towards triangulation bliss. Added code to +// calculate some point guaranteed to be inside a polygon. +// +// Revision 1.2 1999/03/19 22:29:06 curt +// Working on preparationsn for triangulation. +// +// Revision 1.1 1999/03/17 23:52:00 curt +// Initial revision. +// diff --git a/Tools/Triangulate/tripoly.cxx b/Tools/Triangulate/tripoly.cxx new file mode 100644 index 000000000..d01bb8b40 --- /dev/null +++ b/Tools/Triangulate/tripoly.cxx @@ -0,0 +1,189 @@ +// tripoly.cxx -- "Triangle" polygon management class +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include <Include/fg_constants.h> +#include <Math/point3d.hxx> + +#include "tripoly.hxx" + + +// Constructor +FGTriPoly::FGTriPoly( void ) { +} + + +// Destructor +FGTriPoly::~FGTriPoly( void ) { +} + + +// Given a line segment specified by two endpoints p1 and p2, return +// the slope of the line. +static double slope( const Point3D& p0, const Point3D& p1 ) { + if ( fabs(p0.x() - p1.x()) > FG_EPSILON ) { + return (p0.y() - p1.y()) / (p0.x() - p1.x()); + } else { + return 1.0e+999; // really big number + } +} + + +// Given a line segment specified by two endpoints p1 and p2, return +// the y value of a point on the line that intersects with the +// verticle line through x. Return true if an intersection is found, +// false otherwise. +static bool intersects( const Point3D& p0, const Point3D& p1, double x, + Point3D *result ) { + // equation of a line through (x0,y0) and (x1,y1): + // + // y = y1 + (x - x1) * (y0 - y1) / (x0 - x1) + + double y; + + if ( fabs(p0.x() - p1.x()) > FG_EPSILON ) { + y = p1.y() + (x - p1.x()) * (p0.y() - p1.y()) / (p0.x() - p1.x()); + } else { + return false; + } + result->setx(x); + result->sety(y); + + if ( p0.y() <= p1.y() ) { + if ( (p0.y() <= y) && (y <= p1.y()) ) { + return true; + } + } else { + if ( (p0.y() >= y) && (y >= p1.y()) ) { + return true; + } + } + + return false; +} + + +// calculate an "arbitrary" point inside this polygon for assigning +// attribute areas +void FGTriPoly::calc_point_inside( const FGTriNodes& trinodes ) { + Point3D tmp, min, ln, p1, p2, p3, m, result; + + // 1. find point, min, with smallest y + + // min.y() starts greater than the biggest possible lat (degrees) + min.sety( 100.0 ); + // int min_index; + int min_node_index = 0; + + int_list_iterator current, last; + current = poly.begin(); + last = poly.end(); + + int counter = 0; + for ( ; current != last; ++current ) { + tmp = trinodes.get_node( *current ); + if ( tmp.y() < min.y() ) { + min = tmp; + // min_index = *current; + min_node_index = counter; + + // cout << "min index = " << *current + // << " value = " << min_y << endl; + } else { + // cout << " index = " << *current << endl; + } + ++counter; + } + cout << "min node index = " << min_node_index << endl; + cout << "min index = " << poly[min_node_index] + << " value = " << trinodes.get_node( poly[min_node_index] ) + << " == " << min << endl; + + // 2. take midpoint, m, of min with neighbor having lowest + // fabs(slope) + + if ( min_node_index == 0 ) { + p1 = trinodes.get_node( poly[1] ); + p2 = trinodes.get_node( poly[poly.size() - 1] ); + } else if ( min_node_index == (int)(poly.size()) - 1 ) { + p1 = trinodes.get_node( poly[0] ); + p2 = trinodes.get_node( poly[poly.size() - 1] ); + } else { + p1 = trinodes.get_node( poly[min_node_index - 1] ); + p2 = trinodes.get_node( poly[min_node_index + 1] ); + } + double s1 = fabs( slope(min, p1) ); + double s2 = fabs( slope(min, p2) ); + if ( s1 < s2 ) { + ln = p1; + } else { + ln = p2; + } + + m.setx( (min.x() + ln.x()) / 2.0 ); + m.sety( (min.y() + ln.y()) / 2.0 ); + cout << "low mid point = " << m << endl; + + // 3. intersect vertical line through m and all other segments. + // save point, p3, with smallest y > m.y + + p3.sety(100); + for ( int i = 0; i < (int)(poly.size()) - 1; ++i ) { + p1 = trinodes.get_node( poly[i] ); + p2 = trinodes.get_node( poly[i+1] ); + + if ( intersects(p1, p2, m.x(), &result) ) { + // cout << "intersection = " << result << endl; + if ( ( result.y() < p3.y() ) && + ( fabs(result.y() - m.y()) > FG_EPSILON ) ) { + p3 = result; + } + } + } + p1 = trinodes.get_node( poly[0] ); + p2 = trinodes.get_node( poly[poly.size() - 1] ); + if ( intersects(p1, p2, m.x(), &result) ) { + // cout << "intersection = " << result << endl; + if ( ( result.y() < p3.y() ) && + ( fabs(result.y() - m.y()) > FG_EPSILON ) ) { + p3 = result; + } + } + cout << "low intersection of other segment = " << p3 << endl; + + // 4. take midpoint of p2 && m as an arbitrary point inside polygon + + inside.setx( (m.x() + p3.x()) / 2.0 ); + inside.sety( (m.y() + p3.y()) / 2.0 ); + cout << "inside point = " << inside << endl; +} + + +// $Log$ +// Revision 1.2 1999/03/29 13:11:11 curt +// Shuffled stl type names a bit. +// Began adding support for tri-fanning (or maybe other arrangments too.) +// +// Revision 1.1 1999/03/20 13:21:36 curt +// Initial revision. +// diff --git a/Tools/Triangulate/tripoly.hxx b/Tools/Triangulate/tripoly.hxx new file mode 100644 index 000000000..0e156dbae --- /dev/null +++ b/Tools/Triangulate/tripoly.hxx @@ -0,0 +1,106 @@ +// tripoly.hxx -- "Triangle" polygon management class +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _TRIPOLY_HXX +#define _TRIPOLY_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include <Include/compiler.h> + +#include <vector> + +#include <Main/construct_types.hxx> + +#include "trinodes.hxx" + +FG_USING_STD(vector); + + +class FGTriPoly { + +private: + + int_list poly; + Point3D inside; + +public: + + // Constructor and destructor + FGTriPoly( void ); + ~FGTriPoly( void ); + + // Add the specified node (index) to the polygon + inline void add_node( int index ) { poly.push_back( index ); } + + // return size + inline int size() const { return poly.size(); } + + // return the ith polygon point index + inline int get_pt_index( int i ) const { return poly[i]; } + + // calculate an "arbitrary" point inside this polygon for + // assigning attribute areas + void calc_point_inside( const FGTriNodes& trinodes ); + inline Point3D get_point_inside() const { return inside; } + + inline void erase() { poly.erase( poly.begin(), poly.end() ); } +}; + + +typedef vector < FGTriPoly > tripoly_list; +typedef tripoly_list::iterator tripoly_list_iterator; +typedef tripoly_list::const_iterator const_tripoly_list_iterator; + + +#endif // _TRIPOLY_HXX + + +// $Log$ +// Revision 1.5 1999/03/29 13:11:12 curt +// Shuffled stl type names a bit. +// Began adding support for tri-fanning (or maybe other arrangments too.) +// +// Revision 1.4 1999/03/23 22:02:56 curt +// Refinements in naming and organization. +// +// Revision 1.3 1999/03/21 14:02:07 curt +// Added a mechanism to dump out the triangle structures for viewing. +// Fixed a couple bugs in first pass at triangulation. +// - needed to explicitely initialize the polygon accumulator in triangle.cxx +// before each polygon rather than depending on the default behavior. +// - Fixed a problem with region attribute propagation where I wasn't generating +// the hole points correctly. +// +// Revision 1.2 1999/03/20 20:32:58 curt +// First mostly successful tile triangulation works. There's plenty of tweaking +// to do, but we are marching in the right direction. +// +// Revision 1.1 1999/03/20 13:21:36 curt +// Initial revision. +// diff --git a/Tools/Triangulate/trisegs.cxx b/Tools/Triangulate/trisegs.cxx new file mode 100644 index 000000000..d05c6ccf1 --- /dev/null +++ b/Tools/Triangulate/trisegs.cxx @@ -0,0 +1,211 @@ +// trisegs.cxx -- "Triangle" segment management class +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#include <Include/fg_constants.h> +#include <Math/point3d.hxx> + +#include "trinodes.hxx" +#include "trisegs.hxx" + + +// Constructor +FGTriSegments::FGTriSegments( void ) { +} + + +// Destructor +FGTriSegments::~FGTriSegments( void ) { +} + + +// Add a segment to the segment list if it doesn't already exist. +// Returns the index (starting at zero) of the segment in the list. +int FGTriSegments::unique_add( const FGTriSeg& s ) +{ + triseg_list_iterator current, last; + int counter = 0; + + // cout << s.get_n1() << "," << s.get_n2() << endl; + + // check if segment already exists + current = seg_list.begin(); + last = seg_list.end(); + for ( ; current != last; ++current ) { + if ( s == *current ) { + // cout << "found an existing segment match" << endl; + return counter; + } + + ++counter; + } + + // add to list + seg_list.push_back( s ); + + return counter; +} + + +// Divide segment if there are other points on it, return the divided +// list of segments +void FGTriSegments::unique_divide_and_add( const point_list& nodes, + const FGTriSeg& s ) +{ + Point3D p0 = nodes[ s.get_n1() ]; + Point3D p1 = nodes[ s.get_n2() ]; + + bool found_extra = false; + int extra_index = 0; + int counter; + double m, b, y_err, x_err; + const_point_list_iterator current, last; + + // bool temp = false; + // if ( s == FGTriSeg( 170, 206 ) ) { + // cout << "this is it!" << endl; + // temp = true; + // } + + if ( fabs(p0.x() - p1.x()) > 3 * FG_EPSILON ) { + // use y = mx + b + + // sort these in a sensable order + if ( p0.x() > p1.x() ) { + Point3D tmp = p0; + p0 = p1; + p1 = tmp; + } + + m = (p0.y() - p1.y()) / (p0.x() - p1.x()); + b = p1.y() - m * p1.x(); + + // if ( temp ) { + // cout << "m = " << m << " b = " << b << endl; + // } + + current = nodes.begin(); + last = nodes.end(); + counter = 0; + for ( ; current != last; ++current ) { + if ( (current->x() > (p0.x() + FG_EPSILON)) + && (current->x() < (p1.x() - FG_EPSILON)) ) { + + // if ( temp ) { + // cout << counter << endl; + // } + + y_err = fabs(current->y() - (m * current->x() + b)); + + if ( y_err < 10 * FG_EPSILON ) { + cout << "FOUND EXTRA SEGMENT NODE (Y)" << endl; + cout << p0 << " < " << *current << " < " + << p1 << endl; + found_extra = true; + extra_index = counter; + break; + } + } + ++counter; + } + } else { + // use x = constant + + // cout << "FOUND VERTICLE SEGMENT" << endl; + + // sort these in a sensable order + if ( p0.y() > p1.y() ) { + Point3D tmp = p0; + p0 = p1; + p1 = tmp; + } + + // cout << " p0 = " << p0 << " p1 = " << p1 << endl; + + current = nodes.begin(); + last = nodes.end(); + counter = 0; + for ( ; current != last; ++current ) { + // cout << counter << endl; + if ( (current->y() > (p0.y() + FG_EPSILON)) + && (current->y() < (p1.y() - FG_EPSILON)) ) { + x_err = fabs(current->x() - p0.x()); + // cout << " found a potential point, x err = " + // << x_err << endl; + if ( x_err < 10*FG_EPSILON ) { + cout << "FOUND EXTRA SEGMENT NODE (X)" << endl; + cout << p0 << " < " << *current << " < " + << p1 << endl; + found_extra = true; + extra_index = counter; + break; + } + } + ++counter; + } + } + + if ( found_extra ) { + // recurse with two sub segments + cout << "dividing " << s.get_n1() << " " << extra_index + << " " << s.get_n2() << endl; + unique_divide_and_add( nodes, FGTriSeg( s.get_n1(), extra_index ) ); + unique_divide_and_add( nodes, FGTriSeg( extra_index, s.get_n2() ) ); + } else { + // this segment does not need to be divided, lets add it + unique_add( s ); + } +} + + +// $Log$ +// Revision 1.6 1999/04/03 05:22:59 curt +// Found a bug in dividing and adding unique verticle segments which could +// cause the triangulator to end up in an infinite loop. Basically the code +// was correct, but the verticle line test was a bit to selective. +// +// Revision 1.5 1999/03/29 13:11:13 curt +// Shuffled stl type names a bit. +// Began adding support for tri-fanning (or maybe other arrangments too.) +// +// Revision 1.4 1999/03/27 05:30:17 curt +// Handle corner nodes separately from the rest of the fitted nodes. +// Add fitted nodes in after corners and polygon nodes since the fitted nodes +// are less important. Subsequent nodes will "snap" to previous nodes if +// they are "close enough." +// Need to manually divide segments to prevent "T" intersetions which can +// confound the triangulator. Hey, I got to use a recursive method! +// Pass along correct triangle attributes to output file generator. +// Do fine grained node snapping for corners and polygons, but course grain +// node snapping for fitted terrain nodes. +// +// Revision 1.3 1999/03/23 22:02:57 curt +// Refinements in naming and organization. +// +// Revision 1.2 1999/03/20 20:32:59 curt +// First mostly successful tile triangulation works. There's plenty of tweaking +// to do, but we are marching in the right direction. +// +// Revision 1.1 1999/03/20 13:21:36 curt +// Initial revision. +// diff --git a/Tools/Triangulate/trisegs.hxx b/Tools/Triangulate/trisegs.hxx new file mode 100644 index 000000000..2766bcf15 --- /dev/null +++ b/Tools/Triangulate/trisegs.hxx @@ -0,0 +1,142 @@ +// trisegs.hxx -- "Triangle" segment management class +// +// Written by Curtis Olson, started March 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ +// (Log is kept at end of this file) + + +#ifndef _TRISEGS_HXX +#define _TRISEGS_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include <Include/compiler.h> + +#include <vector> + +#include "trinodes.hxx" + +FG_USING_STD(vector); + + +// a segment is two integer pointers into the node list +class FGTriSeg { + int n1, n2; + +public: + + // Constructor and destructor + inline FGTriSeg( void ) { }; + inline FGTriSeg( int i1, int i2 ) { + n1 = i1; + n2 = i2; + } + + inline ~FGTriSeg( void ) { }; + + inline int get_n1() const { return n1; } + inline void set_n1( int i ) { n1 = i; } + inline int get_n2() const { return n2; } + inline void set_n2( int i ) { n2 = i; } + + friend bool operator == (const FGTriSeg& a, const FGTriSeg& b); + +}; + +inline bool operator == (const FGTriSeg& a, const FGTriSeg& b) +{ + return ((a.n1 == b.n1) && (a.n2 == b.n2)) + || ((a.n1 == b.n2) && (a.n2 == b.n1)); +} + + +typedef vector < FGTriSeg > triseg_list; +typedef triseg_list::iterator triseg_list_iterator; +typedef triseg_list::const_iterator const_triseg_list_iterator; + + +class FGTriSegments { + +private: + + triseg_list seg_list; + + // Divide segment if there are other points on it, return the + // divided list of segments + triseg_list divide_segment( const point_list& nodes, + const FGTriSeg& s ); + +public: + + // Constructor and destructor + FGTriSegments( void ); + ~FGTriSegments( void ); + + // delete all the data out of seg_list + inline void clear() { seg_list.clear(); } + + // Add a segment to the segment list if it doesn't already exist. + // Returns the index (starting at zero) of the segment in the + // list. + int unique_add( const FGTriSeg& s ); + + // Add a segment to the segment list if it doesn't already exist. + // Returns the index (starting at zero) of the segment in the list. + void unique_divide_and_add( const point_list& node_list, + const FGTriSeg& s ); + + // return the master node list + inline triseg_list get_seg_list() const { return seg_list; } + + // return the ith segment + inline FGTriSeg get_seg( int i ) const { return seg_list[i]; } +}; + + +#endif // _TRISEGS_HXX + + +// $Log$ +// Revision 1.4 1999/04/05 02:17:14 curt +// Dynamically update "error" until the resulting tile data scales within +// a lower and upper bounds. +// +// Revision 1.3 1999/03/27 05:30:18 curt +// Handle corner nodes separately from the rest of the fitted nodes. +// Add fitted nodes in after corners and polygon nodes since the fitted nodes +// are less important. Subsequent nodes will "snap" to previous nodes if +// they are "close enough." +// Need to manually divide segments to prevent "T" intersetions which can +// confound the triangulator. Hey, I got to use a recursive method! +// Pass along correct triangle attributes to output file generator. +// Do fine grained node snapping for corners and polygons, but course grain +// node snapping for fitted terrain nodes. +// +// Revision 1.2 1999/03/20 20:33:00 curt +// First mostly successful tile triangulation works. There's plenty of tweaking +// to do, but we are marching in the right direction. +// +// Revision 1.1 1999/03/20 13:21:36 curt +// Initial revision. +// diff --git a/Tools/Utils/Makefile.am b/Tools/Utils/Makefile.am new file mode 100644 index 000000000..5c3f005b0 --- /dev/null +++ b/Tools/Utils/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = \ + Makedir