diff --git a/LICENSE_EXCEPTIONS.md b/LICENSE_EXCEPTIONS.md index fa79e94d..2e992c95 100644 --- a/LICENSE_EXCEPTIONS.md +++ b/LICENSE_EXCEPTIONS.md @@ -646,6 +646,34 @@ They are also licensed under the MIT open source license, if you have lawyers wh Every source file includes an explicit dual-license for you to choose from. + BinPack2D is a 2 dimensional, multi-bin, bin-packer. +----------------------------------------------------------------------------- +neo/libs/binpack2d/* + +Copyright (c) 2013, Christopher Stones < chris.stones@zoho.com > +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. 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. + + Research Samples by Morgan McGuire ----------------------------------------------------------------------------- base/renderprogs/builtin/SSAO/* (AlchemyAO) diff --git a/neo/framework/DeclManager.cpp b/neo/framework/DeclManager.cpp index 6b27f836..70116591 100644 --- a/neo/framework/DeclManager.cpp +++ b/neo/framework/DeclManager.cpp @@ -3442,7 +3442,7 @@ struct Category_t void RectAllocator( const idList& inputSizes, idList& outputPositions, idVec2i& totalSize, const int START_MAX = 16384, const int imageMax = -1 ); float RectPackingFraction( const idList& inputSizes, const idVec2i totalSize ); - +void RectAllocatorBinPack2D( const idList& inputSizes, const idStrList& inputNames, idList& outputPositions, idVec2i& totalSize, const int START_MAX ); // uses BFG Rectangle Atlas packer to pack models in 3D @@ -3725,12 +3725,16 @@ void idDeclManagerLocal::MakeZooMapForModels_f( const idCmdArgs& args ) fileSystem->FreeFileList( files ); + // BinPack2D is better, more efficient and can with all really big models but looks worse than the simple rectangle packer + const bool useBinpack2D = false; + // pack models by 2D AABB inside of a folder for( int g = 0; g < entitiesPerFolder.Num(); g++ ) { ModelsGroup_t* group = *entitiesPerFolder.GetIndex( g ); idList inputSizes; + idStrList inputNames; inputSizes.SetNum( group->entityList.Num() ); for( int e = 0; e < group->entityList.Num(); e++ ) { @@ -3743,12 +3747,20 @@ void idDeclManagerLocal::MakeZooMapForModels_f( const idCmdArgs& args ) //idLib::Printf( "model size %ix%i in '%s'\n", allocSize.x, allocSize.y, group->folder.c_str() ); inputSizes[ e ] = allocSize; + inputNames.Append( entInfo->entity->epairs.GetString( "origmodel" ) ); } idList outputPositions; idVec2i totalSize; - RectAllocator( inputSizes, outputPositions, totalSize, 1 << 14 ); + if( useBinpack2D ) + { + RectAllocatorBinPack2D( inputSizes, inputNames, outputPositions, totalSize, 1 << 17 ); + } + else + { + RectAllocator( inputSizes, outputPositions, totalSize, 1 << 14 ); + } group->totalSize = totalSize; @@ -3794,22 +3806,31 @@ void idDeclManagerLocal::MakeZooMapForModels_f( const idCmdArgs& args ) idList inputSizes; inputSizes.SetNum( category->modelGroups.Num() ); + idStrList inputNames; for( int g = 0; g < category->modelGroups.Num(); g++ ) { ModelsGroup_t* group = category->modelGroups[ g ]; - // these are in DXT blocks, not pixels const int offset = 256; idVec2i allocSize( group->totalSize.x + offset, group->totalSize.y + offset ); - idLib::Printf( "folder '%s' size %ix%i\n", group->path.c_str(), allocSize.x, allocSize.y ); + //idLib::Printf( "folder '%s' size %ix%i\n", group->path.c_str(), allocSize.x, allocSize.y ); inputSizes[ g ] = allocSize; + inputNames.Append( group->path ); } idVec2i totalSize; - RectAllocator( inputSizes, category->modelGroupPositions, totalSize, 1 << 14 ); + + if( useBinpack2D ) + { + RectAllocatorBinPack2D( inputSizes, inputNames, category->modelGroupPositions, totalSize, 1 << 17 ); + } + else + { + RectAllocator( inputSizes, category->modelGroupPositions, totalSize, 1 << 14 ); + } category->totalSize = totalSize; } @@ -3817,6 +3838,12 @@ void idDeclManagerLocal::MakeZooMapForModels_f( const idCmdArgs& args ) // pack categories idList inputSizes; inputSizes.SetNum( categories.Num() ); + idStrList inputNames; + + for( int i = 0; i < categories.Num(); i++ ) + { + inputNames.Append( categories[ i ]->tagNames[ 0 ] ); + } idList categoryPositions; idVec2i totalSize; @@ -3833,7 +3860,14 @@ void idDeclManagerLocal::MakeZooMapForModels_f( const idCmdArgs& args ) inputSizes[ i ] = allocSize; // smart allocator - RectAllocator( inputSizes, categoryPositions, totalSize, 1 << 14 ); + if( useBinpack2D ) + { + RectAllocatorBinPack2D( inputSizes, inputNames, categoryPositions, totalSize, 1 << 17 ); + } + else + { + RectAllocator( inputSizes, categoryPositions, totalSize, 1 << 14 ); + } } // place entities inside packed folders diff --git a/neo/idlib/RectAllocator.cpp b/neo/idlib/RectAllocator.cpp index eb69b944..8520edf5 100644 --- a/neo/idlib/RectAllocator.cpp +++ b/neo/idlib/RectAllocator.cpp @@ -29,6 +29,9 @@ If you have questions concerning this license or the applicable additional terms #pragma hdrstop +#include "../libs/binpack2d/binpack2d.h" + + /* This routine performs a tight packing of a list of rectangles, attempting to minimize the area @@ -193,3 +196,117 @@ void RectAllocator( const idList& inputSizes, idList& outputPo } } +// RB +class MyContent +{ +public: + int itemIndex; + idStr str; + MyContent() : str( "default string" ) {} + MyContent( const idStr& str ) : str( str ) {} +}; + +void RectAllocatorBinPack2D( const idList& inputSizes, const idStrList& inputNames, idList& outputPositions, idVec2i& totalSize, const int START_MAX ) +{ + outputPositions.SetNum( inputSizes.Num() ); + + if( inputSizes.Num() == 0 ) + { + totalSize.Set( 0, 0 ); + return; + } + + // Create some 'content' to work on. + BinPack2D::ContentAccumulator inputContent; + + for( int i = 0; i < inputSizes.Num(); i++ ) + { + // random size for this content + int width = inputSizes[ i ].x; + int height = inputSizes[ i ].y; + + // whatever data you want to associate with this content + MyContent mycontent( inputNames[ i ] ); + mycontent.itemIndex = i; + + // Add it + inputContent += BinPack2D::Content( mycontent, BinPack2D::Coord(), BinPack2D::Size( width, height ), false ); + } + + // Sort the input content by size... usually packs better. + inputContent.Sort(); + + // Create some bins! ( 2 bins, 128x128 in this example ) + BinPack2D::CanvasArray canvasArray = + BinPack2D::UniformCanvasArrayBuilder( START_MAX, START_MAX, 2 ).Build(); + + // A place to store content that didnt fit into the canvas array. + BinPack2D::ContentAccumulator remainder; + + // try to pack content into the bins. + bool success = canvasArray.Place( inputContent, remainder ); + + // A place to store packed content. + BinPack2D::ContentAccumulator outputContent; + + // Read all placed content. + canvasArray.CollectContent( outputContent ); + + // parse output. + typedef BinPack2D::Content::Vector::iterator binpack2d_iterator; + //idLib::Printf( "PLACED:\n" ); + + totalSize.x = 0; + totalSize.y = 0; + + int i = 0; + for( binpack2d_iterator itor = outputContent.Get().begin(); itor != outputContent.Get().end(); itor++, i++ ) + { + const BinPack2D::Content& content = *itor; + + // retreive your data. + const MyContent& myContent = content.content; + + int index = myContent.itemIndex; + outputPositions[ index ].x = content.coord.x; + outputPositions[ index ].y = content.coord.y; + + if( ( content.coord.x + content.size.w ) > totalSize.x ) + { + totalSize.x = content.coord.x + content.size.w; + } + + if( ( content.coord.y + content.size.h ) > totalSize.y ) + { + totalSize.y = content.coord.y + content.size.h; + } + +#if 0 + idLib::Printf( "\t%9s of size %3dx%3d at position %3d,%3d,%2d rotated=%s\n", + myContent.str.c_str(), + content.size.w, + content.size.h, + content.coord.x, + content.coord.y, + content.coord.z, + ( content.rotated ? "yes" : " no" ) ); +#endif + } + + + for( binpack2d_iterator itor = remainder.Get().begin(); itor != remainder.Get().end(); itor++ ) + { + const BinPack2D::Content& content = *itor; + + const MyContent& myContent = content.content; + + int index = myContent.itemIndex; + outputPositions[ index ].x = -1; + outputPositions[ index ].y = -1; + + idLib::Printf( "\tFailed to place %9s of size %3dx%3d\n", + myContent.str.c_str(), + content.size.w, + content.size.h ); + } +} \ No newline at end of file diff --git a/neo/libs/binpack2d/binpack2d.h b/neo/libs/binpack2d/binpack2d.h new file mode 100644 index 00000000..c8994b51 --- /dev/null +++ b/neo/libs/binpack2d/binpack2d.h @@ -0,0 +1,704 @@ + +/* +Copyright (c) 2013, christopher stones < chris.stones@zoho.com > +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. 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. +*/ + + + +/** + * BinPack2D is a 2 dimensional, multi-bin, bin-packer. ( Texture Atlas Array! ) + * It supports an arbitrary number of bins, at arbitrary sizes. + * rectangles can be added one at a time, chunks at a time, or all at once. + * rectangles that dont fit are reported back. + * Data can be associated to rectangles before processing via a template, and recalled after processing. + * + * There is no documentation, See ExampleProgram() below for a taste. + * + * Instead of tracking 'free rectangles' like other solutions I've found online, + * this algorithm tracks free 'top lefts', keeps them sorted by closest to origin, and puts new rectangles into + * the first free top left that doesnt collide. Consuming a top left creates 2 new top lefts (x+w,y) and (x,y+h). + * If a rectangle doesnt fit into a bin, before condisering the next bin, the current bin is re-tried with the rectangle rotated. + * This SOMTIMES helps... but not always.. i might disable this in future !? + * + * This Header was origonally part of my rh_texture_packer program. + * A program I wrote to take advantage of my nexus-7's GL_EXT_texture_array extension. + * I wanted to be able to render out whole scenes with a single glDraw* + * blah blah blah... + */ + + +/** ***** EXAMPLE CODE ************************************** + + // Your data - whatever you want to associate with 'rectangle' + class MyContent { + public: + std::string str; + MyContent() : str("default string") {} + MyContent(const std::string &str) : str(str) {} + }; + + int ExampleProgram() { + + srandom(0x69); + + // Create some 'content' to work on. + BinPack2D::ContentAccumulator inputContent; + + for(int i=0;i<20;i++) { + + // random size for this content + int width = ((random() % 32)+1) * ((random() % 10)+1); + int height = ((random() % 32)+1) * ((random() % 10)+1); + + // whatever data you want to associate with this content + std::stringstream ss; + ss << "box " << i; + MyContent mycontent( ss.str().c_str() ); + + // Add it + inputContent += BinPack2D::Content(mycontent, BinPack2D::Coord(), BinPack2D::Size(width, height), false ); + } + + // Sort the input content by size... usually packs better. + inputContent.Sort(); + + // Create some bins! ( 2 bins, 128x128 in this example ) + BinPack2D::CanvasArray canvasArray = + BinPack2D::UniformCanvasArrayBuilder(128,128,2).Build(); + + // A place to store content that didnt fit into the canvas array. + BinPack2D::ContentAccumulator remainder; + + // try to pack content into the bins. + bool success = canvasArray.Place( inputContent, remainder ); + + // A place to store packed content. + BinPack2D::ContentAccumulator outputContent; + + // Read all placed content. + canvasArray.CollectContent( outputContent ); + + // parse output. + typedef BinPack2D::Content::Vector::iterator binpack2d_iterator; + printf("PLACED:\n"); + for( binpack2d_iterator itor = outputContent.Get().begin(); itor != outputContent.Get().end(); itor++ ) { + + const BinPack2D::Content &content = *itor; + + // retreive your data. + const MyContent &myContent = content.content; + + printf("\t%9s of size %3dx%3d at position %3d,%3d,%2d rotated=%s\n", + myContent.str.c_str(), + content.size.w, + content.size.h, + content.coord.x, + content.coord.y, + content.coord.z, + (content.rotated ? "yes":" no")); + } + + printf("NOT PLACED:\n"); + for( binpack2d_iterator itor = remainder.Get().begin(); itor != remainder.Get().end(); itor++ ) { + + const BinPack2D::Content &content = *itor; + + const MyContent &myContent = content.content; + + printf("\t%9s of size %3dx%3d\n", + myContent.str.c_str(), + content.size.w, + content.size.h); + } + + exit(0); +} +*/ + + + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace BinPack2D +{ + +class Size +{ + +public: + + /*const*/ + int w; + /*const*/ int h; + + Size( int w, int h ) + : w( w ), + h( h ) + {} + + bool operator < ( const Size& that ) const + { + if( this->w != that.w ) + { + return this->w < that.w; + } + if( this->h != that.h ) + { + return this->h < that.h; + } + return false; + } +}; + +class Coord +{ + +public: + + typedef std::vector Vector; + typedef std::list List; + + /*const*/ int x; + /*const*/ int y; + /*const*/ int z; + + Coord() + : x( 0 ), + y( 0 ), + z( 0 ) + {} + + Coord( int x, int y ) + : x( x ), + y( y ), + z( 0 ) + {} + + Coord( int x, int y, int z ) + : x( x ), + y( y ), + z( z ) + {} + + bool operator < ( const Coord& that ) const + { + if( this->x != that.x ) + { + return this->x < that.x; + } + if( this->y != that.y ) + { + return this->y < that.y; + } + if( this->z != that.z ) + { + return this->z < that.z; + } + return false; + } +}; + +template class Content +{ + +public: + + typedef std::vector > Vector; + + /*const*/ bool rotated; + /*const*/ Coord coord; + /*const*/ Size size; + /*const*/ _T content; + + Content( const Content<_T>& src ) + : rotated( src.rotated ), + coord( src.coord ), + size( src.size ), + content( src.content ) + {} + + Content( const _T& content, const Coord& coord, const Size& size, bool rotated ) + : + content( content ), + coord( coord ), + size( size ), + rotated( rotated ) + {} + + void Rotate() + { + + rotated = !rotated; + size = Size( size.h, size.w ); + } + + bool intersects( const Content<_T>& that ) const + { + if( this->coord.x >= ( that.coord.x + that.size.w ) ) + { + return false; + } + + if( this->coord.y >= ( that.coord.y + that.size.h ) ) + { + return false; + } + + if( that.coord.x >= ( this->coord.x + this->size.w ) ) + { + return false; + } + + if( that.coord.y >= ( this->coord.y + this->size.h ) ) + { + return false; + } + + return true; + } +}; + +template class Canvas +{ + + Coord::List topLefts; + typename Content<_T>::Vector contentVector; + + bool needToSort; + +public: + + typedef Canvas<_T> CanvasT; + typedef typename std::vector Vector; + + static bool Place( Vector& canvasVector, const typename Content<_T>::Vector& contentVector, typename Content<_T>::Vector& remainder ) + { + + typename Content<_T>::Vector todo = contentVector; + + for( typename Vector::iterator itor = canvasVector.begin(); itor != canvasVector.end(); itor++ ) + { + + Canvas <_T>& canvas = *itor; + + remainder.clear(); + canvas.Place( todo, remainder ); + todo = remainder; + } + + if( remainder.size() == 0 ) + { + return true; + } + + return false; + } + + static bool Place( Vector& canvasVector, const typename Content<_T>::Vector& contentVector ) + { + + typename Content<_T>::Vector remainder; + + return Place( canvasVector, contentVector, remainder ); + } + + static bool Place( Vector& canvasVector, const Content<_T>& content ) + { + + typename Content<_T>::Vector contentVector( 1, content ); + + return Place( canvasVector, contentVector ); + } + + const int w; + const int h; + + Canvas( int w, int h ) + : needToSort( false ), + w( w ), + h( h ) + { + topLefts.push_back( Coord( 0, 0 ) ); + } + + bool HasContent() const + { + + return ( contentVector.size() > 0 ) ; + } + + const typename Content<_T>::Vector& GetContents( ) const + { + + return contentVector; + } + + bool operator < ( const Canvas& that ) const + { + + if( this->w != that.w ) + { + return this->w < that.w; + } + if( this->h != that.h ) + { + return this->h < that.h; + } + return false; + } + + bool Place( const typename Content<_T>::Vector& contentVector, typename Content<_T>::Vector& remainder ) + { + + bool placedAll = true; + + for( typename Content<_T>::Vector::const_iterator itor = contentVector.begin(); itor != contentVector.end(); itor++ ) + { + + const Content<_T>& content = *itor; + + if( Place( content ) == false ) + { + + placedAll = false; + remainder.push_back( content ); + } + } + + return placedAll; + } + + bool Place( Content<_T> content ) + { + Sort(); + + for( Coord::List::iterator itor = topLefts.begin(); itor != topLefts.end(); itor++ ) + { + + content.coord = *itor; + + if( Fits( content ) ) + { + + Use( content ); + topLefts.erase( itor ); + return true; + } + } + + // EXPERIMENTAL - TRY ROTATED? + content.Rotate(); + for( Coord::List::iterator itor = topLefts.begin(); itor != topLefts.end(); itor++ ) + { + + content.coord = *itor; + + if( Fits( content ) ) + { + + Use( content ); + topLefts.erase( itor ); + return true; + } + } + //////////////////////////////// + + + return false; + } + +private: + + bool Fits( const Content<_T>& content ) const + { + if( ( content.coord.x + content.size.w ) > w ) + { + return false; + } + + if( ( content.coord.y + content.size.h ) > h ) + { + return false; + } + + for( typename Content<_T>::Vector::const_iterator itor = contentVector.begin(); itor != contentVector.end(); itor++ ) + if( content.intersects( *itor ) ) + { + return false; + } + + return true; + } + + bool Use( const Content<_T>& content ) + { + const Size& size = content.size; + const Coord& coord = content.coord; + + topLefts.push_front( Coord( coord.x + size.w, coord.y ) ); + topLefts.push_back( Coord( coord.x , coord.y + size.h ) ); + + contentVector.push_back( content ); + + needToSort = true; + + return true; + } + +private: + + struct TopToBottomLeftToRightSort + { + + bool operator()( const Coord& a, const Coord& b ) const + { + + return ( a.x * a.x + a.y * a.y ) < ( b.x * b.x + b.y * b.y ); + } + }; + +public: + + void Sort() + { + + if( !needToSort ) + { + return; + } + + topLefts.sort( TopToBottomLeftToRightSort() ); + + needToSort = false; + } +}; + +template class ContentAccumulator +{ + typename Content<_T>::Vector contentVector; + +public: + + ContentAccumulator() + {} + + const typename Content<_T>::Vector& Get() const + { + + return contentVector; + } + + typename Content<_T>::Vector& Get() + { + + return contentVector; + } + + ContentAccumulator<_T>& operator += ( const Content<_T>& content ) + { + + contentVector.push_back( content ); + + + return *this; + } + + ContentAccumulator<_T>& operator += ( const typename Content<_T>::Vector& content ) + { + + contentVector.insert( contentVector.end(), content.begin(), content.end() ); + + + return *this; + } + + ContentAccumulator<_T> operator + ( const Content<_T>& content ) + { + + ContentAccumulator<_T> temp = *this; + + temp += content; + + return temp; + } + + ContentAccumulator<_T> operator + ( const typename Content<_T>::Vector& content ) + { + + ContentAccumulator<_T> temp = *this; + + temp += content; + + return temp; + } + + + +private: + + struct GreatestWidthThenGreatestHeightSort + { + bool operator()( const Content<_T>& a, const Content<_T>& b ) const + { + + const Size& sa = a.size; + const Size& sb = b.size; + +// return( sa.w * sa.h > sb.w * sb.h ); + + if( sa.w != sb.w ) + { + return sa.w > sb.w; + } + return sa.h > sb.h; + } + }; + + struct MakeHorizontal + { + + Content<_T> operator()( const Content<_T>& elem ) + { + + if( elem.size.h > elem.size.w ) + { + Content<_T> r = elem; + + r.size.w = elem.size.h; + r.size.h = elem.size.w; + r.rotated = !elem.rotated; + + return r; + } + + return elem; + } + }; + +public: + + void Sort() + { + +// if(allow_rotation) +// std::transform(contentVector.begin(), contentVector.end(), contentVector.begin(), MakeHorizontal()); + + std::sort( contentVector.begin(), contentVector.end(), GreatestWidthThenGreatestHeightSort() ); + } +}; + +template class UniformCanvasArrayBuilder +{ + int w; + int h; + int d; + +public: + + UniformCanvasArrayBuilder( int w, int h, int d ) + : w( w ), + h( h ), + d( d ) + {} + + typename Canvas<_T>::Vector Build() + { + + return typename Canvas<_T>::Vector( d, Canvas<_T>( w, h ) ); + } +}; + +template class CanvasArray +{ + typename Canvas<_T>::Vector canvasArray; + +public: + + CanvasArray( const typename Canvas<_T>::Vector& canvasArray ) + : canvasArray( canvasArray ) + {} + + bool Place( const typename Content<_T>::Vector& contentVector, typename Content<_T>::Vector& remainder ) + { + + return Canvas<_T>::Place( canvasArray, contentVector, remainder ); + } + + bool Place( const ContentAccumulator<_T>& content, ContentAccumulator<_T>& remainder ) + { + + return Place( content.Get(), remainder.Get() ); + } + + bool Place( const typename Content<_T>::Vector& contentVector ) + { + + return Canvas<_T>::Place( canvasArray, contentVector ); + } + + bool Place( const ContentAccumulator<_T>& content ) + { + + return Place( content.Get() ); + } + + bool CollectContent( typename Content<_T>::Vector& contentVector ) const + { + + int z = 0; + + for( typename Canvas<_T>::Vector::const_iterator itor = canvasArray.begin(); itor != canvasArray.end(); itor++ ) + { + + const typename Content<_T>::Vector& contents = itor->GetContents(); + + for( typename Content<_T>::Vector::const_iterator itor = contents.begin(); itor != contents.end(); itor++ ) + { + + Content<_T> content = *itor; + + content.coord.z = z; + + contentVector.push_back( content ); + } + z++; + } + return true; + } + + bool CollectContent( ContentAccumulator<_T>& content ) const + { + + return CollectContent( content.Get() ); + } +}; + +} /*** BinPack2D ***/