/*
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.
*/
/*
* AlgoAngularBisectorLines.java
*
* Created on 26. Oktober 2001
*/
package org.geogebra.common.kernel.algos;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.StringTemplate;
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.geos.GeoVector;
import org.geogebra.common.kernel.kernelND.GeoElementND;
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;
/**
* Angle bisectors between two lines
*
* @author Markus
*/
public class AlgoAngularBisectorLines extends AlgoElement
implements SymbolicParametersBotanaAlgo {
private GeoLine g, h; // input
private GeoLine[] bisector; // output
// temp
private double gx, gy, hx, hy, wx, wy, bx, by, lenH, lenG, length, ip;
private GeoVector[] wv; // direction of bisector line bisector
private GeoPoint B; // intersection point of g, h
private boolean infiniteB;
private int index;
private PPolynomial[] botanaPolynomials;
private PVariable[] botanaVars;
/**
* Creates new AlgoAngularBisectorLines
*
* @param cons
* @param label
* @param g
* @param h
*/
AlgoAngularBisectorLines(Construction cons, String label, GeoLine g,
GeoLine h) {
this(cons, g, h);
GeoElement.setLabels(label, bisector);
}
public AlgoAngularBisectorLines(Construction cons, String[] labels,
GeoLine g, GeoLine h) {
this(cons, g, h);
GeoElement.setLabels(labels, bisector);
}
@Override
public Commands getClassName() {
return Commands.AngularBisector;
}
@Override
public int getRelatedModeID() {
return EuclidianConstants.MODE_ANGULAR_BISECTOR;
}
AlgoAngularBisectorLines(Construction cons, GeoLine g, GeoLine h) {
super(cons);
this.g = g;
this.h = h;
bisector = new GeoLine[2];
bisector[0] = new GeoLine(cons);
bisector[1] = new GeoLine(cons);
setInputOutput(); // for AlgoElement
wv = new GeoVector[2];
wv[0] = new GeoVector(cons);
wv[0].setCoords(0, 0, 0);
wv[1] = new GeoVector(cons);
wv[1].setCoords(0, 0, 0);
B = new GeoPoint(cons);
bisector[0].setStartPoint(B);
bisector[1].setStartPoint(B);
// compute bisectors of lines g, h
compute();
}
// for AlgoElement
@Override
public void setInputOutput() {
input = new GeoElement[2];
input[0] = g;
input[1] = h;
super.setOutput(bisector);
setDependencies(); // done by AlgoElement
}
public GeoLine[] getLines() {
return bisector;
}
// Made public for LocusEqu
public GeoLine getg() {
return g;
}
// Made public for LocusEqu
public GeoLine geth() {
return h;
}
// Made public for LocusEqu
public GeoPoint getB() {
return B;
}
@Override
public boolean isNearToAlgorithm() {
return true;
}
@Override
public final void compute() {
// calc intersection B of g and h
GeoVec3D.cross(g, h, B);
infiniteB = B.isInfinite();
// (gx, gy) is direction of g = B v A
gx = g.y;
gy = -g.x;
lenG = MyMath.length(gx, gy);
gx /= lenG;
gy /= lenG;
// (hx, hy) is direction of h = B v C
hx = h.y;
hy = -h.x;
lenH = MyMath.length(hx, hy);
hx /= lenH;
hy /= lenH;
// set direction vector of bisector: (wx, wy)
if (infiniteB) {
// if B is at infinity then g and h are parallel
// and the bisector line has same direction as g (or h)
// calc z value of line in the middle of g, h
// orientation of g, h may differ: 2 cases
if (gx * hx + gy * hy > 0) { // same orientation
index = 0; // set first bisector
bisector[index].z = (g.z / lenG + h.z / lenH) / 2.0;
} else { // different orientation
index = 1; // set second bisector
bisector[index].z = (g.z / lenG - h.z / lenH) / 2.0;
}
// take direction of g as proposed direction for bisector
wx = gx;
wy = gy;
if (kernel.isContinuous()) {
// init old direction of bisectors
if (bisector[0].isDefined()) {
wv[0].x = bisector[0].y;
wv[0].y = -bisector[0].x;
}
if (bisector[1].isDefined()) {
wv[1].x = bisector[1].y;
wv[1].y = -bisector[1].x;
}
// NEAR TO RELATIONSHIP
// check orientation: take smallest change!!!
if (wv[index].x * wx + wv[index].y * wy >= 0) {
wv[index].x = wx;
wv[index].y = wy;
} else { // angle > 180degrees, change orientation
wv[index].x = -wx;
wv[index].y = -wy;
bisector[index].z = -bisector[index].z;
}
} else {
// non continuous
wv[index].x = wx;
wv[index].y = wy;
}
// set direction vector of bisector
bisector[index].x = -wv[index].y;
bisector[index].y = wv[index].x;
// ohter bisector is undefined
bisector[1 - index].setUndefined();
}
// standard case: B is not at infinity
else {
// calc direction vector (wx, wy) of angular bisector
// check if angle between vectors is > 90degrees
ip = gx * hx + gy * hy;
if (ip >= 0.0) { // angle < 90degrees
// standard case
wx = gx + hx;
wy = gy + hy;
} else { // ip <= 0.0, angle > 90degrees
// BC - BA is a normalvector of the bisector
wx = hy - gy;
wy = gx - hx;
// if angle > 180 degree change orientation of direction
// det(g,h) < 0
if (gx * hy < gy * hx) {
wx = -wx;
wy = -wy;
}
}
// make (wx, wy) a unit vector
length = MyMath.length(wx, wy);
wx /= length;
wy /= length;
if (kernel.isContinuous()) {
// init old direction of bisectors
if (bisector[0].isDefined()) {
wv[0].x = bisector[0].y;
wv[0].y = -bisector[0].x;
}
if (bisector[1].isDefined()) {
wv[1].x = bisector[1].y;
wv[1].y = -bisector[1].x;
}
// check orientations: take smallest change!!!
// first bisector: relativ to (wx, wy)
if (wv[0].x * wx + wv[0].y * wy >= 0) {
wv[0].x = wx;
wv[0].y = wy;
} else { // angle > 180 degree change orientation
wv[0].x = -wx;
wv[0].y = -wy;
}
// second bisector: relativ to (-wy, wx)
if (wv[1].y * wx - wv[1].x * wy >= 0) {
wv[1].x = -wy;
wv[1].y = wx;
} else { // angle > 180 degree change orientation
wv[1].x = wy;
wv[1].y = -wx;
}
} else {
// non continuous
wv[0].x = wx;
wv[0].y = wy;
wv[1].x = -wy;
wv[1].y = wx;
}
// calc B's coords
bx = B.inhomX;
by = B.inhomY;
// set first bisector through B
bisector[0].x = -wv[0].y;
bisector[0].y = wv[0].x;
bisector[0].z = -(bx * bisector[0].x + by * bisector[0].y);
// set second bisector perpendicular to first through B
bisector[1].x = -wv[1].y;
bisector[1].y = wv[1].x;
bisector[1].z = -(bx * bisector[1].x + by * bisector[1].y);
}
}
@Override
final public String toString(StringTemplate tpl) {
// Michael Borcherds 2008-03-30
// simplified to allow better Chinese translation
return getLoc().getPlain("AngleBisectorOfAB", g.getLabel(tpl),
h.getLabel(tpl));
}
@Override
public PVariable[] getBotanaVars(GeoElementND geo) {
return botanaVars;
}
@Override
public PPolynomial[] getBotanaPolynomials(GeoElementND geo)
throws NoSymbolicParametersException {
if (botanaPolynomials != null) {
return botanaPolynomials;
}
GeoLine lg = getg();
GeoLine lh = geth();
if (lg != null && lh != null) {
/*
* We need to compute this.B symbolically since it is not computed
* automatically in this class.
*/
PVariable[] varsB, varsLg, varsLh;
varsB = (this.B).getBotanaVars(this.B);
varsLg = lg.getBotanaVars(lg);
varsLh = lh.getBotanaVars(lh);
PPolynomial[] polysB = (this.B).getBotanaPolynomials(this.B);
if (polysB == null) {
// if already exists, let's use it, if not, create a new one
polysB = new PPolynomial[2];
polysB[0] = PPolynomial.collinear(varsB[0], varsB[1], varsLg[0],
varsLg[1], varsLg[2], varsLg[3]);
polysB[1] = PPolynomial.collinear(varsB[0], varsB[1], varsLh[0],
varsLh[1], varsLh[2], varsLh[3]);
}
PVariable[] vA = new PVariable[2];
vA[0] = varsLg[0];
vA[1] = varsLg[1];
PVariable[] vB = new PVariable[2];
vB[0] = varsLh[0];
vB[1] = varsLh[1];
PVariable[] vC = varsB;
botanaPolynomials = new PPolynomial[4];
botanaPolynomials[2] = polysB[0];
botanaPolynomials[3] = polysB[1];
// from now on we use the equations from
// AlgoAngularBisectorPoints
if (botanaVars == null) {
botanaVars = new PVariable[4];
// M
botanaVars[0] = new PVariable(kernel);
botanaVars[1] = new PVariable(kernel);
// A
botanaVars[2] = vC[0];
botanaVars[3] = vC[1];
}
PPolynomial a1 = new PPolynomial(vA[0]);
PPolynomial a2 = new PPolynomial(vA[1]);
PPolynomial b1 = new PPolynomial(vB[0]);
PPolynomial b2 = new PPolynomial(vB[1]);
PPolynomial c1 = new PPolynomial(vC[0]);
PPolynomial c2 = new PPolynomial(vC[1]);
PPolynomial m1 = new PPolynomial(botanaVars[0]); // d1
PPolynomial m2 = new PPolynomial(botanaVars[1]); // d2
// A,M,B collinear (needed for easing computations)
botanaPolynomials[0] = PPolynomial.collinear(vA[0], vA[1], vB[0],
vB[1], botanaVars[0], botanaVars[1]);
// (b1-c1)*(c1-d1)
PPolynomial p1 = b1.subtract(c1).multiply(c1.subtract(m1));
// (b2-c2)*(c2-d2)
PPolynomial p2 = b2.subtract(c2).multiply(c2.subtract(m2));
// (a1-c1)^2+(a2-c2)^2
PPolynomial p3 = (PPolynomial.sqr(a1.subtract(c1)))
.add(PPolynomial.sqr(a2.subtract(c2)));
// (a1-c1)*(c1-d1)
PPolynomial p4 = a1.subtract(c1).multiply(c1.subtract(m1));
// (a2-c2)*(c2-d2)
PPolynomial p5 = a2.subtract(c2).multiply(c2.subtract(m2));
// (b1-c1)^2+(b2-c2)^2
PPolynomial p6 = PPolynomial.sqr(b1.subtract(c1))
.add(PPolynomial.sqr(b2.subtract(c2)));
// ((b1-c1)*(c1-d1)+(b2-c2)*(c2-d2))^2*((a1-c1)^2+(a2-c2)^2)
// -((a1-c1)*(c1-d1)+(a2-c2)*(c2-d2))^2*((b1-c1)^2+(b2-c2)^2)
botanaPolynomials[1] = PPolynomial.sqr((p1.add(p2))).multiply(p3)
.subtract(PPolynomial.sqr(p4.add(p5)).multiply(p6));
return botanaPolynomials;
}
throw new NoSymbolicParametersException();
}
}