/* Copyright (C) 2014-2016 A.Vontobel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The license is in LICENCE-GPL.txt.
* If not, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
*/
package net.sf.drawj2d.cmd;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.BasicStroke;
import java.awt.Stroke;
import java.awt.Font;
import java.awt.geom.*;
import java.math.BigDecimal;
import java.math.MathContext;
import com.udojava.evalex.Expression;
import java.awt.AlphaComposite;
import java.awt.FontFormatException;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.regex.PatternSyntaxException;
import java.util.Stack;
import java.util.EmptyStackException;
import javax.imageio.ImageIO;
import net.sf.drawj2d.UTF2asciiConverter;
import org.scilab.forge.jlatexmath.TeXConstants;
import org.scilab.forge.jlatexmath.TeXFormula;
import org.scilab.forge.jlatexmath.TeXIcon;
import org.scilab.forge.jlatexmath.Box;
/**
*
* @author av
*/
public class API {
static final int SOLID = 1;
static final int DASHED = 2;
static final int DOTTED = 3;
static final int DASHDOTTED = 4;
private final Graphics2D g;
private Point2D currentpos = new Point2D.Double(0,0);
UTF2asciiConverter UTFconverter = new UTF2asciiConverter();
static final double inch = 25.4; // mm
private final Stack blocks = new Stack();
// Strichstärke
private final float stdstroke = 0.50f; //mm
float[] dashed = {2f, 1.5f};
float[] dotted = {0f, 1.5f};
float[] dashdotted = {2f, 1.5f, 0f, 1.5f};
private float currentstrokesize = stdstroke;
private int currentlinetype = SOLID;
// Schrift
private final int schriftgrStd = 4; // Standartschriftgrösse mm
private int schriftgr = schriftgrStd;
private final String fontStd = "Serif"; // Standartschrift
private final String texfont = "/org/scilab/forge/jlatexmath/fonts/latin/jlm_cmr10.ttf";
private Font jarfont;
private String font = fontStd;
private double lastavailabletextwidth = -1;
private boolean lastavailabletextjustified = false;
private final double bzuh = 0.61; // Breite zur Höhe eines Buchstabens
boolean horizFontOnly = false; // if backend (e.g. jtikz) does not support getFontMetrics or rotated fontt
private final int digitsStd = 3;
private int digits = digitsStd; // Stellen nach Komma
private int angledigitsStd = 2;
private int angledigits = angledigitsStd;
private final int forcedigitsStd = 1;
private int forcedigits = forcedigitsStd; // Stellen nach Komma
/** Unit length, im mm */
private double u = 1;
/** Natural unit, mesured in mm .
* required for image (otherwise image scale 1:100 can not be turned in natural units)*/
private double naturalunit = 1;
/** Force unit length, im mm */
private double fu = 1;
private final boolean debug = false;
public API(Graphics2D g) {
this.g = g;
pen();
font();
// Test backend capabilities
try {
g.getFontMetrics().getStringBounds("Test", g);
} catch (Exception e) { // Can happen if backend (e.g. jtikz) does not support getFontMetrics
horizFontOnly = true;
if (debug) System.out.println("Warning: Backend has limited font capabilities.");
}
}
/**
* Set the line and font color.
* @param color
*/
public void pen(Color color) {
g.setColor(color);
}
/**
* Set the line stroke width.
* @param size
*/
public void pen(float size) {
currentstrokesize = size;
pen(currentlinetype);
}
/**
* Set the line type
* @param linetype DASHED, DOTTED, DASHDOTTED, SOLID
*/
public void pen(int linetype) {
boolean solidline;
float[] dash = null; // "on, off, on, off"
switch (linetype) {
case DASHED:
dash = dashed;
solidline = false;
break;
case DOTTED:
dash = dotted;
solidline = false;
break;
case DASHDOTTED:
dash = dashdotted;
solidline = false;
break;
case SOLID:
solidline = true;
break;
default:
assert false;
solidline = true;
}
final int cap = BasicStroke.CAP_ROUND;
final int join = BasicStroke.JOIN_ROUND;
final float miterlimit = 10f; // = new BasicStroke().getMiterLimit();
final float dashphase = 0f;
BasicStroke stroke;
if (solidline) {
stroke = new BasicStroke(currentstrokesize, cap, join);
} else {
stroke = new BasicStroke(currentstrokesize, cap, join, miterlimit, dash, dashphase);
}
g.setStroke(stroke);
currentlinetype = linetype;
}
/**
* Reset the pen to black solid line with standard stroke with.
*/
public final void pen() {
g.setColor(Color.BLACK);
g.setStroke(new BasicStroke(stdstroke, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
currentstrokesize = stdstroke;
currentlinetype = SOLID;
}
/**
* Set the opacity.
* @param opacity 1.0f is opaque.
*/
public final void opacity(float opacity) {
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity));
}
/*
* Set the font.
* @param font Serif, Sans_Serif, Monospaced, font name. If "" keep the current font.
* @param style font style: Font.PLAIN, Font.BOLD or Font.ITALIC
* @param size font height in mm. If 0 keep the current size.
*/
public void font(String font, int style, int size) throws FontFormatException, IOException {
// set current setting for missing arguments
if (size == 0) size = schriftgr;
if (font.equals("")) font = this.font;
if (font.equalsIgnoreCase("tex")) {
jarfont = Font.createFont(Font.TRUETYPE_FONT, this.getClass().getResourceAsStream(texfont)).deriveFont(style, size);
g.setFont(jarfont);
}
else g.setFont(new java.awt.Font(font, style, size));
this.font = font;
schriftgr = size;
if (debug) {
if (debug) System.out.println("font " + font);
Font f = g.getFont();
System.out.println("getFamily " + f.getFamily());
System.out.println("getFontName " + f.getFontName());
System.out.println("getName " + f.getName());
}
}
public final void font() {
g.setFont(new java.awt.Font(fontStd, Font.PLAIN, schriftgrStd));
font = fontStd;
schriftgr = schriftgrStd;
}
/** Set the scale.
* The natural unit is assumed to be [mm] (has impact on image import).
* @param mm The length on the sheet for one drawing unit.
*/
public void unitlength(double mm) {
u = mm;
naturalunit = 1;
}
/** Set the scale and unit. Tne unit length on sheet is the product of both.
* @param scale The length on the sheet for one drawing unit.
* @param unit natural unit mesured in mm. E.g. for [m] set 1000 [mm]
*/
public void unitlength(double scale, double unit) {
u = scale * unit;
naturalunit = unit;
}
/**
* mm to unit length conversion.
* @param mm in mm
* @return length in equivalent unit size
*/
public double mm(double mm) {
return mm/u;
}
/**
* Force unit length
* @param mm length of force unit. Usually kN.
* @param decdigits number of decimal decdigits (Kommastellen)
*/
public void forceunitlength(double mm, int decdigits) {
fu = mm;
forcedigits = decdigits;
}
/**
* force unit to unit length conversion.
* @param kN in force unit. Usually kN.
* @return length in equivalent unit size
*/
public double fu(double kN) {
return kN*fu/u;
}
/**
* Evaluate a mathematical expression.
* @param str expression
* @return double value
*/
public double expr(String str) {
// use 17 digits precision (double precision uses 16 digits)
MathContext mc = new MathContext(17);
Expression expression = new Expression(str, mc);
BigDecimal result = expression.eval();
double val = result.doubleValue();
return val;
}
/** Offset the origin (0/0) coordinate.
* Offset is relative to previous origin position.
* @param dX in drawing units
* @param dY in drawing units
*/
public void offset(double dX, double dY) {
g.translate(dX*u, dY*u);
}
/** Rotate the coordinate axes. Use allowed only within blocks.
* Rotation has its center at the origin (0/0).
* The coursor remains, thus the coordinates of current position do change.
* @param α in degrees
* @return false if called from outside a block
*/
public boolean rotate(double α) {
if (blocks.size() < 1) {
return false;
}
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double αrad = Math.toRadians(α);
double cos = Math.cos(αrad);
double sin = Math.sin(αrad);
g.rotate(αrad);
double x1 = x0 * cos + y0 * sin;
double y1 = -x0 * sin + y0 * cos;
currentpos.setLocation(x1, y1);
return true;
}
/** Mirror vertically. Use allowed only within blocks.
* The y-axis points upwards. Labels will also be mirrored.
* The coursor remains, thus the coordinates of current position do change.
* @return false if called from outside a block
*/
public boolean flip() {
if (blocks.size() < 1) {
return false;
}
double x0 = currentpos.getX();
double y0 = currentpos.getY();
g.scale(1, -1);
currentpos.setLocation(x0, -y0);
return true;
}
/** Scale the axes. Use allowed only within blocks.
* The coursor remains, thus the coordinates of current position do change.
* @param sx scale factor for x-axis
* @param sy scale factor for y-axis
* @return false if called from outside a block
*/
public boolean scale(double sx, double sy) {
if (blocks.size() < 1) {
return false;
}
double x0 = currentpos.getX();
double y0 = currentpos.getY();
g.scale(sx, sy);
currentpos.setLocation(x0 / sx, y0 / sy);
return true;
}
/** Create a block.
* Puts the current AffineTransform and the current position onto a stack.
* The block has its own coordinate space, (0,0) at the current position.
* @return new size of the block stack
*/
public int block() {
clBlock bl = new clBlock(g.getTransform(), currentpos);
blocks.push(bl);
offset(currentpos.getX(), currentpos.getY());
currentpos.setLocation(0, 0);
return blocks.size();
}
/** End a block.
* Resets the AffineTransform and the current position.
* Takes both from the block stack.
* @return remaining size of the block stack. -1 if stack was empty!
*/
public int endblock() {
clBlock bl;
try {
bl = blocks.pop();
} catch (EmptyStackException e) {
return -1;
}
AffineTransform savedAT = bl.getAffineTransform();
g.setTransform(savedAT);
currentpos.setLocation(bl.getPos());
return blocks.size();
}
/** Move the cursor to a new position.
*
* @param x1
* @param y1
*/
public void moveto(double x1, double y1) {
currentpos.setLocation(x1, y1);
}
/** Move the cursor to a new position.
*
* @param xy array {x1, xy}
*/
public void moveto(double[] xy) {
currentpos.setLocation(xy[0], xy[1]);
}
/**
* Move the cursor to a new x-coordinate while keeping the y-coordinate.
*
* @param x1
*/
public void movetox(double x1) {
currentpos.setLocation(x1, currentpos.getY());
}
/**
* Move the cursor to a new y-coordinate while keeping the x-coordinate.
*
* @param y1
*/
public void movetoy(double y1) {
currentpos.setLocation(currentpos.getX(), y1);
}
/** Tells where the cursor is.
* @return the current position of the cursor */
public double[] here() {
double[] here = new double[2];
here[0] = currentpos.getX();
here[1] = currentpos.getY();
return here;
}
/**
* Move the cursor relative to the actual position.
*
* @param dx
* @param dy
*/
public void moverel(double dx, double dy) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
currentpos.setLocation(x0 + dx, y0 + dy);
}
/**
* Move the cursor in polar direction relative to the actual position.
*
* @param dL
* @param α direction from actual position, in degrees, clock-wise
*/
public void movepolar(double dL, double α) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double dx = dL * Math.cos(Math.toRadians(α));
double dy = dL * Math.sin(Math.toRadians(α));
currentpos.setLocation(x0 + dx, y0 + dy);
}
/**
* Draw a line from the actual cursor position to the given coordinate.
* Sets the cursor to the given coordinate.
* @param x1
* @param y1
*/
public void lineto(double x1, double y1) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
g.draw(new Line2D.Double(x0*u, y0*u, x1*u, y1*u));
currentpos.setLocation(x1, y1);
}
/**
* Draw a line from the actual cursor position in x-direction.
* New x-coordinate while keeping the y-coordinate.
* @param x1
*/
public void linetox(double x1) {
lineto(x1, currentpos.getY());
}
/**
* Draw a line from the actual cursor position in y-direction.
* New y-coordinate while keeping the x-coordinate.
* @param y1
*/
public void linetoy(double y1) {
lineto(currentpos.getX(), y1);
}
/**
* Draw a line from the first coordinate to the second one.
* Sets the cursor to the end of the line (second coordinate x1/y1).
* @param x0
* @param y0
* @param x1
* @param y1
*/
public void line(double x0, double y0, double x1, double y1) {
g.draw(new Line2D.Double(x0*u, y0*u, x1*u, y1*u));
currentpos.setLocation(x1, y1);
}
/**
* Draw a line from the actual cursor position to the given relative position.
* Sets the cursor to the end of the line.
* @param dx
* @param dy
*/
public void linerel(double dx, double dy) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double x1 = x0 + dx;
double y1 = y0 + dy;
g.draw(new Line2D.Double(x0*u, y0*u, x1*u, y1*u));
currentpos.setLocation(x1, y1);
}
/**
* Draw a line given its length and direction relative to the actual position.
* Sets the cursor to the end of the line.
*
* @param dL
* @param α direction from actual position, in degrees, clock-wise
*/
public void linepolar(double dL, double α) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double x1 = x0 + dL * Math.cos(Math.toRadians(α));
double y1 = y0 + dL * Math.sin(Math.toRadians(α));
g.draw(new Line2D.Double(x0*u, y0*u, x1*u, y1*u));
currentpos.setLocation(x1, y1);
}
/**
* Draw a line given its length and direction.
* The middle of the line is set at the cursor position. The cursor does not move.
* @param L
* @param α
*/
public void linemid(double L, double α) {
double dx = L/2d * Math.cos(Math.toRadians(α));
double dy = L/2d * Math.sin(Math.toRadians(α));
double x0 = currentpos.getX() - dx;
double y0 = currentpos.getY() - dy;
double x1 = currentpos.getX() + dx;
double y1 = currentpos.getY() + dy;
g.draw(new Line2D.Double(x0*u, y0*u, x1*u, y1*u));
// currentpos remains.
}
/**
* Mark a point.
*/
public void point() {
double durchmesser = 1.4*currentstrokesize;
g.fill(kreis(currentpos, durchmesser));
}
/**
* Mark a point at the given coordinate.
* Sets the cursor to the point.
* @param x1
* @param y1
*/
public void point(double x1, double y1) {
moveto(x1, y1);
point();
}
/**
* Draw an arrow from the actual cursor position to the given coordinate.
* Sets the cursor to the given coordinate.
* @param x1
* @param y1
*/
public void arrowto(double x1, double y1) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
pfeil(x0,y0,x1,y1);
currentpos.setLocation(x1, y1);
}
/**
* Draw an arrow from the actual cursor position to the given relative position.
* Sets the cursor to the arrow head.
* @param dx
* @param dy
*/
public void arrowrel(double dx, double dy) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double x1 = x0 + dx;
double y1 = y0 + dy;
pfeil(x0,y0,x1,y1);
currentpos.setLocation(x1, y1);
}
/**
* Draw an arrow from the first coordinate to the second one.
* Sets the cursor to the arrow head (second coordinate x1/y1).
* @param x0
* @param y0
* @param x1
* @param y1
*/
public void arrow(double x0, double y0, double x1, double y1) {
pfeil(x0,y0,x1,y1);
currentpos.setLocation(x1, y1);
}
/**
* Draw a double headed arrow from the actual cursor position to the given coordinate.
* Sets the cursor to the given coordinate.
* @param x1
* @param y1
*/
public void arrowsto(double x1, double y1) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
doppelpfeil(x0,y0,x1,y1, false);
currentpos.setLocation(x1, y1);
}
/**
* Draw a double headed arrow from the actual cursor position to the given relative position.
* Sets the cursor to the new position.
* @param dx
* @param dy
*/
public void arrowsrel(double dx, double dy) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double x1 = x0 + dx;
double y1 = y0 + dy;
doppelpfeil(x0,y0,x1,y1, false);
currentpos.setLocation(x1, y1);
}
/**
* Draw a double headed arrow from the first coordinate to the second one.
* Sets the cursor to the second coordinate x1/y1.
* @param x0
* @param y0
* @param x1
* @param y1
*/
public void arrows(double x0, double y0, double x1, double y1) {
doppelpfeil(x0,y0,x1,y1, false);
currentpos.setLocation(x1, y1);
}
/**
* Draw a force arrow (pointing away).
* The label is its absolute value.
* Sets the cursor to the arrow head.
* @param Fx
* @param Fy
* @param isTeX
* @see forceunitlength
*/
public void force(double Fx, double Fy, boolean isTeX) {
double F = Math.hypot(Fx, Fy);
String lb = Fkt.nf(F, forcedigits);
force(Fx, Fy, lb, isTeX);
}
/**
* Draw a force arrow (pointing away).
* Sets the cursor to the arrow head.
* @param Fx
* @param Fy
* @param lb
* @param isTeX
* @see forceunitlength
*/
public void force(double Fx, double Fy, String lb, boolean isTeX) {
double F = Math.hypot(Fx, Fy);
if (F > 0) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double x1 = x0 + Fx * fu / u;
double y1 = y0 + Fy * fu / u;
pfeil(x0,y0,x1,y1);
if (isTeX) texlabelline(x0,y0,x1,y1, lb);
else labelline(x0,y0,x1,y1, lb);
currentpos.setLocation(x1, y1);
}
}
/**
* Draw a force arrow (pointing to the cursor).
* The label is its absolute value.
* @param Fx
* @param Fy
* @param isTeX
* @see forceunitlength
*/
public void force2(double Fx, double Fy, boolean isTeX) {
double F = Math.hypot(Fx, Fy);
String lb = Fkt.nf(F, forcedigits);
force2(Fx, Fy, lb, isTeX);
}
/**
* Draw a force arrow (pointing to the cursor).
* @param Fx
* @param Fy
* @param lb
* @param isTeX
* @see forceunitlength
*/
public void force2(double Fx, double Fy, String lb, boolean isTeX) {
double F = Math.hypot(Fx, Fy);
if (F > 0) {
double x1 = currentpos.getX();
double y1 = currentpos.getY();
double x0 = x1 - Fx * fu / u;
double y0 = y1 - Fy * fu / u;
pfeil(x0, y0, x1,y1);
if (isTeX) texlabelline(x0,y0,x1,y1, lb);
else labelline(x0,y0,x1,y1, lb);
}
}
/**
* Draw a circle.
* @param radius
*/
public void circle(double radius) {
g.draw(kreis(currentpos, 2.0*radius*u));
}
/**
* Draw a circle.
* Sets the cursor to the center.
* @param x1
* @param y1
* @param radius
*/
public void circle(double x1, double y1, double radius) {
currentpos.setLocation(x1, y1);
g.draw(kreis(currentpos, 2.0*radius*u));
}
/**
* Draw an ellipse.
* @param radius1
* @param radius2
* @param angle1 Angle of direction 1 to x-axis
*/
public void ellipse(double radius1, double radius2, double angle1) {
double centerx = currentpos.getX() * u;
double centery = currentpos.getY() * u;
radius1 *= u;
radius2 *= u;
Ellipse2D.Double ellip = new Ellipse2D.Double(centerx - radius1, centery - radius2,
2d*radius1, 2d*radius2);
AffineTransform rotiert;
double alphaRot = Math.toRadians(angle1); // Rotationswinkel
// Rotieren und schieben
rotiert = AffineTransform.getRotateInstance(alphaRot, centerx, centery);
Graphics2D tempGraphics = (Graphics2D) g.create();
tempGraphics.setStroke(g.getStroke());
tempGraphics.transform(rotiert);
tempGraphics.draw(ellip);
// Rotation und Verschiebung zurückstellen
tempGraphics.dispose();
}
/**
* Fill an ellipse.
* @param radius1
* @param radius2
* @param angle1 Angle of direction 1 to x-axis
*/
public void fillellipse(double radius1, double radius2, double angle1) {
double centerx = currentpos.getX() * u;
double centery = currentpos.getY() * u;
radius1 *= u;
radius2 *= u;
Ellipse2D.Double ellip = new Ellipse2D.Double(centerx - radius1, centery - radius2,
2d*radius1, 2d*radius2);
AffineTransform rotiert;
double alphaRot = Math.toRadians(angle1); // Rotationswinkel
// Rotieren und schieben
rotiert = AffineTransform.getRotateInstance(alphaRot, centerx, centery);
Graphics2D tempGraphics = (Graphics2D) g.create();
tempGraphics.setStroke(g.getStroke());
tempGraphics.transform(rotiert);
tempGraphics.fill(ellip);
// Rotation und Verschiebung zurückstellen
tempGraphics.dispose();
}
/**
* Draw an arc.
* Zero angle is where the x-axis points to. Clock-wise, consistent with coord sys.
* @param radius
* @param startangle in degrees, clock-wise
* @param endangle in degrees, clock-wise
*/
public void arc(double radius, double startangle, double endangle) {
double centerx = currentpos.getX() * u;
double centery = currentpos.getY() * u;
radius *= u;
double extent = endangle - startangle;
if (extent < 0) extent += 360;
Rectangle2D.Double quadr = new Rectangle2D.Double(centerx - radius, centery - radius,
2d * radius, 2d * radius);
Arc2D.Double bogen = new Arc2D.Double(quadr, -endangle, extent, Arc2D.OPEN); // java2D works counter-clock-wise
g.draw(bogen);
}
/**
* Draw an arc.
* Zero angle is where the x-axis points to. Clock-wise, consistent with coord sys.
* Sets the cursor to the center.
* @param x1 center
* @param y1 center
* @param radius
* @param startangle in degrees, clock-wise
* @param endangle in degrees, clock-wise
*/
public void arc(double x1, double y1, double radius, double startangle, double endangle) {
currentpos.setLocation(x1, y1);
arc(radius, startangle, endangle);
}
/**
* Fill a segment (arc).
* Zero angle is where the x-axis points to. Clock-wise, consistent with coord sys.
* @param radius
* @param startangle in degrees, clock-wise
* @param endangle in degrees, clock-wise
*/
public void fillsegment(double radius, double startangle, double endangle) {
double centerx = currentpos.getX() * u;
double centery = currentpos.getY() * u;
radius *= u;
double extent = endangle - startangle;
if (extent < 0) extent += 360;
Rectangle2D.Double quadr = new Rectangle2D.Double(centerx - radius, centery - radius,
2d * radius, 2d * radius);
Arc2D.Double bogen = new Arc2D.Double(quadr, -endangle, extent, Arc2D.OPEN); // java2D works counter-clock-wise
g.fill(bogen);
}
/**
* Draw a parabola from the first coordinate to the third one.
* The second is the control point (intersection of tangets).
* Sets the cursor to the end of the line (second coordinate x1/y1).
* @param x0
* @param y0
* @param xCtrl Control point (intersection of tangents)
* @param yCtrl Control point (intersection of tangents)
* @param x1
* @param y1
*/
public void quadcurve(double x0, double y0, double xCtrl, double yCtrl, double x1, double y1) {
g.draw(new QuadCurve2D.Double(x0*u, y0*u, xCtrl*u, yCtrl*u, x1*u, y1*u));
currentpos.setLocation(x1, y1);
}
/**
* Draw a cubic parabola from the first coordinate to the last one.
* The second and third are the control points (on the tangets).
* Sets the cursor to the end of the line (second coordinate x1/y1).
* @param x0
* @param y0
* @param x0Ctrl Control point (on first tangent)
* @param y0Ctrl Control point (on first tangent)
* @param x1Ctrl Control point (on second tangent)
* @param y1Ctrl Control point (on second tangent)
* @param x1
* @param y1
*/
public void cubiccurve(double x0, double y0, double x0Ctrl, double y0Ctrl, double x1Ctrl, double y1Ctrl, double x1, double y1) {
g.draw(new CubicCurve2D.Double(x0*u, y0*u, x0Ctrl*u, y0Ctrl*u, x1Ctrl*u, y1Ctrl*u, x1*u, y1*u));
currentpos.setLocation(x1, y1);
}
/**
* Draw a rectangle.
* @param dx
* @param dy
*/
public void rect(double dx, double dy) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double X0 = Math.min(x0, x0+dx);
double Y0 = Math.min(y0, y0+dy);
double dX = Math.abs(dx);
double dY = Math.abs(dy);
g.draw(new java.awt.Rectangle.Double(X0*u, Y0*u, dX*u, dY*u));
}
/**
* Fill a rectangle.
* @param dx
* @param dy
*/
public void fillrect(double dx, double dy) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double X0 = Math.min(x0, x0+dx);
double Y0 = Math.min(y0, y0+dy);
double dX = Math.abs(dx);
double dY = Math.abs(dy);
g.fill(new java.awt.Rectangle.Double(X0*u, Y0*u, dX*u, dY*u));
}
/**
* Draw a rod.
* Actually a rectangle defined by its axis (starting point and orientation), length and width.
* Sets the cursor to the end point on the axis, L from the starting point.
* @param L length
* @param w width
* @param α orientation, in degrees, clock-wise
*/
public void rod(double L, double w, double α) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double cos = Math.cos(Math.toRadians(α));
double sin = Math.sin(Math.toRadians(α));
double x1 = x0 + L * cos;
double y1 = y0 + L * sin;
double dx = w/2d * sin;
double dy = -w/2d * cos;
Path2D path = new Path2D.Double();
path.moveTo((x0-dx)*u, (y0-dy)*u);
path.lineTo((x0+dx)*u, (y0+dy)*u);
path.lineTo((x1+dx)*u, (y1+dy)*u);
path.lineTo((x1-dx)*u, (y1-dy)*u);
path.closePath();
g.draw(path);
currentpos.setLocation(x1, y1);
}
/**
* Fill a rod.
* Actually a rectangle defined by its axis (starting point and orientation), length and width.
* Sets the cursor to the end point on the axis, L from the starting point.
* @param L length
* @param w width
* @param α orientation, in degrees, clock-wise
*/
public void fillrod(double L, double w, double α) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double cos = Math.cos(Math.toRadians(α));
double sin = Math.sin(Math.toRadians(α));
double x1 = x0 + L * cos;
double y1 = y0 + L * sin;
double dx = w/2d * sin;
double dy = -w/2d * cos;
Path2D path = new Path2D.Double();
path.moveTo((x0-dx)*u, (y0-dy)*u);
path.lineTo((x0+dx)*u, (y0+dy)*u);
path.lineTo((x1+dx)*u, (y1+dy)*u);
path.lineTo((x1-dx)*u, (y1-dy)*u);
path.closePath();
g.fill(path);
currentpos.setLocation(x1, y1);
}
/**
* Draw a polygon.
* Sets the cursor to the first vertex x0/y0.
* polygon A-B-C-D is equivalent to lines A-B-C-D-A
* @param x x coordinates of polygon vertices
* @param y y coordinates of polygon vertices
*/
public void polygon(double[] x, double[] y) {
assert x.length == y.length;
assert x.length > 2;
currentpos.setLocation(x[0], y[0]);
Path2D path = new Path2D.Double();
path.moveTo(x[0]*u, y[0]*u);
for(int i = 1; i < x.length; ++i) {
path.lineTo(x[i]*u, y[i]*u);
}
path.closePath();
g.draw(path);
}
/**
* Fill a polygon.
* Sets the cursor to the first vertex x0/y0.
* polygon A-B-C-D is equivalent to lines A-B-C-D-A
* @param x x coordinates of polygon vertices
* @param y y coordinates of polygon vertices
*/
public void fillpolygon(double[] x, double[] y) {
assert x.length == y.length;
assert x.length > 2;
currentpos.setLocation(x[0], y[0]);
Path2D path = new Path2D.Double();
path.moveTo(x[0]*u, y[0]*u);
for(int i = 1; i < x.length; ++i) {
path.lineTo(x[i]*u, y[i]*u);
}
path.closePath();
g.fill(path);
}
/**
* Fill a circle.
* @param radius
*/
public void fillcircle(double radius) {
g.fill(kreis(currentpos, 2.0*radius*u));
}
/**
* Label the position of the cursor.
* The text is placed at the upper right (NE).
* @param label text
*/
public void label(String label) {
label(label, "NE");
}
/**
* Label the position of the cursor.
* The placement of the text is defined by relpos.
* @param label text
* @param relpos N, NW, W, SW, S, SE, E, NE, C, BL
*/
public void label(String label, String relpos) {
double xpix = currentpos.getX()*u;
double ypix = currentpos.getY()*u;
double w;
double h;
// Mavscript-yacas replaces unicode chars (e.g. ω, but every char above 127)
// by a ascii hexadecimal representation (e.g. escu03c9)
// We want to reinsert the original unicode char.
if (UTFconverter.containsUnicodeHexCharacters(label)) {
if (debug) System.out.println("ConvertFrom " + label);
label = UTFconverter.convert2UTF(label);
if (debug) System.out.println("ConvertFrom " + label);
}
if (horizFontOnly) {
w = label.length()*bzuh*schriftgr;
h = schriftgr;
}
else {
Rectangle2D fr = g.getFontMetrics().getStringBounds(label, g);
w = fr.getWidth();
h = fr.getHeight();
}
if (relpos.equals("N")) {xpix -= w/2d; ypix -= h/3d;}
else if (relpos.equals("NW")) {xpix -= w+h/4d; ypix -= h/3d;}
else if (relpos.equals("W")) {xpix -= w+h/4d; ypix += h/3d;}
else if (relpos.equals("SW")) {xpix -= w+h/4d; ypix += h;}
else if (relpos.equals("S")) {xpix -= w/2d; ypix += h;}
else if (relpos.equals("SE")) {xpix += h/4d; ypix += h;}
else if (relpos.equals("E")) {xpix += h/4d; ypix += h/3d;}
else if (relpos.equals("NE")) {xpix += h/4d; ypix -= h/3d;} // default
else if (relpos.equals("C")) {xpix -= w/2d; ypix += h/3d;}
else if (relpos.equals("BL")) {} // baseline
else {xpix += h/4d; ypix -= h/3d;} // default
g.drawString(label, (float) xpix, (float) ypix);
}
/**
* Label the position of the cursor using TeX input.
* The text is placed at the upper right (NE).
* @param latex text
*/
public void texlabel(String latex) {
texlabel(latex, "NE");
}
/**
* Label the position of the cursor using TeX input.
* The placement of the text is defined by relpos.
* @param latex text
* @param relpos N, NW, W, SW, S, SE, E, NE, C, BL
*/
public void texlabel(String latex, String relpos) {
double cx = currentpos.getX(); double cy = currentpos.getY();
double xpix = currentpos.getX()*u;
double ypix = currentpos.getY()*u;
double w;
double h;
// Mavscript-yacas replaces unicode chars (e.g. ω, but every char above 127)
// by a ascii hexadecimal representation (e.g. escu03c9)
// We want to reinsert the original unicode char.
if (UTFconverter.containsUnicodeHexCharacters(latex)) {
if (debug) System.out.println("ConvertFrom " + latex);
latex = UTFconverter.convert2UTF(latex);
if (debug) System.out.println("ConvertFrom " + latex);
}
TeXFormula formula = new TeXFormula(latex);
formula.setColor(g.getColor());
//formula.setDEBUG(true);
TeXIcon icon = formula.createTeXIcon(TeXConstants.STYLE_TEXT, schriftgr, true);
// metrics
w = icon.getTrueIconWidth();
h = icon.getTrueIconHeight();
// minimal font height
TeXIcon tempicon = (new TeXFormula("b")).createTeXIcon(TeXConstants.STYLE_TEXT, schriftgr, true);
double hmin = tempicon.getTrueIconHeight();
if (h < hmin) h = hmin;
if (relpos.equals("N")) {xpix -= w/2d; ypix -= h/3d;}
else if (relpos.equals("NW")) {xpix -= w+h/4d; ypix -= h/3d;}
else if (relpos.equals("W")) {xpix -= w+h/4d; ypix += h/3d;}
else if (relpos.equals("SW")) {xpix -= w+h/4d; ypix += h;}
else if (relpos.equals("S")) {xpix -= w/2d; ypix += h;}
else if (relpos.equals("SE")) {xpix += h/4d; ypix += h;}
else if (relpos.equals("E")) {xpix += h/4d; ypix += h/3d;}
else if (relpos.equals("NE")) {xpix += h/4d; ypix -= h/3d;} // default
else if (relpos.equals("C")) {xpix -= w/2d; ypix += h/2d;}
else if (relpos.equals("BL")) {} // baseline
else {xpix += h/4d; ypix -= h/3d;} // default
Graphics2D tempGraphics = (Graphics2D) g.create();
tempGraphics.translate(xpix, ypix); // schieben
// new settings
tempGraphics.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING,
java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
tempGraphics.setRenderingHint(java.awt.RenderingHints.KEY_RENDERING,
java.awt.RenderingHints.VALUE_RENDER_QUALITY);
tempGraphics.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
tempGraphics.scale(schriftgr, schriftgr); // the point size
// draw formula box
Box box = icon.getBox();
box.draw(tempGraphics, 0, 0);
tempGraphics.dispose();
currentpos.setLocation(cx, cy);
}
/**
* Write some text, break lines at previously used width.
* @param text text
*/
public void text(String text) {
text(text, lastavailabletextwidth, lastavailabletextjustified);
}
/**
* Write some text, break lines.
* @param text text
* @param width available width in drawing units
* @param justified whether justified text or left aligned
*/
public void text(String text, double availablewidth, boolean justified) {
if (availablewidth >= 0) {
lastavailabletextwidth = availablewidth;
} else {
if (lastavailabletextwidth >= 0) {
availablewidth = lastavailabletextwidth;
} else { // initial value
availablewidth = mm(150);
}
}
lastavailabletextjustified = justified;
double xpix = currentpos.getX()*u;
double ypix = currentpos.getY()*u;
double w;
double h;
// Mavscript-yacas replaces unicode chars (e.g. ω, but every char above 127)
// by a ascii hexadecimal representation (e.g. escu03c9)
// We want to reinsert the original unicode char.
if (UTFconverter.containsUnicodeHexCharacters(text)) {
if (debug) System.out.println("ConvertFrom " + text);
text = UTFconverter.convert2UTF(text);
if (debug) System.out.println("ConvertFrom " + text);
}
if (horizFontOnly) {
w = text.length()*bzuh*schriftgr;
h = schriftgr;
}
else {
Rectangle2D fr = g.getFontMetrics().getStringBounds(text, g);
w = fr.getWidth();
h = fr.getHeight();
}
if (debug) System.out.println(h);
double hLF = Math.max(h, 1.2*schriftgr); // vertical LineFeed
hLF = Math.ceil(hLF); // ideally between 1.2 and 1.45*schriftgr
if (debug) System.out.println(hLF);
// If text fits into one line
if (w <= availablewidth*u) {
g.drawString(text, (float) xpix, (float) ypix);
currentpos.setLocation(currentpos.getX(), currentpos.getY() + hLF/u);
return;
}
// Else, linebreaks required
double wS = g.getFontMetrics().getStringBounds(" ", g).getWidth();
if (debug) System.out.println(wS);
String[] words;
try {
words = text.split("[ \\t\\u2000-\\u200a\\u205f]"); // breaking horizontal whitespace characters
}
catch (PatternSyntaxException e) {
words = text.split("[ \\t]");
System.err.println(e.getLocalizedMessage());
System.err.println("Work around activated. JRE: " + Runtime.class.getPackage().getSpecificationVersion());
}
if (debug) for (String wi : words) System.out.println(wi);
double[] widths = new double[words.length];
for (int i = 0; i < words.length; i++) {
widths[i] = g.getFontMetrics().getStringBounds(words[i], g).getWidth();
}
if (debug) for (double wi : widths) System.out.println(wi);
int writtenwords = 0;
double usedwith; int i;
while (writtenwords < words.length) {
usedwith = 0;
i = 0;
while (usedwith <= availablewidth*u) {
i++;
if (i > 1) usedwith += wS;
if (writtenwords+i-1 >= words.length) break;
else usedwith += widths[writtenwords+i-1];
}
if (justified && (writtenwords + i-1 < words.length)) { // the last line will be left aligned
if (i == 1 || i == 2) { // one word only at a line, even if it is longer than available width
g.drawString(words[writtenwords], (float) xpix, (float) ypix); // write it anyway
writtenwords += 1;
}
else { // normal case
assert i > 2; // at least two words on a line
double textonlywidth = 0;
for (int j = 0; j < i-1; j++) {
textonlywidth += widths[writtenwords+j];
}
double space = (availablewidth*u - textonlywidth) / (i-2);
double xpixpos = xpix;
for (int j = 0; j < i-1; j++) {
g.drawString(words[writtenwords+j], (float) xpixpos, (float) ypix);
xpixpos += widths[writtenwords+j];
xpixpos += space;
}
writtenwords += (i-1);
}
}
else {// left aligned text
StringBuilder sb = new StringBuilder();
if (i == 1) { // even one word is longer than available width
sb.append(words[writtenwords]); // write it anyway
writtenwords += 1;
}
else { // normal case
for (int j = 0; j < i-1; j++) {
if (j > 0) sb.append(" ");
sb.append(words[writtenwords+j]);
}
writtenwords += (i-1);
}
g.drawString(sb.toString(), (float) xpix, (float) ypix);
}
ypix += hLF;
}
currentpos.setLocation(currentpos.getX(), ypix/u);
}
/**
* Set number format for dimension lines
* @param nb number of decimal digits
*/
public void decdigits(int nb) {
if (nb >= 0) digits = nb;
else digits = digitsStd;
}
/**
* Draw a dimension line (double headed arrow) from the cursor position
* to the given position and label by its length (in drawing units).
* Set cursor to the new position.
* @param x1
* @param y1
* @param isTeX
*/
public void dimlineto(double x1, double y1, boolean isTeX) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
dimline(x0,y0,x1,y1, isTeX);
}
/**
* Draw a dimension line (double headed arrow) from the cursor position
* to the given position and label it.
* Set cursor to the new position.
* @param x1
* @param y1
* @param lb
* @param isTeX
*/
public void dimlineto(double x1, double y1, String lb, boolean isTeX) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
dimline(x0,y0,x1,y1, lb, isTeX);
}
/**
* Draw a dimension line (double headed arrow) from the cursor position
* to a relative position and label by its length (in drawing units).
* Set cursor to the new position.
* @param dx
* @param dy
* @param isTeX
*/
public void dimlinerel(double dx, double dy, boolean isTeX) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double x1 = x0 + dx;
double y1 = y0 + dy;
dimline(x0,y0,x1,y1, isTeX);
}
/**
* Draw a dimension line (double headed arrow) from the cursor position
* to a relative position and label it.
* Set cursor to the new position.
* @param dx
* @param dy
* @param lb
* @param isTeX
*/
public void dimlinerel(double dx, double dy, String lb, boolean isTeX) {
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double x1 = x0 + dx;
double y1 = y0 + dy;
dimline(x0,y0,x1,y1, lb, isTeX);
}
/**
* Draw a dimension line (double headed arrow)
* and label it by its length (in drawing units).
* Sets the cursor to the new position (second coordinate x1/y1).
* @param x0
* @param y0
* @param x1
* @param y1
* @param isTeX
*/
public void dimline(double x0, double y0, double x1, double y1, boolean isTeX) {
Point2D P0 = new Point2D.Double(x0,y0);
Point2D P1 = new Point2D.Double(x1,y1);
double dim = P1.distance(P0);
String dimstr = Fkt.nf(dim, digits);
if (dimstr.contains(".")) {
while (dimstr.endsWith("0")) {
dimstr = dimstr.substring(0, dimstr.length()-1);
}
if (dimstr.endsWith(".")) {
dimstr = dimstr.substring(0, dimstr.length()-1);
}
}
if (debug) System.out.println(dim + " -- " + dimstr);
dimline(x0, y0, x1, y1, dimstr, isTeX);
}
/**
* Draw a dimension line (double headed arrow) and label it.
* Sets the cursor to the new position (second coordinate x1/y1).
* @param x0
* @param y0
* @param x1
* @param y1
* @param lb text
* @param isTeX
*/
public void dimline(double x0, double y0, double x1, double y1, String lb, boolean isTeX) {
doppelpfeil(x0,y0,x1,y1, true);
if (isTeX) texlabelline(x0,y0,x1,y1, lb);
else labelline(x0,y0,x1,y1, lb);
currentpos.setLocation(x1, y1);
}
/**
* Label a line.
* Does not draw a line. Does not move the cursor.
* @param x0
* @param y0
* @param x1
* @param y1
* @param lb
*/
public void labelline(double x0, double y0, double x1, double y1, String lb) {
Point2D PM = mitte(new Point2D.Double(x0,y0), new Point2D.Double(x1,y1));
if (horizFontOnly) {
String pos; // TODO feiner positionieren
if (Math.abs(x1 - x0) < Math.abs(y1 - y0)) pos = "E";
else pos = "N";
double curX = currentpos.getX();
double curY = currentpos.getY();
currentpos.setLocation(PM);
label(lb, pos);
currentpos.setLocation(curX, curY);
} else {
AffineTransform rotiert;
double alphaRot; // Rotationswinkel
double dx, dy;
// Mavscript-yacas replaces unicode chars (e.g. ω, but every char above 127)
// by a ascii hexadecimal representation (e.g. escu03c9)
// We want to reinsert the original unicode char.
if (UTFconverter.containsUnicodeHexCharacters(lb)) {
if (debug) System.out.println("ConvertFrom " + lb);
lb = UTFconverter.convert2UTF(lb);
if (debug) System.out.println("ConvertFrom " + lb);
}
// Font metrics
Rectangle2D fr = g.getFontMetrics().getStringBounds(lb, g);
double w = fr.getWidth();
double h = fr.getHeight();
// Rotationswinkel ermitteln
dx = x1 - x0;
dy = y1 - y0;
if (dx == 0d) alphaRot = -Math.PI/2d;
else alphaRot = Math.atan(dy/dx);
// Rotieren und schieben
float xpix = (float) (PM.getX() * u);
float ypix = (float) (PM.getY() * u);
rotiert = AffineTransform.getRotateInstance(alphaRot, xpix, ypix);
Graphics2D tempGraphics = (Graphics2D) g.create();
tempGraphics.setStroke(g.getStroke());
tempGraphics.transform(rotiert);
tempGraphics.translate(-w/2d, -h/3d); // schieben
tempGraphics.drawString(lb, xpix, ypix);
// Rotation und Verschiebung zurückstellen
tempGraphics.dispose();
}
}
/**
* Label a line using TeX input.
* Does not draw a line. Does not move the cursor.
* @param x0
* @param y0
* @param x1
* @param y1
* @param latex Label using TeX math syntax
*/
public void texlabelline(double x0, double y0, double x1, double y1, String latex) {
Point2D PM = mitte(new Point2D.Double(x0,y0), new Point2D.Double(x1,y1));
// Mavscript-yacas replaces unicode chars (e.g. ω, but every char above 127)
// by a ascii hexadecimal representation (e.g. escu03c9)
// We want to reinsert the original unicode char.
if (UTFconverter.containsUnicodeHexCharacters(latex)) {
if (debug) System.out.println("ConvertFrom " + latex);
latex = UTFconverter.convert2UTF(latex);
if (debug) System.out.println("ConvertFrom " + latex);
}
TeXFormula formula = new TeXFormula(latex);
formula.setColor(g.getColor());
//formula.setDEBUG(true);
TeXIcon icon = formula.createTeXIcon(TeXConstants.STYLE_TEXT, schriftgr, true);
double w = icon.getTrueIconWidth();
double h = icon.getTrueIconHeight();
AffineTransform rotiert;
double alphaRot; // Rotationswinkel
double dx, dy;
// Rotationswinkel ermitteln
dx = x1 - x0;
dy = y1 - y0;
if (dx == 0d) alphaRot = -Math.PI/2d;
else alphaRot = Math.atan(dy/dx);
// Rotieren und schieben
float xpix = (float) (PM.getX() * u);
float ypix = (float) (PM.getY() * u);
rotiert = AffineTransform.getRotateInstance(alphaRot, 0, 0);
Graphics2D tempGraphics = (Graphics2D) g.create();
tempGraphics.translate(xpix, ypix); // schieben
tempGraphics.setStroke(g.getStroke());
tempGraphics.transform(rotiert);
tempGraphics.translate(-w / 2d, -h / 3d); // schieben
// new settings
tempGraphics.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING,
java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
tempGraphics.setRenderingHint(java.awt.RenderingHints.KEY_RENDERING,
java.awt.RenderingHints.VALUE_RENDER_QUALITY);
tempGraphics.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
tempGraphics.scale(schriftgr, schriftgr); // the point size
// draw formula box
Box box = icon.getBox();
box.draw(tempGraphics, 0, 0);
tempGraphics.dispose();
}
/**
* Set number format for dimension angles
* @param nb number of decimal digits
*/
public void decdigitsangle(int nb) {
if (nb >= 0) angledigits = nb;
else angledigits = angledigitsStd;
}
/**
* Draw a dimension arc at a vertex.
* The label is either its angle in degrees or any text. Clock-wise.
* Sets the cursor to the vertex (x0/y0).
*
* @param x0 vertex x-coord
* @param y0 vertex y-coord
* @param xP
* @param yP
* @param xQ
* @param yQ
* @param isTeX
*/
public void dimangle(double x0, double y0, double xP, double yP, double xQ, double yQ, boolean isTeX) {
double dir2 = Math.atan2(yQ - y0, xQ - x0);
double dir1 = Math.atan2(yP - y0, xP - x0);
double anglerad = dir2 - dir1;
if (anglerad < 0) anglerad += 2. * Math.PI;
double angledeg = 180. / Math.PI * anglerad;
String dimstr = Fkt.nf(angledeg, angledigits);
if (dimstr.contains(".")) {
while (dimstr.endsWith("0")) {
dimstr = dimstr.substring(0, dimstr.length()-1);
}
if (dimstr.endsWith(".")) {
dimstr = dimstr.substring(0, dimstr.length()-1);
}
}
if (isTeX || !font.equalsIgnoreCase("Tex")) dimstr = dimstr + "°"; // TeX (jlm_cmr10.ttf) does not contain °
if (Math.abs(angledeg - 90) < 1E-10 && angledigits < 10) { // hard coded tolerance
dimstr = "."; // right angle
}
dimangle(x0, y0, xP, yP, xQ, yQ, isTeX, dimstr);
}
/**
* Draw a dimension arc at a vertex.
* The label is either its angle in degrees or any text. Clock-wise.
* Sets the cursor to the vertex (x0/y0).
*
* @param x0 vertex x-coord
* @param y0 vertex y-coord
* @param xP
* @param yP
* @param xQ
* @param yQ
* @param isTeX
* @param lb Label. \".\" for right angle sign
*/
public void dimangle(double x0, double y0, double xP, double yP, double xQ, double yQ, boolean isTeX, String lb) {
double dir2 = Math.atan2(yQ - y0, xQ - x0);
if (dir2 < 0) dir2 += 2. * Math.PI;
double dir1 = Math.atan2(yP - y0, xP - x0);
if (dir1 < 0) dir1 += 2. * Math.PI;
double r = 4; // mm
arc(x0, y0, r/u, 180. / Math.PI * dir1, 180. / Math.PI * dir2); // sets cursor to x0,y0
double textdir = (dir1+dir2)/2d;
if (dir2 < dir1) textdir += Math.PI;
assert textdir >= 0;
String orientation;
if (textdir <= Math.PI/4d) orientation = "E";
else if (textdir < 3*Math.PI/4d) orientation = "S";
else if (textdir <= 5*Math.PI/4d) orientation = "W";
else if (textdir < 7*Math.PI/4d) orientation = "N";
else orientation = "E";
if (lb.equals(".")) { // right angle
moverel(0.6*r/u*Math.cos(textdir), 0.6*r/u*Math.sin(textdir));
point();
} else {
moverel(r/u*Math.cos(textdir), r/u*Math.sin(textdir));
if (isTeX) texlabel(lb, orientation);
else label(lb, orientation);
}
currentpos.setLocation(x0, y0);
}
/**
* Draw an image.
*
* @param filename
* @param dpi dots per inch
* @param pxX image dots from the left to the insertion point
* @param pxY image dots from the top to the insertion point
* @param massstab scale of the plan (image). 0 does not do any scaling
* @throws java.io.FileNotFoundException
*/
public void image (String filename, double dpi, double pxX, double pxY, double massstab) throws FileNotFoundException, IOException {
BufferedImage image;
File file = new File(filename);
image = ImageIO.read(file);
double x0 = currentpos.getX();
double y0 = currentpos.getY();
double scalexu; // scale * u
if (massstab == 0) scalexu = inch / dpi;
else scalexu = massstab/naturalunit * inch / dpi * u;
int sx1 = 0;
int sy1 = 0;
int sw = image.getWidth();
int sh = image.getHeight();
double dx1 = (x0*u - scalexu*pxX);
double dy1 = (y0*u - scalexu*pxY);
double dw = (scalexu*sw);
double dh = (scalexu*sh);
// The method drawImage expects integer values (dx1,dx2,dw,dh) for scaling the image:
// g.drawImage(image, dx1, dy1, dx1 + dw, dy1 + dh, sx1, sy1, sx1 + sw, sy1 + sh, null);
// To avoid rough rounding issues, a scaled graphic context is used.
double sf = 256;
AffineTransform scaled = AffineTransform.getScaleInstance(1d/sf, 1d/sf);
Graphics2D tempGraphics = (Graphics2D) g.create();
tempGraphics.translate(dx1, dy1);
tempGraphics.transform(scaled);
tempGraphics.drawImage(image, 0, 0, (int) (sf*dw), (int) (sf*dh), sx1, sy1, sx1 + sw, sy1 + sh, null);
tempGraphics.dispose(); // Release system ressources
}
// ------------------------------------------------------------------
/**
* Get the current number format for dimension lines
*/
int getdecdigits() {
return digits;
}
/**
* Get the unit length in mm
*/
double getunitlength() {
return u;
}
// ------------------------------------------------------------------
/** Returns a circle object.
* @param zentrum center Will be scaled by the unit lenght
* @param diameter diameter In mm, will not be scaled.
*/
private Ellipse2D.Double kreis(Point2D zentrum, double diameter) {
Ellipse2D.Double derkreis = new Ellipse2D.Double(zentrum.getX()*u - diameter/2d, zentrum.getY()*u - diameter/2d,
diameter, diameter);
return derkreis;
}
private Point2D mitte(Point2D pkt1, Point2D pkt2) {
Point2D pkt = new Point2D.Double();
pkt.setLocation((pkt1.getX()+pkt2.getX())/2d, (pkt1.getY()+pkt2.getY())/2d);
return pkt;
}
private void pfeil(double vonX, double vonY, double bisX, double bisY) {
vonX *= u;
vonY *= u;
bisX *= u;
bisY *= u;
double spitzenlängeMax = 15. / 72.*inch;
double spitzenlängeMin = 7 / 72.*inch;
double spitzenlänge = Math.hypot(bisY-vonY, bisX-vonX) / 4d; // bei allfälliger Anpassung, auch darstellenLasten() resp. Auflkr. anpassen
if (spitzenlänge > spitzenlängeMax) spitzenlänge = spitzenlängeMax; //15;
if (spitzenlänge < spitzenlängeMin) spitzenlänge = spitzenlängeMin; //7;
double dx = spitzenlänge * (bisX-vonX) / Math.hypot(bisY-vonY, bisX-vonX);
double dy = spitzenlänge * (bisY-vonY) / Math.hypot(bisY-vonY, bisX-vonX);
if (Math.hypot(bisY-vonY, bisX-vonX) >= spitzenlänge) {
g.draw(new Line2D.Double(vonX,vonY,bisX - dx/2d ,bisY - dy/2d));
}
GeneralPath spitze = new GeneralPath();
spitze.moveTo((float)bisX, (float)bisY);
spitze.lineTo((float)(bisX - dx + dy/3d), (float)(bisY - dy - dx/3d));
spitze.lineTo((float)(bisX - dx - dy/3d), (float)(bisY - dy + dx/3d));
spitze.closePath();
g.fill(spitze);
}
private void doppelpfeil(double vonX, double vonY, double bisX, double bisY, boolean withbar) {
vonX *= u;
vonY *= u;
bisX *= u;
bisY *= u;
double spitzenlängeMax = 11. / 72.*inch;
double spitzenlängeMin = 5 / 72.*inch;
double spitzenlänge = Math.hypot(bisY-vonY, bisX-vonX) / 4d;
if (spitzenlänge > spitzenlängeMax) spitzenlänge = spitzenlängeMax; //11;
if (spitzenlänge < spitzenlängeMin) spitzenlänge = spitzenlängeMin; //5;
double dx = spitzenlänge * (bisX-vonX) / Math.hypot(bisY-vonY, bisX-vonX);
double dy = spitzenlänge * (bisY-vonY) / Math.hypot(bisY-vonY, bisX-vonX);
if (Math.hypot(bisY-vonY, bisX-vonX) >= spitzenlänge) {
g.draw(new Line2D.Double(vonX + dx/2d,vonY + dy/2d,bisX - dx/2d ,bisY - dy/2d));
}
GeneralPath spitzeBis = new GeneralPath();
spitzeBis.moveTo((float)bisX, (float)bisY);
spitzeBis.lineTo((float)(bisX - dx + dy/3d), (float)(bisY - dy - dx/3d));
spitzeBis.lineTo((float)(bisX - dx - dy/3d), (float)(bisY - dy + dx/3d));
spitzeBis.closePath();
g.fill(spitzeBis);
GeneralPath spitzeVon = new GeneralPath();
spitzeVon.moveTo((float)vonX, (float)vonY);
spitzeVon.lineTo((float)(vonX + dx - dy/3d), (float)(vonY + dy + dx/3d));
spitzeVon.lineTo((float)(vonX + dx + dy/3d), (float)(vonY + dy - dx/3d));
spitzeVon.closePath();
g.fill(spitzeVon);
if (withbar) {
Stroke cstroke = g.getStroke();
g.setStroke(new java.awt.BasicStroke(currentstrokesize/2f));
g.draw(new Line2D.Double(vonX + dy/2d, vonY - dx/2d, vonX - dy/2d, vonY + dx/2d));
g.draw(new Line2D.Double(bisX + dy/2d, bisY - dx/2d, bisX - dy/2d, bisY + dx/2d));
g.setStroke(cstroke);
}
}
private class clBlock {
private final AffineTransform at;
private final Point2D pos;
private clBlock (AffineTransform at, Point2D pos) {
this.at = at;
this.pos = new Point2D.Double(pos.getX(), pos.getY());
}
private AffineTransform getAffineTransform() {
return at;
}
private Point2D getPos() {
return pos;
}
}
}
Ads help maintain this website.