123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /*
- * 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;
|