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.
<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; | |
} | |
} |
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.