using UnityEngine;
using System.Collections;
using System;
// A UV Transform is a transform considering only scale and offset it is used to represent the scaling and offset
// from UVs outside the 0,0..1,1 box and material tiling
// Rect objects are used to store the transform
namespace DigitalOpus.MB.Core
{
public struct DVector2
{
static double epsilon = 10e-6;
public double x;
public double y;
public static DVector2 Subtract(DVector2 a, DVector2 b)
{
return new DVector2(a.x - b.x, a.y - b.y);
}
public DVector2(double xx, double yy)
{
x = xx;
y = yy;
}
public DVector2(DVector2 r)
{
x = r.x;
y = r.y;
}
public Vector2 GetVector2()
{
return new Vector2((float)x, (float)y);
}
public bool IsContainedIn(DRect r)
{
if (x >= r.x && y >= r.y && x <= r.x + r.width && y <= r.y + r.height)
{
return true;
} else
{
return false;
}
}
public bool IsContainedInWithMargin(DRect r)
{
if (x >= r.x - epsilon && y >= r.y - epsilon && x <= r.x + r.width + epsilon && y <= r.y + r.height + epsilon)
{
return true;
}
else
{
return false;
}
}
public override string ToString()
{
return string.Format("({0},{1})", x, y);
}
public string ToString(string formatS)
{
return string.Format("({0},{1})", x.ToString(formatS), y.ToString(formatS));
}
public static double Distance(DVector2 a, DVector2 b)
{
double dx = b.x - a.x;
double dy = b.y - a.y;
return System.Math.Sqrt(dx * dx + dy * dy);
}
}
public struct DRect
{
public double x;
public double y;
public double width;
public double height;
public DRect(Rect r)
{
x = r.x;
y = r.y;
width = r.width;
height = r.height;
}
public DRect(Vector2 o, Vector2 s)
{
x = o.x;
y = o.y;
width = s.x;
height = s.y;
}
public DRect(DRect r)
{
x = r.x;
y = r.y;
width = r.width;
height = r.height;
}
public DRect(float xx, float yy, float w, float h)
{
x = xx;
y = yy;
width = w;
height = h;
}
public DRect(double xx, double yy, double w, double h)
{
x = xx;
y = yy;
width = w;
height = h;
}
public Rect GetRect()
{
return new Rect((float)x, (float)y, (float)width, (float)height);
}
public DVector2 minD
{
get
{
return new DVector2(x, y);
}
}
public DVector2 maxD
{
get
{
return new DVector2((x + width), (y + height));
}
}
public Vector2 min
{
get
{
return new Vector2((float)x, (float)y);
}
}
public Vector2 max
{
get
{
return new Vector2((float)(x + width), (float)(y + height));
}
}
public Vector2 size {
get {
return new Vector2((float)(width), (float)(height));
}
}
public DVector2 center
{
get
{
return new DVector2(x + width / 2.0, y + height / 2.0);
}
}
public override bool Equals(object obj)
{
DRect dr = (DRect) obj;
if (dr.x == x && dr.y == y && dr.width == width && dr.height == height)
{
return true;
}
return false;
}
public static bool operator ==(DRect a, DRect b)
{
return a.Equals(b);
}
public static bool operator !=(DRect a, DRect b)
{
return !a.Equals(b);
}
public override string ToString()
{
return String.Format("(x={0},y={1},w={2},h={3})", x.ToString("F5"), y.ToString("F5"), width.ToString("F5"), height.ToString("F5"));
}
public void Expand(float amt)
{
x -= amt;
y -= amt;
width += amt * 2;
height += amt * 2;
}
public bool Encloses(DRect smallToTestIfFits)
{
double smnx = smallToTestIfFits.x;
double smny = smallToTestIfFits.y;
double smxx = smallToTestIfFits.x + smallToTestIfFits.width;
double smxy = smallToTestIfFits.y + smallToTestIfFits.height;
//expand slightly to deal with rounding errors
double bmnx = this.x;
double bmny = this.y;
double bmxx = this.x + this.width;
double bmxy = this.y + this.height;
return bmnx <= smnx && smnx <= bmxx &&
bmnx <= smxx && smxx <= bmxx &&
bmny <= smny && smny <= bmxy &&
bmny <= smxy && smxy <= bmxy;
}
public override int GetHashCode ()
{
return x.GetHashCode() ^ y.GetHashCode() ^ width.GetHashCode() ^ height.GetHashCode();
}
}
public class MB3_UVTransformUtility
{
public static void Test()
{
/*
Debug.Log("Running test");
DRect rawUV = new DRect(.25, .25, 1.5, 1.5);
DRect rawMat = new DRect(.5, .5, 2, 2);
DRect rawCombined = CombineTransforms(ref rawUV, ref rawMat);
DRect matInvHierarchy = new DRect(rawMat.GetRect());
InvertHierarchy(ref rawUV, ref matInvHierarchy);
DRect invertHierarchyCombined = CombineTransforms(ref matInvHierarchy, ref rawUV);
//These transforms should be the same
Debug.Log("These should be same " + rawCombined + " " + invertHierarchyCombined);
//New transform that should fit in combined
DRect otherUV = new DRect(1,1,1.5,1.5);
DRect otherMat = new DRect(0, 0, 1, 1);
DRect otherCombined = CombineTransforms(ref otherUV, ref otherMat);
Debug.Log("Other : " + otherCombined);
Debug.Log("Other fits = " + RectContains(ref rawCombined, ref otherCombined));
DRect invOtherCombined = InverseTransform(ref otherCombined);
Debug.Log(TransformPoint(ref otherCombined, new Vector2(0, 0)) + " " + TransformPoint(ref otherCombined, new Vector2(1, 1)));
Debug.Log(TransformPoint(ref invOtherCombined, new Vector2(0,0)) + " " + TransformPoint(ref invOtherCombined, new Vector2(1,1)).ToString("f5")
+ " " + TransformPoint(ref invOtherCombined, new Vector2(2, 2)).ToString("f5")
+ " " + TransformPoint(ref invOtherCombined, new Vector2(3, 3)).ToString("f5"));
DRect src2combined = CombineTransforms(ref invOtherCombined, ref rawCombined);
Debug.Log(TransformPoint(ref src2combined, new Vector2(0, 0)) + " " + TransformPoint(ref src2combined, new Vector2(1, 1)).ToString("f5")
+ " " + TransformPoint(ref src2combined, new Vector2(2, 2)).ToString("f5")
+ " " + TransformPoint(ref src2combined, new Vector2(3, 3)).ToString("f5"));
*/
//DRect rawUV = new DRect(0, 0, 1, 1);
DRect rawMat = new DRect(.5, .5, 2, 2);
DRect fullSample = new DRect(.25,.25,3,3);
//DRect altasRect = new DRect(0, 0, 1, 1);
DRect invRawMat = InverseTransform(ref rawMat);
DRect invFullSample = InverseTransform(ref fullSample);
DRect relativeTransform = CombineTransforms(ref rawMat, ref invFullSample);
Debug.Log(invRawMat);
Debug.Log(relativeTransform);
Debug.Log("one mat trans " + TransformPoint(ref rawMat, new Vector2(1, 1)));
Debug.Log("one inv mat trans " + TransformPoint(ref invRawMat, new Vector2(1, 1)).ToString("f4"));
Debug.Log("zero " + TransformPoint(ref relativeTransform, new Vector2(0, 0)).ToString("f4"));
Debug.Log("one " + TransformPoint(ref relativeTransform, new Vector2(1, 1)).ToString("f4"));
}
public static float TransformX(DRect r, double x)
{
return (float) (r.width * x + r.x);
}
public static DRect CombineTransforms(ref DRect r1, ref DRect r2)
{
DRect rCombined = new DRect(r1.x * r2.width + r2.x,
r1.y * r2.height + r2.y,
r1.width * r2.width,
r1.height * r2.height);
//rCombined.x = rCombined.x - Mathf.FloorToInt(rCombined.x);
//rCombined.y = rCombined.y - Mathf.FloorToInt(rCombined.y);
return rCombined;
}
public static Rect CombineTransforms(ref Rect r1, ref Rect r2)
{
Rect rCombined = new Rect(r1.x * r2.width + r2.x,
r1.y * r2.height + r2.y,
r1.width * r2.width,
r1.height * r2.height);
//rCombined.x = rCombined.x - Mathf.FloorToInt(rCombined.x);
//rCombined.y = rCombined.y - Mathf.FloorToInt(rCombined.y);
return rCombined;
}
//since the 0,0..1,1 box is tiled the offset should always be between 0 and 1
/*
public static void Canonicalize(ref DRect r, double minX, double minY)
{
r.x = r.x - Mathf.FloorToInt((float) r.x);
if (r.x < minX) { r.x += Mathf.CeilToInt((float)minX); }
r.y = r.y - Mathf.FloorToInt((float) r.y);
if (r.y < minY) { r.y += Mathf.CeilToInt((float)minY); }
}
public static void Canonicalize(ref Rect r, float minX, float minY)
{
r.x = r.x - Mathf.FloorToInt(r.x);
if (r.x < minX) { r.x += Mathf.CeilToInt(minX); }
r.y = r.y - Mathf.FloorToInt(r.y);
if (r.y < minY) { r.y += Mathf.CeilToInt(minY); }
}
*/
public static DRect InverseTransform(ref DRect t)
{
DRect tinv = new DRect();
tinv.x = -t.x / t.width;
tinv.y = -t.y / t.height;
tinv.width = 1f / t.width;
tinv.height = 1f / t.height;
return tinv;
}
public static DRect GetShiftTransformToFitBinA(ref DRect A, ref DRect B)
{
DVector2 ac = A.center;
DVector2 bc = B.center;
DVector2 diff = DVector2.Subtract(ac, bc);
double dx = Convert.ToInt32(diff.x);
double dy = Convert.ToInt32(diff.y);
return new DRect(dx,dy,1.0,1.0);
}
///
/// shifts willBeIn so it is centered in uvRect1, then find a rect that encloses both
///
///
///
///
public static DRect GetEncapsulatingRectShifted(ref DRect uvRect1, ref DRect willBeIn)
{
DVector2 bc = uvRect1.center;
DVector2 tfc = willBeIn.center;
DVector2 diff = DVector2.Subtract(bc, tfc);
double dx = Convert.ToInt32(diff.x);
double dy = Convert.ToInt32(diff.y);
DRect uvRect2 = new DRect(willBeIn);
uvRect2.x += dx;
uvRect2.y += dy;
double smnx = uvRect1.x;
double smny = uvRect1.y;
double smxx = uvRect1.x + uvRect1.width;
double smxy = uvRect1.y + uvRect1.height;
double bmnx = uvRect2.x;
double bmny = uvRect2.y;
double bmxx = uvRect2.x + uvRect2.width;
double bmxy = uvRect2.y + uvRect2.height;
double minx, miny, maxx, maxy;
minx = maxx = smnx;
miny = maxy = smny;
if (bmnx < minx) minx = bmnx;
if (smnx < minx) minx = smnx;
if (bmny < miny) miny = bmny;
if (smny < miny) miny = smny;
if (bmxx > maxx) maxx = bmxx;
if (smxx > maxx) maxx = smxx;
if (bmxy > maxy) maxy = bmxy;
if (smxy > maxy) maxy = smxy;
DRect uvRectCombined = new DRect(minx, miny, maxx - minx, maxy - miny);
return uvRectCombined;
}
public static DRect GetEncapsulatingRect(ref DRect uvRect1, ref DRect uvRect2)
{
double smnx = uvRect1.x;
double smny = uvRect1.y;
double smxx = uvRect1.x + uvRect1.width;
double smxy = uvRect1.y + uvRect1.height;
double bmnx = uvRect2.x;
double bmny = uvRect2.y;
double bmxx = uvRect2.x + uvRect2.width;
double bmxy = uvRect2.y + uvRect2.height;
double minx, miny, maxx, maxy;
minx = maxx = smnx;
miny = maxy = smny;
if (bmnx < minx) minx = bmnx;
if (smnx < minx) minx = smnx;
if (bmny < miny) miny = bmny;
if (smny < miny) miny = smny;
if (bmxx > maxx) maxx = bmxx;
if (smxx > maxx) maxx = smxx;
if (bmxy > maxy) maxy = bmxy;
if (smxy > maxy) maxy = smxy;
DRect uvRectCombined = new DRect(minx, miny, maxx - minx, maxy - miny);
return uvRectCombined;
}
/*
public static void InvertHierarchy(ref DRect uvRect, ref DRect matRect)
{
matRect.x = (uvRect.x * matRect.width + matRect.x - uvRect.x) / uvRect.width;
matRect.y = (uvRect.y * matRect.height + matRect.y - uvRect.y) / uvRect.height;
}
*/
public static bool RectContainsShifted(ref DRect bucket, ref DRect tryFit)
{
//get the centers of bucket and tryFit
DVector2 bc = bucket.center;
DVector2 tfc = tryFit.center;
DVector2 diff = DVector2.Subtract(bc, tfc);
double dx = Convert.ToInt32(diff.x);
double dy = Convert.ToInt32(diff.y);
DRect tmp = new DRect(tryFit);
tmp.x += dx;
tmp.y += dy;
return bucket.Encloses(tmp);
}
public static bool RectContainsShifted(ref Rect bucket, ref Rect tryFit)
{
//get the centers of bucket and tryFit
Vector2 bc = bucket.center;
Vector2 tfc = tryFit.center;
Vector2 diff = bc - tfc;
float dx = Convert.ToInt32(diff.x);
float dy = Convert.ToInt32(diff.y);
Rect tmp = new Rect(tryFit);
tmp.x += dx;
tmp.y += dy;
return RectContains(ref bucket, ref tmp);
}
public static bool LineSegmentContainsShifted(float bucketOffset, float bucketLength, float tryFitOffset, float tryFitLength)
{
Debug.Assert(bucketLength >= 0);
Debug.Assert(tryFitLength >= 0);
float bc = bucketOffset + bucketLength / 2f;
float tfc = tryFitOffset + tryFitLength / 2f;
float diff = bc - tfc;
float delta = Convert.ToInt32(diff);
tryFitOffset += delta;
float sminx = tryFitOffset;
float smaxx = tryFitOffset + tryFitLength;
float bminx = bucketOffset - 10e-3f;
float bmaxx = bucketOffset + bucketLength + 10e-3f;
return bminx <= sminx && sminx <= bmaxx &&
bminx <= smaxx && smaxx <= bmaxx;
}
public static bool RectContains(ref DRect bigRect, ref DRect smallToTestIfFits)
{
double sminx = smallToTestIfFits.x;
double sminy = smallToTestIfFits.y;
double smaxx = smallToTestIfFits.x + smallToTestIfFits.width;
double smaxy = smallToTestIfFits.y + smallToTestIfFits.height;
//expand slightly to deal with rounding errors
double bminx = bigRect.x - 10e-3f;
double bminy = bigRect.y - 10e-3f;
double bmaxx = bigRect.x + bigRect.width + 10e-3f;
double bmaxy = bigRect.y + bigRect.height + 10e-3f;
//is smn in box
/*
string s = "";
s += (bmnx <= smnx);
s += (smnx <= bmxx);
s += (bmnx <= smxx);
s += String.Format("{0} {1} {2}",(smxx <= bmxx).ToString(), smxx.ToString("F5"), bmxx.ToString("F5"));
s += String.Format("{0} {1} {2}",(bmny <= smny), bmny.ToString("F5"), smny.ToString("F5"));
s += (smny <= bmxy);
s += (bmny <= smxy);
s += String.Format("{0} {1} {2}",(smxy <= bmxy).ToString(), smxy.ToString("F5"), bmxy.ToString("F5"));
Debug.Log("==== " + bigRect + " " + smallToTestIfFits + " " + s);
*/
return bminx <= sminx && sminx <= bmaxx &&
bminx <= smaxx && smaxx <= bmaxx &&
bminy <= sminy && sminy <= bmaxy &&
bminy <= smaxy && smaxy <= bmaxy;
}
public static bool RectContains(ref Rect bigRect, ref Rect smallToTestIfFits)
{
float smnx = smallToTestIfFits.x;
float smny = smallToTestIfFits.y;
float smxx = smallToTestIfFits.x + smallToTestIfFits.width;
float smxy = smallToTestIfFits.y + smallToTestIfFits.height;
//expand slightly to deal with rounding errors
float bmnx = bigRect.x - 10e-3f;
float bmny = bigRect.y - 10e-3f;
float bmxx = bigRect.x + bigRect.width + 10e-3f;
float bmxy = bigRect.y + bigRect.height + 10e-3f;
//is smn in box
/*
Debug.Log("==== " + bigRect + " " + smallToTestIfFits);
Debug.Log(bmnx <= smnx);
Debug.Log(smnx <= bmxx);
Debug.Log(bmnx <= smxx);
Debug.LogFormat("{0} {1} {2}", (smxx <= bmxx).ToString(), smxx.ToString("F5"), bmxx.ToString("F5"));
Debug.LogFormat("{0} {1} {2}", (bmny <= smny), bmny.ToString("F5"), smny.ToString("F5"));
Debug.Log(smny <= bmxy);
Debug.Log(bmny <= smxy);
Debug.LogFormat("{0} {1} {2}", (smxy <= bmxy).ToString(), smxy.ToString("F5"), bmxy.ToString("F5"));
Debug.Log("----------------");
*/
return bmnx <= smnx && smnx <= bmxx &&
bmnx <= smxx && smxx <= bmxx &&
bmny <= smny && smny <= bmxy &&
bmny <= smxy && smxy <= bmxy;
}
public static Vector2 TransformPoint(ref DRect r, Vector2 p)
{
return new Vector2((float) (r.width * p.x + r.x),(float)(r.height * p.y + r.y));
}
public static DVector2 TransformPoint(ref DRect r, DVector2 p)
{
return new DVector2((r.width * p.x + r.x), (r.height * p.y + r.y));
}
}
}