Best JavaScript code snippet using fast-check-monorepo
world-render.js
Source: world-render.js 
1// Copyright 2011-2012 Kevin Reid under the terms of the MIT License as detailed2// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.3(function () {4  "use strict";5  6  var AAB = cubes.util.AAB;7  var Blockset = cubes.Blockset;8  var CubeRotation = cubes.util.CubeRotation;9  var DirtyQueue = cubes.util.DirtyQueue;10  var IntVectorMap = cubes.util.IntVectorMap;11  var measuring = cubes.measuring;12  var mod = cubes.util.mod;13  var round = Math.round;14  var UNIT_PX = cubes.util.UNIT_PX;15  var UNIT_PY = cubes.util.UNIT_PY;16  var ZEROVEC = cubes.util.ZEROVEC;17  18  // The side length of the chunks the world is broken into for rendering.19  // Smaller chunks are faster to update when the world changes, but have a higher per-frame cost.20  var LIGHT_TEXTURE_SIZE = 16; // must be power of 221  var CHUNKSIZE = LIGHT_TEXTURE_SIZE - 2;22  23  // 3D Euclidean distance, squared (for efficiency).24  function dist3sq(v1, v2) {25    var x = v1[0] - v2[0];26    var y = v1[1] - v2[1];27    var z = v1[2] - v2[2];28    return x*x + y*y + z*z;29  }30  31  var angleStep = Math.PI/2;32  function discreteRotation(angle) {33    return Math.round(angle/angleStep) * angleStep;34  }35  var rotationsByCode = CubeRotation.byCode;36  var distanceInfoCache = {}, lastSeenRenderDistance = null;37  function renderDistanceInfo(newRenderDistance) {38    // TODO add some visibility into whether this one-element cache is thrashing39    if (newRenderDistance !== lastSeenRenderDistance) {40      //if (typeof console !== "undefined") console.log("Building renderDistanceInfo for " + newRenderDistance.toFixed(1));41      lastSeenRenderDistance = newRenderDistance;42      43      // The distance in chunk-lengths at which chunks are visible44      var chunkDistance = Math.ceil(newRenderDistance/CHUNKSIZE);45      46      // The squared distance at which chunks should be included.47      // The offset of CHUNKSIZE is to account for the origin of a chunk being at one corner.48      var boundSquared = Math.pow(newRenderDistance + CHUNKSIZE, 2);49      distanceInfoCache.addChunkDistanceSquared = boundSquared;50      // The distance at which invisible chunks are dropped from memory. Semi-arbitrary figure...51      distanceInfoCache.dropChunkDistanceSquared = Math.pow(newRenderDistance + 2*CHUNKSIZE, 2);52      // A static table of the offsets of the chunks visible from the player location53      var nearChunkOrder = [];54      for (var x = -chunkDistance-1; x <= chunkDistance; x++)55      for (var y = -chunkDistance-1; y <= chunkDistance; y++)56      for (var z = -chunkDistance-1; z <= chunkDistance; z++) {57        var v = [x*CHUNKSIZE,y*CHUNKSIZE,z*CHUNKSIZE];58        if (dist3sq(v, ZEROVEC) <= boundSquared) {59          nearChunkOrder.push(v);60        }61      }62      nearChunkOrder.sort(function (a,b) {63        return dist3sq(a, ZEROVEC) - dist3sq(b, ZEROVEC);64      });65      distanceInfoCache.nearChunkOrder = Object.freeze(nearChunkOrder);66    }67    return distanceInfoCache;68  }69  70  function WorldRenderer(world, getViewPosition, renderer, optAudio, scheduleDraw, showBoundaries) {71    var gl = renderer.context;72    var config = renderer.config; // TODO eliminate need for this73    74    // World properties cached and used by chunk calculation75    var wx = world.wx;76    var wy = world.wy;77    var wz = world.wz;78    var g = world.g;79    var rawBlocks = world.raw;80    var rawRotations = world.rawRotations;81    var rawLighting = world.rawLighting;82    var inBounds = world.inBounds;83    var lightOutside = world.lightOutside;84    85    // Table of all world rendering chunks which have RenderBundles created, indexed by [x,y,z] of the low-side coordinates (i.e. divisible by CHUNKSIZE).86    var chunks = new IntVectorMap();87    88    var nonemptyChunks = new IntVectorMap();89    function compareByPlayerDistance(a,b) {90      return dist3sq(a, playerChunk) - dist3sq(b, playerChunk);91    }92    // Queue of chunks to rerender. Array (first-to-do at the end); each element is [x,z] where x and z are the low coordinates of the chunk.93    var dirtyChunks = new DirtyQueue(compareByPlayerDistance);94    // Queue of chunks to render for the first time. Distinguished from dirtyChunks in that it can be flushed if the view changes.95    var addChunks = new DirtyQueue(compareByPlayerDistance);96    97    // The origin of the chunk which the player is currently in. Changes to this are used to decide to recompute chunk visibility.98    var playerChunk = null;99    100    // Like chunks, but for circuits. Indexed by the circuit origin block.101    var circuitRenderers = new IntVectorMap();102    var blockset = world.blockset;103    104    // Cached blockset characteristics105    var tileSize = blockset.tileSize;106    var ID_EMPTY = Blockset.ID_EMPTY;107    108    var particles = [];109    110    var boundaryR = new renderer.RenderBundle(gl.LINES, null, function (vertices, normals, colors) {111      function common() {112        normals.push(0, 0, 0);113        normals.push(0, 0, 0);114        colors.push(0.5,0.5,0.5, 1);115        colors.push(0.5,0.5,0.5, 1);116      }117      var extent = 20;118      119      var vec = [];120      for (var dim = 0; dim < 3; dim++) {121        var ud = mod(dim+1,3);122        var vd = mod(dim+2,3);123        for (var u = 0; u < 2; u++)124        for (var v = 0; v < 2; v++) {125          vec[ud] = [world.wx, world.wy, world.wz][ud]*u;126          vec[vd] = [world.wx, world.wy, world.wz][vd]*v;127          vec[dim] = -extent;128          vertices.push(vec[0],vec[1],vec[2]);129          vec[dim] = [world.wx, world.wy, world.wz][dim] + extent;130          vertices.push(vec[0],vec[1],vec[2]);131          common();132        }133      }134    }, {aroundDraw: function (draw) {135      renderer.setLineWidth(1);136      draw();137    }});138    var textureDebugR = new renderer.RenderBundle(139        gl.TRIANGLE_STRIP,140        function () { return blockset.getRenderData(renderer).texture; },141        function (vertices, normals, texcoords) {142      var x = 1;143      var y = 1;144      var z = 0;145      vertices.push(-x, -y, z);146      vertices.push(x, -y, z);147      vertices.push(-x, y, z);148      vertices.push(x, y, z);149      150      normals.push(0,0,0);151      normals.push(0,0,0);152      normals.push(0,0,0);153      normals.push(0,0,0);154      texcoords.push(0, 1);155      texcoords.push(1, 1);156      texcoords.push(0, 0);157      texcoords.push(1, 0);158    }, {159      aroundDraw: function (draw) {160        var restoreView = renderer.saveView();161        renderer.setViewTo2D();162        gl.disable(gl.DEPTH_TEST); // TODO should be handled by renderer?163        gl.depthMask(false);164        draw();165        restoreView();166        gl.enable(gl.DEPTH_TEST);167        gl.depthMask(true);168      }169    });170    171    // --- methods, internals ---172    173    function deleteChunks() {174      chunks.forEachValue(function (chunk) {175        chunk.deleteResources();176      });177      circuitRenderers.forEachValue(function (cr) {178        cr.deleteResources();179      });180      181      chunks = new IntVectorMap();182      nonemptyChunks = new IntVectorMap();183      circuitRenderers = new IntVectorMap();184      dirtyChunks.clear();185      addChunks.clear();186    }187    188    function rerenderChunks() {189      dirtyChunks.clear();190      chunks.forEach(function (chunk, coords) {191        chunk.dirtyGeometry = true;192        dirtyChunks.enqueue(coords);193      });194    }195    196    var listenerBlockset = {197      interest: isAlive,198      // TODO: Optimize by rerendering only if render data (not just texture image) changed, and only199      // chunks containing the changed block ID?200      texturingChanged: dirtyAll,201      tableChanged: dirtyAll202    };203    204    var listenerRenderDistance = {205      interest: isAlive,206      changed: function (v) {207        playerChunk = null; // TODO kludge. The effect of this is to reevaluate which chunks are visible208        addChunks.clear();209        scheduleDraw();210      }211    };212    var listenerRedraw = {213      interest: isAlive,214      changed: function (v) {215        scheduleDraw();216      }217    };218    function deleteResources() {219      deleteChunks();220      textureDebugR.deleteResources();221      world.listen.cancel(listenerWorld);222      blockset.listen.cancel(listenerBlockset);223      config.renderDistance.listen.cancel(listenerRenderDistance);224      subWRs.forEach(function (record) {225        record.wr.deleteResources();226      });227      world = blockset = chunks = nonemptyChunks = dirtyChunks = addChunks = textureDebugR = null;228    }229    function isAlive() {230      // Are we still interested in notifications etc?231      return !!world;232    }233    this.deleteResources = deleteResources;234    function chunkIntersectsWorld(chunkOrigin) {235      var x = chunkOrigin[0];236      var y = chunkOrigin[1];237      var z = chunkOrigin[2];238      return x >= 0 && x - CHUNKSIZE < world.wx &&239             y >= 0 && y - CHUNKSIZE < world.wy &&240             z >= 0 && z - CHUNKSIZE < world.wz;241    }242    243    function chunkContaining(cube) {244      var x = cube[0];245      var y = cube[1];246      var z = cube[2];247      return chunks.get([248        x - mod(x, CHUNKSIZE),249        y - mod(y, CHUNKSIZE),250        z - mod(z, CHUNKSIZE)251      ]);252    }253    254    // x,z must be multiples of CHUNKSIZE255    function setDirtyChunk(x, y, z, dirtyType) {256      var k = [x, y, z];257      if (!chunkIntersectsWorld(k)) return;258      var chunk = chunks.get(k);259      if (chunk) {260        // This routine is used only for "this block changed", so if there is261        // not already a chunk, we don't create it.262        chunk[dirtyType] = true;263        dirtyChunks.enqueue(k);264      }265    }266    267    function dirtyChunksForBlock(cube, dirtyType) {268      if (!isAlive()) return;269      270      var x = cube[0];271      var y = cube[1];272      var z = cube[2];273      274      var xm = mod(x, CHUNKSIZE);275      var ym = mod(y, CHUNKSIZE);276      var zm = mod(z, CHUNKSIZE);277      x -= xm;278      y -= ym;279      z -= zm;280      281      setDirtyChunk(x,y,z, dirtyType);282      if (xm === 0)           setDirtyChunk(x-CHUNKSIZE,y,z, dirtyType);283      if (ym === 0)           setDirtyChunk(x,y-CHUNKSIZE,z, dirtyType);284      if (zm === 0)           setDirtyChunk(x,y,z-CHUNKSIZE, dirtyType);285      if (xm === CHUNKSIZE-1) setDirtyChunk(x+CHUNKSIZE,y,z, dirtyType);286      if (ym === CHUNKSIZE-1) setDirtyChunk(x,y+CHUNKSIZE,z, dirtyType);287      if (zm === CHUNKSIZE-1) setDirtyChunk(x,y,z+CHUNKSIZE, dirtyType);288      289      // TODO: This is actually "Schedule updateSomeChunks()" and shouldn't actually require a frame redraw unless the update does something in view290      scheduleDraw();291    }292    // entry points for change listeners293    function dirtyBlock(cube) {294      dirtyChunksForBlock(cube, "dirtyGeometry");295    }296    function relitBlock(cube) {297      // TODO: Do no light-texture work if lighting is disabled in config â but catch up when it is reenabled.298      dirtyChunksForBlock(cube, "dirtyLighting");299    }300    function dirtyAll() {301      if (!isAlive()) return;302      blockset = world.blockset;303      rerenderChunks();304    }305    function dirtyCircuit(circuit) {306      if (!isAlive()) return;307      var o = circuit.getOrigin();308      var r = circuitRenderers.get(o);309      if (r) {310        r.recompute();311      } else {312        addCircuits();313      }314    }315    316    function deletedCircuit(circuit) {317      if (!isAlive()) return;318      circuitRenderers.delete(circuit.getOrigin());319    }320    var listenerWorld = {321      interest: isAlive,322      dirtyBlock: dirtyBlock,323      relitBlock: relitBlock,324      bodiesChanged: scheduleDraw,325      dirtyAll: dirtyAll,326      dirtyCircuit: dirtyCircuit,327      deletedCircuit: deletedCircuit,328      changedBlockset: dirtyAll,329      transientEvent: function (cube, type, kind) {330        if (!isAlive()) return;331        332        if (optAudio) {333          optAudio.play(vec3.add([0.5,0.5,0.5], cube), type, kind, 1);334        }335        336        switch (kind) {337          case "destroy":338            addParticles(cube, true);339            break;340          case "create":341            addParticles(cube, false);342            break;343        }344      }345    };346    347    function addCircuits() {348      // Add circuits which are in viewing distance.349      // Note: This enumerates every circuit in the world. Currently, this is more efficient than the alternatives because there are not many circuits in typical data. When that changes, we should revisit this and use some type of spatial index to make it efficient. Testing per-block is *not* efficient.350      if (!playerChunk) return;351      var renderDistance = config.renderDistance.get();352      var rdi = renderDistanceInfo(renderDistance);353      world.getCircuits().forEach(function (circuit, origin) {354        if (dist3sq(origin, playerChunk) < rdi.addChunkDistanceSquared) {355          if (!circuitRenderers.get(origin)) {356            circuitRenderers.set(origin, makeCircuitRenderer(circuit));357          }358        }359      });360    }361    362    function updateSomeChunks() {363      // Determine if chunks' visibility to the player has changed364      var rdi = renderDistanceInfo(config.renderDistance.get());365      var pos = getViewPosition();366      var newPlayerChunk = [round(pos[0] - mod(pos[0], CHUNKSIZE)),367                            round(pos[1] - mod(pos[1], CHUNKSIZE)),368                            round(pos[2] - mod(pos[2], CHUNKSIZE))];369      if (playerChunk === null || newPlayerChunk[0] !== playerChunk[0]370                               || newPlayerChunk[1] !== playerChunk[1]371                               || newPlayerChunk[2] !== playerChunk[2]) {372        //console.log("nPC ", newPlayerChunk[0], newPlayerChunk[1], newPlayerChunk[2]);373        374        playerChunk = newPlayerChunk;375        376        // Add chunks which are in viewing distance.377        rdi.nearChunkOrder.forEach(function (offset) {378          var chunkKey = [playerChunk[0] + offset[0], playerChunk[1] + offset[1], playerChunk[2] + offset[2]];379          if (!chunks.has(chunkKey) && chunkIntersectsWorld(chunkKey)) {380            addChunks.enqueue(chunkKey, chunks.get(chunkKey));381          }382        });383        // Drop now-invisible chunks. Has a higher boundary so that we're not constantly reloading chunks if the player is moving back and forth.384        var dds = rdi.dropChunkDistanceSquared;385        chunks.forEach(function (chunk, chunkKey) {386          if (dist3sq(chunkKey, playerChunk) > dds) {387            chunk.deleteResources();388            chunks.delete(chunkKey);389            nonemptyChunks.delete(chunkKey);390          }391        });392        393        addCircuits();394        395        // Drop now-invisible circuits396        // TODO: This works off the origin, but circuits can be arbitrarily large so we should test against their AABB397        circuitRenderers.forEach(function (cr, cube) {398          if (dist3sq(cube, playerChunk) > dds) {399            cr.deleteResources();400            circuitRenderers.delete(cube);401          }402        });403      }404      405      // Update chunks from the queues.406      var deadline = Date.now() + (addChunks.size() > 30 ? 30 : 10);407      var count = 0;408      // Chunks to add409      while (addChunks.size() > 0 && Date.now() < deadline) {410        count += calcChunk(addChunks.dequeue());411      }412      // Dirty chunks (only if visible)413      while (dirtyChunks.size() > 0 && Date.now() < deadline) {414        var chunkKey = dirtyChunks.dequeue();415        if (chunks.has(chunkKey)) {416          count += calcChunk(chunkKey);417        }418      }419      measuring.chunkCount.inc(count);420      421      if (addChunks.size() > 0 || dirtyChunks.size() > 0) {422        // Schedule rendering more chunks423        scheduleDraw();424      }425      426      subWRs.forEach(function (record) {427        record.wr.updateSomeChunks();428      });429    }430    this.updateSomeChunks = updateSomeChunks;431    432    function addParticles(cube, mode) {433      var chunk = chunkContaining(cube);434      var lightTexture = chunk ? chunk.getLightTexture() : null;435      436      particles.push(new renderer.BlockParticles(437        cube,438        tileSize,439        world.gtv(cube),440        mode,441        world.gRotv(cube),442        lightTexture));443    }444    445    446    447    function draw() {448      // Draw chunks.449      renderer.setTileSize(blockset.tileSize);450      nonemptyChunks.forEachValue(function (chunk) {451        if (renderer.aabbInView(chunk.aabb))452          chunk.draw();453      });454      455      // Draw circuits.456      circuitRenderers.forEachValue(function (cr) {457        cr.draw();458      });459      460      461      // Draw particles.462      for (var i = 0; i < particles.length; i++) {463        var particleSystem = particles[i];464        if (particleSystem.expired()) {465          if (i < particles.length - 1) {466            particles[i] = particles.pop();467          } else {468            particles.pop();469          }470          i--;471        } else {472          particleSystem.draw();473        }474      }475      if (particles.length > 0) {476        // If there are any particle systems, we need to continue animating.477        scheduleDraw();478      }479      480      if (showBoundaries) {481        boundaryR.draw();482      }483      // Draw texture debug.484      if (config.debugTextureAllocation.get()) {485        textureDebugR.draw();486      }487      488      // Draw subworlds.489      subWRs.forEach(function (record) {490        var body = record.body;491        var bodyWorld = body.skin;492        var wr = record.wr;493        494        var restoreView = renderer.saveView();495        renderer.modifyModelview(function (m) {496          mat4.translate(m, body.pos);497          498          // Translate to body AABB center499          var aabb = body.aabb;500          mat4.translate(m, [501            (aabb[1] + aabb[0]) * 0.5,502            (aabb[3] + aabb[2]) * 0.5,503            (aabb[5] + aabb[4]) * 0.5504          ]);505          506          // Apply rotation507          mat4.rotateY(m, discreteRotation(body.yaw));508          509          // Translate to center world about AABB center510          mat4.translate(m, [bodyWorld.wx * -0.5, bodyWorld.wy * -0.5, bodyWorld.wz * -0.5]);511        });512        wr.draw();513        restoreView();514      });515    }516    this.draw = draw;517    518    var renderData; // updated as needed by chunk recalculate519    520    // return value is for measuring only521    function calcChunk(chunkKey) {522      var work = 0;523      var c = chunks.get(chunkKey);524      if (c) {525        if (c.dirtyGeometry) {526          c.dirtyGeometry = false;527          c.recompute();528          work = 1;529        }530        if (c.dirtyLighting) {531          c.dirtyLighting = false;532          c.recomputeLight();533          work = 1;534        }535      } else {536        chunks.set(chunkKey, makeChunk(chunkKey));537        work = 1;538      }539      return work;540    }541    542    function makeChunk(chunkKey) {543      var chunkOriginX = chunkKey[0];544      var chunkOriginY = chunkKey[1];545      var chunkOriginZ = chunkKey[2];546      var chunkLimitX = Math.min(wx, chunkOriginX + CHUNKSIZE);547      var chunkLimitY = Math.min(wy, chunkOriginY + CHUNKSIZE);548      var chunkLimitZ = Math.min(wz, chunkOriginZ + CHUNKSIZE);549      var nonempty = false;550      var lightTexture;551      var mustRebuild = function () { return true; };552      var ltData = new Uint8Array(LIGHT_TEXTURE_SIZE*LIGHT_TEXTURE_SIZE*LIGHT_TEXTURE_SIZE);553      554      var chunk = new renderer.RenderBundle(gl.TRIANGLES,555                                            function () { return renderData.texture; },556                                            function (vertices, normals, texcoords) {557        measuring.chunk.start();558        renderData = blockset.getRenderData(renderer);559        var rotatedBlockFaceData = renderData.rotatedBlockFaceData;560        var BOGUS_BLOCK_DATA = rotatedBlockFaceData.bogus;561        var types = renderData.types;562        var opaques = types.map(function (t) { return t.opaque; });563        564        // these variables are used by face() and written by the loop565        var x,y,z;566        var thisOpaque;567        var rawIndex;568        569        function face(vFacing, data) {570          var fx = vFacing[0]; var xfx = x + fx;571          var fy = vFacing[1]; var yfy = y + fy;572          var fz = vFacing[2]; var zfz = z + fz;573          // TODO between the g() and the inBounds() we're testing the neighbor twice574          if (thisOpaque && opaques[g(xfx,yfy,zfz)]) {575            // this face is invisible576            return;577          } else {578            var faceVertices = data.vertices;579            var faceTexcoords = data.texcoords;580            var vl = faceVertices.length / 3;581            for (var i = 0; i < vl; i++) {582              var vi = i*3;583              var ti = i*2;584              vertices.push(faceVertices[vi  ]+x,585                            faceVertices[vi+1]+y,586                            faceVertices[vi+2]+z);587              texcoords.push(faceTexcoords[ti], faceTexcoords[ti+1]);588              normals.push(fx, fy, fz);589            }590          }591        }592        593        for (x = chunkOriginX; x < chunkLimitX; x++)594        for (y = chunkOriginY; y < chunkLimitY; y++)595        for (z = chunkOriginZ; z < chunkLimitZ; z++) {596          // raw array access inlined and simplified for efficiency597          rawIndex = (x*wy+y)*wz+z;598          var value = rawBlocks[rawIndex];599          if (value === ID_EMPTY) continue;600          601          var rotIndex = rawRotations[rawIndex];602          var rot = rotationsByCode[rotIndex];603          var faceData = (rotatedBlockFaceData[value] || BOGUS_BLOCK_DATA)[rotIndex];604          thisOpaque = opaques[value];605          606          face(rot.nx, faceData.lx);607          face(rot.ny, faceData.ly);608          face(rot.nz, faceData.lz);609          face(rot.px, faceData.hx);610          face(rot.py, faceData.hy);611          face(rot.pz, faceData.hz);612        }613        614        var wasNonempty = nonempty;615        nonempty = vertices.length > 0;616        if (nonempty !== wasNonempty) {617          if (nonempty) {618            nonemptyChunks.set(chunkKey, chunk);619          } else {620            nonemptyChunks.delete(chunkKey);621          }622        }623        624        measuring.chunk.end();625      }, {626        aroundDraw: function (draw) {627          if (mustRebuild()) sendLightTexture();628          renderer.setLightTexture(lightTexture);629          draw();630        }631      });632      633      function copyLightTexture() {634        // Repack data635        for (var x = chunkOriginX - 1; x <= chunkLimitX; x++)636        for (var y = chunkOriginY - 1; y <= chunkLimitY; y++)637        for (var z = chunkOriginZ - 1; z <= chunkLimitZ; z++) {638          var ltIndex = ( (  mod(x, LIGHT_TEXTURE_SIZE) *LIGHT_TEXTURE_SIZE639                           + mod(y, LIGHT_TEXTURE_SIZE))*LIGHT_TEXTURE_SIZE640                         +   mod(z, LIGHT_TEXTURE_SIZE));641          var rawIndex = (x*wy+y)*wz+z;642          ltData[ltIndex] = inBounds(x,y,z) ? rawLighting[rawIndex] : lightOutside;643        }644        645        sendLightTexture();646      }647      648      function sendLightTexture() {649        if (mustRebuild()) {650          lightTexture = gl.createTexture();651          mustRebuild = renderer.currentContextTicket();652          gl.bindTexture(gl.TEXTURE_2D, lightTexture);653          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);654          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);655          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);656          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);657        } else {658          gl.bindTexture(gl.TEXTURE_2D, lightTexture);659        }660        gl.texImage2D(gl.TEXTURE_2D,661                      0, // level662                      gl.LUMINANCE, // internalformat663                      LIGHT_TEXTURE_SIZE, // width664                      LIGHT_TEXTURE_SIZE*LIGHT_TEXTURE_SIZE, // height665                      0, // border666                      gl.LUMINANCE, // format667                      gl.UNSIGNED_BYTE, // type668                      ltData);669        gl.bindTexture(gl.TEXTURE_2D, null);670      }671      672      // This is needed because when the calc function is first called by constructing the RenderBundle, 'chunk' has not yet been assigned.673      if (nonempty)674        nonemptyChunks.set(chunkKey, chunk);675      676      chunk.aabb = new AAB(677        chunkOriginX, chunkLimitX,678        chunkOriginY, chunkLimitY,679        chunkOriginZ, chunkLimitZ680      );681      682      chunk.recomputeLight = copyLightTexture;683      684      chunk.getLightTexture = function () {685        if (mustRebuild()) sendLightTexture();686        return lightTexture;687      };688      689      copyLightTexture();690      691      return chunk;692    }693    694    var CYL_RESOLUTION = 9;695    function calcCylinder(pt1, pt2, radius, vertices, normals) {696      function pushVertex(vec) {697        vertices.push(vec[0], vec[1], vec[2]);698        normals.push(0,0,0);699      }700      //function pushNormal(vec) {701      //  normals.push(vec[0], vec[1], vec[2]);702      //}703      704      var length = vec3.subtract(pt2, pt1, vec3.create());705      var perp1 = vec3.cross(length, length[1] ? UNIT_PX : UNIT_PY, vec3.create());706      var perp2 = vec3.cross(perp1, length, vec3.create());707      vec3.normalize(perp1);708      vec3.normalize(perp2);709      function incr(i, r) {710        return vec3.add(711          vec3.scale(perp1, Math.sin(i/10*Math.PI*2), vec3.create()),712          vec3.scale(perp2, Math.cos(i/10*Math.PI*2), vec3.create()));713      }714      for (var i = 0; i < CYL_RESOLUTION; i++) {715        var p1 = incr(i);716        var p2 = incr(mod(i+1, CYL_RESOLUTION));717        //pushNormal(p2);718        //pushNormal(p2);719        //pushNormal(p1);720        //pushNormal(p1);721        //pushNormal(p1);722        //pushNormal(p2);723        vec3.scale(p1, radius);724        vec3.scale(p2, radius);725        var v0 = vec3.add(pt1, p2, vec3.create());726        var v1 = vec3.add(pt2, p2, vec3.create());727        var v2 = vec3.add(pt2, p1, vec3.create());728        var v3 = vec3.add(pt1, p1, vec3.create());729        pushVertex(v0);730        pushVertex(v1);731        pushVertex(v2);732        pushVertex(v2);733        pushVertex(v3);734        pushVertex(v0);735      }736      return 6*CYL_RESOLUTION;737    }738    739    var CENTER = [0.5, 0.5, 0.5];740    var beamRadius = round(0.08 * tileSize) / tileSize;741    function makeCircuitRenderer(circuit) {742      var dyns;743      var circuitRenderer = new renderer.RenderBundle(gl.TRIANGLES, null, function (vertices, normals, colors) {744        dyns = [];745        circuit.getEdges().forEach(function (record) {746          var net = record[0];747          var fromBlock = record[1];748          var block = record[2];749          750          var cbase = colors.length;751          var numVertices = calcCylinder(752            vec3.add(fromBlock, CENTER, vec3.create()),753            vec3.add(block,     CENTER, vec3.create()),754            beamRadius,755            vertices, normals);756          for (var i = 0; i < numVertices; i++)757            colors.push(1,1,1,1); 758            759          dyns.push(function () {760            var carr = circuitRenderer.colors.array;761            var value = circuit.getNetValue(net);762            var color;763            var alpha = 0.5;764            if (value === null || value === undefined) {765              color = [0,0,0,alpha];766            } else if (value === false) {767              color = [0,0,0.2,alpha];768            } else if (value === true) {769              color = [0.2,0.2,1,alpha];770            } else if (typeof value === 'number') {771              // TODO: represent negatives too772              if (value <= 1)773                color = [value, 0, 0, alpha];774              else775                color = [1, 1 - (1/value), 0, alpha];776            } else {777              color = [1,1,1,alpha];778            }779            for (var i = 0, p = cbase; i < numVertices; i++) {780              carr[p++] = color[0];781              carr[p++] = color[1];782              carr[p++] = color[2];783              carr[p++] = color[3];784            }785          });786        });787      }, {788        aroundDraw: function (baseDraw) {789          dyns.forEach(function (f) { f(); });790          circuitRenderer.colors.send(gl.DYNAMIC_DRAW);791          baseDraw();792        }793      });794      795      return circuitRenderer;796    }797    798    // For info/debug displays799    function chunkRendersToDo() {800      return dirtyChunks.size() + addChunks.size();801    }802    this.chunkRendersToDo = chunkRendersToDo;803    804    // --- bodies in the world ---805    806    var subWRs = [];807    world.forEachBody(function (body) {808      if (!body.skin) return;809      subWRs.push({810        body: body, 811        wr: new WorldRenderer(body.skin, function () { return [0,0,0]; }, renderer, optAudio, scheduleDraw, false)812      });813    });814    815    // --- init ---816    817    world.listen(listenerWorld);818    blockset.listen(listenerBlockset);819    config.renderDistance.listen(listenerRenderDistance);820    config.debugTextureAllocation.listen(listenerRedraw);821    822    Object.freeze(this);823  }824  825  WorldRenderer.LIGHT_TEXTURE_SIZE = LIGHT_TEXTURE_SIZE; // exposed for shader826  cubes.WorldRenderer = Object.freeze(WorldRenderer);...timeSeriesSepalExport.js
Source: timeSeriesSepalExport.js 
1const {toFeatureCollection} = require('sepal/ee/aoi')2const {hasImagery: hasOpticalImagery} = require('sepal/ee/optical/collection')3const {hasImagery: hasRadarImagery} = require('sepal/ee/radar/collection')4const {hasImagery: hasPlanetImagery} = require('sepal/ee/planet/collection')5const tile = require('sepal/ee/tile')6const {exportImageToSepal$} = require('../jobs/export/toSepal')7const {mkdirSafe$} = require('task/rxjs/fileSystem')8const {concat, forkJoin, from, of, map, mergeMap, scan, switchMap, tap} = require('rxjs')9const {swallow} = require('sepal/rxjs')10const Path = require('path')11const {terminal$} = require('sepal/terminal')12const {sequence} = require('sepal/utils/array')13const moment = require('moment')14const ee = require('sepal/ee')15const {getCurrentContext$} = require('task/jobs/service/context')16const {getCollection$} = require('sepal/ee/timeSeries/collection')17const _ = require('lodash')18const log = require('sepal/log').getLogger('task')19const DATE_DELTA = 320const DATE_DELTA_UNIT = 'months'21module.exports = {22    submit$: (_id, {workspacePath, description, ...retrieveOptions}) =>23        getCurrentContext$().pipe(24            switchMap(({config}) => {25                const preferredDownloadDir = workspacePath26                    ? `${config.homeDir}/${workspacePath}/`27                    : `${config.homeDir}/downloads/${description}/`28                return mkdirSafe$(preferredDownloadDir, {recursive: true}).pipe(29                    switchMap(downloadDir =>30                        export$({description, downloadDir, ...retrieveOptions})31                    )32                )33            })34        )35}36const export$ = ({37    downloadDir,38    description,39    recipe,40    indicator,41    scale,42    tileSize,43    shardSize,44    fileDimensions,45    crs,46    crsTransform47}) => {48    const aoi = recipe.model.aoi49    const sources = recipe.model.sources50    const dataSets = sources.dataSets51    const {startDate, endDate} = recipe.model.dates52    const reflectance = recipe.model.options.corrections.includes('SR') ? 'SR' : 'TOA'53    const tiles = tile(toFeatureCollection(aoi), tileSize) // synchronous EE54    const exportTiles$ = tileIds => {55        const totalTiles = tileIds.length56        const tile$ = from(57            tileIds.map((tileId, tileIndex) =>58                ({tileId, tileIndex})59            )60        )61        return concat(62            of({totalTiles}),63            tile$.pipe(64                mergeMap(tile => exportTile$(tile), 1)65            )66        )67    }68    const exportTile$ = ({tileId, tileIndex}) =>69        concat(70            of({tileIndex, chunks: 0}),71            exportChunks$(createChunks$({tileId, tileIndex})),72            postProcess$(Path.join(downloadDir, `${tileIndex}`))73        )74    const createChunks$ = ({tileId, tileIndex}) => {75        const tile = tiles.filterMetadata('system:index', 'equals', tileId).first()76        const from = moment(startDate)77        const to = moment(endDate)78        const duration = moment(to).subtract(1, 'day').diff(from, DATE_DELTA_UNIT)79        const dateOffsets = sequence(0, duration, DATE_DELTA)80        const chunks$ = dateOffsets.map(dateOffset => {81            const start = moment(from).add(dateOffset, DATE_DELTA_UNIT)82            const startString = start.format('YYYY-MM-DD')83            const end = moment.min(moment(start).add(DATE_DELTA, DATE_DELTA_UNIT), to)84            const endString = end.format('YYYY-MM-DD')85            const dateRange = `${startString}_${endString}`86            return hasImagery$(87                tile.geometry(),88                ee.Date(startString),89                ee.Date(endString)90            ).pipe(91                switchMap(notEmpty => {92                    if (notEmpty) {93                        return createTimeSeries$(tile, startString, endString).pipe(94                            map(timeSeries =>95                                ({tileIndex, timeSeries, dateRange, notEmpty})96                            )97                        )98                    } else {99                        return of({tileIndex, dateRange, notEmpty})100                    }101                })102            )103        })104        return forkJoin(chunks$)105    }106    const isRadar = () => _.isEqual(Object.values(dataSets).flat(), ['SENTINEL_1'])107    const isOptical = () => Object.keys(dataSets).find(type => ['LANDSAT', 'SENTINEL_2'].includes(type))108    const createTimeSeries$ = (feature, startDate, endDate) => {109        const images$ = getCollection$({recipe, bands: [indicator], startDate, endDate})110        return images$.pipe(111            map(images => {112                images = images.select(indicator)113                const distinctDateImages = images.distinct('date')114                const timeSeries = ee.ImageCollection(115                    ee.Join.saveAll('images')116                        .apply({117                            primary: distinctDateImages,118                            secondary: images,119                            condition: ee.Filter.equals({120                                leftField: 'date',121                                rightField: 'date'122                            })123                        })124                        .map(image => ee.ImageCollection(ee.List(image.get('images')))125                            .median()126                            .rename(image.getString('date'))127                        ))128                    .toBands()129                    .regexpRename('.*(.{10})', '$1')130                    .clip(feature.geometry())131                return timeSeries.select(timeSeries.bandNames().sort())132            })133        )134    }135    const hasImagery$ = (geometry, startDate, endDate) =>136        ee.getInfo$(137            isRadar()138                ? hasRadarImagery({geometry, startDate, endDate, orbits: recipe.model.options.orbits})139                : isOptical()140                    ? hasOpticalImagery({dataSets: extractDataSets(dataSets), reflectance, geometry, startDate, endDate})141                    : hasPlanetImagery({sources: {...sources, source: Object.values(sources.dataSets).flat()[0]}, geometry, startDate, endDate}),142            'check if date range has imagery'143        )144    const exportChunks$ = chunks$ =>145        chunks$.pipe(146            switchMap(chunks => {147                const nonEmptyChunks = chunks.filter(({notEmpty}) => notEmpty)148                const totalChunks = nonEmptyChunks.length149                return concat(150                    of({totalChunks}),151                    from(nonEmptyChunks).pipe(152                        mergeMap(chunk => exportChunk$(chunk))153                    )154                )155            })156        )157    const exportChunk$ = ({tileIndex, timeSeries, dateRange}) => {158        const chunkDescription = `${description}_${tileIndex}_${dateRange}`159        const chunkDownloadDir = `${downloadDir}/${tileIndex}/chunk-${dateRange}`160        const export$ = exportImageToSepal$({161            image: timeSeries,162            folder: chunkDescription,163            description: chunkDescription,164            downloadDir: chunkDownloadDir,165            scale,166            crs,167            crsTransform,168            shardSize,169            fileDimensions170        }).pipe(swallow())171        return concat(172            export$,173            of({completedChunk: true})174        )175    }176    const tileIds$ = ee.getInfo$(tiles.aggregate_array('system:index'), `time-series image ids ${description}`)177    return tileIds$.pipe(178        switchMap(tileIds => exportTiles$(tileIds)),179        tap(progress => log.trace(() => `time-series: ${JSON.stringify(progress)}`)),180        scan(181            (acc, progress) => {182                return ({183                    ...acc,184                    ...progress,185                    chunks: progress.chunks === undefined186                        ? acc.chunks + (progress.completedChunk ? 1 : 0)187                        : progress.chunks188                })189            },190            {tileIndex: 0, chunks: 0}191        ),192        map(toProgress)193    )194}195const postProcess$ = downloadDir =>196    terminal$('sepal-stack-time-series', [downloadDir])197        .pipe(198            tap(({stream, value}) => {199                if (value)200                    stream === 'stdout' ? log.info(value) : log.warn(value)201            }),202            swallow()203        )204const toProgress = ({totalTiles = 0, tileIndex = 0, totalChunks = 0, chunks = 0}) => {205    const currentTilePercent = totalChunks ? Math.round(100 * chunks / totalChunks) : 0206    const currentTile = tileIndex + 1207    return currentTilePercent < 100208        ? {209            totalChunks,210            chunks,211            totalTiles,212            tileIndex,213            defaultMessage: `Exported ${currentTilePercent}% of tile ${currentTile} out of ${totalTiles}.`,214            messageKey: 'tasks.retrieve.time_series_to_sepal.progress',215            messageArgs: {currentTilePercent, currentTile, totalTiles}216        }217        : {218            totalChunks,219            chunks,220            totalTiles,221            tileIndex,222            defaultMessage: `Assembling tile ${currentTile} out of ${totalTiles}...`,223            messageKey: 'tasks.retrieve.time_series_to_sepal.assembling',224            messageArgs: {currentTile, totalTiles}225        }226}227const extractDataSets = sources =>228    Object.values(sources)229        .flat()230        .map(dataSet =>231            dataSet === 'LANDSAT_TM'232                ? ['LANDSAT_4', 'LANDSAT_5']233                : dataSet === 'LANDSAT_TM_T2'234                    ? ['LANDSAT_4_T2', 'LANDSAT_5_T2']235                    : dataSet236        )...ipV6.spec.ts
Source: ipV6.spec.ts 
1import { ipV6 } from '../../../src/arbitrary/ipV6';2import { Value } from '../../../src/check/arbitrary/definition/Value';3import {4  assertProduceValuesShrinkableWithoutContext,5  assertShrinkProducesSameValueWithoutInitialContext,6  assertProduceCorrectValues,7  assertProduceSameValueGivenSameSeed,8  assertGenerateIndependentOfSize,9} from './__test-helpers__/ArbitraryAssertions';10import { buildShrinkTree, renderTree } from './__test-helpers__/ShrinkTree';11describe('ipV6 (integration)', () => {12  const isValidIpV4 = (value: string) => {13    const chunks = value.split('.').map((v) => {14      if (v[0] === '0') {15        if (v[1] === 'x' || v[1] === 'X') return parseInt(v, 16);16        return parseInt(v, 8);17      }18      return parseInt(v, 10);19    });20    // one invalid chunk21    if (chunks.find((v) => Number.isNaN(v)) !== undefined) return false;22    // maximal amount of 4 chunks23    if (chunks.length > 4) return false;24    // all chunks, except the last one are inferior or equal to 25525    if (chunks.slice(0, -1).find((v) => v < 0 && v > 255) !== undefined) return false;26    // last chunk must be below 256^(5 â number of chunks)27    return chunks[chunks.length - 1] < 256 ** (5 - chunks.length);28  };29  const isCorrect = (value: string) => {30    const firstElision = value.indexOf('::');31    if (firstElision !== -1) {32      // At most one '::'33      if (value.substr(firstElision + 1).includes('::')) return false;34    }35    const chunks = value.split(':');36    const last = chunks[chunks.length - 1];37    // The ipv4 can only be composed of 4 decimal chunks separated by dots38    // 1.1000 is not a valid IP v4 in the context of IP v639    const endByIpV4 = last.includes('.') && isValidIpV4(last);40    const nonEmptyChunks = chunks.filter((c) => c !== '');41    const hexaChunks = endByIpV4 ? nonEmptyChunks.slice(0, nonEmptyChunks.length - 1) : nonEmptyChunks;42    if (!hexaChunks.every((s) => /^[0-9a-f]{1,4}$/.test(s))) return false;43    const equivalentChunkLength = endByIpV4 ? hexaChunks.length + 2 : hexaChunks.length;44    return firstElision !== -1 ? equivalentChunkLength < 8 : equivalentChunkLength === 8;45  };46  const ipV6Builder = () => ipV6();47  it('should produce the same values given the same seed', () => {48    assertProduceSameValueGivenSameSeed(ipV6Builder);49  });50  it('should only produce correct values', () => {51    assertProduceCorrectValues(ipV6Builder, isCorrect);52  });53  it('should produce values seen as shrinkable without any context', () => {54    assertProduceValuesShrinkableWithoutContext(ipV6Builder);55  });56  it('should be able to shrink to the same values without initial context', () => {57    assertShrinkProducesSameValueWithoutInitialContext(ipV6Builder);58  });59  it('should be independent of global settings overriding defaults on size', () => {60    assertGenerateIndependentOfSize(ipV6Builder);61  });62  it.each`63    source64    ${'::1'}65    ${'0123:5678:9abc:ef01:2345:6789:128.0.0.1'}66    ${'0123:5678:9abc:ef01:2345:6789:0000:ffff'}67    ${'0:56:9abc:ef01:234:67:8.8.8.8'}68    ${'0:56:9abc:ef01:234:67:0:f'}69    ${'::5678:9abc:ef01:2345:6789:128.0.0.1'}70    ${'::5678:9abc:ef01:2345:6789:0000:ffff'}71    ${'::9abc:ef01:2345:6789:128.0.0.1'}72    ${'::9abc:ef01:2345:6789:0000:ffff'}73    ${'5678::9abc:ef01:2345:6789:128.0.0.1'}74    ${'5678::9abc:ef01:2345:6789:0000:ffff'}75    ${'::ef01:2345:6789:128.0.0.1'}76    ${'::ef01:2345:6789:0000:ffff'}77    ${'9abc::ef01:2345:6789:128.0.0.1'}78    ${'9abc::ef01:2345:6789:0000:ffff'}79    ${'5678:9abc::ef01:2345:6789:128.0.0.1'}80    ${'5678:9abc::ef01:2345:6789:0000:ffff'}81    ${'::2345:6789:128.0.0.1'}82    ${'::2345:6789:0000:ffff'}83    ${'ef01::2345:6789:128.0.0.1'}84    ${'ef01::2345:6789:0000:ffff'}85    ${'9abc:ef01::2345:6789:128.0.0.1'}86    ${'9abc:ef01::2345:6789:0000:ffff'}87    ${'5678:9abc:ef01::2345:6789:128.0.0.1'}88    ${'5678:9abc:ef01::2345:6789:0000:ffff'}89    ${'::6789:128.0.0.1'}90    ${'::6789:0000:ffff'}91    ${'2345::6789:128.0.0.1'}92    ${'2345::6789:0000:ffff'}93    ${'ef01:2345::6789:128.0.0.1'}94    ${'ef01:2345::6789:0000:ffff'}95    ${'9abc:ef01:2345::6789:128.0.0.1'}96    ${'9abc:ef01:2345::6789:0000:ffff'}97    ${'5678:9abc:ef01:2345::6789:128.0.0.1'}98    ${'5678:9abc:ef01:2345::6789:0000:ffff'}99    ${'::128.0.0.1'}100    ${'::0000:ffff'}101    ${'2345::128.0.0.1'}102    ${'2345::0000:ffff'}103    ${'ef01:2345::128.0.0.1'}104    ${'ef01:2345::0000:ffff'}105    ${'9abc:ef01:2345::128.0.0.1'}106    ${'9abc:ef01:2345::0000:ffff'}107    ${'5678:9abc:ef01:2345::128.0.0.1'}108    ${'5678:9abc:ef01:2345::0000:ffff'}109    ${'0123:5678:9abc:ef01:2345::128.0.0.1'}110    ${'0123:5678:9abc:ef01:2345::0000:ffff'}111    ${'::0123'}112    ${'6789::0123'}113    ${'2345:6789::0123'}114    ${'ef01:2345:6789::0123'}115    ${'9abc:ef01:2345:6789::0123'}116    ${'5678:9abc:ef01:2345:6789::0123'}117    ${'0123:5678:9abc:ef01:2345:6789::0123'}118    ${'::'}119    ${'0123::'}120    ${'6789:0123::'}121    ${'2345:6789:0123::'}122    ${'ef01:2345:6789:0123::'}123    ${'9abc:ef01:2345:6789:0123::'}124    ${'5678:9abc:ef01:2345:6789:0123::'}125    ${'0123:5678:9abc:ef01:2345:6789:0123::'}126  `('should be able to generate $source with fc.ipV6()', ({ source }) => {127    // Arrange / Act128    const arb = ipV6();129    const out = arb.canShrinkWithoutContext(source);130    // Assert131    expect(out).toBe(true);132  });133  it.each`134    rawValue135    ${'::1'}136    ${'0123:5678:9abc:ef01:2345:6789:128.0.0.1'}137  `('should be able to shrink $rawValue', ({ rawValue }) => {138    // Arrange139    const arb = ipV6();140    const value = new Value(rawValue, undefined);141    // Act142    const renderedTree = renderTree(buildShrinkTree(arb, value, { numItems: 100 })).join('\n');143    // Assert144    expect(arb.canShrinkWithoutContext(rawValue)).toBe(true);145    expect(renderedTree).toMatchSnapshot();146  });...Using AI Code Generation
1const fc = require('fast-check');2const { nonEmptyChunks } = require('fast-check/lib/check/arbitrary/ChunksArbitrary.js');3fc.assert(4  fc.property(nonEmptyChunks(fc.integer()), (chunks) => {5    return chunks.length > 0;6  })7);8const { nonEmptyChunks } = require('fast-check/lib/check/arbitrary/ChunksArbitrary.js');9SyntaxError: Unexpected token {10    at wrapSafe (internal/modules/cjs/loader.js:1101:16)11    at Module._compile (internal/modules/cjs/loader.js:1153:27)12    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1213:10)13    at Module.load (internal/modules/cjs/loader.js:1032:32)14    at Function.Module._load (internal/modules/cjs/loader.js:924:14)15    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)16What is the correct way to import the nonEmptyChunks method?17const fc = require('fast-check');18const nonEmptyChunks = require('fast-check/lib/check/arbitrary/ChunksArbitrary.js').nonEmptyChunks;19fc.assert(20  fc.property(nonEmptyChunks(fc.integer()), (chunks) => {21    return chunks.length > 0;22  })23);Using AI Code Generation
1import { nonEmptyChunks } from 'fast-check';2import { nonEmptyChunks } from 'fast-check';3import { nonEmptyChunks } from 'fast-check';4import { nonEmptyChunks } from 'fast-check';5import { nonEmptyChunks } from 'fast-check';6import { nonEmptyChunks } from 'fast-check';7import { nonEmptyChunks } from 'fast-check';8import { nonEmptyChunks } from 'fast-check';9import { nonEmptyChunks } from 'fast-check';10import { nonEmptyChunks } from 'fast-check';11import { nonEmptyChunks } from 'fast-check';12import { nonEmptyChunks } from 'fast-check';13import { nonEmptyChunks } from 'fast-check';14import { nonEmptyChunks } from 'fast-check';15import { nonEmptyChunks } from 'fast-check';16import { nonEmptyChunks } from 'fast-check';17import { nonEmptyChunks } from 'fast-check';18import { nonEmptyChunks } from 'Using AI Code Generation
1import { nonEmptyChunks } from "fast-check";2import { suite } from "uvu";3import * as assert from "uvu/assert";4const test = suite("Test nonEmptyChunks");5test("test nonEmptyChunks", () => {6  const chunks = nonEmptyChunks({ maxChunkSize: 4 }, 4);7  assert.is(chunks, [[1, 2, 3, 4]]);8});9test.run();10Could you provide more details on your setup? (How did you install fast-check-monorepo? How did you run the test? Which version of fast-check are you using?)Using AI Code Generation
1const fc = require('fast-check');2const nonEmptyChunks = require('fast-check-monorepo').nonEmptyChunks;3const { nonEmptyArray } = require('fast-check');4const { array } = require('fast-check');5const { tuple } = require('fast-check');6const { constantFrom } = require('fast-check');7const test = () => {8  fc.assert(9    fc.property(nonEmptyChunks(nonEmptyArray(constantFrom(1, 2, 3))), (arr) => {10      console.log(arr);11      return true;Check out the latest blogs from LambdaTest on this topic:
Recently, I was going through some of the design patterns in Java by reading the book Head First Design Patterns by Eric Freeman, Elisabeth Robson, Bert Bates, and Kathy Sierra.
Dries Buytaert, a graduate student at the University of Antwerp, came up with the idea of developing something similar to a chat room. Moreover, he modified the conventional chat rooms into a website where his friends could post their queries and reply through comments. However, for this project, he thought of creating a temporary archive of posts.
Web applications continue to evolve at an unbelievable pace, and the architecture surrounding web apps get more complicated all of the time. With the growth in complexity of the web application and the development process, web application testing also needs to keep pace with the ever-changing demands.
The rapid shift in the use of technology has impacted testing and quality assurance significantly, especially around the cloud adoption of agile development methodologies. With this, the increasing importance of quality and automation testing has risen enough to deliver quality work.
The best agile teams are built from people who work together as one unit, where each team member has both the technical and the personal skills to allow the team to become self-organized, cross-functional, and self-motivated. These are all big words that I hear in almost every agile project. Still, the criteria to make a fantastic agile team are practically impossible to achieve without one major factor: motivation towards a common goal.
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
