Need a way to draw on Javascript canvas straightforwardly.
Simply create a canvas object and write to it.
#canvas code via https://jsfiddle.net/macloo/JLVva/
class Canvas0(object):
def __init__(self,cc):
self.canvas_code=cc
def _repr_html_(self):
return self.canvas_code
txt='''<canvas id="myCanvas0" width="700" height="410">
<p>Some default content can appear here.</p>
</canvas>
<p>Triangles!</p>
<script>function draw() {
// canvas with id="myCanvas"
var canvas = document.getElementById('myCanvas0');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.beginPath(); // note usage below
// triangle 1, at left
ctx.fillStyle = "#F9A520";
ctx.moveTo(0, 0); // start at top left corner of canvas
ctx.lineTo(200, 0); // go 200px to right (x), straight line from 0 to 0
ctx.lineTo(100, 200); // go to horizontal 100 (x) and vertical 200 (y)
ctx.fill(); // connect and fill
// triangle 2, top center
ctx.moveTo(300, 0); // pick up "pen," reposition at 300 (horiz), 0 (vert)
ctx.lineTo(300, 200); // draw straight down (from 300,0) to 200px
ctx.lineTo(500, 100); // draw up toward right (100 half of 200)
ctx.fill(); // connect and fill
// triangle 3, bottom center
ctx.beginPath(); // note: w/o this, color does not work as expected
ctx.fillStyle = "#F00";
ctx.moveTo(300, 200); // pick up "pen," reposition at 300 (horiz), 200 (vert)
ctx.lineTo(300, 400); // draw straight down by 200px (200 + 200)
ctx.lineTo(100, 300); // draw up toward left (100 less than 300, so left)
ctx.fill(); // connect and fill
// triangle 4, at right, top
ctx.beginPath();
ctx.strokeStyle = "#00F";
ctx.moveTo(600, 0); // pick up "pen," reposition at 500 (horiz), 0 (vert)
ctx.lineTo(500, 200); // draw straight down by 200px (200 + 200)
ctx.lineTo(700, 200); // draw up toward left (100 less than 300, so left)
ctx.closePath(); // connect end to start
ctx.stroke(); // outline the shape that's been described
// triangle 5, at right, bottom
ctx.beginPath();
ctx.strokeStyle = "#096";
ctx.moveTo(500, 210); // pick up "pen," reposition
ctx.lineTo(700, 210); // draw straight across to right
ctx.lineTo(600, 410); // draw down toward left
ctx.closePath(); // connect end to start
ctx.stroke(); // outline the shape that's been described
}
}
draw();</script>'''
Canvas0(txt)
Triangles!
What methods can we sensibly extract?
class Canvas1(object):
def __init__(self,script,cid='mytag', context=None):
self.cid=cid
self.context = context if context else 'ctx'
self.wrap = context is None
self._html =[]
self._add_canvas(cid)
self._get_canvas()
self._add_script_tag(script)
def _add_canvas(self, cid):
html='''<canvas id="{}" width="700" height="410"> <p>Some default content can appear here.</p>
</canvas>'''.format(cid)
self._html.append(html)
def _get_canvas(self):
script = '''<script type="text/javascript">var canvas = document.getElementById('{cid}');
//if (canvas.getContext) {{}}
var {ctx} = canvas.getContext('2d');</script> '''.format(cid = self.cid, ctx=self.context)
self._html.append(script)
def _add_script_tag(self, script):
if self.wrap:
self._html.append('<script type="text/javascript">with ({}) {{ {} }}</script>'.format(self.context,script))
else:
self._html.append('<script type="text/javascript">{}</script>'.format(script))
def html(self):
return '\n'.join(self._html)
def _repr_html_(self):
return self.html()
txt='''
ctx.beginPath(); // note usage below
// triangle 1, at left
ctx.fillStyle = "#F9A520";
ctx.moveTo(0, 0); // start at top left corner of canvas
ctx.lineTo(200, 0); // go 200px to right (x), straight line from 0 to 0
ctx.lineTo(100, 200); // go to horizontal 100 (x) and vertical 200 (y)
ctx.fill(); // connect and fill
// triangle 2, top center
ctx.moveTo(300, 0); // pick up "pen," reposition at 300 (horiz), 0 (vert)
ctx.lineTo(300, 200); // draw straight down (from 300,0) to 200px
ctx.lineTo(500, 100); // draw up toward right (100 half of 200)
ctx.fill(); // connect and fill
// triangle 3, bottom center
ctx.beginPath(); // note: w/o this, color does not work as expected
ctx.fillStyle = "#F00";
ctx.moveTo(300, 200); // pick up "pen," reposition at 300 (horiz), 200 (vert)
ctx.lineTo(300, 400); // draw straight down by 200px (200 + 200)
ctx.lineTo(100, 300); // draw up toward left (100 less than 300, so left)
ctx.fill(); // connect and fill
'''
txt2='''
beginPath(); // note usage below
// triangle 1, at left
fillStyle = "#F9A520";
moveTo(0, 0); // start at top left corner of canvas
lineTo(200, 0); // go 200px to right (x), straight line from 0 to 0
lineTo(100, 200); // go to horizontal 100 (x) and vertical 200 (y)
fill(); // connect and fill
// triangle 3, bottom center
beginPath(); // note: w/o this, color does not work as expected
fillStyle = "#F00";
moveTo(300, 200); // pick up "pen," reposition at 300 (horiz), 200 (vert)
lineTo(300, 400); // draw straight down by 200px (200 + 200)
lineTo(100, 300); // draw up toward left (100 less than 300, so left)
fill(); // connect and fill
'''
Canvas1(txt, context= 'ctx')
#Canvas1(txt2)
Let's have a go at creating some magic...
import random
import string
import shlex
from argparse import ArgumentParser
from IPython.core.magic import (
magics_class, line_cell_magic, Magics)
from IPython.core.display import HTML
@magics_class
class CanvasMagic(Magics):
def __init__(self, shell, cache_display_data=False):
super(CanvasMagic, self).__init__(shell)
self.cache_display_data = cache_display_data
@line_cell_magic
def canvas(self,line, cell=''):
'''Run JS canvas commands.'''
parser = ArgumentParser()
parser.add_argument('-c', '--context', default=None)
parser.add_argument('-I', '--id', default=None)
parser.add_argument('--wrap', dest='wrap_env', action='store_true')
parser.add_argument('--no-wrap', dest='wrap_env', action='store_false')
parser.add_argument('-v', '--variable', default=None)
parser.set_defaults(wrap_env=False)
args = parser.parse_args(shlex.split(line))
if args.variable:
cell = self.shell.user_ns[args.variable]
#Create a random id for the canvas tag to try to prevent clashes
argsid = args.id if args.id else ''.join(random.choices(string.ascii_uppercase + string.digits, k=5))
if not args.context:
if not args.wrap_env: context = None
else: context='ctx'
else: context = args.context
#We need to add the extra \n in case the last line in the cell is a comment that would comment out </script>
return Canvas1(cell+'\n', context=context, cid=argsid)
ip = get_ipython()
ip.register_magics(CanvasMagic)
%%canvas -c ctx
ctx.beginPath();
ctx.fillStyle = "#F9A520";
ctx.moveTo(0, 0);
ctx.lineTo(200, 0);
ctx.lineTo(100, 200);
ctx.fill();
// triangle 2, top center
ctx.moveTo(300, 0); // pick up "pen," reposition at 300 (horiz), 0 (vert)
ctx.lineTo(300, 200); // draw straight down (from 300,0) to 200px
ctx.lineTo(500, 100); // draw up toward right (100 half of 200)
ctx.fill(); // connect and fill
ctx.stroke();
%%canvas
beginPath();
fillStyle = "#F9A520";
moveTo(0, 0);
lineTo(200, 0);
lineTo(100, 200);
fill();
beginPath();
fillStyle = "#F00";
moveTo(300, 200);
lineTo(300, 400); // draw straight down by 200px (200 + 200)
lineTo(100, 300); // draw up toward left (100 less than 300, so left)
fill(); // connect and fill
%canvas -v txt2
%canvas -v txt --context ctx