The answer to Convolute a gaussian kernel with a large array of off-grid centroids without looping? (how to make "A Thousand (Gaussian) Points of Light" ) involves summing a 3D array over its stacked direction. This question however is about convolution using continuous functions rather than summation.
The answer there mentions
a Fredholm integral equation of the first kind
and that may work here as well.
I'd like to know how to convolute $f(x, y)$ with a parametric shape; a 1D distribution along a parametric path as defined by $g(t), \ x(t), \ y(t)$ in Python, resulting in a 2D array of $f * g$.
A simple, illustrative example of this would be:
$$g(t) = \frac{\Gamma^2}{(t-t_0)^2 + \Gamma^2} \ \ \ \text{ (the 1D function)}$$
$$x(t), \ y(t) = a\cos(t), \ a\sin(t) \ \ \ \text{ (over the 2D path)}$$
$$f(x, y) = \exp(-(x^2+y^2)/2 \sigma^2) \ \ \ \text{ (the 2D kernel)}$$
If $a=1, \ t_0 = \pi/3, \ \Gamma = \pi/30 \text{ and } \sigma=0.05$ then I can approximate it brute force as follows; I've made the parametric variable $t$ step size large intentionally to illustrate the sum. Making it sufficiently densely spaced to reduce the error in the result and adding the appropriate normalization (omitted here) is the brute-force solution, I'm wondering if I'm missing a simpler solution that might be better in cases where the parametric function is longer and more complicated.
note: "Better" in this case means faster, as in running mostly in compiled code of some Python method or library; this calculation will be done perhaps 100 times each time a user touches a slider in an interactive simulation.
import numpy as np
import matplotlib.pyplot as plt
t_0 = np.pi/3
gamma = np.pi/20
a = 1.
sigma = 0.05
all_t = np.linspace(0, 2*np.pi, 50) # big steps to make it bumpy
all_g = gamma**2 / ((all_t - t_0)**2 + gamma**2)
y, x = np.mgrid[-1:2:501j, -1:2:501j]
result = np.zeros_like(y)
for (yc, xc), g in zip(zip(*[a * f(all_t) for f in (np.sin, np.cos)]), all_g):
result += g * np.exp(-((x-xc)**2 + (y-yc)**2)/(2*sigma**2))
extent = [x.min(), x.max(), y.min(), y.max()]
plt.imshow(result, origin='lower', extent=extent)
plt.show()