2013-12-20 16:31:41 +01:00
/ *
* This file is part of the Doodle3D project ( http : //doodle3d.com).
*
* Copyright ( c ) 2013 , Doodle3D
* This software is licensed under the terms of the GNU GPL v2 or later .
* See file LICENSE . txt or visit http : //www.gnu.org/licenses/gpl.html for full license details.
* /
2013-12-05 17:32:29 +01:00
var MAX _POINTS _TO _PRINT = 200000
2013-07-26 22:39:28 +02:00
var gcode = [ ] ;
2013-10-16 22:25:15 +02:00
function generate _gcode ( ) {
2013-07-26 22:39:28 +02:00
console . log ( "f:generategcode()" ) ;
gcode = [ ] ;
2013-09-07 16:06:59 +02:00
console . log ( "settings: " , settings ) ;
2014-01-10 17:47:05 +01:00
var speed = settings [ "printer.speed" ] ;
2013-09-07 16:06:59 +02:00
var normalSpeed = speed ;
2014-01-10 17:47:05 +01:00
var bottomSpeed = settings [ "printer.bottomLayerSpeed" ] ;
var firstLayerSlow = settings [ "printer.firstLayerSlow" ] ;
var bottomFlowRate = settings [ "printer.bottomFlowRate" ] ;
2013-09-07 16:06:59 +02:00
var travelSpeed = settings [ "printer.travelSpeed" ]
var filamentThickness = settings [ "printer.filamentThickness" ] ;
var wallThickness = settings [ "printer.wallThickness" ] ;
2013-09-17 15:14:59 +02:00
var screenToMillimeterScale = settings [ "printer.screenToMillimeterScale" ] ;
2013-09-07 16:06:59 +02:00
var layerHeight = settings [ "printer.layerHeight" ] ;
var temperature = settings [ "printer.temperature" ] ;
2013-10-22 15:33:08 +02:00
var bedTemperature = settings [ "printer.bed.temperature" ] ;
2013-09-07 16:06:59 +02:00
var useSubLayers = settings [ "printer.useSubLayers" ] ;
2013-09-17 15:14:59 +02:00
var enableTraveling = settings [ "printer.enableTraveling" ] ;
var retractionEnabled = settings [ "printer.retraction.enabled" ] ;
2013-09-07 16:06:59 +02:00
var retractionspeed = settings [ "printer.retraction.speed" ] ;
var retractionminDistance = settings [ "printer.retraction.minDistance" ] ;
var retractionamount = settings [ "printer.retraction.amount" ] ;
2013-10-18 15:55:11 +02:00
var preheatTemperature = settings [ "printer.heatup.temperature" ] ;
2013-10-22 15:33:08 +02:00
var preheatBedTemperature = settings [ "printer.heatup.bed.temperature" ] ;
2013-12-05 17:32:29 +01:00
var printerDimensionsX = settings [ "printer.dimensions.x" ] ;
var printerDimensionsY = settings [ "printer.dimensions.y" ] ;
var printerDimensionsZ = settings [ "printer.dimensions.z" ] ;
2013-11-22 16:51:26 +01:00
2013-12-05 17:32:29 +01:00
var gCodeOffsetX = printerDimensionsX / 2 ;
var gCodeOffsetY = printerDimensionsY / 2 ;
2013-11-22 16:51:26 +01:00
2013-10-28 17:17:50 +01:00
var startCode = generateStartCode ( ) ;
var endCode = generateEndCode ( ) ;
2013-11-22 16:51:26 +01:00
2013-10-14 16:40:48 +02:00
// max amount of real world layers
2013-12-05 17:32:29 +01:00
var layers = printerDimensionsZ / layerHeight ; //maxObjectHeight instead of objectHeight
2013-10-14 16:40:48 +02:00
2013-09-18 16:59:48 +02:00
// translate numLayers in preview to objectHeight in real world
2013-12-05 17:32:29 +01:00
objectHeight = Math . round ( numLayers / maxNumLayers * printerDimensionsZ ) ;
2013-09-18 16:59:48 +02:00
// translate preview rotation (per layer) to real world rotation
var rStepGCode = rStep * maxNumLayers / layers ; ///maxNumLayers*maxObjectHeight;
2013-12-05 17:32:29 +01:00
2013-09-18 16:59:48 +02:00
// correct direction
rStepGCode = - rStepGCode ;
2013-10-14 16:40:48 +02:00
2013-07-26 22:39:28 +02:00
// copy array without reference -> http://stackoverflow.com/questions/9885821/copying-of-an-array-of-objects-to-another-array-without-object-reference-in-java
var points = JSON . parse ( JSON . stringify ( _points ) ) ;
// add gcode begin commands
2013-10-28 01:11:13 +01:00
gcode = gcode . concat ( startCode ) ;
2013-11-22 16:51:26 +01:00
2013-12-05 17:32:29 +01:00
var layers = printerDimensionsZ / layerHeight ; //maxObjectHeight instead of objectHeight
2013-07-26 22:39:28 +02:00
var extruder = 0.0 ;
var prev = new Point ( ) ; prev . set ( 0 , 0 ) ;
2013-07-30 14:21:04 +02:00
// replacement (and improvement) for ofxGetCenterofMass
2013-07-26 22:39:28 +02:00
var centerOfDoodle = {
x : doodleBounds [ 0 ] + ( doodleBounds [ 2 ] - doodleBounds [ 0 ] ) / 2 ,
y : doodleBounds [ 1 ] + ( doodleBounds [ 3 ] - doodleBounds [ 1 ] ) / 2
}
2013-09-07 16:06:59 +02:00
2013-07-26 22:39:28 +02:00
console . log ( "f:generategcode() >> layers: " + layers ) ;
2013-09-07 16:06:59 +02:00
if ( layers == Infinity ) return ;
2013-10-16 22:25:15 +02:00
// check feasibility of design
2013-12-05 17:32:29 +01:00
var pointsToPrint = points . length * layers * ( objectHeight / printerDimensionsZ )
2013-11-22 16:51:26 +01:00
2013-10-23 16:45:41 +02:00
console . log ( "pointsToPrint: " , pointsToPrint ) ;
2013-11-22 16:51:26 +01:00
2013-10-16 22:25:15 +02:00
if ( pointsToPrint > MAX _POINTS _TO _PRINT ) {
2013-10-23 18:46:13 +02:00
alert ( "Sorry, your doodle is too complex or too high. Please try to simplify it." ) ;
2013-10-23 18:25:05 +02:00
console . log ( "ERROR: to many points too convert to gcode" ) ;
2013-10-16 22:25:15 +02:00
return [ ] ;
}
2013-11-22 16:51:26 +01:00
2013-07-26 22:39:28 +02:00
for ( var layer = 0 ; layer < layers ; layer ++ ) {
2013-12-06 15:03:10 +01:00
//gcode.push(";LAYER:"+layer); //this will be added in a next release to support GCODE previewing in CURA
2013-12-05 11:50:02 +01:00
2013-07-26 22:39:28 +02:00
var p = JSON . parse ( JSON . stringify ( points ) ) ; // [].concat(points);
if ( p . length < 2 ) return ;
var even = ( layer % 2 == 0 ) ;
var progress = layer / layers ;
2013-09-18 22:35:38 +02:00
var layerScale = scaleFunction ( progress ) ;
2013-07-26 22:39:28 +02:00
// if begin point this row and end point last row are close enough, isLoop is true
var isLoop = lineLength ( points [ 0 ] [ 0 ] , points [ 0 ] [ 1 ] , points [ points . length - 1 ] [ 0 ] , points [ points . length - 1 ] [ 1 ] ) < 3 ;
// set center of doodle as middle (ie subtract to that)
pointsTranslate ( p , - centerOfDoodle . x , - centerOfDoodle . y ) ;
pointsScale ( p , screenToMillimeterScale , - screenToMillimeterScale ) ;
pointsScale ( p , layerScale , layerScale ) ;
2013-09-18 16:59:48 +02:00
pointsRotate ( p , rStepGCode * layer ) ;
2013-07-26 22:39:28 +02:00
if ( layer == 0 ) {
2013-08-12 21:54:30 +02:00
//gcode.push("M107"); //fan off
if ( firstLayerSlow ) {
//gcode.push("M220 S20"); //slow speed
speed = bottomSpeed ;
2013-08-14 20:54:48 +02:00
//console.log("> speed: ",speed);
2013-08-12 21:54:30 +02:00
}
2013-07-30 14:21:04 +02:00
} else if ( layer == 2 ) { ////////LET OP, pas bij layer 2 weer op normale snelheid ipv layer 1
2013-07-26 22:39:28 +02:00
gcode . push ( "M106" ) ; //fan on
2013-08-12 21:54:30 +02:00
//gcode.push("M220 S100"); //normal speed
speed = normalSpeed ;
2013-08-14 20:54:48 +02:00
//console.log("> speed: ",speed);
2013-07-26 22:39:28 +02:00
}
var curLayerCommand = 0 ;
var totalLayerCommands = p . length ;
2013-07-30 14:21:04 +02:00
var layerProgress = 0 ;
2013-07-26 22:39:28 +02:00
var paths = [ ] ;
var pathCounter = - 1 ;
// var points = [];
for ( var i = 0 ; i < p . length ; i ++ ) {
if ( p [ i ] [ 2 ] == true ) {
pathCounter ++ ;
paths . push ( [ ] ) ;
paths [ pathCounter ] . push ( [ p [ i ] [ 0 ] , p [ i ] [ 1 ] ] ) ;
} else {
paths [ pathCounter ] . push ( [ p [ i ] [ 0 ] , p [ i ] [ 1 ] ] ) ;
}
}
2013-10-14 16:40:48 +02:00
2013-07-26 22:39:28 +02:00
// loop over the subpaths (the separately drawn lines)
2013-07-30 14:21:04 +02:00
for ( var j = 0 ; j < paths . length ; j ++ ) { // TODO paths > subpaths
2013-12-05 17:32:29 +01:00
var commands = paths [ j ] ;
2013-07-26 22:39:28 +02:00
// loop over the coordinates of the subpath
for ( var i = 0 ; i < commands . length ; i ++ ) {
var last = commands . length - 1 ;
var to = new Point ( ) ; to . set ( commands [ i ] [ 0 ] , commands [ i ] [ 1 ] ) ;
2013-09-18 20:15:58 +02:00
to . x += gCodeOffsetX ;
to . y += gCodeOffsetY ;
2013-07-26 22:39:28 +02:00
var sublayer = ( layer == 0 ) ? 0.0 : layer + ( useSubLayers ? ( curLayerCommand / totalLayerCommands ) : 0 ) ;
2013-09-07 16:06:59 +02:00
var z = ( sublayer + 1 ) * layerHeight ; // 2013-09-06 removed zOffset (seemed to be useless)
2013-07-26 22:39:28 +02:00
var isTraveling = ! isLoop && i == 0 ;
2013-09-17 15:14:59 +02:00
var doRetract = retractionEnabled && prev . distance ( to ) > retractionminDistance ;
2013-07-26 22:39:28 +02:00
2015-02-02 13:36:53 +01:00
//if (firstPointEver || layer > 2 && enableTraveling && isTraveling) { //always travel to first point, then disable traveling for first two layers and use settings for remainder of print
2014-01-10 17:47:05 +01:00
var firstPointEver = ( layer == 0 && i == 0 && j == 0 ) ;
2015-02-02 13:36:53 +01:00
if ( enableTraveling && isTraveling ) { //always travel to first point, then disable traveling for first two layers and use settings for remainder of print
2014-01-10 17:47:05 +01:00
if ( ! firstPointEver && doRetract ) gcode . push ( "G0 E" + ( extruder - retractionamount ) . toFixed ( 3 ) + " F" + ( retractionspeed * 60 ) . toFixed ( 3 ) ) ; //retract
2013-09-07 16:06:59 +02:00
gcode . push ( "G0 X" + to . x . toFixed ( 3 ) + " Y" + to . y . toFixed ( 3 ) + " Z" + z . toFixed ( 3 ) + " F" + ( travelSpeed * 60 ) . toFixed ( 3 ) ) ;
2014-01-10 17:47:05 +01:00
if ( ! firstPointEver && doRetract ) gcode . push ( "G0 E" + extruder . toFixed ( 3 ) + " F" + ( retractionspeed * 60 ) . toFixed ( 3 ) ) ; // return to normal
2013-07-26 22:39:28 +02:00
} else {
2014-01-10 17:47:05 +01:00
var f = ( layer < 2 ) ? bottomFlowRate : 1 ;
extruder += prev . distance ( to ) * wallThickness * layerHeight / ( Math . pow ( ( filamentThickness / 2 ) , 2 ) * Math . PI ) * f ;
2013-07-26 22:39:28 +02:00
gcode . push ( "G1 X" + to . x . toFixed ( 3 ) + " Y" + to . y . toFixed ( 3 ) + " Z" + z . toFixed ( 3 ) + " F" + ( speed * 60 ) . toFixed ( 3 ) + " E" + extruder . toFixed ( 3 ) ) ;
}
curLayerCommand ++ ;
2013-07-30 14:21:04 +02:00
layerProgress = curLayerCommand / totalLayerCommands ;
2013-07-26 22:39:28 +02:00
prev = to ;
}
}
2013-12-05 17:32:29 +01:00
if ( ( layer / layers ) > ( objectHeight / printerDimensionsZ ) ) {
console . log ( "f:generategcode() >> (layer/layers) > (objectHeight/printerDimensionsZ) is true -> breaking at layer " + ( layer + 1 ) ) ;
2013-07-26 22:39:28 +02:00
break ;
}
}
// add gcode end commands
2013-10-28 01:11:13 +01:00
gcode = gcode . concat ( endCode ) ;
2013-11-22 16:51:26 +01:00
2013-10-16 22:25:15 +02:00
return gcode ;
2013-07-26 22:39:28 +02:00
}
2013-10-28 17:17:50 +01:00
function generateStartCode ( ) {
var printerType = settings [ "printer.type" ] ;
2013-12-01 18:38:02 +01:00
var startCode = settings [ "printer.startcode" ] ;
2013-10-28 17:17:50 +01:00
startCode = subsituteVariables ( startCode ) ;
startCode = startCode . split ( "\n" ) ;
return startCode ;
}
function generateEndCode ( ) {
var printerType = settings [ "printer.type" ] ;
2013-12-01 18:38:02 +01:00
var endCode = settings [ "printer.endcode" ] ;
2013-10-28 17:17:50 +01:00
endCode = subsituteVariables ( endCode ) ;
endCode = endCode . split ( "\n" ) ;
return endCode ;
}
function subsituteVariables ( gcode ) {
//,temperature,bedTemperature,preheatTemperature,preheatBedTemperature
var temperature = settings [ "printer.temperature" ] ;
var bedTemperature = settings [ "printer.bed.temperature" ] ;
var preheatTemperature = settings [ "printer.heatup.temperature" ] ;
var preheatBedTemperature = settings [ "printer.heatup.bed.temperature" ] ;
2013-10-30 14:17:47 +01:00
var printerType = settings [ "printer.type" ] ;
2013-12-18 17:43:57 +01:00
var heatedbed = settings [ "printer.heatedbed" ] ;
2013-11-22 16:51:26 +01:00
2013-10-30 14:17:47 +01:00
switch ( printerType ) {
2013-12-05 16:37:10 +01:00
case "makerbot_replicator2" : printerType = "r2" ; break ;
case "makerbot_replicator2x" : printerType = "r2x" ; break ;
2013-10-30 14:17:47 +01:00
case "makerbot_thingomatic" : printerType = "t6" ; break ;
2013-12-01 18:38:02 +01:00
case "makerbot_generic" : printerType = "r2" ; break ;
2014-10-09 16:24:24 +02:00
case "_3Dison_plus" : printerType = "r2" ; break ;
2013-10-30 14:17:47 +01:00
}
2013-12-18 17:43:57 +01:00
var heatedBedReplacement = ( heatedbed ) ? "" : ";" ;
2013-10-30 14:17:47 +01:00
2013-10-22 15:33:08 +02:00
gcode = gcode . replace ( /{printingTemp}/gi , temperature ) ;
gcode = gcode . replace ( /{printingBedTemp}/gi , bedTemperature ) ;
gcode = gcode . replace ( /{preheatTemp}/gi , preheatTemperature ) ;
gcode = gcode . replace ( /{preheatBedTemp}/gi , preheatBedTemperature ) ;
2013-10-30 14:17:47 +01:00
gcode = gcode . replace ( /{printerType}/gi , printerType ) ;
2013-12-18 17:43:57 +01:00
gcode = gcode . replace ( /{if heatedBed}/gi , heatedBedReplacement ) ;
2013-10-22 15:33:08 +02:00
return gcode ;
}
2013-09-18 20:15:58 +02:00
function scaleFunction ( percent ) {
2013-09-18 22:35:38 +02:00
var r = 1.0 ;
switch ( VERTICALSHAPE ) {
case verticalShapes . NONE :
r = 1.0 ;
break ;
case verticalShapes . DIVERGING :
r = . 5 + ( percent * . 5 ) ;
break ;
case verticalShapes . CONVERGING :
r = 1.0 - ( percent * . 8 ) ;
break ;
case verticalShapes . SINUS :
r = ( Math . cos ( percent * Math . PI * 4 ) * . 25 ) + . 75 ;
break ;
}
2013-09-18 20:15:58 +02:00
2013-09-18 22:35:38 +02:00
// return 1.0 - (percent *.8);
2013-09-18 20:15:58 +02:00
return r ;
}
2013-07-26 22:39:28 +02:00
pointsTranslate = function ( p , x , y ) {
for ( var i = 0 ; i < p . length ; i ++ ) {
p [ i ] [ 0 ] += x ;
p [ i ] [ 1 ] += y ;
}
}
pointsScale = function ( p , sx , sy ) {
for ( var i = 0 ; i < p . length ; i ++ ) {
p [ i ] [ 0 ] *= sx ;
p [ i ] [ 1 ] *= sy ;
}
}
// rotates around point 0,0 (origin).
// Not the prettiest kind of rotation solution but in our case we're assuming that the points have just been translated to origin
pointsRotate = function ( p , ang ) {
var _ang , dist ;
for ( var i = 0 ; i < p . length ; i ++ ) {
dist = Math . sqrt ( p [ i ] [ 0 ] * p [ i ] [ 0 ] + p [ i ] [ 1 ] * p [ i ] [ 1 ] ) ;
_ang = Math . atan2 ( p [ i ] [ 1 ] , p [ i ] [ 0 ] ) ;
p [ i ] [ 0 ] = Math . cos ( _ang + ang ) * dist ;
p [ i ] [ 1 ] = Math . sin ( _ang + ang ) * dist ;
}
}
//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/math/line-length [rev. #1]
lineLength = function ( x , y , x0 , y0 ) {
return Math . sqrt ( ( x -= x0 ) * x + ( y -= y0 ) * y ) ;
} ;
var Point = function ( ) { } ;
Point . prototype = {
x : 0 ,
y : 0 ,
set : function ( _x , _y ) {
this . x = _x ;
this . y = _y ;
} ,
distance : function ( p ) {
var d = - 1 ;
if ( p instanceof Point ) {
d = Math . sqrt ( ( p . x - this . x ) * ( p . x - this . x ) + ( p . y - this . y ) * ( p . y - this . y ) ) ;
}
return d ;
} ,
toString : function ( ) {
console . log ( "x:" + this . x + ", y:" + this . y ) ;
}
2013-10-14 16:40:48 +02:00
}