content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  package org.apache.asterix.runtime.evaluators.common;
  
  import org.apache.asterix.dataflow.data.nontagged.Coordinate;
  import org.apache.asterix.dataflow.data.nontagged.serde.ADoubleSerializerDeserializer;
  import org.apache.asterix.dataflow.data.nontagged.serde.APolygonSerializerDeserializer;
  import org.apache.asterix.om.base.ARectangle;
  import org.apache.hyracks.api.exceptions.HyracksDataException;
  
  public class SpatialUtils {
      private double xAxis;
      private double yAxis;
      private double minProjection;
      private double maxProjection;
      private static final double doubleEpsilon = computeDoubleEpsilon();
      private static final double pi = 3.14159265;
  
      public SpatialUtils() {
      }
  
      private static double computeDoubleEpsilon() {
          double doubleEpsilon = 1.0;
  
          do {
              doubleEpsilon /= 2.0;
          } while (1.0 + (doubleEpsilon / 2.0) != 1.0);
          return doubleEpsilon;
      }
  
      public static double doubleEpsilon() {
          return doubleEpsilon;
      }
  
      public double getMinProjection() {
          return minProjection;
      }
  
      public void setMinProjection(double minProjection) {
          this.minProjection = minProjection;
      }
  
      public double getMaxProjection() {
          return maxProjection;
      }
  
      public void setMaxProjection(double maxProjection) {
          this.maxProjection = maxProjection;
      }
  
      public double getXAxis() {
          return xAxis;
      }
  
      public void setXAxis(double xAxis) {
          this.xAxis = xAxis;
      }
  
      public double getYAxis() {
          return yAxis;
      }
  
      public void setYAxis(double yAxis) {
          this.yAxis = yAxis;
      }
  
      public final static double pi() {
          return pi;
      }
  
      public final static double dotProduct(double x1, double y1, double x2, double y2) {
          return x1 * x2 + y1 * y2;
      }
  
      public final static double crossProduct(double x1, double y1, double x2, double y2) {
          return x1 * y2 - y1 * x2;
      }
  
      // Warning: The caller is responsible of taking the absolute value
      public final static double polygonArea(byte[] bytes, int offset, int numOfPoints) throws HyracksDataException {
          double area = 0.0;
          for (int i = 0; i < numOfPoints; i++) {
  
              double x1 = ADoubleSerializerDeserializer.getDouble(bytes,
                      offset + 1 + APolygonSerializerDeserializer.getCoordinateOffset(i, Coordinate.X));
              double y1 = ADoubleSerializerDeserializer.getDouble(bytes,
                      offset + 1 + APolygonSerializerDeserializer.getCoordinateOffset(i, Coordinate.Y));
              double x2;
              double y2;
              if (i + 1 == numOfPoints) {
                  x2 = ADoubleSerializerDeserializer.getDouble(bytes,
                          offset + 1 + APolygonSerializerDeserializer.getCoordinateOffset(0, Coordinate.X));
                  y2 = ADoubleSerializerDeserializer.getDouble(bytes,
                          offset + 1 + APolygonSerializerDeserializer.getCoordinateOffset(0, Coordinate.Y));
              } else {
                  x2 = ADoubleSerializerDeserializer.getDouble(bytes,
                          offset + 1 + APolygonSerializerDeserializer.getCoordinateOffset(i + 1, Coordinate.X));
                  y2 = ADoubleSerializerDeserializer.getDouble(bytes,
                          offset + 1 + APolygonSerializerDeserializer.getCoordinateOffset(i + 1, Coordinate.Y));
              }
              area += (x1 * y2) - (x2 * y1);
          }
          return area * 0.5;
      }
  
      public void projectPolygon(DoubleArray trianglesX, DoubleArray trianglesY, int triangleId, double xAxis,
              double yAxis) throws HyracksDataException {
          double temp, min, max;
          min = max = getTriangleXCoordinate(trianglesX, triangleId, 0) * xAxis
                  + getTriangleYCoordinate(trianglesY, triangleId, 0) * yAxis;
  
          for (int i = 1; i < 3; i++) {
              temp = getTriangleXCoordinate(trianglesX, triangleId, i) * xAxis
                      + getTriangleYCoordinate(trianglesY, triangleId, i) * yAxis;
  
              if (temp > max) {
                  max = temp;
              } else if (temp < min) {
                  min = temp;
              }
          }
          setMinProjection(min);
          setMaxProjection(max);
      }
  
      public void findNormals(DoubleArray trianglesX, DoubleArray trianglesY, int triangleId, int side)
              throws HyracksDataException {
          double x, y;
          if (side == 0) {
              x = getTriangleYCoordinate(trianglesY, triangleId, 2)
                      - getTriangleYCoordinate(trianglesY, triangleId, side);
  
              y = getTriangleXCoordinate(trianglesX, triangleId, side)
                      - getTriangleXCoordinate(trianglesX, triangleId, 2);
  
          } else {
              x = getTriangleYCoordinate(trianglesY, triangleId, side - 1)
                      - getTriangleYCoordinate(trianglesY, triangleId, side);
  
              y = getTriangleXCoordinate(trianglesX, triangleId, side)
                      - getTriangleXCoordinate(trianglesX, triangleId, side - 1);
          }
          double temp = Math.sqrt(dotProduct(x, y, x, y));
          x /= temp;
          y /= temp;
          setXAxis(x);
          setYAxis(y);
      }
  
      public static double getTriangleXCoordinate(DoubleArray trianglesX, int triangleId, int point) {
          return trianglesX.get(triangleId * 3 + point);
      }
  
      public static double getTriangleYCoordinate(DoubleArray trianglesY, int triangleId, int point) {
          return trianglesY.get(triangleId * 3 + point);
      }
  
      public static void setTriangleXCoordinate(DoubleArray trianglesX, int triangleId, int point, double value) {
          trianglesX.get()[triangleId * 3 + point] = value;
      }
  
      public static void setTriangleYCoordinate(DoubleArray trianglesY, int triangleId, int point, double value) {
          trianglesY.get()[triangleId * 3 + point] = value;
      }
  
      public static boolean intersects(ARectangle rect1, ARectangle rect2) {
          // If one rectangle is on left side of other
          if ((rect1.getP1().getX() > rect2.getP2().getX()) || (rect2.getP1().getX() > rect1.getP2().getX())) {
              return false;
          }
  
          // If one rectangle is above other
          if ((rect1.getP1().getY() > rect2.getP2().getY()) || (rect2.getP1().getY() > rect1.getP2().getY())) {
              return false;
          }
  
          return true;
      }
  }
  