UNPKG

zrender

Version:

A lightweight graphic library providing 2d draw for Apache ECharts

187 lines (161 loc) 5.92 kB
/* * 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;