package org.geogebra.common.kernel.algos; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.SegmentType; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoLocus; import org.geogebra.common.kernel.geos.GeoNumberValue; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.util.MyMath; /** * Creates a rounded polygon with given "vertices" and radius * * @author Zbynek */ public class AlgoRoundedPolygon extends AlgoElement { private GeoPointND[] points; private GeoLocus locus; private GeoNumberValue radius; private double r; /** * @param c * construction * @param points * points * @param radius * maximal radius */ public AlgoRoundedPolygon(Construction c, GeoPointND[] points, GeoNumberValue radius) { super(c); this.points = points; this.radius = radius; this.locus = new GeoLocus(c); setInputOutput(); compute(); } @Override protected void setInputOutput() { this.input = new GeoElement[points.length + 1]; for (int i = 0; i < points.length; i++) { input[i] = points[i].toGeoElement(); } input[input.length - 1] = radius.toGeoElement(); setOnlyOutput(locus); setDependencies(); } @Override public void compute() { locus.clearPoints(); this.r = radius.getDouble(); for (int i = 0; i < points.length; i++) { int j = i == points.length - 1 ? 0 : i + 1; int k = j == points.length - 1 ? 0 : j + 1; int l = k == points.length - 1 ? 0 : k + 1; double cos2 = tan(i, j, k); double cos1 = tan(j, k, l); double dist = points[j].distance(points[k]); this.r = Math.min(r, dist / (Math.max(Math.abs(cos2 - cos1), Math.abs(cos2 + cos1)))); } locus.insertPoint(cropX(0, 1, tan(1, 0, points.length - 1)), cropY(0, 1, tan(1, 0, points.length - 1)), SegmentType.MOVE_TO); for (int i = 0; i < points.length; i++) { int j = i == points.length - 1 ? 0 : i + 1; int k = j == points.length - 1 ? 0 : j + 1; double cos = tan(i, j, k); locus.insertPoint(cropX(j, i, cos), cropY(j, i, cos), SegmentType.LINE_TO); locus.insertPoint(points[j].getInhomX(), points[j].getInhomY(), SegmentType.AUXILIARY); locus.insertPoint(cropX(j, k, cos), cropY(j, k, cos), SegmentType.ARC_TO); } locus.setDefined(true); } private double tan(int i, int j, int k) { double angle = MyMath.angle( points[i].getInhomX() - points[j].getInhomX(), points[i].getInhomY() - points[j].getInhomY(), points[j].getInhomX() - points[k].getInhomX(), points[j].getInhomY() - points[k].getInhomY()); return Math.abs(Math.tan(angle / 2)); } private double cropX(int i, int j, double cos) { return points[i].getInhomX() + (points[j].getInhomX() - points[i].getInhomX()) * r / points[i].distance(points[j]) * cos; } private double cropY(int i, int j, double cos) { return points[i].getInhomY() + (points[j].getInhomY() - points[i].getInhomY()) * r / points[i].distance(points[j]) * cos; } @Override public GetCommand getClassName() { return Commands.RoundedPolygon; } }