tmp = {{0, 0}, {1, 1}, {2, 1}, {3, 2}, {1, 0.5}};
plot = ListLinePlot[tmp, Filling -> Axis];
Area@DiscretizeGraphics[plot]
(* 0.833333 *)
I figured out how to use Graphics`PolygonUtils`SimplePolygonPartition
. It subdivides the polygon from the plot into non self-intersecting, possibly nonconvex polygons, but some of the polygons it creates lie outside the original polygon. One difficulty is finding a point inside a polygon. We do that by searching for a point, the average of three consecutive vertices, that is inside a convex angle. The subdivision also creates vertices along edges. Numerical round-off error makes it difficult to detect when point is inside or outside the polygon, so we skip those.
ClearAll[findPtInPoly];
SetAttributes[findPtInPoly, Listable];
findPtInPoly::nopt = "Warning: Could not find point inside polygon ``; returning a vertex";
tolerance = 1*^-10;
findPtInPoly[Polygon[poly_]] :=
Module[{point},
Do[With[{pts = poly[[t ;; t + 2]]},
If[VectorAngle @@ Differences[pts] > tolerance && (* == not collinear *)
Graphics`PolygonUtils`InPolygonQ[Polygon[poly], Mean[pts]],
point = Mean[pts];
Break[]]],
{t, Length[poly] - 2}];
If[VectorQ[point],
point,
Message[findPtInPoly::nopt, poly];
First[poly]]]
SeedRandom[0];
npts = 300;
tmp = RandomReal[100, {npts, 2}];
plot = ListLinePlot[tmp, Filling -> Axis]
With[{poly = First@Cases[Normal@plot, _Polygon, Infinity]},
Total[If[Graphics`PolygonUtils`InPolygonQ[poly, findPtInPoly[#]],
Graphics`PolygonUtils`PolygonArea[#], 0] & /@
Graphics`PolygonUtils`SimplePolygonPartition[poly]]
] // AbsoluteTiming
Area@DiscretizeGraphics[plot] // AbsoluteTiming
(*
{2.931620, 4368.75}
{32.840698, 4368.75}
*)
It seems to have better time complexity than Area @* DiscretizeGraphics
. On inputs of size npts
equal to 100, 200, 300, the timing ratios of the two methods are {1.8112, 4.16505, 11.2022}
.
ComponentMeasurements[ColorNegate@Binarize@ListLinePlot[tmp, Filling -> Axis, Axes -> False], "Area"]
$\endgroup$