/* 
 | 
* 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. 
 | 
*/ 
 | 
  
 | 
import Point, { PointLike } from './Point'; 
 | 
import BoundingRect from './BoundingRect'; 
 | 
import { MatrixArray } from './matrix'; 
 | 
  
 | 
const extent = [0, 0]; 
 | 
const extent2 = [0, 0]; 
 | 
  
 | 
const minTv = new Point(); 
 | 
const maxTv = new Point(); 
 | 
  
 | 
class OrientedBoundingRect { 
 | 
  
 | 
    // lt, rt, rb, lb 
 | 
    private _corners: Point[] = []; 
 | 
  
 | 
    private _axes: Point[] = []; 
 | 
  
 | 
    private _origin: number[] = [0, 0]; 
 | 
  
 | 
    constructor(rect?: BoundingRect, transform?: MatrixArray) { 
 | 
        for (let i = 0; i < 4; i++) { 
 | 
            this._corners[i] = new Point(); 
 | 
        } 
 | 
        for (let i = 0; i < 2; i++) { 
 | 
            this._axes[i] = new Point(); 
 | 
        } 
 | 
  
 | 
        if (rect) { 
 | 
            this.fromBoundingRect(rect, transform); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    fromBoundingRect(rect: BoundingRect, transform?: MatrixArray) { 
 | 
        const corners = this._corners; 
 | 
        const axes = this._axes; 
 | 
        const x = rect.x; 
 | 
        const y = rect.y; 
 | 
        const x2 = x + rect.width; 
 | 
        const y2 = y + rect.height; 
 | 
        corners[0].set(x, y); 
 | 
        corners[1].set(x2, y); 
 | 
        corners[2].set(x2, y2); 
 | 
        corners[3].set(x, y2); 
 | 
  
 | 
        if (transform) { 
 | 
            for (let i = 0; i < 4; i++) { 
 | 
                corners[i].transform(transform); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        // Calculate axes 
 | 
        Point.sub(axes[0], corners[1], corners[0]); 
 | 
        Point.sub(axes[1], corners[3], corners[0]); 
 | 
        axes[0].normalize(); 
 | 
        axes[1].normalize(); 
 | 
  
 | 
        // Calculate projected origin 
 | 
        for (let i = 0; i < 2; i++) { 
 | 
            this._origin[i] = axes[i].dot(corners[0]); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * If intersect with another OBB 
 | 
     * @param other Bounding rect to be intersected with 
 | 
     * @param mtv Calculated . 
 | 
     *  If it's not overlapped. it means needs to move given rect with Maximum Translation Vector to be overlapped. 
 | 
     *  Else it means needs to move given rect with Minimum Translation Vector to be not overlapped. 
 | 
     */ 
 | 
    intersect(other: OrientedBoundingRect, mtv?: PointLike): boolean { 
 | 
        // OBB collision with SAT method 
 | 
  
 | 
        let overlapped = true; 
 | 
        const noMtv = !mtv; 
 | 
        minTv.set(Infinity, Infinity); 
 | 
        maxTv.set(0, 0); 
 | 
        // Check two axes for both two obb. 
 | 
        if (!this._intersectCheckOneSide(this, other, minTv, maxTv, noMtv, 1)) { 
 | 
            overlapped = false; 
 | 
            if (noMtv) { 
 | 
                // Early return if no need to calculate mtv 
 | 
                return overlapped; 
 | 
            } 
 | 
        } 
 | 
        if (!this._intersectCheckOneSide(other, this, minTv, maxTv, noMtv, -1)) { 
 | 
            overlapped = false; 
 | 
            if (noMtv) { 
 | 
                return overlapped; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        if (!noMtv) { 
 | 
            Point.copy(mtv, overlapped ? minTv : maxTv); 
 | 
        } 
 | 
  
 | 
        return overlapped; 
 | 
    } 
 | 
  
 | 
  
 | 
    private _intersectCheckOneSide( 
 | 
        self: OrientedBoundingRect, 
 | 
        other: OrientedBoundingRect, 
 | 
        minTv: Point, 
 | 
        maxTv: Point, 
 | 
        noMtv: boolean, 
 | 
        inverse: 1 | -1 
 | 
    ): boolean { 
 | 
        let overlapped = true; 
 | 
        for (let i = 0; i < 2; i++) { 
 | 
            const axis = this._axes[i]; 
 | 
            this._getProjMinMaxOnAxis(i, self._corners, extent); 
 | 
            this._getProjMinMaxOnAxis(i, other._corners, extent2); 
 | 
  
 | 
            // Not overlap on the any axis. 
 | 
            if (extent[1] < extent2[0] || extent[0] > extent2[1]) { 
 | 
                overlapped = false; 
 | 
                if (noMtv) { 
 | 
                    return overlapped; 
 | 
                } 
 | 
                const dist0 = Math.abs(extent2[0] - extent[1]); 
 | 
                const dist1 = Math.abs(extent[0] - extent2[1]); 
 | 
  
 | 
                // Find longest distance of all axes. 
 | 
                if (Math.min(dist0, dist1) > maxTv.len()) { 
 | 
                    if (dist0 < dist1) { 
 | 
                        Point.scale(maxTv, axis, -dist0 * inverse); 
 | 
                    } 
 | 
                    else { 
 | 
                        Point.scale(maxTv, axis, dist1 * inverse); 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
            else if (minTv) { 
 | 
                const dist0 = Math.abs(extent2[0] - extent[1]); 
 | 
                const dist1 = Math.abs(extent[0] - extent2[1]); 
 | 
  
 | 
                if (Math.min(dist0, dist1) < minTv.len()) { 
 | 
                    if (dist0 < dist1) { 
 | 
                        Point.scale(minTv, axis, dist0 * inverse); 
 | 
                    } 
 | 
                    else { 
 | 
                        Point.scale(minTv, axis, -dist1 * inverse); 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
        return overlapped; 
 | 
    } 
 | 
  
 | 
    private _getProjMinMaxOnAxis(dim: number, corners: Point[], out: number[]) { 
 | 
        const axis = this._axes[dim]; 
 | 
        const origin = this._origin; 
 | 
        const proj = corners[0].dot(axis) + origin[dim]; 
 | 
        let min = proj; 
 | 
        let max = proj; 
 | 
  
 | 
        for (let i = 1; i < corners.length; i++) { 
 | 
            const proj = corners[i].dot(axis) + origin[dim]; 
 | 
            min = Math.min(proj, min); 
 | 
            max = Math.max(proj, max); 
 | 
        } 
 | 
  
 | 
        out[0] = min; 
 | 
        out[1] = max; 
 | 
    } 
 | 
} 
 | 
  
 | 
export default OrientedBoundingRect; 
 |