Cocoacrumbs

Cocoacrumbs

All of the buildings, all of those cars
were once just a dream
in somebody's head
Mercy Street - Peter Gabriel

Cocoacrumbs

13 minutes read

Pic 1

Now that my terminal works, it’s time to put it into a nice enclosure. And what better enclosure than a replica of the DEC VT100 terminal? After all, this is the terminal that introduced the famous ANSI terminal codes which are still in use today.

After a bit of searching I found useful documentation giving me the dimensions of a DEC VT100 terminal. On Aliexpress I found a nice 8" LCD display in a retro 4:3 ratio and with an VGA interface board. The dimensions of this 8" display then dictated the scaling factor which turned out to be 60%.

Then it was time to start up openSCAD and draw the shapes for the panels and machine them, glue everything together, lots of sanding, applying a lick of paint and then put everything inside.

Since there was plenty of space inside, I decided to put my CP/M computer inside as well so that the DEC VT100 replica would become a stand alone unit running [of course] CP/M.

The end result

Greeting screen.
Running Turbo Pascal 3.02.
Running WordStar 4 (using an amber/yellow font colour).
Terminal in 60 line mode and cyan font colour.
Using the screen controls to adjust the picture.

Construction photo’s

Machining the front panel out of 18 mm thick MDF.
Glueing the top half panels.
Glueing the bottom half panels.
All panels glued.
Trying out the screen-saver...
Installing the picture adjustment buttons I.
Installing the picture adjustment buttons II.
Installing power supply, keyboard connector and magnets to hold the back-panel.
White paint applied. Taping area for the black paint.
Applying black paint.
Final result.
A look inside (and the big red reset button (just in case ...)).
Close up.
The VGA to LCD interface board is attached to the 'ceiling'.
Back cover in place.

openSCAD code

Front panel

3D view

$fs=0.3;
//$fn=60;

LCDpanelRounding = 1.5;
InsetRounding    = 1.5;
CutOutRadius     = 10;
CutOutInset      = 10;
CaseRounding     = 6;

VT100_Outline();

VT100FacePoints = [
  [          0,  0,       5.1],     //0
  [        274,  0,       5.1],     //1
  [        274, 18,       5.1],     //2
  [          0, 18,       5.1],     //3
  [        4.5,  0, 169 + 5.1 -5],  //4
  [(274 - 4.5),  0, 169 + 5.1 -5],  //5
  [(274 - 4.5), 18, 169 + 5.1],     //6
  [        4.5, 18, 169 + 5.1]      //7
];

VT100FaceFaces = [
  [0,1,2,3],  // bottom
  [4,5,1,0],  // front
  [7,6,5,4],  // top
  [5,6,2,1],  // right
  [6,7,3,2],  // back
  [7,4,0,3]   // left
];


module VT100_Outline()
{
    difference()
    {
        VT100_Face();
        VT100_FrontCutOut();
        VT100_TopCutOut();
        VT100_LcdFront();
        VT100_LcdPanelCutOut();
        VT100_ChinFaceDivider();
    }
    VT100_Chin();
}


module VT100_Face()
{
    hull()
    {
        polyhedron(VT100FacePoints, VT100FaceFaces);
        difference()
        {
            translate([4.5, CaseRounding, 168]) 
                rotate([0, 90, 0]) 
                cylinder(h=(274 - (2*4.5)), r=CaseRounding);
            translate([4.5, 0, 160]) 
                cube([(274), 10, 9]);
        }
    }
}


module VT100_Chin()
{
    translate([0, 5, 0]) cube([274, 13, 5.1], false);
    difference() 
    {
        translate([0, CaseRounding, CaseRounding]) 
            rotate([0, 90, 0]) 
            cylinder(h=274, r=CaseRounding);
        translate([0, 0, 5.1]) 
            cube([274, 18, 10], false);
    }
}


module VT100_FrontCutOut()
{
    hull()
    {
        translate([24 - CutOutInset, 0, 27.6 - CutOutInset + CutOutRadius]) 
            sphere(r = InsetRounding);
        translate([24 - CutOutInset, 0, 169 + 5.1 - CaseRounding]) 
            sphere(r = InsetRounding);
    }
    hull() 
    {
        translate([24 + 161 + CutOutInset, 0, 27.6 - CutOutInset + CutOutRadius]) 
            sphere(r = InsetRounding);
        translate([24 + 161 + CutOutInset, 0, 169 + 5.1 - 6]) 
            sphere(r = InsetRounding);
    }
    hull()
    {
        translate([24 - CutOutInset + CutOutRadius, 0, 27.6 - CutOutInset]) 
            sphere(r = InsetRounding);
        translate([24 + 161 + CutOutInset - CutOutRadius, 0, 27.6 - CutOutInset]) 
            sphere(r = InsetRounding);
    }
    minkowski() 
    {
        translate([24 - CutOutInset + CutOutRadius, 0, 27.6 - CutOutInset + CutOutRadius]) 
            rotate([90, 0, 0])  
            arc(0.01, 0.01, CutOutRadius, 90);
        sphere(r=InsetRounding);
    }  
    minkowski() 
    {
        translate([24 +161 - CutOutInset + CutOutRadius, 0, 27.6 - CutOutInset + CutOutRadius]) 
            rotate([90, 270, 0])  
            arc(0.01, 0.01, CutOutRadius, 90);
        sphere(r=InsetRounding);
    }
}


module VT100_TopCutOut()
{
    hull()
    {
        translate([24 - CutOutInset, 6, 169 + 5.1]) sphere(r = InsetRounding);
        translate([24 - CutOutInset, 18, 169 + 5.1]) sphere(r = InsetRounding);
    }
    hull() 
    {
        translate([24 + 161 + CutOutInset, 6, 169 + 5.1]) sphere(r = InsetRounding);
        translate([24 + 161 + CutOutInset, 18, 169 + 5.1]) sphere(r = InsetRounding);
    }
    minkowski()
    {
        translate([24 - CutOutInset, 6, 169 + 5.1 - CaseRounding]) 
            rotate([0, 90, 0]) 
            arc(0.01, 0.01, CaseRounding, 90);
        sphere(r=InsetRounding);
    }
    minkowski()
    {
        translate([24 + 161 + CutOutInset, 6, 169 + 5.1 - CaseRounding]) 
            rotate([0, 90, 0]) 
            arc(0.01, 0.01, CaseRounding, 90);
        sphere(r=InsetRounding);
    }
}


module VT100_LcdFront()
{
    translate([24, 0, 27.6]) 
        roundedcube([161, 18, 122], radius = LCDpanelRounding, apply_to = "y");
}


module VT100_LcdPanelCutOut()
{
    translate([24 - ((174 - 161) / 2), 2, 27.6 - ((136 - 122) / 2)]) 
        roundedcube([174, 18, 136], radius = LCDpanelRounding, apply_to = "y");
}


module VT100_ChinFaceDivider()
{
    translate([0, 0, 5.1])
        cube([274, 1, 1]);
}


// Helper functions

module roundedcube(size = [1, 1, 1], center = false, radius = 0.5, apply_to = "all") {
    // <https://danielupshaw.com/openscad-rounded-corners/>
	// If single value, convert to [x, y, z] vector
	size = (size[0] == undef) ? [size, size, size] : size;

	translate_min = radius;
	translate_xmax = size[0] - radius;
	translate_ymax = size[1] - radius;
	translate_zmax = size[2] - radius;

	diameter = radius * 2;

	obj_translate = (center == false) ?
		[0, 0, 0] : [
			-(size[0] / 2),
			-(size[1] / 2),
			-(size[2] / 2)
		];

	translate(v = obj_translate) {
		hull() {
			for (translate_x = [translate_min, translate_xmax]) {
				x_at = (translate_x == translate_min) ? "min" : "max";
				for (translate_y = [translate_min, translate_ymax]) {
					y_at = (translate_y == translate_min) ? "min" : "max";
					for (translate_z = [translate_min, translate_zmax]) {
						z_at = (translate_z == translate_min) ? "min" : "max";

						translate(v = [translate_x, translate_y, translate_z])
						if (
							(apply_to == "all") ||
							(apply_to == "xmin" && x_at == "min") || (apply_to == "xmax" && x_at == "max") ||
							(apply_to == "ymin" && y_at == "min") || (apply_to == "ymax" && y_at == "max") ||
							(apply_to == "zmin" && z_at == "min") || (apply_to == "zmax" && z_at == "max")
						) {
							sphere(r = radius);
						} else {
							rotate = 
								(apply_to == "xmin" || apply_to == "xmax" || apply_to == "x") ? [0, 90, 0] : (
								(apply_to == "ymin" || apply_to == "ymax" || apply_to == "y") ? [90, 90, 0] :
								[0, 0, 0]
							);
							rotate(a = rotate)
							cylinder(h = diameter, r = radius, center = true);
						}
					}
				}
			}
		}
	}
}


/* 
 * Excerpt from... 
 * 
 * Parametric Encoder Wheel 
 *
 * by Alex Franke (codecreations), March 2012
 * http://www.theFrankes.com
 * 
 * Licenced under Creative Commons Attribution - Non-Commercial - Share Alike 3.0 
*/
 
module arc( height, depth, radius, degrees ) 
{
    // This dies a horible death if it's not rendered here 
    // -- sucks up all memory and spins out of control 
    render() {
        difference() 
        {
            // Outer ring
            rotate_extrude($fn = 100)
                translate([radius - height, 0, 0])
                    square([height,depth]);
         
            // Cut half off
            translate([0,-(radius+1),-.5]) 
                cube ([radius+1,(radius+1)*2,depth+1]);
         
            // Cover the other half as necessary
            rotate([0,0,180-degrees])
            translate([0,-(radius+1),-.5]) 
                cube ([radius+1,(radius+1)*2,depth+1]);
         
        }
    }
}
3D view of the front panel.

2D view

$fs=0.3;
$fn=60;

LCDBottom               = 27.6;
LCDpanelRoundingRadius  = 1.5;

tolerance               = 0.05;
cutoutRadius            = 10;
cutoutDiameter          = 2 + tolerance;

centerLeftCutout        = 4.5 + 9.5;
centerRightCutout       = 4.5 + 190.5;
cutoutWidth             = (centerRightCutout - centerLeftCutout) - (2 * cutoutRadius);

faceWidth               = 274;
faceHeight              = 169 + 5.1;

VT100FacePoints = [ [               0,           0],
                    [       faceWidth,           0],
                    [(faceWidth - 4.5), faceHeight],
                    [              4.5, faceHeight] ];


VT100_Outline();


module VT100_Outline()
{
    difference()
    {
        VT100_Face();
        VT100_LcdFront();
        VT100_FrontCutOut();
        VT100_TopCutOut();
        // VT100_ChinFaceDivider();
    }
} /* end VT100_Outline */


module VT100_Face()
{
    polygon(VT100FacePoints);
} /* end VT100_Face */


module VT100_LcdFront()
{
    translate([24, LCDBottom]) 
        rounded_square([161, 122], [LCDpanelRoundingRadius, LCDpanelRoundingRadius, LCDpanelRoundingRadius, LCDpanelRoundingRadius]);
} /* end VT100_LcdFront */


module VT100_FrontCutOut()
{
    translate([centerLeftCutout - (cutoutDiameter / 2), 
               LCDBottom + LCDpanelRoundingRadius])
        square([cutoutDiameter, faceHeight - (LCDBottom + LCDpanelRoundingRadius) - cutoutDiameter]);
    translate([centerRightCutout - (cutoutDiameter / 2), 
               LCDBottom + LCDpanelRoundingRadius])
        square([cutoutDiameter, faceHeight - (LCDBottom + LCDpanelRoundingRadius) - cutoutDiameter]);
    translate([centerLeftCutout + cutoutRadius, 
               LCDBottom - cutoutRadius - 0.5])
        square([161, cutoutDiameter]);

    translate([centerLeftCutout - (cutoutDiameter / 2) + cutoutRadius + cutoutDiameter,
               LCDBottom + cutoutDiameter - 0.5])
        rotate([0, 0, 225])
        arc_2D(cutoutRadius, cutoutDiameter, 90);
    translate([centerRightCutout - (cutoutDiameter / 2) - cutoutRadius,
               LCDBottom + cutoutDiameter - 0.5])
        rotate([0, 0, 315])
        arc_2D(cutoutRadius, cutoutDiameter, 90);
} /* end VT100_FrontCutOut */


module VT100_TopCutOut()
{
    translate([centerLeftCutout - (cutoutDiameter / 2), 
               faceHeight - cutoutDiameter])
        square([cutoutDiameter, cutoutDiameter]);
    translate([centerRightCutout - (cutoutDiameter / 2), 
               faceHeight - cutoutDiameter])
        square([cutoutDiameter, cutoutDiameter]);
} /* end VT100_TopCutOut */


module VT100_ChinFaceDivider()
{
    translate([0, 5.1])
        square([274, cutoutDiameter]);
} /* end VT100_ChinFaceDivider */


// Helper functions
module rounded_square(dim, corners=[10,10,10,10], center=false){
  w=dim[0];
  h=dim[1];

  if (center){
    translate([-w/2, -h/2])
    rounded_square_(dim, corners=corners);
  }else{
    rounded_square_(dim, corners=corners);
  }
} /* end rounded_square */


module rounded_square_(dim, corners, center=false){
  w=dim[0];
  h=dim[1];
  render()
  {
    difference(){
      square([w,h]);

      if (corners[0])
        square([corners[0], corners[0]]);

      if (corners[1])
        translate([w-corners[1],0])
        square([corners[1], corners[1]]);

      if (corners[2])
        translate([0,h-corners[2]])
        square([corners[2], corners[2]]);

      if (corners[3])
        translate([w-corners[3], h-corners[3]])
        square([corners[3], corners[3]]);
    }

    if (corners[0])
      translate([corners[0], corners[0]])
      intersection(){
        circle(r=corners[0]);
        translate([-corners[0], -corners[0]])
        square([corners[0], corners[0]]);
      }

    if (corners[1])
      translate([w-corners[1], corners[1]])
      intersection(){
        circle(r=corners[1]);
        translate([0, -corners[1]])
        square([corners[1], corners[1]]);
      }

    if (corners[2])
      translate([corners[2], h-corners[2]])
      intersection(){
        circle(r=corners[2]);
        translate([-corners[2], 0])
        square([corners[2], corners[2]]);
      }

    if (corners[3])
      translate([w-corners[3], h-corners[3]])
      intersection(){
        circle(r=corners[3]);
        square([corners[3], corners[3]]);
      }
  }
} /* end rounded_square_ */

// https://www.thingiverse.com/thing:1092611
module arc_2D(radius, thick, angle)
{
	intersection()
    {
		union()
        {
			rights = floor(angle/90);
			remain = angle-rights*90;
			if(angle > 90)
            {
				for(i = [0:rights-1])
                {
					rotate(i*90-(rights-1)*90/2)
                    {
						polygon([[0, 0], [radius+thick, (radius+thick)*tan(90/2)], [radius+thick, -(radius+thick)*tan(90/2)]]);
					}
				}
				rotate(-(rights)*90/2)
					polygon([[0, 0], [radius+thick, 0], [radius+thick, -(radius+thick)*tan(remain/2)]]);
				rotate((rights)*90/2)
					polygon([[0, 0], [radius+thick, (radius+thick)*tan(remain/2)], [radius+thick, 0]]);
			}
            else
            {
				polygon([[0, 0], [radius+thick, (radius+thick)*tan(angle/2)], [radius+thick, -(radius+thick)*tan(angle/2)]]);
			}
		}
		difference()
        {
			circle(radius+thick);
			circle(radius);
		}
	}
} /* end arc_2D */
2D view of the front panel.

Top panel

This openSCAD code can generate both 3D and 2D outputs by setting the is3D variable to 0 or 1.

$fs=0.3;
$fn=60;


is3D = 1;


topWidth        = 265;
topDepth        = 182;
topHeight       = 12;
topCaseRounding = 6;

cutoutRadius        = 10;
cutoutDiameter      = 2.05;

centerLeftCutout    = 9.5;
centerRightCutout   = 190.5;
cutoutLength        = (topDepth * 0.60);
cutoutWidth         = (centerRightCutout - centerLeftCutout) - (2 * cutoutRadius);

ventilationLeft     = centerRightCutout + (cutoutDiameter / 2) + 10;
ventilationRight    = topWidth - 10;
ventilationWidth    = ventilationRight - ventilationLeft; 


if (is3D == 0)
{
    difference()
    {
        VT100_TopPanelWithBackRounding();
        VT100_TopCutout();
        VT100_ventilation();
    }
}
else
{
    VT100_TopPanelWithBackRounding();
}



module VT100_ventilation()
{
    if (is3D == 1)
    {
        translate([ventilationLeft-0, 2, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 7, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 12, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 17, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 22, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 27, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 32, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 37, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 42, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 47, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 52, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 57, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 62, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 67, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 72, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 77, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 82, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 87, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 92, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 97, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 102, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 107, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 112, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 117, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 122, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 127, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 132, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 137, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 142, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 147, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 152, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 157, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 162, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
        translate([ventilationLeft-0, 167, 0])
            cube([ventilationWidth, cutoutDiameter, 12]);
    }
    else
    {
        translate([ventilationLeft-0, 2])
            square([ventilationWidth, cutoutDiameter]);        
        translate([ventilationLeft-0, 7])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 12])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 17])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 22])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 27])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 32])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 37])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 42])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 47])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 52])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 57])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 62])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 67])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 72])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 77])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 82])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 87])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 92])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 97])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 102])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 107])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 112])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 117])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 122])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 127])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 132])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 137])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 142])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 147])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 152])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 157])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 162])
            square([ventilationWidth, cutoutDiameter]);
        translate([ventilationLeft-0, 167])
            square([ventilationWidth, cutoutDiameter]);
    } /* end if */
} /* end VT100_ventilation */


module VT100_TopCutout()
{
    if (is3D == 1)
    {
        translate([centerLeftCutout - (cutoutDiameter / 2), 
                   0, 
                   topHeight - cutoutDiameter])
            cube([cutoutDiameter, cutoutLength, cutoutDiameter]);
        translate([centerRightCutout - (cutoutDiameter / 2), 
                   0, 
                   topHeight - cutoutDiameter])
            cube([cutoutDiameter, cutoutLength, cutoutDiameter]);
        translate([centerLeftCutout - (cutoutDiameter / 2) + cutoutRadius, 
                   cutoutLength + cutoutRadius,
                   topHeight - cutoutDiameter])
            cube([cutoutWidth, cutoutDiameter, cutoutDiameter]);  
        
        minkowski() 
        {
            translate([centerLeftCutout - (cutoutDiameter / 2) + cutoutRadius, 
                    cutoutLength, 
                    topHeight - cutoutDiameter]) 
                rotate([0, 0, 270])  
                arc_3D(0.01, 0.01, cutoutRadius, 90);
            cube(cutoutDiameter);
        }     
        minkowski() 
        {
            translate([centerRightCutout - (cutoutDiameter / 2) - cutoutRadius, 
                    cutoutLength, 
                    topHeight - cutoutDiameter]) 
                rotate([0, 0, 180])  
                arc_3D(0.01, 0.01, cutoutRadius, 90);
            cube(cutoutDiameter);
        } 
    }
    else
    {
        translate([centerLeftCutout - (cutoutDiameter / 2), 
                   0])
            square([cutoutDiameter, cutoutLength]);
        translate([centerRightCutout - (cutoutDiameter / 2), 
                   0])
            square([cutoutDiameter, cutoutLength]);
        translate([centerLeftCutout - (cutoutDiameter / 2) + cutoutRadius, 
                   cutoutLength + cutoutRadius])
            square([cutoutWidth, cutoutDiameter]);  
        
        translate([centerLeftCutout + (cutoutDiameter / 2) + cutoutRadius, 
                    cutoutLength]) 
            rotate([0, 0, 135])  
            arc_2D(cutoutRadius, cutoutDiameter, 90);

        translate([centerRightCutout - (cutoutDiameter / 2) - cutoutRadius, 
                    cutoutLength]) 
            rotate([0, 0, 45])  
            arc_2D(cutoutRadius, cutoutDiameter, 90);
    } /* end if */
} /* end VT100_TopCutout */


module VT100_TopPanelWithBackRounding()
{
    if (is3D == 1)
    {
        hull()
        {
            cube([topWidth, topDepth, topCaseRounding]);
            translate([0, 0, topCaseRounding]) 
                cube([topWidth, topDepth - topCaseRounding, topCaseRounding]);
            VT100_endRounding(topDepth);
        }
    }
    else
    {
        square([topWidth, topDepth]);
    } /* end if */
} /* end VT100_TopPanelWithBackRounding */


module VT100_endRounding(x)
{
    if (is3D == 1)
    {
        translate([0, x - (topCaseRounding * 2), 0])
        difference()
        {
            difference()
            {
                translate([0, topCaseRounding, topCaseRounding])
                    rotate([0, 90, 0])
                    cylinder(topWidth, r=topCaseRounding);
                cube([topWidth, topCaseRounding * 2, topCaseRounding]);
            }
            cube([topWidth, topCaseRounding, topCaseRounding * 2]);    
        }
    }
    else
    {
        // 3D only operation
    } /* end if */
} /* end VT100_endRounding */



// Helper functions

/* 
 * Excerpt from... 
 * 
 * Parametric Encoder Wheel 
 *
 * by Alex Franke (codecreations), March 2012
 * http://www.theFrankes.com
 * 
 * Licenced under Creative Commons Attribution - Non-Commercial - Share Alike 3.0 
*/
 
module arc_3D( height, depth, radius, degrees ) 
{
    // This dies a horible death if it's not rendered here 
    // -- sucks up all memory and spins out of control 
    render() {
        difference() 
        {
            // Outer ring
            rotate_extrude($fn = 100)
                translate([radius - height, 0, 0])
                    square([height,depth]);
         
            // Cut half off
            translate([0,-(radius+1),-.5]) 
                cube ([radius+1,(radius+1)*2,depth+1]);
         
            // Cover the other half as necessary
            rotate([0,0,180-degrees])
            translate([0,-(radius+1),-.5]) 
                cube ([radius+1,(radius+1)*2,depth+1]);
         
        }
    }
}

// https://www.thingiverse.com/thing:1092611
module arc_2D(radius, thick, angle)
{
	intersection(){
		union(){
			rights = floor(angle/90);
			remain = angle-rights*90;
			if(angle > 90){
				for(i = [0:rights-1]){
					rotate(i*90-(rights-1)*90/2){
						polygon([[0, 0], [radius+thick, (radius+thick)*tan(90/2)], [radius+thick, -(radius+thick)*tan(90/2)]]);
					}
				}
				rotate(-(rights)*90/2)
					polygon([[0, 0], [radius+thick, 0], [radius+thick, -(radius+thick)*tan(remain/2)]]);
				rotate((rights)*90/2)
					polygon([[0, 0], [radius+thick, (radius+thick)*tan(remain/2)], [radius+thick, 0]]);
			}else{
				polygon([[0, 0], [radius+thick, (radius+thick)*tan(angle/2)], [radius+thick, -(radius+thick)*tan(angle/2)]]);
			}
		}
		difference(){
			circle(radius+thick);
			circle(radius);
		}
	}
}

3D view of the top panel.
2D view of the top panel.

Top side panel

sidePanelPoints = [[0, 0], 
                   [31, 173-12], 
                   [211, 148-12], 
                   [211, 0]];

polygon(sidePanelPoints);
2D view of the top side panel.

Bottom side panel

$fs=0.3;
$fn=60;

radius = 6;
bottomPlateThickness = 12;

sidePanelPoints = [[0, 0], 
                   [183, 0], 
                   [183, 54-bottomPlateThickness], 
                   [0, 54-bottomPlateThickness]];
      
polygon(sidePanelPoints);

difference()
    {
        translate([-6, 54 -radius -bottomPlateThickness]) square(size=6);
        translate([-6, 54 -radius -bottomPlateThickness])  circle(r=radius);
    }
2D view of the bottom side panel.

Inlay panel with picture adjustment controls

$fs=0.3;
$fn=60;

panelThickness = 12;
X = 274 - (2 * panelThickness);
Y = 29 + 12;

offsetX = 240;
offsetY =   8;

inlayPanelPoints = [[0, 0], 
                   [X , 0], 
                   [X, Y], 
                   [0, Y]];
      
difference()
{
    polygon(inlayPanelPoints);
    translate([offsetX -  0,   offsetY]) circle(r=3);
    translate([offsetX - 18,   offsetY]) circle(r=3);
    translate([offsetX - 30.5, offsetY]) circle(r=3);
    translate([offsetX - 46.5, offsetY]) circle(r=3);
    translate([offsetX - 64.5, offsetY]) circle(r=3);
    
}
2D view of the inlay panel.

Bottom panel

$fs=0.3;
$fn=60;

CaseRounding     = 6;

hull()
{
    hull()
    {
        translate([0, CaseRounding, CaseRounding]) 
            rotate([0, 90, 0]) 
            cylinder(h=262, r=CaseRounding);
        cube([262, CaseRounding, CaseRounding]);
    }

    hull()
    {
        translate([0, 183-CaseRounding, CaseRounding]) 
            rotate([0, 90, 0]) 
            cylinder(h=262, r=CaseRounding);
        translate([0, 183-CaseRounding, 0]) 
            cube([262, CaseRounding, CaseRounding]);
    }
}
3D view of the bottom panel.

Lower front panel

$fs=0.3;
$fn=60;

panelThickness = 12;
X1 = 274 - (2 * panelThickness);
X2 = 262 - (2 * panelThickness);
X2offset = (X1 - X2) / 2;
Y = 42;

lowerFrontPanelPoints = [[X2offset, 0], 
                   [X2+X2offset, 0], 
                   [X1, Y], 
                   [0, Y]];
      
polygon(lowerFrontPanelPoints);
2D view of the lower front panel.

Recent posts

See more

Categories

About

Cocoacrumbs