diff --git a/lib/components/controls_component.dart b/lib/components/controls_component.dart index 1f2a65d..511a7d4 100644 --- a/lib/components/controls_component.dart +++ b/lib/components/controls_component.dart @@ -1,8 +1,6 @@ import 'package:angular/angular.dart'; import 'package:angular_components/angular_components.dart'; -import 'package:rules_of_living/service/configuration_service.dart'; import 'package:rules_of_living/service/engine_service.dart'; -import 'package:rules_of_living/src/Engine.dart'; @Component( selector: 'sim-controls', @@ -17,13 +15,12 @@ import 'package:rules_of_living/src/Engine.dart'; styleUrls: const ["controls_component.css"], ) class ControlsComponent { - final EngineService engineService; + final EngineService engine; - Engine get engine => engineService.engine; - ControlsComponent(this.engineService); + ControlsComponent(this.engine); void onStartClicked() { - engine.running = !engine.running; + engine.toggleRunning(); } void onStepClicked() { @@ -35,8 +32,7 @@ class ControlsComponent { } void onRandomClicked() { - engine.running = false; - engine.addPattern(); + engine.addRandomPattern(); } void onClearClicked() { diff --git a/lib/components/controls_component.html b/lib/components/controls_component.html index 30ddf55..42e79f7 100644 --- a/lib/components/controls_component.html +++ b/lib/components/controls_component.html @@ -1,7 +1,7 @@
- + diff --git a/lib/components/simulation_component.dart b/lib/components/simulation_component.dart index e64fa82..12b7844 100644 --- a/lib/components/simulation_component.dart +++ b/lib/components/simulation_component.dart @@ -8,7 +8,6 @@ import 'package:rules_of_living/service/engine_service.dart'; templateUrl: "simulation_component.html", directives: [coreDirectives], providers: [], -// styleUrls: const ['package:angular_components/app_layout/layout.scss.css'], ) class SimulationComponent implements OnInit { final EngineService engineService; @@ -26,16 +25,10 @@ class SimulationComponent implements OnInit { canvas.context2D.fillRect(0, 0, canvas.width, canvas.height); canvas.context2D.setFillColorRgb(0, 255, 0); canvas.context2D.fillText(''' - If you see this\n - the app is broken :( + If you see this + + the canvas did not load correctly :( ''', canvas.width / 2 - 50, canvas.height / 2); - engineService.create(canvas); - - html.window.animationFrame.then(animFrame); - } - - void animFrame(num now) { - engineService.engine.process(now); - html.window.animationFrame.then(animFrame); + engineService.canvas = canvas; } } diff --git a/lib/service/engine_service.dart b/lib/service/engine_service.dart index 5107f3c..d7b0bed 100644 --- a/lib/service/engine_service.dart +++ b/lib/service/engine_service.dart @@ -5,7 +5,45 @@ import 'package:rules_of_living/src/Engine.dart'; class EngineService { Engine _engine; - Engine get engine => _engine; + Engine get engine => _engine ?? createEngine(null); + + Engine createEngine(html.CanvasElement canvas) { + _engine = Engine(canvas); + return _engine; + } + + void set canvas(html.CanvasElement canvas) => engine.canvas = canvas; + html.CanvasElement get canvas => engine.canvas; + + void run() { + engine.running = true; + } + + void stop() { + engine.running = false; + } + + void toggleRunning() { + engine.running = !engine.running; + } + + void step() { + engine.step(); + } + + void reset() { + engine.reset(); + } + + void addRandomPattern() { + engine.running = false; + engine.addPattern(); + } + + void clear() { + engine.clear(); + } + + bool get isRunning => engine.running; - void create(html.CanvasElement canvas) => _engine = Engine(canvas); } diff --git a/lib/src/Engine.dart b/lib/src/Engine.dart index 6f7ff16..539e6d2 100644 --- a/lib/src/Engine.dart +++ b/lib/src/Engine.dart @@ -6,7 +6,7 @@ class Engine { // Elapsed Time Counter - useful for Safety Timeout Stopwatch _elapsed = new Stopwatch(); - // Game Tick Rate - *does* impact game speed + // Game Tick Rate - *does* impact game speed TODO add configurable option int _MS_PER_STEP = 1000 ~/ 3; // Max Frame (i.e. Rendering) rate - does *not* impact game speed @@ -15,16 +15,31 @@ class Engine { // ms stuck in updateloop after which game will declare itself unresponsive final int SAFETY_TIMEOUT = 2000; + // Grid Size TODO add as configurable option + static final GRID_X = 100; + static final GRID_Y = 100; + num _updateLag = 0.0; num _drawLag = 0.0; - final html.CanvasElement canvas; - Grid _grid = new Grid(100, 100); - bool _running = false; + /// The Canvas to Paint on + /// + /// 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 + /// rendered later. + html.CanvasElement canvas; + Grid _grid = new Grid(GRID_X, GRID_Y); + bool running = false; - Engine(this.canvas) { + Engine([this.canvas]) { _elapsed.start(); _grid.addPattern(amount: 15, dispersal: 5); + html.window.animationFrame.then(animFrame); + } + + void animFrame(num now) { + process(now); + html.window.animationFrame.then(animFrame); } void reset() { @@ -33,7 +48,7 @@ class Engine { } void clear() { - _grid = new Grid(100, 100); + _grid = new Grid(GRID_X, GRID_Y); running = false; } @@ -58,19 +73,32 @@ class Engine { } } + /// Update Engine Logic + /// + /// Updates the logic of the engine by one tick. Should usually not be called + /// directly, since it is automatically taken care of by the processing function. + /// If simulation should be advanced manually one time, prefer using step(). void update() { -// print("updating"); if (!_grid.update()) running = false; } + /// Advances Logic One Update + /// + /// Moves logic of the engine one step forward and pauses the + /// simulation. Does not automatically re-render the new state + /// (though this should usually not pose a problem). void step() { running = false; _grid.update(); } + /// Renders the Current Simulation State + /// + /// Renders the simulation once. Will usually automatically be called by + /// the internal engine processing. Does not do anything if no canvas is + /// defined. void render([num interp]) { -// print("rendering"); - _grid.render(canvas, interp); + if(canvas != null) _grid.render(canvas, interp); } void addPattern( @@ -92,7 +120,4 @@ class Engine { void toggleEdgeRendering() { _grid.renderEdges = !_grid.renderEdges; } - - void set running(bool on) => _running = on; - bool get running => _running; } diff --git a/test/app_test.dart b/test/app_test_disable.dart similarity index 100% rename from test/app_test.dart rename to test/app_test_disable.dart diff --git a/test/service/engine_service_test.dart b/test/service/engine_service_test.dart new file mode 100644 index 0000000..9d4a226 --- /dev/null +++ b/test/service/engine_service_test.dart @@ -0,0 +1,19 @@ +@TestOn('browser') + +import 'package:test/test.dart'; +import 'package:rules_of_living/service/engine_service.dart'; + +void main() { + EngineService sut; + setUp(() { + sut = EngineService(); + }); + + test("EngineService creates an engine on demand", () { + expect(sut.engine, isNotNull); + }); + + test("EngineService returns the cached engine on subsequent requests", () { + expect(sut.engine, allOf(isNotNull, equals(sut.engine))); + }); +} diff --git a/test/src/engine_test.dart b/test/src/engine_test.dart new file mode 100644 index 0000000..0712ba8 --- /dev/null +++ b/test/src/engine_test.dart @@ -0,0 +1,32 @@ +import 'dart:html' as html; + +@TestOn('browser') + +import 'package:rules_of_living/src/Engine.dart'; +import 'package:test/test.dart'; + +void main() { + Engine sut; + setUp(() { + sut = Engine(); + }); + + test("Engine can be instantiated without canvas", () { + expect(sut, isNot(throwsNoSuchMethodError)); + }); + + test("Engine does not throw errors when calling render directly", () { + // anonymous function necessary since throws can not use functions with args + expect(() => sut.render(), isNot(throwsNoSuchMethodError)); + }); + + test("Engine does not throw errors when processing without attached canvas", () { + // anonymous function necessary since throws can not use functions with args + expect(() => sut.process(1000), isNot(throwsNoSuchMethodError)); + }); + + test("setCanvas allows setting a canvas for an engine at any point", () { + sut.canvas = new html.CanvasElement(); + expect(sut.canvas, isNotNull); + }); +} \ No newline at end of file