Advertisement
Mangus875

[WIP] Pen Plotter gcode Generator

Nov 29th, 2023 (edited)
724
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // Variadic functions reference:
  2. //    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
  3.  
  4. // math constants/functions
  5. const pi = Math.PI; // reduced Math.PI to pi since it is commonly used
  6. const twoPi = pi*2; // same for 2pi
  7. const car = (r, theta) => new Vector2(r*Math.cos(theta), r*Math.sin(theta));    // polar to cartesian
  8. const lerp = (t, a, b) => t*(b-a)+a;
  9.  
  10. // primitive manipulation
  11. const sigFigs = (n, d=100)  => Math.round(n*d)/d;   // round n to the nearest 1/d (3.14159 >> 3.14)
  12. const revStr = (str) => str.split('').reverse().join('');   // reverses a string ("Hello" >> "olleH")
  13. const formatNum = (n, sep=',') => { // used to format large numbers (35829467 >> 35,829,467)
  14.     return revStr(revStr(n.toString()).match(/.{1,3}/g).join(sep));
  15. }
  16.  
  17.  
  18. // more specific version of typeof (e.g:  'typeof([1,2,3])' returns 'object' whereas 'getType([1,2,3])' returns 'Array')
  19. function getType(obj) {
  20.     if (obj === null) return 'null';
  21.     if (obj === undefined) return 'undefined';
  22.     return obj.__proto__.constructor.name;
  23. }
  24.  
  25. // list of allowed commands followed by list of allowed data types for said command
  26. const validCommands = {
  27.     'GCODE': ['String', 'Array'],   // insert gcode
  28.     'PATH': ['Array'],  // list of points to follow
  29.     'GOTO': ['Vector2', 'Vector3'], // go to point
  30.     'PEN': ['String'],  // "UP" or "DOWN"
  31.     'COMMENT': ['String'],  // add gcode comment
  32. };
  33.  
  34. /* possible alternate syntax:
  35. const validCommands = {
  36.     'String': ['GCODE', 'CODE', 'PEN', 'COMMENT'],
  37.     'Array': ['GCODE', 'CODE', 'PATH'],
  38.     'Vector2': ['GOTO'],
  39.     'Vector3': ['GOTO'],
  40. };
  41. */
  42.  
  43. // generate gcode header for a command
  44. function getHeader(cmd, data) {
  45.     switch (cmd) {
  46.         case 'GCODE':
  47.             return ``;
  48.  
  49.         case 'PATH':
  50.             return `Path [${data.length}]`;
  51.  
  52.         case 'GOTO':
  53.             return `Goto (${data.vector3().string()})`;
  54.  
  55.         case 'PEN':
  56.             return ``;
  57.  
  58.         case 'COMMENT':
  59.             return ``;
  60.     }
  61. }
  62.  
  63. // command object
  64. class Command {
  65.     constructor(cmd, data, headGcode=true) {
  66.         let validCmd = validCommands[cmd];
  67.         let validDta = validCmd != null ? validCmd.includes(getType(data)) : null;
  68.         if (!validCmd) throw(`InvalidCommand: '${cmd}'`);
  69.         if (!validDta) throw(`InvalidArgument: Type '${getType(data)}' for '${cmd}'`);
  70.  
  71.         this.cmd = cmd;
  72.         this.data = data;
  73.         this.head = headGcode ? `; ${getHeader(cmd, data)}` : '';
  74.     }
  75.  
  76.     // add gcode comment to command (may or may not remove)
  77.     comment(...txt) {}
  78.  
  79.     // will likely be removed in favor of global 'generateGcode' function
  80.     gcode() {
  81.         let code = this.comment != "" ? `; ${this.comment}` : '';
  82.  
  83.         switch (this.cmd) {
  84.             case 'GCODE':
  85.                 if (typeof(this.data) == 'string') {
  86.                     code += this.data;
  87.                 } else {
  88.                     this.data.forEach((line, i) => {
  89.                         code += line;
  90.                         code += (i < this.data.length-1) ? '\n' : '';
  91.                     });
  92.                 }
  93.                 break;
  94.  
  95.             case 'PATH':
  96.                 break;
  97.  
  98.             case 'GOTO':
  99.                 break;
  100.  
  101.             case 'PEN':
  102.                 break;
  103.  
  104.             case 'COMMENT':
  105.                 break;
  106.         }
  107.     }
  108. }
  109.  
  110. function gcode(command) {
  111.     let code = [`; ${command.head}\n`];
  112.  
  113.     switch (command.cmd) {
  114.         case 'GCODE':
  115.             if (typeof(command.data) == 'string') {
  116.                 code.push(command.data);
  117.             } else {
  118.                 code = code.concat(command.data);
  119.                 /*
  120.                 command.data.forEach((line) => {
  121.                     code.push(line);
  122.                 });
  123.                 */
  124.             }
  125.             break;
  126.            
  127.         case 'PATH':
  128.            
  129.             break;
  130.            
  131.         case 'GOTO':
  132.             break;
  133.            
  134.         case 'PEN':
  135.             break;
  136.            
  137.         case 'COMMENT':
  138.             break;
  139.     }
  140. }
  141.  
  142. // generate gcode from a list of commands
  143. function generateCode(commands) {
  144. }
  145.  
  146. // 2D vector/point object
  147. class Vector2 {
  148.     constructor(x=0, y=0) {
  149.         this.x = x;
  150.         this.y = y;
  151.     }
  152.  
  153.     add(v2) {
  154.         return new Vector2(this.x+v2.x, this.y+v2.y);
  155.     }
  156.    
  157.     addTo(v2) {
  158.         this.x += v2.x;
  159.         this.y += v2.y;
  160.     }
  161.  
  162.     sub(v2) {
  163.         return new Vector2(this.x-v2.x, this.y-v2.y);
  164.     }
  165.    
  166.     subFrom(v2) {
  167.         this.x -= v2.x;
  168.         this.y -= v2.y;
  169.     }
  170.  
  171.     mult(s) {
  172.         return new Vector2(this.x*s, this.y*s);
  173.     }
  174.    
  175.     multBy(s) {
  176.         this.x *= s;
  177.         this.y *= s;
  178.     }
  179.     times = this.mult;
  180.     timesBy = this.multBy;
  181.  
  182.     vector3(plane='xy') {
  183.         // confirm plane is valid:
  184.         if (typeof(plane.match(/^(?:x[yz]|y[zx]|z[xy])$/gi)) != 'object') {
  185.             throw new InvalidArgument(`'${plane}' is an invalid plane`);
  186.         }
  187.  
  188.         // create Vector3 with new plane mapping
  189.         let planeMap = plane.toLowerCase().split('');      
  190.         let newPlane = {'x': planeMap[0], 'y': planeMap[1]};
  191.         let vect3 = new Vector3();
  192.         vect3[newPlane.x] = this.x;
  193.         vect3[newPlane.y] = this.y;
  194.  
  195.         return vect3;
  196.     }
  197.  
  198.     toString(d=100) {
  199.         return `(${sigFigs(this.x, d)}, ${sigFigs(this.y, d)})`;
  200.     }
  201. }
  202.  
  203. // 3D vector/point object
  204. class Vector3 {
  205.     constructor(x=0, y=0, z=0) {
  206.         this.x = x;
  207.         this.y = y;
  208.         this.z = z;
  209.     }
  210.  
  211.     add(v2) {
  212.         return new Vector3(this.x+v2.x, this.y+v2.y, this.z+v2.z);
  213.     }
  214.    
  215.     addTo(v2) {
  216.         this.x += v2.x;
  217.         this.y += v2.y;
  218.         this.z += v2.z;
  219.     }
  220.  
  221.     sub(v2) {
  222.         return new Vector3(this.x-v2.x, this.y-v2.y, this.z-v2.z);
  223.     }
  224.    
  225.     subFrom(v2) {
  226.         this.x -= v2.x;
  227.         this.y -= v2.y;
  228.         this.z -= v2.z;
  229.     }
  230.  
  231.     mult(s) {
  232.         return new Vector3(this.x*s, this.y*s, this.z*s);
  233.     }
  234.    
  235.     multBy(s) {
  236.         this.x *= s;
  237.         this.y *= s;
  238.         this.z *= s;
  239.     }
  240.     times = this.mult;
  241.     timesBy = this.multBy;
  242.  
  243.     toString(d=100) {
  244.         return `(${sigFigs(this.x, d)}, ${sigFigs(this.y, d)}, ${sigFigs(this.z, d)})`;
  245.     }
  246. }
  247.  
  248.  
  249. // path functions
  250. class Bezier {
  251.     static pt(d) {
  252.         switch(d) {
  253.             case 1:
  254.                 return 0;
  255.             case 2:
  256.                 return new Vector2();
  257.             case 3:
  258.                 return new Vector3();
  259.             default:
  260.                 throw new TypeError(`Bezier curves can only be 1, 2, or 3 dimensional. A '${d}' dimensional Bezier curve is invalid.`);
  261.         }
  262.     }
  263.    
  264.     constructor(degree=3, dimension=2) {
  265.         degree++;
  266.        
  267.         this.points = [];
  268.         for (let i = 0; i < degree; i++) {
  269.             this.points.push(Bezier.pt(dimension));
  270.         }
  271.     }
  272.    
  273.     set points(pts) {
  274.         console.log(this.degree);
  275.         if (pts.length != this.degree) {
  276.            
  277.         }
  278.     }
  279.    
  280.     get degree() {
  281.         return this.points.length-1;
  282.     }
  283.    
  284.     get dimension() {
  285.         switch (getType(this.points[0])) {
  286.             case 'Number':
  287.                 return 1;
  288.             case 'Vector2':
  289.                 return 2;
  290.             case 'Vector3':
  291.                 return 3;
  292.         }
  293.     }
  294.    
  295.     set degree(_) {
  296.         throw new TypeError(`'Bezier.degree' is a read-only value.`);
  297.     }
  298.    
  299.     set dimension(_) {
  300.         throw new TypeError(`'Bezier.dimension' is a read-only value.`);
  301.     }
  302. }
  303.  
  304. class Epicycle {
  305.     constructor(radii, rates, offsets, inheritAngle=false) {
  306.         // inheritAngle means the angles of child paths are derrived from adding its angle to all of its ancestors' angles
  307.         // i.e: The rotation of an arm is relative to its parent, not to the world
  308.         this.arms = [];
  309.     }
  310. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement