Skip to content

Commit 3459f75

Browse files
committed
Basic persistence-of-vision effect, better zoom
Dynamic refresh is capped at 30Hz, changes which occur faster than that are shown using persistence-of-vision. In the new system, LEDs which were turned on at any point between the last rendered refresh and this one are displayed as being on. I also added backend support for adjustable dynamic (simulation-driven) refresh rate, although this may not be that useful. Overhauled zoom to support a wider range of zoom levels and smooth zoom in/out e.g. with trackpad. Finally fixed zoom-to-point: zooming in and out rapidly no longer causes the camera location to jump around randomly. Grid display is now disabled when extremely zoomed-out, which greatly improves performance (lots of lines = bad).
1 parent 0617119 commit 3459f75

11 files changed

Lines changed: 129 additions & 66 deletions

File tree

src/com/modsim/gui/GUI.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,15 +254,15 @@ private void createViewport() {
254254
*/
255255
public void zoomInToView()
256256
{
257-
view.zoomIn(view.getWidth()/2,view.getHeight()/2);
257+
view.zoom(view.getWidth()/2, view.getHeight()/2, 1.0);
258258
}
259259

260260
/*
261261
* Accessible method for zooming out of view
262262
*/
263263
public void zoomOutToView()
264264
{
265-
view.zoomOut(view.getWidth()/2,view.getHeight()/2);
265+
view.zoom(view.getWidth()/2, view.getHeight()/2, -1.0);
266266
}
267267

268268

src/com/modsim/gui/view/View.java

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
import java.awt.geom.*;
55
import java.awt.image.BufferedImage;
66
import java.text.DecimalFormat;
7+
import java.util.prefs.Preferences;
78

89
import javax.swing.*;
910

1011
import com.modsim.modules.BaseModule;
1112
import com.modsim.modules.Link;
13+
import com.modsim.modules.parts.VisiblePart;
1214
import com.modsim.res.Colors;
1315
import com.modsim.Main;
1416
import com.modsim.tools.BaseTool;
@@ -38,10 +40,16 @@ public class View extends JPanel {
3840

3941
public boolean useAA = true;
4042

43+
private int dynamicRefreshRate = 30;
44+
4145
private BufferedImage staticCanvas = null;
4246
private boolean staticIsDirty = true;
4347
private long lastDynamicPaint = 0;
4448

49+
// Zoom caps
50+
public static final double minZoom = 0.01;
51+
public static final double maxZoom = 6.0;
52+
4553
public View() {
4654
setFocusable(true);
4755
ViewUtil listener = new ViewUtil();
@@ -50,7 +58,32 @@ public View() {
5058
addMouseWheelListener(listener);
5159
addKeyListener(listener);
5260

53-
//staticCanvas = getGraphicsConfiguration().createCompatibleImage(800, 600);
61+
// Fetch the preferred refresh rate
62+
Preferences prefs = Preferences.userNodeForPackage(View.class);
63+
dynamicRefreshRate = prefs.getInt("dynamic_refresh_rate", dynamicRefreshRate);
64+
}
65+
66+
/***
67+
* @return The current dynamic refresh rate
68+
*/
69+
public int getDynamicRefreshRate() {
70+
return dynamicRefreshRate;
71+
}
72+
73+
/***
74+
* Sets and stores the dynamic refresh rate for the view. Capped to 5-120Hz
75+
* @param newRate The new refresh rate, in Hz
76+
*/
77+
public void setDynamicRefreshRate(int newRate) {
78+
if (newRate < 5) {
79+
newRate = 5;
80+
}
81+
else if (newRate > 120) {
82+
newRate = 120;
83+
}
84+
Preferences prefs = Preferences.userNodeForPackage(View.class);
85+
dynamicRefreshRate = newRate;
86+
prefs.putInt("dynamic_refresh_rate", newRate);
5487
}
5588

5689
public void calcXForm() {
@@ -192,9 +225,9 @@ public void paintComponent(Graphics oldG) {
192225
}
193226

194227
/**
195-
* Draws an error flag
228+
* Draws a module's error flag
196229
*/
197-
public void drawError(Graphics2D g) {
230+
private void drawError(Graphics2D g) {
198231
g.setColor(Colors.errorEdge);
199232
g.drawOval(-30, -30, 60, 60);
200233
g.setColor(Colors.errorFill);
@@ -205,27 +238,32 @@ public void drawError(Graphics2D g) {
205238
}
206239

207240
/**
208-
* Draws a grid
241+
* Draws the background grid
209242
*/
210-
public void drawGrid(Graphics2D g) {
243+
private void drawGrid(Graphics2D g) {
211244
double grid = zoom * Main.sim.grid;
212245

246+
// When extremely zoomed-out, displaying the grid is costly and pointless
247+
if (grid < 1.5) {
248+
return;
249+
}
250+
213251
int xNum = (int)(getWidth() / grid);
214252
int yNum = (int)(getHeight() / grid);
215253

216-
AffineTransform oldxform = new AffineTransform(g.getTransform());
254+
AffineTransform oldTransform = new AffineTransform(g.getTransform());
217255
Line2D verticalLine = new Line2D.Double(0.0, -grid, 0.0, getHeight() + grid);
218256
Line2D horizontalLine = new Line2D.Double(-grid, 0.0, getWidth() + grid, 0.0);
219257
for (int i = 0; i <= xNum + 1; i++) {
220258
g.draw(verticalLine);
221259
g.translate(grid, 0.0);
222260
}
223-
g.setTransform(oldxform);
261+
g.setTransform(oldTransform);
224262
for (int i = 0; i <= yNum + 1; i++) {
225263
g.draw(horizontalLine);
226264
g.translate(0.0, grid);
227265
}
228-
g.setTransform(oldxform);
266+
g.setTransform(oldTransform);
229267
}
230268

231269
/**
@@ -254,45 +292,25 @@ public void setTool(BaseTool newTool) {
254292
}
255293

256294
/**
257-
* Zooms the viewport in on the specified screen point
295+
* Smoothly zooms the viewport in or out of the specified screen point
258296
* @param x X-coordinate
259297
* @param y Y-coordinate
260298
*/
261-
public void zoomIn(int x, int y) {
299+
public void zoom(int x, int y, double amount) {
262300
Vec2 zmPt = ViewUtil.screenToWorld(new Vec2(x, y));
263301

264-
zoomI++;
265-
if (zoomI > ZOOM_LIMIT) {
266-
zoomI --;
302+
zoom -= zoom * amount * ZOOM_MULTIPLIER;
303+
if (zoom < minZoom) {
304+
zoom = minZoom;
267305
}
268-
else {
269-
zoom = zoomI * ZOOM_MULTIPLIER;
270-
calcXForm();
271-
Vec2 newScreenPt = ViewUtil.worldToScreen(zmPt);
272-
camX -= newScreenPt.x - x;
273-
camY -= newScreenPt.y - y;
274-
}
275-
}
276-
277-
/**
278-
* Zooms the view out form the specified screen point
279-
* @param x X-coordinate
280-
* @param y Y-coordinate
281-
*/
282-
public void zoomOut(int x, int y) {
283-
Vec2 zmPt = ViewUtil.screenToWorld(new Vec2(x, y));
284-
285-
zoomI--;
286-
if (zoomI < 1) {
287-
zoomI = 1;
288-
}
289-
else {
290-
zoom = zoomI * ZOOM_MULTIPLIER;
291-
calcXForm();
292-
Vec2 newScreenPt = ViewUtil.worldToScreen(zmPt);
293-
camX -= newScreenPt.x - x;
294-
camY -= newScreenPt.y - y;
306+
else if (zoom > maxZoom) {
307+
zoom = maxZoom;
295308
}
309+
calcXForm();
310+
Vec2 newScreenPt = ViewUtil.worldToScreen(zmPt);
311+
camX -= newScreenPt.x - x;
312+
camY -= newScreenPt.y - y;
313+
calcXForm();
296314
}
297315

298316
/***
@@ -308,12 +326,16 @@ public void flagStaticRedraw() {
308326
*/
309327
public void flagDynamicRedraw() {
310328
long currentTime = System.currentTimeMillis();
311-
if (abs(currentTime - lastDynamicPaint) > 33) {
329+
if (abs(currentTime - lastDynamicPaint) > (1000 / dynamicRefreshRate)) {
312330
repaint();
313331
}
314332
else {
315333
// persistence-of-vision simulation
316-
// TODO!!
334+
for (BaseModule m : Main.sim.getModules()) {
335+
for (VisiblePart p : m.parts) {
336+
p.povTick();
337+
}
338+
}
317339
}
318340
}
319341
}

src/com/modsim/gui/view/ViewUtil.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -385,18 +385,13 @@ public void mouseMoved(MouseEvent e) {
385385
Main.ui.view.curTool = tool.mouseMove(e.getX(), e.getY());
386386
}
387387

388-
Main.ui.view.repaint();
388+
Main.ui.view.flagDynamicRedraw();
389389
}
390390

391391
public void mouseWheelMoved(MouseWheelEvent e) {
392392
testKeys(e);
393393
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
394-
if (e.getWheelRotation() < 0) {
395-
Main.ui.view.zoomIn(e.getX(), e.getY());
396-
}
397-
else {
398-
Main.ui.view.zoomOut(e.getX(), e.getY());
399-
}
394+
Main.ui.view.zoom(e.getX(), e.getY(), e.getPreciseWheelRotation());
400395
}
401396

402397
Main.ui.view.flagStaticRedraw();

src/com/modsim/modules/parts/LED.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ public class LED extends TogglePart {
1111
private Color color;
1212
private Color hColor;
1313

14+
private int povTicks = 0;
15+
private int povHits = 0;
16+
1417
// Convenience method, single LEDs are most commonly control.
1518
public LED(int x, int y) {
1619
this(x, y, LEDColour.BLUE);
@@ -30,9 +33,9 @@ public void paint(Graphics2D g) {
3033
g.setColor(Colors.ledBack);
3134
g.fillRect(x-4, y-4, 8, 8);
3235

33-
if (getEnabled()) {
36+
if (getEnabled() || povHits > 0) {
3437
g.setColor(color);
35-
g.fillRect(x-4, y-4, 8, 8);
38+
g.fillRect(x-4, y-4, 8, 8);
3639
g.setColor(hColor);
3740
g.fillRect(x-2, y-2, 4, 4);
3841
}
@@ -41,6 +44,15 @@ public void paint(Graphics2D g) {
4144
g.fillRect(x-2, y-2, 4, 4);
4245
}
4346

47+
povTicks = 0;
48+
povHits = 0;
4449
}
4550

51+
@Override
52+
public void povTick() {
53+
povTicks++;
54+
if (getEnabled()) {
55+
povHits++;
56+
}
57+
}
4658
}

src/com/modsim/modules/parts/LEDRow.java

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@
1414
*/
1515
public class LEDRow extends VisiblePart {
1616

17+
private static final int NLEDS = 4;
18+
1719
private Color color = Color.BLUE;
1820
private Color hColor = Color.BLUE;
1921

2022
private volatile BinData curVal = new BinData(0);
2123

24+
private int povTicks = 0;
25+
private int povHits[] = new int[NLEDS];
26+
2227
// Convenience method, LEDRow is usually data.
2328
public LEDRow(int x, int y) {
2429
this(x, y, LEDColour.RED);
@@ -55,10 +60,10 @@ public void paint(Graphics2D g) {
5560
g.fillRect(x-15, y-3, 30, 6);
5661

5762
BinData v = getVal();
58-
for (int i = 0; i < 4; i++) {
63+
for (int i = 0; i < NLEDS; i++) {
5964
int offs = x+(1-i)*8;
6065

61-
if (v.getBit(i) == 1) {
66+
if (v.getBit(i) == 1 || povHits[i] > 0) {
6267
g.setColor(color);
6368
g.fillRect(offs+1, y-3, 6, 6);
6469
g.setColor(hColor);
@@ -69,12 +74,27 @@ public void paint(Graphics2D g) {
6974
g.fillRect(offs+2, y-2, 4, 4);
7075
}
7176
}
77+
78+
resetPov();
7279
}
7380

74-
@Override
75-
public void reset() {
76-
curVal = new BinData(0);
77-
}
81+
@Override
82+
public void povTick() {
83+
povTicks++;
84+
BinData v = getVal();
85+
for (int i = 0; i < NLEDS; i++) {
86+
if (v.getBit(i) == 1) {
87+
povHits[i]++;
88+
}
89+
}
90+
}
91+
92+
private void resetPov() {
93+
povTicks = 0;
94+
for (int i = 0; i < NLEDS; i++) {
95+
povHits[i] = 0;
96+
}
97+
}
7898

7999
@Override
80100
public RefreshMode getRefreshMode() {

src/com/modsim/modules/parts/PushBtn.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,8 @@ public void paint(Graphics2D g) {
8888
}
8989
}
9090

91+
@Override
92+
public void povTick() {
93+
// Don't care
94+
}
9195
}

src/com/modsim/modules/parts/SSText.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ public void paint(Graphics2D g) {
5555
}
5656

5757
@Override
58-
public void reset() {
59-
// Noop
58+
public void povTick() {
59+
// Don't care
6060
}
6161

6262
@Override

src/com/modsim/modules/parts/Switch.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,8 @@ public void paint(Graphics2D g) {
6868
}
6969
}
7070

71+
@Override
72+
public void povTick() {
73+
// Don't care
74+
}
7175
}

src/com/modsim/modules/parts/VisiblePart.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ public enum RefreshMode {
2525

2626
// Display
2727
public abstract void paint(Graphics2D g);
28-
public abstract void reset();
2928
public abstract RefreshMode getRefreshMode();
29+
30+
/***
31+
* Use to update the part's persistence-of-vision data
32+
*/
33+
public abstract void povTick();
3034
}

0 commit comments

Comments
 (0)