forked from aimacode/aima-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added TicTacToe to canvas (aimacode#174)
* added kernel test notebook * modified kernel.ipynb * Added canvas support * Added mouseclick to canvas * add TicTacToe to canvas * removed import statement in games.py * corrected HTML tags * Added comments and doctring * Added methods for drawing with normalized units * Added text support * Fixed int type during normalization
- Loading branch information
Showing
4 changed files
with
641 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* | ||
JavaScript functions that are executed by running the corresponding methods of a Canvas object | ||
Donot use these functions by making a js file. Instead use the python Canvas class. | ||
See canvas.py for help on how to use the Canvas class to draw on the HTML Canvas | ||
*/ | ||
|
||
//Manages the output of code executed in IPython kernel | ||
function output_callback(out, block){ | ||
console.log(out); | ||
script = out.content.data['text/html']; | ||
console.log(script); | ||
script = script.substr(8, script.length - 17); | ||
console.log(script); | ||
eval(script) | ||
} | ||
|
||
//Handles mouse click by calling mouse_click of Canvas object with the co-ordinates as arguments | ||
function click_callback(element, event, varname){ | ||
var rect = element.getBoundingClientRect(); | ||
var x = event.clientX - rect.left; | ||
var y = event.clientY - rect.top; | ||
var kernel = IPython.notebook.kernel; | ||
var exec_str = varname + ".mouse_click(" + String(x) + ", " + String(y) + ")"; | ||
console.log(exec_str); | ||
kernel.execute(exec_str,{'iopub': {'output': output_callback}}, {silent: false}); | ||
} | ||
|
||
function rgbToHex(r,g,b){ | ||
var hexValue=(r<<16) + (g<<8) + (b<<0); | ||
var hexString=hexValue.toString(16); | ||
hexString ='#' + Array(7-hexString.length).join('0') + hexString; //Add 0 padding | ||
return hexString; | ||
} | ||
|
||
function toRad(x){ | ||
return x*Math.PI/180; | ||
} | ||
|
||
//Canvas class to store variables | ||
function Canvas(id){ | ||
this.canvas = document.getElementById(id); | ||
this.ctx = this.canvas.getContext("2d"); | ||
this.WIDTH = this.canvas.width; | ||
this.HEIGHT = this.canvas.height; | ||
this.MOUSE = {x:0,y:0}; | ||
} | ||
|
||
//Sets the fill color with which shapes are filled | ||
Canvas.prototype.fill = function(r, g, b){ | ||
this.ctx.fillStyle = rgbToHex(r,g,b); | ||
} | ||
|
||
//Set the stroke color | ||
Canvas.prototype.stroke = function(r, g, b){ | ||
this.ctx.strokeStyle = rgbToHex(r,g,b); | ||
} | ||
|
||
//Set width of the lines/strokes | ||
Canvas.prototype.strokeWidth = function(w){ | ||
this.ctx.lineWidth = w; | ||
} | ||
|
||
//Draw a rectangle with top left at (x,y) with 'w' width and 'h' height | ||
Canvas.prototype.rect = function(x, y, w, h){ | ||
this.ctx.fillRect(x,y,w,h); | ||
} | ||
|
||
//Draw a line with (x1, y1) and (x2, y2) as end points | ||
Canvas.prototype.line = function(x1, y1, x2, y2){ | ||
this.ctx.beginPath(); | ||
this.ctx.moveTo(x1, y1); | ||
this.ctx.lineTo(x2, y2); | ||
this.ctx.stroke(); | ||
} | ||
|
||
//Draw an arc with (x, y) as centre, 'r' as radius from angles start to stop | ||
Canvas.prototype.arc = function(x, y, r, start, stop){ | ||
this.ctx.beginPath(); | ||
this.ctx.arc(x, y, r, toRad(start), toRad(stop)); | ||
this.ctx.stroke(); | ||
} | ||
|
||
//Clear the HTML canvas | ||
Canvas.prototype.clear = function(){ | ||
this.ctx.clearRect(0, 0, this.WIDTH, this.HEIGHT); | ||
} | ||
|
||
//Change font, size and style | ||
Canvas.prototype.font = function(font_str){ | ||
this.ctx.font = font_str; | ||
} | ||
|
||
//Draws "filled" text on the canvas | ||
Canvas.prototype.fill_text = function(text, x, y){ | ||
this.ctx.fillText(text, x, y); | ||
} | ||
|
||
//Write text on the canvas | ||
Canvas.prototype.stroke_text = function(text, x, y){ | ||
this.ctx.strokeText(text, x, y); | ||
} | ||
|
||
|
||
//Test if the canvas functions are working | ||
Canvas.prototype.test_run = function(){ | ||
var dbg = false; | ||
if(dbg) | ||
alert("1"); | ||
this.clear(); | ||
if(dbg) | ||
alert("2"); | ||
this.fill(0, 200, 0); | ||
if(dbg) | ||
alert("3"); | ||
this.rect(this.MOUSE.x, this.MOUSE.y, 100, 200); | ||
if(dbg) | ||
alert("4"); | ||
this.stroke(0, 0, 50); | ||
if(dbg) | ||
alert("5"); | ||
this.line(0, 0, 100, 100); | ||
if(dbg) | ||
alert("6"); | ||
this.stroke(200, 200, 200); | ||
if(dbg) | ||
alert("7"); | ||
this.arc(200, 100, 50, 0, 360); | ||
if(dbg) | ||
alert("8"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
from IPython.display import HTML, display, clear_output | ||
|
||
_canvas = """ | ||
<script type="text/javascript" src="./canvas.js"></script> | ||
<div> | ||
<canvas id="{0}" width="{1}" height="{2}" style="background:rgba(158, 167, 184, 0.2);" onclick='click_callback(this, event, "{3}")'></canvas> | ||
</div> | ||
<script> var {0}_canvas_object = new Canvas("{0}");</script> | ||
""" | ||
|
||
class Canvas: | ||
"""Inherit from this class to manage the HTML canvas element in jupyter notebooks. | ||
To create an object of this class any_name_xyz = Canvas("any_name_xyz") | ||
The first argument given must be the name of the object being create | ||
IPython must be able to refernce the variable name that is being passed | ||
""" | ||
|
||
def __init__(self, varname, id=None, width=800, height=600): | ||
"""""" | ||
self.name = varname | ||
self.id = id or varname | ||
self.width = width | ||
self.height = height | ||
self.html = _canvas.format(self.id, self.width, self.height, self.name) | ||
self.exec_list = [] | ||
display(HTML(self.html)) | ||
|
||
def mouse_click(self, x, y): | ||
"Override this method to handle mouse click at position (x, y)" | ||
raise NotImplementedError | ||
|
||
def mouse_move(self, x, y): | ||
raise NotImplementedError | ||
|
||
def exec(self, exec_str): | ||
"Stores the command to be exectued to a list which is used later during update()" | ||
if not isinstance(exec_str, str): | ||
print("Invalid execution argument:",exec_str) | ||
self.alert("Recieved invalid execution command format") | ||
prefix = "{0}_canvas_object.".format(self.id) | ||
self.exec_list.append(prefix + exec_str + ';') | ||
|
||
def fill(self, r, g, b): | ||
"Changes the fill color to a color in rgb format" | ||
self.exec("fill({0}, {1}, {2})".format(r, g, b)) | ||
|
||
def stroke(self, r, g, b): | ||
"Changes the colors of line/strokes to rgb" | ||
self.exec("stroke({0}, {1}, {2})".format(r, g, b)) | ||
|
||
def strokeWidth(self, w): | ||
"Changes the width of lines/strokes to 'w' pixels" | ||
self.exec("strokeWidth({0})".format(w)) | ||
|
||
def rect(self, x, y, w, h): | ||
"Draw a rectangle with 'w' width, 'h' height and (x, y) as the top-left corner" | ||
self.exec("rect({0}, {1}, {2}, {3})".format(x, y, w, h)) | ||
|
||
def rect_n(self, xn, yn, wn, hn): | ||
"Similar to rect(), but the dimensions are normalized to fall between 0 and 1" | ||
x = round(xn * self.width) | ||
y = round(yn * self.height) | ||
w = round(wn * self.width) | ||
h = round(hn * self.height) | ||
self.rect(x, y, w, h) | ||
|
||
def line(self, x1, y1, x2, y2): | ||
"Draw a line from (x1, y1) to (x, y2)" | ||
self.exec("line({0}, {1}, {2}, {3})".format(x1, y1, x2, y2)) | ||
|
||
def line_n(self, x1n, y1n, x2n, y2n): | ||
"Similar to line(), but the dimensions are normalized to fall between 0 and 1" | ||
x1 = round(x1n * self.width) | ||
y1 = round(y1n * self.height) | ||
x2 = round(x2n * self.width) | ||
y2 = round(y2n * self.height) | ||
self.line(x1, y1, x2, y2) | ||
|
||
def arc(self, x, y, r, start, stop): | ||
"Draw an arc with (x, y) as centre, 'r' as radius from angles 'start' to 'stop'" | ||
self.exec("arc({0}, {1}, {2}, {3}, {4})".format(x, y, r, start, stop)) | ||
|
||
def arc_n(self, xn ,yn, rn, start, stop): | ||
"""Similar to arc(), but the dimensions are normalized to fall between 0 and 1 | ||
The normalizing factor for radius is selected between width and height by seeing which is smaller | ||
""" | ||
x = round(xn * self.width) | ||
y = round(yn * self.height) | ||
r = round(rn * min(self.width, self.height)) | ||
self.arc(x, y, r, start, stop) | ||
|
||
def clear(self): | ||
"Clear the HTML canvas" | ||
self.exec("clear()") | ||
|
||
def font(self, font): | ||
"Changes the font of text" | ||
self.exec('font("{0}")'.format(font)) | ||
|
||
def text(self, txt, x, y, fill = True): | ||
"Display a text at (x, y)" | ||
if fill: | ||
self.exec('fill_text("{0}", {1}, {2})'.format(txt, x, y)) | ||
else: | ||
self.exec('stroke_text("{0}", {1}, {2})'.format(txt, x, y)) | ||
|
||
def text_n(self, txt, xn, yn, fill = True): | ||
"Similar to text(), but with normalized coordinates" | ||
x = round(xn * self.width) | ||
y = round(yn * self.height) | ||
self.text(text, x, y, fill) | ||
|
||
def alert(self, message): | ||
"Immediately display an alert" | ||
display(HTML('<script>alert("{0}")</script>'.format(message))) | ||
|
||
def update(self): | ||
"Execute the JS code to execute the commands queued by exec()" | ||
exec_code = "<script>\n"+'\n'.join(self.exec_list)+"\n</script>" | ||
self.exec_list = [] | ||
display(HTML(exec_code)) |
Oops, something went wrong.