update()   B
last analyzed

Complexity

Conditions 8

Size

Total Lines 19
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 8
eloc 16
c 3
b 0
f 0
dl 0
loc 19
rs 7.3333
1
package org.gannacademy.cdf.graphics.curriculum;
2
3
import org.gannacademy.cdf.graphics.Drawable;
4
import org.gannacademy.cdf.graphics.geom.Rectangle;
5
import org.gannacademy.cdf.graphics.ui.AppWindow;
6
import org.gannacademy.cdf.graphics.ui.DrawingPanel;
7
8
import java.awt.*;
9
10
/**
11
 * <p>A visual representation of an array being sorted.</p>
12
 *
13
 * <p>This class is meant to be extended with a specific sort algorithm. For the visualization to work, the extending
14
 * subclass should make use of a number of inherited methods and data fields:</p>
15
 *
16
 * <ul>
17
 * <li>{@link #list} is an array of {@code int} that contains the data being sorted.</li>
18
 * <li>{@link #finger} is an {@code int} that represents the current position being examined in the array by the sorting algorithm.</li>
19
 * <li>{@link #swap(int, int)} is a method that swaps two elements of the array.</li>
20
 * <li>{@link #sleep(long)} allows the extending class to control the delay between steps (and thus the speed of the animation).</li>
21
 * </ul>
22
 *
23
 * <p>For example, a simple extending class might be:</p>
24
 *
25
 * <pre>
26
 * public class BubbleSort extends VisualSort() {
27
 *     public BubbleSort(DrawingPanel drawingPanel) {
28
 *         super(drawingPanel);
29
 *     }
30
 *
31
 *     public void sort() {
32
 *         for (int pass = 0; pass &lt; list.length; pass++) {
33
 *             for (finger = 0; finger &lt; list.length - 1; finger++) {
34
 *                 if (list[finger] &gt; list[finger + 1]) {
35
 *                     swap(finger, finger + 1);
36
 *                 }
37
 *                 sleep(25);
38
 *             }
39
 *         }
40
 *     }
41
 * }
42
 * </pre>
43
 *
44
 * <p>This subclass of VisualSort could be animated in a simple AppWindow:</p>
45
 *
46
 * <pre>
47
 * public class Visualization extends AppWindow() {
48
 *     private VisualSort bubbleSort;
49
 *
50
 *     protected void setup() {
51
 *         bubbleSort = new BubbleSort(getDrawingPanel());
52
 *         bubbleSort.begin(); // begins the sort
53
 *     }
54
 *
55
 *     &#064;Override
56
 *     protected void loop() {
57
 *         bubbleSort.update();
58
 *         sleep(5);
59
 *     }
60
 *
61
 *     public static void main(String[] args) {
62
 *         new Visualization();
63
 *     }
64
 * }
65
 * </pre>
66
 *
67
 * <p>Note that the {@link #sort()} method is iterating over the {@link #list} array using {@link #finger} as
68
 * the array counter. The {@link #sleep(long)} delay after each comparison slows the algorithm down so that the
69
 * {@link #update()} method can be invoked to periodically display the current state of the array in your
70
 * animation loop in your {@link AppWindow}.</p>
71
 *
72
 * @author <a href="http://github.com/gann-cdf/graphics/issues">Seth Battis</a>
73
 */
