I have the below code that draws an ellipse when the user drags the mouse, however I only want a circle to be drawn and not an ellipse also.
I've tried a few things but haven't quite got it to work, from what I understand it places the circle inside a rectangle, so if I can lock that so it places it inside a square then I should get what I want? The code may not be the most efficient, I'm just trying to write this for a project I'm working, to be clear this is not school homework or anything. If I can get at least this circle aspect of the code working it will save me a lot of time in my project
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Line2D;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class DrawingBoardWithMatrix extends JFrame {
public static void main(String[] args) {
DrawingBoardWithMatrix drawingBoardWithMatrix = new DrawingBoardWithMatrix();
}
public DrawingBoardWithMatrix() {
this.setSize(600, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(new PaintSurface(), BorderLayout.CENTER);
this.setVisible(true);
}
private class PaintSurface extends JComponent {
ArrayList<Shape> shapes = new ArrayList<>();
Point startDrag, endDrag;
public PaintSurface() {
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startDrag = new Point(e.getX(), e.getY());
endDrag = startDrag;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
Shape r = makeCircle(startDrag.x, startDrag.y, e.getX(), e.getY());
shapes.add(r);
startDrag = null;
endDrag = null;
repaint();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
endDrag = new Point(e.getX(), e.getY());
repaint();
}
});
}
private void paintBackground(Graphics2D g2){
g2.setPaint(Color.LIGHT_GRAY);
for (int i = 0; i < getSize().width; i += 10) {
Shape line = new Line2D.Float(i, 0, i, getSize().height);
g2.draw(line);
}
for (int i = 0; i < getSize().height; i += 10) {
Shape line = new Line2D.Float(0, i, getSize().width, i);
g2.draw(line);
}
}
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
paintBackground(g2);
g2.setStroke(new BasicStroke(2));
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.50f));
for (Shape s : shapes) {
g2.setPaint(Color.BLACK);
g2.draw(s);
// g2.setPaint(colors[(colorIndex++) % 6]);
// g2.fill(s);
}
if (startDrag != null && endDrag != null) {
g2.setPaint(Color.LIGHT_GRAY);
Shape r = makeCircle(startDrag.x, startDrag.y, endDrag.x, endDrag.y);
g2.draw(r);
}
}
private Ellipse2D.Float makeCircle(int x1, int y1, int x2, int y2) {
return new Ellipse2D.Float(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
}
}
}
Your makeCircle() draws an ellipse with width Math.abs(x1 - x2) and height Math.abs(y1 - y2). If you want to draw a circle, the width and height must be equal. There are several ways to accomplish this:
Use Math.abs(x1 - x2) for both width and height
Use Math.abs(y1 - y2) for both width and height
Decide between the two based on the mouse position
However, I am very concerned about your statement "I just don't have the time right now to learn Java properly" - this will come back to bite you. If you have any intention of having programming as a serious hobby or potentially a job down the line, you must expend a lot of effort to properly understand what you are doing.
Related
I'm trying to do a simulation program using java swing gui. I have 2 images that are same but one is blurred all the way and other one is normal. And a 3rd image that is just a transparent rectangular frame in .png format. What I want to achieve is that, I will drag the transparent rectangular box over the blurred image and it will reveal the non-blurred image below. How can I achieve this?
P.S: Images are loaded into the program using JLabel onto JLayeredPane and JFrame. Also transparent rectangular box has a mouse listener.
A "overlay" illusion...
Basically this is going to generate a subImage of the un-blurred image based on the current mouse position and view port size, this is then blended with the "overlay effect" and rendered on top of the blurred image, creating the "illusion" of a cutout effect.
The following example will follow the mouse around, exposing a 200x200 pixel area of the image around the mouse cursor.
import java.awt.AlphaComposite;
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.RadialGradientPaint;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage blurredImage;
private BufferedImage normalImage;
private BufferedImage spyScopeImage;
private int viewPortSize = 200;
private Point currentLocation;
public TestPane() throws IOException {
blurredImage = ImageIO.read(getClass().getResource("/images/PosterBlurred.png"));
normalImage = ImageIO.read(getClass().getResource("/images/Poster.png"));
spyScopeImage = new BufferedImage(viewPortSize, viewPortSize, BufferedImage.TYPE_INT_ARGB);
Graphics2D masked = spyScopeImage.createGraphics();
Color transparent = new Color(255, 0, 0, 0);
Color fill = Color.RED;
RadialGradientPaint rgp = new RadialGradientPaint(
new Point2D.Double(viewPortSize / 2d, viewPortSize / 2d),
viewPortSize,
new float[]{0f, 0.5f, 1f},
new Color[]{fill, transparent, transparent});
// masked.setComposite(AlphaComposite.DstAtop);
masked.setPaint(rgp);
masked.fill(new Rectangle(0, 0, viewPortSize, viewPortSize));
masked.dispose();
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
currentLocation = e.getPoint();
repaint();
}
#Override
public void mouseEntered(MouseEvent e) {
currentLocation = e.getPoint();
repaint();
}
#Override
public void mouseExited(MouseEvent e) {
currentLocation = null;
repaint();
}
};
addMouseMotionListener(mouseAdapter);
}
#Override
public Dimension getPreferredSize() {
if (blurredImage != null) {
return new Dimension(blurredImage.getWidth(), blurredImage.getHeight());
}
return new Dimension(800, 600);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (blurredImage != null) {
int x = (getWidth() - blurredImage.getWidth()) / 2;
int y = (getHeight() - blurredImage.getHeight()) / 2;
g2d.drawImage(blurredImage, x, y, this);
if (currentLocation != null && normalImage != null) {
int mouseX = currentLocation.x - x;
int mouseY = currentLocation.y - y;
int viewPortOffset = viewPortSize / 2;
int minX = Math.max(0, mouseX - viewPortOffset);
int minY = Math.max(0, mouseY - viewPortOffset);
int maxX = Math.min(normalImage.getWidth(), mouseX + viewPortSize);
int maxY = Math.min(normalImage.getHeight(), mouseY + viewPortSize);
int viewX = minX - viewPortOffset;
int viewY = minY - viewPortOffset;
BufferedImage subimage = normalImage.getSubimage(minX, minY, maxX - minX, maxY - minY);
// Here we're going to "mask" the sub image and the "spy scope" effect together
BufferedImage masked = new BufferedImage(viewPortSize, viewPortSize, BufferedImage.TYPE_INT_ARGB);
Graphics2D mg = masked.createGraphics();
mg.drawImage(subimage, 0, 0, this);
mg.setComposite(AlphaComposite.DstAtop);
mg.drawImage(spyScopeImage, 0, 0, this);
mg.dispose();
g2d.drawImage(masked, x + minX, y + minY, this);
}
}
g2d.dispose();
}
}
}
Alternatively...
You could create a "masked" version of the blurred image with the "scope effect" which exposes the image rendered below it, but agin, this will be expensive, as each time you're creating a new image the size of the original blurred image.
This concept is demonstrated in How to create a transparent shape in the image
I have a buffered image (width = 66, height = 75) that seems to jitter (as if the image is shaking at a small scale) when trying to translate it when it is at an angle. This is the set up that shows the problem (A and D keys to rotate, W and S keys to translate):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test extends JPanel {
BufferedImage image;
public double angle = 0;
public Point center = new Point(160, 160);
public Test() {
try {
image = ImageIO.read(new File("filePath"));
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(image.getWidth() + ", " + image.getHeight());
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(1000, 500));
frame.add(this);
this.setBackground(Color.gray);
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
double deltaAngle = 3;
int speed = 3;
if(e.getKeyCode() == KeyEvent.VK_A)
angle -= deltaAngle;
else if(e.getKeyCode() == KeyEvent.VK_D)
angle += deltaAngle;
else if(e.getKeyCode() == KeyEvent.VK_W)
center.translate(speed*Math.sin(Math.toRadians(angle)), -speed*Math.cos(Math.toRadians(angle)));
else if(e.getKeyCode() == KeyEvent.VK_S)
center.translate(-speed*Math.sin(Math.toRadians(angle)), speed*Math.cos(Math.toRadians(angle)));
frame.repaint();
}
#Override
public void keyReleased(KeyEvent e) {
}
});
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2.drawString("Angle: " + Double.toString(angle), 20, 20);
drawImageCentered(g2, rotateImage(image, angle), center);
}
public void drawImageAccurate(Graphics2D g, BufferedImage image, Point location) {
AffineTransform transform = new AffineTransform();
transform.translate(location.x, location.y);
//transform.scale(1, 1);
g.drawImage(image, transform, null);
}
public void drawImageCentered(Graphics2D g, BufferedImage image, Point location) {
drawImageAccurate(g, image, new Point(location.x - image.getWidth()/2.0, location.y - image.getHeight()/2.0));
}
public static BufferedImage rotateImage(BufferedImage image, double angle) {
if(angle == 0 || Double.isNaN(angle))
return image;
else {
angle = Math.toRadians(angle);
double x = Math.abs(Math.cos(angle));
double y = Math.abs(Math.sin(angle));
int width = image.getWidth();
int height = image.getHeight();
int nWidth = (int) Math.floor(width*x + height*y);
int nHeight = (int) Math.floor(height*x + width*y);
BufferedImage rotated = new BufferedImage(nWidth, nHeight, image.getType());
Graphics2D tool = rotated.createGraphics();
AffineTransform transformer = new AffineTransform();
transformer.translate((nWidth - width)/2.0, (nHeight - height)/2.0);
transformer.rotate(angle, width/2, height/2);
tool.drawImage(image, transformer, null);
tool.dispose();
return rotated;
}
}
private class Point {
public double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public void translate(double x, double y) {
this.x += x;
this.y += y;
}
}
public static void main(String[] args) {
new Test();
}
}
I notice that the jittering of the image is more noticeable as the image get smaller in size and when the image is at 87 degrees and other close to "straight" angles. I have used rendering hints:
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
and also an AffineTransform:
public void drawImageAccurate(Graphics2D g, BufferedImage image, Point location) {
AffineTransform transform = new AffineTransform();
transform.translate(location.x, location.y);
//transform.scale(1, 1);
g.drawImage(image, transform, null);
}
Which, I think, lowered the intensity of the jittering but I still can see some small jittering. Maybe my expectations are too high, but I do not think I have ever seen jittering in a 2D game which makes me think there can be more done. Or perhaps some solutions are to increase the movement speed of images or prevent using small images for movement. However, it would nice if there is another direct solution I have not been able to find through searches that could remove this image jittering.
I assume the image quality keeps changing as you keep rotating the image into the various angles repeatedly and each time you may perceive conversion losses.
Probably it is better to draw the image once only by applying one AffineTransform only. This AffineTransform is a contatencation of three single transformations:
translate image center to origin
rotate image acount origin
translate image to actual position
So when keys are pressed you recalculate the AffineTransform and ask for repaint.
When repainting, just draw the image using the existing AffineTransform.
I played around a bit and the code looks like this now.
package com.mycompany.test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test extends JPanel {
/** rotation speed in radians. */
final double deltaAngle = 3.0d * Math.PI/180.0d;
final int speed = 3;
BufferedImage image;
/** angle in radians. */
public double angle = 0;
public Point center = new Point(160, 160);
AffineTransform at;
private void updateTransform() {
at = AffineTransform.getRotateInstance(angle);
at.concatenate(AffineTransform.getTranslateInstance(-image.getWidth()/2, -image.getHeight()/2));
at.concatenate(AffineTransform.getTranslateInstance(center.x, center.y));
}
public Test() {
try {
image = ImageIO.read(new File("filepath"));
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(image.getWidth() + ", " + image.getHeight());
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(1000, 500));
frame.add(this);
this.setBackground(Color.gray);
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_A)
angle -= deltaAngle;
else if(e.getKeyCode() == KeyEvent.VK_D)
angle += deltaAngle;
else if(e.getKeyCode() == KeyEvent.VK_W)
center.translate(speed*Math.sin(Math.toRadians(angle)), -speed*Math.cos(Math.toRadians(angle)));
else if(e.getKeyCode() == KeyEvent.VK_S)
center.translate(-speed*Math.sin(Math.toRadians(angle)), speed*Math.cos(Math.toRadians(angle)));
updateTransform();
frame.repaint();
}
#Override
public void keyReleased(KeyEvent e) {
}
});
frame.pack();
updateTransform();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2.drawString("Angle: " + Double.toString(angle), 20, 20);
g2.drawImage(image, at, null);
}
private class Point {
public double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public void translate(double x, double y) {
this.x += x;
this.y += y;
}
}
public static void main(String[] args) {
new Test();
}
}
However the artifacts are not fully resolved. This is since a bitmap is rotated - such artifacts are to be expected. Not even scaling down the image helps. The only chance may be to use SVG as source. Check out Apache Batik.
#Override
public Shape getShape() {
final Ellipse2D.Double result = new Ellipse2D.Double();
final double px = Math.min(getStart().getX(), getEnd().getX());
final double py = Math.min(getStart().getY(), getEnd().getY());
final double pw = Math.abs(getStart().getX() - getEnd().getX());
result.setFrame(px, py, pw, pw);
return result;
}
So this getShape() is returning the shape to a class that draws the shape. getStart() gets the starting Point of the mouse on click, and getEnd() gets the Point at mouse release. Now, when I drag to draw a circle, if i drag to the right or down the circle works as intended and expands to the mouse, if I drag up or left of the cursor the circle expands as it should BUT the circle shape moves up and down with the cursor and I am not sure why.
Shapes in Java are based on the top/left corner as the anchor and the width/height been drawn down/right.
You need to calculate the bounding box between the click point and the drag point
public Shape getShape() {
final Ellipse2D.Double result = new Ellipse2D.Double();
final double px = Math.min(getStart().getX(), getEnd().getX());
final double py = Math.min(getStart().getY(), getEnd().getY());
final double pw = Math.abs(getStart().getX() - getEnd().getX());
result.setFrame(px, py, pw, pw);
return result;
}
The problem is, you're still using the difference between the click point and the drag point to calculate the width, pw should be the difference between the maxX and minX values
So this example shows you how to calculate the anchor and size properties
I read through that post, but it doesn't solve the dragging expansion of the circle, and how it moves up and down.
Then you're doing something wrong
I am trying to get a circle drawn not a circle without the same width and height(ellipse).
Okay, so it should always appear that the circle is been draw from the anchor point, so when the minX or minY is less than the clickPoint's x/y points, then you need to adjust them as a difference of the clickPoint and the size
So, this will make it "appear" as if the circle is always been drawn from the intial click point
import java.awt.BorderLayout;
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.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SelectionExample {
public static void main(String[] args) {
new SelectionExample();
}
public SelectionExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Point clickPoint;
private Shape shape;
private Rectangle box;
public TestPane() {
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
int minX = Math.min(e.getX(), clickPoint.x);
int minY = Math.min(e.getY(), clickPoint.y);
int maxX = Math.max(e.getX(), clickPoint.x);
int maxY = Math.max(e.getY(), clickPoint.y);
box = new Rectangle(minX, minY, maxX - minX, maxY - minY);
int size = Math.min(maxX - minX, maxY - minY);
if (minX < clickPoint.x) {
minX = clickPoint.x - size;
}
if (minY < clickPoint.y) {
minY = clickPoint.y - size;
}
shape = new Ellipse2D.Double(minX, minY, size, size);
repaint();
}
#Override
public void mousePressed(MouseEvent e) {
clickPoint = new Point(e.getPoint());
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (shape != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(0, 0, 255, 64));
g2d.fill(shape);
g2d.setColor(Color.BLUE);
g2d.draw(shape);
g2d.draw(box);
g2d.dispose();
}
}
}
}
I am solving a Java problem about AWT. When you click the right button of Mouse. It will create an oval. If you click the left button, it will remove the oval you clicked. I have implemented the creation of ovals, but I cannot find any built-in functions about removing shapes in AWT. My code is below:
package chapter16;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Points extends JFrame{
public Points(){
add(new NewPanel4());
}
public static void main(String[] args){
Points frame = new Points();
frame.setSize(200, 200);
frame.setTitle("Add and Remove Points");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
class NewPanel4 extends JPanel{
private boolean Add = false;//add points
private int x,y;//the x,y coordinate
public NewPanel4(){
addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e){
if(e.getButton()== MouseEvent.BUTTON3){
Add = true;
x = e.getX();
y = e.getY();
repaint();
}
else if(e.getButton() == MouseEvent.BUTTON1){
Add = false;
}
}
});
}
protected void paintComponent(Graphics g){
//super.paintComponent(g);
//Add oval if Add is true
if (Add == true){
g.fillOval(x, y, 10, 10);
}
//remove oval if Add is false
else if(Add == false){
//code to remove ovals
}
}
}
}
I have refactored your code to use a collection to store the oval. Hope it helps you. It is same as what Andrew suggested.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Points extends JFrame {
public Points() {
add(new NewPanel4());
}
public static void main(String[] args) {
Points frame = new Points();
frame.setSize(200, 200);
frame.setTitle("Add and Remove Points");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
class NewPanel4 extends JPanel {
private List<Point> points = new ArrayList<>();
public NewPanel4() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
Point center = new Point(e.getX(), e.getY());
points.add(center);
repaint();
} else if (e.getButton() == MouseEvent.BUTTON1) {
removeOval(new Point(e.getX(), e.getY()));
repaint();
}
}
});
}
private void removeOval(Point selectedPoint) {
for (Iterator<Point> iterator = points.iterator(); iterator.hasNext();) {
Point center = iterator.next();
if (isInsideOval(selectedPoint, center)) {
iterator.remove();
break;
}
}
}
private boolean isInsideOval(Point point, Point center) {
int r = 5;
int dx = point.x - center.x;
int dy = point.y - center.y;
return (dx * dx + dy * dy <= r * r);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
g2D.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
super.paintComponent(g);
g2D.setColor(Color.BLUE);
for (Point point : points) {
g2D.fillOval(point.x- (10 / 2) , point.y- (10 / 2), 10, 10);
}
}
}
}
Language: Java.
Hi, I need to prevent drawing over the same location of a Graphics2D more than once. For example, if the user draws using a BasicStroke with a round cap and a width of 10 over a certain area, that same area cannot be drawn on a second time.
The reason I want to do this is so that the user can draw (free-hand) translucent colours over an image without drawing over the same stroke (thus increasing the density of the colour and reducing its translucency).
I've tried storing the shapes of all the strokes made by the user (as Area objects that subtract the shape) and then clipping the Graphics2D by the intersection of all those Area objects.
This almost works, but the 'shape' obtained by the clip is not quite the same as the 'shape' drawn by the stroke - it is out by a couple of pixels.
Does anyone have any other ideas that might work?
The concept is relatively simple, you need to have multiple layers onto which you can render...
There are multiple different ways to approach the problem. You could maintain a list of Points and on each paint cycle, render these points to a backing buffer, which you would then draw over the main content using a AlphaComposite.
You could (as this example does) draw directly to the backing buffer and repaint the content, again, using a AlphaComposite to render the higher layer.
You could have any number of layers...
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
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.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
public class PaintOver {
public static void main(String[] args) {
new PaintOver();
}
public PaintOver() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new MapPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MapPane extends JPanel {
private BufferedImage background;
private BufferedImage foreground;
public MapPane() {
try {
background = ImageIO.read(getClass().getResource("/TreasureMap.png"));
foreground = new BufferedImage(background.getWidth(), background.getHeight(), BufferedImage.TYPE_INT_ARGB);
} catch (Exception e) {
e.printStackTrace();
}
MouseAdapter mouseHandler = new MouseAdapter() {
private Point startPoint;
#Override
public void mousePressed(MouseEvent e) {
startPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
startPoint = null;
}
#Override
public void mouseDragged(MouseEvent e) {
Point endPoint = e.getPoint();
Graphics2D g2d = foreground.createGraphics();
Point offset = getOffset();
Point from = new Point(startPoint);
from.translate(-offset.x, -offset.y);
Point to = new Point(endPoint);
to.translate(-offset.x, -offset.y);
g2d.setColor(Color.RED);
g2d.setStroke(new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2d.draw(new Line2D.Float(from, to));
g2d.dispose();
startPoint = endPoint;
repaint();
}
};
addMouseListener(mouseHandler);
addMouseMotionListener(mouseHandler);
}
#Override
public Dimension getPreferredSize() {
return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight());
}
protected Point getOffset() {
Point p = new Point();
if (background != null) {
p.x = (getWidth() - background.getWidth()) / 2;
p.y = (getHeight() - background.getHeight()) / 2;
}
return p;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
Graphics2D g2d = (Graphics2D) g.create();
Point offset = getOffset();
g2d.drawImage(background, offset.x, offset.y, this);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.drawImage(foreground, offset.x, offset.y, this);
g2d.dispose();
}
}
}
}