1
0
Fork 0

Added new independent classes for a bounding rectangle and a

multi-segment line, then pulled some local code out of tgvpf into
util.[ch]xx so that it can be used by other modules.
This commit is contained in:
david 2002-07-22 22:01:39 +00:00
parent 6bd8a08f8b
commit f30ffc0d1f
9 changed files with 571 additions and 194 deletions

View file

@ -2,9 +2,12 @@ noinst_LIBRARIES = libGeometry.a
libGeometry_a_SOURCES = \
contour_tree.cxx contour_tree.hxx \
line.cxx line.hxx \
poly_support.cxx poly_support.hxx \
rectangle.cxx rectangle.hxx \
trinodes.cxx trinodes.hxx \
trisegs.cxx trisegs.hxx
trisegs.cxx trisegs.hxx \
util.cxx util.hxx
INCLUDES += \
-I$(top_srcdir)/src \

46
src/Lib/Geometry/line.cxx Normal file
View file

@ -0,0 +1,46 @@
// line.cxx - a simple multi-segment line class.
//
// Started by David Megginson, July 2002
//
// This file is in the Public Domain and comes with NO WARRANTY OF ANY KIND.
#include "line.hxx"
Line::Line ()
{
}
Line::Line (const Line &l)
{
}
Line::~Line ()
{
}
int
Line::getPointCount () const
{
return _points.size();
}
const Point3D &
Line::getPoint (int index) const
{
return _points[index];
}
Point3D &
Line::getPoint (int index)
{
return _points[index];
}
void
Line::addPoint (const Point3D &point)
{
_points.push_back(point);
}
// end of line.cxx

83
src/Lib/Geometry/line.hxx Normal file
View file

@ -0,0 +1,83 @@
// line.hxx - a simple multi-segment line class.
//
// Started by David Megginson, July 2002
//
// This file is in the Public Domain and comes with NO WARRANTY OF ANY KIND.
#ifndef __LINE_HXX
#define __LINE_HXX
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/compiler.h>
#include <simgear/math/point3d.hxx>
#include <vector>
SG_USING_STD(vector);
#include "rectangle.hxx"
/**
* A simple multi-segment line class.
*
* A line segment is a growable list of points. I will add more
* functionality if or when it is needed.
*/
class Line
{
public:
/**
* Create a new line with no points.
*/
Line ();
/**
* Copy an existing line.
*
* @param l The line to copy.
*/
Line (const Line &l);
/**
* Destructor.
*/
virtual ~Line ();
/**
* Get the number of points currently in the line.
*
* @return The point count.
*/
virtual int getPointCount () const;
/**
* Get a point in the line (const).
*
* @param index The index of the point, zero-based.
* @return The point at the index specified.
*/
virtual const Point3D &getPoint (int index) const;
/**
* Get a point in the line (non-const).
*
* @param index The index of the point, zero-based.
* @return The point at the index specified.
*/
virtual Point3D &getPoint (int index);
/**
* Add a new point to the end of the line.
*
* @param point The point to add.
*/
virtual void addPoint (const Point3D &point);
private:
vector<Point3D> _points;
};
#endif // __LINE_HXX

View file

@ -0,0 +1,58 @@
// rectangle.cxx - a simple rectangle class (for bounds, etc.)
//
// Started by David Megginson, July 2002
//
// This file is in the Public Domain and comes with NO WARRANTY OF ANY KIND.
#include "rectangle.hxx"
Rectangle::Rectangle ()
{
}
Rectangle::Rectangle (const Rectangle &r)
: _min(r.getMin()),
_max(r.getMax())
{
}
Rectangle::~Rectangle ()
{
}
void
Rectangle::setMin (const Point3D &p)
{
_min = p;
}
void
Rectangle::setMax (const Point3D &p)
{
_max = p;
}
void
Rectangle::sanify ()
{
double tmp;
if (_min.x() > _max.x()) {
tmp = _min.x();
_min.setx(_max.x());
_max.setx(tmp);
}
if (_min.y() > _max.y()) {
tmp = _min.y();
_min.sety(_max.y());
_max.sety(tmp);
}
}
bool
Rectangle::isInside (const Point3D &p) const
{
return ((p.x() >= _min.x() && p.x() <= _max.x()) &&
(p.y() >= _min.y() && p.y() <= _max.y()));
}
// end of rectangle.cxx

