package org.geogebra.common.kernel.geos; import java.util.ArrayList; import java.util.List; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.arithmetic.ExpressionNode; import org.geogebra.common.kernel.arithmetic.Function; import org.geogebra.common.kernel.arithmetic.FunctionVariable; import org.geogebra.common.kernel.arithmetic.MyDouble; import org.geogebra.common.kernel.arithmetic.Traversing; import org.geogebra.common.kernel.arithmetic.Traversing.VariableReplacer; import org.geogebra.common.kernel.arithmetic.ValueType; import org.geogebra.common.kernel.kernelND.GeoElementND; import org.geogebra.common.kernel.parser.ParseException; import org.geogebra.common.kernel.parser.ParserInterface; import org.geogebra.common.plugin.GeoClass; import org.geogebra.common.util.lang.Unicode; import org.geogebra.common.util.mathIT.Complex; /** * Class for transfer function (see linear time-invariant system) not for all * complex function * * @author Giuliano * */ public class GeoTransferFunction extends GeoElement { private boolean isDefined = true; private ParserInterface parser; private Function originalFunction; private Function currentFunction; private Traversing t; private ExpressionNode exp; private int omegaStart = 50; private List<Coords> coordsList = new ArrayList<Coords>(); private GeoVec2D v; private GeoFunction geoFunction; private double step = 1.001; /** * Copy costructor * * @param gcf * GeoComplexFunction * */ public GeoTransferFunction(GeoTransferFunction gcf) { super(gcf.cons); set(gcf); } /** * @param c * construction * @param num * list of coefficients of numerator * @param den * list of coefficients of denominator * @param omega * value of interval [-omega;omega] */ public GeoTransferFunction(Construction c, GeoList num, GeoList den, int omega) { super(c); if (num.getElementType().equals(GeoClass.NUMERIC) && den.getElementType().equals(GeoClass.NUMERIC)) { omegaStart = omega; Function strFunc = createFunction(num, den); GeoFunction f = new GeoFunction(c, strFunc); geoFunction = new GeoFunction(f); originalFunction = geoFunction.getFunction(); parser = kernel.getParser(); this.setEuclidianVisible(true); } else { isDefined = false; } } /** * for defalut value of omega * * @param c * construction * @param num * numerator * @param den * denominator */ public GeoTransferFunction(Construction c, GeoList num, GeoList den) { this(c, num, den, 10); } private Function createFunction(GeoList num, GeoList den) { FunctionVariable s = new FunctionVariable(kernel, "s"); return new Function(createPolynom(num, s).divide(createPolynom(den, s)), s); } private static ExpressionNode createPolynom(GeoList values, FunctionVariable s) { ExpressionNode exs = s.wrap(); int size = values.size(); ExpressionNode ret = ((GeoNumberValue) values.get(values.size() - 1)) .getNumber().wrap(); for (int i = 1; i < size; i++) { MyDouble coeff = ((GeoNumberValue) values .get(values.size() - 1 - i)).getNumber(); ret = exs.power(i).multiply(coeff).plus(ret); } return ret; } /** * @return GeoFunction */ public GeoFunction getGeoFunction() { return geoFunction; } /** * Calc values of function */ public void evaluate() { coordsList.clear(); Coords po = evaluate(omegaStart); coordsList.add(po); double p = omegaStart / step; for (; !Kernel.isEqual(p, 0, 0.01); p /= step) { po = evaluate(p); if (!coordsList.contains(po)) { coordsList.add(po); } } } /** * @return list of values */ public List<Coords> getCoordsList() { return coordsList; } private int getOmega() { return omegaStart; } /** * @return function */ public Function getFunction() { return originalFunction; } @Override public GeoClass getGeoClassType() { return GeoClass.CURVE_POLAR; } /** * @param isDefined * defined flag */ public void setDefined(boolean isDefined) { this.isDefined = isDefined; } @Override public GeoElement copy() { return new GeoTransferFunction(this); } @Override public void set(GeoElementND geo) { GeoTransferFunction gcf = (GeoTransferFunction) geo; originalFunction = gcf.getFunction(); omegaStart = gcf.getOmega(); coordsList = gcf.getCoordsList(); } @Override public boolean isDefined() { return isDefined; } @Override public void setUndefined() { isDefined = false; } @Override public boolean showInAlgebraView() { return true; } @Override protected boolean showInEuclidianView() { return isDefined; } @Override public boolean isEqual(GeoElementND geo) { return false; } /** * @param z * value to evaluate * @return function(z) */ public Coords evaluate(Complex z) { try { if (Kernel.isEqual(z.getRe(), 0, Kernel.MIN_PRECISION)) { return (new Coords(originalFunction.value(0), 0, 1)); } exp = parser.parseExpression(z.toString()); currentFunction = new Function(originalFunction, kernel); t = VariableReplacer.getReplacer(currentFunction .getVarString(StringTemplate.defaultTemplate), exp, kernel); currentFunction.traverse(t); v = (GeoVec2D) currentFunction.evaluateComplex().getExpression() .evaluate(StringTemplate.defaultTemplate); return new Coords(v.getX(), v.getY()); } catch (ParseException e) { e.printStackTrace(); setUndefined(); } return null; } /** * @param x * value of omega (0+j*omega) * @return function(0+j*omega) */ public Coords evaluate(double x) { try { if (Kernel.isEqual(x, 0, Kernel.MIN_PRECISION)) { return (new Coords(originalFunction.value(0), 0, 1)); } GeoVec2D xi = new GeoVec2D(kernel, 0, x); xi.setMode(Kernel.COORD_COMPLEX); exp = xi.wrap(); currentFunction = new Function(originalFunction, kernel); t = VariableReplacer.getReplacer(currentFunction .getVarString(StringTemplate.defaultTemplate), exp, kernel); currentFunction.traverse(t); v = (GeoVec2D) currentFunction.evaluateComplex().getExpression() .evaluate(StringTemplate.defaultTemplate); return new Coords(v.getX(), v.getY(), 1); } catch (Exception e) { e.printStackTrace(); setUndefined(); } return null; } @Override public String getLaTeXAlgebraDescription(final boolean substituteNumbers, StringTemplate tpl) { return toLaTeXString(substituteNumbers, tpl); } @Override public String toValueString(StringTemplate tpl) { if (isDefined) { return originalFunction.toValueString(tpl); } return "?"; } @Override public String toLaTeXString(boolean symbolic, StringTemplate tpl) { if (isDefined) { StringBuilder sb = new StringBuilder(); sb.append("\\left."); sb.append(label); sb.append(": "); sb.append(originalFunction.toLaTeXString(symbolic, tpl)); sb.append("\\right\\} \\; "); sb.append(kernel.format(-omegaStart, tpl)); sb.append(" \\le "); sb.append(Unicode.omega); sb.append(" \\le "); sb.append(kernel.format(omegaStart, tpl)); return sb.toString(); } return "?"; } @Override public boolean isLaTeXDrawableGeo() { return true; } @Override final public HitType getLastHitType() { return HitType.ON_BOUNDARY; } @Override public ValueType getValueType() { return ValueType.PARAMETRIC2D; } }