Skip to content

API Reference

RNO edited this page Mar 2, 2018 · 64 revisions

var plotter = require('xy-plotter')(opts)

Instantiate this whole XY-plotter module. You can override the default configuration by specifying an opts object as seen below.

basic instantiation
const plotter = require('xy-plotter')()
overriding default config
const plotter = require('xy-plotter')({
  width: 310,       // default is 310 mm
  height: 380,      // default is 380 mm
  pen_positions: { 
    up: 0,          // default is 0 degree
    down: 30        // default is 30 degree
  },
  decimals: 2       // default is 2 decimals
})

⬆ back to top


plotter.width

getter

Return the width of the plotter's drawing area. Default is 310 mm.

const plotter = require('xy-plotter')()

console.log(plotter.width) // 310

⬆ back to top


plotter.height

getter

Return the height of the plotter's drawing area. Default is 380 mm.

const plotter = require('xy-plotter')()

console.log(plotter.height) // 380

⬆ back to top


plotter.config

getter

Return an object containing the actual configuration of the plotter.

const plotter = require('xy-plotter')({ decimals : 4 })

console.log(plotter.config)
/* {
  width: 310,
  height: 380,
  pen_positions: {
    up: 0,
    down: 90
  },
  decimals: 4
} */

⬆ back to top


plotter.defaultConfig

getter

Return an object containing the default configuration of the plotter.

const plotter = require('xy-plotter')({ decimals : 4 })

console.log(plotter.defaultConfig)
/* {
  width: 310,
  height: 380,
  pen_positions: {
    up: 0,
    down: 30
  },
  decimals: 2
} */

⬆ back to top


EventEmitter

Each plotter submodule (plotter.Job, plotter.Turtle, plotter.Serial, etc) exposes a Tiny-Emitter instance, and can be used to subscribe/unsubscribe from events emitted by this submodule, or emit custom events.

⬆ back to top


var job = plotter.Job(jobName)

Instantiate a new job with the name jobName.

This abstraction is from far the more important of the whole module, as it will handle all the drawing API and the commands buffer.

It is by design very similar to the Processing API.

const job = plotter.Job('my-awesome-drawing')

⬆ back to top


job.name

getter

Return the name of the job.

const job = plotter.Job('my-awesome-drawing')

console.log(job.name) // my-awesome-drawing

⬆ back to top


job.buffer

getter

Return a copy of the internal commands buffer array of the job.

const job = plotter.Job('my-awesome-drawing')

let commands = job.buffer
console.log('this job has' + commands.length + 'commands !')

⬆ back to top


job.setSpeed(speedPercent)

Set the speed movement of the plotter. By default the firmware implements a non-linear speed curve to avoid position shifting, but some jobs will require a more fine tuned speed.

speedPercent needs to be between 0 and 1.

const job = plotter.Job('my-awesome-drawing')

job.setSpeed(0.1) // veeeery slooow
job.setSpeed(1)   // blazing fast, and quite dangerous

Here is a benchmark describing some real world values with different speed settings :

speedPercent real world speed
0.1 10.86 mm/s
0.2 11.88 mm/s
0.3 13.57 mm/s
0.4 15.2 mm/s
0.5 17.27 mm/s
0.6 21.11 mm/s
0.7 25.33 mm/s
0.8 31.67 mm/s
0.9 47.5 mm/s
1.0 76 mm/s

By comparison, the default non-linear speed varies between 10.56 mm/s and 38 mm/s.

I encountered during some heavy tests big position shifting when using the default non-linear speed. Setting a linear speed solved the problem.

Also note that the stepper motors tend to make less noise with a small linear speed.

⬆ back to top


job.resetSpeed()

Reset the speed of the plotter to its default non-linear behavior.

const job = plotter.Job('my-awesome-drawing')

job.resetSpeed()

⬆ back to top


job.optimize(options)

Try to optimize job commands buffer to reduce the time of drawing and improve the overall quality of it.

Default options will spatially sort the lines and remove all single points of a job.

Note that this method is to be called once the job.buffer is ready, so just before job's execution (ie via plotter.Serial or plotter.Server).

optimization with default options
const job = plotter.Job('my-awesome-drawing')

// job commands 
// ...

job.optimize({
    sort: true,               // spatial sort buffer
    points: true,             // remove points
    startingPosition: [0, 0], // starting position for the spatial sort lookup
    skip: ['S1', 'T']         // exclude custom commands from optimization
})

// ...
// job execution

⬆ back to top


job.pen_up(force_motion = false)

Lift up the plotter's pen by moving its servo to plotter.pen_positions.up degrees.

If force_motion is set to true, the plotter will attempt to lift its pen even if it considers it already lift up.

Note: the plotter begins by default with its pen lift up.

basic example
const job = plotter.Job('my-awesome-drawing')

job.pen_up()
job.move(100, 200)
job.pen_up(true) // just to be sure...
job.move(200, 200)
the same example, with chaining
const job = plotter.Job('my-awesome-drawing')

job.pen_up()
   .move(100, 200)
   .pen_up(true) // just to be sure...
   .move(200, 200)

⬆ back to top


job.pen_down(force_motion = false)

Lower down the plotter's pen by moving its servo to plotter.pen_positions.down degrees.

If force_motion is set to true, the plotter will attempt to lower its pen even if it considers it already lowered.

Note: the plotter begins by default with its pen lift up.

const job = plotter.Job('my-awesome-drawing')

job.pen_up().move(plotter.width / 2, plotter.height / 2)
job.pen_down().move(0, 0)

⬆ back to top


job.pen(position)

Set the position of the pen's servo to a specific position value (in degrees). Useful for pressure related drawings.

const job = plotter.Job('my-awesome-drawing')

for (let a = 0, x = 0, y = 0; a < 90; a++) {
  job.pen(a).move(x, y)
  x += 10
  y += 20
}

⬆ back to top


job.move(x, y)

Move the plotter to the x, y coordinates. All values are expressed in millimeters.

const job = plotter.Job('my-awesome-drawing')

job.move(100, 200)

⬆ back to top


job.home()

Send the plotter to its 0, 0 home position, pen up.

const job = plotter.Job('my-awesome-drawing')

job.home()

⬆ back to top


job.wait()

Insert a wait instruction in the job buffer. When reaching this instruction, the plotter.Serial() will wait until you hit enter in your TTY. Useful if you want to change the pen or the paper in the middle of a job.

You can override the default waiting behavior by specifying a function returning a Promise object.

basic example
const job = plotter.Job('my-awesome-drawing')

// draw a first rectangle
job.rect(50, 50, 20, 20)

// wait until user resume the job
job.wait()

// draw a second rectangle with another pen
job.rect(55, 55, 20, 20)
the same example, with a custom promise
const job = plotter.Job('my-awesome-drawing')

// draw a first rectangle
job.rect(50, 50, 20, 20)

// wait until the promise resolves
job.wait(() => {
  return new Promise(resolve, reject) {
    if (ok) resolve()
    else reject()
  })
})

// draw a second rectangle with another pen
job.rect(55, 55, 20, 20)

⬆ back to top


job.drawBoundaries()

Draw the extreme limits of the plotter's drawable area.

const job = plotter.Job('my-awesome-drawing')

job.drawBoundaries()

⬆ back to top


job.point(x, y)

Draw a point at the specified x, y coordinates.

const job = plotter.Job('my-awesome-drawing')

const x = plotter.width / 2
const y = plotter.height / 2
for (let i = 0; i < 1000; i++) {
  job.point(x + (Math.random() * 100), y + (Math.random() * 100))
}

⬆ back to top


job.line(x1, y1, x2, y2)

Draw a line between the point x1, y1 and the point x2, y2.

const job = plotter.Job('my-awesome-drawing')

job.line(0, 0, 50, 0)

⬆ back to top


job.triangle(x1, y1, x2, y2, x3, y3, x4, y4)

Draw a triangle between the points x1, y1, x2, y2 and x3, y3.

const job = plotter.Job('my-awesome-drawing')

