UNPKG

jsge

Version:

Javascript Game Engine

633 lines (549 loc) 20.5 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="expires" content="Wen, 05 Jun 2024 07:01:00 GMT"> <title>JSDoc: Source: utils.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="stage-title">Source: utils.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>import { Vector } from "./base/2d/Primitives.js"; function isMobile() { return /Android|webOS|iPhone|iPad|iPod|Opera Mini/i.test(navigator.userAgent) ; } function isSafari() { return /^((?!chrome|android).)*safari/i.test(navigator.userAgent); } function pointToCircleDistance(x, y, circle) { const pointToCircleCenterDistance = new Vector(x, y, circle.x, circle.y).length; return pointToCircleCenterDistance - circle.r; } function countClosestTraversal(line, sight) { const x1 = sight.x1, y1 = sight.y1, x2 = sight.x2, y2 = sight.y2; const x3 = line.x1, y3 = line.y1, x4 = line.x2, y4 = line.y2; const r_px = x1, r_py = y1, r_dx = x2-x1, r_dy = y2-y1; const s_px = x3, s_py = y3, s_dx = x4-x3, s_dy = y4-y3; const r_mag = Math.sqrt(r_dx*r_dx+r_dy*r_dy), s_mag = Math.sqrt(s_dx*s_dx+s_dy*s_dy); if(r_dx/r_mag==s_dx/s_mag &amp;&amp; r_dy/r_mag==s_dy/s_mag){ return null; } const T2 = (r_dx*(s_py-r_py) + r_dy*(r_px-s_px))/(s_dx*r_dy - s_dy*r_dx), T1 = (s_px+s_dx*T2-r_px)/r_dx; if(T1&lt;0 || isNaN(T1)) return null; if(T2&lt;0 || T2>1) return null; return { x: r_px+r_dx*T1, y: r_py+r_dy*T1, p: T1 }; } /** * * @param {{x1:number, y1:number, x2:number, y2:number}} line1 * @param {{x1:number, y1:number, x2:number, y2:number}} line2 * @returns {{x:number, y:number, p:number} | undefined} * @ignore */ function countClosestTraversal2(line1, line2) { const x1 = line2.x1, y1 = line2.y1, x2 = line2.x2, y2 = line2.y2; const x3 = line1.x1, y3 = line1.y1, x4 = line1.x2, y4 = line1.y2; const det = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); // lines are parallel, or coincident if (det === 0){ return; } let x = ((x1*y2 - y1*x2) * (x3 - x4) - (x1 - x2) * (x3*y4 - y3*x4)) / det; let y = ((x1*y2 - y1*x2) * (y3 - y4) - (y1 - y2) * (x3*y4 - y3*x4)) / det; const point = {x, y}; if (isPointOnTheLine(point, line1, 0.0000000000001) &amp;&amp; isPointOnTheLine(point, line2, 0.0000000000001)) { const p = Math.sqrt(Math.pow((x - x1), 2) + Math.pow((y - y1), 2)); return {x, y, p}; } else { return; } } function angle_2points(x1, y1, x2, y2) { return Math.atan2(y2 - y1, x2 - x1); } function angle_3points(a, b, c) { const x1 = a.x - b.x, x2 = c.x - b.x, y1 = a.y - b.y, y2 = c.y - b.y, d1 = Math.sqrt(x1 * x1 + y1 * y1), d2 = Math.sqrt(x2 * x2 + y2 * y2); //console.log("angle: ", (Math.acos((x1* x2 + y1 * y2) / (d1 * d2))* 180) / Math.PI); return Math.acos((x1* x2 + y1 * y2) / (d1 * d2)); } function dotProductWithAngle(lenA, lenB, angle) { return lenA * lenB * Math.cos(angle); } function dotProduct(vec1, vec2) { return vec1.x * vec2.x + vec1.y * vec2.y; } function crossProduct(a, b) { return (a.x * b.y - b.x * a.y); } function isPointOnTheLine(point, line, m_error = 0) { return ( ((point.x >= (line.x1 - m_error)) &amp;&amp; (point.x &lt;= (line.x2 + m_error))) || ((point.x &lt;= (line.x1 + m_error)) &amp;&amp; (point.x >= (line.x2 - m_error))) ) &amp;&amp; ( ((point.y >= (line.y1 - m_error)) &amp;&amp; (point.y &lt;= (line.y2 + m_error))) || ((point.y &lt;= (line.y1 + m_error)) &amp;&amp; (point.y >= (line.y2 - m_error))) ); } function countDistance(obj1, obj2) { return new Vector(obj1.x, obj1.y, obj2.x, obj2.y).length; } function isLineShorter(line1, line2) { return (new Vector(line1.x1, line1.y1, line1.x2, line1.y2)).length &lt; (new Vector(line2.x1, line2.y1, line2.x2, line2.y2)).length; } function isPointLineIntersect(point, line) { const lineL = new Vector(line.x1, line.y1, line.x2, line.y2).length, lengthAB = new Vector(line.x1, line.y1, point.x, point.y).length + new Vector(line.x2, line.y2, point.x, point.y).length; if (lengthAB &lt;= lineL + 0.2) { //console.log("point to line intersect. line len: " + lineL + ", line AB len: " + lengthAB); return true; } return false; } /** * * @param {Array&lt;Array&lt;number>>} polygon * @param {{x1:number, y1:number, x2:number, y2:number}} line * @returns {{x:number, y:number, p:number} | null} * @ignore */ function isPolygonLineIntersect(polygon, line) { const len = polygon.length; for (let i = 0; i &lt; len; i+=1) { let curr = polygon[i], next = polygon[i+1]; //if next item not exist and current is not first if (!next) { // if current vertex is not the first one if (!(curr[0] === polygon[0][0] &amp;&amp; curr[1] === polygon[0][1])) { next = polygon[0]; } else { continue; } } const edge = { x1: curr[0], y1: curr[1], x2: next[0], y2: next[1] }; const intersection = countClosestTraversal2(edge, line); if (intersection) { return intersection; } } if (polygon[len-1][0] !== polygon[0][0] &amp;&amp; polygon[len-1][1] !== polygon[0][1]) { //check one last item const curr = polygon[len - 1], next = polygon[0]; const edge = { x1: curr[0], y1: curr[1], x2: next[0], y2: next[1] }; const intersection = countClosestTraversal2(edge, line); if (intersection) { return intersection; } } return null; } function isPointPolygonIntersect(x, y, polygon) { const len = polygon.length; for (let i = 0; i &lt; len; i+=1) { let vertex1 = polygon[i], vertex2 = polygon[i + 1]; // if last vertex, set vertex2 as the first if (!vertex2) { vertex2 = polygon[0]; } if (isPointLineIntersect({x,y}, {x1: vertex1[0], y1: vertex1[1], x2: vertex2[0], y2: vertex2[1]})) { return true; } } return false; } function isPointInsidePolygon(x, y, polygon) { const len = polygon.length; let intersections = 0; for (let i = 0; i &lt; len; i++) { let vertex1 = polygon[i], vertex2 = polygon[i + 1] ? polygon[i + 1] : polygon[0], x1 = vertex1[0], y1 = vertex1[1], x2 = vertex2[0], y2 = vertex2[1]; if (y &lt; y1 !== y &lt; y2 &amp;&amp; x &lt; (x2 - x1) * (y - y1) / (y2 - y1) + x1) { intersections++; } } if (intersections > 0) { if (intersections % 2 === 0) { return false; } else { return true; } } else { return false; } } function isPointRectIntersect(x, y, rect) { if (x >= rect.x &amp;&amp; x &lt;= rect.width + rect.x &amp;&amp; y >= rect.y &amp;&amp; y &lt;= rect.y + rect.height) { return true; } else { return false; } } /** * * @param {number} x * @param {number} y * @param {{x:number, y:number, r:number}} circle * @returns {boolean} */ function isPointCircleIntersect(x, y, circle) { const radius = circle.r, lineToCircleCenter = new Vector(x, y, circle.x, circle.y), pointCircleLineLength = lineToCircleCenter.length; if (pointCircleLineLength &lt; radius) return true; else return false; } function isCircleLineIntersect(x, y, r, line) { const x1 = line.x1, y1 = line.y1, x2 = line.x2, y2 = line.y2, vec1 = {x: x1 - x, y: y1-y}, //new Vector(x, y, x1, y1), vec2 = {x: x2 - x, y: y2-y}, //new Vector(x, y, x2, y2), vec3 = {x: x2 - x1, y: y2-y1}, //new Vector(x1 ,y1, x2, y2), vec4 = {x: x1 - x2, y: y1-y2}, //new Vector(x2, y2, x1, y1), vec3Len = Math.sqrt(Math.pow(vec3.x, 2) + Math.pow(vec3.y, 2)),//vec3.length, dotP1 = dotProduct(vec1, vec4), dotP2 = dotProduct(vec2, vec3); // checks if the line is inside the circle, // max_dist = Math.max(vec1Len, vec2Len); let min_dist; if (dotP1 > 0 &amp;&amp; dotP2 > 0) { min_dist = crossProduct(vec1,vec2)/vec3Len; if (min_dist &lt; 0) { min_dist *= -1; } } else { min_dist = Math.min(vec1.length, vec2.length); } if (min_dist &lt;= r) { // &amp;&amp; max_dist >= r) { return true; } else { return false; } } /** * * @param {Array&lt;number>} ellipse - x,y,radX,radY * @param {Array&lt;Array&lt;number>>} line [x1,y1],[x2,y2] */ function isEllipseLineIntersect(ellipse, line) { const x = ellipse[0], y = ellipse[1], radX = ellipse[2], radY = ellipse[3], x1 = line[0][0], y1 = line[0][1], x2 = line[1][0], y2 = line[1][1], lineAToElCenter = { x: x - x1, y: y - y1 }, //new Vector(x, y, x1, y1), lineBToElCenter = { x: x - x2, y: y - y2 }, //new Vector(x, y, x2, y2), lineAToElCenterLen = Math.sqrt(Math.pow(lineAToElCenter.x, 2) + Math.pow(lineAToElCenter.y, 2)), lineBToElCenterLen = Math.sqrt(Math.pow(lineBToElCenter.x, 2) + Math.pow(lineBToElCenter.y, 2)), lineToCenterLenMin = Math.min(lineAToElCenterLen, lineBToElCenterLen), ellipseMax = Math.max(radX, radY); if (lineToCenterLenMin > ellipseMax) { return false; } const traversalLine = lineToCenterLenMin === lineAToElCenterLen ? lineAToElCenter : lineBToElCenter, angleToAxisX = Math.atan2(traversalLine.y, traversalLine.x); const intersectX = Math.cos(angleToAxisX) * radX, intersectY = Math.sin(angleToAxisX) * radY, lineToCenter = { x: 0 - intersectX, y: 0 - intersectY }, intersectLineLen = Math.sqrt(Math.pow(lineToCenter.x, 2) + Math.pow(lineToCenter.y, 2)); //console.log("lenToCheck: ", lenToCheck); //console.log("x: ", intersectX); if (lineToCenterLenMin > intersectLineLen) { return false; } return true; } /** * * @param {Array&lt;number>} ellipse - x,y,radX,radY * @param {{x:number, y:number, r:number}} circle * @returns {{x:number, y:number, p:number} | boolean} */ function isEllipseCircleIntersect(ellipse, circle) { const ellipseX = ellipse[0], ellipseY = ellipse[1], ellipseToCircleLine = { x: ellipseX - circle.x, y: ellipseY - circle.y }, len = Math.sqrt(Math.pow(ellipseToCircleLine.x, 2) + Math.pow(ellipseToCircleLine.y, 2)), maxRad = Math.max(ellipse[2], ellipse[3]); // no collisions for sure if (len > (maxRad + circle.r)) { return false; } else { // check possible collision const angle = angle_2points(ellipseX, ellipseY, circle.x, circle.y), traversalX = ellipseX + (ellipse[2] * Math.cos(angle)), traversalY = ellipseY + (ellipse[3] * Math.sin(angle)), vecTrX = ellipseX - traversalX, vecTrY = ellipseY - traversalY, traversalLen = Math.sqrt(Math.pow(vecTrX, 2) + Math.pow(vecTrY, 2)) + circle.r; if (len &lt;= traversalLen) { return {x: vecTrX, y: vecTrY, p:1}; } else { return false; } } } /** * * @param {Array&lt;number>} ellipse - x,y,radX,radY * @param {Array&lt;Array&lt;number>>} polygon - x,y * @returns {boolean} */ function isEllipsePolygonIntersect(ellipse, polygon) { const len = polygon.length; for (let i = 0; i &lt; len; i+=1) { let vertex1 = polygon[i], vertex2 = polygon[i + 1]; // if last vertex, set vertex2 as the first if (!vertex2) { vertex2 = polygon[0]; } if (isEllipseLineIntersect(ellipse, [vertex1, vertex2])) { return true; } } return false; } function generateUniqId() { return Math.round(Math.random() * 1000000); } function randomFromArray(array) { return array[Math.floor(Math.random()*array.length)]; } function verticesArrayToArrayNumbers(array) { const len = array.length, numbers = []; for (let i = 0; i &lt; len; i++) { const vertex = array[i]; numbers.push([vertex.x, vertex.y]); } return numbers; } /** * * @param {Array&lt;Array&lt;number>>} arrayDots * @returns {Array&lt;Array&lt;number>>} */ function calculateLinesVertices(x = 0, y = 0, r, arrayDots) { const len = arrayDots.length; let arrayLines = Array(len), arrayDotsIterator = 0; for (let i = 0; i &lt; len; i++) { const dot1 = arrayDots[i]; let dot2 = arrayDots[i+1]; if (!dot2) { dot2 = arrayDots[0]; } const x1 = dot1[0], y1 = dot1[1], x2 = dot2[0], y2 = dot2[1]; const x1R = x1 * Math.cos(r) - y1 * Math.sin(r), y1R = x1 * Math.sin(r) + y1 * Math.cos(r), x2R = x2 * Math.cos(r) - y2 * Math.sin(r), y2R = x2 * Math.sin(r) + y2 * Math.cos(r); const line = [x1R + x, y1R + y, x2R + x, y2R + y]; arrayLines[arrayDotsIterator] = line; arrayDotsIterator++; } return arrayLines; } /** * @param {number} x * @param {number} y * @param {number} radiusX * @param {number} radiusY * @param {number} [angle = 2 * Math.PI] * @param {number} [step = Math.PI/12] * @returns {Array&lt;Array&lt;number>>} */ function calculateEllipseVertices(x = 0, y = 0, radiusX, radiusY, angle = 2*Math.PI, step = Math.PI/8) { let ellipsePolygonCoords = []; for (let r = 0; r &lt;= angle; r += step) { let x2 = Math.cos(r) * radiusX + x, y2 = Math.sin(r) * radiusY + y; ellipsePolygonCoords.push([x2, y2]); } return ellipsePolygonCoords; } /** * * @param { Array&lt;number> } mat1 * @param { Array&lt;number> } mat2 * @returns { Array&lt;number> } */ function mat3Multiply(mat1, mat2) { let matResult = []; for (let resultIdx = 0; resultIdx &lt; 9; resultIdx += 3) { let resultIndex = resultIdx; for (let i = 0; i &lt; 3; i++) { let resultVal = 0, k = i; for (let j = 0; j &lt; 3; j++) { const mat1Val = mat1[resultIdx + j], mat2Val = mat2[k]; resultVal += (mat1Val * mat2Val); k+=3; } matResult[resultIndex] = resultVal; resultIndex++; } } return matResult; } /** * * @param {Array&lt;number>} mat3 [a, b, c, * d. e, f, * g, h, i] * @param {Array&lt;number>} vec3 [x1, y1] * @returns {Array&lt;number>} [a * x1 + b * y1 + c * 1, d * x1 + e * y1 + f * 1] */ function mat3MultiplyVector (mat3, vec3) { let result = []; let resultIndex = 0; for (let rowStartIdx = 0; rowStartIdx &lt; 6; rowStartIdx += 3) { let resultVal = 0; const stopInt = rowStartIdx + 3; let vecIdx = 0; for (let rowIdx = rowStartIdx; rowIdx &lt; stopInt; rowIdx++) { const matVal = mat3[rowIdx], vecVal = vec3[vecIdx] || 1; // z1 coord resultVal += (matVal * vecVal); vecIdx++; } result[resultIndex] = resultVal; resultIndex++; } return result; } /** * * @param {Array&lt;number>} mat3 [a, b, c, * d. e, f, * g, h, i] * @param {Array&lt;number>} vec3 [x1, y1, x2, y1, x1, y2, x1, y2, x2, y1, x2, y2, ...] * @returns {Array&lt;number>} [a*x1 + b*y1 + c*1, d*y1 + e*y1 + f*1, ...] */ function mat3MultiplyPosCoords (mat3, vec3) { const vec3Len = vec3.length; let result = []; let resultIndex = 0; for (let nPair = 0; nPair &lt; vec3Len; nPair += 2) { for (let rowStartIdx = 0; rowStartIdx &lt; 6; rowStartIdx += 3) { let resultVal = 0; const stopInt = rowStartIdx + 3; let vecIdx = nPair; let iteration = 1; for (let rowIdx = rowStartIdx; rowIdx &lt; stopInt; rowIdx++) { const matVal = mat3[rowIdx], vecVal = iteration === 3 ? 1 : vec3[vecIdx]; // 3: z1 = 1 coord resultVal += (matVal * vecVal); vecIdx++; iteration++; } result[resultIndex] = resultVal; resultIndex++; } } return result; } export { isMobile, isSafari, pointToCircleDistance, countClosestTraversal, countClosestTraversal2, angle_2points, angle_3points, dotProductWithAngle, dotProduct, crossProduct, isPointOnTheLine, isLineShorter, isPointLineIntersect, isPointPolygonIntersect, isPointRectIntersect, isPointCircleIntersect, isPolygonLineIntersect, isCircleLineIntersect, isEllipseLineIntersect, isEllipseCircleIntersect, isEllipsePolygonIntersect, isPointInsidePolygon, generateUniqId, randomFromArray, verticesArrayToArrayNumbers, countDistance, calculateEllipseVertices, calculateLinesVertices, mat3Multiply, mat3MultiplyVector, mat3MultiplyPosCoords };</code></pre> </article> </section> </div> <nav> <div class="switcher"><ul><li class="active"><a href="/">1.5.9</a></li></div> <h2><a href="/">Home</a></h2><h3>Classes</h3><ul><li><a href="DrawCircleObject.html">DrawCircleObject</a></li><li><a href="DrawConusObject.html">DrawConusObject</a></li><li><a href="DrawImageObject.html">DrawImageObject</a></li><li><a href="DrawLineObject.html">DrawLineObject</a></li><li><a href="DrawObjectFactory.html">DrawObjectFactory</a></li><li><a href="DrawPolygonObject.html">DrawPolygonObject</a></li><li><a href="DrawRectObject.html">DrawRectObject</a></li><li><a href="DrawShapeObject.html">DrawShapeObject</a></li><li><a href="DrawTextObject.html">DrawTextObject</a></li><li><a href="DrawTiledLayer.html">DrawTiledLayer</a></li><li><a href="GameStage.html">GameStage</a></li><li><a href="GameStageData.html">GameStageData</a></li><li><a href="IExtension.html">IExtension</a></li><li><a href="INetwork.html">INetwork</a></li><li><a href="IRender.html">IRender</a></li><li><a href="ISystem.html">ISystem</a></li><li><a href="ISystemAudio.html">ISystemAudio</a></li><li><a href="RenderLoop.html">RenderLoop</a></li><li><a href="RenderLoopDebug.html">RenderLoopDebug</a></li><li><a href="System.html">System</a></li><li><a href="SystemSettings.html">SystemSettings</a></li></ul><h3>Tutorials</h3><ul><li><a href="tutorial-application_scheme.html">Application Scheme</a></li><li><a href="tutorial-assets_manager.html">Assets Manager</a></li><li><a href="tutorial-common_issues.html">Common Issues</a></li><li><a href="tutorial-how_to_add_and_use_audio.html">How to add and use audio</a></li><li><a href="tutorial-how_to_do_animations.html">How to Create Animations</a></li><li><a href="tutorial-how_to_load_and_use_tilemaps.html">How to Load and Use Tilemaps</a></li><li><a href="tutorial-quick_start.html">Quick Start</a></li><li><a href="tutorial-spine_animations.html">How to Add Spine Animations</a></li><li><a href="tutorial-stages_lifecycle.html">Stages Lifecycle</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Wed Jun 18 2025 06:53:54 GMT+0000 (Coordinated Universal Time) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>