001package jmri.jmrit.display.layoutEditor;
002
003import java.awt.*;
004import java.awt.geom.Line2D;
005import java.awt.geom.Point2D;
006import java.awt.geom.Rectangle2D;
007
008import javax.annotation.Nonnull;
009import javax.swing.*;
010
011import jmri.util.*;
012
013/*
014* This is an intermediate component used to put the Layout Editor
015* into the component layers hierarchy so that objects can be drawn
016* in front of or behind layout editor objects.
017*
018* @author George Warner Copyright (c) 2017-2018
019 */
020class LayoutEditorComponent extends JComponent {
021
022    private final LayoutEditor layoutEditor;
023
024    // Antialiasing rendering
025    protected static final RenderingHints antialiasing = new RenderingHints(
026            RenderingHints.KEY_ANTIALIASING,
027            RenderingHints.VALUE_ANTIALIAS_ON);
028
029    protected LayoutEditorComponent(@Nonnull final LayoutEditor LayoutEditor) {
030        super();
031        this.layoutEditor = LayoutEditor;
032    }
033
034    /*
035    * {@inheritDoc}
036     */
037    @Override
038    public void paint(Graphics g) {
039        if (g instanceof Graphics2D) {
040            // layoutEditor.draw((Graphics2D) g);
041
042            Graphics2D g2 = (Graphics2D) g;
043
044            if (clipBounds != null) {
045                if (!clipBounds.isEmpty()) {
046                    if ((clipBounds.getWidth() > 0) && (clipBounds.getHeight() > 0)) {
047                        // clipBounds is the visible part of the panel, 
048                        // used here to cull drawing, while the incoming clip in g2
049                        // is the area Swing actually asked for in original graphics units 
050                        // (not scaled units). 
051
052                        log.debug("LEComponent.paint(): original g2 clip: {} requested clip: {}", 
053                                    g2.getClipBounds(), clipBounds);
054
055                        g2.setClip(clipBounds);
056                    }
057                }
058            }
059            // Optional antialising, to eliminate (reduce) staircase on diagonal lines
060            if (layoutEditor.getAntialiasingOn()) {
061                g2.setRenderingHints(antialiasing);
062            }
063
064            // drawPositionableLabelBorder(g2);
065            // things that only get drawn in edit mode
066            if (layoutEditor.isEditable()) {
067                if (layoutEditor.getDrawGrid()) {
068                    drawPanelGrid(g2);
069                }
070                drawLayoutTracksHidden(g2);
071            }
072
073            drawShapes(g2, true);
074            drawTrackSegmentsDashed(g2);
075            drawLayoutTracksBallast(g2);
076            drawLayoutTracksTies(g2);
077            drawLayoutTracksRails(g2);
078            drawLayoutTracksBlockLines(g2);
079
080            drawPositionablePoints(g2, false);
081            drawPositionablePoints(g2, true);
082
083            drawShapes(g2, false);
084
085            drawDecorations(g2);
086
087            // things that only get drawn in edit mode
088            if (layoutEditor.isEditable()) {
089                drawLayoutTrackEditControls(g2);
090                drawShapeEditControls(g2);
091
092                drawMemoryRects(g2);
093                drawGlobalVariableRects(g2);
094                drawBlockContentsRects(g2);
095
096                if (layoutEditor.allControlling()) {
097                    drawTurnoutControls(g2);
098                }
099                drawSelectionRect(g2);
100                highLightSelection(g2);
101
102                drawTrackSegmentInProgress(g2);
103                drawShapeInProgress(g2);
104
105                if (layoutEditor.isDrawLayoutTracksLabel()) {
106                    drawLayoutTracksLabel(g2);
107                }
108            } else if (layoutEditor.getTurnoutCircles()) {
109                if (layoutEditor.allControlling()) {
110                    drawTurnoutControls(g2);
111                }
112            }
113        } else {
114            log.error("LayoutEditor drawing requires Graphics2D");
115        }
116    }
117
118    private void drawPanelGrid(Graphics2D g2) {
119        int wideMod = layoutEditor.gContext.getGridSize() * layoutEditor.gContext.getGridSize2nd();
120        int wideMin = layoutEditor.gContext.getGridSize() / 2;
121
122        // granulize puts these on getGridSize() increments
123        int minX = 0;
124        int minY = 0;
125        int maxX = (int) MathUtil.granulize(layoutEditor.gContext.getLayoutWidth(), layoutEditor.gContext.getGridSize());
126        int maxY = (int) MathUtil.granulize(layoutEditor.gContext.getLayoutHeight(), layoutEditor.gContext.getGridSize());
127
128        log.debug("drawPanelGrid: minX: {}, minY: {}, maxX: {}, maxY: {}", minX, minY, maxX, maxY);
129
130        Point2D startPt, stopPt;
131        BasicStroke narrow = new BasicStroke(1.0F, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
132        BasicStroke wide = new BasicStroke(2.0F, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
133
134        g2.setColor(Color.gray);
135        g2.setStroke(narrow);
136
137        // draw horizontal lines
138        for (int y = minY; y <= maxY; y += layoutEditor.gContext.getGridSize()) {
139            startPt = new Point2D.Double(minX, y);
140            stopPt = new Point2D.Double(maxX, y);
141
142            if ((y % wideMod) < wideMin) {
143                g2.setStroke(wide);
144                g2.draw(new Line2D.Double(startPt, stopPt));
145                g2.setStroke(narrow);
146            } else {
147                g2.draw(new Line2D.Double(startPt, stopPt));
148            }
149        }
150
151        // draw vertical lines
152        for (int x = minX; x <= maxX; x += layoutEditor.gContext.getGridSize()) {
153            startPt = new Point2D.Double(x, minY);
154            stopPt = new Point2D.Double(x, maxY);
155
156            if ((x % wideMod) < wideMin) {
157                g2.setStroke(wide);
158                g2.draw(new Line2D.Double(startPt, stopPt));
159                g2.setStroke(narrow);
160            } else {
161                g2.draw(new Line2D.Double(startPt, stopPt));
162            }
163        }
164    }
165
166    //
167    //  draw hidden layout tracks
168    //
169    private void drawLayoutTracksHidden(Graphics2D g2) {
170        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
171        Stroke stroke = new BasicStroke(1.F);
172        Stroke dashedStroke = new BasicStroke(1.F,
173                BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.F,
174                new float[]{6.F, 4.F}, 0);
175
176        // setup for drawing hidden sideline rails
177        g2.setColor(ltdo.getSideRailColor());
178        g2.setStroke(stroke);
179        boolean main = false, block = false, hidden = true, dashed = false;
180        draw1(g2, main, block, hidden, dashed);
181        g2.setStroke(dashedStroke);
182        draw1(g2, main, block, hidden, dashed = true);
183
184        // setup for drawing mainline rails
185        main = true;
186        g2.setColor(ltdo.getMainRailColor());
187        g2.setStroke(stroke);
188        draw1(g2, main, block, hidden, dashed = false);
189        g2.setStroke(dashedStroke);
190        dashed = true;
191        draw1(g2, main, block, hidden, dashed);
192    }
193
194    //
195    //  draw dashed track segments
196    //
197    private void drawTrackSegmentsDashed(Graphics2D g2) {
198        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
199        boolean main = false, block = false, hidden = false, dashed = true;
200
201        if (ltdo.getSideRailCount() > 0) {
202            // setup for drawing dashed sideline rails
203            int railWidth = ltdo.getSideRailWidth();
204            float[] dashArray = new float[]{6.F + railWidth, 4.F + railWidth};
205            g2.setStroke(new BasicStroke(
206                    railWidth,
207                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
208                    10.F, dashArray, 0));
209            g2.setColor(ltdo.getSideRailColor());
210            if ((ltdo.getSideRailCount() & 1) == 1) {
211                draw1(g2, main, block, hidden, dashed);
212            }
213            if (ltdo.getSideRailCount() >= 2) {
214                float railDisplacement = railWidth + (ltdo.getSideRailGap() / 2.F);
215                draw2(g2, main, railDisplacement, dashed);
216            }
217        }
218
219        if (ltdo.getMainRailCount() > 0) {
220            // setup for drawing dashed mainline rails
221            main = true;
222            int railWidth = ltdo.getMainRailWidth();
223            float[] dashArray = new float[]{6.F + railWidth, 4.F + railWidth};
224            g2.setStroke(new BasicStroke(
225                    railWidth,
226                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
227                    10.F, dashArray, 0));
228            g2.setColor(ltdo.getMainRailColor());
229            if ((ltdo.getMainRailCount() & 1) == 1) {
230                draw1(g2, main, block, hidden, dashed);
231            }
232            if (ltdo.getMainRailCount() >= 2) {
233                float railDisplacement = railWidth + (ltdo.getSideRailGap() / 2.F);
234                draw2(g2, main, railDisplacement, dashed);
235            }
236        }
237    }   // drawTrackSegmentsDashed
238
239    //
240    // draw layout track ballast
241    //
242    private void drawLayoutTracksBallast(Graphics2D g2) {
243        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
244        boolean main = false, block = false, hidden = false, dashed = false;
245
246        // setup for drawing sideline ballast
247        int ballastWidth = ltdo.getSideBallastWidth();
248        if (ballastWidth > 0) {
249            g2.setStroke(new BasicStroke(ballastWidth,
250                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
251            g2.setColor(ltdo.getSideBallastColor());
252            draw1(g2, main, block, hidden, dashed);
253        }
254
255        // setup for drawing mainline ballast
256        ballastWidth = ltdo.getMainBallastWidth();
257        if (ballastWidth > 0) {
258            g2.setStroke(new BasicStroke(ballastWidth,
259                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
260            g2.setColor(ltdo.getMainBallastColor());
261            main = true;
262            draw1(g2, main, block, hidden, dashed);
263        }
264    }
265
266    //
267    // draw layout track ties
268    //
269    private void drawLayoutTracksTies(Graphics2D g2) {
270        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
271
272        // setup for drawing sideline ties
273        int tieLength = ltdo.getSideTieLength();
274        int tieWidth = ltdo.getSideTieWidth();
275        int tieGap = ltdo.getSideTieGap();
276        if ((tieLength > 0) && (tieWidth > 0) && (tieGap > 0)) {
277            g2.setStroke(new BasicStroke(tieLength,
278                    BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.F,
279                    new float[]{tieWidth, tieGap}, 0));
280            g2.setColor(ltdo.getSideTieColor());
281            draw1(g2, false);  // main = false
282        }
283
284        // setup for drawing mainline ties
285        tieLength = ltdo.getMainTieLength();
286        tieWidth = ltdo.getMainTieWidth();
287        tieGap = ltdo.getMainTieGap();
288        if ((tieLength > 0) && (tieWidth > 0) && (tieGap > 0)) {
289            g2.setStroke(new BasicStroke(tieLength,
290                    BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.F,
291                    new float[]{tieWidth, tieGap}, 0));
292            g2.setColor(ltdo.getMainTieColor());
293            draw1(g2, true); // main = true
294        }
295    }
296
297    //
298    // draw layout track rails
299    //
300    private void drawLayoutTracksRails(Graphics2D g2) {
301        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
302        int railWidth = ltdo.getSideRailWidth();
303        Color railColor = ltdo.getSideRailColor();
304
305        boolean main = false, block = false, hidden = false, dashed = false;
306
307        if (ltdo.getSideRailCount() > 1) {
308            // setup for drawing sideline rails
309            float railDisplacement = railWidth + (ltdo.getSideRailGap() / 2.F);
310            g2.setStroke(new BasicStroke(railWidth,
311                    BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
312            g2.setColor(railColor);
313            draw2(g2, main, railDisplacement);
314        }
315
316        if ((ltdo.getSideRailCount() & 1) == 1) {
317            // setup for drawing sideline rails
318            g2.setStroke(new BasicStroke(
319                    railWidth,
320                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
321            g2.setColor(railColor);
322            draw1(g2, main, block, hidden, dashed);
323        }
324
325        main = true;
326
327        railWidth = ltdo.getMainRailWidth();
328        railColor = ltdo.getMainRailColor();
329        if (ltdo.getMainRailCount() > 1) {
330            // setup for drawing mainline rails
331            float railDisplacement = railWidth + (ltdo.getMainRailGap() / 2.F);
332            g2.setStroke(new BasicStroke(railWidth,
333                    BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
334            g2.setColor(railColor);
335            draw2(g2, main, railDisplacement);
336        }
337        if ((ltdo.getMainRailCount() & 1) == 1) {
338            // setup for drawing mainline rails
339            g2.setStroke(new BasicStroke(
340                    railWidth,
341                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
342            g2.setColor(railColor);
343            dashed = false;
344            draw1(g2, main, block, hidden, dashed);
345        }
346    }   // drawLayoutTracksRails
347
348    //
349    // draw layout track block lines
350    //
351    private void drawLayoutTracksBlockLines(Graphics2D g2) {
352        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
353
354        // setup for drawing sideline block lines
355        int blockLineWidth = ltdo.getSideBlockLineWidth();
356        float[] dashArray = new float[]{6.F + blockLineWidth, 4.F + blockLineWidth};
357
358        Stroke blockLineStroke;
359        int dashPercentageX10 = ltdo.getSideBlockLineDashPercentageX10();
360        if (dashPercentageX10 > 0) {
361            float[] blockLineDashArray = new float[]{
362                dashPercentageX10 + blockLineWidth,
363                10.F - dashPercentageX10 + blockLineWidth};
364            blockLineStroke = new BasicStroke(
365                    blockLineWidth,
366                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
367                    10.F, blockLineDashArray, 0);
368            g2.setStroke(blockLineStroke);
369        } else {
370            blockLineStroke = new BasicStroke(
371                    blockLineWidth,
372                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
373            g2.setStroke(new BasicStroke(
374                    blockLineWidth,
375                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
376                    10.F, dashArray, 0));
377        }
378
379        // note: color is set in layout track's draw1 when isBlock is true
380        boolean main = false, block = true, hidden = false, dashed = true;
381        draw1(g2, main, block, hidden, dashed);
382        g2.setStroke(blockLineStroke);
383        draw1(g2, main, block, hidden, dashed = false);
384
385        // setup for drawing mainline block lines
386        blockLineWidth = ltdo.getMainBlockLineWidth();
387        dashArray = new float[]{6.F + blockLineWidth, 4.F + blockLineWidth};
388
389        dashPercentageX10 = ltdo.getMainBlockLineDashPercentageX10();
390        if (dashPercentageX10 > 0) {
391            float[] blockLineDashArray = new float[]{
392                dashPercentageX10 + blockLineWidth,
393                10 - dashPercentageX10 + blockLineWidth};
394            blockLineStroke = new BasicStroke(
395                    blockLineWidth,
396                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
397                    10.F, blockLineDashArray, 0);
398            g2.setStroke(blockLineStroke);
399        } else {
400            blockLineStroke = new BasicStroke(
401                    blockLineWidth,
402                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
403            g2.setStroke(new BasicStroke(
404                    blockLineWidth,
405                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
406                    10.F, dashArray, 0));
407        }
408        // note: color is set in layout track's draw1 when isBlock is true
409        draw1(g2, main = true, block, hidden, dashed = true);
410        g2.setStroke(blockLineStroke);
411        dashed = false;
412        draw1(g2, main, block, hidden, dashed);
413    }
414
415    // isDashed defaults to false
416    private void draw1(Graphics2D g2,
417            boolean isMain,
418            boolean isBlock,
419            boolean isHidden) {
420        draw1(g2, isMain, isBlock, isHidden, false);
421    }
422
423    // isHidden defaults to false
424    private void draw1(Graphics2D g2,
425            boolean isMain,
426            boolean isBlock) {
427        draw1(g2, isMain, isBlock, false);
428    }
429
430    // isBlock defaults to false
431    private void draw1(Graphics2D g2, boolean isMain) {
432        draw1(g2, isMain, false);
433    }
434
435    // draw single line (ballast, ties & block lines)
436    private void draw1(Graphics2D g2,
437            boolean isMain,
438            boolean isBlock,
439            boolean isHidden,
440            boolean isDashed) {
441        for (LayoutTrackView layoutTrackView : layoutEditor.getLayoutTrackViews()) {
442            if (!(layoutTrackView instanceof PositionablePointView)) {
443                if (isHidden == layoutTrackView.isHidden()) {
444                    if ((layoutTrackView instanceof TrackSegmentView)) {
445                        if (((TrackSegmentView) layoutTrackView).isDashed() == isDashed) {
446                            layoutTrackView.draw1(g2, isMain, isBlock);
447                        }
448                    } else if (!isDashed) {
449                        layoutTrackView.draw1(g2, isMain, isBlock);
450                    }
451                }
452            }
453        }
454    }
455
456    // draw positionable points
457    private void drawPositionablePoints(Graphics2D g2, boolean isMain) {
458        for (PositionablePointView positionablePointView : layoutEditor.getPositionablePointViews()) {
459            positionablePointView.draw1(g2, isMain, false);
460        }
461    }
462
463    // isDashed defaults to false
464    private void draw2(Graphics2D g2, boolean isMain, float railDisplacement) {
465        draw2(g2, isMain, railDisplacement, false);
466    }
467
468    // draw parallel lines (rails)
469    private void draw2(Graphics2D g2, boolean isMain,
470            float railDisplacement, boolean isDashed) {
471        for (LayoutTrackView layoutTrackView : layoutEditor.getLayoutTrackViews()) {
472            if ((layoutTrackView instanceof TrackSegmentView)) {
473                if (((TrackSegmentView) layoutTrackView).isDashed() == isDashed) {
474                    layoutTrackView.draw2(g2, isMain, railDisplacement);
475                }
476            } else if (!isDashed) {
477                layoutTrackView.draw2(g2, isMain, railDisplacement);
478            }
479        }
480    }
481
482    // draw decorations
483    private void drawDecorations(Graphics2D g2) {
484        layoutEditor.getLayoutTrackViews().forEach((tr) -> tr.drawDecorations(g2));
485    }
486
487    // draw shapes
488    private void drawShapes(Graphics2D g2, boolean isBackground) {
489        layoutEditor.getLayoutShapes().forEach((s) -> {
490            if (isBackground == (s.getLevel() < 3)) {
491                s.draw(g2);
492            }
493        });
494    }
495
496    // draw track segment (in progress)
497    private void drawTrackSegmentInProgress(Graphics2D g2) {
498        // check for segment in progress
499        if (layoutEditor.isEditable() && (layoutEditor.beginTrack != null) && layoutEditor.getLayoutEditorToolBarPanel().trackButton.isSelected()) {
500            g2.setColor(layoutEditor.defaultTrackColor);
501            g2.setStroke(new BasicStroke(layoutEditor.gContext.getSidelineTrackWidth(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
502            g2.draw(new Line2D.Double(layoutEditor.beginLocation, layoutEditor.currentLocation));
503
504            // highlight unconnected endpoints of all tracks
505            Color highlightColor = ColorUtil.setAlpha(Color.red, 0.25);
506            Color connectColor = ColorUtil.setAlpha(Color.green, 0.5);
507            g2.setColor(highlightColor);
508            g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
509            for (LayoutTrack lt : layoutEditor.getLayoutTracks()) {
510                if (lt != layoutEditor.beginTrack) {
511                    LayoutTrackView ltv = layoutEditor.getLayoutTrackView(lt);
512                    if (lt == layoutEditor.foundTrack) {
513                        ltv.highlightUnconnected(g2);
514                        g2.setColor(connectColor);
515                        ltv.highlightUnconnected(g2, layoutEditor.foundHitPointType);
516                        g2.setColor(highlightColor);
517                    } else {
518                        ltv.highlightUnconnected(g2);
519                    }
520                }
521            }
522        }
523    }
524
525    // draw shape (in progress)
526    private void drawShapeInProgress(Graphics2D g2) {
527        // check for segment in progress
528        if (layoutEditor.getLayoutEditorToolBarPanel().shapeButton.isSelected()) {
529            // log.warn("drawShapeInProgress: selectedObject: " + selectedObject);
530            if ((layoutEditor.selectedObject != null)) {
531                g2.setColor(Color.DARK_GRAY);
532                g2.setStroke(new BasicStroke(3.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
533                g2.draw(new Line2D.Double(layoutEditor.beginLocation, layoutEditor.currentLocation));
534            }
535        }
536    }
537
538    // draw layout track edit controls
539    private void drawLayoutTrackEditControls(Graphics2D g2) {
540        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
541
542        layoutEditor.getLayoutTrackViews().forEach((tr) -> tr.drawEditControls(g2));
543    }
544
545    private void drawShapeEditControls(Graphics2D g2) {
546        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
547        layoutEditor.getLayoutShapes().forEach((s) -> s.drawEditControls(g2));
548    }
549
550    // draw layout turnout controls
551    private void drawTurnoutControls(Graphics2D g2) {
552        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
553        g2.setColor(layoutEditor.turnoutCircleColor);
554        g2.setBackground(layoutEditor.turnoutCircleThrownColor);
555
556        // loop over all turnouts
557        boolean editable = layoutEditor.isEditable();
558        layoutEditor.getLayoutTrackViews().forEach((tr) -> {
559            if (tr instanceof LayoutTurnoutView) {  //<== this includes LayoutSlips
560                LayoutTurnoutView lt = (LayoutTurnoutView) tr;
561                if (editable || !(lt.isHidden() || lt.isDisabled())) {
562                    lt.drawTurnoutControls(g2);
563                }
564            } else if (tr instanceof LayoutTurntableView) {
565                LayoutTurntableView lt = (LayoutTurntableView) tr;
566                if (editable || !lt.isHidden()) {
567                    lt.drawTurnoutControls(g2);
568                }
569            }else if (tr instanceof LayoutTraverserView) {
570                LayoutTraverserView lt = (LayoutTraverserView) tr;
571                if (editable || !lt.isHidden()) {
572                    lt.drawTurnoutControls(g2);
573                }
574            }
575        });
576
577    }
578
579    private void drawSelectionRect(Graphics2D g2) {
580        if (layoutEditor.selectionActive && (layoutEditor.selectionWidth != 0.0) && (layoutEditor.selectionHeight != 0.0)) {
581            // The Editor super class draws a dashed red selection rectangle...
582            // We're going to also draw a non-dashed yellow selection rectangle...
583            // This could be code stripped if the super-class implementation is "good enough"
584            Stroke stroke = g2.getStroke();
585            Color color = g2.getColor();
586
587            g2.setColor(new Color(204, 207, 88));
588            g2.setStroke(new BasicStroke(3.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
589
590            g2.draw(layoutEditor.getSelectionRect());    // this sets _selectRect also
591
592            g2.setColor(color);
593            g2.setStroke(stroke);
594        } else {
595            layoutEditor.setSelectRect(null); // and clear it to turn it off
596        }
597    }
598
599    private void drawMemoryRects(Graphics2D g2) {
600        g2.setColor(layoutEditor.defaultTrackColor);
601        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
602
603        layoutEditor.getMemoryLabelList().forEach((l) -> g2.draw(new Rectangle2D.Double(l.getX(), l.getY(), l.getSize().width, l.getSize().height)));
604    }
605
606    private void drawGlobalVariableRects(Graphics2D g2) {
607        g2.setColor(layoutEditor.defaultTrackColor);
608        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
609
610        layoutEditor.getGlobalVariableLabelList().forEach((l) -> g2.draw(new Rectangle2D.Double(l.getX(), l.getY(), l.getSize().width, l.getSize().height)));
611    }
612
613    private void drawBlockContentsRects(Graphics2D g2) {
614        g2.setColor(layoutEditor.defaultTrackColor);
615        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
616
617        layoutEditor.getBlockContentsLabelList().forEach((l) -> g2.draw(new Rectangle2D.Double(l.getX(), l.getY(), l.getSize().width, l.getSize().height)));
618    }
619
620    private void highLightSelection(Graphics2D g) {
621        Stroke stroke = g.getStroke();
622        Color color = g.getColor();
623        g.setColor(new Color(204, 207, 88));
624        g.setStroke(new BasicStroke(2.0f));
625
626        layoutEditor.getPositionalSelection().forEach((c) -> g.drawRect(c.getX(), c.getY(), c.maxWidth(), c.maxHeight()));
627
628        layoutEditor._layoutTrackSelection.stream().map((lt) -> {
629            LayoutTrackView ltv = layoutEditor.getLayoutTrackView(lt);
630            Rectangle2D r = ltv.getBounds();
631            if (r.isEmpty()) {
632                r = MathUtil.inset(r, -4.0);
633            }
634            //r = MathUtil.centerRectangleOnPoint(r, ltv.getCoordsCenter());
635            return r;
636        }).forEachOrdered(g::draw);
637
638        layoutEditor._layoutShapeSelection.stream().map((ls) -> {
639            Rectangle2D r = ls.getBounds();
640            if (r.isEmpty()) {
641                r = MathUtil.inset(r, -4.0);
642            }
643            //r = MathUtil.centerRectangleOnPoint(r, ls.getCoordsCenter());
644            return r;
645        }).forEachOrdered(g::draw);
646
647        g.setColor(color);
648        g.setStroke(stroke);
649    }
650
651    private void drawLayoutTracksLabel(Graphics2D g) {
652        g.setFont(new Font(Font.MONOSPACED, Font.BOLD, 12));
653        g.setColor(Color.red);
654        for (LayoutTrackView layoutTrackView : layoutEditor.getLayoutTrackViews()) {
655            layoutTrackView.drawLayoutTrackText(g);
656        }
657    }
658
659    /*
660    * {@inheritDoc}
661     */
662    @Override
663    public Rectangle getBounds() {
664        JComponent targetPanel = layoutEditor.getTargetPanel();
665        Rectangle2D targetBounds = targetPanel.getBounds();
666        Container parent = getParent();
667        if (parent != null) {   // if there is a parent...
668            Rectangle2D parentBounds = parent.getBounds();
669
670            // convert our origin to parent coordinates
671            Point2D origin = new Point2D.Double(
672                    targetBounds.getX() - parentBounds.getX(),
673                    targetBounds.getY() - parentBounds.getY());
674
675            return new Rectangle((int) origin.getX(), (int) origin.getY(),
676                    (int) targetBounds.getWidth(), (int) targetBounds.getHeight());
677        } else {
678            return MathUtil.rectangle2DToRectangle(targetBounds);
679        }
680    }
681
682    /*
683    * {@inheritDoc}
684     */
685    @Override
686    public Rectangle getBounds(Rectangle rv) {
687        rv.setBounds(getBounds());
688        return rv;
689    }
690
691    /*
692    * {@inheritDoc}
693     */
694    @Override
695    public int getX() {
696        Rectangle bounds = getBounds();
697        return (int) bounds.getX();
698    }
699
700    /*
701    * {@inheritDoc}
702     */
703    @Override
704    public int getY() {
705        Rectangle bounds = getBounds();
706        return (int) bounds.getY();
707    }
708
709    /*
710    * {@inheritDoc}
711     */
712    @Override
713    public int getWidth() {
714        Rectangle bounds = getBounds();
715        return (int) bounds.getWidth();
716    }
717
718    /*
719    * {@inheritDoc}
720     */
721    @Override
722    public int getHeight() {
723        Rectangle bounds = getBounds();
724        return (int) bounds.getHeight();
725    }
726
727    private Rectangle2D clipBounds = null;
728
729    public void setClip(Rectangle2D clipBounds) {
730        this.clipBounds = clipBounds;
731    }
732
733    // initialize logging
734    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutEditorComponent.class);
735}