/*
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.
*/
/*
* AlgoApplyMatrix.java
*
* Created on 24. September 2001, 21:37
*/
package org.geogebra.common.kernel.algos;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.MatrixTransformable;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.commands.Commands;
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.GeoNumberValue;
import org.geogebra.common.kernel.geos.GeoPoly;
import org.geogebra.common.kernel.geos.GeoVec3D;
import org.geogebra.common.kernel.geos.GeoVector;
import org.geogebra.common.kernel.geos.Translateable;
import org.geogebra.common.kernel.kernelND.GeoConicPartND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.util.MyMath;
/**
*
* @author Markus
*/
public class AlgoShearOrStretch extends AlgoTransformation {
private MatrixTransformable out;
private GeoElement inGeo, outGeo;
private GeoVec3D line;
private GeoNumberValue num;
private boolean shear;
private double n;
/**
* Creates new shear or stretch algorithm
*
* @param cons
* construction
* @param in
* sheared / stretched geo
* @param l
* stretch direction
* @param num
* stretch coefficient
* @param shear
* shear if true, stretch otherwise
*/
public AlgoShearOrStretch(Construction cons, GeoElement in, GeoVec3D l,
GeoNumberValue num, boolean shear) {
super(cons);
this.shear = shear;
this.line = l;
this.num = num;
inGeo = in;
if (inGeo instanceof GeoPoly || inGeo.isLimitedPath()) {
outGeo = in.copyInternal(cons);
out = (MatrixTransformable) outGeo;
} else if (inGeo.isGeoList()) {
outGeo = new GeoList(cons);
} else if (inGeo instanceof GeoFunction) {
out = new GeoCurveCartesian(cons);
outGeo = (GeoElement) out;
} else {
out = (MatrixTransformable) inGeo.copy();
outGeo = out.toGeoElement();
}
setInputOutput();
compute();
if (inGeo.isGeoFunction()) {
cons.registerEuclidianViewCE(this);
}
}
@Override
public Commands getClassName() {
if (shear) {
return Commands.Shear;
}
return Commands.Stretch;
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[num == null ? 2 : 3];
input[0] = inGeo;
input[1] = line;
if (num != null) {
input[2] = num.toGeoElement();
}
setOutputLength(1);
setOutput(0, outGeo);
setDependencies(); // done by AlgoElement
}
/**
* Returns the resulting element
*
* @return resulting element
*/
@Override
public GeoElement getResult() {
return outGeo;
}
@Override
public final void compute() {
if (inGeo.isGeoList()) {
transformList((GeoList) inGeo, (GeoList) outGeo);
return;
}
if (inGeo.isGeoFunction()) {
((GeoFunction) inGeo)
.toGeoCurveCartesian((GeoCurveCartesian) outGeo);
} else {
outGeo.set(inGeo);
}
if (!outGeo.isDefined()) {
return;
}
// matrix.add
Translateable tranOut = (Translateable) out;
double qx = 0.0d, qy = 0.0d, s, c;
this.n = MyMath.length(line.x, line.y); // ;Math.sqrt(l.x*l.x+l.y*l.y);
if (line instanceof GeoLine) {
if (Math.abs(line.x) > Math.abs(line.y)) {
qx = line.z / line.x;
} else {
qy = line.z / line.y;
}
s = -line.x / n;
c = line.y / n;
this.n = num.getDouble();
} else {
GeoPointND sp = ((GeoVector) line).getStartPoint();
if (sp != null) {
Coords qCoords = ((GeoVector) line).getStartPoint()
.getCoordsInD2();
qx = -qCoords.getX();
qy = -qCoords.getY();
}
c = -line.y / n;
s = line.x / n;
}
// translate -Q
tranOut.translate(new Coords(qx, qy, 0));
if (shear) {
out.matrixTransform(1 - c * s * n, c * c * n, -s * s * n,
1 + s * c * n);
} else {
out.matrixTransform(c * c + s * s * n, c * s * (1 - n),
c * s * (1 - n), s * s + c * c * n);
}
tranOut.translate(new Coords(-qx, -qy, 0));
if (inGeo.isLimitedPath()) {
this.transformLimitedPath(inGeo, outGeo);
}
}
@Override
protected void setTransformedObject(GeoElement g, GeoElement g2) {
inGeo = g;
outGeo = g2;
if (!(outGeo instanceof GeoList)) {
out = (MatrixTransformable) outGeo;
}
}
@Override
protected GeoElement getResultTemplate(GeoElement geo) {
if (geo instanceof GeoFunction) {
return new GeoCurveCartesian(cons);
}
return super.getResultTemplate(geo);
}
@Override
protected void transformLimitedPath(GeoElement a, GeoElement b) {
if (!(a instanceof GeoConicPart)) {
super.transformLimitedPath(a, b);
} else {
super.transformLimitedConic(a, b);
}
}
@Override
public boolean swapOrientation(GeoConicPartND arc) {
if (shear || num == null) {
return (arc == null || arc.positiveOrientation());
}
return (arc == null || arc.positiveOrientation())
^ (num.getDouble() < 0);
}
@Override
public double getAreaScaleFactor() {
if (shear) {
return 1;
}
// else
// stretch
return n;
}
}