package org.geogebra.common.export.pstricks; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import org.apache.commons.math3.fraction.Fraction; import org.geogebra.common.awt.GAffineTransform; import org.geogebra.common.awt.GColor; import org.geogebra.common.awt.GFont; import org.geogebra.common.awt.GPathIterator; import org.geogebra.common.awt.GRectangle; import org.geogebra.common.awt.GShape; import org.geogebra.common.euclidian.DrawableND; import org.geogebra.common.euclidian.draw.DrawPoint; import org.geogebra.common.export.UnicodeTeX; import org.geogebra.common.factories.AwtFactory; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.MyPoint; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.algos.AlgoAngleLines; import org.geogebra.common.kernel.algos.AlgoAnglePoints; import org.geogebra.common.kernel.algos.AlgoAngleVector; import org.geogebra.common.kernel.algos.AlgoAngleVectors; 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.AlgoIntersectAbstract; import org.geogebra.common.kernel.algos.AlgoSlope; 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.Inequality; import org.geogebra.common.kernel.cas.AlgoIntegralDefinite; import org.geogebra.common.kernel.cas.AlgoIntegralFunctions; import org.geogebra.common.kernel.geos.GeoAngle; import org.geogebra.common.kernel.geos.GeoAngle.AngleStyle; 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.GeoLine; 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.GeoVec3D; import org.geogebra.common.kernel.geos.GeoVector; import org.geogebra.common.kernel.implicit.GeoImplicit; import org.geogebra.common.kernel.kernelND.GeoConicND; import org.geogebra.common.kernel.kernelND.GeoConicNDConstants; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.kernel.kernelND.GeoVectorND; import org.geogebra.common.main.App; import org.geogebra.common.plugin.EuclidianStyleConstants; import org.geogebra.common.plugin.Operation; import org.geogebra.common.util.StringTokenizer; import org.geogebra.common.util.StringUtil; import org.geogebra.common.util.lang.Unicode; /** * Generates PGF/Tikz string representation of current view. * * */ public abstract class GeoGebraToPgf extends GeoGebraExport { private static final int FORMAT_LATEX = 0; private static final int FORMAT_PLAIN_TEX = 1; private static final int FORMAT_CONTEXT = 2; private static final int FORMAT_BEAMER = 3; private int functionIdentifier = 0; private boolean forceGnuplot = false; private boolean gnuplotWarning = false; private boolean hatchWarning = false; /** * @param app * application */ public GeoGebraToPgf(App app) { super(app); } @Override public void generateAllCode() { format = frame.getFormat(); forceGnuplot = frame.getGnuplot(); // init unit variables try { xunit = frame.getXUnit(); yunit = frame.getYUnit(); } catch (NullPointerException e2) { xunit = 1; yunit = 1; } // Initialize new StringBuilder for Pstricks code // and CustomColor code = new StringBuilder(); codePoint = new StringBuilder(); codePreamble = new StringBuilder(); codeFilledObject = new StringBuilder(); codeBeginDoc = new StringBuilder(); customColor = new HashMap<GColor, String>(); if (format == GeoGebraToPgf.FORMAT_LATEX) { codePreamble.append("\\documentclass[" + frame.getFontSize() + "pt]{article}\n" + "\\usepackage{pgf,tikz}\n\\usepackage{mathrsfs}\n\\usetikzlibrary{arrows}\n\\pagestyle{empty}\n"); codeBeginDoc.append( "\\begin{tikzpicture}[line cap=round,line join=round,>=triangle 45,x="); codeBeginDoc.append(xunit); codeBeginDoc.append("cm,y="); codeBeginDoc.append(yunit); codeBeginDoc.append("cm]\n"); } else if (format == GeoGebraToPgf.FORMAT_PLAIN_TEX) { codePreamble.append( "%Uncomment next line if XeTeX is used\n%\\def\\pgfsysdriver{pgfsys-xetex.def}\n\n"); codePreamble.append("\\input pgf.tex\n\\input tikz.tex\n"); codePreamble.append("\\usetikzlibrary{arrows}\n"); codePreamble.append("\\baselineskip="); codePreamble.append(frame.getFontSize()); codePreamble.append("pt\n\\hsize=6.3truein\n\\vsize=8.7truein\n"); codeBeginDoc.append( "\\tikzpicture[line cap=round,line join=round,>=triangle 45,x="); codeBeginDoc.append(xunit); codeBeginDoc.append("cm,y="); codeBeginDoc.append(yunit); codeBeginDoc.append("cm]\n"); } else if (format == GeoGebraToPgf.FORMAT_CONTEXT) { codePreamble .append("\\setupbodyfont[" + frame.getFontSize() + "pt]\n"); codePreamble.append("\\usemodule[tikz]\n\\usemodule[pgf]\n"); codePreamble.append( "\\usetikzlibrary[arrows]\n\\setuppagenumbering[location=]\n"); codeBeginDoc.append( "\\startTEXpage\n\\starttikzpicture[line cap=round,line join=round,>=triangle 45,x="); codeBeginDoc.append(xunit); codeBeginDoc.append("cm,y="); codeBeginDoc.append(yunit); codeBeginDoc.append("cm]\n"); } else if (format == GeoGebraToPgf.FORMAT_BEAMER) { codePreamble.append("\\documentclass[" + frame.getFontSize() + "pt]{beamer}\n" + "\\usepackage{pgf,tikz}\n\\usetikzlibrary{arrows}\n\\pagestyle{empty}\n"); codeBeginDoc.append("\\begin{frame}\n"); codeBeginDoc.append( "\\begin{tikzpicture}[line cap=round,line join=round,>=triangle 45,x="); codeBeginDoc.append(xunit); codeBeginDoc.append("cm,y="); codeBeginDoc.append(yunit); codeBeginDoc.append("cm]\n"); } if (format == FORMAT_BEAMER) { format = FORMAT_LATEX; } // Draw Grid if (euclidianView.getShowGrid()) { drawGrid(); } // Draw axis if (euclidianView.getShowXaxis() || euclidianView.getShowYaxis()) { drawAxis(); } // Clipping codeFilledObject.append("\\clip"); writePoint(xmin, ymin, codeFilledObject); codeFilledObject.append(" rectangle "); writePoint(xmax, ymax, codeFilledObject); codeFilledObject.append(";\n"); /* * get all objects from construction and "draw" them by creating PGF * code */ drawAllElements(); /* * Object [] geos = * kernel.getConstruction().getGeoSetConstructionOrder().toArray(); for * (int i=0;i<geos.length;i++){ GeoElement g = (GeoElement)(geos[i]); * drawGeoElement(g,false); } */ // add code for Points and Labels if (codePoint.length() != 0 && format == GeoGebraToPgf.FORMAT_LATEX) { codePoint.insert(0, "\\begin{scriptsize}\n"); codePoint.append("\\end{scriptsize}\n"); } // add code for Points and Labels code.append(codePoint); // Close Environment tikzpicture if (format == GeoGebraToPgf.FORMAT_LATEX) { code.append("\\end{tikzpicture}\n"); if (isBeamer) { code.append("\\end{frame}\n"); } code.append("\\end{document}"); codeBeginDoc.insert(0, "\\begin{document}\n"); } else if (format == GeoGebraToPgf.FORMAT_PLAIN_TEX) { code.append("\\endtikzpicture\n\\bye\n"); } else if (format == GeoGebraToPgf.FORMAT_CONTEXT) { code.append("\\stoptikzpicture\n\\stopTEXpage\n\\stoptext"); codeBeginDoc.insert(0, "\\starttext\n"); } /* * String formatFont=resizeFont(app.getFontSize()); if * (null!=formatFont){ codeBeginPic.insert(0,formatFont+"\n"); * code.append("}\n"); } */ code.insert(0, codeFilledObject + ""); code.insert(0, codeBeginDoc + ""); code.insert(0, codePreamble + ""); frame.write(code); } @Override protected void drawLocus(GeoLocus g) { ArrayList<MyPoint> ll = g.getPoints(); Iterator<MyPoint> it = ll.iterator(); startBeamer(code); code.append("\\draw"); String s = lineOptionCode(g, true); if (s.length() != 0) { s = "[" + s + "] "; } code.append(s); boolean first = true; boolean out = false; while (it.hasNext()) { MyPoint mp = it.next(); double x = mp.x; double y = mp.y; boolean b = mp.getLineTo(); if (x > xmin && x < xmax && y > ymin && y < ymax) { if (b && !first) { code.append(" -- "); } else if (first) { first = false; } writePoint(x, y, code); out = false; } else if (!first && mp.getLineTo() && !out) { out = true; code.append(" -- "); writePoint(x, y, code); } else { first = true; out = false; } } code.append(";\n"); endBeamer(code); } @Override protected void drawBoxPlot(GeoNumeric geo) { AlgoBoxPlot algo = ((AlgoBoxPlot) geo.getParentAlgorithm()); double y = algo.getA().getDouble(); double height = algo.getB().getDouble(); double[] lf = algo.getLeftBorders(); double min = lf[0]; double q1 = lf[1]; double med = lf[2]; double q3 = lf[3]; double max = lf[4]; startBeamer(codeFilledObject); codeFilledObject.append("\\draw "); String s = lineOptionCode(geo, true); if (s.length() != 0) { s = "[" + s + "] "; } codeFilledObject.append(s); // Min vertical bar writePoint(min, y - height, codeFilledObject); codeFilledObject.append("-- "); writePoint(min, y + height, codeFilledObject); codeFilledObject.append(" "); // Max vertical bar writePoint(max, y - height, codeFilledObject); codeFilledObject.append("-- "); writePoint(max, y + height, codeFilledObject); codeFilledObject.append(" "); // Med vertical bar writePoint(med, y - height, codeFilledObject); codeFilledObject.append("-- "); writePoint(med, y + height, codeFilledObject); // Min-q1 horizontal codeFilledObject.append(" "); writePoint(min, y, codeFilledObject); codeFilledObject.append("-- "); writePoint(q1, y, codeFilledObject); // q3-max codeFilledObject.append(" "); writePoint(q3, y, codeFilledObject); codeFilledObject.append("-- "); writePoint(max, y, codeFilledObject); codeFilledObject.append(";\n"); if (isBeamer) { codeFilledObject.append(" "); } // Rectangle q1-q3 codeFilledObject.append("\\draw"); if (s.length() != 0) { codeFilledObject.append(s); } writePoint(q1, y - height, codeFilledObject); codeFilledObject.append(" rectangle "); writePoint(q3, y + height, codeFilledObject); codeFilledObject.append(";\n"); endBeamer(codeFilledObject); } @Override protected void drawSumTrapezoidal(GeoNumeric geo) { AlgoFunctionAreaSums algo = (AlgoFunctionAreaSums) geo .getParentAlgorithm(); int n = algo.getIntervals(); double[] y = algo.getValues(); double[] x = algo.getLeftBorder(); // Trapezoidal sum startBeamer(codeFilledObject); for (int i = 0; i < n; i++) { codeFilledObject.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { codeFilledObject.append("[" + s + "] "); } writePoint(x[i], 0, codeFilledObject); codeFilledObject.append(" -- "); writePoint(x[i + 1], 0, codeFilledObject); codeFilledObject.append(" -- "); writePoint(x[i + 1], y[i + 1], codeFilledObject); codeFilledObject.append(" -- "); writePoint(x[i], y[i], codeFilledObject); codeFilledObject.append(" -- cycle;\n"); if (i != n - 1 && isBeamer) { codeFilledObject.append(" "); } } endBeamer(codeFilledObject); } @Override protected void drawSumUpperLower(GeoNumeric geo) { AlgoFunctionAreaSums algo = (AlgoFunctionAreaSums) geo .getParentAlgorithm(); int n = algo.getIntervals(); double step = algo.getStep(); double[] y = algo.getValues(); double[] x = algo.getLeftBorder(); startBeamer(codeFilledObject); for (int i = 0; i < n; i++) { codeFilledObject.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { codeFilledObject.append("[" + s + "] "); } writePoint(x[i], 0, codeFilledObject); codeFilledObject.append(" rectangle "); writePoint(x[i] + step, y[i], codeFilledObject); codeFilledObject.append(";\n"); if (i != -1 && isBeamer) { codeFilledObject.append(" "); } } endBeamer(codeFilledObject); } @Override protected void drawIntegralFunctions(GeoNumeric geo) { // command: \draw[option]{[domain=a:b,samples=..] plot(\x,{f(\x)}) // }--(b,g(b)) -- {[domain=b:a,samples=..] plot(\x,{g(\x)}) }--(a,f(a)) // --cycle; AlgoIntegralFunctions algo = (AlgoIntegralFunctions) geo .getParentAlgorithm(); // function f GeoFunction f = algo.getF(); // function g GeoFunction g = algo.getG(); // between a and b double a = algo.getA().getDouble(); double b = algo.getB().getDouble(); // values for f(a) and g(b) double fa = f.value(a); double gb = g.value(b); String value = f.toValueString(getStringTemplate()); value = killSpace(StringUtil.toLaTeXString(value, true)); boolean plotWithGnuplot = warningFunc(value, "tan(") || warningFunc(value, "cosh(") || warningFunc(value, "acosh(") || warningFunc(value, "asinh(") || warningFunc(value, "atanh(") || warningFunc(value, "sinh(") || warningFunc(value, "tanh("); startBeamer(codeFilledObject); codeFilledObject.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { codeFilledObject.append("[" + s + "] "); } codeFilledObject.append("{"); if (plotWithGnuplot) { codeFilledObject.append(" plot[raw gnuplot, id=func"); codeFilledObject.append(functionIdentifier); functionIdentifier++; codeFilledObject.append("] function{set samples 100; set xrange ["); codeFilledObject.append(a); codeFilledObject.append(":"); codeFilledObject.append(b); codeFilledObject.append("]; plot "); value = value.replaceAll("\\^", "**"); codeFilledObject.append(value); codeFilledObject.append("}"); } else { codeFilledObject.append("["); codeFilledObject.append("smooth,samples=50,domain="); codeFilledObject.append(a); codeFilledObject.append(":"); codeFilledObject.append(b); codeFilledObject.append("] plot"); codeFilledObject.append("(\\x,{"); value = replaceX(value, "\\x"); codeFilledObject.append(value); codeFilledObject.append("})"); } codeFilledObject.append("} -- "); writePoint(b, gb, codeFilledObject); codeFilledObject.append(" {"); value = g.toValueString(getStringTemplate()); value = killSpace(StringUtil.toLaTeXString(value, true)); plotWithGnuplot = warningFunc(value, "tan(") || warningFunc(value, "cosh(") || warningFunc(value, "acosh(") || warningFunc(value, "asinh(") || warningFunc(value, "atanh(") || warningFunc(value, "sinh(") || warningFunc(value, "tanh("); if (plotWithGnuplot) { codeFilledObject.append("-- plot[raw gnuplot, id=func"); codeFilledObject.append(functionIdentifier); functionIdentifier++; codeFilledObject.append( "] function{set parametric ; set samples 100; set trange ["); codeFilledObject.append(a); codeFilledObject.append(":"); codeFilledObject.append(b); codeFilledObject.append("]; plot "); String variable = format(b + a) + "-t"; codeFilledObject.append(variable); codeFilledObject.append(","); value = replaceX(value, variable); value = value.replaceAll("\\^", "**"); codeFilledObject.append(value); codeFilledObject.append("}"); } else { codeFilledObject.append("["); codeFilledObject.append("smooth,samples=50,domain="); codeFilledObject.append(b); codeFilledObject.append(":"); codeFilledObject.append(a); codeFilledObject.append("] -- plot"); codeFilledObject.append("(\\x,{"); value = replaceX(value, "\\x"); codeFilledObject.append(value); codeFilledObject.append("})"); } codeFilledObject.append("} -- "); writePoint(a, fa, codeFilledObject); codeFilledObject.append(" -- cycle;\n"); endBeamer(codeFilledObject); } @Override protected void drawIntegral(GeoNumeric geo) { // command: \plot[option] // plot[domain]{\pstplot{a}{b}{f(x)}\lineto(b,0)\lineto(a,0)\closepath} AlgoIntegralDefinite algo = (AlgoIntegralDefinite) geo .getParentAlgorithm(); // function f GeoFunction f = algo.getFunction(); // between a and b double a = algo.getA().getDouble(); double b = algo.getB().getDouble(); if (a == Double.NEGATIVE_INFINITY) { a = xmin; } if (b == Double.POSITIVE_INFINITY) { b = xmax; } String value = f.toValueString(getStringTemplate()); value = killSpace(StringUtil.toLaTeXString(value, true)); boolean plotWithGnuplot = warningFunc(value, "tan(") || warningFunc(value, "cosh(") || warningFunc(value, "acosh(") || warningFunc(value, "asinh(") || warningFunc(value, "atanh(") || warningFunc(value, "sinh(") || warningFunc(value, "tanh("); startBeamer(codeFilledObject); if (!isLatexFunction(f.toValueString(StringTemplate.noLocalDefault))) { double af = xmin; double bf = xmax; if (f.hasInterval()) { af = f.getIntervalMin(); bf = f.getIntervalMax(); } f.setInterval(a, b); drawFunction(f, code, true, geo); f.setInterval(af, bf); if (f.isEuclidianVisible()) { drawFunction(f, code, false, geo); } } else { codeFilledObject.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { codeFilledObject.append("["); codeFilledObject.append(s); } if (plotWithGnuplot) { if (s.length() != 0) { codeFilledObject.append("]"); } codeFilledObject.append(" plot[raw gnuplot, id=func"); codeFilledObject.append(functionIdentifier); functionIdentifier++; codeFilledObject .append("] function{set samples 100; set xrange ["); codeFilledObject.append(a); codeFilledObject.append(":"); codeFilledObject.append(b); codeFilledObject.append("]; plot "); value = value.replaceAll("\\^", "**"); codeFilledObject.append(value); codeFilledObject.append("}"); } else { if (s.length() != 0) { codeFilledObject.append(", "); } else { codeFilledObject.append("["); } codeFilledObject.append("smooth,samples=50,domain="); codeFilledObject.append(a); codeFilledObject.append(":"); codeFilledObject.append(b); codeFilledObject.append("] plot"); codeFilledObject.append("(\\x,{"); value = replaceX(value, "\\x"); codeFilledObject.append(value); codeFilledObject.append("})"); } codeFilledObject.append(" -- "); writePoint(b, 0, codeFilledObject); codeFilledObject.append(" -- "); writePoint(a, 0, codeFilledObject); codeFilledObject.append(" -- cycle;\n"); endBeamer(codeFilledObject); } } @Override protected void drawSlope(GeoNumeric geo) { int slopeTriangleSize = geo.getSlopeTriangleSize(); double rwHeight = geo.getValue() * slopeTriangleSize; double height = euclidianView.getYscale() * rwHeight; double[] coords = new double[2]; if (Math.abs(height) > Float.MAX_VALUE) { return; } // get point on line g ((AlgoSlope) geo.getParentAlgorithm()).getInhomPointOnLine(coords); // draw slope triangle double x = coords[0]; double y = coords[1]; double xright = x + slopeTriangleSize; startBeamer(codeFilledObject); codeFilledObject.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { codeFilledObject.append("[" + s + "] "); } writePoint(x, y, codeFilledObject); codeFilledObject.append(" -- "); writePoint(xright, y, codeFilledObject); codeFilledObject.append(" -- "); writePoint(xright, y + rwHeight, codeFilledObject); codeFilledObject.append(";\n"); // draw Label double xLabelHor = (x + xright) / 2; double yLabelHor = y - ((euclidianView.getFont().getSize() + 2) / euclidianView.getYscale()); GColor geocolor = geo.getObjectColor(); codePoint.append("\\draw[color="); colorCode(geocolor, codePoint); codePoint.append("] "); writePoint(xLabelHor, yLabelHor, codePoint); codePoint.append(" node[anchor=south west] {"); codePoint.append(slopeTriangleSize); codePoint.append("};\n"); endBeamer(codeFilledObject); } @Override protected void drawAngle(GeoAngle geo) { int arcSize = geo.getArcSize(); AlgoElement algo = geo.getParentAlgorithm(); GeoPointND vertex, point; GeoVectorND v; GeoPoint tempPoint = new GeoPoint(construction); tempPoint.setCoords(0.0, 0.0, 1.0); double[] firstVec = new double[2]; double[] m = new double[2]; // angle defines with three points if (algo instanceof AlgoAnglePoints) { AlgoAnglePoints pa = (AlgoAnglePoints) algo; vertex = pa.getB(); point = pa.getA(); vertex.getInhomCoords(m); // first vec Coords coords = point.getInhomCoordsInD3(); firstVec[0] = coords.getX() - m[0]; firstVec[1] = coords.getY() - m[1]; } // angle between two vectors else if (algo instanceof AlgoAngleVectors) { AlgoAngleVectors va = (AlgoAngleVectors) algo; v = va.getv(); // vertex vertex = v.getStartPoint(); if (vertex == null) { vertex = tempPoint; } vertex.getInhomCoords(m); // first vec v.getInhomCoords(firstVec); } // angle between two lines else if (algo instanceof AlgoAngleLines) { AlgoAngleLines la = (AlgoAngleLines) algo; vertex = tempPoint; la.updateDrawInfo(m, firstVec, null); } // angle of a single vector or a single point else if (algo instanceof AlgoAngleVector) { AlgoAngleVector va = (AlgoAngleVector) algo; GeoVec3D vec = va.getVec3D(); if (vec instanceof GeoVector) { v = (GeoVector) vec; // vertex vertex = v.getStartPoint(); if (vertex == null) { vertex = tempPoint; } vertex.getInhomCoords(m); } else if (vec instanceof GeoPoint) { vertex = tempPoint; // vertex vertex.getInhomCoords(m); } firstVec[0] = 1; firstVec[1] = 0; } // Fix bug with Slider tempPoint.remove(); // /////////////// double angSt = Math.atan2(firstVec[1], firstVec[0]); // Michael Borcherds 2007-10-21 BEGIN // double angExt = geo.getValue(); double angExt = geo.getRawAngle(); if (angExt > Math.PI * 2) { angExt -= Math.PI * 2; } // if (geo.getAngleStyle() == GeoAngle.ANGLE_ISCLOCKWISE) { // angSt += angExt; // angExt = 2.0 * Math.PI - angExt; // } if (geo.getAngleStyle() == AngleStyle.NOTREFLEX) { if (angExt > Math.PI) { angSt += angExt; angExt = 2.0 * Math.PI - angExt; } } if (geo.getAngleStyle() == AngleStyle.ISREFLEX) { if (angExt < Math.PI) { angSt += angExt; angExt = 2.0 * Math.PI - angExt; } } // if (geo.changedReflexAngle()) { // angSt = angSt - angExt; // } // Michael Borcherds 2007-10-21 END angExt += angSt; double r = arcSize / euclidianView.getXscale(); // if angle=90 and decoration=little square if (Kernel.isEqual(geo.getValue(), Kernel.PI_HALF) && geo.isEmphasizeRightAngle() && euclidianView .getRightAngleStyle() == EuclidianStyleConstants.RIGHT_ANGLE_STYLE_SQUARE) { r = r / Math.sqrt(2); double[] x = new double[8]; x[0] = m[0] + r * Math.cos(angSt); x[1] = m[1] + r * Math.sin(angSt); x[2] = m[0] + r * Math.sqrt(2) * Math.cos(angSt + Kernel.PI_HALF / 2); x[3] = m[1] + r * Math.sqrt(2) * Math.sin(angSt + Kernel.PI_HALF / 2); x[4] = m[0] + r * Math.cos(angSt + Kernel.PI_HALF); x[5] = m[1] + r * Math.sin(angSt + Kernel.PI_HALF); x[6] = m[0]; x[7] = m[1]; startBeamer(codeFilledObject); codeFilledObject.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { codeFilledObject.append("[" + s + "] "); } for (int i = 0; i < 4; i++) { writePoint(x[2 * i], x[2 * i + 1], codeFilledObject); codeFilledObject.append(" -- "); } codeFilledObject.append("cycle; \n"); endBeamer(codeFilledObject); } // draw arc for the angle else { // set arc in real world coords double angStDeg = Math.toDegrees(angSt) % 360; double angEndDeg = Math.toDegrees(angExt) % 360; if (angStDeg > angEndDeg) { angStDeg = angStDeg - 360; } startBeamer(codeFilledObject); codeFilledObject.append("\\draw [shift={"); writePoint(m[0], m[1], codeFilledObject); codeFilledObject.append("}"); String s = lineOptionCode(geo, true); if (s.length() != 0) { codeFilledObject.append("," + s + "] "); } else { codeFilledObject.append("] "); } codeFilledObject.append("(0,0) -- ("); codeFilledObject.append(format(angStDeg)); codeFilledObject.append(":"); codeFilledObject.append(format(r)); codeFilledObject.append(") arc ("); codeFilledObject.append(format(angStDeg)); codeFilledObject.append(":"); codeFilledObject.append(format(angEndDeg)); codeFilledObject.append(":"); codeFilledObject.append(format(r)); codeFilledObject.append(") -- cycle;\n"); endBeamer(codeFilledObject); // draw the dot if angle= 90 and decoration=dot if (Kernel.isEqual(geo.getValue(), Kernel.PI_HALF) && geo.isEmphasizeRightAngle() && euclidianView .getRightAngleStyle() == EuclidianStyleConstants.RIGHT_ANGLE_STYLE_DOT) { double diameter = geo.getLineThickness() / euclidianView.getXscale(); double radius = arcSize / euclidianView.getXscale() / 1.7; double labelAngle = (angSt + angExt) / 2.0; double x1 = m[0] + radius * Math.cos(labelAngle); double x2 = m[1] + radius * Math.sin(labelAngle); // draw an ellipse // command: \draw (0,0) circle diameter startBeamer(code); code.append("\\fill"); s = lineOptionCode(geo, true); if (s.length() != 0) { code.append("[" + s + "] "); } writePoint(x1, x2, code); code.append(" circle ("); code.append(format(diameter / 2)); code.append(");\n"); endBeamer(code); } } int deco = geo.getDecorationType(); if (deco != GeoElement.DECORATION_NONE) { startBeamer(code); markAngle(geo, r, m, angSt, angExt); endBeamer(code); } } @Override protected void drawArrowArc(GeoAngle geo, double[] vertex, double angSt, double angEnd, double r, boolean anticlockwise) { double angStDeg = Math.toDegrees(angSt) % 360; double angEndDeg = Math.toDegrees(angEnd) % 360; if (angStDeg > angEndDeg) { angStDeg -= 360; } code.append("\\draw [shift={"); writePoint(vertex[0], vertex[1], code); code.append("},-"); if (anticlockwise) { code.append(">"); } else { code.append("<"); } String s = lineOptionCode(geo, false); if (s.length() != 0) { code.append("," + s + "] "); } else { code.append("] "); } code.append("("); code.append(format(angStDeg)); code.append(":"); code.append(format(r)); code.append(") arc ("); code.append(format(angStDeg)); code.append(":"); code.append(format(angEndDeg)); code.append(":"); code.append(format(r)); code.append(");\n"); } @Override protected void drawArc(GeoAngle geo, double[] vertex, double angSt, double angEnd, double r) { double angStDeg = Math.toDegrees(angSt) % 360; double angEndDeg = Math.toDegrees(angEnd) % 360; if (angStDeg > angEndDeg) { angStDeg -= 360; } if (isBeamer) { code.append(" "); } code.append("\\draw [shift={"); writePoint(vertex[0], vertex[1], code); code.append("}"); String s = lineOptionCode(geo, false); if (s.length() != 0) { code.append("," + s + "] "); } else { code.append("] "); } code.append("("); code.append(format(angStDeg)); code.append(":"); code.append(format(r)); code.append(") arc ("); code.append(format(angStDeg)); code.append(":"); code.append(format(angEndDeg)); code.append(":"); code.append(format(r)); code.append(");\n"); } @Override protected void drawTick(GeoAngle geo, double[] vertex, double angle0) { double cos = Math.cos(angle0); double sin = Math.sin(-angle0); double radius = geo.getArcSize(); double diff = 2.5 + geo.getLineThickness() / 4d; double x1 = euclidianView.toRealWorldCoordX( vertex[0] + (radius - diff) * cos); double x2 = euclidianView.toRealWorldCoordX( vertex[0] + (radius + diff) * cos); double y1 = euclidianView.toRealWorldCoordY(vertex[1] + (radius - diff) * sin * euclidianView.getScaleRatio()); double y2 = euclidianView.toRealWorldCoordY(vertex[1] + (radius + diff) * sin * euclidianView.getScaleRatio()); if (isBeamer) { code.append(" "); } code.append("\\draw"); String s = lineOptionCode(geo, false); if (s.length() != 0) { code.append("[" + s + "] "); } writePoint(x1, y1, code); code.append(" -- "); writePoint(x2, y2, code); code.append(";\n"); } @Override protected void drawSlider(GeoNumeric geo) { boolean horizontal = geo.isSliderHorizontal(); double max = geo.getIntervalMax(); double min = geo.getIntervalMin(); double value = geo.getValue(); double width = geo.getSliderWidth(); double x = geo.getSliderX(); double y = geo.getSliderY(); // start point of horizontal line for slider if (geo.isAbsoluteScreenLocActive()) { x = euclidianView.toRealWorldCoordX(x); y = euclidianView.toRealWorldCoordY(y); width = horizontal ? width / euclidianView.getXscale() : width / euclidianView.getYscale(); } // create point for slider GeoPoint geoPoint = new GeoPoint(construction); geoPoint.setObjColor(geo.getObjectColor()); String label = StringUtil.toLaTeXString(geo.getLabelDescription(), true); geoPoint.setLabel(label); double param = (value - min) / (max - min); geoPoint.setPointSize(2 + (geo.getLineThickness() + 1) / 3); geoPoint.setLabelVisible(geo.isLabelVisible()); if (horizontal) { geoPoint.setCoords(x + width * param, y, 1.0); } else { geoPoint.setCoords(x, y + width * param, 1.0); } DrawPoint drawPoint = new DrawPoint(euclidianView, geoPoint); drawPoint.setGeoElement(geo); if (geo.isLabelVisible()) { if (horizontal) { drawPoint.xLabel -= 15; drawPoint.yLabel -= 5; } else { drawPoint.xLabel += 5; drawPoint.yLabel += 2 * geoPoint.getPointSize() + 4; } } drawGeoPoint(geoPoint); drawLabel(geoPoint, drawPoint); // Bug fixed with Slider geoPoint.remove(); // draw Line or Slider startBeamer(code); code.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { s = "[" + s + "] "; code.append(s); } writePoint(x, y, code); code.append(" -- "); if (horizontal) { x += width; } else { y += width; } writePoint(x, y, code); code.append(";\n"); endBeamer(code); } @Override protected void drawPolygon(GeoPolygon geo) { // command: \pspolygon[par](x0,y0)....(xn,yn) double alpha = geo.getAlphaValue(); if (alpha == 0.0f && geo.getFillType() == FillType.IMAGE) { return; } startBeamer(codeFilledObject); codeFilledObject.append("\\fill"); String s = lineOptionCode(geo, true); if (s.length() != 0) { s = "[" + s + "] "; codeFilledObject.append(s); } GeoPointND[] points = geo.getPoints(); for (int i = 0; i < points.length; i++) { Coords coords = points[i].getCoordsInD2(); double x = coords.getX(), y = coords.getY(), z = coords.getZ(); x = x / z; y = y / z; writePoint(x, y, codeFilledObject); codeFilledObject.append(" -- "); } codeFilledObject.append("cycle;\n"); endBeamer(codeFilledObject); } @Override protected void drawText(GeoText geo) { boolean isLatex = geo.isLaTeX(); String st = geo.getTextString(); int style = geo.getFontStyle(); int size = (int) (geo.getFontSizeMultiplier() * getApp().getFontSize()); GeoPoint gp; double x, y; // compute location of text if (geo.isAbsoluteScreenLocActive()) { x = geo.getAbsoluteScreenLocX(); y = geo.getAbsoluteScreenLocY(); } else { gp = (GeoPoint) geo.getStartPoint(); if (gp == null) { x = (int) euclidianView.getXZero(); y = (int) euclidianView.getYZero(); } else { if (!gp.isDefined()) { return; } x = euclidianView.toScreenCoordX(gp.inhomX); y = euclidianView.toScreenCoordY(gp.inhomY); } x += geo.labelOffsetX; y += geo.labelOffsetY; } x = euclidianView.toRealWorldCoordX(x); y = euclidianView .toRealWorldCoordY(y - euclidianView.getFont().getSize()); int id = st.indexOf("\n"); // One line if (id == -1 || isLatex) { startBeamer(code); code.append("\\draw "); // Color GColor geocolor = geo.getObjectColor(); if (!geocolor.equals(GColor.BLACK)) { code.append("[color="); colorCode(geocolor, code); code.append("]"); } writePoint(x, y, code); code.append(" node[anchor=north west] {"); addText(st, isLatex, style); code.append("};\n"); endBeamer(code); } // MultiLine else { StringBuilder sb = new StringBuilder(); StringTokenizer stk = new StringTokenizer(st, "\n"); int width = 0; GFont font = AwtFactory.getPrototype().newFont( geo.isSerifFont() ? "Serif" : "SansSerif", style, size); while (stk.hasMoreTokens()) { String line = stk.nextToken(); width = Math.max(width, (int) Math.ceil( StringUtil.getPrototype().estimateLength(line, font))); sb.append(line); if (stk.hasMoreTokens()) { sb.append(" \\\\ "); } } startBeamer(code); code.append("\\draw "); // Color GColor geocolor = geo.getObjectColor(); if (!geocolor.equals(GColor.BLACK)) { code.append("[color="); colorCode(geocolor, code); code.append("]"); } writePoint(x, y, code); code.append(" node[anchor=north west] {"); code.append("\\parbox{"); code.append(format( width * (xmax - xmin) * xunit / euclidianView.getWidth() + 1)); code.append(" cm}{"); addText(new String(sb), isLatex, style); code.append("}};\n"); endBeamer(code); } } private void addText(String st0, boolean isLatex, int style) { String st = st0; if (isLatex) { st = st.replaceAll("\n", " "); } if (format == FORMAT_LATEX) { if (isLatex) { if (!st.startsWith("$")) { code.append("$"); } for (int i = 0; i < st.length(); i++) { char uCode = st.charAt(i); if (UnicodeTeX.getMap().containsKey(uCode)) { addTextPackage(); st = st.replaceAll("\\" + uCode, "\\\\" + UnicodeTeX.getMap().get(uCode)); } } } // Replace all backslash symbol with \textbackslash else { st = st.replaceAll("\\\\", "\\\\textbackslash "); for (int i = 0; i < st.length(); i++) { char uCode = st.charAt(i); if (UnicodeTeX.getMap().containsKey(uCode)) { addTextPackage(); st = st.replaceAll("\\" + uCode, "\\$\\\\" + UnicodeTeX.getMap().get(uCode) + "\\$"); } } st = st.replace("$\\euro$", "euro"); } switch (style) { default: // do nothing break; case 1: if (isLatex) { code.append("\\mathbf{"); } else { code.append("\\textbf{"); } break; case 2: if (isLatex) { code.append("\\mathit{"); } else { code.append("\\textit{"); } break; case 3: if (isLatex) { code.append("\\mathit{\\mathbf{"); } else { code.append("\\textit{\\textbf{"); } break; } code.append(st); switch (style) { default: // do nothing break; case 1: case 2: code.append("}"); break; case 3: code.append("}}"); break; } if (isLatex && !st.endsWith("$")) { code.append("$"); } } else if (format == FORMAT_CONTEXT) { if (isLatex) { code.append("$"); } switch (style) { default: // do nothing break; case 1: code.append("{\\bf "); break; case 2: code.append("{\\em "); break; case 3: code.append("{\\em \\bf"); break; } code.append(st); switch (style) { default: // do nothing break; case 1: case 2: case 3: code.append("}"); break; } if (isLatex && !st.endsWith("$")) { code.append("$"); } } else if (format == FORMAT_PLAIN_TEX) { if (isLatex && !st.endsWith("$")) { code.append("$"); } switch (style) { default: // do nothing break; case 1: code.append("\\bf{"); break; case 2: code.append("\\it{ "); break; case 3: code.append("\\it{\\bf{"); break; } code.append(st); switch (style) { default: // do nothing break; case 1: case 2: code.append("}"); break; case 3: code.append("}}"); break; } if (isLatex && !st.endsWith("$")) { code.append("$"); } } } @Override protected void drawGeoConicPart(GeoConicPart geo) { double r1 = geo.getHalfAxes()[0]; double r2 = geo.getHalfAxes()[1]; double startAngle = geo.getParameterStart(); double endAngle = geo.getParameterEnd(); // Get all coefficients form the transform matrix GAffineTransform af = geo.getAffineTransform(); double m11 = af.getScaleX(); double m22 = af.getScaleY(); double m12 = af.getShearX(); double m21 = af.getShearY(); double tx = af.getTranslateX(); double ty = af.getTranslateY(); if (startAngle > endAngle) { startAngle = startAngle - Math.PI * 2; } // Sector command: \draw[shift={(x0,y0)},par] (0,0) -- // (startAngle:radius) arc (angleStart:EndAngle:radius) -- cycle // Arc command: \draw[shift={(x0,y0)},par] (startAngle:radius) arc // (startAngle:endAngle:radius) startBeamer(code); code.append("\\draw [shift={"); writePoint(tx, ty, code); code.append("}"); String s = lineOptionCode(geo, true); if (s.length() != 0) { s = "," + s + "] "; code.append(s); } else { code.append("]"); } if (geo.getConicPartType() == GeoConicNDConstants.CONIC_PART_SECTOR) { code.append(" (0,0) -- "); StringBuilder sb1 = new StringBuilder(); sb1.append(format(r1)); sb1.append("*cos(\\t r)"); StringBuilder sb2 = new StringBuilder(); sb2.append(format(r2)); sb2.append("*sin(\\t r)"); code.append(" plot[domain="); code.append(format(startAngle)); code.append(":"); code.append(format(endAngle)); code.append(",variable=\\t]({"); code.append(format(m11)); code.append("*"); code.append(sb1); code.append("+"); code.append(format(m12)); code.append("*"); code.append(sb2); code.append("},{"); code.append(format(m21)); code.append("*"); code.append(sb1); code.append("+"); code.append(format(m22)); code.append("*"); code.append(sb2); code.append("})"); code.append(" -- cycle ;\n"); } else if (geo .getConicPartType() == GeoConicNDConstants.CONIC_PART_ARC) { StringBuilder sb1 = new StringBuilder(); sb1.append(format(r1)); sb1.append("*cos(\\t r)"); StringBuilder sb2 = new StringBuilder(); sb2.append(format(r2)); sb2.append("*sin(\\t r)"); code.append(" plot[domain="); code.append(format(startAngle)); code.append(":"); code.append(format(endAngle)); code.append(",variable=\\t]({"); code.append(format(m11)); code.append("*"); code.append(sb1); code.append("+"); code.append(format(m12)); code.append("*"); code.append(sb2); code.append("},{"); code.append(format(m21)); code.append("*"); code.append(sb1); code.append("+"); code.append(format(m22)); code.append("*"); code.append(sb2); code.append("});\n"); } endBeamer(code); } private void drawFunction(GeoFunction geo, StringBuilder sb, boolean integral, GeoNumeric geo1) { Function f = geo.getFunction(); if (null == f) { return; } String value = f.toValueString(getStringTemplate()); value = killSpace(StringUtil.toLaTeXString(value, true)); boolean plotWithGnuplot = warningFunc(value, "cosh(") || warningFunc(value, "acosh(") || warningFunc(value, "asinh(") || warningFunc(value, "atanh(") || warningFunc(value, "sinh(") || warningFunc(value, "tanh("); boolean[] v = hasFractionalOrTrigoExponent(f.getExpression()); if (v[0]) { if (!plotWithGnuplot) { addWarningGnuplot(); } if (v[1]) { value = value.replaceAll("\\*180/pi", ""); } } double a = xmin; double b = xmax; if (geo.hasInterval()) { a = Math.max(a, geo.getIntervalMin()); b = Math.min(b, geo.getIntervalMax()); } double xrangemax = a, xrangemin = a; while (xrangemax < b) { xrangemin = firstDefinedValue(geo, a, b); // Application.debug("xrangemin "+xrangemin); if (xrangemin == b) { break; } xrangemax = maxDefinedValue(geo, xrangemin, b); // Application.debug("xrangemax "+xrangemax); startBeamer(sb); if (forceGnuplot) { if (!isLatexFunction( f.toValueString(StringTemplate.noLocalDefault)) || f.toValueString(StringTemplate.noLocalDefault) .toLowerCase().contains('\u212f' + "^")) { drawNoLatexFunction(geo, sb, xrangemax, xrangemin, integral, geo1); } else { value = value.replaceAll("\\*180/pi", ""); drawGnuPlot(geo, sb, value, xrangemax, xrangemin); } } else { if (!isLatexFunction( f.toValueString(StringTemplate.noLocalDefault)) || isTrigInv( f.toValueString(StringTemplate.noLocalDefault)) || f.toValueString(StringTemplate.noLocalDefault) .toLowerCase().contains('\u212f' + "^")) { drawNoLatexFunction(geo, sb, xrangemax, xrangemin, integral, geo1); } else { drawPgfStandard(geo, sb, value, xrangemax, xrangemin); } } endBeamer(sb); xrangemax += PRECISION_XRANGE_FUNCTION; a = xrangemax; } } private static boolean isTrigInv(String s) { return s.toLowerCase().contains("atan(") || s.toLowerCase().contains("acos(") || s.toLowerCase().contains("asin("); } /** * @param en * @return */ private boolean[] hasFractionalOrTrigoExponent(ExpressionNode en) { boolean[] v = { false, false }; if (en == null || en.getOperation() == Operation.NO_OPERATION) { return v; } Operation op = en.getOperation(); if (op == Operation.POWER) { ExpressionNode le = en.getRightTree(); if (le.isNumberValue()) { if (le.toValueString(getStringTemplate()).contains("sin") || le.toValueString(getStringTemplate()).contains("cos") || le.toValueString(getStringTemplate()) .contains("tan")) { v[1] = true; } double val = le.evaluateDouble(); v[0] = !Kernel.isInteger(val); return v; } op = le.getOperation(); v[0] = op == Operation.DIVIDE; return v; } if (!hasFractionalOrTrigoExponent(en.getRightTree())[0]) { return hasFractionalOrTrigoExponent(en.getLeftTree()); } v[0] = true; return v; } private void drawPgfStandard(GeoFunction geo, StringBuilder sb, String value0, double xrangemax, double xrangemin) { sb.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { sb.append("["); sb.append(s); sb.append(","); } else { sb.append("["); } sb.append("smooth,samples=100,domain="); sb.append(xrangemin); sb.append(":"); sb.append(xrangemax); sb.append("] plot"); sb.append("(\\x,{"); String value = replaceX(value0, "(\\x)"); sb.append(value); sb.append("});\n"); } private void drawGnuPlot(GeoFunction geo, StringBuilder sb, String value, double xrangemax, double xrangemin) { sb.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { sb.append("["); sb.append(s); sb.append("]"); } sb.append(" plot[raw gnuplot, id=func"); sb.append(functionIdentifier); functionIdentifier++; sb.append("] function{set samples 100; set xrange ["); sb.append(xrangemin + 0.1); sb.append(":"); sb.append(xrangemax - 0.1); sb.append("]; plot "); String valueGnu = value.replaceAll("\\^", "**"); sb.append(valueGnu); sb.append("};\n"); } private void drawNoLatexFunction(GeoFunction geo, StringBuilder sb, double xrangemax, double xrangemin, boolean integral, GeoNumeric geo1) { String s = lineOptionCode(geo, true); if (s.length() != 0) { s = "[" + s + "]"; } String template = "\\draw" + s + " (%0,%1) -- (%2,%3);\n"; String cycle = ""; String close = ""; if (integral) { close = "(" + format(geo.getIntervalMax()) + ",0) -- (" + format(geo.getIntervalMin()) + ",0) -- "; sb.append("\\draw[" + lineOptionCode(geo1, true) + "]"); template = " (%0,%1) -- (%2,%3) --"; cycle = "cycle;\n"; } StringBuilder lineBuilder = drawNoLatexFunction(geo, xrangemax, xrangemin, 400, template); sb.append(lineBuilder.toString()); sb.append(close); sb.append(cycle); } @Override protected void drawSingleCurveCartesian(GeoCurveCartesian geo, boolean trasparency) { // \parametricplot[algebraic=true,linecolor=red] // {-3.14}{3.14}{cos(3*t)|sin(2*t)} // Only done using gnuplot // add Warning addWarningGnuplot(); drawSingleCurve(geo, code); } private void drawSingleCurve(GeoCurveCartesian geo, StringBuilder sb) { double start = geo.getMinParameter(); double end = geo.getMaxParameter(); // boolean isClosed=geo.isClosedPath(); String fx = geo.getFunX(getStringTemplate()); fx = killSpace(StringUtil.toLaTeXString(fx, true)); fx = fx.replaceAll("\\^", "**"); String fy = geo.getFunY(getStringTemplate()); fy = killSpace(StringUtil.toLaTeXString(fy, true)); fy = fy.replaceAll("\\^", "**"); // It seems that only for the parametric curve are not required grades fx = fx.replaceAll("\\*180/pi", ""); fy = fy.replaceAll("\\*180/pi", ""); String variable = geo.getVarString(getStringTemplate()); boolean warning = !("t".equals(variable)); if (warning) { code.append( "% WARNING: You have to use the special variable t in parametric plot"); } startBeamer(sb); sb.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { sb.append("["); sb.append(s); } if (s.length() != 0) { sb.append(", "); } else { sb.append("["); } sb.append("smooth,samples=100,domain="); sb.append(start); sb.append(":"); sb.append(end); sb.append("] plot[parametric] function{"); sb.append(fx); sb.append(","); sb.append(fy); sb.append("};\n"); endBeamer(sb); } @Override protected void drawFunction(GeoFunction geo) { drawFunction(geo, code, false, null); } /** * This method replace the letter "x" by the String substitute * * @param name * The function */ private static String replaceX(String name, String substitute) { StringBuilder sb = new StringBuilder(name); // If the expression starts with minus - // Insert a "0" (Bug from TikZ /PGF) if (name.length() > 0 && name.charAt(0) == '-') { sb.insert(0, "0"); } int i = 0; while (i < sb.length()) { char before = '1'; char after = '1'; char character = sb.charAt(i); if (character == 'x') { if (i > 0) { before = sb.charAt(i - 1); } if (i < sb.length() - 1) { after = sb.charAt(i + 1); } int id1 = "1234567890^ +-*/%()\t".indexOf(after); int id2 = "1234567890^ +-*/%()\t".indexOf(before); if (id1 != -1 && id2 != -1) { sb.deleteCharAt(i); sb.insert(i, substitute); i += substitute.length() - 1; } } i++; } return new String(sb); } /** * We have to rewrite the function - kill spaces - add character * when * needed - rename several functions (done in #ExpressionNode.toString()) - * rename constants */ private static String killSpace(String name) { // 2 x +3 ----> 2*x+3 StringBuilder sb = new StringBuilder(); boolean operand = false; boolean space = false; for (int i = 0; i < name.length(); i++) { char c = name.charAt(i); if ("*/+-".indexOf(c) != -1) { sb.append(c); operand = true; space = false; } else if (c == ' ') { if (!operand) { space = true; } else { space = false; operand = false; } } else { if (space && !name.contains("If")) { sb.append("*"); } sb.append(c); space = false; operand = false; } } renameFunc(sb, Unicode.EULER_STRING, "2.718281828"); renameFunc(sb, "\\pi", "3.1415926535"); return new String(sb); } /** * Some Functions are not supported by PGF. This method write a warning in * preamble * * @param sb * The complete Function * @param name * The Function unsupported */ private boolean warningFunc(String sb, String nameFunc) { if (forceGnuplot) { return true; } int ind = sb.indexOf(nameFunc); if (ind != -1) { addWarningGnuplot(); return true; } return false; } private void addWarningGnuplot() { if (gnuplotWarning) { return; } gnuplotWarning = true; codePreamble.append(" \n%<<<<<<<WARNING>>>>>>>\n"); codePreamble.append( "% PGF/Tikz doesn't support the following mathematical functions:\n"); codePreamble.append("% cosh, acosh, sinh, asinh, tanh, atanh,\n"); codePreamble.append("% x^r with r not integer\n\n"); codePreamble.append("% Plotting will be done using GNUPLOT\n"); codePreamble.append( "% GNUPLOT must be installed and you must allow Latex to call external programs by\n"); codePreamble.append("% Adding the following option to your compiler\n"); codePreamble.append("% shell-escape OR enable-write18 \n"); codePreamble.append("% Example: pdflatex --shell-escape file.tex \n\n"); } private void addWarningHatch() { if (hatchWarning) { return; } hatchWarning = true; codePreamble.append(" \n\n%<<<<<<<WARNING>>>>>>>\n"); codePreamble .append("% PGF/Tikz doesn't support hatch filling very well\n"); codePreamble.append("% Use PStricks for a perfect hatching export\n\n"); } @Override protected void drawGeoVector(GeoVector geo) { GeoPointND pointStart = geo.getStartPoint(); double x1, y1; if (null == pointStart) { x1 = 0; y1 = 0; } else { Coords c = pointStart.getCoords(); x1 = c.getX() / c.getZ(); y1 = c.getY() / c.getZ(); } double[] coord = new double[3]; geo.getCoords(coord); double x2 = coord[0] + x1; double y2 = coord[1] + y1; startBeamer(code); code.append("\\draw [->"); String s = lineOptionCode(geo, true); if (s.length() != 0) { code.append(","); code.append(s); } code.append("] "); writePoint(x1, y1, code); code.append(" -- "); writePoint(x2, y2, code); code.append(";\n"); endBeamer(code); } private void drawCircle(GeoConic geo) { StringBuilder build = new StringBuilder(); if (xunit == yunit) { // draw a circle // command: \draw[options](x_center,y_center) circle (R cm) double x = geo.getTranslationVector().getX(); double y = geo.getTranslationVector().getY(); double r = geo.getHalfAxes()[0]; startBeamer(build); build.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { s = " [" + s + "] "; } build.append(s); writePoint(x, y, build); build.append(" circle ("); String tmpr = format(r * xunit); if (kernel.getAlgebraProcessor().evaluateToDouble(tmpr) != 0) { build.append(tmpr); } else { build.append(r); } build.append("cm);\n"); endBeamer(build); } else { // draw an ellipse // command: \draw[options](x_center,y_center) ellipse (XRadius cm // and YRadius cm) double x1 = geo.getTranslationVector().getX(); double y1 = geo.getTranslationVector().getY(); double r1 = geo.getHalfAxes()[0]; double r2 = geo.getHalfAxes()[1]; startBeamer(build); build.append("\\draw"); String s = lineOptionCode(geo, true); if (s.length() != 0) { s = " [" + s + "] "; } build.append(s); writePoint(x1, y1, build); build.append(" ellipse ("); build.append(format(r1 * xunit)); build.append("cm and "); build.append(format(r2 * yunit)); build.append("cm);\n"); endBeamer(build); } if (geo.getAlphaValue() > 0.0f) { codeFilledObject.append(build); } else { code.append(build); } } @Override protected void drawGeoConic(GeoConic geo) { switch (geo.getType()) { default: // do nothing break; // if conic is a circle case GeoConicNDConstants.CONIC_CIRCLE: drawCircle(geo); break; // if conic is an ellipse case GeoConicNDConstants.CONIC_ELLIPSE: // command: \draw[rotate // around={angle:center},lineOptions](x_center,y_center) ellipse (R1 // and R2) GAffineTransform at = geo.getAffineTransform(); double eigenvecX = at.getScaleX(); double eigenvecY = at.getShearY(); double x1 = geo.getTranslationVector().getX(); double y1 = geo.getTranslationVector().getY(); double r1 = geo.getHalfAxes()[0]; double r2 = geo.getHalfAxes()[1]; double angle = Math.toDegrees(Math.atan2(eigenvecY, eigenvecX)); startBeamer(code); code.append("\\draw [rotate around={"); code.append(format(angle)); code.append(":"); writePoint(x1, y1, code); code.append("}"); String s = lineOptionCode(geo, true); if (s.length() != 0) { code.append(","); code.append(s); } code.append("] "); writePoint(x1, y1, code); code.append(" ellipse ("); code.append(format(r1 * xunit)); code.append("cm and "); code.append(format(r2 * yunit)); code.append("cm);\n"); endBeamer(code); break; // if conic is a parabola case GeoConicNDConstants.CONIC_PARABOLA: // command: \draw[rotate // around={angle:center},xshift=x1,yshift=y1,lineOptions] // plot(\x,\x^2/2/p); // parameter of the parabola double p = geo.p; at = geo.getAffineTransform(); // first eigenvec eigenvecX = at.getScaleX(); eigenvecY = at.getShearY(); // vertex x1 = geo.getTranslationVector().getX(); y1 = geo.getTranslationVector().getY(); // calculate the x range to draw the parabola double x0 = Math.max(Math.abs(x1 - xmin), Math.abs(x1 - xmax)); x0 = Math.max(x0, Math.abs(y1 - ymin)); x0 = Math.max(x0, Math.abs(y1 - ymax)); // avoid sqrt by choosing x = k*p with // i = 2*k is quadratic number // make parabola big enough: k*p >= 2*x0 -> 2*k >= 4*x0/p x0 = 4 * x0 / p; int i = 4; int k2 = 16; while (k2 < x0) { i += 2; k2 = i * i; } // x0 = k2/2 * p; // x = k*p x0 = i * p; // y = sqrt(2k p^2) = i p angle = Math.toDegrees(Math.atan2(eigenvecY, eigenvecX)) - 90; startBeamer(code); code.append("\\draw [samples=50,rotate around={"); code.append(format(angle)); code.append(":"); writePoint(x1, y1, code); code.append("},xshift="); code.append(format(x1 * xunit)); code.append("cm,yshift="); code.append(format(y1 * yunit)); code.append("cm"); s = lineOptionCode(geo, true); if (s.length() != 0) { code.append(","); code.append(s); } code.append(",domain="); code.append(-x0); code.append(":"); code.append(x0); code.append(")] plot (\\x,{(\\x)^2/2/"); code.append(p); code.append("});\n"); endBeamer(code); break; case GeoConicNDConstants.CONIC_HYPERBOLA: // command: \draw[domain=-1:1,rotate // around={angle:center},xshift=x1,yshift=y1,lineOptions] // plot({a(1+\x^2)/(1-\x^2)},2b\x/(1-\x^2)); at = geo.getAffineTransform(); eigenvecX = at.getScaleX(); eigenvecY = at.getShearY(); x1 = geo.getTranslationVector().getX(); y1 = geo.getTranslationVector().getY(); r1 = geo.getHalfAxes()[0]; r2 = geo.getHalfAxes()[1]; angle = Math.toDegrees(Math.atan2(eigenvecY, eigenvecX)); startBeamer(code); code.append("\\draw [samples=50,domain=-0.99:0.99,rotate around={"); code.append(format(angle)); code.append(":"); writePoint(x1, y1, code); code.append("},xshift="); code.append(format(x1 * xunit)); code.append("cm,yshift="); code.append(format(y1 * yunit)); code.append("cm"); s = lineOptionCode(geo, true); if (s.length() != 0) { code.append(","); code.append(s); } code.append("] plot ({"); code.append(format(r1)); code.append("*(1+(\\x)^2)/(1-(\\x)^2)},{"); code.append(format(r2)); code.append("*2*(\\x)/(1-(\\x)^2)});\n"); if (isBeamer) { code.append(" "); } code.append("\\draw [samples=50,domain=-0.99:0.99,rotate around={"); code.append(format(angle)); code.append(":"); writePoint(x1, y1, code); code.append("},xshift="); code.append(format(x1 * xunit)); code.append("cm,yshift="); code.append(format(y1 * yunit)); code.append("cm"); s = lineOptionCode(geo, true); if (s.length() != 0) { code.append(","); code.append(s); } code.append("] plot ({"); code.append(format(r1)); code.append("*(-1-(\\x)^2)/(1-(\\x)^2)},{"); code.append(format(r2)); code.append("*(-2)*(\\x)/(1-(\\x)^2)});\n"); endBeamer(code); break; } } /** * This will generate the Tikz code to draw the GeoPoint gp into the * StringBuilder PointCode * * @param gp * The choosen GeoPoint */ @Override protected void drawGeoPoint(GeoPoint gp) { if (frame.getExportPointSymbol()) { double x = kernel.getAlgebraProcessor() .evaluateToDouble("" + gp.getX()); double y = kernel.getAlgebraProcessor() .evaluateToDouble("" + gp.getY()); double z = gp.getZ(); x = x / z; y = y / z; GColor dotcolor = gp.getObjectColor(); double dotsize = gp.getPointSize(); int dotstyle = gp.getPointStyle(); if (dotstyle == -1) { // default dotstyle = EuclidianStyleConstants.POINT_STYLE_DOT; } startBeamer(codePoint); if (dotstyle == EuclidianStyleConstants.POINT_STYLE_CIRCLE) { codePoint.append("\\draw [color="); colorCode(dotcolor, codePoint); codePoint.append("] "); writePoint(x, y, codePoint); codePoint.append(" circle ("); codePoint.append(dotsize / 2); codePoint.append("pt);\n"); } else if (dotstyle == EuclidianStyleConstants.POINT_STYLE_CROSS) { codePoint.append("\\draw [color="); colorCode(dotcolor, codePoint); codePoint.append("] "); writePoint(x, y, codePoint); codePoint.append("-- ++(-"); codePoint.append(dotsize / 2); codePoint.append("pt,-"); codePoint.append(dotsize / 2); codePoint.append("pt) -- ++("); codePoint.append(dotsize); codePoint.append("pt,"); codePoint.append(dotsize); codePoint.append("pt) ++(-"); codePoint.append(dotsize); codePoint.append("pt,0) -- ++("); codePoint.append(dotsize); codePoint.append("pt,-"); codePoint.append(dotsize); codePoint.append("pt);\n"); } else if (dotstyle == EuclidianStyleConstants.POINT_STYLE_EMPTY_DIAMOND) { codePoint.append("\\draw [color="); colorCode(dotcolor, codePoint); codePoint.append("] "); writePoint(x, y, codePoint); codePoint.append(" ++(-"); codePoint.append(dotsize / 2); codePoint.append("pt,0 pt) -- ++("); codePoint.append(dotsize / 2); codePoint.append("pt,"); codePoint.append(dotsize / 2); codePoint.append("pt)--++("); codePoint.append(dotsize / 2); codePoint.append("pt,-"); codePoint.append(dotsize / 2); codePoint.append("pt)--++(-"); codePoint.append(dotsize / 2); codePoint.append("pt,-"); codePoint.append(dotsize / 2); codePoint.append("pt)--++(-"); codePoint.append(dotsize / 2); codePoint.append("pt,"); codePoint.append(dotsize / 2); codePoint.append("pt);\n"); } else if (dotstyle == EuclidianStyleConstants.POINT_STYLE_FILLED_DIAMOND) { codePoint.append("\\draw [fill="); colorCode(dotcolor, codePoint); codePoint.append("] "); writePoint(x, y, codePoint); codePoint.append(" ++(-"); codePoint.append(dotsize / 2); codePoint.append("pt,0 pt) -- ++("); codePoint.append(dotsize / 2); codePoint.append("pt,"); codePoint.append(dotsize / 2); codePoint.append("pt)--++("); codePoint.append(dotsize / 2); codePoint.append("pt,-"); codePoint.append(dotsize / 2); codePoint.append("pt)--++(-"); codePoint.append(dotsize / 2); codePoint.append("pt,-"); codePoint.append(dotsize / 2); codePoint.append("pt)--++(-"); codePoint.append(dotsize / 2); codePoint.append("pt,"); codePoint.append(dotsize / 2); codePoint.append("pt);\n"); } else if (dotstyle == EuclidianStyleConstants.POINT_STYLE_PLUS) { codePoint.append("\\draw [color="); colorCode(dotcolor, codePoint); codePoint.append("] "); writePoint(x, y, codePoint); codePoint.append("-- ++(-"); codePoint.append(dotsize / 2); codePoint.append("pt,0 pt) -- ++("); codePoint.append(dotsize); codePoint.append("pt,0 pt) ++(-"); codePoint.append(dotsize / 2); codePoint.append("pt,-"); codePoint.append(dotsize / 2); codePoint.append("pt) -- ++(0 pt,"); codePoint.append(dotsize); codePoint.append("pt);\n"); } else if (dotstyle == EuclidianStyleConstants.POINT_STYLE_TRIANGLE_EAST) { double radius = 3 * dotsize / 4; codePoint.append("\\draw [fill="); colorCode(dotcolor, codePoint); codePoint.append(",shift={"); writePoint(x, y, codePoint); codePoint.append("},rotate=270] (0,0)"); codePoint.append(" ++(0 pt,"); codePoint.append(radius); codePoint.append("pt) -- ++("); codePoint.append(format(radius / 2 * Math.sqrt(3))); codePoint.append("pt,-"); codePoint.append(radius / 2 * 3); codePoint.append("pt)--++(-"); codePoint.append(format(radius * Math.sqrt(3))); codePoint.append("pt,0 pt) -- ++("); codePoint.append(format(radius / 2 * Math.sqrt(3))); codePoint.append("pt,"); codePoint.append(3 * radius / 2); codePoint.append("pt);\n"); } else if (dotstyle == EuclidianStyleConstants.POINT_STYLE_TRIANGLE_NORTH) { double radius = 3 * dotsize / 4; codePoint.append("\\draw [fill="); colorCode(dotcolor, codePoint); codePoint.append(",shift={"); writePoint(x, y, codePoint); codePoint.append("}] (0,0)"); codePoint.append(" ++(0 pt,"); codePoint.append(radius); codePoint.append("pt) -- ++("); codePoint.append(format(radius / 2 * Math.sqrt(3))); codePoint.append("pt,-"); codePoint.append(radius / 2 * 3); codePoint.append("pt)--++(-"); codePoint.append(format(radius * Math.sqrt(3))); codePoint.append("pt,0 pt) -- ++("); codePoint.append(format(radius / 2 * Math.sqrt(3))); codePoint.append("pt,"); codePoint.append(3 * radius / 2); codePoint.append("pt);\n"); } else if (dotstyle == EuclidianStyleConstants.POINT_STYLE_TRIANGLE_SOUTH) { double radius = 3 * dotsize / 4; codePoint.append("\\draw [fill="); colorCode(dotcolor, codePoint); codePoint.append(",shift={"); writePoint(x, y, codePoint); codePoint.append("},rotate=180] (0,0)"); codePoint.append(" ++(0 pt,"); codePoint.append(radius); codePoint.append("pt) -- ++("); codePoint.append(format(radius / 2 * Math.sqrt(3))); codePoint.append("pt,-"); codePoint.append(radius / 2 * 3); codePoint.append("pt)--++(-"); codePoint.append(format(radius * Math.sqrt(3))); codePoint.append("pt,0 pt) -- ++("); codePoint.append(format(radius / 2 * Math.sqrt(3))); codePoint.append("pt,"); codePoint.append(3 * radius / 2); codePoint.append("pt);\n"); } else if (dotstyle == EuclidianStyleConstants.POINT_STYLE_TRIANGLE_WEST) { double radius = 3 * dotsize / 4; codePoint.append("\\draw [fill="); colorCode(dotcolor, codePoint); codePoint.append(",shift={"); writePoint(x, y, codePoint); codePoint.append("},rotate=90] (0,0)"); codePoint.append(" ++(0 pt,"); codePoint.append(radius); codePoint.append("pt) -- ++("); codePoint.append(format(radius / 2 * Math.sqrt(3))); codePoint.append("pt,-"); codePoint.append(radius / 2 * 3); codePoint.append("pt)--++(-"); codePoint.append(format(radius * Math.sqrt(3))); codePoint.append("pt,0 pt) -- ++("); codePoint.append(format(radius / 2 * Math.sqrt(3))); codePoint.append("pt,"); codePoint.append(3 * radius / 2); codePoint.append("pt);\n"); } // default is the circle point style else { codePoint.append("\\draw [fill="); colorCode(dotcolor, codePoint); codePoint.append("] "); writePoint(x, y, codePoint); codePoint.append(" circle ("); codePoint.append(dotsize / 2); codePoint.append("pt);\n"); } endBeamer(codePoint); } // In case of trimmed intersection if (gp.getShowTrimmedIntersectionLines()) { AlgoElement algo = gp.getParentAlgorithm(); if (algo instanceof AlgoIntersectAbstract) { GeoElement[] geos = algo.getInput(); double x1 = euclidianView.toScreenCoordXd(gp.getInhomX()); double y1 = euclidianView.toScreenCoordYd(gp.getInhomY()); double x2 = euclidianView.toScreenCoordXd(gp.getInhomX()) + 30; double y2 = euclidianView.toScreenCoordYd(gp.getInhomY()) + 30; x1 = euclidianView.toRealWorldCoordX(x1); x2 = euclidianView.toRealWorldCoordX(x2); y1 = euclidianView.toRealWorldCoordY(y1); y2 = euclidianView.toRealWorldCoordY(y2); double r1 = Math.abs(x2 - x1); double r2 = Math.abs(y2 - y1); StringBuilder s = new StringBuilder(); if (format == GeoGebraToPgf.FORMAT_LATEX) { s.append("\\begin{scope}\n"); } else if (format == GeoGebraToPgf.FORMAT_CONTEXT) { s.append("\\startscope\n"); } else if (format == GeoGebraToPgf.FORMAT_PLAIN_TEX) { s.append("\\scope\n"); } s.append("\\clip ("); s.append(format(x1)); s.append(","); s.append(format(y1)); s.append(") ellipse ("); s.append(format(r1)); s.append("cm and "); s.append(format(r2)); s.append("cm);\n"); // Latex format String end = "\\end{scope}\n"; if (format == GeoGebraToPgf.FORMAT_CONTEXT) { end = "\\stopscope\n"; } else if (format == GeoGebraToPgf.FORMAT_PLAIN_TEX) { end = "\\endscope\n"; } boolean fill1 = false; boolean draw = !geos[0].isEuclidianVisible(); if (draw) { fill1 = geos[0].isFillable() && geos[0].getAlphaValue() > 0.0f; if (fill1) { codeFilledObject.append(s); } else { code.append(s); } drawGeoElement(geos[0], false, true); } if (geos.length > 1 && !geos[1].isEuclidianVisible()) { boolean fill2 = geos[1].isFillable() && (geos[1].getAlphaValue() > 0.0f); if (draw) { if (fill1 == fill2) { drawGeoElement(geos[1], false, true); if (fill1) { codeFilledObject.append(end); } else { code.append(end); } } else { if (fill1) { codeFilledObject.append(end); } else { code.append(end); } if (fill2) { codeFilledObject.append(s); } else { code.append(s); } drawGeoElement(geos[1], false, true); if (fill2) { codeFilledObject.append(end); } else { code.append(end); } } } else { if (fill2) { codeFilledObject.append(s); } else { code.append(s); } drawGeoElement(geos[1], false, true); if (fill2) { codeFilledObject.append(end); } else { code.append(end); } } } else if (draw) { if (fill1) { codeFilledObject.append(end); } else { code.append(end); } } } } } /** * Generate the PGF/tikZ code to draw an infinite line */ @Override protected void drawGeoLine(GeoLine geo) { double a = geo.getX(); double b = geo.getY(); double c = geo.getZ(); startBeamer(code); /* * To prevent "dimension too large" prblem with TikZ Eg: \draw [line * width=1.6pt,color=qqcctt,domain=-2.097:9.585] * plot(\x,{(-7.657--2.392*\x)/-0.007}); * * We allow a factor of 40 between the coefficient director and the * window height Else we say it's vertical. */ double heightScreen = frame.textYmaxValue() - frame.textYminValue(); if (Math.abs(a / b / heightScreen) > 40) { b = 0; } if (b != 0) { code.append("\\draw ["); String option = lineOptionCode(geo, true); if (option.length() != 0) { code.append(option); code.append(","); } code.append("domain="); code.append(format(xmin)); code.append(":"); code.append(format(xmax)); code.append("] plot(\\x,{(-"); code.append(format(c)); code.append("-"); code.append(format(a)); code.append("*\\x)/"); String tmpy = format(b); if (kernel.getAlgebraProcessor().evaluateToDouble(tmpy) != 0) { code.append(tmpy); } else { code.append(b); } code.append("});\n"); } else if (b == 0) { code.append("\\draw "); String s = lineOptionCode(geo, true); if (s.length() != 0) { s = "[" + s + "] "; } code.append(s); writePoint(-c / a, ymin, code); code.append(" -- "); writePoint(-c / a, ymax, code); code.append(";\n"); } endBeamer(code); } /** * This will generate the Tikz code to draw the GeoSegment geo into the * StringBuilder code * * @param geo * The choosen GeoPoint */ @Override protected void drawGeoSegment(GeoSegment geo) { double[] A = new double[2]; double[] B = new double[2]; GeoPoint pointStart = geo.getStartPoint(); GeoPoint pointEnd = geo.getEndPoint(); pointStart.getInhomCoords(A); pointEnd.getInhomCoords(B); startBeamer(code); code.append("\\draw "); String s = lineOptionCode(geo, true); if (s.length() != 0) { s = "[" + s + "] "; } code.append(s); writePoint(A[0], A[1], code); code.append("-- "); writePoint(B[0], B[1], code); code.append(";\n"); int deco = geo.getDecorationType(); if (deco != GeoElement.DECORATION_NONE) { mark(A, B, deco, geo); } endBeamer(code); } @Override protected void drawLine(double x1, double y1, double x2, double y2, GeoElement geo) { if (isBeamer) { code.append(" "); } code.append("\\draw "); String s = lineOptionCode(geo, true); if (s.length() != 0) { s = "[" + s + "] "; } code.append(s); writePoint(x1, y1, code); code.append(" -- "); writePoint(x2, y2, code); code.append(";\n"); } @Override protected void drawGeoRay(GeoRay geo) { GeoPoint pointStart = geo.getStartPoint(); double x1 = pointStart.getX(); double z1 = pointStart.getZ(); x1 = x1 / z1; double y1 = pointStart.getY() / z1; double a = geo.getX(); double b = geo.getY(); double c = geo.getZ(); double inf = xmin, sup = xmax; if (b > 0) { inf = x1; } else { sup = x1; } startBeamer(code); if (b != 0) { code.append("\\draw ["); String option = lineOptionCode(geo, true); if (option.length() != 0) { code.append(option); code.append(","); } code.append("domain="); code.append(inf); code.append(":"); code.append(sup); code.append("] plot(\\x,{(-"); code.append(format(c)); code.append("-"); code.append(format(a)); code.append("*\\x)/"); String tmpy = format(b); if (kernel.getAlgebraProcessor().evaluateToDouble(tmpy) != 0) { code.append(tmpy); } else { code.append(b); } code.append("});\n"); } else if (b == 0) { if (a < 0) { sup = ymax; } else { sup = ymin; } code.append("\\draw "); String s = lineOptionCode(geo, true); if (s.length() != 0) { s = "[" + s + "] "; } code.append(s); writePoint(x1, y1, code); code.append(" -- "); writePoint(x1, sup, code); code.append(";\n"); } endBeamer(code); } @Override protected void drawLabel(GeoElement geo, DrawableND drawGeo0) { DrawableND drawGeo = drawGeo0; try { if (geo.isLabelVisible()) { String name = geo.getLabelDescription(); if (geo.getLabelMode() == GeoElement.LABEL_CAPTION) { String nameSym = name; for (int i = 0; i < name.length(); i++) { char uCode = name.charAt(i); if (UnicodeTeX.getMap().containsKey(uCode)) { nameSym = nameSym.replaceAll("\\" + uCode, "\\$\\\\" + UnicodeTeX.getMap().get(uCode) + "\\$"); } } nameSym = nameSym.replace("$\\euro$", "euro"); name = nameSym; if (name.contains("_")) { name = "$" + name + "$"; } } else { name = "$" + StringUtil.toLaTeXString( geo.getLabelDescription(), true) + "$"; } if (name.indexOf(Unicode.DEGREE) != -1) { if (format == GeoGebraToPgf.FORMAT_LATEX) { name = name.replaceAll(Unicode.DEGREE, "\\\\textrm{\\\\degre}"); if (codePreamble.indexOf("\\degre") == -1) { codePreamble.append( "\\newcommand{\\degre}{\\ensuremath{^\\circ}}\n"); } } else if (format == GeoGebraToPgf.FORMAT_CONTEXT || format == GeoGebraToPgf.FORMAT_PLAIN_TEX) { name = name.replaceAll(Unicode.DEGREE, "{}^{\\\\circ}"); } } if (null == drawGeo) { drawGeo = euclidianView.getDrawableFor(geo); } double xLabel = drawGeo.getxLabel(); double yLabel = drawGeo.getyLabel(); xLabel = euclidianView.toRealWorldCoordX(Math.round(xLabel)); yLabel = euclidianView.toRealWorldCoordY(Math.round(yLabel)); GColor geocolor = geo.getObjectColor(); startBeamer(codePoint); int width = (int) Math .ceil(StringUtil.getPrototype() .estimateLength(StringUtil.toLaTeXString( geo.getLabelDescription(), true), euclidianView.getFont())); int height = (int) Math .ceil(StringUtil.getPrototype() .estimateHeight(StringUtil.toLaTeXString( geo.getLabelDescription(), true), euclidianView.getFont())); double translation[] = new double[2]; translation[0] = euclidianView.getXZero() + width / 2.0; translation[1] = euclidianView.getYZero() - height / 2.0; translation[0] = euclidianView .toRealWorldCoordX(translation[0]); translation[1] = euclidianView .toRealWorldCoordY(translation[1]); codePoint.append("\\draw[color="); colorCode(geocolor, codePoint); codePoint.append("] "); writePoint(xLabel + translation[0], yLabel + translation[1], codePoint); codePoint.append(" node {"); codePoint.append(name); codePoint.append("};\n"); endBeamer(codePoint); } } catch (NullPointerException e) { // For GeoElement that don't have a Label // For example (created with geoList) } } /** * Generate the PGF/TikZ code for the Grid */ private void drawGrid() { // resizeFont(codeBeginDoc); GColor gridCol = euclidianView.getGridColor(); double[] GridDist = euclidianView.getGridDistances(); int gridLine = euclidianView.getGridLineStyle(); codeBeginDoc.append("\\draw [color="); colorCode(gridCol, codeBeginDoc); codeBeginDoc.append(","); LinestyleCode(gridLine, codeBeginDoc); codeBeginDoc.append(", xstep="); codeBeginDoc.append(sci2dec(GridDist[0] * xunit)); codeBeginDoc.append("cm,ystep="); codeBeginDoc.append(sci2dec(GridDist[1] * yunit)); codeBeginDoc.append("cm] "); writePoint(xmin, ymin, codeBeginDoc); codeBeginDoc.append(" grid "); writePoint(xmax, ymax, codeBeginDoc); codeBeginDoc.append(";\n"); } /** * Generate the PGF/TikZ code for Axis drawing */ private void drawAxis() { GColor color = euclidianView.getAxesColor(); // Drawing X Axis boolean showAxis = euclidianView.getShowXaxis(); String[] label = euclidianView.getAxesLabels(false); if (ymin > 0 || ymax < 0) { showAxis = false; } double spaceTick = euclidianView.getAxesNumberingDistances()[0]; boolean showNumbers = euclidianView.getShowAxesNumbers()[0]; int tickStyle = euclidianView.getAxesTickStyles()[0]; if (showAxis) { codeBeginDoc.append("\\draw[" + handleAxesStyle() + "color="); colorCode(color, codeBeginDoc); codeBeginDoc.append("] "); boolean[] positiveOnly = euclidianView.getPositiveAxes(); double assignMax = xmin; if (positiveOnly[0]) { assignMax = 0; } writePoint(assignMax, 0, codeBeginDoc); codeBeginDoc.append(" -- "); writePoint(xmax, 0, codeBeginDoc); codeBeginDoc.append(";\n"); int x1 = (int) (assignMax / spaceTick); int xstartInt = x1; double xstart = x1 * spaceTick; StringBuilder tmp = new StringBuilder(); while (xstart < xmax) { if (Math.abs(xstart) > 0.1) { tmp.append(format(xstart)); } xstart += spaceTick; x1++; if (xstart < xmax && Math.abs(xstart) > 0.1) { tmp.append(","); } } StringBuilder buffer = new StringBuilder(); buffer.append("\\foreach \\x in {"); buffer.append(tmp); buffer.append("}\n"); buffer.append("\\draw[shift={(\\x,0)},color="); colorCode(color, buffer); StringBuilder ticks = new StringBuilder(); if (tickStyle != EuclidianStyleConstants.AXES_TICK_STYLE_NONE) { ticks.append("] (0pt,2pt) -- (0pt,-2pt)"); } else { ticks.append("] (0pt,-2pt)"); } if (showNumbers) { String[] units = euclidianView.getAxesUnitLabels(); if (hasMeasureUnit(units, spaceTick, 0)) { String unit = getUnit(units, spaceTick, 0); // case degrees mm etc. if (!unit.contains("p")) { codeBeginDoc.append(buffer); codeBeginDoc.append(ticks); codeBeginDoc.append(" node[below] {"); if (unit.contains("c")) { codeBeginDoc.append(footnotesize("\\x" + unit)); } else { codeBeginDoc.append(footnotesize("\\x") + unit); } codeBeginDoc.append("};\n"); } else { // case pi if (!unit.contains("2")) { for (int i = xstartInt; i < x1; i++) { if (i != 0) { codeBeginDoc.append("\\draw[shift={(" + (i * Math.PI) + ",0)},color="); colorCode(color, codeBeginDoc); codeBeginDoc.append(ticks); codeBeginDoc.append(" node[below] {"); if (i == -1) { codeBeginDoc.append( footnotesize("-" + unit)); } else { if (i == 1) { codeBeginDoc .append(footnotesize(unit)); } else { codeBeginDoc.append( footnotesize(i + unit)); } } codeBeginDoc.append("};\n"); } } } else { // case pi/2 for (int i = xstartInt; i < x1; i++) { if (i != 0) { codeBeginDoc.append("\\draw[shift={(" + (i * Math.PI / 2) + ",0)},color="); colorCode(color, codeBeginDoc); codeBeginDoc.append(ticks); codeBeginDoc.append(" node[below] {"); if (i == -1) { codeBeginDoc.append( footnotesize("-" + unit)); } else { if (i == 1) { codeBeginDoc .append(footnotesize(unit)); } else { Fraction f = new Fraction(i, 2); if (f.doubleValue() == -1) { codeBeginDoc.append( footnotesize("-\\pi")); } else { if (f.doubleValue() == 1) { codeBeginDoc .append(footnotesize( "\\pi")); } else { String foot = "" + f.getNumerator() + "\\pi"; if (f.getDenominator() != 1) { foot += "/" + f .getDenominator(); } codeBeginDoc.append( footnotesize(foot)); } } } } codeBeginDoc.append("};\n"); } } } } } else { codeBeginDoc.append(buffer); codeBeginDoc.append(ticks); codeBeginDoc.append(" node[below] {"); codeBeginDoc.append(footnotesize("\\x")); codeBeginDoc.append("};\n"); } } else { codeBeginDoc.append(buffer); codeBeginDoc.append(ticks); codeBeginDoc.append(";\n"); } // Draw Label on X Axis if (null != label[0]) { codeBeginDoc.append("\\draw[color="); colorCode(color, codeBeginDoc); codeBeginDoc.append("] "); int width = (int) Math.ceil(StringUtil.getPrototype() .estimateLength(label[0], euclidianView.getFont())); GRectangle rect = euclidianView.getSelectionRectangle(); double x = euclidianView.toRealWorldCoordX( euclidianView.getWidth() - 10 - width); if (rect != null) { x = euclidianView .toRealWorldCoordX(rect.getMaxX() - 10 - width); } double y = euclidianView .toRealWorldCoordY(euclidianView.getYZero() - 4); writePoint(x, y, codeBeginDoc); codeBeginDoc.append(" node [anchor=south west] { "); codeBeginDoc.append(label[0]); codeBeginDoc.append("};\n"); } } // Drawing Y Axis showAxis = euclidianView.getShowYaxis(); if (xmin > 0 || xmax < 0) { showAxis = false; } spaceTick = euclidianView.getAxesNumberingDistances()[1]; showNumbers = euclidianView.getShowAxesNumbers()[1]; tickStyle = euclidianView.getAxesTickStyles()[1]; if (showAxis) { codeBeginDoc.append("\\draw[" + handleAxesStyle() + "color="); colorCode(color, codeBeginDoc); codeBeginDoc.append("] "); boolean[] positiveOnly = euclidianView.getPositiveAxes(); double assignMax = ymin; if (positiveOnly[0]) { assignMax = 0; } writePoint(0, assignMax, codeBeginDoc); codeBeginDoc.append(" -- "); writePoint(0, ymax, codeBeginDoc); codeBeginDoc.append(";\n"); int y1 = (int) (assignMax / spaceTick); int ystartInt = y1; double ystart = y1 * spaceTick; StringBuilder tmp = new StringBuilder(); while (ystart < ymax) { if (Math.abs(ystart) > 0.1) { tmp.append(format(ystart)); } ystart += spaceTick; y1++; if (ystart < ymax && Math.abs(ystart) > 0.1) { tmp.append(","); } } StringBuilder buffer = new StringBuilder(); buffer.append("\\foreach \\y in {"); buffer.append(tmp); buffer.append("}\n"); buffer.append("\\draw[shift={(0,\\y)},color="); colorCode(color, buffer); StringBuilder ticks = new StringBuilder(); if (tickStyle != EuclidianStyleConstants.AXES_TICK_STYLE_NONE) { ticks.append("] (2pt,0pt) -- (-2pt,0pt)"); } else { ticks.append("] (-2pt,0pt)"); } if (showNumbers) { String[] units = euclidianView.getAxesUnitLabels(); if (hasMeasureUnit(units, spaceTick, 1)) { String unit = getUnit(units, spaceTick, 1); // case degrees mm etc. if (!unit.contains("p")) { codeBeginDoc.append(buffer); codeBeginDoc.append(ticks); codeBeginDoc.append(" node[left] {"); if (unit.contains("c")) { codeBeginDoc.append(footnotesize("\\y" + unit)); } else { codeBeginDoc.append(footnotesize("\\y") + unit); } codeBeginDoc.append("};\n"); } else { // case pi if (!unit.contains("2")) { for (int i = ystartInt; i < y1; i++) { if (i != 0) { codeBeginDoc.append("\\draw[shift={(0," + (i * Math.PI) + ")},color="); colorCode(color, codeBeginDoc); codeBeginDoc.append(ticks); codeBeginDoc.append(" node[left] {"); if (i == -1) { codeBeginDoc.append( footnotesize("-" + unit)); } else { if (i == 1) { codeBeginDoc .append(footnotesize(unit)); } else { codeBeginDoc.append( footnotesize(i + unit)); } } codeBeginDoc.append("};\n"); } } } else { // case pi/2 for (int i = ystartInt; i < y1; i++) { if (i != 0) { codeBeginDoc.append("\\draw[shift={(0," + (i * Math.PI / 2) + ")},color="); colorCode(color, codeBeginDoc); codeBeginDoc.append(ticks); codeBeginDoc.append(" node[left] {"); if (i == -1) { codeBeginDoc.append( footnotesize("-" + unit)); } else { if (i == 1) { codeBeginDoc .append(footnotesize(unit)); } else { Fraction f = new Fraction(i, 2); if (f.doubleValue() == -1) { codeBeginDoc.append( footnotesize("-\\pi")); } else { if (f.doubleValue() == 1) { codeBeginDoc .append(footnotesize( "\\pi")); } else { String foot = "" + f.getNumerator() + "\\pi"; if (f.getDenominator() != 1) { foot += "/" + f .getDenominator(); } codeBeginDoc.append( footnotesize(foot)); } } } } codeBeginDoc.append("};\n"); } } } } } else { codeBeginDoc.append(buffer); codeBeginDoc.append(ticks); codeBeginDoc.append(" node[left] {"); codeBeginDoc.append(footnotesize("\\y")); codeBeginDoc.append("};\n"); } } else { codeBeginDoc.append(buffer); codeBeginDoc.append(ticks); codeBeginDoc.append(";\n"); } // Draw Label on Y Axis if (null != label[1]) { codeBeginDoc.append("\\draw[color="); colorCode(color, codeBeginDoc); codeBeginDoc.append("] "); int height = (int) Math.ceil(StringUtil.getPrototype() .estimateHeight(label[1], euclidianView.getFont())); GRectangle rect = euclidianView.getSelectionRectangle(); double x = euclidianView .toRealWorldCoordX(euclidianView.getXZero() + 5); double y = euclidianView.toRealWorldCoordY(5 + height); if (rect != null) { y = euclidianView .toRealWorldCoordY(rect.getMinY() + 5 + height); } writePoint(x, y, codeBeginDoc); codeBeginDoc.append(" node [anchor=west] { "); codeBeginDoc.append(label[1]); codeBeginDoc.append("};\n"); } } // Origin boolean notOrigin = ((xmax < 0 || xmin > 0) || (ymax < 0 || ymin > 0)); if (!notOrigin && (euclidianView.getShowAxesNumbers()[0] || euclidianView.getShowAxesNumbers()[1])) { codeBeginDoc.append("\\draw[color="); colorCode(color, codeBeginDoc); // append the origin codeBeginDoc.append("] (0pt,-10pt) node[right] {"); codeBeginDoc.append(footnotesize("0")); codeBeginDoc.append("};\n"); } } private static String getUnit(String[] units, double spaceTick, int xy) { // TODO Auto-generated method stub if (spaceTick == Math.PI / 2) { return "\\pi/2"; } if (spaceTick == Math.PI || units[xy].charAt(0) == 0x03c0) { return "\\pi"; } if (units[xy].charAt(0) == 0x00b0) { return "^\\circ"; } return units[xy]; } private static boolean hasMeasureUnit(String[] units, double spaceTick, int xy) { // TODO Auto-generated method stub return (units != null && units[xy] != null && units[xy].length() != 0) || spaceTick == Math.PI || spaceTick == Math.PI / 2; } private String footnotesize(String s) { if (format == GeoGebraToPgf.FORMAT_LATEX) { return "\\footnotesize $" + s + "$"; } else if (format == GeoGebraToPgf.FORMAT_CONTEXT) { return "\\tfx $" + s + "$"; } else if (format == GeoGebraToPgf.FORMAT_PLAIN_TEX) { return "$" + s + "$"; } return s; } /** * A util method adds point coordinates to a StringBuilder * * @param x * X point * @param y * Y Point * @param sb * The StringBuilder code */ private void writePoint(double x, double y, StringBuilder sb) { sb.append("("); sb.append(format(x)); sb.append(","); sb.append(format(y)); sb.append(")"); } /** * @param geo * element * @param transparency * whether to use transparency * @return line options */ public String lineOptionCode(GeoElement geo, boolean transparency) { StringBuilder sb = new StringBuilder(); int linethickness = geo.getLineThickness(); int linestyle = geo.getLineType(); Info info = new Info(geo); boolean coma = false; if (linethickness != EuclidianStyleConstants.DEFAULT_LINE_THICKNESS) { // coma needed coma = true; // bracket needed sb.append("line width="); sb.append(format(linethickness / 2.0 * 0.8)); sb.append("pt"); } if (linestyle != EuclidianStyleConstants.DEFAULT_LINE_TYPE) { if (coma) { sb.append(","); } else { coma = true; } LinestyleCode(linestyle, sb); } if (!info.getLinecolor().equals(GColor.BLACK)) { if (coma) { sb.append(","); } else { coma = true; } if (transparency && geo.isFillable() && info.getFillType() == FillType.IMAGE) { sb.append("pattern "); } sb.append("color="); colorCode(info.getLinecolor(), sb); } if (transparency && geo.isFillable()) { switch (info.getFillType()) { default: case STANDARD: if (info.getAlpha() > 0.0f) { if (coma) { sb.append(","); } else { coma = true; } sb.append("fill="); colorCode(info.getLinecolor(), sb); sb.append(",fill opacity="); sb.append(info.getAlpha()); } break; case SYMBOLS: case CROSSHATCHED: case CHESSBOARD: case HONEYCOMB: case DOTTED: case BRICK: case HATCH: addWarningHatch(); if (coma) { sb.append(","); } else { coma = true; } sb.append("fill="); colorCode(info.getLinecolor(), sb); sb.append(",pattern="); if (format == GeoGebraToPgf.FORMAT_CONTEXT) { if (codePreamble.indexOf("usetikzlibrary{patterns}") == -1) { codePreamble.append("\\usetikzlibrary{patterns}\n"); } } else { if (codePreamble.indexOf("usetikzlibrary[patterns]") == -1) { codePreamble.append("\\usetikzlibrary[patterns]\n"); } } double angle = info.getAngle(); if (info.getFillType() == FillType.DOTTED) { sb.append("dots"); } else { if (angle < 20) { sb.append("horizontal lines"); } else if (angle < 70) { sb.append("north east lines"); } else if (angle < 110) { sb.append("vertical lines"); } else if (angle < 160) { sb.append("north west lines"); } else { sb.append("horizontal lines"); } } sb.append(",pattern color="); colorCode(info.getLinecolor(), sb); break; } } return new String(sb); } /** * Append the line style parameters to the StringBuilder sb */ private void LinestyleCode(int linestyle, StringBuilder sb) { switch (linestyle) { default: // do nothing break; case EuclidianStyleConstants.LINE_TYPE_DOTTED: sb.append("dotted"); break; case EuclidianStyleConstants.LINE_TYPE_DASHED_SHORT: // sb.append("dash pattern=off 4pt on 4pt"); sb.append("dash pattern=on "); int size = resizePt(4); sb.append(size); sb.append("pt off "); sb.append(size); sb.append("pt"); break; case EuclidianStyleConstants.LINE_TYPE_DASHED_LONG: // sb.append("dash pattern=off 8pt on 8pt"); sb.append("dash pattern=on "); int size8 = resizePt(8); sb.append(size8); sb.append("pt off "); sb.append(size8); sb.append("pt"); break; case EuclidianStyleConstants.LINE_TYPE_DASHED_DOTTED: // sb.append("dash pattern=on 1 pt off 4pt on 8pt off 4 pt"); sb.append("dash pattern=on "); int size1 = resizePt(1); int size4 = resizePt(4); size8 = resizePt(8); sb.append(size1); sb.append("pt off "); sb.append(size4); sb.append("pt on "); sb.append(size8); sb.append("pt off "); sb.append(4); sb.append("pt"); break; } } /** * Append the name color to StringBuilder sb It will create a custom color, * if this color hasn't be defined yet * * @param c0 * The Choosen color * @param sb * The StringBuilder where the color has to be added */ @Override protected void colorCode(GColor c0, StringBuilder sb) { if (frame.isGrayscale()) { if (c0.equals(GColor.BLACK)) { sb.append("black"); return; } String colorname = ""; int red = c0.getRed(); int green = c0.getGreen(); int blue = c0.getBlue(); int grayscale = (red + green + blue) / 3; GColor c = GColor.newColor(grayscale, grayscale, grayscale); if (customColor.containsKey(c)) { colorname = customColor.get(c).toString(); } else { if (c.equals(GColor.BLACK)) { sb.append("black"); return; } else if (c.equals(GColor.RED)) { sb.append("red"); return; } else if (c.equals(GColor.BLUE)) { sb.append("blue"); return; } else if (c.equals(GColor.GREEN)) { sb.append("green"); return; } colorname = createCustomColor(red, green, blue); // Example: \definecolor{orange}{rgb}{1,0.5,0} if (format == GeoGebraToPgf.FORMAT_LATEX || format == GeoGebraToPgf.FORMAT_PLAIN_TEX) { codeBeginDoc.insert(0, "\\definecolor{" + colorname + "}{rgb}{" + format(grayscale / 255d) + "," + format(grayscale / 255d) + "," + format(grayscale / 255d) + "}\n"); customColor.put(c, colorname); } else if (format == GeoGebraToPgf.FORMAT_CONTEXT) { codeBeginDoc.insert(0, "\\definecolor[" + colorname + "][r=" + format(grayscale / 255d) + ",g=" + format(grayscale / 255d) + ",b=" + format(grayscale / 255d) + "]\n"); customColor.put(c, colorname); } } if (c.equals(GColor.BLACK)) { sb.append("black"); return; } sb.append(colorname); } else { if (c0.equals(GColor.BLACK)) { sb.append("black"); return; } String colorname = ""; if (customColor.containsKey(c0)) { colorname = customColor.get(c0).toString(); } else { int red = c0.getRed(); int green = c0.getGreen(); int blue = c0.getBlue(); colorname = createCustomColor(red, green, blue); // Example: \definecolor{orange}{rgb}{1,0.5,0} if (format == GeoGebraToPgf.FORMAT_LATEX || format == GeoGebraToPgf.FORMAT_PLAIN_TEX) { codeBeginDoc.insert(0, "\\definecolor{" + colorname + "}{rgb}{" + format(red / 255d) + "," + format(green / 255d) + "," + format(blue / 255d) + "}\n"); customColor.put(c0, colorname); } else if (format == GeoGebraToPgf.FORMAT_CONTEXT) { codeBeginDoc.insert(0, "\\definecolor[" + colorname + "][r=" + format(red / 255d) + ",g=" + format(green / 255d) + ",b=" + format(blue / 255d) + "]\n"); customColor.put(c0, colorname); } } sb.append(colorname); } } /** * Export Implicit plot for polynom degree greater than 2 */ @Override protected void drawImplicitPoly(GeoImplicit geo) { code.append( "\n%WARNING: PGF/Tikz and Gnuplot don't support implicit curves\n"); code.append("%Rather try PSTricks export\n"); code.append("%Cannot draw "); code.append(getImplicitExpr(geo)); code.append("\n\n"); } @Override protected void drawPolyLine(GeoPolyLine geo) { GeoPointND[] path = geo.getPoints(); if (path.length < 2) { return; } startBeamer(code); StringBuilder str = new StringBuilder(); str.append("\\draw "); String s = lineOptionCode(geo, true); if (s.length() != 0) { s = "[" + s + "] "; } str.append(s); for (int i = 0; i < path.length; i++) { Coords coords = path[i].getInhomCoords(); double x1 = coords.getX(); double y1 = coords.getY(); writePoint(x1, y1, str); if (i != path.length - 1) { str.append("-- "); } } str.append(";\n"); String s1 = str.toString(); s1 = s1.replaceAll("-- \\(\\?,\\?\\)--", ";\n \\\\draw " + s); code.append(s1); endBeamer(code); } @Override protected StringTemplate getStringTemplate() { return StringTemplate.get(StringType.PGF); } private String handleAxesStyle() { String styleAx = ""; if ((euclidianView.getAxesLineStyle() & EuclidianStyleConstants.AXES_RIGHT_ARROW) == EuclidianStyleConstants.AXES_RIGHT_ARROW) { styleAx = "->,"; } if ((euclidianView.getAxesLineStyle() & EuclidianStyleConstants.AXES_LEFT_ARROW) == EuclidianStyleConstants.AXES_LEFT_ARROW) { styleAx = "<" + styleAx; } if ((euclidianView.getAxesLineStyle() & EuclidianStyleConstants.AXES_BOLD) == EuclidianStyleConstants.AXES_BOLD) { styleAx += "ultra thick,"; } return styleAx; } /* * @Override protected GGraphics2D createGraphics(FunctionalNVar ef, * Inequality inequality, EuclidianView euclidianView2){ return new * MyGraphicsPgf(ef, inequality, euclidianView2); } */ @Override protected void drawHistogramOrBarChartBox(double[] y, double[] x, int length, double width, GeoNumeric g) { startBeamer(codeFilledObject); String command = g.getDefinition(StringTemplate.noLocalDefault); if (command.contains("Binomial") && command.contains("true")) { codeFilledObject.append("\\draw"); String s = lineOptionCode(g, true); if (s.length() != 0) { codeFilledObject.append("[" + s + "] "); } writePoint(x[0] + width / 2, 0, codeFilledObject); codeFilledObject.append(" -- "); writePoint(x[0] + width / 2, y[0], codeFilledObject); codeFilledObject.append(";\n"); for (int i = 0; i < length - 1; i++) { codeFilledObject.append("\\draw"); s = lineOptionCode(g, true); if (s.length() != 0) { codeFilledObject.append("[" + s + "] "); } writePoint(x[i] + width / 2, y[i], codeFilledObject); codeFilledObject.append(" -- "); writePoint(x[i + 1] + width / 2, y[i], codeFilledObject); codeFilledObject.append(";\n"); codeFilledObject.append("\\draw"); s = lineOptionCode(g, true); if (s.length() != 0) { codeFilledObject.append("[" + s + "] "); } writePoint(x[i + 1] + width / 2, y[i], codeFilledObject); codeFilledObject.append(" -- "); writePoint(x[i + 1] + width / 2, y[i + 1], codeFilledObject); codeFilledObject.append(";\n"); } } else { for (int i = 0; i < length; i++) { barNumber = i + 1; codeFilledObject.append("\\draw"); String s = lineOptionCode(g, true); if (s.length() != 0) { codeFilledObject.append("[" + s + "] "); } writePoint(x[i], 0, codeFilledObject); codeFilledObject.append(" rectangle "); if (x.length == length) { writePoint(x[i] + width, y[i], codeFilledObject); } else { writePoint(x[i + 1], y[i], codeFilledObject); } codeFilledObject.append(";\n"); } } endBeamer(codeFilledObject); } @Override protected void drawNyquist(GeoTransferFunction g) { String la = "<-"; String ra = "->"; String s = lineOptionCode(g, true); if (s.length() != 0) { la = ",<-"; ra = ",->"; } String template = "\\draw[" + s + Unicode.SECTION_SIGN + "arrows" + Unicode.SECTION_SIGN + "] (%0,%1) -- (%2,%3);\n"; StringBuilder lineBuilder = drawNyquistDiagram(g, template, Unicode.SECTION_SIGN + "arrows" + Unicode.SECTION_SIGN, la, ra); code.append(lineBuilder.toString() + ";\n"); } @Override protected boolean fillSpline(GeoCurveCartesian[] curves) { String liopco = lineOptionCode(curves[0], true); if (!liopco.contains("fill")) { return false; } StringBuilder fill = new StringBuilder(); liopco = "[" + liopco + "]"; String template = "\\pgflineto{\\pgfxy(%0,%1)}\n"; double p = curves[0].getMinParameter(); double y = curves[0].getFunY().value(curves[0].getMinParameter()); double yprec = y; if (Math.abs(y) < 0.001) { y = yprec = 0; } double xprec = curves[0].getFunX() .value(curves[0].getMinParameter()); double x = xprec; fill.append("\\pgfmoveto{\\pgfxy(" + x + "," + y + ")}"); for (int i = 0; i < curves.length; i++) { p = curves[i].getMinParameter(); y = curves[i].getFunY().value(curves[i].getMinParameter()); yprec = y; if (Math.abs(y) < 0.001) { y = yprec = 0; } double step = (curves[i].getMaxParameter() - curves[i].getMinParameter()) / 200; xprec = curves[i].getFunX().value(curves[i].getMinParameter()); x = xprec; for (; p <= curves[i].getMaxParameter(); p += step) { y = curves[i].getFunY().value(p); x = curves[i].getFunX().value(p); if (Math.abs(y) < 0.001) { y = 0; } if (Math.abs(x) < 0.001) { x = 0; } fill.append(StringUtil.format(template, xprec, yprec, x, y)); yprec = y; xprec = x; } } fill.append("\\draw" + liopco + "(" + x + "," + y + ") circle(0pt);\n"); code.append(fill); return true; } /** * @param s * shape * @param ineq * inequality * @param geo * function * @param ds * view parameters */ public void superFill(GShape s, Inequality ineq, FunctionalNVar geo, double[] ds) { ((GeoElement) geo).setLineType(ineq.getBorder().lineType); switch (ineq.getType()) { default: // do nothing break; case INEQUALITY_CONIC: GeoConicND conic = ineq.getConicBorder(); if (conic.getType() == GeoConicNDConstants.CONIC_ELLIPSE || conic.getType() == GeoConicNDConstants.CONIC_CIRCLE) { ((GeoElement) conic) .setObjColor(((GeoElement) geo).getObjectColor()); conic.setType(GeoConicNDConstants.CONIC_ELLIPSE); ((GeoElement) conic) .setAlphaValue(((GeoElement) geo).getAlphaValue()); ((GeoElement) conic).setHatchingAngle( (int) ((GeoElement) geo).getHatchingAngle()); ((GeoElement) conic).setHatchingDistance( ((GeoElement) geo).getHatchingDistance()); ((GeoElement) conic) .setFillType(((GeoElement) geo).getFillType()); drawGeoConic((GeoConic) conic); break; } case INEQUALITY_PARAMETRIC_Y: case INEQUALITY_PARAMETRIC_X: case INEQUALITY_1VAR_X: case INEQUALITY_1VAR_Y: case INEQUALITY_LINEAR: double[] coords = new double[2]; double zeroY = ds[5] * ds[3]; double zeroX = ds[4] * (-ds[0]); GPathIterator path = s.getPathIterator(null); code.append("\\draw["); code.append(lineOptionCode((GeoElement) geo, true)); code.append("]"); double precX = Integer.MAX_VALUE; double precY = Integer.MAX_VALUE; while (!path.isDone()) { path.currentSegment(coords); if (coords[0] == precX && coords[1] == precY) { code.delete(code.length() - 2, code.length()); code.append(";\n\\draw["); code.append(lineOptionCode((GeoElement) geo, true)); code.append("]"); } else { double x1 = (coords[0] - zeroX) / ds[4]; double y1 = -(coords[1] - zeroY) / ds[5]; if (y1 > ymax) { y1 = ymax; } if (y1 < ymin) { y1 = ymin; } code.append("("); code.append(format(x1)); code.append(","); code.append(format(y1)); code.append(")--"); } precX = coords[0]; precY = coords[1]; path.next(); } int i = code.lastIndexOf(")"); code.delete(i + 1, code.length()); code.append(";\n"); break; } } }