@rgsoft/turtle
Version:
Turtle graphics and L-Systems library
1 lines • 12.9 kB
Source Map (JSON)
{"version":3,"sources":["../src/types.ts","../src/rule.ts","../src/lsystem.ts","../src/turtle.ts"],"sourcesContent":["export const alphabetSymbols = ['F', 'G', 'H', 'L', 'M', 'D', 'U', '+', '-', '[', ']'] as const;\r\nexport type AlphabetSymbol = typeof alphabetSymbols[number];\r\n\r\nexport const isValidAlphabetSymbol = (chr: string) => alphabetSymbols.includes(chr as AlphabetSymbol);\r\n","import { AlphabetSymbol, isValidAlphabetSymbol } from \"./types\";\r\n\r\nexport class Rule {\r\n \r\n private _base: AlphabetSymbol;\r\n\r\n private _next: AlphabetSymbol[];\r\n\r\n constructor(rule: string) {\r\n const matches = rule.replace(/\\s/g, '').match(/^(.)(-|=)>(.+)$/);\r\n if (!matches) {\r\n throw new Error('Unrecognized rule pattern');\r\n }\r\n const base = matches[1] as AlphabetSymbol;\r\n const next = matches[3];\r\n if (!isValidAlphabetSymbol(base)) {\r\n throw new Error(`Symbol '${base}' not recognized in condition`);\r\n } \r\n this._base = base;\r\n this._next = [];\r\n next.split('').forEach(c => {\r\n if (!isValidAlphabetSymbol(c)) {\r\n throw new Error(`Symbol '${c}' not recognized in predicate`);\r\n }\r\n this._next.push(c as AlphabetSymbol);\r\n });\r\n }\r\n\r\n get base(): AlphabetSymbol {\r\n return this._base;\r\n }\r\n\r\n get next(): AlphabetSymbol[] {\r\n return this._next;\r\n }\r\n \r\n get nextString(): string {\r\n return this._next.join('');\r\n }\r\n\r\n public toString(): string {\r\n return `${this.base} => ${this._next.join('')}`;\r\n }\r\n};","import { Rule } from \"./rule\";\r\nimport { isValidAlphabetSymbol } from \"./types\";\r\n\r\nexport class LSystem {\r\n\r\n private _axiom: string = '';\r\n private _rules: Rule[];\r\n private _sentence: string = '';\r\n private _generation: number = 0;\r\n\r\n constructor (axiom: string, rules: (string | Rule)[]) {\r\n this.axiom = axiom;\r\n this._rules = rules.map(rule => typeof rule === 'string' ? new Rule(rule) : rule);\r\n this.init();\r\n }\r\n\r\n private init() {\r\n this._sentence = this._axiom;\r\n this._generation = 0;\r\n }\r\n\r\n /**\r\n * \r\n * @param { number } n \r\n */\r\n generate(n: number = 1): void {\r\n let nextGen: string;\r\n\r\n for (let i = 0; i < n; i++) {\r\n nextGen = '';\r\n this._sentence.split('').forEach(c => {\r\n const rule = this._rules.find(r => r.base === c);\r\n nextGen = nextGen.concat(rule === undefined ? c : rule.nextString);\r\n });\r\n this._sentence = nextGen;\r\n this._generation++;\r\n }\r\n }\r\n\r\n get sentence(): string {\r\n return this._sentence;\r\n }\r\n\r\n get generation(): number {\r\n return this._generation;\r\n }\r\n\r\n get rules(): Rule[] {\r\n return [ ...this._rules ];\r\n }\r\n\r\n set axiom(val: string) {\r\n val.split('').forEach((c) => {\r\n if (!isValidAlphabetSymbol(c)) {\r\n throw new Error(`Symbol '${c}' not recognized in axiom`);\r\n }\r\n });\r\n this._axiom = val;\r\n this.init();\r\n }\r\n\r\n get axiom(): string {\r\n return this._axiom;\r\n }\r\n}\r\n","import { AlphabetSymbol } from \"./types\";\r\n\r\nexport interface ScaleConfig {\r\n ratio: number,\r\n scaleStrokeSize?: boolean,\r\n scaleStrokeWeight?: boolean,\r\n scaleAngle?: boolean,\r\n};\r\n\r\nexport interface TurtleConfig {\r\n context: CanvasRenderingContext2D,\r\n strokeSize: number,\r\n strokeWeight: number,\r\n angle: number,\r\n scale: ScaleConfig,\r\n};\r\n\r\nexport class Turtle {\r\n\r\n /**\r\n * @type { number }\r\n */\r\n private currentScale: number = 1\r\n\r\n private readonly alphabetMap: Record<AlphabetSymbol, () => void> = {\r\n F: () => {\r\n const ctx = this.config.context;\r\n const lineWith = this.config.scale.scaleStrokeWeight ? this.config.strokeWeight * this.currentScale : this.config.strokeWeight;\r\n const lineSize = this.config.scale.scaleStrokeSize ? this.config.strokeSize * this.currentScale : this.config.strokeSize;\r\n ctx.beginPath();\r\n ctx.moveTo(0, 0);\r\n ctx.lineCap = 'round';\r\n ctx.lineWidth = lineWith;\r\n ctx.lineTo(lineSize, 0);\r\n ctx.stroke();\r\n ctx.translate(lineSize, 0);\r\n },\r\n G: () => {\r\n const ctx = this.config.context;\r\n const lineWith = this.config.scale.scaleStrokeWeight ? this.config.strokeWeight * this.currentScale : this.config.strokeWeight;\r\n const lineSize = this.config.scale.scaleStrokeSize ? this.config.strokeSize * this.currentScale : this.config.strokeSize;\r\n ctx.beginPath();\r\n ctx.moveTo(0, 0);\r\n ctx.lineCap = 'round';\r\n ctx.lineWidth = lineWith;\r\n ctx.lineTo(lineSize, 0);\r\n ctx.stroke();\r\n ctx.translate(lineSize, 0);\r\n },\r\n H: () => {\r\n const ctx = this.config.context;\r\n const lineWith = this.config.scale.scaleStrokeWeight ? this.config.strokeWeight * this.currentScale : this.config.strokeWeight;\r\n const lineSize = this.config.scale.scaleStrokeSize ? this.config.strokeSize * this.currentScale : this.config.strokeSize;\r\n ctx.beginPath();\r\n ctx.moveTo(0, 0);\r\n ctx.lineCap = 'round';\r\n ctx.lineWidth = lineWith;\r\n ctx.lineTo(lineSize, 0);\r\n ctx.stroke();\r\n ctx.translate(lineSize, 0);\r\n },\r\n L: () => {\r\n const ctx = this.config.context;\r\n const lineWith = this.config.scale.scaleStrokeWeight ? this.config.strokeWeight * this.currentScale : this.config.strokeWeight;\r\n const lineSize = this.config.scale.scaleStrokeSize ? this.config.strokeSize * this.currentScale : this.config.strokeSize;\r\n ctx.beginPath();\r\n ctx.moveTo(0, 0);\r\n ctx.lineCap = 'round';\r\n ctx.lineWidth = lineWith;\r\n ctx.arc(lineSize * 0.5, lineSize * 0.5, lineSize * Math.SQRT1_2, Math.PI * 1.25, Math.PI * 1.75);\r\n ctx.arc(lineSize * 0.5, -lineSize * 0.5, lineSize * Math.SQRT1_2, Math.PI * 0.25, Math.PI * 0.75);\r\n ctx.stroke();\r\n },\r\n M: () => {\r\n const lineSize = this.config.scale.scaleStrokeSize ? this.config.strokeSize * this.currentScale : this.config.strokeSize;\r\n this.config.context.translate(lineSize, 0);\r\n },\r\n D: () => this.currentScale *= this.config.scale.ratio,\r\n U: () => this.currentScale /= this.config.scale.ratio,\r\n \"+\": () => {\r\n const angle = this.config.scale.scaleAngle ? this.config.angle * this.currentScale : this.config.angle;\r\n this.config.context.rotate(angle);\r\n },\r\n \"-\": () => {\r\n const angle = this.config.scale.scaleAngle ? this.config.angle * this.currentScale : this.config.angle;\r\n this.config.context.rotate(-angle);\r\n },\r\n \"[\": () => this.config.context.save(),\r\n \"]\": () => this.config.context.restore()\r\n };\r\n\r\n \r\n constructor(public config: TurtleConfig) {\r\n }\r\n\r\n /**\r\n * \r\n * @param { string } sentence \r\n */\r\n render(sentence: string) {\r\n for (let i = 0; i < sentence.length; i++) {\r\n const chr = sentence[i] as AlphabetSymbol;\r\n const fn = this.alphabetMap[chr];\r\n if (typeof fn === 'function') {\r\n fn.call(this);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * \r\n * @param { number } x \r\n * @param { number } y \r\n * @param { number } angle \r\n */\r\n init(x: number, y: number, angle: number) {\r\n this.config.context.resetTransform();\r\n this.config.context.translate(x, y);\r\n this.config.context.rotate(angle);\r\n this.currentScale = 1;\r\n }\r\n}"],"mappings":"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","names":[]}