249 lines
8.2 KiB
Plaintext
249 lines
8.2 KiB
Plaintext
var geometry = this
|
|
geometry[cell.DOC] = `
|
|
A collection of geometry-related functions for circles, spheres, boxes, polygons,
|
|
and rectangle utilities. Some functionality is implemented in C and exposed here.
|
|
`
|
|
|
|
var math = use('math')
|
|
|
|
geometry.box = {}
|
|
geometry.box[cell.DOC] = `
|
|
An object for box-related operations. Overridden later by a function definition, so
|
|
its direct usage is overshadowed. Contains:
|
|
- points(ll, ur): Return an array of four 2D points for a box from ll (lower-left) to ur (upper-right).
|
|
`
|
|
|
|
geometry.box.points = function (ll, ur) {
|
|
return [ll, ll.add([ur.x - ll.x, 0]), ur, ll.add([0, ur.y - ll.y])]
|
|
}
|
|
geometry.box.points[cell.DOC] = `
|
|
:param ll: Lower-left coordinate as a 2D vector (x,y).
|
|
:param ur: Upper-right coordinate as a 2D vector (x,y).
|
|
:return: An array of four points forming the corners of the box in order [ll, lower-right, ur, upper-left].
|
|
Compute the four corners of a box given lower-left and upper-right corners.
|
|
`
|
|
|
|
geometry.sphere = {}
|
|
geometry.sphere[cell.DOC] = `
|
|
Sphere-related geometry functions:
|
|
- volume(r): Return the volume of a sphere with radius r.
|
|
- random(r, theta, phi): Return a random point on or inside a sphere.
|
|
`
|
|
|
|
geometry.circle = {}
|
|
geometry.circle[cell.DOC] = `
|
|
Circle-related geometry functions:
|
|
- area(r): Return the area of a circle with radius r.
|
|
- random(r, theta): Return a random 2D point on a circle; uses sphere.random internally and extracts x,z.
|
|
`
|
|
|
|
geometry.sphere.volume = function (r) {
|
|
return (Math.pi * r * r * r * 4) / 3
|
|
}
|
|
geometry.sphere.volume[cell.DOC] = `
|
|
:param r: The sphere radius.
|
|
:return: The volume of the sphere, calculated as (4/3) * pi * r^3.
|
|
`
|
|
|
|
geometry.sphere.random = function (r, theta = [0, 1], phi = [-0.5, 0.5]) {
|
|
if (typeof r == "number") r = [r, r]
|
|
if (typeof theta == "number") theta = [theta, theta]
|
|
if (typeof phi == "number") phi = [phi, phi]
|
|
|
|
var ra = Math.random_range(r[0], r[1])
|
|
var ta = Math.turn2rad(Math.random_range(theta[0], theta[1]))
|
|
var pa = Math.turn2rad(Math.random_range(phi[0], phi[1]))
|
|
return [ra * Math.sin(ta) * Math.cos(pa), ra * Math.sin(ta) * Math.sin(pa), ra * Math.cos(ta)]
|
|
}
|
|
geometry.sphere.random[cell.DOC] = `
|
|
:param r: A single number (radius) or a 2-element array [minRadius, maxRadius].
|
|
:param theta: A single number or 2-element array defining the range in turns for the theta angle, default [0,1].
|
|
:param phi: A single number or 2-element array defining the range in turns for the phi angle, default [-0.5,0.5].
|
|
:return: A 3D point (x,y,z) randomly placed within a sphere.
|
|
Generate a random point inside a sphere of variable radius, distributing angles in the specified ranges.
|
|
`
|
|
|
|
geometry.circle.area = function (r) {
|
|
return Math.pi * r * r
|
|
}
|
|
geometry.circle.area[cell.DOC] = `
|
|
:param r: Radius of the circle.
|
|
:return: The area, pi * r^2.
|
|
`
|
|
|
|
geometry.circle.random = function (r, theta) {
|
|
return geometry.sphere.random(r, theta).xz
|
|
}
|
|
geometry.circle.random[cell.DOC] = `
|
|
:param r: A radius or [minRadius, maxRadius].
|
|
:param theta: Angle range in turns (single number or [min,max]).
|
|
:return: A 2D point (x,z) in the circle, using the sphere random generator and ignoring y.
|
|
`
|
|
|
|
geometry.box = function (w, h) {
|
|
w /= 2
|
|
h /= 2
|
|
var points = [
|
|
[w, h],
|
|
[-w, h],
|
|
[-w, -h],
|
|
[w, -h],
|
|
]
|
|
return points
|
|
}
|
|
geometry.box[cell.DOC] = `
|
|
:param w: The width of the box.
|
|
:param h: The height of the box.
|
|
:return: An array of four 2D points representing the corners of a rectangle centered at [0,0].
|
|
Construct a box centered at the origin with the given width and height. This overrides the box object above.
|
|
`
|
|
|
|
geometry.ngon = function (radius, n) {
|
|
return geometry.arc(radius, 360, n)
|
|
}
|
|
geometry.ngon[cell.DOC] = `
|
|
:param radius: The radius of the n-gon from center to each vertex.
|
|
:param n: Number of sides/vertices.
|
|
:return: An array of 2D points forming a regular n-gon.
|
|
Generates a regular n-gon by calling geometry.arc with full 360 degrees.
|
|
`
|
|
|
|
geometry.arc = function (radius, angle, n, start = 0) {
|
|
start = Math.deg2rad(start)
|
|
if (angle >= 360) angle = 360
|
|
if (n <= 1) return []
|
|
var points = []
|
|
angle = Math.deg2rad(angle)
|
|
var arclen = angle / n
|
|
for (var i = 0; i < n; i++) points.push(math.rotate([radius, 0], start + arclen * i))
|
|
return points
|
|
}
|
|
geometry.arc[cell.DOC] = `
|
|
:param radius: The distance from center to the arc points.
|
|
:param angle: The total angle (in degrees) over which points are generated, capped at 360.
|
|
:param n: Number of segments (if <=1, empty array is returned).
|
|
:param start: Starting angle (in degrees), default 0.
|
|
:return: An array of 2D points along the arc.
|
|
Generate an arc (or partial circle) of n points, each angle spread equally over 'angle' degrees from 'start'.
|
|
`
|
|
|
|
geometry.circle.points = function (radius, n) {
|
|
if (n <= 1) return []
|
|
return geometry.arc(radius, 360, n)
|
|
}
|
|
geometry.circle.points[cell.DOC] = `
|
|
:param radius: The circle's radius.
|
|
:param n: Number of points around the circle.
|
|
:return: An array of 2D points equally spaced around a full 360-degree circle.
|
|
Shortcut for geometry.arc(radius, 360, n).
|
|
`
|
|
|
|
geometry.corners2points = function (ll, ur) {
|
|
return [ll, ll.add([ur.x, 0]), ur, ll.add([0, ur.y])]
|
|
}
|
|
geometry.corners2points[cell.DOC] = `
|
|
:param ll: Lower-left 2D coordinate.
|
|
:param ur: Upper-right 2D coordinate (relative offset in x,y).
|
|
:return: A four-point array of corners [ll, lower-right, upper-right, upper-left].
|
|
Similar to box.points, but calculates differently.
|
|
`
|
|
|
|
geometry.sortpointsccw = function (points) {
|
|
var cm = points2cm(points)
|
|
var cmpoints = points.map(function (x) { return x.sub(cm) })
|
|
var ccw = cmpoints.sort(function (a, b) {
|
|
var aatan = Math.atan2(a.y, a.x)
|
|
var batan = Math.atan2(b.y, b.x)
|
|
return aatan - batan
|
|
})
|
|
return ccw.map(function (x) { return x.add(cm) })
|
|
}
|
|
geometry.sortpointsccw[cell.DOC] = `
|
|
:param points: An array of 2D points.
|
|
:return: A new array of the same points, sorted counterclockwise around their centroid.
|
|
Sort an array of points in CCW order based on their angles from the centroid.
|
|
`
|
|
|
|
function points2cm(pts) {
|
|
var x = 0
|
|
var y = 0
|
|
var n = pts.length
|
|
pts.forEach(function (p) {
|
|
x += p[0]
|
|
y += p[1]
|
|
})
|
|
return [x / n, y / n]
|
|
}
|
|
|
|
geometry.points2cm = function(points) {
|
|
var x = 0
|
|
var y = 0
|
|
var n = points.length
|
|
points.forEach(function (p) {
|
|
x += p[0]
|
|
y += p[1]
|
|
})
|
|
return [x / n, y / n]
|
|
}
|
|
geometry.points2cm[cell.DOC] = `
|
|
:param points: An array of 2D points.
|
|
:return: The centroid (average x,y) of the given points.
|
|
`
|
|
|
|
geometry.rect_intersection[cell.DOC] = `
|
|
:param a: The first rectangle as {x, y, w, h}.
|
|
:param b: The second rectangle as {x, y, w, h}.
|
|
:return: A rectangle that is the intersection of the two. May have zero width/height if no overlap.
|
|
Return the intersection of two rectangles. The result may be empty if no intersection.
|
|
`
|
|
|
|
geometry.rect_intersects[cell.DOC] = `
|
|
:param a: Rectangle {x,y,w,h}.
|
|
:param b: Rectangle {x,y,w,h}.
|
|
:return: A boolean indicating whether the two rectangles overlap.
|
|
`
|
|
|
|
geometry.rect_expand[cell.DOC] = `
|
|
:param a: Rectangle {x,y,w,h}.
|
|
:param b: Rectangle {x,y,w,h}.
|
|
:return: A new rectangle that covers the bounds of both input rectangles.
|
|
Merge or combine two rectangles, returning their bounding rectangle.
|
|
`
|
|
|
|
geometry.rect_inside[cell.DOC] = `
|
|
:param inner: A rectangle to test.
|
|
:param outer: A rectangle that may contain 'inner'.
|
|
:return: True if 'inner' is completely inside 'outer', otherwise false.
|
|
`
|
|
|
|
geometry.rect_random[cell.DOC] = `
|
|
:param rect: A rectangle {x,y,w,h}.
|
|
:return: A random point within the rectangle (uniform distribution).
|
|
`
|
|
|
|
geometry.cwh2rect[cell.DOC] = `
|
|
:param center: A 2D point [cx, cy].
|
|
:param wh: A 2D size [width, height].
|
|
:return: A rectangle {x, y, w, h} with x,y set to center and w,h set to the given size.
|
|
Helper: convert a center point and width/height vector to a rect object.
|
|
`
|
|
|
|
geometry.rect_point_inside[cell.DOC] = `
|
|
:param rect: A rectangle {x,y,w,h}.
|
|
:param point: A 2D point [px, py].
|
|
:return: True if the point lies inside the rectangle, otherwise false.
|
|
`
|
|
|
|
geometry.rect_pos[cell.DOC] = `
|
|
:param rect: A rectangle {x,y,w,h}.
|
|
:return: A 2D vector [x,y] giving the rectangle's position.
|
|
`
|
|
|
|
geometry.rect_move[cell.DOC] = `
|
|
:param rect: A rectangle {x,y,w,h}.
|
|
:param offset: A 2D vector to add to the rectangle's position.
|
|
:return: A new rectangle with updated x,y offset.
|
|
`
|
|
|
|
return geometry
|