Index: web/client/scripts/draw.js =================================================================== --- web/client/scripts/draw.js (revision 5232) +++ web/client/scripts/draw.js (revision 5233) @@ -1,9 +1,10 @@  var GC_MAX_COUNT = 32; +var NM_PER_PIXEL = 250000000; /** * Constructs a graphic engine that processes graphic commands. - + * * @param options.comm - communication interface * @param options.canvas - HTML canvas rendering context * @param options.colors - color chart for symbolic color names @@ -13,75 +14,76 @@ this.comm = options.comm; this.canvas = options.canvas; this.colors = options.colors; + this.unit = options.unit || "mm"; this.canvas.imageSmoothingEnabled = false; + //this.canvas.globalAlpha = 0.5; this.GCs = new Array(GC_MAX_COUNT); this.gc = undefined; + this.mask = "off"; } GraphicEngine.prototype.onMessage = function (command, args) { try { switch (command) { - case "line": this.line(args[0], args[1], args[2], args[3], args[4]); + case "line": this.line(Number(args[0]), + Number(args[1]), Number(args[2]), + Number(args[3]), Number(args[4])); return; + case "rect": this.rect(Number(args[0]), + Number(args[1]), Number(args[2]), Number(args[3]), Number(args[4]), + Number(args[5])); + return; + case "fcirc": this.fillCircle(Number(args[0]), + Number(args[1]), Number(args[2]), Number(args[3])); + return; - case "clr": this.setColor(args[0], args[1]); return; - case "setXorState": this.setXor(args[0], args[1]); return; + case "clr": this.setColor(Number(args[0]), args[1]); return; + case "linwid": this.setLineWidth(Number(args[0]), Number(args[1])); + return; + case "setxor": this.setXor(Number(args[0]), Number(args[1])); return; + case "cap": this.setLineCap(Number(args[0]), args[1]); return; + case "umask": this.useMask(args[0]); return; + case "setly": this.setLayer(Number(args[0]), Number(args[1]), Number(args[2])); return; case "makeGC": this.makeGC(); return; - case "delGC": this.deleteGC(args[0]); return; + case "delGC": this.deleteGC(Number(args[0])); return; case "ready": this.ready(); return; + case "unit": this.setUnit(args[0]); return; + case "ver": this.version(Number(args[0])); return; + default: this.comm.error("unknown command: " + command); } } catch (e) { + console.log("exception occured: ", e); } } GraphicEngine.prototype.line = function (gci, x1, y1, x2, y2) { this.selectGC(gci); + this.canvas.beginPath(); this.canvas.moveTo(x1, y1); this.canvas.lineTo(x2, y2); + this.canvas.closePath(); + this.canvas.stroke(); } -GraphicEngine.prototype.selectGC = function (gci) { - if (this.gc && this.gc.gci === gci) - return; - - let gc = this.GCs[gci]; - if (!gc) { - this.comm.error("invalid GC index"); - throw new Error("invalid GC index"); +GraphicEngine.prototype.rect = function (gci, x1, y1, x2, y2, filled) { + this.selectGC(gci); + if (filled == 1) { + this.canvas.fillRect(x1, y1, x2 - x1, y2 - y1); + } else { + var px = NM_PER_PIXEL / nanometerPerUnit(this.unit); + this.canvas.save(); + this.canvas.lineWidth = px; + this.canvas.strokeRect(x1, y1, x2 - x1, y2 - y1); + this.canvas.restore(); } - this.gc = gc; - - // set all drawing properties in advance - // it can be costly so optimization may be necessary - let canvas = this.canvas; - canvas.fillStyle = gc.color; - canvas.strokeStyle = gc.color; - canvas.lineWidth = gc.lineWidth; } -GraphicEngine.prototype.makeGC = function () { - for (var i = 1; i < GC_MAX_COUNT; ++i) { - if (!this.GCs[i]) { - this.GCs[i] = { - gci: i, - color: 0, - lineWidth: 1 - }; - this.comm.send("MadeGC", [i]); - return; - } - } - this.comm.error("too many GC"); +GraphicEngine.prototype.fillCircle = function (gci, x1, y1, r) { + this.selectGC(gci); + this.canvas.beginPath(); + this.canvas.arc(x1, y1, r, 0, 2 * Math.PI); + this.canvas.closePath(); + this.canvas.fill(); } -GraphicEngine.prototype.deleteGC = function (gci) { - if (!this.GCs[gci]) { - this.comm.error("undefined GC"); - return; - } - this.GCs[gci] = undefined; -} -GraphicEngine.prototype.ready = function () { - this.comm.send("Ready", []); -} GraphicEngine.prototype.setColor = function (gci, color) { gc = this.GCs[gci]; if (!gc) { @@ -91,27 +93,42 @@ if (typeof color === "string" && color[0] !== '#') { color = this.colors[color]; if (color == null) { - this.comm.error("undefied color"); + this.comm.error("undefined color: " + color); return; } } gc.color = color; - if (this.gc == gc) { + if (this.gc === gc) { this.canvas.fillStyle = color; this.canvas.strokeStyle = color; } } -GraphicEngine.prototype.setCap = function (gci, cap) { +GraphicEngine.prototype.setLineWidth = function (gci, width) { gc = this.GCs[gci]; if (!gc) { this.comm.error("invalid GC"); return; } - gc.cap = cap; - if (this.gc == gc) { + width = width; + gc.lineWidth = width; + if (this.gc === gc) { + this.canvas.lineWidth = width; } } +GraphicEngine.prototype.useMask = function (mode) { + this.mask = mode; + // hack - more info needed about this + if (this.gc) { + if (mode === "clear") { + this.canvas.fillStyle = "white"; + this.canvas.strokeStyle = "white"; + } else { + this.canvas.fillStyle = this.gc.color; + this.canvas.strokeStyle = this.gc.color; + } + } +} GraphicEngine.prototype.setXor = function (gci, state) { gc = this.GCs[gci]; if (!gc) { @@ -119,6 +136,113 @@ return; } gc.xor = state != 0; - if (this.gc == gc) { + if (this.gc === gc) { } } +GraphicEngine.prototype.setLineCap = function (gci, cap) { + gc = this.GCs[gci]; + if (!gc) { + this.comm.error("invalid GC"); + return; + } + var cap = capToLineCap(cap); + if (!cap) { + this.comm.error("invalid cap: " + cap); + return; + } + gc.lineCap = cap; + if (this.gc === gc) { + this.canvas.lineCap = gc.lineCap; + } +} +GraphicEngine.prototype.setLayer = function (gci, groups, empty) { + // don't need to do anything so far +} +GraphicEngine.prototype.makeGC = function () { + for (var i = 1; i < GC_MAX_COUNT; ++i) { + if (!this.GCs[i]) { + this.GCs[i] = { + gci: i, + color: 0, + lineWidth: 1, + lineCap: "round" + }; + this.comm.send("MadeGC", [i]); + return; + } + } + this.comm.error("too many GC"); +} +GraphicEngine.prototype.deleteGC = function (gci) { + if (!this.GCs[gci]) { + this.comm.error("undefined GC"); + return; + } + this.GCs[gci] = undefined; +} +GraphicEngine.prototype.selectGC = function (gci) { + if (this.gc && this.gc.gci === gci) + return; + + let gc = this.GCs[gci]; + if (!gc) { + this.comm.error("invalid GC index"); + throw new Error("invalid GC index"); + } + this.gc = gc; + + // set all drawing properties in advance + // it can be costly so optimization may be necessary + this.canvas.lineWidth = gc.lineWidth; + this.canvas.lineCap = gc.lineCap; + + if (this.mask === "clear") { + this.canvas.fillStyle = "white"; + this.canvas.strokeStyle = "white"; + } else { + this.canvas.fillStyle = gc.color; + this.canvas.strokeStyle = gc.color; + } +} +GraphicEngine.prototype.setMatrix = function() { + var scale = nanometerPerUnit(this.unit) / NM_PER_PIXEL * 8; + this.canvas.setTransform(1, 0, 0, 1, 0, 0); + this.canvas.scale(scale, scale); +} +GraphicEngine.prototype.ready = function () { + this.setMatrix(); + this.comm.send("Ready", []); +} +GraphicEngine.prototype.version = function (ver) { + if (Number(ver) !== 1) { + this.comm.error("unsupported protocol version: " + ver); + this.comm.disconnect(); + } +} +GraphicEngine.prototype.setUnit = function (unit) { + if (!nanometerPerUnit(unit)) { + this.comm.error("unknown unit: " + unit); + return; + } + this.unit = unit; +} + + +function nanometerPerUnit(unit) { + switch (unit) { + case "nm": return 1; + case "mm": return 1000000000; + case "mil": return 25400000; + default: return undefined; + } +} + + +function capToLineCap(cap) { + switch (cap) { + case "r": return "round"; + case "b": return "round"; // octagon cap is not supported yet + case "s": return "square"; + default: return undefined; + } +} \ No newline at end of file