4
$\begingroup$

There is a very cool application in maple for generating parametric Curves from 2-D Data using Discrete Fourier Transforms https://www.maplesoft.com/Applications/Detail.aspx?id=154546

I wonder if a similar implementation is already available in Mathematica. If not, then how could one translate the maple code into a Mathematica one?

$\endgroup$

2 Answers 2

4
$\begingroup$

The code can be found in the PDF on the same page and is included at the bottom of this answer for reference.

It is straightforward to translate to Mathematica:

ClearAll[parCurve2D]
parCurve2D[{x_?(VectorQ[#, NumericQ] &), y_?(VectorQ[#, NumericQ] &)}] :=
 Module[{n, dftX, dftY, px, py, ax, ay, curveX, curveY},
   n = Length[x];
  
   {dftX, dftY} = Fourier /@ {x, y};
  
   {px, py} = Re[Arg[#[[2 ;; Ceiling[n/2]]]]] & /@ {dftX, dftY};
   {ax, ay} = Abs[ #[[2 ;; Ceiling[n/2]]] ] & /@ {dftX, dftY};
  
   curveX = 3 Sum[ax[[i]] Sin[Pi/2 - 2 Pi/n*i*t + px[[i]] ], {i, 1, Ceiling[n/2] - 1}];
   curveY = 3 Sum[ay[[i]] Sin[Pi/2 - 2 Pi/n*i*t + py[[i]] ], {i, 1, Ceiling[n/2] - 1}];
  
   ParametricPlot[{curveX, curveY}, {t, 0, n/2}]
 ]

Using the example points in that PDF as well, one can generate the same figure:

parCurve2D[{x, y}]

"maple" as a parametric curve


Data points:

x = {558, 533, 512, 509, 519, 538, 527, 501, 485, 493, 499, 499, 465, 
     406, 413, 441, 430, 406, 397, 393, 392, 397, 403, 406, 397, 365, 
     356, 354, 326, 311, 336, 355, 315, 278, 278, 249, 242, 232, 204, 
     208, 209, 204, 206, 242, 242, 263, 274, 287, 334, 361, 320, 314, 
     344, 355, 360, 379, 406, 407, 399, 393, 391, 394, 404, 415, 439, 
     431, 402, 438, 487, 503, 497, 486, 488, 514, 538, 537, 511, 508, 
     518, 546};
y = {420, 412, 431, 480, 522, 505, 444, 431, 480, 591, 605, 548, 444, 
     416, 426, 478, 516, 533, 488, 395, 305, 356, 463, 525, 418, 414, 
     488, 465, 416, 458, 497, 495, 463, 409, 488, 514, 458, 512, 441, 
     441, 485, 412, 508, 493, 463, 516, 437, 403, 503, 482, 495, 429, 
     416, 499, 439, 407, 490, 533, 392, 309, 333, 452, 520, 529, 499, 
     452, 418, 416, 492, 593, 606, 544, 448, 429, 482, 518, 508, 450, 
     424, 414};

Original Maple code from Generating Parametric Curves from 2-D Data using Discrete Fourier Transforms:

n := numelems(X):
P_X := Re(argument~(dft_X[2..ceil(n / 2)])):
A_X := abs(dft_X[2..ceil(n / 2)]):
P_Y := Re(argument~(dft_Y[2..ceil(n / 2)])):
A_Y := abs(dft_Y[2..ceil(n / 2)]):

curve_X := evalf[3](add(seq(A_X[i] * sin(Pi / 2 - 2 * Pi / n * i
  * t + P_X[i]), i = 1..ceil(n / 2) - 1)));
curve_Y := evalf[3](add(seq(A_Y[i] * sin(Pi / 2 - 2 * Pi / n * i
  * t + P_Y[i]), i = 1..ceil(n / 2) - 1)));

plot([curve_X, curve_Y, t = 0 .. n / 2])
$\endgroup$
3
  • $\begingroup$ thanks for the reply. I think there is a problem with your implementation though. The reconstructed curve doesn't fit the data. $\endgroup$
    – SpaceChild
    Oct 5 at 17:00
  • $\begingroup$ @SpaceChild Neither did the original implementation in Maple: you will notice that the raw points are not centered around the origin, but the resulting Maple parametric function is, so they would not have fit anyway. There may also be a scaling factor difference: I noticed that the Maple code introduced a factor of 3 in the parametric curves; I left it there in the MMA code as well. You may play with that factor to get a better fit, but you should mean-center the points first. Consider: Show[parCurve2D[{x, y}], ListPlot[Standardize[Transpose[{x, y}], Mean, 1 &]], PlotRange -> All ]. $\endgroup$
    – MarcoB
    Oct 5 at 18:57
  • $\begingroup$ you're right. Ok it seems reasonable to appear centered at the origin, but as regards the scaling I would expect the scaling factor to occur naturally from the Fourier transform. Interestingly the scaling factor that should be used after deleting the coefficients 3 is 1/8. Any idea why is this? $\endgroup$
    – SpaceChild
    Oct 5 at 19:20
2
$\begingroup$

In MMA we have the function "Interpolation". With this it is easy to do what you want.

Here is a simple example. First we create some points on an arbitrary test curve:

dat = Table[{Sin[x], Sin[2 x + 1]}, {x, 0, 2 Pi, 2 Pi/20}];
ListLinePlot[dat, Epilog -> Point[dat]]

enter image description here

Then we interpolate the x and y values

fun0[x_] = {Interpolation[dat[[All, 1]]][x], 
  Interpolation[dat[[All, 2]]][x]}

Now the argument of this function runs from 1 to Length[dat]. To make a more convenient parametrization, we may define a function with argument between 0 and 1:

fun[x_] = fun0[x (Length[dat] - 1) + 1]

ParametricPlot[fun[x ], {x, 0, 1}, Epilog -> Point[dat]]

enter image description here

As an additional benefit, we get a smooth curve.

$\endgroup$
2
  • $\begingroup$ yes right, one can exploit various sorts of curve fitting, but I am interested particularly in the DFT method. $\endgroup$
    – SpaceChild
    Oct 5 at 16:19
  • $\begingroup$ Why makes things complicated? $\endgroup$ Oct 5 at 16:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.