How to use interactionState method in pact-foundation-pact

Best JavaScript code snippet using pact-foundation-pact

boxeditor.js

Source:boxeditor.js Github

copy

Full Screen

1var BoxEditor = (function () {23 //min and max are both number[3]4 function AABB (min, max) {5 this.min = [min[0], min[1], min[2]];6 this.max = [max[0], max[1], max[2]];7 }89 AABB.prototype.computeVolume = function () {10 var volume = 1;11 for (var i = 0; i < 3; ++i) {12 volume *= (this.max[i] - this.min[i]);13 }14 return volume;15 }1617 AABB.prototype.computeSurfaceArea = function () {18 var width = this.max[0] - this.min[0];19 var height = this.max[1] - this.min[1];20 var depth = this.max[2] - this.min[2];2122 return 2 * (width * height + width * depth + height * depth);23 }2425 //returns new AABB with the same min and max (but not the same array references)26 AABB.prototype.clone = function () {27 return new AABB(28 [this.min[0], this.min[1], this.min[2]],29 [this.max[0], this.max[1], this.max[2]]30 );31 }3233 AABB.prototype.randomPoint = function () { //random point in this AABB34 var point = [];35 for (var i = 0; i < 3; ++i) {36 point[i] = this.min[i] + Math.random() * (this.max[i] - this.min[i]);37 }38 return point;39 }4041 var InteractionMode = {42 RESIZING: 0,43 TRANSLATING: 1,4445 DRAWING: 2, //whilst we're drawing a rectangle on a plane46 EXTRUDING: 3 //whilst we're extruding that rectangle into a box47 };4849 var STEP = 1.0;50 5152 function exclusiveAABBOverlap (a, b) {53 return a.min[0] < b.max[0] && a.max[0] > b.min[0] &&54 a.min[1] < b.max[1] && a.max[1] > b.min[1] &&55 a.min[2] < b.max[2] && a.max[2] > b.min[2];56 }5758 function inclusiveAABBOverlap (a, b) {59 return a.min[0] <= b.max[0] && a.max[0] >= b.min[0] &&60 a.min[1] <= b.max[1] && a.max[1] >= b.min[1] &&61 a.min[2] <= b.max[2] && a.max[2] >= b.min[2];62 }636465 /*66 if there is an intersection then this returns:67 {68 aabb: aabb,69 t: distance to intersection,7071 point: point of intersection,7273 //axis and side together define the plane of intersection (+x, -x, etc)74 axis: 0, 1 or 2 depending on x, y or z,75 side: -1 or 1 depending on which side the intersection happened on76 }777879 otherwise it returns null80 */8182 function rayAABBIntersection (rayOrigin, rayDirection, aabb) {83 //we see it as a series of clippings in t of the line in the AABB planes along each axis84 //the part we are left with after clipping if successful is the region of the line within the AABB and thus we can extract the intersection8586 //the part of the line we have clipped so far87 var lowT = -Infinity;88 var highT = Infinity;8990 var intersectionAxis = 0;9192 for (var i = 0; i < 3; ++i) {93 var t1 = (aabb.min[i] - rayOrigin[i]) / rayDirection[i];94 var t2 = (aabb.max[i] - rayOrigin[i]) / rayDirection[i];95 //so between t1 and t2 we are within the aabb planes in this dimension9697 //ensure t1 < t2 (swap if necessary)98 if (t1 > t2) {99 var temp = t1;100 t1 = t2;101 t2 = temp;102 }103104 //t1 and t2 now hold the lower and upper intersection t's respectively105106 //the part of the line we just clipped for does not overlap the part previously clipped and thus there is no intersection 107 if (t2 < lowT || t1 > highT) return null;108109 //further clip the line between the planes in this axis110 if (t1 > lowT) {111 lowT = t1;112113 intersectionAxis = i; //if we needed to futher clip in this axis then this is the closest intersection axis114 }115116 if (t2 < highT) highT = t2;117 }118119 if (lowT > highT) return null;120121 //if we've reached this far then there is an intersection122123 var intersection = [];124 for (var i = 0; i < 3; ++i) {125 intersection[i] = rayOrigin[i] + rayDirection[i] * lowT;126 }127128129 return {130 aabb: aabb,131 t: lowT,132 axis: intersectionAxis,133 side: rayDirection[intersectionAxis] > 0 ? -1 : 1,134 point: intersection135 };136 }137138 //finds the closest points between the line1 and line2139 //returns [closest point on line1, closest point on line2]140 function closestPointsOnLines (line1Origin, line1Direction, line2Origin, line2Direction) {141 var w0 = Utilities.subtractVectors([], line1Origin, line2Origin);142143 var a = Utilities.dotVectors(line1Direction, line1Direction);144 var b = Utilities.dotVectors(line1Direction, line2Direction);145 var c = Utilities.dotVectors(line2Direction, line2Direction);146 var d = Utilities.dotVectors(line1Direction, w0);147 var e = Utilities.dotVectors(line2Direction, w0);148149150 var t1 = (b * e - c * d) / (a * c - b * b);151 var t2 = (a * e - b * d) / (a * c - b * b);152153 return [154 Utilities.addVectors([], line1Origin, Utilities.multiplyVectorByScalar([], line1Direction, t1)),155 Utilities.addVectors([], line2Origin, Utilities.multiplyVectorByScalar([], line2Direction, t2))156 ];157 }158159 //this defines the bounds of our editing space160 //the grid starts at (0, 0, 0)161 //gridSize is [width, height, depth]162 //onChange is a callback that gets called anytime a box gets edited163 function BoxEditor (canvas, wgl, projectionMatrix, camera, gridSize, onLoaded, onChange) {164 this.canvas = canvas;165166 this.wgl = wgl;167168 this.gridWidth = gridSize[0];169 this.gridHeight = gridSize[1];170 this.gridDepth = gridSize[2];171 this.gridDimensions = [this.gridWidth, this.gridHeight, this.gridDepth];172173 this.projectionMatrix = projectionMatrix;174 this.camera = camera;175176 this.onChange = onChange;177178 //the cube geometry is a 1x1 cube with the origin at the bottom left corner179180 this.cubeVertexBuffer = wgl.createBuffer();181 wgl.bufferData(this.cubeVertexBuffer, wgl.ARRAY_BUFFER, new Float32Array([182 // Front face183 0.0, 0.0, 1.0,184 1.0, 0.0, 1.0,185 1.0, 1.0, 1.0,186 0.0, 1.0, 1.0,187 188 // Back face189 0.0, 0.0, 0.0,190 0.0, 1.0, 0.0,191 1.0, 1.0, 0.0,192 1.0, 0.0, 0.0,193 194 // Top face195 0.0, 1.0, 0.0,196 0.0, 1.0, 1.0,197 1.0, 1.0, 1.0,198 1.0, 1.0, 0.0,199 200 // Bottom face201 0.0, 0.0, 0.0,202 1.0, 0.0, 0.0,203 1.0, 0.0, 1.0,204 0.0, 0.0, 1.0,205 206 // Right face207 1.0, 0.0, 0.0,208 1.0, 1.0, 0.0,209 1.0, 1.0, 1.0,210 1.0, 0.0, 1.0,211 212 // Left face213 0.0, 0.0, 0.0,214 0.0, 0.0, 1.0,215 0.0, 1.0, 1.0,216 0.0, 1.0, 0.0217 ]), wgl.STATIC_DRAW);218219220221 this.cubeIndexBuffer = wgl.createBuffer();222 wgl.bufferData(this.cubeIndexBuffer, wgl.ELEMENT_ARRAY_BUFFER, new Uint16Array([223 0, 1, 2, 0, 2, 3, // front224 4, 5, 6, 4, 6, 7, // back225 8, 9, 10, 8, 10, 11, // top226 12, 13, 14, 12, 14, 15, // bottom227 16, 17, 18, 16, 18, 19, // right228 20, 21, 22, 20, 22, 23 // left229 ]), wgl.STATIC_DRAW);230231232 this.cubeWireframeVertexBuffer = wgl.createBuffer();233 wgl.bufferData(this.cubeWireframeVertexBuffer, wgl.ARRAY_BUFFER, new Float32Array([234 0.0, 0.0, 0.0,235 1.0, 0.0, 0.0,236 1.0, 1.0, 0.0,237 0.0, 1.0, 0.0,238239 0.0, 0.0, 1.0,240 1.0, 0.0, 1.0,241 1.0, 1.0, 1.0,242 0.0, 1.0, 1.0]), wgl.STATIC_DRAW);243244 this.cubeWireframeIndexBuffer = wgl.createBuffer();245 wgl.bufferData(this.cubeWireframeIndexBuffer, wgl.ELEMENT_ARRAY_BUFFER, new Uint16Array([246 0, 1, 1, 2, 2, 3, 3, 0,247 4, 5, 5, 6, 6, 7, 7, 4,248 0, 4, 1, 5, 2, 6, 3, 7249 ]), wgl.STATIC_DRAW);250251252 //there's one grid vertex buffer for the planes normal to each axis 253 this.gridVertexBuffers = [];254255 for (var axis = 0; axis < 3; ++axis) {256 this.gridVertexBuffers[axis] = wgl.createBuffer();257258 var vertexData = [];259 260261 var points; //the points that make up this grid plane262263 if (axis === 0) {264265 points = [266 [0, 0, 0],267 [0, this.gridHeight, 0],268 [0, this.gridHeight, this.gridDepth],269 [0, 0, this.gridDepth]270 ];271272 } else if (axis === 1) {273 points = [274 [0, 0, 0],275 [this.gridWidth, 0, 0],276 [this.gridWidth, 0, this.gridDepth],277 [0, 0, this.gridDepth]278 ];279 } else if (axis === 2) {280281 points = [282 [0, 0, 0],283 [this.gridWidth, 0, 0],284 [this.gridWidth, this.gridHeight, 0],285 [0, this.gridHeight, 0]286 ];287 }288289290 for (var i = 0; i < 4; ++i) {291 vertexData.push(points[i][0]);292 vertexData.push(points[i][1]);293 vertexData.push(points[i][2]);294295 vertexData.push(points[(i + 1) % 4][0]);296 vertexData.push(points[(i + 1) % 4][1]);297 vertexData.push(points[(i + 1) % 4][2]);298 }299 300301 wgl.bufferData(this.gridVertexBuffers[axis], wgl.ARRAY_BUFFER, new Float32Array(vertexData), wgl.STATIC_DRAW);302 }303304 this.pointVertexBuffer = wgl.createBuffer();305 wgl.bufferData(this.pointVertexBuffer, wgl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0]), wgl.STATIC_DRAW);306307308 this.quadVertexBuffer = wgl.createBuffer();309 wgl.bufferData(this.quadVertexBuffer, wgl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0]), wgl.STATIC_DRAW);310311312 /////////////////////////////////////////////////313 // box state314315 this.boxes = [];316317318 ////////////////////////////////////////////////319 // interaction stuff320321 //mouse x and y are in [-1, 1] (clip space)322 this.mouseX = 999;323 this.mouseY = 999;324325 this.keyPressed = []; //an array of booleans that maps a key code to whether or not it's pressed326 for (var i = 0; i < 256; ++i) {327 this.keyPressed[i] = false;328 }329330 /*331 interactions:332 click on a plane and hold down to begin drawing333 when mouse is released we enter extrusion mode for new box334 click again to create box335336 click and drag on side of boxes to resize337338 click and drag on side of boxes whilst holding shift to move339340 341 //while we're not interacting, this is null342 //while we are interacting this contains an object343 /*344345 {346 mode: the interaction mode,347348 during resizing or translating or extrusion:349 box: box we're currently manipulating,350 axis: axis of plane we're manipulating: 0, 1 or 2351 side: side of plane we're manipulating: -1 or 1352 point: the point at which the interaction started353354355 during translation we also have:356 startMax: the starting max along the interaction axis357 startMin: the starting min along the interaction axis358359360 during drawing361 box: box we're currently drawing362 point: the point at which we started drawing363 axis: the axis of the plane which we're drawing on364 side: the side of the plane which we're drawin on365366 }367 */368 this.interactionState = null;369370371 ///////////////////////////////////372 // load programs373374375 wgl.createProgramsFromFiles({376 backgroundProgram: {377 vertexShader: 'shaders/background.vert',378 fragmentShader: 'shaders/background.frag'379 },380 boxProgram: {381 vertexShader: 'shaders/box.vert',382 fragmentShader: 'shaders/box.frag'383 },384 boxWireframeProgram: {385 vertexShader: 'shaders/boxwireframe.vert',386 fragmentShader: 'shaders/boxwireframe.frag'387 },388 gridProgram: {389 vertexShader: 'shaders/grid.vert',390 fragmentShader: 'shaders/grid.frag'391 },392 pointProgram: {393 vertexShader: 'shaders/point.vert',394 fragmentShader: 'shaders/point.frag'395 }396 }, (function (programs) {397 for (var programName in programs) {398 this[programName] = programs[programName];399 }400401 onLoaded(); 402 }).bind(this));403 }404405 function quantize (x, step) {406 return Math.round(x / step) * step;407 }408409 function quantizeVector (v, step) {410 for (var i = 0; i < v.length; ++i) {411 v[i] = quantize(v[i], step);412 }413414 return v;415 }416417 BoxEditor.prototype.onKeyDown = function (event) {418 this.keyPressed[event.keyCode] = true;419 }420421 BoxEditor.prototype.onKeyUp = function (event) {422 this.keyPressed[event.keyCode] = false;423 }424425 BoxEditor.prototype.onMouseMove = function (event) {426 event.preventDefault();427428 var position = Utilities.getMousePosition(event, this.canvas);429 var normalizedX = position.x / this.canvas.width;430 var normalizedY = position.y / this.canvas.height;431432 this.mouseX = normalizedX * 2.0 - 1.0;433 this.mouseY = (1.0 - normalizedY) * 2.0 - 1.0;434435436437 if (this.interactionState !== null) {438 this.onChange();439440 if (this.interactionState.mode === InteractionMode.RESIZING || this.interactionState.mode === InteractionMode.EXTRUDING) {441 var mouseRay = this.getMouseRay();442443 //so when we are dragging to make a box bigger or smaller, what we do is we extend a line out from the intersection point normal to the plane444445 var dragLineOrigin = this.interactionState.point;446 var dragLineDirection = [0, 0, 0];447 dragLineDirection[this.interactionState.axis] = 1.0;448449 //then we find the closest point between the mouse ray and this line and use that to determine how far we've 'dragged'450 var closestPoints = closestPointsOnLines(dragLineOrigin, dragLineDirection, mouseRay.origin, mouseRay.direction);451 var newCoordinate = closestPoints[0][this.interactionState.axis]; //the new coordinate for this box plane452 newCoordinate = quantize(newCoordinate, STEP);453454 var box = this.interactionState.box,455 side = this.interactionState.side,456 axis = this.interactionState.axis;457458 //resize the box, clamping it to itself and the overall grid459 if (side === -1) {460 box.min[axis] = Math.max(Math.min(newCoordinate, box.max[axis]), 0);461 } else if (side === 1) {462 box.max[axis] = Math.min(Math.max(newCoordinate, box.min[axis]), this.gridDimensions[axis]);463 }464465 //collision detection466 for (var i = 0; i < this.boxes.length; ++i) {467 var otherBox = this.boxes[i];468 if (box !== otherBox) { //don't collide with self469 if (exclusiveAABBOverlap(box, otherBox)) {470471 //resolve collision472 if (side === -1) {473 box.min[axis] = otherBox.max[axis]; 474 } else if (side === 1) {475 box.max[axis] = otherBox.min[axis];476 }477 }478 }479 }480481 } else if (this.interactionState.mode === InteractionMode.TRANSLATING) {482483 var mouseRay = this.getMouseRay();484485 //so when we are translating a box, what we do is we extend a line out from the intersection point normal to the plane486487 var dragLineOrigin = this.interactionState.point;488 var dragLineDirection = [0, 0, 0];489 dragLineDirection[this.interactionState.axis] = 1.0;490491 //then we find the closest point between the mouse ray and this line and use that to determine how far we've 'dragged'492 var closestPoints = closestPointsOnLines(dragLineOrigin, dragLineDirection, mouseRay.origin, mouseRay.direction);493 var newCoordinate = closestPoints[0][this.interactionState.axis]; //the new coordinate for this box plane494 newCoordinate = quantize(newCoordinate, STEP);495496 var box = this.interactionState.box,497 side = this.interactionState.side,498 axis = this.interactionState.axis;499 500 501 var length = this.interactionState.startMax - this.interactionState.startMin; //the length of the box along the translation axis502503 if (side === -1) {504 box.min[axis] = newCoordinate;505 box.max[axis] = newCoordinate + length;506 } else if (side === 1) {507 box.max[axis] = newCoordinate;508 box.min[axis] = newCoordinate - length;509 }510511 //clamp to boundaries512 if (box.min[axis] < 0) {513 box.min[axis] = 0;514 box.max[axis] = length;515 }516517 if (box.max[axis] > this.gridDimensions[axis]) {518 box.max[axis] = this.gridDimensions[axis];519 box.min[axis] = this.gridDimensions[axis] - length;520 }521522 523 var translationDirection = 0; //is either -1 or 1 depending on which way we're pushing our box524 //how we resolve collisions depends on our translation direction525 if (side === -1) {526 translationDirection = newCoordinate < this.interactionState.startMin ? -1 : 1;527 } else if (side === 1) {528 translationDirection = newCoordinate < this.interactionState.startMax ? -1 : 1;529 }530531 532 var sweptBox = box.clone(); //we sweep out translating AABB for collision detection to prevent ghosting through boxes533 //reset swept box to original box location before translation534 sweptBox.min[axis] = this.interactionState.startMin;535 sweptBox.max[axis] = this.interactionState.startMax;536537 //sweep out the correct plane to where it has been translated to538 if (translationDirection === 1) {539 sweptBox.max[axis] = box.max[axis];540 } else if (translationDirection === -1) {541 sweptBox.min[axis] = box.min[axis];542 }543 544 //collision detection545 for (var i = 0; i < this.boxes.length; ++i) {546 var otherBox = this.boxes[i];547 if (box !== otherBox) { //don't collide with self548 if (exclusiveAABBOverlap(sweptBox, otherBox)) {549550 //resolve collision551 if (translationDirection === -1) {552 box.min[axis] = otherBox.max[axis]; 553 box.max[axis] = otherBox.max[axis] + length;554 } else if (translationDirection === 1) {555 box.max[axis] = otherBox.min[axis];556 box.min[axis] = otherBox.min[axis] - length;557 }558 }559 }560 }561562 } else if (this.interactionState.mode === InteractionMode.DRAWING) {563 564 var mouseRay = this.getMouseRay();565566 //get the mouse ray intersection with the drawing plane567568 var axis = this.interactionState.axis,569 side = this.interactionState.side,570 startPoint = this.interactionState.point;571572 var planeCoordinate = side === -1 ? 0 : this.gridDimensions[axis];573 var t = (planeCoordinate - mouseRay.origin[axis]) / mouseRay.direction[axis];574575 if (t > 0) { //if the mouse ray misses the drawing plane then the box just stays the same size as it was before576577 var intersection = Utilities.addVectors([], mouseRay.origin, Utilities.multiplyVectorByScalar([], mouseRay.direction, t));578 quantizeVector(intersection, STEP);579580 for (var i = 0; i < 3; ++i) {581 intersection[i] = Utilities.clamp(intersection[i], 0, this.gridDimensions[i]);582 intersection[i] = Utilities.clamp(intersection[i], 0, this.gridDimensions[i]);583 }584585 var min = [Math.min(startPoint[0], intersection[0]), Math.min(startPoint[1], intersection[1]), Math.min(startPoint[2], intersection[2])];586 var max = [Math.max(startPoint[0], intersection[0]), Math.max(startPoint[1], intersection[1]), Math.max(startPoint[2], intersection[2])];587588589 var box = this.interactionState.box;590591 var sweptBox = new AABB(min, max); //we sweep the box a bit into the grid to make sure it collides along the plane axis592 if (this.interactionState.side === -1) {593 sweptBox.max[this.interactionState.axis] = STEP * 0.1;594 } else {595 sweptBox.min[this.interactionState.axis] = this.gridDimensions[this.interactionState.axis] - STEP * 0.1;596597 }598599 //collision detection600 for (var i = 0; i < this.boxes.length; ++i) {601 var otherBox = this.boxes[i];602603 if (box !== otherBox) { //don't collide with self604 if (exclusiveAABBOverlap(sweptBox, otherBox)) {605 606 //we resolve along the axis with the smaller overlap and where the start point doesn't already overlap the other box in that axis607 var smallestOverlap = 99999999;608 var smallestOverlapAxis = -1;609610 for (var axis = 0; axis < 3; ++axis) {611 if (axis !== this.interactionState.axis) { //only resolve collisions in the drawing plane612 var overlap = Math.min(max[axis], otherBox.max[axis]) - Math.max(min[axis], otherBox.min[axis]);613614 if (overlap > 0 && overlap < smallestOverlap && (startPoint[axis] < otherBox.min[axis] || startPoint[axis] > otherBox.max[axis])) {615 smallestOverlap = overlap;616 smallestOverlapAxis = axis;617 }618 }619 }620621 if (intersection[smallestOverlapAxis] > startPoint[smallestOverlapAxis]) { //if we're resizing in the positive direction622 max[smallestOverlapAxis] = otherBox.min[smallestOverlapAxis];623 } else { //if we're resizing in the negative direction624 min[smallestOverlapAxis] = otherBox.max[smallestOverlapAxis];625 }626 }627 }628 }629630 this.interactionState.box.min = min;631 this.interactionState.box.max = max;632633 }634 }635 }636637 this.camera.onMouseMove(event);638 }639640 //returns the closest box intersection data (same as rayAABBIntersection) for the given ray641 //if there is no intersection it returns null642 BoxEditor.prototype.getBoxIntersection = function (rayOrigin, rayDirection) {643 //find the closest box that this collides with644645 var bestIntersectionSoFar = {646 aabb: null,647 t: Infinity648 }649650 for (var i = 0; i < this.boxes.length; ++i) {651 var box = this.boxes[i];652653 var intersection = rayAABBIntersection(rayOrigin, rayDirection, box);654655 if (intersection !== null) { //if there is an intersection656 if (intersection.t < bestIntersectionSoFar.t) { //if this is closer than the best we've seen so far657 bestIntersectionSoFar = intersection;658 }659 }660 }661662 if (bestIntersectionSoFar.aabb === null) { //if we didn't intersect any boxes663 return null;664 } else {665 return bestIntersectionSoFar;666 }667 }668669 //tests for intersection with one of the bounding planes670 /*671 if there is an intersection returns672 {axis, side, point}673 otherwise, returns null674 */675 BoxEditor.prototype.getBoundingPlaneIntersection = function (rayOrigin, rayDirection) {676 //we try to intersect with the two planes on each axis in turn (as long as they are facing towards the camera)677 //we assume we could only ever intersect with one of the planes so we break out as soon as we've found something678679 for (var axis = 0; axis < 3; ++axis) {680681 //now let's try intersecting with each side in turn682 for (var side = -1; side <= 1; side += 2) { //goes between -1 and 1 (hackish!683684 //first let's make sure the plane is front facing to the ray685 var frontFacing = side === -1 ? rayDirection[axis] < 0 : rayDirection[axis] > 0;686 if (frontFacing) {687 var planeCoordinate = side === -1 ? 0 : this.gridDimensions[axis]; //the coordinate of the plane along this axis688689 var t = (planeCoordinate - rayOrigin[axis]) / rayDirection[axis];690691692 if (t > 0) {693 var intersection = Utilities.addVectors([], rayOrigin, Utilities.multiplyVectorByScalar([], rayDirection, t));694695 //if we're still within the bounds of the grid696 if (intersection[0] >= 0.0 && intersection[0] <= this.gridDimensions[0] &&697 intersection[1] >= 0.0 && intersection[1] <= this.gridDimensions[1] &&698 intersection[2] >= 0.0 && intersection[2] <= this.gridDimensions[2]) {699 700 return {701 axis: axis,702 side: side,703 point: intersection704 }705 }706 }707 }708 }709 }710711 return null; //no intersection found712 }713714715 BoxEditor.prototype.onMouseDown = function (event) {716 event.preventDefault();717718 this.onMouseMove(event);719720 if (!this.keyPressed[32]) { //if space isn't held down721722 //we've finished extruding a box723 if (this.interactionState !== null && this.interactionState.mode === InteractionMode.EXTRUDING) {724 //delete zero volume boxes725 if (this.interactionState.box.computeVolume() === 0) {726 this.boxes.splice(this.boxes.indexOf(this.interactionState.box), 1);727 }728 this.interactionState = null;729730 this.onChange();731732 return;733 } else {734735 var mouseRay = this.getMouseRay();736737 //find the closest box that this collides with738739 var boxIntersection = this.getBoxIntersection(mouseRay.origin, mouseRay.direction);740741742 //if we've intersected at least one box then let's start manipulating that box743 if (boxIntersection !== null) {744 var intersection = boxIntersection;745746 if (this.keyPressed[16]) { //if we're holding shift we start to translate747 this.interactionState = {748 mode: InteractionMode.TRANSLATING,749 box: intersection.aabb,750 axis: intersection.axis,751 side: intersection.side,752 point: intersection.point,753754 startMax: intersection.aabb.max[intersection.axis],755 startMin: intersection.aabb.min[intersection.axis]756 };757 } else { //otherwise we start resizing758759 this.interactionState = {760 mode: InteractionMode.RESIZING,761 box: intersection.aabb,762 axis: intersection.axis,763 side: intersection.side,764 point: intersection.point765 };766 }767 }768769770 //if we've not intersected any box then let's see if we should start the box creation process771 if (boxIntersection === null) {772 var mouseRay = this.getMouseRay();773774 var planeIntersection = this.getBoundingPlaneIntersection(mouseRay.origin, mouseRay.direction);775776 if (planeIntersection !== null) { //if we've hit one of the planes777 //go into drawing mode778 779 var point = planeIntersection.point;780 point[0] = quantize(point[0], STEP);781 point[1] = quantize(point[1], STEP);782 point[2] = quantize(point[2], STEP);783784 var newBox = new AABB(point, point);785 this.boxes.push(newBox);786787 this.interactionState = {788 mode: InteractionMode.DRAWING,789 box: newBox,790 axis: planeIntersection.axis,791 side: planeIntersection.side,792 point: planeIntersection.point793 };794 }795796 this.onChange();797 }798799 }800801 }802 803 if (this.interactionState === null) {804 this.camera.onMouseDown(event);805 }806807 }808809 BoxEditor.prototype.onMouseUp = function (event) {810 event.preventDefault();811812 if (this.interactionState !== null) {813 if (this.interactionState.mode === InteractionMode.RESIZING) { //the end of a resize814 //if we've resized to zero volume then we delete the box815 if (this.interactionState.box.computeVolume() === 0) {816 this.boxes.splice(this.boxes.indexOf(this.interactionState.box), 1);817 }818819 this.interactionState = null;820821 } else if (this.interactionState.mode === InteractionMode.TRANSLATING) { //the end of a translate822 this.interactionState = null;823 } else if (this.interactionState.mode === InteractionMode.DRAWING) { //the end of a draw824 //TODO: DRY this825826 if (this.interactionState.box.computeSurfaceArea() > 0) { //make sure we have something to extrude827828 var mouseRay = this.getMouseRay();829830 var axis = this.interactionState.axis,831 side = this.interactionState.side,832 startPoint = this.interactionState.point;833834 var planeCoordinate = side === -1 ? 0 : this.gridDimensions[axis];835 var t = (planeCoordinate - mouseRay.origin[axis]) / mouseRay.direction[axis];836837 var intersection = Utilities.addVectors([], mouseRay.origin, Utilities.multiplyVectorByScalar([], mouseRay.direction, t));838 quantizeVector(intersection, STEP);839 840 //clamp extrusion point to grid and to box841 for (var i = 0; i < 3; ++i) {842 intersection[i] = Utilities.clamp(intersection[i], 0, this.gridDimensions[i]);843 intersection[i] = Utilities.clamp(intersection[i], this.interactionState.box.min[i], this.interactionState.box.max[i]);844 }845846847 //go into extrusion mode848 this.interactionState = {849 mode: InteractionMode.EXTRUDING,850 box: this.interactionState.box,851 axis: this.interactionState.axis,852 side: this.interactionState.side * -1,853 point: intersection854 };855856 } else { //otherwise delete the box we were editing and go straight back into regular mode857 this.boxes.splice(this.boxes.indexOf(this.interactionState.box), 1);858 this.interactionState = null;859 }860 }861862 this.onChange();863 }864865866 if (this.interactionState === null) {867 this.camera.onMouseUp(event);868 }869 }870871872 //returns an object873 /*874 {875 origin: [x, y, z],876 direction: [x, y, z] //normalized877 }878 */879 BoxEditor.prototype.getMouseRay = function () {880 var fov = 2.0 * Math.atan(1.0 / this.projectionMatrix[5]);881882 var viewSpaceMouseRay = [883 this.mouseX * Math.tan(fov / 2.0) * (this.canvas.width / this.canvas.height),884 this.mouseY * Math.tan(fov / 2.0),885 -1.0];886887 var inverseViewMatrix = Utilities.invertMatrix([], this.camera.getViewMatrix());888 var mouseRay = Utilities.transformDirectionByMatrix([], viewSpaceMouseRay, inverseViewMatrix);889 Utilities.normalizeVector(mouseRay, mouseRay);890891892 var rayOrigin = this.camera.getPosition();893894 return {895 origin: rayOrigin,896 direction: mouseRay897 };898 }899900 BoxEditor.prototype.draw = function () {901 var wgl = this.wgl;902903 wgl.clear(904 wgl.createClearState().bindFramebuffer(null).clearColor(0.9, 0.9, 0.9, 1.0),905 wgl.COLOR_BUFFER_BIT | wgl.DEPTH_BUFFER_BIT);906907 /////////////////////////////////////////////908 //draw background909910 var backgroundDrawState = wgl.createDrawState()911 .bindFramebuffer(null)912 .viewport(0, 0, this.canvas.width, this.canvas.height)913914 .useProgram(this.backgroundProgram)915916 .vertexAttribPointer(this.quadVertexBuffer, this.backgroundProgram.getAttribLocation('a_position'), 2, wgl.FLOAT, wgl.FALSE, 0, 0);917918 wgl.drawArrays(backgroundDrawState, wgl.TRIANGLE_STRIP, 0, 4);919920921 /////////////////////////////////////////////922 //draw grid923924 for (var axis = 0; axis < 3; ++axis) {925 for (var side = 0; side <= 1; ++side) {926 var cameraPosition = this.camera.getPosition();927928 var planePosition = [this.gridWidth / 2, this.gridHeight / 2, this.gridDepth / 2];929 planePosition[axis] = side === 0 ? 0 : this.gridDimensions[axis];930 931 var cameraDirection = Utilities.subtractVectors([], planePosition, cameraPosition);932933 var gridDrawState = wgl.createDrawState()934 .bindFramebuffer(null)935 .viewport(0, 0, this.canvas.width, this.canvas.height)936937 .useProgram(this.gridProgram)938939 .vertexAttribPointer(this.gridVertexBuffers[axis], this.gridProgram.getAttribLocation('a_vertexPosition'), 3, wgl.FLOAT, wgl.FALSE, 0, 0)940941 .uniformMatrix4fv('u_projectionMatrix', false, this.projectionMatrix)942 .uniformMatrix4fv('u_viewMatrix', false, this.camera.getViewMatrix());943944 var translation = [0, 0, 0];945 translation[axis] = side * this.gridDimensions[axis];946947 gridDrawState.uniform3f('u_translation', translation[0], translation[1], translation[2]);948949950 if (side === 0 && cameraDirection[axis] <= 0 || side === 1 && cameraDirection[axis] >= 0) {951 wgl.drawArrays(gridDrawState, wgl.LINES, 0, 8);952 }953 }954 }955956957 ///////////////////////////////////////////////958 //draw boxes and point959960 var boxDrawState = wgl.createDrawState()961 .bindFramebuffer(null)962 .viewport(0, 0, this.canvas.width, this.canvas.height)963964 .enable(wgl.DEPTH_TEST)965 .enable(wgl.CULL_FACE)966967 .useProgram(this.boxProgram)968969 .vertexAttribPointer(this.cubeVertexBuffer, this.boxProgram.getAttribLocation('a_cubeVertexPosition'), 3, wgl.FLOAT, wgl.FALSE, 0, 0)970971 .bindIndexBuffer(this.cubeIndexBuffer)972973 .uniformMatrix4fv('u_projectionMatrix', false, this.projectionMatrix)974 .uniformMatrix4fv('u_viewMatrix', false, this.camera.getViewMatrix())975976 .enable(wgl.POLYGON_OFFSET_FILL)977 .polygonOffset(1, 1);978979980 var boxToHighlight = null,981 sideToHighlight = null,982 highlightColor = null;983984 if (this.interactionState !== null) {985 if (this.interactionState.mode === InteractionMode.RESIZING || this.interactionState.mode === InteractionMode.EXTRUDING) {986 boxToHighlight = this.interactionState.box;987 sideToHighlight = [1.5, 1.5, 1.5];988 sideToHighlight[this.interactionState.axis] = this.interactionState.side;989990 highlightColor = [0.75, 0.75, 0.75];991 }992 } else if (!this.keyPressed[32] && !this.camera.isMouseDown()) { //if we're not interacting with anything and we're not in camera mode993 var mouseRay = this.getMouseRay();994995 var boxIntersection = this.getBoxIntersection(mouseRay.origin, mouseRay.direction);996997 //if we're over a box, let's highlight the side we're hovering over998999 if (boxIntersection !== null) {1000 boxToHighlight = boxIntersection.aabb;1001 sideToHighlight = [1.5, 1.5, 1.5];1002 sideToHighlight[boxIntersection.axis] = boxIntersection.side;10031004 highlightColor = [0.9, 0.9, 0.9];1005 }100610071008 //if we're not over a box but hovering over a bounding plane, let's draw a indicator point1009 if (boxIntersection === null && !this.keyPressed[32]) {1010 var planeIntersection = this.getBoundingPlaneIntersection(mouseRay.origin, mouseRay.direction);10111012 if (planeIntersection !== null) {1013 var pointPosition = planeIntersection.point;1014 quantizeVector(pointPosition, STEP);10151016 var rotation = [1017 new Float32Array([0, 0, 1, 0, 1, 0, 1, 0, 0]),1018 new Float32Array([1, 0, 0, 0, 0, 1, 0, 1, 0]),1019 new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1])1020 ][planeIntersection.axis];10211022 var pointDrawState = wgl.createDrawState()1023 .bindFramebuffer(null)1024 .viewport(0, 0, this.canvas.width, this.canvas.height)10251026 .enable(wgl.DEPTH_TEST)10271028 .useProgram(this.pointProgram)10291030 .vertexAttribPointer(this.pointVertexBuffer, this.pointProgram.getAttribLocation('a_position'), 3, wgl.FLOAT, wgl.FALSE, 0, 0)10311032 .uniformMatrix4fv('u_projectionMatrix', false, this.projectionMatrix)1033 .uniformMatrix4fv('u_viewMatrix', false, this.camera.getViewMatrix())10341035 .uniform3f('u_position', pointPosition[0], pointPosition[1], pointPosition[2])10361037 .uniformMatrix3fv('u_rotation', false, rotation);10381039 wgl.drawArrays(pointDrawState, wgl.TRIANGLE_STRIP, 0, 4);1040 }1041 }1042 }1043 1044 for (var i = 0; i < this.boxes.length; ++i) {1045 var box = this.boxes[i];10461047 boxDrawState.uniform3f('u_translation', box.min[0], box.min[1], box.min[2])1048 .uniform3f('u_scale', box.max[0] - box.min[0], box.max[1] - box.min[1], box.max[2] - box.min[2]);10491050 if (box === boxToHighlight) {1051 boxDrawState.uniform3f('u_highlightSide', sideToHighlight[0], sideToHighlight[1], sideToHighlight[2]);1052 boxDrawState.uniform3f('u_highlightColor', highlightColor[0], highlightColor[1], highlightColor[2]);1053 } else {1054 boxDrawState.uniform3f('u_highlightSide', 1.5, 1.5, 1.5);1055 }10561057 wgl.drawElements(boxDrawState, wgl.TRIANGLES, 36, wgl.UNSIGNED_SHORT);1058 }1059106010611062 var boxWireframeDrawState = wgl.createDrawState()1063 .bindFramebuffer(null)1064 .viewport(0, 0, this.canvas.width, this.canvas.height)10651066 .enable(wgl.DEPTH_TEST)10671068 .useProgram(this.boxWireframeProgram)10691070 .vertexAttribPointer(this.cubeWireframeVertexBuffer, this.boxWireframeProgram.getAttribLocation('a_cubeVertexPosition'), 3, wgl.FLOAT, wgl.FALSE, 0, 0)10711072 .bindIndexBuffer(this.cubeWireframeIndexBuffer)10731074 .uniformMatrix4fv('u_projectionMatrix', false, this.projectionMatrix)1075 .uniformMatrix4fv('u_viewMatrix', false, this.camera.getViewMatrix())10761077 1078 for (var i = 0; i < this.boxes.length; ++i) {1079 var box = this.boxes[i];10801081 boxWireframeDrawState.uniform3f('u_translation', box.min[0], box.min[1], box.min[2])1082 .uniform3f('u_scale', box.max[0] - box.min[0], box.max[1] - box.min[1], box.max[2] - box.min[2]);10831084 wgl.drawElements(boxWireframeDrawState, wgl.LINES, 24, wgl.UNSIGNED_SHORT);1085 }108610871088 }10891090 return {1091 BoxEditor: BoxEditor,1092 AABB: AABB,1093 InteractionMode: InteractionMode1094 }; ...

Full Screen

Full Screen

minesweeper.reducer.ts

Source:minesweeper.reducer.ts Github

copy

Full Screen

1import {createFeatureSelector, createSelector} from '@ngrx/store';2import * as ndarray from 'ndarray';3import * as util from 'util';4import {InvalidStateChangeError} from '../models/invalid-state-change.error';5import {MinesweeperActions, MinesweeperActionTypes} from '../actions/minesweeper.actions';6import {7 GameBoardCell, GameBoardState, GameProgress, InteractionState, InteractionStateType, PendingOperationType, SetupOptions8} from '../models/minesweeper.models';9const BLANK_INVALID_BOARD: ReadonlyArray<GameBoardCell> = [];10const DEFAULT_SETUP_OPTIONS: SetupOptions = {11 xSize: 3,12 ySize: 3,13 mineCount: 314};15const DEFAULT_INITIAL_BOARD: ReadonlyArray<GameBoardCell> =16 reallocateBoardContent([], DEFAULT_SETUP_OPTIONS);17export interface State18{19 readonly setupOptions: SetupOptions;20 readonly initialBoard: ReadonlyArray<GameBoardCell>;21 readonly gameBoardState?: GameBoardState;22 readonly interactionState: InteractionState;23}24export const initialState: State = {25 setupOptions: DEFAULT_SETUP_OPTIONS,26 initialBoard: DEFAULT_INITIAL_BOARD,27 interactionState: {28 type: InteractionStateType.INACTIVE,29 },30 gameBoardState: undefined31};32function areBoardOptionsValid(state: SetupOptions)33{34 const {xSize, ySize, mineCount} = state;35 return (36 (xSize >= 3) && (ySize >= 3)) && (mineCount > 0) && (mineCount < (xSize * ySize)37 );38}39function areBoardOptionsMutable(state: State) {40 return state.interactionState.type === InteractionStateType.INACTIVE;41}42function reduceCellRevelations(43 gameBoardContent: ReadonlyArray<GameBoardCell>,44 setupOptions: SetupOptions,45 cellsRevealed: ReadonlyArray<GameBoardCell>)46{47 const boardContent = [...gameBoardContent];48 const ndBoardState: ndarray<GameBoardCell> =49 ndarray<GameBoardCell>(boardContent, [setupOptions.xSize, setupOptions.ySize]);50 let revealedCell: GameBoardCell;51 for (revealedCell of cellsRevealed) {52 // Bypass ndarray.set() since it's type signature incorrectly53 // tolerates only numbers. ndarray.index() incorrectly types its54 // return as <T>, but it is truly always an integer.55 const stateIndex: number =56 ndBoardState.index(57 revealedCell.xCell, revealedCell.yCell58 ) as unknown as number;59 boardContent[stateIndex] = {60 ...ndBoardState.get(revealedCell.xCell, revealedCell.yCell),61 content: revealedCell.content62 };63 console.log(64 `Revealed ${util.inspect(revealedCell, true, 10, true)} at ${stateIndex}`);65 }66 return boardContent;67}68function reallocateBoardContent(69 oldBoardContent: ReadonlyArray<GameBoardCell>, setupOptions: SetupOptions): ReadonlyArray<GameBoardCell>70{71 if (! areBoardOptionsValid(setupOptions)) {72 return BLANK_INVALID_BOARD;73 }74 const boardSize = setupOptions.xSize * setupOptions.ySize;75 const oldBoardSize = oldBoardContent.length;76 const boardContent = new Array(boardSize);77 const noOldCell = {78 xCell: -1, yCell: -1, content: -9979 };80 for (let ii = 0, xx = 0; ii < boardSize; xx++)81 {82 for (let yy = 0; yy < setupOptions.ySize; yy++, ii++) {83 const { xCell, yCell, content } = oldBoardContent[ii]84 ? oldBoardContent[ii]85 : noOldCell;86 boardContent[ii] = ((xCell === xx) && (yCell === yy) && (content === -1))87 ? oldBoardContent[ii]88 : {89 id: ii,90 xCell: xx,91 yCell: yy,92 content: -193 };94 }95 }96 return boardContent;97}98export function reducer(state = initialState, action: MinesweeperActions): State99{100 let {setupOptions, interactionState, gameBoardState} = state;101 switch (action.type) {102 case MinesweeperActionTypes.SetXSize:103 {104 if (! areBoardOptionsMutable(state)) {105 throw new InvalidStateChangeError('Can only change board options when a game is not in progress!');106 }107 if (setupOptions.xSize === action.payload) {108 return state;109 }110 setupOptions = { ...setupOptions, xSize: action.payload };111 const initialBoard = reallocateBoardContent(state.initialBoard, setupOptions);112 return { ...state, setupOptions, initialBoard };113 }114 case MinesweeperActionTypes.SetYSize:115 {116 if (! areBoardOptionsMutable(state)) {117 throw new InvalidStateChangeError('Can only change board options when a game is not in progress!');118 }119 if (setupOptions.ySize === action.payload) {120 return state;121 }122 setupOptions = { ...setupOptions, ySize: action.payload };123 const initialBoard = reallocateBoardContent(state.initialBoard, setupOptions);124 return { ...state, setupOptions, initialBoard };125 }126 case MinesweeperActionTypes.SetMineCount:127 {128 if (! areBoardOptionsMutable(state)) {129 throw new InvalidStateChangeError('Can only change board options when a game is not in progress!');130 }131 if (setupOptions.mineCount === action.payload) {132 return state;133 }134 setupOptions = { ...setupOptions, mineCount: action.payload };135 return { ...state, setupOptions };136 }137 case MinesweeperActionTypes.SendBeginGame:138 {139 if (interactionState.type !== InteractionStateType.INACTIVE) {140 throw new InvalidStateChangeError('Cannot begin a game unless no game is in progress');141 } else if (state.initialBoard === BLANK_INVALID_BOARD) {142 throw new InvalidStateChangeError('Cannot begin a game unless board options are valid');143 }144 interactionState = {145 type: InteractionStateType.WAITING,146 expectedTurnId: -1,147 operationType: PendingOperationType.BEGIN_GAME148 };149 return { ...state, interactionState };150 }151 case MinesweeperActionTypes.SendNextMove:152 {153 if (interactionState.type !== InteractionStateType.THINKING) {154 throw new InvalidStateChangeError('Cannot make a move outside of player\'s turn.');155 }156 interactionState = {157 type: InteractionStateType.WAITING,158 operationType: PendingOperationType.TURN_OUTCOME,159 expectedTurnId: interactionState.nextTurnId,160 latestMove: action.payload,161 };162 return {163 ...state,164 interactionState165 };166 }167 case MinesweeperActionTypes.ReceiveGameContinues:168 {169 // TODO: When supporting canceling a game, tolerate receiving a belated result from cancelled game.170 if ((interactionState.type !== InteractionStateType.WAITING) ||171 (172 (interactionState.operationType !== PendingOperationType.TURN_OUTCOME) &&173 (interactionState.operationType !== PendingOperationType.BEGIN_GAME)174 )175 ) {176 throw new InvalidStateChangeError('Unexpected continue game outcome received');177 } else if (interactionState.expectedTurnId !== action.payload.afterTurnId) {178 throw new InvalidStateChangeError(179 `Outcome for turn id ${action.payload.afterTurnId} does not match expected turn id, ${interactionState.expectedTurnId}`);180 }181 const safeCellsLeft = action.payload.safeCellsLeft;182 const boardContent =183 (interactionState.operationType === PendingOperationType.TURN_OUTCOME)184 ? reduceCellRevelations(gameBoardState.boardContent, setupOptions, action.payload.cellsRevealed)185 : gameBoardState.boardContent;186 gameBoardState = {187 ...gameBoardState,188 safeCellsLeft,189 boardContent190 };191 interactionState = {192 type: InteractionStateType.THINKING,193 nextTurnId: action.payload.nextTurnId194 };195 return {196 ...state,197 gameBoardState,198 interactionState199 };200 }201 case MinesweeperActionTypes.ReceiveGameConcluded:202 {203 if ((interactionState.type !== InteractionStateType.WAITING) ||204 (205 (interactionState.operationType !== PendingOperationType.TURN_OUTCOME) &&206 (interactionState.operationType !== PendingOperationType.ABORT_GAME)207 )208 ) {209 throw new InvalidStateChangeError('Unexpected continue game outcome received');210 } else if (interactionState.expectedTurnId !== action.payload.afterTurnId) {211 throw new InvalidStateChangeError(212 `Outcome for turn id ${action.payload.afterTurnId} does not match expected turn id, ${interactionState.expectedTurnId}`);213 }214 let previousGame;215 if (!! gameBoardState) {216 const {xSize, ySize} = setupOptions;217 const latestMove =218 (interactionState.operationType === PendingOperationType.TURN_OUTCOME)219 ? interactionState.latestMove : undefined;220 const safeCellsLeft = action.payload.safeCellsLeft;221 const boardContent = reduceCellRevelations(222 gameBoardState.boardContent, setupOptions, action.payload.cellsRevealed);223 previousGame = { xSize, ySize, boardContent, safeCellsLeft, latestMove };224 } else {225 const {xSize, ySize} = setupOptions;226 const boardContent = action.payload.cellsRevealed;227 const safeCellsLeft = action.payload.safeCellsLeft;228 previousGame = { xSize, ySize, boardContent, safeCellsLeft };229 }230 interactionState = {231 type: InteractionStateType.INACTIVE,232 previousGame233 };234 return { ...state, gameBoardState, interactionState };235 }236 case MinesweeperActionTypes.SendAbortGame:237 {238 if (interactionState.type === InteractionStateType.INACTIVE) {239 throw new InvalidStateChangeError('Can only end a game when there is a game to end.');240 } else if (interactionState.type !== InteractionStateType.THINKING) {241 throw new InvalidStateChangeError('Please wait for pending network activity to abate.');242 }243 interactionState = {244 type: InteractionStateType.WAITING,245 operationType: PendingOperationType.ABORT_GAME,246 expectedTurnId: interactionState.nextTurnId247 };248 return {249 ...state,250 interactionState251 };252 }253 default:254 return state;255 }256}257export const featureKey = 'minesweeper';258export const selectMinesweeperState = createFeatureSelector<State>(featureKey);259export const selectSetupOptions = createSelector(260 selectMinesweeperState, (state: State) => state.setupOptions261);262export const selectInteractionState = createSelector(263 selectMinesweeperState, (state: State) => state.interactionState264);265export const selectNextTurnId = createSelector(266 selectInteractionState, (interactionState: InteractionState) => {267 if (interactionState.type === InteractionStateType.THINKING) {268 return interactionState.nextTurnId;269 }270 });271export const selectExpectedTurnId = createSelector(272 selectInteractionState, (interactionState: InteractionState) => {273 if (interactionState.type === InteractionStateType.WAITING) {274 return interactionState.expectedTurnId;275 }276 });277export const selectGameBoardState = createSelector(278 selectMinesweeperState, (state: State) => state.gameBoardState);279export const selectInitialBoardState = createSelector(280 selectMinesweeperState, (state: State) => state.initialBoard);281export const selectGameProgress = createSelector(282 [selectSetupOptions, selectInitialBoardState, selectGameBoardState, selectInteractionState],283 function (284 setupOptions: SetupOptions,285 initialBoard: ReadonlyArray<GameBoardCell>,286 gameBoardState: GameBoardState,287 interactionState: InteractionState): GameProgress288 {289 const interactionStateType = interactionState.type;290 if (!! gameBoardState) {291 const {xSize, ySize} = setupOptions;292 const {boardContent, safeCellsLeft} = gameBoardState;293 let latestMove;294 if ((interactionState.type === InteractionStateType.WAITING)295 && (interactionState.operationType === PendingOperationType.TURN_OUTCOME)) {296 latestMove = interactionState.latestMove;297 }298 return {299 xSize,300 ySize,301 boardContent,302 safeCellsLeft,303 latestMove,304 interactionStateType305 };306 } else if (interactionState.type === InteractionStateType.INACTIVE) {307 return {308 ...interactionState.previousGame,309 interactionStateType310 };311 } else {312 const {xSize, ySize} = setupOptions;313 const boardContent = initialBoard;314 const safeCellsLeft = initialBoard.length - setupOptions.mineCount;315 return {316 xSize,317 ySize,318 boardContent,319 safeCellsLeft,320 interactionStateType321 };322 }323 });324export const selectSafeCellsLeft = createSelector(325 selectGameProgress, function(progress: GameProgress): number {326 return progress.safeCellsLeft;327 }328);329export const selectPlayerHasTurn = createSelector(330 selectInteractionState,331 function (state: InteractionState): boolean {332 return state.type === InteractionStateType.THINKING;333 }334);335export const selectPlayerMayStartGame = createSelector(336 [selectInteractionState, selectSetupOptions],337 function (interactionState: InteractionState, setupOptions: SetupOptions): boolean338 {339 return (interactionState.type === InteractionStateType.INACTIVE)340 && areBoardOptionsValid(setupOptions);341 }342);343export const selectWaitingOnServer = createSelector(344 selectInteractionState,345 function (state: InteractionState): boolean {346 return state.type === InteractionStateType.WAITING;347 }348);349export const selectGameInProgress = createSelector(350 selectInteractionState,351 function (interactionState: InteractionState) {352 return interactionState.type !== InteractionStateType.INACTIVE;353 }354);355export const allSelectors = {356 selectSetupOptions: selectSetupOptions,357 selectNextTurnId: selectNextTurnId,358 selectExpectedTurnId: selectExpectedTurnId,359 selectGameProgress: selectGameProgress,360 selectSafeCellsLeft: selectSafeCellsLeft,361 selectPlayerHasTurn: selectPlayerHasTurn,362 selectPlayerMayStartGame: selectPlayerMayStartGame,363 selectWaitingOnServer: selectWaitingOnServer,364 selectGameInProgress: selectGameInProgress...

Full Screen

Full Screen

59c000PanResponder.js

Source:59c000PanResponder.js Github

copy

Full Screen

1'use strict';2var InteractionManager = require('./InteractionManager');3var TouchHistoryMath = require('TouchHistoryMath');4var currentCentroidXOfTouchesChangedAfter = TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;5var currentCentroidYOfTouchesChangedAfter = TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;6var previousCentroidXOfTouchesChangedAfter = TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;7var previousCentroidYOfTouchesChangedAfter = TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;8var currentCentroidX = TouchHistoryMath.currentCentroidX;9var currentCentroidY = TouchHistoryMath.currentCentroidY;10var PanResponder = {11 _initializeGestureState: function _initializeGestureState(gestureState) {12 gestureState.moveX = 0;13 gestureState.moveY = 0;14 gestureState.x0 = 0;15 gestureState.y0 = 0;16 gestureState.dx = 0;17 gestureState.dy = 0;18 gestureState.vx = 0;19 gestureState.vy = 0;20 gestureState.numberActiveTouches = 0;21 gestureState._accountsForMovesUpTo = 0;22 },23 _updateGestureStateOnMove: function _updateGestureStateOnMove(gestureState, touchHistory) {24 gestureState.numberActiveTouches = touchHistory.numberActiveTouches;25 gestureState.moveX = currentCentroidXOfTouchesChangedAfter(touchHistory, gestureState._accountsForMovesUpTo);26 gestureState.moveY = currentCentroidYOfTouchesChangedAfter(touchHistory, gestureState._accountsForMovesUpTo);27 var movedAfter = gestureState._accountsForMovesUpTo;28 var prevX = previousCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);29 var x = currentCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);30 var prevY = previousCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);31 var y = currentCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);32 var nextDX = gestureState.dx + (x - prevX);33 var nextDY = gestureState.dy + (y - prevY);34 var dt = touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo;35 gestureState.vx = (nextDX - gestureState.dx) / dt;36 gestureState.vy = (nextDY - gestureState.dy) / dt;37 gestureState.dx = nextDX;38 gestureState.dy = nextDY;39 gestureState._accountsForMovesUpTo = touchHistory.mostRecentTimeStamp;40 },41 create: function create(config) {42 var interactionState = {43 handle: null44 };45 var gestureState = {46 stateID: Math.random()47 };48 PanResponder._initializeGestureState(gestureState);49 var panHandlers = {50 onStartShouldSetResponder: function onStartShouldSetResponder(e) {51 return config.onStartShouldSetPanResponder === undefined ? false : config.onStartShouldSetPanResponder(e, gestureState);52 },53 onMoveShouldSetResponder: function onMoveShouldSetResponder(e) {54 return config.onMoveShouldSetPanResponder === undefined ? false : config.onMoveShouldSetPanResponder(e, gestureState);55 },56 onStartShouldSetResponderCapture: function onStartShouldSetResponderCapture(e) {57 if (e.nativeEvent.touches.length === 1) {58 PanResponder._initializeGestureState(gestureState);59 }60 gestureState.numberActiveTouches = e.touchHistory.numberActiveTouches;61 return config.onStartShouldSetPanResponderCapture !== undefined ? config.onStartShouldSetPanResponderCapture(e, gestureState) : false;62 },63 onMoveShouldSetResponderCapture: function onMoveShouldSetResponderCapture(e) {64 var touchHistory = e.touchHistory;65 if (gestureState._accountsForMovesUpTo === touchHistory.mostRecentTimeStamp) {66 return false;67 }68 PanResponder._updateGestureStateOnMove(gestureState, touchHistory);69 return config.onMoveShouldSetPanResponderCapture ? config.onMoveShouldSetPanResponderCapture(e, gestureState) : false;70 },71 onResponderGrant: function onResponderGrant(e) {72 if (!interactionState.handle) {73 interactionState.handle = InteractionManager.createInteractionHandle();74 }75 gestureState.x0 = currentCentroidX(e.touchHistory);76 gestureState.y0 = currentCentroidY(e.touchHistory);77 gestureState.dx = 0;78 gestureState.dy = 0;79 if (config.onPanResponderGrant) {80 config.onPanResponderGrant(e, gestureState);81 }82 return config.onShouldBlockNativeResponder === undefined ? true : config.onShouldBlockNativeResponder();83 },84 onResponderReject: function onResponderReject(e) {85 clearInteractionHandle(interactionState, config.onPanResponderReject, e, gestureState);86 },87 onResponderRelease: function onResponderRelease(e) {88 clearInteractionHandle(interactionState, config.onPanResponderRelease, e, gestureState);89 PanResponder._initializeGestureState(gestureState);90 },91 onResponderStart: function onResponderStart(e) {92 var touchHistory = e.touchHistory;93 gestureState.numberActiveTouches = touchHistory.numberActiveTouches;94 if (config.onPanResponderStart) {95 config.onPanResponderStart(e, gestureState);96 }97 },98 onResponderMove: function onResponderMove(e) {99 var touchHistory = e.touchHistory;100 if (gestureState._accountsForMovesUpTo === touchHistory.mostRecentTimeStamp) {101 return;102 }103 PanResponder._updateGestureStateOnMove(gestureState, touchHistory);104 if (config.onPanResponderMove) {105 config.onPanResponderMove(e, gestureState);106 }107 },108 onResponderEnd: function onResponderEnd(e) {109 var touchHistory = e.touchHistory;110 gestureState.numberActiveTouches = touchHistory.numberActiveTouches;111 clearInteractionHandle(interactionState, config.onPanResponderEnd, e, gestureState);112 },113 onResponderTerminate: function onResponderTerminate(e) {114 clearInteractionHandle(interactionState, config.onPanResponderTerminate, e, gestureState);115 PanResponder._initializeGestureState(gestureState);116 },117 onResponderTerminationRequest: function onResponderTerminationRequest(e) {118 return config.onPanResponderTerminationRequest === undefined ? true : config.onPanResponderTerminationRequest(e, gestureState);119 }120 };121 return {122 panHandlers: panHandlers,123 getInteractionHandle: function getInteractionHandle() {124 return interactionState.handle;125 }126 };127 }128};129function clearInteractionHandle(interactionState, callback, event, gestureState) {130 if (interactionState.handle) {131 InteractionManager.clearInteractionHandle(interactionState.handle);132 interactionState.handle = null;133 }134 if (callback) {135 callback(event, gestureState);136 }137}...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1const { Interaction } = require('@pact-foundation/pact');2const { like } = require('@pact-foundation/pact/dsl/matchers');3const interaction = new Interaction()4 .given('some state')5 .uponReceiving('a request to get a user')6 .withRequest({7 headers: {8 },9 })10 .willRespondWith({11 headers: {12 },13 body: {14 name: like('Bob'),15 },16 });17module.exports = interaction;18const { Interaction } = require('@pact-foundation/pact');19const { like } = require('@pact-foundation/pact/dsl/matchers');20const interaction = new Interaction()21 .given('some state')22 .uponReceiving('a request to get a user')23 .withRequest({24 headers: {25 },26 })27 .willRespondWith({28 headers: {29 },30 body: {31 name: like('Bob'),32 },33 });34module.exports = interaction;35const { Interaction } = require('@pact-foundation/pact');36const { like } = require('@pact-foundation/pact/dsl/matchers');37const interaction = new Interaction()38 .given('some state')39 .uponReceiving('a request to get a user')40 .withRequest({41 headers: {42 },43 })44 .willRespondWith({45 headers: {46 },47 body: {48 name: like('Bob'),49 },50 });51module.exports = interaction;

Full Screen

Using AI Code Generation

copy

Full Screen

1var interactionState = require('pact-foundation-pact').interactionState;2var interactionState = require('pact-foundation-pact').interactionState;3var interactionState = require('pact-foundation-pact').interactionState;4var interactionState = require('pact-foundation-pact').interactionState;5var interactionState = require('pact-foundation-pact').interactionState;6var interactionState = require('pact-foundation-pact').interactionState;7var interactionState = require('pact-foundation-pact').interactionState;8var interactionState = require('pact-foundation-pact').interactionState;9var interactionState = require('pact-foundation-pact').interactionState;10var interactionState = require('pact-foundation-pact').interactionState;11var interactionState = require('pact-foundation-pact').interactionState;12var interactionState = require('pact-foundation-pact').interactionState;13var interactionState = require('pact-foundation-pact').interactionState;14var interactionState = require('pact-foundation-pact').interactionState;15var interactionState = require('pact-foundation-pact').interactionState;16var interactionState = require('pact-foundation-pact').interactionState;17var interactionState = require('pact-foundation-pact').interactionState;18var interactionState = require('pact-foundation-pact').interactionState;19var interactionState = require('pact-foundation-pact').interactionState;20var interactionState = require('pact-foundation-pact').interactionState;21var interactionState = require('pact-foundation-pact').interactionState;22var interactionState = require('pact-foundation-pact').interactionState;

