forked from BelfrySCAD/BOSL2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathattachments.scad
4106 lines (3907 loc) · 180 KB
/
attachments.scad
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//////////////////////////////////////////////////////////////////////
// LibFile: attachments.scad
// The modules in this file allows you to attach one object to another by making one object the child of another object.
// You can place the child object in relation to its parent object and control the position and orientation
// relative to the parent. The modifiers allow you to treat children in ways different from simple union, such
// as differencing them from the parent, or changing their color. Attachment only works when the parent and child
// are both written to support attachment. Also included in this file are the tools to make your own "attachable" objects.
// Includes:
// include <BOSL2/std.scad>
// FileGroup: Basic Modeling
// FileSummary: Positioning objects on or relative to other objects. Making your own objects support attachment.
// FileFootnotes: STD=Included in std.scad
//////////////////////////////////////////////////////////////////////
// Default values for attachment code.
$tags=undef; // for backward compatibility
$tag = "";
$tag_prefix = "";
$overlap = 0;
$color = "default";
$save_color = undef; // Saved color to revert back for children
$anchor_override = undef;
$attach_to = undef;
$attach_anchor = [CENTER, CENTER, UP, 0];
$attach_norot = false;
$parent_anchor = BOTTOM;
$parent_spin = 0;
$parent_orient = UP;
$parent_size = undef;
$parent_geom = undef;
$tags_shown = "ALL";
$tags_hidden = [];
_ANCHOR_TYPES = ["intersect","hull"];
// Section: Terminology and Shortcuts
// This library adds the concept of anchoring, spin and orientation to the `cube()`, `cylinder()`
// and `sphere()` builtins, as well as to most of the shapes provided by this library itself.
// - An anchor is a place on an object which you can align the object to, or attach other objects
// to using `attach()` or `position()`. An anchor has a position, a direction, and a spin.
// The direction and spin are used to orient other objects to match when using `attach()`.
// - Spin is a simple rotation around the Z axis.
// - Orientation is rotating an object so that its top is pointed towards a given vector.
// An object will first be translated to its anchor position, then spun, then oriented.
// For a detailed step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// .
// For describing directions, faces, edges, and corners the library provides a set of shortcuts
// all based on combinations of unit direction vectors. You can use these for anchoring and orienting
// attachable objects. You can also them to specify edge sets for rounding or chamfering cuboids,
// or for placing edge, face and corner masks.
// Subsection: Anchor
// Anchoring is specified with the `anchor` argument in most shape modules. Specifying `anchor`
// when creating an object will translate the object so that the anchor point is at the origin
// (0,0,0). Anchoring always occurs before spin and orientation are applied.
// .
// An anchor can be referred to in one of two ways; as a directional vector, or as a named anchor string.
// .
// When given as a vector, it points, in a general way, towards the face, edge, or corner of the
// object that you want the anchor for, relative to the center of the object. You can simply
// specify a vector like `[0,0,1]` to anchor an object at the Z+ end, but you can also use
// directional constants with names like `TOP`, `BOTTOM`, `LEFT`, `RIGHT` and `BACK` that you can add together
// to specify anchor points. See [specifying directions](attachments.scad#subsection-specifying-directions)
// below for the full list of pre-defined directional constants.
// .
// For example:
// - `[0,0,1]` is the same as `TOP` and refers to the center of the top face.
// - `[-1,0,1]` is the same as `TOP+LEFT`, and refers to the center of the top-left edge.
// - `[1,1,-1]` is the same as `BOTTOM+BACK+RIGHT`, and refers to the bottom-back-right corner.
// .
// When the object is cubical or rectangular in shape the anchors must have zero or one values
// for their components and they refer to the face centers, edge centers, or corners of the object.
// The direction of a face anchor will be perpendicular to the face, pointing outward. The direction of a edge anchor
// will be the average of the anchor directions of the two faces the edge is between. The direction
// of a corner anchor will be the average of the anchor directions of the three faces the corner is
// on.
// .
// When the object is cylindrical, conical, or spherical in nature, the anchors will be located
// around the surface of the cylinder, cone, or sphere, relative to the center.
// You can generally use an arbitrary vector to get an anchor positioned anywhere on the curved
// surface of such an object, and the anchor direction will be the surface normal at the anchor location.
// However, for anchor component pointing toward the flat face should be either -1, 1, or 0, and
// anchors that point diagonally toward one of the flat faces will select a point on the edge.
// .
// For objects in two dimensions, the natural expectation is for TOP and BOTTOM to refer to the Y direction
// of the shape. To support this, if you give an anchor in 2D that has anchor.y=0 then the Z component
// will be mapped to the Y direction. This means you can use TOP and BOTTOM for anchors of 2D objects.
// But remember that TOP and BOTTOM are three dimensional vectors and this is a special interpretation
// for 2d anchoring.
// .
// Some more complex objects, like screws and stepper motors, have named anchors to refer to places
// on the object that are not at one of the standard faces, edges or corners. For example, stepper
// motors have anchors for `"screw1"`, `"screw2"`, etc. to refer to the various screwholes on the
// stepper motor shape. The names, positions, directions, and spins of these anchors are
// specific to the object, and are documented when they exist.
// Subsection: Spin
// Spin is specified with the `spin` argument in most shape modules. Specifying a scalar `spin`
// when creating an object will rotate the object counter-clockwise around the Z axis by the given
// number of degrees. If given as a 3D vector, the object will be rotated around each of the X, Y, Z
// axes by the number of degrees in each component of the vector. Spin is always applied after
// anchoring, and before orientation. Since spin is applied after anchoring it is not what
// you might think of intuitively as spinning the shape. To do that, apply `zrot()` to the shape before anchoring.
// Subsection: Orient
// Orientation is specified with the `orient` argument in most shape modules. Specifying `orient`
// when creating an object will rotate the object such that the top of the object will be pointed
// at the vector direction given in the `orient` argument. Orientation is always applied after
// anchoring and spin. The constants `UP`, `DOWN`, `FRONT`, `BACK`, `LEFT`, and `RIGHT` can be
// added together to form the directional vector for this. ie: `LEFT+BACK`
// Subsection: Specifying Directions
// You can use direction vectors to specify anchors for objects or to specify edges, faces, and
// corners of cubes. You can simply specify these direction vectors numerically, but another
// option is to use named constants for direction vectors. These constants define unit vectors
// for the six axis directions as shown below.
// Figure(3D,Big,VPD=6): Named constants for direction vectors. Some directions have more than one name.
// $fn=12;
// stroke([[0,0,0],RIGHT], endcap2="arrow2", width=.05);
// color("black")right(.05)up(.05)move(RIGHT) text3d("RIGHT",size=.1,h=.01,anchor=LEFT,orient=FRONT);
// stroke([[0,0,0],LEFT], endcap2="arrow2", width=.05);
// color("black")left(.05)up(.05)move(LEFT) text3d("LEFT",size=.1,h=.01,anchor=RIGHT,orient=FRONT);
// stroke([[0,0,0],FRONT], endcap2="arrow2", width=.05);
// color("black")
// left(.1){
// up(.12)move(FRONT) text3d("FRONT",size=.1,h=.01,anchor=RIGHT,orient=FRONT);
// move(FRONT) text3d("FWD",size=.1,h=.01,anchor=RIGHT,orient=FRONT);
// down(.12)move(FRONT) text3d("FORWARD",size=.1,h=.01,anchor=RIGHT,orient=FRONT);
// }
// stroke([[0,0,0],BACK], endcap2="arrow2", width=.05);
// right(.05)
// color("black")move(BACK) text3d("BACK",size=.1,h=.01,anchor=LEFT,orient=FRONT);
// stroke([[0,0,0],DOWN], endcap2="arrow2", width=.05);
// color("black")
// right(.1){
// up(.12)move(BOT) text3d("DOWN",size=.1,h=.01,anchor=LEFT,orient=FRONT);
// move(BOT) text3d("BOTTOM",size=.1,h=.01,anchor=LEFT,orient=FRONT);
// down(.12)move(BOT) text3d("BOT",size=.1,h=.01,anchor=LEFT,orient=FRONT);
// }
// stroke([[0,0,0],TOP], endcap2="arrow2", width=.05);
// color("black")left(.05){
// up(.12)move(TOP) text3d("TOP",size=.1,h=.01,anchor=RIGHT,orient=FRONT);
// move(TOP) text3d("UP",size=.1,h=.01,anchor=RIGHT,orient=FRONT);
// }
// Figure(2D,Big): Named constants for direction vectors in 2D. For anchors the TOP and BOTTOM directions are collapsed into 2D as shown here, but do not try to use TOP or BOTTOM as 2D directions in other situations.
// $fn=12;
// stroke(path2d([[0,0,0],RIGHT]), endcap2="arrow2", width=.05);
// color("black")fwd(.22)left(.05)move(RIGHT) text("RIGHT",size=.1,anchor=RIGHT);
// stroke(path2d([[0,0,0],LEFT]), endcap2="arrow2", width=.05);
// color("black")right(.05)fwd(.22)move(LEFT) text("LEFT",size=.1,anchor=LEFT);
// stroke(path2d([[0,0,0],FRONT]), endcap2="arrow2", width=.05);
// color("black")
// fwd(.2)
// right(.15)
// color("black")move(BACK) { text("BACK",size=.1,anchor=LEFT); back(.14) text("(TOP)", size=.1, anchor=LEFT);}
// color("black")
// left(.15)back(.2+.14)move(FRONT){
// back(.14) text("FRONT",size=.1,anchor=RIGHT);
// text("FWD",size=.1,anchor=RIGHT);
// fwd(.14) text("FORWARD",size=.1,anchor=RIGHT);
// fwd(.28) text("(BOTTOM)",size=.1,anchor=RIGHT);
// fwd(.14*3) text("(BOT)",size=.1,anchor=RIGHT);
// }
// stroke(path2d([[0,0,0],BACK]), endcap2="arrow2", width=.05);
// Subsection: Specifying Faces
// Modules operating on faces accept a list of faces to describe the faces to operate on. Each
// face is given by a vector that points to that face. Attachments of cuboid objects onto their faces also
// work by choosing an attachment face with a single vector in the same manner.
// Figure(3D,Big,NoScales,VPD=275): The six faces of the cube. Some have faces have more than one name.
// ydistribute(50) {
// xdistribute(35){
// _show_cube_faces([BACK], botlabel=["BACK"]);
// _show_cube_faces([UP],botlabel=["TOP","UP"]);
// _show_cube_faces([RIGHT],botlabel=["RIGHT"]);
// }
// xdistribute(35){
// _show_cube_faces([FRONT],toplabel=["FRONT","FWD", "FORWARD"]);
// _show_cube_faces([DOWN],toplabel=["BOTTOM","BOT","DOWN"]);
// _show_cube_faces([LEFT],toplabel=["LEFT"]);
// }
// }
// Subsection: Specifying Edges
// Modules operating on edges use two arguments to describe the edge set they will use: The `edges` argument
// is a list of edge set descriptors to include in the edge set, and the `except` argument is a list of
// edge set descriptors to remove from the edge set.
// The default value for `edges` is `"ALL"`, the set of all edges.
// The default value for `except` is the empty set, meaning no edges are removed.
// If either argument is just a single edge set
// descriptor it can be passed directly rather than in a singleton list.
// Each edge set descriptor must be one of:
// - A vector pointing towards an edge, indicating that single edge.
// - A vector pointing towards a face, indicating all edges surrounding that face.
// - A vector pointing towards a corner, indicating all edges touching that corner.
// - The string `"X"`, indicating all X axis aligned edges.
// - The string `"Y"`, indicating all Y axis aligned edges.
// - The string `"Z"`, indicating all Z axis aligned edges.
// - The string `"ALL"`, indicating all edges.
// - The string `"NONE"`, indicating no edges at all.
// - A 3x4 array, where each entry corresponds to one of the 12 edges and is set to 1 if that edge is included and 0 if the edge is not. The edge ordering is:
// ```
// [
// [Y-Z-, Y+Z-, Y-Z+, Y+Z+],
// [X-Z-, X+Z-, X-Z+, X+Z+],
// [X-Y-, X+Y-, X-Y+, X+Y+]
// ]
// ```
// You can specify edge descriptors directly by giving a vector, or you can use sums of the
// named direction vectors described above. Below we show all of the edge sets you can
// describe with sums of the direction vectors, and then we show some examples of combining
// edge set descriptors.
// Figure(3D,Big,VPD=300,NoScales): Vectors pointing toward an edge select that single edge
// ydistribute(50) {
// xdistribute(30) {
// _show_edges(edges=BOT+RIGHT);
// _show_edges(edges=BOT+BACK);
// _show_edges(edges=BOT+LEFT);
// _show_edges(edges=BOT+FRONT);
// }
// xdistribute(30) {
// _show_edges(edges=FWD+RIGHT);
// _show_edges(edges=BACK+RIGHT);
// _show_edges(edges=BACK+LEFT);
// _show_edges(edges=FWD+LEFT);
// }
// xdistribute(30) {
// _show_edges(edges=TOP+RIGHT);
// _show_edges(edges=TOP+BACK);
// _show_edges(edges=TOP+LEFT);
// _show_edges(edges=TOP+FRONT);
// }
// }
// Figure(3D,Med,VPD=205,NoScales): Vectors pointing toward a face select all edges surrounding that face.
// ydistribute(50) {
// xdistribute(30) {
// _show_edges(edges=LEFT);
// _show_edges(edges=FRONT);
// _show_edges(edges=RIGHT);
// }
// xdistribute(30) {
// _show_edges(edges=TOP);
// _show_edges(edges=BACK);
// _show_edges(edges=BOTTOM);
// }
// }
// Figure(3D,Big,VPD=300,NoScales): Vectors pointing toward a corner select all edges surrounding that corner.
// ydistribute(50) {
// xdistribute(30) {
// _show_edges(edges=FRONT+LEFT+TOP);
// _show_edges(edges=FRONT+RIGHT+TOP);
// _show_edges(edges=FRONT+LEFT+BOT);
// _show_edges(edges=FRONT+RIGHT+BOT);
// }
// xdistribute(30) {
// _show_edges(edges=TOP+LEFT+BACK);
// _show_edges(edges=TOP+RIGHT+BACK);
// _show_edges(edges=BOT+LEFT+BACK);
// _show_edges(edges=BOT+RIGHT+BACK);
// }
// }
// Figure(3D,Med,VPD=205,NoScales): Named Edge Sets
// ydistribute(50) {
// xdistribute(30) {
// _show_edges(edges="X");
// _show_edges(edges="Y");
// _show_edges(edges="Z");
// }
// xdistribute(30) {
// _show_edges(edges="ALL");
// _show_edges(edges="NONE");
// }
// }
// Figure(3D,Big,VPD=310,NoScales): Next are some examples showing how you can combine edge descriptors to obtain different edge sets. You can specify the top front edge with a numerical vector or by combining the named direction vectors. If you combine them as a list you get all the edges around the front and top faces. Adding `except` removes an edge.
// xdistribute(43){
// _show_edges(_edges([0,-1,1]),toplabel=["edges=[0,-1,1]"]);
// _show_edges(_edges(TOP+FRONT),toplabel=["edges=TOP+FRONT"]);
// _show_edges(_edges([TOP,FRONT]),toplabel=["edges=[TOP,FRONT]"]);
// _show_edges(_edges([TOP,FRONT],TOP+FRONT),toplabel=["edges=[TOP,FRONT]","except=TOP+FRONT"]);
// }
// Figure(3D,Big,VPD=310,NoScales): Using `except=BACK` removes the four edges surrounding the back face if they are present in the edge set. In the first example only one edge needs to be removed. In the second example we remove two of the Z-aligned edges. The third example removes all four back edges from the default edge set of all edges. You can explicitly give `edges="ALL"` but it is not necessary, since this is the default. In the fourth example, the edge set of Y-aligned edges contains no back edges, so the `except` parameter has no effect.
// xdistribute(43){
// _show_edges(_edges(BOT,BACK), toplabel=["edges=BOT","except=BACK"]);
// _show_edges(_edges("Z",BACK), toplabel=["edges=\"Z\"", "except=BACK"]);
// _show_edges(_edges("ALL",BACK), toplabel=["(edges=\"ALL\")", "except=BACK"]);
// _show_edges(_edges("Y",BACK), toplabel=["edges=\"Y\"","except=BACK"]);
// }
// Figure(3D,Big,NoScales,VPD=310): On the left `except` is a list to remove two edges. In the center we show a corner edge set defined by a numerical vector, and at the right we remove that same corner edge set with named direction vectors.
// xdistribute(52){
// _show_edges(_edges("ALL",[FRONT+RIGHT,FRONT+LEFT]),
// toplabel=["except=[FRONT+RIGHT,"," FRONT+LEFT]"]);
// _show_edges(_edges([1,-1,1]),toplabel=["edges=[1,-1,1]"]);
// _show_edges(_edges([TOP,BOT], TOP+RIGHT+FRONT),toplabel=["edges=[TOP,BOT]","except=TOP+RIGHT+FRONT"]);
// }
// Subsection: Specifying Corners
// Modules operating on corners use two arguments to describe the corner set they will use: The `corners` argument
// is a list of corner set descriptors to include in the corner set, and the `except` argument is a list of
// corner set descriptors to remove from the corner set.
// The default value for `corners` is `"ALL"`, the set of all corners.
// The default value for `except` is the empty set, meaning no corners are removed.
// If either argument is just a single corner set
// descriptor it can be passed directly rather than in a singleton list.
// Each corner set descriptor must be one of:
// - A vector pointing towards a corner, indicating that corner.
// - A vector pointing towards an edge indicating both corners at the ends of that edge.
// - A vector pointing towards a face, indicating all the corners of that face.
// - The string `"ALL"`, indicating all corners.
// - The string `"NONE"`, indicating no corners at all.
// - A length 8 vector where each entry corresponds to a corner and is 1 if the corner is included and 0 if it is excluded. The corner ordering is
// ```
// [X-Y-Z-, X+Y-Z-, X-Y+Z-, X+Y+Z-, X-Y-Z+, X+Y-Z+, X-Y+Z+, X+Y+Z+]
// ```
// You can specify corner descriptors directly by giving a vector, or you can use sums of the
// named direction vectors described above. Below we show all of the corner sets you can
// describe with sums of the direction vectors and then we show some examples of combining
// corner set descriptors.
// Figure(3D,Big,NoScales,VPD=300): Vectors pointing toward a corner select that corner.
// ydistribute(55) {
// xdistribute(35) {
// _show_corners(corners=FRONT+LEFT+TOP);
// _show_corners(corners=FRONT+RIGHT+TOP);
// _show_corners(corners=FRONT+LEFT+BOT);
// _show_corners(corners=FRONT+RIGHT+BOT);
// }
// xdistribute(35) {
// _show_corners(corners=TOP+LEFT+BACK);
// _show_corners(corners=TOP+RIGHT+BACK);
// _show_corners(corners=BOT+LEFT+BACK);
// _show_corners(corners=BOT+RIGHT+BACK);
// }
// }
// Figure(3D,Big,NoScales,VPD=340): Vectors pointing toward an edge select the corners and the ends of the edge.
// ydistribute(55) {
// xdistribute(35) {
// _show_corners(corners=BOT+RIGHT);
// _show_corners(corners=BOT+BACK);
// _show_corners(corners=BOT+LEFT);
// _show_corners(corners=BOT+FRONT);
// }
// xdistribute(35) {
// _show_corners(corners=FWD+RIGHT);
// _show_corners(corners=BACK+RIGHT);
// _show_corners(corners=BACK+LEFT);
// _show_corners(corners=FWD+LEFT);
// }
// xdistribute(35) {
// _show_corners(corners=TOP+RIGHT);
// _show_corners(corners=TOP+BACK);
// _show_corners(corners=TOP+LEFT);
// _show_corners(corners=TOP+FRONT);
// }
// }
// Figure(3D,Med,NoScales,VPD=225): Vectors pointing toward a face select the corners of the face.
// ydistribute(55) {
// xdistribute(35) {
// _show_corners(corners=LEFT);
// _show_corners(corners=FRONT);
// _show_corners(corners=RIGHT);
// }
// xdistribute(35) {
// _show_corners(corners=TOP);
// _show_corners(corners=BACK);
// _show_corners(corners=BOTTOM);
// }
// }
// Figure(3D,Med,NoScales,VPD=200): Corners by name
// xdistribute(35) {
// _show_corners(corners="ALL");
// _show_corners(corners="NONE");
// }
// Figure(3D,Big,NoScales,VPD=300): Next are some examples showing how you can combine corner descriptors to obtain different corner sets. You can specify corner sets numerically or by adding together named directions. The third example shows a list of two corner specifications, giving all the corners on the front face or the right face.
// xdistribute(52){
// _show_corners(_corners([1,-1,-1]),toplabel=["corners=[1,-1,-1]"]);
// _show_corners(_corners(BOT+RIGHT+FRONT),toplabel=["corners=BOT+RIGHT+FRONT"]);
// _show_corners(_corners([FRONT,RIGHT]), toplabel=["corners=[FRONT,RIGHT]"]);
// }
// Figure(3D,Big,NoScales,VPD=300): Corners for one edge, two edges, and all the edges except the two on one edge. Note that since the default is all edges, you only need to give the except argument in this case:
// xdistribute(52){
// _show_corners(_corners(FRONT+TOP), toplabel=["corners=FRONT+TOP"]);
// _show_corners(_corners([FRONT+TOP,BOT+BACK]), toplabel=["corners=[FRONT+TOP,"," BOT+BACK]"]);
// _show_corners(_corners("ALL",FRONT+TOP), toplabel=["(corners=\"ALL\")","except=FRONT+TOP"]);
// }
// Figure(3D,Med,NoScales,VPD=240): The first example shows a single corner removed from the top corners using a numerical vector. The second one shows removing a set of two corner descriptors from the implied set of all corners.
// xdistribute(58){
// _show_corners(_corners(TOP,[1,1,1]), toplabel=["corners=TOP","except=[1,1,1]"]);
// _show_corners(_corners("ALL",[FRONT+RIGHT+TOP,FRONT+LEFT+BOT]),
// toplabel=["except=[FRONT+RIGHT+TOP,"," FRONT+LEFT+BOT]"]);
// }
// Subsection: Anchoring of Non-Rectangular Objects and Anchor Type (atype)
// We focused above on rectangular objects that have well-defined faces and edges aligned with the coordinate axes.
// Things get difficult when the objects are curved, or even when their edges are not neatly aligned with the coordinate axes.
// In these cases, the library may provide multiple different anchoring schemes, called the anchor types. When a module supports
// multiple anchor types, use the `atype=` parameter to select the anchor type you need.
// .
// First consider the case of a simple rectangle whose corners have been rounded. Where should the anchors lie?
// The default anchor type puts them in the same location as the anchors of an unrounded rectangle, which means that for
// positive rounding radii, they are not even located on the perimeter of the object.
// Figure(2D,Med,NoAxes): Default "box" atype anchors for a rounded {{rect()}}
// rect([100,50], rounding=[10,0,0,-20],chamfer=[0,10,-20,0]) show_anchors();
// Continues:
// This choice enables you to position the box, or attach things to it, without regard to its rounding or chamfers. If you need to
// anchor onto the roundovers or chamfers then you can use the "perim" anchor type:
// Figure(2D,Med,NoAxes): The "perim" atype for a rounded and chamfered {{rect()}}
// rect([100,50], rounding=[10,0,0,-20],chamfer=[0,10,-20,0],atype="perim") show_anchors();
// Continues:
// With this anchor type, the anchors are located on the perimeter. For positive roundings they point in the standard anchor direction;
// for negative roundings they are parallel to the base. As noted above, for circles, cylinders, and spheres, the anchor point is
// determined by choosing the point where the anchor vector intersects the shape. On a circle, this results in an anchor whose direction
// matches the user provided anchor vector. But on an ellipse, something else happens:
// Figure(2D,Med,NoAxes): Anchors on an ellipse. The red arrow shows a TOP+RIGHT anchor direction.
// ellipse([70,30]) show_anchors();
// stroke([[0,0],[45,45]], color="red",endcap2="arrow2");
// Continues:
// For a TOP+RIGHT anchor direction, the surface normal at the intersection point does not match the anchor direction,
// so the direction of the anchor shown in blue does not match the direction specified, in red.
// Anchors computed this way have anchor type "intersect". When a shape is concave, intersection anchors can produce
// a result buried inside the shape's concavity. Consider the RIGHT anchor of this supershape example:
// Figure(2D,Med,NoAxes): A supershape with "intersect" anchor type:
// supershape(n=150,r=75, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9,atype="intersect") show_anchors();
// Continues:
// A different anchor type called "hull" finds anchors that are on the convex hull of the shape.
// Figure(2D,Med,NoAxes): A supershape with "hull" anchor type:
// supershape(n=150,r=55, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9,atype="hull") show_anchors();
// Continues:
// Hull anchoring works by creating the line (or plane in 3D) that is normal to the specified anchor direction, and
// finding the point farthest from the center that intersects that line (or plane).
// Figure(2D,Med,NoAxes): Finding the RIGHT and BACK+LEFT "hull" anchors
// supershape(n=128,r=55, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9,atype="hull") {
// position(RIGHT) color_this("red")rect([1,90],anchor=LEFT);
// attach(RIGHT)anchor_arrow2d(13);
// attach(BACK+LEFT) {
// anchor_arrow2d(13);
// color_this("red")rect([30,1]);
// }
// }
// Continues:
// In the example the RIGHT anchor is found when the normal line (shown in red) is tangent to the shape at two points.
// The anchor is then taken to be the midpoint. The BACK+LEFT anchor occurs with a single tangent point, and the
// anchor point is located at the tangent point. For circles intersection is done to the exact circle, but for other
// shapes these calculations are done on the point lists that defines the shape, so if you change the number of points
// in the list, the precise location of the anchors can change. You can also get surprising results if your point list is badly chosen.
// Figure(2D,Med,NoAxes): Circle anchor in blue. The red anchor is computed to a point list of a circle with 17 segments.
// circle(r=31,$fn=128) attach(TOP)anchor_arrow2d(15);
// region(circle(r=33,$fn=17)) {color("red")attach(TOP)anchor_arrow2d(13);}
// Continues:
// The figure shows a large horizontal offset due to a poor choice of sampling for the circular shape when using the "hull" anchor type.
// The determination of "hull" or "intersect" anchors may depend on the location of the centerpoint used in the computation.
// Some of the modules allow you to change the centerpoint using a `cp=` argument. If you need to change the centerpoint for
// a module that does not provide this option, you can use the generic {{region()}} module, which will let you specify a centerpoint.
// The default center point is the centroid, specified by "centroid". You can also choose "mean", which gives the mean of all
// the data points, or "bbox", which gives the centerpoint of the bounding box for the data. Your last option for centerpoint is to
// choose an arbitrary point that meets your needs.
// Figure(2D,Med,NoAxes): The centerpoint for "intersect" anchors is located at the red dot
// region(supershape(n=128,r=55, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9),atype="intersect",cp=[0,30]) show_anchors();
// color("red")back(30)circle(r=2,$fn=16);
// Continues:
// Note that all the anchors for an object have to be determined based on one anchor type and relative to the same centerpoint.
// The supported anchor types for each module appear in the "Anchor Types" section of its entry.
// Section: Attachment Positioning
// Module: position()
// Synopsis: Attaches children to a parent object at an anchor point.
// SynTags: Trans
// Topics: Attachments
// See Also: attachable(), attach(), orient()
// Usage:
// PARENT() position(from) CHILDREN;
// Description:
// Attaches children to a parent object at an anchor point. For a step-by-step explanation
// of attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
// from = The vector, or name of the parent anchor point to attach to.
// Side Effects:
// `$attach_anchor` for each `from=` anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor.
// `$attach_to` is set to `undef`.
// `$attach_norot` is set to `true`.
// Example:
// spheroid(d=20) {
// position(TOP) cyl(l=10, d1=10, d2=5, anchor=BOTTOM);
// position(RIGHT) cyl(l=10, d1=10, d2=5, anchor=BOTTOM);
// position(FRONT) cyl(l=10, d1=10, d2=5, anchor=BOTTOM);
// }
module position(from)
{
req_children($children);
dummy1=assert($parent_geom != undef, "No object to position relative to.");
anchors = (is_vector(from)||is_string(from))? [from] : from;
two_d = _attach_geom_2d($parent_geom);
for (anchr = anchors) {
anch = _find_anchor(anchr, $parent_geom);
$attach_to = undef;
$attach_anchor = anch;
$attach_norot = true;
translate(anch[1]) children();
}
}
// Module: orient()
// Synopsis: Orients children's tops in the directon of the specified anchor.
// SynTags: Trans
// Topics: Attachments
// See Also: attachable(), attach(), position()
// Usage:
// PARENT() orient(anchor, [spin]) CHILDREN;
// Description:
// Orients children such that their top is tilted in the direction of the specified parent anchor point.
// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
// anchor = The anchor on the parent which you want to match the orientation of.
// spin = The spin to add to the children. (Overrides anchor spin.)
// Side Effects:
// `$attach_to` is set to `undef`.
// `$attach_norot` is set to `true`.
// Example: When orienting to an anchor, the spin of the anchor may cause confusion:
// prismoid([50,50],[30,30],h=40) {
// position(TOP+RIGHT)
// orient(RIGHT)
// prismoid([30,30],[0,5],h=20,anchor=BOT+LEFT);
// }
// Example: You can override anchor spin with `spin=`.
// prismoid([50,50],[30,30],h=40) {
// position(TOP+RIGHT)
// orient(RIGHT,spin=0)
// prismoid([30,30],[0,5],h=20,anchor=BOT+LEFT);
// }
// Example: Or you can anchor the child from the back
// prismoid([50,50],[30,30],h=40) {
// position(TOP+RIGHT)
// orient(RIGHT)
// prismoid([30,30],[0,5],h=20,anchor=BOT+BACK);
// }
module orient(anchor, spin) {
req_children($children);
check=
assert($parent_geom != undef, "No parent to orient from!")
assert(is_string(anchor) || is_vector(anchor));
anch = _find_anchor(anchor, $parent_geom);
two_d = _attach_geom_2d($parent_geom);
fromvec = two_d? BACK : UP;
spin = default(spin, anch[3]);
dummy=assert(is_finite(spin));
$attach_to = undef;
$attach_norot = true;
if (two_d)
rot(spin)rot(from=fromvec, to=anch[2]) children();
else
rot(spin, from=fromvec, to=anch[2]) children();
}
// Module: align()
// Synopsis: Position and orient children with alignment to parent edges.
// SynTags: Trans
// Topics: Attachments
// See Also: attachable(), attach(), position(), orient()
// Usage:
// PARENT() align(anchor, [orient], [spin], [inside=]) CHILDREN;
// Description:
// Positions children to the specified anchor(s) on the parent and anchors the
// children so that they are aligned with the edge(s) of the parent at those parent anchors.
// You can specify a parent anchor point in `orient` and in this case, the top of the child
// is tilted in the direction of that anchor.
// This means you can easily place children so they are aligned flush with edges of the parent.
// In contrast, with {{position()}} you will have to work out the correct anchor for the children
// which is not always obvious. It also enables you to place several children that have different
// anchors, which would otherwise require several {{position()}} calls. The inside argument
// causes the object to appear inside the parent for use with {{diff()}}.
// .
// When you use `align()`, the `orient=` and `anchor=` arguments to the child objects are overriden,
// so they do not have any effect. The `spin=` argument to the child still applies.
// Arguments:
// anchor = parent anchor or list of parent anchors for positioning children
// orient = parent anchor to give direction for orienting the children. Default: UP
// spin = spin in degrees for rotating the children. Default: Derived from orient anchor
// ---
// inside = if true, place object inside the parent instead of outside. Default: false
// Side Effects:
// `$attach_anchor` for each anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor.
// `$attach_to` is set to `undef`.
// `$attach_norot` is set to `true`.
// `$anchor_override` is set to the anchor required for proper positioning of the child.
// if inside is true then set default tag to "remove"
// Example: Child would require anchor of RIGHT+FRONT+BOT if placed with {{position()}}.
// cuboid([50,40,15])
// align(RIGHT+FRONT+TOP)
// color("lightblue")prismoid([10,5],[7,4],height=4);
// Example: Child requires a different anchor for each position, so explicit specification of the anchor for children is impossible in this case, without using two separate commands.
// cuboid([50,40,15])
// align([RIGHT+TOP,LEFT+TOP])
// color("lightblue")prismoid([10,5],[7,4],height=4);
// Example: If you try to spin your child, the spin happens after the alignment anchor, so the child will not be flush:
// cuboid([50,40,15])
// align([RIGHT+TOP])
// color("lightblue")
// prismoid([10,5],[7,4],height=4,spin=90);
// Example: You can instead spin the attached children using the spin parameter to `align()`. In this example, the required anchor is BOT+FWD, which is less obvious.
// cuboid([50,40,15])
// align(RIGHT+TOP,spin=90)
// color("lightblue")prismoid([10,5],[7,4],height=4);
// Example: Here the child is oriented to the RIGHT, so it appears flush with the top. In this case you don't have to figure out that the required child anchor is BOT+BACK.
// cuboid([50,40,15])
// align(RIGHT+TOP,RIGHT)
// color("lightblue")prismoid([10,5],[7,4],height=4);
// Example: If you change the orientation the child still appears aligned flush in its changed orientation:
// cuboid([50,40,15])
// align(RIGHT+TOP,DOWN)
// color("lightblue")prismoid([10,5],[7,4],height=4);
// Example: Objects on the right already have nonzero spin by default, so setting spin=0 changes the spin:
// prismoid(50,30,25){
// align(RIGHT+TOP,RIGHT,spin=0)
// color("lightblue")prismoid([10,5],[7,4],height=4);
// align(RIGHT+BOT,RIGHT)
// color("green")prismoid([10,5],[7,4],height=4);
// }
// Example: Setting inside=true enables us to subtract the child from the parent with {{diff()}. The "remove" tag is automatically applied when you set `inside=true`.
// diff()
// cuboid([40,30,10])
// move(.1*[0,-1,1])
// align(FRONT+TOP,inside=true)
// prismoid([10,5],[7,5],height=4);
module align(anchor,orient=UP,spin,inside=false)
{
req_children($children);
dummy1=assert($parent_geom != undef, "No object to align to.")
assert(is_string(orient) || is_vector(orient),"Bad orient value");
position_anchors = (is_vector(anchor)||is_string(anchor))? [anchor] : anchor;
two_d = _attach_geom_2d($parent_geom);
fromvec = two_d? BACK : UP;
orient_anch = _find_anchor(orient, $parent_geom);
spin = default(spin, orient_anch[3]);
dummy2=assert(is_finite(spin));
$attach_to = undef;
$attach_norot = true;
factor = inside?1:-1;
for (thisanch = position_anchors) {
pos_anch = _find_anchor(thisanch, $parent_geom);
init_anch = two_d ? rot(from=orient_anch[2], to=fromvec, p=zrot(-spin,pos_anch[0]))
: rot(spin, from=fromvec, to=orient_anch[2], reverse=true, p=pos_anch[0]);
quant_anch = [for(v=init_anch) sign(round(v))];
$anchor_override = two_d && quant_anch.y!=0 ? [quant_anch.x,factor*quant_anch.y]
: !two_d && quant_anch.z!=0 ? [quant_anch.x,quant_anch.y, factor*quant_anch.z]
: factor*quant_anch;
$attach_anchor = pos_anch;
translate(pos_anch[1]) {
if (two_d)
rot(spin)rot(from=fromvec, to=orient_anch[2])
default_tag("remove",inside) children();
else
rot(spin, from=fromvec, to=orient_anch[2])
default_tag("remove",inside) children();
}
}
}
// Module: attach()
// Synopsis: Attaches children to a parent object at an anchor point and orientation.
// SynTags: Trans
// Topics: Attachments
// See Also: attachable(), position(), face_profile(), edge_profile(), corner_profile()
// Usage:
// PARENT() attach(from, [overlap=], [norot=]) CHILDREN;
// PARENT() attach(from, to, [overlap=], [norot=]) CHILDREN;
// Description:
// Attaches children to a parent object at an anchor point and orientation. Attached objects will
// be overlapped into the parent object by a little bit, as specified by the `$overlap`
// value (0 by default), or by the overriding `overlap=` argument. This is to prevent OpenSCAD
// from making non-manifold objects. You can define `$overlap=` as an argument in a parent
// module to set the default for all attachments to it. For a step-by-step explanation of
// attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
// from = The vector, or name of the parent anchor point to attach to.
// to = Optional name of the child anchor point. If given, orients the child such that the named anchors align together rotationally.
// ---
// overlap = Amount to sink child into the parent. Equivalent to `down(X)` after the attach. This defaults to the value in `$overlap`, which is `0` by default.
// norot = If true, don't rotate children when attaching to the anchor point. Only translate to the anchor point.
// Side Effects:
// `$idx` is set to the index number of each anchor if a list of anchors is given. Otherwise is set to `0`.
// `$attach_anchor` for each `from=` anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor.
// `$attach_to` is set to the value of the `to=` argument, if given. Otherwise, `undef`
// `$attach_norot` is set to the value of the `norot=` argument.
// Example:
// spheroid(d=20) {
// attach(TOP) down(1.5) cyl(l=11.5, d1=10, d2=5, anchor=BOTTOM);
// attach(RIGHT, BOTTOM) down(1.5) cyl(l=11.5, d1=10, d2=5);
// attach(FRONT, BOTTOM, overlap=1.5) cyl(l=11.5, d1=10, d2=5);
// }
module attach(from, to, overlap, norot=false)
{
req_children($children);
assert($parent_geom != undef, "No object to attach to!");
overlap = (overlap!=undef)? overlap : $overlap;
anchors = (is_vector(from)||is_string(from))? [from] : from;
for ($idx = idx(anchors)) {
anchr = anchors[$idx];
anch = _find_anchor(anchr, $parent_geom);
two_d = _attach_geom_2d($parent_geom);
$attach_to = to;
$attach_anchor = anch;
$attach_norot = norot;
olap = two_d? [0,-overlap,0] : [0,0,-overlap];
if (norot || (norm(anch[2]-UP)<1e-9 && anch[3]==0)) {
translate(anch[1]) translate(olap) children();
} else {
fromvec = two_d? BACK : UP;
translate(anch[1]) rot(anch[3],from=fromvec,to=anch[2]) translate(olap) children();
}
}
}
// Section: Tagging
// Module: tag()
// Synopsis: Assigns a tag to an object
// Topics: Attachments
// See Also: force_tag(), recolor(), hide(), show_only(), diff(), intersect()
// Usage:
// PARENT() tag(tag) CHILDREN;
// Description:
// Assigns the specified tag to all of the children. Note that if you want
// to apply a tag to non-tag-aware objects you need to use {{force_tag()}} instead.
// This works by setting the `$tag` variable, but it provides extra error checking and
// handling of scopes. You may set `$tag` directly yourself, but this is not recommended.
// .
// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
// tag = tag string, which must not contain any spaces.
// Side Effects:
// Sets `$tag` to the tag you specify, possibly with a scope prefix.
// Example(3D): Applies the tag to both cuboids instead of having to repeat `$tag="remove"` for each one.
// diff("remove")
// cuboid(10){
// position(TOP) cuboid(3);
// tag("remove")
// {
// position(FRONT) cuboid(3);
// position(RIGHT) cuboid(3);
// }
// }
module tag(tag)
{
req_children($children);
check=
assert(is_string(tag),"tag must be a string")
assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed"));
$tag = str($tag_prefix,tag);
children();
}
// Module: force_tag()
// Synopsis: Assigns a tag to a non-attachable object.
// Topics: Attachments
// See Also: tag(), recolor(), hide(), show_only(), diff(), intersect()
// Usage:
// PARENT() force_tag([tag]) CHILDREN;
// Description:
// You use this module when you want to make a non-attachable or non-BOSL2 module respect tags.
// It applies to its children the tag specified (or the tag currently in force if you don't specify a tag),
// making a final determination about whether to show or hide the children.
// This means that tagging in children's children will be ignored.
// This module is specifically provided for operating on children that are not tag aware such as modules
// that don't use {{attachable()}} or built in modules such as
// - `polygon()`
// - `projection()`
// - `polyhedron()` (or use [`vnf_polyhedron()`](vnf.scad#vnf_polyhedron))
// - `linear_extrude()` (or use [`linear_sweep()`](regions.scad#linear_sweep))
// - `rotate_extrude()`
// - `surface()`
// - `import()`
// - `difference()`
// - `intersection()`
// - `hull()`
// .
// When you use tag-based modules like {{diff()}} with a non-attachable module, the result may be puzzling.
// Any time a test occurs for display of child() that test will succeed. This means that when diff() checks
// to see if it should show a module it will show it, and when diff() checks to see if it should subtract the module
// it will subtract it. The result will be a hole, possibly with zero-thickness edges or faces. In order to
// get the correct behavior, every non-attachable module needs an invocation of force_tag, even ones
// that are not tagged.
// .
// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
// tag = tag string, which must not contain any spaces
// Side Effects:
// Sets `$tag` to the tag you specify, possibly with a scope prefix.
// Example(2D): This example produces the full square without subtracting the "remove" item. When you use non-attachable modules with tags, results are unpredictable.
// diff()
// {
// polygon(square(10));
// move(-[.01,.01])polygon(square(5),$tag="remove");
// }
// Example(2D): Adding force_tag() fixes the model. Note you need to add it to *every* non-attachable module, even the untagged ones, as shown here.
// diff()
// {
// force_tag()
// polygon(square(10));
// force_tag("remove")
// move(-[.01,.01])polygon(square(5));
// }
module force_tag(tag)
{
req_children($children);
check1=assert(is_undef(tag) || is_string(tag),"tag must be a string");
$tag = str($tag_prefix,default(tag,$tag));
assert(undef==str_find($tag," "),str("Tag string \"",$tag,"\" contains a space, which is not allowed"));
if(_is_shown())
show_all()
children();
}
// Module: default_tag()
// Synopsis: Sets a default tag for all children.
// Topics: Attachments
// See Also: force_tag(), recolor(), hide(), show_only(), diff(), intersect()
// Usage:
// PARENT() default_tag(tag) CHILDREN;
// Description:
// Sets a default tag for all of the children. This is intended to be used to set a tag for a whole module
// that is then used outside the module, such as setting the tag to "remove" for easy operation with {{diff()}}.
// The default_tag() module sets the `$tag` variable only if it is not already
// set so you can have a module set a default tag of "remove" but that tag can be overridden by a {{tag()}}
// in force from a parent. If you use {{tag()}} it will override any previously
// specified tag from a parent, which can be very confusing to a user trying to change the tag on a module.
// The `do_tag` parameter allows you to apply a default tag conditionally without having to repeat the children.
// .
// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
// tag = tag string, which must not contain any spaces.
// do_tag = if false do not set the tag.
// Side Effects:
// Sets `$tag` to the tag you specify, possibly with a scope prefix.
// Example(3D): The module thing() is defined with {{tag()}} and the user applied tag of "keep_it" is ignored, leaving the user puzzled.
// module thing() { tag("remove") cuboid(10);}
// diff()
// cuboid(20){
// position(TOP) thing();
// position(RIGHT) tag("keep_it") thing();
// }
// Example(3D): Using default_tag() fixes this problem: the user applied tag does not get overridden by the tag hidden in the module definition.
// module thing() { default_tag("remove") cuboid(10);}
// diff()
// cuboid(20){
// position(TOP) thing();
// position(RIGHT) tag("keep_it") thing();
// }
module default_tag(tag,do_tag=false)
{
if ($tag=="") tag(tag) children();
else children();
}
// Module: tag_scope()
// Synopsis: Creates a new tag scope.
// See Also: tag(), force_tag(), default_tag()
// Topics: Attachments
// Usage:
// tag_scope([scope]) CHILDREN;
// Description:
// Creates a tag scope with locally altered tag names to avoid tag name conflict with other code.
// This is necessary when writing modules because the module's caller might happen to use the same tags.
// Note that if you directly set the `$tag` variable then tag scoping will not work correctly.
// Side Effects:
// `$tag_prefix` is set to the value of `scope=` if given, otherwise is set to a random string.
// Example: In this example the ring module uses "remove" tags which will conflict with use of the same tags by the parent.
// module ring(r,h,w=1,anchor,spin,orient)
// {
// tag_scope("ringscope")
// attachable(anchor,spin,orient,r=r,h=h){
// diff()
// cyl(r=r,h=h)
// tag("remove") cyl(r=r-w,h=h+1);
// children();
// }
// }
// // Calling the module using "remove" tags
// // will conflict with internal tag use in
// // the ring module.
// $fn=32;
// diff(){
// ring(10,7,w=4);
// tag("remove")ring(8,8);
// tag("remove")diff("rem"){
// ring(9.5,8,w=1);
// tag("rem")ring(9.5,8,w=.3);
// }
// }
module tag_scope(scope){
req_children($children);
scope = is_undef(scope) ? rand_str(20) : scope;
assert(is_string(scope), "scope must be a string");
assert(undef==str_find(scope," "),str("Scope string \"",scope,"\" contains a space, which is not allowed"));
$tag_prefix=scope;
children();
}
// Section: Attachment Modifiers
// Module: diff()
// Synopsis: Performs a differencing operation using tags rather than hierarchy to control what happens.
// Topics: Attachments
// See Also: tag(), force_tag(), recolor(), show_only(), hide(), tag_diff(), intersect(), tag_intersect()
// Usage:
// diff([remove], [keep]) PARENT() CHILDREN;
// Description:
// Performs a differencing operation using tags to control what happens. This is specifically intended to
// address the situation where you want differences between a parent and child object, something
// that is impossible with the native difference() module.
// The children to diff are grouped into three categories, regardless of nesting level.
// The `remove` argument is a space delimited list of tags specifying objects to
// subtract. The `keep` argument is a similar list of tags giving objects to be kept.
// Objects not matching either the `remove` or `keep` lists form the third category of base objects.
// To produce its output, diff() forms the union of all the base objects and then
// subtracts all the objects with tags in `remove`. Finally it adds in objects listed in `keep`.
// Attachable objects should be tagged using {{tag()}}
// and non-attachable objects with {{force_tag()}}.
// .
// Remember when using tagged operations with that the operations don't happen in hierarchical order, since
// the point of tags is to break the hierarchy. If you tag an object with a keep tag, nothing will be
// subtracted from it, no matter where it appears because kept objects are unioned in at the end.
// If you want a child of an object tagged with a remove tag to stay in the model it may be
// better to give it a tag that is not a remove tag or a keep tag. Such an object *will* be subject to
// subtractions from other remove-tagged objects.
// .
// Note that `diff()` invokes its children three times.
// .
// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
// remove = String containing space delimited set of tag names of children to difference away. Default: `"remove"`
// keep = String containing space delimited set of tag names of children to keep; that is, to union into the model after differencing is completed. Default: `"keep"`
// Example: Diffing using default tags
// diff()
// cuboid(50) {
// tag("remove") attach(TOP) sphere(d=40);
// tag("keep") attach(CTR) cylinder(h=40, d=10);
// }
// Example: The "hole" items are subtracted from everything else. The other tags can be anything you find convenient.
// diff("hole")
// tag("body")sphere(d=100) {
// tag("pole") zcyl(d=55, h=100); // attach() not needed for center-to-center.
// tag("hole") {
// xcyl(d=55, h=101);
// ycyl(d=55, h=101);
// }
// tag("axle")zcyl(d=15, h=140);
// }
// Example:
// diff(keep="axle")
// sphere(d=100) {
// tag("axle")xcyl(d=40, l=120);
// tag("remove")cuboid([40,120,100]);
// }
// Example: Masking
// diff()
// cube([80,90,100], center=true) {
// edge_mask(FWD)
// rounding_edge_mask(l=max($parent_size)*1.01, r=25);
// }
// Example: Here we subtract the parent object from the child. Because tags propagate to children we need to clear the "remove" tag from the child.
// diff()
// tag("remove")cuboid(10)
// tag("")position(RIGHT+BACK)cyl(r=8,h=9);
// Example(3D,VPR=[104,0,200], VPT=[-0.9,3.03, -0.74], VPD=19,NoAxes,NoScales): A pipe module that subtracts its interior when you call it using diff(). Normally if you union two pipes together, you'll get interfering walls at the intersection, but not here:
// $fn=16;
// // This module must be called by subtracting with "diff"
// module pipe(length, od, id) {
// // Strip the tag the user is using to subtract
// tag("")cylinder(h=length, d=od, center=true);
// // Leave the tag alone here, so this one is removed
// cylinder(h=length+.02, d=id, center=true);
// }
// // Draw some intersecting pipes
// diff(){
// tag("remove"){
// pipe(length=5, od=2, id=1.9);
// zrot(10)xrot(75)
// pipe(length=5, od=2, id=1.9);
// }
// // The orange bar has its center removed
// color("orange") down(1) xcyl(h=8, d=1);