/* symbols.js graphical GUI-symbols for for morphic.js and Snap! written by Jens Mönig jens@moenig.org Copyright (C) 2021 by Jens Mönig This file is part of Snap!. Snap! is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . prerequisites: -------------- needs morphic.js credits: -------- additional symbols have been contributed by members of the Snap! open-source community, especially by Bernat Romagosa */ /*global modules, Morph, Point, radians, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// modules.symbols = '2021-March-03'; var SymbolMorph; // SymbolMorph ////////////////////////////////////////////////////////// /* I display graphical symbols, such as special letters. I have been called into existence out of frustration about not being able to consistently use Unicode characters to the same ends. Symbols can also display costumes, if one is specified in lieu of a name property, although this feature is currently not being used because of asynchronous image loading issues. */ // SymbolMorph inherits from Morph: SymbolMorph.prototype = new Morph(); SymbolMorph.prototype.constructor = SymbolMorph; SymbolMorph.uber = Morph.prototype; // SymbolMorph available symbols: SymbolMorph.prototype.names = [ 'square', 'pointRight', 'stepForward', 'gears', 'gearPartial', 'gearBig', 'file', 'fullScreen', 'grow', 'normalScreen', 'shrink', 'smallStage', 'normalStage', 'turtle', 'turtleOutline', 'stage', 'pause', 'flag', 'octagon', 'cloud', 'cloudGradient', 'cloudOutline', 'turnRight', 'turnLeft', 'turnAround', 'storage', 'poster', 'flash', 'brush', 'tick', 'checkedBox', 'rectangle', 'rectangleSolid', 'circle', 'circleSolid', 'ellipse', 'line', 'cross', 'crosshairs', 'paintbucket', 'eraser', 'pipette', 'speechBubble', 'speechBubbleOutline', 'loop', 'turnBack', 'turnForward', 'arrowUp', 'arrowUpOutline', 'arrowUpThin', 'arrowUpDownThin', 'arrowLeft', 'arrowLeftOutline', 'arrowLeftThin', 'arrowLeftRightThin', 'arrowDown', 'arrowDownOutline', 'arrowDownThin', 'arrowRight', 'arrowRightOutline', 'arrowRightThin', 'robot', 'magnifyingGlass', 'magnifierOutline', 'selection', 'polygon', 'closedBrush', 'notes', 'camera', 'location', 'footprints', 'keyboard', 'keyboardFilled', 'globe', 'globeBig', 'list', 'flipVertical', 'flipHorizontal', 'trash', 'trashFull' ]; // SymbolMorph instance creation: function SymbolMorph(name, size, color, shadowOffset, shadowColor) { this.init(name, size, color, shadowOffset, shadowColor); } SymbolMorph.prototype.init = function ( name, size, color, shadowOffset, shadowColor ) { this.isProtectedLabel = false; // participate in zebraing this.isReadOnly = true; this.name = name || 'square'; this.size = size || 50; this.shadowOffset = shadowOffset || ZERO; this.shadowColor = shadowColor || null; SymbolMorph.uber.init.call(this); this.color = color || BLACK; this.fixLayout(); this.rerender(); }; // SymbolMorph string representation: 'a SymbolMorph: "square"' SymbolMorph.prototype.toString = function () { return 'a ' + (this.constructor.name || this.constructor.toString().split(' ')[1].split('(')[0]) + ': "' + this.name + '" ' + this.bounds; }; // SymbolMorph zebra coloring: SymbolMorph.prototype.setLabelColor = function ( textColor, shadowColor, shadowOffset ) { this.shadowOffset = shadowOffset || new Point(); this.shadowColor = shadowColor; this.setColor(textColor); }; // SymbolMorph dynamic coloring: SymbolMorph.prototype.getShadowRenderColor = function () { // answer the shadow rendering color, can be overridden for my children return this.shadowColor; }; // SymbolMorph layout: SymbolMorph.prototype.setExtent = function (aPoint) { if (this.size === aPoint.y) {return; } this.changed(); this.size = aPoint.y; this.fixLayout(); this.rerender(); }; SymbolMorph.prototype.fixLayout = function () { // determine my extent this.bounds.setWidth(this.symbolWidth() + Math.abs(this.shadowOffset.x)); this.bounds.setHeight(this.size + Math.abs(this.shadowOffset.y)); }; // SymbolMorph displaying: SymbolMorph.prototype.render = function (ctx) { var sx = this.shadowOffset.x < 0 ? 0 : this.shadowOffset.x, sy = this.shadowOffset.y < 0 ? 0 : this.shadowOffset.y, x = this.shadowOffset.x < 0 ? Math.abs(this.shadowOffset.x) : 0, y = this.shadowOffset.y < 0 ? Math.abs(this.shadowOffset.y) : 0; if (this.shadowColor) { ctx.save(); ctx.translate(sx, sy); this.renderShape(ctx, this.getShadowRenderColor()); ctx.restore(); } ctx.save(); ctx.translate(x, y); this.renderShape(ctx, this.getRenderColor()); ctx.restore(); }; SymbolMorph.prototype.renderShape = function (ctx, aColor) { // private switch (this.name) { case 'square': this.renderSymbolStop(ctx, aColor); break; case 'pointRight': this.renderSymbolPointRight(ctx, aColor); break; case 'stepForward': this.renderSymbolStepForward(ctx, aColor); break; case 'gears': this.renderSymbolGears(ctx, aColor); break; case 'gearBig': this.renderSymbolGearBig(ctx, aColor); break; case 'gearPartial': this.renderSymbolGearPartial(ctx, aColor); break; case 'file': this.renderSymbolFile(ctx, aColor); break; case 'fullScreen': this.renderSymbolFullScreen(ctx, aColor); break; case 'grow': this.renderSymbolGrow(ctx, aColor); break; case 'normalScreen': this.renderSymbolNormalScreen(ctx, aColor); break; case 'smallStage': this.renderSymbolSmallStage(ctx, aColor); break; case 'normalStage': this.renderSymbolNormalStage(ctx, aColor); break; case 'shrink': this.renderSymbolShrink(ctx, aColor); break; case 'turtle': this.renderSymbolTurtle(ctx, aColor); break; case 'turtleOutline': this.renderSymbolTurtleOutline(ctx, aColor); break; case 'stage': this.renderSymbolStop(ctx, aColor); break; case 'pause': this.renderSymbolPause(ctx, aColor); break; case 'flag': this.renderSymbolFlag(ctx, aColor); break; case 'octagon': this.renderSymbolOctagon(ctx, aColor); break; case 'cloud': this.renderSymbolCloud(ctx, aColor); break; case 'cloudGradient': this.renderSymbolCloudGradient(ctx, aColor); break; case 'cloudOutline': this.renderSymbolCloudOutline(ctx, aColor); break; case 'turnRight': this.renderSymbolTurnRight(ctx, aColor); break; case 'turnLeft': this.renderSymbolTurnLeft(ctx, aColor); break; case 'turnAround': this.renderSymbolTurnAround(ctx, aColor); break; case 'storage': this.renderSymbolStorage(ctx, aColor); break; case 'poster': this.renderSymbolPoster(ctx, aColor); break; case 'flash': this.renderSymbolFlash(ctx, aColor); break; case 'brush': this.renderSymbolBrush(ctx, aColor); break; case 'tick': this.renderSymbolTick(ctx, aColor); break; case 'checkedBox': this.renderSymbolCheckedBox(ctx, aColor); break; case 'rectangle': this.renderSymbolRectangle(ctx, aColor); break; case 'rectangleSolid': this.renderSymbolRectangleSolid(ctx, aColor); break; case 'circle': this.renderSymbolCircle(ctx, aColor); break; case 'circleSolid': this.renderSymbolCircleSolid(ctx, aColor); break; case 'ellipse': this.renderSymbolCircle(ctx, aColor); break; case 'line': this.renderSymbolLine(ctx, aColor); break; case 'cross': this.renderSymbolCross(ctx, aColor); break; case 'crosshairs': this.renderSymbolCrosshairs(ctx, aColor); break; case 'paintbucket': this.renderSymbolPaintbucket(ctx, aColor); break; case 'eraser': this.renderSymbolEraser(ctx, aColor); break; case 'pipette': this.renderSymbolPipette(ctx, aColor); break; case 'speechBubble': this.renderSymbolSpeechBubble(ctx, aColor); break; case 'speechBubbleOutline': this.renderSymbolSpeechBubbleOutline(ctx, aColor); break; case 'loop': this.renderSymbolLoop(ctx, aColor); break; case 'turnBack': this.renderSymbolTurnBack(ctx, aColor); break; case 'turnForward': this.renderSymbolTurnForward(ctx, aColor); break; case 'arrowUp': this.renderSymbolArrowUp(ctx, aColor); break; case 'arrowUpOutline': this.renderSymbolArrowUpOutline(ctx, aColor); break; case 'arrowUpThin': this.renderSymbolArrowUpThin(ctx, aColor); break; case 'arrowUpDownThin': this.renderSymbolArrowUpDownThin(ctx, aColor); break; case 'arrowLeft': this.renderSymbolArrowLeft(ctx, aColor); break; case 'arrowLeftOutline': this.renderSymbolArrowLeftOutline(ctx, aColor); break; case 'arrowLeftThin': this.renderSymbolArrowLeftThin(ctx, aColor); break; case 'arrowLeftRightThin': this.renderSymbolArrowLeftRightThin(ctx, aColor); break; case 'arrowDown': this.renderSymbolArrowDown(ctx, aColor); break; case 'arrowDownOutline': this.renderSymbolArrowDownOutline(ctx, aColor); break; case 'arrowDownThin': this.renderSymbolArrowDownThin(ctx, aColor); break; case 'arrowRight': this.renderSymbolArrowRight(ctx, aColor); break; case 'arrowRightOutline': this.renderSymbolArrowRightOutline(ctx, aColor); break; case 'arrowRightThin': this.renderSymbolArrowRightThin(ctx, aColor); break; case 'robot': this.renderSymbolRobot(ctx, aColor); break; case 'magnifyingGlass': this.renderSymbolMagnifyingGlass(ctx, aColor); break; case 'magnifierOutline': this.renderSymbolMagnifierOutline(ctx, aColor); break; case 'selection': this.renderSymbolSelection(ctx, aColor); break; case 'polygon': this.renderSymbolOctagonOutline(ctx, aColor); break; case 'closedBrush': this.renderSymbolClosedBrushPath(ctx, aColor); break; case 'notes': this.renderSymbolNotes(ctx, aColor); break; case 'camera': this.renderSymbolCamera(ctx, aColor); break; case 'location': this.renderSymbolLocation(ctx, aColor); break; case 'footprints': this.renderSymbolFootprints(ctx, aColor); break; case 'keyboard': this.renderSymbolKeyboard(ctx, aColor); break; case 'keyboardFilled': this.renderSymbolKeyboardFilled(ctx, aColor); break; case 'globe': this.renderSymbolGlobe(ctx, aColor); break; case 'globeBig': this.renderSymbolGlobeBig(ctx, aColor); break; case 'list': this.renderSymbolList(ctx, aColor); break; case 'flipVertical': this.renderSymbolFlipVertical(ctx, aColor); break; case 'flipHorizontal': this.renderSymbolFlipHorizontal(ctx, aColor); break; case 'trash': this.renderSymbolTrash(ctx, aColor); break; case 'trashFull': this.renderSymbolTrashFull(ctx, aColor); break; default: throw new Error('unknown symbol name: "' + this.name + '"'); } }; SymbolMorph.prototype.symbolWidth = function () { // private var size = this.size; switch (this.name) { case 'pointRight': return Math.sqrt(size * size - Math.pow(size / 2, 2)); case 'location': return size * 0.6; case 'flash': case 'file': case 'list': return size * 0.8; case 'smallStage': case 'normalStage': return size * 1.2; case 'turtle': case 'turtleOutline': case 'stage': return size * 1.3; case 'cloud': case 'cloudGradient': case 'cloudOutline': case 'turnBack': case 'turnForward': case 'keyboard': case 'keyboardFilled': return size * 1.6; case 'turnRight': case 'turnLeft': return size / 3 * 2; case 'loop': return size * 2; default: return size; } }; SymbolMorph.prototype.renderSymbolStop = function (ctx, color) { // draw a vertically centered square ctx.fillStyle = color.toString(); ctx.fillRect(0, 0, this.symbolWidth(), this.size); }; SymbolMorph.prototype.renderSymbolPointRight = function (ctx, color) { // draw a right-pointing, equilateral triangle ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(this.symbolWidth(), Math.round(this.size / 2)); ctx.lineTo(0, this.size); ctx.lineTo(0, 0); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolStepForward = function (ctx, color) { // draw a right-pointing triangle // followed by a vertical bar var w = this.symbolWidth(), h = this.size; ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(w * 0.75, Math.round(h / 2)); ctx.lineTo(0, h); ctx.lineTo(0, 0); ctx.closePath(); ctx.fill(); ctx.fillRect( w * 0.75, 0, w * 0.25, h ); }; SymbolMorph.prototype.renderSymbolGears = function (ctx, color) { // draw gears var w = this.symbolWidth(), r = w / 2, spikes = 8, off = 8, shift = 10, angle, turn, i; ctx.fillStyle = color.toString(); ctx.beginPath(); // draw the spiked outline ctx.moveTo(w, r); angle = 360 / spikes; turn = angle * 0.5; for (i = 0; i < spikes; i += 1) { ctx.arc( r, r, r, radians(i * angle + turn), radians(i * angle + off + turn) ); ctx.arc( r, r, r * 0.7, radians(i * angle - shift + angle * 0.5 + turn), radians(i * angle + shift + angle * 0.5 + turn) ); ctx.arc( r, r, r, radians((i + 1) * angle - off + turn), radians((i + 1) * angle + turn) ); } ctx.lineTo(w, r); // draw the hole in the middle ctx.arc(r, r, r * 0.3, radians(0), radians(360)); // fill ctx.clip('evenodd'); ctx.fillRect(0, 0, w, w); }; SymbolMorph.prototype.renderSymbolGearBig = function (ctx, color) { // draw a large gear var w = this.symbolWidth(), r = w / 2, spikes = 10, off = 7, shift = 8, angle, i; ctx.fillStyle = color.toString(); ctx.beginPath(); // draw the spiked outline ctx.moveTo(w, r); angle = 360 / spikes; for (i = 0; i < spikes; i += 1) { ctx.arc( r, r, r, radians(i * angle), radians(i * angle + off) ); ctx.arc( r, r, r * 0.8, radians(i * angle - shift + angle * 0.5), radians(i * angle + shift + angle * 0.5) ); ctx.arc( r, r, r, radians((i + 1) * angle - off), radians((i + 1) * angle) ); } ctx.lineTo(w, r); // draw the holes in the middle ctx.arc(r, r, r * 0.6, radians(0), radians(360)); ctx.arc(r, r, r * 0.2, radians(0), radians(360)); // fill ctx.clip('evenodd'); ctx.fillRect(0, 0, w, w); }; SymbolMorph.prototype.renderSymbolGearPartial = function (ctx, color) { // draw gears var w = this.symbolWidth(), r = w * 0.75, spikes = 8, off = 8, shift = 10, angle, turn, i; ctx.fillStyle = color.toString(); ctx.beginPath(); // draw the spiked outline ctx.moveTo(w, r); angle = 360 / spikes; turn = angle * 0.5; for (i = 0; i < spikes; i += 1) { ctx.arc( r, r, r, radians(i * angle + turn), radians(i * angle + off + turn) ); ctx.arc( r, r, r * 0.7, radians(i * angle - shift + angle * 0.5 + turn), radians(i * angle + shift + angle * 0.5 + turn) ); ctx.arc( r, r, r, radians((i + 1) * angle - off + turn), radians((i + 1) * angle + turn) ); } ctx.lineTo(w, r); // draw the hole in the middle ctx.arc(r, r, r * 0.3, radians(0), radians(360)); // fill ctx.clip('evenodd'); ctx.fillRect(0, 0, w, w); }; SymbolMorph.prototype.renderSymbolFile = function (ctx, color) { // draw a page symbol var height = this.size, width = this.symbolWidth(), w = Math.min(width, height) / 2; ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(w, 0); ctx.lineTo(w, w); ctx.lineTo(width, w); ctx.lineTo(width, height); ctx.lineTo(0, height); ctx.closePath(); ctx.fill(); ctx.fillStyle = color.darker(25).toString(); ctx.beginPath(); ctx.moveTo(w, 0); ctx.lineTo(width, w); ctx.lineTo(w, w); ctx.lineTo(w, 0); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolFullScreen = function (ctx, color) { // draw two arrows pointing diagonally outwards var h = this.size, width = this.symbolWidth(), c = width / 2, off = width / 20, w = width / 2; ctx.strokeStyle = color.toString(); ctx.lineWidth = width / 5; ctx.beginPath(); ctx.moveTo(c - off, c + off); // ctx.lineTo(0, h); ctx.lineTo(off * 2, h - off * 2); ctx.stroke(); ctx.strokeStyle = color.toString(); ctx.lineWidth = width / 5; ctx.beginPath(); ctx.moveTo(c + off, c - off); ctx.lineTo(h - off * 2, off * 2); ctx.stroke(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(0, h); ctx.lineTo(0, h - w); ctx.lineTo(w, h); ctx.closePath(); ctx.fill(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(h, 0); ctx.lineTo(h - w, 0); ctx.lineTo(h, w); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolGrow = function (ctx, color) { var h = this.size, width = this.symbolWidth(), c = width / 2, off = width / 20, w = width / 3; function arrows() { ctx.strokeStyle = color.toString(); ctx.lineWidth = width / 7; ctx.beginPath(); ctx.moveTo(c - off * 3, c + off * 3); ctx.lineTo(off * 2, h - off * 2); ctx.stroke(); ctx.strokeStyle = color.toString(); ctx.lineWidth = width / 7; ctx.beginPath(); ctx.moveTo(c + off * 3 , c - off * 3); ctx.lineTo(h - off * 2, off * 2); ctx.stroke(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(0, h); ctx.lineTo(0, h - w); ctx.lineTo(w, h); ctx.closePath(); ctx.fill(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(h, 0); ctx.lineTo(h - w, 0); ctx.lineTo(h, w); ctx.closePath(); ctx.fill(); } // draw four arrows pointing diagonally outwards arrows(); ctx.translate(this.size, 0); ctx.rotate(radians(90)); arrows(); }; SymbolMorph.prototype.renderSymbolNormalScreen = function (ctx, color) { var h = this.size, w = this.symbolWidth(), c = w / 2, off = w / 20; ctx.strokeStyle = color.toString(); ctx.lineWidth = w / 5; ctx.beginPath(); ctx.moveTo(c - off * 3, c + off * 3); ctx.lineTo(off, h - off); ctx.stroke(); ctx.strokeStyle = color.toString(); ctx.lineWidth = w / 5; ctx.beginPath(); ctx.moveTo(c + off * 3, c - off * 3); ctx.lineTo(h - off, off); ctx.stroke(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(c + off, c - off); ctx.lineTo(w, c - off); ctx.lineTo(c + off, 0); ctx.closePath(); ctx.fill(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(c - off, c + off); ctx.lineTo(0, c + off); ctx.lineTo(c - off, w); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolShrink = function (ctx, color) { // draw 4 arrows pointing diagonally inwards var h = this.size, w = this.symbolWidth(), c = w / 2, off = w / 20; function arrows() { ctx.strokeStyle = color.toString(); ctx.lineWidth = w / 8; ctx.beginPath(); ctx.moveTo(c - off * 3, c + off * 3); ctx.lineTo(off, h - off); ctx.stroke(); ctx.strokeStyle = color.toString(); ctx.lineWidth = w / 8; ctx.beginPath(); ctx.moveTo(c + off * 3, c - off * 3); ctx.lineTo(h - off, off); ctx.stroke(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(c + off * 2, c - off * 2); ctx.lineTo(w - off, c - off * 2); ctx.lineTo(c + off * 2, 0 + off); ctx.closePath(); ctx.fill(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(c - off * 2, c + off * 2); ctx.lineTo(0 + off, c + off * 2); ctx.lineTo(c - off * 2, w - off); ctx.closePath(); ctx.fill(); } arrows(); ctx.translate(this.size, 0); ctx.rotate(radians(90)); arrows(); }; SymbolMorph.prototype.renderSymbolSmallStage = function (ctx, color) { // draw a stage toggling symbol var w = this.symbolWidth(), h = this.size, w2 = w / 2, h2 = h / 2; ctx.fillStyle = color.darker(50).toString(); ctx.fillRect(0, 0, w, h); ctx.fillStyle = color.toString(); ctx.fillRect(w2, 0, w2, h2); }; SymbolMorph.prototype.renderSymbolNormalStage = function (ctx, color) { // draw a stage toggling symbol var w = this.symbolWidth(), h = this.size, w2 = w / 2, h2 = h / 2; ctx.fillStyle = color.toString(); ctx.fillRect(0, 0, w, h); ctx.fillStyle = color.darker(50).toString(); ctx.fillRect(w2, 0, w2, h2); }; SymbolMorph.prototype.renderSymbolTurtle = function (ctx, color) { // draw a LOGO turtle var w = this.symbolWidth(), h = this.size; ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(w, h / 2); ctx.lineTo(0, h); ctx.lineTo(h / 2, h / 2); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolTurtleOutline = function (ctx, color) { // draw a LOGO turtle var w = this.symbolWidth(), h = this.size; ctx.strokeStyle = color.toString(); ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(w, h / 2); ctx.lineTo(0, h); ctx.lineTo(h / 2, h / 2); ctx.closePath(); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolPause = function (ctx, color) { // draw two parallel rectangles var w = this.symbolWidth() / 5, h = this.size; ctx.fillStyle = color.toString(); ctx.fillRect(0, 0, w * 2, h); ctx.fillRect(w * 3, 0, w * 2, h); }; SymbolMorph.prototype.renderSymbolFlag = function (ctx, color) { // draw a flag var w = this.symbolWidth(), h = this.size, l = Math.max(w / 12, 1); ctx.lineWidth = l; ctx.strokeStyle = color.toString(); ctx.beginPath(); ctx.moveTo(l / 2, 0); ctx.lineTo(l / 2, h); ctx.stroke(); ctx.lineWidth = h / 2; ctx.beginPath(); ctx.moveTo(0, h / 4); ctx.bezierCurveTo( w * 0.8, h / 4, w * 0.1, h * 0.5, w, h * 0.5 ); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolOctagon = function (ctx, color) { // draw an octagon var side = this.symbolWidth(), vert = (side - (side * 0.383)) / 2; ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(vert, 0); ctx.lineTo(side - vert, 0); ctx.lineTo(side, vert); ctx.lineTo(side, side - vert); ctx.lineTo(side - vert, side); ctx.lineTo(vert, side); ctx.lineTo(0, side - vert); ctx.lineTo(0, vert); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolCloud = function (ctx, color) { // draw a cloud var w = this.symbolWidth(), h = this.size, r1 = h * 2 / 5, r2 = h / 4, r3 = h * 3 / 10, r4 = h / 5; ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.arc(r2, h - r2, r2, radians(90), radians(259), false); ctx.arc(w / 20 * 5, h / 9 * 4, r4, radians(165), radians(300), false); ctx.arc(w / 20 * 11, r1, r1, radians(200), radians(357), false); ctx.arc(w - r3, h - r3, r3, radians(269), radians(90), false); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolCloudGradient = function (ctx, color) { // draw a cloud var w = this.symbolWidth(), h = this.size, gradient, r1 = h * 2 / 5, r2 = h / 4, r3 = h * 3 / 10, r4 = h / 5; gradient = ctx.createRadialGradient( 0, 0, 0, 0, 0, w ); gradient.addColorStop(0, color.lighter(25).toString()); gradient.addColorStop(1, color.darker(25).toString()); ctx.fillStyle = gradient; ctx.beginPath(); ctx.arc(r2, h - r2, r2, radians(90), radians(259), false); ctx.arc(w / 20 * 5, h / 9 * 4, r4, radians(165), radians(300), false); ctx.arc(w / 20 * 11, r1, r1, radians(200), radians(357), false); ctx.arc(w - r3, h - r3, r3, radians(269), radians(90), false); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolCloudOutline = function (ctx, color) { // draw cloud var w = this.symbolWidth(), h = this.size, r1 = h * 2 / 5, r2 = h / 4, r3 = h * 3 / 10, r4 = h / 5; ctx.strokeStyle = color.toString(); ctx.beginPath(); ctx.arc(r2 + 1, h - r2 - 1, r2, radians(90), radians(180), false); ctx.arc(w / 20 * 5, h / 9 * 4, r4, radians(150), radians(300), false); ctx.arc(w / 20 * 11, r1 + 1, r1, radians(210), radians(335), false); ctx.arc(w - r3 - 1, h - r3 - 1, r3, radians(280), radians(90), false); ctx.closePath(); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolTurnRight = function (ctx, color) { // draw a right-turning arrow var w = this.symbolWidth(), l = Math.max(w / 10, 1), r = w / 2; ctx.lineWidth = l; ctx.strokeStyle = color.toString(); ctx.beginPath(); ctx.arc(r, r * 2, r - l / 2, radians(0), radians(-90), false); ctx.stroke(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(w, r); ctx.lineTo(r, 0); ctx.lineTo(r, r * 2); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolTurnLeft = function (ctx, color) { // draw a left-turning arrow var w = this.symbolWidth(), l = Math.max(w / 10, 1), r = w / 2; ctx.lineWidth = l; ctx.strokeStyle = color.toString(); ctx.beginPath(); ctx.arc(r, r * 2, r - l / 2, radians(180), radians(-90), true); ctx.stroke(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(0, r); ctx.lineTo(r, 0); ctx.lineTo(r, r * 2); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolTurnAround = function (ctx, color) { // draw a right-around-turning arrow var w = this.symbolWidth(), l = Math.max(w / 10, 1), r = w / 2; ctx.lineWidth = l; ctx.strokeStyle = color.toString(); ctx.beginPath(); ctx.arc(r, r, r - l / 2, radians(-45), radians(225), false); ctx.stroke(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(0, r * 0.1); ctx.lineTo(r * 0.8, 0); ctx.lineTo(r * 0.7, r * 0.7); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolStorage = function (ctx, color) { // draw a stack of three disks var w = this.symbolWidth(), h = this.size, r = h, unit = h / 11; function drawDisk(bottom) { ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.arc(w / 2, bottom - h, r, radians(60), radians(120), false); ctx.lineTo(0, bottom - unit * 2); ctx.arc( w / 2, bottom - h - unit * 2, r, radians(120), radians(60), true ); ctx.closePath(); ctx.fill(); ctx.fillStyle = color.darker(25).toString(); ctx.beginPath(); ctx.arc( w / 2, bottom + unit * 6 + 1, r, radians(-120), // 60 radians(-60), // 120 false // true ); ctx.stroke(); } ctx.strokeStyle = color.toString(); drawDisk(h); drawDisk(h - unit * 3); drawDisk(h - unit * 6); }; SymbolMorph.prototype.renderSymbolPoster = function (ctx, color) { // draw a poster stand var w = this.symbolWidth(), h = this.size, bottom = h * 0.75, edge = h / 5; ctx.fillStyle = color.toString(); ctx.strokeStyle = color.toString(); ctx.lineWidth = w / 15; ctx.beginPath(); ctx.moveTo(w / 2, h / 3); ctx.lineTo(w / 6, h); ctx.stroke(); ctx.beginPath(); ctx.moveTo(w / 2, h / 3); ctx.lineTo(w / 2, h); ctx.stroke(); ctx.beginPath(); ctx.moveTo(w / 2, h / 3); ctx.lineTo(w * 5 / 6, h); ctx.stroke(); ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(w, 0); ctx.lineTo(w, bottom - edge); ctx.lineTo(w - edge, bottom - edge); ctx.lineTo(w - edge, bottom); ctx.lineTo(0, bottom); ctx.closePath(); ctx.fill(); ctx.fillStyle = color.darker(25).toString(); ctx.beginPath(); ctx.moveTo(w, bottom - edge); ctx.lineTo(w - edge, bottom - edge); ctx.lineTo(w - edge, bottom); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolFlash = function (ctx, color) { // draw a lightning bolt var w = this.symbolWidth(), h = this.size, w3 = w / 3, h3 = h / 3, off = h3 / 3; ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(w3, 0); ctx.lineTo(0, h3); ctx.lineTo(w3, h3); ctx.lineTo(0, h3 * 2); ctx.lineTo(w3, h3 * 2); ctx.lineTo(0, h); ctx.lineTo(w, h3 * 2 - off); ctx.lineTo(w3 * 2, h3 * 2 - off); ctx.lineTo(w, h3 - off); ctx.lineTo(w3 * 2, h3 - off); ctx.lineTo(w, 0); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolBrush = function (ctx, color) { // draw a paintbrush var w = this.symbolWidth(), h = this.size, l = Math.max(w / 30, 0.5); ctx.fillStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(w / 8 * 3, h / 2); ctx.quadraticCurveTo(0, h / 2, l, h - l); ctx.quadraticCurveTo(w / 2, h, w / 2, h / 8 * 5); ctx.closePath(); ctx.fill(); ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.strokeStyle = color.toString(); ctx.moveTo(w / 8 * 3, h / 2); ctx.lineTo(w * 0.75, l); ctx.quadraticCurveTo(w, 0, w - l, h * 0.25); ctx.stroke(); ctx.moveTo(w / 2, h / 8 * 5); ctx.lineTo(w - l, h * 0.25); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolTick = function (ctx, color) { // draw a check mark var w = this.symbolWidth(), h = this.size; ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(w * 0.2, h * 0.5); ctx.lineTo(w * 0.5, h); ctx.lineTo(w * 0.8, h * 0.3); ctx.lineTo(w, 0); ctx.lineTo(w * 0.65, h * 0.2); ctx.lineTo(w * 0.5, h * 0.65); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolCheckedBox = function (ctx, color) { // draw a rectangle with a check mark this.renderSymbolRectangle(ctx, color); this.renderSymbolTick(ctx, color); }; SymbolMorph.prototype.renderSymbolRectangle = function (ctx, color) { // draw a rectangle var w = this.symbolWidth(), h = this.size, l = Math.max(w / 20, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(l, l); ctx.lineTo(w - l, l); ctx.lineTo(w - l, h - l); ctx.lineTo(l, h - l); ctx.closePath(); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolRectangleSolid = function (ctx, color) { // draw a solid rectangle var w = this.symbolWidth(), h = this.size; ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(w, 0); ctx.lineTo(w, h); ctx.lineTo(0, h); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolCircle = function (ctx, color) { // draw a circle var w = this.symbolWidth(), l = Math.max(w / 20, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.arc(w / 2, w / 2, w / 2 - l, radians(0), radians(360), false); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolCircleSolid = function (ctx, color) { // draw a solid circle var w = this.symbolWidth(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.arc(w / 2, w / 2, w / 2, radians(0), radians(360), false); ctx.fill(); }; SymbolMorph.prototype.renderSymbolLine = function (ctx, color) { // draw a plus sign cross var w = this.symbolWidth(), h = this.size, l = Math.max(w / 20, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(l, l); ctx.lineTo(w - l, h - l); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolCross = function (ctx, color) { // draw a diagonal line var w = this.symbolWidth(), l = Math.max(w / 20, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(l, w / 2); ctx.lineTo(w - l, w / 2); ctx.stroke(); ctx.beginPath(); ctx.moveTo(w / 2, l); ctx.lineTo(w / 2, w - l); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolCrosshairs = function (ctx, color) { // draw a crosshairs var w = this.symbolWidth(), h = this.size, l = 0.5; ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(l, h / 2); ctx.lineTo(w - l, h / 2); ctx.stroke(); ctx.beginPath(); ctx.moveTo(w / 2, l); ctx.lineTo(w / 2, h - l); ctx.stroke(); ctx.beginPath(); ctx.moveTo(w / 2, h / 2); ctx.arc(w / 2, w / 2, w / 3 - l, radians(0), radians(360), false); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolPaintbucket = function (ctx, color) { // draw a paint bucket var w = this.symbolWidth(), h = this.size, n = w / 5, l = Math.max(w / 30, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(n * 2, n); ctx.lineTo(n * 4, n * 3); ctx.lineTo(n * 3, n * 4); ctx.quadraticCurveTo(n * 2, h, n, n * 4); ctx.quadraticCurveTo(0, n * 3, n, n * 2); ctx.closePath(); ctx.stroke(); ctx.lineWidth = l; ctx.moveTo(n * 2, n * 2.5); ctx.arc(n * 2, n * 2.5, l, radians(0), radians(360), false); ctx.stroke(); ctx.moveTo(n * 2, n * 2.5); ctx.lineTo(n * 2, n / 2 + l); ctx.stroke(); ctx.arc(n * 1.5, n / 2 + l, n / 2, radians(0), radians(180), true); ctx.stroke(); ctx.moveTo(n, n / 2 + l); ctx.lineTo(n, n * 2); ctx.stroke(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(n * 3.5, n * 3.5); ctx.quadraticCurveTo(w, n * 3.5, w - l, h); ctx.lineTo(w, h); ctx.quadraticCurveTo(w, n * 2, n * 2.5, n * 1.5); ctx.lineTo(n * 4, n * 3); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolEraser = function (ctx, color) { // draw an eraser var w = this.symbolWidth(), h = this.size, n = w / 4, l = Math.max(w / 20, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(n * 3, l); ctx.lineTo(l, n * 3); ctx.quadraticCurveTo(n, h, n * 2, n * 3); ctx.lineTo(w - l, n); ctx.closePath(); ctx.stroke(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(n * 3, 0); ctx.lineTo(n * 1.5, n * 1.5); ctx.lineTo(n * 2.5, n * 2.5); ctx.lineTo(w, n); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolPipette = function (ctx, color) { // draw an eyedropper var w = this.symbolWidth(), h = this.size, n = w / 4, n2 = n / 2, l = Math.max(w / 20, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(l, h - l); ctx.quadraticCurveTo(n2, h - n2, n2, h - n); ctx.lineTo(n * 2, n * 1.5); ctx.stroke(); ctx.beginPath(); ctx.moveTo(l, h - l); ctx.quadraticCurveTo(n2, h - n2, n, h - n2); ctx.lineTo(n * 2.5, n * 2); ctx.stroke(); ctx.fillStyle = color.toString(); ctx.arc(n * 3, n, n - l, radians(0), radians(360), false); ctx.fill(); ctx.beginPath(); ctx.moveTo(n * 2, n); ctx.lineTo(n * 3, n * 2); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolSpeechBubble = function (ctx, color) { // draw a speech bubble var w = this.symbolWidth(), h = this.size, n = w / 3, l = Math.max(w / 20, 0.5); ctx.fillStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(n, n * 2); ctx.quadraticCurveTo(l, n * 2, l, n); ctx.quadraticCurveTo(l, l, n, l); ctx.lineTo(n * 2, l); ctx.quadraticCurveTo(w - l, l, w - l, n); ctx.quadraticCurveTo(w - l, n * 2, n * 2, n * 2); ctx.lineTo(n / 2, h - l); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolSpeechBubbleOutline = function ( ctx, color ) { // draw a speech bubble var w = this.symbolWidth(), h = this.size, n = w / 3, l = Math.max(w / 20, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(n, n * 2); ctx.quadraticCurveTo(l, n * 2, l, n); ctx.quadraticCurveTo(l, l, n, l); ctx.lineTo(n * 2, l); ctx.quadraticCurveTo(w - l, l, w - l, n); ctx.quadraticCurveTo(w - l, n * 2, n * 2, n * 2); ctx.lineTo(n / 2, h - l); ctx.closePath(); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolLoop = function (ctx, aColor) { var w = this.symbolWidth(), h = this.size, w2 = w / 2, w4 = w2 / 2, h2 = h / 2, l = Math.max(h / 10, 0.5); ctx.lineWidth = l * 2; ctx.strokeStyle = aColor.toString(); ctx.beginPath(); ctx.moveTo(0, h - l); ctx.lineTo(w2, h - l); ctx.arc(w2, h2, h2 - l, radians(90), radians(0), true); ctx.stroke(); ctx.fillStyle = aColor.toString(); ctx.beginPath(); ctx.moveTo(w4 * 3 - l, 0); ctx.lineTo(w2 - l, h2); ctx.lineTo(w, h2); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolTurnBack = function (ctx, aColor) { var w = this.symbolWidth(), h = this.size, w2 = w / 2, h2 = h / 2, l = Math.max(w / 20, 0.5); ctx.fillStyle = aColor.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(0, h2); ctx.lineTo(w2, 0); ctx.lineTo(w2, h); ctx.closePath(); ctx.fill(); ctx.lineWidth = l * 3; ctx.strokeStyle = aColor.toString(); ctx.beginPath(); ctx.arc(w2, h, h2, radians(0), radians(-90), true); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolTurnForward = function (ctx, aColor) { var w = this.symbolWidth(), h = this.size, w2 = w / 2, h2 = h / 2, l = Math.max(w / 20, 0.5); ctx.fillStyle = aColor.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(w, h2); ctx.lineTo(w2, 0); ctx.lineTo(w2, h); ctx.closePath(); ctx.fill(); ctx.lineWidth = l * 3; ctx.strokeStyle = aColor.toString(); ctx.beginPath(); ctx.arc(w2, h, h2, radians(-180), radians(-90), false); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolArrowUp = function (ctx, color) { // draw an up arrow var w = this.symbolWidth(), h = this.size, n = w / 2, l = Math.max(w / 20, 0.5); ctx.fillStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(l, n); ctx.lineTo(n, l); ctx.lineTo(w - l, n); ctx.lineTo(w * 0.65, n); ctx.lineTo(w * 0.65, h - l); ctx.lineTo(w * 0.35, h - l); ctx.lineTo(w * 0.35, n); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolArrowUpOutline = function (ctx, color) { // draw an up arrow var w = this.symbolWidth(), h = this.size, n = w / 2, l = Math.max(w / 20, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(l, n); ctx.lineTo(n, l); ctx.lineTo(w - l, n); ctx.lineTo(w * 0.65, n); ctx.lineTo(w * 0.65, h - l); ctx.lineTo(w * 0.35, h - l); ctx.lineTo(w * 0.35, n); ctx.closePath(); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolArrowUpThin = function (ctx, color) { // draw a thin up arrow var w = this.symbolWidth(), h = this.size, n = w / 3, l = Math.max(w / 20, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(w - n, n); ctx.lineTo(w / 2, l * 2); ctx.lineTo(n, n); ctx.moveTo(w / 2, l * 2); ctx.lineTo(w / 2, h - l); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolArrowUpDownThin = function (ctx, color) { // draw a thin up-down arrow var w = this.symbolWidth(), h = this.size, n = w / 3, l = Math.max(w / 20, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(w - n, n); ctx.lineTo(w / 2, l * 2); ctx.lineTo(n, n); ctx.moveTo(w - n, h - n); ctx.lineTo(w / 2, h - l * 2); ctx.lineTo(n, h - n); ctx.moveTo(w / 2, l * 2); ctx.lineTo(w / 2, h - l * 2); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolArrowDown = function (ctx, color) { // draw a down arrow var w = this.symbolWidth(); ctx.save(); ctx.translate(w, w); ctx.rotate(radians(180)); this.renderSymbolArrowUp(ctx, color); ctx.restore(); }; SymbolMorph.prototype.renderSymbolArrowDownOutline = function (ctx, color) { // draw a down arrow var w = this.symbolWidth(); ctx.save(); ctx.translate(w, w); ctx.rotate(radians(180)); this.renderSymbolArrowUpOutline(ctx, color); ctx.restore(); }; SymbolMorph.prototype.renderSymbolArrowDownThin = function (ctx, color) { // draw a thin down arrow var w = this.symbolWidth(); ctx.save(); ctx.translate(w, w); ctx.rotate(radians(180)); this.renderSymbolArrowUpThin(ctx, color); ctx.restore(); }; SymbolMorph.prototype.renderSymbolArrowLeft = function (ctx, color) { // draw a left arrow var w = this.symbolWidth(); ctx.save(); ctx.translate(0, w); ctx.rotate(radians(-90)); this.renderSymbolArrowUp(ctx, color); ctx.restore(); }; SymbolMorph.prototype.renderSymbolArrowLeftOutline = function (ctx, color) { // draw a left arrow var w = this.symbolWidth(); ctx.save(); ctx.translate(0, w); ctx.rotate(radians(-90)); this.renderSymbolArrowUpOutline(ctx, color); ctx.restore(); }; SymbolMorph.prototype.renderSymbolArrowLeftThin = function (ctx, color) { // draw a thin left arrow var w = this.symbolWidth(); ctx.save(); ctx.translate(0, w); ctx.rotate(radians(-90)); this.renderSymbolArrowUpThin(ctx, color); ctx.restore(); }; SymbolMorph.prototype.renderSymbolArrowLeftRightThin = function (ctx, color) { // draw a thin left-right arrow var w = this.symbolWidth(); ctx.save(); ctx.translate(0, w); ctx.rotate(radians(-90)); this.renderSymbolArrowUpDownThin(ctx, color); ctx.restore(); }; SymbolMorph.prototype.renderSymbolArrowRight = function (ctx, color) { // draw a right arrow var w = this.symbolWidth(); ctx.save(); ctx.translate(w, 0); ctx.rotate(radians(90)); this.renderSymbolArrowUp(ctx, color); ctx.restore(); }; SymbolMorph.prototype.renderSymbolArrowRightOutline = function (ctx, color) { // draw a right arrow var w = this.symbolWidth(); ctx.save(); ctx.translate(w, 0); ctx.rotate(radians(90)); this.renderSymbolArrowUpOutline(ctx, color); ctx.restore(); }; SymbolMorph.prototype.renderSymbolArrowRightThin = function (ctx, color) { // draw a thin right arrow var w = this.symbolWidth(); ctx.save(); ctx.translate(w, 0); ctx.rotate(radians(90)); this.renderSymbolArrowUpThin(ctx, color); ctx.restore(); }; SymbolMorph.prototype.renderSymbolRobot = function (ctx, color) { // draw a humanoid robot var w = this.symbolWidth(), h = this.size, n = w / 6, n2 = n / 2, l = Math.max(w / 20, 0.5); ctx.fillStyle = color.toString(); //ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(n + l, n); ctx.lineTo(n * 2, n); ctx.lineTo(n * 2.5, n * 1.5); ctx.lineTo(n * 3.5, n * 1.5); ctx.lineTo(n * 4, n); ctx.lineTo(n * 5 - l, n); ctx.lineTo(n * 4, n * 3); ctx.lineTo(n * 4, n * 4 - l); ctx.lineTo(n * 2, n * 4 - l); ctx.lineTo(n * 2, n * 3); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(n * 2.75, n + l); ctx.lineTo(n * 2.4, n); ctx.lineTo(n * 2.2, 0); ctx.lineTo(n * 3.8, 0); ctx.lineTo(n * 3.6, n); ctx.lineTo(n * 3.25, n + l); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(n * 2.5, n * 4); ctx.lineTo(n, n * 4); ctx.lineTo(n2 + l, h); ctx.lineTo(n * 2, h); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(n * 3.5, n * 4); ctx.lineTo(n * 5, n * 4); ctx.lineTo(w - (n2 + l), h); ctx.lineTo(n * 4, h); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(n, n); ctx.lineTo(l, n * 1.5); ctx.lineTo(l, n * 3.25); ctx.lineTo(n * 1.5, n * 3.5); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(n * 5, n); ctx.lineTo(w - l, n * 1.5); ctx.lineTo(w - l, n * 3.25); ctx.lineTo(n * 4.5, n * 3.5); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolMagnifyingGlass = function (ctx, color) { // draw a magnifying glass var w = this.symbolWidth(), h = this.size, gradient, r = w * 0.3, x = w * 2 / 3 - Math.sqrt(r), y = h / 3 + Math.sqrt(r), l = Math.max(w / 5, 0.5); ctx.strokeStyle = color.toString(); gradient = ctx.createRadialGradient( x, y, 0, x + r, y + r, w ); gradient.addColorStop(0, color.inverted().lighter(50).toString()); gradient.addColorStop(1, color.inverted().darker(25).toString()); ctx.fillStyle = gradient; ctx.beginPath(); ctx.arc(x, y, r, radians(0), radians(360), false); ctx.fill(); ctx.lineWidth = l / 2; ctx.beginPath(); ctx.arc(x, y, r, radians(0), radians(360), false); ctx.stroke(); ctx.lineWidth = l; ctx.beginPath(); ctx.moveTo(l / 2, h - l / 2); ctx.lineTo(x - Math.sqrt(r + l), y + Math.sqrt(r + l)); ctx.closePath(); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolMagnifierOutline = function (ctx, color) { // draw a magnifying glass var w = this.symbolWidth(), h = this.size, r = w * 0.3, x = w * 2 / 3 - Math.sqrt(r), y = h / 3 + Math.sqrt(r), l = Math.max(w / 5, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 0.5; ctx.beginPath(); ctx.arc(x, y, r, radians(0), radians(360), false); ctx.stroke(); ctx.lineWidth = l; ctx.beginPath(); ctx.moveTo(l / 2, h - l / 2); ctx.lineTo(x - Math.sqrt(r + l), y + Math.sqrt(r + l)); ctx.closePath(); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolSelection = function (ctx, color) { // draw a filled arrow and a dashed rectangle var w = this.symbolWidth(), h = this.size; ctx.save(); ctx.setLineDash([3]); this.renderSymbolRectangle(ctx, color); ctx.restore(); ctx.save(); ctx.fillStyle = color.toString(); ctx.translate(0.7 * w, 0.4 * h); ctx.scale(0.5, 0.5); ctx.rotate(radians(135)); this.renderSymbolArrowDownOutline(ctx, color); ctx.fill(); ctx.restore(); }; SymbolMorph.prototype.renderSymbolOctagonOutline = function (ctx, color) { // draw an octagon var side = this.symbolWidth(), vert = (side - (side * 0.383)) / 2, l = Math.max(side / 20, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.moveTo(vert, l); ctx.lineTo(side - vert, l); ctx.lineTo(side - l, vert); ctx.lineTo(side - l, side - vert); ctx.lineTo(side - vert, side - l); ctx.lineTo(vert, side - l); ctx.lineTo(l, side - vert); ctx.lineTo(l, vert); ctx.closePath(); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolClosedBrushPath = SymbolMorph.prototype.renderSymbolCloudOutline; SymbolMorph.prototype.renderSymbolNotes = function (ctx, color) { // draw two musical notes var size = this.symbolWidth(), r = size / 6, l = Math.max(r / 3, 1); ctx.strokeStyle = color.toString(); ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.arc(r, size - r, r, radians(0), radians(360), false); ctx.fill(); ctx.beginPath(); ctx.arc(size - r, size - (r * 2), r, radians(0), radians(360), false); ctx.fill(); ctx.beginPath(); ctx.moveTo(r * 2 - l, r); ctx.lineTo(size, 0); ctx.lineTo(size, r); ctx.lineTo(r * 2 - l, r * 2); ctx.closePath(); ctx.fill(); ctx.lineWidth = l; ctx.beginPath(); ctx.moveTo(r * 2 - (l / 2), size - r); ctx.lineTo(r * 2 - (l / 2), r + l); ctx.stroke(); ctx.beginPath(); ctx.moveTo(size - (l / 2), size - (r * 2)); ctx.lineTo(size - (l / 2), l); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolCamera = function (ctx, color) { // draw a camera var w = this.symbolWidth(), h = this.size, r = w * 0.16, l = Math.max(w / 20, 0.5); ctx.lineWidth = l * 2; // camera body ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(l, h * 5 / 6); ctx.lineTo(w - l, h * 5 / 6); ctx.lineTo(w - l, h / 4); ctx.lineTo(w * 3 / 4 , h / 4); ctx.lineTo(w * 5 / 8 , l); ctx.lineTo(w * 3 / 8 , l); ctx.lineTo(w / 4 , h / 4); ctx.lineTo(l , h / 4); ctx.lineTo(l, h * 5 / 6); // camera lens ctx.arc(w / 2, h / 2, r, radians(0), radians(360), false); ctx.clip(); ctx.fillRect(0, 0, w, h); }; SymbolMorph.prototype.renderSymbolLocation = function (ctx, color) { // draw a map pin var w = this.symbolWidth(), h = this.size, r = w / 2; // pin ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(0, r); ctx.arc(r, r, r, radians(-180), radians(0), false); ctx.lineTo(r, h); ctx.lineTo(0, r); // hole ctx.arc(r, r, r * 0.5, radians(0), radians(360), false); ctx.clip('evenodd'); ctx.fillRect(0, 0, w, h); }; SymbolMorph.prototype.renderSymbolFootprints = function (ctx, color) { // draw a pair of (shoe) footprints var w = this.symbolWidth(), u = w / 10, r = u * 1.5; ctx.fillStyle = color.toString(); // left shoe // tip ctx.beginPath(); ctx.arc(r, r, r, radians(-200), radians(0), false); ctx.lineTo(r * 2, u * 5.5); ctx.lineTo(u, u * 6); ctx.closePath(); ctx.fill(); // heel ctx.beginPath(); ctx.arc(u * 2.25, u * 6.75, u , radians(-40), radians(-170), false); ctx.closePath(); ctx.fill(); // right shoe // tip ctx.beginPath(); ctx.arc(w - r, u * 4.5, r, radians(-180), radians(20), false); ctx.lineTo(w - u, u * 8.5); ctx.lineTo(w - (r * 2), u * 8); ctx.closePath(); ctx.fill(); // heel ctx.beginPath(); ctx.arc(w - (u * 2.25), u * 9, u, radians(0), radians(-150), false); ctx.closePath(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolKeyboard = function (ctx, color) { // draw a typing keyboard var h = this.size, u = h / 10, k = h / 5, row, col; ctx.fillStyle = color.toString(); for (row = 0; row < 2; row += 1) { for (col = 0; col < 5; col += 1) { ctx.fillRect( ((u + k) * col) + u, ((u + k) * row) + u, k, k ); } } ctx.fillRect(u * 4, u * 7, k * 4, k); }; SymbolMorph.prototype.renderSymbolKeyboardFilled = function (ctx, color) { // draw a typing keyboard var w = this.symbolWidth(), h = this.size, u = h / 10, k = h / 5, row, col; ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.rect(0, 0, w, h); for (row = 0; row < 2; row += 1) { for (col = 0; col < 5; col += 1) { ctx.rect( ((u + k) * col) + u, ((u + k) * row) + u, k, k ); } } ctx.rect(u * 4, u * 7, k * 4, k); ctx.clip('evenodd'); ctx.fillRect(0, 0, w, h); }; SymbolMorph.prototype.renderSymbolGlobeBig = function (ctx, color) { this.renderSymbolGlobe(ctx, color, true); }; SymbolMorph.prototype.renderSymbolGlobe = function (ctx, color, detailed) { // draw a stylized globe var w = this.symbolWidth(), l = Math.max(w / 30, 0.5); ctx.strokeStyle = color.toString(); ctx.lineWidth = l * 2; ctx.beginPath(); ctx.arc(w / 2, w / 2, w / 2 - l, radians(0), radians(360), false); ctx.stroke(); if (detailed) { ctx.moveTo(l * 3, w * 0.3); ctx.lineTo(w - l * 3, w * 0.3); ctx.stroke(); ctx.moveTo(l * 3, w * 0.7); ctx.lineTo(w - l * 3, w * 0.7); ctx.stroke(); } // single line version, looks better when small: ctx.beginPath(); ctx.moveTo(l, w / 2); ctx.lineTo(w - l, w / 2); ctx.stroke(); ctx.beginPath(); ctx.moveTo(w / 2, l / 2); ctx.arcTo(0, w / 2, w / 2, w, w * 0.75); ctx.stroke(); ctx.beginPath(); ctx.moveTo(w / 2, l / 2); ctx.arcTo(w, w / 2, w / 2, w, w * 0.75); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolList = function (ctx, color) { // draw a stylized list var w = this.symbolWidth(), h = this.size, padding = h / 10, item = h / 5, row; ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.rect(0, 0, w, h); for (row = 0; row < 4; row += 1) { ctx.rect( padding, ((padding + item) * row) + padding, w - item, item ); } ctx.clip('evenodd'); ctx.fillRect(0, 0, w, h); }; SymbolMorph.prototype.renderSymbolFlipHorizontal = function (ctx, color) { var w = this.symbolWidth(), h = this.size, c = w / 2, off = w / 15; ctx.strokeStyle = color.toString(); ctx.lineWidth = w / 15; ctx.beginPath(); ctx.moveTo(0 + off, h - off / 2); ctx.lineTo(c - off * 1.2, h - off / 2); ctx.lineTo(c - off * 1.2, off * 2); ctx.closePath(); ctx.stroke(); ctx.fillStyle = color.toString(); ctx.lineWidth = w / 15; ctx.beginPath(); ctx.moveTo(w - off, h - off / 2); ctx.lineTo(c + off * 1.2, h - off / 2); ctx.lineTo(c + off * 1.2, off * 2); ctx.closePath(); ctx.stroke(); ctx.fill(); }; SymbolMorph.prototype.renderSymbolFlipVertical = function (ctx, color) { ctx.translate(0, this.size); ctx.rotate(radians(-90)); this.renderSymbolFlipHorizontal(ctx, color); }; SymbolMorph.prototype.renderSymbolTrash = function (ctx, color) { var w = this.symbolWidth(), h = this.size, step = w / 10; function stripe(x) { var half = step / 2; ctx.moveTo(x - half, step * 4); ctx.arc(x, step * 4, half, radians(180), radians(0)); ctx.lineTo(x + half, step * 8.5); ctx.arc(x, step * 8.5, half, radians(0), radians(180)); ctx.lineTo(x - half, step * 4); } // body of the can ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(step, step * 2.5); ctx.lineTo(step * 1.5, step * 9.5); ctx.lineTo(step * 2.5, h); ctx.lineTo(step * 7.5, h); ctx.lineTo(step * 8.5, step * 9.5); ctx.lineTo(step * 9, step * 2.5); ctx.lineTo(step, step * 2.5); // vertical stripes stripe(w * 0.3); stripe(w * 0.5); stripe(w * 0.7); ctx.save(); ctx.clip(); ctx.fillRect(0, 0, w, h); ctx.restore(); // the lid ctx.lineWidth = step; ctx.lineJoin = 'round'; ctx.strokeStyle = color.toString(); ctx.lineWidth = step; ctx.beginPath(); ctx.moveTo(step / 2, step * 1.5); ctx.lineTo(step * 9.5, step * 1.5); ctx.stroke(); // the handle on the lid ctx.lineWidth = step / 2; ctx.beginPath(); ctx.moveTo(step * 3, step * 1.5); ctx.lineTo(step * 4, step * 0.25); ctx.lineTo(step * 6, step * 0.25); ctx.lineTo(step * 7, step * 1.5); ctx.stroke(); }; SymbolMorph.prototype.renderSymbolTrashFull = function (ctx, color) { var w = this.symbolWidth(), h = this.size, step = w / 10; function stripe(x) { var half = step / 2; ctx.moveTo(x - half, step * 5.5); ctx.arc(x, step * 5.5, half, radians(180), radians(0)); ctx.lineTo(x + half, step * 8.5); ctx.arc(x, step * 8.5, half, radians(0), radians(180)); ctx.lineTo(x - half, step * 5.5); } // body of the can ctx.fillStyle = color.toString(); ctx.beginPath(); ctx.moveTo(step, step * 4); ctx.lineTo(step * 1.5, step * 9.5); ctx.lineTo(step * 2.5, h); ctx.lineTo(step * 7.5, h); ctx.lineTo(step * 8.5, step * 9.5); ctx.lineTo(step * 9, step * 4); ctx.lineTo(step, step * 4); // vertical stripes stripe(w * 0.3); stripe(w * 0.5); stripe(w * 0.7); ctx.save(); ctx.clip(); ctx.fillRect(0, 0, w, h); ctx.restore(); // document ctx.beginPath(); ctx.moveTo(step * 2, 0); ctx.lineTo(step * 6, 0); ctx.lineTo(step * 8, step * 2); ctx.lineTo(step * 8, step * 3.5); ctx.lineTo(step * 2, step * 3.5); ctx.closePath(); ctx.fill(); ctx.fillStyle = color.darker(25).toString(); ctx.beginPath(); ctx.moveTo(step * 6, 0); ctx.lineTo(step * 8, step * 2); ctx.lineTo(step * 6, step * 2); ctx.closePath(); ctx.fill(); }; /* // register examples with the World demo menu // comment out to shave off a millisecond loading speed ;-) (function () { var bright = new Color(240, 240, 240), dark = new Color(20, 20, 20), offset = new Point(-1, -1); SymbolMorph.prototype.addToDemoMenu([ 'Symbols', SymbolMorph.prototype.names.map(sym => [ new SymbolMorph( sym, 30, bright, offset, dark ), sym ]) ]); })(); */