Add general angle-to-parameter helper function
and improve accuracy of to_polygons
This commit is contained in:
parent
f3aa27a7c4
commit
832e3b46fa
@ -170,14 +170,12 @@ class Arc(Shape):
|
|||||||
r0, r1 = self.radii
|
r0, r1 = self.radii
|
||||||
|
|
||||||
# Convert from polar angle to ellipse parameter (for [rx*cos(t), ry*sin(t)] representation)
|
# Convert from polar angle to ellipse parameter (for [rx*cos(t), ry*sin(t)] representation)
|
||||||
a0, a1 = (numpy.arctan2(r0*numpy.sin(a), r1*numpy.cos(a)) for a in self.angles)
|
a_ranges = self._angles_to_parameters()
|
||||||
sign = numpy.sign(self.angles[1] - self.angles[0])
|
|
||||||
if sign != numpy.sign(a1 - a0):
|
|
||||||
a1 += sign * 2 * pi
|
|
||||||
|
|
||||||
# Approximate perimeter
|
# Approximate perimeter
|
||||||
# Ramanujan, S., "Modular Equations and Approximations to ,"
|
# Ramanujan, S., "Modular Equations and Approximations to ,"
|
||||||
# Quart. J. Pure. Appl. Math., vol. 45 (1913-1914), pp. 350-372
|
# Quart. J. Pure. Appl. Math., vol. 45 (1913-1914), pp. 350-372
|
||||||
|
a0, a1 = a_ranges[1] # use outer arc
|
||||||
h = ((r1 - r0) / (r1 + r0)) ** 2
|
h = ((r1 - r0) / (r1 + r0)) ** 2
|
||||||
ellipse_perimeter = pi * (r1 + r0) * (1 + 3 * h / (10 + math.sqrt(4 - 3 * h)))
|
ellipse_perimeter = pi * (r1 + r0) * (1 + 3 * h / (10 + math.sqrt(4 - 3 * h)))
|
||||||
perimeter = abs(a0 - a1) / (2 * pi) * ellipse_perimeter # TODO: make this more accurate
|
perimeter = abs(a0 - a1) / (2 * pi) * ellipse_perimeter # TODO: make this more accurate
|
||||||
@ -187,18 +185,20 @@ class Arc(Shape):
|
|||||||
n += [poly_num_points]
|
n += [poly_num_points]
|
||||||
if poly_max_arclen is not None:
|
if poly_max_arclen is not None:
|
||||||
n += [perimeter / poly_max_arclen]
|
n += [perimeter / poly_max_arclen]
|
||||||
thetas = numpy.linspace(a1, a0, max(n), endpoint=True)
|
thetas_inner = numpy.linspace(a_ranges[0][1], a_ranges[0][0], max(n), endpoint=True)
|
||||||
|
thetas_outer = numpy.linspace(a_ranges[1][0], a_ranges[1][1], max(n), endpoint=True)
|
||||||
|
|
||||||
sin_th, cos_th = (numpy.sin(thetas), numpy.cos(thetas))
|
sin_th_i, cos_th_i = (numpy.sin(thetas_inner), numpy.cos(thetas_inner))
|
||||||
|
sin_th_o, cos_th_o = (numpy.sin(thetas_outer), numpy.cos(thetas_outer))
|
||||||
wh = self.width / 2.0
|
wh = self.width / 2.0
|
||||||
|
|
||||||
xs1 = (r0 + wh) * cos_th
|
xs1 = (r0 + wh) * cos_th_o
|
||||||
ys1 = (r1 + wh) * sin_th
|
ys1 = (r1 + wh) * sin_th_o
|
||||||
xs2 = (r0 - wh) * cos_th
|
xs2 = (r0 - wh) * cos_th_i
|
||||||
ys2 = (r1 - wh) * sin_th
|
ys2 = (r1 - wh) * sin_th_i
|
||||||
|
|
||||||
xs = numpy.hstack((xs1, xs2[::-1]))
|
xs = numpy.hstack((xs1, xs2))
|
||||||
ys = numpy.hstack((ys1, ys2[::-1]))
|
ys = numpy.hstack((ys1, ys2))
|
||||||
xys = numpy.vstack((xs, ys)).T
|
xys = numpy.vstack((xs, ys)).T
|
||||||
|
|
||||||
poly = Polygon(xys, dose=self.dose, layer=self.layer, offset=self.offset)
|
poly = Polygon(xys, dose=self.dose, layer=self.layer, offset=self.offset)
|
||||||
@ -218,25 +218,20 @@ class Arc(Shape):
|
|||||||
|
|
||||||
If the extrema are innaccessible due to arc constraints, check the arc endpoints instead.
|
If the extrema are innaccessible due to arc constraints, check the arc endpoints instead.
|
||||||
'''
|
'''
|
||||||
|
a_ranges = self._angles_to_parameters()
|
||||||
|
|
||||||
mins = []
|
mins = []
|
||||||
maxs = []
|
maxs = []
|
||||||
for sgn in (+1, -1):
|
for a, sgn in zip(a_ranges, (-1, +1)):
|
||||||
wh = sgn * self.width/2
|
wh = sgn * self.width/2
|
||||||
rx = self.radius_x + wh
|
rx = self.radius_x + wh
|
||||||
ry = self.radius_y + wh
|
ry = self.radius_y + wh
|
||||||
|
|
||||||
# Create paremeter 'a' for parametrized ellipse
|
a0, a1 = a
|
||||||
a0, a1 = (numpy.arctan2(rx*numpy.sin(a), ry*numpy.cos(a)) for a in self.angles)
|
|
||||||
sign = numpy.sign(self.angles[1] - self.angles[0])
|
|
||||||
if sign != numpy.sign(a1 - a0):
|
|
||||||
a1 += sign * 2 * pi
|
|
||||||
|
|
||||||
a = numpy.array((a0, a1))
|
|
||||||
a0_offset = a0 - (a0 % (2 * pi))
|
a0_offset = a0 - (a0 % (2 * pi))
|
||||||
|
|
||||||
sin_r = numpy.sin(self.rotation)
|
sin_r = numpy.sin(self.rotation)
|
||||||
cos_r = numpy.cos(self.rotation)
|
cos_r = numpy.cos(self.rotation)
|
||||||
tan_r = numpy.tan(self.rotation)
|
|
||||||
sin_a = numpy.sin(a)
|
sin_a = numpy.sin(a)
|
||||||
cos_a = numpy.cos(a)
|
cos_a = numpy.cos(a)
|
||||||
|
|
||||||
@ -316,3 +311,23 @@ class Arc(Shape):
|
|||||||
return (type(self), radii, angles, width, self.layer), \
|
return (type(self), radii, angles, width, self.layer), \
|
||||||
(self.offset, scale/norm_value, rotation, self.dose), \
|
(self.offset, scale/norm_value, rotation, self.dose), \
|
||||||
lambda: Arc(radii=radii*norm_value, angles=angles, width=width, layer=self.layer)
|
lambda: Arc(radii=radii*norm_value, angles=angles, width=width, layer=self.layer)
|
||||||
|
|
||||||
|
def _angles_to_parameters(self) -> numpy.ndarray:
|
||||||
|
'''
|
||||||
|
:return: "Eccentric anomaly" parameter ranges for the inner and outer edges, in the form
|
||||||
|
[[a_min_inner, a_max_inner], [a_min_outer, a_max_outer]]
|
||||||
|
'''
|
||||||
|
a = []
|
||||||
|
for sgn in (-1, +1):
|
||||||
|
wh = sgn * self.width/2
|
||||||
|
rx = self.radius_x + wh
|
||||||
|
ry = self.radius_y + wh
|
||||||
|
|
||||||
|
# create paremeter 'a' for parametrized ellipse
|
||||||
|
a0, a1 = (numpy.arctan2(rx*numpy.sin(a), ry*numpy.cos(a)) for a in self.angles)
|
||||||
|
sign = numpy.sign(self.angles[1] - self.angles[0])
|
||||||
|
if sign != numpy.sign(a1 - a0):
|
||||||
|
a1 += sign * 2 * pi
|
||||||
|
|
||||||
|
a.append((a0, a1))
|
||||||
|
return numpy.array(a)
|
||||||
|
Loading…
Reference in New Issue
Block a user