Merge branch '31-allow-engine-to-be-created-without-the-need-for-a-canvas' into 'master'
Resolve "Allow Engine to be created without the need for a canvas" Closes #31 See merge request marty.oehme/cellular-automata!8
This commit is contained in:
commit
7d5b1cfe30
8 changed files with 137 additions and 34 deletions
|
@ -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() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div id="controls">
|
||||
<material-button id="reset" (click)="onResetClicked()"><material-icon icon="replay" baseline></material-icon></material-button>
|
||||
<material-button id="run" (click)="onStartClicked()">
|
||||
<span [ngSwitch]="engine.running">
|
||||
<span [ngSwitch]="engine.isRunning">
|
||||
<material-icon *ngSwitchCase="false" icon="play_arrow" baseline></material-icon>
|
||||
<material-icon *ngSwitchCase="true" icon="pause" baseline></material-icon>
|
||||
</span>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
19
test/service/engine_service_test.dart
Normal file
19
test/service/engine_service_test.dart
Normal file
|
@ -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)));
|
||||
});
|
||||
}
|
32
test/src/engine_test.dart
Normal file
32
test/src/engine_test.dart
Normal file
|
@ -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);
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue