geometry.cpp

#include "geometry.h"
#include "stdafx.h"
#include "testing.h"
#include "matlib.h"

using namespace std;

////////////////////////////////
//    Shape implementation
////////////////////////////////
double Shape::area() const {
    Rectangle r = boundingRectangle();
    int count = 0;
    int nPoints = 10000;
    Matrix u = randuniform( nPoints, 2 );
    for (int i=0; i<nPoints; i++) {
        double x = u(i,0) * r.getWidth() + r.getLeft();
        double y = u(i,1) * r.getHeight() + r.getBottom();
        CartesianPoint p(x,y);
        if (contains(p)) {
            count++;
        }
    }
    return ((double)count)/nPoints * r.area();
}

shared_ptr<Shape> Shape::newCircle( const CartesianPoint& cartesianPoint,
    double radius ) {
    shared_ptr<Circle> ret = make_shared<Circle>();
    ret->setCenter( cartesianPoint );
    ret->setRadius( radius );
    return ret;
}

shared_ptr<Shape> Shape::newHyperCircle() {
    shared_ptr<HyperCircle> ret= make_shared<HyperCircle>();
    return ret;
}



////////////////////////////////
//    Circle implementation
////////////////////////////////

/**
 *   Computes the area of a circle
 */
double Circle::area() const {
	return PI*radius*radius;
}

/**
 *  Computes the circumference of a circle
 */
double Circle::circumference() const {
	return 2*PI*radius;
}

bool Circle::contains( const CartesianPoint& x ) const {
    return x.distanceTo( center )<=radius;
}

Rectangle Circle::boundingRectangle() const {
    Rectangle r;
    r.setTop( center.getY()+radius );
    r.setBottom( center.getY()-radius );
    r.setLeft( center.getX()-radius );
    r.setRight( center.getX()+radius );
    return r;
}

////////////////////////////////
//    CartesianPoint implementation
////////////////////////////////


/**
 *  Computes the distance between two cartesian points
 */
double CartesianPoint::distanceTo( const CartesianPoint& other ) const {
    double dx = other.x - x;
    double dy = other.y - y;
    return sqrt( dx*dx + dy*dy );
}

/**
 *  Computes the distance between this and a polar point
 */
double CartesianPoint::distanceTo( const PolarPoint& other ) const {
    return distanceTo( other.toCartesian());
}



/**
 *  Converts polar coordinates to cartesian. Note, this changes x and y.
 */
CartesianPoint PolarPoint::toCartesian() const {
    CartesianPoint c;
	c.setX( r*cos( theta ));
	c.setY( r*sin( theta ));
    return c;
}


////////////////////////////////
//    PolarPoint implementation
////////////////////////////////

/**
 *   Converts Cartesian coordinates to polar coordinates. Note this
 *   changes r and theta.
 */
PolarPoint CartesianPoint::toPolar() const {
    PolarPoint p;
	// Not as easy as it looks to do this
	p.setR( sqrt( x*x + y*y));
	if (y==0.0) {
		if (x>=0.0) {
			p.setTheta( 0.0);
		} else if (x<0.0) {
			p.setTheta( -PI);
		}
	} else {
		p.setTheta( acos( x/p.getR()));
		if (y<0) {
			p.setTheta( -p.getTheta());
		}
	}
    return p;
}

////////////////////////////////
//    Rectangle implementation
////////////////////////////////

bool Rectangle::contains(const CartesianPoint& p) const {
    double x = p.getX();
    double y = p.getY();
    return x>=left && x<=right && y<=top && y>=bottom;
}

////////////////////////////////
//   HyperCircle implementation
////////////////////////////////

Rectangle HyperCircle::standardRectangle(1,-1,-1,1);

/////////////////////////////////////////////////
//
//  Geometry tests
//
/////////////////////////////////////////////////


static void testAreaOfCircle() {
    Circle c;
    c.setRadius( 4 );
	ASSERT_APPROX_EQUAL( c.area(), 16*PI, 0.01 );
}

static void testCircumferenceOfCircle() {
    Circle c;
    c.setRadius( 2 );
	ASSERT_APPROX_EQUAL( c.circumference(), 4*PI, 0.01 );
}

static void testPolarToCartesian() {
    PolarPoint p;
    p.setR( 2.0 );
    p.setTheta( PI/2 );
	CartesianPoint c = p.toCartesian();
	ASSERT_APPROX_EQUAL( c.getX(),0.0,0.001 );
	ASSERT_APPROX_EQUAL( c.getY(),2.0,0.001 );
}

static void testCartesianToPolar() {
	// the logic of cartesian to polar is quite complex so we have more tests
	vector<double> angles;
	angles.push_back( 0.0 );
	angles.push_back( PI/4 );
	angles.push_back( PI/2 );
	angles.push_back( 7*PI/8 );
	angles.push_back( PI);
	angles.push_back( -PI/4 );
	angles.push_back( -PI/2 );
	angles.push_back( -7*PI/8 );

	int nAngles = angles.size();
	for (int i=0; i<nAngles; i++) {
        PolarPoint p;
		p.setR( 2.0 );
		p.setTheta(angles[i]);
		CartesianPoint c =p.toCartesian();
		PolarPoint p2 = c.toPolar();
		ASSERT_APPROX_EQUAL( p.getR(),p2.getR(),0.001 );
		ASSERT_APPROX_EQUAL( p.getTheta(),p2.getTheta(),0.001 );
	}
}

static void testDistanceTo() {
    CartesianPoint p1;
    p1.setX( 1 );
    p1.setY( 1 );
    CartesianPoint p2;
    p2.setX( 4 );
    p2.setY( 5 );
    double d = p1.distanceTo( p2 );
    ASSERT_APPROX_EQUAL( d, 5.0, 0.0001);    
}

static void testDistanceToPolar() {
    CartesianPoint p1;
    p1.setX( 1 );
    p1.setY( 1 );
    CartesianPoint p2;
    p2.setX( 4 );
    p2.setY( 5 );
    double d = p1.distanceTo( p2.toPolar() );
    ASSERT_APPROX_EQUAL( d, 5.0, 0.0001);    
}

static void testRectangle() {
    Rectangle r;
    r.setTop( 1 );
    r.setBottom(-1);
    r.setLeft( -2 );
    r.setRight(2);
    ASSERT_APPROX_EQUAL( r.area(), 8.0, 0.0001);
}

static void testHyperCircle() {
    HyperCircle h;
    CartesianPoint p(0.5,0);
    ASSERT( h.contains(p));
    p = CartesianPoint(0,0.5);
    ASSERT( h.contains(p));
    Circle c;
    c.setRadius(1);
    Rectangle r = h.boundingRectangle();
    ASSERT( h.area()>c.area() );
    ASSERT( h.area()<r.area() );
}




void testGeometry() {
	TEST( testAreaOfCircle );
	TEST( testCircumferenceOfCircle );
	TEST( testPolarToCartesian );
	TEST( testCartesianToPolar );
    TEST( testDistanceTo );
    TEST( testDistanceToPolar );
    TEST( testRectangle );
    TEST( testHyperCircle);
}