I have been trying to align my java2d shape's center to the JPanel's center with no success. I was able to do it for an image and many 2D shapes like parallelogram using getBounds method but not for rhombus though all of them follow the same pattern. Drastically, when I prepared an SSCCE out of the actual project I could align none of them correctly.
I've written a drawShape method for drawing shapes on center. I didn't understand where I'm going wrong.
This is SSCCE:
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
public class TestPanel extends JPanel{
Point a,b,c,d;
Shape trapezium,parallelogram;
Random random=new Random();
public TestPanel(){
a=new Point();
b=new Point();
c=new Point();
d=new Point();
rhombusFactory(a,b,c,d);
trapezium=getQuadrilateral(a,b,c,d);
}
private void rhombusFactory(Point a,Point b,Point c,Point d)
{ int width=random.nextInt(200-100)+100;
int height=random.nextInt(150-50)+50;
a.x=0;
a.y=0;
b.x=a.x+width/2;
b.y=a.y+height/2;
c.x=a.x+width;
c.y=a.y;
d.x=a.x+width/2;
d.y=a.y-height/2;
}
private void parallelogramFactory(Point a,Point b,Point c,Point d){
int l1=random.nextInt(200-100)+100;
int l2=random.nextInt(150-70)+70;
int offset=(random.nextInt(2)==0?-1:1)*(random.nextInt(50-20)+20);
a.x=0;
a.y=0;
b.x=a.x+l1;
b.y=a.y;
d.x=a.x+offset;
d.y=a.y+l2;
c.x=d.x+l1;
c.y=d.y;
}
private Shape getQuadrilateral(Point a,Point b,Point c,Point d){
GeneralPath gp=new GeneralPath();
gp.moveTo(a.x,a.y);
gp.lineTo(b.x,b.y);
gp.lineTo(c.x,c.y);
gp.lineTo(d.x,d.y);
gp.closePath();
return gp;
}
private void drawShape(Graphics2D g,Shape shape){
AffineTransform oldt=g.getTransform();
Rectangle2D bounds=shape.getBounds2D();
double height=bounds.getHeight();
double width=bounds.getWidth();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
g.translate(this.getWidth()/2,this.getHeight()/2);
g.translate(-width/2,-height/2);
g.draw(shape.getBounds2D());
g.draw(shape);
g.setTransform(oldt);
}
public void paintComponent(Graphics g2){
super.paintComponent(g2);
Graphics2D g=(Graphics2D)g2;
drawShape(g,trapezium);
//drawShape(g,parallelogram);
}
public static void main(String args[]){
JFrame jf=new JFrame();
TestPanel tp=new TestPanel();
jf.setLayout(new BorderLayout());
jf.add(tp,BorderLayout.CENTER);
jf.setSize(500,500);
jf.setVisible(true);
}
}
Any help would be appreciated
EDIT:
I just removed the confusing lines out of the code...
You need to account for the bounded x/y location of the Shape:
Rectangle bounds=shape.getBounds(); // changed this
...
//g.translate(this.getWidth()/2,this.getHeight()/2);
//g.translate(-width/2,-height/2);
g.translate((this.getWidth() - width) / 2,(this.getHeight() - height) / 2);
g.translate(-bounds.x, -bounds.y); // added this
Your translate is wrong. You should first translate the origin of you drawing space to the center of the panel and then translate the center of your drawing to the origin. You did the latter right, but the first wrong.
Change
g.translate(this.getWidth(),this.getHeight());
g.translate(-width/2,-height/2);
to
g.translate(this.getWidth()/2,this.getHeight()/2);
g.translate(-width/2,-height/2);
Or just
g.translate((this.getWidth() - width) / 2,(this.getHeight() - height) / 2);
EDIT:
In method rhombusFactory(), change that:
a.y=0;
to this:
a.y=height/2;
Short expanation:
In your drawShape() method, you place the graphics context's origin where you assume that the upper left corner of the shape's bounds would be (Point: [(getWidth-width)/2, (getHeight()-height)/2]). In your rhombusFactory() method, you set point a at (0, 0), which consequently shifts your shape's upper left corner at (0,-height/2), effectively causing it's not being vertically centered.
Related
I create this to draw a fish when the mouse is pressed at the mouse's x and y coordinate. but i seems then that the drawfish method is not being called. I can't find the reason why is it is not working. I would be me very grateful for any help.
/*FishTank*/
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
/*FishTank class-contains a frame and the WinstonCanvas.*/
public class FishTank{
public static void main ( String[] args ){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
JFrame window = new JFrame();
window.setTitle("Fish Tank");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(30, 30, 700, 430);
window.getContentPane().add(new FishTankCanvas());
window.setVisible(true);
}
});
}
}
/*FishTankCanvas is a component that allows drawing shapes.*/
class FishTankCanvas extends JComponent {
static Graphics2D g;
int x = 11;
Timer myTimer;
public FishTankCanvas(){
myTimer = new Timer (2, new ActionListener(){
public void actionPerformed (ActionEvent evt){
repaint();
}
});
myTimer.start();
}
public void paint(Graphics graphics) {
g = (Graphics2D)graphics;
//makes the background white
Color backgroundColor = new Color(89, 216, 255);//light blue
g.setColor(backgroundColor);
g.fillRect(0,0,this.getWidth(),this.getHeight());
// drawfish (Graphics graphics, int bodyX, int bodyY, int bodyLength,int bodyHeight, int tailwidth, int eyesize,int tailcolor, int bodycolor)
// Mouselistener and mouseadapter
this.addMouseListener (new MouseAdapter() {
public void mousePressed(MouseEvent e) {
//call drawfish method
drawfish(FishTankCanvas.g,e.getX(), e.getY(),118,74,1,((int) (Math.random()*(4 - 0))));
repaint();
}
});
// x coordinate plus 1 of fish (animate)
x= x + 1;
}
// drawfish method
public void drawfish(Graphics graphics, int bodyX, int bodyY, int bodyLength,int bodyHeight,int tailcolor, int bodycolor ){
Graphics2D g = (Graphics2D)graphics;
bodyX +=x;
//colours
Color[] colours= new Color[5];
colours[0] = new Color(0, 0, 0);//black
colours[1] = new Color(162, 0, 255);//purple
colours[2] = Color.red;//red
colours[3] = new Color(255,255,0);// yellow
colours[4] = new Color(60,179,113);//green
//draw fish
// body
g.setColor(colours[bodycolor]);
g.fillOval(bodyX, bodyY, bodyLength, bodyHeight);
// tail
g.setColor(colours[tailcolor]);
int tailWidth = bodyLength/4;
int tailHeight = bodyHeight/2;
int[] tailPointx = new int[3];
int[] tailPointy = new int[3];
tailPointx[0]=bodyX;
tailPointy[0]=bodyY+bodyHeight/2;
tailPointx[1]=bodyX-tailWidth;
tailPointy[1]=bodyY+bodyHeight/2-tailHeight;
tailPointx[2]=bodyX-tailWidth;
tailPointy[2]=bodyY+tailHeight+tailHeight;
g.fillPolygon(tailPointx, tailPointy, 3);
// eye
g.setColor(colours[0]);
g.fillOval(bodyX+3*bodyLength/4, bodyY+bodyHeight/2-bodyHeight/5, bodyHeight/5, bodyHeight/5);
}
}
i seems then that the drawfish method is not being called.
Well that is easy enough to verify. All you need to do is add debug code to the method to determine if this is true or not. Then you can tell us if that is the problem instead of guessing.
Other problems:
Don't add the MouseListener to the component in a painting method. The listener should be added in the constructor of your class.
Don't override paint(). Custom painting is done by overriding the paintComponent() method. And don't forget to invoke super.paintComponent(...).
Extend JPanel instead of JComponent. Then you can just use the setBackground() method to paint the background.
However, the real problem is that when you click the mouse the fish might get drawn, but then the Timer does a repaint which will clear the panel 2ms later, so you never really see the fish. Get rid of the Timer. There is no need for the Timer to draw a fish.
Assuming you want to paint multiple fish you need to keep track of every place you click and then paint all the fish. The two way of doing this are:
Keep an ArrayList of the points where you want to paint the fish and then iterate through this list in your painting method
Paint the fish on a BufferedImage when the mouse click happens, and then just paint the image.
See Custom Painting Approaches for working examples of both of these approaches.
I'm using Swing to create a small GUI in Java. All I am trying to get it to do is take an ArrayListof Circles and draw them. I've run into two problems:
1) I have to call my draw method repeatedly before it draws the circle. If I just call my draw method once nothing happens, I get a blank drawing. If I call it in a loop that runs for less than 30 milliseconds it only draws the first of two circles that I want to draw. Finally, if I call it for more than 30 milliseconds it draws both circles I am trying to draw.
and
2) When I move one of the circles, I get a "flicker" on the drawing.
I'm not too familiar with Swing programming. I've looked at sample code and watched a few videos - and what I have looks right to me. But I figure I must have messed something up, because it doesn't look like this in the videos I've watched.
Here is my GUI class:
package gui;
import draw.*;
import java.util.List;
import javax.swing.*;
public class GUI extends JFrame {
private CirclePainter drawingBoard = new CirclePainter();
public GUI()
{
setSize(500, 500);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setVisible(true);
this.add(drawingBoard);
drawingBoard.setVisible(true);
}
public void draw(List<Circle> circles)
{
drawingBoard.paintComponent(drawingBoard.getGraphics(), circles);
}
}
my CirclePainter class
package gui;
import draw.Circle;
import javax.swing.*;
import java.awt.*;
import java.util.List;
class CirclePainter extends JPanel
{
public void paintComponent(Graphics graphics, List<Circle> circles)
{
super.paintComponent(graphics);
for(Circle circle : circles)
graphics.fillOval(circle.getX(), circle.getY(), circle.getRadius() * 2, circle.getRadius() * 2);
}
}
EDIT: redacted some code since this is for a school project. The remaining code should be enough for someone visiting in the future to still understand the question.
Never call paintComponent(...) directly as you're doing.
Instead suggest a draw by calling repaint() on a component when necessary.
Don't draw with a Graphics object obtained via a getGraphics() call on a component. Instead, draw with the Graphics object provided in the paintComponent method.
Avoid using while (true) loops in a Swing GUI as you risk tying up the Swing event thread and freezing the GUI. Use a Swing Timer for simple animations.
You probably don't even need a Swing Timer since your animation can be driven by your MouseListener/MouseMotionListener.
Most important -- do read the Swing painting and other tutorials, as most of this information can be found there. It looks like you're guessing how to do some of your coding and that's a dangerous thing to do when it comes to drawing or animating a GUI. You can find most tutorials in the Swing info link.
Consider using a Shape object to represent your Circle, such as an ellipse2D. The reason that this will help is that it has some very useful methods, including a contains(Point p) method that will help you determine if a mouse click lands inside of your circle.
You will want to decide where _x and _y represent the center point of your circle or not. If so, then you'll need to adjust your drawing some, by shifting it left and up by _radius amount.
Consider casting your Graphics object into a Graphics2D object in order to use its extra methods and properties.
One such property are the RenderingHings. Set your Graphics2D RenderingHints to allow for anti-aliasing to get rid of your image "jaggies". This can be done with: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON); where g2 is your Graphics2D object.
Your paintComponent method is not a true paintComponent override and thus won't work correctly. It should be a protected method, not public, it should have one parameter, a Graphics object, and nto a second parameter, and you should place the #Override annotation above it.
For example, please have a look at this answer of mine to a similar problem.
An example of a paintComponent method that centers the circles on _x and _y and that uses rendering hints:
class CirclePainter extends JPanel implements Iterable<Circle> {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private CircleList circleList = new CircleList();
#Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
Graphics2D g2 = (Graphics2D) graphics;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Circle circle : circleList) {
// if x and y are the center points, then you must subtract the radius.
int x = circle.getX() - circle.getRadius();
int y = circle.getY() - circle.getRadius();
int width = circle.getRadius() * 2;
int height = width;
g2.fillOval(x, y, width, height);
}
}
Building on your code and the suggestions from Hovercraft Full Of Eels, a small step in the right direction could be taken with these modifications to the GUI and CirclePainter classes:
// GUI.draw
public void draw(List<Circle> circles)
{
// drawingBoard.paintComponent(drawingBoard.getGraphics(), circles);
drawingBoard.setCircles(circles);
drawingBoard.repaint();
}
class CirclePainter extends JPanel
{
// public void paintComponent(Graphics graphics, List<Circle> circles)
// {
// super.paintComponent(graphics);
// for(Circle circle : circles)
// graphics.fillOval(circle.getX(), circle.getY(), circle.getRadius() * 2, circle.getRadius() * 2);
// }
private List<Circle> circles;
public void setCircles(final List<Circle> circles) {
this.circles = circles;
}
#Override
protected void paintComponent(final Graphics graphics) {
super.paintComponent(graphics);
for (Circle circle : circles)
graphics.fillOval(circle.getX(), circle.getY(), circle.getRadius() * 2, circle.getRadius() * 2);
}
}
This way, you might not have fixed all the fundamental issues, but you get your program to work with only minor changes. And Swing is a very nice library that can be much fun to learn more about.
I am having a problem with the following code. My intent is to store the coordinates of a mouse click into an arraylist using getPoint, and then draw a rectangle at each location that the user has clicked. I have searched high and low for how to extract the x and y coordinates individually from a getPoint object to no avail. I am new to java, the line that is giving me trouble at compile time is:
g2.drawRect(coordinateList(j).getHeight(),coordinateList(j.getWidth(),3,3);
I know that I am probably way off, but how can I extract the x and y coordinates of a point individually from an array list, one item of the array by one in order to repaint a rectangle at the new click point and also all the previous clicks as well?
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.event.MouseListener;
import java.awt.Point;
import java.util.*;
public class ClickCloud extends JPanel
{
private int pointxy;
//private Rectangle2D.Double r1;
private boolean mouseClick;
private int count;
//private Point[] points;
private Point coordinates = new Point(0, 0);
private ArrayList<Point> coordinateList = new ArrayList<Point>();
public ClickCloud() {
this.setPreferredSize(new Dimension(500,500));
this.addMouseListener(new MyMouseListener());
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (int j = 0; j < count; j++) {
g2.setStroke(new BasicStroke(1.0f));
g2.setPaint(Color.BLUE);
g2.drawRect(coordinateList(j).getHeight(),coordinateList(j.getWidth(),3,3);
}
}
private class MyMouseListener implements MouseListener {
public void mouseClicked(MouseEvent me) {
count++;
coordinates.setLocation(me.getPoint());
coordinateList.add(coordinates.getLocation());
repaint();
}
public void mousePressed(MouseEvent me) { }
public void mouseReleased(MouseEvent me) { }
public void mouseEntered(MouseEvent me) { }
public void mouseExited(MouseEvent me) { }
}
public static void main(String[] args) {
JFrame f = new JFrame("ClickCloud demo");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLayout(new FlowLayout());
f.add(new ClickCloud());
f.pack();
f.setVisible(true);
}
}
Thanks,
T
Forget all the getLocation and setLocation. It's redundant. Just store me.getPoint() in your coordinateList.
Then you can get the x and y coordinates with point.getX() and point.getY() respectively.
In paintComponent, there is an easier way to iterate over a list of points:
for (Point coordinate : coordinateList) { //"for each coordinate in coordinateList"
//do something with coordinate.getX() and coordinate.getY()
}
You are not getting properly the points from the ArrayList.
g2.drawRect(coordinateList(j).getHeight(),coordinateList(j.getWidth(),3,3);
To get the item at index j with an ArrayList, you simply use the method get():
Point point = coordinateList.get(j);
Then the problem is that pointonly represents, well, points... They only have X and Y coordinates, not width and height. If I try to guess what you want to do and assume that you want to draw 3x3 rectangles where the user has clicked, you would call drawRect() like this:
g2.drawRect(point.getX(), point.getY(), 3, 3);
Also:
You don't need to handle a count variable to know the number of points you have in your ArrayList. Just use the size() method of coordinateList or even better, use an enhanced for loop.
You can use MouseAdapter instead of MouseListener to only override the events you need.
You don't need the coordinates member and the get/setLocation stuff. Just write coordinateList.add(me.getPoint());
Hey guys I need help I am trying to make a program where I can draw in a window with the mouse. So far I have it to where when I click a dot appears but I need to add a drag method so that when I drag the mouse across the page it draws stuff. Can someone look at my code and help me out where you can?
Here is my code:
import javax.swing.*;
import java.awt.event.*;
public class mouse {
private static int x,y;
private static draw object = new draw ();
public static void main(String[] args){
JFrame frame = new JFrame ("Mouse");
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,400);
frame.add(object);
object.addMouseListener(new AL());
}
static class AL extends MouseAdapter{
public void mousePressed(MouseEvent e){
x = e.getX();
y = e.getY();
object.drawing(x, y);
}
public void mouseDragged( MouseEvent e) {
x= e.getX();
y= e.getY();
object.drawing(x, y);
}
}
}
and
import javax.swing.*;
import java.awt.*;
public class draw extends JPanel {
private static int x,y;
public void drawing (int xx, int yy){
x=xx;
y=yy;
repaint();
}
public void paintComponent (Graphics g){
g.setColor(Color.black);
g.fillOval(x, y, 10, 10);
}
}
One idea that I have is to add your mouse coordinates each to a separate list whenever the mouse is clicked and draw based on the size of the lists, however since you are using mouse dragged you could just use Path2D.lineTo(x, y) and use e.getX() and e.getY() for the x and y coords. After this use Path2D.moveTo(x, y) to make sure line path is appended for each pixel the mouse moves (this makes sure that each movement doesn't look like a straight line, but rather like a line moving whatever direction you're "drawing" in). Also, a few tips:
The void mouseDragged usually works better when used in mouseMotionAdapter because from my experience it usually doesn't register the event in just mouseAdapter.
Since this is a drawing program, personally I'd set a variable for the size of your circle to be used in the future if you actually are planning to expand this into something bigger (example: g.fillOval(x, y, brushSize, brushSize)).
For custom rendering, I've created a class that extends JPanel and overrides the paintComponent method. In the custom paintComponent I rendering multiple shape objects held in a array. What I would like to add is the ability to drag and select 1 or more of the shapes. While dragging I would like to show a translucent rectangle defining the selection region akin to what is seen in Windows Explorer. Can any provide a starting point for accomplishing this?
Thanks.
I saw an interesting way of doing this in JFreeChart's source code. You can draw a marquee over a section of the chart, and when you release the mouse the chart zooms in on the selected are. Re-rending the chart is expensive and unfortunately JFreeChart doesn't support partial paints of a chart. So to draw the marquee they do some sort of bitwise operation to the colors of the component, in a reversible fashion. Every time the mouse moves while selecting a marquee, you reverse the previous bitwise operation on the old coordinates, then redo it on the new coordinates.
Take a look at ChartPanel.java in JFreeChart
private void drawZoomRectangle(Graphics2D g2) {
// Set XOR mode to draw the zoom rectangle
g2.setXORMode(Color.gray);
if (this.zoomRectangle != null) {
if (this.fillZoomRectangle) {
g2.fill(this.zoomRectangle);
}
else {
g2.draw(this.zoomRectangle);
}
}
// Reset to the default 'overwrite' mode
g2.setPaintMode();
}
You could use JXLayer. Span it across the whole form and do the painting in a custom LayerUI.
All,
Thanks for the suggestions. Ended up resolving this by adapting some the code used in this rather clever demo. http://forums.sun.com/thread.jspa?threadID=5299064&start=19
public class ExamplePanel extends JPanel
{
Rectangle2D.Double selectionRect;
Point mouseDown, mouseHere;
...
protected void paintComponent(Graphics g)
{
AlphaComposite ta = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f);
g2d.setComposite(ta);
g2d.setColor(Color.BLUE);
g2d.fill(selectionRect);
g2d.setComposite(AlphaComposite.SrcOver);
g2d.setColor(Color.BLACK);
g2d.draw(selectionRect);
}
}
public class ExammpleMouseListener extends MouseAdapter
{
#Override
public void mousePressed(MouseEvent e)
{
super.mousePressed(e);
// store the mouse down location
pnl.mouseDown = e.getPoint();
}
/**
* #see java.awt.event.MouseAdapter#mouseDragged(java.awt.event.MouseEvent)
*/
#Override
public void mouseDragged(MouseEvent e)
{
super.mouseDragged(e);
// check for left mouse button
if ((e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) == 0)
{
return;
}
// store the current location
pnl.mouseHere = e.getPoint();
// calculate the size of the selection rectangle
double downX = pnl.mouseDown.getX();
double downY = pnl.mouseDown.getY();
double hereX = pnl.mouseHere.getX();
double hereY = pnl.mouseHere.getY();
double l = Math.min(downX, hereX);
double t = Math.min(downY, hereY);
double w = Math.abs(downX - hereX);
double h = Math.abs(downY - hereY);
pnl.selectionRect = new Rectangle2D.Double(l, t, w, h);
// queue a repaint of the panel
pnl.repaint();
}
#Override
public void mouseReleased(MouseEvent e)
{
super.mouseReleased(e);
// clear the selection rectangle
pnl.selectionRect = null;
// queue a repaint of the panel
pnl.repaint();
}
}
}
Sure, here's an simple example with methods for creating and moving the shape.
class MyShape implements Shape {
private Shape shape;
public void createShape(Point p1, Point p2, ShapeType t) {
switch(t) {
case RECTANGLE: {
shape = new Rectangle2D.Double(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y);
break;
}
... (other shapes)
}
}
public void moveShape(Point lastPoint, Point newPoint, ShapeType t) {
int xOffset = newPoint.x - lastPoint.x;
int yOffset = newPoint.y - lastPoint.y;
switch(t) {
case RECTANGLE: {
double x1 = shape.getBounds().getX() + xOffset;
double y1 = shape.getBounds().getY() + yOffset;
double w = shape.getBounds().getWidth();
double h = shape.getBounds().getHeight();
shape = new Rectangle2D.Double(x1, y1, w, h);
break;
}
... (other shapes)
}
}
}
For some components ( I don't know if this apply to your components while being dragged ) you can set the background and use a "transparent color"
The Color class does implements Transparency.
To use it you may specify the alpha value in the Color constructor.
For instance this is a semi transparent black background:
// 0: totally transparent
// 255: totally opaque,
// 192 semy transparent.
this.setBackground(new Color( 0, 0, 0, 192 ));
See the [constructor][1]
Again, I'm not sure if this applies to you. Give it a try
[1]: http://java.sun.com/javase/6/docs/api/java/awt/Color.html#Color(int, int, int, int)