I have a panel which is split into a number of sectors. I have attached a MouseListener to it. Each drag event generates a point, adds it to the ArrayList and calls repaint. As the mouse is dragged the mouse pattern is drawn in every sector by Looping trough the ArrayList and calling the Graphics2D rotate method. If I drag the mouse slowly I can see the points incrementally being painted on the screen. How can I fix that?
//performs the drawing on the display panel
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(penColor);
this.sectorLine = new Line2D.Double(getCenterX(), getCenterY(), getCenterX(), 0);
g2d.setClip( new Ellipse2D.Double(getCenterX() - getLineLength(), getCenterY() - getLineLength(), getLineLength()*2, getLineLength()*2));
//draws the sectors on the screen
if(drawSectors)
{
drawSectorLines(g2d);
}
for (Point point : points)
{
g2d.fillOval((int) point.getX() -4 , (int) point.getY() -4,8, 8); // has to return the size of the pen /2 methog get offset
// dva metoda za sektori i rotate
rotate(g2d,getNumberOfSectors() - 1);
}
}
//rotates each drawn point in every sector
public void rotate(Graphics2D g2d, int times)
{
g2d.setColor(penColor);
for(int i=0; i<times; i++)
{ // returns 360/number of sectors
g2d.rotate(Math.toRadians(getRotationDegrees()), getWidth()/2, getHeight()/2);
}
}
// handles drawing
private class DisplayListener extends MouseAdapter
{
private void addPoint(MouseEvent event)
{
Point point = event.getPoint();
points.add(point);
repaint();
}
public void mouseClicked(MouseEvent event)
{
addPoint(event);
}
public void mouseDragged(MouseEvent event)
{
addPoint(event);
}
}
Related
I'm using MouseMotion Listeners to add shapes to a HashSet, and then filling them in using Graphics2D. However when I move my mouse too fast the points no longer make a coherent line.
I've tried googling, but haven't found a relevant answer.
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
//points.add(new Point(e.getX(), e.getY()));
shapes.add(new ShapeInfo(circle, new Point(e.getX(), e.getY()), color));
repaint();
}
});
for(ShapeInfo info : shapes) {
Point location = info.point.getLocation();
g2d.translate(location.x, location.y);
g2d.setColor(info.color);
g2d.fill(info.shape);
g2d.translate(-location.x, -location.y);
}
I hope to get a nice, smooth line made of circles, but end up with sacttered circles. https://imgur.com/a/KLOyPcn <- Here is what happends when I drag the mouse too fast while painting.
Your mouse works at a certain frequency (normal mouse works around 100Hz) so it will pick a certain number of points while you move it.
If you cover 1000 px in half second (which is not really fast) it will pick 50 points, they will be spaced every 20 pixel.
If your circle has a radius of less than that you will see a dotted line.
Even using very a fast mouse could not lead you to have a continuous line.
You could draw a line between points instead of drawing a circle if you want, or interpolate coordinate between last circle and current one and create other circles in between the 2.
Here try this.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
public class FillOvals extends JPanel {
private JFrame frame = new JFrame();
private List<Point> points = new ArrayList<>();
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new FillOvals().start());
}
public FillOvals() {
setPreferredSize(new Dimension(500, 500));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.pack();
frame.setLocationRelativeTo(null); // center on screen
frame.setVisible(true);
}
public void start() {
MyMouseListener ml = new MyMouseListener();
addMouseMotionListener(ml);
addMouseListener(ml);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (points.size() < 1) {
return;
}
Graphics2D g2d = (Graphics2D) g.create();
// keep the line smooth on the edges
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.BLUE);
Point p = points.get(0);
int lastx = p.x;
int lasty = p.y;
g2d.setStroke(new BasicStroke(10.f));
for (int i = 1; i < points.size(); i++) {
p = points.get(i);
g2d.drawLine(lastx, lasty, p.x, p.y);
lastx = p.x;
lasty = p.y;
}
g2d.dispose();
}
private class MyMouseListener extends MouseAdapter {
public void mouseDragged(MouseEvent e) {
points.add(e.getPoint());
repaint();
}
public void mousePressed(MouseEvent e) {
points.add(e.getPoint());
repaint();
}
}
}
The JVM/mouse can't keep up with drawing circles that fast. The example I provided draws a line between two points on a continuous basis.
Right now I am trying to get it so whenever I click inside of the oval that I have painted, and drag the mouse it will move positions by repainting. However, even though the MouseEvents are being detected correctly, the oval image is not updating. I am confused to why that is. Here is the code that deals with the oval, MouseEvents, and updating:
public class DrawOval extends JPanel {
private int size = 50;
private int locX = 0; //vector points
private int locY = 0;
private boolean isPressed = false;
private Shape oval = new Ellipse2D.Double(locX, locY, size, size * 2);
public DrawOval(int size){
this.size = size;
Dimension dims = new Dimension(size, size);
setPreferredSize(dims);
setMaximumSize(dims);
setMinimumSize(dims);
MouseAdapter m = new MouseAdapter(){
public void mouseReleased(MouseEvent e){
isPressed = false;
update(e);
System.out.println("Mouse is released!");
}
public void mousePressed(MouseEvent e){
isPressed = true;
update(e);
System.out.println("Mouse is pressed!");
}
public void mouseDragged(MouseEvent e){
if(isPressed){
update(e);
System.out.println("Mouse is dragged!");
}
}
public void update(MouseEvent e){
System.out.println("X: " + e.getX() + ", Y: " + e.getY());
if(oval.contains(e.getX(), e.getY())){
setX(e.getX()); setY(e.getY());
repaint();
}
//does not update if the mouses click coordinates are outside of the oval
}
};
addMouseListener(m); //for pressing and releasing
addMouseMotionListener(m); //for dragging
}
public void setX(int _x){
this.locX = _x;
}
public void setY(int _y){
this.locY = _y;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLACK);
g2.fill(oval);
}
}
I cannot figure out why it is not updating correctly. I had it partially working before, but it would update all the time, even if where the user clicked was not within the oval.
I think doing setX and setY you forget to update x and y in your oval. You have at least three options:
1) recreate Ellipse2D.Double every time you change this.locX and this.locY.
2) expect that your oval is created with x=0, y=0 once and for all, check mouse event switching to relative coordinates (if(oval.contains(e.getX() - locX, e.getY() - locY)){...}) and draw your oval using AffineTransform by g2.transform(...).
3) Declare oval as Ellipse2D.Double oval = new Ellipse2D.Double(...); and then you can change its x and y directly cause they're public:
oval.x = this.locX;
oval.y = this.locY;
I am having some trouble figuring out how to determine if a mouseclick event was clicked inside of a rectangle, if the rectangle has been rotated.
Lets say I have a MouseAdapter as simple as this. It just prints out a statement saying that we hit inside the rectangle if the mousePressed was in fact within the rectangle.
MouseAdapter mAdapter = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int xPos = e.getX();
int yPos = e.getY();
if(xPos >= rect.x && xPos <= rect.x + rect.width && yPos >= rect.y && yPos <= rect.y + rect.height) {
System.out.println("HIT INSIDE RECTANGLE");
}
}
};
My issue comes from when I rotate the rectangle. The if statement above obviously doesn't consider the rotation, so after I rotate the rectangle, my hit test fails. For rotate, I'm doing something as simple as this in a paint() function:
class drawRect {
Rectangle rect = new Rectangle();
...
public void paint(Graphics g) {
Graphcis2D g2 = (Graphics2D) g;
AffineTransform old = g2.getTransform();
g.rotate(Math.toRadians(90), rect.x, rect.y);
g2.draw(rect);
g2.setTransform(old);
}
}
This is just some quick pseudocode, so that you guys can understand what I am trying to do. So please don't worry about syntax and all of that. Any help would be appreciated!
You could apply the rotation to your mouse coordinates as well. Dry-coded:
MouseAdapter mAdapter = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
// Create the same transform as used for drawing the rectangle
AffineTransform t = new AffineTransform();
t.rotate(Math.toRadians(90), rect.x, rect.y);
Point2D tp = t.inverseTransform(e.getPoint());
if(rect.contains(tp)) {
System.out.println("HIT INSIDE RECTANGLE");
}
}
};
How to move a triangle to a new location using mouse drag (which was previous drawn using mouse drag)?
...
java.util.List<Polygon> triangles = new LinkedList<Polygon>();
Point startDrag, endDrag, midPoint;
Polygon triangle;
...
public PaintSurface() {
this.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
startDrag = new Point(e.getX(), e.getY());
endDrag = startDrag;
repaint();
}//end mousePressed
public void mouseReleased(MouseEvent e) {
...
int[] xs = { startDrag.x, endDrag.x, midPoint.x };
int[] ys = { startDrag.y, startDrag.y, midPoint.y };
triangles.add( new Polygon(xs, ys,3));
startDrag = null;
endDrag = null;
repaint();
}//end mouseReleased
...
});//end addMouseListener
this.addMouseMotionListener(new MouseMotionAdapter() {
/* I dont know how to move (drag) the whole triangle to new location and later delete the previous drawn triangle. The mouseDragged method only draw a new triangle using mouse drag :-(
*/
public void mouseDragged(MouseEvent e) {
endDrag = new Point(e.getX(), e.getY());
repaint();
}//end mouseDragged
}//end paintSurface
//Draw triangles
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//draw the thickness of the line
g2.setStroke(new BasicStroke(1));
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.00f));
g2.setPaint(Color.black);//set the triangle color
for (Polygon triangle : triangles) g2.drawPolygon(triangle);
if (startDrag != null && endDrag != null) {
g2.setPaint(Color.red);
g2.drawPolygon(triangle);
}
}//end paint
}//end private class PaintSurface
when you start to drag you have to detect if your current mouse location is on one of the existing Polygons, also mark the starting location
When it is you dont add a new polygon, but you add the amount moved to the different points of the existing polygon and repaint
i am using java.
i want to draw rectangle based on mousedrag event. if user dragging the mouse, then the rectangle on the applet should increase or decrease basing on current mouse coordinates.
i have the following code.
in the following code i am using [b]SelectionArea[/b] class which extends a canvas on which i am performing drawing operation. i am using [b]image[/b] variable in this class for double buffering to reduce flickering and to save the applet's previous state(i.e drawing content of applet)
but the code is working fine if i draw first rectangle. if i start to draw second rectangle the previously drawn rectangle is disappearing. i want the previously drawn rectangle to be on the screen
can any one tell me how to solve this.
import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;
/*
* This displays a framed area. When the user drags within
* the area, this program displays a rectangle extending from
* where the user first pressed the mouse button to the current
* cursor location.
*/
public class RectangleDemo extends Applet {
SelectionArea drawingPanel;
Label label;
public void init() {
GridBagLayout gridBag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setLayout(gridBag);
drawingPanel = new SelectionArea(this);
c.fill = GridBagConstraints.BOTH;
c.weighty = 1.0;
c.gridwidth = GridBagConstraints.REMAINDER; //end row
gridBag.setConstraints(drawingPanel, c);
add(drawingPanel);
label = new Label("Drag within the framed area.");
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
c.weighty = 0.0;
gridBag.setConstraints(label, c);
add(label);
drawingPanel.setVisible(true);
validate();
}
public void paint(Graphics g){
drawingPanel.repaint();
}
public void update(Graphics g){
paint(g);
}
}
class SelectionArea extends Canvas implements ActionListener, MouseListener, MouseMotionListener{
Rectangle currentRect;
RectangleDemo controller;
//for double buffering
Image image;
Graphics offscreen;
public SelectionArea(RectangleDemo controller) {
super();
this.controller = controller;
addMouseListener(this);
addMouseMotionListener(this);
}
public void actionPerformed(ActionEvent ae){
repaintoffscreen();
}
public void repaintoffscreen(){
image = createImage(this.getWidth(), this.getHeight());
offscreen = image.getGraphics();
Dimension d = getSize();
if(currentRect != null){
Rectangle box = getDrawableRect(currentRect, d);
//Draw the box outline.
offscreen.drawRect(box.x, box.y, box.width - 1, box.height - 1);
//repaint();
}
}
public void mouseEntered(MouseEvent me) {}
public void mouseExited(MouseEvent me){ }
public void mouseClicked(MouseEvent me){}
public void mouseMoved(MouseEvent me){}
public void mousePressed(MouseEvent me) {
currentRect = new Rectangle(me.getX(), me.getY(), 0, 0);
repaintoffscreen();
}
public void mouseDragged(MouseEvent me) {
System.out.println("here in dragged()");
currentRect.setSize(me.getX() - currentRect.x, me.getY() - currentRect.y);
repaintoffscreen();
repaint();
}
public void mouseReleased(MouseEvent me) {
currentRect.setSize(me.getX() - currentRect.x, me.getY() - currentRect.y);
repaintoffscreen();
repaint();
}
public void update(Graphics g){
paint(g);
}
public void paint(Graphics g) {
g.drawImage(image, 0, 0, this);
}
Rectangle getDrawableRect(Rectangle originalRect, Dimension drawingArea) {
int x = originalRect.x;
int y = originalRect.y;
int width = originalRect.width;
int height = originalRect.height;
//Make sure rectangle width and height are positive.
if (width < 0) {
width = 0 - width;
x = x - width + 1;
if (x < 0) {
width += x;
x = 0;
}
}
if (height < 0) {
height = 0 - height;
y = y - height + 1;
if (y < 0) {
height += y;
y = 0;
}
}
//The rectangle shouldn't extend past the drawing area.
if ((x + width) > drawingArea.width) {
width = drawingArea.width - x;
}
if ((y + height) > drawingArea.height) {
height = drawingArea.height - y;
}
return new Rectangle(x, y, width, height);
}
}
also if i run this code on full screen mode then i am seeing that the rectangle is appering on screen only after i released the mouse. but i want the rectangle to be on the screen while dragging the mouse and it should change it's dimension according to the current mouse coordinates.
can any one help me pls.
homework?
basically what you need to do is:
on mouse down keep the mouse-down coordinates and repaint
on mouse move keep current mouse coordinates and repaint
on mouse up, nullify the mouse-down coordinates to indicate there is no rect, and repaint.
on paint, draw background and then rect between mousedown and cur-mouse coordinates.
if you don't want to keep a background image, you can do a trick with the Graphics xor function, drawing the same rect twice will erase the old rect, so you can use it to restore the old image straight on the graphics object.
Edit: code xor usage sample:
public void paint(Graphics g)
{
g.setXORMode(Color.black);
// draw old rect if there is one. this will erase it
// draw new rect, this will draw xored
g.setDrawMode(); // restore normal draw mode
}
Xor has the an interesting property:
xor(xor(x)) = x
so xoring the same pixel twice restores it's original color.
There are a couple issues that need to be addressed.
First, regarding only one rectangle can be drawn, this is due to the design of your program. In your code, whenever the repaintoffscreen method is called, the currectRect field is used to draw a rectangle. However, there is no provision to keep holding onto rectangles which were made in the past.
One way to keep a hold of past rectangles would be perhaps to make another field which is, for example, a List<Rectangle> which is used to store past rectangles. Then, when the mouse is released, add the current rectangle to that list.
Then, in order for all rectangles, currentRect and past rectangles to appear, repaintoffscreen will need to not only perform getDrawableRect and offscreen.drawRect using the currentRect but also with the past rectangles which are stored in the List<Rectangle>. (Hint, use a for loop to iterate through the list.)
Second, regarding the rectangle not appearing until after releasing the mouse button, rather than using the mouseDragged method, maybe using the mouseMoved method along with a check to see that the mouse button is depressed may be a workaround. (I think I've also had trouble dealing with the mouseDragged method in the past.)
The MouseEvent passed into the mouseMoved method can be used to check if a button is depressed by the getButton method:
public void mouseMoved(MouseEvent e)
{
// Check if button1 is pressed.
if (e.getButton() == MouseEvent.BUTTON1)
{
// Perform sizing of rectangle and off-screen drawing, and repaint.
}
}
My question was about create a select rectangle invert mouse click position, but, in the end I got make this with this method:
... //to set the selection area
private int iniSelX;
private int iniSelY;
private int endSelX;
private int endSelY;
private JPanel myJPanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
g.drawLine(260, 5, 260, 260);
g.setColor(Color.BLUE);
//verify if go draw the rectangle
if (iniSelX != 0 || endSelX != 0) {
boolean revertX = iniSelX < endSelX;
boolean revertY = iniSelY < endSelY;
//Simple way
//g.drawRect(iniSelX, iniSelY, endSelX - iniSelX, endSelY - iniSelY);
//reverse way
g.drawRect(revertX ? iniSelX : endSelX, revertY ? iniSelY : endSelY,
revertX ? endSelX - iniSelX : iniSelX - endSelX, revertY ? endSelY - iniSelY : iniSelY - endSelY);
}
}
}; ...
addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent m) {
//update selection area
endSelX = m.getX();
endSelY = m.getY();
repaint();
}
#Override
public void mouseMoved(MouseEvent m) {
repaint();
}
});
addMouseListener(new MouseListener() {
...
#Override
public void mousePressed(MouseEvent e) {
//start drawing the selection
iniSelX = e.getX() - 15;
iniSelY = e.getY() - 20;
}
#Override
public void mouseReleased(MouseEvent e) {
//start drawing the selection
iniSelX = 0;
iniSelY = 0;
endSelX = 0;
endSelY = 0;
}
...
});
}
public void log() {
System.out.println("iniSelX" + iniSelX);
System.out.println("iniSelY" + iniSelY);
System.out.println("endSelX" + endSelX);
System.out.println("endSelY" + endSelY);
} ...
I hope this is useful.