/******************************************************************************* * This is part of SketchChair, an open-source tool for designing your own furniture. * www.sketchchair.cc * * Copyright (C) 2012, Diatom Studio ltd. Contact: hello@diatom.cc * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package ShapePacking; import nu.xom.Attribute; import nu.xom.Element; import cc.sketchchair.core.LOGGER; import ToolPathWriter.CraftRoboWriter; import ToolPathWriter.DXFWriter; import ToolPathWriter.HPGLWriter; import processing.core.PGraphics; /** * Represents a single page in a multipage cutting file. Holds spOutlines. * @author gregsaul * */ public class spPage { spShapePack shapePack; spShapes shapes = new spShapes(); float yPos = 0; float xPos = 0; float lastHeight = 0; int tilePackCount = 0; public void packSmart(spShapes packingShapes) { LOGGER.info("packSmart"); float lineWidth = 0; float lineHeight = 0; float tallestOnLine = 0; float gap = shapePack.shapeGap; float xPos = gap; float yPos = gap; float pageBorder = 10; float step = 5*shapePack.scale; boolean ignorePageBounds = false; //Lets smart pack everything //we could start by ordering parts from large to small but it shouldn't be necessary for (int i = 0; i < packingShapes.l.size(); i++) { spShape shape = (spShape) packingShapes.l.get(i); spShape shapeCopy = shape.clone(); shape.translate(-shape.outlines.getMinX(), -shape.outlines.getMinY()); ignorePageBounds = false; if(shapeCopy.width >= (this.shapePack.materialWidth-(pageBorder*2)) && shapeCopy.height >= this.shapePack.materialHeight-(pageBorder*2)){ ignorePageBounds = true; LOGGER.info("IGNORE BOUNDS"); } boolean placeFound = false; //try each position and rotation on the material placingLoop : for(float yOffsetTry = 0 ; yOffsetTry < this.shapePack.materialWidth ; yOffsetTry+=step){ for(float xOffsetTry = 0 ; xOffsetTry < this.shapePack.materialHeight ; xOffsetTry+=step){ for(float rotateTry = 0 ; rotateTry < (Math.PI*2) ; rotateTry+= (Math.PI/2)){ shapeCopy = shape.clone(); shapeCopy.translate(xOffsetTry, yOffsetTry); shapeCopy.rotate(rotateTry); //LOGGER.info("looking"); boolean inBounds = shapeCopy.inBoundss(pageBorder,pageBorder,this.shapePack.materialWidth-(pageBorder*2),this.shapePack.materialHeight-(pageBorder*2)); // float shapesArea = (this.shapes.getWidth()*this.shapes.getHeight()); if( !placeFound && (inBounds || ignorePageBounds)){ boolean hasCollision = this.shapes.hasCollisions(shapeCopy); if(!hasCollision ){ this.shapes.add(shapeCopy); placeFound = true; packingShapes.l.remove(i); i--; break placingLoop; } } } } } } } public void packTile(spShapes packingShapes) { LOGGER.info("packTile Page"); float lineWidth = 0; float lineHeight = 0; float tallestOnLine = 0; float gap = shapePack.shapeGap; xPos = gap; yPos = gap; for (int i = 0; i < packingShapes.l.size(); i++) { spShape shape = (spShape) packingShapes.l.get(i); if (shape.getWidth() + xPos + gap > this.shapePack.materialWidth) { xPos = gap; yPos += tallestOnLine + gap; lastHeight = tallestOnLine+ gap; tallestOnLine = 0; } if (yPos + shape.getHeight() + gap > this.shapePack.materialHeight) { i = packingShapes.l.size() + 1; return; } if (shape.getHeight() > tallestOnLine){ tallestOnLine = shape.getHeight(); lastHeight = shape.getHeight()+gap; } //shape.offsetX = xPos; //shape.offsetY = yPos; shape.translate(xPos,yPos); xPos += shape.getWidth() + gap; shape.packed = true; this.shapes.add(shape); packingShapes.l.remove(i); i--; } } public void packTileSmart(spShapes packingShapes) { LOGGER.debug("packTileSmart Page"); float lineWidth = 0; float lineHeight = 0; float tallestOnLine = 0; float shortestOnLine = -1; float gap = 2.0f*shapePack.scale;//shapePack.shapeGap; float xPos = gap; float yPos = gap; float pageBorder = 10; float startX =0; float startY = 0; float step = 5*shapePack.scale; //Lets smart pack everything //we could start by ordering parts from large to small but it shouldn't be necessary boolean firstPlace = true; boolean ignorePageBounds = false; for (int i = 0; i < packingShapes.l.size(); i++) { spShape shape = (spShape) packingShapes.l.get(i); spShape shapeCopy = shape.clone(); shape.translate(-shape.outlines.getMinX(), -shape.outlines.getMinY()); float shapeWidth = shapeCopy.width; if(shapeCopy.width == 0) break; boolean placeFound = false; firstPlace = true; ignorePageBounds = false; if(shapeCopy.width >= (this.shapePack.materialWidth-(pageBorder*2)) || shapeCopy.height >= this.shapePack.materialHeight-(pageBorder*2)){ ignorePageBounds = true; LOGGER.debug("ignorePageBounds"); }else{ LOGGER.debug(" dont ignorePageBounds"); } //try each position and rotation on the material placingLoop : for(float yOffsetTry = pageBorder ; yOffsetTry < this.shapePack.materialHeight ; yOffsetTry+=step){ boolean collisionFoundOnRow = false; for(float xOffsetTry = pageBorder ; xOffsetTry < this.shapePack.materialWidth ; xOffsetTry+=step){ if(yOffsetTry > this.shapePack.materialHeight) break placingLoop; //LOGGER.info("checking x" + xOffsetTry + " y" + yOffsetTry + " of " + this.shapePack.materialHeight); if(firstPlace){ yOffsetTry = startY; xOffsetTry = startX; } shapeCopy = shape.clone(); // if we can't fit on the page at least alight to the left if(ignorePageBounds){ shapeCopy.translate(pageBorder, yOffsetTry); xOffsetTry = this.shapePack.materialWidth; }else{ shapeCopy.translate(xOffsetTry, yOffsetTry); } //LOGGER.info("looking at " + xOffsetTry + " " + yOffsetTry); boolean inBounds = shapeCopy.inBounds(pageBorder,pageBorder,this.shapePack.materialWidth-(pageBorder*2),this.shapePack.materialHeight-(pageBorder*2)); // float shapesArea = (this.shapes.getWidth()*this.shapes.getHeight()); if(!firstPlace && !collisionFoundOnRow && !ignorePageBounds && xOffsetTry + shapeCopy.width + gap > this.shapePack.materialWidth-pageBorder){ xOffsetTry = pageBorder; yOffsetTry += shortestOnLine+(gap*2); shortestOnLine = -1; } firstPlace = false; if( !placeFound && (inBounds || ignorePageBounds)){ boolean hasCollision = this.shapes.hasCollisionsBounds(shapeCopy, gap); if(!hasCollision ){ this.shapes.add(shapeCopy); placeFound = true; startX = xOffsetTry + shapeCopy.width + (gap); startY = yOffsetTry; if(ignorePageBounds){ startY += shapeCopy.height + (gap*2); } if(shortestOnLine == -1 || shapeCopy.height < shortestOnLine) shortestOnLine = shapeCopy.height; packingShapes.l.remove(i); i--; break placingLoop; }else{ collisionFoundOnRow = true; } } } } } } public void render(PGraphics g) { g.stroke(0); g.fill(250); g.rect(0, 0, this.shapePack.materialWidth, this.shapePack.materialHeight); shapes.renderPage(g); } public void renderPickBuffer(PGraphics pickBuffer) { pickBuffer.stroke(0); pickBuffer.fill(250); pickBuffer.rect(0, 0, this.shapePack.materialWidth, this.shapePack.materialHeight); shapes.renderPickBufferPage(pickBuffer); } public void renderList(PGraphics g) { shapes.renderPage(g); } public void renderPickBufferList(PGraphics pickBuffer) { shapes.renderPickBufferPage(pickBuffer); } public void renderDXF(DXFWriter dxf, float offsetX, float offsetY) { //g.stroke(0); //g.fill(255); //g.rect(0,0,this.shapePack.pageWidth,this.shapePack.pageHeight); shapes.renderPageDXF(dxf, offsetX, offsetY); } public void renderToPlotter(HPGLWriter craftRoboWriter) { this.shapes.renderToPlotter(craftRoboWriter); } public float getHeight() { return yPos+lastHeight; } public Element toXML() { Element element = new Element("g","http://www.w3.org/2000/svg"); element.addAttribute(new Attribute("id","page")); element.appendChild(this.shapes.toXML()); return element; } }