Skip to content

Commit 480a45f

Browse files
committed
Version 1.6.0
New example. Bugfix for plotting. Code cleaning.
2 parents 17da030 + 67fd9d5 commit 480a45f

File tree

4 files changed

+92
-6
lines changed

4 files changed

+92
-6
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.6.0] (2021-01-22)
8+
9+
### Added
10+
11+
- Added a demo for 3D surfaces with cylindrical symmetries. (`examples/example1.py`)
12+
13+
### Fixes
14+
15+
- Fixes incorrectly plotted curves when no `imshow` has been called.
16+
- Fixes ugly coefficient calculation code.
17+
718
## [1.5.1] (2021-01-22)
819

920
### Added

examples/example1.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
5+
An example showing how to use pyefd for fitting points along a closed curve.
6+
7+
The demo curves are taken from:
8+
"Optimized Fourier representations for three-dimensional magnetic surfaces"
9+
by S. P. Hirshman and H. K. Meier,
10+
Phys. Fluids 28 (5) 1985 (https://doi.org/10.1063/1.864972)
11+
12+
13+
Created by Jonathan Schilling <jonathan.schilling@mail.de> on 2021-12-09.
14+
15+
"""
16+
17+
import numpy as np
18+
19+
# hack to get the import from within module directory to work
20+
# see also: https://stackoverflow.com/a/16985066
21+
import sys
22+
import os
23+
SCRIPT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
24+
if not SCRIPT_DIR in sys.path:
25+
sys.path.append(SCRIPT_DIR)
26+
27+
import pyefd
28+
29+
# bean
30+
rCos = np.array([3.0, 1.042, 0.502, -0.0389])
31+
zSin = np.array([0.0, 1.339, 0.296, -0.0176])
32+
33+
# diamond
34+
# rCos = np.array([0.5, 0.427, 0.0, 0.0732])
35+
# zSin = np.array([0.0, 0.427, 0.0, -0.0732])
36+
37+
# D
38+
# rCos = np.array([3.0, 0.991, 0.136])
39+
# zSin = np.array([0.0, 1.409, -0.118])
40+
41+
# belt
42+
# rCos = np.array([3.0, 0.453, 0.0, 0.0 ])
43+
# zSin = np.array([0.0, 0.6 , 0.0, 0.196])
44+
45+
# ellipse
46+
# rCos = np.array([3.0, 1.0])
47+
# zSin = np.array([0.0, 3.0])
48+
49+
# evaluate curve geometry given as Fourier coefficients above
50+
n = 300
51+
contour = np.zeros([n,2])
52+
theta = np.linspace(0.0, 2.0*np.pi, n)
53+
mMax = len(rCos)
54+
for m in range(mMax):
55+
contour[:,0] += rCos[m] * np.cos(m*theta)
56+
contour[:,1] += zSin[m] * np.sin(m*theta)
57+
58+
# apply pyefd to get elliptic Fourier descriptors
59+
coeffs = pyefd.elliptic_fourier_descriptors(contour)
60+
a0, c0 = pyefd.calculate_dc_coefficients(contour)
61+
pyefd.plot_efd(coeffs, locus=(a0,c0), contour=contour)

pyefd.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ def elliptic_fourier_descriptors(
6060
orders = np.arange(1, order + 1)
6161
consts = T / (2 * orders * orders * np.pi * np.pi)
6262
phi = phi * orders.reshape((order, -1))
63+
6364
d_cos_phi = np.cos(phi[:, 1:]) - np.cos(phi[:, :-1])
6465
d_sin_phi = np.sin(phi[:, 1:]) - np.sin(phi[:, :-1])
65-
cos_phi = (dxy[:, 0] / dt) * d_cos_phi
66-
a = consts * np.sum(cos_phi, axis=1)
66+
67+
a = consts * np.sum((dxy[:, 0] / dt) * d_cos_phi, axis=1)
6768
b = consts * np.sum((dxy[:, 0] / dt) * d_sin_phi, axis=1)
6869
c = consts * np.sum((dxy[:, 1] / dt) * d_cos_phi, axis=1)
6970
d = consts * np.sum((dxy[:, 1] / dt) * d_sin_phi, axis=1)
@@ -247,10 +248,23 @@ def plot_efd(coeffs, locus=(0.0, 0.0), image=None, contour=None, n=300):
247248
)
248249
ax = plt.subplot2grid((n_rows, N_half), (n // N_half, n % N_half))
249250
ax.set_title(str(n + 1))
250-
if contour is not None:
251-
ax.plot(contour[:, 1], contour[:, 0], "c--", linewidth=2)
252-
ax.plot(yt, xt, "r", linewidth=2)
251+
253252
if image is not None:
253+
# A background image of shape [rows, cols] gets transposed
254+
# by imshow so that the first dimension is vertical
255+
# and the second dimension is horizontal.
256+
# This implies swapping the x and y axes when plotting a curve.
257+
if contour is not None:
258+
ax.plot(contour[:, 1], contour[:, 0], "c--", linewidth=2)
259+
ax.plot(yt, xt, "r", linewidth=2)
254260
ax.imshow(image, plt.cm.gray)
261+
else:
262+
# Without a background image, no transpose is implied.
263+
# This case is useful when (x,y) point clouds
264+
# without relation to an image are to be handled.
265+
if contour is not None:
266+
ax.plot(contour[:, 0], contour[:, 1], "c--", linewidth=2)
267+
ax.plot(xt, yt, "r", linewidth=2)
268+
ax.axis("equal")
255269

256270
plt.show()

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
EMAIL = "henrik.blidh@nedomkull.com"
2323
AUTHOR = "Henrik Blidh"
2424
REQUIRES_PYTHON = ">=2.7.10"
25-
VERSION = "1.5.1"
25+
VERSION = "1.6.0"
2626

2727
REQUIRED = ["numpy>=1.7.0"]
2828

0 commit comments

Comments
 (0)