/******************************************************************************* * 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/>. ******************************************************************************/ //#IF JAVA package cc.sketchchair.sketch; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.util.ArrayList; import cc.sketchchair.core.GLOBAL; import cc.sketchchair.core.LOGGER; import cc.sketchchair.core.SETTINGS; import cc.sketchchair.functions.functions; import cc.sketchchair.geometry.SlicePlane; import processing.core.PGraphics; import toxi.geom.Plane; import toxi.geom.Vec2D; /** * SketchSlot represents the slot created between two overlapping SketchPlanes in order to join them. * Parameters can be used to change the tolerance of the joint. * @author gregsaul * */ //#ENDIF JAVA public class SliceSlot { public static final int SLOT = 1; public static final int FINGER = 2; public static final int SLOTS_AND_FINGER = 3; private Vec2D pos; Vec2D dir; float slotLen; float width_mm; float width_screen; private Vec2D linkedVec1; private Vec2D linkedVec2; private float t; private SketchSpline onSpline; public Plane constrainPlane = null; public boolean destroy = false; public SlicePlane slice = null; private float fingerLen; private int numSlots = 1; private boolean startOnSlot = true; public int type = SLOT; private boolean onEdge; private Vec2D dirToEdge; public float fingerTollerance = 0.0f; public boolean makesEdge = false; public SliceSlot(SlicePlane slice, Vec2D pos, Vec2D dir, float len, float width) { this.setPos(pos); this.slice = slice; this.dir = dir; this.slotLen = len; this.width_mm = width; } public SliceSlot(SlicePlane slice, Vec2D pos, Vec2D dir, float slotLen, float fingerLen, float width, int numSlots, boolean startOnSlot,int type) { this.setPos(pos); this.slice = slice; this.dir = dir; this.slotLen = slotLen; this.fingerLen = fingerLen; this.numSlots = numSlots; this.width_mm = width; this.startOnSlot = startOnSlot; this.type = type; } public SliceSlot(SlicePlane slice, Vec2D pos, Vec2D dir, float t, SketchSpline onSpline, float slotLen, float fingerLen, float width, int numSlots, boolean startOnSlot, int type) { this.setPos(pos); this.slice = slice; this.dir = dir; this.slotLen = slotLen; this.fingerLen = fingerLen; this.width_mm = width; // this.linkedVec1 = linkedVec1; // this.linkedVec2 = linkedVec2; this.t = t; this.onSpline = onSpline; this.startOnSlot = startOnSlot; this.numSlots = numSlots; this.type = type; } public void buildSketchOutlines(SketchOutlines sketchOutlines, Sketch parentSketch) { float slotOffset = 0; for (int i = 1; i < numSlots + 1; i++) { if (i % 2 == 0) { if (!this.startOnSlot) { SketchOutline sltOutline = new SketchOutline(parentSketch); sltOutline.getPath().setPath( getOutline(slotOffset, this.slotLen)); sketchOutlines.add(sltOutline); slotOffset += this.slotLen; } else { slotOffset += this.fingerLen; } } else { if (!this.startOnSlot) { slotOffset += this.fingerLen; } else { SketchOutline sltOutline = new SketchOutline(parentSketch); sltOutline.getPath().setPath( getOutline(slotOffset, this.slotLen)); sketchOutlines.add(sltOutline); slotOffset += this.slotLen; } } } } public void checkForCollision(SliceSlot otherSlot) { float tempWidthThis = slice.thickness / SETTINGS.scale; Vec2D corner1This = this.getPos().add(this.dir.getRotated(0).scale( tempWidthThis / 2)); Vec2D corner2This = this.getPos().add(this.dir.getRotated(0).scale( -tempWidthThis / 2)); Vec2D corner3This = this.getPos().add( this.dir.getRotated((float) Math.PI / 2).scale(this.slotLen)) .add(dir.getRotated(0).scale(-tempWidthThis / 2)); Vec2D corner4This = this.getPos().add( this.dir.getRotated((float) Math.PI / 2).scale(this.slotLen)) .add(dir.getRotated(0).scale(tempWidthThis / 2)); float tempWidthThat = otherSlot.slice.thickness / SETTINGS.scale; Vec2D corner1That = otherSlot.getPos().add(otherSlot.dir.getRotated(0) .scale(tempWidthThis / 2)); Vec2D corner2That = otherSlot.getPos().add(otherSlot.dir.getRotated(0) .scale(-tempWidthThis / 2)); //not very scientific add 10 units to the end of each path to make sure it breaks though the edge Vec2D corner3That = otherSlot.getPos().add( otherSlot.dir.getRotated((float) Math.PI / 2).scale( otherSlot.slotLen + 10)).add( dir.getRotated(0).scale(-tempWidthThat / 2)); Vec2D corner4That = otherSlot.getPos().add( otherSlot.dir.getRotated((float) Math.PI / 2).scale( otherSlot.slotLen + 10)).add( dir.getRotated(0).scale(tempWidthThat / 2)); if (functions .intersect(corner1This, corner2This, corner3This, corner4This, corner1That, corner2That, corner3That, corner4That) == functions.DO_INTERSECT) { if (this.slice.tiedToLeg) { if (otherSlot.slice != null) { otherSlot.destroy(); otherSlot.slice.destroy(); } } else if (otherSlot.slice.tiedToLeg) { if (this.slice != null) { this.slice.destroy(); this.destroy(); } } else { if (this.slice != null) { this.slice.destroy(); this.destroy(); } } } } public SliceSlot clone() { SliceSlot newSlot = new SliceSlot(this.slice, this.getPos(), this.dir, this.t, this.onSpline, this.slotLen, this.fingerLen, this.width_mm, this.numSlots, this.startOnSlot, this.type); return newSlot; } void destroy() { this.destroy = true; } public ArrayList<SketchPoint> getOutline(float offset, float sLen) { return this.getOutline(offset, sLen,0); } public ArrayList<SketchPoint> getOutline(float offset, float sLen,float extendLen) { ArrayList<SketchPoint> vec2Ds = new ArrayList<SketchPoint>(); if(sLen > 0) extendLen = -extendLen; float tempWidth = slice.thickness / SETTINGS.scale; float tempLength = sLen + (fingerTollerance * 2)-(extendLen*2); Vec2D corner1 = getPos().add( dir.getRotated((float) Math.PI / 2).scale(offset+extendLen)).add( dir.getRotated(0).normalize().scale(tempWidth / 2)); Vec2D corner2 = getPos().add( dir.getRotated((float) Math.PI / 2).scale(offset+extendLen)).add( dir.getRotated(0).normalize().scale(-tempWidth / 2)); Vec2D corner3 = getPos().add( dir.getRotated((float) Math.PI / 2).scale(offset + tempLength+extendLen)) .add(dir.getRotated(0).scale(-tempWidth / 2)); Vec2D corner4 = getPos().add( dir.getRotated((float) Math.PI / 2).scale(offset + tempLength+extendLen)) .add(dir.getRotated(0).scale(tempWidth / 2)); vec2Ds.add(new SketchPoint(corner1)); vec2Ds.add(new SketchPoint(corner2)); vec2Ds.add(new SketchPoint(corner3)); vec2Ds.add(new SketchPoint(corner4)); return vec2Ds; } void getOutlineGeneralpath(float offset, Area outlineArea, float sLen) { //#IF JAVA GeneralPath gPath = new GeneralPath(); float tempWidth = slice.thickness / SETTINGS.scale; float tempLength = sLen + (fingerTollerance * 2) + GLOBAL.SketchGlobals.extendSlots; /* s1 _______ | | s4 | | s2 |_______| start point s3 start point _________|______________ | | | | --> slot | | |_| */ Vec2D corner1, corner2, corner3, corner4; float s1 = 0, s2 = 0, s3 = 0, s4 = 0; if (this.onEdge) { float extendLen = GLOBAL.SketchGlobals.slotPierceLen; if (dirToEdge.equalsWithTolerance(new Vec2D(0, 1), 1f)) s3 = +extendLen; if (dirToEdge.equalsWithTolerance(new Vec2D(1, 0), 1f)) s2 = extendLen; if (dirToEdge.equalsWithTolerance(new Vec2D(0, -1), 1f)) s3 = -extendLen; if (dirToEdge.equalsWithTolerance(new Vec2D(-1, 0), 1f)) s4 = extendLen; } //tempWidth = tempWidth * (.1f / SETTINGS.scale ); corner1 = getPos().add( dir.getRotated((float) Math.PI / 2).scale(offset + s3)).add( dir.getRotated(0).normalize().scale((tempWidth / 2) + s4)); corner2 = getPos().add( dir.getRotated((float) Math.PI / 2).scale(offset + s3)).add( dir.getRotated(0).normalize().scale((-tempWidth / 2) - s2)); corner3 = getPos().add( dir.getRotated((float) Math.PI / 2).scale( (offset + tempLength) - s1)).add( dir.getRotated(0).normalize().scale((-tempWidth / 2) - s2)); corner4 = getPos().add( dir.getRotated((float) Math.PI / 2).scale( (offset + tempLength) - s1)).add( dir.getRotated(0).normalize().scale((tempWidth / 2) + s4)); gPath.moveTo(corner1.x, corner1.y); gPath.moveTo(corner2.x, corner2.y); gPath.lineTo(corner3.x, corner3.y); gPath.lineTo(corner4.x, corner4.y); gPath.lineTo(corner1.x, corner1.y); outlineArea.add(new Area(gPath)); if (GLOBAL.shapePack.add_guide_divets) { float r = GLOBAL.shapePack.inner_corner_radius; Vec2D radCentre1 = getPos().add( dir.getRotated((float) Math.PI / 2).scale(-(r / 2))).add( dir.getRotated(0).normalize().scale(-tempWidth / 2)); Vec2D radCentre2 = getPos().add( dir.getRotated((float) Math.PI / 2).scale(-(r / 2))).add( dir.getRotated(0).normalize().scale(tempWidth / 2)); Ellipse2D.Float ellipse = new Ellipse2D.Float(radCentre1.x - (r / 2), radCentre1.y - (r / 2), r, r); outlineArea.add(new Area(ellipse)); ellipse = new Ellipse2D.Float(radCentre2.x - (r / 2), radCentre2.y - (r / 2), r, r); outlineArea.add(new Area(ellipse)); } if (GLOBAL.shapePack.addDogbones) { float r = GLOBAL.shapePack.inner_corner_radius; //LOGGER.info(dirToEdge.toString()); int sign = -1; Vec2D radCentre1 = getPos().add( dir.getRotated((float) Math.PI / 2).scale( (offset + tempLength+(r / 2)) - s1)).add( dir.getRotated(0).normalize().scale((-(tempWidth+(r / 2)) / 2) - s2)); Vec2D radCentre2 = getPos().add( dir.getRotated((float) Math.PI / 2).scale( (offset + tempLength+(r / 2)) - s1)).add( dir.getRotated(0).normalize().scale((+(tempWidth+(r / 2)) / 2) - s2)); Ellipse2D.Float ellipse = new Ellipse2D.Float(radCentre1.x - (r / 2), radCentre1.y + ((r / 2)*sign), r, r); outlineArea.add(new Area(ellipse)); ellipse = new Ellipse2D.Float(radCentre2.x - (r / 2), radCentre2.y + ((r / 2)*sign), r, r); outlineArea.add(new Area(ellipse)); } //#ENDIF JAVA } public Area getOutlineGeneralPath() { Area outlineArea = new Area(); float slotOffset = 0; for (int i = 1; i < numSlots + 1; i++) { if (i % 2 == 0) { if (!this.startOnSlot) { getOutlineGeneralpath(slotOffset, outlineArea, slotLen); slotOffset += this.slotLen; } else { slotOffset += this.fingerLen; } } else { if (!this.startOnSlot) { slotOffset += this.fingerLen; } else { getOutlineGeneralpath(slotOffset, outlineArea, slotLen); slotOffset += this.slotLen; } } } return outlineArea; } public Area getOutlineGeneralPathFingers() { Area outlineArea = new Area(); float slotOffset = 0; for (int i = 1; i < numSlots + 1; i++) { if (i % 2 == 0) { if (this.startOnSlot) { getOutlineGeneralpath(slotOffset, outlineArea, slotLen); slotOffset += this.slotLen; } else { slotOffset += this.fingerLen; } } else { if (this.startOnSlot) { slotOffset += this.fingerLen; } else { getOutlineGeneralpath(slotOffset, outlineArea, slotLen); slotOffset += this.slotLen; } } } return outlineArea; } public void renderEdge(PGraphics g) { //if we're not making a edge //if(!makesEdge) // return; g.pushMatrix(); g.translate(this.getPos().x, this.getPos().y, 0.0f); g.rotate(-(functions.angleOf(this.dir))); float tempThickness = slice.thickness / SETTINGS.scale; g.line(-(tempThickness / 2), 0,-(tempThickness / 2),this.slotLen*2); g.line((tempThickness / 2), 0,(tempThickness / 2),this.slotLen*2); g.line(-(tempThickness / 2), 0,-(tempThickness / 2),0); g.line(-(tempThickness / 2), this.slotLen*2,-(tempThickness / 2),this.slotLen*2); g.popMatrix(); } void render(PGraphics g) { //#IF JAVA if(SETTINGS.DEBUG){ g.stroke(255,255,0); SketchPath path = new SketchPath(this.slice.getSketch(),this.getOutline(0, this.slotLen,GLOBAL.SketchGlobals.slotPierceLen)); path.build(); //path.setClosed(true); path.render(g); return; } //#ENDIF JAVA g.pushMatrix(); g.translate(this.getPos().x, this.getPos().y, 1f); g.rotate(-(functions.angleOf(this.dir))); // g.translate(this.width/2,0); float tempThickness = slice.thickness / SETTINGS.scale; // g.ellipse(0,0,tempThickness,tempThickness); for (int i = 1; i < numSlots + 1; i++) { if (i % 2 == 0) { if (!this.startOnSlot) { if (SETTINGS.DEBUG) { g.ellipse(0, 0, 10, 10); if (this.onEdge) { g.line(0, this.slotLen / 2, (this.dirToEdge.y * 10), (this.slotLen / 2) + (this.dirToEdge.x * 10)); } } g.pushMatrix(); g.translate(0, 0,(tempThickness/2)); g.rect(-(tempThickness / 2), 0, (tempThickness), this.slotLen); g.popMatrix(); g.pushMatrix(); g.translate(0, 0,-(tempThickness/2)); g.rect(-(tempThickness / 2), 0, (tempThickness), this.slotLen); g.popMatrix(); g.translate(0, this.slotLen); } else { g.translate(0, this.fingerLen); } } else { if (!this.startOnSlot) { g.translate(0, this.fingerLen); } else { if (SETTINGS.DEBUG) { g.ellipse(0, 0, 10, 10); if (this.onEdge) { g.line(0, this.slotLen / 2, (this.dirToEdge.y * 10), (this.slotLen / 2) + (this.dirToEdge.x * 10)); } } g.pushMatrix(); g.translate(0, 0,(tempThickness/2)); g.rect(-(tempThickness / 2), 0, (tempThickness), this.slotLen); g.popMatrix(); g.pushMatrix(); g.translate(0, 0,-(tempThickness/2)); g.rect(-(tempThickness / 2), 0, (tempThickness), this.slotLen); g.popMatrix(); g.translate(0, this.slotLen); } } } g.popMatrix(); } public void setFingerTollerance(float t) { fingerTollerance = t; } public void setOnEdge(Vec2D dirToEdge) { this.onEdge = true; this.dirToEdge = dirToEdge; } public void swap() { getPos().addSelf(dir.getRotated((float) Math.PI / 2).scale((this.slotLen))); dir.rotate((float) Math.PI); } void update() { this.setPos(this.onSpline.getPos(this.t)); this.dir = this.onSpline.getPerpendicular(this.t); if (this.constrainPlane != null) { this.constrainPlane.x = this.getPos().x; this.constrainPlane.y = this.getPos().y; this.constrainPlane.normal.x = this.dir.x; this.constrainPlane.normal.y = this.dir.y; } } /** * @return the pos */ public Vec2D getPos() { return pos; } /** * @param pos the pos to set */ public void setPos(Vec2D pos) { this.pos = pos; } public void removeNonPiercing(Sketch outlineSketch) { //#IF JAVA SketchPath path = new SketchPath(this.slice.getSketch(),this.getOutline(0, this.slotLen,GLOBAL.SketchGlobals.slotPierceLen)); path.build(); //#ENDIF JAVA boolean intersectionFound = false; for(int i = 0; i < outlineSketch.getSketchShapes().sketchOutlines.l.size();i++){ SketchOutline outline = outlineSketch.getSketchShapes().sketchOutlines.l.get(i); if(outline.getPath().intersects(path)) intersectionFound = true; } if(!intersectionFound && !this.slice.tiedToLeg){ if (this.slice != null) { this.slice.destroy(); this.destroy(); } } } public void removeTrappedSlots(Sketch outlineSketch) { //#IF JAVA SketchPath path = new SketchPath(this.slice.getSketch(),this.getOutline(0, -this.slotLen,GLOBAL.SketchGlobals.slotPierceLen)); path.setClosed(true); path.build(); //#ENDIF JAVA boolean intersectionFound = false; for(int i = 0; i < outlineSketch.getSketchShapes().sketchOutlines.l.size();i++){ SketchOutline outline = outlineSketch.getSketchShapes().sketchOutlines.l.get(i); if(outline.getPath().intersectsCount(path) > 2 && !this.slice.tiedToLeg){ this.slice.destroy(); this.destroy(); } }} }