001package jmri.jmrit.display.controlPanelEditor.shape;
002
003import java.awt.Graphics;
004import java.awt.Graphics2D;
005import java.awt.Point;
006import java.awt.Rectangle;
007import java.awt.event.ActionEvent;
008import java.awt.geom.GeneralPath;
009import java.awt.geom.PathIterator;
010import java.util.ArrayList;
011
012import javax.swing.Box;
013import javax.swing.BoxLayout;
014import javax.swing.JButton;
015import javax.swing.JLabel;
016import javax.swing.JPanel;
017
018import jmri.jmrit.display.Editor;
019import jmri.util.swing.JmriMouseEvent;
020
021import org.slf4j.Logger;
022import org.slf4j.LoggerFactory;
023
024/**
025 * @author Pete Cressman Copyright (c) 2013
026 */
027public class DrawPolygon extends DrawFrame {
028
029    ArrayList<Point> _vertices;
030    int _curX;
031    int _curY;
032    private static final int NEAR = PositionableShape.SIZE;
033    private final boolean _editing;
034
035    public DrawPolygon(String which, String title, PositionableShape ps, Editor ed, boolean create) {
036        super(which, title, ps, ed, create);
037        _vertices = new ArrayList<>();
038        _editing = (ps != null);
039    }
040
041    @Override
042    protected JPanel makeParamsPanel() {
043        JPanel panel = super.makeParamsPanel();
044        int x = getX();
045        int y = getY();
046        PathIterator iter = _shape.getPathIterator(null);
047        float[] coord = new float[6];
048        if (_editing) {
049            while (!iter.isDone()) {
050                iter.currentSegment(coord);
051                _vertices.add(new Point(x + Math.round(coord[0]), y + Math.round(coord[1])));
052                iter.next();
053            }
054        }
055        ((PositionablePolygon)_shape).editing(_editing);
056        _shape.drawHandles();
057
058        panel.add(Box.createVerticalGlue());
059        panel.add(new JLabel(Bundle.getMessage("drawInstructions2c")));
060        JPanel p = new JPanel();
061        p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
062        p.add(Box.createHorizontalGlue());
063        JButton addButton = new JButton(Bundle.getMessage("ButtonAddVertex"));
064        addButton.addActionListener((ActionEvent a) -> addVertex(false));
065        p.add(addButton);
066        p.add(Box.createHorizontalGlue());
067
068        JButton deleteButton = new JButton(Bundle.getMessage("ButtonDeleteVertex"));
069        deleteButton.addActionListener((ActionEvent a) -> deleteVertex());
070        p.add(deleteButton);
071        p.add(Box.createHorizontalGlue());
072        panel.add(p);
073        panel.add(Box.createVerticalGlue());
074        return panel;
075    }
076
077    // double click was made -
078    @Override
079    protected PositionableShape makeFigure(Rectangle r, Editor ed) {
080/*        Point pt = new Point(event.getX(), event.getY());
081        boolean closed;           Do this later
082        if (near(_vertices.get(0), pt)) {
083            closed = true; // close polygon
084        } else {
085            closed = false;
086        }*/
087        Point spt = getStartPoint();
088        _shape = new PositionablePolygon(ed, makePath(spt));
089        log.debug("makeFigure {} vertices", _vertices.size());
090        if (_vertices.size() < 2) {
091            return null;
092        }
093        return _shape;
094    }
095
096    /*
097     * Rubber Band line
098     * @see jmri.jmrit.display.controlPanelEditor.shape.DrawFrame#drawLine(int, int)
099     */
100    protected void moveTo(int x, int y) {
101//        log.debug("moveTo ({}, {})", x, y);
102        if (!_editing) {
103            _curX = x;
104            _curY = y;
105        }
106    }
107
108    protected void anchorPoint(int x, int y) {
109        log.debug("anchorPoint ({}, {})", x, y);
110        _curX = x;
111        _curY = y;
112        Point anchorPt = new Point(x, y);
113        for (Point vertex : _vertices) {
114            if (near(vertex, anchorPt)) {
115                _curX = x;
116                _curY = y;
117                return;
118            }
119        }
120    }
121
122    protected void drawShape(Graphics g) {
123        if (!_editing) {
124            if (_vertices.size() < 1 || !(g instanceof Graphics2D)) {
125                return;
126            }
127            Graphics2D g2d = (Graphics2D)g;
128            // disabled to satisfy P Bender
129            //_lineWidth = _lineSlider.getValue();
130            //BasicStroke stroke = new BasicStroke(_lineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f);
131            //g2d.setColor(_lineColor);
132            //g2d.setStroke(stroke);
133            GeneralPath path = makePath(new Point(0, 0));
134            path.lineTo(_curX, _curY);
135            g2d.draw(path);
136        }
137    }
138
139    protected void makeVertex(JmriMouseEvent event, Editor ed) {
140        log.debug("makeVertex point= ({}, {}), _editing= {}", event.getX(), event.getY(), _editing);
141        if (!_editing) {    // creating new polygon
142             Point pt = new Point(event.getX(), event.getY());
143             int size = _vertices.size();
144             if ( size == 0 || !near(_vertices.get(size-1), pt)) {
145                 _vertices.add(pt);
146             }
147        }
148    }
149
150    protected boolean doHandleMove(int hitIndex, Point pt) {
151        Point p = _vertices.get(hitIndex);
152        p.x += pt.x;
153        p.y += pt.y;
154        if (_editing) {
155            _shape.setShape(makePath(getStartPoint()));
156        }
157        return false;
158    }
159
160    /**
161     * @param pt is "startPoint" the upper left corner of the figure
162     */
163    private GeneralPath makePath(Point pt) {
164        GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD, _vertices.size() + 1);
165        path.moveTo(_vertices.get(0).x - pt.x, _vertices.get(0).y - pt.y);
166        for (int i = 1; i < _vertices.size(); i++) {
167            path.lineTo(_vertices.get(i).x - pt.x, _vertices.get(i).y - pt.y);
168        }
169//        if (closed) {
170//            path.lineTo(_vertices.get(0).x - pt.x, _vertices.get(0).y - pt.y);
171//        }
172        return path;
173    }
174
175    /*
176     * "startPoint" will be the upper left corner of the figure
177     * <p>
178     */
179    protected Point getStartPoint() {
180        if (_vertices.size() > 0) {
181            int x = _vertices.get(0).x;
182            int y = _vertices.get(0).y;
183            for (int i = 1; i < _vertices.size(); i++) {
184                x = Math.min(x, _vertices.get(i).x);
185                y = Math.min(y, _vertices.get(i).y);
186            }
187            return new Point(x, y);
188        } else {
189            _vertices.add( new Point(_curX, _curY));
190            return new Point(_curX, _curY);
191        }
192    }
193
194    static private boolean near(Point p1, Point p2) {
195        return Math.abs(p1.x - p2.x) < NEAR && Math.abs(p1.y - p2.y) < NEAR;
196    }
197
198    @Override
199    void setDisplayWidth(int w) {
200    }
201
202    @Override
203    void setDisplayHeight(int h) {
204    }
205
206    /**
207     *  Add a vertex to polygon relative to selected vertex
208     * @param up  if true, add after selected vertex. otherwise before selected vertex
209     */
210    protected void addVertex(boolean up) {
211        if (_editing) {
212            int hitIndex = _shape._hitIndex;
213            if (hitIndex < 0) {
214                if (_vertices.size() > 1) {
215                    hitIndex = _vertices.size() - 1;
216                } else {
217                    hitIndex = 0;
218                    if (_vertices.size() == 0) {
219                        _vertices.add(new Point(_curX, _curY));
220                    }
221                }
222                _shape._hitIndex = hitIndex;
223                up = true;
224            }
225            Point r1 = _vertices.get(hitIndex);
226            Point newVertex;
227            if (up) {
228                if (hitIndex == _vertices.size() - 1) {
229                    newVertex = new Point(r1.x + 20, r1.y + 20);
230                } else {
231                    Point r2 = _vertices.get(hitIndex + 1);
232                    newVertex = new Point((r1.x + r2.x) / 2, (r1.y + r2.y) / 2);
233                }
234                _shape._hitIndex++;
235                hitIndex++;
236            } else {
237               if (hitIndex > 0) {
238                    Point r2 = _vertices.get(hitIndex - 1);
239                    newVertex = new Point((r1.x + r2.x) / 2, (r1.y + r2.y) / 2);
240                } else {
241                    newVertex = new Point(r1.x + 20, r1.y + 20);
242                }
243            }
244            _vertices.add(hitIndex, newVertex);
245            _shape.setShape(makePath(getStartPoint()));
246            _shape.drawHandles();
247            _shape.updateSize();
248        }
249    }
250
251    protected void deleteVertex() {
252        if (_editing) {
253            if (_vertices.size() <= 2) {
254                return;
255            }
256            int hitIndex = _shape._hitIndex;
257            if (hitIndex < 0) {
258                hitIndex = _vertices.size() - 1;
259            }
260            _vertices.remove(hitIndex);
261            if (hitIndex > 0) {
262                _shape._hitIndex--;
263            }
264            _shape.setShape(makePath(getStartPoint()));
265            _shape.drawHandles();
266            _shape.updateSize();
267        }
268    }
269
270    private final static Logger log = LoggerFactory.getLogger(DrawPolygon.class);
271}