diff --git a/src/main/js/Main.ts b/src/main/js/Main.ts index b15f7d4..804ed0e 100644 --- a/src/main/js/Main.ts +++ b/src/main/js/Main.ts @@ -53,6 +53,16 @@ class LineSegment { } + /** + * Returns the angle formed by this `LineSegment` in radians. + * + * @return the angle formed by this `LineSegment` in radians + */ + angle(): number { + return Math.atan2(this.y, this.x); + } + + /** * Returns `true` if and only if this `LineSegment` is exactly the same as `other`. * @@ -117,6 +127,7 @@ const settings = { fillGoal: 0.3, stepTime: 250, stepsPerLevel: 5, + zoomSpeed: 1 / 30, }; @@ -131,41 +142,37 @@ doAfterLoad(() => { // Model + // TODO: Dynamic thickness calculation const lines: Line[] = [new Line(1), new Line(5)]; - let step = 0; - let lastStepTime = Date.now(); setInterval(() => { - const stepLine = Math.floor(step / (settings.stepsPerLevel ** 2)); - const inductionLine = stepLine + 1; - const superInductionLine = stepLine + 2; + const currentLevel = lines.findIndex(it => it.segments.length !== 25); - // Add new low-level segment - const length = settings.stepsPerLevel ** (2 + stepLine); - // TODO: Accumulate angles - const angle = (random_normal() - 0.5) * Math.PI * 2; - lines[stepLine].segments.push(new LineSegment(Math.cos(angle) * length, Math.sin(angle) * length)); + // Add low-level segment + const length = settings.stepsPerLevel ** (2 + currentLevel); + let angle: number; + if (lines[currentLevel].segments.length > 0) + angle = lines[currentLevel].segments.slice(-1)[0].angle() + (random_normal() - 0.5) * Math.PI * 2; + else + angle = Math.random() * Math.PI * 2; + lines[currentLevel].segments.push(new LineSegment(Math.cos(angle) * length, Math.sin(angle) * length)); - // Add first-level induction line - if ((step + 1) % settings.stepsPerLevel === 0) { - if (lines.length <= inductionLine) { - lines[inductionLine] = new Line(5 ** inductionLine); + // Add induction segments + for (const level of [1, 2]) { + const inductionLevel = currentLevel + level; + const lowerLines = lines[inductionLevel - 1]; + + if (lowerLines.segments.length !== 0 && lowerLines.segments.length % 5 === 0) { + if (lines.length <= inductionLevel) { + lines[inductionLevel] = new Line(5 ** inductionLevel); + } + + lines[inductionLevel].segments.push(lowerLines.sumSegmentsSlice(-settings.stepsPerLevel)); } - - lines[inductionLine].segments.push(lines[stepLine].sumSegmentsSlice(-settings.stepsPerLevel)); - } - - // Add second-level induction line - if ((step + 1) % (settings.stepsPerLevel ** 2) === 0) { - if (lines.length <= superInductionLine) { - lines[superInductionLine] = new Line(5 ** superInductionLine); - } - - lines[superInductionLine].segments.push(lines[inductionLine].sumSegmentsSlice(-settings.stepsPerLevel)); } + // Update shared info step++; - lastStepTime = Date.now(); }, settings.stepTime); @@ -179,8 +186,7 @@ doAfterLoad(() => { // Draw - const startTime = Date.now(); - let maxX = 0; + let maxPoint = new Point(0, 0); let zoomFactor = 1; const draw = () => { ctx.restore(); @@ -201,8 +207,12 @@ doAfterLoad(() => { ctx.stroke(); // Zoom - if ((1 / zoomFactor) * maxX > canvas.width * settings.fillGoal) { - zoomFactor *= (((1 / zoomFactor) * maxX) / (canvas.width * settings.fillGoal)) ** (1 / 60); + const excessFactor = Math.max( + ((1 / zoomFactor) * maxPoint.x) / (canvas.width * settings.fillGoal), + ((1 / zoomFactor) * maxPoint.y) / (canvas.height * settings.fillGoal) + ); + if (excessFactor > 1) { + zoomFactor *= excessFactor ** settings.zoomSpeed; } ctx.scale(1 / zoomFactor, 1 / zoomFactor); @@ -218,7 +228,10 @@ doAfterLoad(() => { lastPos = new Point(lastPos.x + segment.x, lastPos.y + segment.y); ctx.lineTo(lastPos.x, lastPos.y); - maxX = Math.max(maxX, lastPos.x); + maxPoint = new Point( + Math.max(maxPoint.x, Math.abs(lastPos.x)), + Math.max(maxPoint.y, Math.abs(lastPos.y)) + ); } ctx.stroke();