且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2023-11-19 20:28:40

首先阅读一下

Let's start by having a read through the JavaDocs for Path2D#curveTo

公共最终空隙curveTo(double x1,
y1的两倍,
双倍x2,
y2的两倍,
双倍x3,
double y3)

通过绘制一条Bézier曲线,将由三个新点定义的弯曲线段添加到路径中 与当前坐标和指定坐标相交 (x3,y3),使用指定点(x1,y1)和(x2,y2)作为Bézier 控制点.所有坐标均以双精度指定.

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.

好吧,公平地说,这需要一点阅读才能理解,这也许可以通过一些图形更好地解决.

Okay, let's be fair, that takes a little bit of reading to understand, which is probably better addressed with some graphics.

基本概念是,您具有三个控制点,其中一个控制点充当观察"约束.

The basic concept is, you have three control points, one of which is acting as a "look at" constraint.

让我们假设您有三个点,anchortargetclick. anchor是第一个点,target是拖动到的点,click是拖动操作完成后单击的点.

Let's assume you have three points, anchor, target and click. anchor is the first point, target is the point dragged to and click is the point which was clicked AFTER the drag operation completed.

如果使用顺序anchor, target, click,则最终得到一条类似

If we use the order anchor, target, click, we end up with a curve looking something like

其中P1 == anchorP2 == targetP3 == click.如您所见,P2充当查看约束",并试图将结果曲线拉向该约束.

where P1 == anchor, P2 == target and P3 == click. As you can see, the P2 is acting as the "look at constraint" and is trying to pull the resulting curve towards it.

但是,如果相反,我们使用anchor, click, target,我们最终会遇到类似...

However, if, instead, we use anchor, click, target, we end up within something more like...

其中P1 == anchorP2 == clickP3 == target.如您所见,P2充当查看约束",并试图将生成的曲线向其拉.

where P1 == anchor, P2 == click and P3 == target. As you can see, the P2 is acting as the "look at constraint" and is trying to pull the resulting curve towards it.

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

This is essentially how a Bézier curve works

好的,总的来说,我写了一些测试代码,您可以使用它们来更改参数并测试想法

Okay, so to put it all together, I wrote some test code, which you can use to change the parameter and test the idea

此示例所做的一件事是,在建立前两个点之后,随着鼠标的移动,绘制了预期曲线的伪"示例,从而使您对曲线的外观有了视觉反馈,直到用户单击鼠标并设置了路径

One thing this example does is it paints a "fake" example of the intended curve as the mouse is moved, after the first two points are established, giving you some visual feed back of what the curve will look like, until the user clicks the mouse and the path is set

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();
        }

    }

}