1//////////////////////////////////////////////////////////////////////
  2// LibFile: color.scad
  3//   HSV and HSL conversion, rainbow() module for coloring multiple objects. 
  4//   The recolor() and color_this() modules allow you to change the color
  5//   of previously colored attachable objects.  
  6// Includes:
  7//   include <BOSL2/std.scad>
  8// FileGroup: Basic Modeling
  9// FileSummary: HSV and HSL conversion, color multiple objects, change color of objects
 10// FileFootnotes: STD=Included in std.scad
 11//////////////////////////////////////////////////////////////////////
 12
 13
 14use <builtins.scad>
 15
 16// Section: Coloring Objects
 17
 18// Module: recolor()
 19// Synopsis:  Sets the color for attachable children and their descendants.
 20// SynTags: Trans
 21// Topics: Attachments
 22// See Also: color_this(), hsl(), hsv()
 23// Usage:
 24//   recolor([c]) CHILDREN;
 25// Description:
 26//   Sets the color for attachable children and their descendants, down until another {{recolor()}}
 27//   or {{color_this()}}.  This only works with attachables and you cannot have any color() modules
 28//   above it in any parents, only other {{recolor()}} or {{color_this()}} modules.  This works by
 29//   setting the special `$color` variable, which attachable objects make use of to set the color. 
 30// Arguments:
 31//   c = Color name or RGBA vector.  Default: The default color in your color scheme. 
 32// Side Effects:
 33//   Changes the value of `$color`.
 34//   Sets the color of child attachments.
 35// Example:
 36//   cuboid([10,10,5])
 37//     recolor("green")attach(TOP,BOT) cuboid([9,9,4.5])
 38//       attach(TOP,BOT) cuboid([8,8,4])
 39//         recolor("purple") attach(TOP,BOT) cuboid([7,7,3.5])
 40//           attach(TOP,BOT) cuboid([6,6,3])
 41//             recolor("cyan")attach(TOP,BOT) cuboid([5,5,2.5])
 42//               attach(TOP,BOT) cuboid([4,4,2]);
 43module recolor(c="default")
 44{
 45    req_children($children);  
 46    $color=c;
 47    children();
 48}
 49
 50
 51// Module: color_this()
 52// Synopsis: Sets the color for children at the current level only.
 53// SynTags: Trans
 54// Topics: Attachments
 55// See Also: recolor(), hsl(), hsv()
 56// Usage:
 57//   color_this([c]) CHILDREN;
 58// Description:
 59//   Sets the color for children at one level, reverting to the previous color for further descendants.
 60//   This works only with attachables and you cannot have any color() modules above it in any parents,
 61//   only recolor() or other color_this() modules.  This works using the `$color` and `$save_color` variables,
 62//   which attachable objects make use of to set the color. 
 63// Arguments:
 64//   c = Color name or RGBA vector.  Default: the default color in your color scheme
 65// Side Effects:
 66//   Changes the value of `$color` and `$save_color`.
 67//   Sets the color of child attachments.
 68// Example:
 69//   cuboid([10,10,5])
 70//     color_this("green")attach(TOP,BOT) cuboid([9,9,4.5])
 71//       attach(TOP,BOT) cuboid([8,8,4])
 72//         color_this("purple") attach(TOP,BOT) cuboid([7,7,3.5])
 73//           attach(TOP,BOT) cuboid([6,6,3])
 74//             color_this("cyan")attach(TOP,BOT) cuboid([5,5,2.5])
 75//               attach(TOP,BOT) cuboid([4,4,2]);
 76module color_this(c="default")
 77{
 78  req_children($children);  
 79  $save_color=default($color,"default");
 80  $color=c;
 81  children();
 82}
 83
 84
 85// Module: rainbow()
 86// Synopsis: Iterates through a list, displaying children in different colors.
 87// SynTags: Trans
 88// Topics: List Handling
 89// See Also: hsl(), hsv()
 90// Usage:
 91//   rainbow(list,[stride],[maxhues],[shuffle],[seed]) CHILDREN;
 92// Description:
 93//   Iterates the list, displaying children in different colors for each list item.  The color
 94//   is set using the color() module, so this module is not compatible with {{recolor()}} or
 95//   {{color_this()}}.  This is useful for debugging regions or lists of paths. 
 96// Arguments:
 97//   list = The list of items to iterate through.
 98//   stride = Consecutive colors stride around the color wheel divided into this many parts.
 99//   maxhues = max number of hues to use (to prevent lots of indistinguishable hues)