View file

@ -0,0 +1,98 @@
// rectangle.hxx - a simple rectangle class (for bounds, etc.)
//
// Started by David Megginson, July 2002
//
// This file is in the Public Domain and comes with NO WARRANTY OF ANY KIND.
#ifndef __RECTANGLE_HXX
#define __RECTANGLE_HXX 1
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/compiler.h>
#include <simgear/math/point3d.hxx>
/**
* A simple rectangle class for bounding rectanglees.
*
* The class defines a rectangle in by the vertices of its minimum and
* maximum corners, ignoring any z coordinates. There are methods to
* sanify the rectangle (to make certain that each point is correct)
* and to test whether another point lies inside it.
*/
class Rectangle
{
public:
/**
* Create a new empty rectangle with both points at 0,0.
*/
Rectangle ();
/**
* Copy an existing rectangle.
*
* @param r The rectangle to copy.
*/
Rectangle (const Rectangle &r);
/**
* Destructor.
*/
virtual ~Rectangle ();
/**
* Get the minimum (top left) corner of the rectangle.
*
* @return The top-left vertex.
*/
virtual const Point3D &getMin () const { return _min; }
/**
* Get the maximum (bottom right) corner of the rectangle.
*
* @return The bottom-right vertex.
*/
virtual const Point3D &getMax () const { return _max; }
/**
* Set the minimum (top-left) corner of the rectangle.
*
* @param p The top-left vertex.
*/
virtual void setMin (const Point3D &p);
/**
* Set the maximum (bottom-right) corner of the rectangle.
*
* @param p The bottom-right vertex.
*/
virtual void setMax (const Point3D &p);
/**
* Make the rectangle sane.
*
* Ensure that the min vertex is less than the max vertex.
*/
virtual void sanify ();
/**
* Test whether a point lies inside the rectangle.
*
* The z-coordinates are ignored.
*
* @param p The point to test.
* @return true if the point is inside or on the boundary of the
* rectangle, false if it is outside.
*/
virtual bool isInside (const Point3D &p) const;
private:
Point3D _min;
Point3D _max;
};
#endif // __RECTANGLE_HXX

147
src/Lib/Geometry/util.cxx Normal file
View file

