From: Tim Moore Date: Mon, 14 Sep 2009 11:43:55 +0000 (+0200) Subject: Merge FG_Tools as subdirectory X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=c54ce2dd8509aac98c0678d6955076b2d1234756;p=flightgear.git Merge FG_Tools as subdirectory --- c54ce2dd8509aac98c0678d6955076b2d1234756 diff --cc Tools/Areas/Makefile.am index 000000000,000000000..7c6106189 new file mode 100644 --- /dev/null +++ b/Tools/Areas/Makefile.am @@@ -1,0 -1,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 . ++# ++# Revision 1.1 1998/01/15 02:45:25 curt ++# Initial revision. ++# ++ diff --cc Tools/Areas/area.cxx index 000000000,000000000..2dabe331d new file mode 100644 --- /dev/null +++ b/Tools/Areas/area.cxx @@@ -1,0 -1,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 ++#include ++ ++#include ++ ++#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 --cc Tools/Areas/area.hxx index 000000000,000000000..584e5f6a0 new file mode 100644 --- /dev/null +++ b/Tools/Areas/area.hxx @@@ -1,0 -1,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 --cc Tools/Areas/main.cxx index 000000000,000000000..1f6d635b8 new file mode 100644 --- /dev/null +++ b/Tools/Areas/main.cxx @@@ -1,0 -1,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 ++#endif ++ ++#ifdef HAVE_STDLIB_H ++#include ++#endif ++ ++#include ++#include ++ ++#include "area.hxx" ++ ++#include ++#include ++ ++ ++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 \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 --cc Tools/Array/Makefile.am index 000000000,000000000..a62648005 new file mode 100644 --- /dev/null +++ b/Tools/Array/Makefile.am @@@ -1,0 -1,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 --cc Tools/Array/array.cxx index 000000000,000000000..2b7d2a625 new file mode 100644 --- /dev/null +++ b/Tools/Array/array.cxx @@@ -1,0 -1,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 ++#endif ++ ++#include ++ ++// #include // isspace() ++// #include // atoi() ++// #include // rint() ++// #include ++// #include ++// #ifdef HAVE_SYS_STAT_H ++// # include // stat() ++// #endif ++// #ifdef FG_HAVE_STD_INCLUDES ++// # include ++// #else ++// # include ++// #endif ++// #ifdef HAVE_UNISTD_H ++// # include // stat() ++// #endif ++ ++#include STL_STRING ++ ++#include ++#include ++#include ++#include ++ ++#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 <> 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 < 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 --cc Tools/Array/array.hxx index 000000000,000000000..2ec9e2a51 new file mode 100644 --- /dev/null +++ b/Tools/Array/array.hxx @@@ -1,0 -1,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 ++ ++#include ++#include ++#include ++ ++#include
++ ++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 --cc Tools/Array/testarray.cxx index 000000000,000000000..1a0a4d55c new file mode 100644 --- /dev/null +++ b/Tools/Array/testarray.cxx @@@ -1,0 -1,0 +1,33 @@@ ++#include ++ ++#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 --cc Tools/AssemTris/Makefile.am index 000000000,000000000..5e830c88b new file mode 100644 --- /dev/null +++ b/Tools/AssemTris/Makefile.am @@@ -1,0 -1,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 . ++# ++# Revision 1.1 1998/01/15 02:45:25 curt ++# Initial revision. ++# ++ diff --cc Tools/AssemTris/assemtris.cxx index 000000000,000000000..1c87851e3 new file mode 100644 --- /dev/null +++ b/Tools/AssemTris/assemtris.cxx @@@ -1,0 -1,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 ++#include ++#include // for atoi() ++#include ++#include // for stat() ++#include // for stat() ++ ++#include "assemtris.hxx" ++ ++#include ++#include ++ ++ ++// #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 --cc Tools/AssemTris/assemtris.hxx index 000000000,000000000..3c96881f4 new file mode 100644 --- /dev/null +++ b/Tools/AssemTris/assemtris.hxx @@@ -1,0 -1,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 ++#include ++ ++ ++#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 --cc Tools/CVSROOT/checkoutlist index 000000000,000000000..b04b3501f new file mode 100644 --- /dev/null +++ b/Tools/CVSROOT/checkoutlist @@@ -1,0 -1,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: ++# ++# [] ++# ++# comment lines begin with '#' diff --cc Tools/CVSROOT/commitinfo index 000000000,000000000..b19e7b7a6 new file mode 100644 --- /dev/null +++ b/Tools/CVSROOT/commitinfo @@@ -1,0 -1,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 --cc Tools/CVSROOT/cvswrappers index 000000000,000000000..5047bf1c5 new file mode 100644 --- /dev/null +++ b/Tools/CVSROOT/cvswrappers @@@ -1,0 -1,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 --cc Tools/CVSROOT/editinfo index 000000000,000000000..d78886c15 new file mode 100644 --- /dev/null +++ b/Tools/CVSROOT/editinfo @@@ -1,0 -1,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 --cc Tools/CVSROOT/loginfo index 000000000,000000000..226e93771 new file mode 100644 --- /dev/null +++ b/Tools/CVSROOT/loginfo @@@ -1,0 -1,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 --cc Tools/CVSROOT/modules index 000000000,000000000..cb9e9efc9 new file mode 100644 --- /dev/null +++ b/Tools/CVSROOT/modules @@@ -1,0 -1,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 --cc Tools/CVSROOT/notify index 000000000,000000000..34f0bc288 new file mode 100644 --- /dev/null +++ b/Tools/CVSROOT/notify @@@ -1,0 -1,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 --cc Tools/CVSROOT/rcsinfo index 000000000,000000000..49e59f4d0 new file mode 100644 --- /dev/null +++ b/Tools/CVSROOT/rcsinfo @@@ -1,0 -1,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 --cc Tools/CVSROOT/taginfo index 000000000,000000000..274a46dd5 new file mode 100644 --- /dev/null +++ b/Tools/CVSROOT/taginfo @@@ -1,0 -1,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 --cc Tools/Clipper/Makefile.am index 000000000,000000000..87a59e28b new file mode 100644 --- /dev/null +++ b/Tools/Clipper/Makefile.am @@@ -1,0 -1,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 --cc Tools/Clipper/clipper.cxx index 000000000,000000000..a088f0ffb new file mode 100644 --- /dev/null +++ b/Tools/Clipper/clipper.cxx @@@ -1,0 -1,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 ++#include ++#include ++#include ++ ++#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 --cc Tools/Clipper/clipper.hxx index 000000000,000000000..469691a2a new file mode 100644 --- /dev/null +++ b/Tools/Clipper/clipper.hxx @@@ -1,0 -1,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 Generic Polygon Clipping Library ++// ++// http://www.cs.man.ac.uk/aig/staff/alan/software/ ++// ++extern "C" { ++#include ++} ++ ++#include STL_STRING ++#include ++ ++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 --cc Tools/Clipper/testclipper.cxx index 000000000,000000000..2a0477b48 new file mode 100644 --- /dev/null +++ b/Tools/Clipper/testclipper.cxx @@@ -1,0 -1,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 ++#include ++ ++#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 --cc Tools/Combine/Makefile.am index 000000000,000000000..c8c640202 new file mode 100644 --- /dev/null +++ b/Tools/Combine/Makefile.am @@@ -1,0 -1,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 --cc Tools/Combine/genfans.cxx index 000000000,000000000..856a3d79c new file mode 100644 --- /dev/null +++ b/Tools/Combine/genfans.cxx @@@ -1,0 -1,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 --cc Tools/Combine/genfans.hxx index 000000000,000000000..55af05d70 new file mode 100644 --- /dev/null +++ b/Tools/Combine/genfans.hxx @@@ -1,0 -1,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 ++ ++#include
++#include ++ ++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 --cc Tools/Construct/Makefile.am index 000000000,000000000..f19226325 new file mode 100644 --- /dev/null +++ b/Tools/Construct/Makefile.am @@@ -1,0 -1,0 +1,7 @@@ ++SUBDIRS = \ ++ Array \ ++ Clipper \ ++ Combine \ ++ GenOutput \ ++ Triangulate \ ++ Main diff --cc Tools/DEM/Makefile.am index 000000000,000000000..91bfd90d6 new file mode 100644 --- /dev/null +++ b/Tools/DEM/Makefile.am @@@ -1,0 -1,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 --cc Tools/DEM/dem.cxx index 000000000,000000000..e4661a3c7 new file mode 100644 --- /dev/null +++ b/Tools/DEM/dem.cxx @@@ -1,0 -1,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 ++#endif ++ ++#include ++ ++#include // isspace() ++#include // atoi() ++#include // rint() ++#include ++#include ++ ++#ifdef HAVE_SYS_STAT_H ++# include // stat() ++#endif ++ ++#ifdef FG_HAVE_STD_INCLUDES ++# include ++#else ++# include ++#endif ++ ++#ifdef HAVE_UNISTD_H ++# include // stat() ++#endif ++ ++#include ++#include ++#include ++ ++#include "dem.hxx" ++ ++ ++#define MAX_EX_NODES 10000 ++ ++#if 0 ++#ifdef WIN32 ++# ifdef __BORLANDC__ ++# include ++# 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" ++// ++// 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 --cc Tools/DEM/dem.hxx index 000000000,000000000..46849e3a4 new file mode 100644 --- /dev/null +++ b/Tools/DEM/dem.hxx @@@ -1,0 -1,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 ++#include ++ ++ ++#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 --cc Tools/DemChop/Makefile.am index 000000000,000000000..9103e673b new file mode 100644 --- /dev/null +++ b/Tools/DemChop/Makefile.am @@@ -1,0 -1,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 --cc Tools/DemChop/demchop.cxx index 000000000,000000000..8182c8d8e new file mode 100644 --- /dev/null +++ b/Tools/DemChop/demchop.cxx @@@ -1,0 -1,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 STL_STRING ++ ++#include ++#include ++#include ++ ++#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] << " " ); ++ 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 --cc Tools/DemChop/point2d.cxx index 000000000,000000000..817015f8a new file mode 100644 --- /dev/null +++ b/Tools/DemChop/point2d.cxx @@@ -1,0 -1,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 ++ ++#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 --cc Tools/DemChop/point2d.hxx index 000000000,000000000..e4df44488 new file mode 100644 --- /dev/null +++ b/Tools/DemChop/point2d.hxx @@@ -1,0 -1,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 ++ ++ ++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 --cc Tools/DemInfo/Makefile.am index 000000000,000000000..b3d8d3aa7 new file mode 100644 --- /dev/null +++ b/Tools/DemInfo/Makefile.am @@@ -1,0 -1,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 --cc Tools/DemInfo/deminfo.cxx index 000000000,000000000..75f9c2f55 new file mode 100644 --- /dev/null +++ b/Tools/DemInfo/deminfo.cxx @@@ -1,0 -1,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 ++#include ++#include ++#include ++ ++#include ++ ++ ++// 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 \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 --cc Tools/DemInfo/gather-dem-info.pl index 000000000,000000000..746d7120e new file mode 100755 --- /dev/null +++ b/Tools/DemInfo/gather-dem-info.pl @@@ -1,0 -1,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 ( ) { ++ if ( m/Results = / ) { ++ $_ =~ s/Results = //; ++ print $_; ++ } ++ } ++ close(INFO); ++ } ++ } ++} ++ ++ ++#--------------------------------------------------------------------------- ++# $Log$ ++# Revision 1.1 1998/06/04 19:18:06 curt ++# Initial revision. ++# diff --cc Tools/DemRaw2ascii/Makefile.am index 000000000,000000000..b638e2e96 new file mode 100644 --- /dev/null +++ b/Tools/DemRaw2ascii/Makefile.am @@@ -1,0 -1,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 --cc Tools/DemRaw2ascii/main.c index 000000000,000000000..8cc0f2ffa new file mode 100644 --- /dev/null +++ b/Tools/DemRaw2ascii/main.c @@@ -1,0 -1,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 ++#include ++ ++#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 \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 --cc Tools/DemRaw2ascii/rawdem.c index 000000000,000000000..8411035bd new file mode 100644 --- /dev/null +++ b/Tools/DemRaw2ascii/rawdem.c @@@ -1,0 -1,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 ++#endif ++ ++#include /* rint() */ ++#include ++#include /* atoi() atof() */ ++#include /* swab() */ ++ ++#include /* open() */ ++#include ++#include ++#include /* 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 --cc Tools/DemRaw2ascii/rawdem.h index 000000000,000000000..8877b2eaa new file mode 100644 --- /dev/null +++ b/Tools/DemRaw2ascii/rawdem.h @@@ -1,0 -1,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 --cc Tools/FixNode/Makefile.am index 000000000,000000000..a245e6ac0 new file mode 100644 --- /dev/null +++ b/Tools/FixNode/Makefile.am @@@ -1,0 -1,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 . ++# ++# Revision 1.1 1997/11/27 00:17:32 curt ++# Initial revision. ++# diff --cc Tools/FixNode/fixnode.cxx index 000000000,000000000..5269ac459 new file mode 100644 --- /dev/null +++ b/Tools/FixNode/fixnode.cxx @@@ -1,0 -1,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 ++#include ++#include ++#include ++ ++#ifdef HAVE_STDLIB_H ++# include ++#endif // HAVE_STDLIB_H ++ ++#include ++ ++#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 --cc Tools/FixNode/fixnode.hxx index 000000000,000000000..80b6eb2b6 new file mode 100644 --- /dev/null +++ b/Tools/FixNode/fixnode.hxx @@@ -1,0 -1,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 ++#include ++#include ++ ++#include ++#include "Include/fg_stl_config.h" ++ ++#ifdef NEEDNAMESPACESTD ++using namespace std; ++#endif ++ ++#include ++#include ++ ++ ++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 --cc Tools/FixNode/main.cxx index 000000000,000000000..68e540802 new file mode 100644 --- /dev/null +++ b/Tools/FixNode/main.cxx @@@ -1,0 -1,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 ++#include ++// #include ++#include ++#include ++ ++#ifdef HAVE_STDLIB_H ++# include ++#endif // HAVE_STDLIB_H ++ ++#include ++ ++#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 ++// ++// 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 --cc Tools/FixObj/Makefile.am index 000000000,000000000..7ced91cc9 new file mode 100644 --- /dev/null +++ b/Tools/FixObj/Makefile.am @@@ -1,0 -1,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 . ++# ++# Revision 1.1 1997/12/08 19:28:54 curt ++# Initial revision. ++# diff --cc Tools/FixObj/main.cxx index 000000000,000000000..bf117a10c new file mode 100644 --- /dev/null +++ b/Tools/FixObj/main.cxx @@@ -1,0 -1,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 ++ ++#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 --cc Tools/FixObj/obj.cxx index 000000000,000000000..4239a1891 new file mode 100644 --- /dev/null +++ b/Tools/FixObj/obj.cxx @@@ -1,0 -1,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 ++#include ++#include ++ ++#include ++#include "Include/compiler.h" ++ ++#ifdef NEEDNAMESPACESTD ++using namespace std; ++#endif ++ ++#include "obj.hxx" ++ ++#include ++#include ++ ++ ++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 --cc Tools/FixObj/obj.hxx index 000000000,000000000..59a362c8d new file mode 100644 --- /dev/null +++ b/Tools/FixObj/obj.hxx @@@ -1,0 -1,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 --cc Tools/GenAirports/Makefile.am index 000000000,000000000..86c8ecb16 new file mode 100644 --- /dev/null +++ b/Tools/GenAirports/Makefile.am @@@ -1,0 -1,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 . ++# ++# Revision 1.1 1998/01/15 02:45:25 curt ++# Initial revision. ++# ++ diff --cc Tools/GenAirports/area.cxx index 000000000,000000000..75d785377 new file mode 100644 --- /dev/null +++ b/Tools/GenAirports/area.cxx @@@ -1,0 -1,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 ++#include ++ ++#include ++ ++#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 --cc Tools/GenAirports/area.hxx index 000000000,000000000..a1bbd342e new file mode 100644 --- /dev/null +++ b/Tools/GenAirports/area.hxx @@@ -1,0 -1,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 ++ ++#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 --cc Tools/GenAirports/convex_hull.cxx index 000000000,000000000..0cdff1dcb new file mode 100644 --- /dev/null +++ b/Tools/GenAirports/convex_hull.cxx @@@ -1,0 -1,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 ++#include ++ ++#include ++#include ++ ++#ifdef NEEDNAMESPACESTD ++using namespace std; ++#endif ++ ++#include ++ ++#include "convex_hull.hxx" ++#include "point2d.hxx" ++ ++ ++// stl map typedefs ++typedef map < double, double, less > 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 --cc Tools/GenAirports/convex_hull.hxx index 000000000,000000000..42bf0db83 new file mode 100644 --- /dev/null +++ b/Tools/GenAirports/convex_hull.hxx @@@ -1,0 -1,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 ++ ++#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 --cc Tools/GenAirports/main.cxx index 000000000,000000000..7ad07f6db new file mode 100644 --- /dev/null +++ b/Tools/GenAirports/main.cxx @@@ -1,0 -1,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 ++#endif ++ ++#include ++ ++#ifdef HAVE_STDLIB_H ++#include ++#endif ++ ++#include ++#include ++#include ++#include STL_STRING ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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] << " " ); ++ 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 --cc Tools/GenAirports/point2d.cxx index 000000000,000000000..755224477 new file mode 100644 --- /dev/null +++ b/Tools/GenAirports/point2d.cxx @@@ -1,0 -1,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 ++ ++#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 --cc Tools/GenAirports/point2d.hxx index 000000000,000000000..199524047 new file mode 100644 --- /dev/null +++ b/Tools/GenAirports/point2d.hxx @@@ -1,0 -1,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 ++ ++ ++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 --cc Tools/GenOutput/Makefile.am index 000000000,000000000..28ad535a2 new file mode 100644 --- /dev/null +++ b/Tools/GenOutput/Makefile.am @@@ -1,0 -1,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 --cc Tools/GenOutput/genobj.cxx index 000000000,000000000..fc3cc3b68 new file mode 100644 --- /dev/null +++ b/Tools/GenOutput/genobj.cxx @@@ -1,0 -1,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 ++ ++#include ++#include ++#include ++ ++#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 --cc Tools/GenOutput/genobj.hxx index 000000000,000000000..55f984452 new file mode 100644 --- /dev/null +++ b/Tools/GenOutput/genobj.hxx @@@ -1,0 -1,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 STL_STRING ++ ++#include ++#include ++#include ++ ++#include ++#include
++#include ++ ++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 --cc Tools/Lib/Makefile.am index 000000000,000000000..6c6ad9d3e new file mode 100644 --- /dev/null +++ b/Tools/Lib/Makefile.am @@@ -1,0 -1,0 +1,4 @@@ ++SUBDIRS = \ ++ DEM \ ++ Polygon \ ++ Triangle diff --cc Tools/Main/Makefile.am index 000000000,000000000..95e1e4f14 new file mode 100644 --- /dev/null +++ b/Tools/Main/Makefile.am @@@ -1,0 -1,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 --cc Tools/Main/construct.cxx index 000000000,000000000..1c5c117a9 new file mode 100644 --- /dev/null +++ b/Tools/Main/construct.cxx @@@ -1,0 -1,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 // for directory reading ++#include // for directory reading ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++// 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] << " " << 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 --cc Tools/Main/construct_types.hxx index 000000000,000000000..095e48c56 new file mode 100644 --- /dev/null +++ b/Tools/Main/construct_types.hxx @@@ -1,0 -1,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 ++ ++#include ++ ++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 --cc Tools/Makedir/Makefile.am index 000000000,000000000..e93857cc0 new file mode 100644 --- /dev/null +++ b/Tools/Makedir/Makefile.am @@@ -1,0 -1,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 --cc Tools/Makedir/makedir.cxx index 000000000,000000000..1bc7734cb new file mode 100644 --- /dev/null +++ b/Tools/Makedir/makedir.cxx @@@ -1,0 -1,0 +1,119 @@@ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#ifdef HAVE_STDLIB_H ++#include ++#endif ++ ++#include ++#include // stat() ++#include // stat() ++ ++#include ++ ++#include ++ ++ ++ ++#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 --cc Tools/Polygon/Makefile.am index 000000000,000000000..caa4c59be new file mode 100644 --- /dev/null +++ b/Tools/Polygon/Makefile.am @@@ -1,0 -1,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 --cc Tools/Polygon/index.cxx index 000000000,000000000..34352aacd new file mode 100644 --- /dev/null +++ b/Tools/Polygon/index.cxx @@@ -1,0 -1,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 STL_STRING ++ ++#include ++ ++#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 --cc Tools/Polygon/index.hxx index 000000000,000000000..0ea6b5c06 new file mode 100644 --- /dev/null +++ b/Tools/Polygon/index.hxx @@@ -1,0 -1,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 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 --cc Tools/Polygon/names.cxx index 000000000,000000000..2633d19a6 new file mode 100644 --- /dev/null +++ b/Tools/Polygon/names.cxx @@@ -1,0 -1,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 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 --cc Tools/Polygon/names.hxx index 000000000,000000000..0a919a5ed new file mode 100644 --- /dev/null +++ b/Tools/Polygon/names.hxx @@@ -1,0 -1,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 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 --cc Tools/Prep/Makefile.am index 000000000,000000000..96c87b412 new file mode 100644 --- /dev/null +++ b/Tools/Prep/Makefile.am @@@ -1,0 -1,0 +1,6 @@@ ++SUBDIRS = \ ++ DemChop \ ++ DemInfo \ ++ DemRaw2ascii \ ++ GenAirports \ ++ ShapeFile diff --cc Tools/ShapeFile/Makefile.am index 000000000,000000000..b3c41b82b new file mode 100644 --- /dev/null +++ b/Tools/ShapeFile/Makefile.am @@@ -1,0 -1,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 --cc Tools/ShapeFile/main.cxx index 000000000,000000000..b0e629575 new file mode 100644 --- /dev/null +++ b/Tools/ShapeFile/main.cxx @@@ -1,0 -1,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 ++#include ++#include ++#undef E ++#undef DEG_TO_RAD ++#undef RAD_TO_DEG ++ ++// include Generic Polygon Clipping Library ++extern "C" { ++#include ++} ++ ++#include ++ ++#include STL_STRING ++ ++#include ++ ++#include ++#include ++#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] ++ << " " ); ++ 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 --cc Tools/ShapeFile/shape.cxx index 000000000,000000000..badef8823 new file mode 100644 --- /dev/null +++ b/Tools/ShapeFile/shape.cxx @@@ -1,0 -1,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 STL_STRING ++ ++#include ++#include ++ ++#include ++#include ++#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 --cc Tools/ShapeFile/shape.hxx index 000000000,000000000..6a59b77e3 new file mode 100644 --- /dev/null +++ b/Tools/ShapeFile/shape.hxx @@@ -1,0 -1,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 ++} ++ ++#include ++ ++ ++// 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 --cc Tools/SplitTris/Makefile.am index 000000000,000000000..e8f64e5b9 new file mode 100644 --- /dev/null +++ b/Tools/SplitTris/Makefile.am @@@ -1,0 -1,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 . ++# ++# 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 --cc Tools/SplitTris/splittris.cxx index 000000000,000000000..20e26e7a9 new file mode 100644 --- /dev/null +++ b/Tools/SplitTris/splittris.cxx @@@ -1,0 -1,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 ++#include ++#include // for atoi() ++#include ++#include // for stat() ++#include // for stat() ++ ++#include "splittris.hxx" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++// 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 --cc Tools/SplitTris/splittris.hxx index 000000000,000000000..12787872d new file mode 100644 --- /dev/null +++ b/Tools/SplitTris/splittris.hxx @@@ -1,0 -1,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 ++#include ++#include ++ ++#include ++#include "Include/fg_stl_config.h" ++ ++#ifdef NEEDNAMESPACESTD ++using namespace std; ++#endif ++ ++#include ++ ++ ++// 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 --cc Tools/Stripe_u/Makefile.am index 000000000,000000000..8373ea88b new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/Makefile.am @@@ -1,0 -1,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 --cc Tools/Stripe_u/add.c index 000000000,000000000..7d90357fd new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/add.c @@@ -1,0 -1,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 ++#include ++#include ++#include ++#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 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 --cc Tools/Stripe_u/add.h index 000000000,000000000..607363e5e new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/add.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/bands.c index 000000000,000000000..14a9fe4c0 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/bands.c @@@ -1,0 -1,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 ++#include ++#include ++#include ++#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 ++#include ++/* TIMING for UNIX */ ++#else ++#include ++#include ++#include ++#include ++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'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 --cc Tools/Stripe_u/common.c index 000000000,000000000..6e553f149 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/common.c @@@ -1,0 -1,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 ++#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; xpPolygon+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;xnPolSize; ++ 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 --cc Tools/Stripe_u/common.h index 000000000,000000000..a220b3628 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/common.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/define.h index 000000000,000000000..931e5713f new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/define.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/extend.h index 000000000,000000000..78c135e3a new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/extend.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/free.c index 000000000,000000000..9494f4fb4 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/free.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_u/free.h index 000000000,000000000..3303d05e1 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/free.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/global.h index 000000000,000000000..3621b25e1 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/global.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/glove.h index 000000000,000000000..74bcd07da new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/glove.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/init.c index 000000000,000000000..b3b218cec new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/init.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_u/init.h index 000000000,000000000..2faf0e838 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/init.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/local.c index 000000000,000000000..2db94904e new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/local.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_u/local.h index 000000000,000000000..34769ebb8 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/local.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/my_global.h index 000000000,000000000..629c12074 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/my_global.h @@@ -1,0 -1,0 +1,3 @@@ ++int change_in_stripEx = 0; ++int change_in_strip = 0; ++ diff --cc Tools/Stripe_u/newpolve.c index 000000000,000000000..9adbfd651 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/newpolve.c @@@ -1,0 -1,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 ++#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; xnPolSize; ++ ++ /* 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; xnPolSize; ++ 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 --cc Tools/Stripe_u/options.c index 000000000,000000000..7322d937c new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/options.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_u/options.h index 000000000,000000000..055d33ea7 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/options.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/output.c index 000000000,000000000..d6c30f613 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/output.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_u/output.h index 000000000,000000000..d9aed50a9 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/output.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/outputex.c index 000000000,000000000..48d4da147 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/outputex.c @@@ -1,0 -1,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 ++#include ++#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; cpPolygon+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 --cc Tools/Stripe_u/outputex.h index 000000000,000000000..68cff0ca2 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/outputex.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/partial.c index 000000000,000000000..9afb03c19 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/partial.c @@@ -1,0 -1,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 ++#include ++#include ++#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 --cc Tools/Stripe_u/partial.h index 000000000,000000000..c9a9439c8 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/partial.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/polverts.h index 000000000,000000000..79ece86db new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/polverts.h @@@ -1,0 -1,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 ++ ++ ++/* 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 --cc Tools/Stripe_u/polvertsex.h index 000000000,000000000..8e05a7dd3 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/polvertsex.h @@@ -1,0 -1,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 ++ ++ ++/* 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 --cc Tools/Stripe_u/queue.c index 000000000,000000000..29d5e9de0 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/queue.c @@@ -1,0 -1,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 --cc Tools/Stripe_u/queue.h index 000000000,000000000..0bf926e0f new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/queue.h @@@ -1,0 -1,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 ++#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 --cc Tools/Stripe_u/sgi_triang.c index 000000000,000000000..40dff70f6 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/sgi_triang.c @@@ -1,0 -1,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 ++#include ++#include ++#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 --cc Tools/Stripe_u/sgi_triangex.c index 000000000,000000000..afa2fd7db new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/sgi_triangex.c @@@ -1,0 -1,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 ++#include ++#include ++#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= 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 --cc Tools/Stripe_u/struct.c index 000000000,000000000..f822b1da7 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/struct.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_u/struct.h index 000000000,000000000..6eb036cb4 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/struct.h @@@ -1,0 -1,0 +1,6 @@@ ++ ++struct vert_struct { ++ VRDATA x, y, z; /* point coordinates */ ++}; ++ ++ diff --cc Tools/Stripe_u/structex.c index 000000000,000000000..06adba7b5 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/structex.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_u/sturcts.h index 000000000,000000000..07a2bbf97 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/sturcts.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/sturctsex.h index 000000000,000000000..550228306 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/sturctsex.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/ties.c index 000000000,000000000..e1a64545c new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/ties.c @@@ -1,0 -1,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 ++#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 --cc Tools/Stripe_u/ties.h index 000000000,000000000..97e051765 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/ties.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/triangulate.h index 000000000,000000000..de612119f new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/triangulate.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/triangulatex.h index 000000000,000000000..1710f0182 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/triangulatex.h @@@ -1,0 -1,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 --cc Tools/Stripe_u/util.c index 000000000,000000000..f17fe5f7c new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/util.c @@@ -1,0 -1,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 ++#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; xpPolygon+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 --cc Tools/Stripe_u/util.h index 000000000,000000000..2b43a4d35 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_u/util.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/Makefile.am index 000000000,000000000..ac0280b8a new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/Makefile.am @@@ -1,0 -1,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 --cc Tools/Stripe_w/add.c index 000000000,000000000..ef6e673d8 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/add.c @@@ -1,0 -1,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 ++#include ++#include ++#include ++#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 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 --cc Tools/Stripe_w/add.h index 000000000,000000000..fb37f729a new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/add.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/bands.c index 000000000,000000000..161e8ccde new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/bands.c @@@ -1,0 -1,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 ++#endif ++ ++#include ++#include ++#include ++#include ++#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 ++# include ++/* TIMING for UNIX */ ++#else ++# include ++# include ++# include ++# if defined(__FreeBSD__) ++# ifndef HZ ++# include ++# 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'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 --cc Tools/Stripe_w/common.c index 000000000,000000000..ca836e19f new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/common.c @@@ -1,0 -1,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 ++#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; xpPolygon+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;xnPolSize; ++ 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 --cc Tools/Stripe_w/common.h index 000000000,000000000..aca19824b new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/common.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/extend.h index 000000000,000000000..78c135e3a new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/extend.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/free.c index 000000000,000000000..ad655fcf7 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/free.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_w/free.h index 000000000,000000000..4c1d055c6 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/free.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/global.h index 000000000,000000000..3538f82f4 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/global.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/init.c index 000000000,000000000..2e0f25885 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/init.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_w/init.h index 000000000,000000000..fe9a05fd7 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/init.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/local.c index 000000000,000000000..3f3e69da1 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/local.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_w/local.h index 000000000,000000000..df5256f6a new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/local.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/newpolve.c index 000000000,000000000..6267bbc42 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/newpolve.c @@@ -1,0 -1,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 ++#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; xnPolSize; ++ ++ /* 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; xnPolSize; ++ 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 --cc Tools/Stripe_w/options.c index 000000000,000000000..7e1243e24 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/options.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_w/options.h index 000000000,000000000..34c10bcc1 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/options.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/output.c index 000000000,000000000..16eb6abe6 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/output.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_w/output.h index 000000000,000000000..fd4b34c27 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/output.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/outputex.c index 000000000,000000000..10bfb741f new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/outputex.c @@@ -1,0 -1,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 ++#include ++#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; cpPolygon+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 --cc Tools/Stripe_w/outputex.h index 000000000,000000000..f59f7e75c new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/outputex.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/partial.c index 000000000,000000000..847b40501 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/partial.c @@@ -1,0 -1,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 ++#include ++#include ++#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 --cc Tools/Stripe_w/partial.h index 000000000,000000000..6a4e3a5b1 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/partial.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/polverts.h index 000000000,000000000..b3979eb4b new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/polverts.h @@@ -1,0 -1,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 ++#endif ++ ++#include "queue.h" ++ ++#ifdef HAVE_STDLIB_H ++# include ++#else ++# include ++#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 --cc Tools/Stripe_w/polyvertsex.h index 000000000,000000000..4c541f7bc new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/polyvertsex.h @@@ -1,0 -1,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 ++#endif ++ ++#ifdef HAVE_STDLIB_H ++# include ++#else ++# include ++#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 --cc Tools/Stripe_w/queue.c index 000000000,000000000..966f20379 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/queue.c @@@ -1,0 -1,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 --cc Tools/Stripe_w/queue.h index 000000000,000000000..0bf926e0f new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/queue.h @@@ -1,0 -1,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 ++#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 --cc Tools/Stripe_w/sgi_triang.c index 000000000,000000000..1a130906e new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/sgi_triang.c @@@ -1,0 -1,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 ++#include ++#include ++#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 --cc Tools/Stripe_w/sgi_triangex.c index 000000000,000000000..9f153a1e8 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/sgi_triangex.c @@@ -1,0 -1,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 ++#include ++#include ++#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= 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 --cc Tools/Stripe_w/struct.c index 000000000,000000000..7010012b7 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/struct.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_w/structex.c index 000000000,000000000..70359ddc0 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/structex.c @@@ -1,0 -1,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 ++#include ++#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 --cc Tools/Stripe_w/sturcts.h index 000000000,000000000..57490a695 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/sturcts.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/sturctsex.h index 000000000,000000000..6a4a76dbe new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/sturctsex.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/ties.c index 000000000,000000000..e3fd31f78 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/ties.c @@@ -1,0 -1,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 ++#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 --cc Tools/Stripe_w/ties.h index 000000000,000000000..502aabf4d new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/ties.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/triangulate.h index 000000000,000000000..1f677430a new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/triangulate.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/triangulatex.h index 000000000,000000000..3b2d8fb5c new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/triangulatex.h @@@ -1,0 -1,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 --cc Tools/Stripe_w/util.c index 000000000,000000000..f17fe5f7c new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/util.c @@@ -1,0 -1,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 ++#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; xpPolygon+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 --cc Tools/Stripe_w/util.h index 000000000,000000000..64c2f2334 new file mode 100644 --- /dev/null +++ b/Tools/Stripe_w/util.h @@@ -1,0 -1,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 --cc Tools/Tools/Makefile.am index 000000000,000000000..034024ed4 new file mode 100644 --- /dev/null +++ b/Tools/Tools/Makefile.am @@@ -1,0 -1,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 --cc Tools/Tools/README index 000000000,000000000..253e47e04 new file mode 100644 --- /dev/null +++ b/Tools/Tools/README @@@ -1,0 -1,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 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 ++ ++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. diff --cc Tools/Tools/Todo index 000000000,000000000..d056dfc03 new file mode 100644 --- /dev/null +++ b/Tools/Tools/Todo @@@ -1,0 -1,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 --cc Tools/Tools/process-dem.pl index 000000000,000000000..5a92c5bb5 new file mode 100755 --- /dev/null +++ b/Tools/Tools/process-dem.pl @@@ -1,0 -1,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 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 ( ) { ++ 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 ( ) { ++ 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 ( ) { ++ 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 ( ) { ++ 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 ( ) { ++ 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 ( ) { ++ 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 ( ) { ++ 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 = ; ++ open(OUT, "$command |"); ++ while ( ) { ++ print $_; ++ } ++ close(OUT); ++ ++ # copy to destination file ++ # $newfile = $file; ++ # $newfile =~ s/\.1\.obj$//; ++ # print "Copying to $subdir/$newfile.2.obj\n"; ++ # open(IN, "$subdir/$newfile.2.obj"); ++ # while ( ) { ++ # 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 ( ) { ++ 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 ( ) { ++ 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 ( ) { ++ 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 ( ) { ++ 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 --cc Tools/Tools/scenery_version.hxx index 000000000,000000000..c5616a646 new file mode 100644 --- /dev/null +++ b/Tools/Tools/scenery_version.hxx @@@ -1,0 -1,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 --cc Tools/Tri2obj/Makefile.am index 000000000,000000000..c27fc320f new file mode 100644 --- /dev/null +++ b/Tools/Tri2obj/Makefile.am @@@ -1,0 -1,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 . ++# ++# Revision 1.1 1998/01/15 02:45:25 curt ++# Initial revision. ++# ++ diff --cc Tools/Tri2obj/tri2obj.cxx index 000000000,000000000..a3401052c new file mode 100644 --- /dev/null +++ b/Tools/Tri2obj/tri2obj.cxx @@@ -1,0 -1,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 ++#include // for atoi() ++#include ++#include // for stat() ++#include // for stat() ++ ++#include "tri2obj.hxx" ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++ ++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 --cc Tools/Tri2obj/tri2obj.hxx index 000000000,000000000..c7a54d159 new file mode 100644 --- /dev/null +++ b/Tools/Tri2obj/tri2obj.hxx @@@ -1,0 -1,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 ++#include ++ ++ ++#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 --cc Tools/Triangle/A.poly index 000000000,000000000..166a71773 new file mode 100644 --- /dev/null +++ b/Tools/Triangle/A.poly @@@ -1,0 -1,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 --cc Tools/Triangle/Makefile.am index 000000000,000000000..caa86591b new file mode 100644 --- /dev/null +++ b/Tools/Triangle/Makefile.am @@@ -1,0 -1,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 diff --cc Tools/Triangle/README index 000000000,000000000..571d5689f new file mode 100644 --- /dev/null +++ b/Tools/Triangle/README @@@ -1,0 -1,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 --cc Tools/Triangle/showme.c index 000000000,000000000..722cba8ac new file mode 100644 --- /dev/null +++ b/Tools/Triangle/showme.c @@@ -1,0 -1,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 ++#include ++#include ++#include ++#include ++ ++/* 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> <# of attributes>\n"); ++ printf( ++" <# of boundary markers (0 or 1)>\n" ++); ++ printf( ++" Remaining lines: [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> <# of attributes>\n"); ++ printf( ++" <# of boundary markers (0 or 1)>\n" ++); ++ printf( ++" Following lines: [attributes] [boundary marker]\n"); ++ printf(" One line: <# of segments> <# of boundary markers (0 or 1)>\n"); ++ printf( ++" Following lines: [boundary marker]\n"); ++ printf(" One line: <# of holes>\n"); ++ printf(" Following lines: \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> <# of attributes>\n"); ++ printf( ++" Remaining lines: ... [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: [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(" -1 \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: \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: \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 --cc Tools/Triangle/triangle.c index 000000000,000000000..9af47a493 new file mode 100644 --- /dev/null +++ b/Tools/Triangle/triangle.c @@@ -1,0 -1,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 ++#include ++#include ++#ifndef NO_TIMER ++#include ++#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> <# of attributes>\n"); ++ printf( ++" <# of boundary markers (0 or 1)>\n" ++); ++ printf( ++" Remaining lines: [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> <# of attributes>\n"); ++ printf( ++" Remaining lines: ... [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> <# of attributes>\n"); ++ printf( ++" <# of boundary markers (0 or 1)>\n" ++); ++ printf( ++" Following lines: [attributes] [boundary marker]\n"); ++ printf(" One line: <# of segments> <# of boundary markers (0 or 1)>\n"); ++ printf( ++" Following lines: [boundary marker]\n"); ++ printf(" One line: <# of holes>\n"); ++ printf(" Following lines: \n"); ++ printf( ++" Optional line: <# of regional attributes and/or area constraints>\n"); ++ printf( ++" Optional following lines: \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: \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: [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(" -1 \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: \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 --cc Tools/Triangle/triangle.doc index 000000000,000000000..28b633978 new file mode 100644 --- /dev/null +++ b/Tools/Triangle/triangle.doc @@@ -1,0 -1,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> <# of attributes> ++ <# of boundary markers (0 or 1)> ++ Remaining lines: [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> <# of attributes> ++ Remaining lines: ... [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> <# of attributes> ++ <# of boundary markers (0 or 1)> ++ Following lines: [attributes] [boundary marker] ++ One line: <# of segments> <# of boundary markers (0 or 1)> ++ Following lines: [boundary marker] ++ One line: <# of holes> ++ Following lines: ++ Optional line: <# of regional attributes and/or area constraints> ++ Optional following lines: ++ ++ 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: ++ ++ 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: [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: ++ ++ -1 ++ ++ 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: ++ ++ 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 --cc Tools/Triangle/triangle.h index 000000000,000000000..b9be696c3 new file mode 100644 --- /dev/null +++ b/Tools/Triangle/triangle.h @@@ -1,0 -1,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 --cc Tools/Triangle/tricall.c index 000000000,000000000..6beccdc81 new file mode 100644 --- /dev/null +++ b/Tools/Triangle/tricall.c @@@ -1,0 -1,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 ++#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 --cc Tools/Triangulate/Makefile.am index 000000000,000000000..34c320f91 new file mode 100644 --- /dev/null +++ b/Tools/Triangulate/Makefile.am @@@ -1,0 -1,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 --cc Tools/Triangulate/triangle.cxx index 000000000,000000000..38b8bf1ac new file mode 100644 --- /dev/null +++ b/Tools/Triangulate/triangle.cxx @@@ -1,0 -1,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 --cc Tools/Triangulate/triangle.hxx index 000000000,000000000..676f5056c new file mode 100644 --- /dev/null +++ b/Tools/Triangulate/triangle.hxx @@@ -1,0 -1,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 ++#include ++#include ++#include ++ ++#define REAL double ++extern "C" { ++#include ++} ++ ++#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 --cc Tools/Triangulate/trieles.cxx index 000000000,000000000..406a19a91 new file mode 100644 --- /dev/null +++ b/Tools/Triangulate/trieles.cxx @@@ -1,0 -1,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 --cc Tools/Triangulate/trieles.hxx index 000000000,000000000..2d22c7db5 new file mode 100644 --- /dev/null +++ b/Tools/Triangulate/trieles.hxx @@@ -1,0 -1,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 ++ ++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 --cc Tools/Triangulate/trinodes.cxx index 000000000,000000000..50e7df7ee new file mode 100644 --- /dev/null +++ b/Tools/Triangulate/trinodes.cxx @@@ -1,0 -1,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 --cc Tools/Triangulate/trinodes.hxx index 000000000,000000000..f7c5fdd55 new file mode 100644 --- /dev/null +++ b/Tools/Triangulate/trinodes.hxx @@@ -1,0 -1,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 ++ ++#include
++ ++ ++#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 --cc Tools/Triangulate/tripoly.cxx index 000000000,000000000..d01bb8b40 new file mode 100644 --- /dev/null +++ b/Tools/Triangulate/tripoly.cxx @@@ -1,0 -1,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 ++ ++#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 --cc Tools/Triangulate/tripoly.hxx index 000000000,000000000..0e156dbae new file mode 100644 --- /dev/null +++ b/Tools/Triangulate/tripoly.hxx @@@ -1,0 -1,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 ++ ++#include
++ ++#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 --cc Tools/Triangulate/trisegs.cxx index 000000000,000000000..d05c6ccf1 new file mode 100644 --- /dev/null +++ b/Tools/Triangulate/trisegs.cxx @@@ -1,0 -1,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 ++ ++#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 --cc Tools/Triangulate/trisegs.hxx index 000000000,000000000..2766bcf15 new file mode 100644 --- /dev/null +++ b/Tools/Triangulate/trisegs.hxx @@@ -1,0 -1,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 ++ ++#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 --cc Tools/Utils/Makefile.am index 000000000,000000000..5c3f005b0 new file mode 100644 --- /dev/null +++ b/Tools/Utils/Makefile.am @@@ -1,0 -1,0 +1,2 @@@ ++SUBDIRS = \ ++ Makedir