2
// Turtle Graphics in Javascript
5
// Copyright 2009 Joshua Bell
7
// Licensed under the Apache License, Version 2.0 (the "License");
8
// you may not use this file except in compliance with the License.
9
// You may obtain a copy of the License at
11
// http://www.apache.org/licenses/LICENSE-2.0
13
// Unless required by applicable law or agreed to in writing, software
14
// distributed under the License is distributed on an "AS IS" BASIS,
15
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
// See the License for the specific language governing permissions and
17
// limitations under the License.
19
/*global CanvasTextFunctions */
21
//----------------------------------------------------------------------
22
function CanvasTurtle(element, width, height)
23
//----------------------------------------------------------------------
25
function deg2rad(d) { return d / 180 * Math.PI; }
26
function rad2deg(r) { return r * 180 / Math.PI; }
29
self.display = element;
30
self.backbuffer = element.cloneNode(true);
37
self.color = '#000000';
43
/*private*/function moveto(x, y) {
45
self.context.strokeStyle = self.color;
46
self.context.lineWidth = self.width;
47
self.context.globalCompositeOperation =
48
(self.mode === 'erase') ? 'destination-out' :
49
(self.mode === 'reverse') ? 'xor' :
51
self.context.beginPath();
52
self.context.moveTo(self.x, self.y);
53
self.context.lineTo(x, y);
54
self.context.stroke();
61
this.move = function(distance) {
62
var x = self.x + distance * Math.cos(self.r);
63
var y = self.y - distance * Math.sin(self.r);
67
this.turn = function(angle) {
68
self.r -= deg2rad(angle);
71
this.penup = function() { self.down = false; };
72
this.pendown = function() { self.down = true; };
74
this.setpenmode = function(mode) { this.mode = mode; };
75
this.getpenmode = function() { return this.mode; };
77
this.ispendown = function() { return self.down; };
79
var STANDARD_COLORS = {
80
0: "black", 1: "blue", 2: "lime", 3: "cyan",
81
4: "red", 5: "magenta", 6: "yellow", 7: "white",
82
8: "brown", 9: "tan", 10: "green", 11: "aquamarine",
83
12: "salmon", 13: "purple", 14: "orange", 15: "gray"
86
this.setcolor = function(color) {
87
if (STANDARD_COLORS[color] !== undefined) {
88
self.color = STANDARD_COLORS[color];
94
this.getcolor = function() { return self.color; };
96
this.setwidth = function(width) { self.width = width; };
97
this.getwidth = function() { return self.width; };
99
this.setfontsize = function(size) { self.fontsize = size; };
100
this.getfontsize = function() { return self.fontsize; };
102
this.setposition = function(x, y) {
103
x = (x === undefined) ? self.x : x + (width / 2);
104
y = (y === undefined) ? self.y : -y + (height / 2);
109
this.towards = function(x, y) {
111
y = -y + (height / 2);
113
return 90 - rad2deg(Math.atan2(self.y - y, x - self.x));
116
this.setheading = function(angle) {
117
self.r = deg2rad(90 - angle);
120
this.clearscreen = function() {
125
this.clear = function() {
126
self.context.clearRect(0, 0, width, height);
129
this.home = function() {
130
moveto(width / 2, height / 2);
131
self.r = deg2rad(90);
134
this.showturtle = function() {
138
this.hideturtle = function() {
139
self.visible = false;
142
this.isturtlevisible = function() {
146
this.getheading = function() {
147
return 90 - rad2deg(self.r);
150
this.getxy = function() {
151
return [self.x - (width / 2), -self.y + (height / 2)];
154
this.drawtext = function(text) {
155
if (self.context.fillText) {
156
self.context.font = self.fontsize + 'px sans-serif';
157
self.context.fillStyle = self.color;
158
self.context.strokeStyle = self.color;
159
self.context.fillText(text, self.x, self.y);
163
/*private*/function drawturtle() {
165
self.context.strokeStyle = "green";
166
self.context.lineWidth = 2;
167
self.context.beginPath();
168
self.context.moveTo(self.x + Math.cos(self.r) * 20, self.y - Math.sin(self.r) * 20);
169
self.context.lineTo(self.x + Math.cos(self.r - Math.PI * 2 / 3) * 10, self.y - Math.sin(self.r - Math.PI * 2 / 3) * 10);
170
self.context.lineTo(self.x + Math.cos(self.r + Math.PI * 2 / 3) * 10, self.y - Math.sin(self.r + Math.PI * 2 / 3) * 10);
171
self.context.lineTo(self.x + Math.cos(self.r) * 20, self.y - Math.sin(self.r) * 20);
172
self.context.stroke();
176
//----------------------------------------------------------------------
177
this.begin = function() {
178
//----------------------------------------------------------------------
179
// Render to a backbuffer
180
self.context = self.backbuffer.getContext("2d");
182
self.context.lineCap = 'round';
184
// Monkey patch in Hershey Font-based text from
185
// c/o http://jim.studt.net/canvastext/
186
if (!self.context.fillText && CanvasTextFunctions) {
187
CanvasTextFunctions.enable(self.context);
188
self.context.strokeText = function(string, x, y) {
189
var size_font = this.font.split(/ /);
190
var size = parseFloat(size_font[0]);
191
var font = size_font[1];
193
if (this.textAlign === "right" || this.textAlign === "end") {
194
this.drawTextRight(font, size, x, y, string);
196
else if (this.textAlign === "center") {
197
this.drawTextCenter(font, size, x, y, string);
200
this.drawText(font, size, x, y, string);
203
self.context.fillText = function(string, x, y) {
204
var oldStroke = this.strokeStyle;
205
this.strokeStyle = this.fillStyle;
206
this.strokeText(string, x, y);
207
this.strokeStyle = oldStroke;
209
self.context.measureText = function(string) {
210
var size_font = this.font.split(/ /);
211
var size = parseFloat(size_font[0]);
212
var font = size_font[1];
213
return CanvasTextFunctions.measure(font, size, string);
218
//----------------------------------------------------------------------
219
this.end = function() {
220
//----------------------------------------------------------------------
221
// Flip backbuffer into primary
222
var newDisplay = self.backbuffer.cloneNode(true);
223
self.context = newDisplay.getContext("2d");
224
if (self.context.drawImage) { self.context.drawImage(self.backbuffer, 0, 0); } // Clone itself doesn't copy image data
227
self.display.parentNode.replaceChild(newDisplay, self.display);
228
self.display = newDisplay;
230
// Guard against rogue drawing