/*
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.
*/
/*
* AlgoDependentNumber.java
*
* Created on 30. August 2001, 21:37
*/
package org.geogebra.common.geogebra3D.kernel3D.algos;
import org.geogebra.common.geogebra3D.kernel3D.geos.GeoSurfaceCartesian3D;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.Path;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ExpressionValue;
import org.geogebra.common.kernel.arithmetic.FunctionNVar;
import org.geogebra.common.kernel.arithmetic.FunctionVariable;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoNumberValue;
import org.geogebra.common.kernel.geos.GeoPoly;
import org.geogebra.common.kernel.geos.ParametricCurve;
import org.geogebra.common.kernel.kernelND.GeoConicND;
import org.geogebra.common.kernel.kernelND.GeoCurveCartesianND;
import org.geogebra.common.kernel.kernelND.GeoLineND;
import org.geogebra.common.kernel.kernelND.GeoSurfaceCartesianND;
import org.geogebra.common.plugin.Operation;
/**
* Cartesian curve: Curve[ x-expression in var, y-expression in var, var, from,
* to]
*
* @author Markus Hohenwarter
*/
public class AlgoSurfaceOfRevolution extends AlgoElement {
private ParametricCurve function; // input
private GeoNumberValue angle; // input
private GeoSurfaceCartesian3D surface; // output
private GeoLineND line;
private FunctionVariable[] funVar;
private Path path;
/**
*
* @param cons
* construction
* @param label
* output label
* @param function
* path to be rotated
* @param angle
* max angle
*/
public AlgoSurfaceOfRevolution(Construction cons, String label,
ParametricCurve function, GeoNumberValue angle) {
this(cons, label, function, angle, null);
}
/**
*
* @param cons
* construction
* @param label
* output label
* @param path
* path to be rotated
* @param angle
* max angle
* @param line
* rotation axis
*/
public AlgoSurfaceOfRevolution(Construction cons, String label, Path path,
GeoNumberValue angle, GeoLineND line) {
super(cons);
if (path instanceof ParametricCurve) {
this.function = (ParametricCurve) path;
} else {
GeoCurveCartesianND gc = kernel.getGeoFactory()
.newCurve(path.isGeoElement3D() ? 3 : 2, cons);
this.function = gc;
}
this.angle = angle;
this.line = line;
this.path = path;
min = new double[2];
max = new double[2];
min[1] = 0;
this.funVar = new FunctionVariable[2];
funVar[0] = new FunctionVariable(kernel);
funVar[0].setVarString("u");
funVar[1] = new FunctionVariable(kernel);
funVar[1].setVarString("v");
if (line == null && function == path) {
rotateAroundX();
} else {
FunctionNVar[] fun = new FunctionNVar[3];
fun[0] = new FunctionNVar(funVar[0].wrap(), funVar);
fun[1] = new FunctionNVar(funVar[0].wrap(), funVar);
fun[2] = new FunctionNVar(funVar[0].wrap(), funVar);
surface = createSurface(cons, fun);
}
if (path instanceof ParametricCurve
&& ((ParametricCurve) path).isFunctionInX()) {
surface.setIsSurfaceOfRevolutionAroundOx(true);
}
setInputOutput(); // for AlgoElement
// compute value
compute();
surface.setLabel(label);
}
private void rotateAroundX() {
// functions describing the surface
ExpressionNode expU, expF, expV;
if (function.isFunctionInX()) {
expU = new ExpressionNode(kernel, funVar[0]);
expF = new ExpressionNode(kernel, function, Operation.FUNCTION,
funVar[0]);
} else {
expF = function.getFun(1).getExpression().deepCopy(kernel).replace(
function.getFun(1).getFunctionVariable(), funVar[0]).wrap();
expU = function.getFun(0).getExpression().deepCopy(kernel).replace(
function.getFun(0).getFunctionVariable(), funVar[0]).wrap();
}
expV = new ExpressionNode(kernel, funVar[1]);
ExpressionNode expCos = expV.cos();
ExpressionNode expSin = expV.sin();
FunctionNVar[] fun = new FunctionNVar[3];
fun[0] = new FunctionNVar(expU, funVar);
fun[1] = new FunctionNVar(expCos.multiply(expF), funVar);
fun[2] = new FunctionNVar(expSin.multiply(expF), funVar);
// create the curve
surface = createSurface(cons, fun);
}
/**
* creates a surface
*
* @param cons
* @param fun
* @return a curve
*/
protected GeoSurfaceCartesian3D createSurface(Construction cons,
FunctionNVar[] fun) {
return new GeoSurfaceCartesian3D(cons, null, fun);
}
@Override
public Commands getClassName() {
return Commands.Surface;
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[line == null ? 2 : 3];
input[0] = path.toGeoElement();
input[1] = (GeoElement) angle;
if (line != null) {
input[2] = line.toGeoElement();
}
setOnlyOutput(surface);
setDependencies(); // done by AlgoElement
}
public GeoSurfaceCartesianND getSurface() {
return surface;
}
private double[] min, max;
@Override
public final void compute() {
if (path instanceof GeoPoly) {
((GeoPoly) path)
.toGeoCurveCartesian((GeoCurveCartesianND) function);
}
if (path instanceof GeoConicND) {
((GeoConicND) path)
.toGeoCurveCartesian((GeoCurveCartesianND) function);
}
if (function.isDefined() && angle.isDefined()) {
surface.setDefined(true);
} else {
surface.setUndefined();
}
if (line != null || path != function) {
ExpressionValue[][] coeffs = new ExpressionValue[4][4];
FunctionNVar[] fun = surface.getFunctions();
if (line == null) {
rotation4x4(new Coords(1, 0, 0), funVar[1], coeffs);
transform(function, coeffs, fun, new Coords(0, 0, 0));
} else {
rotation4x4(line.getDirectionInD3().normalized(), funVar[1],
coeffs);
transform(function, coeffs, fun, line.getStartInhomCoords());
}
String var = function.getFunctionVariables()[0] + "";
for (int i = 0; i < 3; i++) {
fun[i].getExpression().replaceVariables(var, funVar[0]);
fun[i].getExpression().replaceVariables(
funVar[1].toString(StringTemplate.defaultTemplate),
funVar[1]);
}
}
min[0] = function.getMinParameter();
max[0] = function.getMaxParameter();
// set min, max, and localVar (to avoid false undefined)
max[1] = angle.getDouble();
// the coord-functions don't have to be updated,
// so we only set the interval
surface.setIntervals(min, max);
}
public void transform(ParametricCurve curve, ExpressionValue[][] m,
FunctionNVar[] fun1, Coords startPoint) {
// current expressions
ExpressionNode[] expr = new ExpressionNode[3];
for (int i = 0; i < 3; i++) {
expr[i] = curve.getFun(i).deepCopy(kernel).getExpression()
.subtract(startPoint.get(i + 1));
}
for (int row = 0; row < 3; row++) {
ExpressionValue[] coeff = m[row];
ExpressionNode trans = new ExpressionNode(kernel, coeff[3]);
for (int i = 0; i < 3; i++) {
trans = trans.plus(expr[i].multiply(coeff[i]));
}
fun1[row].setExpression(trans.plus(startPoint.get(row + 1)));
}
}
public static final void rotation4x4(Coords u, ExpressionValue angle,
ExpressionValue[][] m) {
double ux = u.getX();
double uy = u.getY();
double uz = u.getZ();
ExpressionNode c = angle.wrap().cos();
ExpressionNode s = angle.wrap().sin();
Kernel kernel = c.getKernel();
ExpressionNode oneMinusC = new ExpressionNode(kernel, 1).subtract(c);
// Coords[] vec = m.vectors;
m[0][0] = oneMinusC.multiply(ux * ux).plus(c);
m[0][1] = oneMinusC.multiply(ux * uy).subtract(s.multiply(uz));
m[0][2] = oneMinusC.multiply(ux * uz).plus(s.multiply(uy));
// vals[3] = 0;
m[1][0] = oneMinusC.multiply(ux * uy).plus(s.multiply(uz));
m[1][1] = oneMinusC.multiply(uy * uy).plus(c);
m[1][2] = oneMinusC.multiply(uy * uz).subtract(s.multiply(ux));
// vals[7] = 0;
m[2][0] = oneMinusC.multiply(ux * uz).subtract(s.multiply(uy));
m[2][1] = oneMinusC.multiply(uy * uz).plus(s.multiply(ux));
m[2][2] = oneMinusC.multiply(uz * uz).plus(c);
m[3][0] = new ExpressionNode(kernel, 0);
m[3][1] = new ExpressionNode(kernel, 0);
m[3][2] = new ExpressionNode(kernel, 0);
// vals[11] = 0;
m[0][3] = new ExpressionNode(kernel, 0);
m[1][3] = new ExpressionNode(kernel, 0);
m[2][3] = new ExpressionNode(kernel, 0);
m[3][3] = new ExpressionNode(kernel, 1);
// use (Id-M)center for translation
// vec[3].set(0.0);
// m.setOrigin(center.sub(m.mul(center)));
}
}