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