UNPKG

@rgsoft/turtle

Version:

Turtle graphics and L-Systems library

228 lines (223 loc) 7.13 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { LSystem: () => LSystem, Rule: () => Rule, Turtle: () => Turtle }); module.exports = __toCommonJS(src_exports); // src/types.ts var alphabetSymbols = ["F", "G", "H", "L", "M", "D", "U", "+", "-", "[", "]"]; var isValidAlphabetSymbol = (chr) => alphabetSymbols.includes(chr); // src/rule.ts var Rule = class { constructor(rule) { const matches = rule.replace(/\s/g, "").match(/^(.)(-|=)>(.+)$/); if (!matches) { throw new Error("Unrecognized rule pattern"); } const base = matches[1]; const next = matches[3]; if (!isValidAlphabetSymbol(base)) { throw new Error(`Symbol '${base}' not recognized in condition`); } this._base = base; this._next = []; next.split("").forEach((c) => { if (!isValidAlphabetSymbol(c)) { throw new Error(`Symbol '${c}' not recognized in predicate`); } this._next.push(c); }); } get base() { return this._base; } get next() { return this._next; } get nextString() { return this._next.join(""); } toString() { return `${this.base} => ${this._next.join("")}`; } }; // src/lsystem.ts var LSystem = class { constructor(axiom, rules) { this._axiom = ""; this._sentence = ""; this._generation = 0; this.axiom = axiom; this._rules = rules.map((rule) => typeof rule === "string" ? new Rule(rule) : rule); this.init(); } init() { this._sentence = this._axiom; this._generation = 0; } /** * * @param { number } n */ generate(n = 1) { let nextGen; for (let i = 0; i < n; i++) { nextGen = ""; this._sentence.split("").forEach((c) => { const rule = this._rules.find((r) => r.base === c); nextGen = nextGen.concat(rule === void 0 ? c : rule.nextString); }); this._sentence = nextGen; this._generation++; } } get sentence() { return this._sentence; } get generation() { return this._generation; } get rules() { return [...this._rules]; } set axiom(val) { val.split("").forEach((c) => { if (!isValidAlphabetSymbol(c)) { throw new Error(`Symbol '${c}' not recognized in axiom`); } }); this._axiom = val; this.init(); } get axiom() { return this._axiom; } }; // src/turtle.ts var Turtle = class { constructor(config) { this.config = config; /** * @type { number } */ this.currentScale = 1; this.alphabetMap = { F: () => { const ctx = this.config.context; const lineWith = this.config.scale.scaleStrokeWeight ? this.config.strokeWeight * this.currentScale : this.config.strokeWeight; const lineSize = this.config.scale.scaleStrokeSize ? this.config.strokeSize * this.currentScale : this.config.strokeSize; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineCap = "round"; ctx.lineWidth = lineWith; ctx.lineTo(lineSize, 0); ctx.stroke(); ctx.translate(lineSize, 0); }, G: () => { const ctx = this.config.context; const lineWith = this.config.scale.scaleStrokeWeight ? this.config.strokeWeight * this.currentScale : this.config.strokeWeight; const lineSize = this.config.scale.scaleStrokeSize ? this.config.strokeSize * this.currentScale : this.config.strokeSize; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineCap = "round"; ctx.lineWidth = lineWith; ctx.lineTo(lineSize, 0); ctx.stroke(); ctx.translate(lineSize, 0); }, H: () => { const ctx = this.config.context; const lineWith = this.config.scale.scaleStrokeWeight ? this.config.strokeWeight * this.currentScale : this.config.strokeWeight; const lineSize = this.config.scale.scaleStrokeSize ? this.config.strokeSize * this.currentScale : this.config.strokeSize; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineCap = "round"; ctx.lineWidth = lineWith; ctx.lineTo(lineSize, 0); ctx.stroke(); ctx.translate(lineSize, 0); }, L: () => { const ctx = this.config.context; const lineWith = this.config.scale.scaleStrokeWeight ? this.config.strokeWeight * this.currentScale : this.config.strokeWeight; const lineSize = this.config.scale.scaleStrokeSize ? this.config.strokeSize * this.currentScale : this.config.strokeSize; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineCap = "round"; ctx.lineWidth = lineWith; ctx.arc(lineSize * 0.5, lineSize * 0.5, lineSize * Math.SQRT1_2, Math.PI * 1.25, Math.PI * 1.75); ctx.arc(lineSize * 0.5, -lineSize * 0.5, lineSize * Math.SQRT1_2, Math.PI * 0.25, Math.PI * 0.75); ctx.stroke(); }, M: () => { const lineSize = this.config.scale.scaleStrokeSize ? this.config.strokeSize * this.currentScale : this.config.strokeSize; this.config.context.translate(lineSize, 0); }, D: () => this.currentScale *= this.config.scale.ratio, U: () => this.currentScale /= this.config.scale.ratio, "+": () => { const angle = this.config.scale.scaleAngle ? this.config.angle * this.currentScale : this.config.angle; this.config.context.rotate(angle); }, "-": () => { const angle = this.config.scale.scaleAngle ? this.config.angle * this.currentScale : this.config.angle; this.config.context.rotate(-angle); }, "[": () => this.config.context.save(), "]": () => this.config.context.restore() }; } /** * * @param { string } sentence */ render(sentence) { for (let i = 0; i < sentence.length; i++) { const chr = sentence[i]; const fn = this.alphabetMap[chr]; if (typeof fn === "function") { fn.call(this); } } } /** * * @param { number } x * @param { number } y * @param { number } angle */ init(x, y, angle) { this.config.context.resetTransform(); this.config.context.translate(x, y); this.config.context.rotate(angle); this.currentScale = 1; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { LSystem, Rule, Turtle }); //# sourceMappingURL=index.js.map