Skip to content

Instantly share code, notes, and snippets.

@GabiThume
Last active August 29, 2015 14:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GabiThume/12514b45b9a8783e360e to your computer and use it in GitHub Desktop.
Save GabiThume/12514b45b9a8783e360e to your computer and use it in GitHub Desktop.
MozFest code
noflo = require 'noflo'
Mirobot = null
sleep = (ms) ->
start = new Date().getTime()
continue while new Date().getTime() - start < ms
class ControlMirobot extends noflo.Component
description: 'Control Mirobot.'
icon: 'pencil'
constructor: ->
@Mirobot = null
@url = null
@mirobot = null
@commands = []
@points = []
@inPorts =
url: new noflo.Port 'string'
start: new noflo.Port 'bang'
stop: new noflo.Port 'bang'
commands: new noflo.ArrayPort 'object'
points: new noflo.Port 'object'
lib: new noflo.Port 'object'
@outPorts =
path: new noflo.Port 'object'
@inPorts.lib.on 'data', (@Mirobot) =>
@inPorts.url.on 'data', (data) =>
@commands = []
@url = data
if not @mirobot?
console.log 'Connecting on', @url
@mirobot = new @Mirobot @url
@inPorts.start.on 'data', (data) =>
if @mirobot?
@parseThing @commands, 0
@inPorts.stop.on 'data', (data) =>
@commands = []
if @mirobot?
@mirobot.stop()
@inPorts.commands.on 'data', (cmd, i) =>
@commands[i] = cmd
@inPorts.points.on 'data', (data) =>
@points = data.items
parseThing: (thing, currentPoint) ->
if thing? and thing.cmd? and @[thing.cmd]?
@[thing.cmd](thing, currentPoint)
else if thing instanceof Array
for item, i in thing
continue unless item?
@parseThing item, i
drawCommand: (position) =>
return unless @inPorts.points.isAttached()
return unless @outPorts.path.isAttached()
if position < @points.length-1
path = []
path.push @points[position]
path.push @points[position+1]
@outPorts.path.send path
forward: (distance, currentPoint) =>
@setIcon 'arrow-up'
@mirobot.move('forward', distance.arg, (state, msg, recursion) =>
# if state != 'started'
# sleep 50
# else
# @drawCommand currentPoint
)
back: (distance, currentPoint) =>
@setIcon 'arrow-down'
@mirobot.move('back', distance.arg, (state, msg, recursion) =>
# if state != 'started'
# sleep 50
# else
# @drawCommand currentPoint
)
left: (angle, currentPoint) =>
@setIcon 'mail-reply'
@mirobot.turn('left', angle.arg, (state, msg, recursion) =>
# if state != 'started'
# sleep 50
# else
# @drawCommand currentPoint
)
right: (angle, currentPoint) =>
@setIcon 'mail-forward'
@mirobot.turn('right', angle.arg, (state, msg, recursion) =>
# if state != 'started'
# sleep 50
# else
# @drawCommand currentPoint
)
pause: ->
@mirobot.pause()
resume: ->
@mirobot.resume()
ping: ->
@mirobot.ping()
penup: ->
@mirobot.penup()
pendown: ->
@mirobot.pendown()
exports.getComponent = -> new ControlMirobot
noflo = require 'noflo'
# Converts from radians to degrees.
Math.degrees = (radians) ->
return radians * 180 / Math.PI
class PointsToPolar extends noflo.Component
description: 'Converts cartesian coordinates to polar coordinates.'
icon: 'paint-brush'
constructor: ->
@mirobotAngle = 0
@commands = []
@inPorts =
points: new noflo.Port 'object'
@outPorts =
polar: new noflo.Port 'object'
@inPorts.points.on 'data', (data) =>
return unless @outPorts.polar.isAttached()
@mirobotAngle = 0
if data.items?
points = data.items
else
points = data
@parsePoints points
@outPorts.polar.send @commands
parsePoints: (start, end) =>
if start? and end? and start.x? and end.x?
@pathFinding(start, end)
else if start instanceof Array
for item, i in start
continue unless item?
if start[i+1]?
@parsePoints item, start[i+1]
pathFinding: (from, to) =>
# Distance is a simple Pythagoras
dx = to.x - from.x
dy = to.y - from.y
distance = Math.sqrt(dx*dx + dy*dy)
# Calculate the angle
arctangent = Math.atan2(dx, dy)
angle = Math.degrees(arctangent)
# Calculating the walking angle based on previous mirobot angle
@walkingAngle = angle - @mirobotAngle
if @walkingAngle < 0
@walkingAngle = @walkingAngle + 360
@mirobotAngle = angle
if @mirobotAngle >= 360
@mirobotAngle = @mirobotAngle - 360
commandDirection =
cmd: null
arg: null
if @walkingAngle <= 180
commandDirection.cmd = 'right'
commandDirection.arg = @walkingAngle.toString()
else
commandDirection.cmd = 'left'
commandDirection.arg = (360 - @walkingAngle).toString()
commandForward =
cmd: 'forward'
arg: distance.toString()
@commands.push commandDirection
@commands.push commandForward
exports.getComponent = -> new PointsToPolar
noflo = require 'noflo'
class TSP extends noflo.Component
description: 'Ordenates points based on the minimum distance between them.'
icon: 'code-fork'
constructor: ->
@points = []
@inPorts =
points: new noflo.Port 'object'
@outPorts =
points: new noflo.Port 'object'
@inPorts.points.on 'data', (data) =>
return unless @outPorts.points.isAttached()
return unless data.items.length != 0
@points = data.items
@tspGreedy()
@outPorts.points.send @points
distance: (from, to) =>
dx = @points[to].x - @points[from].x
dy = @points[to].y - @points[from].y
return Math.sqrt(dx*dx + dy*dy)
# Inspired by https://code.google.com/p/google-maps-tsp-solver/
tspGreedy: =>
current = 0
currentDistance = 0
lastNode = 0
visited = (false for i in [0...@points.length])
bestPath = (0 for i in [0...@points.length])
for step in [0...@points.length-1]
visited[current] = true
bestPath[step] = current
nearest = 999999
near = -1
for next in [1...@points.length]
if !visited[next] && ((@distance current, next) < nearest)
nearest = @distance current, next
near = next
currentDistance += @distance current, near
current = near
bestPath[@points.length-1] = current
@points = (@points[i] for i in bestPath)
exports.getComponent = -> new TSP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment