1
0
Fork 0

Initial revision

This commit is contained in:
curt 2000-02-09 19:51:45 +00:00
commit b20be51d65
160 changed files with 61258 additions and 0 deletions

0
AUTHORS Normal file
View file

0
ChangeLog Normal file
View file

7
Makefile.am Normal file
View file

@ -0,0 +1,7 @@
EXTRA_DIST = README README.howto scenery_version.hxx
SUBDIRS = \
Lib \
Prep \
Construct \
Utils

0
NEWS Normal file
View file

75
README Normal file
View file

@ -0,0 +1,75 @@
FG Scenery Tools README
=======================
Contained here-in are the FG scenery creation tools. These can be
used to convert 3 arcsec ASCII format DEM files and 30 arcsec binary
format DEM files into Flight Gear scenery.
Eventually these tools will expand to support insertion of airports,
roads, rivers, lakes, etc.
Building the Tools
==================
These tools are compiled and tested under Linux. I'm all for
portability, but I just haven't been as motivated to port these tools,
since scenery creation is less of a general need ... especially at
this stage. However, if anyone wants to work on porting to other
platforms, I will be happy to incorporate patches.
The process for building these tools is very similar to building the
main FG source code.
1. Set the FG_ROOT, FG_ROOT_SRC, and FG_ROOT_LIB environment
variables.
2. Run ``make depend''
3. Run ``make clean''
4. Run ``make''
3 Arcsec ASCII DEM files
========================
Data files for the USA are available in this format from:
http://edcwww.cr.usgs.gov/doc/edchome/ndcdb/ndcdb.html
To generate FG scenery from one of these dem files, run:
./process-dem.pl <error-tolerance-squared> dem-file-1 [ dem-file-2 ...]
You can vary the error tolerance to control the level of detail (and
size) of the resulting scenery. Note, you must specify the error
tolerance squared. So, if you wish to allow up to a 10 meter error
margin (very high level of detail) you would specify a value of 100.
If you desire an error tolerance of 200 meters (medium detail level)
you would specify a value of 40000.
The process-dem.pl script will automatically dump the resulting .obj
files in the proper directory tree.
30 Arcsec Binary DEM files
==========================
These data files have world wide coverage and are available from:
http://edcwww.cr.usgs.gov/landdaac/gtopo30/gtopo30.html
To process these data files, you must first run:
DemRaw2Ascii/raw2ascii <input_file_basename> <output_dir>
For example:
DemRaw2Ascii/raw2ascii /tmp/W020N90 asciidems/
This will create ASCII DEM files for each 1 degree x 1 degree area in
the specified output dir.
Then, you can take these ascii dem files and feed them through the
same procedure you use with the 3 arcsec dem files.

122
README.howto Normal file
View file

@ -0,0 +1,122 @@
Original version by Alexei Novikov <anovikov@heron.itep.ru> Sep. 3, 1999
Updates by Curtis Olson <curt@flightgear.org>
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 <input_file_basename> <output_dir>
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.

1
VERSION Normal file
View file

@ -0,0 +1 @@
0.0.0

1
VERSION.in Normal file
View file

@ -0,0 +1 @@
@VERSION@

324
acconfig.h Normal file
View file

@ -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 <sys/dg_sys_info.h>. */
#undef DGUX
/* Define if you have <dirent.h>. */
#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 <sys/types.h> doesn't define. */
#undef gid_t
/* Define if you have alloca, as a function or macro. */
#undef HAVE_ALLOCA
/* Define if you have <alloca.h> 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 <sys/param.h> */
#undef HAVE_SYS_PARAM_H
/* Define if you have <sys/stat.h> that is POSIX.1 compatible. */
#undef HAVE_SYS_STAT_H
/* Define if you have <sys/wait.h> 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 <unistd.h>. */
#undef HAVE_UNISTD_H
/* Define if utime(file, NULL) sets file's timestamp to the present. */
#undef HAVE_UTIME_NULL
/* Define if you have <vfork.h>. */
#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 <mkdev.h>. */
#undef MAJOR_IN_MKDEV
/* Define if major, minor, and makedev are declared in <sysmacros.h>. */
#undef MAJOR_IN_SYSMACROS
/* Define if on MINIX. */
#undef _MINIX
/* Define to `int' if <sys/types.h> doesn't define. */
#undef mode_t
/* Define if you don't have <dirent.h>, but have <ndir.h>. */
#undef NDIR
/* Define if you have <memory.h>, and <string.h> 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 <nlist.h>. */
#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 <sys/types.h> doesn't define. */
#undef off_t
/* Define to package name */
#undef PACKAGE
/* Define to `int' if <sys/types.h> 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 <sys/types.h> 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 <sys/stat.h> 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 <dirent.h>, but have <sys/dir.h>. */
#undef SYSDIR
/* Define if you don't have <dirent.h>, but have <sys/ndir.h>. */
#undef SYSNDIR
/* Define if `sys_siglist' is declared by <signal.h>. */
#undef SYS_SIGLIST_DECLARED
/* Define if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Define if your <sys/time.h> declares struct tm. */
#undef TM_IN_SYS_TIME
/* Define to `int' if <sys/types.h> doesn't define. */
#undef uid_t
/* Define for Encore UMAX. */
#undef UMAX
/* Define for Encore UMAX 4.3 that has <inq_status/cpustats.h>
instead of <sys/cpustats.h>. */
#undef UMAX4_3
/* Define if you do not have <strings.h>, 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). */

149
aclocal.m4 vendored Normal file
View file

@ -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>>, <<[^ ]>>, <<>>), <<>>,
<<test -z "<<$>>CONFIG_HEADERS" || echo timestamp > patsubst(<<$1>>, <<^\([^:]*/\)?.*>>, <<\1>>)stamp-h<<>>dnl>>,
<<am_indx=1
for am_file in <<$1>>; 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([,]))])

394
acsite.m4 Normal file
View file

@ -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 <sys/select.h> is includable after <sys/time.h>
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 <sys/select.h> is compatible with <sys/time.h>])
selecth=yes
if test "$ac_cv_header_sys_time_h" = yes ; then
AC_TRY_COMPILE([#include <sys/time.h>
#include <sys/select.h>],[
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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
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 <time.h>], [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 <time.h>],
[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 <time.h>],
[extern time_t timezone;
timezone += 1;
exit (0);],
[have_timezone=yes
AC_MSG_RESULT(yes)],
AC_MSG_RESULT(no))
fi
])dnl

89
config.cache Normal file
View file

@ -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}

89
config.log Normal file
View file

@ -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

441
config.status Executable file
View file

@ -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 <<CEOF
${ac_dA}PACKAGE${ac_dB}PACKAGE${ac_dC}"TerraGear"${ac_dD}
${ac_uA}PACKAGE${ac_uB}PACKAGE${ac_uC}"TerraGear"${ac_uD}
${ac_eA}PACKAGE${ac_eB}PACKAGE${ac_eC}"TerraGear"${ac_eD}
${ac_dA}VERSION${ac_dB}VERSION${ac_dC}"0.0.0"${ac_dD}
${ac_uA}VERSION${ac_uB}VERSION${ac_uC}"0.0.0"${ac_uD}
${ac_eA}VERSION${ac_eB}VERSION${ac_eC}"0.0.0"${ac_eD}
${ac_dA}HAVE_LIBM${ac_dB}HAVE_LIBM${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBM${ac_uB}HAVE_LIBM${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBM${ac_eB}HAVE_LIBM${ac_eC}1${ac_eD}
${ac_dA}HAVE_LIBX11${ac_dB}HAVE_LIBX11${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBX11${ac_uB}HAVE_LIBX11${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBX11${ac_eB}HAVE_LIBX11${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_LIBXEXT${ac_dB}HAVE_LIBXEXT${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBXEXT${ac_uB}HAVE_LIBXEXT${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBXEXT${ac_eB}HAVE_LIBXEXT${ac_eC}1${ac_eD}
${ac_dA}HAVE_LIBXI${ac_dB}HAVE_LIBXI${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBXI${ac_uB}HAVE_LIBXI${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBXI${ac_eB}HAVE_LIBXI${ac_eC}1${ac_eD}
${ac_dA}HAVE_LIBICE${ac_dB}HAVE_LIBICE${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBICE${ac_uB}HAVE_LIBICE${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBICE${ac_eB}HAVE_LIBICE${ac_eC}1${ac_eD}
${ac_dA}HAVE_LIBSM${ac_dB}HAVE_LIBSM${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBSM${ac_uB}HAVE_LIBSM${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBSM${ac_eB}HAVE_LIBSM${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_LIBXT${ac_dB}HAVE_LIBXT${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBXT${ac_uB}HAVE_LIBXT${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBXT${ac_eB}HAVE_LIBXT${ac_eC}1${ac_eD}
${ac_dA}HAVE_LIBXMU${ac_dB}HAVE_LIBXMU${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBXMU${ac_uB}HAVE_LIBXMU${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBXMU${ac_eB}HAVE_LIBXMU${ac_eC}1${ac_eD}
${ac_dA}HAVE_LIBGL${ac_dB}HAVE_LIBGL${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBGL${ac_uB}HAVE_LIBGL${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBGL${ac_eB}HAVE_LIBGL${ac_eC}1${ac_eD}
${ac_dA}HAVE_LIBMESAGLU${ac_dB}HAVE_LIBMESAGLU${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBMESAGLU${ac_uB}HAVE_LIBMESAGLU${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBMESAGLU${ac_eB}HAVE_LIBMESAGLU${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_LIBGLUT${ac_dB}HAVE_LIBGLUT${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBGLUT${ac_uB}HAVE_LIBGLUT${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBGLUT${ac_eB}HAVE_LIBGLUT${ac_eC}1${ac_eD}
${ac_dA}HAVE_LIBGLUT${ac_dB}HAVE_LIBGLUT${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIBGLUT${ac_uB}HAVE_LIBGLUT${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIBGLUT${ac_eB}HAVE_LIBGLUT${ac_eC}1${ac_eD}
${ac_dA}HAVE_GPC_H${ac_dB}HAVE_GPC_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_GPC_H${ac_uB}HAVE_GPC_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_GPC_H${ac_eB}HAVE_GPC_H${ac_eC}1${ac_eD}
${ac_dA}STDC_HEADERS${ac_dB}STDC_HEADERS${ac_dC}1${ac_dD}
${ac_uA}STDC_HEADERS${ac_uB}STDC_HEADERS${ac_uC}1${ac_uD}
${ac_eA}STDC_HEADERS${ac_eB}STDC_HEADERS${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_FCNTL_H${ac_dB}HAVE_FCNTL_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_FCNTL_H${ac_uB}HAVE_FCNTL_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_FCNTL_H${ac_eB}HAVE_FCNTL_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_GETOPT_H${ac_dB}HAVE_GETOPT_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_GETOPT_H${ac_uB}HAVE_GETOPT_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_GETOPT_H${ac_eB}HAVE_GETOPT_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_MALLOC_H${ac_dB}HAVE_MALLOC_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_MALLOC_H${ac_uB}HAVE_MALLOC_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_MALLOC_H${ac_eB}HAVE_MALLOC_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_MEMORY_H${ac_dB}HAVE_MEMORY_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_MEMORY_H${ac_uB}HAVE_MEMORY_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_MEMORY_H${ac_eB}HAVE_MEMORY_H${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_STDLIB_H${ac_dB}HAVE_STDLIB_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_STDLIB_H${ac_uB}HAVE_STDLIB_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_STDLIB_H${ac_eB}HAVE_STDLIB_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_SYS_PARAM_H${ac_dB}HAVE_SYS_PARAM_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_SYS_PARAM_H${ac_uB}HAVE_SYS_PARAM_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_SYS_PARAM_H${ac_eB}HAVE_SYS_PARAM_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_SYS_STAT_H${ac_dB}HAVE_SYS_STAT_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_SYS_STAT_H${ac_uB}HAVE_SYS_STAT_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_SYS_STAT_H${ac_eB}HAVE_SYS_STAT_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_SYS_TIME_H${ac_dB}HAVE_SYS_TIME_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_SYS_TIME_H${ac_uB}HAVE_SYS_TIME_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_SYS_TIME_H${ac_eB}HAVE_SYS_TIME_H${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_SYS_TIMEB_H${ac_dB}HAVE_SYS_TIMEB_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_SYS_TIMEB_H${ac_uB}HAVE_SYS_TIMEB_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_SYS_TIMEB_H${ac_eB}HAVE_SYS_TIMEB_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_UNISTD_H${ac_dB}HAVE_UNISTD_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_UNISTD_H${ac_uB}HAVE_UNISTD_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_UNISTD_H${ac_eB}HAVE_UNISTD_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_VALUES_H${ac_dB}HAVE_VALUES_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_VALUES_H${ac_uB}HAVE_VALUES_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_VALUES_H${ac_eB}HAVE_VALUES_H${ac_eC}1${ac_eD}
${ac_dA}TIME_WITH_SYS_TIME${ac_dB}TIME_WITH_SYS_TIME${ac_dC}1${ac_dD}
${ac_uA}TIME_WITH_SYS_TIME${ac_uB}TIME_WITH_SYS_TIME${ac_uC}1${ac_uD}
${ac_eA}TIME_WITH_SYS_TIME${ac_eB}TIME_WITH_SYS_TIME${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}RETSIGTYPE${ac_dB}RETSIGTYPE${ac_dC}void${ac_dD}
${ac_uA}RETSIGTYPE${ac_uB}RETSIGTYPE${ac_uC}void${ac_uD}
${ac_eA}RETSIGTYPE${ac_eB}RETSIGTYPE${ac_eC}void${ac_eD}
${ac_dA}HAVE_VPRINTF${ac_dB}HAVE_VPRINTF${ac_dC}1${ac_dD}
${ac_uA}HAVE_VPRINTF${ac_uB}HAVE_VPRINTF${ac_uC}1${ac_uD}
${ac_eA}HAVE_VPRINTF${ac_eB}HAVE_VPRINTF${ac_eC}1${ac_eD}
${ac_dA}HAVE_FTIME${ac_dB}HAVE_FTIME${ac_dC}1${ac_dD}
${ac_uA}HAVE_FTIME${ac_uB}HAVE_FTIME${ac_uC}1${ac_uD}
${ac_eA}HAVE_FTIME${ac_eB}HAVE_FTIME${ac_eC}1${ac_eD}
${ac_dA}HAVE_GETTIMEOFDAY${ac_dB}HAVE_GETTIMEOFDAY${ac_dC}1${ac_dD}
${ac_uA}HAVE_GETTIMEOFDAY${ac_uB}HAVE_GETTIMEOFDAY${ac_uC}1${ac_uD}
${ac_eA}HAVE_GETTIMEOFDAY${ac_eB}HAVE_GETTIMEOFDAY${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_TIMEGM${ac_dB}HAVE_TIMEGM${ac_dC}1${ac_dD}
${ac_uA}HAVE_TIMEGM${ac_uB}HAVE_TIMEGM${ac_uC}1${ac_uD}
${ac_eA}HAVE_TIMEGM${ac_eB}HAVE_TIMEGM${ac_eC}1${ac_eD}
${ac_dA}HAVE_MEMCPY${ac_dB}HAVE_MEMCPY${ac_dC}1${ac_dD}
${ac_uA}HAVE_MEMCPY${ac_uB}HAVE_MEMCPY${ac_uC}1${ac_uD}
${ac_eA}HAVE_MEMCPY${ac_eB}HAVE_MEMCPY${ac_eC}1${ac_eD}
${ac_dA}HAVE_BCOPY${ac_dB}HAVE_BCOPY${ac_dC}1${ac_dD}
${ac_uA}HAVE_BCOPY${ac_uB}HAVE_BCOPY${ac_uC}1${ac_uD}
${ac_eA}HAVE_BCOPY${ac_eB}HAVE_BCOPY${ac_eC}1${ac_eD}
${ac_dA}HAVE_MKTIME${ac_dB}HAVE_MKTIME${ac_dC}1${ac_dD}
${ac_uA}HAVE_MKTIME${ac_uB}HAVE_MKTIME${ac_uC}1${ac_uD}
${ac_eA}HAVE_MKTIME${ac_eB}HAVE_MKTIME${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_STRSTR${ac_dB}HAVE_STRSTR${ac_dC}1${ac_dD}
${ac_uA}HAVE_STRSTR${ac_uB}HAVE_STRSTR${ac_uC}1${ac_uD}
${ac_eA}HAVE_STRSTR${ac_eB}HAVE_STRSTR${ac_eC}1${ac_eD}
${ac_dA}HAVE_RAND${ac_dB}HAVE_RAND${ac_dC}1${ac_dD}
${ac_uA}HAVE_RAND${ac_uB}HAVE_RAND${ac_uC}1${ac_uD}
${ac_eA}HAVE_RAND${ac_eB}HAVE_RAND${ac_eC}1${ac_eD}
${ac_dA}HAVE_RANDOM${ac_dB}HAVE_RANDOM${ac_dC}1${ac_dD}
${ac_uA}HAVE_RANDOM${ac_uB}HAVE_RANDOM${ac_uC}1${ac_uD}
${ac_eA}HAVE_RANDOM${ac_eB}HAVE_RANDOM${ac_eC}1${ac_eD}
${ac_dA}HAVE_SETITIMER${ac_dB}HAVE_SETITIMER${ac_dC}1${ac_dD}
${ac_uA}HAVE_SETITIMER${ac_uB}HAVE_SETITIMER${ac_uC}1${ac_uD}
${ac_eA}HAVE_SETITIMER${ac_eB}HAVE_SETITIMER${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_GETITIMER${ac_dB}HAVE_GETITIMER${ac_dC}1${ac_dD}
${ac_uA}HAVE_GETITIMER${ac_uB}HAVE_GETITIMER${ac_uC}1${ac_uD}
${ac_eA}HAVE_GETITIMER${ac_eB}HAVE_GETITIMER${ac_eC}1${ac_eD}
${ac_dA}HAVE_SIGNAL${ac_dB}HAVE_SIGNAL${ac_dC}1${ac_dD}
${ac_uA}HAVE_SIGNAL${ac_uB}HAVE_SIGNAL${ac_uC}1${ac_uD}
${ac_eA}HAVE_SIGNAL${ac_eB}HAVE_SIGNAL${ac_eC}1${ac_eD}
${ac_dA}HAVE_RINT${ac_dB}HAVE_RINT${ac_dC}1${ac_dD}
${ac_uA}HAVE_RINT${ac_uB}HAVE_RINT${ac_uC}1${ac_uD}
${ac_eA}HAVE_RINT${ac_eB}HAVE_RINT${ac_eC}1${ac_eD}
${ac_dA}HAVE_GETRUSAGE${ac_dB}HAVE_GETRUSAGE${ac_dC}1${ac_dD}
${ac_uA}HAVE_GETRUSAGE${ac_uB}HAVE_GETRUSAGE${ac_uC}1${ac_uD}
${ac_eA}HAVE_GETRUSAGE${ac_eB}HAVE_GETRUSAGE${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
CEOF
sed -f conftest.frag conftest.in > 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

4298
configure vendored Executable file

File diff suppressed because it is too large Load diff

275
configure.in Normal file
View file

@ -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

183
detect.c Normal file
View file

@ -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;n<length;n++,tempHitList++)
{
p0=ilist[n].p0;
p1=ilist[n].p1;
p2=ilist[n].p2;
temp=ilist;
tempEnd=ilist+length;
while(temp<tempEnd)
{USHORT p;
// The following piece of code accounts for 99% of the time when converting large models
nr=2; // count-down, we only want the ones with two points the same
mask=0;
if ((p=temp->p0)==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<length;p++)
{
if(HitList[p].hits==trynr&&!HitList[p].Stripped)
{
n=p; // n is first triangle of possible strip
i=0; // i is triangle-counter of this possible strip
k=10; // found a matching triangle
while(k>=10) // while found a triangle
{for (k=0;k<trynr;k++) // try all possible triangles
{next=HitList[n].t[k]; // possible triangle
if(!HitList[next].Stripped) // not included yet ?
{if(!i) // if testing with first triangle, it must be rotated so that
// points that are the same as next triangle are p1 and p2
{switch (HitList[n].mask[k]&0x0f){
case 6:
lastp0=ilist[p].p0; // lastp0-p2 is first triangle of strip
lastp1=ilist[p].p1; // is a local triangle that defines the last
lastp2=ilist[p].p2; // triangle added to the strip
break;
case 3:
lastp1=ilist[p].p0;
lastp2=ilist[p].p1;
lastp0=ilist[p].p2;
break;
case 5:
lastp0=ilist[p].p1;
lastp1=ilist[p].p2;
lastp2=ilist[p].p0;
break;
}
stripindex[*nr_indices]=lastp0; //save
stripindex[(*nr_indices)+1]=lastp1;
stripindex[(*nr_indices)+2]=lastp2;
}
if(i&1) // odd or even, makes a difference, see OpenGL
{if (HitList[n].p[k].p0==lastp0&&HitList[n].p[k].p1==lastp2) //new one fits ?
{lastp0=HitList[n].p[k].p0; // update last triangle used
lastp1=HitList[n].p[k].p1;
lastp2=HitList[n].p[k].p2;
HitList[next].Stripped=TRUE; //this one is done
stripindex[(*nr_indices)++]=lastp2; //save, p2 defines this triangle
n=next; //use this one as next source
k=10; // break for-loop with k, found one
i++;
} }
else
{if (HitList[n].p[k].p0==lastp2&&HitList[n].p[k].p1==lastp1) //new one fits ?
{lastp0=HitList[n].p[k].p0; // update last triangle used
lastp1=HitList[n].p[k].p1;
lastp2=HitList[n].p[k].p2;
HitList[next].Stripped=TRUE; //this one is done
if(!i++)
{
(*nr_indices)+=3; //first triangle had 3 indices and keep it
HitList[n].Stripped=TRUE; //and is done also
}
stripindex[(*nr_indices)++]=lastp2; // save, p2 defines this triangle
n=next; //use this one as next source
k=10; // break for-loop with k, found one
} } } } }
i++; //actual number of triangles in strip is one more
if(i>1)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<length;p++)
{if(!HitList[p].Stripped)
{
ilist[i++]=ilist[p];
}
}
GlobalFree(HitList);
return(i);
}

26
scenery_version.hxx Normal file
View file

@ -0,0 +1,26 @@
// scenery_version.hxx -- Scenery file format version
//
// Written by Curtis Olson, started March 1999.
//
// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// $Id$
#define FG_SCENERY_FILE_FORMAT "0.4"

View file

@ -0,0 +1,47 @@
#---------------------------------------------------------------------------
# Makefile
#
# Written by Curtis Olson, started January 1998.
#
# Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# $Id$
#---------------------------------------------------------------------------
bin_PROGRAMS = genapts
genapts_SOURCES = \
build.cxx build.hxx \
convex_hull.cxx convex_hull.hxx \
main.cxx \
point2d.cxx point2d.hxx \
runway.cxx runway.hxx
genapts_LDADD = \
$(top_builddir)/Lib/Array/libArray.a \
$(top_builddir)/Lib/Polygon/libPolygon.a \
$(top_builddir)/Lib/poly2tri/libpoly2tri.a \
$(top_builddir)/Lib/Build/libBuild.a \
-lsgbucket -lsgdebug -lsgmath -lsgmisc -lz -lgpc \
$(base_LIBS)
INCLUDES += \
-I$(top_builddir) \
-I$(top_builddir)/Tools \
-I$(top_builddir)/Lib \
-I$(top_builddir)/Tools/Lib

File diff suppressed because it is too large Load diff

View file

@ -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 <list>
#include "point2d.hxx"
// build 3d airport
void build_airport( string airport, string_list& runways_raw,
const string& root );
#endif // _BUILD_HXX

View file

@ -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 <math.h>
#include <stdio.h>
#include <map>
#ifdef NEEDNAMESPACESTD
using namespace std;
#endif
#include <simgear/constants.h>
#include "convex_hull.hxx"
#include "point2d.hxx"
// stl map typedefs
typedef map < double, double, less<double> > map_container;
typedef map_container::iterator map_iterator;
// Calculate theta of angle (a, b, c)
double calc_angle(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;
}

View file

@ -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 <list>
#ifdef NEEDNAMESPACESTD
using namespace std;
#endif
#include <simgear/fg_types.hxx>
#include <Polygon/polygon.hxx>
#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

View file

@ -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 <config.h>
#endif
#include <simgear/compiler.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <list>
#include <stdio.h>
#include <string.h>
#include STL_STRING
#include <simgear/newbucket.hxx>
#include <simgear/logstream.hxx>
#include <simgear/constants.h>
#include <simgear/fgstream.hxx>
#include <Polygon/index.hxx>
#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] << " <apt_file> <work_dir>" );
exit(-1);
}
// make work directory
string work_dir = argv[2];
string command = "mkdir -p " + work_dir;
system( command.c_str() );
// initialize persistant polygon counter
string counter_file = work_dir + "/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;
}

View file

@ -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 <math.h>
#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;
}

View file

@ -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 <simgear/fg_types.hxx>
#include <simgear/point3d.hxx>
// convert a point from cartesian to polar coordinates
Point3D cart_to_polar_2d(const Point3D& in);
#endif // _POINT2D_HXX

View file

@ -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 <math.h>
#include <stdio.h>
#include <simgear/constants.h>
#include <simgear/fg_types.hxx>
#include <simgear/fg_geodesy.hxx>
#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;
}

View file

@ -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 <string>
#include <vector>
#include <simgear/point3d.hxx>
#include <Polygon/polygon.hxx>
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

View file

@ -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

View file

@ -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 <simgear/logstream.hxx>
#include <simgear/constants.h>
#include <simgear/fgstream.hxx>
#include <Polygon/names.hxx>
#include "clipper.hxx"
// Constructor
FGClipper::FGClipper( void ) {
}
// Destructor
FGClipper::~FGClipper( void ) {
}
// Initialize Clipper (allocate and/or connect structures)
bool FGClipper::init() {
// v_list.num_vertices = 0;
// v_list.vertex = new gpc_vertex[FG_MAX_VERTICES];;
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;
}

View file

@ -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 <simgear/compiler.h>
#include <simgear/fg_types.hxx>
#include <Polygon/polygon.hxx>
#include STL_STRING
#include <vector>
FG_USING_STD(string);
FG_USING_STD(vector);
// typedef vector < gpc_polygon * > gpcpoly_container;
// typedef gpcpoly_container::iterator gpcpoly_iterator;
// typedef gpcpoly_container::const_iterator const_gpcpoly_iterator;
#define FG_MAX_AREA_TYPES 20
#define EXTRA_SAFETY_CLIP
// #define FG_MAX_VERTICES 100000
class 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

View file

@ -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 <simgear/logstream.hxx>
#include <simgear/newbucket.hxx>
#include "clipper.hxx"
int main( int argc, char **argv ) {
point2d global_min, global_max;
fglog().setLogLevels( FG_ALL, FG_DEBUG );
global_min.x = global_min.y = 200;
global_max.y = global_max.x = -200;
FGClipper clipper;
clipper.init();
if ( argc < 2 ) {
FG_LOG( FG_CLIPPER, FG_ALERT, "Usage: " << argv[0]
<< " file1 file2 ..." );
exit(-1);
}
// process all specified polygon files
for ( int i = 1; i < argc; i++ ) {
string full_path = argv[i];
// determine bucket for this polygon
int pos = full_path.rfind("/");
string file_name = full_path.substr(pos + 1);
cout << "file name = " << file_name << endl;
pos = file_name.find(".");
string base_name = file_name.substr(0, pos);
cout << "base_name = " << base_name << endl;
long int index;
sscanf( base_name.c_str(), "%ld", &index);
FGBucket b(index);
cout << "bucket = " << b << endl;
// calculate bucket dimensions
point2d c, min, max;
c.x = b.get_center_lon();
c.y = b.get_center_lat();
double span = bucket_span(c.y);
if ( (c.y >= -89.0) && (c.y < 89.0) ) {
min.x = c.x - span / 2.0;
max.x = c.x + span / 2.0;
min.y = c.y - FG_HALF_BUCKET_SPAN;
max.y = c.y + FG_HALF_BUCKET_SPAN;
} else if ( c.y < -89.0) {
min.x = -90.0;
max.x = -89.0;
min.y = -180.0;
max.y = 180.0;
} else if ( c.y >= 89.0) {
min.x = 89.0;
max.x = 90.0;
min.y = -180.0;
max.y = 180.0;
} else {
FG_LOG ( FG_GENERAL, FG_ALERT,
"Out of range latitude in clip_and_write_poly() = "
<< c.y );
}
if ( min.x < global_min.x ) global_min.x = min.x;
if ( min.y < global_min.y ) global_min.y = min.y;
if ( max.x > global_max.x ) global_max.x = max.x;
if ( max.y > global_max.y ) global_max.y = max.y;
// finally, load the polygon(s) from this file
clipper.load_polys( full_path );
}
// do the clipping
clipper.clip_all(global_min, global_max);
FG_LOG( FG_CLIPPER, FG_INFO, "finished main" );
return 0;
}

View file

@ -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

View file

@ -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 <time.h>
#include <simgear/texcoord.hxx>
#include <Polygon/names.hxx>
#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], &center, &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;
}

View file

@ -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 <simgear/compiler.h>
#include STL_STRING
#include <simgear/fg_types.hxx>
#include <simgear/newbucket.hxx>
#include <simgear/fg_geodesy.hxx>
#include <simgear/point3d.hxx>
#include <Combine/genfans.hxx>
#include <Main/construct.hxx>
#include <Triangulate/triangle.hxx>
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

View file

@ -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

View file

@ -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() { }

View file

@ -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 <simgear/compiler.h>
#include STL_STRING
#include <simgear/newbucket.hxx>
#include <Build/trinodes.hxx>
#include <Build/trisegs.hxx>
#include <Clipper/clipper.hxx>
#include <Triangulate/trieles.hxx>
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

View file

@ -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 <sys/types.h> // for directory reading
#include <dirent.h> // for directory reading
#include <sys/time.h> // set mem allocation limit
#include <sys/resource.h> // set mem allocation limit
#include <unistd.h> // set mem allocation limit
#include <simgear/newbucket.hxx>
#include <simgear/constants.h>
#include <simgear/mat3.h>
#include <simgear/logstream.hxx>
#include <Array/array.hxx>
#include <Clipper/clipper.hxx>
#include <GenOutput/genobj.hxx>
#include <Match/match.hxx>
#include <Triangulate/triangle.hxx>
#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
<< " <min_tri_angle> <work_base> <output_base> tile_id" << endl;
cout << "Usage: " << name
<< " <min_tri_angle> <work_base> <output_base> 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;
}

View file

@ -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 <stdlib.h> // for system()
#include <sys/stat.h> // for stat()
#include <unistd.h> // for stat()
#include <string>
#include <simgear/newbucket.hxx>
// #include <Include/fg_constants.h>
// #include <Math/mat3.h>
// #include <Debug/logstream.hxx>
// #include <Array/array.hxx>
// #include <Clipper/clipper.hxx>
// #include <GenOutput/genobj.hxx>
// #include <Match/match.hxx>
// #include <Triangulate/triangle.hxx>
// 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 << " <work_base> <output_base>" << 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;
}
}

View file

@ -0,0 +1,9 @@
SUBDIRS = \
Triangulate \
Clipper \
Combine \
GenOutput \
Match \
Parallel \
Main

View file

@ -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

View file

@ -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 <config.h>
#endif
#include <simgear/fg_geodesy.hxx>
#include <simgear/point3d.hxx>
#include <simgear/fgstream.hxx>
#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;
}

View file

@ -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 <simgear/compiler.h>
#include <simgear/fg_types.hxx>
#include <simgear/newbucket.hxx>
#include <Main/construct.hxx>
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

View file

@ -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

View file

@ -0,0 +1,283 @@
/* remote_exec.c -- Written by Curtis Olson */
/* -- for CSci 5502 */
#ifdef HAVE_CONFIG_H
# include <Include/config.h>
#endif
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h> // BSD macro definitions
#endif
#include <sys/time.h> // FD_ISSET(), etc.
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <utmp.h>
#include <stdio.h>
#include <stdlib.h> // atoi()
#include <string.h> // bcopy()
#include <iostream>
#include <string>
#include <simgear/newbucket.hxx>
#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 );
}
}
}

View file

@ -0,0 +1,406 @@
// remote_server.c -- Written by Curtis Olson
// -- for CSci 5502
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h> // FD_ISSET(), etc.
#include <sys/stat.h> // for stat()
#include <time.h> // for time();
#include <unistd.h>
#include <sys/socket.h> // bind
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <simgear/newbucket.hxx>
#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 << " <work_base> <output_base> 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);
}
}
}
}

View file

@ -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

View file

@ -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 <Build/poly_support.hxx>
#include <Polygon/polygon.hxx>
#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;
}

View file

@ -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 <simgear/compiler.h>
#include <simgear/point3d.hxx>
#include <Array/array.hxx>
#include <Clipper/clipper.hxx>
#include <Main/construct.hxx>
#include <Build/trinodes.hxx>
#include <Build/trisegs.hxx>
#include <Polygon/names.hxx>
#include <Polygon/polygon.hxx>
#define REAL double
extern "C" {
#include <Triangle/triangle.h>
}
#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

View file

@ -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"

View file

@ -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 <simgear/compiler.h>
#include <vector>
FG_USING_STD(vector);
// a segment is two integer pointers into the node list
class FGTriEle {
int n1, n2, n3;
double attribute;
public:
// Constructor and destructor
inline FGTriEle( void ) { };
inline FGTriEle( int i1, int i2, int i3, double a ) {
n1 = i1; n2 = i2; n3 = i3; attribute = a;
}
inline ~FGTriEle( void ) { };
inline int get_n1() const { return n1; }
inline void set_n1( int i ) { n1 = i; }
inline int get_n2() const { return n2; }
inline void set_n2( int i ) { n2 = i; }
inline int get_n3() const { return n3; }
inline void set_n3( int i ) { n3 = i; }
inline double get_attribute() const { return attribute; }
inline void set_attribute( double a ) { attribute = a; }
};
typedef vector < FGTriEle > triele_list;
typedef triele_list::iterator triele_list_iterator;
typedef triele_list::const_iterator const_triele_list_iterator;
#endif // _TRIELES_HXX

208
src/Include/config.h Normal file
View file

@ -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 <sys/types.h> 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 <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1
/* Define if your <sys/time.h> 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 <sys/types.h> 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 <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1
/* Define if your <sys/time.h> 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 <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define if you have the <getopt.h> header file. */
#define HAVE_GETOPT_H 1
/* Define if you have the <gpc.h> header file. */
#define HAVE_GPC_H 1
/* Define if you have the <malloc.h> header file. */
#define HAVE_MALLOC_H 1
/* Define if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1
/* Define if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define if you have the <sys/timeb.h> header file. */
#define HAVE_SYS_TIMEB_H 1
/* Define if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define if you have the <values.h> header file. */
#define HAVE_VALUES_H 1
/* Define if you have the <winbase.h> header file. */
/* #undef HAVE_WINBASE_H */
/* Define if you have the <windows.h> 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 */

207
src/Include/config.h.in Normal file
View file

@ -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 <sys/types.h> 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 <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Define if your <sys/time.h> 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 <sys/types.h> 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 <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Define if your <sys/time.h> 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 <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define if you have the <getopt.h> header file. */
#undef HAVE_GETOPT_H
/* Define if you have the <gpc.h> header file. */
#undef HAVE_GPC_H
/* Define if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define if you have the <sys/param.h> header file. */
#undef HAVE_SYS_PARAM_H
/* Define if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define if you have the <sys/timeb.h> header file. */
#undef HAVE_SYS_TIMEB_H
/* Define if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define if you have the <values.h> header file. */
#undef HAVE_VALUES_H
/* Define if you have the <winbase.h> header file. */
#undef HAVE_WINBASE_H
/* Define if you have the <windows.h> 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

1
src/Include/stamp-h Normal file
View file

@ -0,0 +1 @@
timestamp

17
src/Lib/Array/Makefile.am Normal file
View file

@ -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

577
src/Lib/Array/array.cxx Normal file
View file

@ -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 <config.h>
#endif
#include <simgear/compiler.h>
// #include <ctype.h> // isspace()
// #include <stdlib.h> // atoi()
// #include <math.h> // rint()
// #include <stdio.h>
// #include <string.h>
// #ifdef HAVE_SYS_STAT_H
// # include <sys/stat.h> // stat()
// #endif
// #ifdef FG_HAVE_STD_INCLUDES
// # include <cerrno>
// #else
// # include <errno.h>
// #endif
// #ifdef HAVE_UNISTD_H
// # include <unistd.h> // stat()
// #endif
#include STL_STRING
#include <simgear/constants.h>
#include <simgear/fgstream.hxx>
#include <simgear/strutils.hxx>
#include <simgear/leastsqs.hxx>
#include "array.hxx"
FG_USING_STD(string);
FGArray::FGArray( void ) {
// cout << "class FGArray CONstructor called." << endl;
in_data = new float[ARRAY_SIZE_1][ARRAY_SIZE_1];
// out_data = new float[ARRAY_SIZE_1][ARRAY_SIZE_1];
}
FGArray::FGArray( const string &file ) {
// cout << "class FGArray CONstructor called." << endl;
in_data = new float[ARRAY_SIZE_1][ARRAY_SIZE_1];
// out_data = new float[ARRAY_SIZE_1][ARRAY_SIZE_1];
FGArray::open(file);
}
// open an Array file
int
FGArray::open( const string& file ) {
// open input file (or read from stdin)
if ( file == "-" ) {
cout << " Opening array data pipe from stdin" << endl;
// fd = stdin;
// fd = gzdopen(STDIN_FILENO, "r");
cout << " Not yet ported ..." << endl;
return 0;
} else {
in = new fg_gzifstream( file );
if ( ! in->is_open() ) {
cout << " Cannot open " << file << endl;
return 0;
}
cout << " Opening array data file: " << file << endl;
}
return 1;
}
// close an Array file
int
FGArray::close() {
// the fg_gzifstream doesn't seem to have a close()
delete in;
return 1;
}
// parse Array file, pass in the bucket so we can make up values when
// the file wasn't found.
int
FGArray::parse( FGBucket& b ) {
if ( in->is_open() ) {
// file open, parse
*in >> originx >> originy;
*in >> cols >> col_step;
*in >> rows >> row_step;
cout << " origin = " << originx << " " << originy << endl;
cout << " cols = " << cols << " rows = " << rows << endl;
cout << " col_step = " << col_step
<< " row_step = " << row_step <<endl;
for ( int i = 0; i < cols; i++ ) {
for ( int j = 0; j < rows; j++ ) {
*in >> in_data[i][j];
}
}
cout << " Done parsing\n";
} else {
// file not open (not found?), fill with zero'd data
originx = ( b.get_center_lon() - 0.5 * b.get_width() ) * 3600.0;
originy = ( b.get_center_lat() - 0.5 * b.get_height() ) * 3600.0;
double max_x = ( b.get_center_lon() + 0.5 * b.get_width() ) * 3600.0;
double max_y = ( b.get_center_lat() + 0.5 * b.get_height() ) * 3600.0;
cols = 3;
col_step = (max_x - originx) / (cols - 1);
rows = 3;
row_step = (max_y - originy) / (rows - 1);
cout << " origin = " << originx << " " << originy << endl;
cout << " cols = " << cols << " rows = " << rows << endl;
cout << " col_step = " << col_step
<< " row_step = " << row_step <<endl;
for ( int i = 0; i < cols; i++ ) {
for ( int j = 0; j < rows; j++ ) {
in_data[i][j] = 0.0;
}
}
cout << " File not open, so using zero'd data" << endl;
}
return 1;
}
// add a node to the output corner node list
void FGArray::add_corner_node( int i, int j, double val ) {
double x = (originx + i * col_step) / 3600.0;
double y = (originy + j * row_step) / 3600.0;
// cout << "originx = " << originx << " originy = " << originy << endl;
cout << "corner = " << Point3D(x, y, val) << endl;
corner_list.push_back( Point3D(x, y, val) );
}
// add a node to the output fitted node list
void FGArray::add_fit_node( int i, int j, double val ) {
double x = (originx + i * col_step) / 3600.0;
double y = (originy + j * row_step) / 3600.0;
// cout << Point3D(x, y, val) << endl;
node_list.push_back( Point3D(x, y, val) );
}
// Use least squares to fit a simpler data set to dem data. Return
// the number of fitted nodes
int FGArray::fit( double error ) {
double x[ARRAY_SIZE_1], y[ARRAY_SIZE_1];
double m, b, max_error, error_sq;
double x1, y1;
// double ave_error;
double cury, lasty;
int n, row, start, end;
int colmin, colmax, rowmin, rowmax;
bool good_fit;
// FILE *dem, *fit, *fit1;
error_sq = error * error;
cout << " Initializing fitted node list" << endl;
corner_list.clear();
node_list.clear();
// determine dimensions
colmin = 0;
colmax = cols;
rowmin = 0;
rowmax = rows;
cout << " Fitting region = " << colmin << "," << rowmin << " to "
<< colmax << "," << rowmax << endl;;
// generate corners list
add_corner_node( colmin, rowmin, in_data[colmin][rowmin] );
add_corner_node( colmin, rowmax-1, in_data[colmin][rowmax] );
add_corner_node( colmax-1, rowmin, in_data[colmax][rowmin] );
add_corner_node( colmax-1, rowmax-1, in_data[colmax][rowmax] );
cout << " Beginning best fit procedure" << endl;
lasty = 0;
for ( row = rowmin; row < rowmax; row++ ) {
// fit = fopen("fit.dat", "w");
// fit1 = fopen("fit1.dat", "w");
start = colmin;
// cout << " fitting row = " << row << endl;
while ( start < colmax - 1 ) {
end = start + 1;
good_fit = true;
x[0] = start * col_step;
y[0] = in_data[start][row];
x[1] = end * col_step;
y[1] = in_data[end][row];
n = 2;
// cout << "Least square of first 2 points" << endl;
least_squares(x, y, n, &m, &b);
end++;
while ( (end < colmax) && good_fit ) {
++n;
// cout << "Least square of first " << n << " points" << endl;
x[n-1] = x1 = end * col_step;
y[n-1] = y1 = in_data[end][row];
least_squares_update(x1, y1, &m, &b);
// ave_error = least_squares_error(x, y, n, m, b);
max_error = least_squares_max_error(x, y, n, m, b);
/*
printf("%d - %d ave error = %.2f max error = %.2f y = %.2f*x + %.2f\n",
start, end, ave_error, max_error, m, b);
f = fopen("gnuplot.dat", "w");
for ( j = 0; j <= end; j++) {
fprintf(f, "%.2f %.2f\n", 0.0 + ( j * col_step ),
in_data[row][j]);
}
for ( j = start; j <= end; j++) {
fprintf(f, "%.2f %.2f\n", 0.0 + ( j * col_step ),
in_data[row][j]);
}
fclose(f);
printf("Please hit return: "); gets(junk);
*/
if ( max_error > error_sq ) {
good_fit = false;
}
end++;
}
if ( !good_fit ) {
// error exceeded the threshold, back up
end -= 2; // back "end" up to the last good enough fit
n--; // back "n" up appropriately too
} else {
// we popped out of the above loop while still within
// the error threshold, so we must be at the end of
// the data set
end--;
}
least_squares(x, y, n, &m, &b);
// ave_error = least_squares_error(x, y, n, m, b);
max_error = least_squares_max_error(x, y, n, m, b);
/*
printf("\n");
printf("%d - %d ave error = %.2f max error = %.2f y = %.2f*x + %.2f\n",
start, end, ave_error, max_error, m, b);
printf("\n");
fprintf(fit1, "%.2f %.2f\n", x[0], m * x[0] + b);
fprintf(fit1, "%.2f %.2f\n", x[end-start], m * x[end-start] + b);
*/
if ( start > colmin ) {
// skip this for the first line segment
cury = m * x[0] + b;
add_fit_node( start, row, (lasty + cury) / 2 );
// fprintf(fit, "%.2f %.2f\n", x[0], (lasty + cury) / 2);
}
lasty = m * x[end-start] + b;
start = end;
}
/*
fclose(fit);
fclose(fit1);
dem = fopen("gnuplot.dat", "w");
for ( j = 0; j < ARRAY_SIZE_1; j++) {
fprintf(dem, "%.2f %.2f\n", 0.0 + ( j * col_step ),
in_data[j][row]);
}
fclose(dem);
*/
// NOTICE, this is for testing only. This instance of
// output_nodes should be removed. It should be called only
// once at the end once all the nodes have been generated.
// newmesh_output_nodes(&nm, "mesh.node");
// printf("Please hit return: "); gets(junk);
}
// outputmesh_output_nodes(fg_root, p);
// return fit nodes + 4 corners
return node_list.size() + 4;
}
// return the current altitude based on grid data. We should rewrite
// this to interpolate exact values, but for now this is good enough
double FGArray::interpolate_altitude( double lon, double lat ) const {
// we expect incoming (lon,lat) to be in arcsec for now
double xlocal, ylocal, dx, dy, zA, zB, elev;
int x1, x2, x3, y1, y2, y3;
float z1, z2, z3;
int xindex, yindex;
/* determine if we are in the lower triangle or the upper triangle
______
| /|
| / |
| / |
|/ |
------
then calculate our end points
*/
xlocal = (lon - originx) / col_step;
ylocal = (lat - originy) / row_step;
xindex = (int)(xlocal);
yindex = (int)(ylocal);
// printf("xindex = %d yindex = %d\n", xindex, yindex);
if ( xindex + 1 == cols ) {
xindex--;
}
if ( yindex + 1 == rows ) {
yindex--;
}
if ( (xindex < 0) || (xindex + 1 >= cols) ||
(yindex < 0) || (yindex + 1 >= rows) ) {
cout << "WARNING: Attempt to interpolate value outside of array!!!"
<< endl;
return -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;
}

124
src/Lib/Array/array.hxx Normal file
View file

@ -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 <simgear/compiler.h>
#include <vector>
#include <simgear/fg_types.hxx>
#include <simgear/newbucket.hxx>
#include <simgear/point3d.hxx>
#include <simgear/fgstream.hxx>
FG_USING_STD(vector);
#define ARRAY_SIZE 1200
#define ARRAY_SIZE_1 1201
class FGArray {
private:
// file pointer for input
// gzFile fd;
fg_gzifstream *in;
// coordinates (in arc seconds) of south west corner
double originx, originy;
// number of columns and rows
int cols, rows;
// Distance between column and row data points (in arc seconds)
double col_step, row_step;
// pointers to the actual grid data allocated here
float (*in_data)[ARRAY_SIZE_1];
// float (*out_data)[ARRAY_SIZE_1];
// output nodes
point_list corner_list;
point_list node_list;
public:
// Constructor
FGArray( void );
FGArray( const string& file );
// Destructor
~FGArray( void );
// open an Array file (use "-" if input is coming from stdin)
int open ( const string& file );
// close a Array file
int close();
// parse a Array file
int parse( FGBucket& b );
// Use least squares to fit a simpler data set to dem data.
// Return the number of fitted nodes
int fit( double error );
// add a node to the output corner node list
void add_corner_node( int i, int j, double val );
// add a node to the output fitted node list
void add_fit_node( int i, int j, double val );
// return the current altitude based on grid data. We should
// rewrite this to interpolate exact values, but for now this is
// good enough
double interpolate_altitude( double lon, double lat ) const;
// Informational methods
inline double get_originx() const { return originx; }
inline double get_originy() const { return originy; }
inline int get_cols() const { return cols; }
inline int get_rows() const { return rows; }
inline double get_col_step() const { return col_step; }
inline double get_row_step() const { return row_step; }
inline point_list get_corner_node_list() const { return corner_list; }
inline point_list get_fit_node_list() const { return node_list; }
};
#endif // _ARRAY_HXX

View file

@ -0,0 +1,33 @@
#include <simgear/newbucket.hxx>
#include "array.hxx"
main(int argc, char **argv) {
double lon, lat;
if ( argc != 2 ) {
cout << "Usage: " << argv[0] << " work_dir" << endl;
exit(-1);
}
string work_dir = argv[1];
lon = -146.248360; lat = 61.133950; // PAVD (Valdez, AK)
lon = -110.664244; lat = 33.352890; // P13
FGBucket b( lon, lat );
string base = b.gen_base_path();
string path = work_dir + "/" + 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 );
}

11
src/Lib/DEM/Makefile.am Normal file
View file

@ -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

871
src/Lib/DEM/dem.cxx Normal file
View file

@ -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 <config.h>
#endif
#include <simgear/compiler.h>
#include <ctype.h> // isspace()
#include <stdlib.h> // atoi()
#include <math.h> // rint()
#include <stdio.h>
#include <string.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h> // stat()
#endif
#ifdef FG_HAVE_STD_INCLUDES
# include <cerrno>
#else
# include <errno.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h> // stat()
#endif
#include <simgear/fgstream.hxx>
#include <simgear/strutils.hxx>
#include <simgear/constants.h>
#include "dem.hxx"
#define MAX_EX_NODES 10000
#if 0
#ifdef WIN32
# ifdef __BORLANDC__
# include <dir.h>
# define MKDIR(a) mkdir(a)
# else
# define MKDIR(a) mkdir(a,S_IRWXU) // I am just guessing at this flag (NHV)
# endif // __BORLANDC__
#endif // WIN32
#endif //0
FGDem::FGDem( void ) {
// cout << "class FGDem CONstructor called." << endl;
dem_data = new float[DEM_SIZE_1][DEM_SIZE_1];
output_data = new float[DEM_SIZE_1][DEM_SIZE_1];
}
FGDem::FGDem( const string &file ) {
// cout << "class FGDem CONstructor called." << endl;
dem_data = new float[DEM_SIZE_1][DEM_SIZE_1];
output_data = new float[DEM_SIZE_1][DEM_SIZE_1];
FGDem::open(file);
}
// open a DEM file
int
FGDem::open ( const string& file ) {
// open input file (or read from stdin)
if ( file == "-" ) {
printf("Loading DEM data file: stdin\n");
// fd = stdin;
// fd = gzdopen(STDIN_FILENO, "r");
printf("Not yet ported ...\n");
return 0;
} else {
in = new fg_gzifstream( file );
if ( !(*in) ) {
cout << "Cannot open " << file << endl;
return 0;
}
cout << "Loading DEM data file: " << file << endl;
}
return 1;
}
// close a DEM file
int
FGDem::close () {
// the fg_gzifstream doesn't seem to have a close()
delete in;
return 1;
}
// return next token from input stream
string
FGDem::next_token() {
string token;
*in >> token;
// cout << " returning " + token + "\n";
return token;
}
// return next integer from input stream
int
FGDem::next_int() {
int result;
*in >> result;
return result;
}
// return next double from input stream
double
FGDem::next_double() {
double result;
*in >> result;
return result;
}
// return next exponential num from input stream
double
FGDem::next_exp() {
string token;
token = next_token();
const char* p = token.c_str();
char buf[64];
char* bp = buf;
for ( ; *p != 0; ++p )
{
if ( *p == 'D' )
*bp++ = 'E';
else
*bp++ = *p;
}
*bp = 0;
return ::atof( buf );
}
// read and parse DEM "A" record
int
FGDem::read_a_record() {
int i, inum;
double dnum;
string name, token;
char c;
// get the name field (144 characters)
for ( i = 0; i < 144; i++ ) {
in->get(c);
name += c;
}
// clean off the trailing whitespace
name = trim(name);
cout << " Quad name field: " << name << endl;
// DEM level code, 3 reflects processing by DMA
inum = next_int();
cout << " DEM level code = " << inum << "\n";
if ( inum > 3 ) {
return 0;
}
// Pattern code, 1 indicates a regular elevation pattern
inum = next_int();
cout << " Pattern code = " << inum << "\n";
// Planimetric reference system code, 0 indicates geographic
// coordinate system.
inum = next_int();
cout << " Planimetric reference code = " << inum << "\n";
// Zone code
inum = next_int();
cout << " Zone code = " << inum << "\n";
// Map projection parameters (ignored)
for ( i = 0; i < 15; i++ ) {
dnum = next_exp();
// printf("%d: %f\n",i,dnum);
}
// Units code, 3 represents arc-seconds as the unit of measure for
// ground planimetric coordinates throughout the file.
inum = next_int();
if ( inum != 3 ) {
cout << " Unknown (X,Y) units code = " << inum << "!\n";
exit(-1);
}
// Units code; 2 represents meters as the unit of measure for
// elevation coordinates throughout the file.
inum = next_int();
if ( inum != 2 ) {
cout << " Unknown (Z) units code = " << inum << "!\n";
exit(-1);
}
// Number (n) of sides in the polygon which defines the coverage of
// the DEM file (usually equal to 4).
inum = next_int();
if ( inum != 4 ) {
cout << " Unknown polygon dimension = " << inum << "!\n";
exit(-1);
}
// Ground coordinates of bounding box in arc-seconds
dem_x1 = originx = next_exp();
dem_y1 = originy = next_exp();
cout << " Origin = (" << originx << "," << originy << ")\n";
dem_x2 = next_exp();
dem_y2 = next_exp();
dem_x3 = next_exp();
dem_y3 = next_exp();
dem_x4 = next_exp();
dem_y4 = next_exp();
// Minimum/maximum elevations in meters
dem_z1 = next_exp();
dem_z2 = next_exp();
cout << " Elevation range " << dem_z1 << " to " << dem_z2 << "\n";
// Counterclockwise angle from the primary axis of ground
// planimetric referenced to the primary axis of the DEM local
// reference system.
token = next_token();
// Accuracy code; 0 indicates that a record of accuracy does not
// exist and that no record type C will follow.
// DEM spacial resolution. Usually (3,3,1) (3,6,1) or (3,9,1)
// depending on latitude
// I will eventually have to do something with this for data at
// higher latitudes */
token = next_token();
cout << " accuracy & spacial resolution string = " << token << endl;
i = token.length();
cout << " length = " << i << "\n";
inum = atoi( token.substr( 0, i - 36 ) );
row_step = atof( token.substr( i - 24, 12 ) );
col_step = atof( token.substr( i - 36, 12 ) );
cout << " Accuracy code = " << inum << "\n";
cout << " column step = " << col_step <<
" row step = " << row_step << "\n";
// dimension of arrays to follow (1)
token = next_token();
// number of profiles
dem_num_profiles = cols = next_int();
cout << " Expecting " << dem_num_profiles << " profiles\n";
return 1;
}
// read and parse DEM "B" record
void
FGDem::read_b_record( ) {
string token;
int i;
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;
}

154
src/Lib/DEM/dem.hxx Normal file
View file

@ -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 <simgear/newbucket.hxx>
#include <simgear/fgstream.hxx>
#define DEM_SIZE 1200
#define DEM_SIZE_1 1201
class FGDem {
private:
// file pointer for input
// gzFile fd;
fg_gzifstream *in;
// coordinates (in arc seconds) of south west corner
double originx, originy;
// number of columns and rows
int cols, rows;
// Distance between column and row data points (in arc seconds)
double col_step, row_step;
// pointers to the actual grid data allocated here
float (*dem_data)[DEM_SIZE_1];
float (*output_data)[DEM_SIZE_1];
// Current "A" Record Information
char dem_description[80], dem_quadrangle[80];
double dem_x1, dem_y1, dem_x2, dem_y2, dem_x3, dem_y3, dem_x4, dem_y4;
double dem_z1, dem_z2;
int dem_resolution, dem_num_profiles;
// Current "B" Record Information
int prof_col, prof_row;
int prof_num_cols, prof_num_rows;
double prof_x1, prof_y1;
int prof_data;
// temporary values for the class to use
char option_name[32];
int do_data;
int cur_col, cur_row;
// return next token from input stream
string next_token();
// return next integer from input stream
int next_int();
// return next double from input stream
double next_double();
// return next exponential num from input stream
double next_exp();
public:
// Constructor
FGDem( void );
FGDem( const string& file );
// Destructor
~FGDem( void );
// open a DEM file (use "-" if input is coming from stdin)
int open ( const string& file );
// close a DEM file
int close();
// parse a DEM file
int parse();
// read and parse DEM "A" record
int read_a_record();
// read and parse DEM "B" record
void read_b_record();
// write out the area of data covered by the specified bucket.
// Data is written out column by column starting at the lower left
// hand corner.
int write_area( const string& root, FGBucket& b, bool compress );
#if 0
// return the current altitude based on grid data. We should
// rewrite this to interpolate exact values, but for now this is
// good enough
double interpolate_altitude( double lon, double lat );
// Use least squares to fit a simpler data set to dem data
void fit( double error, FGBucket& p );
// Initialize output mesh structure
void outputmesh_init( void );
// Get the value of a mesh node
double outputmesh_get_pt( int i, int j );
// Set the value of a mesh node
void outputmesh_set_pt( int i, int j, double value );
// Write out a node file that can be used by the "triangle" program
void outputmesh_output_nodes( const string& fg_root, FGBucket& p );
#endif
// Informational methods
inline double get_originx() const { return originx; }
inline double get_originy() const { return originy; }
inline int get_cols() const { return cols; }
inline int get_rows() const { return rows; }
inline double get_col_step() const { return col_step; }
inline double get_row_step() const { return row_step; }
};
#endif // _DEM_HXX

View file

@ -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

View file

@ -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 <simgear/compiler.h>
#include <simgear/fg_types.hxx>
#include <Polygon/polygon.hxx>
#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 <simgear/constants.h>
#include <simgear/point3d.hxx>
#include <Build/trisegs.hxx>
// 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;
}

View file

@ -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 <simgear/compiler.h>
#include <simgear/fg_types.hxx>
#include <Polygon/polygon.hxx>
#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

View file

@ -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;
}

View file

@ -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 <simgear/compiler.h>
#include <simgear/fg_types.hxx>
#include <simgear/point3d.hxx>
#define FG_PROXIMITY_EPSILON 0.000001
#define FG_COURSE_EPSILON 0.0003
class FGTriNodes {
private:
point_list node_list;
// return true of the two points are "close enough" as defined by
// FG_PROXIMITY_EPSILON
bool close_enough( const Point3D& p, const Point3D& p ) 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

View file

@ -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 <simgear/constants.h>
#include <simgear/point3d.hxx>
#include "trinodes.hxx"
#include "trisegs.hxx"
// Constructor
FGTriSegments::FGTriSegments( void ) {
}
// Destructor
FGTriSegments::~FGTriSegments( void ) {
}
// Add a segment to the segment list if it doesn't already exist.
// Returns the index (starting at zero) of the segment in the list.
int FGTriSegments::unique_add( const FGTriSeg& s )
{
triseg_list_iterator current, last;
int counter = 0;
// cout << s.get_n1() << "," << s.get_n2() << endl;
// check if segment 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 );
}
}

View file

@ -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 <simgear/compiler.h>
#include <vector>
#include "trinodes.hxx"
FG_USING_STD(vector);
// a segment is two integer pointers into the node list
class FGTriSeg {
int n1, n2; // 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

8
src/Lib/Makefile.am Normal file
View file

@ -0,0 +1,8 @@
SUBDIRS = \
Array \
Build \
DEM \
Polygon \
poly2tri \
shapelib \
Triangle

View file

@ -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

View file

@ -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();
}

View file

@ -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 <simgear/compiler.h>
#include <vector>
#include <simgear/fg_types.hxx>
#include <Triangulate/trieles.hxx>
FG_USING_STD(vector);
typedef vector < int_list > fan_list;
typedef fan_list::iterator fan_list_iterator;
typedef fan_list::const_iterator const_fan_list_iterator;
typedef vector < int_list > reverse_list;
typedef reverse_list::iterator reverse_list_iterator;
typedef reverse_list::const_iterator const_reverse_list_iterator;
class FGGenFans {
private:
fan_list fans;
// make sure the list is expanded at least to hold "n" and then
// push "i" onto the back of the "n" list.
void add_and_expand( reverse_list& by_node, int n, int i );
public:
// Constructor && Destructor
inline FGGenFans() { }
inline ~FGGenFans() { }
// recursive build fans from triangle list
// fan_list greedy_build( triele_list tris );
fan_list greedy_build( triele_list tris );
// report average fan size
double ave_size();
};
#endif // _GENFANS_HXX

View file

@ -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

71
src/Lib/Polygon/index.cxx Normal file
View file

@ -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 <simgear/compiler.h>
#include STL_STRING
#include <stdio.h>
#include "index.hxx"
static long int poly_index;
static string poly_path;
// initialize the unique polygon index counter stored in path
bool poly_index_init( string path ) {
poly_path = path;
FILE *fp = fopen( poly_path.c_str(), "r" );
if ( fp == NULL ) {
cout << "Error cannot open " << poly_path << endl;
poly_index = 0;
return false;
}
fscanf( fp, "%ld", &poly_index );
fclose( fp );
}
// increment the persistant counter and return the next poly_index
long int poly_index_next() {
++poly_index;
FILE *fp = fopen( poly_path.c_str(), "w" );
if ( fp == NULL ) {
cout << "Error cannot open " << poly_path << " for writing" << endl;
}
fprintf( fp, "%ld\n", poly_index );
fclose( fp );
return poly_index;
}

43
src/Lib/Polygon/index.hxx Normal file
View file

@ -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 <simgear/compiler.h>
#include STL_STRING
// initialize the unique polygon index counter stored in path
bool poly_index_init( string path );
// increment the persistant counter and return the next poly_index
long int poly_index_next();
#endif // _INDEX_HXX

118
src/Lib/Polygon/names.cxx Normal file
View file

@ -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 <simgear/compiler.h>
#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";
}
}

68
src/Lib/Polygon/names.hxx Normal file
View file

@ -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 <simgear/compiler.h>
#include STL_STRING
FG_USING_STD(string);
// Posible shape file types. Note the order of these is important and
// defines the priority of these shapes if they should intersect. The
// smaller the number, the higher the priority.
enum AreaType {
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

493
src/Lib/Polygon/polygon.cxx Normal file
View file

@ -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 <gpc.h>
}
#include <simgear/constants.h>
#include <simgear/point3d.hxx>
#include <poly2tri/interface.h>
#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

189
src/Lib/Polygon/polygon.hxx Normal file
View file

@ -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 <simgear/compiler.h>
#include <simgear/fg_types.hxx>
#include <string>
#include <vector>
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

233
src/Lib/Polygon/split.cxx Normal file
View file

@ -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 <simgear/compiler.h>
#include STL_STRING
#include <simgear/newbucket.hxx>
#include <simgear/logstream.hxx>
#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;
}
}

38
src/Lib/Polygon/split.hxx Normal file
View file

@ -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

View file

@ -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

181
src/Lib/TriangleJRS/README Normal file
View file

@ -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

3384
src/Lib/TriangleJRS/showme.c Normal file

File diff suppressed because it is too large Load diff

13241
src/Lib/TriangleJRS/triangle.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -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 */

View file

@ -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

96
src/Lib/poly2tri/README Normal file
View file

@ -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 <filename> /* 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)

1055
src/Lib/poly2tri/construct.c Normal file

File diff suppressed because it is too large Load diff

24
src/Lib/poly2tri/data_1 Normal file
View file

@ -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

View file

@ -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 */

157
src/Lib/poly2tri/misc.c Normal file
View file

@ -0,0 +1,157 @@
#include <triangulate.h>
#include <sys/time.h>
#include <math.h>
#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);
}

737
src/Lib/poly2tri/monotone.c Normal file
View file

@ -0,0 +1,737 @@
#include <triangulate.h>
#include <math.h>
#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;
}

167
src/Lib/poly2tri/tri.c Normal file
View file

@ -0,0 +1,167 @@
#include <triangulate.h>
#include <sys/time.h>
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 <filename>\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 */

View file

@ -0,0 +1,159 @@
#ifndef _triangulate_h
#define _triangulate_h
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
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 */

Binary file not shown.

View file

@ -0,0 +1,6 @@
Tue May 4 11:04:31 1999 Frank Warmerdam <warmerda@gdal.velocet.ca>
* Prepare 1.2.5 release.
* Added support for 'F' fields.

View file

@ -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

View file

@ -0,0 +1,322 @@
<html>
<head>
<title>Attribute (.DBF) API</title>
</head>
<body>
<h1>Attribute (.DBF) API</h1>
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.<p>
<!-------------------------------------------------------------------------->
<h2>DBFOpen()</h2>
<pre>
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.
</pre>
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.<p>
<!-------------------------------------------------------------------------->
<h2>DBFCreate()</h2>
<pre>
DBFHandle DBFCreate( const char * pszDBFFile );
pszDBFFile: The name of the xBase (.dbf) file to create.
</pre>
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.
<!-------------------------------------------------------------------------->
<h2>DBFGetFieldCount()</h2>
<pre>
int DBFGetFieldCount( DBFHandle hDBF );
hDBF: The access handle for the file to be queried, as returned
by DBFOpen(), or DBFCreate().
</pre>
The DBFGetFieldCount() function returns the number of fields currently
defined for the indicated xBase file.
<!-------------------------------------------------------------------------->
<h2>DBFGetRecordCount()</h2>
<pre>
int DBFGetRecordCount( DBFHandle hDBF );
hDBF: The access handle for the file to be queried, as returned by
DBFOpen(), or DBFCreate().
</pre>
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.<p>
<!-------------------------------------------------------------------------->
<h2>DBFGetFieldInfo()</h2>
<pre>
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.
</pre>
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. <p>
<pre>
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;
</pre>
<!-------------------------------------------------------------------------->
<h2>DBFAddField()</h2>
<pre>
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'.
</pre>
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.<p>
The DBFAddField() return value is the field number of the new field, or
-1 if the addition of the field failed.<p>
<!-------------------------------------------------------------------------->
<h2>DBFReadIntegerAttribute()</h2>
<pre>
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.
</pre>
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.<p>
<!-------------------------------------------------------------------------->
<h2>DBFReadDoubleAttribute()</h2>
<pre>
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.
</pre>
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.<p>
<!-------------------------------------------------------------------------->
<h2>DBFReadStringAttribute()</h2>
<pre>
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.
</pre>
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.<p>
<!-------------------------------------------------------------------------->
<h2>DBFWriteIntegerAttribute</h2>
<pre>
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.
</pre>
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.<p>
<!-------------------------------------------------------------------------->
<h2>DBFWriteDoubleAttribute()</h2>
<pre>
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.
</pre>
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.<p>
<!-------------------------------------------------------------------------->
<h2>DBFWriteStringAttribute()</h2>
<pre>
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.
</pre>
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.<p>
<!-------------------------------------------------------------------------->
<h2>DBFClose()</h2>
<pre>
void DBFClose( DBFHandle hDBF );
hDBF: The access handle for the file to be closed.
</pre>
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().<p>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show more