LCOV - code coverage report
Current view: top level - simulation/helpers - ObstructionSnap.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 3 83 3.6 %
Date: 2023-04-02 12:52:40 Functions: 0 5 0.0 %

          Line data    Source code
       1             : /**
       2             :  * The class allows the player to position structures so that they are aligned
       3             :  * with nearby structures.
       4             :  */
       5             : class ObstructionSnap
       6             : {
       7             :         getValidEdges(allEdges, position, maxSide)
       8             :         {
       9           0 :                 let edges = [];
      10           0 :                 let dir1 = new Vector2D();
      11           0 :                 let dir2 = new Vector2D();
      12           0 :                 for (let edge of allEdges)
      13             :                 {
      14           0 :                         let signedDistance = Vector2D.dot(edge.normal, position) -
      15             :                                              Vector2D.dot(edge.normal, edge.begin);
      16             :                         // Negative signed distance means that the template position
      17             :                         // lays behind the edge.
      18           0 :                         if (signedDistance < -this.MinimalDistanceToSnap - maxSide ||
      19             :                             signedDistance > this.MinimalDistanceToSnap + maxSide)
      20           0 :                                 continue;
      21           0 :                         dir1.setFrom(edge.begin).sub(edge.end).normalize();
      22           0 :                         dir2.setFrom(dir1).mult(-1);
      23           0 :                         let offsetDistance = Math.max(
      24             :                                 Vector2D.dot(dir1, position) - Vector2D.dot(dir1, edge.begin),
      25             :                                 Vector2D.dot(dir2, position) - Vector2D.dot(dir2, edge.end));
      26           0 :                         if (offsetDistance > this.MinimalDistanceToSnap + maxSide)
      27           0 :                                 continue;
      28             :                         // If a projection of the template position on the edge is
      29             :                         // lying inside the edge then obviously we don't need to
      30             :                         // account the offset distance.
      31           0 :                         if (offsetDistance < 0)
      32           0 :                                 offsetDistance = 0;
      33           0 :                         edge.signedDistance = signedDistance;
      34           0 :                         edge.offsetDistance = offsetDistance;
      35           0 :                         edges.push(edge);
      36             :                 }
      37           0 :                 return edges;
      38             :         }
      39             : 
      40             :         // We need a small padding to avoid unnecessary collisions
      41             :         // because of loss of accuracy.
      42             :         getPadding(edge)
      43             :         {
      44           0 :                 const snapPadding = 0.05;
      45             :                 // We don't need to padding for edges with normals directed inside
      46             :                 // its entity, as we try to snap from an internal side of the edge.
      47           0 :                 return edge.order == "ccw" ? 0 : snapPadding;
      48             :         }
      49             : 
      50             :         // Pick a base edge, it will be the first axis and fix the angle.
      51             :         // We can't just pick an edge by signed distance, because we might have
      52             :         // a case when one segment is closer by signed distance than another
      53             :         // one but much farther by actual (euclid) distance.
      54             :         compareEdges(a, b)
      55             :         {
      56           0 :                 const behindA = a.signedDistance < -this.EPS;
      57           0 :                 const behindB = b.signedDistance < -this.EPS;
      58           0 :                 const scoreA = Math.abs(a.signedDistance) + a.offsetDistance;
      59           0 :                 const scoreB = Math.abs(b.signedDistance) + b.offsetDistance;
      60           0 :                 if (Math.abs(scoreA - scoreB) < this.EPS)
      61             :                 {
      62           0 :                         if (behindA != behindB)
      63           0 :                                 return behindA - behindB;
      64           0 :                         if (!behindA)
      65           0 :                                 return a.offsetDistance - b.offsetDistance;
      66           0 :                         return -a.signedDistance - -b.signedDistance;
      67             :                 }
      68           0 :                 return scoreA - scoreB;
      69             :         }
      70             : 
      71             :         getNearestSizeAlongNormal(width, depth, angle, normal)
      72             :         {
      73             :                 // Front face direction.
      74           0 :                 let direction = new Vector2D(0.0, 1.0);
      75           0 :                 direction.rotate(angle);
      76           0 :                 let dot = direction.dot(normal);
      77           0 :                 const threshold = Math.cos(Math.PI / 4.0);
      78           0 :                 if (Math.abs(dot) > threshold)
      79           0 :                         return [depth, width];
      80           0 :                 return [width, depth];
      81             :         }
      82             : 
      83             :         getPosition(data, template)
      84             :         {
      85           0 :                 if (!data.snapToEdges || !template.Obstruction || !template.Obstruction.Static)
      86           0 :                         return undefined;
      87             : 
      88           0 :                 const width = template.Obstruction.Static["@width"] / 2;
      89           0 :                 const depth = template.Obstruction.Static["@depth"] / 2;
      90           0 :                 const maxSide = Math.max(width, depth);
      91           0 :                 let templatePos = Vector2D.from3D(data);
      92           0 :                 let templateAngle = data.angle || 0;
      93             : 
      94           0 :                 let edges = this.getValidEdges(data.snapToEdges, templatePos, maxSide);
      95           0 :                 if (!edges.length)
      96           0 :                         return undefined;
      97             : 
      98           0 :                 let baseEdge = edges[0];
      99           0 :                 for (let edge of edges)
     100           0 :                         if (this.compareEdges(edge, baseEdge) < 0)
     101           0 :                                 baseEdge = edge;
     102             :                 // Now we have the normal, we need to determine an angle,
     103             :                 // which side will be snapped first.
     104           0 :                 for (let dir = 0; dir < 4; ++dir)
     105             :                 {
     106           0 :                         const angleCandidate = baseEdge.angle + dir * Math.PI / 2;
     107             :                         // We need to find a minimal angle difference.
     108           0 :                         let difference = Math.abs(angleCandidate - templateAngle);
     109           0 :                         difference = Math.min(difference, Math.PI * 2 - difference);
     110           0 :                         if (difference < Math.PI / 4 + this.EPS)
     111             :                         {
     112           0 :                                 templateAngle = angleCandidate;
     113           0 :                                 break;
     114             :                         }
     115             :                 }
     116             :                 let [sizeToBaseEdge, sizeToPairedEdge] =
     117           0 :                         this.getNearestSizeAlongNormal(width, depth, templateAngle, baseEdge.normal);
     118             : 
     119           0 :                 let distance = Vector2D.dot(baseEdge.normal, templatePos) - Vector2D.dot(baseEdge.normal, baseEdge.begin);
     120           0 :                 templatePos.sub(Vector2D.mult(baseEdge.normal, distance - sizeToBaseEdge - this.getPadding(baseEdge)));
     121           0 :                 edges = this.getValidEdges(data.snapToEdges, templatePos, maxSide);
     122           0 :                 if (edges.length > 1)
     123             :                 {
     124           0 :                         let pairedEdges = [];
     125           0 :                         for (let edge of edges)
     126             :                         {
     127             :                                 // We have to place a rectangle, so the angle between
     128             :                                 // edges should be 90 degrees.
     129           0 :                                 if (Math.abs(Vector2D.dot(baseEdge.normal, edge.normal)) > this.EPS)
     130           0 :                                         continue;
     131           0 :                                 let newEdge = {
     132             :                                         "begin": edge.end,
     133             :                                         "end": edge.begin,
     134             :                                         "normal": Vector2D.mult(edge.normal, -1),
     135             :                                         "signedDistance": -edge.signedDistance,
     136             :                                         "offsetDistance": edge.offsetDistance,
     137             :                                         "order": "ccw",
     138             :                                 };
     139           0 :                                 pairedEdges.push(edge);
     140           0 :                                 pairedEdges.push(newEdge);
     141             :                         }
     142           0 :                         pairedEdges.sort(this.compareEdges.bind(this));
     143           0 :                         if (pairedEdges.length)
     144             :                         {
     145           0 :                                 let secondEdge = pairedEdges[0];
     146           0 :                                 for (let edge of pairedEdges)
     147           0 :                                         if (this.compareEdges(edge, secondEdge) < 0)
     148           0 :                                                 secondEdge = edge;
     149           0 :                                 let distance = Vector2D.dot(secondEdge.normal, templatePos) - Vector2D.dot(secondEdge.normal, secondEdge.begin);
     150           0 :                                 templatePos.sub(Vector2D.mult(secondEdge.normal, distance - sizeToPairedEdge - this.getPadding(secondEdge)));
     151             :                         }
     152             :                 }
     153           0 :                 return {
     154             :                         "x": templatePos.x,
     155             :                         "z": templatePos.y,
     156             :                         "angle": templateAngle
     157             :                 };
     158             :         }
     159             : }
     160             : 
     161           1 : ObstructionSnap.prototype.MinimalDistanceToSnap = 5;
     162             : 
     163           1 : ObstructionSnap.prototype.EPS = 1e-3;
     164             : 
     165           1 : Engine.RegisterGlobal("ObstructionSnap", ObstructionSnap);

Generated by: LCOV version 1.14