let a = {x: 100, y: 200}
let b = {x: 200, y: 200}
let c = {x: 100, y: 100}
job.triangle(a.x, a.y, b.x, b.y, c.x, c.y)

⬆ back to top


job.rect(x, y, width, height)

Draw a rectangle of dimensions width*height at the coordinates x, y from its bottom top left corner.

const job = plotter.Job('my-awesome-drawing');

job.rect(plotter.width / 2, plotter.height / 2, 100, 200)

⬆ back to top


job.quad(x1, y1, x2, y2, x3, y3, x4, y4)

Draw a quadrilateral between the points x1, y1, x2, y2, x3, y3 and x4, y4.

const job = plotter.Job('my-awesome-drawing')

let points = []
for (let i = 0; i < 4; i++) {
  let point = {
    x : Math.random() * plotter.width,
    y : Math.random() * plotter.height
  };
  points.push(point)
}

job.quad(points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y, points[3].x, points[3].y)

⬆ back to top


job.polygon(points)

Draw a closed polygon given an array of point, where point is an object with a x and a y key.

const job = plotter.Job('my-awesome-drawing')

let points = []
for (let i = 0; i < 100; i++) {
  const point = [Math.random() * plotter.width, Math.random() * plotter.height]
  points.push(point)
}

job.polygon(points)

⬆ back to top


job.circle(centerX, centerY, radius, sides = 100)

Draw a circle of radius radius at the coordinates centerX, centerY. The optional parameter sides allows to control the resolution of the circle.

const job = plotter.Job('my-awesome-drawing')

let x = plotter.width / 2
let y = plotter.height / 2

job.circle(x, y, 50)
job.circle(x, y, 50, 3) // this is in fact a triangle

⬆ back to top


job.ellipse(centerX, centerY, width, height, sides = 100)

Draw an ellipse of dimensions width*height at the coordinates centerX, centerY. The optional parameter sides allows to control the resolution of the ellipse.

const job = plotter.Job('my-awesome-drawing')

job.ellipse(10, 100, 50, 10)
job.ellipse(100, 10, 50, 10, 8) // an ellipse with 8 sides

⬆ back to top


job.text(string, x, y, options)

Draw the given string at the coordinates x, y, x being the left side of the text, and y its baseline.

The default font file is CamBam Stick Font 3.

const job = plotter.Job('my-awesome-text')

job.text('hello world !', 100, 50, {
  fontSize: 10,                  // height of the text in millimeters (default 10)
  fontFile: 'path/to/myFont.otf' // support for WOFF, OTF, TTF via opentype.js
})

⬆ back to top


job.svg(path, transformations)

Draw the given SVG file. The optional transformations object allows multiple manipulation of position, angle, scale and transformation origin. See the examples below for usage.

Note: the coordinates of the SVG will be interpreted as millimeters : a rectangle of 100px by 200px will be drawn as a rectangle of 100mm by 200mm.

basic example
const job = plotter.Job('my-awesome-drawing')

job.svg('heart.svg')
with transformations
const job = plotter.Job('my-awesome-drawing')

job.svg('heart.svg', {
  x: 100,       // x coordinate
  y: 200,       // y coordinate
  width: 200,   // custom width
  height: 300,  // custom height
  angle: 90,    // angle in degrees
  origin: [0.5, 1] // origin point of the transformation
})

The transformation.origin object defines a point of coordinates between 0 and 1, [0, 0] beeing the top left corner and [1, 1] the bottom right one.

⬆ back to top


job.addToBuffer(cmd)

Add the specified cmd to the job's buffer. Useful if you modify the plotter's firmware.

const job = plotter.Job('my-awesome-drawing')

let GCode = 'G28'
job.addToBuffer(GCode)

⬆ back to top


Event: data

The data event's callback is called whith a data object when a new command is pushed to the job's buffer.

data object
{
  cmd: 'cmd', // the pushed command
  buffer: [...], // the current buffer
}

⬆ back to top


var turtle = plotter.Turtle(turtleName, options)

Instantiate a new turtle with the name turtleName.

This whole module is an alternative to the job drawing API, using the famous Logo Turtle Graphics API.

You can learn more about the Turtle syntax and logic on this excellent website.

Note that this module is basically a layer on top of the job module and exposes the same buffer API, meaning that where modules like Serial or Server accept a job as a parameter, turtle can also be passed.

Also note that all turtle methods (except getters) can be chained, allowing a syntax close to the original Logo API.

instanciation with the default options
const turtle = plotter.Turtle('my-turtle', {
  x: plotter.width / 2,  // starting x position on the plotter
  y: plotter.height / 2, // starting y position on the plotter
  angle: 0,              // starting angle in degrees
})

⬆ back to top


turtle.name

getter

Return the name of the turtle.

const turtle = plotter.Turtle('my-turtle')

console.log(turtle.name) // my-turtle

⬆ back to top


turtle.buffer

getter

Return a copy of the internal ommands buffer array of the turtle.

const turtle = plotter.Turtle('my-turtle')

let commands = turtle.buffer
console.log('this turtle has' + commands.length + 'commands !')

⬆ back to top


turtle.position

getter

Get the current position of the turtle as a {x, y} coordinates object. This coordinates are absolute.

Note that this only return the current position during buffer building, not buffer execution, meaning that this is not designed to be used during the actual plotting.

const turtle = plotter.Turtle('my-turtle')

// ...
console.log(turtle.position) // [0, 0]
turtle.forward(10)
console.log(turtle.position) // [10, 0]

⬆ back to top


turtle.x

getter

Get the current x coordinate of the turtle. This is a shorthand for turtle.position.x

⬆ back to top


turtle.y

getter

Get the current y coordinate of the turtle. This is a shorthand for turtle.position.y

⬆ back to top


turtle.angle

getter

Get the current angle of the turtle in degrees. Works like turtle.position.

⬆ back to top


turtle.home()

Reset the turtle angle and position, leaving it as instanciated.

Note that this command calls turtle.up() before moving it.

const turtle = plotter.Turtle('my-turtle', {
  x: 0,
  y: 0,
  angle: 90,
}) // turtle is at [0, 0] with an angle of 90 degrees

// ...
// lots of moves
// ...

turtle.home() // turtle is now back at [0, 0] with an angle of 90 degrees

⬆ back to top


turtle.up()

Lift up the pen, as described in job.pen_up().

const turtle = plotter.Turtle('my-turtle')

turtle.up()
      .forward(10) // no drawing here

⬆ back to top


turtle.down()

Lower down the pen, as described in job.pen_down().

const turtle = plotter.Turtle('my-turtle')

turtle.up()
      .forward(10)  // no drawing here...
      .down()
      .forward(10) // ...but not here

⬆ back to top


turtle.forward(distance)

Make the turtle move forward distance millimeters.

const turtle = plotter.Turtle('my-turtle')

turtle.forward(100)

⬆ back to top


turtle.backward(distance)

Make the turtle move backward distance millimeters.

const turtle = plotter.Turtle('my-turtle')

turtle.backward(100)

⬆ back to top


turtle.back(distance)

Shorthand for turtle.backward(distance).

⬆ back to top


turtle.left(alpha)

Make the turtle rotate left to alpha degrees. This rotation is relative to the precedent angle.

const turtle = plotter.Turtle('my-turtle')

turtle.left(90)

⬆ back to top


turtle.right(alpha)

Make the turtle rotate right to alpha degrees. This rotation is relative to the precedent angle.

const turtle = plotter.Turtle('my-turtle')

turtle.right(90)

⬆ back to top


turtle.setXY(x, y)

Move the turtle to the specified [x, y] position. The coordinates are in a cartesian system relative to the turtle original position (defined at its instanciation), with the X axis going right and the Y axis going up, the point [0, 0] being the original position.

Note that this will conserve the turtle's angle, meaning that if turtle.angle === 90 before turtle.setXY() is called, the angle will still be 90 degrees after the call, no matter where the target position is.

