Add zooming in both directions
This commit is contained in:
parent
c5969362ad
commit
c5e9d763ce
|
@ -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`.
|
* Returns `true` if and only if this `LineSegment` is exactly the same as `other`.
|
||||||
*
|
*
|
||||||
|
@ -117,6 +127,7 @@ const settings = {
|
||||||
fillGoal: 0.3,
|
fillGoal: 0.3,
|
||||||
stepTime: 250,
|
stepTime: 250,
|
||||||
stepsPerLevel: 5,
|
stepsPerLevel: 5,
|
||||||
|
zoomSpeed: 1 / 30,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,41 +142,37 @@ doAfterLoad(() => {
|
||||||
|
|
||||||
|
|
||||||
// Model
|
// Model
|
||||||
|
// TODO: Dynamic thickness calculation
|
||||||
const lines: Line[] = [new Line(1), new Line(5)];
|
const lines: Line[] = [new Line(1), new Line(5)];
|
||||||
|
|
||||||
let step = 0;
|
let step = 0;
|
||||||
let lastStepTime = Date.now();
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
const stepLine = Math.floor(step / (settings.stepsPerLevel ** 2));
|
const currentLevel = lines.findIndex(it => it.segments.length !== 25);
|
||||||
const inductionLine = stepLine + 1;
|
|
||||||
const superInductionLine = stepLine + 2;
|
|
||||||
|
|
||||||
// Add new low-level segment
|
// Add low-level segment
|
||||||
const length = settings.stepsPerLevel ** (2 + stepLine);
|
const length = settings.stepsPerLevel ** (2 + currentLevel);
|
||||||
// TODO: Accumulate angles
|
let angle: number;
|
||||||
const angle = (random_normal() - 0.5) * Math.PI * 2;
|
if (lines[currentLevel].segments.length > 0)
|
||||||
lines[stepLine].segments.push(new LineSegment(Math.cos(angle) * length, Math.sin(angle) * length));
|
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
|
// Add induction segments
|
||||||
if ((step + 1) % settings.stepsPerLevel === 0) {
|
for (const level of [1, 2]) {
|
||||||
if (lines.length <= inductionLine) {
|
const inductionLevel = currentLevel + level;
|
||||||
lines[inductionLine] = new Line(5 ** inductionLine);
|
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++;
|
step++;
|
||||||
lastStepTime = Date.now();
|
|
||||||
}, settings.stepTime);
|
}, settings.stepTime);
|
||||||
|
|
||||||
|
|
||||||
|
@ -179,8 +186,7 @@ doAfterLoad(() => {
|
||||||
|
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
const startTime = Date.now();
|
let maxPoint = new Point(0, 0);
|
||||||
let maxX = 0;
|
|
||||||
let zoomFactor = 1;
|
let zoomFactor = 1;
|
||||||
const draw = () => {
|
const draw = () => {
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
@ -201,8 +207,12 @@ doAfterLoad(() => {
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
// Zoom
|
// Zoom
|
||||||
if ((1 / zoomFactor) * maxX > canvas.width * settings.fillGoal) {
|
const excessFactor = Math.max(
|
||||||
zoomFactor *= (((1 / zoomFactor) * maxX) / (canvas.width * settings.fillGoal)) ** (1 / 60);
|
((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);
|
ctx.scale(1 / zoomFactor, 1 / zoomFactor);
|
||||||
|
|
||||||
|
@ -218,7 +228,10 @@ doAfterLoad(() => {
|
||||||
lastPos = new Point(lastPos.x + segment.x, lastPos.y + segment.y);
|
lastPos = new Point(lastPos.x + segment.x, lastPos.y + segment.y);
|
||||||
ctx.lineTo(lastPos.x, lastPos.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();
|
ctx.stroke();
|
||||||
|
|
Loading…
Reference in New Issue