Skip to content

Commit bf130c7

Browse files
authored
Merge pull request #78 from bwohlberg/master
New geometry class and some minor fixes
2 parents afbd052 + b40a50e commit bf130c7

File tree

9 files changed

+103
-31
lines changed

9 files changed

+103
-31
lines changed

docs/source/api/xdesign.geometry.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Two dimensional entities
5353
Curve
5454
Circle
5555
Polygon
56+
RegularPolygon
5657
Triangle
5758
Rectangle
5859
Square

docs/source/conf.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
napoleon_use_param = False
5858
napoleon_use_rtype = False
5959

60+
# Bibtex settings
61+
bibtex_bibfiles = ['bibtex/cite.bib', 'bibtex/refs.bib']
62+
6063
# Add any paths that contain templates here, relative to this directory.
6164
templates_path = ['_templates']
6265

setup.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,19 @@
4646
# POSSIBILITY OF SUCH DAMAGE. #
4747
# #########################################################################
4848

49+
import os
4950
from setuptools import setup, find_packages
5051

52+
long_desc = """
53+
XDesign is an open-source Python package for generating configurable
54+
x-ray imaging phantoms, simulating data acquisition, and benchmarking x-ray
55+
tomographic image reconstruction.
56+
"""
57+
58+
with open(os.path.join("requirements.txt")) as f:
59+
lines = f.readlines()
60+
install_requires = [line.strip() for line in lines]
61+
5162
setup(
5263
name='xdesign',
5364
packages=find_packages('src'),
@@ -56,7 +67,9 @@
5667
use_scm_version=True,
5768
author='Daniel Ching, Doga Gursoy',
5869
description='Benchmarking and optimization tools for tomography.',
70+
long_description=long_desc,
5971
include_package_data=True,
72+
install_requires=install_requires,
6073
url='http://github.com/tomography/xdesign.git',
6174
keywords=['xdesign', 'tomography'],
6275
license='BSD-3',

src/xdesign/acquisition.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def __repr__(self):
113113
)
114114

115115
def __str__(self):
116-
"""Return the string respresentation of the Beam."""
116+
"""Return the string representation of the Beam."""
117117
return "Probe(" + super(Probe, self).__str__() + ")"
118118

119119
def distance(self, other):

src/xdesign/geometry/area.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
'Curve',
88
'Circle',
99
'Polygon',
10+
'RegularPolygon',
1011
'Triangle',
1112
'Rectangle',
1213
'Square',
@@ -290,12 +291,18 @@ class Polygon(Entity):
290291
vertices : List of Points
291292
sign : int (-1 or 1)
292293
The sign of the area
294+
295+
Raises
296+
------
297+
ValueError : If the number of vertices is less than three.
293298
"""
294299

295300
def __init__(self, vertices, sign=1):
296301
for v in vertices:
297302
if not isinstance(v, Point):
298303
raise TypeError("vertices must be of type Point.")
304+
if len(vertices) < 3:
305+
raise ValueError("A Polygon has at least three vertices.")
299306
super(Polygon, self).__init__()
300307
self.vertices = vertices
301308
self._dim = vertices[0].dim
@@ -533,6 +540,42 @@ def contains(self, other):
533540
return np.logical_not(border.contains_points(np.atleast_2d(x)))
534541

535542

543+
class RegularPolygon(Polygon):
544+
"""A regular polygon in 2D cartesian space.
545+
546+
It is defined by the polynomial center, order, and radius.
547+
548+
By default (i.e. when the ``angle`` parameter is zero), the regular
549+
polygon is oriented so that one of the vertices is at coordinates
550+
:math:`(x + r, x)` where :math:`x` is the x-coordinate of
551+
``center`` and :math:`r` = ``radius``. The ``angle`` parameter is
552+
only meaningful modulo :math:`2\pi /` ``order`` since rotation by
553+
:math:`2\pi /` ``order`` gives a result equivalent to no rotation.
554+
555+
Parameters
556+
----------
557+
center : :class:`Point`
558+
The center of the polygon
559+
radius : float
560+
Distance from polygon center to vertices
561+
order : int
562+
Order of the polygon (e.g. order 6 is a hexagon).
563+
angle : float
564+
Optional rotation angle in radians.
565+
sign : int (-1 or 1)
566+
Optional sign of the area (see :class:`Polygon`)
567+
"""
568+
569+
def __init__(self, center, radius, order, angle=0, sign=1):
570+
vertex_angles = (np.linspace(0, 2 * np.pi, order, endpoint=False) +
571+
angle)
572+
vertices = [
573+
Point([radius * np.cos(theta), radius * np.sin(theta)]) + center
574+
for theta in vertex_angles
575+
]
576+
super(RegularPolygon, self).__init__(vertices, sign=sign)
577+
578+
536579
class Triangle(Polygon):
537580
"""Triangle in 2D cartesian space.
538581
@@ -566,7 +609,7 @@ class Rectangle(Polygon):
566609
567610
Defined by a point and a vector to enforce perpendicular sides.
568611
569-
Attributes
612+
Parameters
570613
----------
571614
side_lengths : array
572615
The lengths of the sides

