diff --git a/package-lock.json b/package-lock.json index af0ee334..55a2b6e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "chs-js-lib", - "version": "0.2.18", + "version": "0.2.19", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "0.2.18", + "version": "0.2.19", "license": "ISC", "dependencies": { "tone": "^14.7.77", diff --git a/package.json b/package.json index 032b0af7..2a1dd77f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chs-js-lib", - "version": "0.2.18", + "version": "0.2.19", "description": "JavaScript graphics library used in CodeHS's platform.", "main": "dist/chs.cjs", "module": "dist/chs.mjs", diff --git a/src/graphics/index.js b/src/graphics/index.js index c24b45ee..a6fe04ef 100644 --- a/src/graphics/index.js +++ b/src/graphics/index.js @@ -462,6 +462,10 @@ class GraphicsManager extends Manager { * @param {Thing} elem - The element to be removed from the canvas. */ remove(elem) { + if (!(elem instanceof Thing)) { + return; + } + if (elem instanceof WebVideo) { elem.stop(); } diff --git a/src/graphics/polygon.js b/src/graphics/polygon.js index a226beaa..ce871243 100644 --- a/src/graphics/polygon.js +++ b/src/graphics/polygon.js @@ -215,6 +215,35 @@ class Polygon extends Thing { const dy = y - this.y; this.move(dx, dy); } + + /** + * Polygons manually calculate their bounds with their own implementation of _updateBounds + * (rather than the implementation in the Thing superclass) because Polygon's can have + * negative points which draw to the left of their x value or above their y value. + * @private + */ + _updateBounds() { + let minX = Infinity; + let maxX = -Infinity; + let minY = Infinity; + let maxY = -Infinity; + this.points.forEach(({ x, y }) => { + minX = Math.min(minX, x); + maxX = Math.max(maxX, x); + minY = Math.min(minY, y); + maxY = Math.max(maxY, y); + }); + const width = maxX - minX; + const height = maxY - minY; + this.bounds = { + left: minX - this.anchor.horizontal * width, + right: maxX - this.anchor.horizontal * width, + top: minY - this.anchor.vertical * height, + bottom: maxY - this.anchor.vertical * height, + }; + this._boundsInvalidated = false; + this._lastCalculatedBoundsID++; + } } export default Polygon; diff --git a/src/graphics/thing.js b/src/graphics/thing.js index 1ddacd4d..2002e934 100644 --- a/src/graphics/thing.js +++ b/src/graphics/thing.js @@ -482,7 +482,13 @@ class Thing { context.strokeStyle = 'red'; context.fill(); const bounds = this.getBounds(); - context.strokeRect(0, 0, bounds.right - bounds.left, bounds.bottom - bounds.top); + context.translate(-drawX, -drawY); + context.strokeRect( + bounds.left, + bounds.top, + bounds.right - bounds.left, + bounds.bottom - bounds.top + ); } context.restore(); diff --git a/test/graphics.test.js b/test/graphics.test.js index 1b69c7a5..356ed6c1 100644 --- a/test/graphics.test.js +++ b/test/graphics.test.js @@ -302,6 +302,19 @@ describe('Graphics', () => { expect(g.elementPool).toEqual([a, a, b, c]); expect(g.elementPoolSize).toEqual(4); }); + it('Removing an undefined element does not throw', () => { + const g = new Graphics({ shouldUpdate: false }); + let el; + expect(() => { + g.remove(el); + }).not.toThrow(); + }); + it('Removing a non-Thing does not throw', () => { + const g = new Graphics({ shouldUpdate: false }); + expect(() => { + g.remove(Circle); + }).not.toThrow(); + }); }); describe('fullReset', () => { it('Clears all elements', () => { diff --git a/test/polygon.test.js b/test/polygon.test.js index 9f9c1628..90244dc2 100644 --- a/test/polygon.test.js +++ b/test/polygon.test.js @@ -88,4 +88,26 @@ describe('Polygon', () => { ]); }); }); + describe('Bounds', () => { + it('Handles negative points', () => { + const p = new Polygon(); + p.addPoint(-30, 0); + p.addPoint(0, 30); + p.addPoint(30, 30); + + expect(p.getBounds()).toEqual({ + top: 0, + left: -30, + bottom: 30, + right: 30, + }); + p.setAnchor({ vertical: 0.5, horizontal: 0.5 }); + expect(p.getBounds()).toEqual({ + top: -15, + left: -60, + bottom: 15, + right: 0, + }); + }); + }); });