// Copyright 2016 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
// www.source-code.biz, www.inventec.ch/chdh
//
// License: GPL, GNU General Public License, http://www.gnu.org/licenses/gpl.html
// Home page: http://www.source-code.biz/snippets/typescript/patterns
///
///
// Pattern generator for dots within a two-dimensional square grid.
namespace SquareGridPatternGenerator {
import BitSpace = Utils.BitSpace;
import makeSmi = Utils.makeSmi;
import performanceNow = Polyfills.performanceNow;
import Point = Utils.Point;
import SeedableRandom = Utils.SeedableRandom;
import windowCancelAnimationFrame = Polyfills.windowCancelAnimationFrame;
import windowRequestAnimationFrame = Polyfills.windowRequestAnimationFrame;
//------------------------------------------------------------------------------
export interface Finder {
findNextPoint (p: Point) : Point | null; }
//------------------------------------------------------------------------------
export class PatternGenerator {
private bitSpace: BitSpace;
private canvas: HTMLCanvasElement;
private finder: Finder;
private ctx: CanvasRenderingContext2D;
private width: number;
private height: number;
private random: SeedableRandom;
private active: boolean;
private timerId: number | null;
private currentPoint: Point | null;
constructor (bitSpace: BitSpace, finder: Finder, canvas: HTMLCanvasElement) {
this.bitSpace = bitSpace;
this.canvas = canvas;
this.finder = finder;
this.width = makeSmi(bitSpace.width);
this.height = makeSmi(bitSpace.height);
if (this.width != canvas.width || this.height != canvas.height) {
throw new Error(); }
let ctx = canvas.getContext("2d");
if (ctx == null) {
throw new Error("Canvas 2D context not available."); }
this.ctx = ctx;
this.random = new SeedableRandom();
this.currentPoint = {x: makeSmi(Math.floor(this.width / 2)), y: makeSmi(Math.floor(this.height / 2))};
this.clearCanvas(); }
public setRandomPoints (n: number) {
let freePoints = this.bitSpace.totalCells - this.bitSpace.setCells;
let n2 = Math.min(n, freePoints);
for (let i = 0; i < n2; i++) {
let p = this.getFreeRandomPoint();
if (p == null) {
throw new Error(); }
this.setPoint(p); }}
public start() {
this.active = true;
this.startTimer(); }
public stop() {
this.active = false;
this.stopTimer(); }
private startTimer() {
if (this.timerId != null) {
return; }
this.timerId = windowRequestAnimationFrame(() => this.timerEvent()); }
private stopTimer() {
if (this.timerId == null) {
return; }
windowCancelAnimationFrame(this.timerId);
this.timerId = null; }
private timerEvent() {
try {
this.timerEvent2(); }
catch (e) {
this.stop();
alert("Error: " + e);
throw e; }}
private timerEvent2() {
this.timerId = null;
if (!this.active) {
return; }
let startTime : number = performanceNow();
while (this.active) {
this.setNextPoint();
if (performanceNow() - startTime > 16) {
break; }}
this.checkAutoStop();
if (this.active) {
this.startTimer(); }}
private checkAutoStop() {
if (this.bitSpace.setCells >= this.bitSpace.totalCells) {
this.stop(); }}
private setNextPoint() {
let p0: Point = this.currentPoint || this.getRandomPoint();
let p: Point | null = this.finder.findNextPoint(p0);
if (p != null) {
this.setPoint(p); }
this.currentPoint = p; }
private setPoint (p: Point) {
this.setPixel(p.x, p.y);
this.bitSpace.setCell(p.x, p.y); }
private getRandomPoint() : Point {
return new Point(this.random.getInt(this.width), this.random.getInt(this.height)); }
private getFreeRandomPoint() : Point | null {
if (this.bitSpace.setCells >= this.bitSpace.totalCells) {
return null; }
let n = this.width * this.height * 100;
for (let i = 0; i < n; i++) {
let x = this.random.getInt(this.width);
let y = this.random.getInt(this.height);
if (!this.bitSpace.getCell(x, y)) {
return new Point(x, y); }}
throw new Error("Bad random number generator or program logic error."); }
private clearCanvas() {
// this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.fillStyle = "#fff";
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); }
private setPixel (x: number, y: number) {
this.ctx.fillStyle = "#000";
this.ctx.fillRect(x, y, 1, 1); }}
} // end namespace SquareGridPatternGenerator