Опубликован: 27.09.2006 | Уровень: для всех | Доступ: платный | ВУЗ: Московский государственный индустриальный университет
Лекция 13:
Изображение полиэдра
ShadowDrawer.java
/** * @author Е.А. Роганов * @version 1.1 * Класс ShadowDrawer, обеспечивающий изображение проекции * полиэдра с удалением невидимых линий. */ public class ShadowDrawer extends SimpleDrawer { /** * Начало изображаемого ребра. */ protected R3Vector begin; /** * Конец изображаемого ребра. */ protected R3Vector end; /** * Максимальный размер списка. */ private static final int MAXSIZE = 128; /** * Односвязный список видимых отрезков ребра. */ private L1ListSegments list; /** * Достаточно малая константа, предназначенная для * решения проблемы неточного представления действительных * чисел на ЭВМ. */ private static final double EPSILON = 1.e-12; /** * Одномерная координата начала обрабатываемого ребра. */ private static final double t0 = 0.; /** * Одномерная координата конца обрабатываемого ребра. */ private static final double t1 = 1.; /** * Вычислить пространственные координаты точки ребра, * заданной одномерной координатой. * @param t Одномерная координата точки на ребре. * @return Трехмерная координата этой точки ребра. */ private R3Vector R3(double t) { return R3Vector.plus( R3Vector.mul((1.-t), begin), R3Vector.mul(t, end) ); } /** * Вычислить одномерный отрезок тени от грани. * @param f Грань полиэдра. * @return Отрезок тени от этой грани на ребре. */ private Segment shadow(Facet f) { if (f.vertical(pr)) return new Segment(t1, t0); int n = f.getVertexesQuantity(); Vertex a = f.getVertex(n-1); Vertex b = f.getVertex(0); Segment result = hCross(f, a); if (result.degenerate()) return result; result.intersection(vCross(a, b, f.getCenter())); if (result.degenerate()) return result; for (int i=1; i<n; i++) { a = b; b = f.getVertex(i); result.intersection(vCross(a, b, f.getCenter())); if (result.degenerate()) return result; } return result; } /** * Вычислить пересечение с "горизонтальным" полупространством. * @param f Грань полиэдра. * @param a Точка (вектор) на грани. * @return Отрезок пересечения ребра с "горизонтальным" полупространством. */ private Segment hCross(Facet f, R3Vector a) { R3Vector n = f.getNormal(); if (R3Vector.scalMul(n, pr) < 0.0) n.mul(-1); return crossWith(a, n); } /** * Вычислить пересечение с "вертикальным" полупространством. * @param a Точка (вектор) на грани. * @param b Точка (вектор) на грани. * @param c Точка (вектор) на грани. * @return Отрезок пересечения ребра с "вертикальным" полупространством. */ private Segment vCross(R3Vector a, R3Vector b, R3Vector c) { R3Vector n = R3Vector.vectMul(R3Vector.minus(b,a), pr); if (R3Vector.scalMul(n, R3Vector.minus(a,c)) < 0.0) n.mul(-1); return crossWith(a, n); } /** * Вычислить пересечение отрезка с заданным полупространством. * @param a Точка (вектор) на грани. * @param n Вектор внешней нормали к полупространству. * @return Отрезок пересечения ребра с полупространством. */ private Segment crossWith(R3Vector a, R3Vector n) { double f0 = R3Vector.scalMul(n, R3Vector.minus(begin, a)); double f1 = R3Vector.scalMul(n, R3Vector.minus(end, a)); if(Math.abs(f0) < EPSILON) f0 = 0.; if(Math.abs(f1) < EPSILON) f1 = 0.; if(f0 >= 0. && f1 >= 0.) return new Segment(t1, t0); if(f0 < 0. && f1 < 0. ) return new Segment(t0, t1); double t = - f0 / (f1 - f0); if (f0 < 0.) return new Segment(t0, t); return new Segment(t, t1); } /** * Учесть тень от одной грани. * @param f Грань, тень от которой должна быть учтена. */ protected final void addShadow(Facet f) { try { Segment s = shadow(f); if (!s.degenerate()) { list.toFront(); while (!list.end()) { Segment next = list.erase(); Segment left = next.leftSub(s); if (!left.degenerate()) { list.insert(left); list.forward(); } Segment right = next.rightSub(s); if (!right.degenerate()) { list.insert(right); list.forward(); } } } } catch(Exception e) { Xterm.println("Слишком много видимых отрезков ребра."); System.exit(0); } } /** * Учесть тень от всех граней. */ protected void addShadow() { for(int j=0; j<p.getFacetsQuantity(); j++) addShadow(p.getFacet(j)); } /** * Конструктор класса. * @param p Полиэдр. * @param pr Вектор проектирования. * @param angle Угол поворота в плоскости проекции. */ public ShadowDrawer(Polyedr p, R3Vector pr, double angle) { super(p, pr, angle); list = new L1ListSegments(MAXSIZE); } /** * Изобразить видимую часть ребра полиэдра. * @param s Обрабатываемое ребро полиэдра. */ public final void drawEdge(Edge s) { begin = s.getBegin(); end = s.getEnd(); list.clear(); try { list.insert(new Segment(t0, t1)); } catch(Exception e) {;} addShadow(); try { for (list.toFront(); ! list.end(); list.forward()) { Segment u = list.after(); R3Vector begin = R3(u.getBegin()); R3Vector end = R3(u.getEnd()); draw(xnProection(begin), ynProection(begin), xnProection(end), ynProection(end)); } } catch(Exception e) {;} Xterm.print("."); } }