Blender rigging

Last Updated or created 2023-02-04

Last week i’ve been learning about rigging in blender.
Before that i’ve learned about bumpmapping .. cool stuff

Bumpmapping

Below is a little shorthand/lab notes/screenshot dump, there are far better tutorials on the interwebs!

First add Rigify Addon

Get a model in T pose, and add armature.
(Not my model, just used for demo purposes)

Kodi push pictures/directory from website

Last Updated or created 2023-01-26

For a new project i’m using parts of my Photo Manager.

UPDATE: Random photo push from directory in one script.

I will post the Minimal code for the following:

  • Caching thumbnail generation
  • Drag and drop image for displaying on a kodi instance
  • Button to start slideshow of a directory
  • Open image in browser with obfuscated image url (not shown in movie)
  • Stop image playing

Json RPC used:

# Playing a single file
curl -H "content-type:application/json" -i -X POST -d '{"jsonrpc":"2.0","id":1,"method":"Player.Open","params":{"item":{"file":"'.$dir2.'"}}}' http://KODI-IP:8080/jsonrpc &';

# Playing a directory
curl -H "content-type:application/json" -i -X POST -d '{"jsonrpc":"2.0","id":1,"method":"Player.Open","params":{"item":{"directory":"/path/'.$dir.'"}}}' http://KODI-IP:8080/jsonrpc &';

# Stop playing (i'm stopping player 1 and 2) You can query which player is active, this works also 
curl -H "content-type:application/json" -i -X POST -d '{"jsonrpc": "2.0", "method": "Player.Stop", "params": { "playerid": 1 }, "id": 1}' http://10.1.0.73:8080/jsonrpc';
Kodi enable http control and disable authentication (if you want to use authentication, change the curl commands accordingly)

Below the multiple PHP files, i’ve removed a lot of code specific for my manager.

By the way, I love this trick:

header("HTTP/1.1 204 NO CONTENT");

I’ve you put this in the top of your php script which is linked from the first page, you won’t open this link in your browser, but it gets executed nevertheless!

::::::::::::::
push.php
::::::::::::::
<!DOCTYPE html>
<html>
<head>
</head>
<body>

<div id="dropbox">Drop Image</div><br>
<a href="playpush.php?dir=TEMP/Sake">playdir</a> 
<a href="stoppush.php">stop</a> 

# Here i have a generated part to list my photoalbum photos in this format

<a href='getimg.php?imagepath=/mnt/fileserver/TEMP/1.jpg'><img src='getimg2.php?imagepath=/mnt/fileserver/TEMP/1.jpg' width=300 title='1.jpg'></a>
<a href='getimg.php?imagepath=/mnt/fileserver/TEMP/2.jpg'><img src='getimg2.php?imagepath=/mnt/fileserver/TEMP/2.jpg' width=300 title='2.jpg'></a>

<script
    type="text/javascript"
    src="javascript2.js"
    
  ></script>
</body></html>
::::::::::::::
getimg.php - Displays photo in browser (forgotten in movie)
::::::::::::::
<?php
Header("Content-Type: image/jpeg"); 
$file = $_GET['imagepath'];
$file = str_replace("%20", "\ ", $file);
$file = str_replace("(", "\(", $file);
$file = str_replace(")", "\)", $file);
$log =  'imggetlog';
file_put_contents($log, $file, FILE_APPEND);
file_put_contents($log, "\n\r", FILE_APPEND);
header('Content-Length: ' . filesize($file));
readfile($file);
?>

::::::::::::::
getimg2.php - makes a caching thumbnail 
/long/image/path/to/photo.jpg -> cachedir/longimagepathtophoto.jpg
::::::::::::::
<?php
Header("Content-Type: image/jpeg"); 
$file = $_GET['imagepath'];
$file = str_replace("%28", "\(", $file);
$file = str_replace("%29", "\)", $file);
$file = str_replace("%20", "\ ", $file);

$cachename = str_replace("/", "", $file);
$cachename = str_replace(" ", "", $cachename);
$cachename = "cachedir/$cachename";
$log =  'imggetlog';
file_put_contents($log, $file, FILE_APPEND);
file_put_contents($log, "\n\r", FILE_APPEND);
if (!file_exists("$cachename")) {

exec("convert -resize 300x300 \"$file\" \"$cachename\"");
}
header('Content-Length: ' . filesize("$cachename"));
readfile("$cachename");
?>

::::::::::::::
playpush.php - Pushes DIRECTORY play to Kodi
::::::::::::::
<?PHP
header("HTTP/1.1 204 NO CONTENT");

header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP 1.1.
header("Pragma: no-cache"); // HTTP 1.0.
header("Expires: 0"); // Proxies.

$dir=$_GET['dir'];

$command='nohup curl -H "content-type:application/json" -i -X POST -d \'{"jsonrpc":"2.0","id":1,"method":"Player.Open","params":{"item":{"directory":"/mnt/fileserver/'.$dir.'"}}}\' http://IPKODI:8080/jso
nrpc &';
exec($command, $output, $retval);
?>

::::::::::::::
stoppush.php - stops displaying
::::::::::::::
<?PHP
header("HTTP/1.1 204 NO CONTENT");

header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP 1.1.
header("Pragma: no-cache"); // HTTP 1.0.
header("Expires: 0"); // Proxies.

$command='curl -H "content-type:application/json" -i -X POST -d \'{"jsonrpc": "2.0", "method": "Player.Stop", "params": { "playerid": 1 }, "id": 1}\' http://IPKODI:8080/jsonrpc';
exec($command, $output, $retval);
$command='curl -H "content-type:application/json" -i -X POST -d \'{"jsonrpc": "2.0", "method": "Player.Stop", "params": { "playerid": 2 }, "id": 1}\' http://IPKODI:8080/jsonrpc';
exec($command, $output, $retval);
?>
::::::::::::::
javascript2.js
::::::::::::::
function getAllElementsWith(tag, attribute, value)
{
  var matchingElements = [];
  var allElements = document.getElementsByTagName(tag);
  for (var i = 0; i < allElements.length; i++)
  {
    if (value.indexOf(allElements[i].getAttribute(attribute)) != -1)
    {
      // Element exists with attribute. Add to array.
      matchingElements.push(allElements[i]);
    }
  }
  return matchingElements;
}

// onDrop
function onDrop(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    var imageUrl = evt.dataTransfer.getData("URL");
    var links = getAllElementsWith("a", "href", imageUrl);
    var image;
     console.log(links, evt);
    
    if(links.length){
        image = links[0].getElementsByTagName("img");
        if(image.length)
            imageUrl = image[0].getAttribute("src");
        else
            imageUrl = "#no-image";
    }
    
///    alert(imageUrl);
var res = imageUrl.replace(/getimg/, "pushplay2");

location.href = (res);

};

// onDragOver
function onDragOver(evt){
    evt.preventDefault();
}

var dropbox = document.getElementById('dropbox');
dropbox.addEventListener('drop', onDrop);
dropbox.addEventListener("dragover", onDragOver, false);

Random picture push

file=$(find /mnt/fileserver/examples -type f  | shuf | head -1)
post_data="{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"Player.Open\",\"params\":{\"item\":{\"file\":\"$file\"}}}"
curl --user user:pass  -H "content-type:application/json" -i -X POST --data "${post_data}" http://KODI-IP:8080/jsonrpc

Below a example of what your can do with the code above

Quick previewing negatives using OBS

Last Updated or created 2023-01-18

I’m using a camlink to connect a Nikon camera to my PC, but you could use your webcam also for this trick.

Now i can activate the full screen projector in OBS to preview and sort the negatives on a big screen. (right click on you preview screen)

When connecting a camera to OBS you initially have a “negative” image.

Below trick inverts your image, after that you can use your camera controls or OBS colour correction filter to adjust.

I’m using gimp to invert the LUT png image, but most image editors can invert an image.

A LUTs is a LookUp Table. Color presets/filters for adjusting the image.

Hardcore method .. command line

convert input.png -channel RGB -negate invert.png

ARDUINO CONCERTINA – POC 2

Last Updated or created 2023-01-17

See also

Potmeter needs some tweaking

New code

#include <Keypad.h>
int buzzer=9;
int lastsensorread;
int prevkey;
int push=450;
int pull=550;

const byte ROWS = 8; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'#','0','*'},
  {'A','B','C'},
  {'D','E','F'},
  {'G','H','I'},
  {'J','K','L'}
};
byte rowPins[ROWS] = {5, 6, 7, 8, 10, 11, 12, 13}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {2, 3, 4 }; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup(){
  Serial.begin(9600);
  pinMode(buzzer,OUTPUT);

}
  
void loop(){
  char key = keypad.getKey();
//  if (key == NO_KEY){
 //   key = prevkey;
   //   }
      
  
 // if (key != NO_KEY){
    int freq = 0;
    int sensorValue = analogRead(A0);
    //sensorValue = ((sensorValue+4)/5)*5;



    
    if (sensorValue > push && sensorValue < pull   ) {
    // Serial.println("No pull or push");
    noTone(buzzer);
    }
    else if (key == NO_KEY){
      switch (keypad.getState()){
            case RELEASED:
             noTone(buzzer);
      }
    }
    else 
    {
    if (key == '1' && sensorValue < push ) { tone(buzzer,415); }; // G push
    if (key == '1' && sensorValue > pull ) { tone(buzzer,466); }; // A pull
    if (key == '2' && sensorValue < push ) { tone(buzzer,392); }; // G push
    if (key == '2' && sensorValue > pull ) { tone(buzzer,440); }; // A pull
    if (key == '3' && sensorValue < push ) { tone(buzzer,587); }; // G push
    if (key == '3' && sensorValue > pull ) { tone(buzzer,659); }; // A pull
    
    if (key == '4' && sensorValue < push ) { tone(buzzer,440); };
    if (key == '4' && sensorValue > pull ) { tone(buzzer,392); };
    if (key == '5' && sensorValue < push ) { tone(buzzer,329); };
    if (key == '5' && sensorValue > pull ) { tone(buzzer,349); };
    if (key == '6' && sensorValue < push ) { tone(buzzer,493); };
    if (key == '6' && sensorValue > pull ) { tone(buzzer,523); };
      //8l f/e
    if (key == '8' && sensorValue < push ) { tone(buzzer,261); };
    if (key == '8' && sensorValue > pull ) { tone(buzzer,587); };

    if (key == 'A' && sensorValue < push ) { tone(buzzer,783); };
    if (key == 'A' && sensorValue > pull ) { tone(buzzer,739); };
    if (key == 'B' && sensorValue < push ) { tone(buzzer,523); };
    if (key == 'B' && sensorValue > pull ) { tone(buzzer,493); };

    if (key == 'D' && sensorValue < push ) { tone(buzzer,987); };
    if (key == 'D' && sensorValue > pull ) { tone(buzzer,880); };
    if (key == 'E' && sensorValue < push ) { tone(buzzer,659); };
    if (key == 'E' && sensorValue > pull ) { tone(buzzer,587); };

    if (key == 'H' && sensorValue < push ) { tone(buzzer,783); };
    if (key == 'H' && sensorValue > pull ) { tone(buzzer,698); };
    //tone(buzzer,freq);
    }
    Serial.println(sensorValue);
    Serial.println(key);
    Serial.println(freq);
 //   lastsensorread = sensorValue;
    prevkey = key;
      
 // }
}

Wanted to make a mini Sid Player, and failed

Last Updated or created 2024-07-03

UPDATE: https://www.henriaanstoot.nl/2024/05/30/c64-mobile-sid-player-using-raspberry/

I’ve got an old Speaker Phat, and a Raspberry Zero

An audio add-on board for Raspberry ( same size as the Zero )
Connections

My initial idea was to have the “High Voltage Sid Collection” (Downloaded the 55000 pack)
On a mini device, battery operated and with a little keypad.

On the keypad i can select the Sidtune to play, or pressing
A and a number the Sids from a certain artist.

The display gives you information about the tune being played.
( The display has an I2C hat to convert 8bits to I2C )

See pinout phat above.
I’ve got three choices for I2C connection (green/blue to the Phat)

  • Direct connect and use different addresses
  • Use a I2C hub and different addresses
  • Define a secondary I2C on the raspberry

So I made the first test setup …

Underrun occurred .. So back to the drawingboard.
I probably need a better Audio Hat.
First to try .. Zero fast enough for sidplay2?
Maybe audio over hdmi works??

A Harp in the house again. JOY!

Last Updated or created 2023-07-18

First I have to replace 4 strings.

Apparently F4 B4 F3 and D1

I’ve replaced strings before so that’s no problem. Starting all over again.

Our Folk Band Harp player wants to due some duets. So lets get playin’

http://pinnerstringquartet.com/

