svdsvglib
Version:
SVG Lib
1,652 lines (1,294 loc) • 129 kB
JavaScript
'use strict';
var runningUnderNodeJS = false;
var numericQuantity = null;
if (typeof exports !== "undefined")
{
runningUnderNodeJS = true;
console.log("Running under NodeJS");
}
exports.test= function(args, success, failure) {
executor(args,success,failure);
};
exports.testParse=function(textFile ) {
processTextStringToSVG(textFile);
};
exports.svdSVG=function() {
return new svdSVG(0);
}
exports.newFile=function() {
return newFile();
}
exports.svdSVGCommandList=function() {
return new svdSVGCommandList();
}
exports.svdSVGCreateCommand=function(commandIndex) {
return new svdSVGCreateCommand(commandIndex);
}
exports.svdSVGCreateCommandData=function() {
return new svdSVGCommandData(); ;
}
var DEBUG_LEVEL_0=0;
var DEBUG_LEVEL_1=1;
var DEBUG_LEVEL_2=2;
var DEBUG_LEVEL_3=3;
exports.svdSVGCommandEnum={firstPos:0, units: 0, depth: 1,moveTo: 2 , lineTo: 3, curvedLineTo: 4 , puzzleLineTo: 5, wavyLineTo:6, rect: 7, circle: 8, wavyRect: 9, boxLineTo: 10, arcTo: 11, text: 12, splineLineTo: 13, materialThickness: 14, cutType: 15, bitSize:16, ifCC:17, label:18, let:19, goto:20, then:21, image:22, closepath:23, include:24, keyLineTo: 25 , scarf:26, tolerance:27, warning:28, error:29, lastPos: 30}; // make sure last is set to +1 of last item
exports.svdSVGSides={ top : 0x01 , right : 0x02 , bottom : 0x04 , left : 0x08 };
exports.svdSVGCutType={firstPos:0, fill : 0 , outlineOnPath : 1 , outlineOutside : 2 , outlineInside : 3 , lastPos: 4};
exports.svdSVGDirection={noDirection:0, xDirection:1,yDirection:2,zDirection:3};
exports.svdSVGCutTypeText=[ "FILL" , "OUTLINEONPATH" , "OUTLINEOUTSIDE" , "OUTLINEINSIDE" ];
exports.globalError= "";
exports.debugLevel= DEBUG_LEVEL_1;
// Define a properties array that returns array of objects representing
// the accepted properties for your application
var properties = [
{id: "Offset File", type: "file-input"}
];
var listOfCommands=["UNITS","DEPTH OF CUT","MOVE TO","LINE TO","CURVED LINE TO", "PUZZLE LINE TO","WAVY LINE TO","RECT","CIRCLE","WAVY RECT", "BOX LINE TO","ARC TO","TEXT","SPLINE LINE TO","MATERIAL THICKNESS","CUT TYPE","BIT SIZE","IF","LABEL","LET","GOTO","THEN","IMAGE","CLOSEPATH","INCLUDE","KEY LINE TO","SCARF","TOLERANCE", "WARNING","ERROR"]; // make sure this and the enum are kept in sync
var successFunction;
var failureFunction;
var commandEnum= exports.svdSVGCommandEnum;
//
// gets the file contents from the server after it has been uploaded
//
function loadFile(fileName) {
//console.log(fileName);
var xhr = new XMLHttpRequest(); // to get the file after it has uploaded
xhr.open('GET', fileName, true);
xhr.onload = function(e) {
if (this.status == 200) {
var str = this.responseText;
processTextStringToSVG(str);
}
};
xhr.send();
}
// Define an executor function that generates a valid SVG document string,
// and passes it to the provided success callback, or invokes the failure
// callback if unable to do so
var executor = function(args, success, failure) {
var params = args[0];
var selectedVolumes = args[1];
successFunction=success;
failureFunction=failure;
var fileName;
if ( typeof params["Offset File"] != 'undefined' )
{
fileName= params["Offset File"];
if (fileName.length > 0 )
{
loadFile(fileName);
}
}
};
// this is the main guts. It takes the command file loaded
// in as a string, and calls the parse functions to process
// it to an SVG
function processTextStringToSVG(textFile) {
//console.log(commandList);
var svg = new svdSVG();
var commands=svg.parseCommandTextFile(textFile);
// console.log("Length of command list "+commands.length());
if (exports.globalError.length > 0)
{
failureFunction(exports.globalError);
}
else
{
svg.convertCommandListToSVG(commands);
if (exports.globalError.length > 0)
{
failureFunction(exports.globalError);
}
else
{
successFunction(svg.svgString());
}
}
}
//return the control point necessary for a bezier to pass through
// p2 given start p1 and end p3
// U is variable, but uses 0.5 as a default
function findPOintOnBexierQuadratic(p1,p2,p3){
var rtnPoint=0;
do {
var u =0.5;
p1=parseFloat(p1);
p2=parseFloat(p2);
p3=parseFloat(p3);
// try and calculate where on the line between points the middle point is, in order to closer calculate u
if (p3 != p2){
// u=Math.abs(p2-p1)/Math.abs(p3-p1);
// u=Math.abs(p2-p1)/(Math.abs(p3-p2)+Math.abs(p2-p1));
}
if (u == 0 ) {
rtnPoint= p1;
break;
}
if (u == 1 ) {
rtnPoint= p3;
break;
}
if ((u > 1 ) || ( u < 0 )) {
u=0.5;
break;
}
rtnPoint=(1/(2*(1-u)*u) * p2) - (((1-u)/(2*u)) * p1) - (u/(2*(1-u)) * p3);
} while (0);
console.log("u "+u+" p1 "+p1+" p2 "+p2+" p3 "+p3+" rtn "+rtnPoint);
return rtnPoint;
// https://github.com/mbostock/d3/blob/master/src/svg/line.js#L377
}
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
angleInDegrees-=90;
angleInDegrees= angleInDegrees % 360;
var angleInRadians = (angleInDegrees) * Math.PI / 180.0;
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
}
/*computes control points given knots K, this is the brain of the operation*/
function computeControlPoints(K)
{
p1=new Array();
p2=new Array();
n = K.length-1;
/*rhs vector*/
a=new Array();
b=new Array();
c=new Array();
r=new Array();
/*left most segment*/
a[0]=0;
b[0]=2;
c[0]=1;
r[0] = K[0]+2*K[1];
/*internal segments*/
for (i = 1; i < n - 1; i++)
{
a[i]=1;
b[i]=4;
c[i]=1;
r[i] = 4 * K[i] + 2 * K[i+1];
}
/*right segment*/
a[n-1]=2;
b[n-1]=7;
c[n-1]=0;
r[n-1] = 8*K[n-1]+K[n];
/*solves Ax=b with the Thomas algorithm (from Wikipedia)*/
for (i = 1; i < n; i++)
{
m = a[i]/b[i-1];
b[i] = b[i] - m * c[i - 1];
r[i] = r[i] - m*r[i-1];
}
p1[n-1] = r[n-1]/b[n-1];
for (i = n - 2; i >= 0; --i)
p1[i] = (r[i] - c[i] * p1[i+1]) / b[i];
/*we have p1, now compute p2*/
for (i=0;i<n-1;i++)
p2[i]=2*K[i+1]-p1[i+1];
p2[n-1]=0.5*(K[n]+p1[n-1]);
return {p1:p1, p2:p2};
}
function isNumber(n) {
// the last char may be a % sign, so remove and retry is necessary
if (n[n.length-1] =='%') {
n=n.slice(0,-1); // remove the % for the test
// return isNumber(n);
}
return !isNaN(parseFloat(n)) && isFinite(n);
}
// parses a string and find the x/y pair
function findPointOnTextLine(command) {
var data= new svdSVGCommandData();
var coords=command.split(",");
// console.log("coords.length "+coords.length+" command "+command+" "+typeof coords);
data.addData(coords);
return data;
}
function buildErrorString(line){
exports.globalError=" Line "+(line+1)+"-"+exports.globalError;
}
// finds the index of the command within the command list
function findCommandIndex(command) {
var com=command.trim().toUpperCase(); // get rid of trailing and leading whitespace
com=com.replace(/\s+/g, ' '); // this gets rid of double spaces
// is this a label ?
if (com[com.length-1] == ':') {
// so this is a label !
// console.log("Found label "+com);
return(commandEnum.label);
}
for (var loop=commandEnum.firstPos;loop < commandEnum.lastPos ; loop++){
// if (listOfCommands[loop] == com)
var temp = com.substr(0,listOfCommands[loop].length)
if (listOfCommands[loop] == temp)
{
// console.log(temp+" "+com);
return(loop);
}
}
return commandEnum.lastPos;
}
// candidate to go into it's own separate library
/**
* inbuilt variables
* @author andy (12/17/2017)
*/
var UNITS= '$units';
var CUTTYPE= '$cuttype';
var BITSIZE= '$bitsize';
var BITSIZEVARIANCE= '$bitsizevariance';
var CLOSEPATH = '$closepath';
var DEPTHOFCUT = '$depthofcut';
var MINY="$miny";
var MINX="$minx";
var MAXY="$maxy";
var MAXX="$maxx";
var MATERIALTHICKNESS="$materialthickness";
var DOGBONES = '$dogbones';
var OFFSETX= "$svgoffsetx";
var OFFSETY= "$svgoffsety";
var TOLERANCE= "$tolerance";
var PERCENT="$percent";
var OLDSTYLECOMMAND="$oldstylecommand";
/**
* Variables which would be used within a file
*
* @author andy (12/17/2017)
*/
var X="x";
var Y="y";
var MALE="male"; //male or frmale is just resolved to this one var
var DELTAX="deltax";
var DELTAY="deltay";
var REPEAT="repeat";
var INTERSPACE="interspace";
var WIDTH="width";
var HEIGHT="height";
var NOSTEPS="nosteps";
var RADIUS="radius";
var RADIUSX="rx";
var RADIUSY="ry";
var ROTATE="rotate";
var TRANSFORM="transform";
var OFFSETX1="sizex1";
var OFFSETX2="sizex2";
var OFFSETX3="sizex3";
var OFFSETY1="sizey1";
var OFFSETY2="sizey2";
var FONTFAMILY="fontfamily";
var FONTTEXT="text";
var FONTSIZE="fontsize";
var STARTANGLE="startangle";
var ENDANGLE="endangle";
var SWEEP="sweep";
/**
* contants which are to define a fixed item to a more sensible
* constant
*
* @author andy (12/17/2017)
*/
var PERCENT_RAW="%";
var newFile = function() {
var rtn="";
var varPrefix= ";Variable: ";
console.log("in new file");
rtn += ";Describe the function of this file here12\n";
rtn += ";\n";
rtn += ";\n";
rtn += ";Date this file was created or modified, your choice\n";
rtn += ";"+Date().toString()+"\n";
rtn +="\n";
rtn +="\n";
rtn +=";Best to first define your units used in dimensions\n";
rtn +=";use either mm or in\n";
rtn +=";default is in\n";
rtn +=varPrefix+UNITS+"\n";
rtn +=listOfCommands[exports.svdSVGCommandEnum.units]+"\n";
rtn +="in\n";
rtn +="\n";
rtn +="\n";
rtn +=";include statements allow you to set a series of variables or common blocks\n";
rtn +=";to be used in your files\n";
rtn +=";Anything after the include statement will override what is in the include file\n";
rtn +=";This allows to to define a set of common blocks or variables, and then specialise in each file\n";
rtn +=";There is nothing special about the name dimensions.txt use anything you want ;-)\n";
rtn +="\n";
rtn +=";include dimensions.txt\n";
rtn +="\n";
rtn +=";This won't be automatically imported into Easel, you will need to set manually\n";
rtn +=varPrefix+BITSIZE+"\n";
rtn +=listOfCommands[exports.svdSVGCommandEnum.bitSize]+"\n";
rtn +="1/8 \n";
rtn +="\n";
rtn +=varPrefix+MATERIALTHICKNESS+"\n";
rtn +=listOfCommands[exports.svdSVGCommandEnum.materialThickness]+"\n";
rtn +="1/4 \n";
rtn +="\n";
rtn +=";Use % or actual DoC\n";
rtn +=";This won't be automatically imported into Easel, you will need to set manually\n";
rtn +=varPrefix+DEPTHOFCUT+"\n";
rtn +=listOfCommands[exports.svdSVGCommandEnum.depth]+"\n";
rtn +="100% \n";
rtn +="\n";
rtn +=varPrefix+CUTTYPE+"\n";
rtn +=listOfCommands[exports.svdSVGCommandEnum.cutType]+"\n";
rtn +=exports.svdSVGCutTypeText[exports.svdSVGCutType.fill]+"\n";
rtn +=";"+exports.svdSVGCutTypeText[exports.svdSVGCutType.outlineOnPath]+"\n";
rtn +=";"+exports.svdSVGCutTypeText[exports.svdSVGCutType.outlineOutside]+"\n";
rtn +=";"+exports.svdSVGCutTypeText[exports.svdSVGCutType.outlineInside]+"\n";
rtn +="\n";
rtn +="\n";
rtn +=";The following are examples of how to declare variables\n";
rtn +=";Although not enforced, it is good to keep to the convention that built in variables\n";
rtn +=";start with $, and that other variables do not. This can make it easier to debug.\n";
rtn +="let\n";
rtn +="widthOfPiece =4;\n";
rtn +="heightOfPiece =5;\n";
rtn +="x =9;\n";
rtn +="\n";
rtn +=";The following are examples of commands to use\n";
rtn +=";Most commands have two forms. \n";
rtn +=";Shorthand of numbers in an order e.g 0,0,10,20\n";
rtn +=";Longhand e.g x=0, y=0, width=10, height=20\n";
rtn +=";When using shorthand, other variables specific to that item can be added \n";
rtn +=";in longhand at the end, but nothing that would normally be used for that command in longhand\n";
rtn +="\n";
rtn +=listOfCommands[exports.svdSVGCommandEnum.moveTo]+"\n";
rtn +=X+"=0,"+Y+"=0\n";
rtn +=";0,0\n";
rtn +="\n";
rtn +="; A basic line from one location to another";
rtn +="\n";
rtn +=listOfCommands[exports.svdSVGCommandEnum.lineTo]+"\n";
rtn +=X+"=1,"+Y+"=2\n";
rtn +=";1,2\n";
rtn +="\n";
rtn +=listOfCommands[exports.svdSVGCommandEnum.splineLineTo]+"\n";
rtn +=X+"=1.5,"+Y+"=2\n";
rtn +=X+"=2,"+Y+"=2\n";
rtn +=";1.5,2\n";
rtn +=";2,2\n";
rtn +="\n";
rtn +=listOfCommands[exports.svdSVGCommandEnum.circle]+"\n";
rtn +=X+"=4,"+Y+"=4, "+RADIUS+"=0.5\n";
rtn +=";4,4, 0.5\n";
rtn +="\n";
rtn +=listOfCommands[exports.svdSVGCommandEnum.rect]+"\n";
rtn +=";rx, ry, rotate are optional. For shorthand, they are added in that order, longhand use whichever you want\n";
// rtn +="x=10,y=20,width=widthOfPiece,height=heightOfPiece, rotate=0,cuttype=outsideoutline\n";
rtn +="x=20,y=5,width=widthOfPiece,height=heightOfPiece, "+ROTATE+"=30,"+CUTTYPE+"=fill\n";
rtn +=";20,5,widthOfPiece,heightOfPiece \n";
rtn +="\n";
rtn +=listOfCommands[exports.svdSVGCommandEnum.text]+"\n";
rtn +="x=5,y=15,fontsize=10,fontfamily=Ariel, text = blah blah \n";
rtn +=";5,15,10,Arial, blah blah \n";
return(rtn);
}
/**
* d
*/
function svdSVG() {
this.defaultVars = function() {
this.svg = '';
this.pathOpened = false;
this.offsetX=0; // used if there are negative x points to move the centerline into the middle
this.offsetY=0; // used if there are negative y points to move the centerline into the middle
this.drawingWidth=0;
this.drawingHeight=0;
this.lastPoint= new svdSVGCommandData();
this.hasText=false;
this.hasTextConverted=false;
this.parser=require("mathjs");
this.globalParserScope={};
this.setVariable(CUTTYPE,exports.svdSVGCutType.fill,this.globalParserScope);
this.setVariable(UNITS,'in',this.globalParserScope);
this.setVariable(BITSIZE,'0.125',this.globalParserScope);
this.setVariable(BITSIZEVARIANCE,'2',this.globalParserScope);
this.setVariable(DEPTHOFCUT,0,this.globalParserScope);
this.setVariable(CLOSEPATH,1 ,this.globalParserScope);
this.setVariable(MATERIALTHICKNESS, 0.5 ,this.globalParserScope);
this.setVariable(MAXX,-99999999999,this.globalParserScope);
this.setVariable(MAXY,-99999999999,this.globalParserScope);
this.setVariable(MINX, 99999999999,this.globalParserScope);
this.setVariable(MINY, 99999999999,this.globalParserScope);
this.setVariable(DOGBONES, 0 ,this.globalParserScope);
this.setVariable(OFFSETX, 0 ,this.globalParserScope);
this.setVariable(OFFSETY, 0 ,this.globalParserScope);
this.setVariable(TOLERANCE, 0 ,this.globalParserScope);
this.setVariable(PERCENT, 0.01 ,this.globalParserScope);
}
/**
* Basic logging function. Has ability to have different logging
* levels
*/
this.log=function(level,logStr) {
if (this.stackTrace != undefined) {
var trace = this.stackTrace.get();
// console.log(trace[1].getFunctionName()+":"+trace[1].getLineNumber()+" "+" " +logStr);
if (level <= exports.debugLevel) {
console.log(trace[1].getFunctionName()+":"+trace[1].getLineNumber()+" "+" " +logStr);
}
}
}
// creates the SVG header including the viewbox functions.
// make sure the dimensions have been set before calling this
this.addSVGDimensions = function() {
// this.defaultVars();
var width = this.drawingWidth+this.offsetX;
var height= this.drawingHeight+this.offsetY;
var units = this.getVariable(UNITS,this.globalParserScope);
this.svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg\n xmlns="http://www.w3.org/2000/svg" version="1.0"\n width="'+this.drawingWidth+units+'"\n height="' +this.drawingHeight+units+'"\n viewBox="'+this.offsetX+" "+this.offsetY+" "+width+' '+height+'"\n preserveAspectRatio="xMinYMin meet" \n>'+this.svg;
}
this.closeSVG = function() {
this.closePath(this.globalParserScope); // in case it was never closed
this.svg+='\n</svg>';
this.drawingWidth=parseFloat(Math.abs(this.parser.eval(MAXX, this.globalParserScope)-this.parser.eval(MINX, this.globalParserScope)));
this.drawingHeight=parseFloat(Math.abs(this.parser.eval(MAXY, this.globalParserScope)-this.parser.eval(MINY, this.globalParserScope)));
// this.offsetX=parseFloat(0-this.limits.minX);
// this.offsetY=parseFloat(0-this.limits.minY);
this.offsetX=parseFloat(this.parser.eval(MINX, this.globalParserScope));
this.offsetY=parseFloat(this.parser.eval(MINY, this.globalParserScope));
// console.log ("limits.maxX "+this.limits.maxX+" limits.minX "+this.limits.minX+" limits.maxY "+this.limits.maxY+" limits.minY "+this.limits.minY);
this.addSVGDimensions(); // add in the dimensions etc
if (this.hasText == true) {
if (this.hasTextConverted == false) {
// used text, but it hasn't been converted yet
this.convertTextToPaths();
}
}
}
this.setUnits=function(currentCommand,context) {
// var units = this.getVariableUnique(UNITS,context);
var units = context[UNITS];
context[UNITS]=""; // clear it out, so we know if it got set below
do {
// console.log(dim);
// console.log(context);
for (var loop2=0;loop2 < currentCommand.data.length;loop2++) {
var loopContext=this.populateContext(currentCommand, context, loop2);
if (exports.globalError.length > 0) break;
}
if (exports.globalError.length > 0) break;
var newUnits=this.getVariable(UNITS,context); // clear it out, so we know if it got set below
if (exports.globalError.length > 0) break;
// console.log("newUnits "+newUnits+" oldunits "+units);
if (newUnits === "") {
// it wasn't set
for (var loop2=0;loop2 < currentCommand.data.length;loop2++) {
newUnits= currentCommand.data[loop2].data[0].trim();
}
}
newUnits=newUnits.toLowerCase();
if ((newUnits !="in") && (newUnits !="mm")) {
exports.globalError="Dimensions should be in or mm";
break;
}
units=newUnits;
// console.log("newUnits "+newUnits+" oldunits "+units);
} while (0);
// console.log("dimensions= "+units);
this.setVariable(UNITS,units,context); // units always get set !
}
// adds a svg open path statement to a string
this.openPath=function() {
if (this.pathOpened === false) {
this.svg+='\n<path d="'
this.pathOpened=true;
// console.log("path opened");
}
}
this.closePath=function(context) {
do {
if (this.pathOpened === true) {
var closePath = this.processNumber(CLOSEPATH,this.globalParserScope); if (exports.globalError.length > 0) break;
if (closePath > 0) {
this.addCommandToSVGString("Z");
}
this.svg+='"';
this.addStyleToPath(context);
this.closeTag();
this.pathOpened=false;
}
} while (0);
}
// returns a copy of the svg string
this.closeTag=function() {
this.svg+='\n';
this.svg+='/>';
this.svg+='\n';
}
// returns a copy of the svg string
this.svgString=function() {
// console.log(this.globalParserScope);
return this.svg;
}
//
// given a command list of commands and points,
// parses it and generates an svg string
//
this.convertCommandListToSVG =function(commandList) {
var width = 20;
var length = 20;
do {
if (runningUnderNodeJS == true) {
this.stackTrace = require('stack-trace');
}
this.commandList=commandList;
this.commandListPos=0;
this.executeCommandListIncludes();
if (exports.globalError.length > 0)
{
buildErrorString(this.commandListPos);
break;
}
this.commandListPos=0;
this.executeCommandList();
if (exports.globalError.length > 0)
{
buildErrorString(this.commandListPos);
break;
}
this.closePath(this.globalParserScope);
this.closeSVG();
console.log ("drawingWidth "+this.drawingWidth+" drawingHeight "+this.drawingHeight+" offsetX "+this.offsetX+" offsetY "+this.offsetY);
if (runningUnderNodeJS == true) {
// var Gcanvas = require('gcanvas');
//var driver = new Gcanvas.GcodeDriver({
// write: function(cmd) {
// console.log(cmd);
// }
//});
//var ctx = new Gcanvas(driver);
// ctx.depthOfCut=5;
// ctx.depth=1;
// ctx.circle(10,10,5);
}
} while (0);
if (exports.globalError.length > 0)
{
return "";
}
// console.log(this.svg);
return(this.svg);
}
// process any includes. This is done prior
this.executeCommandListIncludes =function() {
do {
if ( (typeof this.commandList) == 'undefined' ) {
exports.globalError="ExecuteCommandList called with a zero length command list";
break;
}
if ( this.commandList.length() == 0) {
exports.globalError="ExecuteCommandList called with a zero length command list";
break;
}
var includeFound=false;
var includeDepth=0;
for (this.commandListPos=0;this.commandListPos < this.commandList.length();this.commandListPos++) {
var currentCommand=this.commandList.itemAt(this.commandListPos);
//console.log("command "+currentCommand.pos+" number of points "+currentCommand.data.length);
if (exports.globalError.length > 0)
{
// buildErrorString(this.commandListPos);
break;
}
if (currentCommand.valid == false) {
continue;
}
switch(currentCommand.command)
{
case commandEnum.include :
{
this.include(currentCommand);
includeFound=true;
this.commandListPos=-1;
}
break;
}
// console.log("exports.globalError.length "+exports.globalError.length);
if (exports.globalError.length > 0)
{
return;
}
}
} while (0);
}
this.executeCommandList =function() {
do {
if ( (typeof this.commandList) == 'undefined' ) {
exports.globalError="ExecuteCommandList called with a zero length command list";
break;
}
if ( this.commandList.length() == 0) {
exports.globalError="ExecuteCommandList called with a zero length command list";
break;
}
for (this.commandListPos=0;this.commandListPos < this.commandList.length();this.commandListPos++) {
var currentCommand=this.commandList.itemAt(this.commandListPos);
//console.log("command "+currentCommand.command+" number of points "+currentCommand.data.length);
if (exports.globalError.length > 0)
{
break;
}
if (currentCommand.valid == false) {
continue;
}
var context= this.deepCopy(this.globalParserScope);
// console.log("loop "+currentCommand.command);
// console.log(context);
switch(currentCommand.command)
{
case commandEnum.materialThickness :
{
this.materialThicknessCalc(currentCommand,this.globalParserScope);
}
break;
case commandEnum.cutType:
{
this.cutType(currentCommand,this.globalParserScope);
}
break;
case commandEnum.include :
{
console.log("You should really fix this, second include");
}
break;
case commandEnum.depth :
{
this.depthOfCut(currentCommand,this.globalParserScope);
}
break;
case commandEnum.units :
{
// console.log("You should really fix this, using MM");
this.setUnits(currentCommand,this.globalParserScope) ;
}
break;
case commandEnum.moveTo :
{
this.moveTo(currentCommand,context);
}
break;
case commandEnum.text :
{
this.text(currentCommand,context);
}
break;
case commandEnum.boxLineTo :
{
this.boxLineTo(currentCommand,context);
}
break;
case commandEnum.puzzleLineTo :
{
this.puzzleLineTo(currentCommand,context);
}
break;
case commandEnum.keyLineTo :
{
this.keyLineTo(currentCommand,context);
}
break;
case commandEnum.lineTo :
{
this.lineTo(currentCommand,context);
}
break;
case commandEnum.rect :
{
this.rect(currentCommand,context);
}
break;
case commandEnum.circle :
{
this.circle(currentCommand,context);
}
break;
case commandEnum.splineLineTo :
{
this.splineLineTo(currentCommand,context);
}
break;
case commandEnum.arcTo :
{
this.arcTo(currentCommand,context);
}
break;
case commandEnum.curvedLineTo :
{
this.curvedLineTo(currentCommand,context);
}
break;
case commandEnum.wavyRect :
{
this.wavyRect(currentCommand,context);
}
break;
case commandEnum.wavyLineTo :
{
this.wavyLineTo(currentCommand,context);
}
break;
case commandEnum.let :
{
this.let(currentCommand,this.globalParserScope);
}
break;
case commandEnum.ifCC :
{
this.ifCC(currentCommand);
}
break;
case commandEnum.bitSize :
{
this.bitsize(currentCommand,this.globalParserScope);
}
break;
case commandEnum.image :
{
this.image(currentCommand,context);
}
break;
case commandEnum.scarf :
{
this.scarf(currentCommand,context);
}
break;
case commandEnum.label :
{
// does nothing
}
break;
case commandEnum.error :
{
// does nothing
}
break;
case commandEnum.warning :
{
// does nothing
}
break;
// default:
// {
// console.log("Unknown tag "+currentCommand.pos);
// exports.globalError="Unknown Tag";
// }
// break;
}
if (exports.globalError.length > 0)
{
return;
}
}
} while (0);
}
/**
* Does a deep javascript copy. This may not be the best for
* super deep or speed, but seems to work for what we need !
*/
this.deepCopy=function(obj) {
var str = JSON.stringify( obj );
// console.log(str);
var val=JSON.parse(str);
return(val);
}
/**
* Creates and populates a new context based on the current
* context and the args passed in
*/
this.populateContext=function(currentCommand,context,index) {
var loopContext;
do {
loopContext = this.deepCopy(context);
loopContext[OLDSTYLECOMMAND]=true;
if (exports.globalError.length > 0) break;
// console.log("currentCommand.data[index] "+currentCommand.data[index]);
if ( currentCommand.data[index] === undefined) break;
for (var loop3=0;loop3 < currentCommand.data[index].data.length;loop3++) {
var tempData = currentCommand.data[index].data[loop3].toString();
// console.log("tempData = "+tempData+" "+ typeof tempData);
var temp = tempData.search("=");
if (temp > 0) {
var tempStr = currentCommand.data[index].data[loop3].substr(0,temp).trim().toLowerCase();
var tempVal= currentCommand.data[index].data[loop3].substr(temp+1); // is a string value
//figure out ones where we just take the text
// console.log("populateContext "+tempData+" as tempStr="+tempStr+" tempVal=" +tempVal);
if (tempStr === FONTFAMILY) {
// this.parser.set(tempStr, "blah blah",loopContext);
loopContext[FONTFAMILY]=tempVal;
}
else if (tempStr == FONTTEXT) {
loopContext[FONTTEXT]=tempVal;
}
else if (tempStr == CUTTYPE) {
loopContext[CUTTYPE]=tempVal.toUpperCase();
}
else if (tempStr == DEPTHOFCUT) {
// var tempProcessVal=this.processNumber(tempVal,loopContext); // process to a number if it can
loopContext[DEPTHOFCUT]=tempVal;
this.depthOfCut(currentCommand,loopContext);
}
else {
// this.processNumber(currentCommand.data[index].data[loop3],loopContext); if (exports.globalError.length > 0) break;
this.processNumber(currentCommand.data[index].data[loop3],loopContext);
}
loopContext[OLDSTYLECOMMAND]=false;
}
else {
// this.processNumber(currentCommand.data[index].data[loop3],loopContext); if (exports.globalError.length > 0) break;
// this.processNumber(currentCommand.data[index].data[loop3],loopContext);
}
// console.log(currentCommand.data[index].data[loop3]);
}
} while (0);
return(loopContext);
}
this.processNumber=function(num,context) {
var val=null;
try {
// num may be an expression, or just a number, we don't know
// num = this.parser.unit(num);
// num may be an expression, or just a number, we don't know
var validMatch="%|C|D/i";
var matchPos = num.toString().match(validMatch);
var setMatchPos=false;
this.log(DEBUG_LEVEL_2, "num "+num+" match "+matchPos);
// // check for an num after a match, just in case it's a word like "chicken"
if (matchPos != null) {
if (((num[1] >="1") && (num[1] <="9")) || (num[1] == "-")) {
// best guess that this is a number
// this.modifiers[loop]=value[0];
num=num.substring(1);
}
if (num[num.length-1] == matchPos) {
if (matchPos == '%') {
// replace % with "percent" variable
// num=num.substring(0,num.length-1);
// num+=" ";
// num+=PERCENT;
// this.log(DEBUG_LEVEL_1, "num "+num+" match "+matchPos);
setMatchPos=true;
}
else {
// this.modifiers[loop]=match;
num=num.substring(0,num.length-1);
}
}
}
// console.log("num "+num);
val=this.parser.eval(num, context);
if (setMatchPos == true) {
// context[num].modifiers=matchPos;
}
// console.log("2");
// val = parseFloat(val);
} catch (err) {
console.log("processNumber error for \""+num+"\" which is a "+this.parser.typeof(num)+" isNumeric "+this.parser.isNumeric(num));
console.log(context);
console.log(err);
exports.globalError=String(err);
}
return(val);
}
this.setVariable=function(variable,value,context) {
var val=null;
try {
var num=variable+"="+value;
// this.log(DEBUG_LEVEL_2, variable+" to "+value);
// val=this.processNumber(num, context);
// val=this.parser.set(, context);
context[variable]=value;
} catch (err) {
console.log("setVariable error for \""+num+"\" which is a "+this.parser.typeof(num));
console.log(context);
console.log(err);
exports.globalError=String(err);
// buildErrorString(this.commandListPos);
}
// val=parseFloat(val);
// console.log("num "+num+" val "+val+" type "+typeof val);
return(val);
}
this.getVariable=function(num,context) {
var val = this.processNumber(num,context);
return(val);
}
this.getVariableUnique=function(num,context) {
var rtn;
do {
var rtnUD=this.getVariable(num,context);
if (exports.globalError.length > 0)
{
return;
}
// rtn=this.deepCopy(rtnUD);
rtn = Object.assign({}, rtnUD);
// console.log("orig "+rtnUD+" deep "+rtn);
} while (0);
return(rtn);
}
this.roundNumber=function(num) {
num=parseFloat(num);
if (num != parseInt(num))
{
num+=0.0005;
num=num * 1000;
num=Math.round(num);
num/=1000;
}
return(num);
}
// adds a number to the SVG String.
// truncates the length so as not to have silly numbers with many DP
this.addNumberToSVGString=function(num) {
num=this.roundNumber(num);
if ((this.svg.length > 0) && (this.svg[this.svg.length-1] != ' ')) {
this.svg+=" ";
}
this.svg+=num+" ";
}
// adds a X location to the SVG String.
// truncates the length so as not to have silly numbers with many DP
this.addXLocationToSVGString=function(num) {
num+=this.offsetx;
this.addNumberToSVGString(num);
}
// adds a Y location to the SVG String.
// truncates the length so as not to have silly numbers with many DP
this.addYLocationToSVGString=function(num) {
num+=this.offsety;
this.addNumberToSVGString(num);
}
this.addQuotedTagToSVGString=function(tag,num) {
if ((this.svg.length > 0) && (this.svg[this.svg.length-1] != ' ')) {
this.svg+=" ";
}
this.svg+='\n';
if (tag === ROTATE) {
this.svg+=TRANSFORM;
num=tag+"("+num+")";
}
else {
this.svg+=tag;
}
this.svg+='=';
this.addQuotedNumberToSVGString(num);
}
this.addQuotedNumberToSVGString=function(num) {
if (typeof(num) === 'number') {
num=this.roundNumber(num);
}
// if ((this.svg.length > 0) && (this.svg[this.svg.length-1] != ' ')) {
// this.svg+=" ";
// }
this.svg+='"'+num+'"';
}
this.addCRToSVGString=function() {
this.svg+="\n";
}
// adds a command to the SVG String
this.addCommandToSVGString=function(command) {
if (this.svg.length > 0) {
if (this.svg[this.svg.length-1] != ' ') {
this.svg+=" ";
}
}
this.addCRToSVGString();
this.svg+=command+" ";
this.addCRToSVGString();
}
// adds the style tag to the path and closes it
this.addStyleToPath=function(context) {
var depth = this.getVariable(DEPTHOFCUT,context);
var cutTypeStyle = this.getVariable(CUTTYPE,context);
// console.log("cut type "+exports.svdSVGCutType.fill+" "+cutTypeStyle+" depth "+depth);
this.svg+='\n';
if (cutTypeStyle == exports.svdSVGCutType.fill) {
this.svg+='style="fill:rgb('+depth+','+depth+','+depth+')" ';
}
else {
var sw= "0.1";
var bs=this.getVariable(BITSIZE,context);
if (bs > 0) {
sw=bs;
var closePath = this.getVariable(CLOSEPATH,context);
if (closePath == 0) {
// an open path we need to add the variance to make sure it gets cut
var variance = this.getVariable(BITSIZEVARIANCE,context);
if (variance > 0) {
sw=sw + ((bs * variance) / 100);
}
}
}
this.svg+='fill="none" stroke="rgb('+depth+','+depth+','+depth +')" stroke-width="'+sw+'"';
}
}
// searchs the command list to find a label
this.findLabel=function(label) {
var rtn= -1;
do {
// console.log("length "+this.commandList.length());
for (var loop=0;loop < this.commandList.length();loop++) {
var currentCommand=this.commandList.itemAt(loop);
// console.log("pos "+currentCommand.command+" "+currentCommand );
switch(currentCommand.command)
{
case commandEnum.label :
{
// console.log(currentCommand.length+" loop "+loop);
var temp = currentCommand.data[0].data[0];
// console.log("temp "+temp);
if (temp === label) {
rtn = loop;
break;
}
}
break;
}
}
} while (0);
return(rtn);
}
this.include=function(currentCommand) {
do {
if ( currentCommand.data.length != 1 )
{
exports.globalError="Include Command must have one value";
return;
}
for (var loop=0;loop < currentCommand.data.length;loop++) {
var include = currentCommand.data[loop].data[0];
this.log("adding include "+include);
if (include.length > 0) {
var fs = require('fs'); // loading/saving files
var os = require('os'); // loading/saving files
var path = require('path'); // path parsing functionality
currentCommand.valid=false; // regardless of outcome, we don't process this include again
try {
var data=fs.readFileSync(include, 'utf8');
} catch (err) {
exports.globalError =String(err);
return;
}
if (exports.globalError.length > 0)
{
return;
}
// console.log("data "+data);
var insertList= this.parseCommandTextFile(data);
if (exports.globalError.length > 0)
{
// buildErrorString(this.commandListPos);
return;
}
// console.log(insertList);
// console.log(this.commandListPos+" "+insertList.length());
// now need to add the new command list to the existing command list.
for (var loop2=0;loop2 < insertList.length();loop2++) {
this.commandList.insert(this.commandListPos+1+loop2,insertList.itemAt(loop2));
// console.log(insertList.itemAt(loop2));
}
}
if (exports.globalError.length > 0)
{
// buildErrorString(this.commandListPos);
return;
}
}
} while (0);
// console.log(this.commandList);
}
this.materialThicknessCalc=function(currentCommand,context) {
do {
if ( currentCommand.data.length != 1 )
{
exports.globalError="Material Thickness Command must have one value";
break;
}
var newThickness = this.processNumber(currentCommand.data[0].data[0],context); if (exports.globalError.length > 0) break;
this.setVariable(MATERIALTHICKNESS,newThickness,context);
if (exports.globalError.length > 0)
{
// buildErrorString(this.commandListPos);
break;
}
} while (0);
}
// this inserts an SVG image into the SVG at the reqired coords
this.image=function(currentCommand,context) {
do {
if ( currentCommand.data.length == 0 )
{
exports.globalError="Image Command must have at least one value";
break;
}
this.closePath(context); //close out any existing paths
for (var loop2=0;loop2 < currentCommand.data.length;loop2++) {
var x = this.processNumber(currentCommand.data[loop2].data[0]); if (exports.globalError.length > 0) break;
var y = this.processNumber(currentCommand.data[loop2].data[1]); if (exports.globalError.length > 0) break;
var convertFileName = currentCommand.data[loop2].data[2];
// read in the image sync
var data=fs.readFileSync(convertFileName, 'utf8');
console.log(data);
}
} while (0);
}
// this inserts a Scarf joint into the SVG at the reqired coords
this.scarf=function(currentCommand,context) {
do {
if ( currentCommand.data.length == 0 )
{
exports.globalError="Image Command must have at least one value";
break;
}
this.closePath(context); //close out any existing paths
for (var loop2=0;loop2 < currentCommand.data.length;loop2++) {
var x = this.processNumber(currentCommand.data[loop2].data[0],context); if (exports.globalError.length > 0) break;
var y = this.processNumber(currentCommand.data[loop2].data[1],context); if (exports.globalError.length > 0) break;
var convertFileName = currentCommand.data[loop2].data[2];
// read in the image sync
var data=fs.readFileSync(convertFileName, 'utf8');
console.log(data);
}
} while (0);
}
this.cutType=function(currentCommand,context) {
do {
if ( currentCommand.data.length < 1 )
{
exports.globalError="Cut Type Command must have one value";
break;
}
var text = String(currentCommand.data[0].data[0]);
text=text.trim().toUpperCase(); // get rid of trailing and leading whitespace
text = text.replace(/\s/g, '');
var valFound=false;
// console.log("cut type text "+text);
for (var loop=exports.svdSVGCutType.firstPos;loop < exports.svdSVGCutType.lastPos ; loop++){
if (exports.svdSVGCutTypeText[loop] == text)
{
this.closePath(context);
this.setVariable(CUTTYPE,loop,context);
valFound=true;
// console.log("cut type text "+this.cutTypeStyle);
break;
}
}
if (valFound == false) {
exports.globalError="Cut Type "+currentCommand.data[0].data[0] +" not found";
break;
}
} while (0);
}
this.depthOfCut=function(currentCommand,context) {
do {
// if ( currentCommand.data.length != 1)
// {
// exports.globalError="Depth Command must have only one value "+currentCommand.data;
// break;
// }
this.doDepthOfCut(currentCommand,context);
} while (0);
}
this.doDepthOfCut=function(currentCommand,context) {
do {
// var newDepth = String(currentCommand.data[0].data[0]);
console.log(context);
var newDepth = this.getVariable(DEPTHOFCUT,context);
if ( newDepth === undefined )
{
exports.globalError="doDepthOfCut-depth is undefined";
break;
}
var modifiers = currentCommand.data[0].modifiers[0];
var materialThickness = this.getVariable(MATERIALTHICKNESS,context).value;
this.log(DEBUG_LEVEL_1,"new depth "+newDepth+ " modifiers "+modifiers+" mat thickness "+materialThickness+" NewDepth "+newDepth+" error "+exports.globalError.length);
if (exports.globalError.length > 0)
{
break;
}
// console.log(context);
if (materialThickness > 0) {
if (modifiers == '%')
{
// it will just fall out in this case using existing logic
}
else
{
newDepth *= 255;
newDepth /= materialThickness;
}
}
// console.log("11 new depth "+this.depth+ " modifiers "+modifiers+" mat thickness "+materialThickness+" new "+String(currentCommand.data[0].data[0])+" NewDepth "+newDepth);
if (modifiers == '%')
{
newDepth=parseFloat(newDepth) * 255;
newDepth/=100;
}
// depth is "depth of cut", which is inverse
if (newDepth < 0 ) {
newDepth = 0;
}
if (newDepth > 255 ) {
newDepth = 255;
}
// console.log("12 new depth "+this.depth+ " modifiers "+modifiers+" mat thickness "+materialThickness+" new "+String(currentCommand.data[0].data[0])+" NewDepth "+newDepth);
newDepth = 255-newDepth;
var depth = this.getVariable(DEPTHOFCUT,context);
if (exports.globalError.length > 0)
{
this.log(DEBUG_LEVEL_1,"new depth "+newDepth+ " modifiers "+modifiers+" mat thickness "+materialThickness+" NewDepth "+newDepth+" error "+exports.globalError.length);
break;
}
if (depth != newDepth)
{
this.closePath(context);
}
newDepth=parseInt(newDepth);
this.setVariable(DEPTHOFCUT,newDepth,context);
// this.log(DEBUG_LEVEL_1,"new depth "+newDepth+ " modifiers "+modifiers+" mat thickness "+materialThickness+" NewDepth "+newDepth+" error "+exports.globalError.length);
if (exports.globalError.length > 0)
{
// buildErrorString(this.commandListPos);
break;
}
// console.log(this.globalParserScope);
// console.log("1 new depth "+this.depth+ " modifiers "+modifiers+" mat thickness "+materialThickness+" new "+String(currentCommand.data[0].data[0])+" NewDepth "+newDepth);
} while (0);
}
this.bitsize=function(currentCommand,context) {
do {
if ( currentCommand.data.length != 1 )
{
exports.globalError="Bitsize Command must have one value";
break;
}
// console.log("bitsize");
var bs = this.processNumber(currentCommand.data[0].data[0],context);
if (exports.globalError.length > 0)
{
break;
}
this.bitSize=bs;
this.setVariable(BITSIZE,bs,context);
if (exports.globalError.length > 0)
{
break;
}
// console.log(this.globalParserScope);
} while (0);
}
/*
Generates an arc
0- X center coordinate
1- Y center coordinate
2- Radius X
3- Radius Y
4- start Angle in Degrees
5- end Angle in Degrees
6-