dragonbones-runtime
Version:
the tools to build dragonbones file for diffrent framework
552 lines (469 loc) • 19 kB
text/typescript
//////////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2014-present, Egret Technology.
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Egret nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY EGRET AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL EGRET AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////////////
namespace eui.sys {
let SOLUTION_TOLERANCE = 0.1;
let MIN_MAX_TOLERANCE = 0.1;
/**
* @private
*/
export class MatrixUtil {
/**
* @private
*/
public static fitBounds(width:number, height:number, matrix:egret.Matrix,
explicitWidth:number, explicitHeight:number,
preferredWidth:number, preferredHeight:number,
minWidth:number, minHeight:number,
maxWidth:number, maxHeight:number):egret.Point {
if (isNaN(width) && isNaN(height))
return egret.Point.create(preferredWidth, preferredHeight);
let newMinWidth = (minWidth < MIN_MAX_TOLERANCE) ? 0 : minWidth - MIN_MAX_TOLERANCE;
let newMinHeight = (minHeight < MIN_MAX_TOLERANCE) ? 0 : minHeight - MIN_MAX_TOLERANCE;
let newMaxWidth = maxWidth + MIN_MAX_TOLERANCE;
let newMaxHeight = maxHeight + MIN_MAX_TOLERANCE;
let actualSize:egret.Point;
if (!isNaN(width) && !isNaN(height)) {
actualSize = calcUBoundsToFitTBounds(width, height, matrix,
newMinWidth, newMinHeight,
newMaxWidth, newMaxHeight);
if (!actualSize) {
let actualSize1:egret.Point;
actualSize1 = fitTBoundsWidth(width, matrix,
explicitWidth, explicitHeight,
preferredWidth, preferredHeight,
newMinWidth, newMinHeight,
newMaxWidth, newMaxHeight);
if (actualSize1) {
let fitHeight = transformSize(actualSize1.x, actualSize1.y, matrix).height;
if (fitHeight - SOLUTION_TOLERANCE > height)
{
egret.Point.release(actualSize1);
actualSize1 = null;
}
}
let actualSize2:egret.Point
actualSize2 = fitTBoundsHeight(height, matrix,
explicitWidth, explicitHeight,
preferredWidth, preferredHeight,
newMinWidth, newMinHeight,
newMaxWidth, newMaxHeight);
if (actualSize2) {
let fitWidth = transformSize(actualSize2.x, actualSize2.y, matrix).width;
if (fitWidth - SOLUTION_TOLERANCE > width)
{
egret.Point.release(actualSize2);
actualSize2 = null;
}
}
if (actualSize1 && actualSize2) {
actualSize = ((actualSize1.x * actualSize1.y) > (actualSize2.x * actualSize2.y)) ? actualSize1 : actualSize2;
}
else if (actualSize1) {
actualSize = actualSize1;
}
else {
actualSize = actualSize2;
}
egret.Point.release(actualSize1);
egret.Point.release(actualSize2);
}
return actualSize;
}
else if (!isNaN(width)) {
return fitTBoundsWidth(width, matrix,
explicitWidth, explicitHeight,
preferredWidth, preferredHeight,
newMinWidth, newMinHeight,
newMaxWidth, newMaxHeight);
}
else {
return fitTBoundsHeight(height, matrix,
explicitWidth, explicitHeight,
preferredWidth, preferredHeight,
newMinWidth, newMinHeight,
newMaxWidth, newMaxHeight);
}
}
}
/**
* @private
*/
function fitTBoundsWidth(width:number, matrix:egret.Matrix,
explicitWidth:number, explicitHeight:number,
preferredWidth:number, preferredHeight:number,
minWidth:number, minHeight:number,
maxWidth:number, maxHeight:number):egret.Point {
let actualSize:egret.Point;
if (!isNaN(explicitWidth) && isNaN(explicitHeight)) {
actualSize = calcUBoundsToFitTBoundsWidth(width, matrix,
explicitWidth, preferredHeight,
explicitWidth, minHeight,
explicitWidth, maxHeight);
if (actualSize)
return actualSize;
}
else if (isNaN(explicitWidth) && !isNaN(explicitHeight)) {
actualSize = calcUBoundsToFitTBoundsWidth(width, matrix,
preferredWidth, explicitHeight,
minWidth, explicitHeight,
maxWidth, explicitHeight);
if (actualSize)
return actualSize;
}
actualSize = calcUBoundsToFitTBoundsWidth(width, matrix,
preferredWidth, preferredHeight,
minWidth, minHeight,
maxWidth, maxHeight);
return actualSize;
}
/**
* @private
*/
function fitTBoundsHeight(height:number, matrix:egret.Matrix,
explicitWidth:number, explicitHeight:number,
preferredWidth:number, preferredHeight:number,
minWidth:number, minHeight:number,
maxWidth:number, maxHeight:number):egret.Point {
let actualSize:egret.Point;
if (!isNaN(explicitWidth) && isNaN(explicitHeight)) {
actualSize = calcUBoundsToFitTBoundsHeight(height, matrix,
explicitWidth, preferredHeight,
explicitWidth, minHeight,
explicitWidth, maxHeight);
if (actualSize)
return actualSize;
}
else if (isNaN(explicitWidth) && !isNaN(explicitHeight)) {
actualSize = calcUBoundsToFitTBoundsHeight(height, matrix,
preferredWidth, explicitHeight,
minWidth, explicitHeight,
maxWidth, explicitHeight);
if (actualSize)
return actualSize;
}
actualSize = calcUBoundsToFitTBoundsHeight(height, matrix,
preferredWidth, preferredHeight,
minWidth, minHeight,
maxWidth, maxHeight);
return actualSize;
}
/**
* @private
*/
function calcUBoundsToFitTBoundsHeight(h:number,
matrix:egret.Matrix,
preferredX:number,
preferredY:number,
minX:number,
minY:number,
maxX:number,
maxY:number):egret.Point {
let b = matrix.b;
let d = matrix.d;
if (-1.0e-9 < b && b < +1.0e-9)
b = 0;
if (-1.0e-9 < d && d < +1.0e-9)
d = 0;
if (b == 0 && d == 0)
return null;
if (b == 0 && d == 0)
return null;
if (b == 0)
return egret.Point.create(preferredX, h / Math.abs(d));
else if (d == 0)
return egret.Point.create(h / Math.abs(b), preferredY);
let d1 = (b * d >= 0) ? d : -d;
let s:egret.Point;
let x:number;
let y:number;
if (d1 != 0 && preferredX > 0) {
let invD1 = 1 / d1;
preferredX = Math.max(minX, Math.min(maxX, preferredX));
x = preferredX;
y = (h - b * x) * invD1;
if (minY <= y && y <= maxY &&
b * x + d1 * y >= 0) {
s = egret.Point.create(x, y);
}
y = (-h - b * x) * invD1;
if (minY <= y && y <= maxY &&
b * x + d1 * y < 0) {
if (!s || transformSize(s.x, s.y, matrix).width > transformSize(x, y, matrix).width)
{
egret.Point.release(s);
s = egret.Point.create(x, y);
}
}
}
if (b != 0 && preferredY > 0) {
let invB = 1 / b;
preferredY = Math.max(minY, Math.min(maxY, preferredY));
y = preferredY;
x = ( h - d1 * y ) * invB;
if (minX <= x && x <= maxX &&
b * x + d1 * y >= 0) {
if (!s || transformSize(s.x, s.y, matrix).width > transformSize(x, y, matrix).width)
s = egret.Point.create(x, y);
}
x = ( -h - d1 * y ) * invB;
if (minX <= x && x <= maxX &&
b * x + d1 * y < 0) {
if (!s || transformSize(s.x, s.y, matrix).width > transformSize(x, y, matrix).width)
{
egret.Point.release(s);
s = egret.Point.create(x, y);
}
}
}
if (s)
return s;
let a = matrix.a;
let c = matrix.c;
let c1 = ( a * c >= 0 ) ? c : -c;
return solveEquation(b, d1, h, minX, minY, maxX, maxY, a, c1);
}
/**
* @private
*/
function calcUBoundsToFitTBoundsWidth(w:number,
matrix:egret.Matrix,
preferredX:number,
preferredY:number,
minX:number,
minY:number,
maxX:number,
maxY:number):egret.Point {
let a = matrix.a;
let c = matrix.c;
if (-1.0e-9 < a && a < +1.0e-9)
a = 0;
if (-1.0e-9 < c && c < +1.0e-9)
c = 0;
if (a == 0 && c == 0)
return null;
if (a == 0)
return egret.Point.create(preferredX, w / Math.abs(c));
else if (c == 0)
return egret.Point.create(w / Math.abs(a), preferredY);
let c1 = ( a * c >= 0 ) ? c : -c;
let s:egret.Point;
let x:number;
let y:number;
if (c1 != 0 && preferredX > 0) {
let invC1 = 1 / c1;
preferredX = Math.max(minX, Math.min(maxX, preferredX));
x = preferredX;
y = (w - a * x) * invC1;
if (minY <= y && y <= maxY &&
a * x + c1 * y >= 0) {
s = egret.Point.create(x, y);
}
y = (-w - a * x) * invC1;
if (minY <= y && y <= maxY &&
a * x + c1 * y < 0) {
if (!s || transformSize(s.x, s.y, matrix).height > transformSize(x, y, matrix).height)
{
egret.Point.release(s);
s = egret.Point.create(x, y);
}
}
}
if (a != 0 && preferredY > 0) {
let invA = 1 / a;
preferredY = Math.max(minY, Math.min(maxY, preferredY));
y = preferredY;
x = (w - c1 * y ) * invA;
if (minX <= x && x <= maxX &&
a * x + c1 * y >= 0) {
if (!s || transformSize(s.x, s.y, matrix).height > transformSize(x, y, matrix).height)
{
egret.Point.release(s);
s = egret.Point.create(x, y);
}
}
x = (-w - c1 * y ) * invA;
if (minX <= x && x <= maxX &&
a * x + c1 * y < 0) {
if (!s || transformSize(s.x, s.y, matrix).height > transformSize(x, y, matrix).height)
{
egret.Point.release(s);
s = egret.Point.create(x, y);
}
}
}
if (s)
return s;
let b = matrix.b;
let d = matrix.d;
let d1 = (b * d >= 0) ? d : -d;
return solveEquation(a, c1, w, minX, minY, maxX, maxY, b, d1);
}
/**
* @private
*/
function solveEquation(a:number,
c:number,
w:number,
minX:number,
minY:number,
maxX:number,
maxY:number,
b:number,
d:number):egret.Point {
if (a == 0 || c == 0)
return null;
let x:number;
let y:number;
let A = (w - minX * a) / c;
let B = (w - maxX * a) / c;
let rangeMinY = Math.max(minY, Math.min(A, B));
let rangeMaxY = Math.min(maxY, Math.max(A, B));
let det = (b * c - a * d);
if (rangeMinY <= rangeMaxY) {
if (Math.abs(det) < 1.0e-9) {
y = w / ( a + c );
}
else {
y = b * w / det;
}
y = Math.max(rangeMinY, Math.min(y, rangeMaxY));
x = (w - c * y) / a;
return egret.Point.create(x, y);
}
A = -(minX * a + w) / c;
B = -(maxX * a + w) / c;
rangeMinY = Math.max(minY, Math.min(A, B));
rangeMaxY = Math.min(maxY, Math.max(A, B));
if (rangeMinY <= rangeMaxY) {
if (Math.abs(det) < 1.0e-9) {
y = -w / ( a + c );
}
else {
y = -b * w / det;
}
y = Math.max(rangeMinY, Math.min(y, rangeMaxY));
x = (-w - c * y) / a;
return egret.Point.create(x, y);
}
return null;
}
/**
* @private
*/
function calcUBoundsToFitTBounds(w:number,
h:number,
matrix:egret.Matrix,
minX:number,
minY:number,
maxX:number,
maxY:number):egret.Point {
let a = matrix.a;
let b = matrix.b;
let c = matrix.c;
let d = matrix.d;
if (-1.0e-9 < a && a < +1.0e-9)
a = 0;
if (-1.0e-9 < b && b < +1.0e-9)
b = 0;
if (-1.0e-9 < c && c < +1.0e-9)
c = 0;
if (-1.0e-9 < d && d < +1.0e-9)
d = 0;
if (b == 0 && c == 0) {
if (a == 0 || d == 0)
return null;
return egret.Point.create(w / Math.abs(a), h / Math.abs(d));
}
if (a == 0 && d == 0) {
if (b == 0 || c == 0)
return null;
return egret.Point.create(h / Math.abs(b), w / Math.abs(c));
}
let c1 = ( a * c >= 0 ) ? c : -c;
let d1 = ( b * d >= 0 ) ? d : -d;
let det = a * d1 - b * c1;
if (Math.abs(det) < 1.0e-9) {
if (c1 == 0 || a == 0 || a == -c1)
return null;
if (Math.abs(a * h - b * w) > 1.0e-9)
return null;
return solveEquation(a, c1, w, minX, minX, maxX, maxY, b, d1);
}
let invDet = 1 / det;
w *= invDet;
h *= invDet;
let s:egret.Point;
s = solveSystem(a, c1, b, d1, w, h);
if (s &&
minX <= s.x && s.x <= maxX && minY <= s.y && s.y <= maxY &&
a * s.x + c1 * s.x >= 0 &&
b * s.x + d1 * s.y >= 0)
return s;
s = solveSystem(a, c1, b, d1, w, -h);
if (s &&
minX <= s.x && s.x <= maxX && minY <= s.y && s.y <= maxY &&
a * s.x + c1 * s.x >= 0 &&
b * s.x + d1 * s.y < 0)
return s;
s = solveSystem(a, c1, b, d1, -w, h);
if (s &&
minX <= s.x && s.x <= maxX && minY <= s.y && s.y <= maxY &&
a * s.x + c1 * s.x < 0 &&
b * s.x + d1 * s.y >= 0)
return s;
s = solveSystem(a, c1, b, d1, -w, -h);
if (s &&
minX <= s.x && s.x <= maxX && minY <= s.y && s.y <= maxY &&
a * s.x + c1 * s.x < 0 &&
b * s.x + d1 * s.y < 0)
return s;
egret.Point.release(s);
return null;
}
/**
* @private
*/
function transformSize(width:number, height:number, matrix:egret.Matrix):egret.Rectangle {
let bounds = egret.$TempRectangle.setTo(0, 0, width, height);
matrix.$transformBounds(bounds);
return bounds;
}
/**
* @private
*/
function solveSystem(a:number,
c:number,
b:number,
d:number,
mOverDet:number,
nOverDet:number):egret.Point {
return egret.Point.create(d * mOverDet - c * nOverDet,
a * nOverDet - b * mOverDet);
}
}