marvin.scad with syntax coloring

This HTML file was generated with Kalle's syntaxcolor.py


   1// Marvin the Martian, that adorable Looney Tunes cartoon character
   2// By Kalle (http://qalle.net)
   3
   4$fn = 180;
   5$vpr = [80, 0, 135];  // pitch, roll, yaw
   6$vpd = 150;
   7
   8BLACK = [.0, .0, .0];
   9MAROON = [.5, .0, .0];
  10RED = [.9, .1, .1];
  11GREEN = [.3, .4, .2];
  12BROWN = [.8, .7, .4];
  13YELLOW = [1, 1, .6];
  14WHITE = [1, 1, 1];
  15
  16HEAD_RADIUS = 8;
  17
  18EYE_LATITUDE = 10;
  19EYE_LONGITUDE = 25;
  20
  21HELMET_THICKNESS = 1;  // also visor thickness
  22HELMET_BOTTOM_SUBTRACT = 3;
  23HELMET_SPACE_RADIUS = 1;
  24COLLAR_HEIGHT = 3;
  25COLLAR_EXTENSION_LENGTH = 5;
  26VISOR_HEIGHT = 5;
  27VISOR_ANGLE = 50;
  28
  29BROOM_ARC = 45;  // degrees
  30BROOM_WIDTH = 5;
  31BROOM_RADIUS_OUTER = 20;
  32BROOM_RADIUS_MIDDLE = 18;
  33BROOM_RADIUS_INNER = 17;
  34BROOMSHAFT_LENGTH = 2;
  35BROOMSHAFT_THICKNESS = 1;
  36BROOM_ANGLE = 45;  // rearwards
  37
  38BODY_BOTTOM_RADIUS = 4;
  39BODY_HEIGHT = 10;  // also arm length
  40LIMB_RADIUS = 1;  // also neck radius
  41LEG_HEIGHT = 10;
  42
  43SKIRT_THICKNESS = 1;
  44SKIRT_RADIUS = 9;
  45SKIRT_PARTS = 7;
  46SKIRT_PART_ARC = 40;  // degrees
  47
  48// arm pitch: 0 = down, 90 = horizontal
  49// arm yaw: 0 = forwards, 90 = to side; no effect if pitch=0
  50LEFT_ARM_PITCH = 50;
  51LEFT_ARM_YAW = 50;
  52RIGHT_ARM_PITCH = 50;
  53RIGHT_ARM_YAW = 50;
  54
  55SHOE_WIDTH = 6;
  56SHOE_LENGTH = 12;
  57SHOE_HEIGHT = 5;
  58SHOESOLE_HEIGHT = .5;
  59SHOE_ANGLE = 30;
  60
  61// derived lengths
  62HELMET_RADIUS = HEAD_RADIUS + HELMET_SPACE_RADIUS + HELMET_THICKNESS;
  63HELMET_DIAMETER = 2 * HELMET_RADIUS;
  64COLLAR_RADIUS_OUTER = sqrt(
  65    pow(HELMET_RADIUS, 2) - pow(HELMET_RADIUS - HELMET_BOTTOM_SUBTRACT, 2)
  66);
  67COLLAR_RADIUS_INNER = sqrt(
  68    pow(HEAD_RADIUS + HELMET_SPACE_RADIUS, 2) -
  69    pow(HELMET_RADIUS - HELMET_BOTTOM_SUBTRACT, 2)
  70);
  71COLLAR_THICKNESS = COLLAR_RADIUS_OUTER - COLLAR_RADIUS_INNER;
  72VISOR_RADIUS = HELMET_RADIUS + HELMET_THICKNESS;
  73NECK_HEIGHT = 2 * LIMB_RADIUS;  // because of shoulders
  74
  75translate([0, 0, HELMET_RADIUS]) {
  76    helmet();
  77    rotate([BROOM_ANGLE, 0, 0]) {
  78        translate([0, 0, HEAD_RADIUS + HELMET_SPACE_RADIUS]) {
  79            broom();
  80        }
  81    }
  82    head();
  83    body();
  84    translate([0, 0, -(HEAD_RADIUS + NECK_HEIGHT + BODY_HEIGHT)]) {
  85        skirt();
  86    }
  87    translate([0, 0, -(HEAD_RADIUS + NECK_HEIGHT + BODY_HEIGHT)]) {
  88        legs();
  89    }
  90    translate([0, 0, -(HEAD_RADIUS + NECK_HEIGHT + BODY_HEIGHT + LEG_HEIGHT)])
  91    {
  92        shoes();
  93    }
  94}
  95
  96module head() {
  97    color(WHITE) {
  98        sphere(HEAD_RADIUS - .001);
  99    }
 100
 101    color(BLACK) {
 102        difference() {
 103            sphere(HEAD_RADIUS);
 104
 105            // subtract eyes
 106            for(x = [-1,1]) {
 107                rotate([EYE_LATITUDE, 0, x * EYE_LONGITUDE]) {
 108                    translate([0, 1.95 * HEAD_RADIUS, 0]) {
 109                        cube(2 * HEAD_RADIUS, true);
 110                    }
 111                }
 112            }
 113        }
 114    }
 115}
 116
 117module helmet() {
 118    color(GREEN) {
 119        difference() {
 120            sphere(HELMET_RADIUS);
 121
 122            // subtract inside
 123            sphere(HEAD_RADIUS + HELMET_SPACE_RADIUS);
 124
 125            // subtract bottom part
 126            translate([0, 0, -(HELMET_DIAMETER - HELMET_BOTTOM_SUBTRACT)]) {
 127                cube(HELMET_DIAMETER, true);
 128            }
 129
 130            // subtract front part
 131            translate([0, 1.1 * HELMET_RADIUS, 0]) {
 132                rotate([0, 90, 0]) {
 133                    cylinder(h = HELMET_DIAMETER, r = HELMET_RADIUS,
 134                    center = true);
 135                }
 136            }
 137        }
 138
 139        // collar
 140        translate([0, 0, -(HELMET_RADIUS - HELMET_BOTTOM_SUBTRACT +
 141        COLLAR_HEIGHT / 2)]) {
 142            rotate([180, 0, 0]) {
 143                halfpipe(COLLAR_HEIGHT, COLLAR_RADIUS_OUTER,
 144                COLLAR_RADIUS_INNER);
 145            }
 146
 147            // front extensions
 148            for(x = [-1, 1]) {
 149                translate([
 150                    x * (COLLAR_RADIUS_OUTER - COLLAR_THICKNESS / 2),
 151                    COLLAR_EXTENSION_LENGTH / 2,
 152                    0
 153                ]) {
 154                    cube([COLLAR_THICKNESS, COLLAR_EXTENSION_LENGTH,
 155                    COLLAR_HEIGHT], true);
 156                }
 157            }
 158        }
 159
 160        // visor
 161        rotate([VISOR_ANGLE, 0, 0]) {
 162            halfpipe(VISOR_HEIGHT, VISOR_RADIUS, HELMET_RADIUS);
 163            for(x=[-1,1]) {
 164                translate([x * (VISOR_RADIUS + HELMET_RADIUS) / 2, 0, 0])
 165                rotate([180, 90, 0]) {
 166                    hemicyl(VISOR_RADIUS - HELMET_RADIUS, VISOR_HEIGHT / 2);
 167                }
 168            }
 169        }
 170    }
 171}
 172
 173module broom() {
 174    rotate([90, 0, 90]) {
 175        translate([0, -BROOM_RADIUS_INNER + HELMET_THICKNESS +
 176        BROOMSHAFT_LENGTH, 0]) {
 177            // outer part
 178            color(YELLOW) {
 179                hollow_wedge(BROOM_WIDTH, BROOM_RADIUS_OUTER,
 180                BROOM_RADIUS_MIDDLE, BROOM_ARC);
 181            }
 182
 183            // inner part
 184            color(BROWN) {
 185                hollow_wedge(BROOM_WIDTH, BROOM_RADIUS_MIDDLE,
 186                BROOM_RADIUS_INNER, BROOM_ARC);
 187            }
 188        }
 189
 190        // shaft
 191        color(BROWN) {
 192            translate([0, (HELMET_THICKNESS + BROOMSHAFT_LENGTH) / 2, 0]) {
 193                rotate([90, 0, 0]) {
 194                    cylinder(h = HELMET_THICKNESS + BROOMSHAFT_LENGTH, r =
 195                    BROOMSHAFT_THICKNESS, center = true);
 196                }
 197            }
 198        }
 199    }
 200}
 201
 202module body() {
 203    color(RED) {
 204        // neck (actually extends to center of head)
 205        translate([0, 0, -(HEAD_RADIUS + NECK_HEIGHT) / 2]) {
 206            cylinder(h = HEAD_RADIUS + NECK_HEIGHT, r = LIMB_RADIUS, center =
 207            true);
 208        }
 209
 210        // shoulders
 211        translate([0, 0, -(HEAD_RADIUS + LIMB_RADIUS)]) {
 212            rotate([0, 90, 0]) {
 213                cylinder(h = 4 * LIMB_RADIUS, r = LIMB_RADIUS, center = true);
 214            }
 215        }
 216
 217        // middle part
 218        translate([0, 0, -(HEAD_RADIUS + NECK_HEIGHT + BODY_HEIGHT / 2)]) {
 219            cylinder(BODY_HEIGHT, BODY_BOTTOM_RADIUS, LIMB_RADIUS, center =
 220            true);
 221        }
 222
 223        // bottom part
 224        translate([0, 0, -(HEAD_RADIUS + NECK_HEIGHT + BODY_HEIGHT)]) {
 225            rotate([180, 0, 0]) {
 226                hemisph(BODY_BOTTOM_RADIUS);
 227            }
 228        }
 229    }
 230
 231    // left shoulder joint, arm, hand
 232    translate([-2 * LIMB_RADIUS, 0, -(HEAD_RADIUS + LIMB_RADIUS)]) {
 233        // shoulder joint
 234        color(RED) {
 235            sphere(LIMB_RADIUS);
 236        }
 237
 238        rotate([LEFT_ARM_PITCH, 0, LEFT_ARM_YAW]) {
 239            arm_and_hand();
 240        }
 241    }
 242
 243    // right shoulder joint, arm, hand
 244    translate([2 * LIMB_RADIUS, 0, -(HEAD_RADIUS + LIMB_RADIUS)]) {
 245        // shoulder joint
 246        color(RED) {
 247            sphere(LIMB_RADIUS);
 248        }
 249
 250        rotate([RIGHT_ARM_PITCH, 0, -RIGHT_ARM_YAW]) {
 251            arm_and_hand();
 252        }
 253    }
 254}
 255
 256module arm_and_hand() {
 257   // arm
 258    translate([0, 0, -BODY_HEIGHT / 2]) {
 259        color(RED) {
 260            cylinder(h = BODY_HEIGHT, r = LIMB_RADIUS, center = true);
 261        }
 262    }
 263
 264    // hand
 265    color(WHITE) {
 266        translate([0, 0, -(BODY_HEIGHT + LIMB_RADIUS / 2)]) {
 267            cylinder(h = LIMB_RADIUS, r = LIMB_RADIUS, center = true);
 268        }
 269
 270        translate([0, 0, -(BODY_HEIGHT + LIMB_RADIUS)]) {
 271            sphere(LIMB_RADIUS);
 272        }
 273    }
 274}
 275
 276module legs() {
 277    color(RED) {
 278        for(x=[-1, 1]) {
 279            translate([x * (BODY_BOTTOM_RADIUS - LIMB_RADIUS), 0, -LEG_HEIGHT
 280            / 2]) {
 281                cylinder(h = LEG_HEIGHT, r = LIMB_RADIUS, center = true);
 282            }
 283        }
 284    }
 285}
 286
 287module shoes() {
 288    for(x=[-1, 1]) {
 289        translate([x * (BODY_BOTTOM_RADIUS - LIMB_RADIUS), 0, 0]) {
 290            rotate([0, 0, -x * SHOE_ANGLE]) {
 291                translate([0, SHOE_LENGTH / 2 - LIMB_RADIUS - .001, -.001]) {
 292                    color(MAROON) {
 293                        translate([0, 0, SHOESOLE_HEIGHT / 2]) {
 294                            scale([1, SHOE_LENGTH / SHOE_WIDTH, 1]) {
 295                                cylinder(h = SHOESOLE_HEIGHT, r = SHOE_WIDTH /
 296                                2, center = true);
 297                            }
 298                        }
 299                    }
 300
 301                    color(WHITE) {
 302                        translate([0, 0, SHOESOLE_HEIGHT]) {
 303                            scale([SHOE_WIDTH / 2, SHOE_LENGTH / 2, SHOE_HEIGHT
 304                            / 2]) {
 305                                hemisph(1);
 306                            }
 307                        }
 308                    }
 309                }
 310            }
 311        }
 312    }
 313}
 314
 315module skirt() {
 316    color(GREEN) {
 317        for(angle = [0 : SKIRT_PARTS - 1]) {
 318            rotate(angle * 360 / SKIRT_PARTS) {
 319                wedge(SKIRT_THICKNESS, SKIRT_RADIUS, SKIRT_PART_ARC);
 320            }
 321        }
 322    }
 323}
 324
 325module hemicyl(h, r) {
 326    // hemicylinder (no rear part); h=height, r=radius
 327
 328    difference() {
 329        cylinder(h = h, r = r, center = true);
 330        translate([0, -r / 2, 0]) {
 331            cube([2 * r, r, h + .001], center = true);
 332        }
 333    }
 334}
 335
 336module halfpipe(h, ro, ri) {
 337    // halfpipe, a.k.a. hollow hemicylinder (no rear part);
 338    // h=height, ro/ri=outer/inner radius
 339
 340    difference() {
 341        cylinder(h = h, r = ro, center = true);
 342        cylinder(h = h + .001, r = ri, center = true);
 343        translate([0, -ro / 2, 0]) {
 344            cube([2 * ro, ro, h + .001], center = true);
 345        }
 346    }
 347}
 348
 349module wedge(h, r, angle) {
 350    // wedge; h=height, r=radius, a=angle (0-360)
 351
 352    if(angle <= 180) {
 353        intersection() {
 354            cylinder(h = h, r = r, center = true);
 355            rotate(-angle / 2) {
 356                translate([-r / 2, 0, 0]) {
 357                    cube([r, 2 * r, h + .001], center = true);
 358                }
 359            }
 360            rotate(angle / 2) {
 361                translate([r / 2, 0, 0]) {
 362                    cube([r, 2 * r, h + .001], center = true);
 363                }
 364            }
 365        }
 366    } else {
 367        difference() {
 368            cylinder(h = h, r = r, center = true);
 369            intersection() {
 370                rotate(angle / 2) {
 371                    translate([-r / 2, 0, 0]) {
 372                        cube([r, 2 * r, h + .001], center = true);
 373                    }
 374                }
 375                rotate(-angle / 2) {
 376                    translate([r / 2, 0, 0]) {
 377                        cube([r, 2 * r, h + .001], center = true);
 378                    }
 379                }
 380            }
 381        }
 382    }
 383}
 384
 385module hollow_wedge(h, ro, ri, angle) {
 386    // hollow wedge, i.e. a circle arc with width and thickness;
 387    // h=height, ro/ri=outer/inner radius, a=angle (0-360)
 388
 389    difference() {
 390        wedge(h, ro, angle);
 391        cylinder(h = h + .001, r = ri, center = true);
 392    }
 393}
 394
 395module hemisph(r) {
 396    // hemisphere (no bottom part); r=radius
 397
 398    difference() {
 399        sphere(r);
 400        translate([0, 0, -r]) {
 401            cube(2 * r, center = true);
 402        }
 403    }
 404}