Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Packing arbitrary triangles into a finite box?

I need to pack triangles into a box as tightly as reasonable, as part of a 3D optimization (I'm stuffing alpha-using segments of different textures into a single different texture, for use with depth-sorting, so textures don't switch with every new tri)

Is there an algorithm for doing this? The triangles themselves can be made to be plyable (transformable to be right angles, effectively making this a box-stuffing algorithm instead), but I would like to avoid this if possible, as it would distort the underlying texture art.

like image 954
Anne Quinn Avatar asked Jul 14 '14 10:07

Anne Quinn


1 Answers

"tight as reasonable" -> Something working is better than nothing.

These code fragments provide a simple solution to stuff shapes (also triangles) band by band into a rectangle.

public abstract class Shape {     protected Point offset = new Point();     public abstract int getHeight();     public abstract int getWidth(); }  public class Triangle extends Shape {     // all points are relative to offset (from Shape)     Point top = new Point(); // top.y is always 0, left.y >= 0 right.y >= 0      Point left = new Point(); // left.x < right.x     Point right = new Point();      public int getHeight() {         return left.y >= right.y ? left.y : right.y;     }      public int getWidth() {         int xmin = left.x <= top.x ? left.x : top.x;         int xmax = right.x >= top.x ? right.x : top.x;         return xmax - xmin;     } }  public class StuffRectangle extends Shape {     private Point ww = new Point();      private ArrayList<Shape> maintained = new ArrayList<Shape>();     private int insx;     private int insy;     private int maxy;      public int getHeight() {         return ww.y;     }      public int getWidth() {         return ww.x;     }      public void clear() {         insx = 0;         insy = 0;         maxy = 0;         maintained.clear();     }      /**      * Fill the rectangle band by band.      *       * The inserted shapes are removed from the provided shape collection.      *       * @param shapes      *            the shapes to insert      * @return the count of inserted shapes.      */     public int stuff(Collection<Shape> shapes) {         int inserted = 0;          for (;;) {             int insertedInPass = 0;             for (Iterator<Shape> i = shapes.iterator(); i.hasNext();) {                 Shape shape = i.next();                  // does the shape fit into current band?                 int sx = shape.getWidth();                 if (insx + sx > getWidth())                     continue;                 int sy = shape.getHeight();                 if (insy + sy > getHeight())                     continue;                  // does fit                 ++insertedInPass;                  // remove from shapes                 i.remove();                  // add to maintained and adjust offset                 maintained.add(shape);                 shape.offset.x = insx;                 shape.offset.y = insy;                  insx += sx;                 if (sy > maxy)                     maxy = sy;              }             inserted += insertedInPass;             if (shapes.isEmpty())                 break;             // nothing fits current band - try a new band             if (insertedInPass == 0) {                 // already a new band - does not fit at all                 if (insx == 0)                     break;                  // start new band                 insx = 0;                 insy += maxy;                 maxy = 0;                 continue;             }         }          return inserted;     } } 
like image 119
bebbo Avatar answered Sep 21 '22 03:09

bebbo