content: /*
   * FXGL - JavaFX Game Library. The MIT License (MIT).
   * Copyright (c) AlmasB (almaslvl@gmail.com).
   * See LICENSE for details.
   */
  package com.almasb.fxgl.physics.box2d.collision;
  
  import com.almasb.fxgl.core.math.Vec2;
  import com.almasb.fxgl.physics.box2d.collision.Manifold.ManifoldType;
  import com.almasb.fxgl.physics.box2d.collision.shapes.CircleShape;
  import com.almasb.fxgl.physics.box2d.collision.shapes.EdgeShape;
  import com.almasb.fxgl.physics.box2d.collision.shapes.PolygonShape;
  import com.almasb.fxgl.physics.box2d.common.JBoxSettings;
  import com.almasb.fxgl.physics.box2d.common.Rotation;
  import com.almasb.fxgl.physics.box2d.common.Transform;
  import com.almasb.fxgl.physics.box2d.pooling.IWorldPool;
  
  /**
   * Functions used for computing contact points, distance queries, and TOI queries.
   * Collision methods are non-static for pooling speed, retrieve a collision object from the {@link IWorldPool}.
   * Should not be constructed.
   *
   * @author Daniel Murphy
   */
  public final class Collision {
  
      public Collision() {
          incidentEdge[0] = new ClipVertex();
          incidentEdge[1] = new ClipVertex();
          clipPoints1[0] = new ClipVertex();
          clipPoints1[1] = new ClipVertex();
          clipPoints2[0] = new ClipVertex();
          clipPoints2[1] = new ClipVertex();
      }
  
      /**
       * Clipping for contact manifolds.
       * Sutherland-Hodgman clipping.
       */
      public static int clipSegmentToLine(ClipVertex[] vOut, ClipVertex[] vIn, Vec2 normal, float offset, int vertexIndexA) {
  
          // Start with no output points
          int numOut = 0;
          final ClipVertex vIn0 = vIn[0];
          final ClipVertex vIn1 = vIn[1];
          final Vec2 vIn0v = vIn0.v;
          final Vec2 vIn1v = vIn1.v;
  
          // Calculate the distance of end points to the line
          float distance0 = Vec2.dot(normal, vIn0v) - offset;
          float distance1 = Vec2.dot(normal, vIn1v) - offset;
  
          // If the points are behind the plane
          if (distance0 <= 0.0f) {
              vOut[numOut++].set(vIn0);
          }
          if (distance1 <= 0.0f) {
              vOut[numOut++].set(vIn1);
          }
  
          // If the points are on different sides of the plane
          if (distance0 * distance1 < 0.0f) {
              // Find intersection point of edge and plane
              float interp = distance0 / (distance0 - distance1);
  
              ClipVertex vOutNO = vOut[numOut];
              // vOut[numOut].v = vIn[0].v + interp * (vIn[1].v - vIn[0].v);
              vOutNO.v.x = vIn0v.x + interp * (vIn1v.x - vIn0v.x);
              vOutNO.v.y = vIn0v.y + interp * (vIn1v.y - vIn0v.y);
  
              // VertexA is hitting edgeB.
              vOutNO.id.indexA = (byte) vertexIndexA;
              vOutNO.id.indexB = vIn0.id.indexB;
              vOutNO.id.typeA = (byte) ContactID.Type.VERTEX.ordinal();
              vOutNO.id.typeB = (byte) ContactID.Type.FACE.ordinal();
              ++numOut;
          }
  
          return numOut;
      }
  
      // #### COLLISION STUFF (not from collision.h or collision.cpp) ####
  
      // djm pooling
      private static Vec2 d = new Vec2();
  
      /**
       * Compute the collision manifold between two circles.
       */
      @SuppressWarnings("PMD.UselessParentheses")
      public void collideCircles(Manifold manifold, CircleShape circle1, Transform xfA, CircleShape circle2, Transform xfB) {
          manifold.pointCount = 0;
          // before inline:
          // Transform.mulToOut(xfA, circle1.m_p, pA);
          // Transform.mulToOut(xfB, circle2.m_p, pB);
          // d.set(pB).subLocal(pA);
          // float distSqr = d.x * d.x + d.y * d.y;
  
          // after inline:
          Vec2 circle1p = circle1.center;
          Vec2 circle2p = circle2.center;
          float pAx = (xfA.q.c * circle1p.x - xfA.q.s * circle1p.y) + xfA.p.x;
          float pAy = (xfA.q.s * circle1p.x + xfA.q.c * circle1p.y) + xfA.p.y;
          float pBx = (xfB.q.c * circle2p.x - xfB.q.s * circle2p.y) + xfB.p.x;
          float pBy = (xfB.q.s * circle2p.x + xfB.q.c * circle2p.y) + xfB.p.y;
          float dx = pBx - pAx;
          float dy = pBy - pAy;
          float distSqr = dx * dx + dy * dy;
          // end inline
  
          final float radius = circle1.getRadius() + circle2.getRadius();
          if (distSqr > radius * radius) {
              return;
          }
  
          manifold.type = ManifoldType.CIRCLES;
          manifold.localPoint.set(circle1p);
          manifold.localNormal.setZero();
          manifold.pointCount = 1;
  
          manifold.points[0].localPoint.set(circle2p);
          manifold.points[0].id.zero();
      }
  
      /**
       * Compute the collision manifold between a polygon and a circle.
       *
       * @param manifold
       * @param polygon
       * @param xfA
       * @param circle
       * @param xfB
       */
      @SuppressWarnings("PMD.UselessParentheses")
      public void collidePolygonAndCircle(Manifold manifold,
                                          final PolygonShape polygon, final Transform xfA,
                                          final CircleShape circle, final Transform xfB) {
          manifold.pointCount = 0;
          // Vec2 v = circle.m_p;
  
          // Compute circle position in the frame of the polygon.
          // before inline:
          // Transform.mulToOutUnsafe(xfB, circle.m_p, c);
          // Transform.mulTransToOut(xfA, c, cLocal);
          // final float cLocalx = cLocal.x;
          // final float cLocaly = cLocal.y;
          // after inline:
          final Vec2 circlep = circle.center;
          final Rotation xfBq = xfB.q;
          final Rotation xfAq = xfA.q;
          final float cx = (xfBq.c * circlep.x - xfBq.s * circlep.y) + xfB.p.x;
          final float cy = (xfBq.s * circlep.x + xfBq.c * circlep.y) + xfB.p.y;
          final float px = cx - xfA.p.x;
          final float py = cy - xfA.p.y;
          final float cLocalx = (xfAq.c * px + xfAq.s * py);
          final float cLocaly = (-xfAq.s * px + xfAq.c * py);
          // end inline
  
          // Find the min separating edge.
          int normalIndex = 0;
          float separation = -Float.MAX_VALUE;
          final float radius = polygon.getRadius() + circle.getRadius();
          final int vertexCount = polygon.getVertexCount();
  
          final Vec2[] vertices = polygon.m_vertices;
          final Vec2[] normals = polygon.m_normals;
  
          for (int i = 0; i < vertexCount; i++) {
              // before inline
              // temp.set(cLocal).subLocal(vertices[i]);
              // float s = Vec2.dot(normals[i], temp);
              // after inline
              final Vec2 vertex = vertices[i];
              final float tempx = cLocalx - vertex.x;
              final float tempy = cLocaly - vertex.y;
              float s = normals[i].x * tempx + normals[i].y * tempy;
  
              if (s > radius) {
                  // early out
                  return;
              }
  
              if (s > separation) {
                  separation = s;
                  normalIndex = i;
              }
          }
  
          // Vertices that subtend the incident face.
          final int vertIndex1 = normalIndex;
          final int vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0;
          final Vec2 v1 = vertices[vertIndex1];
          final Vec2 v2 = vertices[vertIndex2];
  
          // If the center is inside the polygon ...
          if (separation < JBoxSettings.EPSILON) {
              manifold.pointCount = 1;
              manifold.type = ManifoldType.FACE_A;
  
              // before inline:
              // manifold.localNormal.set(normals[normalIndex]);
              // manifold.localPoint.set(v1).addLocal(v2).mulLocal(.5f);
              // manifold.points[0].localPoint.set(circle.m_p);
              // after inline:
              final Vec2 normal = normals[normalIndex];
              manifold.localNormal.x = normal.x;
              manifold.localNormal.y = normal.y;
              manifold.localPoint.x = (v1.x + v2.x) * .5f;
              manifold.localPoint.y = (v1.y + v2.y) * .5f;
              final ManifoldPoint mpoint = manifold.points[0];
              mpoint.localPoint.x = circlep.x;
              mpoint.localPoint.y = circlep.y;
              mpoint.id.zero();
              // end inline
  
              return;
          }
  
          // Compute barycentric coordinates
          // before inline:
          // temp.set(cLocal).subLocal(v1);
          // temp2.set(v2).subLocal(v1);
          // float u1 = Vec2.dot(temp, temp2);
          // temp.set(cLocal).subLocal(v2);
          // temp2.set(v1).subLocal(v2);
          // float u2 = Vec2.dot(temp, temp2);
          // after inline:
          final float tempX = cLocalx - v1.x;
          final float tempY = cLocaly - v1.y;
          final float temp2X = v2.x - v1.x;
          final float temp2Y = v2.y - v1.y;
          final float u1 = tempX * temp2X + tempY * temp2Y;
  
          final float temp3X = cLocalx - v2.x;
          final float temp3Y = cLocaly - v2.y;
          final float temp4X = v1.x - v2.x;
          final float temp4Y = v1.y - v2.y;
          final float u2 = temp3X * temp4X + temp3Y * temp4Y;
          // end inline
  
          if (u1 <= 0f) {
              // inlined
              final float dx = cLocalx - v1.x;
              final float dy = cLocaly - v1.y;
              if (dx * dx + dy * dy > radius * radius) {
                  return;
              }
  
              manifold.pointCount = 1;
              manifold.type = ManifoldType.FACE_A;
              // before inline:
              // manifold.localNormal.set(cLocal).subLocal(v1);
              // after inline:
              manifold.localNormal.x = cLocalx - v1.x;
              manifold.localNormal.y = cLocaly - v1.y;
              // end inline
              manifold.localNormal.getLengthAndNormalize();
              manifold.localPoint.set(v1);
              manifold.points[0].localPoint.set(circlep);
              manifold.points[0].id.zero();
          } else if (u2 <= 0.0f) {
              // inlined
              final float dx = cLocalx - v2.x;
              final float dy = cLocaly - v2.y;
              if (dx * dx + dy * dy > radius * radius) {
                  return;
              }
  
              manifold.pointCount = 1;
              manifold.type = ManifoldType.FACE_A;
              // before inline:
              // manifold.localNormal.set(cLocal).subLocal(v2);
              // after inline:
              manifold.localNormal.x = cLocalx - v2.x;
              manifold.localNormal.y = cLocaly - v2.y;
              // end inline
              manifold.localNormal.getLengthAndNormalize();
              manifold.localPoint.set(v2);
              manifold.points[0].localPoint.set(circlep);
              manifold.points[0].id.zero();
          } else {
              // Vec2 faceCenter = 0.5f * (v1 + v2);
              // (temp is faceCenter)
              // before inline:
              // temp.set(v1).addLocal(v2).mulLocal(.5f);
              //
              // temp2.set(cLocal).subLocal(temp);
              // separation = Vec2.dot(temp2, normals[vertIndex1]);
              // if (separation > radius) {
              // return;
              // }
              // after inline:
              final float fcx = (v1.x + v2.x) * .5f;
              final float fcy = (v1.y + v2.y) * .5f;
  
              final float tx = cLocalx - fcx;
              final float ty = cLocaly - fcy;
              final Vec2 normal = normals[vertIndex1];
              separation = tx * normal.x + ty * normal.y;
              if (separation > radius) {
                  return;
              }
              // end inline
  
              manifold.pointCount = 1;
              manifold.type = ManifoldType.FACE_A;
              manifold.localNormal.set(normals[vertIndex1]);
              manifold.localPoint.x = fcx; // (faceCenter)
              manifold.localPoint.y = fcy;
              manifold.points[0].localPoint.set(circlep);
              manifold.points[0].id.zero();
          }
      }
  
      // djm pooling, and from above
      private final Vec2 temp = new Vec2();
  
      @SuppressWarnings("PMD.UselessParentheses")
      public void findIncidentEdge(final ClipVertex[] c,
                                   final PolygonShape poly1, final Transform xf1, int edge1,
                                   final PolygonShape poly2, final Transform xf2) {
  
          int count1 = poly1.getVertexCount();
          final Vec2[] normals1 = poly1.m_normals;
  
          int count2 = poly2.getVertexCount();
          final Vec2[] vertices2 = poly2.m_vertices;
          final Vec2[] normals2 = poly2.m_normals;
  
          assert 0 <= edge1 && edge1 < count1;
  
          final ClipVertex c0 = c[0];
          final ClipVertex c1 = c[1];
          final Rotation xf1q = xf1.q;
          final Rotation xf2q = xf2.q;
  
          // Get the normal of the reference edge in poly2's frame.
          // Vec2 normal1 = MulT(xf2.R, Mul(xf1.R, normals1[edge1]));
          // before inline:
          // Rot.mulToOutUnsafe(xf1.q, normals1[edge1], normal1); // temporary
          // Rot.mulTrans(xf2.q, normal1, normal1);
          // after inline:
          final Vec2 v = normals1[edge1];
          final float tempx = xf1q.c * v.x - xf1q.s * v.y;
          final float tempy = xf1q.s * v.x + xf1q.c * v.y;
          final float normal1x = xf2q.c * tempx + xf2q.s * tempy;
          final float normal1y = -xf2q.s * tempx + xf2q.c * tempy;
  
          // end inline
  
          // Find the incident edge on poly2.
          int index = 0;
          float minDot = Float.MAX_VALUE;
          for (int i = 0; i < count2; ++i) {
              Vec2 b = normals2[i];
              float dot = normal1x * b.x + normal1y * b.y;
              if (dot < minDot) {
                  minDot = dot;
                  index = i;
              }
          }
  
          // Build the clip vertices for the incident edge.
          int i1 = index;
          int i2 = i1 + 1 < count2 ? i1 + 1 : 0;
  
          // c0.v = Mul(xf2, vertices2[i1]);
          Vec2 v1 = vertices2[i1];
          Vec2 out = c0.v;
          out.x = (xf2q.c * v1.x - xf2q.s * v1.y) + xf2.p.x;
          out.y = (xf2q.s * v1.x + xf2q.c * v1.y) + xf2.p.y;
          c0.id.indexA = (byte) edge1;
          c0.id.indexB = (byte) i1;
          c0.id.typeA = (byte) ContactID.Type.FACE.ordinal();
          c0.id.typeB = (byte) ContactID.Type.VERTEX.ordinal();
  
          // c1.v = Mul(xf2, vertices2[i2]);
          Vec2 v2 = vertices2[i2];
          Vec2 out1 = c1.v;
          out1.x = (xf2q.c * v2.x - xf2q.s * v2.y) + xf2.p.x;
          out1.y = (xf2q.s * v2.x + xf2q.c * v2.y) + xf2.p.y;
          c1.id.indexA = (byte) edge1;
          c1.id.indexB = (byte) i2;
          c1.id.typeA = (byte) ContactID.Type.FACE.ordinal();
          c1.id.typeB = (byte) ContactID.Type.VERTEX.ordinal();
      }
  
      private final EdgeResults results1 = new EdgeResults();
      private final EdgeResults results2 = new EdgeResults();
      private final ClipVertex[] incidentEdge = new ClipVertex[2];
      private final Vec2 localTangent = new Vec2();
      private final Vec2 localNormal = new Vec2();
      private final Vec2 planePoint = new Vec2();
      private final Vec2 tangent = new Vec2();
      private final Vec2 v11 = new Vec2();
      private final Vec2 v12 = new Vec2();
      private final ClipVertex[] clipPoints1 = new ClipVertex[2];
      private final ClipVertex[] clipPoints2 = new ClipVertex[2];
  
      /**
       * Compute the collision manifold between two polygons.
       */
      @SuppressWarnings("PMD.UselessParentheses")
      public void collidePolygons(Manifold manifold,
                                  final PolygonShape polyA, final Transform xfA,
                                  final PolygonShape polyB, final Transform xfB) {
          // Find edge normal of max separation on A - return if separating axis is found
          // Find edge normal of max separation on B - return if separation axis is found
          // Choose reference edge as min(minA, minB)
          // Find incident edge
          // Clip
  
          // The normal points from 1 to 2
  
          manifold.pointCount = 0;
          float totalRadius = polyA.getRadius() + polyB.getRadius();
  
          findMaxSeparation(results1, polyA, xfA, polyB, xfB);
          if (results1.separation > totalRadius) {
              return;
          }
  
          findMaxSeparation(results2, polyB, xfB, polyA, xfA);
          if (results2.separation > totalRadius) {
              return;
          }
  
          final PolygonShape poly1;  // reference polygon
          final PolygonShape poly2;  // incident polygon
          Transform xf1, xf2;
          int edge1;                 // reference edge
          boolean flip;
          final float k_tol = 0.1f * JBoxSettings.linearSlop;
  
          if (results2.separation > results1.separation + k_tol) {
              poly1 = polyB;
              poly2 = polyA;
              xf1 = xfB;
              xf2 = xfA;
              edge1 = results2.edgeIndex;
              manifold.type = ManifoldType.FACE_B;
              flip = true;
          } else {
              poly1 = polyA;
              poly2 = polyB;
              xf1 = xfA;
              xf2 = xfB;
              edge1 = results1.edgeIndex;
              manifold.type = ManifoldType.FACE_A;
              flip = false;
          }
          final Rotation xf1q = xf1.q;
  
          findIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2);
  
          int count1 = poly1.getVertexCount();
          final Vec2[] vertices1 = poly1.m_vertices;
  
          final int iv1 = edge1;
          final int iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0;
          v11.set(vertices1[iv1]);
          v12.set(vertices1[iv2]);
          localTangent.x = v12.x - v11.x;
          localTangent.y = v12.y - v11.y;
          localTangent.getLengthAndNormalize();
  
          // Vec2 localNormal = Vec2.cross(dv, 1.0f);
          localNormal.x = 1f * localTangent.y;
          localNormal.y = -1f * localTangent.x;
  
          // Vec2 planePoint = 0.5f * (v11+ v12);
          planePoint.x = (v11.x + v12.x) * .5f;
          planePoint.y = (v11.y + v12.y) * .5f;
  
          // Rot.mulToOutUnsafe(xf1.q, localTangent, tangent);
          tangent.x = xf1q.c * localTangent.x - xf1q.s * localTangent.y;
          tangent.y = xf1q.s * localTangent.x + xf1q.c * localTangent.y;
  
          // Vec2.crossToOutUnsafe(tangent, 1f, normal);
          final float normalx = 1f * tangent.y;
          final float normaly = -1f * tangent.x;
  
  
          Transform.mulToOut(xf1, v11, v11);
          Transform.mulToOut(xf1, v12, v12);
          // v11 = Mul(xf1, v11);
          // v12 = Mul(xf1, v12);
  
          // Face offset
          // float frontOffset = Vec2.dot(normal, v11);
          float frontOffset = normalx * v11.x + normaly * v11.y;
  
          // Side offsets, extended by polytope skin thickness.
          // float sideOffset1 = -Vec2.dot(tangent, v11) + totalRadius;
          // float sideOffset2 = Vec2.dot(tangent, v12) + totalRadius;
          float sideOffset1 = -(tangent.x * v11.x + tangent.y * v11.y) + totalRadius;
          float sideOffset2 = tangent.x * v12.x + tangent.y * v12.y + totalRadius;
  
          // Clip incident edge against extruded edge1 side edges.
          // ClipVertex clipPoints1[2];
          // ClipVertex clipPoints2[2];
          int np;
  
          // Clip to box side 1
          // np = ClipSegmentToLine(clipPoints1, incidentEdge, -sideNormal, sideOffset1);
          tangent.negateLocal();
          np = clipSegmentToLine(clipPoints1, incidentEdge, tangent, sideOffset1, iv1);
          tangent.negateLocal();
  
          if (np < 2) {
              return;
          }
  
          // Clip to negative box side 1
          np = clipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2, iv2);
  
          if (np < 2) {
              return;
          }
  
          // Now clipPoints2 contains the clipped points.
          manifold.localNormal.set(localNormal);
          manifold.localPoint.set(planePoint);
  
          int pointCount = 0;
          for (int i = 0; i < JBoxSettings.maxManifoldPoints; ++i) {
              // float separation = Vec2.dot(normal, clipPoints2[i].v) - frontOffset;
              float separation = normalx * clipPoints2[i].v.x + normaly * clipPoints2[i].v.y - frontOffset;
  
              if (separation <= totalRadius) {
                  ManifoldPoint cp = manifold.points[pointCount];
                  // cp.m_localPoint = MulT(xf2, clipPoints2[i].v);
                  Vec2 out = cp.localPoint;
                  final float px = clipPoints2[i].v.x - xf2.p.x;
                  final float py = clipPoints2[i].v.y - xf2.p.y;
                  out.x = (xf2.q.c * px + xf2.q.s * py);
                  out.y = (-xf2.q.s * px + xf2.q.c * py);
                  cp.id.set(clipPoints2[i].id);
                  if (flip) {
                      // Swap features
                      cp.id.flip();
                  }
                  ++pointCount;
              }
          }
  
          manifold.pointCount = pointCount;
      }
  
      private final Transform xf = new Transform();
      private final Vec2 n = new Vec2();
      private final Vec2 v1 = new Vec2();
  
      /**
       * Find the max separation between poly1 and poly2 using edge normals from poly1.
       */
      public void findMaxSeparation(EdgeResults results, PolygonShape poly1, Transform xf1, PolygonShape poly2, Transform xf2) {
          int count1 = poly1.getVertexCount();
          int count2 = poly2.getVertexCount();
          Vec2[] n1s = poly1.m_normals;
          Vec2[] v1s = poly1.m_vertices;
          Vec2[] v2s = poly2.m_vertices;
  
          Transform.mulTransToOutUnsafe(xf2, xf1, xf);
          final Rotation xfq = xf.q;
  
          int bestIndex = 0;
          float maxSeparation = -Float.MAX_VALUE;
          for (int i = 0; i < count1; i++) {
              // Get poly1 normal in frame2.
              Rotation.mulToOutUnsafe(xfq, n1s[i], n);
              Transform.mulToOutUnsafe(xf, v1s[i], v1);
  
              // Find deepest point for normal i.
              float si = Float.MAX_VALUE;
              for (int j = 0; j < count2; ++j) {
                  Vec2 v2sj = v2s[j];
                  float sij = n.x * (v2sj.x - v1.x) + n.y * (v2sj.y - v1.y);
                  if (sij < si) {
                      si = sij;
                  }
              }
  
              if (si > maxSeparation) {
                  maxSeparation = si;
                  bestIndex = i;
              }
          }
  
          results.edgeIndex = bestIndex;
          results.separation = maxSeparation;
      }
  
      private final Vec2 Q = new Vec2();
      private final Vec2 e = new Vec2();
      private final ContactID cf = new ContactID();
      private final Vec2 e1 = new Vec2();
      private final Vec2 P = new Vec2();
  
      // Compute contact points for edge versus circle.
      // This accounts for edge connectivity.
      public void collideEdgeAndCircle(Manifold manifold,
                                       final EdgeShape edgeA, final Transform xfA,
                                       final CircleShape circleB, final Transform xfB) {
          manifold.pointCount = 0;
  
  
          // Compute circle in frame of edge
          // Vec2 Q = MulT(xfA, Mul(xfB, circleB.m_p));
          Transform.mulToOutUnsafe(xfB, circleB.center, temp);
          Transform.mulTransToOutUnsafe(xfA, temp, Q);
  
          final Vec2 A = edgeA.m_vertex1;
          final Vec2 B = edgeA.m_vertex2;
          e.set(B).subLocal(A);
  
          // Barycentric coordinates
          float u = Vec2.dot(e, temp.set(B).subLocal(Q));
          float v = Vec2.dot(e, temp.set(Q).subLocal(A));
  
          float radius = edgeA.getRadius() + circleB.getRadius();
  
          // ContactFeature cf;
          cf.indexB = 0;
          cf.typeB = (byte) ContactID.Type.VERTEX.ordinal();
  
          // Region A
          if (v <= 0.0f) {
              final Vec2 P = A;
              d.set(Q).subLocal(P);
              float dd = Vec2.dot(d, d);
              if (dd > radius * radius) {
                  return;
              }
  
              // Is there an edge connected to A?
              if (edgeA.m_hasVertex0) {
                  final Vec2 A1 = edgeA.m_vertex0;
                  final Vec2 B1 = A;
                  e1.set(B1).subLocal(A1);
                  float u1 = Vec2.dot(e1, temp.set(B1).subLocal(Q));
  
                  // Is the circle in Region AB of the previous edge?
                  if (u1 > 0.0f) {
                      return;
                  }
              }
  
              cf.indexA = 0;
              cf.typeA = (byte) ContactID.Type.VERTEX.ordinal();
              manifold.pointCount = 1;
              manifold.type = Manifold.ManifoldType.CIRCLES;
              manifold.localNormal.setZero();
              manifold.localPoint.set(P);
              // manifold.points[0].id.key = 0;
              manifold.points[0].id.set(cf);
              manifold.points[0].localPoint.set(circleB.center);
              return;
          }
  
          // Region B
          if (u <= 0.0f) {
              Vec2 P = B;
              d.set(Q).subLocal(P);
              float dd = Vec2.dot(d, d);
              if (dd > radius * radius) {
                  return;
              }
  
              // Is there an edge connected to B?
              if (edgeA.m_hasVertex3) {
                  final Vec2 B2 = edgeA.m_vertex3;
                  final Vec2 A2 = B;
                  final Vec2 e2 = e1;
                  e2.set(B2).subLocal(A2);
                  float v2 = Vec2.dot(e2, temp.set(Q).subLocal(A2));
  
                  // Is the circle in Region AB of the next edge?
                  if (v2 > 0.0f) {
                      return;
                  }
              }
  
              cf.indexA = 1;
              cf.typeA = (byte) ContactID.Type.VERTEX.ordinal();
              manifold.pointCount = 1;
              manifold.type = Manifold.ManifoldType.CIRCLES;
              manifold.localNormal.setZero();
              manifold.localPoint.set(P);
              // manifold.points[0].id.key = 0;
              manifold.points[0].id.set(cf);
              manifold.points[0].localPoint.set(circleB.center);
              return;
          }
  
          // Region AB
          float den = Vec2.dot(e, e);
          assert den > 0.0f;
  
          // Vec2 P = (1.0f / den) * (u * A + v * B);
          P.set(A).mulLocal(u).addLocal(temp.set(B).mulLocal(v));
          P.mulLocal(1.0f / den);
          d.set(Q).subLocal(P);
          float dd = Vec2.dot(d, d);
          if (dd > radius * radius) {
              return;
          }
  
          n.x = -e.y;
          n.y = e.x;
          if (Vec2.dot(n, temp.set(Q).subLocal(A)) < 0.0f) {
              n.set(-n.x, -n.y);
          }
          n.getLengthAndNormalize();
  
          cf.indexA = 0;
          cf.typeA = (byte) ContactID.Type.FACE.ordinal();
          manifold.pointCount = 1;
          manifold.type = Manifold.ManifoldType.FACE_A;
          manifold.localNormal.set(n);
          manifold.localPoint.set(A);
          // manifold.points[0].id.key = 0;
          manifold.points[0].id.set(cf);
          manifold.points[0].localPoint.set(circleB.center);
      }
  
      private final EPCollider collider = new EPCollider();
  
      public void collideEdgeAndPolygon(Manifold manifold,
                                        final EdgeShape edgeA, final Transform xfA,
                                        final PolygonShape polygonB, final Transform xfB) {
          collider.collide(manifold, edgeA, xfA, polygonB, xfB);
      }
  
      /**
       * Java-specific class for returning edge results
       */
      private static class EdgeResults {
          float separation;
          int edgeIndex;
      }
  
      /**
       * Used for computing contact manifolds.
       */
      private static class ClipVertex {
          public final Vec2 v = new Vec2();
          public final ContactID id = new ContactID();
  
          public void set(final ClipVertex cv) {
              Vec2 v1 = cv.v;
              v.x = v1.x;
              v.y = v1.y;
              ContactID c = cv.id;
              id.indexA = c.indexA;
              id.indexB = c.indexB;
              id.typeA = c.typeA;
              id.typeB = c.typeB;
          }
      }
  
      /**
       * This structure is used to keep track of the best separating axis.
       */
      private static class EPAxis {
          enum Type {
              UNKNOWN, EDGE_A, EDGE_B
          }
  
          Type type;
          int index;
          float separation;
      }
  
      /**
       * This holds polygon B expressed in frame A.
       */
      private static class TempPolygon {
          final Vec2[] vertices = new Vec2[JBoxSettings.maxPolygonVertices];
          final Vec2[] normals = new Vec2[JBoxSettings.maxPolygonVertices];
          int count;
  
          TempPolygon() {
              for (int i = 0; i < vertices.length; i++) {
                  vertices[i] = new Vec2();
                  normals[i] = new Vec2();
              }
          }
      }
  
      /**
       * Reference face used for clipping
       */
      private static class ReferenceFace {
          int i1, i2;
          final Vec2 v1 = new Vec2();
          final Vec2 v2 = new Vec2();
          final Vec2 normal = new Vec2();
  
          final Vec2 sideNormal1 = new Vec2();
          float sideOffset1;
  
          final Vec2 sideNormal2 = new Vec2();
          float sideOffset2;
      }
  
      /**
       * This class collides and edge and a polygon, taking into account edge adjacency.
       */
      private static class EPCollider {
  
          final TempPolygon m_polygonB = new TempPolygon();
  
          final Transform m_xf = new Transform();
          final Vec2 m_centroidB = new Vec2();
          Vec2 m_v0 = new Vec2();
          Vec2 m_v1 = new Vec2();
          Vec2 m_v2 = new Vec2();
          Vec2 m_v3 = new Vec2();
          final Vec2 m_normal0 = new Vec2();
          final Vec2 m_normal1 = new Vec2();
          final Vec2 m_normal2 = new Vec2();
          final Vec2 m_normal = new Vec2();
  
          final Vec2 m_lowerLimit = new Vec2();
          final Vec2 m_upperLimit = new Vec2();
          float m_radius;
          boolean m_front;
  
          EPCollider() {
              for (int i = 0; i < 2; i++) {
                  ie[i] = new ClipVertex();
                  clipPoints1[i] = new ClipVertex();
                  clipPoints2[i] = new ClipVertex();
              }
          }
  
          private final Vec2 edge1 = new Vec2();
          private final Vec2 temp = new Vec2();
          private final Vec2 edge0 = new Vec2();
          private final Vec2 edge2 = new Vec2();
          private final ClipVertex[] ie = new ClipVertex[2];
          private final ClipVertex[] clipPoints1 = new ClipVertex[2];
          private final ClipVertex[] clipPoints2 = new ClipVertex[2];
          private final ReferenceFace rf = new ReferenceFace();
          private final EPAxis edgeAxis = new EPAxis();
          private final EPAxis polygonAxis = new EPAxis();
  
          public void collide(Manifold manifold, final EdgeShape edgeA, final Transform xfA,
                              final PolygonShape polygonB, final Transform xfB) {
  
              Transform.mulTransToOutUnsafe(xfA, xfB, m_xf);
              Transform.mulToOutUnsafe(m_xf, polygonB.m_centroid, m_centroidB);
  
              m_v0 = edgeA.m_vertex0;
              m_v1 = edgeA.m_vertex1;
              m_v2 = edgeA.m_vertex2;
              m_v3 = edgeA.m_vertex3;
  
              boolean hasVertex0 = edgeA.m_hasVertex0;
              boolean hasVertex3 = edgeA.m_hasVertex3;
  
              edge1.set(m_v2).subLocal(m_v1);
              edge1.getLengthAndNormalize();
              m_normal1.set(edge1.y, -edge1.x);
              float offset1 = Vec2.dot(m_normal1, temp.set(m_centroidB).subLocal(m_v1));
              float offset0 = 0.0f, offset2 = 0.0f;
              boolean convex1 = false, convex2 = false;
  
              // Is there a preceding edge?
              if (hasVertex0) {
                  edge0.set(m_v1).subLocal(m_v0);
                  edge0.getLengthAndNormalize();
                  m_normal0.set(edge0.y, -edge0.x);
                  convex1 = Vec2.cross(edge0, edge1) >= 0.0f;
                  offset0 = Vec2.dot(m_normal0, temp.set(m_centroidB).subLocal(m_v0));
              }
  
              // Is there a following edge?
              if (hasVertex3) {
                  edge2.set(m_v3).subLocal(m_v2);
                  edge2.getLengthAndNormalize();
                  m_normal2.set(edge2.y, -edge2.x);
                  convex2 = Vec2.cross(edge1, edge2) > 0.0f;
                  offset2 = Vec2.dot(m_normal2, temp.set(m_centroidB).subLocal(m_v2));
              }
  
              // Determine front or back collision. Determine collision normal limits.
              if (hasVertex0 && hasVertex3) {
                  if (convex1 && convex2) {
                      m_front = offset0 >= 0.0f || offset1 >= 0.0f || offset2 >= 0.0f;
                      if (m_front) {
                          m_normal.x = m_normal1.x;
                          m_normal.y = m_normal1.y;
                          m_lowerLimit.x = m_normal0.x;
                          m_lowerLimit.y = m_normal0.y;
                          m_upperLimit.x = m_normal2.x;
                          m_upperLimit.y = m_normal2.y;
                      } else {
                          m_normal.x = -m_normal1.x;
                          m_normal.y = -m_normal1.y;
                          m_lowerLimit.x = -m_normal1.x;
                          m_lowerLimit.y = -m_normal1.y;
                          m_upperLimit.x = -m_normal1.x;
                          m_upperLimit.y = -m_normal1.y;
                      }
                  } else if (convex1) {
                      m_front = offset0 >= 0.0f || offset1 >= 0.0f && offset2 >= 0.0f;
                      if (m_front) {
                          m_normal.x = m_normal1.x;
                          m_normal.y = m_normal1.y;
                          m_lowerLimit.x = m_normal0.x;
                          m_lowerLimit.y = m_normal0.y;
                          m_upperLimit.x = m_normal1.x;
                          m_upperLimit.y = m_normal1.y;
                      } else {
                          m_normal.x = -m_normal1.x;
                          m_normal.y = -m_normal1.y;
                          m_lowerLimit.x = -m_normal2.x;
                          m_lowerLimit.y = -m_normal2.y;
                          m_upperLimit.x = -m_normal1.x;
                          m_upperLimit.y = -m_normal1.y;
                      }
                  } else if (convex2) {
                      m_front = offset2 >= 0.0f || offset0 >= 0.0f && offset1 >= 0.0f;
                      if (m_front) {
                          m_normal.x = m_normal1.x;
                          m_normal.y = m_normal1.y;
                          m_lowerLimit.x = m_normal1.x;
                          m_lowerLimit.y = m_normal1.y;
                          m_upperLimit.x = m_normal2.x;
                          m_upperLimit.y = m_normal2.y;
                      } else {
                          m_normal.x = -m_normal1.x;
                          m_normal.y = -m_normal1.y;
                          m_lowerLimit.x = -m_normal1.x;
                          m_lowerLimit.y = -m_normal1.y;
                          m_upperLimit.x = -m_normal0.x;
                          m_upperLimit.y = -m_normal0.y;
                      }
                  } else {
                      m_front = offset0 >= 0.0f && offset1 >= 0.0f && offset2 >= 0.0f;
                      if (m_front) {
                          m_normal.x = m_normal1.x;
                          m_normal.y = m_normal1.y;
                          m_lowerLimit.x = m_normal1.x;
                          m_lowerLimit.y = m_normal1.y;
                          m_upperLimit.x = m_normal1.x;
                          m_upperLimit.y = m_normal1.y;
                      } else {
                          m_normal.x = -m_normal1.x;
                          m_normal.y = -m_normal1.y;
                          m_lowerLimit.x = -m_normal2.x;
                          m_lowerLimit.y = -m_normal2.y;
                          m_upperLimit.x = -m_normal0.x;
                          m_upperLimit.y = -m_normal0.y;
                      }
                  }
              } else if (hasVertex0) {
                  if (convex1) {
                      m_front = offset0 >= 0.0f || offset1 >= 0.0f;
                      if (m_front) {
                          m_normal.x = m_normal1.x;
                          m_normal.y = m_normal1.y;
                          m_lowerLimit.x = m_normal0.x;
                          m_lowerLimit.y = m_normal0.y;
                          m_upperLimit.x = -m_normal1.x;
                          m_upperLimit.y = -m_normal1.y;
                      } else {
                          m_normal.x = -m_normal1.x;
                          m_normal.y = -m_normal1.y;
                          m_lowerLimit.x = m_normal1.x;
                          m_lowerLimit.y = m_normal1.y;
                          m_upperLimit.x = -m_normal1.x;
                          m_upperLimit.y = -m_normal1.y;
                      }
                  } else {
                      m_front = offset0 >= 0.0f && offset1 >= 0.0f;
                      if (m_front) {
                          m_normal.x = m_normal1.x;
                          m_normal.y = m_normal1.y;
                          m_lowerLimit.x = m_normal1.x;
                          m_lowerLimit.y = m_normal1.y;
                          m_upperLimit.x = -m_normal1.x;
                          m_upperLimit.y = -m_normal1.y;
                      } else {
                          m_normal.x = -m_normal1.x;
                          m_normal.y = -m_normal1.y;
                          m_lowerLimit.x = m_normal1.x;
                          m_lowerLimit.y = m_normal1.y;
                          m_upperLimit.x = -m_normal0.x;
                          m_upperLimit.y = -m_normal0.y;
                      }
                  }
              } else if (hasVertex3) {
                  if (convex2) {
                      m_front = offset1 >= 0.0f || offset2 >= 0.0f;
                      if (m_front) {
                          m_normal.x = m_normal1.x;
                          m_normal.y = m_normal1.y;
                          m_lowerLimit.x = -m_normal1.x;
                          m_lowerLimit.y = -m_normal1.y;
                          m_upperLimit.x = m_normal2.x;
                          m_upperLimit.y = m_normal2.y;
                      } else {
                          m_normal.x = -m_normal1.x;
                          m_normal.y = -m_normal1.y;
                          m_lowerLimit.x = -m_normal1.x;
                          m_lowerLimit.y = -m_normal1.y;
                          m_upperLimit.x = m_normal1.x;
                          m_upperLimit.y = m_normal1.y;
                      }
                  } else {
                      m_front = offset1 >= 0.0f && offset2 >= 0.0f;
                      if (m_front) {
                          m_normal.x = m_normal1.x;
                          m_normal.y = m_normal1.y;
                          m_lowerLimit.x = -m_normal1.x;
                          m_lowerLimit.y = -m_normal1.y;
                          m_upperLimit.x = m_normal1.x;
                          m_upperLimit.y = m_normal1.y;
                      } else {
                          m_normal.x = -m_normal1.x;
                          m_normal.y = -m_normal1.y;
                          m_lowerLimit.x = -m_normal2.x;
                          m_lowerLimit.y = -m_normal2.y;
                          m_upperLimit.x = m_normal1.x;
                          m_upperLimit.y = m_normal1.y;
                      }
                  }
              } else {
                  m_front = offset1 >= 0.0f;
                  if (m_front) {
                      m_normal.x = m_normal1.x;
                      m_normal.y = m_normal1.y;
                      m_lowerLimit.x = -m_normal1.x;
                      m_lowerLimit.y = -m_normal1.y;
                      m_upperLimit.x = -m_normal1.x;
                      m_upperLimit.y = -m_normal1.y;
                  } else {
                      m_normal.x = -m_normal1.x;
                      m_normal.y = -m_normal1.y;
                      m_lowerLimit.x = m_normal1.x;
                      m_lowerLimit.y = m_normal1.y;
                      m_upperLimit.x = m_normal1.x;
                      m_upperLimit.y = m_normal1.y;
                  }
              }
  
              // Get polygonB in frameA
              m_polygonB.count = polygonB.getVertexCount();
              for (int i = 0; i < polygonB.getVertexCount(); ++i) {
                  Transform.mulToOutUnsafe(m_xf, polygonB.m_vertices[i], m_polygonB.vertices[i]);
                  Rotation.mulToOutUnsafe(m_xf.q, polygonB.m_normals[i], m_polygonB.normals[i]);
              }
  
              m_radius = 2.0f * JBoxSettings.polygonRadius;
  
              manifold.pointCount = 0;
  
              computeEdgeSeparation(edgeAxis);
  
              // If no valid normal can be found than this edge should not collide.
              if (edgeAxis.type == EPAxis.Type.UNKNOWN) {
                  return;
              }
  
              if (edgeAxis.separation > m_radius) {
                  return;
              }
  
              computePolygonSeparation(polygonAxis);
              if (polygonAxis.type != EPAxis.Type.UNKNOWN && polygonAxis.separation > m_radius) {
                  return;
              }
  
              // Use hysteresis for jitter reduction.
              final float k_relativeTol = 0.98f;
              final float k_absoluteTol = 0.001f;
  
              EPAxis primaryAxis;
              if (polygonAxis.type == EPAxis.Type.UNKNOWN) {
                  primaryAxis = edgeAxis;
              } else if (polygonAxis.separation > k_relativeTol * edgeAxis.separation + k_absoluteTol) {
                  primaryAxis = polygonAxis;
              } else {
                  primaryAxis = edgeAxis;
              }
  
              final ClipVertex ie0 = ie[0];
              final ClipVertex ie1 = ie[1];
  
              if (primaryAxis.type == EPAxis.Type.EDGE_A) {
                  manifold.type = Manifold.ManifoldType.FACE_A;
  
                  // Search for the polygon normal that is most anti-parallel to the edge normal.
                  int bestIndex = 0;
                  float bestValue = Vec2.dot(m_normal, m_polygonB.normals[0]);
                  for (int i = 1; i < m_polygonB.count; ++i) {
                      float value = Vec2.dot(m_normal, m_polygonB.normals[i]);
                      if (value < bestValue) {
                          bestValue = value;
                          bestIndex = i;
                      }
                  }
  
                  int i1 = bestIndex;
                  int i2 = i1 + 1 < m_polygonB.count ? i1 + 1 : 0;
  
                  ie0.v.set(m_polygonB.vertices[i1]);
                  ie0.id.indexA = 0;
                  ie0.id.indexB = (byte) i1;
                  ie0.id.typeA = (byte) ContactID.Type.FACE.ordinal();
                  ie0.id.typeB = (byte) ContactID.Type.VERTEX.ordinal();
  
                  ie1.v.set(m_polygonB.vertices[i2]);
                  ie1.id.indexA = 0;
                  ie1.id.indexB = (byte) i2;
                  ie1.id.typeA = (byte) ContactID.Type.FACE.ordinal();
                  ie1.id.typeB = (byte) ContactID.Type.VERTEX.ordinal();
  
                  if (m_front) {
                      rf.i1 = 0;
                      rf.i2 = 1;
                      rf.v1.set(m_v1);
                      rf.v2.set(m_v2);
                      rf.normal.set(m_normal1);
                  } else {
                      rf.i1 = 1;
                      rf.i2 = 0;
                      rf.v1.set(m_v2);
                      rf.v2.set(m_v1);
                      rf.normal.set(m_normal1).negateLocal();
                  }
              } else {
                  manifold.type = Manifold.ManifoldType.FACE_B;
  
                  ie0.v.set(m_v1);
                  ie0.id.indexA = 0;
                  ie0.id.indexB = (byte) primaryAxis.index;
                  ie0.id.typeA = (byte) ContactID.Type.VERTEX.ordinal();
                  ie0.id.typeB = (byte) ContactID.Type.FACE.ordinal();
  
                  ie1.v.set(m_v2);
                  ie1.id.indexA = 0;
                  ie1.id.indexB = (byte) primaryAxis.index;
                  ie1.id.typeA = (byte) ContactID.Type.VERTEX.ordinal();
                  ie1.id.typeB = (byte) ContactID.Type.FACE.ordinal();
  
                  rf.i1 = primaryAxis.index;
                  rf.i2 = rf.i1 + 1 < m_polygonB.count ? rf.i1 + 1 : 0;
                  rf.v1.set(m_polygonB.vertices[rf.i1]);
                  rf.v2.set(m_polygonB.vertices[rf.i2]);
                  rf.normal.set(m_polygonB.normals[rf.i1]);
              }
  
              rf.sideNormal1.set(rf.normal.y, -rf.normal.x);
              rf.sideNormal2.set(rf.sideNormal1).negateLocal();
              rf.sideOffset1 = Vec2.dot(rf.sideNormal1, rf.v1);
              rf.sideOffset2 = Vec2.dot(rf.sideNormal2, rf.v2);
  
              // Clip incident edge against extruded edge1 side edges.
              int np;
  
              // Clip to box side 1
              np = clipSegmentToLine(clipPoints1, ie, rf.sideNormal1, rf.sideOffset1, rf.i1);
  
              if (np < JBoxSettings.maxManifoldPoints) {
                  return;
              }
  
              // Clip to negative box side 1
              np = clipSegmentToLine(clipPoints2, clipPoints1, rf.sideNormal2, rf.sideOffset2, rf.i2);
  
              if (np < JBoxSettings.maxManifoldPoints) {
                  return;
              }
  
              // Now clipPoints2 contains the clipped points.
              if (primaryAxis.type == EPAxis.Type.EDGE_A) {
                  manifold.localNormal.set(rf.normal);
                  manifold.localPoint.set(rf.v1);
              } else {
                  manifold.localNormal.set(polygonB.m_normals[rf.i1]);
                  manifold.localPoint.set(polygonB.m_vertices[rf.i1]);
              }
  
              int pointCount = 0;
              for (int i = 0; i < JBoxSettings.maxManifoldPoints; ++i) {
                  float separation;
  
                  separation = Vec2.dot(rf.normal, temp.set(clipPoints2[i].v).subLocal(rf.v1));
  
                  if (separation <= m_radius) {
                      ManifoldPoint cp = manifold.points[pointCount];
  
                      if (primaryAxis.type == EPAxis.Type.EDGE_A) {
                          // cp.localPoint = MulT(m_xf, clipPoints2[i].v);
                          Transform.mulTransToOutUnsafe(m_xf, clipPoints2[i].v, cp.localPoint);
                          cp.id.set(clipPoints2[i].id);
                      } else {
                          cp.localPoint.set(clipPoints2[i].v);
                          cp.id.typeA = clipPoints2[i].id.typeB;
                          cp.id.typeB = clipPoints2[i].id.typeA;
                          cp.id.indexA = clipPoints2[i].id.indexB;
                          cp.id.indexB = clipPoints2[i].id.indexA;
                      }
  
                      ++pointCount;
                  }
              }
  
              manifold.pointCount = pointCount;
          }
  
          public void computeEdgeSeparation(EPAxis axis) {
              axis.type = EPAxis.Type.EDGE_A;
              axis.index = m_front ? 0 : 1;
              axis.separation = Float.MAX_VALUE;
              float nx = m_normal.x;
              float ny = m_normal.y;
  
              for (int i = 0; i < m_polygonB.count; ++i) {
                  Vec2 v = m_polygonB.vertices[i];
                  float tempx = v.x - m_v1.x;
                  float tempy = v.y - m_v1.y;
                  float s = nx * tempx + ny * tempy;
                  if (s < axis.separation) {
                      axis.separation = s;
                  }
              }
          }
  
          private final Vec2 perp = new Vec2();
          private final Vec2 n = new Vec2();
  
          public void computePolygonSeparation(EPAxis axis) {
              axis.type = EPAxis.Type.UNKNOWN;
              axis.index = -1;
              axis.separation = -Float.MAX_VALUE;
  
              perp.x = -m_normal.y;
              perp.y = m_normal.x;
  
              for (int i = 0; i < m_polygonB.count; ++i) {
                  Vec2 normalB = m_polygonB.normals[i];
                  Vec2 vB = m_polygonB.vertices[i];
                  n.x = -normalB.x;
                  n.y = -normalB.y;
  
                  // float s1 = Vec2.dot(n, temp.set(vB).subLocal(m_v1));
                  // float s2 = Vec2.dot(n, temp.set(vB).subLocal(m_v2));
                  float tempx = vB.x - m_v1.x;
                  float tempy = vB.y - m_v1.y;
                  float s1 = n.x * tempx + n.y * tempy;
                  tempx = vB.x - m_v2.x;
                  tempy = vB.y - m_v2.y;
                  float s2 = n.x * tempx + n.y * tempy;
                  float s = Math.min(s1, s2);
  
                  if (s > m_radius) {
                      // No collision
                      axis.type = EPAxis.Type.EDGE_B;
                      axis.index = i;
                      axis.separation = s;
                      return;
                  }
  
                  // Adjacency
                  if (n.x * perp.x + n.y * perp.y >= 0.0f) {
                      if (Vec2.dot(temp.set(n).subLocal(m_upperLimit), m_normal) < -JBoxSettings.angularSlop) {
                          continue;
                      }
                  } else {
                      if (Vec2.dot(temp.set(n).subLocal(m_lowerLimit), m_normal) < -JBoxSettings.angularSlop) {
                          continue;
                      }
                  }
  
                  if (s > axis.separation) {
                      axis.type = EPAxis.Type.EDGE_B;
                      axis.index = i;
                      axis.separation = s;
                  }
              }
          }
      }
  }
  