Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update demo, tests and fix PolygonSelector area and intersects function calculation bugs #2

Merged
merged 3 commits into from
Oct 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion demo/src/components/Samples/Multiple/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import Annotation from '../../../../../src'
import {
PointSelector,
RectangleSelector,
OvalSelector
OvalSelector,
PolygonSelector
} from '../../../../../src/selectors'

import Button from '../../Button'
Expand Down Expand Up @@ -65,6 +66,12 @@ export default class Multiple extends Component {
>
{OvalSelector.TYPE}
</Button>
<Button
onClick={this.onChangeType}
active={PolygonSelector.TYPE === this.state.type}
>
{PolygonSelector.TYPE}
</Button>

<Annotation
src={img}
Expand Down
5 changes: 1 addition & 4 deletions demo/src/components/Samples/Simple/index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import React, { Component } from 'react'
import Annotation from '../../../../../src'

import PolygonSelector from '../../../../../src/hocs/PolygonSelector'

import Root from '../../Root'
import img from '../../../img.jpeg'

export default class Simple extends Component {
state = {
annotations: [],
annotation: {},
type: PolygonSelector.TYPE
annotation: {}
}

onChange = (annotation) => {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"test:watch": "nwb test-react --server"
},
"dependencies": {
"point-in-polygon": "^1.0.1",
"polygon-lookup": "^2.4.0",
"polygon-tools": "^0.4.8",
"react-lineto": "^3.1.2",
"styled-components": "^3.1.6"
Expand Down
3 changes: 2 additions & 1 deletion src/components/Annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ export default compose(
})
)
}
{props.value.geometry
{props.value
&& props.value.geometry
&& (props.value.geometry.type === PolygonSelector.TYPE)
&& (!props.value.selection || !props.value.selection.showEditor)
&& (
Expand Down
12 changes: 6 additions & 6 deletions src/components/Polygon/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ const PointDot = styled.div`
function edgesFromPoints(points) {
if (!points || points.length < 3) return [];

const edges = [];
const edges = []
for (let i = 0; i < points.length; ++i) {
if (i + 1 === points.length) {
edges.push(Math.hypot(points[0].x-points[i].x, points[0].y-points[i].y));
edges.push(Math.hypot(points[0].x-points[i].x, points[0].y-points[i].y))
} else {
edges.push(Math.hypot(points[i + 1].x-points[i].x, points[i + 1].y-points[i].y));
edges.push(Math.hypot(points[i + 1].x-points[i].x, points[i + 1].y-points[i].y))
}
}

Expand All @@ -42,11 +42,11 @@ function Polygon (props) {
}}
>
{(geometry.points.length >= 3) && geometry.points.map((item,i) => { // Iterate over points to create the edge lines
let prevItem;
let prevItem
if (i === 0) { // First point (links from last to first)
prevItem = geometry.points[geometry.points.length - 1];
prevItem = geometry.points[geometry.points.length - 1]
} else {
prevItem = geometry.points[i - 1];
prevItem = geometry.points[i - 1]
}
return (
// Note that each LineTo element must have a unique key (unique relative to the connected points)
Expand Down
8 changes: 7 additions & 1 deletion src/components/defaultProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'

import Point from './Point'
import Editor from './Editor'
import PolygonControls from './PolygonControls';
import PolygonControls from './PolygonControls'
import FancyRectangle from './FancyRectangle'
import Rectangle from './Rectangle'
import Oval from './Oval'
Expand Down Expand Up @@ -129,6 +129,12 @@ export default {
Click to Annotate
</Overlay>
)
case PolygonSelector.TYPE:
return (
<Overlay>
Click to Add Points to Annotation
</Overlay>
)
default:
return (
<Overlay>
Expand Down
65 changes: 57 additions & 8 deletions src/hocs/PolygonSelector.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const inside = require('point-in-polygon');
import { polygon } from 'polygon-tools';
var PolygonLookup = require('polygon-lookup')
import { polygon } from 'polygon-tools'

import { getHorizontallyCentralPoint, getVerticallyLowestPoint } from '../utils/pointsUtils'

Expand All @@ -10,18 +10,67 @@ const getCoordPercentage = (e) => ({

export const TYPE = 'POLYGON'

/*
* This function checks if the argument pointToCheck exists on the line created between pointA and pointB.
* All point arguments are represented as two element arrays (e.g.: [10, 15]).
*/
function isPointOnLine (pointA, pointB, pointToCheck) {
return (Math.hypot(pointToCheck[0]-pointA[0], pointToCheck[1]-pointA[1])
+ Math.hypot(pointB[0]-pointToCheck[0], pointB[1]-pointToCheck[1])
=== Math.hypot(pointB[0]-pointA[0], pointB[1]-pointA[1]))
}

/*
* This function checks if the point [x, y] exists on the edge of the polygon created by points polygonPoints.
* The argument polygonPoints is an array of objects (e.g.: [{x: 10, y: 15}, ...]).
*/
function isPointOnPolygonEdge ({ x, y }, polygonPoints) {
if (!polygonPoints || polygonPoints.length < 3 || !x || !y) { return false }

for (let i = 0; i < polygonPoints.length - 1; ++i) {
if (i === 0) { // First point
if (isPointOnLine(polygonPoints[0], polygonPoints[polygonPoints.length - 1], [x, y])) {
return true
}
} else {
if (isPointOnLine(polygonPoints[i], polygonPoints[i + 1], [x, y])) {
return true
}
}
}
return false
}

export function intersects ({ x, y }, geometry) {
if (!geometry || !geometry.points || geometry.points.length < 3) return false;
if (!geometry || !geometry.points || geometry.points.length < 3) return false

// Switch to point array format (e.g.: [{x: 10, y: 15}, ...] -> [[10, 15], ...])
const pointsAsArrays = geometry.points.map(point => [point.x, point.y])

// Setup GeoJSON json format
const featureCollection = {
type: 'FeatureCollection',
features: [{
geometry: {
type: 'Polygon',
coordinates: [ pointsAsArrays ]
}
}]
}

const pointsAsArrays = geometry.points.map(point => [point.x, point.y]);
// Determine if point is inside polygon
const lookup = new PolygonLookup(featureCollection)
const poly = lookup.search(x, y)

return inside([x, y], pointsAsArrays);
// Return whether the point is inside the polygon (poly equals undefined if not) or if the
// point is on the edge (isPointOnPolygonEdge function call)
return (poly !== undefined || isPointOnPolygonEdge({x, y}, pointsAsArrays))
}

export function area (geometry) {
if (!geometry || !geometry.points || geometry.points.length < 3) return 0;
if (!geometry || !geometry.points || geometry.points.length < 3) return 0

return polygon.area(geometry.points);
return polygon.area(geometry.points.map(point => [point.x, point.y]))
}

export const methods = {
Expand All @@ -47,7 +96,7 @@ export const methods = {
},

onMouseUp (annotation, e) {
const coordOfClick = getCoordPercentage(e);
const coordOfClick = getCoordPercentage(e)

return {
...annotation,
Expand Down
70 changes: 70 additions & 0 deletions tests/selectors/PolygonSelector.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { mount } from 'enzyme'
import { expect } from 'chai'
import React from 'react'

import { PolygonSelector as selector } from '../../src/selectors'

function createPolygon ({ points } = { points: [{x: 10, y: 10}, {x: 90, y: 10}, {x: 50, y: 90}] }) {
return {
points
}
}

describe('PolygonSelector', () => {
describe('TYPE', () => {
it('should be a defined string', () => {
expect(selector.TYPE).to.be.a('string')
})
})

describe('intersects', () => {
it('should return true when point is on top left of geometry', () => {
expect(
selector.intersects({ x: 10, y: 10 }, createPolygon())
).to.be.true
})
it('should return true when point is on top right of geometry', () => {
expect(
selector.intersects({ x: 90, y: 10 }, createPolygon())
).to.be.true
})
it('should return true when point is on bottom left of geometry', () => {
expect(
selector.intersects({ x: 40, y: 60 }, createPolygon())
).to.be.true
})
it('should return true when point is on bottom right of geometry', () => {
expect(
selector.intersects({ x: 60, y: 60 }, createPolygon())
).to.be.true
})
it('should return true when point is on bottom center of geometry', () => {
expect(
selector.intersects({ x: 50, y: 90 }, createPolygon())
).to.be.true
})
it('should return true when point is inside geometry', () => {
expect(
selector.intersects({ x: 50, y: 50 }, createPolygon())
).to.be.true
})
it('should return false when point is outside of geometry', () => {
expect(selector.intersects({ x: 0, y: 0 }, createPolygon())).to.be.false
expect(selector.intersects({ x: 100, y: 0 }, createPolygon())).to.be.false
expect(selector.intersects({ x: 0, y: 100 }, createPolygon())).to.be.false
expect(selector.intersects({ x: 100, y: 100 }, createPolygon())).to.be.false
expect(selector.intersects({ x: 10, y: 20 }, createPolygon())).to.be.false
expect(selector.intersects({ x: 90, y: 20 }, createPolygon())).to.be.false
})
})

describe('area', () => {
it('should return geometry area', () => {
expect(selector.area(createPolygon())).to.equal(3200)
})
})

describe('methods', () => {
xit('should be defined')
})
})