Edit: Added trapezoidal rule for higher accurary.
Rahul's code does what the OP asked for. However, it is still not too fast.
The bottlenecks are signedAngle
and Sum
. Both can be vectorized away. The following is a function that maps a MeshRegion
to a CompiledFunction
that maps points to their average radius. In contrast to Rahul, we can also use the trapezoidal for integration. It comes at virtually free cost (we employ RotateRight
) and should improve the accuracy significantly.
avgRadiusFunction[curve_MeshRegion] :=
Module[{q, e1, e2}, q = MeshCoordinates[curve];
{e1, e2} = Transpose[Developer`ToPackedArray[MeshCells[curve, 1][[All, 1]]]];
With[{q11 = q[[e1, 1]], q12 = q[[e1, 2]], q21 = q[[e2, 1]], q22 = q[[e2, 2]]},
Compile[{{p, _Real, 1}},
Module[{u11, u12, u21, u22, r, dθ, r2squared, r2, p1, p2},
p1 = Part[p, 1];
p2 = Part[p, 2];
u11 = q11 - p1;
u12 = q12 - p2;
u21 = q21 - p1;
u22 = q22 - p2;
r2squared = (u21^2 + u22^2);
dθ = ArcTan[(u11 u21 + u12 u22)/r2squared, (u12 u21 - u11 u22)/r2squared];
r2 = Sqrt[r2squared];
Abs[(RotateRight[r2] + r2).dθ/(4. Pi)]
],
RuntimeAttributes -> {Listable},
Parallelization -> True]
]
];
Using
curve = DiscretizeRegion[ ImplicitRegion[x^2+y^2+Sin[4*x]+Sin[4*y] == 4, {{x, -3, 3}, {y, -4, 4}}], {{-3, 3},{-4, 4}}, AccuracyGoal -> 8]
from this post by the OP, we can create an averaged radius function like this:
f = avgRadiusFunction[curve]; // AbsoluteTiming
{0.050776, Null}
Comparison to avgRadius
:
avgRadius[{0.1, 0.}] // AbsoluteTiming
f[{0.1, 0.}] // AbsoluteTiming
{0.999227, 1.99519}
{0.002347, 1.99519}
So, this is already 425 times faster.
Moreover, the resulting function f
threads over list:
pp = RandomReal[{-10, 10}, {1000, 2}];
f[pp]; // AbsoluteTiming
{0.590299, Null}
Per point, this is 1800 times faster than avgRadius
.
Update
In the meantime I was able to produce a version of the function above that produces CompiledFunction
s that are more tolerant towards evaluation on the boundary - at the cost of some speed, though.
avgRadiusFunction[curve_MeshRegion] := Module[{q, e1, e2},
q = MeshCoordinates[curve];
{e1, e2} =
Transpose[
Developer`ToPackedArray[MeshCells[curve, 1][[All, 1]]]];
With[{
q11 = q[[e1, 1]], q12 = q[[e1, 2]], q21 = q[[e2, 1]],
q22 = q[[e2, 2]]
},
Compile[{{p, _Real, 1}},
Module[{u11, u12, u21, u22, r, dθ, r2squared, r2, p1, p2, idx, bag, z1, z2, j},
p1 = Part[p, 1];
p2 = Part[p, 2];
u11 = q11 - p1;
u12 = q12 - p2;
u21 = q21 - p1;
u22 = q22 - p2;
r2squared = (u21^2 + u22^2);
z1 = (u11 u21 + u12 u22);
z2 = (u12 u21 - u11 u22);
bag = Internal`Bag[{0}];
Do[If[(r2squared[[i]] < 1. 10^-14) || ((Abs[z1[[i]]] < 1. 10^-14) && (Abs[z2[[i]]] < 1. 10^-14)),
Internal`StuffBag[bag, i]], {i, 1, Length[r2squared]}
];
idx = Internal`BagPart[bag, All];
If[Length[idx] > 1,
Do[j = idx[[i]]; r2squared[[j]] = 1.; z1[[j]] = 1.; z2[[j]] = 1.;, {i, 2, Length[idx]}];
dθ = ArcTan[z1/r2squared, z2/r2squared];
Do[dθ[[idx[[i]]]] = 0.;, {i, 2, Length[idx]}];
,
dθ = ArcTan[z1/r2squared, z2/r2squared];
];
r2 = Sqrt[r2squared];
Abs[(RotateRight[r2] + r2).dθ/(4. Pi)]
],
CompilationTarget -> "WVM",
RuntimeAttributes -> {Listable},
Parallelization -> True
]]];