Skip to content

Instantly share code, notes, and snippets.

@josephg
Created November 4, 2017 23:02
Show Gist options
  • Save josephg/b8de10fba45a53ffd37a145fe81cf79b to your computer and use it in GitHub Desktop.
Save josephg/b8de10fba45a53ffd37a145fe81cf79b to your computer and use it in GitHub Desktop.
// Layers is a list (layers[0] is the bottom). Each layer is:
// clip: polygon[] for clipping layers below
// content: path[] of stuff to actually draw.
const polyToPslg = require('poly-to-pslg')
const pathsToPslg = require('paths-to-pslg')
const pslgToPaths = require('pslg-to-paths')
const pathtrimPslg = require('pathtrim-pslg')
const boxIntersect = require('box-intersect')
const pslgUnion = (a, b) => {
const offset = a.points.length
const points = a.points.concat(b.points)
const edges = a.edges.concat(b.edges.map(([a,b]) => ([a+offset, b+offset])))
return {points, edges}
}
const bbForPath = path => {
if (path.length === 0) return [0,0,0,0]
let l= Infinity, r = -Infinity, t = Infinity, b = -Infinity
for (let i = 0; i < path.length; i++) {
const [x, y] = path[i]
if (x < l) l = x
if (x > r) r = x
if (y < t) t = y
if (y > b) b = y
}
return [l, t, r, b]
}
function flatten(layers) {
if (layers.length == 0) return []
//if (layers.length == 1) return layers[0].content
// I'm going to go from the bottom to the top, aggregating the content and clipping region
// from each layer. Note that each layer is not clipped by its own clipping plane.
// The clipping plane from the bottom layer is unused.
let contentPslg = {points:[], edges:[]} //pathsToPslg(layers[0].content)
let pathsOutside = []
let pathsBBs = []
let clipBBs = []
for (let i = 0; i < layers.length; i++) {
const l = layers[i]
// Clip the aggregate content
if (l.clip) {
const clipBBs = l.clip.map(poly => bbForPath(poly))
const moveIdx = []
boxIntersect(pathsBBs, clipBBs, (pi, ci) => {
moveIdx.push(pi)
})
moveIdx.sort((a, b) => a-b)
let last = -1
const newGeom = []
let off = 0
for (let i = 0; i < moveIdx.length; i++) {
const pi = moveIdx[i]
if (pi === last) continue
last = pi
newGeom.push(pathsOutside[pi-off])
pathsOutside.splice(pi-off, 1)
pathsBBs.splice(pi-off, 1)
off++
}
//console.log('newGeom len', newGeom.length)
contentPslg = pslgUnion(contentPslg, pathsToPslg(newGeom))
const {points, edges} = polyToPslg(l.clip)
contentPslg = pathtrimPslg(contentPslg.points, contentPslg.edges, points, edges, false)
}
// And add the new content.
if (l.content) {
const off = pathsOutside.length
pathsOutside.push.apply(pathsOutside, l.content)
for (let i = off; i < pathsOutside.length; i++) pathsBBs.push(bbForPath(pathsOutside[i]))
}
}
return pslgToPaths(contentPslg.points, contentPslg.edges).concat(pathsOutside)
}
module.exports = flatten
/*
{
const tri = [
[0.5, 0.25],
[0.25, 0.5],
[0.75, 0.75]
]
const sq = [
[0.25, 0.25],
[0.25, 0.6],
[0.6, 0.6],
[0.6, 0.25]
]
const vert = []
for (let x = 0; x <= 1; x += 0.1) {
vert.push([[x, 0], [x, 1]])
}
const horiz = []
for (let y = 0; y <= 1; y += 0.1) {
horiz.push([[0, y], [1, y]])
}
console.log(flatten([
{content: vert},
{clip: [tri]}
]))
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment