/* * Copyright © 2009-2011 Rebecca G. Bettencourt / Kreative Software * <p> * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a> * <p> * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * <p> * Alternatively, the contents of this file may be used under the terms * of the GNU Lesser General Public License (the "LGPL License"), in which * case the provisions of LGPL License are applicable instead of those * above. If you wish to allow use of your version of this file only * under the terms of the LGPL License and not to allow others to use * your version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the LGPL License. If you do not delete * the provisions above, a recipient may use your version of this file * under either the MPL or the LGPL License. * @since PowerPaint 1.0 * @author Rebecca G. Bettencourt, Kreative Software */ package com.kreative.paint.geom; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; public class Cycloid extends CircularShape { public static final int DEFAULT_SMOOTHNESS = 6; public static final int DEFAULT_BEGIN = 0; public static final int DEFAULT_END = 0; public static final double DEFAULT_R_FOR_HYPOCYCLOID = 67.0/22.0 + 2.0; public static final double DEFAULT_R_FOR_EPICYCLOID = 67.0/22.0 - 2.0; public static final double DEFAULT_r = 1.0; public static final double DEFAULT_d = -3.0; public static final int DEFAULT_CURVE_FACTOR = 6; public static final int DEFAULT_FIXED_RING = 67; public static final int DEFAULT_ROLLING_WHEEL = 22; public static final int DEFAULT_PEN_POSITION = 3; // true if this is an epitrochoid, false if this is a hypotrochoid private boolean epi; // smoothness is the resolution of theta (or number of steps) over 2*pi private int smoothness; // theta starts at begin * 2pi private int begin; // theta ends at end * 2pi; if end == begin then theta ends only when we reach a point we've reached before private int end; // the radius of the outer circle private double R; // the radius of the inner circle private double r; // the position of the pen from the center of the inner circle private double d; // the center of the outer circle private Point2D.Float center; // a point at a distance from the outer circle's center that determines the maximum size of the cycloid private Point2D.Float endPoint; // the path private GeneralPath path; public Cycloid(boolean epi, int smoothness, int begin, int end, double R, double r, double d, float cx, float cy, float ex, float ey) { this.epi = epi; this.smoothness = smoothness; this.begin = begin; this.end = end; this.R = R; this.r = r; this.d = d; this.center = new Point2D.Float(cx, cy); this.endPoint = new Point2D.Float(ex, ey); this.path = makePath(); } public Cycloid(boolean epi, int curveFactor, int fixedRing, int rollingWheel, int penPosition, float cx, float cy, float ex, float ey) { this.epi = epi; this.smoothness = curveFactor; this.begin = 0; this.end = 0; this.R = (double)rollingWheel / (double)fixedRing + (epi ? -2.0 : 2.0); this.r = 1.0; this.d = -penPosition; this.center = new Point2D.Float(cx, cy); this.endPoint = new Point2D.Float(ex, ey); this.path = makePath(); } public Cycloid clone() { return new Cycloid(epi, smoothness, begin, end, R, r, d, (float)center.getX(), (float)center.getY(), (float)endPoint.getX(), (float)endPoint.getY()); } public boolean isEpicycloid() { return epi; } public int getSmoothness() { return smoothness; } public int getBegin() { return begin; } public int getEnd() { return end; } public double getR() { return R; } public double getr() { return r; } public double getd() { return d; } public Point2D.Float getCenter() { return center; } public Point2D.Float getEndpoint() { return endPoint; } public double getCenterX() { return center.x; } public double getCenterY() { return center.y; } public double getEndpointX() { return endPoint.x; } public double getEndpointY() { return endPoint.y; } public void setCircle(double cx, double cy, double ex, double ey) { this.center = new Point2D.Float((float)cx, (float)cy); this.endPoint = new Point2D.Float((float)ex, (float)ey); this.path = makePath(); } private GeneralPath makePath() { GeneralPath p = new GeneralPath(); double rf = Math.hypot(endPoint.x-center.x, endPoint.y-center.y) / (epi ? (Math.abs(R+r)+Math.abs(d)) : (Math.abs(R-r)+Math.abs(d))); double th0 = begin*2.0*Math.PI; double x0 = epi ? ((R+r)*Math.cos(th0) - d*Math.cos(((R+r)*th0)/r)) : ((R-r)*Math.cos(th0) + d*Math.cos(((R-r)*th0)/r)); double y0 = epi ? ((R+r)*Math.sin(th0) - d*Math.sin(((R+r)*th0)/r)) : ((R-r)*Math.sin(th0) - d*Math.sin(((R-r)*th0)/r)); double xx0 = center.x + x0*rf; double yy0 = center.y - y0*rf; p.moveTo((float)xx0, (float)yy0); boolean repeated = false; for (int i = begin; (begin == end) ? (!repeated) : (i <= end); i++) { for (int j = 1; (begin == end) ? (j <= smoothness && !repeated) : (j <= smoothness); j++) { double th = ( (double)i + (double)j/(double)smoothness )*2.0*Math.PI; double x = epi ? ((R+r)*Math.cos(th) - d*Math.cos(((R+r)*th)/r)) : ((R-r)*Math.cos(th) + d*Math.cos(((R-r)*th)/r)); double y = epi ? ((R+r)*Math.sin(th) - d*Math.sin(((R+r)*th)/r)) : ((R-r)*Math.sin(th) - d*Math.sin(((R-r)*th)/r)); double xx = center.x + x*rf; double yy = center.y - y*rf; p.lineTo((float)xx, (float)yy); if ((Math.abs(x-x0) < 1e-10) && (Math.abs(y-y0) < 1e-10)) repeated = true; } } return p; } public boolean contains(Point2D p) { return path.contains(p); } public boolean contains(Rectangle2D r) { return path.contains(r); } public boolean contains(double x, double y) { return path.contains(x,y); } public boolean contains(double x, double y, double w, double h) { return path.contains(x,y,w,h); } public Rectangle getBounds() { return path.getBounds(); } public Rectangle2D getBounds2D() { return path.getBounds2D(); } public PathIterator getPathIterator(AffineTransform at) { return path.getPathIterator(at); } public PathIterator getPathIterator(AffineTransform at, double flatness) { return path.getPathIterator(at, flatness); } public boolean intersects(Rectangle2D r) { return path.intersects(r); } public boolean intersects(double x, double y, double w, double h) { return path.intersects(x,y,w,h); } public String toString() { return "com.kreative.paint.geom.Cycloid["+smoothness+","+begin+","+end+","+epi+","+R+","+r+","+d+","+center+","+endPoint+"]"; } }