Skip to content

Instantly share code, notes, and snippets.

@lostintangent
Created February 12, 2020 00:08
Show Gist options
  • Save lostintangent/429b1c092452da95276e1e28b18635e0 to your computer and use it in GitHub Desktop.
Save lostintangent/429b1c092452da95276e1e28b18635e0 to your computer and use it in GitHub Desktop.
Twitter Kirby SVG Animation
<div class="wrapper">
<svg class="frame" viewBox="0 0 530 380">
<defs>
<filter id="blackOutlineEffect">
<feMorphology in="SourceAlpha" result="MORPH" operator="dilate" radius="2" />
<feColorMatrix in="MORPH" result="WHITENED" type="matrix" values="0 0 0 0 0, 0 0 0 0 0, 0 0 0 0 0, 0 0 0 1 0"/>
<feMerge>
<feMergeNode in="WHITENED"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="blackOutlineEffectThin">
<feMorphology in="SourceAlpha" result="MORPH" operator="dilate" radius="2" />
<feColorMatrix in="MORPH" result="WHITENED" type="matrix" values="0 0 0 0 0, 0 0 0 0 0, 0 0 0 0 0, 0 0 0 1 0"/>
<feMerge>
<feMergeNode in="WHITENED"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="blackOutlineEffect2">
<feMorphology in="SourceAlpha" result="MORPH" operator="dilate" radius="2" />
<feColorMatrix in="MORPH"
result="WHITENED2"
type="matrix" values="0 0 0 0 0, 0 0 0 0 0, 0 0 0 0 0, 1 1 1 1 0"
/>
<feMerge>
<feMergeNode in="WHITENED2"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<g class="svg-out"></g>
</svg>
<div class="social-container">
<div class="social-message">
a pen by Luke Lincoln
<br/>
</div>
<div class="social-links">
<a href="https://codepen.io/lukes611" target="_blank">
<svg class="social" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 312.94 300.81"><title>codepen-logo</title><path d="M284.23,94.67,168.56,18.26s-8.78-12.11-26.18,0L27,95.8s-9.07,1.51-9.07,21.94v67.33S16.41,197.55,25.87,204s121,80.57,121,80.57,9.08,7.56,17.78,0l122.94-82.09s6.81.38,6.81-16.64v-73S299,104.5,284.23,94.67Zm-115.67-47L261,108l-41.17,28.12-51.22-34.65Zm-24.11,0v53.73L92.73,136.08,51.05,108.46ZM41,129.05l32.14,20.84L41,170.23ZM144.45,254.59l-93.4-62.77L92.73,164.2l51.72,33.64Zm11.6-75.83-42.23-28.87L156.05,121l42.13,30.09Zm12.51,75.83V199.35l51.72-35.15L261,192.32Zm101.93-84.36-28.62-19.59L270.49,132Z" style="fill:#1da1f2;stroke:#000;stroke-miterlimit:10;stroke-width:7px"/></svg>
</a>
<a href="https://twitter.com/Lukes611" target="_blank">
<svg class="social" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 312.94 300.81"><title>twtter-logo</title><path d="M17.76,235.94c24.65,16.18,76.06,37,137.66,19.94C217.1,238,278.84,178.27,275.25,87c0,0,21.72-16.64,29-30.28,0,0-21.69,9.22-33.18,8.93,0,0,19.72-10.84,25.24-31.83,0,0-14.68,9-37,13.81-3.69-5.52-29.89-27.53-63.24-15-27.58,8.69-43.22,41.09-37.14,68.52,0,0-70.77-.84-121.43-61.27,0,0-26.69,43.62,18.06,78.31-2.74-.05-11.23.77-26.41-7.14-.82,16.33,10.55,51.83,46.94,58-9.45,4.53-26.29,1.42-26.29,1.42s9.14,37,54.94,41.13C90.82,222.82,58.69,241.27,17.76,235.94Z" style="fill:#1da1f2;stroke:#000;stroke-miterlimit:10;stroke-width:7px"/></svg>
</a>
</div>
</div>
</div>
/**
* @param {LMat4} matrix
* @param {LV3} kirbyCenter
* @param {number} kirbyRadius
* @param {object} colors
*/
function makeBody (matrix, kirbyCenter, kirbyRadius, colors) {
let [p1, p2] = [
kirbyCenter.copy(),
kirbyCenter.add(new LV3(kirbyRadius, 0, 0)),
].map(x => matrix.multLV3(x));
const circleRadius = p1.dist(p2);
const circlePos = matrix.multLV3(kirbyCenter);
return {
point: matrix.multLV3(kirbyCenter),
string: `<circle cx="${circlePos.x}" cy="${circlePos.y}" r="${circleRadius}" style="fill:${colors.kirbyColor};stroke:${colors.outline};stroke-width:2;"/>`
};
}
/**
* @param {number} kirbyRadius
* @param {number} brimAngle
* @param {number} angleTowardsTop
*/
function computeHatPoint(kirbyRadius, brimAngle, angleTowardsTop) {
const p = new LV3(
Angle.cos(brimAngle),
0,
Angle.sin(brimAngle),
).scale(kirbyRadius * Angle.cos(angleTowardsTop));
const b = new LV3(0, -kirbyRadius, 0).scale(Angle.sin(angleTowardsTop));
return p.add(b);
}
/**
* @param {LMat4} matrix
* @param {Object} colors
* @param {number} kirbyRadius
* @param {number} headTiltAngle
* @param {number} opacity
*/
function makeHat(matrix, colors, kirbyRadius, headTiltAngle, opacity) {
const m = buildMatrix([
matrix,
LMat4.rotateX(20 + headTiltAngle),
]);
const pathz = [];
for (let k = 0; k < 36; k++) {
const brimAngle = k*10;
const brimAngle2 = (k*10+11) % 360;
for (let i = 20; i < 90; i+= 5) {
const angle = i;
const angle2 = i + 6;
const a = computeHatPoint(kirbyRadius, brimAngle, angle);
const b = computeHatPoint(kirbyRadius, brimAngle2, angle);
const c = computeHatPoint(kirbyRadius, brimAngle2, angle2);
const d = computeHatPoint(kirbyRadius, brimAngle, angle2);
const arr = [a,b,c,d].map(x => m.multLV3(x));
const n = arr[1].sub(arr[0]).unit().cross(arr[3].sub(arr[0]).unit());
if (n.dot(new LV3(0, 0, 1)) < 0) continue;
pathz.push(`<path d="${pointsToSvgPath(arr)}" style="fill:${withOpacity(colors.twitter, opacity)};stroke-width:0;"/>`);
}
}
const fullOpacity = opacity >= 0.9;
return {
point: m.multLV3(new LV3(0, 0, 0)), // <- this just works 🤷‍
string: `
${fullOpacity ? `<g filter="url(#blackOutlineEffectThin)">`:''}
${pathz.join('\n')}
${fullOpacity ?'</g>':''}
`,
};
}
/**
*
* @param {LMat4} matrix
* @param {Object} colors
* @param {number} kirbyRadius
* @param {number} headTiltAngle
* @param {boolean} [isLeft=false]
*/
function makeEye(matrix, colors, kirbyRadius, headTiltAngle, isLeft = false) {
const eyeSize = { w: 14, h: 23 };
const zeroVec = new LV3(0, 0, 0);
const fwdVec = new LV3(0, 0, 1);
const thisModelViewMatrix = buildMatrix([
LMat4.rotateX(20 + headTiltAngle),
LMat4.rotateY(14 * (isLeft ? -1 : 1)),
]);
const eyeLocationMatrix = buildMatrix([
matrix,
thisModelViewMatrix,
]);
function makeCirclePoints(scale = 1) {
return Array.from({ length: 37 }).map((_,v) => new LV3(
Angle.cos(v*10) * eyeSize.w * scale,
Angle.sin(v*10) * eyeSize.h * scale,
-60
)).map(p => vectorIntersectsSphere(zeroVec, kirbyRadius, p, fwdVec)).filter(x => x);
}
const mainPoint = new LV3(0, 0, kirbyRadius);
const eyeWhiteMatrix = buildMatrix([
eyeLocationMatrix,
LMat4.trans(0, -eyeSize.h/2, 0),
]);
const eyeBlueMatrix = buildMatrix([
eyeLocationMatrix,
LMat4.trans(0, eyeSize.h*1.2, 0),
]);
const eyeWhite = makeCirclePoints(1/2.2).map(x => eyeWhiteMatrix.multLV3(x));
const mainEyePoints = makeCirclePoints().map(p => eyeLocationMatrix.multLV3(p));
const eyeBlue = makeCirclePoints().map(p => eyeBlueMatrix.multLV3(p));
const clipId = `eye-${isLeft?'left':'right'}`;
return ({
point: eyeLocationMatrix.multLV3(mainPoint),
string: `
<clipPath id="${clipId}">
<path d="${pointsToSvgPath(mainEyePoints)}"/>
</clipPath>
<g clip-path="url(#${clipId})">
<path d="${pointsToSvgPath(mainEyePoints)}" style="stroke:none;fill:black"/>
<path d="${pointsToSvgPath(eyeWhite)}" style="fill:white"/>
<path d="${pointsToSvgPath(eyeBlue)}" style="fill:${colors.eye};"/>
</g>
<path d="${pointsToSvgPath(mainEyePoints)}" style="stroke-width:2;fill:none;stroke:black;"/>
`,
});
}
/**
* @param {LMat4} matrix
* @param {Object} colors
* @param {any} footTriangles
*/
function makeFoot(matrix, colors, footTriangles) {
const foot = footTriangles.map(f => f.map(v => {
return matrix.multLV3(v);
}));
return {
point: matrix.multLV3(new LV3(0,0,0)),
string: `
<g filter="url(#blackOutlineEffect)">
${foot.map(x => `<path d="${pointsToSvgPath(x)}" style="stroke-width:5;fill:${colors.foot}"/>`).join('\n')}
</g>
`,
};
}
/**
* @param {LMat4} matrix
* @param {LV3} rightV
* @param {LV3} realUp
* @param {number} angle
* @param {number} R
* @param {LV3} p
*/
function getArmPoint(matrix, rightV, realUp, angle, R, p) {
const cScalar = Angle.cos(angle) * R;
const sScalar = Angle.sin(angle) * R;
const p1 = rightV.scale(cScalar);
const p2 = realUp.scale(sScalar);
const x = p1.add(p2).add(p);
return matrix.multLV3(x);
}
/**
* @param {LMat4} matrix
* @param {Object} colors
* @param {number} kirbyRadius
* @param {number} headTiltAngle
* @param {boolean} [isRightArm = false]
*/
function makeArm(matrix, colors, kirbyRadius, headTiltAngle, isRightArm = false) {
const armScalar = isRightArm ? -1 : 1;
const armFullMatrix = buildMatrix([
matrix,
LMat4.rotateX(headTiltAngle),
LMat4.trans(kirbyRadius*-0.8 * armScalar, 0, 0),
LMat4.rotateZ(
50 * -armScalar
),
]);
const armFwd = new LV3(-1, 0, 0).scale(armScalar);
const realUp = new LV3(0, -1, 0).scale(armScalar);
const rightV = new LV3(0, 0, 1).scale(armScalar);
const origin = new LV3(0, 0, 0);
let optimalAngle = 0;
{
let bestSimilarity = -1000;
for (let angle = 0; angle < 360; angle++) {
const angleToTest = (angle + 90) % 360;
const p = getArmPoint(armFullMatrix, rightV, realUp, angleToTest, 1, origin).unit(); // optimise for angle
const similarity = vectorSimilarity(p, new LV3(0, 0, 1));
if (similarity > bestSimilarity) {
bestSimilarity = similarity;
optimalAngle = angle;
}
}
}
const MaxR = 25;
const NN = 60;
const goodArmPoints = [];
for (let i = 0; i <= NN; i++) {
const a = Math.cos(i/NN * Math.PI) * MaxR;
const b = Math.sin(i/NN * Math.PI) * 80;
const R = a;
const p = new LV3(10 * armScalar, 0, 0).add(armFwd.scale(b));
goodArmPoints.push(getArmPoint(armFullMatrix, rightV, realUp, optimalAngle, R, p));
}
return {
point: armFullMatrix.multLV3(new LV3(-kirbyRadius*0.8*armScalar, 0, 0)),
string: `<path d="${pointsToSvgPath(goodArmPoints)}" style="stroke:${colors.outline};stroke-width:2;fill:${colors.kirbyColor};"/>`,
};
}
/**
*
* @param {LMat4} matrix
* @param {Object} colors
*/
function makeBeak(matrix, colors) {
const beakLength = 100;
const baseRadius = 16;
const beakFn = (x) => -0.003*x**2;
function getPoint(angle, t) {
const R = t * beakLength;
const yrise = beakFn(R+0.1) - beakFn(R);
const beakFwd = new LV3(0, yrise, 0.1).unit();
const fakeRight = beakFwd.cross(new LV3(0, -1, 0));
const beakUp = fakeRight.cross(beakFwd);
const beakRight = beakFwd.cross(beakUp);
const p = new LV3(0, beakFn(R), R);
const radius = (1 - t) * baseRadius;
const cScalar = Math.cos(angle / 57.3) * radius;
const sScalar = Math.sin(angle / 57.3) * radius;
const p1 = beakRight.scale(cScalar);
const p2 = beakUp.scale(sScalar);
const x = p1.add(p2).add(p);
return matrix.multLV3(x);
}
const rave = Array.from({ length: 360 }, (_, angle) => {
const p = getPoint((angle + 90) % 360, 0.5).unit(); // optimise for angle
const similarity = vectorSimilarity(p, new LV3(0, 0, 1));
return { similarity, angle };
}).sort((a, b) => a.similarity - b.similarity)[0].angle;
const NN = 100;
const beakPoints = [];
for (let i = 0; i <= NN; i++) {
beakPoints.push(getPoint(rave, i/NN));
}
for (let i = 0; i <= NN; i++) {
beakPoints.push(getPoint((rave + 180) % 360, 1 - i/NN));
}
return {
point: matrix.multLV3(new LV3(0, 0, 0)),
string: `
<path d="${pointsToSvgPath(beakPoints)}" style="stroke:${colors.outline};stroke-width:2;fill:${colors.twitter};"/>
`,
};
}
/**
* @param {LMat4} matrix
* @param {number} twitterHeadRadius
* @param {Object} colors
* @param {number} kirbyRadius
* @param {number} headTiltAngle
* @param {number} scaleAmount
*/
function makeTwitterHead(matrix, twitterHeadRadius, colors, kirbyRadius, headTiltAngle, scaleAmount) {
const cs = Angle.cos(-70);
const sn = Angle.sin(-70);
const Roff = kirbyRadius + twitterHeadRadius*0.7;
const m = buildMatrix([
matrix,
LMat4.rotateX(headTiltAngle),
LMat4.trans(0, sn * Roff, cs * Roff),
LMat4.scale(scaleAmount),
]);
let [p1, p2] = [
new LV3(0, 0, 0),
new LV3(0, 0, twitterHeadRadius),
].map(x => m.multLV3(x));
const circleRad = p1.dist(p2);
const center = m.multLV3(new LV3(0, 0, 0));
return {
point: center,
string: `<circle cx="${center.x}" cy="${center.y}" r="${circleRad}" style="fill:${colors.twitter};stroke:${colors.outline};stroke-width:2;"/>`
};
}
/**
* @param {LMat4} matrix
* @param {Object} colors
* @param {number} kirbyRadius
* @param {number} headTiltAngle
* @param {boolean} isRight
* @param {number} scaleAmount
*/
function makeWing(matrix, colors, kirbyRadius, headTiltAngle, isRight = false, scaleAmount) {
const scalar = isRight ? -1 : 1;
const wingMatrix = buildMatrix([
matrix,
LMat4.rotateX(headTiltAngle),
LMat4.trans(-kirbyRadius*0.5 * scalar, -kirbyRadius*0.7, kirbyRadius*-0.2),
LMat4.rotateZ(40 * scalar),
LMat4.rotateY(-15 * scalar),
LMat4.rotateX(-70),
LMat4.scale(1.4 * scaleAmount),
isRight ? LMat4.rotateY(180) : LMat4.identity(),
]);
const points = wingPoints.map(x => wingMatrix.multLV3(x));
return {
point: wingMatrix.multLV3(new LV3(0, 0, 0)),
string: `
<path d="${pointsToSvgPath(points)}" style="stroke:${colors.outline};stroke-width:2;fill:${colors.twitter};"/>
`,
};
}
/**
* @param {number} time
* @param {LMat4} matrix
* @param {number} kirbyRadius
* @param {Object} colors
* @param {number} offsetY
* @param {number} offsetZ
*/
function makeWindStripe(time, matrix, kirbyRadius, colors, offsetY, offsetZ) {
const origin = new LV3(0, -25, kirbyRadius);
const from = new LV3(0, offsetY * kirbyRadius, offsetZ * kbody.r);
function getPoint(time) {
const dy = origin.y - (offsetY*kirbyRadius);
const dz = origin.z - (offsetZ*kirbyRadius);
return new LV3(
0,
dy * Math.sin(time * Math.PI/2),
dz * time,
).scale(time).add(from);
}
const points = [];
for (let i = 0; i < 20; i++) {
const x = i/100 + time;
if (x < 0 || x > 1) continue;
points.push(getPoint(x));
}
return {
string: `
<path d="${pointsToSvgPath(points.map(x => matrix.multLV3(x)))}" style="fill:none;stroke:${colors.wind};stroke-width:1;"/>
`,
point: matrix.multLV3(from.copy()),
};
}
/**
* @param {Array<LV3>} twitterBirdPoints
* @param {number} time
* @param {number} twitterSpinTime
* @param {LMat4} matrix
* @param {Object} colors
* @param {number} kirbyRadius
* @returns {Array<{ string: string, point: LV3 }>}
*/
function makeTwitterBird (twitterBirdPoints, time, twitterSpinTime, matrix, colors, kirbyRadius) {
const OFFSET = 1 + (1-Math.min(time/0.8,1))*5;
const scale = 0.30;// - time*0.25;
const opacity = time > 0.7 ? 1-Math.min((time-0.7)/0.2,1) : 1;
const rotateMat = buildMatrix([LMat4.rotateZ(-0*360), LMat4.rotateY(-twitterSpinTime*360)]);
const commonMat = buildMatrix([
LMat4.trans(0, 0, kirbyRadius * OFFSET),
LMat4.scale(scale),
rotateMat,
]);
const distance = 30;
const frontM = buildMatrix([matrix, commonMat, LMat4.trans(0, 0, -distance/2)]);
const backM = buildMatrix([matrix, commonMat, LMat4.trans(0, 0, distance/2)]);
const frontPoints = twitterBirdPoints.map(x => frontM.multLV3(x));
const backPoints = twitterBirdPoints.map(x => backM.multLV3(x));
const M = buildMatrix([matrix, commonMat]);
const rv = [];
for (let i = 0; i < twitterBirdPoints.length; i++) {
const j = (i+2) % twitterBirdPoints.length;
const a = twitterBirdPoints[i].add(new LV3(0, 0, -distance/2));
const b = twitterBirdPoints[j].add(new LV3(0, 0, -distance/2));
const c = twitterBirdPoints[j].add(new LV3(0, 0, distance/2));
const d = twitterBirdPoints[i].add(new LV3(0, 0, distance/2));
const pts = [a,b,c,d].map(x => M.multLV3(x));
rv.push({
point: pts[0],
string: `
<path d="${pointsToSvgPath(pts)}" style="fill:${withOpacity(colors.twitterLite, opacity)};"/>
`,
});
}
rv.push({
point: frontM.multLV3(new LV3(0, 0, 0)),
string: `
<path d="${pointsToSvgPath(frontPoints)}" style="fill:${withOpacity(colors.twitter, opacity)};"/>
`,
});
rv.push({
point: backM.multLV3(new LV3(0, 0, 0)),
string: `
<path d="${pointsToSvgPath(backPoints)}" style="fill:${withOpacity(colors.twitter, opacity)};"/>
`,
});
return rv;
}
/**
* @param {LMat4} matrix
* @param {Object} colors
* @param {number} kirbyRadius
* @param {{ xRad: number, yRadAbove: number, yRadBelow: number, ppmAngle: number, tilt: number }} options
*/
function makeMouth(matrix, colors, kirbyRadius, options) {
const m = buildMatrix([
matrix,
LMat4.rotateX(options.tilt),
]);
const ppm = LMat4.rotateX(options.ppmAngle); // pre-projection matrix
let points = [];
for (let i = 0; i < 36; i++) {
const yRad = i > 17 ? options.yRadAbove : options.yRadBelow;
const p = new LV3(
Angle.cos(i*10) * options.xRad,
Angle.sin(i*10) * yRad,
-60,
);
points.push(ppm.multLV3(p));
}
points.push(points[0].copy());
points.push(points[1].copy());
points = points.map(x => vectorIntersectsSphere(new LV3(0,0,0), kirbyRadius, x, new LV3(0, 0, 1))).filter(x => x);
points = points.map(x => m.multLV3(x));
return {
point: m.multLV3(new LV3(0, 0, kirbyRadius)),
string: `<path d="${pointsToSvgPath(points)}" style="stroke:${colors.outline};stroke-width:5;fill:${colors.outline};"/>`,
};
}
const svg = document.querySelector('.frame');
const svgOut = document.querySelector('.svg-out');
const colors = {
kirbyColor: '#ff9ff8',
outline: 'black',
twitter: '#1da1f2',
twitterLite: '#2aacfc',
foot: '#ff0072',
eye: '#3577ff',
wind: 'white',
};
const MODE = {
WALKING: 0,
SUCKING: 1,
SMILING_TO_SUCKING: 2,
TWITTER_IN: 3,
TRANSFORM: 4,
WALKING_TWITTER: 5,
};
const modeToNextMode = new Map([
[MODE.WALKING, MODE.SMILING_TO_SUCKING],
[MODE.SMILING_TO_SUCKING, MODE.SUCKING],
[MODE.SUCKING, MODE.TWITTER_IN],
[MODE.TWITTER_IN, MODE.TRANSFORM],
[MODE.TRANSFORM, MODE.WALKING_TWITTER],
[MODE.WALKING_TWITTER, MODE.WALKING],
]);
const modeToSpeed = new Map([
[MODE.WALKING, 0.01],
[MODE.SMILING_TO_SUCKING, 0.08],
[MODE.SUCKING, 0.08],
[MODE.TWITTER_IN, 0.05],
[MODE.TRANSFORM, 0.04],
[MODE.WALKING_TWITTER, 0.01],
]);
function createMouthSettingsFunctionMap() {
const walkingSettingsFn = () => ({
xRad: 15,
yRadAbove: 3,
yRadBelow: 20,
ppmAngle: 5,
tilt: 0,
});
const suckingSettingsFn = (time, suckingTime) => ({
xRad: 35 - suckingTime*2,
yRadAbove: 45 - suckingTime*2,
yRadBelow: 45 - suckingTime*2,
ppmAngle: 10 - suckingTime*2,
tilt: -2,
});
return new Map([
[MODE.WALKING, walkingSettingsFn],
[MODE.WALKING_TWITTER, walkingSettingsFn],
[MODE.SMILING_TO_SUCKING, time => ({
xRad: 15 + time * 15,
yRadAbove: 3 + time*37,
yRadBelow: 20 + time*20,
ppmAngle: 5 + time*5,
tilt: 7 - 5* time,
})],
[MODE.TRANSFORM, time => {
const tt = Math.min(time/0.4, 1);
return {
xRad: 15 + (1-tt) * 15,
yRadAbove: 3 + (1-tt) * 37,
yRadBelow: 20 + (1-tt) * 20,
ppmAngle: 5 + (1-tt) * 5,
tilt: -2 + 2*tt,
};
}],
[MODE.SUCKING, suckingSettingsFn],
[MODE.TWITTER_IN, suckingSettingsFn],
]);
}
const modeToWalkingSettingsFunction = createMouthSettingsFunctionMap();
let footTriangles = undefined;
let wingPoints = undefined;
let twitterBirdPoints = undefined;
let kbody = undefined;
let camera = undefined;
let precomputedZAxisRotations = undefined;
document.addEventListener('DOMContentLoaded', function() {
footTriangles = createFootTriangles();
wingPoints = createWingPoints();
twitterBirdPoints = createTwitterPoints();
kbody = {
pos: new LV3(0, 0, 0),
r: 100,
};
camera = {
r: new LV3(-5, 37, 0),
scale: 1,
window: {
w: 530,
h: 380,
}
};
createViewportTouchController(camera.r);
precomputedZAxisRotations = new Map([
[20, LMat4.rotateZ(20)],
[60, LMat4.rotateZ(60)],
[100, LMat4.rotateZ(100)],
[120, LMat4.rotateZ(120)],
[200, LMat4.rotateZ(200)],
[230, LMat4.rotateZ(230)],
[270, LMat4.rotateZ(270)],
[300, LMat4.rotateZ(300)],
[310, LMat4.rotateZ(310)],
[340, LMat4.rotateZ(340)],
])
step(0);
});
function draw({
mode,
time: timeIn,
walkingTime,
suckingTime,
windTime,
twitterSpinTime,
}) {
let itemz = [];
let headTiltAngle = 0;
let time = timeIn;
let twitterTime = undefined; // undefined is - don't show any twitter stuff
switch (mode) {
case MODE.WALKING:
case MODE.SMILING:
case MODE.WALKING_TWITTER:
headTiltAngle = 0;
break;
case MODE.SMILING_TO_SUCKING:
headTiltAngle = time * 20;
break;
case MODE.TRANSFORM:
headTiltAngle = 20 - Math.min(time/0.4, 1) * 20;
break;
case MODE.TWITTER_IN:
case MODE.SUCKING:
headTiltAngle = 20 - Math.sin(collapseTime(suckingTime,4) * Math.PI*2);
break;
default:
headTiltAngle = 0;
}
let isWalking = false;
switch (mode) {
case MODE.TRANSFORM:
isWalking = true;
break;
case MODE.WALKING:
case MODE.WALKING_TWITTER:
isWalking = true;
break;
}
switch (mode) {
case MODE.WALKING:
case MODE.SMILING:
case MODE.SMILING_TO_SUCKING:
case MODE.SUCKING:
case MODE.TWITTER_IN:
twitterTime = undefined;
break;
case MODE.WALKING_TWITTER:
twitterTime = 1;
break;
case MODE.TRANSFORM:
twitterTime = time;
break;
}
const fullMatrix = buildMatrix([
getFullMatrix(camera.window, camera.scale, camera.r),
isWalking ? LMat4.trans(0, Math.sin((collapseTime(1-walkingTime, 4)) * Math.PI*2) * 5, 0) : LMat4.identity(),
]);
// body
itemz.push(makeBody(fullMatrix,
kbody.pos,
kbody.r,
colors,
));
// twitter-stuff
const twitterHeadRadius = 26;
if (twitterTime != null) {
// incorporate time
const opacity = twitterTime < 0.1 ? 0 : 1;
itemz.push(makeHat(fullMatrix, colors, kbody.r, headTiltAngle, opacity));
const offset = kbody.r + 26*0.7;
const scaleAmount = twitterTime > 0.3 && twitterTime < 0.7 ? (twitterTime-0.3)/0.4 : (twitterTime > 0.7 ? 1 : 0);
const beakScaleAmount = twitterTime > 0.8 && twitterTime < 1 ? (twitterTime-0.8)/0.2 : (twitterTime < 0.8 ? 0 : 1);
// top beak
itemz.push(makeBeak(buildMatrix([
fullMatrix,
LMat4.rotateX(headTiltAngle),
LMat4.trans(0, Angle.sin(-70) * offset - twitterHeadRadius*0.2, Angle.cos(-70) * offset + twitterHeadRadius*0.9),
LMat4.rotateX(16),
LMat4.scale(0.33 * scaleAmount),
]), colors));
// bottom beak
itemz.push(makeBeak(buildMatrix([
fullMatrix,
LMat4.rotateX(headTiltAngle),
LMat4.trans(0, Angle.sin(-70) * offset, Angle.cos(-70) * offset + twitterHeadRadius*0.9),
LMat4.rotateX(2),
LMat4.scale(0.23 * beakScaleAmount),
]), colors));
itemz.push(makeTwitterHead(fullMatrix, twitterHeadRadius, colors, kbody.r, headTiltAngle, scaleAmount));
itemz.push(makeWing(fullMatrix, colors, kbody.r, headTiltAngle, false, scaleAmount));
itemz.push(makeWing(fullMatrix, colors, kbody.r, headTiltAngle, true, scaleAmount));
}
// eyes
itemz.push(makeEye(fullMatrix, colors, kbody.r, headTiltAngle, true));
itemz.push(makeEye(fullMatrix, colors, kbody.r, headTiltAngle, false));
// left foot
const commonFootMat = buildMatrix([
LMat4.rotateY(180),
LMat4.rotateX(90),
LMat4.scale(30),
]);
const walkScale = 0.1;
const walkMat = time => buildMatrix([
LMat4.trans(0, -120, -20),
LMat4.rotateX(Math.sin(collapseTime(time, 2) * Math.PI*2) * 100*walkScale - 60*walkScale),
LMat4.trans(0, 120, 20),
]);
const walkMat2 = (time) => buildMatrix([
LMat4.rotateX(Math.sin(collapseTime(time, 2) * Math.PI*2) * 50 *walkScale),
]);
const leftFootMatrix = buildMatrix([
fullMatrix,
isWalking ? walkMat2(walkingTime) : LMat4.identity(),
LMat4.trans(-50, 95, isWalking ? 30 : 0),
isWalking ? walkMat(walkingTime) : LMat4.identity(),
commonFootMat,
]);
itemz.push(makeFoot(leftFootMatrix, colors, footTriangles));
// right foot
const rightFootMatrix = buildMatrix([
fullMatrix,
isWalking ? walkMat2(1-walkingTime) : LMat4.identity(),
LMat4.trans(50, 95, isWalking ?30:0),
isWalking ? walkMat(1-walkingTime) : LMat4.rotateX(-10),
commonFootMat,
]);
itemz.push(makeFoot(rightFootMatrix, colors, footTriangles));
// MOUTH
const mouthOptions = modeToWalkingSettingsFunction.get(mode)(time, suckingTime);
itemz.push(makeMouth(fullMatrix, colors, kbody.r, mouthOptions));
// arms
const armSwingFn = (time) => Math.sin(collapseTime(time,2) * Math.PI*2) * 50;
itemz.push(makeArm(
fullMatrix,
colors,
kbody.r,
headTiltAngle + (isWalking ? armSwingFn(walkingTime) : 0),
true,
));
itemz.push(makeArm(
fullMatrix,
colors,
kbody.r,
headTiltAngle + (isWalking ? armSwingFn(1-walkingTime) : 0),
false,
));
const showSucking = [
MODE.SUCKING,
MODE.TWITTER_IN,
].includes(mode);
if (showSucking) {
const timeBy = offset => (windTime+offset)%1;
itemz.push(makeWindStripe(timeBy(0.2), fullMatrix.mult(precomputedZAxisRotations.get(20)), kbody.r, colors, -2.5, 4));
itemz.push(makeWindStripe(timeBy(0.4), fullMatrix.mult(precomputedZAxisRotations.get(60)), kbody.r, colors, -1.5, 2));
itemz.push(makeWindStripe(timeBy(0.0), fullMatrix.mult(precomputedZAxisRotations.get(100)), kbody.r, colors, -1, 6));
itemz.push(makeWindStripe(timeBy(0.6), fullMatrix.mult(precomputedZAxisRotations.get(120)), kbody.r, colors, -3, 3));
itemz.push(makeWindStripe(timeBy(0.7), fullMatrix.mult(precomputedZAxisRotations.get(200)), kbody.r, colors, -5, 6));
itemz.push(makeWindStripe(timeBy(0.1), fullMatrix.mult(precomputedZAxisRotations.get(230)), kbody.r, colors, -4, 5));
itemz.push(makeWindStripe(timeBy(0.5), fullMatrix.mult(precomputedZAxisRotations.get(270)), kbody.r, colors, -3, 2.3));
itemz.push(makeWindStripe(timeBy(0.8), fullMatrix.mult(precomputedZAxisRotations.get(300)), kbody.r, colors, -1.5, 4.2));
itemz.push(makeWindStripe(timeBy(0.7), fullMatrix.mult(precomputedZAxisRotations.get(310)), kbody.r, colors, -3.4, 7.4));
itemz.push(makeWindStripe(timeBy(0.9), fullMatrix.mult(precomputedZAxisRotations.get(340)), kbody.r, colors, -2.2, 3));
}
// draw twitter
if (mode === MODE.TWITTER_IN) {
itemz.push(...makeTwitterBird(twitterBirdPoints, time, twitterSpinTime, fullMatrix, colors, kbody.r));
}
svgOut.innerHTML = `
${itemz.sort((a, b) => a.point.z - b.point.z).map(x => x.string).join('\n')}
`;
}
let time = 0.7;
let mode = MODE.WALKING;
const times = {
walking: { t: 0, speed: 0.02, },
sucking: { t: 0, speed: 0.05, },
wind: { t: 0, speed: 0.05, },
twitterSpin: { t: 0, speed: 0.05, },
};
let previousDelta = 0;
function step(delta) {
const timePassed = delta - previousDelta;
const speedScalar = timePassed/28;
draw({
mode,
time,
walkingTime: times.walking.t,
suckingTime: times.sucking.t,
windTime: times.wind.t,
twitterSpinTime: times.twitterSpin.t,
});
time += modeToSpeed.get(mode) * speedScalar;
// increments
times.walking.t = incTime(times.walking.t, times.walking.speed * speedScalar);
times.sucking.t = incTime(times.sucking.t, times.sucking.speed * speedScalar);
times.wind.t = incTime(times.wind.t, times.wind.speed * speedScalar);
times.twitterSpin.t = incTime(times.wind.t, times.twitterSpin.speed * speedScalar);
if (time >= 1) {
mode = modeToNextMode.get(mode);
time %= 1;
}
previousDelta = delta;
requestAnimationFrame(step);
}
const angle2Rad = Math.PI / 180;
const Angle = {
sin(x) { return Math.sin(angle2Rad*x); },
cos(x) { return Math.cos(angle2Rad*x); },
};
function collapseTime(t, nt) {
const bw = 1 / nt;
const bracket = Math.floor(t / bw);
const time = (t - bracket * bw) / bw;
return time;
}
/**
* @param {number} t
* @param {number} inc
*/
function incTime(t, inc) {
return (t + inc) % 1;
}
function withOpacity(hexColor, opacity) {
const opacityPart = Math.floor(opacity * 255).toString(16);
return hexColor + (opacityPart.length === 2 ? opacityPart : '0'+opacityPart);
}
/**
* @param {LV3} v1
* @param {LV3} v2
* @returns {number} - returns positive if v1 and v2 are
* facing the same way, the higher the return value, the better the similarity
*/
function vectorSimilarity(v1, v2) {
const x = v1.dot(v2);
return x;
}
/**
* @param {LV3} v1
* @param {LV3} v2
* @returns {LV3}
*/
function computeNormal(v1, v2) {
return v1.cross(v2).unit();
}
/**
*
* @param {LV3} rotation
* @returns {LMat4}
*/
function getModelViewMatrix(rotation) {
return [
LMat4.rotateZ(rotation.z),
LMat4.rotateX(rotation.x),
LMat4.rotateY(rotation.y),
].reduce((p, c) => p.mult(c), LMat4.identity());
}
/**
*
* @param {{ w: number, h: number }} windowSize
* @param {number} scale
*/
function getProjectionMatrix(windowSize, scale) {
return [
LMat4.trans(windowSize.w/2, windowSize.h/2, 0),
LMat4.scale(scale),
].reduce((p, c) => p.mult(c));
}
/**
*
* @param {{ w: number, h: number }} windowSize
* @param {number} scale
* @param {LV3} rotation
*/
function getFullMatrix(windowSize, scale, rotation) {
const mv = getModelViewMatrix(rotation);
const pm = getProjectionMatrix(windowSize, scale);
return pm.mult(mv);
}
/**
* @desc solves the quadratic equation
* @param {number} a
* @param {number} b
* @param {number} c
*/
function quadSolve(a, b, c) {
return [
(-b + Math.sqrt(b**2 - 4*a*c)) / (2 * a),
(-b - Math.sqrt(b**2 - 4*a*c)) / (2 * a),
];
}
/**
*
* @param {LV3} C - center of sphere
* @param {number} r - radius of sphere
* @param {LV3} P - point on line
* @param {LV3} U - unit vector in direction of line
* @returns {void|LV3}
*/
function vectorIntersectsSphere(C, r, P, U) {
const Q = P.sub(C);
const a = U.dot(U); // should be = 1
const b = 2*U.dot(Q);
const c = Q.dot(Q) - r*r;
const d = b*b - 4*a*c; // discriminant of quadratic
if (d < 0) return undefined; // then solutions are complex, so no intersections
// if (d >= 0) return undefined; // then solutions are real, so there are intersections
// To find intersections (if you want them)
const [t1, t2] = quadSolve(a, b, c);
let P1 = undefined, P2 = undefined;
if (t1 >= 0) P1 = P.add(U.scale(t1)); // first intersection
if (t2 >= 0) P2 = P.add(U.scale(t2)); // second intersection
if (P1 && P2) {
return P1.dist(P) > P2.dist(P) ? P2 : P1;
}
return P1 || P2;
}
/**
* @desc interpolates between two 3d points N times
* @param {LV3} p1
* @param {LV3} p2
* @param {number} N
* @returns {Array<LV3>}
*/
function twerp(p1, p2, N) {
const v = p2.sub(p1).scale(1/N);
const arr = [p1];
let tmp = p1.copy();
for (let i = 0; i <= N; i++) {
tmp.iadd(v);
arr.push(tmp.copy());
}
return arr;
}
/**
* @desc computes the path attribute string <path d="{....}"/> given a set of points
* @param {Array<LV3>} points
* @returns {string}
*/
function pointsToSvgPath(points) {
if (!points.length) return '';
return `M ${points[0].x} ${points[0].y} ${points.slice(1).map(x => `L ${x.x} ${x.y}`).join(' ')}`;
}
/**
* @param {Array<LMat4>} matrixList
* @returns {LMat4}
*/
function buildMatrix(matrixList) {
return matrixList.reduce((p, c) => p.mult(c), LMat4.identity());
}
class ViewportTouchController {
constructor() {
this.recording = false;
this.recordingId = undefined;
this.prev = [0, 0];
}
onTouchOn(id, x, y) {
if (this.recording) return;
this.recording = true;
this.recordingId = id;
this.prev = [x, y];
}
onTouchOff(id) {
if (id !== this.recordingId) return;
this.recordingId = undefined;
this.recording = false;
}
onTouchMove(id, x, y, currentRotation) {
if (!this.recording || id !== this.recordingId) return;
const d = [x-this.prev[0], y-this.prev[1]];
const s = 1;
currentRotation.iadd(new LV3(-d[1]*s, d[0]*s, 0));
const axis = ['x', 'y', 'z'];
for (let i = 0; i < 3; i++) {
if (currentRotation[axis[i]] < 0) currentRotation[axis[i]] = 360+currentRotation[axis[i]];
currentRotation[axis[i]] = currentRotation[axis[i]] % 360;
}
this.prev = [x, y];
}
}
function createViewportTouchController(rotationVec) {
const controller = new ViewportTouchController();
document.body.onmousedown = (event) => controller.onTouchOn(0, event.clientX, event.clientY);
document.body.onmousemove = (event) => controller.onTouchMove(0, event.clientX, event.clientY, rotationVec);
document.body.onmouseup = () => controller.onTouchOff(0);
const ee = document.querySelector('.wrapper');
const otherPaths = ['a', 'path', 'svg'];
ee.addEventListener('touchstart', (event) => {
if (otherPaths.includes(event.target.nodeName)) return;
event.preventDefault();
[...event.touches].forEach(t => controller.onTouchOn(t.identifier, t.pageX, t.pageY));
}, false);
ee.addEventListener('touchmove', (event) => {
event.preventDefault();
[...event.touches].forEach(t => controller.onTouchMove(t.identifier, t.pageX, t.pageY, rotationVec));
}, false);
ee.addEventListener('touchend', (event) => {
if (otherPaths.includes(event.target.nodeName)) return;
event.preventDefault();
[...event.changedTouches].forEach(t => controller.onTouchOff(t.identifier));
}, false);
}
function stringToPointsList(str) {
const points = [];
const s = str.split(' ');
for (let i = 0; i < s.length; i+=2) {
const x = Number(s[i]);
const y = Number(s[i+1]);
const p = new LV3(x, y, 0);
points.push(p);
}
return points;
}
function createWingPoints () {
const WING_STRING = '48.99 0.47 44.16 2.21 39.63 3.58 36.61 4.36 33.03 5.15 28.87 5.89 26.05 6.25 23 6.52 19.95 6.64 17.45 6.64 15.42 6.5 12.65 6.18 9.9 5.67 6.86 4.81 4.31 3.8 1.06 2.01 2.35 4.94 4.29 7.8 5.62 9.53 7.72 11.93 9.63 13.82 11.55 15.51 13.49 16.99 15.09 18.07 17.31 19.37 19.18 20.3 20.62 20.92 22.13 21.49 23.83 22.03 25.76 22.52 27.9 22.92 29.78 23.15 31.13 23.15 31.71 23.15 31.37 23.89 30.38 24.75 29.23 25.67 28.21 26.44 27.08 27.24 25.99 27.95 25.02 28.53 23.98 29.11 22.55 29.83 21.23 30.41 19.6 31 17.87 31.47 15.85 31.8 14.24 31.87 12.65 31.76 11.14 31.47 9.93 31.09 8.65 30.53 7.6 30.13 8.25 31.1 9.1 32.18 9.95 33.15 10.81 34.02 11.68 34.81 12.73 35.67 13.93 36.52 15.36 37.38 16.9 38.15 18.53 38.77 20.27 39.27 21.88 39.58 23.33 39.74 24.4 39.79 25.79 39.78 27.71 39.62 29.41 39.34 31.35 38.89 32.99 38.39 34.07 38.01 33.78 39.02 33.37 40.15 32.82 41.36 32.2 42.5 31.64 43.37 31.14 44.06 30.49 44.85 29.81 45.58 28.89 46.4 28.13 46.98 27.32 47.52 26.46 47.99 25.38 48.48 24.39 48.83 23.36 49.11 22.21 49.33 21.19 49.45 20.16 49.51 19.19 49.51 18.25 49.47 17.36 49.38 16.35 49.25 16.82 49.83 17.57 50.65 18.25 51.32 18.86 51.88 19.51 52.46 20.3 53.09 20.96 53.59 21.8 54.18 22.66 54.73 23.66 55.32 24.55 55.79 25.9 56.43 26.98 56.87 28.11 57.27 29.51 57.68 31.17 58.06 32.81 58.31 34.45 58.45 35.85 58.49 36.83 58.47 37.83 58.41 39.12 58.28 40.75 58.03 42.49 57.66 44.29 57.16 45.9 56.63 47.8 55.89 49.1 55.31';
const wingPoints = stringToPointsList(WING_STRING);
const center = wingPoints[0].add(wingPoints.slice(-1)[0]).scale(0.5);
return wingPoints.map(p => p.sub(center));
}
function createTwitterPoints() {
const twitterString = '224.73 89.12 225.44 85.07 226.35 80.31 227.06 75.45 227.5 70.32 227.92 65.14 227.92 60.15 227.92 55.03 227.92 51.36 230.93 49.19 234.48 46.32 238.28 42.81 241.89 39.26 245.38 35.47 248.27 32.1 250.61 29.03 252.72 26.02 253.62 24.64 250.85 25.78 245.8 27.7 240.68 29.33 235.99 30.53 232.02 31.38 228.41 32.04 226.01 32.41 224.2 32.52 227.19 30.6 229.33 28.94 233.79 25 236.74 21.9 239.28 18.73 241.16 15.96 242.97 12.87 244.63 9.62 245.73 6.89 246.8 3.98 244.48 5.27 242.49 6.27 239.72 7.74 236.81 9.18 231.83 11.28 227.59 12.79 223.21 14.2 219.11 15.3 216.16 16.04 215.13 16.22 213.73 16.41 210.93 13.66 207.47 10.67 203.84 8.1 200.22 6.05 196.65 4.37 192.76 2.85 188.45 1.64 184.2 0.74 180.68 0.27 176.06 0 170.96 0.22 166.97 0.74 162.19 1.74 157.31 3.37 153.37 5.1 149.69 7.05 144.68 10.24 140.12 14.12 137.22 17.09 135.5 19.08 133.77 21.31 131.53 24.62 129.77 27.7 127.87 31.66 126.58 34.92 125.74 37.49 124.97 40.72 124.34 44.2 123.9 47.43 123.68 51.51 123.87 56.49 124.12 59.03 124.71 62.36 125.08 63.94 122.03 63.76 116.68 63.35 113.12 62.95 109.23 62.36 101.16 60.9 91 58.33 81.61 55.25 72 51.32 64.62 47.76 57.51 43.8 51.12 39.88 45.97 36.27 41.05 32.58 36.41 28.8 31.81 24.75 27.12 20.11 23.85 16.74 20.62 13.1 18.34 10.41 17.7 9.55 16.38 11.96 14.29 16.51 13.24 19.56 12.06 23.61 11.29 27.66 10.74 33.26 10.74 39.77 11.38 45.05 13.02 51.33 14.79 56.42 16.89 60.68 19.61 65.13 23.21 69.77 26.34 73.08 29.43 75.91 32.3 78.05 33.59 78.89 31.27 78.97 27.56 78.56 24.17 77.9 20.82 77.05 17.66 76.06 14.53 74.77 12.62 73.81 10.74 72.86 10.12 72.49 10.12 74 10.34 78.27 10.93 82.24 12.1 87.32 13.21 90.81 14.68 94.53 16.7 98.58 19.24 102.58 21.33 105.44 23.39 107.89 26.54 111.17 29.95 114.09 32.68 116.15 36.03 118.24 38.95 119.82 42.33 121.33 46.67 122.94 49.95 123.87 51.79 124.32 49.02 125.06 46.29 125.64 42.88 126.03 39.31 126.19 35.96 126.03 32.84 125.83 30.11 125.51 28.44 125.16 29.64 128.62 31.12 131.82 32.82 135.15 34.7 138.18 37.33 141.8 39.38 144.16 41.21 146.1 43.71 148.47 46.08 150.41 49.29 152.67 52.16 154.5 54.9 155.91 57.05 156.98 60.24 158.23 63.62 159.37 66.41 160.11 69.81 160.78 72.63 161.1 75.27 161.36 76.94 161.36 75.5 162.59 72.82 164.63 70.29 166.33 68.46 167.53 66.63 168.65 64.1 170.19 62.37 171.15 59.87 172.4 57.13 173.75 54.21 175.03 51.15 176.33 47.19 177.82 43.66 178.96 40.36 179.97 35.89 181.09 30.07 182.18 26.14 182.79 22.23 183.19 18.59 183.53 11.75 183.64 6.88 183.56 3.44 183.36 0 182.96 3.07 184.91 7.13 187.25 10.38 189.07 14.05 190.97 17.46 192.63 21.29 194.34 25.4 195.99 29.2 197.48 33.48 198.94 37.97 200.34 44.2 202.05 49.47 203.28 55.76 204.45 65.21 205.67 76.67 206.31 93.47 205.83 101.24 204.96 110.21 203.61 119.25 201.51 126.81 199.35 134.44 196.66 141.93 193.55 148.07 190.58 154.01 187.34 160.76 183.29 166.9 179.04 172.23 174.99 176.75 171.08 181.41 166.89 186.81 161.43 191.33 156.37 195.18 151.64 198.69 147.19 202.06 142.26 206.24 135.65 210.02 128.9 212.92 123.23 215.49 117.43 218.12 110.95 220.35 104.88 222.19 99.03 224.73 89.12';
const center = new LV3(139.49, 100.95, 0);
const points = stringToPointsList(twitterString);
return points.map(p => p.sub(center));
}
function createFootTriangles() {
return [[new LV3(-1.0994818750000002,0.5139003374999999,-0.38493505000000006),new LV3(-1.089070875,0.7508233375,0.10882994999999998),new LV3(-0.9663558750000001,0.9277763374999999,-0.40304305000000007)],
[new LV3(-0.43199987500000003,0.4591243374999999,0.9095939500000001),new LV3(-0.5287948750000001,0.9001143374999999,0.73856995),new LV3(-0.7064558750000001,0.47272633749999987,0.76631695)],
[new LV3(-0.7064558750000001,0.47272633749999987,0.76631695),new LV3(-0.7665548750000001,1.0520803374999999,0.42058795000000004),new LV3(-1.0141778750000001,0.4104433374999999,0.43334494999999995)],
[new LV3(-0.5799808750000001,0.5528553374999999,-0.86712305),new LV3(-0.8529778750000001,0.7186453374999999,-0.74034305),new LV3(-0.39778287500000004,0.9178503374999999,-0.86630805)],
[new LV3(-1.089070875,0.7508233375,0.10882994999999998),new LV3(-0.7665548750000001,1.0520803374999999,0.42058795000000004),new LV3(-0.8899178750000001,1.1012183374999998,-0.011816050000000022)],
[new LV3(-0.5799808750000001,0.5528553374999999,-0.86712305),new LV3(0.10723112499999998,0.9491233374999999,-0.89215605),new LV3(-0.07863887500000001,-0.5152186625000001,-0.82869405)],
[new LV3(0.478042125,0.7564953374999999,-0.88080105),new LV3(-0.07863887500000001,-0.5152186625000001,-0.82869405),new LV3(0.10723112499999998,0.9491233374999999,-0.89215605)],
[new LV3(-0.8529778750000001,0.7186453374999999,-0.74034305),new LV3(-0.695478875,1.2184283374999998,-0.41714205000000004),new LV3(-0.5492268750000001,1.0779453374999999,-0.75872305)],
[new LV3(-0.9663558750000001,0.9277763374999999,-0.40304305000000007),new LV3(-0.8899178750000001,1.1012183374999998,-0.011816050000000022),new LV3(-0.695478875,1.2184283374999998,-0.41714205000000004)],
[new LV3(-0.7665548750000001,1.0520803374999999,0.42058795000000004),new LV3(-0.478906875,1.3961123375,-0.028621050000000037),new LV3(-0.8899178750000001,1.1012183374999998,-0.011816050000000022)],
[new LV3(-0.5492268750000001,1.0779453374999999,-0.75872305),new LV3(-0.31798787500000003,1.3960953374999998,-0.42519105),new LV3(-0.08836287500000002,1.2479953375,-0.7750080500000001)],
[new LV3(-0.5287948750000001,0.9001143374999999,0.73856995),new LV3(-0.08809387500000002,1.2455133374999998,0.57451695),new LV3(-0.7665548750000001,1.0520803374999999,0.42058795000000004)],
[new LV3(-0.5799808750000001,0.5528553374999999,-0.86712305),new LV3(-0.39778287500000004,0.9178503374999999,-0.86630805),new LV3(0.10723112499999998,0.9491233374999999,-0.89215605)],
[new LV3(-0.7665548750000001,1.0520803374999999,0.42058795000000004),new LV3(-0.338219875,1.3915303374999999,0.18495594999999995),new LV3(-0.478906875,1.3961123375,-0.028621050000000037)],
[new LV3(-0.695478875,1.2184283374999998,-0.41714205000000004),new LV3(-0.478906875,1.3961123375,-0.028621050000000037),new LV3(-0.31798787500000003,1.3960953374999998,-0.42519105)],
[new LV3(0.11056812499999999,0.5751083374999999,0.97214095),new LV3(0.13983612499999998,0.8975163374999999,0.8619279499999999),new LV3(-0.24935287500000003,0.6423693374999999,0.9385219499999999)],
[new LV3(-0.24935287500000003,0.6423693374999999,0.9385219499999999),new LV3(0.13983612499999998,0.8975163374999999,0.8619279499999999),new LV3(-0.5287948750000001,0.9001143374999999,0.73856995)],
[new LV3(-0.39778287500000004,0.9178503374999999,-0.86630805),new LV3(-0.08836287500000002,1.2479953375,-0.7750080500000001),new LV3(0.10723112499999998,0.9491233374999999,-0.89215605)],
[new LV3(-0.08809387500000002,1.2455133374999998,0.57451695),new LV3(0.07282112499999999,1.4288293374999999,0.20690794999999998),new LV3(-0.338219875,1.3915303374999999,0.18495594999999995)],
[new LV3(-0.478906875,1.3961123375,-0.028621050000000037),new LV3(0.07282112499999999,1.4288293374999999,0.20690794999999998),new LV3(0.10974612499999999,1.4613383375,-0.34754005000000004)],
[new LV3(-0.08809387500000002,1.2455133374999998,0.57451695),new LV3(0.13983612499999998,0.8975163374999999,0.8619279499999999),new LV3(0.563772125,1.0817773375,0.57136295)],
[new LV3(0.07282112499999999,1.4288293374999999,0.20690794999999998),new LV3(0.563772125,1.0817773375,0.57136295),new LV3(0.514701125,1.3630713374999999,0.08751694999999995)],
[new LV3(0.10974612499999999,1.4613383375,-0.34754005000000004),new LV3(0.5591481249999999,1.0850953374999999,-0.76159205),new LV3(-0.08836287500000002,1.2479953375,-0.7750080500000001)],
[new LV3(0.5591481249999999,1.0850953374999999,-0.76159205),new LV3(0.478042125,0.7564953374999999,-0.88080105),new LV3(0.10723112499999998,0.9491233374999999,-0.89215605)],
[new LV3(0.514701125,1.3630713374999999,0.08751694999999995),new LV3(0.563772125,1.0817773375,0.57136295),new LV3(0.8896761249999999,1.0887053375,0.09993094999999999)],
[new LV3(0.514701125,1.3630713374999999,0.08751694999999995),new LV3(0.845068125,1.0847193375,-0.41078105000000004),new LV3(0.5146031249999999,1.3236213374999999,-0.42197605000000005)],
[new LV3(0.8896761249999999,1.0887053375,0.09993094999999999),new LV3(0.847941125,0.6594653374999999,0.60202495),new LV3(1.086896125,0.7508233375,0.10882994999999998)],
[new LV3(0.8896761249999999,1.0887053375,0.09993094999999999),new LV3(1.0463551249999998,0.7534573374999999,-0.3941400500000001),new LV3(0.845068125,1.0847193375,-0.41078105000000004)],
[new LV3(0.13983612499999998,0.8975163374999999,0.8619279499999999),new LV3(0.11056812499999999,0.5751083374999999,0.97214095),new LV3(0.5185791249999999,0.4531583374999999,0.8777289500000001)],
[new LV3(0.845068125,1.0847193375,-0.41078105000000004),new LV3(0.8658651249999999,0.6457173374999999,-0.73486405),new LV3(0.5591481249999999,1.0850953374999999,-0.76159205)],
[new LV3(0.563772125,1.0817773375,0.57136295),new LV3(0.484878125,0.8293333374999998,0.78675495),new LV3(0.847941125,0.6594653374999999,0.60202495)],
[new LV3(0.8658651249999999,0.6457173374999999,-0.73486405),new LV3(0.709482125,0.2403973374999999,-0.81388505),new LV3(0.478042125,0.7564953374999999,-0.88080105)],
[new LV3(1.086896125,0.7508233375,0.10882994999999998),new LV3(1.090224125,0.4059933374999999,-0.37802105),new LV3(1.0463551249999998,0.7534573374999999,-0.3941400500000001)],
[new LV3(1.090224125,0.4059933374999999,-0.37802105),new LV3(0.8574051249999999,-0.046715662500000144,-0.6424870500000001),new LV3(0.8658651249999999,0.6457173374999999,-0.73486405)],
[new LV3(0.5185791249999999,0.4531583374999999,0.8777289500000001),new LV3(0.7750871249999999,-0.05858466250000005,0.65748195),new LV3(0.847941125,0.6594653374999999,0.60202495)],
[new LV3(0.847941125,0.6594653374999999,0.60202495),new LV3(0.7750871249999999,-0.05858466250000005,0.65748195),new LV3(1.012003125,0.4104413374999999,0.43334494999999995)],
[new LV3(1.1346341249999998,0.3884273374999999,0.043463949999999946),new LV3(1.012003125,0.4104413374999999,0.43334494999999995),new LV3(0.9912751249999999,-0.3723646625000001,0.07113894999999995)],
[new LV3(1.1346341249999998,0.3884273374999999,0.043463949999999946),new LV3(0.966783125,-0.2681326625000001,-0.34814005000000003),new LV3(1.090224125,0.4059933374999999,-0.37802105)],
[new LV3(1.012003125,0.4104413374999999,0.43334494999999995),new LV3(0.7750871249999999,-0.05858466250000005,0.65748195),new LV3(0.7502361249999999,-1.0051066625,0.26668594999999995)],
[new LV3(0.9912751249999999,-0.3723646625000001,0.07113894999999995),new LV3(0.7502361249999999,-1.0051066625,0.26668594999999995),new LV3(0.6752991249999999,-1.3646986625000002,-0.04060705000000003)],
[new LV3(0.9912751249999999,-0.3723646625000001,0.07113894999999995),new LV3(0.6560991249999999,-1.2435226625000002,-0.32937405000000003),new LV3(0.966783125,-0.2681326625000001,-0.34814005000000003)],
[new LV3(0.6560991249999999,-1.2435226625000002,-0.32937405000000003),new LV3(0.28896612499999996,-1.5297696625000001,-0.5583320500000001),new LV3(0.8574051249999999,-0.046715662500000144,-0.6424870500000001)],
[new LV3(0.7750871249999999,-0.05858466250000005,0.65748195),new LV3(0.395487125,-0.22797966250000012,0.85308495),new LV3(0.418374125,-0.9550096625000002,0.63649895)],
[new LV3(0.8574051249999999,-0.046715662500000144,-0.6424870500000001),new LV3(0.30046412499999997,-0.7270236625000002,-0.77996005),new LV3(0.709482125,0.2403973374999999,-0.81388505)],
[new LV3(0.7502361249999999,-1.0051066625,0.26668594999999995),new LV3(0.418374125,-0.9550096625000002,0.63649895),new LV3(0.35149712499999997,-1.8803906625,0.20857094999999995)],
[new LV3(0.709482125,0.2403973374999999,-0.81388505),new LV3(-0.07863887500000001,-0.5152186625000001,-0.82869405),new LV3(0.478042125,0.7564953374999999,-0.88080105)],
[new LV3(0.6752991249999999,-1.3646986625000002,-0.04060705000000003),new LV3(0.35149712499999997,-1.8803906625,0.20857094999999995),new LV3(0.265794125,-2.1318796625000003,-0.08620005000000003)],
[new LV3(0.6752991249999999,-1.3646986625000002,-0.04060705000000003),new LV3(0.298625125,-1.9678296625000002,-0.31517405000000004),new LV3(0.6560991249999999,-1.2435226625000002,-0.32937405000000003)],
[new LV3(0.418374125,-0.9550096625000002,0.63649895),new LV3(0.07321412499999999,-0.8127486625,0.7838599500000001),new LV3(0.09208012499999998,-1.7908736625000001,0.39185395)],
[new LV3(0.395487125,-0.22797966250000012,0.85308495),new LV3(0.14121812499999997,0.0893203374999999,0.9661339499999999),new LV3(0.07321412499999999,-0.8127486625,0.7838599500000001)],
[new LV3(0.265794125,-2.1318796625000003,-0.08620005000000003),new LV3(-0.0010878750000000194,-2.1363046625,-0.32697305000000004),new LV3(0.298625125,-1.9678296625000002,-0.31517405000000004)],
[new LV3(0.298625125,-1.9678296625000002,-0.31517405000000004),new LV3(-0.0010878750000000194,-2.1363046625,-0.32697305000000004),new LV3(0.28896612499999996,-1.5297696625000001,-0.5583320500000001)],
[new LV3(0.28896612499999996,-1.5297696625000001,-0.5583320500000001),new LV3(-0.08869887500000001,-1.3258646625000001,-0.67787505),new LV3(0.30046412499999997,-0.7270236625000002,-0.77996005)],
[new LV3(0.35149712499999997,-1.8803906625,0.20857094999999995),new LV3(0.09208012499999998,-1.7908736625000001,0.39185395),new LV3(-0.0010878750000000194,-2.2661626625,-0.03398205000000004)],
[new LV3(0.07321412499999999,-0.8127486625,0.7838599500000001),new LV3(-0.24724187500000003,-0.8594796625000001,0.75268395),new LV3(0.09208012499999998,-1.7908736625000001,0.39185395)],
[new LV3(0.09208012499999998,-1.7908736625000001,0.39185395),new LV3(-0.290045875,-2.0110926625000003,0.12409694999999996),new LV3(-0.0010878750000000194,-2.2661626625,-0.03398205000000004)],
[new LV3(0.14121812499999997,0.0893203374999999,0.9661339499999999),new LV3(0.11056812499999999,0.5751083374999999,0.97214095),new LV3(-0.18356287500000001,0.1361453374999999,0.9633379499999999)],
[new LV3(-0.0010878750000000194,-2.1363046625,-0.32697305000000004),new LV3(-0.267970875,-2.1318796625000003,-0.08620005000000003),new LV3(-0.285644875,-2.0167606625000003,-0.27769105000000005)],
[new LV3(-0.0010878750000000194,-2.1363046625,-0.32697305000000004),new LV3(-0.285644875,-2.0167606625000003,-0.27769105000000005),new LV3(-0.324334875,-1.5622216625000003,-0.5259390500000001)],
[new LV3(0.14121812499999997,0.0893203374999999,0.9661339499999999),new LV3(-0.24724187500000003,-0.8594796625000001,0.75268395),new LV3(0.07321412499999999,-0.8127486625,0.7838599500000001)],
[new LV3(-0.08869887500000001,-1.3258646625000001,-0.67787505),new LV3(-0.324334875,-1.5622216625000003,-0.5259390500000001),new LV3(-0.385746875,-0.5585806625,-0.78901505)],
[new LV3(-0.36853287500000004,-1.6437176625000003,0.35386995),new LV3(-0.668787875,-1.3885796625000002,0.05596894999999999),new LV3(-0.290045875,-2.0110926625000003,0.12409694999999996)],
[new LV3(-0.290045875,-2.0110926625000003,0.12409694999999996),new LV3(-0.668787875,-1.3885796625000002,0.05596894999999999),new LV3(-0.267970875,-2.1318796625000003,-0.08620005000000003)],
[new LV3(-0.285644875,-2.0167606625000003,-0.27769105000000005),new LV3(-0.668787875,-1.3885796625000002,0.05596894999999999),new LV3(-0.658274875,-1.2435216625,-0.32937505)],
[new LV3(-0.18356287500000001,0.1361453374999999,0.9633379499999999),new LV3(-0.494077875,-0.5893746625,0.73329795),new LV3(-0.24724187500000003,-0.8594796625000001,0.75268395)],
[new LV3(-0.324334875,-1.5622216625000003,-0.5259390500000001),new LV3(-0.658274875,-1.2435216625,-0.32937505),new LV3(-0.8091908750000001,0.023031337499999915,-0.71744405)],
[new LV3(-0.494077875,-0.5893746625,0.73329795),new LV3(-0.777348875,-0.5782186625000001,0.46882895),new LV3(-0.36853287500000004,-1.6437176625000003,0.35386995)],
[new LV3(-0.777348875,-0.5782186625000001,0.46882895),new LV3(-0.993449875,-0.37236666250000006,0.07113894999999995),new LV3(-0.668787875,-1.3885796625000002,0.05596894999999999)],
[new LV3(-0.658274875,-1.2435216625,-0.32937505),new LV3(-0.993449875,-0.37236666250000006,0.07113894999999995),new LV3(-0.9689578750000001,-0.2681316625000001,-0.34814005000000003)],
[new LV3(-0.43199987500000003,0.4591243374999999,0.9095939500000001),new LV3(-0.7064558750000001,0.47272633749999987,0.76631695),new LV3(-0.494077875,-0.5893746625,0.73329795)],
[new LV3(-0.494077875,-0.5893746625,0.73329795),new LV3(-0.8413028750000001,0.1870143374999999,0.62644095),new LV3(-0.777348875,-0.5782186625000001,0.46882895)],
[new LV3(-0.385746875,-0.5585806625,-0.78901505),new LV3(-0.8091908750000001,0.023031337499999915,-0.71744405),new LV3(-0.5799808750000001,0.5528553374999999,-0.86712305)],
[new LV3(-0.18356287500000001,0.1361453374999999,0.9633379499999999),new LV3(0.11056812499999999,0.5751083374999999,0.97214095),new LV3(-0.24935287500000003,0.6423693374999999,0.9385219499999999)],
[new LV3(-1.0141778750000001,0.4104433374999999,0.43334494999999995),new LV3(-1.136809875,0.3884323374999999,0.043464949999999974),new LV3(-0.993449875,-0.37236666250000006,0.07113894999999995)],
[new LV3(-0.9689578750000001,-0.2681316625000001,-0.34814005000000003),new LV3(-1.136809875,0.3884323374999999,0.043464949999999974),new LV3(-1.0994818750000002,0.5139003374999999,-0.38493505000000006)],
[new LV3(-0.8091908750000001,0.023031337499999915,-0.71744405),new LV3(-1.0994818750000002,0.5139003374999999,-0.38493505000000006),new LV3(-0.8529778750000001,0.7186453374999999,-0.74034305)],
[new LV3(-0.8413028750000001,0.1870143374999999,0.62644095),new LV3(-0.7064558750000001,0.47272633749999987,0.76631695),new LV3(-1.0141778750000001,0.4104433374999999,0.43334494999999995)],
[new LV3(-1.136809875,0.3884323374999999,0.043464949999999974),new LV3(-1.0141778750000001,0.4104433374999999,0.43334494999999995),new LV3(-1.089070875,0.7508233375,0.10882994999999998)],
[new LV3(-1.0994818750000002,0.5139003374999999,-0.38493505000000006),new LV3(-1.136809875,0.3884323374999999,0.043464949999999974),new LV3(-1.089070875,0.7508233375,0.10882994999999998)],
[new LV3(-0.8529778750000001,0.7186453374999999,-0.74034305),new LV3(-1.0994818750000002,0.5139003374999999,-0.38493505000000006),new LV3(-0.9663558750000001,0.9277763374999999,-0.40304305000000007)],
[new LV3(-0.7064558750000001,0.47272633749999987,0.76631695),new LV3(-0.5287948750000001,0.9001143374999999,0.73856995),new LV3(-0.7665548750000001,1.0520803374999999,0.42058795000000004)],
[new LV3(-1.089070875,0.7508233375,0.10882994999999998),new LV3(-1.0141778750000001,0.4104433374999999,0.43334494999999995),new LV3(-0.7665548750000001,1.0520803374999999,0.42058795000000004)],
[new LV3(-0.9663558750000001,0.9277763374999999,-0.40304305000000007),new LV3(-1.089070875,0.7508233375,0.10882994999999998),new LV3(-0.8899178750000001,1.1012183374999998,-0.011816050000000022)],
[new LV3(-0.43199987500000003,0.4591243374999999,0.9095939500000001),new LV3(-0.24935287500000003,0.6423693374999999,0.9385219499999999),new LV3(-0.5287948750000001,0.9001143374999999,0.73856995)],
[new LV3(-0.8529778750000001,0.7186453374999999,-0.74034305),new LV3(-0.9663558750000001,0.9277763374999999,-0.40304305000000007),new LV3(-0.695478875,1.2184283374999998,-0.41714205000000004)],
[new LV3(-0.39778287500000004,0.9178503374999999,-0.86630805),new LV3(-0.8529778750000001,0.7186453374999999,-0.74034305),new LV3(-0.5492268750000001,1.0779453374999999,-0.75872305)],
[new LV3(-0.695478875,1.2184283374999998,-0.41714205000000004),new LV3(-0.8899178750000001,1.1012183374999998,-0.011816050000000022),new LV3(-0.478906875,1.3961123375,-0.028621050000000037)],
[new LV3(-0.5492268750000001,1.0779453374999999,-0.75872305),new LV3(-0.695478875,1.2184283374999998,-0.41714205000000004),new LV3(-0.31798787500000003,1.3960953374999998,-0.42519105)],
[new LV3(-0.39778287500000004,0.9178503374999999,-0.86630805),new LV3(-0.5492268750000001,1.0779453374999999,-0.75872305),new LV3(-0.08836287500000002,1.2479953375,-0.7750080500000001)],
[new LV3(-0.7665548750000001,1.0520803374999999,0.42058795000000004),new LV3(-0.08809387500000002,1.2455133374999998,0.57451695),new LV3(-0.338219875,1.3915303374999999,0.18495594999999995)],
[new LV3(-0.5287948750000001,0.9001143374999999,0.73856995),new LV3(0.13983612499999998,0.8975163374999999,0.8619279499999999),new LV3(-0.08809387500000002,1.2455133374999998,0.57451695)],
[new LV3(-0.08836287500000002,1.2479953375,-0.7750080500000001),new LV3(-0.31798787500000003,1.3960953374999998,-0.42519105),new LV3(0.10974612499999999,1.4613383375,-0.34754005000000004)],
[new LV3(-0.478906875,1.3961123375,-0.028621050000000037),new LV3(-0.338219875,1.3915303374999999,0.18495594999999995),new LV3(0.07282112499999999,1.4288293374999999,0.20690794999999998)],
[new LV3(-0.31798787500000003,1.3960953374999998,-0.42519105),new LV3(-0.478906875,1.3961123375,-0.028621050000000037),new LV3(0.10974612499999999,1.4613383375,-0.34754005000000004)],
[new LV3(0.07282112499999999,1.4288293374999999,0.20690794999999998),new LV3(0.514701125,1.3630713374999999,0.08751694999999995),new LV3(0.10974612499999999,1.4613383375,-0.34754005000000004)],
[new LV3(-0.08836287500000002,1.2479953375,-0.7750080500000001),new LV3(0.5591481249999999,1.0850953374999999,-0.76159205),new LV3(0.10723112499999998,0.9491233374999999,-0.89215605)],
[new LV3(0.07282112499999999,1.4288293374999999,0.20690794999999998),new LV3(-0.08809387500000002,1.2455133374999998,0.57451695),new LV3(0.563772125,1.0817773375,0.57136295)],
[new LV3(0.10974612499999999,1.4613383375,-0.34754005000000004),new LV3(0.514701125,1.3630713374999999,0.08751694999999995),new LV3(0.5146031249999999,1.3236213374999999,-0.42197605000000005)],
[new LV3(0.10974612499999999,1.4613383375,-0.34754005000000004),new LV3(0.5146031249999999,1.3236213374999999,-0.42197605000000005),new LV3(0.5591481249999999,1.0850953374999999,-0.76159205)],
[new LV3(0.563772125,1.0817773375,0.57136295),new LV3(0.13983612499999998,0.8975163374999999,0.8619279499999999),new LV3(0.484878125,0.8293333374999998,0.78675495)],
[new LV3(0.514701125,1.3630713374999999,0.08751694999999995),new LV3(0.8896761249999999,1.0887053375,0.09993094999999999),new LV3(0.845068125,1.0847193375,-0.41078105000000004)],
[new LV3(0.5146031249999999,1.3236213374999999,-0.42197605000000005),new LV3(0.845068125,1.0847193375,-0.41078105000000004),new LV3(0.5591481249999999,1.0850953374999999,-0.76159205)],
[new LV3(0.5591481249999999,1.0850953374999999,-0.76159205),new LV3(0.8658651249999999,0.6457173374999999,-0.73486405),new LV3(0.478042125,0.7564953374999999,-0.88080105)],
[new LV3(0.8896761249999999,1.0887053375,0.09993094999999999),new LV3(0.563772125,1.0817773375,0.57136295),new LV3(0.847941125,0.6594653374999999,0.60202495)],
[new LV3(0.8896761249999999,1.0887053375,0.09993094999999999),new LV3(1.086896125,0.7508233375,0.10882994999999998),new LV3(1.0463551249999998,0.7534573374999999,-0.3941400500000001)],
[new LV3(0.484878125,0.8293333374999998,0.78675495),new LV3(0.13983612499999998,0.8975163374999999,0.8619279499999999),new LV3(0.5185791249999999,0.4531583374999999,0.8777289500000001)],
[new LV3(0.845068125,1.0847193375,-0.41078105000000004),new LV3(1.0463551249999998,0.7534573374999999,-0.3941400500000001),new LV3(0.8658651249999999,0.6457173374999999,-0.73486405)],
[new LV3(0.847941125,0.6594653374999999,0.60202495),new LV3(0.484878125,0.8293333374999998,0.78675495),new LV3(0.5185791249999999,0.4531583374999999,0.8777289500000001)],
[new LV3(0.847941125,0.6594653374999999,0.60202495),new LV3(1.012003125,0.4104413374999999,0.43334494999999995),new LV3(1.086896125,0.7508233375,0.10882994999999998)],
[new LV3(1.086896125,0.7508233375,0.10882994999999998),new LV3(1.012003125,0.4104413374999999,0.43334494999999995),new LV3(1.1346341249999998,0.3884273374999999,0.043463949999999946)],
[new LV3(1.086896125,0.7508233375,0.10882994999999998),new LV3(1.1346341249999998,0.3884273374999999,0.043463949999999946),new LV3(1.090224125,0.4059933374999999,-0.37802105)],
[new LV3(1.0463551249999998,0.7534573374999999,-0.3941400500000001),new LV3(1.090224125,0.4059933374999999,-0.37802105),new LV3(0.8658651249999999,0.6457173374999999,-0.73486405)],
[new LV3(0.8658651249999999,0.6457173374999999,-0.73486405),new LV3(0.8574051249999999,-0.046715662500000144,-0.6424870500000001),new LV3(0.709482125,0.2403973374999999,-0.81388505)],
[new LV3(1.1346341249999998,0.3884273374999999,0.043463949999999946),new LV3(0.9912751249999999,-0.3723646625000001,0.07113894999999995),new LV3(0.966783125,-0.2681326625000001,-0.34814005000000003)],
[new LV3(1.090224125,0.4059933374999999,-0.37802105),new LV3(0.966783125,-0.2681326625000001,-0.34814005000000003),new LV3(0.8574051249999999,-0.046715662500000144,-0.6424870500000001)],
[new LV3(0.7750871249999999,-0.05858466250000005,0.65748195),new LV3(0.5185791249999999,0.4531583374999999,0.8777289500000001),new LV3(0.395487125,-0.22797966250000012,0.85308495)],
[new LV3(0.9912751249999999,-0.3723646625000001,0.07113894999999995),new LV3(1.012003125,0.4104413374999999,0.43334494999999995),new LV3(0.7502361249999999,-1.0051066625,0.26668594999999995)],
[new LV3(0.5185791249999999,0.4531583374999999,0.8777289500000001),new LV3(0.11056812499999999,0.5751083374999999,0.97214095),new LV3(0.14121812499999997,0.0893203374999999,0.9661339499999999)],
[new LV3(0.9912751249999999,-0.3723646625000001,0.07113894999999995),new LV3(0.6752991249999999,-1.3646986625000002,-0.04060705000000003),new LV3(0.6560991249999999,-1.2435226625000002,-0.32937405000000003)],
[new LV3(0.395487125,-0.22797966250000012,0.85308495),new LV3(0.5185791249999999,0.4531583374999999,0.8777289500000001),new LV3(0.14121812499999997,0.0893203374999999,0.9661339499999999)],
[new LV3(0.966783125,-0.2681326625000001,-0.34814005000000003),new LV3(0.6560991249999999,-1.2435226625000002,-0.32937405000000003),new LV3(0.8574051249999999,-0.046715662500000144,-0.6424870500000001)],
[new LV3(0.8574051249999999,-0.046715662500000144,-0.6424870500000001),new LV3(0.28896612499999996,-1.5297696625000001,-0.5583320500000001),new LV3(0.30046412499999997,-0.7270236625000002,-0.77996005)],
[new LV3(0.7502361249999999,-1.0051066625,0.26668594999999995),new LV3(0.7750871249999999,-0.05858466250000005,0.65748195),new LV3(0.418374125,-0.9550096625000002,0.63649895)],
[new LV3(0.709482125,0.2403973374999999,-0.81388505),new LV3(0.30046412499999997,-0.7270236625000002,-0.77996005),new LV3(-0.07863887500000001,-0.5152186625000001,-0.82869405)],
[new LV3(0.6752991249999999,-1.3646986625000002,-0.04060705000000003),new LV3(0.7502361249999999,-1.0051066625,0.26668594999999995),new LV3(0.35149712499999997,-1.8803906625,0.20857094999999995)],
[new LV3(0.6752991249999999,-1.3646986625000002,-0.04060705000000003),new LV3(0.265794125,-2.1318796625000003,-0.08620005000000003),new LV3(0.298625125,-1.9678296625000002,-0.31517405000000004)],
[new LV3(0.6560991249999999,-1.2435226625000002,-0.32937405000000003),new LV3(0.298625125,-1.9678296625000002,-0.31517405000000004),new LV3(0.28896612499999996,-1.5297696625000001,-0.5583320500000001)],
[new LV3(0.418374125,-0.9550096625000002,0.63649895),new LV3(0.395487125,-0.22797966250000012,0.85308495),new LV3(0.07321412499999999,-0.8127486625,0.7838599500000001)],
[new LV3(0.35149712499999997,-1.8803906625,0.20857094999999995),new LV3(0.418374125,-0.9550096625000002,0.63649895),new LV3(0.09208012499999998,-1.7908736625000001,0.39185395)],
[new LV3(0.265794125,-2.1318796625000003,-0.08620005000000003),new LV3(0.35149712499999997,-1.8803906625,0.20857094999999995),new LV3(-0.0010878750000000194,-2.2661626625,-0.03398205000000004)],
[new LV3(0.265794125,-2.1318796625000003,-0.08620005000000003),new LV3(-0.0010878750000000194,-2.2661626625,-0.03398205000000004),new LV3(-0.0010878750000000194,-2.1363046625,-0.32697305000000004)],
[new LV3(0.28896612499999996,-1.5297696625000001,-0.5583320500000001),new LV3(-0.0010878750000000194,-2.1363046625,-0.32697305000000004),new LV3(-0.08869887500000001,-1.3258646625000001,-0.67787505)],
[new LV3(0.30046412499999997,-0.7270236625000002,-0.77996005),new LV3(-0.08869887500000001,-1.3258646625000001,-0.67787505),new LV3(-0.07863887500000001,-0.5152186625000001,-0.82869405)],
[new LV3(0.09208012499999998,-1.7908736625000001,0.39185395),new LV3(-0.24724187500000003,-0.8594796625000001,0.75268395),new LV3(-0.36853287500000004,-1.6437176625000003,0.35386995)],
[new LV3(0.09208012499999998,-1.7908736625000001,0.39185395),new LV3(-0.36853287500000004,-1.6437176625000003,0.35386995),new LV3(-0.290045875,-2.0110926625000003,0.12409694999999996)],
[new LV3(-0.0010878750000000194,-2.2661626625,-0.03398205000000004),new LV3(-0.290045875,-2.0110926625000003,0.12409694999999996),new LV3(-0.267970875,-2.1318796625000003,-0.08620005000000003)],
[new LV3(-0.0010878750000000194,-2.1363046625,-0.32697305000000004),new LV3(-0.0010878750000000194,-2.2661626625,-0.03398205000000004),new LV3(-0.267970875,-2.1318796625000003,-0.08620005000000003)],
[new LV3(0.14121812499999997,0.0893203374999999,0.9661339499999999),new LV3(-0.18356287500000001,0.1361453374999999,0.9633379499999999),new LV3(-0.24724187500000003,-0.8594796625000001,0.75268395)],
[new LV3(-0.08869887500000001,-1.3258646625000001,-0.67787505),new LV3(-0.0010878750000000194,-2.1363046625,-0.32697305000000004),new LV3(-0.324334875,-1.5622216625000003,-0.5259390500000001)],
[new LV3(-0.07863887500000001,-0.5152186625000001,-0.82869405),new LV3(-0.08869887500000001,-1.3258646625000001,-0.67787505),new LV3(-0.385746875,-0.5585806625,-0.78901505)],
[new LV3(-0.24724187500000003,-0.8594796625000001,0.75268395),new LV3(-0.494077875,-0.5893746625,0.73329795),new LV3(-0.36853287500000004,-1.6437176625000003,0.35386995)],
[new LV3(-0.285644875,-2.0167606625000003,-0.27769105000000005),new LV3(-0.267970875,-2.1318796625000003,-0.08620005000000003),new LV3(-0.668787875,-1.3885796625000002,0.05596894999999999)],
[new LV3(-0.324334875,-1.5622216625000003,-0.5259390500000001),new LV3(-0.285644875,-2.0167606625000003,-0.27769105000000005),new LV3(-0.658274875,-1.2435216625,-0.32937505)],
[new LV3(-0.385746875,-0.5585806625,-0.78901505),new LV3(-0.324334875,-1.5622216625000003,-0.5259390500000001),new LV3(-0.8091908750000001,0.023031337499999915,-0.71744405)],
[new LV3(-0.36853287500000004,-1.6437176625000003,0.35386995),new LV3(-0.777348875,-0.5782186625000001,0.46882895),new LV3(-0.668787875,-1.3885796625000002,0.05596894999999999)],
[new LV3(-0.658274875,-1.2435216625,-0.32937505),new LV3(-0.668787875,-1.3885796625000002,0.05596894999999999),new LV3(-0.993449875,-0.37236666250000006,0.07113894999999995)],
[new LV3(-0.18356287500000001,0.1361453374999999,0.9633379499999999),new LV3(-0.43199987500000003,0.4591243374999999,0.9095939500000001),new LV3(-0.494077875,-0.5893746625,0.73329795)],
[new LV3(-0.8091908750000001,0.023031337499999915,-0.71744405),new LV3(-0.658274875,-1.2435216625,-0.32937505),new LV3(-0.9689578750000001,-0.2681316625000001,-0.34814005000000003)],
[new LV3(-0.494077875,-0.5893746625,0.73329795),new LV3(-0.7064558750000001,0.47272633749999987,0.76631695),new LV3(-0.8413028750000001,0.1870143374999999,0.62644095)],
[new LV3(-0.777348875,-0.5782186625000001,0.46882895),new LV3(-0.8413028750000001,0.1870143374999999,0.62644095),new LV3(-1.0141778750000001,0.4104433374999999,0.43334494999999995)],
[new LV3(-0.07863887500000001,-0.5152186625000001,-0.82869405),new LV3(-0.385746875,-0.5585806625,-0.78901505),new LV3(-0.5799808750000001,0.5528553374999999,-0.86712305)],
[new LV3(-0.777348875,-0.5782186625000001,0.46882895),new LV3(-1.0141778750000001,0.4104433374999999,0.43334494999999995),new LV3(-0.993449875,-0.37236666250000006,0.07113894999999995)],
[new LV3(-0.18356287500000001,0.1361453374999999,0.9633379499999999),new LV3(-0.24935287500000003,0.6423693374999999,0.9385219499999999),new LV3(-0.43199987500000003,0.4591243374999999,0.9095939500000001)],
[new LV3(-0.9689578750000001,-0.2681316625000001,-0.34814005000000003),new LV3(-0.993449875,-0.37236666250000006,0.07113894999999995),new LV3(-1.136809875,0.3884323374999999,0.043464949999999974)],
[new LV3(-0.8091908750000001,0.023031337499999915,-0.71744405),new LV3(-0.9689578750000001,-0.2681316625000001,-0.34814005000000003),new LV3(-1.0994818750000002,0.5139003374999999,-0.38493505000000006)],
[new LV3(-0.8091908750000001,0.023031337499999915,-0.71744405),new LV3(-0.8529778750000001,0.7186453374999999,-0.74034305),new LV3(-0.5799808750000001,0.5528553374999999,-0.86712305)]];
}
const CONSTANTS = {
rad2deg: 57.295779513082320,
};
class LV3 {
// x: number
// y: number
// z: number
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
// return: string
toString() {
return '[' + this.x + ',' + this.y + ',' + this.z + ']';
}
// return: LV3
copy() {
return new LV3(this.x, this.y, this.z);
}
// o: LV3
setAs(o) {
this.x = o.x;
this.y = o.y;
this.z = o.z;
}
// x: number
// y: number
// z: number
setValues(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
// o: LV3
// return: LV3
add(o) {
return new LV3(this.x + o.x, this.y + o.y, this.z + o.z);
}
// o: LV3
iadd(o) {
this.x += o.x;
this.y += o.y;
this.z += o.z;
}
// o: LV3
// return: LV3
sub(o) {
return new LV3(this.x - o.x, this.y - o.y, this.z - o.z);
}
// o: LV3
isub(o) {
this.x -= o.x;
this.y -= o.y;
this.z -= o.z;
}
// s: number
// return: LV3
scale(s) {
return new LV3(this.x * s, this.y * s, this.z * s);
}
// s: number
// return: LV3
iscale(s) {
this.x *= s;
this.y *= s;
this.z *= s;
}
// s: number
// return: LV3
div(s) {
return new LV3(this.x / s, this.y / s, this.z / s);
}
// s: number
idiv(s) {
this.x /= s;
this.y /= s;
this.z /= s;
}
// o: LV3
// return: number
dot(o) {
return this.x * o.x + this.y * o.y + this.z * o.z;
}
// o: LV3
// return: LV3
cross(o) {
return new LV3(
this.y * o.z - this.z * o.y,
this.z * o.x - this.x * o.z,
this.x * o.y - this.y * o.x
);
}
// o: LV3
icross(o) {
var x = this.x;
var y = this.y;
var z = this.z;
this.x = y * o.z - z * o.y;
this.y = z * o.x - x * o.z;
this.z = x * o.y - y * o.x;
}
// o: LV3
// return: number
dist(o) {
var dx = this.x - o.x;
var dy = this.y - o.y;
var dz = this.z - o.z;
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
// return: number
mag() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
// return: LV3
round() {
return new LV3(Math.round(this.x), Math.round(this.y), Math.round(this.z));
}
// return: LV3
floor() {
return new LV3(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z));
}
iround() {
this.x = Math.round(this.x);
this.y = Math.round(this.y);
this.z = Math.round(this.z);
}
ifloor() {
this.x = Math.floor(this.x);
this.y = Math.floor(this.y);
this.z = Math.floor(this.z);
}
// return: LV3
unit() {
var m = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
return new LV3(this.x / m, this.y / m, this.z / m);
}
iunit() {
var m = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
this.x /= m;
this.y /= m;
this.z /= m;
}
}
class LMat4 {
// inp?: [number * 16]
constructor(inp) {
if(inp === undefined) {
this.arr = [0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0];
} else this.arr = inp;
}
// return: string
toString() {
return '|' + this.arr[0] + ',' + this.arr[1] + ',' + this.arr[2] + ',' + this.arr[3] + '|\n' +
'|' + this.arr[4] + ',' + this.arr[5] + ',' + this.arr[6] + ',' + this.arr[7] + '|\n' +
'|' + this.arr[8] + ',' + this.arr[9] + ',' + this.arr[10] + ',' + this.arr[11] + '|\n' +
'|' + this.arr[12] + ',' + this.arr[13] + ',' + this.arr[14] + ',' + this.arr[15] + '|\n';
}
// return: LMat4
copy() {
return new LMat4(this.arr.slice());
}
itranspose() {
this.arr = [
this.arr[0], this.arr[4], this.arr[8], this.arr[12],
this.arr[1], this.arr[5], this.arr[9], this.arr[13],
this.arr[2], this.arr[6], this.arr[10], this.arr[14],
this.arr[3], this.arr[7], this.arr[11], this.arr[15]
];
}
// return: LMat4
transpose() {
return new LMat4([
this.arr[0], this.arr[4], this.arr[8], this.arr[12],
this.arr[1], this.arr[5], this.arr[9], this.arr[13],
this.arr[2], this.arr[6], this.arr[10], this.arr[14],
this.arr[3], this.arr[7], this.arr[11], this.arr[15]
]);
}
// m: LMat4
imult(m) {
this.arr = [
this.arr[0]*m.arr[0] + this.arr[1]*m.arr[4] + this.arr[2]*m.arr[8] + this.arr[3]*m.arr[12],
this.arr[0]*m.arr[1] + this.arr[1]*m.arr[5] + this.arr[2]*m.arr[9] + this.arr[3]*m.arr[13],
this.arr[0]*m.arr[2] + this.arr[1]*m.arr[6] + this.arr[2]*m.arr[10] + this.arr[3]*m.arr[14],
this.arr[0]*m.arr[3] + this.arr[1]*m.arr[7] + this.arr[2]*m.arr[11] + this.arr[3]*m.arr[15],
this.arr[4]*m.arr[0] + this.arr[5]*m.arr[4] + this.arr[6]*m.arr[8] + this.arr[7]*m.arr[12],
this.arr[4]*m.arr[1] + this.arr[5]*m.arr[5] + this.arr[6]*m.arr[9] + this.arr[7]*m.arr[13],
this.arr[4]*m.arr[2] + this.arr[5]*m.arr[6] + this.arr[6]*m.arr[10] + this.arr[7]*m.arr[14],
this.arr[4]*m.arr[3] + this.arr[5]*m.arr[7] + this.arr[6]*m.arr[11] + this.arr[7]*m.arr[15],
this.arr[8]*m.arr[0] + this.arr[9]*m.arr[4] + this.arr[10]*m.arr[8] + this.arr[11]*m.arr[12],
this.arr[8]*m.arr[1] + this.arr[9]*m.arr[5] + this.arr[10]*m.arr[9] + this.arr[11]*m.arr[13],
this.arr[8]*m.arr[2] + this.arr[9]*m.arr[6] + this.arr[10]*m.arr[10] + this.arr[11]*m.arr[14],
this.arr[8]*m.arr[3] + this.arr[9]*m.arr[7] + this.arr[10]*m.arr[11] + this.arr[11]*m.arr[15],
this.arr[12]*m.arr[0] + this.arr[13]*m.arr[4] + this.arr[14]*m.arr[8] + this.arr[15]*m.arr[12],
this.arr[12]*m.arr[1] + this.arr[13]*m.arr[5] + this.arr[14]*m.arr[9] + this.arr[15]*m.arr[13],
this.arr[12]*m.arr[2] + this.arr[13]*m.arr[6] + this.arr[14]*m.arr[10] + this.arr[15]*m.arr[14],
this.arr[12]*m.arr[3] + this.arr[13]*m.arr[7] + this.arr[14]*m.arr[11] + this.arr[15]*m.arr[15]
];
}
// m: LMat4
// return: LMat4
mult(m) {
return new LMat4([
this.arr[0]*m.arr[0] + this.arr[1]*m.arr[4] + this.arr[2]*m.arr[8] + this.arr[3]*m.arr[12],
this.arr[0]*m.arr[1] + this.arr[1]*m.arr[5] + this.arr[2]*m.arr[9] + this.arr[3]*m.arr[13],
this.arr[0]*m.arr[2] + this.arr[1]*m.arr[6] + this.arr[2]*m.arr[10] + this.arr[3]*m.arr[14],
this.arr[0]*m.arr[3] + this.arr[1]*m.arr[7] + this.arr[2]*m.arr[11] + this.arr[3]*m.arr[15],
this.arr[4]*m.arr[0] + this.arr[5]*m.arr[4] + this.arr[6]*m.arr[8] + this.arr[7]*m.arr[12],
this.arr[4]*m.arr[1] + this.arr[5]*m.arr[5] + this.arr[6]*m.arr[9] + this.arr[7]*m.arr[13],
this.arr[4]*m.arr[2] + this.arr[5]*m.arr[6] + this.arr[6]*m.arr[10] + this.arr[7]*m.arr[14],
this.arr[4]*m.arr[3] + this.arr[5]*m.arr[7] + this.arr[6]*m.arr[11] + this.arr[7]*m.arr[15],
this.arr[8]*m.arr[0] + this.arr[9]*m.arr[4] + this.arr[10]*m.arr[8] + this.arr[11]*m.arr[12],
this.arr[8]*m.arr[1] + this.arr[9]*m.arr[5] + this.arr[10]*m.arr[9] + this.arr[11]*m.arr[13],
this.arr[8]*m.arr[2] + this.arr[9]*m.arr[6] + this.arr[10]*m.arr[10] + this.arr[11]*m.arr[14],
this.arr[8]*m.arr[3] + this.arr[9]*m.arr[7] + this.arr[10]*m.arr[11] + this.arr[11]*m.arr[15],
this.arr[12]*m.arr[0] + this.arr[13]*m.arr[4] + this.arr[14]*m.arr[8] + this.arr[15]*m.arr[12],
this.arr[12]*m.arr[1] + this.arr[13]*m.arr[5] + this.arr[14]*m.arr[9] + this.arr[15]*m.arr[13],
this.arr[12]*m.arr[2] + this.arr[13]*m.arr[6] + this.arr[14]*m.arr[10] + this.arr[15]*m.arr[14],
this.arr[12]*m.arr[3] + this.arr[13]*m.arr[7] + this.arr[14]*m.arr[11] + this.arr[15]*m.arr[15]
]);
}
// p: LV3
// return: LV3
multLV3(p) {
return new LV3(p.x * this.arr[0] + p.y * this.arr[1] + p.z * this.arr[2] + this.arr[3],
p.x * this.arr[4] + p.y * this.arr[5] + p.z * this.arr[6] + this.arr[7],
p.x * this.arr[8] + p.y * this.arr[9] + p.z * this.arr[10] + this.arr[11]);
}
// scalar: number
// return: LMat4
static scale(scalar) {
return new LMat4([scalar, 0, 0, 0,
0, scalar, 0, 0,
0, 0, scalar, 0,
0, 0, 0, 1]);
}
// x: number
// y: number
// z: number
// return: LMat4
static trans(x, y, z) {
return new LMat4([1, 0, 0, x,
0, 1, 0, y,
0, 0, 1, z,
0, 0, 0, 1]);
}
// angle: number
// return: LMat4
static rotateX(angle) {
const radian = angle * 0.0174533;
const cosine = Math.cos(radian);
const sinus = Math.sin(radian);
return new LMat4([1, 0, 0, 0,
0, cosine, -sinus, 0,
0, sinus, cosine, 0,
0, 0, 0, 1,
]);
}
// angle: number
// return: LMat4
static rotateY(angle) {
angle *= 0.0174533;
var cosine = Math.cos(angle);
var sinus = Math.sin(angle);
return new LMat4([cosine, 0, sinus, 0,
0, 1, 0, 0,
-sinus, 0, cosine, 0,
0, 0, 0, 1
]);
}
// angle: number
// return: LMat4
static rotateZ(angle) {
const radian = angle * 0.0174533;
const cosine = Math.cos(radian);
const sinus = Math.sin(radian);
return new LMat4([cosine, -sinus, 0, 0,
sinus, cosine, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]);
}
// return: LMat4
static zero() {
return new LMat4();
}
// return: LMat4
static identity() {
return new LMat4([1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1]);
}
}
body {
margin: 0;
overflow: hidden;
}
.wrapper {
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow: hidden;
background: linear-gradient(to top left, lightblue, lightgreen);
}
.frame {
width: 100%;
max-width: 700px;
pointer-events: none;
}
@media(max-width: 700px) {
svg { border: none; }
}
.social-container {
font-family: 'Inconsolata', monospace;
}
.social-message {
text-align: center;
pointer-events: none;
font-size: 16px;
}
.social-links {
display: grid;
grid-auto-flow: column;
justify-content: center;
margin-top: 16px;
margin-bottom: 16px;
grid-gap: 16px;
pointer-events: auto;
}
.social {
width: 60px;
}
@media (max-width: 400px) {
.social { width: 40px; }
.social-message { font-size: 12px; }
}
@media (max-height: 600px) {
.frame {
height: calc(100vh - 110px);
width: auto;
max-width: initial;
}
}

Twitter Kirby SVG Animation

An svg animation depicting what might happen if Kirby sucked in twitter.

Use mouse or touch to rotate the camera.

A Pen by luke lincoln on CodePen.

License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment