Revision 5:b748ad5feff9

View differences:

src/main/java/cz/agents/alite/visualizer2d/Layer.java
9 9

  
10 10
    default public void deinit(VisPane<T> visPane) {}
11 11

  
12
    public void paint(VisCanvas canvas, Point2dAdapter<T> adapter);
12
    public void paint(VisCanvas visCanvas, Point2dAdapter<T> adapter);
13 13

  
14 14
}
src/main/java/cz/agents/alite/visualizer2d/LayerContainer.java
1
package cz.agents.alite.visualizer2d;
2

  
3
import java.util.HashSet;
4
import java.util.List;
5
import java.util.Set;
6
import java.util.concurrent.CopyOnWriteArrayList;
7

  
8
/**
9
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
10
 */
11
public class LayerContainer<T> implements Layer<T> {
12

  
13
    private final Set<VisPane<T>> initializedPanes = new HashSet<>();
14
    private final List<Layer<T>> layers = new CopyOnWriteArrayList<>();
15

  
16
    @Override
17
    public synchronized void init(VisPane<T> visPane) {
18
        if (initializedPanes.contains(visPane)) {
19
            initializedPanes.add(visPane);
20
            layers.forEach(layer -> layer.init(visPane));
21
        }
22
    }
23

  
24
    @Override
25
    public synchronized void deinit(VisPane<T> visPane) {
26
        if (initializedPanes.contains(visPane)) {
27
            initializedPanes.remove(visPane);
28
            layers.forEach(layer -> layer.deinit(visPane));
29
        }
30

  
31
    }
32

  
33
    @Override
34
    public final void paint(VisCanvas visCanvas, Point2dAdapter<T> adapter) {
35
        for (Layer<T> layer : layers) {
36
            layer.paint(visCanvas, adapter);
37
        }
38
    }
39

  
40
    public synchronized void addLayer(Layer<T> layer) {
41
        if (!layers.contains(layer)) {
42
            initializedPanes.forEach(layer::init);
43
            layers.add(layer);
44
        }
45
    }
46

  
47
    public synchronized boolean removeLayer(Layer<T> layer) {
48
        if (!layers.contains(layer)) {
49
            layers.remove(layer);
50
            //TODO ensure thread safety
51
            initializedPanes.forEach(layer::deinit);
52
            return true;
53
        }
54
        return false;
55
    }
56

  
57
}
src/main/java/cz/agents/alite/visualizer2d/VisCanvas.java
2 2

  
3 3
import java.awt.*;
4 4
import java.awt.geom.AffineTransform;
5
import java.awt.geom.NoninvertibleTransformException;
6
import java.awt.geom.Point2D;
5 7

  
6 8
/**
7 9
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
8 10
 */
