使用鼠标拖动在 Java 中绘制曲线

Drawing a curve in Java using mouse drag

我是 java 的新手,我正在尝试绘制曲线。我试图实现的功能是曲线应该通过鼠标拖动然后单击鼠标来定义。 拖动动作完成后,我希望从拖动开始到结束绘制一条线。之后点击canvas应该算是定义曲线所必需的第三个点,绘制的线应该变成曲线。
我怎么做? 我在这里浏览了关于如何绘制贝塞尔曲线的不同帖子,但我非常困惑。这是我使用鼠标事件绘制矩形、椭圆和线条的代码块:

   public void mousePressed(MouseEvent e) {

        xCoordinateInitial = e.getX(); //Initialize x-coordinate to the mouse x-coordinate
        yCoordinateInitial = e.getY() + shiftInY; //Initialize y-coordinate to the mouse y-coordinate
        System.out.println("X-coordinate: " + xCoordinateInitial);
        System.out.println("Y-coordinate: " + yCoordinateInitial);
    }

    public void mouseReleased(MouseEvent e) {

        Graphics2D G = (Graphics2D) getGraphics();
        G.setStroke(new BasicStroke(lineThickness));
        G.setColor(colorSelected);
        G.setPaint(colorSelected);
        xCoordinateFinal = e.getX(); //Set final x-coordinate to the mouse x-coordinate after drag
        yCoordinateFinal = e.getY() + shiftInY; //Set final y-coordinate to the mouse y-coordinate after drag
        int x = xCoordinateInitial;
        int y = yCoordinateInitial;
        int width = xCoordinateFinal - xCoordinateInitial; //Setting width
        int height = yCoordinateFinal - yCoordinateInitial; //Setting height



        if (yCoordinateFinal < yCoordinateInitial) {
            y = yCoordinateFinal;
            height = yCoordinateInitial - yCoordinateFinal;
        }

        if (xCoordinateFinal < xCoordinateInitial) {
            x = xCoordinateFinal;
            width = xCoordinateInitial - xCoordinateFinal;
        }


        // Shape Selection
        switch (shapeSelected) {
            case Line:

                G.drawLine(xCoordinateInitial, yCoordinateInitial, xCoordinateFinal, yCoordinateFinal);
                break;

            case Rectangle:
                G.fillRect(x, y, width, height);
                break;

            case Oval:
                G.fillOval(x, y, width, height);
                break;

            case Curve :

                // To implement

        }

    }
});

我试图理解以下内容,但无法理解:

                        Path2D p = new GeneralPath();
                        p.moveTo(x1, y1);
                        p.curveTo(bx1, by1, bx2, by2, x2, y2);
                        G.draw(p);

让我们首先通读 Path2D#curveTo

的 JavaDocs

public final void curveTo(double x1,
double y1,
double x2,
double y2,
double x3,
double y3)

Adds a curved segment, defined by three new points, to the path by drawing a Bézier curve that intersects both the current coordinates and the specified coordinates (x3,y3), using the specified points (x1,y1) and (x2,y2) as Bézier control points. All coordinates are specified in double precision.

好吧,说句公道话,这需要一点阅读才能理解,用一些图形可能会更好地解决这个问题。

基本概念是,您有三个控制点,其中一个作为 "look at" 约束。

假设您有三个点,anchortargetclickanchor 是第一个点,target 是拖动到的点,click 是拖动操作完成后单击的点。

如果我们使用顺序 anchor, target, click,我们最终得到的曲线看起来像

其中 P1 == anchorP2 == targetP3 == click。如您所见,P2 充当 "look at constraint" 并试图将生成的曲线拉向它。

然而,如果我们使用 anchor, click, target,我们最终会得到更像...

其中 P1 == anchorP2 == clickP3 == target。如您所见,P2 充当 "look at constraint" 并试图将生成的曲线拉向它。

这基本上就是贝塞尔曲线的工作原理

示例...

好的,综上所述,我写了一些测试代码,您可以使用它来更改参数并测试想法

这个例子做的一件事是它在移动鼠标时绘制一个 "fake" 预期曲线的例子,在建立前两个点之后,给你一些关于曲线外观的视觉反馈就像,直到用户单击鼠标并设置路径

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.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private boolean dragging = false;

        private Point anchor;
        private Point target;

        private Shape fakePath;

        private List<Shape> shapes = new ArrayList<>(25);

        public TestPane() {
            addMouseMotionListener(new MouseAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    dragging = true;
                    target = new Point(e.getPoint());
                    repaint();
                }

                @Override
                public void mouseMoved(MouseEvent e) {
                    if (target != null && anchor != null) {
                        fakePath = makePath(anchor, e.getPoint(), target);
                        repaint();
                    }
                }

            });

            addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    if (anchor == null) {
                        target = null;
                        anchor = new Point(e.getPoint());
                    }
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    dragging = false;
                }

                @Override
                public void mouseClicked(MouseEvent e) {
                    if (anchor != null && target != null) {
                        fakePath = null;

                        shapes.add(makePath(anchor, e.getPoint(), target));

                        anchor = null;
                        target = null;

                        repaint();
                    }
                }
            });
        }

        protected Path2D makePath(Point p1, Point p2, Point p3) {
            Path2D p = new GeneralPath();
            p.moveTo(p1.getX(), p1.getY());
            p.curveTo(p1.getX(), p1.getY(),
                            p2.getX(), p2.getY(),
                            p3.getX(), p3.getY());

            return p;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
            g2d.setColor(Color.BLACK);
            for (Shape shape : shapes) {
                g2d.draw(shape);
            }
            if (anchor != null && target != null) {
                g2d.setColor(Color.GREEN);
                g2d.draw(new Line2D.Double(anchor, target));
            }
            if (fakePath != null) {
                g2d.setColor(Color.BLUE);
                g2d.draw(fakePath);
            }
            g2d.dispose();
        }

    }

}