首页 > 解决方案 > 我做错了什么导致此图表滚动不正确?

问题描述

我正在尝试创建一个简单的 Java 程序,该程序将数学函数作为输入并在 JPanel 中显示 2D(x 和 y)图。我的图表最初显示正确。我尝试向画布添加滚动(我调用了 JPanel 子类,我创建了一个 GraphCanvas),我的意思是当鼠标在画布上拖动时,2D 平面也会滚动,并且图形会针对新显示的区域进行更新。为了显示这个实际函数,我使用了一个 Path2D 对象,该对象由连接足够多点的线段组成,以便图形处于最大分辨率,并且我一直在使用 AffineTransforms 来转换路径。值得注意的是,到目前为止,我只使用 f(x) 形式的函数测试了程序,而不是参数,即使我 我们在 GraphedInstance 类中为参数函数创建了一个类和支持代码。我不想包含项目的所有代码,但我将其推送到 GitHub 存储库这里(我对 GitHub 的工作原理知之甚少,但这应该是正确的)。

我的问题是,目前,每当我尝试滚动图形时,该函数都会做自己的事情,并且无法与初始化的部分正确连接。它没有正确显示新生成的部分。

我已经对其进行了一些工作,并通过等待转换描述函数的路径,直到我向其中添加了新部分,显着改善了问题,但它仍然无法正确连接。

这是应该创建函数路径的新部分并将其缝合在一起的方法,我认为问题可能出在此处。这个和项目的其余部分也可以在 GitHub 存储库中找到。另外,对于任何乱七八糟的代码和/或缺少评论,我很抱歉,我只是大学一年级的学生,并没有真正接受过任何正式的 Java 培训,我只是在前进的过程中学会了它。我很乐意接受任何建议或更好的方法来做任何事情!


import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;

public class Minimal {

    private static int[] lastPointer = new int[2];
    private static double xMin, xMax, yMin, yMax;
    private static double xScale, yScale;
    private static double minXGraphed, maxXGraphed;

    public static Path2D.Double function;

    //The frame enclosing the graphing canvas
    private static JFrame frame;
    //The graphing canvas
    private static JPanel canvas = new JPanel() {
        private static final int DEFAULT_CANVAS_SIZE = 600;

        //Ensures the frame initialized to the right size
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(DEFAULT_CANVAS_SIZE, DEFAULT_CANVAS_SIZE);
        }

        //Paints the graph whenever it needs updating
        @Override
        public void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            super.paintComponent(g2);

            xScale = getWidth() / (xMax - xMin);
            yScale = getHeight() / (yMax - yMin);

            g2.setColor(Color.BLUE);
            try {
                g2.draw(function);
            } catch (NullPointerException e) {
                e.printStackTrace();
            }
        }

    };

    /**
     * I'm using the function y = exp(x) as an example. Feel free to replace it.
     * @param x the x-value
     * @return exp(x), the y-value
     */
    private static double getYVal(double x) {
        return Math.exp(x);
    }

    //Initializes the function path
    public static void initPath() {
        final double STEP_X = (xMax - xMin) / canvas.getWidth();

        function = new Path2D.Double();

        double x = xMin;
        double y = getYVal(x);

        function.moveTo(getGraphX(x), getGraphY(y));
        for(x = xMin + STEP_X; x <= xMax; x += STEP_X) {
            y = getYVal(x);
            function.lineTo(getGraphX(x), getGraphY(y));
        }
    }

    /**
     * Gets the pixel x-coordinate on the canvas
     * that corresponds to a given x-value of the function
     * @param x the x-value of the function
     * @return the adjusted x-coordinate
     */
    private static int getGraphX(double x) {
        return (int) (canvas.getWidth() * (x - xMin) / (xMax - xMin));
    }

    //Same thing as getGraphX except for y-coords
    private static int getGraphY(double y) {
        return (int) (canvas.getHeight() * (yMax - y) / (yMax - yMin));
    }

    /*
     * This is probably where the problem is, this extends the path so that it
     *      covers the entire visible range
     */
    public static void updateFunctionBounds(double dx) {

        double newXMin = xMin - dx / xScale;
        double newXMax = xMax - dx / xScale;

        final int WIDTH = canvas.getWidth();
        final double STEP_X = (xMax - xMin) / WIDTH;
        double drawTo;

        if((drawTo = newXMin) < xMin && drawTo < minXGraphed) {
            minXGraphed = drawTo;

            double x = drawTo;
            function.moveTo(getGraphX(x), getGraphY(getYVal(x)));

            for(x = drawTo + STEP_X; x < xMin; x += STEP_X) {
                function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
            }

            x = xMin;
            function.lineTo(getGraphX(x), getGraphY(getYVal(x)));

        } else if((drawTo = newXMax) > xMax && drawTo > maxXGraphed) {
            maxXGraphed = drawTo;

            double x = xMax;
            function.moveTo(getGraphX(x), getGraphY(getYVal(x)));

            for(x = xMax + STEP_X; x < drawTo; x += STEP_X) {
                function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
            }

            x = drawTo;
            function.lineTo(getGraphX(x), getGraphY(getYVal(x)));

        }
    }

    public static void main(String[] args) {
        initBounds();
        initFrame();
    }

    //Initializes the graph boundaries. Feel free to change this
    private static void initBounds() {
        xMin = yMin = minXGraphed = -10;
        xMax = yMax = maxXGraphed = 10;
    }

    //Initializes the frame and a MouseAdapter that controls the scrolling
    private static void initFrame() {
        MouseAdapter adapter = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                super.mousePressed(e);
                lastPointer[0] = e.getX();
                lastPointer[1] = e.getY();
            }

            @Override
            public void mouseDragged(MouseEvent e) {
                super.mouseDragged(e);
                AffineTransform t = new AffineTransform();
                double dx = e.getX() - lastPointer[0];
                double dy = e.getY() - lastPointer[1];
                t.translate(dx, dy);

                updateFunctionBounds(dx);
                function.transform(t);

                xMin -= dx / xScale;
                xMax -= dx / xScale;
                yMin -= dy / yScale;
                yMax -= dy / yScale;

                canvas.repaint();

                lastPointer[0] = e.getX();
                lastPointer[1] = e.getY();
            }
        };
        canvas.addMouseListener(adapter);
        canvas.addMouseMotionListener(adapter);

        frame = new JFrame("Minimal Reproducible Approach");
        frame.setContentPane(canvas);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.pack();
        frame.setVisible(true);

        initPath();
        canvas.repaint();
    }
}

如果它工作正常,拼接将是无缝的,并且当鼠标指针在画布上拖动时,该功能将平滑滚动,有点像在 Desmos 中所做的那样。实际上,它不会保留正确的高度(y 值),并且在所有情况下都不会实际连接到先前的段。

感谢您的任何帮助!

编辑:我已将代码更新为我认为的最小可重现示例。

标签: javaswingawt

解决方案


推荐阅读