@ -0,0 +1,147 @@
// util.cxx - a collection of simple geometry utility functions.
//
// Started by David Megginson, July 2002
//
// This file is in the Public Domain and comes with NO WARRANTY OF ANY KIND.
#include "util.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <Polygon/polygon.hxx>
bool
getIntersection (const Point3D &p0, const Point3D &p1,
const Point3D &p2, const Point3D &p3,
Point3D &intersection)
{
double u_num =
((p3.x()-p2.x())*(p0.y()-p2.y()))-((p3.y()-p2.y())*(p0.x()-p2.x()));
double u_den =
((p3.y()-p2.y())*(p1.x()-p0.x()))-((p3.x()-p2.x())*(p1.y()-p0.y()));
if (u_den == 0) {
if (u_num == 0)
SG_LOG(SG_GENERAL, SG_ALERT, "Intersection: coincident lines");
else
SG_LOG(SG_GENERAL, SG_ALERT, "Intersection: parallel lines");
return false;
} else {
double u = u_num/u_den;
intersection = Point3D((p0.x()+u*(p1.x()-p0.x())),
(p0.y()+u*(p1.y()-p0.y())),
0);
return true;
}
}
/**
* Create a polygon out of a point.
*
* Note that simple geometry doesn't work here, because the scale is
* not even -- the points on the x-axis (longitude) become closer and
* closer as the y-axis (latitude) approaches the poles, meeting in
* a single point at y=90 and y=-90. As a result, this function
* uses the WGS80 functions, rather than simple Pythagorean stuff.
*/
void
makePolygon (const Point3D &p, int width, FGPolygon &polygon)
{
double x, y, az;
double lon = p.x();
double lat = p.y();
polygon.erase();
geo_direct_wgs_84(0, lat, lon, 90, width/2, &y, &x, &az);
double dlon = x - lon;
geo_direct_wgs_84(0, lat, lon, 0, width/2, &y, &x, &az);
double dlat = y - lat;
polygon.add_node(0, Point3D(lon - dlon, lat - dlat, 0));
polygon.add_node(0, Point3D(lon + dlon, lat - dlat, 0));
polygon.add_node(0, Point3D(lon + dlon, lat + dlat, 0));
polygon.add_node(0, Point3D(lon - dlon, lat + dlat, 0));
}
void
makePolygon (const Line &line, int width, FGPolygon &polygon)
{
vector<FGPolygon> segment_list;
int nPoints = line.getPointCount();
int i;
for (i = 0; i < nPoints - 1; i++) {
const Point3D p1 = line.getPoint(i);
const Point3D p2 = line.getPoint(i+1);
double angle1, angle2, dist, x, y, az;
geo_inverse_wgs_84(0, p1.y(), p1.x(), p2.y(), p2.x(), &angle1, &angle2, &dist);
polygon.erase();
// Wind each rectangle counterclockwise
// Corner 1
geo_direct_wgs_84(0, p1.y(), p1.x(), CLAMP_ANGLE(angle1+90), width/2, &y, &x, &az);
polygon.add_node(0, Point3D(x, y, 0));
// Corner 2
geo_direct_wgs_84(0, p2.y(), p2.x(), CLAMP_ANGLE(angle1+90), width/2, &y, &x, &az);
polygon.add_node(0, Point3D(x, y, 0));
// Corner 3
geo_direct_wgs_84(0, p2.y(), p2.x(), CLAMP_ANGLE(angle1-90), width/2, &y, &x, &az);
polygon.add_node(0, Point3D(x, y, 0));
// Corner 4
geo_direct_wgs_84(0, p1.y(), p1.x(), CLAMP_ANGLE(angle1-90), width/2, &y, &x, &az);
polygon.add_node(0, Point3D(x, y, 0));
// Save this rectangle
segment_list.push_back(polygon);
}
// Build one big polygon out of all the rectangles by intersecting
// the lines running through the bottom and top sides
polygon.erase();
// Connect the bottom part.
int nSegments = segment_list.size();
Point3D intersection;
polygon.add_node(0, segment_list[0].get_pt(0, 0));
for (i = 0; i < nSegments - 1; i++) {
if (getIntersection(segment_list[i].get_pt(0, 0),
segment_list[i].get_pt(0, 1),
segment_list[i+1].get_pt(0, 0),
segment_list[i+1].get_pt(0, 1),
intersection))
polygon.add_node(0, intersection);
else
polygon.add_node(0, segment_list[i].get_pt(0, 1));
}
polygon.add_node(0, segment_list[nSegments-1].get_pt(0, 1));
// Connect the top part
polygon.add_node(0, segment_list[nSegments-1].get_pt(0, 2));
for (i = nSegments - 1; i > 0; i--) {
if (getIntersection(segment_list[i].get_pt(0, 2),
segment_list[i].get_pt(0, 3),
segment_list[i-1].get_pt(0, 2),
segment_list[i-1].get_pt(0, 3),
intersection))
polygon.add_node(0, intersection);
else
polygon.add_node(0, segment_list[i].get_pt(0, 3));
}
polygon.add_node(0, segment_list[0].get_pt(0, 3));
}
// end of util.cxx

87
src/Lib/Geometry/util.hxx Normal file
View file

