From 8066c531fe263c9da7d9a8388a2e769b7e742861 Mon Sep 17 00:00:00 2001 From: Michael Aschauer Date: Mon, 22 Oct 2018 19:05:13 +0200 Subject: [PATCH] add various color options --- stitchcode/blocks.js | 69 +++++++++++- stitchcode/gui.js | 5 +- stitchcode/morphic.js | 222 ++++++++++++++++++++++++++++++++++++ stitchcode/objects.js | 254 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 522 insertions(+), 28 deletions(-) diff --git a/stitchcode/blocks.js b/stitchcode/blocks.js index c26c86f9..ce9606e6 100644 --- a/stitchcode/blocks.js +++ b/stitchcode/blocks.js @@ -259,5 +259,72 @@ SymbolMorph.prototype.drawSymbolFile = function (canvas, color) { ctx.fill(); return canvas; +}; + + +// Hue slot morph + +var HueSlotMorph; + +HueSlotMorph.prototype = new ColorSlotMorph(); +HueSlotMorph.prototype.constructor = HueSlotMorph; +HueSlotMorph.uber = ColorSlotMorph.prototype; + +function HueSlotMorph(clr) { + this.init(clr); } -; + +HueSlotMorph.prototype.init = function (clr) { + HueSlotMorph.uber.init.call(this, null, true); // silently + this.setColor(clr || new Color(127.5, 0, 0)); +}; + +HueSlotMorph.prototype.getUserColor = function () { + var myself = this, + world = this.world(), + hand = world.hand, + posInDocument = getDocumentPositionOf(world.worldCanvas), + mouseMoveBak = hand.processMouseMove, + mouseDownBak = hand.processMouseDown, + mouseUpBak = hand.processMouseUp, + pal = new HueWheelMorph(null, new Point( + this.fontSize * 12, + this.fontSize * 12 + )); + world.add(pal); + pal.setPosition(this.bottomLeft().add(new Point(0, this.edge))); + pal.addShadow(new Point(2, 2), 80); + + hand.processMouseMove = function (event) { + hand.setPosition(new Point( + event.pageX - posInDocument.x, + event.pageY - posInDocument.y + )); + myself.setColor(world.getGlobalPixelColor(hand.position())); + }; + + hand.processMouseDown = nop; + + hand.processMouseUp = function () { + pal.destroy(); + hand.processMouseMove = mouseMoveBak; + hand.processMouseDown = mouseDownBak; + hand.processMouseUp = mouseUpBak; + }; +}; + +// labelPart() proxy +SyntaxElementMorph.prototype.originalLabelPart = SyntaxElementMorph.prototype.labelPart; +SyntaxElementMorph.prototype.labelPart = function (spec) { + var part; + switch (spec) { + case '%huewheel': + part = new HueSlotMorph(); + part.isStatic = true; + break; + default: + part = this.originalLabelPart(spec); + break; + } + return part; +}; diff --git a/stitchcode/gui.js b/stitchcode/gui.js index 766a9fcb..ed959ca6 100644 --- a/stitchcode/gui.js +++ b/stitchcode/gui.js @@ -674,8 +674,7 @@ IDE_Morph.prototype.aboutTurtleStitch = function () { function () { window.open('http://www.turtlestitch.com', 'TurtleStitchWebsite'); }, - 'TurtleStitch! website', - + 'TurtleStitch Web Site', ); dlg.fixLayout(); }; @@ -1812,7 +1811,7 @@ IDE_Morph.prototype.snapMenu = function () { } ); menu.addItem( - 'TurtleStitch! website', + 'TurtleStitch Web Site', function () { window.open('http://www.turtlestitch.com', 'TurtleStitchWebsite'); } diff --git a/stitchcode/morphic.js b/stitchcode/morphic.js index 3488b0d7..7f3a4c9b 100644 --- a/stitchcode/morphic.js +++ b/stitchcode/morphic.js @@ -29,3 +29,225 @@ Morph.fromImageURL = function(url) { return m; } + +// ColorPaletteMorph /////////////////////////////////////////////////// + +var ColorPaletteMorph; + +// ColorPaletteMorph inherits from Morph: + +ColorPaletteMorph.prototype = new Morph(); +ColorPaletteMorph.prototype.constructor = ColorPaletteMorph; +ColorPaletteMorph.uber = Morph.prototype; + +// ColorPaletteMorph instance creation: + +function ColorPaletteMorph(target, sizePoint) { + this.init( + target || null, + sizePoint || new Point(80, 50) + ); +} + +ColorPaletteMorph.prototype.init = function (target, size) { + ColorPaletteMorph.uber.init.call(this); + this.target = target; + this.targetSetter = 'color'; + this.silentSetExtent(size); + this.choice = null; + this.drawNew(); +}; + +ColorPaletteMorph.prototype.drawNew = function () { + var context, ext, x, y, h, l, colors; + + ext = this.extent(); + this.image = newCanvas(this.extent()); + context = this.image.getContext('2d'); + this.choice = new Color(); + colors = ['rgb(0, 0,0)', //black + 'rgb(128, 128, 128)', //gray + 'rgb(192, 192, 192)', //silver + 'rgb(255, 255, 255)', //white + 'rgb(139, 69, 19)', //saddlebrown + 'rgb(128, 0, 0)', //maroon + 'rgb(255, 0, 0)', //red + 'rgb(255, 192, 203)', //pink + 'rgb(255, 165, 0)', //orange + 'rgb(210, 105, 30)', //chocolate + 'rgb(255, 255, 0)', //yellow + 'rgb(128, 128, 0)', //olive + 'rgb(0, 255, 0)', //lime + 'rgb(0, 128, 0)', //green + 'rgb(0, 255, 255)', //aqua + 'rgb(0, 128, 128)', //teal + 'rgb(0, 0, 255)', //blue + 'rgb(0, 0, 128)', //navy + 'rgb(128, 0, 128)', //purple + 'rgb(255, 0, 255)' //magenta + ]; + // HSL palette (with saturation = 100%) + for (x = 0; x <= ext.x; x++) { + h = 360 * x / ext.x; + for (y = 0; y <= ext.y - 30; y++) { + l = 100 - (y / (ext.y - 30) * 100); + context.fillStyle = 'hsl(' + h + ',100%,' + l + '%)'; + context.fillRect(x, y, 1, 1); + } + } + // Gray scale + for (x = 0; x <= ext.x; x++) { + l = 100 - (x/ ext.x * 100); + context.fillStyle = 'hsl(0, 0%, ' + l + '%)'; + context.fillRect(x, ext.y - 30, 1, 10); + } + // 20 colors palette (two rows) + for (x = 0; x < 20; x++) { + context.fillStyle = colors[x]; + if (x % 2 == 0) { + context.fillRect((x / 2) * ext.x / 10, ext.y - 20, ext.x / 10, 10); + } else { + context.fillRect((Math.round(x / 2) - 1) * ext.x / 10, ext.y - 10, ext.x / 10, 10); + } + } +}; + +ColorPaletteMorph.prototype.mouseMove = function (pos) { + this.choice = this.getPixelColor(pos); + this.updateTarget(); +}; + +ColorPaletteMorph.prototype.mouseDownLeft = function (pos) { + this.choice = this.getPixelColor(pos); + this.updateTarget(); +}; + +ColorPaletteMorph.prototype.updateTarget = function () { + if (this.target instanceof Morph && this.choice !== null) { + if (this.target[this.targetSetter] instanceof Function) { + this.target[this.targetSetter](this.choice); + } else { + this.target[this.targetSetter] = this.choice; + this.target.drawNew(); + this.target.changed(); + } + } +}; + +// ColorPaletteMorph menu: + +ColorPaletteMorph.prototype.developersMenu = function () { + var menu = ColorPaletteMorph.uber.developersMenu.call(this); + menu.addLine(); + menu.addItem( + 'set target', + "setTarget", + 'choose another morph\nwhose color property\n will be' + + ' controlled by this one' + ); + return menu; +}; + +ColorPaletteMorph.prototype.setTarget = function () { + var choices = this.overlappedMorphs(), + menu = new MenuMorph(this, 'choose target:'), + myself = this; + + choices.push(this.world()); + choices.forEach(function (each) { + menu.addItem(each.toString().slice(0, 50), function () { + myself.target = each; + myself.setTargetSetter(); + }); + }); + if (choices.length === 1) { + this.target = choices[0]; + this.setTargetSetter(); + } else if (choices.length > 0) { + menu.popUpAtHand(this.world()); + } +}; + +ColorPaletteMorph.prototype.setTargetSetter = function () { + var choices = this.target.colorSetters(), + menu = new MenuMorph(this, 'choose target property:'), + myself = this; + + choices.forEach(function (each) { + menu.addItem(each, function () { + myself.targetSetter = each; + }); + }); + if (choices.length === 1) { + this.targetSetter = choices[0]; + } else if (choices.length > 0) { + menu.popUpAtHand(this.world()); + } +}; + + +// Hue Wheel + +var HueWheelMorph; + +// ColorPaletteMorph inherits from Morph: + +HueWheelMorph.prototype = new ColorPaletteMorph(); +HueWheelMorph.prototype.constructor = HueWheelMorph; +HueWheelMorph.uber = ColorPaletteMorph.prototype; + +// ColorPaletteMorph instance creation: + +function HueWheelMorph(target, sizePoint) { + this.init( + target || null, + sizePoint || new Point(80, 50) + ); +}; + +HueWheelMorph.prototype.drawNew = function () { + var context, ext, x, y, radius; + + ext = this.extent(); + this.image = newCanvas(this.extent()); + context = this.image.getContext('2d'); + this.choice = new Color(); + x = this.image.width / 2 + 2; + y = this.image.height / 2; + radius = this.image.width / 2 - 22; + + context.font = '9px Arial'; + context.fillStyle = 'rgb(200,200,200)'; + context.fillRect(0, 0, this.image.width, this.image.height); + context.strokeRect(0, 0, this.image.width, this.image.height); + + context.textAlign = 'center'; + context.textBaseline = 'middle'; + + for (var angle = 360; angle > 0; angle --) { + var startAngle = (angle - 1) * Math.PI/180; + var endAngle = (angle + 1) * Math.PI/180; + context.beginPath(); + context.moveTo(x, y); + context.arc(x, y, radius, startAngle, endAngle, false); + context.closePath(); + context.fillStyle = 'hsl(' + angle + ', 100%, 50%)'; + context.fill(); + + if (angle % 30 == 0) { + var tx = x + (radius + 12) * Math.cos(radians(angle)), + ty = y + (radius + 12) * Math.sin(radians(angle)); + + context.fillStyle = 'rgb(10,10,10)'; + + if (angle % 90 == 0) { + context.fillText(angle % 360 + '°', tx, ty); + } else { + context.beginPath() + context.moveTo(tx, ty); + context.lineTo(tx + 5 * Math.cos(radians(angle)), ty + 5 * Math.sin(radians(angle))); + context.stroke(); + } + } + } +}; diff --git a/stitchcode/objects.js b/stitchcode/objects.js index 057b9f51..496d1d2a 100644 --- a/stitchcode/objects.js +++ b/stitchcode/objects.js @@ -1,7 +1,32 @@ /* Sprite */ // modified SpriteMorph turtlestitch functions +/* +SpriteMorph.prototype.categories = + [ + 'motion', + 'control', + 'sensing', + 'operators', + 'pen', + 'variables', + 'colors', + 'my blocks' + ]; +SpriteMorph.prototype.blockColor = { + motion : new Color(74, 108, 212), + pen : new Color(143, 86, 227), + colors : new Color(32, 128, 54), + control : new Color(230, 168, 34), + sensing : new Color(4, 148, 220), + operators : new Color(98, 194, 19), + variables : new Color(243, 118, 29), + lists : new Color(217, 77, 17), + other : new Color(150, 150, 150), + 'my blocks': new Color(150, 150, 150), +}; +*/ SpriteMorph.prototype.origInit = SpriteMorph.prototype.init; SpriteMorph.prototype.init = function(globals) { @@ -11,6 +36,7 @@ SpriteMorph.prototype.init = function(globals) { this.turtle = null; this.isDown = true; this.cache = new Cache; + }; SpriteMorph.prototype.addStitch = function(x1, y1, x2, y2) { @@ -529,6 +555,77 @@ SpriteMorph.prototype.setHeading = function (degrees) { stage.rotateTurtle(this.heading); }; + +SpriteMorph.prototype.setColorRGB = function (r,g,b) { + var a = this.color.a; + r = Math.max(Math.min(r, 255), 0); + b = Math.max(Math.min(b, 255), 0); + g = Math.max(Math.min(g, 255), 0); + this.setColor(new Color(r, g, b, a)); +}; + +SpriteMorph.prototype.pickHue = function (value) { + this.setColor(value); +}; + + +SpriteMorph.prototype.setAlpha = function (a) { + this.color = aColor.copy(); + this.color.a = a; + this.setColor(this.color); +}; + +SpriteMorph.prototype.setColorHex = function (hex) { + var a = this.color.a; + // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + + hex = hex.replace(shorthandRegex, function(m, r, g, b) { + return r + r + g + g + b + b; + }); + + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + if (result) { + r = parseInt(result[1], 16); + g = parseInt(result[2], 16); + b = parseInt(result[3], 16); + this.setColor(new Color(r, g, b, a)); + } else { + // silently ignore + } +}; + +SpriteMorph.prototype.setColorHSV = function (h, s, v) { + var col = new Color(); + h = Math.max(Math.min(h, 1), 0); + s = Math.max(Math.min(s, 1), 0); + v = Math.max(Math.min(v, 1), 0); + col.set_hsv(h, s, v); + col.a = this.color.a; + this.setColor(col); +} + +SpriteMorph.prototype.getColorRGB = function (){ + return new List([this.color.r, this.color.g, this.color.b]); +} + +SpriteMorph.prototype.getColorHex = function (){ + return new String("#" + ((1 << 24) + (Math.round(this.color.r) << 16) + (Math.round(this.color.g) << 8) + + Math.round(this.color.b)).toString(16).slice(1)); +} + +SpriteMorph.prototype.getColorHSV = function (){ + return new List(this.color.hsv()); +} + +SpriteMorph.prototype.isPenDown = function (){ + return this.isDown; +} + +SpriteMorph.prototype.getPenSize = function (){ + return this.penSize(); +} + SpriteMorph.prototype.setColor = function (aColor) { var stage = this.parentThatIsA(StageMorph); if (!this.color.eq(aColor)) { @@ -536,9 +633,6 @@ SpriteMorph.prototype.setColor = function (aColor) { } stage.setColor(aColor); stage.turtleShepherd.addColorChange(aColor); - - - // TO DO: set color in turtleShepherd }; SpriteMorph.prototype.origSetHue = SpriteMorph.prototype.setHue; @@ -1017,12 +1111,10 @@ StageMorph.prototype.initTurtle = function() { }, null, null, null, false ); }; mtlloader.load( 'stitchcode/assets/turtle.mtl', onLoadMtl ); - - } this.drawingColor = new Color(0,0,0); - this.penSize = 0.8; + this.penSize = 1; }; StageMorph.prototype.moveTurtle = function(x, y) { @@ -1253,7 +1345,6 @@ SpriteMorph.prototype.initBlocks = function () { var myself = this; this.originalInitBlocks(); - // control this.blocks.resetAll = { only: SpriteMorph, @@ -1261,8 +1352,6 @@ SpriteMorph.prototype.initBlocks = function () { spec: 'reset', category: 'control' }; - - // control this.blocks.forwardBy = { only: SpriteMorph, @@ -1271,8 +1360,7 @@ SpriteMorph.prototype.initBlocks = function () { spec: 'move %n steps by %n steps', defaults: [100,10] }; - - // control + this.blocks.forwardByNr = { only: SpriteMorph, @@ -1281,8 +1369,6 @@ SpriteMorph.prototype.initBlocks = function () { spec: 'move %n steps in %n', defaults: [100,10] }; - - // control this.blocks.gotoXYBy = { only: SpriteMorph, @@ -1291,8 +1377,6 @@ SpriteMorph.prototype.initBlocks = function () { spec: 'go to x: %n y: %n by %n', defaults: [0, 0, 10] }; - - // control this.blocks.gotoXYIn = { only: SpriteMorph, @@ -1301,9 +1385,6 @@ SpriteMorph.prototype.initBlocks = function () { spec: 'go to x: %n y: %n in %n', defaults: [0, 0, 10] }; - - - // control this.blocks.pointTowards = { only: SpriteMorph, @@ -1312,8 +1393,6 @@ SpriteMorph.prototype.initBlocks = function () { spec: 'point towards x: %n y: %n', defaults: [0, 0] }; - - // control this.blocks.drawText = { only: SpriteMorph, @@ -1322,6 +1401,116 @@ SpriteMorph.prototype.initBlocks = function () { spec: 'draw text: %s scale: %n font: %n', defaults: ["hello", 2, 0] }; + + // pen blocks + this.blocks.isPenDown = + { + only: SpriteMorph, + type: 'reporter', + category: 'pen', + spec: 'is pen down', + }; + + // pen blocks + this.blocks.getPenSize = + { + only: SpriteMorph, + type: 'reporter', + category: 'pen', + spec: 'pen size', + }; + + this.blocks.setColorRGB = + { + only: SpriteMorph, + type: 'command', + category: 'pen', + spec: 'set color to RGB: %n %n %n', + defaults: [0, 255, 0] + }; + + this.blocks.setColorHex = + { + only: SpriteMorph, + type: 'command', + category: 'pen', + spec: 'set color to hex value: %s', + defaults: ['#ff0000'] + }; + + this.blocks.setColorHSV = + { + only: SpriteMorph, + type: 'command', + category: 'pen', + spec: 'set color to HSV: %n %n %n', + defaults: [0.3, 0.7, 0.6] + }; + + this.blocks.setAlpha = + { + only: SpriteMorph, + type: 'command', + category: 'pen', + spec: 'set opacity to %s', + defaults: [255] + }; + + this.blocks.getColorRGB = + { + only: SpriteMorph, + type: 'reporter', + category: 'pen', + spec: 'RGB color', + }; + + this.blocks.getColorHSV = + { + only: SpriteMorph, + type: 'reporter', + category: 'pen', + spec: 'HSV color', + }; + + this.blocks.getColorHex = + { + only: SpriteMorph, + type: 'reporter', + category: 'pen', + spec: 'hex color', + }; + + + // colors + this.blocks.pickHue = + { + type: 'command', + spec: 'set pen hue to %huewheel', + category: 'pen' + }; + + /* + this.blocks.setHSLA = + { + type: 'command', + spec: 'set %hsla to %n', + category: 'colors', + defaults: ['hue', 50] + }; + this.blocks.changeHSLA = + { + type: 'command', + spec: 'change %hsla by %n', + category: 'colors', + defaults: ['hue', 10] + }; + this.blocks.getHSLA = + { + type: 'reporter', + spec: 'color %hsla', + category: 'colors' + }; + */ }; SpriteMorph.prototype.initBlocks(); @@ -1530,16 +1719,33 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('down')); blocks.push(block('up')); + blocks.push(block('isPenDown')); + blocks.push('-'); + blocks.push(block('changeSize')); + blocks.push(block('setSize')); + blocks.push(block('getPenSize')); blocks.push('-'); blocks.push(block('setColor')); + blocks.push(block('setColorRGB')); + blocks.push(block('setColorHSV')); + blocks.push(block('setColorHex')); + blocks.push(block('getColorRGB')); + blocks.push(block('getColorHSV')); + blocks.push(block('getColorHex')); + blocks.push('-'); + blocks.push(block('pickHue')); blocks.push(block('changeHue')); blocks.push(block('setHue')); blocks.push('-'); - blocks.push(block('changeBrightness')); - blocks.push(block('setBrightness')); + + /* + } else if (cat === 'colors') { + blocks.push(block('pickHue')); blocks.push('-'); - blocks.push(block('changeSize')); - blocks.push(block('setSize')); + blocks.push(block('setHSLA')); + blocks.push(block('changeHSLA')); + blocks.push(block('getHSLA')); + */ } else if (cat === 'control') { blocks.push(block('resetAll'));