I have a list of 2d points which are the control vertices (Dx) for a closed uniform cubic B-spline. I am assuming a simple curve (non-self-intersecting, all control points are distinct).
I am trying to find the area enclosed by the curve:
If I calculate the knot points (Px), I can treat the curve as a polygon; then I "just" need to find the remaining delta areas, for each segment, between the actual curve and the straight line joining the knot points.
I understand that the shape (and therefore area) of a Bspline is invariant under rotation and translation - so for each segment, I can find a translation to put the t=0 knot at the origin and a rotation to put the t=1 knot on the +x axis:
I can find the equation for the curve by plugging the points in and re-grouping:
P(t) = (
(t**3)*(-Dm1 + 3*D0 - 3*D1 + D2)
+ (t**2)*(3*Dm1 - 6*D0 + 3*D1)
+ t*(-3*Dm1 + 3*D1)
+ (Dm1 + 4*D0 + D1)
) / 6.
but I'm tearing my hair out trying to integrate it - I can do
1
/
| Py(t) dt
/
t=0
but that doesn't give me area. I think what I need is
Px(t=1)
/
| Py(t) (dPx(t) / dt) dt
/
x = Px(t=0)
but before I go further, I'd really like to know:
Is this the correct calculation for area? Ideally, an analytic solution would make my day!
Once I find this area, how can I tell whether I need to add or subtract it from the base poly (red vs green areas in the first diagram)?
Are there any Python modules which would do this calculation for me? Numpy has some methods for evaluating cubic Bsplines, but none which seem to deal with area.
Is there an easier way to do this? I am thinking about maybe evaluating P(t) at a bunch of points - something like t = numpy.arange(0.0, 1.0, 0.05)
- and treating the whole thing as a polygon. Any idea how many subdivisions are needed to guarantee a given level of accuracy (I'd like error < 1%)?
Personally, I would use the splines to their best advantage and rewrite the area integral as a contour integral using Green's theorem.
Since you already know the curve, it'll be an easy matter to do the integration using Gaussian quadrature of sufficient order. No shenanigans to estimate the extra areas needed. I'll bet it'll be computationally efficient as well, because Gaussian quadrature over polynomials is so well behaved. Cubic B-splines will integrate nicely.
I would write your code in such a way that the first and last points must be coincident. That's an invariant on the problem.
This approach will even work for areas with holes in them. You integrate along the outer curve, make an imaginary straight line that connects the outer to the inner curve, integrate along the inner curve, then pass back to the outer along the straight line that got you there.
The result you get is the area for one segment of the whole curve. Some will be negative, as they face the opposite direction. If you sum up the areas for all segments, you get the area of the whole curve.
The result is:
Areai = (31 xi-1yi + 28 xi-1yi+1 + xi-1yi+2 - 31 xiyi-1 + 183 xiyi+1 + 28 xiyi+2 - 28 xi+1yi-1 - 183 xi+1yi + 31 xi+1yi+2 - xi+2yi-1 - 28 xi+2yi - 31 xi+2yi+1) / 720
If you convert it into matrix form, you get:
Areai = <xi-1 xi xi+1 xi+2> · P · <yi-1 yi yi+1 yi+2>T
where P is
[ 0 31 28 1]
[ -31 0 183 28] / 720
[ -28 -183 0 31]
[ -1 -28 -31 0]
If the control points are [(0,0) (1,0) (1,1) (0,1)]
, the resulting areas become:
[(0,0), (1,0), (1,1), (0,1)] -> 242/720
[(1,0), (1,1), (0,1), (0,0)] -> 242/720
[(1,1), (0,1), (0,0), (1,0)] -> 2/720
[(0,1), (0,0), (1,0), (1,1)] -> 2/720
The sum is 488/720 = 61/90.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With