@ -0,0 +1,87 @@
// util.hxx - a collection of simple WGS84 utility functions.
//
// Started by David Megginson, July 2002
//
// This file is in the Public Domain and comes with NO WARRANTY OF ANY KIND.
#ifndef __UTIL_HXX
#define __UTIL_HXX 1
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/compiler.h>
#include <simgear/math/point3d.hxx>
#include <Polygon/polygon.hxx>
#include "line.hxx"
/**
* Inline function to clamp an angle between 0 and 360 degrees.
*
* @param a The angle to clamp, in degrees.
* @return The clamped angle.
*/
inline double
CLAMP_ANGLE (double a)
{
while (a < 0.0)
a += 360.0;
while (a >= 360.0)
a -= 360.0;
return a;
}
/**
* Calculate the intersection of two line segments.
*
* @param p0 First point on the first segment.
* @param p1 A second point on the second segment.
* @param p2 First point on the second segment.
* @param p3 A second point on the second segment.
* @param intersection A variable to hold the calculated intersection.
* @return true if there was an intersection, false if the segments.
* are parallel or coincident.
*/
bool getIntersection (const Point3D &p0, const Point3D &p1,
const Point3D &p2, const Point3D &p3,
Point3D &intersection);
/**
* Create a polygon out of a point.
*
* Note that simple geometry doesn't work here, because the scale is
* not even -- the points on the x-axis (longitude) become closer and
* closer as the y-axis (latitude) approaches the poles, meeting in
* a single point at y=90 and y=-90. As a result, this function
* uses the WGS80 functions, rather than simple Pythagorean stuff.
*
* @param p The point at the centre of the new polygon.
* @param width The width in standard units (meters for FlightGear).
* @param polygon The object that will hold the new polygon.
*/
void makePolygon (const Point3D &p, int width, FGPolygon &polygon);
/**
* Create a polygon out of a line.
*
* Note that simple geometry doesn't work here, because the scale is
* not even -- the points on the x-axis (longitude) become closer and
* closer as the y-axis (latitude) approaches the poles, meeting in
* a single point at y=90 and y=-90. As a result, this function
* uses the WGS80 functions, rather than simple Pythagorean stuff.
*
* @param line The multi-segment line inside the new polygon.
* @param width The width in standard units (meters for FlightGear).
* @param polygon The object that will hold the new polygon.
*/
void makePolygon (const Line &line, int width, FGPolygon &polygon);
#endif // __UTIL_HXX

View file

@ -3,6 +3,7 @@ bin_PROGRAMS = tgvpf
tgvpf_SOURCES = tgvpf.cxx
tgvpf_LDADD = \
$(top_builddir)/src/Lib/Geometry/libGeometry.a \
$(top_builddir)/src/Lib/Polygon/libPolygon.a \
$(top_builddir)/src/Lib/poly2tri/libpoly2tri.a \
$(top_builddir)/src/Lib/vpf/libvpf.a \

View file