100//   shuffle = if true then shuffle the hues in a random order.  Default: false
101//   seed = seed to use for shuffle
102// Side Effects:
103//   Sets the color to progressive values along the ROYGBIV spectrum for each item.
104//   Sets `$idx` to the index of the current item in `list` that we want to show.
105//   Sets `$item` to the current item in `list` that we want to show.
106// Example(2D):
107//   rainbow(["Foo","Bar","Baz"]) fwd($idx*10) text(text=$item,size=8,halign="center",valign="center");
108// Example(2D):
109//   rgn = [circle(d=45,$fn=3), circle(d=75,$fn=4), circle(d=50)];
110//   rainbow(rgn) stroke($item, closed=true);
111module rainbow(list, stride=1, maxhues, shuffle=false, seed)
112{
113    req_children($children);  
114    ll = len(list);
115    maxhues = first_defined([maxhues,ll]);
116    huestep = 360 / maxhues;
117    huelist = [for (i=[0:1:ll-1]) posmod(i*huestep+i*360/stride,360)];
118    hues = shuffle ? shuffle(huelist, seed=seed) : huelist;
119    for($idx=idx(list)) {
120        $item = list[$idx];
121        hsv(h=hues[$idx]) children();
122    }
123}
124
125
126// Module: color_overlaps()
127// Synopsis: Shows ghostly children, with overlaps highlighted in color.
128// SynTags: Trans
129// Topics: Debugging
130// See Also: rainbow(), debug_vnf()
131// Usage:
132//   color_overlaps([color]) CHILDREN;
133// Description:
134//   Displays the given children in ghostly transparent gray, while the places where the
135//   they overlap are highlighted with the given color.
136// Arguments:
137//   color = The color to highlight overlaps with.  Default: "red"
138// Example(2D): 2D Overlaps
139//   color_overlaps() {
140//       circle(d=50);
141//       left(20) circle(d=50);
142//       right(20) circle(d=50);
143//   }
144// Example(): 3D Overlaps
145//   color_overlaps() {
146//       cuboid(50);
147//       left(30) sphere(d=50);
148//       right(30) sphere(d=50);
149//       xcyl(d=10,l=120);
150//   }
151module color_overlaps(color="red") {
152    pairs = [for (i=[0:1:$children-1], j=[i+1:1:$children-1]) [i,j]];
153    for (p = pairs) {
154        color(color) {
155            intersection() {
156                children(p.x);
157                children(p.y);
158            }
159        }
160    }
161    %children();
162}
163
164
165// Section: Colorspace Conversion
166
167// Function&Module: hsl()
168// Synopsis: Sets the color of children to a specified hue, saturation, lightness and optional alpha channel value.
169// SynTags: Trans
170// See Also: hsv(), recolor(), color_this()
171// Topics: Colors, Colorspace
172// Usage:
173//   hsl(h,[s],[l],[a]) CHILDREN;
174//   rgb = hsl(h,[s],[l],[a]);
175// Description:
176//   When called as a function, returns the `[R,G,B]` color for the given hue `h`, saturation `s`, and
177//   lightness `l` from the HSL colorspace.  If you supply the `a` value then you'll get a length 4
178//   list `[R,G,B,A]`.  When called as a module, sets the color using the color() module to the given
179//   hue `h`, saturation `s`, and lightness `l` from the HSL colorspace.
180// Arguments:
181//   h = The hue, given as a value between 0 and 360.  0=red, 60=yellow, 120=green, 180=cyan, 240=blue, 300=magenta.
182//   s = The saturation, given as a value between 0 and 1.  0 = grayscale, 1 = vivid colors.  Default: 1
183//   l = The lightness, between 0 and 1.  0 = black, 0.5 = bright colors, 1 = white.  Default: 0.5
184//   a = Specifies the alpha channel as a value between 0 and 1.  0 = fully transparent, 1=opaque.  Default: 1
185// Side Effects:
186//   When called as a module, sets the color of all children.
187// Example:
188//   hsl(h=120,s=1,l=0.5) sphere(d=60);
189// Example:
190//   rgb = hsl(h=270,s=0.75,l=0.6);
191//   color(rgb) cube(60, center=true);
192function hsl(h,s=1,l=0.5,a) =
193    let(
194        h=posmod(h,360)
195    ) [
196        for (n=[0,8,4])
197          let(k=(n+h/30)%12)
198          l - s*min(l,1-l)*max(min(k-3,9-k,1),-1),
199        if (is_def(a)) a
200    ];
201
202module hsl(h,s=1,l=0.5,a=1)
203{
204  req_children($children);  
205  color(hsl(h,s,l),a) children();
206}
207
208
209// Function&Module: hsv()
210// Synopsis: Sets the color of children to a hue, saturation, value and optional alpha channel value.
211// SynTags: Trans
212// See Also: hsl(), recolor(), color_this()
213// Topics: Colors, Colorspace
214// Usage:
215//   hsv(h,[s],[v],[a]) CHILDREN;
216//   rgb = hsv(h,[s],[v],[a]);
217// Description:
218//   When called as a function, returns the `[R,G,B]` color for the given hue `h`, saturation `s`, and
219//   value `v` from the HSV colorspace.  If you supply the `a` value then you'll get a length 4 list
220//   `[R,G,B,A]`.  When called as a module, sets the color using the color() module to the given hue
221//   `h`, saturation `s`, and value `v` from the HSV colorspace.
222// Arguments:
223//   h = The hue, given as a value between 0 and 360.  0=red, 60=yellow, 120=green, 180=cyan, 240=blue, 300=magenta.
224//   s = The saturation, given as a value between 0 and 1.  0 = grayscale, 1 = vivid colors.  Default: 1
225//   v = The value, between 0 and 1.  0 = darkest black, 1 = bright.  Default: 1
226//   a = Specifies the alpha channel as a value between 0 and 1.  0 = fully transparent, 1=opaque.  Default: 1
227// Side Effects:
228//   When called as a module, sets the color of all children.
229// Example:
230//   hsv(h=120,s=1,v=1) sphere(d=60);
231// Example:
232//   rgb = hsv(h=270,s=0.75,v=0.9);
233//   color(rgb) cube(60, center=true);
234function hsv(h,s=1,v=1,a) =
235    assert(s>=0 && s<=1)
236    assert(v>=0 && v<=1)
237    assert(is_undef(a) || a>=0 && a<=1)
238    let(
239        h = posmod(h,360),
240        c = v * s,
241        hprime = h/60,
242        x = c * (1- abs(hprime % 2 - 1)),
243        rgbprime = hprime <=1 ? [c,x,0]
244                 : hprime <=2 ? [x,c,0]
245                 : hprime <=3 ? [0,c,x]
246                 : hprime <=4 ? [0,x,c]
247                 : hprime <=5 ? [x,0,c]
248                 : hprime <=6 ? [c,0,x]
249                 : [0,0,0],
250        m=v-c
251    )
252    is_def(a) ? point4d(add_scalar(rgbprime,m),a)
253              : add_scalar(rgbprime,m);
254
255module hsv(h,s=1,v=1,a=1)
256{
257    req_children($children);
258    color(hsv(h,s,v),a) children();
259}    
260
261
262
263// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap