package com.project.shared.utils; import java.util.LinkedList; import com.project.shared.data.Pair; import com.project.shared.data.Point2D; public class PointUtils { public enum ConstraintMode { NONE, // identity function MEAN, // transforms both x and y to (x+y)/2 KEEP_RATIO, // uses the initialPos to change (x,y) so that the original aspect ratio is preserved SNAP_X, // sets y = 0 SNAP_Y, // sets x = 0 } public static Point2D constrain(Point2D pos, Point2D initialPos, ConstraintMode mode) { switch (mode) { case NONE: return pos; case KEEP_RATIO: return constrainKeepRatio(pos, initialPos); case MEAN: return constrainToMean(pos); case SNAP_X: return new Point2D(pos.getX(), 0); case SNAP_Y: return new Point2D(0 , pos.getY()); default: throw new RuntimeException("Unsupported transformation mode"); } } private static Point2D constrainKeepRatio(Point2D pos, Point2D initialPos) { int y = initialPos.getY(); int x = initialPos.getX(); if (y == 0) { return new Point2D(pos.getX(), 0); } if (x == 0) { return new Point2D(0, pos.getY()); } double initialRatio = x / (double)y; if (pos.getX() > pos.getY()) { return new Point2D(pos.getX(), (int)Math.round(pos.getX() / initialRatio)); } return new Point2D((int)Math.round(pos.getY() * initialRatio), pos.getY()); } private static Point2D constrainToMean(Point2D pos) { int mean = (pos.getX() + pos.getY()) / 2; return new Point2D(mean, mean); } public static Point2D nullToZero(Point2D pos) { if (null == pos) { return new Point2D(Point2D.zero); } return pos; } public static class MovingAverage { LinkedList<Point2D> points = new LinkedList<Point2D>(); private int _size; public MovingAverage(int size) { this._size = size; } public void clear() { this.points.clear(); } public void add(Point2D point) { this.points.addLast(point); if (this.points.size() > this._size) { this.points.removeFirst(); } } public Point2D getAverage() { Point2D result = Point2D.zero; for (Point2D point : points) { result = result.plus(point); } return result.mul(1.0 / this.points.size()); } } /** * Calculates the required control points for a segment of a bezier curve to follow exactly through the given points. * @See <a href="http://scaledinnovation.com/analytics/splines/splines.html">Spline Interpolation</a> on scaledinnovation.com */ public static Pair<Point2D, Point2D> getBezierControlPoints(Point2D segmentStart, Point2D segmentEnd, Point2D nextSegmentEnd, double tension) { double startToEnd = segmentStart.minus(segmentEnd).getRadius(); double endToNext = segmentEnd.minus(nextSegmentEnd).getRadius(); double fa = tension * startToEnd / (startToEnd + endToNext); double fb = fa - tension; final Point2D startToNext = segmentStart.minus(nextSegmentEnd); Point2D control1 = startToNext.mul(fa).plus(segmentEnd); Point2D control2 = startToNext.mul(fb).plus(segmentEnd); return new Pair<Point2D, Point2D>(control1, control2); } /** * Given the vertices (corners) of a polygon, returns an array of the edge * vectors. An edge vector is a vector from one vertex (corner) to the next. * * <pre>Edge number i = v[i] - v[(i-1) mod n]</pre> */ public static Point2D[] getEdgeVectors(Point2D[] corners) { Point2D[] result = new Point2D[corners.length]; for (int i = 1; i <= corners.length; i++) { int modI = i % corners.length; result[modI] = corners[modI].minus(corners[i - 1]); } return result; } }