package org.geogebra.common.export.pstricks;
import java.util.HashMap;
import java.util.List;
import org.geogebra.common.awt.GColor;
import org.geogebra.common.awt.GGraphics2D;
import org.geogebra.common.awt.GRectangle;
import org.geogebra.common.euclidian.DrawableND;
import org.geogebra.common.euclidian.EuclidianView;
import org.geogebra.common.euclidian.draw.DrawAngle;
import org.geogebra.common.euclidian.draw.DrawInequality;
import org.geogebra.common.euclidian.draw.DrawLine;
import org.geogebra.common.euclidian.draw.DrawPoint;
import org.geogebra.common.euclidian.plot.CurvePlotter;
import org.geogebra.common.factories.AwtFactory;
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.algos.AlgoBarChart;
import org.geogebra.common.kernel.algos.AlgoBoxPlot;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.algos.AlgoFunctionAreaSums;
import org.geogebra.common.kernel.algos.AlgoSlope;
import org.geogebra.common.kernel.algos.AlgoSumLeft;
import org.geogebra.common.kernel.algos.AlgoSumLower;
import org.geogebra.common.kernel.algos.AlgoSumRectangle;
import org.geogebra.common.kernel.algos.AlgoSumTrapezoidal;
import org.geogebra.common.kernel.algos.AlgoSumUpper;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ExpressionNodeConstants.StringType;
import org.geogebra.common.kernel.arithmetic.Function;
import org.geogebra.common.kernel.arithmetic.FunctionalNVar;
import org.geogebra.common.kernel.arithmetic.IneqTree;
import org.geogebra.common.kernel.arithmetic.Inequality;
import org.geogebra.common.kernel.cas.AlgoIntegralDefinite;
import org.geogebra.common.kernel.cas.AlgoIntegralFunctions;
import org.geogebra.common.kernel.commands.AlgebraProcessor;
import org.geogebra.common.kernel.geos.GeoAngle;
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.GeoElement.FillType;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoFunctionNVar;
import org.geogebra.common.kernel.geos.GeoLine;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoLocus;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoPolyLine;
import org.geogebra.common.kernel.geos.GeoPolygon;
import org.geogebra.common.kernel.geos.GeoRay;
import org.geogebra.common.kernel.geos.GeoSegment;
import org.geogebra.common.kernel.geos.GeoText;
import org.geogebra.common.kernel.geos.GeoTransferFunction;
import org.geogebra.common.kernel.geos.GeoVector;
import org.geogebra.common.kernel.implicit.GeoImplicit;
import org.geogebra.common.kernel.kernelND.GeoConicNDConstants;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.statistics.AlgoHistogram;
import org.geogebra.common.main.App;
import org.geogebra.common.util.MyMath;
import org.geogebra.common.util.StringUtil;
import org.geogebra.common.util.debug.Log;
public abstract class GeoGebraExport {
protected int beamerSlideNumber = 1;
protected final static double PRECISION_XRANGE_FUNCTION = 0.00001;
protected StringBuilder code, codePoint, codePreamble, codeFilledObject,
codeBeginDoc;
private App app;
protected Kernel kernel;
protected Construction construction;
protected EuclidianView euclidianView;
protected ExportSettings frame;
protected HashMap<GColor, String> customColor;
protected double xunit, yunit, xmin, xmax, ymin, ymax;
// The exported format: Latex, tex, ConTexT, Beamer
protected int format = 0;
protected boolean isBeamer = false;
protected int barNumber;
private StringTemplate tpl;
public GeoGebraExport(App app) {
this.app = app;
this.kernel = app.getKernel();
this.construction = kernel.getConstruction();
this.euclidianView = app.getActiveEuclidianView();
this.tpl = StringTemplate.printFigures(StringType.PSTRICKS, 12, false);
initBounds();
}
public App getApp() {
return app;
}
protected String format(double d) {
String ret = kernel.format(Kernel.checkDecimalFraction(d), tpl);
ret = StringUtil.cannonicNumber2(ret);
return ret;
}
// Functions added to access and modify xmin, xmax, ymin and ymax
// When xmin,xmax,ymin or ymax are changed
// the selected area is reported accodingly on the euclidianView.
// This is not visible, on the view, but one may expect that when
// the selection rectangle is changed it is displayed on the view.
// This may be implemented by changing the class EuclidianView.
// Furthermore the definition of a class EuclidianView listerner
// which this class would implement would be desirable so that
// when the selection is modified by the mouse, this is reported
// to the values xmin, xmax, ymin and ymax of instances of this class.
// refresh the selection rectangle when values change in TextField
public void refreshSelectionRectangle() {
int x1 = euclidianView.toScreenCoordX(xmin);
int x2 = euclidianView.toScreenCoordX(xmax);
int y1 = euclidianView.toScreenCoordY(ymin);
int y2 = euclidianView.toScreenCoordY(ymax);
GRectangle rec = AwtFactory.getPrototype().newRectangle(x1, y2, x2 - x1,
y1 - y2);
// Application.debug(x1+" "+x2+" "+y1+" "+y2);
euclidianView.setSelectionRectangle(rec);
euclidianView.repaint();
}
public void setxmin(double xmin) {
this.xmin = xmin;
this.refreshSelectionRectangle();
}
public void setxmax(double xmax) {
this.xmax = xmax;
this.refreshSelectionRectangle();
}
public void setymin(double ymin) {
this.ymin = ymin;
this.refreshSelectionRectangle();
}
public void setymax(double ymax) {
this.ymax = ymax;
this.refreshSelectionRectangle();
}
public double getxmin() {
return this.xmin;
}
public double getxmax() {
return this.xmax;
}
public double getymin() {
return this.ymin;
}
public double getymax() {
return this.ymax;
}
/**
* Initialize Gui JFrame
*/
private void initBounds() {
xunit = 1;
yunit = 1;
// Changes to make xmin,xmax,ymin,ymax be defined by the selection
// rectangle
// when this one is defined.
GRectangle rect = this.euclidianView.getSelectionRectangle();
if (rect != null) {
xmin = euclidianView.toRealWorldCoordX(rect.getMinX());
xmax = euclidianView.toRealWorldCoordX(rect.getMaxX());
ymin = euclidianView.toRealWorldCoordY(rect.getMaxY());
ymax = euclidianView.toRealWorldCoordY(rect.getMinY());
} else {
xmin = euclidianView.getXmin();
xmax = euclidianView.getXmax();
ymin = euclidianView.getYmin();
ymax = euclidianView.getYmax();
}
}
/**
* When The Button "generate Code" has been clicked
*/
public void setBeamer(boolean beamer) {
isBeamer = beamer;
}
/**
* This method converts a double with engineering notation to decimal<br>
* Example: 3E-4 becomes 0.0003
*
* @param d
* The double to translate
* @return The resulting String
*/
protected String sci2dec(double d) {
String s = StringUtil.toLowerCase(String.valueOf(d));
// StringTokenizer st = new StringTokenizer(s, "e");
StringBuilder number;
int posE = s.indexOf("e");
if (posE == -1) {
return s;
}
String token1 = s.substring(0, posE);
String token2 = s.substring(posE + 1);
number = new StringBuilder(token1);
int exp = Integer.parseInt(token2);
if (exp > 0) {
int id_point = number.indexOf(".");
if (id_point == -1) {
for (int i = 0; i < exp; i++) {
number.append("0");
}
} else {
number.deleteCharAt(id_point);
int zeros = exp - (number.length() - id_point);
for (int i = 0; i < zeros; i++) {
number.append("0");
}
}
} else {
exp = -exp;
int id_point = number.indexOf(".");
number.deleteCharAt(id_point);
for (int i = 0; i < exp - 1; i++) {
number.insert(0, "0");
}
number.insert(0, "0.");
}
return number.toString();
}
/**
* This method creates the name for all custom colors
*
* @param red
* The red color part
* @param green
* The green color part
* @param blue
* The blue color part
* @return The name for the color uses hexadecimal decomposition
*/
protected String createCustomColor(int red, int green, int blue) {
final String suff = "qrstuvwxyzabcdef";
int[] nb = { red, green, blue };
StringBuilder sb = new StringBuilder();
for (int i = 0; i < nb.length; i++) {
int quot = nb[i] / 16;
int reste = nb[i] % 16;
sb.append(suff.charAt(quot));
sb.append(suff.charAt(reste));
}
return new String(sb);
}
/**
* This method is generic method to generate code according to GeoElement
* nature
*
* @param g
* GeoElement g
* @param fromGeoList
* if GeoElement comes from a GeoList object
* @param trimmedInter
*/
protected void drawGeoElement(GeoElement g, boolean fromGeoList,
boolean trimmedInter) {
if (g.isGeoList()) {
GeoList geo = ((GeoList) g);
for (int i = 0; i < geo.size(); i++) {
drawGeoElement(geo.get(i), true, false);
}
} else if (g.isEuclidianVisible() || trimmedInter) {
if (g instanceof GeoPoint) {
drawGeoPoint((GeoPoint) g);
drawLabel(g, null);
} else if (g instanceof GeoSegment) {
drawGeoSegment((GeoSegment) g);
drawLabel(g, null);
} else if (g instanceof GeoRay) {
drawGeoRay((GeoRay) g);
drawLabel(g, null);
} else if (g instanceof GeoPolyLine) {
drawPolyLine((GeoPolyLine) g);
} else if (g instanceof GeoLine) {
drawGeoLine((GeoLine) g);
drawLabel(g, null);
} else if (g instanceof GeoPolygon) {
drawPolygon((GeoPolygon) g);
drawLabel(g, null);
} else if (g instanceof GeoAngle) {
if (g.isIndependent()) {
// independent number may be shown as slider
drawSlider((GeoNumeric) g);
} else {
drawAngle((GeoAngle) g);
// String
// label="$"+Util.toLaTeXString(g.getLabelDescription(),true)+"$";
drawLabel(g, euclidianView.getDrawableFor(g));
}
} else if (g instanceof GeoImplicit) {
drawImplicitPoly((GeoImplicit) g);
}
// To draw Inequalities
else if (g.getTypeString().equals("Inequality")) {
if (g.isGeoFunctionBoolean()) {
drawGeoInequalities(null, g);
} else {
drawGeoInequalities((GeoFunctionNVar) g, null);
}
}
else if (g.isGeoNumeric()) {
AlgoElement algo = g.getParentAlgorithm();
if (algo == null) {
// indpendent number may be shown as slider
drawSlider((GeoNumeric) g);
} else if (algo instanceof AlgoSlope) {
drawSlope((GeoNumeric) g);
drawLabel(g, null);
} else if (algo instanceof AlgoIntegralDefinite) {
drawIntegral((GeoNumeric) g);
drawLabel(g, null);
} else if (algo instanceof AlgoIntegralFunctions) {
drawIntegralFunctions((GeoNumeric) g);
drawLabel(g, null);
}
// BoxPlot
else if (algo instanceof AlgoBoxPlot) {
drawBoxPlot((GeoNumeric) g);
} else if (algo instanceof AlgoFunctionAreaSums) {
// Trapezoidal Sum
if (algo instanceof AlgoSumTrapezoidal) {
drawSumTrapezoidal((GeoNumeric) g);
} else if (algo instanceof AlgoHistogram) {
drawBarChartOrHistogram((GeoNumeric) g);
} else if (algo instanceof AlgoSumUpper
|| algo instanceof AlgoSumLower
|| algo instanceof AlgoSumLeft
|| algo instanceof AlgoSumRectangle) {
drawSumUpperLower((GeoNumeric) g);
}
drawLabel(g, null);
}
// Bar Chart
else if (algo instanceof AlgoBarChart) {
drawBarChartOrHistogram((GeoNumeric) g);
drawLabel(g, null);
}
} else if (g instanceof GeoVector) {
drawGeoVector((GeoVector) g);
drawLabel(g, null);
} else if (g instanceof GeoConicPart) {
GeoConicPart geo = (GeoConicPart) g;
drawGeoConicPart(geo);
if (geo.getConicPartType() == GeoConicNDConstants.CONIC_PART_ARC
|| geo.getConicPartType() == GeoConicNDConstants.CONIC_PART_SECTOR) {
drawLabel(g, null);
}
} else if (g instanceof GeoConic) {
if (isSinglePointConic(g)) {
GeoConic geo = (GeoConic) g;
GeoPoint point = geo.getSinglePoint();
point.copyLabel(geo);
point.setObjColor(geo.getObjectColor());
point.setPointSize(geo.getLineThickness());
point.setLabelOffset(geo.labelOffsetX, geo.labelOffsetY);
DrawPoint drawPoint = new DrawPoint(euclidianView, point);
drawPoint.setGeoElement(geo);
drawGeoPoint(point);
drawLabel(point, drawPoint);
} else if (isDoubleLineConic(g)) {
GeoConic geo = (GeoConic) g;
GeoLine[] lines = geo.getLines();
DrawLine[] drawLines = new DrawLine[2];
for (int i = 0; i < 2; i++) {
lines[i].copyLabel(geo);
lines[i].setObjColor(geo.getObjectColor());
lines[i].setLineThickness(geo.getLineThickness());
lines[i].lineType = geo.lineType;
}
drawLines[0] = new DrawLine(euclidianView, lines[0]);
drawLines[1] = new DrawLine(euclidianView, lines[1]);
drawLines[0].setGeoElement(geo);
drawLines[1].setGeoElement(geo);
drawGeoLine(lines[0]);
drawGeoLine(lines[1]);
drawLabel(lines[0], drawLines[0]);
drawLabel(lines[1], drawLines[1]);
} else if (isEmpty(g)) {
//
} else {
drawGeoConic((GeoConic) g);
drawLabel(g, null);
}
} else if (g instanceof GeoFunction) {
drawFunction((GeoFunction) g);
drawLabel(g, null);
} else if (g instanceof GeoCurveCartesian) {
drawCurveCartesian(g);
drawLabel(g, null);
} else if (g.isGeoText()) {
drawText((GeoText) g);
} else if (g.isGeoImage()) {
// Image --> export to eps is better and easier!
} else if (g instanceof GeoLocus) {
drawLocus((GeoLocus) g);
} else if (g instanceof GeoTransferFunction) {
drawNyquist((GeoTransferFunction) g);
} else {
Log.debug("Export: unsupported GeoElement "
+ g.getGeoClassType());
}
}
}
protected void drawBarChartOrHistogram(GeoNumeric g) {
if (g.getParentAlgorithm() instanceof AlgoFunctionAreaSums) {
AlgoFunctionAreaSums algo = (AlgoFunctionAreaSums) g
.getParentAlgorithm();
drawHistogramOrBarChartBox(algo.getValues(), algo.getLeftBorder(),
algo.getValues().length - 1, 0, g);
} else if (g.getParentAlgorithm() instanceof AlgoBarChart) {
AlgoBarChart algo = (AlgoBarChart) g.getParentAlgorithm();
drawHistogramOrBarChartBox(algo.getValues(), algo.getLeftBorder(),
algo.getValues().length, algo.getWidth(), g);
}
}
protected boolean isSinglePointConic(GeoElement geo) {
if (geo.isGeoConic()) {
if (((GeoConic) geo)
.getType() == GeoConicNDConstants.CONIC_SINGLE_POINT) {
return true;
}
}
return false;
}
protected boolean isDoubleLineConic(GeoElement geo) {
if (geo.isGeoConic()) {
if (((GeoConic) geo)
.getType() == GeoConicNDConstants.CONIC_DOUBLE_LINE
|| ((GeoConic) geo)
.getType() == GeoConicNDConstants.CONIC_INTERSECTING_LINES
|| ((GeoConic) geo)
.getType() == GeoConicNDConstants.CONIC_PARALLEL_LINES) {
return true;
}
}
return false;
}
protected boolean isEmpty(GeoElement geo) {
if (geo.isGeoConic()) {
if (((GeoConic) geo).getType() == GeoConicNDConstants.CONIC_EMPTY) {
return true;
}
}
return false;
}
protected int resizePt(int size) {
double height_geogebra = euclidianView.getHeight() / 30.0;
double height_latex = frame.getLatexHeight();
double ratio = height_latex / height_geogebra;
int tmp = (int) Math.round(ratio * size);
if (tmp != 0) {
return tmp;
}
return 1;
}
/**
* Export as PSTricks or PGF/TikZ GeoPoint
*
* @param geo
* The point to export
*/
abstract protected void drawGeoPoint(GeoPoint geo);
/**
* Export as PSTricks or PGF/TikZ GeoLine line[A,B]
*
* @param geo
* The line to export
*/
abstract protected void drawGeoLine(GeoLine geo);
/**
* Export as PSTricks or PGF/TikZ GeoRay ray[A,B]
*
* @param geo
* The ray to export
*/
abstract protected void drawGeoRay(GeoRay geo);
/**
* Export as PSTricks or PGF/TikZ GeoSegment segment[A,B]
*
* @param geo
* The segment to export
*/
abstract protected void drawGeoSegment(GeoSegment geo);
/**
* Export as PSTricks or PGF/TikZ Objects created by command: polygon[A,B,C]
*
* @param geo
* The polygon to export
*/
abstract protected void drawPolygon(GeoPolygon geo);
/**
* Export as PSTricks or PGF/TikZ Objects created by command:
* slider[1,2,0.1]
*
* @param geo
* The slider to export
*/
abstract protected void drawSlider(GeoNumeric geo);
abstract protected void drawSlope(GeoNumeric geo);
/**
* Export as PSTricks or PGF/TikZ Objects created by command:
* integral(f(x),a,b)
*
* @param geo
* The object to export
*/
abstract protected void drawIntegral(GeoNumeric geo);
/**
* Export as PSTricks or PGF/TikZ Objects created by command:
* integral(f(x),g(x),a,b)
*
* @param geo
* The object to export
*/
abstract protected void drawIntegralFunctions(GeoNumeric geo);
/**
* Export as PSTricks or PGF/TikZ Objects created by following commands
* leftSum, rectangleSum, lowerSum upperSum
*
* @param geo
* The object to export
*/
abstract protected void drawSumUpperLower(GeoNumeric geo);
/**
* Export as PSTricks or PGF/TikZ Objects created by command trapezoidalsum
*
* @param geo
* The object to export
*/
abstract protected void drawSumTrapezoidal(GeoNumeric geo);
/**
* Export as PSTricks or PGF/TikZ Objects created by command: BoxPlot[0, 1,
* {2,2,3,4,5,5,6,7,7,8,8,8,9}]
*
* @param geo
* The boxplot to export
*/
abstract protected void drawBoxPlot(GeoNumeric geo);
/**
* Export as PSTricks or PGF/TikZ GeoAngle objects angle[A,B,C]
*
* @param geo
* The angle to export
*/
abstract protected void drawAngle(GeoAngle geo);
/**
* Export as PSTricks or PGF/TikZ GeoVector objects vector[A,B]
*
* @param geo
* The vector to export
*/
abstract protected void drawGeoVector(GeoVector geo);
/**
* Export as PSTricks or PGF/TikZ GeoConic objects conic[A,B,C,D,E]
*
* @param geo
* The conic to export
*/
abstract protected void drawGeoConic(GeoConic geo);
/**
* Export as PSTricks or PGF/TikZ GeoConicPart objects (sectors...)
*
* @param geo
* The conic part to export
*/
abstract protected void drawGeoConicPart(GeoConicPart geo);
/**
* Export as PSTricks or PGF/TikZ the object's label
*
* @param geo
* The label to export
* @param drawGeo
* Drawable object attached to geo
*/
abstract protected void drawLabel(GeoElement geo, DrawableND drawGeo);
/**
* Export as PSTricks or PGF/TikZ the GeoFunction object
*
* @param geo
* The function to export
*/
abstract protected void drawFunction(GeoFunction geo);
/**
* Export as PSTricks or PGF/TikZ implicit functions
*
* @param geo
* The function to export
*/
abstract protected void drawImplicitPoly(GeoImplicit geo);
/**
* Export as PSTricks or PGF/TikZ parametric functions
*
* @param geo
* The function to export
*/
abstract protected void drawSingleCurveCartesian(GeoCurveCartesian geo,
boolean trasparency);
/**
* Export as PSTricks or PGF/TikZ Text on euclidian view
*
* @param geo
* The text to export
*/
abstract protected void drawText(GeoText geo);
/**
* Export as PSTricks or PGF/TikZ locus
*
* @param geo
* The locus to export
*/
abstract protected void drawLocus(GeoLocus geo);
/**
* Exports as PStricks or PGF a line defined by all its parameter
*
* @param x1
* x start
* @param y1
* y start
* @param x2
* x end
* @param y2
* y end
* @param geo
* geo Object
*/
abstract protected void drawLine(double x1, double y1, double x2, double y2,
GeoElement geo);
/**
* Export as PStricks or PGF arc mark for angle
*
* @param geo
* geo Object
* @param vertex
* center
* @param angSt
* angle start
* @param angEnd
* angle end
* @param r
* radius
*/
abstract protected void drawArc(GeoAngle geo, double[] vertex, double angSt,
double angEnd, double r);
/**
* Export as PStricks or PGF segment mark
*
* @param geo
* geo Object
* @param vertex
* @param angleTick
* angle
*/
abstract protected void drawTick(GeoAngle geo, double[] vertex,
double angleTick);
abstract protected void drawArrowArc(GeoAngle geo, double[] vertex,
double angSt, double angEnd, double r, boolean clockwise);
/**
* @param settingsFrame
* frame where user may edit the settings and where he becomes
* the output
*/
public final void setFrame(ExportSettings settingsFrame) {
frame = settingsFrame;
}
public abstract void generateAllCode();
/**
* Export as PSTricks or PGF/TikZ color's code
*
* @param color
* The color
* @param sb
* The StringBuilder to complete
*/
abstract protected void colorCode(GColor color, StringBuilder sb);
/**
* Export as PSTricks or PGF/TikZ PolyLine objects polyline[A,B,C,D,E]
*
* @param geo
* The Polyline objects
*/
abstract protected void drawPolyLine(GeoPolyLine geo);
/**
* Export as PSTricks or PGF/TikZ BarChart or Histogram
*
* @param values
* Values of Histogram or BarChart
* @param leftBorder
* @param length
* Length of data. Along with leftBorder length allows you to
* choose between Histogram or BarChart
* @param width
* Used if is a BarChart
* @param g
* For visual style
*/
protected abstract void drawHistogramOrBarChartBox(double[] values,
double[] leftBorder, int length, double width, GeoNumeric g);
/**
* Export inequalities as PSTricks or PGF or Asymptote
*
* @param geo
* The inequality function
* @param e
* If is inequality with one variable eg. x>3
*/
protected void drawGeoInequalities(GeoFunctionNVar geo, GeoElement e) {
FunctionalNVar ef = null;
if (geo == null) {
ef = (FunctionalNVar) e;
} else {
ef = geo;
}
DrawInequality drawable = new DrawInequality(euclidianView, ef);
GGraphics2D g = null;
IneqTree tree = ef.getFunction().getIneqs();
if (tree.getLeft() != null) {
for (int i = 0; i < tree.getLeft().getSize(); i++) {
g = createGraphics(ef, tree.getLeft().get(i));
drawable.draw(g);
}
}
if (tree.getRight() != null) {
for (int i = 0; i < tree.getLeft().getSize(); i++) {
g = createGraphics(ef, tree.getRight().get(i));
drawable.draw(g);
}
}
if (tree.getIneq() != null) {
g = createGraphics(ef, tree.getIneq());
drawable.draw(g);
}
// Only for syntax. Never throws
}
/**
* Export as PSTricks or PGF/TikZ or Asympote, Nyquist diagram
*
* @param g
* Transfer function
*
*
*/
protected abstract void drawNyquist(GeoTransferFunction g);
// Create the appropriate instance of MyGraphics of various implementations
// (pstricks,pgf,asymptote)
abstract protected GGraphics2D createGraphics(FunctionalNVar ef,
Inequality inequality);
abstract protected boolean fillSpline(GeoCurveCartesian[] curves);
/**
* @return the xmin
*/
public double getXmin() {
return xmin;
}
/**
* @param xmin
* the xmin to set
*/
public void setXmin(double xmin) {
this.xmin = xmin;
}
/**
* @return the xmax
*/
public double getXmax() {
return xmax;
}
/**
* @param xmax
* the xmax to set
*/
public void setXmax(double xmax) {
this.xmax = xmax;
}
/**
* @return the ymin
*/
public double getYmin() {
return ymin;
}
/**
* @param ymin
* the ymin to set
*/
public void setYmin(double ymin) {
this.ymin = ymin;
}
/**
* @return the ymax
*/
public double getYmax() {
return ymax;
}
/**
* @param ymax
* the ymax to set
*/
public void setYmax(double ymax) {
this.ymax = ymax;
}
/**
* @return the xunit
*/
public double getXunit() {
return xunit;
}
/**
* @param xunit
* the xunit to set
*/
public void setXunit(double xunit) {
this.xunit = xunit;
}
/**
* @return the yunit
*/
public double getYunit() {
return yunit;
}
/**
* @param yunit
* the yunit to set
*/
public void setYunit(double yunit) {
this.yunit = yunit;
}
/**
* This method draws decoration on segment using abstract method drawLine
*
* @param A
* First Point
* @param B
* Second Point
* @param deco
* Decoration type
* @param geo
* GeoElement
*/
protected void mark(double[] A, double[] B, int deco, GeoElement geo) {
// calc midpoint (midX, midY) and perpendicular vector (nx, ny)
euclidianView.toScreenCoords(A);
euclidianView.toScreenCoords(B);
double midX = (A[0] + B[0]) / 2.0;
double midY = (A[1] + B[1]) / 2.0;
double nx = A[1] - B[1];
double ny = B[0] - A[0];
double nLength = MyMath.length(nx, ny);
// tick spacing and length.
double tickSpacing = 2.5 + geo.getLineThickness() / 2d;
double tickLength = tickSpacing + 1;
// Michael Borcherds 20071006 start
double arrowlength = 1.5;
// Michael Borcherds 20071006 end
double vx, vy, factor, x1, x2, y1, y2;
switch (deco) {
default:
// do nothing
break;
case GeoElement.DECORATION_SEGMENT_ONE_TICK:
factor = tickLength / nLength;
nx *= factor / xunit;
ny *= factor / yunit;
x1 = euclidianView.toRealWorldCoordX(midX - nx);
y1 = euclidianView.toRealWorldCoordY(midY - ny);
x2 = euclidianView.toRealWorldCoordX(midX + nx);
y2 = euclidianView.toRealWorldCoordY(midY + ny);
drawLine(x1, y1, x2, y2, geo);
break;
case GeoElement.DECORATION_SEGMENT_TWO_TICKS:
// vector (vx, vy) to get 2 points around midpoint
factor = tickSpacing / (2 * nLength);
vx = -ny * factor;
vy = nx * factor;
// use perpendicular vector to set ticks
factor = tickLength / nLength;
nx *= factor;
ny *= factor;
x1 = euclidianView.toRealWorldCoordX(midX + vx - nx);
x2 = euclidianView.toRealWorldCoordX(midX + vx + nx);
y1 = euclidianView.toRealWorldCoordY(midY + vy - ny);
y2 = euclidianView.toRealWorldCoordY(midY + vy + ny);
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX - vx - nx);
x2 = euclidianView.toRealWorldCoordX(midX - vx + nx);
y1 = euclidianView.toRealWorldCoordY(midY - vy - ny);
y2 = euclidianView.toRealWorldCoordY(midY - vy + ny);
drawLine(x1, y1, x2, y2, geo);
break;
case GeoElement.DECORATION_SEGMENT_THREE_TICKS:
// vector (vx, vy) to get 2 points around midpoint
factor = tickSpacing / nLength;
vx = -ny * factor;
vy = nx * factor;
// use perpendicular vector to set ticks
factor = tickLength / nLength;
nx *= factor;
ny *= factor;
x1 = euclidianView.toRealWorldCoordX(midX + vx - nx);
x2 = euclidianView.toRealWorldCoordX(midX + vx + nx);
y1 = euclidianView.toRealWorldCoordY(midY + vy - ny);
y2 = euclidianView.toRealWorldCoordY(midY + vy + ny);
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX - nx);
x2 = euclidianView.toRealWorldCoordX(midX + nx);
y1 = euclidianView.toRealWorldCoordY(midY - ny);
y2 = euclidianView.toRealWorldCoordY(midY + ny);
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX - vx - nx);
x2 = euclidianView.toRealWorldCoordX(midX - vx + nx);
y1 = euclidianView.toRealWorldCoordY(midY - vy - ny);
y2 = euclidianView.toRealWorldCoordY(midY - vy + ny);
drawLine(x1, y1, x2, y2, geo);
break;
// Michael Borcherds 20071006 start
case GeoElement.DECORATION_SEGMENT_ONE_ARROW:
// vector (vx, vy) to get 2 points around midpoint
factor = tickSpacing / (nLength);
vx = -ny * factor;
vy = nx * factor;
// use perpendicular vector to set ticks
factor = tickLength / nLength;
nx *= factor;
ny *= factor;
x1 = euclidianView.toRealWorldCoordX(midX - arrowlength * vx);
y1 = euclidianView.toRealWorldCoordY(midY - arrowlength * vy);
x2 = euclidianView.toRealWorldCoordX(
midX - arrowlength * vx + arrowlength * (nx + vx));
y2 = euclidianView.toRealWorldCoordY(
midY - arrowlength * vy + arrowlength * (ny + vy));
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX - arrowlength * vx);
y1 = euclidianView.toRealWorldCoordY(midY - arrowlength * vy);
x2 = euclidianView.toRealWorldCoordX(
midX - arrowlength * vx + arrowlength * (-nx + vx));
y2 = euclidianView.toRealWorldCoordY(
midY - arrowlength * vy + arrowlength * (-ny + vy));
drawLine(x1, y1, x2, y2, geo);
break;
case GeoElement.DECORATION_SEGMENT_TWO_ARROWS:
// vector (vx, vy) to get 2 points around midpoint
factor = tickSpacing / (nLength);
vx = -ny * factor;
vy = nx * factor;
// use perpendicular vector to set ticks
factor = tickLength / nLength;
nx *= factor;
ny *= factor;
x1 = euclidianView.toRealWorldCoordX(midX - 2 * arrowlength * vx);
y1 = euclidianView.toRealWorldCoordY(midY - 2 * arrowlength * vy);
x2 = euclidianView.toRealWorldCoordX(
midX - 2 * arrowlength * vx + arrowlength * (nx + vx));
y2 = euclidianView.toRealWorldCoordY(
midY - 2 * arrowlength * vy + arrowlength * (ny + vy));
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX - 2 * arrowlength * vx);
y1 = euclidianView.toRealWorldCoordY(midY - 2 * arrowlength * vy);
x2 = euclidianView.toRealWorldCoordX(
midX - 2 * arrowlength * vx + arrowlength * (-nx + vx));
y2 = euclidianView.toRealWorldCoordY(
midY - 2 * arrowlength * vy + arrowlength * (-ny + vy));
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX);
y1 = euclidianView.toRealWorldCoordY(midY);
x2 = euclidianView
.toRealWorldCoordX(midX + arrowlength * (nx + vx));
y2 = euclidianView
.toRealWorldCoordY(midY + arrowlength * (ny + vy));
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX);
y1 = euclidianView.toRealWorldCoordY(midY);
x2 = euclidianView
.toRealWorldCoordX(midX + arrowlength * (-nx + vx));
y2 = euclidianView
.toRealWorldCoordY(midY + arrowlength * (-ny + vy));
drawLine(x1, y1, x2, y2, geo);
break;
case GeoElement.DECORATION_SEGMENT_THREE_ARROWS:
// vector (vx, vy) to get 2 points around midpoint
factor = tickSpacing / nLength;
vx = -ny * factor;
vy = nx * factor;
// use perpendicular vector to set ticks
factor = tickLength / nLength;
nx *= factor;
ny *= factor;
x1 = euclidianView.toRealWorldCoordX(midX - arrowlength * vx);
y1 = euclidianView.toRealWorldCoordY(midY - arrowlength * vy);
x2 = euclidianView.toRealWorldCoordX(
midX - arrowlength * vx + arrowlength * (nx + vx));
y2 = euclidianView.toRealWorldCoordY(
midY - arrowlength * vy + arrowlength * (ny + vy));
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX - arrowlength * vx);
y1 = euclidianView.toRealWorldCoordY(midY - arrowlength * vy);
x2 = euclidianView.toRealWorldCoordX(
midX - arrowlength * vx + arrowlength * (-nx + vx));
y2 = euclidianView.toRealWorldCoordY(
midY - arrowlength * vy + arrowlength * (-ny + vy));
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX + arrowlength * vx);
y1 = euclidianView.toRealWorldCoordY(midY + arrowlength * vy);
x2 = euclidianView.toRealWorldCoordX(
midX + arrowlength * vx + arrowlength * (nx + vx));
y2 = euclidianView.toRealWorldCoordY(
midY + arrowlength * vy + arrowlength * (ny + vy));
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX + arrowlength * vx);
y1 = euclidianView.toRealWorldCoordY(midY + arrowlength * vy);
x2 = euclidianView.toRealWorldCoordX(
midX + arrowlength * vx + arrowlength * (-nx + vx));
y2 = euclidianView.toRealWorldCoordY(
midY + arrowlength * vy + arrowlength * (-ny + vy));
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX - 3 * arrowlength * vx);
y1 = euclidianView.toRealWorldCoordY(midY - 3 * arrowlength * vy);
x2 = euclidianView.toRealWorldCoordX(
midX - 3 * arrowlength * vx + arrowlength * (nx + vx));
y2 = euclidianView.toRealWorldCoordY(
midY - 3 * arrowlength * vy + arrowlength * (ny + vy));
drawLine(x1, y1, x2, y2, geo);
x1 = euclidianView.toRealWorldCoordX(midX - 3 * arrowlength * vx);
y1 = euclidianView.toRealWorldCoordY(midY - 3 * arrowlength * vy);
x2 = euclidianView.toRealWorldCoordX(
midX - 3 * arrowlength * vx + arrowlength * (-nx + vx));
y2 = euclidianView.toRealWorldCoordY(
midY - 3 * arrowlength * vy + arrowlength * (-ny + vy));
drawLine(x1, y1, x2, y2, geo);
break;
// Michael Borcherds 20071006 end
}
}
/**
* This Method draws The decoration for GeoAngle geo
*
* @param geo
* The GeoAngle
* @param r0
* The radius
* @param vertex
* The vertex coordinates
* @param angSt
* Angle Start
* @param angEnd
* Angle End
*/
protected void markAngle(GeoAngle geo, double r0, double[] vertex,
double angSt, double angEnd) {
double rdiff;
double r = r0;
switch (geo.getDecorationType()) {
default:
// do nothing
break;
case GeoElement.DECORATION_ANGLE_TWO_ARCS:
rdiff = 4 + geo.getLineThickness() / 2d;
drawArc(geo, vertex, angSt, angEnd, r);
r -= rdiff / euclidianView.getXscale();
drawArc(geo, vertex, angSt, angEnd, r);
break;
case GeoElement.DECORATION_ANGLE_THREE_ARCS:
rdiff = 4 + geo.getLineThickness() / 2d;
drawArc(geo, vertex, angSt, angEnd, r);
r -= rdiff / euclidianView.getXscale();
drawArc(geo, vertex, angSt, angEnd, r);
r -= rdiff / euclidianView.getXscale();
drawArc(geo, vertex, angSt, angEnd, r);
break;
case GeoElement.DECORATION_ANGLE_ONE_TICK:
drawArc(geo, vertex, angSt, angEnd, r);
euclidianView.toScreenCoords(vertex);
drawTick(geo, vertex, (angSt + angEnd) / 2);
break;
case GeoElement.DECORATION_ANGLE_TWO_TICKS:
drawArc(geo, vertex, angSt, angEnd, r);
euclidianView.toScreenCoords(vertex);
double angleTick[] = new double[2];
angleTick[0] = (2 * angSt + 3 * angEnd) / 5;
angleTick[1] = (3 * angSt + 2 * angEnd) / 5;
if (Math.abs(angleTick[1]
- angleTick[0]) > DrawAngle.MAX_TICK_DISTANCE) {
angleTick[0] = (angSt + angEnd) / 2
- DrawAngle.MAX_TICK_DISTANCE / 2;
angleTick[1] = (angSt + angEnd) / 2
+ DrawAngle.MAX_TICK_DISTANCE / 2;
}
drawTick(geo, vertex, angleTick[0]);
drawTick(geo, vertex, angleTick[1]);
break;
case GeoElement.DECORATION_ANGLE_THREE_TICKS:
drawArc(geo, vertex, angSt, angEnd, r);
euclidianView.toScreenCoords(vertex);
angleTick = new double[2];
angleTick[0] = (5 * angSt + 3 * angEnd) / 8;
angleTick[1] = (3 * angSt + 5 * angEnd) / 8;
if (Math.abs(angleTick[1]
- angleTick[0]) > DrawAngle.MAX_TICK_DISTANCE) {
angleTick[0] = (angSt + angEnd) / 2
- DrawAngle.MAX_TICK_DISTANCE / 2;
angleTick[1] = (angSt + angEnd) / 2
+ DrawAngle.MAX_TICK_DISTANCE / 2;
}
drawTick(geo, vertex, (angSt + angEnd) / 2);
drawTick(geo, vertex, angleTick[0]);
drawTick(geo, vertex, angleTick[1]);
break;
case GeoElement.DECORATION_ANGLE_ARROW_CLOCKWISE:
drawArrowArc(geo, vertex, angSt, angEnd, r, false);
break;
case GeoElement.DECORATION_ANGLE_ARROW_ANTICLOCKWISE:
drawArrowArc(geo, vertex, angSt, angEnd, r, true);
break;
}
}
protected void drawAllElements() {
boolean increment = (euclidianView.getShowGrid()
|| euclidianView.getShowXaxis()
|| euclidianView.getShowYaxis());
for (int step = 0; step < construction.steps(); step++) {
if (increment) {
beamerSlideNumber = step + 2;
} else {
beamerSlideNumber = step + 1;
}
GeoElementND[] geos = construction.getConstructionElement(step)
.getGeoElements();
for (int j = 0; j < geos.length; j++) {
GeoElement g = geos[j].toGeoElement();
drawGeoElement(g, false, false);
}
}
}
// added by Hoszu Henrietta
protected void drawAllInDependentElements() {
boolean increment = (euclidianView.getShowGrid()
|| euclidianView.getShowXaxis()
|| euclidianView.getShowYaxis());
for (int step = 0; step < construction.steps(); step++) {
if (increment) {
beamerSlideNumber = step + 2;
} else {
beamerSlideNumber = step + 1;
}
GeoElementND[] geos = construction.getConstructionElement(step)
.getGeoElements();
for (int j = 0; j < geos.length; j++) {
GeoElement g = geos[j].toGeoElement();
if (g.isIndependent()) {
drawGeoElement(g, false, false);
}
}
}
}
protected void startBeamer(StringBuilder sb) {
if (isBeamer) {
sb.append("\\onslide<" + beamerSlideNumber + "->{\n ");
}
}
protected void endBeamer(StringBuilder sb) {
if (isBeamer) {
sb.append("}\n");
}
}
protected void resizeFont(StringBuilder sb) {
// Number of units that represents the font size:
int ggbSize = app.getGUIFontSize();
double ggbYUnit = euclidianView.getYscale();
double fontUnits = ggbSize / ggbYUnit;
// Now, on the output, calculate the size in centimeter
double yunit1 = frame.getYUnit();
double sizeCM = fontUnits * yunit1;
// 1cm=1/2.54 in
// 1 in=72.27pt
// new size in pt:
int sizept = (int) (sizeCM / 2.54 * 72.27 + 0.5);
int texSize = frame.getFontSize();
if (texSize == 10) {
if (sizept <= 5) {
sb.append("\\tiny{");
} else if (sizept <= 7) {
sb.append("\\scriptsize{");
} else if (sizept == 8) {
sb.append("\\footnotesize{");
} else if (sizept == 9) {
sb.append("\\small{");
} else if (sizept == 10) {
sb.append("\\normalsize{");
} else if (sizept <= 12) {
sb.append("\\large{");
} else if (sizept <= 14) {
sb.append("\\Large{");
} else if (sizept <= 17) {
sb.append("\\LARGE{");
} else if (sizept <= 20) {
sb.append("\\huge{");
} else {
sb.append("\\Huge{");
}
} else if (texSize == 11) {
if (sizept <= 6) {
sb.append("\\tiny{");
} else if (sizept <= 8) {
sb.append("\\scriptsize{");
} else if (sizept == 9) {
sb.append("\\footnotesize{");
} else if (sizept == 10) {
sb.append("\\small{");
} else if (sizept == 11) {
sb.append("\\normalsize{");
} else if (sizept == 12) {
sb.append("\\large{");
} else if (sizept <= 14) {
sb.append("\\Large{");
} else if (sizept <= 17) {
sb.append("\\LARGE{");
} else if (sizept <= 20) {
sb.append("\\huge{");
} else {
sb.append("\\Huge{");
}
} else if (texSize == 12) {
if (sizept <= 6) {
sb.append("\\tiny{");
} else if (sizept <= 8) {
sb.append("\\scriptsize{");
} else if (sizept <= 10) {
sb.append("\\footnotesize{");
} else if (sizept == 11) {
sb.append("\\small{");
} else if (sizept == 12) {
sb.append("\\normalsize{");
} else if (sizept <= 14) {
sb.append("\\large{");
} else if (sizept <= 17) {
sb.append("\\Large{");
} else if (sizept <= 20) {
sb.append("\\LARGE{");
} else if (sizept <= 25) {
sb.append("\\huge{");
} else {
sb.append("\\Huge{");
}
}
}
protected String getImplicitExpr(GeoImplicit geo) {
StringBuilder sb = new StringBuilder();
double[][] coeff = geo.getCoeff();
boolean first = true;
for (int i = 0; i < coeff.length; i++) {
for (int j = 0; j < coeff[i].length; j++) {
double tmp = coeff[i][j];
if (tmp != 0) {
if (tmp > 0) {
if (!first) {
sb.append("+");
}
}
sb.append(tmp);
if (i == 0) {
if (j != 0) {
sb.append("*y^");
sb.append(j);
}
} else {
sb.append("*x^");
sb.append(i);
if (j != 0) {
sb.append("*y^");
sb.append(j);
}
}
first = false;
}
}
}
return new String(sb);
}
protected StringTemplate getStringTemplate() {
return tpl;
}
protected StringBuilder drawNoLatexFunction(GeoFunction geo,
double xrangemax, double xrangemin, int point, String template) {
GeoCurveCartesian curve = new GeoCurveCartesian(
app.getKernel().getConstruction());
geo.toGeoCurveCartesian(curve);
StringBuilder lineBuilder = new StringBuilder();
double y = geo.value(xrangemin);
double yprec = y;
if (Math.abs(y) < 0.001) {
y = yprec = 0;
}
double step = (xrangemax - xrangemin) / point;
double xprec = xrangemin;
double x = xprec;
for (; x <= xrangemax; x += step) {
y = geo.value(x);
if (Math.abs(y) < 0.001) {
y = 0;
}
if (Math.abs(x) < 0.001) {
x = 0;
}
if (Math.abs(yprec - y) < (ymax - ymin)) {
if (CurvePlotter.isContinuous(curve, xprec, x, 8)) {
lineBuilder.append(
StringUtil.format(template, xprec, yprec, x, y));
}
}
yprec = y;
xprec = x;
}
return lineBuilder;
}
protected StringBuilder drawNyquistDiagram(GeoTransferFunction geo,
String template, String arrowMark, String arrowCommand,
String reverseArrowCommand) {
String t = template;
String sub;
boolean flag = true;
StringBuilder lineBuilder = new StringBuilder();
List<Coords> coordsList = geo.getCoordsList();
Coords p = coordsList.get(0);
double xprec = p.getX();
double yprec = p.getY();
double x;
double y;
for (int i = 1; i < coordsList.size() - 10; i += 10) {
p = coordsList.get(i);
x = p.getX();
y = p.getY();
if (flag && i > coordsList.size() / 2.3) {
sub = t.replaceAll(arrowMark, arrowCommand);
flag = false;
} else {
sub = t.replaceAll(arrowMark, "");
}
lineBuilder.append(StringUtil.format(sub, xprec, yprec, x, y));
xprec = x;
yprec = y;
}
flag = true;
p = coordsList.get(0);
xprec = p.getX();
yprec = -p.getY();
for (int i = 1; i < coordsList.size(); i += 4) {
p = coordsList.get(i);
x = p.getX();
y = -p.getY();
if (flag && i > coordsList.size() / 2.3) {
sub = t.replaceAll(arrowMark, reverseArrowCommand);
flag = false;
} else {
sub = t.replaceAll(arrowMark, "");
}
lineBuilder.append(StringUtil.format(sub, xprec, yprec, x, y));
xprec = x;
yprec = y;
}
return lineBuilder;
}
protected boolean isLatexFunction(String s) {
// used if there are other non-latex
return !s.toLowerCase().contains("erf(")
&& !s.toLowerCase().contains("gamma(")
&& !s.toLowerCase().contains("gammaRegularized(")
&& !s.toLowerCase().contains("cbrt(")
&& !s.toLowerCase().contains("csc(")
&& !s.toLowerCase().contains("csch(")
&& !s.toLowerCase().contains("sec(")
&& !s.toLowerCase().contains("cot(")
&& !s.toLowerCase().contains("coth(")
&& !s.toLowerCase().contains("sech(")
&& !s.toLowerCase().contains("if");
}
protected void addTextPackage() {
StringBuilder packages = new StringBuilder();
if (codePreamble.indexOf("amssymb") == -1) {
packages.append("amssymb,");
}
if (codePreamble.indexOf("fancyhdr") == -1) {
packages.append("fancyhdr,");
}
if (codePreamble.indexOf("txfonts") == -1) {
packages.append("txfonts,");
}
if (codePreamble.indexOf("pxfonts") == -1) {
packages.append("pxfonts,");
}
if (packages.length() != 0) {
packages.delete(packages.length() - 1, packages.length());
codePreamble.append("\\usepackage{" + packages.toString() + "}\n");
}
}
protected class Info {
private double alpha;
private int y;
private double angle;
private FillType fillType;
private GColor linecolor;
public Info(GeoElement geo) {
alpha = geo.getAlphaValue();
y = geo.getHatchingDistance();
angle = geo.getHatchingAngle();
fillType = geo.getFillType();
linecolor = geo.getObjectColor();
if (geo.getParentAlgorithm() instanceof AlgoBarChart) {
boolean setAlpha = false;
AlgoBarChart algo = (AlgoBarChart) geo.getParentAlgorithm();
if (algo.getBarColor(barNumber) != null) {
linecolor = algo.getBarColor(barNumber);
setAlpha = true;
}
if (algo.getBarHatchDistance(barNumber) != -1) {
y = algo.getBarHatchDistance(barNumber);
}
if (algo.getBarHatchAngle(barNumber) != -1) {
angle = algo.getBarHatchAngle(barNumber);
}
if (algo.getBarFillType(barNumber) != null) {
fillType = FillType.values()[algo.getBarFillType(barNumber)
.ordinal()];
}
if (algo.getBarAlpha(barNumber) != -1 && setAlpha) {
alpha = algo.getBarColor(barNumber).getAlpha() / 255.0;
}
}
}
public double getAlpha() {
return alpha;
}
public int getY() {
return y;
}
public double getAngle() {
return angle;
}
public FillType getFillType() {
return fillType;
}
public GColor getLinecolor() {
return linecolor;
}
}
private void drawCurveCartesian(GeoElement geo) {
if (!isLatexFunction(
geo.toValueString(StringTemplate.noLocalDefault))) {
GeoCurveCartesian curve = (GeoCurveCartesian) geo;
Function f = curve.getFunX();
ExpressionNode exl = f.getFunctionExpression().getLeftTree();
ExpressionNode exr = f.getFunctionExpression().getRightTree();
String exls = exl.toValueString(StringTemplate.noLocalDefault);
String exrs = exr.toValueString(StringTemplate.noLocalDefault);
exrs = exrs.replace("{", "");
exrs = exrs.replace("}", "");
exls = exls.replace("{", "");
exls = exls.replace("}", "");
String[] exlsv = exls.split(",");
String[] exrsv = exrs.split(",");
String[] paramValues = new String[exlsv.length + 1];
paramValues[0] = "0";
for (int i = 0; i < exlsv.length; i++) {
paramValues[i + 1] = exlsv[i].split("<")[1];
}
GeoCurveCartesian[] curves = new GeoCurveCartesian[exlsv.length];
AlgebraProcessor ap = kernel.getAlgebraProcessor();
for (int i = 0; i < exlsv.length; i++) {
GeoFunction fxx = ap
.evaluateToFunction("xspline(t)=" + exrsv[i], true);
curves[i] = new GeoCurveCartesian(this.construction);
curves[i].setFunctionX(fxx.getFunction());
curves[i].setInterval(Double.parseDouble(paramValues[i]),
Double.parseDouble(paramValues[i + 1]));
curves[i].setAllVisualProperties(geo, false);
}
f = curve.getFunY();
exl = f.getFunctionExpression().getLeftTree();
exr = f.getFunctionExpression().getRightTree();
exls = exl.toValueString(StringTemplate.noLocalDefault);
exrs = exr.toValueString(StringTemplate.noLocalDefault);
exrs = exrs.replace("{", "");
exrs = exrs.replace("}", "");
exls = exls.replace("{", "");
exls = exls.replace("}", "");
exlsv = exls.split(",");
exrsv = exrs.split(",");
for (int i = 0; i < exlsv.length; i++) {
GeoFunction fxx = ap
.evaluateToFunction("yspline(t)=" + exrsv[i], true);
curves[i].setFunctionY(fxx.getFunction());
curves[i].setInterval(Double.parseDouble(paramValues[i]),
Double.parseDouble(paramValues[i + 1]));
}
boolean fill = fillSpline(curves);
if (!fill) {
for (int i = 0; i < curves.length; i++) {
drawSingleCurveCartesian(curves[i],
true);
}
}
} else {
drawSingleCurveCartesian((GeoCurveCartesian) geo, true);
}
}
protected double firstDefinedValue(GeoFunction f, double a, double b) {
double x = a;
double step = (b - a) / 100;
while (x <= b) {
double y = f.value(x);
if (!Double.isNaN(y)) {
if (Kernel.isEqual(x, a)) {
return a;
} else if (step < PRECISION_XRANGE_FUNCTION) {
return x;
} else {
return firstDefinedValue(f, x - step, x);
}
}
x += step;
}
return b;
}
protected double maxDefinedValue(GeoFunction f, double a, double b) {
double x = a;
double step1 = (b - a) / 100;
while (x <= b) {
double y = f.value(x);
if (Double.isNaN(y)) {
if (step1 < PRECISION_XRANGE_FUNCTION) {
return x - step1;
}
return maxDefinedValue(f, x - step1, x);
}
x += step1;
}
return b;
}
protected static void renameFunc(StringBuilder sb, String nameFunc,
String nameNew) {
int ind = sb.indexOf(nameFunc);
while (ind > -1) {
sb.replace(ind, ind + nameFunc.length(), nameNew);
ind = sb.indexOf(nameFunc);
}
}
}