/******************************************************************************* * 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 cc.sketchchair.geometry; import java.util.ArrayList; import java.util.List; import toxi.geom.Plane; import toxi.geom.Vec2D; import toxi.geom.Vec3D; import ShapePacking.BezierControlNode; import cc.sketchchair.core.CrossSliceSelection; import cc.sketchchair.core.GLOBAL; import cc.sketchchair.core.LOGGER; import cc.sketchchair.core.SETTINGS; import cc.sketchchair.core.SketchChair; import cc.sketchchair.sketch.SETTINGS_SKETCH; import cc.sketchchair.sketch.SketchPath; import cc.sketchchair.sketch.SketchPoint; import cc.sketchchair.sketch.SketchShape; import cc.sketchchair.sketch.SketchShapes; import cc.sketchchair.sketch.SketchSpline; import cc.sketchchair.sketch.SliceSlot; /** * Functions to perform geometry operations in SketchChair for example intersecting planes. * @author gregsaul * */ public class GeometryOperations { public static void generateSlat(SlicePlanes planesToSlice, SlicePlanes crossSlices, CrossSliceSelection crossSliceSelection, SketchShape guideSpline, SketchChair chair) { float dirSign = -1; //direction of plane float planeOffset = ((SETTINGS.materialThickness / 2) / SETTINGS.scale) * dirSign; crossSliceSelection.spacing = planeOffset; float widthBetweenPlanes = planesToSlice.getMaxZ() - planesToSlice.getMinZ(); float startX = 0; if (guideSpline == null) return; float start = Math.min(crossSliceSelection.start, crossSliceSelection.end); float end = Math .max(crossSliceSelection.start, crossSliceSelection.end); if (end >= 1) end = .999f; if (start <= 0) start = .01f; Vec2D startPos2D = guideSpline.getPos(start); Vec2D endPos2D = guideSpline.getPos(end); if (startPos2D == null || endPos2D == null) return; Vec3D vecPosStart = new Vec3D(startPos2D.x, startPos2D.y, 0); Vec3D vecPosEnd = new Vec3D(endPos2D.x, endPos2D.y, 0); float pLen = vecPosStart.distanceTo(vecPosEnd); Vec3D vecDir = new Vec3D(vecPosStart.x - vecPosEnd.x, vecPosStart.y - vecPosEnd.y, 0); vecDir = vecDir.normalize(); Vec3D vecDirOffset = vecDir.copy(); vecDirOffset.rotateZ((float) (Math.PI / 2)); vecDirOffset.scaleSelf(crossSliceSelection.spacing); vecPosStart.addSelf(vecDirOffset); vecDir.rotateZ((float) (Math.PI / 2)); //vecDir.rotateX((float) (Math.PI/2 )); int sign = 1; if(Math.atan2(vecDir.x, vecDir.y) < 0){ sign = -1; } SlicePlane slicePlane = new SlicePlane(new Plane(vecPosStart, vecDir)); slicePlane.setCrossSliceSelection(crossSliceSelection); //LOGGER.info("vecDir"+Math.atan2(vecDir.x, vecDir.y)); SketchPath sketchPath = new SketchPath(slicePlane.getSketch()); /* float offsetWidth = widthBetweenPlanes / 2; sketchPath.add(new SketchPoint(startX + offsetWidth + crossSliceSelection.boarderX, -crossSliceSelection.boarderY)); sketchPath.add(new SketchPoint(startX - (offsetWidth + crossSliceSelection.boarderX), -crossSliceSelection.boarderY)); sketchPath.add(new SketchPoint(startX - (offsetWidth + crossSliceSelection.boarderX), pLen + crossSliceSelection.boarderY)); sketchPath.add(new SketchPoint(startX + offsetWidth + crossSliceSelection.boarderX, pLen + crossSliceSelection.boarderY)); */ float penetration = 0.01f; float minZ = sign*planesToSlice.getMinZ(); float maxZ = sign*planesToSlice.getMaxZ(); //TODO: Adding +1 to width fixes overlapping issue but does not seem right. float materialHalfWidth = ((SETTINGS.materialThickness / 2) / SETTINGS.scale); sketchPath.add(new SketchPoint(maxZ-materialHalfWidth+penetration,0)); sketchPath.add(new SketchPoint(minZ+materialHalfWidth-penetration,0)); sketchPath.add(new SketchPoint(minZ+materialHalfWidth-penetration, pLen-penetration)); sketchPath.add(new SketchPoint(maxZ-materialHalfWidth+penetration, pLen-penetration)); slicePlane.getSketch().getSketchShapes().add(sketchPath); sketchPath.setClosed(true); /** * Go though each Plane/Layer */ // add slot to all pieces for (int j = 0; j < planesToSlice.size(); j++) { SlicePlane currentPlaneY = planesToSlice.get(j); //LOGGER.info(currentPlaneY.getPlane().toString()); Vec2D slotPos = new Vec2D(vecPosStart.x, vecPosStart.y); Vec2D slotDir = new Vec2D(vecDir.x, vecDir.y); SliceSlot slot = new SliceSlot(slicePlane, slotPos, slotDir, (float) (pLen / crossSliceSelection.teethCount), (float) (pLen / crossSliceSelection.teethCount), SETTINGS.materialThickness, (int) (crossSliceSelection.teethCount), false, SliceSlot.SLOT); slot.setFingerTollerance(-(crossSliceSelection.fingerTollerance / SETTINGS.scale)); if(crossSliceSelection.type == CrossSliceSelection.PLANE_ON_EDGE) slot.setOnEdge(new Vec2D(-1, 0)); currentPlaneY.getSketch().getSketchShapes().getSlots().add(slot); Vec2D slotPosSlice = new Vec2D(sign*currentPlaneY.getPlane().z,0); Vec2D slotDirSlice = new Vec2D(1, 0); SliceSlot slotSlice = new SliceSlot(slicePlane, slotPosSlice, slotDirSlice, (float) (pLen / crossSliceSelection.teethCount), (float) (pLen / crossSliceSelection.teethCount), SETTINGS.materialThickness, (int) (crossSliceSelection.teethCount), true, SliceSlot.SLOTS_AND_FINGER); SliceSlot slotSlice2 = new SliceSlot(slicePlane, slotPosSlice, slotDirSlice, (float) (pLen), (float) (pLen), SETTINGS.materialThickness, (int) (1), true, SliceSlot.SLOTS_AND_FINGER); if(!currentPlaneY.guide) slicePlane.getSketch().getSketchShapes().getSlots().add(slotSlice); } /* Vec2D slotPos = new Vec2D(offsetWidth + crossSliceSelection.boarderX, -crossSliceSelection.boarderY); Vec2D slotDir = new Vec2D(1, 0); SliceSlot slot = new SliceSlot(slicePlane, slotPos, slotDir, (int) (pLen / crossSliceSelection.teethCount), (int) (pLen / crossSliceSelection.teethCount), SETTINGS.plane_thickness, (int) (crossSliceSelection.teethCount), true, SliceSlot.SLOTS_AND_FINGER); // slot.setOnEdge(new Vec2D(0,1)); slicePlane.getSketch().getSketchShapes().getSlots().add(slot); slotPos = new Vec2D(-(offsetWidth + crossSliceSelection.boarderX), -crossSliceSelection.boarderY); slotDir = new Vec2D(1, 0); slot = new SliceSlot(slicePlane, slotPos, slotDir, (int) (pLen / crossSliceSelection.teethCount), (int) (pLen / crossSliceSelection.teethCount), SETTINGS.plane_thickness, (int) (crossSliceSelection.teethCount), true, SliceSlot.SLOTS_AND_FINGER); slicePlane.getSketch().getSketchShapes().getSlots().add(slot); */ //build the outline slicePlane.getSketch().buildOutline(); crossSlices.add(slicePlane); } public static void generateSlatSlices(SlicePlanes planesToSlice, SlicePlanes crossSlices, CrossSliceSelection crossSliceSelection, SketchShape guideSpline, SketchChair chair) { int numSlices = (int) SETTINGS.slat_num;// (int) float spacing = SETTINGS.chair_width / (numSlices + 1); float scale = 1; float width = 10; // TODO: option flush edges // Add fake profiles on the ends, this might be a option later for flush // edges ! SlicePlane startSlicePlaneend1 = planesToSlice.getList().get(0); SlicePlane slicePlane1 = new SlicePlane(startSlicePlaneend1.getSketch() .clone(), new Plane(new Vec3D(0, 0, (startSlicePlaneend1.getPlane().z) - SETTINGS.chair_slatslot_end_size), new Vec3D(0, 0, -1))); planesToSlice.add(0, slicePlane1); // Add fake profiles on the ends SlicePlane startSlicePlaneend2 = planesToSlice .get(planesToSlice.size() - 1); SlicePlane slicePlane2 = new SlicePlane(startSlicePlaneend2.getSketch() .clone(), new Plane(new Vec3D(0, 0, (startSlicePlaneend2.getPlane().z) + SETTINGS.chair_slatslot_end_size), new Vec3D(0, 0, -1))); planesToSlice.add(slicePlane2); // Add fake profiles on the ends float coverWidth = width + (SETTINGS.chair_slatslot_end_size * 2); float coverLength = guideSpline.getlength() + (SETTINGS_SKETCH.chair_slat_end_size * 2); float step = crossSliceSelection.spacing / guideSpline.getlength(); float start = Math.min(crossSliceSelection.start, crossSliceSelection.end); float end = Math .max(crossSliceSelection.start, crossSliceSelection.end); // special case for lines with 2 points if (guideSpline.size() == 2) { start += step; end -= step; } for (float i = start; i <= end; i += step) { Vec3D vecPos = new Vec3D(guideSpline.getPos(i).x, guideSpline.getPos(i).y, 0); Vec3D vecDir = new Vec3D(guideSpline.getPerpendicular(i).x, guideSpline.getPerpendicular(i).y, 0); // do we want to add a strenghtening beam to the leg if (crossSliceSelection.tieToLeg) { SketchPoint point1 = (SketchPoint) guideSpline.get(0); SketchPoint point2 = (SketchPoint) guideSpline.get(1); Vec2D dir2D = point1.sub(point2).normalize(); dir2D.rotate((float) (Math.PI / 2)); vecPos = new Vec3D(point2.x, point2.y, 0); vecDir = new Vec3D(dir2D.x, dir2D.y, 0); } SlicePlane slicePlane = new SlicePlane(new Plane(vecPos, vecDir)); slicePlane.setCrossSliceSelection(crossSliceSelection); // SketchOutline sktOutline = new SketchOutline(); List<SketchPoint> topLine = new ArrayList<SketchPoint>(); List<SketchPoint> bottomLine = new ArrayList<SketchPoint>(); for (int j = 0; j < planesToSlice.size(); j++) { SlicePlane currentPlaneY = planesToSlice.get(j); topLine.add(new SketchPoint(currentPlaneY.getPlane().z, 0)); bottomLine.add(new SketchPoint(currentPlaneY.getPlane().z, crossSliceSelection.getSlatHeight())); if (j != 0 && j != planesToSlice.size() - 1) { SliceSlot slot = new SliceSlot(slicePlane, new Vec2D( vecPos.x, vecPos.y), new Vec2D(vecDir.x, vecDir.y), (crossSliceSelection.getSlatHeight() / 2), SETTINGS.materialThickness); //slot.setOnEdge(new Vec2D(0, 1)); currentPlaneY.getSketch().getSlots().add(slot); slot = new SliceSlot(currentPlaneY, new Vec2D( currentPlaneY.getPlane().z, (crossSliceSelection.getSlatHeight() / 2)), new Vec2D(1, 0), (crossSliceSelection.getSlatHeight() / 2), SETTINGS.materialThickness); //slot.setOnEdge(new Vec2D(0,1)); slicePlane.getSketch().getSlots().add(slot); } } // now go around the lines we collected and add them to a // outline Vec2D controlPointLeft = null; Vec2D bezierPointLeft = null; Vec2D controlPointRight = null; Vec2D bezierPointRight = null; // now change the line caping if (bottomLine.size() > 1) { controlPointRight = bottomLine.get(0); bezierPointRight = bottomLine.get(1); controlPointLeft = bottomLine.get(bottomLine.size() - 1); bezierPointLeft = bottomLine.get(bottomLine.size() - 2); bottomLine.remove(0); bottomLine.remove(bottomLine.size() - 1); if (crossSliceSelection.tieToLeg) { controlPointRight = new Vec2D(bezierPointRight.x - 15, bezierPointRight.y); controlPointLeft = new Vec2D(bezierPointLeft.x + 15, bezierPointLeft.y); } } SketchPath sketchPath = new SketchPath(slicePlane.getSketch()); for (int i1 = 0; i1 < topLine.size(); i1++) { SketchPoint tempP = topLine.get(i1); // sktOutline.add(tempVec); sketchPath.add(tempP); float offsetX = (width / (numSlices - 1)) / 2; if (i1 != 0 && i1 != topLine.size() - 1 && SETTINGS_SKETCH.Draw_Curves) sketchPath.addBezier(tempP, new Vec2D(tempP.x - offsetX, tempP.y), new Vec2D( tempP.x + offsetX, tempP.y)); } for (int i1 = bottomLine.size() - 1; i1 >= 0; i1--) { // sketchPath.add((Vec2D) bottomLine.get(i1)); SketchPoint tempP = bottomLine.get(i1); sketchPath.add(tempP); float offsetX = (width / (numSlices - 1)) / 2; if (i1 != bottomLine.size() - 1 && i1 != 0 && !crossSliceSelection.tieToLeg && SETTINGS_SKETCH.Draw_Curves) sketchPath.addBezier(tempP, new Vec2D(tempP.x + offsetX, tempP.y), new Vec2D( tempP.x - offsetX, tempP.y)); } if (controlPointLeft != null) { sketchPath .addBezier((SketchPoint) bezierPointLeft,controlPointLeft, bezierPointLeft); sketchPath.addBezier((SketchPoint) bezierPointRight, bezierPointRight, controlPointRight); } // sketchPath.add((Vec2D) bottomLine.get(bottomLine.size()-1)); // System.out.println(" shape len " + sktOutline.l.size()); // slicePlane.sketchShapes.sketchOutlines.add(sktOutline); // slicePlane.selected = true; // slicePlane.sketchShapes.optimize(); sketchPath.setClosed(true); slicePlane.getSketch().add(sketchPath); slicePlane.getSketch().buildOutline(); slicePlane.generate(); if (crossSliceSelection.tieToLeg) slicePlane.tiedToLeg = true; // slicePlane.unselect(); // slicePlane.selected = false; crossSlices.add(slicePlane); if (crossSliceSelection.tieToLeg) i = 100; float gap = 3f * SETTINGS.scale; float lenPerPecent = guideSpline.getlengthPerPercent() / SETTINGS.scale; float stepOffset = ((SETTINGS.materialThickness / 2) + gap) / (lenPerPecent); float dirSign = -1; float planeOffset = ((SETTINGS.materialThickness / 2) / SETTINGS.scale) * dirSign; if (i < end - step) { SlicePlanes slatPlanes = new SlicePlanes(); slatPlanes.add(planesToSlice); slatPlanes.remove(slicePlane1); slatPlanes.remove(slicePlane2); CrossSliceSelection planeSelection = new CrossSliceSelection( crossSliceSelection.path, crossSliceSelection.plane, i + stepOffset, i - stepOffset + step, planeOffset, crossSliceSelection.parentChair); planeSelection.type = CrossSliceSelection.SLATSLICES; generateSlat(slatPlanes, crossSlices, planeSelection, guideSpline, chair); } } // now take away the very outer planes planesToSlice.remove(0); planesToSlice.remove(planesToSlice.size() - 1); //planesToSlice.removeCollisions(); //planesToSlice.checkForCollisionsSlots(); // if(SETTINGS.EXPERT_MODE && GLOBAL.slicesWidget != null) // GLOBAL.slicesWidget.rebuild(crossSliceSelections); // this.rebuildLength = false; } public static void generateSlices(SlicePlanes planesToSlice, SlicePlanes crossSlices, CrossSliceSelection crossSliceSelection, SketchShape guideSpline) { if(guideSpline == null) return; int numSlices = (int) SETTINGS.slat_num;// (int) float spacing = SETTINGS.chair_width / (numSlices + 1); float scale = 1; float width = 10; /* * ADD fake/guide layers, based on adjacent layers. * * : | | | : * : | | | : * : | | | : * ^ ^ * | | * fake fake */ if (crossSliceSelection.getCapType() != CrossSliceSelection.CAP_INSIDE) { // TODO: option flush edges // Add fake profiles on the ends, this might be a option later for // flush edges ! SlicePlane startSlicePlaneend1 = planesToSlice.getList().get(0); SlicePlane slicePlane1 = new SlicePlane(startSlicePlaneend1 .getSketch().clone(), new Plane(new Vec3D(0, 0, (startSlicePlaneend1.getPlane().z) - SETTINGS.chair_slat_end_size), new Vec3D(0, 0, -1))); slicePlane1.destroy();//a bit of a hack make sure that our temp slice is deleted later slicePlane1.getSketch().removeLegs(); // remove any leg shapes slicePlane1.getSketch().build(); planesToSlice.add(0, slicePlane1); // Add fake profiles on the ends SlicePlane startSlicePlaneend2 = planesToSlice.get(planesToSlice .size() - 1); SlicePlane slicePlane2 = new SlicePlane(startSlicePlaneend2 .getSketch().clone(), new Plane(new Vec3D(0, 0, (startSlicePlaneend2.getPlane().z) + SETTINGS.chair_slat_end_size), new Vec3D(0, 0, -1))); slicePlane2.destroy();//a bit of a hack make sure that our temp slice is deleted later slicePlane2.getSketch().removeLegs(); // remove any leg shapes slicePlane2.getSketch().build(); planesToSlice.add(slicePlane2); } // Add fake profiles on the ends float step = crossSliceSelection.spacing / guideSpline.getlength(); float start = Math.min(crossSliceSelection.start, crossSliceSelection.end); float end = Math .max(crossSliceSelection.start, crossSliceSelection.end); if (SETTINGS.DEBUG) guideSpline.debugPercent = start; //Clear dubugging info off planes if (SETTINGS.DEBUG) { for (int j = 0; j < planesToSlice.size(); j++) { SlicePlane currentPlaneY = planesToSlice.get(j); currentPlaneY.debugIntersectionPoints.clear(); currentPlaneY.debugIntersectionPointsTop.clear(); currentPlaneY.debugIntersectionPointsBottom.clear(); } } /* * Step though the spline to slice * * <pre> * * start * /|/|/|/|/| * 0.1f -----------< --- (crossSliceSelection) * / / / / / * 0.2f -----------< * / / / / / * 0.3f -----------< * |/|/|/|/|/ * end * * </pre> */ for (float i = start; i <= end; i += step) { if (guideSpline == null || i > 1) return; Vec2D sPos = guideSpline.getPos(i); if(sPos == null) return; Vec3D vecPos = new Vec3D(sPos.x, sPos.y, 0); Vec3D vecDir = new Vec3D(guideSpline.getPerpendicular(i).x, guideSpline.getPerpendicular(i).y, 0); vecDir = vecDir.rotateZ(crossSliceSelection.offsetRotation); //vecDir = vecDir.rotateZ((float) (Math.PI )); // do we want to add a strengthening beam to the leg if (crossSliceSelection.tieToLeg) { SketchPoint point1 = (SketchPoint) guideSpline.get(0); SketchPoint point2 = (SketchPoint) guideSpline.get(1); Vec2D dir2D = point1.sub(point2).normalize(); dir2D.rotate((float) (Math.PI / 2)); vecPos = new Vec3D(point1.x, point1.y, 0); vecDir = new Vec3D(dir2D.x, dir2D.y, 0); } SlicePlane slicePlane = new SlicePlane(new Plane(vecPos, vecDir)); slicePlane.setCrossSliceSelection(crossSliceSelection); //slicePlane.getPlane().normal.rotateZ((float) -(Math.PI/2)); SketchPath topLine = new SketchPath(null); SketchPath bottomLine = new SketchPath(null); topLine.setClosed(false); bottomLine.setClosed(false); /* * Slice up each plane * * <pre> * * 1 2 3 4 5 -> (currentPlaneY) go through each plane * * | | | | | * ---------< (crossSliceSelection) calculate intersects * | | | | | * | | | | | * |\ \ \ \ \ * | \ \ \ \ \ * | \ \ \ \ \ * | | | | * | | * | | * * </pre> */ for (int j = 0; j < planesToSlice.size(); j++) { SlicePlane currentPlaneY = planesToSlice.get(j); // get the spline we want to produce cross sections along // do we want to generate planes across the whole profile or // just the current sketch? //TODO: crop to outline minus leg! Object spline = null; if (crossSliceSelection.cropToCurrentShape) { //if(crossSliceSelection.path instanceof SketchSpline) //spline = crossSliceSelection.path; spline = currentPlaneY.getSketch().getFirst(); ////THIS IS WRONG TODO: } else { if (currentPlaneY.getSketch().getSketchShapes().sketchOutlines .getOutterOutline() == null) return; spline = currentPlaneY.getSketch().getSketchShapes().sketchOutlines .getOutterOutline().getPath(); } /** GUIDE SKETCHSPLINE * * <pre> * intersect points based on center of spline and width * ^ * ___|__ __|_____|_____ * \/ _|_ \ / |_____|_____ * /\ / |\ \/ /| | * | \ \____/ * * </pre> **/ // we want to produce cross sections just across the current // sketch if (spline != null && spline instanceof SketchSpline) { SketchSpline currentSpline = (SketchSpline) spline; //LOGGER.info("slices on SketchSpline"); if (currentSpline == null) { crossSliceSelection.destroy(null); return; } currentSpline = currentSpline; Vec2D topIntersect = null; Vec2D bottomIntersect = null; Vec2D intersectX = new Vec2D(-1, -1); Vec2D vecPosOnSpline = currentSpline.getPos(i); if(vecPosOnSpline != null){ vecPos = new Vec3D(vecPosOnSpline.x, vecPosOnSpline.y, currentPlaneY.getPlane().z); Vec2D posPerp = currentSpline.getPerpendicular(i); float percent = -1f; Vec2D percentVec = new Vec2D(-1, -1); posPerp = posPerp.rotate(crossSliceSelection.offsetRotation); currentPlaneY.getIntersectionCentre(slicePlane, currentSpline, i, percentVec, intersectX); if(intersectX.x != -1 && intersectX.y!=-1){ float splineWidth = currentSpline.getOffsetSize(); splineWidth = currentSpline.getOffsetSize(i); // float splineIntersectPercent = percentVec.x; // if (splineIntersectPercent != -1) { Vec2D topIntersectY = null; Vec2D bottomIntersectY = null; float dist = 0; float neartestDist = -1; Vec2D tempPos = percentVec.copy();// currentSpline.getPos(percentVec.x); Vec2D tempDir = posPerp.copy();// currentSpline.getPerpendicular(percentVec.x); if (tempDir == null) break; tempDir = tempDir.normalize(); topIntersect = intersectX.add(new Vec2D(0, splineWidth)); bottomIntersect = intersectX.add(new Vec2D(0, -splineWidth)); // Switch what side slots are added to // slots along Y Vec2D slotPos = new Vec2D(tempPos.x, tempPos.y); Vec2D slotDir = null; if (!crossSliceSelection.flipSide) { slotDir = new Vec2D(vecDir.x, vecDir.y); } else { slotDir = new Vec2D(-vecDir.x, -vecDir.y); } // slotPos.addSelf(slotDir.scale(100)); float offsetToEdge = -1; if (!crossSliceSelection.flipSide) { offsetToEdge = 1; } else { offsetToEdge = -1; } if (!currentPlaneY.guide) { SliceSlot slot = new SliceSlot(slicePlane, slotPos, slotDir, (-splineWidth - offsetToEdge), SETTINGS.materialThickness); slot.swap(); slot.setOnEdge(new Vec2D(0, 1)); if(crossSliceSelection.getCapType() != CrossSliceSelection.CAP_INSIDE) slot.makesEdge = true; currentPlaneY.getSketch().getSlots().add(slot); } if (!currentPlaneY.guide) { if (!crossSliceSelection.flipSide) { if (j != 0 && j != planesToSlice.size() - 1 ){ float yPosSlot = intersectX.y + (splineWidth); SliceSlot slot = new SliceSlot( currentPlaneY, new Vec2D(intersectX.x,yPosSlot), new Vec2D(-1, 0), splineWidth, SETTINGS.materialThickness); if(crossSliceSelection.getCapType() != CrossSliceSelection.CAP_INSIDE) slot.makesEdge = true; slot.setOnEdge(new Vec2D(0,-1)); // these slots start from the middle so this doesn't work, flip slots. //LOGGER.info("X " + slot.getPos().x + " Y " + slot.getPos().y ); slicePlane .getSketch() .getSlots() .add(slot); } } else { if (j != 0 && j != planesToSlice.size() - 1){ SliceSlot slot = new SliceSlot( currentPlaneY, new Vec2D(intersectX.x, intersectX.y - (splineWidth)), new Vec2D(1, 0), splineWidth, SETTINGS.materialThickness); slot.setOnEdge(new Vec2D(0, -1)); if(crossSliceSelection.getCapType() != CrossSliceSelection.CAP_INSIDE) slot.makesEdge = true; slicePlane .getSketch() .getSlots() .add(slot); } } } // if(topIntersect != null){ Vec3D tempVec = new Vec3D(bottomIntersect.x, bottomIntersect.y, 0); tempVec = slicePlane.getWorldPosIntersect(tempVec); if (SETTINGS.DEBUG) { currentPlaneY.debugIntersectionPoints.add(slotPos); currentPlaneY.debugIntersectionPointsTop.add(tempPos .scale(tempDir)); currentPlaneY.debugIntersectionPointsBottom.add(tempPos .scale(tempDir.scale(-1))); } topLine.add(new SketchPoint(bottomIntersect)); bottomLine.add(new SketchPoint(topIntersect)); } } } //} /* ACROSS SKETCHPATH */ /** * Find intersections across cross section * * <pre> * currentPath * ^ * | * ------ x1 -> (topIntersect) * | \/ -------------------------> (crossSliceSelection) * | /\ * | / / * \ / \ * \_/____\ * / * x2 -> (bottomIntersect) * </pre> */ else if (spline instanceof SketchPath) { SketchPath currentPath = (SketchPath) spline; vecPos = new Vec3D(vecPos.x, vecPos.y,currentPlaneY.getPlane().z); //always crop to shape as we work out the correct shape earlier List<List<Vec2D>> points = currentPlaneY.getIntersection( slicePlane, true, currentPath); if (points != null && points.size() > 1) { Vec2D topIntersect = null; Vec2D bottomIntersect = null; Vec2D topIntersectY = null; Vec2D bottomIntersectY = null; Vec2D topIntersectX = null; Vec2D bottomIntersectX = null; float dist = 0; float neartestDist = -1; Vec2D vecStart = new Vec2D(vecPos.x, vecPos.y); //find the closest point for (int k = 0; k < points.size(); k++) { Vec2D vec2d = (Vec2D) points.get(k).get(0); Vec2D vec2dX = (Vec2D) points.get(k).get(1); Vec3D vec3d = new Vec3D(vec2dX.x, vec2dX.y,vecPos.z); float d = vecStart.distanceTo(vec2dX); //Display intersects in debug mode if (SETTINGS.DEBUG) { currentPlaneY.debugIntersectionPoints .add(vec2dX); currentPlaneY.debugIntersetStart = new Vec2D( vecPos.x, vecPos.y); } if (d < neartestDist || neartestDist == -1) { neartestDist = d; topIntersect = vec2d; topIntersectX = vec2dX; } } float MIN_DUPLICATE_DIST = 2; //Remove the closest point for (int k = 0; k < points.size(); k++) { Vec2D vec2d = (Vec2D) points.get(k).get(0); Vec2D vec2dx = (Vec2D) points.get(k).get(1); float distToLastIntersect = topIntersectX .distanceTo(vec2dx); if (vec2d.equals(topIntersect) || distToLastIntersect < MIN_DUPLICATE_DIST) { points.remove(k); k--; } } //find the next closest intersect! neartestDist = -1; for (int k = 0; k < points.size(); k++) { Vec2D vec2d = (Vec2D) points.get(k).get(0); Vec2D vec2dX = (Vec2D) points.get(k).get(1); Vec3D vec3d = new Vec3D(vec2d.x, vec2d.y, currentPlaneY.getPlane().z); float d = vecStart.distanceTo(vec2dX); if ((d < neartestDist && vec2d != topIntersect) || neartestDist == -1) { neartestDist = d; bottomIntersect = vec2d; bottomIntersectX = vec2dX; } } /** * Lets Fix any intersects that are swapped around */ if ((bottomIntersect != null && topIntersect != null) && bottomIntersect.y < topIntersect.y) { Vec2D topIntersectTemp = topIntersect.copy(); Vec2D topIntersectXTemp = topIntersectX.copy(); topIntersect = bottomIntersect; topIntersectX = bottomIntersectX; bottomIntersect = topIntersectTemp; bottomIntersectX = topIntersectXTemp; } /** * Are slats a set height? */ if (crossSliceSelection.getSlatHeight() != 0) { bottomIntersect = topIntersect.add(0, crossSliceSelection.getSlatHeight()); bottomIntersectX = topIntersectX.add(0, crossSliceSelection.getSlatHeight()); } if (SETTINGS.DEBUG) { currentPlaneY.debugIntersectionPointsTop .add(topIntersectX); currentPlaneY.debugIntersectionPointsBottom .add(bottomIntersectX); } //if we have found both intersects then add them to the outline if (bottomIntersect != null && topIntersect != null) { float len = bottomIntersect .distanceTo(topIntersect); if (!currentPlaneY.guide) { if (crossSliceSelection.flipSide) { Vec2D slotPos = bottomIntersectX.copy(); Vec2D dirPerp = new Vec2D(vecDir.x, vecDir.y); dirPerp.rotate((float) (Math.PI / 2)); SliceSlot slot = new SliceSlot(slicePlane, slotPos, new Vec2D( -vecDir.x, -vecDir.y), (len / 2f), SETTINGS.materialThickness); slot.setOnEdge(new Vec2D(0, -1)); if(crossSliceSelection.getCapType() != CrossSliceSelection.CAP_INSIDE) slot.makesEdge = true; currentPlaneY .getSketch() .getSlots() .add(slot); } else { Vec2D slotPos = topIntersectX.copy(); Vec2D dirPerp = new Vec2D(vecDir.x, vecDir.y); dirPerp.rotate((float) (Math.PI / 2)); SliceSlot slot = new SliceSlot(slicePlane, slotPos, new Vec2D(vecDir.x, vecDir.y), (len / 2f), SETTINGS.materialThickness); slot.setOnEdge(new Vec2D(0, -1)); if(crossSliceSelection.getCapType() != CrossSliceSelection.CAP_INSIDE) slot.makesEdge = true; // Switch what side slots are added to currentPlaneY .getSketch() .getSlots() .add(slot); } } Vec2D topSlotintersect = bottomIntersect.copy(); Vec2D bottomtopSlotintersect = topIntersect.copy(); if (!currentPlaneY.guide) { if (!crossSliceSelection.flipSide) { if (j != 0 && j != planesToSlice.getList() .size() - 1){ SliceSlot slot = new SliceSlot( currentPlaneY, topSlotintersect, new Vec2D(-1, 0), (len / 2f), SETTINGS.materialThickness); slot.setOnEdge(new Vec2D(0, -1)); if(crossSliceSelection.getCapType() != CrossSliceSelection.CAP_INSIDE) slot.makesEdge = true; slicePlane .getSketch() .getSlots() .add(slot); } } else { if (j != 0 && j != planesToSlice.getList() .size() - 1){ SliceSlot slot = new SliceSlot( currentPlaneY, bottomtopSlotintersect, new Vec2D(1, 0), ((len / 2f)), SETTINGS.materialThickness); slot.setOnEdge(new Vec2D(0, -1)); if(crossSliceSelection.getCapType() != CrossSliceSelection.CAP_INSIDE) slot.makesEdge = true; slicePlane .getSketch() .getSlots() .add(slot); } } } // if(topIntersect != null){ // add bezier points /** * BUILD the legs * * ________ * |:/ \:| * || || * -- -- */ if (crossSliceSelection.tieToLeg) { // if(crossSliceSelection.tiedToPlanes.contains(currentPlaneY)){ if (crossSliceSelection.legSpline != null && crossSliceSelection.legSpline .getPath() != null && crossSliceSelection.tiedToPlanes.contains(currentPlaneY)) { Vec2D seatBottom = null; if(crossSliceSelection.extendLegSliceToTopOfLeg){ seatBottom = new Vec2D(0,0); }else{ SketchShapes tempSketchShapes = currentPlaneY.getSketch().getSketchShapes().clone(); tempSketchShapes.removeLegs(); tempSketchShapes.sketchOutlines.clear(); tempSketchShapes.buildOutline(); if (tempSketchShapes.sketchOutlines .getOutterOutline() == null) return; SketchShape sktch = tempSketchShapes.sketchOutlines.getOutterOutline().getPath(); List<List<Vec2D>> points02 = currentPlaneY.getIntersection( slicePlane, true, sktch); for (int k = 0; k < points02.size(); k++) { Vec2D vec2d = (Vec2D) points02.get(k).get(0); if(seatBottom == null || vec2d.distanceTo(topIntersect) > vec2d.distanceTo(seatBottom) ) seatBottom = vec2d; } if(seatBottom == null) seatBottom = bottomIntersect; } // this is hard coded so legs are always on // the outside slices! topLine.add(new SketchPoint(topIntersect)); //bottomLine.add(new SketchPoint(bottomIntersect)); float legWidthBottom = 20; float legWidthTop = 20; SketchSpline legSpline = crossSliceSelection.legSpline; float Yoffset = 0; float yMin = 0; SketchPoint p1 = null, p2, p3, p4; if (legSpline.getPath().size() == 4) { p1 = (SketchPoint) legSpline.getPath() .get(1); p2 = (SketchPoint) legSpline.getPath() .get(2); p3 = (SketchPoint) legSpline.getPath() .get(0); p4 = (SketchPoint) legSpline.getPath() .get(3); legWidthBottom = p1.distanceTo(p2); legWidthTop = p3.distanceTo(p4); Yoffset = 0; Vec2D p5 = GLOBAL.uiTools.getPointOnPlane(legSpline.getCentrePath().get(0), slicePlane.getPlane()); Vec2D p6 = GLOBAL.uiTools.getPointOnPlane(legSpline.getCentrePath().get(1), slicePlane.getPlane()); //slicePlane.getPlane().getIntersectionWithRay(r); yMin = 29;//Math.min(p5.y,p6.y); } //Get the last intersect //TODO this will get the intersect to the left, whitch is not always desirable if(bottomLine.size() > 0) Yoffset = bottomLine.get(bottomLine.size()-1).y; //TODO: LEG STUFF //Yoffset = legSpline.getCentrePath().get(0).distanceTo(legSpline.getCentrePath().get(1)); bottomLine.add(new SketchPoint( bottomIntersect.x - (legWidthTop / 2), seatBottom.y)); bottomLine.add(new SketchPoint( bottomIntersect.x - (legWidthBottom / 2), bottomIntersect.y)); // bottomLine.add(new SketchPoint( // bottomIntersect.x, // yMin)); bottomLine.add(new SketchPoint( bottomIntersect.x + (legWidthBottom / 2), bottomIntersect.y)); bottomLine.add(new SketchPoint( bottomIntersect.x + (legWidthTop / 2), seatBottom.y)); } else { topLine.add(new SketchPoint(topIntersect)); bottomLine.add(new SketchPoint( bottomIntersect)); } } else { topLine.add(new SketchPoint(topIntersect)); bottomLine .add(new SketchPoint(bottomIntersect)); } } } } } /** * Check what direction our slats are going in and reverse them if they're going in the wrong dir */ if(topLine.size() > 0 && ((SketchPoint)topLine.get(0)).x > ((SketchPoint)topLine.get(topLine.size()-1)).x){ topLine.reverseWinding(); bottomLine.reverseWinding(); } /** * Should we generate flat tops? * */ // now go around the lines we collected and add them to a // outline Vec2D controlPointLeft = null; Vec2D bezierPointLeft = null; Vec2D controlPointRight = null; Vec2D bezierPointRight = null; if (crossSliceSelection.smooth) { bottomLine.smoothLeft(SETTINGS_SKETCH.SMOOTH_AMOUNT); topLine.smoothRight(SETTINGS_SKETCH.SMOOTH_AMOUNT); } if(crossSliceSelection.generateFlushTops){ float materialWidth = SETTINGS.materialThickness/SETTINGS.scale; //TODO: change this so it is more general for (int i1 = 0; i1 < topLine.size(); i1++) { SketchPoint tempP = topLine.get(i1); SketchPoint pLeft = new SketchPoint(tempP.x+((materialWidth/2)), tempP.y); SketchPoint pRight = new SketchPoint(tempP.x-((materialWidth/2)), tempP.y); if(tempP.containsBezier()){ //pLeft.controlPoint1 = tempP.controlPoint1.copy(); pLeft.controlPoint2 = tempP.controlPoint2.copy(); pRight.controlPoint1 = tempP.controlPoint1.copy(); //pRight.controlPoint2 = tempP.controlPoint2.copy(); } topLine.set(i1,pLeft); topLine.add(i1, pRight); i1++; } if(crossSliceSelection.type != CrossSliceSelection.LEG){ for (int i1 = 0; i1 < bottomLine.size(); i1++) { SketchPoint tempP = bottomLine.get(i1); SketchPoint pLeft = new SketchPoint(tempP.x-(materialWidth/2), tempP.y); SketchPoint pRight = new SketchPoint(tempP.x+(materialWidth/2), tempP.y); if(tempP.containsBezier()){ //pLeft.controlPoint1 = tempP.controlPoint1.copy(); pLeft.controlPoint2 = tempP.controlPoint2.copy(); pRight.controlPoint1 = tempP.controlPoint1.copy(); //pRight.controlPoint2 = tempP.controlPoint2.copy(); } bottomLine.set(i1,pRight); bottomLine.add(i1, pLeft); i1++; } } } /** * <pre> * CAPPING * |-----------| * |-----------| * </pre> */ SketchPath sketchPath = new SketchPath(slicePlane.getSketch()); if (crossSliceSelection.getCapType() == CrossSliceSelection.CAP_ROUND_SQUARE) { // now change the line caping if (bottomLine.size() > 1) { float cornerRad = crossSliceSelection.cornerRadius; bottomLine.add(0, (SketchPoint) bottomLine.get(0).clone()); bottomLine.add((SketchPoint) bottomLine.get( bottomLine.size() - 1).clone()); bottomLine.get(0).addSelf(0, -cornerRad); bottomLine.get(1).addSelf(cornerRad, 0); bottomLine.get(0).controlPoint1 = new Vec2D( bottomLine.get(0).x, bottomLine.get(0).y + cornerRad); bottomLine.get(0).controlPoint2 = bottomLine.get(0).copy(); bottomLine.get(bottomLine.size() - 1) .addSelf(0, -cornerRad); bottomLine.get(bottomLine.size() - 2) .addSelf(-cornerRad, 0); bottomLine.get(bottomLine.size() - 1).controlPoint2 = new Vec2D( bottomLine.get(bottomLine.size() - 1).x, bottomLine.get(bottomLine.size() - 1).y + cornerRad); bottomLine.get(bottomLine.size() - 1).controlPoint1 = bottomLine .get(bottomLine.size() - 1).copy(); topLine.add(0, (SketchPoint) topLine.get(0).clone()); topLine.add((SketchPoint) topLine.get(topLine.size() - 1) .clone()); topLine.get(0).addSelf(0, cornerRad); topLine.get(1).addSelf(cornerRad, 0); topLine.get(0).controlPoint2 = new Vec2D(topLine.get(0).x, topLine.get(0).y - cornerRad); topLine.get(0).controlPoint1 = topLine.get(0).copy(); topLine.get(topLine.size() - 1).addSelf(0, cornerRad); topLine.get(topLine.size() - 2).addSelf(-cornerRad, 0); topLine.get(topLine.size() - 1).controlPoint1 = new Vec2D( topLine.get(topLine.size() - 1).x, topLine.get(topLine.size() - 1).y - cornerRad); topLine.get(topLine.size() - 1).controlPoint2 = topLine .get(topLine.size() - 1).copy(); for (int i1 = 0; i1 < topLine.size(); i1++) { SketchPoint tempP = topLine.get(i1); sketchPath.add(tempP); } for (int i1 = bottomLine.size() - 1; i1 >= 0; i1--) { SketchPoint tempP = bottomLine.get(i1); sketchPath.add(tempP); } } } // adds a curve to the bottom line if (crossSliceSelection.getCapType() == CrossSliceSelection.CAP_CURVE) { // now change the line capping if (bottomLine.size() > 1) { controlPointRight = bottomLine.get(0); bezierPointRight = bottomLine.get(1); controlPointLeft = bottomLine.get(bottomLine.size() - 1); bezierPointLeft = bottomLine.get(bottomLine.size() - 2); bottomLine.remove(0); bottomLine.remove(bottomLine.size() - 1); if (crossSliceSelection.tieToLeg) { controlPointRight = new Vec2D(bezierPointRight.x - 15, bezierPointRight.y); controlPointLeft = new Vec2D(bezierPointLeft.x + 15, bezierPointLeft.y); } } for (int i1 = 0; i1 < topLine.size(); i1++) { SketchPoint tempP = topLine.get(i1); sketchPath.add(tempP); float offsetX = (width / (numSlices - 1)) / 2; if (i1 != 0 && i1 != topLine.size() - 1 && SETTINGS_SKETCH.Draw_Curves) sketchPath.addBezier(tempP, new Vec2D(tempP.x - offsetX, tempP.y), new Vec2D(tempP.x + offsetX, tempP.y)); } for (int i1 = bottomLine.size() - 1; i1 >= 0; i1--) { SketchPoint tempP = bottomLine.get(i1); sketchPath.add(tempP); float offsetX = (width / (numSlices - 1)) / 2; if (i1 != bottomLine.size() - 1 && i1 != 0 && !crossSliceSelection.tieToLeg && SETTINGS_SKETCH.Draw_Curves) sketchPath.addBezier(tempP, new Vec2D(tempP.x + offsetX, tempP.y), new Vec2D(tempP.x - offsetX, tempP.y)); } if (controlPointLeft != null) { sketchPath.addBezier((SketchPoint) bezierPointLeft, controlPointLeft, bezierPointLeft); sketchPath.addBezier((SketchPoint) bezierPointRight, bezierPointRight, controlPointRight); } } if (crossSliceSelection.getCapType() == CrossSliceSelection.CAP_BUTT || crossSliceSelection.getCapType() == CrossSliceSelection.CAP_INSIDE) { bottomLine.get(0).removeBezier(); bottomLine.get(bottomLine.size() - 1).removeBezier(); topLine.get(0).removeBezier(); topLine.get(topLine.size() - 1).removeBezier(); for (int i1 = 0; i1 < topLine.size(); i1++) { SketchPoint tempP = topLine.get(i1); sketchPath.add(tempP); } for (int i1 = bottomLine.size() - 1; i1 >= 0; i1--) { SketchPoint tempP = bottomLine.get(i1); sketchPath.add(tempP); } } sketchPath.setClosed(true); slicePlane.getSketch().add(sketchPath); slicePlane.getSketch().buildOutline(); slicePlane.generate(); if (crossSliceSelection.tieToLeg) slicePlane.tiedToLeg = true; crossSlices.add(slicePlane); if (crossSliceSelection.tieToLeg) i = 100; } if (crossSliceSelection.getCapType() != CrossSliceSelection.CAP_INSIDE) { // now take away the very outer planes planesToSlice.remove(0); planesToSlice.remove(planesToSlice.size() - 1); } //planesToSlice.removeCollisions(); //planesToSlice.checkForCollisions(); } }