commit b20be51d653cb0cc9cad14d869dab9d2f0976683 Author: curt Date: Wed Feb 9 19:51:45 2000 +0000 Initial revision diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..f68d0346 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_DIST = README README.howto scenery_version.hxx + +SUBDIRS = \ + Lib \ + Prep \ + Construct \ + Utils diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/README b/README new file mode 100644 index 00000000..253e47e0 --- /dev/null +++ b/README @@ -0,0 +1,75 @@ +FG Scenery Tools README +======================= + +Contained here-in are the FG scenery creation tools. These can be +used to convert 3 arcsec ASCII format DEM files and 30 arcsec binary +format DEM files into Flight Gear scenery. + +Eventually these tools will expand to support insertion of airports, +roads, rivers, lakes, etc. + + +Building the Tools +================== + +These tools are compiled and tested under Linux. I'm all for +portability, but I just haven't been as motivated to port these tools, +since scenery creation is less of a general need ... especially at +this stage. However, if anyone wants to work on porting to other +platforms, I will be happy to incorporate patches. + +The process for building these tools is very similar to building the +main FG source code. + +1. Set the FG_ROOT, FG_ROOT_SRC, and FG_ROOT_LIB environment + variables. + +2. Run ``make depend'' + +3. Run ``make clean'' + +4. Run ``make'' + + +3 Arcsec ASCII DEM files +======================== + +Data files for the USA are available in this format from: + + http://edcwww.cr.usgs.gov/doc/edchome/ndcdb/ndcdb.html + +To generate FG scenery from one of these dem files, run: + + ./process-dem.pl 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. \ No newline at end of file diff --git a/README.howto b/README.howto new file mode 100644 index 00000000..ca6a05ca --- /dev/null +++ b/README.howto @@ -0,0 +1,122 @@ +Original version by Alexei Novikov Sep. 3, 1999 +Updates by Curtis Olson + + +Overview +======== +The scenery creation process has 3 basic steps. 1. Fetch the raw +data. 2. Preprocess this data into an intermediate form. 3. +Assemble the intermediate data into the final scenery tiles. + +There are a couple basic types of data used to create scenery. 1. DEM +data which is typically a set of elevation points on a regular grid. +2. Polygonal data such as landmass outlines, lakes, urban areas, +glaciers, etc. 3. Other more specialized data such as airport +runways and taxiways, lighthouse locations, etc. + + +Preparation and Preprocessing +============================ +1) DEM data. + +As the first step you have to download DEM for the area you need. (You +can get them from USGS site). If this is a 30 arc sec DEM (which is most +probable) you need to transfer this file into ASCII format using +raw2ascii + +Usage: ./raw2ascii + +Here output dir is quite arbitrary as we will remove all the files from +it later. +Then you have to chop files that we got into the tiles used for the +scenery generation. You should use demchop for this. As we already have +a number of files I'm using the following Perl script + +#-------------------------------------------------------------------------- +#!/usr/bin/perl +my @files=glob("./*.dem"); +my $size=@files; +for (my $i=0;$i<$size;$i++) { + system("demchop $files[$i] /home/anovikov/w020n90/Work/DEM-30"); +} +#-------------------------------------------------------------------------- + +Here you can change /home/anovikov/w020n90 to whatever you like but keep +in mind that we will use that directory for our scenery generation. + +The 3 arcsec DEMs (which are available for the USA) should go in +.../Work/DEM-3/ + +That's all with the DEM files. If yu want to have a blibloagraphy on +them see f.e. http://www.geo.ed.ac.uk/geoinfo/dem.send + +2) Airport Data + +Next thing is to generate airports data. You need to have default.apt +from the fgfs-base package and genapts compiled. Usage is genapts +/path/to/appt_file /work/dir (in our example +genapts .../RawData/AirNa/default.apt.gz .../Work/Airports/ + +3) Landmass Data + +You can just download it from Curtis FTP site +ftp://kenai.me.umn.edu/pub/fgfs/RawData/Global-Landmass/ +or generate it by yourself. If you download data from Curtis site then +you should extract it in the .../Work/Global-Landmass + +If you are generating it by yourself you have to ask Curtis how he is +doing that. + +4) Landuse Data. (Curtis, you asked me for this). + +If you have a CD-ROM with DCW then you are lucky, if not you need to go +to ESRI WWW site (http://www.esri.com/data/online/index.html), "Select +by Geographic Area" - "Esri Thematic Data" (Go!) then +Select a Theme - Natural Landscape Zones, Select a Region - Continent +you need and download data. Please read the license agreement first. + +Then comes the tricky part. (You will need to modify the code) +I included one file you have to change as an attachment . You have to +put it in /FlightGear/Tools/Lib/Polygon/ rebuild the libPolygon and +then change the shape-decode. Go to /FlightGear/Tools/Prep/ShapeFile/ +open main.cxx and either comment out line # 357 or add || area == +DefaultArea at line #279. +Then you have to rebuild the shape-decode. Now we are ready to generate +the landuse data. +./shapedecode /name/of_the_file_from_esri /work_dir/work.states + +(in my case it is ./shapedecode /users/anovikov/fgfs_data/aalandsc +/home/anovikov/w020n90/work.states). + +5) Hydro and Urban data. + +Origianlly I was using the same ESRI web server but this is kind of very +tricky part that I will not tell you. (I was writing about the way to do +it in the mailing list) + +I think that one can get data from Coastline Extractor using "Rivers +from WDBII" but I'm not 100% sure. + +If you have acess to DCW then you just run shapedecode on the +corresponding files puting hydro data in the directory work.hydro and +urban data in the directory work.urban. + +As Curtis changed the directory structure in the last CVS versions you +have to look in Construct/Main/main.cxx first and modify the directory +names accordingly. (FE 30 arc-sec files should be in work_base/DEM-30/ + +Now we finished with data preparation and we can start generating +scenery but first we need to change the resolution of the data from the +default values ( if we are working with 30 arc sec DEMs). You have to +find line +double error = 200.0 in Construct/Main/main.cxx and change initial error +to smth like 10. Then you need to rebuild everything in that directory. + +I generally hate using fgfs-launch-clients and fgfs-launch-server so I'm +doing everyting (almost) by hand. So I start from Construct/Parallel +launch fgfs-tools-server on the computer with data on local disk and +then slogin to other computers ( with data mounted over nfs) and run +fgfs-tools-client in rude mode. After a while scenery is ready. + + That's all folks, + Alexei. diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..77d6f4ca --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.0.0 diff --git a/VERSION.in b/VERSION.in new file mode 100644 index 00000000..d78bda93 --- /dev/null +++ b/VERSION.in @@ -0,0 +1 @@ +@VERSION@ diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 00000000..4e22afc2 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,324 @@ +/* acconfig.h + This file is in the public domain. + + Descriptive text for the C preprocessor macros that + the distributed Autoconf macros can define. + No software package will use all of them; autoheader copies the ones + your configure.in uses into your configuration header file templates. + + The entries are in sort -df order: alphabetical, case insensitive, + ignoring punctuation (such as underscores). Although this order + can split up related entries, it makes it easier to check whether + a given entry is in the file. + + Leave the following blank line there!! Autoheader needs it. */ + + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +#undef _ALL_SOURCE +#endif + +/* Define if using alloca.c. */ +#undef C_ALLOCA + +/* Define if type char is unsigned and you are not using gcc. */ +#ifndef __CHAR_UNSIGNED__ +#undef __CHAR_UNSIGNED__ +#endif + +/* Define if the closedir function returns void instead of int. */ +#undef CLOSEDIR_VOID + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. + This function is required for alloca.c support on those systems. */ +#undef CRAY_STACKSEG_END + +/* Define for DGUX with . */ +#undef DGUX + +/* Define if you have . */ +#undef DIRENT + +/* Define to enable audio support */ +#undef ENABLE_AUDIO_SUPPORT + +/* Define to enable GLUT joystick support (limited to 3 axes) */ +#undef ENABLE_GLUT_JOYSTICK + +/* Define to enable plib joystick support (recommended) */ +#undef ENABLE_PLIB_JOYSTICK + +/* Define to eliminate all trace of debugging messages such as for a + release build */ +#undef FG_NDEBUG + +/* Define to include Oliver's networking support */ +#undef FG_NETWORK_OLK + +/* Define to avoid Christian's new weather code */ +#undef FG_OLD_WEATHER + +/* Define if we are building FGFS (should always be defined) */ +#undef FGFS + +/* Define to enable 3dfx/glide render in a window hack under unix. + This probably won't work under windows. */ +#undef XMESA +#undef FX + +/* Define to the type of elements in the array set by `getgroups'. + Usually this is either `int' or `gid_t'. */ +#undef GETGROUPS_T + +/* Define if the `getloadavg' function needs to be run setuid or setgid. */ +#undef GETLOADAVG_PRIVILEGED + +/* Define if the `getpgrp' function takes no argument. */ +#undef GETPGRP_VOID + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define if you have alloca, as a function or macro. */ +#undef HAVE_ALLOCA + +/* Define if you have and it should be used (not on Ultrix). */ +#undef HAVE_ALLOCA_H + +/* Define if you external variables daylight. */ +#undef HAVE_DAYLIGHT + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if your system has a working fnmatch function. */ +#undef HAVE_FNMATCH + +/* Define if your system has its own `getloadavg' function. */ +#undef HAVE_GETLOADAVG + +/* Define if you have getrusage() */ +#undef HAVE_GETRUSAGE + +/* Define if you have the getmntent function. */ +#undef HAVE_GETMNTENT + +/* Define if you have the gpc library and headers installed. */ +#undef HAVE_GPC_H + +/* Define if the `long double' type works. */ +#undef HAVE_LONG_DOUBLE + +/* Define if you support file names longer than 14 characters. */ +#undef HAVE_LONG_FILE_NAMES + +/* Define if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define if system calls automatically restart after interruption + by a signal. */ +#undef HAVE_RESTARTABLE_SYSCALLS + +/* Define if you have rint() which rounds to closest int but returns + result as a double data type. */ +#undef HAVE_RINT + +/* Define if your struct stat has st_blksize. */ +#undef HAVE_ST_BLKSIZE + +/* Define if your struct stat has st_blocks. */ +#undef HAVE_ST_BLOCKS + +/* Define if you have the strcoll function and it is properly defined. */ +#undef HAVE_STRCOLL + +/* Define if your struct stat has st_rdev. */ +#undef HAVE_ST_RDEV + +/* Define if you have the strftime function. */ +#undef HAVE_STRFTIME + +/* Define if you have */ +#undef HAVE_SYS_PARAM_H + +/* Define if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_STAT_H + +/* Define if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define if you have timegm() */ +#undef HAVE_TIMEGM + +/* Define if you external variables timezone. */ +#undef HAVE_TIMEZONE + +/* Define if your struct tm has tm_zone. */ +#undef HAVE_TM_ZONE + +/* Define if you don't have tm_zone but do have the external array + tzname. */ +#undef HAVE_TZNAME + +/* Define if you have . */ +#undef HAVE_UNISTD_H + +/* Define if utime(file, NULL) sets file's timestamp to the present. */ +#undef HAVE_UTIME_NULL + +/* Define if you have . */ +#undef HAVE_VFORK_H + +/* Define if you have the vprintf function. */ +#undef HAVE_VPRINTF + +/* Define if you have the wait3 system call. */ +#undef HAVE_WAIT3 + +/* Define as __inline if that's what the C compiler calls it. */ +#undef inline + +/* Define if int is 16 bits instead of 32. */ +#undef INT_16_BITS + +/* Define if long int is 64 bits. */ +#undef LONG_64_BITS + +/* Define if major, minor, and makedev are declared in . */ +#undef MAJOR_IN_MKDEV + +/* Define if major, minor, and makedev are declared in . */ +#undef MAJOR_IN_SYSMACROS + +/* Define if on MINIX. */ +#undef _MINIX + +/* Define to `int' if doesn't define. */ +#undef mode_t + +/* Define if you don't have , but have . */ +#undef NDIR + +/* Define if you have , and doesn't declare the + mem* functions. */ +#undef NEED_MEMORY_H + +/* Define if your struct nlist has an n_un member. */ +#undef NLIST_NAME_UNION + +/* Define if you have . */ +#undef NLIST_STRUCT + +/* Define if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Define to `long' if doesn't define. */ +#undef off_t + +/* Define to package name */ +#undef PACKAGE + +/* Define to `int' if doesn't define. */ +#undef pid_t + +/* Define if the system does not provide POSIX.1 features except + with this defined. */ +#undef _POSIX_1_SOURCE + +/* Define if you need to in order for stat and other things to work. */ +#undef _POSIX_SOURCE + +/* Define as the return type of signal handlers (int or void). */ +#undef RETSIGTYPE + +/* Define if the `setpgrp' function takes no argument. */ +#undef SETPGRP_VOID + +/* Define if the setvbuf function takes the buffering type as its second + argument and the buffer pointer as the third, as on System V + before release 3. */ +#undef SETVBUF_REVERSED + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at run-time. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown + */ +#undef STACK_DIRECTION + +/* Define if the `S_IS*' macros in do not work properly. */ +#undef STAT_MACROS_BROKEN + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define on System V Release 4. */ +#undef SVR4 + +/* Define if you don't have , but have . */ +#undef SYSDIR + +/* Define if you don't have , but have . */ +#undef SYSNDIR + +/* Define if `sys_siglist' is declared by . */ +#undef SYS_SIGLIST_DECLARED + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define to `int' if doesn't define. */ +#undef uid_t + +/* Define for Encore UMAX. */ +#undef UMAX + +/* Define for Encore UMAX 4.3 that has + instead of . */ +#undef UMAX4_3 + +/* Define if you do not have , index, bzero, etc.. */ +#undef USG + +/* Define to version number */ +#undef VERSION + +/* Define vfork as fork if vfork does not work. */ +#undef vfork + +/* Define if the closedir function returns void instead of int. */ +#undef VOID_CLOSEDIR + +/* Define if compiling on a Winbloze (95, NT, etc.) platform */ +#undef WIN32 + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define if the X Window System is missing or not being used. */ +#undef X_DISPLAY_MISSING + +/* Define if lex declares yytext as a char * by default, not a char[]. */ +#undef YYTEXT_POINTER + + +/* Leave that blank line there!! Autoheader needs it. + If you're adding to this file, keep in mind: + The entries are in sort -df order: alphabetical, case insensitive, + ignoring punctuation (such as underscores). */ + diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 00000000..df0bfb9e --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,149 @@ +dnl aclocal.m4 generated automatically by aclocal 1.3 + +dnl Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc. +dnl This Makefile.in is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without +dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A +dnl PARTICULAR PURPOSE. + +# Do all the work for Automake. This macro actually does too much -- +# some checks are only needed if your package does certain things. +# But this isn't really a big deal. + +# serial 1 + +dnl Usage: +dnl AM_INIT_AUTOMAKE(package,version, [no-define]) + +AC_DEFUN(AM_INIT_AUTOMAKE, +[AC_REQUIRE([AM_PROG_INSTALL]) +PACKAGE=[$1] +AC_SUBST(PACKAGE) +VERSION=[$2] +AC_SUBST(VERSION) +dnl test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi +ifelse([$3],, +AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE") +AC_DEFINE_UNQUOTED(VERSION, "$VERSION")) +AC_REQUIRE([AM_SANITY_CHECK]) +AC_REQUIRE([AC_ARG_PROGRAM]) +dnl FIXME This is truly gross. +missing_dir=`cd $ac_aux_dir && pwd` +AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir) +AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) +AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir) +AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) +AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) +AC_REQUIRE([AC_PROG_MAKE_SET])]) + + +# serial 1 + +AC_DEFUN(AM_PROG_INSTALL, +[AC_REQUIRE([AC_PROG_INSTALL]) +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' +AC_SUBST(INSTALL_SCRIPT)dnl +]) + +# +# Check to make sure that the build environment is sane. +# + +AC_DEFUN(AM_SANITY_CHECK, +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftestfile +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null` + if test "[$]*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftestfile` + fi + if test "[$]*" != "X $srcdir/configure conftestfile" \ + && test "[$]*" != "X conftestfile $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "[$]2" = conftestfile + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +rm -f conftest* +AC_MSG_RESULT(yes)]) + +dnl AM_MISSING_PROG(NAME, PROGRAM, DIRECTORY) +dnl The program must properly implement --version. +AC_DEFUN(AM_MISSING_PROG, +[AC_MSG_CHECKING(for working $2) +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if ($2 --version) < /dev/null > /dev/null 2>&1; then + $1=$2 + AC_MSG_RESULT(found) +else + $1="$3/missing $2" + AC_MSG_RESULT(missing) +fi +AC_SUBST($1)]) + +# Define a conditional. + +AC_DEFUN(AM_CONDITIONAL, +[AC_SUBST($1_TRUE) +AC_SUBST($1_FALSE) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi]) + +# Like AC_CONFIG_HEADER, but automatically create stamp file. + +AC_DEFUN(AM_CONFIG_HEADER, +[AC_PREREQ([2.12]) +AC_CONFIG_HEADER([$1]) +dnl When config.status generates a header, we must update the stamp-h file. +dnl This file resides in the same directory as the config header +dnl that is generated. We must strip everything past the first ":", +dnl and everything past the last "/". +AC_OUTPUT_COMMANDS(changequote(<<,>>)dnl +ifelse(patsubst(<<$1>>, <<[^ ]>>, <<>>), <<>>, +<>CONFIG_HEADERS" || echo timestamp > patsubst(<<$1>>, <<^\([^:]*/\)?.*>>, <<\1>>)stamp-h<<>>dnl>>, +<>; do + case " <<$>>CONFIG_HEADERS " in + *" <<$>>am_file "*<<)>> + echo timestamp > `echo <<$>>am_file | sed -e 's%:.*%%' -e 's%[^/]*$%%'`stamp-h$am_indx + ;; + esac + am_indx=`expr "<<$>>am_indx" + 1` +done<<>>dnl>>) +changequote([,]))]) + diff --git a/acsite.m4 b/acsite.m4 new file mode 100644 index 00000000..3a0e116e --- /dev/null +++ b/acsite.m4 @@ -0,0 +1,394 @@ +dnl +dnl originally from ncftp 2.3.0 +dnl added wi_EXTRA_PDIR and wi_ANSI_C +dnl $Id$ +dnl +AC_DEFUN(wi_EXTRA_IDIR, [ +incdir="$1" +if test -r $incdir ; then + case "$CPPFLAGS" in + *-I${incdir}*) + # echo " + already had $incdir" 1>&6 + ;; + *) + if test "$CPPFLAGS" = "" ; then + CPPFLAGS="-I$incdir" + else + CPPFLAGS="$CPPFLAGS -I$incdir" + fi + echo " + found $incdir" 1>&6 + ;; + esac +fi +]) +dnl +dnl +dnl +dnl +AC_DEFUN(wi_EXTRA_LDIR, [ +mylibdir="$1" +if test -r $mylibdir ; then + case "$LDFLAGS" in + *-L${mylibdir}*) + # echo " + already had $mylibdir" 1>&6 + ;; + *) + if test "$LDFLAGS" = "" ; then + LDFLAGS="-L$mylibdir" + else + LDFLAGS="$LDFLAGS -L$mylibdir" + fi + echo " + found $mylibdir" 1>&6 + ;; + esac +fi +]) +dnl +dnl __FP__ +dnl +dnl +AC_DEFUN(wi_EXTRA_PDIR, [ +progdir="$1" +if test -r $progdir ; then + case "$PATH" in + *:${progdir}*) + # echo " + already had $progdir" 1>&6 + ;; + *${progdir}:*) + # echo " + already had $progdir" 1>&6 + ;; + *) + if test "$PATH" = "" ; then + PATH="$progdir" + else + PATH="$PATH:$progdir" + fi + echo " + found $progdir" 1>&6 + ;; + esac +fi +]) +dnl +dnl +dnl If you want to also look for include and lib subdirectories in the +dnl $HOME tree, you supply "yes" as the first argument to this macro. +dnl +dnl If you want to look for subdirectories in include/lib directories, +dnl you pass the names in argument 3, otherwise pass a dash. +dnl +AC_DEFUN(wi_EXTRA_DIRS, [echo "checking for extra include and lib directories..." 1>&6 +ifelse([$1], yes, [dnl +b1=`cd .. ; pwd` +b2=`cd ../.. ; pwd` +exdirs="$HOME $j $b1 $b2 $prefix $2" +],[dnl +exdirs="$prefix $2" +]) +subexdirs="$3" +if test "$subexdirs" = "" ; then + subexdirs="-" +fi +for subexdir in $subexdirs ; do +if test "$subexdir" = "-" ; then + subexdir="" +else + subexdir="/$subexdir" +fi +for exdir in $exdirs ; do + if test "$exdir" != "/usr" || test "$subexdir" != ""; then + incdir="${exdir}/include${subexdir}" + wi_EXTRA_IDIR($incdir) + + mylibdir="${exdir}/lib${subexdir}" + wi_EXTRA_LDIR($mylibdir) + + progdir="${exdir}/bin${subexdirr}" + wi_EXTRA_PDIR($progdir) + fi +done +done +]) +dnl +dnl +dnl +AC_DEFUN(wi_HPUX_CFLAGS, +[AC_MSG_CHECKING(if HP-UX ansi C compiler flags are needed) +AC_REQUIRE([AC_PROG_CC]) +os=`uname -s | tr '[A-Z]' '[a-z]'` +ac_cv_hpux_flags=no +if test "$os" = hp-ux ; then + if test "$ac_cv_prog_gcc" = yes ; then + if test "$CFLAGS" != "" ; then + # Shouldn't be in there. + CFLAGS=`echo "$CFLAGS" | sed 's/-Aa//g'` + fi + else + # If you're not using gcc, then you better have a cc/c89 + # that is usable. If you have the barebones compiler, it + # won't work. The good compiler uses -Aa for the ANSI + # compatible stuff. + x=`echo $CFLAGS | grep 'Aa' 2>/dev/null` + if test "$x" = "" ; then + CFLAGS="$CFLAGS -Aa" + fi + ac_cv_hpux_flags=yes + fi + # Also add _HPUX_SOURCE to get the extended namespace. + x=`echo $CFLAGS | grep '_HPUX_SOURCE' 2>/dev/null` + if test "$x" = "" ; then + CFLAGS="$CFLAGS -D_HPUX_SOURCE" + fi +fi +AC_MSG_RESULT($ac_cv_hpux_flags) +]) +dnl +dnl +dnl +AC_DEFUN(wi_CFLAGS, [AC_REQUIRE([AC_PROG_CC]) +wi_HPUX_CFLAGS + if test "$CFLAGS" = "" ; then + CFLAGS="-O" + elif test "$ac_cv_prog_gcc" = "yes" ; then + case "$CFLAGS" in + *"-g -O"*) + #echo "using -g as default gcc CFLAGS" 1>&6 + CFLAGS=`echo $CFLAGS | sed 's/-g\ -O/-O/'` + ;; + *"-O -g"*) + # Leave the -g, but remove all -O options. + #echo "using -g as default gcc CFLAGS" 1>&6 + CFLAGS=`echo $CFLAGS | sed 's/-O\ -g/-O/'` + ;; + esac + fi +]) +dnl +dnl +dnl +AC_DEFUN(wi_PROTOTYPES, [ +AC_MSG_CHECKING(if the compiler supports function prototypes) +AC_TRY_COMPILE(,[extern void exit(int status);],[wi_cv_prototypes=yes +AC_DEFINE(PROTOTYPES)],wi_cv_prototypes=no) +AC_MSG_RESULT($wi_cv_prototypes) +]) +dnl +dnl +dnl +AC_DEFUN(wi_ANSI_C, [ +AC_MSG_CHECKING(ANSI-style function definitions) +AC_TRY_COMPILE(,[int blubb(int x) { return 0; }],[wi_cv_ansi_funcs=yes +AC_DEFINE(ANSI_FUNCS)],wi_cv_ansi_funcs=no) +AC_MSG_RESULT($wi_cv_ansi_funcs) +]) +dnl +dnl +dnl +AC_DEFUN(wi_HEADER_SYS_SELECT_H, [ +# See if is includable after +if test "$ac_cv_header_sys_time_h" = no ; then +AC_CHECK_HEADERS(sys/time.h sys/select.h) +else +AC_CHECK_HEADERS(sys/select.h) +fi +if test "$ac_cv_header_sys_select_h" = yes ; then + AC_MSG_CHECKING([if is compatible with ]) + selecth=yes + if test "$ac_cv_header_sys_time_h" = yes ; then + AC_TRY_COMPILE([#include + #include ],[ + fd_set a; + struct timeval tmval; + + tmval.tv_sec = 0;],selecth=yes,selecth=no) + + if test "$selecth" = yes ; then + AC_DEFINE(CAN_USE_SYS_SELECT_H) + fi + fi + AC_MSG_RESULT($selecth) +fi +]) +dnl +dnl +dnl +AC_DEFUN(wi_LIB_RESOLV, [ +# See if we could access two well-known sites without help of any special +# libraries, like resolv. + +AC_TRY_RUN([ +#include +#include +#include +#include + +main() +{ + struct hostent *hp1, *hp2; + int result; + + hp1 = gethostbyname("gatekeeper.dec.com"); + hp2 = gethostbyname("ftp.ncsa.uiuc.edu"); + result = ((hp1 != (struct hostent *) 0) && (hp2 != (struct hostent *) 0)); + exit(! result); +}],look_for_resolv=no,look_for_resolv=yes,look_for_resolv=yes) + +AC_MSG_CHECKING([if we need to look for -lresolv]) +AC_MSG_RESULT($look_for_resolv) +if test "$look_for_resolv" = yes ; then +AC_CHECK_LIB(resolv,main) +else + ac_cv_lib_resolv=no +fi +]) +dnl +dnl +dnl + +AC_DEFUN(wi_LIB_NSL, [ +AC_MSG_CHECKING(if we can use -lnsl) +ac_save_LIBS="$LIBS"; +LIBS="$LIBS -lnsl"; +AC_CACHE_VAL(r_cv_use_libnsl, [ +AC_TRY_RUN( +main() { if (getpwuid(getuid())) exit(0); exit(-1); }, +nc_cv_use_libnsl=yes, nc_cv_use_libnsl=no, nc_cv_use_libnsl=no) +]) +if test "$nc_cv_use_libnsl" = "no"; then LIBS="$ac_save_LIBS"; fi +AC_MSG_RESULT($nc_cv_use_libnsl) +])dnl + +dnl +dnl +dnl + +AC_DEFUN(nc_PATH_PROG_ZCAT, [ +AC_PATH_PROG(GZCAT,gzcat) +AC_PATH_PROG(ZCAT,zcat) +if test "x$GZCAT" = x ; then + if test "x$ZCAT" != x ; then + # See if zcat is really gzcat. gzcat has a --version option, regular + # zcat does not. + AC_MSG_CHECKING(if zcat is really gzcat in disguise) + if $ZCAT --version 2> /dev/null ; then + AC_DEFINE_UNQUOTED(GZCAT, "$ZCAT") + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi + fi +else + AC_DEFINE_UNQUOTED(GZCAT, "$GZCAT") +fi + +if test "x$ZCAT" != x ; then + AC_DEFINE_UNQUOTED(ZCAT, "$ZCAT") +fi +]) +dnl +dnl +dnl +AC_DEFUN(wi_SYSV_EXTRA_DIRS, [ +# Use System V because their curses extensions are required. This must +# be done early so we use the -I and -L in the library checks also. +# This is mostly a Solaris/SunOS hack. Note that doing this will also +# use all of the other System V libraries and headers. + +AC_MSG_CHECKING(for alternative System V libraries) +if test -f /usr/5include/curses.h ; then + CPPFLAGS="$CPPFLAGS -I/usr/5include" + LDFLAGS="$LDFLAGS -L/usr/5lib" + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +]) +dnl +dnl +dnl +AC_DEFUN(wi_DEFINE_UNAME, [ +# Get first 127 chars of all uname information. Some folks have +# way too much stuff there, so grab only the first 127. +unam=`uname -a 2>/dev/null | cut -c1-127` +if test "$unam" != "" ; then + AC_DEFINE_UNQUOTED(UNAME, "$unam") +fi +]) +dnl +dnl +dnl +AC_DEFUN(wi_READLINE_WITH_NCURSES, [ +# Readline and Ncurses could both define "backspace". +# Warn about this if we have both things in our definitions list. + +if test "$ac_cv_lib_readline" = yes && test "$ac_cv_lib_ncurses" = yes ; then + +AC_MSG_CHECKING(if readline and ncurses will link together) +j="$LIBS" +LIBS="-lreadline -lncurses" +AC_TRY_LINK(,[ +readline("prompt"); +endwin(); +],k=yes,k=no) +if test "$k" = no ; then + AC_MSG_RESULT(no) + # Remove '-lreadline' from LIBS. + LIBS=`echo $j | sed s/-lreadline//g` + ac_cv_lib_readline=no + AC_WARN([The versions of GNU readline and ncurses you have installed on this system +can't be used together, because they use the same symbol, backspace. If +possible, recompile one of the libraries with -Dbackspace=back_space, then +re-run configure.]) + +else + AC_MSG_RESULT(yes) + LIBS="$j" +fi + +fi +]) +dnl +dnl +dnl + +dnl AC_EXT_DAYLIGHT +dnl Check for an external variable daylight. Stolen from w3c-libwww. +AC_DEFUN(AC_EXT_DAYLIGHT, +[ AC_MSG_CHECKING(int daylight variable) +AC_TRY_COMPILE([#include ], [return daylight;], + have_daylight=yes, + have_daylight=no) +AC_MSG_RESULT($have_daylight) +])dnl + +dnl AC_EXT_TIMEZONE +dnl Check for an external variable timezone. Stolen from tcl-8.0. +AC_DEFUN(AC_EXT_TIMEZONE, +[ +# +# Its important to include time.h in this check, as some systems (like convex) +# have timezone functions, etc. +# +have_timezone=no +AC_MSG_CHECKING([long timezone variable]) +AC_TRY_COMPILE([#include ], + [extern long timezone; + timezone += 1; + exit (0);], + [have_timezone=yes + AC_MSG_RESULT(yes)], + AC_MSG_RESULT(no)) + +# +# On some systems (eg IRIX 6.2), timezone is a time_t and not a long. +# +if test "$have_timezone" = no; then + AC_MSG_CHECKING([time_t timezone variable]) + AC_TRY_COMPILE([#include ], + [extern time_t timezone; + timezone += 1; + exit (0);], + [have_timezone=yes + AC_MSG_RESULT(yes)], + AC_MSG_RESULT(no)) +fi +])dnl diff --git a/config.cache b/config.cache new file mode 100644 index 00000000..cc80f8ba --- /dev/null +++ b/config.cache @@ -0,0 +1,89 @@ +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +ac_cv_c_const=${ac_cv_c_const=yes} +ac_cv_func_GetLocalTime=${ac_cv_func_GetLocalTime=no} +ac_cv_func_bcopy=${ac_cv_func_bcopy=yes} +ac_cv_func_connect=${ac_cv_func_connect=yes} +ac_cv_func_ftime=${ac_cv_func_ftime=yes} +ac_cv_func_gethostbyname=${ac_cv_func_gethostbyname=yes} +ac_cv_func_getitimer=${ac_cv_func_getitimer=yes} +ac_cv_func_getrusage=${ac_cv_func_getrusage=yes} +ac_cv_func_gettimeofday=${ac_cv_func_gettimeofday=yes} +ac_cv_func_memcpy=${ac_cv_func_memcpy=yes} +ac_cv_func_mktime=${ac_cv_func_mktime=yes} +ac_cv_func_rand=${ac_cv_func_rand=yes} +ac_cv_func_random=${ac_cv_func_random=yes} +ac_cv_func_remove=${ac_cv_func_remove=yes} +ac_cv_func_rint=${ac_cv_func_rint=yes} +ac_cv_func_setitimer=${ac_cv_func_setitimer=yes} +ac_cv_func_shmat=${ac_cv_func_shmat=yes} +ac_cv_func_signal=${ac_cv_func_signal=yes} +ac_cv_func_strstr=${ac_cv_func_strstr=yes} +ac_cv_func_timegm=${ac_cv_func_timegm=yes} +ac_cv_func_vprintf=${ac_cv_func_vprintf=yes} +ac_cv_have_x=${ac_cv_have_x='have_x=yes ac_x_includes=/usr/X11R6/include ac_x_libraries=/usr/X11R6/lib'} +ac_cv_header_fcntl_h=${ac_cv_header_fcntl_h=yes} +ac_cv_header_getopt_h=${ac_cv_header_getopt_h=yes} +ac_cv_header_gpc_h=${ac_cv_header_gpc_h=yes} +ac_cv_header_malloc_h=${ac_cv_header_malloc_h=yes} +ac_cv_header_memory_h=${ac_cv_header_memory_h=yes} +ac_cv_header_plib_pu_h=${ac_cv_header_plib_pu_h=yes} +ac_cv_header_stdc=${ac_cv_header_stdc=yes} +ac_cv_header_stdlib_h=${ac_cv_header_stdlib_h=yes} +ac_cv_header_sys_param_h=${ac_cv_header_sys_param_h=yes} +ac_cv_header_sys_stat_h=${ac_cv_header_sys_stat_h=yes} +ac_cv_header_sys_time_h=${ac_cv_header_sys_time_h=yes} +ac_cv_header_sys_timeb_h=${ac_cv_header_sys_timeb_h=yes} +ac_cv_header_time=${ac_cv_header_time=yes} +ac_cv_header_unistd_h=${ac_cv_header_unistd_h=yes} +ac_cv_header_values_h=${ac_cv_header_values_h=yes} +ac_cv_header_winbase_h=${ac_cv_header_winbase_h=no} +ac_cv_header_windows_h=${ac_cv_header_windows_h=no} +ac_cv_lib_GLU_gluLookAt=${ac_cv_lib_GLU_gluLookAt=no} +ac_cv_lib_GL_glNewList=${ac_cv_lib_GL_glNewList=yes} +ac_cv_lib_GLcore_glNewList=${ac_cv_lib_GLcore_glNewList=no} +ac_cv_lib_ICE_IceConnectionNumber=${ac_cv_lib_ICE_IceConnectionNumber=yes} +ac_cv_lib_ICE_IceOpenConnection=${ac_cv_lib_ICE_IceOpenConnection=yes} +ac_cv_lib_MesaGLU_gluLookAt=${ac_cv_lib_MesaGLU_gluLookAt=yes} +ac_cv_lib_SM_SmcOpenConnection=${ac_cv_lib_SM_SmcOpenConnection=yes} +ac_cv_lib_X11_XCreateWindow=${ac_cv_lib_X11_XCreateWindow=yes} +ac_cv_lib_Xext_XShmCreateImage=${ac_cv_lib_Xext_XShmCreateImage=yes} +ac_cv_lib_Xi_XGetExtensionVersion=${ac_cv_lib_Xi_XGetExtensionVersion=yes} +ac_cv_lib_Xmu_XmuLookupStandardColormap=${ac_cv_lib_Xmu_XmuLookupStandardColormap=yes} +ac_cv_lib_Xt_XtMalloc=${ac_cv_lib_Xt_XtMalloc=yes} +ac_cv_lib_dnet_dnet_ntoa=${ac_cv_lib_dnet_dnet_ntoa=no} +ac_cv_lib_dnet_stub_dnet_ntoa=${ac_cv_lib_dnet_stub_dnet_ntoa=no} +ac_cv_lib_glut_glutGameModeString=${ac_cv_lib_glut_glutGameModeString=yes} +ac_cv_lib_glut_glutGetModifiers=${ac_cv_lib_glut_glutGetModifiers=yes} +ac_cv_lib_m_cos=${ac_cv_lib_m_cos=yes} +ac_cv_lib_socket_socket=${ac_cv_lib_socket_socket=no} +ac_cv_path_install=${ac_cv_path_install='/usr/bin/install -c'} +ac_cv_prog_CC=${ac_cv_prog_CC=gcc} +ac_cv_prog_CPP=${ac_cv_prog_CPP='gcc -E'} +ac_cv_prog_CXX=${ac_cv_prog_CXX=c++} +ac_cv_prog_LN_S=${ac_cv_prog_LN_S='ln -s'} +ac_cv_prog_RANLIB=${ac_cv_prog_RANLIB=ranlib} +ac_cv_prog_cc_cross=${ac_cv_prog_cc_cross=no} +ac_cv_prog_cc_g=${ac_cv_prog_cc_g=yes} +ac_cv_prog_cc_works=${ac_cv_prog_cc_works=yes} +ac_cv_prog_cxx_cross=${ac_cv_prog_cxx_cross=no} +ac_cv_prog_cxx_g=${ac_cv_prog_cxx_g=yes} +ac_cv_prog_cxx_works=${ac_cv_prog_cxx_works=yes} +ac_cv_prog_gcc=${ac_cv_prog_gcc=yes} +ac_cv_prog_gxx=${ac_cv_prog_gxx=yes} +ac_cv_prog_make_make_set=${ac_cv_prog_make_make_set=yes} +ac_cv_struct_tm=${ac_cv_struct_tm=time.h} +ac_cv_type_signal=${ac_cv_type_signal=void} +ac_cv_type_size_t=${ac_cv_type_size_t=yes} diff --git a/config.log b/config.log new file mode 100644 index 00000000..1d546dc7 --- /dev/null +++ b/config.log @@ -0,0 +1,89 @@ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +configure:561: checking for a BSD compatible install +configure:614: checking whether build environment is sane +configure:671: checking whether make sets ${MAKE} +configure:717: checking for working aclocal +configure:730: checking for working autoconf +configure:743: checking for working automake +configure:756: checking for working autoheader +configure:769: checking for working makeinfo +configure:784: checking whether make sets ${MAKE} +configure:813: checking for gcc +configure:890: checking whether the C compiler (gcc ) works +configure:904: gcc -o conftest conftest.c 1>&5 +configure:924: checking whether the C compiler (gcc ) is a cross-compiler +configure:929: checking whether we are using GNU C +configure:953: checking whether gcc accepts -g +configure:985: checking for c++ +configure:1016: checking whether the C++ compiler (c++ ) works +configure:1030: c++ -o conftest conftest.C 1>&5 +configure:1056: checking whether the C++ compiler (c++ ) is a cross-compiler +configure:1061: checking whether we are using GNU C++ +configure:1085: checking whether c++ accepts -g +configure:1115: checking for ranlib +configure:1152: checking for a BSD compatible install +configure:1202: checking whether ln -s works +configure:1261: checking how to run the C preprocessor +configure:1323: checking for windows.h +configure:1452: checking for X +configure:1766: checking for dnet_ntoa in -ldnet +configure:1807: checking for dnet_ntoa in -ldnet_stub +configure:1855: checking for gethostbyname +configure:1953: checking for connect +configure:2045: checking for remove +configure:2137: checking for shmat +configure:2238: checking for IceConnectionNumber in -lICE +configure:2286: checking for cos in -lm +configure:2336: checking for socket in -lsocket +configure:2383: checking for XCreateWindow in -lX11 +configure:2430: checking for XShmCreateImage in -lXext +configure:2477: checking for XGetExtensionVersion in -lXi +configure:2524: checking for IceOpenConnection in -lICE +configure:2571: checking for SmcOpenConnection in -lSM +configure:2618: checking for XtMalloc in -lXt +configure:2665: checking for XmuLookupStandardColormap in -lXmu +configure:2716: checking for glNewList in -lGLcore +configure:2764: checking for glNewList in -lGL +configure:2956: checking for gluLookAt in -lGLU +configure:3004: checking for gluLookAt in -lMesaGLU +configure:3053: checking for glutGetModifiers in -lglut +configure:3102: checking for glutGameModeString in -lglut +configure:3221: checking for plib/pu.h +configure:3267: checking for gpc.h +configure:3317: checking for ANSI C header files +configure:3426: checking for fcntl.h +configure:3426: checking for getopt.h +configure:3426: checking for malloc.h +configure:3426: checking for memory.h +configure:3426: checking for stdlib.h +configure:3426: checking for sys/param.h +configure:3426: checking for sys/stat.h +configure:3426: checking for sys/time.h +configure:3426: checking for sys/timeb.h +configure:3426: checking for unistd.h +configure:3426: checking for windows.h +configure:3426: checking for winbase.h +configure:3426: checking for values.h +configure:3464: checking for working const +configure:3539: checking for size_t +configure:3572: checking whether time.h and sys/time.h may both be included +configure:3607: checking whether struct tm is in sys/time.h or time.h +configure:3642: checking return type of signal handlers +configure:3683: checking for vprintf +configure:3791: checking for ftime +configure:3791: checking for gettimeofday +configure:3791: checking for timegm +configure:3791: checking for memcpy +configure:3791: checking for bcopy +configure:3791: checking for mktime +configure:3791: checking for strstr +configure:3791: checking for rand +configure:3791: checking for random +configure:3791: checking for setitimer +configure:3791: checking for getitimer +configure:3791: checking for signal +configure:3791: checking for GetLocalTime +configure:3791: checking for rint +configure:3791: checking for getrusage diff --git a/config.status b/config.status new file mode 100755 index 00000000..e3a1cd9c --- /dev/null +++ b/config.status @@ -0,0 +1,441 @@ +#! /bin/sh +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# This directory was configured as follows, +# on host kenai: +# +# ./configure --prefix=/h/curt --bindir=/h/curt/bin/Linux +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: ./config.status [--recheck] [--version] [--help]" +for ac_option +do + case "$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running ${CONFIG_SHELL-/bin/sh} ./configure --prefix=/h/curt --bindir=/h/curt/bin/Linux --no-create --no-recursion" + exec ${CONFIG_SHELL-/bin/sh} ./configure --prefix=/h/curt --bindir=/h/curt/bin/Linux --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "./config.status generated by autoconf version 2.12" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "$ac_cs_usage"; exit 0 ;; + *) echo "$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=. +ac_given_INSTALL="/usr/bin/install -c" + +trap 'rm -fr VERSION Makefile Construct/Makefile Construct/Clipper/Makefile Construct/Combine/Makefile Construct/GenOutput/Makefile Construct/Match/Makefile Construct/Triangulate/Makefile Construct/Main/Makefile Construct/Parallel/Makefile Lib/Makefile Lib/Array/Makefile Lib/Build/Makefile Lib/DEM/Makefile Lib/Polygon/Makefile Lib/poly2tri/Makefile Lib/shapelib/Makefile Lib/Triangle/Makefile Prep/Makefile Prep/DemChop/Makefile Prep/DemInfo/Makefile Prep/DemRaw2ascii/Makefile Prep/GenAirports/Makefile Prep/GSHHS/Makefile Prep/MergerClipper/Makefile Prep/ShapeFile/Makefile Utils/Makefile Include/config.h conftest*; exit 1' 1 2 15 + +# Protect against being on the right side of a sed subst in config.status. +sed 's/%@/@@/; s/@%/@@/; s/%g$/@g/; /@g$/s/[\\&%]/\\&/g; + s/@@/%@/; s/@@/@%/; s/@g$/%g/' > conftest.subs <<\CEOF +/^[ ]*VPATH[ ]*=[^:]*$/d + +s%@CFLAGS@%-g -O2%g +s%@CPPFLAGS@%-I/usr/local/include -I/usr/X11R6/include%g +s%@CXXFLAGS@%-g -O2%g +s%@DEFS@%-DHAVE_CONFIG_H%g +s%@LDFLAGS@%-L/h/curt/lib -L/usr/local/lib -L/usr/X11R6/lib%g +s%@LIBS@%-lm %g +s%@exec_prefix@%${prefix}%g +s%@prefix@%/h/curt%g +s%@program_transform_name@%s,x,x,%g +s%@bindir@%/h/curt/bin/Linux%g +s%@sbindir@%${exec_prefix}/sbin%g +s%@libexecdir@%${exec_prefix}/libexec%g +s%@datadir@%${prefix}/share%g +s%@sysconfdir@%${prefix}/etc%g +s%@sharedstatedir@%${prefix}/com%g +s%@localstatedir@%${prefix}/var%g +s%@libdir@%${exec_prefix}/lib%g +s%@includedir@%${prefix}/include%g +s%@oldincludedir@%/usr/include%g +s%@infodir@%${prefix}/info%g +s%@mandir@%${prefix}/man%g +s%@INSTALL_PROGRAM@%${INSTALL}%g +s%@INSTALL_DATA@%${INSTALL} -m 644%g +s%@INSTALL_SCRIPT@%${INSTALL_PROGRAM}%g +s%@PACKAGE@%TerraGear%g +s%@VERSION@%0.0.0%g +s%@ACLOCAL@%aclocal%g +s%@AUTOCONF@%autoconf%g +s%@AUTOMAKE@%automake%g +s%@AUTOHEADER@%autoheader%g +s%@MAKEINFO@%/h/curt/projects/TerraGear-0.0/src/missing makeinfo%g +s%@SET_MAKE@%%g +s%@CC@%gcc%g +s%@CXX@%c++%g +s%@RANLIB@%ranlib%g +s%@LN_S@%ln -s%g +s%@CPP@%gcc -E%g +s%@X_CFLAGS@% -I/usr/X11R6/include%g +s%@X_PRE_LIBS@% -lSM -lICE%g +s%@X_LIBS@% -L/usr/X11R6/lib%g +s%@X_EXTRA_LIBS@%%g +s%@ENABLE_XMESA_FX_TRUE@%%g +s%@ENABLE_XMESA_FX_FALSE@%%g +s%@base_LIBS@%-lm %g +s%@opengl_LIBS@%-lglut -lMesaGLU -lGL -lXmu -lXt -lSM -lICE -lXi -lXext -lX11 -lm %g +s%@HAVE_XWINDOWS_TRUE@%%g +s%@HAVE_XWINDOWS_FALSE@%#%g + +CEOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi + +CONFIG_FILES=${CONFIG_FILES-" VERSION Makefile Construct/Makefile Construct/Clipper/Makefile Construct/Combine/Makefile Construct/GenOutput/Makefile Construct/Match/Makefile Construct/Triangulate/Makefile Construct/Main/Makefile Construct/Parallel/Makefile Lib/Makefile Lib/Array/Makefile Lib/Build/Makefile Lib/DEM/Makefile Lib/Polygon/Makefile Lib/poly2tri/Makefile Lib/shapelib/Makefile Lib/Triangle/Makefile Prep/Makefile Prep/DemChop/Makefile Prep/DemInfo/Makefile Prep/DemRaw2ascii/Makefile Prep/GenAirports/Makefile Prep/GSHHS/Makefile Prep/MergerClipper/Makefile Prep/ShapeFile/Makefile Utils/Makefile "} +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then + CONFIG_HEADERS="Include/config.h" +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + + + +test -z "$CONFIG_HEADERS" || echo timestamp > Include/stamp-h + +exit 0 diff --git a/configure b/configure new file mode 100755 index 00000000..68e8f7f6 --- /dev/null +++ b/configure @@ -0,0 +1,4298 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.12 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --with-logging Include logging output (default)" +ac_help="$ac_help + --with-efence Specify if we are building with "electric-fence"" +ac_help="$ac_help + --with-sgi-opengl Build against SGI's opengl.dll glu.dll and glut.dll" +ac_help="$ac_help + --with-x use the X Window System" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.12" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=Lib/DEM/dem.cxx + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:561: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' + +echo $ac_n "checking whether build environment is sane""... $ac_c" 1>&6 +echo "configure:614: checking whether build environment is sane" >&5 +# Just in case +sleep 1 +echo timestamp > conftestfile +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftestfile` + fi + if test "$*" != "X $srcdir/configure conftestfile" \ + && test "$*" != "X conftestfile $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { echo "configure: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" 1>&2; exit 1; } + fi + + test "$2" = conftestfile + ) +then + # Ok. + : +else + { echo "configure: error: newly created file is older than distributed files! +Check your system clock" 1>&2; exit 1; } +fi +rm -f conftest* +echo "$ac_t""yes" 1>&6 +if test "$program_transform_name" = s,x,x,; then + program_transform_name= +else + # Double any \ or $. echo might interpret backslashes. + cat <<\EOF_SED > conftestsed +s,\\,\\\\,g; s,\$,$$,g +EOF_SED + program_transform_name="`echo $program_transform_name|sed -f conftestsed`" + rm -f conftestsed +fi +test "$program_prefix" != NONE && + program_transform_name="s,^,${program_prefix},; $program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$\$,${program_suffix},; $program_transform_name" + +# sed with no file args requires a program. +test "$program_transform_name" = "" && program_transform_name="s,x,x," + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:671: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + +PACKAGE=TerraGear + +VERSION=0.0.0 + +if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then + { echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; } +fi +cat >> confdefs.h <> confdefs.h <&6 +echo "configure:717: checking for working aclocal" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (aclocal --version) < /dev/null > /dev/null 2>&1; then + ACLOCAL=aclocal + echo "$ac_t""found" 1>&6 +else + ACLOCAL="$missing_dir/missing aclocal" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working autoconf""... $ac_c" 1>&6 +echo "configure:730: checking for working autoconf" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (autoconf --version) < /dev/null > /dev/null 2>&1; then + AUTOCONF=autoconf + echo "$ac_t""found" 1>&6 +else + AUTOCONF="$missing_dir/missing autoconf" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working automake""... $ac_c" 1>&6 +echo "configure:743: checking for working automake" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (automake --version) < /dev/null > /dev/null 2>&1; then + AUTOMAKE=automake + echo "$ac_t""found" 1>&6 +else + AUTOMAKE="$missing_dir/missing automake" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working autoheader""... $ac_c" 1>&6 +echo "configure:756: checking for working autoheader" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (autoheader --version) < /dev/null > /dev/null 2>&1; then + AUTOHEADER=autoheader + echo "$ac_t""found" 1>&6 +else + AUTOHEADER="$missing_dir/missing autoheader" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working makeinfo""... $ac_c" 1>&6 +echo "configure:769: checking for working makeinfo" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (makeinfo --version) < /dev/null > /dev/null 2>&1; then + MAKEINFO=makeinfo + echo "$ac_t""found" 1>&6 +else + MAKEINFO="$missing_dir/missing makeinfo" + echo "$ac_t""missing" 1>&6 +fi + + + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:784: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:813: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:842: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + ac_prog_rejected=no + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:890: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:924: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:929: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes + ac_test_CFLAGS="${CFLAGS+set}" + ac_save_CFLAGS="$CFLAGS" + CFLAGS= + echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:953: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 + if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" + elif test $ac_cv_prog_cc_g = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-O2" + fi +else + GCC= + test "${CFLAGS+set}" = set || CFLAGS="-g" +fi + +for ac_prog in $CCC c++ g++ gcc CC cxx cc++ +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:985: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CXX="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CXX="$ac_cv_prog_CXX" +if test -n "$CXX"; then + echo "$ac_t""$CXX" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$CXX" && break +done +test -n "$CXX" || CXX="gcc" + + +echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:1016: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works" >&5 + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + ac_cv_prog_cxx_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cxx_cross=no + else + ac_cv_prog_cxx_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cxx_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cxx_works" 1>&6 +if test $ac_cv_prog_cxx_works = no; then + { echo "configure: error: installation or configuration problem: C++ compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:1056: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cxx_cross" 1>&6 +cross_compiling=$ac_cv_prog_cxx_cross + +echo $ac_n "checking whether we are using GNU C++""... $ac_c" 1>&6 +echo "configure:1061: checking whether we are using GNU C++" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gxx'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.C <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gxx=yes +else + ac_cv_prog_gxx=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gxx" 1>&6 + +if test $ac_cv_prog_gxx = yes; then + GXX=yes + ac_test_CXXFLAGS="${CXXFLAGS+set}" + ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS= + echo $ac_n "checking whether ${CXX-g++} accepts -g""... $ac_c" 1>&6 +echo "configure:1085: checking whether ${CXX-g++} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cxx_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.cc +if test -z "`${CXX-g++} -g -c conftest.cc 2>&1`"; then + ac_cv_prog_cxx_g=yes +else + ac_cv_prog_cxx_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cxx_g" 1>&6 + if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS="$ac_save_CXXFLAGS" + elif test $ac_cv_prog_cxx_g = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-O2" + fi +else + GXX= + test "${CXXFLAGS+set}" = set || CXXFLAGS="-g" +fi + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1115: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:1152: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6 +echo "configure:1202: checking whether ln -s works" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + rm -f conftestdata +if ln -s X conftestdata 2>/dev/null +then + rm -f conftestdata + ac_cv_prog_LN_S="ln -s" +else + ac_cv_prog_LN_S=ln +fi +fi +LN_S="$ac_cv_prog_LN_S" +if test "$ac_cv_prog_LN_S" = "ln -s"; then + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +# set logging default value +# with_logging=yes +# Check whether --with-logging or --without-logging was given. +if test "${with_logging+set}" = set; then + withval="$with_logging" + : +fi + +if test "x$with_logging" = "xno" ; then + cat >> confdefs.h <<\EOF +#define FG_NDEBUG 1 +EOF + +fi + + +# Check whether --with-efence or --without-efence was given. +if test "${with_efence+set}" = set; then + withval="$with_efence" + : +fi + + +if test "x$with_efence" = "xyes" ; then + echo "Building with efence" + LIBS= "$LIBS -lefence" +fi + + +# Check whether --with-sgi-opengl or --without-sgi-opengl was given. +if test "${with_sgi_opengl+set}" = set; then + withval="$with_sgi_opengl" + : +fi + + + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:1261: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1282: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1299: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +ac_safe=`echo "windows.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for windows.h""... $ac_c" 1>&6 +echo "configure:1323: checking for windows.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1333: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + + +EXTRA_DIRS="/usr/local /usr/local/plib /usr/X11R6" + +if test -d /opt/X11R6 ; then + EXTRA_DIRS="$EXTRA_DIRS /opt/X11R6" +fi + +if test "x$ac_cv_header_windows_h" = "xyes" ; then + EXTRA_DIRS="${EXTRA_DIRS} `pwd`/Win32" +fi +echo "checking for extra include and lib directories..." 1>&6 +exdirs="$prefix ${EXTRA_DIRS}" + +subexdirs="" +if test "$subexdirs" = "" ; then + subexdirs="-" +fi +for subexdir in $subexdirs ; do +if test "$subexdir" = "-" ; then + subexdir="" +else + subexdir="/$subexdir" +fi +for exdir in $exdirs ; do + if test "$exdir" != "/usr" || test "$subexdir" != ""; then + incdir="${exdir}/include${subexdir}" + +incdir="$incdir" +if test -r $incdir ; then + case "$CPPFLAGS" in + *-I${incdir}*) + # echo " + already had $incdir" 1>&6 + ;; + *) + if test "$CPPFLAGS" = "" ; then + CPPFLAGS="-I$incdir" + else + CPPFLAGS="$CPPFLAGS -I$incdir" + fi + echo " + found $incdir" 1>&6 + ;; + esac +fi + + + mylibdir="${exdir}/lib${subexdir}" + +mylibdir="$mylibdir" +if test -r $mylibdir ; then + case "$LDFLAGS" in + *-L${mylibdir}*) + # echo " + already had $mylibdir" 1>&6 + ;; + *) + if test "$LDFLAGS" = "" ; then + LDFLAGS="-L$mylibdir" + else + LDFLAGS="$LDFLAGS -L$mylibdir" + fi + echo " + found $mylibdir" 1>&6 + ;; + esac +fi + + + progdir="${exdir}/bin${subexdirr}" + +progdir="$progdir" +if test -r $progdir ; then + case "$PATH" in + *:${progdir}*) + # echo " + already had $progdir" 1>&6 + ;; + *${progdir}:*) + # echo " + already had $progdir" 1>&6 + ;; + *) + if test "$PATH" = "" ; then + PATH="$progdir" + else + PATH="$PATH:$progdir" + fi + echo " + found $progdir" 1>&6 + ;; + esac +fi + + fi +done +done + + + +# If we find X, set shell vars x_includes and x_libraries to the +# paths, otherwise set no_x=yes. +# Uses ac_ vars as temps to allow command line to override cache and checks. +# --without-x overrides everything else, but does not touch the cache. +echo $ac_n "checking for X""... $ac_c" 1>&6 +echo "configure:1452: checking for X" >&5 + +# Check whether --with-x or --without-x was given. +if test "${with_x+set}" = set; then + withval="$with_x" + : +fi + +# $have_x is `yes', `no', `disabled', or empty when we do not yet know. +if test "x$with_x" = xno; then + # The user explicitly disabled X. + have_x=disabled +else + if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then + # Both variables are already set. + have_x=yes + else +if eval "test \"`echo '$''{'ac_cv_have_x'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # One or both of the vars are not set, and there is no cached value. +ac_x_includes=NO ac_x_libraries=NO +rm -fr conftestdir +if mkdir conftestdir; then + cd conftestdir + # Make sure to not put "make" in the Imakefile rules, since we grep it out. + cat > Imakefile <<'EOF' +acfindx: + @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"' +EOF + if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then + # GNU make sometimes prints "make[1]: Entering...", which would confuse us. + eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` + # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. + for ac_extension in a so sl; do + if test ! -f $ac_im_usrlibdir/libX11.$ac_extension && + test -f $ac_im_libdir/libX11.$ac_extension; then + ac_im_usrlibdir=$ac_im_libdir; break + fi + done + # Screen out bogus values from the imake configuration. They are + # bogus both because they are the default anyway, and because + # using them would break gcc on systems where it needs fixed includes. + case "$ac_im_incroot" in + /usr/include) ;; + *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes="$ac_im_incroot" ;; + esac + case "$ac_im_usrlibdir" in + /usr/lib | /lib) ;; + *) test -d "$ac_im_usrlibdir" && ac_x_libraries="$ac_im_usrlibdir" ;; + esac + fi + cd .. + rm -fr conftestdir +fi + +if test "$ac_x_includes" = NO; then + # Guess where to find include files, by looking for this one X11 .h file. + test -z "$x_direct_test_include" && x_direct_test_include=X11/Intrinsic.h + + # First, try using that file with no special directory specified. +cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1519: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + # We can compile using X headers with no special include directory. +ac_x_includes= +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + # Look for the header file in a standard set of common directories. +# Check X11 before X11Rn because it is often a symlink to the current release. + for ac_dir in \ + /usr/X11/include \ + /usr/X11R6/include \ + /usr/X11R5/include \ + /usr/X11R4/include \ + \ + /usr/include/X11 \ + /usr/include/X11R6 \ + /usr/include/X11R5 \ + /usr/include/X11R4 \ + \ + /usr/local/X11/include \ + /usr/local/X11R6/include \ + /usr/local/X11R5/include \ + /usr/local/X11R4/include \ + \ + /usr/local/include/X11 \ + /usr/local/include/X11R6 \ + /usr/local/include/X11R5 \ + /usr/local/include/X11R4 \ + \ + /usr/X386/include \ + /usr/x386/include \ + /usr/XFree86/include/X11 \ + \ + /usr/include \ + /usr/local/include \ + /usr/unsupported/include \ + /usr/athena/include \ + /usr/local/x11r5/include \ + /usr/lpp/Xamples/include \ + \ + /usr/openwin/include \ + /usr/openwin/share/include \ + ; \ + do + if test -r "$ac_dir/$x_direct_test_include"; then + ac_x_includes=$ac_dir + break + fi + done +fi +rm -f conftest* +fi # $ac_x_includes = NO + +if test "$ac_x_libraries" = NO; then + # Check for the libraries. + + test -z "$x_direct_test_library" && x_direct_test_library=Xt + test -z "$x_direct_test_function" && x_direct_test_function=XtMalloc + + # See if we find them without any special options. + # Don't add to $LIBS permanently. + ac_save_LIBS="$LIBS" + LIBS="-l$x_direct_test_library $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + LIBS="$ac_save_LIBS" +# We can link X programs with no special library path. +ac_x_libraries= +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + LIBS="$ac_save_LIBS" +# First see if replacing the include by lib works. +# Check X11 before X11Rn because it is often a symlink to the current release. +for ac_dir in `echo "$ac_x_includes" | sed s/include/lib/` \ + /usr/X11/lib \ + /usr/X11R6/lib \ + /usr/X11R5/lib \ + /usr/X11R4/lib \ + \ + /usr/lib/X11 \ + /usr/lib/X11R6 \ + /usr/lib/X11R5 \ + /usr/lib/X11R4 \ + \ + /usr/local/X11/lib \ + /usr/local/X11R6/lib \ + /usr/local/X11R5/lib \ + /usr/local/X11R4/lib \ + \ + /usr/local/lib/X11 \ + /usr/local/lib/X11R6 \ + /usr/local/lib/X11R5 \ + /usr/local/lib/X11R4 \ + \ + /usr/X386/lib \ + /usr/x386/lib \ + /usr/XFree86/lib/X11 \ + \ + /usr/lib \ + /usr/local/lib \ + /usr/unsupported/lib \ + /usr/athena/lib \ + /usr/local/x11r5/lib \ + /usr/lpp/Xamples/lib \ + /lib/usr/lib/X11 \ + \ + /usr/openwin/lib \ + /usr/openwin/share/lib \ + ; \ +do + for ac_extension in a so sl; do + if test -r $ac_dir/lib${x_direct_test_library}.$ac_extension; then + ac_x_libraries=$ac_dir + break 2 + fi + done +done +fi +rm -f conftest* +fi # $ac_x_libraries = NO + +if test "$ac_x_includes" = NO || test "$ac_x_libraries" = NO; then + # Didn't find X anywhere. Cache the known absence of X. + ac_cv_have_x="have_x=no" +else + # Record where we found X for the cache. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries" +fi +fi + fi + eval "$ac_cv_have_x" +fi # $with_x != no + +if test "$have_x" != yes; then + echo "$ac_t""$have_x" 1>&6 + no_x=yes +else + # If each of the values was on the command line, it overrides each guess. + test "x$x_includes" = xNONE && x_includes=$ac_x_includes + test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries + # Update the cache value to reflect the command line values. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$x_includes ac_x_libraries=$x_libraries" + echo "$ac_t""libraries $x_libraries, headers $x_includes" 1>&6 +fi + +if test "$no_x" = yes; then + # Not all programs may use this symbol, but it does not hurt to define it. + cat >> confdefs.h <<\EOF +#define X_DISPLAY_MISSING 1 +EOF + + X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS= +else + if test -n "$x_includes"; then + X_CFLAGS="$X_CFLAGS -I$x_includes" + fi + + # It would also be nice to do this for all -L options, not just this one. + if test -n "$x_libraries"; then + X_LIBS="$X_LIBS -L$x_libraries" + # For Solaris; some versions of Sun CC require a space after -R and + # others require no space. Words are not sufficient . . . . + case "`(uname -sr) 2>/dev/null`" in + "SunOS 5"*) + echo $ac_n "checking whether -R must be followed by a space""... $ac_c" 1>&6 +echo "configure:1701: checking whether -R must be followed by a space" >&5 + ac_xsave_LIBS="$LIBS"; LIBS="$LIBS -R$x_libraries" + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + ac_R_nospace=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_R_nospace=no +fi +rm -f conftest* + if test $ac_R_nospace = yes; then + echo "$ac_t""no" 1>&6 + X_LIBS="$X_LIBS -R$x_libraries" + else + LIBS="$ac_xsave_LIBS -R $x_libraries" + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + ac_R_space=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_R_space=no +fi +rm -f conftest* + if test $ac_R_space = yes; then + echo "$ac_t""yes" 1>&6 + X_LIBS="$X_LIBS -R $x_libraries" + else + echo "$ac_t""neither works" 1>&6 + fi + fi + LIBS="$ac_xsave_LIBS" + esac + fi + + # Check for system-dependent libraries X programs must link with. + # Do this before checking for the system-independent R6 libraries + # (-lICE), since we may need -lsocket or whatever for X linking. + + if test "$ISC" = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet" + else + # Martyn.Johnson@cl.cam.ac.uk says this is needed for Ultrix, if the X + # libraries were built with DECnet support. And karl@cs.umb.edu says + # the Alpha needs dnet_stub (dnet does not exist). + echo $ac_n "checking for dnet_ntoa in -ldnet""... $ac_c" 1>&6 +echo "configure:1766: checking for dnet_ntoa in -ldnet" >&5 +ac_lib_var=`echo dnet'_'dnet_ntoa | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldnet $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet" +else + echo "$ac_t""no" 1>&6 +fi + + if test $ac_cv_lib_dnet_dnet_ntoa = no; then + echo $ac_n "checking for dnet_ntoa in -ldnet_stub""... $ac_c" 1>&6 +echo "configure:1807: checking for dnet_ntoa in -ldnet_stub" >&5 +ac_lib_var=`echo dnet_stub'_'dnet_ntoa | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldnet_stub $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub" +else + echo "$ac_t""no" 1>&6 +fi + + fi + + # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT, + # to get the SysV transport functions. + # chad@anasazi.com says the Pyramis MIS-ES running DC/OSx (SVR4) + # needs -lnsl. + # The nsl library prevents programs from opening the X display + # on Irix 5.2, according to dickey@clark.net. + echo $ac_n "checking for gethostbyname""... $ac_c" 1>&6 +echo "configure:1855: checking for gethostbyname" >&5 +if eval "test \"`echo '$''{'ac_cv_func_gethostbyname'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname) +choke me +#else +gethostbyname(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1883: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_gethostbyname=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_gethostbyname=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'gethostbyname`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + + if test $ac_cv_func_gethostbyname = no; then + echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6 +echo "configure:1904: checking for gethostbyname in -lnsl" >&5 +ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lnsl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl" +else + echo "$ac_t""no" 1>&6 +fi + + fi + + # lieder@skyler.mavd.honeywell.com says without -lsocket, + # socket/setsockopt and other routines are undefined under SCO ODT + # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary + # on later versions), says simon@lia.di.epfl.ch: it contains + # gethostby* variants that don't use the nameserver (or something). + # -lsocket must be given before -lnsl if both are needed. + # We assume that if connect needs -lnsl, so does gethostbyname. + echo $ac_n "checking for connect""... $ac_c" 1>&6 +echo "configure:1953: checking for connect" >&5 +if eval "test \"`echo '$''{'ac_cv_func_connect'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_connect) || defined (__stub___connect) +choke me +#else +connect(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1981: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_connect=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_connect=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'connect`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + + if test $ac_cv_func_connect = no; then + echo $ac_n "checking for connect in -lsocket""... $ac_c" 1>&6 +echo "configure:2002: checking for connect in -lsocket" >&5 +ac_lib_var=`echo socket'_'connect | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsocket $X_EXTRA_LIBS $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS" +else + echo "$ac_t""no" 1>&6 +fi + + fi + + # gomez@mi.uni-erlangen.de says -lposix is necessary on A/UX. + echo $ac_n "checking for remove""... $ac_c" 1>&6 +echo "configure:2045: checking for remove" >&5 +if eval "test \"`echo '$''{'ac_cv_func_remove'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char remove(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_remove) || defined (__stub___remove) +choke me +#else +remove(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2073: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_remove=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_remove=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'remove`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + + if test $ac_cv_func_remove = no; then + echo $ac_n "checking for remove in -lposix""... $ac_c" 1>&6 +echo "configure:2094: checking for remove in -lposix" >&5 +ac_lib_var=`echo posix'_'remove | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lposix $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix" +else + echo "$ac_t""no" 1>&6 +fi + + fi + + # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay. + echo $ac_n "checking for shmat""... $ac_c" 1>&6 +echo "configure:2137: checking for shmat" >&5 +if eval "test \"`echo '$''{'ac_cv_func_shmat'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shmat(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_shmat) || defined (__stub___shmat) +choke me +#else +shmat(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2165: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_shmat=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_shmat=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'shmat`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + + if test $ac_cv_func_shmat = no; then + echo $ac_n "checking for shmat in -lipc""... $ac_c" 1>&6 +echo "configure:2186: checking for shmat in -lipc" >&5 +ac_lib_var=`echo ipc'_'shmat | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lipc $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc" +else + echo "$ac_t""no" 1>&6 +fi + + fi + fi + + # Check for libraries that X11R6 Xt/Xaw programs need. + ac_save_LDFLAGS="$LDFLAGS" + test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries" + # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to + # check for ICE first), but we must link in the order -lSM -lICE or + # we get undefined symbols. So assume we have SM if we have ICE. + # These have to be linked with before -lX11, unlike the other + # libraries we check for below, so use a different variable. + # --interran@uluru.Stanford.EDU, kb@cs.umb.edu. + echo $ac_n "checking for IceConnectionNumber in -lICE""... $ac_c" 1>&6 +echo "configure:2238: checking for IceConnectionNumber in -lICE" >&5 +ac_lib_var=`echo ICE'_'IceConnectionNumber | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lICE $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE" +else + echo "$ac_t""no" 1>&6 +fi + + LDFLAGS="$ac_save_LDFLAGS" + +fi + + + +null_LIBS="$LIBS" + +echo $ac_n "checking for cos in -lm""... $ac_c" 1>&6 +echo "configure:2286: checking for cos in -lm" >&5 +ac_lib_var=`echo m'_'cos | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lm $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo m | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + +base_LIBS="$LIBS" + +echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6 +echo "configure:2336: checking for socket in -lsocket" >&5 +ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsocket $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +echo $ac_n "checking for XCreateWindow in -lX11""... $ac_c" 1>&6 +echo "configure:2383: checking for XCreateWindow in -lX11" >&5 +ac_lib_var=`echo X11'_'XCreateWindow | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lX11 $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo X11 | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +echo $ac_n "checking for XShmCreateImage in -lXext""... $ac_c" 1>&6 +echo "configure:2430: checking for XShmCreateImage in -lXext" >&5 +ac_lib_var=`echo Xext'_'XShmCreateImage | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lXext $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo Xext | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +echo $ac_n "checking for XGetExtensionVersion in -lXi""... $ac_c" 1>&6 +echo "configure:2477: checking for XGetExtensionVersion in -lXi" >&5 +ac_lib_var=`echo Xi'_'XGetExtensionVersion | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lXi $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo Xi | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +echo $ac_n "checking for IceOpenConnection in -lICE""... $ac_c" 1>&6 +echo "configure:2524: checking for IceOpenConnection in -lICE" >&5 +ac_lib_var=`echo ICE'_'IceOpenConnection | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lICE $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo ICE | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +echo $ac_n "checking for SmcOpenConnection in -lSM""... $ac_c" 1>&6 +echo "configure:2571: checking for SmcOpenConnection in -lSM" >&5 +ac_lib_var=`echo SM'_'SmcOpenConnection | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lSM $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo SM | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +echo $ac_n "checking for XtMalloc in -lXt""... $ac_c" 1>&6 +echo "configure:2618: checking for XtMalloc in -lXt" >&5 +ac_lib_var=`echo Xt'_'XtMalloc | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lXt $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo Xt | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +echo $ac_n "checking for XmuLookupStandardColormap in -lXmu""... $ac_c" 1>&6 +echo "configure:2665: checking for XmuLookupStandardColormap in -lXmu" >&5 +ac_lib_var=`echo Xmu'_'XmuLookupStandardColormap | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lXmu $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo Xmu | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + + +if test "x$ac_cv_header_windows_h" != "xyes" ; then + + echo $ac_n "checking for glNewList in -lGLcore""... $ac_c" 1>&6 +echo "configure:2716: checking for glNewList in -lGLcore" >&5 +ac_lib_var=`echo GLcore'_'glNewList | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lGLcore $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo GLcore | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + if test "x$ac_cv_lib_GLcore_glNewList" = "xno" ; then + echo $ac_n "checking for glNewList in -lGL""... $ac_c" 1>&6 +echo "configure:2764: checking for glNewList in -lGL" >&5 +ac_lib_var=`echo GL'_'glNewList | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lGL $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo GL | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + if test "x$ac_cv_lib_GL_glNewList" = "xno" ; then + echo $ac_n "checking for glNewList in -lMesaGL""... $ac_c" 1>&6 +echo "configure:2812: checking for glNewList in -lMesaGL" >&5 +ac_lib_var=`echo MesaGL'_'glNewList | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lMesaGL $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo MesaGL | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + fi + else + echo $ac_n "checking for glXCreateContext in -lGL""... $ac_c" 1>&6 +echo "configure:2861: checking for glXCreateContext in -lGL" >&5 +ac_lib_var=`echo GL'_'glXCreateContext | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lGL $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo GL | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + fi + + if test "x$ac_cv_lib_MesaGL_glNewList" = "xyes" ; then + ac_safe=`echo "GL/xmesa.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for GL/xmesa.h""... $ac_c" 1>&6 +echo "configure:2912: checking for GL/xmesa.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2922: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + + if test "x$ac_cv_header_GL_xmesa_h" = "xyes"; then + cat >> confdefs.h <<\EOF +#define XMESA 1 +EOF + + cat >> confdefs.h <<\EOF +#define FX 1 +EOF + + fi + fi + + echo $ac_n "checking for gluLookAt in -lGLU""... $ac_c" 1>&6 +echo "configure:2956: checking for gluLookAt in -lGLU" >&5 +ac_lib_var=`echo GLU'_'gluLookAt | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lGLU $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo GLU | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + if test "x$ac_cv_lib_GLU_gluLookAt" = "xno" ; then + echo $ac_n "checking for gluLookAt in -lMesaGLU""... $ac_c" 1>&6 +echo "configure:3004: checking for gluLookAt in -lMesaGLU" >&5 +ac_lib_var=`echo MesaGLU'_'gluLookAt | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lMesaGLU $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo MesaGLU | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + fi + + echo $ac_n "checking for glutGetModifiers in -lglut""... $ac_c" 1>&6 +echo "configure:3053: checking for glutGetModifiers in -lglut" >&5 +ac_lib_var=`echo glut'_'glutGetModifiers | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lglut $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo glut | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + + save_LIBS="$LIBS" + echo $ac_n "checking for glutGameModeString in -lglut""... $ac_c" 1>&6 +echo "configure:3102: checking for glutGameModeString in -lglut" >&5 +ac_lib_var=`echo glut'_'glutGameModeString | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lglut $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo glut | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + LIBS="$save_LIBS" +else + + echo Win32 specific hacks... + cat >> confdefs.h <<\EOF +#define WIN32 1 +EOF + + + + +if test "no" = "yes"; then + ENABLE_XMESA_FX_TRUE= + ENABLE_XMESA_FX_FALSE='#' +else + ENABLE_XMESA_FX_TRUE='#' + ENABLE_XMESA_FX_FALSE= +fi + + ac_cv_lib_glut_glutGetModifiers="yes" + ac_cv_lib_glut_glutGameModeString="yes" + + if test "x$with_sgi_opengl" = "xyes" ; then + echo "Building with glut.dll, glu.dll, and opengl.dll" + WIN32_GLUT=glut + WIN32_GLU=glu + WIN32_OPENGL=opengl + else + echo "Building with glut32.dll, glu32.dll, and opengl32.dll" + WIN32_GLUT=glut32 + WIN32_GLU=glu32 + WIN32_OPENGL=opengl32 + fi + + LIBS="$LIBS -l${WIN32_GLUT} -l${WIN32_GLU} -l${WIN32_OPENGL}" + LIBS="$LIBS -luser32 -lgdi32" + echo "Will link apps with $LIBS" +fi + +if test "x$ac_cv_lib_glut_glutGetModifiers" = "xno"; then + echo + echo "Unable to find the necessary OpenGL or GLUT libraries." + echo "See config.log for automated test details and results ..." + exit 1 +fi + +if test "x$ac_cv_lib_glut_glutGameModeString" = "xno"; then + echo + echo "Your version of glut doesn't support game mode." + echo "You need to fetch and install the latest version of glut from:" + echo + echo " http://reality.sgi.com/opengl/glut3/glut3.html" + exit 1 +fi + +opengl_LIBS="$LIBS" +LIBS="$base_LIBS" + + + + + + +if test "x$ac_cv_lib_X11_XCreateWindow" = "xyes" ; then + HAVE_XWINDOWS_TRUE= + HAVE_XWINDOWS_FALSE='#' +else + HAVE_XWINDOWS_TRUE='#' + HAVE_XWINDOWS_FALSE= +fi + +ac_safe=`echo "plib/pu.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for plib/pu.h""... $ac_c" 1>&6 +echo "configure:3221: checking for plib/pu.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:3231: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + +if test "x$ac_cv_header_plib_pu_h" != "xyes"; then + echo + echo "You *must* have the plib library installed on your system to build" + echo "the FGFS simulator!" + echo + echo "Please see README.plib for more details." + echo + echo "configure aborted." + exit +fi + +for ac_hdr in gpc.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:3267: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:3277: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +if test "x$ac_cv_header_gpc_h" != "xyes"; then + echo + echo "You need to have the GPC library installed on your system to" + echo "build some of the scenery generation tools, otherwise you won't" + echo "be able to create scenery." + echo + echo "Please see README.gpc for more details." + echo + echo "(pausing 5 seconds)" + sleep 5 + echo +fi + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:3317: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:3330: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:3397: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +for ac_hdr in \ + fcntl.h getopt.h malloc.h memory.h stdlib.h sys/param.h sys/stat.h \ + sys/time.h sys/timeb.h unistd.h windows.h winbase.h values.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:3426: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:3436: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:3464: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:3518: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:3539: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 +echo "configure:3572: checking whether time.h and sys/time.h may both be included" >&5 +if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +int main() { +struct tm *tp; +; return 0; } +EOF +if { (eval echo configure:3586: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_header_time=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_time=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_time" 1>&6 +if test $ac_cv_header_time = yes; then + cat >> confdefs.h <<\EOF +#define TIME_WITH_SYS_TIME 1 +EOF + +fi + +echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6 +echo "configure:3607: checking whether struct tm is in sys/time.h or time.h" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +int main() { +struct tm *tp; tp->tm_sec; +; return 0; } +EOF +if { (eval echo configure:3620: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm=time.h +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm=sys/time.h +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm" 1>&6 +if test $ac_cv_struct_tm = sys/time.h; then + cat >> confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + + +echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 +echo "configure:3642: checking return type of signal handlers" >&5 +if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#ifdef signal +#undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int main() { +int i; +; return 0; } +EOF +if { (eval echo configure:3664: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_type_signal=void +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_type_signal=int +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_type_signal" 1>&6 +cat >> confdefs.h <&6 +echo "configure:3683: checking for vprintf" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char vprintf(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_vprintf) || defined (__stub___vprintf) +choke me +#else +vprintf(); +#endif + +; return 0; } +EOF +if { (eval echo configure:3711: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_vprintf=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_vprintf=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_VPRINTF 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +if test "$ac_cv_func_vprintf" != yes; then +echo $ac_n "checking for _doprnt""... $ac_c" 1>&6 +echo "configure:3735: checking for _doprnt" >&5 +if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub__doprnt) || defined (__stub____doprnt) +choke me +#else +_doprnt(); +#endif + +; return 0; } +EOF +if { (eval echo configure:3763: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func__doprnt=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func__doprnt=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_DOPRNT 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +fi + +for ac_func in ftime gettimeofday timegm memcpy bcopy mktime strstr rand \ + random setitimer getitimer signal GetLocalTime rint getrusage +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:3791: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:3819: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + + + + + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.12" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "\ + VERSION \ + Makefile \ + Construct/Makefile \ + Construct/Clipper/Makefile \ + Construct/Combine/Makefile \ + Construct/GenOutput/Makefile \ + Construct/Match/Makefile \ + Construct/Triangulate/Makefile \ + Construct/Main/Makefile \ + Construct/Parallel/Makefile \ + Lib/Makefile \ + Lib/Array/Makefile \ + Lib/Build/Makefile \ + Lib/DEM/Makefile \ + Lib/Polygon/Makefile \ + Lib/poly2tri/Makefile \ + Lib/shapelib/Makefile \ + Lib/Triangle/Makefile \ + Prep/Makefile \ + Prep/DemChop/Makefile \ + Prep/DemInfo/Makefile \ + Prep/DemRaw2ascii/Makefile \ + Prep/GenAirports/Makefile \ + Prep/GSHHS/Makefile \ + Prep/MergerClipper/Makefile \ + Prep/ShapeFile/Makefile \ + Utils/Makefile \ + Include/config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g +s%@PACKAGE@%$PACKAGE%g +s%@VERSION@%$VERSION%g +s%@ACLOCAL@%$ACLOCAL%g +s%@AUTOCONF@%$AUTOCONF%g +s%@AUTOMAKE@%$AUTOMAKE%g +s%@AUTOHEADER@%$AUTOHEADER%g +s%@MAKEINFO@%$MAKEINFO%g +s%@SET_MAKE@%$SET_MAKE%g +s%@CC@%$CC%g +s%@CXX@%$CXX%g +s%@RANLIB@%$RANLIB%g +s%@LN_S@%$LN_S%g +s%@CPP@%$CPP%g +s%@X_CFLAGS@%$X_CFLAGS%g +s%@X_PRE_LIBS@%$X_PRE_LIBS%g +s%@X_LIBS@%$X_LIBS%g +s%@X_EXTRA_LIBS@%$X_EXTRA_LIBS%g +s%@ENABLE_XMESA_FX_TRUE@%$ENABLE_XMESA_FX_TRUE%g +s%@ENABLE_XMESA_FX_FALSE@%$ENABLE_XMESA_FX_FALSE%g +s%@base_LIBS@%$base_LIBS%g +s%@opengl_LIBS@%$opengl_LIBS%g +s%@HAVE_XWINDOWS_TRUE@%$HAVE_XWINDOWS_TRUE%g +s%@HAVE_XWINDOWS_FALSE@%$HAVE_XWINDOWS_FALSE%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +test -z "$CONFIG_HEADERS" || echo timestamp > Include/stamp-h + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + + +echo "" +echo "Configure Summary" +echo "=================" + +echo "Prefix: $prefix" + +if test "x$with_logging" != "x"; then + echo "Debug messages: $with_logging" +else + echo "Debug messages: yes" +fi + +if test "x$with_efence" != "x"; then + echo "Electric fence: $with_efence" +else + echo "Electric fence: no" +fi diff --git a/configure.in b/configure.in new file mode 100644 index 00000000..41cfefcf --- /dev/null +++ b/configure.in @@ -0,0 +1,275 @@ +dnl Process this file with aclocal ; automake -a ; autoconf to produce a +dnl working configure script. +dnl +dnl $Id$ + +AC_INIT(Lib/DEM/dem.cxx) + +dnl Initialize the automake stuff +AM_INIT_AUTOMAKE(TerraGear, 0.0.0) + +dnl Checks for programs. +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_CXX +AC_PROG_RANLIB +AC_PROG_INSTALL +AC_PROG_LN_S + +dnl Specify if we want logging (testing build) or not (release build) +# set logging default value +# with_logging=yes +AC_ARG_WITH(logging, [ --with-logging Include logging output (default)]) +if test "x$with_logging" = "xno" ; then + AC_DEFINE(FG_NDEBUG) +fi + + +dnl specify if we are building with "checker" +AC_ARG_WITH(efence, [ --with-efence Specify if we are building with "electric-fence"]) + +if test "x$with_efence" = "xyes" ; then + echo "Building with efence" + LIBS= "$LIBS -lefence" +fi + + +dnl Let the Win32 user specify if they want to build with the SGI +dnl opengl.dll as opposed to the more standard openg32.dll +AC_ARG_WITH(sgi-opengl, [ --with-sgi-opengl Build against SGI's opengl.dll glu.dll and glut.dll]) + + +dnl Check for MS Windows environment +AC_CHECK_HEADER(windows.h) + +dnl extra library and include directories +EXTRA_DIRS="/usr/local /usr/local/plib /usr/X11R6" + +if test -d /opt/X11R6 ; then + EXTRA_DIRS="$EXTRA_DIRS /opt/X11R6" +fi + +if test "x$ac_cv_header_windows_h" = "xyes" ; then + EXTRA_DIRS="${EXTRA_DIRS} `pwd`/Win32" +fi +wi_EXTRA_DIRS(no, ${EXTRA_DIRS}) + +dnl Using AM_CONDITIONAL is a step out of the protected little +dnl automake fold so it is potentially dangerous. But, we are +dnl beginning to run into cases where the standard checks are not +dnl enough. AM_CONDITIONALS are then referenced to conditionally +dnl build a Makefile.in from a Makefile.am which lets us define custom +dnl includes, compile alternative source files, etc. + +dnl Check for X11 (fancy) +AC_PATH_XTRA + +dnl Checks for libraries. + +null_LIBS="$LIBS" + +AC_CHECK_LIB(m, cos) + +base_LIBS="$LIBS" + +AC_CHECK_LIB(socket, socket) +AC_CHECK_LIB(X11, XCreateWindow) +AC_CHECK_LIB(Xext, XShmCreateImage) +AC_CHECK_LIB(Xi, XGetExtensionVersion) +AC_CHECK_LIB(ICE, IceOpenConnection) +AC_CHECK_LIB(SM, SmcOpenConnection) +AC_CHECK_LIB(Xt, XtMalloc) +AC_CHECK_LIB(Xmu, XmuLookupStandardColormap) + +dnl check for OpenGL related libraries + +if test "x$ac_cv_header_windows_h" != "xyes" ; then + dnl Reasonable stuff for non-windoze variants ... :-) + + AC_CHECK_LIB(GLcore, glNewList) + if test "x$ac_cv_lib_GLcore_glNewList" = "xno" ; then + dnl if no GLcore, check for GL + AC_CHECK_LIB(GL, glNewList) + if test "x$ac_cv_lib_GL_glNewList" = "xno" ; then + dnl if no GL, check for MesaGL + AC_CHECK_LIB(MesaGL, glNewList) + fi + else + dnl if GLcore found, then also check for GL + AC_CHECK_LIB(GL, glXCreateContext) + fi + + dnl if using mesa, check for xmesa.h + if test "x$ac_cv_lib_MesaGL_glNewList" = "xyes" ; then + AC_CHECK_HEADER(GL/xmesa.h) + if test "x$ac_cv_header_GL_xmesa_h" = "xyes"; then + AC_DEFINE( XMESA ) + AC_DEFINE( FX ) + fi + fi + + AC_CHECK_LIB(GLU, gluLookAt) + if test "x$ac_cv_lib_GLU_gluLookAt" = "xno" ; then + dnl if no GLU, check for MesaGLU + AC_CHECK_LIB(MesaGLU, gluLookAt) + fi + + dnl check for glut + AC_CHECK_LIB(glut, glutGetModifiers) + + dnl test for glutGameModeString, but avoid adding glut a second time into + dnl the list of libraries + save_LIBS="$LIBS" + AC_CHECK_LIB(glut, glutGameModeString) + LIBS="$save_LIBS" +else + dnl Win32 is a little wierd because it has to try to handle the various + dnl winbloze-isms. We'll just do this manually for now. + + echo Win32 specific hacks... + AC_DEFINE(WIN32) + + dnl force a failed check since we will be building under windoze + AM_CONDITIONAL(ENABLE_XMESA_FX, test "no" = "yes") + + dnl just define these to true and hope for the best + ac_cv_lib_glut_glutGetModifiers="yes" + ac_cv_lib_glut_glutGameModeString="yes" + + if test "x$with_sgi_opengl" = "xyes" ; then + echo "Building with glut.dll, glu.dll, and opengl.dll" + WIN32_GLUT=glut + WIN32_GLU=glu + WIN32_OPENGL=opengl + else + echo "Building with glut32.dll, glu32.dll, and opengl32.dll" + WIN32_GLUT=glut32 + WIN32_GLU=glu32 + WIN32_OPENGL=opengl32 + fi + + LIBS="$LIBS -l${WIN32_GLUT} -l${WIN32_GLU} -l${WIN32_OPENGL}" + LIBS="$LIBS -luser32 -lgdi32" + echo "Will link apps with $LIBS" +fi + +if test "x$ac_cv_lib_glut_glutGetModifiers" = "xno"; then + echo + echo "Unable to find the necessary OpenGL or GLUT libraries." + echo "See config.log for automated test details and results ..." + exit 1 +fi + +if test "x$ac_cv_lib_glut_glutGameModeString" = "xno"; then + echo + echo "Your version of glut doesn't support game mode." + echo "You need to fetch and install the latest version of glut from:" + echo + echo " http://reality.sgi.com/opengl/glut3/glut3.html" + exit 1 +fi + +opengl_LIBS="$LIBS" +LIBS="$base_LIBS" + +AC_SUBST(base_LIBS) +AC_SUBST(opengl_LIBS) + +AM_CONDITIONAL(HAVE_XWINDOWS, test "x$ac_cv_lib_X11_XCreateWindow" = "xyes" ) + +dnl Check for "plib" without which we cannot go on +AC_CHECK_HEADER(plib/pu.h) +if test "x$ac_cv_header_plib_pu_h" != "xyes"; then + echo + echo "You *must* have the plib library installed on your system to build" + echo "the FGFS simulator!" + echo + echo "Please see README.plib for more details." + echo + echo "configure aborted." + exit +fi + +dnl Check if Generic Polygon Clipping library is installed +dnl (from http://www.cs.man.ac.uk/aig/staff/alan/software/) +AC_CHECK_HEADERS( gpc.h ) +if test "x$ac_cv_header_gpc_h" != "xyes"; then + echo + echo "You need to have the GPC library installed on your system to" + echo "build some of the scenery generation tools, otherwise you won't" + echo "be able to create scenery." + echo + echo "Please see README.gpc for more details." + echo + echo "(pausing 5 seconds)" + sleep 5 + echo +fi + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS( \ + fcntl.h getopt.h malloc.h memory.h stdlib.h sys/param.h sys/stat.h \ + sys/time.h sys/timeb.h unistd.h windows.h winbase.h values.h ) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_STRUCT_TM + +dnl Checks for library functions. +AC_TYPE_SIGNAL +AC_FUNC_VPRINTF +AC_CHECK_FUNCS( ftime gettimeofday timegm memcpy bcopy mktime strstr rand \ + random setitimer getitimer signal GetLocalTime rint getrusage ) + +AM_CONFIG_HEADER(Include/config.h) + +AC_OUTPUT( \ + VERSION \ + Makefile \ + Construct/Makefile \ + Construct/Clipper/Makefile \ + Construct/Combine/Makefile \ + Construct/GenOutput/Makefile \ + Construct/Match/Makefile \ + Construct/Triangulate/Makefile \ + Construct/Main/Makefile \ + Construct/Parallel/Makefile \ + Lib/Makefile \ + Lib/Array/Makefile \ + Lib/Build/Makefile \ + Lib/DEM/Makefile \ + Lib/Polygon/Makefile \ + Lib/poly2tri/Makefile \ + Lib/shapelib/Makefile \ + Lib/Triangle/Makefile \ + Prep/Makefile \ + Prep/DemChop/Makefile \ + Prep/DemInfo/Makefile \ + Prep/DemRaw2ascii/Makefile \ + Prep/GenAirports/Makefile \ + Prep/GSHHS/Makefile \ + Prep/MergerClipper/Makefile \ + Prep/ShapeFile/Makefile \ + Utils/Makefile \ +) + +echo "" +echo "Configure Summary" +echo "=================" + +echo "Prefix: $prefix" + +if test "x$with_logging" != "x"; then + echo "Debug messages: $with_logging" +else + echo "Debug messages: yes" +fi + +if test "x$with_efence" != "x"; then + echo "Electric fence: $with_efence" +else + echo "Electric fence: no" +fi diff --git a/detect.c b/detect.c new file mode 100644 index 00000000..ca48b482 --- /dev/null +++ b/detect.c @@ -0,0 +1,183 @@ +#include "plugin.h" +// Triangle-stripdetection made simple. By Joost Bloemen, Copyright Vimana BV +// If you can improve the algorithm, be sure to let me know you can reach me +// at research@3dtop.com + +struct BMF_SURFACE{ + USHORT p0, p1, p2; +}; + +struct Hit{ + USHORT hits; + USHORT t[3]; + UCHAR mask[3]; + BMF_SURFACE p[3]; + BOOL Stripped; +}* HitList; + +// INPUT: +//ilist is the list with indices of the original triangles, will be broken down to +// only the triangles that can 't be stripped, at the end. +//length is the number of triangles in ilist. +// OUTPUT: +//nr-strips is the number of strips we detected, they have a minimum of two triangles +//strips_length contains an array[nr_strips] of strip-lengths, strip1, strip2 strip3 etc... +//nr_indices is the total number of vertice-indices we have in the array "stripindex": +//stripindex is a long list of strip1-indices, strip2-indices, strip3-indices etc... +//return-value is the new number of seperate triangles in ilist. +USHORT DetectStrips(BMF_SURFACE * ilist, USHORT length, + USHORT * nr_strips, USHORT * strip_length, + USHORT * nr_indices, USHORT * stripindex) +{ + USHORT lastp0,lastp1,lastp2; + USHORT n,nr,trynr; + USHORT p0,p1,p2; + USHORT i,k,next,p; + UCHAR mask; + BMF_SURFACE * temp, * tempEnd; + struct Hit * tempHitList; + // First part makes a hitlist per triangle, how many sides(hits) a triangle has shared, + // the indexes of the hits, up to 3 masks that define wich points the triangles each share + // and the points of each shared triangle. + HitList=(struct Hit *)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,length*sizeof(struct Hit)); + tempHitList=HitList; + for(n=0;np0)==p0){mask|=0x11;nr--;} + else {if (p==p1){mask|=0x12;nr--;} + else {if (p==p2){mask|=0x14;nr--;}} + } + if ((p=temp->p1)==p0){mask|=0x21;nr--;} + else {if (p==p1){mask|=0x22;nr--;} + else {if (p==p2){mask|=0x24;nr--;}} + } + if ((p=temp->p2)==p0){mask|=0x41;nr--;} + else {if (p==p1){mask|=0x42;nr--;} + else {if (p==p2){mask|=0x44;nr--;}} + } + + // That's it + if(!nr) //this doesn't happen very often + {USHORT nrs=tempHitList->hits; + //Rotate and save points so that p0 and p1 point to points that are the same. + // to check later, if we can make a strip of this + switch (mask>>4){ + case 5: + tempHitList->p[nrs].p1=temp->p0; + tempHitList->p[nrs].p0=temp->p2; + tempHitList->p[nrs].p2=temp->p1; + break; + + case 6: + tempHitList->p[nrs].p0=temp->p1; + tempHitList->p[nrs].p1=temp->p2; + tempHitList->p[nrs].p2=temp->p0; + break; + + case 3: + tempHitList->p[nrs].p0=temp->p0; + tempHitList->p[nrs].p1=temp->p1; + tempHitList->p[nrs].p2=temp->p2; + break; + } + tempHitList->t[nrs]=temp-ilist; // (temp-ilist) is actually the triangle-number + tempHitList->mask[nrs]=mask; // mask is for later + if(++tempHitList->hits==3)temp=tempEnd; // break while-loop if we have 3 hits + } + temp++; + } + } + // Next: + // Start with trying to make a strip of all triangles with 1 hit as a starting point, + // then 2 , finally 3. + // That's all. + for(trynr=1;trynr<=3;trynr++) + { + for(p=0;p=10) // while found a triangle + {for (k=0;k1)strip_length[(*nr_strips)++]=i+2; //actual strip-length (in indices) is traingles +2 + } } } + // Done. + // Now keep all triangles that aren't stripped in original triangle-list and return + // remaining number of triangles in original triangle-list. + i=0; + for(p=0;p +#endif + +#include + +#include +#include // for atoi() atof() +#include + +#include +#include STL_STRING + +#include // plib include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "convex_hull.hxx" +#include "point2d.hxx" +#include "runway.hxx" +#include "scenery_version.hxx" + + +typedef vector < int_list > group_list; +typedef group_list::iterator group_list_iterator; +typedef group_list::const_iterator const_group_list_iterator; + + +#if 0 +// calculate distance in meters between two lat/lon points +static double gc_dist( Point3D p1, Point3D p2 ) { + Point3D r1( p1.x() * DEG_TO_RAD, p1.y() * DEG_TO_RAD, 0 ); + Point3D r2( p2.x() * DEG_TO_RAD, p2.y() * DEG_TO_RAD, 0 ); + + // d=2*asin(sqrt((sin((lat1-lat2)/2))^2 + + // cos(lat1)*cos(lat2)*(sin((lon1-lon2)/2))^2)) + double tmp1 = sin( (r1.y() - r2.y()) / 2.0 ); + double tmp2 = sin( (r1.x() - r2.x()) / 2.0 ); + + // d=2*asin(sqrt((tmp1)^2 + cos(lat1)*cos(lat2)*(tmp2)^2)) + double clat1 = cos( r1.y() ); + double clat2 = cos( r1.y() ); + + // d=2*asin(sqrt(tmp1*tmp1 + clat1*clat2*tmp2*tmp2)) + double tmp3 = sqrt(tmp1*tmp1 + clat1*clat2*tmp2*tmp2); + + // d=2*asin(tmp3) + double d_rad = 2 * asin( tmp3 ); + // cout << " dist (rad) = " << d_rad << endl; + + double d_nm = d_rad * RAD_TO_NM; + // cout << " dist (nm) = " << d_nm << endl; + + double d_m = d_nm * NM_TO_METER; + // cout << " dist (m) = " << d_m << endl; + + return d_m; +} + + +// calculate true course between two points given the distance in meters +static double gc_course( Point3D p1, Point3D p2, double d_m ) { + double lon1 = p1.x() * DEG_TO_RAD; + double lon2 = p2.x() * DEG_TO_RAD; + double lat1 = p1.y() * DEG_TO_RAD; + double lat2 = p2.y() * DEG_TO_RAD; + + double d_rad = d_m * METER_TO_NM * NM_TO_RAD; + + double tc1; + + if ( cos(lat1) < FG_EPSILON) { + if ( lat1 > 0.0 ) { + tc1 = FG_PI; // starting from N pole + } else { + tc1 = 0.0; // starting from S pole + } + } + + // For starting points other than the poles: + + if ( sin(lon2 - lon1) < 0.0 ) { + tc1 = acos( (sin(lat2)-sin(lat1)*cos(d_rad))/(sin(d_rad)*cos(lat1))); + } else { + tc1 = FG_2PI - + acos((sin(lat2)-sin(lat1)*cos(d_rad))/(sin(d_rad)*cos(lat1))); + } + + return tc1; +} +#endif + + +// calculate texture coordinates for a 1/2 runway. Returns a mirror +// polygon to the runway, except each point is the texture coordinate +// of the corresponding point in the original polygon. +static FGPolygon rwy_calc_tex_coords( const FGRunway& rwy, + double hdg_offset, + const FGPolygon& in_poly ) +{ + FGPolygon result; + result.erase(); + double length = rwy.length * FEET_TO_METER; + double width = rwy.width * FEET_TO_METER; + + Point3D center( rwy.lon, rwy.lat, 0 ); + cout << "runway heading = " << rwy.heading << endl; + double angle = rwy.heading + hdg_offset; + Point3D p, tp; + double x, y, tx, ty; + + for ( int i = 0; i < in_poly.contours(); ++i ) { + for ( int j = 0; j < in_poly.contour_size( i ); ++j ) { + p = in_poly.get_pt( i, j ); + + // dist = gc_dist( center, p ); + // course = gc_course( center, p, dist ) + angle; + + // given alt, lat1, lon1, lat2, lon2, calculate starting + // and ending az1, az2 and distance (s). Lat, lon, and + // azimuth are in degrees. distance in meters + double az1, az2, dist; + geo_inverse_wgs_84( 0, center.y(), center.x(), p.y(), p.x(), + &az1, &az2, &dist ); + + cout << "basic course = " << az1 << endl; + double course = az1 + angle; + cout << "course = " << course << endl; + while ( course < -360 ) { course += 360; } + while ( course > 360 ) { course -= 360; } + // cout << "Dist = " << dist << endl; + // cout << " Course = " << course * 180.0 / FG_PI << endl; + + x = cos( course * DEG_TO_RAD ) * dist; + y = sin( course * DEG_TO_RAD ) * dist; + cout << " x = " << x << " y = " << y << endl; + + tx = x / (length / 2.0); + tx = ((int)(tx * 100)) / 100.0; + if ( tx < -1.0 ) { tx = -1.0; } + if ( tx > 1.0 ) { tx = 1.0; } + + ty = (y + (width / 2.0)) / width; + ty = ((int)(ty * 100)) / 100.0; + if ( ty < -1.0 ) { ty = -1.0; } + if ( ty > 1.0 ) { ty = 1.0; } + + tp = Point3D( tx, ty, 0 ); + cout << " (" << tx << ", " << ty << ")" << endl; + + result.add_node( i, tp ); + } + } + + return result; +} + + +#if 0 +// Find a the specified point in the polygon and set the contour/index +// values for it. Returns true if a match found, false otherwise +static bool find_in_polygon( const Point3D p, const FGPolygon poly, + int *contour, int *index ) { + *contour = *index = -1; + Point3D tmp; + + for ( int i = 0; i < poly.contours(); ++i ) { + for ( int j = 0; j < poly.contour_size( i ); ++j ) { + tmp = poly.get_pt( i, j ); + if ( tmp == p ) { + *contour = i; + *index = j; + return true; + } + } + } + + return false; +} +#endif + + +#if 0 +// Add points to keep line segment lengths under 1000' +static FGPolygon add_points( const FGPolygon& in_poly ) { + FGPolygon result; + result.erase(); + + for ( int i = 0; i < in_poly.contours(); ++i ) { + for ( int j = 0; j < in_poly.contour_size( i ) - 1; ++j ) { + gc_dist( in_poly.get_pt( i, j ), + in_poly.get_pt( i, j+1 ) ); + } + gc_dist( in_poly.get_pt( i, in_poly.contour_size( i ) - 1 ), + in_poly.get_pt( i, 0 ) ); + } + + return result; +} +#endif + + +// Divide segment if there are other existing points on it, return the +// new polygon +void add_intermediate_nodes( int contour, const Point3D& start, + const Point3D& end, const FGTriNodes& tmp_nodes, + FGPolygon *result ) +// FGPolygon add_extra( const point_list& nodes, int n1, int n2 ) +{ + bool found_extra = false; + int extra_index = 0; + int counter; + double m, m1, b, b1, y_err, x_err, y_err_min, x_err_min; + const_point_list_iterator current, last; + point_list nodes = tmp_nodes.get_node_list(); + + Point3D p0 = start; + Point3D p1 = end; + + cout << " add_intermediate_nodes()" << endl; + + double xdist = fabs(p0.x() - p1.x()); + double ydist = fabs(p0.y() - p1.y()); + cout << "xdist = " << xdist << " ydist = " << ydist << endl; + x_err_min = xdist + 1.0; + y_err_min = ydist + 1.0; + + if ( xdist > ydist ) { + cout << "use y = mx + b" << endl; + + // sort these in a sensible 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 ) { + cout << counter << endl; + + if ( (current->x() > (p0.x() + FG_EPSILON)) + && (current->x() < (p1.x() - FG_EPSILON)) ) { + + cout << "found a potential candidate " << *current << endl; + y_err = fabs(current->y() - (m * current->x() + b)); + cout << "y_err = " << y_err << endl; + + if ( y_err < FG_PROXIMITY_EPSILON ) { + cout << "FOUND EXTRA SEGMENT NODE (Y)" << endl; + cout << p0 << " < " << *current << " < " + << p1 << endl; + found_extra = true; + if ( y_err < y_err_min ) { + extra_index = counter; + y_err_min = y_err; + } + } + } + ++counter; + } + } else { + cout << "use x = m1 * y + b1" << endl; + + // sort these in a sensible order + if ( p0.y() > p1.y() ) { + Point3D tmp = p0; + p0 = p1; + p1 = tmp; + } + + m1 = (p0.x() - p1.x()) / (p0.y() - p1.y()); + b1 = p1.x() - m1 * p1.y(); + + // bool temp = true; + // if ( temp ) { + cout << " m1 = " << m1 << " b1 = " << b1 << endl; + // } + + // cout << " should = 0 = " << fabs(p0.x() - (m1 * p0.y() + b1)) << endl;; + // cout << " should = 0 = " << fabs(p1.x() - (m1 * p1.y() + b1)) << endl;; + + current = nodes.begin(); + last = nodes.end(); + counter = 0; + for ( ; current != last; ++current ) { + if ( (current->y() > (p0.y() + FG_EPSILON)) + && (current->y() < (p1.y() - FG_EPSILON)) ) { + + cout << "found a potential candidate " << *current << endl; + + x_err = fabs(current->x() - (m1 * current->y() + b1)); + cout << "x_err = " << x_err << endl; + + // if ( temp ) { + // cout << " (" << counter << ") x_err = " << x_err << endl; + // } + + if ( x_err < FG_PROXIMITY_EPSILON ) { + cout << "FOUND EXTRA SEGMENT NODE (X)" << endl; + cout << p0 << " < " << *current << " < " + << p1 << endl; + found_extra = true; + if ( x_err < x_err_min ) { + extra_index = counter; + x_err_min = x_err; + } + } + } + ++counter; + } + } + + if ( found_extra ) { + // recurse with two sub segments + // cout << "dividing " << s.get_n1() << " " << extra_index + // << " " << s.get_n2() << endl; + add_intermediate_nodes( contour, p0, nodes[extra_index], tmp_nodes, + result ); + + result->add_node( contour, nodes[extra_index] ); + + add_intermediate_nodes( contour, nodes[extra_index], p1, tmp_nodes, + result ); + } else { + // this segment does not need to be divided + } +} + + +// Search each segment for additional vertex points that may have been +// created elsewhere that lie on the segment and split it there to +// avoid "T" intersections. + +static FGPolygon add_nodes_to_poly( const FGPolygon& poly, + const FGTriNodes& tmp_nodes ) { + FGPolygon result; + Point3D p0, p1; + + cout << "add_nodes_to_poly" << endl; + + for ( int i = 0; i < poly.contours(); ++i ) { + for ( int j = 0; j < poly.contour_size(i) - 1; ++j ) { + p0 = poly.get_pt( i, j ); + p1 = poly.get_pt( i, j + 1 ); + + // add start of segment + result.add_node( i, p0 ); + + // add intermediate points + add_intermediate_nodes( i, p0, p1, tmp_nodes, &result ); + + // end of segment is beginning of next segment + } + p0 = poly.get_pt( i, poly.contour_size(i) - 1 ); + p1 = poly.get_pt( i, 0 ); + + // add start of segment + result.add_node( i, p0 ); + + // add intermediate points + add_intermediate_nodes( i, p0, p1, tmp_nodes, &result ); + + // end of segment is beginning of next segment + result.add_node( i, p1 ); + } + + return result; +} + + +#if 0 +// print polygon +static void print_poly( const FGPolygon& poly ) { + for ( int i = 0; i < poly.contours(); ++i ) { + cout << "contour " << i << endl; + for ( int j = 0; j < poly.contour_size( i ); ++j ) { + cout << " " << poly.get_pt( i, j ) << endl; + } + } +} +#endif + + + +// calculate the center of a list of points, by taking the halfway +// point between the min and max points. +Point3D calc_center( point_list& wgs84_nodes ) { + Point3D p, min, max; + + if ( wgs84_nodes.size() ) { + min = max = wgs84_nodes[0]; + } else { + min = max = Point3D( 0 ); + } + + for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) { + p = wgs84_nodes[i]; + + if ( p.x() < min.x() ) { min.setx( p.x() ); } + if ( p.y() < min.y() ) { min.sety( p.y() ); } + if ( p.z() < min.z() ) { min.setz( p.z() ); } + + if ( p.x() > max.x() ) { max.setx( p.x() ); } + if ( p.y() > max.y() ) { max.sety( p.y() ); } + if ( p.z() > max.z() ) { max.setz( p.z() ); } + } + + return ( min + max ) / 2.0; +} + +// calculate the global bounding sphere. Center is the center of the +// tile and zero elevation +double calc_bounding_radius( Point3D center, point_list& wgs84_nodes ) { + double dist_squared; + double radius_squared = 0; + + for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) { + dist_squared = center.distance3Dsquared( wgs84_nodes[i] ); + if ( dist_squared > radius_squared ) { + radius_squared = dist_squared; + } + } + + return sqrt(radius_squared); +} + + +// fix node elevations +point_list calc_elevations( const string& root, const point_list& geod_nodes ) { + bool done = false; + point_list result = geod_nodes; + int i; + FGArray array; + + // set all elevations to -9999 + for ( i = 0; i < (int)result.size(); ++i ) { + result[i].setz( -9999.0 ); + } + + while ( !done ) { + // find first node with -9999 elevation + i = 0; + while ( result[i].z() > -9000 ) { ++i; } + + if ( i < (int)result.size() ) { + FGBucket b( result[i].x(), result[i].y() ); + string base = b.gen_base_path(); + + // try 3 arcsec dems first + string dem_path = root + "/DEM-3/" + base + + "/" + b.gen_index_str() + ".dem"; + cout << "dem_path = " << dem_path << endl; + + if ( ! array.open(dem_path) ) { + cout << "ERROR: cannot open 3 arcsec file " << dem_path << endl; + cout << "trying 30 arcsec file" << endl; + + // try 30 arcsec dem + dem_path = root + "/DEM-30/" + base + + "/" + b.gen_index_str() + ".dem"; + cout << "dem_path = " << dem_path << endl; + if ( ! array.open(dem_path) ) { + cout << "ERROR: cannot open 3 arcsec file " + << dem_path << endl; + } + } + array.parse( b ); + + // update all the non-updated elevations that are inside + // this dem file + double elev; + done = true; + for ( int j = 0; j < (int)result.size(); ++j ) { + if ( result[j].z() < -9000 ) { + done = false; + cout << "interpolating for " << result[j] << endl; + elev = array.interpolate_altitude( result[j].x() * 3600.0, + result[j].y() * 3600.0 ); + if ( elev > -9000 ) { + result[j].setz( elev ); + } + } + } + array.close(); + } else { + done = true; + } + } + + + return result; +} + + +// write out the structures to a file. We assume that the groups come +// to us sorted by material property. If not, things don't break, but +// the result won't be as optimal. +void write( const string& base, const FGBucket& b, const string& name, + Point3D gbs_center, double gbs_radius, + const point_list& wgs84_nodes, const point_list& normals, + const point_list& texcoords, + const group_list& strips_v, const group_list& strips_tc, + const string_list& strip_materials, + const group_list& fans_v, const group_list& fans_tc, + const string_list& fan_materials ) +{ + Point3D p; + + string dir = base + "/" + b.gen_base_path(); + string command = "mkdir -p " + dir; + system(command.c_str()); + + // string file = dir + "/" + b.gen_index_str(); + string file = dir + "/" + name; + cout << "Output file = " << file << endl; + + FILE *fp; + if ( (fp = fopen( file.c_str(), "w" )) == NULL ) { + cout << "ERROR: opening " << file << " for writing!" << endl; + exit(-1); + } + + cout << "strip size = " << strips_v.size() << " strip_materials = " + << strip_materials.size() << endl; + cout << "points = " << wgs84_nodes.size() << endl; + cout << "tex coords = " << texcoords.size() << endl; + // write headers + fprintf(fp, "# FGFS Scenery\n"); + fprintf(fp, "# 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"); + + // dump vertex list + fprintf(fp, "# vertex list\n"); + for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) { + p = wgs84_nodes[i] - gbs_center; + + fprintf(fp, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() ); + } + fprintf(fp, "\n"); + + fprintf(fp, "# vertex normal list\n"); + for ( int i = 0; i < (int)normals.size(); ++i ) { + p = normals[i]; + fprintf(fp, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() ); + } + fprintf(fp, "\n"); + + // dump texture coordinates + fprintf(fp, "# texture coordinate list\n"); + for ( int i = 0; i < (int)texcoords.size(); ++i ) { + p = texcoords[i]; + fprintf(fp, "vt %.5f %.5f\n", p.x(), p.y() ); + } + fprintf(fp, "\n"); + + // dump triangle groups + fprintf(fp, "# triangle groups\n"); + + int start = 0; + int end = 1; + string material; + while ( start < (int)strip_materials.size() ) { + // find next group + material = strip_materials[start]; + while ( (end < (int)strip_materials.size()) && + (material == strip_materials[end]) ) + { + // cout << "end = " << end << endl; + end++; + } + // cout << "group = " << start << " to " << end - 1 << endl; + + // make a list of points for the group + point_list group_nodes; + group_nodes.clear(); + Point3D bs_center; + double bs_radius; + for ( int i = start; i < end; ++i ) { + for ( int j = 0; j < (int)strips_v[i].size(); ++j ) { + group_nodes.push_back( wgs84_nodes[ strips_v[i][j] ] ); + bs_center = calc_center( group_nodes ); + bs_radius = calc_bounding_radius( bs_center, group_nodes ); + } + } + + // write group headers + fprintf(fp, "\n"); + fprintf(fp, "# usemtl %s\n", material.c_str()); + fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n", + bs_center.x(), bs_center.y(), bs_center.z(), bs_radius); + + // write groups + for ( int i = start; i < end; ++i ) { + fprintf(fp, "ts"); + for ( int j = 0; j < (int)strips_v[i].size(); ++j ) { + fprintf(fp, " %d/%d", strips_v[i][j], strips_tc[i][j] ); + } + fprintf(fp, "\n"); + } + + start = end; + end = start + 1; + } + fclose(fp); + + command = "gzip --force --best " + file; + system(command.c_str()); +} + + +// update index +void write_index(const string& base, const FGBucket& b, const string& name) { + string dir = base + "/" + b.gen_base_path(); + string command = "mkdir -p " + dir; + system(command.c_str()); + + string file = dir + "/" + b.gen_index_str() + ".ind"; + // string file = dir + "/" + name; + cout << "Output file = " << file << endl; + + FILE *fp; + if ( (fp = fopen( file.c_str(), "a" )) == NULL ) { + cout << "ERROR: opening " << file << " for writing!" << endl; + exit(-1); + } + + fprintf( fp, "OBJECT %s\n", name.c_str() ); + fclose( fp ); +} + + +void write_boundary( const string& base, const FGBucket& b, + const FGPolygon& bounds, long int p_index ) +{ + Point3D p; + + string dir = base + "/" + b.gen_base_path(); + string command = "mkdir -p " + dir; + system(command.c_str()); + + string file = dir + "/" + b.gen_index_str(); + + char poly_index[256]; + sprintf( poly_index, "%ld", p_index ); + file += "."; + file += poly_index; + + cout << "Output file = " << file << endl; + + FILE *fp; + if ( (fp = fopen( file.c_str(), "w" )) == NULL ) { + cout << "ERROR: opening " << file << " for writing!" << endl; + exit(-1); + } + + fprintf( fp, "Hole\n" ); + + fprintf( fp, "%d\n", bounds.contours() ); + for ( int i = 0; i < bounds.contours(); ++i ) { + fprintf( fp, "%d\n", bounds.contour_size(i) ); + fprintf( fp, "%d\n", bounds.get_hole_flag(i) ); + for ( int j = 0; j < bounds.contour_size(i); ++j ) { + p = bounds.get_pt( i, j ); + fprintf( fp, "%.15f %.15f\n", p.x(), p.y() ); + } + } + fclose( fp ); +} + + +// strip trailing spaces +static void my_chomp( string& str ) { + cout << "my_chomp()" << endl; + cout << "'" << str.substr( str.length() - 1, 1 ) << "'" << endl; + while ( str.substr( str.length() - 1, 1 ) == " " ) { + str = str.substr( 0, str.length() - 1 ); + cout << "'" << str.substr( str.length() - 1, 1 ) << "'" << endl; + } +} + + +// build 3d airport +void build_airport( string airport_raw, string_list& runways_raw, + const string& root ) { + + poly_list rwy_nodes, rwy_strips, rwy_txs; + FGPolygon runway, runway_a, runway_b, result, result_a, result_b; + FGPolygon base; + point_list apt_pts; + Point3D p; + + FGPolygon accum; + accum.erase(); + + // parse main airport information + double apt_lon, apt_lat; + int elev; + + cout << airport_raw << endl; + string apt_type = airport_raw.substr(0, 1); + string apt_code = airport_raw.substr(2, 4); my_chomp( apt_code ); + string apt_lat_str = airport_raw.substr(7, 10); + apt_lat = atof( apt_lat_str.c_str() ); + string apt_lon_str = airport_raw.substr(18, 11); + apt_lon = atof( apt_lon_str.c_str() ); + string apt_elev = airport_raw.substr(30, 5); + elev = atoi( apt_elev.c_str() ); + string apt_use = airport_raw.substr(36, 1); + string apt_twr = airport_raw.substr(37, 1); + string apt_bldg = airport_raw.substr(38, 1); + string apt_name = airport_raw.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; + */ + + FGBucket b( apt_lon, apt_lat ); + Point3D center_geod( b.get_center_lon() * DEG_TO_RAD, + b.get_center_lat() * DEG_TO_RAD, 0 ); + Point3D gbs_center = fgGeodToCart( center_geod ); + cout << b.gen_base_path() << "/" << b.gen_index_str() << endl; + + // Ignore any seaplane bases + if ( apt_type == "S" ) { + return; + } + + // parse runways and generate the vertex list + runway_list runways; + runways.clear(); + string rwy_str; + + for (int i = 0; i < (int)runways_raw.size(); ++i ) { + rwy_str = runways_raw[i]; + + FGRunway rwy; + + cout << rwy_str << endl; + rwy.rwy_no = rwy_str.substr(2, 4); + + string rwy_lat = rwy_str.substr(6, 10); + rwy.lat = atof( rwy_lat.c_str() ); + + string rwy_lon = rwy_str.substr(17, 11); + rwy.lon = atof( rwy_lon.c_str() ); + + string rwy_hdg = rwy_str.substr(29, 7); + rwy.heading = atof( rwy_hdg.c_str() ); + + string rwy_len = rwy_str.substr(36, 7); + rwy.length = atoi( rwy_len.c_str() ); + + string rwy_width = rwy_str.substr(43, 4); + rwy.width = atoi( rwy_width.c_str() ); + + rwy.surface_flags = rwy_str.substr(47, 4); + rwy.end1_flags = rwy_str.substr(52, 8); + rwy.end2_flags = 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; + */ + + runways.push_back( rwy ); + } + + for ( int i = 0; i < (int)runways.size(); ++i ) { + runway = gen_runway_w_mid( runways[i] ); + + // runway half "a" + runway_a.erase(); + runway_a.add_node( 0, runway.get_pt(0, 0) ); + runway_a.add_node( 0, runway.get_pt(0, 1) ); + runway_a.add_node( 0, runway.get_pt(0, 2) ); + runway_a.add_node( 0, runway.get_pt(0, 5) ); + + // runway half "b" + runway_b.erase(); + runway_b.add_node( 0, runway.get_pt(0, 5) ); + runway_b.add_node( 0, runway.get_pt(0, 2) ); + runway_b.add_node( 0, runway.get_pt(0, 3) ); + runway_b.add_node( 0, runway.get_pt(0, 4) ); + + cout << "raw runway pts (a half)" << endl; + for ( int j = 0; j < runway_a.contour_size( 0 ); ++j ) { + p = runway_a.get_pt(0, j); + cout << " point = " << p << endl; + } + cout << "raw runway pts (b half)" << endl; + for ( int j = 0; j < runway_b.contour_size( 0 ); ++j ) { + p = runway_b.get_pt(0, j); + cout << " point = " << p << endl; + } + + result_a = polygon_diff( runway_a, accum ); + rwy_nodes.push_back( result_a ); + cout << "result_a = " << result_a.contours() << endl; + accum = polygon_union( runway_a, accum ); + + result_b = polygon_diff( runway_b, accum ); + rwy_nodes.push_back( result_b ); + cout << "result_b = " << result_b.contours() << endl; + accum = polygon_union( runway_b, accum ); + + // print runway points + cout << "clipped runway pts (a)" << endl; + for ( int j = 0; j < result_a.contours(); ++j ) { + for ( int k = 0; k < result_a.contour_size( j ); ++k ) { + p = result_a.get_pt(j, k); + cout << " point = " << p << endl; + } + } + + // print runway points + cout << "clipped runway pts (b)" << endl; + for ( int j = 0; j < result_b.contours(); ++j ) { + for ( int k = 0; k < result_b.contour_size( j ); ++k ) { + p = result_b.get_pt(j, k); + cout << " point = " << p << endl; + } + } + + base = gen_runway_area( runways[i], 1.01, 1.5 ); + + // add base to apt_pts (for convex hull of airport area) + for ( int j = 0; j < base.contour_size( 0 ); ++j ) { + p = base.get_pt(0, j); + // cout << " point = " << p << endl; + apt_pts.push_back( p ); + } + } + + // generate convex hull + FGPolygon hull = convex_hull(apt_pts); + FGPolygon base_nodes = polygon_diff( hull, accum ); + + if ( apt_pts.size() == 0 ) { + cout << "no airport points generated" << endl; + return; + } + + // add segments to polygons to remove any possible "T" + // intersections + FGTriNodes tmp_nodes; + + // build temporary node list + for ( int k = 0; k < (int)rwy_nodes.size(); ++k ) { + for ( int i = 0; i < rwy_nodes[k].contours(); ++i ) { + for ( int j = 0; j < rwy_nodes[k].contour_size( i ); ++j ) { + tmp_nodes.unique_add( rwy_nodes[k].get_pt(i, j) ); + } + } + } + for ( int i = 0; i < base_nodes.contours(); ++i ) { + for ( int j = 0; j < base_nodes.contour_size( i ); ++j ) { + tmp_nodes.unique_add( base_nodes.get_pt(i, j) ); + } + } + + point_list ttt = tmp_nodes.get_node_list(); + for ( int i = 0; i < (int)ttt.size(); ++i ) { + char name[256]; + sprintf(name, "p%d", i ); + FILE *fp = fopen( name, "w" ); + fprintf(fp, "%.8f %.8f\n", ttt[i].x(), ttt[i].y()); + fclose(fp); + } + + for ( int i = 0; i < base_nodes.contours(); ++i ) { + char name[256]; + sprintf(name, "l%d", i ); + FILE *fp = fopen( name, "w" ); + + for ( int j = 0; j < base_nodes.contour_size( i ) - 1; ++j ) { + Point3D p0 = base_nodes.get_pt(i, j); + Point3D p1 = base_nodes.get_pt(i, j + 1); + fprintf(fp, "%.8f %.8f\n", p0.x(), p0.y()); + fprintf(fp, "%.8f %.8f\n", p1.x(), p1.y()); + } + Point3D p0 = base_nodes.get_pt(i, base_nodes.contour_size( i ) - 1); + Point3D p1 = base_nodes.get_pt(i, 0); + fprintf(fp, "%.8f %.8f\n", p0.x(), p0.y()); + fprintf(fp, "%.8f %.8f\n", p1.x(), p1.y()); + fclose(fp); + } + + for ( int k = 0; k < (int)rwy_nodes.size(); ++k ) { + rwy_nodes[k] = add_nodes_to_poly( rwy_nodes[k], tmp_nodes ); + } + base_nodes = add_nodes_to_poly( base_nodes, tmp_nodes ); + + // generate convex hull and strip version + FGPolygon base_strips = polygon_to_tristrip( base_nodes ); + + for ( int i = 0; i < base_strips.contours(); ++i ) { + char name[256]; + sprintf(name, "s%d", i ); + FILE *fp = fopen( name, "w" ); + + for ( int j = 0; j < base_strips.contour_size( i ) - 1; ++j ) { + Point3D p0 = base_strips.get_pt(i, j); + Point3D p1 = base_strips.get_pt(i, j + 1); + fprintf(fp, "%.8f %.8f\n", p0.x(), p0.y()); + fprintf(fp, "%.8f %.8f\n", p1.x(), p1.y()); + } + Point3D p0 = base_strips.get_pt(i, base_strips.contour_size( i ) - 1); + Point3D p1 = base_strips.get_pt(i, 0); + fprintf(fp, "%.8f %.8f\n", p0.x(), p0.y()); + fprintf(fp, "%.8f %.8f\n", p1.x(), p1.y()); + fclose(fp); + } + + // generate strips and texture coordinates + for ( int i = 0; i < (int)runways.size(); ++i ) { + FGPolygon strip_a, strip_b, tc_a, tc_b; + + strip_a = polygon_to_tristrip( rwy_nodes[2 * i] ); + tc_a = rwy_calc_tex_coords( runways[i], 0.0, strip_a ); + rwy_strips.push_back( strip_a ); + rwy_txs.push_back( tc_a ); + + strip_b = polygon_to_tristrip( rwy_nodes[2 * i + 1] ); + tc_b = rwy_calc_tex_coords( runways[i], 180.0, strip_b ); + rwy_strips.push_back( strip_b ); + rwy_txs.push_back( tc_b ); + } + + // + // We should now have the runway polygons all generated with their + // corresponding strips and texture coordinates, and the + // surrounding base area. + // + // Next we need to create the output lists ... vertices, normals, + // texture coordinates, and tri-strips + // + + // traverse the strip list and create ordered node and texture + // coordinate lists + + FGTriNodes nodes, texcoords; + nodes.clear(); + texcoords.clear(); + + group_list strips_v; strips_v.clear(); + group_list strips_tc; strips_tc.clear(); + string_list strip_materials; strip_materials.clear(); + + Point3D tc; + int index; + int_list strip_v; + int_list strip_tc; + + for ( int k = 0; k < (int)rwy_strips.size(); ++k ) { + cout << "strip " << k << endl; + FGPolygon strip_poly = rwy_strips[k]; + for ( int i = 0; i < strip_poly.contours(); ++i ) { + strip_v.clear(); + strip_tc.clear(); + for ( int j = 0; j < strip_poly.contour_size(i); ++j ) { + p = strip_poly.get_pt( i, j ); + index = nodes.unique_add( p ); + strip_v.push_back( index ); + tc = rwy_txs[k].get_pt( i, j ); + index = texcoords.unique_add( tc ); + strip_tc.push_back( index ); + } + strips_v.push_back( strip_v ); + strips_tc.push_back( strip_tc ); + strip_materials.push_back( "Concrete" ); + } + } + + // add base points + point_list base_txs; + int_list base_tc; + for ( int i = 0; i < base_strips.contours(); ++i ) { + strip_v.clear(); + strip_tc.clear(); + for ( int j = 0; j < base_strips.contour_size(i); ++j ) { + p = base_strips.get_pt( i, j ); + index = nodes.unique_add( p ); + strip_v.push_back( index ); + } + strips_v.push_back( strip_v ); + strip_materials.push_back( "Grass" ); + + base_txs.clear(); + base_txs = calc_tex_coords( b, nodes.get_node_list(), strip_v ); + + base_tc.clear(); + for ( int j = 0; j < (int)base_txs.size(); ++j ) { + tc = base_txs[j]; + cout << "base_tc = " << tc << endl; + index = texcoords.simple_add( tc ); + base_tc.push_back( index ); + } + strips_tc.push_back( base_tc ); + } + + // calculate node elevations + point_list geod_nodes = calc_elevations( root, nodes.get_node_list() ); + + // calculate wgs84 mapping of nodes + point_list wgs84_nodes; + for ( int i = 0; i < (int)geod_nodes.size(); ++i ) { + p.setx( geod_nodes[i].x() * DEG_TO_RAD ); + p.sety( geod_nodes[i].y() * DEG_TO_RAD ); + p.setz( geod_nodes[i].z() ); + wgs84_nodes.push_back( fgGeodToCart( p ) ); + } + double gbs_radius = calc_bounding_radius( gbs_center, wgs84_nodes ); + + // calculate normal(s) for this airport + p.setx( rwy_strips[0].get_pt(0, 0).x() * DEG_TO_RAD ); + p.sety( rwy_strips[0].get_pt(0, 0).y() * DEG_TO_RAD ); + p.setz( 0 ); + Point3D tmp = fgGeodToCart( p ); + // cout << "geod = " << p << endl; + // cout << "cart = " << tmp << endl; + + sgdVec3 vn; + sgdSetVec3( vn, tmp.x(), tmp.y(), tmp.z() ); + sgdNormalizeVec3( vn ); + point_list normals; + normals.clear(); + for ( int i = 0; i < (int)nodes.size(); ++i ) { + normals.push_back( Point3D( vn[0], vn[1], vn[2] ) ); + } + + group_list fans_v; fans_v.clear(); + group_list fans_tc; fans_tc.clear(); + string_list fan_materials; + fan_materials.clear(); + + string objpath = root + "/AirportObj"; + string name = apt_code; + + write( objpath, b, name, gbs_center, gbs_radius, + wgs84_nodes, normals, + texcoords.get_node_list(), + strips_v, strips_tc, strip_materials, + fans_v, fans_tc, fan_materials ); + + write_index( objpath, b, name ); + + string holepath = root + "/AirportArea"; + long int poly_index = poly_index_next(); + write_boundary( holepath, b, hull, poly_index ); +} diff --git a/src/Airports/GenAirports/build.hxx b/src/Airports/GenAirports/build.hxx new file mode 100644 index 00000000..0568f429 --- /dev/null +++ b/src/Airports/GenAirports/build.hxx @@ -0,0 +1,43 @@ +// build.hxx -- routines to build polygon model of an airport from the runway +// definition +// +// Written by Curtis Olson, started September 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$ +// + + +#ifndef _BUILD_HXX +#define _BUILD_HXX + + +#include + +#include "point2d.hxx" + + +// build 3d airport +void build_airport( string airport, string_list& runways_raw, + const string& root ); + + + +#endif // _BUILD_HXX + + diff --git a/src/Airports/GenAirports/convex_hull.cxx b/src/Airports/GenAirports/convex_hull.cxx new file mode 100644 index 00000000..42c56d69 --- /dev/null +++ b/src/Airports/GenAirports/convex_hull.cxx @@ -0,0 +1,247 @@ +// 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$ +// + + +#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(Point3D a, Point3D b, Point3D c) { + Point3D u, v; + double udist, vdist, uv_dot, tmp; + + // u . v = ||u|| * ||v|| * cos(theta) + + u.setx( b.x() - a.x() ); + u.sety( b.y() - a.y() ); + udist = sqrt( u.x() * u.x() + u.y() * u.y() ); + // printf("udist = %.6f\n", udist); + + v.setx( b.x() - c.x() ); + v.sety( 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(Point3D Pa, Point3D Pb, Point3D Pc) { + double a1, a2; + + Point3D origin( 0.0 ); + + Point3D a( cos(Pa.y()) * Pa.x(), + sin(Pa.y()) * Pa.x(), 0 ); + + Point3D b( cos(Pb.y()) * Pb.x(), + sin(Pb.y()) * Pb.x(), 0 ); + + Point3D c( cos(Pc.y()) * Pc.x(), + sin(Pc.y()) * Pc.x(), 0 ); + + // 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 +FGPolygon convex_hull( const point_list& input_list ) { + map_iterator map_current, map_next, map_next_next, map_last; + + // list of translated points + point_list trans_list; + + // points sorted by radian degrees + map_container radians_map; + + // will contain the convex hull + FGPolygon con_hull; + + Point3D p, 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 + in_count = input_list.size(); + sum_x = sum_y = 0.0; + + for ( int i = 0; i < in_count; ++i ) { + sum_x += input_list[i].x(); + sum_y += input_list[i].y(); + } + + Point3D average( sum_x / in_count, sum_y / in_count, 0 ); + + // printf("Average center point is %.4f %.4f\n", average.x, average.y); + + // STEP TWO: Translate input points so average is at origin + trans_list.clear(); + + for ( int i = 0; i < in_count; ++i ) { + p = Point3D( input_list[i].x() - average.x(), + input_list[i].y() - average.y(), 0 ); + // printf("%.6f %.6f\n", p.x, p.y); + trans_list.push_back( p ); + } + + // STEP THREE: convert to radians and sort by theta + radians_map.clear(); + + for ( int i = 0; i < in_count; ++i ) { + p = cart_to_polar_2d( trans_list[i] ); + if ( p.x() > radians_map[p.y()] ) { + radians_map[p.y()] = p.x(); + } + } + + /* + // printf("Sorted list\n"); + map_current = radians_map.begin(); + map_last = radians_map.end(); + for ( ; map_current != map_last ; ++map_current ) { + p.setx( (*map_current).first ); + p.sety( (*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 > (int)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.sety( (*map_current).first ); + Pa.setx( (*map_current).second ); + + // get second element + map_next = map_current; + ++map_next; + if ( map_next == radians_map.end() ) { + map_next = radians_map.begin(); + } + Pb.sety( (*map_next).first ); + Pb.setx( (*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.sety( (*map_next_next).first ); + Pc.setx( (*map_next_next).second ); + + // printf("Pa is %.6f %.6f\n", Pa.y(), Pa.x()); + // printf("Pb is %.6f %.6f\n", Pb.y(), Pb.x()); + // printf("Pc is %.6f %.6f\n", Pc.y(), Pc.x()); + + 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(); + map_current = radians_map.begin(); + map_last = radians_map.end(); + for ( ; map_current != map_last ; ++map_current ) { + p.sety( (*map_current).first ); + p.setx( (*map_current).second ); + + result.setx( cos(p.y()) * p.x() + average.x() ); + result.sety( sin(p.y()) * p.x() + average.y() ); + + // printf("%.6f %.6f\n", result.x, result.y); + + con_hull.add_node(0, result); + } + + return con_hull; +} + + diff --git a/src/Airports/GenAirports/convex_hull.hxx b/src/Airports/GenAirports/convex_hull.hxx new file mode 100644 index 00000000..d42c6d11 --- /dev/null +++ b/src/Airports/GenAirports/convex_hull.hxx @@ -0,0 +1,51 @@ +// 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$ +// + + +#ifndef _CONVEX_HULL_HXX +#define _CONVEX_HULL_HXX + + +#include + +#ifdef NEEDNAMESPACESTD +using namespace std; +#endif + +#include + +#include + +#include "point2d.hxx" + + + +// 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 +FGPolygon convex_hull( const point_list& input_list ); + + +#endif // _CONVEX_HULL_HXX + + diff --git a/src/Airports/GenAirports/main.cxx b/src/Airports/GenAirports/main.cxx new file mode 100644 index 00000000..74e48bc3 --- /dev/null +++ b/src/Airports/GenAirports/main.cxx @@ -0,0 +1,351 @@ +// 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$ +// + + +#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 "build.hxx" +#include "convex_hull.hxx" + + +#if 0 +// write out airport data +void write_airport( long int p_index, const FGPolygon& 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 + "/" + 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.contour_size(0) ); + + // hole flag + fprintf( fd, "%d\n", 0 ); // not a hole + + // write contour (polygon) points + for ( int i = 0; i < hull_list.contour_size(0); ++i ) { + fprintf( fd, "%.7f %.7f\n", + hull_list.get_pt(0,i).x(), + hull_list.get_pt(0,i).y() ); + } + + fclose(fd); +} + + +// process and airport + runway list +void process_airport( string airport, list < string > & runways_list, + const string& root ) { + FGPolygon rwy_list, hull; + point_list apt_list; + + // parse main airport information + int elev; + + cout << airport << endl; + string apt_type = airport.substr(0, 1); + string apt_code = airport.substr(2, 4); my_chomp(apt_code); + 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; + */ + + // Ignore any seaplane bases + if ( apt_type == "S" ) { + return; + } + + // parse runways and generate the vertex list + string rwy_str; + double lon, lat, hdg; + int len, width; + + list < string >::iterator last_runway = runways_list.end(); + for ( list < string >::iterator current_runway = runways_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 + for (int i = 0; i < (int)rwy_list.contour_size(0); ++i ) { + apt_list.push_back( rwy_list.get_pt(0,i) ); + } + } + + 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 = convex_hull(apt_list); + + // get next polygon index + long int index = poly_index_next(); + + // find average center, min, and max point of convex hull + Point3D min(200.0), max(-200.0); + double sum_x, sum_y; + int count = hull.contour_size(0); + sum_x = sum_y = 0.0; + for ( int i = 0; i < count; ++i ) { + // printf("return = %.6f %.6f\n", (*current).x, (*current).y); + Point3D p = hull.get_pt( 0, i ); + sum_x += p.x(); + sum_y += p.y(); + + if ( p.x() < min.x() ) { min.setx( p.x() ); } + if ( p.y() < min.y() ) { min.sety( p.y() ); } + if ( p.x() > max.x() ) { max.setx( p.x() ); } + if ( p.y() > max.y() ) { max.sety( p.y() ); } + } + Point3D average( sum_x / count, sum_y / count, 0 ); + + // 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.x(), average.y()); + 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, 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, b_cur, root, true ); + } else { + write_airport( index, hull, b_cur, root, true ); + } + } + } + // string answer; cin >> answer; + } +} +#endif + + +// reads the apt_full file and extracts and processes the individual +// airport records +int main( int argc, char **argv ) { + string_list runways_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 + "/poly_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, runways_list, argv[2]); + build_airport(last_airport, runways_list, argv[2]); + } + + // clear runway list for start of next airport + runways_list.clear(); + + last_airport = airport; + } else if ( line[0] == 'R' ) { + // runway entry + runways_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, runways_list, argv[2]); + build_airport(last_airport, runways_list, argv[2]); + } + + return 0; +} + + diff --git a/src/Airports/GenAirports/point2d.cxx b/src/Airports/GenAirports/point2d.cxx new file mode 100644 index 00000000..ea214366 --- /dev/null +++ b/src/Airports/GenAirports/point2d.cxx @@ -0,0 +1,38 @@ +// 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$ +// + + +#include + +#include "point2d.hxx" + + +// convert a point from cartesian to polar coordinates +Point3D cart_to_polar_2d(const Point3D& in) { + Point3D result( sqrt(in.x() * in.x() + in.y() * in.y()), + atan2(in.y(), in.x()), + 0 ); + return result; +} + + diff --git a/src/Airports/GenAirports/point2d.hxx b/src/Airports/GenAirports/point2d.hxx new file mode 100644 index 00000000..15677c7d --- /dev/null +++ b/src/Airports/GenAirports/point2d.hxx @@ -0,0 +1,39 @@ +// 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$ +// + + +#ifndef _POINT2D_HXX +#define _POINT2D_HXX + + +#include +#include + + +// convert a point from cartesian to polar coordinates +Point3D cart_to_polar_2d(const Point3D& in); + + +#endif // _POINT2D_HXX + + diff --git a/src/Airports/GenAirports/runway.cxx b/src/Airports/GenAirports/runway.cxx new file mode 100644 index 00000000..da5bdc98 --- /dev/null +++ b/src/Airports/GenAirports/runway.cxx @@ -0,0 +1,201 @@ +// 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$ +// + + +#include +#include + +#include +#include +#include + +#include "runway.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) ); +} + + +FGPolygon batch_cart_to_polar_2d( const FGPolygon& in_list ) { + FGPolygon out_list; + Point3D p; + + out_list.erase(); + for ( int i = 0; i < (int)in_list.contour_size( 0 ); ++i ) { + p = cart_to_polar_2d( in_list.get_pt( 0, i ) ); + out_list.add_node( 0, 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. +FGPolygon gen_area(Point3D origin, double angle, const FGPolygon& cart_list) { + FGPolygon rad_list; + FGPolygon result_list; + Point3D p; + + // convert to polar coordinates + rad_list = batch_cart_to_polar_2d(cart_list); + + // display points + // cout << "converted to polar" << endl; + // for ( int i = 0; i < rad_list.contour_size( 0 ); ++i ) { + // cout << " " << rad_list.get_pt(0, i) << endl; + // } + + // rotate by specified angle + // cout << "Rotating points by " << angle << endl; + for ( int i = 0; i < rad_list.contour_size( 0 ); ++i) { + p = rad_list.get_pt( 0, i ); + double theta = p.y() - angle; + while ( theta < FG_2PI ) { + theta += FG_2PI; + } + p.sety( theta ); + rad_list.set_pt( 0, i, p ); + // cout << " " << p << endl; + } + + // find actual lon,lat of coordinates + // cout << "convert to lon, lat relative to " << origin << endl; + for ( int i = 0; i < (int)rad_list.contour_size( 0 ); ++i ) { + // p = calc_lon_lat(origin_rad, rad_list.get_pt(0, i) ); + + double lat2, lon2, az2; + geo_direct_wgs_84 ( 0, origin.y(), origin.x(), + rad_list.get_pt(0, i).y() * RAD_TO_DEG, + rad_list.get_pt(0, i).x(), + &lat2, &lon2, &az2 ); + + // convert from radians to degress + p.setx( lon2 ); + p.sety( lat2 ); + // cout << " " << p << endl; + result_list.add_node( 0, p ); + } + + return result_list; +} + + +// generate an area for a runway +FGPolygon gen_runway_area( const FGRunway& runway, + double len_scale = 1.0, + double width_scale = 1.0 ) { + + FGPolygon result_list; + FGPolygon tmp_list; + + double l, w; + + /* + printf("runway: lon = %.2f lat = %.2f hdg = %.2f len = %.2f width = %.2f\n", + lon, lat, heading, length, width); + */ + + Point3D origin(runway.lon, runway.lat, 0); + l = runway.length * len_scale * FEET_TO_METER / 2.0; + w = runway.width * width_scale * FEET_TO_METER / 2.0; + + // generate untransformed runway area vertices + tmp_list.add_node( 0, Point3D( l, w, 0 ) ); + tmp_list.add_node( 0, Point3D( l, -w, 0 ) ); + tmp_list.add_node( 0, Point3D( -l, -w, 0 ) ); + tmp_list.add_node( 0, Point3D( -l, w, 0 ) ); + + // display points + // cout << "Untransformed, unrotated runway" << endl; + // for ( int i = 0; i < tmp_list.contour_size( 0 ); ++i ) { + // cout << " " << tmp_list.get_pt(0, i) << endl; + // } + + // rotate, transform, and convert points to lon, lat in degrees + result_list = gen_area(origin, runway.heading * DEG_TO_RAD, tmp_list); + + // display points + // cout << "Results in radians." << endl; + // for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) { + // cout << " " << result_list.get_pt(0, i) << endl; + // } + + return result_list; +} + + +// generate an area for a runway and include midpoints +FGPolygon gen_runway_w_mid( const FGRunway& runway, + double len_scale = 1.0, + double width_scale = 1.0 ) { + FGPolygon result_list; + FGPolygon tmp_list; + + double l, w; + + /* + printf("runway: lon = %.2f lat = %.2f hdg = %.2f len = %.2f width = %.2f\n", + lon, lat, heading, length, width); + */ + + Point3D origin(runway.lon, runway.lat, 0); + l = runway.length * len_scale * FEET_TO_METER / 2.0; + w = runway.width * width_scale * FEET_TO_METER / 2.0; + + // generate untransformed runway area vertices + tmp_list.add_node( 0, Point3D( l, w, 0 ) ); + tmp_list.add_node( 0, Point3D( l, -w, 0 ) ); + tmp_list.add_node( 0, Point3D( 0, -w, 0 ) ); + tmp_list.add_node( 0, Point3D( -l, -w, 0 ) ); + tmp_list.add_node( 0, Point3D( -l, w, 0 ) ); + tmp_list.add_node( 0, Point3D( 0, w, 0 ) ); + + // display points + // cout << "Untransformed, unrotated runway" << endl; + // for ( int i = 0; i < tmp_list.contour_size( 0 ); ++i ) { + // cout << " " << tmp_list.get_pt(0, i) << endl; + // } + + // rotate, transform, and convert points to lon, lat in degrees + result_list = gen_area(origin, runway.heading * DEG_TO_RAD, tmp_list); + + // display points + // cout << "Results in radians." << endl; + // for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) { + // cout << " " << result_list.get_pt(0, i) << endl; + // } + + return result_list; +} diff --git a/src/Airports/GenAirports/runway.hxx b/src/Airports/GenAirports/runway.hxx new file mode 100644 index 00000000..6b743a9a --- /dev/null +++ b/src/Airports/GenAirports/runway.hxx @@ -0,0 +1,68 @@ +// runway.hxx -- class to store runway info +// +// Written by Curtis Olson, started November 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$ +// + + +#ifndef _RUNWAY_HXX +#define _RUNWAY_HXX + + +#include +#include + +#include + +#include + + +struct FGRunway { + string rwy_no; + + double lon; + double lat; + double heading; + double length; + double width; + + string surface_flags; + string end1_flags; + string end2_flags; +}; + + +typedef vector < FGRunway > runway_list; +typedef runway_list::iterator runway_list_iterator; +typedef runway_list::const_iterator const_runway_list_iterator; + + +// generate an area for a runway (return result points in degrees) +FGPolygon gen_runway_area( const FGRunway& runway, + double len_scale = 1.0, + double width_scale = 1.0 ); + +// generate an area for half a runway +FGPolygon gen_runway_w_mid( const FGRunway& runway, + double len_scale = 1.0, + double width_scale = 1.0 ); + + +#endif // _RUNWAY_HXX diff --git a/src/BuildTiles/Clipper/Makefile.am b/src/BuildTiles/Clipper/Makefile.am new file mode 100644 index 00000000..ec60303c --- /dev/null +++ b/src/BuildTiles/Clipper/Makefile.am @@ -0,0 +1,17 @@ +noinst_LIBRARIES = libClipper.a + +libClipper_a_SOURCES = clipper.cxx clipper.hxx + +noinst_PROGRAMS = testclipper + +testclipper_SOURCES = testclipper.cxx + +testclipper_LDADD = \ + $(top_builddir)/Construct/Clipper/libClipper.a \ + $(top_builddir)/Construct/Triangulate/libTriangulate.a \ + $(top_builddir)/Lib/Polygon/libPolygon.a \ + $(top_builddir)/Lib/poly2tri/libpoly2tri.a \ + -lsgdebug -lsgmisc -lz -lgpc + +INCLUDES += -I$(top_builddir) \ + -I$(top_builddir)/Lib diff --git a/src/BuildTiles/Clipper/clipper.cxx b/src/BuildTiles/Clipper/clipper.cxx new file mode 100644 index 00000000..203327ca --- /dev/null +++ b/src/BuildTiles/Clipper/clipper.cxx @@ -0,0 +1,440 @@ +// 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$ + + + +#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];; + + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + polys_in.polys[i].clear(); + } + + 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; + int hole_flag; + 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; + FGPolygon poly; + + Point3D p; + 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; + + poly.erase(); + + 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 >> hole_flag; + + in >> startx; + in >> starty; + p = Point3D(startx, starty, 0.0); + poly.add_node( i, p ); + FG_LOG( FG_CLIPPER, FG_BULK, "0 = " + << startx << ", " << starty ); + + for ( j = 1; j < count - 1; ++j ) { + in >> x; + in >> y; + p = Point3D( x, y, 0.0 ); + poly.add_node( i, p ); + FG_LOG( FG_CLIPPER, FG_BULK, j << " = " << x << ", " << y ); + } + + in >> lastx; + in >> lasty; + + if ( (fabs(startx - lastx) < FG_EPSILON) + && (fabs(starty - lasty) < FG_EPSILON) ) { + // last point same as first, discard + } else { + p = Point3D( lastx, lasty, 0.0 ); + poly.add_node( i, p ); + FG_LOG( FG_CLIPPER, FG_BULK, count - 1 << " = " + << lastx << ", " << lasty ); + } + + // gpc_add_contour( poly, &v_list, hole_flag ); + } + + in >> skipcomment; + } + + int area = (int)poly_type; + + // if ( area == OceanArea ) { + // TEST - Ignore + // } else + + 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; +} + + +// remove any slivers from in polygon and move them to out polygon. +void FGClipper::move_slivers( FGPolygon& in, FGPolygon& out ) { + cout << "Begin move slivers" << endl; + // traverse each contour of the polygon and attempt to identify + // likely slivers + + out.erase(); + + double angle_cutoff = 10.0 * DEG_TO_RAD; + double area_cutoff = 0.000008; + double min_angle; + double area; + + point_list contour; + int hole_flag; + + // process contours in reverse order so deleting a contour doesn't + // foul up our sequence + for ( int i = in.contours() - 1; i >= 0; --i ) { + cout << "contour " << i << endl; + + min_angle = in.minangle_contour( i ); + area = in.area_contour( i ); + + cout << " min_angle (rad) = " + << min_angle << endl; + cout << " min_angle (deg) = " + << min_angle * 180.0 / FG_PI << endl; + cout << " area = " << area << endl; + + if ( ((min_angle < angle_cutoff) && (area < area_cutoff)) || + ( area < area_cutoff / 10.0) ) + { + cout << " WE THINK IT'S A SLIVER!" << endl; + + // check if this is a hole + hole_flag = in.get_hole_flag( i ); + + if ( hole_flag ) { + // just delete/eliminate/remove sliver holes + cout << "just deleting a sliver hole" << endl; + in.delete_contour( i ); + } else { + // move sliver contour to out polygon + contour = in.get_contour( i ); + in.delete_contour( i ); + out.add_contour( contour, hole_flag ); + } + } + } +} + + +// for each sliver contour, see if a union with another polygon yields +// a polygon with no increased contours (i.e. the sliver is adjacent +// and can be merged.) If so, replace the clipped polygon with the +// new polygon that has the sliver merged in. +void FGClipper::merge_slivers( FGPolyList& clipped, FGPolygon& slivers ) { + FGPolygon poly, result, sliver; + point_list contour; + int original_contours, result_contours; + bool done; + + for ( int i = 0; i < slivers.contours(); ++i ) { + cout << "Merging sliver = " << i << endl; + + // make the sliver polygon + contour = slivers.get_contour( i ); + sliver.erase(); + sliver.add_contour( contour, 0 ); + done = false; + + for ( int area = 0; area < FG_MAX_AREA_TYPES && !done; ++area ) { + cout << " testing area = " << area << " with " + << clipped.polys[area].size() << " polys" << endl; + for ( int j = 0; + j < (int)clipped.polys[area].size() && !done; + ++j ) + { + cout << " polygon = " << j << endl; + + poly = clipped.polys[area][j]; + original_contours = poly.contours(); + result = polygon_union( poly, sliver ); + result_contours = result.contours(); + + if ( original_contours == result_contours ) { + cout << " FOUND a poly to merge the sliver with" << endl; + clipped.polys[area][j] = result; + done = true; + // poly.write("orig"); + // sliver.write("sliver"); + // result.write("result"); + // cout << "press return: "; + // string input; + // cin >> input; + } else { + cout << " poly not a match" << endl; + cout << " original = " << original_contours + << " result = " << result_contours << endl; + cout << " sliver = " << endl; + for ( int k = 0; k < (int)contour.size(); ++k ) { + cout << " " << contour[k].x() << ", " + << contour[k].y() << endl; + } + } + } + } + if ( !done ) { + cout << "no suitable polys found for sliver merge" << endl; + } + } +} + + +// Do actually clipping work +bool FGClipper::clip_all(const point2d& min, const point2d& max) { + FGPolygon accum, result_union, tmp; + FGPolygon result_diff, slivers, remains; + // gpcpoly_iterator current, last; + + FG_LOG( FG_CLIPPER, FG_INFO, "Running master clipper" ); + + accum.erase(); + + cout << " (" << min.x << "," << min.y << ") (" + << max.x << "," << max.y << ")" << endl; + + // set up clipping tile + polys_in.safety_base.erase(); + polys_in.safety_base.add_node( 0, Point3D(min.x, min.y, 0.0) ); + polys_in.safety_base.add_node( 0, Point3D(max.x, min.y, 0.0) ); + polys_in.safety_base.add_node( 0, Point3D(max.x, max.y, 0.0) ); + polys_in.safety_base.add_node( 0, Point3D(min.x, max.y, 0.0) ); + + // int count = 0; + // process polygons in priority order + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + cout << "num polys of type (" << i << ") = " + << polys_in.polys[i].size() << endl; + // current = polys_in.polys[i].begin(); + // last = polys_in.polys[i].end(); + // for ( ; current != last; ++current ) { + for( int j = 0; j < (int)polys_in.polys[i].size(); ++j ) { + FGPolygon current = polys_in.polys[i][j]; + FG_LOG( FG_CLIPPER, FG_DEBUG, get_area_name( (AreaType)i ) + << " = " << current.contours() ); + +#ifdef EXTRA_SAFETY_CLIP + // clip to base tile + tmp = polygon_int( current, polys_in.safety_base ); +#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.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, 1, &tmp); + // fclose(ofp); + + // ofp= fopen("accum-debug", "w"); + // gpc_write_polygon(ofp, 1, &accum); + // fclose(ofp); + + result_diff = polygon_diff( tmp, accum); + result_union = polygon_union( tmp, accum); + } + + /* + 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.contours() > 0 ) { + // move slivers from result_diff polygon to slivers polygon + move_slivers(result_diff, slivers); + cout << " After sliver move:" << endl; + cout << " result_diff = " << result_diff.contours() << endl; + cout << " slivers = " << slivers.contours() << endl; + + // merge any slivers with previously clipped + // neighboring polygons + if ( slivers.contours() > 0 ) { + merge_slivers(polys_clipped, slivers); + } + + // add the sliverless result polygon (from after the + // move_slivers) to the clipped polys list + if ( result_diff.contours() > 0 ) { + polys_clipped.polys[i].push_back(result_diff); + } + + // char filename[256]; + // sprintf(filename, "next-result-%02d", count++); + // FILE *tmpfp= fopen(filename, "w"); + // gpc_write_polygon(tmpfp, 1, result_diff); + // fclose(tmpfp); + } + accum = result_union; + } + } + + // finally, what ever is left over goes to ocean + + // clip to accum against original base tile + // remains = new gpc_polygon; + // remains->num_contours = 0; + // remains->contour = NULL; + remains = polygon_diff( polys_in.safety_base, accum ); + + if ( remains.contours() > 0 ) { + cout << "remains contours = " << remains.contours() << endl; + // move slivers from remains polygon to slivers polygon + move_slivers(remains, slivers); + cout << " After sliver move:" << endl; + cout << " remains = " << remains.contours() << endl; + cout << " slivers = " << slivers.contours() << endl; + + // merge any slivers with previously clipped + // neighboring polygons + if ( slivers.contours() > 0 ) { + merge_slivers(polys_clipped, slivers); + } + + if ( remains.contours() > 0 ) { + polys_clipped.polys[(int)OceanArea].push_back(remains); + } + } + +#if 0 + FILE *ofp; + + // tmp output accum + if ( accum.num_contours ) { + ofp = fopen("accum", "w"); + gpc_write_polygon(ofp, 1, &accum); + fclose(ofp); + } + + // tmp output safety_base + if ( remains->num_contours ) { + ofp= fopen("remains", "w"); + gpc_write_polygon(ofp, 1, remains); + fclose(ofp); + } +#endif + + return true; +} + + diff --git a/src/BuildTiles/Clipper/clipper.hxx b/src/BuildTiles/Clipper/clipper.hxx new file mode 100644 index 00000000..82f30add --- /dev/null +++ b/src/BuildTiles/Clipper/clipper.hxx @@ -0,0 +1,107 @@ +// 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$ + + + +#ifndef _CLIPPER_HXX +#define _CLIPPER_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include +#include + +#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 FGPolyList { +public: + poly_list polys[FG_MAX_AREA_TYPES]; + FGPolygon safety_base; +}; + + +class FGClipper { + +private: + + // gpc_vertex_list v_list; + // static gpc_polygon poly; + FGPolyList 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); + + // remove any slivers from in polygon and move them to out + // polygon. + void move_slivers( FGPolygon& in, FGPolygon& out ); + + // for each sliver contour, see if a union with another polygon + // yields a polygon with no increased contours (i.e. the sliver is + // adjacent and can be merged.) If so, replace the clipped + // polygon with the new polygon that has the sliver merged in. + void merge_slivers( FGPolyList& clipped, FGPolygon& slivers ); + + // Do actually clipping work + bool clip_all(const point2d& min, const point2d& max); + + // return output poly list + inline FGPolyList get_polys_clipped() const { return polys_clipped; } +}; + + +#endif // _CLIPPER_HXX + + diff --git a/src/BuildTiles/Clipper/testclipper.cxx b/src/BuildTiles/Clipper/testclipper.cxx new file mode 100644 index 00000000..73ed9073 --- /dev/null +++ b/src/BuildTiles/Clipper/testclipper.cxx @@ -0,0 +1,110 @@ +// 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$ + + + +#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; +} + diff --git a/src/BuildTiles/GenOutput/Makefile.am b/src/BuildTiles/GenOutput/Makefile.am new file mode 100644 index 00000000..a9577a0f --- /dev/null +++ b/src/BuildTiles/GenOutput/Makefile.am @@ -0,0 +1,8 @@ +noinst_LIBRARIES = libGenOutput.a + +libGenOutput_a_SOURCES = genobj.cxx genobj.hxx + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Construct diff --git a/src/BuildTiles/GenOutput/genobj.cxx b/src/BuildTiles/GenOutput/genobj.cxx new file mode 100644 index 00000000..54c43d5f --- /dev/null +++ b/src/BuildTiles/GenOutput/genobj.cxx @@ -0,0 +1,410 @@ +// 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$ + + +#include + +#include + +#include +#include "scenery_version.hxx" + +#include "genobj.hxx" + + +// calculate the global bounding sphere. Center is the center of the +// tile and zero elevation +void FGGenOutput::calc_gbs( FGConstruct& c ) { + double dist_squared; + double radius_squared = 0; + + FGBucket b = c.get_bucket(); + + Point3D p( b.get_center_lon() * DEG_TO_RAD, + b.get_center_lat() * DEG_TO_RAD, + 0 ); + + gbs_center = fgGeodToCart(p); + + point_list wgs84_nodes = c.get_wgs84_nodes(); + const_point_list_iterator current = wgs84_nodes.begin(); + const_point_list_iterator last = wgs84_nodes.end(); + for ( ; current != last; ++current ) { + dist_squared = gbs_center.distance3Dsquared(*current); + if ( dist_squared > radius_squared ) { + radius_squared = dist_squared; + } + } + + gbs_radius = sqrt(radius_squared); +} + + +#if 0 +#define FG_STANDARD_TEXTURE_DIMENSION 1000.0 // meters + +// traverse the specified fan and attempt to calculate "none +// stretching" texture coordinates +int_list FGGenOutput::calc_tex_coords( FGConstruct& c, point_list geod_nodes, + int_list fan ) +{ + // cout << "calculating texture coordinates for a specific fan of size = " + // << fan.size() << endl; + + FGBucket b = c.get_bucket(); + double clat = b.get_center_lat(); + double clat_rad = clat * DEG_TO_RAD; + double cos_lat = cos( clat_rad ); + double local_radius = cos_lat * EQUATORIAL_RADIUS_M; + double local_perimeter = 2.0 * local_radius * FG_PI; + double degree_width = local_perimeter / 360.0; + + // cout << "clat = " << clat << endl; + // cout << "clat (radians) = " << clat_rad << endl; + // cout << "cos(lat) = " << cos_lat << endl; + // cout << "local_radius = " << local_radius << endl; + // cout << "local_perimeter = " << local_perimeter << endl; + // cout << "degree_width = " << degree_width << endl; + + double perimeter = 2.0 * EQUATORIAL_RADIUS_M * FG_PI; + double degree_height = perimeter / 360.0; + // cout << "degree_height = " << degree_height << endl; + + // find min/max of fan + Point3D min, max, p, t; + bool first = true; + + for ( int i = 0; i < (int)fan.size(); ++i ) { + p = geod_nodes[ fan[i] ]; + t.setx( p.x() * ( degree_width / FG_STANDARD_TEXTURE_DIMENSION ) ); + t.sety( p.y() * ( degree_height / FG_STANDARD_TEXTURE_DIMENSION ) ); + + if ( first ) { + min = max = t; + first = false; + } else { + if ( t.x() < min.x() ) { + min.setx( t.x() ); + } + if ( t.y() < min.y() ) { + min.sety( t.y() ); + } + if ( t.x() > max.x() ) { + max.setx( t.x() ); + } + if ( t.y() > max.y() ) { + max.sety( t.y() ); + } + } + } + min.setx( (double)( (int)min.x() - 1 ) ); + min.sety( (double)( (int)min.y() - 1 ) ); + // cout << "found min = " << min << endl; + + // generate tex_list + Point3D shifted_t; + int index; + int_list tex; + tex.clear(); + for ( int i = 0; i < (int)fan.size(); ++i ) { + p = geod_nodes[ fan[i] ]; + t.setx( p.x() * ( degree_width / FG_STANDARD_TEXTURE_DIMENSION ) ); + t.sety( p.y() * ( degree_height / FG_STANDARD_TEXTURE_DIMENSION ) ); + shifted_t = t - min; + if ( shifted_t.x() < FG_EPSILON ) { + shifted_t.setx( 0.0 ); + } + if ( shifted_t.y() < FG_EPSILON ) { + shifted_t.sety( 0.0 ); + } + shifted_t.setz( 0.0 ); + // cout << "shifted_t = " << shifted_t << endl; + index = tex_coords.unique_add( shifted_t ); + tex.push_back( index ); + } + + return tex; +} +#endif + + +// build the necessary output structures based on the triangulation +// data +int FGGenOutput::build( FGConstruct& c ) { + FGTriNodes trinodes = c.get_tri_nodes(); + + // copy the geodetic node list into this class + geod_nodes = trinodes.get_node_list(); + + // copy the triangle list into this class + tri_elements = c.get_tri_elements(); + + // 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 ); + } + } + + // build the texture coordinate list and make a parallel structure + // to the fan list for pointers into the texture list + cout << "calculating texture coordinates" << endl; + tex_coords.clear(); + + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + for ( int j = 0; j < (int)fans[i].size(); ++j ) { + // int_list t_list = calc_tex_coords( c, geod_nodes, fans[i][j] ); + // cout << fans[i][j].size() << " === " + // << t_list.size() << endl; + point_list tp_list = calc_tex_coords( c.get_bucket(), + geod_nodes, fans[i][j] ); + int_list ti_list; + ti_list.clear(); + for ( int k = 0; k < (int)tp_list.size(); ++k ) { + int index = tex_coords.simple_add( tp_list[k] ); + ti_list.push_back( index ); + } + textures[i].push_back( ti_list ); + } + } + + // calculate the global bounding sphere + calc_gbs( c ); + cout << "center = " << gbs_center << " radius = " << gbs_radius << endl; + + return 1; +} + + +// caclulate the bounding sphere for a list of triangle faces +void FGGenOutput::calc_group_bounding_sphere( FGConstruct& c, + const fan_list& fans, + Point3D *center, double *radius ) +{ + cout << "calculate group bounding sphere for " << fans.size() << " fans." + << endl; + + point_list wgs84_nodes = c.get_wgs84_nodes(); + + // 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 + *center = Point3D( 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 ) { + *center += *p_current; + } + *center /= 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 = (*center).distance3Dsquared(*p_current); + if ( dist_squared > max_squared ) { + max_squared = dist_squared; + } + } + + *radius = sqrt(max_squared); +} + + +// caclulate the bounding sphere for the specified triangle face +void FGGenOutput::calc_bounding_sphere( FGConstruct& c, const FGTriEle& t, + Point3D *center, double *radius ) +{ + point_list wgs84_nodes = c.get_wgs84_nodes(); + + *center = Point3D( 0.0 ); + + Point3D p1 = wgs84_nodes[ t.get_n1() ]; + Point3D p2 = wgs84_nodes[ t.get_n2() ]; + Point3D p3 = wgs84_nodes[ t.get_n3() ]; + + *center = p1 + p2 + p3; + *center /= 3; + + double dist_squared; + double max_squared = 0; + + dist_squared = (*center).distance3Dsquared(p1); + if ( dist_squared > max_squared ) { + max_squared = dist_squared; + } + + dist_squared = (*center).distance3Dsquared(p2); + if ( dist_squared > max_squared ) { + max_squared = dist_squared; + } + + dist_squared = (*center).distance3Dsquared(p3); + if ( dist_squared > max_squared ) { + max_squared = dist_squared; + } + + *radius = sqrt(max_squared); +} + + +// write out the fgfs scenery file +int FGGenOutput::write( FGConstruct &c ) { + Point3D p; + + string base = c.get_output_base(); + FGBucket b = c.get_bucket(); + + 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\n"); + fprintf(fp, "# 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 + point_list wgs84_nodes = c.get_wgs84_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 + point_list point_normals = c.get_point_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 texture coordinates + point_list tex_coord_list = tex_coords.get_node_list(); + fprintf(fp, "# texture coordinate list\n"); + for ( int i = 0; i < (int)tex_coord_list.size(); ++i ) { + p = tex_coord_list[i]; + fprintf(fp, "vt %.5f %.5f\n", p.x(), p.y()); + } + 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( c, 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); + + for ( int j = 0; j < (int)fans[i].size(); ++j ) { + fprintf( fp, "tf" ); + total_tris += fans[i][j].size() - 2; + for ( int k = 0; k < (int)fans[i][j].size(); ++k ) { + fprintf( fp, " %d/%d", fans[i][j][k], textures[i][j][k] ); + } + fprintf( fp, "\n" ); + } + + 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; +} + + diff --git a/src/BuildTiles/GenOutput/genobj.hxx b/src/BuildTiles/GenOutput/genobj.hxx new file mode 100644 index 00000000..9a3ef282 --- /dev/null +++ b/src/BuildTiles/GenOutput/genobj.hxx @@ -0,0 +1,111 @@ +// 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$ + + +#ifndef _GENOBJ_HXX +#define _GENOBJ_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include + +#include STL_STRING + +#include +#include +#include +#include + +#include +#include
+#include + +FG_USING_STD(string); +FG_USING_STD(vector); + + +typedef vector < int_list > tex_list; +typedef tex_list::iterator tex_list_iterator; +typedef tex_list::const_iterator const_tex_list_iterator; + +class FGGenOutput { + +private: + + // node list in geodetic coordinates + point_list geod_nodes; + + // triangles (by index into point list) + triele_list tri_elements; + + // texture coordinates + FGTriNodes tex_coords; + + // fan list + fan_list fans[FG_MAX_AREA_TYPES]; + + // textures pointer list + tex_list textures[FG_MAX_AREA_TYPES]; + + // global bounding sphere + Point3D gbs_center; + double gbs_radius; + + // calculate the global bounding sphere. Center is the average of + // the points. + void calc_gbs( FGConstruct& c ); + + // caclulate the bounding sphere for a list of triangle faces + void calc_group_bounding_sphere( FGConstruct& c, const fan_list& fans, + Point3D *center, double *radius ); + + // caclulate the bounding sphere for the specified triangle face + void calc_bounding_sphere( FGConstruct& c, const FGTriEle& t, + Point3D *center, double *radius ); + + // traverse the specified fan and attempt to calculate "none + // stretching" texture coordinates + // int_list calc_tex_coords( FGConstruct& c, point_list geod_nodes, int_list fan ); + +public: + + // Constructor && Destructor + inline FGGenOutput() { } + inline ~FGGenOutput() { } + + // build the necessary output structures based on the + // triangulation data + int build( FGConstruct& c ); + + // write out the fgfs scenery file + int write( FGConstruct &c ); +}; + + +#endif // _GENOBJ_HXX + + diff --git a/src/BuildTiles/Main/Makefile.am b/src/BuildTiles/Main/Makefile.am new file mode 100644 index 00000000..b5fc850f --- /dev/null +++ b/src/BuildTiles/Main/Makefile.am @@ -0,0 +1,27 @@ +bin_PROGRAMS = fgfs-construct fgfs-master + +fgfs_construct_SOURCES = \ + construct.cxx construct.hxx \ + main.cxx + +fgfs_construct_LDADD = \ + $(top_builddir)/Construct/Clipper/libClipper.a \ + $(top_builddir)/Construct/GenOutput/libGenOutput.a \ + $(top_builddir)/Construct/Combine/libCombine.a \ + $(top_builddir)/Construct/Match/libMatch.a \ + $(top_builddir)/Construct/Triangulate/libTriangulate.a \ + $(top_builddir)/Lib/Array/libArray.a \ + $(top_builddir)/Lib/Build/libBuild.a \ + $(top_builddir)/Lib/Polygon/libPolygon.a \ + $(top_builddir)/Lib/poly2tri/libpoly2tri.a \ + $(top_builddir)/Lib/Triangle/libTriangle.a \ + -lsgbucket -lsgmath -lsgmisc -lsgdebug -lz -lgpc + +fgfs_master_SOURCES = master.cxx + +fgfs_master_LDADD = -lsgbucket -lsgmisc + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Construct diff --git a/src/BuildTiles/Main/construct.cxx b/src/BuildTiles/Main/construct.cxx new file mode 100644 index 00000000..04ced791 --- /dev/null +++ b/src/BuildTiles/Main/construct.cxx @@ -0,0 +1,33 @@ +// construct.cxx -- Class to manage the primary data used in the +// construction process +// +// Written by Curtis Olson, started May 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$ + + +#include "construct.hxx" + + +// Constructor +FGConstruct::FGConstruct() { } + + +// Destructor +FGConstruct::~FGConstruct() { } diff --git a/src/BuildTiles/Main/construct.hxx b/src/BuildTiles/Main/construct.hxx new file mode 100644 index 00000000..d3b6584f --- /dev/null +++ b/src/BuildTiles/Main/construct.hxx @@ -0,0 +1,175 @@ +// construct.hxx -- Class to manage the primary data used in the +// construction process +// +// Written by Curtis Olson, started May 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$ + + +#ifndef _CONSTRUCT_HXX +#define _CONSTRUCT_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +// Maximum nodes per tile +#define FG_MAX_NODES 2000 + + +#include + +#include STL_STRING + +#include + +#include +#include + +#include +#include + +FG_USING_STD(string); + + +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 FGConstruct { + +private: + + // minimum interior angle for triangulation + string angle; + + // paths + string work_base; + string output_base; + + // detail level constraints + int min_nodes; + int max_nodes; + + // this bucket + FGBucket bucket; + + // clipped polygons (gpc format) + FGPolyList clipped_polys; + + // raw node list (after triangulation) + FGTriNodes tri_nodes; + + // node list in geodetic coords (with fixed elevation) + point_list geod_nodes; + + // node list in cartesian coords (wgs84 model) + point_list wgs84_nodes; + + // triangle elements (after triangulation) + triele_list tri_elements; + + // edge segments (after triangulation) + FGTriSegments tri_segs; + + // for each node, a list of triangle indices that contain this node + belongs_to_list reverse_ele_lookup; + + // 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; + +public: + + // Constructor + FGConstruct(); + + // Destructor + ~FGConstruct(); + + // minimum interior angle for triangulation + inline string get_angle() const { return angle; } + inline void set_angle( const string s ) { angle = s; } + + // paths + inline string get_work_base() const { return work_base; } + inline void set_work_base( const string s ) { work_base = s; } + inline string get_output_base() const { return output_base; } + inline void set_output_base( const string s ) { output_base = s; } + + // detail level constraints + inline int get_min_nodes() const { return min_nodes; } + inline void set_min_nodes( const int n ) { min_nodes = n; } + inline int get_max_nodes() const { return max_nodes; } + inline void set_max_nodes( const int n ) { max_nodes = n; } + + // this bucket + inline FGBucket get_bucket() const { return bucket; } + inline void set_bucket( const FGBucket b ) { bucket = b; } + + // clipped polygons + inline FGPolyList get_clipped_polys() const { return clipped_polys; } + inline void set_clipped_polys( FGPolyList p ) { clipped_polys = p; } + + // node list (after triangulation) + inline FGTriNodes get_tri_nodes() const { return tri_nodes; } + inline void set_tri_nodes( FGTriNodes n ) { tri_nodes = n; } + + // triangle elements (after triangulation) + inline triele_list get_tri_elements() const { return tri_elements; } + inline void set_tri_elements( triele_list e ) { tri_elements = e; } + + // edge segments (after triangulation) + inline FGTriSegments get_tri_segs() const { return tri_segs; } + inline void set_tri_segs( FGTriSegments s ) { tri_segs = s; } + + // node list in geodetic coords (with fixed elevation) + inline point_list get_geod_nodes() const { return geod_nodes; } + inline void set_geod_nodes( point_list n ) { geod_nodes = n; } + + // node list in cartesian coords (wgs84 model) + inline point_list get_wgs84_nodes() const { return wgs84_nodes; } + inline void set_wgs84_nodes( point_list n ) { wgs84_nodes = n; } + + // for each node, a list of triangle indices that contain this node + inline belongs_to_list get_reverse_ele_lookup() const { + return reverse_ele_lookup; + } + inline void set_reverse_ele_lookup( belongs_to_list r ) { + reverse_ele_lookup = r; + } + + // face normal list (for flat shading) + inline point_list get_face_normals() const { return face_normals; } + inline void set_face_normals( point_list n ) { face_normals = n; } + + // normal list (for each point) in cart coords (for smooth + // shading) + inline point_list get_point_normals() const { return point_normals; } + inline void set_point_normals( point_list n ) { point_normals = n; } +}; + + +#endif // _CONSTRUCT_HXX diff --git a/src/BuildTiles/Main/main.cxx b/src/BuildTiles/Main/main.cxx new file mode 100644 index 00000000..4ea46362 --- /dev/null +++ b/src/BuildTiles/Main/main.cxx @@ -0,0 +1,794 @@ +// 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$ + + +#include // for directory reading +#include // for directory reading + +#include // set mem allocation limit +#include // set mem allocation limit +#include // set mem allocation limit + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "construct.hxx" + + +// do actual scan of directory and loading of files +int actual_load_polys( const string& dir, FGConstruct& c, FGClipper& clipper ) { + int counter = 0; + string base = c.get_bucket().gen_base_path(); + string tile_str = c.get_bucket().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; + } + } + } + + closedir(d); + + return counter; +} + + +// load all 2d polygons matching the specified base path and clip +// against each other to resolve any overlaps +int load_polys( FGConstruct& c ) { + FGClipper clipper; + + string base = c.get_bucket().gen_base_path(); + string poly_path; + int count = 0; + + // initialize clipper + clipper.init(); + + // load airports + poly_path = c.get_work_base() + "/AirportArea/" + base; + cout << "poly_path = " << poly_path << endl; + count += actual_load_polys( poly_path, c, clipper ); + cout << " loaded " << count << " total polys" << endl; + + // load hydro + poly_path = c.get_work_base() + "/USA-Hydro/" + base; + cout << "poly_path = " << poly_path << endl; + count += actual_load_polys( poly_path, c, clipper ); + cout << " loaded " << count << " total polys" << endl; + + // load land masses + poly_path = c.get_work_base() + "/Global-LandMass/" + base; + cout << "poly_path = " << poly_path << endl; + count += actual_load_polys( poly_path, c, clipper ); + cout << " loaded " << count << " total polys" << endl; + + // load urban areas + poly_path = c.get_work_base() + "/USA-Urban/" + base; + cout << "poly_path = " << poly_path << endl; + count += actual_load_polys( poly_path, c, clipper ); + cout << " loaded " << count << " total polys" << endl; + + point2d min, max; + min.x = c.get_bucket().get_center_lon() - 0.5 * c.get_bucket().get_width(); + min.y = c.get_bucket().get_center_lat() - 0.5 * c.get_bucket().get_height(); + max.x = c.get_bucket().get_center_lon() + 0.5 * c.get_bucket().get_width(); + max.y = c.get_bucket().get_center_lat() + 0.5 * c.get_bucket().get_height(); + + // do clipping + cout << "clipping polygons" << endl; + clipper.clip_all(min, max); + + // update main data repository + c.set_clipped_polys( clipper.get_polys_clipped() ); + + return count; +} + + +// load regular grid of elevation data (dem based), return list of +// fitted nodes +int load_dem( FGConstruct& c, FGArray& array) { + point_list result; + string base = c.get_bucket().gen_base_path(); + + // try 3 arcsec dems first + string dem_path = c.get_work_base() + "/DEM-3/" + base + + "/" + c.get_bucket().gen_index_str() + ".dem"; + cout << "dem_path = " << dem_path << endl; + + if ( ! array.open(dem_path) ) { + cout << "ERROR: cannot open 3 arcsec file " << dem_path << endl; + cout << "trying 30 arcsec file" << endl; + + // try 30 arcsec dem + dem_path = c.get_work_base() + "/DEM-30/" + base + + "/" + c.get_bucket().gen_index_str() + ".dem"; + cout << "dem_path = " << dem_path << endl; + if ( ! array.open(dem_path) ) { + cout << "ERROR: cannot open 3 arcsec file " << dem_path << endl; + } + } + + FGBucket b = c.get_bucket(); + array.parse( b ); + + return 1; +} + + +// fit dem nodes, return number of fitted nodes +int fit_dem(FGArray& array, int error) { + return array.fit( error ); +} + + +// triangulate the data for each polygon ( first time before splitting ) +void first_triangulate( FGConstruct& c, const FGArray& array, + 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(); + FGPolyList gpc_polys = c.get_clipped_polys(); + + 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( c.get_angle(), 1 ); + cout << "finished triangulation" << endl; +} + + +// triangulate the data for each polygon ( second time after splitting +// and reassembling ) +void second_triangulate( FGConstruct& c, FGTriangle& t ) { + t.rebuild( c ); + cout << "done re building node list and polygons" << endl; + + cout << "ready to do second triangulation" << endl; + t.run_triangulate( c.get_angle(), 2 ); + cout << "finished second triangulation" << endl; +} + + +// calculate distance based on x,y only +static double distance2D( const Point3D p1, const Point3D p2 ) { + double dx = p1.x() - p2.x(); + double dy = p1.y() - p2.y(); + return sqrt( dx*dx + dy*dy ); +} + + +// fix the elevations of the geodetic nodes +static void fix_point_heights( FGConstruct& c, const FGArray& array ) { + double z; + + cout << "fixing node heights" << endl; + + point_list raw_nodes = c.get_tri_nodes().get_node_list(); + + for ( int i = 0; i < (int)raw_nodes.size(); ++i ) { + z = array.interpolate_altitude( raw_nodes[i].x() * 3600.0, + raw_nodes[i].y() * 3600.0 ); + cout << " old z = " << raw_nodes[i].z() << " new z = " << z << endl; + if ( raw_nodes[i].z() != z ) { + cout << " DIFFERENT" << endl; + } + raw_nodes[i].setz( z ); + } + + cout << "flattening ocean connected nodes" << endl; + + triele_list tris = c.get_tri_elements(); + FGTriEle t; + Point3D p; + AreaType a; + int n1, n2, n3; + + for ( int count = 0; count < 3; ++count ) { + for ( int i = 0; i < (int)tris.size(); ++i ) { + double e1, e2, e3, ave, min; + + t = tris[i]; + n1 = t.get_n1(); + n2 = t.get_n2(); + n3 = t.get_n3(); + a = (AreaType)((int)(t.get_attribute())); + + // scale elevation of all water nodes based on the average + // of the elevations of the nodes of the triangle of which + // they are a member. This could really suck for certain + // cases, but it is my first stab at something reasonable. + // It might be better to eventually iterate, and allow + // some flexibility in elevations to handle rivers and + // things like that. + if ( (a == LakeArea) || (a == ReservoirArea) ) { + e1 = raw_nodes[n1].z(); + e2 = raw_nodes[n2].z(); + e3 = raw_nodes[n3].z(); + + min = e1; p = raw_nodes[n1]; + if ( e2 < min ) { min = e2; p = raw_nodes[n2]; } + if ( e3 < min ) { min = e3; p = raw_nodes[n3]; } + ave = (e1 + e2 + e3) / 3.0; + + raw_nodes[n1].setz( min ); + raw_nodes[n2].setz( min ); + raw_nodes[n3].setz( min ); + } else if ( (a == StreamArea) || (a == CanalArea) ) { + e1 = raw_nodes[n1].z(); + e2 = raw_nodes[n2].z(); + e3 = raw_nodes[n3].z(); + + min = e1; p = raw_nodes[n1]; + if ( e2 < min ) { min = e2; p = raw_nodes[n2]; } + if ( e3 < min ) { min = e3; p = raw_nodes[n3]; } + + double d1 = distance2D( p, raw_nodes[n1] ); + double d2 = distance2D( p, raw_nodes[n2] ); + double d3 = distance2D( p, raw_nodes[n3] ); + + double max1 = 1000.0 * d1 + min; + double max2 = 1000.0 * d2 + min; + double max3 = 1000.0 * d3 + min; + + if ( max1 < e1 ) { raw_nodes[n1].setz( max1 ); } + if ( max2 < e2 ) { raw_nodes[n2].setz( max2 ); } + if ( max3 < e3 ) { raw_nodes[n3].setz( max3 ); } + } + } + } + + for ( int i = 0; i < (int)tris.size(); ++i ) { + // set all ocean nodes to 0.0 + t = tris[i]; + n1 = t.get_n1(); + n2 = t.get_n2(); + n3 = t.get_n3(); + a = (AreaType)((int)(t.get_attribute())); + + if ( a == OceanArea ) { + raw_nodes[n1].setz( 0.0 ); + raw_nodes[n2].setz( 0.0 ); + raw_nodes[n3].setz( 0.0 ); + } + + } + + FGTriNodes tmp; + tmp.set_node_list( raw_nodes ); + c.set_tri_nodes( tmp ); +} + + +// build the wgs-84 point list +static void build_wgs_84_point_list( FGConstruct& c, const FGArray& array ) { + point_list geod_nodes; + point_list wgs84_nodes; + + cout << "generating wgs84 list" << endl; + Point3D geod, radians, cart; + + point_list raw_nodes = c.get_tri_nodes().get_node_list(); + + for ( int i = 0; i < (int)raw_nodes.size(); ++i ) { + geod = raw_nodes[i]; + + // convert to radians + radians = Point3D( geod.x() * DEG_TO_RAD, + geod.y() * DEG_TO_RAD, + geod.z() ); + + cart = fgGeodToCart(radians); + // cout << cart << endl; + + geod_nodes.push_back(geod); + wgs84_nodes.push_back(cart); + } + + c.set_geod_nodes( geod_nodes ); + c.set_wgs84_nodes( wgs84_nodes ); +} + + +// 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. +static belongs_to_list gen_node_ele_lookup_table( FGConstruct& c ) { + belongs_to_list reverse_ele_lookup; + reverse_ele_lookup.clear(); + + int_list ele_list; + ele_list.clear(); + + // initialize reverse_ele_lookup structure by creating an empty + // list for each point + point_list wgs84_nodes = c.get_wgs84_nodes(); + 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 + triele_list tri_elements = c.get_tri_elements(); + 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; + } + + return reverse_ele_lookup; +} + + +// caclulate the normal for the specified triangle face +static Point3D calc_normal( FGConstruct& c, int i ) { + double v1[3], v2[3], normal[3]; + double temp; + + point_list wgs84_nodes = c.get_wgs84_nodes(); + triele_list tri_elements = c.get_tri_elements(); + + 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 +static point_list gen_face_normals( FGConstruct& c ) { + point_list face_normals; + + // traverse triangle structure building the face normal table + + cout << "calculating face normals" << endl; + + triele_list tri_elements = c.get_tri_elements(); + for ( int i = 0; i < (int)tri_elements.size(); i++ ) { + // cout << calc_normal( i ) << endl; + face_normals.push_back( calc_normal( c, i ) ); + } + + return face_normals; +} + + +// calculate the normals for each point in wgs84_nodes +static point_list gen_point_normals( FGConstruct& c ) { + point_list point_normals; + + Point3D normal; + cout << "caculating node normals" << endl; + + point_list wgs84_nodes = c.get_wgs84_nodes(); + belongs_to_list reverse_ele_lookup = c.get_reverse_ele_lookup(); + point_list face_normals = c.get_face_normals(); + + // 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 ); + } + + cout << "1st" << endl; + cout << "wgs84 node list size = " << wgs84_nodes.size() << endl; + cout << "normal list size = " << point_normals.size() << endl; + + return point_normals; +} + + +// generate the flight gear scenery file +void do_output( FGConstruct& c, FGGenOutput& output ) { + output.build( c ); + output.write( c ); +} + + +// collect custom objects and move to scenery area +void do_custom_objects( const FGConstruct& c ) { + FGBucket b = c.get_bucket(); + + string work_base = c.get_work_base(); + string src_dir = work_base + "/AirportObj/" + b.gen_base_path(); + string index_file = src_dir + "/" + b.gen_index_str() + ".ind"; + + cout << "collecting custom objects from " << index_file << endl; + + string output_base = c.get_output_base(); + string dest_dir = output_base + "/Scenery/" + b.gen_base_path(); + string dest_ind = dest_dir + "/" + b.gen_index_str() + ".ind"; + + fg_gzifstream in( index_file ); + + if ( ! in.is_open() ) { + cout << "No custom objects" << endl; + } else { + FILE *fp; + if ( (fp = fopen( dest_ind.c_str(), "w" )) == NULL ) { + cout << "ERROR: opening " << dest_ind << " for writing!" << endl; + exit(-1); + } + + string token, name; + + while ( ! in.eof() ) { + in >> token; + in >> name; + in >> skipws; + + cout << "token = " << token << " name = " << name << endl; + string command = "cp " + src_dir + "/" + name + ".gz " + dest_dir; + cout << "running " << command << endl; + system( command.c_str() ); + + fprintf(fp, "OBJECT %s\n", name.c_str()); + } + + fclose(fp); + } +} + +// master construction routine +void construct_tile( FGConstruct& c ) { + cout << "Construct tile, bucket = " << c.get_bucket() << 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. + + bool acceptable = false; + bool growing = false; + bool shrinking = false; + + double error = 200.0; + int count = 0; + + // load and clip 2d polygon data + if ( load_polys( c ) == 0 ) { + // don't build the tile if there is no 2d data ... it *must* + // be ocean and the sim can build the tile on the fly. + return; + } + + // load grid of elevation data (dem) + FGArray array; + load_dem( c, array ); + + FGTriangle t; + + while ( ! acceptable ) { + // do a least squares fit of the (dem) data with the given + // error tolerance + array.fit( error ); + + // triangulate the data for each polygon + first_triangulate( c, array, t ); + + acceptable = true; + + count = t.get_out_nodes_size(); + + if ( (count < c.get_min_nodes()) && (error >= 25.0) ) { + // reduce error tolerance until number of points exceeds the + // minimum threshold + cout << "produced too few nodes ..." << endl; + + acceptable = false; + growing = true; + + if ( shrinking ) { + error /= 1.25; + shrinking = false; + } else { + error /= 1.5; + } + cout << "Setting error to " << error << " and retrying fit." + << endl; + } + + if ( count > c.get_max_nodes() ) { + if ( error <= 1000.0 ) { + // increase error tolerance until number of points drops below + // the maximum threshold + cout << "produced too many nodes ..." << endl; + + acceptable = false; + shrinking = true; + + if ( growing ) { + error *= 1.25; + growing = false; + } else { + error *= 1.5; + } + + cout << "Setting error to " << error << " and retrying fit." + << endl; + } else { + // we tried, but can't seem to get down to a + // reasonable number of points even with a huge error + // tolerance. This could be related to the triangle() + // call which might be having trouble with our input + // set. Let's just die hope that our parent can try + // again with a smaller interior triangle angle. + cout << "Error: Too many nodes." << endl; + exit(-1); + } + } + } + + cout << "finished fit with error = " << error << " node count = " + << count << endl; + + // save the results of the triangulation + c.set_tri_nodes( t.get_out_nodes() ); + c.set_tri_elements( t.get_elelist() ); + c.set_tri_segs( t.get_out_segs() ); + + // calculate wgs84 (cartesian) form of node list + fix_point_heights( c, array ); + build_wgs_84_point_list( c, array ); + + // build the node -> element (triangle) reverse lookup table + c.set_reverse_ele_lookup( gen_node_ele_lookup_table( c ) ); + + // build the face normal list + c.set_face_normals( gen_face_normals( c ) ); + + // calculate the normals for each point in wgs84_nodes + c.set_point_normals( gen_point_normals( c ) ); + + // match tile edges with any neighbor tiles that have already been + // generated + FGMatch m; + m.load_neighbor_shared( c ); + m.split_tile( c ); + m.write_shared( c ); + m.assemble_tile( c ); + + // now we must retriangulate the pasted together tile points + second_triangulate( c, t ); + + // save the results of the triangulation + c.set_tri_nodes( t.get_out_nodes() ); + c.set_tri_elements( t.get_elelist() ); + c.set_tri_segs( t.get_out_segs() ); + + // calculate wgs84 (cartesian) form of node list + build_wgs_84_point_list( c, array ); + + // generate the output + FGGenOutput output; + do_output( c, output ); + + array.close(); + + // collect custom objects and move to scenery area + do_custom_objects( c ); +} + + +// display usage and exit +void usage( const string name ) { + cout << "Usage: " << name + << " tile_id" << endl; + cout << "Usage: " << name + << " center_lon center_lat xdist ydist" + << endl; + exit(-1); +} + + +main(int argc, char **argv) { + double lon, lat; + + fglog().setLogLevels( FG_ALL, FG_DEBUG ); + + if ( argc < 4 ) { + usage( argv[0] ); + } + + // set mem allocation limit. Reason: occasionally the triangle() + // routine can blow up and allocate memory forever. We'd like + // this process to die before things get out of hand so we can try + // again with a smaller interior angle limit. + int result; + struct rlimit limit; + limit.rlim_cur = 20000000; + limit.rlim_max = 20000000; + result = setrlimit( RLIMIT_DATA, &limit ); + cout << "result of setting mem limit = " << result << endl; + result = setrlimit( RLIMIT_STACK, &limit ); + cout << "result of setting mem limit = " << result << endl; + result = setrlimit( RLIMIT_CORE, &limit ); + cout << "result of setting mem limit = " << result << endl; + result = setrlimit( RLIMIT_RSS, &limit ); + cout << "result of setting mem limit = " << result << endl; + + // cpu time limit since occassionally the triangulator can go into + // and infinite loop. + limit.rlim_cur = 120; + limit.rlim_max = 120; + result = setrlimit( RLIMIT_CPU, &limit ); + cout << "result of setting mem limit = " << result << endl; + + // main construction data management class + FGConstruct c; + + c.set_angle( argv[1] ); + c.set_work_base( argv[2] ); + c.set_output_base( argv[3] ); + + c.set_min_nodes( 50 ); + c.set_max_nodes( (int)(FG_MAX_NODES * 0.8) ); + + // 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.7446823; lat= 29.314495; + // 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 = -121.534; lat = 47.0131; // 6WA8 + // lon = -148.798131; lat = 63.645099; // AK06 (Danali, AK) + // lon = -92.5; lat = 47.5; // Marsh test (northern MN) + // lon = -111.977773; lat = 40.788388; // KSLC + // lon = -121.914; lat = 42.5655; // TEST (Oregon SW of Crater) + // lon = -76.201239; lat = 36.894606; // KORF (Norfolk, Virginia) + // lon = -147.166; lat = 60.9925; // Hale-bop test + + if ( argc == 4 ) { + // construct the default tile and exit + + FGBucket b( lon, lat ); + c.set_bucket( b ); + construct_tile( c ); + } else if ( argc == 5 ) { + // construct a specific tile and exit + + long index = atoi( argv[4] ); + FGBucket b( index ); + c.set_bucket( b ); + construct_tile( c ); + } else if ( argc == 8 ) { + // build all the tiles in an area + + lon = atof( argv[4] ); + lat = atof( argv[5] ); + double xdist = atof( argv[6] ); + double ydist = atof( argv[7] ); + + double min_x = lon - xdist; + double min_y = lat - ydist; + FGBucket b_min( min_x, min_y ); + FGBucket b_max( lon + xdist, lat + ydist ); + + FGBucket b_start(550401L); + bool do_tile = true; + + if ( b_min == b_max ) { + c.set_bucket( b_min ); + construct_tile( c ); + } 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_start ) { + do_tile = true; + } + + if ( do_tile ) { + c.set_bucket( b_cur ); + construct_tile( c ); + } else { + cout << "skipping " << b_cur << endl; + } + } + } + // string answer; cin >> answer; + } + } else { + usage( argv[0] ); + } + + cout << "[Finished successfully]" << endl; +} diff --git a/src/BuildTiles/Main/master.cxx b/src/BuildTiles/Main/master.cxx new file mode 100644 index 00000000..a7da0047 --- /dev/null +++ b/src/BuildTiles/Main/master.cxx @@ -0,0 +1,153 @@ +// master.cxx -- top level construction routines +// +// Written by Curtis Olson, started May 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$ + + +#include // for system() +#include // for stat() +#include // for stat() + +#include + +#include + +// #include +// #include + +// #include +// #include +// #include +// #include +// #include +// #include + + +// return true if file exists +static bool file_exists( const string& file ) { + struct stat buf; + + if ( stat( file.c_str(), &buf ) == 0 ) { + return true; + } else { + return false; + } +} + + +// check if the specified tile has data defined for it +static bool has_data( const string& path, const FGBucket& b ) { + string dem_file = path + "DEM-3/" + b.gen_base_path() + + "/" + b.gen_index_str() + ".dem"; + if ( file_exists( dem_file ) ) { + return true; + } + + dem_file += ".gz"; + if ( file_exists( dem_file ) ) { + return true; + } + + return false; +} + + +// build the tile and check for success +bool build_tile( const string& work_base, const string& output_base, + const FGBucket& b ) { + string result_file = "result"; + + string command = "./construct " + work_base + " " + output_base + " " + + b.gen_index_str() + " > " + result_file + " 2>&1"; + cout << command << endl; + + system( command.c_str() ); + + FILE *fp = fopen( result_file.c_str(), "r" ); + char line[256]; + while ( fgets( line, 256, fp ) != NULL ) { + string line_str = line; + line_str = line_str.substr(0, line_str.length() - 1); + cout << line_str << endl; + if ( line_str == "[Finished successfully]" ) { + fclose(fp); + return true; + } + } + + fclose(fp); + return false; +} + + +// display usage and exit +void usage( const string name ) { + cout << "Usage: " << name << " " << endl; + exit(-1); +} + + +main(int argc, char **argv) { + double lon, lat; + + if ( argc < 3 ) { + usage( argv[0] ); + } + + string work_base = argv[1]; + string output_base = argv[2]; + + FGBucket tmp1( 0.0, 0.0 ); + + double dy = tmp1.get_height(); + lat = 68.75 + dy * 0.5; + + bool start_lon = true; + lon = -152.9 /* + (1.0/16.0) */; + + while ( lat <= 90.0 ) { + FGBucket tmp2( 0.0, lat ); + double dx = tmp2.get_width(); + + if ( ! start_lon ) { + lon = -180 + dx * 0.5; + } else { + start_lon = false; + } + while ( lon <= 180.0 ) { + FGBucket b( lon, lat ); + cout << "Bucket = " << b << endl; + + if ( has_data( work_base, b ) ) { + if ( ! build_tile( work_base, output_base, b ) ) { + cout << "error building last tile" << endl; + exit(-1); + } + } + + lon += dx; + } + + + lat += dy; + } +} + + diff --git a/src/BuildTiles/Makefile.am b/src/BuildTiles/Makefile.am new file mode 100644 index 00000000..97170022 --- /dev/null +++ b/src/BuildTiles/Makefile.am @@ -0,0 +1,9 @@ +SUBDIRS = \ + Triangulate \ + Clipper \ + Combine \ + GenOutput \ + Match \ + Parallel \ + Main + diff --git a/src/BuildTiles/Match/Makefile.am b/src/BuildTiles/Match/Makefile.am new file mode 100644 index 00000000..317ce8c7 --- /dev/null +++ b/src/BuildTiles/Match/Makefile.am @@ -0,0 +1,8 @@ +noinst_LIBRARIES = libMatch.a + +libMatch_a_SOURCES = match.cxx match.hxx + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Construct diff --git a/src/BuildTiles/Match/match.cxx b/src/BuildTiles/Match/match.cxx new file mode 100644 index 00000000..c010d6f9 --- /dev/null +++ b/src/BuildTiles/Match/match.cxx @@ -0,0 +1,721 @@ +// 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$ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "match.hxx" + + + +FGMatch::FGMatch( void ) { +} + + +FGMatch::~FGMatch( void ) { +} + + +// scan the specified share file for the specified information +void FGMatch::scan_share_file( const string& dir, const FGBucket& b, + neighbor_type search, neighbor_type dest ) +{ + string file = dir + "/" + b.gen_base_path() + "/" + b.gen_index_str(); + + fg_gzifstream in( file ); + if ( !in.is_open() ) { + cout << "Cannot open file: " << file << endl; + return; + } + + // cout << "reading shared data from " << file << endl; + + string target; + if ( search == SW_Corner ) { + target = "sw_node"; + } else if ( search == SE_Corner ) { + target = "se_node"; + } else if ( search == NE_Corner ) { + target = "ne_node"; + } else if ( search == NW_Corner ) { + target = "nw_node"; + } else if ( search == NORTH ) { + target = "n_node"; + } else if ( search == SOUTH ) { + target = "s_node"; + } else if ( search == EAST ) { + target = "e_node"; + } else if ( search == WEST ) { + target = "w_node"; + } + + string key; + Point3D node, normal; + while ( in ) { + in >> key; + in >> node; + if ( key == target ) { + // cout << key << " " << node << endl; + in >> key; + in >> normal; + + if ( dest == SW_Corner ) { + sw_node = node; + sw_normal = normal; + sw_flag = true; + } else if ( dest == SE_Corner ) { + se_node = node; + se_normal = normal; + se_flag = true; + } else if ( dest == NE_Corner ) { + ne_node = node; + ne_normal = normal; + ne_flag = true; + } else if ( dest == NW_Corner ) { + nw_node = node; + nw_normal = normal; + nw_flag = true; + } else if ( dest == NORTH ) { + north_nodes.push_back(node); + north_normals.push_back(normal); + north_flag = true; + } else if ( dest == SOUTH ) { + south_nodes.push_back(node); + south_normals.push_back(normal); + south_flag = true; + } else if ( dest == EAST ) { + east_nodes.push_back(node); + east_normals.push_back(normal); + east_flag = true; + } else if ( dest == WEST ) { + west_nodes.push_back(node); + west_normals.push_back(normal); + west_flag = true; + } + } else if ( (target == "n_node") && (key == "n_null") ) { + south_flag = true; + } else if ( (target == "s_node") && (key == "s_null") ) { + north_flag = true; + } else if ( (target == "e_node") && (key == "e_null") ) { + west_flag = true; + } else if ( (target == "w_node") && (key == "w_null") ) { + east_flag = true; + } + } +} + + +// try to find info for the specified shared component +void FGMatch::load_shared( const FGConstruct& c, neighbor_type n ) { + FGBucket b = c.get_bucket(); + + double clon = b.get_center_lon(); + double clat = b.get_center_lat(); + + string base = c.get_work_base() + "/Shared/"; + + FGBucket cb; + + if ( n == SW_Corner ) { + // cout << "searching for SW corner data" << endl; + cb = fgBucketOffset(clon, clat, -1, 0); + scan_share_file( base, cb, SE_Corner, n ); + cb = fgBucketOffset(clon, clat, -1, -1); + scan_share_file( base, cb, NE_Corner, n ); + cb = fgBucketOffset(clon, clat, 0, -1); + scan_share_file( base, cb, NW_Corner, n ); + } else if ( n == SE_Corner ) { + // cout << "searching for SE corner data" << endl; + cb = fgBucketOffset(clon, clat, 0, -1); + scan_share_file( base, cb, NE_Corner, n ); + cb = fgBucketOffset(clon, clat, 1, -1); + scan_share_file( base, cb, NW_Corner, n ); + cb = fgBucketOffset(clon, clat, 1, 0); + scan_share_file( base, cb, SW_Corner, n ); + } else if ( n == NE_Corner ) { + // cout << "searching for NE corner data" << endl; + cb = fgBucketOffset(clon, clat, 1, 0); + scan_share_file( base, cb, NW_Corner, n ); + cb = fgBucketOffset(clon, clat, 1, 1); + scan_share_file( base, cb, SW_Corner, n ); + cb = fgBucketOffset(clon, clat, 0, 1); + scan_share_file( base, cb, SE_Corner, n ); + } else if ( n == NW_Corner ) { + // cout << "searching for NW corner data" << endl; + cb = fgBucketOffset(clon, clat, 0, 1); + scan_share_file( base, cb, SW_Corner, n ); + cb = fgBucketOffset(clon, clat, -1, 1); + scan_share_file( base, cb, SE_Corner, n ); + cb = fgBucketOffset(clon, clat, -1, 0); + scan_share_file( base, cb, NE_Corner, n ); + } else if ( n == NORTH ) { + // cout << "searching for NORTH edge data" << endl; + cb = fgBucketOffset(clon, clat, 0, 1); + scan_share_file( base, cb, SOUTH, n ); + } else if ( n == SOUTH ) { + // cout << "searching for SOUTH edge data" << endl; + cb = fgBucketOffset(clon, clat, 0, -1); + scan_share_file( base, cb, NORTH, n ); + } else if ( n == EAST ) { + // cout << "searching for EAST edge data" << endl; + cb = fgBucketOffset(clon, clat, 1, 0); + scan_share_file( base, cb, WEST, n ); + } else if ( n == WEST ) { + // cout << "searching for WEST edge data" << endl; + cb = fgBucketOffset(clon, clat, -1, 0); + scan_share_file( base, cb, EAST, n ); + } +} + + +// load any previously existing shared data from all neighbors (if +// shared data for a component exists set that components flag to true +void FGMatch::load_neighbor_shared( FGConstruct& c ) { + cout << "Loading existing shared data from neighbor tiles" << endl; + + // start with all flags false + sw_flag = se_flag = ne_flag = nw_flag = false; + north_flag = south_flag = east_flag = west_flag = false; + + load_shared( c, SW_Corner ); + load_shared( c, SE_Corner ); + load_shared( c, NE_Corner ); + load_shared( c, NW_Corner ); + + north_nodes.clear(); + south_nodes.clear(); + east_nodes.clear(); + west_nodes.clear(); + + load_shared( c, NORTH ); + load_shared( c, SOUTH ); + load_shared( c, EAST ); + load_shared( c, WEST ); + + cout << "Shared data read in:" << endl; + if ( sw_flag ) { cout << " sw corner = " << sw_node << endl; } + if ( se_flag ) { cout << " se corner = " << se_node << endl; } + if ( ne_flag ) { cout << " ne corner = " << ne_node << endl; } + if ( nw_flag ) { cout << " nw corner = " << nw_node << endl; } + if ( north_flag ) { + cout << " north nodes = " << north_nodes.size() << endl; + for ( int i = 0; i < (int)north_nodes.size(); ++i ) { + cout << " " << north_nodes[i] << endl; + } + } + if ( south_flag ) { + cout << " south nodes = " << south_nodes.size() << endl; + for ( int i = 0; i < (int)south_nodes.size(); ++i ) { + cout << " " << south_nodes[i] << endl; + } + } + if ( east_flag ) { + cout << " east nodes = " << east_nodes.size() << endl; + for ( int i = 0; i < (int)east_nodes.size(); ++i ) { + cout << " " << east_nodes[i] << endl; + } + } + if ( west_flag ) { + cout << " west nodes = " << west_nodes.size() << endl; + for ( int i = 0; i < (int)west_nodes.size(); ++i ) { + cout << " " << west_nodes[i] << endl; + } + } +} + + +// split up the tile between the shared edge points, normals, and +// segments and the body. This must be done after calling +// load_neighbor_data() and will ignore any shared data from the +// current tile that already exists from a neighbor. +void FGMatch::split_tile( FGConstruct& c ) { + cout << "Spliting tile" << endl; + cout << " extracting (shared) edge nodes and normals" << endl; + + // calculate tile boundaries + point2d min, max; + FGBucket b = c.get_bucket(); + 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(); + + // separate nodes and normals into components + + body_nodes.clear(); + + point_list nodes = c.get_geod_nodes(); + point_list point_normals = c.get_point_normals(); + + for ( int i = 0; i < (int)nodes.size(); ++i ) { + Point3D node = nodes[i]; + Point3D normal = point_normals[i]; + + if ( (fabs(node.y() - min.y) < FG_EPSILON) && + (fabs(node.x() - min.x) < FG_EPSILON) ) { + if ( ! sw_flag ) { + sw_node = node; + sw_normal = normal; + } + } else if ( (fabs(node.y() - min.y) < FG_EPSILON) && + (fabs(node.x() - max.x) < FG_EPSILON) ) { + if ( ! se_flag ) { + se_node = node; + se_normal = normal; + } + } else if ( (fabs(node.y() - max.y) < FG_EPSILON) && + (fabs(node.x() - max.x) < FG_EPSILON)) { + if ( ! ne_flag ) { + ne_node = node; + ne_normal = normal; + } + } else if ( (fabs(node.y() - max.y) < FG_EPSILON) && + (fabs(node.x() - min.x) < FG_EPSILON) ) { + if ( ! nw_flag ) { + nw_node = node; + nw_normal = normal; + } + } else if ( fabs(node.x() - min.x) < FG_EPSILON ) { + if ( ! west_flag ) { + west_nodes.push_back( node ); + west_normals.push_back( normal ); + } + } else if ( fabs(node.x() - max.x) < FG_EPSILON ) { + if ( ! east_flag ) { + east_nodes.push_back( node ); + east_normals.push_back( normal ); + } + } else if ( fabs(node.y() - min.y) < FG_EPSILON ) { + if ( ! south_flag ) { + south_nodes.push_back( node ); + south_normals.push_back( normal ); + } + } else if ( fabs(node.y() - max.y) < FG_EPSILON ) { + if ( ! north_flag ) { + north_nodes.push_back( node ); + north_normals.push_back( normal ); + } + } else { + body_nodes.push_back( node ); + body_normals.push_back( normal ); + } + } + + // separate area edge segment into components + cout << " extracting (shared) area edge segments" << endl; + + FGTriSeg seg; + Point3D p1, p2; + triseg_list seg_list = c.get_tri_segs().get_seg_list(); + triseg_list_iterator current = seg_list.begin(); + triseg_list_iterator last = seg_list.end(); + + for ( ; current != last; ++current ) { + seg = *current; + p1 = nodes[ seg.get_n1() ]; + p2 = nodes[ seg.get_n2() ]; + + if ( fabs(p1.y() - p2.y()) < FG_EPSILON ) { + // check if horizontal + if ( fabs(p1.y() - max.y) < FG_EPSILON ) { + north_segs.push_back( seg ); + } else if ( fabs(p1.y() - min.y) < FG_EPSILON ) { + south_segs.push_back( seg ); + } else { + body_segs.push_back( seg ); + } + } else if ( fabs(p1.x() - p2.x()) < FG_EPSILON ) { + // check if vertical + if ( fabs(p1.x() - max.x) < FG_EPSILON ) { + east_segs.push_back( seg ); + } else if ( fabs(p1.x() - min.x) < FG_EPSILON ) { + west_segs.push_back( seg ); + } else { + body_segs.push_back( seg ); + } + } else { + body_segs.push_back( seg ); + } + } + + if ( !sw_flag ) { cout << " sw corner = " << sw_node << endl; } + if ( !se_flag ) { cout << " se corner = " << se_node << endl; } + if ( !ne_flag ) { cout << " ne corner = " << ne_node << endl; } + if ( !nw_flag ) { cout << " nw corner = " << nw_node << endl; } + if ( !north_flag ) { + cout << " north nodes = " << north_nodes.size() << endl; + for ( int i = 0; i < (int)north_nodes.size(); ++i ) { + cout << " " << north_nodes[i] << endl; + } + } + if ( !south_flag ) { + cout << " south nodes = " << south_nodes.size() << endl; + for ( int i = 0; i < (int)south_nodes.size(); ++i ) { + cout << " " << south_nodes[i] << endl; + } + } + if ( !east_flag ) { + cout << " east nodes = " << east_nodes.size() << endl; + for ( int i = 0; i < (int)east_nodes.size(); ++i ) { + cout << " " << east_nodes[i] << endl; + } + } + if ( !west_flag ) { + cout << " west nodes = " << west_nodes.size() << endl; + for ( int i = 0; i < (int)west_nodes.size(); ++i ) { + cout << " " << west_nodes[i] << endl; + } + } + cout << " body nodes = " << body_nodes.size() << endl; + for ( int i = 0; i < (int)body_nodes.size(); ++i ) { + cout << " " << body_nodes[i] << endl; + } +} + + +// write the new shared edge points, normals, and segments for this +// tile +void FGMatch::write_shared( FGConstruct& c ) { + string base = c.get_work_base(); + FGBucket b = c.get_bucket(); + + string dir = base + "/Shared/" + b.gen_base_path(); + string command = "mkdir -p " + dir; + string file = dir + "/" + b.gen_index_str(); + cout << "shared data will be written to " << file << endl; + + system(command.c_str()); + +#if 0 + cout << "FLAGS" << endl; + cout << "=====" << endl; + cout << "sw_flag = " << sw_flag << endl; + cout << "se_flag = " << se_flag << endl; + cout << "ne_flag = " << ne_flag << endl; + cout << "nw_flag = " << nw_flag << endl; + cout << "north_flag = " << north_flag << endl; + cout << "south_flag = " << south_flag << endl; + cout << "east_flag = " << east_flag << endl; + cout << "west_flag = " << west_flag << endl; +#endif + + FILE *fp; + if ( (fp = fopen( file.c_str(), "w" )) == NULL ) { + cout << "ERROR: opening " << file << " for writing!" << endl; + exit(-1); + } + + if ( ! sw_flag ) { + fprintf( fp, "sw_node %.6f %.6f %.6f\n", + sw_node.x(), sw_node.y(), sw_node.z() ); + fprintf( fp, "sw_normal %.6f %.6f %.6f\n", + sw_normal.x(), sw_normal.y(), sw_normal.z() ); + } + + if ( ! se_flag ) { + fprintf( fp, "se_node %.6f %.6f %.6f\n", + se_node.x(), se_node.y(), se_node.z() ); + fprintf( fp, "se_normal %.6f %.6f %.6f\n", + se_normal.x(), se_normal.y(), se_normal.z() ); + } + + if ( ! nw_flag ) { + fprintf( fp, "nw_node %.6f %.6f %.6f\n", + nw_node.x(), nw_node.y(), nw_node.z() ); + fprintf( fp, "nw_normal %.6f %.6f %.6f\n", + nw_normal.x(), nw_normal.y(), nw_normal.z() ); + } + + if ( ! ne_flag ) { + fprintf( fp, "ne_node %.6f %.6f %.6f\n", + ne_node.x(), ne_node.y(), ne_node.z() ); + fprintf( fp, "ne_normal %.6f %.6f %.6f\n", + ne_normal.x(), ne_normal.y(), ne_normal.z() ); + } + + if ( ! north_flag ) { + if ( (int)north_nodes.size() == 0 ) { + fprintf( fp, "n_null -999.0 -999.0 -999.0\n" ); + } else { + for ( int i = 0; i < (int)north_nodes.size(); ++i ) { + fprintf( fp, "n_node %.6f %.6f %.6f\n", + north_nodes[i].x(), north_nodes[i].y(), + north_nodes[i].z() ); + fprintf( fp, "n_normal %.6f %.6f %.6f\n", + north_normals[i].x(), north_normals[i].y(), + north_normals[i].z() ); + } + } + } + + if ( ! south_flag ) { + if ( (int)south_nodes.size() == 0 ) { + fprintf( fp, "s_null -999.0 -999.0 -999.0\n" ); + } else { + for ( int i = 0; i < (int)south_nodes.size(); ++i ) { + fprintf( fp, "s_node %.6f %.6f %.6f\n", + south_nodes[i].x(), south_nodes[i].y(), + south_nodes[i].z() ); + fprintf( fp, "s_normal %.6f %.6f %.6f\n", + south_normals[i].x(), south_normals[i].y(), + south_normals[i].z() ); + } + } + } + + if ( ! east_flag ) { + if ( (int)east_nodes.size() == 0 ) { + fprintf( fp, "e_null -999.0 -999.0 -999.0\n" ); + } else { + for ( int i = 0; i < (int)east_nodes.size(); ++i ) { + fprintf( fp, "e_node %.6f %.6f %.6f\n", + east_nodes[i].x(), east_nodes[i].y(), + east_nodes[i].z() ); + fprintf( fp, "e_normal %.6f %.6f %.6f\n", + east_normals[i].x(), east_normals[i].y(), + east_normals[i].z() ); + } + } + } + + if ( ! west_flag ) { + if ( (int)west_nodes.size() == 0 ) { + fprintf( fp, "w_null -999.0 -999.0 -999.0\n" ); + } else { + for ( int i = 0; i < (int)west_nodes.size(); ++i ) { + fprintf( fp, "w_node %.6f %.6f %.6f\n", + west_nodes[i].x(), west_nodes[i].y(), + west_nodes[i].z() ); + fprintf( fp, "w_normal %.6f %.6f %.6f\n", + west_normals[i].x(), west_normals[i].y(), + west_normals[i].z() ); + } + } + } + +#if 0 // not needed + point_list nodes = c.get_geod_nodes(); + Point3D p1, p2; + + for ( int i = 0; i < (int)north_segs.size(); ++i ) { + p1 = nodes[ north_segs[i].get_n1() ]; + p2 = nodes[ north_segs[i].get_n2() ]; + fprintf( fp, "n_seg %.6f %.6f %.6f %.6f\n", + p1.x(), p1.y(), p2.x(), p2.y() ); + } + + for ( int i = 0; i < (int)south_segs.size(); ++i ) { + p1 = nodes[ south_segs[i].get_n1() ]; + p2 = nodes[ south_segs[i].get_n2() ]; + fprintf( fp, "s_seg %.6f %.6f %.6f %.6f\n", + p1.x(), p1.y(), p2.x(), p2.y() ); + } + + for ( int i = 0; i < (int)east_segs.size(); ++i ) { + p1 = nodes[ east_segs[i].get_n1() ]; + p2 = nodes[ east_segs[i].get_n2() ]; + fprintf( fp, "e_seg %.6f %.6f %.6f %.6f\n", + p1.x(), p1.y(), p2.x(), p2.y() ); + } + + for ( int i = 0; i < (int)west_segs.size(); ++i ) { + p1 = nodes[ west_segs[i].get_n1() ]; + p2 = nodes[ west_segs[i].get_n2() ]; + fprintf( fp, "w_seg %.6f %.6f %.6f %.6f\n", + p1.x(), p1.y(), p2.x(), p2.y() ); + } +#endif + + fclose( fp ); + + command = "gzip --force --best " + file; + system(command.c_str()); +} + + +// insert normal into vector, extending it first if needed +void insert_normal( point_list& normals, Point3D n, int i ) { + Point3D empty( 0.0 ); + + // extend vector if needed + while ( i >= (int)normals.size() ) { + normals.push_back( empty ); + } + + normals[i] = n; +} + + +// fake a normal for a point which is basically straight up +static Point3D fake_normal( const Point3D& p ) { + Point3D radians = Point3D( p.x() * DEG_TO_RAD, + p.y() * DEG_TO_RAD, + p.z() ); + Point3D cart = fgGeodToCart(radians); + double len = Point3D(0.0).distance3D(cart); + cout << "len = " << len << endl; + cart /= len; + cout << "fake normal = " << cart << endl; + + return cart; +} + + +// reassemble the tile pieces (combining the shared data and our own +// data) +void FGMatch::assemble_tile( FGConstruct& c ) { + FGTriNodes new_nodes; + new_nodes.clear(); + + point_list new_normals; + new_normals.clear(); + + FGTriSegments new_segs; + new_segs.clear(); + + // add the corner points + int sw_index = new_nodes.unique_add( sw_node ); + insert_normal( new_normals, sw_normal, sw_index ); + + int se_index = new_nodes.unique_add( se_node ); + insert_normal( new_normals, se_normal, se_index ); + + int ne_index = new_nodes.unique_add( ne_node ); + insert_normal( new_normals, ne_normal, ne_index ); + + int nw_index = new_nodes.unique_add( nw_node ); + insert_normal( new_normals, nw_normal, nw_index ); + + cout << "after adding corners:" << endl; + cout << " new_nodes = " << new_nodes.size() << endl; + cout << " new normals = " << new_normals.size() << endl; + + // add the edge points + + int index; + + for ( int i = 0; i < (int)north_nodes.size(); ++i ) { + index = new_nodes.unique_add( north_nodes[i] ); + insert_normal( new_normals, north_normals[i], index ); + } + + for ( int i = 0; i < (int)south_nodes.size(); ++i ) { + index = new_nodes.unique_add( south_nodes[i] ); + insert_normal( new_normals, south_normals[i], index ); + } + + for ( int i = 0; i < (int)east_nodes.size(); ++i ) { + index = new_nodes.unique_add( east_nodes[i] ); + insert_normal( new_normals, east_normals[i], index ); + } + + // cout << "Total west nodes = " << west_nodes.size() << endl; + for ( int i = 0; i < (int)west_nodes.size(); ++i ) { + // cout << "adding west node " << west_nodes[i] << endl; + index = new_nodes.unique_add( west_nodes[i] ); + insert_normal( new_normals, west_normals[i], index ); + } + + cout << "after adding edges:" << endl; + cout << " new_nodes = " << new_nodes.size() << endl; + cout << " new normals = " << new_normals.size() << endl; + + // add the body points + for ( int i = 0; i < (int)body_nodes.size(); ++i ) { + index = new_nodes.unique_add( body_nodes[i] ); + insert_normal( new_normals, body_normals[i], index ); + } + + cout << "after adding body points:" << endl; + cout << " new_nodes = " << new_nodes.size() << endl; + cout << " new normals = " << new_normals.size() << endl; + + // add the edge segments + new_segs.unique_divide_and_add( new_nodes.get_node_list(), + FGTriSeg(sw_index, se_index, 1) ); + new_segs.unique_divide_and_add( new_nodes.get_node_list(), + FGTriSeg(se_index, ne_index, 1) ); + new_segs.unique_divide_and_add( new_nodes.get_node_list(), + FGTriSeg(ne_index, nw_index, 1) ); + new_segs.unique_divide_and_add( new_nodes.get_node_list(), + FGTriSeg(nw_index, sw_index, 1) ); + cout << "after adding edge segments:" << endl; + cout << " new_nodes = " << new_nodes.size() << endl; + cout << " new normals = " << new_normals.size() << endl; + + // add the body segments + + point_list geod_nodes = c.get_geod_nodes(); + + FGTriSeg seg; + Point3D p1, p2; + int n1, n2, marker; + + triseg_list_iterator current = body_segs.begin(); + triseg_list_iterator last = body_segs.end(); + + for ( ; current != last; ++current ) { + seg = *current; + + // get the original points (x,y,z) + p1 = geod_nodes[ seg.get_n1() ]; + p2 = geod_nodes[ seg.get_n2() ]; + marker = seg.get_boundary_marker(); + + // make sure these points are in the new node list (and get + // their new index) + n1 = new_nodes.unique_add( p1 ); + if ( n1 >= (int)new_normals.size() ) { + cout << "Adding a segment resulted in a new node, faking a normal" + << endl; + Point3D fake = fake_normal( p1 ); + insert_normal( new_normals, fake, n1 ); + } + + n2 = new_nodes.unique_add( p2 ); + if ( n2 >= (int)new_normals.size() ) { + cout << "Adding a segment resulted in a new node, faking a normal" + << endl; + Point3D fake = fake_normal( p2 ); + insert_normal( new_normals, fake, n2 ); + } + + // add the segment using the new indices + new_segs.unique_divide_and_add( new_nodes.get_node_list(), + FGTriSeg(n1, n2, marker) ); + } + + c.set_tri_nodes( new_nodes ); + c.set_point_normals( new_normals ); + c.set_tri_segs( new_segs ); + + cout << "after adding all segments (should be the same):" << endl; + cout << " new_nodes = " << new_nodes.size() << endl; + cout << " new normals = " << new_normals.size() << endl; + +} diff --git a/src/BuildTiles/Match/match.hxx b/src/BuildTiles/Match/match.hxx new file mode 100644 index 00000000..96b1e01e --- /dev/null +++ b/src/BuildTiles/Match/match.hxx @@ -0,0 +1,109 @@ +// match.hxx -- Class to help match up tile edges +// +// Written by Curtis Olson, started April 1999. +// +// 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$ + + +#ifndef _MATCH_HXX +#define _MATCH_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include +#include +#include + +#include
+ + +class FGMatch { + +private: + + // nodes breakdown + Point3D sw_node, se_node, ne_node, nw_node; + point_list north_nodes, south_nodes, east_nodes, west_nodes; + point_list body_nodes; + + // normals breakdown + Point3D sw_normal, se_normal, ne_normal, nw_normal; + point_list north_normals, south_normals, east_normals, west_normals; + point_list body_normals; + + // flags + bool sw_flag, se_flag, ne_flag, nw_flag; + bool north_flag, south_flag, east_flag, west_flag; + + // segment breakdown + triseg_list north_segs, south_segs, east_segs, west_segs; + triseg_list body_segs; + +public: + + enum neighbor_type { + SW_Corner = 1, + SE_Corner = 2, + NE_Corner = 3, + NW_Corner = 4, + NORTH = 5, + SOUTH = 6, + EAST = 7, + WEST = 8 + }; + + // Constructor + FGMatch( void ); + + // Destructor + ~FGMatch( void ); + + // load any previously existing shared data from all neighbors (if + // shared data for a component exists set that components flag to + // true + void load_neighbor_shared( FGConstruct& c ); + + // scan the specified share file for the specified information + void scan_share_file( const string& dir, const FGBucket& b, + neighbor_type search, neighbor_type dest ); + + // try to find info for the specified shared component + void load_shared( const FGConstruct& c, neighbor_type n ); + + // split up the tile between the shared edge points, normals, and + // segments and the body. This must be done after calling + // load_neighbor_data() and will ignore any shared data from the + // current tile that already exists from a neighbor. + void split_tile( FGConstruct& c ); + + // write the new shared edge points, normals, and segments for + // this tile + void write_shared( FGConstruct& c ); + + // reassemble the tile pieces (combining the shared data and our + // own data) + void assemble_tile( FGConstruct& c ); +}; + + +#endif // _MATCH_HXX diff --git a/src/BuildTiles/Parallel/Makefile.am b/src/BuildTiles/Parallel/Makefile.am new file mode 100644 index 00000000..11779441 --- /dev/null +++ b/src/BuildTiles/Parallel/Makefile.am @@ -0,0 +1,14 @@ +bin_PROGRAMS = fgfs-tools-server fgfs-tools-client + +fgfs_tools_server_SOURCES = server.cxx + +fgfs_tools_server_LDADD = -lsgbucket -lsgmisc + +fgfs_tools_client_SOURCES = client.cxx + +fgfs_tools_client_LDADD = -lsgbucket -lsgmisc + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Construct diff --git a/src/BuildTiles/Parallel/client.cxx b/src/BuildTiles/Parallel/client.cxx new file mode 100644 index 00000000..52c543e7 --- /dev/null +++ b/src/BuildTiles/Parallel/client.cxx @@ -0,0 +1,283 @@ +/* remote_exec.c -- Written by Curtis Olson */ +/* -- for CSci 5502 */ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_SYS_PARAM_H +# include // BSD macro definitions +#endif + +#include // FD_ISSET(), etc. +#include +#include +#include +#include +#include +#include + +#include + +#include +#include // atoi() +#include // bcopy() + +#include +#include + +#include + + +#define MAXBUF 1024 +#define BUSY_WAIT_TIME 30 + + +string work_base = ""; +string output_base = ""; + + +// check if it is ok to run +void check_master_switch() { + string file = work_base + "/Status/MASTER_ON"; + + FILE *fp = fopen( file.c_str(), "r" ); + if ( fp == NULL ) { + cout << "MASTER_ON file, " << file << " not found ... exiting." << endl; + exit(0); + } + + fclose( fp ); +} + + +// check if the host system is free of interactive users +int system_free() { + +#ifndef BSD + struct utmp *uptr; + + setutent(); + + while ( (uptr = getutent()) != NULL ) { + // cout << "NULL = " << NULL << " uptr = " << uptr << endl; + // cout << "user = ut_user = " << uptr->ut_user << endl; + // cout << "user = ut_type = " << uptr->ut_type << endl; + if (uptr->ut_type == USER_PROCESS) { + // found someone + endutent(); + return 0; + } + } + + endutent(); +#else +# warning Port me +#endif + + return 1; +} + + +int make_socket (char *host, unsigned short int port) { + int sock; + struct sockaddr_in name; + struct hostent *hp; + + // Create the socket. + sock = socket (PF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror ("socket"); + exit (EXIT_FAILURE); + } + + // specify address family + name.sin_family = AF_INET; + + // get the hosts official name/info + hp = gethostbyname(host); + + // Connect this socket to the host and the port specified on the + // command line + bcopy(hp->h_addr, &(name.sin_addr.s_addr), hp->h_length); + name.sin_port = htons(port); + + if ( connect(sock, (struct sockaddr *) &name, + sizeof(struct sockaddr_in)) < 0 ) + { + close(sock); + perror("Cannot connect to stream socket"); + return -1; + } + + return sock; +} + + +// connect to the server and get the next task +long int get_next_task( const string& host, int port, long int last_tile ) { + long int tile; + int sock, len; + fd_set ready; + char message[256]; + + // loop till we get a socket connection + while ( (sock = make_socket( (char *)host.c_str(), port )) < 0 ) { + // check if the master switch is on + check_master_switch(); + + sleep(1); + } + + // build a command string from the argv[]'s + sprintf(message, "%ld", last_tile); + + // send command and arguments to remote server + if ( write(sock, message, sizeof(message)) < 0 ) { + perror("Cannot write to stream socket"); + } + + // loop until remote program finishes + cout << "querying server for next task ..." << endl; + + FD_ZERO(&ready); + FD_SET(sock, &ready); + + // block until input from sock + select(32, &ready, 0, 0, NULL); + cout << " received reply" << endl; + + if ( FD_ISSET(sock, &ready) ) { + /* input coming from socket */ + if ( (len = read(sock, message, 1024)) > 0 ) { + message[len] = '\0'; + tile = atoi(message); + cout << " tile to construct = " << tile << endl; + close(sock); + return tile; + } else { + close(sock); + return -1; + } + } + + close(sock); + return -1; +} + + +// build the specified tile, return true if contruction completed +// successfully +bool construct_tile( const FGBucket& b, const string& result_file ) { + double angle = 10.0; + bool still_trying = true; + + while ( still_trying ) { + still_trying = false; + char angle_str[256]; + sprintf(angle_str, "%.0f", angle); + string command = "fgfs-construct "; + command += angle_str; + command += " " + work_base + " " + output_base + " " + + b.gen_index_str() + " > " + result_file + " 2>&1"; + cout << command << endl; + + system( command.c_str() ); + + FILE *fp = fopen( result_file.c_str(), "r" ); + char line[256]; + while ( fgets( line, 256, fp ) != NULL ) { + string line_str = line; + line_str = line_str.substr(0, line_str.length() - 1); + // cout << line_str << endl; + if ( line_str == "[Finished successfully]" ) { + fclose(fp); + return true; + } else if + ( (line_str.substr(0, 31) == "Error: Ran out of precision at") + || (line_str.substr(0, 22) == "Error: Out of memory.") + || (line_str.substr(0, 23) == "Error: Too many nodes.") ) { + if ( angle > 9.0 ) { + angle = 5.0; + still_trying = true; + } else if ( angle > 4.0 ) { + angle = 0.0; + still_trying = true; + } + } + + } + fclose(fp); + + if ( !still_trying && ( angle > 0.0 ) ) { + // build died for some reason ... lets try one last time + // with an interior angle restriction of 0 + angle = 0.0; + still_trying = true; + } + } + return false; +} + + +main(int argc, char *argv[]) { + long int tile, last_tile; + bool rude = false; + bool result; + + // Check usage + if ( argc < 5 ) { + printf("Usage: %s remote_machine port work_base output_base [ -r ]\n", + argv[0]); + exit(1); + } + + string host = argv[1]; + int port = atoi( argv[2] ); + work_base = argv[3]; + output_base = argv[4]; + + if ( argc == 6 ) { + string option = argv[5]; + if ( option == "-r" ) { + cout << "Running in RUDE mode!" << endl; + rude = true; + } + } + // get hostname and pid + char hostname[MAXBUF]; + gethostname( hostname, MAXBUF ); + pid_t pid = getpid(); + + char tmp[MAXBUF]; + sprintf(tmp, "/tmp/result.%s.%d", hostname, pid); + string result_file = tmp; + + last_tile = 0; + + // check if the master switch is on + check_master_switch(); + + while ( (tile = get_next_task( host, port, last_tile )) >= 0 ) { + result = construct_tile( FGBucket(tile), result_file ); + if ( result ) { + last_tile = tile; + } else { + last_tile = -tile; + } + + // check if the master switch is on + check_master_switch(); + + // niceness policy: This whole process should run niced. But + // additionally, if there is interactive use, we will sleep + // for 60 seconds between each tile to stagger out the load + // and impose less of an impact on the machine. + if ( !system_free() && !rude) { + cout << "System has interactive use, sleeping for " + << BUSY_WAIT_TIME << " seconds..." << endl; + sleep( BUSY_WAIT_TIME ); + } + } +} diff --git a/src/BuildTiles/Parallel/server.cxx b/src/BuildTiles/Parallel/server.cxx new file mode 100644 index 00000000..872a772e --- /dev/null +++ b/src/BuildTiles/Parallel/server.cxx @@ -0,0 +1,406 @@ +// remote_server.c -- Written by Curtis Olson +// -- for CSci 5502 + + +#include +#include +#include // FD_ISSET(), etc. +#include // for stat() +#include // for time(); +#include + +#include // bind +#include + +#include +#include + +#include +#include + +#include + + +#if defined (sun) +# define WAIT_ANY (pid_t)-1 +#endif + +#define MAXBUF 1024 + + +static double start_lon, start_lat; +static double lat = 0.0; +static double lon = 0.0; +static double dy = 0.0; +static int pass = 0; + + +int make_socket (unsigned short int* port) { + int sock; + struct sockaddr_in name; + socklen_t length; + + // Create the socket. + sock = socket (PF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror ("socket"); + exit (EXIT_FAILURE); + } + + // Give the socket a name. + name.sin_family = AF_INET; + name.sin_addr.s_addr = INADDR_ANY; + name.sin_port = 0 /* htons (port) */; + name.sin_addr.s_addr = htonl (INADDR_ANY); + if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) { + perror ("bind"); + exit (EXIT_FAILURE); + } + + // Find the assigned port number + length = sizeof(struct sockaddr_in); + if ( getsockname(sock, (struct sockaddr *) &name, &length) ) { + perror("Cannot get socket's port number"); + } + *port = ntohs(name.sin_port); + + return sock; +} + + +#if 0 +// let's keep these two around for a while in case we need to revive +// them + +// return true if file exists +static bool file_exists( const string& file ) { + struct stat buf; + + if ( stat( file.c_str(), &buf ) == 0 ) { + return true; + } else { + return false; + } +} + + + +// check if the specified tile has data defined for it [ depricated ] +static bool has_data( const string& path, const FGBucket& b ) { + string dem_file = path + ".dem" + "/" + b.gen_base_path() + + "/" + b.gen_index_str() + ".dem"; + if ( file_exists( dem_file ) ) { + return true; + } + + dem_file += ".gz"; + if ( file_exists( dem_file ) ) { + return true; + } + + return false; +} +#endif + + +// initialize the tile counting system +void init_tile_count( const string& chunk ) { + // pre-pass + pass = 0; + + // initial bogus value + lat = 100; + + // determine tile height + FGBucket tmp1( 0.0, 0.0 ); + dy = tmp1.get_height(); + + string lons = chunk.substr(0, 4); + string lats = chunk.substr(4, 3); + cout << "lons = " << lons << " lats = " << lats << endl; + + string horz = lons.substr(0, 1); + start_lon = atof( lons.substr(1,3).c_str() ); + if ( horz == "w" ) { start_lon *= -1; } + + string vert = lats.substr(0, 1); + start_lat = atof( lats.substr(1,2).c_str() ); + if ( vert == "s" ) { start_lat *= -1; } + + cout << "start_lon = " << start_lon << " start_lat = " << start_lat + << endl; +} + + +// return the next tile +long int get_next_tile() { + FGBucket b; + static double shift_over = 0.0; + static double shift_up = 0.0; + static bool first_time = true; + static time_t start_seconds, seconds; + static int counter; + static int global_counter; + + // first time this routine is called, init counters + if ( first_time ) { + first_time = false; + start_seconds = seconds = time(NULL); + counter = global_counter = 0; + } + + // cout << "lon = " << lon << " lat = " << lat << endl; + // cout << "start_lat = " << start_lat << endl; + + if ( lon > start_lon + 10.0 ) { + // increment to next row + // skip every other row (to avoid two clients working on + // adjacent tiles) + lat += 2.0 * dy; + + FGBucket tmp( 0.0, lat ); + double dx = tmp.get_width(); + lon = start_lon + (shift_over*dx) + (dx*0.5); + } + + if ( lat > start_lat + 10.0 ) { + ++pass; + if ( pass == 1 ) { + shift_over = 0.0; + shift_up = 0.0; + } else if ( pass == 2 ) { + shift_over = 1.0; + shift_up = 0.0; + } else if ( pass == 3 ) { + shift_over = 0.0; + shift_up = 1.0; + } else if ( pass == 4 ) { + shift_over = 1.0; + shift_up = 1.0; + } else { + return -1; + } + + // reset lat + // lat = -89.0 + (shift_up*dy) - (dy*0.5); + // lat = 27.0 + (0*dy) + (dy*0.5); + lat = start_lat + (shift_up*dy) + (dy*0.5); + + // reset lon + FGBucket tmp( 0.0, lat ); + double dx = tmp.get_width(); + // lon = -82 + (shift_over*dx) + (dx*0.5); + lon = start_lon + (shift_over*dx) + (dx*0.5); + + cout << "starting pass = " << pass + << " with lat = " << lat << " lon = " << lon << endl; + } + + // if ( ! start_lon ) { + // lon = -180 + dx * 0.5; + // } else { + // start_lon = false; + // } + + b = FGBucket( lon, lat ); + + // increment to next tile + FGBucket tmp( 0.0, lat ); + double dx = tmp.get_width(); + + // skip every other column (to avoid two clients working on + // adjacent tiles) + lon += 2.0 * dx; + + ++global_counter; + ++counter; + + time_t tmp_time = time(NULL); + if ( tmp_time != seconds ) { + seconds = tmp_time; + cout << "Current tile per second rate = " << counter << endl; + cout << "Overall tile per second rate = " + << global_counter / ( seconds - start_seconds ) << endl; + counter = 0; + } + + return b.gen_index(); +} + + +// log a pending tile (has been given out as a taks for some client) +void log_pending_tile( const string& path, long int tile ) { + FGBucket b(tile); + + string pending_file = path + "/" + b.gen_index_str() + ".pending"; + + string command = "touch " + pending_file; + system( command.c_str() ); +} + + +// a tile is finished (removed the .pending file) +void log_finished_tile( const string& path, long int tile ) { + FGBucket b(tile); + + string finished_file = path + "/" + b.gen_index_str() + ".pending"; + // cout << "unlinking " << finished_file << endl; + unlink( finished_file.c_str() ); +} + + +// make note of a failed tile +void log_failed_tile( const string& path, long int tile ) { + FGBucket b(tile); + + string failed_file = path + "/" + b.gen_index_str() + ".failed"; + + string command = "touch " + failed_file; + system( command.c_str() ); + + cout << "logged bad tile = " << tile << endl; +} + + +// display usage and exit +void usage( const string name ) { + cout << "Usage: " << name << " chunk1 chunk2 ..." + << endl; + cout << "\twhere chunk represent the south west corner of a 10x10 degree" + << endl; + cout << "\tsquare and is of the form [we]xxx[ns]yy. For example:" + << endl; + cout << "\tw020n10 e150s70" << endl; + exit(-1); +} + + +int main( int argc, char **argv ) { + int arg_counter; + long int next_tile; + int sock, msgsock, length, pid; + fd_set ready; + short unsigned int port; + + // quick argument sanity check + if ( argc < 4 ) { + usage( argv[0] ); + } + + string work_base = argv[1]; + string output_base = argv[2]; + + arg_counter = 3; + + // initialize tile counter / incrementer + init_tile_count( argv[arg_counter++] ); + + // temp test + // while ( (next_tile = get_next_tile()) != -1 ) { + // cout << next_tile << " " << FGBucket(next_tile) << endl; + // } + // cout << "done" << endl; + // exit(0); + + // create the status directory + string status_dir = work_base + "/Status"; + string command = "mkdir -p " + status_dir; + system( command.c_str() ); + + // setup socket to listen on + sock = make_socket( &port ); + cout << "socket is connected to port = " << port << endl; + + // Specify the maximum length of the connection queue + listen(sock, 10); + + for ( ;; ) { + FD_ZERO(&ready); + FD_SET(sock, &ready); + + // block until we get some input on sock + select(32, &ready, 0, 0, NULL); + + if ( FD_ISSET(sock, &ready) ) { + // printf("%d %d Incomming message --> ", getpid(), pid); + + // get the next tile to work on + next_tile = get_next_tile(); + + if ( next_tile == -1 ) { + // end of chunk see if there are more chunks + if ( arg_counter < argc ) { + // still more chunks to process + init_tile_count( argv[arg_counter++] ); + next_tile = get_next_tile(); + } + } + + cout << "Bucket = " << FGBucket(next_tile) + << " (" << pass << ")" << endl; + + log_pending_tile( status_dir, next_tile ); + // cout << "next tile = " << next_tile << endl;; + + msgsock = accept(sock, 0, 0); + // cout << "msgsock = " << msgsock << endl; + + // spawn a child + pid = fork(); + + if ( pid < 0 ) { + // error + perror("Cannot fork child process"); + exit(-1); + } else if ( pid > 0 ) { + // This is the parent + close(msgsock); + + // clean up all of our zombie children + int status; + while ( (pid = waitpid( WAIT_ANY, &status, WNOHANG )) > 0 ) { + // cout << "waitpid(): pid = " << pid + // << " status = " << status << endl; + } + } else { + // This is the child + + // cout << "new process started to handle new connection for " + // << next_tile << endl; + + // Read client's message (which is the status of the + // last scenery creation task.) + char buf[MAXBUF]; + if ( (length = read(msgsock, buf, MAXBUF)) < 0) { + perror("Cannot read command"); + exit(-1); + } + buf[length] = '\0'; + long int returned_tile = atoi(buf); + cout << "client returned = " << returned_tile << endl; + + // record status + if ( returned_tile < 0 ) { + // failure + log_failed_tile( status_dir, -returned_tile ); + log_finished_tile( status_dir, -returned_tile ); + } else { + // success + log_finished_tile( status_dir, returned_tile ); + } + + // reply to the client + char message[MAXBUF]; + sprintf(message, "%ld", next_tile); + length = strlen(message); + if ( write(msgsock, message, length) < 0 ) { + perror("Cannot write to stream socket"); + } + close(msgsock); + // cout << "process for " << next_tile << " ended" << endl; + + exit(0); + } + } + } +} diff --git a/src/BuildTiles/Triangulate/Makefile.am b/src/BuildTiles/Triangulate/Makefile.am new file mode 100644 index 00000000..1a33eec0 --- /dev/null +++ b/src/BuildTiles/Triangulate/Makefile.am @@ -0,0 +1,10 @@ +noinst_LIBRARIES = libTriangulate.a + +libTriangulate_a_SOURCES = \ + triangle.cxx triangle.hxx \ + trieles.cxx trieles.hxx + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Construct diff --git a/src/BuildTiles/Triangulate/triangle.cxx b/src/BuildTiles/Triangulate/triangle.cxx new file mode 100644 index 00000000..bd6cf566 --- /dev/null +++ b/src/BuildTiles/Triangulate/triangle.cxx @@ -0,0 +1,529 @@ +// 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$ + + +#include +#include + +#include "triangle.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 FGPolyList& gpc_polys ) +{ + int debug_counter = 0; + int index; + + in_nodes.clear(); + in_segs.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 + FGPolygon gpc_poly; + const_poly_list_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; + debug_counter = 0; + 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.contours() << endl; + + if (gpc_poly.contours() <= 0 ) { + cout << "FATAL ERROR! no contours in this polygon" << endl; + exit(-1); + } + + int j; + for ( j = 0; j < gpc_poly.contours(); ++j ) { + cout << " processing contour = " << j << ", nodes = " + << gpc_poly.contour_size( j ) << ", hole = " + << gpc_poly.get_hole_flag( j ) << endl; + + // sprintf(junkn, "g.%d", junkc++); + // junkfp = fopen(junkn, "w"); + + for ( int k = 0; k < gpc_poly.contour_size( j ); k++ ) { + Point3D p = gpc_poly.get_pt( j, k ); + index = in_nodes.unique_add( p ); + // junkp = in_nodes.get_node( index ); + // fprintf(junkfp, "%.4f %.4f\n", junkp.x(), junkp.y()); + // cout << " - " << index << endl; + } + // fprintf(junkfp, "%.4f %.4f\n", + // gpc_poly->contour[j].vertex[0].x, + // gpc_poly->contour[j].vertex[0].y); + // fclose(junkfp); + } + + Point3D inside_pt; + for ( j = 0; j < gpc_poly.contours(); ++j ) { + inside_pt = calc_point_inside( gpc_poly, j, in_nodes ); + gpc_poly.set_point_inside( j, inside_pt ); + } + + polylist[i].push_back( gpc_poly ); + +#if 0 + // temporary ... write out hole/polygon info for debugging + for ( j = 0; j < (int)gpc_poly.contours(); ++j ) { + char pname[256]; + sprintf(pname, "poly%02d-%02d-%02d", i, debug_counter, j); + cout << "writing to " << pname << endl; + FILE *fp = fopen( pname, "w" ); + Point3D point; + for ( int k = 0; k < gpc_poly.contour_size( j ); ++k ) { + point = gpc_poly.get_pt( j, k ); + fprintf( fp, "%.6f %.6f\n", point.x(), point.y() ); + } + point = gpc_poly.get_pt( j, 0 ); + fprintf( fp, "%.6f %.6f\n", point.x(), point.y() ); + fclose(fp); + + char hname[256]; + sprintf(hname, "hole%02d-%02d-%02d", i, debug_counter, j); + FILE *fh = fopen( hname, "w" ); + point = gpc_poly.get_point_inside( j ); + fprintf( fh, "%.6f %.6f\n", point.x(), point.y() ); + fclose(fh); + } + + // cout << "type a letter + enter to continue: "; + // string input; + // cin >> input; +#endif + + ++debug_counter; + } + } + + // 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. + + cout << "building segment list" << endl; + int i1, i2; + Point3D p1, p2; + point_list node_list = in_nodes.get_node_list(); + FGPolygon poly; + + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + cout << "area type = " << i << endl; + poly_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; + cout << " processing a polygon with contours = " + << poly.contours() << endl; + for ( int j = 0; j < (int)poly.contours(); ++j) { + for ( int k = 0; k < (int)(poly.contour_size(j) - 1); ++k ) { + p1 = poly.get_pt( j, k ); + p2 = poly.get_pt( j, k + 1 ); + i1 = in_nodes.find( p1 ); + i2 = in_nodes.find( p2 ); + // calc_line_params(i1, i2, &m, &b); + if ( i == (int)HoleArea ) { + // mark as a boundary + in_segs.unique_divide_and_add( node_list, + FGTriSeg(i1, i2, 1) ); + } else { + // non boundary + in_segs.unique_divide_and_add( node_list, + FGTriSeg(i1, i2, 0) ); + } + } + p1 = poly.get_pt( j, 0 ); + p2 = poly.get_pt( j, poly.contour_size(j) - 1 ); + i1 = in_nodes.find( p1 ); + i2 = in_nodes.find( p2 ); + // calc_line_params(i1, i2, &m, &b); + if ( i == (int)HoleArea ) { + // mark as a boundary + in_segs.unique_divide_and_add( node_list, + FGTriSeg(i1, i2, 1) ); + } else { + // non boundary + in_segs.unique_divide_and_add( node_list, + FGTriSeg(i1, i2, 0) ); + } + } + } + } + + return 0; +} + + +// populate this class based on the specified gpc_polys list +int FGTriangle::rebuild( FGConstruct& c ) { + in_nodes.clear(); + in_segs.clear(); + + in_nodes = c.get_tri_nodes(); + in_segs = c.get_tri_segs(); + + 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 1\n", out->numberofsegments); + for (int i = 0; i < out->numberofsegments; ++i) { + fprintf(fp, "%d %d %d %d\n", + i, out->segmentlist[2*i], out->segmentlist[2*i + 1], + out->segmentmarkerlist[i] ); + } + 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); +} + + +// Front end triangulator for polygon list. Allocates and builds up +// all the needed structures for the triangulator, runs it, copies the +// results, and frees all the data structures used by the +// triangulator. "pass" can be 1 or 2. 1 = first pass which +// generates extra nodes for a better triangulation. 2 = second pass +// after split/reassem where we don't want any extra nodes generated. + +int FGTriangle::run_triangulate( const string& angle, const int pass ) { + FGPolygon 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)); + + for ( int i = 0; i < in.numberofpoints; ++i ) { + in.pointlist[2*i] = node_list[i].x(); + in.pointlist[2*i + 1] = node_list[i].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] = node_list[i].z(); + } + + 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 = in_segs.get_seg_list(); + in.numberofsegments = seg_list.size(); + in.segmentlist = (int *) malloc(in.numberofsegments * 2 * sizeof(int)); + in.segmentmarkerlist = (int *) malloc(in.numberofsegments * sizeof(int)); + + 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(); + } + s_current = seg_list.begin(); + s_last = seg_list.end(); + counter = 0; + for ( ; s_current != s_last; ++s_current ) { + in.segmentmarkerlist[counter++] = s_current->get_boundary_marker(); + } + + // hole list (make holes for airport ignore areas) + in.numberofholes = polylist[(int)HoleArea].size(); + in.holelist = (REAL *) malloc(in.numberofholes * 2 * sizeof(REAL)); + + poly_list_iterator h_current, h_last; + h_current = polylist[(int)HoleArea].begin(); + h_last = polylist[(int)HoleArea].end(); + counter = 0; + for ( ; h_current != h_last; ++h_current ) { + poly = *h_current; + for ( int j = 0; j < poly.contours(); ++j ) { + p = poly.get_point_inside( j ); + 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 ) { + poly_list_iterator h_current, h_last; + h_current = polylist[i].begin(); + h_last = polylist[i].end(); + for ( ; h_current != h_last; ++h_current ) { + poly = *h_current; + for ( int j = 0; j < poly.contours(); ++j ) { + if ( ! poly.get_hole_flag( j ) ) { + ++in.numberofregions; + } + } + } + } + + in.regionlist = (REAL *) malloc(in.numberofregions * 4 * sizeof(REAL)); + counter = 0; + for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { + poly_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; + for ( int j = 0; j < poly.contours(); ++j ) { + if ( ! poly.get_hole_flag( j ) ) { + p = poly.get_point_inside( j ); + cout << "Region point = " << p << endl; + 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; + if ( pass == 1 ) { + // use a quality value of 10 (q10) meaning no interior + // triangle angles less than 10 degrees + // tri_options = "pczAen"; + if ( angle == "0" ) { + tri_options = "pczAen"; + } else { + tri_options = "pczq" + angle + "Aen"; + } + // // string tri_options = "pzAen"; + // // string tri_options = "pczq15S400Aen"; + } else if ( pass == 2 ) { + // no new points on boundary (Y), no internal segment + // splitting (YY), no quality refinement (q) + tri_options = "pczYYAen"; + } else { + cout << "unknown pass number = " << pass + << " in FGTriangle::run_triangulate()" << endl; + exit(-1); + } + cout << "Triangulation with options = " << tri_options << endl; + + triangulate( (char *)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], + out.pointattributelist[i] ); + // cout << "point = " << p << endl; + out_nodes.simple_add( p ); + } + + // segments + out_segs.clear(); + for ( int i = 0; i < out.numberofsegments; ++i ) { + out_segs.unique_add( FGTriSeg( out.segmentlist[2*i], + out.segmentlist[2*i+1], + out.segmentmarkerlist[i] ) ); + } + + // 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; +} + + diff --git a/src/BuildTiles/Triangulate/triangle.hxx b/src/BuildTiles/Triangulate/triangle.hxx new file mode 100644 index 00000000..c998f505 --- /dev/null +++ b/src/BuildTiles/Triangulate/triangle.hxx @@ -0,0 +1,106 @@ +// 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$ + + +#ifndef _TRIANGLE_HXX +#define _TRIANGLE_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include +#include + +#include +#include +#include
+ +#include +#include +#include +#include + +#define REAL double +extern "C" { +#include +} + +#include "trieles.hxx" + + +class FGTriangle { + +private: + + // list of nodes + FGTriNodes in_nodes; + FGTriNodes out_nodes; + + // list of segments + FGTriSegments in_segs; + FGTriSegments out_segs; + + // polygon list + poly_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 FGPolyList& gpc_polys ); + + // populate this class based on the specified gpc_polys list + int rebuild( FGConstruct& c ); + + // Front end triangulator for polygon list. Allocates and builds + // up all the needed structures for the triangulator, runs it, + // copies the results, and frees all the data structures used by + // the triangulator. "pass" can be 1 or 2. 1 = first pass which + // generates extra nodes for a better triangulation. 2 = second + // pass after split/reassem where we don't want any extra nodes + // generated. + int run_triangulate( const string& angle, const int pass ); + + 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; } + inline FGTriSegments get_out_segs() const { return out_segs; } +}; + + +#endif // _TRIANGLE_HXX + + diff --git a/src/BuildTiles/Triangulate/trieles.cxx b/src/BuildTiles/Triangulate/trieles.cxx new file mode 100644 index 00000000..f3b66d6d --- /dev/null +++ b/src/BuildTiles/Triangulate/trieles.cxx @@ -0,0 +1,26 @@ +// 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$ + + +#include "trieles.hxx" + + diff --git a/src/BuildTiles/Triangulate/trieles.hxx b/src/BuildTiles/Triangulate/trieles.hxx new file mode 100644 index 00000000..29cb72e2 --- /dev/null +++ b/src/BuildTiles/Triangulate/trieles.hxx @@ -0,0 +1,75 @@ +// 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$ + + +#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 + + diff --git a/src/Include/config.h b/src/Include/config.h new file mode 100644 index 00000000..f71c69a2 --- /dev/null +++ b/src/Include/config.h @@ -0,0 +1,208 @@ +/* Include/config.h. Generated automatically by configure. */ +/* Include/config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if you don't have vprintf but do have _doprnt. */ +/* #undef HAVE_DOPRNT */ + +/* Define if you have the vprintf function. */ +#define HAVE_VPRINTF 1 + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define to `unsigned' if doesn't define. */ +/* #undef size_t */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* Define if the X Window System is missing or not being used. */ +/* #undef X_DISPLAY_MISSING */ + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + + +/* Define to eliminate all trace of debugging messages such as for a + release build */ +/* #undef FG_NDEBUG */ + + +/* Define to enable 3dfx/glide render in a window hack under unix. + This probably won't work under windows. */ +/* #undef XMESA */ +/* #undef FX */ + +/* Define if you don't have vprintf but do have _doprnt. */ +/* #undef HAVE_DOPRNT */ + +/* Define if you have the vprintf function. */ +#define HAVE_VPRINTF 1 + +/* Define to package name */ +#define PACKAGE "TerraGear" + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define to `unsigned' if doesn't define. */ +/* #undef size_t */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* Define to version number */ +#define VERSION "0.0.0" + +/* Define if compiling on a Winbloze (95, NT, etc.) platform */ +/* #undef WIN32 */ + +/* Define if the X Window System is missing or not being used. */ +/* #undef X_DISPLAY_MISSING */ + +/* Define if you have the GetLocalTime function. */ +/* #undef HAVE_GETLOCALTIME */ + +/* Define if you have the bcopy function. */ +#define HAVE_BCOPY 1 + +/* Define if you have the ftime function. */ +#define HAVE_FTIME 1 + +/* Define if you have the getitimer function. */ +#define HAVE_GETITIMER 1 + +/* Define if you have the getrusage function. */ +#define HAVE_GETRUSAGE 1 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the memcpy function. */ +#define HAVE_MEMCPY 1 + +/* Define if you have the mktime function. */ +#define HAVE_MKTIME 1 + +/* Define if you have the rand function. */ +#define HAVE_RAND 1 + +/* Define if you have the random function. */ +#define HAVE_RANDOM 1 + +/* Define if you have the rint function. */ +#define HAVE_RINT 1 + +/* Define if you have the setitimer function. */ +#define HAVE_SETITIMER 1 + +/* Define if you have the signal function. */ +#define HAVE_SIGNAL 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the timegm function. */ +#define HAVE_TIMEGM 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define if you have the header file. */ +#define HAVE_GPC_H 1 + +/* Define if you have the header file. */ +#define HAVE_MALLOC_H 1 + +/* Define if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +#define HAVE_VALUES_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_WINBASE_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define if you have the GL library (-lGL). */ +#define HAVE_LIBGL 1 + +/* Define if you have the GLU library (-lGLU). */ +/* #undef HAVE_LIBGLU */ + +/* Define if you have the GLcore library (-lGLcore). */ +/* #undef HAVE_LIBGLCORE */ + +/* Define if you have the ICE library (-lICE). */ +#define HAVE_LIBICE 1 + +/* Define if you have the MesaGL library (-lMesaGL). */ +/* #undef HAVE_LIBMESAGL */ + +/* Define if you have the MesaGLU library (-lMesaGLU). */ +#define HAVE_LIBMESAGLU 1 + +/* Define if you have the SM library (-lSM). */ +#define HAVE_LIBSM 1 + +/* Define if you have the X11 library (-lX11). */ +#define HAVE_LIBX11 1 + +/* Define if you have the Xext library (-lXext). */ +#define HAVE_LIBXEXT 1 + +/* Define if you have the Xi library (-lXi). */ +#define HAVE_LIBXI 1 + +/* Define if you have the Xmu library (-lXmu). */ +#define HAVE_LIBXMU 1 + +/* Define if you have the Xt library (-lXt). */ +#define HAVE_LIBXT 1 + +/* Define if you have the glut library (-lglut). */ +#define HAVE_LIBGLUT 1 + +/* Define if you have the m library (-lm). */ +#define HAVE_LIBM 1 + +/* Define if you have the socket library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ diff --git a/src/Include/config.h.in b/src/Include/config.h.in new file mode 100644 index 00000000..fa7ef654 --- /dev/null +++ b/src/Include/config.h.in @@ -0,0 +1,207 @@ +/* Include/config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have the vprintf function. */ +#undef HAVE_VPRINTF + +/* Define as the return type of signal handlers (int or void). */ +#undef RETSIGTYPE + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define if the X Window System is missing or not being used. */ +#undef X_DISPLAY_MISSING + +/* Define to empty if the keyword does not work. */ +#undef const + + +/* Define to eliminate all trace of debugging messages such as for a + release build */ +#undef FG_NDEBUG + + +/* Define to enable 3dfx/glide render in a window hack under unix. + This probably won't work under windows. */ +#undef XMESA +#undef FX + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have the vprintf function. */ +#undef HAVE_VPRINTF + +/* Define to package name */ +#undef PACKAGE + +/* Define as the return type of signal handlers (int or void). */ +#undef RETSIGTYPE + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define to version number */ +#undef VERSION + +/* Define if compiling on a Winbloze (95, NT, etc.) platform */ +#undef WIN32 + +/* Define if the X Window System is missing or not being used. */ +#undef X_DISPLAY_MISSING + +/* Define if you have the GetLocalTime function. */ +#undef HAVE_GETLOCALTIME + +/* Define if you have the bcopy function. */ +#undef HAVE_BCOPY + +/* Define if you have the ftime function. */ +#undef HAVE_FTIME + +/* Define if you have the getitimer function. */ +#undef HAVE_GETITIMER + +/* Define if you have the getrusage function. */ +#undef HAVE_GETRUSAGE + +/* Define if you have the gettimeofday function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define if you have the memcpy function. */ +#undef HAVE_MEMCPY + +/* Define if you have the mktime function. */ +#undef HAVE_MKTIME + +/* Define if you have the rand function. */ +#undef HAVE_RAND + +/* Define if you have the random function. */ +#undef HAVE_RANDOM + +/* Define if you have the rint function. */ +#undef HAVE_RINT + +/* Define if you have the setitimer function. */ +#undef HAVE_SETITIMER + +/* Define if you have the signal function. */ +#undef HAVE_SIGNAL + +/* Define if you have the strstr function. */ +#undef HAVE_STRSTR + +/* Define if you have the timegm function. */ +#undef HAVE_TIMEGM + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the header file. */ +#undef HAVE_GETOPT_H + +/* Define if you have the header file. */ +#undef HAVE_GPC_H + +/* Define if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIMEB_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the header file. */ +#undef HAVE_VALUES_H + +/* Define if you have the header file. */ +#undef HAVE_WINBASE_H + +/* Define if you have the header file. */ +#undef HAVE_WINDOWS_H + +/* Define if you have the GL library (-lGL). */ +#undef HAVE_LIBGL + +/* Define if you have the GLU library (-lGLU). */ +#undef HAVE_LIBGLU + +/* Define if you have the GLcore library (-lGLcore). */ +#undef HAVE_LIBGLCORE + +/* Define if you have the ICE library (-lICE). */ +#undef HAVE_LIBICE + +/* Define if you have the MesaGL library (-lMesaGL). */ +#undef HAVE_LIBMESAGL + +/* Define if you have the MesaGLU library (-lMesaGLU). */ +#undef HAVE_LIBMESAGLU + +/* Define if you have the SM library (-lSM). */ +#undef HAVE_LIBSM + +/* Define if you have the X11 library (-lX11). */ +#undef HAVE_LIBX11 + +/* Define if you have the Xext library (-lXext). */ +#undef HAVE_LIBXEXT + +/* Define if you have the Xi library (-lXi). */ +#undef HAVE_LIBXI + +/* Define if you have the Xmu library (-lXmu). */ +#undef HAVE_LIBXMU + +/* Define if you have the Xt library (-lXt). */ +#undef HAVE_LIBXT + +/* Define if you have the glut library (-lglut). */ +#undef HAVE_LIBGLUT + +/* Define if you have the m library (-lm). */ +#undef HAVE_LIBM + +/* Define if you have the socket library (-lsocket). */ +#undef HAVE_LIBSOCKET diff --git a/src/Include/stamp-h b/src/Include/stamp-h new file mode 100644 index 00000000..9788f702 --- /dev/null +++ b/src/Include/stamp-h @@ -0,0 +1 @@ +timestamp diff --git a/src/Lib/Array/Makefile.am b/src/Lib/Array/Makefile.am new file mode 100644 index 00000000..02b49b02 --- /dev/null +++ b/src/Lib/Array/Makefile.am @@ -0,0 +1,17 @@ +noinst_LIBRARIES = libArray.a + +libArray_a_SOURCES = array.cxx array.hxx + +noinst_PROGRAMS = testarray + +testarray_SOURCES = testarray.cxx + +testarray_LDADD = \ + $(top_builddir)/Lib/Array/libArray.a \ + -lsgbucket -lsgmath -lsgmisc -lz + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Tools/Lib \ + -I$(top_builddir)/Tools/Construct diff --git a/src/Lib/Array/array.cxx b/src/Lib/Array/array.cxx new file mode 100644 index 00000000..de6fb886 --- /dev/null +++ b/src/Lib/Array/array.cxx @@ -0,0 +1,577 @@ +// 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$ + + +#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 -9999; + } + + 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 + "/" + 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; +} + + diff --git a/src/Lib/Array/array.hxx b/src/Lib/Array/array.hxx new file mode 100644 index 00000000..38d5e3b1 --- /dev/null +++ b/src/Lib/Array/array.hxx @@ -0,0 +1,124 @@ +// 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$ + + +#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 + + diff --git a/src/Lib/Array/testarray.cxx b/src/Lib/Array/testarray.cxx new file mode 100644 index 00000000..b3781881 --- /dev/null +++ b/src/Lib/Array/testarray.cxx @@ -0,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 + "/" + base; + + string arrayfile = path + "/" + b.gen_index_str() + ".dem"; + cout << "arrayfile = " << arrayfile << endl; + + FGArray a(arrayfile); + a.parse( b ); + + lon *= 3600; + lat *= 3600; + cout << " " << a.interpolate_altitude(lon, lat) << endl; + + a.fit( 100 ); +} diff --git a/src/Lib/DEM/Makefile.am b/src/Lib/DEM/Makefile.am new file mode 100644 index 00000000..91bfd90d --- /dev/null +++ b/src/Lib/DEM/Makefile.am @@ -0,0 +1,11 @@ +noinst_LIBRARIES = libDEM.a + +libDEM_a_SOURCES = dem.cxx dem.hxx + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib + +# We can't build this with "-O2" (optimization) since this causes a seg fault +# I haven't found a way to strip this out of the CXXFLAGS, so I'm just +# setting it to "-g" +# CXXFLAGS = -g + diff --git a/src/Lib/DEM/dem.cxx b/src/Lib/DEM/dem.cxx new file mode 100644 index 00000000..22d74f5a --- /dev/null +++ b/src/Lib/DEM/dem.cxx @@ -0,0 +1,871 @@ +// 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$ + + +#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; + int last; + + // 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 + last = 0; + for ( i = 0; i < prof_num_rows; i++ ) { + prof_data = next_int(); + + // a bit of sanity checking that is unfortunately necessary + if ( prof_data > 10000 ) { // meters + prof_data = last; + } + + dem_data[cur_col][i] = (float)prof_data; + last = 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 + "/" + 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 + "/" + 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 + "/" + tmp_path; + if (my_mkdir ( dir.c_str() )) { exit (-1); } + + dir = fg_root + "/" + 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; +} + + diff --git a/src/Lib/DEM/dem.hxx b/src/Lib/DEM/dem.hxx new file mode 100644 index 00000000..eb23738c --- /dev/null +++ b/src/Lib/DEM/dem.hxx @@ -0,0 +1,154 @@ +// 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$ + + +#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 + + diff --git a/src/Lib/Geometry/Makefile.am b/src/Lib/Geometry/Makefile.am new file mode 100644 index 00000000..c7266c5a --- /dev/null +++ b/src/Lib/Geometry/Makefile.am @@ -0,0 +1,8 @@ +noinst_LIBRARIES = libBuild.a + +libBuild_a_SOURCES = \ + poly_support.cxx poly_support.hxx \ + trinodes.cxx trinodes.hxx \ + trisegs.cxx trisegs.hxx + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib -I$(top_builddir)/Tools/Lib diff --git a/src/Lib/Geometry/poly_support.cxx b/src/Lib/Geometry/poly_support.cxx new file mode 100644 index 00000000..1ac847d5 --- /dev/null +++ b/src/Lib/Geometry/poly_support.cxx @@ -0,0 +1,229 @@ +// poly_support.cxx -- additional supporting routines for the FGPolygon class +// specific to the object building process. +// +// Written by Curtis Olson, started October 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$ + + +#include +#include + +#include + +#include "trinodes.hxx" + + +// calculate some "arbitrary" point inside the specified contour for +// assigning attribute areas. This requires data structures outside +// of "FGPolygon" which is why it is living over here in "Lib/Build" + +void calc_point_inside( const int contour, const FGTriNodes& trinodes ); + + +#include +#include + +#include + + +// 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( Point3D p0, Point3D p1, double x, Point3D *result ) { + // sort the end points + if ( p0.x() > p1.x() ) { + Point3D tmp = p0; + p0 = p1; + p1 = tmp; + } + + if ( (x < p0.x()) || (x > p1.x()) ) { + // out of range of line segment, bail right away + return false; + } + + // 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 some "arbitrary" point inside the specified contour for +// assigning attribute areas +Point3D calc_point_inside( const FGPolygon& p, const int contour, + const FGTriNodes& trinodes ) { + Point3D tmp, min, ln, p1, p2, p3, m, result, inside_pt; + int min_node_index = 0; + int min_index = 0; + int p1_index = 0; + int p2_index = 0; + int ln_index = 0; + + // 1. find a point on the specified contour, min, with smallest y + + // min.y() starts greater than the biggest possible lat (degrees) + min.sety( 100.0 ); + + point_list c = p.get_contour(contour); + point_list_iterator current, last; + current = c.begin(); + last = c.end(); + + for ( int i = 0; i < p.contour_size( contour ); ++i ) { + tmp = p.get_pt( contour, i ); + if ( tmp.y() < min.y() ) { + min = tmp; + min_index = trinodes.find( min ); + min_node_index = i; + + // cout << "min index = " << *current + // << " value = " << min_y << endl; + } else { + // cout << " index = " << *current << endl; + } + } + + cout << "min node index = " << min_node_index << endl; + cout << "min index = " << min_index + << " value = " << trinodes.get_node( min_index ) + << " == " << min << endl; + + // 2. take midpoint, m, of min with neighbor having lowest + // fabs(slope) + + if ( min_node_index == 0 ) { + p1 = c[1]; + p2 = c[c.size() - 1]; + } else if ( min_node_index == (int)(c.size()) - 1 ) { + p1 = c[0]; + p2 = c[c.size() - 2]; + } else { + p1 = c[min_node_index - 1]; + p2 = c[min_node_index + 1]; + } + p1_index = trinodes.find( p1 ); + p2_index = trinodes.find( p2 ); + + double s1 = fabs( slope(min, p1) ); + double s2 = fabs( slope(min, p2) ); + if ( s1 < s2 ) { + ln_index = p1_index; + ln = p1; + } else { + ln_index = p2_index; + ln = p2; + } + + FGTriSeg base_leg( min_index, ln_index, 0 ); + + 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 of + // all other contours of this polygon. save point, p3, with + // smallest y > m.y + + p3.sety(100); + + for ( int i = 0; i < (int)p.contours(); ++i ) { + cout << "contour = " << i << " size = " << p.contour_size( i ) << endl; + for ( int j = 0; j < (int)(p.contour_size( i ) - 1); ++j ) { + // cout << " p1 = " << poly[i][j] << " p2 = " + // << poly[i][j+1] << endl; + p1 = p.get_pt( i, j ); + p2 = p.get_pt( i, j+1 ); + p1_index = trinodes.find( p1 ); + p2_index = trinodes.find( p2 ); + + if ( intersects(p1, p2, m.x(), &result) ) { + cout << "intersection = " << result << endl; + if ( ( result.y() < p3.y() ) && + ( result.y() > m.y() ) && + ( base_leg != FGTriSeg(p1_index, p2_index, 0) ) ) { + p3 = result; + } + } + } + // cout << " p1 = " << poly[i][0] << " p2 = " + // << poly[i][poly[i].size() - 1] << endl; + p1 = p.get_pt( i, 0 ); + p2 = p.get_pt( i, p.contour_size( i ) - 1 ); + p1_index = trinodes.find( p1 ); + p2_index = trinodes.find( p2 ); + if ( intersects(p1, p2, m.x(), &result) ) { + cout << "intersection = " << result << endl; + if ( ( result.y() < p3.y() ) && + ( result.y() > m.y() ) && + ( base_leg != FGTriSeg(p1_index, p2_index, 0) ) ) { + p3 = result; + } + } + } + if ( p3.y() < 100 ) { + cout << "low intersection of other segment = " << p3 << endl; + inside_pt = Point3D( (m.x() + p3.x()) / 2.0, + (m.y() + p3.y()) / 2.0, + 0.0 ); + } else { + cout << "Error: Failed to find a point inside :-(" << endl; + inside_pt = p3; + } + + // 4. take midpoint of p2 && m as an arbitrary point inside polygon + + cout << "inside point = " << inside_pt << endl; + + return inside_pt; +} diff --git a/src/Lib/Geometry/poly_support.hxx b/src/Lib/Geometry/poly_support.hxx new file mode 100644 index 00000000..d6fd40d3 --- /dev/null +++ b/src/Lib/Geometry/poly_support.hxx @@ -0,0 +1,52 @@ +// poly_support.hxx -- additional supporting routines for the FGPolygon class +// specific to the object building process. +// +// Written by Curtis Olson, started October 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$ + + +#ifndef _POLY_SUPPORT_HXX +#define _POLY_SUPPORT_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include +#include + +#include + +#include "trinodes.hxx" + + +// calculate some "arbitrary" point inside the specified contour for +// assigning attribute areas. This requires data structures outside +// of "FGPolygon" which is why it is living over here in "Lib/Build" + +Point3D calc_point_inside( const FGPolygon& p, const int contour, + const FGTriNodes& trinodes ); + + +#endif // _POLY_SUPPORT_HXX + + diff --git a/src/Lib/Geometry/trinodes.cxx b/src/Lib/Geometry/trinodes.cxx new file mode 100644 index 00000000..42258451 --- /dev/null +++ b/src/Lib/Geometry/trinodes.cxx @@ -0,0 +1,123 @@ +// 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$ + + +#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; +} + + +// Find the index of the specified point (compair to the same +// tolerance as unique_add(). Returns -1 if not found. +int FGTriNodes::find( const Point3D& p ) const { + const_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; + } + + return -1; +} + + diff --git a/src/Lib/Geometry/trinodes.hxx b/src/Lib/Geometry/trinodes.hxx new file mode 100644 index 00000000..8259a63e --- /dev/null +++ b/src/Lib/Geometry/trinodes.hxx @@ -0,0 +1,124 @@ +// 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$ + + +#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 ) const; + + // 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 ); + + // Find the index of the specified point (compair to the same + // tolerance as unique_add(). Returns -1 if not found. + int find( const Point3D& p ) const; + + // return the master node list + inline point_list get_node_list() const { return node_list; } + inline void set_node_list( point_list pl ) { node_list = pl; } + + // 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 ) + const +{ + 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 + + diff --git a/src/Lib/Geometry/trisegs.cxx b/src/Lib/Geometry/trisegs.cxx new file mode 100644 index 00000000..9828c284 --- /dev/null +++ b/src/Lib/Geometry/trisegs.cxx @@ -0,0 +1,210 @@ +// 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$ + + +#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 has duplicated endpoints + if ( s.get_n1() == s.get_n2() ) { + cout << "WARNING: ignoring null segment with the same " + << "point for both endpoints" << endl; + return -1; + } + + // 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, m1, b, b1, y_err, x_err, y_err_min, x_err_min; + const_point_list_iterator current, last; + + // bool temp = false; + // if ( s == FGTriSeg( 170, 206 ) ) { + // cout << "this is it!" << endl; + // temp = true; + // } + + double xdist = fabs(p0.x() - p1.x()); + double ydist = fabs(p0.y() - p1.y()); + x_err_min = xdist + 1.0; + y_err_min = ydist + 1.0; + + if ( xdist > ydist ) { + // use y = mx + b + + // sort these in a sensible 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 < FG_PROXIMITY_EPSILON ) { + cout << "FOUND EXTRA SEGMENT NODE (Y)" << endl; + cout << p0 << " < " << *current << " < " + << p1 << endl; + found_extra = true; + if ( y_err < y_err_min ) { + extra_index = counter; + y_err_min = y_err; + } + } + } + ++counter; + } + } else { + // use x = m1 * y + b1 + + // sort these in a sensible order + if ( p0.y() > p1.y() ) { + Point3D tmp = p0; + p0 = p1; + p1 = tmp; + } + + m1 = (p0.x() - p1.x()) / (p0.y() - p1.y()); + b1 = p1.x() - m1 * p1.y(); + + // bool temp = true; + // if ( temp ) { + // cout << "xdist = " << xdist << " ydist = " << ydist << endl; + // cout << " p0 = " << p0 << " p1 = " << p1 << endl; + // cout << " m1 = " << m1 << " b1 = " << b1 << endl; + // } + + // cout << " should = 0 = " << fabs(p0.x() - (m1 * p0.y() + b1)) << endl;; + // cout << " should = 0 = " << fabs(p1.x() - (m1 * p1.y() + b1)) << endl;; + + current = nodes.begin(); + last = nodes.end(); + counter = 0; + for ( ; current != last; ++current ) { + if ( (current->y() > (p0.y() + FG_EPSILON)) + && (current->y() < (p1.y() - FG_EPSILON)) ) { + + x_err = fabs(current->x() - (m1 * current->y() + b1)); + + // if ( temp ) { + // cout << " (" << counter << ") x_err = " << x_err << endl; + // } + + if ( x_err < FG_PROXIMITY_EPSILON ) { + cout << "FOUND EXTRA SEGMENT NODE (X)" << endl; + cout << p0 << " < " << *current << " < " + << p1 << endl; + found_extra = true; + if ( x_err < x_err_min ) { + extra_index = counter; + x_err_min = x_err; + } + } + } + ++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, + s.get_boundary_marker() ) ); + unique_divide_and_add( nodes, FGTriSeg( extra_index, s.get_n2(), + s.get_boundary_marker() ) ); + } else { + // this segment does not need to be divided, lets add it + unique_add( s ); + } +} + + diff --git a/src/Lib/Geometry/trisegs.hxx b/src/Lib/Geometry/trisegs.hxx new file mode 100644 index 00000000..4f2c59cc --- /dev/null +++ b/src/Lib/Geometry/trisegs.hxx @@ -0,0 +1,123 @@ +// 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$ + + +#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; // indices into point list + int boundary_marker; // flag if segment is a boundary + // (i.e. shouldn't get split) + +public: + + // Constructor and destructor + inline FGTriSeg( void ) { }; + inline FGTriSeg( int i1, int i2, int b ) { + n1 = i1; + n2 = i2; + boundary_marker = b; + } + + 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; } + inline int get_boundary_marker() const { return boundary_marker; } + inline void set_boundary_marker( int b ) { boundary_marker = b; } + + 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 segment 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 + + diff --git a/src/Lib/Makefile.am b/src/Lib/Makefile.am new file mode 100644 index 00000000..385d126a --- /dev/null +++ b/src/Lib/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = \ + Array \ + Build \ + DEM \ + Polygon \ + poly2tri \ + shapelib \ + Triangle diff --git a/src/Lib/Optimize/Makefile.am b/src/Lib/Optimize/Makefile.am new file mode 100644 index 00000000..e039449c --- /dev/null +++ b/src/Lib/Optimize/Makefile.am @@ -0,0 +1,8 @@ +noinst_LIBRARIES = libCombine.a + +libCombine_a_SOURCES = genfans.cxx genfans.hxx + +INCLUDES += \ + -I$(top_builddir) \ + -I$(top_builddir)/Lib \ + -I$(top_builddir)/Construct diff --git a/src/Lib/Optimize/genfans.cxx b/src/Lib/Optimize/genfans.cxx new file mode 100644 index 00000000..78f0e707 --- /dev/null +++ b/src/Lib/Optimize/genfans.cxx @@ -0,0 +1,244 @@ +// 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$ + + +#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(); +} + + diff --git a/src/Lib/Optimize/genfans.hxx b/src/Lib/Optimize/genfans.hxx new file mode 100644 index 00000000..c56af87a --- /dev/null +++ b/src/Lib/Optimize/genfans.hxx @@ -0,0 +1,81 @@ +// 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$ + + +#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 + + diff --git a/src/Lib/Polygon/Makefile.am b/src/Lib/Polygon/Makefile.am new file mode 100644 index 00000000..e6bc3a04 --- /dev/null +++ b/src/Lib/Polygon/Makefile.am @@ -0,0 +1,9 @@ +noinst_LIBRARIES = libPolygon.a + +libPolygon_a_SOURCES = \ + index.cxx index.hxx \ + names.cxx names.hxx \ + polygon.cxx polygon.hxx \ + split.cxx split.hxx + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib -I$(top_builddir)/Tools/Lib diff --git a/src/Lib/Polygon/index.cxx b/src/Lib/Polygon/index.cxx new file mode 100644 index 00000000..a37db140 --- /dev/null +++ b/src/Lib/Polygon/index.cxx @@ -0,0 +1,71 @@ +// 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$ + +#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; +} + + diff --git a/src/Lib/Polygon/index.hxx b/src/Lib/Polygon/index.hxx new file mode 100644 index 00000000..c2336cd0 --- /dev/null +++ b/src/Lib/Polygon/index.hxx @@ -0,0 +1,43 @@ +// 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$ + + +#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 + + diff --git a/src/Lib/Polygon/names.cxx b/src/Lib/Polygon/names.cxx new file mode 100644 index 00000000..b631c791 --- /dev/null +++ b/src/Lib/Polygon/names.cxx @@ -0,0 +1,118 @@ +// 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$ + +#include + +#include STL_STRING + +#include "names.hxx" + + +// return area type from text name +AreaType get_area_type( string area ) { + if ( area == "SomeSort" ) { + return SomeSortOfArea; + } else if ( area == "Hole" ) { + return HoleArea; + } else if ( (area == "Swamp or Marsh") + || (area == "Marsh") ) { + return MarshArea; + } 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 == "Urban" ) { + return UrbanArea; + } else if ( area == "Default" ) { + return DefaultArea; + } else if ( (area == "Bay Estuary or Ocean") + || (area == "Ocean") ) { + return OceanArea; + } 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 == HoleArea ) { + return "Hole"; + } else if ( area == MarshArea ) { + return "Marsh"; + } 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 == UrbanArea ) { + return "Urban"; + } else if ( area == OceanArea ) { + return "Ocean"; + } else if ( area == VoidArea ) { + return "VoidArea"; + } else if ( area == NullArea ) { + return "Null"; + } else { + cout << "unknown area code = " << (int)area << endl; + return "Unknown"; + } +} + + diff --git a/src/Lib/Polygon/names.hxx b/src/Lib/Polygon/names.hxx new file mode 100644 index 00000000..9b3d86fd --- /dev/null +++ b/src/Lib/Polygon/names.hxx @@ -0,0 +1,68 @@ +// 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$ + + +#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 { + SomeSortOfArea = 0, + HoleArea = 1, + LakeArea = 2, + DryLakeArea = 3, + IntLakeArea = 4, + ReservoirArea = 5, + IntReservoirArea = 6, + StreamArea = 7, + CanalArea = 8, + GlacierArea = 9, + OceanArea = 10, + UrbanArea = 11, + MarshArea = 12, + DefaultArea = 13, + 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 + + diff --git a/src/Lib/Polygon/polygon.cxx b/src/Lib/Polygon/polygon.cxx new file mode 100644 index 00000000..1b340865 --- /dev/null +++ b/src/Lib/Polygon/polygon.cxx @@ -0,0 +1,493 @@ +// polygon.cxx -- polygon (with holes) 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$ + + +// include Generic Polygon Clipping Library +// +// http://www.cs.man.ac.uk/aig/staff/alan/software/ +// +extern "C" { +#include +} + +#include +#include + +#include + +#include "polygon.hxx" + + +// Constructor +FGPolygon::FGPolygon( void ) { +} + + +// Destructor +FGPolygon::~FGPolygon( void ) { +} + + +// Calculate theta of angle (a, b, c) +static 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); +} + + +// return the perimeter of a contour (assumes simple polygons, +// i.e. non-self intersecting.) +// +// negative areas indicate counter clockwise winding +// postitive areas indicate clockwise winding. + +double FGPolygon::area_contour( const int contour ) const { + // area = 1/2 * sum[i = 0 to k-1][x(i)*y(i+1) - x(i+1)*y(i)] + // where k is defined as 0 + + point_list c = poly[contour]; + int size = c.size(); + double sum = 0.0; + + for ( int i = 0; i < size; ++i ) { + sum += c[(i+1)%size].x() * c[i].y() - c[i].x() * c[(i+1)%size].y(); + } + + // area can be negative or positive depending on the polygon + // winding order + return fabs(sum / 2.0); +} + + +// return the smallest interior angle of the polygon +double FGPolygon::minangle_contour( const int contour ) { + point_list c = poly[contour]; + int size = c.size(); + int p1_index, p2_index, p3_index; + point2d p1, p2, p3; + double angle; + double min_angle = 2.0 * FG_PI; + + for ( int i = 0; i < size; ++i ) { + p1_index = i - 1; + if ( p1_index < 0 ) { + p1_index += size; + } + + p2_index = i; + + p3_index = i + 1; + if ( p3_index >= size ) { + p3_index -= size; + } + + p1.x = c[p1_index].x(); + p1.y = c[p1_index].y(); + + p2.x = c[p2_index].x(); + p2.y = c[p2_index].y(); + + p3.x = c[p3_index].x(); + p3.y = c[p3_index].y(); + + angle = calc_angle( p1, p2, p3 ); + + if ( angle < min_angle ) { + min_angle = angle; + } + } + + return min_angle; +} + + +// shift every point in the polygon by lon, lat +void FGPolygon::shift( double lon, double lat ) { + for ( int i = 0; i < (int)poly.size(); ++i ) { + for ( int j = 0; j < (int)poly[i].size(); ++j ) { + poly[i][j].setx( poly[i][j].x() + lon ); + poly[i][j].sety( poly[i][j].y() + lat ); + } + } +} + + +// output +void FGPolygon::write( const string& file ) { + FILE *fp = fopen( file.c_str(), "w" ); + + for ( int i = 0; i < (int)poly.size(); ++i ) { + for ( int j = 0; j < (int)poly[i].size(); ++j ) { + fprintf(fp, "%.6f %.6f\n", poly[i][j].x(), poly[i][j].y()); + } + fprintf(fp, "%.6f %.6f\n", poly[i][0].x(), poly[i][0].y()); + } + + fclose(fp); +} + + +// +// wrapper functions for gpc polygon clip routines +// + +// Make a gpc_poly from an FGPolygon +void make_gpc_poly( const FGPolygon& in, gpc_polygon *out ) { + gpc_vertex_list v_list; + v_list.num_vertices = 0; + v_list.vertex = new gpc_vertex[FG_MAX_VERTICES]; + + // cout << "making a gpc_poly" << endl; + // cout << " input contours = " << in.contours() << endl; + + Point3D p; + // build the gpc_polygon structures + for ( int i = 0; i < in.contours(); ++i ) { + // cout << " contour " << i << " = " << in.contour_size( i ) << endl; + if ( in.contour_size( i ) > FG_MAX_VERTICES ) { + cout << "Polygon too large, need to increase FG_MAX_VERTICES to at " + << "least " << in.contour_size( i ) << endl; + exit(-1); + } + + for ( int j = 0; j < in.contour_size( i ); ++j ) { + p = in.get_pt( i, j ); + v_list.vertex[j].x = p.x(); + v_list.vertex[j].y = p.y(); + } + v_list.num_vertices = in.contour_size( i ); + gpc_add_contour( out, &v_list, in.get_hole_flag( i ) ); + } + + // free alocated memory + delete v_list.vertex; +} + + +// Set operation type +typedef enum { + POLY_DIFF, // Difference + POLY_INT, // Intersection + POLY_XOR, // Exclusive or + POLY_UNION // Union +} clip_op; + + +// Generic clipping routine +FGPolygon polygon_clip( clip_op poly_op, const FGPolygon& subject, + const FGPolygon& clip ) +{ + FGPolygon result; + + gpc_polygon *gpc_subject = new gpc_polygon; + gpc_subject->num_contours = 0; + gpc_subject->contour = NULL; + gpc_subject->hole = NULL; + make_gpc_poly( subject, gpc_subject ); + + gpc_polygon *gpc_clip = new gpc_polygon; + gpc_clip->num_contours = 0; + gpc_clip->contour = NULL; + gpc_clip->hole = NULL; + make_gpc_poly( clip, gpc_clip ); + + gpc_polygon *gpc_result = new gpc_polygon; + gpc_result->num_contours = 0; + gpc_result->contour = NULL; + gpc_result->hole = NULL; + + gpc_op op; + if ( poly_op == POLY_DIFF ) { + op = GPC_DIFF; + } else if ( poly_op == POLY_INT ) { + op = GPC_INT; + } else if ( poly_op == POLY_XOR ) { + op = GPC_XOR; + } else if ( poly_op == POLY_UNION ) { + op = GPC_UNION; + } else { + cout << "Unknown polygon op, exiting." << endl; + exit(-1); + } + + gpc_polygon_clip( op, gpc_subject, gpc_clip, gpc_result ); + + for ( int i = 0; i < gpc_result->num_contours; ++i ) { + // cout << " processing contour = " << i << ", nodes = " + // << gpc_result->contour[i].num_vertices << ", hole = " + // << gpc_result->hole[i] << endl; + + // sprintf(junkn, "g.%d", junkc++); + // junkfp = fopen(junkn, "w"); + + for ( int j = 0; j < gpc_result->contour[i].num_vertices; j++ ) { + Point3D p( gpc_result->contour[i].vertex[j].x, + gpc_result->contour[i].vertex[j].y, + 0 ); + // junkp = in_nodes.get_node( index ); + // fprintf(junkfp, "%.4f %.4f\n", junkp.x(), junkp.y()); + result.add_node(i, p); + // cout << " - " << index << endl; + } + // fprintf(junkfp, "%.4f %.4f\n", + // gpc_result->contour[i].vertex[0].x, + // gpc_result->contour[i].vertex[0].y); + // fclose(junkfp); + + result.set_hole_flag( i, gpc_result->hole[i] ); + } + + // free allocated memory + gpc_free_polygon( gpc_subject ); + gpc_free_polygon( gpc_clip ); + gpc_free_polygon( gpc_result ); + + return result; +} + + +// Difference +FGPolygon polygon_diff( const FGPolygon& subject, const FGPolygon& clip ) { + return polygon_clip( POLY_DIFF, subject, clip ); +} + +// Intersection +FGPolygon polygon_int( const FGPolygon& subject, const FGPolygon& clip ) { + return polygon_clip( POLY_INT, subject, clip ); +} + + +// Exclusive or +FGPolygon polygon_xor( const FGPolygon& subject, const FGPolygon& clip ) { + return polygon_clip( POLY_XOR, subject, clip ); +} + + +// Union +FGPolygon polygon_union( const FGPolygon& subject, const FGPolygon& clip ) { + return polygon_clip( POLY_UNION, subject, clip ); +} + + +// canonify the polygon winding, outer contour must be anti-clockwise, +// all inner contours must be clockwise. +FGPolygon polygon_canonify( const FGPolygon& in_poly ) { + FGPolygon result; + result.erase(); + + // Negative areas indicate counter clockwise winding. Postitive + // areas indicate clockwise winding. + + int non_hole_count = 0; + + for ( int i = 0; i < in_poly.contours(); ++i ) { + point_list contour = in_poly.get_contour( i ); + int hole_flag = in_poly.get_hole_flag( i ); + if ( !hole_flag ) { + non_hole_count++; + if ( non_hole_count > 1 ) { + cout << "ERROR: polygon with more than one enclosing" << endl; + cout << " contour. I bet you don't handle that!" << endl; + cout << " dying!!!" << endl; + } + } + double area = in_poly.area_contour( i ); + if ( hole_flag && (area < 0) ) { + // reverse contour + point_list rcontour; + rcontour.clear(); + for ( int j = (int)contour.size() - 1; j >= 0; --j ) { + rcontour.push_back( contour[j] ); + } + result.add_contour( rcontour, hole_flag ); + } else if ( !hole_flag && (area > 0) ) { + // reverse contour + point_list rcontour; + rcontour.clear(); + for ( int j = (int)contour.size() - 1; j >= 0; --j ) { + rcontour.push_back( contour[j] ); + } + result.add_contour( rcontour, hole_flag ); + } else { + result.add_contour( contour, hole_flag ); + } + } + + return result; +} + + +// Wrapper for the fast Polygon Triangulation based on Seidel's +// Algorithm by Atul Narkhede and Dinesh Manocha +// http://www.cs.unc.edu/~dm/CODE/GEM/chapter.html + +// I make this oversize because given an n sided concave polygon with +// m, n, o, ... sided holes, I have no idea how many triangles would +// result and I don't have time right now to see if an upper bound can +// be determined easily. +#define FG_MAX_TRIANGLES 100000 + +FGPolygon polygon_to_tristrip( const FGPolygon& in_poly ) { + + // canonify the polygon winding, outer contour must be + // anti-clockwise, all inner contours must be clockwise. + FGPolygon canon_poly = polygon_canonify( in_poly ); + + // create and fill in the required structures + int ncontours = canon_poly.contours(); + int cntr[ncontours]; + int vsize = 1; + for ( int i = 0; i < canon_poly.contours(); ++i ) { + cntr[i] = canon_poly.contour_size( i ); + vsize += cntr[i]; + } + double vertices[vsize][2]; + int counter = 1; + Point3D p; + for ( int i = 0; i < canon_poly.contours(); ++i ) { + for ( int j = 0; j < canon_poly.contour_size( i ); ++j ) { + p = canon_poly.get_pt( i, j ); + vertices[counter][0] = p.x(); + vertices[counter][1] = p.y(); + counter++; + } + } + int triangles[FG_MAX_TRIANGLES][3]; + + // do the triangulation + int ntriangles = triangulate_polygon(ncontours, cntr, vertices, triangles); + + /* + gpc_polygon *tmp_poly = new gpc_polygon; + tmp_poly->num_contours = 0; + tmp_poly->contour = NULL; + tmp_poly->hole = NULL; + make_gpc_poly( in_poly, tmp_poly ); + + gpc_tristrip *tmp_tristrip = new gpc_tristrip; + tmp_tristrip->num_strips = 0; + tmp_tristrip->strip = NULL; + + gpc_polygon_to_tristrip( tmp_poly, tmp_tristrip ); + + FGPolygon result; + + for ( int i = 0; i < tmp_tristrip->num_strips; ++i ) { + cout << " processing strip = " << i << ", nodes = " + << tmp_tristrip->strip[i].num_vertices << endl; + + // sprintf(junkn, "g.%d", junkc++); + // junkfp = fopen(junkn, "w"); + + for ( int j = 0; j < tmp_tristrip->strip[i].num_vertices; j++ ) { + Point3D p( tmp_tristrip->strip[i].vertex[j].x, + tmp_tristrip->strip[i].vertex[j].y, + 0 ); + // junkp = in_nodes.get_node( index ); + // fprintf(junkfp, "%.4f %.4f\n", junkp.x(), junkp.y()); + result.add_node(i, p); + // cout << " - " << index << endl; + } + // fprintf(junkfp, "%.4f %.4f\n", + // gpc_result->contour[i].vertex[0].x, + // gpc_result->contour[i].vertex[0].y); + // fclose(junkfp); + } + + // free allocated memory + gpc_free_polygon( tmp_poly ); + gpc_free_tristrip( tmp_tristrip ); + */ + + // return result; +} + + +#if 0 +// +// wrapper functions for gpc polygon to tristrip routine +// + +FGPolygon polygon_to_tristrip_old( const FGPolygon& in_poly ) { + gpc_polygon *tmp_poly = new gpc_polygon; + tmp_poly->num_contours = 0; + tmp_poly->contour = NULL; + tmp_poly->hole = NULL; + make_gpc_poly( in_poly, tmp_poly ); + + gpc_tristrip *tmp_tristrip = new gpc_tristrip; + tmp_tristrip->num_strips = 0; + tmp_tristrip->strip = NULL; + + gpc_polygon_to_tristrip( tmp_poly, tmp_tristrip ); + + FGPolygon result; + + for ( int i = 0; i < tmp_tristrip->num_strips; ++i ) { + cout << " processing strip = " << i << ", nodes = " + << tmp_tristrip->strip[i].num_vertices << endl; + + // sprintf(junkn, "g.%d", junkc++); + // junkfp = fopen(junkn, "w"); + + for ( int j = 0; j < tmp_tristrip->strip[i].num_vertices; j++ ) { + Point3D p( tmp_tristrip->strip[i].vertex[j].x, + tmp_tristrip->strip[i].vertex[j].y, + 0 ); + // junkp = in_nodes.get_node( index ); + // fprintf(junkfp, "%.4f %.4f\n", junkp.x(), junkp.y()); + result.add_node(i, p); + // cout << " - " << index << endl; + } + // fprintf(junkfp, "%.4f %.4f\n", + // gpc_result->contour[i].vertex[0].x, + // gpc_result->contour[i].vertex[0].y); + // fclose(junkfp); + } + + // free allocated memory + gpc_free_polygon( tmp_poly ); + gpc_free_tristrip( tmp_tristrip ); + + return result; +} +#endif diff --git a/src/Lib/Polygon/polygon.hxx b/src/Lib/Polygon/polygon.hxx new file mode 100644 index 00000000..d3f39eea --- /dev/null +++ b/src/Lib/Polygon/polygon.hxx @@ -0,0 +1,189 @@ +// polygon.hxx -- polygon (with holes) 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$ + + +#ifndef _POLYGON_HXX +#define _POLYGON_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#include +#include + +#include +#include + +FG_USING_STD(string); +FG_USING_STD(vector); + + +#define FG_MAX_VERTICES 1500000 + + +typedef vector < point_list > polytype; +typedef polytype::iterator polytype_iterator; +typedef polytype::const_iterator const_polytype_iterator; + + +class FGPolygon { + +private: + + polytype poly; // polygons + point_list inside_list; // point inside list + int_list hole_list; // hole flag list + +public: + + // Constructor and destructor + FGPolygon( void ); + ~FGPolygon( void ); + + // Add a contour + inline void add_contour( const point_list contour, const int hole_flag ) { + poly.push_back( contour ); + hole_list.push_back( hole_flag ); + } + + // Get a contour + inline point_list get_contour( const int i ) const { + return poly[i]; + } + + // Delete a contour + inline void delete_contour( const int i ) { + polytype_iterator start_poly = poly.begin(); + poly.erase( start_poly + i ); + + int_list_iterator start_hole = hole_list.begin(); + hole_list.erase( start_hole + i ); + } + + // Add the specified node (index) to the polygon + inline void add_node( int contour, Point3D p ) { + if ( contour >= (int)poly.size() ) { + // extend polygon + point_list empty_contour; + empty_contour.clear(); + for ( int i = 0; i < contour - (int)poly.size() + 1; ++i ) { + poly.push_back( empty_contour ); + inside_list.push_back( Point3D(0.0) ); + hole_list.push_back( 0 ); + } + } + poly[contour].push_back( p ); + } + + // return size + inline int contours() const { return poly.size(); } + inline int contour_size( int contour ) const { + return poly[contour].size(); + } + + // return the ith polygon point index from the specified contour + inline Point3D get_pt( int contour, int i ) const { + return poly[contour][i]; + } + + // update the value of a point + inline void set_pt( int contour, int i, const Point3D& p ) { + poly[contour][i] = p; + } + + // get and set an arbitrary point inside the specified polygon contour + inline Point3D get_point_inside( const int contour ) const { + return inside_list[contour]; + } + inline void set_point_inside( int contour, const Point3D& p ) { + inside_list[contour] = p; + } + + // get and set hole flag + inline int get_hole_flag( const int contour ) const { + return hole_list[contour]; + } + inline void set_hole_flag( const int contour, const int flag ) { + hole_list[contour] = flag; + } + + // shift every point in the polygon by lon, lat + void shift( double lon, double lat ); + + // erase + inline void erase() { poly.clear(); } + + // informational + + // return the area of a contour (assumes simple polygons, + // i.e. non-self intersecting.) + // + // negative areas indicate counter clockwise winding + // postitive areas indicate clockwise winding. + double area_contour( const int contour ) const; + + // return the smallest interior angle of the polygon + double minangle_contour( const int contour ); + + // output + void write( const string& file ); +}; + + +typedef vector < FGPolygon > poly_list; +typedef poly_list::iterator poly_list_iterator; +typedef poly_list::const_iterator const_poly_list_iterator; + + +// canonify the polygon winding, outer contour must be anti-clockwise, +// all inner contours must be clockwise. +FGPolygon polygon_canonify( const FGPolygon& in_poly ); + + +// Wrapper for the fast Polygon Triangulation based on Seidel's +// Algorithm by Atul Narkhede and Dinesh Manocha +// http://www.cs.unc.edu/~dm/CODE/GEM/chapter.html + +FGPolygon polygon_to_tristrip( const FGPolygon& poly ); + + +// wrapper functions for gpc polygon clip routines + +// Difference +FGPolygon polygon_diff( const FGPolygon& subject, const FGPolygon& clip ); + +// Intersection +FGPolygon polygon_int( const FGPolygon& subject, const FGPolygon& clip ); + +// Exclusive or +FGPolygon polygon_xor( const FGPolygon& subject, const FGPolygon& clip ); + +// Union +FGPolygon polygon_union( const FGPolygon& subject, const FGPolygon& clip ); + + +#endif // _POLYGON_HXX + + diff --git a/src/Lib/Polygon/split.cxx b/src/Lib/Polygon/split.cxx new file mode 100644 index 00000000..1c1a0bc2 --- /dev/null +++ b/src/Lib/Polygon/split.cxx @@ -0,0 +1,233 @@ +// split.cxx -- polygon splitting 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$ + + +#include + +#include STL_STRING + +#include +#include + +#include "index.hxx" +#include "names.hxx" +#include "split.hxx" + + +static void clip_and_write_poly( string root, long int p_index, AreaType area, + FGBucket b, const FGPolygon& shape ) { + Point3D c, min, max, p; + c = Point3D( b.get_center_lon(), b.get_center_lat(), 0 ); + double span = bucket_span( c.y() ); + FGPolygon base, result; + char tile_name[256], poly_index[256]; + + // calculate bucket dimensions + if ( (c.y() >= -89.0) && (c.y() < 89.0) ) { + min.setx( c.x() - span / 2.0 ); + max.setx( c.x() + span / 2.0 ); + min.sety( c.y() - FG_HALF_BUCKET_SPAN ); + max.sety( c.y() + FG_HALF_BUCKET_SPAN ); + } else if ( c.y() < -89.0) { + min.setx( -90.0 ); + max.setx( -89.0 ); + min.sety( -180.0 ); + max.sety( 180.0 ); + } else if ( c.y() >= 89.0) { + min.setx( 89.0 ); + max.setx( 90.0 ); + min.sety( -180.0 ); + max.sety( 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_DEBUG, " (" << min << ") (" << max << ")" ); + + // set up clipping tile + base.add_node( 0, Point3D(min.x(), min.y(), 0) ); + base.add_node( 0, Point3D(max.x(), min.y(), 0) ); + base.add_node( 0, Point3D(max.x(), max.y(), 0) ); + base.add_node( 0, Point3D(min.x(), max.y(), 0) ); + + // FG_LOG( FG_GENERAL, FG_DEBUG, "base = 4 vertices" ); + + /* + FILE *bfp= fopen("base", "w"); + gpc_write_polygon(bfp, &base); + fclose(bfp); + */ + + result = polygon_int( base, shape ); + + if ( result.contours() > 0 ) { + long int t_index = b.gen_index(); + string path = root + "/" + 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() ); + + fprintf( rfp, "%d\n", result.contours() ); + for ( int i = 0; i < result.contours(); ++i ) { + fprintf( rfp, "%d\n", result.contour_size(i) ); + fprintf( rfp, "%d\n", result.get_hole_flag(i) ); + for ( int j = 0; j < result.contour_size(i); ++j ) { + p = result.get_pt( i, j ); + fprintf( rfp, "%.15f %.15f\n", p.x(), p.y() ); + } + } + fclose( rfp ); + } +} + + +// process shape (write polygon to all intersecting tiles) +void split_polygon(const string& path, AreaType area, const FGPolygon& shape) { + Point3D min, max, p; + // point2d min, max; + long int index; + int i, j; + + // bail out immediately if polygon is empty + if ( shape.contours() == 0 ) { + return; + } + + min = Point3D( 200.0 ); + max = Point3D( -200.0 ); + + // find min/max of polygon + for ( i = 0; i < shape.contours(); i++ ) { + for ( j = 0; j < shape.contour_size(i); j++ ) { + p = shape.get_pt( i, j ); + + if ( p.x() < min.x() ) { min.setx( p.x() ); } + if ( p.y() < min.y() ) { min.sety( p.y() ); } + if ( p.x() > max.x() ) { max.setx( p.x() ); } + if ( p.y() > max.y() ) { max.sety( p.y() ); } + } + } + + // get next polygon index + index = poly_index_next(); + + FG_LOG( FG_GENERAL, FG_INFO, " min = " << min << " max = " << max ); + + // 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, 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 > 2880) || (dy > 1440) ) { + FG_LOG( FG_GENERAL, FG_ALERT, + "somethings really wrong!!!!" ); + exit(-1); + } + + for ( j = 0; j <= dy; j++ ) { + // for performance reasons, we'll clip out just this + // horizontal row, and clip all the tiles in this row + // against the smaller shape + + FG_LOG ( FG_GENERAL, FG_INFO, + "Generating clip row " << j << " of " << dy ); + + FGBucket b_clip = fgBucketOffset(min.x(), min.y(), 0, j); + FGPolygon row, clip_row; + Point3D c, clip_max, clip_min; + c = Point3D( b_clip.get_center_lon(), b_clip.get_center_lat(), 0 ); + + row.erase(); + clip_row.erase(); + + // calculate bucket clip_min.y and clip_max.y + if ( (c.y() >= -89.0) && (c.y() < 89.0) ) { + clip_min.sety( c.y() - FG_HALF_BUCKET_SPAN ); + clip_max.sety( c.y() + FG_HALF_BUCKET_SPAN ); + } else if ( c.y() < -89.0) { + clip_min.sety( -90.0 ); + clip_max.sety( -89.0 ); + } else if ( c.y() >= 89.0) { + clip_min.sety( 89.0 ); + clip_max.sety( 90.0 ); + } else { + FG_LOG ( FG_GENERAL, FG_ALERT, + "Out of range latitude in clip_and_write_poly() = " + << c.y() ); + } + clip_min.setx( -180.0 ); + clip_max.setx( 180.0 ); + + // set up clipping tile + row.add_node( 0, Point3D(clip_min.x(), clip_min.y(), 0) ); + row.add_node( 0, Point3D(clip_max.x(), clip_min.y(), 0) ); + row.add_node( 0, Point3D(clip_max.x(), clip_max.y(), 0) ); + row.add_node( 0, Point3D(clip_min.x(), clip_max.y(), 0) ); + + clip_row = polygon_int( row, shape ); + + /* FILE *sfp = fopen("shape", "w"); + gpc_write_polygon(sfp, 0, shape); + fclose(sfp); + sfp = fopen("clip_row", "w"); + gpc_write_polygon(sfp, 0, &clip_row); + fclose(sfp); */ + + for ( i = 0; i <= dx; i++ ) { + b_cur = fgBucketOffset(min.x(), min.y(), i, j); + clip_and_write_poly( path, index, area, b_cur, clip_row ); + } + } + // string answer; cin >> answer; + } +} diff --git a/src/Lib/Polygon/split.hxx b/src/Lib/Polygon/split.hxx new file mode 100644 index 00000000..55ab2af4 --- /dev/null +++ b/src/Lib/Polygon/split.hxx @@ -0,0 +1,38 @@ +// split.hxx -- polygon splitting 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$ + + +#ifndef _SPLIT_HXX +#define _SPLIT_HXX + + +#include "names.hxx" +#include "polygon.hxx" + + +// process shape (write polygon to all intersecting tiles) +void split_polygon(const string& path, AreaType area, const FGPolygon& shape); + + +#endif // _SPLIT_HXX + + diff --git a/src/Lib/TriangleJRS/Makefile.am b/src/Lib/TriangleJRS/Makefile.am new file mode 100644 index 00000000..7788c150 --- /dev/null +++ b/src/Lib/TriangleJRS/Makefile.am @@ -0,0 +1,24 @@ +# CPPFLAGS 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: +# +# CPPFLAGS = -DTRILIBRARY -DREDUCED -DCDT_ONLY + +CPPFLAGS += -DTRILIBRARY + +noinst_LIBRARIES = libTriangle.a + +libTriangle_a_SOURCES = triangle.c triangle.h + +if HAVE_XWINDOWS + +bin_PROGRAMS = showme +showme_SOURCES = showme.c +showme_LDADD = -lX11 + +endif \ No newline at end of file diff --git a/src/Lib/TriangleJRS/README b/src/Lib/TriangleJRS/README new file mode 100644 index 00000000..571d5689 --- /dev/null +++ b/src/Lib/TriangleJRS/README @@ -0,0 +1,181 @@ +Triangle +A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. +Version 1.3 + +Show Me +A Display Program for Meshes and More. +Version 1.3 + +Copyright 1996 Jonathan Richard Shewchuk +School of Computer Science +Carnegie Mellon University +5000 Forbes Avenue +Pittsburgh, Pennsylvania 15213-3891 +Please send bugs and comments to jrs@cs.cmu.edu + +Created as part of the Archimedes project (tools for parallel FEM). +Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship. +There is no warranty whatsoever. Use at your own risk. + + +Triangle generates exact Delaunay triangulations, constrained Delaunay +triangulations, and quality conforming Delaunay triangulations. The +latter can be generated with no small angles, and are thus suitable for +finite element analysis. Show Me graphically displays the contents of +the geometric files used by Triangle. Show Me can also write images in +PostScript form. + +Information on the algorithms used by Triangle, including complete +references, can be found in the comments at the beginning of the triangle.c +source file. Another listing of these references, with PostScript copies +of some of the papers, is available from the Web page + + http://www.cs.cmu.edu/~quake/triangle.research.html + +------------------------------------------------------------------------------ + +These programs may be freely redistributed under the condition that the +copyright notices (including the copy of this notice in the code comments +and the copyright notice printed when the `-h' switch is selected) are +not removed, and no compensation is received. Private, research, and +institutional use is free. You may distribute modified versions of this +code UNDER THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT +IN THE SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH +SOURCE AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND +CLEAR NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as +part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT +WITH THE AUTHOR. (If you are not directly supplying this code to a +customer, and you are instead telling them how they can obtain it for +free, then you are not required to make any arrangement with me.) + +------------------------------------------------------------------------------ + +The files included in this distribution are: + + README The file you're reading now. + triangle.c Complete C source code for Triangle. + showme.c Complete C source code for Show Me. + triangle.h Include file for calling Triangle from another program. + tricall.c Sample program that calls Triangle. + makefile Makefile for compiling Triangle and Show Me. + A.poly A sample data file. + +Triangle and Show Me are each a single portable C file. The easiest way to +compile them is to edit and use the included makefile. Before compiling, +read the makefile, which describes your options, and edit it accordingly. +You should specify: + + The source and binary directories. + + The C compiler and level of optimization. + + Do you want single precision or double? Do you want to leave out some of + Triangle's features to reduce the size of the executable file? + + The "correct" directories for include files (especially X include files), + if necessary. + +Once you've done this, type "make" to compile the programs. Alternatively, +the files are usually easy to compile without a makefile: + + cc -O -o triangle triangle.c -lm + cc -O -o showme showme.c -lX11 + +On some systems, the C compiler won't be able to find the X include files +or libraries, and you'll need to specify an include path or library path: + + cc -O -I/usr/local/include -o showme showme.c -L/usr/local/lib -lX11 + +However, on other systems (like my workstation), the latter incantation +will cause the wrong files to be read, and the Show Me mouse buttons won't +work properly in the main window. Hence, try the "-I" and "-L" switches +ONLY if the compiler fails without it. (If you're using the makefile, you +may edit it to add this switch.) + +Some processors, possibly including Intel x86 family and Motorola 68xxx +family chips, are IEEE conformant but have extended length internal +floating-point registers that may defeat Triangle's exact arithmetic +routines by failing to cause enough roundoff error! Typically, there is +a way to set these internal registers so that they are rounded off to +IEEE single or double precision format. If you have such a processor, +you should check your C compiler or system manuals to find out how to +configure these internal registers to the precision you are using. +Otherwise, the exact arithmetic routines won't be exact at all. +Unfortunately, I don't have access to any such systems, and can't give +advice on how to configure them. These problems don't occur on any +workstations I am aware of. However, Triangle's exact arithmetic hasn't +a hope of working on machines like the Cray C90 or Y-MP, which are not +IEEE conformant and have inaccurate rounding. + +Triangle and Show Me both produce their own documentation. Complete +instructions are printed by invoking each program with the `-h' switch: + + triangle -h + showme -h + +The instructions are long; you'll probably want to pipe the output to +`more' or `lpr' or redirect it to a file. Both programs give a short list +of command line options if they are invoked without arguments (that is, +just type `triangle' or `showme'). Alternatively, you may want to read +the instructions on the World Wide Web. The appropriate URLs are: + + http://www.cs.cmu.edu/~quake/triangle.html + http://www.cs.cmu.edu/~quake/showme.html + +Try out Triangle on the enclosed sample file, A.poly: + + triangle -p A + showme A.poly & + +Triangle will read the Planar Straight Line Graph defined by A.poly, and +write its constrained Delaunay triangulation to A.1.node and A.1.ele. +Show Me will display the figure defined by A.poly. There are two buttons +marked "ele" in the Show Me window; click on the top one. This will cause +Show Me to load and display the triangulation. + +For contrast, try running + + triangle -pq A + +Now, click on the same "ele" button. A new triangulation will be loaded; +this one having no angles smaller than 20 degrees. + +To see a Voronoi diagram, try this: + + cp A.poly A.node + triangle -v A + +Click the "ele" button again. You will see the Delaunay triangulation of +the points in A.poly, without the segments. Now click the top "voro" button. +You will see the Voronoi diagram corresponding to that Delaunay triangulation. +Click the "Reset" button to see the full extent of the diagram. + +------------------------------------------------------------------------------ + +If you wish to call Triangle from another program, instructions for doing +so are contained in the file `triangle.h' (but read Triangle's regular +instructions first!). Also look at `tricall.c', which provides an example. + +Type "make trilibrary" to create triangle.o, a callable object file. +Alternatively, the object file is usually easy to compile without a +makefile: + + cc -DTRILIBRARY -O -c triangle.c + +------------------------------------------------------------------------------ + +If you use Triangle, and especially if you use it to accomplish real +work, I would like very much to hear from you. A short letter or email +(to jrs@cs.cmu.edu) describing how you use Triangle will mean a lot to +me. The more people I know are using this program, the more easily I can +justify spending time on improvements and on the three-dimensional +successor to Triangle, which in turn will benefit you. Also, I can put +you on a list to receive email whenever a new version of Triangle is +available. + +If you use a mesh generated by Triangle or plotted by Show Me in a +publication, please include an acknowledgment as well. + + +Jonathan Richard Shewchuk +July 20, 1996 diff --git a/src/Lib/TriangleJRS/showme.c b/src/Lib/TriangleJRS/showme.c new file mode 100644 index 00000000..722cba8a --- /dev/null +++ b/src/Lib/TriangleJRS/showme.c @@ -0,0 +1,3384 @@ +/*****************************************************************************/ +/* */ +/* ,d88^^o 888 o o */ +/* 8888 888o^88, o88^^o Y88b o / d8b d8b o88^^8o */ +/* "Y88b 888 888 d888 b Y88b d8b / d888bdY88b d888 88b */ +/* "Y88b, 888 888 8888 8 Y888/Y88b/ / Y88Y Y888b 8888oo888 */ +/* o 8888 888 888 q888 p Y8/ Y8/ / YY Y888b q888 */ +/* "oo88P" 888 888 "88oo" Y Y / Y888b "88oooo" */ +/* */ +/* A Display Program for Meshes and More. */ +/* (showme.c) */ +/* */ +/* Version 1.3 */ +/* July 20, 1996 */ +/* */ +/* Copyright 1996 */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/* This program may be freely redistributed under the condition that the */ +/* copyright notices (including this entire header and the copyright */ +/* notice printed when the `-h' switch is selected) are not removed, and */ +/* no compensation is received. Private, research, and institutional */ +/* use is free. You may distribute modified versions of this code UNDER */ +/* THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE */ +/* SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE */ +/* AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND CLEAR */ +/* NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as */ +/* part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT */ +/* WITH THE AUTHOR. (If you are not directly supplying this code to a */ +/* customer, and you are instead telling them how they can obtain it for */ +/* free, then you are not required to make any arrangement with me.) */ +/* */ +/* Hypertext instructions for Triangle are available on the Web at */ +/* */ +/* http://www.cs.cmu.edu/~quake/showme.html */ +/* */ +/* Show Me was created as part of the Archimedes project in the School of */ +/* Computer Science at Carnegie Mellon University. Archimedes is a */ +/* system for compiling parallel finite element solvers. For further */ +/* information, see Anja Feldmann, Omar Ghattas, John R. Gilbert, Gary L. */ +/* Miller, David R. O'Hallaron, Eric J. Schwabe, Jonathan R. Shewchuk, */ +/* and Shang-Hua Teng. "Automated Parallel Solution of Unstructured PDE */ +/* Problems." To appear in Communications of the ACM, we hope. */ +/* */ +/* If you make any improvements to this code, please please please let me */ +/* know, so that I may obtain the improvements. Even if you don't change */ +/* the code, I'd still love to hear what it's being used for. */ +/* */ +/* Disclaimer: Neither I nor Carnegie Mellon warrant this code in any way */ +/* whatsoever. Use at your own risk. */ +/* */ +/*****************************************************************************/ + +/* For single precision (which will save some memory and reduce paging), */ +/* write "#define SINGLE" below. */ +/* */ +/* For double precision (which will allow you to display triangulations of */ +/* a finer resolution), leave SINGLE undefined. */ + +/* #define SINGLE */ + +#ifdef SINGLE +#define REAL float +#else +#define REAL double +#endif + +/* Maximum number of characters in a file name (including the null). */ + +#define FILENAMESIZE 1024 + +/* Maximum number of characters in a line read from a file (including the */ +/* null). */ + +#define INPUTLINESIZE 512 + +#define STARTWIDTH 414 +#define STARTHEIGHT 414 +#define MINWIDTH 50 +#define MINHEIGHT 50 +#define BUTTONHEIGHT 21 +#define BUTTONROWS 3 +#define PANELHEIGHT (BUTTONHEIGHT * BUTTONROWS) +#define MAXCOLORS 64 + +#define IMAGE_TYPES 7 +#define NOTHING -1 +#define NODE 0 +#define POLY 1 +#define ELE 2 +#define EDGE 3 +#define PART 4 +#define ADJ 5 +#define VORO 6 + +#define STARTEXPLOSION 0.5 + +#include +#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 --git a/src/Lib/TriangleJRS/triangle.c b/src/Lib/TriangleJRS/triangle.c new file mode 100644 index 00000000..9af47a49 --- /dev/null +++ b/src/Lib/TriangleJRS/triangle.c @@ -0,0 +1,13241 @@ +/*****************************************************************************/ +/* */ +/* 888888888 ,o, / 888 */ +/* 888 88o88o " o8888o 88o8888o o88888o 888 o88888o */ +/* 888 888 888 88b 888 888 888 888 888 d888 88b */ +/* 888 888 888 o88^o888 888 888 "88888" 888 8888oo888 */ +/* 888 888 888 C888 888 888 888 / 888 q888 */ +/* 888 888 888 "88o^888 888 888 Cb 888 "88oooo" */ +/* "8oo8D */ +/* */ +/* A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. */ +/* (triangle.c) */ +/* */ +/* Version 1.3 */ +/* July 19, 1996 */ +/* */ +/* Copyright 1996 */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/* This program may be freely redistributed under the condition that the */ +/* copyright notices (including this entire header and the copyright */ +/* notice printed when the `-h' switch is selected) are not removed, and */ +/* no compensation is received. Private, research, and institutional */ +/* use is free. You may distribute modified versions of this code UNDER */ +/* THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE */ +/* SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE */ +/* AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND CLEAR */ +/* NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as */ +/* part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT */ +/* WITH THE AUTHOR. (If you are not directly supplying this code to a */ +/* customer, and you are instead telling them how they can obtain it for */ +/* free, then you are not required to make any arrangement with me.) */ +/* */ +/* Hypertext instructions for Triangle are available on the Web at */ +/* */ +/* http://www.cs.cmu.edu/~quake/triangle.html */ +/* */ +/* Some of the references listed below are marked [*]. These are available */ +/* for downloading from the Web page */ +/* */ +/* http://www.cs.cmu.edu/~quake/triangle.research.html */ +/* */ +/* A paper discussing some aspects of Triangle is available. See Jonathan */ +/* Richard Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator */ +/* and Delaunay Triangulator," First Workshop on Applied Computational */ +/* Geometry, ACM, May 1996. [*] */ +/* */ +/* Triangle was created as part of the Archimedes project in the School of */ +/* Computer Science at Carnegie Mellon University. Archimedes is a */ +/* system for compiling parallel finite element solvers. For further */ +/* information, see Anja Feldmann, Omar Ghattas, John R. Gilbert, Gary L. */ +/* Miller, David R. O'Hallaron, Eric J. Schwabe, Jonathan R. Shewchuk, */ +/* and Shang-Hua Teng, "Automated Parallel Solution of Unstructured PDE */ +/* Problems." To appear in Communications of the ACM, we hope. */ +/* */ +/* The quality mesh generation algorithm is due to Jim Ruppert, "A */ +/* Delaunay Refinement Algorithm for Quality 2-Dimensional Mesh */ +/* Generation," Journal of Algorithms 18(3):548-585, May 1995. [*] */ +/* */ +/* My implementation of the divide-and-conquer and incremental Delaunay */ +/* triangulation algorithms follows closely the presentation of Guibas */ +/* and Stolfi, even though I use a triangle-based data structure instead */ +/* of their quad-edge data structure. (In fact, I originally implemented */ +/* Triangle using the quad-edge data structure, but switching to a */ +/* triangle-based data structure sped Triangle by a factor of two.) The */ +/* mesh manipulation primitives and the two aforementioned Delaunay */ +/* triangulation algorithms are described by Leonidas J. Guibas and Jorge */ +/* Stolfi, "Primitives for the Manipulation of General Subdivisions and */ +/* the Computation of Voronoi Diagrams," ACM Transactions on Graphics */ +/* 4(2):74-123, April 1985. */ +/* */ +/* Their O(n log n) divide-and-conquer algorithm is adapted from Der-Tsai */ +/* Lee and Bruce J. Schachter, "Two Algorithms for Constructing the */ +/* Delaunay Triangulation," International Journal of Computer and */ +/* Information Science 9(3):219-242, 1980. The idea to improve the */ +/* divide-and-conquer algorithm by alternating between vertical and */ +/* horizontal cuts was introduced by Rex A. Dwyer, "A Faster Divide-and- */ +/* Conquer Algorithm for Constructing Delaunay Triangulations," */ +/* Algorithmica 2(2):137-151, 1987. */ +/* */ +/* The incremental insertion algorithm was first proposed by C. L. Lawson, */ +/* "Software for C1 Surface Interpolation," in Mathematical Software III, */ +/* John R. Rice, editor, Academic Press, New York, pp. 161-194, 1977. */ +/* For point location, I use the algorithm of Ernst P. Mucke, Isaac */ +/* Saias, and Binhai Zhu, "Fast Randomized Point Location Without */ +/* Preprocessing in Two- and Three-dimensional Delaunay Triangulations," */ +/* Proceedings of the Twelfth Annual Symposium on Computational Geometry, */ +/* ACM, May 1996. [*] If I were to randomize the order of point */ +/* insertion (I currently don't bother), their result combined with the */ +/* result of Leonidas J. Guibas, Donald E. Knuth, and Micha Sharir, */ +/* "Randomized Incremental Construction of Delaunay and Voronoi */ +/* Diagrams," Algorithmica 7(4):381-413, 1992, would yield an expected */ +/* O(n^{4/3}) bound on running time. */ +/* */ +/* The O(n log n) sweepline Delaunay triangulation algorithm is taken from */ +/* Steven Fortune, "A Sweepline Algorithm for Voronoi Diagrams", */ +/* Algorithmica 2(2):153-174, 1987. A random sample of edges on the */ +/* boundary of the triangulation are maintained in a splay tree for the */ +/* purpose of point location. Splay trees are described by Daniel */ +/* Dominic Sleator and Robert Endre Tarjan, "Self-Adjusting Binary Search */ +/* Trees," Journal of the ACM 32(3):652-686, July 1985. */ +/* */ +/* The algorithms for exact computation of the signs of determinants are */ +/* described in Jonathan Richard Shewchuk, "Adaptive Precision Floating- */ +/* Point Arithmetic and Fast Robust Geometric Predicates," Technical */ +/* Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon */ +/* University, Pittsburgh, Pennsylvania, May 1996. [*] (Submitted to */ +/* Discrete & Computational Geometry.) An abbreviated version appears as */ +/* Jonathan Richard Shewchuk, "Robust Adaptive Floating-Point Geometric */ +/* Predicates," Proceedings of the Twelfth Annual Symposium on Computa- */ +/* tional Geometry, ACM, May 1996. [*] Many of the ideas for my exact */ +/* arithmetic routines originate with Douglas M. Priest, "Algorithms for */ +/* Arbitrary Precision Floating Point Arithmetic," Tenth Symposium on */ +/* Computer Arithmetic, 132-143, IEEE Computer Society Press, 1991. [*] */ +/* Many of the ideas for the correct evaluation of the signs of */ +/* determinants are taken from Steven Fortune and Christopher J. Van Wyk, */ +/* "Efficient Exact Arithmetic for Computational Geometry," Proceedings */ +/* of the Ninth Annual Symposium on Computational Geometry, ACM, */ +/* pp. 163-172, May 1993, and from Steven Fortune, "Numerical Stability */ +/* of Algorithms for 2D Delaunay Triangulations," International Journal */ +/* of Computational Geometry & Applications 5(1-2):193-213, March-June */ +/* 1995. */ +/* */ +/* For definitions of and results involving Delaunay triangulations, */ +/* constrained and conforming versions thereof, and other aspects of */ +/* triangular mesh generation, see the excellent survey by Marshall Bern */ +/* and David Eppstein, "Mesh Generation and Optimal Triangulation," in */ +/* Computing and Euclidean Geometry, Ding-Zhu Du and Frank Hwang, */ +/* editors, World Scientific, Singapore, pp. 23-90, 1992. */ +/* */ +/* The time for incrementally adding PSLG (planar straight line graph) */ +/* segments to create a constrained Delaunay triangulation is probably */ +/* O(n^2) per segment in the worst case and O(n) per edge in the common */ +/* case, where n is the number of triangles that intersect the segment */ +/* before it is inserted. This doesn't count point location, which can */ +/* be much more expensive. (This note does not apply to conforming */ +/* Delaunay triangulations, for which a different method is used to */ +/* insert segments.) */ +/* */ +/* The time for adding segments to a conforming Delaunay triangulation is */ +/* not clear, but does not depend upon n alone. In some cases, very */ +/* small features (like a point lying next to a segment) can cause a */ +/* single segment to be split an arbitrary number of times. Of course, */ +/* floating-point precision is a practical barrier to how much this can */ +/* happen. */ +/* */ +/* The time for deleting a point from a Delaunay triangulation is O(n^2) in */ +/* the worst case and O(n) in the common case, where n is the degree of */ +/* the point being deleted. I could improve this to expected O(n) time */ +/* by "inserting" the neighboring vertices in random order, but n is */ +/* usually quite small, so it's not worth the bother. (The O(n) time */ +/* for random insertion follows from L. Paul Chew, "Building Voronoi */ +/* Diagrams for Convex Polygons in Linear Expected Time," Technical */ +/* Report PCS-TR90-147, Department of Mathematics and Computer Science, */ +/* Dartmouth College, 1990. */ +/* */ +/* Ruppert's Delaunay refinement algorithm typically generates triangles */ +/* at a linear rate (constant time per triangle) after the initial */ +/* triangulation is formed. There may be pathological cases where more */ +/* time is required, but these never arise in practice. */ +/* */ +/* The segment intersection formulae are straightforward. If you want to */ +/* see them derived, see Franklin Antonio. "Faster Line Segment */ +/* Intersection." In Graphics Gems III (David Kirk, editor), pp. 199- */ +/* 202. Academic Press, Boston, 1992. */ +/* */ +/* If you make any improvements to this code, please please please let me */ +/* know, so that I may obtain the improvements. Even if you don't change */ +/* the code, I'd still love to hear what it's being used for. */ +/* */ +/* Disclaimer: Neither I nor Carnegie Mellon warrant this code in any way */ +/* whatsoever. This code is provided "as-is". Use at your own risk. */ +/* */ +/*****************************************************************************/ + +/* For single precision (which will save some memory and reduce paging), */ +/* define the symbol SINGLE by using the -DSINGLE compiler switch or by */ +/* writing "#define SINGLE" below. */ +/* */ +/* For double precision (which will allow you to refine meshes to a smaller */ +/* edge length), leave SINGLE undefined. */ +/* */ +/* Double precision uses more memory, but improves the resolution of the */ +/* meshes you can generate with Triangle. It also reduces the likelihood */ +/* of a floating exception due to overflow. Finally, it is much faster */ +/* than single precision on 64-bit architectures like the DEC Alpha. I */ +/* recommend double precision unless you want to generate a mesh for which */ +/* you do not have enough memory. */ + +/* #define SINGLE */ + +#ifdef SINGLE +#define REAL float +#else /* not SINGLE */ +#define REAL double +#endif /* not SINGLE */ + +/* If yours is not a Unix system, define the NO_TIMER compiler switch to */ +/* remove the Unix-specific timing code. */ + +/* #define NO_TIMER */ + +/* To insert lots of self-checks for internal errors, define the SELF_CHECK */ +/* symbol. This will slow down the program significantly. It is best to */ +/* define the symbol using the -DSELF_CHECK compiler switch, but you could */ +/* write "#define SELF_CHECK" below. If you are modifying this code, I */ +/* recommend you turn self-checks on. */ + +/* #define SELF_CHECK */ + +/* To compile Triangle as a callable object library (triangle.o), define the */ +/* TRILIBRARY symbol. Read the file triangle.h for details on how to call */ +/* the procedure triangulate() that results. */ + +/* #define TRILIBRARY */ + +/* It is possible to generate a smaller version of Triangle using one or */ +/* both of the following symbols. Define the REDUCED symbol to eliminate */ +/* all features that are primarily of research interest; specifically, the */ +/* -i, -F, -s, and -C switches. Define the CDT_ONLY symbol to eliminate */ +/* all meshing algorithms above and beyond constrained Delaunay */ +/* triangulation; specifically, the -r, -q, -a, -S, and -s switches. */ +/* These reductions are most likely to be useful when generating an object */ +/* library (triangle.o) by defining the TRILIBRARY symbol. */ + +/* #define REDUCED */ +/* #define CDT_ONLY */ + +/* On some machines, the exact arithmetic routines might be defeated by the */ +/* use of internal extended precision floating-point registers. Sometimes */ +/* this problem can be fixed by defining certain values to be volatile, */ +/* thus forcing them to be stored to memory and rounded off. This isn't */ +/* a great solution, though, as it slows Triangle down. */ +/* */ +/* To try this out, write "#define INEXACT volatile" below. Normally, */ +/* however, INEXACT should be defined to be nothing. ("#define INEXACT".) */ + +#define INEXACT /* Nothing */ +/* #define INEXACT volatile */ + +/* Maximum number of characters in a file name (including the null). */ + +#define FILENAMESIZE 512 + +/* Maximum number of characters in a line read from a file (including the */ +/* null). */ + +#define INPUTLINESIZE 512 + +/* For efficiency, a variety of data structures are allocated in bulk. The */ +/* following constants determine how many of each structure is allocated */ +/* at once. */ + +#define TRIPERBLOCK 4092 /* Number of triangles allocated at once. */ +#define SHELLEPERBLOCK 508 /* Number of shell edges allocated at once. */ +#define POINTPERBLOCK 4092 /* Number of points allocated at once. */ +#define VIRUSPERBLOCK 1020 /* Number of virus triangles allocated at once. */ +/* Number of encroached segments allocated at once. */ +#define BADSEGMENTPERBLOCK 252 +/* Number of skinny triangles allocated at once. */ +#define BADTRIPERBLOCK 4092 +/* Number of splay tree nodes allocated at once. */ +#define SPLAYNODEPERBLOCK 508 + +/* The point marker DEADPOINT is an arbitrary number chosen large enough to */ +/* (hopefully) not conflict with user boundary markers. Make sure that it */ +/* is small enough to fit into your machine's integer size. */ + +#define DEADPOINT -1073741824 + +/* The next line is used to outsmart some very stupid compilers. If your */ +/* compiler is smarter, feel free to replace the "int" with "void". */ +/* Not that it matters. */ + +#define VOID int + +/* Two constants for algorithms based on random sampling. Both constants */ +/* have been chosen empirically to optimize their respective algorithms. */ + +/* Used for the point location scheme of Mucke, Saias, and Zhu, to decide */ +/* how large a random sample of triangles to inspect. */ +#define SAMPLEFACTOR 11 +/* Used in Fortune's sweepline Delaunay algorithm to determine what fraction */ +/* of boundary edges should be maintained in the splay tree for point */ +/* location on the front. */ +#define SAMPLERATE 10 + +/* A number that speaks for itself, every kissable digit. */ + +#define PI 3.141592653589793238462643383279502884197169399375105820974944592308 + +/* Another fave. */ + +#define SQUAREROOTTWO 1.4142135623730950488016887242096980785696718753769480732 + +/* And here's one for those of you who are intimidated by math. */ + +#define ONETHIRD 0.333333333333333333333333333333333333333333333333333333333333 + +#include +#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 --git a/src/Lib/TriangleJRS/triangle.h b/src/Lib/TriangleJRS/triangle.h new file mode 100644 index 00000000..b9be696c --- /dev/null +++ b/src/Lib/TriangleJRS/triangle.h @@ -0,0 +1,289 @@ +/*****************************************************************************/ +/* */ +/* (triangle.h) */ +/* */ +/* Include file for programs that call Triangle. */ +/* */ +/* Accompanies Triangle Version 1.3 */ +/* July 19, 1996 */ +/* */ +/* Copyright 1996 */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* How to call Triangle from another program */ +/* */ +/* */ +/* If you haven't read Triangle's instructions (run "triangle -h" to read */ +/* them), you won't understand what follows. */ +/* */ +/* Triangle must be compiled into an object file (triangle.o) with the */ +/* TRILIBRARY symbol defined (preferably by using the -DTRILIBRARY compiler */ +/* switch). The makefile included with Triangle will do this for you if */ +/* you run "make trilibrary". The resulting object file can be called via */ +/* the procedure triangulate(). */ +/* */ +/* If the size of the object file is important to you, you may wish to */ +/* generate a reduced version of triangle.o. The REDUCED symbol gets rid */ +/* of all features that are primarily of research interest. Specifically, */ +/* the -DREDUCED switch eliminates Triangle's -i, -F, -s, and -C switches. */ +/* The CDT_ONLY symbol gets rid of all meshing algorithms above and beyond */ +/* constrained Delaunay triangulation. Specifically, the -DCDT_ONLY switch */ +/* eliminates Triangle's -r, -q, -a, -S, and -s switches. */ +/* */ +/* IMPORTANT: These definitions (TRILIBRARY, REDUCED, CDT_ONLY) must be */ +/* made in the makefile or in triangle.c itself. Putting these definitions */ +/* in this file will not create the desired effect. */ +/* */ +/* */ +/* The calling convention for triangulate() follows. */ +/* */ +/* void triangulate(triswitches, in, out, vorout) */ +/* char *triswitches; */ +/* struct triangulateio *in; */ +/* struct triangulateio *out; */ +/* struct triangulateio *vorout; */ +/* */ +/* `triswitches' is a string containing the command line switches you wish */ +/* to invoke. No initial dash is required. Some suggestions: */ +/* */ +/* - You'll probably find it convenient to use the `z' switch so that */ +/* points (and other items) are numbered from zero. This simplifies */ +/* indexing, because the first item of any type always starts at index */ +/* [0] of the corresponding array, whether that item's number is zero or */ +/* one. */ +/* - You'll probably want to use the `Q' (quiet) switch in your final code, */ +/* but you can take advantage of Triangle's printed output (including the */ +/* `V' switch) while debugging. */ +/* - If you are not using the `q' or `a' switches, then the output points */ +/* will be identical to the input points, except possibly for the */ +/* boundary markers. If you don't need the boundary markers, you should */ +/* use the `N' (no nodes output) switch to save memory. (If you do need */ +/* boundary markers, but need to save memory, a good nasty trick is to */ +/* set out->pointlist equal to in->pointlist before calling triangulate(),*/ +/* so that Triangle overwrites the input points with identical copies.) */ +/* - The `I' (no iteration numbers) and `g' (.off file output) switches */ +/* have no effect when Triangle is compiled with TRILIBRARY defined. */ +/* */ +/* `in', `out', and `vorout' are descriptions of the input, the output, */ +/* and the Voronoi output. If the `v' (Voronoi output) switch is not used, */ +/* `vorout' may be NULL. `in' and `out' may never be NULL. */ +/* */ +/* Certain fields of the input and output structures must be initialized, */ +/* as described below. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* The `triangulateio' structure. */ +/* */ +/* Used to pass data into and out of the triangulate() procedure. */ +/* */ +/* */ +/* Arrays are used to store points, triangles, markers, and so forth. In */ +/* all cases, the first item in any array is stored starting at index [0]. */ +/* However, that item is item number `1' unless the `z' switch is used, in */ +/* which case it is item number `0'. Hence, you may find it easier to */ +/* index points (and triangles in the neighbor list) if you use the `z' */ +/* switch. Unless, of course, you're calling Triangle from a Fortran */ +/* program. */ +/* */ +/* Description of fields (except the `numberof' fields, which are obvious): */ +/* */ +/* `pointlist': An array of point coordinates. The first point's x */ +/* coordinate is at index [0] and its y coordinate at index [1], followed */ +/* by the coordinates of the remaining points. Each point occupies two */ +/* REALs. */ +/* `pointattributelist': An array of point attributes. Each point's */ +/* attributes occupy `numberofpointattributes' REALs. */ +/* `pointmarkerlist': An array of point markers; one int per point. */ +/* */ +/* `trianglelist': An array of triangle corners. The first triangle's */ +/* first corner is at index [0], followed by its other two corners in */ +/* counterclockwise order, followed by any other nodes if the triangle */ +/* represents a nonlinear element. Each triangle occupies */ +/* `numberofcorners' ints. */ +/* `triangleattributelist': An array of triangle attributes. Each */ +/* triangle's attributes occupy `numberoftriangleattributes' REALs. */ +/* `trianglearealist': An array of triangle area constraints; one REAL per */ +/* triangle. Input only. */ +/* `neighborlist': An array of triangle neighbors; three ints per */ +/* triangle. Output only. */ +/* */ +/* `segmentlist': An array of segment endpoints. The first segment's */ +/* endpoints are at indices [0] and [1], followed by the remaining */ +/* segments. Two ints per segment. */ +/* `segmentmarkerlist': An array of segment markers; one int per segment. */ +/* */ +/* `holelist': An array of holes. The first hole's x and y coordinates */ +/* are at indices [0] and [1], followed by the remaining holes. Two */ +/* REALs per hole. Input only, although the pointer is copied to the */ +/* output structure for your convenience. */ +/* */ +/* `regionlist': An array of regional attributes and area constraints. */ +/* The first constraint's x and y coordinates are at indices [0] and [1], */ +/* followed by the regional attribute and index [2], followed by the */ +/* maximum area at index [3], followed by the remaining area constraints. */ +/* Four REALs per area constraint. Note that each regional attribute is */ +/* used only if you select the `A' switch, and each area constraint is */ +/* used only if you select the `a' switch (with no number following), but */ +/* omitting one of these switches does not change the memory layout. */ +/* Input only, although the pointer is copied to the output structure for */ +/* your convenience. */ +/* */ +/* `edgelist': An array of edge endpoints. The first edge's endpoints are */ +/* at indices [0] and [1], followed by the remaining edges. Two ints per */ +/* edge. Output only. */ +/* `edgemarkerlist': An array of edge markers; one int per edge. Output */ +/* only. */ +/* `normlist': An array of normal vectors, used for infinite rays in */ +/* Voronoi diagrams. The first normal vector's x and y magnitudes are */ +/* at indices [0] and [1], followed by the remaining vectors. For each */ +/* finite edge in a Voronoi diagram, the normal vector written is the */ +/* zero vector. Two REALs per edge. Output only. */ +/* */ +/* */ +/* Any input fields that Triangle will examine must be initialized. */ +/* Furthermore, for each output array that Triangle will write to, you */ +/* must either provide space by setting the appropriate pointer to point */ +/* to the space you want the data written to, or you must initialize the */ +/* pointer to NULL, which tells Triangle to allocate space for the results. */ +/* The latter option is preferable, because Triangle always knows exactly */ +/* how much space to allocate. The former option is provided mainly for */ +/* people who need to call Triangle from Fortran code, though it also makes */ +/* possible some nasty space-saving tricks, like writing the output to the */ +/* same arrays as the input. */ +/* */ +/* Triangle will not free() any input or output arrays, including those it */ +/* allocates itself; that's up to you. */ +/* */ +/* Here's a guide to help you decide which fields you must initialize */ +/* before you call triangulate(). */ +/* */ +/* `in': */ +/* */ +/* - `pointlist' must always point to a list of points; `numberofpoints' */ +/* and `numberofpointattributes' must be properly set. */ +/* `pointmarkerlist' must either be set to NULL (in which case all */ +/* markers default to zero), or must point to a list of markers. If */ +/* `numberofpointattributes' is not zero, `pointattributelist' must */ +/* point to a list of point attributes. */ +/* - If the `r' switch is used, `trianglelist' must point to a list of */ +/* triangles, and `numberoftriangles', `numberofcorners', and */ +/* `numberoftriangleattributes' must be properly set. If */ +/* `numberoftriangleattributes' is not zero, `triangleattributelist' */ +/* must point to a list of triangle attributes. If the `a' switch is */ +/* used (with no number following), `trianglearealist' must point to a */ +/* list of triangle area constraints. `neighborlist' may be ignored. */ +/* - If the `p' switch is used, `segmentlist' must point to a list of */ +/* segments, `numberofsegments' must be properly set, and */ +/* `segmentmarkerlist' must either be set to NULL (in which case all */ +/* markers default to zero), or must point to a list of markers. */ +/* - If the `p' switch is used without the `r' switch, then */ +/* `numberofholes' and `numberofregions' must be properly set. If */ +/* `numberofholes' is not zero, `holelist' must point to a list of */ +/* holes. If `numberofregions' is not zero, `regionlist' must point to */ +/* a list of region constraints. */ +/* - If the `p' switch is used, `holelist', `numberofholes', */ +/* `regionlist', and `numberofregions' is copied to `out'. (You can */ +/* nonetheless get away with not initializing them if the `r' switch is */ +/* used.) */ +/* - `edgelist', `edgemarkerlist', `normlist', and `numberofedges' may be */ +/* ignored. */ +/* */ +/* `out': */ +/* */ +/* - `pointlist' must be initialized (NULL or pointing to memory) unless */ +/* the `N' switch is used. `pointmarkerlist' must be initialized */ +/* unless the `N' or `B' switch is used. If `N' is not used and */ +/* `in->numberofpointattributes' is not zero, `pointattributelist' must */ +/* be initialized. */ +/* - `trianglelist' must be initialized unless the `E' switch is used. */ +/* `neighborlist' must be initialized if the `n' switch is used. If */ +/* the `E' switch is not used and (`in->numberofelementattributes' is */ +/* not zero or the `A' switch is used), `elementattributelist' must be */ +/* initialized. `trianglearealist' may be ignored. */ +/* - `segmentlist' must be initialized if the `p' or `c' switch is used, */ +/* and the `P' switch is not used. `segmentmarkerlist' must also be */ +/* initialized under these circumstances unless the `B' switch is used. */ +/* - `edgelist' must be initialized if the `e' switch is used. */ +/* `edgemarkerlist' must be initialized if the `e' switch is used and */ +/* the `B' switch is not. */ +/* - `holelist', `regionlist', `normlist', and all scalars may be ignored.*/ +/* */ +/* `vorout' (only needed if `v' switch is used): */ +/* */ +/* - `pointlist' must be initialized. If `in->numberofpointattributes' */ +/* is not zero, `pointattributelist' must be initialized. */ +/* `pointmarkerlist' may be ignored. */ +/* - `edgelist' and `normlist' must both be initialized. */ +/* `edgemarkerlist' may be ignored. */ +/* - Everything else may be ignored. */ +/* */ +/* After a call to triangulate(), the valid fields of `out' and `vorout' */ +/* will depend, in an obvious way, on the choice of switches used. Note */ +/* that when the `p' switch is used, the pointers `holelist' and */ +/* `regionlist' are copied from `in' to `out', but no new space is */ +/* allocated; be careful that you don't free() the same array twice. On */ +/* the other hand, Triangle will never copy the `pointlist' pointer (or any */ +/* others); new space is allocated for `out->pointlist', or if the `N' */ +/* switch is used, `out->pointlist' remains uninitialized. */ +/* */ +/* All of the meaningful `numberof' fields will be properly set; for */ +/* instance, `numberofedges' will represent the number of edges in the */ +/* triangulation whether or not the edges were written. If segments are */ +/* not used, `numberofsegments' will indicate the number of boundary edges. */ +/* */ +/*****************************************************************************/ + +/* CLO: 3/21/99 - this could be done as a compile flag, but I always want +this defined and I don't want to sprinkle extra stuff throughout the +Makefile system if I don't have to. */ +#define ANSI_DECLARATORS 1 + +struct triangulateio { + REAL *pointlist; /* In / out */ + REAL *pointattributelist; /* In / out */ + int *pointmarkerlist; /* In / out */ + int numberofpoints; /* In / out */ + int numberofpointattributes; /* In / out */ + + int *trianglelist; /* In / out */ + REAL *triangleattributelist; /* In / out */ + REAL *trianglearealist; /* In only */ + int *neighborlist; /* Out only */ + int numberoftriangles; /* In / out */ + int numberofcorners; /* In / out */ + int numberoftriangleattributes; /* In / out */ + + int *segmentlist; /* In / out */ + int *segmentmarkerlist; /* In / out */ + int numberofsegments; /* In / out */ + + REAL *holelist; /* In / pointer to array copied out */ + int numberofholes; /* In / copied out */ + + REAL *regionlist; /* In / pointer to array copied out */ + int numberofregions; /* In / copied out */ + + int *edgelist; /* Out only */ + int *edgemarkerlist; /* Not used with Voronoi diagram; out only */ + REAL *normlist; /* Used only with Voronoi diagram; out only */ + int numberofedges; /* Out only */ +}; + +#ifdef ANSI_DECLARATORS +void triangulate(char *, struct triangulateio *, struct triangulateio *, + struct triangulateio *); +#else /* not ANSI_DECLARATORS */ +void triangulate(); +#endif /* not ANSI_DECLARATORS */ diff --git a/src/Lib/poly2tri/Makefile.am b/src/Lib/poly2tri/Makefile.am new file mode 100644 index 00000000..8acaf613 --- /dev/null +++ b/src/Lib/poly2tri/Makefile.am @@ -0,0 +1,19 @@ +EXTRA_DIST = README makefile.orig data_1 + +# if OLD_AUTOMAKE +# CFLAGS += -DSTANDALONE +# else +# AM_CFLAGS = -DSTANDALONE +# endif + +noinst_LIBRARIES = libpoly2tri.a + +libpoly2tri_a_SOURCES = \ + construct.c \ + interface.h \ + misc.c \ + monotone.c \ + tri.c \ + triangulate.h + +# INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib diff --git a/src/Lib/poly2tri/README b/src/Lib/poly2tri/README new file mode 100644 index 00000000..161cbeb1 --- /dev/null +++ b/src/Lib/poly2tri/README @@ -0,0 +1,96 @@ + This program is an implementation of a fast polygon +triangulation algorithm based on the paper "A simple and fast +incremental randomized algorithm for computing trapezoidal +decompositions and for triangulating polygons" by Raimund Seidel. + +(A copy of the implementation report is available as + http://www.cs.unc.edu/~manocha/CODE/GEM/chapter.html) + + The algorithm handles simple polygons with holes. The input is +specified as contours. The outermost contour is anti-clockwise, while +all the inner contours must be clockwise. No point should be repeated +in the input. A sample input file 'data_1' is provided. + + + The output is a list of triangles. Each triangle gives a pair +(i, j, k) where i, j, and k are indices of the vertices specified in +the input array. (The index numbering starts from 1, since the first +location v[0] in the input array of vertices is unused). The number of +output triangles produced for a polygon with n points is, + (n - 2) + 2*(#holes) + + + The algorithm also generates a qyery structure which can be +used to answer point-location queries very fast. + + int triangulate_polygon(...) +Time for triangulation: O(n log*n) + + int is_point_inside_polygon(...) +Time for query : O(log n) + + Both the routines are defined in 'tri.c'. See that file for +interfacing details. If not used stand_alone, include the header file +"interface.h" which contains the declarations for these +functions. Inclusion of "triangulation.h" is not necessary. + + + The implementation uses statically allocated arrays. Choose +appropriate value for SEGSIZE /* in triangulate.h */ depending on +input size. + + + There sould not be any compilation problem. If log2() is not +defined in your math library, you will have to supply the definition. + + +USAGE: + triangulate /* For standalone */ + + +------------------------------------------------------------------ +Bibliography: + + +@article{Sei91, + AUTHOR = "R. Seidel", + TITLE = "A simple and Fast Randomized Algorithm for Computing Trapezoidal Decompositions and for Triangulating Polygons", + JOURNAL = "Computational Geometry Theory \& Applications", + PAGES = "51-64", + NUMBER = 1, + YEAR = 1991, + VOLUME = 1 } + + +@book{o-cgc-94 +, author = "J. O'Rourke" +, title = "Computational Geometry in {C}" +, publisher = "Cambridge University Press" +, year = 1994 +, note = "ISBN 0-521-44592-2/Pb \$24.95, + ISBN 0-521-44034-3/Hc \$49.95. + Cambridge University Press + 40 West 20th Street + New York, NY 10011-4211 + 1-800-872-7423 + 346+xi pages, 228 exercises, 200 figures, 219 references" +, update = "94.05 orourke, 94.01 orourke" +, annote = "Textbook" +} + + + +Implementation report: Narkhede A. and Manocha D., Fast polygon + triangulation algorithm based on Seidel's Algorithm, UNC-CH, 1994. + +------------------------------------------------------------------- + +UNC-CH GIVES NO WARRANTY, EXPRESSED OR IMPLIED, FOR THE SOFTWARE +AND/OR DOCUMENTATION PROVIDED, INCLUDING, WITHOUT LIMITATION, WARRANTY +OF MERCHANTABILITY AND WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. + +This software is for non-commercial use only. + + + + - Atul Narkhede (narkhede@cs.unc.edu) diff --git a/src/Lib/poly2tri/construct.c b/src/Lib/poly2tri/construct.c new file mode 100644 index 00000000..6c18041f --- /dev/null +++ b/src/Lib/poly2tri/construct.c @@ -0,0 +1,1055 @@ +#include +#include + + +node_t qs[QSIZE]; /* Query structure */ +trap_t tr[TRSIZE]; /* Trapezoid structure */ +segment_t seg[SEGSIZE]; /* Segment table */ + +static int q_idx; +static int tr_idx; + +/* Return a new node to be added into the query tree */ +static int newnode() +{ + if (q_idx < QSIZE) + return q_idx++; + else + { + fprintf(stderr, "newnode: Query-table overflow\n"); + return -1; + } +} + +/* Return a free trapezoid */ +static int newtrap() +{ + if (tr_idx < TRSIZE) + { + tr[tr_idx].lseg = -1; + tr[tr_idx].rseg = -1; + tr[tr_idx].state = ST_VALID; + return tr_idx++; + } + else + { + fprintf(stderr, "newtrap: Trapezoid-table overflow\n"); + return -1; + } +} + + +/* Return the maximum of the two points into the yval structure */ +static int _max(yval, v0, v1) + point_t *yval; + point_t *v0; + point_t *v1; +{ + if (v0->y > v1->y + C_EPS) + *yval = *v0; + else if (FP_EQUAL(v0->y, v1->y)) + { + if (v0->x > v1->x + C_EPS) + *yval = *v0; + else + *yval = *v1; + } + else + *yval = *v1; + + return 0; +} + + +/* Return the minimum of the two points into the yval structure */ +static int _min(yval, v0, v1) + point_t *yval; + point_t *v0; + point_t *v1; +{ + if (v0->y < v1->y - C_EPS) + *yval = *v0; + else if (FP_EQUAL(v0->y, v1->y)) + { + if (v0->x < v1->x) + *yval = *v0; + else + *yval = *v1; + } + else + *yval = *v1; + + return 0; +} + + +int _greater_than(v0, v1) + point_t *v0; + point_t *v1; +{ + if (v0->y > v1->y + C_EPS) + return TRUE; + else if (v0->y < v1->y - C_EPS) + return FALSE; + else + return (v0->x > v1->x); +} + + +int _equal_to(v0, v1) + point_t *v0; + point_t *v1; +{ + return (FP_EQUAL(v0->y, v1->y) && FP_EQUAL(v0->x, v1->x)); +} + +int _greater_than_equal_to(v0, v1) + point_t *v0; + point_t *v1; +{ + if (v0->y > v1->y + C_EPS) + return TRUE; + else if (v0->y < v1->y - C_EPS) + return FALSE; + else + return (v0->x >= v1->x); +} + +int _less_than(v0, v1) + point_t *v0; + point_t *v1; +{ + if (v0->y < v1->y - C_EPS) + return TRUE; + else if (v0->y > v1->y + C_EPS) + return FALSE; + else + return (v0->x < v1->x); +} + + +/* Initilialise the query structure (Q) and the trapezoid table (T) + * when the first segment is added to start the trapezoidation. The + * query-tree starts out with 4 trapezoids, one S-node and 2 Y-nodes + * + * 4 + * ----------------------------------- + * \ + * 1 \ 2 + * \ + * ----------------------------------- + * 3 + */ + +static int init_query_structure(segnum) + int segnum; +{ + int i1, i2, i3, i4, i5, i6, i7, root; + int t1, t2, t3, t4; + segment_t *s = &seg[segnum]; + + q_idx = tr_idx = 1; + memset((void *)tr, 0, sizeof(tr)); + memset((void *)qs, 0, sizeof(qs)); + + i1 = newnode(); + qs[i1].nodetype = T_Y; + _max(&qs[i1].yval, &s->v0, &s->v1); /* root */ + root = i1; + + qs[i1].right = i2 = newnode(); + qs[i2].nodetype = T_SINK; + qs[i2].parent = i1; + + qs[i1].left = i3 = newnode(); + qs[i3].nodetype = T_Y; + _min(&qs[i3].yval, &s->v0, &s->v1); /* root */ + qs[i3].parent = i1; + + qs[i3].left = i4 = newnode(); + qs[i4].nodetype = T_SINK; + qs[i4].parent = i3; + + qs[i3].right = i5 = newnode(); + qs[i5].nodetype = T_X; + qs[i5].segnum = segnum; + qs[i5].parent = i3; + + qs[i5].left = i6 = newnode(); + qs[i6].nodetype = T_SINK; + qs[i6].parent = i5; + + qs[i5].right = i7 = newnode(); + qs[i7].nodetype = T_SINK; + qs[i7].parent = i5; + + t1 = newtrap(); /* middle left */ + t2 = newtrap(); /* middle right */ + t3 = newtrap(); /* bottom-most */ + t4 = newtrap(); /* topmost */ + + tr[t1].hi = tr[t2].hi = tr[t4].lo = qs[i1].yval; + tr[t1].lo = tr[t2].lo = tr[t3].hi = qs[i3].yval; + tr[t4].hi.y = (double) (INFINITY); + tr[t4].hi.x = (double) (INFINITY); + tr[t3].lo.y = (double) -1* (INFINITY); + tr[t3].lo.x = (double) -1* (INFINITY); + tr[t1].rseg = tr[t2].lseg = segnum; + tr[t1].u0 = tr[t2].u0 = t4; + tr[t1].d0 = tr[t2].d0 = t3; + tr[t4].d0 = tr[t3].u0 = t1; + tr[t4].d1 = tr[t3].u1 = t2; + + tr[t1].sink = i6; + tr[t2].sink = i7; + tr[t3].sink = i4; + tr[t4].sink = i2; + + tr[t1].state = tr[t2].state = ST_VALID; + tr[t3].state = tr[t4].state = ST_VALID; + + qs[i2].trnum = t4; + qs[i4].trnum = t3; + qs[i6].trnum = t1; + qs[i7].trnum = t2; + + s->is_inserted = TRUE; + return root; +} + + +/* Retun TRUE if the vertex v is to the left of line segment no. + * segnum. Takes care of the degenerate cases when both the vertices + * have the same y--cood, etc. + */ + +static int is_left_of(segnum, v) + int segnum; + point_t *v; +{ + segment_t *s = &seg[segnum]; + double area; + + if (_greater_than(&s->v1, &s->v0)) /* seg. going upwards */ + { + if (FP_EQUAL(s->v1.y, v->y)) + { + if (v->x < s->v1.x) + area = 1.0; + else + area = -1.0; + } + else if (FP_EQUAL(s->v0.y, v->y)) + { + if (v->x < s->v0.x) + area = 1.0; + else + area = -1.0; + } + else + area = CROSS(s->v0, s->v1, (*v)); + } + else /* v0 > v1 */ + { + if (FP_EQUAL(s->v1.y, v->y)) + { + if (v->x < s->v1.x) + area = 1.0; + else + area = -1.0; + } + else if (FP_EQUAL(s->v0.y, v->y)) + { + if (v->x < s->v0.x) + area = 1.0; + else + area = -1.0; + } + else + area = CROSS(s->v1, s->v0, (*v)); + } + + if (area > 0.0) + return TRUE; + else + return FALSE; +} + + + +/* Returns true if the corresponding endpoint of the given segment is */ +/* already inserted into the segment tree. Use the simple test of */ +/* whether the segment which shares this endpoint is already inserted */ + +static int inserted(segnum, whichpt) + int segnum; + int whichpt; +{ + if (whichpt == FIRSTPT) + return seg[seg[segnum].prev].is_inserted; + else + return seg[seg[segnum].next].is_inserted; +} + +/* This is query routine which determines which trapezoid does the + * point v lie in. The return value is the trapezoid number. + */ + +int locate_endpoint(v, vo, r) + point_t *v; + point_t *vo; + int r; +{ + node_t *rptr = &qs[r]; + + switch (rptr->nodetype) + { + case T_SINK: + return rptr->trnum; + + case T_Y: + if (_greater_than(v, &rptr->yval)) /* above */ + return locate_endpoint(v, vo, rptr->right); + else if (_equal_to(v, &rptr->yval)) /* the point is already */ + { /* inserted. */ + if (_greater_than(vo, &rptr->yval)) /* above */ + return locate_endpoint(v, vo, rptr->right); + else + return locate_endpoint(v, vo, rptr->left); /* below */ + } + else + return locate_endpoint(v, vo, rptr->left); /* below */ + + case T_X: + if (_equal_to(v, &seg[rptr->segnum].v0) || + _equal_to(v, &seg[rptr->segnum].v1)) + { + if (FP_EQUAL(v->y, vo->y)) /* horizontal segment */ + { + if (vo->x < v->x) + return locate_endpoint(v, vo, rptr->left); /* left */ + else + return locate_endpoint(v, vo, rptr->right); /* right */ + } + + else if (is_left_of(rptr->segnum, vo)) + return locate_endpoint(v, vo, rptr->left); /* left */ + else + return locate_endpoint(v, vo, rptr->right); /* right */ + } + else if (is_left_of(rptr->segnum, v)) + return locate_endpoint(v, vo, rptr->left); /* left */ + else + return locate_endpoint(v, vo, rptr->right); /* right */ + + default: + fprintf(stderr, "Haggu !!!!!\n"); + break; + } +} + + +/* Thread in the segment into the existing trapezoidation. The + * limiting trapezoids are given by tfirst and tlast (which are the + * trapezoids containing the two endpoints of the segment. Merges all + * possible trapezoids which flank this segment and have been recently + * divided because of its insertion + */ + +static int merge_trapezoids(segnum, tfirst, tlast, side) + int segnum; + int tfirst; + int tlast; + int side; +{ + int t, tnext, cond; + int ptnext; + + /* First merge polys on the LHS */ + t = tfirst; + while ((t > 0) && _greater_than_equal_to(&tr[t].lo, &tr[tlast].lo)) + { + if (side == S_LEFT) + cond = ((((tnext = tr[t].d0) > 0) && (tr[tnext].rseg == segnum)) || + (((tnext = tr[t].d1) > 0) && (tr[tnext].rseg == segnum))); + else + cond = ((((tnext = tr[t].d0) > 0) && (tr[tnext].lseg == segnum)) || + (((tnext = tr[t].d1) > 0) && (tr[tnext].lseg == segnum))); + + if (cond) + { + if ((tr[t].lseg == tr[tnext].lseg) && + (tr[t].rseg == tr[tnext].rseg)) /* good neighbours */ + { /* merge them */ + /* Use the upper node as the new node i.e. t */ + + ptnext = qs[tr[tnext].sink].parent; + + if (qs[ptnext].left == tr[tnext].sink) + qs[ptnext].left = tr[t].sink; + else + qs[ptnext].right = tr[t].sink; /* redirect parent */ + + + /* Change the upper neighbours of the lower trapezoids */ + + if ((tr[t].d0 = tr[tnext].d0) > 0) + if (tr[tr[t].d0].u0 == tnext) + tr[tr[t].d0].u0 = t; + else if (tr[tr[t].d0].u1 == tnext) + tr[tr[t].d0].u1 = t; + + if ((tr[t].d1 = tr[tnext].d1) > 0) + if (tr[tr[t].d1].u0 == tnext) + tr[tr[t].d1].u0 = t; + else if (tr[tr[t].d1].u1 == tnext) + tr[tr[t].d1].u1 = t; + + tr[t].lo = tr[tnext].lo; + tr[tnext].state = ST_INVALID; /* invalidate the lower */ + /* trapezium */ + } + else /* not good neighbours */ + t = tnext; + } + else /* do not satisfy the outer if */ + t = tnext; + + } /* end-while */ + + return 0; +} + + +/* Add in the new segment into the trapezoidation and update Q and T + * structures. First locate the two endpoints of the segment in the + * Q-structure. Then start from the topmost trapezoid and go down to + * the lower trapezoid dividing all the trapezoids in between . + */ + +static int add_segment(segnum) + int segnum; +{ + segment_t s; + segment_t *so = &seg[segnum]; + int tu, tl, sk, tfirst, tlast, tnext; + int tfirstr, tlastr, tfirstl, tlastl; + int i1, i2, t, t1, t2, tn; + point_t tpt; + int tritop = 0, tribot = 0, is_swapped = 0; + int tmptriseg; + + s = seg[segnum]; + if (_greater_than(&s.v1, &s.v0)) /* Get higher vertex in v0 */ + { + int tmp; + tpt = s.v0; + s.v0 = s.v1; + s.v1 = tpt; + tmp = s.root0; + s.root0 = s.root1; + s.root1 = tmp; + is_swapped = TRUE; + } + + if ((is_swapped) ? !inserted(segnum, LASTPT) : + !inserted(segnum, FIRSTPT)) /* insert v0 in the tree */ + { + int tmp_d; + + tu = locate_endpoint(&s.v0, &s.v1, s.root0); + tl = newtrap(); /* tl is the new lower trapezoid */ + tr[tl].state = ST_VALID; + tr[tl] = tr[tu]; + tr[tu].lo.y = tr[tl].hi.y = s.v0.y; + tr[tu].lo.x = tr[tl].hi.x = s.v0.x; + tr[tu].d0 = tl; + tr[tu].d1 = 0; + tr[tl].u0 = tu; + tr[tl].u1 = 0; + + if (((tmp_d = tr[tl].d0) > 0) && (tr[tmp_d].u0 == tu)) + tr[tmp_d].u0 = tl; + if (((tmp_d = tr[tl].d0) > 0) && (tr[tmp_d].u1 == tu)) + tr[tmp_d].u1 = tl; + + if (((tmp_d = tr[tl].d1) > 0) && (tr[tmp_d].u0 == tu)) + tr[tmp_d].u0 = tl; + if (((tmp_d = tr[tl].d1) > 0) && (tr[tmp_d].u1 == tu)) + tr[tmp_d].u1 = tl; + + /* Now update the query structure and obtain the sinks for the */ + /* two trapezoids */ + + i1 = newnode(); /* Upper trapezoid sink */ + i2 = newnode(); /* Lower trapezoid sink */ + sk = tr[tu].sink; + + qs[sk].nodetype = T_Y; + qs[sk].yval = s.v0; + qs[sk].segnum = segnum; /* not really reqd ... maybe later */ + qs[sk].left = i2; + qs[sk].right = i1; + + qs[i1].nodetype = T_SINK; + qs[i1].trnum = tu; + qs[i1].parent = sk; + + qs[i2].nodetype = T_SINK; + qs[i2].trnum = tl; + qs[i2].parent = sk; + + tr[tu].sink = i1; + tr[tl].sink = i2; + tfirst = tl; + } + else /* v0 already present */ + { /* Get the topmost intersecting trapezoid */ + tfirst = locate_endpoint(&s.v0, &s.v1, s.root0); + tritop = 1; + } + + + if ((is_swapped) ? !inserted(segnum, FIRSTPT) : + !inserted(segnum, LASTPT)) /* insert v1 in the tree */ + { + int tmp_d; + + tu = locate_endpoint(&s.v1, &s.v0, s.root1); + + tl = newtrap(); /* tl is the new lower trapezoid */ + tr[tl].state = ST_VALID; + tr[tl] = tr[tu]; + tr[tu].lo.y = tr[tl].hi.y = s.v1.y; + tr[tu].lo.x = tr[tl].hi.x = s.v1.x; + tr[tu].d0 = tl; + tr[tu].d1 = 0; + tr[tl].u0 = tu; + tr[tl].u1 = 0; + + if (((tmp_d = tr[tl].d0) > 0) && (tr[tmp_d].u0 == tu)) + tr[tmp_d].u0 = tl; + if (((tmp_d = tr[tl].d0) > 0) && (tr[tmp_d].u1 == tu)) + tr[tmp_d].u1 = tl; + + if (((tmp_d = tr[tl].d1) > 0) && (tr[tmp_d].u0 == tu)) + tr[tmp_d].u0 = tl; + if (((tmp_d = tr[tl].d1) > 0) && (tr[tmp_d].u1 == tu)) + tr[tmp_d].u1 = tl; + + /* Now update the query structure and obtain the sinks for the */ + /* two trapezoids */ + + i1 = newnode(); /* Upper trapezoid sink */ + i2 = newnode(); /* Lower trapezoid sink */ + sk = tr[tu].sink; + + qs[sk].nodetype = T_Y; + qs[sk].yval = s.v1; + qs[sk].segnum = segnum; /* not really reqd ... maybe later */ + qs[sk].left = i2; + qs[sk].right = i1; + + qs[i1].nodetype = T_SINK; + qs[i1].trnum = tu; + qs[i1].parent = sk; + + qs[i2].nodetype = T_SINK; + qs[i2].trnum = tl; + qs[i2].parent = sk; + + tr[tu].sink = i1; + tr[tl].sink = i2; + tlast = tu; + } + else /* v1 already present */ + { /* Get the lowermost intersecting trapezoid */ + tlast = locate_endpoint(&s.v1, &s.v0, s.root1); + tribot = 1; + } + + /* Thread the segment into the query tree creating a new X-node */ + /* First, split all the trapezoids which are intersected by s into */ + /* two */ + + t = tfirst; /* topmost trapezoid */ + + while ((t > 0) && + _greater_than_equal_to(&tr[t].lo, &tr[tlast].lo)) + /* traverse from top to bot */ + { + int t_sav, tn_sav; + sk = tr[t].sink; + i1 = newnode(); /* left trapezoid sink */ + i2 = newnode(); /* right trapezoid sink */ + + qs[sk].nodetype = T_X; + qs[sk].segnum = segnum; + qs[sk].left = i1; + qs[sk].right = i2; + + qs[i1].nodetype = T_SINK; /* left trapezoid (use existing one) */ + qs[i1].trnum = t; + qs[i1].parent = sk; + + qs[i2].nodetype = T_SINK; /* right trapezoid (allocate new) */ + qs[i2].trnum = tn = newtrap(); + tr[tn].state = ST_VALID; + qs[i2].parent = sk; + + if (t == tfirst) + tfirstr = tn; + if (_equal_to(&tr[t].lo, &tr[tlast].lo)) + tlastr = tn; + + tr[tn] = tr[t]; + tr[t].sink = i1; + tr[tn].sink = i2; + t_sav = t; + tn_sav = tn; + + /* error */ + + if ((tr[t].d0 <= 0) && (tr[t].d1 <= 0)) /* case cannot arise */ + { + fprintf(stderr, "add_segment: error\n"); + break; + } + + /* only one trapezoid below. partition t into two and make the */ + /* two resulting trapezoids t and tn as the upper neighbours of */ + /* the sole lower trapezoid */ + + else if ((tr[t].d0 > 0) && (tr[t].d1 <= 0)) + { /* Only one trapezoid below */ + if ((tr[t].u0 > 0) && (tr[t].u1 > 0)) + { /* continuation of a chain from abv. */ + if (tr[t].usave > 0) /* three upper neighbours */ + { + if (tr[t].uside == S_LEFT) + { + tr[tn].u0 = tr[t].u1; + tr[t].u1 = -1; + tr[tn].u1 = tr[t].usave; + + tr[tr[t].u0].d0 = t; + tr[tr[tn].u0].d0 = tn; + tr[tr[tn].u1].d0 = tn; + } + else /* intersects in the right */ + { + tr[tn].u1 = -1; + tr[tn].u0 = tr[t].u1; + tr[t].u1 = tr[t].u0; + tr[t].u0 = tr[t].usave; + + tr[tr[t].u0].d0 = t; + tr[tr[t].u1].d0 = t; + tr[tr[tn].u0].d0 = tn; + } + + tr[t].usave = tr[tn].usave = 0; + } + else /* No usave.... simple case */ + { + tr[tn].u0 = tr[t].u1; + tr[t].u1 = tr[tn].u1 = -1; + tr[tr[tn].u0].d0 = tn; + } + } + else + { /* fresh seg. or upward cusp */ + int tmp_u = tr[t].u0; + int td0, td1; + if (((td0 = tr[tmp_u].d0) > 0) && + ((td1 = tr[tmp_u].d1) > 0)) + { /* upward cusp */ + if ((tr[td0].rseg > 0) && + !is_left_of(tr[td0].rseg, &s.v1)) + { + tr[t].u0 = tr[t].u1 = tr[tn].u1 = -1; + tr[tr[tn].u0].d1 = tn; + } + else /* cusp going leftwards */ + { + tr[tn].u0 = tr[tn].u1 = tr[t].u1 = -1; + tr[tr[t].u0].d0 = t; + } + } + else /* fresh segment */ + { + tr[tr[t].u0].d0 = t; + tr[tr[t].u0].d1 = tn; + } + } + + if (FP_EQUAL(tr[t].lo.y, tr[tlast].lo.y) && + FP_EQUAL(tr[t].lo.x, tr[tlast].lo.x) && tribot) + { /* bottom forms a triangle */ + + if (is_swapped) + tmptriseg = seg[segnum].prev; + else + tmptriseg = seg[segnum].next; + + if ((tmptriseg > 0) && is_left_of(tmptriseg, &s.v0)) + { + /* L-R downward cusp */ + tr[tr[t].d0].u0 = t; + tr[tn].d0 = tr[tn].d1 = -1; + } + else + { + /* R-L downward cusp */ + tr[tr[tn].d0].u1 = tn; + tr[t].d0 = tr[t].d1 = -1; + } + } + else + { + if ((tr[tr[t].d0].u0 > 0) && (tr[tr[t].d0].u1 > 0)) + { + if (tr[tr[t].d0].u0 == t) /* passes thru LHS */ + { + tr[tr[t].d0].usave = tr[tr[t].d0].u1; + tr[tr[t].d0].uside = S_LEFT; + } + else + { + tr[tr[t].d0].usave = tr[tr[t].d0].u0; + tr[tr[t].d0].uside = S_RIGHT; + } + } + tr[tr[t].d0].u0 = t; + tr[tr[t].d0].u1 = tn; + } + + t = tr[t].d0; + } + + + else if ((tr[t].d0 <= 0) && (tr[t].d1 > 0)) + { /* Only one trapezoid below */ + if ((tr[t].u0 > 0) && (tr[t].u1 > 0)) + { /* continuation of a chain from abv. */ + if (tr[t].usave > 0) /* three upper neighbours */ + { + if (tr[t].uside == S_LEFT) + { + tr[tn].u0 = tr[t].u1; + tr[t].u1 = -1; + tr[tn].u1 = tr[t].usave; + + tr[tr[t].u0].d0 = t; + tr[tr[tn].u0].d0 = tn; + tr[tr[tn].u1].d0 = tn; + } + else /* intersects in the right */ + { + tr[tn].u1 = -1; + tr[tn].u0 = tr[t].u1; + tr[t].u1 = tr[t].u0; + tr[t].u0 = tr[t].usave; + + tr[tr[t].u0].d0 = t; + tr[tr[t].u1].d0 = t; + tr[tr[tn].u0].d0 = tn; + } + + tr[t].usave = tr[tn].usave = 0; + } + else /* No usave.... simple case */ + { + tr[tn].u0 = tr[t].u1; + tr[t].u1 = tr[tn].u1 = -1; + tr[tr[tn].u0].d0 = tn; + } + } + else + { /* fresh seg. or upward cusp */ + int tmp_u = tr[t].u0; + int td0, td1; + if (((td0 = tr[tmp_u].d0) > 0) && + ((td1 = tr[tmp_u].d1) > 0)) + { /* upward cusp */ + if ((tr[td0].rseg > 0) && + !is_left_of(tr[td0].rseg, &s.v1)) + { + tr[t].u0 = tr[t].u1 = tr[tn].u1 = -1; + tr[tr[tn].u0].d1 = tn; + } + else + { + tr[tn].u0 = tr[tn].u1 = tr[t].u1 = -1; + tr[tr[t].u0].d0 = t; + } + } + else /* fresh segment */ + { + tr[tr[t].u0].d0 = t; + tr[tr[t].u0].d1 = tn; + } + } + + if (FP_EQUAL(tr[t].lo.y, tr[tlast].lo.y) && + FP_EQUAL(tr[t].lo.x, tr[tlast].lo.x) && tribot) + { /* bottom forms a triangle */ + int tmpseg; + + if (is_swapped) + tmptriseg = seg[segnum].prev; + else + tmptriseg = seg[segnum].next; + + if ((tmpseg > 0) && is_left_of(tmpseg, &s.v0)) + { + /* L-R downward cusp */ + tr[tr[t].d1].u0 = t; + tr[tn].d0 = tr[tn].d1 = -1; + } + else + { + /* R-L downward cusp */ + tr[tr[tn].d1].u1 = tn; + tr[t].d0 = tr[t].d1 = -1; + } + } + else + { + if ((tr[tr[t].d1].u0 > 0) && (tr[tr[t].d1].u1 > 0)) + { + if (tr[tr[t].d1].u0 == t) /* passes thru LHS */ + { + tr[tr[t].d1].usave = tr[tr[t].d1].u1; + tr[tr[t].d1].uside = S_LEFT; + } + else + { + tr[tr[t].d1].usave = tr[tr[t].d1].u0; + tr[tr[t].d1].uside = S_RIGHT; + } + } + tr[tr[t].d1].u0 = t; + tr[tr[t].d1].u1 = tn; + } + + t = tr[t].d1; + } + + /* two trapezoids below. Find out which one is intersected by */ + /* this segment and proceed down that one */ + + else + { + int tmpseg = tr[tr[t].d0].rseg; + double y0, yt; + point_t tmppt; + int tnext, i_d0, i_d1; + + i_d0 = i_d1 = FALSE; + if (FP_EQUAL(tr[t].lo.y, s.v0.y)) + { + if (tr[t].lo.x > s.v0.x) + i_d0 = TRUE; + else + i_d1 = TRUE; + } + else + { + tmppt.y = y0 = tr[t].lo.y; + yt = (y0 - s.v0.y)/(s.v1.y - s.v0.y); + tmppt.x = s.v0.x + yt * (s.v1.x - s.v0.x); + + if (_less_than(&tmppt, &tr[t].lo)) + i_d0 = TRUE; + else + i_d1 = TRUE; + } + + /* check continuity from the top so that the lower-neighbour */ + /* values are properly filled for the upper trapezoid */ + + if ((tr[t].u0 > 0) && (tr[t].u1 > 0)) + { /* continuation of a chain from abv. */ + if (tr[t].usave > 0) /* three upper neighbours */ + { + if (tr[t].uside == S_LEFT) + { + tr[tn].u0 = tr[t].u1; + tr[t].u1 = -1; + tr[tn].u1 = tr[t].usave; + + tr[tr[t].u0].d0 = t; + tr[tr[tn].u0].d0 = tn; + tr[tr[tn].u1].d0 = tn; + } + else /* intersects in the right */ + { + tr[tn].u1 = -1; + tr[tn].u0 = tr[t].u1; + tr[t].u1 = tr[t].u0; + tr[t].u0 = tr[t].usave; + + tr[tr[t].u0].d0 = t; + tr[tr[t].u1].d0 = t; + tr[tr[tn].u0].d0 = tn; + } + + tr[t].usave = tr[tn].usave = 0; + } + else /* No usave.... simple case */ + { + tr[tn].u0 = tr[t].u1; + tr[tn].u1 = -1; + tr[t].u1 = -1; + tr[tr[tn].u0].d0 = tn; + } + } + else + { /* fresh seg. or upward cusp */ + int tmp_u = tr[t].u0; + int td0, td1; + if (((td0 = tr[tmp_u].d0) > 0) && + ((td1 = tr[tmp_u].d1) > 0)) + { /* upward cusp */ + if ((tr[td0].rseg > 0) && + !is_left_of(tr[td0].rseg, &s.v1)) + { + tr[t].u0 = tr[t].u1 = tr[tn].u1 = -1; + tr[tr[tn].u0].d1 = tn; + } + else + { + tr[tn].u0 = tr[tn].u1 = tr[t].u1 = -1; + tr[tr[t].u0].d0 = t; + } + } + else /* fresh segment */ + { + tr[tr[t].u0].d0 = t; + tr[tr[t].u0].d1 = tn; + } + } + + if (FP_EQUAL(tr[t].lo.y, tr[tlast].lo.y) && + FP_EQUAL(tr[t].lo.x, tr[tlast].lo.x) && tribot) + { + /* this case arises only at the lowest trapezoid.. i.e. + tlast, if the lower endpoint of the segment is + already inserted in the structure */ + + tr[tr[t].d0].u0 = t; + tr[tr[t].d0].u1 = -1; + tr[tr[t].d1].u0 = tn; + tr[tr[t].d1].u1 = -1; + + tr[tn].d0 = tr[t].d1; + tr[t].d1 = tr[tn].d1 = -1; + + tnext = tr[t].d1; + } + else if (i_d0) + /* intersecting d0 */ + { + tr[tr[t].d0].u0 = t; + tr[tr[t].d0].u1 = tn; + tr[tr[t].d1].u0 = tn; + tr[tr[t].d1].u1 = -1; + + /* new code to determine the bottom neighbours of the */ + /* newly partitioned trapezoid */ + + tr[t].d1 = -1; + + tnext = tr[t].d0; + } + else /* intersecting d1 */ + { + tr[tr[t].d0].u0 = t; + tr[tr[t].d0].u1 = -1; + tr[tr[t].d1].u0 = t; + tr[tr[t].d1].u1 = tn; + + /* new code to determine the bottom neighbours of the */ + /* newly partitioned trapezoid */ + + tr[tn].d0 = tr[t].d1; + tr[tn].d1 = -1; + + tnext = tr[t].d1; + } + + t = tnext; + } + + tr[t_sav].rseg = tr[tn_sav].lseg = segnum; + } /* end-while */ + + /* Now combine those trapezoids which share common segments. We can */ + /* use the pointers to the parent to connect these together. This */ + /* works only because all these new trapezoids have been formed */ + /* due to splitting by the segment, and hence have only one parent */ + + tfirstl = tfirst; + tlastl = tlast; + merge_trapezoids(segnum, tfirstl, tlastl, S_LEFT); + merge_trapezoids(segnum, tfirstr, tlastr, S_RIGHT); + + seg[segnum].is_inserted = TRUE; + return 0; +} + + +/* Update the roots stored for each of the endpoints of the segment. + * This is done to speed up the location-query for the endpoint when + * the segment is inserted into the trapezoidation subsequently + */ +static int find_new_roots(segnum) + int segnum; +{ + segment_t *s = &seg[segnum]; + + if (s->is_inserted) + return 0; + + s->root0 = locate_endpoint(&s->v0, &s->v1, s->root0); + s->root0 = tr[s->root0].sink; + + s->root1 = locate_endpoint(&s->v1, &s->v0, s->root1); + s->root1 = tr[s->root1].sink; + return 0; +} + + +/* Main routine to perform trapezoidation */ +int construct_trapezoids(nseg) + int nseg; +{ + register int i; + int root, h; + + /* Add the first segment and get the query structure and trapezoid */ + /* list initialised */ + + root = init_query_structure(choose_segment()); + + for (i = 1; i <= nseg; i++) + seg[i].root0 = seg[i].root1 = root; + + for (h = 1; h <= math_logstar_n(nseg); h++) + { + for (i = math_N(nseg, h -1) + 1; i <= math_N(nseg, h); i++) + add_segment(choose_segment()); + + /* Find a new root for each of the segment endpoints */ + for (i = 1; i <= nseg; i++) + find_new_roots(i); + } + + for (i = math_N(nseg, math_logstar_n(nseg)) + 1; i <= nseg; i++) + add_segment(choose_segment()); + + return 0; +} + + diff --git a/src/Lib/poly2tri/data_1 b/src/Lib/poly2tri/data_1 new file mode 100644 index 00000000..f405c7dc --- /dev/null +++ b/src/Lib/poly2tri/data_1 @@ -0,0 +1,24 @@ +4 + +4 +0.0 0.0 +6.0 0.0 +6.0 6.0 +0.0 6.0 + +3 +0.5 1.0 +1.0 2.0 +2.0 1.5 + +3 +0.5 4.0 +1.0 5.0 +2.0 4.5 + +3 +3.0 3.0 +5.0 3.5 +5.0 2.5 + + diff --git a/src/Lib/poly2tri/interface.h b/src/Lib/poly2tri/interface.h new file mode 100644 index 00000000..fb84ab88 --- /dev/null +++ b/src/Lib/poly2tri/interface.h @@ -0,0 +1,18 @@ +#ifndef __interface_h +#define __interface_h + +#define TRUE 1 +#define FALSE 0 + +#if defined ( __cplusplus ) +extern "C" { +#endif + +extern int triangulate_polygon(int, int *, double (*)[2], int (*)[3]); +extern int is_point_inside_polygon(double *); + +#if defined ( __cplusplus ) +} +#endif + +#endif /* __interface_h */ diff --git a/src/Lib/poly2tri/misc.c b/src/Lib/poly2tri/misc.c new file mode 100644 index 00000000..4d3c6fef --- /dev/null +++ b/src/Lib/poly2tri/misc.c @@ -0,0 +1,157 @@ +#include +#include +#include + +#ifdef __STDC__ +extern double log2(double); +#else +extern double log2(); +#endif + +static int choose_idx; +static int permute[SEGSIZE]; + + +/* Generate a random permutation of the segments 1..n */ +int generate_random_ordering(n) + int n; +{ + struct timeval tval; + struct timezone tzone; + register int i; + int m, st[SEGSIZE], *p; + + choose_idx = 1; + gettimeofday(&tval, &tzone); + srand48(tval.tv_sec); + + for (i = 0; i <= n; i++) + st[i] = i; + + p = st; + for (i = 1; i <= n; i++, p++) + { + m = lrand48() % (n + 1 - i) + 1; + permute[i] = p[m]; + if (m != 1) + p[m] = p[1]; + } + return 0; +} + + +/* Return the next segment in the generated random ordering of all the */ +/* segments in S */ +int choose_segment() +{ + int i; + +#ifdef DEBUG + fprintf(stderr, "choose_segment: %d\n", permute[choose_idx]); +#endif + return permute[choose_idx++]; +} + + +#ifdef STANDALONE + +/* Read in the list of vertices from infile */ +int read_segments(filename, genus) + char *filename; + int *genus; +{ + FILE *infile; + int ccount; + register int i; + int ncontours, npoints, first, last; + + if ((infile = fopen(filename, "r")) == NULL) + { + perror(filename); + return -1; + } + + fscanf(infile, "%d", &ncontours); + if (ncontours <= 0) + return -1; + + /* For every contour, read in all the points for the contour. The */ + /* outer-most contour is read in first (points specified in */ + /* anti-clockwise order). Next, the inner contours are input in */ + /* clockwise order */ + + ccount = 0; + i = 1; + + while (ccount < ncontours) + { + int j; + + fscanf(infile, "%d", &npoints); + first = i; + last = first + npoints - 1; + for (j = 0; j < npoints; j++, i++) + { + fscanf(infile, "%lf%lf", &seg[i].v0.x, &seg[i].v0.y); + if (i == last) + { + seg[i].next = first; + seg[i].prev = i-1; + seg[i-1].v1 = seg[i].v0; + } + else if (i == first) + { + seg[i].next = i+1; + seg[i].prev = last; + seg[last].v1 = seg[i].v0; + } + else + { + seg[i].prev = i-1; + seg[i].next = i+1; + seg[i-1].v1 = seg[i].v0; + } + + seg[i].is_inserted = FALSE; + } + + ccount++; + } + + *genus = ncontours - 1; + return i-1; +} + +#endif + + +/* Get log*n for given n */ +int math_logstar_n(n) + int n; +{ + register int i; + double v; + + for (i = 0, v = (double) n; v >= 1; i++) { + /* v = log2(v); */ + v = log(v) / log(2.0); + } + + return (i - 1); +} + + +int math_N(n, h) + int n; + int h; +{ + register int i; + double v; + + for (i = 0, v = (int) n; i < h; i++) { + /* v = log2(v); */ + v = log(v) / log(2.0); + } + + return (int) ceil((double) 1.0*n/v); +} diff --git a/src/Lib/poly2tri/monotone.c b/src/Lib/poly2tri/monotone.c new file mode 100644 index 00000000..689987c2 --- /dev/null +++ b/src/Lib/poly2tri/monotone.c @@ -0,0 +1,737 @@ +#include +#include + +#define CROSS_SINE(v0, v1) ((v0).x * (v1).y - (v1).x * (v0).y) +#define LENGTH(v0) (sqrt((v0).x * (v0).x + (v0).y * (v0).y)) + +static monchain_t mchain[TRSIZE]; /* Table to hold all the monotone */ + /* polygons . Each monotone polygon */ + /* is a circularly linked list */ + +static vertexchain_t vert[SEGSIZE]; /* chain init. information. This */ + /* is used to decide which */ + /* monotone polygon to split if */ + /* there are several other */ + /* polygons touching at the same */ + /* vertex */ + +static int mon[SEGSIZE]; /* contains position of any vertex in */ + /* the monotone chain for the polygon */ +static int visited[TRSIZE]; +static int chain_idx, op_idx, mon_idx; + + +static int triangulate_single_polygon(int, int, int, int (*)[3]); +static int traverse_polygon(int, int, int, int); + +/* Function returns TRUE if the trapezoid lies inside the polygon */ +static int inside_polygon(t) + trap_t *t; +{ + int rseg = t->rseg; + + if (t->state == ST_INVALID) + return 0; + + if ((t->lseg <= 0) || (t->rseg <= 0)) + return 0; + + if (((t->u0 <= 0) && (t->u1 <= 0)) || + ((t->d0 <= 0) && (t->d1 <= 0))) /* triangle */ + return (_greater_than(&seg[rseg].v1, &seg[rseg].v0)); + + return 0; +} + + +/* return a new mon structure from the table */ +static int newmon() +{ + return ++mon_idx; +} + + +/* return a new chain element from the table */ +static int new_chain_element() +{ + return ++chain_idx; +} + + +static double get_angle(vp0, vpnext, vp1) + point_t *vp0; + point_t *vpnext; + point_t *vp1; +{ + point_t v0, v1; + + v0.x = vpnext->x - vp0->x; + v0.y = vpnext->y - vp0->y; + + v1.x = vp1->x - vp0->x; + v1.y = vp1->y - vp0->y; + + if (CROSS_SINE(v0, v1) >= 0) /* sine is positive */ + return DOT(v0, v1)/LENGTH(v0)/LENGTH(v1); + else + return (-1.0 * DOT(v0, v1)/LENGTH(v0)/LENGTH(v1) - 2); +} + + +/* (v0, v1) is the new diagonal to be added to the polygon. Find which */ +/* chain to use and return the positions of v0 and v1 in p and q */ +static int get_vertex_positions(v0, v1, ip, iq) + int v0; + int v1; + int *ip; + int *iq; +{ + vertexchain_t *vp0, *vp1; + register int i; + double angle, temp; + int tp, tq; + + vp0 = &vert[v0]; + vp1 = &vert[v1]; + + /* p is identified as follows. Scan from (v0, v1) rightwards till */ + /* you hit the first segment starting from v0. That chain is the */ + /* chain of our interest */ + + angle = -4.0; + for (i = 0; i < 4; i++) + { + if (vp0->vnext[i] <= 0) + continue; + if ((temp = get_angle(&vp0->pt, &(vert[vp0->vnext[i]].pt), + &vp1->pt)) > angle) + { + angle = temp; + tp = i; + } + } + + *ip = tp; + + /* Do similar actions for q */ + + angle = -4.0; + for (i = 0; i < 4; i++) + { + if (vp1->vnext[i] <= 0) + continue; + if ((temp = get_angle(&vp1->pt, &(vert[vp1->vnext[i]].pt), + &vp0->pt)) > angle) + { + angle = temp; + tq = i; + } + } + + *iq = tq; + + return 0; +} + + +/* v0 and v1 are specified in anti-clockwise order with respect to + * the current monotone polygon mcur. Split the current polygon into + * two polygons using the diagonal (v0, v1) + */ +static int make_new_monotone_poly(mcur, v0, v1) + int mcur; + int v0; + int v1; +{ + int p, q, ip, iq; + int mnew = newmon(); + int i, j, nf0, nf1; + vertexchain_t *vp0, *vp1; + + vp0 = &vert[v0]; + vp1 = &vert[v1]; + + get_vertex_positions(v0, v1, &ip, &iq); + + p = vp0->vpos[ip]; + q = vp1->vpos[iq]; + + /* At this stage, we have got the positions of v0 and v1 in the */ + /* desired chain. Now modify the linked lists */ + + i = new_chain_element(); /* for the new list */ + j = new_chain_element(); + + mchain[i].vnum = v0; + mchain[j].vnum = v1; + + mchain[i].next = mchain[p].next; + mchain[mchain[p].next].prev = i; + mchain[i].prev = j; + mchain[j].next = i; + mchain[j].prev = mchain[q].prev; + mchain[mchain[q].prev].next = j; + + mchain[p].next = q; + mchain[q].prev = p; + + nf0 = vp0->nextfree; + nf1 = vp1->nextfree; + + vp0->vnext[ip] = v1; + + vp0->vpos[nf0] = i; + vp0->vnext[nf0] = mchain[mchain[i].next].vnum; + vp1->vpos[nf1] = j; + vp1->vnext[nf1] = v0; + + vp0->nextfree++; + vp1->nextfree++; + +#ifdef DEBUG + fprintf(stderr, "make_poly: mcur = %d, (v0, v1) = (%d, %d)\n", + mcur, v0, v1); + fprintf(stderr, "next posns = (p, q) = (%d, %d)\n", p, q); +#endif + + mon[mcur] = p; + mon[mnew] = i; + return mnew; +} + +/* Main routine to get monotone polygons from the trapezoidation of + * the polygon. + */ + +int monotonate_trapezoids(n) + int n; +{ + register int i; + int tr_start; + + memset((void *)vert, 0, sizeof(vert)); + memset((void *)visited, 0, sizeof(visited)); + memset((void *)mchain, 0, sizeof(mchain)); + memset((void *)mon, 0, sizeof(mon)); + + /* First locate a trapezoid which lies inside the polygon */ + /* and which is triangular */ + for (i = 0; i < TRSIZE; i++) + if (inside_polygon(&tr[i])) + break; + tr_start = i; + + /* Initialise the mon data-structure and start spanning all the */ + /* trapezoids within the polygon */ + +#if 0 + for (i = 1; i <= n; i++) + { + mchain[i].prev = i - 1; + mchain[i].next = i + 1; + mchain[i].vnum = i; + vert[i].pt = seg[i].v0; + vert[i].vnext[0] = i + 1; /* next vertex */ + vert[i].vpos[0] = i; /* locn. of next vertex */ + vert[i].nextfree = 1; + } + mchain[1].prev = n; + mchain[n].next = 1; + vert[n].vnext[0] = 1; + vert[n].vpos[0] = n; + chain_idx = n; + mon_idx = 0; + mon[0] = 1; /* position of any vertex in the first */ + /* chain */ + +#else + + for (i = 1; i <= n; i++) + { + mchain[i].prev = seg[i].prev; + mchain[i].next = seg[i].next; + mchain[i].vnum = i; + vert[i].pt = seg[i].v0; + vert[i].vnext[0] = seg[i].next; /* next vertex */ + vert[i].vpos[0] = i; /* locn. of next vertex */ + vert[i].nextfree = 1; + } + + chain_idx = n; + mon_idx = 0; + mon[0] = 1; /* position of any vertex in the first */ + /* chain */ + +#endif + + /* traverse the polygon */ + if (tr[tr_start].u0 > 0) + traverse_polygon(0, tr_start, tr[tr_start].u0, TR_FROM_UP); + else if (tr[tr_start].d0 > 0) + traverse_polygon(0, tr_start, tr[tr_start].d0, TR_FROM_DN); + + /* return the number of polygons created */ + return newmon(); +} + + +/* recursively visit all the trapezoids */ +static int traverse_polygon(mcur, trnum, from, dir) + int mcur; + int trnum; + int from; + int dir; +{ + trap_t *t = &tr[trnum]; + int howsplit, mnew; + int v0, v1, v0next, v1next; + int retval, tmp; + int do_switch = FALSE; + + if ((trnum <= 0) || visited[trnum]) + return 0; + + visited[trnum] = TRUE; + + /* We have much more information available here. */ + /* rseg: goes upwards */ + /* lseg: goes downwards */ + + /* Initially assume that dir = TR_FROM_DN (from the left) */ + /* Switch v0 and v1 if necessary afterwards */ + + + /* special cases for triangles with cusps at the opposite ends. */ + /* take care of this first */ + if ((t->u0 <= 0) && (t->u1 <= 0)) + { + if ((t->d0 > 0) && (t->d1 > 0)) /* downward opening triangle */ + { + v0 = tr[t->d1].lseg; + v1 = t->lseg; + if (from == t->d1) + { + do_switch = TRUE; + mnew = make_new_monotone_poly(mcur, v1, v0); + traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->d0, trnum, TR_FROM_UP); + } + else + { + mnew = make_new_monotone_poly(mcur, v0, v1); + traverse_polygon(mcur, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->d1, trnum, TR_FROM_UP); + } + } + else + { + retval = SP_NOSPLIT; /* Just traverse all neighbours */ + traverse_polygon(mcur, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP); + } + } + + else if ((t->d0 <= 0) && (t->d1 <= 0)) + { + if ((t->u0 > 0) && (t->u1 > 0)) /* upward opening triangle */ + { + v0 = t->rseg; + v1 = tr[t->u0].rseg; + if (from == t->u1) + { + do_switch = TRUE; + mnew = make_new_monotone_poly(mcur, v1, v0); + traverse_polygon(mcur, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->u0, trnum, TR_FROM_DN); + } + else + { + mnew = make_new_monotone_poly(mcur, v0, v1); + traverse_polygon(mcur, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->u1, trnum, TR_FROM_DN); + } + } + else + { + retval = SP_NOSPLIT; /* Just traverse all neighbours */ + traverse_polygon(mcur, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP); + } + } + + else if ((t->u0 > 0) && (t->u1 > 0)) + { + if ((t->d0 > 0) && (t->d1 > 0)) /* downward + upward cusps */ + { + v0 = tr[t->d1].lseg; + v1 = tr[t->u0].rseg; + retval = SP_2UP_2DN; + if (((dir == TR_FROM_DN) && (t->d1 == from)) || + ((dir == TR_FROM_UP) && (t->u1 == from))) + { + do_switch = TRUE; + mnew = make_new_monotone_poly(mcur, v1, v0); + traverse_polygon(mcur, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->d0, trnum, TR_FROM_UP); + } + else + { + mnew = make_new_monotone_poly(mcur, v0, v1); + traverse_polygon(mcur, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->d1, trnum, TR_FROM_UP); + } + } + else /* only downward cusp */ + { + if (_equal_to(&t->lo, &seg[t->lseg].v1)) + { + v0 = tr[t->u0].rseg; + v1 = seg[t->lseg].next; + + retval = SP_2UP_LEFT; + if ((dir == TR_FROM_UP) && (t->u0 == from)) + { + do_switch = TRUE; + mnew = make_new_monotone_poly(mcur, v1, v0); + traverse_polygon(mcur, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->d1, trnum, TR_FROM_UP); + } + else + { + mnew = make_new_monotone_poly(mcur, v0, v1); + traverse_polygon(mcur, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->u0, trnum, TR_FROM_DN); + } + } + else + { + v0 = t->rseg; + v1 = tr[t->u0].rseg; + retval = SP_2UP_RIGHT; + if ((dir == TR_FROM_UP) && (t->u1 == from)) + { + do_switch = TRUE; + mnew = make_new_monotone_poly(mcur, v1, v0); + traverse_polygon(mcur, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->d1, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->u0, trnum, TR_FROM_DN); + } + else + { + mnew = make_new_monotone_poly(mcur, v0, v1); + traverse_polygon(mcur, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->u1, trnum, TR_FROM_DN); + } + } + } + } + else if ((t->u0 > 0) || (t->u1 > 0)) /* no downward cusp */ + { + if ((t->d0 > 0) && (t->d1 > 0)) /* only upward cusp */ + { + if (_equal_to(&t->hi, &seg[t->lseg].v0)) + { + v0 = tr[t->d1].lseg; + v1 = t->lseg; + retval = SP_2DN_LEFT; + if (!((dir == TR_FROM_DN) && (t->d0 == from))) + { + do_switch = TRUE; + mnew = make_new_monotone_poly(mcur, v1, v0); + traverse_polygon(mcur, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP); + traverse_polygon(mcur, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->d0, trnum, TR_FROM_UP); + } + else + { + mnew = make_new_monotone_poly(mcur, v0, v1); + traverse_polygon(mcur, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->d1, trnum, TR_FROM_UP); + } + } + else + { + v0 = tr[t->d1].lseg; + v1 = seg[t->rseg].next; + + retval = SP_2DN_RIGHT; + if ((dir == TR_FROM_DN) && (t->d1 == from)) + { + do_switch = TRUE; + mnew = make_new_monotone_poly(mcur, v1, v0); + traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->d0, trnum, TR_FROM_UP); + } + else + { + mnew = make_new_monotone_poly(mcur, v0, v1); + traverse_polygon(mcur, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mcur, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->d1, trnum, TR_FROM_UP); + } + } + } + else /* no cusp */ + { + if (_equal_to(&t->hi, &seg[t->lseg].v0) && + _equal_to(&t->lo, &seg[t->rseg].v0)) + { + v0 = t->rseg; + v1 = t->lseg; + retval = SP_SIMPLE_LRDN; + if (dir == TR_FROM_UP) + { + do_switch = TRUE; + mnew = make_new_monotone_poly(mcur, v1, v0); + traverse_polygon(mcur, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->d1, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->d0, trnum, TR_FROM_UP); + } + else + { + mnew = make_new_monotone_poly(mcur, v0, v1); + traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP); + traverse_polygon(mcur, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->u1, trnum, TR_FROM_DN); + } + } + else if (_equal_to(&t->hi, &seg[t->rseg].v1) && + _equal_to(&t->lo, &seg[t->lseg].v1)) + { + v0 = seg[t->rseg].next; + v1 = seg[t->lseg].next; + + retval = SP_SIMPLE_LRUP; + if (dir == TR_FROM_UP) + { + do_switch = TRUE; + mnew = make_new_monotone_poly(mcur, v1, v0); + traverse_polygon(mcur, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->d1, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->d0, trnum, TR_FROM_UP); + } + else + { + mnew = make_new_monotone_poly(mcur, v0, v1); + traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP); + traverse_polygon(mcur, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mnew, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mnew, t->u1, trnum, TR_FROM_DN); + } + } + else /* no split possible */ + { + retval = SP_NOSPLIT; + traverse_polygon(mcur, t->u0, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->d0, trnum, TR_FROM_UP); + traverse_polygon(mcur, t->u1, trnum, TR_FROM_DN); + traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP); + } + } + } + + return retval; +} + + +/* For each monotone polygon, find the ymax and ymin (to determine the */ +/* two y-monotone chains) and pass on this monotone polygon for greedy */ +/* triangulation. */ +/* Take care not to triangulate duplicate monotone polygons */ + +int triangulate_monotone_polygons(nvert, nmonpoly, op) + int nvert; + int nmonpoly; + int op[][3]; +{ + register int i; + point_t ymax, ymin; + int p, vfirst, posmax, posmin, v; + int vcount, processed; + +#ifdef DEBUG + for (i = 0; i < nmonpoly; i++) + { + fprintf(stderr, "\n\nPolygon %d: ", i); + vfirst = mchain[mon[i]].vnum; + p = mchain[mon[i]].next; + fprintf (stderr, "%d ", mchain[mon[i]].vnum); + while (mchain[p].vnum != vfirst) + { + fprintf(stderr, "%d ", mchain[p].vnum); + p = mchain[p].next; + } + } + fprintf(stderr, "\n"); +#endif + + op_idx = 0; + for (i = 0; i < nmonpoly; i++) + { + vcount = 1; + processed = FALSE; + vfirst = mchain[mon[i]].vnum; + ymax = ymin = vert[vfirst].pt; + posmax = posmin = mon[i]; + mchain[mon[i]].marked = TRUE; + p = mchain[mon[i]].next; + while ((v = mchain[p].vnum) != vfirst) + { + if (mchain[p].marked) + { + processed = TRUE; + break; /* break from while */ + } + else + mchain[p].marked = TRUE; + + if (_greater_than(&vert[v].pt, &ymax)) + { + ymax = vert[v].pt; + posmax = p; + } + if (_less_than(&vert[v].pt, &ymin)) + { + ymin = vert[v].pt; + posmin = p; + } + p = mchain[p].next; + vcount++; + } + + if (processed) /* Go to next polygon */ + continue; + + if (vcount == 3) /* already a triangle */ + { + op[op_idx][0] = mchain[p].vnum; + op[op_idx][1] = mchain[mchain[p].next].vnum; + op[op_idx][2] = mchain[mchain[p].prev].vnum; + op_idx++; + } + else /* triangulate the polygon */ + { + v = mchain[mchain[posmax].next].vnum; + if (_equal_to(&vert[v].pt, &ymin)) + { /* LHS is a single line */ + triangulate_single_polygon(nvert, posmax, TRI_LHS, op); + } + else + triangulate_single_polygon(nvert, posmax, TRI_RHS, op); + } + } + +#ifdef DEBUG + for (i = 0; i < op_idx; i++) + fprintf(stderr, "tri #%d: (%d, %d, %d)\n", i, op[i][0], op[i][1], + op[i][2]); +#endif + return op_idx; +} + + +/* A greedy corner-cutting algorithm to triangulate a y-monotone + * polygon in O(n) time. + * Joseph O-Rourke, Computational Geometry in C. + */ +static int triangulate_single_polygon(nvert, posmax, side, op) + int nvert; + int posmax; + int side; + int op[][3]; +{ + register int v; + int rc[SEGSIZE], ri = 0; /* reflex chain */ + int endv, tmp, vpos; + + if (side == TRI_RHS) /* RHS segment is a single segment */ + { + rc[0] = mchain[posmax].vnum; + tmp = mchain[posmax].next; + rc[1] = mchain[tmp].vnum; + ri = 1; + + vpos = mchain[tmp].next; + v = mchain[vpos].vnum; + + if ((endv = mchain[mchain[posmax].prev].vnum) == 0) + endv = nvert; + } + else /* LHS is a single segment */ + { + tmp = mchain[posmax].next; + rc[0] = mchain[tmp].vnum; + tmp = mchain[tmp].next; + rc[1] = mchain[tmp].vnum; + ri = 1; + + vpos = mchain[tmp].next; + v = mchain[vpos].vnum; + + endv = mchain[posmax].vnum; + } + + while ((v != endv) || (ri > 1)) + { + if (ri > 0) /* reflex chain is non-empty */ + { + if (CROSS(vert[v].pt, vert[rc[ri - 1]].pt, + vert[rc[ri]].pt) > 0) + { /* convex corner: cut if off */ + op[op_idx][0] = rc[ri - 1]; + op[op_idx][1] = rc[ri]; + op[op_idx][2] = v; + op_idx++; + ri--; + } + else /* non-convex */ + { /* add v to the chain */ + ri++; + rc[ri] = v; + vpos = mchain[vpos].next; + v = mchain[vpos].vnum; + } + } + else /* reflex-chain empty: add v to the */ + { /* reflex chain and advance it */ + rc[++ri] = v; + vpos = mchain[vpos].next; + v = mchain[vpos].vnum; + } + } /* end-while */ + + /* reached the bottom vertex. Add in the triangle formed */ + op[op_idx][0] = rc[ri - 1]; + op[op_idx][1] = rc[ri]; + op[op_idx][2] = v; + op_idx++; + ri--; + + return 0; +} + + diff --git a/src/Lib/poly2tri/tri.c b/src/Lib/poly2tri/tri.c new file mode 100644 index 00000000..1b3a64b8 --- /dev/null +++ b/src/Lib/poly2tri/tri.c @@ -0,0 +1,167 @@ +#include +#include + + +static int initialise(n) + int n; +{ + register int i; + + for (i = 1; i <= n; i++) + seg[i].is_inserted = FALSE; + + generate_random_ordering(n); + + return 0; +} + +#ifdef STANDALONE + +int main(argc, argv) + int argc; + char *argv[]; +{ + int n, nmonpoly, genus; + int op[SEGSIZE][3], i, ntriangles; + + if ((argc < 2) || ((n = read_segments(argv[1], &genus)) < 0)) + { + fprintf(stderr, "usage: triangulate \n"); + exit(1); + } + + initialise(n); + construct_trapezoids(n); + nmonpoly = monotonate_trapezoids(n); + ntriangles = triangulate_monotone_polygons(n, nmonpoly, op); + + for (i = 0; i < ntriangles; i++) + printf("triangle #%d: %d %d %d\n", i, + op[i][0], op[i][1], op[i][2]); + + return 0; +} + + +#else /* Not standalone. Use this as an interface routine */ + + +/* Input specified as contours. + * Outer contour must be anti-clockwise. + * All inner contours must be clockwise. + * + * Every contour is specified by giving all its points in order. No + * point shoud be repeated. i.e. if the outer contour is a square, + * only the four distinct endpoints shopudl be specified in order. + * + * ncontours: #contours + * cntr: An array describing the number of points in each + * contour. Thus, cntr[i] = #points in the i'th contour. + * vertices: Input array of vertices. Vertices for each contour + * immediately follow those for previous one. Array location + * vertices[0] must NOT be used (i.e. i/p starts from + * vertices[1] instead. The output triangles are + * specified w.r.t. the indices of these vertices. + * triangles: Output array to hold triangles. + * + * Enough space must be allocated for all the arrays before calling + * this routine + */ + + +int triangulate_polygon(ncontours, cntr, vertices, triangles) + int ncontours; + int cntr[]; + double (*vertices)[2]; + int (*triangles)[3]; +{ + register int i; + int nmonpoly, ccount, npoints, genus; + int n, ntriangles; + + memset((void *)seg, 0, sizeof(seg)); + ccount = 0; + i = 1; + + while (ccount < ncontours) + { + int j; + int first, last; + + npoints = cntr[ccount]; + first = i; + last = first + npoints - 1; + for (j = 0; j < npoints; j++, i++) + { + seg[i].v0.x = vertices[i][0]; + seg[i].v0.y = vertices[i][1]; + + if (i == last) + { + seg[i].next = first; + seg[i].prev = i-1; + seg[i-1].v1 = seg[i].v0; + } + else if (i == first) + { + seg[i].next = i+1; + seg[i].prev = last; + seg[last].v1 = seg[i].v0; + } + else + { + seg[i].prev = i-1; + seg[i].next = i+1; + seg[i-1].v1 = seg[i].v0; + } + + seg[i].is_inserted = FALSE; + } + + ccount++; + } + + genus = ncontours - 1; + n = i-1; + + initialise(n); + construct_trapezoids(n); + nmonpoly = monotonate_trapezoids(n); + ntriangles = triangulate_monotone_polygons(n, nmonpoly, triangles); + + return ntriangles; +} + + +/* This function returns TRUE or FALSE depending upon whether the + * vertex is inside the polygon or not. The polygon must already have + * been triangulated before this routine is called. + * This routine will always detect all the points belonging to the + * set (polygon-area - polygon-boundary). The return value for points + * on the boundary is not consistent!!! + */ + +int is_point_inside_polygon(vertex) + double vertex[2]; +{ + point_t v; + int trnum, rseg; + trap_t *t; + + v.x = vertex[0]; + v.y = vertex[1]; + + trnum = locate_endpoint(&v, &v, 1); + t = &tr[trnum]; + + if (t->state == ST_INVALID) + return FALSE; + + if ((t->lseg <= 0) || (t->rseg <= 0)) + return FALSE; + rseg = t->rseg; + return _greater_than_equal_to(&seg[rseg].v1, &seg[rseg].v0); +} + + +#endif /* STANDALONE */ diff --git a/src/Lib/poly2tri/triangulate.h b/src/Lib/poly2tri/triangulate.h new file mode 100644 index 00000000..ee323c60 --- /dev/null +++ b/src/Lib/poly2tri/triangulate.h @@ -0,0 +1,159 @@ +#ifndef _triangulate_h +#define _triangulate_h + +#include +#include +#include + +typedef struct { + double x, y; +} point_t, vector_t; + + +/* Segment attributes */ + +typedef struct { + point_t v0, v1; /* two endpoints */ + int is_inserted; /* inserted in trapezoidation yet ? */ + int root0, root1; /* root nodes in Q */ + int next; /* Next logical segment */ + int prev; /* Previous segment */ +} segment_t; + + +/* Trapezoid attributes */ + +typedef struct { + int lseg, rseg; /* two adjoining segments */ + point_t hi, lo; /* max/min y-values */ + int u0, u1; + int d0, d1; + int sink; /* pointer to corresponding in Q */ + int usave, uside; /* I forgot what this means */ + int state; +} trap_t; + + +/* Node attributes for every node in the query structure */ + +typedef struct { + int nodetype; /* Y-node or S-node */ + int segnum; + point_t yval; + int trnum; + int parent; /* doubly linked DAG */ + int left, right; /* children */ +} node_t; + + +typedef struct { + int vnum; + int next; /* Circularly linked list */ + int prev; /* describing the monotone */ + int marked; /* polygon */ +} monchain_t; + + +typedef struct { + point_t pt; + int vnext[4]; /* next vertices for the 4 chains */ + int vpos[4]; /* position of v in the 4 chains */ + int nextfree; +} vertexchain_t; + + +/* Node types */ + +#define T_X 1 +#define T_Y 2 +#define T_SINK 3 + + +#define SEGSIZE 200 /* max# of segments. Determines how */ + /* many points can be specified as */ + /* input. If your datasets have large */ + /* number of points, increase this */ + /* value accordingly. */ + +#define QSIZE 8*SEGSIZE /* maximum table sizes */ +#define TRSIZE 4*SEGSIZE /* max# trapezoids */ + + +#define TRUE 1 +#define FALSE 0 + + +#define FIRSTPT 1 /* checking whether pt. is inserted */ +#define LASTPT 2 + + +#define INFINITY 1<<30 +#define C_EPS 1.0e-7 /* tolerance value: Used for making */ + /* all decisions about collinearity or */ + /* left/right of segment. Decrease */ + /* this value if the input points are */ + /* spaced very close together */ + + +#define S_LEFT 1 /* for merge-direction */ +#define S_RIGHT 2 + + +#define ST_VALID 1 /* for trapezium state */ +#define ST_INVALID 2 + + +#define SP_SIMPLE_LRUP 1 /* for splitting trapezoids */ +#define SP_SIMPLE_LRDN 2 +#define SP_2UP_2DN 3 +#define SP_2UP_LEFT 4 +#define SP_2UP_RIGHT 5 +#define SP_2DN_LEFT 6 +#define SP_2DN_RIGHT 7 +#define SP_NOSPLIT -1 + +#define TR_FROM_UP 1 /* for traverse-direction */ +#define TR_FROM_DN 2 + +#define TRI_LHS 1 +#define TRI_RHS 2 + + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#define CROSS(v0, v1, v2) (((v1).x - (v0).x)*((v2).y - (v0).y) - \ + ((v1).y - (v0).y)*((v2).x - (v0).x)) + +#define DOT(v0, v1) ((v0).x * (v1).x + (v0).y * (v1).y) + +#define FP_EQUAL(s, t) (fabs(s - t) <= C_EPS) + + + +/* Global variables */ + +extern node_t qs[QSIZE]; /* Query structure */ +extern trap_t tr[TRSIZE]; /* Trapezoid structure */ +extern segment_t seg[SEGSIZE]; /* Segment table */ + + +/* Functions */ + +extern int monotonate_trapezoids(int); +extern int triangulate_monotone_polygons(int, int, int (*)[3]); + +extern int _greater_than(point_t *, point_t *); +extern int _equal_to(point_t *, point_t *); +extern int _greater_than_equal_to(point_t *, point_t *); +extern int _less_than(point_t *, point_t *); +extern int locate_endpoint(point_t *, point_t *, int); +extern int construct_trapezoids(int); + +extern int generate_random_ordering(int); +extern int choose_segment(void); +extern int read_segments(char *, int *); +extern int math_logstar_n(int); +extern int math_N(int, int); + +#endif /* triangulate_h */ diff --git a/src/Lib/shapelib-1.2.5.tar.gz b/src/Lib/shapelib-1.2.5.tar.gz new file mode 100644 index 00000000..bef122f3 Binary files /dev/null and b/src/Lib/shapelib-1.2.5.tar.gz differ diff --git a/src/Lib/shapelib/ChangeLog b/src/Lib/shapelib/ChangeLog new file mode 100644 index 00000000..5b633a2d --- /dev/null +++ b/src/Lib/shapelib/ChangeLog @@ -0,0 +1,6 @@ +Tue May 4 11:04:31 1999 Frank Warmerdam + + * Prepare 1.2.5 release. + + * Added support for 'F' fields. + diff --git a/src/Lib/shapelib/Makefile.am b/src/Lib/shapelib/Makefile.am new file mode 100644 index 00000000..07805df7 --- /dev/null +++ b/src/Lib/shapelib/Makefile.am @@ -0,0 +1,37 @@ +EXTRA_DIST = ChangeLog dbf_api.html makeshape.sh shape.errata \ + shapelib.html shp_api.html stream1.sh stream2.sh + +noinst_LIBRARIES = libshape.a + +libshape_a_SOURCES = \ + dbfopen.c shpopen.c shapefil.h + +noinst_PROGRAMS = shpcreate shpadd shpdump \ + dbfcreate dbfadd dbfdump \ + shptest shputils + +shpcreate_SOURCES = shpcreate.c +shpcreate_LDADD = libshape.a + +shpadd_SOURCES = shpadd.c +shpadd_LDADD = libshape.a + +shpdump_SOURCES = shpdump.c +shpdump_LDADD = libshape.a + +dbfcreate_SOURCES = dbfcreate.c +dbfcreate_LDADD = libshape.a + +dbfadd_SOURCES = dbfadd.c +dbfadd_LDADD = libshape.a + +dbfdump_SOURCES = dbfdump.c +dbfdump_LDADD = libshape.a + +shptest_SOURCES = shptest.c +shptest_LDADD = libshape.a + +shputils_SOURCES = shputils.c +shputils_LDADD = libshape.a + +INCLUDES += -I$(top_builddir) -I$(top_builddir)/Lib diff --git a/src/Lib/shapelib/dbf_api.html b/src/Lib/shapelib/dbf_api.html new file mode 100644 index 00000000..15f85ec8 --- /dev/null +++ b/src/Lib/shapelib/dbf_api.html @@ -0,0 +1,322 @@ + +
+Attribute (.DBF) API + + +

Attribute (.DBF) API

+ +The Attribute (DBF) API uses DBFHandle to represent a handle for access +to one .dbf file. The contents of the DBFHandle are visible (see shapefil.h) +but should be ignored by the application. It is intended that all information +be accessed by API functions. Note that there should be exactly one record +in the .dbf file for each shape in the .shp/.shx files. This constraint +must be maintained by the application.

+ + + +

DBFOpen()

+ +
+DBFHandle DBFOpen( const char * pszDBFFile, const char * pszAccess );
+
+  pszDBFFile:		The name of the xBase (.dbf) file to access.
+
+  pszAccess:		The fopen() style access string.  At this time only
+			"rb" (read-only binary) and "rb+" (read/write binary) 
+		        should be used.
+
+ + The DBFOpen() function should be used to establish access to an existing + xBase format table file. The returned DBFHandle is passed to other + access functions, and DBFClose() should be invoked to recover resources, and + flush changes to disk when complete. The DBFCreate() function should + called to create new xBase files. As a convenience, DBFOpen() can be + called with the name of a .shp or .shx file, and it will figure out the + name of the related .dbf file.

+ + + +

DBFCreate()

+ +
+DBFHandle DBFCreate( const char * pszDBFFile );
+
+  pszDBFFile:		The name of the xBase (.dbf) file to create.
+
+ + The DBFCreate() function creates a new xBase format file with the given + name, and returns an access handle that can be used with other DBF functions. + The newly created file will have no fields, and no records. Fields should + be added with DBFAddField() before any records add written. + + + +

DBFGetFieldCount()

+ +
+int DBFGetFieldCount( DBFHandle hDBF );
+
+  hDBF:		The access handle for the file to be queried, as returned
+                by DBFOpen(), or DBFCreate().
+
+ + The DBFGetFieldCount() function returns the number of fields currently + defined for the indicated xBase file. + + + +

DBFGetRecordCount()

+ +
+int DBFGetRecordCount( DBFHandle hDBF );
+
+  hDBF:		The access handle for the file to be queried, as returned by
+		DBFOpen(), or DBFCreate().
+
+ + The DBFGetRecordCount() function returns the number of records that + exist on the xBase file currently. Note that with shape files one xBase + record exists for each shape in the .shp/.shx files.

+ + + +

DBFGetFieldInfo()

+ +
+DBFFieldType DBFGetFieldInfo( DBFHandle hDBF, int iField, char * pszFieldName,
+                              int * pnWidth, int * pnDecimals );
+
+  hDBF:		The access handle for the file to be queried, as returned by
+		DBFOpen(), or DBFCreate().
+
+  iField:	The field to be queried.  This should be a number between 
+                0 and n-1, where n is the number fields on the file, as
+                returned by DBFGetFieldCount().
+
+  pszFieldName:	If this pointer is not NULL the name of the requested field
+		will be written to this location.  The pszFieldName buffer 
+                should be at least 12 character is size in order to hold
+		the longest possible field name of 11 characters plus a 
+                terminating zero character.
+
+  pnWidth:	If this pointer is not NULL, the width of the requested field
+		will be returned in the int pointed to by pnWidth.  This is
+                the width in characters.  
+
+  pnDecimals:	If this pointer is not NULL, the number of decimal places
+                precision defined for the field will be returned.  This is
+                zero for integer fields, or non-numeric fields.
+
+ + The DBFGetFieldInfo() returns the type of the requested field, which is + one of the DBFFieldType enumerated values. As well, the field name, and + field width information can optionally be returned. The field type returned + does not correspond one to one with the xBase field types. For instance + the xBase field type for Date will just be returned as being FTInteger.

+ +

+    typedef enum {
+      FTString,			/* fixed length string field 		*/
+      FTInteger,		/* numeric field with no decimals 	*/
+      FTDouble,			/* numeric field with decimals 		*/
+      FTInvalid                 /* not a recognised field type 		*/
+    } DBFFieldType;
+
+ + + +

DBFAddField()

+ +
+int DBFAddField( DBFHandle hDBF, const char * pszFieldName, 
+                 DBFFieldType eType, int nWidth, int nDecimals );
+
+  hDBF:		The access handle for the file to be updated, as returned by
+		DBFOpen(), or DBFCreate().
+
+  pszFieldName:	The name of the new field.  At most 11 character will be used.
+                In order to use the xBase file in some packages it may be
+                necessary to avoid some special characters in the field names
+                such as spaces, or arithmetic operators.
+
+  eType:	One of FTString, FTInteger or FTDouble in order to establish
+                the type of the new field.  Note that some valid xBase field
+                types cannot be created such as date fields.
+
+  nWidth:	The width of the field to be created.  For FTString fields this
+                establishes the maximum length of string that can be stored.
+                For FTInteger this establishes the largest number that can
+                be represented.  For FTDouble fields this in combination
+                with the nDecimals value establish the size, and precision
+                of the created field.
+
+  nDecimals:    The number of decimal places to reserve for FTDouble fields.
+                For all other field types this should be zero.  For instance
+                with nWidth=7, and nDecimals=3 numbers would be formatted
+                similarly to `123.456'.
+
+ + The DBFAddField() function is used to add new fields to an existing xBase + file opened with DBFOpen(), or created with DBFCreate(). Note that fields + can only be added to xBase files with no records, though this is limitation + of this API, not of the file format.

+ + The DBFAddField() return value is the field number of the new field, or + -1 if the addition of the field failed.

+ + + +

DBFReadIntegerAttribute()

+ +
+int DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField );
+  
+  hDBF:		The access handle for the file to be queried, as returned by
+		DBFOpen(), or DBFCreate().
+
+  iShape:	The record number (shape number) from which the field value
+                should be read.
+
+  iField:	The field within the selected record that should be read.
+
+ + The DBFReadIntegerAttribute() will read the value of one field and return + it as an integer. This can be used even with FTString fields, though the + returned value will be zero if not interpretable as a number.

+ + + +

DBFReadDoubleAttribute()

+ +
+double DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField );
+  
+  hDBF:		The access handle for the file to be queried, as returned by
+		DBFOpen(), or DBFCreate().
+
+  iShape:	The record number (shape number) from which the field value
+                should be read.
+
+  iField:	The field within the selected record that should be read.
+
+ + The DBFReadDoubleAttribute() will read the value of one field and return + it as a double. This can be used even with FTString fields, though the + returned value will be zero if not interpretable as a number.

+ + + +

DBFReadStringAttribute()

+ +
+const char *DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField );
+  
+  hDBF:		The access handle for the file to be queried, as returned by
+		DBFOpen(), or DBFCreate().
+
+  iShape:	The record number (shape number) from which the field value
+                should be read.
+
+  iField:	The field within the selected record that should be read.
+
+ + The DBFReadStringAttribute() will read the value of one field and return + it as a string. This function may be used on any field type (including + FTInteger and FTDouble) and will return the string representation stored + in the .dbf file. The returned pointer is to an internal buffer + which is only valid untill the next DBF function call. It's contents may + be copied with normal string functions such as strcpy(), or strdup(). If + the TRIM_DBF_WHITESPACE macro is defined in shapefil.h (it is by default) + then all leading and trailing space (ASCII 32) characters will be stripped + before the string is returned.

+ + + +

DBFWriteIntegerAttribute

+ +
+int DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField,
+                              int nFieldValue );
+
+  hDBF:		The access handle for the file to be written, as returned by
+		DBFOpen(), or DBFCreate().
+
+  iShape:	The record number (shape number) to which the field value
+                should be written.
+
+  iField:	The field within the selected record that should be written.
+
+  nFieldValue:	The integer value that should be written.
+
+ +The DBFWriteIntegerAttribute() function is used to write a value to a numeric +field (FTInteger, or FTDouble). If the write succeeds the value TRUE will +be returned, otherwise FALSE will be returned. The value may be truncated +without warning if written to a field to narrow to represent the value.

+ + + +

DBFWriteDoubleAttribute()

+ +
+int DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField,
+                             double dFieldValue );
+
+  hDBF:		The access handle for the file to be written, as returned by
+		DBFOpen(), or DBFCreate().
+
+  iShape:	The record number (shape number) to which the field value
+                should be written.
+
+  iField:	The field within the selected record that should be written.
+
+  dFieldValue:	The floating point value that should be written.
+
+ +The DBFWriteDoubleAttribute() function is used to write a value to a numeric +field (FTInteger, or FTDouble). If the write succeeds the value TRUE will +be returned, otherwise FALSE will be returned. The value may be truncated +without warning if written to a field to narrow to represent the value.

+ + + +

DBFWriteStringAttribute()

+ +
+int DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField,
+                             const char * pszFieldValue );
+
+  hDBF:		The access handle for the file to be written, as returned by
+		DBFOpen(), or DBFCreate().
+
+  iShape:	The record number (shape number) to which the field value
+                should be written.
+
+  iField:	The field within the selected record that should be written.
+
+  pszFieldValue: The string to be written to the field.
+
+ +The DBFWriteStringAttribute() function is used to write a value to a string +field (FString). If the write succeeds the value TRUE willbe returned, +otherwise FALSE will be returned. The value may be truncated +without warning if written to a field to narrow to hold the string.

+ + + +

DBFClose()

+ +
+void DBFClose( DBFHandle hDBF );
+
+  hDBF:		The access handle for the file to be closed.
+
+ + The DBFClose() function will close the indicated xBase file (opened with + DBFOpen(), or DBFCreate()), flushing out all information to the file on + disk, and recovering any resources associated with having the file open. + The file handle (hDBF) should not be used again with the DBF API after + calling DBFClose().

+ + + diff --git a/src/Lib/shapelib/dbfadd.c b/src/Lib/shapelib/dbfadd.c new file mode 100644 index 00000000..9aeb263d --- /dev/null +++ b/src/Lib/shapelib/dbfadd.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1995 Frank Warmerdam + * + * This code is in the public domain. + * + * $Log$ + * Revision 1.1 2000/02/09 19:51:46 curt + * Initial revision + * + * Revision 1.1 1999/08/24 21:13:00 curt + * Initial revision. + * + * Revision 1.4 1998/12/03 16:36:06 warmerda + * Added stdlib.h and math.h to get atof() prototype. + * + * Revision 1.3 1995/10/21 03:13:23 warmerda + * Use binary mode.. + * + * Revision 1.2 1995/08/04 03:15:59 warmerda + * Added header. + * + */ + +static char rcsid[] = + "$Id$"; + +#include "shapefil.h" +#include +#include + +int main( int argc, char ** argv ) + +{ + DBFHandle hDBF; + int i, iRecord; + +/* -------------------------------------------------------------------- */ +/* Display a usage message. */ +/* -------------------------------------------------------------------- */ + if( argc < 3 ) + { + printf( "dbfadd xbase_file field_values\n" ); + + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Create the database. */ +/* -------------------------------------------------------------------- */ + hDBF = DBFOpen( argv[1], "r+b" ); + if( hDBF == NULL ) + { + printf( "DBFOpen(%s,\"rb+\") failed.\n", argv[1] ); + exit( 2 ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have the correct number of arguments? */ +/* -------------------------------------------------------------------- */ + if( DBFGetFieldCount( hDBF ) != argc - 2 ) + { + printf( "Got %d fields, but require %d\n", + argc - 2, DBFGetFieldCount( hDBF ) ); + exit( 3 ); + } + + iRecord = DBFGetRecordCount( hDBF ); + +/* -------------------------------------------------------------------- */ +/* Loop assigning the new field values. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < DBFGetFieldCount(hDBF); i++ ) + { + if( DBFGetFieldInfo( hDBF, i, NULL, NULL, NULL ) == FTString ) + DBFWriteStringAttribute(hDBF, iRecord, i, argv[i+2] ); + else + DBFWriteDoubleAttribute(hDBF, iRecord, i, atof(argv[i+2]) ); + } + +/* -------------------------------------------------------------------- */ +/* Close and cleanup. */ +/* -------------------------------------------------------------------- */ + DBFClose( hDBF ); + + return( 0 ); +} diff --git a/src/Lib/shapelib/dbfcreate.c b/src/Lib/shapelib/dbfcreate.c new file mode 100644 index 00000000..31024244 --- /dev/null +++ b/src/Lib/shapelib/dbfcreate.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 1995 Frank Warmerdam + * + * This code is in the public domain. + * + * $Log$ + * Revision 1.1 2000/02/09 19:51:46 curt + * Initial revision + * + * Revision 1.1 1999/08/24 21:13:00 curt + * Initial revision. + * + * Revision 1.3 1999/04/01 18:47:44 warmerda + * Fixed DBFAddField() call convention. + * + * Revision 1.2 1995/08/04 03:17:11 warmerda + * Added header. + * + */ + +static char rcsid[] = + "$Id$"; + +#include "shapefil.h" + +int main( int argc, char ** argv ) + +{ + DBFHandle hDBF; + int *panWidth, i, iRecord; + char szFormat[32], szField[1024]; + int nWidth, nDecimals; + +/* -------------------------------------------------------------------- */ +/* Display a usage message. */ +/* -------------------------------------------------------------------- */ + if( argc < 2 ) + { + printf( "dbfcreate xbase_file [[-s field_name width],[-n field_name width decimals]]...\n" ); + + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Create the database. */ +/* -------------------------------------------------------------------- */ + hDBF = DBFCreate( argv[1] ); + if( hDBF == NULL ) + { + printf( "DBFCreate(%s) failed.\n", argv[1] ); + exit( 2 ); + } + +/* -------------------------------------------------------------------- */ +/* Loop over the field definitions adding new fields. */ +/* -------------------------------------------------------------------- */ + for( i = 2; i < argc; i++ ) + { + if( strcmp(argv[i],"-s") == 0 && i < argc-2 ) + { + if( DBFAddField( hDBF, argv[i+1], FTString, atoi(argv[i+2]), 0 ) + == -1 ) + { + printf( "DBFAddField(%s,FTString,%d,0) failed.\n", + argv[i+1], atoi(argv[i+2]) ); + exit( 4 ); + } + i = i + 2; + } + else if( strcmp(argv[i],"-n") == 0 && i < argc-3 ) + { + if( DBFAddField( hDBF, argv[i+1], FTDouble, atoi(argv[i+2]), + atoi(argv[i+3]) ) == -1 ) + { + printf( "DBFAddField(%s,FTDouble,%d,%d) failed.\n", + argv[i+1], atoi(argv[i+2]), atoi(argv[i+3]) ); + exit( 4 ); + } + i = i + 3; + } + else + { + printf( "Argument incomplete, or unrecognised:%s\n", argv[i] ); + exit( 3 ); + } + } + + DBFClose( hDBF ); + + return( 0 ); +} diff --git a/src/Lib/shapelib/dbfdump.c b/src/Lib/shapelib/dbfdump.c new file mode 100644 index 00000000..86b6c0f9 --- /dev/null +++ b/src/Lib/shapelib/dbfdump.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1995 Frank Warmerdam + * + * This code is in the public domain. + * + * $Log$ + * Revision 1.1 2000/02/09 19:51:46 curt + * Initial revision + * + * Revision 1.1 1999/08/24 21:13:00 curt + * Initial revision. + * + * Revision 1.4 1998/12/31 15:30:13 warmerda + * Added -m, -r, and -h commandline options. + * + * Revision 1.3 1995/10/21 03:15:01 warmerda + * Changed to use binary file access. + * + * Revision 1.2 1995/08/04 03:16:22 warmerda + * Added header. + * + */ + +static char rcsid[] = + "$Id$"; + +#include "shapefil.h" + +int main( int argc, char ** argv ) + +{ + DBFHandle hDBF; + int *panWidth, i, iRecord; + char szFormat[32], szField[1024], *pszFilename = NULL; + int nWidth, nDecimals; + int bHeader = 0; + int bRaw = 0; + int bMultiLine = 0; + char szTitle[12]; + +/* -------------------------------------------------------------------- */ +/* Handle arguments. */ +/* -------------------------------------------------------------------- */ + for( i = 1; i < argc; i++ ) + { + if( strcmp(argv[i],"-h") == 0 ) + bHeader = 1; + else if( strcmp(argv[i],"-r") == 0 ) + bRaw = 1; + else if( strcmp(argv[i],"-m") == 0 ) + bMultiLine = 1; + else + pszFilename = argv[i]; + } + +/* -------------------------------------------------------------------- */ +/* Display a usage message. */ +/* -------------------------------------------------------------------- */ + if( pszFilename == NULL ) + { + printf( "dbfdump [-h] [-r] [-m] xbase_file\n" ); + printf( " -h: Write header info (field descriptions)\n" ); + printf( " -r: Write raw field info, numeric values not reformatted\n" ); + printf( " -m: Multiline, one line per field.\n" ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + hDBF = DBFOpen( pszFilename, "rb" ); + if( hDBF == NULL ) + { + printf( "DBFOpen(%s,\"r\") failed.\n", argv[1] ); + exit( 2 ); + } + +/* -------------------------------------------------------------------- */ +/* If there is no data in this file let the user know. */ +/* -------------------------------------------------------------------- */ + if( DBFGetFieldCount(hDBF) == 0 ) + { + printf( "There are no fields in this table!\n" ); + exit( 3 ); + } + +/* -------------------------------------------------------------------- */ +/* Dump header definitions. */ +/* -------------------------------------------------------------------- */ + if( bHeader ) + { + for( i = 0; i < DBFGetFieldCount(hDBF); i++ ) + { + DBFFieldType eType; + const char *pszTypeName; + + eType = DBFGetFieldInfo( hDBF, i, szTitle, &nWidth, &nDecimals ); + if( eType == FTString ) + pszTypeName = "String"; + else if( eType == FTInteger ) + pszTypeName = "Integer"; + else if( eType == FTDouble ) + pszTypeName = "Double"; + else if( eType == FTInvalid ) + pszTypeName = "Invalid"; + + printf( "Field %d: Type=%s, Title=`%s', Width=%d, Decimals=%d\n", + i, pszTypeName, szTitle, nWidth, nDecimals ); + } + } + +/* -------------------------------------------------------------------- */ +/* Compute offsets to use when printing each of the field */ +/* values. We make each field as wide as the field title+1, or */ +/* the field value + 1. */ +/* -------------------------------------------------------------------- */ + panWidth = (int *) malloc( DBFGetFieldCount( hDBF ) * sizeof(int) ); + + for( i = 0; i < DBFGetFieldCount(hDBF) && !bMultiLine; i++ ) + { + DBFFieldType eType; + + eType = DBFGetFieldInfo( hDBF, i, szTitle, &nWidth, &nDecimals ); + if( strlen(szTitle) > nWidth ) + panWidth[i] = strlen(szTitle); + else + panWidth[i] = nWidth; + + if( eType == FTString ) + sprintf( szFormat, "%%-%ds ", panWidth[i] ); + else + sprintf( szFormat, "%%%ds ", panWidth[i] ); + printf( szFormat, szTitle ); + } + printf( "\n" ); + +/* -------------------------------------------------------------------- */ +/* Read all the records */ +/* -------------------------------------------------------------------- */ + for( iRecord = 0; iRecord < DBFGetRecordCount(hDBF); iRecord++ ) + { + if( bMultiLine ) + printf( "Record: %d\n", iRecord ); + + for( i = 0; i < DBFGetFieldCount(hDBF); i++ ) + { + DBFFieldType eType; + + eType = DBFGetFieldInfo( hDBF, i, szTitle, &nWidth, &nDecimals ); + + if( bMultiLine ) + { + printf( "%s: ", szTitle ); + } + +/* -------------------------------------------------------------------- */ +/* Print the record according to the type and formatting */ +/* information implicit in the DBF field description. */ +/* -------------------------------------------------------------------- */ + if( !bRaw ) + { + switch( eType ) + { + case FTString: + sprintf( szFormat, "%%-%ds", nWidth ); + printf( szFormat, + DBFReadStringAttribute( hDBF, iRecord, i ) ); + break; + + case FTInteger: + sprintf( szFormat, "%%%dd", nWidth ); + printf( szFormat, + DBFReadIntegerAttribute( hDBF, iRecord, i ) ); + break; + + case FTDouble: + sprintf( szFormat, "%%%d.%dlf", nWidth, nDecimals ); + printf( szFormat, + DBFReadDoubleAttribute( hDBF, iRecord, i ) ); + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Just dump in raw form (as formatted in the file). */ +/* -------------------------------------------------------------------- */ + else + { + sprintf( szFormat, "%%-%ds", nWidth ); + printf( szFormat, + DBFReadStringAttribute( hDBF, iRecord, i ) ); + } + +/* -------------------------------------------------------------------- */ +/* Write out any extra spaces required to pad out the field */ +/* width. */ +/* -------------------------------------------------------------------- */ + if( !bMultiLine ) + { + sprintf( szFormat, "%%%ds", panWidth[i] - nWidth + 1 ); + printf( szFormat, "" ); + } + + if( bMultiLine ) + printf( "\n" ); + + fflush( stdout ); + } + printf( "\n" ); + } + + DBFClose( hDBF ); + + return( 0 ); +} diff --git a/src/Lib/shapelib/dbfopen.c b/src/Lib/shapelib/dbfopen.c new file mode 100644 index 00000000..d46b45b0 --- /dev/null +++ b/src/Lib/shapelib/dbfopen.c @@ -0,0 +1,851 @@ +/****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log$ + * Revision 1.1 2000/02/09 19:51:46 curt + * Initial revision + * + * Revision 1.1 1999/08/24 21:13:00 curt + * Initial revision. + * + * Revision 1.14 1999/05/04 15:01:48 warmerda + * Added 'F' support. + * + * Revision 1.13 1999/03/23 17:38:59 warmerda + * DBFAddField() now actually does return the new field number, or -1 if + * it fails. + * + * Revision 1.12 1999/03/06 02:54:46 warmerda + * Added logic to convert shapefile name to dbf filename in DBFOpen() + * for convenience. + * + * Revision 1.11 1998/12/31 15:30:34 warmerda + * Improved the interchangability of numeric and string attributes. Add + * white space trimming option for attributes. + * + * Revision 1.10 1998/12/03 16:36:44 warmerda + * Use r+b instead of rb+ for binary access. + * + * Revision 1.9 1998/12/03 15:34:23 warmerda + * Updated copyright message. + * + * Revision 1.8 1997/12/04 15:40:15 warmerda + * Added newline character after field definitions. + * + * Revision 1.7 1997/03/06 14:02:10 warmerda + * Ensure bUpdated is initialized. + * + * Revision 1.6 1996/02/12 04:54:41 warmerda + * Ensure that DBFWriteAttribute() returns TRUE if it succeeds. + * + * Revision 1.5 1995/10/21 03:15:12 warmerda + * Changed to use binary file access, and ensure that the + * field name field is zero filled, and limited to 10 chars. + * + * Revision 1.4 1995/08/24 18:10:42 warmerda + * Added use of SfRealloc() to avoid pre-ANSI realloc() functions such + * as on the Sun. + * + * Revision 1.3 1995/08/04 03:15:16 warmerda + * Fixed up header. + * + * Revision 1.2 1995/08/04 03:14:43 warmerda + * Added header. + */ + +static char rcsid[] = + "$Id$"; + +#include "shapefil.h" + +#include + +typedef unsigned char uchar; + +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif + +/************************************************************************/ +/* SfRealloc() */ +/* */ +/* A realloc cover function that will access a NULL pointer as */ +/* a valid input. */ +/************************************************************************/ + +static void * SfRealloc( void * pMem, int nNewSize ) + +{ + if( pMem == NULL ) + return( (void *) malloc(nNewSize) ); + else + return( (void *) realloc(pMem,nNewSize) ); +} + +/************************************************************************/ +/* DBFWriteHeader() */ +/* */ +/* This is called to write out the file header, and field */ +/* descriptions before writing any actual data records. This */ +/* also computes all the DBFDataSet field offset/size/decimals */ +/* and so forth values. */ +/************************************************************************/ + +static void DBFWriteHeader(DBFHandle psDBF) + +{ + uchar abyHeader[32]; + int i; + + if( !psDBF->bNoHeader ) + return; + + psDBF->bNoHeader = FALSE; + +/* -------------------------------------------------------------------- */ +/* Initialize the file header information. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < 32; i++ ) + abyHeader[i] = 0; + + abyHeader[0] = 0x03; /* memo field? - just copying */ + + /* date updated on close, record count preset at zero */ + + abyHeader[8] = psDBF->nHeaderLength % 256; + abyHeader[9] = psDBF->nHeaderLength / 256; + + abyHeader[10] = psDBF->nRecordLength % 256; + abyHeader[11] = psDBF->nRecordLength / 256; + +/* -------------------------------------------------------------------- */ +/* Write the initial 32 byte file header, and all the field */ +/* descriptions. */ +/* -------------------------------------------------------------------- */ + fseek( psDBF->fp, 0, 0 ); + fwrite( abyHeader, 32, 1, psDBF->fp ); + fwrite( psDBF->pszHeader, 32, psDBF->nFields, psDBF->fp ); + +/* -------------------------------------------------------------------- */ +/* Write out the newline character if there is room for it. */ +/* -------------------------------------------------------------------- */ + if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 ) + { + char cNewline; + + cNewline = 0x0d; + fwrite( &cNewline, 1, 1, psDBF->fp ); + } +} + +/************************************************************************/ +/* DBFFlushRecord() */ +/* */ +/* Write out the current record if there is one. */ +/************************************************************************/ + +static void DBFFlushRecord( DBFHandle psDBF ) + +{ + int nRecordOffset; + + if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 ) + { + psDBF->bCurrentRecordModified = FALSE; + + nRecordOffset = psDBF->nRecordLength * psDBF->nCurrentRecord + + psDBF->nHeaderLength; + + fseek( psDBF->fp, nRecordOffset, 0 ); + fwrite( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); + } +} + +/************************************************************************/ +/* DBFOpen() */ +/* */ +/* Open a .dbf file. */ +/************************************************************************/ + +DBFHandle DBFOpen( const char * pszFilename, const char * pszAccess ) + +{ + DBFHandle psDBF; + uchar *pabyBuf; + int nFields, nRecords, nHeadLen, nRecLen, iField, i; + char *pszDBFFilename; + FILE *fp; + +/* -------------------------------------------------------------------- */ +/* We only allow the access strings "rb" and "r+". */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 + && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"r+b") != 0 ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Ensure the extension is converted to dbf or DBF if it is */ +/* currently .shp or .shx. */ +/* -------------------------------------------------------------------- */ + pszDBFFilename = (char *) malloc(strlen(pszFilename)+1); + strcpy( pszDBFFilename, pszFilename ); + + if( strcmp(pszFilename+strlen(pszFilename)-4,".shp") + || strcmp(pszFilename+strlen(pszFilename)-4,".shx") ) + { + strcpy( pszDBFFilename+strlen(pszDBFFilename)-4, ".dbf"); + } + else if( strcmp(pszFilename+strlen(pszFilename)-4,".SHP") + || strcmp(pszFilename+strlen(pszFilename)-4,".SHX") ) + { + strcpy( pszDBFFilename+strlen(pszDBFFilename)-4, ".DBF"); + } + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) ); + psDBF->fp = fopen( pszDBFFilename, pszAccess ); + if( psDBF->fp == NULL ) + return( NULL ); + + psDBF->bNoHeader = FALSE; + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + + free( pszDBFFilename ); + +/* -------------------------------------------------------------------- */ +/* Read Table Header info */ +/* -------------------------------------------------------------------- */ + pabyBuf = (uchar *) malloc(500); + fread( pabyBuf, 32, 1, psDBF->fp ); + + psDBF->nRecords = nRecords = + pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256; + + psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256; + psDBF->nRecordLength = nRecLen = pabyBuf[10] + pabyBuf[11]*256; + + psDBF->nFields = nFields = (nHeadLen - 32) / 32; + + psDBF->pszCurrentRecord = (char *) malloc(nRecLen); + +/* -------------------------------------------------------------------- */ +/* Read in Field Definitions */ +/* -------------------------------------------------------------------- */ + + pabyBuf = (uchar *) SfRealloc(pabyBuf,nHeadLen); + psDBF->pszHeader = (char *) pabyBuf; + + fseek( psDBF->fp, 32, 0 ); + fread( pabyBuf, nHeadLen, 1, psDBF->fp ); + + psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields); + psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields); + psDBF->panFieldDecimals = (int *) malloc(sizeof(int) * nFields); + psDBF->pachFieldType = (char *) malloc(sizeof(char) * nFields); + + for( iField = 0; iField < nFields; iField++ ) + { + char *pszTitle; + int nType; + uchar *pabyFInfo; + + pabyFInfo = pabyBuf+iField*32; + + if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' ) + { + psDBF->panFieldSize[iField] = pabyFInfo[16]; + psDBF->panFieldDecimals[iField] = pabyFInfo[17]; + } + else + { + psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17]*256; + psDBF->panFieldDecimals[iField] = 0; + } + + psDBF->pachFieldType[iField] = (char) pabyFInfo[11]; + if( iField == 0 ) + psDBF->panFieldOffset[iField] = 1; + else + psDBF->panFieldOffset[iField] = + psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1]; + } + + return( psDBF ); +} + +/************************************************************************/ +/* DBFClose() */ +/************************************************************************/ + +void DBFClose(DBFHandle psDBF) +{ +/* -------------------------------------------------------------------- */ +/* Write out header if not already written. */ +/* -------------------------------------------------------------------- */ + if( psDBF->bNoHeader ) + DBFWriteHeader( psDBF ); + + DBFFlushRecord( psDBF ); + +/* -------------------------------------------------------------------- */ +/* Update last access date, and number of records if we have */ +/* write access. */ +/* -------------------------------------------------------------------- */ + if( psDBF->bUpdated ) + { + uchar abyFileHeader[32]; + + fseek( psDBF->fp, 0, 0 ); + fread( abyFileHeader, 32, 1, psDBF->fp ); + + abyFileHeader[1] = 95; /* YY */ + abyFileHeader[2] = 7; /* MM */ + abyFileHeader[3] = 26; /* DD */ + + abyFileHeader[4] = psDBF->nRecords % 256; + abyFileHeader[5] = (psDBF->nRecords/256) % 256; + abyFileHeader[6] = (psDBF->nRecords/(256*256)) % 256; + abyFileHeader[7] = (psDBF->nRecords/(256*256*256)) % 256; + + fseek( psDBF->fp, 0, 0 ); + fwrite( abyFileHeader, 32, 1, psDBF->fp ); + } + +/* -------------------------------------------------------------------- */ +/* Close, and free resources. */ +/* -------------------------------------------------------------------- */ + fclose( psDBF->fp ); + + if( psDBF->panFieldOffset != NULL ) + { + free( psDBF->panFieldOffset ); + free( psDBF->panFieldSize ); + free( psDBF->panFieldDecimals ); + free( psDBF->pachFieldType ); + } + + free( psDBF->pszHeader ); + free( psDBF->pszCurrentRecord ); + + free( psDBF ); +} + +/************************************************************************/ +/* DBFCreate() */ +/* */ +/* Create a new .dbf file. */ +/************************************************************************/ + +DBFHandle DBFCreate( const char * pszFilename ) + +{ + DBFHandle psDBF; + FILE *fp; + +/* -------------------------------------------------------------------- */ +/* Create the file. */ +/* -------------------------------------------------------------------- */ + fp = fopen( pszFilename, "wb" ); + if( fp == NULL ) + return( NULL ); + + fputc( 0, fp ); + fclose( fp ); + + fp = fopen( pszFilename, "rb+" ); + if( fp == NULL ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Create the info structure. */ +/* -------------------------------------------------------------------- */ + psDBF = (DBFHandle) malloc(sizeof(DBFInfo)); + + psDBF->fp = fp; + psDBF->nRecords = 0; + psDBF->nFields = 0; + psDBF->nRecordLength = 1; + psDBF->nHeaderLength = 33; + + psDBF->panFieldOffset = NULL; + psDBF->panFieldSize = NULL; + psDBF->panFieldDecimals = NULL; + psDBF->pachFieldType = NULL; + psDBF->pszHeader = NULL; + + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->pszCurrentRecord = NULL; + + psDBF->bNoHeader = TRUE; + + return( psDBF ); +} + +/************************************************************************/ +/* DBFAddField() */ +/* */ +/* Add a field to a newly created .dbf file before any records */ +/* are written. */ +/************************************************************************/ + +int DBFAddField(DBFHandle psDBF, const char * pszFieldName, + DBFFieldType eType, int nWidth, int nDecimals ) + +{ + char *pszFInfo; + int i; + +/* -------------------------------------------------------------------- */ +/* Do some checking to ensure we can add records to this file. */ +/* -------------------------------------------------------------------- */ + if( psDBF->nRecords > 0 ) + return( -1 ); + + if( !psDBF->bNoHeader ) + return( -1 ); + + if( eType != FTDouble && nDecimals != 0 ) + return( -1 ); + +/* -------------------------------------------------------------------- */ +/* SfRealloc all the arrays larger to hold the additional field */ +/* information. */ +/* -------------------------------------------------------------------- */ + psDBF->nFields++; + + psDBF->panFieldOffset = (int *) + SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldSize = (int *) + SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldDecimals = (int *) + SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); + + psDBF->pachFieldType = (char *) + SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); + +/* -------------------------------------------------------------------- */ +/* Assign the new field information fields. */ +/* -------------------------------------------------------------------- */ + psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength; + psDBF->nRecordLength += nWidth; + psDBF->panFieldSize[psDBF->nFields-1] = nWidth; + psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals; + + if( eType == FTString ) + psDBF->pachFieldType[psDBF->nFields-1] = 'C'; + else + psDBF->pachFieldType[psDBF->nFields-1] = 'N'; + +/* -------------------------------------------------------------------- */ +/* Extend the required header information. */ +/* -------------------------------------------------------------------- */ + psDBF->nHeaderLength += 32; + psDBF->bUpdated = FALSE; + + psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); + + pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1); + + for( i = 0; i < 32; i++ ) + pszFInfo[i] = '\0'; + + if( strlen(pszFieldName) < 10 ) + strncpy( pszFInfo, pszFieldName, strlen(pszFieldName)); + else + strncpy( pszFInfo, pszFieldName, 10); + + pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1]; + + if( eType == FTString ) + { + pszFInfo[16] = nWidth % 256; + pszFInfo[17] = nWidth / 256; + } + else + { + pszFInfo[16] = nWidth; + pszFInfo[17] = nDecimals; + } + +/* -------------------------------------------------------------------- */ +/* Make the current record buffer appropriately larger. */ +/* -------------------------------------------------------------------- */ + psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, + psDBF->nRecordLength); + + return( psDBF->nFields-1 ); +} + +/************************************************************************/ +/* DBFReadAttribute() */ +/* */ +/* Read one of the attribute fields of a record. */ +/************************************************************************/ + +static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, + char chReqType ) + +{ + int nRecordOffset, i, j; + uchar *pabyRec; + char *pszSField; + void *pReturnField = NULL; + + static double dDoubleField; + static char * pszStringField = NULL; + static int nStringFieldLen = 0; + +/* -------------------------------------------------------------------- */ +/* Have we read the record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity >= psDBF->nRecords ) + return( NULL ); + + if( psDBF->nCurrentRecord != hEntity ) + { + DBFFlushRecord( psDBF ); + + nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; + + fseek( psDBF->fp, nRecordOffset, 0 ); + fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); + + psDBF->nCurrentRecord = hEntity; + } + + pabyRec = (uchar *) psDBF->pszCurrentRecord; + +/* -------------------------------------------------------------------- */ +/* Ensure our field buffer is large enough to hold this buffer. */ +/* -------------------------------------------------------------------- */ + if( psDBF->panFieldSize[iField]+1 > nStringFieldLen ) + { + nStringFieldLen = psDBF->panFieldSize[iField]*2 + 10; + pszStringField = (char *) SfRealloc(pszStringField,nStringFieldLen); + } + +/* -------------------------------------------------------------------- */ +/* Extract the requested field. */ +/* -------------------------------------------------------------------- */ + strncpy( pszStringField, pabyRec+psDBF->panFieldOffset[iField], + psDBF->panFieldSize[iField] ); + pszStringField[psDBF->panFieldSize[iField]] = '\0'; + + pReturnField = pszStringField; + +/* -------------------------------------------------------------------- */ +/* Decode the field. */ +/* -------------------------------------------------------------------- */ + if( chReqType == 'N' ) + { + sscanf( pszStringField, "%lf", &dDoubleField ); + + pReturnField = &dDoubleField; + } + +/* -------------------------------------------------------------------- */ +/* Should we trim white space off the string attribute value? */ +/* -------------------------------------------------------------------- */ +#ifdef TRIM_DBF_WHITESPACE + else + { + char *pchSrc, *pchDst; + + pchDst = pchSrc = pszStringField; + while( *pchSrc == ' ' ) + pchSrc++; + + while( *pchSrc != '\0' ) + *(pchDst++) = *(pchSrc++); + *pchDst = '\0'; + + while( *(--pchDst) == ' ' && pchDst != pszStringField ) + *pchDst = '\0'; + + } +#endif + + return( pReturnField ); +} + +/************************************************************************/ +/* DBFReadIntAttribute() */ +/* */ +/* Read an integer attribute. */ +/************************************************************************/ + +int DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + double *pdValue; + + pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); + + return( (int) *pdValue ); +} + +/************************************************************************/ +/* DBFReadDoubleAttribute() */ +/* */ +/* Read a double attribute. */ +/************************************************************************/ + +double DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + double *pdValue; + + pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); + + return( *pdValue ); +} + +/************************************************************************/ +/* DBFReadStringAttribute() */ +/* */ +/* Read a string attribute. */ +/************************************************************************/ + +const char *DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) ); +} + +/************************************************************************/ +/* DBFGetFieldCount() */ +/* */ +/* Return the number of fields in this table. */ +/************************************************************************/ + +int DBFGetFieldCount( DBFHandle psDBF ) + +{ + return( psDBF->nFields ); +} + +/************************************************************************/ +/* DBFGetRecordCount() */ +/* */ +/* Return the number of records in this table. */ +/************************************************************************/ + +int DBFGetRecordCount( DBFHandle psDBF ) + +{ + return( psDBF->nRecords ); +} + +/************************************************************************/ +/* DBFGetFieldInfo() */ +/* */ +/* Return any requested information about the field. */ +/************************************************************************/ + +DBFFieldType DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, + int * pnWidth, int * pnDecimals ) + +{ + if( iField < 0 || iField >= psDBF->nFields ) + return( FTInvalid ); + + if( pnWidth != NULL ) + *pnWidth = psDBF->panFieldSize[iField]; + + if( pnDecimals != NULL ) + *pnDecimals = psDBF->panFieldDecimals[iField]; + + if( pszFieldName != NULL ) + { + int i; + + strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 ); + pszFieldName[11] = '\0'; + for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- ) + pszFieldName[i] = '\0'; + } + + if( psDBF->pachFieldType[iField] == 'N' + || psDBF->pachFieldType[iField] == 'F' + || psDBF->pachFieldType[iField] == 'D' ) + { + if( psDBF->panFieldDecimals[iField] > 0 ) + return( FTDouble ); + else + return( FTInteger ); + } + else + { + return( FTString ); + } +} + +/************************************************************************/ +/* DBFWriteAttribute() */ +/* */ +/* Write an attribute record to the file. */ +/************************************************************************/ + +static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, + void * pValue ) + +{ + int nRecordOffset, i, j; + uchar *pabyRec; + char szSField[40], szFormat[12]; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + DBFFlushRecord( psDBF ); + + psDBF->nRecords++; + for( i = 0; i < psDBF->nRecordLength; i++ ) + psDBF->pszCurrentRecord[i] = ' '; + + psDBF->nCurrentRecord = hEntity; + } + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( psDBF->nCurrentRecord != hEntity ) + { + DBFFlushRecord( psDBF ); + + nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; + + fseek( psDBF->fp, nRecordOffset, 0 ); + fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); + + psDBF->nCurrentRecord = hEntity; + } + + pabyRec = (uchar *) psDBF->pszCurrentRecord; + +/* -------------------------------------------------------------------- */ +/* Assign all the record fields. */ +/* -------------------------------------------------------------------- */ + switch( psDBF->pachFieldType[iField] ) + { + case 'D': + case 'N': + case 'F': + if( psDBF->panFieldDecimals[iField] == 0 ) + { + sprintf( szFormat, "%%%dd", psDBF->panFieldSize[iField] ); + sprintf(szSField, szFormat, (int) *((double *) pValue) ); + if( strlen(szSField) > psDBF->panFieldSize[iField] ) + szSField[psDBF->panFieldSize[iField]] = '\0'; + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + szSField, strlen(szSField) ); + } + else + { + sprintf( szFormat, "%%%d.%df", + psDBF->panFieldSize[iField], + psDBF->panFieldDecimals[iField] ); + sprintf(szSField, szFormat, *((double *) pValue) ); + if( strlen(szSField) > psDBF->panFieldSize[iField] ) + szSField[psDBF->panFieldSize[iField]] = '\0'; + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + szSField, strlen(szSField) ); + } + break; + + default: + if( strlen((char *) pValue) > psDBF->panFieldSize[iField] ) + j = psDBF->panFieldSize[iField]; + else + j = strlen((char *) pValue); + + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + (char *) pValue, j ); + break; + } + + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + + return( TRUE ); +} + +/************************************************************************/ +/* DBFWriteDoubleAttribute() */ +/* */ +/* Write a double attribute. */ +/************************************************************************/ + +int DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField, + double dValue ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); +} + +/************************************************************************/ +/* DBFWriteIntegerAttribute() */ +/* */ +/* Write a integer attribute. */ +/************************************************************************/ + +int DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField, + int nValue ) + +{ + double dValue = nValue; + + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); +} + +/************************************************************************/ +/* DBFWriteStringAttribute() */ +/* */ +/* Write a string attribute. */ +/************************************************************************/ + +int DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField, + const char * pszValue ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) ); +} + diff --git a/src/Lib/shapelib/makeshape.sh b/src/Lib/shapelib/makeshape.sh new file mode 100755 index 00000000..dbc3ebee --- /dev/null +++ b/src/Lib/shapelib/makeshape.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# +# Use example programs to create a very simple dataset that +# should display in ARCView II. +# + +shpcreate test polygon +dbfcreate test.dbf -s Description 30 -n TestInt 6 0 -n TestDouble 16 5 + +shpadd test 0 0 100 0 100 100 0 100 0 0 + 20 20 20 30 30 30 20 20 +dbfadd test.dbf "Square with triangle missing" 1.5 2.5 + +shpadd test 150 150 160 150 180 170 150 150 +dbfadd test.dbf "Smaller triangle" 100 1000.25 + +shpdump test.shp +dbfdump test.dbf diff --git a/src/Lib/shapelib/shape.errata b/src/Lib/shapelib/shape.errata new file mode 100644 index 00000000..9263f89e --- /dev/null +++ b/src/Lib/shapelib/shape.errata @@ -0,0 +1,18 @@ +Shapefile Specification Errata +------------------------------ + +1) The table on page 23 (Multipatch Record Contents) shows ShapeType + being 15 when it should be 31. + +2) The example file brklinz.shp does not seem to initialize the + ZMin, ZMax values in the header of the .shp file (at byte 68-83). + Is there a reason? + +3) The table on page 15 for PointZ type does not show the Measure value + as being optional, yet the masspntz.shp example dataset doesn't have + the measure values implying that it must be optional. + +4) The sample file, 3dpoints.shp/shx/dbf seems to be corrupt. In particular + the record offsets in the .shx file are all over the place, leading to + some of the shapes reading improperly. + diff --git a/src/Lib/shapelib/shapefil.h b/src/Lib/shapelib/shapefil.h new file mode 100644 index 00000000..f775afb4 --- /dev/null +++ b/src/Lib/shapelib/shapefil.h @@ -0,0 +1,242 @@ +#ifndef _SHAPEFILE_H_INCLUDED +#define _SHAPEFILE_H_INCLUDED + +/* + * Copyright (c) 1995 Frank Warmerdam + * + * This code is in the public domain. + * + * $Log$ + * Revision 1.1 2000/02/09 19:51:46 curt + * Initial revision + * + * Revision 1.1 1999/08/24 21:13:01 curt + * Initial revision. + * + * Revision 1.8 1999/03/23 17:22:27 warmerda + * Added extern "C" protection for C++ users of shapefil.h. + * + * Revision 1.7 1998/12/31 15:31:07 warmerda + * Added the TRIM_DBF_WHITESPACE and DISABLE_MULTIPATCH_MEASURE options. + * + * Revision 1.6 1998/12/03 15:48:15 warmerda + * Added SHPCalculateExtents(). + * + * Revision 1.5 1998/11/09 20:57:16 warmerda + * Altered SHPGetInfo() call. + * + * Revision 1.4 1998/11/09 20:19:33 warmerda + * Added 3D support, and use of SHPObject. + * + * Revision 1.3 1995/08/23 02:24:05 warmerda + * Added support for reading bounds. + * + * Revision 1.2 1995/08/04 03:17:39 warmerda + * Added header. + * + */ + +#include + +#ifdef USE_DBMALLOC +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************/ +/* Configuration options. */ +/************************************************************************/ + +/* -------------------------------------------------------------------- */ +/* Should the DBFReadStringAttribute() strip leading and */ +/* trailing white space? */ +/* -------------------------------------------------------------------- */ +#define TRIM_DBF_WHITESPACE + +/* -------------------------------------------------------------------- */ +/* Should we write measure values to the Multipatch object? */ +/* Reportedly ArcView crashes if we do write it, so for now it */ +/* is disabled. */ +/* -------------------------------------------------------------------- */ +#define DISABLE_MULTIPATCH_MEASURE + +/************************************************************************/ +/* SHP Support. */ +/************************************************************************/ +typedef struct +{ + FILE *fpSHP; + FILE *fpSHX; + + int nShapeType; /* SHPT_* */ + + int nFileSize; /* SHP file */ + + int nRecords; + int nMaxRecords; + int *panRecOffset; + int *panRecSize; + + double adBoundsMin[4]; + double adBoundsMax[4]; + + int bUpdated; +} SHPInfo; + +typedef SHPInfo * SHPHandle; + +/* -------------------------------------------------------------------- */ +/* Shape types (nSHPType) */ +/* -------------------------------------------------------------------- */ +#define SHPT_POINT 1 +#define SHPT_ARC 3 +#define SHPT_POLYGON 5 +#define SHPT_MULTIPOINT 8 +#define SHPT_POINTZ 11 +#define SHPT_ARCZ 13 +#define SHPT_POLYGONZ 15 +#define SHPT_MULTIPOINTZ 18 +#define SHPT_POINTM 21 +#define SHPT_ARCM 23 +#define SHPT_POLYGONM 25 +#define SHPT_MULTIPOINTM 28 +#define SHPT_MULTIPATCH 31 + + +/* -------------------------------------------------------------------- */ +/* Part types - everything but SHPT_MULTIPATCH just uses */ +/* SHPP_RING. */ +/* -------------------------------------------------------------------- */ + +#define SHPP_TRISTRIP 0 +#define SHPP_TRIFAN 1 +#define SHPP_OUTERRING 2 +#define SHPP_INNERRING 3 +#define SHPP_FIRSTRING 4 +#define SHPP_RING 5 + +/* -------------------------------------------------------------------- */ +/* SHPObject - represents on shape (without attributes) read */ +/* from the .shp file. */ +/* -------------------------------------------------------------------- */ +typedef struct +{ + int nSHPType; + + int nShapeId; /* -1 is unknown/unassigned */ + + int nParts; + int *panPartStart; + int *panPartType; + + int nVertices; + double *padfX; + double *padfY; + double *padfZ; + double *padfM; + + double dfXMin; + double dfYMin; + double dfZMin; + double dfMMin; + + double dfXMax; + double dfYMax; + double dfZMax; + double dfMMax; +} SHPObject; + +/* -------------------------------------------------------------------- */ +/* SHP API Prototypes */ +/* -------------------------------------------------------------------- */ +SHPHandle SHPOpen( const char * pszShapeFile, const char * pszAccess ); +SHPHandle SHPCreate( const char * pszShapeFile, int nShapeType ); +void SHPGetInfo( SHPHandle hSHP, int * pnEntities, int * pnShapeType, + double * padfMinBound, double * padfMaxBound ); + +SHPObject *SHPReadObject( SHPHandle hSHP, int iShape ); +int SHPWriteObject( SHPHandle hSHP, int iShape, SHPObject * psObject ); + +void SHPDestroyObject( SHPObject * psObject ); +void SHPComputeExtents( SHPObject * psObject ); +SHPObject *SHPCreateObject( int nSHPType, int nShapeId, + int nParts, int * panPartStart, int * panPartType, + int nVertices, double * padfX, double * padfY, + double * padfZ, double * padfM ); +SHPObject *SHPCreateSimpleObject( int nSHPType, int nVertices, + double * padfX, double * padfY, double * padfZ ); + +void SHPClose( SHPHandle hSHP ); + +const char *SHPTypeName( int nSHPType ); +const char *SHPPartTypeName( int nPartType ); + +/************************************************************************/ +/* DBF Support. */ +/************************************************************************/ +typedef struct +{ + FILE *fp; + + int nRecords; + + int nRecordLength; + int nHeaderLength; + int nFields; + int *panFieldOffset; + int *panFieldSize; + int *panFieldDecimals; + char *pachFieldType; + + char *pszHeader; + + int nCurrentRecord; + int bCurrentRecordModified; + char *pszCurrentRecord; + + int bNoHeader; + int bUpdated; +} DBFInfo; + +typedef DBFInfo * DBFHandle; + +typedef enum { + FTString, + FTInteger, + FTDouble, + FTInvalid +} DBFFieldType; + +DBFHandle DBFOpen( const char * pszDBFFile, const char * pszAccess ); +DBFHandle DBFCreate( const char * pszDBFFile ); + +int DBFGetFieldCount( DBFHandle psDBF ); +int DBFGetRecordCount( DBFHandle psDBF ); +int DBFAddField( DBFHandle hDBF, const char * pszFieldName, + DBFFieldType eType, int nWidth, int nDecimals ); + +DBFFieldType DBFGetFieldInfo( DBFHandle psDBF, int iField, + char * pszFieldName, + int * pnWidth, int * pnDecimals ); + +int DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField ); +double DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField ); +const char *DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField ); + +int DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, + int nFieldValue ); +int DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField, + double dFieldValue ); +int DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField, + const char * pszFieldValue ); + +void DBFClose( DBFHandle hDBF ); + +#ifdef __cplusplus +} +#endif + +#endif /* ndef _SHAPEFILE_H_INCLUDED */ diff --git a/src/Lib/shapelib/shapelib.html b/src/Lib/shapelib/shapelib.html new file mode 100644 index 00000000..f8bc67d2 --- /dev/null +++ b/src/Lib/shapelib/shapelib.html @@ -0,0 +1,212 @@ + + +Shapefile C Library V1.2 + + + +

Shapefile C Library V1.2

+ +

Purpose

+ +The Shapefile C Library provides the ability to write simple C programs +for reading, writing and updating (to a limited extent) ESRI Shapefiles, +and the associated attribute file (.dbf).

+ + +

Copyright

+ +The source for the Shapefile C Library is (c) 1998 Frank Warmerdam, +and released under the following conditions. The intent is that anyone +can do anything with the code, but that I do not assume any liability, nor +express any warranty for this code.

+ + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions:

+ +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software.

+ +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE.

+ + +

Manifest

+ +
    +
  • shapelib.html: This file - general documentation on the +Shapefile C Library.

    + +

  • shp_api.html: Documentation +for the API for accessing the .shp/.shx files.

    + +

  • dbf_api.html: Documentation +for the API for accessing the .dbf attribute files.

    + +

  • shpopen.c: C code for access to .shp/.shx vertex files.

    + +

  • dbfopen.c: C code for access to .dbf attribute file.

    + +

  • shapefil.h: Include file defining all the services of dbfopen.c +and shpopen.c.

    + +

  • dbfcreate.c: Simple example program for creating a new .dbf file. +

    + +

  • dbfadd.c: + Simple example program for adding a record to a .dbf file.

    + +

  • dbfdump.c: Simple example program for displaying the contents of + a .dbf file.

    + +

  • shpcreate.c: Simple example program for creating a new .shp and +.shx file.

    + +

  • shpadd.c: Simple example program for adding a shape to an existing + shape file.

    + +

  • shpdump.c: Simple program for dumping all the vertices in a + shapefile with an indicating of the parts.

    + +

  • shputils.c: Complex contributed program capable of clipping and + appending + shapefiles as well as a few other things. Type shputils + after building to get a full usage message.

    + +

  • Makefile: A simple makefile to compile the library and example + programs.

    + +

  • makeshape.sh: A simple script for running some of the example +programs.

    + +

  • shptest.c: A simple test harnass to generate each of the supported + types of shapefiles.

    + +

  • stream1.sh - A test script, which should produce stream1.out. +Note this will only work if you have the example data downloaded.

    + +

  • stream1.out: Expected output of stream1.sh test script.

    + +

  • stream2.sh: A test script, which should produce stream2.out.

    + +

  • stream2.out: Expected output of stream2.sh test script.

    +

+ +

What is a Shapefile?

+ +If you don't know, you probably don't need this library. The Shapefile +format is a new working and interchange format promulagated by ESRI +(http://www.esri.com/) for simple vector data with attributes. It is +apparently the only file format that can be edited in ARCView 2/3, and can +also be exported and imported in Arc/Info.

+ +An excellent white paper on the shapefile format is available from ESRI, +but it is .pdf format, so you will need Adobe Acrobat to browse it.

+ +The file format actually consists of three files.

+ +

+XXX.shp - holds the actual vertices.
+XXX.shx - hold index data pointing to the structures in the .shp file.
+XXX.dbf - holds the attributes in xBase (dBase) format.  
+
+ +

Release Notes

+ +Release 1.2.5: SHPOpen() now forcably uses "rb" or "r+b" access string +to avoid common mistakes on Windows.

+ +Release 1.2.4: DBFOpen() will now automatically translate a .shp +extension to .dbf for convenience. SHPOpen() will try datasets with lower +and uppercase extension. DBFAddField() now returns the field number, +not TRUE/FALSE.

+ +Release 1.2.3: Disable writing measures to multi-patches as ArcView +seems to puke on them (as reported by Monika Sester). Add white space +trimming, and string/numeric attribute interchangability in DBF API +as suggested by Steve Lime. Dbfdump was updated to include several +reporting options.

+ +Release 1.2.2: Added proper support for multipatch (reading and +writing) - this release just for testing purposes.

+ +Release 1.2 is mostly a rewrite of the .shp/.shx access API to account +for ArcView 3.x 3D shapes, and to encapsulate the shapes in a structure. +Existing code using the shapefile library will require substantial changes +to use release 1.2.

+ +Release V1.1 has been built on a number of platforms, and used by a +number of people successfully. V1.1 is the first release with the xBase API +documentation.

+ + +

Maintainer

+ +This library is maintained by me (Frank Warmerdam) on my own time. Please +send me bug patches and suggestions for the library. Email can be sent to +warmerda@home.com.

+ +The current status of the Shapelib code can be found somewhere off my +home page at http://members.home.com/warmerda.

+ +The shputils.c module was contributed by Bill Miller (NC-DOT) who can be +reached at bmiller@doh.dot.state.nc.us. I had to modify it substantially +to work with the 1.2 API, and I am not sure that it works as well as it +did when it was originally provided by Bill.

+ +

Portability

+ +The Shapefile C Library should port easily to 32bit systems with ANSI C +compilers. It should work on 64 bit architectures (such as the DEC AXP).

+ +Care should also be taken to pass the binary access flag into SHPOpen() +and DBFOpen() when operating on systems with special text file translation +such as MSDOS.

+ +The shputils.c module is contributed, and may not take the same approach +to portability as the rest of the package.

+ + +

Limitations

+ +
    + +
  • You can't modify the vertices of existing structures (though you + can update the attributes of existing structures, and create new + structures).

    + +

  • Not written in such a way as to be particularly fast. This is +particularly true of the 1.2 API. For applications more concerned with +speed it may be worth using the V1.1 API.

    + +

  • Doesn't set the last access time properly in the .dbf files.

    + +

  • There is no way to synchronize information to the file except to close it. +

    + +

  • Poor error checking and reporting.

    + +

  • Not professionally supported (well it can be, if you want to pay).

    + +

  • Some aspects of xBase files not supported, though I believe they are +not used by ESRI.

    + +

  • The application must keep the .dbf file in sync with the .shp/.shx +files through appropriate use of the DBF and SHP APIs.

    + +

+ + + + + + diff --git a/src/Lib/shapelib/shp_api.html b/src/Lib/shapelib/shp_api.html new file mode 100644 index 00000000..f2002520 --- /dev/null +++ b/src/Lib/shapelib/shp_api.html @@ -0,0 +1,351 @@ + + +.SHP File API + + + +

.SHP File API

+ +The .SHP API uses a SHPHandle to represent an open .shp/.shx file pair. +The contents of the SHPHandle are visible (see shapefile.h) but should +be ignored by the application. It is intended that all information be +accessed by the API functions.

+ + + +

Shape Types

+ +Shapes have types associated with them. The following is a list of the +different shapetypes supported by Shapefiles. At this time all shapes in +a Shapefile must be of the same type.

+ +

+  2D Shape Types (pre ArcView 3.x):
+
+  #define SHPT_POINT		1	Points
+  #define SHPT_ARC		3	Arcs (Polylines, possible in parts)
+  #define SHPT_POLYGON		5	Polygons (possible in parts)
+  #define SHPT_MULTIPOINT	8	MultiPoint (related points)
+
+  3D Shape Types (may include "measure" values for vertices):
+
+  #define SHPT_POINTZ		11	
+  #define SHPT_ARCZ		13
+  #define SHPT_POLYGONZ		15
+  #define SHPT_MULTIPOINTZ 	18
+
+  2D + Measure Types:
+
+  #define SHPT_POINTM		21
+  #define SHPT_ARCM		23
+  #define SHPT_POLYGONM		25
+  #define SHPT_MULTIPOINTM 	28
+
+  Complex (TIN-like) with Z, and Measure:
+
+  #define SHPT_MULTIPATCH 	31
+
+ + + +

SHPObject

+ +An individual shape is represented by the SHPObject structure. SHPObject's +created with SHPCreateObject(), SHPCreateSimpleObject(), or SHPReadObject() +should be disposed of with SHPDestroyObject().

+ +

+  typedef struct
+  {
+    int		nSHPType;	Shape Type (SHPT_* - see list above)
+
+    int		nShapeId; 	Shape Number (-1 is unknown/unassigned)
+
+    int		nParts;		# of Parts (0 implies single part with no info)
+    int		*panPartStart;  Start Vertex of part
+    int		*panPartType;	Part Type (SHPP_RING if not SHPT_MULTIPATCH)
+    
+    int		nVertices;	Vertex list 
+    double	*padfX;		
+    double	*padfY;
+    double	*padfZ;		(all zero if not provided)
+    double	*padfM;		(all zero if not provided)
+
+    double	dfXMin;		Bounds in X, Y, Z and M dimensions
+    double	dfYMin;
+    double	dfZMin;
+    double	dfMMin;
+
+    double	dfXMax;
+    double	dfYMax;
+    double	dfZMax;
+    double	dfMMax;
+  } SHPObject;
+
+ + + +

SHPOpen()

+ +
+SHPHandle SHPOpen( const char * pszShapeFile, const char * pszAccess );
+
+  pszShapeFile:		The name of the layer to access.  This can be the
+			name of either the .shp or the .shx file or can
+			just be the path plus the basename of the pair.
+
+  pszAccess:		The fopen() style access string.  At this time only
+			"rb" (read-only binary) and "rb+" (read/write binary) 
+		        should be used.
+
+ + The SHPOpen() function should be used to establish access to the two files + for accessing vertices (.shp and .shx). Note that both files have to + be in the indicated directory, and must have the expected extensions in + lower case. The returned SHPHandle is passed to other access functions, + and SHPClose() should be invoked to recover resources, and flush changes + to disk when complete.

+ + + +

SHPGetInfo()

+ +
+void SHPGetInfo( SHPHandle hSHP, int * pnEntities, int * pnShapeType,
+                 double * padfMinBound, double * padfMaxBound );
+
+  hSHP:			The handle previously returned by SHPOpen() 
+			or SHPCreate().
+
+  pnEntities:		A pointer to an integer into which the number of
+			entities/structures should be placed.  May be NULL.
+
+  pnShapetype:		A pointer to an integer into which the shapetype
+			of this file should be placed.  Shapefiles may contain
+			either SHPT_POINT, SHPT_ARC, SHPT_POLYGON or 
+			SHPT_MULTIPOINT entities.  This may be NULL.
+
+  padfMinBound:		The X, Y, Z and M minimum values will be placed into
+                        this four entry array.  This may be NULL.
+			
+  padfMaxBound:		The X, Y, Z and M maximum values will be placed into
+                        this four entry array.  This may be NULL.
+
+ + The SHPGetInfo() function retrieves various information about shapefile + as a whole. The bounds are read from the file header, and may be + inaccurate if the file was improperly generated.

+ + + +

SHPReadObject()

+ +
+SHPObject *SHPReadObject( SHPHandle hSHP, int iShape );
+
+  hSHP:			The handle previously returned by SHPOpen() 
+			or SHPCreate().
+
+  iShape:		The entity number of the shape to read.  Entity 
+			numbers are between 0 and nEntities-1 (as returned
+			by SHPGetInfo()).
+
+ + The SHPReadObject() call is used to read a single structure, or entity + from the shapefile. See the definition of the SHPObject structure for + detailed information on fields of a SHPObject. SHPObject's returned from + SHPReadObject() should be deallocated with SHPDestroyShape(). + SHPReadObject() will return NULL if an illegal iShape value is requested.

+ + Note that the bounds placed into the SHPObject are those read from the + file, and may not be correct. For points the bounds are generated from + the single point since bounds aren't normally provided for point types.

+ + + +

SHPClose()

+ +
+void	SHPClose( SHPHandle hSHP );
+
+  hSHP:			The handle previously returned by SHPOpen() 
+			or SHPCreate().
+
+ + The SHPClose() function will close the .shp and .shx files, and flush + all outstanding header information to the files. It will also recover + resources associated with the handle. After this call the hSHP handle + cannot be used again.

+ + + +

SHPCreate()

+ +
+SHPHandle SHPCreate( const char * pszShapeFile, int nShapeType );
+
+  pszShapeFile:		The name of the layer to access.  This can be the
+			name of either the .shp or the .shx file or can
+			just be the path plus the basename of the pair.
+
+  nShapeType:		The type of shapes to be stored in the newly created
+			file.  It may be either SHPT_POINT, SHPT_ARC, 
+		        SHPT_POLYGON or SHPT_MULTIPOINT.
+
+ + The SHPCreate() function will create a new .shp and .shx file of the + desired type.

+ + + +

SHPCreateSimpleObject()

+ +
+SHPObject * 
+     SHPCreateSimpleObject( int nSHPType, int nVertices, 
+			    double *padfX, double * padfY, double *padfZ, );
+
+  nSHPType:		The SHPT_ type of the object to be created, such
+                        as SHPT_POINT, or SHPT_POLYGON.
+  
+  nVertices:		The number of vertices being passed in padfX,    
+                        padfY, and padfZ. 
+
+  padfX:		An array of nVertices X coordinates of the vertices
+                        for this object.
+
+  padfY:		An array of nVertices Y coordinates of the vertices
+                        for this object.
+
+  padfZ:		An array of nVertices Z coordinates of the vertices
+                        for this object.  This may be NULL in which case
+		        they are all assumed to be zero.
+
+ + The SHPCreateSimpleObject() allows for the convenient creation of + simple objects. This is normally used so that the SHPObject can be + passed to SHPWriteObject() to write it to the file. The simple object + creation API assumes an M (measure) value of zero for each vertex. For + complex objects (such as polygons) it is assumed that there is only one + part, and that it is of the default type (SHPP_RING).

+ + Use the SHPCreateObject() function for more sophisticated objects. The + SHPDestroyObject() function should be used to free resources associated with + an object allocated with SHPCreateSimpleObject().

+ + This function computes a bounding box for the SHPObject from the given + vertices.

+ + + +

SHPCreateObject()

+ +
+SHPObject * 
+     SHPCreateObject( int nSHPType, int iShape,
+                      int nParts, int * panPartStart, int * panPartType,
+                      int nVertices, double *padfX, double * padfY, 
+                      double *padfZ, double *padfM );
+
+  nSHPType:		The SHPT_ type of the object to be created, such
+                        as SHPT_POINT, or SHPT_POLYGON.
+
+  iShape:		The shapeid to be recorded with this shape.
+
+  nParts:		The number of parts for this object.  If this is
+                        zero for ARC, or POLYGON type objects, a single 
+                        zero valued part will be created internally.
+  
+  panPartStart:		The list of zero based start vertices for the rings
+                        (parts) in this object.  The first should always be
+                        zero.  This may be NULL if nParts is 0.
+  
+  panPartType:		The type of each of the parts.  This is only meaningful
+                        for MULTIPATCH files.  For all other cases this may
+                        be NULL, and will be assumed to be SHPP_RING.
+  
+  nVertices:		The number of vertices being passed in padfX,    
+                        padfY, and padfZ. 
+
+  padfX:		An array of nVertices X coordinates of the vertices
+                        for this object.
+
+  padfY:		An array of nVertices Y coordinates of the vertices
+                        for this object.
+
+  padfZ:		An array of nVertices Z coordinates of the vertices
+                        for this object.  This may be NULL in which case
+		        they are all assumed to be zero.
+
+  padfM:		An array of nVertices M (measure values) of the 
+			vertices for this object.  This may be NULL in which 
+			case they are all assumed to be zero.
+
+ + The SHPCreateSimpleObject() allows for the creation of objects (shapes). + This is normally used so that the SHPObject can be passed to + SHPWriteObject() to write it to the file.

+ + The SHPDestroyObject() function should be used to free resources associated + with an object allocated with SHPCreateObject().

+ + This function computes a bounding box for the SHPObject from the given + vertices.

+ + + +

SHPComputeExtents()

+ +
+void SHPComputeExtents( SHPObject * psObject );
+
+  psObject:		An existing shape object to be updated in place.
+
+ + This function will recompute the extents of this shape, replacing the + existing values of the dfXMin, dfYMin, dfZMin, dfMMin, dfXMax, dfYMax, + dfZMax, and dfMMax values based on the current set of vertices for the + shape. This function is automatically called by SHPCreateObject() but + if the vertices of an existing object are altered it should be called again + to fix up the extents.

+ + + +

SHPWriteObject()

+ +
+int SHPWriteObject( SHPHandle hSHP, int iShape, SHPObject *psObject );
+
+  hSHP:			The handle previously returned by SHPOpen("r+") 
+			or SHPCreate().
+
+  iShape:		The entity number of the shape to write.  A value of
+		        -1 should be used for new shapes.  
+
+  psObject:		The shape to write to the file. This should have
+                        been created with SHPCreateObject(), or 
+                        SHPCreateSimpleObject().
+
+ + The SHPWriteObject() call is used to write a single structure, or entity + to the shapefile. See the definition of the SHPObject structure for + detailed information on fields of a SHPObject. The return value is the + entity number of the written shape.

+ + + +

SHPDestroyObject()

+ +
+void SHPDestroyObject( SHPObject *psObject );
+
+  psObject:		The object to deallocate.
+
+ + This function should be used to deallocate the resources associated with + a SHPObject when it is no longer needed, including those created with + SHPCreateSimpleObject(), SHPCreateObject() and returned from SHPReadObject(). +

+ + + diff --git a/src/Lib/shapelib/shpadd.c b/src/Lib/shapelib/shpadd.c new file mode 100644 index 00000000..1cddc2a4 --- /dev/null +++ b/src/Lib/shapelib/shpadd.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 1995 Frank Warmerdam + * + * This code is in the public domain. + * + * $Log$ + * Revision 1.1 2000/02/09 19:51:46 curt + * Initial revision + * + * Revision 1.1 1999/08/24 21:13:01 curt + * Initial revision. + * + * Revision 1.8 1998/12/03 16:36:26 warmerda + * Use r+b rather than rb+ for binary access. + * + * Revision 1.7 1998/11/09 20:57:04 warmerda + * Fixed SHPGetInfo() call. + * + * Revision 1.6 1998/11/09 20:19:16 warmerda + * Changed to use SHPObject based API. + * + * Revision 1.5 1997/03/06 14:05:02 warmerda + * fixed typo. + * + * Revision 1.4 1997/03/06 14:01:16 warmerda + * added memory allocation checking, and free()s. + * + * Revision 1.3 1995/10/21 03:14:37 warmerda + * Changed to use binary file access + * + * Revision 1.2 1995/08/04 03:18:01 warmerda + * Added header. + * + */ + +static char rcsid[] = + "$Id$"; + +#include "shapefil.h" + +int main( int argc, char ** argv ) + +{ + SHPHandle hSHP; + int nShapeType, nVertices, nParts, *panParts, i; + double *padfX, *padfY; + SHPObject *psObject; + +/* -------------------------------------------------------------------- */ +/* Display a usage message. */ +/* -------------------------------------------------------------------- */ + if( argc < 4 ) + { + printf( "shpadd shp_file [[x y] [+]]*\n" ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Open the passed shapefile. */ +/* -------------------------------------------------------------------- */ + hSHP = SHPOpen( argv[1], "r+b" ); + + if( hSHP == NULL ) + { + printf( "Unable to open:%s\n", argv[1] ); + exit( 1 ); + } + + SHPGetInfo( hSHP, NULL, &nShapeType, NULL, NULL ); + +/* -------------------------------------------------------------------- */ +/* Build a vertex/part list from the command line arguments. */ +/* -------------------------------------------------------------------- */ + padfX = (double *) malloc(sizeof(double) * 1000); + padfY = (double *) malloc(sizeof(double) * 1000); + + nVertices = 0; + + if( (panParts = (int *) malloc(sizeof(int) * 1000 )) == NULL ) + { + printf( "Out of memory\n" ); + exit( 1 ); + } + + nParts = 1; + panParts[0] = 0; + + for( i = 2; i < argc; ) + { + if( argv[i][0] == '+' ) + { + panParts[nParts++] = nVertices; + i++; + } + else if( i < argc-1 ) + { + sscanf( argv[i], "%lg", padfX+nVertices ); + sscanf( argv[i+1], "%lg", padfY+nVertices ); + nVertices += 1; + i += 2; + } + } + +/* -------------------------------------------------------------------- */ +/* Write the new entity to the shape file. */ +/* -------------------------------------------------------------------- */ + psObject = SHPCreateObject( nShapeType, -1, nParts, panParts, NULL, + nVertices, padfX, padfY, NULL, NULL ); + SHPWriteObject( hSHP, -1, psObject ); + SHPDestroyObject( psObject ); + + SHPClose( hSHP ); + + free( panParts ); + free( padfX ); + free( padfY ); +} diff --git a/src/Lib/shapelib/shpcreate.c b/src/Lib/shapelib/shpcreate.c new file mode 100644 index 00000000..c001a977 --- /dev/null +++ b/src/Lib/shapelib/shpcreate.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1995 Frank Warmerdam + * + * This code is in the public domain. + * + * $Log$ + * Revision 1.1 2000/02/09 19:51:46 curt + * Initial revision + * + * Revision 1.1 1999/08/24 21:13:01 curt + * Initial revision. + * + * Revision 1.2 1995/08/04 03:16:43 warmerda + * Added header. + * + */ + +static char rcsid[] = + "$Id$"; + +#include "shapefil.h" + +int main( int argc, char ** argv ) + +{ + SHPHandle hSHP; + int nShapeType; + double *padVertices; + +/* -------------------------------------------------------------------- */ +/* Display a usage message. */ +/* -------------------------------------------------------------------- */ + if( argc != 3 ) + { + printf( "shpcreate shp_file [point/arc/polygon/multipoint]\n" ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Figure out the shape type. */ +/* -------------------------------------------------------------------- */ + if( strcmp(argv[2],"POINT") == 0 || strcmp(argv[2],"point") == 0 ) + nShapeType = SHPT_POINT; + else if( strcmp(argv[2],"ARC") == 0 || strcmp(argv[2],"arc") == 0 ) + nShapeType = SHPT_ARC; + else if( strcmp(argv[2],"POLYGON") == 0 || strcmp(argv[2],"polygon") == 0 ) + nShapeType = SHPT_POLYGON; + else if( strcmp(argv[2],"MULTIPOINT")==0 ||strcmp(argv[2],"multipoint")==0) + nShapeType = SHPT_MULTIPOINT; + else + { + printf( "Shape Type `%s' not recognised.\n", argv[2] ); + exit( 2 ); + } + +/* -------------------------------------------------------------------- */ +/* Create the requested layer. */ +/* -------------------------------------------------------------------- */ + hSHP = SHPCreate( argv[1], nShapeType ); + + if( hSHP == NULL ) + { + printf( "Unable to create:%s\n", argv[1] ); + exit( 3 ); + } + + SHPClose( hSHP ); +} diff --git a/src/Lib/shapelib/shpdump.c b/src/Lib/shapelib/shpdump.c new file mode 100644 index 00000000..ba2c311f --- /dev/null +++ b/src/Lib/shapelib/shpdump.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 1995 Frank Warmerdam + * + * This code is in the public domain. + * + * $Log$ + * Revision 1.1 2000/02/09 19:51:46 curt + * Initial revision + * + * Revision 1.1 1999/08/24 21:13:01 curt + * Initial revision. + * + * Revision 1.6 1998/12/03 15:48:48 warmerda + * Added report of shapefile type, and total number of shapes. + * + * Revision 1.5 1998/11/09 20:57:36 warmerda + * use SHPObject. + * + * Revision 1.4 1995/10/21 03:14:49 warmerda + * Changed to use binary file access. + * + * Revision 1.3 1995/08/23 02:25:25 warmerda + * Added support for bounds. + * + * Revision 1.2 1995/08/04 03:18:11 warmerda + * Added header. + * + */ + +static char rcsid[] = + "$Id$"; + +#include "shapefil.h" + +int main( int argc, char ** argv ) + +{ + SHPHandle hSHP; + int nShapeType, nEntities, i, iPart; + const char *pszPlus; + double adfMinBound[4], adfMaxBound[4]; + +/* -------------------------------------------------------------------- */ +/* Display a usage message. */ +/* -------------------------------------------------------------------- */ + if( argc != 2 ) + { + printf( "shpdump shp_file\n" ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Open the passed shapefile. */ +/* -------------------------------------------------------------------- */ + hSHP = SHPOpen( argv[1], "rb" ); + + if( hSHP == NULL ) + { + printf( "Unable to open:%s\n", argv[1] ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Print out the file bounds. */ +/* -------------------------------------------------------------------- */ + SHPGetInfo( hSHP, &nEntities, &nShapeType, adfMinBound, adfMaxBound ); + + printf( "Shapefile Type: %s # of Shapes: %d\n\n", + SHPTypeName( nShapeType ), nEntities ); + + printf( "File Bounds: (%12.3f,%12.3f,%lg,%lg)\n" + " to (%12.3f,%12.3f,%lg,%lg)\n", + adfMinBound[0], + adfMinBound[1], + adfMinBound[2], + adfMinBound[3], + adfMaxBound[0], + adfMaxBound[1], + adfMaxBound[2], + adfMaxBound[3] ); + +/* -------------------------------------------------------------------- */ +/* Skim over the list of shapes, printing all the vertices. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nEntities; i++ ) + { + int j; + SHPObject *psShape; + + psShape = SHPReadObject( hSHP, i ); + + printf( "\nShape:%d (%s) nVertices=%d, nParts=%d\n" + " Bounds:(%12.3f,%12.3f, %lg, %lg)\n" + " to (%12.3f,%12.3f, %lg, %lg)\n", + i, SHPTypeName(psShape->nSHPType), + psShape->nVertices, psShape->nParts, + psShape->dfXMin, psShape->dfYMin, + psShape->dfZMin, psShape->dfMMin, + psShape->dfXMax, psShape->dfYMax, + psShape->dfZMax, psShape->dfMMax ); + + for( j = 0, iPart = 1; j < psShape->nVertices; j++ ) + { + const char *pszPartType = ""; + + if( j == 0 && psShape->nParts > 0 ) + pszPartType = SHPPartTypeName( psShape->panPartType[0] ); + + if( iPart < psShape->nParts + && psShape->panPartStart[iPart] == j ) + { + pszPartType = SHPPartTypeName( psShape->panPartType[iPart] ); + iPart++; + pszPlus = "+"; + } + else + pszPlus = " "; + + printf(" %s (%12.3f,%12.3f, %lg, %lg) %s \n", + pszPlus, + psShape->padfX[j], + psShape->padfY[j], + psShape->padfZ[j], + psShape->padfM[j], + pszPartType ); + } + + SHPDestroyObject( psShape ); + } + + SHPClose( hSHP ); + +#ifdef USE_DBMALLOC + malloc_dump(2); +#endif + + exit( 0 ); +} diff --git a/src/Lib/shapelib/shpopen.c b/src/Lib/shapelib/shpopen.c new file mode 100644 index 00000000..ad652e21 --- /dev/null +++ b/src/Lib/shapelib/shpopen.c @@ -0,0 +1,1562 @@ +/****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log$ + * Revision 1.1 2000/02/09 19:51:46 curt + * Initial revision + * + * Revision 1.1 1999/08/24 21:13:01 curt + * Initial revision. + * + * Revision 1.20 1999/04/19 21:04:17 warmerda + * Fixed syntax error. + * + * Revision 1.19 1999/04/19 21:01:57 warmerda + * Force access string to binary in SHPOpen(). + * + * Revision 1.18 1999/04/01 18:48:07 warmerda + * Try upper case extensions if lower case doesn't work. + * + * Revision 1.17 1998/12/31 15:29:39 warmerda + * Disable writing measure values to multipatch objects if + * DISABLE_MULTIPATCH_MEASURE is defined. + * + * Revision 1.16 1998/12/16 05:14:33 warmerda + * Added support to write MULTIPATCH. Fixed reading Z coordinate of + * MULTIPATCH. Fixed record size written for all feature types. + * + * Revision 1.15 1998/12/03 16:35:29 warmerda + * r+b is proper binary access string, not rb+. + * + * Revision 1.14 1998/12/03 15:47:56 warmerda + * Fixed setting of nVertices in SHPCreateObject(). + * + * Revision 1.13 1998/12/03 15:33:54 warmerda + * Made SHPCalculateExtents() separately callable. + * + * Revision 1.12 1998/11/11 20:01:50 warmerda + * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines. + * + * Revision 1.11 1998/11/09 20:56:44 warmerda + * Fixed up handling of file wide bounds. + * + * Revision 1.10 1998/11/09 20:18:51 warmerda + * Converted to support 3D shapefiles, and use of SHPObject. + * + * Revision 1.9 1998/02/24 15:09:05 warmerda + * Fixed memory leak. + * + * Revision 1.8 1997/12/04 15:40:29 warmerda + * Fixed byte swapping of record number, and record length fields in the + * .shp file. + * + * Revision 1.7 1995/10/21 03:15:58 warmerda + * Added support for binary file access, the magic cookie 9997 + * and tried to improve the int32 selection logic for 16bit systems. + * + * Revision 1.6 1995/09/04 04:19:41 warmerda + * Added fix for file bounds. + * + * Revision 1.5 1995/08/25 15:16:44 warmerda + * Fixed a couple of problems with big endian systems ... one with bounds + * and the other with multipart polygons. + * + * Revision 1.4 1995/08/24 18:10:17 warmerda + * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc() + * functions (such as on the Sun). + * + * Revision 1.3 1995/08/23 02:23:15 warmerda + * Added support for reading bounds, and fixed up problems in setting the + * file wide bounds. + * + * Revision 1.2 1995/08/04 03:16:57 warmerda + * Added header. + * + */ + +static char rcsid[] = + "$Id$"; + +#include "shapefil.h" + +#include +#include +#include + +typedef unsigned char uchar; + +#if UINT_MAX == 65535 +typedef long int32; +#else +typedef int int32; +#endif + +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif + +#define ByteCopy( a, b, c ) memcpy( b, a, c ) +#ifndef MAX +# define MIN(a,b) ((ab) ? a : b) +#endif + +static int bBigEndian; + +/************************************************************************/ +/* SwapWord() */ +/* */ +/* Swap a 2, 4 or 8 byte word. */ +/************************************************************************/ + +static void SwapWord( int length, void * wordP ) + +{ + int i; + uchar temp; + + for( i=0; i < length/2; i++ ) + { + temp = ((uchar *) wordP)[i]; + ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1]; + ((uchar *) wordP)[length-i-1] = temp; + } +} + +/************************************************************************/ +/* SfRealloc() */ +/* */ +/* A realloc cover function that will access a NULL pointer as */ +/* a valid input. */ +/************************************************************************/ + +static void * SfRealloc( void * pMem, int nNewSize ) + +{ + if( pMem == NULL ) + return( (void *) malloc(nNewSize) ); + else + return( (void *) realloc(pMem,nNewSize) ); +} + +/************************************************************************/ +/* SHPWriteHeader() */ +/* */ +/* Write out a header for the .shp and .shx files as well as the */ +/* contents of the index (.shx) file. */ +/************************************************************************/ + +static void SHPWriteHeader( SHPHandle psSHP ) + +{ + uchar abyHeader[100]; + int i; + int32 i32; + double dValue; + int32 *panSHX; + +/* -------------------------------------------------------------------- */ +/* Prepare header block for .shp file. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < 100; i++ ) + abyHeader[i] = 0; + + abyHeader[2] = 0x27; /* magic cookie */ + abyHeader[3] = 0x0a; + + i32 = psSHP->nFileSize/2; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + i32 = 1000; /* version */ + ByteCopy( &i32, abyHeader+28, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+28 ); + + i32 = psSHP->nShapeType; /* shape type */ + ByteCopy( &i32, abyHeader+32, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+32 ); + + dValue = psSHP->adBoundsMin[0]; /* set bounds */ + ByteCopy( &dValue, abyHeader+36, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+36 ); + + dValue = psSHP->adBoundsMin[1]; + ByteCopy( &dValue, abyHeader+44, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+44 ); + + dValue = psSHP->adBoundsMax[0]; + ByteCopy( &dValue, abyHeader+52, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+52 ); + + dValue = psSHP->adBoundsMax[1]; + ByteCopy( &dValue, abyHeader+60, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+60 ); + + dValue = psSHP->adBoundsMin[2]; /* z */ + ByteCopy( &dValue, abyHeader+68, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+68 ); + + dValue = psSHP->adBoundsMax[2]; + ByteCopy( &dValue, abyHeader+76, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+76 ); + + dValue = psSHP->adBoundsMin[3]; /* m */ + ByteCopy( &dValue, abyHeader+84, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+84 ); + + dValue = psSHP->adBoundsMax[3]; + ByteCopy( &dValue, abyHeader+92, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+92 ); + +/* -------------------------------------------------------------------- */ +/* Write .shp file header. */ +/* -------------------------------------------------------------------- */ + fseek( psSHP->fpSHP, 0, 0 ); + fwrite( abyHeader, 100, 1, psSHP->fpSHP ); + +/* -------------------------------------------------------------------- */ +/* Prepare, and write .shx file header. */ +/* -------------------------------------------------------------------- */ + i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + fseek( psSHP->fpSHX, 0, 0 ); + fwrite( abyHeader, 100, 1, psSHP->fpSHX ); + +/* -------------------------------------------------------------------- */ +/* Write out the .shx contents. */ +/* -------------------------------------------------------------------- */ + panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords); + + for( i = 0; i < psSHP->nRecords; i++ ) + { + panSHX[i*2 ] = psSHP->panRecOffset[i]/2; + panSHX[i*2+1] = psSHP->panRecSize[i]/2; + if( !bBigEndian ) SwapWord( 4, panSHX+i*2 ); + if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 ); + } + + fwrite( panSHX, sizeof(int32) * 2, psSHP->nRecords, psSHP->fpSHX ); + + free( panSHX ); +} + +/************************************************************************/ +/* SHPOpen() */ +/* */ +/* Open the .shp and .shx files based on the basename of the */ +/* files or either file name. */ +/************************************************************************/ + +SHPHandle SHPOpen( const char * pszLayer, const char * pszAccess ) + +{ + char *pszFullname, *pszBasename; + SHPHandle psSHP; + + uchar *pabyBuf; + int iField, i; + double dValue; + +/* -------------------------------------------------------------------- */ +/* Ensure the access string is one of the legal ones. We */ +/* ensure the result string indicates binary to avoid common */ +/* problems on Windows. */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 ) + pszAccess = "r+b"; + else + pszAccess = "rb"; + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this machine. */ +/* -------------------------------------------------------------------- */ + i = 1; + if( *((uchar *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; + +/* -------------------------------------------------------------------- */ +/* Initialize the info structure. */ +/* -------------------------------------------------------------------- */ + psSHP = (SHPHandle) malloc(sizeof(SHPInfo)); + + psSHP->bUpdated = FALSE; + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszLayer)+5); + strcpy( pszBasename, pszLayer ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Open the .shp and .shx files. Note that files pulled from */ +/* a PC to Unix with upper case filenames won't work! */ +/* -------------------------------------------------------------------- */ + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.shp", pszBasename ); + psSHP->fpSHP = fopen(pszFullname, pszAccess ); + if( psSHP->fpSHP == NULL ) + { + sprintf( pszFullname, "%s.SHP", pszBasename ); + psSHP->fpSHP = fopen(pszFullname, pszAccess ); + } + + if( psSHP->fpSHP == NULL ) + return( NULL ); + + sprintf( pszFullname, "%s.shx", pszBasename ); + psSHP->fpSHX = fopen(pszFullname, pszAccess ); + if( psSHP->fpSHX == NULL ) + { + sprintf( pszFullname, "%s.SHX", pszBasename ); + psSHP->fpSHX = fopen(pszFullname, pszAccess ); + } + + if( psSHP->fpSHX == NULL ) + return( NULL ); + + free( pszFullname ); + free( pszBasename ); + +/* -------------------------------------------------------------------- */ +/* Read the file size from the SHP file. */ +/* -------------------------------------------------------------------- */ + pabyBuf = (uchar *) malloc(100); + fread( pabyBuf, 100, 1, psSHP->fpSHP ); + + psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256 + + pabyBuf[25] * 256 * 256 + + pabyBuf[26] * 256 + + pabyBuf[27]) * 2; + +/* -------------------------------------------------------------------- */ +/* Read SHX file Header info */ +/* -------------------------------------------------------------------- */ + fread( pabyBuf, 100, 1, psSHP->fpSHX ); + + if( pabyBuf[0] != 0 + || pabyBuf[1] != 0 + || pabyBuf[2] != 0x27 + || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) ) + { + fclose( psSHP->fpSHP ); + fclose( psSHP->fpSHX ); + free( psSHP ); + + return( NULL ); + } + + psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256 + + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256; + psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8; + + psSHP->nShapeType = pabyBuf[32]; + +/* -------------------------------------------------------------------- */ +/* Read the bounds. */ +/* -------------------------------------------------------------------- */ + if( bBigEndian ) SwapWord( 8, pabyBuf+36 ); + memcpy( &dValue, pabyBuf+36, 8 ); + psSHP->adBoundsMin[0] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+44 ); + memcpy( &dValue, pabyBuf+44, 8 ); + psSHP->adBoundsMin[1] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+52 ); + memcpy( &dValue, pabyBuf+52, 8 ); + psSHP->adBoundsMax[0] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+60 ); + memcpy( &dValue, pabyBuf+60, 8 ); + psSHP->adBoundsMax[1] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */ + memcpy( &dValue, pabyBuf+68, 8 ); + psSHP->adBoundsMin[2] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+76 ); + memcpy( &dValue, pabyBuf+76, 8 ); + psSHP->adBoundsMax[2] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */ + memcpy( &dValue, pabyBuf+84, 8 ); + psSHP->adBoundsMin[3] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+92 ); + memcpy( &dValue, pabyBuf+92, 8 ); + psSHP->adBoundsMax[3] = dValue; + + free( pabyBuf ); + +/* -------------------------------------------------------------------- */ +/* Read the .shx file to get the offsets to each record in */ +/* the .shp file. */ +/* -------------------------------------------------------------------- */ + psSHP->nMaxRecords = psSHP->nRecords; + + psSHP->panRecOffset = + (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); + psSHP->panRecSize = + (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); + + pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) ); + fread( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ); + + for( i = 0; i < psSHP->nRecords; i++ ) + { + int32 nOffset, nLength; + + memcpy( &nOffset, pabyBuf + i * 8, 4 ); + if( !bBigEndian ) SwapWord( 4, &nOffset ); + + memcpy( &nLength, pabyBuf + i * 8 + 4, 4 ); + if( !bBigEndian ) SwapWord( 4, &nLength ); + + psSHP->panRecOffset[i] = nOffset*2; + psSHP->panRecSize[i] = nLength*2; + } + free( pabyBuf ); + + return( psSHP ); +} + +/************************************************************************/ +/* SHPClose() */ +/* */ +/* Close the .shp and .shx files. */ +/************************************************************************/ + +void SHPClose(SHPHandle psSHP ) + +{ +/* -------------------------------------------------------------------- */ +/* Update the header if we have modified anything. */ +/* -------------------------------------------------------------------- */ + if( psSHP->bUpdated ) + { + SHPWriteHeader( psSHP ); + } + +/* -------------------------------------------------------------------- */ +/* Free all resources, and close files. */ +/* -------------------------------------------------------------------- */ + free( psSHP->panRecOffset ); + free( psSHP->panRecSize ); + + fclose( psSHP->fpSHX ); + fclose( psSHP->fpSHP ); + + free( psSHP ); +} + +/************************************************************************/ +/* SHPGetInfo() */ +/* */ +/* Fetch general information about the shape file. */ +/************************************************************************/ + +void SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, + double * padfMinBound, double * padfMaxBound ) + +{ + int i; + + if( pnEntities != NULL ) + *pnEntities = psSHP->nRecords; + + if( pnShapeType != NULL ) + *pnShapeType = psSHP->nShapeType; + + for( i = 0; i < 4; i++ ) + { + if( padfMinBound != NULL ) + padfMinBound[i] = psSHP->adBoundsMin[i]; + if( padfMaxBound != NULL ) + padfMaxBound[i] = psSHP->adBoundsMax[i]; + } +} + +/************************************************************************/ +/* SHPCreate() */ +/* */ +/* Create a new shape file and return a handle to the open */ +/* shape file with read/write access. */ +/************************************************************************/ + +SHPHandle SHPCreate( const char * pszLayer, int nShapeType ) + +{ + char *pszBasename, *pszFullname; + int i; + FILE *fpSHP, *fpSHX; + uchar abyHeader[100]; + int32 i32; + double dValue; + int32 *panSHX; + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this system. */ +/* -------------------------------------------------------------------- */ + i = 1; + if( *((uchar *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszLayer)+5); + strcpy( pszBasename, pszLayer ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Open the two files so we can write their headers. */ +/* -------------------------------------------------------------------- */ + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.shp", pszBasename ); + fpSHP = fopen(pszFullname, "wb" ); + if( fpSHP == NULL ) + return( NULL ); + + sprintf( pszFullname, "%s.shx", pszBasename ); + fpSHX = fopen(pszFullname, "wb" ); + if( fpSHX == NULL ) + return( NULL ); + + free( pszFullname ); + free( pszBasename ); + +/* -------------------------------------------------------------------- */ +/* Prepare header block for .shp file. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < 100; i++ ) + abyHeader[i] = 0; + + abyHeader[2] = 0x27; /* magic cookie */ + abyHeader[3] = 0x0a; + + i32 = 50; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + i32 = 1000; /* version */ + ByteCopy( &i32, abyHeader+28, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+28 ); + + i32 = nShapeType; /* shape type */ + ByteCopy( &i32, abyHeader+32, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+32 ); + + dValue = 0.0; /* set bounds */ + ByteCopy( &dValue, abyHeader+36, 8 ); + ByteCopy( &dValue, abyHeader+44, 8 ); + ByteCopy( &dValue, abyHeader+52, 8 ); + ByteCopy( &dValue, abyHeader+60, 8 ); + +/* -------------------------------------------------------------------- */ +/* Write .shp file header. */ +/* -------------------------------------------------------------------- */ + fwrite( abyHeader, 100, 1, fpSHP ); + +/* -------------------------------------------------------------------- */ +/* Prepare, and write .shx file header. */ +/* -------------------------------------------------------------------- */ + i32 = 50; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + fwrite( abyHeader, 100, 1, fpSHX ); + +/* -------------------------------------------------------------------- */ +/* Close the files, and then open them as regular existing files. */ +/* -------------------------------------------------------------------- */ + fclose( fpSHP ); + fclose( fpSHX ); + + return( SHPOpen( pszLayer, "r+b" ) ); +} + +/************************************************************************/ +/* _SHPSetBounds() */ +/* */ +/* Compute a bounds rectangle for a shape, and set it into the */ +/* indicated location in the record. */ +/************************************************************************/ + +static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape ) + +{ + ByteCopy( &(psShape->dfXMin), pabyRec + 0, 8 ); + ByteCopy( &(psShape->dfYMin), pabyRec + 8, 8 ); + ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 ); + ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 ); + + if( bBigEndian ) + { + SwapWord( 8, pabyRec + 0 ); + SwapWord( 8, pabyRec + 8 ); + SwapWord( 8, pabyRec + 16 ); + SwapWord( 8, pabyRec + 24 ); + } +} + +/************************************************************************/ +/* SHPComputeExtents() */ +/* */ +/* Recompute the extents of a shape. Automatically done by */ +/* SHPCreateObject(). */ +/************************************************************************/ + +void SHPComputeExtents( SHPObject * psObject ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Build extents for this object. */ +/* -------------------------------------------------------------------- */ + if( psObject->nVertices > 0 ) + { + psObject->dfXMin = psObject->dfXMax = psObject->padfX[0]; + psObject->dfYMin = psObject->dfYMax = psObject->padfY[0]; + psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0]; + psObject->dfMMin = psObject->dfMMax = psObject->padfM[0]; + } + + for( i = 0; i < psObject->nVertices; i++ ) + { + psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]); + psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]); + psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]); + psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]); + + psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]); + psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]); + psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]); + psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]); + } +} + +/************************************************************************/ +/* SHPCreateObject() */ +/* */ +/* Create a shape object. It should be freed with */ +/* SHPDestroyObject(). */ +/************************************************************************/ + +SHPObject *SHPCreateObject( int nSHPType, int nShapeId, int nParts, + int * panPartStart, int * panPartType, + int nVertices, double * padfX, double * padfY, + double * padfZ, double * padfM ) + +{ + SHPObject *psObject; + int i, bHasM, bHasZ; + + psObject = (SHPObject *) calloc(1,sizeof(SHPObject)); + psObject->nSHPType = nSHPType; + psObject->nShapeId = nShapeId; + +/* -------------------------------------------------------------------- */ +/* Establish whether this shape type has M, and Z values. */ +/* -------------------------------------------------------------------- */ + if( nSHPType == SHPT_ARCM + || nSHPType == SHPT_POINTM + || nSHPType == SHPT_POLYGONM + || nSHPType == SHPT_MULTIPOINTM ) + { + bHasM = TRUE; + bHasZ = FALSE; + } + else if( nSHPType == SHPT_ARCZ + || nSHPType == SHPT_POINTZ + || nSHPType == SHPT_POLYGONZ + || nSHPType == SHPT_MULTIPOINTZ + || nSHPType == SHPT_MULTIPATCH ) + { + bHasM = TRUE; + bHasZ = TRUE; + } + else + { + bHasM = FALSE; + bHasZ = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Capture parts. Note that part type is optional, and */ +/* defaults to ring. */ +/* -------------------------------------------------------------------- */ + if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON + || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM + || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ + || nSHPType == SHPT_MULTIPATCH ) + { + psObject->nParts = MAX(1,nParts); + + psObject->panPartStart = (int *) + malloc(sizeof(int) * psObject->nParts); + psObject->panPartType = (int *) + malloc(sizeof(int) * psObject->nParts); + + psObject->panPartStart[0] = 0; + psObject->panPartType[0] = SHPP_RING; + + for( i = 0; i < nParts; i++ ) + { + psObject->panPartStart[i] = panPartStart[i]; + if( panPartType != NULL ) + psObject->panPartType[i] = panPartType[i]; + else + psObject->panPartType[i] = SHPP_RING; + } + } + +/* -------------------------------------------------------------------- */ +/* Capture vertices. Note that Z and M are optional, but X and */ +/* Y are not. */ +/* -------------------------------------------------------------------- */ + psObject->padfX = (double *) calloc(sizeof(double),nVertices); + psObject->padfY = (double *) calloc(sizeof(double),nVertices); + psObject->padfZ = (double *) calloc(sizeof(double),nVertices); + psObject->padfM = (double *) calloc(sizeof(double),nVertices); + + assert( padfX != NULL ); + assert( padfY != NULL ); + + for( i = 0; i < nVertices; i++ ) + { + psObject->padfX[i] = padfX[i]; + psObject->padfY[i] = padfY[i]; + if( padfZ != NULL && bHasZ ) + psObject->padfZ[i] = padfZ[i]; + if( padfM != NULL && bHasM ) + psObject->padfM[i] = padfM[i]; + } + +/* -------------------------------------------------------------------- */ +/* Compute the extents. */ +/* -------------------------------------------------------------------- */ + psObject->nVertices = nVertices; + + SHPComputeExtents( psObject ); + + return( psObject ); +} + +/************************************************************************/ +/* SHPCreateSimpleObject() */ +/* */ +/* Create a simple (common) shape object. Destroy with */ +/* SHPDestroyObject(). */ +/************************************************************************/ + +SHPObject *SHPCreateSimpleObject( int nSHPType, int nVertices, + double * padfX, double * padfY, + double * padfZ ) + +{ + return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL, + nVertices, padfX, padfY, padfZ, NULL ) ); +} + +/************************************************************************/ +/* SHPWriteObject() */ +/* */ +/* Write out the vertices of a new structure. Note that it is */ +/* only possible to write vertices at the end of the file. */ +/************************************************************************/ + +int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) + +{ + int nRecordOffset, i, j, nRecordSize; + uchar *pabyRec; + int32 i32; + + assert( nShapeId == -1 ); + + psSHP->bUpdated = TRUE; + +/* -------------------------------------------------------------------- */ +/* Add the new entity to the in memory index. */ +/* -------------------------------------------------------------------- */ + psSHP->nRecords++; + if( psSHP->nRecords > psSHP->nMaxRecords ) + { + psSHP->nMaxRecords = psSHP->nMaxRecords * 1.3 + 100; + + psSHP->panRecOffset = (int *) + SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords ); + psSHP->panRecSize = (int *) + SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize record. */ +/* -------------------------------------------------------------------- */ + psSHP->panRecOffset[psSHP->nRecords-1] = nRecordOffset = psSHP->nFileSize; + + pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) + + psObject->nParts * 8 + 128); + +/* -------------------------------------------------------------------- */ +/* Extract vertices for a Polygon or Arc. */ +/* -------------------------------------------------------------------- */ + if( psSHP->nShapeType == SHPT_POLYGON + || psSHP->nShapeType == SHPT_POLYGONZ + || psSHP->nShapeType == SHPT_POLYGONM + || psSHP->nShapeType == SHPT_ARC + || psSHP->nShapeType == SHPT_ARCZ + || psSHP->nShapeType == SHPT_ARCM + || psSHP->nShapeType == SHPT_MULTIPATCH ) + { + int32 nPoints, nParts; + int i; + + nPoints = psObject->nVertices; + nParts = psObject->nParts; + + _SHPSetBounds( pabyRec + 12, psObject ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + if( bBigEndian ) SwapWord( 4, &nParts ); + + ByteCopy( &nPoints, pabyRec + 40 + 8, 4 ); + ByteCopy( &nParts, pabyRec + 36 + 8, 4 ); + + nRecordSize = 52; + + /* + * Write part start positions. + */ + ByteCopy( psObject->panPartStart, pabyRec + 44 + 8, + 4 * psObject->nParts ); + for( i = 0; i < psObject->nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i ); + nRecordSize += 4; + } + + /* + * Write multipatch part types if needed. + */ + if( psSHP->nShapeType == SHPT_MULTIPATCH ) + { + memcpy( pabyRec + nRecordSize, psObject->panPartType, + 4*psObject->nParts ); + for( i = 0; i < psObject->nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize ); + nRecordSize += 4; + } + } + + /* + * Write the (x,y) vertex values. + */ + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 ); + ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 ); + + if( bBigEndian ) + SwapWord( 8, pabyRec + nRecordSize ); + + if( bBigEndian ) + SwapWord( 8, pabyRec + nRecordSize + 8 ); + + nRecordSize += 2 * 8; + } + + /* + * Write the Z coordinates (if any). + */ + if( psSHP->nShapeType == SHPT_POLYGONZ + || psSHP->nShapeType == SHPT_ARCZ + || psSHP->nShapeType == SHPT_MULTIPATCH ) + { + ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + + /* + * Write the M values, if any. + */ + if( psSHP->nShapeType == SHPT_POLYGONM + || psSHP->nShapeType == SHPT_ARCM +#ifndef DISABLE_MULTIPATCH_MEASURE + || psSHP->nShapeType == SHPT_MULTIPATCH +#endif + || psSHP->nShapeType == SHPT_POLYGONZ + || psSHP->nShapeType == SHPT_ARCZ ) + { + ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Extract vertices for a MultiPoint. */ +/* -------------------------------------------------------------------- */ + else if( psSHP->nShapeType == SHPT_MULTIPOINT + || psSHP->nShapeType == SHPT_MULTIPOINTZ + || psSHP->nShapeType == SHPT_MULTIPOINTM ) + { + int32 nPoints; + int i; + + nPoints = psObject->nVertices; + + _SHPSetBounds( pabyRec + 12, psObject ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + ByteCopy( &nPoints, pabyRec + 44, 4 ); + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 ); + ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 ); + if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 ); + } + + nRecordSize = 48 + 16 * psObject->nVertices; + + if( psSHP->nShapeType == SHPT_MULTIPOINTZ ) + { + ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + + if( psSHP->nShapeType == SHPT_MULTIPOINTZ + || psSHP->nShapeType == SHPT_MULTIPOINTM ) + { + ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Extract vertices for a point. */ +/* -------------------------------------------------------------------- */ + else if( psSHP->nShapeType == SHPT_POINT + || psSHP->nShapeType == SHPT_POINTZ + || psSHP->nShapeType == SHPT_POINTM ) + { + ByteCopy( psObject->padfX, pabyRec + 12, 8 ); + ByteCopy( psObject->padfY, pabyRec + 20, 8 ); + + if( bBigEndian ) SwapWord( 8, pabyRec + 12 ); + if( bBigEndian ) SwapWord( 8, pabyRec + 20 ); + + nRecordSize = 28; + + if( psSHP->nShapeType == SHPT_POINTZ ) + { + ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + + if( psSHP->nShapeType == SHPT_POINTZ + || psSHP->nShapeType == SHPT_POINTM ) + { + ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + + else + { + /* unknown type */ + assert( FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Set the shape type, record number, and record size. */ +/* -------------------------------------------------------------------- */ + i32 = psSHP->nRecords-1+1; /* record # */ + if( !bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec, 4 ); + + i32 = nRecordSize/2; /* record size */ + if( !bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec + 4, 4 ); + + i32 = psSHP->nShapeType; /* shape type */ + if( bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec + 8, 4 ); + +/* -------------------------------------------------------------------- */ +/* Write out record. */ +/* -------------------------------------------------------------------- */ + fseek( psSHP->fpSHP, nRecordOffset, 0 ); + fwrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ); + free( pabyRec ); + + psSHP->panRecSize[psSHP->nRecords-1] = nRecordSize-8; + psSHP->nFileSize += nRecordSize; + +/* -------------------------------------------------------------------- */ +/* Expand file wide bounds based on this shape. */ +/* -------------------------------------------------------------------- */ + if( psSHP->nRecords == 1 ) + { + psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0]; + psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0]; + psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0]; + psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0]; + } + + for( i = 0; i < psObject->nVertices; i++ ) + { + psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]); + psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]); + psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]); + psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]); + psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]); + psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]); + psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]); + psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); + } + + return( psSHP->nRecords - 1 ); +} + +/************************************************************************/ +/* SHPReadObject() */ +/* */ +/* Read the vertices, parts, and other non-attribute information */ +/* for one shape. */ +/************************************************************************/ + +SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) + +{ + int nRecordOffset, i, j; + SHPObject *psShape; + + static uchar *pabyRec = NULL; + static int nBufSize = 0; + +/* -------------------------------------------------------------------- */ +/* Validate the record/entity number. */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity >= psSHP->nRecords ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Ensure our record buffer is large enough. */ +/* -------------------------------------------------------------------- */ + if( psSHP->panRecSize[hEntity]+8 > nBufSize ) + { + nBufSize = psSHP->panRecSize[hEntity]+8; + pabyRec = (uchar *) SfRealloc(pabyRec,nBufSize); + } + +/* -------------------------------------------------------------------- */ +/* Read the record. */ +/* -------------------------------------------------------------------- */ + fseek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ); + fread( pabyRec, psSHP->panRecSize[hEntity]+8, 1, psSHP->fpSHP ); + +/* -------------------------------------------------------------------- */ +/* Allocate and minimally initialize the object. */ +/* -------------------------------------------------------------------- */ + psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); + psShape->nShapeId = hEntity; + psShape->nSHPType = psSHP->nShapeType; /* should get from this shape */ + +/* ==================================================================== */ +/* Extract vertices for a Polygon or Arc. */ +/* ==================================================================== */ + if( psSHP->nShapeType == SHPT_POLYGON || psSHP->nShapeType == SHPT_ARC + || psSHP->nShapeType == SHPT_POLYGONZ + || psSHP->nShapeType == SHPT_POLYGONM + || psSHP->nShapeType == SHPT_ARCZ + || psSHP->nShapeType == SHPT_ARCM + || psSHP->nShapeType == SHPT_MULTIPATCH ) + { + int32 nPoints, nParts; + int i, nOffset; + +/* -------------------------------------------------------------------- */ +/* Get the X/Y bounds. */ +/* -------------------------------------------------------------------- */ + memcpy( &(psShape->dfXMin), pabyRec + 8 + 4, 8 ); + memcpy( &(psShape->dfYMin), pabyRec + 8 + 12, 8 ); + memcpy( &(psShape->dfXMax), pabyRec + 8 + 20, 8 ); + memcpy( &(psShape->dfYMax), pabyRec + 8 + 28, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); + +/* -------------------------------------------------------------------- */ +/* Extract part/point count, and build vertex and part arrays */ +/* to proper size. */ +/* -------------------------------------------------------------------- */ + memcpy( &nPoints, pabyRec + 40 + 8, 4 ); + memcpy( &nParts, pabyRec + 36 + 8, 4 ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + if( bBigEndian ) SwapWord( 4, &nParts ); + + psShape->nVertices = nPoints; + psShape->padfX = (double *) calloc(nPoints,sizeof(double)); + psShape->padfY = (double *) calloc(nPoints,sizeof(double)); + psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); + psShape->padfM = (double *) calloc(nPoints,sizeof(double)); + + psShape->nParts = nParts; + psShape->panPartStart = (int *) calloc(nParts,sizeof(int)); + psShape->panPartType = (int *) calloc(nParts,sizeof(int)); + + for( i = 0; i < nParts; i++ ) + psShape->panPartType[i] = SHPP_RING; + +/* -------------------------------------------------------------------- */ +/* Copy out the part array from the record. */ +/* -------------------------------------------------------------------- */ + memcpy( psShape->panPartStart, pabyRec + 44 + 8, 4 * nParts ); + for( i = 0; i < nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i ); + } + + nOffset = 44 + 8 + 4*nParts; + +/* -------------------------------------------------------------------- */ +/* If this is a multipatch, we will also have parts types. */ +/* -------------------------------------------------------------------- */ + if( psSHP->nShapeType == SHPT_MULTIPATCH ) + { + memcpy( psShape->panPartType, pabyRec + nOffset, 4*nParts ); + for( i = 0; i < nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, psShape->panPartType+i ); + } + + nOffset += 4*nParts; + } + +/* -------------------------------------------------------------------- */ +/* Copy out the vertices from the record. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nPoints; i++ ) + { + memcpy(psShape->padfX + i, + pabyRec + nOffset + i * 16, + 8 ); + + memcpy(psShape->padfY + i, + pabyRec + nOffset + i * 16 + 8, + 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); + if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); + } + + nOffset += 16*nPoints; + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psSHP->nShapeType == SHPT_POLYGONZ + || psSHP->nShapeType == SHPT_ARCZ + || psSHP->nShapeType == SHPT_MULTIPATCH ) + { + memcpy( &(psShape->dfZMin), pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfZMax), pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfZ + i, + pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); + } + + nOffset += 16 + 8*nPoints; + } + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints ) + { + memcpy( &(psShape->dfMMin), pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfMMax), pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfM + i, + pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); + } + } + + } + +/* ==================================================================== */ +/* Extract vertices for a MultiPoint. */ +/* ==================================================================== */ + else if( psSHP->nShapeType == SHPT_MULTIPOINT + || psSHP->nShapeType == SHPT_MULTIPOINTM + || psSHP->nShapeType == SHPT_MULTIPOINTZ ) + { + int32 nPoints; + int i, nOffset; + + memcpy( &nPoints, pabyRec + 44, 4 ); + if( bBigEndian ) SwapWord( 4, &nPoints ); + + psShape->nVertices = nPoints; + psShape->padfX = (double *) calloc(nPoints,sizeof(double)); + psShape->padfY = (double *) calloc(nPoints,sizeof(double)); + psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); + psShape->padfM = (double *) calloc(nPoints,sizeof(double)); + + for( i = 0; i < nPoints; i++ ) + { + memcpy(psShape->padfX+i, pabyRec + 48 + 16 * i, 8 ); + memcpy(psShape->padfY+i, pabyRec + 48 + 16 * i + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); + if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); + } + + nOffset = 48 + 16*nPoints; + +/* -------------------------------------------------------------------- */ +/* Get the X/Y bounds. */ +/* -------------------------------------------------------------------- */ + memcpy( &(psShape->dfXMin), pabyRec + 8 + 4, 8 ); + memcpy( &(psShape->dfYMin), pabyRec + 8 + 12, 8 ); + memcpy( &(psShape->dfXMax), pabyRec + 8 + 20, 8 ); + memcpy( &(psShape->dfYMax), pabyRec + 8 + 28, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psSHP->nShapeType == SHPT_MULTIPOINTZ ) + { + memcpy( &(psShape->dfZMin), pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfZMax), pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfZ + i, + pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); + } + + nOffset += 16 + 8*nPoints; + } + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints ) + { + memcpy( &(psShape->dfMMin), pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfMMax), pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfM + i, + pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); + } + } + } + +/* ==================================================================== */ +/* Extract vertices for a point. */ +/* ==================================================================== */ + else if( psSHP->nShapeType == SHPT_POINT + || psSHP->nShapeType == SHPT_POINTM + || psSHP->nShapeType == SHPT_POINTZ ) + { + int nOffset; + + psShape->nVertices = 1; + psShape->padfX = (double *) calloc(1,sizeof(double)); + psShape->padfY = (double *) calloc(1,sizeof(double)); + psShape->padfZ = (double *) calloc(1,sizeof(double)); + psShape->padfM = (double *) calloc(1,sizeof(double)); + + memcpy( psShape->padfX, pabyRec + 12, 8 ); + memcpy( psShape->padfY, pabyRec + 20, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX ); + if( bBigEndian ) SwapWord( 8, psShape->padfY ); + + nOffset = 20 + 8; + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psSHP->nShapeType == SHPT_POINTZ ) + { + memcpy( psShape->padfZ, pabyRec + nOffset, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfZ ); + + nOffset += 8; + } + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( psSHP->panRecSize[hEntity]+8 >= nOffset + 8 ) + { + memcpy( psShape->padfM, pabyRec + nOffset, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfM ); + } + +/* -------------------------------------------------------------------- */ +/* Since no extents are supplied in the record, we will apply */ +/* them from the single vertex. */ +/* -------------------------------------------------------------------- */ + psShape->dfXMin = psShape->dfXMax = psShape->padfX[0]; + psShape->dfYMin = psShape->dfYMax = psShape->padfY[0]; + psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0]; + psShape->dfMMin = psShape->dfMMax = psShape->padfM[0]; + } + + return( psShape ); +} + +/************************************************************************/ +/* SHPTypeName() */ +/************************************************************************/ + +const char *SHPTypeName( int nSHPType ) + +{ + switch( nSHPType ) + { + case 0: + return "NullShape"; + + case SHPT_POINT: + return "Point"; + + case SHPT_ARC: + return "Arc"; + + case SHPT_POLYGON: + return "Polygon"; + + case SHPT_MULTIPOINT: + return "MultiPoint"; + + case SHPT_POINTZ: + return "PointZ"; + + case SHPT_ARCZ: + return "ArcZ"; + + case SHPT_POLYGONZ: + return "PolygonZ"; + + case SHPT_MULTIPOINTZ: + return "MultiPointZ"; + + case SHPT_POINTM: + return "PointM"; + + case SHPT_ARCM: + return "ArcM"; + + case SHPT_POLYGONM: + return "PolygonM"; + + case SHPT_MULTIPOINTM: + return "MultiPointM"; + + case SHPT_MULTIPATCH: + return "MultiPatch"; + + default: + return "UnknownShapeType"; + } +} + +/************************************************************************/ +/* SHPPartTypeName() */ +/************************************************************************/ + +const char *SHPPartTypeName( int nPartType ) + +{ + switch( nPartType ) + { + case SHPP_TRISTRIP: + return "TriangleStrip"; + + case SHPP_TRIFAN: + return "TriangleFan"; + + case SHPP_OUTERRING: + return "OuterRing"; + + case SHPP_INNERRING: + return "InnerRing"; + + case SHPP_FIRSTRING: + return "FirstRing"; + + case SHPP_RING: + return "Ring"; + + default: + return "UnknownPartType"; + } +} + +/************************************************************************/ +/* SHPDestroyObject() */ +/************************************************************************/ + +void SHPDestroyObject( SHPObject * psShape ) + +{ + if( psShape == NULL ) + return; + + if( psShape->padfX != NULL ) + free( psShape->padfX ); + if( psShape->padfY != NULL ) + free( psShape->padfY ); + if( psShape->padfZ != NULL ) + free( psShape->padfZ ); + if( psShape->padfM != NULL ) + free( psShape->padfM ); + + if( psShape->panPartStart != NULL ) + free( psShape->panPartStart ); + if( psShape->panPartType != NULL ) + free( psShape->panPartType ); + + free( psShape ); +} diff --git a/src/Lib/shapelib/shptest.c b/src/Lib/shapelib/shptest.c new file mode 100644 index 00000000..4c30ec6f --- /dev/null +++ b/src/Lib/shapelib/shptest.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 1998 Frank Warmerdam + * + * This code is in the public domain. + * + * $Log$ + * Revision 1.1 2000/02/09 19:51:46 curt + * Initial revision + * + * Revision 1.1 1999/08/24 21:13:01 curt + * Initial revision. + * + * Revision 1.2 1998/12/16 05:15:20 warmerda + * Added support for writing multipatch. + * + * Revision 1.1 1998/11/09 20:18:42 warmerda + * Initial revision + * + */ + +static char rcsid[] = + "$Id$"; + +#include "shapefil.h" + +/************************************************************************/ +/* Test_WritePoints() */ +/* */ +/* Write a small point file. */ +/************************************************************************/ + +static void Test_WritePoints( int nSHPType, const char *pszFilename ) + +{ + SHPHandle hSHPHandle; + SHPObject *psShape; + double x, y, z, m; + + hSHPHandle = SHPCreate( pszFilename, nSHPType ); + + x = 1.0; + y = 2.0; + z = 3.0; + m = 4.0; + psShape = SHPCreateObject( nSHPType, -1, 0, NULL, NULL, + 1, &x, &y, &z, &m ); + SHPWriteObject( hSHPHandle, -1, psShape ); + SHPDestroyObject( psShape ); + + x = 10.0; + y = 20.0; + z = 30.0; + m = 40.0; + psShape = SHPCreateObject( nSHPType, -1, 0, NULL, NULL, + 1, &x, &y, &z, &m ); + SHPWriteObject( hSHPHandle, -1, psShape ); + SHPDestroyObject( psShape ); + + SHPClose( hSHPHandle ); +} + +/************************************************************************/ +/* Test_WriteMultiPoints() */ +/* */ +/* Write a small multipoint file. */ +/************************************************************************/ + +static void Test_WriteMultiPoints( int nSHPType, const char *pszFilename ) + +{ + SHPHandle hSHPHandle; + SHPObject *psShape; + double x[4], y[4], z[4], m[4]; + int i, iShape; + + hSHPHandle = SHPCreate( pszFilename, nSHPType ); + + for( iShape = 0; iShape < 3; iShape++ ) + { + for( i = 0; i < 4; i++ ) + { + x[i] = iShape * 10 + i + 1.15; + y[i] = iShape * 10 + i + 2.25; + z[i] = iShape * 10 + i + 3.35; + m[i] = iShape * 10 + i + 4.45; + } + + psShape = SHPCreateObject( nSHPType, -1, 0, NULL, NULL, + 4, x, y, z, m ); + SHPWriteObject( hSHPHandle, -1, psShape ); + SHPDestroyObject( psShape ); + } + + SHPClose( hSHPHandle ); +} + +/************************************************************************/ +/* Test_WriteArcPoly() */ +/* */ +/* Write a small arc or polygon file. */ +/************************************************************************/ + +static void Test_WriteArcPoly( int nSHPType, const char *pszFilename ) + +{ + SHPHandle hSHPHandle; + SHPObject *psShape; + double x[100], y[100], z[100], m[100]; + int anPartStart[100]; + int anPartType[100], *panPartType; + int i, iShape; + + hSHPHandle = SHPCreate( pszFilename, nSHPType ); + + if( nSHPType == SHPT_MULTIPATCH ) + panPartType = anPartType; + else + panPartType = NULL; + + for( iShape = 0; iShape < 3; iShape++ ) + { + x[0] = 1.0; + y[0] = 1.0+iShape*3; + x[1] = 2.0; + y[1] = 1.0+iShape*3; + x[2] = 2.0; + y[2] = 2.0+iShape*3; + x[3] = 1.0; + y[3] = 2.0+iShape*3; + x[4] = 1.0; + y[4] = 1.0+iShape*3; + + for( i = 0; i < 5; i++ ) + { + z[i] = iShape * 10 + i + 3.35; + m[i] = iShape * 10 + i + 4.45; + } + + psShape = SHPCreateObject( nSHPType, -1, 0, NULL, NULL, + 5, x, y, z, m ); + SHPWriteObject( hSHPHandle, -1, psShape ); + SHPDestroyObject( psShape ); + } + +/* -------------------------------------------------------------------- */ +/* Do a multi part polygon (shape). We close it, and have two */ +/* inner rings. */ +/* -------------------------------------------------------------------- */ + x[0] = 0.0; + y[0] = 0.0; + x[1] = 0; + y[1] = 100; + x[2] = 100; + y[2] = 100; + x[3] = 100; + y[3] = 0; + x[4] = 0; + y[4] = 0; + + x[5] = 10; + y[5] = 20; + x[6] = 30; + y[6] = 20; + x[7] = 30; + y[7] = 40; + x[8] = 10; + y[8] = 40; + x[9] = 10; + y[9] = 20; + + x[10] = 60; + y[10] = 20; + x[11] = 90; + y[11] = 20; + x[12] = 90; + y[12] = 40; + x[13] = 60; + y[13] = 40; + x[14] = 60; + y[14] = 20; + + for( i = 0; i < 15; i++ ) + { + z[i] = i; + m[i] = i*2; + } + + anPartStart[0] = 0; + anPartStart[1] = 5; + anPartStart[2] = 10; + + anPartType[0] = SHPP_RING; + anPartType[1] = SHPP_INNERRING; + anPartType[2] = SHPP_INNERRING; + + psShape = SHPCreateObject( nSHPType, -1, 3, anPartStart, panPartType, + 15, x, y, z, m ); + SHPWriteObject( hSHPHandle, -1, psShape ); + SHPDestroyObject( psShape ); + + + SHPClose( hSHPHandle ); +} + +/************************************************************************/ +/* main() */ +/************************************************************************/ +int main( int argc, char ** argv ) + +{ + SHPHandle hSHP; + int nShapeType, nEntities, i, iPart; + const char *pszPlus; + +/* -------------------------------------------------------------------- */ +/* Display a usage message. */ +/* -------------------------------------------------------------------- */ + if( argc != 2 ) + { + printf( "shptest test_number\n" ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Figure out which test to run. */ +/* -------------------------------------------------------------------- */ + + if( atoi(argv[1]) == 1 ) + Test_WritePoints( SHPT_POINT, "test1.shp" ); + else if( atoi(argv[1]) == 2 ) + Test_WritePoints( SHPT_POINTZ, "test2.shp" ); + else if( atoi(argv[1]) == 3 ) + Test_WritePoints( SHPT_POINTM, "test3.shp" ); + + else if( atoi(argv[1]) == 4 ) + Test_WriteMultiPoints( SHPT_MULTIPOINT, "test4.shp" ); + else if( atoi(argv[1]) == 5 ) + Test_WriteMultiPoints( SHPT_MULTIPOINTZ, "test5.shp" ); + else if( atoi(argv[1]) == 6 ) + Test_WriteMultiPoints( SHPT_MULTIPOINTM, "test6.shp" ); + + else if( atoi(argv[1]) == 7 ) + Test_WriteArcPoly( SHPT_ARC, "test7.shp" ); + else if( atoi(argv[1]) == 8 ) + Test_WriteArcPoly( SHPT_ARCZ, "test8.shp" ); + else if( atoi(argv[1]) == 9 ) + Test_WriteArcPoly( SHPT_ARCM, "test9.shp" ); + + else if( atoi(argv[1]) == 10 ) + Test_WriteArcPoly( SHPT_POLYGON, "test10.shp" ); + else if( atoi(argv[1]) == 11 ) + Test_WriteArcPoly( SHPT_POLYGONZ, "test11.shp" ); + else if( atoi(argv[1]) == 12 ) + Test_WriteArcPoly( SHPT_POLYGONM, "test12.shp" ); + + else if( atoi(argv[1]) == 13 ) + Test_WriteArcPoly( SHPT_MULTIPATCH, "test13.shp" ); + else + { + printf( "Test `%s' not recognised.\n", argv[1] ); + exit( 10 ); + } + +#ifdef USE_DBMALLOC + malloc_dump(2); +#endif + + exit( 0 ); +} diff --git a/src/Lib/shapelib/shputils.c b/src/Lib/shapelib/shputils.c new file mode 100644 index 00000000..c347e718 --- /dev/null +++ b/src/Lib/shapelib/shputils.c @@ -0,0 +1,821 @@ +/* + * ORGINAL CODE WAS FROM "dbfdump.c", "shpdump.c", and "shpopen.c". + * Frank Warmerdam 1995 + * + * This code is in the public domain. + * + * $Log$ + * Revision 1.1 2000/02/09 19:51:46 curt + * Initial revision + * + * Revision 1.1 1999/08/24 21:13:01 curt + * Initial revision. + * + * Revision 1.3 1998/12/03 15:47:39 warmerda + * Did a bunch of rewriting to make it work with the V1.2 API. + * + * Revision 1.2 1998/06/18 01:19:49 warmerda + * Made C++ compilable. + * + * Revision 1.1 1997/05/27 20:40:27 warmerda + * Initial revision + * + * + * Altered "shpdump" and "dbfdump" to allow two files to be appended. + * Other Functions: + * Selecting from the DBF before the write occurs. + * Change the UNITS between Feet and Meters and Shift X,Y. + * Clip and Erase boundary. + * + * Bill Miller NC-DOT -- Feb. 1997 -- bmiller@doh.dot.state.nc.us + * There was not a lot of time to debug hidden problems; + * And the code is not very well organized or documented. + * The clip/erase function was not well tested. + * + * PURPOSE: I needed a program to Append, Select, Change Unit, and + * Clip boundaries. The program only passes thru the + * data once. + * + */ + +static char rcsid[] = + "$Id$"; + +#include "shapefil.h" +#include "string.h" +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif + +char infile[80], outfile[80], temp[400]; + +/* Variables for shape files */ +SHPHandle hSHP; +SHPHandle hSHPappend; +int nShapeType, nEntities, iPart; +int nShapeTypeAppend, nEntitiesAppend; +SHPObject *psCShape; +double adfBoundsMin[4], adfBoundsMax[4]; + + +/* Variables for DBF files */ +DBFHandle hDBF; +DBFHandle hDBFappend; + +DBFFieldType iType; +DBFFieldType jType; + +char iszTitle[12]; +char jszTitle[12]; + +int *pt; +char iszFormat[32], iszField[1024]; +char jszFormat[32], jszField[1024]; +int i, ti, iWidth, iDecimals, iRecord; +int j, tj, jWidth, jDecimals, jRecord; +int found, newdbf; + +void openfiles(void); +void setext(char *pt, char *ext); +int strncasecmp2(char *s1, char *s2, int n); +void mergefields(void); +void findselect(void); +void showitems(void); +int selectrec(); +int check_theme_bnd(); +int clip(); +void error(); + + +/* -------------------------------------------------------------------- */ +/* Variables for the SELECT function */ +/* -------------------------------------------------------------------- */ + char selectitem[40], *cpt; + int selectvalues[150]; + int iselect = FALSE, iselectitem = -1, selcount=0; + int iunselect = FALSE; + +/* -------------------------------------------------------------------- */ +/* Variables for the CLIP and ERASE functions */ +/* -------------------------------------------------------------------- */ + double cxmin, cymin, cxmax, cymax; + int iclip = FALSE, ierase = FALSE; + int itouch = FALSE, iinside = FALSE, icut = FALSE; + int ibound = FALSE, ipoly = FALSE; + char clipfile[80]; + +/* -------------------------------------------------------------------- */ +/* Variables for the UNIT function */ +/* -------------------------------------------------------------------- */ + double factor = 1; /* NO FACTOR */ + int iunit = FALSE; + + +/* -------------------------------------------------------------------- */ +/* Variables for the SHIFT function */ +/* -------------------------------------------------------------------- */ + double xshift = 0, yshift = 0; /* NO SHIFT */ + +int main( int argc, char ** argv ) +{ + +/* -------------------------------------------------------------------- */ +/* Check command line usage. */ +/* -------------------------------------------------------------------- */ + if( argc < 2 ) error(); + strcpy(infile, argv[1]); + if (argc == 2 ) { + setext(infile, "shp"); + printf("DESCRIBE: %s\n",infile); + strcpy(outfile,""); + } else { + strcpy(outfile,argv[2]); + } +/* -------------------------------------------------------------------- */ +/* Look for other functions on the command line. (SELECT, UNIT) */ +/* -------------------------------------------------------------------- */ + for (i = 3; i < argc; i++) + { + if ((strncasecmp2(argv[i], "SEL",3) == 0) || + (strncasecmp2(argv[i], "UNSEL",5) == 0)) + { + if (strncasecmp2(argv[i], "UNSEL",5) == 0) iunselect=TRUE; + i++; + if (i >= argc) error(); + strcpy(selectitem,argv[i]); + i++; + if (i >= argc) error(); + selcount=0; + strcpy(temp,argv[i]); + cpt=temp; + tj = atoi(cpt); + ti = 0; + while (tj>0) { + selectvalues[selcount] = tj; + while( *cpt >= '0' && *cpt <= '9') + cpt++; + while( *cpt > '\0' && (*cpt < '0' || *cpt > '9') ) + cpt++; + tj=atoi(cpt); + selcount++; + } + iselect=TRUE; + } + else + if ((strncasecmp2(argv[i], "CLIP",4) == 0) || + (strncasecmp2(argv[i], "ERASE",5) == 0)) + { + if (strncasecmp2(argv[i], "ERASE",5) == 0) ierase=TRUE; + i++; + if (i >= argc) error(); + strcpy(clipfile,argv[i]); + sscanf(argv[i],"%lf",&cxmin); + i++; + if (i >= argc) error(); + if (strncasecmp2(argv[i], "BOUND",5) == 0) { + setext(clipfile, "shp"); + hSHP = SHPOpen( clipfile, "rb" ); + if( hSHP == NULL ) + { + printf( "ERROR: Unable to open the clip shape file:%s\n", clipfile ); + exit( 1 ); + } + SHPGetInfo( hSHPappend, NULL, NULL, + adfBoundsMin, adfBoundsMax ); + cxmin = adfBoundsMin[0]; + cymin = adfBoundsMin[1]; + cxmax = adfBoundsMax[0]; + cymax = adfBoundsMax[1]; + printf("Theme Clip Boundary: (%lf,%lf) - (%lf,%lf)\n", + cxmin, cymin, cxmax, cymax); + ibound=TRUE; + } + else if (strncasecmp2(argv[i], "POLY",4) == 0) + { + ipoly=TRUE; + } + else { /*** xmin,ymin,xmax,ymax ***/ + sscanf(argv[i],"%lf",&cymin); + i++; + if (i >= argc) error(); + sscanf(argv[i],"%lf",&cxmax); + i++; + if (i >= argc) error(); + sscanf(argv[i],"%lf",&cymax); + printf("Clip Box: (%lf,%lf) - (%lf,%lf)\n",cxmin, cymin, cxmax, cymax); + } + i++; + if (i >= argc) error(); + if (strncasecmp2(argv[i], "CUT",3) == 0) icut=TRUE; + else if (strncasecmp2(argv[i], "TOUCH",5) == 0) itouch=TRUE; + else if (strncasecmp2(argv[i], "INSIDE",6) == 0) iinside=TRUE; + else error(); + iclip=TRUE; + } + else + if (strncasecmp2(argv[i], "UNIT",4) == 0) + { + i++; + if (i >= argc) error(); + if (strncasecmp2(argv[i], "METER",5) == 0) + factor=0.304800609601; + else + { + if (strncasecmp2(argv[i], "FEET",4) == 0) + factor=3.280833; + else + sscanf(argv[i],"%lf",&factor); + } + if (factor == 0) error(); + iunit=TRUE; + printf("Output file coordinate values will be factored by %lg\n",factor); + } + else + if (strncasecmp2(argv[i],"SHIFT",5) == 0) + { + i++; + if (i >= argc) error(); + sscanf(argv[i],"%lf",&xshift); + i++; + if (i >= argc) error(); + sscanf(argv[i],"%lf",&yshift); + iunit=TRUE; + printf("X Shift: %lg Y Shift: %lg\n",xshift,yshift); + } + else + { + printf("ERROR: Unknown function %s\n",argv[i]); error(); + } + } +/* -------------------------------------------------------------------- */ +/* If there is no data in this file let the user know. */ +/* -------------------------------------------------------------------- */ + openfiles(); /* Open the infile and the outfile for shape and dbf. */ + if( DBFGetFieldCount(hDBF) == 0 ) + { + puts( "There are no fields in this table!" ); + exit( 1 ); + } +/* -------------------------------------------------------------------- */ +/* Print out the file bounds. */ +/* -------------------------------------------------------------------- */ + iRecord = DBFGetRecordCount( hDBF ); + SHPGetInfo( hSHP, NULL, NULL, adfBoundsMin, adfBoundsMax ); + + printf( "Input Bounds: (%lg,%lg) - (%lg,%lg) Entities: %d DBF: %d\n", + adfBoundsMin[0], adfBoundsMin[1], + adfBoundsMax[0], adfBoundsMax[1], + nEntities, iRecord ); + + if (strcmp(outfile,"") == 0) + { + ti = DBFGetFieldCount( hDBF ); + showitems(); + exit(0); + } + + if (iclip) check_theme_bnd(); + + jRecord = DBFGetRecordCount( hDBFappend ); + SHPGetInfo( hSHPappend, NULL, NULL, adfBoundsMin, adfBoundsMax ); + if (nEntitiesAppend == 0) + puts("New Output File\n"); + else + printf( "Append Bounds: (%lg,%lg)-(%lg,%lg) Entities: %d DBF: %d\n", + adfBoundsMin[0], adfBoundsMin[1], + adfBoundsMax[0], adfBoundsMax[1], + nEntitiesAppend, jRecord ); + +/* -------------------------------------------------------------------- */ +/* Find matching fields in the append file or add new items. */ +/* -------------------------------------------------------------------- */ + mergefields(); +/* -------------------------------------------------------------------- */ +/* Find selection field if needed. */ +/* -------------------------------------------------------------------- */ + if (iselect) findselect(); + +/* -------------------------------------------------------------------- */ +/* Read all the records */ +/* -------------------------------------------------------------------- */ + jRecord = DBFGetRecordCount( hDBFappend ); + for( iRecord = 0; iRecord < nEntities; iRecord++) /** DBFGetRecordCount(hDBF) **/ + { +/* -------------------------------------------------------------------- */ +/* SELECT for values if needed. (Can the record be skipped.) */ +/* -------------------------------------------------------------------- */ + if (iselect) + if (selectrec() == 0) goto SKIP_RECORD; /** SKIP RECORD **/ + +/* -------------------------------------------------------------------- */ +/* Read a Shape record */ +/* -------------------------------------------------------------------- */ + psCShape = SHPReadObject( hSHP, iRecord ); + +/* -------------------------------------------------------------------- */ +/* Clip coordinates of shapes if needed. */ +/* -------------------------------------------------------------------- */ + if (iclip) + if (clip() == 0) goto SKIP_RECORD; /** SKIP RECORD **/ + +/* -------------------------------------------------------------------- */ +/* Read a DBF record and copy each field. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < DBFGetFieldCount(hDBF); i++ ) + { +/* -------------------------------------------------------------------- */ +/* Store the record according to the type and formatting */ +/* information implicit in the DBF field description. */ +/* -------------------------------------------------------------------- */ + if (pt[i] > -1) /* if the current field exists in output file */ + { + switch( DBFGetFieldInfo( hDBF, i, NULL, &iWidth, &iDecimals ) ) + { + case FTString: + DBFWriteStringAttribute(hDBFappend, jRecord, pt[i], + (DBFReadStringAttribute( hDBF, iRecord, i )) ); + break; + + case FTInteger: + DBFWriteIntegerAttribute(hDBFappend, jRecord, pt[i], + (DBFReadIntegerAttribute( hDBF, iRecord, i )) ); + break; + + case FTDouble: + DBFWriteDoubleAttribute(hDBFappend, jRecord, pt[i], + (DBFReadDoubleAttribute( hDBF, iRecord, i )) ); + break; + } + } + } + jRecord++; +/* -------------------------------------------------------------------- */ +/* Change UNIT and SHIFT coordinates of shapes if needed. */ +/* -------------------------------------------------------------------- */ + if (iunit) + { + for( j = 0; j < psCShape->nVertices; j++ ) + { + psCShape->padfX[j] = psCShape->padfX[j] * factor + xshift; + psCShape->padfY[j] = psCShape->padfY[j] * factor + yshift; + } + } + +/* -------------------------------------------------------------------- */ +/* Write the Shape record after recomputing current extents. */ +/* -------------------------------------------------------------------- */ + SHPComputeExtents( psCShape ); + SHPWriteObject( hSHPappend, -1, psCShape ); + + SKIP_RECORD: + SHPDestroyObject( psCShape ); + j=0; + } + +/* -------------------------------------------------------------------- */ +/* Print out the # of Entities and the file bounds. */ +/* -------------------------------------------------------------------- */ + jRecord = DBFGetRecordCount( hDBFappend ); + SHPGetInfo( hSHPappend, &nEntitiesAppend, &nShapeTypeAppend, + adfBoundsMin, adfBoundsMax ); + + printf( "Output Bounds: (%lg,%lg) - (%lg,%lg) Entities: %d DBF: %d\n\n", + adfBoundsMin[0], adfBoundsMin[1], + adfBoundsMax[0], adfBoundsMax[1], + nEntitiesAppend, jRecord ); + +/* -------------------------------------------------------------------- */ +/* Close the both shapefiles. */ +/* -------------------------------------------------------------------- */ + SHPClose( hSHP ); + SHPClose( hSHPappend ); + DBFClose( hDBF ); + DBFClose( hDBFappend ); + return( 0 ); +} + + +/************************************************************************/ +/* openfiles() */ +/************************************************************************/ + +void openfiles() { +/* -------------------------------------------------------------------- */ +/* Open the DBF file. */ +/* -------------------------------------------------------------------- */ + setext(infile, "dbf"); + hDBF = DBFOpen( infile, "rb" ); + if( hDBF == NULL ) + { + printf( "ERROR: Unable to open the input DBF:%s\n", infile ); + exit( 1 ); + } +/* -------------------------------------------------------------------- */ +/* Open the append DBF file. */ +/* -------------------------------------------------------------------- */ + if (strcmp(outfile,"")) { + setext(outfile, "dbf"); + hDBFappend = DBFOpen( outfile, "rb+" ); + newdbf=0; + if( hDBFappend == NULL ) + { + newdbf=1; + hDBFappend = DBFCreate( outfile ); + if( hDBFappend == NULL ) + { + printf( "ERROR: Unable to open the append DBF:%s\n", outfile ); + exit( 1 ); + } + } + } +/* -------------------------------------------------------------------- */ +/* Open the passed shapefile. */ +/* -------------------------------------------------------------------- */ + setext(infile, "shp"); + hSHP = SHPOpen( infile, "rb" ); + + if( hSHP == NULL ) + { + printf( "ERROR: Unable to open the input shape file:%s\n", infile ); + exit( 1 ); + } + + SHPGetInfo( hSHP, &nEntities, &nShapeType, NULL, NULL ); + +/* -------------------------------------------------------------------- */ +/* Open the passed append shapefile. */ +/* -------------------------------------------------------------------- */ + if (strcmp(outfile,"")) { + setext(outfile, "shp"); + hSHPappend = SHPOpen( outfile, "rb+" ); + + if( hSHPappend == NULL ) + { + hSHPappend = SHPCreate( outfile, nShapeType ); + if( hSHPappend == NULL ) + { + printf( "ERROR: Unable to open the append shape file:%s\n", + outfile ); + exit( 1 ); + } + } + SHPGetInfo( hSHPappend, &nEntitiesAppend, &nShapeTypeAppend, + NULL, NULL ); + + if (nShapeType != nShapeTypeAppend) + { + puts( "ERROR: Input and Append shape files are of different types."); + exit( 1 ); + } + } +} + +/* -------------------------------------------------------------------- */ +/* Change the extension. If there is any extension on the */ +/* filename, strip it off and add the new extension */ +/* -------------------------------------------------------------------- */ +void setext(char *pt, char *ext) +{ +int i; + for( i = strlen(pt)-1; + i > 0 && pt[i] != '.' && pt[i] != '/' && pt[i] != '\\'; + i-- ) {} + + if( pt[i] == '.' ) + pt[i] = '\0'; + + strcat(pt,"."); + strcat(pt,ext); +} + + + +/* -------------------------------------------------------------------- */ +/* Find matching fields in the append file. */ +/* Output file must have zero records to add any new fields. */ +/* -------------------------------------------------------------------- */ +void mergefields() +{ + int i,j; + ti = DBFGetFieldCount( hDBF ); + tj = DBFGetFieldCount( hDBFappend ); + /* Create a pointer array for the max # of fields in the output file */ + pt = (int *) malloc( (ti+tj+1) * sizeof(int) ); + + for( i = 0; i < ti; i++ ) + { + pt[i]= -1; /* Initial pt values to -1 */ + } + /* DBF must be empty before adding items */ + jRecord = DBFGetRecordCount( hDBFappend ); + for( i = 0; i < ti; i++ ) + { + iType = DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals ); + found=0; + { + for( j = 0; j < tj; j++ ) /* Search all field names for a match */ + { + jType = DBFGetFieldInfo( hDBFappend, j, jszTitle, &jWidth, &jDecimals ); + if (iType == jType && (strcmp(iszTitle, jszTitle) == 0) ) + { + if (found == 1 || newdbf == 1) + { + if (i == j) pt[i]=j; + printf("Warning: Duplicate field name found (%s)\n",iszTitle); + /* Duplicate field name + (Try to guess the correct field by position) */ + } + else + { + pt[i]=j; found=1; + } + } + } + } + + if (pt[i] == -1 && found == 0) /* Try to force into an existing field */ + { /* Ignore the field name, width, and decimal places */ + jType = DBFGetFieldInfo( hDBFappend, j, jszTitle, &jWidth, &jDecimals ); + if (iType == jType) + { + pt[i]=i; found=1; + } + } + if (found == 0 && jRecord == 0) /* Add missing field to the append table */ + { /* The output DBF must be is empty */ + pt[i]=tj; + tj++; + if( !DBFAddField( hDBFappend, iszTitle, iType, iWidth, iDecimals )) + { + printf( "Warning: DBFAddField(%s, TYPE:%d, WIDTH:%d DEC:%d, ITEM#:%d of %d) failed.\n", + iszTitle, iType, iWidth, iDecimals, (i+1), (ti+1) ); + pt[i]=-1; + } + } + } +} + + +void findselect() +{ + /* Find the select field name */ + iselectitem = -1; + for( i = 0; i < ti && iselectitem < 0; i++ ) + { + iType = DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals ); + if (strncasecmp2(iszTitle, selectitem, 0) == 0) iselectitem = i; + } + if (iselectitem == -1) + { + printf("Warning: Item not found for selection (%s)\n",selectitem); + iselect = 0; + showitems(); + printf("Continued... (Selecting entire file)\n"); + } + /* Extract all of the select values (by field type) */ + +} + +void showitems() +{ + printf("Available Items: "); + for( i = 0; i < ti; i++ ) + { + iType = DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals ); + printf("%s, ",iszTitle); + } + printf("(total=%d)\n",ti); +} + +int selectrec() +{ +int value, ty; + + ty = DBFGetFieldInfo( hDBF, iselectitem, NULL, &iWidth, &iDecimals); + switch(ty) + { + case FTString: + break; + case FTInteger: + value = DBFReadIntegerAttribute( hDBF, iRecord, iselectitem ); + for (j = 0; j= cxmin) && (adfBoundsMax[0] <= cxmax) && + (adfBoundsMin[1] >= cymin) && (adfBoundsMax[1] <= cymax) ) + { /** Theme is totally inside clip area **/ + if (ierase) nEntities=0; /** SKIP THEME **/ + else iclip=FALSE; /** WRITE THEME (Clip not needed) **/ + } + + if ( ( (adfBoundsMin[0] < cxmin) && (adfBoundsMax[0] < cxmin) ) || + ( (adfBoundsMin[1] < cymin) && (adfBoundsMax[1] < cymin) ) || + ( (adfBoundsMin[0] > cxmax) && (adfBoundsMax[0] > cxmax) ) || + ( (adfBoundsMin[1] > cymax) && (adfBoundsMax[1] > cymax) ) ) + { /** Theme is totally outside clip area **/ + if (ierase) iclip=FALSE; /** WRITE THEME (Clip not needed) **/ + else nEntities=0; /** SKIP THEME **/ + } + + if (nEntities == 0) + puts("WARNING: Theme is outside the clip area."); /** SKIP THEME **/ +} + +int clip() +{ + int outside=FALSE; + int j2=0, i2=0; + + if ( (psCShape->dfXMin >= cxmin) && (psCShape->dfXMax <= cxmax) && + (psCShape->dfYMin >= cymin) && (psCShape->dfYMax <= cymax) ) + { /** Feature is totally inside clip area **/ + if (ierase) return(0); /** SKIP RECORD **/ + else return(1); /** WRITE RECORD **/ + } + + if ( ( psCShape->dfXMax < cxmin ) || + ( psCShape->dfYMax < cymin ) || + ( psCShape->dfXMin > cxmax ) || + ( psCShape->dfYMin > cymax ) ) + { /** Feature is totally outside clip area **/ + if (ierase) return(1); /** WRITE RECORD **/ + else return(0); /** SKIP RECORD **/ + } + + if (itouch) + { + if (ierase) return(0); /** SKIP RECORD **/ + else return(1); /** WRITE RECORD **/ + } + + if (iinside) + { + if (ierase) return(1); /** WRITE RECORD **/ + else return(0); /** SKIP RECORD **/ + } + + /*** SECOND check each vertex in the feature ***/ + for( j2 = 0; j2 < psCShape->nVertices; j2++ ) + { + if (psCShape->padfX[j2] < cxmin || psCShape->padfX[j2] > cxmax) + { + outside=TRUE; + } + else + { + if (psCShape->padfY[j2] < cymin || psCShape->padfY[j2] > cymax) + outside=TRUE; + else + outside=FALSE; + } + + + if (icut) + { + if (outside) + { + } else { + if (i2 != j2) + { + /* write vertex */ + psCShape->padfX[i2] = psCShape->padfX[j2]; + psCShape->padfY[i2] = psCShape->padfY[j2]; + } + i2++; + } + } + else + if (outside) /* vertex is outside boundary */ + { + if (iinside) + { + if (ierase) return(1); /** WRITE RECORD **/ + else return(0); /** SKIP RECORD **/ + } + } + else /* vertex is inside boundary */ + { + if (itouch) + { + if (ierase) return(0); /** SKIP RECORD **/ + else return(1); /** WRITE RECORD **/ + } + } + } + + if (icut) + { + j2 = psCShape->nVertices; + if (i2 < 2) return(0); /** SKIP RECORD **/ + + psCShape->nVertices = i2; + + printf("Vertices:%d OUT:%d Number of Parts:%d\n", + j2, psCShape->nVertices, psCShape->nParts ); + } + if (itouch) + { + if (ierase) return(1); /** WRITE RECORD **/ + else return(0); /** SKIP RECORD **/ + } + if (iinside) + { + if (ierase) return(0); /** SKIP RECORD **/ + else return(1); /** WRITE RECORD **/ + } +} + +/* -------------------------------------------------------------------- */ +/* Display a usage message. */ +/* -------------------------------------------------------------------- */ +void error() + { + puts( "USAGE: shputils "); + puts( "USAGE: shputils " ); + puts( " {