Kirjoitusalusta.fi
Koko ruutu

Server Notice:

hide

Julkinen työtila Latest text of pad hacklab-animatronic-dragon Tallennettu Tammikuu 8, 2013

 
     
// Openscad, animatronic dragon, work in progress, see http://github.com/zzorn/dragon
 
$fn = 40;
cutCurveFn = 60;
 
holeMargin = 1;
 
 
//testSpine(xNum=2, yNum=2);
 
//dragon();
 
neckSegment(30, 
            20, 
            30, 
            10.3, 
            3,
            spikeSize=1);
 
 
// Slice outline for a neck / spine piece.
// Use a polygon editor to define this, e.g. http://daid.mine.nu/~daid/3d/
spineOutline = [[0,18],[1,16],[5,13],[10,11],[14,12],[13,9],[14,5],[16,2],[18,0],[16,-1],[14,-5],[12,-11],[11,-12],[6,-15],[0,-16],[-6,-15],[-11,-12],[-12,-11],[-14,-5],[-16,-1],[-18,0],[-16,2],[-14,5],[-13,9],[-14,12],[-10,11],[-5,13],[-1,16]];
spineScaleX = 1.0/32.0;
spineScaleZ = 1.0/32.0;
 
 
// Profile for a neck / spline segment
// Small: spineProfile = [[15,32],[16,31],[16,30],[15,28],[14,23],[14,21],[15,21],[15,19],[13,16],[12,13],[12,11],[13,11],[13,9],[11,6],[10,2],[10,0]];
spineProfile = [[13,32/*1:0,0,0,0*/] ,[14,31.88] ,[14.88,31.39],[15,31/*1:0,1,1,-3*/] ,[15.13,29.95] ,[14.98,28.9] ,[14.69,27.92] ,[14.32,26.93] ,[13.95,25.96] ,[13.61,24.93] ,[13.43,23.9] ,[13.63,22.89] ,[14.37,22.22],[15,22/*1:-4,1,-1,-6*/] ,[14.79,20.95] ,[14.53,19.97] ,[14.17,18.89] ,[13.79,17.89] ,[13.39,16.97] ,[12.95,16] ,[12.5,15] ,[12.12,14.01] ,[11.91,12.96] ,[12.09,11.97] ,[12.75,11.18],[13,11/*1:-3,2,-1,-5*/] ,[12.78,10.01] ,[12.49,8.99] ,[12.15,7.98] ,[11.75,6.97] ,[11.35,6.04] ,[10.94,5.05] ,[10.58,4.03] ,[10.3,3.02] ,[10.12,2.01] ,[10.03,0.99],[10,0/*1:0,5,-4,0*/] ,[8.88,0] ,[7.83,0] ,[6.75,0] ,[5.67,0] ,[4.62,0] ,[3.62,0] ,[2.58,0] ,[1.58,0] ,[0.54,0]];
spineProfileScaleX = 0.5*1.0/15.0;
spineProfileScaleY = 1.0/32.0;
spineProfileLen = 46; // Works in later openscad versions only? len(spineProfile);
 
 
// Test setup for spines
module testSpine(xNum = 3, yNum = 3, step = 10, startSize = 20) {    
    spacing = max(xNum,yNum)*step*1.8+startSize+10;
    w = xNum*spacing;
    h = yNum*spacing;
    
    for (x = [0 : xNum-1], y = [0 : yNum-1])
        translate([(x-(xNum-1)/2)*spacing, (y-(yNum-1)/2) * spacing, 0]) {
            neckSegment(x*step + startSize, 
                        y*step + startSize, 
                        (x+y) * 0.5 *step + startSize, 
                        10.3, 
                        3);
                        
            translate([0,0,(x+y) * 0.5 *step + startSize])
                neckSegment(x*step + startSize, 
                            y*step + startSize, 
                            (x+y) * 0.5 *step + startSize, 
                            10.3, 
                            3);
        }
}
 
 
module dragon(width = 100, length = 700, wingspan = 500, tubeDiam = 8, tendonDiam = 3) {
    
    spacing = 0;
    
    
    // Head
    headWidth = width * 0.6;
    headLength = length * 0.125;
    translate([-headWidth/2, bodyLength/2 + spacing + neckLength + spacing*neckSegments, 0]) {
        head(headWidth, headWidth*0.7, headLength);
    }
    
    // Neck
    neckWidth = width * 0.3;
    neckLength = length * 0.2;
    neckSegments = 4;
    translate([0, bodyLength/2 + spacing, 0]) {
        spine(neckWidth*1.1, neckWidth, neckLength, 1.2, 0.8, tubeDiam, tendonDiam, neckSegments, spacing, startSpikeSize=1, startSpikeAngle=28, endSpikeSize=0.9, endSpikeAngle=26);
    }
    
    // Body
    bodyWidth = width*1;
    bodyLength = length * 0.175;        
    translate([0,-bodyLength/2,0]) {
        body(bodyWidth, bodyLength, wingspan);
    } 
    
    // Tail
    tailWidth = width * 0.25;
    tailLength = length * 0.5;
    tailSegments = 8;
    translate([0, -tailLength - spacing*tailSegments-bodyLength/2, 0]) {
        spine(tailWidth*1.2, tailWidth, tailLength, 0.6, 1.2, tubeDiam, tendonDiam, tailSegments, spacing, startSpikeSize=0.5, startSpikeAngle=30);
    }
}
 
 
module body(width, length, wingspan) {
    
    height = width * 0.5;
    
    wingBoneSize = 10;
 
    translate([-width/2,0,0])    
        cube([width,length,height]);
    
    translate([width/2, 0, height/2])
        wing((wingspan-width)/2, length, wingBoneSize);    
    translate([-width/2, 0, height/2])
        scale([-1, 1, 1])
            wing((wingspan-width)/2, length, wingBoneSize);    
    
}
 
module wing(span, width, wingBoneSize) {
    cube([span,width,wingBoneSize]);
}
 
module spine(width, height, length, startSize, endSize, tubeDiam, tendonDiam, neckSegments, spacing, startSpikeSize=1, endSpikeSize=1, startSpikeAngle=28, endSpikeAngle=28) {
    segmentLength = length / neckSegments;
    for (segment = [1 : neckSegments]) {
        translate([0, segmentLength + (segment - 1) * (segmentLength+spacing), width/2]) {
            rotate([90, 0,0])
                neckSegment(width * mix(segment/neckSegments, startSize, endSize), 
                            height * mix(segment/neckSegments, startSize, endSize), 
                            segmentLength, 
                            tubeDiam, 
                            tendonDiam,
                            spikeSize=mix(segment/neckSegments, startSpikeSize, endSpikeSize),
                            spikeAngle=mix(segment/neckSegments, startSpikeAngle, endSpikeAngle));
        }
    }
}
 
function mix(pos, start, end) = start + pos * (end -start);
 
function profileScale(relativePos, scale, startScale, endScale) = mix(relativePos, startScale, endScale) * scale * (spineProfile[spineProfileLen - 1 - relativePos * (spineProfileLen - 1)][0]);
 
module neckSegment(sizeX, sizeY, length, tubeDiam, tendonDiam, spikeSize = 1, spikeAngle = 40, bendAmount = 0.3, supportSpacing = 0.75) {
    tendonX = (sizeX/2 - tendonDiam/2) - holeMargin;    
    tendonY = (sizeY/2 - tendonDiam/2) - holeMargin;    
    sliceCount = spineProfileLen; 
    sliceH = length / sliceCount;
 
    
    depressionRelativePos = 0.5;
    depressionDepth = length * depressionRelativePos;
    bumpHeight = 0.5 * max(sizeX, sizeY) * bendAmount;
 
    connectorLength = depressionDepth*0.5;
    
    //bumpWidth = tubeDiam;// + 2 * holeMargin*2;
    //bumpLen = (width - bumpWidth) * bendAmount;    
    difference() {
        union() {
        
            // Basic body
            difference() {
                translate([0,0,connectorLength])
                    carvedSegment(sizeX, sizeY, length, spikeSize, spikeAngle = spikeAngle);
 
                // Depression for connecting to the previous segment
                translate([0,0,length - bumpHeight])
                    scale([sizeX, sizeY, 1])
                        cylinder(r1=0.5, r2= 0.6 + bendAmount * depressionRelativePos, h=depressionDepth+bumpHeight);       
            }
        
            // Bump to pivot previous segment on
            translate([0,0,length - bumpHeight])
                scale([sizeX, sizeY, 1])
                    cylinder(r1=0.5, r2=0, h=bumpHeight);       
 
            // Socket to connect to next segment
            translate([0,0,0])
                scale([sizeX, sizeY, length - bumpHeight])
                    cylinder(r=0.5, h=1);       
        }
 
        
 
        // Hole for central tube carrying electronic wires
        translate([0,0,-1])
            cylinder(r=tubeDiam/2, h=length+connectorLength + 2);       
        
        // Holes for tendons along each edge    
        cylinderCircle(tendonDiam, length+connectorLength, tendonX, angleNum=2, startAngle=0);
        cylinderCircle(tendonDiam, length+connectorLength, tendonY, angleNum=2, startAngle=90);
                
    }
}
 
 
function polarR(a, x, y) = sqrt(pow(sin(a) * y * 0.5, 2) + pow(cos(a) * x * 0.5, 2));
 
module carvedSegment(origX, 
                     origY, 
                     sizeZ, 
                     spikeSize = 1,
                     spikeAngle = 28,
                     padding = 1.7, 
                     bottomCarveDepth = 0.4, 
                     bumpCount = 1) {
 
    extraSideCarveAngle = 15;
    carveAreaAngle = 180.0+extraSideCarveAngle*2;
                     
    carvePadding = padding * 5;
    carveDepth = 1.0 - 1.0 / carvePadding;
    
 
    maxSizeX = origX * padding;
    maxSizeY = origY * padding;
    
    cutSharpness = 5;
    cutLength = 7;
    cutOffset = -6;
 
    spikeRootSize = 0.4;
 
    
    bottomCutAspect = 1.1;
    bottomCutRadius = 0.9;
    bottomCutDist = sizeZ * 1.45;
 
    difference() {
        union() {       
            scale([origX, origY, sizeZ]) {
                translate([0,0,-0.2])
                    cylinder(r1=0.5, r2=padding/2*0.65, h=0.25);
                translate([0,0,-0.1])
                    cylinder(r1=0.5, r2=padding/2*0.74, h=0.25);
                translate([0,0,1])
                    scale([1,1,1.5])
                        sphere(r=padding/2);
 
                // Spike
                translate([0,0.5,0.6])
                    scale([spikeSize, spikeSize, spikeSize])
                        rotate([270+spikeAngle,0,0])
                            cylinder(r1 = spikeRootSize, r2 = 0.05, h=1);
            }
 
        }
 
        scale([origX, origY, sizeZ]) {
            // Cut behind spike
            translate([0,0.5,1.2])
                scale([spikeSize, spikeSize, spikeSize])
                    rotate([270,0,0])
                        cylinder(r1 = spikeRootSize, r2 = 0.05, h=1);
 
            // Carve sides
            sideCarver(padding-0.35, 
                       cutRadiusFactor = 0.85, 
                       cutRootScale = 0.05,
                       cutAngle=25,
                       carveStartAngle = -extraSideCarveAngle, 
                       carvingCount=4,
                       carveAreaAngle = carveAreaAngle);
 
            sideCarver(padding-0.04, 
                       cutRadiusFactor = 1.20, 
                       cutAngle=24,
                       cutSharpness = 1,
                       cutRootScale=0.75, 
                       carveStartAngle = -extraSideCarveAngle, 
                       carvingCount=2,
                       carveAreaAngle = carveAreaAngle);
        }
        
        // Carve bottom
        translate([0,0,bottomCutDist])
            scale([origX*bottomCutAspect, origY/bottomCutAspect, sizeZ])
                sphere(r=bottomCutRadius, $fn=cutCurveFn);
        translate([0,0,bottomCutDist*1.35])
            scale([origX*bottomCutAspect*1.4, origY/bottomCutAspect*1.4, sizeZ])
                rotate([0,90,0])
                    cylinder(r=1, h=2, center=true, $fn=cutCurveFn);
    }
 
}
 
 
module sideCarver(cutDistanceFactor, 
                  cutRadiusFactor = 0.9, 
                  cutAngle = 25, 
                  cutRootScale = 0.1, 
                  cutSharpness = 1.5,
                  carveStartAngle = 90,
                  carveAreaAngle = 360.0,
                  carvingCount = 8) {
                  
    carveStep = carveAreaAngle / carvingCount;
    
    for (ca = [carveStep*0.5 : carveStep : carveAreaAngle + 0.01 - carveStep*0.5]) {
        rotate([0,0,ca + carveStartAngle]) {
            translate([cutDistanceFactor, 0, 0]) {
                rotate([0, cutAngle, 0])
                    scale([cutSharpness, 1/cutSharpness, 1]) 
                        cylinder(r1=cutRootScale*cutRadiusFactor, r2=cutRadiusFactor, h=2, center=true, $fn=cutCurveFn);                    
            }
        }
    }
}
 
module cylinderCircle(diameter, length, centerDistance, angleNum=4, startAngle = 0, extend = 1) {
    angleStep = 360.0 / angleNum;
    for (ai = [0 : angleNum - 1]) {
        rotate([0,0,startAngle + angleStep * ai])
            translate([centerDistance, 0, -extend])
                cylinder(r = diameter/2, h = length + extend*2);
    }
}
 
module neckEnd(radius, thickness) {
    cylinder(r=radius, h=thickness);
}
 
module rib(radius, thickness) {
    cylinder(r=radius, h=thickness);
}
 
module head(headWidth, headHeight, headLength) {
    cube([headWidth, headLength, headHeight]);
}
 
// A box rounded along the ground plane only, with rounding equal to the size of the smallest side
module stretchedCylinder(width, depth, height, centerHeight=false) {
    heightOffset = centerHeight ? -height/2 : 0;
    if (depth > width) {
        union() {
            translate([0, -0.5*(depth-width), heightOffset])
                cylinder(r=width/2, h = height);
            translate([0,  0.5*(depth-width), heightOffset])
                cylinder(r=width/2, h = height);
            translate([0, 0, height/2 + heightOffset])
                cube([width, depth-width, height], center=true);
        }        
    }
    else if (width == depth) {
        translate([0,0, heightOffset])
            cylinder(r=width/2, h = height);
    }
    else {
        union() {
            translate([-0.5*(width-depth), 0, heightOffset])
                cylinder(r=depth/2, h = height);
            translate([ 0.5*(width-depth), 0, heightOffset])
                cylinder(r=depth/2, h = height);
            translate([0, 0, height/2 + heightOffset])
                cube([width-depth, depth, height], center=true);
        }        
    }
    
}