import uk.co.nickthecoder.foocad.extras.v1.* import static uk.co.nickthecoder.foocad.extras.v1.Extras.* import uk.co.nickthecoder.foocad.compound.v1.* import uk.co.nickthecoder.foocad.smartextrusion.v1.* import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.* import uk.co.nickthecoder.foocad.screws.v3.* class BoxConstruction : Model { @Custom( about="The width and thickness of the wooden struts" ) var crossSection = Vector2( 20, 16 ) @Custom( about="Radius of the rounding on the wooden struts" ) var insideRadius = 1 @Custom( about="Extra height to support the corners" ) var cornerExtra = 20 @Custom( about="Extra height to support angle braces" ) var angleExtra = 10 @Custom( about="Wall thickness" ) var thickness = 3.0 @Custom var chamfer = 1 @Custom var holeChamfer = 0.4 var screwHole : Shape3d = Countersink().depth(6) meth inside() = Square( crossSection ).center().roundAllCorners( insideRadius ) // length is the length of wood that fits in. The piece is taller by jointThickness. meth endStop( length : double ) : Shape3d { val inside = inside() val outside = inside.offset( thickness ) .roundAllCorners( chamfer ) val solid = outside.smartExtrude( length + thickness ) .bottom( Chamfer( chamfer ) ) .top( Chamfer( chamfer ) ) val hole = inside.smartExtrude( length + 0.01 ) .top( Chamfer( holeChamfer ).reverse() ) .translateZ( thickness ) return solid.remove(hole) } meth through( length : double ) : Shape3d { val inside = inside() val outside = inside.offset( thickness ) .roundAllCorners( chamfer ) val solid = outside.smartExtrude( length ) .bottom( Chamfer( chamfer ) ) .top( Chamfer( chamfer ) ) val hole = inside.smartExtrude( length + 0.02 ) //.edges( Chamfer( holeChamfer ).reverse() ) .translateZ( -0.01 ) return solid.remove(hole) } @Piece meth jointA() : Shape3d { val up = endStop( crossSection.x + thickness + cornerExtra ).toOrigin() val right = endStop( crossSection.x + thickness + cornerExtra ).toOrigin().rotateY(90).bottomTo(0) val holes = Compound().apply { + screwHole.rotateX(-90) .translateX( crossSection.x/2 + thickness ) .translateY( crossSection.y + thickness*2 ) .translateZ( up.top - cornerExtra/2 ) +screwHole.rotateX(-90) .translateX( right.right - cornerExtra/2 ) .translateZ( crossSection.x/2 + thickness ) .translateY( up.back ) }.build() return up and right remove holes } @Piece meth jointB() : Shape3d { val up = endStop( crossSection.x + thickness + cornerExtra ).toOrigin() val forward = endStop( crossSection.y + thickness + cornerExtra ) .rotateZ(90).rotateX(-90).toOrigin() val holes = Compound().apply { + screwHole.rotateY(90) .translateX( forward.right ) .translateY( forward.back - cornerExtra / 2 ) .translateZ( forward.top / 2 ) + screwHole.rotateX(-90) .translateX( crossSection.x/2 + thickness ) .translateY( crossSection.y + thickness*2 ) .translateZ( up.top - cornerExtra/2 ) }.build() return up and forward remove holes } @Piece meth jointC() : Shape3d { val forward = endStop( crossSection.y + thickness + cornerExtra ).rotateZ(90).toOrigin() val right = endStop( crossSection.y + thickness + cornerExtra ).rotateZ(90).rotateY(90).toOrigin() val holes = Compound().apply { + screwHole.rotateY(90) .translateX( crossSection.y + thickness*2 ) .translateY( crossSection.x/2 + thickness ) .translateZ( forward.top - cornerExtra/2 ) + screwHole .translateX( right.right - cornerExtra/2 ) .translateY( right.back/2 ) .translateZ( right.top ) }.build().color("Red") return (forward and right) remove holes } @Piece meth cornerA() : Shape3d { val one = jointA() val forward = endStop( crossSection.y + thickness + cornerExtra ) .rotateZ(90).rotateX(-90).toOrigin() val holes = Compound().apply { + screwHole.rotateY(90) .translateX( forward.right ) .translateY( forward.back - cornerExtra / 2 ) .translateZ( forward.top / 2 ) }.build() return one and forward remove holes } @Piece meth cornerB() : Shape3d { val one = jointA().rotateZ(90).mirrorX() val right = endStop( crossSection.y + thickness + cornerExtra ) .rotateY(90).toOrigin().leftTo(one.left) val hole = screwHole.rotateX(-90) .translateX( right.right - cornerExtra / 2 ) .translateY( right.back ) .translateZ( right.top / 2 ) return one and right remove hole } meth angledCornerA(angle : double) : Shape3d { val radians = angle / 180 * Math.PI val flat = jointC().rotateX(-90).bottomTo(0) val up = endStop( cornerExtra + crossSection.y * Math.sin(Math.abs(radians)) ) val hole = screwHole.rotateY(90) .translateX( up.right ) .translateZ( up.top - cornerExtra / 2 ) .color("Red") val deltaZ = if (angle < 0) { -crossSection.y * Math.sin(-radians) } else { 0.0 } val angledUp = (up remove hole) .translateZ( deltaZ ) .frontTo(-thickness).rotateX(-angle).translateY(thickness) .leftTo(0) .translateZ( flat.top - thickness*2 ) return flat and angledUp } meth angledCornerB(angle : double) : Shape3d { val radians = angle / 180 * Math.PI val flat = jointC().rotateX(-90).bottomTo(0) val up = endStop( cornerExtra + crossSection.x * Math.sin(Math.abs(radians)) ).rotateZ(90) val hole = screwHole.rotateY(90) .translateX( up.right ) .translateZ( up.top - cornerExtra/2 ) .color("Red") val deltaZ = if (angle < 0) { -crossSection.x * Math.sin(-radians) } else { 0.0 } val angledUp = (up remove hole) .translateZ( deltaZ ) .frontTo(0).rotateX(-angle) .translateZ( flat.top - thickness*2 ) .leftTo(0) return flat and angledUp } @Piece meth cornerReinforcement() : Shape3d = cornerReinforcement( 90, thickness ) meth cornerReinforcement( size : double, panelThickness : double ) : Shape3d { val clearance = 0.3 val forCorner = Square( thickness, thickness + cornerExtra ) .offset(clearance) .rotate(-90).frontTo(-clearance).also(2) val flatShape = Triangle( size+thickness, size+thickness ).intersection(Square(size)) - forCorner val flat = flatShape.toPolygon() .smartExtrude( panelThickness ) .bottom( Chamfer(chamfer) ) val flap = Square( flatShape.back - forCorner.back, screwHole.size.x + 4 ) .roundCorner(1,chamfer) .roundCorner(0,chamfer) .smartExtrude(thickness) .top( Chamfer( chamfer ) ) val screwHoles = screwHole .translateZ(flap.top) .centerYTo( flap.middle.y ) .centerXTo( 10 ) .centerXTo( flap.size.x - 10 ).also() val flaps = (flap remove screwHoles) .rotateX(-90).bottomTo(0) .leftTo( forCorner.back ) .bottomTo( flat.top - chamfer ) return flat + flaps.rotateZ(90).mirrorX().also(2) } @Piece meth spacer() : Shape3d { val shape = Square( crossSection ).offset(-1 ) .center().roundAllCorners(3) val hole = Circle( 3 ) return (shape - hole).smartExtrude( thickness ) .edges( Chamfer(0.6) ) } @Piece meth spacer(length : double) : Shape3d { val shape = Square( length, crossSection.y ).offset(-1 ) .center().roundAllCorners(3) val holes = Circle( 3 ) .leftTo( shape.left + 10 ).mirrorX().also() return (shape - holes).smartExtrude( thickness ) .edges( Chamfer(0.6) ) } meth angleA( angle : double ) : Shape3d { val radians = angle / 180 * Math.PI val l1 = (crossSection.x + thickness*2) / Math.cos( radians ) val l2 = (crossSection.x + thickness*2) * Math.tan( radians ) + angleExtra + thickness val part1 = through( l1 ).rightTo( thickness ) val screwHole1 = screwHole .rotateY(-90) .leftTo(part1.left) .translateZ( part1.top *0.66 ) val part2 = through( l2 ) val screwHole2 = screwHole.rotateY(90) .translateX( crossSection.x/2 + thickness ) .translateZ( l2 - 3 - angleExtra/2 ) val angled = (part2 remove screwHole2) .rightTo( thickness ) .rotateY( 90-angle ) .bottomTo(0) return part1 and angled remove screwHole1 } meth angleB( angle : double ) : Shape3d { val radians = angle / 180 * Math.PI val l1 = (crossSection.y + thickness*2) / Math.cos( radians ) val l2 = (crossSection.y + thickness*2) * Math.tan( radians ) + angleExtra + thickness val part1 = through( l1 ).frontTo(-thickness) val screwHole1 = screwHole .rotateX(90) .frontTo( part1.front ) .translateZ( part1.top * 0.66 ) val part2 = through( l2 ) val screwHole2 = screwHole.rotateX(-90) .translateY( crossSection.y/2 + thickness ) .translateZ( l2 - 3 - angleExtra/2 ) val angled = (part2 remove screwHole2) .backTo( thickness ) .rotateX( -90 + angle ) .translateY( crossSection.y ) .bottomTo(0) return part1 and angled remove screwHole1 } // TODO Buggy when crossSection.x much larger than crossSection.y. // i.e. only works when the wood is roughly square. meth angleC( angle : double ) : Shape3d { val radians = angle / 180 * Math.PI val l1 = (crossSection.x + thickness*2) / Math.cos( radians ) val l2 = (crossSection.y + thickness*2) * Math.tan( radians ) + angleExtra + thickness val part1 = through( l1 ).rotateZ(90).rightTo(thickness).frontTo(0) val screwHole1 = screwHole .rotateY(-90) .translateY(crossSection.y/2 + thickness) .leftTo( part1.left ) .translateZ( part1.top * 0.66 ) val part2 = through( l2 ) val screwHole2 = screwHole.rotateY(90) .translateX( part2.right ) .translateZ( l2 * 0.5 ) val angled = (part2 remove screwHole2) .rightTo( thickness ) .rotateY( 90 - angle ) .bottomTo(0) .frontTo(0) return part1 and angled remove screwHole1 } @Piece meth angleA45() = angleA( 45 ) @Piece meth angleB45() = angleB( 45 ) @Piece meth angleC45() = angleC( 45 ) @Piece meth straightConnectorA() = angleA(0) @Piece meth straightConnectorB() = angleB(0) override fun build() : Shape3d { val height = 350 val width = 300 val length = 500 val sides = Cube( crossSection.y, length, crossSection.x ) .leftTo( -width/2 ).centerY() .mirrorX().also() .topTo( height ).also() .color("Green") val frontAcross = Cube( width - crossSection.y*2, crossSection.y, crossSection.x ) .centerX().frontTo( -length/2 ) .mirrorY().also() .topTo( height ).also() .color("LightGreen") val uprights = Cube( crossSection.y, crossSection.x, height - crossSection.x * 2 ) .bottomTo( crossSection.x ) .leftTo( sides.left ) .frontTo( sides.front ) .mirrorX().also().mirrorY().also() .color("DarkGreen") val corners = cornerB() .leftTo( -width/2 - thickness ).frontTo(-length/2-thickness).bottomTo(-thickness) .mirrorX().also().mirrorY().also().mirrorZ().translateZ(height).also(2) val cornerReinforcement = cornerReinforcement() .rotateY(-90) .rightTo( uprights.right ) .frontTo( uprights.front + crossSection.x ) .bottomTo( crossSection.x ) return Compound().apply { + sides + frontAcross + uprights + corners + cornerReinforcement }.build() } }