// 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