Skip to content
Browse files

Graph, js, etc...

  • Loading branch information...
1 parent 21a8e58 commit 823f96e5511eabfee5c66d9bc2ce26e69af331c8 @hugokernel committed Sep 25, 2013
Showing with 19,196 additions and 104 deletions.
  1. +8 −2 src/BleuettePi/Python/lib/BleuettePi.py
  2. +7 −4 src/BleuettePi/Python/lib/Fake.py
  3. +16 −5 src/BleuettePi/Python/lib/Sequencer.py
  4. +81 −21 src/BleuettePi/Python/lib/Servo.py
  5. +80 −70 src/BleuettePi/Python/ws.py
  6. 0 src/BleuettePi/Python/www/OrbitControls.js
  7. +437 −0 src/BleuettePi/Python/www/bleuette.js
  8. +368 −0 src/BleuettePi/Python/www/curvedLines.js
  9. +0 −2 src/BleuettePi/Python/www/dygraph-combined.js
  10. +1,464 −0 src/BleuettePi/Python/www/flot/API.md
  11. +99 −0 src/BleuettePi/Python/www/flot/CONTRIBUTING.md
  12. +75 −0 src/BleuettePi/Python/www/flot/FAQ.md
  13. +22 −0 src/BleuettePi/Python/www/flot/LICENSE.txt
  14. +12 −0 src/BleuettePi/Python/www/flot/Makefile
  15. +893 −0 src/BleuettePi/Python/www/flot/NEWS.md
  16. +143 −0 src/BleuettePi/Python/www/flot/PLUGINS.md
  17. +110 −0 src/BleuettePi/Python/www/flot/README.md
  18. 0 src/BleuettePi/Python/www/flot/build.log
  19. +4 −0 src/BleuettePi/Python/www/flot/examples/ajax/data-eu-gdp-growth-1.json
  20. +4 −0 src/BleuettePi/Python/www/flot/examples/ajax/data-eu-gdp-growth-2.json
  21. +4 −0 src/BleuettePi/Python/www/flot/examples/ajax/data-eu-gdp-growth-3.json
  22. +4 −0 src/BleuettePi/Python/www/flot/examples/ajax/data-eu-gdp-growth-4.json
  23. +4 −0 src/BleuettePi/Python/www/flot/examples/ajax/data-eu-gdp-growth-5.json
  24. +4 −0 src/BleuettePi/Python/www/flot/examples/ajax/data-eu-gdp-growth.json
  25. +4 −0 src/BleuettePi/Python/www/flot/examples/ajax/data-japan-gdp-growth.json
  26. +4 −0 src/BleuettePi/Python/www/flot/examples/ajax/data-usa-gdp-growth.json
  27. +173 −0 src/BleuettePi/Python/www/flot/examples/ajax/index.html
  28. +87 −0 src/BleuettePi/Python/www/flot/examples/annotating/index.html
  29. +97 −0 src/BleuettePi/Python/www/flot/examples/axes-interacting/index.html
  30. +77 −0 src/BleuettePi/Python/www/flot/examples/axes-multiple/index.html
  31. +893 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/date.js
  32. +114 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/index.html
  33. +1,181 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/africa
  34. +413 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/antarctica
  35. +2,717 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/asia
  36. +1,719 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/australasia
  37. +117 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/backward
  38. +81 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/etcetera
  39. +2,856 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/europe
  40. +10 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/factory
  41. +276 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/iso3166.tab
  42. +100 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/leapseconds
  43. +3,235 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/northamerica
  44. +28 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/pacificnew
  45. +390 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/solar87
  46. +390 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/solar88
  47. +395 −0 src/BleuettePi/Python/www/flot/examples/axes-time-zones/tz/solar89
