Add general angle-to-parameter helper function

and improve accuracy of to_polygons
This commit is contained in:
jan 2018-09-02 21:05:18 -07:00
parent f3aa27a7c4
commit 832e3b46fa

View File

@ -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)