src/xdesign/geometry/intersect.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ def halfspacecirc(d, r):
3737
f : scalar
3838
The proportion of the circle in the smaller half-space
3939
40-
Reference
41-
---------
40+
References
41+
----------
4242
Glassner, A. S. (Ed.). (2013). Graphics gems. Elsevier.
4343
4444
"""

src/xdesign/geometry/point.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def calc_standard(A):
7878
Return
7979
------
8080
c0 : :py:class:`np.array` (..., N)
81-
The first N coeffients for the hyper-plane
81+
The first N coefficients for the hyper-plane
8282
c1 : :py:class:`np.array` (..., 1)
8383
The last coefficient for the hyper-plane
8484
@@ -186,7 +186,7 @@ def rotate(self, theta, point=None, axis=None):
186186
self._x += center
187187

188188
def scale(self, vector):
189-
"""SScale the ambient space in each dimension according to vector.
189+
"""Scale the ambient space in each dimension according to vector.
190190
191191
Scaling is centered on the origin.
192192
"""

src/xdesign/metrics/standards.py

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,31 @@ def compute_mtf(phantom, image):
3333
HyperbolicCocentric phantom. Calculate the MTF from the modulation depth
3434
at each edge on the line from (0.5,0.5) to (0.5,1). MTF = (hi-lo)/(hi+lo)
3535
36+
.. deprecated:: 0.3
37+
38+
This method rapidly becomes inaccurate at small wavelenths because the
39+
measurement gets out of phase with the waves due to rounding error. Use
40+
another one of the MTF functions instead. This method will be removed
41+
in xdesign 0.6.
42+
43+
.. seealso::
44+
45+
:meth:`compute_mtf_ffst`
46+
:meth:`compute_mtf_lwkj`
47+
3648
Parameters
37-
---------------
49+
----------
3850
phantom : HyperbolicConcentric
3951
Predefined phantom of cocentric rings whose widths decay parabolically.
4052
image : ndarray
4153
The reconstruction of the above phantom.
4254
4355
Returns
44-
--------------
56+
-------
4557
wavelength : list
4658
wavelenth in the scale of the original phantom
4759
MTF : list
4860
MTF values
49-
50-
.. deprecated:: 0.3
51-
This method rapidly becomes inaccurate at small wavelenths because the
52-
measurement gets out of phase with the waves due to rounding error. Use
53-
another one of the MTF functions instead. This method will be removed
54-
in xdesign 0.6.
55-
56-
.. seealso::
57-
:meth:`compute_mtf_ffst`
58-
:meth:`compute_mtf_lwkj`
59-
6061
"""
6162
warnings.warn(
6263
'compute_mtf will be removed in xdesign 0.6, use compute_mtf_lwkj or '
@@ -94,6 +95,10 @@ def compute_mtf(phantom, image):
9495
def compute_mtf_ffst(phantom, image, Ntheta=4):
9596
'''Calculate the MTF using the method described in :cite:`Friedman:13`.
9697
98+
.. seealso::
99+
100+
:meth:`compute_mtf_lwkj`
101+
97102
Parameters
98103
----------
99104
phantom : :py:class:`.UnitCircle`
@@ -111,9 +116,6 @@ def compute_mtf_ffst(phantom, image, Ntheta=4):
111116
MTF values
112117
bin_centers : ndarray
113118
the center of the bins if Ntheta >= 1
114-
115-
.. seealso::
116-
:meth:`compute_mtf_lwkj`
117119
'''
118120
if not isinstance(phantom, UnitCircle):
119121
raise TypeError('MTF requires unit circle phantom.')
@@ -204,6 +206,10 @@ def compute_mtf_lwkj(phantom, image):
204206
"""Calculate the MTF using the modulated Siemens Star method in
205207
:cite:`loebich2007digital`.
206208
209+
.. seealso::
210+
211+
:meth:`compute_mtf_ffst`
212+
207213
Parameters
208214
----------
209215
phantom : :py:class:`.SiemensStar`
@@ -216,9 +222,6 @@ def compute_mtf_lwkj(phantom, image):
216222
The spatial frequency in cycles per unit length
217223
M : array
218224
The MTF values for each frequency
219-
220-
.. seealso::
221-
:meth:`compute_mtf_ffst`
222225
"""
223226
# Determine which radii to sample. Do not sample linearly because the
224227
# spatial frequency changes as 1/r
@@ -296,7 +299,7 @@ def fit_sinusoid(value, angle, f, p0=[0.5, 0.25, 0.25]):
296299
sinusoidal instead of square, contrast values larger than unity are clipped
297300
back to unity.
298301
299-
parameters
302+
Parameters
300303
----------
301304
value : NxM ndarray
302305
The value of the function at N angles and M radii
@@ -308,8 +311,8 @@ def fit_sinusoid(value, angle, f, p0=[0.5, 0.25, 0.25]):
308311
p0 : list, optional
309312
The initial guesses for the parameters.
310313
311-
returns:
312-
--------
314+
Returns
315+
-------
313316
MTFR: 1xM ndarray
314317
The modulation part of the MTF at each of the M radii
315318
"""
@@ -340,7 +343,7 @@ def errorfunc(p, x, y):
340343
def periodic_function(p, x):
341344
"""A periodic function for fitting to the spokes of the Siemens Star.
342345
343-
parameters
346+
Parameters
344347
----------
345348
p[0] : scalar
346349
the mean of the function
@@ -355,7 +358,7 @@ def periodic_function(p, x):
355358
theta : Nx1 ndarray
356359
input angles for the function
357360
358-
returns
361+
Returns
359362
-------
360363
value : Nx1 array
361364
the values of the function at phi; cannot return NaNs.
@@ -385,7 +388,7 @@ def compute_nps_ffst(phantom, A, B=None, plot_type='frequency'):
385388
'histogram' returns a plot binned by radial coordinate wavenumber
386389
'frequency' returns a wavenumber vs wavenumber plot
387390
388-
returns
391+
Returns
389392
-------
390393
bins :
391394
Bins for the radially binned NPS

tests/test_geometry/test_area.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,15 @@ def test_Polygon_contains():
196196
contains_no_overlap(A, Bn)
197197

198198

199+
def test_RegularPolygon_contains():
200+
r = 0.5
201+
order = 7
202+
A = Circle(Point([0, 0]), r)
203+
B = RegularPolygon(Point([0, 0]), r - 1e-7, order)
204+
A.contains(B)
205+
assert A.contains(B)
206+
207+
199208
def test_Mesh_contains():
200209
p0 = Point([0, 0])
201210
p1 = Point([0, 1])

0 commit comments

Comments
 (0)