text: /**
   * @module RayShoot
   */
  "use strict";
  import Flatten from '../flatten';
  import * as Utils from '../utils/utils';
  import * as Constants from '../utils/constants';
  /**
   * Implements ray shooting algorithm. Returns relation between point and polygon: inside, outside or boundary
   * @param {Polygon} polygon - polygon to test
   * @param {Point} point - point to test
   * @returns {INSIDE|OUTSIDE|BOUNDARY}
   */
  export function ray_shoot(polygon, point) {
      let contains = undefined;
  
      // 1. Quick reject
      // if (polygon.box.not_intersect(point.box)) {
      //     return Flatten.OUTSIDE;
      // }
  
      let ray = new Flatten.Ray(point);
      let line = new Flatten.Line(ray.pt, ray.norm);
  
      // 2. Locate relevant edges of the polygon
      const searchBox = new Flatten.Box(
          ray.box.xmin-Flatten.DP_TOL, ray.box.ymin-Flatten.DP_TOL,
          ray.box.xmax, ray.box.ymax+Flatten.DP_TOL
      );
  
      if (polygon.box.not_intersect(searchBox)) {
          return Flatten.OUTSIDE;
      }
  
      let resp_edges = polygon.edges.search(searchBox);
  
      if (resp_edges.length == 0) {
          return Flatten.OUTSIDE;
      }
  
      // 2.5 Check if boundary
      for (let edge of resp_edges) {
          if (edge.shape.contains(point)) {
              return Flatten.BOUNDARY;
          }
      }
  
      // 3. Calculate intersections
      let intersections = [];
      for (let edge of resp_edges) {
          for (let ip of ray.intersect(edge.shape)) {
  
              // If intersection is equal to query point then point lays on boundary
              if (ip.equalTo(point)) {
                  return Flatten.BOUNDARY;
              }
  
              intersections.push({
                  pt: ip,
                  edge: edge
              });
          }
      }
  
      // 4. Sort intersection in x-ascending order
      intersections.sort((i1, i2) => {
          if (Utils.LT(i1.pt.x, i2.pt.x)) {
              return -1;
          }
          if (Utils.GT(i1.pt.x, i2.pt.x)) {
              return 1;
          }
          return 0;
      });
  
      // 5. Count real intersections, exclude touching
      let counter = 0;
  
      for (let i = 0; i < intersections.length; i++) {
          let intersection = intersections[i];
          if (intersection.pt.equalTo(intersection.edge.shape.start)) {
              /* skip same point between same edges if already counted */
              if (i > 0 && intersection.pt.equalTo(intersections[i - 1].pt) &&
                  intersection.edge.prev === intersections[i - 1].edge) {
                  continue;
              }
              let prev_edge = intersection.edge.prev;
              while (Utils.EQ_0(prev_edge.length)) {
                  prev_edge = prev_edge.prev;
              }
              let prev_tangent = prev_edge.shape.tangentInEnd();
              let prev_point = intersection.pt.translate(prev_tangent);
  
              let cur_tangent = intersection.edge.shape.tangentInStart();
              let cur_point = intersection.pt.translate(cur_tangent);
  
              let prev_on_the_left = prev_point.leftTo(line);
              let cur_on_the_left = cur_point.leftTo(line);
  
              if ((prev_on_the_left && !cur_on_the_left) || (!prev_on_the_left && cur_on_the_left)) {
                  counter++;
              }
          } else if (intersection.pt.equalTo(intersection.edge.shape.end)) {
              /* skip same point between same edges if already counted */
              if (i > 0 && intersection.pt.equalTo(intersections[i - 1].pt) &&
                  intersection.edge.next === intersections[i - 1].edge) {
                  continue;
              }
              let next_edge = intersection.edge.next;
              while (Utils.EQ_0(next_edge.length)) {
                  next_edge = next_edge.next;
              }
              let next_tangent = next_edge.shape.tangentInStart();
              let next_point = intersection.pt.translate(next_tangent);
  
              let cur_tangent = intersection.edge.shape.tangentInEnd();
              let cur_point = intersection.pt.translate(cur_tangent);
  
              let next_on_the_left = next_point.leftTo(line);
              let cur_on_the_left = cur_point.leftTo(line);
  
              if ((next_on_the_left && !cur_on_the_left) || (!next_on_the_left && cur_on_the_left)) {
                  counter++;
              }
          } else {        /* intersection point is not a coincident with a vertex */
              if (intersection.edge.shape instanceof Flatten.Segment) {
                  counter++;
              } else {
                  /* Check if ray does not touch the curve in the extremal (top or bottom) point */
                  let box = intersection.edge.shape.box;
                  if (!(Utils.EQ(intersection.pt.y, box.ymin) ||
                      Utils.EQ(intersection.pt.y, box.ymax))) {
                      counter++;
                  }
              }
          }
      }
  
      // 6. Odd or even?
      contains = counter % 2 == 1 ? Constants.INSIDE : Constants.OUTSIDE;
  
      return contains;
  };
  