$(function(){ window.canvas = $('#turtle_canvas').turtle_canvas(); }); $.fn.extend({ turtle_canvas: function(){ var elem = this.get(0); if (elem.tagName.toLowerCase() == 'canvas'){ return new TurtleCanvas(elem); }else{ throw new Error('No canvas element found'); } } }); function TurtleCanvas(canvas_element){ this.canvas = canvas_element.getContext('2d'); this.width = parseInt(canvas_element.getAttribute('width')); this.height = parseInt(canvas_element.getAttribute('height')); } TurtleCanvas.prototype.eval_script = function(id){ eval($(id)[0].value); }; TurtleCanvas.prototype.clear = function(){ this.canvas.clearRect(0, 0, this.width, this.height); }; TurtleCanvas.prototype.toString = function(){ return ''; } TurtleCanvas.prototype.triangle = function(p1, p2, p3){ this.canvas.beginPath(); this.canvas.moveTo(p1.x, p1.y); this.canvas.lineTo(p2.x, p2.y); this.canvas.lineTo(p3.x, p3.y); this.canvas.closePath(); this.canvas.fill(); }; TurtleCanvas.prototype.lines = function(paths){ var self = this; $.each(paths, function(){ if (this.length > 1){ self.canvas.beginPath(); self.canvas.moveTo(this[0].x, this[0].y); $.each(this.slice(1), function(){ self.canvas.lineTo(this.x, this.y); }); self.canvas.stroke(); } }); }; $.extend({ radians: function(degrees){ return (Math.PI / 180.0) * degrees; } }); function Scheduler(obj){ this.schedule = [] this.delay = 100; this.run_idx = 0; Scheduler.objects.push(obj); this.running = false; } Scheduler.objects = []; Scheduler.partition = function(number, maxpart){ var sign = 1; if (number < 0){ sign = -1; number *= sign; } var segments = []; var whole_count = Math.floor(number / maxpart); var remainder = number % maxpart; for(var i = 0; i < whole_count; i++){ segments.push(maxpart * sign); } if (remainder){ segments.push(remainder * sign); } return segments; }; Scheduler.prototype.clear = function(){ this.schedule = []; this.running = false; this.run_idx = 0; } Scheduler.prototype.add = function(fn){ this.schedule.push(fn); if (!this.running){ this.run(); } }; Scheduler.prototype.run = function(){ if(this.run_idx < this.schedule.length){ this.running = true; canvas.clear(); this.schedule[this.run_idx](); $.each(Scheduler.objects, function(idx, obj){ obj.draw(); }); this.run_idx++; var self = this; setTimeout(function(){self.run();}, this.delay); }else{ this.running = false; } }; function Turtle(position, direction){ this.position = position || {x: 100, y: 100}; this.direction = $.radians(direction || 0); this.current_path = [this.position]; this.paths = [this.current_path]; this._pendown = true; this.visible = true; this.scheduler = new Scheduler(this); } Turtle.prototype._position_from = function(distance){ var p = this.position; var d = this.direction; return {x: p.x + Math.cos(d) * distance, y: p.y + Math.sin(d) * distance}; } Turtle.prototype.forward = function(distance){ var self = this; var distance_partition = Scheduler.partition(distance, 10); $.each(distance_partition, function(idx, distance){ self.scheduler.add(function(){ self.position = self._position_from(distance); if (self._pendown){ self.current_path.push(self.position); } }); }); }; Turtle.prototype.right = function(degrees){ this.left(-degrees); }; Turtle.prototype.left = function(degrees){ var self = this; var degree_partition = Scheduler.partition(degrees, 10); $.each(degree_partition, function(idx, degrees){ self.scheduler.add(function(){ self.direction -= $.radians(degrees); }); }); }; Turtle.prototype.draw = function(degrees){ canvas.lines(this.paths); if (this.visible){ this._draw_turtle(); } }; Turtle.prototype.toString = function(){ return 'Turtle facing ' + this.direction + ', at ' + this.position.x + ', ' + this.position.y; }; Turtle.prototype._draw_turtle = function(){ var p = this.position; var d = this.direction; var p1 = {x: p.x + Math.cos(d + $.radians(90)) * 5, y: p.y + Math.sin(d + $.radians(90)) * 5}; var p2 = {x: p.x + Math.cos(d - $.radians(90)) * 5, y: p.y + Math.sin(d - $.radians(90)) * 5}; var p3 = {x: p.x + Math.cos(d) * 8, y: p.y + Math.sin(d) * 8}; canvas.triangle(p1, p2, p3); }; Turtle.prototype.penup = function(){ var self = this; this.scheduler.add(function(){ self._pendown = false; }); }; Turtle.prototype.pendown = function(){ var self = this; this.scheduler.add(function(){ if (!self._pendown){ self.current_path = [self.position]; self.paths.push(self.current_path); } self._pendown = true; }); } Turtle.prototype.show = function(){ var self = this; this.scheduler.add(function(){ self.visible = true; }); } Turtle.prototype.hide = function(){ var self = this; this.scheduler.add(function(){ self.visible = false; }); } Turtle.prototype.clear = function(){ this.current_path = [this.position]; this.paths = [this.current_path]; this.scheduler.clear(); canvas.clear(); this.draw(); }