diff --git a/python/controller.py b/python/controller.py index f293210..88e3e20 100644 --- a/python/controller.py +++ b/python/controller.py @@ -39,7 +39,7 @@ class PuzzleState: class SharedWebToSerial: game_duration: timedelta = timedelta(seconds=60) max_allowed_strikes: int = 3 - seed: int = 0 + seed: int = 1 blocked_modules: list[ModuleAddress] = field(default_factory=list) start_game: bool = False restart_game: bool = False @@ -171,21 +171,23 @@ def serial_controller(serialport, web_to_serial, serial_to_web): @app.route('/status.json') def status(): status_dict = { - 'gamestate': serial_to_web.gamestate.name + 'gamestate': serial_to_web.gamestate.name, + 'server_id': server_id } if serial_to_web.gamestate == Gamestate.GAME: # Send the time left to avoid time syncronisation issues between server and client # Client can then extrapolate if it wants to - status_dict['timeleft'] = (datetime.now() - serial_to_web.game_start).total_seconds() + status_dict['timeleft'] = (web_to_serial.game_duration - (datetime.now() - serial_to_web.game_start)).total_seconds() elif serial_to_web.gamestate == Gamestate.GAMEOVER: status_dict['timeleft'] = (serial_to_web.game_stop - serial_to_web.game_start).total_seconds() if serial_to_web.gamestate in (Gamestate.DISCOVER, Gamestate.GAME, Gamestate.GAMEOVER): status_dict['puzzles'] = [ - {'address': address.to_binary(), 'solved': state.solved if address.is_puzzle() else None, 'strikes': state.strike_amount} + {'address': address.as_binary(), 'solved': state.solved if address.is_puzzle() else None, 'strikes': state.strike_amount} for address, state in serial_to_web.registered_modules.items() ] + print(status_dict) return jsonify(status_dict) @app.route('/start') @@ -211,6 +213,7 @@ if __name__ == '__main__': if len(sys.argv) != 2: print("Usage: python3 controller.py [serial port]") sys.exit() - thread = Thread(target=serial_controller, args=(sys.argv[1], web_to_serial, serial_to_web)) - thread.start() + if sys.argv[1] != 'mock': + thread = Thread(target=serial_controller, args=(sys.argv[1], web_to_serial, serial_to_web)) + thread.start() app.run(debug=False, host='0.0.0.0', port=8080) diff --git a/python/static/controller.html b/python/static/controller.html index f870a08..a0deb60 100644 --- a/python/static/controller.html +++ b/python/static/controller.html @@ -4,11 +4,18 @@
Timer:
+Game state:
+ + + + diff --git a/python/static/controller.js b/python/static/controller.js index 4155f67..3881974 100644 --- a/python/static/controller.js +++ b/python/static/controller.js @@ -1,4 +1,6 @@ - +var estimated_timeout_date; +var display; +var laststate; function startbutton() { fetch('/start'); @@ -7,3 +9,60 @@ function startbutton() { function restartbutton() { fetch('/restart'); } + +function updateDisplay() { + if (laststate != 'GAME' || !estimated_timeout_date) { + return; + } + + setTimeleft((estimated_timeout_date - (new Date())) * 1000); +} + +function setTimeleft(timeleft) { + var integral = Math.floor(timeleft); + var fractional = timeleft - integral; + var minutes = Math.floor(integral / 60); + var seconds = integral % 60; + display.setValue(String(minutes).padStart(2, '0') + ':' + String(seconds).padStart(2, '0') + '.' + String(Math.floor(fractional * 10))); +} + +function state_update() { + // TODO automatically timeout this request after the update interval + fetch('/status.json') + .then( + function(response) { + if (response.status !== 200) { + console.log('FAIL: ' + response.status); + return; + } + response.json().then(function(data) { + document.getElementById('gamestate').innerHTML = data['gamestate']; + laststate = data['gamestate']; + if (data['gamestate'] == 'GAME') { + // TODO maybe smooth this with the previous value of estimated_timeout_date? + var new_estimate = new Date(); + new_estimate.setSeconds(new_estimate.getSeconds() + data['timeleft']); + estimated_timeout_date = new_estimate; + } + }); + } + ) +} + +window.onload = function() { + setInterval(state_update, 500); + display = new SegmentDisplay("display"); + display.pattern = "##:##.#"; + display.displayAngle = 0; + display.digitHeight = 26.5; + display.digitWidth = display.digitHeight / 2; + display.digitDistance = 2.9; + display.segmentWidth = 3; + display.segmentDistance = 0.3; + display.segmentCount = 7; + display.cornerType = 1; + display.colorOn = "#24dd22"; + display.colorOff = "#1b4105"; + display.draw(); + display.draw(); +}; diff --git a/python/static/segment-display.js b/python/static/segment-display.js new file mode 100644 index 0000000..15e7514 --- /dev/null +++ b/python/static/segment-display.js @@ -0,0 +1,555 @@ +/*! + * segment-display.js + * + * Copyright 2012, RĂ¼diger Appel + * http://www.3quarks.com + * Published under Creative Commons 3.0 License. + * + * Date: 2012-02-14 + * Version: 1.0.0 + * + * Dokumentation: http://www.3quarks.com/de/Segmentanzeige + * Documentation: http://www.3quarks.com/en/SegmentDisplay + */ + +// Segment display types +SegmentDisplay.SevenSegment = 7; +SegmentDisplay.FourteenSegment = 14; +SegmentDisplay.SixteenSegment = 16; + +// Segment corner types +SegmentDisplay.SymmetricCorner = 0; +SegmentDisplay.SquaredCorner = 1; +SegmentDisplay.RoundedCorner = 2; + + +function SegmentDisplay(displayId) { + this.displayId = displayId; + this.pattern = '##:##:##'; + this.value = '12:34:56'; + this.digitHeight = 20; + this.digitWidth = 10; + this.digitDistance = 2.5; + this.displayAngle = 12; + this.segmentWidth = 2.5; + this.segmentDistance = 0.2; + this.segmentCount = SegmentDisplay.SevenSegment; + this.cornerType = SegmentDisplay.RoundedCorner; + this.colorOn = 'rgb(233, 93, 15)'; + this.colorOff = 'rgb(75, 30, 5)'; +}; + +SegmentDisplay.prototype.setValue = function(value) { + this.value = value; + this.draw(); +}; + +SegmentDisplay.prototype.draw = function() { + var display = document.getElementById(this.displayId); + if (display) { + var context = display.getContext('2d'); + if (context) { + // clear canvas + context.clearRect(0, 0, display.width, display.height); + + // compute and check display width + var width = 0; + var first = true; + if (this.pattern) { + for (var i = 0; i < this.pattern.length; i++) { + var c = this.pattern.charAt(i).toLowerCase(); + if (c == '#') { + width += this.digitWidth; + } else if (c == '.' || c == ':') { + width += this.segmentWidth; + } else if (c != ' ') { + return; + } + width += first ? 0 : this.digitDistance; + first = false; + } + } + if (width <= 0) { + return; + } + + // compute skew factor + var angle = -1.0 * Math.max(-45.0, Math.min(45.0, this.displayAngle)); + var skew = Math.tan((angle * Math.PI) / 180.0); + + // compute scale factor + var scale = Math.min(display.width / (width + Math.abs(skew * this.digitHeight)), display.height / this.digitHeight); + + // compute display offset + var offsetX = (display.width - (width + skew * this.digitHeight) * scale) / 2.0; + var offsetY = (display.height - this.digitHeight * scale) / 2.0; + + // context transformation + context.save(); + context.translate(offsetX, offsetY); + context.scale(scale, scale); + context.transform(1, 0, skew, 1, 0, 0); + + // draw segments + var xPos = 0; + var size = (this.value) ? this.value.length : 0; + for (var i = 0; i < this.pattern.length; i++) { + var mask = this.pattern.charAt(i); + var value = (i < size) ? this.value.charAt(i).toLowerCase() : ' '; + xPos += this.drawDigit(context, xPos, mask, value); + } + + // finish drawing + context.restore(); + } + } +}; + +SegmentDisplay.prototype.drawDigit = function(context, xPos, mask, c) { + switch (mask) { + case '#': + var r = Math.sqrt(this.segmentWidth * this.segmentWidth / 2.0); + var d = Math.sqrt(this.segmentDistance * this.segmentDistance / 2.0); + var e = d / 2.0; + var f = (this.segmentWidth - d) * Math.sin((45.0 * Math.PI) / 180.0); + var g = f / 2.0; + var h = (this.digitHeight - 3.0 * this.segmentWidth) / 2.0; + var w = (this.digitWidth - 3.0 * this.segmentWidth) / 2.0; + var s = this.segmentWidth / 2.0; + var t = this.digitWidth / 2.0; + + // draw segment a (a1 and a2 for 16 segments) + if (this.segmentCount == 16) { + var x = xPos; + var y = 0; + context.fillStyle = this.getSegmentColor(c, null, '02356789abcdefgiopqrstz@%'); + context.beginPath(); + switch (this.cornerType) { + case SegmentDisplay.SymmetricCorner: + context.moveTo(x + s + d, y + s); + context.lineTo(x + this.segmentWidth + d, y); + break; + case SegmentDisplay.SquaredCorner: + context.moveTo(x + s + e, y + s - e); + context.lineTo(x + this.segmentWidth, y); + break; + default: + context.moveTo(x + this.segmentWidth - f, y + this.segmentWidth - f - d); + context.quadraticCurveTo(x + this.segmentWidth - g, y, x + this.segmentWidth, y); + } + context.lineTo(x + t - d - s, y); + context.lineTo(x + t - d, y + s); + context.lineTo(x + t - d - s, y + this.segmentWidth); + context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); + context.fill(); + + var x = xPos; + var y = 0; + context.fillStyle = this.getSegmentColor(c, null, '02356789abcdefgiopqrstz@'); + context.beginPath(); + context.moveTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); + context.lineTo(x + t + d + s, y + this.segmentWidth); + context.lineTo(x + t + d, y + s); + context.lineTo(x + t + d + s, y); + switch (this.cornerType) { + case SegmentDisplay.SymmetricCorner: + context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); + context.lineTo(x + this.digitWidth - s - d, y + s); + break; + case SegmentDisplay.SquaredCorner: + context.lineTo(x + this.digitWidth - this.segmentWidth, y); + context.lineTo(x + this.digitWidth - s - e, y + s - e); + break; + default: + context.lineTo(x + this.digitWidth - this.segmentWidth, y); + context.quadraticCurveTo(x + this.digitWidth - this.segmentWidth + g, y, x + this.digitWidth - this.segmentWidth + f, y + this.segmentWidth - f - d); + } + context.fill(); + + } else { + var x = xPos; + var y = 0; + context.fillStyle = this.getSegmentColor(c, '02356789acefp', '02356789abcdefgiopqrstz@'); + context.beginPath(); + switch (this.cornerType) { + case SegmentDisplay.SymmetricCorner: + context.moveTo(x + s + d, y + s); + context.lineTo(x + this.segmentWidth + d, y); + context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); + context.lineTo(x + this.digitWidth - s - d, y + s); + break; + case SegmentDisplay.SquaredCorner: + context.moveTo(x + s + e, y + s - e); + context.lineTo(x + this.segmentWidth, y); + context.lineTo(x + this.digitWidth - this.segmentWidth, y); + context.lineTo(x + this.digitWidth - s - e, y + s - e); + break; + default: + context.moveTo(x + this.segmentWidth - f, y + this.segmentWidth - f - d); + context.quadraticCurveTo(x + this.segmentWidth - g, y, x + this.segmentWidth, y); + context.lineTo(x + this.digitWidth - this.segmentWidth, y); + context.quadraticCurveTo(x + this.digitWidth - this.segmentWidth + g, y, x + this.digitWidth - this.segmentWidth + f, y + this.segmentWidth - f - d); + } + context.lineTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); + context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); + context.fill(); + } + + // draw segment b + x = xPos + this.digitWidth - this.segmentWidth; + y = 0; + context.fillStyle = this.getSegmentColor(c, '01234789adhpy', '01234789abdhjmnopqruwy'); + context.beginPath(); + switch (this.cornerType) { + case SegmentDisplay.SymmetricCorner: + context.moveTo(x + s, y + s + d); + context.lineTo(x + this.segmentWidth, y + this.segmentWidth + d); + break; + case SegmentDisplay.SquaredCorner: + context.moveTo(x + s + e, y + s + e); + context.lineTo(x + this.segmentWidth, y + this.segmentWidth); + break; + default: + context.moveTo(x + f + d, y + this.segmentWidth - f); + context.quadraticCurveTo(x + this.segmentWidth, y + this.segmentWidth - g, x + this.segmentWidth, y + this.segmentWidth); + } + context.lineTo(x + this.segmentWidth, y + h + this.segmentWidth - d); + context.lineTo(x + s, y + h + this.segmentWidth + s - d); + context.lineTo(x, y + h + this.segmentWidth - d); + context.lineTo(x, y + this.segmentWidth + d); + context.fill(); + + // draw segment c + x = xPos + this.digitWidth - this.segmentWidth; + y = h + this.segmentWidth; + context.fillStyle = this.getSegmentColor(c, '013456789abdhnouy', '01346789abdghjmnoqsuw@', '%'); + context.beginPath(); + context.moveTo(x, y + this.segmentWidth + d); + context.lineTo(x + s, y + s + d); + context.lineTo(x + this.segmentWidth, y + this.segmentWidth + d); + context.lineTo(x + this.segmentWidth, y + h + this.segmentWidth - d); + switch (this.cornerType) { + case SegmentDisplay.SymmetricCorner: + context.lineTo(x + s, y + h + this.segmentWidth + s - d); + context.lineTo(x, y + h + this.segmentWidth - d); + break; + case SegmentDisplay.SquaredCorner: + context.lineTo(x + s + e, y + h + this.segmentWidth + s - e); + context.lineTo(x, y + h + this.segmentWidth - d); + break; + default: + context.quadraticCurveTo(x + this.segmentWidth, y + h + this.segmentWidth + g, x + f + d, y + h + this.segmentWidth + f); // + context.lineTo(x, y + h + this.segmentWidth - d); + } + context.fill(); + + // draw segment d (d1 and d2 for 16 segments) + if (this.segmentCount == 16) { + x = xPos; + y = this.digitHeight - this.segmentWidth; + context.fillStyle = this.getSegmentColor(c, null, '0235689bcdegijloqsuz_=@'); + context.beginPath(); + context.moveTo(x + this.segmentWidth + d, y); + context.lineTo(x + t - d - s, y); + context.lineTo(x + t - d, y + s); + context.lineTo(x + t - d - s, y + this.segmentWidth); + switch (this.cornerType) { + case SegmentDisplay.SymmetricCorner: + context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); + context.lineTo(x + s + d, y + s); + break; + case SegmentDisplay.SquaredCorner: + context.lineTo(x + this.segmentWidth, y + this.segmentWidth); + context.lineTo(x + s + e, y + s + e); + break; + default: + context.lineTo(x + this.segmentWidth, y + this.segmentWidth); + context.quadraticCurveTo(x + this.segmentWidth - g, y + this.segmentWidth, x + this.segmentWidth - f, y + f + d); + context.lineTo(x + this.segmentWidth - f, y + f + d); + } + context.fill(); + + x = xPos; + y = this.digitHeight - this.segmentWidth; + context.fillStyle = this.getSegmentColor(c, null, '0235689bcdegijloqsuz_=@', '%'); + context.beginPath(); + context.moveTo(x + t + d + s, y + this.segmentWidth); + context.lineTo(x + t + d, y + s); + context.lineTo(x + t + d + s, y); + context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); + switch (this.cornerType) { + case SegmentDisplay.SymmetricCorner: + context.lineTo(x + this.digitWidth - s - d, y + s); + context.lineTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); + break; + case SegmentDisplay.SquaredCorner: + context.lineTo(x + this.digitWidth - s - e, y + s + e); + context.lineTo(x + this.digitWidth - this.segmentWidth, y + this.segmentWidth); + break; + default: + context.lineTo(x + this.digitWidth - this.segmentWidth + f, y + f + d); + context.quadraticCurveTo(x + this.digitWidth - this.segmentWidth + g, y + this.segmentWidth, x + this.digitWidth - this.segmentWidth, y + this.segmentWidth); + } + context.fill(); + } + else { + x = xPos; + y = this.digitHeight - this.segmentWidth; + context.fillStyle = this.getSegmentColor(c, '0235689bcdelotuy_', '0235689bcdegijloqsuz_=@'); + context.beginPath(); + context.moveTo(x + this.segmentWidth + d, y); + context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); + switch (this.cornerType) { + case SegmentDisplay.SymmetricCorner: + context.lineTo(x + this.digitWidth - s - d, y + s); + context.lineTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); + context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); + context.lineTo(x + s + d, y + s); + break; + case SegmentDisplay.SquaredCorner: + context.lineTo(x + this.digitWidth - s - e, y + s + e); + context.lineTo(x + this.digitWidth - this.segmentWidth, y + this.segmentWidth); + context.lineTo(x + this.segmentWidth, y + this.segmentWidth); + context.lineTo(x + s + e, y + s + e); + break; + default: + context.lineTo(x + this.digitWidth - this.segmentWidth + f, y + f + d); + context.quadraticCurveTo(x + this.digitWidth - this.segmentWidth + g, y + this.segmentWidth, x + this.digitWidth - this.segmentWidth, y + this.segmentWidth); + context.lineTo(x + this.segmentWidth, y + this.segmentWidth); + context.quadraticCurveTo(x + this.segmentWidth - g, y + this.segmentWidth, x + this.segmentWidth - f, y + f + d); + context.lineTo(x + this.segmentWidth - f, y + f + d); + } + context.fill(); + } + + // draw segment e + x = xPos; + y = h + this.segmentWidth; + context.fillStyle = this.getSegmentColor(c, '0268abcdefhlnoprtu', '0268acefghjklmnopqruvw@'); + context.beginPath(); + context.moveTo(x, y + this.segmentWidth + d); + context.lineTo(x + s, y + s + d); + context.lineTo(x + this.segmentWidth, y + this.segmentWidth + d); + context.lineTo(x + this.segmentWidth, y + h + this.segmentWidth - d); + switch (this.cornerType) { + case SegmentDisplay.SymmetricCorner: + context.lineTo(x + s, y + h + this.segmentWidth + s - d); + context.lineTo(x, y + h + this.segmentWidth - d); + break; + case SegmentDisplay.SquaredCorner: + context.lineTo(x + s - e, y + h + this.segmentWidth + s - d + e); + context.lineTo(x, y + h + this.segmentWidth); + break; + default: + context.lineTo(x + this.segmentWidth - f - d, y + h + this.segmentWidth + f); + context.quadraticCurveTo(x, y + h + this.segmentWidth + g, x, y + h + this.segmentWidth); + } + context.fill(); + + // draw segment f + x = xPos; + y = 0; + context.fillStyle = this.getSegmentColor(c, '045689abcefhlpty', '045689acefghklmnopqrsuvwy@', '%'); + context.beginPath(); + context.moveTo(x + this.segmentWidth, y + this.segmentWidth + d); + context.lineTo(x + this.segmentWidth, y + h + this.segmentWidth - d); + context.lineTo(x + s, y + h + this.segmentWidth + s - d); + context.lineTo(x, y + h + this.segmentWidth - d); + switch (this.cornerType) { + case SegmentDisplay.SymmetricCorner: + context.lineTo(x, y + this.segmentWidth + d); + context.lineTo(x + s, y + s + d); + break; + case SegmentDisplay.SquaredCorner: + context.lineTo(x, y + this.segmentWidth); + context.lineTo(x + s - e, y + s + e); + break; + default: + context.lineTo(x, y + this.segmentWidth); + context.quadraticCurveTo(x, y + this.segmentWidth - g, x + this.segmentWidth - f - d, y + this.segmentWidth - f); + context.lineTo(x + this.segmentWidth - f - d, y + this.segmentWidth - f); + } + context.fill(); + + // draw segment g for 7 segments + if (this.segmentCount == 7) { + x = xPos; + y = (this.digitHeight - this.segmentWidth) / 2.0; + context.fillStyle = this.getSegmentColor(c, '2345689abdefhnoprty-='); + context.beginPath(); + context.moveTo(x + s + d, y + s); + context.lineTo(x + this.segmentWidth + d, y); + context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); + context.lineTo(x + this.digitWidth - s - d, y + s); + context.lineTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); + context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); + context.fill(); + } + + // draw inner segments for the fourteen- and sixteen-segment-display + if (this.segmentCount != 7) { + // draw segment g1 + x = xPos; + y = (this.digitHeight - this.segmentWidth) / 2.0; + context.fillStyle = this.getSegmentColor(c, null, '2345689aefhkprsy-+*=', '%'); + context.beginPath(); + context.moveTo(x + s + d, y + s); + context.lineTo(x + this.segmentWidth + d, y); + context.lineTo(x + t - d - s, y); + context.lineTo(x + t - d, y + s); + context.lineTo(x + t - d - s, y + this.segmentWidth); + context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); + context.fill(); + + // draw segment g2 + x = xPos; + y = (this.digitHeight - this.segmentWidth) / 2.0; + context.fillStyle = this.getSegmentColor(c, null, '234689abefghprsy-+*=@', '%'); + context.beginPath(); + context.moveTo(x + t + d, y + s); + context.lineTo(x + t + d + s, y); + context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); + context.lineTo(x + this.digitWidth - s - d, y + s); + context.lineTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); + context.lineTo(x + t + d + s, y + this.segmentWidth); + context.fill(); + + // draw segment j + x = xPos + t - s; + y = 0; + context.fillStyle = this.getSegmentColor(c, null, 'bdit+*', '%'); + context.beginPath(); + if (this.segmentCount == 14) { + context.moveTo(x, y + this.segmentWidth + this.segmentDistance); + context.lineTo(x + this.segmentWidth, y + this.segmentWidth + this.segmentDistance); + } else { + context.moveTo(x, y + this.segmentWidth + d); + context.lineTo(x + s, y + s + d); + context.lineTo(x + this.segmentWidth, y + this.segmentWidth + d); + } + context.lineTo(x + this.segmentWidth, y + h + this.segmentWidth - d); + context.lineTo(x + s, y + h + this.segmentWidth + s - d); + context.lineTo(x, y + h + this.segmentWidth - d); + context.fill(); + + // draw segment m + x = xPos + t - s; + y = this.digitHeight; + context.fillStyle = this.getSegmentColor(c, null, 'bdity+*@', '%'); + context.beginPath(); + if (this.segmentCount == 14) { + context.moveTo(x, y - this.segmentWidth - this.segmentDistance); + context.lineTo(x + this.segmentWidth, y - this.segmentWidth - this.segmentDistance); + } else { + context.moveTo(x, y - this.segmentWidth - d); + context.lineTo(x + s, y - s - d); + context.lineTo(x + this.segmentWidth, y - this.segmentWidth - d); + } + context.lineTo(x + this.segmentWidth, y - h - this.segmentWidth + d); + context.lineTo(x + s, y - h - this.segmentWidth - s + d); + context.lineTo(x, y - h - this.segmentWidth + d); + context.fill(); + + // draw segment h + x = xPos + this.segmentWidth; + y = this.segmentWidth; + context.fillStyle = this.getSegmentColor(c, null, 'mnx\\*'); + context.beginPath(); + context.moveTo(x + this.segmentDistance, y + this.segmentDistance); + context.lineTo(x + this.segmentDistance + r, y + this.segmentDistance); + context.lineTo(x + w - this.segmentDistance , y + h - this.segmentDistance - r); + context.lineTo(x + w - this.segmentDistance , y + h - this.segmentDistance); + context.lineTo(x + w - this.segmentDistance - r , y + h - this.segmentDistance); + context.lineTo(x + this.segmentDistance, y + this.segmentDistance + r); + context.fill(); + + // draw segment k + x = xPos + w + 2.0 * this.segmentWidth; + y = this.segmentWidth; + context.fillStyle = this.getSegmentColor(c, null, '0kmvxz/*', '%'); + context.beginPath(); + context.moveTo(x + w - this.segmentDistance, y + this.segmentDistance); + context.lineTo(x + w - this.segmentDistance, y + this.segmentDistance + r); + context.lineTo(x + this.segmentDistance + r, y + h - this.segmentDistance); + context.lineTo(x + this.segmentDistance, y + h - this.segmentDistance); + context.lineTo(x + this.segmentDistance, y + h - this.segmentDistance - r); + context.lineTo(x + w - this.segmentDistance - r, y + this.segmentDistance); + context.fill(); + + // draw segment l + x = xPos + w + 2.0 * this.segmentWidth; + y = h + 2.0 * this.segmentWidth; + context.fillStyle = this.getSegmentColor(c, null, '5knqrwx\\*'); + context.beginPath(); + context.moveTo(x + this.segmentDistance, y + this.segmentDistance); + context.lineTo(x + this.segmentDistance + r, y + this.segmentDistance); + context.lineTo(x + w - this.segmentDistance , y + h - this.segmentDistance - r); + context.lineTo(x + w - this.segmentDistance , y + h - this.segmentDistance); + context.lineTo(x + w - this.segmentDistance - r , y + h - this.segmentDistance); + context.lineTo(x + this.segmentDistance, y + this.segmentDistance + r); + context.fill(); + + // draw segment n + x = xPos + this.segmentWidth; + y = h + 2.0 * this.segmentWidth; + context.fillStyle = this.getSegmentColor(c, null, '0vwxz/*', '%'); + context.beginPath(); + context.moveTo(x + w - this.segmentDistance, y + this.segmentDistance); + context.lineTo(x + w - this.segmentDistance, y + this.segmentDistance + r); + context.lineTo(x + this.segmentDistance + r, y + h - this.segmentDistance); + context.lineTo(x + this.segmentDistance, y + h - this.segmentDistance); + context.lineTo(x + this.segmentDistance, y + h - this.segmentDistance - r); + context.lineTo(x + w - this.segmentDistance - r, y + this.segmentDistance); + context.fill(); + } + + return this.digitDistance + this.digitWidth; + + case '.': + context.fillStyle = (c == '#') || (c == '.') ? this.colorOn : this.colorOff; + this.drawPoint(context, xPos, this.digitHeight - this.segmentWidth, this.segmentWidth); + return this.digitDistance + this.segmentWidth; + + case ':': + context.fillStyle = (c == '#') || (c == ':') ? this.colorOn : this.colorOff; + var offset = this.segmentWidth / 2; + var y = (this.digitHeight - this.segmentWidth) / 2.0 - this.segmentWidth; + this.drawPoint(context, xPos, y - offset, this.segmentWidth); + this.drawPoint(context, xPos, y + 2.0 * this.segmentWidth + offset, this.segmentWidth); + return this.digitDistance + this.segmentWidth; + + default: + return this.digitDistance; + } +}; + +SegmentDisplay.prototype.drawPoint = function(context, x1, y1, size) { + var x2 = x1 + size; + var y2 = y1 + size; + var d = size / 4.0; + + context.beginPath(); + context.moveTo(x2 - d, y1); + context.quadraticCurveTo(x2, y1, x2, y1 + d); + context.lineTo(x2, y2 - d); + context.quadraticCurveTo(x2, y2, x2 - d, y2); + context.lineTo(x1 + d, y2); + context.quadraticCurveTo(x1, y2, x1, y2 - d); + context.lineTo(x1, y1 + d); + context.quadraticCurveTo(x1, y1, x1 + d, y1); + context.fill(); +}; + +SegmentDisplay.prototype.getSegmentColor = function(c, charSet7, charSet14, charSet16) { + if (c == '#') { + return this.colorOn; + } else { + switch (this.segmentCount) { + case 7: return (charSet7.indexOf(c) == -1) ? this.colorOff : this.colorOn; + case 14: return (charSet14.indexOf(c) == -1) ? this.colorOff : this.colorOn; + case 16: var pattern = charSet14 + (charSet16 === undefined ? '' : charSet16); + return (pattern.indexOf(c) == -1) ? this.colorOff : this.colorOn; + default: return this.colorOff; + } + } +};