Add the option to use explicit x= or y= in path_to

This commit is contained in:
Jan Petykiewicz 2023-10-07 01:49:32 -07:00
parent f6bfd3b638
commit 13bb3e36c6
3 changed files with 67 additions and 22 deletions

View File

@ -236,8 +236,10 @@ class Pather(Builder):
self,
portspec: str,
ccw: SupportsBool | None,
position: float,
position: float | None = None,
*,
x: float | None = None,
y: float | None = None,
tool_port_names: tuple[str, str] = ('A', 'B'),
base_name: str = '_pathto',
**kwargs,
@ -246,8 +248,13 @@ class Pather(Builder):
logger.error('Skipping path_to() since device is dead')
return self
pos_count = sum(vv is not None for vv in (position, x, y))
if pos_count > 1:
raise BuildError('Only one of `position`, `x`, and `y` may be specified at once')
if pos_count < 1:
raise BuildError('One of `position`, `x`, and `y` must be specified')
port = self.pattern[portspec]
x, y = port.offset
if port.rotation is None:
raise PortError(f'Port {portspec} has no rotation and cannot be used for path_to()')
@ -256,13 +263,25 @@ class Pather(Builder):
is_horizontal = numpy.isclose(port.rotation % pi, 0)
if is_horizontal:
if numpy.sign(numpy.cos(port.rotation)) == numpy.sign(position - x):
raise BuildError(f'path_to routing to behind source port: x={x:g} to {position:g}')
length = numpy.abs(position - x)
if y is not None:
raise BuildError(f'Asked to path to y-coordinate, but port is horizontal')
if position is None:
position = x
else:
if numpy.sign(numpy.sin(port.rotation)) == numpy.sign(position - y):
raise BuildError(f'path_to routing to behind source port: y={y:g} to {position:g}')
length = numpy.abs(position - y)
if x is not None:
raise BuildError(f'Asked to path to x-coordinate, but port is vertical')
if position is None:
position = y
x0, y0 = port.offset
if is_horizontal:
if numpy.sign(numpy.cos(port.rotation)) == numpy.sign(position - x0):
raise BuildError(f'path_to routing to behind source port: x0={x0:g} to {position:g}')
length = numpy.abs(position - x0)
else:
if numpy.sign(numpy.sin(port.rotation)) == numpy.sign(position - y0):
raise BuildError(f'path_to routing to behind source port: y0={y0:g} to {position:g}')
length = numpy.abs(position - y0)
return self.path(portspec, ccw, length, tool_port_names=tool_port_names, base_name=base_name, **kwargs)
@ -286,7 +305,7 @@ class Pather(Builder):
if 'bound_type' in kwargs:
bound_types.add(kwargs['bound_type'])
bound = kwargs['bound']
for bt in ('emin', 'emax', 'pmin', 'pmax', 'min_past_furthest'):
for bt in ('emin', 'emax', 'pmin', 'pmax', 'xmin', 'xmax', 'ymin', 'ymax', 'min_past_furthest'):
if bt in kwargs:
bound_types.add(bt)
bound = kwargs[bt]

View File

@ -301,15 +301,23 @@ class RenderPather(PortList):
self,
portspec: str,
ccw: SupportsBool | None,
position: float,
position: float | None = None,
*,
x: float | None = None,
y: float | None = None,
**kwargs,
) -> Self:
if self._dead:
logger.error('Skipping path_to() since device is dead')
return self
pos_count = sum(vv is not None for vv in (position, x, y))
if pos_count > 1:
raise BuildError('Only one of `position`, `x`, and `y` may be specified at once')
if pos_count < 1:
raise BuildError('One of `position`, `x`, and `y` must be specified')
port = self.pattern[portspec]
x, y = port.offset
if port.rotation is None:
raise PortError(f'Port {portspec} has no rotation and cannot be used for path_to()')
@ -318,13 +326,25 @@ class RenderPather(PortList):
is_horizontal = numpy.isclose(port.rotation % pi, 0)
if is_horizontal:
if numpy.sign(numpy.cos(port.rotation)) == numpy.sign(position - x):
raise BuildError(f'path_to routing to behind source port: x={x:g} to {position:g}')
length = numpy.abs(position - x)
if y is not None:
raise BuildError(f'Asked to path to y-coordinate, but port is horizontal')
if position is None:
position = x
else:
if numpy.sign(numpy.sin(port.rotation)) == numpy.sign(position - y):
raise BuildError(f'path_to routing to behind source port: y={y:g} to {position:g}')
length = numpy.abs(position - y)
if x is not None:
raise BuildError(f'Asked to path to x-coordinate, but port is vertical')
if position is None:
position = y
x0, y0 = port.offset
if is_horizontal:
if numpy.sign(numpy.cos(port.rotation)) == numpy.sign(position - x0):
raise BuildError(f'path_to routing to behind source port: x0={x0:g} to {position:g}')
length = numpy.abs(position - x0)
else:
if numpy.sign(numpy.sin(port.rotation)) == numpy.sign(position - y0):
raise BuildError(f'path_to routing to behind source port: y0={y0:g} to {position:g}')
length = numpy.abs(position - y0)
return self.path(portspec, ccw, length, **kwargs)
@ -345,7 +365,7 @@ class RenderPather(PortList):
if 'bound_type' in kwargs:
bound_types.add(kwargs['bound_type'])
bound = kwargs['bound']
for bt in ('emin', 'emax', 'pmin', 'pmax', 'min_past_furthest'):
for bt in ('emin', 'emax', 'pmin', 'pmax', 'xmin', 'xmax', 'ymin', 'ymax', 'min_past_furthest'):
if bt in kwargs:
bound_types.add(bt)
bound = kwargs[bt]

View File

@ -53,9 +53,9 @@ def ell(
The distance between furthest out-port (B) and the innermost bend (D's bend).
- 'max_extension' or 'emax':
The total extension value for the closest-in port (C in the diagram).
- 'min_position' or 'pmin':
- 'min_position', 'pmin', 'xmin', 'ymin':
The coordinate of the innermost bend (D's bend).
- 'max_position' or 'pmax':
- 'max_position', 'pmax', 'xmax', 'ymax':
The coordinate of the outermost bend (A's bend).
`bound` can also be a vector. If specifying an extension (e.g. 'min_extension',
@ -109,6 +109,12 @@ def ell(
raise BuildError('set_rotation must be specified if no ports have rotations!')
rotations = numpy.full_like(has_rotation, set_rotation, dtype=float)
is_horizontal = numpy.isclose(rotations[0] % pi, 0)
if bound_type in ('ymin', 'ymax') and is_horizontal:
raise BuildError('Asked for {bound_type} position but ports are pointing along the x-axis!')
elif bound_type in ('xmin', 'xmax') and not is_horizontal:
raise BuildError('Asked for {bound_type} position but ports are pointing along the y-axis!')
direction = rotations[0] + pi # direction we want to travel in (+pi relative to port)
rot_matrix = rotation_matrix_2d(-direction)
@ -184,9 +190,9 @@ def ell(
rot_bound = -bound if neg else bound
min_possible = x_start + offsets
if bound_type in ('pmax', 'max_position'):
if bound_type in ('pmax', 'max_position', 'xmax', 'ymax'):
extension = rot_bound - min_possible.max()
elif bound_type in ('pmin', 'min_position'):
elif bound_type in ('pmin', 'min_position', 'xmin', 'ymin'):
extension = rot_bound - min_possible.min()
offsets += extension