Sorry, we could not display the entire diff because it was too big.
View
10 src/BleuettePi/Python/lib/BleuettePi.py
@@ -18,6 +18,8 @@
from drivers.Adafruit_MCP230xx import Adafruit_MCP230XX as MCP230XX
from Analog import Analog
+logger = logging.getLogger('BleuettePi')
+
class BleuettePi_Compass:
# Declination, ~Paris : 0.7
@@ -28,8 +30,11 @@ class BleuettePi_Compass:
ADDRESS = 0x1E
def get(self):
- compass = hmc5883l(port = self.PORT, address = self.ADDRESS, gauss = 4.7, declination = self.DECLINATION)
- return [ compass.axes(), compass.degrees(compass.heading()) ]
+ try:
+ compass = hmc5883l(port = self.PORT, address = self.ADDRESS, gauss = 4.7, declination = self.DECLINATION)
+ return [ compass.axes(), compass.degrees(compass.heading()) ]
+ except Exception, e:
+ logger.critical("Exception ! " + str(e));
def realTime(self):
compass = hmc5883l(port = self.PORT, address = self.ADDRESS, gauss = 4.7, declination = self.DECLINATION)
@@ -115,6 +120,7 @@ def __init__(self, mixed):
self.Servo = Servo.Servo(self.serial, fakemode = Config.FAKE_MODE)
self.Servo.init()
+
Servo.Servo_Trim.values = Data.Instance().get(['servo', 'trims'])
Servo.Servo_Limit.values = Data.Instance().get(['servo', 'limits'])
View
11 src/BleuettePi/Python/lib/Fake.py
@@ -26,10 +26,10 @@ def __init__(self, port=1, address=0x1E, gauss=1.3, declination=(0,0)):
pass
def axes(self):
- return (0, 0, 0)
+ return (randint(0, 10), randint(0, 10), randint(0, 10))
def heading(self):
- return (0, 0)
+ return (randint(0, 260), randint(0, 260))
def degrees(self, (degrees, minutes)):
return ""
@@ -46,7 +46,7 @@ def quickCalibrate(self):
pass
def readAccelCal(self):
- return (0, 0, 0)
+ return (randint(0, 10), randint(0, 10), randint(0, 10))
class MCP230XX:
@@ -75,5 +75,8 @@ def __init__(self, a):
pass
def getCurrent(self):
- return 0
+ return 1 + 0.1 * randint(0, 5)
+
+ def read(self, index):
+ return 11 + 0.1 * randint(0, 1)
View
21 src/BleuettePi/Python/lib/Sequencer.py
@@ -2,6 +2,8 @@
import threading, time
import Sequences
import Define
+import Servo
+
#from Define import __, PAUSE, FRONT, BACK, DOWN, UP, MID, DELAY_MIN
HMIN = 85
@@ -61,7 +63,7 @@ def run(self):
self._stopevent.wait(0.5)
- print "le thread s'est termine proprement"
+ print "End of Thread"
def stop(self):
self._stopevent.set()
@@ -119,6 +121,8 @@ def forward(self, count = 1):
class Sequencer:
Servo = None
+ ServoSeq = None
+
Buffer = None
Thread = None
@@ -131,6 +135,10 @@ def __init__(self, servo):
self.Buffer = Sequencer_Buffer(self)
self.Thread = Sequencer_Thread(self)
+ self.ServoSeq = Servo.Servo_Sequencer(self.Servo)
+ self.ServoSeq.daemon = True;
+ self.ServoSeq.start()
+
# def test(self):
# self.Thread = Sequencer_Thread(self)
@@ -193,11 +201,14 @@ def reverse(self, seq, count = 1, start_at = 0, callback = None):
c(s, seq.sequence)
def play(self, seq):
- for i in range(0, len(seq[1])):
- if seq[1][i] != Define.__:
- self.Servo.setValue(i, self.getValue(i, seq[1][i]))
- self.Servo.sendValues()
+ with self.ServoSeq as servoSeq:
+ for i in range(0, len(seq[1])):
+ if seq[1][i] != Define.__:
+ servoSeq.setPosition(i, self.getValue(i, seq[1][i]))
+
+ #self.Servo.setValue(i, self.getValue(i, seq[1][i]))
+ #self.Servo.sendValues()
if seq[2]:
seq[2]()
View
102 src/BleuettePi/Python/lib/Servo.py
@@ -1,7 +1,7 @@
+import serial, types, time, copy, logging, threading
from Serial import Serial
from Define import BPi_Cmd
-import serial, types, time, copy, logging
from array import array
from Data import Data
from Singleton import Singleton
@@ -37,6 +37,69 @@ def wrapper(*args, **kwargs):
return fn(*args)
return wrapper
+# Run in background and send target value when possible
+class Servo_Sequencer(threading.Thread):
+
+ BUFFER_SIZE = 100
+
+ event = None
+
+ __servo = None
+
+ __values = []
+ __buffer = []
+
+ __contextManagerMode = False
+
+ def __init__(self, servo):
+ self.__servo = servo
+
+ threading.Thread.__init__(self)
+ self.event = threading.Event()
+
+ self.logger = logging.getLogger('Servo sequencer')
+
+ def run(self):
+ i = 0
+
+ while True: #not self.__pause:
+
+ if len(self.__buffer):
+ v = self.__buffer.pop()
+ if v:
+ if len(v) == 2:
+ #self.logger.debug('Standard value')
+ self.__servo.setValue(v[0], v[1])
+ self.__servo.sendValues()
+ else:
+ #self.logger.debug('Transaction')
+ for i in range(0, len(v)):
+ self.__servo.setValue(i, v[i])
+ self.__servo.sendValues()
+
+ self.event.wait(0.005)
+
+ def __enter__(self):
+ self.__contextManagerMode = True
+ self.__values = self.__servo.getValues()
+ return self
+
+ def __exit__(self, exception_type, exception_value, traceback):
+ self.__contextManagerMode = False
+ self.__buffer.append(self.__values);
+
+ def setPosition(self, index, value):
+ if len(self.__buffer) <= self.BUFFER_SIZE:
+ if self.__contextManagerMode:
+ self.__values[index] = value
+ else:
+ self.__buffer.append([ index, value ]);
+ else:
+ self.logger.error('Buffer full : Skip value !')
+
+ #def stop(self):
+ # self._stopevent.set()
+
LOCK = False
class Servo(Serial):
@@ -63,11 +126,8 @@ class Servo(Serial):
last_status_code = ''
- #values = array('c', [chr(0)] * COUNT)
- #sent_values = array('c', [chr(0)] * COUNT)
-
- values = array('h', [0] * COUNT)
- sent_values = array('h', [0] * COUNT)
+ __values = array('h', [0] * COUNT)
+ __sent_values = array('h', [0] * COUNT)
__callback = []
@@ -96,7 +156,6 @@ def sendValues(self):
retry = self.MAX_RETRY
if LOCK:
- self.logger.critical('Lock error !')
raise ServoException('Lock error !')
LOCK = True
@@ -109,8 +168,7 @@ def sendValues(self):
control = 0
for i in range(0, self.COUNT):
- #self.serial.write(chr(value))
- value = self.writeValue(i, self.values[i])
+ value = self.writeValue(i, self.__values[i])
values.append(value)
control += value
@@ -121,10 +179,10 @@ def sendValues(self):
self.last_status_code = self.getResponse()
- if (self.last_status_code == self.ACK):
+ if self.last_status_code == self.ACK or self.fakemode:
if self.DEBUG:
self.logger.debug('Ack OK !')
- self.sent_values = copy.copy(self.values)
+ self.__sent_values = copy.copy(self.__values)
break
else:
self.logger.warning("Ack KO !")
@@ -141,26 +199,26 @@ def sendValues(self):
return self.getLastStatus()
def init(self):
- return self.command(BPi_Cmd.INIT)
+ return self.serial.command(BPi_Cmd.INIT)
# Pause
def pause(self):
- return self.command(BPi_Cmd.PAUSE)
+ return self.serial.command(BPi_Cmd.PAUSE)
# Resume
def resume(self):
- return self.command(BPi_Cmd.RESUME)
+ return self.serial.command(BPi_Cmd.RESUME)
# Set to 0 all consigne
def clear(self):
if self.command(BPi_Cmd.CLEAR) == self.ACK:
for i in xrange(0, self.COUNT):
- self.sent_values[i] = 0
+ self.__sent_values[i] = 0
def setValue(self, index, value):
#if type(value) == types.IntType:
# value = chr(value)
- self.values[index] = value
+ self.__values[index] = value
def setValues(self, servos, values):
v = 0
@@ -169,9 +227,11 @@ def setValues(self, servos, values):
self.setValue(i, values[v])
v += 1
- def dump(self):
- self.logger.debug('Buff values: %s' % self.values)
- self.logger.debug('Sent values: %s' % self.sent_values)
- #print 'Buff values:', self.values
- #print 'Sent values:', self.sent_values
+ def getValues(self):
+ return self.__sent_values
+
+ def dump(self, allval = False):
+ self.logger.debug('Sent values: %s' % self.__sent_values)
+ if allval:
+ self.logger.debug('Buff values: %s' % self.__values)
View
150 src/BleuettePi/Python/ws.py
@@ -11,7 +11,7 @@
from Bleuette import Bleuette
from Data import Data
from Sequences import Sequences
-import Drive
+import Servo
import config as Config
from RainbowHandler import RainbowLoggingHandler
@@ -72,45 +72,50 @@ def emit(self, record):
B = Bleuette()
-class Affiche(threading.Thread):
+class Bleuette_Thread(threading.Thread):
data = {}
data_last = {}
- def __init__(self, nom = ''):
+ def __init__(self):
threading.Thread.__init__(self)
- self.nom = nom
- self._stopevent = threading.Event( )
+ self._stopevent = threading.Event()
+
+ def sendSensors(self):
+ compass = 0
+
+ acc = B.BPi.Accelerometer.get()
+ ground = B.BPi.GroundSensor.get()
+ compass = B.BPi.Compass.get()
+ #current = '%.1f' % round(B.BPi.Analog.getCurrent(), 2)
+ #batteryVoltage = '%.1f' % round(B.BPi.Analog.read(6), 2)
+ current = 0
+ batteryVoltage = 0
+
+ self.data = {
+ 'type': 'sensors',
+ 'sensors': {
+ 'accelerometer': acc,
+ 'compass': compass,
+ 'ground': ground,
+ 'current': current,
+ 'batteryVoltage': batteryVoltage
+ }
+ }
+
+ #if any(map(eq, self.data, self.data_last))== False:
+ self.update()
def run(self):
while not self._stopevent.isSet():
- compass = 0
-
- acc = B.BPi.Accelerometer.get()
- ground = B.BPi.GroundSensor.get()
- compass = B.BPi.Compass.get()
- #current = B.BPi.Analog.getCurrent()
- current = 0
-
- self.data = {
- 'type': 'sensors',
- 'sensors': {
- 'accelerometer': acc,
- 'compass': compass,
- 'ground': ground,
- 'current': current
- }
- }
+ # Handle sensors
+ self.sendSensors()
- #print self.data
+ # Handle servos value
+ #self.servos()
- #if any(map(eq, self.data, self.data_last))== False:
- self.update()
-
- self._stopevent.wait(0.5)
-
- print "End of thread !"
+ self._stopevent.wait(0.1)
def update(self):
for c in cl:
@@ -119,11 +124,11 @@ def update(self):
self.data_last = copy.copy(self.data)
def stop(self):
- self._stopevent.set( )
+ self._stopevent.set()
-a = Affiche('Thread A')
-a.daemon = True;
-a.start();
+Bt = Bleuette_Thread()
+Bt.daemon = True;
+Bt.start();
speed = 16
import Define
@@ -169,23 +174,16 @@ def on_message(self, message):
data = json.loads(message)
- import Servo
+ CMD_SET = 'set'
+ CMD_DRIVE = 'drive'
+ CMD_CONFIG = 'config'
+ CMD_CONTROL = 'control'
+ CMD_SEQUENCE = 'sequence'
+ CMD_SERVO = 'servo'
try:
- if data['cmd'] == 'set':
- if data['type'] == 'trim':
- Servo.Servo_Trim.values[data['servo']] = data['value'];
- B.Sequencer.Servo.sendValues()
- elif data['type'] == 'limit':
- Servo.Servo_Limit.values[data['servo']] = [ data['min'], data['max'] ];
- B.Sequencer.Servo.sendValues()
- elif data['type'] == 'position':
- B.Sequencer.Servo.setValue(data['servo'], data['value'])
- B.Sequencer.Servo.sendValues()
- elif data['type'] == 'speed':
- global speed
- speed = data['value']
- elif data['type'] == 'livemode':
+ if data['cmd'] == CMD_SET:
+ if data['type'] == 'livemode':
if data['status']:
B.Sequencer.Servo.setCallback(self.livemode)
else:
@@ -214,7 +212,7 @@ def on_message(self, message):
#print logger.getEffectiveLevel()
- elif data['cmd'] == 'drive':
+ elif data['cmd'] == CMD_DRIVE:
if data['status'] == 'begin':
if data['direction'] == 'forward':
@@ -228,27 +226,14 @@ def on_message(self, message):
elif data['status'] == 'end':
B.Drive.end()
- elif data['cmd'] == 'config':
- if data['action'] == 'save':
- Data.Instance().set(['servo', 'trims'], Servo.Servo_Trim.values)
- Data.Instance().set(['servo', 'limits'], Servo.Servo_Limit.values)
- Data.Instance().save()
- elif data['action'] == 'get':
- config = {
- 'type': 'config',
- 'data': {
- 'trims': Data.Instance().get(['servo', 'trims']),
- 'limits': Data.Instance().get(['servo', 'limits'])
- }
- }
- self.write(json.dumps(config))
-
- elif data['cmd'] == 'control':
+ elif data['cmd'] == CMD_CONFIG:
+ pass
+ elif data['cmd'] == CMD_CONTROL:
if data['action'] == 'reset':
B.BPi.reset()
- elif data['cmd'] == 'sequence':
+ elif data['cmd'] == CMD_SEQUENCE:
if data['name'] == 'middle':
B.Sequencer.forward(Sequences['middle'], 1)
@@ -259,14 +244,39 @@ def on_message(self, message):
elif data['name'] == 'release':
B.Sequencer.forward(Sequences['release'], 1)
- elif data['cmd'] == 'servo':
+ elif data['cmd'] == CMD_SERVO:
- if data['name'] == 'init':
+ if data['action'] == 'init':
B.Sequencer.Servo.init()
- elif data['name'] == 'pause':
+ elif data['action'] == 'pause':
B.Sequencer.Servo.pause()
- elif data['name'] == 'resume':
+ elif data['action'] == 'resume':
B.Sequencer.Servo.resume()
+ elif data['action'] == 'save':
+ Data.Instance().set(['servo', 'trims'], Servo.Servo_Trim.values)
+ Data.Instance().set(['servo', 'limits'], Servo.Servo_Limit.values)
+ Data.Instance().save()
+ elif data['action'] == 'trim':
+ with B.Sequencer.ServoSeq:
+ Servo.Servo_Trim.values[data['servo']] = data['value'];
+ elif data['action'] == 'limit':
+ with B.Sequencer.ServoSeq:
+ Servo.Servo_Limit.values[data['servo']] = [ data['min'], data['max'] ];
+ elif data['action'] == 'position':
+ with B.Sequencer.ServoSeq:
+ B.Sequencer.ServoSeq.setPosition(data['servo'], data['value'])
+ elif data['action'] == 'speed':
+ global speed
+ speed = data['value']
+ elif data['action'] == 'get':
+ config = {
+ 'type': CMD_SERVO,
+ 'data': {
+ 'trims': Data.Instance().get(['servo', 'trims']),
+ 'limits': Data.Instance().get(['servo', 'limits'])
+ }
+ }
+ self.write(json.dumps(config))
else:
logger.warning("Message : %s" % message)
View
0 src/BleuettePi/Python/www/OrbitControls.js 100644 → 100755
File mode changed.
View
437 src/BleuettePi/Python/www/bleuette.js
@@ -0,0 +1,437 @@
+
+(function (exports, $) {
+
+ exports.Bleuette = function() {
+
+ var self = this;
+
+ return {
+
+ host: '',
+ fakeMode: false,
+
+ CMD_SET: 'set',
+ CMD_DRIVE: 'drive',
+ CMD_CONFIG: 'config',
+ CMD_CONTROL: 'control',
+ CMD_SEQUENCE: 'sequence',
+ CMD_SERVO: 'servo',
+
+ init: function() {
+
+ self.ws = new WebSocket('ws://' + this.host + ':8888/ws');
+ var $message = $('#message');
+
+ self.ws.onopen = function(){
+ $message.attr("class", 'label label-success');
+ $message.text('open');
+
+ B.sendCmd(B.CMD_SERVO, { action: 'get' });
+ };
+
+ self.ws.onmessage = function(ev){
+ json = JSON.parse(ev.data);
+ switch (json.type) {
+ case B.CMD_SERVO:
+ Events.emit('servo.received', json.data);
+ break;
+
+ case 'position':
+ Events.emit('position.received', json.data.servos);
+ break;
+ case 'sensors':
+ Events.emit('sensors.received', json.sensors);
+ break;
+ case 'log':
+
+ switch(json.data.levelname) {
+ case 'DEBUG':
+ console.debug('[' + json.data.levelname + '] ' + json.data.msg);
+ break;
+ case 'WARNING':
+ console.warn('[' + json.data.levelname + '] ' + json.data.msg);
+ break;
+ case 'INFO':
+ console.info('[' + json.data.levelname + '] ' + json.data.msg);
+ break;
+ case 'ERROR':
+ case 'CRITICAL':
+ console.error('[' + json.data.levelname + '] ' + json.data.msg);
+ $.pnotify({
+ title: 'Error !',
+ text: '[' + json.data.levelname + '] ' + json.data.msg,
+ type: 'error'
+ });
+ break;
+ default:
+ alert('Log level error unknow ' + json.data.levelname + ' !');
+ }
+
+ break;
+ default:
+ console.info(json);
+ }
+ };
+
+ self.ws.onclose = function(ev){
+ $message.attr("class", 'label label-important');
+ $message.text('closed');
+ };
+
+ self.ws.onerror = function(ev){
+ $message.attr("class", 'label label-warning');
+ $message.text('error occurred');
+ };
+ },
+
+ config: (function() {
+
+ return {
+ received: function(data) {
+ alert('received!');
+ }
+ }
+ })(),
+
+ sendCmd: function(cmd, data) {
+ data = data || {};
+ data['cmd'] = cmd;
+ self.ws.send(JSON.stringify(data));
+ },
+
+ simulation: (function() {
+
+ //var parent, renderer, scene, camera, controls;
+
+ //var bodyTruc, leg0, leg1, leg2, leg3, leg4, leg5;
+
+ return {
+
+ legs: [],
+
+ init: function() {
+
+ // info
+ info = document.createElement( 'div' );
+ info.style.position = 'absolute';
+ info.style.top = '30px';
+ info.style.width = '100%';
+ info.style.textAlign = 'center';
+ info.style.color = '#fff';
+ info.style.fontWeight = 'bold';
+ info.style.backgroundColor = 'transparent';
+ info.style.zIndex = '1';
+ info.style.fontFamily = 'Monospace';
+ //info.innerHTML = 'Drag mouse to rotate camera';
+ document.body.appendChild( info );
+
+/*
+ // renderer
+ //renderer = new THREE.CanvasRenderer();
+ renderer = new THREE.WebGLRenderer();
+ renderer.setSize( window.innerWidth, window.innerHeight );
+ renderer.physicallyBasedShading = true;
+
+ //document.getElementById('render').appendChild(renderer.domElement);
+*/
+ container = document.getElementById( 'canvas' );
+ //document.body.appendChild( container );
+
+ renderer = new THREE.CanvasRenderer();
+ //renderer = new THREE.WebGLRenderer();
+ renderer.setSize(640, 480);
+ container.appendChild( renderer.domElement );
+
+ //document.body.appendChild( renderer.domElement );
+
+ // scene
+ scene = new THREE.Scene();
+
+ //light = new THREE.AmbientLight(0x00000);
+ //scene.add(light);
+ //var ambientLight = new THREE.AmbientLight(0x0000c1);
+ //scene.add(ambientLight);
+ var light = new THREE.PointLight(0xffffc1);
+ light.position.set(10, 10, 10);
+ light.add(new THREE.AxisHelper(20));
+ scene.add(light);
+
+ // camera
+ camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.1, 100);
+ camera.position.set(20, 20, 20);
+
+ // controls
+ controls = new THREE.OrbitControls(camera, container);
+
+ // axes
+ scene.add(new THREE.AxisHelper(20));
+
+ // geometry
+ var geometry = new THREE.CubeGeometry(2, 2, 2);
+
+ // material
+ /*
+ var material = new THREE.MeshBasicMaterial({
+ color: 0xffffff,
+ wireframe: true
+ });
+ */
+ // parent
+ parent = new THREE.Object3D();
+ scene.add(parent);
+
+/*
+ leg = function(pos) {
+ legGeo = new THREE.CubeGeometry(1, 1, 4);
+ legMaterial = new THREE.MeshBasicMaterial({
+ color: 0xff0000,
+ wireframe: false
+ });
+
+ l = new THREE.Object3D();
+ l.position.x = pos.x;
+ l.position.y = pos.y;
+ l.position.z = pos.z;
+ //l.rotation.x = 5;
+ parent.add(l);
+
+ m = new THREE.Mesh(legGeo, legMaterial);
+ if (pos.z == 2.5)
+ m.position.z = 2;
+ else
+ m.position.z = -2;
+ l.add(m);
+
+ return l;
+ };
+*/
+
+ cube = function(w, l, h, parent, color) {
+ color = color || 0x000000;
+ geo = new THREE.CubeGeometry(w, l, h);
+ mat = new THREE.MeshBasicMaterial({
+ color: color,
+ wireframe: false
+ });
+
+ obj = new THREE.Object3D();
+ parent.add(obj);
+ mesh = new THREE.Mesh(geo, mat);
+ obj.add(mesh)
+
+ //obj.add(new THREE.AxisHelper(20));
+
+ return { obj: obj, mesh: mesh };
+ };
+
+ cad = (function() {
+ return {
+
+ body: (function() {
+
+ return {
+ make: function() {
+ geo = new THREE.CubeGeometry(15, 1, 6);
+ material = new THREE.MeshLambertMaterial({ color: 'blue', ambient: 'blue' });
+
+ /*
+ material = new THREE.MeshBasicMaterial({
+ color: 0x00ff00,
+ wireframe: false
+ });
+ */
+
+ obj = new THREE.Object3D();
+ parent.add(obj)
+
+ mesh = new THREE.Mesh(geo, material);
+ obj.add(mesh)
+
+ return obj;
+ },
+
+ create: function() {
+ obj = this.make();
+ obj.position.y = 0.5;
+ //obj = this.make();
+ //obj.position.y = 1;
+ }
+ };
+
+ })(),
+
+ leg: (function() {
+
+ return {
+
+ positions: [
+ [ -5, 0.5, 3, 0 ],
+ [ -5, 0.5, -3, Math.PI ],
+
+ [ 0, 0.5, 3, 0 ],
+ [ 0, 0.5, -3, Math.PI ],
+
+ [ 5, 0.5, 3, 0 ],
+ [ 5, 0.5, -3, Math.PI ],
+ ],
+
+ data: {},
+
+ get: function(index) {
+ return this.data[index];
+ },
+
+ create: function(index, x, y, z, value) {
+ armh = cube(0.5, 0.5, 3, parent, 'yellow');
+ armh.obj.position.x = x;
+ armh.obj.position.y = y;
+ armh.obj.position.z = z;
+
+ armh.obj.rotation.y = value;
+ armh.mesh.position.z = 1.5;
+ this.data[index] = armh;
+
+ armv = cube(0.5, 0.5, 3, armh.mesh, 'green');
+ armv.obj.position.y = 0;
+ armv.obj.position.z = 1.5;
+ armv.mesh.position.z = 1.5;
+
+ this.data[index + 6] = armv;
+ },
+
+ createAll: function() {
+ for (i = 0; i < this.positions.length; i++) {
+ position = this.positions[i];
+ this.create(i, position[0], position[1], position[2], position[3]);
+ }
+ },
+
+ setV: function(index, value) {
+ if (!(index % 1)) {
+ value += Math.PI;
+ }
+ this.data[index].obj.rotation.x = value;
+ },
+
+ setH: function(index, value) {
+ if (index % 2) {
+ value -= Math.PI;
+ }
+ this.data[index].obj.rotation.y = value;
+ },
+
+ setColor: function(index, color) {
+ this.data[index].mesh.material.color.setHex(color);
+ }
+ }
+ })(),
+
+ }
+ })();
+
+ cad.body.create();
+ cad.leg.createAll();
+/*
+ leg.create(0, 5, 0, 3, 0);
+ leg.create(1, 0, 0, 3, 0);
+ data = leg.create(2, -5, 0, 3, 0);
+ A = data[0];
+ AA = data[1];
+
+ leg.create(3, -5, 0, -3, Math.PI);
+ leg.create(4, 0, 0, -3, Math.PI);
+ leg.create(5, 5, 0, -3, Math.PI);
+*/
+
+/*
+ A = cube(0.5, 0.5, 3, parent);
+ A[0].position.x = -5;
+ A[0].position.y = 0;
+ A[0].position.z = 3;
+ A[1].position.z = 1.5;
+
+ AA = cube(0.5, 0.5, 3, A[1]);
+ AA[0].position.y = 0;
+ AA[0].position.z = 1.5;
+ AA[1].position.z = 1.5;
+*/
+
+
+/*
+ this.legs[0] = leg({ x: -3, y: 0, z: 2.5 });
+ this.legs[1] = leg({ x: 0, y: 0, z: 2.5 });
+ this.legs[2] = leg({ x: 3, y: 0, z: 2.5 });
+
+ this.legs[3] = leg({ x: -3, y: 0, z: -2.5 });
+ this.legs[4] = leg({ x: 0, y: 0, z: -2.5 });
+ this.legs[5] = leg({ x: 3, y: 0, z: -2.5 });
+*/
+ },
+
+ setServoPosition: function(servo, value) {
+
+ // 0 -> -0.5
+ // 128 -> 0
+ // 255 -> 0.5
+
+ // 0
+
+
+ //console.info("Coeff: " + coeff);
+
+ // Value = 0, Skip !
+ if (!value) {
+ return;
+ }
+
+ // Horizontal
+ if (servo <= 5) {
+ //B.simulation.legs[i].rotation.y = coeff * json.data.servos[i] - 1.4/2;
+ max = 0.9;
+ coeff = max / 128;
+
+ //console.info("Servo:" + servo + ", value: " + value + ", sent:" + (coeff * value - max));
+ value = coeff * value - max;
+
+ if (servo % 2) {
+ value = -value;
+ }
+
+ //if (servo == 1)
+ if (cad) {
+ cad.leg.setH(servo, value);
+ }
+ } else {
+ //value = -value;
+ max = 1;
+ coeff = max / 128;
+
+ //console.info("Servo:" + servo + ", value: " + value + ", sent:" + (coeff * value - max));
+ value = coeff * value + max + 2;
+
+ if (cad) {
+ cad.leg.setV(servo, value);
+ }
+ //B.simulation.legs[i - 3].rotation.x = coeff * json.data.servos[i] - 1.4/2;
+ }
+ },
+
+ animate: function() {
+
+ requestAnimationFrame(B.simulation.animate);
+
+ controls.update();
+
+ renderer.render(scene, camera);
+ }
+ };
+
+ })()
+ }
+
+ return self;
+ };
+
+})(this, jQuery);
+
+
View
368 src/BleuettePi/Python/www/curvedLines.js
@@ -0,0 +1,368 @@
+/* The MIT License
+
+ Copyright (c) 2011 by Michael Zinsmaier and nergal.dev
+ Copyright (c) 2012 by Thomas Ritou
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+ /*
+
+ ____________________________________________________
+
+ what it is:
+ ____________________________________________________
+
+ curvedLines is a plugin for flot, that tries to display lines in a smoother way.
+ The plugin is based on nergal.dev's work https://code.google.com/p/flot/issues/detail?id=226
+ and further extended with a mode that forces the min/max points of the curves to be on the
+ points. Both modes are achieved through adding of more data points
+ => 1) with large data sets you may get trouble
+ => 2) if you want to display the points too, you have to plot them as 2nd data series over the lines
+
+ && 3) consecutive x data points are not allowed to have the same value
+
+ This is version 0.5 of curvedLines so it will probably not work in every case. However
+ the basic form of use descirbed next works (:
+
+ Feel free to further improve the code
+
+ ____________________________________________________
+
+ how to use it:
+ ____________________________________________________
+
+ var d1 = [[5,5],[7,3],[9,12]];
+
+ var options = { series: { curvedLines: { active: true }}};
+
+ $.plot($("#placeholder"), [{data = d1, lines: { show: true}, curvedLines: {apply: true}}], options);
+
+ _____________________________________________________
+
+ options:
+ _____________________________________________________
+
+ active: bool true => plugin can be used
+ apply: bool true => series will be drawn as curved line
+ fit: bool true => forces the max,mins of the curve to be on the datapoints
+ curvePointFactor int defines how many "virtual" points are used per "real" data point to
+ emulate the curvedLines
+ fitPointDist: int defines the x axis distance of the additional two points that are used
+ to enforce the min max condition. (you will get curvePointFactor * 3 * |datapoints|
+ "virtual" points if fit is true)
+
+ + line options (since v0.5 curved lines use flots line implementation for drawing
+ => line options like fill, show ... are supported out of the box)
+
+ */
+
+ /*
+ * v0.1 initial commit
+ * v0.15 negative values should work now (outcommented a negative -> 0 hook hope it does no harm)
+ * v0.2 added fill option (thanks to monemihir) and multi axis support (thanks to soewono effendi)
+ * v0.3 improved saddle handling and added basic handling of Dates
+ * v0.4 rewritten fill option (thomas ritou) mostly from original flot code (now fill between points rather than to graph bottom), corrected fill Opacity bug
+ * v0.5 rewritten instead of implementing a own draw function CurvedLines is now based on the processDatapoints flot hook (credits go to thomas ritou).
+ * This change breakes existing code however CurvedLines are now just many tiny straight lines to flot and therefore all flot lines options (like gradient fill,
+ * shadow) are now supported out of the box
+ */
+
+ (function($) {
+
+ var options = {
+ series : {
+ curvedLines : {
+ active : false,
+ apply: false,
+ fit : false,
+ curvePointFactor : 20,
+ fitPointDist : undefined
+ }
+ }
+ };
+
+ function init(plot) {
+
+ plot.hooks.processOptions.push(processOptions);
+
+ //if the plugin is active register processDatapoints method
+ function processOptions(plot, options) {
+
+ if (options.series.curvedLines.active) {
+ plot.hooks.processDatapoints.unshift(processDatapoints);
+ }
+ }
+
+ //only if the plugin is active
+ function processDatapoints(plot, series, datapoints) {
+ if (series.curvedLines.apply == true && series.originSeries === undefined) {
+ if (series.lines.fill) {
+
+ var pointsTop = calculateCurvePoints(datapoints, series.curvedLines, 1)
+ ,pointsBottom = calculateCurvePoints(datapoints, series.curvedLines, 2); //flot makes sure for us that we've got a second y point if fill is true !
+
+ //Merge top and bottom curve
+ datapoints.pointsize = 3;
+ datapoints.points = [];
+ var j = 0;
+ var k = 0;
+ var i = 0;
+ var ps = 2;
+ while (i < pointsTop.length || j < pointsBottom.length) {
+ if (pointsTop[i] == pointsBottom[j]) {
+ datapoints.points[k] = pointsTop[i];
+ datapoints.points[k + 1] = pointsTop[i + 1];
+ datapoints.points[k + 2] = pointsBottom[j + 1];
+ j += ps;
+ i += ps;
+
+ } else if (pointsTop[i] < pointsBottom[j]) {
+ datapoints.points[k] = pointsTop[i];
+ datapoints.points[k + 1] = pointsTop[i + 1];
+ datapoints.points[k + 2] = k > 0 ? datapoints.points[k-1] : null;
+ i += ps;
+ } else {
+ datapoints.points[k] = pointsBottom[j];
+ datapoints.points[k + 1] = k > 1 ? datapoints.points[k-2] : null;
+ datapoints.points[k + 2] = pointsBottom[j + 1];
+ j += ps;
+ }
+ k += 3;
+ }
+
+ if (series.lines.lineWidth > 0) {//Let's draw line in separate series
+ var newSerie = $.extend({}, series);
+ newSerie.lines = $.extend({}, series.lines);
+ newSerie.lines.fill = undefined;
+ newSerie.label = undefined;
+ newSerie.datapoints = $.extend({}, series.datapoints);
+ //Redefine datapoints to top only (else it can have null values which will open the cruve !)
+ newSerie.datapoints.points = pointsTop;
+ newSerie.datapoints.pointsize = 2;
+ newSerie.curvedLines.apply = false;
+ //Don't redo curve point calculation as datapoint is copied to this new serie
+ //We find our series to add the line just after the fill (so other series you wanted above this one will still be)
+ var allSeries = plot.getData();
+ for ( i = 0; i < allSeries.length; i++) {
+ if (allSeries[i] == series) {
+ plot.getData().splice(i + 1, 0, newSerie);
+ break;
+ }
+ }
+
+ series.lines.lineWidth = 0;
+ }
+
+ } else if (series.lines.lineWidth > 0) {
+ datapoints.points = calculateCurvePoints(datapoints, series.curvedLines, 1);
+ datapoints.pointsize = 2;
+ }
+ }
+ }
+
+ //no real idea whats going on here code mainly from https://code.google.com/p/flot/issues/detail?id=226
+ //if fit option is selected additional datapoints get inserted before the curve calculations in nergal.dev s code.
+ function calculateCurvePoints(datapoints, curvedLinesOptions, yPos) {
+
+ var points = datapoints.points, ps = datapoints.pointsize;
+ var num = curvedLinesOptions.curvePointFactor * (points.length / ps);
+
+ var xdata = new Array;
+ var ydata = new Array;
+
+ var X = 0;
+ var Y = yPos;
+
+ var curX = -1;
+ var curY = -1;
+ var j = 0;
+
+ if (curvedLinesOptions.fit) {
+ //insert a point before and after the "real" data point to force the line
+ //to have a max,min at the data point however only if it is a lowest or highest point of the
+ //curve => avoid saddles
+
+ var fpDist;
+ if(typeof curvedLinesOptions.fitPointDist == 'undefined') {
+ //estimate it
+ var minX = points[0];
+ var maxX = points[points.length-ps];
+ fpDist = (maxX - minX) / (500 * 100); //x range / (estimated pixel length of placeholder * factor)
+ } else {
+ //use user defined value
+ fpDist = curvedLinesOptions.fitPointDist;
+ }
+
+ for (var i = 0; i < points.length; i += ps) {
+
+ var front = new Array;
+ var back = new Array;
+ curX = i;
+ curY = i + yPos;
+
+ //add point X s
+ front[X] = points[curX] - fpDist;
+ back[X] = points[curX] + fpDist;
+
+ var factor = 2;
+ while (front[X] == points[curX] || back[X] == points[curX]) {
+ //inside the ulp
+ front[X] = points[curX] - (fpDist * factor);
+ back[X] = points[curX] + (fpDist * factor);
+ factor++;
+ }
+
+ //add point Y s
+ back[Y] = points[curY];
+ front[Y] = points[curY];
+
+
+
+ //get points (front and back) Y value for saddle test
+ var frontPointY = points[curY];
+ var backPointY = points[curY];
+ if (i >= ps) {
+ frontPointY = points[curY - ps];
+ }
+ if ((i + ps) < points.length) {
+ backPointY = points[curY + ps];
+ }
+
+ //test for a saddle
+ if ((frontPointY <= points[curY] && backPointY <= points[curY]) || //max or partial horizontal
+ (frontPointY >= points[curY] && backPointY >= points[curY])) {//min or partial horizontal
+
+ //add curve points
+ xdata[j] = front[X];
+ ydata[j] = front[Y];
+ j++;
+
+ xdata[j] = points[curX];
+ ydata[j] = points[curY];
+ j++;
+
+ xdata[j] = back[X];
+ ydata[j] = back[Y];
+ j++;
+ } else {//saddle
+ //use original point only
+ xdata[j] = points[curX];
+ ydata[j] = points[curY];
+ j++;
+ }
+
+ }
+ } else {
+ //just use the datapoints
+ for (var i = 0; i < points.length; i += ps) {
+ curX = i;
+ curY = i + yPos;
+
+ xdata[j] = points[curX];
+ ydata[j] = points[curY];
+ j++;
+ }
+ }
+
+ var n = xdata.length;
+
+ var y2 = new Array();
+ var delta = new Array();
+ y2[0] = 0;
+ y2[n - 1] = 0;
+ delta[0] = 0;
+
+ for (var i = 1; i < n - 1; ++i) {
+ var d = (xdata[i + 1] - xdata[i - 1]);
+ if (d == 0) {
+ //point before current point and after current point need some space in between
+ return [];
+ }
+
+ var s = (xdata[i] - xdata[i - 1]) / d;
+ var p = s * y2[i - 1] + 2;
+ y2[i] = (s - 1) / p;
+ delta[i] = (ydata[i + 1] - ydata[i]) / (xdata[i + 1] - xdata[i]) - (ydata[i] - ydata[i - 1]) / (xdata[i] - xdata[i - 1]);
+ delta[i] = (6 * delta[i] / (xdata[i + 1] - xdata[i - 1]) - s * delta[i - 1]) / p;
+ }
+
+ for (var j = n - 2; j >= 0; --j) {
+ y2[j] = y2[j] * y2[j + 1] + delta[j];
+ }
+
+ var step = (xdata[n - 1] - xdata[0]) / (num - 1);
+
+ var xnew = new Array;
+ var ynew = new Array;
+ var result = new Array;
+
+ xnew[0] = xdata[0];
+ ynew[0] = ydata[0];
+
+ result.push(xnew[0]);
+ result.push(ynew[0]);
+
+ for ( j = 1; j < num; ++j) {
+ //new x point (sampling point for the created curve)
+ xnew[j] = xnew[0] + j * step;
+
+ var max = n - 1;
+ var min = 0;
+
+ while (max - min > 1) {
+ var k = Math.round((max + min) / 2);
+ if (xdata[k] > xnew[j]) {
+ max = k;
+ } else {
+ min = k;
+ }
+ }
+
+ //found point one to the left and one to the right of generated new point
+ var h = (xdata[max] - xdata[min]);
+
+ if (h == 0) {
+ //similar to above two points from original x data need some space between them
+ return [];
+ }
+
+ var a = (xdata[max] - xnew[j]) / h;
+ var b = (xnew[j] - xdata[min]) / h;
+
+ ynew[j] = a * ydata[min] + b * ydata[max] + ((a * a * a - a) * y2[min] + (b * b * b - b) * y2[max]) * (h * h) / 6;
+
+ result.push(xnew[j]);
+ result.push(ynew[j]);
+ }
+
+ return result;
+ }
+
+ }//end init
+
+ $.plot.plugins.push({
+ init : init,
+ options : options,
+ name : 'curvedLines',
+ version : '0.5'
+ });
+
+ })(jQuery);
+
View
2 src/BleuettePi/Python/www/dygraph-combined.js
0 additions, 2 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
1,464 src/BleuettePi/Python/www/flot/API.md
@@ -0,0 +1,1464 @@
+# Flot Reference #
+
+Consider a call to the plot function:
+
+```js
+var plot = $.plot(placeholder, data, options)
+```
+
+The placeholder is a jQuery object or DOM element or jQuery expression
+that the plot will be put into. This placeholder needs to have its
+width and height set as explained in the [README](README.md) (go read that now if
+you haven't, it's short). The plot will modify some properties of the
+placeholder so it's recommended you simply pass in a div that you
+don't use for anything else. Make sure you check any fancy styling
+you apply to the div, e.g. background images have been reported to be a
+problem on IE 7.
+
+The plot function can also be used as a jQuery chainable property. This form
+naturally can't return the plot object directly, but you can still access it
+via the 'plot' data key, like this:
+
+```js
+var plot = $("#placeholder").plot(data, options).data("plot");
+```
+
+The format of the data is documented below, as is the available
+options. The plot object returned from the call has some methods you
+can call. These are documented separately below.
+
+Note that in general Flot gives no guarantees if you change any of the
+objects you pass in to the plot function or get out of it since
+they're not necessarily deep-copied.
+
+
+## Data Format ##
+
+The data is an array of data series:
+
+```js
+[ series1, series2, ... ]
+```
+
+A series can either be raw data or an object with properties. The raw
+data format is an array of points:
+
+```js
+[ [x1, y1], [x2, y2], ... ]
+```
+
+E.g.
+
+```js
+[ [1, 3], [2, 14.01], [3.5, 3.14] ]
+```
+
+Note that to simplify the internal logic in Flot both the x and y
+values must be numbers (even if specifying time series, see below for
+how to do this). This is a common problem because you might retrieve
+data from the database and serialize them directly to JSON without
+noticing the wrong type. If you're getting mysterious errors, double
+check that you're inputting numbers and not strings.
+
+If a null is specified as a point or if one of the coordinates is null
+or couldn't be converted to a number, the point is ignored when
+drawing. As a special case, a null value for lines is interpreted as a
+line segment end, i.e. the points before and after the null value are
+not connected.
+
+Lines and points take two coordinates. For filled lines and bars, you
+can specify a third coordinate which is the bottom of the filled
+area/bar (defaults to 0).
+
+The format of a single series object is as follows:
+
+```js
+{
+ color: color or number
+ data: rawdata
+ label: string
+ lines: specific lines options
+ bars: specific bars options
+ points: specific points options
+ xaxis: number
+ yaxis: number
+ clickable: boolean
+ hoverable: boolean
+ shadowSize: number
+ highlightColor: color or number
+}
+```
+
+You don't have to specify any of them except the data, the rest are
+options that will get default values. Typically you'd only specify
+label and data, like this:
+
+```js
+{
+ label: "y = 3",
+ data: [[0, 3], [10, 3]]
+}
+```
+
+The label is used for the legend, if you don't specify one, the series
+will not show up in the legend.
+
+If you don't specify color, the series will get a color from the
+auto-generated colors. The color is either a CSS color specification
+(like "rgb(255, 100, 123)") or an integer that specifies which of
+auto-generated colors to select, e.g. 0 will get color no. 0, etc.
+
+The latter is mostly useful if you let the user add and remove series,
+in which case you can hard-code the color index to prevent the colors
+from jumping around between the series.
+
+The "xaxis" and "yaxis" options specify which axis to use. The axes
+are numbered from 1 (default), so { yaxis: 2} means that the series
+should be plotted against the second y axis.
+
+"clickable" and "hoverable" can be set to false to disable
+interactivity for specific series if interactivity is turned on in
+the plot, see below.
+
+The rest of the options are all documented below as they are the same
+as the default options passed in via the options parameter in the plot
+commmand. When you specify them for a specific data series, they will
+override the default options for the plot for that data series.
+
+Here's a complete example of a simple data specification:
+
+```js
+[ { label: "Foo", data: [ [10, 1], [17, -14], [30, 5] ] },
+ { label: "Bar", data: [ [11, 13], [19, 11], [30, -7] ] }
+]
+```
+
+
+## Plot Options ##
+
+All options are completely optional. They are documented individually
+below, to change them you just specify them in an object, e.g.
+
+```js
+var options = {
+ series: {
+ lines: { show: true },
+ points: { show: true }
+ }
+};
+
+$.plot(placeholder, data, options);
+```
+
+
+## Customizing the legend ##
+
+```js
+legend: {
+ show: boolean
+ labelFormatter: null or (fn: string, series object -> string)
+ labelBoxBorderColor: color
+ noColumns: number
+ position: "ne" or "nw" or "se" or "sw"
+ margin: number of pixels or [x margin, y margin]
+ backgroundColor: null or color
+ backgroundOpacity: number between 0 and 1
+ container: null or jQuery object/DOM element/jQuery expression
+ sorted: null/false, true, "ascending", "descending", "reverse", or a comparator
+}
+```
+
+The legend is generated as a table with the data series labels and
+small label boxes with the color of the series. If you want to format
+the labels in some way, e.g. make them to links, you can pass in a
+function for "labelFormatter". Here's an example that makes them
+clickable:
+
+```js
+labelFormatter: function(label, series) {
+ // series is the series object for the label
+ return '<a href="#' + label + '">' + label + '</a>';
+}
+```
+
+To prevent a series from showing up in the legend, simply have the function
+return null.
+
+"noColumns" is the number of columns to divide the legend table into.
+"position" specifies the overall placement of the legend within the
+plot (top-right, top-left, etc.) and margin the distance to the plot
+edge (this can be either a number or an array of two numbers like [x,
+y]). "backgroundColor" and "backgroundOpacity" specifies the
+background. The default is a partly transparent auto-detected
+background.
+
+If you want the legend to appear somewhere else in the DOM, you can
+specify "container" as a jQuery object/expression to put the legend
+table into. The "position" and "margin" etc. options will then be
+ignored. Note that Flot will overwrite the contents of the container.
+
+Legend entries appear in the same order as their series by default. If "sorted"
+is "reverse" then they appear in the opposite order from their series. To sort
+them alphabetically, you can specify true, "ascending" or "descending", where
+true and "ascending" are equivalent.
+
+You can also provide your own comparator function that accepts two
+objects with "label" and "color" properties, and returns zero if they
+are equal, a positive value if the first is greater than the second,
+and a negative value if the first is less than the second.
+
+```js
+sorted: function(a, b) {
+ // sort alphabetically in ascending order
+ return a.label == b.label ? 0 : (
+ a.label > b.label ? 1 : -1
+ )
+}
+```
+
+
+## Customizing the axes ##
+
+```js
+xaxis, yaxis: {
+ show: null or true/false
+ position: "bottom" or "top" or "left" or "right"
+ mode: null or "time" ("time" requires jquery.flot.time.js plugin)
+ timezone: null, "browser" or timezone (only makes sense for mode: "time")
+
+ color: null or color spec
+ tickColor: null or color spec
+ font: null or font spec object
+
+ min: null or number
+ max: null or number
+ autoscaleMargin: null or number
+
+ transform: null or fn: number -> number
+ inverseTransform: null or fn: number -> number
+
+ ticks: null or number or ticks array or (fn: axis -> ticks array)
+ tickSize: number or array
+ minTickSize: number or array
+ tickFormatter: (fn: number, object -> string) or string
+ tickDecimals: null or number
+
+ labelWidth: null or number
+ labelHeight: null or number
+ reserveSpace: null or true
+
+ tickLength: null or number
+
+ alignTicksWithAxis: null or number
+}
+```
+
+All axes have the same kind of options. The following describes how to
+configure one axis, see below for what to do if you've got more than
+one x axis or y axis.
+
+If you don't set the "show" option (i.e. it is null), visibility is
+auto-detected, i.e. the axis will show up if there's data associated
+with it. You can override this by setting the "show" option to true or
+false.
+
+The "position" option specifies where the axis is placed, bottom or
+top for x axes, left or right for y axes. The "mode" option determines
+how the data is interpreted, the default of null means as decimal
+numbers. Use "time" for time series data; see the time series data
+section. The time plugin (jquery.flot.time.js) is required for time
+series support.
+
+The "color" option determines the color of the line and ticks for the axis, and
+defaults to the grid color with transparency. For more fine-grained control you
+can also set the color of the ticks separately with "tickColor".
+
+You can customize the font and color used to draw the axis tick labels with CSS
+or directly via the "font" option. When "font" is null - the default - each
+tick label is given the 'flot-tick-label' class. For compatibility with Flot
+0.7 and earlier the labels are also given the 'tickLabel' class, but this is
+deprecated and scheduled to be removed with the release of version 1.0.0.
+
+To enable more granular control over styles, labels are divided between a set
+of text containers, with each holding the labels for one axis. These containers
+are given the classes 'flot-[x|y]-axis', and 'flot-[x|y]#-axis', where '#' is
+the number of the axis when there are multiple axes. For example, the x-axis
+labels for a simple plot with only a single x-axis might look like this:
+
+```html
+<div class='flot-x-axis flot-x1-axis'>
+ <div class='flot-tick-label'>January 2013</div>
+ ...
+</div>
+```
+
+For direct control over label styles you can also provide "font" as an object
+with this format:
+
+```js
+{
+ size: 11,
+ lineHeight: 13,
+ style: "italic",
+ weight: "bold",
+ family: "sans-serif",
+ variant: "small-caps",
+ color: "#545454"
+}
+```
+
+The size and lineHeight must be expressed in pixels; CSS units such as 'em'
+or 'smaller' are not allowed.
+
+The options "min"/"max" are the precise minimum/maximum value on the
+scale. If you don't specify either of them, a value will automatically
+be chosen based on the minimum/maximum data values. Note that Flot
+always examines all the data values you feed to it, even if a
+restriction on another axis may make some of them invisible (this
+makes interactive use more stable).
+
+The "autoscaleMargin" is a bit esoteric: it's the fraction of margin
+that the scaling algorithm will add to avoid that the outermost points
+ends up on the grid border. Note that this margin is only applied when
+a min or max value is not explicitly set. If a margin is specified,
+the plot will furthermore extend the axis end-point to the nearest
+whole tick. The default value is "null" for the x axes and 0.02 for y
+axes which seems appropriate for most cases.
+
+"transform" and "inverseTransform" are callbacks you can put in to
+change the way the data is drawn. You can design a function to
+compress or expand certain parts of the axis non-linearly, e.g.
+suppress weekends or compress far away points with a logarithm or some
+other means. When Flot draws the plot, each value is first put through
+the transform function. Here's an example, the x axis can be turned
+into a natural logarithm axis with the following code:
+
+```js
+xaxis: {
+ transform: function (v) { return Math.log(v); },
+ inverseTransform: function (v) { return Math.exp(v); }
+}
+```
+
+Similarly, for reversing the y axis so the values appear in inverse
+order:
+
+```js
+yaxis: {
+ transform: function (v) { return -v; },
+ inverseTransform: function (v) { return -v; }
+}
+```
+
+Note that for finding extrema, Flot assumes that the transform
+function does not reorder values (it should be monotone).
+
+The inverseTransform is simply the inverse of the transform function
+(so v == inverseTransform(transform(v)) for all relevant v). It is
+required for converting from canvas coordinates to data coordinates,
+e.g. for a mouse interaction where a certain pixel is clicked. If you
+don't use any interactive features of Flot, you may not need it.
+
+
+The rest of the options deal with the ticks.
+
+If you don't specify any ticks, a tick generator algorithm will make
+some for you. The algorithm has two passes. It first estimates how
+many ticks would be reasonable and uses this number to compute a nice
+round tick interval size. Then it generates the ticks.
+
+You can specify how many ticks the algorithm aims for by setting
+"ticks" to a number. The algorithm always tries to generate reasonably
+round tick values so even if you ask for three ticks, you might get
+five if that fits better with the rounding. If you don't want any
+ticks at all, set "ticks" to 0 or an empty array.
+
+Another option is to skip the rounding part and directly set the tick
+interval size with "tickSize". If you set it to 2, you'll get ticks at
+2, 4, 6, etc. Alternatively, you can specify that you just don't want
+ticks at a size less than a specific tick size with "minTickSize".
+Note that for time series, the format is an array like [2, "month"],
+see the next section.
+
+If you want to completely override the tick algorithm, you can specify
+an array for "ticks", either like this:
+
+```js
+ticks: [0, 1.2, 2.4]
+```
+
+Or like this where the labels are also customized:
+
+```js
+ticks: [[0, "zero"], [1.2, "one mark"], [2.4, "two marks"]]
+```
+
+You can mix the two if you like.
+
+For extra flexibility you can specify a function as the "ticks"
+parameter. The function will be called with an object with the axis
+min and max and should return a ticks array. Here's a simplistic tick
+generator that spits out intervals of pi, suitable for use on the x
+axis for trigonometric functions:
+
+```js
+function piTickGenerator(axis) {
+ var res = [], i = Math.floor(axis.min / Math.PI);
+ do {
+ var v = i * Math.PI;
+ res.push([v, i + "\u03c0"]);
+ ++i;
+ } while (v < axis.max);
+ return res;
+}
+```
+
+You can control how the ticks look like with "tickDecimals", the
+number of decimals to display (default is auto-detected).
+
+Alternatively, for ultimate control over how ticks are formatted you can
+provide a function to "tickFormatter". The function is passed two
+parameters, the tick value and an axis object with information, and
+should return a string. The default formatter looks like this:
+
+```js
+function formatter(val, axis) {
+ return val.toFixed(axis.tickDecimals);
+}
+```
+
+The axis object has "min" and "max" with the range of the axis,
+"tickDecimals" with the number of decimals to round the value to and
+"tickSize" with the size of the interval between ticks as calculated
+by the automatic axis scaling algorithm (or specified by you). Here's
+an example of a custom formatter:
+
+```js
+function suffixFormatter(val, axis) {
+ if (val > 1000000)
+ return (val / 1000000).toFixed(axis.tickDecimals) + " MB";
+ else if (val > 1000)
+ return (val / 1000).toFixed(axis.tickDecimals) + " kB";
+ else
+ return val.toFixed(axis.tickDecimals) + " B";
+}
+```
+
+"labelWidth" and "labelHeight" specifies a fixed size of the tick
+labels in pixels. They're useful in case you need to align several
+plots. "reserveSpace" means that even if an axis isn't shown, Flot
+should reserve space for it - it is useful in combination with
+labelWidth and labelHeight for aligning multi-axis charts.
+
+"tickLength" is the length of the tick lines in pixels. By default, the
+innermost axes will have ticks that extend all across the plot, while
+any extra axes use small ticks. A value of null means use the default,
+while a number means small ticks of that length - set it to 0 to hide
+the lines completely.
+
+If you set "alignTicksWithAxis" to the number of another axis, e.g.
+alignTicksWithAxis: 1, Flot will ensure that the autogenerated ticks
+of this axis are aligned with the ticks of the other axis. This may
+improve the looks, e.g. if you have one y axis to the left and one to
+the right, because the grid lines will then match the ticks in both
+ends. The trade-off is that the forced ticks won't necessarily be at
+natural places.
+
+
+## Multiple axes ##
+
+If you need more than one x axis or y axis, you need to specify for
+each data series which axis they are to use, as described under the
+format of the data series, e.g. { data: [...], yaxis: 2 } specifies
+that a series should be plotted against the second y axis.
+
+To actually configure that axis, you can't use the xaxis/yaxis options
+directly - instead there are two arrays in the options:
+
+```js
+xaxes: []
+yaxes: []
+```
+
+Here's an example of configuring a single x axis and two y axes (we
+can leave options of the first y axis empty as the defaults are fine):
+
+```js
+{
+ xaxes: [ { position: "top" } ],
+ yaxes: [ { }, { position: "right", min: 20 } ]
+}
+```
+
+The arrays get their default values from the xaxis/yaxis settings, so
+say you want to have all y axes start at zero, you can simply specify
+yaxis: { min: 0 } instead of adding a min parameter to all the axes.
+
+Generally, the various interfaces in Flot dealing with data points
+either accept an xaxis/yaxis parameter to specify which axis number to
+use (starting from 1), or lets you specify the coordinate directly as
+x2/x3/... or x2axis/x3axis/... instead of "x" or "xaxis".
+
+
+## Time series data ##
+
+Please note that it is now required to include the time plugin,
+jquery.flot.time.js, for time series support.
+
+Time series are a bit more difficult than scalar data because
+calendars don't follow a simple base 10 system. For many cases, Flot
+abstracts most of this away, but it can still be a bit difficult to
+get the data into Flot. So we'll first discuss the data format.
+
+The time series support in Flot is based on Javascript timestamps,
+i.e. everywhere a time value is expected or handed over, a Javascript
+timestamp number is used. This is a number, not a Date object. A
+Javascript timestamp is the number of milliseconds since January 1,
+1970 00:00:00 UTC. This is almost the same as Unix timestamps, except it's
+in milliseconds, so remember to multiply by 1000!
+
+You can see a timestamp like this
+
+```js
+alert((new Date()).getTime())
+```
+
+There are different schools of thought when it comes to diplay of
+timestamps. Many will want the timestamps to be displayed according to
+a certain time zone, usually the time zone in which the data has been
+produced. Some want the localized experience, where the timestamps are
+displayed according to the local time of the visitor. Flot supports
+both. Optionally you can include a third-party library to get
+additional timezone support.
+
+Default behavior is that Flot always displays timestamps according to
+UTC. The reason being that the core Javascript Date object does not
+support other fixed time zones. Often your data is at another time
+zone, so it may take a little bit of tweaking to work around this
+limitation.
+
+The easiest way to think about it is to pretend that the data
+production time zone is UTC, even if it isn't. So if you have a
+datapoint at 2002-02-20 08:00, you can generate a timestamp for eight
+o'clock UTC even if it really happened eight o'clock UTC+0200.
+
+In PHP you can get an appropriate timestamp with:
+
+```php
+strtotime("2002-02-20 UTC") * 1000
+```
+
+In Python you can get it with something like:
+
+```python
+calendar.timegm(datetime_object.timetuple()) * 1000
+```
+
+In .NET you can get it with something like:
+
+```aspx
+public static int GetJavascriptTimestamp(System.DateTime input)
+{
+ System.TimeSpan span = new System.TimeSpan(System.DateTime.Parse("1/1/1970").Ticks);
+ System.DateTime time = input.Subtract(span);
+ return (long)(time.Ticks / 10000);
+}
+```
+
+Javascript also has some support for parsing date strings, so it is
+possible to generate the timestamps manually client-side.
+
+If you've already got the real UTC timestamp, it's too late to use the
+pretend trick described above. But you can fix up the timestamps by
+adding the time zone offset, e.g. for UTC+0200 you would add 2 hours
+to the UTC timestamp you got. Then it'll look right on the plot. Most
+programming environments have some means of getting the timezone
+offset for a specific date (note that you need to get the offset for
+each individual timestamp to account for daylight savings).
+
+The alternative with core Javascript is to interpret the timestamps
+according to the time zone that the visitor is in, which means that
+the ticks will shift with the time zone and daylight savings of each
+visitor. This behavior is enabled by setting the axis option
+"timezone" to the value "browser".
+
+If you need more time zone functionality than this, there is still
+another option. If you include the "timezone-js" library
+<https://github.com/mde/timezone-js> in the page and set axis.timezone
+to a value recognized by said library, Flot will use timezone-js to
+interpret the timestamps according to that time zone.
+
+Once you've gotten the timestamps into the data and specified "time"
+as the axis mode, Flot will automatically generate relevant ticks and
+format them. As always, you can tweak the ticks via the "ticks" option
+- just remember that the values should be timestamps (numbers), not
+Date objects.
+
+Tick generation and formatting can also be controlled separately
+through the following axis options:
+
+```js
+minTickSize: array
+timeformat: null or format string
+monthNames: null or array of size 12 of strings
+dayNames: null or array of size 7 of strings
+twelveHourClock: boolean
+```
+
+Here "timeformat" is a format string to use. You might use it like
+this:
+
+```js
+xaxis: {
+ mode: "time",
+ timeformat: "%Y/%m/%d"
+}
+```
+
+This will result in tick labels like "2000/12/24". A subset of the
+standard strftime specifiers are supported (plus the nonstandard %q):
+
+```js
+%a: weekday name (customizable)
+%b: month name (customizable)
+%d: day of month, zero-padded (01-31)
+%e: day of month, space-padded ( 1-31)
+%H: hours, 24-hour time, zero-padded (00-23)
+%I: hours, 12-hour time, zero-padded (01-12)
+%m: month, zero-padded (01-12)
+%M: minutes, zero-padded (00-59)
+%q: quarter (1-4)
+%S: seconds, zero-padded (00-59)
+%y: year (two digits)
+%Y: year (four digits)
+%p: am/pm
+%P: AM/PM (uppercase version of %p)
+%w: weekday as number (0-6, 0 being Sunday)
+```
+
+Flot 0.8 switched from %h to the standard %H hours specifier. The %h specifier
+is still available, for backwards-compatibility, but is deprecated and
+scheduled to be removed permanently with the release of version 1.0.
+
+You can customize the month names with the "monthNames" option. For
+instance, for Danish you might specify:
+
+```js
+monthNames: ["jan", "feb", "mar", "apr", "maj", "jun", "jul", "aug", "sep", "okt", "nov", "dec"]
+```
+
+Similarly you can customize the weekday names with the "dayNames"
+option. An example in French:
+
+```js
+dayNames: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"]
+```
+
+If you set "twelveHourClock" to true, the autogenerated timestamps
+will use 12 hour AM/PM timestamps instead of 24 hour. This only
+applies if you have not set "timeformat". Use the "%I" and "%p" or
+"%P" options if you want to build your own format string with 12-hour
+times.
+
+If the Date object has a strftime property (and it is a function), it
+will be used instead of the built-in formatter. Thus you can include
+a strftime library such as http://hacks.bluesmoon.info/strftime/ for
+more powerful date/time formatting.
+
+If everything else fails, you can control the formatting by specifying
+a custom tick formatter function as usual. Here's a simple example
+which will format December 24 as 24/12:
+
+```js
+tickFormatter: function (val, axis) {
+ var d = new Date(val);
+ return d.getUTCDate() + "/" + (d.getUTCMonth() + 1);
+}
+```
+
+Note that for the time mode "tickSize" and "minTickSize" are a bit
+special in that they are arrays on the form "[value, unit]" where unit
+is one of "second", "minute", "hour", "day", "month" and "year". So
+you can specify
+
+```js
+minTickSize: [1, "month"]
+```
+
+to get a tick interval size of at least 1 month and correspondingly,
+if axis.tickSize is [2, "day"] in the tick formatter, the ticks have
+been produced with two days in-between.
+
+
+## Customizing the data series ##
+
+```js
+series: {
+ lines, points, bars: {
+ show: boolean
+ lineWidth: number
+ fill: boolean or number
+ fillColor: null or color/gradient
+ }
+
+ lines, bars: {
+ zero: boolean
+ }
+
+ points: {
+ radius: number
+ symbol: "circle" or function
+ }
+
+ bars: {
+ barWidth: number
+ align: "left", "right" or "center"
+ horizontal: boolean
+ }
+
+ lines: {
+ steps: boolean
+ }
+
+ shadowSize: number
+ highlightColor: color or number
+}
+
+colors: [ color1, color2, ... ]
+```
+
+The options inside "series: {}" are copied to each of the series. So
+you can specify that all series should have bars by putting it in the
+global options, or override it for individual series by specifying
+bars in a particular the series object in the array of data.
+
+The most important options are "lines", "points" and "bars" that
+specify whether and how lines, points and bars should be shown for
+each data series. In case you don't specify anything at all, Flot will
+default to showing lines (you can turn this off with
+lines: { show: false }). You can specify the various types
+independently of each other, and Flot will happily draw each of them
+in turn (this is probably only useful for lines and points), e.g.
+
+```js
+var options = {
+ series: {
+ lines: { show: true, fill: true, fillColor: "rgba(255, 255, 255, 0.8)" },
+ points: { show: true, fill: false }
+ }
+};
+```
+
+"lineWidth" is the thickness of the line or outline in pixels. You can
+set it to 0 to prevent a line or outline from being drawn; this will
+also hide the shadow.
+
+"fill" is whether the shape should be filled. For lines, this produces
+area graphs. You can use "fillColor" to specify the color of the fill.
+If "fillColor" evaluates to false (default for everything except
+points which are filled with white), the fill color is auto-set to the
+color of the data series. You can adjust the opacity of the fill by
+setting fill to a number between 0 (fully transparent) and 1 (fully
+opaque).
+
+For bars, fillColor can be a gradient, see the gradient documentation
+below. "barWidth" is the width of the bars in units of the x axis (or
+the y axis if "horizontal" is true), contrary to most other measures
+that are specified in pixels. For instance, for time series the unit
+is milliseconds so 24 * 60 * 60 * 1000 produces bars with the width of
+a day. "align" specifies whether a bar should be left-aligned
+(default), right-aligned or centered on top of the value it represents.
+When "horizontal" is on, the bars are drawn horizontally, i.e. from the
+y axis instead of the x axis; note that the bar end points are still
+defined in the same way so you'll probably want to swap the
+coordinates if you've been plotting vertical bars first.
+
+Area and bar charts normally start from zero, regardless of the data's range.
+This is because they convey information through size, and starting from a
+different value would distort their meaning. In cases where the fill is purely
+for decorative purposes, however, "zero" allows you to override this behavior.
+It defaults to true for filled lines and bars; setting it to false tells the
+series to use the same automatic scaling as an un-filled line.
+
+For lines, "steps" specifies whether two adjacent data points are
+connected with a straight (possibly diagonal) line or with first a
+horizontal and then a vertical line. Note that this transforms the
+data by adding extra points.
+
+For points, you can specify the radius and the symbol. The only
+built-in symbol type is circles, for other types you can use a plugin
+or define them yourself by specifying a callback:
+
+```js
+function cross(ctx, x, y, radius, shadow) {
+ var size = radius * Math.sqrt(Math.PI) / 2;
+ ctx.moveTo(x - size, y - size);
+ ctx.lineTo(x + size, y + size);
+ ctx.moveTo(x - size, y + size);
+ ctx.lineTo(x + size, y - size);
+}
+```
+
+The parameters are the drawing context, x and y coordinates of the
+center of the point, a radius which corresponds to what the circle
+would have used and whether the call is to draw a shadow (due to
+limited canvas support, shadows are currently faked through extra
+draws). It's good practice to ensure that the area covered by the
+symbol is the same as for the circle with the given radius, this
+ensures that all symbols have approximately the same visual weight.
+
+"shadowSize" is the default size of shadows in pixels. Set it to 0 to
+remove shadows.
+
+"highlightColor" is the default color of the translucent overlay used
+to highlight the series when the mouse hovers over it.
+
+The "colors" array specifies a default color theme to get colors for
+the data series from. You can specify as many colors as you like, like
+this:
+
+```js
+colors: ["#d18b2c", "#dba255", "#919733"]
+```
+
+If there are more data series than colors, Flot will try to generate
+extra colors by lightening and darkening colors in the theme.
+
+
+## Customizing the grid ##
+
+```js
+grid: {
+ show: boolean
+ aboveData: boolean
+ color: color
+ backgroundColor: color/gradient or null
+ margin: number or margin object
+ labelMargin: number
+ axisMargin: number
+ markings: array of markings or (fn: axes -> array of markings)
+ borderWidth: number or object with "top", "right", "bottom" and "left" properties with different widths
+ borderColor: color or null or object with "top", "right", "bottom" and "left" properties with different colors
+ minBorderMargin: number or null
+ clickable: boolean
+ hoverable: boolean
+ autoHighlight: boolean
+ mouseActiveRadius: number
+}
+
+interaction: {
+ redrawOverlayInterval: number or -1
+}
+```
+
+The grid is the thing with the axes and a number of ticks. Many of the
+things in the grid are configured under the individual axes, but not
+all. "color" is the color of the grid itself whereas "backgroundColor"
+specifies the background color inside the grid area, here null means
+that the background is transparent. You can also set a gradient, see
+the gradient documentation below.
+
+You can turn off the whole grid including tick labels by setting
+"show" to false. "aboveData" determines whether the grid is drawn
+above the data or below (below is default).
+
+"margin" is the space in pixels between the canvas edge and the grid,
+which can be either a number or an object with individual margins for
+each side, in the form:
+
+```js
+margin: {
+ top: top margin in pixels
+ left: left margin in pixels
+ bottom: bottom margin in pixels
+ right: right margin in pixels
+}
+```
+
+"labelMargin" is the space in pixels between tick labels and axis
+line, and "axisMargin" is the space in pixels between axes when there
+are two next to each other.
+
+"borderWidth" is the width of the border around the plot. Set it to 0
+to disable the border. Set it to an object with "top", "right",
+"bottom" and "left" properties to use different widths. You can
+also set "borderColor" if you want the border to have a different color
+than the grid lines. Set it to an object with "top", "right", "bottom"