package org.geogebra.common.geogebra3D.kernel3D.commands; import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoCurveCartesian3D; import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoDependentConic3D; import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoSurfaceCartesian3D; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoConic3D; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoLine3D; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.Matrix.CoordSys; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.algos.AlgoCurveCartesian; import org.geogebra.common.kernel.algos.AlgoDependentNumber; import org.geogebra.common.kernel.arithmetic.Equation; import org.geogebra.common.kernel.arithmetic.ExpressionNode; import org.geogebra.common.kernel.arithmetic.ExpressionValue; import org.geogebra.common.kernel.arithmetic.FunctionVariable; import org.geogebra.common.kernel.arithmetic.Polynomial; import org.geogebra.common.kernel.arithmetic.VectorArithmetic; import org.geogebra.common.kernel.arithmetic3D.Vector3DValue; import org.geogebra.common.kernel.commands.AlgebraProcessor; import org.geogebra.common.kernel.commands.EvalInfo; import org.geogebra.common.kernel.commands.ParametricProcessor; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoNumberValue; import org.geogebra.common.kernel.geos.GeoNumeric; import org.geogebra.common.plugin.Operation; import org.geogebra.common.util.debug.Log; import org.geogebra.common.util.lang.Unicode; /** * Processor for 3D parametric curves * */ public class ParametricProcessor3D extends ParametricProcessor { /** * @param kernel * kernel * @param ap * algebra processor */ public ParametricProcessor3D(Kernel kernel, AlgebraProcessor ap) { super(kernel, ap); } @Override protected GeoElement[] processParametricFunction(ExpressionNode exp, ExpressionValue ev, FunctionVariable[] fv, String label, EvalInfo info) { Construction cons = kernel.getConstruction(); if (ev instanceof Vector3DValue) { if (fv.length == 2) { return processSurface(exp, fv, label); } GeoNumeric loc = getLocalVar(exp, fv[0]); if (exp.getOperation() == Operation.IF) { ExpressionNode exp1 = exp.getRightTree(); ExpressionNode cx = VectorArithmetic.computeCoord(exp1, 0); ExpressionNode cy = VectorArithmetic.computeCoord(exp1, 1); ExpressionNode cz = VectorArithmetic.computeCoord(exp1, 2); return cartesianCurve(cons, label, exp1, loc, new ExpressionNode[] { cx, cy, cz }, exp.getLeftTree()); } ExpressionNode cx = VectorArithmetic.computeCoord(exp, 0); ExpressionNode cy = VectorArithmetic.computeCoord(exp, 1); ExpressionNode cz = VectorArithmetic.computeCoord(exp, 2); ExpressionValue[] coefX = new ExpressionValue[5]; ExpressionValue[] coefY = new ExpressionValue[5]; ExpressionValue[] coefZ = new ExpressionValue[5]; if (ap.getTrigCoeffs(cx, coefX, new ExpressionNode(kernel, 1.0), loc) && ap.getTrigCoeffs(cy, coefY, new ExpressionNode(kernel, 1.0), loc) && ap.getTrigCoeffs(cz, coefZ, new ExpressionNode(kernel, 1.0), loc)) { boolean constant = true; for (int i = 0; i < coefX.length; i++) { // coefX[i] = expr(coefX[i]); // coefY[i] = expr(coefY[i]); // coefZ[i] = expr(coefZ[i]); constant = constant && expr(coefX[i]).isConstant() && expr(coefY[i]).isConstant() && expr(coefZ[i]).isConstant(); } if (constant) { GeoConic3D conic = new GeoConic3D(kernel.getConstruction()); updateTrigConic(conic, coefX, coefY, coefZ); conic.toParametric(fv[0].getSetVarString()); conic.setDefinition(buildParamEq(exp)); conic.setLabel(label); return new GeoElement[] { conic }; } return dependentConic(cons, exp, coefX, coefY, coefZ, label, fv[0], true); } for (int i = 0; i < coefX.length; i++) { coefX[i] = new ExpressionNode(kernel, 0); coefY[i] = new ExpressionNode(kernel, 0); coefZ[i] = new ExpressionNode(kernel, 0); } int degX = ap.getPolyCoeffs(cx, coefX, new ExpressionNode(kernel, 1), loc); int degY = ap.getPolyCoeffs(cy, coefY, new ExpressionNode(kernel, 1), loc); int degZ = ap.getPolyCoeffs(cz, coefZ, new ExpressionNode(kernel, 1), loc); if ((degX >= 0 && degY >= 0 && degZ >= 0) && (degX < 2 && degY < 2 && degZ < 2)) { /* * if (P.isGeoElement3D() || v.isGeoElement3D()) { if * (isConstant) { line = new GeoLine3D(cons); ((GeoLine3D) * line).setCoord(P.getCoordsInD3(),v.getCoordsInD3()); * line.setLabel(par.getLabel()); }else{ line = * kernel.getManager3D().Line3D(par.getLabel(), P, v); } } else * { line = Line(par, (GeoPoint) P, (GeoVector) v, isConstant); * } */ GeoLine3D line; if (coefX[0].isConstant() && coefY[0].isConstant() && coefZ[0].isConstant() && coefX[1].isConstant() && coefY[1].isConstant() && coefZ[1].isConstant()) { line = new GeoLine3D(cons); line.showUndefinedInAlgebraView(true); Coords start = new Coords( new double[] { coefX[0].evaluateDouble(), coefY[0].evaluateDouble(), coefZ[0].evaluateDouble() }); Coords v = new Coords( new double[] { coefX[1].evaluateDouble(), coefY[1].evaluateDouble(), coefZ[1].evaluateDouble() }); line.setCoord(start, v); line.setToParametric(fv[0].getSetVarString()); if (info.isLabelOutput()) { line.setLabel(label); } } else { line = (GeoLine3D) kernel.getManager3D().Line3D(label, coefX, coefY, coefZ); } line.setToParametric(Unicode.lambdaStr); return new GeoElement[] { line }; } if ((degX >= 0 && degY >= 0 && degZ >= 0) && (degX < 3 && degY < 3 && degZ < 3)) { boolean constant = true; for (int i = 0; i < coefX.length; i++) { // coefX[i] = expr(coefX[i]); // coefY[i] = expr(coefY[i]); // coefZ[i] = expr(coefZ[i]); constant = constant && expr(coefX[i]).isConstant() && expr(coefY[i]).isConstant() && expr(coefZ[i]).isConstant(); } Log.printStacktrace("parabola" + constant); if (constant) { GeoConic3D conic = new GeoConic3D(kernel.getConstruction()); updateParabola(conic, coefX, coefY, coefZ); conic.toParametric(fv[0].getSetVarString()); conic.setDefinition(buildParamEq(exp)); conic.setLabel(label); return new GeoElement[] { conic }; } // return dependentConic(cons, exp, coefX, coefY, coefZ, label, fv[0], false); } return cartesianCurve(cons, label, exp, loc, new ExpressionNode[] { cx, cy, cz }, null); } return super.processParametricFunction(exp, ev, fv, label, info); } @Override protected AlgoCurveCartesian makeCurveAlgo(Construction cons, ExpressionNode wrap, GeoNumberValue[] coords, GeoNumeric locVar, GeoNumberValue from, GeoNumberValue to) { if (coords.length == 2) { return super.makeCurveAlgo(cons, wrap, coords, locVar, from, to); } return new AlgoCurveCartesian3D(cons, wrap, coords, locVar, from, to); } private GeoElement[] dependentConic(Construction cons, ExpressionNode exp, ExpressionValue[] coefX, ExpressionValue[] coefY, ExpressionValue[] coefZ, String label, FunctionVariable fv0, boolean trig) { AlgoDependentConic3D ellipseHyperbolaAlgo = new AlgoDependentConic3D( cons, buildParamEq(exp), coefX, coefY, coefZ, trig); ellipseHyperbolaAlgo.getConic3D().setLabel(label); ellipseHyperbolaAlgo.getConic3D().toParametric(fv0.getSetVarString()); return new GeoElement[] { ellipseHyperbolaAlgo.getConic3D() }; } /** * @param conic * conic * @param coefX * coefficients for (sin,cos,1) of x coord * @param coefY * coefficients for (sin,cos,1) of y coord * @param coefZ * coefficients for (sin,cos,1) of z coord */ public static void updateTrigConic(GeoConic3D conic, ExpressionValue[] coefX, ExpressionValue[] coefY, ExpressionValue[] coefZ) { CoordSys cs = new CoordSys(2); double mx = eval(coefX[0]), my = eval(coefY[0]), mz = eval(coefZ[0]); double xx = 0, xy = 0, yy = 0, det = 0; if (coefX[1] != null || coefX[2] != null) { double vx = eval(coefX[1]), vy = eval(coefY[1]), vz = eval(coefZ[1]); double wx = eval(coefX[2]), wy = eval(coefY[2]), wz = eval(coefZ[2]); cs.resetCoordSys(); cs.addPoint(new Coords(mx, my, mz, 1)); cs.addVector(new Coords(vx, vy, vz)); cs.addVector(new Coords(wx, wy, wz)); cs.makeOrthoMatrix(false, false); Coords v = cs.getNormalProjection(new Coords(vx, vy, vz, 0))[1]; Coords w = cs.getNormalProjection(new Coords(wx, wy, wz, 0))[1]; yy = v.getX() * v.getX() + w.getX() * w.getX(); xx = v.getY() * v.getY() + w.getY() * w.getY(); xy = v.getX() * v.getY() + w.getX() * w.getY(); det = v.getX() * w.getY() - w.getX() * v.getY(); } else if (coefX[3] != null || coefX[4] != null) { double vx = eval(coefX[3]), vy = eval(coefY[3]), vz = eval(coefZ[3]); double wx = eval(coefX[4]), wy = eval(coefY[4]), wz = eval(coefZ[4]); cs.resetCoordSys(); cs.addPoint(new Coords(mx, my, mz, 1)); cs.addVector(new Coords(vx, vy, vz)); cs.addVector(new Coords(wx, wy, wz)); cs.makeOrthoMatrix(false, false); Coords v = cs.getNormalProjection(new Coords(vx, vy, vz, 0))[1]; Coords w = cs.getNormalProjection(new Coords(wx, wy, wz, 0))[1]; yy = v.getX() * v.getX() - w.getX() * w.getX(); xx = v.getY() * v.getY() - w.getY() * w.getY(); xy = v.getX() * v.getY() - w.getX() * w.getY(); det = v.getX() * w.getY() - w.getX() * v.getY(); } conic.setCoordSys(cs); conic.setMatrix(new double[] { xx, yy, -det * det, -xy, 0, 0 }); } private static double eval(ExpressionValue ev) { if (ev == null) { return 0; } return ev.evaluateDouble(); } /** * @param conic * conic * @param coefX * coefficients of x as t-polynomial * @param coefY * coefficients of y as t-polynomial * @param coefZ * coefficients of z as t-polynomial */ public static void updateParabola(GeoConic3D conic, ExpressionValue[] coefX, ExpressionValue[] coefY, ExpressionValue[] coefZ) { Kernel kernel = conic.getKernel(); double mx = eval(coefX[0]), my = eval(coefY[0]), mz = eval(coefZ[0]); double vx = eval(coefX[1]), vy = eval(coefY[1]), vz = eval(coefZ[1]); double wx = eval(coefX[2]), wy = eval(coefY[2]), wz = eval(coefZ[2]); CoordSys cs = new CoordSys(2); cs.resetCoordSys(); cs.addPoint(new Coords(mx, my, mz, 1)); cs.addVector(new Coords(vx, vy, vz)); cs.addVector(new Coords(wx, wy, wz)); cs.makeOrthoMatrix(false, false); Coords v = cs.getNormalProjection(new Coords(vx, vy, vz, 0))[1]; Coords w = cs.getNormalProjection(new Coords(wx, wy, wz, 0))[1]; FunctionVariable px = new FunctionVariable(kernel, "x"); FunctionVariable py = new FunctionVariable(kernel, "y"); ExpressionNode t = px.wrap().multiply(w.getY()) .subtract(py.wrap().multiply(w.getX())); double d = w.getY() * v.getX() - w.getX() * v.getY(); Equation eq; // Numerically unstable eq = new Equation(kernel, px.wrap().multiply(d * d * w.getX()) .plus(py.wrap().multiply(d * d * w.getY())), t.power(2).multiply(w.getX() * w.getX() + w.getY() * w.getY()) .plus(t.multiply( w.getY() * v.getY() + v.getX() * w.getX()) .multiply(d)) ); eq.setForceConic(); Log.debug("3D proc"); eq.initEquation(); Polynomial lhs = eq.getNormalForm(); double xx = lhs.getCoeffValue("xx"); double xy = lhs.getCoeffValue("xy"); double yy = lhs.getCoeffValue("yy"); double x = lhs.getCoeffValue("x"); double y = lhs.getCoeffValue("y"); double cst = lhs.getCoeffValue(""); Log.debug("3D proc done"); conic.setCoordSys(cs); // conic.setMatrix(new double[] { xx, yy, -det * det, -xy, 0, // 0 // }); conic.setMatrix(new double[] { xx, yy, cst, xy / 2, x / 2, y / 2 }); } private GeoElement[] processSurface(ExpressionNode exp, FunctionVariable[] fv, String label) { GeoNumeric loc0 = getLocalVar(exp, fv[0]); GeoNumeric loc1 = getLocalVar(exp, fv[1]); Construction cons = kernel.getConstruction(); ExpressionNode cx = VectorArithmetic.computeCoord(exp, 0); ExpressionNode cy = VectorArithmetic.computeCoord(exp, 1); ExpressionNode cz = VectorArithmetic.computeCoord(exp, 2); AlgoDependentNumber nx = new AlgoDependentNumber(cons, cx, false); cons.removeFromConstructionList(nx); AlgoDependentNumber ny = new AlgoDependentNumber(cons, cy, false); cons.removeFromConstructionList(ny); AlgoDependentNumber nz = new AlgoDependentNumber(cons, cz, false); cons.removeFromConstructionList(nz); AlgoSurfaceCartesian3D algo = new AlgoSurfaceCartesian3D(cons, label, exp, new GeoNumberValue[] { nx.getNumber(), ny.getNumber(), nz.getNumber() }, new GeoNumeric[] { loc0, loc1 }, new GeoNumberValue[] { num(-10), num(-10) }, new GeoNumberValue[] { num(10), num(10) }); return algo.getOutput(); } private GeoNumberValue num(double d) { return new GeoNumeric(kernel.getConstruction(), d); } }