1*c8dee2aaSAndroid Build Coastguard Worker<!DOCTYPE html> 2*c8dee2aaSAndroid Build Coastguard Worker<title>Mesh2D Demo</title> 3*c8dee2aaSAndroid Build Coastguard Worker<meta charset="utf-8" /> 4*c8dee2aaSAndroid Build Coastguard Worker<meta name="viewport" content="width=device-width, initial-scale=1.0"> 5*c8dee2aaSAndroid Build Coastguard Worker 6*c8dee2aaSAndroid Build Coastguard Worker<!-- Mesh2D origin trial (https://developer.chrome.com/origintrials/#/view_trial/2797298318550499329) --> 7*c8dee2aaSAndroid Build Coastguard Worker<meta http-equiv="origin-trial" content="AtLoDlklU0E4Hvr2CcMAmFtHYbi+esffS5I/qCK8i5bG9hhtiqpiJgM9qdK+7sbunIPtgSntEYNWExeHzn1tTAQAAABUeyJvcmlnaW4iOiJodHRwczovL2RlbW9zLnNraWEub3JnOjQ0MyIsImZlYXR1cmUiOiJDYW52YXMyZE1lc2giLCJleHBpcnkiOjE3NDk1OTk5OTl9"> 8*c8dee2aaSAndroid Build Coastguard Worker 9*c8dee2aaSAndroid Build Coastguard Worker<style> 10*c8dee2aaSAndroid Build Coastguard Worker canvas { 11*c8dee2aaSAndroid Build Coastguard Worker width: 1024px; 12*c8dee2aaSAndroid Build Coastguard Worker height: 1024px; 13*c8dee2aaSAndroid Build Coastguard Worker background-color: #ccc; 14*c8dee2aaSAndroid Build Coastguard Worker display: none; 15*c8dee2aaSAndroid Build Coastguard Worker } 16*c8dee2aaSAndroid Build Coastguard Worker 17*c8dee2aaSAndroid Build Coastguard Worker .root { 18*c8dee2aaSAndroid Build Coastguard Worker display: flex; 19*c8dee2aaSAndroid Build Coastguard Worker } 20*c8dee2aaSAndroid Build Coastguard Worker 21*c8dee2aaSAndroid Build Coastguard Worker .controls { 22*c8dee2aaSAndroid Build Coastguard Worker display: flex; 23*c8dee2aaSAndroid Build Coastguard Worker } 24*c8dee2aaSAndroid Build Coastguard Worker .controls-left { width: 50%; } 25*c8dee2aaSAndroid Build Coastguard Worker .controls-right { width: 50%; } 26*c8dee2aaSAndroid Build Coastguard Worker .controls-right select { width: 100%; } 27*c8dee2aaSAndroid Build Coastguard Worker 28*c8dee2aaSAndroid Build Coastguard Worker #loader { 29*c8dee2aaSAndroid Build Coastguard Worker width: 1024px; 30*c8dee2aaSAndroid Build Coastguard Worker height: 1024px; 31*c8dee2aaSAndroid Build Coastguard Worker display: flex; 32*c8dee2aaSAndroid Build Coastguard Worker flex-direction: column; 33*c8dee2aaSAndroid Build Coastguard Worker justify-content: center; 34*c8dee2aaSAndroid Build Coastguard Worker align-items: center; 35*c8dee2aaSAndroid Build Coastguard Worker background-color: #f1f2f3; 36*c8dee2aaSAndroid Build Coastguard Worker font: bold 2em monospace; 37*c8dee2aaSAndroid Build Coastguard Worker color: #85a2b6; 38*c8dee2aaSAndroid Build Coastguard Worker } 39*c8dee2aaSAndroid Build Coastguard Worker</style> 40*c8dee2aaSAndroid Build Coastguard Worker 41*c8dee2aaSAndroid Build Coastguard Worker<div class="root"> 42*c8dee2aaSAndroid Build Coastguard Worker <div id="loader"> 43*c8dee2aaSAndroid Build Coastguard Worker <img src="BeanEater-1s-200px.gif"> 44*c8dee2aaSAndroid Build Coastguard Worker <div>Fetching <a href="https://skia.org/docs/user/modules/canvaskit/">CanvasKit</a>...</div> 45*c8dee2aaSAndroid Build Coastguard Worker </div> 46*c8dee2aaSAndroid Build Coastguard Worker 47*c8dee2aaSAndroid Build Coastguard Worker <div id="canvas_wrapper"> 48*c8dee2aaSAndroid Build Coastguard Worker <canvas id="canvas2d" width="1024" height="1024"></canvas> 49*c8dee2aaSAndroid Build Coastguard Worker <canvas id="canvas3d" width="1024" height="1024"></canvas> 50*c8dee2aaSAndroid Build Coastguard Worker </div> 51*c8dee2aaSAndroid Build Coastguard Worker 52*c8dee2aaSAndroid Build Coastguard Worker <div class="controls"> 53*c8dee2aaSAndroid Build Coastguard Worker <div class="controls-left"> 54*c8dee2aaSAndroid Build Coastguard Worker <div>Show mesh</div> 55*c8dee2aaSAndroid Build Coastguard Worker <div>Level of detail</div> 56*c8dee2aaSAndroid Build Coastguard Worker <div>Animator</div> 57*c8dee2aaSAndroid Build Coastguard Worker <div>Renderer</div> 58*c8dee2aaSAndroid Build Coastguard Worker </div> 59*c8dee2aaSAndroid Build Coastguard Worker <div class="controls-right"> 60*c8dee2aaSAndroid Build Coastguard Worker <div> 61*c8dee2aaSAndroid Build Coastguard Worker <input type="checkbox" id="show_mesh"/> 62*c8dee2aaSAndroid Build Coastguard Worker </div> 63*c8dee2aaSAndroid Build Coastguard Worker <div> 64*c8dee2aaSAndroid Build Coastguard Worker <select id="lod"> 65*c8dee2aaSAndroid Build Coastguard Worker <option value="4">4x4</option> 66*c8dee2aaSAndroid Build Coastguard Worker <option value="8" selected>8x8</option> 67*c8dee2aaSAndroid Build Coastguard Worker <option value="16">16x16</option> 68*c8dee2aaSAndroid Build Coastguard Worker <option value="32">32x32</option> 69*c8dee2aaSAndroid Build Coastguard Worker <option value="64">64x64</option> 70*c8dee2aaSAndroid Build Coastguard Worker <option value="128">128x128</option> 71*c8dee2aaSAndroid Build Coastguard Worker <option value="255">255x255</option> 72*c8dee2aaSAndroid Build Coastguard Worker </select> 73*c8dee2aaSAndroid Build Coastguard Worker </div> 74*c8dee2aaSAndroid Build Coastguard Worker <div> 75*c8dee2aaSAndroid Build Coastguard Worker <select id="animator"> 76*c8dee2aaSAndroid Build Coastguard Worker <option value="">Manual</option> 77*c8dee2aaSAndroid Build Coastguard Worker <option value="squircleAnimator">Squircle</option> 78*c8dee2aaSAndroid Build Coastguard Worker <option value="twirlAnimator">Twirl</option> 79*c8dee2aaSAndroid Build Coastguard Worker <option value="wiggleAnimator">Wiggle</option> 80*c8dee2aaSAndroid Build Coastguard Worker <option value="cylinderAnimator" selected>Cylinder</option> 81*c8dee2aaSAndroid Build Coastguard Worker </select> 82*c8dee2aaSAndroid Build Coastguard Worker </div> 83*c8dee2aaSAndroid Build Coastguard Worker <div> 84*c8dee2aaSAndroid Build Coastguard Worker <select id="renderer" disabled> 85*c8dee2aaSAndroid Build Coastguard Worker <option value="ckRenderer" selected>CanvasKit (polyfill)</option> 86*c8dee2aaSAndroid Build Coastguard Worker <option value="nativeRenderer">Canvas2D (native)</option> 87*c8dee2aaSAndroid Build Coastguard Worker </select> 88*c8dee2aaSAndroid Build Coastguard Worker </div> 89*c8dee2aaSAndroid Build Coastguard Worker </div> 90*c8dee2aaSAndroid Build Coastguard Worker </div> 91*c8dee2aaSAndroid Build Coastguard Worker</div> 92*c8dee2aaSAndroid Build Coastguard Worker 93*c8dee2aaSAndroid Build Coastguard Worker<script type="text/javascript" src="canvaskit.js"></script> 94*c8dee2aaSAndroid Build Coastguard Worker 95*c8dee2aaSAndroid Build Coastguard Worker<script type="text/javascript"> 96*c8dee2aaSAndroid Build Coastguard Worker class MeshData { 97*c8dee2aaSAndroid Build Coastguard Worker constructor(size, renderer) { 98*c8dee2aaSAndroid Build Coastguard Worker const vertex_count = size*size; 99*c8dee2aaSAndroid Build Coastguard Worker 100*c8dee2aaSAndroid Build Coastguard Worker // 2 floats per point 101*c8dee2aaSAndroid Build Coastguard Worker this.verts = new Float32Array(vertex_count*2); 102*c8dee2aaSAndroid Build Coastguard Worker this.animated_verts = new Float32Array(vertex_count*2); 103*c8dee2aaSAndroid Build Coastguard Worker this.uvs = new Float32Array(vertex_count*2); 104*c8dee2aaSAndroid Build Coastguard Worker 105*c8dee2aaSAndroid Build Coastguard Worker let i = 0; 106*c8dee2aaSAndroid Build Coastguard Worker for (let y = 0; y < size; ++y) { 107*c8dee2aaSAndroid Build Coastguard Worker for (let x = 0; x < size; ++x) { 108*c8dee2aaSAndroid Build Coastguard Worker // To keep things simple, all vertices are normalized. 109*c8dee2aaSAndroid Build Coastguard Worker this.verts[i + 0] = this.uvs[i + 0] = x / (size - 1); 110*c8dee2aaSAndroid Build Coastguard Worker this.verts[i + 1] = this.uvs[i + 1] = y / (size - 1); 111*c8dee2aaSAndroid Build Coastguard Worker 112*c8dee2aaSAndroid Build Coastguard Worker i += 2; 113*c8dee2aaSAndroid Build Coastguard Worker } 114*c8dee2aaSAndroid Build Coastguard Worker } 115*c8dee2aaSAndroid Build Coastguard Worker 116*c8dee2aaSAndroid Build Coastguard Worker // 2 triangles per LOD square, 3 indices per triangle 117*c8dee2aaSAndroid Build Coastguard Worker this.indices = new Uint16Array((size - 1)*(size - 1)*6); 118*c8dee2aaSAndroid Build Coastguard Worker i = 0; 119*c8dee2aaSAndroid Build Coastguard Worker for (let y = 0; y < size - 1; ++y) { 120*c8dee2aaSAndroid Build Coastguard Worker for (let x = 0; x < size - 1; ++x) { 121*c8dee2aaSAndroid Build Coastguard Worker const vidx0 = x + y*size; 122*c8dee2aaSAndroid Build Coastguard Worker const vidx1 = vidx0 + size; 123*c8dee2aaSAndroid Build Coastguard Worker 124*c8dee2aaSAndroid Build Coastguard Worker this.indices[i++] = vidx0; 125*c8dee2aaSAndroid Build Coastguard Worker this.indices[i++] = vidx0 + 1; 126*c8dee2aaSAndroid Build Coastguard Worker this.indices[i++] = vidx1 + 1; 127*c8dee2aaSAndroid Build Coastguard Worker 128*c8dee2aaSAndroid Build Coastguard Worker this.indices[i++] = vidx0; 129*c8dee2aaSAndroid Build Coastguard Worker this.indices[i++] = vidx1; 130*c8dee2aaSAndroid Build Coastguard Worker this.indices[i++] = vidx1 + 1; 131*c8dee2aaSAndroid Build Coastguard Worker } 132*c8dee2aaSAndroid Build Coastguard Worker } 133*c8dee2aaSAndroid Build Coastguard Worker 134*c8dee2aaSAndroid Build Coastguard Worker // These can be cached upfront (constant during animation). 135*c8dee2aaSAndroid Build Coastguard Worker this.uvBuffer = renderer.makeUVBuffer(this.uvs); 136*c8dee2aaSAndroid Build Coastguard Worker this.indexBuffer = renderer.makeIndexBuffer(this.indices); 137*c8dee2aaSAndroid Build Coastguard Worker } 138*c8dee2aaSAndroid Build Coastguard Worker 139*c8dee2aaSAndroid Build Coastguard Worker animate(animator) { 140*c8dee2aaSAndroid Build Coastguard Worker function bezier(t, p0, p1, p2, p3){ 141*c8dee2aaSAndroid Build Coastguard Worker return (1 - t)*(1 - t)*(1 - t)*p0 + 142*c8dee2aaSAndroid Build Coastguard Worker 3*(1 - t)*(1 - t)*t*p1 + 143*c8dee2aaSAndroid Build Coastguard Worker 3*(1 - t)*t*t*p2 + 144*c8dee2aaSAndroid Build Coastguard Worker t*t*t*p3; 145*c8dee2aaSAndroid Build Coastguard Worker } 146*c8dee2aaSAndroid Build Coastguard Worker 147*c8dee2aaSAndroid Build Coastguard Worker // Tuned for non-linear transition. 148*c8dee2aaSAndroid Build Coastguard Worker function ease(t) { return bezier(t, 0, 0.4, 1, 1); } 149*c8dee2aaSAndroid Build Coastguard Worker 150*c8dee2aaSAndroid Build Coastguard Worker if (!animator) { 151*c8dee2aaSAndroid Build Coastguard Worker return; 152*c8dee2aaSAndroid Build Coastguard Worker } 153*c8dee2aaSAndroid Build Coastguard Worker 154*c8dee2aaSAndroid Build Coastguard Worker const ms = Date.now() - timeBase; 155*c8dee2aaSAndroid Build Coastguard Worker const t = Math.abs((ms / 1000) % 2 - 1); 156*c8dee2aaSAndroid Build Coastguard Worker 157*c8dee2aaSAndroid Build Coastguard Worker animator(this.verts, this.animated_verts, t); 158*c8dee2aaSAndroid Build Coastguard Worker } 159*c8dee2aaSAndroid Build Coastguard Worker 160*c8dee2aaSAndroid Build Coastguard Worker generateTriangles(func) { 161*c8dee2aaSAndroid Build Coastguard Worker for (let i = 0; i < this.indices.length; i += 3) { 162*c8dee2aaSAndroid Build Coastguard Worker const i0 = 2*this.indices[i + 0]; 163*c8dee2aaSAndroid Build Coastguard Worker const i1 = 2*this.indices[i + 1]; 164*c8dee2aaSAndroid Build Coastguard Worker const i2 = 2*this.indices[i + 2]; 165*c8dee2aaSAndroid Build Coastguard Worker 166*c8dee2aaSAndroid Build Coastguard Worker func(this.animated_verts[i0 + 0], this.animated_verts[i0 + 1], 167*c8dee2aaSAndroid Build Coastguard Worker this.animated_verts[i1 + 0], this.animated_verts[i1 + 1], 168*c8dee2aaSAndroid Build Coastguard Worker this.animated_verts[i2 + 0], this.animated_verts[i2 + 1]); 169*c8dee2aaSAndroid Build Coastguard Worker } 170*c8dee2aaSAndroid Build Coastguard Worker } 171*c8dee2aaSAndroid Build Coastguard Worker } 172*c8dee2aaSAndroid Build Coastguard Worker 173*c8dee2aaSAndroid Build Coastguard Worker class PatchControls { 174*c8dee2aaSAndroid Build Coastguard Worker constructor() { 175*c8dee2aaSAndroid Build Coastguard Worker this.controls = [ 176*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 0.00, 0.33], color: '#0ff', deps: [] }, 177*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 0.00, 0.00], color: '#0f0', deps: [0, 2] }, 178*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 0.33, 0.00], color: '#0ff', deps: [] }, 179*c8dee2aaSAndroid Build Coastguard Worker 180*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 0.66, 0.00], color: '#0ff', deps: [] }, 181*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 1.00, 0.00], color: '#0f0', deps: [3, 5] }, 182*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 1.00, 0.33], color: '#0ff', deps: [] }, 183*c8dee2aaSAndroid Build Coastguard Worker 184*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 1.00, 0.66], color: '#0ff', deps: [] }, 185*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 1.00, 1.00], color: '#0f0', deps: [6, 8] }, 186*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 0.66, 1.00], color: '#0ff', deps: [] }, 187*c8dee2aaSAndroid Build Coastguard Worker 188*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 0.33, 1.00], color: '#0ff', deps: [] }, 189*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 0.00, 1.00], color: '#0f0', deps: [9, 11] }, 190*c8dee2aaSAndroid Build Coastguard Worker { pos: [ 0.00, 0.66], color: '#0ff', deps: [] }, 191*c8dee2aaSAndroid Build Coastguard Worker ]; 192*c8dee2aaSAndroid Build Coastguard Worker 193*c8dee2aaSAndroid Build Coastguard Worker this.radius = 0.01; 194*c8dee2aaSAndroid Build Coastguard Worker this.drag_target = null; 195*c8dee2aaSAndroid Build Coastguard Worker } 196*c8dee2aaSAndroid Build Coastguard Worker 197*c8dee2aaSAndroid Build Coastguard Worker mapMouse(ev) { 198*c8dee2aaSAndroid Build Coastguard Worker const w = canvas2d.width, 199*c8dee2aaSAndroid Build Coastguard Worker h = canvas2d.height; 200*c8dee2aaSAndroid Build Coastguard Worker return [ 201*c8dee2aaSAndroid Build Coastguard Worker (ev.offsetX - w*(1 - meshScale)*0.5)/(w*meshScale), 202*c8dee2aaSAndroid Build Coastguard Worker (ev.offsetY - h*(1 - meshScale)*0.5)/(h*meshScale), 203*c8dee2aaSAndroid Build Coastguard Worker ]; 204*c8dee2aaSAndroid Build Coastguard Worker } 205*c8dee2aaSAndroid Build Coastguard Worker 206*c8dee2aaSAndroid Build Coastguard Worker onMouseDown(ev) { 207*c8dee2aaSAndroid Build Coastguard Worker const mouse_pos = this.mapMouse(ev); 208*c8dee2aaSAndroid Build Coastguard Worker 209*c8dee2aaSAndroid Build Coastguard Worker for (let i = this.controls.length - 1; i >= 0; --i) { 210*c8dee2aaSAndroid Build Coastguard Worker const dx = this.controls[i].pos[0] - mouse_pos[0], 211*c8dee2aaSAndroid Build Coastguard Worker dy = this.controls[i].pos[1] - mouse_pos[1]; 212*c8dee2aaSAndroid Build Coastguard Worker 213*c8dee2aaSAndroid Build Coastguard Worker if (dx*dx + dy*dy <= this.radius*this.radius) { 214*c8dee2aaSAndroid Build Coastguard Worker this.drag_target = this.controls[i]; 215*c8dee2aaSAndroid Build Coastguard Worker this.drag_offset = [dx, dy]; 216*c8dee2aaSAndroid Build Coastguard Worker break; 217*c8dee2aaSAndroid Build Coastguard Worker } 218*c8dee2aaSAndroid Build Coastguard Worker } 219*c8dee2aaSAndroid Build Coastguard Worker } 220*c8dee2aaSAndroid Build Coastguard Worker 221*c8dee2aaSAndroid Build Coastguard Worker onMouseMove(ev) { 222*c8dee2aaSAndroid Build Coastguard Worker if (!this.drag_target) return; 223*c8dee2aaSAndroid Build Coastguard Worker 224*c8dee2aaSAndroid Build Coastguard Worker const mouse_pos = this.mapMouse(ev), 225*c8dee2aaSAndroid Build Coastguard Worker dx = mouse_pos[0] + this.drag_offset[0] - this.drag_target.pos[0], 226*c8dee2aaSAndroid Build Coastguard Worker dy = mouse_pos[1] + this.drag_offset[1] - this.drag_target.pos[1]; 227*c8dee2aaSAndroid Build Coastguard Worker 228*c8dee2aaSAndroid Build Coastguard Worker this.drag_target.pos = [ this.drag_target.pos[0] + dx, this.drag_target.pos[1] + dy ]; 229*c8dee2aaSAndroid Build Coastguard Worker 230*c8dee2aaSAndroid Build Coastguard Worker for (let dep_index of this.drag_target.deps) { 231*c8dee2aaSAndroid Build Coastguard Worker const dep = this.controls[dep_index]; 232*c8dee2aaSAndroid Build Coastguard Worker dep.pos = [ dep.pos[0] + dx, dep.pos[1] + dy ]; 233*c8dee2aaSAndroid Build Coastguard Worker } 234*c8dee2aaSAndroid Build Coastguard Worker 235*c8dee2aaSAndroid Build Coastguard Worker this.updateVerts(); 236*c8dee2aaSAndroid Build Coastguard Worker } 237*c8dee2aaSAndroid Build Coastguard Worker 238*c8dee2aaSAndroid Build Coastguard Worker onMouseUp(ev) { 239*c8dee2aaSAndroid Build Coastguard Worker this.drag_target = null; 240*c8dee2aaSAndroid Build Coastguard Worker } 241*c8dee2aaSAndroid Build Coastguard Worker 242*c8dee2aaSAndroid Build Coastguard Worker updateVerts() { 243*c8dee2aaSAndroid Build Coastguard Worker this.samplePatch(parseInt(lodSelectUI.value), meshData.animated_verts); 244*c8dee2aaSAndroid Build Coastguard Worker } 245*c8dee2aaSAndroid Build Coastguard Worker 246*c8dee2aaSAndroid Build Coastguard Worker drawUI(line_func, circle_func) { 247*c8dee2aaSAndroid Build Coastguard Worker for (let i = 0; i < this.controls.length; i += 3) { 248*c8dee2aaSAndroid Build Coastguard Worker const c0 = this.controls[i + 0], 249*c8dee2aaSAndroid Build Coastguard Worker c1 = this.controls[i + 1], 250*c8dee2aaSAndroid Build Coastguard Worker c2 = this.controls[i + 2]; 251*c8dee2aaSAndroid Build Coastguard Worker 252*c8dee2aaSAndroid Build Coastguard Worker line_func(c0.pos, c1.pos, '#f00'); 253*c8dee2aaSAndroid Build Coastguard Worker line_func(c1.pos, c2.pos, '#f00'); 254*c8dee2aaSAndroid Build Coastguard Worker circle_func(c0.pos, this.radius, c0.color); 255*c8dee2aaSAndroid Build Coastguard Worker circle_func(c1.pos, this.radius, c1.color); 256*c8dee2aaSAndroid Build Coastguard Worker circle_func(c2.pos, this.radius, c2.color); 257*c8dee2aaSAndroid Build Coastguard Worker } 258*c8dee2aaSAndroid Build Coastguard Worker } 259*c8dee2aaSAndroid Build Coastguard Worker 260*c8dee2aaSAndroid Build Coastguard Worker // Based on https://github.com/google/skia/blob/de56f293eb41d65786b9e6224fdf9a4702b30f51/src/utils/SkPatchUtils.cpp#L84 261*c8dee2aaSAndroid Build Coastguard Worker sampleCubic(cind, lod) { 262*c8dee2aaSAndroid Build Coastguard Worker const divisions = lod - 1, 263*c8dee2aaSAndroid Build Coastguard Worker h = 1/divisions, 264*c8dee2aaSAndroid Build Coastguard Worker h2 = h*h, 265*c8dee2aaSAndroid Build Coastguard Worker h3 = h*h2, 266*c8dee2aaSAndroid Build Coastguard Worker pts = [ 267*c8dee2aaSAndroid Build Coastguard Worker this.controls[cind[0]].pos, 268*c8dee2aaSAndroid Build Coastguard Worker this.controls[cind[1]].pos, 269*c8dee2aaSAndroid Build Coastguard Worker this.controls[cind[2]].pos, 270*c8dee2aaSAndroid Build Coastguard Worker this.controls[cind[3]].pos, 271*c8dee2aaSAndroid Build Coastguard Worker ], 272*c8dee2aaSAndroid Build Coastguard Worker coeffs = [ 273*c8dee2aaSAndroid Build Coastguard Worker [ 274*c8dee2aaSAndroid Build Coastguard Worker pts[3][0] + 3*(pts[1][0] - pts[2][0]) - pts[0][0], 275*c8dee2aaSAndroid Build Coastguard Worker pts[3][1] + 3*(pts[1][1] - pts[2][1]) - pts[0][1], 276*c8dee2aaSAndroid Build Coastguard Worker ], 277*c8dee2aaSAndroid Build Coastguard Worker [ 278*c8dee2aaSAndroid Build Coastguard Worker 3*(pts[2][0] - 2*pts[1][0] + pts[0][0]), 279*c8dee2aaSAndroid Build Coastguard Worker 3*(pts[2][1] - 2*pts[1][1] + pts[0][1]), 280*c8dee2aaSAndroid Build Coastguard Worker ], 281*c8dee2aaSAndroid Build Coastguard Worker [ 282*c8dee2aaSAndroid Build Coastguard Worker 3*(pts[1][0] - pts[0][0]), 283*c8dee2aaSAndroid Build Coastguard Worker 3*(pts[1][1] - pts[0][1]), 284*c8dee2aaSAndroid Build Coastguard Worker ], 285*c8dee2aaSAndroid Build Coastguard Worker pts[0], 286*c8dee2aaSAndroid Build Coastguard Worker ], 287*c8dee2aaSAndroid Build Coastguard Worker fwDiff3 = [ 288*c8dee2aaSAndroid Build Coastguard Worker 6*h3*coeffs[0][0], 289*c8dee2aaSAndroid Build Coastguard Worker 6*h3*coeffs[0][1], 290*c8dee2aaSAndroid Build Coastguard Worker ]; 291*c8dee2aaSAndroid Build Coastguard Worker 292*c8dee2aaSAndroid Build Coastguard Worker let fwDiff = [ 293*c8dee2aaSAndroid Build Coastguard Worker coeffs[3], 294*c8dee2aaSAndroid Build Coastguard Worker [ 295*c8dee2aaSAndroid Build Coastguard Worker h3*coeffs[0][0] + h2*coeffs[1][0] + h*coeffs[2][0], 296*c8dee2aaSAndroid Build Coastguard Worker h3*coeffs[0][1] + h2*coeffs[1][1] + h*coeffs[2][1], 297*c8dee2aaSAndroid Build Coastguard Worker ], 298*c8dee2aaSAndroid Build Coastguard Worker [ 299*c8dee2aaSAndroid Build Coastguard Worker fwDiff3[0] + 2*h2*coeffs[1][0], 300*c8dee2aaSAndroid Build Coastguard Worker fwDiff3[1] + 2*h2*coeffs[1][1], 301*c8dee2aaSAndroid Build Coastguard Worker ], 302*c8dee2aaSAndroid Build Coastguard Worker fwDiff3, 303*c8dee2aaSAndroid Build Coastguard Worker ]; 304*c8dee2aaSAndroid Build Coastguard Worker 305*c8dee2aaSAndroid Build Coastguard Worker let verts = []; 306*c8dee2aaSAndroid Build Coastguard Worker 307*c8dee2aaSAndroid Build Coastguard Worker for (let i = 0; i <= divisions; ++i) { 308*c8dee2aaSAndroid Build Coastguard Worker verts.push(fwDiff[0]); 309*c8dee2aaSAndroid Build Coastguard Worker fwDiff[0] = [ fwDiff[0][0] + fwDiff[1][0], fwDiff[0][1] + fwDiff[1][1] ]; 310*c8dee2aaSAndroid Build Coastguard Worker fwDiff[1] = [ fwDiff[1][0] + fwDiff[2][0], fwDiff[1][1] + fwDiff[2][1] ]; 311*c8dee2aaSAndroid Build Coastguard Worker fwDiff[2] = [ fwDiff[2][0] + fwDiff[3][0], fwDiff[2][1] + fwDiff[3][1] ]; 312*c8dee2aaSAndroid Build Coastguard Worker } 313*c8dee2aaSAndroid Build Coastguard Worker 314*c8dee2aaSAndroid Build Coastguard Worker return verts; 315*c8dee2aaSAndroid Build Coastguard Worker } 316*c8dee2aaSAndroid Build Coastguard Worker 317*c8dee2aaSAndroid Build Coastguard Worker // Based on https://github.com/google/skia/blob/de56f293eb41d65786b9e6224fdf9a4702b30f51/src/utils/SkPatchUtils.cpp#L256 318*c8dee2aaSAndroid Build Coastguard Worker samplePatch(lod, verts) { 319*c8dee2aaSAndroid Build Coastguard Worker const top_verts = this.sampleCubic([ 1, 2, 3, 4 ], lod), 320*c8dee2aaSAndroid Build Coastguard Worker right_verts = this.sampleCubic([ 4, 5, 6, 7 ], lod), 321*c8dee2aaSAndroid Build Coastguard Worker bottom_verts = this.sampleCubic([ 10, 9, 8, 7 ], lod), 322*c8dee2aaSAndroid Build Coastguard Worker left_verts = this.sampleCubic([ 1, 0, 11, 10 ], lod); 323*c8dee2aaSAndroid Build Coastguard Worker 324*c8dee2aaSAndroid Build Coastguard Worker let i = 0; 325*c8dee2aaSAndroid Build Coastguard Worker for (let y = 0; y < lod; ++y) { 326*c8dee2aaSAndroid Build Coastguard Worker const v = y/(lod - 1), 327*c8dee2aaSAndroid Build Coastguard Worker left = left_verts[y], 328*c8dee2aaSAndroid Build Coastguard Worker right = right_verts[y]; 329*c8dee2aaSAndroid Build Coastguard Worker 330*c8dee2aaSAndroid Build Coastguard Worker for (let x = 0; x < lod; ++x) { 331*c8dee2aaSAndroid Build Coastguard Worker const u = x/(lod - 1), 332*c8dee2aaSAndroid Build Coastguard Worker top = top_verts[x], 333*c8dee2aaSAndroid Build Coastguard Worker bottom = bottom_verts[x], 334*c8dee2aaSAndroid Build Coastguard Worker 335*c8dee2aaSAndroid Build Coastguard Worker s0 = [ 336*c8dee2aaSAndroid Build Coastguard Worker (1 - v)*top[0] + v*bottom[0], 337*c8dee2aaSAndroid Build Coastguard Worker (1 - v)*top[1] + v*bottom[1], 338*c8dee2aaSAndroid Build Coastguard Worker ], 339*c8dee2aaSAndroid Build Coastguard Worker s1 = [ 340*c8dee2aaSAndroid Build Coastguard Worker (1 - u)*left[0] + u*right[0], 341*c8dee2aaSAndroid Build Coastguard Worker (1 - u)*left[1] + u*right[1], 342*c8dee2aaSAndroid Build Coastguard Worker ], 343*c8dee2aaSAndroid Build Coastguard Worker s2 = [ 344*c8dee2aaSAndroid Build Coastguard Worker (1 - v)*((1 - u)*this.controls[ 1].pos[0] + u*this.controls[4].pos[0]) + 345*c8dee2aaSAndroid Build Coastguard Worker v*((1 - u)*this.controls[10].pos[0] + u*this.controls[7].pos[0]), 346*c8dee2aaSAndroid Build Coastguard Worker (1 - v)*((1 - u)*this.controls[ 1].pos[1] + u*this.controls[4].pos[1]) + 347*c8dee2aaSAndroid Build Coastguard Worker v*((1 - u)*this.controls[10].pos[1] + u*this.controls[7].pos[1]), 348*c8dee2aaSAndroid Build Coastguard Worker ]; 349*c8dee2aaSAndroid Build Coastguard Worker 350*c8dee2aaSAndroid Build Coastguard Worker verts[i++] = s0[0] + s1[0] - s2[0]; 351*c8dee2aaSAndroid Build Coastguard Worker verts[i++] = s0[1] + s1[1] - s2[1]; 352*c8dee2aaSAndroid Build Coastguard Worker } 353*c8dee2aaSAndroid Build Coastguard Worker } 354*c8dee2aaSAndroid Build Coastguard Worker } 355*c8dee2aaSAndroid Build Coastguard Worker } 356*c8dee2aaSAndroid Build Coastguard Worker 357*c8dee2aaSAndroid Build Coastguard Worker class CKRenderer { 358*c8dee2aaSAndroid Build Coastguard Worker constructor(ck, img, canvasElement) { 359*c8dee2aaSAndroid Build Coastguard Worker this.ck = ck; 360*c8dee2aaSAndroid Build Coastguard Worker this.surface = ck.MakeCanvasSurface(canvasElement); 361*c8dee2aaSAndroid Build Coastguard Worker this.meshPaint = new ck.Paint(); 362*c8dee2aaSAndroid Build Coastguard Worker 363*c8dee2aaSAndroid Build Coastguard Worker // UVs are normalized, so we scale the image shader down to 1x1. 364*c8dee2aaSAndroid Build Coastguard Worker const skimg = ck.MakeImageFromCanvasImageSource(img); 365*c8dee2aaSAndroid Build Coastguard Worker const localMatrix = [1/skimg.width(), 0, 0, 366*c8dee2aaSAndroid Build Coastguard Worker 0, 1/skimg.height(), 0, 367*c8dee2aaSAndroid Build Coastguard Worker 0, 0, 1]; 368*c8dee2aaSAndroid Build Coastguard Worker 369*c8dee2aaSAndroid Build Coastguard Worker this.meshPaint.setShader(skimg.makeShaderOptions(ck.TileMode.Decal, 370*c8dee2aaSAndroid Build Coastguard Worker ck.TileMode.Decal, 371*c8dee2aaSAndroid Build Coastguard Worker ck.FilterMode.Linear, 372*c8dee2aaSAndroid Build Coastguard Worker ck.MipmapMode.None, 373*c8dee2aaSAndroid Build Coastguard Worker localMatrix)); 374*c8dee2aaSAndroid Build Coastguard Worker 375*c8dee2aaSAndroid Build Coastguard Worker this.gridPaint = new ck.Paint(); 376*c8dee2aaSAndroid Build Coastguard Worker this.gridPaint.setColor(ck.BLUE); 377*c8dee2aaSAndroid Build Coastguard Worker this.gridPaint.setAntiAlias(true); 378*c8dee2aaSAndroid Build Coastguard Worker this.gridPaint.setStyle(ck.PaintStyle.Stroke); 379*c8dee2aaSAndroid Build Coastguard Worker 380*c8dee2aaSAndroid Build Coastguard Worker this.controlsPaint = new ck.Paint(); 381*c8dee2aaSAndroid Build Coastguard Worker this.controlsPaint.setAntiAlias(true); 382*c8dee2aaSAndroid Build Coastguard Worker this.controlsPaint.setStyle(ck.PaintStyle.Fill); 383*c8dee2aaSAndroid Build Coastguard Worker } 384*c8dee2aaSAndroid Build Coastguard Worker 385*c8dee2aaSAndroid Build Coastguard Worker // Unlike the native renderer, CK drawVertices() takes typed arrays directly - so 386*c8dee2aaSAndroid Build Coastguard Worker // we don't need to allocate separate buffers. 387*c8dee2aaSAndroid Build Coastguard Worker makeVertexBuffer(buf) { return buf; } 388*c8dee2aaSAndroid Build Coastguard Worker makeUVBuffer (buf) { return buf; } 389*c8dee2aaSAndroid Build Coastguard Worker makeIndexBuffer (buf) { return buf; } 390*c8dee2aaSAndroid Build Coastguard Worker 391*c8dee2aaSAndroid Build Coastguard Worker meshPath(mesh) { 392*c8dee2aaSAndroid Build Coastguard Worker // 4 commands per triangle, 3 floats per cmd 393*c8dee2aaSAndroid Build Coastguard Worker const cmds = new Float32Array(mesh.indices.length*12); 394*c8dee2aaSAndroid Build Coastguard Worker let ci = 0; 395*c8dee2aaSAndroid Build Coastguard Worker mesh.generateTriangles((x0, y0, x1, y1, x2, y2) => { 396*c8dee2aaSAndroid Build Coastguard Worker cmds[ci++] = this.ck.MOVE_VERB; cmds[ci++] = x0; cmds[ci++] = y0; 397*c8dee2aaSAndroid Build Coastguard Worker cmds[ci++] = this.ck.LINE_VERB; cmds[ci++] = x1; cmds[ci++] = y1; 398*c8dee2aaSAndroid Build Coastguard Worker cmds[ci++] = this.ck.LINE_VERB; cmds[ci++] = x2; cmds[ci++] = y2; 399*c8dee2aaSAndroid Build Coastguard Worker cmds[ci++] = this.ck.LINE_VERB; cmds[ci++] = x0; cmds[ci++] = y0; 400*c8dee2aaSAndroid Build Coastguard Worker }); 401*c8dee2aaSAndroid Build Coastguard Worker return this.ck.Path.MakeFromCmds(cmds); 402*c8dee2aaSAndroid Build Coastguard Worker } 403*c8dee2aaSAndroid Build Coastguard Worker 404*c8dee2aaSAndroid Build Coastguard Worker drawMesh(mesh, ctrls) { 405*c8dee2aaSAndroid Build Coastguard Worker const vertices = this.ck.MakeVertices(this.ck.VertexMode.Triangles, 406*c8dee2aaSAndroid Build Coastguard Worker this.makeVertexBuffer(mesh.animated_verts), 407*c8dee2aaSAndroid Build Coastguard Worker mesh.uvBuffer, null, mesh.indexBuffer, false); 408*c8dee2aaSAndroid Build Coastguard Worker 409*c8dee2aaSAndroid Build Coastguard Worker const canvas = this.surface.getCanvas(); 410*c8dee2aaSAndroid Build Coastguard Worker const w = this.surface.width(), 411*c8dee2aaSAndroid Build Coastguard Worker h = this.surface.height(); 412*c8dee2aaSAndroid Build Coastguard Worker 413*c8dee2aaSAndroid Build Coastguard Worker canvas.save(); 414*c8dee2aaSAndroid Build Coastguard Worker canvas.translate(w*(1-meshScale)*0.5, h*(1-meshScale)*0.5); 415*c8dee2aaSAndroid Build Coastguard Worker canvas.scale(w*meshScale, h*meshScale); 416*c8dee2aaSAndroid Build Coastguard Worker 417*c8dee2aaSAndroid Build Coastguard Worker canvas.drawVertices(vertices, this.ck.BlendMode.Dst, this.meshPaint); 418*c8dee2aaSAndroid Build Coastguard Worker 419*c8dee2aaSAndroid Build Coastguard Worker if (showMeshUI.checked) { 420*c8dee2aaSAndroid Build Coastguard Worker canvas.drawPath(this.meshPath(mesh), this.gridPaint); 421*c8dee2aaSAndroid Build Coastguard Worker } 422*c8dee2aaSAndroid Build Coastguard Worker 423*c8dee2aaSAndroid Build Coastguard Worker ctrls?.drawUI( 424*c8dee2aaSAndroid Build Coastguard Worker (p0, p1, color) => { 425*c8dee2aaSAndroid Build Coastguard Worker this.controlsPaint.setColor(this.ck.parseColorString(color)); 426*c8dee2aaSAndroid Build Coastguard Worker canvas.drawLine(p0[0], p0[1], p1[0], p1[1], this.controlsPaint); 427*c8dee2aaSAndroid Build Coastguard Worker }, 428*c8dee2aaSAndroid Build Coastguard Worker (c, r, color) => { 429*c8dee2aaSAndroid Build Coastguard Worker this.controlsPaint.setColor(this.ck.parseColorString(color)); 430*c8dee2aaSAndroid Build Coastguard Worker canvas.drawCircle(c[0], c[1], r, this.controlsPaint); 431*c8dee2aaSAndroid Build Coastguard Worker } 432*c8dee2aaSAndroid Build Coastguard Worker ); 433*c8dee2aaSAndroid Build Coastguard Worker canvas.restore(); 434*c8dee2aaSAndroid Build Coastguard Worker this.surface.flush(); 435*c8dee2aaSAndroid Build Coastguard Worker } 436*c8dee2aaSAndroid Build Coastguard Worker } 437*c8dee2aaSAndroid Build Coastguard Worker 438*c8dee2aaSAndroid Build Coastguard Worker class NativeRenderer { 439*c8dee2aaSAndroid Build Coastguard Worker constructor(img, canvasElement) { 440*c8dee2aaSAndroid Build Coastguard Worker this.img = img; 441*c8dee2aaSAndroid Build Coastguard Worker this.ctx = canvasElement.getContext("2d"); 442*c8dee2aaSAndroid Build Coastguard Worker } 443*c8dee2aaSAndroid Build Coastguard Worker 444*c8dee2aaSAndroid Build Coastguard Worker // New Mesh2D API: https://github.com/fserb/canvas2D/blob/master/spec/mesh2d.md#mesh2d-api 445*c8dee2aaSAndroid Build Coastguard Worker makeVertexBuffer(buf) { return this.ctx.createMesh2DVertexBuffer(buf); } 446*c8dee2aaSAndroid Build Coastguard Worker makeUVBuffer(buf) { 447*c8dee2aaSAndroid Build Coastguard Worker return this.ctx.createMesh2DUVBuffer(buf); 448*c8dee2aaSAndroid Build Coastguard Worker } 449*c8dee2aaSAndroid Build Coastguard Worker makeIndexBuffer(buf) { return this.ctx.createMesh2DIndexBuffer(buf); } 450*c8dee2aaSAndroid Build Coastguard Worker 451*c8dee2aaSAndroid Build Coastguard Worker meshPath(mesh) { 452*c8dee2aaSAndroid Build Coastguard Worker const path = new Path2D(); 453*c8dee2aaSAndroid Build Coastguard Worker mesh.generateTriangles((x0, y0, x1, y1, x2, y2) => { 454*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(x0, y0); 455*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(x1, y1); 456*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(x2, y2); 457*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(x0, y0); 458*c8dee2aaSAndroid Build Coastguard Worker }); 459*c8dee2aaSAndroid Build Coastguard Worker return path; 460*c8dee2aaSAndroid Build Coastguard Worker } 461*c8dee2aaSAndroid Build Coastguard Worker 462*c8dee2aaSAndroid Build Coastguard Worker drawMesh(mesh, ctrls) { 463*c8dee2aaSAndroid Build Coastguard Worker const vbuf = this.ctx.createMesh2DVertexBuffer(mesh.animated_verts); 464*c8dee2aaSAndroid Build Coastguard Worker const w = canvas2d.width, 465*c8dee2aaSAndroid Build Coastguard Worker h = canvas2d.height; 466*c8dee2aaSAndroid Build Coastguard Worker 467*c8dee2aaSAndroid Build Coastguard Worker this.ctx.clearRect(0, 0, canvas2d.width, canvas2d.height); 468*c8dee2aaSAndroid Build Coastguard Worker this.ctx.save(); 469*c8dee2aaSAndroid Build Coastguard Worker this.ctx.translate(w*(1-meshScale)*0.5, h*(1-meshScale)*0.5); 470*c8dee2aaSAndroid Build Coastguard Worker this.ctx.scale(w*meshScale, h*meshScale); 471*c8dee2aaSAndroid Build Coastguard Worker 472*c8dee2aaSAndroid Build Coastguard Worker this.ctx.drawMesh(vbuf, mesh.uvBuffer, mesh.indexBuffer, this.img); 473*c8dee2aaSAndroid Build Coastguard Worker 474*c8dee2aaSAndroid Build Coastguard Worker if (showMeshUI.checked) { 475*c8dee2aaSAndroid Build Coastguard Worker this.ctx.strokeStyle = "blue"; 476*c8dee2aaSAndroid Build Coastguard Worker this.ctx.lineWidth = 0.001; 477*c8dee2aaSAndroid Build Coastguard Worker this.ctx.stroke(this.meshPath(mesh)); 478*c8dee2aaSAndroid Build Coastguard Worker } 479*c8dee2aaSAndroid Build Coastguard Worker 480*c8dee2aaSAndroid Build Coastguard Worker ctrls?.drawUI( 481*c8dee2aaSAndroid Build Coastguard Worker (p0, p1, color) => { 482*c8dee2aaSAndroid Build Coastguard Worker this.ctx.lineWidth = 0.001; 483*c8dee2aaSAndroid Build Coastguard Worker this.ctx.strokeStyle = color; 484*c8dee2aaSAndroid Build Coastguard Worker this.ctx.beginPath(); 485*c8dee2aaSAndroid Build Coastguard Worker this.ctx.moveTo(p0[0], p0[1]); 486*c8dee2aaSAndroid Build Coastguard Worker this.ctx.lineTo(p1[0], p1[1]); 487*c8dee2aaSAndroid Build Coastguard Worker this.ctx.stroke(); 488*c8dee2aaSAndroid Build Coastguard Worker }, 489*c8dee2aaSAndroid Build Coastguard Worker (c, r, color) => { 490*c8dee2aaSAndroid Build Coastguard Worker this.ctx.fillStyle = color; 491*c8dee2aaSAndroid Build Coastguard Worker this.ctx.beginPath(); 492*c8dee2aaSAndroid Build Coastguard Worker this.ctx.arc(c[0], c[1], r, 0, 2*Math.PI); 493*c8dee2aaSAndroid Build Coastguard Worker this.ctx.fill(); 494*c8dee2aaSAndroid Build Coastguard Worker } 495*c8dee2aaSAndroid Build Coastguard Worker ); 496*c8dee2aaSAndroid Build Coastguard Worker this.ctx.restore(); 497*c8dee2aaSAndroid Build Coastguard Worker } 498*c8dee2aaSAndroid Build Coastguard Worker } 499*c8dee2aaSAndroid Build Coastguard Worker 500*c8dee2aaSAndroid Build Coastguard Worker function squircleAnimator(verts, animated_verts, t) { 501*c8dee2aaSAndroid Build Coastguard Worker function lerp(a, b, t) { return a + t*(b - a); } 502*c8dee2aaSAndroid Build Coastguard Worker 503*c8dee2aaSAndroid Build Coastguard Worker for (let i = 0; i < verts.length; i += 2) { 504*c8dee2aaSAndroid Build Coastguard Worker const uvx = verts[i + 0] - 0.5, 505*c8dee2aaSAndroid Build Coastguard Worker uvy = verts[i + 1] - 0.5, 506*c8dee2aaSAndroid Build Coastguard Worker d = Math.sqrt(uvx*uvx + uvy*uvy)*0.5/Math.max(Math.abs(uvx), Math.abs(uvy)), 507*c8dee2aaSAndroid Build Coastguard Worker s = d > 0 ? lerp(1, (0.5/ d), t) : 1; 508*c8dee2aaSAndroid Build Coastguard Worker animated_verts[i + 0] = uvx*s + 0.5; 509*c8dee2aaSAndroid Build Coastguard Worker animated_verts[i + 1] = uvy*s + 0.5; 510*c8dee2aaSAndroid Build Coastguard Worker } 511*c8dee2aaSAndroid Build Coastguard Worker } 512*c8dee2aaSAndroid Build Coastguard Worker 513*c8dee2aaSAndroid Build Coastguard Worker function twirlAnimator(verts, animated_verts, t) { 514*c8dee2aaSAndroid Build Coastguard Worker const kMaxRotate = Math.PI*4; 515*c8dee2aaSAndroid Build Coastguard Worker 516*c8dee2aaSAndroid Build Coastguard Worker for (let i = 0; i < verts.length; i += 2) { 517*c8dee2aaSAndroid Build Coastguard Worker const uvx = verts[i + 0] - 0.5, 518*c8dee2aaSAndroid Build Coastguard Worker uvy = verts[i + 1] - 0.5, 519*c8dee2aaSAndroid Build Coastguard Worker r = Math.sqrt(uvx*uvx + uvy*uvy), 520*c8dee2aaSAndroid Build Coastguard Worker a = kMaxRotate * r * t; 521*c8dee2aaSAndroid Build Coastguard Worker animated_verts[i + 0] = uvx*Math.cos(a) - uvy*Math.sin(a) + 0.5; 522*c8dee2aaSAndroid Build Coastguard Worker animated_verts[i + 1] = uvy*Math.cos(a) + uvx*Math.sin(a) + 0.5; 523*c8dee2aaSAndroid Build Coastguard Worker } 524*c8dee2aaSAndroid Build Coastguard Worker } 525*c8dee2aaSAndroid Build Coastguard Worker 526*c8dee2aaSAndroid Build Coastguard Worker function wiggleAnimator(verts, animated_verts, t) { 527*c8dee2aaSAndroid Build Coastguard Worker const radius = t*0.2/(Math.sqrt(verts.length/2) - 1); 528*c8dee2aaSAndroid Build Coastguard Worker 529*c8dee2aaSAndroid Build Coastguard Worker for (let i = 0; i < verts.length; i += 2) { 530*c8dee2aaSAndroid Build Coastguard Worker const phase = i*Math.PI*0.1505; 531*c8dee2aaSAndroid Build Coastguard Worker const angle = phase + t*Math.PI*2; 532*c8dee2aaSAndroid Build Coastguard Worker animated_verts[i + 0] = verts[i + 0] + radius*Math.cos(angle); 533*c8dee2aaSAndroid Build Coastguard Worker animated_verts[i + 1] = verts[i + 1] + radius*Math.sin(angle); 534*c8dee2aaSAndroid Build Coastguard Worker } 535*c8dee2aaSAndroid Build Coastguard Worker } 536*c8dee2aaSAndroid Build Coastguard Worker 537*c8dee2aaSAndroid Build Coastguard Worker function cylinderAnimator(verts, animated_verts, t) { 538*c8dee2aaSAndroid Build Coastguard Worker const kCylRadius = .2; 539*c8dee2aaSAndroid Build Coastguard Worker const cyl_pos = t; 540*c8dee2aaSAndroid Build Coastguard Worker 541*c8dee2aaSAndroid Build Coastguard Worker for (let i = 0; i < verts.length; i += 2) { 542*c8dee2aaSAndroid Build Coastguard Worker const uvx = verts[i + 0], 543*c8dee2aaSAndroid Build Coastguard Worker uvy = verts[i + 1]; 544*c8dee2aaSAndroid Build Coastguard Worker 545*c8dee2aaSAndroid Build Coastguard Worker if (uvx <= cyl_pos) { 546*c8dee2aaSAndroid Build Coastguard Worker animated_verts[i + 0] = uvx; 547*c8dee2aaSAndroid Build Coastguard Worker animated_verts[i + 1] = uvy; 548*c8dee2aaSAndroid Build Coastguard Worker continue; 549*c8dee2aaSAndroid Build Coastguard Worker } 550*c8dee2aaSAndroid Build Coastguard Worker 551*c8dee2aaSAndroid Build Coastguard Worker const arc_len = uvx - cyl_pos, 552*c8dee2aaSAndroid Build Coastguard Worker arc_ang = arc_len/kCylRadius; 553*c8dee2aaSAndroid Build Coastguard Worker 554*c8dee2aaSAndroid Build Coastguard Worker animated_verts[i + 0] = cyl_pos + Math.sin(arc_ang)*kCylRadius; 555*c8dee2aaSAndroid Build Coastguard Worker animated_verts[i + 1] = uvy; 556*c8dee2aaSAndroid Build Coastguard Worker } 557*c8dee2aaSAndroid Build Coastguard Worker } 558*c8dee2aaSAndroid Build Coastguard Worker 559*c8dee2aaSAndroid Build Coastguard Worker function drawFrame() { 560*c8dee2aaSAndroid Build Coastguard Worker meshData.animate(animator); 561*c8dee2aaSAndroid Build Coastguard Worker currentRenderer.drawMesh(meshData, patchControls); 562*c8dee2aaSAndroid Build Coastguard Worker requestAnimationFrame(drawFrame); 563*c8dee2aaSAndroid Build Coastguard Worker } 564*c8dee2aaSAndroid Build Coastguard Worker 565*c8dee2aaSAndroid Build Coastguard Worker function switchRenderer(renderer) { 566*c8dee2aaSAndroid Build Coastguard Worker currentRenderer = renderer; 567*c8dee2aaSAndroid Build Coastguard Worker meshData = new MeshData(parseInt(lodSelectUI.value), currentRenderer); 568*c8dee2aaSAndroid Build Coastguard Worker 569*c8dee2aaSAndroid Build Coastguard Worker const showCanvas = renderer == ckRenderer ? canvas3d : canvas2d; 570*c8dee2aaSAndroid Build Coastguard Worker const hideCanvas = renderer == ckRenderer ? canvas2d : canvas3d; 571*c8dee2aaSAndroid Build Coastguard Worker showCanvas.style.display = 'block'; 572*c8dee2aaSAndroid Build Coastguard Worker hideCanvas.style.display = 'none'; 573*c8dee2aaSAndroid Build Coastguard Worker 574*c8dee2aaSAndroid Build Coastguard Worker patchControls?.updateVerts(); 575*c8dee2aaSAndroid Build Coastguard Worker } 576*c8dee2aaSAndroid Build Coastguard Worker 577*c8dee2aaSAndroid Build Coastguard Worker const canvas2d = document.getElementById("canvas2d"); 578*c8dee2aaSAndroid Build Coastguard Worker const canvas3d = document.getElementById("canvas3d"); 579*c8dee2aaSAndroid Build Coastguard Worker const hasMesh2DAPI = 'drawMesh' in CanvasRenderingContext2D.prototype; 580*c8dee2aaSAndroid Build Coastguard Worker const showMeshUI = document.getElementById("show_mesh"); 581*c8dee2aaSAndroid Build Coastguard Worker const lodSelectUI = document.getElementById("lod"); 582*c8dee2aaSAndroid Build Coastguard Worker const animatorSelectUI = document.getElementById("animator"); 583*c8dee2aaSAndroid Build Coastguard Worker const rendererSelectUI = document.getElementById("renderer"); 584*c8dee2aaSAndroid Build Coastguard Worker 585*c8dee2aaSAndroid Build Coastguard Worker const meshScale = 0.75; 586*c8dee2aaSAndroid Build Coastguard Worker 587*c8dee2aaSAndroid Build Coastguard Worker const loadCK = CanvasKitInit({ locateFile: (file) => 'https://demos.skia.org/demo/mesh2d/' + file }); 588*c8dee2aaSAndroid Build Coastguard Worker const loadImage = new Promise(resolve => { 589*c8dee2aaSAndroid Build Coastguard Worker const image = new Image(); 590*c8dee2aaSAndroid Build Coastguard Worker image.addEventListener('load', () => { resolve(image); }); 591*c8dee2aaSAndroid Build Coastguard Worker image.src = 'baby_tux.png'; 592*c8dee2aaSAndroid Build Coastguard Worker }); 593*c8dee2aaSAndroid Build Coastguard Worker 594*c8dee2aaSAndroid Build Coastguard Worker var ckRenderer; 595*c8dee2aaSAndroid Build Coastguard Worker var nativeRenderer; 596*c8dee2aaSAndroid Build Coastguard Worker var currentRenderer; 597*c8dee2aaSAndroid Build Coastguard Worker var meshData; 598*c8dee2aaSAndroid Build Coastguard Worker var image; 599*c8dee2aaSAndroid Build Coastguard Worker 600*c8dee2aaSAndroid Build Coastguard Worker const timeBase = Date.now(); 601*c8dee2aaSAndroid Build Coastguard Worker 602*c8dee2aaSAndroid Build Coastguard Worker var animator = window[animatorSelectUI.value]; 603*c8dee2aaSAndroid Build Coastguard Worker var patchControls = animator ? null : new PatchControls(); 604*c8dee2aaSAndroid Build Coastguard Worker 605*c8dee2aaSAndroid Build Coastguard Worker Promise.all([loadCK, loadImage]).then(([ck, img]) => { 606*c8dee2aaSAndroid Build Coastguard Worker ckRenderer = new CKRenderer(ck, img, canvas3d); 607*c8dee2aaSAndroid Build Coastguard Worker nativeRenderer = 'drawMesh' in CanvasRenderingContext2D.prototype 608*c8dee2aaSAndroid Build Coastguard Worker ? new NativeRenderer(img, canvas2d) 609*c8dee2aaSAndroid Build Coastguard Worker : null; 610*c8dee2aaSAndroid Build Coastguard Worker 611*c8dee2aaSAndroid Build Coastguard Worker rendererSelectUI.disabled = !nativeRenderer; 612*c8dee2aaSAndroid Build Coastguard Worker rendererSelectUI.value = nativeRenderer ? "nativeRenderer" : "ckRenderer"; 613*c8dee2aaSAndroid Build Coastguard Worker 614*c8dee2aaSAndroid Build Coastguard Worker document.getElementById('loader').style.display = 'none'; 615*c8dee2aaSAndroid Build Coastguard Worker switchRenderer(nativeRenderer ? nativeRenderer : ckRenderer); 616*c8dee2aaSAndroid Build Coastguard Worker 617*c8dee2aaSAndroid Build Coastguard Worker requestAnimationFrame(drawFrame); 618*c8dee2aaSAndroid Build Coastguard Worker }); 619*c8dee2aaSAndroid Build Coastguard Worker 620*c8dee2aaSAndroid Build Coastguard Worker lodSelectUI.onchange = () => { switchRenderer(currentRenderer); } 621*c8dee2aaSAndroid Build Coastguard Worker rendererSelectUI.onchange = () => { switchRenderer(window[rendererSelectUI.value]); } 622*c8dee2aaSAndroid Build Coastguard Worker animatorSelectUI.onchange = () => { 623*c8dee2aaSAndroid Build Coastguard Worker animator = window[animatorSelectUI.value]; 624*c8dee2aaSAndroid Build Coastguard Worker patchControls = animator ? null : new PatchControls(); 625*c8dee2aaSAndroid Build Coastguard Worker patchControls?.updateVerts(); 626*c8dee2aaSAndroid Build Coastguard Worker } 627*c8dee2aaSAndroid Build Coastguard Worker 628*c8dee2aaSAndroid Build Coastguard Worker const cwrapper = document.getElementById('canvas_wrapper'); 629*c8dee2aaSAndroid Build Coastguard Worker cwrapper.onmousedown = (ev) => { patchControls?.onMouseDown(ev); } 630*c8dee2aaSAndroid Build Coastguard Worker cwrapper.onmousemove = (ev) => { patchControls?.onMouseMove(ev); } 631*c8dee2aaSAndroid Build Coastguard Worker cwrapper.onmouseup = (ev) => { patchControls?.onMouseUp(ev); } 632*c8dee2aaSAndroid Build Coastguard Worker</script> 633