fix to avoid coincident edges that mess up OpenSCAD meshes
This commit is contained in:
105
src/case.scad
105
src/case.scad
@@ -1,67 +1,76 @@
|
||||
include <lib/defaults.scad>
|
||||
use <lib/primitives.scad>
|
||||
use <lib/transformations.scad>
|
||||
|
||||
// thickness of case walls
|
||||
Thickness = 3.5;
|
||||
Thickness = 2.5;
|
||||
// how large of cuts (radial) to make around each component
|
||||
MarginCameraCut = 2;
|
||||
MarginButtonsCut = 2.8;
|
||||
MarginPortsCut = 5.5;
|
||||
MarginPortsCut = 7.0;
|
||||
|
||||
MarginButtonsSeat = 1.5;
|
||||
// radial margin; this value (halved) becomes the margin for thickness
|
||||
MarginButtonsSeat = 1.4;
|
||||
// how far into the xy plane to extend the part of the case which covers the front of the phone.
|
||||
// the top of the phone contains stuff we don't want to cover (camera); the bottom has more margin
|
||||
FrontOverhangX = 4;
|
||||
FrontOverhangTop = 5;
|
||||
FrontOverhangBot = 7;
|
||||
FrontOverhangX = 3;
|
||||
FrontOverhangTop = 3.5;
|
||||
FrontOverhangBot = 6;
|
||||
// shift the overhang into the screen, to account that the case has some flex
|
||||
FrontSquashZ = 1.2;
|
||||
|
||||
// gives a 1cm margin around the whole body of the phone, but only in the xy plane.
|
||||
module _PhoneBulkLayer(tol=tol)
|
||||
// gives a wide margin around the whole body of the phone, but only in the xy plane.
|
||||
module _PhoneBulkLayer(zMin=0)
|
||||
{
|
||||
minkowski() {
|
||||
children(/*body*/);
|
||||
cube([20, 20, tol], center=true);
|
||||
}
|
||||
hull() {
|
||||
translate([-500, -500, zMin]) extractZDomain() children(/*body*/);
|
||||
translate([ 500, -500, zMin]) extractZDomain() children(/*body*/);
|
||||
translate([ 500, 500, zMin]) extractZDomain() children(/*body*/);
|
||||
translate([-500, 500, zMin]) extractZDomain() children(/*body*/);
|
||||
};
|
||||
}
|
||||
|
||||
/// gives as 1cm margin around the whole body of the phone,
|
||||
/// on all axis EXCEPT the -z axis
|
||||
module _PhoneBulkAndBack()
|
||||
/// gives a margin around the whole body of the phone,
|
||||
/// on all axes EXCEPT the -z axis
|
||||
module _PhoneBulkAndBack(zMin=0)
|
||||
{
|
||||
minkowski() {
|
||||
children(/*body*/);
|
||||
translate([0, 0, 5+tol]) cube([20, 20, 10], center=true);
|
||||
hull() {
|
||||
_PhoneBulkLayer(zMin=zMin) children(/*body*/);
|
||||
translate([0, 0, 10]) _PhoneBulkLayer(zMin=zMin) children(/*body*/);
|
||||
}
|
||||
}
|
||||
|
||||
module _DilatedBody(ButtonStyle) {
|
||||
body = 0;
|
||||
buttons = 1;
|
||||
minkowski() {
|
||||
sphere(r=Thickness);
|
||||
union() {
|
||||
union() {
|
||||
minkowski() {
|
||||
sphere(r=Thickness);
|
||||
children(body);
|
||||
if (ButtonStyle == "extrude") {
|
||||
};
|
||||
if (ButtonStyle == "extrude") {
|
||||
minkowski() {
|
||||
sphere(r=Thickness);
|
||||
// cylinderX(r=Thickness, h=2*Thickness, center=true);
|
||||
children(buttons);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module _DilatedBodyExceptFront(ButtonStyle) {
|
||||
module _DilatedBodyExceptFront(ButtonStyle, zMin=0) {
|
||||
body = 0;
|
||||
buttons = 1;
|
||||
intersection() {
|
||||
_DilatedBody() {
|
||||
_DilatedBody(ButtonStyle=ButtonStyle) {
|
||||
children(body);
|
||||
children(buttons);
|
||||
};
|
||||
_PhoneBulkAndBack() children(body);
|
||||
_PhoneBulkAndBack(zMin=zMin) children(body);
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the geometry which should be removed from the case, to make room for ports/buttons
|
||||
module _PeripheralCutouts(ButtonStyle) {
|
||||
body = 0;
|
||||
buttons = 1;
|
||||
@@ -69,7 +78,8 @@ module _PeripheralCutouts(ButtonStyle) {
|
||||
camera = 3;
|
||||
union() {
|
||||
minkowski() {
|
||||
sphere(r=MarginCameraCut);
|
||||
// sphere(r=MarginCameraCut);
|
||||
cylinderZ(r=MarginCameraCut, h=2*Thickness+tol, center=true);
|
||||
children(camera);
|
||||
};
|
||||
// restrict these cutouts to just the phone bulk -- don't let them impact the back of the case
|
||||
@@ -84,6 +94,7 @@ module _PeripheralCutouts(ButtonStyle) {
|
||||
};
|
||||
minkowski() {
|
||||
sphere(r=MarginPortsCut);
|
||||
// cylinderY(r=MarginPortsCut, h=2*MarginPortsCut, center=true);
|
||||
children(ports);
|
||||
};
|
||||
};
|
||||
@@ -91,11 +102,11 @@ module _PeripheralCutouts(ButtonStyle) {
|
||||
};
|
||||
};
|
||||
|
||||
module _FrontFace(tol=tol) {
|
||||
module _FrontFace() {
|
||||
color("thistle")
|
||||
difference() {
|
||||
translate([0, 0, -Thickness]) children(/*body*/);
|
||||
_PhoneBulkLayer(tol=tol) children(/*body*/);
|
||||
_PhoneBulkLayer() children(/*body*/);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,6 +118,7 @@ module _FrontKeep(tol=tol) {
|
||||
minkowski() {
|
||||
// take the outline of the front face of the phone
|
||||
difference() {
|
||||
// TODO: projection, then hull
|
||||
minkowski() {
|
||||
cylinder(r=2*tol, h=tol, center=true);
|
||||
_FrontFace() children(/*body*/);
|
||||
@@ -123,23 +135,15 @@ module _FrontKeep(tol=tol) {
|
||||
};
|
||||
}
|
||||
|
||||
/// returns the region which we want to subtract from the case, in order to keep the front screen accessible.
|
||||
/// the returned cutout is strictly z <= 0,
|
||||
module _FrontCutout() {
|
||||
difference() {
|
||||
_FrontFace() children(/*body*/);
|
||||
_FrontKeep() children(/*body*/);
|
||||
};
|
||||
}
|
||||
|
||||
module _CaseExceptFeatures(ButtonStyle)
|
||||
/// complete case except for cutouts
|
||||
module _CaseExceptFeatures(ButtonStyle, tol=tol)
|
||||
{
|
||||
body = 0;
|
||||
buttons = 1;
|
||||
union() {
|
||||
difference() {
|
||||
// start by dilating the phone
|
||||
_DilatedBodyExceptFront(ButtonStyle=ButtonStyle) {
|
||||
_DilatedBodyExceptFront(ButtonStyle=ButtonStyle, zMin=tol) {
|
||||
children(body);
|
||||
children(buttons);
|
||||
};
|
||||
@@ -149,7 +153,7 @@ module _CaseExceptFeatures(ButtonStyle)
|
||||
// add in the front part of the case
|
||||
intersection() {
|
||||
_FrontKeep() translate([0, 0, FrontSquashZ]) children(body);
|
||||
_DilatedBody() {
|
||||
_DilatedBody(ButtonStyle=ButtonStyle) {
|
||||
children(body);
|
||||
children(buttons);
|
||||
};
|
||||
@@ -197,13 +201,13 @@ module _Case(ButtonStyle)
|
||||
/// ```
|
||||
///
|
||||
/// replace "Phone" above with the specific model, e.g. `PP` like `PPBody()`
|
||||
module Case(ButtonStyle="extrude", RenderPhone=false) {
|
||||
module Case(ButtonStyle="extrude", RenderPhone=false, OrientForPrint=true) {
|
||||
body = 0;
|
||||
buttons = 1;
|
||||
ports = 2;
|
||||
camera = 3;
|
||||
// translate([PPBodyWidth + Thickness, Thickness, PPBodyHeight + Thickness])
|
||||
rotate(a=[0, 180, 0])
|
||||
|
||||
_MaybeOrientForPrint(OrientForPrint)
|
||||
union() {
|
||||
color("DarkSlateGray") _Case(ButtonStyle) {
|
||||
children(body);
|
||||
@@ -223,3 +227,16 @@ module Case(ButtonStyle="extrude", RenderPhone=false) {
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
module _MaybeOrientForPrint(OrientForPrint) {
|
||||
if (OrientForPrint) {
|
||||
// XXX these translations deform the mesh, slightly.
|
||||
// it should export OK, but if rendering gives errors about the mesh not being solid,
|
||||
// then clear cache and re-render (F6)
|
||||
translateToAxis(x="pos", z="pos")
|
||||
rotate([0, 180, 0])
|
||||
children(0);
|
||||
} else {
|
||||
children(0);
|
||||
};
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use <../case.scad>
|
||||
use <../pp/exports.scad>
|
||||
|
||||
Case(RenderPhone=false) {
|
||||
Case(RenderPhone=false, OrientForPrint=true) {
|
||||
PPBody();
|
||||
PPButtons();
|
||||
PPPorts();
|
||||
|
@@ -2,12 +2,14 @@
|
||||
|
||||
include <defaults.scad>
|
||||
|
||||
module empty() {}
|
||||
|
||||
/// cylinder() but with the axis on the x axis instead of the z axis.
|
||||
/// and where `center=false` behaves like for `cube(center=false)`,
|
||||
/// i.e. circle center is not at (0, 0) but (r, r)
|
||||
module cylinderX(d=undef, r=undef, h=undef, center=false) {
|
||||
_translateIf(!center, [0, _toRad(r=r, d=d), _toRad(r=r, d=d)]) {
|
||||
rotate(a=[0,-90,0]) cylinder(d=d, r=r, h=h, center=center);
|
||||
rotate(a=[0,90,0]) cylinder(d=d, r=r, h=h, center=center);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,30 +30,62 @@ module cylinderZ(d=undef, r=undef, h=undef, center=false) {
|
||||
}
|
||||
}
|
||||
|
||||
// a 2d-cylinder extruded along the x-axis
|
||||
module pillX(dimX, dimY, dimZ, tol=tol, center=false) {
|
||||
diam = min(dimY, dimZ) - tol;
|
||||
minkowski() {
|
||||
cube([dimX/2, dimY-diam, dimZ-diam], center=center);
|
||||
cylinderX(d=diam, h=dimX/2, center=center);
|
||||
// a 2d-cylinder normal to the x axis, and elongated over the y/z axis.
|
||||
module pillX(dimX, dimY, dimZ, center=false) {
|
||||
diam = min(dimY, dimZ);
|
||||
hull() {
|
||||
cylinderX(d=diam, h=dimX, center=center);
|
||||
translate([0, dimY-diam, dimZ-diam]) cylinderX(d=diam, h=dimX, center=center);
|
||||
};
|
||||
}
|
||||
|
||||
// a 2d-cylinder normal to the y axis, and elongated over the x/z axis.
|
||||
module pillY(dimX, dimY, dimZ, center=false) {
|
||||
diam = min(dimX, dimZ);
|
||||
hull() {
|
||||
cylinderY(d=diam, h=dimY, center=center);
|
||||
translate([dimX-diam, 0, dimZ-diam]) cylinderY(d=diam, h=dimY, center=center);
|
||||
};
|
||||
}
|
||||
|
||||
// a 2d-cylinder normal to the z axis, and elongated over the x/y axis.
|
||||
module pillZ(dimX, dimY, dimZ, center=false) {
|
||||
diam = min(dimX, dimY);
|
||||
hull() {
|
||||
cylinderZ(d=diam, h=dimZ, center=center);
|
||||
translate([dimX-diam, dimY-diam, 0]) cylinderZ(d=diam, h=dimZ, center=center);
|
||||
};
|
||||
}
|
||||
|
||||
module sphere_(r, align=undef, alignX=undef, alignY=undef, alignZ=undef) {
|
||||
alignX_ = _default(alignX, align);
|
||||
alignY_ = _default(alignY, align);
|
||||
alignZ_ = _default(alignZ, align);
|
||||
|
||||
sX = _selectScale(alignX);
|
||||
sY = _selectScale(alignY);
|
||||
sZ = _selectScale(alignZ);
|
||||
|
||||
translate([r * sX, r * sY, r * sZ]) sphere(r=r);
|
||||
}
|
||||
|
||||
function _default(primary, secondary) = (primary != undef) ? primary : secondary;
|
||||
|
||||
function _selectScale(align) = (align == "center") ? 0 : ((align == "bbox") ? 1 : 0.707);
|
||||
|
||||
/// a hollowed-out sphere
|
||||
module sphereShell(r, thickness) {
|
||||
difference() {
|
||||
sphere(r=r+thickness/2);
|
||||
sphere(r=r-thickness/2);
|
||||
}
|
||||
}
|
||||
|
||||
// a 2d-cylinder extruded along the x-axis
|
||||
module pillY(dimX, dimY, dimZ, tol=tol, center=false) {
|
||||
diam = min(dimX, dimZ) - tol;
|
||||
minkowski() {
|
||||
cube([dimX-diam, dimY/2, dimZ-diam], center=center);
|
||||
cylinderY(d=diam, h=dimY/2, center=center);
|
||||
}
|
||||
}
|
||||
|
||||
// a 2d-cylinder extruded along the z-axis
|
||||
module pillZ(dimX, dimY, dimZ, tol=tol, center=false) {
|
||||
diam = min(dimX, dimY) - tol;
|
||||
minkowski() {
|
||||
cube([dimX-diam, dimY-diam, dimZ/2], center=center);
|
||||
cylinderZ(d=diam, h=dimZ/2, center=center);
|
||||
/// a hollowed-out cube
|
||||
module cubeShell(x, y, z, thickness, center=false) {
|
||||
difference() {
|
||||
cube([x+thickness/2, y+thickness/2, z+thickness/2], center=center);
|
||||
cube([x-thickness/2, y-thickness/2, z-thickness/2], center=center);
|
||||
}
|
||||
}
|
||||
|
||||
|
160
src/lib/transformations.scad
Normal file
160
src/lib/transformations.scad
Normal file
@@ -0,0 +1,160 @@
|
||||
include <defaults.scad>
|
||||
|
||||
/// projects the child onto the x axis, making it effectively 1 dimensional.
|
||||
/// actually, the result is a 3d "line" of thickness `tol` centered about the x axis.
|
||||
/// the x bounds are precise (regardless of tol): it's only the y/z thickness which requires tolerance)
|
||||
module extractXDomain(tol=tol) {
|
||||
translate([0, -tol/2, -tol/2])
|
||||
// compress (x,y=[0,tol],z) -> (x,y=[0,tol],z=[0,tol]
|
||||
linear_extrude(tol) projection(cut=false)
|
||||
// rotate so it's (x,z), with y in [0, tol]
|
||||
rotate([-90, 0, 0])
|
||||
// compress (x,y,z) -> (x,y,z=[0,tol])
|
||||
linear_extrude(tol) projection(cut=false)
|
||||
children(0);
|
||||
}
|
||||
|
||||
/// creates a dot of size `tol` at (x=<the minimum x coordinate occupied by the child module>, y=0, z=0>
|
||||
module extractXMin(tol=tol) {
|
||||
difference() {
|
||||
translate([-tol/2, 0, 0]) extractXDomain(tol=tol) children(0);
|
||||
translate([tol/2, 0, 0]) extractXDomain(tol=tol) children(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// creates a dot of size `tol` at (x=<the maximum x coordinate occupied by the child module>, y=0, z=0>
|
||||
module extractXMax(tol=tol) {
|
||||
difference() {
|
||||
translate([tol/2, 0, 0]) extractXDomain(tol=tol) children(0);
|
||||
translate([-tol/2, 0, 0]) extractXDomain(tol=tol) children(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// translates the child so that its lowest x value is 0.
|
||||
module translateToX0Pos(tol=tol) {
|
||||
minkowski() {
|
||||
// flip about yz plane, so that we're shifting by -(lower_x_bound)
|
||||
mirror([1, 0, 0]) extractXMin(tol=tol) children(0);
|
||||
children(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// translates the child so that its highest x value is 0.
|
||||
module translateToX0Neg(tol=tol) {
|
||||
minkowski() {
|
||||
// flip about yz plane, so that we're shifting by -(upper_x_bound)
|
||||
mirror([1, 0, 0]) extractXMax(tol=tol) children(0);
|
||||
children(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// projects the child onto the y axis, making it effectively 1 dimensional.
|
||||
/// actually, the result is a 3d "line" of thickness `tol` centered about the y axis.
|
||||
module extractYDomain(tol=tol) {
|
||||
rotate([0, 0, 90])
|
||||
extractXDomain(tol=tol) {
|
||||
rotate([0, 0, -90]) children(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// creates a dot of size `tol` at (x=0, y=<the minimum y coordinate occupied by the child module>, z=0).
|
||||
module extractYMin(tol=tol) {
|
||||
difference() {
|
||||
translate([0, -tol/2, 0]) extractYDomain(tol=tol) children(0);
|
||||
translate([0, tol/2, 0]) extractYDomain(tol=tol) children(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// creates a dot of size `tol` at (x=0, y=<the maximum y coordinate occupied by the child module>, 0).
|
||||
module extractYMax(tol=tol) {
|
||||
difference() {
|
||||
translate([0, tol/2, 0]) extractYDomain(tol=tol) children(0);
|
||||
translate([0, -tol/2, 0]) extractYDomain(tol=tol) children(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// translates the child so that its lowest y value is 0.
|
||||
module translateToY0Pos(tol=tol) {
|
||||
minkowski() {
|
||||
// flip about xz plane, so that we're shifting by -(lower_y_bound)
|
||||
mirror([0, 1, 0]) extractYMin(tol=tol) children(0);
|
||||
children(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// translates the child so that its highest x value is 0.
|
||||
module translateToY0Neg(tol=tol) {
|
||||
minkowski() {
|
||||
// flip about xz plane, so that we're shifting by -(upper_y_bound)
|
||||
mirror([0, 1, 0]) extractYMax(tol=tol) children(0);
|
||||
children(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// projects the child onto the z axis, making it effectively 1 dimensional.
|
||||
/// actually, the result is a 3d "line" of thickness `tol` centered about the z axis.
|
||||
module extractZDomain(tol=tol) {
|
||||
rotate([0, 90, 0])
|
||||
extractXDomain(tol=tol) {
|
||||
rotate([0, -90, 0]) children(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// creates a dot of size `tol` at (x=0, y=0, z=<the minimum z coordinate occupied by the child module>).
|
||||
module extractZMin(tol=tol) {
|
||||
difference() {
|
||||
translate([0, 0, -tol/2]) extractZDomain(tol=tol) children(0);
|
||||
translate([0, 0, tol/2]) extractZDomain(tol=tol) children(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// creates a dot of size `tol` at (x=0, y=0, z=<the maximum z coordinate occupied by the child module>).
|
||||
module extractZMax(tol=tol) {
|
||||
difference() {
|
||||
translate([0, 0, tol/2]) extractZDomain(tol=tol) children(0);
|
||||
translate([0, 0, -tol/2]) extractZDomain(tol=tol) children(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// translates the child so that its lowest z value is 0.
|
||||
module translateToZ0Pos(tol=tol) {
|
||||
minkowski() {
|
||||
// flip about xy plane, so that we're shifting by -(lower_z_bound)
|
||||
mirror([0, 0, 1]) extractZMin(tol=tol) children(0);
|
||||
children(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// translates the child so that its highest z value is 0.
|
||||
module translateToZ0Neg(tol=tol) {
|
||||
minkowski() {
|
||||
// flip about xy plane, so that we're shifting by -(upper_z_bound)
|
||||
mirror([0, 0, 1]) extractZMax(tol=tol) children(0);
|
||||
children(0);
|
||||
}
|
||||
}
|
||||
|
||||
module translateToAxis(x=undef, y=undef, z=undef, tol=tol) {
|
||||
minkowski() {
|
||||
minkowski() {
|
||||
if (x == "neg") {
|
||||
mirror([1, 0, 0]) extractXMax(tol=tol) children(0);
|
||||
} else if (x == "pos") {
|
||||
mirror([1, 0, 0]) extractXMin(tol=tol) children(0);
|
||||
}
|
||||
if (y == "neg") {
|
||||
mirror([0, 1, 0]) extractYMax(tol=tol) children(0);
|
||||
} else if (y == "pos") {
|
||||
mirror([0, 1, 0]) extractYMin(tol=tol) children(0);
|
||||
}
|
||||
if (z == "neg") {
|
||||
mirror([0, 0, 1]) extractZMax(tol=tol) children(0);
|
||||
} else if (z == "pos") {
|
||||
mirror([0, 0, 1]) extractZMin(tol=tol) children(0);
|
||||
}
|
||||
};
|
||||
children(0);
|
||||
}
|
||||
}
|
@@ -3,22 +3,22 @@
|
||||
use <phone.scad>
|
||||
use <bits.scad>
|
||||
|
||||
module PPButtons(volume=true, power=true) {
|
||||
module PPButtons(volume=true, power=true, box=false) {
|
||||
union() {
|
||||
if (volume) Volume();
|
||||
if (power) Power();
|
||||
if (volume) Volume(Box=box);
|
||||
if (power) Power(Box=box);
|
||||
};
|
||||
}
|
||||
|
||||
module PPPorts(usb=true, aux=true) {
|
||||
module PPPorts(usb=true, aux=true, box=false) {
|
||||
union() {
|
||||
if (usb) Usb();
|
||||
if (usb) Usb(Box=box);
|
||||
if (aux) Aux();
|
||||
};
|
||||
}
|
||||
|
||||
module PPCamera() {
|
||||
Camera();
|
||||
module PPCamera(box=false) {
|
||||
Camera(Box=box);
|
||||
}
|
||||
|
||||
module PPBody(box=false) {
|
||||
|
@@ -19,16 +19,26 @@ module _BodyConvolve(tol=tol) {
|
||||
}
|
||||
}
|
||||
|
||||
/// represents one corner of the body
|
||||
module _BodyPill() {
|
||||
union() {
|
||||
translate([BodyRadXY, BodyRadXY, BodyRadFrontZ]) rotate_extrude() translate([BodyRadXY - BodyRadFrontZ, 0, 0]) circle(r=BodyRadFrontZ);
|
||||
translate([BodyRadXY, BodyRadXY, BodyHeight-BodyRadFrontZ]) rotate_extrude() translate([BodyRadXY - BodyRadFrontZ, 0, 0]) circle(r=BodyRadFrontZ);
|
||||
}
|
||||
}
|
||||
|
||||
module Body(Box=false)
|
||||
{
|
||||
if (Box) {
|
||||
cube([BodyWidth, BodyLength, BodyHeight], center=false);
|
||||
} else {
|
||||
translate([BodyRadXY, BodyRadXY, BodyRadFrontZ])
|
||||
minkowski() {
|
||||
cube([BodyWidth-2*BodyRadXY, BodyLength-2*BodyRadXY, BodyHeight-2*BodyRadFrontZ], center=false);
|
||||
_BodyConvolve();
|
||||
}
|
||||
hull() {
|
||||
// body is a `hull` over four pill-shaped corners
|
||||
_BodyPill();
|
||||
translate([BodyWidth-2*BodyRadXY, 0, 0]) _BodyPill();
|
||||
translate([BodyWidth-2*BodyRadXY, BodyLength-2*BodyRadXY, 0]) _BodyPill();
|
||||
translate([0, BodyLength-2*BodyRadXY, 0]) _BodyPill();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user