const turtle = plotter.Turtle('my-turtle')

turtle.up()
      .setXY(0, 0) // move the turtle at its original position

⬆ back to top


turtle.repeat(n, callback)

Repeat the given callback n times. This is mainly designed to keep consistency with the original Turtle Graphics API, and could easily be replaced by a basic for loop.

basic example
const turtle = plotter.Turtle('my-turtle')

// draw a square of 100mm length by calling 4 times [forward(100), right(90)]
turtle.repeat(4, turtle => {
  turtle.forward(100).right(90)
})
keeping count of iterations using repcount
const turtle = plotter.Turtle('my-turtle')

turtle.repeat(100, (turtle, repcount) => {
  turtle.forward(repcount).right(60)
})

⬆ back to top


turtle.to(procedureName, procedure)

Store a procedure in the turtle API, allowing later reference by calling turtle.procedureName(). This allows parameters to be passed, see examples below.

procedureName need to be a valid function name, and procedure a valid callable function, otherwise turtle.to() will throw an error.

Note that if a procedure with the same name has already been stored, it will be overwritten by the new one.

basic example
const turtle = plotter.Turtle('my-turtle')

// learn to the turtle how to draw a square of length 20
turtle.to('square', turtle => {
  turtle.repeat(4, turtle => {
    turtle
      .forward(20)
      .right(90)
  })
})

turtle.square() // draw a square of length 20
passing custom parameters
const turtle = plotter.Turtle('my-turtle')

// learn to the turtle how to draw a square of variable length
turtle.to('square', (turtle, sideLength) => {
  turtle.repeat(4, turtle => {
    turtle
      .forward(sideLength)
      .right(90)
  })
})

turtle.square(20)  // draw a square of length 20
turtle.square(100) // draw a square of length 100

⬆ back to top


turtle.forget(procedureName)

Delete a reference to a previously stored procedure.

const turtle = plotter.Turtle('my-turtle')

// store the 'square' procedure
turtle.to('square', turtle => {
  turtle.repeat(4, turtle => { turtle.forward(20).right(90) })
})

turtle.square() // ok

turtle.forget('square')
turtle.square() // throw "TypeError: turtle.square is not a function"

⬆ back to top


Event: data

The data event's callback is called whith a data object when a new command is pushed to the turtle's buffer.

data object
{
  cmd: 'cmd', // the pushed command
  buffer: [...], // the current buffer
}

⬆ back to top


var serial = plotter.Serial(port, options)

Instantiate a serial object on port.

If options.verbose and options.progressBar are both set to true, the progress bar will show the current command running on the plotter.

When setting options.disconnectOnJobEnd to false, you will need to call serial.disconnect() at the end of the job.

const serial = plotter.Serial('/dev/tty.wchusbserial1410', {
  baudRate: 115200,           // baudrate of the plotter's firmware
  verbose: false,             // output running commands in the TTY
  progressBar: true,          // output job progress in the TTY
  disconnectOnJobEnd: true    // auto close the serial port at the end of a job
})

⬆ back to top


serial.connected

getter

Return true of false depending on whether the serial port is open or not.

Note: during connection, the plotter is going home before handshaking, so you should test if serial.connected == true before doing something hacky on the serial port.

const serial = plotter.Serial('/dev/tty.wchusbserial1410')

serial.connect()
console.log(serial.connected) // true 

serial.disconnect()
console.log(serial.connected) // false

⬆ back to top


serial.paused

getter

Return true of false depending on whether the current serial communication is paused or not.

const serial = plotter.Serial('/dev/tty.wchusbserial1410')

serial.pause()
console.log(serial.paused) // true 

serial.resume()
console.log(serial.paused) // false

⬆ back to top


serial.send(job, streamed = false)

Send job through the serial port specified in const serial = plotter.Serial(port, options). Return a Promise object resolved when job is ended.

If streamed is set to true, switch the serial communication to a "realtime job streaming", where Serial listens to changes in the job internal buffer (see example below).

If the serial port has not been opened prior to this, automatically open the port by calling serial.connect()

When the job is done, and if serial.options.disconnectOnJobEnd is set to true, automatically close the port by calling serial.disconnect()

basic Promise chaining
const serial = plotter.Serial('/dev/tty.wchusbserial1410')

const job1 = plotter.Job('job-number-1')
serial
  .send(job1)
  .then(j => {
    console.log(`${j.name} is done !``)
  });
realtime job streaming mode
const serial = plotter.Serial('/dev/tty.wchusbserial1410')
const job = plotter.Job('realtime-random-strokes')

serial
  .send(job, true)
  .then(j => {
    console.log(`'${j.name}' streaming has been terminated.`)
  })

setInterval(() => {
  let x = Math.random() * plotter.width
  let y = Math.random() * plotter.height
  job.move(x, y)  
}, 1000)
multiple jobs
const serial = plotter.Serial('/dev/tty.wchusbserial1410')

const job1 = plotter.Job('job-number-1')
const job2 = plotter.Job('job-number-2')
serial
  .send(job1)
  .then(j => {
    console.log(`${j.name} is done, starting job-number-2 !`j`)
    serial.send(job2)
  })
manually closing the port
const serial = plotter.Serial('/dev/tty.wchusbserial1410', {disonnectOnJobEnd: false})

const job = plotter.Job('my-awesome-job')
serial
  .send(job)
  .then(j => {
    console.log(`${j.name} is done !``)
    serial.disconnect()
  });

⬆ back to top


serial.sendList([job1, job2, ...])

Open the serial port specified in const serial = plotter.Serial(port, options), send multiples jobs through it and resolve a Promise when all the jobs are done.

Set serial.options.disonnectOnJobEnd to false, to avoid closing the serial port between each job.

const serial = plotter.Serial('/dev/tty.wchusbserial1410', {disonnectOnJobEnd: false})

const jobs = [
  plotter.Job('job-number-1'),
  plotter.Job('job-number-2')
]

serial
  .sendList(jobs)
  .then(() => {
    console.log('all jobs done !')
    serial.disconnect()
  })

⬆ back to top


serial.end(job)

If job is being sent or streamed via serial.send(job), end it by emitting a job-end event.

cancel sent job
const serial = plotter.Serial('/dev/tty.wchusbserial1410')
const job = plotter.Job('my-soon-to-die-job')

serial.send(job)

something.on('event', () => serial.end(job))
terminate realtime job streaming
const serial = plotter.Serial('/dev/tty.wchusbserial1410')
const job = plotter.Job('my-soon-to-die-job')

// manually connect and use Promise.resolve to begin interval 
// when the plotter is ready, whereas when the script starts
serial.connect().then(() => {
  serial
    .send(job, true) // serial.send(job, true) to stream
    .then(j => {
      console.log(`'${j.name}' has ended.`)
      clearInterval(randomStrokes)
    })

  let randomStrokes = setInterval(() => {
    let x = Math.random() * plotter.width
    let y = Math.random() * plotter.height
    job.pen_down().move(x, y)
  }, 3000)

  // end the stream one minute after it starts
  setTimeout(() => serial.end(job), 60000)
})

⬆ back to top


serial.connect()

Manually open the serial port specified in const serial = plotter.Serial(port, options)

const serial = plotter.Serial('/dev/tty.wchusbserial1410')

serial.connect()

⬆ back to top


serial.disconnect()

Manually close the serial port specified in const serial = plotter.Serial(port, options)

const serial = plotter.Serial('/dev/tty.wchusbserial1410')

serial.connect()
// do something
serial.disconnect()

⬆ back to top


serial.pause()

Pause the current serial communication. For pausing the serial in the context of job buffer, see job.wait().

const serial = plotter.Serial('/dev/tty.wchusbserial1410')
const job = plotter.Job('job')
serial.send(job)

// pause the job after 5 seconds
setTimeout(function() {
  serial.pause()
}, 5000)

⬆ back to top


serial.resume()

Resume the current paused serial communication.

const serial = plotter.Serial('/dev/tty.wchusbserial1410')
const job = plotter.Job('job')

// begin the job in a paused state
serial.pause().send(job)

// resume the job when a certain event is fired
someObject.on('someEvent', (data) => {
  serial.resume()
});

⬆ back to top


serial.sendCommand(command)

Send a custom command through the serial port specified in const serial = plotter.Serial(port, options). Useful if you modify the plotter's firmware.

Note: you need to have the port open in order to send the command.

const serial = plotter.Serial('/dev/tty.wchusbserial1410')

const hackyCommand = 'G28'
if (!serial.connected) {
  serial.connect()
}
serial.sendCommand(hackyCommand)

⬆ back to top


Event: error

The error event's callback is called with an error object when a serial error occurs.

⬆ back to top


Event: connect

The connect event's callback is called when the serial communication is established.

⬆ back to top


Event: disconnect

The disconnect event's callback is called when the serial communication is closed.

⬆ back to top


Event: pause

The pause event's callback is called when the current serial communication is paused, either by serial.pause() or by job.wait().

⬆ back to top


Event: resume

The resume event's callback is called when the current paused serial communication is resumed, either by serial.resume() or by resolving job.wait().

⬆ back to top


Event: job-start

The job-start event's callback is called with a data object when a job starts.

data object
{
  job: plotter.job  // the job object
}

⬆ back to top


Event: job-progress

The job-progress event's callback is called whith a data object when a new command is sent to the plotter in the context of a job.

data object
{
  job: plotter.job, // the job object
  cmd: 'G1 0 0',    // the command sent to the plotter
  progress: {
    total: 10,      // the total length of commands in the buffer
    elapsed: 2,     // the length of commands already sent
    remaining: 8,   // the length of remaining commands to send
  }
}

⬆ back to top


Event: job-end

The job-done event's callback is called with a data object when a job is ended, either because it is finished or because it has been cancelled by calling serial.end(job).

data object
{
  job: plotter.job // the job object
}

⬆ back to top


var server = plotter.Server(address, options)

Instantiate a server object and open a websocket communication on the specified address. The default port is 8080, and can be overridden by setting options.port.

For more details on setting up the corresponding server, see arnaudjuracek/xy-server.

const server = plotter.Server('xy-server.local', {
  port: 8888  // the default port is 8080
})

⬆ back to top


server.queue(job, callback)

Send the specified job to the server's queue. The optional callback is executed after the job has ben sent, and accept a success arguement which is either true or false.

For more details on setting up the corresponding server, see arnaudjuracek/xy-server.

const server = plotter.Server('xy-server.local')
const job = plotter.Job('job')

server.queue(job, (success) => {
  if (success) console.log('job successfully queued !')
  else console.log('something went wrong...')
})

⬆ back to top


var file = plotter.File()

Instantiate the plotter.File() module, which handles all file manipulations, such as saving/loading job or exporting images.

const plotter = require('xy-plotter')()
const file = plotter.File()

⬆ back to top


file.save(job, path)

const plotter = require('xy-plotter')()
const file = plotter.File()

const job = plotter.Job('my-drawing')
file.save(job, '/path/to/the/file.json')

⬆ back to top


file.load(path)

const plotter = require('xy-plotter')()
const file = plotter.File()

const job = file.load('/path/to/the/file.json')

⬆ back to top


file.export(job, path, options)

Export the specified job as an image file.

Format is chosen by specifying the file extension, and can be .png, .svg, .jpg or .jpeg.

png export with default options
const plotter = require('xy-plotter')()
const file = plotter.File()
const job = plotter.Job('my-drawing')

file.export(job, '/path/to/the/image.png', {
  width: plotter.width * 4,   // width of the image in px
  height: plotter.height * 4, // height of the image in px
  stroke: '#000',             // stroke color
  background: '#FFF',         // background color
  strokeWeight: 1,            // line and point width
  quality: 75,                // JPEG quality
  progressive: false          // true to enable JPEG progressive compression
})

⬆ back to top


var stats = plotter.Stats(job)

Instantiate a stats object for the specified job. The stats object handle all sorts of stats and infos calculation, such as the job's estimated duration or the distance travelled by the pen.

const plotter = require('xy-plotter')()
const job = plotter.Job('my-drawing')
const stats = plotter.Stats(job)

⬆ back to top


stats.distance

getter

Return the distance travelled by the pen during the job specified in the stats object.

const plotter = require('xy-plotter')()
const job = plotter.Job('my-drawing')
const stats = plotter.Stats(job)

console.log('The pen will travel ' + stats.distance + 'mm during this job')

⬆ back to top


stats.duration

getter

Return an object containing informations about the duration of the job specified in the stats object.

const plotter = require('xy-plotter')()
const job = plotter.Job('my-drawing')
const stats = plotter.Stats(job)

console.log(stats.duration)
/* { 
  estimation: { 
    value: 1641, 
    formatted: '00:27:21' 
  },
  min: { 
    value: 1476.9, 
    formatted: '00:24:36' 
  },
  max: { 
    value: 1805.1, 
    formatted: '00:30:05' 
  } 
} */

⬆ back to top


stats.benchmark

getter

Return an object containing the benchmark used to estimate the duration of the job.

Note: this benchmark has been made with my very own setup, and may differ on other setups.

const plotter = require('xy-plotter')()
const job = plotter.Job('my-drawing')
const stats = plotter.Stats(job)

console.log(stats.benchmark)
/* {
  // benchmark with a movement distance of 380mm
  duration: [
    // depending on the length of the segment, the default none-linear
    // duration varies between 10s and 36s, averaging to 23s
    {speedSetting: null, seconds: 23, min: 10, max: 36},
    // linear speeds
    {speedSetting:  0.1, seconds: 35},
    {speedSetting:  0.2, seconds: 32},
    {speedSetting:  0.3, seconds: 28},
    {speedSetting:  0.4, seconds: 25},
    {speedSetting:  0.5, seconds: 22},
    {speedSetting:  0.6, seconds: 18},
    {speedSetting:  0.7, seconds: 15},
    {speedSetting:  0.8, seconds: 12},
    {speedSetting:  0.9, seconds: 8},
    {speedSetting:  1.0, seconds: 5}
  ],
  speed: {
    values: [
      // depending on the length of the segment, the default non-linear speed
      // varies between 10.56mm/s and 38mm/s, averaging to 24.28mm/s
      {speedSetting: null, mmPerSecond: 24.28, min: 10.56, max: 38},
      // linear speeds
      {speedSetting:  0.1, mmPerSecond: 10.86},
      {speedSetting:  0.2, mmPerSecond: 11.88},
      {speedSetting:  0.3, mmPerSecond: 13.57},
      {speedSetting:  0.4, mmPerSecond: 15.2},
      {speedSetting:  0.5, mmPerSecond: 17.27},
      {speedSetting:  0.6, mmPerSecond: 21.11},
      {speedSetting:  0.7, mmPerSecond: 25.33},
      {speedSetting:  0.8, mmPerSecond: 31.67},
      {speedSetting:  0.9, mmPerSecond: 47.5},
      {speedSetting:  1.0, mmPerSecond: 76},
    ],
    interpolate: function(x) { return 1.3145 * Math.exp(3.8312 * x)   5435; },
    lerp: function(x) { return 58.2018 - x; }
  }
} */

⬆ back to top


stats.refresh()

Recalculate all the stats and return the stats object.

const plotter = require('xy-plotter')()
const job = plotter.Job('my-drawing')
const stats = plotter.Stats(job)

console.log(stats.distance)

// ...
// some modifications to the job's buffer
// ...

console.log(stats.refresh().distance) // get the new distance

⬆ back to top


stats.pretty(emoji = true)

Return a pretty string of the stats. The emoji prefixes can be disabled by calling stats.pretty(false)

const plotter = require('xy-plotter')()
const job = plotter.Job('my-drawing')
const stats = plotter.Stats(job)

console.log(stats.pretty())
console.log(stats.pretty(false)) // no emojis

⬆ back to top

Clone this wiki locally