Drawing curved line arrow on a bar graph using affine transformation - java

I am trying to draw a curved Line arrow on a stacked bar graph.I have been able to draw the curved line and arrow.But i am not able to connect the arrow to the end of the curved line.I am using affine transformation to draw the curved line.The below link describes the curved line and arrow that i have been able to draw http://i58.tinypic.com/2m422hy.png.Can anyone guide me as to how to connect the arrow to the end of the curved line.
Here is the code
package Stack;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author OSPL-B4
/
/
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import org.jfree.chart.annotations.CategoryAnnotation;
import org.jfree.chart.axis.CategoryAnchor;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.event.AnnotationChangeListener;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import org.jfree.io.SerialUtilities;
import org.jfree.ui.RectangleEdge;
import org.jfree.util.ObjectUtilities;
import org.jfree.util.PaintUtilities;
//import java.awt.Font;
/**
* A line annotation that can be placed on a
* {#link org.jfree.chart.plot.CategoryPlot}.
*/
public class CategoryLineAnnotation_demo1 implements CategoryAnnotation,
Cloneable, Serializable {
/** The category for the start of the line. */
private Comparable category1;
/** The value for the start of the line. */
private double value1;
/** The category for the end of the line. */
private Comparable category2;
/** The value for the end of the line. */
private double value2;
private final int ARR_SIZE = 4;
/** The line color. */
private transient Paint paint = Color.black;
/** The line stroke. */
private transient Stroke stroke = new BasicStroke(1.0f);
/**
* Creates a new annotation that draws a line between (category1, value1)
* and (category2, value2).
*
* #param category1 the category (<code>null</code> not permitted).
* #param value1 the value.
* #param category2 the category (<code>null</code> not permitted).
* #param value2 the value.
*/
public CategoryLineAnnotation_demo1(Comparable category1, double value1,
Comparable category2, double value2,
Paint paint, Stroke stroke) {
if (category1 == null) {
throw new IllegalArgumentException("Null 'category1' argument.");
}
if (category2 == null) {
throw new IllegalArgumentException("Null 'category2' argument.");
}
if (paint == null) {
throw new IllegalArgumentException("Null 'paint' argument.");
}
if (stroke == null) {
throw new IllegalArgumentException("Null 'stroke' argument.");
}
this.category1 = category1;
System.out.println("First Category value is "+category1);
this.value1 = value1;
this.category2 = category2;
System.out.println("Second Category value is "+category2);
this.value2 = value2;
this.paint = paint;
this.stroke = stroke;
}
/**
* Returns the category for the start of the line.
*
* #return The category for the start of the line (never <code>null</code>).
*/
public Comparable getCategory1() {
return this.category1;
}
/**
* Sets the category for the start of the line.
*
* #param category the category (<code>null</code> not permitted).
*/
public void setCategory1(Comparable category) {
if (category == null) {
throw new IllegalArgumentException("Null 'category' argument.");
}
this.category1 = category;
}
/**
* Returns the y-value for the start of the line.
*
* #return The y-value for the start of the line.
*/
public double getValue1() {
return this.value1;
}
/**
* Sets the y-value for the start of the line.
*
* #param value the value.
*/
public void setValue1(double value) {
this.value1 = value;
}
/**
* Returns the category for the end of the line.
*
* #return The category for the end of the line (never <code>null</code>).
*/
public Comparable getCategory2() {
return this.category2;
}
/**
* Sets the category for the end of the line.
*
* #param category the category (<code>null</code> not permitted).
*/
public void setCategory2(Comparable category) {
if (category == null) {
throw new IllegalArgumentException("Null 'category' argument.");
}
this.category2 = category;
}
/**
* Returns the y-value for the end of the line.
*
* #return The y-value for the end of the line.
*/
public double getValue2() {
return this.value2;
}
/**
* Sets the y-value for the end of the line.
*
* #param value the value.
*/
public void setValue2(double value) {
this.value2 = value;
}
/**
* Returns the paint used to draw the connecting line.
*
* #return The paint (never <code>null</code>).
*/
public Paint getPaint() {
return this.paint;
}
/**
* Sets the paint used to draw the connecting line.
*
* #param paint the paint (<code>null</code> not permitted).
*/
public void setPaint(Paint paint) {
if (paint == null) {
throw new IllegalArgumentException("Null 'paint' argument.");
}
this.paint = paint;
}
/**
* Returns the stroke used to draw the connecting line.
*
* #return The stroke (never <code>null</code>).
*/
public Stroke getStroke() {
// System.out.println("In Stacked bar Stroke is "+getStroke());
return this.stroke;
}
/**
* Sets the stroke used to draw the connecting line.
*
* #param stroke the stroke (<code>null</code> not permitted).
*/
public void setStroke(Stroke stroke) {
if (stroke == null) {
throw new IllegalArgumentException("Null 'stroke' argument.");
}
this.stroke = stroke;
}
/**
* Draws the annotation.
*
* #param g2 the graphics device.
* #param plot the plot.
* #param dataArea the data area.
* #param domainAxis the domain axis.
* #param rangeAxis the range axis.
*/
public void draw(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea,
CategoryAxis domainAxis, ValueAxis rangeAxis) {
CategoryDataset dataset = plot.getDataset();
int catIndex1 = dataset.getColumnIndex(this.category1);
int catIndex2 = dataset.getColumnIndex(this.category2);
int catCount = dataset.getColumnCount();
double lineX1 = 0.0f;
double lineY1 = 0.0f;
double lineX2 = 0.0f;
double lineY2 = 0.0f;
PlotOrientation orientation = plot.getOrientation();
RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
plot.getDomainAxisLocation(), orientation);
RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
plot.getRangeAxisLocation(), orientation);
if (orientation == PlotOrientation.HORIZONTAL) {
lineY1 = domainAxis.getCategoryJava2DCoordinate(
CategoryAnchor.MIDDLE, catIndex1, catCount, dataArea,
domainEdge);
lineX1 = rangeAxis.valueToJava2D(this.value1, dataArea, rangeEdge);
lineY2 = domainAxis.getCategoryJava2DCoordinate(
CategoryAnchor.MIDDLE, catIndex2, catCount, dataArea,
domainEdge);
lineX2 = rangeAxis.valueToJava2D(this.value2, dataArea, rangeEdge);
}
else if (orientation == PlotOrientation.VERTICAL) {
lineX1 = domainAxis.getCategoryJava2DCoordinate(
CategoryAnchor.MIDDLE, catIndex1, catCount, dataArea,
domainEdge);
lineY1 = rangeAxis.valueToJava2D(this.value1, dataArea, rangeEdge);
lineX2 = domainAxis.getCategoryJava2DCoordinate(
CategoryAnchor.MIDDLE, catIndex2, catCount, dataArea,
domainEdge);
lineY2 = rangeAxis.valueToJava2D(this.value2, dataArea, rangeEdge);
}
g2.setPaint(this.paint);
g2.setStroke(this.stroke);
drawArrow(g2,(int) lineX1, (int) lineY1, (int) lineX2, (int) lineY2);
}
void drawArrow(Graphics g1, int x1, int y1, int x2, int y2) {
Graphics2D g = (Graphics2D) g1.create();
double dx = x2 - x1, dy = y2 - y1;
System.out.println("Value of DX "+dx);
System.out.println("Value of DY "+dy);
double angle = Math.atan2(dy, dx);
System.out.println("Getting angle "+angle);
int len = (int) Math.sqrt(dx*dx + dy*dy);
AffineTransform at = AffineTransform.getTranslateInstance(x1, y1);
at.concatenate(AffineTransform.getRotateInstance(angle));
g.transform(at);
System.out.println("Affine transform X co-ordinate value is "+at.getScaleX());
System.out.println("Affine transform Y co-ordinate value is "+at.getScaleY());
float center1=(x1+x2)/2-40;
float center2= (y1+y2)/2-40;
QuadCurve2D q=new QuadCurve2D.Float(0,0,center1,center2,x2,y2);
g.draw(q);
g.setColor(Color.RED);
System.out.println("Length of arrow is "+len);
System.out.println("Get Start point 2D "+q.getP1());
System.out.println("Get End point 2D "+q.getP2());
g.fillPolygon(new int[] {len, len-ARR_SIZE, len-ARR_SIZE-10, len-60},
new int[] {0, -ARR_SIZE, ARR_SIZE-20, 5}, 4);
}
public void paintComponent(Graphics g) {
for (int x = 15; x < 200; x += 16)
drawArrow(g, x, x, x, 150);
drawArrow(g, 30, 300, 300, 190);
}
/**
* Tests this object for equality with another.
*
* #param obj the object (<code>null</code> permitted).
*
* #return <code>true</code> or <code>false</code>.
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof CategoryLineAnnotation_demo1)) {
return false;
}
CategoryLineAnnotation_demo1 that = (CategoryLineAnnotation_demo1) obj;
if (!this.category1.equals(that.getCategory1())) {
return false;
}
if (this.value1 != that.getValue1()) {
return false;
}
if (!this.category2.equals(that.getCategory2())) {
return false;
}
if (this.value2 != that.getValue2()) {
return false;
}
if (!PaintUtilities.equal(this.paint, that.paint)) {
return false;
}
if (!ObjectUtilities.equal(this.stroke, that.stroke)) {
return false;
}
return true;
}
/**
* Returns a hash code for this instance.
*
* #return A hash code.
*/
public int hashCode() {
// TODO: this needs work
return this.category1.hashCode() + this.category2.hashCode();
}
/**
* Returns a clone of the annotation.
*
* #return A clone.
*
* #throws CloneNotSupportedException this class will not throw this
* exception, but subclasses (if any) might.
*/
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* Provides serialization support.
*
* #param stream the output stream.
*
* #throws IOException if there is an I/O error.
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
SerialUtilities.writePaint(this.paint, stream);
SerialUtilities.writeStroke(this.stroke, stream);
}
/**
* Provides serialization support.
*
* #param stream the input stream.
*
* #throws IOException if there is an I/O error.
* #throws ClassNotFoundException if there is a classpath problem.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
this.paint = SerialUtilities.readPaint(stream);
this.stroke = SerialUtilities.readStroke(stream);
}
#Override
public void addChangeListener(AnnotationChangeListener al) {
}
#Override
public void removeChangeListener(AnnotationChangeListener al) {
}
}

You can create the arrow head based on the last line segment (which might already be transformed using an AffineTransform)
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ArrowPainter
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new ArrowPaintPanel();
f.getContentPane().add(panel);
f.setSize(500,500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ArrowPaintPanel extends JPanel implements MouseMotionListener
{
private Point2D startPoint = null;
private Point2D endPoint = null;
ArrowPaintPanel()
{
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
if (startPoint == null)
{
startPoint = new Point(getWidth()/2, getHeight()/2);
}
if (endPoint == null)
{
return;
}
Line2D line = new Line2D.Double(startPoint, endPoint);
Shape arrowHead = createArrowHead(line, 30, 20);
g.draw(line);
g.fill(arrowHead);
}
#Override
public void mouseDragged(MouseEvent e)
{
endPoint = e.getPoint();
repaint();
}
#Override
public void mouseMoved(MouseEvent e)
{
endPoint = e.getPoint();
repaint();
}
private static Shape createArrowHead(Line2D line, double length, double width)
{
Point2D p0 = line.getP1();
Point2D p1 = line.getP2();
double x0 = p0.getX();
double y0 = p0.getY();
double x1 = p1.getX();
double y1 = p1.getY();
double dx = x1 - x0;
double dy = y1 - y0;
double invLength = 1.0 / Math.sqrt(dx*dx+dy*dy);
double dirX = dx * invLength;
double dirY = dy * invLength;
double ax = x1 - length * dirX;
double ay = y1 - length * dirY;
double offsetX = width * -dirY * 0.5;
double offsetY = width * dirX * 0.5;
double c0x = ax + offsetX;
double c0y = ay + offsetY;
double c1x = ax - offsetX;
double c1y = ay - offsetY;
Path2D arrowHead = new Path2D.Double();
arrowHead.moveTo(x1, y1);
arrowHead.lineTo(c0x, c0y);
arrowHead.lineTo(c1x, c1y);
arrowHead.closePath();
return arrowHead;
}
}
EDIT: Update for the above EDIT and the comments: That's a lot of code, but still nothing that can be tested easily. What happens when you replace your line
drawArrow(g2,(int) lineX1, (int) lineY1, (int) lineX2, (int) lineY2);
with
g.fill(createArrowHead(new Line2D.Double(lineX1, lineY1, lineX2, lineY2), 30, 20));
?

Related

Rotating a triangle around a point java

I am having trouble. I need to rotate an equilateral triangle around it's centre by using the drag listener and click listener. The triangle should grow but now change angles and be rotated by a point while being centred at the middle of the triangle. This is my problem, it is currently dragging by the point 3 and rotating around point 1. I have an array of values x and y and it stores 4 values each containing the initial point first at ordinal value 0 and point 1 2 and 3 at the corresponding values.
`
public class DrawTriangle extends JFrame {
enter code here
/** The Constant NUMBER_3. */
private static final int NUMBER_3 = 3;
/** The Constant EQUL_ANGLE. */
#SuppressWarnings("unused")
private static final double EQUL_ANGLE = 1;
/** The Constant TRIANGLE_POINTS. */
private static final int TRIANGLE_POINTS = 4;
/** The Constant _400. */
private static final int SIZE = 400;
/** The x points. */
private int [] xPoints = new int[TRIANGLE_POINTS];
/** The y points. */
private int [] yPoints = new int[TRIANGLE_POINTS];
private int xInitial;
private int yInitial;
/** The x. */
private double x = EQUL_ANGLE;
/** The new x. */
private double newX;
/** The new y. */
private double newY;
/**
* Instantiates a new draw triangle.
*/
public DrawTriangle() {
super("Dimitry Rakhlei");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setContentPane(new DrawTrianglePanel());
setSize(SIZE, SIZE); // you can change this size but don't make it HUGE!
setVisible(true);
}
/**
* The Class DrawTrianglePanel.
*/
private class DrawTrianglePanel extends JPanel implements MouseListener,
MouseMotionListener {
/**
* Instantiates a new draw triangle panel.
*/
public DrawTrianglePanel() {
addMouseListener(this);
addMouseMotionListener(this);
}
/**
* Drawing the triangle.
*
* #param g
* the g
* #see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
// DRAWING CODE HERE
g.drawPolygon(xPoints, yPoints, 3);
System.out.println("Paint called");
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseListener#mousePressed
* (java.awt.event.MouseEvent)
*/
public void mousePressed(MouseEvent e) {
System.out.println("Mouse pressed called");
e.getPoint();
xPoints[0] = e.getPoint().x;
yPoints[0] = e.getPoint().y;
repaint();
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseListener#mouseReleased
* (java.awt.event.MouseEvent)
*/
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse released called");
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseMotionListener#mouseDragged
* (java.awt.event.MouseEvent)
*/
public void mouseDragged(MouseEvent e) {
System.out.println("Mouse dragged called");
newX = e.getPoint().x;
newY = e.getPoint().y;
xPoints[1] = (int) newX;
yPoints[1] = (int) newY;
newX = xPoints[0] + (xPoints[1]-xPoints[0])*Math.cos(x) - (yPoints[1]-yPoints[0])*Math.sin(x);
newY = yPoints[0] + (xPoints[1]-xPoints[0])*Math.sin(x) + (yPoints[1]-yPoints[0])*Math.cos(x);
xPoints[2] = (int) newX;
yPoints[2] = (int) newY;
newX = xPoints[0] + (xPoints[1]-xPoints[0])*Math.cos(x) - (yPoints[1]-yPoints[0])*Math.sin(x);
newY = yPoints[0] + (xPoints[1]-xPoints[0])*Math.sin(x) + (yPoints[1]-yPoints[0])*Math.cos(x);
xPoints[3] = (int) newX;
yPoints[3] = (int) newY;
repaint();
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseListener#mouseEntered
* (java.awt.event.MouseEvent)
*/
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse Entered.");
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseListener#mouseExited
* (java.awt.event.MouseEvent)
*/
public void mouseExited(MouseEvent e) {
System.out.println("Mouse exited.");
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseListener#mouseClicked
* (java.awt.event.MouseEvent)
*/
public void mouseClicked(MouseEvent e) {
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseMotionListener#mouseMoved
* (java.awt.event.MouseEvent)
*/
public void mouseMoved(MouseEvent e) {
}
}
/**
* The main method.
*
* #param args
* the arguments
*/
public static void main(String[] args) {
new DrawTriangle();
}
};`
My issue is that this code basically runs correctly but I am told the vertex point of rotation has to be in the middle of the triangle. Mine is the first point.
Start by taking a look at 2D Graphics, in particular Transforming Shapes, Text, and Images.
Basically, your "polygon" will have a definable size (the maximum x/y point), from this, you can determine the center position of the "polygon", for example...
protected Dimension getTriangleSize() {
int maxX = 0;
int maxY = 0;
for (int index = 0; index < xPoints.length; index++) {
maxX = Math.max(maxX, xPoints[index]);
}
for (int index = 0; index < yPoints.length; index++) {
maxY = Math.max(maxY, yPoints[index]);
}
return new Dimension(maxX, maxY);
}
This just returns the maximum x and y bounds of your polygon. This allows you to calculate the center position of the polygon. You'll see why in a second why you don't need to actually specify the origin point...
Next, we calculate a AffineTransform, which is the applied to the Graphics context directly...
Graphics2D g2d = (Graphics2D) g.create();
AffineTransform at = new AffineTransform();
Dimension size = getTriangleSize();
int x = clickPoint.x - (size.width / 2);
int y = clickPoint.y - (size.height / 2);
at.translate(x, y);
at.rotate(Math.toRadians(angle), clickPoint.x - x, clickPoint.y - y);
g2d.setTransform(at);
g2d.drawPolygon(xPoints, yPoints, 3);
// Guide
g2d.setColor(Color.RED);
g2d.drawLine(size.width / 2, 0, size.width / 2, size.height / 2);
g2d.dispose();
This not only translates the triangle position, but will also rotate it. What this means you can create a normalised polygon (whose origin point is 0x0) and allow the Graphics context to place it where you want it, this makes life SO much easier...
Now, the rotation calculation is based on calculating the angle between two points, the "click" point and the "drag" point...
angle = -Math.toDegrees(Math.atan2(e.getPoint().x - clickPoint.x, e.getPoint().y - clickPoint.y)) + 180;
Which is based on the solution in this question
For example...
The red line is simple a guide to show that the tip of the triangle is point towards the mouse...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawTriangle extends JFrame {
/**
* The x points.
*/
private int[] xPoints = new int[]{0, 25, 50};
/**
* The y points.
*/
private int[] yPoints = new int[]{50, 0, 50};
double angle = 0f;
/**
* Instantiates a new draw triangle.
*/
public DrawTriangle() {
super("Dimitry Rakhlei");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setContentPane(new DrawTrianglePanel());
pack();
setLocationRelativeTo(null);
setVisible(true);
}
/**
* The Class DrawTrianglePanel.
*/
private class DrawTrianglePanel extends JPanel implements MouseListener,
MouseMotionListener {
private Point clickPoint;
/**
* Instantiates a new draw triangle panel.
*/
public DrawTrianglePanel() {
addMouseListener(this);
addMouseMotionListener(this);
clickPoint = new Point(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected Dimension getTriangleSize() {
int maxX = 0;
int maxY = 0;
for (int index = 0; index < xPoints.length; index++) {
maxX = Math.max(maxX, xPoints[index]);
}
for (int index = 0; index < yPoints.length; index++) {
maxY = Math.max(maxY, yPoints[index]);
}
return new Dimension(maxX, maxY);
}
/**
* Drawing the triangle.
*
* #param g the g
* #see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
AffineTransform at = new AffineTransform();
Dimension size = getTriangleSize();
int x = clickPoint.x - (size.width / 2);
int y = clickPoint.y - (size.height / 2);
at.translate(x, y);
at.rotate(Math.toRadians(angle), clickPoint.x - x, clickPoint.y - y);
g2d.setTransform(at);
g2d.drawPolygon(xPoints, yPoints, 3);
// Guide
g2d.setColor(Color.RED);
g2d.drawLine(size.width / 2, 0, size.width / 2, size.height / 2);
g2d.dispose();
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseListener#mousePressed (java.awt.event.MouseEvent)
*/
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Mouse pressed called");
// clickPoint = e.getPoint();
repaint();
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseListener#mouseReleased (java.awt.event.MouseEvent)
*/
#Override
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse released called");
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseMotionListener#mouseDragged (java.awt.event.MouseEvent)
*/
public void mouseDragged(MouseEvent e) {
System.out.println("Mouse dragged called");
angle = -Math.toDegrees(Math.atan2(e.getPoint().x - clickPoint.x, e.getPoint().y - clickPoint.y)) + 180;
repaint();
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseListener#mouseEntered (java.awt.event.MouseEvent)
*/
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse Entered.");
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseListener#mouseExited (java.awt.event.MouseEvent)
*/
public void mouseExited(MouseEvent e) {
System.out.println("Mouse exited.");
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseListener#mouseClicked (java.awt.event.MouseEvent)
*/
public void mouseClicked(MouseEvent e) {
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseMotionListener#mouseMoved (java.awt.event.MouseEvent)
*/
public void mouseMoved(MouseEvent e) {
}
}
/**
* The main method.
*
* #param args the arguments
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
new DrawTriangle();
}
});
}
}
Now, before you jump all over me and complain that the solution is "too complex", understand that I'm an idiot, seriously, my 2 year old has a better grasp on basic mathematics then I do, this is the most simplistic solution I can come up with that doesn't melt my brain and uses the dual array polygon API. Personally, I'd use the Shape API, but that's not what you started with...

Error in NewJFrame with ActionListener

I am completely new to Java and I am learning how to write a program that detects and reacts to collision based on a professor's lecture video. Here is a link to the video.
All of my code should be similar to what is in his lecture. My error appears to be in the NewJFrame.java file. Why is ActionListener not working? Thanks in advance for the help.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import java.awt.event.ActionListener;
import javafx.event.ActionEvent;
import javax.swing.Timer;
/**
*
* #author PC
*/
public class NewJFrame extends javax.swing.JFrame {
/**
* Creates new form NewJFrame
*/
public NewJFrame() {
initComponents();
bf = new BallField(getWidth(), getHeight());
add(bf);
pack();
njTimer = new Timer(1,
new ActionListener() {
public void actionPerformed(ActionEvent e) {
bf.detectCollision();
}
});
njTimer.start();
}
BallField bf;
Timer njTimer;
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
#SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
);
pack();
}// </editor-fold>
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}
// Variables declaration - do not modify
// End of variables declaration
}
The line saying "new ActionListener() {" is underlined and I get an error message saying:
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - is not abstract and does not override abstract method actionPerformed(java.awt.event.ActionEvent) in java.awt.event.ActionListener
at collisiondetection.NewJFrame.(NewJFrame.java:28)
at collisiondetection.Main.main(Main.java:20)
NewJFrame.java:28 refers to the line that says "njTimer = new Timer(1," in the code above.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here is my code for the other .java files for reference.
Main file:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
/**
*
* #author PC
*/
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
NewJFrame njf = new NewJFrame();
njf.setVisible(true);
}
}
BallField.java:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.LinkedList;
import java.util.ListIterator;
import javax.swing.JPanel;
/**
*
* #author PC
*/
public class BallField extends JPanel {
public BallField(int width, int height)
{
setSize(new Dimension(width, height));
setMinimumSize(new Dimension(width, height));
setMaximumSize(new Dimension(width, height));
bfList = new LinkedList<Shape>();
fillList();
}
public void detectCollision()
{
if(bfList.size() == 0)
{
fillList();
}
bfBall.move();
ListIterator iter = bfList.listIterator();
boolean collision = false;
while ((collision == false && iter.hasNext()))
{
Shape sh = (Shape) iter.next();
if(sh.collide(bfBall))
{
iter.remove();
collision = true;
}
}
}
public void PaintComponent(Graphics gfx)
{
int bWidth = getWidth();
int bHeight = getHeight();
gfx.setColor(bfBackground);
gfx.fillRect(0, 0, bWidth, bHeight);
ListIterator iter = bfList.listIterator();
while(iter.hasNext())
{
Shape sh = (Shape) iter.next();
sh.drawShape(gfx);
}
bfBall.drawBall(gfx);
}
private void fillList() {
int bWidth = getWidth();
int bHeight = getHeight();
int size = Math.min(bWidth, bHeight);
size -= Math.max(bfNumRow, bfNumCol) * brickGap;
size = size / (Math.max(bfNumRow, bfNumCol) + bfEmptyRow);
// add more margin
Shape.setSize(size);
if (bfBall == null) {
bfBall = new MovingBall(size, bWidth, bHeight);
} else {
bfBall.reset();
}
for (int rowCnt = 0; rowCnt < bfNumRow; rowCnt++) {
int xloc = bWidth / 2 - (bfNumRow / 2 - rowCnt) * (size + brickGap);
for (int colCnt = 0; colCnt < bfNumCol; colCnt++) {
double rand = Math.random();
Float cR = new Float(Math.random());
Float cG = new Float(Math.random());
Float cB = new Float(Math.random());
Color bc = new Color(cR.floatValue(), cG = cG.floatValue(), cB.floatValue());
int yloc = bHeight / 2 - (bfNumCol / 2 - colCnt) * (size + brickGap);
if (rand > .5) {
Circle cb = new Circle(xloc, yloc, bc);
bfList.add(cb);
} else {
Square sb = new Square(xloc, yloc, bc);
bfList.add(sb);
}
}
}
}
LinkedList<Shape> bfList;
MovingBall bfBall;
static private final Color bfBackground = Color.white;
static private final int bfNumRow = 6;
static private final int bfNumCol = 6;
static private final int bfEmptyRow = 4;
static private final int brickGap = 4;
}
Vector2D.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
/**
*
* #author PC
*/
public class Vector2D {
public Vector2D(double x, double y, boolean u)
{
xVal = x;
yVal = y;
unit = u;
if(unit)
{
makeUnit();
}
}
public void add(Vector2D vec)
{
xVal += vec.xVal;
yVal += vec.yVal;
if(unit)
{
makeUnit();
}
}
public double getX()
{
return xVal;
}
public double getY()
{
return yVal;
}
public void reflect(Vector2D nor)
{
if((unit == false) || (nor.unit == false))
{
System.out.println("ERROR, not unit vector");
}
double ip = innerProduct(nor);
xVal += -2 * ip * nor.xVal;
yVal += -2 * ip * nor.yVal;
makeUnit();
}
private double innerProduct(Vector2D vec)
{
return (xVal * vec.xVal + yVal * vec.yVal);
}
private void makeUnit()
{
double mag = xVal * xVal + yVal * yVal;
if(mag == 0)
{
System.out.println("ERROR, zero vector");
}
else
{
mag = Math.sqrt(mag);
xVal /= mag;
yVal /= mag;
}
}
private double xVal;
private double yVal;
private boolean unit;
}
MovingBall.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
/**
*
* #author PC
*/
public class MovingBall {
public MovingBall(int sz, int bw, int bh)
{
mbSize = sz;
bWidth = bw;
bHeight = bh;
reset();
}
public Rectangle getRect()
{
return (new Rectangle((int) Math.round(mbLoc.getX() - mbSize/2), (int) Math.round(mbLoc.getY() - mbSize/2), mbSize, mbSize));
}
public void reset()
{
mbLoc = new Vector2D(mbSize, mbSize, false);
mbVel = new Vector2D(Math.random() + .1, Math.random() + .1, true);
}
public double getX()
{
return mbLoc.getX();
}
public double getY()
{
return mbLoc.getY();
}
public int getSize()
{
return mbSize;
}
public void reflect(Vector2D nor)
{
mbVel.reflect(nor);
}
public void move()
{
mbLoc.add(mbVel);
// hit a wall?
if(mbLoc.getX() >= (bWidth - mbSize/2))
{
// hit right wall
Vector2D nor = new Vector2D(-1, 0, true);
reflect(nor);
}
if(mbLoc.getY() >= (bHeight - mbSize/2))
{
Vector2D nor = new Vector2D(0, -1, true);
reflect(nor);
}
}
public void drawBall(Graphics gfx)
{
int x = (int) Math.round(mbLoc.getX() - mbSize/2);
int y = (int) Math.round(mbLoc.getY() - mbSize/2);
gfx.setColor(bColor);
gfx.fillOval(x, y, mbSize, mbSize);
}
private Vector2D mbLoc;
private Vector2D mbVel;
private int mbSize;
private Color bColor = Color.black;
int bWidth;
int bHeight;
}
Shape.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import java.awt.Color;
import java.awt.Graphics;
/**
*
* #author PC
*/
abstract public class Shape {
public Shape(int x, int y, Color c)
{
s_X = x;
s_Y = y;
s_Color = c;
}
public static void setSize(int sz)
{
s_Size = sz;
}
abstract public boolean collide(MovingBall ball);
abstract public void drawShape(Graphics gfx);
protected int s_X;
protected int s_Y;
protected Color s_Color;
protected static int s_Size;
}
Square.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import static collisiondetection.Shape.s_Size;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
/**
*
* #author PC
*/
public class Square extends Shape {
public Square(int x, int y, Color c)
{
super(x, y, c);
}
public boolean collide(MovingBall ball)
{
Rectangle r1 = new Rectangle(s_X - s_Size / 2, s_Y - s_Size, s_Size, s_Size);
Rectangle r2 = ball.getRect();
Rectangle r3 = r1.intersection(r2);
if (r3.isEmpty())
{
// no collision
// note thatr3 is not null
return false;
}
if (r3.getWidth() < r3.getHeight())
{
// hit horizontally
if (ball.getX() < s_X) {
// hit the left side
Vector2D nor = new Vector2D(-1, 0, true);
ball.reflect(nor);
} else {
Vector2D nor = new Vector2D(1, 0, true);
ball.reflect(nor);
}
} else {
if (ball.getY() < s_Y) {
// hit the top
Vector2D nor = new Vector2D(0, -1, true);
ball.reflect(nor);
} else {
Vector2D nor = new Vector2D(0, 1, true);
ball.reflect(nor);
}
}
return true;
}
public void drawShape(Graphics gfx)
{
gfx.setColor(s_Color);
gfx.fillRect(s_X - s_Size/2, s_Y - s_Size/2, s_Size, s_Size);
}
}
Cirlce.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import java.awt.Color;
import java.awt.Graphics;
/**
*
* #author PC
*/
public class Circle extends Shape{
public Circle(int x, int y, Color c)
{
super(x, y, c);
}
public boolean collide(MovingBall ball)
{
double deltaX = ball.getX() - s_X;
double deltaY = ball.getY() - s_Y;
double centerDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if(centerDistance * 2 > s_Size + ball.getSize())
{
// no collision
// size is the diameter, not radius
return false;
}
Vector2D nor = new Vector2D(deltaX, deltaY, true);
ball.reflect(nor);
return true;
}
public void drawShape(Graphics gfx)
{
gfx.setColor(s_Color);
gfx.fillOval(s_X - s_Size/2, s_Y - s_Size/2, s_Size, s_Size);
}
}
You're importing the wrong ActionEvent.
import javafx.event.ActionEvent;
should be
import java.awt.event.ActionEvent;
... and welcome to this site, and thanks for providing the offending code, the error message, and the line that causes the error. I predict that you will go far with your coding.

Java LWJGL OPENGL trying to render .obj file with no success

I'm trying to render a 3D model of a bunny.
I tried to render the .obj file but nothing shows up on my screen, here's the code :
MainDisplay class :
Code:
package com.dryadengine.gui;
import com.dryadengine.core.Model;
import com.dryadengine.framework.OBJLoader;
import com.dryadengine.framework.ShaderFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.LWJGLException;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import org.lwjgl.util.vector.Matrix4f;
/**
*
* #author Roy
*/
public class MainDisplay {
private Model bunny;
private Matrix4f mProjection;
private Matrix4f mView;
private Matrix4f mModel;
private int shaderProgramID;
private int vboID;
private int vPositionID;
private int mProjectionID;
private int mViewID;
private int mModelID;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
MainDisplay md = new MainDisplay();
md.create();
md.init();
md.run();
}
public MainDisplay() {
}
public void create() {
try {
Display.setDisplayMode(new DisplayMode(800, 600));
Display.setTitle("Dryad Engine 1.0.0");
Display.setFullscreen(false);
Display.setResizable(true);
Display.create();
} catch (LWJGLException ex) {
Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
System.exit(-1);
}
}
public void init() {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
try {
shaderProgramID = ShaderFactory.createShaderProgram("vertexShader", "fragmentShader");
glUseProgram(shaderProgramID);
bunny = OBJLoader.parseOBJ(new File("src/com/dryadengine/assets/bunny.obj"));
FloatBuffer vbo = BufferUtils.createFloatBuffer(bunny.getVertices().length);
vbo.put(bunny.getVertices());
vbo.flip();
vboID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, vbo, GL_STATIC_DRAW);
vPositionID = glGetAttribLocation(shaderProgramID, "vPosition");
glEnableVertexAttribArray(vPositionID);
mProjection = new Matrix4f();
float fieldOfView = 60f;
float aspectRatio = (float)Display.getWidth() / (float)Display.getHeight();
float nearPlane = 0.1f;
float farPlane = 100f;
float yScale = (float)(1.0f / Math.tan((fieldOfView / 2.0f) * Math.PI / 180));//this.coTangent(this.degreesToRadians(fieldOfView / 2f));
float xScale = yScale / aspectRatio;
float frustum_length = farPlane - nearPlane;
mProjection.m00 = xScale;
mProjection.m11 = yScale;
mProjection.m22 = -((farPlane + nearPlane) / frustum_length);
mProjection.m23 = -1;
mProjection.m32 = -((2 * nearPlane * farPlane) / frustum_length);
mProjection.m33 = 0;
mView = new Matrix4f();
mView.m23 = -5;
mModel = new Matrix4f();
mProjectionID = glGetUniformLocation(shaderProgramID, "mProjection");
mViewID = glGetUniformLocation(shaderProgramID, "mView");
mModelID = glGetUniformLocation(shaderProgramID, "mModel");
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glEnable(GL_DEPTH_TEST);
} catch (FileNotFoundException ex) {
Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void run() {
while (!Display.isCloseRequested()) {
if (Display.isVisible()) {
render();
}
if (Display.wasResized()) {
reshape();
}
Display.update();
Display.sync(60);
}
destroy();
}
public void render() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
FloatBuffer fb1 = BufferUtils.createFloatBuffer(16);
FloatBuffer fb2 = BufferUtils.createFloatBuffer(16);;
FloatBuffer fb3 = BufferUtils.createFloatBuffer(16);;
mProjection.store(fb1);
mView.store(fb2);
mModel.store(fb3);
glUniformMatrix4(mProjectionID, true, fb1);
glUniformMatrix4(mViewID, true, fb2);
glUniformMatrix4(mModelID, true, fb3);
for (int i = 0; i < bunny.getVertices().length / 3; i += 3) {
glVertexAttribPointer(vPositionID, 3, GL_FLOAT, false, 0, i);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
}
public void reshape() {
glViewport(0, 0, Display.getWidth(), Display.getHeight());
}
public void dispose() {
glDeleteProgram(shaderProgramID);
glUseProgram(0);
glDeleteBuffers(vboID);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
public void destroy() {
Display.destroy();
}
}
ShaderFactory class :
package com.dryadengine.framework;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
/**
*
* #author Roy
*/
public class ShaderFactory {
private static final String COMMON_SHADERS_PATH = "/com/dryadengine/shaders/";
private static final String SHADER_EXTENSION = ".dsf";
/**
*
* #param vertexShaderName
* #param fragmentShaderName
* #return a shader program
* #throws FileNotFoundException
* #throws IOException
*/
public static int createShaderProgram(String vertexShaderName, String fragmentShaderName) throws FileNotFoundException, IOException {
ArrayList<Integer> shaders = new ArrayList();
shaders.add(ShaderFactory.compileShader(GL_VERTEX_SHADER, getShaderFileCode(COMMON_SHADERS_PATH + vertexShaderName + SHADER_EXTENSION)));
shaders.add(ShaderFactory.compileShader(GL_FRAGMENT_SHADER, getShaderFileCode(COMMON_SHADERS_PATH + fragmentShaderName + SHADER_EXTENSION)));
return ShaderFactory.linkProgram(shaders);
}
/**
*
* #param shaderFilePath
* #return a shader file code
* #throws FileNotFoundException
* #throws IOException
*/
private static String getShaderFileCode(String shaderFilePath) throws FileNotFoundException, IOException {
StringBuilder shaderCode = new StringBuilder();
String line;
try {
try (BufferedReader br = new BufferedReader(new InputStreamReader(ShaderFactory.class.getResourceAsStream(shaderFilePath)))) {
while ((line = br.readLine()) != null) {
shaderCode.append(line).append("\n");
}
}
} catch (FileNotFoundException e) {
throw new FileNotFoundException(e.getMessage());
}
return shaderCode.toString();
}
/**
*
* #param shaderType
* #param shaderCode
* #return a compiled shader file id
*/
public static int compileShader(int shaderType, String shaderCode) {
int shaderID = glCreateShader(shaderType);
glShaderSource(shaderID, shaderCode);
glCompileShader(shaderID);
int status = glGetShaderi(shaderID, GL_COMPILE_STATUS);
if (status == GL_FALSE) {
glDeleteShader(shaderID);
throw new RuntimeException(glGetShaderInfoLog(shaderID, glGetShaderi(shaderID, GL_INFO_LOG_LENGTH)));
}
return shaderID;
}
/**
* Link the vertex shader and the fragment shader to the shader program
* #param shaders
* #return a shader program
*/
public static int linkProgram(ArrayList <Integer> shaders) {
int shaderProgramID = glCreateProgram();
for (Integer shader : shaders) {
glAttachShader(shaderProgramID, shader);
}
glLinkProgram(shaderProgramID);
int status = glGetProgrami(shaderProgramID, GL_LINK_STATUS);
if (status == GL_FALSE) {
glDeleteProgram(shaderProgramID);
throw new RuntimeException(glGetShaderInfoLog(shaderProgramID, glGetProgrami(shaderProgramID, GL_INFO_LOG_LENGTH)));
}
for (int shader : shaders) {
glDeleteShader(shader);
}
return shaderProgramID;
}
}
OBJLoader class :
package com.dryadengine.framework;
import com.dryadengine.core.Model;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
*
* #author Roy
*/
public class OBJLoader {
/**
* Parse .obj file and make a model from it.
* #param f
* #return a model object
* #throws FileNotFoundException
* #throws IOException
*/
public static Model parseOBJ(File f) throws FileNotFoundException, IOException {
BufferedReader br = new BufferedReader(new FileReader(f));
String line;
Model m;
List<Float> vertices = new ArrayList<>();
List<Float> normals = new ArrayList<>();
while ((line = br.readLine()) != null) {
if (line.startsWith("v")) {
float x = Float.valueOf(line.split(" ")[1]);
float y = Float.valueOf(line.split(" ")[2]);
float z = Float.valueOf(line.split(" ")[3]);
vertices.add(x);
vertices.add(y);
vertices.add(z);
} else if (line.startsWith("vn")) {
float x = Float.valueOf(line.split(" ")[1]);
float y = Float.valueOf(line.split(" ")[2]);
float z = Float.valueOf(line.split(" ")[3]);
normals.add(x);
normals.add(y);
normals.add(z);
}
}
br.close();
float[] a = new float[vertices.size()];
float[] b = new float[normals.size()];
for (int i = 0; i < vertices.size(); i++) {
a[i] = vertices.get(i);
}
for (int i = 0; i < normals.size(); i++) {
b[i] = normals.get(i);
}
m = new Model(a, b);
return m;
}
}
Model class :
package com.dryadengine.core;
/**
*
* #author Roy
*/
public class Model {
private float[] vertices;
private float[] normals;
/**
* Construct a new model object.
* #param vertices
* #param normals
*/
public Model(float[] vertices, float[] normals) {
this.vertices = vertices;
this.normals = normals;
}
/**
*
* #return the model vertices array
*/
public float[] getVertices() {
return vertices;
}
/**
*
* #return the model normals array
*/
public float[] getNormals() {
return normals;
}
}
vertex shader code :
#version 330
uniform mat4 mProjection;
uniform mat4 mView;
uniform mat4 mModel;
in vec4 vPosition;
void main()
{
gl_Position = mProjection * mView * mModel * vPosition;
}
fragment shader code :
#version 330
out vec4 color;
void main()
{
color = vec4(1.0, 0.0, 0.0, 1.0);
}
Result is just black screen
I'm not sure if anything else is wrong since I'm not too experienced with GL, but I noticed that in your OBJLoader class, you're checking if a line starts with 'v' first.
if (line.startsWith("v")) {
} else if (line.startsWith("vn")) {
}
This means that if it detects a 'vn', then it will be handled by the first part of the statement since 'vn' starts with 'v'. The second part of the statement never gets executed. You could either check for 'vn' first, or add a space after 'v', checking for 'v '.
You are doing something very strange in your render (...) method; issuing 1 draw call per-triangle.
With the way your data is laid out, you should be able to draw all of your triangles in a single call if you pass the number of vertices in the array instead of setting up a different vertex pointer and passing the value 3 for each triangle.
glVertexAttribPointer (vPositionID, 3, GL_FLOAT, false, 0, 0);
glDrawArrays (GL_TRIANGLES, 0, bunny.getVertices ().length / 3);
You are also telling GL to transpose your matrices by passing true to glUniformMatrix4. If you transpose your matrices, then you need to reverse the order of your matrix multiplication in the vertex shader for proper transformation.
void main()
{
gl_Position = vPosition * mModel * mView * mProjection;
}
This runs counter to everything you should be learning in OpenGL (column-major matrices), so rather than modifying your vertex shader that way you should really just stop passing true.

Java call to create new Area doesn't create shape?

I was working on an issue where I was getting a cast problem trying to cast from Shape to Area (see previous post cast exception question). Now it seems that my shape that is create is not getting created correctly. Instead of posting all of my source code here I am attaching a link to all the source files here.
Essentially I create the shape as follows with a standard call of
YingYang shape = new YingYang();
shape = shape.moveTo(x, y);
shape = shape.scaleBy(size);
shape.setColor(getNextColor());
and the calls to the Area Class are:
public YingYang()
{
Area mainCircle = new Area(new Ellipse2D.Double(...)
...
yingYang.add(mainCircle);
}
The MoveTo call:
public YingYang moveTo(double x, double y)
{
at.translate(x, y);
at.setToTranslation(x, y);
yingYang.transform(at);
return new YingYang(at.createTransformedShape(yingYang));
}
The ScaleBy:
public YingYang scaleBy(double scale)
{
double cx = this.getBounds2D().getCenterX();
double cy = this.getBounds2D().getCenterY();
at.translate(cx, cy);
at.setToTranslation(cx, cy);
at.scale(scale, scale);
at.translate(-cx, -cy);
return new YingYang(at.createTransformedShape(yingYang));
}
When I call the paintComponent() in my drawing panel:
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for(YingYang s : shapes)
{
System.out.println(s.getBounds2D());
g2.setColor(s.getColor());
g2.fill(s);
}
}
The print statement prints out:
java.awt.geom.Rectangle2D$Double[x=0.0,y=0.0,w=0.0,h=0.0]
I'm at a loss... Any Ideas?
It looks like you have combined both my recommendations into one piece of code. If you are going to use your variable yingYang then you should implement the shape on the class. However if you are going to extend the area you need to remove the yingYang variable and use the class as the area eg: yingYang.add(mainCircle); becomes add(mainCircle);... essentially remove all references of the yingYang variable.
So instead of the "yingYang" variable you are using "this". heres is a modified version of your YingYang class with the references removed.
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
public class YingYang extends Area
{
AffineTransform at = new AffineTransform();
private boolean movingRight = true;
private boolean movingUp = true;
private Color color = Color.BLACK;
private int dx = 10, dy = 10;
public YingYang(Shape shape)
{
super(shape);
}
public YingYang()
{
// Construct the Outer Circle & Lower Dot
Area mainCircle = new Area(new Ellipse2D.Double(-210, -210, 420, 420));
Area lowerDot = new Area(new Ellipse2D.Double(-10, 90, 40, 40));
mainCircle.subtract(lowerDot);
// Begin Construction of the whit side of symbol
Area whiteSide = new Area(new Ellipse2D.Double(-200, -200, 400, 400));
Area rect = new Area(new Rectangle2D.Double(0, -200, 200, 400));
whiteSide.subtract(rect);
// Construct the upper white Circle
Area upperCircle = new Area(new Ellipse2D.Double(-100, -200, 200, 200));
whiteSide.add(upperCircle);
// Construct the Upper Dot
Area upperDot = new Area(new Ellipse2D.Double(-10, -110, 40, 40));
whiteSide.subtract(upperDot);
// Remove the lower circle portion
Area lowerCircle = new Area(new Ellipse2D.Double(-100, 0, 200, 200));
whiteSide.subtract(lowerCircle);
// Add Main Circle
add(mainCircle);
// Subtract the white side
subtract(whiteSide);
}
//------------------------ Methods -----------------------------------------
/**
* Sets this shapes color
* (must call getColor before drawing this shape)
* #param color
*/
public void setColor(Color color)
{
this.color = color;
}
/**
* Gets this shapes current color
* #return color
*/
public Color getColor()
{
return this.color;
}
/**
* Determines if the shape is moving left to right
* #return - boolean
*/
public boolean isMovingRight()
{
return movingRight;
}
/**
* Determines if the shape is moving from down to up
* #return - boolean
*/
public boolean isMovingUp()
{
return movingUp;
}
/**
* Changes the Horizontal Path that this shape is traveling
*/
public void changeHorizonalMovement()
{
if(isMovingRight())
{
movingRight = false;
}
else
{
movingRight = true;
}
}
/**
* Changes the Vertical Path that this shape is traveling
*/
public void changeVerticalMovement()
{
if(isMovingUp())
{
movingUp = false;
}
else
{
movingUp = true;
}
}
/**
* Sets the direction of the Horizontal Path of this shape
* true = left to right : false = right to left
* #param dir - boolean
*/
public void setHorizonalMovement(boolean dir)
{
this.movingRight = dir;
}
/**
* Sets the direction of the Vertical Path of this shape
* true = down to up : false = up to down
* #param dir - boolean
*/
public void setVerticalMovement(boolean dir){
this.movingUp = dir;
}
/**
* Moves the current shape by the amount x,y
* #param x - double
* #param y - double
*/
public YingYang moveTo(double x, double y)
{
at.translate(x, y);
at.setToTranslation(x, y);
transform(at);
return new YingYang(at.createTransformedShape(this));
}
/**
* Rotate this shape
* #param theta - amount to rotate shape by
* #return
*/
public YingYang rotate(double theta)
{
double cx = getBounds2D().getCenterX();
double cy = getBounds2D().getCenterY();
at.translate(cx, cy);
at.setToTranslation(cx, cy);
at.rotate(Math.toRadians(theta));
at.translate(-cx, -cy);
return new YingYang(at.createTransformedShape(this));
}
public YingYang moveToAndRotate(double x, double y, double theta)
{
double cx = getBounds2D().getCenterX();
double cy = getBounds2D().getCenterY();
at.translate(cx, cy);
at.setToTranslation(cx, cy);
at.translate(x, y);
at.rotate(Math.toRadians(theta));
at.translate(-cx, -cy);
return new YingYang(at.createTransformedShape(this));
}
/**
* Scales this shape uniformly by the amount of scale
* about the origin
* #param scale - double
*/
public YingYang scaleBy(double scale)
{
double cx = this.getBounds2D().getCenterX();
double cy = this.getBounds2D().getCenterY();
at.translate(cx, cy);
at.setToTranslation(cx, cy);
at.scale(scale, scale);
at.translate(-cx, -cy);
return new YingYang(at.createTransformedShape(this));
}
/**
* Rotates this shape theta degrees about the origin
*/
public YingYang rotate(Double theta)
{
double cx = this.getBounds2D().getCenterX();
double cy = this.getBounds2D().getCenterY();
at.translate(cx, cy);
at.setToTranslation(cx, cy);
at.rotate(Math.toRadians(theta));
at.translate(-cx, -cy);
return new YingYang(at.createTransformedShape(this));
}
public int getDx()
{
return this.dx;
}
public void setDx(int x)
{
this.dx = x;
}
public int getDy()
{
return this.dy;
}
public void setDy(int y)
{
this.dy = y;
}
}

Create a marker / crosshair in a CombinedDomainXYPlot

I want to show in a CombinedDomainXYPlot a marker. This is not working. I can add the marker for each subplot. But I want to add it for the CombinedDomainXYPlot. Anybody who can tell me something about this issue. I think with the crosshair the behaviour is the same.
This is working example which creates a CombinedDomainXYPlot and tries to add a marker. The marker adding is in createCombinedChart()
import java.awt.Font;
import javax.swing.JPanel;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYTextAnnotation;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.IntervalMarker;
import org.jfree.chart.plot.Marker;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
/**
* A demonstration application showing how to create a combined chart.
*/
public class MarkerDemo2 extends ApplicationFrame {
/**
* Constructs a new demonstration application.
*
* #param title the frame title.
*/
public MarkerDemo2(String title) {
super(title);
JFreeChart chart = createCombinedChart();
ChartPanel panel = (ChartPanel) createDemoPanel();
panel.setPreferredSize(new java.awt.Dimension(500, 270));
setContentPane(panel);
}
/**
* Creates a combined chart.
*
* #return The combined chart.
*/
private static JFreeChart createCombinedChart() {
// create subplot 1...
XYDataset data1 = createDataset1();
XYItemRenderer renderer1 = new StandardXYItemRenderer();
NumberAxis rangeAxis1 = new NumberAxis("Range 1");
XYPlot subplot1 = new XYPlot(data1, null, rangeAxis1, renderer1);
subplot1.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
// add secondary axis
subplot1.setDataset(1, createDataset2());
NumberAxis axis2 = new NumberAxis("Range Axis 2");
axis2.setAutoRangeIncludesZero(false);
subplot1.setRangeAxis(1, axis2);
subplot1.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_RIGHT);
subplot1.setRenderer(1, new StandardXYItemRenderer());
subplot1.mapDatasetToRangeAxis(1, 1);
XYTextAnnotation annotation = new XYTextAnnotation("Hello!", 50.0, 10000.0);
annotation.setFont(new Font("SansSerif", Font.PLAIN, 9));
annotation.setRotationAngle(Math.PI / 4.0);
subplot1.addAnnotation(annotation);
// create subplot 2...
XYDataset data2 = createDataset2();
XYItemRenderer renderer2 = new StandardXYItemRenderer();
NumberAxis rangeAxis2 = new NumberAxis("Range 2");
rangeAxis2.setAutoRangeIncludesZero(false);
XYPlot subplot2 = new XYPlot(data2, null, rangeAxis2, renderer2);
subplot2.setRangeAxisLocation(AxisLocation.TOP_OR_LEFT);
// parent plot...
CombinedDomainXYPlot plot = new CombinedDomainXYPlot(new NumberAxis("Domain"));
plot.setGap(10.0);
// add the subplots...
plot.add(subplot1, 1);
plot.add(subplot2, 1);
plot.setOrientation(PlotOrientation.VERTICAL);
// Add marker
Marker marker = new IntervalMarker(30, 40);
plot.addDomainMarker(marker);
// Working
// subplot1.addDomainMarker(marker);
// return a new chart containing the overlaid plot...
JFreeChart chart = new JFreeChart("CombinedDomainXYPlot Demo",
JFreeChart.DEFAULT_TITLE_FONT, plot, true);
ChartUtilities.applyCurrentTheme(chart);
return chart;
}
/**
* Creates a sample dataset.
*
* #return Series 1.
*/
private static XYDataset createDataset1() {
// create dataset 1...
XYSeries series1 = new XYSeries("Series 1a");
series1.add(10.0, 12353.3);
series1.add(20.0, 13734.4);
series1.add(30.0, 14525.3);
series1.add(40.0, 13984.3);
series1.add(50.0, 12999.4);
series1.add(60.0, 14274.3);
series1.add(70.0, 15943.5);
series1.add(80.0, 14845.3);
series1.add(90.0, 14645.4);
series1.add(100.0, 16234.6);
series1.add(110.0, 17232.3);
series1.add(120.0, 14232.2);
series1.add(130.0, 13102.2);
series1.add(140.0, 14230.2);
series1.add(150.0, 11235.2);
XYSeries series1b = new XYSeries("Series 1b");
series1b.add(10.0, 15000.3);
series1b.add(20.0, 11000.4);
series1b.add(30.0, 17000.3);
series1b.add(40.0, 15000.3);
series1b.add(50.0, 14000.4);
series1b.add(60.0, 12000.3);
series1b.add(70.0, 11000.5);
series1b.add(80.0, 12000.3);
series1b.add(90.0, 13000.4);
series1b.add(100.0, 12000.6);
series1b.add(110.0, 13000.3);
series1b.add(120.0, 17000.2);
series1b.add(130.0, 18000.2);
series1b.add(140.0, 16000.2);
series1b.add(150.0, 17000.2);
XYSeriesCollection collection = new XYSeriesCollection();
collection.addSeries(series1);
collection.addSeries(series1b);
return collection;
}
/**
* Creates a sample dataset.
*
* #return A sample dataset.
*/
private static XYDataset createDataset2() {
// create dataset 2...
XYSeries series2 = new XYSeries("Series 2");
series2.add(10.0, 6853.2);
series2.add(20.0, 9642.3);
series2.add(30.0, 8253.5);
series2.add(40.0, 5352.3);
series2.add(50.0, 3532.0);
series2.add(60.0, 2635.3);
series2.add(70.0, 3998.2);
series2.add(80.0, 1943.2);
series2.add(90.0, 6943.9);
series2.add(100.0, 7843.2);
series2.add(105.0, 6495.3);
series2.add(110.0, 7943.6);
series2.add(115.0, 8500.7);
series2.add(120.0, 9595.9);
return new XYSeriesCollection(series2);
}
/**
* Creates a panel for the demo (used by SuperDemo.java).
*
* #return A panel.
*/
public static JPanel createDemoPanel() {
JFreeChart chart = createCombinedChart();
ChartPanel panel = new ChartPanel(chart);
panel.setMouseWheelEnabled(true);
return new ChartPanel(chart);
}
/**
* Starting point for the demonstration application.
*
* #param args ignored.
*/
public static void main(String[] args) {
MarkerDemo2 demo = new MarkerDemo2(
"JFreeChart: CombinedDomainXYPlotDemo4.java");
demo.pack();
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
}
}
drawAnnotations is implemented in XYPlot, unfortunately CombinedDomainXYPlot subclasses XYPlot but doesn't call drawAnnotations in its overridden implementation of draw or forward the annotation on to the subplots.
You could possible provide your on implemntation of addAnnotation and removeAnnotation by subclassing CombinedDomainXYPlot.
Thanks to Graham I developed an adjusted version of CombinedDomainXYPlot, which works for me. The result looks like this.
I merged Code from XYPlot and XYLineAndShapeRenderer to achieve this.
Here is the source code.
import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jfree.chart.annotations.XYAnnotation;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.IntervalMarker;
import org.jfree.chart.plot.Marker;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.PlotState;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.Range;
import org.jfree.text.TextUtilities;
import org.jfree.ui.GradientPaintTransformer;
import org.jfree.ui.Layer;
import org.jfree.ui.LengthAdjustmentType;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleInsets;
public class MyCombinedDomainXYPlot extends CombinedDomainXYPlot {
public MyCombinedDomainXYPlot() {
super();
// TODO Auto-generated constructor stub
}
public MyCombinedDomainXYPlot(ValueAxis domainAxis) {
super(domainAxis);
// TODO Auto-generated constructor stub
}
#Override
public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
PlotState parentState, PlotRenderingInfo info) {
super.draw(g2, area, anchor, parentState, info);
drawDomainMarkers(g2, area, 0, Layer.FOREGROUND);
}
/**
* Draws the domain markers (if any) for an axis and layer. This method is
* typically called from within the draw() method.
*
* #param g2 the graphics device.
* #param dataArea the data area.
* #param index the renderer index.
* #param layer the layer (foreground or background).
*/
#Override
protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea,
int index, Layer layer) {
// check that the renderer has a corresponding dataset (it doesn't
// matter if the dataset is null)
if (index >= getDatasetCount()) {
return;
}
Collection markers = getDomainMarkers(index, layer);
ValueAxis axis = getDomainAxisForDataset(index);
if (markers != null && axis != null) {
Iterator iterator = markers.iterator();
while (iterator.hasNext()) {
Marker marker = (Marker) iterator.next();
drawDomainMarker(g2, marker, dataArea);
}
}
}
/**
* Draws a vertical line on the chart to represent a 'range marker'.
*
* #param g2
* the graphics device.
* #param plot
* the plot.
* #param domainAxis
* the domain axis.
* #param marker
* the marker line.
* #param dataArea
* the axis data area.
*/
public void drawDomainMarker(Graphics2D g2, Marker marker, Rectangle2D dataArea) {
ValueAxis domainAxis = getDomainAxis();
if (marker instanceof ValueMarker) {
ValueMarker vm = (ValueMarker) marker;
double value = vm.getValue();
Range range = domainAxis.getRange();
if (!range.contains(value)) {
return;
}
double v = domainAxis.valueToJava2D(value, dataArea,
getDomainAxisEdge());
PlotOrientation orientation = getOrientation();
Line2D line = null;
if (orientation == PlotOrientation.HORIZONTAL) {
line = new Line2D.Double(dataArea.getMinX(), v,
dataArea.getMaxX(), v);
} else if (orientation == PlotOrientation.VERTICAL) {
line = new Line2D.Double(v, dataArea.getMinY(), v,
dataArea.getMaxY());
}
final Composite originalComposite = g2.getComposite();
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, marker.getAlpha()));
g2.setPaint(marker.getPaint());
g2.setStroke(marker.getStroke());
g2.draw(line);
String label = marker.getLabel();
RectangleAnchor anchor = marker.getLabelAnchor();
if (label != null) {
Font labelFont = marker.getLabelFont();
g2.setFont(labelFont);
g2.setPaint(marker.getLabelPaint());
Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
g2, orientation, dataArea, line.getBounds2D(),
marker.getLabelOffset(),
LengthAdjustmentType.EXPAND, anchor);
TextUtilities.drawAlignedString(label, g2,
(float) coordinates.getX(),
(float) coordinates.getY(),
marker.getLabelTextAnchor());
}
g2.setComposite(originalComposite);
} else if (marker instanceof IntervalMarker) {
IntervalMarker im = (IntervalMarker) marker;
double start = im.getStartValue();
double end = im.getEndValue();
Range range = domainAxis.getRange();
if (!(range.intersects(start, end))) {
return;
}
double start2d = domainAxis.valueToJava2D(start, dataArea,
getDomainAxisEdge());
double end2d = domainAxis.valueToJava2D(end, dataArea,
getDomainAxisEdge());
double low = Math.min(start2d, end2d);
double high = Math.max(start2d, end2d);
PlotOrientation orientation = getOrientation();
Rectangle2D rect = null;
if (orientation == PlotOrientation.HORIZONTAL) {
// clip top and bottom bounds to data area
low = Math.max(low, dataArea.getMinY());
high = Math.min(high, dataArea.getMaxY());
rect = new Rectangle2D.Double(dataArea.getMinX(), low,
dataArea.getWidth(), high - low);
} else if (orientation == PlotOrientation.VERTICAL) {
// clip left and right bounds to data area
low = Math.max(low, dataArea.getMinX());
high = Math.min(high, dataArea.getMaxX());
rect = new Rectangle2D.Double(low, dataArea.getMinY(), high
- low, dataArea.getHeight());
}
final Composite originalComposite = g2.getComposite();
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, marker.getAlpha()));
Paint p = marker.getPaint();
if (p instanceof GradientPaint) {
GradientPaint gp = (GradientPaint) p;
GradientPaintTransformer t = im
.getGradientPaintTransformer();
if (t != null) {
gp = t.transform(gp, rect);
}
g2.setPaint(gp);
} else {
g2.setPaint(p);
}
g2.fill(rect);
// now draw the outlines, if visible...
if (im.getOutlinePaint() != null
&& im.getOutlineStroke() != null) {
if (orientation == PlotOrientation.VERTICAL) {
Line2D line = new Line2D.Double();
double y0 = dataArea.getMinY();
double y1 = dataArea.getMaxY();
g2.setPaint(im.getOutlinePaint());
g2.setStroke(im.getOutlineStroke());
if (range.contains(start)) {
line.setLine(start2d, y0, start2d, y1);
g2.draw(line);
}
if (range.contains(end)) {
line.setLine(end2d, y0, end2d, y1);
g2.draw(line);
}
} else { // PlotOrientation.HORIZONTAL
Line2D line = new Line2D.Double();
double x0 = dataArea.getMinX();
double x1 = dataArea.getMaxX();
g2.setPaint(im.getOutlinePaint());
g2.setStroke(im.getOutlineStroke());
if (range.contains(start)) {
line.setLine(x0, start2d, x1, start2d);
g2.draw(line);
}
if (range.contains(end)) {
line.setLine(x0, end2d, x1, end2d);
g2.draw(line);
}
}
}
String label = marker.getLabel();
RectangleAnchor anchor = marker.getLabelAnchor();
if (label != null) {
Font labelFont = marker.getLabelFont();
g2.setFont(labelFont);
g2.setPaint(marker.getLabelPaint());
Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
g2, orientation, dataArea, rect,
marker.getLabelOffset(),
marker.getLabelOffsetType(), anchor);
TextUtilities.drawAlignedString(label, g2,
(float) coordinates.getX(),
(float) coordinates.getY(),
marker.getLabelTextAnchor());
}
g2.setComposite(originalComposite);
}
}
/**
* Calculates the (x, y) coordinates for drawing a marker label.
*
* #param g2
* the graphics device.
* #param orientation
* the plot orientation.
* #param dataArea
* the data area.
* #param markerArea
* the rectangle surrounding the marker area.
* #param markerOffset
* the marker label offset.
* #param labelOffsetType
* the label offset type.
* #param anchor
* the label anchor.
*
* #return The coordinates for drawing the marker label.
*/
protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2,
PlotOrientation orientation, Rectangle2D dataArea,
Rectangle2D markerArea, RectangleInsets markerOffset,
LengthAdjustmentType labelOffsetType, RectangleAnchor anchor) {
Rectangle2D anchorRect = null;
if (orientation == PlotOrientation.HORIZONTAL) {
anchorRect = markerOffset.createAdjustedRectangle(markerArea,
LengthAdjustmentType.CONTRACT, labelOffsetType);
} else if (orientation == PlotOrientation.VERTICAL) {
anchorRect = markerOffset.createAdjustedRectangle(markerArea,
labelOffsetType, LengthAdjustmentType.CONTRACT);
}
return RectangleAnchor.coordinates(anchorRect, anchor);
}
}

Categories

Resources