### Kaleidoscope ðŸ«¶

Only a triangular section of the turtle is 'writeable'. The contents of that triangle is then rotated and mirrored in the way a Kaleidoscope does.

```const mirrors = 4; //min=2 max=50 step=1
const circleChance = .75; //min=0 max=1 step=.01

// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(-.7);

// Global code will be evaluated once.
const turtle = new Kaleidoscope(0, 0, mirrors);

function rot2(a) { return [Math.cos(a), -Math.sin(a), Math.sin(a), Math.cos(a)]; }
function trans2(m, a) { return [m[0]*a[0]+m[2]*a[1], m[1]*a[0]+m[3]*a[1]]; }

// The walk function will be called until it returns false.
function walk(i) {
if(i % 20 == 0) {
const newY = Math.random() * 50;
turtle.jump(trans2(rot2(Math.PI / mirrors), [
0,
Math.random() * 50
]));
}
if(Math.random() < circleChance) {
turtle.circle(Math.random() * 30);
} else {
turtle.forward(Math.random() * 50);
turtle.right(Math.random() * 360);
}
return i < 100;
}

function Kaleidoscope(x, y, mirrors = 4) {

function rot2(a) { return [Math.cos(a), -Math.sin(a), Math.sin(a), Math.cos(a)]; }
function trans2(m, a) { return [m[0]*a[0]+m[2]*a[1], m[1]*a[0]+m[3]*a[1]]; }
function segment_intersect2(a,b,d,c) {
const e=(c[1]-d[1])*(b[0]-a[0])-(c[0]-d[0])*(b[1]-a[1]);
if(0==e)return false;
c=((c[0]-d[0])*(a[1]-d[1])-(c[1]-d[1])*(a[0]-d[0]))/e;
d=((b[0]-a[0])*(a[1]-d[1])-(b[1]-a[1])*(a[0]-d[0]))/e;
return 0<=c&&1>=c&&0<=d&&1>=d?[a[0]+c*(b[0]-a[0]),a[1]+c*(b[1]-a[1])]:false;
}

function pointInTriangle(pt, ...v) {
const signFn = (p1, p2, p3) => (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
const signs = v.map((v, i, arr) => signFn(pt, v, arr[(i+1)%arr.length]));
return !(signs.reduce((p, c) => p || c < 0, false) && signs.reduce((p, c) => p || c > 0, false));
}

class Kaleidoscope extends Turtle {
constructor(x, y, n = 4) {
super(x, y);

this.boundaries = [[0,0], [0, 1000], trans2(rot2(2*Math.PI/(n*2)), [0,1000])];

const rotations = Array.from({length: n}).map((v, i) => {
return (pt) => trans2(rot2(i * 2 * Math.PI / n), pt);
});
const mirrors = rotations.map(r => (pt) => trans2([-1, 0, 0, 1], r(pt)));

this.transforms = [...rotations, ...mirrors];

this.cur = this.pos();
this.curIn = this.inSegment(this.cur);

this.turtle = new Turtle(x, y);
}
inSegment(pt) {
return pointInTriangle(pt, ...this.boundaries);
}
goto(x, y) {
const target = Array.isArray(x)? x: [x, y];
const isDown = this.isdown();

const targetIn = this.turtle === undefined? false: this.inSegment(target);
if (isDown) {
(() => {
const bOne = segment_intersect2(this.cur, target, this.boundaries[0], this.boundaries[1]);
const bTwo = segment_intersect2(this.cur, target, this.boundaries[0], this.boundaries[2]);
debugger;
let from = this.cur;
let to = target;
if(!this.curIn && !targetIn) {
if(bOne === false && bTwo === false) return;
from = bOne;
to = bTwo;
} else if(!this.curIn) {
from = bOne === false? bTwo: bOne;
} else if(!targetIn) {
to = bOne === false? bTwo: bOne;
}
this.transforms.forEach(t => {
this.turtle.jump(t(from));
this.turtle.goto(t(to));
});
})();
}

this.cur = target;
this.curIn = targetIn

this.up();
super.goto(x, y);
if(isDown) this.down();
}
}
return new Kaleidoscope(x, y, mirrors)
}
```