kopia lustrzana https://github.com/backface/turtlestitch
commit
2c361bc9a1
118
gui.js
118
gui.js
|
@ -1419,6 +1419,7 @@ IDE_Morph.prototype.createCorralBar = function () {
|
||||||
var padding = 5,
|
var padding = 5,
|
||||||
newbutton,
|
newbutton,
|
||||||
paintbutton,
|
paintbutton,
|
||||||
|
cambutton,
|
||||||
colors = [
|
colors = [
|
||||||
this.groupColor,
|
this.groupColor,
|
||||||
this.frameColor.darker(50),
|
this.frameColor.darker(50),
|
||||||
|
@ -1480,6 +1481,41 @@ IDE_Morph.prototype.createCorralBar = function () {
|
||||||
this.corralBar.left() + padding + newbutton.width() + padding
|
this.corralBar.left() + padding + newbutton.width() + padding
|
||||||
);
|
);
|
||||||
this.corralBar.add(paintbutton);
|
this.corralBar.add(paintbutton);
|
||||||
|
|
||||||
|
cambutton = new PushButtonMorph(
|
||||||
|
this,
|
||||||
|
"newCamSprite",
|
||||||
|
new SymbolMorph("camera", 15)
|
||||||
|
);
|
||||||
|
|
||||||
|
cambutton.corner = 12;
|
||||||
|
cambutton.color = colors[0];
|
||||||
|
cambutton.highlightColor = colors[1];
|
||||||
|
cambutton.pressColor = colors[2];
|
||||||
|
cambutton.labelMinExtent = new Point(36, 18);
|
||||||
|
cambutton.padding = 0;
|
||||||
|
cambutton.labelShadowOffset = new Point(-1, -1);
|
||||||
|
cambutton.labelShadowColor = colors[1];
|
||||||
|
cambutton.labelColor = this.buttonLabelColor;
|
||||||
|
cambutton.contrast = this.buttonContrast;
|
||||||
|
cambutton.drawNew();
|
||||||
|
cambutton.hint = "take a camera snapshot and\nimport it as a new sprite";
|
||||||
|
cambutton.fixLayout();
|
||||||
|
cambutton.setCenter(this.corralBar.center());
|
||||||
|
cambutton.setLeft(
|
||||||
|
this.corralBar.left() + padding + newbutton.width() + padding + paintbutton.width() + padding
|
||||||
|
);
|
||||||
|
|
||||||
|
if (location.protocol === 'http:') {
|
||||||
|
cambutton.hint = 'Due to browser security policies, you need to\n' +
|
||||||
|
'access Snap! through HTTPS to use the camera.\n\n' +
|
||||||
|
'Plase replace the "http://" part of the address\n' +
|
||||||
|
'in your browser by "https://" and try again.';
|
||||||
|
cambutton.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.corralBar.add(cambutton);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
IDE_Morph.prototype.createCorral = function () {
|
IDE_Morph.prototype.createCorral = function () {
|
||||||
|
@ -2140,6 +2176,30 @@ IDE_Morph.prototype.paintNewSprite = function () {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
IDE_Morph.prototype.newCamSprite = function () {
|
||||||
|
var sprite = new SpriteMorph(this.globalVariables),
|
||||||
|
camDialog,
|
||||||
|
myself = this;
|
||||||
|
|
||||||
|
sprite.name = this.newSpriteName(sprite.name);
|
||||||
|
sprite.setCenter(this.stage.center());
|
||||||
|
this.stage.add(sprite);
|
||||||
|
this.sprites.add(sprite);
|
||||||
|
this.corral.addSprite(sprite);
|
||||||
|
this.selectSprite(sprite);
|
||||||
|
|
||||||
|
camDialog = new CamSnapshotDialogMorph(
|
||||||
|
this,
|
||||||
|
sprite,
|
||||||
|
function () { myself.removeSprite(sprite); },
|
||||||
|
function (costume) {
|
||||||
|
sprite.addCostume(costume);
|
||||||
|
sprite.wearCostume(costume);
|
||||||
|
});
|
||||||
|
|
||||||
|
camDialog.popUp(this.world());
|
||||||
|
};
|
||||||
|
|
||||||
IDE_Morph.prototype.duplicateSprite = function (sprite) {
|
IDE_Morph.prototype.duplicateSprite = function (sprite) {
|
||||||
var duplicate = sprite.fullCopy();
|
var duplicate = sprite.fullCopy();
|
||||||
duplicate.setPosition(this.world().hand.position());
|
duplicate.setPosition(this.world().hand.position());
|
||||||
|
@ -7227,7 +7287,7 @@ CostumeIconMorph.prototype.removeCostume = function () {
|
||||||
var wardrobe = this.parentThatIsA(WardrobeMorph),
|
var wardrobe = this.parentThatIsA(WardrobeMorph),
|
||||||
idx = this.parent.children.indexOf(this),
|
idx = this.parent.children.indexOf(this),
|
||||||
ide = this.parentThatIsA(IDE_Morph);
|
ide = this.parentThatIsA(IDE_Morph);
|
||||||
wardrobe.removeCostumeAt(idx - 2);
|
wardrobe.removeCostumeAt(idx - 3); // ignore the paintbrush and camera buttons
|
||||||
if (ide.currentSprite.costume === this.object) {
|
if (ide.currentSprite.costume === this.object) {
|
||||||
ide.currentSprite.wearCostume(null);
|
ide.currentSprite.wearCostume(null);
|
||||||
}
|
}
|
||||||
|
@ -7500,7 +7560,8 @@ WardrobeMorph.prototype.updateList = function () {
|
||||||
icon,
|
icon,
|
||||||
template,
|
template,
|
||||||
txt,
|
txt,
|
||||||
paintbutton;
|
paintbutton,
|
||||||
|
cambutton;
|
||||||
|
|
||||||
this.changed();
|
this.changed();
|
||||||
oldFlag = Morph.prototype.trackChanges;
|
oldFlag = Morph.prototype.trackChanges;
|
||||||
|
@ -7541,9 +7602,40 @@ WardrobeMorph.prototype.updateList = function () {
|
||||||
paintbutton.setCenter(icon.center());
|
paintbutton.setCenter(icon.center());
|
||||||
paintbutton.setLeft(icon.right() + padding * 4);
|
paintbutton.setLeft(icon.right() + padding * 4);
|
||||||
|
|
||||||
|
|
||||||
this.addContents(paintbutton);
|
this.addContents(paintbutton);
|
||||||
|
|
||||||
|
cambutton = new PushButtonMorph(
|
||||||
|
this,
|
||||||
|
"newFromCam",
|
||||||
|
new SymbolMorph("camera", 15)
|
||||||
|
);
|
||||||
|
cambutton.padding = 0;
|
||||||
|
cambutton.corner = 12;
|
||||||
|
cambutton.color = IDE_Morph.prototype.groupColor;
|
||||||
|
cambutton.highlightColor = IDE_Morph.prototype.frameColor.darker(50);
|
||||||
|
cambutton.pressColor = paintbutton.highlightColor;
|
||||||
|
cambutton.labelMinExtent = new Point(36, 18);
|
||||||
|
cambutton.labelShadowOffset = new Point(-1, -1);
|
||||||
|
cambutton.labelShadowColor = paintbutton.highlightColor;
|
||||||
|
cambutton.labelColor = TurtleIconMorph.prototype.labelColor;
|
||||||
|
cambutton.contrast = this.buttonContrast;
|
||||||
|
cambutton.drawNew();
|
||||||
|
cambutton.hint = "Import a new costume from your webcam";
|
||||||
|
cambutton.setPosition(new Point(x, y));
|
||||||
|
cambutton.fixLayout();
|
||||||
|
cambutton.setCenter(paintbutton.center());
|
||||||
|
cambutton.setLeft(paintbutton.right() + padding * 4);
|
||||||
|
|
||||||
|
if (location.protocol === 'http:') {
|
||||||
|
cambutton.hint = 'Due to browser security policies, you need to\n' +
|
||||||
|
'access Snap! through HTTPS to use the camera.\n\n' +
|
||||||
|
'Plase replace the "http://" part of the address\n' +
|
||||||
|
'in your browser by "https://" and try again.';
|
||||||
|
cambutton.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addContents(cambutton);
|
||||||
|
|
||||||
txt = new TextMorph(localize(
|
txt = new TextMorph(localize(
|
||||||
"costumes tab help" // look up long string in translator
|
"costumes tab help" // look up long string in translator
|
||||||
));
|
));
|
||||||
|
@ -7554,7 +7646,6 @@ WardrobeMorph.prototype.updateList = function () {
|
||||||
this.addContents(txt);
|
this.addContents(txt);
|
||||||
y = txt.bottom() + padding;
|
y = txt.bottom() + padding;
|
||||||
|
|
||||||
|
|
||||||
this.sprite.costumes.asArray().forEach(function (costume) {
|
this.sprite.costumes.asArray().forEach(function (costume) {
|
||||||
template = icon = new CostumeIconMorph(costume, template);
|
template = icon = new CostumeIconMorph(costume, template);
|
||||||
icon.setPosition(new Point(x, y));
|
icon.setPosition(new Point(x, y));
|
||||||
|
@ -7614,6 +7705,25 @@ WardrobeMorph.prototype.paintNew = function () {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
WardrobeMorph.prototype.newFromCam = function () {
|
||||||
|
var camDialog,
|
||||||
|
ide = this.parentThatIsA(IDE_Morph),
|
||||||
|
myself = this,
|
||||||
|
sprite = this.sprite;
|
||||||
|
|
||||||
|
camDialog = new CamSnapshotDialogMorph(
|
||||||
|
ide,
|
||||||
|
sprite,
|
||||||
|
nop,
|
||||||
|
function (costume) {
|
||||||
|
sprite.addCostume(costume);
|
||||||
|
sprite.wearCostume(costume);
|
||||||
|
myself.updateList();
|
||||||
|
});
|
||||||
|
|
||||||
|
camDialog.popUp(this.world());
|
||||||
|
};
|
||||||
|
|
||||||
// Wardrobe drag & drop
|
// Wardrobe drag & drop
|
||||||
|
|
||||||
WardrobeMorph.prototype.wantsDropOf = function (morph) {
|
WardrobeMorph.prototype.wantsDropOf = function (morph) {
|
||||||
|
|
129
objects.js
129
objects.js
|
@ -97,6 +97,7 @@ var WatcherMorph;
|
||||||
var StagePrompterMorph;
|
var StagePrompterMorph;
|
||||||
var Note;
|
var Note;
|
||||||
var SpriteHighlightMorph;
|
var SpriteHighlightMorph;
|
||||||
|
var CamSnapshotDialogMorph;
|
||||||
|
|
||||||
function isSnapObject(thing) {
|
function isSnapObject(thing) {
|
||||||
return thing instanceof SpriteMorph || (thing instanceof StageMorph);
|
return thing instanceof SpriteMorph || (thing instanceof StageMorph);
|
||||||
|
@ -9525,4 +9526,132 @@ StagePrompterMorph.prototype.mouseClickLeft = function () {
|
||||||
|
|
||||||
StagePrompterMorph.prototype.accept = function () {
|
StagePrompterMorph.prototype.accept = function () {
|
||||||
this.isDone = true;
|
this.isDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CamSnapshotDialogMorph /////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*
|
||||||
|
I am a dialog morph that lets users take a snapshot using their webcam
|
||||||
|
and use it as a costume for their sprites or a background for the Stage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// CamSnapshotDialogMorph inherits from DialogBoxMorph:
|
||||||
|
|
||||||
|
|
||||||
|
CamSnapshotDialogMorph.prototype = new DialogBoxMorph();
|
||||||
|
CamSnapshotDialogMorph.prototype.constructor = CamSnapshotDialogMorph;
|
||||||
|
CamSnapshotDialogMorph.uber = DialogBoxMorph.prototype;
|
||||||
|
|
||||||
|
// CamSnapshotDialogMorph instance creation
|
||||||
|
|
||||||
|
function CamSnapshotDialogMorph(ide, sprite, onCancel, onAccept) {
|
||||||
|
this.init(ide, sprite, onCancel, onAccept);
|
||||||
|
};
|
||||||
|
|
||||||
|
CamSnapshotDialogMorph.prototype.init = function (ide, sprite, onCancel, onAccept) {
|
||||||
|
this.ide = ide;
|
||||||
|
this.sprite = sprite;
|
||||||
|
this.padding = 10;
|
||||||
|
|
||||||
|
this.oncancel = onCancel;
|
||||||
|
this.accept = onAccept;
|
||||||
|
|
||||||
|
this.videoElement = null; // an HTML5 video element
|
||||||
|
this.videoView = new Morph(); // a morph where we'll copy the video contents
|
||||||
|
|
||||||
|
CamSnapshotDialogMorph.uber.init.call(this);
|
||||||
|
|
||||||
|
this.labelString = 'Camera';
|
||||||
|
this.createLabel();
|
||||||
|
|
||||||
|
this.buildContents();
|
||||||
|
};
|
||||||
|
|
||||||
|
CamSnapshotDialogMorph.prototype.buildContents = function () {
|
||||||
|
var myself = this,
|
||||||
|
stage = this.ide.stage;
|
||||||
|
|
||||||
|
this.videoElement = document.createElement('video');
|
||||||
|
this.videoElement.hidden = true;
|
||||||
|
document.body.appendChild(this.videoElement);
|
||||||
|
|
||||||
|
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
||||||
|
navigator.mediaDevices.getUserMedia({ video: true })
|
||||||
|
.then(function (stream) {
|
||||||
|
myself.videoElement.src = window.URL.createObjectURL(stream);
|
||||||
|
myself.videoElement.play();
|
||||||
|
myself.videoElement.stream = stream;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.videoView.setExtent(this.ide.stage.dimensions);
|
||||||
|
this.videoView.image = newCanvas(this.videoView.extent());
|
||||||
|
this.videoView.drawOn = function (aCanvas) {
|
||||||
|
var context = aCanvas.getContext('2d'),
|
||||||
|
w = this.width(),
|
||||||
|
h = this.height();
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
|
||||||
|
// Flip the image so it looks like a mirror
|
||||||
|
context.translate(w, 0);
|
||||||
|
context.scale(-1, 1);
|
||||||
|
|
||||||
|
context.drawImage(
|
||||||
|
myself.videoElement,
|
||||||
|
this.left() * -1,
|
||||||
|
this.top(),
|
||||||
|
w,
|
||||||
|
h
|
||||||
|
);
|
||||||
|
|
||||||
|
context.restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.videoView.step = function () {
|
||||||
|
this.changed();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addBody(new AlignmentMorph('column', this.padding / 2));
|
||||||
|
this.body.add(this.videoView);
|
||||||
|
this.body.fixLayout();
|
||||||
|
|
||||||
|
this.addButton('ok', 'Save');
|
||||||
|
this.addButton('cancel', 'Cancel');
|
||||||
|
|
||||||
|
this.fixLayout();
|
||||||
|
this.drawNew();
|
||||||
|
};
|
||||||
|
|
||||||
|
CamSnapshotDialogMorph.prototype.ok = function () {
|
||||||
|
var stage = this.ide.stage,
|
||||||
|
canvas = newCanvas(stage.dimensions);
|
||||||
|
|
||||||
|
context = canvas.getContext('2d');
|
||||||
|
context.translate(stage.dimensions.x, 0);
|
||||||
|
context.scale(-1, 1);
|
||||||
|
|
||||||
|
context.drawImage(
|
||||||
|
this.videoElement,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
stage.dimensions.x,
|
||||||
|
stage.dimensions.y
|
||||||
|
);
|
||||||
|
|
||||||
|
this.accept(new Costume(canvas), this.sprite.newCostumeName('camera'));
|
||||||
|
this.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
CamSnapshotDialogMorph.prototype.destroy = function () {
|
||||||
|
this.oncancel.call(this);
|
||||||
|
this.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
CamSnapshotDialogMorph.prototype.close = function () {
|
||||||
|
if (this.videoElement) {
|
||||||
|
this.videoElement.stream.getTracks()[0].stop();
|
||||||
|
this.videoElement.remove();
|
||||||
|
}
|
||||||
|
CamSnapshotDialogMorph.uber.destroy.call(this);
|
||||||
};
|
};
|
||||||
|
|
40
symbols.js
40
symbols.js
|
@ -129,7 +129,8 @@ SymbolMorph.prototype.names = [
|
||||||
'arrowRightOutline',
|
'arrowRightOutline',
|
||||||
'robot',
|
'robot',
|
||||||
'magnifyingGlass',
|
'magnifyingGlass',
|
||||||
'notes'
|
'notes',
|
||||||
|
'camera'
|
||||||
];
|
];
|
||||||
|
|
||||||
// SymbolMorph instance creation:
|
// SymbolMorph instance creation:
|
||||||
|
@ -303,6 +304,8 @@ SymbolMorph.prototype.symbolCanvasColored = function (aColor) {
|
||||||
return this.drawSymbolMagnifyingGlass(canvas, aColor);
|
return this.drawSymbolMagnifyingGlass(canvas, aColor);
|
||||||
case 'notes':
|
case 'notes':
|
||||||
return this.drawSymbolNotes(canvas, aColor);
|
return this.drawSymbolNotes(canvas, aColor);
|
||||||
|
case 'camera':
|
||||||
|
return this.drawSymbolCamera(canvas, aColor);
|
||||||
default:
|
default:
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
@ -1516,3 +1519,38 @@ SymbolMorph.prototype.drawSymbolNotes = function (canvas, color) {
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
return canvas;
|
return canvas;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SymbolMorph.prototype.drawSymbolCamera = function (canvas, color) {
|
||||||
|
// answer a canvas showing a camera
|
||||||
|
var ctx = canvas.getContext('2d'),
|
||||||
|
w = canvas.width,
|
||||||
|
h = canvas.width,
|
||||||
|
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.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// camera lens
|
||||||
|
ctx.save();
|
||||||
|
ctx.globalCompositeOperation = 'destination-out';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(w / 2, h / 2, r, radians(0), radians(360), false);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
return canvas;
|
||||||
|
};
|
||||||
|
|
25
widgets.js
25
widgets.js
|
@ -168,6 +168,7 @@ PushButtonMorph.prototype.init = function (
|
||||||
this.hint = hint || null;
|
this.hint = hint || null;
|
||||||
this.template = template || null; // for pre-computed backbrounds
|
this.template = template || null; // for pre-computed backbrounds
|
||||||
// if a template is specified, its background images are used as cache
|
// if a template is specified, its background images are used as cache
|
||||||
|
this.enabled = true;
|
||||||
|
|
||||||
// initialize inherited properties:
|
// initialize inherited properties:
|
||||||
TriggerMorph.uber.init.call(this);
|
TriggerMorph.uber.init.call(this);
|
||||||
|
@ -204,9 +205,11 @@ PushButtonMorph.prototype.mouseDownLeft = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
PushButtonMorph.prototype.mouseClickLeft = function () {
|
PushButtonMorph.prototype.mouseClickLeft = function () {
|
||||||
PushButtonMorph.uber.mouseClickLeft.call(this);
|
if (this.enabled) {
|
||||||
if (this.label) {
|
PushButtonMorph.uber.mouseClickLeft.call(this);
|
||||||
this.label.setCenter(this.center());
|
if (this.label) {
|
||||||
|
this.label.setCenter(this.center());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -479,6 +482,22 @@ PushButtonMorph.prototype.createLabel = function () {
|
||||||
this.add(this.label);
|
this.add(this.label);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// PushButtonMorph states
|
||||||
|
|
||||||
|
PushButtonMorph.prototype.disable = function () {
|
||||||
|
this.enabled = false;
|
||||||
|
this.forAllChildren(function (child) {
|
||||||
|
child.alpha = 0.3;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
PushButtonMorph.prototype.enable = function () {
|
||||||
|
this.enabled = true;
|
||||||
|
this.forAllChildren(function (child) {
|
||||||
|
child.alpha = 1;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// ToggleButtonMorph ///////////////////////////////////////////////////////
|
// ToggleButtonMorph ///////////////////////////////////////////////////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Ładowanie…
Reference in New Issue