package com.exh.bevel.algo; import java.util.List; import com.exh.bevel.support.QuadEntry; import com.exh.bevel.support.QuadList; import com.exh.math.primitive.Geo; import com.exh.math.primitive.Point3; import com.exh.math.primitive.Quad; /* The initial bevel: My role is simple, to take a polygon and * extrude it into 3d by converting it to a series of quadrilaterals. * The final result will either be perfect, if the amount of extrusion * is low enough that no quads intersect, or very very messed up if * they do. */ public final class Bevel { private Point3[] mPts = null; private final Point3.Order mOrder = new Point3.Order(); private final Point3 mAb1 = new Point3(), mAb2 = new Point3(), mBc1 = new Point3(), mBc2 = new Point3(); // Given a list of points, a depth (how far to extrude the // points) and a height (the z value of the extruded points), // answer a list of extruded quads. public void asBevel(final List pts, final double depth, final double height, final QuadList output) { if (!makePts(pts)) return; int ni = 1, nni = 2; Point3 prev = mPts[mPts.length-1]; final Point3 inter = new Point3(); final Point3 p1 = new Point3(0, 0, height), p3 = new Point3(0, 0, height); for (int k=0; k= mPts.length) ni = 0; nni++; if (nni >= mPts.length) nni = 0; } } // Convert the points into a local array so that we can verify // they are ordered correctly. A point list going the wrong // direction will invert the operation. private boolean makePts(final List pts) { if (pts == null || pts.size() < 3) return false; mPts = new Point3[pts.size()]; int k = 0; for (Point3 pt : pts) { mPts[k] = pt; k++; } final Point3[] tri = new Point3[3]; tri[0] = mPts[0]; tri[1] = mPts[1]; tri[2] = mPts[2]; if (!mOrder.isCw(tri, tri.length)) mOrder.makeReverse(mPts); // Flip the points to go from an inset to an offset // mOrder.makeReverse(mPts); return true; } // Given three ordered points (a, b, c) and a depth, find // a new point representing the bevel at the given depth. private void getBevelPoint( final Point3 a, final Point3 b, final Point3 c, final double depth, final Point3 out) { final double m_ab = Geo.perpendicularSlope(a.x, a.y, b.x, b.y), m_bc = Geo.perpendicularSlope(b.x, b.y, c.x, c.y); // Use the segment direction to decide which direction // to extend the new point in. final double dist_ab = directionedDist(a.x, a.y, b.x, b.y, depth), dist_bc = directionedDist(b.x, b.y, c.x, c.y, depth); // Get two new line segments, perpendicular to the supplied points. Geo.newPtOnLine(a.x, a.y, m_ab, dist_ab, mAb1); Geo.newPtOnLine(b.x, b.y, m_ab, dist_ab, mAb2); Geo.newPtOnLine(b.x, b.y, m_bc, dist_bc, mBc1); Geo.newPtOnLine(c.x, c.y, m_bc, dist_bc, mBc2); // Find the interesection of these lines. Geo.lineIntersection(mAb1, mAb2, mBc1, mBc2, out); } private final double directionedDist( final double ax, final double ay, final double bx, final double by, final double dist) { double deg = Geo.degree(bx-ax, by-ay); // My getDegree func doesn't correspond exactly to the // angles that determine direction. deg -= 90; if (deg < 0) deg += 360; if (deg >= 0 && deg < 180) return -dist; return dist; } }