/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. */ /* * AlgoCircleThreePoints.java * * Created on 15. November 2001, 21:37 */ package org.geogebra.common.kernel.algos; import org.geogebra.common.euclidian.EuclidianConstants; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoConic; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoLine; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.kernel.geos.GeoVec3D; import org.geogebra.common.kernel.kernelND.GeoConicND; import org.geogebra.common.kernel.kernelND.GeoElementND; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.kernel.prover.NoSymbolicParametersException; import org.geogebra.common.kernel.prover.polynomial.PPolynomial; import org.geogebra.common.kernel.prover.polynomial.PVariable; import org.geogebra.common.util.MyMath; //import geogebra.kernel.kernelND.GeoConicND; /** * * @author Markus */ public class AlgoCircleThreePoints extends AlgoElement implements SymbolicParametersBotanaAlgo { private GeoPointND A, B, C; // input // protected GeoConicND circle; // output protected GeoConicND circle; // output // line bisectors private GeoLine s0, s1; private GeoPoint center; private double[] det = new double[3]; transient private double ax, ay, bx, by, cx, cy, ABx, ABy, ACx, ACy, BCx, BCy, maxDet; transient private int casenr; private BotanaCircleThreePoints botanaParams; public AlgoCircleThreePoints(Construction cons, String label, GeoPointND A, GeoPointND B, GeoPointND C) { this(cons, A, B, C); circle.setLabel(label); } public AlgoCircleThreePoints(Construction cons, GeoPointND A, GeoPointND B, GeoPointND C) { super(cons); setPoints(A, B, C); createCircle(); circle.addPointOnConic(getA()); // move into setIncidence(); circle.addPointOnConic(getB()); circle.addPointOnConic(getC()); // temp: line bisectors s0 = new GeoLine(cons); s1 = new GeoLine(cons); center = new GeoPoint(cons); setInputOutput(); // for AlgoElement compute(); setIncidence(); } private void setIncidence() { if (A instanceof GeoPoint) { ((GeoPoint) A).addIncidence(circle, false); } if (B instanceof GeoPoint) { ((GeoPoint) B).addIncidence(circle, false); } if (C instanceof GeoPoint) { ((GeoPoint) C).addIncidence(circle, false); } } /** * set the three points of the circle to A, B, C * * @param A * first point * @param B * second point * @param C * third point */ protected void setPoints(GeoPointND A, GeoPointND B, GeoPointND C) { this.A = A; this.B = B; this.C = C; } /** * create the object circle */ protected void createCircle() { circle = new GeoConic(cons); } @Override public Commands getClassName() { return Commands.Circle; } @Override public int getRelatedModeID() { return EuclidianConstants.MODE_CIRCLE_THREE_POINTS; } // for AlgoElement @Override protected void setInputOutput() { setInput(); setOutput(); setDependencies(); // done by AlgoElement } protected void setInput() { input = new GeoElement[3]; input[0] = A.toGeoElement(); input[1] = B.toGeoElement(); input[2] = C.toGeoElement(); } protected void setOutput() { super.setOutputLength(1); super.setOutput(0, circle); } // public GeoConicND getCircle() { public GeoConicND getCircle() { return circle; } public GeoPoint getA() { return (GeoPoint) A; } public GeoPoint getB() { return (GeoPoint) B; } public GeoPoint getC() { return (GeoPoint) C; } // compute circle through A, B, C @Override public void compute() { // A, B or C undefined if (!getA().isFinite() || !getB().isFinite() || !getC().isFinite()) { circle.setUndefined(); return; } // get inhomogenous coords of points ax = getA().inhomX; ay = getA().inhomY; bx = getB().inhomX; by = getB().inhomY; cx = getC().inhomX; cy = getC().inhomY; // Log.debug("\n"+ax+","+ay+"\n"+bx+","+by+"\n"+cx+","+cy); // same points if (Kernel.isEqual(ax, bx) && Kernel.isEqual(ay, by)) { // A = B if (Kernel.isEqual(ax, cx) && Kernel.isEqual(ay, cy)) { // A = B = C circle.setCircle(getA(), 0.0); // single point return; } // else{ // A = B <> C ACx = cx - ax; ACy = cy - ay; center.setCoords(-ACy, ACx, 0.0d); circle.setCircle(center, getA()); return; // } } else if (Kernel.isEqual(ax, cx) && Kernel.isEqual(ay, cy)) { // A = C // <> B ABx = bx - ax; ABy = by - ay; center.setCoords(-ABy, ABx, 0.0d); circle.setCircle(center, getA()); return; } else if (Kernel.isEqual(bx, cx) && Kernel.isEqual(by, cy)) { // B = C // <> A ACx = cx - ax; ACy = cy - ay; center.setCoords(-ACy, ACx, 0.0d); circle.setCircle(center, getA()); return; } // calc vectors AB, AC, BC ABx = bx - ax; ABy = by - ay; ACx = cx - ax; ACy = cy - ay; BCx = cx - bx; BCy = cy - by; double lengthAB = MyMath.length(ABx, ABy); double lengthAC = MyMath.length(ACx, ACy); double lengthBC = MyMath.length(BCx, BCy); // find the two bisectors with max intersection angle // i.e. maximum abs of determinant of directions // max( abs(det(AB, AC)), abs(det(AC, BC)), abs(det(AB, BC)) ) det[0] = Math.abs(ABx * ACy - ABy * ACx) / (lengthAB * lengthAC); // AB, AC det[1] = Math.abs(ACx * BCy - ACy * BCx) / (lengthAC * lengthBC); // AC, BC det[2] = Math.abs(ABx * BCy - ABy * BCx) / (lengthAB * lengthBC); // AB, BC // take ip[0] as init minimum and find minimum case maxDet = det[0]; casenr = 0; if (det[1] > maxDet) { casenr = 1; maxDet = det[1]; } if (det[2] > maxDet) { casenr = 2; maxDet = det[2]; } // A, B, C are collinear: set M to infinite point // in perpendicular direction of AB if (Kernel.isZero(maxDet)) { center.setCoords(-ABy, ABx, 0.0d); circle.setCircle(center, getA()); } // standard case else { // intersect two line bisectors according to casenr switch (casenr) { default: case 0: // bisectors of AB, AC s0.x = ABx; s0.y = ABy; s0.z = -((ax + bx) * s0.x + (ay + by) * s0.y) / 2.0; s1.x = ACx; s1.y = ACy; s1.z = -((ax + cx) * s1.x + (ay + cy) * s1.y) / 2.0; break; case 1: // bisectors of AC, BC s1.x = ACx; s1.y = ACy; s1.z = -((ax + cx) * s1.x + (ay + cy) * s1.y) / 2.0; s0.x = BCx; s0.y = BCy; s0.z = -((bx + cx) * s0.x + (by + cy) * s0.y) / 2.0; break; case 2: // bisectors of AB, BC s0.x = ABx; s0.y = ABy; s0.z = -((ax + bx) * s0.x + (ay + by) * s0.y) / 2.0; s1.x = BCx; s1.y = BCy; s1.z = -((bx + cx) * s1.x + (by + cy) * s1.y) / 2.0; break; } // intersect line bisectors to get midpoint GeoVec3D.cross(s0, s1, center); circle.setCircle(center, center.distance(getA())); } } @Override public String toString(StringTemplate tpl) { // Michael Borcherds 2008-03-30 // simplified to allow better Chinese translation return getLoc().getPlain("CircleThroughABC", A.getLabel(tpl), B.getLabel(tpl), C.getLabel(tpl)); } @Override public PVariable[] getBotanaVars(GeoElementND geo) { if (botanaParams == null) { botanaParams = new BotanaCircleThreePoints(); } return botanaParams.getVars(); } @Override public PPolynomial[] getBotanaPolynomials(GeoElementND geo) throws NoSymbolicParametersException { if (botanaParams == null) { botanaParams = new BotanaCircleThreePoints(); } return botanaParams.getPolynomials(getInput()); } }