📄 shapeutils.java
字号:
package cs101.awt.geom;/* * ShapeUtils.java * * Created on March 2, 2003, 2:30 PM */import java.awt.Shape;import java.awt.Point;import java.awt.Rectangle;import java.awt.geom.Point2D;import java.awt.geom.Line2D;import java.awt.geom.GeneralPath;import java.awt.geom.PathIterator;import java.awt.geom.AffineTransform;import java.awt.geom.IllegalPathStateException;import java.awt.geom.NoninvertibleTransformException;import java.util.ArrayList;import java.util.List;/** * This utility class holds routines for doing conversions on classes that * implement the <code>Shape</code> interface. Generally the first step is * in each routine is to convert the shape to a <code>GeneralPath</code>, and * return values of type <code>Shape</code> will in fact be instances of * the <code>GeneralPath</code> class unless specified otherwise. * * <a id="knotpts"></a> * <p>Java's <code>GeneralPath</code> class is a series of concatenated line * segments, second order (quadratic) Bezier curves and third order (cubic) * Bezier curves. Bezier Curves consist of two types of points: <b>knot * points</b> which are points that lie on the curve, and <b>control points</b> * which do not lie on the curve but denote vectors that influence the shape * of the curve between knot points. * * <p>None of the routines in this class attempt to convert between lines, * quadratics, and cubics. A shape consisting entirely of lines, will remain * entirely line based, and a purely cubic shape will also remain cubic. Mixed * shapes will remain mixed, but the ratios of the number of segements of any * two types is almost certain to change. * * @author Patrick G. Heck, Gus.heck@olin.edu * @version $Id: ShapeUtils.java,v 1.18 2003/09/23 15:23:40 gus Exp $ */public final class ShapeUtils { /** Creates a new instance of ShapeUtils */ private ShapeUtils() { } /** * Compute a velocity vector after a bounce off a given surface. The bounce * is computed by rotating the system until the line is vertical, * inverting the x component to calculate the bounce, and then performing * the inverse transform to get the desired. * * @param surface The line representing the angle of the surface to bounce * off of. * @param velVect A line with starting point 0,0 representing the velocity * vector to be bounced. * @return A line starting at 0,0 representing the new velocity vector. */ public static Line2D.Float bounce(Line2D.Float surface, Line2D.Float velVect) { AffineTransform rotateVert, rotateBack; double lineSlant = angleToVert(surface); rotateVert = AffineTransform.getRotateInstance(lineSlant); try { rotateBack = rotateVert.createInverse(); } catch (NoninvertibleTransformException nte) { System.out.println("NoninvertableTransformException! bounce not calculated."); throw new Error("Rotations shoud be invertable!"); } // this should rotate the line around 0,0 which is the x1,y1 point Shape s = rotateVert.createTransformedShape(velVect); PointIterator iter = new PointIterator(s); velVect = new Line2D.Float(iter.nextPoint(),iter.nextPoint()); // bouncing off a vertical line is just a matter of inverting the // x component velVect.setLine(0,0,-velVect.getX2(),velVect.getY2()); s = rotateBack.createTransformedShape(velVect); iter = new PointIterator(s); velVect = new Line2D.Float(iter.nextPoint(),iter.nextPoint()); return velVect; } /** * Find the resultant velocity from the collision of a mobile and imobile * shape. The two shapes must have already collided and be in a state of * overlap, and must be shapes consisting of more than one point. * * @param s1 The moving shape (tiny mass) * @param s2 The imobile shape (infinite mass) * @param v1X The x component of the velocity of s1 * @param v1Y The y component of the velocity of s1 * @return a line starting at the origin and representing the new velocity * vector */ public static Line2D.Float doReflect(Shape s1, Shape s2, double v1X, double v1Y) { if (!isOverlapping(s1,s2)) { //System.exit(1); throw new ShapesDontOverlapException("Cannot reflect shapes until"+ "they hit each other"); } Line2D.Float nearSeg = null; Line2D.Float velVect = new Line2D.Float(0,0, new Double(v1X).floatValue(), new Double(v1Y).floatValue()); Point2D.Float insidePt; try { insidePt = meanContainedPoint(s2,s1); // points conatained by stationary object } catch (NotEnoughPointsException nepe) { try { insidePt = meanContainedPoint(s1,s2); } catch (NotEnoughPointsException nepe2) { throw new Error("overlapping but neither conatins the other point!"); } } try { nearSeg = nearestSegment(s2,insidePt); } catch (NotEnoughPointsException nepe) { System.out.println("Ignoring malformed shape:" + nepe.getMessage()); return velVect; // pretend malformed shapes arn't there } catch (NoUniqueLineException nule) { Point2D.Float nearest = getNearestPoint(s1,insidePt); nearSeg = getLineFromNeighbors(s1,nearest); } return bounce(nearSeg, velVect); } /** * Find the resultant velocity from the collision of a mobile and imobile * shape. The two shapes must have already collided and be in a state of * overlap, and must be shapes consisting of more than one point. * * @param s1 The moving shape (tiny mass) * @param s2 The imobile shape (infinite mass) * @param velVect1 The velocity vector of s1, (x1,y1 point at 0.0 x2,y2 * representing the x and y components of the velocity) * @return a line starting at the origin and representing the new velocity * vector */ public static Line2D.Float doReflect(Shape s1, Shape s2, Line2D.Float velVect1) { return doReflect(s1,s2,velVect1.getX2(),velVect1.getY2()); } /** * Find the new x velocity after reflecting a moving shape from a imobile * shape. * * @param s1 The moving shape (tiny mass) * @param s2 The imobile shape (infinite mass) * @param v1X The x component of the velocity of s1 * @param v1Y The y component of the velocity of s1 */ public static double doXReflect(Shape s1, Shape s2, double v1X, double v1Y) { return doReflect(s1,s2,v1X,v1Y).getX2(); } /** * Find the new x velocity after reflecting a moving shape from a imobile * shape. * * @param s1 The moving shape (tiny mass) * @param s2 The imobile shape (infinite mass) * @param v1X The x component of the velocity of s1 * @param v1Y The y component of the velocity of s1 */ public static double doYReflect(Shape s1, Shape s2, double v1X, double v1Y) { return doReflect(s1,s2,v1X,v1Y).getY2(); } /** * Find the angle between a given line segment and the vertical axis in * direction that roataes the positive x axis towards positive y axis * * @param aLine A line to find the angle of * @return The angle in radians. */ public static double angleToVert(Line2D.Float aLine) { Point2D.Float start = new Point2D.Float(); Point2D.Float end = new Point2D.Float(); Point2D.Float near, far; start.setLocation(aLine.getX1(),aLine.getY1()); end.setLocation(aLine.getX2(),aLine.getY2()); double rise = start.y - end.y; double run = start.x - end.x; double tanTheta = rise/run; return Math.PI/2 - Math.atan(tanTheta); } /** * Find the line between points on either side of a knot point on a given * shape. If the shape is unclosed, and the point queried is the start * or end of the shape then the line between the querried point and the * nearest point will be returned. The shape must have more than one point. * * @param s The shape to find points from, comprised of than one point. * @param pt The point that is a knot of s for which we want a neighbors * line. */ public static Line2D.Float getLineFromNeighbors(Shape s, Point2D.Float pt) { PointIterator iter = new PointIterator(s); Point2D.Float preceding = null; Point2D.Float following = null; Point2D.Float prevPt = null; Point2D.Float currPt = iter.nextPoint(); Point2D.Float startPt = currPt; while (iter.hasNext() && iter.isStart(startPt)) { if (startPt.equals(pt)) { if (iter.hasNext()) { following = iter.nextPoint(); currPt = following; } else { throw new IllegalArgumentException("Shape has only one Point!"); } while (iter.hasNext() && iter.isStart(startPt) && (currPt != startPt)) { prevPt = currPt; currPt = iter.nextPoint(); } if (currPt == startPt) { // closed shape return new Line2D.Float(prevPt,following); } // open or discontinuous shape return new Line2D.Float(startPt,following); } prevPt = currPt; currPt = iter.nextPoint(); if (currPt.equals(pt)) { preceding = prevPt; if(iter.hasNext()) { currPt = iter.nextPoint(); } return new Line2D.Float(preceding,currPt); } } throw new IllegalArgumentException("Point was not a knot of the supplied Shape"); } /** * Find the knot point in the given shape that is closest to the specified * point. In the case of equidistant points, the qualifying point closest * to the start of the shape will be returned. The shape passed to this * method must have at least one point. * * @param s The shape to find a point from * @param pt The point to which the points of the shape are compared. * @return A point on the shape that is as close or closer than any other * point on the shape to pt */ public static Point2D.Float getNearestPoint(Shape s, Point2D.Float pt) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -