Rounding Inaccuracies When Combining Areas in Java?

前端 未结 3 2154
迷失自我
迷失自我 2020-12-21 05:08

I\'m working with Areas in Java.

My test program draws three random triangles and combines them to form one or more polygons. After the Areas

3条回答
  •  醉酒成梦
    2020-12-21 05:41

    I've re-factored your example to make testing easier, adding features of both answers. Restoring triangle.reset() seemed to eliminate the artifatcts for me. In addition,

    • Build the GUI on the event dispatch thread.

    • For rendering, extend a JComponent, e.g. JPanel, and override paintComponent().

    • Absent subcomponents having a preferred size, override getPreferredSize().

    • Use RenderingHints.

    SSCCE:

    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.RenderingHints;
    import java.awt.event.ActionEvent;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.Area;
    import java.awt.geom.Line2D;
    import java.awt.geom.Path2D;
    import java.awt.geom.PathIterator;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    import javax.swing.AbstractAction;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JSpinner;
    import javax.swing.SpinnerNumberModel;
    import javax.swing.event.ChangeEvent;
    import javax.swing.event.ChangeListener;
    
    /** @see http://stackoverflow.com/q/9526835/230513 */
    public class AreaTest extends JPanel {
    
        private static final int SIZE = 500;
        private static final int INSET = SIZE / 10;
        private static final int BOUND = SIZE - 2 * INSET;
        private static final int N = 5;
        private static final AffineTransform I = new AffineTransform();
        private static final double FLATNESS = 1;
        private static final Random random = new Random();
        private Area area = new Area();
        private List areaSegments = new ArrayList();
        private int count = N;
    
        AreaTest() {
            setLayout(new BorderLayout());
            create();
            add(new JPanel() {
    
                @Override
                public void paintComponent(Graphics g) {
                    Graphics2D g2d = (Graphics2D) g;
                    g2d.setRenderingHint(
                        RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
                    g.setColor(Color.lightGray);
                    g2d.fill(area);
                    g.setColor(Color.black);
                    for (Line2D.Double line : areaSegments) {
                        g2d.draw(line);
                    }
                }
    
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(SIZE, SIZE);
                }
            });
    
            JPanel control = new JPanel();
            control.add(new JButton(new AbstractAction("Update") {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    create();
                    repaint();
                }
            }));
            JSpinner countSpinner = new JSpinner();
            countSpinner.setModel(new SpinnerNumberModel(N, 3, 42, 1));
            countSpinner.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    JSpinner s = (JSpinner) e.getSource();
                    count = ((Integer) s.getValue()).intValue();
                }
            });
            control.add(countSpinner);
            add(control, BorderLayout.SOUTH);
        }
    
        private int randomPoint() {
            return random.nextInt(BOUND) + INSET;
        }
    
        private void create() {
            area.reset();
            areaSegments.clear();
            Path2D.Double triangle = new Path2D.Double();
    
            // Draw three random triangles
            for (int i = 0; i < count; i++) {
                triangle.moveTo(randomPoint(), randomPoint());
                triangle.lineTo(randomPoint(), randomPoint());
                triangle.lineTo(randomPoint(), randomPoint());
                triangle.closePath();
                area.add(new Area(triangle));
                triangle.reset();
            }
    
            // Note: we're storing double[] and not Point2D.Double
            List areaPoints = new ArrayList();
            double[] coords = new double[6];
    
            for (PathIterator pi = area.getPathIterator(I, FLATNESS);
                !pi.isDone(); pi.next()) {
    
                // Because the Area is composed of straight lines
                int type = pi.currentSegment(coords);
                // We record a double array of {segment type, x coord, y coord}
                double[] pathIteratorCoords = {type, coords[0], coords[1]};
                areaPoints.add(pathIteratorCoords);
            }
    
            // To record where each polygon starts
            double[] start = new double[3];
            for (int i = 0; i < areaPoints.size(); i++) {
                // If we're not on the last point, return a line from this point to the next
                double[] currentElement = areaPoints.get(i);
    
                // We need a default value in case we've reached the end of the List
                double[] nextElement = {-1, -1, -1};
                if (i < areaPoints.size() - 1) {
                    nextElement = areaPoints.get(i + 1);
                }
    
                // Make the lines
                if (currentElement[0] == PathIterator.SEG_MOVETO) {
                    // Record where the polygon started to close it later
                    start = currentElement;
                }
    
                if (nextElement[0] == PathIterator.SEG_LINETO) {
                    areaSegments.add(
                        new Line2D.Double(
                        currentElement[1], currentElement[2],
                        nextElement[1], nextElement[2]));
                } else if (nextElement[0] == PathIterator.SEG_CLOSE) {
                    areaSegments.add(
                        new Line2D.Double(
                        currentElement[1], currentElement[2],
                        start[1], start[2]));
                }
            }
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    JFrame f = new JFrame();
                    f.add(new AreaTest());
                    f.pack();
                    f.setLocationRelativeTo(null);
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    f.setResizable(false);
                    f.setVisible(true);
                }
            });
        }
    }
    

提交回复
热议问题