Search code examples
javagraphics2dpolyline

Is it possible to create an outline around a PolyLine


This example draws a simple PolyLine.

Is it possible to draw an outline around this PolyLine in red. Not a single large red square but one that outlines the original PolyLine by 3-5 points away from all areas.

Some calculations were attempted and work for a fixed value, but when the PolyLine values are random, the algorithm doesn't always work as the next section of the line could turn right instead of left or up instead of down.

You almost have to look 2-3 points ahead to know if you are going to have add or subtract.

Is there an easier way to do it?

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class PolyLine extends JPanel 
{

  public void paint(Graphics g) {


        int[] xs = {25,  125, 85,  75, 25, 65, };
        int[] ys = {50,  50, 100,  110, 150, 100};

        BasicStroke traceStroke = new BasicStroke (1);
        Graphics2D gc = (Graphics2D) g.create();
        gc.setStroke(traceStroke);
        gc.setColor(Color.BLUE);
        gc.drawPolyline(xs, ys, 6);
      }

      public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.add(new PolyLine());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(20,20, 1500,1500);
        frame.setVisible(true);
        }

}

Solution

  • First, a remark: It's usually preferable to have the geometric primitives as a Shape. The drawPolyline function (which uses these odd integer array coordinates) is somewhat out-dated. Creating the polyline as a Path2D is far more flexible.

    For the task that you described, it will also be necessary to convert the polyline coordinates into a Path2D (if you switched to Path2D anyhow, you could omit this conversion step).

    When you have the polyline as a Shape, the task is rather simple: You can create a stroked version of this shape, using a BasicStroke with the desired thickness and cap/join characteristics, by calling BasicStroke#createStrokedShape. This shape will basically be the shape of a "thick line". In order to avoid artifacts at the joins, you can create an Area from this Shape, and then draw this area.

    So in the end, painting the actual outline is done with 2 lines of code, and the result is as follows:

    Outline01.png

    But the MCVE here, for completeness:

    import java.awt.BasicStroke;
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.geom.Area;
    import java.awt.geom.Path2D;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class ShapeOutlineTest
    {
        public static void main(String[] args)
        {
            SwingUtilities.invokeLater(() -> createAndShowGUI());
        }
    
        private static void createAndShowGUI()
        {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            f.getContentPane().add(new ShapeOutlineTestPanel());
            f.setSize(500, 500);
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        }
    }
    
    class ShapeOutlineTestPanel extends JPanel
    {
        @Override
        protected void paintComponent(Graphics gr)
        {
            super.paintComponent(gr);
            Graphics2D g = (Graphics2D) gr;
    
            int[] xs = { 25, 125, 85, 75, 25, 65, };
            int[] ys = { 50, 50, 100, 110, 150, 100 };
    
            BasicStroke traceStroke = new BasicStroke(1);
            g.setStroke(traceStroke);
            g.setColor(Color.BLUE);
            g.drawPolyline(xs, ys, 6);
    
            Path2D path = new Path2D.Double();
            for (int i = 0; i < xs.length; i++)
            {
                if (i == 0)
                {
                    path.moveTo(xs[i], ys[i]);
                }
                else
                {
                    path.lineTo(xs[i], ys[i]);
                }
            }
            g.setColor(Color.RED);
            BasicStroke stroke = new BasicStroke(
                10.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
            g.draw(new Area(stroke.createStrokedShape(path)));
    
        }
    }