问题
So, when I click and drag a Path2D circle on my JPanel, it is resizing. The problem is that when I initially click-and-drag it, the circle jumps to a smaller size, but then resized correctly as you click and drag. The easiest way to demonstrate is to run the runnable code below.
I know I need to fix this code:
@Override
public void mouseDragged(MouseEvent e) {
int mouseX = e.getX();
int mouseY = e.getY();
if (resizing) {
System.out.println("resizing");
Rectangle bounds = shapes.get(currentIndex).getBounds();
int shapeX = bounds.x;
int shapeY = bounds.y;
shapes.get(currentIndex).reset();
shapes.get(currentIndex).append(
new Ellipse2D.Double(shapeX, shapeY, mouseX
- shapeX, mouseX - shapeX), true);
repaint();
}
}
I'm not sure how, though. I am clicking on the outer edge of the circle, and then I am setting the length and width of the bounds of the circle to where the new mouse point is...but what I need to do is the the outer edge of the circle's coordinates to the new mouse points. Any ideas on how to compute the correct point?
Full Code
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Panel;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Editor {
public static void main(String[] args) {
new Editor();
}
public Editor() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException
| UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new UMLWindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(30, 30, 1000, 700);
frame.getContentPane().setBackground(Color.white);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class UMLWindow extends JFrame {
Shapes shapeList = new Shapes();
Panel panel;
private static final long serialVersionUID = 1L;
public UMLWindow() {
addMenus();
panel = new Panel();
}
public void addMenus() {
getContentPane().add(shapeList);
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
shapeList.addCircle(100, 100);
}
}
// Shapes class, used to draw the shapes on the panel
// as well as implements the MouseListener for dragging
public static class Shapes extends JPanel {
private static final long serialVersionUID = 1L;
private List<Path2D> shapes = new ArrayList<Path2D>();
int currentIndex;
private Point mousePoint;
public Shapes() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
public void addCircle(int width, int height) {
Path2D circ = new Path2D.Double();
circ.append(new Ellipse2D.Double(442, 269, width, height), true);
shapes.add(circ);
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(2));
for (Path2D shape : shapes) {
g2.setColor(Color.BLACK);
g2.draw(shape);
}
}
public Rectangle2D getActiveBounds(float angel, Rectangle bounds) {
Point2D p = getPointOnEdge(angel, bounds);
return new Rectangle2D.Double(p.getX() - 4, p.getY() - 4, 8, 8);
}
public Point2D getPointOnEdge(float angel, Rectangle bounds) {
float radius = Math.max(bounds.width, bounds.height) / 2;
float x = radius;
float y = radius;
double rads = Math.toRadians((angel + 90));
// Calculate the outter point of the line
float xPosy = (float) (x + Math.cos(rads) * radius);
float yPosy = (float) (y + Math.sin(rads) * radius);
return new Point2D.Float(xPosy + bounds.x, yPosy + bounds.y);
}
class MyMouseAdapter extends MouseAdapter {
Boolean hovering = false;
Boolean resizing = true;
@Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
for (int i = 0; i < shapes.size(); i++) {
Path2D shape = shapes.get(i);
Rectangle2D bottomRight = getActiveBounds(-45,
shape.getBounds());
if (mousePoint != null) {
if (bottomRight.contains(mousePoint)) {
Cursor cursor = Cursor
.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR);
setCursor(cursor);
hovering = true;
} else {
Cursor cursor = Cursor
.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
setCursor(cursor);
hovering = false;
}
}
}
repaint();
}
@Override
public void mousePressed(MouseEvent e) {
if (hovering) {
resizing = true;
System.out.println("Starting to resize");
}
}
@Override
public void mouseDragged(MouseEvent e) {
int mouseX = e.getX();
int mouseY = e.getY();
if (resizing) {
System.out.println("resizing");
Rectangle bounds = shapes.get(currentIndex).getBounds();
int shapeX = bounds.x;
int shapeY = bounds.y;
shapes.get(currentIndex).reset();
shapes.get(currentIndex).append(
new Ellipse2D.Double(shapeX, shapeY, mouseX
- shapeX, mouseX - shapeX), true);
repaint();
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (resizing) {
System.out.println("Done resizing");
resizing = false;
}
}
}
}
}
回答1:
You need to know two things, you need to know the original point that the mouse was pressed and the original bounds of the shape
private Point clickPoint;
private Rectangle originalBounds;
//...
@Override
public void mousePressed(MouseEvent e) {
if (hovering) {
resizing = true;
System.out.println("Starting to resize");
clickPoint = e.getPoint();
originalBounds = new Rectangle(shapes.get(currentIndex).getBounds());
}
}
With this information you can calculate the delta between the original click point and the drag point, armed with this information, you can modify the the shape by adding the difference to the originalBounse
@Override
public void mouseDragged(MouseEvent e) {
if (resizing && clickPoint != null) {
int mouseX = e.getX();
int mouseY = e.getY();
int xDelta = mouseX - clickPoint.x;
int yDelta = mouseY - clickPoint.y;
int delta = Math.max(xDelta, yDelta);
Rectangle bounds = shapes.get(currentIndex).getBounds();
int shapeX = bounds.x;
int shapeY = bounds.y;
int shapeWidth = originalBounds.width + delta;
int shapeHeight = originalBounds.height + delta;
if (shapeWidth < 0) {
shapeWidth *= -1;
shapeX = originalBounds.x - shapeWidth;
}
if (shapeHeight < 0) {
shapeHeight *= -1;
shapeY = originalBounds.y - shapeHeight;
}
System.out.printf("%d %dx%dx%dx%d%n", delta, shapeX, shapeY, shapeWidth, shapeHeight);
shapes.get(currentIndex).reset();
shapes.get(currentIndex).append(
new Ellipse2D.Double(shapeX, shapeY, shapeWidth, shapeHeight), true);
repaint();
}
}
Just don't forget to reset the clickPoint
and originalBounds
when the user releases the button ;)
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Panel;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Editor {
public static void main(String[] args) {
new Editor();
}
public Editor() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new UMLWindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(30, 30, 1000, 700);
frame.getContentPane().setBackground(Color.white);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class UMLWindow extends JFrame {
Shapes shapeList = new Shapes();
Panel panel;
private static final long serialVersionUID = 1L;
public UMLWindow() {
addMenus();
panel = new Panel();
}
public void addMenus() {
getContentPane().add(shapeList);
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
shapeList.addCircle(100, 100);
}
}
// Shapes class, used to draw the shapes on the panel
// as well as implements the MouseListener for dragging
public static class Shapes extends JPanel {
private static final long serialVersionUID = 1L;
private List<Path2D> shapes = new ArrayList<Path2D>();
int currentIndex;
private Point mousePoint;
public Shapes() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
public void addCircle(int width, int height) {
Path2D circ = new Path2D.Double();
circ.append(new Ellipse2D.Double(442, 269, width, height), true);
shapes.add(circ);
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(2));
for (Path2D shape : shapes) {
g2.setColor(Color.BLACK);
g2.draw(shape);
}
}
public Rectangle2D getActiveBounds(float angel, Rectangle bounds) {
Point2D p = getPointOnEdge(angel, bounds);
return new Rectangle2D.Double(p.getX() - 4, p.getY() - 4, 8, 8);
}
public Point2D getPointOnEdge(float angel, Rectangle bounds) {
float radius = Math.max(bounds.width, bounds.height) / 2;
float x = radius;
float y = radius;
double rads = Math.toRadians((angel + 90));
// Calculate the outter point of the line
float xPosy = (float) (x + Math.cos(rads) * radius);
float yPosy = (float) (y + Math.sin(rads) * radius);
return new Point2D.Float(xPosy + bounds.x, yPosy + bounds.y);
}
class MyMouseAdapter extends MouseAdapter {
Boolean hovering = false;
Boolean resizing = true;
private Point clickPoint;
private Rectangle originalBounds;
@Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
for (int i = 0; i < shapes.size(); i++) {
Path2D shape = shapes.get(i);
Rectangle2D bottomRight = getActiveBounds(-45,
shape.getBounds());
if (mousePoint != null) {
if (bottomRight.contains(mousePoint)) {
Cursor cursor = Cursor
.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR);
setCursor(cursor);
hovering = true;
} else {
Cursor cursor = Cursor
.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
setCursor(cursor);
hovering = false;
}
}
}
repaint();
}
@Override
public void mousePressed(MouseEvent e) {
if (hovering) {
resizing = true;
System.out.println("Starting to resize");
clickPoint = e.getPoint();
originalBounds = new Rectangle(shapes.get(currentIndex).getBounds());
}
}
@Override
public void mouseDragged(MouseEvent e) {
if (resizing && clickPoint != null) {
int mouseX = e.getX();
int mouseY = e.getY();
int xDelta = mouseX - clickPoint.x;
int yDelta = mouseY - clickPoint.y;
int delta = Math.max(xDelta, yDelta);
Rectangle bounds = shapes.get(currentIndex).getBounds();
int shapeX = bounds.x;
int shapeY = bounds.y;
int shapeWidth = originalBounds.width + delta;
int shapeHeight = originalBounds.height + delta;
if (shapeWidth < 0) {
shapeWidth *= -1;
shapeX = originalBounds.x - shapeWidth;
}
if (shapeHeight < 0) {
shapeHeight *= -1;
shapeY = originalBounds.y - shapeHeight;
}
System.out.printf("%d %dx%dx%dx%d%n", delta, shapeX, shapeY, shapeWidth, shapeHeight);
shapes.get(currentIndex).reset();
shapes.get(currentIndex).append(
new Ellipse2D.Double(shapeX, shapeY, shapeWidth, shapeHeight), true);
repaint();
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (resizing) {
System.out.println("Done resizing");
resizing = false;
clickPoint = null;
originalBounds = null;
}
}
}
}
}
来源:https://stackoverflow.com/questions/26476645/resizing-path2d-circle-by-clicking-and-dragging-its-outer-edge