diff --git a/hw3/Claudio_Maggioni/scripts/app.js b/hw3/Claudio_Maggioni/scripts/app.js index 087f490..b0a499f 100644 --- a/hw3/Claudio_Maggioni/scripts/app.js +++ b/hw3/Claudio_Maggioni/scripts/app.js @@ -1,5 +1,13 @@ // vim: set ts=2 sw=2 et tw=80: class App { + static get BRUSHES() { + return { + "PenBrush": new PenBrush(), + "DiscBrush": new DiscBrush(), + "StarBrush": new StarBrush(), + }; + } + constructor(conf) { if (!(typeof conf === 'object' && conf)) { throw new Error('Argument conf different from specification'); @@ -15,12 +23,17 @@ class App { if (typeof conf.buttons === 'object' && conf.buttons) { this.buttons = {} - for (const b of ['clear', 'button', 'camera']) + for (const b of ['clear', 'undo', 'camera']) this.buttons[b] = document.getElementById(conf.buttons[b]); if (this.buttons.clear) - this.buttons.clear.addEventListener('click', () => - this.ctx.clearRect(0, 0, canvas.width, canvas.height)); + this.buttons.clear.addEventListener('click', () => { + this.erase(); + history.clear(); + }); + + if (this.buttons.undo) + this.buttons.undo.addEventListener('click', () => history.pop(this)); if (this.buttons.camera) this.buttons.camera.addEventListener('click', () => { @@ -34,29 +47,22 @@ class App { }); } - this.canvas.addEventListener('mousedown', (e) => { - this.ctx.save(); - this.ctx.lineWidth = 1; - this.ctx.moveTo(e.offsetX, e.offsetY); - this.ctx.beginPath(); - this.mousedown = true; - }); - - this.canvas.addEventListener('mousemove', (e) => this.mousedown && this.draw(e)); - - const endPath = (e) => { - if (this.mousedown) { - this.ctx.lineTo(e.offsetX, e.offsetY); - this.ctx.stroke(); - this.ctx.restore(); - this.mousedown = false; - } - }; - - this.canvas.addEventListener('mouseup', endPath); - this.canvas.addEventListener('mouseout', endPath); - + this.ctx.lineWidth = 1; this.strokeStyle = this.constructor.defaultStrokeStyle; + this.brush = "PenBrush"; + + const brushToolbar = document.querySelector('#brush-toolbar'); + for (const name in App.BRUSHES) { + const b = document.createElement('button'); + b.innerText = name; + b.addEventListener('click', () => this.brush = name); + brushToolbar.appendChild(b); + } + + this.canvas.addEventListener('mousedown', this.startPath.bind(this)); + this.canvas.addEventListener('mousemove', this.draw.bind(this)); + this.canvas.addEventListener('mouseup', this.endPath.bind(this)); + this.canvas.addEventListener('mouseout', this.endPath.bind(this)); } static get defaultStrokeStyle() { @@ -74,12 +80,63 @@ class App { this.ctx.strokeStyle = style; } - draw(e) { - console.log(e); - this.ctx.lineTo(e.offsetX, e.offsetY); - this.ctx.stroke(); + get brush() { + return this._brush.name; + } + + set brush(brushName) { + this._brush = App.BRUSHES[brushName]; + } + + erase() { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + } + + startPath(e, record = true) { this.ctx.beginPath(); this.ctx.moveTo(e.offsetX, e.offsetY); + + if (record) { + history.initializeNewPath(); + history.push(new Stroke(this.brush, e.offsetX, e.offsetY)); + } + + this.mousedown = true; + } + + draw(e, beginNew = true, record = true) { + if (this.mousedown) { + this._brush.draw(this.ctx, this.strokeStyle, e.offsetX, e.offsetY); + + if (record) { + history.push(new Stroke(this.brush, e.offsetX, e.offsetY)); + } + + if (beginNew) { + this.ctx.beginPath(); + this.ctx.moveTo(e.offsetX, e.offsetY); + } else { + this.mousedown = false; + } + } + } + + endPath(e, record = true) { + this.draw(e, false, record); + } + + drawPath(path) { + const last = path.length - 1; + const lastBrush = this.brush; + for (let i = 0; i <= last; i++) { + this.brush = path[i].brushName; + switch(i) { + case 0: this.startPath(path[i], false); break; + case last: this.endPath(path[i], false); break; + default: this.draw(path[i], true, false); + } + } + this.brush = lastBrush; } } diff --git a/hw3/Claudio_Maggioni/scripts/brushes.js b/hw3/Claudio_Maggioni/scripts/brushes.js index 54e7b95..0b314ba 100644 --- a/hw3/Claudio_Maggioni/scripts/brushes.js +++ b/hw3/Claudio_Maggioni/scripts/brushes.js @@ -1,19 +1,53 @@ +// vim: set ts=2 sw=2 et tw=80: + class PenBrush { + constructor() { + this.opacity = 1; + this.name = "PenBrush"; + } - constructor() { - this.opacity = 1; - this.name = "PenBrush"; - } - - draw(ctx, strokeStyle, x, y) { - ctx.lineJoin = ctx.lineCap = 'round'; - ctx.strokeStyle = strokeStyle; - ctx.lineTo(x, y); - ctx.stroke(); - } - + draw(ctx, strokeStyle, x, y) { + ctx.lineJoin = ctx.lineCap = 'round'; + ctx.strokeStyle = strokeStyle; + ctx.lineTo(x, y); + ctx.stroke(); + } } -//TODO DiscBrush -//TODO StarBrush +class DiscBrush extends PenBrush { + static get RADIUS() { return 10; } + constructor() { + super(); + this.name = "DiscBrush"; + } + + draw(ctx, strokeStyle, x, y) { + ctx.beginPath(); // clear previous path starting + ctx.ellipse(x, y, 10, 10, 0, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + } +} + +class StarBrush extends PenBrush { + constructor() { + super(); + this.name = "StarBrush"; + } + + draw(ctx, strokeStyle, x, y) { + ctx.moveTo(x - 8, y - 3); + ctx.lineTo(x - 3, y - 3); + ctx.lineTo(x, y - 8); + ctx.lineTo(x + 3, y - 3); + ctx.lineTo(x + 8, y - 3); + ctx.lineTo(x + 4, y + 1); + ctx.lineTo(x + 4, y + 6); + ctx.lineTo(x, y + 3); + ctx.lineTo(x - 4, y + 6); + ctx.lineTo(x - 4, y + 1); + ctx.lineTo(x - 8, y - 3); + ctx.stroke(); + } +} diff --git a/hw3/Claudio_Maggioni/scripts/undo.js b/hw3/Claudio_Maggioni/scripts/undo.js index 7faa747..ba2f0ba 100644 --- a/hw3/Claudio_Maggioni/scripts/undo.js +++ b/hw3/Claudio_Maggioni/scripts/undo.js @@ -1,4 +1,39 @@ +// vim: set ts=2 sw=2 et tw=80: + const history = { + paths: [] +} - } +history.pop = (app) => { + if (history.paths.length == 0) return; + history.paths.pop(); + app.erase(); + for (const path of history.paths) { + app.drawPath(path); + } +}; +history.initializeNewPath = () => { + history.paths.push([]); +}; + +history.push = (stroke) => { + if (!stroke instanceof Stroke) { + throw new Error(JSON.stringify(stroke) + ' is not a Stroke instance'); + } + history.paths[history.paths.length - 1].push(stroke); +} + +history.clear = () => { + history.paths = []; +}; + + + +class Stroke { + constructor(brushName, x, y) { + this.brushName = brushName; + this.offsetX = x; + this.offsetY = y; + } +}