diff --git a/__tests__/shape.test.js b/__tests__/shape.test.js index 06b1ab16a9c8f1687f592021b56b46f8f7253f72..ffe3fcef3917b008a61ca771a91976b657cc14f6 100644 --- a/__tests__/shape.test.js +++ b/__tests__/shape.test.js @@ -40,7 +40,7 @@ describe("shape recognition", () => { }) test("should recognize a slightly curve horizontal line", () => { - const points = [[0, 0], [30, 5], [100, 2]] + const points = [[0, 0], [30, 5], [100, 0]] const result = recognizeFromPoints(points) expect(result.shape).toBe(Shapes.line) expect(result.angle).toBe(0) @@ -192,7 +192,7 @@ describe("shape recognition", () => { const result = recognizeFromPoints(points) expect(result.shape).toBe(Shapes.line) - expect(result.angle).toBe(20) + expect(result.angle).toBeGreaterThan(12) }) }) diff --git a/src/app.js b/src/app.js index dd5c44d42326acb3daa2e38b1b9554b2d3806f78..552aebd5d838870e1f1a5110a169d2ad316e9ebd 100644 --- a/src/app.js +++ b/src/app.js @@ -161,20 +161,14 @@ function attributedPoint(x, y, pressure = 0) { ] } -const INF_LINE_OFFSET = 1000 - function getRecognizedShapePoints(points) { const recognizedShape = recognizeFromPoints(points) if (!recognizedShape.shape) return undefined switch (recognizedShape.shape) { case Shapes.line: { const [x, y] = points[0] - const a = (recognizedShape.angle * Math.PI) / 180 - const length = recognizedShape.length - const dx = length * Math.cos(a) - const dy = length * Math.sin(a) const p1 = [x, y] - const p2 = [x + dx, y - dy] + const p2 = recognizedShape.lastPoint return [p1, p2] } case Shapes.rectangle: { diff --git a/src/room.js b/src/room.js index b69f8f2eb4d811e0b73a8692b7228ea3ef7c8261..2f920ef7366ed4c833e26cc4ecafb7d11eb3ae70 100644 --- a/src/room.js +++ b/src/room.js @@ -44,34 +44,17 @@ class Room extends EventTarget { } extendErasureIntervals(pathID, pointID, newIntervals) { - console.log(flattenErasureIntervals({ [pointID]: newIntervals })) - this.shared.eraseIntervals .get(pathID) .merge(flattenErasureIntervals({ [pointID]: newIntervals })) } - // TODO: Refactor duplication replacePath(pathID, newPoints) { this.shared.eraseIntervals .get(pathID) .merge([[0, this.shared.strokePoints.get(pathID).length]]) newPoints.forEach((point) => this.extendPath(pathID, point)) - // // eslint-disable-next-line require-yield - // this._y.db.requestTransaction(function* requestTransaction() { - // const prevJSON = self.shared.eraseIntervals.get(pathID) || "[]" - // const postJSON = JSON.stringify([ - // [0, self.shared.strokePoints.get(pathID).length], - // ]) - - // if (prevJSON == postJSON) { - // return - // } - - // newPoints.forEach((point) => self.extendPath(pathID, point)) - // self.shared.eraseIntervals.set(pathID, postJSON) - // }) } getPaths() { diff --git a/src/shapes.js b/src/shapes.js index 348990ce3e64d17b8bdf26f7b4d3fc2426560082..be083fe80e875b6ee80a10719b029d67030aa753 100644 --- a/src/shapes.js +++ b/src/shapes.js @@ -1,17 +1,3 @@ -function dtor(a) { - return (Math.PI * a) / 180 -} -function cos(a) { - return Math.cos(dtor(a)) -} -function sin(a) { - return Math.sin(dtor(a)) -} - -const angleStep = 90 -const numAngleCells = 180 / angleStep -const rhoMax = 10000 - const getDistance = (a, b) => { if (!(a && b)) return 0 return Math.sqrt( @@ -19,48 +5,60 @@ const getDistance = (a, b) => { ) } -function findMaxInHough(accum, threshold) { - let max = 0 - // let bestRho = 0 - let bestTheta = 0 - for (let i = 0; i < numAngleCells; i++) { - if (!accum[i]) continue - for (let j = 0; j < accum[i].length; j++) { - if (accum[i][j] > max) { - max = accum[i][j] - // bestRho = j - bestTheta = i - } - } - } - // bestRho <<= 1 - // bestRho -= rhoMax - // bestRho *= rhoStep - bestTheta *= angleStep - - if (max > threshold) { - return bestTheta - } - return undefined -} - -function constructHoughAccumulator(config, accumulator, x, y) { - for (let thetaIndex = 0; thetaIndex < numAngleCells; thetaIndex++) { - const theta = thetaIndex * angleStep - let rho = x * cos(theta) + y * sin(theta) - rho = Math.floor(rho) - rho += rhoMax - rho >>= 1 - rho /= config.rhoStep - rho = Math.floor(rho) - if (accumulator[thetaIndex] == undefined) accumulator[thetaIndex] = [] - if (accumulator[thetaIndex][rho] == undefined) { - accumulator[thetaIndex][rho] = 1 - } else { - accumulator[thetaIndex][rho]++ - } - } -} +// function dtor(a) { +// return (Math.PI * a) / 180 +// } + +// function cos(a) { +// return Math.cos(dtor(a)) +// } + +// function sin(a) { +// return Math.sin(dtor(a)) +// } + +// const angleStep = 90 +// const numAngleCells = 180 / angleStep +// const rhoMax = 10000 + +// function findMaxInHough(accum, threshold) { +// let max = 0 +// // let bestRho = 0 +// let bestTheta = 0 +// for (let i = 0; i < numAngleCells; i++) { +// if (!accum[i]) continue +// for (let j = 0; j < accum[i].length; j++) { +// if (accum[i][j] > max) { +// max = accum[i][j] +// bestTheta = i +// } +// } +// } +// bestTheta *= angleStep + +// if (max > threshold) { +// return bestTheta +// } +// return undefined +// } + +// function constructHoughAccumulator(config, accumulator, x, y) { +// for (let thetaIndex = 0; thetaIndex < numAngleCells; thetaIndex++) { +// const theta = thetaIndex * angleStep +// let rho = x * cos(theta) + y * sin(theta) +// rho = Math.floor(rho) +// rho += rhoMax +// rho >>= 1 +// rho /= config.rhoStep +// rho = Math.floor(rho) +// if (accumulator[thetaIndex] == undefined) accumulator[thetaIndex] = [] +// if (accumulator[thetaIndex][rho] == undefined) { +// accumulator[thetaIndex][rho] = 1 +// } else { +// accumulator[thetaIndex][rho]++ +// } +// } +// } function boundingCoords(points) { const xs = points.map((p) => p[0]) @@ -86,11 +84,14 @@ function angleBetweenVectors(p1, p2) { return Math.acos((x0 * x1 + y0 * y1) / (vectorLength(p1) * vectorLength(p2))) } -const LINE_ANGLE_THRESHOLD = Math.PI / 4 -const VECTOR_LEN_THRESHOLD = 5 +const LINE_ANGLE_THRESHOLD = Math.PI / 6 +const VECTOR_LEN_THRESHOLD_FRACTION = 0.15 function couldBeLine(points) { if (points.length < 2) return false + const vectorThreshold = Math.floor( + points.length * VECTOR_LEN_THRESHOLD_FRACTION, + ) const pivot = points[0] let cumulativeThreshold = 0 for (let i = 2; i < points.length; i++) { @@ -103,10 +104,7 @@ function couldBeLine(points) { const angle = angleBetweenVectors(d1, d2) if (Math.abs(angle) > LINE_ANGLE_THRESHOLD) { - if ( - cumulativeThreshold < VECTOR_LEN_THRESHOLD && - d2Len < VECTOR_LEN_THRESHOLD - ) { + if (cumulativeThreshold < vectorThreshold && d2Len < vectorThreshold) { cumulativeThreshold += d2Len continue } @@ -217,39 +215,45 @@ function recognizeRect(points, rectDetectionData) { } } -const MAX_RHO_STEP = 50 -const MIN_RHO_STEP = 5 - -function rhoStepForPoints(points) { - return points.length > 50 ? MAX_RHO_STEP : MIN_RHO_STEP -} - function recognizeLine(points) { const [p1, p2] = [points[0], points[points.length - 1]] - const angle = angleBetweenVectors(p1, p2) - return { shape: Shapes.line, angle, points, length: getDistance(p1, p2) } -} - -function recognizeLineHough(points) { - if (!(points && points.length)) return {} - const accum = Array(numAngleCells) - const houghConfig = { - rhoStep: rhoStepForPoints(points), - } - points.forEach((x) => constructHoughAccumulator(houghConfig, accum, ...x)) - const angle = findMaxInHough(accum, points.length - 10) - - if (angle !== undefined) { - return { - shape: Shapes.line, - angle: 90 - angle, - hough: accum, - points, - } + const angle = + (angleBetweenVectors(diffVector(p2, p1), [1, 0]) / Math.PI) * 180 + return { + shape: Shapes.line, + angle, + points, + length: getDistance(p1, p2), + lastPoint: p2.slice(0, 2), } - - return {} } +// const MAX_RHO_STEP = 50 +// const MIN_RHO_STEP = 5 + +// function rhoStepForPoints(points) { +// return points.length > 50 ? MAX_RHO_STEP : MIN_RHO_STEP +// } + +// function recognizeLineHough(points) { +// if (!(points && points.length)) return {} +// const accum = Array(numAngleCells) +// const houghConfig = { +// rhoStep: rhoStepForPoints(points), +// } +// points.forEach((x) => constructHoughAccumulator(houghConfig, accum, ...x)) +// const angle = findMaxInHough(accum, points.length - 10) + +// if (angle !== undefined) { +// return { +// shape: Shapes.line, +// angle: 90 - angle, +// hough: accum, +// points, +// } +// } + +// return {} +// } function recognizeFromPoints(points) { const rectDetectData = couldBeRect(points)