UPDATE: 20230119
Replaced the 4 strings yesterday, not fully tuned yet.
Gave the string some time to rest.
Looking at the harp this morning. G4 was broken .. d*mn

UPDATE: 20230126
Replaced also the G4, all done.
Tuning this kind of Harp. (Salvi Lever harp)
You have to put all levers down, and tune them from lowest note to highest.
The A B and E strings have to be tuned flat!
So by using the levers you can play in all kinds of different keys.

StringCDEFGAB
TunerCDD#/EbFGG#/AbA#/Bb

Very nice overview of the keys (harp-school.com)

Computer cards i’ve owned / used

Last Updated or created 2023-01-13

Only cards worth mentioning.
I will add more information to this page

Graphics:

Hercules ???? – Did a lot of machinecode on this one. (Which?)
CGA/EGA Card ??? – Machinecode hacking
VGA .. first card also machine code hacking
Matrox
Some cards i knew a lot about, i did some manipulations using assembly that were very interesting, but only worked on that specific brand.

16 Bit ISA VGA card that is compatibe with 8bits slots for my XT.
see https://www.henriaanstoot.nl/2022/11/16/hercules-to-vga/

Sound:

I’ve bought a lot of Crystal Soundcards, there were breeze to install and use under linux, way back when it was hard to get supported hardware.



Firewire card (for Studio equipment):

Our old trusty mixer

Videocapture:

Video blaster – Which i used to record video (Like ‘Sepp en fash vervelen zich nooit’) and my DIY controllable webcam.

Video Blaster


Pinnacle PCTV- Brooktree Bt848

I used the firewire connection to get the footage of my Canon Video Camera

Firewire cable
Not my card but comparable


Hauppauge WinTV PVR 350


Basetech BR116 (Current) – RCA

Not really a “card”


Camlink 4K (Current) – hdmi

Other:

Stallion RS-232 card for connecting multiple serial terminals.
(Icecrew chat server on Lan Parties)

insmod istallion board0=brumby,0x350,0xcc000
/usr/lib/stallion/stlload -i 2681.sys


Wyse Multiport Serial Card

Ramvantage 16bits ISA memory expansion

PC Hardware diagnostics card

Could not find any information on this card

Catweasel

The Catweasel is a family of enhanced floppy-disk controllers from German company Individual Computers. These controllers are designed to allow more recent computers, such as PCs, to access a wide variety of older or non-native disk formats using standard floppy drives.

You could connect joysticks and there is a socket for a SID chip on the card.

Arduino Concertina – POC

Last Updated or created 2023-01-17

As mentioned in post below

Update: https://www.henriaanstoot.nl/2023/01/17/arduino-concertina-poc-2/

So i’ve bought some needed parts and made a proof of concept.

First to try : 3×4 matrix .. later the full 30 keys version
First part Bella Ciao

Arduino Code
Needs https://playground.arduino.cc/Code/Keypad/ keypad library
Not all buttons are configured with frequencies … yet

#include <Keypad.h>
int buzzer=9;
int lastsensorread;
int prevkey;
int push=400;
int pull=600;

const byte ROWS = 8; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'#','0','*'},
  {'A','B','C'},
  {'D','E','F'},
  {'G','H','I'},
  {'J','K','L'}
};
byte rowPins[ROWS] = {5, 6, 7, 8, 10, 11, 12, 13}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {2, 3, 4 }; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup(){
  Serial.begin(9600);
  pinMode(buzzer,OUTPUT);

}
  
void loop(){
  char key = keypad.getKey();
//  if (key == NO_KEY){
 //   key = prevkey;
   //   }
      
  
 // if (key != NO_KEY){
    int freq = 0;
    int sensorValue = analogRead(A0);
    //sensorValue = ((sensorValue+4)/5)*5;



    
    if (sensorValue > push && sensorValue < pull   ) {
    // Serial.println("No pull or push");
    noTone(buzzer);
    }
    else 
    {
    if (key == '1' && sensorValue < push ) { tone(buzzer,415); }; // G push
    if (key == '1' && sensorValue > pull ) { tone(buzzer,466); }; // A pull
    if (key == '2' && sensorValue < push ) { tone(buzzer,392); }; // G push
    if (key == '2' && sensorValue > pull ) { tone(buzzer,440); }; // A pull
    if (key == '3' && sensorValue < push ) { tone(buzzer,587); }; // G push
    if (key == '3' && sensorValue > pull ) { tone(buzzer,659); }; // A pull
    
    if (key == '4' && sensorValue < push ) { tone(buzzer,440); };
    if (key == '4' && sensorValue > pull ) { tone(buzzer,392); };
    if (key == '5' && sensorValue < push ) { tone(buzzer,329); };
    if (key == '5' && sensorValue > pull ) { tone(buzzer,349); };
    if (key == '6' && sensorValue < push ) { tone(buzzer,493); };
    if (key == '6' && sensorValue > pull ) { tone(buzzer,523); };
      //8l f/e
    if (key == '8' && sensorValue < push ) { tone(buzzer,261); };
    if (key == '8' && sensorValue > pull ) { tone(buzzer,587); };

    if (key == 'A' && sensorValue < push ) { tone(buzzer,783); };
    if (key == 'A' && sensorValue > pull ) { tone(buzzer,739); };
    if (key == 'B' && sensorValue < push ) { tone(buzzer,523); };
    if (key == 'B' && sensorValue > pull ) { tone(buzzer,493); };

    if (key == 'D' && sensorValue < push ) { tone(buzzer,987); };
    if (key == 'D' && sensorValue > pull ) { tone(buzzer,880); };
    if (key == 'E' && sensorValue < push ) { tone(buzzer,659); };
    if (key == 'E' && sensorValue > pull ) { tone(buzzer,587); };
    
    //tone(buzzer,freq);
    }
    Serial.println(sensorValue);
    Serial.println(key);
    Serial.println(freq);
 //   lastsensorread = sensorValue;
    prevkey = key;
      
 // }
}

Lets make a test holder from pieces of wood, when i’ve got the sizes correct, i’ll 3D print something

Rendering images from dos till now

Last Updated or created 2023-01-17

I like creating Art, painting, drawing, sculpting but also computer generated. Most of the things i create are for adults. So i can’t post my best work.

Below are some of the programs i’ve used, these are NOT the generic drawing programs like Gimp, Photoshop or alike.
Ony programs that generate (photorealistic) graphics.
I tried to start with the oldest ending with Blender. There is an overlap and sometimes i’m not sure when I used these programs.

BMRT

Blue Moon Rendering Tools, or BMRT, was one of the most famous RenderMan-compliant photorealistic rendering systems.

Could not find examples?

3DS4

3D Studio, not to be confused with the later “3d Studio Max” product, is a DOS-based tool from Autodesk for creating 3d models and animations.

Vivid

This is the Vivid raytracer. It will only run in a dos environment.
http://paulbourke.net/dataformats/vivid/

Povray

The Persistence of Vision Ray Tracer, most commonly acronymed as POV-Ray, is a cross-platform ray-tracing program that generates images from a text-based scene description.

Example povray source
megapov -geometry 1600×1200 +L /usr/share/povray/include/ +L /data/povray/megapov-1.2.1/include/ +L /data/povray/povray-3.7.0.RC3/include/ ./mine.pov

#include "colors.inc"
#include "metals.inc"
#include "woods.inc"

global_settings { ambient_light rgb<0,0,0> }

#declare Jump_Start  = 0.5;
#declare Jump_Height = 7;
#if (clock < Jump_Start )
 #declare Camera_Y = 1;
#else
 #declare Camera_Y = 1
   + Jump_Height*
     0.5*(1-cos(4*pi*(clock-Jump_Start)));
#end

camera {
 angle 38
 location <0.3,Camera_Y,-3>
 right x*image_width/image_height
 look_at <0,1,0>
 rotate<0,-360*(clock+0.01),0>
} 

plane { 
  y, 0  
  pigment { checker color LightGray color White } // checkered floor
}

// deze later spotlight maken
//light_source { <10, 10, -10> color White }
//light_source { <-10, 5, -15> color White }

light_source
{ <100, 200, -150>/50, 1
  fade_distance 6 fade_power 2
  area_light x*3, y*3, 12, 12 circular orient adaptive 0
}


light_source {
  <3,7,-4>     // position
  color White
  spotlight    // specifies spotlight
  radius 15    // cone opening from its axis in degrees. Light start to dim outside of this.
  falloff 20   // outside of this, there is no light.
  tightness 1  // over-all coherence of the light beam
  point_at <0, 2, 0>
}

box 
 { <0,-200,0>, <143,1100,33>
	scale 0.001
      texture {T_Wood4}  
translate <0.42,-1,0.65>
	rotate <-20,0,0>
 }

#declare plank1 = box 
 { <0,0,0>, <143,2200,33>
	scale 0.001
      texture {T_Wood4}  
 }

#declare size1 = union {
object { plank1 }
#declare xpos = 153;
#declare xpos1 = -10;
#declare ypos = 90;
#declare zpos = 16;
#declare xfinal = 1000;
#declare yfinal = 2200;
#declare zfinal = 1000;
#while (ypos <= yfinal)
    #torus { 10,5 rotate<90,0,0> translate<xpos,ypos,zpos>  texture {T_Chrome_4E} scale 0.001 }
    #torus { 10,5 rotate<90,0,0> translate<xpos1,ypos,zpos>  texture {T_Chrome_4E} scale 0.001 }
  #declare ypos = ypos + 200;
#end
}

union {
object { size1 
	rotate <0,0,-20>
}
object { size1 
	rotate <0,180,20>
	translate <1,0,0.033>
}
	rotate <10,0,0>
}

Bryce

Bryce or Bryce3D, is a 3D modeling, rendering and animation program specialising in fractal landscapes.

More about the webcam controls https://www.henriaanstoot.nl/1998/10/23/made-a-webinterface-for-my-diy-webcam/


Poser

Poser (Pro) is a 3D computer graphics program optimized for the 3D modeling of human figures.

Blender

Blender is a free and open-source 3D computer graphics software tool set used for creating animated films, visual effects, art, 3D-printed models, motion graphics, interactive 3D applications, virtual reality, and, formerly, video games. Blender’s features include 3D modelling, UV mapping, texturing, digital drawing, raster graphics editing, rigging and skinning, fluid and smoke simulation, particle simulation, soft body simulation, sculpting, animation, match moving, rendering, motion graphics, video editing, and compositing. (Dutch developers started it in 1994)
https://en.wikipedia.org/wiki/Blender_(software)

Below here not really for art but graphical generators .

Others: Zbrush, Xara3D, Sketchup, OpenScad
Terrain Maker, Terragen

Planning the cocktail bar, same month i did the whole house in Sketchup
Used an android app before to figure out the picture sizes, sketchup works also

I made the double chanter in blender in this post
https://www.henriaanstoot.nl/2021/07/02/3d-printed-double-chanter-proof-of-concept/

Below my openscad version

difference(){
    difference(){
difference(){
    union(){
cylinder($fn = 180, $fa = 12, $fs = 2, h = 100, d1 = 16, d2 = 16, center = false);

translate([-12.5,0,0]){

cylinder($fn = 180, $fa = 12, $fs = 2, h = 70, d1 = 25, d2 = 25, center = false);
}
translate([12.5,0,0]){
cylinder($fn = 180, $fa = 12, $fs = 2, h = 70, d1 = 25, d2 = 25, center = false);
}
}




translate([-12.5,0,0]){
cylinder($fn = 180, $fa = 12, $fs = 2, h = 70, d1 = 17, d2 = 17, center = false);
}

translate([12.5,0,0]){
cylinder($fn = 180, $fa = 12, $fs = 2, h = 70, d1 = 17, d2 = 17, center = false);
}
}
translate([-12.5,0,65])
rotate([0,90,0])
cylinder($fn = 180, $fa = 12, $fs = 2, h = 20, d1 = 10, d2 = 10, center = false);
}
translate([0,0,70])
cylinder($fn = 180, $fa = 12, $fs = 2, h = 50, d1 = 12, d2 = 12, center = false);



}
translate([0,0,69]){
difference(){
union(){
translate([0,0,0])
cylinder($fn = 180, $fa = 12, $fs = 2, h = 5, d1 = 16, d2 = 16, center = false);
translate([-12.5,0,0])
cylinder($fn = 180, $fa = 12, $fs = 2, h = 5, d1 = 25, d2 = 25, center = false);
translate([12.5,0,0])
cylinder($fn = 180, $fa = 12, $fs = 2, h = 5, d1 = 25, d2 = 25, center = false);
}
translate([0,0,0])
cylinder($fn = 180, $fa = 12, $fs = 2, h = 5, d1 = 12, d2 = 12, center = false);
}
}
SUperb example of generated boxes for 3D printing using dimension variables.

NSFW Galleries:

Restricted Content
To view this protected content, enter the password below: