├── LICENSE ├── README.md ├── cad_kickstart ├── constructive-compiled.scad └── tryExamples.scad ├── compile-lib.sh ├── constructive-all.scad ├── constructive-compiled.scad ├── examples ├── constructive-compiled.scad ├── mount-demo.scad ├── pulley-demo.scad └── skin.scad ├── gallery ├── cart14-Plugs.png ├── cart14-tensioner.png └── sources │ ├── cart14-Plugs.scad │ └── cart14-tensioner.scad ├── img ├── mount.gif └── pulley.gif ├── kickstart.zip ├── sources ├── assemble.scad ├── basicfuncs.scad ├── colorSystem.scad ├── constructive.scad ├── geomInfo.scad ├── globals.scad ├── metricScrews.scad ├── nonMetricScrews.scad ├── placements.scad └── typeSystem.scad ├── tubeV2.scad ├── tutorials ├── basic-tutorial.md ├── partII-images │ ├── assemble1.png │ ├── assemble2.png │ ├── bentStripXZ.png │ ├── cscale.png │ ├── every1.png │ ├── every2.png │ ├── g.png │ ├── ifFirst.png │ ├── ifLast.png │ ├── margin.png │ ├── pieces_3_span.png │ ├── pieces_5.png │ ├── pieces_5_span_X.png │ ├── reflectZ.png │ ├── removing_var.png │ ├── runFor.png │ ├── selectPieces.png │ ├── sides1.png │ ├── sides2.png │ ├── sidesReflectX.png │ ├── skin-toup.png │ ├── skin.png │ ├── skipFirst.png │ ├── spanAllBalls.png │ ├── spanAllButLastBall.png │ ├── spanButLast_3_boxes.png │ ├── span_3_boxes.png │ ├── span_5_boxes_100.png │ ├── span_8_rotated_boxes.png │ ├── tubeShell.png │ ├── vRepeat1.png │ ├── vSpread1.png │ ├── valPtr.png │ ├── vals2.png │ └── vals3.png ├── tutorial-images │ ├── align1.png │ ├── align2.png │ ├── align3.png │ ├── align4.png │ ├── ball.png │ ├── box1.png │ ├── box2.png │ ├── box3.png │ ├── box4.png │ ├── box5.png │ ├── box6.png │ ├── box7.png │ ├── box8.png │ ├── box9.png │ ├── mainblock.png │ ├── move1.png │ ├── move2.png │ ├── stack1.png │ ├── stack2.png │ ├── tube1.png │ ├── tube1c.png │ ├── tube2.png │ ├── tube3.png │ ├── turn1.png │ ├── turn2.png │ └── turn3.png ├── tutorial-partII.md └── tutorial-partIII.md └── versionlog.txt /LICENSE: -------------------------------------------------------------------------------- 1 | this work is dual licensed under GPL-2.0 and CERN-OHL-W`licenses 2 | *You can choose between one of them if you use this work.* 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## License 2 | 3 | This work is dual-licensed under GPL 2.0 or CERN-OHL-W . 4 | 5 | *You can choose between one of them if you use this work.* 6 | 7 | `SPDX-License-Identifier: GPL-2.0 OR CERN-OHL-W` 8 | 9 | # Constructive Library: 10 | Extends OpenScad Language for complex and complementary mechanical parts with less mathematical code. Offers a "Stamping" approach to quickly create mechanical parts fitting and complementing one another. This is used as a replacement for Constraints known form other CAD systems 11 | Easily create holes from modules and overcome many Openscad's limitiations for complex models using the library's own syntax. 12 | 13 | *Compiles directly by OpenScad*. No other tools needed. Just include the: 14 | _constructive-compiled.scad_ in your .scad file, that is all. 15 | 16 | ---- 17 | 18 | ![screeen](./img/mount.gif) 19 | 20 | 21 | Constructive Library (about 1900 lines of Code) 22 | introduces a different approach to OpenScad Syntax: you *rarely need difference(), for(),intersection()* or their one-to-one equivalents, In the *library's own dialect a for()* block might look like: 23 | 24 | pieces(4) X(every(35)) turnXY (vals(10,25,-15,40)) cube(10); 25 | 26 | It is all *valid OpenScad*, you do not need any additional programs, just the OpenScad and the constructive-compiled.scad file. 27 | 28 | The Constuctive-Syntax tries to align more with mechanical construction of parts and less with mathematical concepts like arrays, vectors and functions. (but they are heavily used behind the scenes). So it aims to be more concise and fluent for mechanical parts than vanilla OpenScad. It allows you to make holes from Modules and really good deal more. 29 | 30 | NOTE: if you use a development version openscad, please make sure the experimental option "lazy Unions" is tuned off under Settings. This experiment breaks Openscads compatibility and you might encounter problems with this library also. 31 | 32 | 33 | For a **basic introduction** (specially if you are new to Openscad ) 34 | see the [beginners tutorial](./tutorials/basic-tutorial.md) it explains Constructive Syntax for main Building blocks, like tube(), box() or bentStrip() and their placement and alignment in space like stack() , align(), X(),Y(),Z() or turnXZ() 35 | 36 | [Part II tutorial](./tutorials/tutorial-partII.md) shows somee basic object modification like reflectX(), cScale() ,or colors and then goes on to explain, how to work with sets of similar objects without for(), with: pieces(), span(), vals(), selectPieces(), etc.. 37 | 38 | [Part III tutorial](./tutorials/tutorial-partIII.md) shows more advanced Features like grouping commands into a g() group, working with Parts, and combinig them into Assembly 39 | 40 | 41 | __if you have questions you can ask me on the Openscad mailing list: https://lists.openscad.org/list/discuss.lists.openscad.org . Just add: constructive-lib to your subject to make sure i will see it__ 42 | 43 | 44 | For a more advanced use also look at the explanations inside the example below 45 | 46 | https://github.com/solidboredom/constructive/blob/main/examples/mount-demo.scad 47 | 48 | there is also another Example at: 49 | 50 | https://github.com/solidboredom/constructive/blob/main/examples/pulley-demo.scad 51 | ![screeen](./img/pulley.gif) 52 | 53 | >Note: Here A Gallery where some shiny constructive examples will be added, 54 | to show what can be acheived 55 | 56 | https://github.com/solidboredom/constructive/blob/main/gallery/ 57 | 58 | >Note: see here the source code ofthe Gallery pieces here: 59 | https://github.com/solidboredom/constructive/blob/main/gallery/sources/ 60 | 61 | how little code is actually needed for this, 62 | the code is in part not commented nor cleaned up, but it still can be used for reference 63 | 64 | ------------------- 65 | The easiest way to try out the Library is to download the [kickstart.zip](https://github.com/solidboredom/constructive/blob/main/kickstart.zip) 66 | 67 | 68 | Still uncovered are inverse transformations like in: 69 | 70 | g(X(10),Y(15),turnXY(45),X(30)) 71 | 72 | ``` 73 | g(backwards([X(10),Y(15),turnXY(45),X(30)]) 74 | 75 | box(10); 76 | ``` 77 | 78 | also part inheritance, part selector prefixes, internal type Ssystem and some more features 79 | 80 | Try it! i hope you will find it as useful as i do. 81 | 82 | Peter 83 | -------------------------------------------------------------------------------- /cad_kickstart/tryExamples.scad: -------------------------------------------------------------------------------- 1 | include 2 | 3 | box(side=10); 4 | -------------------------------------------------------------------------------- /compile-lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #../cad_devlibsV6/v6assemble.scad \ 3 | 4 | cat \ 5 | sources/basicfuncs.scad \ 6 | sources/globals.scad \ 7 | sources/typeSystem.scad \ 8 | sources/geomInfo.scad \ 9 | sources/placements.scad \ 10 | sources/colorSystem.scad \ 11 | sources/assemble.scad \ 12 | sources/constructive.scad \ 13 | sources/metricScrews.scad \ 14 | sources/nonMetricScrews.scad \ 15 | | grep -v '^include' > ./constructive-compiled.scad 16 | echo "constructive-compiled.scad written." 17 | cp constructive-compiled.scad examples/constructive-compiled.scad 18 | cp constructive-compiled.scad cad_kickstart/constructive-compiled.scad 19 | zip kickstart.zip cad_kickstart/* 20 | -------------------------------------------------------------------------------- /constructive-all.scad: -------------------------------------------------------------------------------- 1 | //This is a part of: 2 | //CONSTRUCTIVE LIBRARY by PPROJ (version from 05.06.2021) 3 | //released under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 4 | 5 | //you only need a single file: constructive-compiled.scad which 6 | //contains all the partial files you find. you can ignore everything else.. 7 | //just include it in your code by: 8 | //include 9 | 10 | //if you wish to improve the library or make changes to it, 11 | // it might be handier to use: 12 | //include instead. so you do not have to recreate constructive-compiled.scad from the parts 13 | //every time you make a change to a part of the library 14 | 15 | include 16 | include 17 | include 18 | include 19 | include 20 | include 21 | include 22 | include 23 | include 24 | include 25 | -------------------------------------------------------------------------------- /examples/mount-demo.scad: -------------------------------------------------------------------------------- 1 | //constructive library by PPROJ (version from 05.06.2021) 2 | //under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 3 | 4 | //Demo of some possibilities of the 5 | //"Constructive" Library by PPROJ (version from 05.06.2021) 6 | 7 | include 8 | 9 | $fn=21; 10 | 11 | //asks user to set Animations FPS and Steps. 12 | // this module is included below 13 | animationMessage(); 14 | 15 | 16 | //this function returns list of Names of Parts. 17 | //each element of the list specifies parts to be displayed 18 | // in one time frame of OpenScad anmation 19 | function partNameTextSelector(t=$t) = 20 | let(partSets=[ 21 | "screws,base,mountEnds" ,"base" 22 | ,"screws,base" ,"base" 23 | ,"base,mountEnds(count=2)","mountEnds" 24 | ,"mountEnds(count=1)" ,"screws" 25 | ]) partSets[round(t*len(partSets))]; 26 | 27 | //this is the actual call to the library to render parts 28 | //with names returned by the partNameTextSelector function 29 | assemble(partNameTextSelector($t)) 30 | mount(); //everything in this demo (but screws) is constructed 31 | //inside mount() module 32 | //which is coded below 33 | 34 | //if you do not want animations 35 | //you can specify part names directly instead, like: 36 | // assemble("screws,base,mountEnds(count=1)") mount(); 37 | 38 | 39 | //the autocolor()function used below is passed this custom olor table 40 | partColors= [ 41 | ["base",khaki,0.5] 42 | ,["mountEnds",pink,.6] 43 | ,["screws",black,.5] 44 | ]; 45 | 46 | //here in this module we construct everything 47 | module mount() 48 | //uses the colors above 49 | autoColor(custom=partColors) 50 | //appyTo("somename") speifies default name of the part 51 | //which following add(), remove(), confine() or addRemove() will affect, 52 | //unless specifically specified by their first argument, like in add("mountEnds") 53 | applyTo("base",height(margin(8)) //height() sets default if height of tube() or box() 54 | //is not given as argument of their calls. 55 | //its value is also returned by the heightInfo() function if needed. 56 | ,chamfer(-2,-2) //chamfers top, bottom, and/or sides of 57 | //each child by an approx. 2 mm cut 58 | ,solid()) //solid means that children tube() and tubeFast() 59 | //will produce solid rods without inner holes 60 | { 61 | //most commands scan be chained like: X(10) turnXY(30) Z(40) TOUP() cube(10); 62 | //but also grouped under one g(....) : g(X(10),turnXY(30),Z(40),TOUP()) cube(40); 63 | //the result is the same, but the g(...) variant is FASTER in openscad, so it is the preferred one. 64 | 65 | g(Z(-4),TOUP()) //X(),Y(),Z() just move the object like 66 | //translate([x,y,z]) 67 | //TOUP() makes the following tube() or box() stand on the 68 | //coordinates point they are drawn in,instead of beeing centered on them. 69 | //it works similar to the cube([10,10,10]) vs cube([10,10,10],center=true) 70 | //but makes it possible to align to any of the 6 cube's corners 71 | //TOUP() is a shorthand for align(TOUP) 72 | //in Fact align(TOUP,TOREAR,TORIGHT)box(10); is equivalent to cube([10,10,10]); 73 | // and just the box(10); is equivalent to cube([10,10,10],center=true); 74 | { 75 | //add() adds the following objects to the part specified by the parent applyTo() 76 | // this ones will add to the part called "base". bacause this specific add(...) is achild of an applyTo("base",....) above. 77 | add(stack()) //stack() stacks tube()s,box()es or both !!!!NOT SEPARATED by ";"!!!! upon each other (or sidwards if wished) 78 | tube(dOuter=75) 79 | tube(dOuter=52,h=10); 80 | //margin() function is used to make holes, around a part, 81 | //bigger size (by the margin given or default) 82 | // than the part itself 83 | //this function : margin(14,0.2) will return 14.2 if called inside of child of remove() 84 | // and only 14 if called inside of a child of add() 85 | //if it is used after remove(). the currrent default is..8 so marging(14) will return 14.8 or 14 coordingly 86 | remove(Z(-margin(0)/2),chamfer(1,1)) 87 | tube(d=40,h=18+1); 88 | } 89 | remove(X(-15),Y(-39),Z(1),TOUP()) 90 | //tubeFast() works faster than tube() but will ignore chamfer. it cannot chamfer. 91 | tubeFast(d=30,h=40); 92 | 93 | // two() is a shorthand for pieces(2) essentially a for($valPtr=[0:1]) 94 | //removes from the part specified by applyTo("base") 95 | //AND adds to part "screws" 96 | two() 97 | //reflectY() is equivalent ot mirror([1,-1,1]) or similar 98 | //sides() function returns -1 for the first element and 1 for the second 99 | add(reflectY(sides()),Y(96),Z(4) 100 | ,chamfer(-.5,-.5),turnYZ() 101 | ,align(TOFRONT,TODOWN)) 102 | box(y=8,x=12,h=4); 103 | two() 104 | add(turnXY(sides(90)),X(30)) 105 | hull() 106 | { 107 | X(-3)tubeFast(d=20); 108 | X(50)box(side=10); 109 | } 110 | 111 | g(TOUP(),chamfer(-1,-1)) 112 | { 113 | two() 114 | remove(add="screws", 115 | reflectY(sides()),Y(115-22),turnYZ(-90)) 116 | screwM3(h=20);//screwM4 and screwM3 are part of the library 117 | //adds to the part specified by applyTo("base") 118 | //AND removes from "mountEnds" 119 | add(Z(-4),remove="mountEnds") 120 | box(x=margin(10),y=margin(224),h=margin(8)); 121 | } 122 | //pieces(n) is a shorthand for 123 | // for ($varPtr =[0:1:n]) and a bit more 124 | // argInt("count",default=2) gets the value of an argument 125 | // specified in the assemble() string 126 | //for Example argInt("someval") returns the number 123 127 | //for a call of assemble("mountEnds(someval=123,count=2)")mount(); 128 | pieces(argInt("count",default=2)) 129 | add("mountEnds" 130 | ,reflectY(sides()),Y(100),Z(8) 131 | ,turnYZ(),align(TOFRONT,TODOWN) 132 | ,chamfer(-1,-1),height(12)) 133 | { 134 | box(y=16,x=18); 135 | hull() 136 | pieces(2) 137 | g(X(-3),Y(-6-vals(0,12))) 138 | tube(d=vals(12,12)); 139 | } 140 | //this removes from "mountEnds". if part name 141 | //is specified as first parameter of add() or remove() 142 | // it overrides the default set 143 | //by any appyTo("whatever") 144 | remove("mountEnds",Z(-16),X(-3),turnYZ()) 145 | tube(d=5,h=500); 146 | two() 147 | g(reflectX(sides()),X(37)) 148 | { 149 | g(Z(4),turnYZ()) 150 | { 151 | hull()two() 152 | add(vals(XYZ(),XYZ(-11,0,-4))) 153 | tube(dOuter=18-every(7),h=25); 154 | remove(add="screws",Z(-13)) 155 | screwM4(h=25,nutMargin=10); 156 | } 157 | 158 | two() 159 | remove(align(vals(TOFRONT,TOREAR),TOUP) 160 | ,X(10),Z(8.5) 161 | ,turnXZ(18),turnXY(-22) 162 | //every(x) will return 0,x,2*x,3*x,...,n*x 163 | // for every element in pieces(n) 164 | ,turnYZ(40+every(100)) 165 | ,chamfer(-.3,-.3)) 166 | box(100,h=110,y=1.2); 167 | } 168 | } 169 | 170 | module animationMessage() 171 | g(Z(20),X(20),Y(-40),turnYZ(45)) 172 | { 173 | opaq(black)text(partNameTextSelector(),size=7); 174 | if($t==0)opaq(red) 175 | { 176 | Y(-10)text("Enable Animations in Openscad now ",size=7); 177 | Y(-20)text("set FPS to 0.2 and Steps to 8",size=6); 178 | Y(-28)text("(press Alt-V and select [Animate] first)",size=4); 179 | } 180 | else Y(-10)text("Thanks! updating every few seconds",size=6); 181 | } 182 | -------------------------------------------------------------------------------- /examples/pulley-demo.scad: -------------------------------------------------------------------------------- 1 | //constructive library by PPROJ (version from 05.06.2021) 2 | //under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 3 | 4 | //Demo of some possibilities of the 5 | //"Constructive" Library by PPROJ (version from 05.06.2021) 6 | include 7 | 8 | $fn=12+12; 9 | fastRender=true; 10 | 11 | 12 | //the autocolor()function used below is passed this custom olor table 13 | partColors= [ 14 | ["screws",grey,0.4] 15 | ,["bearing",grey,.6] 16 | ,["wheel",orange,.3] 17 | ,["tube",red,.4] 18 | ,["screwMounts",yellow,.8] 19 | ,["hub",brown,.8] 20 | ,["walls",yellow,.8] 21 | 22 | ]; 23 | 24 | 25 | function partNameTextSelector(t=$t) = 26 | let(partSets=[ 27 | "screws,bearing,tube,wheel" ,"screws" 28 | ,"bearing,wheel,tube","bearing,tube" 29 | ]) partSets[round(t*len(partSets))]; 30 | 31 | 32 | animationMessage(); 33 | 34 | $derivedParts=["screwMounts,walls"]; 35 | 36 | assemble(partNameTextSelector()) 37 | { 38 | pulley(); 39 | remove(ALL,align(TOFRONT,TOLEFT),turnXY(45)) box(70); 40 | } 41 | 42 | render() 43 | assemble("hub,walls") 44 | { 45 | pulley(); 46 | remove(ALL,align(TOFRONT,TOLEFT),turnXY(45+20)) box(70); 47 | } 48 | 49 | module pulley(capOrNut=-1) autoColor(custom=partColors) 50 | { 51 | pieces(bodyIs("wheel",2,$removing?1:2)) 52 | g(applyTo("wheel") 53 | ,Z(.5+8),reflectZ(sides(-1)) 54 | ,Z(.5-10) 55 | ,height(7+2) 56 | ,solid() 57 | ,toUp()) 58 | { 59 | pieces(fastRender?0:6) 60 | { 61 | remove(Z(-3),turnXY(span()),X(15)) 62 | tubeFast(d=10,$fn=21); 63 | 64 | remove(Z(2),turnXY(span()+360/6/2),X(25)) 65 | difference() 66 | { 67 | tubeFast(d=14,h=100,$fn=21); 68 | g(X(6.5),turnXY(45)) 69 | tubeFast(d=24,h=20,$fn=4); 70 | } 71 | } 72 | remove("wheel") 73 | tubeFast(d=24,h=100); 74 | 75 | remove(add="bearing",Z(-.01),solid($removing) 76 | ,partIs("bearing",chamfer(-.5,-.5) 77 | ,chamfer(1,-.5))) 78 | tube(dOuter=28+margin(),dInner=15 79 | ,h=margin(7,.4)); 80 | 81 | remove(add="tube",solid(false) 82 | ,Z(2+bodyIs("tube",.3,0))) 83 | invertFor("wheel") 84 | tubeFast(dInner=46-bodyIs("tube",0,.6) 85 | ,dOuter=50+bodyIs("tube",0,.6) 86 | ,h=7*2); 87 | 88 | add(remove="enclosureBase,walls,screwMounts" 89 | ,solid()) 90 | { 91 | dMargin=1; 92 | 93 | two() 94 | g(vals(chamfer(-.5,-.5),UNITY) 95 | ,Z(every(6)-margin(0,2)/2)) 96 | if(vals(true,!bodyIs("wheel"))) 97 | tube(d=margin(67,3.6),h=margin(2,2+every(20))); 98 | 99 | g($removing?chamfer(7-.1,-.5):chamfer(10,-.5)) 100 | tube(d=margin(46.8,8+dMargin*2)); 101 | 102 | removeOnly() 103 | g(chamfer(.01,2),Z(2.5+.01)) 104 | tube(d=margin(46.8,10+dMargin*2+3.5),h=3); 105 | } 106 | } 107 | 108 | g(Z(-4.5-margin()/2) 109 | ,height(margin(7+2+4-.2)),TOUP(),solid()) 110 | { 111 | applyTo("hub",Z(-.001)) 112 | { 113 | //add()tube(dOuter=pad(15)); 114 | add()tube(d=20,h=3.4); 115 | add()tube(d=24,h=3); 116 | add()tube(d=40,h=2.5); 117 | 118 | } 119 | remove("hub,walls,enclosureBase" 120 | ,chamfer(.5,.5)) 121 | tube(d=margin(15,.3)); 122 | 123 | //*tube(dOuter=73,h=1); 124 | add("enclosureBase,walls" 125 | ,Z(-.002),solid(false) 126 | ,bodyIs("walls",UNITY,chamfer(.5,-3))) 127 | difference() 128 | { 129 | tube(dOuter=74+4,wall=bodyIs("walls",6,7) 130 | ,h=bodyIs("walls",heightInfo(),8.5-.001)); 131 | g(align(bodyIs("enclosureBase",TODOWN,TOLEFT))) 132 | box(100); 133 | } 134 | // add(solid(false)) 135 | // tube(dOuter=74,wall=2); 136 | 137 | applyTo("screwMounts") 138 | screwMounts(); 139 | 140 | applyTo("walls") 141 | { 142 | two()add(turnXY(every(90))) 143 | box(x=80,y=18,h=3.5); 144 | 145 | add(chamfer(-3,-.03),X(4) 146 | ,scale(1.4,1,1),solid(false)) 147 | { 148 | tube(dOuter=92,wall=4); 149 | g(chamfer(-.5,-.03),solid(true)) 150 | tube(dOuter=92-4,h=1); 151 | } 152 | add(chamfer(-1,-.03),X(54),solid(false)) 153 | tube(dOuter=38,wall=3); 154 | addRemove(chamfer(-3,-.03),X(70),solid(false)) 155 | tube(dOuter=18,wall=4); 156 | two() 157 | remove() 158 | difference() 159 | { 160 | g(align(TOLEFT,ZCENTER)) 161 | box(100,h=100); 162 | tubeFast(d=84,h=9+.001); 163 | } 164 | } 165 | pieces(4) 166 | add("screws" 167 | ,remove="walls,enclosureBase,screwMounts" 168 | ,turnXY(every(90)) 169 | ,solid(),X(39),Z(2),turnXY(30) 170 | ,Z(10),reflectZ(-capOrNut),Z(-11.5)) 171 | screwM4(h=margin(20),capMargin=3,nutMargin=1); 172 | } 173 | } 174 | module screwMounts() 175 | { 176 | add()pieces(4) 177 | g(solid(),turnXY(every(90)),X(35+4) 178 | ,chamfer(-1,-.01) 179 | ,scale(.5,.8,1)) 180 | tube(dOuter=22+5); 181 | } 182 | module animationMessage() 183 | g(X(-10),Y(-40),turnYZ(45)) 184 | { 185 | opaq(black)text(partNameTextSelector(),size=7); 186 | if($t==0)opaq(red) 187 | { 188 | Y(-10)text("Enable Animations in Openscad now ",size=7); 189 | Y(-20)text("set FPS to 0.2 and Steps to 4",size=6); 190 | Y(-28)text("(press Alt-V and select [Animate] first)",size=4); 191 | } 192 | else Y(-10)text("Thanks! updating every few seconds",size=6); 193 | } 194 | -------------------------------------------------------------------------------- /examples/skin.scad: -------------------------------------------------------------------------------- 1 | include 2 | 3 | $skinThick=1.5; //thickness of the Skin in mm 4 | $margin=0; 5 | 6 | 7 | assemble() 8 | { 9 | //hull() 10 | 11 | addRemove(height(skin(20)), TOUP(),alignSkin(TOUP)) 12 | { 13 | 14 | g(X(-10),chamfer(-4,-4-1)) 15 | tube( d = skin(30),solid=true); 16 | 17 | g(X(4),chamfer(-1,-1,-5)) 18 | box(skin(30), skin(18), h=skin(10)); 19 | } 20 | remove(TOLEFT(TOFRONT)) box(30,h=100); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /gallery/cart14-Plugs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/gallery/cart14-Plugs.png -------------------------------------------------------------------------------- /gallery/cart14-tensioner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/gallery/cart14-tensioner.png -------------------------------------------------------------------------------- /gallery/sources/cart14-Plugs.scad: -------------------------------------------------------------------------------- 1 | include <../../constructive-compiled.scad> 2 | 3 | $margin=.5; 4 | 5 | 6 | clear(khaki) 7 | assemble("axlePlug","screws") 8 | plugs(); 9 | *assemble("lineEnd","screws") 10 | plugs(); 11 | 12 | module plugs(h=15.1,$marginNut=1.3)autoColor() 13 | { 14 | 15 | applyTo("axlePlug",turnXZ(-90),Z(-56+5),TOUP(),solid()) 16 | { 17 | g(Z(-6),turnXZ(-90),Z(7),$fn=21) 18 | applyTo("axlePlug,lineEnd",solid(),$fn=41,TOUP() 19 | ,Z(-14/2),X(),turnXZ(-90)) 20 | { 21 | 22 | g(Z(10+.1),reflectZ(),Z(7),chamfer(-.5,0),TOUP()) 23 | { 24 | add(confines()) 25 | tube(d=22+isConfining($margin*1.5),h=h); 26 | remove(confines()) 27 | pieces(4) 28 | g(turnXY(spanAllButLast()+45) 29 | ,Y(-10+1-isConfining($margin*1.5)),ZCENTER(),turnYZ(90+40) 30 | ,chamfer(-.5,5))box(x=4,y=26,h=5); 31 | two() 32 | remove(Z(6.6+every(10)),turnXZ(),ZCENTER(TORIGHT),chamfer(-1,-1)) 33 | box(5,x=5,h=30); 34 | 35 | remove(add="screws",Z(7.5),reflectZ()) 36 | { 37 | Z(5-3) 38 | inbusScrewM3(h=10,capMargin=20,$margin=.9); 39 | pressNutM3(marginDown=20,$margin=$marginNut); 40 | } 41 | } 42 | } 43 | 44 | add(Z(5),chamfer(0,-3)) 45 | tube(dOuter=29.5,h=20); 46 | add(Z(25+2-3)) 47 | rotate_extrude()g(X(29/2-3),cscale(1,2,1)) 48 | circle(d=3*2,$fn=30); 49 | 50 | two() 51 | g(reflectX(sides())) 52 | { 53 | remove(Z(15),Z(4),turnXZ(90),Z(13) 54 | ,cscale(1,0.5,1),turnYZ(45) 55 | ,ZCENTER(TORIGHT)) 56 | box(20,x=30); 57 | remove(X(10),TORIGHT(ZCENTER))box(80,x=7); 58 | } 59 | remove() 60 | assemble() 61 | add(Z(.5-13+25)) 62 | tube(d=2+22/43*25,d2=24,h=43 -25); 63 | } 64 | 65 | 66 | } -------------------------------------------------------------------------------- /gallery/sources/cart14-tensioner.scad: -------------------------------------------------------------------------------- 1 | include <../../constructive-compiled.scad> 2 | include 3 | 4 | //this is a placement where need to place the tesioner to putit on a plug tube tensioned 5 | placeAtTheAxlePlug=X(-77)*turnXZ(180)*turnYZ(45); 6 | 7 | 8 | //Assemble all Parts together 9 | 10 | assemble("tensionerCore,tensionerArms,caret","screws") 11 | tensioner(); 12 | 13 | //----------- 14 | //assemble every single part separately ready for Printing, aligning every Part on its own lying on the Print bed 15 | opaq(yellow) 16 | { 17 | assemble("tensionerCore") 18 | g(Y(-20),Z(-3),turnXZ(-90),stepBack(placeAtTheAxlePlug)) 19 | tensioner(); 20 | assemble("tensionerArms") 21 | g(Y(35),turnXZ(-90),stepBack(placeAtTheAxlePlug)) 22 | tensioner(); 23 | two() 24 | assemble("caret") 25 | g(Y(65),Z(31),reflectX(sides()),X(20),turnXZ() 26 | ,turnXZ(-90),stepBack(placeAtTheAxlePlug)) 27 | tensioner(carets=1); 28 | } 29 | //----------------------------------------------- 30 | //--this code below creates all Parts already aligned together 31 | module tensioner($fn=45,carets=2) 32 | autoColor() 33 | g(placeAtTheAxlePlug) 34 | { 35 | //remove the Plug from from the 36 | //TensionerCore leaving the surface fitting the Plug 37 | remove("tensionerCore",stepBack(placeAtTheAxlePlug)) 38 | confinementOf() 39 | plugs(); 40 | //create the removable Core for the Tensioner arms (handles) making 41 | //space for it in the Socket belonging to Tensioner Arms (created below) 42 | applyTo("tensionerCore",X(-5-16),turnYZ(45)) 43 | { 44 | g(turnXZ(),Z(2),TODOWN(),solid(),height(margin(25))) 45 | { 46 | add(remove="tensionerArms") 47 | tube(d=margin(29)); 48 | remove() 49 | tube(d=17); 50 | } 51 | two() 52 | remove(X(-3),turnYZ(span(90)+45),cscale(1.5,1,1),turnXY(45)) 53 | box(5,h=30); 54 | } 55 | //create a socket for the removable core 56 | add("tensionerArms",turnXZ()) 57 | tube(dOuter=36,dInner=22,h=10); 58 | 59 | pieces(carets) 60 | g(reflectZ(sides()),Z(15),TOUP(),solid()) 61 | { 62 | //create the tensioner Arms 63 | two() 64 | add("tensionerArms",remove="caret",reflectY(sides()),Y(5),chamfer(-1,-1)) 65 | box(margin(10,1),y=margin(5,1),h=60); 66 | //create carets as many as "carets" argument inside pieces() above 67 | add("caret",Z(10+4),X(10),turnXZ(90),chamfer(-1,-1)) 68 | box(21,14); 69 | //create the scres and Nuts making space for them in carets 70 | applyTo("screws") 71 | { 72 | add(remove="caret")tubeFast(d=margin(3),h=80); 73 | add(remove="caret",Z(9),X(5),turnXZ(90))tubeFast(d=margin(5),h=margin(10)); 74 | 75 | add(remove="caret",Z(-30),turnXZ(90),X(42),Z(2)) 76 | { 77 | g(chamfer(-1,-1)) 78 | tubeFast(d=margin(3),h=margin(8)); 79 | g(Z(3.5),turnXY(45),chamfer(-1,-1)) 80 | pressNutM3(marginDown=margin()); 81 | Z(3) 82 | inbusScrewM3(h=10,capMargin=20,$margin=.9); 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /img/mount.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/img/mount.gif -------------------------------------------------------------------------------- /img/pulley.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/img/pulley.gif -------------------------------------------------------------------------------- /kickstart.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/kickstart.zip -------------------------------------------------------------------------------- /sources/assemble.scad: -------------------------------------------------------------------------------- 1 | //ASSEMBLE.SCAD 2 | //This is a part of: 3 | //CONSTRUCTIVE LIBRARY by PPROJ (version from 05.06.2021) 4 | //released under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 5 | 6 | //you only need a single file: constructive-compiled.scad which 7 | //contains all the partial files you find. you can ignore everything else.. 8 | //just include it in your code by: 9 | //include 10 | 11 | //if you wish to improve the library or make changes to it, 12 | // it might be handier to use: 13 | //include instead. so you do not have to recreate constructive-compiled.scad from the parts 14 | //every time you make a change to a part of the library 15 | 16 | ALL="ALL"; 17 | 18 | //allows to use confinementOf() which assembles a confinement from Parts which are marked whith confines() marker 19 | //function to mark which operations constitute aconfinement, 20 | //like in add(confines("part1"))box(): 21 | //or in in remove(confines("part2"))tube(d=2,h=10); 22 | //then you can use 23 | //intersection() 24 | // { 25 | // confinement()moduleWithParts(); 26 | // assemble()moduleWithParts(); 27 | // } 28 | //to confine the PArts inside the confinement 29 | function confinementPartName() = "confinement"; 30 | function isConfining(truePart=0,falsePart=0) = partIs(confinementPartName())?truePart:falsePart; 31 | function confines(otherparts=currentPart()) = str(otherparts,",",confinementPartName() ); 32 | module confinementOf() assemble(confinementPartName())children(); 33 | 34 | //--------- 35 | 36 | function isAddingFirst()= ($summingUp 37 | && !$removing ); 38 | function expandParentsStripArgs(child,derivedParts=$derivedParts)= 39 | join([for(p = split( 40 | expandParents(child,derivedParts),",")) 41 | stripPartArgs(p)],","); 42 | 43 | function expandParents(child,derivedParts=$derivedParts)= 44 | let(parents = 45 | [ for(found = getGenialogyIndexForPart(child,derivedParts)) 46 | each 47 | let(foundInFamily=[ for(i = [1:1:len(found)-1])found[i]]) 48 | let(foundAt=found[0]) 49 | [for(a=[len(foundInFamily)-1:-1:0]) 50 | if(foundAt && foundAt>a && 51 | (!enclosesOneOf(child,foundInFamily[a])))foundInFamily[a] 52 | ]])join(concat(reverse(parents),[child]),","); 53 | 54 | function stripPartArgs(partNameWithArgs)= 55 | split(partNameWithArgs,"(")[0]; 56 | function stripPartName(partNameWithArgs)= 57 | let(rightPart=split(partNameWithArgs,"(")[1]) 58 | (rightPart==undef?undef:split(rightPart,")")[0]); 59 | 60 | function addArgVals(parts,valArray1,valArray2,valArray3,valArray4,valArray5 61 | ,valArray6,valArray7,valArray8,valArray9,valArray10)= 62 | let(argValsArrays=collect(valArray1,valArray2,valArray3,valArray4,valArray5 63 | ,valArray6,valArray7,valArray8,valArray9,valArray10) 64 | ,partsAsArray= split(parts,",") 65 | ,partsCount= len(partsAsArray)) 66 | join([for(pIndex=[0:1:partsCount-1]) 67 | let(partName= stripPartArgs(partsAsArray[pIndex]) 68 | ,argNames=split(stripPartName(partsAsArray[pIndex]),",") 69 | ,nArgMax= -1 + (argNames==undef?0:len(argNames))) 70 | str(partName,"(" 71 | ,join([for(i=[0:1:nArgMax]) 72 | str(argNames[i],"=",argValsArrays[pIndex][i])] 73 | ,",") 74 | ,")") 75 | ],","); 76 | 77 | $currentPartArgs=undef; 78 | function argString(paramName,default=undef,currentArgs=$currentPartArgs)= 79 | let(matched=[for(args = split(currentArgs,",")) 80 | let(argParts= split(args,"=")) 81 | if(argParts[0] == paramName) (argParts[1])]) 82 | len(matched)>0?matched[0]:default; 83 | function argInt(paramName,default=undef,currentArgs=$currentPartArgs)= 84 | let(matched=[for(args = split(currentArgs,",")) 85 | let(argParts= split(args,"=")) 86 | if(argParts[0] == paramName) toInt(argParts[1])]) 87 | len(matched)>0?matched[0]:default; 88 | 89 | function argIntList(paramName,default=undef,currentArgs=$currentPartArgs)= 90 | let(matched=[for(args = split(currentArgs,",")) 91 | let(argParts= split(args,"=")) 92 | if(argParts[0] == paramName) toIntList(argParts[1])]) 93 | len(matched)>0?matched[0]:default; 94 | 95 | 96 | function getGenialogyIndexForPart(child,derivedParts=$derivedParts)= 97 | [for(f=[0:1:len(derivedParts)-1]) 98 | let(family=derivedParts[f]) 99 | let(familySplit=split(family,",")) 100 | let( foundAt=max([for(i=[len(familySplit)-1:-1:1]) 101 | enclosesOneOf(child,split(familySplit[i],"(")[0])?i:-1])) 102 | concat(foundAt,familySplit)]; 103 | 104 | function currentPartIn(bodySet,standardBody 105 | ,concatStadardPrefix="+",exactNamePrefix=":",excludeItPrefix="!" 106 | ,currentBody=$currentBody) = ( bodySet==ALL 107 | || ( ($currentBody != undef) 108 | && (bodySet != undef) 109 | && (let(expandedParentsCurrentBody = expandParentsStripArgs(currentBody) 110 | , bodySetReplacedPlusSign = is_undef(standardBody) 111 | ?bodySet 112 | :join(split(bodySet,concatStadardPrefix) 113 | ,str(standardBody,",")) 114 | , bodysInSet= split(bodySetReplacedPlusSign,",") 115 | , positiveBodySet= [for(b=bodysInSet) 116 | let(sp=split(b,excludeItPrefix)) 117 | if(len(sp)==1)b] 118 | , negativeBodySet= [for(b=bodysInSet) 119 | let(sp=split(b,excludeItPrefix)) 120 | if(len(sp)>1) 121 | let(syntaxError= assert(len(sp)<3 122 | ,str("Only one exclusionPrefix ''" 123 | ,excludeItPrefix 124 | ,"' allowed per body name." 125 | ," seprate prefixed excluded " 126 | ,"bodynames by commas ','" 127 | ))) 128 | sp[1]] 129 | // ,debu= echo(negativeBodySet) 130 | )(!encloses(negativeBodySet,currentBody) && 131 | len([for(body=positiveBodySet) 132 | let(exactlyThis = reverse(split(body,exactNamePrefix)) 133 | ,curBody = (len(exactlyThis)>1 134 | ?currentBody 135 | :expandedParentsCurrentBody)) 136 | if(encloses(curBody, exactlyThis[0]))true 137 | ])>0)))); 138 | // ? (true) : (false); 139 | function currentPartExactIn(bodySet) = ( bodySet==ALL 140 | || ( ($currentBody != undef) 141 | && (bodySet != undef) 142 | && enclosesOneOf($currentBody,bodySet) )) ? (true) : (false); 143 | 144 | function currentBodyIn(bodySet) = currentPartIn(bodySet); 145 | function ifBodyIs(bodySet, ifTrue, ifFalse=0) = currentPartIn(bodySet) 146 | ? ifTrue : ifFalse; 147 | function bodyIs(bodySet, ifTrue=true, ifFalse=false) = currentPartIn(bodySet) 148 | ? ifTrue : ifFalse; 149 | function partIs(bodySet, ifTrue=true, ifFalse=false) = currentPartIn(bodySet) 150 | ? ifTrue : ifFalse; 151 | function partIsExact(bodySet, ifTrue=true, ifFalse=false) = currentPartExactIn(bodySet) 152 | ? ifTrue : ifFalse; 153 | 154 | 155 | 156 | module assemble( 157 | shells=currentPart(),details="",bodys2="",bodys3="",bodys4="",bodys5="",bodys6="",bodys7="",bodys8="",bodys9="" 158 | , $summingUp=true,$removing=false,$beforeRemoving=true,$derivedParts=[]) 159 | { 160 | 161 | bodyListCommaSeparated=str( ",",bodys9 162 | ,",",bodys8 163 | ,",",bodys7 164 | ,",",bodys6 165 | 166 | ,",",bodys5 167 | ,",",bodys4 168 | 169 | ,",",bodys3 170 | ,",",bodys2 171 | ,",",details 172 | ,",",shells 173 | ); 174 | 175 | echo("Assembling: ",splitBodies(expandParents(bodyListCommaSeparated))); 176 | $shellPartsForAutoColor=shells; 177 | $detailPartsForAutoColor=details; 178 | for(currentPartWithArgs =splitBodies(expandParents(bodyListCommaSeparated))) 179 | { 180 | $currentBody= stripPartArgs(currentPartWithArgs); 181 | $currentPartArgs=stripPartName(currentPartWithArgs); 182 | 183 | difference() 184 | { 185 | children(); 186 | union() 187 | { 188 | $removing = true; 189 | $beforeRemoving=false; 190 | children(); 191 | } 192 | } 193 | } 194 | } 195 | 196 | module addHullStep(onlyFor=currentPart(),addOnly=true,removeOnly=false) 197 | { 198 | if((removeOnly ||!(addOnly && $removing)) && 199 | !(removeOnly && $removing) && 200 | partIs(onlyFor)) hull() 201 | { 202 | $hulling=true; 203 | children(); 204 | } 205 | children(); 206 | } 207 | 208 | module noHull(bodySet=currentPart()) 209 | { 210 | if(!$hulling 211 | || ($hulling && (bodySet!="" && !currentPartIn(bodySet,currentPart()))))children(); 212 | } 213 | 214 | module hullIf(case=false) 215 | { 216 | if(case)hull()children(); 217 | else children(); 218 | } 219 | 220 | module addOnly() 221 | if(!$removing) children(); 222 | 223 | module removeOnly() 224 | if($removing)children(); 225 | 226 | 227 | module encloseOnly() 228 | if(!$enlosing) children(); 229 | 230 | 231 | 232 | module addRemove(only,step1=UNITY,step2=UNITY,step3=UNITY,step4=UNITY,step5=UNITY 233 | ,step6=UNITY,step7=UNITY,step8=UNITY,step9=UNITY,step10=UNITY 234 | ,step11=UNITY,step12=UNITY,step13=UNITY,step14=UNITY,step15=UNITY 235 | ,step16=UNITY,step17=UNITY,step18=UNITY,step19=UNITY,step20=UNITY 236 | ,name="",geom,add,remove) 237 | { 238 | ////////////// 239 | p=[(isPlaceOrGeom(only)?only:UNITY),step1,step2,step3,step4,step5 240 | ,step6,step7,step8,step9,step10 241 | ,step11,step12,step13,step14,step15 242 | ,step16,step17,step18,step19,step20]; 243 | 244 | onlyPart = ( only == undef || isPlaceOrGeom(only)) 245 | ?currentPart($removing?2:1):only; 246 | 247 | place=placesOnly(p); 248 | 249 | 250 | $geomInfo= geomsOnly(p,geom); 251 | 252 | $placement=multAll(place); 253 | $placementStack=concat($placementStack, [[name,$placement]]); 254 | $placementStackTop=$placementStackTop+1; 255 | 256 | if( $currentBody == undef || onlyPart==ALL 257 | || (onlyPart == undef && add == undef && remove== undef) 258 | || currentPartIn (onlyPart) 259 | ) 260 | multmatrix($placement) 261 | { 262 | $placement=UNITY; 263 | children(); 264 | } 265 | 266 | if( currentPartIn (add,currentPartAdd())) 267 | addOnly() 268 | multmatrix($placement) 269 | { 270 | $placement=UNITY; 271 | children(); 272 | } 273 | if( currentPartIn(remove,currentPartRemove())) 274 | removeOnly() 275 | multmatrix($placement) 276 | { 277 | $placement=UNITY; 278 | children(); 279 | } 280 | } 281 | 282 | module invert($removing=$removing?false:true,$beforeRemoving=$beforeRemoving?false:true) 283 | { 284 | children(); 285 | } 286 | 287 | module invertFor(body) 288 | { 289 | $removing=bodyIs(body)?($removing?false:true):$removing; 290 | $beforeRemoving=bodyIs(body)?($beforeRemoving?false:true):$beforeRemoving; 291 | children(); 292 | } 293 | 294 | module add(to,step1=UNITY,step2=UNITY,step3=UNITY,step4=UNITY,step5=UNITY 295 | ,step6=UNITY,step7=UNITY,step8=UNITY,step9=UNITY,step10=UNITY 296 | ,step11=UNITY,step12=UNITY,step13=UNITY,step14=UNITY,step15=UNITY 297 | ,step16=UNITY,step17=UNITY,step18=UNITY,step19=UNITY,step20=UNITY 298 | ,name="",geom,remove) 299 | { 300 | 301 | 302 | ////////////// 303 | p=[(isPlaceOrGeom(to)?to:UNITY),step1,step2,step3,step4,step5 304 | ,step6,step7,step8,step9,step10 305 | ,step11,step12,step13,step14,step15 306 | ,step16,step17,step18,step19,step20]; 307 | 308 | toPart = ( to == undef || isPlaceOrGeom(to))?currentPart():to; 309 | place=placesOnly(p); 310 | 311 | $geomInfo= geomsOnly(p,geom); 312 | what=toPart; 313 | $placement=multAll(place); 314 | $placementStack=concat($placementStack, [[name,$placement]]); 315 | $placementStackTop=$placementStackTop+1; 316 | 317 | //echo("add.geomInfo:",$geomInfo); 318 | if( currentPartIn (toPart,currentPartAdd())) 319 | addOnly() multmatrix($placement) 320 | { 321 | $placement=UNITY; 322 | children(); 323 | } 324 | if(currentPartIn(remove,currentPartRemove())) 325 | removeOnly() multmatrix($placement) 326 | { 327 | 328 | $placement=UNITY; 329 | children(); 330 | } 331 | } 332 | 333 | module remove(from,step1=UNITY,step2=UNITY,step3=UNITY,step4=UNITY,step5=UNITY 334 | ,step6=UNITY,step7=UNITY,step8=UNITY,step9=UNITY,step10=UNITY 335 | ,step11=UNITY,step12=UNITY,step13=UNITY,step14=UNITY,step15=UNITY 336 | ,step16=UNITY,step17=UNITY,step18=UNITY,step19=UNITY,step20=UNITY 337 | ,name="",geom,add) 338 | { 339 | 340 | ////////////// 341 | p=[(isPlaceOrGeom(from)?from:UNITY),step1,step2,step3,step4,step5 342 | ,step6,step7,step8,step9,step10 343 | ,step11,step12,step13,step14,step15 344 | ,step16,step17,step18,step19,step20]; 345 | 346 | fromPart = ( from == undef || isPlaceOrGeom(from))?currentPart():from; 347 | 348 | place=placesOnly(p); 349 | 350 | 351 | $geomInfo= geomsOnly(p,geom); 352 | 353 | $placement=multAll(place); 354 | $placementStack=concat($placementStack, [[name,$placement]]); 355 | $placementStackTop=$placementStackTop+1; 356 | 357 | if( currentPartIn (add,currentPartAdd())) 358 | addOnly() multmatrix($placement) 359 | { 360 | $placement=UNITY; 361 | children(); 362 | } 363 | 364 | if( currentPartIn(fromPart,currentPartRemove())) 365 | removeOnly() multmatrix($placement) 366 | { 367 | $placement=UNITY; 368 | children(); 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /sources/basicfuncs.scad: -------------------------------------------------------------------------------- 1 | //BASICFUNCS.SCAD 2 | //This is a part of: 3 | //CONSTRUCTIVE LIBRARY by PPROJ (version from 05.06.2021) 4 | //released under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 5 | 6 | //you only need a single file: constructive-compiled.scad which 7 | //contains all the partial files you find. you can ignore everything else.. 8 | //just include it in your code by: 9 | //include 10 | 11 | //if you wish to improve the library or make changes to it, 12 | // it might be handier to use: 13 | //include instead. so you do not have to recreate constructive-compiled.scad from the parts 14 | //every time you make a change to a part of the library 15 | 16 | function toInt(str) = 17 | str=="-"?undef 18 | :str[0]=="-"? -1*toInt([for(i=[1:1:len(str)-1])str[i]]) 19 | :let(d = [for (s = str) ord(s) - 48], l = len(d)) 20 | [for (i = 0, a = d[i]; i < l; i = i + 1 ,a = i 0 36 | && blockOpeners[0] == "[" && chr == "]") 37 | || (len(blockOpeners) > 0 38 | && blockOpeners[0] == "(" && chr == ")") ? [for(i=[1:1:len(blockOpeners)-1])blockOpeners[i]] 39 | : chr=="(" || chr=="[" ? concat([chr],blockOpeners) 40 | : blockOpeners; 41 | 42 | //------ 43 | function split(str, sep=" ", i=0, word="", v=[], blockOpeners=[]) = 44 | str ==undef ? undef 45 | : i == len(str) ? concat(v, word) 46 | : (len(blockOpeners)>0 || (str[i] != sep)) ? split(str, sep, i+1, str(word, str[i]), v 47 | , checkInside(blockOpeners,str[i])) 48 | : split(str, sep, i+1, "", concat(v, word),checkInside(blockOpeners,str[i])) ; 49 | 50 | function splitSimple(str, sep=" ", i=0, word="", v=[]) = 51 | str ==undef ? undef : 52 | i == len(str) ? concat(v, word) : 53 | str[i] == sep ? split(str, sep, i+1, "", concat(v, word)) : 54 | split(str, sep, i+1, str(word, str[i]), v); 55 | 56 | 57 | 58 | // some general utility functions 59 | function debugprint(s) = search(s, []) || true; 60 | 61 | function multV(what,byWhat=[1]) = [for (i = [ 0 : min(len(what),len(byWhat)) - 1 ]) what[i]*byWhat[i]]; 62 | function absV(vector) = [for (i = [ 0 : len(vector) - 1 ]) abs(vector[i])]; 63 | function addToV(vector,scalar=0) = [for (i = [ 0 : len(vector) - 1 ]) vector[i]+scalar]; 64 | function zeroIfUndef(what) = (what==undef)?0:what; 65 | function falseIfUndef(what) = (what==undef)?false:what; 66 | 67 | function zeroIfNot(boolVal)= (boolVal)?1:0; 68 | function zeroIf(boolVal)= (boolVal)?0:1; 69 | 70 | 71 | //returns the maximum elemnt of the i's component of a vector of vectors 72 | // for example of a[1][3], a[2][3], a[3][3], a[4][3] 73 | // the i parametere is only needed internally and can be ommitedin calls 74 | // like: maximum(a,3); 75 | function maximum(a,componentIndex, i = 0) = (i < len(a) - 1) 76 | ? max(a[i][componentIndex], maximum(a,componentIndex, i +1)) : a[i][componentIndex]; 77 | //just like mximum above but the minimum 78 | function minimum(a,componentIndex, i = 0) = (i < len(a) - 1) 79 | ? min(a[i][componentIndex], minimum(a,componentIndex, i +1)) : a[i][componentIndex]; 80 | 81 | //adds an element to a vector 82 | function push(vector,element)=concat(vector,[element]); 83 | //gets last element from a vetor 84 | function top(vector)=vector[len(vector)-1]; 85 | 86 | function ifAnyOf(where,what,sep=":")= (len([for(el=split(where,sep)) 87 | if(str(what)==el)1])!=0)?true:false; 88 | function anyPairMatches(setA,setB)= 89 | len([for(a=setA,b=setB)if(str(a)==str(b))1])!=0; 90 | 91 | function encloses(where,what) = 92 | (len([for(el=split(where,",")) 93 | if(str(what)==el)1])!=0)?true:false; 94 | function enclosesOneOf(where,commaSeparatedListOfWhat) = 95 | (len([for(el=split(commaSeparatedListOfWhat,",")) 96 | if(encloses(where,el))1])!=0)?true:false; 97 | 98 | 99 | //--------------------------------------------------- 100 | // List helpers 101 | 102 | /*! 103 | Creates a list from a range: 104 | range([0:2:6]) => [0,2,4,6] 105 | */ 106 | function range(r) = [ for(x=r) x ]; 107 | 108 | /*! 109 | Reverses a list: 110 | reverse([1,2,3]) => [3,2,1] 111 | */ 112 | function reverse(list) = [for (i = [len(list)-1:-1:0]) list[i]]; 113 | 114 | /*! 115 | Extracts a subarray from index begin (inclusive) to end (exclusive) 116 | FIXME: Change name to use list instead of array? 117 | subarray([1,2,3,4], 1, 2) => [2,3] 118 | */ 119 | function subarray(list,begin=0,end=-1) = [ 120 | let(end = end < 0 ? len(list) : end) 121 | for (i = [begin : 1 : end-1]) 122 | list[i] 123 | ]; 124 | 125 | /*! 126 | Returns a copy of a list with the element at index i set to x 127 | set([1,2,3,4], 2, 5) => [1,2,5,4] 128 | */ 129 | function set(list, i, x) = [for (i_=[0:len(list)-1]) i == i_ ? x : list[i_]]; 130 | 131 | /*! 132 | Remove element from the list by index. 133 | remove([4,3,2,1],1) => [4,2,1] 134 | */ 135 | function remove(list, i) = [for (i_=[0:1:len(list)-2]) list[i_ < i ? i_ : i_ + 1]]; 136 | 137 | 138 | /*! 139 | Flattens a list one level: 140 | flatten([[0,1],[2,3]]) => [0,1,2,3] 141 | */ 142 | function flatten(list) = [ for (i = list, v = i) v ]; 143 | 144 | //function flatten(vec,max=inf) = [let(i=min(len(vec)-1,max)) concat(flatten(vec,i-1),vec[i])]; 145 | function splitBodies(bodyListCommaSeparated)= flatten(concat( 146 | [for( body=split(bodyListCommaSeparated,",")) 147 | if(len(body)>0) [for(i=[0:1: len(split(body,"."))-1]) 148 | let(substep = split(body,".")[i])(i==0 //&& len(split(body,"."))>2 149 | ?substep: 150 | str(split(body,".")[0],".",substep))]])) ; 151 | //str(split(body,".")[0],".",substep)] 152 | 153 | function mapByStringKey(stringKey,objArray) = [for(i=[0:1:len(objArray)]) if(objArray[i][0] == stringKey)objArray[i]]; 154 | //function find(stringKey,arrayVect)occurencesAt(stringKey,arrayVect) 155 | 156 | function vals(val1,val2,val3,val4,val5 157 | ,val6,val7,val8,val9,val10) 158 | = ( val2==undef) 159 | ?val1[$valPtr] 160 | :concat([val1],[val2] 161 | ,[val3] 162 | ,[val4] 163 | ,[val5] 164 | ,[val6] 165 | ,[val7] 166 | ,[val8] 167 | ,[val9] 168 | ,[val10])[$valPtr]; 169 | 170 | function valsFromList(list=[undef])=list[$valPtr]; 171 | 172 | function collect(val1,val2,val3,val4,val5 173 | ,val6,val7,val8,val9,val10) = 174 | [for(a=concat([val1] 175 | ,[val2] 176 | ,[val3] 177 | ,[val4] 178 | ,[val5] 179 | ,[val6] 180 | ,[val7] 181 | ,[val8] 182 | ,[val9] 183 | ,[val10])) if(a!=undef)a]; 184 | -------------------------------------------------------------------------------- /sources/colorSystem.scad: -------------------------------------------------------------------------------- 1 | 2 | //shorthand predicates to set color or transparecy 3 | //allows for syntactic sugar to set opaq or transparent object colors 4 | //like: clear(green) box(side=15); 5 | //allows for syntactic sugar to set opaq or transparent object colors 6 | //like: clear(green) box(side=15); 7 | 8 | 9 | 10 | 11 | module autoColor(shells= $shellPartsForAutoColor 12 | ,details=$detailPartsForAutoColor,colorFor=$currentBody,custom=[]) 13 | { 14 | color =pickColorWithOpacity(shells,details,colorFor); 15 | mapped =is_undef(colorFor)?[]:mapByStringKey(colorFor,custom); 16 | custom= (len(mapped)>0)?mapped[0]:[]; 17 | isCustom = len(custom)>0; 18 | drawColor=isCustom?custom[1]:color[0]; 19 | remColor=$removing?0.3:(isCustom? custom[2]:color[1]); 20 | 21 | *echo("autoColor() Debug Data:",custom,shells,details,drawColor,remColor,colorFor); 22 | 23 | color(drawColor,remColor)children(); 24 | } 25 | module clear (col) 26 | { 27 | color($removing?undef:col,$removing?undef:0.4)children(); 28 | } 29 | module opaq (col) 30 | { 31 | color($removing?undef:col,$removing?undef:1)children(); 32 | } 33 | 34 | //global color constants 35 | //define Global Constnts (actuall variable) for each color aviliable here 36 | yellow="yellow"; grey="grey"; gray="grey"; 37 | purple="purple"; olive="olive"; orange="orange"; 38 | cyan="cyan"; blue="blue"; fuchsia="fuchsia"; 39 | 40 | pink="pink"; silver="silver"; khaki="khaki"; beige="beige"; 41 | navy="navy"; brown="brown"; red="red"; black="black"; 42 | 43 | aqua="aqua"; gold="gold"; maroon="maroon"; 44 | green="green"; lime="lime"; teal="teal"; 45 | 46 | 47 | $colorNames=[ 48 | yellow, gray 49 | ,pink , navy 50 | ,silver, brown 51 | ,khaki, red 52 | ,beige, black 53 | ,aqua , green 54 | ,gold , lime 55 | ,orange, fuchsia 56 | ,purple, cyan 57 | ,maroon, teal 58 | ,olive, blue 59 | ]; 60 | 61 | 62 | 63 | //new style color system 64 | function findInList(list=[],elem=$currentBody) = 65 | let(found=[for(i=[0:len(list)-1]) 66 | if(list[i]==elem)i])found; 67 | 68 | function pickColorWithOpacity(shells= $shellPartsForAutoColor 69 | ,details=$detailPartsForAutoColor 70 | ,part=$currentBody) = 71 | let(foundShell=findInList(split(is_undef(shells)?"":shells,","),part) 72 | ,foundDetail=findInList(split(is_undef(details)?"":details,","),part) 73 | ,ind= (len(foundShell)>0) 74 | ?[foundShell[0]*2,.4] 75 | :(len(foundDetail)>0 76 | ?([foundDetail[0]*2+1,1]) 77 | :[0,.4]) 78 | )[$colorNames[ind[0]],ind[1]]; 79 | 80 | 81 | 82 | //picks onec olor for shells (even index) 83 | function shellColor(ind)=$colorNames[ind*2]; 84 | //picks one color for details inside shell (oddindex) 85 | function detailColor(ind)=$colorNames[ind*2+1]; 86 | 87 | 88 | // for compatibility: old style coloring system- cumbersome to use 89 | 90 | // old style System end 91 | 92 | 93 | 94 | /* 95 | //example of Module to show all colors in their typical combinations 96 | module showColors() 97 | pieces(len($colorNames)/2)///2) 98 | g(X(every(30)),TOFRONT()) 99 | { 100 | opaq(detailColor($valPtr)) 101 | box(5); 102 | clear(shellColor($valPtr)) 103 | box(20); 104 | } 105 | */ 106 | 107 | -------------------------------------------------------------------------------- /sources/constructive.scad: -------------------------------------------------------------------------------- 1 | //CONSTRUCTIVE.SCAD 2 | //This is a part of: 3 | //CONSTRUCTIVE LIBRARY by PPROJ (version from 05.06.2021) 4 | //released under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 5 | 6 | //you only need a single file: constructive-compiled.scad which 7 | //contains all the partial files you find. you can ignore everything else.. 8 | //just include it in your code by: 9 | //include 10 | 11 | //if you wish to improve the library or make changes to it, 12 | // it might be handier to use: 13 | //include instead. so you do not have to recreate constructive-compiled.scad from the parts 14 | //every time you make a change to a part of the library 15 | 16 | function margin(dim=0,margin=$margin)= dim +removeExtra(margin); 17 | function pad(dim=0,padding=$padding) = dim -padding + removeExtra(padding); 18 | 19 | 20 | 21 | 22 | module runFor(conditionList=[true]) 23 | { 24 | $totalPieces=len(conditionList); 25 | valPtrs=[for(i=[0:1:(len(conditionList)-1)]) 26 | if(conditionList[i]) i]; 27 | for($valPtr=valPtrs) 28 | children(); 29 | } 30 | module pieces(n) 31 | { 32 | $totalPieces=n; 33 | for($valPtr=[0:1:n-1]) 34 | children(); 35 | } 36 | module two() 37 | { 38 | $totalPieces=2; 39 | for($valPtr=[0:1]) 40 | children(); 41 | } 42 | module skipFirst(n=1) 43 | { 44 | if($valPtr>=n) 45 | children(); 46 | } 47 | module ifFirst(n=1) 48 | { 49 | if($valPtr=totalPieces-n) 55 | children(); 56 | } 57 | module selectPieces(decisionList=[true],valPtr=$valPtr) 58 | { 59 | if(valPtr=180) 548 | intersection_for(angle=[-180+90,arc+90]) 549 | g(turnXY(angle) 550 | ,chamferInfoUpdate(invert=innerChamfer)) 551 | box(side = max(d,(d2==undef)?0:d2) 552 | ,y=max(d,(d2==undef)?0:d2) 553 | +2*2*max(zeroIfUndef(chamferInfo()[0][0]) 554 | ,zeroIfUndef(chamferInfo()[1][0])) 555 | ,z=h+.1); 556 | 557 | if(arc>0 && arc<180) 558 | for(angle=[-180+90,arc+90]) 559 | g(turnXY(angle) 560 | ,chamferInfoUpdate(invert=innerChamfer)) 561 | box(side=max(d,(d2==undef)?0:d2) 562 | ,y=max(d,(d2==undef)?0:d2) 563 | +2*2*max(zeroIfUndef(chamferInfo()[0][0]) 564 | ,zeroIfUndef(chamferInfo()[1][0]) 565 | ),z=h+.1); 566 | } 567 | if(!solid) up(stickOutBothEnds?0:holeStickOut/2) 568 | cylinder(((h+.03+abs(holeStickOut) 569 | +(stickOutBothEnds?abs(holeStickOut):0))) 570 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 571 | ,center=true); 572 | 573 | if((!solid && $inverted && $removing)) 574 | up(stickOutBothEnds?0:holeStickOut/2) 575 | cylinder(((h+.03+abs(holeStickOut) 576 | +(stickOutBothEnds?abs(holeStickOut):0))) 577 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 578 | ,center=true); 579 | } 580 | if(!solid && !$inverted && summingUp && $removing) 581 | up(stickOutBothEnds?0:holeStickOut/2) 582 | cylinder(((h+.03+abs(holeStickOut) 583 | +(stickOutBothEnds?abs(holeStickOut):0))) 584 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 585 | ,center=true); 586 | } 587 | stackingTranslation=calcStackingTranslation(lx,ly,lz); 588 | $centerLineStack=calcCenterLineStackTube(lx,ly,lz,stackingTranslation); 589 | 590 | translate(stackingTranslation) 591 | children(); 592 | } 593 | 594 | module tube(h=heightInfo(),d=$d,dInner=$dInner,dOuter=$dOuter,wall=$wall,d1=undef, 595 | d2=$d2,solid=solidInfo(),arc=0 596 | ,holeStickOut=0,stickOutBothEnds=false,innerChamfer=false,inverted=undef) 597 | { 598 | $inverted= inverted==undef?$inverted:inverted; 599 | wall = zeroIfUndef(((dInner!=undef && dOuter!=undef) 600 | ?((dOuter-dInner)/2):wall)); 601 | d = ((dInner!=undef) 602 | ?dInner+wall*2: 603 | (dOuter!=undef 604 | ?dOuter:((d==undef)?d1:d))); 605 | 606 | lx=d; 607 | ly=d; 608 | assert(h!=undef,"TUBE():h is undefined"); 609 | assert(d!=undef,"TUBE():d is undefined"); 610 | lz=h; 611 | summingUp= falseIfUndef($summingUp) 612 | && !falseIfUndef($partOfAddAfterRemoving); 613 | 614 | translate(multV(alignInfo(),[lx,ly,lz])/2) 615 | scale(addToV(multV(absV(stackingInfo()[1]/*stackOverlap*/) 616 | ,[1/lx,1/ly,1/lz]),1)) 617 | { 618 | if(($inverted && $removing) 619 | || (!$inverted 620 | && (solid || !(summingUp 621 | && ($removing || !$beforeRemoving) 622 | )))) 623 | difference() 624 | { 625 | doChamferTube(lx=lx, ly=ly, lz=lz) 626 | cylinder(h, d1=d, d2=(d2==undef)?d:d2, center=true); 627 | align(TORIGHT,ZCENTER) 628 | { 629 | if(arc>=180) 630 | intersection_for(angle=[-180+90,arc+90]) 631 | g(turnXY(angle) 632 | ,chamferInfoUpdate(invert=innerChamfer)) 633 | box(side = max(d,(d2==undef)?0:d2) 634 | ,y=max(d,(d2==undef)?0:d2) 635 | +2*2*max(zeroIfUndef(chamferInfo()[0][0]) 636 | ,zeroIfUndef(chamferInfo()[1][0])) 637 | ,z=h+.1); 638 | 639 | if(arc>0 && arc<180) 640 | for(angle=[-180+90,arc+90]) 641 | g(turnXY(angle) 642 | //chamfer(disable=true) 643 | ,chamferInfoUpdate(invert=innerChamfer)) 644 | box(side=max(d,(d2==undef)?0:d2) 645 | ,y=max(d,(d2==undef)?0:d2) 646 | +2*2*max(zeroIfUndef(chamferInfo()[0][0]) 647 | ,zeroIfUndef(chamferInfo()[1][0]) 648 | ),h=h+.1); 649 | } 650 | if(!solid)up(stickOutBothEnds?0:holeStickOut/2) 651 | cylinder(((h+.03+abs(holeStickOut) 652 | +(stickOutBothEnds?abs(holeStickOut):0))) 653 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 654 | ,center=true); 655 | 656 | if((!solid && $inverted && $removing)) 657 | up(stickOutBothEnds?0:holeStickOut/2) 658 | cylinder(((h+.03+abs(holeStickOut) 659 | +(stickOutBothEnds?abs(holeStickOut):0))) 660 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 661 | ,center=true); 662 | } 663 | if(!solid && !$inverted && summingUp && $removing) 664 | up(stickOutBothEnds?0:holeStickOut/2) 665 | cylinder(((h+.03+abs(holeStickOut) 666 | +(stickOutBothEnds?abs(holeStickOut):0))) 667 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 668 | ,center=true); 669 | } 670 | stackingTranslation=calcStackingTranslation(lx,ly,lz); 671 | $centerLineStack=calcCenterLineStackTube(lx,ly,lz,stackingTranslation); 672 | translate(stackingTranslation) 673 | children(); 674 | } 675 | 676 | //-------------------------------------------------- 677 | module tubeShell(h=heightInfo(),d=$d,dInner=$dInner,dOuter=$dOuter,wall=$wall 678 | ,solid=solidInfo(),arc=0 679 | ,innerChamfer=false) 680 | { 681 | wall = zeroIfUndef(((dInner!=undef && dOuter!=undef) 682 | ?((dOuter-dInner)/2):wall)); 683 | d = ( dInner != undef ) 684 | ? dInner + wall * 2 685 | : ( dOuter != undef? dOuter : d); 686 | 687 | lx=d; 688 | ly=d; 689 | assert(h!=undef,"TUBESOFTHOLE():h is undefined"); 690 | assert(d!=undef,"TUBESOFTHOLE():d is undefined"); 691 | lz=h; 692 | summingUp= falseIfUndef($summingUp) 693 | && !falseIfUndef($partOfAddAfterRemoving); 694 | 695 | translate(multV(alignInfo(),[lx,ly,lz])/2) 696 | scale(addToV(multV(absV(stackingInfo()[1]/*stackOverlap*/) 697 | ,[1/lx,1/ly,1/lz]),1)) 698 | { 699 | difference() 700 | { 701 | translate([0,0,-h/2]) 702 | linear_extrude(height=h) 703 | difference() 704 | { 705 | circle(d=d); 706 | if(!solid) 707 | circle(d=d-wall*2); 708 | } 709 | 710 | align(TORIGHT,ZCENTER) 711 | { 712 | if(arc>=180) 713 | intersection_for(angle=[-180+90,arc+90]) 714 | g(turnXY(angle) 715 | ,chamferInfoUpdate(invert=innerChamfer)) 716 | box(side = d 717 | ,y = d + 2*2 718 | * max(zeroIfUndef(chamferInfo()[0][0]) 719 | ,zeroIfUndef(chamferInfo()[1][0])) 720 | ,z=h+.1); 721 | 722 | if(arc>0 && arc<180) 723 | for(angle=[-180+90,arc+90]) 724 | g(turnXY(angle) 725 | ,chamferInfoUpdate(invert=innerChamfer)) 726 | box(side = d 727 | ,y=d + 2*2*max(zeroIfUndef(chamferInfo()[0][0]) 728 | ,zeroIfUndef(chamferInfo()[1][0]) 729 | ),z=h+.1); 730 | } 731 | } 732 | } 733 | stackingTranslation=calcStackingTranslation(lx,ly,lz); 734 | $centerLineStack=calcCenterLineStackTube(lx,ly,lz,stackingTranslation); 735 | 736 | translate(stackingTranslation) 737 | children(); 738 | } 739 | 740 | //------------------------------------------------- 741 | 742 | //USE THE chamfer() function instead!! 743 | //THIS FUNCTION IS NORMALLY NOT SUPPOSED TO BE CALLED BY THE USER 744 | //because you need the object dimensions to use ist , 745 | //it caries out the chamfering when called internally by tube() or Box() 746 | // with parameters set before by chamfer() 747 | // it is called within tube() and box() function 748 | //lz =height Of chamfered Object to Top ) 749 | //lx,ly= horizontal size of chamfered object 750 | //chamferInfo set of sides to chamfer, set by the previos call to chamfer() module 751 | module doChamfer(lx,ly,lz,chamferInfo=chamferInfo(),tube=false,box=false,childFn=$fn,tubeData=tubeInfo()) 752 | { 753 | //i[0] = -1 at bottom ,1 at top; i[1] =chamferRadius, 754 | 755 | //echo("doChamfer.chamferInfo:",chamferInfo); 756 | 757 | useTubeData = (!lx && !ly && !lz && tube); 758 | lx= !useTubeData?lx:tubeData[2]; 759 | ly= !useTubeData?ly:tubeData[3]; 760 | lz= !useTubeData?lz:tubeData[4];//=h 761 | 762 | //echo("chamf:",chamferInfo); 763 | disable = chamferInfo[2]; 764 | 765 | if(!disable) 766 | for( i = [chamferInfo[0] , chamferInfo[1]] ) 767 | { 768 | r=i[1]; 769 | rSide=i[2]; 770 | fnCorner=i[3]; 771 | up( (lz==undef )?0:((lz/2-abs(r))*i[0])) 772 | mirror([0,0,i[0]>0?0:1]) 773 | linear_extrude(height=abs(r) 774 | ,slices=1 775 | ,scale=[(lx+2*r)/lx,(ly+2*r)/ly]) 776 | resize(tube?0:[lx,ly]) 777 | offset(r = abs( tube?0:rSide ), $fn = fnCorner) 778 | { 779 | if((!box && !tube) || disable) 780 | projection(cut=false) 781 | { 782 | $fn=childFn; 783 | children(); 784 | } 785 | else if(box) square([lx,ly],center=true,$fn=childFn); 786 | else if(tube) circle(d=lx,$fn=childFn); 787 | } 788 | } 789 | //addOnly()children(); 790 | if(!disable) 791 | intersection() 792 | { 793 | for(i=[chamferInfo[0],chamferInfo[1]]) 794 | { 795 | r=i[1]; 796 | rSide=i[2]; 797 | fnCorner=i[3]; 798 | //echo(rSide); 799 | mirror([0,0,i[0]>0?0:1]) 800 | down(chamferInfo[1]==undef?lz/2:0.02) 801 | linear_extrude( 802 | height=(chamferInfo[1]==undef?lz:lz/2) 803 | -abs(r)+.05 804 | ,slices=1) 805 | resize(tube?0:[lx,ly]) 806 | offset(r=abs(tube?0:rSide),$fn=fnCorner) 807 | { 808 | if((!box && !tube) ) 809 | projection(cut=false) 810 | { 811 | $fn=childFn; 812 | children(); 813 | } 814 | else if(box) square([lx,ly],center=true,$fn=childFn); 815 | else if(tube) circle(d=lx,$fn=childFn); 816 | } 817 | } 818 | 819 | if(!box && !tube) 820 | { 821 | echo(box,tube); 822 | children(); 823 | } 824 | } 825 | else children(); 826 | 827 | } 828 | module doChamferBox(lx,ly,lz,chamferInfo=chamferInfo(),childFn=$fn) 829 | { 830 | //i[0] = -1 at bottom ,1 at top; i[1] =chamferRadius, 831 | 832 | //echo("doChamfer.chamferInfo:",chamferInfo); 833 | 834 | useTubeData =false; 835 | lx= !useTubeData?lx:tubeData[2]; 836 | ly= !useTubeData?ly:tubeData[3]; 837 | lz= !useTubeData?lz:tubeData[4];//=h 838 | 839 | //echo("chamf:",chamferInfo); 840 | disable = chamferInfo[2]; 841 | 842 | if(!disable) 843 | for( i = [chamferInfo[0] , chamferInfo[1]] ) 844 | { 845 | r=i[1]; 846 | rSide=i[2]; 847 | fnCorner=i[3]; 848 | up( (lz==undef )?0:((lz/2-abs(r))*i[0])) 849 | mirror([0,0,i[0]>0?0:1]) 850 | linear_extrude(height=abs(r) 851 | ,slices=1 852 | ,scale=[(lx+2*r)/lx,(ly+2*r)/ly]) 853 | resize([lx,ly]) 854 | offset(r = abs( rSide ), $fn = fnCorner) 855 | square([lx,ly],center=true,$fn=childFn); 856 | 857 | } 858 | //addOnly()children(); 859 | if(!disable) 860 | for(i=[chamferInfo[0],chamferInfo[1]]) 861 | { 862 | r=i[1]; 863 | rSide=i[2]; 864 | fnCorner=i[3]; 865 | //echo(rSide); 866 | mirror([0,0,i[0]>0?0:1]) 867 | down(chamferInfo[1]==undef?lz/2:0.02) 868 | linear_extrude( 869 | height=(chamferInfo[1]==undef?lz:lz/2) 870 | -abs(r)+.05 871 | ,slices=1) 872 | resize([lx,ly]) 873 | offset(r=abs(rSide),$fn=fnCorner) 874 | square([lx,ly],center=true,$fn=childFn); 875 | } 876 | else children(); 877 | } 878 | 879 | module doChamferTube(lx,ly,lz,chamferInfo=chamferInfo(),childFn=$fn,tubeData=tubeInfo()) 880 | { 881 | useTubeData = (!lx && !ly && !lz); 882 | lx= !useTubeData?lx:tubeData[2]; 883 | ly= !useTubeData?ly:tubeData[3]; 884 | lz= !useTubeData?lz:tubeData[4];//=h 885 | 886 | //echo("chamf:",chamferInfo); 887 | disable = chamferInfo[2]; 888 | 889 | if(!disable) 890 | for( i = [chamferInfo[0] , chamferInfo[1]] ) 891 | { 892 | r=i[1]; 893 | rSide=i[2]; 894 | fnCorner=i[3]; 895 | up( (lz==undef )?0:((lz/2-abs(r))*i[0])) 896 | mirror([0,0,i[0]>0?0:1]) 897 | linear_extrude(height=abs(r) 898 | ,slices=1 899 | ,scale=[(lx+2*r)/lx,(ly+2*r)/ly]) 900 | circle(d=lx,$fn=childFn); 901 | 902 | mirror([0,0,i[0]>0?0:1]) 903 | down(chamferInfo[1]==undef?lz/2:0.02) 904 | linear_extrude( 905 | height=(chamferInfo[1]==undef?lz:lz/2)-abs(r)+.05 906 | ,slices=1) 907 | circle(d=lx,$fn=childFn); 908 | } 909 | //addOnly()children(); 910 | else children(); 911 | 912 | } 913 | -------------------------------------------------------------------------------- /sources/geomInfo.scad: -------------------------------------------------------------------------------- 1 | //GEOMINFO.SCAD 2 | //This is a part of: 3 | //CONSTRUCTIVE LIBRARY by PPROJ (version from 05.06.2021) 4 | //released under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 5 | 6 | //you only need a single file: constructive-compiled.scad which 7 | //contains all the partial files you find. you can ignore everything else.. 8 | //just include it in your code by: 9 | //include 10 | 11 | //if you wish to improve the library or make changes to it, 12 | // it might be handier to use: 13 | //include instead. so you do not have to recreate constructive-compiled.scad from the parts 14 | //every time you make a change to a part of the library 15 | 16 | function _prototype_PackedGeomerty() = 17 | definePrototype("type.PackedGeometry",/*index=*/undef, 18 | [ 19 | definePrototype("type.Geometry.solid",/*index*/0, 20 | [ 21 | false 22 | ]) 23 | ,definePrototype("type.Geometry.tube",/*index=*/1, 24 | [ 25 | 0/*wall*/ 26 | ,10/*d*/ 27 | ,10/*lx=d*/ 28 | ,10/*ly=d*/ 29 | ,10/*h*/ 30 | ,undef/*d1*/ 31 | ,undef/*d2*/ 32 | ,false/*solid*/ 33 | ,false//useGeom 34 | ]) 35 | ,definePrototype("type.Geometry.chamfer",/*index=*/2, 36 | [ 37 | [] //-1, -2, -2, 7 38 | ,[] 39 | ,true //disable 40 | ]) 41 | ,definePrototype("type.Geometry.align",/*index*/3, 42 | [ 43 | NOCHANGE 44 | ]) 45 | ,definePrototype("type.Geometry.stacking",/*index*/4, 46 | [ 47 | [0,0,0]//$stackDirection= 48 | ,[0,0,0]//direction*abs(mergeBy),//$stackOverlap 49 | ,[0,0,0]//direction*abs(spaceBy) //$stackSpaceBy]) 50 | ]) 51 | ,definePrototype("type.Geometry.height",/*index*/5, 52 | [ 53 | undef 54 | ]) 55 | ,definePrototype("type.Geometry.currentPartStack",/*index*/6, 56 | [ 57 | "main",undef,undef 58 | ]) 59 | /* ,definePrototype("type.Geometry.noHull",, 60 | [ 61 | false 62 | ]) 63 | */ 64 | 65 | ]); 66 | 67 | function _prototype_geomInfo_solid() = _prototype_PackedGeomerty() [0]; 68 | function _prototype_geomInfo_Tube() = _prototype_PackedGeomerty() [1]; 69 | function _prototype_geomInfo_Chamfer() = _prototype_PackedGeomerty() [2]; 70 | function _prototype_geomInfo_Align() = _prototype_PackedGeomerty() [3]; 71 | function _prototype_geomInfo_Stacking() = _prototype_PackedGeomerty() [4]; 72 | function _prototype_geomInfo_Height() = _prototype_PackedGeomerty() [5]; 73 | function _prototype_geomInfo_currentPartStack() = _prototype_PackedGeomerty() [6]; 74 | 75 | //function _prototype_geomInfo_noHull() = _prototype_PackedGeomerty() [1]; 76 | 77 | 78 | $type_geomInfoSolid = typeOf(_prototype_geomInfo_solid()); 79 | $type_geomInfoTube = typeOf(_prototype_geomInfo_Tube()); 80 | $type_geomInfoChamfer = typeOf(_prototype_geomInfo_Chamfer()); 81 | $type_geomInfoAlign = typeOf(_prototype_geomInfo_Align()); 82 | $type_geomInfoStacking = typeOf(_prototype_geomInfo_Stacking()); 83 | $type_geomInfoHeight = typeOf(_prototype_geomInfo_Height()); 84 | $type_currentPartStack= typeOf(_prototype_geomInfo_currentPartStack()); 85 | //$type_geomInfoNoHull = typeOf(_prototype_geomInfo_solid()); 86 | 87 | $type_geomInfoPacked = typeOf(_prototype_PackedGeomerty()); 88 | 89 | function isOfGeomType(obj)= 90 | ( isOfType(obj,$type_geomInfoSolid) 91 | || isOfType(obj,$type_geomInfoTube) 92 | || isOfType(obj,$type_geomInfoChamfer) 93 | || isOfType(obj,$type_geomInfoAlign) 94 | || isOfType(obj,$type_geomInfoStacking) 95 | || isOfType(obj,$type_geomInfoHeight) 96 | || isOfType(obj,$type_currentPartStack) 97 | //$type_geomInfoNoHull = typeOf(_prototype_geomInfo_solid()); 98 | || isOfType(obj,$type_geomInfoPacked) ); 99 | 100 | $geomInfo= _prototype_PackedGeomerty(); 101 | 102 | function set(geom1,geom2,geom3,geom4,geom5,geom6,geom7,geom8,geom9,geom10 103 | ,geom11,geom12,geom13,geom14,geom15,geom16,geom17,geom18,geom19,geom20 104 | ,oldGeom=$geomInfo) 105 | = isOfType(geom1,$type_geomInfoPacked) 106 | ?geom1 107 | //"error: only unpacked geometry must be passed to geomSet" 108 | //:geom1; 109 | :[ for(member=_prototype_PackedGeomerty()) 110 | (member == typeOf(oldGeom)) 111 | ? member 112 | : isOfTypeOrElse(typeOf(member),geom1, 113 | isOfTypeOrElse(typeOf(member),geom2, 114 | isOfTypeOrElse(typeOf(member),geom3, 115 | isOfTypeOrElse(typeOf(member),geom4, 116 | isOfTypeOrElse(typeOf(member),geom5, 117 | isOfTypeOrElse(typeOf(member),geom6, 118 | isOfTypeOrElse(typeOf(member),geom7, 119 | isOfTypeOrElse(typeOf(member),geom8, 120 | isOfTypeOrElse(typeOf(member),geom9, 121 | isOfTypeOrElse(typeOf(member),geom10, 122 | isOfTypeOrElse(typeOf(member),geom11, 123 | isOfTypeOrElse(typeOf(member),geom12, 124 | isOfTypeOrElse(typeOf(member),geom13, 125 | isOfTypeOrElse(typeOf(member),geom14, 126 | isOfTypeOrElse(typeOf(member),geom15, 127 | isOfTypeOrElse(typeOf(member),geom16, 128 | isOfTypeOrElse(typeOf(member),geom17, 129 | isOfTypeOrElse(typeOf(member),geom18, 130 | isOfTypeOrElse(typeOf(member),geom19, 131 | isOfTypeOrElse(typeOf(member),geom20, 132 | oldGeom[getTypeIndex(typeOf(member))])))))))))))))))))))) 133 | ]; 134 | 135 | function setFromList(geomList,oldGeom=$geomInfo) 136 | = isOfType(geomList[0],$type_geomInfoPacked) 137 | ?geomList[0] 138 | //"error: only unpacked geometry must be passed to geomSet" 139 | //:geom1; 140 | :[ for(member=_prototype_PackedGeomerty()) 141 | (member == typeOf(oldGeom)) 142 | ? member 143 | : isOfTypeOrElse(typeOf(member),geomList[0], 144 | isOfTypeOrElse(typeOf(member),geomList[1], 145 | isOfTypeOrElse(typeOf(member),geomList[2], 146 | isOfTypeOrElse(typeOf(member),geomList[3], 147 | isOfTypeOrElse(typeOf(member),geomList[4], 148 | isOfTypeOrElse(typeOf(member),geomList[5], 149 | isOfTypeOrElse(typeOf(member),geomList[6], 150 | isOfTypeOrElse(typeOf(member),geomList[7], 151 | isOfTypeOrElse(typeOf(member),geomList[8], 152 | isOfTypeOrElse(typeOf(member),geomList[9], 153 | isOfTypeOrElse(typeOf(member),geomList[10], 154 | isOfTypeOrElse(typeOf(member),geomList[11], 155 | isOfTypeOrElse(typeOf(member),geomList[12], 156 | isOfTypeOrElse(typeOf(member),geomList[13], 157 | isOfTypeOrElse(typeOf(member),geomList[14], 158 | isOfTypeOrElse(typeOf(member),geomList[15], 159 | isOfTypeOrElse(typeOf(member),geomList[16], 160 | isOfTypeOrElse(typeOf(member),geomList[17], 161 | isOfTypeOrElse(typeOf(member),geomList[18], 162 | isOfTypeOrElse(typeOf(member),geomList[19], 163 | oldGeom[getTypeIndex(typeOf(member))])))))))))))))))))))) 164 | ]; 165 | 166 | 167 | function stackingInfo() = $geomInfo[getTypeIndex($type_geomInfoStacking)]; 168 | function stack(direction=TOUP,spaceBy=0,mergeBy=0,geom=$geomInfo) 169 | = setType($type_geomInfoStacking 170 | ,[direction,//$stackDirection= 171 | direction*abs(mergeBy),//$stackOverlap= 172 | direction*abs(spaceBy)//$stackSpaceBy=]) 173 | ]); 174 | // $geomInfo=set(geom); 175 | // g(align(direction)) 176 | // children(); 177 | 178 | //different geomInfo setter Functions 179 | function alignInfo() = $geomInfo[getTypeIndex($type_geomInfoAlign) ][0]; 180 | function align(a1=NOCHANGE,a2=NOCHANGE,a3=NOCHANGE) = 181 | setType($type_geomInfoAlign 182 | , let(vals=a1+a2+a3) 183 | let(changes=[vals[0+3],vals[1+3],vals[2+3]]) 184 | let(alignment=alignInfo()) 185 | [[ (changes[0])?vals[0]:alignment[0] 186 | ,(changes[1])?vals[1]:alignment[1] 187 | ,(changes[2])?vals[2]:alignment[2]]]); 188 | 189 | 190 | function currentPartRemove() = currentPart(2); 191 | function currentPartAdd() = currentPart(1); 192 | 193 | function currentPart(forWhat=0) = 194 | let(val = $geomInfo[getTypeIndex($type_currentPartStack)]) 195 | is_undef(val[forWhat])? val[0] : val[forWhat]; 196 | 197 | function applyTo(partName="main",add=undef,remove=undef) 198 | = setType($type_currentPartStack 199 | ,[partName,add,remove]); 200 | -------------------------------------------------------------------------------- /sources/globals.scad: -------------------------------------------------------------------------------- 1 | //GLOBALS.SCAD 2 | //This is a part of: 3 | //CONSTRUCTIVE LIBRARY by PPROJ (version from 05.06.2021) 4 | //released under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 5 | 6 | //you only need a single file: constructive-compiled.scad which 7 | //contains all the partial files you find. you can ignore everything else.. 8 | //just include it in your code by: 9 | //include 10 | 11 | //if you wish to improve the library or make changes to it, 12 | // it might be handier to use: 13 | //include instead. so you do not have to recreate constructive-compiled.scad from the parts 14 | //every time you make a change to a part of the library 15 | 16 | 17 | 18 | $padding=.8; 19 | $margin=.8; 20 | 21 | 22 | $sideX=undef; 23 | $sideY=undef; 24 | $sideZ=undef; 25 | 26 | 27 | $derivedParts=[]; 28 | 29 | $autoColor=[undef,1]; 30 | 31 | UNITY= [ [1, 0, 0, 0], 32 | [0, 1, 0, 0], 33 | [0, 0, 1, 0], 34 | [0, 0, 0, 1]]; 35 | $placement =UNITY; 36 | infinity=1e308; 37 | inf=infinity; 38 | 39 | 40 | $placementStack=[["origin",UNITY]]; 41 | $placementStackTop=0; 42 | 43 | $summingUp=false; 44 | 45 | $hulling=false; 46 | $removing=false; 47 | $removingAgain=false; 48 | $addingAgain=false; 49 | $partOfAddAfterRemoving=false; 50 | $inverted=false; 51 | $beforeRemoving=true; 52 | $enclosing=false; 53 | $currentBody=undef; 54 | 55 | $valPtr=0; 56 | 57 | $arc=undef; 58 | $x=undef; 59 | $y=undef; 60 | $h=undef; 61 | $d=undef; 62 | $d2=undef; 63 | $dInner=undef; 64 | $dOuter=undef; 65 | $wall=0; 66 | $solid=false; 67 | $hBore=undef; 68 | 69 | 70 | //used to track transformations made by move() to be still 71 | // able to use connect() 72 | $centerLineStack=[[0,0,0,"m"]]; 73 | 74 | 75 | 76 | //global constants which an be used with the align() function 77 | //like align(TORIGHT,TOUP) box(side=10); 78 | // instead of toRight() toUp() box(side=10); 79 | RESET=[0,0,0,1,1,1]; 80 | NOCHANGE=[0,0,0,0,0,0]; 81 | 82 | XCENTER=[0,0,0,1,0,0]; 83 | YCENTER=[0,0,0,0,1,0]; 84 | ZCENTER=[0,0,0,0,0,1]; 85 | 86 | TORIGHT=[1,0,0,1,0,0]; 87 | TOREAR= [0,1,0,0,1,0]; 88 | TOBEHIND=[0,1,0,0,1,0]; 89 | 90 | TOUP=[0,0,1,0,0,1]; 91 | TODOWN=-1* TOUP; 92 | TOLEFT=-1* TORIGHT; 93 | TOFRONT=-1* TOBEHIND; 94 | 95 | 96 | /***internal global variables end************************/ 97 | 98 | -------------------------------------------------------------------------------- /sources/metricScrews.scad: -------------------------------------------------------------------------------- 1 | //METRICSCREWS.SCAD 2 | //This is a part of: 3 | //CONSTRUCTIVE LIBRARY by PPROJ (version from 05.06.2021) 4 | //released under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 5 | 6 | //you only need a single file: constructive-compiled.scad which 7 | //contains all the partial files you find. you can ignore everything else.. 8 | //just include it in your code by: 9 | //include 10 | 11 | //if you wish to improve the library or make changes to it, 12 | // it might be handier to use: 13 | //include instead. so you do not have to recreate constructive-compiled.scad from the parts 14 | //every time you make a change to a part of the library 15 | 16 | 17 | module cableClampSm(hMargin=margin(0),marginUp=0,xyMargin=margin(0,1),x=4,y=5.4,h=10.6) 18 | cableClamp(hMargin,marginUp,xyMargin,x,y,h)children(); 19 | 20 | module cableClamp6m(hMargin=margin(0),marginUp=0,xyMargin=margin(0,1),x=4.3,y=5.9,h=10.4) 21 | cableClamp(hMargin,marginUp,xyMargin,x,y,h)children(); 22 | 23 | module clampScrews(hMargin=margin(0),screws=2) 24 | pieces(screws)g(Z(-sides($screwDist/2-3/2)),turnXZ(-90),TOUP()) 25 | { 26 | tubeFast(d=margin(2.2),h=5,solid=true,$fn=15); 27 | Z(5-.01)tubeFast(d=margin(4),h=3+hMargin,solid=true,$fn=15); 28 | } 29 | module cableClamp(hMargin=margin(0),marginUp=0,xyMargin=margin(0),x=4.3,y=5.7,h=10.3) 30 | 31 | { 32 | Z((-h-hMargin)/2)linear_extrude(hMargin+h+marginUp) 33 | turnXY(-90) 34 | Y(y/2-xyMargin/2) 35 | { 36 | 37 | circle(d=x+xyMargin,$fn=21); 38 | yCube=y-x/2+xyMargin/2; 39 | Y(yCube/2)square([x+xyMargin,yCube],center=true); 40 | } 41 | $screwDist=h-1.4; 42 | X(3) children(); 43 | } 44 | 45 | module pressNutM3(marginUp=margin(0),marginDown=margin(0), 46 | dSpike=margin(2,.5),hSpike=margin(4.5,.5),rSpike=5.5, 47 | washerOnly=false) 48 | g(TOUP(),solid()) 49 | { 50 | opaq(grey) 51 | g(align(XCENTER,YCENTER) 52 | ,Z(margin(0,-marginUp-marginDown))) 53 | stack() 54 | tubeFast(dOuter=margin(13),h =margin(.9,marginUp+marginDown)) 55 | if(!washerOnly)Z(-0.01)tubeFast(dOuter=margin(4),h = margin(5,marginUp)-.9 56 | + marginUp); 57 | pieces(4) 58 | g(turnXY(spanAllButLast()),Y(-rSpike),Z(0.05) 59 | ,chamfer(0,-1.5),cscale(.7,1,1)) 60 | tube(d=dSpike,h=hSpike); 61 | } 62 | 63 | module pressNutM4(marginUp=margin(0),marginDown=margin(0)) 64 | { 65 | opaq(grey) 66 | g(align(XCENTER,YCENTER) 67 | ,Z(margin(0,-marginUp-marginDown))) 68 | stack() 69 | tubeFast(dOuter=margin(15-4.4*0),h =margin(.7,marginUp+marginDown)) 70 | tubeFast(dOuter=margin(5.1),h = margin(6,marginUp)-.7 71 | + marginUp); 72 | } 73 | 74 | 75 | module nutM4(nutMargin=.4,dMargin=margin(0)) 76 | clear(grey) 77 | { 78 | g(Z(3.1),align(TODOWN,XCENTER,YCENTER),solid()) 79 | tubeFast(d=8.1+dMargin 80 | ,h=margin(3.1,nutMargin),$fn=6); 81 | } 82 | 83 | module nutM3(h=margin(3.8),d=margin(6.2),align=TOUP()) 84 | g(align)tubeFast(d=d,h=h,$fn=6); 85 | 86 | module screwM3(h=10.1,nut=true 87 | ,capMargin=3.8) 88 | clear(grey) 89 | { 90 | g(align(TOUP,XCENTER,YCENTER),solid()) 91 | { 92 | tubeFast(dOuter = margin(3),h = h-2.1+.02); 93 | Z(h-2.1) 94 | tubeFast(d = margin(5.5),h = 2.1+.02); 95 | Z(h-.02) 96 | tubeFast(d=margin(5.5) 97 | ,wall=2 98 | ,h = .01+removeExtra(capMargin)); 99 | } 100 | children(); 101 | } 102 | module inbusScrewM3(h=10.1,nut=true 103 | ,capMargin=3.8) 104 | clear(grey) 105 | { 106 | g(align(TOUP,XCENTER,YCENTER),solid()) 107 | { 108 | tubeFast(dOuter = margin(3),h = h-2.1+.02); 109 | Z(h-2.1) 110 | tubeFast(d = margin(5.5),h = 2.1+.02); 111 | Z(h-.02) 112 | tubeFast(d=margin(5.5) 113 | ,wall=2 114 | ,h = .01+removeExtra(capMargin)); 115 | } 116 | children(); 117 | } 118 | module screwM4(h=16,nut=true 119 | ,capMargin=3.8,nutMargin=.4,noNut=false) 120 | clear(grey) 121 | { 122 | if(!noNut) 123 | g(align(TODOWN,XCENTER,YCENTER), 124 | Z(3.1),solid()) 125 | tubeFast(d=margin(8.1) 126 | ,h=margin(3.1,nutMargin),$fn=6); 127 | g(align(TOUP,XCENTER,YCENTER),solid()) 128 | { 129 | tubeFast(dOuter = margin(4),h = h-2.4+.02); 130 | Z(h-2.4) 131 | tubeFast(d2=7.5,d = margin(4),h = 2.4+.02); 132 | Z(h-.02) 133 | tubeFast(d=margin(8.2) 134 | ,wall=2 135 | ,h = .01+removeExtra(capMargin)); 136 | } 137 | children(); 138 | } 139 | -------------------------------------------------------------------------------- /sources/nonMetricScrews.scad: -------------------------------------------------------------------------------- 1 | //NONMETRICSCRES.SCAD 2 | //This is a part of: 3 | //CONSTRUCTIVE LIBRARY by PPROJ (version from 05.06.2021) 4 | //released under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 5 | 6 | //you only need a single file: constructive-compiled.scad which 7 | //contains all the partial files you find. you can ignore everything else.. 8 | //just include it in your code by: 9 | //include 10 | 11 | //if you wish to improve the library or make changes to it, 12 | // it might be handier to use: 13 | //include instead. so you do not have to recreate constructive-compiled.scad from the parts 14 | //every time you make a change to a part of the library 15 | 16 | module screw3mm(hCap=5,side=1,h=12.3) 17 | g(align(RESET,TOUP) 18 | ,Z(-hCap)) 19 | chamfer(1,-1) 20 | tube(d=(5.7),h=hCap+.1,$fn=12) 21 | g(Z(hCap+.1-.05),chamfer(.1,-.3)) 22 | { 23 | tube(d=margin(3.1,.2),h=4,$fn=12); 24 | tubeFast(d=margin(2,.2),h=h,$fn=12); 25 | } 26 | -------------------------------------------------------------------------------- /sources/placements.scad: -------------------------------------------------------------------------------- 1 | //PLACEMENTS.SCAD 2 | //This is a part of: 3 | //CONSTRUCTIVE LIBRARY by PPROJ (version from 05.06.2021) 4 | //released under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 5 | 6 | //you only need a single file: constructive-compiled.scad which 7 | //contains all the partial files you find. you can ignore everything else.. 8 | //just include it in your code by: 9 | //include 10 | 11 | //if you wish to improve the library or make changes to it, 12 | // it might be handier to use: 13 | //include instead. so you do not have to recreate constructive-compiled.scad from the parts 14 | //every time you make a change to a part of the library 15 | 16 | //include 17 | //include 18 | //include 19 | 20 | //the followoing Matrix inversion funcion derived from Oscar lindes code 21 | //Copyright (c) 2014 Oskar Linde 22 | 23 | //Permission is hereby granted, free of charge, to any person obtaining a copy 24 | //of this software and associated documentation files (the "Software"), to deal 25 | //in the Software without restriction, including without limitation the rights 26 | //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 27 | //copies of the Software, and to permit persons to whom the Software is 28 | //furnished to do so, subject to the following conditions: 29 | 30 | //The above copyright notice and this permission notice shall be included in all 31 | //copies or substantial portions of the Software. 32 | 33 | //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | //SOFTWARE. 40 | 41 | 42 | //matrix math 43 | function vec3(p) = len(p) < 3 ? concat(p,0) : p; 44 | function vec4(p) = let (v3=vec3(p)) len(v3) < 4 ? concat(v3,1) : v3; 45 | function unit(v) = v/norm(v); 46 | function take3(v) = [v[0],v[1],v[2]]; 47 | function tail3(v) = [v[3],v[4],v[5]]; 48 | function identity3()=[[1,0,0],[0,1,0],[0,0,1]]; 49 | function identity4()=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]; 50 | function take3(v) = [v[0],v[1],v[2]]; 51 | function tail3(v) = [v[3],v[4],v[5]]; 52 | function rotation_part(m) = [take3(m[0]),take3(m[1]),take3(m[2])]; 53 | function unit(v) = v/norm(v); 54 | function rot_trace(m) = m[0][0] + m[1][1] + m[2][2]; 55 | function rot_cos_angle(m) = (rot_trace(m)-1)/2; 56 | 57 | 58 | q1=[[1,0,0,0],[1,1,1,1],[0,1,2,3],[0,0,1,3]]; 59 | q1inv=[[1,0,0,0],[-3,3,-2,1],[3,-3,3,-2],[-1,1,-1,1]]; 60 | q2=[[0,0,0,0],[0,0,0,0],[0,-1,0,0],[0,0,-1,0]]; 61 | qn1i2=-q1inv*q2; 62 | z3=[0,0,0]; 63 | z4=[0,0,0,0]; 64 | 65 | function matrix_power(m,n)= n==0? (len(m)==3?identity3():identity4()) : 66 | n==1 ? m : (n%2==1) ? matrix_power(m*m,floor(n/2))*m : matrix_power(m*m,n/2); 67 | 68 | function det(m) = let(r=[for(i=[0:1:len(m)-1]) i]) det_help(m, 0, r); 69 | // Construction indices list is inefficient, but currently there is no way to imperatively 70 | // assign to a list element 71 | function det_help(m, i, r) = len(r) == 0 ? 1 : 72 | m[len(m)-len(r)][r[i]]*det_help(m,0,remove(r,i)) - (i+10)?setFromList(geomList):set(geom); 191 | 192 | function placesOnly(p)= [for(e=p)if(isPlace(e) && !isOfGeomType(e))e]; 193 | 194 | module g(step1=UNITY,step2=UNITY,step3=UNITY,step4=UNITY,step5=UNITY 195 | ,step6=UNITY,step7=UNITY,step8=UNITY,step9=UNITY,step10=UNITY 196 | ,step11=UNITY,step12=UNITY,step13=UNITY,step14=UNITY,step15=UNITY 197 | ,step16=UNITY,step17=UNITY,step18=UNITY,step19=UNITY,step20=UNITY 198 | ,name="",geom) 199 | { 200 | p=[step1,step2,step3,step4,step5 201 | ,step6,step7,step8,step9,step10 202 | ,step11,step12,step13,step14,step15 203 | ,step16,step17,step18,step19,step20]; 204 | 205 | place=placesOnly(p); 206 | $geomInfo = geomsOnly(p,geom); 207 | $placement=multAll(place); 208 | $placementStack=concat($placementStack, [[name,$placement]]); 209 | $placementStackTop=$placementStackTop+1; 210 | multmatrix($placement) 211 | { 212 | $placement=UNITY; 213 | children(); 214 | } 215 | } 216 | 217 | module applyTo(partName,step2=UNITY,step3=UNITY,step4=UNITY,step5=UNITY 218 | ,step6=UNITY,step7=UNITY,step8=UNITY,step9=UNITY,step10=UNITY 219 | ,step11=UNITY,step12=UNITY,step13=UNITY,step14=UNITY,step15=UNITY 220 | ,step16=UNITY,step17=UNITY,step18=UNITY,step19=UNITY,step20=UNITY 221 | ,name="",geom) 222 | //step1 is already occupied by the "applyTo" parameter, so we start with the step2 223 | g(applyTo(partName),step2,step3,step4,step5,step6 224 | ,step7,step8,step9,step10 225 | ,step11,step12,step13,step14,step15 226 | ,step16,step17,step18,step19,step20 227 | ,name=name,geom=geom) 228 | children(); 229 | -------------------------------------------------------------------------------- /sources/typeSystem.scad: -------------------------------------------------------------------------------- 1 | // TYPEINFO.SCAD 2 | //This is a part of: 3 | //CONSTRUCTIVE LIBRARY by PPROJ (version from 05.06.2021) 4 | //released under dual-licensed under GPL 2.0 or CERN-OHL-W.*You can choose between one of them if you use this work.. 5 | 6 | //you only need a single file: constructive-compiled.scad which 7 | //contains all the partial files you find. you can ignore everything else.. 8 | //just include it in your code by: 9 | //include 10 | 11 | //if you wish to improve the library or make changes to it, 12 | // it might be handier to use: 13 | //include instead. so you do not have to recreate constructive-compiled.scad from the parts 14 | //every time you make a change to a part of the library 15 | 16 | function noUndefs( 17 | what1=true 18 | ,what2=true 19 | ,what3=true 20 | ,what4=true 21 | ,what5=true 22 | ,what6=true 23 | ,what7=true 24 | ,what8=true 25 | ,what9=true 26 | ,what10=true 27 | ,what11=true 28 | ,what12=true 29 | ) = (what1 != undef) && 30 | (what2 != undef) && 31 | (what3 != undef) && 32 | (what4 != undef) && 33 | (what5 != undef) && 34 | (what6 != undef) && 35 | (what7 != undef) && 36 | (what8 != undef) && 37 | (what9 != undef) && 38 | (what10 != undef) && 39 | (what11 != undef) && 40 | (what12 != undef); 41 | function has3ElemsOrMore(what) = noUndefs(what,what[0],what[1],what[2]); 42 | function isPlace(what)= has3ElemsOrMore(what) 43 | && has3ElemsOrMore(what[0]) 44 | && has3ElemsOrMore(what[1]) 45 | && has3ElemsOrMore(what[2]) 46 | && has3ElemsOrMore(top(what)) 47 | && top(what)[3]==1; 48 | 49 | function isPlaceOrGeom(what)= isOfGeomType(what) || isPlace(what); 50 | 51 | //---------typesystem start-------------------------------- 52 | 53 | function _prototype_type() = "type.typeMark"; 54 | function definePrototype(typeString,index, protoData)= 55 | [for (i =[0:1: len(protoData)]) 56 | (i =180) 36 | intersection_for(angle=[-180+90,arc+90]) 37 | g(turnXY(angle) 38 | ,chamferInfoUpdate(invert=innerChamfer)) 39 | box(side = max(d,(d2==undef)?0:d2) 40 | ,y=max(d,(d2==undef)?0:d2) 41 | +2*2*max(zeroIfUndef(chamferInfo()[0][0]) 42 | ,zeroIfUndef(chamferInfo()[1][0])) 43 | ,z=h+.1); 44 | 45 | if(arc>0 && arc<180) 46 | for(angle=[-180+90,arc+90]) 47 | g(turnXY(angle) 48 | ,chamferInfoUpdate(invert=innerChamfer)) 49 | box(side=max(d,(d2==undef)?0:d2) 50 | ,y=max(d,(d2==undef)?0:d2) 51 | +2*2*max(zeroIfUndef(chamferInfo()[0][0]) 52 | ,zeroIfUndef(chamferInfo()[1][0]) 53 | ),z=h+.1); 54 | } 55 | up(stickOutBothEnds?0:holeStickOut/2) 56 | cylinder(solid?0:((h+.03+abs(holeStickOut) 57 | +(stickOutBothEnds?abs(holeStickOut):0))) 58 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 59 | ,center=true); 60 | 61 | if(($inverted && $removing)) 62 | up(stickOutBothEnds?0:holeStickOut/2) 63 | cylinder(solid?0:((h+.03+abs(holeStickOut) 64 | +(stickOutBothEnds?abs(holeStickOut):0))) 65 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 66 | ,center=true); 67 | } 68 | if(!$inverted && summingUp && $removing) 69 | up(stickOutBothEnds?0:holeStickOut/2) 70 | cylinder(solid?0:((h+.03+abs(holeStickOut) 71 | +(stickOutBothEnds?abs(holeStickOut):0))) 72 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 73 | ,center=true); 74 | } 75 | stackingTranslation=calcStackingTranslation(lx,ly,lz); 76 | $centerLineStack=calcCenterLineStackTube(lx,ly,lz,stackingTranslation); 77 | 78 | translate(stackingTranslation) 79 | children(); 80 | } 81 | /* 82 | module tube(h=heightInfo(),d=$d,dInner=$dInner,dOuter=$dOuter,wall=$wall,d1=undef, 83 | d2=$d2,solid=solidInfo(),arc=0 84 | ,holeStickOut=0,stickOutBothEnds=false,innerChamfer=false,inverted=undef) 85 | { 86 | $inverted= inverted==undef?$inverted:inverted; 87 | wall = zeroIfUndef(((dInner!=undef && dOuter!=undef) 88 | ?((dOuter-dInner)/2):wall)); 89 | d = ((dInner!=undef) 90 | ?dInner+wall*2: 91 | (dOuter!=undef 92 | ?dOuter:((d==undef)?d1:d))); 93 | 94 | lx=d; 95 | ly=d; 96 | assert(h!=undef,"TUBE():h is undefined"); 97 | assert(d!=undef,"TUBE():d is undefined"); 98 | lz=h; 99 | summingUp= falseIfUndef($summingUp) 100 | && !falseIfUndef($partOfAddAfterRemoving); 101 | 102 | translate(multV(alignInfo(),[lx,ly,lz])/2) 103 | scale(addToV(multV(absV(stackingInfo()[1])//stackOverlap 104 | ,[1/lx,1/ly,1/lz]),1)) 105 | { 106 | if(($inverted && $removing) 107 | || (!$inverted 108 | && (solid || !(summingUp 109 | && ($removing || !$beforeRemoving) 110 | )))) 111 | difference() 112 | { 113 | doChamferTube(lx=lx, ly=ly, lz=lz) 114 | cylinder(h, d1=d, d2=(d2==undef)?d:d2, center=true); 115 | align(TORIGHT,ZCENTER) 116 | { 117 | if(arc>=180) 118 | intersection_for(angle=[-180+90,arc+90]) 119 | g(turnXY(angle) 120 | ,geom = chamfer(invert=innerChamfer)) 121 | box(side = max(d,(d2==undef)?0:d2) 122 | ,y=max(d,(d2==undef)?0:d2) 123 | +2*2*max(zeroIfUndef(chamferInfo()[0][0]) 124 | ,zeroIfUndef(chamferInfo()[1][0])) 125 | ,z=h+.1); 126 | 127 | if(arc>0 && arc<180) 128 | for(angle=[-180+90,arc+90]) 129 | g(turnXY(angle) 130 | ,geom=chamfer(invert=innerChamfer)) 131 | box(side=max(d,(d2==undef)?0:d2) 132 | ,y=max(d,(d2==undef)?0:d2) 133 | +2*2*max(zeroIfUndef(chamferInfo()[0][0]) 134 | ,zeroIfUndef(chamferInfo()[1][0]) 135 | ),z=h+.1); 136 | } 137 | up(stickOutBothEnds?0:holeStickOut/2) 138 | cylinder(solid?0:((h+.03+abs(holeStickOut) 139 | +(stickOutBothEnds?abs(holeStickOut):0))) 140 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 141 | ,center=true); 142 | 143 | if(($inverted && $removing)) 144 | up(stickOutBothEnds?0:holeStickOut/2) 145 | cylinder(solid?0:((h+.03+abs(holeStickOut) 146 | +(stickOutBothEnds?abs(holeStickOut):0))) 147 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 148 | ,center=true); 149 | } 150 | if(!$inverted && summingUp && $removing) 151 | up(stickOutBothEnds?0:holeStickOut/2) 152 | cylinder(solid?0:((h+.03+abs(holeStickOut) 153 | +(stickOutBothEnds?abs(holeStickOut):0))) 154 | ,d1=d-wall*2,d2=((d2==undef)?d:d2)-wall*2 155 | ,center=true); 156 | } 157 | stackingTranslation=calcStackingTranslation(lx,ly,lz); 158 | $centerLineStack=calcCenterLineStackTube(lx,ly,lz,stackingTranslation); 159 | translate(stackingTranslation) 160 | children(); 161 | } 162 | */ 163 | /* 164 | //USE THE chamfer() function instead!! 165 | //THIS FUNCTION IS NORMALLY NOT SUPPOSED TO BE CALLED BY THE USER 166 | //because you need the object dimensions to use ist , 167 | //it caries out the chamfering when called internally by tube() or Box() 168 | // with parameters set before by chamfer() 169 | // it is called within tube() and box() function 170 | //lz =height Of chamfered Object to Top ) 171 | //lx,ly= horizontal size of chamfered object 172 | //chamferInfo set of sides to chamfer, set by the previos call to chamfer() module 173 | module doChamfer(lx,ly,lz,chamferInfo=chamferInfo(),tube=false,box=false,childFn=$fn,tubeData=tubeInfo()) 174 | { 175 | //i[0] = -1 at bottom ,1 at top; i[1] =chamferRadius, 176 | 177 | //echo("doChamfer.chamferInfo:",chamferInfo); 178 | 179 | useTubeData = (!lx && !ly && !lz && tube); 180 | lx= !useTubeData?lx:tubeData[2]; 181 | ly= !useTubeData?ly:tubeData[3]; 182 | lz= !useTubeData?lz:tubeData[4];//=h 183 | 184 | //echo("chamf:",chamferInfo); 185 | disable = chamferInfo[2]; 186 | 187 | if(!disable) 188 | for( i = [chamferInfo[0] , chamferInfo[1]] ) 189 | { 190 | r=i[1]; 191 | rSide=i[2]; 192 | fnCorner=i[3]; 193 | up( (lz==undef )?0:((lz/2-abs(r))*i[0])) 194 | mirror([0,0,i[0]>0?0:1]) 195 | linear_extrude(height=abs(r) 196 | ,slices=1 197 | ,scale=[(lx+2*r)/lx,(ly+2*r)/ly]) 198 | resize(tube?0:[lx,ly]) 199 | offset(r = abs( tube?0:rSide ), $fn = fnCorner) 200 | { 201 | if((!box && !tube) || disable) 202 | projection(cut=false) 203 | { 204 | $fn=childFn; 205 | children(); 206 | } 207 | else if(box) square([lx,ly],center=true,$fn=childFn); 208 | else if(tube) circle(d=lx,$fn=childFn); 209 | } 210 | } 211 | //addOnly()children(); 212 | if(!disable) 213 | intersection() 214 | { 215 | for(i=[chamferInfo[0],chamferInfo[1]]) 216 | { 217 | r=i[1]; 218 | rSide=i[2]; 219 | fnCorner=i[3]; 220 | //echo(rSide); 221 | mirror([0,0,i[0]>0?0:1]) 222 | down(chamferInfo[1]==undef?lz/2:0.02) 223 | linear_extrude( 224 | height=(chamferInfo[1]==undef?lz:lz/2) 225 | -abs(r)+.05 226 | ,slices=1) 227 | resize(tube?0:[lx,ly]) 228 | offset(r=abs(tube?0:rSide),$fn=fnCorner) 229 | { 230 | if((!box && !tube) ) 231 | projection(cut=false) 232 | { 233 | $fn=childFn; 234 | children(); 235 | } 236 | else if(box) square([lx,ly],center=true,$fn=childFn); 237 | else if(tube) circle(d=lx,$fn=childFn); 238 | } 239 | } 240 | 241 | if(!box && !tube) 242 | { 243 | echo(box,tube); 244 | children(); 245 | } 246 | } 247 | else children(); 248 | 249 | } 250 | module doChamferTube(lx,ly,lz,chamferInfo=chamferInfo(),childFn=$fn,tubeData=tubeInfo()) 251 | { 252 | useTubeData = (!lx && !ly && !lz); 253 | lx= !useTubeData?lx:tubeData[2]; 254 | ly= !useTubeData?ly:tubeData[3]; 255 | lz= !useTubeData?lz:tubeData[4];//=h 256 | 257 | //echo("chamf:",chamferInfo); 258 | disable = chamferInfo[2]; 259 | 260 | if(!disable) 261 | for( i = [chamferInfo[0] , chamferInfo[1]] ) 262 | { 263 | r=i[1]; 264 | rSide=i[2]; 265 | fnCorner=i[3]; 266 | up( (lz==undef )?0:((lz/2-abs(r))*i[0])) 267 | mirror([0,0,i[0]>0?0:1]) 268 | linear_extrude(height=abs(r) 269 | ,slices=1 270 | ,scale=[(lx+2*r)/lx,(ly+2*r)/ly]) 271 | circle(d=lx,$fn=childFn); 272 | 273 | mirror([0,0,i[0]>0?0:1]) 274 | down(chamferInfo[1]==undef?lz/2:0.02) 275 | linear_extrude( 276 | height=(chamferInfo[1]==undef?lz:lz/2)-abs(r)+.05 277 | ,slices=1) 278 | circle(d=lx,$fn=childFn); 279 | } 280 | //addOnly()children(); 281 | else children(); 282 | 283 | } 284 | */ 285 | -------------------------------------------------------------------------------- /tutorials/basic-tutorial.md: -------------------------------------------------------------------------------- 1 | # Basic introduction to openscad with the Constructive library for a new OpenScad user 2 | 3 | -------------------- 4 | A **basic introduction** (especially if you are new to Openscad ) 5 | it explains Constructive Syntax for main Building blocks, like tube(), box() or bentStrip() and their placement and alignment in space like stack() , align(), X(),Y(),Z() or turnXZ() 6 | 7 | --- 8 | see also: 9 | [Part II tutorial](./tutorial-partII.md) shows some basic object modification like reflectX(), cScale() ,or colors and then goes on to explain, how to work with sets of similar objects without for(), with: pieces(), span(), vals(), selectPieces(), etc.. 10 | 11 | [Part III tutorial](./tutorial-partIII.md) shows more advanced Features like grouping commands into a g() group, working with Parts, and combining them into Assembly 12 | 13 | ---- 14 | 15 | The easiest way to try out the Library is to download the [kickstart.zip](https://github.com/solidboredom/constructive/blob/main/kickstart.zip) 16 | 17 | ---------------- 18 | 19 | > NOTE: To run all code examples from this tutorial you will need only Openscad and 20 | > a single file: `constructive-compiled.scad` put it in the same Folder as your own .scad files. (or into the OpenScad Library folder) 21 | > the easiest way to start is to download the [kickstart.zip](https://github.com/solidboredom/constructive/blob/main/kickstart.zip) 22 | > and then extract both files contained in it into same folder. Then you can open the tryExamples.scad from this folder with OpenScad, and then use this file to try the code Examples from the Tutorial, or anything else you like. Just Pressing F5 in Openscad to see the Results. 23 | 24 | --- 25 | 26 | ## Basic building blocks 27 | 28 | here are the basic buildng blocks box(),tube(),tubeShell(), and ball(),ballShell() 29 | they are very similar to OpenScad's native cube(),cylinder() and sphere() but have simpler, less mathematical argument syntax and additionally allow for powerful Constructive features introduced later . 30 | There are other more advanced blocks and features, such as bentStripXZ() or applying of chamfer(), which have no vanilla Openscad equivalent, the will be explained later 31 | #### Basic building block : box() 32 | 33 | ```.scad 34 | include 35 | 36 | box(side=10); 37 | ``` 38 | 39 | This will create a box, all box sides of side=10 mm and with its center around center of coordinates. 40 | Same as cube([10,10,10],center=true) in vanilla Openscad syntax. 41 | 42 | ![screen](./tutorial-images/box1.png) 43 | 44 | --- 45 | 46 | > NOTE: for people used to classic vanilla openscad: 47 | > unless specified differently by TOUP(),TORIGHT(),align(...), etc.(which are explained below) all box(), tube() and other "constructive" bodies will be centered around center of coordinates, like when using ",center=true)" parameter in vanilla Openscad. 48 | 49 | --- 50 | 51 | In constructive syntax you can also omit the parameter name for the 'side=' parameter. 52 | So: 53 | 54 | ```.scad 55 | include 56 | 57 | box(10); 58 | ``` 59 | 60 | Does exactly the same as box(side=10); 61 | 62 | --- 63 | 64 | ```.scad 65 | include 66 | 67 | box(10,h=15); 68 | ``` 69 | 70 | Just like above, a box with all sides of 10, but a height of h=15. 71 | Same as cube([10,10,15],center=true) in vanilla Openscad syntax. 72 | 73 | ![screen](./tutorial-images/box2.png) 74 | 75 | --- 76 | 77 | ```.scad 78 | include 79 | 80 | box(10,y=25,h=15); 81 | ``` 82 | 83 | Like above, a box with all sides of 10, but a height of h=15 and a depth of y=25. 84 | Same as cube([10,25,15],center=true) in vanilla Openscad syntax. 85 | 86 | ![screen](./tutorial-images/box3.png) 87 | --- 88 | 89 | ```.scad 90 | include 91 | 92 | box(10,x=35,h=15); 93 | ``` 94 | 95 | A cube with a side of 10 , but x=35 and h=15, so that only the remaining unset y will get the value of 10: y=10 96 | same as cube([35,15,15],center=true) in vanilla Openscad syntax. 97 | 98 | ![screen](./tutorial-images/box4.png) 99 | --- 100 | 101 | > NOTE: all the examples here will work in openscad's preview (F5-Key), but later you might need to render them correctly, when (pressing the F6-key) or exporting them as .stl, you will need 102 | > to add one more simple line to your code to achieve that. This line is called "a main() block". It is given and described at the very end of this tutorial 103 | 104 | --- 105 | #### Basic building block: ball(diameter) 106 | Creates a ball (a sphere() in vanilla OpenScad of given diameter ) it works just the same as sphere() 107 | but it also correctly reacts on align(), stack() and other Constructive concepts described below. Out of the box. So it is preferred to use ball(d) instead of sphere() 108 | 109 | ```.scad 110 | 111 | include 112 | ball(10); 113 | 114 | ``` 115 | ![screen](./tutorial-images/ball.png) 116 | 117 | --- 118 | #### Basic building block: ball(diameter) 119 | creates a hollow ball shell (a pin pong ball) (the space inside Ball is not a "Hard" cavity, it is not affecting/erasing other bodies inside it. 120 | but it also correctly reacts on align(), stack() and other Constructive concepts described below. out of the box. so it is preferred to use ball(d) instead of sphere() 121 | 122 | ```.scad 123 | 124 | include 125 | ballShell(10); 126 | 127 | ``` 128 | ![screen](./tutorial-images/ballShell.png) 129 | 130 | --- 131 | 132 | ballShell(d=heightInfo(),wall=wallInfo()) 133 | 134 | 135 | #### Basic building block: tube() 136 | 137 | ```.scad 138 | include 139 | 140 | tube(d=10,h=20,wall=2.5); 141 | ``` 142 | 143 | Creates a tube with outer Diameter of 10,a wall of 2.5 and height of 20mm 144 | ![screen](./tutorial-images/tube1.png) 145 | 146 | --- 147 | #### Basic building block: tubeShell() 148 | 149 | ```.scad 150 | // the code for this example needs some additional features like assemble() so it is documented in 151 | the Part 3 ofthis Tutorial 152 | ``` 153 | 154 | equivalent to a tube() , 155 | but the bore inside Ball is not a "Hard" cavity, it is not affecting/erasing other bodies inside it. 156 | 157 | ![screen](./partII-images/tubeShell.png) 158 | 159 | --- 160 | ```.scad 161 | include 162 | 163 | tube(dOuter=45,dInner=25,h=20); 164 | ``` 165 | 166 | Just like above, but instead of specifying just d and wall thickness it is possible to specify dInner and dOuter. 167 | 168 | ![screen](./tutorial-images/tube2.png) --- 169 | 170 | ```.scad 171 | include 172 | 173 | tube(d=10,h=20,solid=true); 174 | ``` 175 | 176 | Creates a solid rod of d=10 in diameter and height of h=20, the solid=true argument is one of the ways to make the rod solid,skipping the inner hole, which would turn it into a proper tube; 177 | in vanilla Openscad syntax this would be cylinder(d=10,h=20); 178 | ![screen](./tutorial-images/tube3.png) 179 | 180 | ---- 181 | ## Moving and turning the Object around (also called translation and transformation): 182 | --- 183 | #### moving: 184 | 185 | The first box with the side of (4 mm) is moved by 5mm to the right 186 | the second box with a 8mm side is moved by 13mm to the bottom since the negative Z(-13) 187 | 188 | ```.scad 189 | include 190 | 191 | X(5) box(4); 192 | Z(-13) box(8); 193 | ``` 194 | 195 | ![screen](./tutorial-images/move1.png) 196 | 197 | --- 198 | 199 | #### Applying several Movements to one Object: 200 | 201 | Here we have several boxes scattered around. The first and the biggest box of 15 mm sides is moved by 8mm to the right by X(8) then by 20mm to the front and by 30 mm up 202 | 203 | ```.scad 204 | include 205 | 206 | X(8) Y(-20) Z(-30) box(15); 207 | X(5) box(4); 208 | Z(13) box(5); 209 | Y(-24) box(3); 210 | ``` 211 | 212 | ![screen](./tutorial-images/move2.png) 213 | 214 | --- 215 | 216 | ## Turning Objects : 217 | 218 | ```.scad 219 | include 220 | 221 | turnXY(45) box(10); 222 | ``` 223 | 224 | This turns the box by 45 degrees in the horizontal(XY) plane 225 | 226 | ![screen](./tutorial-images/turn1.png) 227 | 228 | --- 229 | 230 | Turns are also possible around each of the other two axes: turnXY(), turnXZ() and turnYZ(), and you of course can apply several different turns to the same objects 231 | 232 | ```.scad 233 | include 234 | 235 | turnXZ(-30) turnXY(45) box(10); 236 | ``` 237 | 238 | ![screen](./tutorial-images/turn2.png) 239 | --- 240 | 241 | You can combine turns and moves as you wish: 242 | 243 | ```.scad 244 | include 245 | 246 | turnXZ(-30) X(5) Y(10) turnXY(45) Z(15) box(10); 247 | ``` 248 | 249 | ![screen](./tutorial-images/turn3.png) 250 | 251 | This will move the box up by 10 mm, turn it by 45 degrees in the horizontal(XY) plane, and the move it 10 in y axis and 5 to the right and then turn the whole thing around XZ axis by -30 degrees 252 | 253 | > NOTE: the sequence in which you apply turns and moves does matter. 254 | > 255 | > Because the 256 | > 257 | > turnXZ(30) X(20) box(10); 258 | > 259 | > first moves the box by 20 mm and then rotates the whole arrangement around the center of coordinates, whereas the 260 | > X(20) turnXZ(30) box(10); 261 | > 262 | > first rotates the box around its own axis and then moves it to the right by 20 mm 263 | > you can try it by yourself , just not forget the 264 | > 265 | > include 266 | > 267 | > at the beginning 268 | 269 | --------------- 270 | ---- 271 | ### Advanced body Positioning: aligning a body relative to its coordinates: 272 | 273 | Instead of moving the body around by using X() Y() or Z() it is very often handier to just specify that it needs to be aligned so that, 274 | its specific corner or side touches a corner or side of another body, and let the Constructive do the moving. (This is called "creating a constraint" in a traditional CAD) 275 | here is an example: 276 | 277 | ```.scad 278 | include 279 | 280 | TOUP()box(10); 281 | TODOWN()tube(d=10,h=10,solid=true); 282 | ``` 283 | 284 | The alignment moves the body around,so that not its center, but one of it's corners, or a center of a chosen side of the body is place in the coordinates, where it is drawn. 285 | Here the box() is aligned UP, for the complete body to be drown the top side of the coordinates center,just touching it with its bottom sides center, 286 | and the tube is aligned Down to be to the Bottom of coordinates center, just touching it with its top sides center 287 | 288 | ![screen](./tutorial-images/align1.png) 289 | 290 | --- 291 | 292 | There are alignment commands for each dimension, not only TOUP() and TODOWN() but also TORIGHT() TOLEFT() for the X and TOREAR() and TOFRONT() for the Y axis 293 | 294 | ```.scad 295 | include 296 | 297 | TORIGHT() box(10); 298 | TOLEFT() box(5,y=30); 299 | ``` 300 | 301 | ![screen](./tutorial-images/align2.png) 302 | 303 | --- 304 | 305 | You can combine them like here 306 | 307 | ```.scad 308 | include 309 | 310 | TORIGHT() TOREAR() TOUP() box(5); 311 | TODOWN() tube(d=20,h=20,wall=3); 312 | ``` 313 | 314 | ![screen](./tutorial-images/align3.png) 315 | 316 | The TORIGHT() TOREAR() TOUP() alignment is the default alignment for cube in vanilla Openscad, so that its cube([5,5,5]); will achieve the same as 317 | TORIGHT() TOREAR() TOUP() box(5); in constructive dialect. 318 | Why use the longer version? 319 | Because it is possible to assign alignment over one axis to a block of several bodies,and then specify alignment on another axis to a specific body, like here: 320 | 321 | ```.scad 322 | include 323 | TOUP() 324 | { 325 | TOLEFT()box(20); 326 | TORIGHT()box(10); 327 | } 328 | 329 | TODOWN()TOFRONT()tube(d=20,h=10,wall=2); 330 | ``` 331 | 332 | ![screen](./tutorial-images/align4.png) 333 | --- 334 | 335 | NOTE that it is possible and in some cases needed to combine several following align statements like TOUP() TORIGHT() into a shorter single align(...) command: 336 | align(TORIGHT, TOREAR, TOUP) box(5); does the same as the TORIGHT() TOREAR() TOUP() box(5); from above. 337 | In fact, TOUP() is only as short for align(TOUP) and TORIGHT() for align (TORIGHT), etc. 338 | 339 | --- 340 | 341 | #### Stacking Bodies on top or to side 342 | 343 | When you have similar bodies stacked on top each other, or to a side, than in vanilla openscad you would have to move each body to the exact position it needs to be, not so with Constructive. 344 | You just stack Bodies like that, just set the direction of Stacking: 345 | 346 | > IMPORTANT: please pay attention there are NO SEMICOLONS between stacked parts. 347 | > his looks very unusual, but this is essential for stack() to work. If stack is not working properly, usually there is a semicolon between stacked bodies somewhere: 348 | 349 | ```.scad 350 | include 351 | TOUP() stack(TOUP) 352 | box(20) 353 | box(15) 354 | box(10,h=30) 355 | tube(d=10,wall=2,h=20) 356 | tube(d=5,h=10,solid=true) 357 | turnXY(45)box(5); 358 | ``` 359 | 360 | ![screen](./tutorial-images/stack1.png) 361 | 362 | > note please do not forget the TOUP() alignment if you are using the stack(TOUP) this will align each individual body you are stacking in the same direction with the Stacking, only the then the Bodies are Stacked properly on top of each other. 363 | 364 | --- 365 | ---- 366 | ## advanced building blocks 367 | --- 368 | 369 | #### advanced building block: chamfer() : tube() 370 | 371 | You can chamfer the tubes up and bottom sides just like the we did to the box(), or even add skirts. 372 | 373 | ```.scad 374 | include 375 | 376 | chamfer(4,-2) tube(d=10,h=20,wall=2.5); 377 | ``` 378 | 379 | Chamfering with the chamfer() is only possible on the outer surface of the tube for now. 380 | To chamfer the hole inside a tube in the current version of Constructive, it needs a little trick, we will return to the trick in another tutorial. 381 | 382 | ![screen](./tutorial-images/tube1c.png) 383 | 384 | 385 | #### advanced building block: chamfer() : box() 386 | 387 | You can easily chamfer the sides of a box() , just use chamfer(down,up,side) to set by how many millimeters the bottom,top, or side edges of the cube need to be cut, use negative numbers to indicate we want to remove material (and not to add a skirt) 388 | 389 | ```.scad 390 | include 391 | 392 | chamfer(-1,-2,-3)box(10,x=35,h=15); 393 | ``` 394 | 395 | ![screen](./tutorial-images/box5.png) 396 | 397 | --- 398 | 399 | If you want smoother rounded vertical edges set fnCorner parameter to a higher value than its default fnCorner=7 400 | 401 | ```.scad 402 | include 403 | 404 | chamfer(-1,-2,-3,fnCorner=60) box(10,x=35,h=15); 405 | ``` 406 | 407 | ![screen](./tutorial-images/box6.png) 408 | 409 | --- 410 | 411 | Or make them straight with fncorner=2 412 | 413 | ```.scad 414 | include 415 | 416 | chamfer(-1,-2,-3,fnCorner=2) box(10,x=35,h=15); 417 | ``` 418 | 419 | Or anything in between with value you choose 420 | 421 | ![screen](./tutorial-images/box7.png) 422 | --- 423 | 424 | Or to chamfer only the sides the top, but not bottom, or otherwise: 425 | 426 | ```.scad 427 | include 428 | 429 | chamfer(0,-2,-3) box(10,x=35,h=15); 430 | ``` 431 | 432 | Or anything in between with value you choose 433 | 434 | ![screen](./tutorial-images/box8.png) 435 | 436 | --- 437 | 438 | ####Skirt 439 | You can also create a skirt at the top or bottom using chamfer() but with a positive parameter for according side 440 | 441 | ```.scad 442 | include 443 | 444 | chamfer(2,-2) box(10,x=35,h=15); 445 | ``` 446 | 447 | ![screen](./tutorial-images/box9.png) 448 | 449 | --- 450 | 451 | #### advanced building block: bentStripXZ() 452 | 453 | ```.scad 454 | include 455 | 456 | bentStripXZ(places=[ X(20),turnXZ(60),X(20),turnXZ(-45),X(10) ], 457 |              y=5, thick=10); 458 | ``` 459 | 460 | This will create a 3D strip by moving a cylindrical base element of height y=5 according to command-list in its first argument places=[ ... ] 461 | 462 | ![screen](./partII-images/bentStripXZ.png) 463 | 464 | > NOTE: ONLY TURNS/MOVES in XZ plane are allowed inside places parameter 465 | > no alignment commands, like TOUP() or TOLEFT() are allowed in current version. 466 | 467 | #### Advanced stacking Example 468 | 469 | Here is a very similar Example, but we stack horizontally, and also apply chamfer() to remove 2 mm form the top and bottom sides edges, just to make them look better: 470 | 471 | ```.scad 472 | include 473 | chamfer(-2,-2)TORIGHT()stack(TORIGHT) 474 | box(20) 475 | box(15) 476 | box(10,h=30) 477 | tube(d=10,wall=2,h=20) 478 | tube(d=5,h=10,solid=true) 479 | turnXY(45)box(5); 480 | ``` 481 | 482 | ![screen](./tutorial-images/stack2.png) 483 | 484 | > note please do not forget the TOUP() alignment if you are using the stack(TOUP) this will align each individual body you are stacking in the same direction with the Stacking, only the then the Bodies are Stacked properly on top of each other. 485 | 486 | --------------------- 487 | 488 | ## Main() block: 489 | 490 | To be able to render the above examples with F6 or export their .stl you will need to wrap your code with the so called main () block statement. 491 | You just do it by adding the assemble() add() line at the top of the code, just after the include line : 492 | 493 | ``` 494 | include 495 | 496 | assemble() add() 497 | ``` 498 | 499 | And then wrapping the rest of your own code as code block, using curly braces: 500 | 501 | ``` 502 | include 503 | 504 | assemble() add() 505 | { 506 | .... your code here..... 507 | } 508 | ``` 509 | 510 | So for example, a program: 511 | 512 | ``` 513 | include 514 | 515 | chamfer(-2,-2)TORIGHT()stack(TORIGHT) 516 | box(20) 517 | box(15) 518 | box(10,h=30) 519 | tube(d=10,wall=2,h=20) 520 | tube(d=5,h=10,solid=true) 521 | turnXY(45)box(5); 522 | 523 | TODOWN()TOREAR()tube(d=20,h=10,wall=2); 524 | ``` 525 | 526 | Will become: 527 | 528 | ``` 529 | include 530 | 531 | assemble() add() 532 | { 533 | chamfer(-2,-2)TORIGHT()stack(TORIGHT) 534 | box(20) 535 | box(15) 536 | box(10,h=30) 537 | tube(d=10,wall=2,h=20) 538 | tube(d=5,h=10,solid=true) 539 | turnXY(45)box(5); 540 | 541 | TODOWN()TOREAR()tube(d=20,h=10,wall=2); 542 | } 543 | ``` 544 | 545 | The result looks just the same like this and renders well with (F6-Key) as well as F5 546 | ![screen](./tutorial-images/mainblock.png) 547 | 548 | --- 549 | 550 | See also: 551 | 552 | [Part II tutorial](./tutorial-partII.md) shows some basic object modification like reflectX(), cScale() ,or colors and then goes on to explain, how to work with sets of similar objects without for(), with: pieces(), span(), vals(), selectPieces(), etc.. 553 | 554 | [Part III tutorial](./tutorial-partIII.md) shows more advanced Features like grouping commands into a g() group, working with Parts, and combining them into Assembly 555 | 556 | For a more advanced use also look at the explanations inside the example below 557 | 558 | https://github.com/solidboredom/constructive/blob/main/examples/mount-demo.scad 559 | 560 | There is also another Example at: 561 | 562 | https://github.com/solidboredom/constructive/blob/main/examples/pulley-demo.scad 563 | 564 | 565 | The easiest way to try out the Library is to download the [kickstart.zip](https://github.com/solidboredom/constructive/blob/main/kickstart.zip) 566 | 567 | 568 | That was it! Now you can render and export STLs 569 | -------------------------------------------------------------------------------- /tutorials/partII-images/assemble1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/assemble1.png -------------------------------------------------------------------------------- /tutorials/partII-images/assemble2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/assemble2.png -------------------------------------------------------------------------------- /tutorials/partII-images/bentStripXZ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/bentStripXZ.png -------------------------------------------------------------------------------- /tutorials/partII-images/cscale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/cscale.png -------------------------------------------------------------------------------- /tutorials/partII-images/every1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/every1.png -------------------------------------------------------------------------------- /tutorials/partII-images/every2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/every2.png -------------------------------------------------------------------------------- /tutorials/partII-images/g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/g.png -------------------------------------------------------------------------------- /tutorials/partII-images/ifFirst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/ifFirst.png -------------------------------------------------------------------------------- /tutorials/partII-images/ifLast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/ifLast.png -------------------------------------------------------------------------------- /tutorials/partII-images/margin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/margin.png -------------------------------------------------------------------------------- /tutorials/partII-images/pieces_3_span.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/pieces_3_span.png -------------------------------------------------------------------------------- /tutorials/partII-images/pieces_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/pieces_5.png -------------------------------------------------------------------------------- /tutorials/partII-images/pieces_5_span_X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/pieces_5_span_X.png -------------------------------------------------------------------------------- /tutorials/partII-images/reflectZ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/reflectZ.png -------------------------------------------------------------------------------- /tutorials/partII-images/removing_var.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/removing_var.png -------------------------------------------------------------------------------- /tutorials/partII-images/runFor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/runFor.png -------------------------------------------------------------------------------- /tutorials/partII-images/selectPieces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/selectPieces.png -------------------------------------------------------------------------------- /tutorials/partII-images/sides1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/sides1.png -------------------------------------------------------------------------------- /tutorials/partII-images/sides2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/sides2.png -------------------------------------------------------------------------------- /tutorials/partII-images/sidesReflectX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/sidesReflectX.png -------------------------------------------------------------------------------- /tutorials/partII-images/skin-toup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/skin-toup.png -------------------------------------------------------------------------------- /tutorials/partII-images/skin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/skin.png -------------------------------------------------------------------------------- /tutorials/partII-images/skipFirst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/skipFirst.png -------------------------------------------------------------------------------- /tutorials/partII-images/spanAllBalls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/spanAllBalls.png -------------------------------------------------------------------------------- /tutorials/partII-images/spanAllButLastBall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/spanAllButLastBall.png -------------------------------------------------------------------------------- /tutorials/partII-images/spanButLast_3_boxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/spanButLast_3_boxes.png -------------------------------------------------------------------------------- /tutorials/partII-images/span_3_boxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/span_3_boxes.png -------------------------------------------------------------------------------- /tutorials/partII-images/span_5_boxes_100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/span_5_boxes_100.png -------------------------------------------------------------------------------- /tutorials/partII-images/span_8_rotated_boxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/span_8_rotated_boxes.png -------------------------------------------------------------------------------- /tutorials/partII-images/tubeShell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/tubeShell.png -------------------------------------------------------------------------------- /tutorials/partII-images/vRepeat1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/vRepeat1.png -------------------------------------------------------------------------------- /tutorials/partII-images/vSpread1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/vSpread1.png -------------------------------------------------------------------------------- /tutorials/partII-images/valPtr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/valPtr.png -------------------------------------------------------------------------------- /tutorials/partII-images/vals2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/vals2.png -------------------------------------------------------------------------------- /tutorials/partII-images/vals3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/partII-images/vals3.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/align1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/align1.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/align2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/align2.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/align3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/align3.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/align4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/align4.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/ball.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/box1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/box1.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/box2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/box2.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/box3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/box3.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/box4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/box4.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/box5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/box5.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/box6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/box6.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/box7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/box7.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/box8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/box8.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/box9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/box9.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/mainblock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/mainblock.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/move1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/move1.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/move2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/move2.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/stack1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/stack1.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/stack2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/stack2.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/tube1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/tube1.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/tube1c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/tube1c.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/tube2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/tube2.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/tube3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/tube3.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/turn1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/turn1.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/turn2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/turn2.png -------------------------------------------------------------------------------- /tutorials/tutorial-images/turn3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidboredom/constructive/35732620d8aa124ba0a0b49e0595370dc2dc8a40/tutorials/tutorial-images/turn3.png -------------------------------------------------------------------------------- /tutorials/tutorial-partII.md: -------------------------------------------------------------------------------- 1 | # introduction to Openscad with the Constructive library for a new openscad user 2 | 3 | ### PART II 4 | 5 | 6 | [Part II tutorial](./tutorial-partII.md) shows some basic object modification like reflectX(), cScale() or colors and then goes on to explain how to work with sets of similar objects without for(), with: pieces(), span(), vals(), selectPieces(), etc.. 7 | 8 | -------------------- 9 | 10 | If you are unsure about particular basic commands used in the codes snippets below, please refer to the [basic tutorial](./basic-tutorial.md). 11 | 12 | [Part III tutorial](./tutorial-partIII.md) shows more advanced Features like grouping commands into a g() group, working with Parts, and combining them into Assembly 13 | 14 | For a more advanced use also look at the explanations inside the example below 15 | 16 | https://github.com/solidboredom/constructive/blob/main/examples/mount-demo.scad 17 | 18 | There is also another Example at: 19 | 20 | https://github.com/solidboredom/constructive/blob/main/examples/pulley-demo.scad 21 | 22 | ------------------- 23 | The easiest way to try out the Library is to download the [kickstart.zip](https://github.com/solidboredom/constructive/blob/main/kickstart.zip) 24 | 25 | 26 | > NOTE: To run all code examples from this tutorial you will need only Openscad and 27 | > a single file: `constructive-compiled.scad` put it in the same Folder as your own .scad files. (or into the OpenScad Library folder) 28 | > the easiest way to start is to download the [kickstart.zip](https://github.com/solidboredom/constructive/blob/main/kickstart.zip) 29 | > and then extract both files contained in it into same folder. Then you can open the tryExamples.scad from this folder with OpenScad, and then use this file to try the code Examples from the Tutorial, or anything else you like. Just Pressing F5 in Openscad to see the Results. 30 | 31 | --- 32 | 33 | ### body Colors 34 | #### opaq(color),clear(color) 35 | makes the Block of certain color and transparency 36 | you can use opaq(color) as a short to native openscads color(color), and clear(color) as a short for color(color,.4) 37 | a dozen of basic colors like red,green or silver 38 | are defined as constants (see the sources/globals.scad) 39 | so these color names can also be used without quotes 40 | example: 41 | 42 | ```.scad 43 | opaq(red) 44 | 45 | clear(blue) 46 | ``` 47 | 48 | Other web colors will still need the quotes 49 | Example: `opaq("lightblue")` 50 | >NOTE: there is also an autocolor functionality for Parts introduced late in this tutorial after the parts() concept is introduced 51 | 52 | ### body transformations 53 | 54 | #### reflectX(),reflectY(),reflectZ() 55 | 56 | Put reflectX(), reflectX() or reflectZ() in front of a block to reflect it along the specific axis. 57 | Example: 58 | 59 | ```.scad 60 | include 61 | 62 | 63 | reflectZ() TOUP() 64 | { 65 | box(10,h=3); 66 | Z(3) ball(5); 67 | } 68 | //----------- 69 | //and this Part of the example is not using reflectZ() 70 | #TOUP() 71 | { 72 | box(10,h=3); 73 | Z(3) ball(5); 74 | } 75 | ``` 76 | ![screen](./partII-images/reflectZ.png) 77 | 78 | --- 79 | 80 | ### cscale(x,y,z) 81 | to resize a body or a block by a Factor 82 | put cscale() in front of it. 83 | 84 | ```.scad 85 | include 86 | 87 | cscale(x=2)ball(20); 88 | Y(-30) cscale(y=2,z=.5) ball(20); 89 | ``` 90 | cscale(x=2) doubles the size along the x axis, cscale(y=2,z=.5) doubles the y and halves z 91 | 92 | ![screen](./partII-images/cscale.png) 93 | 94 | 95 | >NOTE: when cscale(x,y,z) is used inside a g() group function its synonymous scale(x,y,z) can also be used instead 96 | --- 97 | 98 | ### Body duplication, "life without for()" 99 | 100 | To create several similar bodies or say a sequence of holes in one object, 101 | usually do not need to use for() and index variables, like in vanilla OpenScad. The command you use instead are less general, allowing to express the intent of what you trying to achieve in less code shorter and make it easier to understand: 102 | 103 | ### pieces(n) and every(distance) 104 | 105 | When you need to create n similar bodies, instead of using vanilla openscad's for() you would use 106 | pieces(n), so lets say we want to create 7 boxes (with side 10),and moving each up by 20 mm, 107 | very short simple,no need to use variables: 108 | 109 | ```.scad 110 | include 111 | 112 | pieces(7) X(every(20)) box(10); 113 | ``` 114 | 115 | ![screen](./partII-images/every1.png) 116 | ---- 117 | 118 | We can also make boxes all different, say increasing their height by 5 each 119 | 120 | ```.scad 121 | include 122 | pieces(7) X(every(20)) box(10,h=10+every(5)); 123 | ``` 124 | 125 | ![screen](./partII-images/every2.png) 126 | 127 | --- 128 | 129 | #### sides(distance) 130 | 131 | Sides is useful to create two symmetrically placed or mirrored bodies. 132 | Because "there are always two sides" sides() need to always be preceded by pieces(2) or by _two()_ which is a short for _pieces(2)_ 133 | 134 | `sides()` will always return -1 for the fist piece and 1 for the second piece. 135 | If you use it with an argument like: sides(arg) it will return `-arg` for the first piece and `arg` for the second: 136 | 137 | ```.scad 138 | include 139 | 140 | two() X(sides(15)) ball(10); 141 | ``` 142 | 143 | ![screen](./partII-images/sides1.png) 144 | ---- 145 | 146 | ```.scad 147 | include 148 | 149 | two() X(sides(15)) turnXZ(-sides(30)) box(10); 150 | ``` 151 | 152 | ![screen](./partII-images/sides2.png) 153 | --- 154 | 155 | Or you can use relflectX() to achieve the same result, mirroring the right and left Parts 156 | 157 | ```.scad 158 | include 159 | 160 | two() reflectX(sides()) X(15) turnXZ(-30) box(10); 161 | ``` 162 | 163 | So it produces the same result as above 164 | ![screen](./partII-images/sidesReflectX.png) 165 | 166 | --- 167 | 168 | #### span(range) and pieces(n) 169 | 170 | Sometimes you do not want to figure what would be the distance between neighbours you need to pass to every(distance) to say span 650 mm by 8 pieces, then it is handier to use span(range) than every(distance). 171 | 172 | span(_range_) preceded by pieces(n) can (among other uses) be used to fill a range with a repetitive body. 173 | 174 | Used inside of a pieces(_n_) block to automatically calculate values for each call, so that the _n_ values will evenly cover the whole _range_. It is like using a for(), but the step is determined automatically along lines of step = range/pieces 175 | 176 | For example: 177 | 178 | ```.scad 179 | include 180 | 181 | pieces(5) X(span(100)) box(10); 182 | ``` 183 | 184 | Will fill a range of 100mm with box of side 10 185 | ![screen](./partII-images/span_5_boxes_100.png) 186 | 187 | > NOTE: 100mm is measured between centers of the boxes, the whole space taken up will be (range + boxsize) which is in our case 110 mm 188 | 189 | ```.scad 190 | include 191 | 192 | pieces(8) X(span(100)) turnYZ(span(90)) box(10); 193 | ``` 194 | 195 | Will create 8 boxes with their centers filling a range of 100 mm and each one turned in YZ axis by an appropriate angle picked from the [0:90] range 196 | ![screen](./partII-images/span_8_rotated_boxes.png) 197 | 198 | > NOTE: pieces(_n_) span(_range_) will put a body or run _n_ times to span all the _range_. including the upper range limit. For example the pieces(3) turnXY(span(180)) X(20) box(10); above, will put a box each at 0°,90°a and 180° degrees, which is what you most probably want. 199 | > But if you want to have full circle (360°) range or run it from an outer for() you will need to use 200 | 201 | #### spanAllButLast(range) vs span(range) differences 202 | 203 | Instead of span(range) 204 | it works just the same like span() but does not put an element at the very end of the range, placing all elements closer together to leave a space at the end for just another element. You might use it when you use pieces(n) to span full closed circle(360°), For Example: 205 | 206 | ```.scad 207 | include 208 | 209 | pieces(3) turnXY(span(180)) X(20) box(10); 210 | ``` 211 | 212 | Will create 3 boxes at 0, 90 and 180 degrees, which is just fine 213 | ![screen](./partII-images/pieces_3_span.png) 214 | 215 | ,but the: 216 | 217 | ```.scad 218 | include 219 | 220 | pieces(3) turnXY(span(360)) X(20) box(10); 221 | ``` 222 | 223 | Will create 3 boxes, each one at at 0°, 180° and 360° degrees, the 0° and 360° is the same Angle, so the first and the last box are drawn at the same position, so there will be only two discernable boxes visible, which is most probably not what you want. 224 | ![screen](./partII-images/span_3_boxes.png) 225 | 226 | What you probably wanted in this case is to put boxes at positions 0°, 120°, 240° so there are thee boxes visible, and the first hand last one are not drawn at the same position like above. 227 | 228 | ```.scad 229 | include 230 | 231 | pieces(3) turnXY(spanAllButLast(360)) X(20) box(10); 232 | ``` 233 | 234 | Will do just exactly that, so use it when you want to span 360°, 235 | ![screen](./partII-images/spanButLast_3_boxes.png) 236 | 237 | To further illustrate the difference, look at the following example spanning 100 mm (between centers) : 238 | 239 | ```.scad 240 | include 241 | 242 | pieces(5) X(span(100)) ball(10); 243 | ``` 244 | 245 | ![screen](./partII-images/spanAllBalls.png) 246 | 247 | And the following, placing the balls closer to each other, to leave a space for a just one more ball at the end. This one will be useful when using pieces(n) ...span(range). Within another for() "cycle". So that the first element in the next for() iteration could take the space left for the last one 248 | 249 | ```.scad 250 | include 251 | 252 | pieces(5) X(spanAllButLast(100)) ball(10); 253 | ``` 254 | 255 | Will produce: 256 | 257 | ![screen](./partII-images/spanAllButLastBall.png) 258 | 259 | ---- 260 | 261 | > NOTE: that 262 | 263 | ```.scad 264 | spanAllButLast(range) 265 | ``` 266 | 267 | Is same as 268 | 269 | ``` 270 | span(n,allButLast=true) 271 | ``` 272 | 273 | ---- 274 | 275 | #### specific value for each piece: vals(val1,val2,val3,...) 276 | 277 | ```.scad 278 | include 279 | 280 | pieces(6) X(span(120)) Z(vals(10,20,40,15,25,35)) ball(20); 281 | ``` 282 | 283 | This will pick one value from the list of arguments of vals(10,20,40,15,25,35) for each peace, 284 | of course you will need to provide enough values, so each peace gets one. 285 | ![screen](./partII-images/vals2.png) 286 | 287 | --- 288 | 289 | Wrapping the vals() function grouping command g() (explained later) will also allow you to use movement or turning commands themselves as single values in the value list: 290 | 291 | ```.scad 292 | include 293 | 294 | pieces(3) g(vals(X(20),X(40)*Y(20),turnXY(45)*X(100))) 295 | color(vals(red,green,blue)) 296 | ball(15); 297 | ``` 298 | 299 | ![screen](./partII-images/vals3.png) 300 | 301 | > NOTE: the asterisk '*' is used to connect several different commands into a single value,which will be used for one piece of the pieces(3). Behind the scenes, movement or turning commands are represented as matrices so multipling arbitrarary number of such command to another ones will result in a new matrix, which represents a command containing all the transformations from each command multiplied 302 | 303 | ### vRepeat(val1,val2,...) 304 | 305 | When using the vals() function you need to provide a value for each piece created. 306 | vRepeat works almost the same, but if you have more pieces than values, like say: pieces(15) and only 4 values (10,20,30,40) the vRepeat will use all (in our case 4 ) values given and then start over, applying the values as a repeating pattern: 307 | 308 | ```.scad 309 | include 310 | 311 | pieces(15) X(span(200)) 312 | Y(vRepeat(0,10,30,70)) 313 | color(vRepeat(red,green,blue,cyan)) 314 | ball(15); 315 | ``` 316 | 317 | ![screen](./partII-images/vRepeat1.png) 318 | 319 | --- 320 | 321 | ### vSpread(val1,val2,...) 322 | 323 | Works similar to vRepeat, but instead of repeating the Pattern, tries to evenly Spread the Pattern among all Elements, repeating not the whole Sequence, but every element instead, "Spreading its value among several neighbouring Pieces 324 | 325 | ```.scad 326 | include 327 | 328 | pieces(15) X(span(200)) 329 | Y(vSpread(0,10,30,70)) 330 | color(vSpread(red,green,blue,cyan)) 331 | ball(15); 332 | ``` 333 | 334 | ![screen](./partII-images/vSpread1.png) 335 | 336 | ---- 337 | 338 | #### skipFirst(n=1) 339 | 340 | Omits a command or block for the first n pieces, 341 | consider following example: 342 | 343 | ```.scad 344 | include 345 | 346 | pieces(15) X(span(200)) 347 | { 348 | TOUP()ball(10); 349 | 350 | skipFirst(10) 351 | box(10,h=2); 352 | } 353 | ``` 354 | 355 | ![screen](./partII-images/skipFirst.png) 356 | 357 | As you can see the box() command is only run starting with 11th piece 358 | 359 | ---- 360 | 361 | #### ifFirst(n=1) 362 | 363 | Runs a command or block only for the first n pieces, 364 | consider following example: 365 | 366 | ```.scad 367 | include 368 | 369 | pieces(15) X(span(200)) 370 | { 371 | TOUP()ball(10); 372 | 373 | ifFirst(5) 374 | box(10,h=2); 375 | } 376 | ``` 377 | 378 | ![screen](./partII-images/ifFirst.png) 379 | 380 | ---- 381 | 382 | #### ifLast(n=1) 383 | 384 | Runs a command or block only for the last n pieces, 385 | consider following example: 386 | 387 | ```.scad 388 | include 389 | 390 | pieces(15) X(span(200)) 391 | { 392 | TOUP()ball(10); 393 | 394 | ifLast(2) 395 | box(10,h=2); 396 | } 397 | ``` 398 | 399 | ![screen](./partII-images/ifLast.png) 400 | 401 | ---- 402 | 403 | #### selectPieces( decisionList =[...]) 404 | 405 | selectPieces decides for each piece to run a block on not. 406 | It runs a block for piece number n if the according boolean value in the decisionList[n] is true: 407 | 408 | ```.scad 409 | include 410 | 411 | pieces(5) X(span(200)) 412 | { 413 | TOUP()ball(10); 414 | 415 | selectPieces([true,false,false,true]) 416 | box(10,h=2); 417 | } 418 | ``` 419 | 420 | ![screen](./partII-images/selectPieces.png) 421 | 422 | ---- 423 | 424 | ### runFor(conditionList=[true]) vs pieces(n) 425 | 426 | runFor(conditionList=[...]) 427 | works like pieces(n) but lets you select particular piece numbers to create, where pieces(n) calls its children for every number [0...n-1], runFor(conditionList=[...]) takes a list of n bool values as a parameter. 428 | Is a short for writing 429 | 430 | ```.scad 431 | pieces(n) selectPieces(conditionList=[...]) 432 | ``` 433 | 434 | The n argument for the pieces(n) is autodetermined counting the elements in the conditionList 435 | 436 | ```.scad 437 | include 438 | 439 | runFor([true,false,false,true,false,true,true]) 440 | X(span(100)) 441 | { 442 | TOUP()ball(10); 443 | box(10,h=2); 444 | } 445 | ``` 446 | 447 | ![screen](./partII-images/runFor.png) 448 | --- 449 | 450 | #### \$valPtr 451 | 452 | \$valPtr is a Constractive system variable, which can tell you the number of the current piece in the pieces(n) body. 453 | Using it allows you to implement your own logic on how to filter or change elements, in cases vals(), sides(), selectPieces() are to cumbersome to express the conditions intent. 454 | 455 | > in fact, behind the scenes pieces(n) is translated into vanilla OpenScad along lines of for($varPtr=[0:1:n]) 456 | 457 | ```.scad 458 | include 459 | 460 | pieces(10) 461 | X(span(100)) 462 | { 463 | if($valPtr==3) 464 | ball(10); 465 | else if($valPtr< 3 || $valPtr >4) 466 | box(10,h=$valPtr*4+1); 467 | } 468 | ``` 469 | 470 | ![screen](./partII-images/valPtr.png) 471 | --- 472 | For a **basic introduction**** (specially if you are new to Openscad) 473 | see the [beginners tutorial](./basic-tutorial.md) it explains Constructive Syntax for main Building blocks, like tube(), box() or bentStrip() and their placement and alignment in space like stack() , align(), X(),Y(),Z() or turnXZ() 474 | 475 | [Part II tutorial](./tutorial-partII.md) shows some basic object modification like reflectX(), cScale() or colors and then goes on to explain, how to work with sets of similar objects without for(), with: pieces(), span(), vals(), selectPieces(), etc.. 476 | 477 | [Part III tutorial](./tutorial-partIII.md) shows more advanced Features like grouping commands into a g() group, working with Parts, and combining them into Assembly 478 | -------------------------------------------------------------------------------- /tutorials/tutorial-partIII.md: -------------------------------------------------------------------------------- 1 | # Introduction to Openscad with the Constructive library for a new openscad user 2 | 3 | ### PART III 4 | 5 | NOTE: THIS PART OF THE TUTORIAL IS STILL UNDER CONSTRUCTION. 6 | THE FUNCTIONALITY IS IN THE LIBRARY IS READY AND BEING USED BUT THE TUTORIAL IS NOT COMPETE. 7 | The Source code in 8 | https://github.com/solidboredom/constructive/blob/main/examples/mount-demo.scad 9 | and 10 | https://github.com/solidboredom/constructive/blob/main/examples/pulley-demo.scad 11 | might give some idea on parts missing here. 12 | 13 | [Part III tutorial](./tutorial-partIII.md) shows advanced Features like grouping commands into a g() group, working with Parts, and combining them into Assembly 14 | -------------------- 15 | 16 | If you are unsure about particular basic commands used in the codes snippets below, please refer to the [basic tutorial](./basic-tutorial.md). 17 | 18 | See also: 19 | 20 | [Part II tutorial](./tutorial-partII.md) shows some basic object modification like reflectX(), cScale(), or colors and then goes on to explain, how to work with sets of similar objects without for(), with: pieces(), span(), vals(), selectPieces(), etc.. 21 | 22 | For a more advanced use also look at the explanations inside the example below 23 | 24 | https://github.com/solidboredom/constructive/blob/main/examples/mount-demo.scad 25 | 26 | There is also another Example at: 27 | 28 | https://github.com/solidboredom/constructive/blob/main/examples/pulley-demo.scad 29 | 30 | > Note: Here A Gallery where some shiny constructive examples will be added, 31 | > to show what can be achieved 32 | 33 | https://github.com/solidboredom/constructive/blob/main/gallery/ 34 | 35 | > Note: see here the source code of the Gallery pieces here: 36 | > https://github.com/solidboredom/constructive/blob/main/gallery/sources/ 37 | 38 | Now little code is actually needed for this, 39 | the code is in part not commented nor cleaned up, but it still can be used for reference 40 | 41 | ------------------- 42 | 43 | The easiest way to try out the Library is to download the [kickstart.zip](https://github.com/solidboredom/constructive/blob/main/kickstart.zip) 44 | 45 | > NOTE: To run all code examples from this tutorial you will need only Openscad and 46 | > a single file: `constructive-compiled.scad` put it in the same Folder as your own .scad files. (or into the OpenScad Library folder) 47 | > the easiest way to start is to download the [kickstart.zip](https://github.com/solidboredom/constructive/blob/main/kickstart.zip) 48 | > and then extract both files contained in it into same folder. Then you can open the tryExamples.scad from this folder with OpenScad, and then use this file to try the code Examples from the Tutorial, or anything else you like. Just Pressing F5 in Openscad to see the Results. 49 | 50 | ### g() groups several commands and parameters into single command, 51 | 52 | Most of Constructive commands can be used inside and outside of g() providing exactly the same result, So in most cases usage of g() is not essential but is preferred. It generally reduces model compilation time by Openscad and allows you to specify additional default values like height(x) and solid() for a code block, 53 | for example: 54 | 55 | ```.scad 56 | include 57 | 58 | two() 59 | g( reflectZ(sides()), Z(10) ,TOUP() 60 | , height(7), solid(true) ) 61 | { 62 | tube(d=20); 63 | g(turnXY(45), X(60)) box(20); 64 | } 65 | ``` 66 | 67 | ![screen](./partII-images/g.png) 68 | 69 | #### height(h) and solid(true/false) 70 | 71 | Specify the default value for Constructive primitive tube() and box(). (solid() only affects tube(), tubFast(), and tubeShell() not the box()). So inside of the g() block above, the h and solid arguments can be omitted when using box() or tube(). 72 | 73 | ------------------------------------------------ 74 | 75 | ## assembling mechanical Parts from several Modules 76 | 77 | #### assemble() 78 | 79 | Allows application of universal operations like adding and removing solids (i.e. boring holes or adding screws), only to a Part of the Model representing a specific mechanical (sub-Part). 80 | 81 | To start with, surrounding your code by an `assemble()` block without any parameters simply allows you to use add() and remove() instead of Openscads native difference(). 82 | One of the advantages over difference() is that you can put positive parts(to be subtracted from) and negative parts(to be subtracted) in any order at any place of the block in any number, even inside movements and rotations. 83 | Whereas with difference() you are forced to start with a single positive part (or you have to use a union() of several) and continue by negative parts which will be subtracted, forcing this order on you. 84 | 85 | For Example: 86 | 87 | ```.scad 88 | include 89 | 90 | assemble() 91 | { 92 | g(X(10),turnXY(45),solid()) 93 | { 94 | remove() 95 | box(15,h=10); 96 | add() 97 | tube(d=10,h=20); 98 | } 99 | 100 | add()Z(-3) 101 | box(10,h=10); 102 | } 103 | ``` 104 | 105 | ![screen](./partII-images/assemble1.png) 106 | 107 | Similar to g() command it is preferred to group. 108 | The preceding and following movement and rotation commands inside the according add statements() brackets. 109 | 110 | So that: 111 | 112 | ```.scad 113 | include 114 | 115 | assemble() 116 | { 117 | Z(10) turnXY(45) TOUP() add() g(height(10)) X(20) box(30); 118 | } 119 | ``` 120 | 121 | Produces the same result as: 122 | 123 | ```.scad 124 | include 125 | 126 | assemble() 127 | { 128 | add( Z(10), turnXY(45), TOUP(), height(10), X(20) ) box(30); 129 | } 130 | ``` 131 | 132 | ![screen](./partII-images/assemble2.png) 133 | 134 | The same applies to remove(), applyTo() and confineTo() described later. 135 | 136 | -------- 137 | #### Basic building block: tubeShell() 138 | just like above, but instead of specifing just d and wall thickness it is possible to specify dInner and dOuter. 139 | 140 | ![screen](./tutorial-images/tube2.png) --- 141 | 142 | ```.scad 143 | 144 | include <../devlibs/constructive/constructive-all.scad> 145 | 146 | //Assemb 147 | assemble() 148 | { 149 | add(TOUP())tube(d=20,wall=2,h=100); 150 | 151 | //we use add() here because addRemove() would add and then remove the same tubeShell() 152 | //so it would dissaperafter rendering (with keypress F7) 153 | add(Z(20))tubeShell(d=60,wall=3,h=8); 154 | 155 | //the tube can be used with addRemove(),it is meant to remove bodies of the Part inside it 156 | addRemove(Z(60))tube(d=60,wall=3,h=12); 157 | 158 | } 159 | 160 | ``` 161 | equivalent to a tube() , 162 | but the bore inside Ball is not a "Hard" cavity, it is not affecting/erasing other bodies inside it. 163 | 164 | ### Easily create skins with skin(size=0, skinThick=$skinThick, walls=2, margin=$margin) 165 | 166 | Like here: 167 | 168 | ```.scad 169 | include 170 | 171 | $skinThick=1.5; //thickness of the Skin in mm 172 | 173 | assemble() 174 | { 175 | //hull() 176 | addRemove(height(skin(20))) 177 | { 178 | 179 | g(X(-10),chamfer(-4,-4-1)) 180 | tube( d = skin(30),solid=true); 181 | 182 | g(X(4),chamfer(-1,-1,-5)) 183 | box(skin(37), skin(18), h=skin(4)); 184 | } 185 | remove(TOLEFT(TOFRONT)) box(30); 186 | } 187 | ``` 188 | 189 | Results in: 190 | ![screen](./partII-images/skin.png) 191 | 192 | Note: if you use the TOUP() to TODOWN(), TOLEFT(), etc. to align your Part also need 193 | to add alignSkin(TOUP),alignSkin(TODOWN) (or which ever alignment you are using), 194 | to keep Skins Walls each sides equally thick, like here: 195 | 196 | ---- 197 | 198 | ```.scad 199 | assemble() 200 | { 201 | //hull() 202 | addRemove(height(skin(20)), TOUP(),alignSkin(TOUP)) 203 | { 204 | 205 | g(X(-10),chamfer(-4,-4-1)) 206 | tube( d = skin(30),solid=true); 207 | 208 | g(X(4),chamfer(-1,-1,-5)) 209 | box(skin(30), skin(18), h=skin(10)); 210 | } 211 | remove(TOLEFT(TOFRONT)) box(30,h=100); 212 | } 213 | ``` 214 | 215 | Results in: 216 | ![screen](./partII-images/skin-toup.png) 217 | 218 | --- 219 | 220 | There are also functions to conditionally create skins: 221 | - skinIf(condition, size=0, skinThick=skinThick, walls=2, margin=margin) 222 | - skinParts(partList, size=0, skinThick=skinThick, walls=2 , margin=margin) 223 | 224 | ---- 225 | 226 | #### applyTo() 227 | 228 | Specifies name of the part which will be affected by the following add() and remove(). 229 | To create this part you also need to pass the part's name to assemble(). 230 | 231 | #### assemble Part name prefixes 232 | 233 | +name - use the list of part name already provided by apply(), but add name to this list 234 | 235 | :name exclude ancestors,only proceed if a part with exactly this name is beeing assembled, do not proceed for ancestors(which is a standard behaviour without the :) 236 | 237 | !name - exclude exact part with name, but not its ancestors or children (if part was included) 238 | 239 | Example of prefix use: 240 | 241 | ```.scad 242 | include 243 | 244 | assemble("aaa,ccc,ddd",$derivedParts=["bbb,ccc"]) 245 | applyTo("ddd,bbb") 246 | { 247 | add("ccc,+aaa,!bbb") 248 | echo($currentBody); 249 | } 250 | ``` 251 | 252 | This outputs: 253 | 254 | ``` 255 | Compiling design (CSG Tree generation)... 256 | ECHO: "Assembling: ", ["bbb", "aaa", "ccc", "ddd"] 257 | ECHO: "aaa" 258 | ECHO: "ccc" 259 | ECHO: "ddd" 260 | ``` 261 | 262 | ----- 263 | 264 | ##Parameterizing bodies for specific operations or parts 265 | 266 | It is possible to parametrize your code depending on whether it is being added or removed or depending 267 | on the name of the body which is constructed. 268 | Adding and removing 269 | 270 | #### margin() 271 | 272 | Allows create gaps between bodies 273 | 274 | ```.scad 275 | include 276 | assemble("rod,plate") 277 | { 278 | g(X(10),turnXY(45),solid()) 279 | { 280 | add("rod",remove="plate") 281 | tube(d=margin(16,1),h=20); 282 | 283 | add("plate") 284 | box(margin(20),h=3); 285 | } 286 | } 287 | ``` 288 | 289 | The tube(d=margin(16,1)... part of the code will adjust the diameter of the tube, so that d=16 when the tube is added (when constructing the part "rod") and d=16+1 290 | when the body is removed (when creating hole in the part "plate"). 291 | As a result there will be a visible gap between two bodies: 292 | ![screen](./partII-images/margin.png) 293 | 294 | The default margin is set to .8 by default so omitting the second parameter in margin call, like tube(d=margin(16),h=10); 295 | will produce a 16mm tube when added and 16.8 mm when removed, resulting in a gap of the half of 0.8 on each side of the tube. 296 | 297 | #### $removing variable 298 | 299 | Allows you code to change parameters depending on weather it is being removed from another object: 300 | 301 | ```.scad 302 | include 303 | assemble("rod,plate") 304 | { 305 | g(X(10),turnXY(45),solid()) 306 | { 307 | add("rod",remove="plate", 308 | Z($removing?5:0),turnXZ($removing?-35:15)) 309 | tube(d=$removing?30:10,h=20); 310 | 311 | clear(grey) 312 | add("plate") 313 | box(margin(40),h=10); 314 | } 315 | } 316 | ``` 317 | 318 | Results in: 319 | ![screen](./partII-images/removing_var.png) 320 | 321 | #### autocolor() 322 | 323 | 18.02.23 : 324 | strongly simplified autocoloring system, just call assemble with two Arguments: 325 | like assemble("Part1,part2,part3","screws,part5,part6")autoColor((){ .....} 326 | the Parts in the first Argument are considered to outer objects like Object shells 327 | and are drawn in a transparent color. The "screws" and part 5 and part6 given in the Second argument 328 | are considered drawings inside Details and are drawn in Opaque colors, so you can see them through the 329 | "shell" bodies. Every part is automatically given a distinct color from fixed builtin Palette 330 | 331 | #### confinementOf() 332 | 333 | a new simplified "confinement" mechanism to construct a confining Object for your Part 334 | you may use it with intersection() 335 | allows to use confinementOf() which assembles a confinement from Parts which are marked 336 | with the confines() marker function to mark which operations constitute a confinement detail, 337 | like in add(confines("part1"))box(): 338 | or in in remove(confines("part2"))tube(d=2,h=10); 339 | then you can use 340 | intersection() 341 | { 342 | confinement()moduleWithParts(); 343 | assemble()moduleWithParts(); 344 | } 345 | to confine the Parts inside the confinement 346 | 347 | ---- 348 | 349 | 350 | 351 | #### confineTo() 352 | 353 | > NOTE: Due to Openscads own issues in current versions of Openscad. confineTo can sometimes produce unpredictable results, so you might be better off using the old good intersection() instead, until it is fixed 354 | 355 | ## splitting your model into building blocks called "Parts" 356 | 357 | It is essential at certain complexity to split the model into parts,so you can later remove one part from another 358 | the Parts are orthogonal to the usage of modules: 359 | a single part can be constructed by several modules, and several modules can add or remove to/from the same part in their code. 360 | 361 | The parts which you want to display need to be span_8_rotated_boxes in the assemble("part1,greatpart,screw,orAlikePart") argument; 362 | if you decide to hide a Part just remove its name from the assemble(".....") string argument 363 | So here an example: 364 | TODO:..... 365 | 366 | -------- 367 | 368 | 369 | #### Topics still to cover 370 | 371 | ---- 372 | 373 | *simple constrains(touching/distance) 374 | ------ 375 | 376 | *bodyIs(body) 377 | bodyIs(body)?(what+($removing? extra:0)):0; 378 | 379 | ----- 380 | 381 | *removeFor(body,extra=$removeExtra,what=0) 382 | ---- 383 | 384 | *adjustFor 385 | ----- 386 | 387 | ----- 388 | 389 | *misc. 2D 390 | arc(r,angle=90,deltaA=1,noCenter=false,wall=0) 391 | addOffset(rOuter=1,rInner=0) 392 | function arcPoints(r,angle=90,deltaA=1,noCenter=false) 393 | 394 | ------ 395 | 396 | For a **basic introduction** (specially if you are new to Openscad ) 397 | see the [beginners tutorial](./basic-tutorial.md) it explains Constructive Syntax for main Building blocks, like tube(), box() or bentStrip() and their placement and alignment in space like stack() , align(), X(),Y(),Z() or turnXZ() 398 | 399 | [Part II tutorial](./tutorial-partII.md) shows somee basic object modification like reflectX(), cScale() ,or colors and then goes on to explain, how to work with sets of similar objects without for(), with: pieces(), span(), vals(), selectPieces(), etc.. 400 | -------------------------------------------------------------------------------- /versionlog.txt: -------------------------------------------------------------------------------- 1 | 19.02.23 : renamed tuebeSoftHole (malnamed module) to tubeShell 2 | 18.02.23 : 3 | ###autocolor() 4 | strongly simplified autocoloring system, just call assemble with two Arguments: 5 | like assemble("Part1,part2,part3","screws,part5,part6")autoColor((){ .....} 6 | the Parts in the firt Argument are considered to outer objects like Object shells 7 | and are drawn in a transprent color. the "screws" and part 5 and part6 given in the Second argument 8 | are considered drawings inside Detils and are drawn in Opque colors, so you can see them through the 9 | "shell" bodies. Every part is automatically given a distinct color from fixed builtin Palette 10 | 11 | ###confinementOf() 12 | a new simplified "confinement" mechanism to construct a confining Object for your Part 13 | you may use it with intersection() 14 | allows to use confinementOf() which assembles a confinement from Parts which are marked 15 | with the confines() marker function to mark which operations constitute a confinement detail, 16 | like in add(confines("part1"))box(): 17 | or in in remove(confines("part2"))tube(d=2,h=10); 18 | then you can use 19 | intersection() 20 | { 21 | confinement()moduleWithParts(); 22 | assemble()moduleWithParts(); 23 | } 24 | to confine the PArts inside the confinement 25 | ###Gallery 26 | created a Gallery where new shiny constructive examples to see what can be acheived 27 | can be published in the Gallery, 28 | their code is not commented and not cleaned up, but still can be used for reference 29 | 30 | 31 | --------------------------------------------------------------------------------