Initial re-upload of spice2x-24-08-24
This commit is contained in:
643
external/layeredfs/3rd_party/GuillotineBinPack.cpp
vendored
Normal file
643
external/layeredfs/3rd_party/GuillotineBinPack.cpp
vendored
Normal file
@@ -0,0 +1,643 @@
|
||||
/** @file GuillotineBinPack.cpp
|
||||
@author Jukka Jylänki
|
||||
|
||||
@brief Implements different bin packer algorithms that use the GUILLOTINE data structure.
|
||||
|
||||
This work is released to Public Domain, do whatever you want with it.
|
||||
*/
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
#include "GuillotineBinPack.h"
|
||||
|
||||
#pragma warning( disable : 4267 )
|
||||
|
||||
namespace rbp {
|
||||
|
||||
using namespace std;
|
||||
|
||||
GuillotineBinPack::GuillotineBinPack()
|
||||
:binWidth(0),
|
||||
binHeight(0)
|
||||
{
|
||||
}
|
||||
|
||||
GuillotineBinPack::GuillotineBinPack(int width, int height)
|
||||
{
|
||||
Init(width, height);
|
||||
}
|
||||
|
||||
void GuillotineBinPack::Init(int width, int height)
|
||||
{
|
||||
binWidth = width;
|
||||
binHeight = height;
|
||||
|
||||
#ifdef _DEBUG
|
||||
disjointRects.Clear();
|
||||
#endif
|
||||
|
||||
// Clear any memory of previously packed rectangles.
|
||||
usedRectangles.clear();
|
||||
|
||||
// We start with a single big free rectangle that spans the whole bin.
|
||||
Rect n;
|
||||
n.x = 0;
|
||||
n.y = 0;
|
||||
n.width = width;
|
||||
n.height = height;
|
||||
|
||||
freeRectangles.clear();
|
||||
freeRectangles.push_back(n);
|
||||
}
|
||||
|
||||
void GuillotineBinPack::Insert(std::vector<RectSize> &rects, bool merge,
|
||||
FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod)
|
||||
{
|
||||
// Remember variables about the best packing choice we have made so far during the iteration process.
|
||||
int bestFreeRect = 0;
|
||||
int bestRect = 0;
|
||||
bool bestFlipped = false;
|
||||
|
||||
// Pack rectangles one at a time until we have cleared the rects array of all rectangles.
|
||||
// rects will get destroyed in the process.
|
||||
while(rects.size() > 0)
|
||||
{
|
||||
// Stores the penalty score of the best rectangle placement - bigger=worse, smaller=better.
|
||||
int bestScore = std::numeric_limits<int>::max();
|
||||
|
||||
for(size_t i = 0; i < freeRectangles.size(); ++i)
|
||||
{
|
||||
for(size_t j = 0; j < rects.size(); ++j)
|
||||
{
|
||||
// If this rectangle is a perfect match, we pick it instantly.
|
||||
if (rects[j].width == freeRectangles[i].width && rects[j].height == freeRectangles[i].height)
|
||||
{
|
||||
bestFreeRect = i;
|
||||
bestRect = j;
|
||||
bestFlipped = false;
|
||||
bestScore = std::numeric_limits<int>::min();
|
||||
i = freeRectangles.size(); // Force a jump out of the outer loop as well - we got an instant fit.
|
||||
break;
|
||||
}
|
||||
// If flipping this rectangle is a perfect match, pick that then.
|
||||
/*else if (rects[j].height == freeRectangles[i].width && rects[j].width == freeRectangles[i].height)
|
||||
{
|
||||
bestFreeRect = i;
|
||||
bestRect = j;
|
||||
bestFlipped = true;
|
||||
bestScore = std::numeric_limits<int>::min();
|
||||
i = freeRectangles.size(); // Force a jump out of the outer loop as well - we got an instant fit.
|
||||
break;
|
||||
}*/
|
||||
// Try if we can fit the rectangle upright.
|
||||
else if (rects[j].width <= freeRectangles[i].width && rects[j].height <= freeRectangles[i].height)
|
||||
{
|
||||
int score = ScoreByHeuristic(rects[j].width, rects[j].height, freeRectangles[i], rectChoice);
|
||||
if (score < bestScore)
|
||||
{
|
||||
bestFreeRect = i;
|
||||
bestRect = j;
|
||||
bestFlipped = false;
|
||||
bestScore = score;
|
||||
}
|
||||
}
|
||||
// If not, then perhaps flipping sideways will make it fit?
|
||||
/*else if (rects[j].height <= freeRectangles[i].width && rects[j].width <= freeRectangles[i].height)
|
||||
{
|
||||
int score = ScoreByHeuristic(rects[j].height, rects[j].width, freeRectangles[i], rectChoice);
|
||||
if (score < bestScore)
|
||||
{
|
||||
bestFreeRect = i;
|
||||
bestRect = j;
|
||||
bestFlipped = true;
|
||||
bestScore = score;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't manage to find any rectangle to pack, abort.
|
||||
if (bestScore == std::numeric_limits<int>::max())
|
||||
return;
|
||||
|
||||
// Otherwise, we're good to go and do the actual packing.
|
||||
Rect newNode;
|
||||
newNode.x = freeRectangles[bestFreeRect].x;
|
||||
newNode.y = freeRectangles[bestFreeRect].y;
|
||||
newNode.width = rects[bestRect].width;
|
||||
newNode.height = rects[bestRect].height;
|
||||
|
||||
if (bestFlipped)
|
||||
std::swap(newNode.width, newNode.height);
|
||||
|
||||
// Remove the free space we lost in the bin.
|
||||
SplitFreeRectByHeuristic(freeRectangles[bestFreeRect], newNode, splitMethod);
|
||||
freeRectangles.erase(freeRectangles.begin() + bestFreeRect);
|
||||
|
||||
// Remove the rectangle we just packed from the input list.
|
||||
rects.erase(rects.begin() + bestRect);
|
||||
|
||||
// Perform a Rectangle Merge step if desired.
|
||||
if (merge)
|
||||
MergeFreeList();
|
||||
|
||||
// Remember the new used rectangle.
|
||||
usedRectangles.push_back(newNode);
|
||||
|
||||
// Check that we're really producing correct packings here.
|
||||
debug_assert(disjointRects.Add(newNode) == true);
|
||||
}
|
||||
}
|
||||
|
||||
/// @return True if r fits inside freeRect (possibly rotated).
|
||||
bool Fits(const RectSize &r, const Rect &freeRect)
|
||||
{
|
||||
return (r.width <= freeRect.width && r.height <= freeRect.height) ||
|
||||
(r.height <= freeRect.width && r.width <= freeRect.height);
|
||||
}
|
||||
|
||||
/// @return True if r fits perfectly inside freeRect, i.e. the leftover area is 0.
|
||||
bool FitsPerfectly(const RectSize &r, const Rect &freeRect)
|
||||
{
|
||||
return (r.width == freeRect.width && r.height == freeRect.height) ||
|
||||
(r.height == freeRect.width && r.width == freeRect.height);
|
||||
}
|
||||
|
||||
/*
|
||||
// A helper function for GUILLOTINE-MAXFITTING. Counts how many rectangles fit into the given rectangle
|
||||
// after it has been split.
|
||||
void CountNumFitting(const Rect &freeRect, int width, int height, const std::vector<RectSize> &rects,
|
||||
int usedRectIndex, bool splitHorizontal, int &score1, int &score2)
|
||||
{
|
||||
const int w = freeRect.width - width;
|
||||
const int h = freeRect.height - height;
|
||||
|
||||
Rect bottom;
|
||||
bottom.x = freeRect.x;
|
||||
bottom.y = freeRect.y + height;
|
||||
bottom.height = h;
|
||||
|
||||
Rect right;
|
||||
right.x = freeRect.x + width;
|
||||
right.y = freeRect.y;
|
||||
right.width = w;
|
||||
|
||||
if (splitHorizontal)
|
||||
{
|
||||
bottom.width = freeRect.width;
|
||||
right.height = height;
|
||||
}
|
||||
else // Split vertically
|
||||
{
|
||||
bottom.width = width;
|
||||
right.height = freeRect.height;
|
||||
}
|
||||
|
||||
int fitBottom = 0;
|
||||
int fitRight = 0;
|
||||
for(size_t i = 0; i < rects.size(); ++i)
|
||||
if (i != usedRectIndex)
|
||||
{
|
||||
if (FitsPerfectly(rects[i], bottom))
|
||||
fitBottom |= 0x10000000;
|
||||
if (FitsPerfectly(rects[i], right))
|
||||
fitRight |= 0x10000000;
|
||||
|
||||
if (Fits(rects[i], bottom))
|
||||
++fitBottom;
|
||||
if (Fits(rects[i], right))
|
||||
++fitRight;
|
||||
}
|
||||
|
||||
score1 = min(fitBottom, fitRight);
|
||||
score2 = max(fitBottom, fitRight);
|
||||
}
|
||||
*/
|
||||
/*
|
||||
// Implements GUILLOTINE-MAXFITTING, an experimental heuristic that's really cool but didn't quite work in practice.
|
||||
void GuillotineBinPack::InsertMaxFitting(std::vector<RectSize> &rects, std::vector<Rect> &dst, bool merge,
|
||||
FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod)
|
||||
{
|
||||
dst.clear();
|
||||
int bestRect = 0;
|
||||
bool bestFlipped = false;
|
||||
bool bestSplitHorizontal = false;
|
||||
|
||||
// Pick rectangles one at a time and pack the one that leaves the most choices still open.
|
||||
while(rects.size() > 0 && freeRectangles.size() > 0)
|
||||
{
|
||||
int bestScore1 = -1;
|
||||
int bestScore2 = -1;
|
||||
|
||||
///\todo Different sort predicates.
|
||||
clb::sort::QuickSort(&freeRectangles[0], freeRectangles.size(), CompareRectShortSide);
|
||||
|
||||
Rect &freeRect = freeRectangles[0];
|
||||
|
||||
for(size_t j = 0; j < rects.size(); ++j)
|
||||
{
|
||||
int score1;
|
||||
int score2;
|
||||
|
||||
if (rects[j].width == freeRect.width && rects[j].height == freeRect.height)
|
||||
{
|
||||
bestRect = j;
|
||||
bestFlipped = false;
|
||||
bestScore1 = bestScore2 = std::numeric_limits<int>::max();
|
||||
break;
|
||||
}
|
||||
else if (rects[j].width <= freeRect.width && rects[j].height <= freeRect.height)
|
||||
{
|
||||
CountNumFitting(freeRect, rects[j].width, rects[j].height, rects, j, false, score1, score2);
|
||||
|
||||
if (score1 > bestScore1 || (score1 == bestScore1 && score2 > bestScore2))
|
||||
{
|
||||
bestRect = j;
|
||||
bestScore1 = score1;
|
||||
bestScore2 = score2;
|
||||
bestFlipped = false;
|
||||
bestSplitHorizontal = false;
|
||||
}
|
||||
|
||||
CountNumFitting(freeRect, rects[j].width, rects[j].height, rects, j, true, score1, score2);
|
||||
|
||||
if (score1 > bestScore1 || (score1 == bestScore1 && score2 > bestScore2))
|
||||
{
|
||||
bestRect = j;
|
||||
bestScore1 = score1;
|
||||
bestScore2 = score2;
|
||||
bestFlipped = false;
|
||||
bestSplitHorizontal = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (rects[j].height == freeRect.width && rects[j].width == freeRect.height)
|
||||
{
|
||||
bestRect = j;
|
||||
bestFlipped = true;
|
||||
bestScore1 = bestScore2 = std::numeric_limits<int>::max();
|
||||
break;
|
||||
}
|
||||
else if (rects[j].height <= freeRect.width && rects[j].width <= freeRect.height)
|
||||
{
|
||||
CountNumFitting(freeRect, rects[j].height, rects[j].width, rects, j, false, score1, score2);
|
||||
|
||||
if (score1 > bestScore1 || (score1 == bestScore1 && score2 > bestScore2))
|
||||
{
|
||||
bestRect = j;
|
||||
bestScore1 = score1;
|
||||
bestScore2 = score2;
|
||||
bestFlipped = true;
|
||||
bestSplitHorizontal = false;
|
||||
}
|
||||
|
||||
CountNumFitting(freeRect, rects[j].height, rects[j].width, rects, j, true, score1, score2);
|
||||
|
||||
if (score1 > bestScore1 || (score1 == bestScore1 && score2 > bestScore2))
|
||||
{
|
||||
bestRect = j;
|
||||
bestScore1 = score1;
|
||||
bestScore2 = score2;
|
||||
bestFlipped = true;
|
||||
bestSplitHorizontal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestScore1 >= 0)
|
||||
{
|
||||
Rect newNode;
|
||||
newNode.x = freeRect.x;
|
||||
newNode.y = freeRect.y;
|
||||
newNode.width = rects[bestRect].width;
|
||||
newNode.height = rects[bestRect].height;
|
||||
if (bestFlipped)
|
||||
std::swap(newNode.width, newNode.height);
|
||||
|
||||
assert(disjointRects.Disjoint(newNode));
|
||||
SplitFreeRectAlongAxis(freeRect, newNode, bestSplitHorizontal);
|
||||
|
||||
rects.erase(rects.begin() + bestRect);
|
||||
|
||||
if (merge)
|
||||
MergeFreeList();
|
||||
|
||||
usedRectangles.push_back(newNode);
|
||||
#ifdef _DEBUG
|
||||
disjointRects.Add(newNode);
|
||||
#endif
|
||||
}
|
||||
|
||||
freeRectangles.erase(freeRectangles.begin());
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Rect GuillotineBinPack::Insert(int width, int height, bool merge, FreeRectChoiceHeuristic rectChoice,
|
||||
GuillotineSplitHeuristic splitMethod)
|
||||
{
|
||||
// Find where to put the new rectangle.
|
||||
int freeNodeIndex = 0;
|
||||
Rect newRect = FindPositionForNewNode(width, height, rectChoice, &freeNodeIndex);
|
||||
|
||||
// Abort if we didn't have enough space in the bin.
|
||||
if (newRect.height == 0)
|
||||
return newRect;
|
||||
|
||||
// Remove the space that was just consumed by the new rectangle.
|
||||
SplitFreeRectByHeuristic(freeRectangles[freeNodeIndex], newRect, splitMethod);
|
||||
freeRectangles.erase(freeRectangles.begin() + freeNodeIndex);
|
||||
|
||||
// Perform a Rectangle Merge step if desired.
|
||||
if (merge)
|
||||
MergeFreeList();
|
||||
|
||||
// Remember the new used rectangle.
|
||||
usedRectangles.push_back(newRect);
|
||||
|
||||
// Check that we're really producing correct packings here.
|
||||
debug_assert(disjointRects.Add(newRect) == true);
|
||||
|
||||
return newRect;
|
||||
}
|
||||
|
||||
/// Computes the ratio of used surface area to the total bin area.
|
||||
float GuillotineBinPack::Occupancy() const
|
||||
{
|
||||
///\todo The occupancy rate could be cached/tracked incrementally instead
|
||||
/// of looping through the list of packed rectangles here.
|
||||
unsigned long usedSurfaceArea = 0;
|
||||
for(size_t i = 0; i < usedRectangles.size(); ++i)
|
||||
usedSurfaceArea += usedRectangles[i].width * usedRectangles[i].height;
|
||||
|
||||
return (float)usedSurfaceArea / (binWidth * binHeight);
|
||||
}
|
||||
|
||||
/// Returns the heuristic score value for placing a rectangle of size width*height into freeRect. Does not try to rotate.
|
||||
int GuillotineBinPack::ScoreByHeuristic(int width, int height, const Rect &freeRect, FreeRectChoiceHeuristic rectChoice)
|
||||
{
|
||||
switch(rectChoice)
|
||||
{
|
||||
case RectBestAreaFit: return ScoreBestAreaFit(width, height, freeRect);
|
||||
case RectBestShortSideFit: return ScoreBestShortSideFit(width, height, freeRect);
|
||||
case RectBestLongSideFit: return ScoreBestLongSideFit(width, height, freeRect);
|
||||
case RectWorstAreaFit: return ScoreWorstAreaFit(width, height, freeRect);
|
||||
case RectWorstShortSideFit: return ScoreWorstShortSideFit(width, height, freeRect);
|
||||
case RectWorstLongSideFit: return ScoreWorstLongSideFit(width, height, freeRect);
|
||||
default: assert(false); return std::numeric_limits<int>::max();
|
||||
}
|
||||
}
|
||||
|
||||
int GuillotineBinPack::ScoreBestAreaFit(int width, int height, const Rect &freeRect)
|
||||
{
|
||||
return freeRect.width * freeRect.height - width * height;
|
||||
}
|
||||
|
||||
int GuillotineBinPack::ScoreBestShortSideFit(int width, int height, const Rect &freeRect)
|
||||
{
|
||||
int leftoverHoriz = abs(freeRect.width - width);
|
||||
int leftoverVert = abs(freeRect.height - height);
|
||||
int leftover = min(leftoverHoriz, leftoverVert);
|
||||
return leftover;
|
||||
}
|
||||
|
||||
int GuillotineBinPack::ScoreBestLongSideFit(int width, int height, const Rect &freeRect)
|
||||
{
|
||||
int leftoverHoriz = abs(freeRect.width - width);
|
||||
int leftoverVert = abs(freeRect.height - height);
|
||||
int leftover = max(leftoverHoriz, leftoverVert);
|
||||
return leftover;
|
||||
}
|
||||
|
||||
int GuillotineBinPack::ScoreWorstAreaFit(int width, int height, const Rect &freeRect)
|
||||
{
|
||||
return -ScoreBestAreaFit(width, height, freeRect);
|
||||
}
|
||||
|
||||
int GuillotineBinPack::ScoreWorstShortSideFit(int width, int height, const Rect &freeRect)
|
||||
{
|
||||
return -ScoreBestShortSideFit(width, height, freeRect);
|
||||
}
|
||||
|
||||
int GuillotineBinPack::ScoreWorstLongSideFit(int width, int height, const Rect &freeRect)
|
||||
{
|
||||
return -ScoreBestLongSideFit(width, height, freeRect);
|
||||
}
|
||||
|
||||
Rect GuillotineBinPack::FindPositionForNewNode(int width, int height, FreeRectChoiceHeuristic rectChoice, int *nodeIndex)
|
||||
{
|
||||
Rect bestNode;
|
||||
memset(&bestNode, 0, sizeof(Rect));
|
||||
|
||||
int bestScore = std::numeric_limits<int>::max();
|
||||
|
||||
/// Try each free rectangle to find the best one for placement.
|
||||
for(size_t i = 0; i < freeRectangles.size(); ++i)
|
||||
{
|
||||
// If this is a perfect fit upright, choose it immediately.
|
||||
if (width == freeRectangles[i].width && height == freeRectangles[i].height)
|
||||
{
|
||||
bestNode.x = freeRectangles[i].x;
|
||||
bestNode.y = freeRectangles[i].y;
|
||||
bestNode.width = width;
|
||||
bestNode.height = height;
|
||||
bestScore = std::numeric_limits<int>::min();
|
||||
*nodeIndex = i;
|
||||
debug_assert(disjointRects.Disjoint(bestNode));
|
||||
break;
|
||||
}
|
||||
// If this is a perfect fit sideways, choose it.
|
||||
else if (height == freeRectangles[i].width && width == freeRectangles[i].height)
|
||||
{
|
||||
bestNode.x = freeRectangles[i].x;
|
||||
bestNode.y = freeRectangles[i].y;
|
||||
bestNode.width = height;
|
||||
bestNode.height = width;
|
||||
bestScore = std::numeric_limits<int>::min();
|
||||
*nodeIndex = i;
|
||||
debug_assert(disjointRects.Disjoint(bestNode));
|
||||
break;
|
||||
}
|
||||
// Does the rectangle fit upright?
|
||||
else if (width <= freeRectangles[i].width && height <= freeRectangles[i].height)
|
||||
{
|
||||
int score = ScoreByHeuristic(width, height, freeRectangles[i], rectChoice);
|
||||
|
||||
if (score < bestScore)
|
||||
{
|
||||
bestNode.x = freeRectangles[i].x;
|
||||
bestNode.y = freeRectangles[i].y;
|
||||
bestNode.width = width;
|
||||
bestNode.height = height;
|
||||
bestScore = score;
|
||||
*nodeIndex = i;
|
||||
debug_assert(disjointRects.Disjoint(bestNode));
|
||||
}
|
||||
}
|
||||
// Does the rectangle fit sideways?
|
||||
else if (height <= freeRectangles[i].width && width <= freeRectangles[i].height)
|
||||
{
|
||||
int score = ScoreByHeuristic(height, width, freeRectangles[i], rectChoice);
|
||||
|
||||
if (score < bestScore)
|
||||
{
|
||||
bestNode.x = freeRectangles[i].x;
|
||||
bestNode.y = freeRectangles[i].y;
|
||||
bestNode.width = height;
|
||||
bestNode.height = width;
|
||||
bestScore = score;
|
||||
*nodeIndex = i;
|
||||
debug_assert(disjointRects.Disjoint(bestNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
return bestNode;
|
||||
}
|
||||
|
||||
void GuillotineBinPack::SplitFreeRectByHeuristic(const Rect &freeRect, const Rect &placedRect, GuillotineSplitHeuristic method)
|
||||
{
|
||||
// Compute the lengths of the leftover area.
|
||||
const int w = freeRect.width - placedRect.width;
|
||||
const int h = freeRect.height - placedRect.height;
|
||||
|
||||
// Placing placedRect into freeRect results in an L-shaped free area, which must be split into
|
||||
// two disjoint rectangles. This can be achieved with by splitting the L-shape using a single line.
|
||||
// We have two choices: horizontal or vertical.
|
||||
|
||||
// Use the given heuristic to decide which choice to make.
|
||||
|
||||
bool splitHorizontal;
|
||||
switch(method)
|
||||
{
|
||||
case SplitShorterLeftoverAxis:
|
||||
// Split along the shorter leftover axis.
|
||||
splitHorizontal = (w <= h);
|
||||
break;
|
||||
case SplitLongerLeftoverAxis:
|
||||
// Split along the longer leftover axis.
|
||||
splitHorizontal = (w > h);
|
||||
break;
|
||||
case SplitMinimizeArea:
|
||||
// Maximize the larger area == minimize the smaller area.
|
||||
// Tries to make the single bigger rectangle.
|
||||
splitHorizontal = (placedRect.width * h > w * placedRect.height);
|
||||
break;
|
||||
case SplitMaximizeArea:
|
||||
// Maximize the smaller area == minimize the larger area.
|
||||
// Tries to make the rectangles more even-sized.
|
||||
splitHorizontal = (placedRect.width * h <= w * placedRect.height);
|
||||
break;
|
||||
case SplitShorterAxis:
|
||||
// Split along the shorter total axis.
|
||||
splitHorizontal = (freeRect.width <= freeRect.height);
|
||||
break;
|
||||
case SplitLongerAxis:
|
||||
// Split along the longer total axis.
|
||||
splitHorizontal = (freeRect.width > freeRect.height);
|
||||
break;
|
||||
default:
|
||||
splitHorizontal = true;
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Perform the actual split.
|
||||
SplitFreeRectAlongAxis(freeRect, placedRect, splitHorizontal);
|
||||
}
|
||||
|
||||
/// This function will add the two generated rectangles into the freeRectangles array. The caller is expected to
|
||||
/// remove the original rectangle from the freeRectangles array after that.
|
||||
void GuillotineBinPack::SplitFreeRectAlongAxis(const Rect &freeRect, const Rect &placedRect, bool splitHorizontal)
|
||||
{
|
||||
// Form the two new rectangles.
|
||||
Rect bottom;
|
||||
bottom.x = freeRect.x;
|
||||
bottom.y = freeRect.y + placedRect.height;
|
||||
bottom.height = freeRect.height - placedRect.height;
|
||||
|
||||
Rect right;
|
||||
right.x = freeRect.x + placedRect.width;
|
||||
right.y = freeRect.y;
|
||||
right.width = freeRect.width - placedRect.width;
|
||||
|
||||
if (splitHorizontal)
|
||||
{
|
||||
bottom.width = freeRect.width;
|
||||
right.height = placedRect.height;
|
||||
}
|
||||
else // Split vertically
|
||||
{
|
||||
bottom.width = placedRect.width;
|
||||
right.height = freeRect.height;
|
||||
}
|
||||
|
||||
// Add the new rectangles into the free rectangle pool if they weren't degenerate.
|
||||
if (bottom.width > 0 && bottom.height > 0)
|
||||
freeRectangles.push_back(bottom);
|
||||
if (right.width > 0 && right.height > 0)
|
||||
freeRectangles.push_back(right);
|
||||
|
||||
debug_assert(disjointRects.Disjoint(bottom));
|
||||
debug_assert(disjointRects.Disjoint(right));
|
||||
}
|
||||
|
||||
void GuillotineBinPack::MergeFreeList()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
DisjointRectCollection test;
|
||||
for(size_t i = 0; i < freeRectangles.size(); ++i)
|
||||
assert(test.Add(freeRectangles[i]) == true);
|
||||
#endif
|
||||
|
||||
// Do a Theta(n^2) loop to see if any pair of free rectangles could me merged into one.
|
||||
// Note that we miss any opportunities to merge three rectangles into one. (should call this function again to detect that)
|
||||
for(size_t i = 0; i < freeRectangles.size(); ++i)
|
||||
for(size_t j = i+1; j < freeRectangles.size(); ++j)
|
||||
{
|
||||
if (freeRectangles[i].width == freeRectangles[j].width && freeRectangles[i].x == freeRectangles[j].x)
|
||||
{
|
||||
if (freeRectangles[i].y == freeRectangles[j].y + freeRectangles[j].height)
|
||||
{
|
||||
freeRectangles[i].y -= freeRectangles[j].height;
|
||||
freeRectangles[i].height += freeRectangles[j].height;
|
||||
freeRectangles.erase(freeRectangles.begin() + j);
|
||||
--j;
|
||||
}
|
||||
else if (freeRectangles[i].y + freeRectangles[i].height == freeRectangles[j].y)
|
||||
{
|
||||
freeRectangles[i].height += freeRectangles[j].height;
|
||||
freeRectangles.erase(freeRectangles.begin() + j);
|
||||
--j;
|
||||
}
|
||||
}
|
||||
else if (freeRectangles[i].height == freeRectangles[j].height && freeRectangles[i].y == freeRectangles[j].y)
|
||||
{
|
||||
if (freeRectangles[i].x == freeRectangles[j].x + freeRectangles[j].width)
|
||||
{
|
||||
freeRectangles[i].x -= freeRectangles[j].width;
|
||||
freeRectangles[i].width += freeRectangles[j].width;
|
||||
freeRectangles.erase(freeRectangles.begin() + j);
|
||||
--j;
|
||||
}
|
||||
else if (freeRectangles[i].x + freeRectangles[i].width == freeRectangles[j].x)
|
||||
{
|
||||
freeRectangles[i].width += freeRectangles[j].width;
|
||||
freeRectangles.erase(freeRectangles.begin() + j);
|
||||
--j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
test.Clear();
|
||||
for(size_t i = 0; i < freeRectangles.size(); ++i)
|
||||
assert(test.Add(freeRectangles[i]) == true);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
134
external/layeredfs/3rd_party/GuillotineBinPack.h
vendored
Normal file
134
external/layeredfs/3rd_party/GuillotineBinPack.h
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
/** @file GuillotineBinPack.h
|
||||
@author Jukka Jylänki
|
||||
|
||||
@brief Implements different bin packer algorithms that use the GUILLOTINE data structure.
|
||||
|
||||
This work is released to Public Domain, do whatever you want with it.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Rect.h"
|
||||
|
||||
namespace rbp {
|
||||
|
||||
/** GuillotineBinPack implements different variants of bin packer algorithms that use the GUILLOTINE data structure
|
||||
to keep track of the free space of the bin where rectangles may be placed. */
|
||||
class GuillotineBinPack
|
||||
{
|
||||
public:
|
||||
/// The initial bin size will be (0,0). Call Init to set the bin size.
|
||||
GuillotineBinPack();
|
||||
|
||||
/// Initializes a new bin of the given size.
|
||||
GuillotineBinPack(int width, int height);
|
||||
|
||||
/// (Re)initializes the packer to an empty bin of width x height units. Call whenever
|
||||
/// you need to restart with a new bin.
|
||||
void Init(int width, int height);
|
||||
|
||||
/// Specifies the different choice heuristics that can be used when deciding which of the free subrectangles
|
||||
/// to place the to-be-packed rectangle into.
|
||||
enum FreeRectChoiceHeuristic
|
||||
{
|
||||
RectBestAreaFit, ///< -BAF
|
||||
RectBestShortSideFit, ///< -BSSF
|
||||
RectBestLongSideFit, ///< -BLSF
|
||||
RectWorstAreaFit, ///< -WAF
|
||||
RectWorstShortSideFit, ///< -WSSF
|
||||
RectWorstLongSideFit ///< -WLSF
|
||||
};
|
||||
|
||||
/// Specifies the different choice heuristics that can be used when the packer needs to decide whether to
|
||||
/// subdivide the remaining free space in horizontal or vertical direction.
|
||||
enum GuillotineSplitHeuristic
|
||||
{
|
||||
SplitShorterLeftoverAxis, ///< -SLAS
|
||||
SplitLongerLeftoverAxis, ///< -LLAS
|
||||
SplitMinimizeArea, ///< -MINAS, Try to make a single big rectangle at the expense of making the other small.
|
||||
SplitMaximizeArea, ///< -MAXAS, Try to make both remaining rectangles as even-sized as possible.
|
||||
SplitShorterAxis, ///< -SAS
|
||||
SplitLongerAxis ///< -LAS
|
||||
};
|
||||
|
||||
/// Inserts a single rectangle into the bin. The packer might rotate the rectangle, in which case the returned
|
||||
/// struct will have the width and height values swapped.
|
||||
/// @param merge If true, performs free Rectangle Merge procedure after packing the new rectangle. This procedure
|
||||
/// tries to defragment the list of disjoint free rectangles to improve packing performance, but also takes up
|
||||
/// some extra time.
|
||||
/// @param rectChoice The free rectangle choice heuristic rule to use.
|
||||
/// @param splitMethod The free rectangle split heuristic rule to use.
|
||||
Rect Insert(int width, int height, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod);
|
||||
|
||||
/// Inserts a list of rectangles into the bin.
|
||||
/// @param rects The list of rectangles to add. This list will be destroyed in the packing process.
|
||||
/// @param merge If true, performs Rectangle Merge operations during the packing process.
|
||||
/// @param rectChoice The free rectangle choice heuristic rule to use.
|
||||
/// @param splitMethod The free rectangle split heuristic rule to use.
|
||||
void Insert(std::vector<RectSize> &rects, bool merge,
|
||||
FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod);
|
||||
|
||||
// Implements GUILLOTINE-MAXFITTING, an experimental heuristic that's really cool but didn't quite work in practice.
|
||||
// void InsertMaxFitting(std::vector<RectSize> &rects, std::vector<Rect> &dst, bool merge,
|
||||
// FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod);
|
||||
|
||||
/// Computes the ratio of used/total surface area. 0.00 means no space is yet used, 1.00 means the whole bin is used.
|
||||
float Occupancy() const;
|
||||
|
||||
/// Returns the internal list of disjoint rectangles that track the free area of the bin. You may alter this vector
|
||||
/// any way desired, as long as the end result still is a list of disjoint rectangles.
|
||||
std::vector<Rect> &GetFreeRectangles() { return freeRectangles; }
|
||||
|
||||
/// Returns the list of packed rectangles. You may alter this vector at will, for example, you can move a Rect from
|
||||
/// this list to the Free Rectangles list to free up space on-the-fly, but notice that this causes fragmentation.
|
||||
std::vector<Rect> &GetUsedRectangles() { return usedRectangles; }
|
||||
|
||||
/// Performs a Rectangle Merge operation. This procedure looks for adjacent free rectangles and merges them if they
|
||||
/// can be represented with a single rectangle. Takes up Theta(|freeRectangles|^2) time.
|
||||
void MergeFreeList();
|
||||
|
||||
private:
|
||||
int binWidth;
|
||||
int binHeight;
|
||||
|
||||
/// Stores a list of all the rectangles that we have packed so far. This is used only to compute the Occupancy ratio,
|
||||
/// so if you want to have the packer consume less memory, this can be removed.
|
||||
std::vector<Rect> usedRectangles;
|
||||
|
||||
/// Stores a list of rectangles that represents the free area of the bin. This rectangles in this list are disjoint.
|
||||
std::vector<Rect> freeRectangles;
|
||||
|
||||
#ifdef _DEBUG
|
||||
/// Used to track that the packer produces proper packings.
|
||||
DisjointRectCollection disjointRects;
|
||||
#endif
|
||||
|
||||
/// Goes through the list of free rectangles and finds the best one to place a rectangle of given size into.
|
||||
/// Running time is Theta(|freeRectangles|).
|
||||
/// @param nodeIndex [out] The index of the free rectangle in the freeRectangles array into which the new
|
||||
/// rect was placed.
|
||||
/// @return A Rect structure that represents the placement of the new rect into the best free rectangle.
|
||||
Rect FindPositionForNewNode(int width, int height, FreeRectChoiceHeuristic rectChoice, int *nodeIndex);
|
||||
|
||||
static int ScoreByHeuristic(int width, int height, const Rect &freeRect, FreeRectChoiceHeuristic rectChoice);
|
||||
// The following functions compute (penalty) score values if a rect of the given size was placed into the
|
||||
// given free rectangle. In these score values, smaller is better.
|
||||
|
||||
static int ScoreBestAreaFit(int width, int height, const Rect &freeRect);
|
||||
static int ScoreBestShortSideFit(int width, int height, const Rect &freeRect);
|
||||
static int ScoreBestLongSideFit(int width, int height, const Rect &freeRect);
|
||||
|
||||
static int ScoreWorstAreaFit(int width, int height, const Rect &freeRect);
|
||||
static int ScoreWorstShortSideFit(int width, int height, const Rect &freeRect);
|
||||
static int ScoreWorstLongSideFit(int width, int height, const Rect &freeRect);
|
||||
|
||||
/// Splits the given L-shaped free rectangle into two new free rectangles after placedRect has been placed into it.
|
||||
/// Determines the split axis by using the given heuristic.
|
||||
void SplitFreeRectByHeuristic(const Rect &freeRect, const Rect &placedRect, GuillotineSplitHeuristic method);
|
||||
|
||||
/// Splits the given L-shaped free rectangle into two new free rectangles along the given fixed split axis.
|
||||
void SplitFreeRectAlongAxis(const Rect &freeRect, const Rect &placedRect, bool splitHorizontal);
|
||||
};
|
||||
|
||||
}
|
||||
51
external/layeredfs/3rd_party/Rect.cpp
vendored
Normal file
51
external/layeredfs/3rd_party/Rect.cpp
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/** @file Rect.cpp
|
||||
@author Jukka Jylänki
|
||||
|
||||
This work is released to Public Domain, do whatever you want with it.
|
||||
*/
|
||||
#include <utility>
|
||||
|
||||
#include "Rect.h"
|
||||
|
||||
namespace rbp {
|
||||
|
||||
/*
|
||||
#include "clb/Algorithm/Sort.h"
|
||||
|
||||
int CompareRectShortSide(const Rect &a, const Rect &b)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
int smallerSideA = min(a.width, a.height);
|
||||
int smallerSideB = min(b.width, b.height);
|
||||
|
||||
if (smallerSideA != smallerSideB)
|
||||
return clb::sort::TriCmp(smallerSideA, smallerSideB);
|
||||
|
||||
// Tie-break on the larger side.
|
||||
int largerSideA = max(a.width, a.height);
|
||||
int largerSideB = max(b.width, b.height);
|
||||
|
||||
return clb::sort::TriCmp(largerSideA, largerSideB);
|
||||
}
|
||||
*/
|
||||
/*
|
||||
int NodeSortCmp(const Rect &a, const Rect &b)
|
||||
{
|
||||
if (a.x != b.x)
|
||||
return clb::sort::TriCmp(a.x, b.x);
|
||||
if (a.y != b.y)
|
||||
return clb::sort::TriCmp(a.y, b.y);
|
||||
if (a.width != b.width)
|
||||
return clb::sort::TriCmp(a.width, b.width);
|
||||
return clb::sort::TriCmp(a.height, b.height);
|
||||
}
|
||||
*/
|
||||
bool IsContainedIn(const Rect &a, const Rect &b)
|
||||
{
|
||||
return a.x >= b.x && a.y >= b.y
|
||||
&& a.x+a.width <= b.x+b.width
|
||||
&& a.y+a.height <= b.y+b.height;
|
||||
}
|
||||
|
||||
}
|
||||
94
external/layeredfs/3rd_party/Rect.h
vendored
Normal file
94
external/layeredfs/3rd_party/Rect.h
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
/** @file Rect.h
|
||||
@author Jukka Jylänki
|
||||
|
||||
This work is released to Public Domain, do whatever you want with it.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef _DEBUG
|
||||
/// debug_assert is an assert that also requires debug mode to be defined.
|
||||
#define debug_assert(x) assert(x)
|
||||
#else
|
||||
#define debug_assert(x)
|
||||
#endif
|
||||
|
||||
//using namespace std;
|
||||
|
||||
namespace rbp {
|
||||
|
||||
struct RectSize
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct Rect
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
/// Performs a lexicographic compare on (rect short side, rect long side).
|
||||
/// @return -1 if the smaller side of a is shorter than the smaller side of b, 1 if the other way around.
|
||||
/// If they are equal, the larger side length is used as a tie-breaker.
|
||||
/// If the rectangles are of same size, returns 0.
|
||||
int CompareRectShortSide(const Rect &a, const Rect &b);
|
||||
|
||||
/// Performs a lexicographic compare on (x, y, width, height).
|
||||
int NodeSortCmp(const Rect &a, const Rect &b);
|
||||
|
||||
/// Returns true if a is contained in b.
|
||||
bool IsContainedIn(const Rect &a, const Rect &b);
|
||||
|
||||
class DisjointRectCollection
|
||||
{
|
||||
public:
|
||||
std::vector<Rect> rects;
|
||||
|
||||
bool Add(const Rect &r)
|
||||
{
|
||||
// Degenerate rectangles are ignored.
|
||||
if (r.width == 0 || r.height == 0)
|
||||
return true;
|
||||
|
||||
if (!Disjoint(r))
|
||||
return false;
|
||||
rects.push_back(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
rects.clear();
|
||||
}
|
||||
|
||||
bool Disjoint(const Rect &r) const
|
||||
{
|
||||
// Degenerate rectangles are ignored.
|
||||
if (r.width == 0 || r.height == 0)
|
||||
return true;
|
||||
|
||||
for(size_t i = 0; i < rects.size(); ++i)
|
||||
if (!Disjoint(rects[i], r))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Disjoint(const Rect &a, const Rect &b)
|
||||
{
|
||||
if (a.x + a.width <= b.x ||
|
||||
b.x + b.width <= a.x ||
|
||||
a.y + a.height <= b.y ||
|
||||
b.y + b.height <= a.y)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
6488
external/layeredfs/3rd_party/lodepng.cpp
vendored
Normal file
6488
external/layeredfs/3rd_party/lodepng.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2020
external/layeredfs/3rd_party/lodepng.h
vendored
Normal file
2020
external/layeredfs/3rd_party/lodepng.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2596
external/layeredfs/3rd_party/rapidxml.hpp
vendored
Normal file
2596
external/layeredfs/3rd_party/rapidxml.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
446
external/layeredfs/3rd_party/rapidxml_print.hpp
vendored
Normal file
446
external/layeredfs/3rd_party/rapidxml_print.hpp
vendored
Normal file
@@ -0,0 +1,446 @@
|
||||
#ifndef RAPIDXML_PRINT_HPP_INCLUDED
|
||||
#define RAPIDXML_PRINT_HPP_INCLUDED
|
||||
|
||||
// Copyright (C) 2006, 2009 Marcin Kalicinski
|
||||
// Version 1.13
|
||||
// Revision $DateTime: 2009/05/13 01:46:17 $
|
||||
//! \file rapidxml_print.hpp This file contains rapidxml printer implementation
|
||||
|
||||
#include "rapidxml.hpp"
|
||||
|
||||
// Only include streams if not disabled
|
||||
#ifndef RAPIDXML_NO_STREAMS
|
||||
#include <ostream>
|
||||
#include <iterator>
|
||||
#endif
|
||||
|
||||
namespace rapidxml
|
||||
{
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Printing flags
|
||||
|
||||
const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Internal
|
||||
|
||||
//! \cond internal
|
||||
namespace internal
|
||||
{
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Internal character operations
|
||||
|
||||
// Copy characters from given range to given output iterator
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out)
|
||||
{
|
||||
while (begin != end)
|
||||
*out++ = *begin++;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Copy characters from given range to given output iterator and expand
|
||||
// characters into references (< > ' " &)
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out)
|
||||
{
|
||||
while (begin != end)
|
||||
{
|
||||
if (*begin == noexpand)
|
||||
{
|
||||
*out++ = *begin; // No expansion, copy character
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (*begin)
|
||||
{
|
||||
case Ch('<'):
|
||||
*out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';');
|
||||
break;
|
||||
case Ch('>'):
|
||||
*out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';');
|
||||
break;
|
||||
case Ch('\''):
|
||||
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';');
|
||||
break;
|
||||
case Ch('"'):
|
||||
*out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';');
|
||||
break;
|
||||
case Ch('&'):
|
||||
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';');
|
||||
break;
|
||||
default:
|
||||
*out++ = *begin; // No expansion, copy character
|
||||
}
|
||||
}
|
||||
++begin; // Step to next character
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Fill given output iterator with repetitions of the same character
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt fill_chars(OutIt out, int n, Ch ch)
|
||||
{
|
||||
for (int i = 0; i < n; ++i)
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Find character
|
||||
template<class Ch, Ch ch>
|
||||
inline bool find_char(const Ch *begin, const Ch *end)
|
||||
{
|
||||
while (begin != end)
|
||||
if (*begin++ == ch)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Internal printing operations
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags);
|
||||
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||
// Print node
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||
{
|
||||
// Print proper node type
|
||||
switch (node->type())
|
||||
{
|
||||
|
||||
// Document
|
||||
case node_document:
|
||||
out = print_children(out, node, flags, indent);
|
||||
break;
|
||||
|
||||
// Element
|
||||
case node_element:
|
||||
out = print_element_node(out, node, flags, indent);
|
||||
break;
|
||||
|
||||
// Data
|
||||
case node_data:
|
||||
out = print_data_node(out, node, flags, indent);
|
||||
break;
|
||||
|
||||
// CDATA
|
||||
case node_cdata:
|
||||
out = print_cdata_node(out, node, flags, indent);
|
||||
break;
|
||||
|
||||
// Declaration
|
||||
case node_declaration:
|
||||
out = print_declaration_node(out, node, flags, indent);
|
||||
break;
|
||||
|
||||
// Comment
|
||||
case node_comment:
|
||||
out = print_comment_node(out, node, flags, indent);
|
||||
break;
|
||||
|
||||
// Doctype
|
||||
case node_doctype:
|
||||
out = print_doctype_node(out, node, flags, indent);
|
||||
break;
|
||||
|
||||
// Pi
|
||||
case node_pi:
|
||||
out = print_pi_node(out, node, flags, indent);
|
||||
break;
|
||||
|
||||
// Unknown
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
// If indenting not disabled, add line break after node
|
||||
if (!(flags & print_no_indenting))
|
||||
*out = Ch('\n'), ++out;
|
||||
|
||||
// Return modified iterator
|
||||
return out;
|
||||
}
|
||||
|
||||
// Print children of the node
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||
{
|
||||
for (xml_node<Ch> *child = node->first_node(); child; child = child->next_sibling())
|
||||
out = print_node(out, child, flags, indent);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Print attributes of the node
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags)
|
||||
{
|
||||
for (xml_attribute<Ch> *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute())
|
||||
{
|
||||
if (attribute->name() && attribute->value())
|
||||
{
|
||||
// Print attribute name
|
||||
*out = Ch(' '), ++out;
|
||||
out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out);
|
||||
*out = Ch('='), ++out;
|
||||
// Print attribute value using appropriate quote type
|
||||
if (find_char<Ch, Ch('"')>(attribute->value(), attribute->value() + attribute->value_size()))
|
||||
{
|
||||
*out = Ch('\''), ++out;
|
||||
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out);
|
||||
*out = Ch('\''), ++out;
|
||||
}
|
||||
else
|
||||
{
|
||||
*out = Ch('"'), ++out;
|
||||
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out);
|
||||
*out = Ch('"'), ++out;
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Print data node
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||
{
|
||||
assert(node->type() == node_data);
|
||||
if (!(flags & print_no_indenting))
|
||||
out = fill_chars(out, indent, Ch('\t'));
|
||||
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Print data node
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||
{
|
||||
assert(node->type() == node_cdata);
|
||||
if (!(flags & print_no_indenting))
|
||||
out = fill_chars(out, indent, Ch('\t'));
|
||||
*out = Ch('<'); ++out;
|
||||
*out = Ch('!'); ++out;
|
||||
*out = Ch('['); ++out;
|
||||
*out = Ch('C'); ++out;
|
||||
*out = Ch('D'); ++out;
|
||||
*out = Ch('A'); ++out;
|
||||
*out = Ch('T'); ++out;
|
||||
*out = Ch('A'); ++out;
|
||||
*out = Ch('['); ++out;
|
||||
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
||||
*out = Ch(']'); ++out;
|
||||
*out = Ch(']'); ++out;
|
||||
*out = Ch('>'); ++out;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Print element node
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||
{
|
||||
assert(node->type() == node_element);
|
||||
|
||||
// Print element name and attributes, if any
|
||||
if (!(flags & print_no_indenting))
|
||||
out = fill_chars(out, indent, Ch('\t'));
|
||||
*out = Ch('<'), ++out;
|
||||
out = copy_chars(node->name(), node->name() + node->name_size(), out);
|
||||
out = print_attributes(out, node, flags);
|
||||
|
||||
// If node is childless
|
||||
if (node->value_size() == 0 && !node->first_node())
|
||||
{
|
||||
// Print childless node tag ending
|
||||
*out = Ch('/'), ++out;
|
||||
*out = Ch('>'), ++out;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Print normal node tag ending
|
||||
*out = Ch('>'), ++out;
|
||||
|
||||
// Test if node contains a single data node only (and no other nodes)
|
||||
xml_node<Ch> *child = node->first_node();
|
||||
if (!child)
|
||||
{
|
||||
// If node has no children, only print its value without indenting
|
||||
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
|
||||
}
|
||||
else if (child->next_sibling() == 0 && child->type() == node_data)
|
||||
{
|
||||
// If node has a sole data child, only print its value without indenting
|
||||
out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Print all children with full indenting
|
||||
if (!(flags & print_no_indenting))
|
||||
*out = Ch('\n'), ++out;
|
||||
out = print_children(out, node, flags, indent + 1);
|
||||
if (!(flags & print_no_indenting))
|
||||
out = fill_chars(out, indent, Ch('\t'));
|
||||
}
|
||||
|
||||
// Print node end
|
||||
*out = Ch('<'), ++out;
|
||||
*out = Ch('/'), ++out;
|
||||
out = copy_chars(node->name(), node->name() + node->name_size(), out);
|
||||
*out = Ch('>'), ++out;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Print declaration node
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||
{
|
||||
// Print declaration start
|
||||
if (!(flags & print_no_indenting))
|
||||
out = fill_chars(out, indent, Ch('\t'));
|
||||
*out = Ch('<'), ++out;
|
||||
*out = Ch('?'), ++out;
|
||||
*out = Ch('x'), ++out;
|
||||
*out = Ch('m'), ++out;
|
||||
*out = Ch('l'), ++out;
|
||||
|
||||
// Print attributes
|
||||
out = print_attributes(out, node, flags);
|
||||
|
||||
// Print declaration end
|
||||
*out = Ch('?'), ++out;
|
||||
*out = Ch('>'), ++out;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Print comment node
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||
{
|
||||
assert(node->type() == node_comment);
|
||||
if (!(flags & print_no_indenting))
|
||||
out = fill_chars(out, indent, Ch('\t'));
|
||||
*out = Ch('<'), ++out;
|
||||
*out = Ch('!'), ++out;
|
||||
*out = Ch('-'), ++out;
|
||||
*out = Ch('-'), ++out;
|
||||
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
||||
*out = Ch('-'), ++out;
|
||||
*out = Ch('-'), ++out;
|
||||
*out = Ch('>'), ++out;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Print doctype node
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||
{
|
||||
assert(node->type() == node_doctype);
|
||||
if (!(flags & print_no_indenting))
|
||||
out = fill_chars(out, indent, Ch('\t'));
|
||||
*out = Ch('<'), ++out;
|
||||
*out = Ch('!'), ++out;
|
||||
*out = Ch('D'), ++out;
|
||||
*out = Ch('O'), ++out;
|
||||
*out = Ch('C'), ++out;
|
||||
*out = Ch('T'), ++out;
|
||||
*out = Ch('Y'), ++out;
|
||||
*out = Ch('P'), ++out;
|
||||
*out = Ch('E'), ++out;
|
||||
*out = Ch(' '), ++out;
|
||||
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
||||
*out = Ch('>'), ++out;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Print pi node
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||
{
|
||||
assert(node->type() == node_pi);
|
||||
if (!(flags & print_no_indenting))
|
||||
out = fill_chars(out, indent, Ch('\t'));
|
||||
*out = Ch('<'), ++out;
|
||||
*out = Ch('?'), ++out;
|
||||
out = copy_chars(node->name(), node->name() + node->name_size(), out);
|
||||
*out = Ch(' '), ++out;
|
||||
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
||||
*out = Ch('?'), ++out;
|
||||
*out = Ch('>'), ++out;
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
//! \endcond
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Printing
|
||||
|
||||
//! Prints XML to given output iterator.
|
||||
//! \param out Output iterator to print to.
|
||||
//! \param node Node to be printed. Pass xml_document to print entire document.
|
||||
//! \param flags Flags controlling how XML is printed.
|
||||
//! \return Output iterator pointing to position immediately after last character of printed text.
|
||||
template<class OutIt, class Ch>
|
||||
inline OutIt print(OutIt out, const xml_node<Ch> &node, int flags = 0)
|
||||
{
|
||||
return internal::print_node(out, &node, flags, 0);
|
||||
}
|
||||
|
||||
#ifndef RAPIDXML_NO_STREAMS
|
||||
|
||||
//! Prints XML to given output stream.
|
||||
//! \param out Output stream to print to.
|
||||
//! \param node Node to be printed. Pass xml_document to print entire document.
|
||||
//! \param flags Flags controlling how XML is printed.
|
||||
//! \return Output stream.
|
||||
template<class Ch>
|
||||
inline std::basic_ostream<Ch> &print(std::basic_ostream<Ch> &out, const xml_node<Ch> &node, int flags = 0)
|
||||
{
|
||||
print(std::ostream_iterator<Ch>(out), node, flags);
|
||||
return out;
|
||||
}
|
||||
|
||||
//! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.
|
||||
//! \param out Output stream to print to.
|
||||
//! \param node Node to be printed.
|
||||
//! \return Output stream.
|
||||
template<class Ch>
|
||||
inline std::basic_ostream<Ch> &operator <<(std::basic_ostream<Ch> &out, const xml_node<Ch> &node)
|
||||
{
|
||||
return print(out, node);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
37
external/layeredfs/3rd_party/stb_dxt.cpp
vendored
Normal file
37
external/layeredfs/3rd_party/stb_dxt.cpp
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// stb_dxt.cpp - Real-Time DXT1/DXT5 compressor
|
||||
// Based on original by fabian "ryg" giesen v1.04
|
||||
// Custom version, modified by Yann Collet
|
||||
//
|
||||
/*
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
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.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
|
||||
OWNER OR 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.
|
||||
|
||||
You can contact the author at :
|
||||
- RygsDXTc source repository : http://code.google.com/p/rygsdxtc/
|
||||
*/
|
||||
|
||||
|
||||
#define STB_DXT_IMPLEMENTATION
|
||||
#include "stb_dxt.h"
|
||||
1043
external/layeredfs/3rd_party/stb_dxt.h
vendored
Normal file
1043
external/layeredfs/3rd_party/stb_dxt.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user