mirror of
https://github.com/Irev-Dev/Round-Anything.git
synced 2025-09-07 05:40:39 +02:00
Compare commits
61 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
061fef7c42 | ||
|
2f47c7631c | ||
|
57c7889ff0 | ||
|
0161f336e0 | ||
|
980948b31c | ||
|
d4a001ce09 | ||
|
116c961393 | ||
|
5711ff827c | ||
|
dd7d7121c0 | ||
|
ea20000c74 | ||
|
df71daa0cb | ||
|
23a4204593 | ||
|
ba1e80058c | ||
|
0fbca7c2d5 | ||
|
dd05f2e441 | ||
|
f35262f955 | ||
|
1b7623e17b | ||
|
c56190a81b | ||
|
e9faa0785d | ||
|
e9d3d805ab | ||
|
df50ac47ce | ||
|
9c2e9b929b | ||
|
cfd739d79d | ||
|
f69ddaa05b | ||
|
bce6705d4c | ||
|
28814a34a0 | ||
|
8ead00a2bf | ||
|
66f30bed93 | ||
|
f9928e36e1 | ||
|
26fb282626 | ||
|
c04a81f25c | ||
|
e6d91c352b | ||
|
297a7ce5dc | ||
|
a6999a7202 | ||
|
a85e6ffd48 | ||
|
5a7b6db0ec | ||
|
6b16407430 | ||
|
9613a81f15 | ||
|
e6d67b6304 | ||
|
8d5b6a180b | ||
|
0697444270 | ||
|
764b3b7a73 | ||
|
a3934972b5 | ||
|
cb9e7f7b30 | ||
|
1f15acb04b | ||
|
ddbb97b5ee | ||
|
40a14892f2 | ||
|
bf97548f88 | ||
|
9b235f236b | ||
|
ad61abc74a | ||
|
aacf657b62 | ||
|
1d124289d4 | ||
|
52afd869d0 | ||
|
3f9d124bb7 | ||
|
2c71de2b5e | ||
|
e364960bea | ||
|
6ee436a6b2 | ||
|
16e5c4833d | ||
|
f4a75dd38a | ||
|
e2cdd6c1bc | ||
|
7e74a98b6a |
@@ -32,10 +32,11 @@ Both this modules do the same thing as minkowskiRound() but focus on either insi
|
||||
// }
|
||||
//}
|
||||
|
||||
//minkowskiRound(0.5,2,1,[50,50,50])union(){//--example in the thiniverse thumbnail/main image
|
||||
// $fn=20;
|
||||
// minkowskiRound(0.7,1.5,1,[50,50,50])union(){//--example in the thiniverse thumbnail/main image
|
||||
// cube([6,6,22]);
|
||||
// rotate([30,45,10])cylinder(h=22,d=10);
|
||||
//}//--I rendered this out with a $fn=25 and it took more than 12 hours on my computer
|
||||
// rotate([30,45,10])cylinder(h=22,d=10);
|
||||
// }//--I rendered this out with a $fn=25 and it took more than 12 hours on my computer
|
||||
|
||||
|
||||
module round2d(OR=3,IR=1){
|
||||
@@ -48,16 +49,16 @@ module round2d(OR=3,IR=1){
|
||||
}
|
||||
}
|
||||
|
||||
module minkowskiRound(OR=1,IR=1,enable=1,cubeSize=[500,500,500]){
|
||||
module minkowskiRound(OR=1,IR=1,enable=1,boundingEnvelope=[500,500,500]){
|
||||
if(enable==0){//do nothing if not enabled
|
||||
children();
|
||||
} else {
|
||||
minkowski(){//expand the now positive shape back out
|
||||
difference(){//make the negative shape positive again
|
||||
cube(cubeSize-[0.1,0.1,0.1],center=true);
|
||||
cube(boundingEnvelope-[0.1,0.1,0.1],center=true);
|
||||
minkowski(){//expand the negative shape inwards
|
||||
difference(){//create a negative of the children
|
||||
cube(cubeSize,center=true);
|
||||
cube(boundingEnvelope,center=true);
|
||||
minkowski(){//expand the children
|
||||
children();
|
||||
sphere(IR);
|
||||
@@ -71,16 +72,16 @@ module minkowskiRound(OR=1,IR=1,enable=1,cubeSize=[500,500,500]){
|
||||
}
|
||||
}
|
||||
|
||||
module minkowskiOutsideRound(r=1,enable=1,cubeSize=[500,500,500]){
|
||||
module minkowskiOutsideRound(r=1,enable=1,boundingEnvelope=[500,500,500]){
|
||||
if(enable==0){//do nothing if not enabled
|
||||
children();
|
||||
} else {
|
||||
minkowski(){//expand the now positive shape
|
||||
difference(){//make the negative positive
|
||||
cube(cubeSize-[0.1,0.1,0.1],center=true);
|
||||
cube(boundingEnvelope-[0.1,0.1,0.1],center=true);
|
||||
minkowski(){//expand the negative inwards
|
||||
difference(){//create a negative of the children
|
||||
cube(cubeSize,center=true);
|
||||
cube(boundingEnvelope,center=true);
|
||||
children();
|
||||
}
|
||||
sphere(r);
|
||||
@@ -91,15 +92,15 @@ module minkowskiOutsideRound(r=1,enable=1,cubeSize=[500,500,500]){
|
||||
}
|
||||
}
|
||||
|
||||
module minkowskiInsideRound(r=1,enable=1,cubeSize=[500,500,500]){
|
||||
module minkowskiInsideRound(r=1,enable=1,boundingEnvelope=[500,500,500]){
|
||||
if(enable==0){//do nothing if not enabled
|
||||
children();
|
||||
} else {
|
||||
difference(){//make the negative positive again
|
||||
cube(cubeSize-[0.1,0.1,0.1],center=true);
|
||||
cube(boundingEnvelope-[0.1,0.1,0.1],center=true);
|
||||
minkowski(){//expand the negative shape inwards
|
||||
difference(){//make the expanded children a negative shape
|
||||
cube(cubeSize,center=true);
|
||||
cube(boundingEnvelope,center=true);
|
||||
minkowski(){//expand the children
|
||||
children();
|
||||
sphere(r);
|
||||
|
53
README.md
53
README.md
@@ -1,34 +1,43 @@
|
||||
# Round-Anything
|
||||
|
||||
### minkowskiRound();
|
||||
Is an incredibly powerful module that will allow you to fillet any of your OpenSCAD modules retrospectively while keeping the original dimensions. You don't need design your part again, or start using rounded cube modules etc, just throw minkowskiRound() out the front of it and your good to go. Also I hope the example in the first picture demonstrates that besides convenience of not having to add each fillet yourself, it will also fillet some areas (internal corners in particular) that are near impossible to fillet with other methods in SCAD.
|
||||
Round-Anything is primarily a set of OpenSCAD utilities that help with rounding parts, but it also embodies a robust approach to developing OpenSCAD parts. The heart of the library a list of polygon points, with a 3rd radius parameter at each point. That is a series of [x, y, radius] points.
|
||||
|
||||
Biggest downside is that it is very computationally intensive, you will have to keep the $fn low (I would recommend 10-15 as a starting point) unless you are willing to wait a long time (12 hours plus). The modules have been made with an enable variable so that you can disable the module and keep designing and only add the fillets when you are ready to output your final model.
|
||||
There is also a minkowskiInsideRound() and minkowskiOutsideRound() which takes less time to render if you only need inside or outside radii
|
||||
<img src="https://cdn.sanity.io/images/2hqqc7om/production/5d73db6dfd44ab2606d5e3ff5d993361eba61c5e-1371x1567.png?w=500&h=571&fit=crop" width="100%" align="left">
|
||||
|
||||
### round2d();
|
||||
Allows you to round any 2d object in openscad
|
||||
## The Why
|
||||
|
||||
### polyRound();
|
||||
is used the same way that polygon(); is used, however along with a list of coordinates, a list of radiuses should also be supplied and the corners will be rounded with these radiuses.
|
||||
This is can be incredible useful if you try and design parts using extruded 2d polygons instead of with primatives since radiuses are generally hard to implement in OpenSCAD (internal radiuses in particular). see examples below.
|
||||
The truth is radii, internal radii in particular can be a real pain to add in openscad. and the more you move away from shapes with 90 degree angles the more difficult it becomes, effectively putting a complexity ceiling on parts you can produce in OpenScad. Because of how important radii in both making an appealing and strong part, reducing stress concentration etc, A library that focuses on radii as a core principle makes for a solid foundation for your parts. Furthermore the heart of the library revolves around the polygon, this is because we're leveraging the battle tested paradigm of extruding from 2d sketches of most CAD packages. I can't imagine making an OpenScad part without Round-Anything.
|
||||
|
||||
I would love if people sent me examples that I can upload along with the ones I have here.
|
||||
### Quick side-notes
|
||||
|
||||
[thingiverse page here](https://www.thingiverse.com/thing:2419664)
|
||||
I'm currently working on a community website for "Code-CAD" (like OpenSCAD). A good way to think of it is codepen crossed with a thing repository. You can check it out at [cadhub.xyz](https://cadhub.xyz/) or it's [repo](https://github.com/Irev-Dev/cadhub).
|
||||
|
||||
### Examples
|
||||
##### minkowsikRound();
|
||||
<img src="https://github.com/Irev-Dev/Round-Anything/blob/master/images/mainminkowski.png" width="100%" align="left">
|
||||
Also please submit examples of what you build with the library in the [discussions](https://github.com/Irev-Dev/Round-Anything/discussions), I'd love to see them. I also recommend you "watch" the repo with notifications turned on for the discussions to stay up-to-date.
|
||||
|
||||
##### minkowsikInsideRound(); & minkowsikOutsideRound();
|
||||
<img src="https://github.com/Irev-Dev/Round-Anything/blob/master/images/InOutminkowski.png" width="100%" align="left">
|
||||
## Documentation
|
||||
|
||||
##### round2d();
|
||||
<img src="https://github.com/Irev-Dev/Round-Anything/blob/master/images/round2d.png" width="100%" align="left">
|
||||
See an overview of the library in [video form](https://www.youtube.com/watch?v=laxv2wFKq8Q)
|
||||
|
||||
##### polyRound();
|
||||
<img src="https://github.com/Irev-Dev/Round-Anything/blob/master/images/example1.png" width="100%" align="left">
|
||||
<img src="https://github.com/Irev-Dev/Round-Anything/blob/master/images/PolyRoundexample3fn.png" width="100%" align="left">
|
||||
<img src="https://github.com/Irev-Dev/Round-Anything/blob/master/images/example2.png" width="100%" align="left">
|
||||
<a href="https://www.youtube.com/watch?v=laxv2wFKq8Q"><img src="https://i.ytimg.com/vi/laxv2wFKq8Q/sddefault.jpg" width="100%" align="left"></a>
|
||||
|
||||
|
||||
[Written overview](https://kurthutten.com/blog/round-anything-a-pragmatic-approach-to-openscad-design/).
|
||||
|
||||
[Full documentation of the API is here](https://kurthutten.com/blog/round-anything-api/).
|
||||
|
||||
[Installation instructions](https://github.com/Irev-Dev/Round-Anything/discussions/21)
|
||||
|
||||
## Extra
|
||||
|
||||
I [live streamed](https://www.youtube.com/watch?v=1Tegarwy69I&t=2s) the making of [this part](https://github.com/Irev-Dev/monitor-stand-turn-camera) using this library. I was able to make the bulk of this part quickly even with some complex radii involved thanks to the library.
|
||||
|
||||
<img src="https://cdn.thingiverse.com/assets/ea/fb/83/89/57/featured_preview_camera_mount.png" width="100%" align="left">
|
||||
|
||||
Below are some of the example parts that can be found in [roundAnythingExamples.scad](https://github.com/Irev-Dev/Round-Anything/blob/master/roundAnythingExamples.scad).
|
||||
|
||||
<img src="https://cdn.sanity.io/images/2hqqc7om/production/2dba6301d1f25b1c45a634058b280b52fa713b60-1920x1080.png?w=1920&h=1000&fit=crop" width="100%" align="left">
|
||||
|
||||
## Citation
|
||||
roundUnionMask Includes code based on examples from:
|
||||
Kogan, Jonathan (2017) "A New Computationally Efficient Method for Spacing n Points on a Sphere," Rose-Hulman Undergraduate Mathematics Journal: Vol. 18 : Iss. 2 , Article 5.
|
||||
Available at: [https://scholar.rose-hulman.edu/rhumj/vol18/iss2/5]
|
||||
|
3
examples/README.md
Normal file
3
examples/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Round-Anything examples
|
||||
|
||||
These examples are mostly to go with the [library Documentation](https://learn.cadhub.xyz/docs/round-anything/overview) and so are best viewed there.
|
34
examples/beamChain-1.scad
Normal file
34
examples/beamChain-1.scad
Normal file
@@ -0,0 +1,34 @@
|
||||
// beamChain example 1
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
function beamPoints(r1,r2,rStart=0,rEnd=0)=[
|
||||
[0, 0, rStart],
|
||||
[2, 8, 0 ],
|
||||
[5, 4, r1 ],
|
||||
[15, 10, r2 ],
|
||||
[17, 2, rEnd ]
|
||||
];
|
||||
|
||||
linear_extrude(1){
|
||||
|
||||
// chained lines by themselves
|
||||
translate(){
|
||||
radiiPoints=beamPoints(0,0);
|
||||
polygon(polyRound(beamChain(radiiPoints,offset1=0.02, offset2=-0.02),20));
|
||||
}
|
||||
|
||||
|
||||
// Add some radii to the line transitions
|
||||
translate([0,-7,0]){
|
||||
radiiPoints=beamPoints(2,1);
|
||||
polygon(polyRound(beamChain(radiiPoints,offset1=0.02, offset2=-0.02),20));
|
||||
}
|
||||
|
||||
// Give make the lines beams with some thickness
|
||||
translate([0,-7*2,0]){
|
||||
radiiPoints=beamPoints(2,1);
|
||||
polygon(polyRound(beamChain(radiiPoints,offset1=0.5, offset2=-0.5),20));
|
||||
}
|
||||
|
||||
}
|
27
examples/beamChain-2.scad
Normal file
27
examples/beamChain-2.scad
Normal file
@@ -0,0 +1,27 @@
|
||||
// beamChain example 2
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
function beamPoints(r1,r2,rStart=0,rEnd=0)=[
|
||||
[0, 0, rStart],
|
||||
[2, 8, 0 ],
|
||||
[5, 4, r1 ],
|
||||
[15, 10, r2 ],
|
||||
[17, 2, rEnd ]
|
||||
];
|
||||
|
||||
linear_extrude(1){
|
||||
|
||||
// Add an angle to the start of the beam
|
||||
translate([0,-7*3,0]){
|
||||
radiiPoints=beamPoints(2,1);
|
||||
polygon(polyRound(beamChain(radiiPoints,offset1=0.5, offset2=-0.5, startAngle=45),20));
|
||||
}
|
||||
|
||||
// Put a negative radius at the start for transationing to a flat surface
|
||||
translate([0,-7*4,0]){
|
||||
radiiPoints=beamPoints(2,1,rStart=-0.7);
|
||||
polygon(polyRound(beamChain(radiiPoints,offset1=0.5, offset2=-0.5, startAngle=45),20));
|
||||
}
|
||||
|
||||
}
|
50
examples/beamChain-3.scad
Normal file
50
examples/beamChain-3.scad
Normal file
@@ -0,0 +1,50 @@
|
||||
// beamChain example 3
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
function beamPoints(r1,r2,rStart=0,rEnd=0)=[
|
||||
[0, 0, rStart],
|
||||
[2, 8, 0 ],
|
||||
[5, 4, r1 ],
|
||||
[15, 10, r2 ],
|
||||
[17, 2, rEnd ]
|
||||
];
|
||||
|
||||
// Define more points for a polygon to be atteched to the end of the beam chain
|
||||
clipP=[
|
||||
[16, 1.2, 0 ],
|
||||
[16, 0, 0 ],
|
||||
[16.5, 0, 0 ],
|
||||
[16.5, 1, 0.2],
|
||||
[17.5, 1, 0.2],
|
||||
[17.5, 0, 0 ],
|
||||
[18, 0, 0 ],
|
||||
[18, 1.2, 0 ]
|
||||
];
|
||||
|
||||
linear_extrude(1){
|
||||
// end hook
|
||||
translate([-15,-7*5+3,0]){
|
||||
polygon(polyRound(clipP,20));
|
||||
}
|
||||
|
||||
// Attached to the end of the beam chain by dividing the beam paths in forward and return and
|
||||
// concat other polygon inbetween
|
||||
translate([0,-7*6,0]){
|
||||
radiiPoints=beamPoints(2,1);
|
||||
forwardPath=beamChain(radiiPoints,offset1=0.5,startAngle=-15,mode=2);
|
||||
returnPath=revList(beamChain(radiiPoints,offset1=-0.5,startAngle=-15,mode=2));
|
||||
entirePath=concat(forwardPath,clipP,returnPath);
|
||||
polygon(polyRound(entirePath,20));
|
||||
}
|
||||
|
||||
// Add transitioning radii into the end polygong
|
||||
translate([0,-7*7-2,0]){
|
||||
radiiPoints=beamPoints(2,1,rEnd=3);
|
||||
forwardPath=beamChain(radiiPoints,offset1=0.5,startAngle=-15,mode=2);
|
||||
returnPath=revList(beamChain(radiiPoints,offset1=-0.5,startAngle=-15,mode=2));
|
||||
entirePath=concat(forwardPath,clipP,returnPath);
|
||||
polygon(polyRound(entirePath,20));
|
||||
}
|
||||
|
||||
}
|
22
examples/beamChain-4.scad
Normal file
22
examples/beamChain-4.scad
Normal file
@@ -0,0 +1,22 @@
|
||||
// beamChain example 4
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
function beamPoints(r1,r2,rStart=0,rEnd=0)=[
|
||||
[0, 0, rStart],
|
||||
[2, 8, 0 ],
|
||||
[5, 4, r1 ],
|
||||
[15, 10, r2 ],
|
||||
[17, 2, rEnd ]
|
||||
];
|
||||
|
||||
linear_extrude(1){
|
||||
|
||||
translate([0,-7*9,0]){
|
||||
// Define multiple shells from the the one set of points
|
||||
for(i=[0:2]){
|
||||
polygon(polyRound(beamChain(beamPoints(2,1),offset1=-1+i*0.4, offset2=-1+i*0.4+0.25),20));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
13
examples/extrudeWithRadius.scad
Normal file
13
examples/extrudeWithRadius.scad
Normal file
@@ -0,0 +1,13 @@
|
||||
// extrudeWithRadius example
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
radiiPoints=[
|
||||
[-4, 0, 1 ],
|
||||
[5, 3, 1.5 ],
|
||||
[0, 7, 0.1 ],
|
||||
[8, 7, 10 ],
|
||||
[20, 20, 0.8 ],
|
||||
[10, 0, 10 ]
|
||||
];
|
||||
extrudeWithRadius(3,0.5,0.5,5)polygon(polyRound(radiiPoints,30));
|
10
examples/mirrorPoints.scad
Normal file
10
examples/mirrorPoints.scad
Normal file
@@ -0,0 +1,10 @@
|
||||
// mirrorPoints example
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
centerRadius=7;
|
||||
points=[[0,0,0],[2,8,0],[5,4,3],[15,10,0.5],[10,2,centerRadius]];
|
||||
mirroredPoints=mirrorPoints(points,0,[0,0]);
|
||||
linear_extrude(1)
|
||||
translate([0,-20,0])
|
||||
polygon(polyRound(mirroredPoints,20));
|
24
examples/negative-polyRoundExtrude.scad
Normal file
24
examples/negative-polyRoundExtrude.scad
Normal file
@@ -0,0 +1,24 @@
|
||||
// negative polyRoundExtrude example
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
extrudeRadius = 0.8;
|
||||
extrudeHeight = 2;
|
||||
tiny = 0.005; // tiny value is used to stop artifacts from planes lining up perfectly
|
||||
|
||||
radiiPoints=[
|
||||
[-7, -3, 0 ],
|
||||
[7, -3, 0 ],
|
||||
[0, 6, 1 ] // top of the triagle is rounded
|
||||
];
|
||||
negativeRadiiPoints=[
|
||||
[-3, -1, 0 ],
|
||||
[3, -1, 0 ],
|
||||
[0, 3, 1 ] // top of the triagle is rounded
|
||||
];
|
||||
|
||||
difference() {
|
||||
polyRoundExtrude(radiiPoints,extrudeHeight, extrudeRadius, extrudeRadius,fn=20);
|
||||
translate([0,0,-tiny])
|
||||
polyRoundExtrude(negativeRadiiPoints,extrudeHeight+2*tiny, -extrudeRadius, -extrudeRadius,fn=20);
|
||||
}
|
13
examples/polyRoundExtrude.scad
Normal file
13
examples/polyRoundExtrude.scad
Normal file
@@ -0,0 +1,13 @@
|
||||
// polyRoundExtrude example
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
radiiPoints=[
|
||||
[10, 0, 10 ],
|
||||
[20, 20, 1.1],
|
||||
[8, 7, 10 ],
|
||||
[0, 7, 0.3],
|
||||
[5, 3, 0.1],
|
||||
[-4, 0, 1 ]
|
||||
];
|
||||
polyRoundExtrude(radiiPoints,2,0.5,-0.8,fn=20);
|
13
examples/polyround.scad
Normal file
13
examples/polyround.scad
Normal file
@@ -0,0 +1,13 @@
|
||||
// polyRound example
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
radiiPoints=[
|
||||
[-4, 0, 1 ],
|
||||
[5, 3, 1.5 ],
|
||||
[0, 7, 0.1 ],
|
||||
[8, 7, 10 ],
|
||||
[20, 20, 0.8 ],
|
||||
[10, 0, 10 ]
|
||||
];
|
||||
linear_extrude(3)polygon(polyRound(radiiPoints,30));
|
25
examples/radii-conflict.scad
Normal file
25
examples/radii-conflict.scad
Normal file
@@ -0,0 +1,25 @@
|
||||
// radii conflict example
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
//example of radii conflict handling and debuging feature
|
||||
function makeRadiiPoints(r1, r2)=[
|
||||
[0, 0, 0 ],
|
||||
[0, 20, r1 ],
|
||||
[20, 20, r2 ],
|
||||
[20, 0, 0 ]
|
||||
];
|
||||
|
||||
linear_extrude(3){
|
||||
// the squre shape being 20 wide, two radii of 10 both fit into the shape (just)
|
||||
translate([-25,0,0])polygon(polyRound(makeRadiiPoints(10,10),50));
|
||||
|
||||
//radii are too large and are reduced to fit and will be reduce to 10 and 10
|
||||
translate([0,0,0])polygon(polyRound(makeRadiiPoints(30,30),50));
|
||||
|
||||
//radii are too large again and are reduced to fit, but keep their ratios r1 will go from 10 to 4 and r2 will go from 40 to 16
|
||||
translate([25,0,0])polygon(polyRound(makeRadiiPoints(10,40),50));
|
||||
|
||||
//mode 2 = no radii limiting
|
||||
translate([50,0,0])polygon(polyRound(makeRadiiPoints(15,20),50,mode=2));
|
||||
}
|
29
examples/shell2d.scad
Normal file
29
examples/shell2d.scad
Normal file
@@ -0,0 +1,29 @@
|
||||
// shell2d example
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
module gridpattern(memberW = 4, sqW = 12, iter = 5, r = 3){
|
||||
round2d(0, r)rotate([0, 0, 45])translate([-(iter * (sqW + memberW) + memberW) / 2, -(iter * (sqW + memberW) + memberW) / 2])difference(){
|
||||
square([(iter) * (sqW + memberW) + memberW, (iter) * (sqW + memberW) + memberW]);
|
||||
for (i = [0:iter - 1], j = [0:iter - 1]){
|
||||
translate([i * (sqW + memberW) + memberW, j * (sqW + memberW) + memberW])square([sqW, sqW]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
radiiPoints=[
|
||||
[-4, 0, 1 ],
|
||||
[5, 3, 1.5 ],
|
||||
[0, 7, 0.1 ],
|
||||
[8, 7, 10 ],
|
||||
[20, 20, 0.8 ],
|
||||
[10, 0, 10 ]
|
||||
];
|
||||
|
||||
linear_extrude(1){
|
||||
shell2d(-0.5)polygon(polyRound(radiiPoints,30));
|
||||
translate([0,-10,0])shell2d(-0.5){
|
||||
polygon(polyRound(radiiPoints,30));
|
||||
translate([8,8])gridpattern(memberW = 0.3, sqW = 1, iter = 17, r = 0.2);
|
||||
}
|
||||
}
|
30
examples/translateRadiiPoints.scad
Normal file
30
examples/translateRadiiPoints.scad
Normal file
@@ -0,0 +1,30 @@
|
||||
// translateRadiiPoints example
|
||||
|
||||
include <Round-Anything-1.0.4/polyround.scad>
|
||||
|
||||
nutW=5.5; nutH=3; boltR=1.6;
|
||||
minT=2; minR=0.8;
|
||||
function nutCapture(startAndEndRadius=0)=[
|
||||
[-boltR, 0, startAndEndRadius],
|
||||
[-boltR, minT, 0],
|
||||
[-nutW/2, minT, minR],
|
||||
[-nutW/2, minT+nutH, minR],
|
||||
[nutW/2, minT+nutH, minR],
|
||||
[nutW/2, minT, minR],
|
||||
[boltR, minT, 0],
|
||||
[boltR, 0, startAndEndRadius],
|
||||
];
|
||||
linear_extrude(3)translate([-5,0,0])polygon(polyRound(nutCapture(),20));
|
||||
|
||||
negativeNutCapture=translateRadiiPoints(nutCapture(),tran=[5,0]);
|
||||
rotatedNegativeNutCapture=translateRadiiPoints(nutCapture(1),tran=[20,5],rot=90);
|
||||
aSquare=concat(
|
||||
[[0,0,0]],
|
||||
negativeNutCapture,
|
||||
[[20,0,0]],
|
||||
rotatedNegativeNutCapture,
|
||||
[[20,10,0]],
|
||||
[[0,10,0]]
|
||||
);
|
||||
|
||||
linear_extrude(3)polygon(polyRound(aSquare,20));
|
File diff suppressed because it is too large
Load Diff
228020
nicefillets.stl
228020
nicefillets.stl
File diff suppressed because it is too large
Load Diff
552
polyround.scad
552
polyround.scad
@@ -6,94 +6,179 @@
|
||||
// License: MIT
|
||||
|
||||
|
||||
|
||||
//examples();
|
||||
module examples(){
|
||||
//Example of how a parametric part might be designed with this tool
|
||||
width=20; height=25;
|
||||
slotW=8; slotH=15;
|
||||
slotPosition=8;
|
||||
minR=1.5; farcornerR=6;
|
||||
internalR=3;
|
||||
points=[
|
||||
[0, 0, farcornerR],
|
||||
[0, height, minR],
|
||||
[slotPosition, height, minR],
|
||||
[slotPosition, height-slotH, internalR],
|
||||
[slotPosition+slotW, height-slotH, internalR],
|
||||
[slotPosition+slotW, height, minR],
|
||||
[width, height, minR],
|
||||
[width, 0, minR]
|
||||
];
|
||||
points2=[
|
||||
[0, 0, farcornerR],
|
||||
["l", height, minR],
|
||||
[slotPosition, "l", minR],
|
||||
["l", height-slotH, internalR],
|
||||
[slotPosition+slotW, "l", internalR],
|
||||
["l", height, minR],
|
||||
[width, "l", minR],
|
||||
["l", height*0.2, minR],
|
||||
[45, 0, minR+5, "ayra"]
|
||||
];//,["l",0,minR]];
|
||||
echo(processRadiiPoints(points2));
|
||||
translate([-25,0,0]){
|
||||
polygon(polyRound(points,5));
|
||||
function addZcoord(points,displacement)=[for(i=[0:len(points)-1])[points[i].x,points[i].y, displacement]];
|
||||
function translate3Dcoords(points,tran=[0,0,0],mult=[1,1,1])=[for(i=[0:len(points)-1])[
|
||||
(points[i].x*mult.x)+tran.x,
|
||||
(points[i].y*mult.y)+tran.y,
|
||||
(points[i].z*mult.z)+tran.z
|
||||
]];
|
||||
function offsetPolygonPoints(points, offset=0)=
|
||||
// Work sthe same as the offset does, except for the fact that instead of a 2d shape
|
||||
// It works directly on polygon points
|
||||
// It returns the same number of points just offset into or, away from the original shape.
|
||||
// points= a series of x,y points[[x1,y1],[x2,y2],...]
|
||||
// offset= amount to offset by, negative numbers go inwards into the shape, positive numbers go out
|
||||
// return= a series of x,y points[[x1,y1],[x2,y2],...]
|
||||
let(
|
||||
isCWorCCW=sign(offset)*CWorCCW(points)*-1,
|
||||
lp=len(points)
|
||||
)
|
||||
[for(i=[0:lp-1]) parallelFollow([
|
||||
points[listWrap(i-1,lp)],
|
||||
points[i],
|
||||
points[listWrap(i+1,lp)],
|
||||
],thick=offset,mode=isCWorCCW)];
|
||||
|
||||
function reverseList(list) = [ for(i=[len(list) - 1:-1:0]) list[i] ];
|
||||
|
||||
// Apply `reverseList` to the array of vertex indices for an array of faces
|
||||
function invertFaces(faces) = [ for(f=faces) reverseList(f) ];
|
||||
|
||||
function makeCurvedPartOfPolyHedron(radiiPoints,r,fn,minR=0.01)=
|
||||
// this is a private function that I'm not expecting library users to use directly
|
||||
// radiiPoints= serise of x, y, r points
|
||||
// r= radius of curve that will be put on the end of the extrusion
|
||||
// fn= amount of subdivisions
|
||||
// minR= if one of the points in radiiPoints is less than r, it's likely to converge and form a sharp edge,
|
||||
// the min radius on these converged edges can be controled with minR, though because of legacy reasons it can't be 0, but can be a very small number.
|
||||
// return= array of [polyhedronPoints, Polyhedronfaces, theLength of a singe layer in the curve]
|
||||
let(
|
||||
lp=len(radiiPoints),
|
||||
radii=[for(i=[0:lp-1])radiiPoints[i].z],
|
||||
isCWorCCWOverall=CWorCCW(radiiPoints),
|
||||
dir=sign(r),
|
||||
absR=abs(r),
|
||||
fractionOffLp=1-1/fn,
|
||||
allPoints=[for(fraction=[0:1/fn:1])
|
||||
let(
|
||||
iterationOffset=dir*sqrt(sq(absR)-sq(fraction*absR))-dir*absR,
|
||||
theOffsetPoints=offsetPolygonPoints(radiiPoints,iterationOffset),
|
||||
polyRoundOffsetPoints=[for(i=[0:lp-1])
|
||||
let(
|
||||
pointsAboutCurrent=[
|
||||
theOffsetPoints[listWrap(i-1,lp)],
|
||||
theOffsetPoints[i],
|
||||
theOffsetPoints[listWrap(i+1,lp)]
|
||||
],
|
||||
isCWorCCWLocal=CWorCCW(pointsAboutCurrent),
|
||||
isInternalRadius=(isCWorCCWLocal*isCWorCCWOverall)==-1,
|
||||
// the radius names are only true for positive r,
|
||||
// when are r is negative increasingRadius is actually decreasing and vice-vs
|
||||
// increasingRadiusWithPositiveR is just to verbose of a variable name for my liking
|
||||
increasingRadius=max(radii[i]-iterationOffset, minR),
|
||||
decreasingRadius=max(radii[i]+iterationOffset, minR)
|
||||
)
|
||||
[theOffsetPoints[i].x, theOffsetPoints[i].y, isInternalRadius? increasingRadius: decreasingRadius]
|
||||
],
|
||||
pointsForThisLayer=polyRound(polyRoundOffsetPoints,fn)
|
||||
)
|
||||
addZcoord(pointsForThisLayer,fraction*absR)
|
||||
],
|
||||
polyhedronPoints=flatternArray(allPoints),
|
||||
allLp=len(allPoints),
|
||||
layerLength=len(allPoints[0]),
|
||||
loopToSecondLastLayer=allLp-2,
|
||||
sideFaces=[for(layerIndex=[0:loopToSecondLastLayer])let(
|
||||
currentLayeroffset=layerIndex*layerLength,
|
||||
nextLayeroffset=(layerIndex+1)*layerLength,
|
||||
layerFaces=[for(subLayerIndex=[0:layerLength-1])
|
||||
[
|
||||
currentLayeroffset+subLayerIndex, currentLayeroffset + listWrap(subLayerIndex+1,layerLength), nextLayeroffset+listWrap(subLayerIndex+1,layerLength), nextLayeroffset+subLayerIndex]
|
||||
]
|
||||
)layerFaces],
|
||||
polyhedronFaces=flatternArray(sideFaces)
|
||||
)
|
||||
[polyhedronPoints, polyhedronFaces, layerLength];
|
||||
|
||||
function flatternRecursion(array, init=[], currentIndex=0)=
|
||||
// this is a private function, init and currentIndex are for the function's use
|
||||
// only for when it's calling itself, which is why there is a simplified version flatternArray that just calls this one
|
||||
// array= array to flattern by one level of nesting
|
||||
// init= the array used to cancat with the next call, only for when the function calls itself
|
||||
// currentIndex= so the function can keep track of how far it's progressed through the array, only for when it's calling itself
|
||||
// returns= flatterned array, by one level of nesting
|
||||
let(
|
||||
shouldKickOffRecursion=currentIndex==undef?1:0,
|
||||
isLastIndex=currentIndex+1==len(array)?1:0,
|
||||
flatArray=shouldKickOffRecursion?flatternRecursion(array,[],0):
|
||||
isLastIndex?concat(init,array[currentIndex]):
|
||||
flatternRecursion(array,concat(init,array[currentIndex]),currentIndex+1)
|
||||
)
|
||||
flatArray;
|
||||
|
||||
function flatternArray(array)=
|
||||
// public version of flatternRecursion, has simplified params to avoid confusion
|
||||
// array= array to be flatterned
|
||||
// return= array that been flatterend by one level of nesting
|
||||
flatternRecursion(array);
|
||||
|
||||
function offsetAllFacesBy(array,offset)=[
|
||||
// polyhedron faces are simply a list of indices to points, if your concat points together than you probably need to offset
|
||||
// your faces array to points to the right place in the new list
|
||||
// array= array of point indicies
|
||||
// offset= number to offset all indecies by
|
||||
// return= array of point indices (i.e. faces) with offset applied
|
||||
for(faceIndex=[0:len(array)-1])[
|
||||
for(pointIndex=[0:len(array[faceIndex])-1])array[faceIndex][pointIndex]+offset
|
||||
]
|
||||
];
|
||||
|
||||
function extrudePolygonWithRadius(radiiPoints,h=5,r1=1,r2=1,fn=4)=
|
||||
// this basically calls makeCurvedPartOfPolyHedron twice to get the curved section of the final polyhedron
|
||||
// and then goes about assmbling them, as the side faces and the top and bottom face caps are missing
|
||||
// radiiPoints= series of [x,y,r] points,
|
||||
// h= height of the extrude (total including radius sections)
|
||||
// r1,r2= define the radius at the top and bottom of the extrud respectively, negative number flange out the extrude
|
||||
// fn= number of subdivisions
|
||||
// returns= [polyhedronPoints, polyhedronFaces]
|
||||
let(
|
||||
// top is the top curved part of the extrude
|
||||
top=makeCurvedPartOfPolyHedron(radiiPoints,r1,fn),
|
||||
topRadiusPoints=translate3Dcoords(top[0],[0,0,h-abs(r1)]),
|
||||
singeLayerLength=top[2],
|
||||
topRadiusFaces=top[1],
|
||||
radiusPointsLength=len(topRadiusPoints), // is the same length as bottomRadiusPoints
|
||||
// bottom is the bottom curved part of the extrude
|
||||
bottom=makeCurvedPartOfPolyHedron(radiiPoints,r2,fn),
|
||||
// Z axis needs to be multiplied by -1 to flip it so the radius is going in the right direction [1,1,-1]
|
||||
bottomRadiusPoints=translate3Dcoords(bottom[0],[0,0,abs(r2)],[1,1,-1]),
|
||||
// becaues the points will be all concatenated into the same array, and the bottom points come second, than
|
||||
// the original indices the faces are points towards are wrong and need to have an offset applied to them
|
||||
bottomRadiusFaces=offsetAllFacesBy(bottom[1],radiusPointsLength),
|
||||
// all of the side panel of the extrusion, connecting points from the inner layers of each
|
||||
// of the curved sections
|
||||
sideFaces=[for(i=[0:singeLayerLength-1])[
|
||||
i,
|
||||
listWrap(i+1,singeLayerLength),
|
||||
radiusPointsLength + listWrap(i+1,singeLayerLength),
|
||||
radiusPointsLength + i
|
||||
]],
|
||||
// both of these caps are simple every point from the last layer of the radius points
|
||||
topCapFace=[for(i=[0:singeLayerLength-1])radiusPointsLength-singeLayerLength+i],
|
||||
bottomCapFace=[for(i=[0:singeLayerLength-1])radiusPointsLength*2-singeLayerLength+i],
|
||||
finalPolyhedronPoints=concat(topRadiusPoints,bottomRadiusPoints),
|
||||
finalPolyhedronFaces=concat(topRadiusFaces,invertFaces(bottomRadiusFaces),invertFaces(sideFaces),[topCapFace],invertFaces([bottomCapFace]))
|
||||
)
|
||||
[
|
||||
finalPolyhedronPoints,
|
||||
finalPolyhedronFaces
|
||||
];
|
||||
|
||||
module polyRoundExtrude(radiiPoints,length=5,r1=1,r2=1,fn=10,convexity=10) {
|
||||
assert(len(radiiPoints) > 2, str("There must be at least 3 radii points for polyRoundExtrude. ", radiiPoints, " is not long enough, you need ", 3 - len(radiiPoints), " more point/s. Example: polyRoundExtrude([[11,0,1],[20,20,1.1],[8,7,0.5]],2,0.5,-0.8,fn=8);"));
|
||||
if(len(radiiPoints) > 2) {
|
||||
orderedRadiiPoints = CWorCCW(radiiPoints) == 1
|
||||
? reverseList(radiiPoints)
|
||||
: radiiPoints;
|
||||
|
||||
polyhedronPointsNFaces=extrudePolygonWithRadius(orderedRadiiPoints,length,r1,r2,fn);
|
||||
polyhedron(points=polyhedronPointsNFaces[0], faces=polyhedronPointsNFaces[1], convexity=convexity);
|
||||
}
|
||||
%translate([-25,0,0.2]){
|
||||
polygon(getpoints(points));//transparent copy of the polgon without rounding
|
||||
}
|
||||
translate([-50,0,0]){
|
||||
polygon(polyRound(points2,5));
|
||||
}
|
||||
%translate([-50,0,0.2]){
|
||||
polygon(getpoints(processRadiiPoints(points2)));//transparent copy of the polgon without rounding
|
||||
}
|
||||
//Example of features 2
|
||||
// 1 2 3 4 5 6
|
||||
b=[[-4,0,1],[5,3,1.5],[0,7,0.1],[8,7,10],[20,20,0.8],[10,0,10]]; //points
|
||||
polygon(polyRound(b,30));/*polycarious() will make the same shape but doesn't have radii conflict handling*/ //polygon(polycarious(b,30));
|
||||
%translate([0,0,0.3])polygon(getpoints(b));//transparent copy of the polgon without rounding
|
||||
|
||||
//Example of features 3
|
||||
// 1 2 3 4 5 6
|
||||
p=[[0,0,1.2],[0,20,1],[15,15,1],[3,10,3],[15,0,1],[6,2,10]];//points
|
||||
a=polyRound(p,5);
|
||||
translate([25,0,0]){
|
||||
polygon(a);
|
||||
}
|
||||
%translate([25,0,0.2]){
|
||||
polygon(getpoints(p));//transparent copy of the polgon without rounding
|
||||
}
|
||||
//example of radii conflict handling and debuging feature
|
||||
r1a=10; r1b=10;
|
||||
r2a=30; r2b=30;
|
||||
r3a=10; r3b=40;
|
||||
r4a=15; r4b=20;
|
||||
c1=[[0,0,0],[0,20,r1a],[20,20,r1b],[20,0,0]];//both radii fit and don't need to be changed
|
||||
translate([-25,-30,0]){
|
||||
polygon(polyRound(c1,8));
|
||||
}
|
||||
echo(str("c1 debug= ",polyRound(c1,8,mode=1)," all zeros indicates none of the radii were reduced"));
|
||||
|
||||
c2=[[0,0,0],[0,20,r2a],[20,20,r2b],[20,0,0]];//radii are too large and are reduced to fit
|
||||
translate([0,-30,0]){
|
||||
polygon(polyRound(c2,8));
|
||||
}
|
||||
echo(str("c2 debug= ",polyRound(c2,8,mode=1)," 2nd and 3rd radii reduced by 20mm i.e. from 30 to 10mm radius"));
|
||||
|
||||
c3=[[0,0,0],[0,20,r3a],[20,20,r3b],[20,0,0]];//radii are too large again and are reduced to fit, but keep their ratios
|
||||
translate([25,-30,0]){
|
||||
polygon(polyRound(c3,8));
|
||||
}
|
||||
echo(str("c3 debug= ",polyRound(c3,8,mode=1)," 2nd and 3rd radii reduced by 6 and 24mm respectively"));
|
||||
//resulting in radii of 4 and 16mm,
|
||||
//notice the ratio from the orginal radii stays the same r3a/r3b = 10/40 = 4/16
|
||||
c4=[[0,0,0],[0,20,r4a],[20,20,r4b],[20,0,0]];//radii are too large again but not corrected this time
|
||||
translate([50,-30,0]){
|
||||
polygon(polyRound(c4,8,mode=2));//mode 2 = no radii limiting
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// testingInternals();
|
||||
module testingInternals(){
|
||||
//example of rounding random points, this has no current use but is a good demonstration
|
||||
random=[for(i=[0:20])[rnd(0,50),rnd(0,50),/*rnd(0,30)*/1000]];
|
||||
R =polyRound(random,7);
|
||||
@@ -123,7 +208,7 @@ module examples(){
|
||||
radius=6;
|
||||
radiipoints=[[0,0,0],[10,20,radius],[20,0,0]];
|
||||
tangentsNcen=round3points(radiipoints);
|
||||
translate([100,0,0]){
|
||||
translate([10,0,0]){
|
||||
for(i=[0:2]){
|
||||
color("red")translate(getpoints(radiipoints)[i])circle(1);//plots the 3 input points
|
||||
color("cyan")translate(tangentsNcen[i])circle(1);//plots the two tangent poins and the circle centre
|
||||
@@ -131,67 +216,6 @@ module examples(){
|
||||
translate([tangentsNcen[2][0],tangentsNcen[2][1],-0.2])circle(r=radius,$fn=25);//draws the cirle
|
||||
%polygon(getpoints(radiipoints));//draws a polygon
|
||||
}
|
||||
|
||||
//for(i=[0:len(b2)-1]) translate([b2[i].x,b2[i].y,2])#circle(0.2);
|
||||
ex=[[0,0,-1],[2,8,0],[5,4,3],[15,10,0.5],[10,2,1]];
|
||||
translate([15,-50,0]){
|
||||
ang=55;
|
||||
minR=0.2;
|
||||
rotate([0,0,ang+270])translate([0,-5,0])square([10,10],true);
|
||||
clipP=[[9,1,0],[9,0,0],[9.5,0,0],[9.5,1,0.2],[10.5,1,0.2],[10.5,0,0],[11,0,0],[11,1,0]];
|
||||
a=RailCustomiser(ex,o1=0.5,minR=minR,a1=ang-90,a2=0,mode=2);
|
||||
b=revList(RailCustomiser(ex,o1=-0.5,minR=minR,a1=ang-90,a2=0,mode=2));
|
||||
points=concat(a,clipP,b);
|
||||
points2=concat(ex,clipP,b);
|
||||
polygon(polyRound(points,20));
|
||||
//%polygon(polyRound(points2,20));
|
||||
}
|
||||
|
||||
//the following exapmle shows how the offsets in RailCustomiser could be used to makes shells
|
||||
translate([-20,-60,0]){
|
||||
for(i=[-9:0.5:1])polygon(polyRound(RailCustomiser(ex,o1=i-0.4,o2=i,minR=0.1),20));
|
||||
}
|
||||
|
||||
// This example shows how a list of points can be used multiple times in the same
|
||||
nutW=5.5; nutH=3; boltR=1.6;
|
||||
minT=2; minR=0.8;
|
||||
nutCapture=[
|
||||
[-boltR, 0, 0],
|
||||
[-boltR, minT, 0],
|
||||
[-nutW/2, minT, minR],
|
||||
[-nutW/2, minT+nutH, minR],
|
||||
[nutW/2, minT+nutH, minR],
|
||||
[nutW/2, minT, minR],
|
||||
[boltR, minT, 0],
|
||||
[boltR, 0, 0],
|
||||
];
|
||||
aSquare=concat(
|
||||
[[0,0,0]],
|
||||
moveRadiiPoints(nutCapture,tran=[5,0],rot=0),
|
||||
[[20,0,0]],
|
||||
moveRadiiPoints(nutCapture,tran=[20,5],rot=90),
|
||||
[[20,10,0]],
|
||||
[[0,10,0]]
|
||||
);
|
||||
echo(aSquare);
|
||||
translate([40,-60,0]){
|
||||
polygon(polyRound(aSquare,20));
|
||||
translate([10,12,0])polygon(polyRound(nutCapture,20));
|
||||
}
|
||||
|
||||
translate([70,-52,0]){
|
||||
a=mirrorPoints(ex,0,[1,0]);
|
||||
polygon(polyRound(a,20));
|
||||
}
|
||||
|
||||
|
||||
translate([0,-90,0]){
|
||||
r_extrude(3,0.5*$t,0.5*$t,100)polygon(polyRound(b,30));
|
||||
#translate([7,4,3])r_extrude(3,-0.5,0.95,100)circle(1,$fn=30);
|
||||
}
|
||||
|
||||
translate([-30,-90,0])
|
||||
shell2d(-0.5,0,0)polygon(polyRound(b,30));
|
||||
}
|
||||
|
||||
function polyRound(radiipoints,fn=5,mode=0)=
|
||||
@@ -201,18 +225,26 @@ function polyRound(radiipoints,fn=5,mode=0)=
|
||||
mode=1 - Debug, output radius reduction for automatic radius limiting
|
||||
mode=2 - No radius limiting*/
|
||||
let(
|
||||
getpoints=mode==2?1:2,
|
||||
p=getpoints(radiipoints), //make list of coordinates without radii
|
||||
Lp=len(p),
|
||||
//remove the middle point of any three colinear points
|
||||
newrp=[
|
||||
for(i=[0:len(p)-1]) if(isColinear(p[wrap(i-1,Lp)],p[wrap(i+0,Lp)],p[wrap(i+1,Lp)])==0||p[wrap(i+0,Lp)].z!=0)radiipoints[wrap(i+0,Lp)]
|
||||
//remove the middle point of any three colinear points, otherwise adding a radius to the middle of a straigh line causes problems
|
||||
radiiPointsWithoutTrippleColinear=[
|
||||
for(i=[0:len(p)-1]) if(
|
||||
// keep point if it isn't colinear or if the radius is 0
|
||||
!isColinear(
|
||||
p[listWrap(i-1,Lp)],
|
||||
p[listWrap(i+0,Lp)],
|
||||
p[listWrap(i+1,Lp)]
|
||||
)||
|
||||
p[listWrap(i+0,Lp)].z!=0
|
||||
) radiipoints[listWrap(i+0,Lp)]
|
||||
],
|
||||
newrp2=processRadiiPoints(newrp),
|
||||
newrp2=processRadiiPoints(radiiPointsWithoutTrippleColinear),
|
||||
plusMinusPointRange=mode==2?1:2,
|
||||
temp=[
|
||||
for(i=[0:len(newrp2)-1]) //for each point in the radii array
|
||||
let(
|
||||
thepoints=[for(j=[-getpoints:getpoints])newrp2[wrap(i+j,len(newrp2))]],//collect 5 radii points
|
||||
thepoints=[for(j=[-plusMinusPointRange:plusMinusPointRange])newrp2[listWrap(i+j,len(newrp2))]],//collect 5 radii points
|
||||
temp2=mode==2?round3points(thepoints,fn):round5points(thepoints,fn,mode)
|
||||
)
|
||||
mode==1?temp2:newrp2[i][2]==0?
|
||||
@@ -270,11 +302,11 @@ function round3points(rp,fn)=
|
||||
tangD=r/tan(ang/2),//distance to the tangent point from p2
|
||||
circD=r/sin(ang/2),//distance to the circle centre from p2
|
||||
//find the angles from the p2 with respect to the postitive x axis
|
||||
a12=getAngle(p[0],p[1]),//angle from point 2 to 1
|
||||
a23=getAngle(p[2],p[1]),//angle from point 2 to 3
|
||||
angleFromPoint1ToPoint2=getAngle(p[0],p[1]),
|
||||
angleFromPoint2ToPoint3=getAngle(p[2],p[1]),
|
||||
//find tangent points
|
||||
t12=[p[1][0]-cos(a12)*tangD,p[1][1]-sin(a12)*tangD],//tangent point between points 1&2
|
||||
t23=[p[1][0]-cos(a23)*tangD,p[1][1]-sin(a23)*tangD],//tangent point between points 2&3
|
||||
t12=[p[1][0]-cos(angleFromPoint1ToPoint2)*tangD,p[1][1]-sin(angleFromPoint1ToPoint2)*tangD],//tangent point between points 1&2
|
||||
t23=[p[1][0]-cos(angleFromPoint2ToPoint3)*tangD,p[1][1]-sin(angleFromPoint2ToPoint3)*tangD],//tangent point between points 2&3
|
||||
//find circle centre
|
||||
tmid=getMidpoint(t12,t23),//midpoint between the two tangent points
|
||||
angCen=getAngle(tmid,p[1]),//angle from point 2 to circle centre
|
||||
@@ -294,11 +326,11 @@ function parallelFollow(rp,thick=4,minR=1,mode=1)=
|
||||
sgn=CWorCCW(rp),//rotation of the three points cw or ccw?let(sgn=mode==0?1:-1)
|
||||
circD=mode*sgn*r/sin(ang/2),//distance to the circle centre from p2
|
||||
//find the angles from the p2 with respect to the postitive x axis
|
||||
a12=getAngle(p[0],p[1]),//angle from point 2 to 1
|
||||
a23=getAngle(p[2],p[1]),//angle from point 2 to 3
|
||||
angleFromPoint1ToPoint2=getAngle(p[0],p[1]),
|
||||
angleFromPoint2ToPoint3=getAngle(p[2],p[1]),
|
||||
//find tangent points
|
||||
t12=[p[1][0]-cos(a12)*tangD,p[1][1]-sin(a12)*tangD],//tangent point between points 1&2
|
||||
t23=[p[1][0]-cos(a23)*tangD,p[1][1]-sin(a23)*tangD],//tangent point between points 2&3
|
||||
t12=[p[1][0]-cos(angleFromPoint1ToPoint2)*tangD,p[1][1]-sin(angleFromPoint1ToPoint2)*tangD],//tangent point between points 1&2
|
||||
t23=[p[1][0]-cos(angleFromPoint2ToPoint3)*tangD,p[1][1]-sin(angleFromPoint2ToPoint3)*tangD],//tangent point between points 2&3
|
||||
//find circle centre
|
||||
tmid=getMidpoint(t12,t23),//midpoint between the two tangent points
|
||||
angCen=getAngle(tmid,p[1]),//angle from point 2 to circle centre
|
||||
@@ -307,85 +339,106 @@ function parallelFollow(rp,thick=4,minR=1,mode=1)=
|
||||
)
|
||||
concat(cen,outR);
|
||||
|
||||
function is90or270(ang)=ang==90?1:ang==270?1:0;
|
||||
|
||||
function findPoint(ang1,refpoint1,ang2,refpoint2,r=0)=
|
||||
// finds the intersection of two lines given two angles and points on those lines
|
||||
let(
|
||||
overrideX=is90or270(ang1)?
|
||||
refpoint1.x:
|
||||
is90or270(ang2)?
|
||||
refpoint2.x:
|
||||
0,
|
||||
m1=tan(ang1),
|
||||
c1=refpoint1.y-m1*refpoint1.x,
|
||||
m2=tan(ang2),
|
||||
c2=refpoint2.y-m2*refpoint2.x,
|
||||
outputX=(c2-c1)/(m1-m2),
|
||||
outputY=m1*outputX+c1
|
||||
outputX=overrideX?overrideX:(c2-c1)/(m1-m2),
|
||||
outputY=is90or270(ang1)?m2*outputX+c2:m1*outputX+c1
|
||||
)
|
||||
[outputX,outputY,r];
|
||||
|
||||
function RailCustomiser(rp,o1=0,o2,mode=0,minR=0,a1,a2)=
|
||||
/*This function takes a series of radii points and plots points to run along side at a constanit distance, think of it as offset but for line instead of a polygon
|
||||
rp=radii points, o1&o2=offset 1&2,minR=min radius, a1&2=angle 1&2
|
||||
mode=1 - include endpoints a1&2 are relative to the angle of the last two points and equal 90deg if not defined
|
||||
mode=2 - endpoints not included
|
||||
mode=3 - include endpoints a1&2 are absolute from the x axis and are 0 if not defined
|
||||
function beamChain(radiiPoints,offset1=0,offset2,mode=0,minR=0,startAngle,endAngle)=
|
||||
/*This function takes a series of radii points and plots points to run along side at a consistant distance, think of it as offset but for line instead of a polygon
|
||||
radiiPoints=radii points,
|
||||
offset1 & offset2= The two offsets that give the beam it's thickness. When using with mode=2 only offset1 is needed as there is no return path for the polygon
|
||||
minR=min radius, if all of your radii are set properly within the radii points this value can be ignored
|
||||
startAngle & endAngle= Angle at each end of the beam, different mode determine if this angle is relative to the ending legs of the beam or absolute.
|
||||
mode=1 - include endpoints startAngle&2 are relative to the angle of the last two points and equal 90deg if not defined
|
||||
mode=2 - Only the forward path is defined, useful for combining the beam with other radii points, see examples for a use-case.
|
||||
mode=3 - include endpoints startAngle&2 are absolute from the x axis and are 0 if not defined
|
||||
negative radiuses only allowed for the first and last radii points
|
||||
|
||||
As it stands this function could probably be tidied a lot, but it works, I'll tidy later*/
|
||||
let(
|
||||
o2undef=o2==undef?1:0,
|
||||
o2=o2undef==1?0:o2,
|
||||
CWorCCW1=sign(o1)*CWorCCW(rp),
|
||||
CWorCCW2=sign(o2)*CWorCCW(rp),
|
||||
o1=abs(o1),
|
||||
o2b=abs(o2),
|
||||
Lrp3=len(rp)-3,
|
||||
Lrp=len(rp),
|
||||
a1=mode==0&&a1==undef?
|
||||
getAngle(rp[0],rp[1])+90:
|
||||
mode==2&&a1==undef?
|
||||
offset2undef=offset2==undef?1:0,
|
||||
offset2=offset2undef==1?0:offset2,
|
||||
CWorCCW1=sign(offset1)*CWorCCW(radiiPoints),
|
||||
CWorCCW2=sign(offset2)*CWorCCW(radiiPoints),
|
||||
offset1=abs(offset1),
|
||||
offset2b=abs(offset2),
|
||||
Lrp3=len(radiiPoints)-3,
|
||||
Lrp=len(radiiPoints),
|
||||
startAngle=mode==0&&startAngle==undef?
|
||||
getAngle(radiiPoints[0],radiiPoints[1])+90:
|
||||
mode==2&&startAngle==undef?
|
||||
0:
|
||||
mode==0?
|
||||
getAngle(rp[0],rp[1])+a1:
|
||||
a1,
|
||||
a2=mode==0&&a2==undef?
|
||||
getAngle(rp[Lrp-1],rp[Lrp-2])+90:
|
||||
mode==2&&a2==undef?
|
||||
getAngle(radiiPoints[0],radiiPoints[1])+startAngle:
|
||||
startAngle,
|
||||
endAngle=mode==0&&endAngle==undef?
|
||||
getAngle(radiiPoints[Lrp-1],radiiPoints[Lrp-2])+90:
|
||||
mode==2&&endAngle==undef?
|
||||
0:
|
||||
mode==0?
|
||||
getAngle(rp[Lrp-1],rp[Lrp-2])+a2:
|
||||
a2,
|
||||
OffLn1=[for(i=[0:Lrp3]) o1==0?rp[i+1]:parallelFollow([rp[i],rp[i+1],rp[i+2]],o1,minR,mode=CWorCCW1)],
|
||||
OffLn2=[for(i=[0:Lrp3]) o2==0?rp[i+1]:parallelFollow([rp[i],rp[i+1],rp[i+2]],o2b,minR,mode=CWorCCW2)],
|
||||
Rp1=abs(rp[0].z),
|
||||
Rp2=abs(rp[Lrp-1].z),
|
||||
endP1a=findPoint(getAngle(rp[0],rp[1]), OffLn1[0], a1,rp[0], Rp1),
|
||||
endP1b=findPoint(getAngle(rp[Lrp-1],rp[Lrp-2]), OffLn1[len(OffLn1)-1], a2,rp[Lrp-1], Rp2),
|
||||
endP2a=findPoint(getAngle(rp[0],rp[1]), OffLn2[0], a1,rp[0], Rp1),
|
||||
endP2b=findPoint(getAngle(rp[Lrp-1],rp[Lrp-2]), OffLn2[len(OffLn1)-1], a2,rp[Lrp-1], Rp2),
|
||||
getAngle(radiiPoints[Lrp-1],radiiPoints[Lrp-2])+endAngle:
|
||||
endAngle,
|
||||
OffLn1=[for(i=[0:Lrp3]) offset1==0?radiiPoints[i+1]:parallelFollow([radiiPoints[i],radiiPoints[i+1],radiiPoints[i+2]],offset1,minR,mode=CWorCCW1)],
|
||||
OffLn2=[for(i=[0:Lrp3]) offset2==0?radiiPoints[i+1]:parallelFollow([radiiPoints[i],radiiPoints[i+1],radiiPoints[i+2]],offset2b,minR,mode=CWorCCW2)],
|
||||
|
||||
Rp1=abs(radiiPoints[0].z),
|
||||
Rp2=abs(radiiPoints[Lrp-1].z),
|
||||
|
||||
endP1aAngle = getAngle(radiiPoints[0],radiiPoints[1]),
|
||||
endP1a=findPoint(endP1aAngle, OffLn1[0], startAngle,radiiPoints[0], Rp1),
|
||||
|
||||
endP1bAngle = getAngle(radiiPoints[Lrp-1],radiiPoints[Lrp-2]),
|
||||
endP1b=findPoint(endP1bAngle, OffLn1[len(OffLn1)-1], endAngle,radiiPoints[Lrp-1], Rp2),
|
||||
|
||||
endP2aAngle = getAngle(radiiPoints[0],radiiPoints[1]),
|
||||
endP2a=findPoint(endP2aAngle, OffLn2[0], startAngle,radiiPoints[0], Rp1),
|
||||
|
||||
endP2bAngle = getAngle(radiiPoints[Lrp-1],radiiPoints[Lrp-2]),
|
||||
endP2b=findPoint(endP2bAngle, OffLn2[len(OffLn1)-1], endAngle,radiiPoints[Lrp-1], Rp2),
|
||||
|
||||
absEnda=getAngle(endP1a,endP2a),
|
||||
absEndb=getAngle(endP1b,endP2b),
|
||||
negRP1a=[cos(absEnda)*rp[0].z*10+endP1a.x, sin(absEnda)*rp[0].z*10+endP1a.y, 0.0],
|
||||
negRP2a=[cos(absEnda)*-rp[0].z*10+endP2a.x, sin(absEnda)*-rp[0].z*10+endP2a.y, 0.0],
|
||||
negRP1b=[cos(absEndb)*rp[Lrp-1].z*10+endP1b.x, sin(absEndb)*rp[Lrp-1].z*10+endP1b.y, 0.0],
|
||||
negRP2b=[cos(absEndb)*-rp[Lrp-1].z*10+endP2b.x, sin(absEndb)*-rp[Lrp-1].z*10+endP2b.y, 0.0],
|
||||
OffLn1b=(mode==0||mode==2)&&rp[0].z<0&&rp[Lrp-1].z<0?
|
||||
negRP1a=[cos(absEnda)*radiiPoints[0].z*10+endP1a.x, sin(absEnda)*radiiPoints[0].z*10+endP1a.y, 0.0],
|
||||
negRP2a=[cos(absEnda)*-radiiPoints[0].z*10+endP2a.x, sin(absEnda)*-radiiPoints[0].z*10+endP2a.y, 0.0],
|
||||
negRP1b=[cos(absEndb)*radiiPoints[Lrp-1].z*10+endP1b.x, sin(absEndb)*radiiPoints[Lrp-1].z*10+endP1b.y, 0.0],
|
||||
negRP2b=[cos(absEndb)*-radiiPoints[Lrp-1].z*10+endP2b.x, sin(absEndb)*-radiiPoints[Lrp-1].z*10+endP2b.y, 0.0],
|
||||
OffLn1b=(mode==0||mode==2)&&radiiPoints[0].z<0&&radiiPoints[Lrp-1].z<0?
|
||||
concat([negRP1a],[endP1a],OffLn1,[endP1b],[negRP1b])
|
||||
:(mode==0||mode==2)&&rp[0].z<0?
|
||||
:(mode==0||mode==2)&&radiiPoints[0].z<0?
|
||||
concat([negRP1a],[endP1a],OffLn1,[endP1b])
|
||||
:(mode==0||mode==2)&&rp[Lrp-1].z<0?
|
||||
:(mode==0||mode==2)&&radiiPoints[Lrp-1].z<0?
|
||||
concat([endP1a],OffLn1,[endP1b],[negRP1b])
|
||||
:mode==0||mode==2?
|
||||
concat([endP1a],OffLn1,[endP1b])
|
||||
:
|
||||
OffLn1,
|
||||
OffLn2b=(mode==0||mode==2)&&rp[0].z<0&&rp[Lrp-1].z<0?
|
||||
OffLn2b=(mode==0||mode==2)&&radiiPoints[0].z<0&&radiiPoints[Lrp-1].z<0?
|
||||
concat([negRP2a],[endP2a],OffLn2,[endP2b],[negRP2b])
|
||||
:(mode==0||mode==2)&&rp[0].z<0?
|
||||
:(mode==0||mode==2)&&radiiPoints[0].z<0?
|
||||
concat([negRP2a],[endP2a],OffLn2,[endP2b])
|
||||
:(mode==0||mode==2)&&rp[Lrp-1].z<0?
|
||||
:(mode==0||mode==2)&&radiiPoints[Lrp-1].z<0?
|
||||
concat([endP2a],OffLn2,[endP2b],[negRP2b])
|
||||
:mode==0||mode==2?
|
||||
concat([endP2a],OffLn2,[endP2b])
|
||||
:
|
||||
OffLn2
|
||||
)//end of let()
|
||||
o2undef==1?OffLn1b:concat(OffLn2b,revList(OffLn1b));
|
||||
offset2undef==1?OffLn1b:concat(OffLn2b,revList(OffLn1b));
|
||||
|
||||
function revList(list)=//reverse list
|
||||
let(Llist=len(list)-1)
|
||||
@@ -395,10 +448,10 @@ function CWorCCW(p)=
|
||||
let(
|
||||
Lp=len(p),
|
||||
e=[for(i=[0:Lp-1])
|
||||
(p[wrap(i+0,Lp)].x-p[wrap(i+1,Lp)].x)*(p[wrap(i+0,Lp)].y+p[wrap(i+1,Lp)].y)
|
||||
(p[listWrap(i+0,Lp)].x-p[listWrap(i+1,Lp)].x)*(p[listWrap(i+0,Lp)].y+p[listWrap(i+1,Lp)].y)
|
||||
]
|
||||
)
|
||||
sign(sum(e));
|
||||
sign(polySum(e));
|
||||
|
||||
function CentreN2PointsArc(p1,p2,cen,mode=0,fn)=
|
||||
/* This function plots an arc from p1 to p2 with fn increments using the cen as the centre of the arc.
|
||||
@@ -409,25 +462,26 @@ function CentreN2PointsArc(p1,p2,cen,mode=0,fn)=
|
||||
mode==3, plotted counter clockwise
|
||||
*/
|
||||
let(
|
||||
CWorCCW=CWorCCW([cen,p1,p2]),//determine the direction of rotation
|
||||
isCWorCCW=CWorCCW([cen,p1,p2]),//determine the direction of rotation
|
||||
//determine the arc angle depending on the mode
|
||||
p1p2Angle=cosineRuleAngle(p2,cen,p1),
|
||||
arcAngle=
|
||||
mode==0?p1p2Angle:
|
||||
mode==1?p1p2Angle-360:
|
||||
mode==2&&CWorCCW==-1?p1p2Angle:
|
||||
mode==2&&CWorCCW== 1?p1p2Angle-360:
|
||||
mode==3&&CWorCCW== 1?p1p2Angle:
|
||||
mode==3&&CWorCCW==-1?p1p2Angle-360:
|
||||
cosineRuleAngle(p2,cen,p1)
|
||||
,
|
||||
mode==2&&isCWorCCW==-1?p1p2Angle:
|
||||
mode==2&&isCWorCCW== 1?p1p2Angle-360:
|
||||
mode==3&&isCWorCCW== 1?p1p2Angle:
|
||||
mode==3&&isCWorCCW==-1?p1p2Angle-360:
|
||||
cosineRuleAngle(p2,cen,p1),
|
||||
r=pointDist(p1,cen),//determine the radius
|
||||
p1Angle=getAngle(cen,p1) //angle of line 1
|
||||
)
|
||||
[for(i=[0:fn]) [cos(p1Angle+(arcAngle/fn)*i*CWorCCW)*r+cen[0],sin(p1Angle+(arcAngle/fn)*i*CWorCCW)*r+cen[1]]];
|
||||
[for(i=[0:fn])
|
||||
let(angleIncrement=(arcAngle/fn)*i*isCWorCCW)
|
||||
[cos(p1Angle+angleIncrement)*r+cen.x,sin(p1Angle+angleIncrement)*r+cen.y]];
|
||||
|
||||
function moveRadiiPoints(rp,tran=[0,0],rot=0)=
|
||||
[for(i=rp)
|
||||
function translateRadiiPoints(radiiPoints,tran=[0,0],rot=0)=
|
||||
[for(i=radiiPoints)
|
||||
let(
|
||||
a=getAngle([0,0],[i.x,i.y]),//get the angle of the this point
|
||||
h=pointDist([0,0],[i.x,i.y]) //get the hypotenuse/radius
|
||||
@@ -436,25 +490,25 @@ function moveRadiiPoints(rp,tran=[0,0],rot=0)=
|
||||
];
|
||||
|
||||
module round2d(OR=3,IR=1){
|
||||
offset(OR){
|
||||
offset(-IR-OR){
|
||||
offset(IR){
|
||||
offset(OR,$fn=100){
|
||||
offset(-IR-OR,$fn=100){
|
||||
offset(IR,$fn=100){
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module shell2d(o1,OR=0,IR=0,o2=0){
|
||||
module shell2d(offset1,offset2=0,minOR=0,minIR=0){
|
||||
difference(){
|
||||
round2d(OR,IR){
|
||||
offset(max(o1,o2)){
|
||||
round2d(minOR,minIR){
|
||||
offset(max(offset1,offset2)){
|
||||
children(0);//original 1st child forms the outside of the shell
|
||||
}
|
||||
}
|
||||
round2d(IR,OR){
|
||||
round2d(minIR,minOR){
|
||||
difference(){//round the inside cutout
|
||||
offset(min(o1,o2)){
|
||||
offset(min(offset1,offset2)){
|
||||
children(0);//shrink the 1st child to form the inside of the shell
|
||||
}
|
||||
if($children>1){
|
||||
@@ -478,25 +532,25 @@ module internalSq(size,r,center=0){
|
||||
}
|
||||
}
|
||||
|
||||
module r_extrude(ln,r1=0,r2=0,fn=30){
|
||||
module extrudeWithRadius(length,r1=0,r2=0,fn=30){
|
||||
n1=sign(r1);n2=sign(r2);
|
||||
r1=abs(r1);r2=abs(r2);
|
||||
translate([0,0,r1]){
|
||||
linear_extrude(ln-r1-r2){
|
||||
linear_extrude(length-r1-r2){
|
||||
children();
|
||||
}
|
||||
}
|
||||
for(i=[0:1/fn:1]){
|
||||
translate([0,0,i*r1]){
|
||||
linear_extrude(r1/fn){
|
||||
offset(n1*sqrt(sq(r1)-sq(r1-i*r1))-n1*r1){
|
||||
for(i=[0:fn-1]){
|
||||
translate([0,0,i/fn*r1]){
|
||||
linear_extrude(r1/fn+0.01){
|
||||
offset(n1*sqrt(sq(r1)-sq(r1-i/fn*r1))-n1*r1){
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
translate([0,0,ln-r2+i*r2]){
|
||||
linear_extrude(r2/fn){
|
||||
offset(n2*sqrt(sq(r2)-sq(i*r2))-n2*r2){
|
||||
translate([0,0,length-r2+i/fn*r2]){
|
||||
linear_extrude(r2/fn+0.01){
|
||||
offset(n2*sqrt(sq(r2)-sq(i/fn*r2))-n2*r2){
|
||||
children();
|
||||
}
|
||||
}
|
||||
@@ -504,19 +558,19 @@ module r_extrude(ln,r1=0,r2=0,fn=30){
|
||||
}
|
||||
}
|
||||
|
||||
function mirrorPoints(b,rot=0,atten=[0,0])= //mirrors a list of points about Y, ignoring the first and last points and returning them in reverse order for use with polygon or polyRound
|
||||
function mirrorPoints(radiiPoints,rot=0,endAttenuation=[0,0])= //mirrors a list of points about Y, ignoring the first and last points and returning them in reverse order for use with polygon or polyRound
|
||||
let(
|
||||
a=moveRadiiPoints(b,[0,0],-rot),
|
||||
temp3=[for(i=[0+atten[0]:len(a)-1-atten[1]])
|
||||
a=translateRadiiPoints(radiiPoints,[0,0],-rot),
|
||||
temp3=[for(i=[0+endAttenuation[0]:len(a)-1-endAttenuation[1]])
|
||||
[a[i][0],-a[i][1],a[i][2]]
|
||||
],
|
||||
temp=moveRadiiPoints(temp3,[0,0],rot),
|
||||
temp=translateRadiiPoints(temp3,[0,0],rot),
|
||||
temp2=revList(temp3)
|
||||
)
|
||||
concat(b,temp2);
|
||||
concat(radiiPoints,temp2);
|
||||
|
||||
function processRadiiPoints(rp)=
|
||||
[for(i=[0:len(rp)-1])
|
||||
[for(i=[0:len(rp)-1])
|
||||
processRadiiPoints2(rp,i)
|
||||
];
|
||||
|
||||
@@ -631,8 +685,8 @@ function cosineRuleAngle(p1,p2,p3)=
|
||||
)
|
||||
acos((sq(p23)+sq(p12)-sq(p13))/(2*p23*p12));
|
||||
|
||||
function sum(list, idx = 0, result = 0) =
|
||||
idx >= len(list) ? result : sum(list, idx + 1, result + list[idx]);
|
||||
function polySum(list, idx = 0, result = 0) =
|
||||
idx >= len(list) ? result : polySum(list, idx + 1, result + list[idx]);
|
||||
|
||||
function sq(x)=x*x;
|
||||
function getGradient(p1,p2)=(p2.y-p1.y)/(p2.x-p1.x);
|
||||
@@ -640,9 +694,9 @@ function getAngle(p1,p2)=p1==p2?0:invtan(p2[0]-p1[0],p2[1]-p1[1]);
|
||||
function getMidpoint(p1,p2)=[(p1[0]+p2[0])/2,(p1[1]+p2[1])/2]; //returns the midpoint of two points
|
||||
function pointDist(p1,p2)=sqrt(abs(sq(p1[0]-p2[0])+sq(p1[1]-p2[1]))); //returns the distance between two points
|
||||
function isColinear(p1,p2,p3)=getGradient(p1,p2)==getGradient(p2,p3)?1:0;//return 1 if 3 points are colinear
|
||||
module polyline(p) {
|
||||
module polyline(p, width=0.3) {
|
||||
for(i=[0:max(0,len(p)-1)]){
|
||||
line(p[i],p[wrap(i+1,len(p) )]);
|
||||
color([i*1/len(p),1-i*1/len(p),0,0.5])line(p[i],p[listWrap(i+1,len(p) )],width);
|
||||
}
|
||||
} // polyline plotter
|
||||
module line(p1, p2 ,width=0.3) { // single line plotter
|
||||
@@ -657,7 +711,7 @@ module line(p1, p2 ,width=0.3) { // single line plotter
|
||||
}
|
||||
|
||||
function getpoints(p)=[for(i=[0:len(p)-1])[p[i].x,p[i].y]];// gets [x,y]list of[x,y,r]list
|
||||
function wrap(x,x_max=1,x_min=0) = (((x - x_min) % (x_max - x_min)) + (x_max - x_min)) % (x_max - x_min) + x_min; // wraps numbers inside boundaries
|
||||
function listWrap(x,x_max=1,x_min=0) = (((x - x_min) % (x_max - x_min)) + (x_max - x_min)) % (x_max - x_min) + x_min; // wraps numbers inside boundaries
|
||||
function rnd(a = 1, b = 0, s = []) =
|
||||
s == [] ?
|
||||
(rands(min(a, b), max( a, b), 1)[0]):(rands(min(a, b), max(a, b), 1, s)[0]); // nice rands wrapper
|
||||
(rands(min(a, b), max( a, b), 1)[0]):(rands(min(a, b), max(a, b), 1, s)[0]); // nice rands wrapper
|
||||
|
269
roundAnythingExamples.scad
Normal file
269
roundAnythingExamples.scad
Normal file
@@ -0,0 +1,269 @@
|
||||
include <polyround.scad>
|
||||
|
||||
basicPolyRoundExample();
|
||||
// polyLineExample();
|
||||
// parametricPolyRoundExample();
|
||||
// experimentalParametricPolyRoundExample();
|
||||
// conflicResolutionExample();
|
||||
// translateRadiiPointsExample();
|
||||
// 2dShellExample();
|
||||
// beamChainExample();
|
||||
// mirrorPointsExample();
|
||||
// radiusExtrudeExample();
|
||||
// polyRoundExtrudeExample();
|
||||
|
||||
|
||||
// testing
|
||||
// testGeometries();
|
||||
|
||||
module basicPolyRoundExample(){
|
||||
// polyLine is a dev helper. Aim is to show the points of the polygon and their order before
|
||||
// you're ready to move on to polyRound and a polygon
|
||||
radiiPoints=[[-4,0,1],[5,3,1.5],[0,7,0.1],[8,7,10],[20,20,0.8],[10,0,10]];
|
||||
polygon(polyRound(radiiPoints,30));
|
||||
%translate([0,0,0.3])polygon(getpoints(radiiPoints));//transparent copy of the polgon without rounding
|
||||
}
|
||||
|
||||
module polyLineExample() {
|
||||
radiiPoints=[[-4,0,1],[5,3,1.5],[0,7,0.1],[8,7,10],[20,20,0.8],[10,0,10]];
|
||||
polyline(polyRound(radiiPoints,3), 0.05);
|
||||
translate([0,10,0])
|
||||
polyline(radiiPoints, 0.05);
|
||||
}
|
||||
|
||||
module parametricPolyRoundExample() {
|
||||
//Example of how a parametric part might be designed with this tool
|
||||
width=20; height=25;
|
||||
slotW=8; slotH=15;
|
||||
slotPosition=8;
|
||||
minR=1.5; farcornerR=6;
|
||||
internalR=3;
|
||||
// radii points defined in terms of shape dimensions
|
||||
points=[
|
||||
[0, 0, farcornerR],
|
||||
[0, height, minR],
|
||||
[slotPosition, height, minR],
|
||||
[slotPosition, height-slotH, internalR],
|
||||
[slotPosition+slotW, height-slotH, internalR],
|
||||
[slotPosition+slotW, height, minR],
|
||||
[width, height, minR],
|
||||
[width, 0, minR]
|
||||
];
|
||||
translate([-25,0,0]){
|
||||
polygon(polyRound(points,5));
|
||||
}
|
||||
%translate([-25,0,0.2])polygon(getpoints(points));//transparent copy of the polgon without rounding
|
||||
}
|
||||
|
||||
module experimentalParametricPolyRoundExample() {
|
||||
//very similar to parametric example, but with some experimental syntax
|
||||
width=20; height=25;
|
||||
slotW=8; slotH=15;
|
||||
slotPosition=8;
|
||||
minR=1.5; farcornerR=6;
|
||||
internalR=3;
|
||||
// radii points defined in terms of shape dimensions
|
||||
points2=[
|
||||
[0, 0, farcornerR],
|
||||
["l", height, minR],
|
||||
[slotPosition, "l", minR],
|
||||
["l", height-slotH, internalR],
|
||||
[slotPosition+slotW, "l", internalR],
|
||||
["l", height, minR],
|
||||
[width, "l", minR],
|
||||
["l", height*0.2, minR],
|
||||
[45, 0, minR+5, "ayra"]
|
||||
];
|
||||
translate([-50,0,0])polygon(polyRound(points2,5));
|
||||
%translate([-50,0,0.2])polygon(getpoints(processRadiiPoints(points2)));//transparent copy of the polgon without rounding
|
||||
}
|
||||
|
||||
module conflicResolutionExample(){
|
||||
//example of radii conflict handling and debuging feature
|
||||
function makeRadiiPoints(r1, r2)=[[0,0,0],[0,20,r1],[20,20,r2],[20,0,0]];
|
||||
|
||||
// the squre shape being 20 wide, two radii of 10 both fit into the shape (just)
|
||||
translate([-25,0,0])polygon(polyRound(makeRadiiPoints(10,10),50));
|
||||
|
||||
//radii are too large and are reduced to fit and will be reduce to 10 and 10
|
||||
translate([0,0,0])polygon(polyRound(makeRadiiPoints(30,30),50));
|
||||
|
||||
//radii are too large again and are reduced to fit, but keep their ratios r1 will go from 10 to 4 and r2 will go from 40 to 16
|
||||
translate([25,0,0])polygon(polyRound(makeRadiiPoints(10,40),50));
|
||||
|
||||
//mode 2 = no radii limiting
|
||||
translate([50,0,0])polygon(polyRound(makeRadiiPoints(12,20),50,mode=2));
|
||||
}
|
||||
|
||||
module translateRadiiPointsExample() {
|
||||
// This example shows how a list of points can be used multiple times in the same
|
||||
nutW=5.5; nutH=3; boltR=1.6;
|
||||
minT=2; minR=0.8;
|
||||
function nutCapture(startAndEndRadius=0)=[
|
||||
[-boltR, 0, startAndEndRadius],
|
||||
[-boltR, minT, 0],
|
||||
[-nutW/2, minT, minR],
|
||||
[-nutW/2, minT+nutH, minR],
|
||||
[nutW/2, minT+nutH, minR],
|
||||
[nutW/2, minT, minR],
|
||||
[boltR, minT, 0],
|
||||
[boltR, 0, startAndEndRadius],
|
||||
];
|
||||
|
||||
negativeNutCapture=translateRadiiPoints(nutCapture(),tran=[5,0]);
|
||||
rotatedNegativeNutCapture=translateRadiiPoints(nutCapture(1),tran=[20,5],rot=90);
|
||||
aSquare=concat(
|
||||
[[0,0,0]],
|
||||
negativeNutCapture,
|
||||
[[20,0,0]],
|
||||
rotatedNegativeNutCapture,
|
||||
[[20,10,0]],
|
||||
[[0,10,0]]
|
||||
);
|
||||
polygon(polyRound(aSquare,20));
|
||||
translate([-5,0,0])polygon(polyRound(nutCapture(),20));
|
||||
}
|
||||
|
||||
module 2dShellExample(){
|
||||
radiiPoints=[[-4,0,1],[5,3,1.5],[0,7,0.1],[8,7,10],[20,20,0.8],[10,0,10]];
|
||||
linear_extrude(1)shell2d(-0.5)polygon(polyRound(radiiPoints,30));
|
||||
translate([0,-10,0])linear_extrude(1)shell2d(-0.5){
|
||||
polygon(polyRound(radiiPoints,30));
|
||||
translate([8,8])gridpattern(memberW = 0.3, sqW = 1, iter = 17, r = 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
module beamChainExample(){
|
||||
function beamPoints(r1,r2,rStart=0,rEnd=0)=[[0,0,rStart],[2,8,0],[5,4,r1],[15,10,r2],[17,2,rEnd]];
|
||||
|
||||
// chained lines by themselves
|
||||
translate([0,0,0]){
|
||||
radiiPoints=beamPoints(0,0);
|
||||
for(i=[0: len(radiiPoints)-1]){color("red")translate([radiiPoints[i].x,radiiPoints[i].y,0])cylinder(d=0.2, h=1);}
|
||||
linear_extrude(1)polygon(polyRound(beamChain(radiiPoints,offset1=0.02, offset2=-0.02),20));
|
||||
}
|
||||
|
||||
|
||||
// Add some radii to the line transitions
|
||||
translate([0,-7,0]){
|
||||
radiiPoints=beamPoints(2,1);
|
||||
for(i=[0: len(radiiPoints)-1]){color("red")translate([radiiPoints[i].x,radiiPoints[i].y,0])cylinder(d=0.2, h=1);}
|
||||
linear_extrude(1)polygon(polyRound(beamChain(radiiPoints,offset1=0.02, offset2=-0.02),20));
|
||||
}
|
||||
|
||||
// Give make the lines beams with some thickness
|
||||
translate([0,-7*2,0]){
|
||||
radiiPoints=beamPoints(2,1);
|
||||
linear_extrude(1)polygon(polyRound(beamChain(radiiPoints,offset1=0.5, offset2=-0.5),20));
|
||||
}
|
||||
|
||||
// Add an angle to the start of the beam
|
||||
translate([0,-7*3,0]){
|
||||
radiiPoints=beamPoints(2,1);
|
||||
linear_extrude(1)polygon(polyRound(beamChain(radiiPoints,offset1=0.5, offset2=-0.5, startAngle=45),20));
|
||||
}
|
||||
|
||||
// Put a negative radius at the start for transationing to a flat surface
|
||||
translate([0,-7*4,0]){
|
||||
radiiPoints=beamPoints(2,1,rStart=-0.7);
|
||||
linear_extrude(1)polygon(polyRound(beamChain(radiiPoints,offset1=0.5, offset2=-0.5, startAngle=45),20));
|
||||
}
|
||||
|
||||
// Define more points for a polygon to be atteched to the end of the beam chain
|
||||
clipP=[[16,1.2,0],[16,0,0],[16.5,0,0],[16.5,1,0.2],[17.5,1,0.2],[17.5,0,0],[18,0,0],[18,1.2,0]];
|
||||
translate([-15,-7*5+3,0]){
|
||||
for(i=[0:len(clipP)-1]){color("red")translate([clipP[i].x,clipP[i].y,0])cylinder(d=0.2, h=1);}
|
||||
linear_extrude(1)polygon(polyRound(clipP,20));
|
||||
}
|
||||
|
||||
// Attached to the end of the beam chain by dividing the beam paths in forward and return and
|
||||
// concat other polygon inbetween
|
||||
translate([0,-7*6,0]){
|
||||
radiiPoints=beamPoints(2,1);
|
||||
forwardPath=beamChain(radiiPoints,offset1=0.5,startAngle=-15,mode=2);
|
||||
returnPath=revList(beamChain(radiiPoints,offset1=-0.5,startAngle=-15,mode=2));
|
||||
entirePath=concat(forwardPath,clipP,returnPath);
|
||||
linear_extrude(1)polygon(polyRound(entirePath,20));
|
||||
}
|
||||
|
||||
// Add transitioning radii into the end polygong
|
||||
translate([0,-7*7-2,0]){
|
||||
radiiPoints=beamPoints(2,1,rEnd=3);
|
||||
forwardPath=beamChain(radiiPoints,offset1=0.5,startAngle=-15,mode=2);
|
||||
returnPath=revList(beamChain(radiiPoints,offset1=-0.5,startAngle=-15,mode=2));
|
||||
entirePath=concat(forwardPath,clipP,returnPath);
|
||||
linear_extrude(1)polygon(polyRound(entirePath,20));
|
||||
}
|
||||
|
||||
// Define multiple shells from the the one set of points
|
||||
translate([0,-7*9,0]){
|
||||
radiiPoints=beamPoints(2,1,rEnd=3);
|
||||
for(i=[0:2]){linear_extrude(1)polygon(polyRound(beamChain(radiiPoints,offset1=-1+i*0.4, offset2=-1+i*0.4+0.25),20));}
|
||||
}
|
||||
}
|
||||
|
||||
module mirrorPointsExample(){
|
||||
function points(endR=0)=[[0,0,0],[2,8,0],[5,4,3],[15,10,0.5],[10,2,endR]];
|
||||
mirroredPoints=mirrorPoints(points(0),0,[1,0]);
|
||||
polygon(polyRound(mirroredPoints,20));
|
||||
mirroredPoints2=mirrorPoints(points(7),0,[1,0]);
|
||||
translate([0,-20,0])polygon(polyRound(mirroredPoints2,20));
|
||||
}
|
||||
|
||||
module radiusExtrudeExample(){
|
||||
radiiPoints=[[-4,0,1],[5,3,1.5],[0,7,0.1],[8,7,10],[20,20,0.8],[10,0,10]];
|
||||
extrudeWithRadius(3,0.5,0.5,50)polygon(polyRound(radiiPoints,30));
|
||||
#translate([7,4,3])extrudeWithRadius(3,-0.5,0.95,50)circle(1,$fn=30);
|
||||
}
|
||||
|
||||
module polyRoundExtrudeExample(){
|
||||
radiiPoints=[[10,0,10],[20,20,1.1],[8,7,10],[0,7,0.3],[5,3,0.1],[-4,0,1]];
|
||||
polyRoundExtrude(radiiPoints,2,0.5,-0.8,fn=8);
|
||||
}
|
||||
|
||||
module gridpattern(memberW = 4, sqW = 12, iter = 5, r = 3){
|
||||
round2d(0, r)rotate([0, 0, 45])translate([-(iter * (sqW + memberW) + memberW) / 2, -(iter * (sqW + memberW) + memberW) / 2])difference(){
|
||||
square([(iter) * (sqW + memberW) + memberW, (iter) * (sqW + memberW) + memberW]);
|
||||
for (i = [0:iter - 1], j = [0:iter - 1]){
|
||||
translate([i * (sqW + memberW) + memberW, j * (sqW + memberW) + memberW])square([sqW, sqW]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module testGeometries() {
|
||||
// Check these shapes preview (plus "thrown together") and render correctly with each PR
|
||||
points = [
|
||||
[0, 10, 5],
|
||||
[10, 0, 5],
|
||||
[0, -10, 5],
|
||||
[-10, 0, 5],
|
||||
];
|
||||
reversedPoints = [
|
||||
[-10, 0, 5],
|
||||
[0, -10, 5],
|
||||
[10, 0, 5],
|
||||
[0, 10, 5],
|
||||
];
|
||||
polyRoundExtrudeTestShape(points);
|
||||
translate([0,20,0])polyRoundExtrudeTestShape(reversedPoints);
|
||||
|
||||
// Bug report submitted by @lopisan in issue #16, similar to #11, geometry breaks with 90 degree angles
|
||||
didBreakWhen0=0;
|
||||
issue16pointsa=[[0, 0, 0], [0+didBreakWhen0, 10, 0], [10, 10+didBreakWhen0, 0]];
|
||||
translate([20,0,0])linear_extrude(1)polygon(polyRound( beamChain(issue16pointsa, offset1=1, offset2=-1), 30));
|
||||
|
||||
didBreakWhen0b=1e-6;
|
||||
issue16pointsb=[[0, 0, 0], [0+didBreakWhen0b, 10, 0], [10, 10+didBreakWhen0b, 0]];
|
||||
translate([20,15,0])linear_extrude(1)polygon(polyRound( beamChain(issue16pointsb, offset1=1, offset2=-1), 30));
|
||||
|
||||
}
|
||||
|
||||
module polyRoundExtrudeTestShape(points) {
|
||||
// make sure no faces are inverted
|
||||
difference() {
|
||||
translate([0, 0, -2.5]) polyRoundExtrude(points,r1=-1,r2=1);
|
||||
sphere(d=9);
|
||||
translate([0,0,7])sphere(d=9);
|
||||
}
|
||||
}
|
91
unionRoundMask-Doc.md
Normal file
91
unionRoundMask-Doc.md
Normal file
@@ -0,0 +1,91 @@
|
||||
## unionRoundMask / unionRound
|
||||
---
|
||||
### Union with round fillet at selected places. Created for Round-Anything by TLC123 (2021).
|
||||
A shortcut for faster fillet union is enabled when the operation constrained to convex operands.
|
||||
For most cases unionRoundMask / unionRound replaces MinkowskiRound.
|
||||
Combined with a system of mask selectors, unionRound becomes even more versetile.
|
||||
|
||||

|
||||
|
||||
## unionRoundMask
|
||||
---
|
||||
Union with round fillet at selected places.
|
||||
### module unionRoundMask(r=1, detail = 5 , q=70, epsilon = 1e-6, showMask = true , includeOperands = true)
|
||||
|
||||
Masks are a method to perform unionRound on selected only areas,
|
||||
and circumvents the previous limitation to common convex work area.
|
||||
Mask are essentially just common primitives that is used to mark out areas by intersection.
|
||||
|
||||
r:
|
||||
* approximate radius for fillet. Exact radius is dependant on crease angle.
|
||||
* detail: numbers of fillet segments. 1 is essensially a chamfer/bevel.
|
||||
* Set low for faster preview. ( $preview?3:10 )
|
||||
|
||||
q:
|
||||
* determine how detailed clad operations are.
|
||||
* Set low for faster preview. ( $preview?30:70 )
|
||||
|
||||
epsilon:
|
||||
* For debugging, leave as is.
|
||||
|
||||
showMask:
|
||||
* For debugging, try it.
|
||||
|
||||
includeOperands:
|
||||
* For debugging, render only fillet when false.
|
||||
|
||||
### usage:
|
||||
````
|
||||
unionRoundMask( r = 1 , detail = $preview ? 3 : 10 , q = $preview ? 30 : 70 )
|
||||
{
|
||||
yourObject1();
|
||||
yourObject2();
|
||||
yourMask1();
|
||||
yourMask2();
|
||||
yourMask3();
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
}
|
||||
````
|
||||
|
||||
---
|
||||
## unionRound
|
||||
---
|
||||
### module unionRound(r=1, detail = 5 , q=70, epsilon = 1e-6 , includeOperands = true )
|
||||
|
||||
Module unionRound is the underlying work module of unionRoundMask.
|
||||
It can be used by it self, in some cases faster but more raw.
|
||||
|
||||
### usage:
|
||||
````
|
||||
unionRoundMask( r = 1 , detail = $preview ? 3 : 10 , q= $preview ? 30 : 70 )
|
||||
{
|
||||
yourObject1();
|
||||
yourObject2();
|
||||
}
|
||||
````
|
||||
|
||||
---
|
||||
## intersectionRound
|
||||
---
|
||||
module intersectionRound(r, q=70, epsilon = 1e-6,showOperands = true)
|
||||
prototype module
|
||||
Undocumented for now.
|
||||
|
||||
---
|
||||
## helpers
|
||||
---
|
||||
````
|
||||
module clad(r,q=70) // speed is limited to convex operand.
|
||||
module shell(r,q=70) // not in use.
|
||||
module inset(r,q=20) // speed is limited to convex operand.
|
||||
````
|
||||
---
|
||||
## Citation
|
||||
---
|
||||
### roundUnionMask Includes code based on examples from:
|
||||
Kogan, Jonathan (2017)
|
||||
"A New Computationally Efficient Method for Spacing n Points on a Sphere,"
|
||||
Rose-Hulman Undergraduate Mathematics Journal: Vol. 18 : Iss. 2 , Article 5.
|
||||
Available at: [https://scholar.rose-hulman.edu/rhumj/vol18/iss2/5]
|
179
unionRoundMask.scad
Normal file
179
unionRoundMask.scad
Normal file
@@ -0,0 +1,179 @@
|
||||
////////////////////////////////////////////////////////
|
||||
/*
|
||||
unionRound() 1.0 Module by Torleif Ceder - TLC123 late summer 2021
|
||||
Pretty fast Union with radius, But limited to a subset of cases
|
||||
Usage
|
||||
unionRound( radius , detail )
|
||||
{
|
||||
YourObject1();
|
||||
YourObject2();
|
||||
}
|
||||
unionRoundMask (r, detail , epsilon ,showMask )
|
||||
{
|
||||
YourObject1();
|
||||
YourObject2();
|
||||
YourMask();
|
||||
YourMask();
|
||||
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
|
||||
}
|
||||
limitations:
|
||||
0. Only really fast when boolean operands are convex,
|
||||
Minkowski is fast in that case.
|
||||
1. Boolean operands may be concave but can only touch
|
||||
in a single convex area
|
||||
2. Radius is of elliptic type and is only approximate r
|
||||
were operand intersect at perpendicular angle.
|
||||
*/
|
||||
////////////////////////////////////////////////////////
|
||||
// Demo code
|
||||
demo= false;
|
||||
if (demo)
|
||||
unionRoundMask( r=1.5 , detail= 5 , q=70, includeOperands = true) {
|
||||
cube([10,10,2],true);
|
||||
rotate([20,-10,0])cylinder(5,1,1,$fn=12);
|
||||
translate([0,0,1.5])cube([1.5,10,3],center=true); //mask
|
||||
rotate(90)
|
||||
translate([0,0,1.5])cube([3,10,3],center=true); //mask
|
||||
}
|
||||
|
||||
// end of demo code
|
||||
//
|
||||
module unionRoundMask(r=1, detail = 5,q=70, epsilon = 1e-6, showMask = false, includeOperands = true) {
|
||||
//automask if none
|
||||
if($children <=2){
|
||||
unionRoundMask(r,detail,q,epsilon,showMask, includeOperands)
|
||||
{
|
||||
children(0);
|
||||
children(1);
|
||||
clad(max(r),q) intersection(){
|
||||
children(0);
|
||||
children(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
union() {
|
||||
if(includeOperands){
|
||||
children(0);
|
||||
children(1);
|
||||
}
|
||||
if (showMask && $children > 2) %
|
||||
for (i = [2: max(2, $children - 1)]) children(i);
|
||||
|
||||
if ($children > 2)
|
||||
for (i = [2: max(2, $children - 1)]) {
|
||||
intersection() {
|
||||
children(i);
|
||||
|
||||
unionRound(r, detail,q, epsilon,includeOperands) {
|
||||
intersection() {
|
||||
children(0);
|
||||
children(i); // mask
|
||||
}
|
||||
intersection() {
|
||||
children(1);
|
||||
children(i); // mask
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module unionRound(r=1, detail = 5,q=70, epsilon = 1e-6, includeOperands=true) {
|
||||
if(includeOperands){
|
||||
children(0);
|
||||
children(1);
|
||||
}
|
||||
step = 90 / detail;
|
||||
rx=is_list(r)?r[1]:r;
|
||||
ry=is_list(r)?r[0]:r;
|
||||
union()for (i = [0: detail-1]) {
|
||||
{
|
||||
x = rx - sin(i * step ) * rx;
|
||||
y = ry - cos(i * step ) * ry;
|
||||
xi = rx - sin((i * step + step) ) * rx;
|
||||
yi = ry - cos((i * step + step) ) * ry;
|
||||
// color(rands(0, 1, 3, i))
|
||||
hull() {
|
||||
intersection() {
|
||||
// shell(epsilon)
|
||||
clad(x,q) children(0);
|
||||
// shell(epsilon)
|
||||
clad(y,q) children(1);
|
||||
}
|
||||
intersection() {
|
||||
// shell(epsilon)
|
||||
clad(xi,q) children(0);
|
||||
// shell(epsilon)
|
||||
clad(yi,q) children(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prototype module slow maybe on concave feature
|
||||
module intersectionRound(r, q=70, epsilon = 1e-6,showOperands = true) {
|
||||
%if (showOperands){children(0);
|
||||
children(1);}
|
||||
|
||||
clad(r,q) inset(r,q)
|
||||
hull()intersection() {
|
||||
children(0);
|
||||
children(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// unionRound helper expand by r
|
||||
module clad(r,q=70) {
|
||||
minkowski() {
|
||||
children();
|
||||
// icosphere(r,2);
|
||||
isosphere(r,q);
|
||||
}
|
||||
}
|
||||
// unionRound helper
|
||||
module shell(r,q=70) {
|
||||
difference() {
|
||||
clad(r,q) children();
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
// inset 3d "negative offset", optimally on convex hull
|
||||
// else jagged inner corners by q quality factor
|
||||
module inset(r,q=20){
|
||||
a= generatepoints(q)*r;
|
||||
//#children();
|
||||
intersection_for(t=a){
|
||||
translate(t ) children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// The following is a sphere with some equidistant properties.
|
||||
// Not strictly necessary
|
||||
|
||||
Kogan, Jonathan (2017) "A New Computationally Efficient Method for Spacing n Points on a Sphere," Rose-Hulman Undergraduate Mathematics Journal: Vol. 18 : Iss. 2 , Article 5.
|
||||
Available at: https://scholar.rose-hulman.edu/rhumj/vol18/iss2/5 */
|
||||
|
||||
function sphericalcoordinate(x,y)= [cos(x )*cos(y ), sin(x )*cos(y ), sin(y )];
|
||||
function NX(n=70,x)=
|
||||
let(toDeg=57.2958,PI=acos(-1)/toDeg,
|
||||
start=(-1.+1./(n-1.)),increment=(2.-2./(n-1.))/(n-1.) )
|
||||
[ for (j= [0:n-1])let (s=start+j*increment )
|
||||
sphericalcoordinate( s*x*toDeg, PI/2.* sign(s)*(1.-sqrt(1.-abs(s)))*toDeg)];
|
||||
function generatepoints(n=70)= NX(n,0.1+1.2*n);
|
||||
module isosphere(r,q=70){
|
||||
a= generatepoints(q);
|
||||
scale(r)hull()polyhedron(a,[[for(i=[0:len(a)-1])i]]);
|
||||
}
|
Reference in New Issue
Block a user