mrscheme
Version:
Scheme didactic subset interpreter in JavaScript.
551 lines (468 loc) • 17.5 kB
JavaScript
export const CanvasLib = {};
import { TypeFun, TypeReal, makeTypeBool } from "./type101.mjs";
import { M$ } from './message101.mjs';
import { primitiveTypePredicate } from './prims101.mjs';
import { jQuery as $ } from './jquerylight.mjs';
CanvasLib.installPrimEnv = function(penv) {
/* graphiques cf. VideoScm/src/Book/book */
penv.register("empty-image",new CanvasLib.primitiveEmptyImage());
penv.register("filled-triangle",new CanvasLib.primitiveFillTriangle());
// invert
penv.register("line",new CanvasLib.primitiveDrawLine());
penv.register("overlay",new CanvasLib.primitiveOverlay());
penv.register("quarter-turn-right", new CanvasLib.primitiveTurn(270));
// stack
// image?
penv.register("image?", new primitiveTypePredicate("image?", "image"));
// image-width
//penv.register("image-width", new CanvasLib.primitiveImageWidth());
// image-height
// resize-image
// Still bugs in mirror-image:
//penv.register("mirror-image", new CanvasLib.primitiveSymetric());
/* other names */
penv.register("draw-line",new CanvasLib.primitiveDrawLine());
penv.register("fill-triangle",new CanvasLib.primitiveFillTriangle());
penv.register("draw-ellipse",new CanvasLib.primitiveDrawEllipse());
penv.register("fill-ellipse",new CanvasLib.primitiveFillEllipse());
/* nouveaux noms */
penv.register("image-vide",new CanvasLib.primitiveEmptyImage());
penv.register("superposition",new CanvasLib.primitiveOverlay());
penv.register("ligne",new CanvasLib.primitiveDrawLine());
penv.register("triangle",new CanvasLib.primitiveFillTriangle());
penv.register("ellipse-contour",new CanvasLib.primitiveDrawEllipse());
penv.register("contour-ellipse",new CanvasLib.primitiveDrawEllipse());
penv.register("ellipse",new CanvasLib.primitiveFillEllipse());
penv.register("quart-de-tour-gauche", new CanvasLib.primitiveTurn(90));
//penv.register("symetrique", new CanvasLib.primitiveSymetric());
};
// manque image-hauteur, image-largeur, resize, invert, stack, image?
CanvasLib.TypeImage = function () {
this.type = "Image";
this.toString = function() {
return "Image";
}
this.show = function() {
return "image"; // for users
}
this.convert = function(other,mvars) {
if(other.type=="Image") {
return this;
} else if(other.type=="Option") {
return other.convert(this,mvars);
} else if(other.type=="Sum") {
return other.convert(this,mvars);
} else if(other.type=="Var") {
return mvars.convertVar(this,other.ref);
} else {
return null;
}
}
this.updateVars = function(mvars,trans) {
return new CanvasLib.TypeImage();
}
this.normalize = function(mvars) {
return new CanvasLib.TypeImage();
}
};
CanvasLib.NbImages = 0;
CanvasLib.ImageValue = function(image) {
this.type = "image";
this.isNumber = false;
this.image = image;
this.id = CanvasLib.NbImages;
CanvasLib.NbImages++;
this.copyCount = 0;
this.equal = function(other) {
if(other==null || other==undefined) {
return false;
}
if(other.type!="image") {
return false;
}
return this.id==other.id; // id comparison
}
this.toString = function() {
var str = "image"+this.id;
return str;
}
this.contentToHTML = function() {
this.divId = 'image'+this.id+'-'+this.copyCount;
this.copyCount++;
var html = '<div id="'+this.divId+'"></div>';
this.image.containerId = this.divId;
return html;
}
this.afterOutput = function() {
this.image.draw();
}
this.toHTML = function() {
return '<span class="value">'+this.contentToHTML()+'<span class="tooltip">type <strong>Image</strong></span></span>';
}
};
// primitives
CanvasLib.primitiveEmptyImage = function() {
this.typeRepr = new TypeFun(new Array(),new CanvasLib.TypeImage());
this.arity = 0;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
return new CanvasLib.ImageValue(new CanvasLib.EmptyImage());
}
}
CanvasLib.primitiveImageWidth = function () {
this.typeRepr = new TypeFun(
new Array(new CanvasLib.TypeImage()),
new CanvasLib.TypeImage());
this.arity = 1;
this.nary = false;
this.exec = function (evaluator, lexenv, expr, args) {
if ( args[0].type != "image" ) {
return new PrimitiveError(
expr.get(1), "image-width", args,
M$("Expecting an image") );
}
return new IntegerValue(image.value);
};
};
CanvasLib.primitiveTurn = function (turn=270) {
this.typeRepr = new TypeFun(
new Array(new CanvasLib.TypeImage()),
makeTypeBool());
this.arity = 1;
this.nary = false;
this.exec = function (evaluator, lexenv, expr, args) {
if ( args[0].type != "image" ) {
return new PrimitiveError(
expr.get(1), "quart-de-tour", args,
M$("Expecting an image") );
}
let image = new CanvasLib.TurnImage(args[0].image, turn);
return new CanvasLib.ImageValue(image);
};
};
CanvasLib.primitiveSymetric = function () {
this.typeRepr = new TypeFun(
new Array(new CanvasLib.TypeImage()),
new CanvasLib.TypeImage());
this.arity = 1;
this.nary = false;
this.exec = function (evaluator, lexenv, expr, args) {
if ( args[0].type != "image" ) {
return new PrimitiveError(
expr.get(1), "symmetric", args,
M$("Expecting an image") );
}
let image = new CanvasLib.SymetricImage(args[0].image);
return new CanvasLib.ImageValue(image);
};
};
CanvasLib.primitiveOverlay = function() {
this.typeRepr = new TypeFun(new Array(new CanvasLib.TypeImage(),new CanvasLib.TypeImage()),new CanvasLib.TypeImage(),true);
this.arity = 2;
this.nary = true;
this.exec = function(evaluator,lexenv,expr,args) {
var image = new CanvasLib.EmptyImage();
//console.log("args",args);
for(var i=args.length-1;i>=0;i--) {
if(args[i].type!="image") {
return new PrimitiveError(expr.get(i+1),"overlay",args,M$("Expecting an image"));
}
image = new CanvasLib.OverlayImage(args[i].image,image);
}
return new CanvasLib.ImageValue(image);
}
}
CanvasLib.primitiveDrawLine = function() {
this.typeRepr = new TypeFun(new Array(new TypeReal(), new TypeReal(), new TypeReal(), new TypeReal()),new CanvasLib.TypeImage());
this.arity = 4;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
return new CanvasLib.ImageValue(new CanvasLib.DrawLineImage("",args[0].value,args[1].value,args[2].value,args[3].value));
}
}
CanvasLib.primitiveFillTriangle = function() {
this.typeRepr = new TypeFun(new Array(new TypeReal(), new TypeReal(), new TypeReal(), new TypeReal(), new TypeReal(), new TypeReal()),new CanvasLib.TypeImage());
this.arity = 6;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
return new CanvasLib.ImageValue(new CanvasLib.FillTriangleImage("",args[0].value,args[1].value,args[2].value,args[3].value,args[4].value,args[5].value));
}
}
CanvasLib.primitiveDrawEllipse = function() {
this.typeRepr = new TypeFun(new Array(new TypeReal(), new TypeReal(), new TypeReal(), new TypeReal()),new CanvasLib.TypeImage());
this.arity = 4;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
return new CanvasLib.ImageValue(new CanvasLib.DrawEllipseImage("",args[0].value,args[1].value,args[2].value,args[3].value));
}
}
CanvasLib.primitiveFillEllipse = function() {
this.typeRepr = new TypeFun(new Array(new TypeReal(), new TypeReal(), new TypeReal(), new TypeReal()),new CanvasLib.TypeImage());
this.arity = 4;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
return new CanvasLib.ImageValue(new CanvasLib.FillEllipseImage("",args[0].value,args[1].value,args[2].value,args[3].value));
}
}
// canvas engine
CanvasLib.CANVAS_ID_COUNT = 0;
CanvasLib.DEFAULT_IMAGE_WIDTH = 200;
CanvasLib.DEFAULT_IMAGE_HEIGHT = 200;
CanvasLib.DEFAULT_DRAW_COLOR = "#000";
CanvasLib.DEFAULT_FILL_COLOR = "#9cf";
CanvasLib.genCanvasId = function() {
return "image"+(CanvasLib.CANVAS_ID_COUNT++);
}
CanvasLib.ImageInfos = function(containerId, canvasId, width, height) {
this.type = "imageInfos";
this.containerId = containerId;
this.canvasId = canvasId;
this.width = width;
this.height = height;
}
CanvasLib.prepareImage = function(containerId,width,height) {
if(containerId==null) {
throw "No container: please report";
}
var nwidth = width;
if(nwidth==null) {
nwidth = CanvasLib.DEFAULT_IMAGE_WIDTH;
}
var nheight = height;
if(nheight==null) {
nheight = CanvasLib.DEFAULT_IMAGE_HEIGHT;
}
var canvasId = CanvasLib.genCanvasId();
$('#'+containerId).append('<canvas class="canvas101" id="'+canvasId+'" width='+nwidth+' height='+nheight+' /><br>');
return new CanvasLib.ImageInfos(containerId, canvasId, nwidth, nheight);
}
CanvasLib.TurnImage = function (image, turn) {
this.type = "image";
this.containerId = image.containerId;
this.image = image;
// turn is 90(turn right) or 270(turn left)
this.draw = function(canvasId,width,height) {
this.image.containerId = this.containerId;
let infos = new CanvasLib.ImageInfos(
this.containerId,canvasId,width,height);
if(canvasId==null) {
infos = CanvasLib.prepareImage(this.containerId,width,height);
}
const c = $('#'+infos.canvasId).getCanvas();
//window.lastcanvas = c; // DEBUG
c.rotateCanvas({
inDegrees: true,
angle: turn,
x: infos.width/2,
y: infos.height/2
});
this.image.draw(infos.canvasId,infos.width,infos.height);
return infos;
};
};
CanvasLib.SymetricImage = function (image) {
this.type = "image";
this.containerId = image.containerId;
this.image = image;
this.draw = function(canvasId,width,height) {
this.image.containerId = this.containerId;
let infos = new CanvasLib.ImageInfos(
this.containerId,canvasId,width,height);
if(canvasId==null) {
infos = CanvasLib.prepareImage(this.containerId,width,height);
}
const c = $('#'+infos.canvasId).getCanvas();
//window.lastcanvas = c; // DEBUG
function innerDraw () {
this.image.draw(infos.canvasId,infos.width,infos.height);
}
c.symetrizeCanvas({
width: infos.width,
height: infos.height,
symetrizeX: true,
symetrizeY: false,
innerDraw: innerDraw.bind(this)
});
return infos;
};
};
CanvasLib.OverlayImage = function(image1, image2) {
this.type = "image";
this.containerId = image1.containerId;
/*if(image2.containerId!=this.containerId) {
throw "Wrong containers";
}*/
this.image1 = image1;
this.image2 = image2;
this.draw = function(canvasId,width,height) {
this.image1.containerId = this.containerId;
this.image2.containerId = this.containerId;
var infos = this.image2.draw(canvasId,width,height);
this.image1.draw(infos.canvasId,infos.width,infos.height);
return infos;
}
}
CanvasLib.EmptyImage = function(containerId) {
this.type = "image";
this.containerId = containerId;
this.draw = function(canvasId,width,height) {
var infos = new CanvasLib.ImageInfos(this.containerId,canvasId,width,height);
if(canvasId==null) {
infos = CanvasLib.prepareImage(this.containerId,width,height);
}
return infos;
}
}
CanvasLib.normalizeX = function(x,width) {
var nx = x;
//if(nx<-1.0) nx = -1.0;
//if(nx>1.0) nx = 1.0;
var result = Math.round((nx+1.0)*(width/2.0));
return result;
}
CanvasLib.normalizeY = function(y,height) {
var ny = -y;
//if(ny<-1.0) ny = -1.0;
//if(ny>1.0) ny = 1.0;
var result = Math.round((ny+1.0)*(height/2.0));
return result;
}
CanvasLib.DrawLineImage = function(containerId, xx1, yy1, xx2, yy2, color) {
this.type = "image";
this.containerId = containerId;
if(color==null) {
this.color = CanvasLib.DEFAULT_DRAW_COLOR;
} else {
this.color = color;
}
this.draw = function(canvasId,width,height) {
var infos = new CanvasLib.ImageInfos(this.containerId,canvasId,width,height);
if(canvasId==null) {
infos = CanvasLib.prepareImage(this.containerId,width,height);
}
// draw line
$('#'+infos.canvasId).getCanvas().drawLine({
strokeStyle : this.color,
x1 : CanvasLib.normalizeX(xx1, infos.width),
y1 : CanvasLib.normalizeY(yy1, infos.height),
x2 : CanvasLib.normalizeX(xx2, infos.width),
y2 : CanvasLib.normalizeY(yy2, infos.height)});
return infos;
}
}
CanvasLib.FillTriangleImage = function(containerId, xx1, yy1, xx2, yy2, xx3, yy3, color) {
this.type = "image";
this.containerId = containerId;
if(color==null) {
this.color = CanvasLib.DEFAULT_FILL_COLOR;
} else {
this.color = color;
}
this.draw = function(canvasId,width,height) {
var infos = new CanvasLib.ImageInfos(this.containerId,canvasId,width,height);
if(canvasId==null) {
infos = CanvasLib.prepareImage(this.containerId,width,height);
}
// draw line
const canvas = $('#'+infos.canvasId).getCanvas();
//window.lastcanvas = canvas; // DEBUG
canvas.drawLine({
strokeStyle : this.color,
fillStyle : this.color,
//shadowColor : "black",
//shadowBlur : 3,
//shadowX : 1,
//shadowY : 1,
x1 : CanvasLib.normalizeX(xx1, infos.width),
y1 : CanvasLib.normalizeY(yy1, infos.height),
x2 : CanvasLib.normalizeX(xx2, infos.width),
y2 : CanvasLib.normalizeY(yy2, infos.height),
x3 : CanvasLib.normalizeX(xx3, infos.width),
y3 : CanvasLib.normalizeY(yy3, infos.height)});
return infos;
}
}
CanvasLib.DrawEllipseImage = function(containerId, xx1, yy1, xx2, yy2, color) {
this.type = "image";
this.containerId = containerId;
if(color==null) {
this.color = CanvasLib.DEFAULT_DRAW_COLOR;
} else {
this.color = color;
}
this.draw = function(canvasId,width,height) {
var infos = new CanvasLib.ImageInfos(this.containerId,canvasId,width,height);
if(canvasId==null) {
infos = CanvasLib.prepareImage(this.containerId,width,height);
}
var x1 = 0;
var x2 = 0;
if(xx1<=xx2) {
x1 = CanvasLib.normalizeX(xx1, infos.width);
x2 = CanvasLib.normalizeX(xx2, infos.width);
} else {
x1 = CanvasLib.normalizeX(xx2, infos.width);
x2 = CanvasLib.normalizeX(xx1, infos.width);
}
var y1 = 0;
var y2 = 0;
if(yy1>=yy2) {
y1 = CanvasLib.normalizeY(yy1, infos.height);
y2 = CanvasLib.normalizeY(yy2, infos.height);
} else {
y1 = CanvasLib.normalizeY(yy2, infos.height);
y2 = CanvasLib.normalizeY(yy1, infos.height);
}
//console.log("draw ellipse","x1",x1,"y1",y1,"x2",x2,"y2",y2);
$('#'+infos.canvasId).getCanvas().drawEllipse({
strokeStyle : this.color,
x : x1,
y : y1,
width : (x2-x1),
height : (y2-y1),
fromCenter : false });
return infos;
}
}
CanvasLib.FillEllipseImage = function(containerId, xx1, yy1, xx2, yy2, color) {
this.type = "image";
this.containerId = containerId;
if(color==null) {
this.color = CanvasLib.DEFAULT_FILL_COLOR;
} else {
this.color = color;
}
this.draw = function(canvasId,width,height) {
var infos = new CanvasLib.ImageInfos(this.containerId,canvasId,width,height);
if(canvasId==null) {
infos = CanvasLib.prepareImage(this.containerId,width,height);
}
var x1 = 0;
var x2 = 0;
if(xx1<=xx2) {
x1 = CanvasLib.normalizeX(xx1, infos.width);
x2 = CanvasLib.normalizeX(xx2, infos.width);
} else {
x1 = CanvasLib.normalizeX(xx2, infos.width);
x2 = CanvasLib.normalizeX(xx1, infos.width);
}
var y1 = 0;
var y2 = 0;
if(yy1>=yy2) {
y1 = CanvasLib.normalizeY(yy1, infos.height);
y2 = CanvasLib.normalizeY(yy2, infos.height);
} else {
y1 = CanvasLib.normalizeY(yy2, infos.height);
y2 = CanvasLib.normalizeY(yy1, infos.height);
}
$('#'+infos.canvasId).getCanvas().drawEllipse({
strokeStyle : this.color,
fillStyle : this.color,
x : x1,
y : y1,
width : (x2-x1),
height : (y2-y1),
fromCenter : false });
return infos;
}
}