Skip to content

Commit

Permalink
[dropper,rect,tests] Separate rectangle functions to own library
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kepi committed May 2, 2020
1 parent 63c51dc commit 716991f
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 87 deletions.
76 changes: 76 additions & 0 deletions src/__tests__/rect.test.ts
Original file line number Diff line number Diff line change
@@ -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()
})
151 changes: 64 additions & 87 deletions src/edropper2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -30,7 +31,7 @@ var page = {
canvasBorders: 20,
imageData: null,

rects: null,
rects: [] as Array<Rect>,

screenshoting: false,
dropperActivated: false,
Expand Down Expand Up @@ -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()

Expand All @@ -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)
Expand Down Expand Up @@ -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() {
Expand All @@ -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,
Expand All @@ -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()
}
79 changes: 79 additions & 0 deletions src/rect.ts
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 716991f

Please sign in to comment.