Compare commits

...

3 Commits

5 changed files with 185 additions and 23 deletions

View File

@ -1,6 +1,7 @@
import 'dart:html' as html; import 'dart:html' as html;
import 'dart:math'; import 'dart:math';
import 'package:rules_of_living/src/Render.dart';
import 'package:rules_of_living/src/Simulation.dart'; import 'package:rules_of_living/src/Simulation.dart';
class Engine { class Engine {
@ -29,33 +30,52 @@ class Engine {
// ms stuck in updateloop after which game will declare itself unresponsive // ms stuck in updateloop after which game will declare itself unresponsive
final int SAFETY_TIMEOUT = 2000; final int SAFETY_TIMEOUT = 2000;
bool running = false;
Render _render;
/// Grid Size /// Grid Size
/// ///
/// Number of cells on x coordinate and y coordinate. Can be set individually. /// Number of cells on x coordinate and y coordinate. Can be set individually.
Point get gridSize => Point<int>(_grid.w, _grid.h); Point get gridSize => Point<int>(_simulation.w, _simulation.h);
void set gridSize(Point<int> value) { void set gridSize(Point<int> value) {
if (value.x <= 0 || value.y <= 0) if (value.x <= 0 || value.y <= 0)
throw ArgumentError("grid size must not be smaller than 1"); throw ArgumentError("grid size must not be smaller than 1");
_grid = Simulation(value.x, value.y); _simulation = Simulation(value.x, value.y);
} }
num _updateLag = 0.0; num _updateLag = 0.0;
num _drawLag = 0.0; num _drawLag = 0.0;
Simulation __simulation;
Simulation get _simulation => __simulation;
void set _simulation(Simulation value) {
__simulation = value;
_resetRenderer();
}
/// The Canvas to Paint on /// The Canvas to Paint on
/// ///
/// Manually define or change the canvas the engine should render to. Should /// Manually define or change the canvas the engine should render to. Should
/// be used if no canvas was defined at engine creation and it should be /// be used if no canvas was defined at engine creation and it should be
/// rendered later. /// rendered later.
html.CanvasElement canvas; html.CanvasElement _canvas;
Simulation _grid; html.CanvasElement get canvas => _canvas;
bool running = false; void set canvas(html.CanvasElement canvas) {
_canvas = canvas;
_resetRenderer();
}
Engine([x = 100, y = 100, this.canvas]) { void _resetRenderer() {
_grid = Simulation(x, y); if (this.canvas == null || _simulation == null) return;
_render = Render(canvas, gridSize);
}
Engine([x = 100, y = 100, canvas]) {
this.canvas = canvas;
_simulation = Simulation(x, y);
_elapsed.start(); _elapsed.start();
_grid.addPattern(amount: 15, dispersal: 5); _simulation.addPattern(amount: 15, dispersal: 5);
html.window.animationFrame.then(animFrame); html.window.animationFrame.then(animFrame);
} }
@ -65,12 +85,12 @@ class Engine {
} }
void reset() { void reset() {
_grid.reset(); _simulation.reset();
running = false; running = false;
} }
void clear() { void clear() {
_grid = new Simulation(gridSize.x, gridSize.y); _simulation = new Simulation(gridSize.x, gridSize.y);
running = false; running = false;
} }
@ -101,7 +121,13 @@ class Engine {
/// directly, since it is automatically taken care of by the processing function. /// directly, since it is automatically taken care of by the processing function.
/// If simulation should be advanced manually one time, prefer using step(). /// If simulation should be advanced manually one time, prefer using step().
void update() { void update() {
if (!_grid.update()) running = false; Map<int, bool> stateChanges = _simulation.update();
if (stateChanges.length == 0) {
running = false;
return;
} else {
_render.mergeChanges(stateChanges);
}
} }
/// Advances Logic One Update /// Advances Logic One Update
@ -111,7 +137,7 @@ class Engine {
/// (though this should usually not pose a problem). /// (though this should usually not pose a problem).
void step() { void step() {
running = false; running = false;
_grid.update(); _simulation.update();
} }
/// Renders the Current Simulation State /// Renders the Current Simulation State
@ -119,9 +145,7 @@ class Engine {
/// Renders the simulation once. Will usually automatically be called by /// Renders the simulation once. Will usually automatically be called by
/// the internal engine processing. Does not do anything if no canvas is /// the internal engine processing. Does not do anything if no canvas is
/// defined. /// defined.
void render([num interp]) { void render([num interp]) {}
if (canvas != null) _grid.render(canvas, interp);
}
void addPattern( void addPattern(
{CellPattern pattern, {CellPattern pattern,
@ -130,7 +154,7 @@ class Engine {
int amount, int amount,
int dispersal, int dispersal,
int seed}) { int seed}) {
_grid.addPattern( _simulation.addPattern(
pattern: pattern, pattern: pattern,
x: x, x: x,
y: y, y: y,
@ -140,6 +164,6 @@ class Engine {
} }
void toggleEdgeRendering() { void toggleEdgeRendering() {
_grid.renderEdges = !_grid.renderEdges; // _grid.renderEdges = !_grid.renderEdges;
} }
} }

121
lib/src/Render.dart Normal file
View File

@ -0,0 +1,121 @@
import 'dart:html' as html;
import 'dart:math';
import 'package:stagexl/stagexl.dart' as sxl;
class ColorScheme {
//TODO make iterable (perhaps through backing list which gets accesses by the variables)
final int ON;
final int EXPANSION;
final int CORE;
final int OFF;
const ColorScheme(this.ON, this.EXPANSION, this.CORE,
[this.OFF = sxl.Color.Transparent]);
}
class Render {
final sxl.Stage _stage;
final sxl.RenderLoop _renderLoop = sxl.RenderLoop();
Map<int, sxl.BitmapData> _colorMap;
final sxl.BitmapContainer _renderContainer = sxl.BitmapContainer();
bool transparentBG = false;
bool _renderEdges = false;
bool get renderEdges => _renderEdges;
void set renderEdges(bool value) {
_renderEdges = value;
_colorMap = _getColorMap(_cellSize, _colorScheme);
}
Point<int> _gridSize;
Point<int> get _cellSize => Point(
_stage.stageWidth ~/ _gridSize.x, _stage.stageHeight ~/ _gridSize.y);
final ColorScheme _colorScheme;
//TODO replace with scheme data structure to enable different color scheme injection
static const defaultScheme =
ColorScheme(sxl.Color.Blue, 1, 1, sxl.Color.Black);
// TODO gridSize rendering can only be scaled uniformly currently - switch to Point(w,h)?
Render(html.CanvasElement canvas, Point<int> gridSize,
[ColorScheme colorScheme = defaultScheme])
: _stage = _createStage(canvas),
_colorScheme = colorScheme,
_gridSize = gridSize {
_colorMap = _getColorMap(_cellSize, colorScheme);
_initRenderGrid(_cellSize, gridSize).forEach((sxl.Bitmap bm) {
_renderContainer.addChild(bm);
});
_renderLoop.addStage(_stage);
_stage.addChild(_renderContainer);
setDirty();
}
static sxl.Stage _createStage(html.CanvasElement canvas) {
sxl.StageXL.stageOptions.renderEngine = sxl.RenderEngine.WebGL;
sxl.StageXL.stageOptions.stageRenderMode = sxl.StageRenderMode.AUTO_INVALID;
// sxl.StageXL.stageOptions.backgroundColor = sxl.Color.Yellow;
return sxl.Stage(canvas);
}
Map<int, sxl.BitmapData> _getColorMap(Point<int> size, ColorScheme scheme) {
print("${size.toString()}, ${scheme.toString()}");
// Creates a shape with color of each quad in scheme
sxl.Shape shape = sxl.Shape();
// ON
shape.graphics.beginPath();
shape.graphics.rect(0 * size.x, 0, size.x, size.y);
shape.graphics.fillColor(scheme.ON ?? sxl.Color.Transparent);
shape.graphics
.strokeColor(renderEdges ? sxl.Color.DarkGray : sxl.Color.Transparent);
shape.graphics.closePath();
// OFF
shape.graphics.beginPath();
shape.graphics.rect(1 * size.x, 0, size.x, size.y);
shape.graphics.fillColor(scheme.OFF ?? sxl.Color.Transparent);
shape.graphics
.strokeColor(renderEdges ? sxl.Color.DarkGray : sxl.Color.Transparent);
shape.graphics.closePath();
// creates one texture out of shape
sxl.BitmapData texture = sxl.BitmapData(2 * size.x, size.y);
texture.draw(shape);
// re-slice texture into individual BitmapDatas
Map<int, sxl.BitmapData> colorMap = Map();
List<sxl.BitmapData> colorBitmaps = texture.sliceIntoFrames(size.x, size.y);
print("Found ${colorBitmaps.length} colors.");
// TODO more elegant way through iterables etc; also include EXPANSION, CORE functionality
colorMap[scheme.ON] = colorBitmaps[0];
colorMap[scheme.OFF] = colorBitmaps[1];
return colorMap;
}
List<sxl.Bitmap> _initRenderGrid(Point<int> cellSize, Point<int> gridSize) {
List<sxl.Bitmap> grid = List();
for (int y = 0; y < gridSize.y; y++) {
for (int x = 0; x < gridSize.x; x++) {
sxl.Bitmap bm = sxl.Bitmap();
bm.bitmapData = _colorMap[_colorScheme.OFF];
bm.x = x * cellSize.x;
bm.y = y * cellSize.y;
grid.add(bm);
}
}
return grid;
}
void setDirty() => _stage.invalidate();
void mergeChanges(Map<int, bool> stateChanges) {
if (stateChanges.length == 0) return;
stateChanges.forEach((int i, bool state) {
_renderContainer.getChildAt(i).bitmapData =
(state ? _colorMap[_colorScheme.ON] : _colorMap[_colorScheme.OFF]);
});
setDirty();
}
}

View File

@ -125,19 +125,20 @@ class Simulation {
return null; return null;
} }
bool update() { Map<int, bool> update() {
bool stateChanges = false; Map<int, bool> stateChanges = Map();
for (int i = 0; i < map.length; i++) { for (int i = 0; i < map.length; i++) {
math.Point p = map.toCoordinates(i); math.Point p = map.toCoordinates(i);
map[i].update(getSurroundingNeighbors(p.x, p.y, 1)); map[i].update(getSurroundingNeighbors(p.x, p.y, 1));
} }
// TODO when implementing changeSet we can remove this second loop and add to changeSet in the first // TODO when implementing changeSet we can remove this second loop and add to changeSet in the first
map.forEach((Cell el) { for (int i = 0; i < map.length; i++) {
if (el.state != el.nextState) stateChanges = true; Cell el = map[i];
if (el.state != el.nextState) stateChanges[i] = el.nextState;
el.advanceState(); el.advanceState();
}); }
stateChanges ? _dirty = true : false; (stateChanges.length > 0) ? _dirty = true : false;
return stateChanges; return stateChanges;
} }

View File

@ -10,6 +10,7 @@ environment:
dependencies: dependencies:
angular: ^5.0.0-beta angular: ^5.0.0-beta
angular_components: ^0.9.0-beta angular_components: ^0.9.0-beta
stagexl: ^1.4.0+1
dev_dependencies: dev_dependencies:
angular_test: ^2.0.0-beta angular_test: ^2.0.0-beta

15
test/src/render_test.dart Normal file
View File

@ -0,0 +1,15 @@
import 'dart:html' as html;
import 'dart:math';
@TestOn('browser')
import 'package:rules_of_living/src/Render.dart';
import 'package:test/test.dart';
void main() {
Render sut;
setUp(() {
html.CanvasElement canvas = html.CanvasElement();
sut = Render(canvas, 8);
});
group("colorMap", () {});
}