/*
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.
*/
package org.geogebra.common.kernel.algos;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.commands.Commands;
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.GeoConicNDConstants;
import org.geogebra.common.kernel.kernelND.GeoConicPartND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.util.debug.Log;
/**
* Circle arc or sector defined by three points.
*/
public abstract class AlgoConicPartCircumcircleND extends AlgoConicPart {
protected GeoPointND A, B, C;
private GeoLine line; // for degenerate case
/**
* @param cons
* construction
* @param label
* label
* @param A
* start point
* @param B
* point on arc
* @param C
* end point
* @param type
* conic type
*/
public AlgoConicPartCircumcircleND(Construction cons, String label,
GeoPointND A, GeoPointND B, GeoPointND C, int type) {
this(cons, A, B, C, type);
conicPart.setLabel(label);
}
/**
* @param cons
* construction
* @param A
* start point
* @param B
* point on arc
* @param C
* end point
* @param type
* conic type
*/
public AlgoConicPartCircumcircleND(Construction cons, GeoPointND A,
GeoPointND B, GeoPointND C, int type) {
super(cons, type);
this.A = A;
this.B = B;
this.C = C;
// helper algo to get circle
AlgoCircleThreePoints algo = getAlgo();
cons.removeFromConstructionList(algo);
conic = algo.getCircle();
conicPart = createConicPart(cons, type);
conicPart.addPointOnConic(A);
conicPart.addPointOnConic(B);
conicPart.addPointOnConic(C);
setInputOutput(); // for AlgoElement
compute();
setIncidence();
}
/**
*
* @param cons1
* construction
* @param type1
* part type (arc or sector)
* @return output conic part
*/
abstract protected GeoConicND createConicPart(Construction cons1,
int type1);
/**
*
* @return circle algo
*/
abstract protected AlgoCircleThreePoints getAlgo();
private void setIncidence() {
A.addIncidence(conicPart, false);
B.addIncidence(conicPart, false);
C.addIncidence(conicPart, false);
}
@Override
public Commands getClassName() {
switch (type) {
case GeoConicNDConstants.CONIC_PART_ARC:
return Commands.CircumcircleArc;
default:
return Commands.CircumcircleSector;
}
}
@Override
public int getRelatedModeID() {
switch (type) {
case GeoConicNDConstants.CONIC_PART_ARC:
return EuclidianConstants.MODE_CIRCUMCIRCLE_ARC_THREE_POINTS;
default:
return EuclidianConstants.MODE_CIRCUMCIRCLE_SECTOR_THREE_POINTS;
}
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[3];
input[0] = (GeoElement) A;
input[1] = (GeoElement) B;
input[2] = (GeoElement) C;
super.setOutputLength(1);
super.setOutput(0, conicPart);
setDependencies();
}
@Override
public void compute() {
if (!conic.isDefined()) {
conicPart.setUndefined();
return;
}
conicPart.set(conic);
switch (conicPart.getType()) {
case GeoConicNDConstants.CONIC_PARALLEL_LINES:
computeDegenerate();
break;
case GeoConicNDConstants.CONIC_CIRCLE:
computeCircle();
break;
case GeoConicNDConstants.CONIC_SINGLE_POINT:
computeSinglePoint();
break;
default:
// this should not happen
Log.debug("AlgoCirclePartPoints: unexpected conic type: "
+ conicPart.getType());
conicPart.setUndefined();
}
}
// arc degenerated to segment or two rays
private void computeDegenerate() {
if (line == null) { // init lines
line = conicPart.getLines()[0];
conicPart.getLines()[1].setStartPoint(getC());
}
line.setStartPoint(getA());
line.setEndPoint(getC());
// make sure the line goes through A and C
GeoVec3D.lineThroughPoints(getA(), getC(), line);
// check if B is between A and C => (1) segment AC
// otherwise we got (2) two rays starting at A and C in oposite
// directions
// case (1): use parameters 0, 1 and positive orientation to tell
// conicPart how to behave
// case (2): use parameters 0, 1 and negative orientation
double lambda = GeoPoint.affineRatio(getA(), getC(), getB());
if (lambda < 0 || lambda > 1) {
// two rays
// second ray with start point C and direction of AC
conicPart.getLines()[1].setCoords(line);
conicPart.getLines()[1].setStartPoint(getC());
// first ray with start point A and oposite direction
line.changeSign();
// tell conicPart about this case: two rays
((GeoConicPartND) conicPart).setParameters(0, 1, false);
} else {
// segment
// tell conicPart about this case: one segment
((GeoConicPartND) conicPart).setParameters(0, 1, true);
}
}
// circle through A, B, C
private void computeCircle() {
// start angle from vector MA
double alpha = Math.atan2(
getAy() - conicPart.getTranslationVector().getY(),
getAx() - conicPart.getTranslationVector().getX());
// end angle from vector MC
double beta = Math.atan2(
getCy() - conicPart.getTranslationVector().getY(),
getCx() - conicPart.getTranslationVector().getX());
// check orientation of triangle A, B, C to see
// whether we have to swap start and end angle
double det = (getBx() - getAx()) * (getCy() - getAy())
- (getBy() - getAy()) * (getCx() - getAx());
((GeoConicPartND) conicPart).setParameters(alpha, beta, det > 0);
}
/**
* compute as single point (A)
*/
protected void computeSinglePoint() {
((GeoConicPartND) conicPart).setParametersToSinglePoint();
}
/**
* Method for LocusEqu.
*
* @return first point.
*/
abstract public GeoPoint getA();
/**
* Method for LocusEqu.
*
* @return second point.
*/
abstract public GeoPoint getB();
/**
* Method for LocusEqu.
*
* @return third point.
*/
abstract public GeoPoint getC();
final private double getAx() {
return getA().inhomX;
}
final private double getAy() {
return getA().inhomY;
}
final private double getBx() {
return getB().inhomX;
}
final private double getBy() {
return getB().inhomY;
}
final private double getCx() {
return getC().inhomX;
}
final private double getCy() {
return getC().inhomY;
}
}