74
public abstract class VisualSort implements Runnable {
75
    /**
76
     * A list index value that will prevent that index from being highlighted
77
     */
78
    protected static final int INVALID = -1;
79
80
    /**
81
     * The number of frames to leave the positions of a swap call highlighted
82
     */
83
    protected static int swapVisualLifetime = 5;
84
85
    /**
86
     * The color of a normal list element
87
     */
88
    protected static Color elementColor = Color.BLUE;
89
90
    /**
91
     * The color of the list element indexed by {@link #finger}
92
     */
93
    protected static Color fingerColor = Color.RED;
94
95
    /**
96
     * The color of the list elements affected by the most recent call to {@link #swap(int, int)}
97
     */
98
    protected static Color swapColor = Color.YELLOW;
99
100
    /**
101
     * The list of data to be sorted
102
     */
103
    protected int[] list;
104
105
    /**
106
     * A variable to be used as the list index if the finger is to be highlighted visually. If this field is
107
     * set to {@link #INVALID} it will not be highlighted.
108
     */
109
    protected int finger;
110
111
    /**
112
     * The list indices most recently swapped by {@link #swap(int, int)}
113
     */
114
    private int left, right;
115
116
    /**
117
     * The number of frames since a swap was performed
118
     */
119
    private int swapVisualCount;
120
121
    /**
122
     *
123
     */
124
    private boolean hasBegun;
125
126
    /**
127
     * The visuals representing the list elements
128
     */
129
    protected Rectangle[] visuals;
130
    private DrawingPanel drawingPanel;
131
132
    /**
133
     * <p>Construct a new VisualSort instance with default parameters</p>
134
     *
135
     * @param drawingPanel The DrawingPanel on which to display the VisualSort
136
     */
137
    public VisualSort(DrawingPanel drawingPanel) {
138
        this(100, drawingPanel);
139
    }
140
141
    /**
142
     * <p>Construct a new VisualSort instance with a specified number of elements</p>
143
     *
144
     * @param elementCount The number of elements in the list
145
     * @param drawingPanel The DrawingPanel on which to display the VisualSort
146
     */
147
    public VisualSort(int elementCount, DrawingPanel drawingPanel) {
148
        this(elementCount, false, drawingPanel);
149
    }
150
151
    /**
152
     * <p>Construct a new VisualSort instance with a specified number of elements, which may also be unique</p>
153
     *
154
     * @param elementCount   The number of elements in the list
155
     * @param uniqueElements Whether or not the elements should be unique (or will there be repeat values)
156
     * @param drawingPanel   The DrawingPanel on which to display the VisualSort
157
     */
158
    public VisualSort(int elementCount, boolean uniqueElements, DrawingPanel drawingPanel) {
159
        hasBegun = false;
160
        swapVisualCount = 0;
161
        finger = INVALID;
162
        resetSwapVisual();
163
164
        list = new int[elementCount];
165
        if (uniqueElements) {
166
            initializeWithUniqueElements();
167
        } else {
168
            initializeWithRandomElements();
169
        }
170
171
        this.drawingPanel = drawingPanel;
172
        visuals = new Rectangle[elementCount];
173
        for (int i = 0; i < visuals.length; i++) {
174
            visuals[i] = new Rectangle(0, 0, 0, 0, drawingPanel);
175
            visuals[i].setStroke(Drawable.NO_STROKE);
176
            visuals[i].setFillColor(elementColor);
177
        }
178
179
        new Thread(this).start();
180
    }
181
182
    /**
183
     * Disable the highlighting of the swap indices
184
     */
185
    private void resetSwapVisual() {
186
        swapVisualCount++;
187
        if (swapVisualCount == swapVisualLifetime) {
188
            left = INVALID;
189
            right = INVALID;
190
        }
191
    }
192
193
    /**
194
     * Initialize the list with random non-unique elements
195
     */
196
    private void initializeWithRandomElements() {
197
        for (int i = 0; i < list.length; i++) {
198
            list[i] = (int) (Math.random() * list.length);
0 ignored issues
show
introduced by
Use "java.util.Random.nextInt()" instead.
Loading history...
199
        }
200
    }
201
202
    /**
203
     * Initialize the list with unique elements and then shuffle them
204
     */
205
    private void initializeWithUniqueElements() {
206
        for (int i = 0; i < list.length; i++) {
207
            list[i] = i;
208
        }
209
        for (int i = 0; i < 7; i++) {
210
            shuffle();
211
        }
212
    }
213
214
    /**
215
     * Shuffle the list elements by randomly swapping them
216
     */
217
    private void shuffle() {
218
        for (int i = 0; i < list.length; i++) {
219
            swap((int) (Math.random() * list.length), (int) (Math.random() * list.length));
0 ignored issues
show
introduced by
Use "java.util.Random.nextInt()" instead.
Loading history...
220
        }
221
    }
222
223
    /**
224
     * Swap the values of two list elements
225
     *
226
     * @param a The index of one element to be swapped
227
     * @param b The index of the second element to be swapped
228
     */
229
    protected synchronized void swap(int a, int b) {
230
        swapVisualCount = 0;
231
        left = a;
232
        right = b;
233
        int temp = list[a];
234
        list[a] = list[b];
235
        list[b] = temp;
236
    }
237
238
    /**
239
     * Begin the animation of the sort algorithm
240
     */
241
    public void begin() {
242
        hasBegun = true;
243
    }
244
245
    /**
246
     * Pause execution of the sort algorithm
247
     *
248
     * @param millis The number of milliseconds to pause
249
     */
250
    protected void sleep(long millis) {
251
        try {
252
            Thread.sleep(millis);
253
        } catch (InterruptedException e) {
0 ignored issues
show
introduced by
Either re-interrupt this method or rethrow the "InterruptedException".
Loading history...
254
            e.printStackTrace();
0 ignored issues
show
Best Practice introduced by
Throwable.printStackTrace writes to the console which might not be available at runtime. Using a logger is preferred.
Loading history...
255
        }
256
    }
257
258
    /**
259
     * Begin the sorting thread
260
     *
261
     * @deprecated This should not be called manually
262
     */
263
    public void run() {
264
        while (!hasBegun) {
265
            sleep(10);
266
        }
267
        sort();
268
    }
269
270
    /**
271
     * The sorting algorithm to be animated
272
     */
273
    protected abstract void sort();
274
275
    /**
276
     * Update the visual display of the list elements to reflect the progress of the sorting algorithm
277
     */
278
    public synchronized void update() {
279
        double
280
                width = (double) (drawingPanel.getWidth() - 1) / (double) list.length,
281
                height = (double) (drawingPanel.getHeight() - 2) / (double) list.length;
282
        for (int i = 0; i < list.length; i++) {
283
            visuals[i].setLocation(i * width + 1, drawingPanel.getHeight() - 1 - list[i] * height);
284
            visuals[i].setWidth(Math.max(width - 1, width * 0.5));
285
            visuals[i].setHeight(list[i] * height);
286
            visuals[i].setFillColor(elementColor);
287
        }
288
        if (left >= 0 && left < list.length) {
289
            visuals[left].setFillColor(swapColor);
290
        }
291
        if (right >= 0 && right < list.length) {
292
            visuals[right].setFillColor(swapColor);
293
        }
294
        resetSwapVisual();
295
        if (finger >= 0 && finger < list.length) {
296
            visuals[finger].setFillColor(fingerColor);
297
        }
298
    }
299
}