Опубликован: 27.09.2006 | Уровень: для всех | Доступ: свободно | ВУЗ: Московский государственный индустриальный университет
Лекция 11:
Проект "Выпуклая оболочка"
Текст эталонного проекта
Данная секция содержит исходный текст программы, являющейся итогом работы над проектом "Выпуклая оболочка".
Эталонный проект
//Класс, описывающий точку (Point) на плоскости (R2).
class R2Point {
private double x, y;
public R2Point(double x, double y) {
this.x = x; this.y = y;
}
public R2Point() throws Exception {
x = Xterm.inputDouble("x -> ");
y = Xterm.inputDouble("y -> ");
}
public static double dist(R2Point a, R2Point b) {
return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
public static double area(R2Point a, R2Point b, R2Point c) {
return 0.5*((a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x));
}
public static boolean equal(R2Point a, R2Point b) {
return a.x==b.x && a.y==b.y;
}
public static boolean isTriangle(R2Point a, R2Point b, R2Point c) {
return area(a, b, c) != 0.0;
}
public boolean inside(R2Point a, R2Point b) {
return (a.x <= x && x <= b.x || a.x >= x && x >= b.x) &&
(a.y <= y && y <= b.y || a.y >= y && y >= b.y);
}
public boolean light(R2Point a, R2Point b) {
double s = area(a, b, this);
return s < 0.0 || ( s == 0.0 && ! inside(a, b));
}
}
// Непрерывная реализация дека.
class Deq {
private final static int DEFSIZE = 16;
private R2Point[] array;
private int size, head, tail;
private int forward(int index) {
return ++index < array.length ? index : 0;
}
private int backward(int index) {
return --index >= 0 ? index : array.length - 1;
}
public Deq(int size) {
array = new R2Point[size];
this.size = head = 0;
tail = array.length - 1;
}
public Deq() {
this(DEFSIZE);
}
public int length() {
return size;
}
public void pushFront(R2Point p) {
array[head=backward(head)] = p;
size += 1;
}
public void pushBack(R2Point p) {
array[tail=forward(tail)] = p;
size += 1;
}
public R2Point popFront() {
R2Point p = front();
head = forward(head);
size -= 1;
return p;
}
public R2Point popBack() {
R2Point p = back();
tail = backward(tail);
size -= 1;
return p;
}
public R2Point front() {
return array[head];
}
public R2Point back() {
return array[tail];
}
}
// Интерфейс, задающий новый тип - фигуру.
interface Figure {
public double perimeter();
public double area();
public Figure add(R2Point p);
}
// Класс "нульугольник", реализующий интерфейс фигуры.
class Void implements Figure {
public double perimeter() {
return 0.0;
}
public double area() {
return 0.0;
}
public Figure add(R2Point p) {
return new Point(p);
}
}
// Класс "одноугольник", реализующий интерфейс фигуры.
class Point implements Figure {
private R2Point p;
public Point(R2Point p) {
this.p = p;
}
public double perimeter() {
return 0.0;
}
public double area() {
return 0.0;
}
public Figure add(R2Point q) {
if (!R2Point.equal(p,q)) return new Segment(p, q);
else return this;
}
}
// Класс "двуугольник", реализующий интерфейс фигуры.
class Segment implements Figure {
private R2Point p, q;
public Segment(R2Point p, R2Point q) {
this.p = p; this.q = q;
}
public double perimeter() {
return 2.0 * R2Point.dist(p, q);
}
public double area() {
return 0.0;
}
public Figure add(R2Point r) {
if (R2Point.isTriangle(p, q, r))
return new Polygon(p, q, r);
if (q.inside(p, r)) q = r;
if (p.inside(r, q)) p = r;
return this;
}
}
// Класс "многоугольник", реализующий интерфейс фигуры.
class Polygon extends Deq implements Figure {
private double s, p;
private void grow(R2Point a, R2Point b, R2Point t) {
p -= R2Point.dist(a, b);
s += Math.abs(R2Point.area(a, b, t));
}
public Polygon(R2Point a, R2Point b, R2Point c) {
pushFront(b);
if (b.light(a, c)) {
pushFront(a); pushBack(c);
} else {
pushFront(c); pushBack(a);
}
p = R2Point.dist(a, b) + R2Point.dist(b, c)
+ R2Point.dist(c, a);
s = Math.abs(R2Point.area(a, b, c));
}
public double perimeter() {
return p;
}
public double area() {
return s;
}
public Figure add(R2Point t) {
int i;
// Ищем освещенные ребра, просматривая их одно за другим.
for (i=length(); i>0 && !t.light(back(),front()); i--)
pushBack(popFront());
// УТВЕРЖДЕНИЕ: либо ребро [back(),front()] освещено из t,
// либо освещенных ребер нет совсем.
if (i>0) {
R2Point x;
grow(back(), front(), t);
// Удаляем все освещенные ребра из начала дека.
for (x = popFront(); t.light(x, front()); x = popFront())
grow(x, front(), t );
pushFront(x);
// Удаляем все освещенные ребра из конца дека.
for (x = popBack(); t.light(back(), x); x = popBack())
grow(back(), x, t);
pushBack(x);
// Завершаем обработку добавляемой точки.
p += R2Point.dist(back(), t) + R2Point.dist(t, front());
pushFront(t);
}
return this;
}
}
// Класс "выпуклая оболочка".
class Convex {
private Figure fig;
public Convex() {
fig = new Void();
}
public void add(R2Point p) {
fig = fig.add(p);
}
public double area() {
return fig.area();
}
public double perimeter() {
return fig.perimeter();
}
}
// Тест для выпуклой оболочки.
class ConvexTest {
public static void main(String[] args) throws Exception {
Convex convex = new Convex();
while (true) {
convex.add(new R2Point());
Xterm.println("S = " + convex.area() + " , P = "
+ convex.perimeter());
}
}
}