/* This file is part of the source code for 3D-XplorMath-J, Version 1.0 (January 2008).
* Copyright (c) 2008 The 3D-XplorMath Consortium (http://3d-xplormath.org).
* This source code is released under a BSD License, which allows redistribution
* in source and binary form, with or without modification, provided copyright
* and license information are included, and with no warranty or guarantee of
* any kind. For details, see http://3d-xplormath.org/j/source/BSDLicense.txt
*/
package vmm.core.render;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import vmm.core.Transform;
import vmm.core.View;
public class ImageRenderer2D implements Renderer2D {
protected BufferedImage image;
protected Transform transform;
protected Graphics2D currentGraphics;
protected Color foregroundColor; // from the View
protected Color backgroundColor;
protected boolean antialiased;
protected Color currentColor;
private Point2D tempPoint = new Point2D.Double();
public void startRender(View view, Transform transform, int width, int height) {
if (image == null || image.getWidth() != width || image.getHeight() != height || image.getType() != BufferedImage.TYPE_INT_RGB) {
image = null;
image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
}
this.transform = transform;
foregroundColor = view.getForeground();
backgroundColor = view.getBackground();
currentGraphics = image.createGraphics();
currentGraphics.setBackground(backgroundColor);
currentGraphics.clearRect(0, 0, width, height);
currentColor = foregroundColor;
currentGraphics.setColor(currentColor);
antialiased = view.getAntialiased();
if (antialiased)
currentGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
transform.setUpDrawInfo(currentGraphics, 0, 0, width, height, view.getPreserveAspect(), view.getApplyGraphics2DTransform());
}
public boolean restartRender(View view, Transform transform, int width, int height) {
if (image == null || image.getWidth() != width || image.getHeight() != height)
return false;
this.transform = transform;
foregroundColor = view.getForeground();
backgroundColor = view.getBackground();
currentGraphics = image.createGraphics();
currentColor = foregroundColor;
currentGraphics.setColor(currentColor);
antialiased = view.getAntialiased();
if (antialiased)
if (view.getAntialiased())
currentGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
transform.setUpDrawInfo(currentGraphics, 0, 0, width, height, view.getPreserveAspect(), view.getApplyGraphics2DTransform());
return true;
}
public void endRender() {
currentGraphics.dispose();
currentGraphics = null;
transform.finishDrawing();
transform = null;
}
public void dispose() {
image = null;
}
public void draw(Graphics2D g) {
g.drawImage(image,0,0,null);
}
public BufferedImage getImage(boolean alwaysCopy) {
if ( image == null || !alwaysCopy )
return image;
BufferedImage copy = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
Graphics g = copy.getGraphics();
g.drawImage(image,0,0,null);
g.dispose();
return copy;
}
/**
* This method will set the color in the current graphics context. This method should only
* be used while drawing an exhibit or after {@link #beginDrawToOffscreenImage()}. If called
* at other times, there is no effect.
* @param c the color to be used for subsequent drawing; if c is null, then the default foreground
* color of the View is restored.
*/
public void setColor(Color c) {
if (currentGraphics != null)
currentGraphics.setColor( c == null? foregroundColor : c );
currentColor = currentGraphics.getColor();
}
/**
* Returns the drawing color of the current graphics context. This method should only be
* used while drawing an exhibit or after {@link #beginDrawToOffscreenImage()}. If called
* at other times, the return value is null.
*/
public Color getColor() {
return (currentGraphics == null)? null : currentGraphics.getColor();
}
/**
* Returns the Stroke used by current graphics context. This method should only be
* used while drawing an exhibit or after {@link #beginDrawToOffscreenImage()}. If called
* at other times, the return value is null.
*/
public Stroke getStroke() {
if (currentGraphics != null)
return currentGraphics.getStroke();
else
return null;
}
/**
* This method will set the Stroke for the current graphics context. This method should only
* be used while drawing an exhibit or after {@link #beginDrawToOffscreenImage()}. If called
* at other times, there is no effect.
* @param stroke the stroke to be used for subsequent drawing; if stroke is null, then a default stroke
* is used with a width of 1 pixel.
*/
public void setStroke(Stroke stroke) {
if (currentGraphics != null)
currentGraphics.setStroke(stroke == null? new BasicStroke(transform.getDefaultStrokeSize()) : stroke);
}
/**
* Draws a point by turning on a single pixel. This is done by transforming (x,y) to pixel coordinates,
* then using drawPixelDirect to set the color of the single pixel.
* @see #drawPixelDirect(Color, int, int)
* @param x The x-coordinate of the point, in window (real xy) coodinates.
* @param y The y-coordinate of the point, in window (real xy) coodinates.
*/
public void drawPixel(double x, double y) {
tempPoint.setLocation(x,y);
transform.windowToViewport(tempPoint);
int xInt = (int)(tempPoint.getX() + 0.4999);
int yInt = (int)(tempPoint.getY() + 0.4999);
int rgb = currentColor.getRGB();
try {
image.setRGB(xInt,yInt,rgb);
}
catch (Exception e) {
}
}
/**
* Draws a dot of specified diameter centered at a specified point.
* This is done by calling the Graphics2D fill() command for an
* appropriate Ellipse2D. The diameter is specified in pixels.
* Note that if the preserveAspectRatio is off for this View, then
* the dot will can be an oval rather than a circle.
*/
public void drawDot(Point2D pt, double diameter) {
tempPoint.setLocation(pt);
transform.windowToDrawingCoords(tempPoint);
double h = diameter*transform.getPixelWidth();
double w = diameter*transform.getPixelHeight();
currentGraphics.fill(new Ellipse2D.Double(pt.getX()-h/2,pt.getY()-w/2,h,w));
}
/**
* Draws a list of pixels in the current drawing context, where the pixels are specified in object coordinates.
* This should only be called while drawing is in progress.*/
public void drawPixels(Point2D[] points, int pointIndexStart, int pointIndexEnd) { // points in window coordinates
if (points == null)
return;
if (pointIndexStart >= points.length)
pointIndexStart = points.length - 1;
if (pointIndexStart < 0)
pointIndexStart = 0;
if (pointIndexEnd >= points.length)
pointIndexEnd = points.length - 1;
if (pointIndexEnd < 0)
pointIndexEnd = 0;
if (pointIndexEnd <= pointIndexStart)
return;
Color color = currentGraphics.getColor();
int rgb = color.getRGB();
for (int i = pointIndexStart; i <= pointIndexEnd; i++) {
if (points[i] != null) {
tempPoint.setLocation(points[i]);
transform.windowToViewport(tempPoint);
try {
image.setRGB((int)(tempPoint.getX()+0.499), (int)(tempPoint.getY()+0.499),rgb);
}
catch (Exception e) {
}
}
}
}
/**
* This can be called during drawing to draw a string at a specified point, given in window (real x,y) coordinates.
* The font is NOT transformed, as it would be if you simply used the drawString
* method of a drawing context to which a transform has been applied.
* The point (x,y) is properly transformed from xy-coordinates to pixel coordinates, whether a
* transform has been applied ot the drawing context or not. After conversion, if necessary, the
* graphics context returned by {@link Transform#getUntransformedGraphics()} is used to draw the string.
* @see #drawString(String, Point2D)
*/
public void drawString(String s, double x, double y) {
Point2D pt = new Point2D.Double(x,y);
transform.windowToViewport(pt);
transform.getUntransformedGraphics().drawString(s,(float)pt.getX(),(float)pt.getY());
}
/**
* Draws a line in the current drawing context. This should only be called while drawing is
* in progress. The line has endpoints (x1,y1) and (x2,y2), where the coordinates are
* in window (real xy) coordinates. This should only be called when a drawing operation is in
* progress.
*/
public void drawLine(double x1, double y1, double x2, double y2) {
double tx1, ty1;
tempPoint.setLocation(x1,y1);
transform.windowToDrawingCoords(tempPoint);
tx1 = tempPoint.getX();
ty1 = tempPoint.getY();
tempPoint.setLocation(x2,y2);
transform.windowToDrawingCoords(tempPoint);
currentGraphics.draw(new Line2D.Double(tx1, ty1, tempPoint.getX(), tempPoint.getY()));
}
/**
* Draws a curve in the current drawing context. The points on the curve are given in window (real xy) coordinates.
* This should only be called while drawing is in progress.
* @param points The curve is drawn through some or all of the points in this array. If the array is null, nothing is done.
* The curve is acutually just made up of lines from one point to the next. An element in the array can be null.
* In that case, one or two segments are missing from the curve -- the segments on either side of the missing point.
* Consecutive points are also not joined by a segment if the jump from one point to the next is too large.
* @param pointIndexStart The number of points in the array that should be used for the curve. A curve is drawn
* though points[pointIndexStart], point[pointIndexStart+1], ..., points[pointIndexEnd]. The value of pointIndexStart
* is clamped to lie in the range 0 to points.length-1.
* @param pointIndexEnd The number of points in the array that should be used for the curve. A curve is drawn
* though points[pointIndexStart], point[pointIndexStart+1], ..., points[pointIndexEnd]. The value of pointIndexEnd
* is clamped to lie in the range 0 to points.length-1. If pointIndexEnd is less than or equal to pointIndexStart,
* nothing is drawn.
*/
public void drawCurve(Point2D[] points, int pointIndexStart, int pointIndexEnd) { // points in window coordinates
if (points == null)
return;
if (pointIndexStart >= points.length)
pointIndexStart = points.length - 1;
if (pointIndexStart < 0)
pointIndexStart = 0;
if (pointIndexEnd >= points.length)
pointIndexEnd = points.length - 1;
if (pointIndexEnd < 0)
pointIndexEnd = 0;
if (pointIndexEnd <= pointIndexStart)
return;
GeneralPath curve = new GeneralPath();
double maxJumpX = Math.abs(transform.getXmax() - transform.getXmin())/4;
double maxJumpY = Math.abs(transform.getYmax() - transform.getYmin())/4;
if (transform.appliedTransform2D())
maxJumpX = maxJumpY = Math.max(maxJumpX,maxJumpY);
boolean moved = false;
for (int i = pointIndexStart; i < pointIndexEnd; i++) {
if (points[i] != null) {
tempPoint.setLocation(points[i]);
transform.windowToDrawingCoords(tempPoint);
if (i == pointIndexStart)
curve.moveTo((float)tempPoint.getX(), (float)tempPoint.getY());
else if (i > pointIndexStart && points[i-1] != null && Math.abs(points[i].getX() - points[i-1].getX()) <= maxJumpX
&& Math.abs(points[i].getY() - points[i-1].getY()) <= maxJumpY)
curve.lineTo((float)tempPoint.getX(), (float)tempPoint.getY());
else {
curve.moveTo((float)tempPoint.getX(), (float)tempPoint.getY());
moved = true;
}
}
}
if (points[pointIndexEnd] != null) {
if ( (pointIndexStart == 0) && (pointIndexEnd == points.length-1)
&& (points[0] != null) && (Math.abs(points[0].getX() - points[pointIndexEnd].getX()) <= transform.getPixelWidth()/100 )
&& (Math.abs(points[0].getY() - points[pointIndexEnd].getY()) <= transform.getPixelHeight()/100 )
&& (!moved) ) {
curve.closePath(); // System.out.println("Path was closed"); // Do NOT close if moved = true - leads to errors.
}
else {
// replaced from here
tempPoint.setLocation(points[pointIndexEnd]);
transform.windowToDrawingCoords(tempPoint);
if (points[pointIndexEnd-1] != null && Math.abs(points[pointIndexEnd].getX() - points[pointIndexEnd-1].getX()) <= maxJumpX
&& Math.abs(points[pointIndexEnd].getY() - points[pointIndexEnd-1].getY()) <= maxJumpY)
curve.lineTo((float)tempPoint.getX(), (float)tempPoint.getY());
else
curve.moveTo((float)tempPoint.getX(), (float)tempPoint.getY());
}
}
currentGraphics.draw(curve);
}
public void drawOval(double x, double y, double width, double height) {
Point2D pt1 = new Point2D.Double(x,y);
Point2D pt2 = new Point2D.Double(x+width,y+height);
transform.windowToDrawingCoords(pt1);
transform.windowToDrawingCoords(pt2);
currentGraphics.draw(new Ellipse2D.Double(pt1.getX(), pt1.getY(), pt2.getX()-pt1.getX(), pt2.getY()-pt1.getY()));
}
public void fillOval(double x, double y, double width, double height) {
Point2D pt1 = new Point2D.Double(x,y);
Point2D pt2 = new Point2D.Double(x+width,y+height);
transform.windowToDrawingCoords(pt1);
transform.windowToDrawingCoords(pt2);
currentGraphics.fill(new Ellipse2D.Double(pt1.getX(), pt1.getY(), pt2.getX()-pt1.getX(), pt2.getY()-pt1.getY()));
}
public void drawRect(double x, double y, double width, double height) {
Point2D pt1 = new Point2D.Double(x,y);
Point2D pt2 = new Point2D.Double(x+width,y+height);
transform.windowToDrawingCoords(pt1);
transform.windowToDrawingCoords(pt2);
currentGraphics.draw(new Rectangle2D.Double(pt1.getX(), pt1.getY(), pt2.getX()-pt1.getX(), pt2.getY()-pt1.getY()));
}
public void fillRect(double x, double y, double width, double height) {
Point2D pt1 = new Point2D.Double(x,y);
Point2D pt2 = new Point2D.Double(x+width,y+height);
transform.windowToDrawingCoords(pt1);
transform.windowToDrawingCoords(pt2);
currentGraphics.fill(new Rectangle2D.Double(pt1.getX(), pt1.getY(), pt2.getX()-pt1.getX(), pt2.getY()-pt1.getY()));
}
public void drawCrosshair(double x, double y, int armlength, int strokeWidth, Color color, Color background) {
Point2D pt = new Point2D.Double(x,y);
transform.windowToViewport(pt);
int cx = (int)(pt.getX()+0.499);
int cy = (int)(pt.getY()+0.499);
Graphics2D g1 = transform.getUntransformedGraphics();
Color saveColor = g1.getColor();
int size = 2*armlength + strokeWidth;
int offset = (size+1)/2;
int offset2 = (strokeWidth+1)/2;
if (backgroundColor != null) {
g1.setColor(backgroundColor);
g1.fillRect(cx-offset-1, cy-offset2-1, size+2, strokeWidth+2);
g1.fillRect(cx-offset2-1, cy-offset-1, strokeWidth+2, size+2);
}
if (color != null)
g1.setColor(color);
g1.fillRect(cx-offset, cy-offset2, size, strokeWidth);
g1.fillRect(cx-offset2, cy-offset, strokeWidth, size);
g1.setColor(saveColor);
}
public void setDrawAntialiased(boolean antialiased) {
currentGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
antialiased ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
this.antialiased = antialiased;
}
public boolean getDrawAntialiased() {
return antialiased;
}
// //--------------------------- Some pixel-oriented drawing methods -----------------------
//
// /**
// * Sets the pixel with pixel coordinates (x,y) to be a specified color.
// * The pixel color is changed in the off-screen image, not on the screen immmediately.
// * The current transformation is not applied to the coordinates.
// * @param color the color for the pixel; if null, the current drawing color is used.
// * @param x the horizontal pixel coordinate.
// * @param y the vertical pixel coordinate.
// */
// public void drawPixelDirect(Color color, int x, int y) {
// if (x < 0 || y < 0 || x >= currentImage.getWidth() || y >= currentImage.getHeight())
// return;
// if (color == null)
// color = currentGraphics.getColor();
// int rgb;
// rgb = (color.getRed() << 16) | (color.getGreen() << 8) | (color.getBlue());
// currentImage.setRGB(x,y,rgb);
// }
//
// /**
// * Draws a line in the current color between points that are specified using
// * pixel coordinates.
// */
// public void drawLineDirect(int x1, int y1, int x2, int y2) {
// Graphics2D g = currentImage.createGraphics();
// g.setColor(currentColor);
// if (antialiased)
// g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// g.drawLine(x1,y1,x2,y2);
// g.dispose();
// }
//
// /**
// * Draws a filled-in rectangle in the current color, where the rectangle is specified in pixel coordinates.
// */
// public void fillRectDirect(int x, int y, int width, int height) {
// Graphics g = currentImage.getGraphics();
// g.setColor(currentColor);
// g.fillRect(x,y,width,height);
// g.dispose();
// }
//
// public void setImageRGBDirect(int x, int y, int width, int height, int[] rgb) {
// try {
// currentImage.setRGB(x,y,width,height,rgb,0,width);
// }
// catch (Exception e) {
// }
// }
//
//
}