tapspace
Version:
A zoomable user interface lib for web apps
74 lines (66 loc) • 1.84 kB
JavaScript
const circle3 = require('affineplane').circle3
module.exports = (Circle) => {
return function (basis, points) {
// @Circle:fromPoints(basis, points)
//
// Find an approximate bounding circle for the given set of points.
// Reads only x and y. The circle has z coordinate at the basis origin.
// The circle contains all the points but can be larger than minimal.
// Empty array will return zero-size circle at origin.
//
// Parameters:
// basis
// a Component for the circle
// points
// array of Point
//
// Return:
// a Circle
//
if (points.length === 0) {
return new Circle(basis, circle3.ZERO)
}
// Transit the points onto the basis
const ps = []
for (let i = 0; i < points.length; i += 1) {
const p = points[i]
ps.push(p.transitRaw(basis))
}
const len = ps.length
// TODO use circle3.boundingCircle
// Find bounding box and use its center point for the circle.
let minx = ps[0].x
let maxx = ps[0].x
let miny = ps[0].y
let maxy = ps[0].y
for (let i = 1; i < len; i += 1) {
const p = ps[i]
minx = Math.min(minx, p.x)
maxx = Math.max(maxx, p.x)
miny = Math.min(miny, p.y)
maxy = Math.max(maxy, p.y)
}
// The circle center
const cx = minx + (maxx - minx) / 2
const cy = miny + (maxy - miny) / 2
// Find distance to the farthest point
let maxDist2 = 0
for (let i = 0; i < len; i += 1) {
const p = ps[i]
const dx = (cx - p.x)
const dy = (cy - p.y)
const dist2 = dx * dx + dy * dy
if (dist2 > maxDist2) {
maxDist2 = dist2
}
}
// The circle radius
const cr = Math.sqrt(maxDist2)
return new Circle(basis, {
x: cx,
y: cy,
z: 0,
r: cr
})
}
}