/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 1998-2000, Matthes Bender * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ * Copyright (c) 2009-2016, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ /* Basic classes for rectangles and vertex outlines */ #include "C4Include.h" #include "lib/C4Rect.h" #include "graphics/C4FacetEx.h" #include "lib/StdCompiler.h" #include "lib/StdAdaptors.h" void C4Rect::Default() { x=y=Wdt=Hgt=0; } void C4Rect::CompileFunc(StdCompiler *pComp) { pComp->Value(mkDefaultAdapt(x, 0)); pComp->Separator(); pComp->Value(mkDefaultAdapt(y, 0)); pComp->Separator(); pComp->Value(mkDefaultAdapt(Wdt, 0)); pComp->Separator(); pComp->Value(mkDefaultAdapt(Hgt, 0)); } void C4TargetRect::Default() { C4Rect::Default(); tx=ty=0; } void C4TargetRect::Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, int32_t iTX, int32_t iTY) { C4Rect::Set(iX,iY,iWdt,iHgt); tx=iTX; ty=iTY; } bool C4TargetRect::ClipBy(C4TargetRect &rClip) { int32_t d; // clip left if ((d = x - rClip.x) < 0) { Wdt += d; x = rClip.x; } else tx += d; // clip top if ((d = y - rClip.y) < 0) { Hgt += d; y = rClip.y; } else ty += d; // clip right if ((d = (x+Wdt - rClip.x-rClip.Wdt)) > 0) Wdt -= d; // clip bottom if ((d = (y+Hgt - rClip.y-rClip.Hgt)) > 0) Hgt -= d; // check validity if (Wdt <= 0 || Hgt <= 0) return false; // add target pos tx += rClip.tx; ty += rClip.ty; // done return true; } void C4TargetRect::Set(const C4TargetFacet &rSrc) { // copy members x=rSrc.X; y=rSrc.Y; Wdt=rSrc.Wdt; Hgt=rSrc.Hgt; tx=rSrc.TargetX; ty=rSrc.TargetY; } void C4TargetRect::CompileFunc(StdCompiler *pComp) { C4Rect::CompileFunc(pComp); pComp->Separator(); pComp->Value(mkDefaultAdapt(tx,0)); pComp->Separator(); pComp->Value(mkDefaultAdapt(ty,0)); } void C4Rect::Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt) { x=iX; y=iY; Wdt=iWdt; Hgt=iHgt; } bool C4Rect::Overlap(C4Rect &rTarget) { if (x+Wdt<=rTarget.x) return false; if (x>=rTarget.x+rTarget.Wdt) return false; if (y+Hgt<=rTarget.y) return false; if (y>=rTarget.y+rTarget.Hgt) return false; return true; } void C4Rect::Intersect(const C4Rect &r2) { // Narrow bounds if (r2.x > x) if (r2.x + r2.Wdt < x + Wdt) { x = r2.x; Wdt = r2.Wdt; } else { Wdt -= (r2.x - x); x = r2.x; } else if (r2.x + r2.Wdt < x + Wdt) Wdt = r2.x + r2.Wdt - x; if (r2.y > y) if (r2.y + r2.Hgt < y + Hgt) { y = r2.y; Hgt = r2.Hgt; } else { Hgt -= (r2.y - y); y = r2.y; } else if (r2.y + r2.Hgt < y + Hgt) Hgt = r2.y + r2.Hgt - y; // Degenerated? Will happen when the two rects don't overlap if (Wdt < 0) Wdt = 0; if (Hgt < 0) Hgt = 0; } bool C4Rect::IntersectsLine(int32_t iX, int32_t iY, int32_t iX2, int32_t iY2) { // Easy cases first if (Contains(iX, iY)) return true; if (Contains(iX2, iY2)) return true; if (iX < x && iX2 < x) return false; if (iY < y && iY2 < y) return false; if (iX >= x+Wdt && iX2 >= x+Wdt) return false; if (iY >= y+Hgt && iY2 >= y+Hgt) return false; // check some special cases if (iX == iX2 || iY == iY2) return true; // Check intersection left/right int32_t iXI, iYI; iXI = (iX < x ? x : x+Wdt); iYI = iY + (iY2 - iY) * (iXI - iX) / (iX2 - iX); if (iYI >= y && iYI < y+Hgt) return true; // Check intersection up/down iYI = (iY < y ? y : y+Hgt); iXI = iX + (iX2 - iX) * (iYI - iY) / (iY2 - iY); return iXI >= x && iXI < x+Wdt; } void C4Rect::Add(const C4Rect &r2) { // Null? Don't do anything if (!r2.Wdt || !r2.Hgt) return; if (!Wdt || !Hgt) { *this = r2; return; } // Expand bounds if (r2.x < x) if (r2.x + r2.Wdt > x + Wdt) { x = r2.x; Wdt = r2.Wdt; } else { Wdt += (x - r2.x); x = r2.x; } else if (r2.x + r2.Wdt > x + Wdt) Wdt = r2.x + r2.Wdt - x; if (r2.y < y) if (r2.y + r2.Hgt > y + Hgt) { y = r2.y; Hgt = r2.Hgt; } else { Hgt += (y - r2.y); y = r2.y; } else if (r2.y + r2.Hgt > y + Hgt) Hgt = r2.y + r2.Hgt - y; } // ---- C4RectList ---- void C4RectList::ClipByRect(const C4Rect &rClip) { // split up all rectangles for (size_t i = 0; i < GetCount(); ++i) { C4Rect *pTarget = &Get(i); // any overlap? if (rClip.x+rClip.Wdt <= pTarget->x) continue; if (rClip.y+rClip.Hgt <= pTarget->y) continue; if (rClip.x >= pTarget->x+pTarget->Wdt) continue; if (rClip.y >= pTarget->y+pTarget->Hgt) continue; // okay; split up rectangle // first split will just reduce the target rectangle size // if more splits are done, additional rectangles need to be added int32_t iSplitCount = 0, iOver; C4Rect rcThis(*pTarget); // clipped by right side if ((iOver=rcThis.x+rcThis.Wdt-rClip.x-rClip.Wdt)>0) { pTarget->x += pTarget->Wdt - iOver; pTarget->Wdt = iOver; rcThis.Wdt -= iOver; ++iSplitCount; } // clipped by obttom side if ((iOver=rcThis.y+rcThis.Hgt-rClip.y-rClip.Hgt)>0) { if (iSplitCount) { AddRect(rcThis); pTarget = &Get(GetCount()-1); } pTarget->y += pTarget->Hgt - iOver; pTarget->Hgt = iOver; rcThis.Hgt -= iOver; ++iSplitCount; } // clipped by left side if ((iOver=rClip.x-rcThis.x)>0) { if (iSplitCount) { AddRect(rcThis); pTarget = &Get(GetCount()-1); } pTarget->Wdt = iOver; rcThis.Wdt -= iOver; rcThis.x = rClip.x; ++iSplitCount; } // clipped by top side if ((iOver=rClip.y-rcThis.y)>0) { if (iSplitCount) { AddRect(rcThis); pTarget = &Get(GetCount()-1); } else ++iSplitCount; pTarget->Hgt = iOver; /* rcThis.Hgt -= iOver; rcThis.y = rClip.y; not needed, since rcThis is no longer used */ } // nothing split? This means this rectnagle is completely contained if (!iSplitCount) { // make it vanish RemoveIndexedRect(i); --i; } } // concat rectangles if possible bool fDone = false; while (!fDone) { fDone=true; for (size_t i = 0, cnt=GetCount(); i < cnt && fDone; ++i) { C4Rect &rc1 = Get(i); for (size_t j = i+1; j < cnt; ++j) { C4Rect &rc2 = Get(j); if (rc1.y == rc2.y && rc1.Hgt == rc2.Hgt) { if (rc1.x + rc1.Wdt == rc2.x) { rc1.Wdt += rc2.Wdt; RemoveIndexedRect(j); fDone=false; break; } else if (rc2.x + rc2.Wdt == rc1.x) { rc2.Wdt += rc1.Wdt; RemoveIndexedRect(i); fDone=false; break; } } else if (rc1.x == rc2.x && rc1.Wdt == rc2.Wdt) { if (rc1.y + rc1.Hgt == rc2.y) { rc1.Hgt += rc2.Hgt; RemoveIndexedRect(j); fDone=false; break; } else if (rc2.y + rc2.Hgt == rc1.y) { rc2.Hgt += rc1.Hgt; RemoveIndexedRect(i); fDone=false; break; } } } } } }