/*
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.
*/
/*
* AlgoMirrorPointPoint.java
*
* Created on 24. September 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.Region;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.ConicMirrorable;
import org.geogebra.common.kernel.geos.GeoConic;
import org.geogebra.common.kernel.geos.GeoConicPart;
import org.geogebra.common.kernel.geos.GeoCurveCartesian;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoLine;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoPoly;
import org.geogebra.common.kernel.geos.GeoRay;
import org.geogebra.common.kernel.geos.GeoSegment;
import org.geogebra.common.kernel.geos.GeoVec2D;
import org.geogebra.common.kernel.geos.Mirrorable;
import org.geogebra.common.kernel.implicit.GeoImplicit;
import org.geogebra.common.kernel.kernelND.GeoConicNDConstants;
import org.geogebra.common.kernel.kernelND.GeoConicPartND;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoLineND;
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;
/**
*
* @author Markus
*/
public class AlgoMirror extends AlgoTransformation implements
SymbolicParametersBotanaAlgo {
protected Mirrorable out;
protected GeoElement inGeo;
protected GeoElement outGeo;
private GeoLineND mirrorLine;
protected GeoPointND mirrorPoint;
private GeoConic mirrorConic;
protected GeoElement mirror;
private GeoPoint transformedPoint;
private MirrorBotana mirrorBotana;
/**
* Creates new "mirror at point" algo
*
* @param cons
* @param label
* @param in
* @param p
*/
protected AlgoMirror(Construction cons, String label, GeoElement in,
GeoPointND p) {
this(cons, in, p);
outGeo.setLabel(label);
}
/**
* Creates new "mirror at point" algo
*
* @param cons
* @param in
* @param p
*/
public AlgoMirror(Construction cons, GeoElement in, GeoPointND p) {
this(cons);
mirrorPoint = p;
endOfConstruction(cons, in, (GeoElement) p);
}
/**
* Creates new "mirror at conic" algo
*
* @param cons
* @param label
* @param in
* @param c
*/
AlgoMirror(Construction cons, String label, GeoElement in, GeoConic c) {
this(cons, in, c);
outGeo.setLabel(label);
}
/**
* Creates new "mirror at conic" algo
*
* @param cons
* @param in
* @param c
*/
public AlgoMirror(Construction cons, GeoElement in, GeoConic c) {
this(cons);
mirrorConic = c;
endOfConstruction(cons, in, c);
}
/**
* Creates new "mirror at line" algo
*
* @param cons
* @param label
* @param in
* @param g
*/
AlgoMirror(Construction cons, String label, GeoElement in, GeoLineND g) {
this(cons, in, g);
outGeo.setLabel(label);
}
/**
* Creates new "mirror at line" algo
*
* @param cons
* @param label
* @param in
* @param g
*/
public AlgoMirror(Construction cons, GeoElement in, GeoLineND g) {
this(cons);
mirrorLine = g;
endOfConstruction(cons, in, (GeoElement) g);
}
/**
* used for 3D
*
* @param cons
* cons
*/
protected AlgoMirror(Construction cons) {
super(cons);
}
/**
* end of construction
*
* @param cons
* cons
* @param in
* transformed geo
* @param mirror
* mirror
*/
public void endOfConstruction(Construction cons, GeoElement in,
GeoElement mirror) {
this.mirror = mirror;
inGeo = in;
outGeo = getResultTemplate(inGeo);
if (outGeo instanceof Mirrorable) {
out = (Mirrorable) outGeo;
}
setInputOutput();
transformedPoint = new GeoPoint(cons);
compute();
if (inGeo.isGeoFunction()) {
cons.registerEuclidianViewCE(this);
}
}
@Override
public Commands getClassName() {
return Commands.Mirror;
}
@Override
public int getRelatedModeID() {
if (mirror.isGeoLine()) {
return EuclidianConstants.MODE_MIRROR_AT_LINE;
} else if (mirror.isGeoPoint()) {
return EuclidianConstants.MODE_MIRROR_AT_POINT;
} else {
return EuclidianConstants.MODE_MIRROR_AT_CIRCLE;
}
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[2];
input[0] = inGeo;
input[1] = mirror;
setOutputLength(1);
setOutput(0, outGeo);
setDependencies(); // done by AlgoElement
}
/**
* Returns the transformed geo
*
* @return transformed geo
*/
@Override
public GeoElement getResult() {
return outGeo;
}
@Override
public final void compute() {
if (!mirror.isDefined()) {
outGeo.setUndefined();
return;
}
if (inGeo.isGeoList()) {
transformList((GeoList) inGeo, (GeoList) outGeo);
return;
}
setOutGeo();
if (!outGeo.isDefined()) {
return;
}
if (inGeo.isRegion() && mirror == mirrorConic) {
GeoVec2D v = mirrorConic.getTranslationVector();
outGeo.setInverseFill(
((Region) inGeo).isInRegion(v.getX(), v.getY())
^ inGeo.isInverseFill());
}
computeRegardingMirror();
if (inGeo.isLimitedPath()) {
this.transformLimitedPath(inGeo, outGeo);
}
}
/**
* compute regarding which mirror type is used
*/
protected void computeRegardingMirror() {
if (mirror == mirrorLine) {
if (mirrorLine.getStartPoint() == null) {
mirrorLine.setStandardStartPoint();
}
out.mirror(mirrorLine);
} else if (mirror == mirrorPoint) {
if (outGeo.isGeoFunction()) {
((GeoFunction) outGeo).mirror(getMirrorCoords());
} else {
out.mirror(getMirrorCoords());
}
} else {
((ConicMirrorable) out).mirror(mirrorConic);
}
}
/**
* set inGeo to outGeo
*/
protected void setOutGeo() {
if (mirror instanceof GeoConic && inGeo instanceof GeoLine) {
((GeoLine) inGeo).toGeoConic((GeoConic) outGeo);
}
/*
* else if(mirror instanceof GeoConic && geoIn instanceof GeoConic &&
* geoOut instanceof GeoCurveCartesian){
* ((GeoConic)geoIn).toGeoCurveCartesian((GeoCurveCartesian)geoOut); }
*/
else if (mirror instanceof GeoConic && inGeo instanceof GeoConic
&& outGeo instanceof GeoImplicit) {
((GeoConic) inGeo).toGeoImplicitCurve((GeoImplicit) outGeo);
} else if (inGeo instanceof GeoFunction && mirror != mirrorPoint) {
((GeoFunction) inGeo)
.toGeoCurveCartesian((GeoCurveCartesian) outGeo);
} else if (inGeo instanceof GeoPoly && mirror == mirrorConic) {
((GeoPoly) inGeo).toGeoCurveCartesian((GeoCurveCartesian) outGeo);
} else {
outGeo.set(inGeo);
}
}
/**
*
* @return inhom coords for mirror point
*/
protected Coords getMirrorCoords() {
return mirrorPoint.getInhomCoords();
}
@Override
final public String toString(StringTemplate tpl) {
// Michael Borcherds 2008-03-31
// simplified to allow better translation
return getLoc().getPlain("AMirroredAtB", inGeo.getLabel(tpl),
mirror.getLabel(tpl));
}
@Override
protected void setTransformedObject(GeoElement g, GeoElement g2) {
inGeo = g;
outGeo = g2;
if (!(outGeo instanceof GeoList)) {
out = (Mirrorable) outGeo;
}
}
@Override
protected GeoElement getResultTemplate(GeoElement geo) {
if ((geo instanceof GeoPoly) && mirror == mirrorConic) {
return new GeoCurveCartesian(cons);
}
if ((geo instanceof GeoFunction) && mirror != mirrorPoint) {
return new GeoCurveCartesian(cons);
}
if (geo.isLimitedPath() && mirror == mirrorConic) {
return new GeoConicPart(cons, GeoConicNDConstants.CONIC_PART_ARC);
}
if (mirror instanceof GeoConic && geo instanceof GeoLine) {
return new GeoConic(cons);
}
if (mirror instanceof GeoConic && geo instanceof GeoConic
&& (!((GeoConic) geo).isCircle()
|| !((GeoConic) geo).keepsType())) {
return kernel.newImplicitPoly(cons).toGeoElement();
}
if (geo instanceof GeoPoly
|| (geo.isLimitedPath() && mirror != mirrorConic)) {
return copyInternal(cons, geo);
}
if (geo.isGeoList()) {
return new GeoList(cons);
}
return copy(geo);
}
@Override
protected void transformLimitedPath(GeoElement a, GeoElement b) {
if (mirror != mirrorConic) {
super.transformLimitedPath(a, b);
return;
}
GeoConicPart arc = (GeoConicPart) b;
arc.setParameters(0, 6.28, true);
if (a instanceof GeoRay) {
transformedPoint.removePath();
setTransformedObject(((GeoRay) a).getStartPoint(),
transformedPoint);
compute();
arc.pathChanged(transformedPoint);
double d = transformedPoint.getPathParameter().getT();
transformedPoint.removePath();
transformedPoint.setCoords(mirrorConic.getTranslationVector());
arc.pathChanged(transformedPoint);
double e = transformedPoint.getPathParameter().getT();
arc.setParameters(d * Kernel.PI_2, e * Kernel.PI_2, true);
transformedPoint.removePath();
setTransformedObject(arc.getPointParam(0.5), transformedPoint);
compute();
if (!((GeoRay) a).isOnPath(transformedPoint,
Kernel.STANDARD_PRECISION)) {
arc.setParameters(d * Kernel.PI_2, e * Kernel.PI_2, false);
}
setTransformedObject(a, b);
} else if (a instanceof GeoSegment) {
arc.setParameters(0, Kernel.PI_2, true);
transformedPoint.removePath();
setTransformedObject(((GeoSegment) a).getStartPoint(),
transformedPoint);
compute();
if (arc.getType() == GeoConicNDConstants.CONIC_LINE) {
arc.getLines()[0].setStartPoint(transformedPoint.copy());
}
// if start point itself is on path, transformed point may have
// wrong path param #2306
transformedPoint.removePath();
arc.pathChanged(transformedPoint);
double d = transformedPoint.getPathParameter().getT();
arc.setParameters(0, Kernel.PI_2, true);
transformedPoint.removePath();
setTransformedObject(((GeoSegment) a).getEndPoint(),
transformedPoint);
compute();
if (arc.getType() == GeoConicNDConstants.CONIC_LINE) {
arc.getLines()[0].setEndPoint(transformedPoint.copy());
}
arc.pathChanged(transformedPoint);
double e = transformedPoint.getPathParameter().getT();
arc.setParameters(d * Kernel.PI_2, e * Kernel.PI_2, true);
transformedPoint.removePath();
transformedPoint.setCoords(mirrorConic.getTranslationVector());
if (arc.isOnPath(transformedPoint, Kernel.STANDARD_PRECISION)) {
arc.setParameters(d * Kernel.PI_2, e * Kernel.PI_2, false);
}
setTransformedObject(a, b);
}
if (a instanceof GeoConicPart) {
transformLimitedConic(a, b);
}
}
@Override
public boolean swapOrientation(GeoConicPartND arc) {
if (arc == null) {
return true;
} else if (mirror != mirrorConic || !(arc instanceof GeoConicPart)) {
return arc.positiveOrientation();
}
GeoVec2D arcCentre = ((GeoConicPart) arc).getTranslationVector();
GeoVec2D mirrorCentre = mirrorConic.getTranslationVector();
double dist = MyMath.length(arcCentre.getX() - mirrorCentre.getX(),
arcCentre.getY() - mirrorCentre.getY());
return !Kernel.isGreater(dist, ((GeoConicPart) arc).halfAxes[0]);
}
@Override
public double getAreaScaleFactor() {
return -1;
}
@Override
public PVariable[] getBotanaVars(GeoElementND geo) {
if (mirrorBotana == null) {
mirrorBotana = new MirrorBotana();
}
return mirrorBotana.getBotanaVars();
}
@Override
public PPolynomial[] getBotanaPolynomials(GeoElementND geo)
throws NoSymbolicParametersException {
if (mirrorBotana == null) {
mirrorBotana = new MirrorBotana();
}
return this.mirrorBotana.getBotanaPolynomials(geo, inGeo, mirrorLine,
mirrorPoint, mirrorConic);
}
}