-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Dirk Groot
committed
May 11, 2014
1 parent
56c9fee
commit 35c8878
Showing
9 changed files
with
341 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package nl.dricus.gameoflife.app; | ||
|
||
import nl.dricus.gameoflife.presentation.display.DisplayView; | ||
|
||
import com.airhacks.afterburner.views.FXMLView; | ||
|
||
public class Application extends FXMLApplication { | ||
|
||
@Override | ||
protected String getTitle() { | ||
return "Conway's Game of Life"; | ||
} | ||
|
||
@Override | ||
protected FXMLView createAppRootView() { | ||
return new DisplayView(); | ||
} | ||
|
||
public static void main(String[] args) { | ||
launch(args); | ||
} | ||
|
||
} |
90 changes: 90 additions & 0 deletions
90
src/main/java/nl/dricus/gameoflife/app/FXMLApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package nl.dricus.gameoflife.app; | ||
|
||
import javafx.application.Application; | ||
import javafx.scene.Scene; | ||
import javafx.stage.Stage; | ||
|
||
import com.airhacks.afterburner.injection.InjectionProvider; | ||
import com.airhacks.afterburner.views.FXMLView; | ||
|
||
/** | ||
* Base class for JavaFX applications. Initializes the {@link Scene}, and the primary {@link Stage}. | ||
* Provides several "hooks" so subclasses can do extra initialization if needed/wanted. | ||
* | ||
* @author Dirk | ||
*/ | ||
public abstract class FXMLApplication extends Application { | ||
|
||
public static final String DEFAULT_APP_CSS_FILENAME = "app.css"; | ||
|
||
@Override | ||
public void start(Stage primaryStage) throws Exception { | ||
FXMLView appView = createAppRootView(); | ||
Scene primaryScene = createPrimaryScene(appView); | ||
|
||
initializePrimaryScene(primaryScene); | ||
initializePrimaryStage(primaryStage); | ||
|
||
primaryStage.setScene(primaryScene); | ||
primaryStage.show(); | ||
} | ||
|
||
@Override | ||
public void stop() throws Exception { | ||
InjectionProvider.forgetAll(); | ||
} | ||
|
||
/** | ||
* @return the title of the main window of the application. | ||
*/ | ||
protected abstract String getTitle(); | ||
|
||
/** | ||
* @return a new {@link FXMLView}, which will be the main view of the application. | ||
*/ | ||
protected abstract FXMLView createAppRootView(); | ||
|
||
/** | ||
* @return a new {@link Scene} with <code>appView</code> as it's root node. | ||
*/ | ||
protected Scene createPrimaryScene(FXMLView appView) { | ||
return new Scene(appView.getView()); | ||
} | ||
|
||
/** | ||
* Adds the gobal CSS file to the scene. Called by {@link #start(Stage)} before initializing the | ||
* primary {@link Stage}. Override this if you want to do more initialization. | ||
*/ | ||
protected void initializePrimaryScene(Scene scene) { | ||
scene.getStylesheets().add(getAppCssPath()); | ||
} | ||
|
||
/** | ||
* Sets the title of the primary stage (and thus of the main window of the application). Called | ||
* by {@link #start(Stage)} before setting the scene and showing the {@link Stage}. Override | ||
* this if you want to do more initialization. | ||
*/ | ||
protected void initializePrimaryStage(Stage primaryStage) { | ||
primaryStage.setTitle(getTitle()); | ||
} | ||
|
||
/** | ||
* Override this if you want to use a global CSS file which is not in the same package as your | ||
* application class. | ||
* | ||
* @return the absolute path to the file returned by {@link #getAppCssFileName()}. | ||
*/ | ||
protected String getAppCssPath() { | ||
return getClass().getResource(getAppCssFileName()).toExternalForm(); | ||
} | ||
|
||
/** | ||
* Override this is you want to use a different filename than {@link #DEFAULT_APP_CSS_FILENAME}. | ||
* | ||
* @return the filename of the global CSS file. | ||
*/ | ||
protected String getAppCssFileName() { | ||
return DEFAULT_APP_CSS_FILENAME; | ||
} | ||
|
||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
174 changes: 174 additions & 0 deletions
174
src/main/java/nl/dricus/gameoflife/presentation/display/DisplayPresenter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
package nl.dricus.gameoflife.presentation.display; | ||
|
||
import javafx.animation.Animation.Status; | ||
import javafx.animation.AnimationTimer; | ||
import javafx.animation.KeyFrame; | ||
import javafx.animation.Timeline; | ||
import javafx.beans.value.ChangeListener; | ||
import javafx.beans.value.ObservableValue; | ||
import javafx.event.ActionEvent; | ||
import javafx.event.EventHandler; | ||
import javafx.fxml.FXML; | ||
import javafx.geometry.Bounds; | ||
import javafx.scene.canvas.Canvas; | ||
import javafx.scene.image.PixelFormat; | ||
import javafx.scene.image.PixelWriter; | ||
import javafx.scene.layout.AnchorPane; | ||
import javafx.util.Duration; | ||
|
||
import javax.inject.Inject; | ||
|
||
import nl.dricus.gameoflife.boundary.Game; | ||
import nl.dricus.gameoflife.entity.Generation; | ||
|
||
public class DisplayPresenter { | ||
|
||
private static final int SCALE_FACTOR = 16; | ||
|
||
@Inject | ||
Game game; | ||
|
||
@FXML | ||
private Canvas canvas; | ||
|
||
@FXML | ||
private AnchorPane anchorPane; | ||
|
||
private AnimationTimer timer; | ||
private Timeline timeline; | ||
|
||
@FXML | ||
public void initialize() { | ||
initializeGame(); | ||
|
||
createAnimation(); | ||
createTimeline(); | ||
|
||
adjustCanvasToGameDimensions(); | ||
updateCanvasSizeOnWindowResize(); | ||
|
||
changeGameDimensionsWhenCanvasSizeChanges(); | ||
|
||
startRendering(); | ||
} | ||
|
||
private void initializeGame() { | ||
game.setDimensions((int) canvas.getWidth() / SCALE_FACTOR, (int) canvas.getHeight() / SCALE_FACTOR); | ||
game.randomizeCurrentGeneration(); | ||
} | ||
|
||
private void createAnimation() { | ||
timer = new AnimationTimer() { | ||
@Override | ||
public void handle(long now) { | ||
render(); | ||
} | ||
}; | ||
} | ||
|
||
private void createTimeline() { | ||
timeline = new Timeline(); | ||
|
||
timeline.setCycleCount(Timeline.INDEFINITE); | ||
timeline.getKeyFrames().add(createTickKeyFrame()); | ||
} | ||
|
||
private KeyFrame createTickKeyFrame() { | ||
return new KeyFrame(Duration.millis(100), new EventHandler<ActionEvent>() { | ||
@Override | ||
public void handle(ActionEvent event) { | ||
game.tick(); | ||
} | ||
}); | ||
} | ||
|
||
private void adjustCanvasToGameDimensions() { | ||
canvas.setWidth(game.getCurrentGeneration().getWidth() * SCALE_FACTOR); | ||
canvas.setHeight(game.getCurrentGeneration().getHeight() * SCALE_FACTOR); | ||
} | ||
|
||
private void updateCanvasSizeOnWindowResize() { | ||
anchorPane.widthProperty().addListener(new ChangeListener<Number>() { | ||
@Override | ||
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { | ||
canvas.setWidth(newValue.doubleValue()); | ||
} | ||
}); | ||
anchorPane.heightProperty().addListener(new ChangeListener<Number>() { | ||
@Override | ||
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { | ||
canvas.setHeight(newValue.doubleValue()); | ||
} | ||
}); | ||
} | ||
|
||
private void changeGameDimensionsWhenCanvasSizeChanges() { | ||
canvas.boundsInLocalProperty().addListener(new ChangeListener<Bounds>() { | ||
@Override | ||
public void changed(ObservableValue<? extends Bounds> observable, Bounds oldValue, Bounds newValue) { | ||
game.setDimensions((int) newValue.getWidth() / SCALE_FACTOR, (int) newValue.getHeight() / SCALE_FACTOR); | ||
game.randomizeCurrentGeneration(); | ||
} | ||
}); | ||
} | ||
|
||
private void startRendering() { | ||
timer.start(); | ||
} | ||
|
||
@FXML | ||
public void onMouseClicked() { | ||
if (timeline.getStatus() == Status.RUNNING) | ||
timeline.pause(); | ||
else | ||
timeline.play(); | ||
} | ||
|
||
private void render() { | ||
int[] pixels = renderGenerationToPixelArray(game.getCurrentGeneration()); | ||
|
||
renderPixelsToCanvas(pixels); | ||
} | ||
|
||
private int[] renderGenerationToPixelArray(Generation currentGeneration) { | ||
int scaledHeight = getScaledHeight(currentGeneration); | ||
int scaledWidth = getScaledWidth(currentGeneration); | ||
int[] pixels = new int[scaledHeight * scaledWidth]; | ||
|
||
for (int row = 0; row < scaledHeight; row++) { | ||
for (int col = 0; col < scaledWidth; col++) { | ||
int index = (row * scaledWidth) + col; | ||
|
||
if (currentGeneration.isCellAlive(col / SCALE_FACTOR, row / SCALE_FACTOR)) | ||
pixels[index] = 0xffffffff; | ||
else | ||
pixels[index] = 0xff000000; | ||
} | ||
} | ||
|
||
return pixels; | ||
} | ||
|
||
private void renderPixelsToCanvas(int[] pixels) { | ||
Generation currentGeneration = game.getCurrentGeneration(); | ||
int scaledHeight = getScaledHeight(currentGeneration); | ||
int scaledWidth = getScaledWidth(currentGeneration); | ||
PixelWriter pixelWriter = getPixelWriter(); | ||
|
||
pixelWriter | ||
.setPixels(0, 0, scaledWidth, scaledHeight, PixelFormat.getIntArgbInstance(), pixels, 0, scaledWidth); | ||
} | ||
|
||
private int getScaledHeight(Generation currentGeneration) { | ||
return currentGeneration.getHeight() * SCALE_FACTOR; | ||
} | ||
|
||
private int getScaledWidth(Generation currentGeneration) { | ||
return currentGeneration.getWidth() * SCALE_FACTOR; | ||
} | ||
|
||
private PixelWriter getPixelWriter() { | ||
return canvas.getGraphicsContext2D().getPixelWriter(); | ||
} | ||
|
||
} |
7 changes: 7 additions & 0 deletions
7
src/main/java/nl/dricus/gameoflife/presentation/display/DisplayView.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package nl.dricus.gameoflife.presentation.display; | ||
|
||
import com.airhacks.afterburner.views.FXMLView; | ||
|
||
public class DisplayView extends FXMLView { | ||
|
||
} |
Empty file.
14 changes: 14 additions & 0 deletions
14
src/main/java/nl/dricus/gameoflife/presentation/display/display.fxml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
|
||
<?import java.lang.*?> | ||
<?import javafx.scene.canvas.*?> | ||
<?import javafx.scene.canvas.Canvas?> | ||
<?import javafx.scene.layout.*?> | ||
<?import javafx.scene.layout.AnchorPane?> | ||
<?import javafx.scene.shape.*?> | ||
|
||
<AnchorPane id="pane" fx:id="anchorPane" pickOnBounds="true" scaleX="1.0" scaleY="1.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="nl.dricus.gameoflife.presentation.display.DisplayPresenter"> | ||
<children> | ||
<Canvas fx:id="canvas" height="600.0" layoutX="0.0" layoutY="0.0" onMouseClicked="#onMouseClicked" scaleX="1.0" scaleY="1.0" width="800.0" /> | ||
</children> | ||
</AnchorPane> |