9 11
public final class VisCanvas {
10 12

  
11
    private boolean originalTransformSet = true;
12
    private final AffineTransform worldAffineTransform;
13
    private final AffineTransform originalTransform;
13
    private boolean screenTransformSet = true;
14
    private final AffineTransform worldTransform;
15
    private final AffineTransform screenTransform;
14 16
    private final Graphics2D canvas;
15 17
    private final Rectangle bounds;
16 18

  
17
    public VisCanvas(AffineTransform worldAffineTransform, Graphics2D canvas, Rectangle bounds) {
19
    public VisCanvas(AffineTransform worldTransform, Graphics2D canvas, Rectangle bounds) {
18 20
        this.bounds = bounds;
19 21
        this.canvas = canvas;
20 22

  
21
        this.originalTransform = canvas.getTransform();
22
        this.worldAffineTransform = new AffineTransform(originalTransform);
23
        this.worldAffineTransform.concatenate(worldAffineTransform);
23
        this.screenTransform = canvas.getTransform();
24
        this.worldTransform = new AffineTransform(screenTransform);
25
        this.worldTransform.concatenate(worldTransform);
24 26
    }
25 27

  
26 28
    public final Graphics2D getWorldCanvas() {
27
        if (originalTransformSet) {
28
            canvas.setTransform(worldAffineTransform);
29
            originalTransformSet = false;
29
        if (screenTransformSet) {
30
            canvas.setTransform(worldTransform);
31
            screenTransformSet = false;
30 32
        }
31 33
        return canvas;
32 34
    }
33 35

  
34
    public final Graphics2D getOriginalCanvas() {
35
        if (!originalTransformSet) {
36
            canvas.setTransform(originalTransform);
37
            originalTransformSet = true;
36
    public final Graphics2D getScreenCanvas() {
37
        if (!screenTransformSet) {
38
            canvas.setTransform(screenTransform);
39
            screenTransformSet = true;
38 40
        }
39 41
        return canvas;
40 42
    }
41 43

  
42
    public Rectangle getBounds() {
44
    public final Rectangle getBounds() {
43 45
        return bounds;
44 46
    }
47

  
48
    public final Point2D toWorldCoordinates(Point2D screenCoordinates) {
49
        try {
50
            return worldTransform.inverseTransform(screenCoordinates, null);
51
        } catch (NoninvertibleTransformException e) {
52
            e.printStackTrace();
53
        }
54
        return null;
55
    }
45 56
}
src/main/java/cz/agents/alite/visualizer2d/VisPane.java
4 4
import java.awt.*;
5 5
import java.awt.event.ComponentAdapter;
6 6
import java.awt.event.ComponentEvent;
7
import java.awt.event.MouseListener;
8
import java.awt.event.MouseMotionListener;
7 9
import java.awt.geom.AffineTransform;
8 10
import java.awt.geom.NoninvertibleTransformException;
9 11
import java.awt.geom.Point2D;
......
21 23
    public static final int REFRESH_IN_MS = 40; //25 FPS
22 24
    private static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(1);
23 25

  
24
    private LayerContainer<T> layerContainer;
26
    private Layer<T> layer;
25 27
    //TODO solve volatile vs synchronized
26 28
    private volatile Point2dAdapter<T> adapter;
27 29
    private volatile Optional<VisCanvas> visCanvas = Optional.empty();
......
36 38
    private final Canvas canvas = new Canvas();
37 39
    private final JPanel panel = new JPanel();
38 40

  
39
    public VisPane(LayerContainer<T> layerContainer, Point2dAdapter<T> adapter) {
40
        this.layerContainer = layerContainer;
41
    public VisPane(Layer<T> layer, Point2dAdapter<T> adapter) {
42
        this.layer = layer;
41 43
        this.adapter = adapter;
42 44

  
43 45
        VisPaneMouseListener mouseListener = new VisPaneMouseListener(this);
......
61 63
    public synchronized void start() {
62 64
        if (!running) {
63 65
            running = true;
64
            layerContainer.init(this);
66
            layer.init(this);
65 67
            updateFuture = EXECUTOR.scheduleAtFixedRate(this::update, 0, REFRESH_IN_MS, TimeUnit.MILLISECONDS);
66 68
        }
67 69
    }
......
72 74
            reinitializeBuffers = true;
73 75
            updateFuture.cancel(true);
74 76
            updateFuture = null;
75
            layerContainer.deinit(this);
77
            layer.deinit(this);
76 78
        }
77 79
    }
78 80

  
......
88 90
        this.adapter = adapter;
89 91
    }
90 92

  
91
    public synchronized LayerContainer<T> getLayerContainer() {
92
        return layerContainer;
93
    public synchronized Layer<T> getLayer() {
94
        return layer;
93 95
    }
94 96

  
95
    public synchronized void setLayerContainer(LayerContainer<T> layerContainer) {
97
    public synchronized void setLayer(Layer<T> layer) {
96 98
        boolean wasRunning = running;
97 99
        stop();
98
        this.layerContainer = layerContainer;
100
        this.layer = layer;
99 101
        if (wasRunning) {
100 102
            start();
101 103
        }
......
159 161
            AffineTransform affineTransform = calculateAffineTransform();
160 162
            visCanvas = Optional.of(new VisCanvas(affineTransform, graphics, panel.getBounds()));
161 163

  
162
            layerContainer.paint(visCanvas.get(), adapter);
164
            layer.paint(visCanvas.get(), adapter);
163 165

  
164 166
            graphics.setTransform(originalTransform);
165 167
            strategy.show();
......
188 190
            return new AffineTransform(transform);
189 191
        }
190 192
    }
193

  
194
    public void addMouseListener(MouseListener mouseListener) {
195
        canvas.addMouseListener(mouseListener);
196
    }
197

  
198
    public void addMouseMotionListener(MouseMotionListener mouseMotionListener) {
199
        canvas.addMouseMotionListener(mouseMotionListener);
200
    }
201

  
202
    public void removeMouseMotionListener(MouseMotionListener mouseMotionListener) {
203
        canvas.removeMouseMotionListener(mouseMotionListener);
204
    }
205

  
206
    public void removeMouseListener(MouseListener mouseListener) {
207
        canvas.removeMouseListener(mouseListener);
208
    }
191 209
}
src/main/java/cz/agents/alite/visualizer2d/demo/Demo.java
1 1
package cz.agents.alite.visualizer2d.demo;
2 2

  
3
import cz.agents.alite.visualizer2d.LayerContainer;
4 3
import cz.agents.alite.visualizer2d.Point2dAdapter;
5 4
import cz.agents.alite.visualizer2d.VisPane;
6 5
import cz.agents.alite.visualizer2d.layers.ColorLayer;
6
import cz.agents.alite.visualizer2d.layers.DraggableLayer;
7
import cz.agents.alite.visualizer2d.layers.GroupLayer;
7 8
import cz.agents.alite.visualizer2d.layers.elements.ElementsLayer;
8 9
import cz.agents.alite.visualizer2d.layers.elements.PointElement;
10
import cz.agents.alite.visualizer2d.layers.textBackgroundLayer.ColoredTextElementImpl;
11
import cz.agents.alite.visualizer2d.layers.textBackgroundLayer.TextBackgroundLayer;
12
import cz.agents.alite.visualizer2d.layers.textBackgroundLayer.TextBackgroundLayerBuilder;
9 13

  
10 14
import javax.swing.*;
11 15
import javax.vecmath.Point2d;
......
21 25
    public static void main(String[] args) throws InterruptedException {
22 26
        final AngleProvider angleProvider = new AngleProvider();
23 27

  
24
        LayerContainer<Point2d> layers = new LayerContainer<>();
28
        GroupLayer<Point2d> layers = GroupLayer.create();
25 29
        layers.addLayer(ColorLayer.create(Color.BLACK));
26 30
        layers.addLayer(ElementsLayer.create(() -> Arrays.asList(
27 31
                new PointElement<>(new Point2d(AngleProvider.TRANS_X, AngleProvider.TRANS_Y), 3, Color.RED),
28 32
                new PointElement<>(angleProvider.getPoint(), 3, Color.WHITE),
29 33
                new PointElement<>(new Point2d(AngleProvider.TRANS_X + 25, AngleProvider.TRANS_Y), 3, Color.GREEN))
30 34
        ));
35
        TextBackgroundLayer textBackgroundLayer = TextBackgroundLayerBuilder.createTopLeft(15, 15,
36
                () -> Arrays.asList(new ColoredTextElementImpl(String.format("%f\n, %f", angleProvider.getPoint().x,
37
                        angleProvider.getPoint().y), Color.WHITE))
38
        ).setBackgroundColor(Color.RED).build();
39
        GroupLayer draggable = DraggableLayer.create(textBackgroundLayer);
40
        layers.addLayer(draggable);
31 41
        VisPane<Point2d> visPane = new VisPane<>(layers, Point2dAdapter.IDENTITY);
32 42
        JFrame frame = new JFrame("Visualizer2d Demo");
33 43
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
......
37 47

  
38 48
        visPane.start();
39 49
        frame.setVisible(true);
40

  
41
        Thread.sleep(5000);
42
        //frame.pack();
43 50
    }
44 51

  
45 52
}
src/main/java/cz/agents/alite/visualizer2d/layers/ColorLayer.java
22 22
    }
23 23

  
24 24
    @Override
25
    public void paint(VisCanvas canvas, Point2dAdapter<T> adapter) {
26
        Graphics2D graphics = canvas.getOriginalCanvas();
25
    public void paint(VisCanvas visCanvas, Point2dAdapter<T> adapter) {
26
        Graphics2D graphics = visCanvas.getScreenCanvas();
27 27
        graphics.setColor(color);
28
        Rectangle rectangle = canvas.getBounds();
28
        Rectangle rectangle = visCanvas.getBounds();
29 29
        graphics.fill(rectangle);
30 30
    }
31 31
}
src/main/java/cz/agents/alite/visualizer2d/layers/DraggableLayer.java
1
package cz.agents.alite.visualizer2d.layers;
2

  
3
import cz.agents.alite.visualizer2d.Layer;
4
import cz.agents.alite.visualizer2d.Point2dAdapter;
5
import cz.agents.alite.visualizer2d.VisCanvas;
6
import cz.agents.alite.visualizer2d.VisPane;
7

  
8
import java.awt.event.MouseEvent;
9
import java.awt.event.MouseListener;
10
import java.awt.event.MouseMotionListener;
11

  
12
/**
13
 * DraggableLayer allows to move another layer by mouse over screen. The another layer must implement {@link
14
 * Draggable} interface.
15
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
16
 *
17
 * @see Draggable
18
 * @see cz.agents.alite.visualizer2d.layers.textBackgroundLayer.TextBackgroundLayer
19
 */
20
public class DraggableLayer<T> implements Layer<T>, MouseListener, MouseMotionListener {
21

  
22
    private final int mouseButton;
23
    private final Draggable draggable;
24

  
25
    private int lastX, lastY;
26
    private boolean dragging = false;
27

  
28
    /**
29
     * Binds DraggableLayer with given {@code draggable}. Returned layer containts both of these layers.
30
     * @param mouseButton mouse button on which the layer should be dragged
31
     * @param draggable layer to bind
32
     * @return composition of the given {@code draggable} and newly created instance of {@link DraggableLayer}
33
     *
34
     * @see java.awt.event.MouseEvent
35
     */
36
    public static <T> GroupLayer<T> create(int mouseButton, Draggable<T> draggable) {
37
        GroupLayer<T> out = GroupLayer.create();
38

  
39
        out.addLayer(draggable);
40
        out.addLayer(new DraggableLayer<>(mouseButton, draggable));
41

  
42
        return out;
43
    }
44

  
45
    /**
46
     * /**
47
     * Binds DraggableLayer with given {@code draggable}. Returned layer containts both of these layers. The layer
48
     * reacts on left mouse button.
49
     * @param draggable layer to bind
50
     * @return composition of the given {@code draggable} and newly created instance of {@link DraggableLayer}
51
     *
52
     * @see java.awt.event.MouseEvent
53
     */
54
    public static <T> GroupLayer<T> create(Draggable<T> draggable) {
55
        return create(MouseEvent.BUTTON1, draggable);
56
    }
57

  
58
    private DraggableLayer(int mouseButton, Draggable draggable) {
59
        this.mouseButton = mouseButton;
60
        this.draggable = draggable;
61
    }
62

  
63
    @Override
64
    public void init(VisPane visPane) {
65
        visPane.addMouseListener(this);
66
        visPane.addMouseMotionListener(this);
67
        System.out.println("Mouse listener added");
68
    }
69

  
70
    @Override
71
    public void deinit(VisPane visPane) {
72
        visPane.removeMouseMotionListener(this);
73
        visPane.removeMouseListener(this);
74
    }
75

  
76
    @Override
77
    public void paint(VisCanvas visCanvas, Point2dAdapter<T> adapter) {}
78

  
79
    @Override
80
    public void mouseDragged(MouseEvent e) {
81
        if (dragging && draggable.isActive()) {
82
            int deltaX = e.getX() - lastX;
83
            int deltaY = e.getY() - lastY;
84

  
85
            lastX = e.getX();
86
            lastY = e.getY();
87

  
88
            draggable.moveLocation(deltaX, deltaY);
89
        }
90
    }
91

  
92
    @Override
93
    public void mouseMoved(MouseEvent e) {}
94

  
95
    @Override
96
    public void mouseClicked(MouseEvent e) {}
97

  
98
    @Override
99
    public void mousePressed(MouseEvent e) {
100
        if (e.getButton() == mouseButton && draggable.isActive() && e.getClickCount() == 1 &&
101
                draggable.isInArea(e.getPoint().x, e.getPoint().y)) {
102
            dragging = true;
103
            lastX = e.getX();
104
            lastY = e.getY();
105
        }
106
    }
107

  
108
    @Override
109
    public void mouseReleased(MouseEvent e) {
110
        dragging = false;
111
    }
112

  
113
    @Override
114
    public void mouseEntered(MouseEvent e) {}
115

  
116
    @Override
117
    public void mouseExited(MouseEvent e) {}
118

  
119
    /**
120
     * This interface allows for layer to be moved on screen by drag and drop.
121
     */
122
    public interface Draggable<T> extends Layer<T> {
123

  
124
        /**
125
         * Method decides if the layer is active and if the dragging is allowed. This method determines if
126
         * {@link DraggableLayer} will react on mouse click by dragging the layer or not.
127
         * @return {@code true} if the layer is active and allows dragging, {@code false} otherwise.
128
         */
129
        public boolean isActive();
130

  
131
        /**
132
         * Tests whether given coordinates are in area of interest given by implementation. This method determines if
133
         * {@link DraggableLayer} will react on mouse click by dragging the layer or not.
134
         * @param x x-coordinate of mouse click
135
         * @param y y-coordinate of mouse click
136
         * @return {@code true} if the layer is allowed to drag from given coordinates, {@code false} otherwise.
137
         */
138
        public boolean isInArea(int x, int y);
139

  
140
        /**
141
         * This method is called during dragging. The parameters represent amount of change in which the layer should
142
         * be moved
143
         * @param deltaX delta in x-coordinates
144
         * @param deltaY delta in y-coordinates
145
         */
146
        public void moveLocation(int deltaX, int deltaY);
147
    }
148
}
src/main/java/cz/agents/alite/visualizer2d/layers/GroupLayer.java
1
package cz.agents.alite.visualizer2d.layers;
2

  
3
import cz.agents.alite.visualizer2d.Layer;
4
import cz.agents.alite.visualizer2d.Point2dAdapter;
5
import cz.agents.alite.visualizer2d.VisCanvas;
6
import cz.agents.alite.visualizer2d.VisPane;
7

  
8
import java.util.HashSet;
9
import java.util.List;
10
import java.util.Set;
11
import java.util.concurrent.CopyOnWriteArrayList;
12

  
13
/**
14
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
15
 */
16
public class GroupLayer<T> implements Layer<T> {
17

  
18
    public static <T> GroupLayer<T> create() {
19
        return new GroupLayer<>();
20
    }
21

  
22
    private GroupLayer() {}
23

  
24
    private final Set<VisPane<T>> initializedPanes = new HashSet<>();
25
    private final List<Layer<T>> layers = new CopyOnWriteArrayList<>();
26

  
27
    @Override
28
    public synchronized void init(VisPane<T> visPane) {
29
        if (!initializedPanes.contains(visPane)) {
30
            initializedPanes.add(visPane);
31
            layers.forEach(layer -> layer.init(visPane));
32
        }
33
    }
34

  
35
    @Override
36
    public synchronized void deinit(VisPane<T> visPane) {
37
        if (initializedPanes.contains(visPane)) {
38
            initializedPanes.remove(visPane);
39
            layers.forEach(layer -> layer.deinit(visPane));
40
        }
41

  
42
    }
43

  
44
    @Override
45
    public final void paint(VisCanvas visCanvas, Point2dAdapter<T> adapter) {
46
        for (Layer<T> layer : layers) {
47
            layer.paint(visCanvas, adapter);
48
        }
49
    }
50

  
51
    public synchronized void addLayer(Layer<T> layer) {
52
        if (!layers.contains(layer)) {
53
            initializedPanes.forEach(layer::init);
54
            layers.add(layer);
55
        }
56
    }
57

  
58
    public synchronized boolean removeLayer(Layer<T> layer) {
59
        if (!layers.contains(layer)) {
60
            layers.remove(layer);
61
            //TODO ensure thread safety
62
            initializedPanes.forEach(layer::deinit);
63
            return true;
64
        }
65
        return false;
66
    }
67

  
68
}
src/main/java/cz/agents/alite/visualizer2d/layers/HudLayer.java
1
package cz.agents.alite.visualizer2d.layers;
2

  
3
import cz.agents.alite.visualizer2d.Layer;
4
import cz.agents.alite.visualizer2d.Point2dAdapter;
5
import cz.agents.alite.visualizer2d.VisCanvas;
6
import cz.agents.alite.visualizer2d.layers.elements.Element;
7

  
8
import java.awt.*;
9
import java.util.function.Supplier;
10

  
11
/**
12
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
13
 */
14
public class HudLayer implements Layer {
15

  
16
    private final Supplier<Iterable<Element>> elementsSupplier;
17

  
18
    public HudLayer(Supplier<Iterable<Element>> elementsSupplier) {
19
        this.elementsSupplier = elementsSupplier;
20
    }
21

  
22
    @Override
23
    public void paint(VisCanvas visCanvas, Point2dAdapter adapter) {
24
        Graphics2D screen = visCanvas.getScreenCanvas();
25
        elementsSupplier.get().forEach(element -> element.paint(visCanvas, screen, Point2dAdapter.IDENTITY));
26
    }
27
}
src/main/java/cz/agents/alite/visualizer2d/layers/VisInfoLayer.java
1
package cz.agents.alite.visualizer2d.layers;
2

  
3
import cz.agents.alite.visualizer2d.Layer;
4
import cz.agents.alite.visualizer2d.Point2dAdapter;
5
import cz.agents.alite.visualizer2d.VisCanvas;
6
import cz.agents.alite.visualizer2d.VisPane;
7

  
8
/**
9
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
10
 */
11
public class VisInfoLayer implements Layer {
12

  
13

  
14

  
15

  
16
    @Override
17
    public void init(VisPane visPane) {
18

  
19
    }
20

  
21
    @Override
22
    public void deinit(VisPane visPane) {
23

  
24
    }
25

  
26
    @Override
27
    public void paint(VisCanvas visCanvas, Point2dAdapter adapter) {
28

  
29
    }
30
}
src/main/java/cz/agents/alite/visualizer2d/layers/elements/Element.java
1 1
package cz.agents.alite.visualizer2d.layers.elements;
2 2

  
3 3
import cz.agents.alite.visualizer2d.Point2dAdapter;
4
import cz.agents.alite.visualizer2d.VisCanvas;
4 5

  
5 6
import java.awt.*;
6 7

  
......
9 10
 */
10 11
public interface Element<T> {
11 12

  
12
    public void paint(Graphics2D canvas, Point2dAdapter<T> adapter);
13
    public void paint(VisCanvas visCanvas, Graphics2D canvas, Point2dAdapter<T> adapter);
13 14

  
14 15
}
src/main/java/cz/agents/alite/visualizer2d/layers/elements/ElementsLayer.java
7 7
import java.awt.*;
8 8
import java.util.Arrays;
9 9
import java.util.List;
10
import java.util.function.Supplier;
10 11

  
11 12
/**
12 13
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
......
19 20
        return create(() -> pointList);
20 21
    }
21 22

  
22
    public static <T> Layer<T> create(ElementsProvider<T> provider) {
23
        return new ElementsLayer<>(provider);
23
    public static <T> Layer<T> create(Supplier<Iterable<Element<T>>> elementsSupplier) {
24
        return new ElementsLayer<>(elementsSupplier);
24 25
    }
25 26

  
26
    private final ElementsProvider<T> provider;
27
    private final Supplier<Iterable<Element<T>>> elementsSupplier;
27 28

  
28
    private ElementsLayer(ElementsProvider<T> provider) {
29
        this.provider = provider;
29
    private ElementsLayer(Supplier<Iterable<Element<T>>> elementsSupplier) {
30
        this.elementsSupplier = elementsSupplier;
30 31
    }
31 32

  
32 33
    //TODO check if forEach is as fast as for each cycle
33 34
    @Override
34
    public void paint(VisCanvas canvas, Point2dAdapter<T> adapter) {
35
        final Graphics2D worldCanvas = canvas.getWorldCanvas();
36
        provider.getElements().forEach(element -> element.paint(worldCanvas, adapter));
35
    public void paint(VisCanvas visCanvas, Point2dAdapter<T> adapter) {
36
        final Graphics2D worldCanvas = visCanvas.getWorldCanvas();
37
        elementsSupplier.get().forEach(element -> element.paint(visCanvas, worldCanvas, adapter));
37 38
    }
38 39
}
src/main/java/cz/agents/alite/visualizer2d/layers/elements/ElementsProvider.java
1
package cz.agents.alite.visualizer2d.layers.elements;
2

  
3
/**
4
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
5
 */
6
public interface ElementsProvider<T> {
7

  
8
    public Iterable<? extends Element<T>> getElements();
9

  
10
}
src/main/java/cz/agents/alite/visualizer2d/layers/elements/PointElement.java
1 1
package cz.agents.alite.visualizer2d.layers.elements;
2 2

  
3 3
import cz.agents.alite.visualizer2d.Point2dAdapter;
4
import cz.agents.alite.visualizer2d.VisCanvas;
4 5

  
5 6
import javax.vecmath.Point2d;
6 7
import java.awt.*;
......
22 23
    }
23 24

  
24 25
    @Override
25
    public void paint(Graphics2D canvas, Point2dAdapter<T> adapter) {
26
    public void paint(VisCanvas visCanvas, Graphics2D canvas, Point2dAdapter<T> adapter) {
26 27
        Point2d center = adapter.convert(centerPosition);
27 28
        canvas.setColor(color);
28 29

  
src/main/java/cz/agents/alite/visualizer2d/layers/textBackgroundLayer/BottomOffsetPositionFunction.java
1
package cz.agents.alite.visualizer2d.layers.textBackgroundLayer;
2

  
3
import java.awt.*;
4

  
5
/**
6
 * This implementation will keep drawn rectangle by given distance from bottom border of canvas. The distance is
7
 * between bottom edge of drawn rectangle and bottom border of canvas.
8
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
9
 */
10
public class BottomOffsetPositionFunction implements PositionFunction {
11

  
12
    private int x;
13
    private int bottomOffset;
14

  
15
    /**
16
     * @param x x-coordinate of the left edge of the drawn rectange
17
     * @param bottomOffset distance between bottom edge of the drawn rectangle and bottom border of canvas
18
     */
19
    public BottomOffsetPositionFunction(int x, int bottomOffset) {
20
        this.x = x;
21
        this.bottomOffset = bottomOffset;
22
    }
23

  
24
    @Override
25
    public Point getTopLeftPoint(int rectangeWidth, int rectangleHeight, Dimension drawingDimension) {
26
        return new Point(x, (int) (drawingDimension.getHeight() - bottomOffset - rectangleHeight));
27
    }
28

  
29
    @Override
30
    public void moveLocation(int deltaX, int deltaY) {
31
        x += deltaX;
32
        bottomOffset -= deltaY;
33
    }
34

  
35
}
src/main/java/cz/agents/alite/visualizer2d/layers/textBackgroundLayer/ColoredTextElement.java
1
package cz.agents.alite.visualizer2d.layers.textBackgroundLayer;
2

  
3
import java.awt.*;
4

  
5
/**
6
 * Element representing line with given color.
7
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
8
 *
9
 * @see TextBackgroundLayer
10
 */
11
public interface ColoredTextElement {
12

  
13
    /**
14
     * @return color of the text
15
     */
16
    public Color getColor();
17

  
18
    /**
19
     * Gets text split to individual lines. Each element in the array represents one line.
20
     * @return text split to individual lines
21
     */
22
    public String[] getTextByLines();
23

  
24
}
src/main/java/cz/agents/alite/visualizer2d/layers/textBackgroundLayer/ColoredTextElementImpl.java
1
package cz.agents.alite.visualizer2d.layers.textBackgroundLayer;
2

  
3
import java.awt.*;
4

  
5
/**
6
 * Default implementation of {@link ColoredTextElement}
7
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
8
 */
9
public class ColoredTextElementImpl implements ColoredTextElement {
10

  
11
    private final Color color;
12
    private final String[] textLines;
13

  
14
    /**
15
     * @param textLine text, can be multiline
16
     * @param color color of the line
17
     */
18
    public ColoredTextElementImpl(String textLine, Color color) {
19
        this.textLines = textLine.split("\\n");
20
        this.color = color;
21
    }
22

  
23
    @Override
24
    public Color getColor() {
25
        return color;
26
    }
27

  
28
    @Override
29
    public String[] getTextByLines() {
30
        return textLines;
31
    }
32
}
src/main/java/cz/agents/alite/visualizer2d/layers/textBackgroundLayer/Padding.java
1
package cz.agents.alite.visualizer2d.layers.textBackgroundLayer;
2

  
3
public class Padding {
4
    private final int leftPadding;
5
    private final int rightPadding;
6
    private final int bottomPadding;
7
    private final int topPadding;
8

  
9
    public Padding(int leftPadding, int rightPadding, int bottomPadding, int topPadding) {
10
        this.leftPadding = leftPadding;
11
        this.rightPadding = rightPadding;
12
        this.bottomPadding = bottomPadding;
13
        this.topPadding = topPadding;
14
    }
15

  
16
    public int getLeft() {
17
        return leftPadding;
18
    }
19

  
20
    public int getRight() {
21
        return rightPadding;
22
    }
23

  
24
    public int getBottom() {
25
        return bottomPadding;
26
    }
27

  
28
    public int getTop() {
29
        return topPadding;
30
    }
31
}
src/main/java/cz/agents/alite/visualizer2d/layers/textBackgroundLayer/PositionFunction.java
1
package cz.agents.alite.visualizer2d.layers.textBackgroundLayer;
2

  
3
import java.awt.*;
4

  
5
/**
6
 * Implementation of this interface is responsible for getting position of the top left corner of
7
 * {@link TextBackgroundLayer} rectangle. Various implementations allows anchoring the rectangle to different points
8
 * in drawing area.
9
 *
10
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
11
 * @see TextBackgroundLayer
12
 */
13
public interface PositionFunction {
14

  
15
    /**
16
     * Returns position of the top left corner of {@link TextBackgroundLayer} rectangle
17
     *
18
     * @param rectangeWidth    width of the rectangle
19
     * @param rectangleHeight  height of the rectangle
20
     * @param drawingDimension dimension of the area in which drawing occures
21
     * @return position of the top left corner
22
     */
23
    public Point getTopLeftPoint(int rectangeWidth, int rectangleHeight, Dimension drawingDimension);
24

  
25
    /**
26
     * moveLocation method should change internal field of implementation that way,
27
     * that the drawn rectangle will be drawn on location that differs by {@code deltaX} and {@code deltaY} in
28
     * horizontal and vertical direction respectively
29
     *
30
     * @param deltaX delta in x-coordinates
31
     * @param deltaY delta in y-coordinates
32
     */
33
    public void moveLocation(int deltaX, int deltaY);
34

  
35
}
src/main/java/cz/agents/alite/visualizer2d/layers/textBackgroundLayer/RightBottomOffsetPositionFunction.java
1
package cz.agents.alite.visualizer2d.layers.textBackgroundLayer;
2

  
3
import java.awt.*;
4

  
5
/**
6
 * This implementation will keep drawn rectangle by given distance from bottom and righ border of canvas. The
7
 * distance  is between right edge of drawn rectangle and right border of canvas and between bottom edge of drawn
8
 * rectangle and bottom border of canvas.
9
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
10
 */
11
public class RightBottomOffsetPositionFunction implements PositionFunction {
12

  
13
    private int rightOffset;
14
    private int bottomOffset;
15

  
16
    /**
17
     * @param rightOffset distance between right edge of the drawn rectangle and right border of canvas
18
     * @param bottomOffset distance between bottom edge of the drawn rectangle and bottom border of canvas
19
     */
20
    public RightBottomOffsetPositionFunction(int rightOffset, int bottomOffset) {
21
        this.rightOffset = rightOffset;
22
        this.bottomOffset = bottomOffset;
23
    }
24

  
25
    @Override
26
    public Point getTopLeftPoint(int rectangeWidth, int rectangleHeight, Dimension drawingDimension) {
27
        return new Point((int) (drawingDimension.getWidth() - rightOffset - rectangeWidth),
28
                (int) (drawingDimension.getHeight() - bottomOffset - rectangleHeight));
29
    }
30

  
31
    @Override
32
    public void moveLocation(int deltaX, int deltaY) {
33
        rightOffset -= deltaX;
34
        bottomOffset -= deltaY;
35
    }
36
}
src/main/java/cz/agents/alite/visualizer2d/layers/textBackgroundLayer/RightOffsetPositionFunction.java
1
package cz.agents.alite.visualizer2d.layers.textBackgroundLayer;
2

  
3
import java.awt.*;
4

  
5
/**
6
 * This implementation will keep drawn rectangle by given distance from right border of canvas. The distance is
7
 * between right border of drawn rectangle and right border of canvas.
8
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
9
 */
10
public class RightOffsetPositionFunction implements PositionFunction {
11

  
12
    private int rightOffset;
13
    private int y;
14

  
15
    /**
16
     * @param rightOffset distance between right edge of the drawn rectangle and right border of canvas
17
     * @param y y-coordinate of the top edge of the drawn rectangle
18
     */
19
    public RightOffsetPositionFunction(int rightOffset, int y) {
20
        this.rightOffset = rightOffset;
21
        this.y = y;
22
    }
23

  
24
    @Override
25
    public Point getTopLeftPoint(int rectangeWidth, int rectangleHeight, Dimension drawingDimension) {
26
        return new Point((int) (drawingDimension.getWidth() - rightOffset - rectangeWidth), y);
27
    }
28

  
29
    @Override
30
    public void moveLocation(int deltaX, int deltaY) {
31
        rightOffset -= deltaX;
32
        y += deltaY;
33
    }
34
}
src/main/java/cz/agents/alite/visualizer2d/layers/textBackgroundLayer/TextBackgroundLayer.java
1
package cz.agents.alite.visualizer2d.layers.textBackgroundLayer;
2

  
3
import cz.agents.alite.visualizer2d.Layer;
4
import cz.agents.alite.visualizer2d.Point2dAdapter;
5
import cz.agents.alite.visualizer2d.VisCanvas;
6
import cz.agents.alite.visualizer2d.layers.DraggableLayer;
7

  
8
import java.awt.*;
9
import java.util.ArrayList;
10
import java.util.List;
11
import java.util.function.Supplier;
12

  
13
/**
14
 * <p>This layer provides suppor for drawing text inside colored box. The box size is streched in order to contain
15
 * whole text dynamically. The text is represented by {@link } class and therefore the text
16
 * can have different color for each line. </p>
17
 *
18
 * <p>The box can be parametrized by color and padding. The position of the box can is specified by implementation
19
 * of {@link PositionFunction}, which allows custom positiong. The TextBackgroundLayer implements {@link
20
 * cz.agents.alite.visualizer2d.layers.DraggableLayer.Draggable} interface.</p>
21
 *
22
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
23
 * @see TextBackgroundLayerBuilder
24
 * @see PositionFunction
25
 * @see cz.agents.alite.visualizer2d.layers.DraggableLayer.Draggable
26
 */
27
public class TextBackgroundLayer implements Layer, DraggableLayer.Draggable {
28

  
29
    private final Padding padding;
30
    private final Supplier<? extends Iterable<ColoredTextElement>> supplier;
31
    private final PositionFunction positionFunction;
32
    private final Color backgroundColor;
33

  
34
    private Rectangle rectangle;
35
    private boolean active = false;
36

  
37
    /**
38
     * Default implementation of factory method. It is recommended to use {@link TextBackgroundLayerBuilder} instead.
39
     * @param supplier elements that will be displayed
40
     * @param positionFunction position function determing position of the box
41
     * @param backgroundColor background color of the box
42
     * @param padding padding parameters
43
     * @return an instance of the class
44
     */
45
    public static TextBackgroundLayer create(Supplier<? extends Iterable<ColoredTextElement>> supplier,
46
                                             PositionFunction positionFunction, Color backgroundColor,
47
                                             Padding padding) {
48
        return new TextBackgroundLayer(supplier, positionFunction, padding, backgroundColor);
49
    }
50

  
51
    private TextBackgroundLayer(Supplier<? extends Iterable<ColoredTextElement>> supplier, PositionFunction positionFunction, Padding padding,
52
                                Color backgroundColor) {
53
        this.padding = padding;
54
        this.supplier = supplier;
55
        this.positionFunction = positionFunction;
56
        this.backgroundColor = backgroundColor;
57
    }
58

  
59
    @Override
60
    public void paint(VisCanvas visCanvas, Point2dAdapter adapter) {
61

  
62
        Graphics2D canvas = visCanvas.getScreenCanvas();
63
        Iterable<ColoredTextElement> elements = supplier.get();
64

  
65
        FontMetrics fontMetrics = canvas.getFontMetrics();
66
        List<ColoredTextElement> textLines = new ArrayList<>();
67
        int maxLineSize = 0;
68
        int totalLines = 0;
69

  
70
        active = false;
71
        for (ColoredTextElement element : elements) {
72
            active = true;
73

  
74
            textLines.add(element);
75

  
76
            String[] textByLines = element.getTextByLines();
77
            totalLines += textByLines.length;
78
            for (String line : textByLines) {
79
                maxLineSize = checkMaxLineSize(maxLineSize, fontMetrics, line);
80
            }
81

  
82
        }
83

  
84
        if (active == false) {
85
            return;
86
        }
87

  
88
        int yStep = fontMetrics.getHeight();
89

  
90
        int width = padding.getLeft() + maxLineSize + padding.getRight();
91
        int height = totalLines * yStep + padding.getTop() + padding.getBottom();
92

  
93
        Dimension drawingDimension = visCanvas.getBounds().getSize();
94
        Point topLeft = positionFunction.getTopLeftPoint(width, height, drawingDimension);
95

  
96
        if (rectangle == null) {
97
            rectangle = new Rectangle(topLeft);
98
        }
99

  
100
        rectangle.setLocation(topLeft);
101
        rectangle.setSize(width, height);
102
        int x = topLeft.x;
103
        int y = topLeft.y;
104

  
105
        canvas.setColor(backgroundColor);
106
        canvas.fill(rectangle);
107

  
108
        x += padding.getLeft();
109
        y += padding.getTop() + fontMetrics.getAscent();
110

  
111
        for (ColoredTextElement textLine : textLines) {
112
            canvas.setColor(textLine.getColor());
113
            for (String line : textLine.getTextByLines()) {
114
                canvas.drawString(line, x, y);
115
                y += yStep;
116
            }
117

  
118
        }
119
    }
120

  
121
    private int checkMaxLineSize(int maxLineSize, FontMetrics fontMetrics, String line) {
122
        return Math.max(maxLineSize, fontMetrics.stringWidth(line));
123
    }
124

  
125
    public boolean isActive() {
126
        return active;
127
    }
128

  
129
    @Override
130
    public boolean isInArea(int x, int y) {
131
        return rectangle != null && rectangle.contains(x, y);
132
    }
133

  
134
    @Override
135
    public void moveLocation(int deltaX, int deltaY) {
136
        positionFunction.moveLocation(deltaX, deltaY);
137
    }
138

  
139

  
140
}
src/main/java/cz/agents/alite/visualizer2d/layers/textBackgroundLayer/TextBackgroundLayerBuilder.java
1
package cz.agents.alite.visualizer2d.layers.textBackgroundLayer;
2

  
3
import java.awt.*;
4
import java.util.function.Supplier;
5

  
6
/**
7
 * Builder for {@link TextBackgroundLayer} class.
8
 *
9
 * @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
10
 * @see TextBackgroundLayer
11
 * @see PositionFunction
12
 */
13
public class TextBackgroundLayerBuilder {
14

  
15
    public static final Color DEFAULT_COLOR = new Color(0, 0, 0, 100);
16

  
17
    public static final int DEFAULT_LEFT_PADDING = 15;
18
    public static final int DEFAULT_RIGHT_PADDING = 15;
19
    public static final int DEFAULT_BOTTOM_PADDING = 15;
20
    public static final int DEFAULT_TOP_PADDING = 15;
21

  
22
    private Supplier<? extends Iterable<ColoredTextElement>> supplier;
23
    private PositionFunction positionFunction;
24
    private Color backgroundColor = DEFAULT_COLOR;
25
    private Padding padding = new Padding(DEFAULT_LEFT_PADDING, DEFAULT_RIGHT_PADDING, DEFAULT_BOTTOM_PADDING, DEFAULT_TOP_PADDING);
26

  
27
    /**
28
     * Create builder with {@link TopLeftPositionFunction}.
29
     * @param x x-coordinate of top left corner
30
     * @param y y-coordinate of top left corner
31
     * @param supplier elements to display
32
     * @return the builder
33
     */
34
    public static TextBackgroundLayerBuilder createTopLeft(int x, int y,
35
                                                           Supplier<? extends Iterable<ColoredTextElement>> supplier) {
36
        return new TextBackgroundLayerBuilder(new TopLeftPositionFunction(x, y),
37
                supplier);
38
    }
39

  
40
    /**
41
     * Create builder with {@link RightOffsetPositionFunction}.
42
     * @param rightOffset distance between right edge of the drawn rectangle and right border of canvas
43
     * @param y y-coordinate of the top edge of the drawn rectangle
44
     * @param supplier elements to display
45
     * @return the builder
46
     */
47
    public static TextBackgroundLayerBuilder createRightOffset(int rightOffset, int y,
48
                                                               Supplier<? extends Iterable<ColoredTextElement>> supplier) {
49
        return new TextBackgroundLayerBuilder(new RightOffsetPositionFunction(rightOffset, y),
50
                supplier);
51
    }
52

  
53
    /**
54
     * Create builder with {@link BottomOffsetPositionFunction}.
55
     * @param x x-coordinate of the left edge of the drawn rectange
56
     * @param bottomOffset distance between bottom edge of the drawn rectangle and bottom border of canvas
57
     * @param supplier elements to display
58
     * @return the builder
59
     */
60
    public static TextBackgroundLayerBuilder createBottomOffset(int x, int bottomOffset,
61
                                                                Supplier<? extends Iterable<ColoredTextElement>> supplier) {
62
        return new TextBackgroundLayerBuilder(new BottomOffsetPositionFunction(x, bottomOffset),
63
                supplier);
64
    }
65

  
66
    /**
67
     * Create builder with {@link RightBottomOffsetPositionFunction}.
68
     * @param rightOffset distance between right edge of the drawn rectangle and right border of canvas
69
     * @param bottomOffset distance between bottom edge of the drawn rectangle and bottom border of canvas
70
     * @param coloredTextLineElements elements to display
71
     * @return the builder
72
     */
73
    public static TextBackgroundLayerBuilder
74
    createRightBottomOffset(int rightOffset, int bottomOffset,
75
                            Supplier<? extends Iterable<ColoredTextElement>> coloredTextLineElements) {
76
        return new TextBackgroundLayerBuilder(new RightBottomOffsetPositionFunction(rightOffset,
77
                bottomOffset), coloredTextLineElements);
78
    }
79

  
80
    /**
81
     * Create builder with given positionFunction and text
82
     * @param positionFunction position function
83
     * @param supplier text elements to display
84
     */
85
    public TextBackgroundLayerBuilder(PositionFunction positionFunction,
86
                                      Supplier<? extends Iterable<ColoredTextElement>> supplier) {
87
        this.positionFunction = positionFunction;
88
        this.supplier = supplier;
89
    }
90

  
91
    public TextBackgroundLayerBuilder setSupplier(
92
            Supplier<? extends Iterable<ColoredTextElement>> supplier) {
93
        this.supplier = supplier;
94
        return this;
95
    }
96

  
97
    public TextBackgroundLayerBuilder setPositionFunction(PositionFunction positionFunction) {
98
        this.positionFunction = positionFunction;
99
        return this;
100
    }
101

  
102
    public TextBackgroundLayerBuilder setBackgroundColor(Color backgroundColor) {
103
        this.backgroundColor = backgroundColor;
104
        return this;
105
    }
106

  
107
    public TextBackgroundLayerBuilder setPadding(Padding padding) {
108
        this.padding = padding;
109
        return this;
110
    }
111

  
112
    public TextBackgroundLayer build() {
113
        return TextBackgroundLayer.create(supplier, positionFunction,
114
                backgroundColor, padding);
115
    }
116
}
src/main/java/cz/agents/alite/visualizer2d/layers/textBackgroundLayer/TopLeftPositionFunction.java
1
package cz.agents.alite.visualizer2d.layers.textBackgroundLayer;
2

  
3
import java.awt.*;
4

  
5
/**
6
* This implementation will keep drawn rectangle's top left corner in given point.
7
* @author Ondrej Hrstka (ondrej.hrstka at agents.fel.cvut.cz)
8
*/
9
public class TopLeftPositionFunction implements PositionFunction {
10

  
11
    private Point point;
12

  
13
    /**
14
     *
15
     * @param x x-coordinate of top left corner
16
     * @param y y-coordinate of top left corner
17
     */
18
    public TopLeftPositionFunction(int x, int y) {
19
        point = new Point(x, y);
20
    }
21

  
22
    @Override
23
    public Point getTopLeftPoint(int rectangeWidth, int rectangleHeight, Dimension drawingDimension) {
24
        return point;
25
    }
26

  
27
    @Override
28
    public void moveLocation(int deltaX, int deltaY) {
29
        point = new Point(point.x + deltaX, point.y + deltaY);
30
    }
31
}

Also available in: Unified diff