@ -44,6 +44,8 @@ SG_USING_STD(cout);
SG_USING_STD(string);
SG_USING_STD(vector);
#include <Geometry/line.hxx>
#include <Geometry/util.hxx>
#include <Polygon/index.hxx>
#include <Polygon/names.hxx>
#include <Polygon/polygon.hxx>
@ -65,11 +67,13 @@ static const char * progname;
////////////////////////////////////////////////////////////////////////
// Utility stuff.
// VPF conversion code.
////////////////////////////////////////////////////////////////////////
static inline ostream &
/**
* Print out a VPF bounding rectangle.
*/
static ostream &
operator<< (ostream &output, const VpfRectangle &rect)
{
output << rect.minX << ','
@ -79,54 +83,53 @@ operator<< (ostream &output, const VpfRectangle &rect)
return output;
}
/**
* Inline function to clamp an angle between 0 and 360 degrees.
* Convert a VPF point to a regular TerraGear point.
*/
static inline double
ANGLE (double a)
static inline const Point3D
vpf2tg (const VpfPoint &p)
{
while (a < 0.0)
a += 360.0;
while (a >= 360.0)
a -= 360.0;
return a;
return Point3D(p.x, p.y, p.z);
}
/**
* Calculate the intersection of two lines.
*
* @param p0 First point on the first line.
* @param p1 A second point on the first line.
* @param p2 First point on the second line.
* @param p3 A second point on the second line.
* @param intersection A variable to hold the calculated intersection.
* @return true if there was an intersection, false if the lines
* are parallel or coincident.
* Convert a VPF line to a regular TerraGear line.
*/
static bool
getIntersection (const Point3D &p0, const Point3D &p1,
const Point3D &p2, const Point3D &p3,
Point3D &intersection)
static const Line
vpf2tg (const VpfLine &l)
{
double u_num =
((p3.x()-p2.x())*(p0.y()-p2.y()))-((p3.y()-p2.y())*(p0.x()-p2.x()));
double u_den =
((p3.y()-p2.y())*(p1.x()-p0.x()))-((p3.x()-p2.x())*(p1.y()-p0.y()));
Line result;
int nPoints = l.getPointCount();
for (int i = 0; i < nPoints; i++)
result.addPoint(vpf2tg(l.getPoint(i)));
return result;
}
if (u_den == 0) {
if (u_num == 0)
SG_LOG(SG_GENERAL, SG_ALERT, "Intersection: coincident lines");
else
SG_LOG(SG_GENERAL, SG_ALERT, "Intersection: parallel lines");
return false;
} else {
double u = u_num/u_den;
intersection = Point3D((p0.x()+u*(p1.x()-p0.x())),
(p0.y()+u*(p1.y()-p0.y())),
0);
return true;
/**
* Convert a VPF polygon to a TerraGear polygon.
*/
static const FGPolygon
vpf2tg (const VpfPolygon &polygon)
{
FGPolygon shape;
shape.erase();
int nContours = polygon.getContourCount();
int contour_num = 0;
for (int i = 0; i < nContours; i++) {
const VpfContour contour = polygon.getContour(i);
int nPoints = contour.getPointCount();
for (int j = 0; j < nPoints; j++) {
const VpfPoint p = contour.getPoint(j);
shape.add_node(contour_num, Point3D(p.x, p.y, p.z));
}
shape.set_hole_flag(contour_num, (i > 0));
contour_num++;
}
return shape;
}
@ -175,155 +178,6 @@ checkAttribute (const VpfFeature &feature, int index, const Attribute &att)
}
/**
* Create a polygon out of a point.
*
* Note that simple geometry doesn't work here, because the scale is
* not even -- the points on the x-axis (longitude) become closer and
* closer as the y-axis (latitude) approaches the poles, meeting in
* a single point at y=90 and y=-90. As a result, this function
* uses the WGS80 functions, rather than simple Pythagorean stuff.
*/
static const FGPolygon
makePolygon (const VpfPoint &p, int width)
{
FGPolygon result;
double x, y, az;
double lon = p.x;
double lat = p.y;
result.erase();
geo_direct_wgs_84(0, lat, lon, 90, width/2, &y, &x, &az);
double dlon = x - lon;
geo_direct_wgs_84(0, lat, lon, 0, width/2, &y, &x, &az);
double dlat = y - lat;
result.add_node(0, Point3D(lon - dlon, lat - dlat, 0));
result.add_node(0, Point3D(lon + dlon, lat - dlat, 0));
result.add_node(0, Point3D(lon + dlon, lat + dlat, 0));
result.add_node(0, Point3D(lon - dlon, lat + dlat, 0));
return result;
}
/**
* Create a polygon out of a line.
*
* Note that simple geometry doesn't work here, because the scale is
* not even -- the points on the x-axis (longitude) become closer and
* closer as the y-axis (latitude) approaches the poles, meeting in
* a single point at y=90 and y=-90. As a result, this function
* uses the WGS80 functions, rather than simple Pythagorean stuff.
*/
static const FGPolygon
makePolygon (const VpfLine &line, int width)
{
FGPolygon shape;
vector<FGPolygon> segment_list;
int nPoints = line.getPointCount();
int i;
for (i = 0; i < nPoints - 1; i++) {
const VpfPoint p1 = line.getPoint(i);
const VpfPoint p2 = line.getPoint(i+1);
double angle1, angle2, dist, x, y, az;
geo_inverse_wgs_84(0, p1.y, p1.x, p2.y, p2.x, &angle1, &angle2, &dist);
shape.erase();
// Wind each rectangle counterclockwise
// Corner 1
geo_direct_wgs_84(0, p1.y, p1.x, ANGLE(angle1+90), width/2, &y, &x, &az);
shape.add_node(0, Point3D(x, y, 0));
// Corner 2
geo_direct_wgs_84(0, p2.y, p2.x, ANGLE(angle1+90), width/2, &y, &x, &az);
shape.add_node(0, Point3D(x, y, 0));
// Corner 3
geo_direct_wgs_84(0, p2.y, p2.x, ANGLE(angle1-90), width/2, &y, &x, &az);
shape.add_node(0, Point3D(x, y, 0));
// Corner 4
geo_direct_wgs_84(0, p1.y, p1.x, ANGLE(angle1-90), width/2, &y, &x, &az);
shape.add_node(0, Point3D(x, y, 0));
// Save this rectangle
segment_list.push_back(shape);
}
// Build one big polygon out of all the rectangles by intersecting
// the lines running through the bottom and top sides
shape.erase();
// Connect the bottom part.
int nSegments = segment_list.size();
Point3D intersection;
shape.add_node(0, segment_list[0].get_pt(0, 0));
for (i = 0; i < nSegments - 1; i++) {
if (getIntersection(segment_list[i].get_pt(0, 0),
segment_list[i].get_pt(0, 1),
segment_list[i+1].get_pt(0, 0),
segment_list[i+1].get_pt(0, 1),
intersection))
shape.add_node(0, intersection);
else
shape.add_node(0, segment_list[i].get_pt(0, 1));
}
shape.add_node(0, segment_list[nSegments-1].get_pt(0, 1));
// Connect the top part
shape.add_node(0, segment_list[nSegments-1].get_pt(0, 2));
for (i = nSegments - 1; i > 0; i--) {
if (getIntersection(segment_list[i].get_pt(0, 2),
segment_list[i].get_pt(0, 3),
segment_list[i-1].get_pt(0, 2),
segment_list[i-1].get_pt(0, 3),
intersection))
shape.add_node(0, intersection);
else
shape.add_node(0, segment_list[i].get_pt(0, 3));
}
shape.add_node(0, segment_list[0].get_pt(0, 3));
return shape;
}
/**
* Import all polygons.
*/
static const FGPolygon
makePolygon (const VpfPolygon &polygon)
{
FGPolygon shape;
shape.erase();
int nContours = polygon.getContourCount();
int contour_num = 0;
for (int i = 0; i < nContours; i++) {
const VpfContour contour = polygon.getContour(i);
int nPoints = contour.getPointCount();
for (int j = 0; j < nPoints; j++) {
const VpfPoint p = contour.getPoint(j);
shape.add_node(contour_num, Point3D(p.x, p.y, p.z));
}
shape.set_hole_flag(contour_num, (i > 0));
contour_num++;
}
return shape;
}
////////////////////////////////////////////////////////////////////////
// Main program.
@ -657,28 +511,28 @@ main (int argc, const char **argv)
const VpfPoint p = feature.getPoint(i);
if (!inside(p, bounds))
continue;
shape = makePolygon(p, (width == -1 ? 500 : width));
makePolygon(vpf2tg(p), (width == -1 ? 500 : width), shape);
break;
}
case VpfFeature::LINE: {
const VpfLine line = feature.getLine(i);
if (!overlap(line.getBoundingRectangle(), bounds))
continue;
shape = makePolygon(line, (width == -1 ? 50 : width));
makePolygon(vpf2tg(line), (width == -1 ? 50 : width), shape);
break;
}
case VpfFeature::POLYGON: {
const VpfPolygon polygon = feature.getPolygon(i);
if (!overlap(polygon.getBoundingRectangle(), bounds))
continue;
shape = makePolygon(polygon);
shape = vpf2tg(polygon);
break;
}
case VpfFeature::LABEL: {
const VpfPoint p = feature.getLabel(i).getPoint();
if (!inside(p, bounds))
continue;
shape = makePolygon(p, (width == -1 ? 500 : width));
makePolygon(vpf2tg(p), (width == -1 ? 500 : width), shape);
break;
}
default: