Wednesday, December 2, 2015

AutoLISP Snowflakes



In this article I will describe a technique for making snowflakes similar to those that you may have learned to make as a child, where you fold a piece of paper up into thirds, then fold it in half, then cut out parts.  With this procedure you can obtain a similar result using AutoCAD and then cut it out on a laser engraver.

  1. First of all you need to create a couple of lines at a 30 degree angle to each other.  Select the LINE tool, type 0,0 of the starting point, then rotate up and type 10 on the Y axis or type in 0,10 to create a vertical line 10 units long.
  2. Rotate the line using the ROTATE command, select the line and hit enter, specify the origin as the point of rotation, hit C <enter> to copy and then 30 to rotate 30 degrees.
  3. If you are making a bunch of snowflakes you might want to make this shape into a block.
  4. I like to set up my OSNAP so that I can snap to Nearest.
  5. Use LINE, PLINE or other tools to make the "cuts" in the wedge.  Remember if you are actually cutting on a laser engraver you will want your shape to remain intact.
  6. Delete the two straight lines.
  7. Use the (sf1) macro to make the snowflake. First save the AutoLisp code to snowflake.lsp. Click Manage, Load Application, Select the location with the file snowflake.lsp, type (sf1) at the command line.  You may have to escape a couple of times to exit the command.
  8. If you don't want to use AutoLISP you can just mirror your lines around the 90 degree line, delete the line and then use polar array, repeat 6 to generate the snowflake.

;Snowflake generator
(defun sf1 ()
  (setq p1 (list 0.0 0.0  0.0))
  (setq p2 (list 0.0 10.0 0.0))
  (command "mirror" "ALL" "" p1 p2 "N")
  (command "ARRAYPOLAR" "ALL" "" p1 6 360.0 "" "")
)

Here is another example:

Sunday, October 11, 2015

Laser Box AutoLISP Code


This code can be used to generate a laser box with dove-tail edges.  It includes a kerf feature and should be used as in AutoCAD.  You need to know how to use AutoLISP macros to use this.

Sorry this is fairly long and I didn't put in many comments so hopefully you can make sense of it.  If not comment and I will explain further.

; Jig Saw Line X Outy
(defun jsbx1 (x1 x2 y1 n s krf)
  
  (setq n2 (* n 2))
  (setq dx (- x2 x1))
  (setq px (* (/ dx (+ n2 1)) 2.0))
  (setq tx (+ (/ px 2.0) krf))
  (setq gx (- (/ px 2.0) krf))

  (setq p1 (list x1 y1 0.0))

  (repeat n
    (list 
      (setq p2 (list (+ (car p1) tx)    y1    0))
      (setq p3 (list (+ (car p1) tx) (+ y1 s) 0))
      (setq p4 (list (+ (car p2) gx) (+ y1 s) 0))
      (setq p5 (list (+ (car p2) gx)    y1    0))
      (command "pline" p1 p2 p3 p4 p5 "")
      (setq p1 p5)
  ))
  
  (setq p2 (list (+ (car p1) tx) y1 0))
  
  (command "pline" p1 p2 "")
)

; Jig Saw Line Y Outy
(defun jsby1 (x1 y1 y2 n s krf)

  (setq n2 (* n 2))
  (setq dy (- y2 y1))
  (setq py (* (/ dy(+ n2 1)) 2.0))
  (setq ty (+ (/ py 2.0) krf))
  (setq gy (- (/ py 2.0) krf))

  (setq p1 (list x1 y1 0.0))

  (repeat n
    (list 
      (setq p2 (list    x1    (+ (cadr p1) ty) 0))
      (setq p3 (list (+ x1 s) (+ (cadr p1) ty) 0))
      (setq p4 (list (+ x1 s) (+ (cadr p2) gy) 0))
      (setq p5 (list    x1    (+ (cadr p2) gy) 0))
      (command "pline" p1 p2 p3 p4 p5 "")
      (setq p1 p5)
  ))
  
  (setq p2 (list x1 (+ (cadr p1) ty) 0))
  
  (command "pline" p1 p2 "")
)

; Jig Saw Line X Inny
(defun jsbx2 (x1 x2 y1 n s krf)
  
  (setq n2 (* n 2))
  (setq dx (- x2 x1))
  (setq px (* (/ dx (+ n2 1)) 2.0))
  (setq tx (+ (/ px 2.0) krf))
  (setq gx (- (/ px 2.0) krf))

  (setq p1 (list (+ x1 (abs s)) (+ y1 s) 0.0))
  (setq p2 (list (+ x1 gx krf) (+ y1 s) 0.0))
  (setq p3 (list (car p2) y1 0.0))
  (command "pline" p1 p2 p3 "")
  (setq p1 p3)
  
  (repeat (- n 1)
    (list
      (setq p2 (list (+ (car p1) tx)    y1    0))
      (setq p3 (list (+ (car p1) tx) (+ y1 s) 0))
      (setq p4 (list (+ (car p3) gx) (+ y1 s) 0))
      (setq p5 (list (+ (car p3) gx)    y1    0))
      (command "pline" p1 p2 p3 p4 p5 "")
      (setq p1 p5)
  ))
   
  (setq p2 (list (+ (car p1) tx)          y1    0))
  (setq p3 (list (+ (car p1) tx)       (+ y1 s) 0))
  (setq p4 (list (- (+ (car p3) gx krf) (abs s)) (+ y1 s) 0))
  
  (command "pline" p1 p2 p3 p4 "")
)

; Jig Saw Line Y Inny
(defun jsby2 (x1 y1 y2 n s krf)

  (setq n2 (* n 2))
  (setq dy (- y2 y1))
  (setq py (* (/ dy(+ n2 1)) 2.0))
  (setq ty (+ (/ py 2.0) krf))
  (setq gy (- (/ py 2.0) krf))
  
  (setq p1 (list (+ x1 s) (+ y1 (abs s)) 0.0))
  (setq p2 (list (+ x1 s ) (+ y1 gy krf) 0.0))
  (setq p3 (list x1 (cadr p2) 0.0))
  (command "pline" p1 p2 p3 "")
  (setq p1 p3)

  (repeat (- n 1)
    (list
      (setq p2 (list    x1    (+ (cadr p1) ty) 0))
      (setq p3 (list (+ x1 s) (+ (cadr p1) ty) 0))
      (setq p4 (list (+ x1 s) (+ (cadr p3) gy) 0))
      (setq p5 (list    x1    (+ (cadr p3) gy) 0))
      (command "pline" p1 p2 p3 p4 p5 "")
      (setq p1 p5)
  ))

  (setq p2 (list    x1    (+ (cadr p1) ty)    0))
  (setq p3 (list (+ x1 s) (+ (cadr p1) ty)    0))
  (setq p4 (list (+ x1 s) (- (+ (cadr p3) gy krf) (abs s)) 0))
  (command "pline" p1 p2 p3 p4 "")
)

; Jig Saw Line X Mixed
(defun jsbx3 (x1 x2 y1 n s krf)
  
  (setq n2 (* n 2))
  (setq dx (- x2 x1))
  (setq px (* (/ dx (+ n2 1)) 2.0))
  (setq tx (+ (/ px 2.0) krf))
  (setq gx (- (/ px 2.0) krf))

  (setq p1 (list (+ x1 (abs s))              y1 0.0))
  (setq p2 (list (- (+ (car p1) tx) (abs s)) y1 0.0))
  
  (command "pline" p1 p2 "")
  (setq p1 p2)
  
  (repeat (- n 1)
    (list

      (setq p2 (list    (car p1)     (+ y1 s) 0))
      (setq p3 (list (+ (car p1) gx) (+ y1 s) 0))
      (setq p4 (list (+ (car p1) gx)    y1    0))
      (setq p5 (list (+ (car p3) tx)    y1    0))

      (command "pline" p1 p2 p3 p4 p5 "")
      (setq p1 p5)
  ))
   
  (setq p2 (list       (car p1)           (+ y1 s) 0))
  (setq p3 (list    (+ (car p1) gx)       (+ y1 s) 0))
  (setq p4 (list    (+ (car p1) gx)          y1    0))
  (setq p5 (list (- (+ (car p3) tx) (abs s)) y1    0))
  (command "pline" p1 p2 p3 p4 p5 "")
)

; Jig Saw Line Y Mixed
(defun jsby3 (x1 y1 y2 n s krf)

  (setq n2 (* n 2))
  (setq dy (- y2 y1))
  (setq py (* (/ dy(+ n2 1)) 2.0))
  (setq ty (+ (/ py 2.0) krf))
  (setq gy (- (/ py 2.0) krf))
  
  (setq p1 (list (+ x1 s) y1   0.0))
  (setq p2 (list (car p1) (+ y1 gy krf) 0.0))
  
  (command "pline" p1 p2 "")
  (setq p1 p2)

  (repeat (- n 1)
    (list
      
      (setq p2 (list    x1    (cadr p1) 0))
      (setq p3 (list    x1    (+ (cadr p1) ty) 0))
      (setq p4 (list (+ x1 s) (+ (cadr p1) ty) 0))
      (setq p5 (list (+ x1 s) (+ (cadr p3) gy) 0))
      
      (command "pline" p1 p2 p3 p4 p5 "")
      (setq p1 p5)
  ))

  (setq p2 (list    x1       (cadr p1)         0))
  (setq p3 (list    x1    (+ (cadr p1) ty)     0))
  (setq p4 (list (+ x1 s)    (cadr p3)         0))
  (setq p5 (list (+ x1 s) (+ (cadr p3) gy krf) 0))
  (command "pline" p1 p2 p3 p4 p5 "")
)

;(jsb 4.0 6.0 5 0.11 0.01)
;Outy
(defun jsb1 (x1 x2 y1 y2 nx ny s krf)
   (jsbx1 x1 x2 y1    nx    s  krf)
   (jsby1 x1 y1    y2      ny    s  krf)
   (jsbx1 x1 x2 (+ y2 krf)   nx (- s) krf)
   (jsby1 (+ x2 krf) y1  y2  ny (- s) krf)
)

;(jsb 4.0 6.0 5 0.11 0.01)
;Inny
(defun jsb2 (x1 x2 y1 y2 nx ny s krf)
   (jsbx2 x1 x2      y1    nx    s  krf)
   (jsby2 x1 y1    y2      ny    s  krf)
   (jsbx2 x1 x2 (+ y2 krf)   nx (- s) krf)
   (jsby2 (+ x2 krf) y1  y2  ny (- s) krf)
)

;(jsb 4.0 6.0 5 0.11 0.01)
;Mixed
(defun jsb3 (x1 x2 y1 y2 nx ny s krf)
   (jsbx3 x1 x2   y1  nx    s  krf)
   (jsbx3 x1 x2   (+ y2 krf) nx (- s) krf)
   (jsby3 x1 y1 y2    ny    s  krf)
   (jsby3 (+ x2 krf) y1    y2      ny (- s) krf)
)

(defun jsb (x y z nx ny nz s krf)
  (jsb1 0.0 x 0.0 y nx ny s krf)
  (jsb1 (+ x 0.5 s) (+ x x 0.5 s) 0.0 y nx ny s krf)
  (jsb2 (+ (* x 2) 1.0 s) (+ (* x 3) 1.0 s) 0.0 z nx nz s krf)
  (jsb2 (+ (* x 3) 1.5 s) (+ (* x 4) 1.5 s) 0.0 z nx nz s krf)
  (jsb3 (+ (* x 4) 2.0 s) (+ (* x 4) z 2.0 s) 0.0 y nz ny s krf)
  (jsb3 (+ (* x 4) z 2.5 s) (+ (* x 4) z z 2.5 s) 0.0 y nz ny s krf)
)

The main routine is jsb.  At the AutoCAD command line you would type:

(jsb 2.0 3.0 4.0 2 3 4 0.125 0.01)

to generate a box 2"x3"x4" with 2 tabs, 3 tabs, and 4 tabs respectively for 1/8" wood with a laser width (kerf) of 1/100th of an inch.  Don't forget to turn off object snap (f3) (and other snaps).  You should get something like the following:



Thursday, April 9, 2015

Half Normal Probability Paper


The code below can be used to generate half normal probability paper.  The erfinv function is a bit inaccurate (2 sig figs) so it could be improved with a better approximation.  Update: I modified this with the code from Wikipedia to give better accuracy.   Create a blank file first.  Mine was 1500 by 2000.  The code is Python.  I had to install Pillow for this.

from PIL import Image,ImageDraw,ImageFont
from math import *

def halfnormal(z,s):
  return erf(z/sqrt(2)/s)

def erfinv(z):
  a = .147
  x=sqrt(sqrt((2/pi/a+log(1-z**2)/2)**2-log(1-z**2)/a)-(2/pi/a+log(1-z**2)/2))
  #print z,z-erf(x)
  return x
  
def invhalfnorm(z,s):
  return erfinv(z)*sqrt(2)
  
def halfnormalgraph():
  im = Image.open("blank_graph.png")
  draw = ImageDraw.Draw(im)
  font1 = ImageFont.truetype("arial.ttf", 16)
  font2 = ImageFont.truetype("arial.ttf", 28)
  j=30.0
  k=700.0
  xmax=46
  xshift=30
  gridfill = (180,180,250)
  
  draw.text((550,10), "Half Normal Probability Paper", font=font2, fill=256)
  for i in range(xmax):
    x1=j*i+j*2+xshift
    y1=im.size[1]-j*2
    y2=j*2
    draw.line((x1, y1, x1, y2), fill=gridfill)
  x1=j*2+xshift
  x2=j*xmax+j+xshift
  for i in range(10):
    iv=i/20.0+0
    y1=im.size[1]-invhalfnorm(iv,1.0)*k-j*2
    draw.line((x1, y1, x2, y1 ), fill=gridfill)
    draw.text((2+xshift,y1-8), "{:10.0f}".format(iv*100), font=font1, fill=256)
  for i in range(20):
    iv=i/50.0+.5
    y1=im.size[1]-invhalfnorm(iv,1.0)*k-j*2
    draw.line((x1, y1, x2, y1 ), fill=gridfill)
    draw.text((2+xshift,y1-8), "{:10.0f}".format(iv*100), font=font1, fill=256)
  for i in range(10):
    iv=i/100.0+.90
    y1=im.size[1]-invhalfnorm(iv,1.0)*k-j*2
    draw.line((x1, y1, x2, y1 ), fill=gridfill)
    draw.text((2+xshift,y1-8), "{:10.0f}".format(iv*100), font=font1, fill=256)
  im.save("halfnormgraph.png")

Here is another program that makes the desired number of divisions and an example of the output.  This would be useful for plotting full factorial designed experiments, DOE, with 2 levels and 3 factors.  Included are sheets for 7, 15, and 31 divisions which are the ones most useful for full factorial experiments.





def hng2(bins=7):
  im = Image.open("blank_graph.png")
  draw = ImageDraw.Draw(im)
  font1 = ImageFont.truetype("arial.ttf", 16)
  font2 = ImageFont.truetype("arial.ttf", 28)
  j=30.0
  k=700.0
  xmax=46
  xshift=30
  gridfill = (180,180,250)
  
  draw.text((550,10), "Half Normal Probability Paper", font=font2, fill=256)
  for i in range(xmax):
    x1=j*i+j*2+xshift
    y1=im.size[1]-j*2
    y2=j*2
    draw.line((x1, y1, x1, y2), fill=gridfill)
  x1=j*2+xshift
  x2=j*xmax+j+xshift
  iv=0.0
  y1=im.size[1]-invhalfnorm(iv,1.0)*k-j*2
  draw.line((x1, y1, x2, y1 ), fill=gridfill)
  draw.text((-2+xshift,y1-8), "{:10.2f}".format(iv*100), font=font1, fill=256)
  for i in range(bins):
    iv=1.0*i/bins+1.0/2.0/bins
    y1=im.size[1]-invhalfnorm(iv,1.0)*k-j*2
    draw.line((x1, y1, x2, y1 ), fill=gridfill)
    draw.text((-2+xshift,y1-8), "{:10.2f}".format(iv*100), font=font1, fill=256)
  im.save("hng2.png")

hng2(7)

Friday, March 27, 2015

Spider Eyes

/* 
 * File:   spider2main.c
 * Author: hmikelson
 *
 * Created on March 24, 2015, 4:30 PM
 */

#if defined(__XC)
    #include <xc.h>         /* XC8 General Include File */
#elif defined(HI_TECH_C)
    #include <htc.h>        /* HiTech General Include File */
#endif

#include <stdint.h>        /* For uint8_t definition */
#include <stdbool.h>       /* For true/false definition */
#include <stdlib.h>     /*rand()*/

#pragma config MCLRE=OFF,CP=OFF,WDTE=OFF,FOSC=INTOSCIO
#define _XTAL_FREQ 4000000
uint8_t sGPIO;

void init()
{
    //Configure GPIO Port
    ANSEL =  0b00000000;  //Configure all GPIO pins as digital
    TRISIO = 0b11001000;  //Set GP3 as input and the rest as outputs
    OPTION_REGbits.nGPPU = 0;
    WPU = 0b00000100;     //Enable weak pullups on GP2
    //Configuer AD Convertor
    ADCON0 = 0x00;        //AD disabled
    ADRESH = 0x00;        //Init the AD Register
    //Configure Comparator
    CMCON0 = 0xFF;   // Comparator is turned off
    CMCON1 = 0x00;   // Comparator is turned off
    //Interrupt configuration
    INTCON = 0x00;   //Disable all interrupts
}

void vdelay(int n)
{
    int i;
    for (i=0;i<=n;i++)
    {
     __delay_us(1);
    }
}

void fade_eyes(int n) // PWM sweep high f to low f
{
 int i;
  {
   for (i=0;i<n;i++)
    {
     GPIO = 0b11000101;
     vdelay(i);
     GPIO = 0b11000000;
     vdelay(n-i);
    }
   for (i=0;i<n;i++)
    {
     GPIO = 0b11000101;
     vdelay(n-i);
     GPIO = 0b11000000;
     vdelay(i);
    }
  }
}

void scan_eyes(int n) // PWM sweep high f to low f
{
 int i,j,k;
 int i1,i2,i3,pw1,pw2;
 uint8_t sGPIO;

 i1=1; i2=-1; pw1=0; pw2=n;
 sGPIO = GPIO;
 for (k=0;k<6;k++)
 {
  for (j=0;j<n;j++)
   {
    for (i=0;i<n;i++)
     {
      if (i>pw1)
       {
        sGPIO = sGPIO & 0b11111110;
       }
      else
       {
        sGPIO = sGPIO | 0b00000001;
       }
      if (i>pw2)
       {
        sGPIO = sGPIO & 0b11111011;
       }
      else
       {
        sGPIO = sGPIO | 0b00000100;
       }
      GPIO = sGPIO;
     }
    pw1 = pw1 + i1;
    pw2 = pw2 + i2;
   }
  i3 = i1; i1 = i2, i2 = i3;
 }
}

void dark_eyes(void) // PWM sweep high f to low f
{
 GPIO = 0b11000000;
 __delay_ms(1000);
}

void main()
{
uint8_t r,r2;

 init();

 while(1)
  {
   r=rand()%8;
   r2 = rand()%8+1;
   //r=3;
   switch (r)
    {
     case 1:
     fade_eyes(100*r2);
     break;

     case 2:
     fade_eyes(20*r2);
     break;

     case 3:
     scan_eyes(20*r2);
     break;

     default:
     dark_eyes();
    }
  }
}

Wednesday, March 18, 2015

Spherical Lissajous Curves using POVRay






Some examples of spherical Lissajous curves created with POVRay.

##############################################################################
# This program created by Hans Mikelson February 1998
# This program generates a POV file so that 3D graphs can be
# rendered using the POV ray tracing package.  The surface is mapped
# as a sphere for each data point sampled.  This was configured for
# use on a Win95 computer.  Some changes may need to be made for use on
# other systems.  This program assumes that POVRay has been installed
# and that there is a path to it.
##############################################################################

# Define files for the template (header/footer), the data and the POV file.
$povfile="plotter.pov";
$scale   =.25;
$radius  = $scale;

# open the POV file.
open (POV, ">$povfile");

# Print the header
print POV "#version 3.0\n";
print POV "global_settings { assumed_gamma 2.2 }\n\n";
print POV "#include \"colors.inc\"\n";
print POV "#include \"shapes.inc\"\n";
print POV "#include \"textures.inc\"\n";
print POV "#include \"glass.inc\"\n";
print POV "#include \"stones.inc\"\n\n";
print POV "#include \"metals.inc\"\n\n";
print POV "camera {\n";
print POV "location <-2,-9,-2>\n";
print POV "  look_at <-2,0,-2>}\n\n";
print POV "// Light sources \n";
print POV "light_source {<-30, 21,  40> color White}\n";
print POV "light_source {< 0, -8, -4> color White}\n";
print POV "light_source {< 2,  30,  3> color White}\n";
print POV "background { color rgb <1, 1, 1> }\n\n";

@ul = (.3,.3,.6);
@vl = (.2,.2,.9);

for ($j=0; $j<1; $j++)
 {
  for ($k=0; $k<1; $k++)
   {
$u = $ul[$j];
$v = $vl[$k];
$rad = 2;

# Print the data.
for ($i=0; $i<800; $i++)
 {
  $t = $i * .1;
  # Cosines
  $cosu = cos($u*$t);
  $cosv = cos($v*$t);

  # Sines
  $sinu = sin($u*$t);
  $sinv = sin($v*$t);

  # Compute X and Y
  $xval = $rad*$sinu*$cosv;
  $yval = $rad*$sinu*$sinv;
  $zval = $rad*$cosu;


  $colr=1/(1+$xval*$xval);
  $colg=1/sqrt(1+abs($yval));
  $colb=1/(1+$zval*$zval*$zval*$zval);
  
  $xval += $j*5; $yval += $k*5;

  print POV "sphere { <$xval, $zval, $yval>, $radius\n";
  print POV "    texture { pigment {color rgb <$colr, $colg, $colb>}}\n";
  print POV "    finish {caustics 1.0 phong 1 phong_size 300 reflection 0.15}\n";
  print POV "}\n\n";

  if ($i>0)
   {
    print POV "cylinder { <$xval, $zval, $yval>, <$xold, $zold, $yold> $radius\n";
    print POV "    texture { pigment {color rgb <$colr, $colg, $colb>}}\n";
    print POV "    finish {caustics 1.0 phong 1 phong_size 300 reflection 0.15}\n";
    print POV "}\n\n";
   }
  $xold = $xval;
  $yold = $yval;
  $zold = $zval;
 }

}}
close(POV);

# End of script

Electron Orbitals using POVRay Graphics


This image was created using POVRay and the wave function for electron orbitals.  1s, 2s, 3s on the left, p orbitals on the right, 2p on top and 3p on the bottom.