├── docs └── .keep ├── libs ├── blocksprj │ ├── README.md │ ├── main.ts │ ├── main.blocks │ └── pxt.json ├── tsconfig.json └── core │ ├── pxt.json │ ├── enums.d.ts │ ├── ns.ts │ ├── _locales │ └── de │ │ ├── core-jsdoc-strings.json │ │ └── core-strings.json │ └── sims.d.ts ├── targetconfig.json ├── examples ├── koch.png ├── tree.png ├── hilbert.png ├── quadrats.png ├── simple.gif ├── quadrats_blocks.png ├── quadrats.js ├── simple.js ├── primes.js ├── hilbert.js ├── koch.js ├── tree.js ├── bounce.js ├── mandelbrot.js └── clock.js ├── .gitattributes ├── .gitignore ├── .editorconfig ├── sim ├── tsconfig.json ├── public │ ├── sim.css │ ├── simulator.html │ ├── turtle2.svg │ ├── turtle1.svg │ └── turtle3.svg ├── sprites.ts ├── api.ts └── simulator.ts ├── .vscode └── settings.json ├── tslint.json ├── package.json ├── LICENSE ├── pxtarget.json └── README.md /docs/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libs/blocksprj/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /targetconfig.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /libs/blocksprj/main.ts: -------------------------------------------------------------------------------- 1 | // 2 | -------------------------------------------------------------------------------- /examples/koch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwestphal/pxt-turtle/HEAD/examples/koch.png -------------------------------------------------------------------------------- /examples/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwestphal/pxt-turtle/HEAD/examples/tree.png -------------------------------------------------------------------------------- /examples/hilbert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwestphal/pxt-turtle/HEAD/examples/hilbert.png -------------------------------------------------------------------------------- /examples/quadrats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwestphal/pxt-turtle/HEAD/examples/quadrats.png -------------------------------------------------------------------------------- /examples/simple.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwestphal/pxt-turtle/HEAD/examples/simple.gif -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /libs/core/sims.d.ts eol=lf 2 | /sim/api.ts eol=lf 3 | /.vscode/settings.json eol=lf 4 | -------------------------------------------------------------------------------- /examples/quadrats_blocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwestphal/pxt-turtle/HEAD/examples/quadrats_blocks.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | built/ 3 | /libs/core/_locales/* 4 | !/libs/core/_locales/de/ 5 | /projects/ 6 | *.log 7 | /gh-pages/ 8 | /temp/ 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | -------------------------------------------------------------------------------- /libs/blocksprj/main.blocks: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "strict": true, 5 | "outDir": "../built", 6 | "rootDir": "." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sim/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "strict": true, 5 | "declaration": true, 6 | "out": "../built/sim.js", 7 | "rootDir": "." 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /libs/blocksprj/pxt.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{0}", 3 | "dependencies": { 4 | "core": "file:../core" 5 | }, 6 | "files": [ 7 | "main.blocks", 8 | "main.ts", 9 | "README.md" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /sim/public/sim.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-top: 3px; 3 | margin-right: 4px; 4 | margin-bottom: 3px; 5 | margin-left: 3px; 6 | overflow: hidden; 7 | } 8 | 9 | #area { 10 | width: 100%; 11 | border: 2px solid black; 12 | } 13 | -------------------------------------------------------------------------------- /examples/quadrats.js: -------------------------------------------------------------------------------- 1 | function Quadrat() { 2 | for (let i = 0; i < 4; i++) { 3 | turtle.forward(100) 4 | turtle.turnRight(90) 5 | } 6 | } 7 | for (let i = 0; i < 12; i++) { 8 | Quadrat() 9 | turtle.turnRight(30) 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.detectIndentation": false, 3 | "telemetry.enableCrashReporter": false, 4 | "telemetry.enableTelemetry": false, 5 | "typescript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": false, 6 | "typescript.tsdk": "node_modules/typescript/lib" 7 | } 8 | -------------------------------------------------------------------------------- /libs/core/pxt.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "core", 3 | "files": [ 4 | "pxt-core.d.ts", 5 | "pxt-helpers.ts", 6 | "enums.d.ts", 7 | "sims.d.ts", 8 | "ns.ts", 9 | "_locales/de/core-strings.json", 10 | "_locales/de/core-jsdoc-strings.json" 11 | ], 12 | "testFiles": [], 13 | "dependencies": {} 14 | } 15 | -------------------------------------------------------------------------------- /libs/core/enums.d.ts: -------------------------------------------------------------------------------- 1 | declare const enum Speed { 2 | //% block=slow 3 | Slow, 4 | //% block=normal 5 | Normal, 6 | //% block=fast 7 | Fast, 8 | //% block=fastest 9 | Fastest, 10 | } 11 | 12 | declare interface Sprite { 13 | //% shim=.getWidth 14 | readonly width: number; 15 | //% shim=.getHeight 16 | readonly height: number; 17 | } 18 | -------------------------------------------------------------------------------- /examples/simple.js: -------------------------------------------------------------------------------- 1 | turtle.turnLeft(30) 2 | turtle.forward(100) 3 | turtle.turnRight(120) 4 | turtle.penUp() 5 | turtle.forward(100) 6 | turtle.turnRight(120) 7 | turtle.penDown() 8 | turtle.setPenColor(0x007fff) 9 | turtle.setPenSize(4) 10 | turtle.forward(200) 11 | turtle.turnLeft(120) 12 | turtle.setPenColor(0x000000) 13 | turtle.setPenSize(8) 14 | turtle.forward(100) 15 | turtle.home() 16 | -------------------------------------------------------------------------------- /examples/primes.js: -------------------------------------------------------------------------------- 1 | let a = 0 2 | let ps: number[] = [] 3 | turtle.penUp() 4 | ps = [2, 3, 5, 7] 5 | a = 1 6 | while (a < 100) { 7 | turtle.setPenSize(a * 0.1) 8 | turtle.setPenColor(0xff0000) 9 | for (let p of ps) { 10 | if (a % p == 0 && a != p || a == 1) { 11 | turtle.setPenColor(0x000000) 12 | } 13 | } 14 | turtle.print("" + a) 15 | turtle.forward(a) 16 | turtle.turnRight(30) 17 | a += 1 18 | } 19 | -------------------------------------------------------------------------------- /sim/public/simulator.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "rules": { 7 | "comment-format": false, 8 | "max-classes-per-file": false, 9 | "max-line-length": [ 10 | true, 11 | 170 12 | ], 13 | "member-access": [ 14 | true, 15 | "no-public" 16 | ], 17 | "no-bitwise": false, 18 | "no-empty": false, 19 | "no-namespace": false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/hilbert.js: -------------------------------------------------------------------------------- 1 | function h (d: number, s: number, a: number) { 2 | if (d > 0) { 3 | turtle.turnLeft(a) 4 | h(d - 1, s, 0 - a) 5 | turtle.forward(s) 6 | turtle.turnRight(a) 7 | h(d - 1, s, a) 8 | turtle.forward(s) 9 | h(d - 1, s, a) 10 | turtle.turnRight(a) 11 | turtle.forward(s) 12 | h(d - 1, s, 0 - a) 13 | turtle.turnLeft(a) 14 | } 15 | } 16 | turtle.hide() 17 | turtle.setSpeed(Speed.Fastest) 18 | turtle.goto(108, -108) 19 | h(5, 7, 90) 20 | -------------------------------------------------------------------------------- /examples/koch.js: -------------------------------------------------------------------------------- 1 | function k (d: number, s: number) { 2 | if (d > 0) { 3 | k(d - 1, s) 4 | turtle.turnLeft(60) 5 | k(d - 1, s) 6 | turtle.turnRight(120) 7 | k(d - 1, s) 8 | turtle.turnLeft(60) 9 | k(d - 1, s) 10 | } else { 11 | turtle.forward(s) 12 | } 13 | } 14 | turtle.hide() 15 | turtle.setSpeed(Speed.Fastest) 16 | turtle.setPenSize(1) 17 | turtle.goto(-120, 70) 18 | turtle.turnLeft(30) 19 | for (let index = 0; index < 3; index++) { 20 | turtle.turnRight(120) 21 | k(5, 1) 22 | } 23 | -------------------------------------------------------------------------------- /examples/tree.js: -------------------------------------------------------------------------------- 1 | function tree (d: number) { 2 | if (d > 0) { 3 | turtle.setPenSize(d) 4 | turtle.setPenColor((10 - d) * 6400) 5 | turtle.forward(4 * d) 6 | turtle.turnLeft(20) 7 | tree(d - 1) 8 | turtle.turnRight(40) 9 | tree(d - 1) 10 | turtle.turnLeft(20) 11 | turtle.penUp() 12 | turtle.backward(4 * d) 13 | turtle.penDown() 14 | } 15 | } 16 | turtle.hide() 17 | turtle.setSpeed(Speed.Fastest) 18 | turtle.penUp() 19 | turtle.backward(100) 20 | turtle.penDown() 21 | tree(10) 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "version": "1.0.1", 4 | "scripts": { 5 | "build": "rm -rf built/ libs/core/built && pxt staticpkg", 6 | "start": "rm -rf built/ libs/core/built projects/ && pxt serve", 7 | "start:static": "http-server -s -c-1 built/packaged" 8 | }, 9 | "devDependencies": { 10 | "@types/bluebird": "^2.0.33", 11 | "@types/easeljs": "^1.0.0", 12 | "@types/preloadjs": "^0.6.32", 13 | "@types/soundjs": "^0.6.27", 14 | "@types/tweenjs": "^1.0.0", 15 | "http-server": "^0.12.0", 16 | "pxt": "^0.5.1", 17 | "pxt-core": "5.24.11", 18 | "tslint": "^5.10.0", 19 | "typescript": "2.6" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libs/core/ns.ts: -------------------------------------------------------------------------------- 1 | //% color=#00bcd4 weight=100 icon="\uf044" 2 | namespace turtle { 3 | 4 | //% blockId=spriteEditor block="%sprite" 5 | //% sprite.fieldEditor="sprite" 6 | //% sprite.fieldOptions.sizes="32,32;16,16;8,8" 7 | //% shim=TD_ID 8 | //% weight=4 9 | export function __sprite(sprite: Sprite) { 10 | return sprite; 11 | } 12 | 13 | } 14 | 15 | //% shim=@f4 16 | //% blockIdentity="turtle.__sprite" 17 | //% helper=toSprite 18 | //% groups=["0.","1#","2","3","4","5","6","7","8","9","aA","bB","cC","dD","eE","fF"] 19 | declare function img(lits: string[], ...values: any[]): Sprite; 20 | 21 | //% color=#ff8000 weight=50 icon="\uf017" advanced=true 22 | namespace time { 23 | } 24 | 25 | declare namespace console { 26 | /** 27 | * Print out message in browser console 28 | */ 29 | //% shim=log 30 | function log(msg: string): void; 31 | } 32 | -------------------------------------------------------------------------------- /examples/bounce.js: -------------------------------------------------------------------------------- 1 | turtle.turnRight(Math.randomRange(-180, 180)) 2 | for (let i = 0; i < 2000; i++) { 3 | turtle.forward(10) 4 | if (turtle.y() < -145 || turtle.y() > 144) { 5 | if (turtle.heading() > 180) { 6 | turtle.turnLeft(180 + 2 * (turtle.heading() - 360)) 7 | } else { 8 | turtle.turnRight(180 - 2 * turtle.heading()) 9 | } 10 | } else if (turtle.x() < -145 || turtle.x() > 144) { 11 | if (turtle.heading() >= 0 && turtle.heading() < 90) { 12 | turtle.turnLeft(2 * turtle.heading()) 13 | } else if (turtle.heading() >= 90 && turtle.heading() < 180) { 14 | turtle.turnRight(360 - 2 * turtle.heading()) 15 | } else if (turtle.heading() >= 180 && turtle.heading() < 270) { 16 | turtle.turnLeft(2 * turtle.heading() - 360) 17 | } else { 18 | turtle.turnRight(2 * (360 - turtle.heading())) 19 | } 20 | } 21 | } 22 | turtle.home() 23 | -------------------------------------------------------------------------------- /examples/mandelbrot.js: -------------------------------------------------------------------------------- 1 | function mand (r: number, i: number) { 2 | c = 0 3 | zr = 0 4 | zi = 0 5 | while (Math.sqrt(zr * zr + zi * zi) <= 2 && c < 100) { 6 | t = zr 7 | zr = zr * zr - zi * zi + r 8 | zi = 2 * t * zi + i 9 | c += 1 10 | } 11 | c = c / 100 12 | } 13 | let t = 0 14 | let zi = 0 15 | let zr = 0 16 | let c = 0 17 | turtle.hide() 18 | turtle.setSpeed(Speed.Fastest) 19 | turtle.goto(-125, 125) 20 | turtle.turnRight(90) 21 | turtle.setPenSize(5) 22 | for (let y = 0; y <= 49; y++) { 23 | for (let index = 0; index < 50; index++) { 24 | mand(-2 + (turtle.x() + 125) / 250 * 3, -1 + (turtle.y() + 125) / 250 * 2) 25 | turtle.setPenColor(65793 * (255 - Math.floor(c * 255))) 26 | turtle.forward(5) 27 | } 28 | turtle.penUp() 29 | if (y % 2 == 0) { 30 | turtle.turnRight(90) 31 | turtle.forward(5) 32 | turtle.turnRight(90) 33 | } else { 34 | turtle.turnLeft(90) 35 | turtle.forward(5) 36 | turtle.turnLeft(90) 37 | } 38 | turtle.penDown() 39 | } 40 | -------------------------------------------------------------------------------- /examples/clock.js: -------------------------------------------------------------------------------- 1 | let minutes = 0 2 | let seconds = 0 3 | let hours = 0 4 | let ts = 0 5 | function drawHands() { 6 | turtle.setPenColor(0x000000) 7 | turtle.turnRight((hours + minutes / 60 + seconds / 3600) * 30) 8 | turtle.forward(45) 9 | turtle.home() 10 | turtle.turnRight((minutes + seconds / 60) * 6) 11 | turtle.forward(60) 12 | turtle.home() 13 | turtle.turnRight(seconds * 6) 14 | turtle.forward(75) 15 | turtle.home() 16 | } 17 | function drawDial() { 18 | turtle.setPenColor(0xff0000) 19 | for (let i = 0; i <= 11; i++) { 20 | turtle.turnRight(i * 30) 21 | turtle.penUp() 22 | turtle.forward(100) 23 | turtle.penDown() 24 | if (i == 0) { 25 | turtle.backward(20) 26 | } else { 27 | turtle.backward(10) 28 | } 29 | turtle.home() 30 | } 31 | } 32 | turtle.hide() 33 | turtle.setSpeed(Speed.Fastest) 34 | while (true) { 35 | turtle.clear() 36 | drawDial() 37 | ts = time.now() 38 | hours = time.hours(ts) 39 | minutes = time.minutes(ts) 40 | seconds = time.seconds(ts) 41 | drawHands() 42 | time.wait(1) 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Harald Westphal 4 | Copyright (c) Yutaka Catch (for turtle icons) 5 | 6 | All rights reserved. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | -------------------------------------------------------------------------------- /pxtarget.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "turtle", 3 | "name": "Turtle", 4 | "corepkg": "core", 5 | "bundleddirs": [ 6 | "libs/core" 7 | ], 8 | "runtime": { 9 | "loopsBlocks": true, 10 | "logicBlocks": true, 11 | "variablesBlocks": true, 12 | "mathBlocks": true, 13 | "functionBlocks": true, 14 | "listsBlocks": true, 15 | "textBlocks": true, 16 | "onStartColor": "#f48942", 17 | "onStartUnDeletable": true, 18 | "palette": [ 19 | "#ffffff", 20 | "#000000", 21 | "#ff0000", 22 | "#ff8000", 23 | "#ffff00", 24 | "#ff9da5", 25 | "#00ff00", 26 | "#b09eff", 27 | "#00ffff", 28 | "#007fff", 29 | "#65471f", 30 | "#0000ff", 31 | "#7f00ff", 32 | "#ff0080", 33 | "#ff00ff", 34 | "#999999" 35 | ] 36 | }, 37 | "simulator": { 38 | "autoRun": true, 39 | "stopOnChange": true, 40 | "hideFullscreen": true, 41 | "aspectRatio": 1 42 | }, 43 | "appTheme": { 44 | "availableLocales": [ 45 | "en", 46 | "de" 47 | ], 48 | "selectLanguage": true, 49 | "docMenu": [], 50 | "monacoToolbox": true, 51 | "githubUrl": "https://github.com/hwestphal/pxt-turtle" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sim/sprites.ts: -------------------------------------------------------------------------------- 1 | namespace pxsim { 2 | 3 | export async function createTurtleSprite() { 4 | return new createjs.Sprite(await turtleSpriteSheet(), "default"); 5 | } 6 | 7 | async function turtleSpriteSheet() { 8 | if (turtleSpriteSheet.cached) { 9 | return turtleSpriteSheet.cached; 10 | } 11 | const ssb = new createjs.SpriteSheetBuilder(); 12 | (await loadImages("turtle1.svg", "turtle2.svg", "turtle3.svg")).map((i) => { 13 | i.regX = 218; 14 | i.regY = 265; 15 | ssb.addFrame(i, undefined, 0.06); 16 | }); 17 | ssb.addAnimation("default", [0, 1, 0, 2], undefined, 0.4); 18 | turtleSpriteSheet.cached = ssb.build(); 19 | return turtleSpriteSheet.cached; 20 | } 21 | 22 | namespace turtleSpriteSheet { 23 | export let cached: createjs.SpriteSheet; 24 | } 25 | 26 | function loadImages(...sources: string[]) { 27 | return new Promise((resolve, reject) => { 28 | const queue = new createjs.LoadQueue(); 29 | for (const src of sources) { 30 | queue.loadFile({ src, type: createjs.LoadQueue.IMAGE }); 31 | } 32 | queue.addEventListener("error", (e) => { 33 | queue.removeAllEventListeners("complete"); 34 | reject(e); 35 | }); 36 | queue.addEventListener("complete", () => { 37 | resolve(queue.getItems(true).map((i) => new createjs.Bitmap((i as any).result))); 38 | }); 39 | }); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /libs/core/_locales/de/core-jsdoc-strings.json: -------------------------------------------------------------------------------- 1 | { 2 | "time.day": "Liefere den Tag des Zeitstempels zurück", 3 | "time.hours": "Liefere die Stunden des Zeitstempels zurück", 4 | "time.minutes": "Liefere die Minuten des Zeitstempels zurück", 5 | "time.month": "Liefere den Monat des Zeitstempels zurück", 6 | "time.now": "Liefere den aktuellen Zeitstempel zurück (in Sekunden seit Epoch)", 7 | "time.seconds": "Liefere die Sekunden des Zeitstempels zurück", 8 | "time.wait": "Warte für eine gewisse Zeit", 9 | "time.year": "Liefere das Jahr des Zeitstempels zurück", 10 | "turtle.backward": "Bewege die Turtle rückwärts", 11 | "turtle.clear": "Lösche die Zeichenfläche", 12 | "turtle.drawSprite": "Zeichne Sprite", 13 | "turtle.forward": "Bewege die Turtle vorwärts", 14 | "turtle.goto": "Bewege die Turtle zu der angegebenen Position", 15 | "turtle.heading": "Richtung der Turtle", 16 | "turtle.hide": "Verstecke die Turtle", 17 | "turtle.home": "Bewege die Turtle zum Ursprung und setze die Richtung auf 0", 18 | "turtle.penDown": "Nehme den Stift nach unten", 19 | "turtle.penUp": "Nehme den Stift nach oben", 20 | "turtle.print": "Drucke einen Text und bleibe auf der Stelle stehen", 21 | "turtle.printAndMove": "Drucke einen Text und bewege die Turtle vorwärts", 22 | "turtle.setPenColor": "Setze die Stiftfarbe", 23 | "turtle.setPenSize": "Setze die Stiftgröße", 24 | "turtle.setSpeed": "Setze die Geschwindigkeit der Turtle", 25 | "turtle.show": "Zeige die Turtle", 26 | "turtle.turnLeft": "Drehe die Turtle nach links", 27 | "turtle.turnRight": "Drehe die Turtle nach rechts", 28 | "turtle.x": "X-Position der Turtle", 29 | "turtle.y": "Y-Position der Turtle" 30 | } 31 | -------------------------------------------------------------------------------- /libs/core/_locales/de/core-strings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Speed.Fastest|block": "am schnellsten", 3 | "Speed.Fast|block": "schnell", 4 | "Speed.Normal|block": "normal", 5 | "Speed.Slow|block": "langsam", 6 | "time.day|block": "Tag von %ts", 7 | "time.hours|block": "Stunden von %ts", 8 | "time.minutes|block": "Minuten von %ts", 9 | "time.month|block": "Monat von %ts", 10 | "time.now|block": "aktuelles Datum und Uhrzeit", 11 | "time.seconds|block": "Sekunden von %ts", 12 | "time.wait|block": "warte für %delay Sekunden", 13 | "time.year|block": "Jahr von %ts", 14 | "turtle.backward|block": "rückwärts %distance Schritte", 15 | "turtle.clear|block": "lösche die Zeichenfläche", 16 | "turtle.drawSprite|block": "zeichne %sprite=spriteEditor", 17 | "turtle.forward|block": "vorwärts %distance Schritte", 18 | "turtle.goto|block": "gehe zu x=%xpos und y=%ypos", 19 | "turtle.heading|block": "Richtung", 20 | "turtle.hide|block": "verstecke Turtle", 21 | "turtle.home|block": "zurück nach Hause", 22 | "turtle.penDown|block": "Stift nach unten", 23 | "turtle.penUp|block": "Stift nach oben", 24 | "turtle.printAndMove|block": "drucke %text und gehe vorwärts", 25 | "turtle.print|block": "drucke %text", 26 | "turtle.setPenColor|block": "Stiftfarbe auf %color=colorNumberPicker setzen", 27 | "turtle.setPenSize|block": "Stiftgröße auf %size setzen", 28 | "turtle.setSpeed|block": "Geschwindigkeit auf %speed setzen", 29 | "turtle.show|block": "zeige Turtle", 30 | "turtle.turnLeft|block": "nach links drehen um %angle Grad", 31 | "turtle.turnRight|block": "nach rechts drehen um %angle Grad", 32 | "turtle.x|block": "X-Position", 33 | "turtle.y|block": "Y-Position", 34 | "{id:category}Time": "Zeit" 35 | } 36 | -------------------------------------------------------------------------------- /sim/public/turtle2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | image/svg+xml 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /sim/public/turtle1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | image/svg+xml 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /sim/public/turtle3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | image/svg+xml 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Turtle Editor # 2 | 3 | *pxt-turtle* is a programming environment fur [turtle graphics](https://en.wikipedia.org/wiki/Turtle_graphics), based on [Microsoft MakeCode](https://github.com/Microsoft/pxt). It's meant for teaching beginners in programming fundamentals. 4 | 5 | ## Features ## 6 | 7 | ### Animated turtle graphics ### 8 | ![Simple](examples/simple.gif) 9 | 10 | (Turtle design taken from https://github.com/ycatch/p5.turtle.js, [CreateJS](https://createjs.com/) used as animation framework) 11 | 12 | ### Visual programming ### 13 | ![Quadrats](examples/quadrats.png) ![Blocks](examples/quadrats_blocks.png) 14 | 15 | ## Further examples ## 16 | 17 | ### [Tree](examples/tree.js) ### 18 | ![Tree](examples/tree.png) 19 | 20 | ### [Hilbert](examples/hilbert.js) and [Koch](examples/koch.js) curves ### 21 | ![Hilbert Curve](examples/hilbert.png) ![Koch Curve](examples/koch.png) 22 | 23 | ## API ## 24 | 25 | declare namespace turtle { 26 | function forward(distance: number): void; 27 | function backward(distance: number): void; 28 | function turnRight(angle: number): void; 29 | function turnLeft(angle: number): void; 30 | function penUp(): void; 31 | function penDown(): void; 32 | function home(): void; 33 | function x(): number; 34 | function y(): number; 35 | function heading(): number; 36 | function setSpeed(speed: Speed): void; 37 | function setPenColor(color: number): void; 38 | function setPenSize(size: number): void; 39 | function show(): void; 40 | function hide(): void; 41 | function goto(xpos: number, ypos: number): void; 42 | function printAndMove(text: string): void; 43 | function print(text: string): void; 44 | function clear(): void; 45 | function drawSprite(sprite: Sprite): void; 46 | } 47 | 48 | declare const enum Speed { 49 | Slow, Normal, Fast, Fastest 50 | } 51 | 52 | declare interface Sprite { 53 | readonly width: number; 54 | readonly height: number; 55 | } 56 | 57 | declare namespace time { 58 | function wait(delay: number): void; 59 | function now(): number; 60 | function year(ts: number): number; 61 | function month(ts: number): number; 62 | function day(ts: number): number; 63 | function hours(ts: number): number; 64 | function minutes(ts: number): number; 65 | function seconds(ts: number): number; 66 | } 67 | 68 | declare namespace console { 69 | function log(msg: string): void; 70 | } 71 | 72 | ## How to build ## 73 | *pxt-turtle* uses [Yarn](https://yarnpkg.com/) for dependency management. To install the needed packages, simply execute `yarn` in the project root folder. 74 | 75 | ### Start in development mode ### 76 | 77 | yarn start 78 | 79 | ### Publish as GitHub pages ### 80 | 81 | yarn build --githubpages 82 | -------------------------------------------------------------------------------- /libs/core/sims.d.ts: -------------------------------------------------------------------------------- 1 | // Auto-generated from simulator. Do not edit. 2 | declare namespace turtle { 3 | /** 4 | * Move the turtle forward 5 | * @param distance distance to move, eg: 50 6 | */ 7 | //% weight=90 8 | //% blockId=turtleForward block="forward %distance steps" 9 | //% shim=turtle::forwardAsync promise 10 | function forward(distance: number): void; 11 | 12 | /** 13 | * Move the turtle backward 14 | * @param distance distance to move, eg: 50 15 | */ 16 | //% weight=85 17 | //% blockId=turtleBackward block="backward %distance steps" 18 | //% shim=turtle::backwardAsync promise 19 | function backward(distance: number): void; 20 | 21 | /** 22 | * Turn the turtle to the right 23 | * @param angle degrees to turn, eg: 90 24 | */ 25 | //% weight=80 26 | //% blockId=turtleTurnRight block="turn right by %angle degrees" 27 | //% angle.min=0 angle.max=360 28 | //% shim=turtle::turnRightAsync promise 29 | function turnRight(angle: number): void; 30 | 31 | /** 32 | * Turn the turtle to the left 33 | * @param angle degrees to turn, eg: 90 34 | */ 35 | //% weight=75 36 | //% blockId=turtleTurnLeft block="turn left by %angle degrees" 37 | //% angle.min=0 angle.max=360 38 | //% shim=turtle::turnLeftAsync promise 39 | function turnLeft(angle: number): void; 40 | 41 | /** 42 | * Pull the pen up 43 | */ 44 | //% weight=70 45 | //% blockId=turtlePenUp block="pull the pen up" 46 | //% shim=turtle::penUp 47 | function penUp(): void; 48 | 49 | /** 50 | * Pull the pen down 51 | */ 52 | //% weight=65 53 | //% blockId=turtlePenDown block="pull the pen down" 54 | //% shim=turtle::penDown 55 | function penDown(): void; 56 | 57 | /** 58 | * Move the turtle to the origin and set heading to 0 59 | */ 60 | //% weight=60 61 | //% blockId=turtleHome block="back to home" 62 | //% shim=turtle::homeAsync promise 63 | function home(): void; 64 | 65 | /** 66 | * X position of the turtle 67 | */ 68 | //% weight=55 69 | //% blockId=turtleX block="x position" 70 | //% shim=turtle::x 71 | function x(): number; 72 | 73 | /** 74 | * Y position of the turtle 75 | */ 76 | //% weight=54 77 | //% blockId=turtleY block="y position" 78 | //% shim=turtle::y 79 | function y(): number; 80 | 81 | /** 82 | * Heading of the turtle 83 | */ 84 | //% weight=53 85 | //% blockId=turtleHeading block="heading" 86 | //% shim=turtle::heading 87 | function heading(): number; 88 | 89 | /** 90 | * Set the speed of the turtle 91 | * @param speed turtle speed, eg: Speed.Fast 92 | */ 93 | //% weight=40 94 | //% blockId=turtleSpeed block="set speed to %speed" 95 | //% shim=turtle::setSpeed 96 | function setSpeed(speed: Speed): void; 97 | 98 | /** 99 | * Set the pen color 100 | * @param color pen color, eg: 0x007fff 101 | */ 102 | //% weight=50 103 | //% blockId="turtlePenColor" block="set pen color to %color=colorNumberPicker" 104 | //% shim=turtle::setPenColor 105 | function setPenColor(color: number): void; 106 | 107 | /** 108 | * Set the pen size 109 | * @param size pen size, eg: 3 110 | */ 111 | //% weight=45 112 | //% blockId="turtlePenSize" block="set pen size to %size" 113 | //% size.min=1 size.max=10 114 | //% shim=turtle::setPenSize 115 | function setPenSize(size: number): void; 116 | 117 | /** 118 | * Show the turtle 119 | */ 120 | //% weight=30 121 | //% blockId=turtleShow block="show turtle" 122 | //% shim=turtle::show 123 | function show(): void; 124 | 125 | /** 126 | * Hide the turtle 127 | */ 128 | //% weight=35 129 | //% blockId=turtleHide block="hide turtle" 130 | //% shim=turtle::hide 131 | function hide(): void; 132 | 133 | /** 134 | * Move the turtle to the given position 135 | * @param xpos x position 136 | * @param ypos y position 137 | */ 138 | //% weight=29 139 | //% blockId=turtleGoto block="goto x=%xpos and y=%ypos" 140 | //% shim=turtle::gotoAsync promise 141 | function goto(xpos: number, ypos: number): void; 142 | 143 | /** 144 | * Print a text and move forward 145 | * @param text text to print, eg: "Hello World" 146 | */ 147 | //% weight=20 148 | //% blockId=turtlePrintAndMove block="print %text and move forward" 149 | //% shim=turtle::printAndMoveAsync promise 150 | function printAndMove(text: string): void; 151 | 152 | /** 153 | * Print a text and stand still 154 | * @param text text to print, eg: "Hello World" 155 | */ 156 | //% weight=25 157 | //% blockId=turtlePrint block="print %text" 158 | //% shim=turtle::printAsync promise 159 | function print(text: string): void; 160 | 161 | /** 162 | * Clear the canvas 163 | */ 164 | //% weight=15 165 | //% blockId=turtleClear block="clear the canvas" 166 | //% shim=turtle::clear 167 | function clear(): void; 168 | 169 | /** 170 | * Draw sprite 171 | * @param sprite sprite to draw, eg: img`` 172 | */ 173 | //% weight=5 174 | //% blockId=turtleDrawSprite block="draw %sprite=spriteEditor" 175 | //% shim=turtle::drawSprite 176 | function drawSprite(sprite: Sprite): void; 177 | 178 | } 179 | declare namespace time { 180 | /** 181 | * Wait for some time 182 | * @param delay time to wait in seconds, eg: 5 183 | */ 184 | //% weight=90 185 | //% blockId=timeWait block="wait for %delay seconds" 186 | //% shim=time::waitAsync promise 187 | function wait(delay: number): void; 188 | 189 | /** 190 | * Return the current date and time as seconds since epoch 191 | */ 192 | //% weight=80 193 | //% blockId=timeNow block="current date and time" 194 | //% shim=time::now 195 | function now(): number; 196 | 197 | /** 198 | * Return the year of the given timestamp 199 | * @param ts timestamp 200 | */ 201 | //% weight=78 202 | //% blockId=timeYear block="year of %ts" 203 | //% shim=time::year 204 | function year(ts: number): number; 205 | 206 | /** 207 | * Return the month of the given timestamp 208 | * @param ts timestamp 209 | */ 210 | //% weight=77 211 | //% blockId=timeMonth block="month of %ts" 212 | //% shim=time::month 213 | function month(ts: number): number; 214 | 215 | /** 216 | * Return the day of the given timestamp 217 | * @param ts timestamp 218 | */ 219 | //% weight=76 220 | //% blockId=timeDay block="day of %ts" 221 | //% shim=time::day 222 | function day(ts: number): number; 223 | 224 | /** 225 | * Return the hours of the given timestamp 226 | * @param ts timestamp 227 | */ 228 | //% weight=75 229 | //% blockId=timeHours block="hours of %ts" 230 | //% shim=time::hours 231 | function hours(ts: number): number; 232 | 233 | /** 234 | * Return the minutes of the given timestamp 235 | * @param ts timestamp 236 | */ 237 | //% weight=74 238 | //% blockId=timeMinutes block="minutes of %ts" 239 | //% shim=time::minutes 240 | function minutes(ts: number): number; 241 | 242 | /** 243 | * Return the seconds of the given timestamp 244 | * @param ts timestamp 245 | */ 246 | //% weight=73 247 | //% blockId=timeSeconds block="seconds of %ts" 248 | //% shim=time::seconds 249 | function seconds(ts: number): number; 250 | 251 | } 252 | 253 | // Auto-generated. Do not edit. Really. 254 | -------------------------------------------------------------------------------- /sim/api.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | namespace pxsim.turtle { 4 | /** 5 | * Move the turtle forward 6 | * @param distance distance to move, eg: 50 7 | */ 8 | //% weight=90 9 | //% blockId=turtleForward block="forward %distance steps" 10 | export async function forwardAsync(distance: number) { 11 | await board().move(distance); 12 | } 13 | 14 | /** 15 | * Move the turtle backward 16 | * @param distance distance to move, eg: 50 17 | */ 18 | //% weight=85 19 | //% blockId=turtleBackward block="backward %distance steps" 20 | export async function backwardAsync(distance: number) { 21 | await board().move(-distance); 22 | } 23 | 24 | /** 25 | * Turn the turtle to the right 26 | * @param angle degrees to turn, eg: 90 27 | */ 28 | //% weight=80 29 | //% blockId=turtleTurnRight block="turn right by %angle degrees" 30 | //% angle.min=0 angle.max=360 31 | export async function turnRightAsync(angle: number) { 32 | await board().turn(angle); 33 | } 34 | 35 | /** 36 | * Turn the turtle to the left 37 | * @param angle degrees to turn, eg: 90 38 | */ 39 | //% weight=75 40 | //% blockId=turtleTurnLeft block="turn left by %angle degrees" 41 | //% angle.min=0 angle.max=360 42 | export async function turnLeftAsync(angle: number) { 43 | await board().turn(-angle); 44 | } 45 | 46 | /** 47 | * Pull the pen up 48 | */ 49 | //% weight=70 50 | //% blockId=turtlePenUp block="pull the pen up" 51 | export function penUp() { 52 | board().pen = false; 53 | } 54 | 55 | /** 56 | * Pull the pen down 57 | */ 58 | //% weight=65 59 | //% blockId=turtlePenDown block="pull the pen down" 60 | export function penDown() { 61 | board().pen = true; 62 | } 63 | 64 | /** 65 | * Move the turtle to the origin and set heading to 0 66 | */ 67 | //% weight=60 68 | //% blockId=turtleHome block="back to home" 69 | export async function homeAsync() { 70 | await board().moveTo(0, 0, 0); 71 | } 72 | 73 | /** 74 | * X position of the turtle 75 | */ 76 | //% weight=55 77 | //% blockId=turtleX block="x position" 78 | export function x() { 79 | return board().x; 80 | } 81 | 82 | /** 83 | * Y position of the turtle 84 | */ 85 | //% weight=54 86 | //% blockId=turtleY block="y position" 87 | export function y() { 88 | return board().y; 89 | } 90 | 91 | /** 92 | * Heading of the turtle 93 | */ 94 | //% weight=53 95 | //% blockId=turtleHeading block="heading" 96 | export function heading() { 97 | return board().heading; 98 | } 99 | 100 | /** 101 | * Set the speed of the turtle 102 | * @param speed turtle speed, eg: Speed.Fast 103 | */ 104 | //% weight=40 105 | //% blockId=turtleSpeed block="set speed to %speed" 106 | export function setSpeed(speed: Speed) { 107 | board().speed = speed; 108 | } 109 | 110 | /** 111 | * Set the pen color 112 | * @param color pen color, eg: 0x007fff 113 | */ 114 | //% weight=50 115 | //% blockId="turtlePenColor" block="set pen color to %color=colorNumberPicker" 116 | export function setPenColor(color: number) { 117 | board().penColor = color; 118 | } 119 | 120 | /** 121 | * Set the pen size 122 | * @param size pen size, eg: 3 123 | */ 124 | //% weight=45 125 | //% blockId="turtlePenSize" block="set pen size to %size" 126 | //% size.min=1 size.max=10 127 | export function setPenSize(size: number) { 128 | board().penSize = size; 129 | } 130 | 131 | /** 132 | * Show the turtle 133 | */ 134 | //% weight=30 135 | //% blockId=turtleShow block="show turtle" 136 | export function show() { 137 | board().turtle = true; 138 | } 139 | 140 | /** 141 | * Hide the turtle 142 | */ 143 | //% weight=35 144 | //% blockId=turtleHide block="hide turtle" 145 | export function hide() { 146 | board().turtle = false; 147 | } 148 | 149 | /** 150 | * Move the turtle to the given position 151 | * @param xpos x position 152 | * @param ypos y position 153 | */ 154 | //% weight=29 155 | //% blockId=turtleGoto block="goto x=%xpos and y=%ypos" 156 | export async function gotoAsync(xpos: number, ypos: number) { 157 | await board().moveTo(xpos, ypos, board().heading); 158 | } 159 | 160 | /** 161 | * Print a text and move forward 162 | * @param text text to print, eg: "Hello World" 163 | */ 164 | //% weight=20 165 | //% blockId=turtlePrintAndMove block="print %text and move forward" 166 | export async function printAndMoveAsync(text: string) { 167 | await board().print(text, true); 168 | } 169 | 170 | /** 171 | * Print a text and stand still 172 | * @param text text to print, eg: "Hello World" 173 | */ 174 | //% weight=25 175 | //% blockId=turtlePrint block="print %text" 176 | export async function printAsync(text: string) { 177 | await board().print(text, false); 178 | } 179 | 180 | /** 181 | * Clear the canvas 182 | */ 183 | //% weight=15 184 | //% blockId=turtleClear block="clear the canvas" 185 | export function clear() { 186 | board().clear(); 187 | } 188 | 189 | /** 190 | * Draw sprite 191 | * @param sprite sprite to draw, eg: img`` 192 | */ 193 | //% weight=5 194 | //% blockId=turtleDrawSprite block="draw %sprite=spriteEditor" 195 | export function drawSprite(sprite: Sprite) { 196 | board().drawSprite(sprite as SpriteImpl); 197 | } 198 | 199 | } 200 | 201 | namespace pxsim.time { 202 | /** 203 | * Wait for some time 204 | * @param delay time to wait in seconds, eg: 5 205 | */ 206 | //% weight=90 207 | //% blockId=timeWait block="wait for %delay seconds" 208 | export async function waitAsync(delay: number) { 209 | await Promise.delay(delay * 1000); 210 | } 211 | 212 | /** 213 | * Return the current date and time as seconds since epoch 214 | */ 215 | //% weight=80 216 | //% blockId=timeNow block="current date and time" 217 | export function now() { 218 | return Math.floor(Date.now() / 1000); 219 | } 220 | 221 | /** 222 | * Return the year of the given timestamp 223 | * @param ts timestamp 224 | */ 225 | //% weight=78 226 | //% blockId=timeYear block="year of %ts" 227 | export function year(ts: number) { 228 | return asDate(ts).getFullYear(); 229 | } 230 | 231 | /** 232 | * Return the month of the given timestamp 233 | * @param ts timestamp 234 | */ 235 | //% weight=77 236 | //% blockId=timeMonth block="month of %ts" 237 | export function month(ts: number) { 238 | return asDate(ts).getMonth() + 1; 239 | } 240 | 241 | /** 242 | * Return the day of the given timestamp 243 | * @param ts timestamp 244 | */ 245 | //% weight=76 246 | //% blockId=timeDay block="day of %ts" 247 | export function day(ts: number) { 248 | return asDate(ts).getDate(); 249 | } 250 | 251 | /** 252 | * Return the hours of the given timestamp 253 | * @param ts timestamp 254 | */ 255 | //% weight=75 256 | //% blockId=timeHours block="hours of %ts" 257 | export function hours(ts: number) { 258 | return asDate(ts).getHours(); 259 | } 260 | 261 | /** 262 | * Return the minutes of the given timestamp 263 | * @param ts timestamp 264 | */ 265 | //% weight=74 266 | //% blockId=timeMinutes block="minutes of %ts" 267 | export function minutes(ts: number) { 268 | return asDate(ts).getMinutes(); 269 | } 270 | 271 | /** 272 | * Return the seconds of the given timestamp 273 | * @param ts timestamp 274 | */ 275 | //% weight=73 276 | //% blockId=timeSeconds block="seconds of %ts" 277 | export function seconds(ts: number) { 278 | return asDate(ts).getSeconds(); 279 | } 280 | 281 | function asDate(ts: number) { 282 | const d = new Date(); 283 | d.setTime(ts * 1000); 284 | return d; 285 | } 286 | 287 | } 288 | -------------------------------------------------------------------------------- /sim/simulator.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | namespace pxsim { 4 | 5 | initCurrentRuntime = () => { 6 | runtime.board = new TurtleBoard(); 7 | }; 8 | 9 | export function board() { 10 | return runtime.board as TurtleBoard; 11 | } 12 | 13 | export class TurtleBoard extends BaseBoard { 14 | x = 0; 15 | y = 0; 16 | heading = 0; 17 | pen = true; 18 | penSize = 2; 19 | 20 | private readonly stage: createjs.Stage; 21 | private readonly xOffset: number; 22 | private readonly yOffset: number; 23 | private delay = delays[Speed.Normal]; 24 | private color = "#ff0000"; 25 | private turtleSprite?: createjs.Sprite; 26 | 27 | constructor() { 28 | super(); 29 | this.stage = new createjs.Stage("area"); 30 | createjs.Ticker.addEventListener("tick", this.stage); 31 | const canvas = this.stage.canvas as HTMLCanvasElement; 32 | canvas.getContext("2d")!.imageSmoothingEnabled = false; 33 | this.xOffset = canvas.width / 2; 34 | this.yOffset = canvas.height / 2; 35 | const rect = this.stage.addChild(new createjs.Shape()); 36 | rect.graphics.beginFill("white").rect(0, 0, canvas.width, canvas.height); 37 | } 38 | 39 | async initAsync(msg: SimulatorRunMessage) { 40 | const sprite = await createTurtleSprite(); 41 | sprite.x = this.xOffset; 42 | sprite.y = this.yOffset; 43 | sprite.paused = true; 44 | this.turtleSprite = this.stage.addChild(sprite); 45 | // avoid flickering on start 46 | await Promise.delay(1000); 47 | } 48 | 49 | kill() { 50 | createjs.Ticker.removeEventListener("tick", this.stage as any); 51 | } 52 | 53 | set speed(s: Speed) { 54 | this.delay = delays[s]; 55 | } 56 | 57 | set penColor(color: number) { 58 | this.color = `#${("00000" + color.toString(16)).substr(-6)}`; 59 | } 60 | 61 | set turtle(visible: boolean) { 62 | this.turtleSprite!.visible = visible; 63 | } 64 | 65 | move(distance: number) { 66 | const x = this.x; 67 | const y = this.y; 68 | this.x += distance * Math.sin(this.heading * Math.PI / 180); 69 | this.y += distance * Math.cos(this.heading * Math.PI / 180); 70 | const tx = this.xOffset + this.x; 71 | const ty = this.yOffset - this.y; 72 | if (this.pen || this.turtleSprite!.visible) { 73 | const line = this.stage.addChild(new createjs.Shape()); 74 | line.visible = this.pen; 75 | const g = line.graphics 76 | .setStrokeStyle(this.penSize) 77 | .beginStroke(this.color) 78 | .moveTo(this.xOffset + x, this.yOffset - y); 79 | this.turtleToFront(); 80 | if (this.delay > 0) { 81 | const cmd = g.lineTo(this.xOffset + x, this.yOffset - y).command; 82 | const duration = this.delay * Math.abs(distance); 83 | this.turtleSprite!.play(); 84 | createjs.Tween.get(this.turtleSprite!).to({ x: tx, y: ty }, duration); 85 | return new Promise((resolve) => { 86 | createjs.Tween.get(cmd) 87 | .to({ x: tx, y: ty }, duration) 88 | .call(() => { 89 | this.turtleSprite!.gotoAndStop(0); 90 | resolve(); 91 | }); 92 | }); 93 | } 94 | g.lineTo(tx, ty).endStroke(); 95 | } 96 | this.turtleSprite!.x = tx; 97 | this.turtleSprite!.y = ty; 98 | return Promise.resolve(); 99 | } 100 | 101 | turn(angle: number) { 102 | const h = (this.heading + angle) % 360; 103 | const heading = this.heading; 104 | this.heading = h < 0 ? h + 360 : h; 105 | if (this.turtleSprite!.visible && this.delay > 0) { 106 | this.turtleSprite!.play(); 107 | return new Promise((resolve) => { 108 | createjs.Tween.get(this.turtleSprite!) 109 | .to({ rotation: heading + angle }, this.delay * 0.5 * Math.abs(angle)) 110 | .call(() => { 111 | this.turtleSprite!.gotoAndStop(0); 112 | this.turtleSprite!.rotation = this.heading; 113 | resolve(); 114 | }); 115 | }); 116 | } 117 | this.turtleSprite!.rotation = this.heading; 118 | return Promise.resolve(); 119 | } 120 | 121 | async moveTo(nx: number, ny: number, nh: number) { 122 | if (this.x !== nx || this.y !== ny) { 123 | const pen = this.pen; 124 | this.pen = false; 125 | const angle = Math.atan2(this.x - nx, this.y - ny) * 180 / Math.PI; 126 | await this.turn(normalize(angle - this.heading - 180)); 127 | await this.move(Math.sqrt((this.x - nx) ** 2 + (this.y - ny) ** 2)); 128 | this.pen = pen; 129 | } 130 | await this.turn(normalize(nh - this.heading)); 131 | this.x = nx; 132 | this.y = ny; 133 | this.heading = nh; 134 | this.turtleSprite!.x = this.xOffset + nx; 135 | this.turtleSprite!.y = this.yOffset - ny; 136 | this.turtleSprite!.rotation = nh; 137 | } 138 | 139 | async print(text: string, move: boolean) { 140 | const t = this.stage.addChild(new createjs.Text(text, `${8 + this.penSize * 2}px monospace`, this.color)); 141 | t.x = this.xOffset + this.x; 142 | t.y = this.yOffset - this.y; 143 | t.rotation = this.heading - 90; 144 | t.textBaseline = "middle"; 145 | this.turtleToFront(); 146 | if (move) { 147 | const pen = this.pen; 148 | this.pen = false; 149 | await this.move(t.getBounds().width); 150 | this.pen = pen; 151 | } 152 | } 153 | 154 | clear() { 155 | while (this.stage.numChildren > 2) { 156 | this.stage.removeChildAt(1); 157 | } 158 | } 159 | 160 | drawSprite(sprite: SpriteImpl) { 161 | const bitmap = new createjs.Bitmap(sprite.canvas); 162 | bitmap.regX = sprite.width / 2; 163 | bitmap.regY = sprite.height / 2; 164 | bitmap.x = this.xOffset + this.x; 165 | bitmap.y = this.yOffset - this.y; 166 | bitmap.rotation = this.heading; 167 | bitmap.scaleX = bitmap.scaleY = this.penSize; 168 | this.stage.addChild(bitmap); 169 | this.turtleToFront(); 170 | } 171 | 172 | private turtleToFront() { 173 | this.stage.setChildIndex(this.turtleSprite!, this.stage.numChildren - 1); 174 | } 175 | 176 | } 177 | 178 | function normalize(a: number) { 179 | a %= 360; 180 | return a > 180 ? a - 360 : a <= -180 ? a + 360 : a; 181 | } 182 | 183 | const delays = { 184 | [Speed.Normal]: 15, 185 | [Speed.Slow]: 30, 186 | [Speed.Fast]: 1, 187 | [Speed.Fastest]: 0, 188 | }; 189 | 190 | export function log(msg: string) { 191 | // tslint:disable-next-line:no-console 192 | console.log(`%c${toLocalISOString(new Date())} %c[TURTLE]`, "color: blue; font-style: italic", "font-weight: bold", msg); 193 | } 194 | 195 | function toLocalISOString(date: Date) { 196 | const modDate = new Date(); 197 | modDate.setTime(date.getTime() - date.getTimezoneOffset() * 60 * 1000); 198 | return modDate.toISOString().slice(0, -1); 199 | } 200 | 201 | export function toSprite(buffer: RefBuffer): Sprite { 202 | const width = buffer.data[2] + (buffer.data[3] << 8); 203 | const height = buffer.data[4] + (buffer.data[5] << 8); 204 | const dataHeight = Math.ceil(height / 8) * 8; 205 | const data = buffer.data.slice(8); 206 | const array = new Uint8ClampedArray(width * height * 4); 207 | for (let i = 0; i < data.length; i++) { 208 | const x = Math.floor(2 * i / dataHeight); 209 | const y = (2 * i) % dataHeight; 210 | setColor(data[i] & 0x0f, array, width, x, y); 211 | setColor(data[i] >> 4, array, width, x, y + 1); 212 | } 213 | const canvas = document.createElement("canvas"); 214 | canvas.width = width; 215 | canvas.height = height; 216 | canvas.getContext("2d")!.putImageData(new ImageData(array, width, height), 0, 0); 217 | return new SpriteImpl(canvas); 218 | } 219 | 220 | export class SpriteImpl implements Sprite { 221 | constructor(public canvas: HTMLCanvasElement) { } 222 | 223 | get width() { 224 | return this.canvas.width; 225 | } 226 | 227 | get height() { 228 | return this.canvas.height; 229 | } 230 | 231 | private getWidth() { 232 | return this.width; 233 | } 234 | 235 | private getHeight() { 236 | return this.height; 237 | } 238 | } 239 | 240 | const palette = [ 241 | [0x00, 0x00, 0x00], 242 | [0xff, 0x00, 0x00], 243 | [0xff, 0x80, 0x00], 244 | [0xff, 0xff, 0x00], 245 | [0xff, 0x9d, 0xa5], 246 | [0x00, 0xff, 0x00], 247 | [0xb0, 0x9e, 0xff], 248 | [0x00, 0xff, 0xff], 249 | [0x00, 0x7f, 0xff], 250 | [0x65, 0x47, 0x1f], 251 | [0x00, 0x00, 0xff], 252 | [0x7f, 0x00, 0xff], 253 | [0xff, 0x00, 0x80], 254 | [0xff, 0x00, 0xff], 255 | [0x99, 0x99, 0x99], 256 | ]; 257 | 258 | function setColor(color: number, data: Uint8Array, width: number, x: number, y: number) { 259 | if (color > 0) { 260 | color -= 1; 261 | const i = 4 * (x + y * width); 262 | data[i] = palette[color][0]; 263 | data[i + 1] = palette[color][1]; 264 | data[i + 2] = palette[color][2]; 265 | data[i + 3] = 0xff; 266 | } 267 | } 268 | 269 | } 270 | --------------------------------------------------------------------------------