Skip to content

Commit

Permalink
Get rid of webgl code
Browse files Browse the repository at this point in the history
  • Loading branch information
furstenheim committed Apr 25, 2017
1 parent 56d5f40 commit f169561
Show file tree
Hide file tree
Showing 17 changed files with 74 additions and 1,276 deletions.
659 changes: 34 additions & 625 deletions dist/automatic-labelling.js

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
"type": "git",
"url": "https://github.com/furstenheim/automatic-scatter-labelling"
},
"standard": {
"ignore": [
"/coverage/",
"/dist"
]
},
"author": "Gabriel Fürstenheim <furstenheim@gmail.com>",
"license": "ISC",
"devDependencies": {
Expand All @@ -23,6 +29,7 @@
"chai": "^3.5.0",
"exposify": "^0.5.0",
"gulp": "3.x.x",
"gulp-notify": "^3.0.0",
"gulp-spawn-mocha": "^3.1.0",
"istanbul": "^0.4.5",
"karma": "^1.3.0",
Expand Down
41 changes: 10 additions & 31 deletions src/find-best-ray.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@ const multiInterval = require('./multi-interval').multiInterval
const interval = require('./interval').interval
const utils = require('./utils')

const TOLERANCE = 2 // pixels

async function findBestRay (pointsToLabel, pointsNotToLabel, isWebgl, webglExtra) {
let {intersectionData, rectangleData} = webglExtra
const computeIntersection = webglExtra.computeIntersection
async function findBestRay (pointsToLabel, pointsNotToLabel) {
// We follow the article page 4 Algorithm 1
var P = pointsToLabel
var P0 = pointsNotToLabel.concat(pointsToLabel)
Expand All @@ -30,18 +27,6 @@ async function findBestRay (pointsToLabel, pointsNotToLabel, isWebgl, webglExtra
const pi = _.minBy(P, 'availableMeasure')
let mindik = _.minBy(pi.rays, 'minimum').minimum
let R = pi.rays.filter(r => r.availableMeasure > 0)
if (isWebgl) {
R.forEach(function (rij) {
const index = 4 * rij.selfIndex
let segment = {x: rij.vector.x * rij.minimum, y: rij.vector.y * rij.minimum}
const rectangle = extendedPointMethods.translateLabel(pi, segment)
rectangleData[index] = rectangle.top
rectangleData[index + 1] = rectangle.left
rectangleData[index + 2] = rectangle.bottom
rectangleData[index + 3] = rectangle.right
});
({intersectionData, rectangleData} = await computeIntersection(rectangleData, pi.position.x, pi.position.y, intersectionData))
}
rijloop: for (let rij of R) {
let Vij = []
let segment = {x: rij.vector.x * rij.minimum, y: rij.vector.y * rij.minimum}
Expand All @@ -56,19 +41,13 @@ async function findBestRay (pointsToLabel, pointsNotToLabel, isWebgl, webglExtra
for (let rkl of pk.rays) {
let labelIntersection
let segmentIntersection
if (isWebgl) {
const index = rkl.index + rij.selfIndex * 4
labelIntersection = interval(intersectionData[index], intersectionData[index + 1])
segmentIntersection = interval(intersectionData[index + 2], intersectionData[index + 3])
} else {
// We have split label rectangle intersection into two algorithms, label rectangle and label segment. Those two intervals should intersect since the segment intersects the rectangle, so we can coalesce the intervals
const labelInterval = labelRectangleIntersection(rectangle, pk.label, rkl.vector, pk.position)
const segmentInterval = labelSegmentIntersection(pi.position, segment, pk.label, rkl.vector, pk.position)
const rayInterval = rayRectangleIntersection(rectangle, rkl.vector, pk.position)
const raySegmentInterval = raySegmentIntersection(pi.position, segment, pk.position, rkl.vector)
labelIntersection = labelInterval.coalesceInPlace(rayInterval)
segmentIntersection = segmentInterval.coalesceInPlace(raySegmentInterval)
}
// We have split label rectangle intersection into two algorithms, label rectangle and label segment. Those two intervals should intersect since the segment intersects the rectangle, so we can coalesce the intervals
const labelInterval = labelRectangleIntersection(rectangle, pk.label, rkl.vector, pk.position)
const segmentInterval = labelSegmentIntersection(pi.position, segment, pk.label, rkl.vector, pk.position)
const rayInterval = rayRectangleIntersection(rectangle, rkl.vector, pk.position)
const raySegmentInterval = raySegmentIntersection(pi.position, segment, pk.position, rkl.vector)
labelIntersection = labelInterval.coalesceInPlace(rayInterval)
segmentIntersection = segmentInterval.coalesceInPlace(raySegmentInterval)
if (!labelIntersection.empty || !segmentIntersection.empty) {
availableSpace -= rkl.available.measureMultipleIntersection(multiInterval.coalesce(labelIntersection, segmentIntersection))
}
Expand All @@ -88,5 +67,5 @@ async function findBestRay (pointsToLabel, pointsNotToLabel, isWebgl, webglExtra
}
}
// We need to return intersectionData because the reference has been neutered in find ray intersection
return {rbest: rbest, pbest: pbest, intersectionData, rectangleData, usedWebgl: isWebgl}
}
return {rbest: rbest, pbest: pbest}
}
63 changes: 14 additions & 49 deletions src/main-algorithm-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ module.exports = {mainAlgorithm}
const work = require('webworkify')
const algorithm = work(require('./main-algorithm.js'))
const _ = require('lodash')
const webgl = require('./webgl/webgl')
const webGLFunctions = {} // Here we store the reference to the functions
const promiseResolutions = {}
function mainAlgorithm (extendedPoints, params = {}) {
return new Promise(function (resolve, reject) {
Expand All @@ -17,42 +15,25 @@ function mainAlgorithm (extendedPoints, params = {}) {
label: p.label
}
})
const NUMBER_OF_RAYS = _.isNumber(params.NUMBER_OF_RAYS) ? params.NUMBER_OF_RAYS : 3
const isWebgl = params.isWebgl
let intersectionData, computeIntersection, rectangleData
const processUUID = parseInt(Math.random() * 1000000).toString() // no need for anything fancy
if (isWebgl) {
({intersectionData, computeIntersection, rectangleData} = webgl.setUp(extendedPoints, NUMBER_OF_RAYS))
algorithm.postMessage({
type: 'start',
extendedPoints,
params,
intersectionData,
rectangleData,
processUUID
}, [intersectionData.buffer, rectangleData.buffer])
webGLFunctions[processUUID] = computeIntersection
} else {
algorithm.postMessage({
type: 'start',
extendedPoints,
params,
processUUID
})
}
algorithm.postMessage({
type: 'start',
extendedPoints,
params,
processUUID
})
promiseResolutions[processUUID] = function (event) {
const result = event.data.result.map(p => {
return {
id: p.id,
rectangle: {
left: p.rectangle.left,
right: p.rectangle.right,
top: -p.rectangle.top,
bottom: -p.rectangle.bottom
}
return {
id: p.id,
rectangle: {
left: p.rectangle.left,
right: p.rectangle.right,
top: -p.rectangle.top,
bottom: -p.rectangle.bottom
}
}
)
})
return resolve(result)
}
})
Expand All @@ -63,30 +44,14 @@ algorithm.onmessage = function (event) {
case 'end':
endEvent(event)
break
case 'computeIntersection':
computeInGPU(event)
break
default:
console.error('This event case should not happen', data.type)
}
}
function computeInGPU (event) {
const data = event.data
const processUUID = data.processUUID
const computeIntersection = webGLFunctions[processUUID]
const {intersectionData, rectangleData} = computeIntersection(data.rectangleData, data.pix, data.piy, data.intersectionData)
algorithm.postMessage({
intersectionData,
rectangleData,
uuid: data.uuid,
type: 'computeIntersection'
}, [intersectionData.buffer, rectangleData.buffer])
}

function endEvent (event) {
const {processUUID} = event.data
const callback = promiseResolutions[processUUID]
callback(event)
delete promiseResolutions[processUUID]
delete webGLFunctions[processUUID]
}
49 changes: 1 addition & 48 deletions src/main-algorithm.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,29 @@
//module.exports = {mainAlgorithm}


let NUMBER_OF_RAYS
// In this object we register the callbacks for computations in the main thread (gpu case)
const callbacks = {}
// Called as webworker
module.exports = function (self) {
importScripts('https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js')
const extendedPointMethods = require('./extended-point-methods')
const _ = require('lodash')
const rayIntersection = require('./ray-intersection').rayIntersection
const iterativeGreedy = require('iterative-greedy')
const webgl = require('./webgl/webgl')
if (typeof postMessage !== 'undefined') {
self.onmessage = function (event) {
var data = event.data
switch (data.type) {
case 'start':
launchMainAlgorithmFromEvent(event)
break
case 'computeIntersection':
returnGPUComputation(event)
break
default:
console.error('Not a valid event type', data.type)
}
}
}
function returnGPUComputation (event) {
const uuid = event.data.uuid
if (_.isFunction(callbacks[uuid])) {
callbacks[uuid](event)
delete callbacks[uuid]
} else {
console.error('Callback should be a function, uuid:', uuid)
}
}

function launchMainAlgorithmFromEvent (event) {
const data = event.data
const extendedPoints = data.extendedPoints
const params = data.params
const processUUID = data.processUUID // we use this in case the algorihm is required several times
if (params.isWebgl) {
params.intersectionData = data.intersectionData
params.rectangleData = data.rectangleData
params.computeIntersection = _.partialRight(computeIntersectionWithGPU, processUUID)
}
mainAlgorithm(extendedPoints, params)
.then(function (result) {
postMessage({
Expand All @@ -57,42 +34,18 @@ module.exports = function (self) {
})
}

function computeIntersectionWithGPU (rectangleData, pix, piy, intersectionData, processUUID) {
var uuid = parseInt(Math.random() * 1000000).toString() // no need for anything fancy
return new Promise(function (resolve, reject) {
postMessage({
type: 'computeIntersection',
rectangleData,
pix,
piy,
intersectionData,
uuid,
processUUID
}, [rectangleData.buffer, intersectionData.buffer])
callbacks[uuid] = function (event) {
resolve({intersectionData: event.data.intersectionData, rectangleData: event.data.rectangleData})
}
})
}

function mainAlgorithm (extendedPoints, params = {}) {
NUMBER_OF_RAYS = _.isNumber(params.NUMBER_OF_RAYS) ? params.NUMBER_OF_RAYS : 3
const MAX_NUMBER_OF_ITERATIONS = _.isNumber(params.MAX_NUMBER_OF_ITERATIONS) ? params.MAX_NUMBER_OF_ITERATIONS : 1
const isWebgl = params.isWebgl
computeRays(extendedPoints)
var intersectionData, computeIntersection, rectangleData
if (isWebgl && !params.intersectionData) {
({intersectionData, computeIntersection, rectangleData} = webgl.setUp(extendedPoints, NUMBER_OF_RAYS))
} else if (isWebgl && params.intersectionData) {
({intersectionData, computeIntersection, rectangleData} = params)
}
extendedPointMethods.computeInitialAvailabeSpaces(extendedPoints, {radius: params.radius || 2, bbox: params.bbox})
extendedPoints.forEach(function (p) {
extendedPointMethods.resetAvailableSpace(p)
extendedPointMethods.updateAvailableSpace(p)
})
const possiblePoints = extendedPoints.filter(p => p.availableMeasure > 0)
return iterativeGreedy.solve(_.partialRight(rayIntersection, isWebgl, {intersectionData, computeIntersection, rectangleData}), possiblePoints, resetFunction, {serializeFunction, MAX_NUMBER_OF_ITERATIONS})
return iterativeGreedy.solve(_.partialRight(rayIntersection, isWebgl, {}), possiblePoints, resetFunction, {serializeFunction, MAX_NUMBER_OF_ITERATIONS})
}

function computeRays (extendedPoints) {
Expand Down
31 changes: 8 additions & 23 deletions src/ray-intersection.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,17 @@ const raySegmentIntersection = require('./ray-segment-intersection').raySegmentI
const _ = require('lodash')

// TODO use sets
async function rayIntersection (pointsToLabel, pointsNotToLabel, isWebgl, webglExtra) {
let {intersectionData, rectangleData} = webglExtra
const computeIntersection = webglExtra.computeIntersection
async function rayIntersection (pointsToLabel, pointsNotToLabel) {
pointsToLabel.forEach(p=> extendedPointMethods.updateAvailableSpace(p))
const rejectedPoints = _.filter(pointsToLabel, p => p.availableMeasure === 0)
// P in the article
var remainingPoints = _.filter(pointsToLabel, p => p.availableMeasure > 0)
var P0 = pointsToLabel.concat(pointsNotToLabel)
const pointsLabeled = [] // Here we differ from the original article, once we find a point in P to label we remove it from P and add it to pointsLabeled, otherwise the algorithm does not finish
while (remainingPoints.length !== 0) {
webglExtra = {computeIntersection, intersectionData, rectangleData}
let bestRay = await findBestRay.findBestRay(remainingPoints, pointsNotToLabel, isWebgl, webglExtra)
let bestRay = await findBestRay.findBestRay(remainingPoints, pointsNotToLabel)
let rij = bestRay.rbest
let pi = bestRay.pbest
intersectionData = bestRay.intersectionData
rectangleData = bestRay.rectangleData
const usedWebgl = bestRay.usedWebgl
if (rij === undefined) {
// It could only happen that we get rij undefined in the first iteration
if (pointsLabeled.length !== 0 || rejectedPoints.length !== 0) {
Expand All @@ -39,28 +33,19 @@ async function rayIntersection (pointsToLabel, pointsNotToLabel, isWebgl, webglE
}
let vi = {x: rij.vector.x * rij.available.getMin(), y: rij.vector.y * rij.available.getMin()}
extendedPointMethods.promoteLabelToRectangle(pi, vi)
//let index = pointsToLabel.findIndex(el => el === pi)
remainingPoints = remainingPoints.filter(el => el !== pi)
P0 = P0.filter(el => el !== pi)
//P0 = P0.filter((el, i) => i!== index)
//P = P.filter((el, i) => i!== index)
pointsLabeled.push(pi)
for (let pk of P0) {
for (let rkl of pk.rays) {
let labelIntersection
let segmentIntersection
if (usedWebgl) {
const index = rkl.index + rij.selfIndex * 4
labelIntersection = interval(intersectionData[index], intersectionData[index + 1])
segmentIntersection = interval(intersectionData[index + 2], intersectionData[index + 3])
} else {
const labelInterval = labelRectangleIntersection.labelRectangleIntersection(pi.rectangle, pk.label, rkl.vector, pk.position)
const segmentInterval = labelSegmentIntersection.labelSegmentIntersection(pi.position, vi, pk.label, rkl.vector, pk.position)
const rayInterval = rayRectangleIntersection(pi.rectangle, rkl.vector, pk.position)
const raySegmentInterval = raySegmentIntersection(pi.position, vi, pk.position, rkl.vector)
labelIntersection = labelInterval.coalesceInPlace(rayInterval)
segmentIntersection = segmentInterval.coalesceInPlace(raySegmentInterval)
}
const labelInterval = labelRectangleIntersection.labelRectangleIntersection(pi.rectangle, pk.label, rkl.vector, pk.position)
const segmentInterval = labelSegmentIntersection.labelSegmentIntersection(pi.position, vi, pk.label, rkl.vector, pk.position)
const rayInterval = rayRectangleIntersection(pi.rectangle, rkl.vector, pk.position)
const raySegmentInterval = raySegmentIntersection(pi.position, vi, pk.position, rkl.vector)
labelIntersection = labelInterval.coalesceInPlace(rayInterval)
segmentIntersection = segmentInterval.coalesceInPlace(raySegmentInterval)
if (!labelIntersection.empty || !segmentIntersection.empty) {
rkl.available.multipleRemove(multiInterval.coalesce(labelIntersection, segmentIntersection))
}
Expand Down
Loading

0 comments on commit f169561

Please sign in to comment.