From 716991f14a15153974cbe999d0089bbaae2c270c Mon Sep 17 00:00:00 2001 From: Kepi Date: Sat, 2 May 2020 19:21:59 +0200 Subject: [PATCH] [dropper,rect,tests] Separate rectangle functions to own library Separate all rectangle functions to its own class Rect and rewrite of most during the process. Basic unit tests provided so we can catch bugs easier. So far works much better then previous implementation. --- src/__tests__/rect.test.ts | 76 +++++++++++++++++++ src/edropper2.ts | 151 ++++++++++++++++--------------------- src/rect.ts | 79 +++++++++++++++++++ 3 files changed, 219 insertions(+), 87 deletions(-) create mode 100644 src/__tests__/rect.test.ts create mode 100644 src/rect.ts diff --git a/src/__tests__/rect.test.ts b/src/__tests__/rect.test.ts new file mode 100644 index 0000000..95fe800 --- /dev/null +++ b/src/__tests__/rect.test.ts @@ -0,0 +1,76 @@ +import Rect from '../rect' + +const dw = 1680 +const dh = 930 + +const rA = new Rect(0, 0, dw, dh) +const rB = new Rect(0, 100, dw, dh) +const rC = new Rect(50, 20, 100, 70) +const rD = new Rect(0, 1000, dw, dh) +const rE = new Rect(0, 900, dw, dh) + +const rX = new Rect(173, 0, dw, dh) +const rY = new Rect(1700, 0, dw, dh) + +const rAB = new Rect(0, 0, dw, 1030) +const rAE = new Rect(0, 0, dw, 1830) +const rAED = new Rect(0, 0, dw, 1930) + +const rAX = new Rect(0, 0, dw + 173, dh) +const rAXY = new Rect(0, 0, dw + 1700, dh) + +const rCt = new Rect(20, 50, 70, 100) + +// FIXME: add horizontal merge tests + +test('rA right', () => { + expect(rA.right).toBe(dw) +}) + +test('rC bottom', () => { + expect(rC.bottom).toBe(90) +}) + +test('rB should not be contained in rA', () => { + expect(rA.contains(rB)).toBe(false) +}) + +test('merge of rA and rB equal to rAB', () => { + expect(rA.merge(rB)).toStrictEqual(rAB) +}) + +test('cannot merge rA and rD', () => { + expect(rA.merge(rD)).toBeNull() +}) + +test('can merge rA with rE and then with rD', () => { + const m = rA.merge(rE) + expect(m).toStrictEqual(rAE) + expect(m.merge(rD)).toStrictEqual(rAED) +}) + +test('transposed rC', () => { + expect(rC.transposed()).toStrictEqual(rCt) +}) + +test('double transposed rC should be rC', () => { + expect(rC.transposed().transposed()).toStrictEqual(rC) +}) + +test('can merge rA with rX', () => { + expect(rA.merge(rX)).toStrictEqual(rAX) +}) + +test('cannot merge rA with rY', () => { + expect(rA.merge(rY)).toBeNull() +}) + +test('can merge rA with rX and then rY', () => { + const m = rA.merge(rX) + expect(m).toStrictEqual(rAX) + expect(m.merge(rY)).toStrictEqual(rAXY) +}) + +test('cannot merge horizontally and vertically different', () => { + expect(rC.merge(rX)).toBeNull() +}) diff --git a/src/edropper2.ts b/src/edropper2.ts index 9897272..1e59175 100644 --- a/src/edropper2.ts +++ b/src/edropper2.ts @@ -3,6 +3,7 @@ import scrollStop from './vendor/scrollStop' import { createNode } from './helpers' import Overlay from './overlay' import Color from './Color.d' +import Rect from './rect' var EDROPPER_VERSION = 12 var CANVAS_MAX_SIZE = 32767 - 20 @@ -30,7 +31,7 @@ var page = { canvasBorders: 20, imageData: null, - rects: null, + rects: [] as Array, screenshoting: false, dropperActivated: false, @@ -185,7 +186,7 @@ var page = { // window is resized onWindowResize: function() { if (!page.dropperActivated) return - console.log('dropper: window resized') + console.log('dropper: window resized or pixelRatio changed') // set defaults page.defaults() @@ -212,68 +213,19 @@ var page = { color, }) }, - // return true if rectangle A is whole in rectangle B - rectInRect: function(A, B) { - if ( - A.x >= B.x && - A.y >= B.y && - A.x + A.width <= B.x + B.width && - A.y + A.height <= B.y + B.height - ) - return true - else return false - }, - // found out if two points and length overlaps - // and merge it if needed. Helper method for - // rectMerge - rectMergeGeneric: function(a, b, length) { - // swap them if b is above a - if (b < a) { - ;[a, b] = [b, a] - } - // shapes are overlaping - if (b <= a + length) - return { - a: a, - length: b - a + length, - } - else return false - }, - // merge same x or y positioned rectangles if overlaps - // width (or height) of B has to be equal to A - rectMerge: function(a, b) { - var t - // same x position and same width - if (a.x == b.x && a.width == b.width) { - t = page.rectMergeGeneric(a.y, b.y, a.height) - if (t != false) { - a.y = t.a - a.height = length - return a - } - // same y position and same height - } else if (a.y == b.y && a.height == b.height) { - t = page.rectMergeGeneric(a.x, b.x, a.width) - if (t != false) { - a.x = t.a - a.width = length - return a - } - } - return false - }, + // --------------------------------- // COLORS // --------------------------------- pickColor: function(x, y) { if (page.canvasData === null) return - var canvasIndex = (e.pageX + e.pageY * page.canvas.width) * 4 - ////console.log(e.pageX + ' ' + e.pageY + ' ' + page.canvas.width); + const redIndex = y * page.canvas.width * 4 + x * 4 + let color: Color = { - r: page.canvasData[canvasIndex], - g: page.canvasData[canvasIndex + 1], - b: page.canvasData[canvasIndex + 2], - alpha: page.canvasData[canvasIndex + 3], + r: page.canvasData[redIndex], + g: page.canvasData[redIndex + 1], + b: page.canvasData[redIndex + 2], + alpha: page.canvasData[redIndex + 3], } color.rgbhex = page.rgbToHex(color.r, color.g, color.b) color.opposite = page.rgbToHex(255 - color.r, 255 - color.g, 255 - color.b) @@ -332,23 +284,23 @@ var page = { screenChanged: function(force = false) { if (!page.dropperActivated) return console.log('dropper: screenChanged') - var rect = { - x: xOffset, - y: yOffset, - width: page.screenWidth, - height: page.screenHeight, - } page.yOffset = Math.round(document.documentElement.scrollTop) page.xOffset = Math.round(document.documentElement.scrollLeft) + + const rect = new Rect(page.xOffset, page.yOffset, page.screenWidth, page.screenHeight) + + console.group(`comparing rect ${rect} with [ ${page.rects.join(', ')} ]`) // don't screenshot if we already have this one if (!force && page.rects.length > 0) { - for (let index in page.rects) { - if (page.rectInRect(rect, page.rects[index])) { + for (let r of page.rects) { + if (r.contains(rect)) { console.log('dropper: already shoted, skipping') + console.groupEnd() return } } } + console.groupEnd() page.setScreenshoting(true) setTimeout(function() { @@ -357,33 +309,53 @@ var page = { }) }, 50) }, + + updateRects: function(rect) { + console.group('updateRects') + + if (page.rects.length === 0) { + page.rects.push(rect) + console.log('no rects yet, pushing first') + console.groupEnd() + return + } + + let merged = false + + page.rects.forEach((r, i) => { + console.group(`Trying merge ${rect} with ${r}`) + let t = rect.merge(r) + if (t !== null) { + console.log('merged') + merged = true + page.rects.splice(i, 1) + page.updateRects(t) + } + console.groupEnd() + }) + + if (!merged) { + console.log('dropper: pushing merged to rects') + page.rects.push(rect) + } + + console.groupEnd() + }, + // capture actual Screenshot capture: function() { + console.group('capture') page.checkCanvas() - ////console.log(page.rects); - // var image = new Image(); + console.log('dropper: creating image element and waiting on load') var image = document.createElement('img') image.onload = function() { console.log(`dropper: got new screenshot ${image.width}x${image.height}`) - var merged = false - // if there are already any rectangles - if (page.rects.length > 0) { - // try to merge shot with others - for (let index in page.rects) { - var t = page.rectMerge(rect, page.rects[index]) - if (t != false) { - console.log('dropper: merging') - merged = true - page.rects[index] = t - } - } - } - // put rectangle in array - if (merged == false) page.rects.push(rect) - page.canvasContext.drawImage(image, xOffset, yOffset) // page.screenWidth = image.width // page.screenHeight = image.height + const rect = new Rect(page.xOffset, page.yOffset, image.width, image.height) + page.updateRects(rect) + page.canvasContext.drawImage(image, page.xOffset, page.yOffset) page.canvasData = page.canvasContext.getImageData( 0, 0, @@ -402,13 +374,18 @@ var page = { } else { console.error('ed: no imageData') } + + console.groupEnd() }, init: function() { page.messageListener() + + window.onresize = function() { + page.onWindowResize() } + + const mqString = `(resolution: ${window.devicePixelRatio}dppx)` + matchMedia(mqString).addListener(page.onWindowResize) }, } page.init() -window.onresize = function() { - page.onWindowResize() -} diff --git a/src/rect.ts b/src/rect.ts new file mode 100644 index 0000000..00d6ed6 --- /dev/null +++ b/src/rect.ts @@ -0,0 +1,79 @@ +class Rect { + left: number + top: number + right: number + bottom: number + width: number + height: number + + constructor(left, top, width, height) { + this.left = left + this.top = top + this.right = left + width + this.bottom = top + height + this.width = width + this.height = height + } + + transposed() { + return new Rect(this.top, this.left, this.height, this.width) + } + + contains(other: Rect): boolean { + return ( + other.left >= this.left && + other.right <= this.right && + other.top >= this.top && + other.bottom <= this.bottom + ) + } + + horizontalMerge(other) { + const A = this.transposed() + const B = other.transposed() + + const X = A.verticalMerge(B) + if (X === null) { + return null + } + + return X.transposed() + } + + verticalMerge(other) { + let A = this + let B = other + + // swap A and B if A is not on top + if (this.top > other.top) { + ;[A, B] = [B, A] + } + + // if A is overlapping with B + if (A.bottom >= B.top && A.bottom <= B.bottom) { + return new Rect(A.left, A.top, A.right - A.left, B.bottom - A.top) + } else { + return null + } + } + + merge(other: Rect): Rect { + let x, y, w, h: number + + // same left and right + if (this.left == other.left && this.right == other.right) { + return this.verticalMerge(other) + // same top and bottom + } else if (this.top == other.top && this.bottom == other.bottom) { + return this.horizontalMerge(other) + } + + return null + } + + toString(): string { + return `Rect(${this.width}x${this.height})@${this.left},${this.top}` + } +} + +export default Rect