Full Screen

Using AI Code Generation

copy

Full Screen

1const { InteractionObject } = require('@pact-foundation/pact');2const { Matchers } = require('@pact-foundation/pact');3const interaction = new InteractionObject({4 withRequest: {5 },6 willRespondWith: {7 headers: {8 'Content-Type': 'application/json; charset=utf-8',9 },10 body: Matchers.eachLike(11 {12 id: Matchers.term({13 }),14 name: Matchers.like('John'),15 },16 { min: 1 }17 },18});19module.exports = interaction;20const { Matchers } = require('@pact-foundation/pact');21const { interactionState } = require('./test2.js');22describe('Pact', () => {23 describe('test2', () => {24 beforeAll(async () => {25 await interactionState();26 });27 it('should create a new state', async () => {28 });29 });30});31const { InteractionObject } = require('@pact-foundation/pact');32const { Matchers } = require('@pact-foundation/pact');

Full Screen

Using AI Code Generation

copy

Full Screen

1 .given("test state")2 .uponReceiving("a request for test state")3 .withRequest("get", "/test/state")4 .willRespondWith({5 body: {6 }7 });8 .given("test state 2")9 .uponReceiving("a request for test state 2")10 .withRequest("get", "/test/state/2")11 .willRespondWith({12 body: {13 }14 });15describe("Pact", () => {16 describe("test2", () => {17 it("test2", () => {18 return provider.setup().then(() => {19 return axios.get(`${provider.mockService.baseUrl}/test/state`).then(() => {20 return axios.get(`${provider.mockService.baseUrl}/test/state/2`);21 });22 });23 });24 });25});

Full Screen

Using AI Code Generation

copy

Full Screen

1import { interactionState } from "pact-foundation-pact-web";2const state = interactionState();3state.get().then((currentState) => {4 console.log(currentState);5});6state.set("a new state").then((currentState) => {7 console.log(currentState);8});

Full Screen

Automation Testing Tutorials

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.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run pact-foundation-pact automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful