diff --git a/index.html b/index.html index 80f0a009..c495f39f 100644 --- a/index.html +++ b/index.html @@ -8,4 +8,4 @@ - \ No newline at end of file + diff --git a/main/.buildinfo b/main/.buildinfo index e62610fd..3fd2545e 100644 --- a/main/.buildinfo +++ b/main/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: dc202c585b0d864e5ec07d24a66bfe52 +config: 54759b71d0c40bc7ec5736f2b3298a4f tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/main/.doctrees/_api.doctree b/main/.doctrees/_api.doctree new file mode 100644 index 00000000..c0283d08 Binary files /dev/null and b/main/.doctrees/_api.doctree differ diff --git a/main/.doctrees/_api/scanspec.core.doctree b/main/.doctrees/_api/scanspec.core.doctree new file mode 100644 index 00000000..16f7ff40 Binary files /dev/null and b/main/.doctrees/_api/scanspec.core.doctree differ diff --git a/main/.doctrees/_api/scanspec.doctree b/main/.doctrees/_api/scanspec.doctree new file mode 100644 index 00000000..79a89a76 Binary files /dev/null and b/main/.doctrees/_api/scanspec.doctree differ diff --git a/main/.doctrees/_api/scanspec.plot.doctree b/main/.doctrees/_api/scanspec.plot.doctree new file mode 100644 index 00000000..533c7f31 Binary files /dev/null and b/main/.doctrees/_api/scanspec.plot.doctree differ diff --git a/main/.doctrees/_api/scanspec.regions.doctree b/main/.doctrees/_api/scanspec.regions.doctree new file mode 100644 index 00000000..6c96165e Binary files /dev/null and b/main/.doctrees/_api/scanspec.regions.doctree differ diff --git a/main/.doctrees/_api/scanspec.specs.doctree b/main/.doctrees/_api/scanspec.specs.doctree new file mode 100644 index 00000000..7a21b5a9 Binary files /dev/null and b/main/.doctrees/_api/scanspec.specs.doctree differ diff --git a/main/.doctrees/explanations/why-squash-can-change-path.doctree b/main/.doctrees/explanations/why-squash-can-change-path.doctree index 6da6f0dc..4b0ee8d6 100644 Binary files a/main/.doctrees/explanations/why-squash-can-change-path.doctree and b/main/.doctrees/explanations/why-squash-can-change-path.doctree differ diff --git a/main/.doctrees/explanations/why-stack-frames.doctree b/main/.doctrees/explanations/why-stack-frames.doctree index 5a80bd99..9f2f1986 100644 Binary files a/main/.doctrees/explanations/why-stack-frames.doctree and b/main/.doctrees/explanations/why-stack-frames.doctree differ diff --git a/main/.doctrees/how-to/contribute.doctree b/main/.doctrees/how-to/contribute.doctree index b8e2d7d1..92a08d4c 100644 Binary files a/main/.doctrees/how-to/contribute.doctree and b/main/.doctrees/how-to/contribute.doctree differ diff --git a/main/.doctrees/how-to/run-container.doctree b/main/.doctrees/how-to/run-container.doctree index 9c8d396e..9b6974c6 100644 Binary files a/main/.doctrees/how-to/run-container.doctree and b/main/.doctrees/how-to/run-container.doctree differ diff --git a/main/.doctrees/index.doctree b/main/.doctrees/index.doctree index c85b3b15..561bf7f3 100644 Binary files a/main/.doctrees/index.doctree and b/main/.doctrees/index.doctree differ diff --git a/main/.doctrees/reference.doctree b/main/.doctrees/reference.doctree index 136fe6ea..7dfeadbd 100644 Binary files a/main/.doctrees/reference.doctree and b/main/.doctrees/reference.doctree differ diff --git a/main/.doctrees/reference/rest_api.doctree b/main/.doctrees/reference/rest_api.doctree index f7b6ced0..88d6613e 100644 Binary files a/main/.doctrees/reference/rest_api.doctree and b/main/.doctrees/reference/rest_api.doctree differ diff --git a/main/.doctrees/tutorials/creating-a-spec.doctree b/main/.doctrees/tutorials/creating-a-spec.doctree index 2d5393df..87bfe904 100644 Binary files a/main/.doctrees/tutorials/creating-a-spec.doctree and b/main/.doctrees/tutorials/creating-a-spec.doctree differ diff --git a/main/.doctrees/tutorials/installation.doctree b/main/.doctrees/tutorials/installation.doctree index 8c61a3e0..aa523e68 100644 Binary files a/main/.doctrees/tutorials/installation.doctree and b/main/.doctrees/tutorials/installation.doctree differ diff --git a/main/_api.html b/main/_api.html new file mode 100644 index 00000000..571af8e3 --- /dev/null +++ b/main/_api.html @@ -0,0 +1,566 @@ + + + + + + + + + + + API — scanspec 0.7.3.dev9+ge42f8eba documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

API#

+
+ + + + + +

scanspec

Top level API.

+
+
+ + +
+ + + + + +
+ +
+
+
+ +
+ + + +
+ + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/main/_api/scanspec.core.html b/main/_api/scanspec.core.html new file mode 100644 index 00000000..2320aaf2 --- /dev/null +++ b/main/_api/scanspec.core.html @@ -0,0 +1,1084 @@ + + + + + + + + + + + scanspec.core — scanspec 0.7.3.dev9+ge42f8eba documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

scanspec.core#

+

Core classes like Frames and Path.

+

Members

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

if_instance_do

If x is of type cls then return func(x), otherwise return NotImplemented.

Axis

A type variable for an Axis that can be specified for a scan

AxesPoints

Map of axes to float ndarray of points E.g.

Frames

Represents a series of scan frames along a number of axes.

SnakedFrames

Like a Frames object, but each alternate repetition will run in reverse.

gap_between_frames

Is there a gap between end of frames1 and start of frames2.

squash_frames

Squash a stack of nested Frames into a single one.

Path

A consumable route through a stack of Frames, representing a scan path.

Midpoints

Convenience iterable that produces the scan midpoints for each axis.

discriminated_union_of_subclasses

Add all subclasses of super_cls to a discriminated union.

StrictConfig

Used to ensure pydantic dataclasses error if given extra arguments

+
+
+
+scanspec.core.if_instance_do(x: Any, cls: type, func: Callable)[source]#
+

If x is of type cls then return func(x), otherwise return NotImplemented.

+

Used as a helper when implementing operator overloading.

+
+ +
+
+class scanspec.core.Axis#
+

A type variable for an Axis that can be specified for a scan

+

alias of TypeVar(‘Axis’)

+
+ +
+
+scanspec.core.AxesPoints#
+

Map of axes to float ndarray of points +E.g. {xmotor: array([0, 1, 2]), ymotor: array([2, 2, 2])}

+

alias of dict[Axis, ndarray]

+
+ +
+
+class scanspec.core.Frames(midpoints: dict[Axis, ndarray], lower: dict[Axis, ndarray] | None = None, upper: dict[Axis, ndarray] | None = None, gap: ndarray | None = None)[source]#
+

Represents a series of scan frames along a number of axes.

+

During a scan each axis will traverse lower-midpoint-upper for each frame.

+
+
Parameters:
+
    +
  • midpoints – The midpoints of scan frames for each axis

  • +
  • lower – Lower bounds of scan frames if different from midpoints

  • +
  • upper – Upper bounds of scan frames if different from midpoints

  • +
  • gap – If supplied, define if there is a gap between frame and previous +otherwise it is calculated by looking at lower and upper bounds

  • +
+
+
+

Typically used in two ways:

+
    +
  • A list of Frames objects returned from Spec.calculate represents a scan +as a linear stack of frames. Interpreted as nested from slowest moving to +fastest moving, so each faster Frames object will iterate once per +position of the slower Frames object. It is passed to a Path for +calculation of the actual scan path.

  • +
  • A single Frames object returned from Path.consume represents a chunk of +frames forming part of a scan path, for interpretation by the code +that will actually perform the scan.

  • +
+
+

See also

+

Technical Terms

+
+
+
+midpoints#
+

The midpoints of scan frames for each axis

+
+ +
+
+lower#
+

The lower bounds of each scan frame in each axis for fly-scanning

+
+ +
+
+upper#
+

The upper bounds of each scan frame in each axis for fly-scanning

+
+ +
+
+gap#
+

Whether there is a gap between this frame and the previous. First +element is whether there is a gap between the last frame and the first

+
+ +
+
+axes() list[Axis][source]#
+

The axes which will move during the scan.

+

These will be present in midpoints, lower and upper.

+
+ +
+
+extract(indices: ndarray, calculate_gap=True) Frames[Axis][source]#
+

Return a new Frames object restricted to the indices provided.

+
+
Parameters:
+
    +
  • indices – The indices of the frames to extract, modulo scan length

  • +
  • calculate_gap – If True then recalculate the gap from upper and lower

  • +
+
+
+
>>> frames = Frames({"x": np.array([1, 2, 3])})
+>>> frames.extract(np.array([1, 0, 1])).midpoints
+{'x': array([2, 1, 2])}
+
+
+
+ +
+
+concat(other: Frames[Axis], gap: bool = False) Frames[Axis][source]#
+

Return a new Frames object concatenating self and other.

+

Requires both Frames objects to have the same axes, but not necessarily in +the same order. The order is inherited from self, so other may be reordered.

+
+
Parameters:
+
    +
  • other – The Frames to concatenate to self

  • +
  • gap – Whether to force a gap between the two Frames objects

  • +
+
+
+
>>> frames = Frames({"x": np.array([1, 2, 3]), "y": np.array([6, 5, 4])})
+>>> frames2 = Frames({"y": np.array([3, 2, 1]), "x": np.array([4, 5, 6])})
+>>> frames.concat(frames2).midpoints
+{'x': array([1, 2, 3, 4, 5, 6]), 'y': array([6, 5, 4, 3, 2, 1])}
+
+
+
+ +
+
+zip(other: Frames[Axis]) Frames[Axis][source]#
+

Return a new Frames object merging self and other.

+

Require both Frames objects to not share axes.

+
>>> fx = Frames({"x": np.array([1, 2, 3])})
+>>> fy = Frames({"y": np.array([5, 6, 7])})
+>>> fx.zip(fy).midpoints
+{'x': array([1, 2, 3]), 'y': array([5, 6, 7])}
+
+
+
+ +
+ +
+
+class scanspec.core.SnakedFrames(midpoints: dict[Axis, ndarray], lower: dict[Axis, ndarray] | None = None, upper: dict[Axis, ndarray] | None = None, gap: ndarray | None = None)[source]#
+

Like a Frames object, but each alternate repetition will run in reverse.

+
+
+classmethod from_frames(frames: Frames[Axis]) SnakedFrames[Axis][source]#
+

Create a snaked version of a Frames object.

+
+ +
+
+extract(indices: ndarray, calculate_gap=True) Frames[Axis][source]#
+

Return a new Frames object restricted to the indices provided.

+
+
Parameters:
+
    +
  • indices – The indices of the frames to extract, can extend past len(self)

  • +
  • calculate_gap – If True then recalculate the gap from upper and lower

  • +
+
+
+
>>> frames = SnakedFrames({"x": np.array([1, 2, 3])})
+>>> frames.extract(np.array([0, 1, 2, 3, 4, 5])).midpoints
+{'x': array([1, 2, 3, 3, 2, 1])}
+
+
+
+ +
+ +
+
+scanspec.core.gap_between_frames(frames1: Frames[Axis], frames2: Frames[Axis]) bool[source]#
+

Is there a gap between end of frames1 and start of frames2.

+
+ +
+
+scanspec.core.squash_frames(stack: list[Frames[Axis]], check_path_changes=True) Frames[Axis][source]#
+

Squash a stack of nested Frames into a single one.

+
+
Parameters:
+
    +
  • stack – The Frames stack to squash, from slowest to fastest moving

  • +
  • check_path_changes – If True then check that nesting the output +Frames object within others will provide the same path +as nesting the input Frames stack within others

  • +
+
+
+ +
>>> fx = SnakedFrames({"x": np.array([1, 2])})
+>>> fy = Frames({"y": np.array([3, 4])})
+>>> squash_frames([fy, fx]).midpoints
+{'y': array([3, 3, 4, 4]), 'x': array([1, 2, 2, 1])}
+
+
+
+ +
+
+class scanspec.core.Path(stack: list[Frames[Axis]], start: int = 0, num: int | None = None)[source]#
+

A consumable route through a stack of Frames, representing a scan path.

+
+
Parameters:
+
    +
  • stack – The Frames stack describing the scan, from slowest to fastest +moving

  • +
  • start – The index of where in the Path to start

  • +
  • num – The number of scan frames to produce after start. None means up to +the end

  • +
+
+
+
+

See also

+

How to Iterate a Spec

+
+
+
+stack#
+

The Frames stack describing the scan, from slowest to fastest moving

+
+ +
+
+index#
+

Index that is next to be consumed

+
+ +
+
+lengths#
+

The lengths of all the stack

+
+ +
+
+end_index#
+

Index of the end frame, one more than the last index that will be +produced

+
+ +
+
+consume(num: int | None = None) Frames[Axis][source]#
+

Consume at most num frames from the Path and return as a Frames object.

+
>>> fx = SnakedFrames({"x": np.array([1, 2])})
+>>> fy = Frames({"y": np.array([3, 4])})
+>>> path = Path([fy, fx])
+>>> path.consume(3).midpoints
+{'y': array([3, 3, 4]), 'x': array([1, 2, 2])}
+>>> path.consume(3).midpoints
+{'y': array([4]), 'x': array([1])}
+>>> path.consume(3).midpoints
+{'y': array([], dtype=int64), 'x': array([], dtype=int64)}
+
+
+
+ +
+ +
+
+class scanspec.core.Midpoints(stack: list[Frames[Axis]])[source]#
+

Convenience iterable that produces the scan midpoints for each axis.

+

For better performance, consume from a Path instead.

+
+
Parameters:
+

stack – The stack of Frames describing the scan, from slowest to fastest +moving

+
+
+
+

See also

+

How to Iterate a Spec

+
+
>>> fx = SnakedFrames({"x": np.array([1, 2])})
+>>> fy = Frames({"y": np.array([3, 4])})
+>>> mp = Midpoints([fy, fx])
+>>> for p in mp: print(p)
+{'y': np.int64(3), 'x': np.int64(1)}
+{'y': np.int64(3), 'x': np.int64(2)}
+{'y': np.int64(4), 'x': np.int64(2)}
+{'y': np.int64(4), 'x': np.int64(1)}
+
+
+
+
+stack#
+

The stack of Frames describing the scan, from slowest to fastest moving

+
+ +
+
+property axes: list[Axis]#
+

The axes that will be present in each points dictionary.

+
+ +
+ +
+
+scanspec.core.discriminated_union_of_subclasses(super_cls: type[C], discriminator: str = 'type') type[C][source]#
+

Add all subclasses of super_cls to a discriminated union.

+

For all subclasses of super_cls, add a discriminator field to identify +the type. Raw JSON should look like {<discriminator>: <type name>, params for +<type name>…}.

+

Subclasses that extend this class must be Pydantic dataclasses, and types that +need their schema to be updated when a new type that extends super_cls is +created must be either Pydantic dataclasses or BaseModels.

+

Example:

+
@discriminated_union_of_subclasses
+class Expression(ABC):
+    @abstractmethod
+    def calculate(self) -> int:
+        ...
+
+
+@dataclass
+class Add(Expression):
+    left: Expression
+    right: Expression
+
+    def calculate(self) -> int:
+        return self.left.calculate() + self.right.calculate()
+
+
+@dataclass
+class Subtract(Expression):
+    left: Expression
+    right: Expression
+
+    def calculate(self) -> int:
+        return self.left.calculate() - self.right.calculate()
+
+
+@dataclass
+class IntLiteral(Expression):
+    value: int
+
+    def calculate(self) -> int:
+        return self.value
+
+
+my_sum = Add(IntLiteral(5), Subtract(IntLiteral(10), IntLiteral(2)))
+assert my_sum.calculate() == 13
+
+assert my_sum == parse_obj_as(
+    Expression,
+    {
+        "type": "Add",
+        "left": {"type": "IntLiteral", "value": 5},
+        "right": {
+            "type": "Subtract",
+            "left": {"type": "IntLiteral", "value": 10},
+            "right": {"type": "IntLiteral", "value": 2},
+        },
+    },
+)
+
+
+
+
Parameters:
+
    +
  • super_cls – The superclass of the union, Expression in the above example

  • +
  • discriminator – The discriminator that will be inserted into the +serialized documents for type determination. Defaults to “type”.

  • +
+
+
Returns:
+

+
decorated superclass with handling for subclasses to be added

to its discriminated union for deserialization

+
+
+

+
+
Return type:
+

Type

+
+
+
+ +
+
+scanspec.core.StrictConfig: ConfigDict = {'extra': 'forbid'}#
+

Used to ensure pydantic dataclasses error if given extra arguments

+
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/main/_api/scanspec.html b/main/_api/scanspec.html new file mode 100644 index 00000000..3e96cbc5 --- /dev/null +++ b/main/_api/scanspec.html @@ -0,0 +1,637 @@ + + + + + + + + + + + scanspec — scanspec 0.7.3.dev9+ge42f8eba documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + + + + + + + + + +
+ + + +
+ + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/main/_api/scanspec.plot.html b/main/_api/scanspec.plot.html new file mode 100644 index 00000000..43fb42a1 --- /dev/null +++ b/main/_api/scanspec.plot.html @@ -0,0 +1,649 @@ + + + + + + + + + + + scanspec.plot — scanspec 0.7.3.dev9+ge42f8eba documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

scanspec.plot#

+

plot_spec to visualize a scan.

+

Members

+
+ + + + + +

plot_spec

Plot a spec, drawing the path taken through the scan.

+
+
+
+scanspec.plot.plot_spec(spec: Spec[Any], title: str | None = None)[source]#
+

Plot a spec, drawing the path taken through the scan.

+

Uses a different colour for each frame, grey for the turnarounds, and +marks the midpoints with a filled circle if there are less than 200 of +them. If the scan is 2D then 2D regions are shown in black.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line
+from scanspec.regions import Circle
+
+cube = Line("z", 1, 3, 3) * Line("y", 1, 3, 10) * ~Line("x", 0, 2, 10)
+spec = cube & Circle("x", "y", 1, 2, 0.9)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-plot-1.png +
+
+ +
+ + +
+ + + + + + + +
+ + + +
+ + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/main/_api/scanspec.regions.html b/main/_api/scanspec.regions.html new file mode 100644 index 00000000..1bee27e5 --- /dev/null +++ b/main/_api/scanspec.regions.html @@ -0,0 +1,3972 @@ + + + + + + + + + + + scanspec.regions — scanspec 0.7.3.dev9+ge42f8eba documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

scanspec.regions#

+

Region and its subclasses.

+
+

Inheritance diagram of scanspec.regions

+

Members

+
+ + + + + + + + + + + +

Region

Abstract baseclass for a Region that can Mask a Spec.

get_mask

Return a mask of the points inside the region.

find_regions

Recursively yield Regions from obj and its children.

+
+
+
+class scanspec.regions.Region[source]#
+

Abstract baseclass for a Region that can Mask a Spec.

+

Supports operators:

+ +
+
+axis_sets() list[set[Axis]][source]#
+

Produce the non-overlapping sets of axes this region spans.

+
+ +
+
+mask(points: dict[Axis, ndarray]) ndarray[source]#
+

Produce a mask of which points are in the region.

+
+ +
+
+serialize() Mapping[str, Any][source]#
+

Serialize the Region to a dictionary.

+
+ +
+
+static deserialize(obj) Region[source]#
+

Deserialize a Region from a dictionary.

+
+ +
+ +
+
+scanspec.regions.get_mask(region: Region[Axis], points: dict[Axis, ndarray]) ndarray[source]#
+

Return a mask of the points inside the region.

+

If there is an overlap of axes of region and points return a +mask of the points in the region, otherwise return all ones

+
+ +
+
+pydantic model scanspec.regions.CombinationOf[source]#
+

Abstract baseclass for a combination of two regions, left and right.

+

+Show JSON schema
{
+   "$defs": {
+      "Circle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the circle",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the circle",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "radius": {
+               "description": "Radius of the circle",
+               "exclusiveMinimum": 0.0,
+               "title": "Radius",
+               "type": "number"
+            },
+            "type": {
+               "const": "Circle",
+               "default": "Circle",
+               "enum": [
+                  "Circle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "radius"
+         ],
+         "title": "Circle",
+         "type": "object"
+      },
+      "CombinationOf": {
+         "additionalProperties": false,
+         "description": "Abstract baseclass for a combination of two regions, left and right.",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "CombinationOf",
+               "default": "CombinationOf",
+               "enum": [
+                  "CombinationOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "CombinationOf",
+         "type": "object"
+      },
+      "DifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "DifferenceOf",
+               "default": "DifferenceOf",
+               "enum": [
+                  "DifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "DifferenceOf",
+         "type": "object"
+      },
+      "Ellipse": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the ellipse",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the ellipse",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "x_radius": {
+               "description": "The radius along the x axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "X Radius",
+               "type": "number"
+            },
+            "y_radius": {
+               "description": "The radius along the y axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "Y Radius",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "The angle of the ellipse (degrees)",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Ellipse",
+               "default": "Ellipse",
+               "enum": [
+                  "Ellipse"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "x_radius",
+            "y_radius"
+         ],
+         "title": "Ellipse",
+         "type": "object"
+      },
+      "IntersectionOf": {
+         "additionalProperties": false,
+         "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False,  True, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "IntersectionOf",
+               "default": "IntersectionOf",
+               "enum": [
+                  "IntersectionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "IntersectionOf",
+         "type": "object"
+      },
+      "Polygon": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_verts": {
+               "description": "The Nx1 x coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "X Verts",
+               "type": "array"
+            },
+            "y_verts": {
+               "description": "The Nx1 y coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "Y Verts",
+               "type": "array"
+            },
+            "type": {
+               "const": "Polygon",
+               "default": "Polygon",
+               "enum": [
+                  "Polygon"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_verts",
+            "y_verts"
+         ],
+         "title": "Polygon",
+         "type": "object"
+      },
+      "Range": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+         "properties": {
+            "axis": {
+               "description": "The name matching the axis to mask in spec",
+               "title": "Axis"
+            },
+            "min": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Min",
+               "type": "number"
+            },
+            "max": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Max",
+               "type": "number"
+            },
+            "type": {
+               "const": "Range",
+               "default": "Range",
+               "enum": [
+                  "Range"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "min",
+            "max"
+         ],
+         "title": "Range",
+         "type": "object"
+      },
+      "Rectangle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_min": {
+               "description": "Minimum inclusive x value in the region",
+               "title": "X Min",
+               "type": "number"
+            },
+            "y_min": {
+               "description": "Minimum inclusive y value in the region",
+               "title": "Y Min",
+               "type": "number"
+            },
+            "x_max": {
+               "description": "Maximum inclusive x value in the region",
+               "title": "X Max",
+               "type": "number"
+            },
+            "y_max": {
+               "description": "Maximum inclusive y value in the region",
+               "title": "Y Max",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "Clockwise rotation angle of the rectangle",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Rectangle",
+               "default": "Rectangle",
+               "enum": [
+                  "Rectangle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_min",
+            "y_min",
+            "x_max",
+            "y_max"
+         ],
+         "title": "Rectangle",
+         "type": "object"
+      },
+      "Region": {
+         "discriminator": {
+            "mapping": {
+               "Circle": "#/$defs/Circle",
+               "CombinationOf": "#/$defs/CombinationOf",
+               "DifferenceOf": "#/$defs/DifferenceOf",
+               "Ellipse": "#/$defs/Ellipse",
+               "IntersectionOf": "#/$defs/IntersectionOf",
+               "Polygon": "#/$defs/Polygon",
+               "Range": "#/$defs/Range",
+               "Rectangle": "#/$defs/Rectangle",
+               "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf",
+               "UnionOf": "#/$defs/UnionOf"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/CombinationOf"
+            },
+            {
+               "$ref": "#/$defs/UnionOf"
+            },
+            {
+               "$ref": "#/$defs/IntersectionOf"
+            },
+            {
+               "$ref": "#/$defs/DifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/SymmetricDifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/Range"
+            },
+            {
+               "$ref": "#/$defs/Rectangle"
+            },
+            {
+               "$ref": "#/$defs/Polygon"
+            },
+            {
+               "$ref": "#/$defs/Circle"
+            },
+            {
+               "$ref": "#/$defs/Ellipse"
+            }
+         ]
+      },
+      "SymmetricDifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "SymmetricDifferenceOf",
+               "default": "SymmetricDifferenceOf",
+               "enum": [
+                  "SymmetricDifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "SymmetricDifferenceOf",
+         "type": "object"
+      },
+      "UnionOf": {
+         "additionalProperties": false,
+         "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "UnionOf",
+               "default": "UnionOf",
+               "enum": [
+                  "UnionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "UnionOf",
+         "type": "object"
+      }
+   },
+   "$ref": "#/$defs/CombinationOf"
+}
+
+
+

+
Fields:
+
+
+
+
+
+field left: Region[Axis] [Required]#
+

The left-hand Region to combine

+
+ +
+
+field right: Region[Axis] [Required]#
+

The right-hand Region to combine

+
+ +
+
+field type: Literal['CombinationOf'] = 'CombinationOf'#
+
+ +
+
+axis_sets() list[set[Axis]][source]#
+

Produce the non-overlapping sets of axes this region spans.

+
+ +
+ +
+
+pydantic model scanspec.regions.UnionOf[source]#
+

A point is in UnionOf(a, b) if in either a or b.

+

Typically created with the | operator

+
>>> r = Range("x", 0.5, 2.5) | Range("x", 1.5, 3.5)
+>>> r.mask({"x": np.array([0, 1, 2, 3, 4])})
+array([False,  True,  True,  True, False])
+
+
+

+Show JSON schema
{
+   "$defs": {
+      "Circle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the circle",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the circle",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "radius": {
+               "description": "Radius of the circle",
+               "exclusiveMinimum": 0.0,
+               "title": "Radius",
+               "type": "number"
+            },
+            "type": {
+               "const": "Circle",
+               "default": "Circle",
+               "enum": [
+                  "Circle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "radius"
+         ],
+         "title": "Circle",
+         "type": "object"
+      },
+      "CombinationOf": {
+         "additionalProperties": false,
+         "description": "Abstract baseclass for a combination of two regions, left and right.",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "CombinationOf",
+               "default": "CombinationOf",
+               "enum": [
+                  "CombinationOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "CombinationOf",
+         "type": "object"
+      },
+      "DifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "DifferenceOf",
+               "default": "DifferenceOf",
+               "enum": [
+                  "DifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "DifferenceOf",
+         "type": "object"
+      },
+      "Ellipse": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the ellipse",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the ellipse",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "x_radius": {
+               "description": "The radius along the x axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "X Radius",
+               "type": "number"
+            },
+            "y_radius": {
+               "description": "The radius along the y axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "Y Radius",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "The angle of the ellipse (degrees)",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Ellipse",
+               "default": "Ellipse",
+               "enum": [
+                  "Ellipse"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "x_radius",
+            "y_radius"
+         ],
+         "title": "Ellipse",
+         "type": "object"
+      },
+      "IntersectionOf": {
+         "additionalProperties": false,
+         "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False,  True, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "IntersectionOf",
+               "default": "IntersectionOf",
+               "enum": [
+                  "IntersectionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "IntersectionOf",
+         "type": "object"
+      },
+      "Polygon": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_verts": {
+               "description": "The Nx1 x coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "X Verts",
+               "type": "array"
+            },
+            "y_verts": {
+               "description": "The Nx1 y coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "Y Verts",
+               "type": "array"
+            },
+            "type": {
+               "const": "Polygon",
+               "default": "Polygon",
+               "enum": [
+                  "Polygon"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_verts",
+            "y_verts"
+         ],
+         "title": "Polygon",
+         "type": "object"
+      },
+      "Range": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+         "properties": {
+            "axis": {
+               "description": "The name matching the axis to mask in spec",
+               "title": "Axis"
+            },
+            "min": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Min",
+               "type": "number"
+            },
+            "max": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Max",
+               "type": "number"
+            },
+            "type": {
+               "const": "Range",
+               "default": "Range",
+               "enum": [
+                  "Range"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "min",
+            "max"
+         ],
+         "title": "Range",
+         "type": "object"
+      },
+      "Rectangle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_min": {
+               "description": "Minimum inclusive x value in the region",
+               "title": "X Min",
+               "type": "number"
+            },
+            "y_min": {
+               "description": "Minimum inclusive y value in the region",
+               "title": "Y Min",
+               "type": "number"
+            },
+            "x_max": {
+               "description": "Maximum inclusive x value in the region",
+               "title": "X Max",
+               "type": "number"
+            },
+            "y_max": {
+               "description": "Maximum inclusive y value in the region",
+               "title": "Y Max",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "Clockwise rotation angle of the rectangle",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Rectangle",
+               "default": "Rectangle",
+               "enum": [
+                  "Rectangle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_min",
+            "y_min",
+            "x_max",
+            "y_max"
+         ],
+         "title": "Rectangle",
+         "type": "object"
+      },
+      "Region": {
+         "discriminator": {
+            "mapping": {
+               "Circle": "#/$defs/Circle",
+               "CombinationOf": "#/$defs/CombinationOf",
+               "DifferenceOf": "#/$defs/DifferenceOf",
+               "Ellipse": "#/$defs/Ellipse",
+               "IntersectionOf": "#/$defs/IntersectionOf",
+               "Polygon": "#/$defs/Polygon",
+               "Range": "#/$defs/Range",
+               "Rectangle": "#/$defs/Rectangle",
+               "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf",
+               "UnionOf": "#/$defs/UnionOf"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/CombinationOf"
+            },
+            {
+               "$ref": "#/$defs/UnionOf"
+            },
+            {
+               "$ref": "#/$defs/IntersectionOf"
+            },
+            {
+               "$ref": "#/$defs/DifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/SymmetricDifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/Range"
+            },
+            {
+               "$ref": "#/$defs/Rectangle"
+            },
+            {
+               "$ref": "#/$defs/Polygon"
+            },
+            {
+               "$ref": "#/$defs/Circle"
+            },
+            {
+               "$ref": "#/$defs/Ellipse"
+            }
+         ]
+      },
+      "SymmetricDifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "SymmetricDifferenceOf",
+               "default": "SymmetricDifferenceOf",
+               "enum": [
+                  "SymmetricDifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "SymmetricDifferenceOf",
+         "type": "object"
+      },
+      "UnionOf": {
+         "additionalProperties": false,
+         "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "UnionOf",
+               "default": "UnionOf",
+               "enum": [
+                  "UnionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "UnionOf",
+         "type": "object"
+      }
+   },
+   "$ref": "#/$defs/UnionOf"
+}
+
+
+

+
Fields:
+
+
+
+
+
+field left: Region[Axis] [Required]#
+

The left-hand Region to combine

+
+ +
+
+field right: Region[Axis] [Required]#
+

The right-hand Region to combine

+
+ +
+
+field type: Literal['UnionOf'] = 'UnionOf'#
+
+ +
+
+mask(points: dict[Axis, ndarray]) ndarray[source]#
+

Produce a mask of which points are in the region.

+
+ +
+ +
+
+pydantic model scanspec.regions.IntersectionOf[source]#
+

A point is in IntersectionOf(a, b) if in both a and b.

+

Typically created with the & operator.

+
>>> r = Range("x", 0.5, 2.5) & Range("x", 1.5, 3.5)
+>>> r.mask({"x": np.array([0, 1, 2, 3, 4])})
+array([False, False,  True, False, False])
+
+
+

+Show JSON schema
{
+   "$defs": {
+      "Circle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the circle",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the circle",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "radius": {
+               "description": "Radius of the circle",
+               "exclusiveMinimum": 0.0,
+               "title": "Radius",
+               "type": "number"
+            },
+            "type": {
+               "const": "Circle",
+               "default": "Circle",
+               "enum": [
+                  "Circle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "radius"
+         ],
+         "title": "Circle",
+         "type": "object"
+      },
+      "CombinationOf": {
+         "additionalProperties": false,
+         "description": "Abstract baseclass for a combination of two regions, left and right.",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "CombinationOf",
+               "default": "CombinationOf",
+               "enum": [
+                  "CombinationOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "CombinationOf",
+         "type": "object"
+      },
+      "DifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "DifferenceOf",
+               "default": "DifferenceOf",
+               "enum": [
+                  "DifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "DifferenceOf",
+         "type": "object"
+      },
+      "Ellipse": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the ellipse",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the ellipse",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "x_radius": {
+               "description": "The radius along the x axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "X Radius",
+               "type": "number"
+            },
+            "y_radius": {
+               "description": "The radius along the y axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "Y Radius",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "The angle of the ellipse (degrees)",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Ellipse",
+               "default": "Ellipse",
+               "enum": [
+                  "Ellipse"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "x_radius",
+            "y_radius"
+         ],
+         "title": "Ellipse",
+         "type": "object"
+      },
+      "IntersectionOf": {
+         "additionalProperties": false,
+         "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False,  True, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "IntersectionOf",
+               "default": "IntersectionOf",
+               "enum": [
+                  "IntersectionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "IntersectionOf",
+         "type": "object"
+      },
+      "Polygon": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_verts": {
+               "description": "The Nx1 x coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "X Verts",
+               "type": "array"
+            },
+            "y_verts": {
+               "description": "The Nx1 y coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "Y Verts",
+               "type": "array"
+            },
+            "type": {
+               "const": "Polygon",
+               "default": "Polygon",
+               "enum": [
+                  "Polygon"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_verts",
+            "y_verts"
+         ],
+         "title": "Polygon",
+         "type": "object"
+      },
+      "Range": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+         "properties": {
+            "axis": {
+               "description": "The name matching the axis to mask in spec",
+               "title": "Axis"
+            },
+            "min": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Min",
+               "type": "number"
+            },
+            "max": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Max",
+               "type": "number"
+            },
+            "type": {
+               "const": "Range",
+               "default": "Range",
+               "enum": [
+                  "Range"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "min",
+            "max"
+         ],
+         "title": "Range",
+         "type": "object"
+      },
+      "Rectangle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_min": {
+               "description": "Minimum inclusive x value in the region",
+               "title": "X Min",
+               "type": "number"
+            },
+            "y_min": {
+               "description": "Minimum inclusive y value in the region",
+               "title": "Y Min",
+               "type": "number"
+            },
+            "x_max": {
+               "description": "Maximum inclusive x value in the region",
+               "title": "X Max",
+               "type": "number"
+            },
+            "y_max": {
+               "description": "Maximum inclusive y value in the region",
+               "title": "Y Max",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "Clockwise rotation angle of the rectangle",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Rectangle",
+               "default": "Rectangle",
+               "enum": [
+                  "Rectangle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_min",
+            "y_min",
+            "x_max",
+            "y_max"
+         ],
+         "title": "Rectangle",
+         "type": "object"
+      },
+      "Region": {
+         "discriminator": {
+            "mapping": {
+               "Circle": "#/$defs/Circle",
+               "CombinationOf": "#/$defs/CombinationOf",
+               "DifferenceOf": "#/$defs/DifferenceOf",
+               "Ellipse": "#/$defs/Ellipse",
+               "IntersectionOf": "#/$defs/IntersectionOf",
+               "Polygon": "#/$defs/Polygon",
+               "Range": "#/$defs/Range",
+               "Rectangle": "#/$defs/Rectangle",
+               "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf",
+               "UnionOf": "#/$defs/UnionOf"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/CombinationOf"
+            },
+            {
+               "$ref": "#/$defs/UnionOf"
+            },
+            {
+               "$ref": "#/$defs/IntersectionOf"
+            },
+            {
+               "$ref": "#/$defs/DifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/SymmetricDifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/Range"
+            },
+            {
+               "$ref": "#/$defs/Rectangle"
+            },
+            {
+               "$ref": "#/$defs/Polygon"
+            },
+            {
+               "$ref": "#/$defs/Circle"
+            },
+            {
+               "$ref": "#/$defs/Ellipse"
+            }
+         ]
+      },
+      "SymmetricDifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "SymmetricDifferenceOf",
+               "default": "SymmetricDifferenceOf",
+               "enum": [
+                  "SymmetricDifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "SymmetricDifferenceOf",
+         "type": "object"
+      },
+      "UnionOf": {
+         "additionalProperties": false,
+         "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "UnionOf",
+               "default": "UnionOf",
+               "enum": [
+                  "UnionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "UnionOf",
+         "type": "object"
+      }
+   },
+   "$ref": "#/$defs/IntersectionOf"
+}
+
+
+

+
Fields:
+
+
+
+
+
+field left: Region[Axis] [Required]#
+

The left-hand Region to combine

+
+ +
+
+field right: Region[Axis] [Required]#
+

The right-hand Region to combine

+
+ +
+
+field type: Literal['IntersectionOf'] = 'IntersectionOf'#
+
+ +
+
+mask(points: dict[Axis, ndarray]) ndarray[source]#
+

Produce a mask of which points are in the region.

+
+ +
+ +
+
+pydantic model scanspec.regions.DifferenceOf[source]#
+

A point is in DifferenceOf(a, b) if in a and not in b.

+

Typically created with the - operator.

+
>>> r = Range("x", 0.5, 2.5) - Range("x", 1.5, 3.5)
+>>> r.mask({"x": np.array([0, 1, 2, 3, 4])})
+array([False,  True, False, False, False])
+
+
+

+Show JSON schema
{
+   "$defs": {
+      "Circle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the circle",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the circle",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "radius": {
+               "description": "Radius of the circle",
+               "exclusiveMinimum": 0.0,
+               "title": "Radius",
+               "type": "number"
+            },
+            "type": {
+               "const": "Circle",
+               "default": "Circle",
+               "enum": [
+                  "Circle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "radius"
+         ],
+         "title": "Circle",
+         "type": "object"
+      },
+      "CombinationOf": {
+         "additionalProperties": false,
+         "description": "Abstract baseclass for a combination of two regions, left and right.",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "CombinationOf",
+               "default": "CombinationOf",
+               "enum": [
+                  "CombinationOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "CombinationOf",
+         "type": "object"
+      },
+      "DifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "DifferenceOf",
+               "default": "DifferenceOf",
+               "enum": [
+                  "DifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "DifferenceOf",
+         "type": "object"
+      },
+      "Ellipse": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the ellipse",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the ellipse",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "x_radius": {
+               "description": "The radius along the x axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "X Radius",
+               "type": "number"
+            },
+            "y_radius": {
+               "description": "The radius along the y axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "Y Radius",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "The angle of the ellipse (degrees)",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Ellipse",
+               "default": "Ellipse",
+               "enum": [
+                  "Ellipse"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "x_radius",
+            "y_radius"
+         ],
+         "title": "Ellipse",
+         "type": "object"
+      },
+      "IntersectionOf": {
+         "additionalProperties": false,
+         "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False,  True, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "IntersectionOf",
+               "default": "IntersectionOf",
+               "enum": [
+                  "IntersectionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "IntersectionOf",
+         "type": "object"
+      },
+      "Polygon": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_verts": {
+               "description": "The Nx1 x coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "X Verts",
+               "type": "array"
+            },
+            "y_verts": {
+               "description": "The Nx1 y coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "Y Verts",
+               "type": "array"
+            },
+            "type": {
+               "const": "Polygon",
+               "default": "Polygon",
+               "enum": [
+                  "Polygon"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_verts",
+            "y_verts"
+         ],
+         "title": "Polygon",
+         "type": "object"
+      },
+      "Range": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+         "properties": {
+            "axis": {
+               "description": "The name matching the axis to mask in spec",
+               "title": "Axis"
+            },
+            "min": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Min",
+               "type": "number"
+            },
+            "max": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Max",
+               "type": "number"
+            },
+            "type": {
+               "const": "Range",
+               "default": "Range",
+               "enum": [
+                  "Range"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "min",
+            "max"
+         ],
+         "title": "Range",
+         "type": "object"
+      },
+      "Rectangle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_min": {
+               "description": "Minimum inclusive x value in the region",
+               "title": "X Min",
+               "type": "number"
+            },
+            "y_min": {
+               "description": "Minimum inclusive y value in the region",
+               "title": "Y Min",
+               "type": "number"
+            },
+            "x_max": {
+               "description": "Maximum inclusive x value in the region",
+               "title": "X Max",
+               "type": "number"
+            },
+            "y_max": {
+               "description": "Maximum inclusive y value in the region",
+               "title": "Y Max",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "Clockwise rotation angle of the rectangle",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Rectangle",
+               "default": "Rectangle",
+               "enum": [
+                  "Rectangle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_min",
+            "y_min",
+            "x_max",
+            "y_max"
+         ],
+         "title": "Rectangle",
+         "type": "object"
+      },
+      "Region": {
+         "discriminator": {
+            "mapping": {
+               "Circle": "#/$defs/Circle",
+               "CombinationOf": "#/$defs/CombinationOf",
+               "DifferenceOf": "#/$defs/DifferenceOf",
+               "Ellipse": "#/$defs/Ellipse",
+               "IntersectionOf": "#/$defs/IntersectionOf",
+               "Polygon": "#/$defs/Polygon",
+               "Range": "#/$defs/Range",
+               "Rectangle": "#/$defs/Rectangle",
+               "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf",
+               "UnionOf": "#/$defs/UnionOf"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/CombinationOf"
+            },
+            {
+               "$ref": "#/$defs/UnionOf"
+            },
+            {
+               "$ref": "#/$defs/IntersectionOf"
+            },
+            {
+               "$ref": "#/$defs/DifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/SymmetricDifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/Range"
+            },
+            {
+               "$ref": "#/$defs/Rectangle"
+            },
+            {
+               "$ref": "#/$defs/Polygon"
+            },
+            {
+               "$ref": "#/$defs/Circle"
+            },
+            {
+               "$ref": "#/$defs/Ellipse"
+            }
+         ]
+      },
+      "SymmetricDifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "SymmetricDifferenceOf",
+               "default": "SymmetricDifferenceOf",
+               "enum": [
+                  "SymmetricDifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "SymmetricDifferenceOf",
+         "type": "object"
+      },
+      "UnionOf": {
+         "additionalProperties": false,
+         "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "UnionOf",
+               "default": "UnionOf",
+               "enum": [
+                  "UnionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "UnionOf",
+         "type": "object"
+      }
+   },
+   "$ref": "#/$defs/DifferenceOf"
+}
+
+
+

+
Fields:
+
+
+
+
+
+field left: Region[Axis] [Required]#
+

The left-hand Region to combine

+
+ +
+
+field right: Region[Axis] [Required]#
+

The right-hand Region to combine

+
+ +
+
+field type: Literal['DifferenceOf'] = 'DifferenceOf'#
+
+ +
+
+mask(points: dict[Axis, ndarray]) ndarray[source]#
+

Produce a mask of which points are in the region.

+
+ +
+ +
+
+pydantic model scanspec.regions.SymmetricDifferenceOf[source]#
+

A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.

+

Typically created with the ^ operator.

+
>>> r = Range("x", 0.5, 2.5) ^ Range("x", 1.5, 3.5)
+>>> r.mask({"x": np.array([0, 1, 2, 3, 4])})
+array([False,  True, False,  True, False])
+
+
+

+Show JSON schema
{
+   "$defs": {
+      "Circle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the circle",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the circle",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "radius": {
+               "description": "Radius of the circle",
+               "exclusiveMinimum": 0.0,
+               "title": "Radius",
+               "type": "number"
+            },
+            "type": {
+               "const": "Circle",
+               "default": "Circle",
+               "enum": [
+                  "Circle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "radius"
+         ],
+         "title": "Circle",
+         "type": "object"
+      },
+      "CombinationOf": {
+         "additionalProperties": false,
+         "description": "Abstract baseclass for a combination of two regions, left and right.",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "CombinationOf",
+               "default": "CombinationOf",
+               "enum": [
+                  "CombinationOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "CombinationOf",
+         "type": "object"
+      },
+      "DifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "DifferenceOf",
+               "default": "DifferenceOf",
+               "enum": [
+                  "DifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "DifferenceOf",
+         "type": "object"
+      },
+      "Ellipse": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the ellipse",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the ellipse",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "x_radius": {
+               "description": "The radius along the x axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "X Radius",
+               "type": "number"
+            },
+            "y_radius": {
+               "description": "The radius along the y axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "Y Radius",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "The angle of the ellipse (degrees)",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Ellipse",
+               "default": "Ellipse",
+               "enum": [
+                  "Ellipse"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "x_radius",
+            "y_radius"
+         ],
+         "title": "Ellipse",
+         "type": "object"
+      },
+      "IntersectionOf": {
+         "additionalProperties": false,
+         "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False,  True, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "IntersectionOf",
+               "default": "IntersectionOf",
+               "enum": [
+                  "IntersectionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "IntersectionOf",
+         "type": "object"
+      },
+      "Polygon": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_verts": {
+               "description": "The Nx1 x coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "X Verts",
+               "type": "array"
+            },
+            "y_verts": {
+               "description": "The Nx1 y coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "Y Verts",
+               "type": "array"
+            },
+            "type": {
+               "const": "Polygon",
+               "default": "Polygon",
+               "enum": [
+                  "Polygon"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_verts",
+            "y_verts"
+         ],
+         "title": "Polygon",
+         "type": "object"
+      },
+      "Range": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+         "properties": {
+            "axis": {
+               "description": "The name matching the axis to mask in spec",
+               "title": "Axis"
+            },
+            "min": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Min",
+               "type": "number"
+            },
+            "max": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Max",
+               "type": "number"
+            },
+            "type": {
+               "const": "Range",
+               "default": "Range",
+               "enum": [
+                  "Range"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "min",
+            "max"
+         ],
+         "title": "Range",
+         "type": "object"
+      },
+      "Rectangle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_min": {
+               "description": "Minimum inclusive x value in the region",
+               "title": "X Min",
+               "type": "number"
+            },
+            "y_min": {
+               "description": "Minimum inclusive y value in the region",
+               "title": "Y Min",
+               "type": "number"
+            },
+            "x_max": {
+               "description": "Maximum inclusive x value in the region",
+               "title": "X Max",
+               "type": "number"
+            },
+            "y_max": {
+               "description": "Maximum inclusive y value in the region",
+               "title": "Y Max",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "Clockwise rotation angle of the rectangle",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Rectangle",
+               "default": "Rectangle",
+               "enum": [
+                  "Rectangle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_min",
+            "y_min",
+            "x_max",
+            "y_max"
+         ],
+         "title": "Rectangle",
+         "type": "object"
+      },
+      "Region": {
+         "discriminator": {
+            "mapping": {
+               "Circle": "#/$defs/Circle",
+               "CombinationOf": "#/$defs/CombinationOf",
+               "DifferenceOf": "#/$defs/DifferenceOf",
+               "Ellipse": "#/$defs/Ellipse",
+               "IntersectionOf": "#/$defs/IntersectionOf",
+               "Polygon": "#/$defs/Polygon",
+               "Range": "#/$defs/Range",
+               "Rectangle": "#/$defs/Rectangle",
+               "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf",
+               "UnionOf": "#/$defs/UnionOf"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/CombinationOf"
+            },
+            {
+               "$ref": "#/$defs/UnionOf"
+            },
+            {
+               "$ref": "#/$defs/IntersectionOf"
+            },
+            {
+               "$ref": "#/$defs/DifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/SymmetricDifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/Range"
+            },
+            {
+               "$ref": "#/$defs/Rectangle"
+            },
+            {
+               "$ref": "#/$defs/Polygon"
+            },
+            {
+               "$ref": "#/$defs/Circle"
+            },
+            {
+               "$ref": "#/$defs/Ellipse"
+            }
+         ]
+      },
+      "SymmetricDifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "SymmetricDifferenceOf",
+               "default": "SymmetricDifferenceOf",
+               "enum": [
+                  "SymmetricDifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "SymmetricDifferenceOf",
+         "type": "object"
+      },
+      "UnionOf": {
+         "additionalProperties": false,
+         "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "UnionOf",
+               "default": "UnionOf",
+               "enum": [
+                  "UnionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "UnionOf",
+         "type": "object"
+      }
+   },
+   "$ref": "#/$defs/SymmetricDifferenceOf"
+}
+
+
+

+
Fields:
+
+
+
+
+
+field left: Region[Axis] [Required]#
+

The left-hand Region to combine

+
+ +
+
+field right: Region[Axis] [Required]#
+

The right-hand Region to combine

+
+ +
+
+field type: Literal['SymmetricDifferenceOf'] = 'SymmetricDifferenceOf'#
+
+ +
+
+mask(points: dict[Axis, ndarray]) ndarray[source]#
+

Produce a mask of which points are in the region.

+
+ +
+ +
+
+pydantic model scanspec.regions.Range[source]#
+

Mask contains points of axis >= min and <= max.

+
>>> r = Range("x", 1, 2)
+>>> r.mask({"x": np.array([0, 1, 2, 3, 4])})
+array([False,  True,  True, False, False])
+
+
+

+Show JSON schema
{
+   "title": "Range",
+   "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+   "type": "object",
+   "properties": {
+      "axis": {
+         "description": "The name matching the axis to mask in spec",
+         "title": "Axis"
+      },
+      "min": {
+         "description": "The minimum inclusive value in the region",
+         "title": "Min",
+         "type": "number"
+      },
+      "max": {
+         "description": "The minimum inclusive value in the region",
+         "title": "Max",
+         "type": "number"
+      },
+      "type": {
+         "const": "Range",
+         "default": "Range",
+         "enum": [
+            "Range"
+         ],
+         "title": "Type",
+         "type": "string"
+      }
+   },
+   "additionalProperties": false,
+   "required": [
+      "axis",
+      "min",
+      "max"
+   ]
+}
+
+
+

+
Fields:
+
+
+
+
+
+field axis: Axis [Required]#
+

The name matching the axis to mask in spec

+
+ +
+
+field max: float [Required]#
+

The minimum inclusive value in the region

+
+ +
+
+field min: float [Required]#
+

The minimum inclusive value in the region

+
+ +
+
+field type: Literal['Range'] = 'Range'#
+
+ +
+
+axis_sets() list[set[Axis]][source]#
+

Produce the non-overlapping sets of axes this region spans.

+
+ +
+
+mask(points: dict[Axis, ndarray]) ndarray[source]#
+

Produce a mask of which points are in the region.

+
+ +
+ +
+
+pydantic model scanspec.regions.Rectangle[source]#
+

Mask contains points of axis within a rotated xy rectangle.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.regions import Rectangle
+from scanspec.specs import Line
+
+grid = Line("y", 1, 3, 10) * ~Line("x", 0, 2, 10)
+spec = grid & Rectangle("x", "y", 0, 1.1, 1.5, 2.1, 30)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-regions-1.png +
+

+Show JSON schema
{
+   "title": "Rectangle",
+   "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+   "type": "object",
+   "properties": {
+      "x_axis": {
+         "description": "The name matching the x axis of the spec",
+         "title": "X Axis"
+      },
+      "y_axis": {
+         "description": "The name matching the y axis of the spec",
+         "title": "Y Axis"
+      },
+      "x_min": {
+         "description": "Minimum inclusive x value in the region",
+         "title": "X Min",
+         "type": "number"
+      },
+      "y_min": {
+         "description": "Minimum inclusive y value in the region",
+         "title": "Y Min",
+         "type": "number"
+      },
+      "x_max": {
+         "description": "Maximum inclusive x value in the region",
+         "title": "X Max",
+         "type": "number"
+      },
+      "y_max": {
+         "description": "Maximum inclusive y value in the region",
+         "title": "Y Max",
+         "type": "number"
+      },
+      "angle": {
+         "default": 0.0,
+         "description": "Clockwise rotation angle of the rectangle",
+         "title": "Angle",
+         "type": "number"
+      },
+      "type": {
+         "const": "Rectangle",
+         "default": "Rectangle",
+         "enum": [
+            "Rectangle"
+         ],
+         "title": "Type",
+         "type": "string"
+      }
+   },
+   "additionalProperties": false,
+   "required": [
+      "x_axis",
+      "y_axis",
+      "x_min",
+      "y_min",
+      "x_max",
+      "y_max"
+   ]
+}
+
+
+

+
Fields:
+
+
+
+
+
+field angle: float = 0.0#
+

Clockwise rotation angle of the rectangle

+
+ +
+
+field type: Literal['Rectangle'] = 'Rectangle'#
+
+ +
+
+field x_axis: Axis [Required]#
+

The name matching the x axis of the spec

+
+ +
+
+field x_max: float [Required]#
+

Maximum inclusive x value in the region

+
+ +
+
+field x_min: float [Required]#
+

Minimum inclusive x value in the region

+
+ +
+
+field y_axis: Axis [Required]#
+

The name matching the y axis of the spec

+
+ +
+
+field y_max: float [Required]#
+

Maximum inclusive y value in the region

+
+ +
+
+field y_min: float [Required]#
+

Minimum inclusive y value in the region

+
+ +
+
+axis_sets() list[set[Axis]][source]#
+

Produce the non-overlapping sets of axes this region spans.

+
+ +
+
+mask(points: dict[Axis, ndarray]) ndarray[source]#
+

Produce a mask of which points are in the region.

+
+ +
+ +
+
+pydantic model scanspec.regions.Polygon[source]#
+

Mask contains points of axis within a rotated xy polygon.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.regions import Polygon
+from scanspec.specs import Line
+
+grid = Line("y", 3, 8, 10) * ~Line("x", 1 ,8, 10)
+spec = grid & Polygon("x", "y", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-regions-2.png +
+

+Show JSON schema
{
+   "title": "Polygon",
+   "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+   "type": "object",
+   "properties": {
+      "x_axis": {
+         "description": "The name matching the x axis of the spec",
+         "title": "X Axis"
+      },
+      "y_axis": {
+         "description": "The name matching the y axis of the spec",
+         "title": "Y Axis"
+      },
+      "x_verts": {
+         "description": "The Nx1 x coordinates of the polygons vertices",
+         "items": {
+            "type": "number"
+         },
+         "minItems": 3,
+         "title": "X Verts",
+         "type": "array"
+      },
+      "y_verts": {
+         "description": "The Nx1 y coordinates of the polygons vertices",
+         "items": {
+            "type": "number"
+         },
+         "minItems": 3,
+         "title": "Y Verts",
+         "type": "array"
+      },
+      "type": {
+         "const": "Polygon",
+         "default": "Polygon",
+         "enum": [
+            "Polygon"
+         ],
+         "title": "Type",
+         "type": "string"
+      }
+   },
+   "additionalProperties": false,
+   "required": [
+      "x_axis",
+      "y_axis",
+      "x_verts",
+      "y_verts"
+   ]
+}
+
+
+

+
Fields:
+
+
+
+
+
+field type: Literal['Polygon'] = 'Polygon'#
+
+ +
+
+field x_axis: Axis [Required]#
+

The name matching the x axis of the spec

+
+ +
+
+field x_verts: list[float] [Required]#
+

The Nx1 x coordinates of the polygons vertices

+
+
Constraints:
+
    +
  • min_length = 3

  • +
+
+
+
+ +
+
+field y_axis: Axis [Required]#
+

The name matching the y axis of the spec

+
+ +
+
+field y_verts: list[float] [Required]#
+

The Nx1 y coordinates of the polygons vertices

+
+
Constraints:
+
    +
  • min_length = 3

  • +
+
+
+
+ +
+
+axis_sets() list[set[Axis]][source]#
+

Produce the non-overlapping sets of axes this region spans.

+
+ +
+
+mask(points: dict[Axis, ndarray]) ndarray[source]#
+

Produce a mask of which points are in the region.

+
+ +
+ +
+
+pydantic model scanspec.regions.Circle[source]#
+

Mask contains points of axis within an xy circle of given radius.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.regions import Circle
+from scanspec.specs import Line
+
+grid = Line("y", 1, 3, 10) * ~Line("x", 0, 2, 10)
+spec = grid & Circle("x", "y", 1, 2, 0.9)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-regions-3.png +
+

+Show JSON schema
{
+   "title": "Circle",
+   "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+   "type": "object",
+   "properties": {
+      "x_axis": {
+         "description": "The name matching the x axis of the spec",
+         "title": "X Axis"
+      },
+      "y_axis": {
+         "description": "The name matching the y axis of the spec",
+         "title": "Y Axis"
+      },
+      "x_middle": {
+         "description": "The central x point of the circle",
+         "title": "X Middle",
+         "type": "number"
+      },
+      "y_middle": {
+         "description": "The central y point of the circle",
+         "title": "Y Middle",
+         "type": "number"
+      },
+      "radius": {
+         "description": "Radius of the circle",
+         "exclusiveMinimum": 0.0,
+         "title": "Radius",
+         "type": "number"
+      },
+      "type": {
+         "const": "Circle",
+         "default": "Circle",
+         "enum": [
+            "Circle"
+         ],
+         "title": "Type",
+         "type": "string"
+      }
+   },
+   "additionalProperties": false,
+   "required": [
+      "x_axis",
+      "y_axis",
+      "x_middle",
+      "y_middle",
+      "radius"
+   ]
+}
+
+
+

+
Fields:
+
+
+
+
+
+field radius: float [Required]#
+

Radius of the circle

+
+
Constraints:
+
    +
  • gt = 0

  • +
+
+
+
+ +
+
+field type: Literal['Circle'] = 'Circle'#
+
+ +
+
+field x_axis: Axis [Required]#
+

The name matching the x axis of the spec

+
+ +
+
+field x_middle: float [Required]#
+

The central x point of the circle

+
+ +
+
+field y_axis: Axis [Required]#
+

The name matching the y axis of the spec

+
+ +
+
+field y_middle: float [Required]#
+

The central y point of the circle

+
+ +
+
+axis_sets() list[set[Axis]][source]#
+

Produce the non-overlapping sets of axes this region spans.

+
+ +
+
+mask(points: dict[Axis, ndarray]) ndarray[source]#
+

Produce a mask of which points are in the region.

+
+ +
+ +
+
+pydantic model scanspec.regions.Ellipse[source]#
+

Mask contains points of axis within an xy ellipse of given radius.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.regions import Ellipse
+from scanspec.specs import Line
+
+grid = Line("y", 3, 8, 10) * ~Line("x", 1 ,8, 10)
+spec = grid & Ellipse("x", "y", 5, 5, 2, 3, 75)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-regions-4.png +
+

+Show JSON schema
{
+   "title": "Ellipse",
+   "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+   "type": "object",
+   "properties": {
+      "x_axis": {
+         "description": "The name matching the x axis of the spec",
+         "title": "X Axis"
+      },
+      "y_axis": {
+         "description": "The name matching the y axis of the spec",
+         "title": "Y Axis"
+      },
+      "x_middle": {
+         "description": "The central x point of the ellipse",
+         "title": "X Middle",
+         "type": "number"
+      },
+      "y_middle": {
+         "description": "The central y point of the ellipse",
+         "title": "Y Middle",
+         "type": "number"
+      },
+      "x_radius": {
+         "description": "The radius along the x axis of the ellipse",
+         "exclusiveMinimum": 0.0,
+         "title": "X Radius",
+         "type": "number"
+      },
+      "y_radius": {
+         "description": "The radius along the y axis of the ellipse",
+         "exclusiveMinimum": 0.0,
+         "title": "Y Radius",
+         "type": "number"
+      },
+      "angle": {
+         "default": 0.0,
+         "description": "The angle of the ellipse (degrees)",
+         "title": "Angle",
+         "type": "number"
+      },
+      "type": {
+         "const": "Ellipse",
+         "default": "Ellipse",
+         "enum": [
+            "Ellipse"
+         ],
+         "title": "Type",
+         "type": "string"
+      }
+   },
+   "additionalProperties": false,
+   "required": [
+      "x_axis",
+      "y_axis",
+      "x_middle",
+      "y_middle",
+      "x_radius",
+      "y_radius"
+   ]
+}
+
+
+

+
Fields:
+
+
+
+
+
+field angle: float = 0.0#
+

The angle of the ellipse (degrees)

+
+ +
+
+field type: Literal['Ellipse'] = 'Ellipse'#
+
+ +
+
+field x_axis: Axis [Required]#
+

The name matching the x axis of the spec

+
+ +
+
+field x_middle: float [Required]#
+

The central x point of the ellipse

+
+ +
+
+field x_radius: float [Required]#
+

The radius along the x axis of the ellipse

+
+
Constraints:
+
    +
  • gt = 0

  • +
+
+
+
+ +
+
+field y_axis: Axis [Required]#
+

The name matching the y axis of the spec

+
+ +
+
+field y_middle: float [Required]#
+

The central y point of the ellipse

+
+ +
+
+field y_radius: float [Required]#
+

The radius along the y axis of the ellipse

+
+
Constraints:
+
    +
  • gt = 0

  • +
+
+
+
+ +
+
+axis_sets() list[set[Axis]][source]#
+

Produce the non-overlapping sets of axes this region spans.

+
+ +
+
+mask(points: dict[Axis, ndarray]) ndarray[source]#
+

Produce a mask of which points are in the region.

+
+ +
+ +
+
+scanspec.regions.find_regions(obj) Iterator[Region][source]#
+

Recursively yield Regions from obj and its children.

+
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/main/_api/scanspec.specs.html b/main/_api/scanspec.specs.html new file mode 100644 index 00000000..410ab60b --- /dev/null +++ b/main/_api/scanspec.specs.html @@ -0,0 +1,7083 @@ + + + + + + + + + + + scanspec.specs — scanspec 0.7.3.dev9+ge42f8eba documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

scanspec.specs#

+

Spec and its subclasses.

+
+

Inheritance diagram of scanspec.specs

+

Members

+
+ + + + + + + + + + + + + + +

DURATION

Can be used as a special key to indicate how long each point should be

Spec

A serializable representation of the type and parameters of a scan.

fly

Flyscan, zipping with fixed duration for every frame.

step

Step scan, with num frames of given duration at each frame in the spec.

+
+
+
+scanspec.specs.DURATION = 'DURATION'#
+

Can be used as a special key to indicate how long each point should be

+
+ +
+
+class scanspec.specs.Spec[source]#
+

A serializable representation of the type and parameters of a scan.

+

Abstract baseclass for the specification of a scan. Supports operators:

+
    +
  • *: Outer Product of two Specs, nesting the second within the first. +If the first operand is an integer, wrap it in a Repeat

  • +
  • &: Mask the Spec with a Region, excluding midpoints outside of it

  • +
  • ~: Snake the Spec, reversing every other iteration of it

  • +
+
+
+axes() list[Axis][source]#
+

Return the list of axes that are present in the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+calculate(bounds=True, nested=False) list[Frames[Axis]][source]#
+

Produce a stack of nested Frames that form the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+frames() Frames[Axis][source]#
+

Expand all the scan Frames and return them.

+
+ +
+
+midpoints() Midpoints[Axis][source]#
+

Return Midpoints that can be iterated point by point.

+
+ +
+
+shape() tuple[int, ...][source]#
+

Return the final, simplified shape of the scan.

+
+ +
+
+zip(other: Spec) Zip[Axis][source]#
+

Zip the Spec with another, iterating in tandem.

+
+ +
+
+concat(other: Spec) Concat[Axis][source]#
+

Concat the Spec with another, iterating one after the other.

+
+ +
+
+serialize() Mapping[str, Any][source]#
+

Serialize the Spec to a dictionary.

+
+ +
+
+static deserialize(obj) Spec[source]#
+

Deserialize a Spec from a dictionary.

+
+ +
+ +
+
+pydantic model scanspec.specs.Product[source]#
+

Outer product of two Specs, nesting inner within outer.

+

This means that inner will run in its entirety at each point in outer.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line
+
+spec = Line("y", 1, 2, 3) * Line("x", 3, 4, 12)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-1.png +
+

+Show JSON schema
{
+   "$defs": {
+      "Circle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the circle",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the circle",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "radius": {
+               "description": "Radius of the circle",
+               "exclusiveMinimum": 0.0,
+               "title": "Radius",
+               "type": "number"
+            },
+            "type": {
+               "const": "Circle",
+               "default": "Circle",
+               "enum": [
+                  "Circle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "radius"
+         ],
+         "title": "Circle",
+         "type": "object"
+      },
+      "CombinationOf": {
+         "additionalProperties": false,
+         "description": "Abstract baseclass for a combination of two regions, left and right.",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "CombinationOf",
+               "default": "CombinationOf",
+               "enum": [
+                  "CombinationOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "CombinationOf",
+         "type": "object"
+      },
+      "Concat": {
+         "additionalProperties": false,
+         "description": "Concatenate two Specs together, running one after the other.\n\nEach Dimension of left and right must contain the same axes. Typically\nformed using `Spec.concat`.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 3, 3).concat(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Concat, midpoints will appear earlier"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Concat, midpoints will appear later"
+            },
+            "gap": {
+               "default": false,
+               "description": "If True, force a gap in the output at the join",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Concat",
+               "default": "Concat",
+               "enum": [
+                  "Concat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Concat",
+         "type": "object"
+      },
+      "DifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "DifferenceOf",
+               "default": "DifferenceOf",
+               "enum": [
+                  "DifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "DifferenceOf",
+         "type": "object"
+      },
+      "Ellipse": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the ellipse",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the ellipse",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "x_radius": {
+               "description": "The radius along the x axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "X Radius",
+               "type": "number"
+            },
+            "y_radius": {
+               "description": "The radius along the y axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "Y Radius",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "The angle of the ellipse (degrees)",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Ellipse",
+               "default": "Ellipse",
+               "enum": [
+                  "Ellipse"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "x_radius",
+            "y_radius"
+         ],
+         "title": "Ellipse",
+         "type": "object"
+      },
+      "IntersectionOf": {
+         "additionalProperties": false,
+         "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False,  True, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "IntersectionOf",
+               "default": "IntersectionOf",
+               "enum": [
+                  "IntersectionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "IntersectionOf",
+         "type": "object"
+      },
+      "Line": {
+         "additionalProperties": false,
+         "description": "Linearly spaced frames with start and stop as first and last midpoints.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 2, 5)",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "start": {
+               "description": "Midpoint of the first point of the line",
+               "title": "Start",
+               "type": "number"
+            },
+            "stop": {
+               "description": "Midpoint of the last point of the line",
+               "title": "Stop",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Line",
+               "default": "Line",
+               "enum": [
+                  "Line"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "start",
+            "stop",
+            "num"
+         ],
+         "title": "Line",
+         "type": "object"
+      },
+      "Mask": {
+         "additionalProperties": false,
+         "description": "Restrict Spec to only midpoints that fall inside the given Region.\n\nTypically created with the ``&`` operator. It also pushes down the\n``& | ^ -`` operators to its `Region` to avoid the need for brackets on\ncombinations of Regions.\n\nIf a Region spans multiple Frames objects, they will be squashed together.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * Line(\"x\", 3, 5, 5) & Circle(\"x\", \"y\", 4, 2, 1.2)\n\nSee Also: `why-squash-can-change-path`",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec containing the source midpoints"
+            },
+            "region": {
+               "$ref": "#/$defs/Region",
+               "description": "The Region that midpoints will be inside"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Mask",
+               "default": "Mask",
+               "enum": [
+                  "Mask"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec",
+            "region"
+         ],
+         "title": "Mask",
+         "type": "object"
+      },
+      "Polygon": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_verts": {
+               "description": "The Nx1 x coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "X Verts",
+               "type": "array"
+            },
+            "y_verts": {
+               "description": "The Nx1 y coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "Y Verts",
+               "type": "array"
+            },
+            "type": {
+               "const": "Polygon",
+               "default": "Polygon",
+               "enum": [
+                  "Polygon"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_verts",
+            "y_verts"
+         ],
+         "title": "Polygon",
+         "type": "object"
+      },
+      "Product": {
+         "additionalProperties": false,
+         "description": "Outer product of two Specs, nesting inner within outer.\n\nThis means that inner will run in its entirety at each point in outer.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 2, 3) * Line(\"x\", 3, 4, 12)",
+         "properties": {
+            "outer": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed once"
+            },
+            "inner": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed len(outer) times"
+            },
+            "type": {
+               "const": "Product",
+               "default": "Product",
+               "enum": [
+                  "Product"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "outer",
+            "inner"
+         ],
+         "title": "Product",
+         "type": "object"
+      },
+      "Range": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+         "properties": {
+            "axis": {
+               "description": "The name matching the axis to mask in spec",
+               "title": "Axis"
+            },
+            "min": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Min",
+               "type": "number"
+            },
+            "max": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Max",
+               "type": "number"
+            },
+            "type": {
+               "const": "Range",
+               "default": "Range",
+               "enum": [
+                  "Range"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "min",
+            "max"
+         ],
+         "title": "Range",
+         "type": "object"
+      },
+      "Rectangle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_min": {
+               "description": "Minimum inclusive x value in the region",
+               "title": "X Min",
+               "type": "number"
+            },
+            "y_min": {
+               "description": "Minimum inclusive y value in the region",
+               "title": "Y Min",
+               "type": "number"
+            },
+            "x_max": {
+               "description": "Maximum inclusive x value in the region",
+               "title": "X Max",
+               "type": "number"
+            },
+            "y_max": {
+               "description": "Maximum inclusive y value in the region",
+               "title": "Y Max",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "Clockwise rotation angle of the rectangle",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Rectangle",
+               "default": "Rectangle",
+               "enum": [
+                  "Rectangle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_min",
+            "y_min",
+            "x_max",
+            "y_max"
+         ],
+         "title": "Rectangle",
+         "type": "object"
+      },
+      "Region": {
+         "discriminator": {
+            "mapping": {
+               "Circle": "#/$defs/Circle",
+               "CombinationOf": "#/$defs/CombinationOf",
+               "DifferenceOf": "#/$defs/DifferenceOf",
+               "Ellipse": "#/$defs/Ellipse",
+               "IntersectionOf": "#/$defs/IntersectionOf",
+               "Polygon": "#/$defs/Polygon",
+               "Range": "#/$defs/Range",
+               "Rectangle": "#/$defs/Rectangle",
+               "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf",
+               "UnionOf": "#/$defs/UnionOf"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/CombinationOf"
+            },
+            {
+               "$ref": "#/$defs/UnionOf"
+            },
+            {
+               "$ref": "#/$defs/IntersectionOf"
+            },
+            {
+               "$ref": "#/$defs/DifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/SymmetricDifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/Range"
+            },
+            {
+               "$ref": "#/$defs/Rectangle"
+            },
+            {
+               "$ref": "#/$defs/Polygon"
+            },
+            {
+               "$ref": "#/$defs/Circle"
+            },
+            {
+               "$ref": "#/$defs/Ellipse"
+            }
+         ]
+      },
+      "Repeat": {
+         "additionalProperties": false,
+         "description": "Repeat an empty frame num times.\n\nCan be used on the outside of a scan to repeat the same scan many times.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = 2 * ~Line.bounded(\"x\", 3, 4, 1)\n\nIf you want snaked axes to have no gap between iterations you can do:\n\n.. example_spec::\n\n    from scanspec.specs import Line, Repeat\n\n    spec = Repeat(2, gap=False) * ~Line.bounded(\"x\", 3, 4, 1)\n\n.. note:: There is no turnaround arrow at x=4",
+         "properties": {
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "gap": {
+               "default": true,
+               "description": "If False and the slowest of the stack of Frames is snaked then the end and start of consecutive iterations of Spec will have no gap",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Repeat",
+               "default": "Repeat",
+               "enum": [
+                  "Repeat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "num"
+         ],
+         "title": "Repeat",
+         "type": "object"
+      },
+      "Snake": {
+         "additionalProperties": false,
+         "description": "Run the Spec in reverse on every other iteration when nested.\n\nTypically created with the ``~`` operator.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * ~Line(\"x\", 3, 5, 5)",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to run in reverse every other iteration"
+            },
+            "type": {
+               "const": "Snake",
+               "default": "Snake",
+               "enum": [
+                  "Snake"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Snake",
+         "type": "object"
+      },
+      "Spec": {
+         "discriminator": {
+            "mapping": {
+               "Concat": "#/$defs/Concat",
+               "Line": "#/$defs/Line",
+               "Mask": "#/$defs/Mask",
+               "Product": "#/$defs/Product",
+               "Repeat": "#/$defs/Repeat",
+               "Snake": "#/$defs/Snake",
+               "Spiral": "#/$defs/Spiral",
+               "Squash": "#/$defs/Squash",
+               "Static": "#/$defs/Static",
+               "Zip": "#/$defs/Zip"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/Product"
+            },
+            {
+               "$ref": "#/$defs/Repeat"
+            },
+            {
+               "$ref": "#/$defs/Zip"
+            },
+            {
+               "$ref": "#/$defs/Mask"
+            },
+            {
+               "$ref": "#/$defs/Snake"
+            },
+            {
+               "$ref": "#/$defs/Concat"
+            },
+            {
+               "$ref": "#/$defs/Squash"
+            },
+            {
+               "$ref": "#/$defs/Line"
+            },
+            {
+               "$ref": "#/$defs/Static"
+            },
+            {
+               "$ref": "#/$defs/Spiral"
+            }
+         ]
+      },
+      "Spiral": {
+         "additionalProperties": false,
+         "description": "Archimedean spiral of \"x_axis\" and \"y_axis\".\n\nStarts at centre point (\"x_start\", \"y_start\") with angle \"rotate\". Produces\n\"num\" points in a spiral spanning width of \"x_range\" and height of \"y_range\"\n\n.. example_spec::\n\n    from scanspec.specs import Spiral\n\n    spec = Spiral(\"x\", \"y\", 1, 5, 10, 50, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "An identifier for what to move for x",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "An identifier for what to move for y",
+               "title": "Y Axis"
+            },
+            "x_start": {
+               "description": "x centre of the spiral",
+               "title": "X Start",
+               "type": "number"
+            },
+            "y_start": {
+               "description": "y centre of the spiral",
+               "title": "Y Start",
+               "type": "number"
+            },
+            "x_range": {
+               "description": "x width of the spiral",
+               "title": "X Range",
+               "type": "number"
+            },
+            "y_range": {
+               "description": "y width of the spiral",
+               "title": "Y Range",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "rotate": {
+               "default": 0.0,
+               "description": "How much to rotate the angle of the spiral",
+               "title": "Rotate",
+               "type": "number"
+            },
+            "type": {
+               "const": "Spiral",
+               "default": "Spiral",
+               "enum": [
+                  "Spiral"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_start",
+            "y_start",
+            "x_range",
+            "y_range",
+            "num"
+         ],
+         "title": "Spiral",
+         "type": "object"
+      },
+      "Squash": {
+         "additionalProperties": false,
+         "description": "Squash a stack of Frames together into a single expanded Frames object.\n\nSee Also:\n    `why-squash-can-change-path`\n\n.. example_spec::\n\n    from scanspec.specs import Line, Squash\n\n    spec = Squash(Line(\"y\", 1, 2, 3) * Line(\"x\", 0, 1, 4))",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to squash the dimensions of"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Squash",
+               "default": "Squash",
+               "enum": [
+                  "Squash"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Squash",
+         "type": "object"
+      },
+      "Static": {
+         "additionalProperties": false,
+         "description": "A static frame, repeated num times, with axis at value.\n\nCan be used to set axis=value at every point in a scan.\n\n.. example_spec::\n\n    from scanspec.specs import Line, Static\n\n    spec = Line(\"y\", 1, 2, 3).zip(Static(\"x\", 3))",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "value": {
+               "description": "The value at each point",
+               "title": "Value",
+               "type": "number"
+            },
+            "num": {
+               "default": 1,
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Static",
+               "default": "Static",
+               "enum": [
+                  "Static"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "value"
+         ],
+         "title": "Static",
+         "type": "object"
+      },
+      "SymmetricDifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "SymmetricDifferenceOf",
+               "default": "SymmetricDifferenceOf",
+               "enum": [
+                  "SymmetricDifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "SymmetricDifferenceOf",
+         "type": "object"
+      },
+      "UnionOf": {
+         "additionalProperties": false,
+         "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "UnionOf",
+               "default": "UnionOf",
+               "enum": [
+                  "UnionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "UnionOf",
+         "type": "object"
+      },
+      "Zip": {
+         "additionalProperties": false,
+         "description": "Run two Specs in parallel, merging their midpoints together.\n\nTypically formed using `Spec.zip`.\n\nStacks of Frames are merged by:\n\n- If right creates a stack of a single Frames object of size 1, expand it to\n  the size of the fastest Frames object created by left\n- Merge individual Frames objects together from fastest to slowest\n\nThis means that Zipping a Spec producing stack [l2, l1] with a Spec\nproducing stack [r1] will assert len(l1)==len(r1), and produce\nstack [l2, l1.zip(r1)].\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"z\", 1, 2, 3) * Line(\"y\", 3, 4, 5).zip(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Zip, will appear earlier in axes"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Zip, will appear later in axes"
+            },
+            "type": {
+               "const": "Zip",
+               "default": "Zip",
+               "enum": [
+                  "Zip"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Zip",
+         "type": "object"
+      }
+   },
+   "$ref": "#/$defs/Product"
+}
+
+
+

+
Fields:
+
+
+
+
+
+field inner: Spec[Axis] [Required]#
+

Will be executed len(outer) times

+
+ +
+
+field outer: Spec[Axis] [Required]#
+

Will be executed once

+
+ +
+
+field type: Literal['Product'] = 'Product'#
+
+ +
+
+axes() list[Axis][source]#
+

Return the list of axes that are present in the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+calculate(bounds=True, nested=False) list[Frames[Axis]][source]#
+

Produce a stack of nested Frames that form the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+ +
+
+pydantic model scanspec.specs.Repeat[source]#
+

Repeat an empty frame num times.

+

Can be used on the outside of a scan to repeat the same scan many times.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line
+
+spec = 2 * ~Line.bounded("x", 3, 4, 1)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-2.png +
+

If you want snaked axes to have no gap between iterations you can do:

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line, Repeat
+
+spec = Repeat(2, gap=False) * ~Line.bounded("x", 3, 4, 1)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-3.png +
+
+

Note

+

There is no turnaround arrow at x=4

+
+

+Show JSON schema
{
+   "title": "Repeat",
+   "description": "Repeat an empty frame num times.\n\nCan be used on the outside of a scan to repeat the same scan many times.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = 2 * ~Line.bounded(\"x\", 3, 4, 1)\n\nIf you want snaked axes to have no gap between iterations you can do:\n\n.. example_spec::\n\n    from scanspec.specs import Line, Repeat\n\n    spec = Repeat(2, gap=False) * ~Line.bounded(\"x\", 3, 4, 1)\n\n.. note:: There is no turnaround arrow at x=4",
+   "type": "object",
+   "properties": {
+      "num": {
+         "description": "Number of frames to produce",
+         "minimum": 1,
+         "title": "Num",
+         "type": "integer"
+      },
+      "gap": {
+         "default": true,
+         "description": "If False and the slowest of the stack of Frames is snaked then the end and start of consecutive iterations of Spec will have no gap",
+         "title": "Gap",
+         "type": "boolean"
+      },
+      "type": {
+         "const": "Repeat",
+         "default": "Repeat",
+         "enum": [
+            "Repeat"
+         ],
+         "title": "Type",
+         "type": "string"
+      }
+   },
+   "additionalProperties": false,
+   "required": [
+      "num"
+   ]
+}
+
+
+

+
Fields:
+
+
+
+
+
+field gap: bool = True#
+

If False and the slowest of the stack of Frames is snaked then the end and start of consecutive iterations of Spec will have no gap

+
+ +
+
+field num: int [Required]#
+

Number of frames to produce

+
+
Constraints:
+
    +
  • ge = 1

  • +
+
+
+
+ +
+
+field type: Literal['Repeat'] = 'Repeat'#
+
+ +
+
+axes() list[Axis][source]#
+

Return the list of axes that are present in the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+calculate(bounds=True, nested=False) list[Frames[Axis]][source]#
+

Produce a stack of nested Frames that form the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+ +
+
+pydantic model scanspec.specs.Zip[source]#
+

Run two Specs in parallel, merging their midpoints together.

+

Typically formed using Spec.zip.

+

Stacks of Frames are merged by:

+
    +
  • If right creates a stack of a single Frames object of size 1, expand it to +the size of the fastest Frames object created by left

  • +
  • Merge individual Frames objects together from fastest to slowest

  • +
+

This means that Zipping a Spec producing stack [l2, l1] with a Spec +producing stack [r1] will assert len(l1)==len(r1), and produce +stack [l2, l1.zip(r1)].

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line
+
+spec = Line("z", 1, 2, 3) * Line("y", 3, 4, 5).zip(Line("x", 4, 5, 5))
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-4.png +
+

+Show JSON schema
{
+   "$defs": {
+      "Circle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the circle",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the circle",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "radius": {
+               "description": "Radius of the circle",
+               "exclusiveMinimum": 0.0,
+               "title": "Radius",
+               "type": "number"
+            },
+            "type": {
+               "const": "Circle",
+               "default": "Circle",
+               "enum": [
+                  "Circle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "radius"
+         ],
+         "title": "Circle",
+         "type": "object"
+      },
+      "CombinationOf": {
+         "additionalProperties": false,
+         "description": "Abstract baseclass for a combination of two regions, left and right.",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "CombinationOf",
+               "default": "CombinationOf",
+               "enum": [
+                  "CombinationOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "CombinationOf",
+         "type": "object"
+      },
+      "Concat": {
+         "additionalProperties": false,
+         "description": "Concatenate two Specs together, running one after the other.\n\nEach Dimension of left and right must contain the same axes. Typically\nformed using `Spec.concat`.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 3, 3).concat(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Concat, midpoints will appear earlier"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Concat, midpoints will appear later"
+            },
+            "gap": {
+               "default": false,
+               "description": "If True, force a gap in the output at the join",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Concat",
+               "default": "Concat",
+               "enum": [
+                  "Concat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Concat",
+         "type": "object"
+      },
+      "DifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "DifferenceOf",
+               "default": "DifferenceOf",
+               "enum": [
+                  "DifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "DifferenceOf",
+         "type": "object"
+      },
+      "Ellipse": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the ellipse",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the ellipse",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "x_radius": {
+               "description": "The radius along the x axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "X Radius",
+               "type": "number"
+            },
+            "y_radius": {
+               "description": "The radius along the y axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "Y Radius",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "The angle of the ellipse (degrees)",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Ellipse",
+               "default": "Ellipse",
+               "enum": [
+                  "Ellipse"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "x_radius",
+            "y_radius"
+         ],
+         "title": "Ellipse",
+         "type": "object"
+      },
+      "IntersectionOf": {
+         "additionalProperties": false,
+         "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False,  True, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "IntersectionOf",
+               "default": "IntersectionOf",
+               "enum": [
+                  "IntersectionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "IntersectionOf",
+         "type": "object"
+      },
+      "Line": {
+         "additionalProperties": false,
+         "description": "Linearly spaced frames with start and stop as first and last midpoints.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 2, 5)",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "start": {
+               "description": "Midpoint of the first point of the line",
+               "title": "Start",
+               "type": "number"
+            },
+            "stop": {
+               "description": "Midpoint of the last point of the line",
+               "title": "Stop",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Line",
+               "default": "Line",
+               "enum": [
+                  "Line"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "start",
+            "stop",
+            "num"
+         ],
+         "title": "Line",
+         "type": "object"
+      },
+      "Mask": {
+         "additionalProperties": false,
+         "description": "Restrict Spec to only midpoints that fall inside the given Region.\n\nTypically created with the ``&`` operator. It also pushes down the\n``& | ^ -`` operators to its `Region` to avoid the need for brackets on\ncombinations of Regions.\n\nIf a Region spans multiple Frames objects, they will be squashed together.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * Line(\"x\", 3, 5, 5) & Circle(\"x\", \"y\", 4, 2, 1.2)\n\nSee Also: `why-squash-can-change-path`",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec containing the source midpoints"
+            },
+            "region": {
+               "$ref": "#/$defs/Region",
+               "description": "The Region that midpoints will be inside"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Mask",
+               "default": "Mask",
+               "enum": [
+                  "Mask"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec",
+            "region"
+         ],
+         "title": "Mask",
+         "type": "object"
+      },
+      "Polygon": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_verts": {
+               "description": "The Nx1 x coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "X Verts",
+               "type": "array"
+            },
+            "y_verts": {
+               "description": "The Nx1 y coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "Y Verts",
+               "type": "array"
+            },
+            "type": {
+               "const": "Polygon",
+               "default": "Polygon",
+               "enum": [
+                  "Polygon"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_verts",
+            "y_verts"
+         ],
+         "title": "Polygon",
+         "type": "object"
+      },
+      "Product": {
+         "additionalProperties": false,
+         "description": "Outer product of two Specs, nesting inner within outer.\n\nThis means that inner will run in its entirety at each point in outer.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 2, 3) * Line(\"x\", 3, 4, 12)",
+         "properties": {
+            "outer": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed once"
+            },
+            "inner": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed len(outer) times"
+            },
+            "type": {
+               "const": "Product",
+               "default": "Product",
+               "enum": [
+                  "Product"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "outer",
+            "inner"
+         ],
+         "title": "Product",
+         "type": "object"
+      },
+      "Range": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+         "properties": {
+            "axis": {
+               "description": "The name matching the axis to mask in spec",
+               "title": "Axis"
+            },
+            "min": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Min",
+               "type": "number"
+            },
+            "max": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Max",
+               "type": "number"
+            },
+            "type": {
+               "const": "Range",
+               "default": "Range",
+               "enum": [
+                  "Range"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "min",
+            "max"
+         ],
+         "title": "Range",
+         "type": "object"
+      },
+      "Rectangle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_min": {
+               "description": "Minimum inclusive x value in the region",
+               "title": "X Min",
+               "type": "number"
+            },
+            "y_min": {
+               "description": "Minimum inclusive y value in the region",
+               "title": "Y Min",
+               "type": "number"
+            },
+            "x_max": {
+               "description": "Maximum inclusive x value in the region",
+               "title": "X Max",
+               "type": "number"
+            },
+            "y_max": {
+               "description": "Maximum inclusive y value in the region",
+               "title": "Y Max",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "Clockwise rotation angle of the rectangle",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Rectangle",
+               "default": "Rectangle",
+               "enum": [
+                  "Rectangle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_min",
+            "y_min",
+            "x_max",
+            "y_max"
+         ],
+         "title": "Rectangle",
+         "type": "object"
+      },
+      "Region": {
+         "discriminator": {
+            "mapping": {
+               "Circle": "#/$defs/Circle",
+               "CombinationOf": "#/$defs/CombinationOf",
+               "DifferenceOf": "#/$defs/DifferenceOf",
+               "Ellipse": "#/$defs/Ellipse",
+               "IntersectionOf": "#/$defs/IntersectionOf",
+               "Polygon": "#/$defs/Polygon",
+               "Range": "#/$defs/Range",
+               "Rectangle": "#/$defs/Rectangle",
+               "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf",
+               "UnionOf": "#/$defs/UnionOf"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/CombinationOf"
+            },
+            {
+               "$ref": "#/$defs/UnionOf"
+            },
+            {
+               "$ref": "#/$defs/IntersectionOf"
+            },
+            {
+               "$ref": "#/$defs/DifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/SymmetricDifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/Range"
+            },
+            {
+               "$ref": "#/$defs/Rectangle"
+            },
+            {
+               "$ref": "#/$defs/Polygon"
+            },
+            {
+               "$ref": "#/$defs/Circle"
+            },
+            {
+               "$ref": "#/$defs/Ellipse"
+            }
+         ]
+      },
+      "Repeat": {
+         "additionalProperties": false,
+         "description": "Repeat an empty frame num times.\n\nCan be used on the outside of a scan to repeat the same scan many times.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = 2 * ~Line.bounded(\"x\", 3, 4, 1)\n\nIf you want snaked axes to have no gap between iterations you can do:\n\n.. example_spec::\n\n    from scanspec.specs import Line, Repeat\n\n    spec = Repeat(2, gap=False) * ~Line.bounded(\"x\", 3, 4, 1)\n\n.. note:: There is no turnaround arrow at x=4",
+         "properties": {
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "gap": {
+               "default": true,
+               "description": "If False and the slowest of the stack of Frames is snaked then the end and start of consecutive iterations of Spec will have no gap",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Repeat",
+               "default": "Repeat",
+               "enum": [
+                  "Repeat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "num"
+         ],
+         "title": "Repeat",
+         "type": "object"
+      },
+      "Snake": {
+         "additionalProperties": false,
+         "description": "Run the Spec in reverse on every other iteration when nested.\n\nTypically created with the ``~`` operator.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * ~Line(\"x\", 3, 5, 5)",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to run in reverse every other iteration"
+            },
+            "type": {
+               "const": "Snake",
+               "default": "Snake",
+               "enum": [
+                  "Snake"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Snake",
+         "type": "object"
+      },
+      "Spec": {
+         "discriminator": {
+            "mapping": {
+               "Concat": "#/$defs/Concat",
+               "Line": "#/$defs/Line",
+               "Mask": "#/$defs/Mask",
+               "Product": "#/$defs/Product",
+               "Repeat": "#/$defs/Repeat",
+               "Snake": "#/$defs/Snake",
+               "Spiral": "#/$defs/Spiral",
+               "Squash": "#/$defs/Squash",
+               "Static": "#/$defs/Static",
+               "Zip": "#/$defs/Zip"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/Product"
+            },
+            {
+               "$ref": "#/$defs/Repeat"
+            },
+            {
+               "$ref": "#/$defs/Zip"
+            },
+            {
+               "$ref": "#/$defs/Mask"
+            },
+            {
+               "$ref": "#/$defs/Snake"
+            },
+            {
+               "$ref": "#/$defs/Concat"
+            },
+            {
+               "$ref": "#/$defs/Squash"
+            },
+            {
+               "$ref": "#/$defs/Line"
+            },
+            {
+               "$ref": "#/$defs/Static"
+            },
+            {
+               "$ref": "#/$defs/Spiral"
+            }
+         ]
+      },
+      "Spiral": {
+         "additionalProperties": false,
+         "description": "Archimedean spiral of \"x_axis\" and \"y_axis\".\n\nStarts at centre point (\"x_start\", \"y_start\") with angle \"rotate\". Produces\n\"num\" points in a spiral spanning width of \"x_range\" and height of \"y_range\"\n\n.. example_spec::\n\n    from scanspec.specs import Spiral\n\n    spec = Spiral(\"x\", \"y\", 1, 5, 10, 50, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "An identifier for what to move for x",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "An identifier for what to move for y",
+               "title": "Y Axis"
+            },
+            "x_start": {
+               "description": "x centre of the spiral",
+               "title": "X Start",
+               "type": "number"
+            },
+            "y_start": {
+               "description": "y centre of the spiral",
+               "title": "Y Start",
+               "type": "number"
+            },
+            "x_range": {
+               "description": "x width of the spiral",
+               "title": "X Range",
+               "type": "number"
+            },
+            "y_range": {
+               "description": "y width of the spiral",
+               "title": "Y Range",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "rotate": {
+               "default": 0.0,
+               "description": "How much to rotate the angle of the spiral",
+               "title": "Rotate",
+               "type": "number"
+            },
+            "type": {
+               "const": "Spiral",
+               "default": "Spiral",
+               "enum": [
+                  "Spiral"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_start",
+            "y_start",
+            "x_range",
+            "y_range",
+            "num"
+         ],
+         "title": "Spiral",
+         "type": "object"
+      },
+      "Squash": {
+         "additionalProperties": false,
+         "description": "Squash a stack of Frames together into a single expanded Frames object.\n\nSee Also:\n    `why-squash-can-change-path`\n\n.. example_spec::\n\n    from scanspec.specs import Line, Squash\n\n    spec = Squash(Line(\"y\", 1, 2, 3) * Line(\"x\", 0, 1, 4))",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to squash the dimensions of"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Squash",
+               "default": "Squash",
+               "enum": [
+                  "Squash"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Squash",
+         "type": "object"
+      },
+      "Static": {
+         "additionalProperties": false,
+         "description": "A static frame, repeated num times, with axis at value.\n\nCan be used to set axis=value at every point in a scan.\n\n.. example_spec::\n\n    from scanspec.specs import Line, Static\n\n    spec = Line(\"y\", 1, 2, 3).zip(Static(\"x\", 3))",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "value": {
+               "description": "The value at each point",
+               "title": "Value",
+               "type": "number"
+            },
+            "num": {
+               "default": 1,
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Static",
+               "default": "Static",
+               "enum": [
+                  "Static"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "value"
+         ],
+         "title": "Static",
+         "type": "object"
+      },
+      "SymmetricDifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "SymmetricDifferenceOf",
+               "default": "SymmetricDifferenceOf",
+               "enum": [
+                  "SymmetricDifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "SymmetricDifferenceOf",
+         "type": "object"
+      },
+      "UnionOf": {
+         "additionalProperties": false,
+         "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "UnionOf",
+               "default": "UnionOf",
+               "enum": [
+                  "UnionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "UnionOf",
+         "type": "object"
+      },
+      "Zip": {
+         "additionalProperties": false,
+         "description": "Run two Specs in parallel, merging their midpoints together.\n\nTypically formed using `Spec.zip`.\n\nStacks of Frames are merged by:\n\n- If right creates a stack of a single Frames object of size 1, expand it to\n  the size of the fastest Frames object created by left\n- Merge individual Frames objects together from fastest to slowest\n\nThis means that Zipping a Spec producing stack [l2, l1] with a Spec\nproducing stack [r1] will assert len(l1)==len(r1), and produce\nstack [l2, l1.zip(r1)].\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"z\", 1, 2, 3) * Line(\"y\", 3, 4, 5).zip(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Zip, will appear earlier in axes"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Zip, will appear later in axes"
+            },
+            "type": {
+               "const": "Zip",
+               "default": "Zip",
+               "enum": [
+                  "Zip"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Zip",
+         "type": "object"
+      }
+   },
+   "$ref": "#/$defs/Zip"
+}
+
+
+

+
Fields:
+
+
+
+
+
+field left: Spec[Axis] [Required]#
+

The left-hand Spec to Zip, will appear earlier in axes

+
+ +
+
+field right: Spec[Axis] [Required]#
+

The right-hand Spec to Zip, will appear later in axes

+
+ +
+
+field type: Literal['Zip'] = 'Zip'#
+
+ +
+
+axes() list[Axis][source]#
+

Return the list of axes that are present in the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+calculate(bounds=True, nested=False) list[Frames[Axis]][source]#
+

Produce a stack of nested Frames that form the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+ +
+
+pydantic model scanspec.specs.Mask[source]#
+

Restrict Spec to only midpoints that fall inside the given Region.

+

Typically created with the & operator. It also pushes down the +& | ^ - operators to its Region to avoid the need for brackets on +combinations of Regions.

+

If a Region spans multiple Frames objects, they will be squashed together.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.regions import Circle
+from scanspec.specs import Line
+
+spec = Line("y", 1, 3, 3) * Line("x", 3, 5, 5) & Circle("x", "y", 4, 2, 1.2)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-5.png +
+

See Also: Why Squash (and Mask) can change the Path

+

+Show JSON schema
{
+   "$defs": {
+      "Circle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the circle",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the circle",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "radius": {
+               "description": "Radius of the circle",
+               "exclusiveMinimum": 0.0,
+               "title": "Radius",
+               "type": "number"
+            },
+            "type": {
+               "const": "Circle",
+               "default": "Circle",
+               "enum": [
+                  "Circle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "radius"
+         ],
+         "title": "Circle",
+         "type": "object"
+      },
+      "CombinationOf": {
+         "additionalProperties": false,
+         "description": "Abstract baseclass for a combination of two regions, left and right.",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "CombinationOf",
+               "default": "CombinationOf",
+               "enum": [
+                  "CombinationOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "CombinationOf",
+         "type": "object"
+      },
+      "Concat": {
+         "additionalProperties": false,
+         "description": "Concatenate two Specs together, running one after the other.\n\nEach Dimension of left and right must contain the same axes. Typically\nformed using `Spec.concat`.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 3, 3).concat(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Concat, midpoints will appear earlier"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Concat, midpoints will appear later"
+            },
+            "gap": {
+               "default": false,
+               "description": "If True, force a gap in the output at the join",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Concat",
+               "default": "Concat",
+               "enum": [
+                  "Concat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Concat",
+         "type": "object"
+      },
+      "DifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "DifferenceOf",
+               "default": "DifferenceOf",
+               "enum": [
+                  "DifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "DifferenceOf",
+         "type": "object"
+      },
+      "Ellipse": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the ellipse",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the ellipse",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "x_radius": {
+               "description": "The radius along the x axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "X Radius",
+               "type": "number"
+            },
+            "y_radius": {
+               "description": "The radius along the y axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "Y Radius",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "The angle of the ellipse (degrees)",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Ellipse",
+               "default": "Ellipse",
+               "enum": [
+                  "Ellipse"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "x_radius",
+            "y_radius"
+         ],
+         "title": "Ellipse",
+         "type": "object"
+      },
+      "IntersectionOf": {
+         "additionalProperties": false,
+         "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False,  True, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "IntersectionOf",
+               "default": "IntersectionOf",
+               "enum": [
+                  "IntersectionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "IntersectionOf",
+         "type": "object"
+      },
+      "Line": {
+         "additionalProperties": false,
+         "description": "Linearly spaced frames with start and stop as first and last midpoints.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 2, 5)",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "start": {
+               "description": "Midpoint of the first point of the line",
+               "title": "Start",
+               "type": "number"
+            },
+            "stop": {
+               "description": "Midpoint of the last point of the line",
+               "title": "Stop",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Line",
+               "default": "Line",
+               "enum": [
+                  "Line"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "start",
+            "stop",
+            "num"
+         ],
+         "title": "Line",
+         "type": "object"
+      },
+      "Mask": {
+         "additionalProperties": false,
+         "description": "Restrict Spec to only midpoints that fall inside the given Region.\n\nTypically created with the ``&`` operator. It also pushes down the\n``& | ^ -`` operators to its `Region` to avoid the need for brackets on\ncombinations of Regions.\n\nIf a Region spans multiple Frames objects, they will be squashed together.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * Line(\"x\", 3, 5, 5) & Circle(\"x\", \"y\", 4, 2, 1.2)\n\nSee Also: `why-squash-can-change-path`",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec containing the source midpoints"
+            },
+            "region": {
+               "$ref": "#/$defs/Region",
+               "description": "The Region that midpoints will be inside"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Mask",
+               "default": "Mask",
+               "enum": [
+                  "Mask"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec",
+            "region"
+         ],
+         "title": "Mask",
+         "type": "object"
+      },
+      "Polygon": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_verts": {
+               "description": "The Nx1 x coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "X Verts",
+               "type": "array"
+            },
+            "y_verts": {
+               "description": "The Nx1 y coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "Y Verts",
+               "type": "array"
+            },
+            "type": {
+               "const": "Polygon",
+               "default": "Polygon",
+               "enum": [
+                  "Polygon"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_verts",
+            "y_verts"
+         ],
+         "title": "Polygon",
+         "type": "object"
+      },
+      "Product": {
+         "additionalProperties": false,
+         "description": "Outer product of two Specs, nesting inner within outer.\n\nThis means that inner will run in its entirety at each point in outer.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 2, 3) * Line(\"x\", 3, 4, 12)",
+         "properties": {
+            "outer": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed once"
+            },
+            "inner": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed len(outer) times"
+            },
+            "type": {
+               "const": "Product",
+               "default": "Product",
+               "enum": [
+                  "Product"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "outer",
+            "inner"
+         ],
+         "title": "Product",
+         "type": "object"
+      },
+      "Range": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+         "properties": {
+            "axis": {
+               "description": "The name matching the axis to mask in spec",
+               "title": "Axis"
+            },
+            "min": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Min",
+               "type": "number"
+            },
+            "max": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Max",
+               "type": "number"
+            },
+            "type": {
+               "const": "Range",
+               "default": "Range",
+               "enum": [
+                  "Range"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "min",
+            "max"
+         ],
+         "title": "Range",
+         "type": "object"
+      },
+      "Rectangle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_min": {
+               "description": "Minimum inclusive x value in the region",
+               "title": "X Min",
+               "type": "number"
+            },
+            "y_min": {
+               "description": "Minimum inclusive y value in the region",
+               "title": "Y Min",
+               "type": "number"
+            },
+            "x_max": {
+               "description": "Maximum inclusive x value in the region",
+               "title": "X Max",
+               "type": "number"
+            },
+            "y_max": {
+               "description": "Maximum inclusive y value in the region",
+               "title": "Y Max",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "Clockwise rotation angle of the rectangle",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Rectangle",
+               "default": "Rectangle",
+               "enum": [
+                  "Rectangle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_min",
+            "y_min",
+            "x_max",
+            "y_max"
+         ],
+         "title": "Rectangle",
+         "type": "object"
+      },
+      "Region": {
+         "discriminator": {
+            "mapping": {
+               "Circle": "#/$defs/Circle",
+               "CombinationOf": "#/$defs/CombinationOf",
+               "DifferenceOf": "#/$defs/DifferenceOf",
+               "Ellipse": "#/$defs/Ellipse",
+               "IntersectionOf": "#/$defs/IntersectionOf",
+               "Polygon": "#/$defs/Polygon",
+               "Range": "#/$defs/Range",
+               "Rectangle": "#/$defs/Rectangle",
+               "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf",
+               "UnionOf": "#/$defs/UnionOf"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/CombinationOf"
+            },
+            {
+               "$ref": "#/$defs/UnionOf"
+            },
+            {
+               "$ref": "#/$defs/IntersectionOf"
+            },
+            {
+               "$ref": "#/$defs/DifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/SymmetricDifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/Range"
+            },
+            {
+               "$ref": "#/$defs/Rectangle"
+            },
+            {
+               "$ref": "#/$defs/Polygon"
+            },
+            {
+               "$ref": "#/$defs/Circle"
+            },
+            {
+               "$ref": "#/$defs/Ellipse"
+            }
+         ]
+      },
+      "Repeat": {
+         "additionalProperties": false,
+         "description": "Repeat an empty frame num times.\n\nCan be used on the outside of a scan to repeat the same scan many times.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = 2 * ~Line.bounded(\"x\", 3, 4, 1)\n\nIf you want snaked axes to have no gap between iterations you can do:\n\n.. example_spec::\n\n    from scanspec.specs import Line, Repeat\n\n    spec = Repeat(2, gap=False) * ~Line.bounded(\"x\", 3, 4, 1)\n\n.. note:: There is no turnaround arrow at x=4",
+         "properties": {
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "gap": {
+               "default": true,
+               "description": "If False and the slowest of the stack of Frames is snaked then the end and start of consecutive iterations of Spec will have no gap",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Repeat",
+               "default": "Repeat",
+               "enum": [
+                  "Repeat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "num"
+         ],
+         "title": "Repeat",
+         "type": "object"
+      },
+      "Snake": {
+         "additionalProperties": false,
+         "description": "Run the Spec in reverse on every other iteration when nested.\n\nTypically created with the ``~`` operator.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * ~Line(\"x\", 3, 5, 5)",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to run in reverse every other iteration"
+            },
+            "type": {
+               "const": "Snake",
+               "default": "Snake",
+               "enum": [
+                  "Snake"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Snake",
+         "type": "object"
+      },
+      "Spec": {
+         "discriminator": {
+            "mapping": {
+               "Concat": "#/$defs/Concat",
+               "Line": "#/$defs/Line",
+               "Mask": "#/$defs/Mask",
+               "Product": "#/$defs/Product",
+               "Repeat": "#/$defs/Repeat",
+               "Snake": "#/$defs/Snake",
+               "Spiral": "#/$defs/Spiral",
+               "Squash": "#/$defs/Squash",
+               "Static": "#/$defs/Static",
+               "Zip": "#/$defs/Zip"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/Product"
+            },
+            {
+               "$ref": "#/$defs/Repeat"
+            },
+            {
+               "$ref": "#/$defs/Zip"
+            },
+            {
+               "$ref": "#/$defs/Mask"
+            },
+            {
+               "$ref": "#/$defs/Snake"
+            },
+            {
+               "$ref": "#/$defs/Concat"
+            },
+            {
+               "$ref": "#/$defs/Squash"
+            },
+            {
+               "$ref": "#/$defs/Line"
+            },
+            {
+               "$ref": "#/$defs/Static"
+            },
+            {
+               "$ref": "#/$defs/Spiral"
+            }
+         ]
+      },
+      "Spiral": {
+         "additionalProperties": false,
+         "description": "Archimedean spiral of \"x_axis\" and \"y_axis\".\n\nStarts at centre point (\"x_start\", \"y_start\") with angle \"rotate\". Produces\n\"num\" points in a spiral spanning width of \"x_range\" and height of \"y_range\"\n\n.. example_spec::\n\n    from scanspec.specs import Spiral\n\n    spec = Spiral(\"x\", \"y\", 1, 5, 10, 50, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "An identifier for what to move for x",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "An identifier for what to move for y",
+               "title": "Y Axis"
+            },
+            "x_start": {
+               "description": "x centre of the spiral",
+               "title": "X Start",
+               "type": "number"
+            },
+            "y_start": {
+               "description": "y centre of the spiral",
+               "title": "Y Start",
+               "type": "number"
+            },
+            "x_range": {
+               "description": "x width of the spiral",
+               "title": "X Range",
+               "type": "number"
+            },
+            "y_range": {
+               "description": "y width of the spiral",
+               "title": "Y Range",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "rotate": {
+               "default": 0.0,
+               "description": "How much to rotate the angle of the spiral",
+               "title": "Rotate",
+               "type": "number"
+            },
+            "type": {
+               "const": "Spiral",
+               "default": "Spiral",
+               "enum": [
+                  "Spiral"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_start",
+            "y_start",
+            "x_range",
+            "y_range",
+            "num"
+         ],
+         "title": "Spiral",
+         "type": "object"
+      },
+      "Squash": {
+         "additionalProperties": false,
+         "description": "Squash a stack of Frames together into a single expanded Frames object.\n\nSee Also:\n    `why-squash-can-change-path`\n\n.. example_spec::\n\n    from scanspec.specs import Line, Squash\n\n    spec = Squash(Line(\"y\", 1, 2, 3) * Line(\"x\", 0, 1, 4))",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to squash the dimensions of"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Squash",
+               "default": "Squash",
+               "enum": [
+                  "Squash"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Squash",
+         "type": "object"
+      },
+      "Static": {
+         "additionalProperties": false,
+         "description": "A static frame, repeated num times, with axis at value.\n\nCan be used to set axis=value at every point in a scan.\n\n.. example_spec::\n\n    from scanspec.specs import Line, Static\n\n    spec = Line(\"y\", 1, 2, 3).zip(Static(\"x\", 3))",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "value": {
+               "description": "The value at each point",
+               "title": "Value",
+               "type": "number"
+            },
+            "num": {
+               "default": 1,
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Static",
+               "default": "Static",
+               "enum": [
+                  "Static"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "value"
+         ],
+         "title": "Static",
+         "type": "object"
+      },
+      "SymmetricDifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "SymmetricDifferenceOf",
+               "default": "SymmetricDifferenceOf",
+               "enum": [
+                  "SymmetricDifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "SymmetricDifferenceOf",
+         "type": "object"
+      },
+      "UnionOf": {
+         "additionalProperties": false,
+         "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "UnionOf",
+               "default": "UnionOf",
+               "enum": [
+                  "UnionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "UnionOf",
+         "type": "object"
+      },
+      "Zip": {
+         "additionalProperties": false,
+         "description": "Run two Specs in parallel, merging their midpoints together.\n\nTypically formed using `Spec.zip`.\n\nStacks of Frames are merged by:\n\n- If right creates a stack of a single Frames object of size 1, expand it to\n  the size of the fastest Frames object created by left\n- Merge individual Frames objects together from fastest to slowest\n\nThis means that Zipping a Spec producing stack [l2, l1] with a Spec\nproducing stack [r1] will assert len(l1)==len(r1), and produce\nstack [l2, l1.zip(r1)].\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"z\", 1, 2, 3) * Line(\"y\", 3, 4, 5).zip(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Zip, will appear earlier in axes"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Zip, will appear later in axes"
+            },
+            "type": {
+               "const": "Zip",
+               "default": "Zip",
+               "enum": [
+                  "Zip"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Zip",
+         "type": "object"
+      }
+   },
+   "$ref": "#/$defs/Mask"
+}
+
+
+

+
Fields:
+
+
+
+
+
+field check_path_changes: bool = True#
+

If True path through scan will not be modified by squash

+
+ +
+
+field region: Region[Axis] [Required]#
+

The Region that midpoints will be inside

+
+ +
+
+field spec: Spec[Axis] [Required]#
+

The Spec containing the source midpoints

+
+ +
+
+field type: Literal['Mask'] = 'Mask'#
+
+ +
+
+axes() list[Axis][source]#
+

Return the list of axes that are present in the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+calculate(bounds=True, nested=False) list[Frames[Axis]][source]#
+

Produce a stack of nested Frames that form the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+ +
+
+pydantic model scanspec.specs.Snake[source]#
+

Run the Spec in reverse on every other iteration when nested.

+

Typically created with the ~ operator.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line
+
+spec = Line("y", 1, 3, 3) * ~Line("x", 3, 5, 5)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-6.png +
+

+Show JSON schema
{
+   "$defs": {
+      "Circle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the circle",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the circle",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "radius": {
+               "description": "Radius of the circle",
+               "exclusiveMinimum": 0.0,
+               "title": "Radius",
+               "type": "number"
+            },
+            "type": {
+               "const": "Circle",
+               "default": "Circle",
+               "enum": [
+                  "Circle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "radius"
+         ],
+         "title": "Circle",
+         "type": "object"
+      },
+      "CombinationOf": {
+         "additionalProperties": false,
+         "description": "Abstract baseclass for a combination of two regions, left and right.",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "CombinationOf",
+               "default": "CombinationOf",
+               "enum": [
+                  "CombinationOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "CombinationOf",
+         "type": "object"
+      },
+      "Concat": {
+         "additionalProperties": false,
+         "description": "Concatenate two Specs together, running one after the other.\n\nEach Dimension of left and right must contain the same axes. Typically\nformed using `Spec.concat`.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 3, 3).concat(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Concat, midpoints will appear earlier"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Concat, midpoints will appear later"
+            },
+            "gap": {
+               "default": false,
+               "description": "If True, force a gap in the output at the join",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Concat",
+               "default": "Concat",
+               "enum": [
+                  "Concat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Concat",
+         "type": "object"
+      },
+      "DifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "DifferenceOf",
+               "default": "DifferenceOf",
+               "enum": [
+                  "DifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "DifferenceOf",
+         "type": "object"
+      },
+      "Ellipse": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the ellipse",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the ellipse",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "x_radius": {
+               "description": "The radius along the x axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "X Radius",
+               "type": "number"
+            },
+            "y_radius": {
+               "description": "The radius along the y axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "Y Radius",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "The angle of the ellipse (degrees)",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Ellipse",
+               "default": "Ellipse",
+               "enum": [
+                  "Ellipse"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "x_radius",
+            "y_radius"
+         ],
+         "title": "Ellipse",
+         "type": "object"
+      },
+      "IntersectionOf": {
+         "additionalProperties": false,
+         "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False,  True, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "IntersectionOf",
+               "default": "IntersectionOf",
+               "enum": [
+                  "IntersectionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "IntersectionOf",
+         "type": "object"
+      },
+      "Line": {
+         "additionalProperties": false,
+         "description": "Linearly spaced frames with start and stop as first and last midpoints.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 2, 5)",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "start": {
+               "description": "Midpoint of the first point of the line",
+               "title": "Start",
+               "type": "number"
+            },
+            "stop": {
+               "description": "Midpoint of the last point of the line",
+               "title": "Stop",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Line",
+               "default": "Line",
+               "enum": [
+                  "Line"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "start",
+            "stop",
+            "num"
+         ],
+         "title": "Line",
+         "type": "object"
+      },
+      "Mask": {
+         "additionalProperties": false,
+         "description": "Restrict Spec to only midpoints that fall inside the given Region.\n\nTypically created with the ``&`` operator. It also pushes down the\n``& | ^ -`` operators to its `Region` to avoid the need for brackets on\ncombinations of Regions.\n\nIf a Region spans multiple Frames objects, they will be squashed together.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * Line(\"x\", 3, 5, 5) & Circle(\"x\", \"y\", 4, 2, 1.2)\n\nSee Also: `why-squash-can-change-path`",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec containing the source midpoints"
+            },
+            "region": {
+               "$ref": "#/$defs/Region",
+               "description": "The Region that midpoints will be inside"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Mask",
+               "default": "Mask",
+               "enum": [
+                  "Mask"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec",
+            "region"
+         ],
+         "title": "Mask",
+         "type": "object"
+      },
+      "Polygon": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_verts": {
+               "description": "The Nx1 x coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "X Verts",
+               "type": "array"
+            },
+            "y_verts": {
+               "description": "The Nx1 y coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "Y Verts",
+               "type": "array"
+            },
+            "type": {
+               "const": "Polygon",
+               "default": "Polygon",
+               "enum": [
+                  "Polygon"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_verts",
+            "y_verts"
+         ],
+         "title": "Polygon",
+         "type": "object"
+      },
+      "Product": {
+         "additionalProperties": false,
+         "description": "Outer product of two Specs, nesting inner within outer.\n\nThis means that inner will run in its entirety at each point in outer.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 2, 3) * Line(\"x\", 3, 4, 12)",
+         "properties": {
+            "outer": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed once"
+            },
+            "inner": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed len(outer) times"
+            },
+            "type": {
+               "const": "Product",
+               "default": "Product",
+               "enum": [
+                  "Product"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "outer",
+            "inner"
+         ],
+         "title": "Product",
+         "type": "object"
+      },
+      "Range": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+         "properties": {
+            "axis": {
+               "description": "The name matching the axis to mask in spec",
+               "title": "Axis"
+            },
+            "min": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Min",
+               "type": "number"
+            },
+            "max": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Max",
+               "type": "number"
+            },
+            "type": {
+               "const": "Range",
+               "default": "Range",
+               "enum": [
+                  "Range"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "min",
+            "max"
+         ],
+         "title": "Range",
+         "type": "object"
+      },
+      "Rectangle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_min": {
+               "description": "Minimum inclusive x value in the region",
+               "title": "X Min",
+               "type": "number"
+            },
+            "y_min": {
+               "description": "Minimum inclusive y value in the region",
+               "title": "Y Min",
+               "type": "number"
+            },
+            "x_max": {
+               "description": "Maximum inclusive x value in the region",
+               "title": "X Max",
+               "type": "number"
+            },
+            "y_max": {
+               "description": "Maximum inclusive y value in the region",
+               "title": "Y Max",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "Clockwise rotation angle of the rectangle",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Rectangle",
+               "default": "Rectangle",
+               "enum": [
+                  "Rectangle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_min",
+            "y_min",
+            "x_max",
+            "y_max"
+         ],
+         "title": "Rectangle",
+         "type": "object"
+      },
+      "Region": {
+         "discriminator": {
+            "mapping": {
+               "Circle": "#/$defs/Circle",
+               "CombinationOf": "#/$defs/CombinationOf",
+               "DifferenceOf": "#/$defs/DifferenceOf",
+               "Ellipse": "#/$defs/Ellipse",
+               "IntersectionOf": "#/$defs/IntersectionOf",
+               "Polygon": "#/$defs/Polygon",
+               "Range": "#/$defs/Range",
+               "Rectangle": "#/$defs/Rectangle",
+               "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf",
+               "UnionOf": "#/$defs/UnionOf"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/CombinationOf"
+            },
+            {
+               "$ref": "#/$defs/UnionOf"
+            },
+            {
+               "$ref": "#/$defs/IntersectionOf"
+            },
+            {
+               "$ref": "#/$defs/DifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/SymmetricDifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/Range"
+            },
+            {
+               "$ref": "#/$defs/Rectangle"
+            },
+            {
+               "$ref": "#/$defs/Polygon"
+            },
+            {
+               "$ref": "#/$defs/Circle"
+            },
+            {
+               "$ref": "#/$defs/Ellipse"
+            }
+         ]
+      },
+      "Repeat": {
+         "additionalProperties": false,
+         "description": "Repeat an empty frame num times.\n\nCan be used on the outside of a scan to repeat the same scan many times.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = 2 * ~Line.bounded(\"x\", 3, 4, 1)\n\nIf you want snaked axes to have no gap between iterations you can do:\n\n.. example_spec::\n\n    from scanspec.specs import Line, Repeat\n\n    spec = Repeat(2, gap=False) * ~Line.bounded(\"x\", 3, 4, 1)\n\n.. note:: There is no turnaround arrow at x=4",
+         "properties": {
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "gap": {
+               "default": true,
+               "description": "If False and the slowest of the stack of Frames is snaked then the end and start of consecutive iterations of Spec will have no gap",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Repeat",
+               "default": "Repeat",
+               "enum": [
+                  "Repeat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "num"
+         ],
+         "title": "Repeat",
+         "type": "object"
+      },
+      "Snake": {
+         "additionalProperties": false,
+         "description": "Run the Spec in reverse on every other iteration when nested.\n\nTypically created with the ``~`` operator.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * ~Line(\"x\", 3, 5, 5)",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to run in reverse every other iteration"
+            },
+            "type": {
+               "const": "Snake",
+               "default": "Snake",
+               "enum": [
+                  "Snake"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Snake",
+         "type": "object"
+      },
+      "Spec": {
+         "discriminator": {
+            "mapping": {
+               "Concat": "#/$defs/Concat",
+               "Line": "#/$defs/Line",
+               "Mask": "#/$defs/Mask",
+               "Product": "#/$defs/Product",
+               "Repeat": "#/$defs/Repeat",
+               "Snake": "#/$defs/Snake",
+               "Spiral": "#/$defs/Spiral",
+               "Squash": "#/$defs/Squash",
+               "Static": "#/$defs/Static",
+               "Zip": "#/$defs/Zip"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/Product"
+            },
+            {
+               "$ref": "#/$defs/Repeat"
+            },
+            {
+               "$ref": "#/$defs/Zip"
+            },
+            {
+               "$ref": "#/$defs/Mask"
+            },
+            {
+               "$ref": "#/$defs/Snake"
+            },
+            {
+               "$ref": "#/$defs/Concat"
+            },
+            {
+               "$ref": "#/$defs/Squash"
+            },
+            {
+               "$ref": "#/$defs/Line"
+            },
+            {
+               "$ref": "#/$defs/Static"
+            },
+            {
+               "$ref": "#/$defs/Spiral"
+            }
+         ]
+      },
+      "Spiral": {
+         "additionalProperties": false,
+         "description": "Archimedean spiral of \"x_axis\" and \"y_axis\".\n\nStarts at centre point (\"x_start\", \"y_start\") with angle \"rotate\". Produces\n\"num\" points in a spiral spanning width of \"x_range\" and height of \"y_range\"\n\n.. example_spec::\n\n    from scanspec.specs import Spiral\n\n    spec = Spiral(\"x\", \"y\", 1, 5, 10, 50, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "An identifier for what to move for x",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "An identifier for what to move for y",
+               "title": "Y Axis"
+            },
+            "x_start": {
+               "description": "x centre of the spiral",
+               "title": "X Start",
+               "type": "number"
+            },
+            "y_start": {
+               "description": "y centre of the spiral",
+               "title": "Y Start",
+               "type": "number"
+            },
+            "x_range": {
+               "description": "x width of the spiral",
+               "title": "X Range",
+               "type": "number"
+            },
+            "y_range": {
+               "description": "y width of the spiral",
+               "title": "Y Range",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "rotate": {
+               "default": 0.0,
+               "description": "How much to rotate the angle of the spiral",
+               "title": "Rotate",
+               "type": "number"
+            },
+            "type": {
+               "const": "Spiral",
+               "default": "Spiral",
+               "enum": [
+                  "Spiral"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_start",
+            "y_start",
+            "x_range",
+            "y_range",
+            "num"
+         ],
+         "title": "Spiral",
+         "type": "object"
+      },
+      "Squash": {
+         "additionalProperties": false,
+         "description": "Squash a stack of Frames together into a single expanded Frames object.\n\nSee Also:\n    `why-squash-can-change-path`\n\n.. example_spec::\n\n    from scanspec.specs import Line, Squash\n\n    spec = Squash(Line(\"y\", 1, 2, 3) * Line(\"x\", 0, 1, 4))",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to squash the dimensions of"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Squash",
+               "default": "Squash",
+               "enum": [
+                  "Squash"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Squash",
+         "type": "object"
+      },
+      "Static": {
+         "additionalProperties": false,
+         "description": "A static frame, repeated num times, with axis at value.\n\nCan be used to set axis=value at every point in a scan.\n\n.. example_spec::\n\n    from scanspec.specs import Line, Static\n\n    spec = Line(\"y\", 1, 2, 3).zip(Static(\"x\", 3))",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "value": {
+               "description": "The value at each point",
+               "title": "Value",
+               "type": "number"
+            },
+            "num": {
+               "default": 1,
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Static",
+               "default": "Static",
+               "enum": [
+                  "Static"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "value"
+         ],
+         "title": "Static",
+         "type": "object"
+      },
+      "SymmetricDifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "SymmetricDifferenceOf",
+               "default": "SymmetricDifferenceOf",
+               "enum": [
+                  "SymmetricDifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "SymmetricDifferenceOf",
+         "type": "object"
+      },
+      "UnionOf": {
+         "additionalProperties": false,
+         "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "UnionOf",
+               "default": "UnionOf",
+               "enum": [
+                  "UnionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "UnionOf",
+         "type": "object"
+      },
+      "Zip": {
+         "additionalProperties": false,
+         "description": "Run two Specs in parallel, merging their midpoints together.\n\nTypically formed using `Spec.zip`.\n\nStacks of Frames are merged by:\n\n- If right creates a stack of a single Frames object of size 1, expand it to\n  the size of the fastest Frames object created by left\n- Merge individual Frames objects together from fastest to slowest\n\nThis means that Zipping a Spec producing stack [l2, l1] with a Spec\nproducing stack [r1] will assert len(l1)==len(r1), and produce\nstack [l2, l1.zip(r1)].\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"z\", 1, 2, 3) * Line(\"y\", 3, 4, 5).zip(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Zip, will appear earlier in axes"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Zip, will appear later in axes"
+            },
+            "type": {
+               "const": "Zip",
+               "default": "Zip",
+               "enum": [
+                  "Zip"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Zip",
+         "type": "object"
+      }
+   },
+   "$ref": "#/$defs/Snake"
+}
+
+
+

+
Fields:
+
+
+
+
+
+field spec: Spec[Axis] [Required]#
+

The Spec to run in reverse every other iteration

+
+ +
+
+field type: Literal['Snake'] = 'Snake'#
+
+ +
+
+axes() list[Axis][source]#
+

Return the list of axes that are present in the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+calculate(bounds=True, nested=False) list[Frames[Axis]][source]#
+

Produce a stack of nested Frames that form the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+ +
+
+pydantic model scanspec.specs.Concat[source]#
+

Concatenate two Specs together, running one after the other.

+

Each Dimension of left and right must contain the same axes. Typically +formed using Spec.concat.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line
+
+spec = Line("x", 1, 3, 3).concat(Line("x", 4, 5, 5))
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-7.png +
+

+Show JSON schema
{
+   "$defs": {
+      "Circle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the circle",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the circle",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "radius": {
+               "description": "Radius of the circle",
+               "exclusiveMinimum": 0.0,
+               "title": "Radius",
+               "type": "number"
+            },
+            "type": {
+               "const": "Circle",
+               "default": "Circle",
+               "enum": [
+                  "Circle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "radius"
+         ],
+         "title": "Circle",
+         "type": "object"
+      },
+      "CombinationOf": {
+         "additionalProperties": false,
+         "description": "Abstract baseclass for a combination of two regions, left and right.",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "CombinationOf",
+               "default": "CombinationOf",
+               "enum": [
+                  "CombinationOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "CombinationOf",
+         "type": "object"
+      },
+      "Concat": {
+         "additionalProperties": false,
+         "description": "Concatenate two Specs together, running one after the other.\n\nEach Dimension of left and right must contain the same axes. Typically\nformed using `Spec.concat`.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 3, 3).concat(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Concat, midpoints will appear earlier"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Concat, midpoints will appear later"
+            },
+            "gap": {
+               "default": false,
+               "description": "If True, force a gap in the output at the join",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Concat",
+               "default": "Concat",
+               "enum": [
+                  "Concat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Concat",
+         "type": "object"
+      },
+      "DifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "DifferenceOf",
+               "default": "DifferenceOf",
+               "enum": [
+                  "DifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "DifferenceOf",
+         "type": "object"
+      },
+      "Ellipse": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the ellipse",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the ellipse",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "x_radius": {
+               "description": "The radius along the x axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "X Radius",
+               "type": "number"
+            },
+            "y_radius": {
+               "description": "The radius along the y axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "Y Radius",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "The angle of the ellipse (degrees)",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Ellipse",
+               "default": "Ellipse",
+               "enum": [
+                  "Ellipse"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "x_radius",
+            "y_radius"
+         ],
+         "title": "Ellipse",
+         "type": "object"
+      },
+      "IntersectionOf": {
+         "additionalProperties": false,
+         "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False,  True, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "IntersectionOf",
+               "default": "IntersectionOf",
+               "enum": [
+                  "IntersectionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "IntersectionOf",
+         "type": "object"
+      },
+      "Line": {
+         "additionalProperties": false,
+         "description": "Linearly spaced frames with start and stop as first and last midpoints.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 2, 5)",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "start": {
+               "description": "Midpoint of the first point of the line",
+               "title": "Start",
+               "type": "number"
+            },
+            "stop": {
+               "description": "Midpoint of the last point of the line",
+               "title": "Stop",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Line",
+               "default": "Line",
+               "enum": [
+                  "Line"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "start",
+            "stop",
+            "num"
+         ],
+         "title": "Line",
+         "type": "object"
+      },
+      "Mask": {
+         "additionalProperties": false,
+         "description": "Restrict Spec to only midpoints that fall inside the given Region.\n\nTypically created with the ``&`` operator. It also pushes down the\n``& | ^ -`` operators to its `Region` to avoid the need for brackets on\ncombinations of Regions.\n\nIf a Region spans multiple Frames objects, they will be squashed together.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * Line(\"x\", 3, 5, 5) & Circle(\"x\", \"y\", 4, 2, 1.2)\n\nSee Also: `why-squash-can-change-path`",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec containing the source midpoints"
+            },
+            "region": {
+               "$ref": "#/$defs/Region",
+               "description": "The Region that midpoints will be inside"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Mask",
+               "default": "Mask",
+               "enum": [
+                  "Mask"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec",
+            "region"
+         ],
+         "title": "Mask",
+         "type": "object"
+      },
+      "Polygon": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_verts": {
+               "description": "The Nx1 x coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "X Verts",
+               "type": "array"
+            },
+            "y_verts": {
+               "description": "The Nx1 y coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "Y Verts",
+               "type": "array"
+            },
+            "type": {
+               "const": "Polygon",
+               "default": "Polygon",
+               "enum": [
+                  "Polygon"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_verts",
+            "y_verts"
+         ],
+         "title": "Polygon",
+         "type": "object"
+      },
+      "Product": {
+         "additionalProperties": false,
+         "description": "Outer product of two Specs, nesting inner within outer.\n\nThis means that inner will run in its entirety at each point in outer.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 2, 3) * Line(\"x\", 3, 4, 12)",
+         "properties": {
+            "outer": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed once"
+            },
+            "inner": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed len(outer) times"
+            },
+            "type": {
+               "const": "Product",
+               "default": "Product",
+               "enum": [
+                  "Product"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "outer",
+            "inner"
+         ],
+         "title": "Product",
+         "type": "object"
+      },
+      "Range": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+         "properties": {
+            "axis": {
+               "description": "The name matching the axis to mask in spec",
+               "title": "Axis"
+            },
+            "min": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Min",
+               "type": "number"
+            },
+            "max": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Max",
+               "type": "number"
+            },
+            "type": {
+               "const": "Range",
+               "default": "Range",
+               "enum": [
+                  "Range"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "min",
+            "max"
+         ],
+         "title": "Range",
+         "type": "object"
+      },
+      "Rectangle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_min": {
+               "description": "Minimum inclusive x value in the region",
+               "title": "X Min",
+               "type": "number"
+            },
+            "y_min": {
+               "description": "Minimum inclusive y value in the region",
+               "title": "Y Min",
+               "type": "number"
+            },
+            "x_max": {
+               "description": "Maximum inclusive x value in the region",
+               "title": "X Max",
+               "type": "number"
+            },
+            "y_max": {
+               "description": "Maximum inclusive y value in the region",
+               "title": "Y Max",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "Clockwise rotation angle of the rectangle",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Rectangle",
+               "default": "Rectangle",
+               "enum": [
+                  "Rectangle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_min",
+            "y_min",
+            "x_max",
+            "y_max"
+         ],
+         "title": "Rectangle",
+         "type": "object"
+      },
+      "Region": {
+         "discriminator": {
+            "mapping": {
+               "Circle": "#/$defs/Circle",
+               "CombinationOf": "#/$defs/CombinationOf",
+               "DifferenceOf": "#/$defs/DifferenceOf",
+               "Ellipse": "#/$defs/Ellipse",
+               "IntersectionOf": "#/$defs/IntersectionOf",
+               "Polygon": "#/$defs/Polygon",
+               "Range": "#/$defs/Range",
+               "Rectangle": "#/$defs/Rectangle",
+               "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf",
+               "UnionOf": "#/$defs/UnionOf"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/CombinationOf"
+            },
+            {
+               "$ref": "#/$defs/UnionOf"
+            },
+            {
+               "$ref": "#/$defs/IntersectionOf"
+            },
+            {
+               "$ref": "#/$defs/DifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/SymmetricDifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/Range"
+            },
+            {
+               "$ref": "#/$defs/Rectangle"
+            },
+            {
+               "$ref": "#/$defs/Polygon"
+            },
+            {
+               "$ref": "#/$defs/Circle"
+            },
+            {
+               "$ref": "#/$defs/Ellipse"
+            }
+         ]
+      },
+      "Repeat": {
+         "additionalProperties": false,
+         "description": "Repeat an empty frame num times.\n\nCan be used on the outside of a scan to repeat the same scan many times.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = 2 * ~Line.bounded(\"x\", 3, 4, 1)\n\nIf you want snaked axes to have no gap between iterations you can do:\n\n.. example_spec::\n\n    from scanspec.specs import Line, Repeat\n\n    spec = Repeat(2, gap=False) * ~Line.bounded(\"x\", 3, 4, 1)\n\n.. note:: There is no turnaround arrow at x=4",
+         "properties": {
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "gap": {
+               "default": true,
+               "description": "If False and the slowest of the stack of Frames is snaked then the end and start of consecutive iterations of Spec will have no gap",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Repeat",
+               "default": "Repeat",
+               "enum": [
+                  "Repeat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "num"
+         ],
+         "title": "Repeat",
+         "type": "object"
+      },
+      "Snake": {
+         "additionalProperties": false,
+         "description": "Run the Spec in reverse on every other iteration when nested.\n\nTypically created with the ``~`` operator.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * ~Line(\"x\", 3, 5, 5)",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to run in reverse every other iteration"
+            },
+            "type": {
+               "const": "Snake",
+               "default": "Snake",
+               "enum": [
+                  "Snake"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Snake",
+         "type": "object"
+      },
+      "Spec": {
+         "discriminator": {
+            "mapping": {
+               "Concat": "#/$defs/Concat",
+               "Line": "#/$defs/Line",
+               "Mask": "#/$defs/Mask",
+               "Product": "#/$defs/Product",
+               "Repeat": "#/$defs/Repeat",
+               "Snake": "#/$defs/Snake",
+               "Spiral": "#/$defs/Spiral",
+               "Squash": "#/$defs/Squash",
+               "Static": "#/$defs/Static",
+               "Zip": "#/$defs/Zip"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/Product"
+            },
+            {
+               "$ref": "#/$defs/Repeat"
+            },
+            {
+               "$ref": "#/$defs/Zip"
+            },
+            {
+               "$ref": "#/$defs/Mask"
+            },
+            {
+               "$ref": "#/$defs/Snake"
+            },
+            {
+               "$ref": "#/$defs/Concat"
+            },
+            {
+               "$ref": "#/$defs/Squash"
+            },
+            {
+               "$ref": "#/$defs/Line"
+            },
+            {
+               "$ref": "#/$defs/Static"
+            },
+            {
+               "$ref": "#/$defs/Spiral"
+            }
+         ]
+      },
+      "Spiral": {
+         "additionalProperties": false,
+         "description": "Archimedean spiral of \"x_axis\" and \"y_axis\".\n\nStarts at centre point (\"x_start\", \"y_start\") with angle \"rotate\". Produces\n\"num\" points in a spiral spanning width of \"x_range\" and height of \"y_range\"\n\n.. example_spec::\n\n    from scanspec.specs import Spiral\n\n    spec = Spiral(\"x\", \"y\", 1, 5, 10, 50, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "An identifier for what to move for x",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "An identifier for what to move for y",
+               "title": "Y Axis"
+            },
+            "x_start": {
+               "description": "x centre of the spiral",
+               "title": "X Start",
+               "type": "number"
+            },
+            "y_start": {
+               "description": "y centre of the spiral",
+               "title": "Y Start",
+               "type": "number"
+            },
+            "x_range": {
+               "description": "x width of the spiral",
+               "title": "X Range",
+               "type": "number"
+            },
+            "y_range": {
+               "description": "y width of the spiral",
+               "title": "Y Range",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "rotate": {
+               "default": 0.0,
+               "description": "How much to rotate the angle of the spiral",
+               "title": "Rotate",
+               "type": "number"
+            },
+            "type": {
+               "const": "Spiral",
+               "default": "Spiral",
+               "enum": [
+                  "Spiral"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_start",
+            "y_start",
+            "x_range",
+            "y_range",
+            "num"
+         ],
+         "title": "Spiral",
+         "type": "object"
+      },
+      "Squash": {
+         "additionalProperties": false,
+         "description": "Squash a stack of Frames together into a single expanded Frames object.\n\nSee Also:\n    `why-squash-can-change-path`\n\n.. example_spec::\n\n    from scanspec.specs import Line, Squash\n\n    spec = Squash(Line(\"y\", 1, 2, 3) * Line(\"x\", 0, 1, 4))",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to squash the dimensions of"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Squash",
+               "default": "Squash",
+               "enum": [
+                  "Squash"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Squash",
+         "type": "object"
+      },
+      "Static": {
+         "additionalProperties": false,
+         "description": "A static frame, repeated num times, with axis at value.\n\nCan be used to set axis=value at every point in a scan.\n\n.. example_spec::\n\n    from scanspec.specs import Line, Static\n\n    spec = Line(\"y\", 1, 2, 3).zip(Static(\"x\", 3))",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "value": {
+               "description": "The value at each point",
+               "title": "Value",
+               "type": "number"
+            },
+            "num": {
+               "default": 1,
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Static",
+               "default": "Static",
+               "enum": [
+                  "Static"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "value"
+         ],
+         "title": "Static",
+         "type": "object"
+      },
+      "SymmetricDifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "SymmetricDifferenceOf",
+               "default": "SymmetricDifferenceOf",
+               "enum": [
+                  "SymmetricDifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "SymmetricDifferenceOf",
+         "type": "object"
+      },
+      "UnionOf": {
+         "additionalProperties": false,
+         "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "UnionOf",
+               "default": "UnionOf",
+               "enum": [
+                  "UnionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "UnionOf",
+         "type": "object"
+      },
+      "Zip": {
+         "additionalProperties": false,
+         "description": "Run two Specs in parallel, merging their midpoints together.\n\nTypically formed using `Spec.zip`.\n\nStacks of Frames are merged by:\n\n- If right creates a stack of a single Frames object of size 1, expand it to\n  the size of the fastest Frames object created by left\n- Merge individual Frames objects together from fastest to slowest\n\nThis means that Zipping a Spec producing stack [l2, l1] with a Spec\nproducing stack [r1] will assert len(l1)==len(r1), and produce\nstack [l2, l1.zip(r1)].\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"z\", 1, 2, 3) * Line(\"y\", 3, 4, 5).zip(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Zip, will appear earlier in axes"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Zip, will appear later in axes"
+            },
+            "type": {
+               "const": "Zip",
+               "default": "Zip",
+               "enum": [
+                  "Zip"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Zip",
+         "type": "object"
+      }
+   },
+   "$ref": "#/$defs/Concat"
+}
+
+
+

+
Fields:
+
+
+
+
+
+field check_path_changes: bool = True#
+

If True path through scan will not be modified by squash

+
+ +
+
+field gap: bool = False#
+

If True, force a gap in the output at the join

+
+ +
+
+field left: Spec[Axis] [Required]#
+

The left-hand Spec to Concat, midpoints will appear earlier

+
+ +
+
+field right: Spec[Axis] [Required]#
+

The right-hand Spec to Concat, midpoints will appear later

+
+ +
+
+field type: Literal['Concat'] = 'Concat'#
+
+ +
+
+axes() list[Axis][source]#
+

Return the list of axes that are present in the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+calculate(bounds=True, nested=False) list[Frames[Axis]][source]#
+

Produce a stack of nested Frames that form the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+ +
+
+pydantic model scanspec.specs.Squash[source]#
+

Squash a stack of Frames together into a single expanded Frames object.

+ +
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line, Squash
+
+spec = Squash(Line("y", 1, 2, 3) * Line("x", 0, 1, 4))
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-8.png +
+

+Show JSON schema
{
+   "$defs": {
+      "Circle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the circle",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the circle",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "radius": {
+               "description": "Radius of the circle",
+               "exclusiveMinimum": 0.0,
+               "title": "Radius",
+               "type": "number"
+            },
+            "type": {
+               "const": "Circle",
+               "default": "Circle",
+               "enum": [
+                  "Circle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "radius"
+         ],
+         "title": "Circle",
+         "type": "object"
+      },
+      "CombinationOf": {
+         "additionalProperties": false,
+         "description": "Abstract baseclass for a combination of two regions, left and right.",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "CombinationOf",
+               "default": "CombinationOf",
+               "enum": [
+                  "CombinationOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "CombinationOf",
+         "type": "object"
+      },
+      "Concat": {
+         "additionalProperties": false,
+         "description": "Concatenate two Specs together, running one after the other.\n\nEach Dimension of left and right must contain the same axes. Typically\nformed using `Spec.concat`.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 3, 3).concat(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Concat, midpoints will appear earlier"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Concat, midpoints will appear later"
+            },
+            "gap": {
+               "default": false,
+               "description": "If True, force a gap in the output at the join",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Concat",
+               "default": "Concat",
+               "enum": [
+                  "Concat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Concat",
+         "type": "object"
+      },
+      "DifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "DifferenceOf",
+               "default": "DifferenceOf",
+               "enum": [
+                  "DifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "DifferenceOf",
+         "type": "object"
+      },
+      "Ellipse": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n    from scanspec.regions import Ellipse\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_middle": {
+               "description": "The central x point of the ellipse",
+               "title": "X Middle",
+               "type": "number"
+            },
+            "y_middle": {
+               "description": "The central y point of the ellipse",
+               "title": "Y Middle",
+               "type": "number"
+            },
+            "x_radius": {
+               "description": "The radius along the x axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "X Radius",
+               "type": "number"
+            },
+            "y_radius": {
+               "description": "The radius along the y axis of the ellipse",
+               "exclusiveMinimum": 0.0,
+               "title": "Y Radius",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "The angle of the ellipse (degrees)",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Ellipse",
+               "default": "Ellipse",
+               "enum": [
+                  "Ellipse"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_middle",
+            "y_middle",
+            "x_radius",
+            "y_radius"
+         ],
+         "title": "Ellipse",
+         "type": "object"
+      },
+      "IntersectionOf": {
+         "additionalProperties": false,
+         "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False,  True, False, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "IntersectionOf",
+               "default": "IntersectionOf",
+               "enum": [
+                  "IntersectionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "IntersectionOf",
+         "type": "object"
+      },
+      "Line": {
+         "additionalProperties": false,
+         "description": "Linearly spaced frames with start and stop as first and last midpoints.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 2, 5)",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "start": {
+               "description": "Midpoint of the first point of the line",
+               "title": "Start",
+               "type": "number"
+            },
+            "stop": {
+               "description": "Midpoint of the last point of the line",
+               "title": "Stop",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Line",
+               "default": "Line",
+               "enum": [
+                  "Line"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "start",
+            "stop",
+            "num"
+         ],
+         "title": "Line",
+         "type": "object"
+      },
+      "Mask": {
+         "additionalProperties": false,
+         "description": "Restrict Spec to only midpoints that fall inside the given Region.\n\nTypically created with the ``&`` operator. It also pushes down the\n``& | ^ -`` operators to its `Region` to avoid the need for brackets on\ncombinations of Regions.\n\nIf a Region spans multiple Frames objects, they will be squashed together.\n\n.. example_spec::\n\n    from scanspec.regions import Circle\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * Line(\"x\", 3, 5, 5) & Circle(\"x\", \"y\", 4, 2, 1.2)\n\nSee Also: `why-squash-can-change-path`",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec containing the source midpoints"
+            },
+            "region": {
+               "$ref": "#/$defs/Region",
+               "description": "The Region that midpoints will be inside"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Mask",
+               "default": "Mask",
+               "enum": [
+                  "Mask"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec",
+            "region"
+         ],
+         "title": "Mask",
+         "type": "object"
+      },
+      "Polygon": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n    from scanspec.regions import Polygon\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n    spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_verts": {
+               "description": "The Nx1 x coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "X Verts",
+               "type": "array"
+            },
+            "y_verts": {
+               "description": "The Nx1 y coordinates of the polygons vertices",
+               "items": {
+                  "type": "number"
+               },
+               "minItems": 3,
+               "title": "Y Verts",
+               "type": "array"
+            },
+            "type": {
+               "const": "Polygon",
+               "default": "Polygon",
+               "enum": [
+                  "Polygon"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_verts",
+            "y_verts"
+         ],
+         "title": "Polygon",
+         "type": "object"
+      },
+      "Product": {
+         "additionalProperties": false,
+         "description": "Outer product of two Specs, nesting inner within outer.\n\nThis means that inner will run in its entirety at each point in outer.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 2, 3) * Line(\"x\", 3, 4, 12)",
+         "properties": {
+            "outer": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed once"
+            },
+            "inner": {
+               "$ref": "#/$defs/Spec",
+               "description": "Will be executed len(outer) times"
+            },
+            "type": {
+               "const": "Product",
+               "default": "Product",
+               "enum": [
+                  "Product"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "outer",
+            "inner"
+         ],
+         "title": "Product",
+         "type": "object"
+      },
+      "Range": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True, False, False])",
+         "properties": {
+            "axis": {
+               "description": "The name matching the axis to mask in spec",
+               "title": "Axis"
+            },
+            "min": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Min",
+               "type": "number"
+            },
+            "max": {
+               "description": "The minimum inclusive value in the region",
+               "title": "Max",
+               "type": "number"
+            },
+            "type": {
+               "const": "Range",
+               "default": "Range",
+               "enum": [
+                  "Range"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "min",
+            "max"
+         ],
+         "title": "Range",
+         "type": "object"
+      },
+      "Rectangle": {
+         "additionalProperties": false,
+         "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n    from scanspec.regions import Rectangle\n    from scanspec.specs import Line\n\n    grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n    spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "The name matching the x axis of the spec",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "The name matching the y axis of the spec",
+               "title": "Y Axis"
+            },
+            "x_min": {
+               "description": "Minimum inclusive x value in the region",
+               "title": "X Min",
+               "type": "number"
+            },
+            "y_min": {
+               "description": "Minimum inclusive y value in the region",
+               "title": "Y Min",
+               "type": "number"
+            },
+            "x_max": {
+               "description": "Maximum inclusive x value in the region",
+               "title": "X Max",
+               "type": "number"
+            },
+            "y_max": {
+               "description": "Maximum inclusive y value in the region",
+               "title": "Y Max",
+               "type": "number"
+            },
+            "angle": {
+               "default": 0.0,
+               "description": "Clockwise rotation angle of the rectangle",
+               "title": "Angle",
+               "type": "number"
+            },
+            "type": {
+               "const": "Rectangle",
+               "default": "Rectangle",
+               "enum": [
+                  "Rectangle"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_min",
+            "y_min",
+            "x_max",
+            "y_max"
+         ],
+         "title": "Rectangle",
+         "type": "object"
+      },
+      "Region": {
+         "discriminator": {
+            "mapping": {
+               "Circle": "#/$defs/Circle",
+               "CombinationOf": "#/$defs/CombinationOf",
+               "DifferenceOf": "#/$defs/DifferenceOf",
+               "Ellipse": "#/$defs/Ellipse",
+               "IntersectionOf": "#/$defs/IntersectionOf",
+               "Polygon": "#/$defs/Polygon",
+               "Range": "#/$defs/Range",
+               "Rectangle": "#/$defs/Rectangle",
+               "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf",
+               "UnionOf": "#/$defs/UnionOf"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/CombinationOf"
+            },
+            {
+               "$ref": "#/$defs/UnionOf"
+            },
+            {
+               "$ref": "#/$defs/IntersectionOf"
+            },
+            {
+               "$ref": "#/$defs/DifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/SymmetricDifferenceOf"
+            },
+            {
+               "$ref": "#/$defs/Range"
+            },
+            {
+               "$ref": "#/$defs/Rectangle"
+            },
+            {
+               "$ref": "#/$defs/Polygon"
+            },
+            {
+               "$ref": "#/$defs/Circle"
+            },
+            {
+               "$ref": "#/$defs/Ellipse"
+            }
+         ]
+      },
+      "Repeat": {
+         "additionalProperties": false,
+         "description": "Repeat an empty frame num times.\n\nCan be used on the outside of a scan to repeat the same scan many times.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = 2 * ~Line.bounded(\"x\", 3, 4, 1)\n\nIf you want snaked axes to have no gap between iterations you can do:\n\n.. example_spec::\n\n    from scanspec.specs import Line, Repeat\n\n    spec = Repeat(2, gap=False) * ~Line.bounded(\"x\", 3, 4, 1)\n\n.. note:: There is no turnaround arrow at x=4",
+         "properties": {
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "gap": {
+               "default": true,
+               "description": "If False and the slowest of the stack of Frames is snaked then the end and start of consecutive iterations of Spec will have no gap",
+               "title": "Gap",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Repeat",
+               "default": "Repeat",
+               "enum": [
+                  "Repeat"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "num"
+         ],
+         "title": "Repeat",
+         "type": "object"
+      },
+      "Snake": {
+         "additionalProperties": false,
+         "description": "Run the Spec in reverse on every other iteration when nested.\n\nTypically created with the ``~`` operator.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"y\", 1, 3, 3) * ~Line(\"x\", 3, 5, 5)",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to run in reverse every other iteration"
+            },
+            "type": {
+               "const": "Snake",
+               "default": "Snake",
+               "enum": [
+                  "Snake"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Snake",
+         "type": "object"
+      },
+      "Spec": {
+         "discriminator": {
+            "mapping": {
+               "Concat": "#/$defs/Concat",
+               "Line": "#/$defs/Line",
+               "Mask": "#/$defs/Mask",
+               "Product": "#/$defs/Product",
+               "Repeat": "#/$defs/Repeat",
+               "Snake": "#/$defs/Snake",
+               "Spiral": "#/$defs/Spiral",
+               "Squash": "#/$defs/Squash",
+               "Static": "#/$defs/Static",
+               "Zip": "#/$defs/Zip"
+            },
+            "propertyName": "type"
+         },
+         "oneOf": [
+            {
+               "$ref": "#/$defs/Product"
+            },
+            {
+               "$ref": "#/$defs/Repeat"
+            },
+            {
+               "$ref": "#/$defs/Zip"
+            },
+            {
+               "$ref": "#/$defs/Mask"
+            },
+            {
+               "$ref": "#/$defs/Snake"
+            },
+            {
+               "$ref": "#/$defs/Concat"
+            },
+            {
+               "$ref": "#/$defs/Squash"
+            },
+            {
+               "$ref": "#/$defs/Line"
+            },
+            {
+               "$ref": "#/$defs/Static"
+            },
+            {
+               "$ref": "#/$defs/Spiral"
+            }
+         ]
+      },
+      "Spiral": {
+         "additionalProperties": false,
+         "description": "Archimedean spiral of \"x_axis\" and \"y_axis\".\n\nStarts at centre point (\"x_start\", \"y_start\") with angle \"rotate\". Produces\n\"num\" points in a spiral spanning width of \"x_range\" and height of \"y_range\"\n\n.. example_spec::\n\n    from scanspec.specs import Spiral\n\n    spec = Spiral(\"x\", \"y\", 1, 5, 10, 50, 30)",
+         "properties": {
+            "x_axis": {
+               "description": "An identifier for what to move for x",
+               "title": "X Axis"
+            },
+            "y_axis": {
+               "description": "An identifier for what to move for y",
+               "title": "Y Axis"
+            },
+            "x_start": {
+               "description": "x centre of the spiral",
+               "title": "X Start",
+               "type": "number"
+            },
+            "y_start": {
+               "description": "y centre of the spiral",
+               "title": "Y Start",
+               "type": "number"
+            },
+            "x_range": {
+               "description": "x width of the spiral",
+               "title": "X Range",
+               "type": "number"
+            },
+            "y_range": {
+               "description": "y width of the spiral",
+               "title": "Y Range",
+               "type": "number"
+            },
+            "num": {
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "rotate": {
+               "default": 0.0,
+               "description": "How much to rotate the angle of the spiral",
+               "title": "Rotate",
+               "type": "number"
+            },
+            "type": {
+               "const": "Spiral",
+               "default": "Spiral",
+               "enum": [
+                  "Spiral"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "x_axis",
+            "y_axis",
+            "x_start",
+            "y_start",
+            "x_range",
+            "y_range",
+            "num"
+         ],
+         "title": "Spiral",
+         "type": "object"
+      },
+      "Squash": {
+         "additionalProperties": false,
+         "description": "Squash a stack of Frames together into a single expanded Frames object.\n\nSee Also:\n    `why-squash-can-change-path`\n\n.. example_spec::\n\n    from scanspec.specs import Line, Squash\n\n    spec = Squash(Line(\"y\", 1, 2, 3) * Line(\"x\", 0, 1, 4))",
+         "properties": {
+            "spec": {
+               "$ref": "#/$defs/Spec",
+               "description": "The Spec to squash the dimensions of"
+            },
+            "check_path_changes": {
+               "default": true,
+               "description": "If True path through scan will not be modified by squash",
+               "title": "Check Path Changes",
+               "type": "boolean"
+            },
+            "type": {
+               "const": "Squash",
+               "default": "Squash",
+               "enum": [
+                  "Squash"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "spec"
+         ],
+         "title": "Squash",
+         "type": "object"
+      },
+      "Static": {
+         "additionalProperties": false,
+         "description": "A static frame, repeated num times, with axis at value.\n\nCan be used to set axis=value at every point in a scan.\n\n.. example_spec::\n\n    from scanspec.specs import Line, Static\n\n    spec = Line(\"y\", 1, 2, 3).zip(Static(\"x\", 3))",
+         "properties": {
+            "axis": {
+               "description": "An identifier for what to move",
+               "title": "Axis"
+            },
+            "value": {
+               "description": "The value at each point",
+               "title": "Value",
+               "type": "number"
+            },
+            "num": {
+               "default": 1,
+               "description": "Number of frames to produce",
+               "minimum": 1,
+               "title": "Num",
+               "type": "integer"
+            },
+            "type": {
+               "const": "Static",
+               "default": "Static",
+               "enum": [
+                  "Static"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "axis",
+            "value"
+         ],
+         "title": "Static",
+         "type": "object"
+      },
+      "SymmetricDifferenceOf": {
+         "additionalProperties": false,
+         "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True, False,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "SymmetricDifferenceOf",
+               "default": "SymmetricDifferenceOf",
+               "enum": [
+                  "SymmetricDifferenceOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "SymmetricDifferenceOf",
+         "type": "object"
+      },
+      "UnionOf": {
+         "additionalProperties": false,
+         "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False,  True,  True,  True, False])",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Region",
+               "description": "The left-hand Region to combine"
+            },
+            "right": {
+               "$ref": "#/$defs/Region",
+               "description": "The right-hand Region to combine"
+            },
+            "type": {
+               "const": "UnionOf",
+               "default": "UnionOf",
+               "enum": [
+                  "UnionOf"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "UnionOf",
+         "type": "object"
+      },
+      "Zip": {
+         "additionalProperties": false,
+         "description": "Run two Specs in parallel, merging their midpoints together.\n\nTypically formed using `Spec.zip`.\n\nStacks of Frames are merged by:\n\n- If right creates a stack of a single Frames object of size 1, expand it to\n  the size of the fastest Frames object created by left\n- Merge individual Frames objects together from fastest to slowest\n\nThis means that Zipping a Spec producing stack [l2, l1] with a Spec\nproducing stack [r1] will assert len(l1)==len(r1), and produce\nstack [l2, l1.zip(r1)].\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"z\", 1, 2, 3) * Line(\"y\", 3, 4, 5).zip(Line(\"x\", 4, 5, 5))",
+         "properties": {
+            "left": {
+               "$ref": "#/$defs/Spec",
+               "description": "The left-hand Spec to Zip, will appear earlier in axes"
+            },
+            "right": {
+               "$ref": "#/$defs/Spec",
+               "description": "The right-hand Spec to Zip, will appear later in axes"
+            },
+            "type": {
+               "const": "Zip",
+               "default": "Zip",
+               "enum": [
+                  "Zip"
+               ],
+               "title": "Type",
+               "type": "string"
+            }
+         },
+         "required": [
+            "left",
+            "right"
+         ],
+         "title": "Zip",
+         "type": "object"
+      }
+   },
+   "$ref": "#/$defs/Squash"
+}
+
+
+

+
Fields:
+
+
+
+
+
+field check_path_changes: bool = True#
+

If True path through scan will not be modified by squash

+
+ +
+
+field spec: Spec[Axis] [Required]#
+

The Spec to squash the dimensions of

+
+ +
+
+field type: Literal['Squash'] = 'Squash'#
+
+ +
+
+axes() list[Axis][source]#
+

Return the list of axes that are present in the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+calculate(bounds=True, nested=False) list[Frames[Axis]][source]#
+

Produce a stack of nested Frames that form the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+ +
+
+pydantic model scanspec.specs.Line[source]#
+

Linearly spaced frames with start and stop as first and last midpoints.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line
+
+spec = Line("x", 1, 2, 5)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-9.png +
+

+Show JSON schema
{
+   "title": "Line",
+   "description": "Linearly spaced frames with start and stop as first and last midpoints.\n\n.. example_spec::\n\n    from scanspec.specs import Line\n\n    spec = Line(\"x\", 1, 2, 5)",
+   "type": "object",
+   "properties": {
+      "axis": {
+         "description": "An identifier for what to move",
+         "title": "Axis"
+      },
+      "start": {
+         "description": "Midpoint of the first point of the line",
+         "title": "Start",
+         "type": "number"
+      },
+      "stop": {
+         "description": "Midpoint of the last point of the line",
+         "title": "Stop",
+         "type": "number"
+      },
+      "num": {
+         "description": "Number of frames to produce",
+         "minimum": 1,
+         "title": "Num",
+         "type": "integer"
+      },
+      "type": {
+         "const": "Line",
+         "default": "Line",
+         "enum": [
+            "Line"
+         ],
+         "title": "Type",
+         "type": "string"
+      }
+   },
+   "additionalProperties": false,
+   "required": [
+      "axis",
+      "start",
+      "stop",
+      "num"
+   ]
+}
+
+
+

+
Fields:
+
+
+
+
+
+field axis: Axis [Required]#
+

An identifier for what to move

+
+ +
+
+field num: int [Required]#
+

Number of frames to produce

+
+
Constraints:
+
    +
  • ge = 1

  • +
+
+
+
+ +
+
+field start: float [Required]#
+

Midpoint of the first point of the line

+
+ +
+
+field stop: float [Required]#
+

Midpoint of the last point of the line

+
+ +
+
+field type: Literal['Line'] = 'Line'#
+
+ +
+
+axes() list[Axis][source]#
+

Return the list of axes that are present in the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+bounded(lower: float = FieldInfo(annotation=float, required=True, description='Lower bound of the first point of the line'), upper: float = FieldInfo(annotation=float, required=True, description='Upper bound of the last point of the line'), num: int = FieldInfo(annotation=int, required=True, description='Number of frames to produce', metadata=[Ge(ge=1)])) Line[Axis][source]#
+

Specify a Line by extreme bounds instead of midpoints.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line
+
+spec = Line.bounded("x", 1, 2, 5)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-10.png +
+
+ +
+
+calculate(bounds=True, nested=False) list[Frames[Axis]][source]#
+

Produce a stack of nested Frames that form the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+ +
+
+pydantic model scanspec.specs.Static[source]#
+

A static frame, repeated num times, with axis at value.

+

Can be used to set axis=value at every point in a scan.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line, Static
+
+spec = Line("y", 1, 2, 3).zip(Static("x", 3))
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-11.png +
+

+Show JSON schema
{
+   "title": "Static",
+   "description": "A static frame, repeated num times, with axis at value.\n\nCan be used to set axis=value at every point in a scan.\n\n.. example_spec::\n\n    from scanspec.specs import Line, Static\n\n    spec = Line(\"y\", 1, 2, 3).zip(Static(\"x\", 3))",
+   "type": "object",
+   "properties": {
+      "axis": {
+         "description": "An identifier for what to move",
+         "title": "Axis"
+      },
+      "value": {
+         "description": "The value at each point",
+         "title": "Value",
+         "type": "number"
+      },
+      "num": {
+         "default": 1,
+         "description": "Number of frames to produce",
+         "minimum": 1,
+         "title": "Num",
+         "type": "integer"
+      },
+      "type": {
+         "const": "Static",
+         "default": "Static",
+         "enum": [
+            "Static"
+         ],
+         "title": "Type",
+         "type": "string"
+      }
+   },
+   "additionalProperties": false,
+   "required": [
+      "axis",
+      "value"
+   ]
+}
+
+
+

+
Fields:
+
+
+
+
+
+field axis: Axis [Required]#
+

An identifier for what to move

+
+ +
+
+field num: int = 1#
+

Number of frames to produce

+
+
Constraints:
+
    +
  • ge = 1

  • +
+
+
+
+ +
+
+field type: Literal['Static'] = 'Static'#
+
+ +
+
+field value: float [Required]#
+

The value at each point

+
+ +
+
+axes() list[Axis][source]#
+

Return the list of axes that are present in the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+calculate(bounds=True, nested=False) list[Frames[Axis]][source]#
+

Produce a stack of nested Frames that form the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+duration(num: int = FieldInfo(annotation=int, required=False, default=1, description='Number of frames to produce', metadata=[Ge(ge=1)])) Static[str][source]#
+

A static spec with no motion, only a duration repeated “num” times.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line, Static
+
+spec = Line("y", 1, 2, 3).zip(Static.duration(0.1))
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-12.png +
+
+ +
+ +
+
+pydantic model scanspec.specs.Spiral[source]#
+

Archimedean spiral of “x_axis” and “y_axis”.

+

Starts at centre point (“x_start”, “y_start”) with angle “rotate”. Produces +“num” points in a spiral spanning width of “x_range” and height of “y_range”

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Spiral
+
+spec = Spiral("x", "y", 1, 5, 10, 50, 30)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-13.png +
+

+Show JSON schema
{
+   "title": "Spiral",
+   "description": "Archimedean spiral of \"x_axis\" and \"y_axis\".\n\nStarts at centre point (\"x_start\", \"y_start\") with angle \"rotate\". Produces\n\"num\" points in a spiral spanning width of \"x_range\" and height of \"y_range\"\n\n.. example_spec::\n\n    from scanspec.specs import Spiral\n\n    spec = Spiral(\"x\", \"y\", 1, 5, 10, 50, 30)",
+   "type": "object",
+   "properties": {
+      "x_axis": {
+         "description": "An identifier for what to move for x",
+         "title": "X Axis"
+      },
+      "y_axis": {
+         "description": "An identifier for what to move for y",
+         "title": "Y Axis"
+      },
+      "x_start": {
+         "description": "x centre of the spiral",
+         "title": "X Start",
+         "type": "number"
+      },
+      "y_start": {
+         "description": "y centre of the spiral",
+         "title": "Y Start",
+         "type": "number"
+      },
+      "x_range": {
+         "description": "x width of the spiral",
+         "title": "X Range",
+         "type": "number"
+      },
+      "y_range": {
+         "description": "y width of the spiral",
+         "title": "Y Range",
+         "type": "number"
+      },
+      "num": {
+         "description": "Number of frames to produce",
+         "minimum": 1,
+         "title": "Num",
+         "type": "integer"
+      },
+      "rotate": {
+         "default": 0.0,
+         "description": "How much to rotate the angle of the spiral",
+         "title": "Rotate",
+         "type": "number"
+      },
+      "type": {
+         "const": "Spiral",
+         "default": "Spiral",
+         "enum": [
+            "Spiral"
+         ],
+         "title": "Type",
+         "type": "string"
+      }
+   },
+   "additionalProperties": false,
+   "required": [
+      "x_axis",
+      "y_axis",
+      "x_start",
+      "y_start",
+      "x_range",
+      "y_range",
+      "num"
+   ]
+}
+
+
+

+
Fields:
+
+
+
+
+
+field num: int [Required]#
+

Number of frames to produce

+
+
Constraints:
+
    +
  • ge = 1

  • +
+
+
+
+ +
+
+field rotate: float = 0.0#
+

How much to rotate the angle of the spiral

+
+ +
+
+field type: Literal['Spiral'] = 'Spiral'#
+
+ +
+
+field x_axis: Axis [Required]#
+

An identifier for what to move for x

+
+ +
+
+field x_range: float [Required]#
+

x width of the spiral

+
+ +
+
+field x_start: float [Required]#
+

x centre of the spiral

+
+ +
+
+field y_axis: Axis [Required]#
+

An identifier for what to move for y

+
+ +
+
+field y_range: float [Required]#
+

y width of the spiral

+
+ +
+
+field y_start: float [Required]#
+

y centre of the spiral

+
+ +
+
+axes() list[Axis][source]#
+

Return the list of axes that are present in the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+calculate(bounds=True, nested=False) list[Frames[Axis]][source]#
+

Produce a stack of nested Frames that form the scan.

+

Ordered from slowest moving to fastest moving.

+
+ +
+
+spaced(y_axis: Axis = FieldInfo(annotation=~Axis, required=True, description='An identifier for what to move for y'), x_start: float = FieldInfo(annotation=float, required=True, description='x centre of the spiral'), y_start: float = FieldInfo(annotation=float, required=True, description='y centre of the spiral'), radius: float = FieldInfo(annotation=float, required=True, description='radius of the spiral'), dr: float = FieldInfo(annotation=float, required=True, description='difference between each ring'), rotate: float = FieldInfo(annotation=float, required=False, default=0.0, description='How much to rotate the angle of the spiral')) Spiral[Axis][source]#
+

Specify a Spiral equally spaced in “x_axis” and “y_axis”.

+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Spiral
+
+spec = Spiral.spaced("x", "y", 0, 0, 10, 3)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-14.png +
+
+ +
+ +
+
+scanspec.specs.fly(spec: Spec[Axis], duration: float) Spec[Axis][source]#
+

Flyscan, zipping with fixed duration for every frame.

+
+
Parameters:
+
    +
  • spec – The source Spec to continuously move

  • +
  • duration – How long to spend at each frame in the spec

  • +
+
+
+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line, fly
+
+spec = fly(Line("x", 1, 2, 3), 0.1)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-15.png +
+
+ +
+
+scanspec.specs.step(spec: Spec[Axis], duration: float, num: int = 1) Spec[Axis][source]#
+

Step scan, with num frames of given duration at each frame in the spec.

+
+
Parameters:
+
    +
  • spec – The source Spec with midpoints to move to and stop

  • +
  • duration – The duration of each scan frame

  • +
  • num – Number of frames to produce with given duration at each of frame +in the spec

  • +
+
+
+
# Example Spec
+
+from scanspec.plot import plot_spec
+from scanspec.specs import Line, step
+
+spec = step(Line("x", 1, 2, 3), 0.1)
+plot_spec(spec)
+
+
+

(Source code, png, hires.png, pdf)

+
+../_images/scanspec-specs-16.png +
+
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/main/_downloads/0223a88adc6d09735d8fff79b4ca64a0/scanspec-regions-3.py b/main/_downloads/0223a88adc6d09735d8fff79b4ca64a0/scanspec-regions-3.py new file mode 100644 index 00000000..aff5125b --- /dev/null +++ b/main/_downloads/0223a88adc6d09735d8fff79b4ca64a0/scanspec-regions-3.py @@ -0,0 +1,9 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.regions import Circle +from scanspec.specs import Line + +grid = Line("y", 1, 3, 10) * ~Line("x", 0, 2, 10) +spec = grid & Circle("x", "y", 1, 2, 0.9) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/029d34da503729102c31e8b9bcdcce2a/scanspec-specs-6.py b/main/_downloads/029d34da503729102c31e8b9bcdcce2a/scanspec-specs-6.py new file mode 100644 index 00000000..f10a8074 --- /dev/null +++ b/main/_downloads/029d34da503729102c31e8b9bcdcce2a/scanspec-specs-6.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line + +spec = Line("y", 1, 3, 3) * ~Line("x", 3, 5, 5) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/0ae73fb7939821b1edc3d22233280318/scanspec-regions-1.pdf b/main/_downloads/0ae73fb7939821b1edc3d22233280318/scanspec-regions-1.pdf new file mode 100644 index 00000000..0facaa2e Binary files /dev/null and b/main/_downloads/0ae73fb7939821b1edc3d22233280318/scanspec-regions-1.pdf differ diff --git a/main/_downloads/0b4799657e877d5b1b4cacd9b2acb170/why-squash-can-change-path-3.pdf b/main/_downloads/0b4799657e877d5b1b4cacd9b2acb170/why-squash-can-change-path-3.pdf index 580e4301..6bc222d0 100644 Binary files a/main/_downloads/0b4799657e877d5b1b4cacd9b2acb170/why-squash-can-change-path-3.pdf and b/main/_downloads/0b4799657e877d5b1b4cacd9b2acb170/why-squash-can-change-path-3.pdf differ diff --git a/main/_downloads/0e091704af9d4694c1d1ec0a0954dc8f/scanspec-regions-2.png b/main/_downloads/0e091704af9d4694c1d1ec0a0954dc8f/scanspec-regions-2.png new file mode 100644 index 00000000..bb1adfdb Binary files /dev/null and b/main/_downloads/0e091704af9d4694c1d1ec0a0954dc8f/scanspec-regions-2.png differ diff --git a/main/_downloads/1473d71a9aa74675a8d9df70051456f4/scanspec-specs-8.png b/main/_downloads/1473d71a9aa74675a8d9df70051456f4/scanspec-specs-8.png new file mode 100644 index 00000000..6cf49529 Binary files /dev/null and b/main/_downloads/1473d71a9aa74675a8d9df70051456f4/scanspec-specs-8.png differ diff --git a/main/_downloads/14cd9f05ba952a1aeae7bc548b9fd177/scanspec-specs-3.pdf b/main/_downloads/14cd9f05ba952a1aeae7bc548b9fd177/scanspec-specs-3.pdf new file mode 100644 index 00000000..d6206f31 Binary files /dev/null and b/main/_downloads/14cd9f05ba952a1aeae7bc548b9fd177/scanspec-specs-3.pdf differ diff --git a/main/_downloads/17b16611218f29da558120b1699442be/scanspec-specs-11.py b/main/_downloads/17b16611218f29da558120b1699442be/scanspec-specs-11.py new file mode 100644 index 00000000..2132f391 --- /dev/null +++ b/main/_downloads/17b16611218f29da558120b1699442be/scanspec-specs-11.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line, Static + +spec = Line("y", 1, 2, 3).zip(Static("x", 3)) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/1881c7b7398842eb252241a18e511a61/scanspec-regions-2.py b/main/_downloads/1881c7b7398842eb252241a18e511a61/scanspec-regions-2.py new file mode 100644 index 00000000..845343f9 --- /dev/null +++ b/main/_downloads/1881c7b7398842eb252241a18e511a61/scanspec-regions-2.py @@ -0,0 +1,9 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.regions import Polygon +from scanspec.specs import Line + +grid = Line("y", 3, 8, 10) * ~Line("x", 1 ,8, 10) +spec = grid & Polygon("x", "y", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0]) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/1aef3bb8ee2a641e32a2d2d96e29b7b6/scanspec-specs-4.hires.png b/main/_downloads/1aef3bb8ee2a641e32a2d2d96e29b7b6/scanspec-specs-4.hires.png new file mode 100644 index 00000000..85b2152a Binary files /dev/null and b/main/_downloads/1aef3bb8ee2a641e32a2d2d96e29b7b6/scanspec-specs-4.hires.png differ diff --git a/main/_downloads/28f92b8fad4dde904a13111227dc1427/scanspec-specs-9.png b/main/_downloads/28f92b8fad4dde904a13111227dc1427/scanspec-specs-9.png new file mode 100644 index 00000000..04509ee2 Binary files /dev/null and b/main/_downloads/28f92b8fad4dde904a13111227dc1427/scanspec-specs-9.png differ diff --git a/main/_downloads/29ca263728fd0f8d0c6cfad280625143/scanspec-specs-7.py b/main/_downloads/29ca263728fd0f8d0c6cfad280625143/scanspec-specs-7.py new file mode 100644 index 00000000..b1474a68 --- /dev/null +++ b/main/_downloads/29ca263728fd0f8d0c6cfad280625143/scanspec-specs-7.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line + +spec = Line("x", 1, 3, 3).concat(Line("x", 4, 5, 5)) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/29efac10d5b5e4957caf26c08046ad85/scanspec-specs-15.png b/main/_downloads/29efac10d5b5e4957caf26c08046ad85/scanspec-specs-15.png new file mode 100644 index 00000000..cd0b2340 Binary files /dev/null and b/main/_downloads/29efac10d5b5e4957caf26c08046ad85/scanspec-specs-15.png differ diff --git a/main/_downloads/29f8e9f676c07ef454b88a78fef753ad/scanspec-specs-10.pdf b/main/_downloads/29f8e9f676c07ef454b88a78fef753ad/scanspec-specs-10.pdf new file mode 100644 index 00000000..3754b24d Binary files /dev/null and b/main/_downloads/29f8e9f676c07ef454b88a78fef753ad/scanspec-specs-10.pdf differ diff --git a/main/_downloads/2bdcf85251aa86ede3065d379aff5cd7/scanspec-specs-13.py b/main/_downloads/2bdcf85251aa86ede3065d379aff5cd7/scanspec-specs-13.py new file mode 100644 index 00000000..41d808fc --- /dev/null +++ b/main/_downloads/2bdcf85251aa86ede3065d379aff5cd7/scanspec-specs-13.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Spiral + +spec = Spiral("x", "y", 1, 5, 10, 50, 30) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/2c9a9fd7b530c5c706073c6c74b7e9c8/scanspec-regions-1.py b/main/_downloads/2c9a9fd7b530c5c706073c6c74b7e9c8/scanspec-regions-1.py new file mode 100644 index 00000000..9fd0362c --- /dev/null +++ b/main/_downloads/2c9a9fd7b530c5c706073c6c74b7e9c8/scanspec-regions-1.py @@ -0,0 +1,9 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.regions import Rectangle +from scanspec.specs import Line + +grid = Line("y", 1, 3, 10) * ~Line("x", 0, 2, 10) +spec = grid & Rectangle("x", "y", 0, 1.1, 1.5, 2.1, 30) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/3114561b7e33fb4f9b1b212000aa552b/scanspec-specs-13.png b/main/_downloads/3114561b7e33fb4f9b1b212000aa552b/scanspec-specs-13.png new file mode 100644 index 00000000..64604a20 Binary files /dev/null and b/main/_downloads/3114561b7e33fb4f9b1b212000aa552b/scanspec-specs-13.png differ diff --git a/main/_downloads/32c9e69b107f5cd11ed75edfef7a521c/creating-a-spec-4.pdf b/main/_downloads/32c9e69b107f5cd11ed75edfef7a521c/creating-a-spec-4.pdf index c2e53e1a..8cdb3bd5 100644 Binary files a/main/_downloads/32c9e69b107f5cd11ed75edfef7a521c/creating-a-spec-4.pdf and b/main/_downloads/32c9e69b107f5cd11ed75edfef7a521c/creating-a-spec-4.pdf differ diff --git a/main/_downloads/36360e8aeabfd64e7339de3c85a6a2ae/scanspec-specs-2.pdf b/main/_downloads/36360e8aeabfd64e7339de3c85a6a2ae/scanspec-specs-2.pdf new file mode 100644 index 00000000..c90fca17 Binary files /dev/null and b/main/_downloads/36360e8aeabfd64e7339de3c85a6a2ae/scanspec-specs-2.pdf differ diff --git a/main/_downloads/391cf72d62c69bafc113af94890f714e/scanspec-specs-10.py b/main/_downloads/391cf72d62c69bafc113af94890f714e/scanspec-specs-10.py new file mode 100644 index 00000000..bff58d33 --- /dev/null +++ b/main/_downloads/391cf72d62c69bafc113af94890f714e/scanspec-specs-10.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line + +spec = Line.bounded("x", 1, 2, 5) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/4076f4b2e334f3de92b85af48122e3ab/scanspec-specs-9.py b/main/_downloads/4076f4b2e334f3de92b85af48122e3ab/scanspec-specs-9.py new file mode 100644 index 00000000..d40cfb88 --- /dev/null +++ b/main/_downloads/4076f4b2e334f3de92b85af48122e3ab/scanspec-specs-9.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line + +spec = Line("x", 1, 2, 5) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/41ee09f6b8792abbc8b43852678d87f3/scanspec-regions-3.hires.png b/main/_downloads/41ee09f6b8792abbc8b43852678d87f3/scanspec-regions-3.hires.png new file mode 100644 index 00000000..1155ca67 Binary files /dev/null and b/main/_downloads/41ee09f6b8792abbc8b43852678d87f3/scanspec-regions-3.hires.png differ diff --git a/main/_downloads/4509167960562ec708efdd0be55da36c/scanspec-regions-4.png b/main/_downloads/4509167960562ec708efdd0be55da36c/scanspec-regions-4.png new file mode 100644 index 00000000..27e11047 Binary files /dev/null and b/main/_downloads/4509167960562ec708efdd0be55da36c/scanspec-regions-4.png differ diff --git a/main/_downloads/495047b8b8a7867da4c99568301922ec/scanspec-specs-14.py b/main/_downloads/495047b8b8a7867da4c99568301922ec/scanspec-specs-14.py new file mode 100644 index 00000000..99605daf --- /dev/null +++ b/main/_downloads/495047b8b8a7867da4c99568301922ec/scanspec-specs-14.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Spiral + +spec = Spiral.spaced("x", "y", 0, 0, 10, 3) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/4ad72ca01eda81b905ebb3d9d4b4bd07/scanspec-specs-6.png b/main/_downloads/4ad72ca01eda81b905ebb3d9d4b4bd07/scanspec-specs-6.png new file mode 100644 index 00000000..bd687072 Binary files /dev/null and b/main/_downloads/4ad72ca01eda81b905ebb3d9d4b4bd07/scanspec-specs-6.png differ diff --git a/main/_downloads/4cff00b0b1d9fd7cbd80ee99b1324784/scanspec-specs-13.pdf b/main/_downloads/4cff00b0b1d9fd7cbd80ee99b1324784/scanspec-specs-13.pdf new file mode 100644 index 00000000..6c14b075 Binary files /dev/null and b/main/_downloads/4cff00b0b1d9fd7cbd80ee99b1324784/scanspec-specs-13.pdf differ diff --git a/main/_downloads/4d5401a5ef1906f42ef40eae55ca4842/scanspec-specs-16.hires.png b/main/_downloads/4d5401a5ef1906f42ef40eae55ca4842/scanspec-specs-16.hires.png new file mode 100644 index 00000000..f0ac194d Binary files /dev/null and b/main/_downloads/4d5401a5ef1906f42ef40eae55ca4842/scanspec-specs-16.hires.png differ diff --git a/main/_downloads/4e6a723d5cba2932d0dc34c140576fd6/scanspec-specs-8.pdf b/main/_downloads/4e6a723d5cba2932d0dc34c140576fd6/scanspec-specs-8.pdf new file mode 100644 index 00000000..04feae8d Binary files /dev/null and b/main/_downloads/4e6a723d5cba2932d0dc34c140576fd6/scanspec-specs-8.pdf differ diff --git a/main/_downloads/4fea1518115551cb791d855e6a66eb55/scanspec-specs-3.png b/main/_downloads/4fea1518115551cb791d855e6a66eb55/scanspec-specs-3.png new file mode 100644 index 00000000..b1a79d80 Binary files /dev/null and b/main/_downloads/4fea1518115551cb791d855e6a66eb55/scanspec-specs-3.png differ diff --git a/main/_downloads/52bdbd66208e717704106cf91a12bc13/scanspec-regions-2.hires.png b/main/_downloads/52bdbd66208e717704106cf91a12bc13/scanspec-regions-2.hires.png new file mode 100644 index 00000000..e5649cc1 Binary files /dev/null and b/main/_downloads/52bdbd66208e717704106cf91a12bc13/scanspec-regions-2.hires.png differ diff --git a/main/_downloads/55477252c82aa8a8b1380f6521402e48/scanspec-specs-16.py b/main/_downloads/55477252c82aa8a8b1380f6521402e48/scanspec-specs-16.py new file mode 100644 index 00000000..e08f52ed --- /dev/null +++ b/main/_downloads/55477252c82aa8a8b1380f6521402e48/scanspec-specs-16.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line, step + +spec = step(Line("x", 1, 2, 3), 0.1) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/56bb872d1a387f9c4a1e9fd60023d0ad/scanspec-regions-4.pdf b/main/_downloads/56bb872d1a387f9c4a1e9fd60023d0ad/scanspec-regions-4.pdf new file mode 100644 index 00000000..12463a90 Binary files /dev/null and b/main/_downloads/56bb872d1a387f9c4a1e9fd60023d0ad/scanspec-regions-4.pdf differ diff --git a/main/_downloads/594f21876b9d2230f2a0af7771fd53e3/scanspec-regions-2.pdf b/main/_downloads/594f21876b9d2230f2a0af7771fd53e3/scanspec-regions-2.pdf new file mode 100644 index 00000000..90e76fbd Binary files /dev/null and b/main/_downloads/594f21876b9d2230f2a0af7771fd53e3/scanspec-regions-2.pdf differ diff --git a/main/_downloads/59ccb090bb2d3360ace4709fff9b5b3b/creating-a-spec-1.pdf b/main/_downloads/59ccb090bb2d3360ace4709fff9b5b3b/creating-a-spec-1.pdf index a245b38e..fd1f4ccc 100644 Binary files a/main/_downloads/59ccb090bb2d3360ace4709fff9b5b3b/creating-a-spec-1.pdf and b/main/_downloads/59ccb090bb2d3360ace4709fff9b5b3b/creating-a-spec-1.pdf differ diff --git a/main/_downloads/5c0624426b92b4d678699185e497f06b/creating-a-spec-6.pdf b/main/_downloads/5c0624426b92b4d678699185e497f06b/creating-a-spec-6.pdf index 78bdd833..6e74979f 100644 Binary files a/main/_downloads/5c0624426b92b4d678699185e497f06b/creating-a-spec-6.pdf and b/main/_downloads/5c0624426b92b4d678699185e497f06b/creating-a-spec-6.pdf differ diff --git a/main/_downloads/5f4cbd3d9eca3474f783397c1af34245/scanspec-specs-14.pdf b/main/_downloads/5f4cbd3d9eca3474f783397c1af34245/scanspec-specs-14.pdf new file mode 100644 index 00000000..d9c2b06e Binary files /dev/null and b/main/_downloads/5f4cbd3d9eca3474f783397c1af34245/scanspec-specs-14.pdf differ diff --git a/main/_downloads/5fecf49fd9e18b0cbc137cab2570ce30/scanspec-specs-12.py b/main/_downloads/5fecf49fd9e18b0cbc137cab2570ce30/scanspec-specs-12.py new file mode 100644 index 00000000..726271d9 --- /dev/null +++ b/main/_downloads/5fecf49fd9e18b0cbc137cab2570ce30/scanspec-specs-12.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line, Static + +spec = Line("y", 1, 2, 3).zip(Static.duration(0.1)) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/622f2957f6aa7d79df0cad5d9e9946f6/scanspec-specs-14.png b/main/_downloads/622f2957f6aa7d79df0cad5d9e9946f6/scanspec-specs-14.png new file mode 100644 index 00000000..88156f69 Binary files /dev/null and b/main/_downloads/622f2957f6aa7d79df0cad5d9e9946f6/scanspec-specs-14.png differ diff --git a/main/_downloads/6273f467b19bc56f75d71e9fa791260b/scanspec-specs-13.hires.png b/main/_downloads/6273f467b19bc56f75d71e9fa791260b/scanspec-specs-13.hires.png new file mode 100644 index 00000000..e1a9f433 Binary files /dev/null and b/main/_downloads/6273f467b19bc56f75d71e9fa791260b/scanspec-specs-13.hires.png differ diff --git a/main/_downloads/62c5e5947d127957c1809c6287b64e8a/scanspec-specs-4.py b/main/_downloads/62c5e5947d127957c1809c6287b64e8a/scanspec-specs-4.py new file mode 100644 index 00000000..79f5f57d --- /dev/null +++ b/main/_downloads/62c5e5947d127957c1809c6287b64e8a/scanspec-specs-4.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line + +spec = Line("z", 1, 2, 3) * Line("y", 3, 4, 5).zip(Line("x", 4, 5, 5)) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/6377083c2cf9e1c6c2ce2f15c65b9035/scanspec-specs-12.pdf b/main/_downloads/6377083c2cf9e1c6c2ce2f15c65b9035/scanspec-specs-12.pdf new file mode 100644 index 00000000..ef998911 Binary files /dev/null and b/main/_downloads/6377083c2cf9e1c6c2ce2f15c65b9035/scanspec-specs-12.pdf differ diff --git a/main/_downloads/64847659a317500c96e79e38caf63631/scanspec-specs-8.py b/main/_downloads/64847659a317500c96e79e38caf63631/scanspec-specs-8.py new file mode 100644 index 00000000..1cc7c4e1 --- /dev/null +++ b/main/_downloads/64847659a317500c96e79e38caf63631/scanspec-specs-8.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line, Squash + +spec = Squash(Line("y", 1, 2, 3) * Line("x", 0, 1, 4)) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/64bfb258ba45ae9ac7554b9fd23e1714/scanspec-specs-9.hires.png b/main/_downloads/64bfb258ba45ae9ac7554b9fd23e1714/scanspec-specs-9.hires.png new file mode 100644 index 00000000..fbd32b10 Binary files /dev/null and b/main/_downloads/64bfb258ba45ae9ac7554b9fd23e1714/scanspec-specs-9.hires.png differ diff --git a/main/_downloads/6765155ac6402e82e524480d6a75c968/scanspec-specs-12.png b/main/_downloads/6765155ac6402e82e524480d6a75c968/scanspec-specs-12.png new file mode 100644 index 00000000..9e71796d Binary files /dev/null and b/main/_downloads/6765155ac6402e82e524480d6a75c968/scanspec-specs-12.png differ diff --git a/main/_downloads/67664503153d45229a4637c1dc49d984/scanspec-specs-7.png b/main/_downloads/67664503153d45229a4637c1dc49d984/scanspec-specs-7.png new file mode 100644 index 00000000..62fc4e12 Binary files /dev/null and b/main/_downloads/67664503153d45229a4637c1dc49d984/scanspec-specs-7.png differ diff --git a/main/_downloads/67c27b53de3dac2211647804f6bdf9fd/scanspec-specs-16.png b/main/_downloads/67c27b53de3dac2211647804f6bdf9fd/scanspec-specs-16.png new file mode 100644 index 00000000..74202a1d Binary files /dev/null and b/main/_downloads/67c27b53de3dac2211647804f6bdf9fd/scanspec-specs-16.png differ diff --git a/main/_downloads/6b2bfdefec22ee159c8ced5f7866a2cb/why-squash-can-change-path-4.pdf b/main/_downloads/6b2bfdefec22ee159c8ced5f7866a2cb/why-squash-can-change-path-4.pdf index 55e44a23..9a25d4f2 100644 Binary files a/main/_downloads/6b2bfdefec22ee159c8ced5f7866a2cb/why-squash-can-change-path-4.pdf and b/main/_downloads/6b2bfdefec22ee159c8ced5f7866a2cb/why-squash-can-change-path-4.pdf differ diff --git a/main/_downloads/6b86bc97919ff794b55d489569679bfb/scanspec-specs-10.png b/main/_downloads/6b86bc97919ff794b55d489569679bfb/scanspec-specs-10.png new file mode 100644 index 00000000..d6daa85e Binary files /dev/null and b/main/_downloads/6b86bc97919ff794b55d489569679bfb/scanspec-specs-10.png differ diff --git a/main/_downloads/6c39d5369cd428a1fc9b6f8931f5af16/scanspec-regions-3.pdf b/main/_downloads/6c39d5369cd428a1fc9b6f8931f5af16/scanspec-regions-3.pdf new file mode 100644 index 00000000..999d7c0d Binary files /dev/null and b/main/_downloads/6c39d5369cd428a1fc9b6f8931f5af16/scanspec-regions-3.pdf differ diff --git a/main/_downloads/722f276ee9a699027f8d55b64c4700c9/scanspec-regions-3.png b/main/_downloads/722f276ee9a699027f8d55b64c4700c9/scanspec-regions-3.png new file mode 100644 index 00000000..a8acaf00 Binary files /dev/null and b/main/_downloads/722f276ee9a699027f8d55b64c4700c9/scanspec-regions-3.png differ diff --git a/main/_downloads/738467511cf5dfa5a9f00429c35d07d2/scanspec-specs-14.hires.png b/main/_downloads/738467511cf5dfa5a9f00429c35d07d2/scanspec-specs-14.hires.png new file mode 100644 index 00000000..066a862b Binary files /dev/null and b/main/_downloads/738467511cf5dfa5a9f00429c35d07d2/scanspec-specs-14.hires.png differ diff --git a/main/_downloads/765eb3a3427fe6d21b1a1aa535319276/scanspec-specs-15.pdf b/main/_downloads/765eb3a3427fe6d21b1a1aa535319276/scanspec-specs-15.pdf new file mode 100644 index 00000000..1bccd830 Binary files /dev/null and b/main/_downloads/765eb3a3427fe6d21b1a1aa535319276/scanspec-specs-15.pdf differ diff --git a/main/_downloads/78010eb471c3624d320b01abff0752f8/scanspec-plot-1.png b/main/_downloads/78010eb471c3624d320b01abff0752f8/scanspec-plot-1.png new file mode 100644 index 00000000..094d4d1a Binary files /dev/null and b/main/_downloads/78010eb471c3624d320b01abff0752f8/scanspec-plot-1.png differ diff --git a/main/_downloads/78f3e6b8ef9b120d4577737c6704b4c6/scanspec-regions-1.hires.png b/main/_downloads/78f3e6b8ef9b120d4577737c6704b4c6/scanspec-regions-1.hires.png new file mode 100644 index 00000000..7790c15c Binary files /dev/null and b/main/_downloads/78f3e6b8ef9b120d4577737c6704b4c6/scanspec-regions-1.hires.png differ diff --git a/main/_downloads/795a14e724a7b3c14d4291bca0c93156/scanspec-specs-5.hires.png b/main/_downloads/795a14e724a7b3c14d4291bca0c93156/scanspec-specs-5.hires.png new file mode 100644 index 00000000..1c3648d4 Binary files /dev/null and b/main/_downloads/795a14e724a7b3c14d4291bca0c93156/scanspec-specs-5.hires.png differ diff --git a/main/_downloads/7b8826e2f0542681558845e82107a45e/scanspec-specs-3.hires.png b/main/_downloads/7b8826e2f0542681558845e82107a45e/scanspec-specs-3.hires.png new file mode 100644 index 00000000..9bfd9740 Binary files /dev/null and b/main/_downloads/7b8826e2f0542681558845e82107a45e/scanspec-specs-3.hires.png differ diff --git a/main/_downloads/7cd1d1d5cbbe023e131aff0cf050eb3a/scanspec-regions-4.py b/main/_downloads/7cd1d1d5cbbe023e131aff0cf050eb3a/scanspec-regions-4.py new file mode 100644 index 00000000..a337b2ed --- /dev/null +++ b/main/_downloads/7cd1d1d5cbbe023e131aff0cf050eb3a/scanspec-regions-4.py @@ -0,0 +1,9 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.regions import Ellipse +from scanspec.specs import Line + +grid = Line("y", 3, 8, 10) * ~Line("x", 1 ,8, 10) +spec = grid & Ellipse("x", "y", 5, 5, 2, 3, 75) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/7e8f24eafd1a977a48125d3f45572c1d/scanspec-specs-5.png b/main/_downloads/7e8f24eafd1a977a48125d3f45572c1d/scanspec-specs-5.png new file mode 100644 index 00000000..5f74c50b Binary files /dev/null and b/main/_downloads/7e8f24eafd1a977a48125d3f45572c1d/scanspec-specs-5.png differ diff --git a/main/_downloads/81242f4d60e5033090690ffb37f6e8e3/scanspec-specs-7.hires.png b/main/_downloads/81242f4d60e5033090690ffb37f6e8e3/scanspec-specs-7.hires.png new file mode 100644 index 00000000..92e7e763 Binary files /dev/null and b/main/_downloads/81242f4d60e5033090690ffb37f6e8e3/scanspec-specs-7.hires.png differ diff --git a/main/_downloads/8def2e8f1cf11e2f6361b9872d5c1f96/scanspec-specs-5.pdf b/main/_downloads/8def2e8f1cf11e2f6361b9872d5c1f96/scanspec-specs-5.pdf new file mode 100644 index 00000000..16130b1b Binary files /dev/null and b/main/_downloads/8def2e8f1cf11e2f6361b9872d5c1f96/scanspec-specs-5.pdf differ diff --git a/main/_downloads/90481c9e5c2b3588d1b16f603ccb3a86/creating-a-spec-3.pdf b/main/_downloads/90481c9e5c2b3588d1b16f603ccb3a86/creating-a-spec-3.pdf index d12afdcc..cefcdaca 100644 Binary files a/main/_downloads/90481c9e5c2b3588d1b16f603ccb3a86/creating-a-spec-3.pdf and b/main/_downloads/90481c9e5c2b3588d1b16f603ccb3a86/creating-a-spec-3.pdf differ diff --git a/main/_downloads/9068d5b74602d43c895bb204f8a33a47/scanspec-specs-1.py b/main/_downloads/9068d5b74602d43c895bb204f8a33a47/scanspec-specs-1.py new file mode 100644 index 00000000..afdf3cbb --- /dev/null +++ b/main/_downloads/9068d5b74602d43c895bb204f8a33a47/scanspec-specs-1.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line + +spec = Line("y", 1, 2, 3) * Line("x", 3, 4, 12) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/968d95e03a89754709e773f72ed8f154/scanspec-specs-6.hires.png b/main/_downloads/968d95e03a89754709e773f72ed8f154/scanspec-specs-6.hires.png new file mode 100644 index 00000000..8e521ed6 Binary files /dev/null and b/main/_downloads/968d95e03a89754709e773f72ed8f154/scanspec-specs-6.hires.png differ diff --git a/main/_downloads/979e789f6fe7f6a0edf7b923298e6c35/scanspec-specs-1.hires.png b/main/_downloads/979e789f6fe7f6a0edf7b923298e6c35/scanspec-specs-1.hires.png new file mode 100644 index 00000000..a6fda002 Binary files /dev/null and b/main/_downloads/979e789f6fe7f6a0edf7b923298e6c35/scanspec-specs-1.hires.png differ diff --git a/main/_downloads/a3e641038fd95a120295f2d3ec2190b6/scanspec-specs-16.pdf b/main/_downloads/a3e641038fd95a120295f2d3ec2190b6/scanspec-specs-16.pdf new file mode 100644 index 00000000..fc4a5420 Binary files /dev/null and b/main/_downloads/a3e641038fd95a120295f2d3ec2190b6/scanspec-specs-16.pdf differ diff --git a/main/_downloads/a4cf0ccadef62899dc1c214dc006c23e/scanspec-regions-4.hires.png b/main/_downloads/a4cf0ccadef62899dc1c214dc006c23e/scanspec-regions-4.hires.png new file mode 100644 index 00000000..6a7d8e23 Binary files /dev/null and b/main/_downloads/a4cf0ccadef62899dc1c214dc006c23e/scanspec-regions-4.hires.png differ diff --git a/main/_downloads/a59083bb391dc8562b94035c10b73be3/scanspec-specs-5.py b/main/_downloads/a59083bb391dc8562b94035c10b73be3/scanspec-specs-5.py new file mode 100644 index 00000000..68dd4100 --- /dev/null +++ b/main/_downloads/a59083bb391dc8562b94035c10b73be3/scanspec-specs-5.py @@ -0,0 +1,8 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.regions import Circle +from scanspec.specs import Line + +spec = Line("y", 1, 3, 3) * Line("x", 3, 5, 5) & Circle("x", "y", 4, 2, 1.2) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/aaf02e051399c63656daa735de933754/scanspec-specs-4.pdf b/main/_downloads/aaf02e051399c63656daa735de933754/scanspec-specs-4.pdf new file mode 100644 index 00000000..51743752 Binary files /dev/null and b/main/_downloads/aaf02e051399c63656daa735de933754/scanspec-specs-4.pdf differ diff --git a/main/_downloads/ab15ad6488d7de55d318e0ce16a402e4/scanspec-specs-8.hires.png b/main/_downloads/ab15ad6488d7de55d318e0ce16a402e4/scanspec-specs-8.hires.png new file mode 100644 index 00000000..18105e6f Binary files /dev/null and b/main/_downloads/ab15ad6488d7de55d318e0ce16a402e4/scanspec-specs-8.hires.png differ diff --git a/main/_downloads/ac74e6bf48373aad32d4450b8d529998/scanspec-plot-1.hires.png b/main/_downloads/ac74e6bf48373aad32d4450b8d529998/scanspec-plot-1.hires.png new file mode 100644 index 00000000..629b776f Binary files /dev/null and b/main/_downloads/ac74e6bf48373aad32d4450b8d529998/scanspec-plot-1.hires.png differ diff --git a/main/_downloads/af8890f1d73a12db82a79c5489cf8f06/scanspec-specs-2.py b/main/_downloads/af8890f1d73a12db82a79c5489cf8f06/scanspec-specs-2.py new file mode 100644 index 00000000..4a2e2b75 --- /dev/null +++ b/main/_downloads/af8890f1d73a12db82a79c5489cf8f06/scanspec-specs-2.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line + +spec = 2 * ~Line.bounded("x", 3, 4, 1) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/b0cff8737e1b445e779e40c4781f03db/scanspec-specs-11.hires.png b/main/_downloads/b0cff8737e1b445e779e40c4781f03db/scanspec-specs-11.hires.png new file mode 100644 index 00000000..f2515a21 Binary files /dev/null and b/main/_downloads/b0cff8737e1b445e779e40c4781f03db/scanspec-specs-11.hires.png differ diff --git a/main/_downloads/b56b9e73e96faf1b75092b65a5c06b6c/scanspec-regions-1.png b/main/_downloads/b56b9e73e96faf1b75092b65a5c06b6c/scanspec-regions-1.png new file mode 100644 index 00000000..993c099c Binary files /dev/null and b/main/_downloads/b56b9e73e96faf1b75092b65a5c06b6c/scanspec-regions-1.png differ diff --git a/main/_downloads/b5b911e9e126d0860f4da9a6a4bafafb/scanspec-specs-1.png b/main/_downloads/b5b911e9e126d0860f4da9a6a4bafafb/scanspec-specs-1.png new file mode 100644 index 00000000..812fd598 Binary files /dev/null and b/main/_downloads/b5b911e9e126d0860f4da9a6a4bafafb/scanspec-specs-1.png differ diff --git a/main/_downloads/b93b66585638eb26b1622a0b98b703fd/scanspec-specs-6.pdf b/main/_downloads/b93b66585638eb26b1622a0b98b703fd/scanspec-specs-6.pdf new file mode 100644 index 00000000..b0f52434 Binary files /dev/null and b/main/_downloads/b93b66585638eb26b1622a0b98b703fd/scanspec-specs-6.pdf differ diff --git a/main/_downloads/ba43228fe3b2f308e04e5ea9c1c6c121/scanspec-specs-12.hires.png b/main/_downloads/ba43228fe3b2f308e04e5ea9c1c6c121/scanspec-specs-12.hires.png new file mode 100644 index 00000000..39220889 Binary files /dev/null and b/main/_downloads/ba43228fe3b2f308e04e5ea9c1c6c121/scanspec-specs-12.hires.png differ diff --git a/main/_downloads/bb03a5082403681ddefda243d79d914e/scanspec-specs-15.py b/main/_downloads/bb03a5082403681ddefda243d79d914e/scanspec-specs-15.py new file mode 100644 index 00000000..34e91b66 --- /dev/null +++ b/main/_downloads/bb03a5082403681ddefda243d79d914e/scanspec-specs-15.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line, fly + +spec = fly(Line("x", 1, 2, 3), 0.1) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/bd59a3a4d280d4586f4bb2550db6f079/scanspec-specs-7.pdf b/main/_downloads/bd59a3a4d280d4586f4bb2550db6f079/scanspec-specs-7.pdf new file mode 100644 index 00000000..828322d3 Binary files /dev/null and b/main/_downloads/bd59a3a4d280d4586f4bb2550db6f079/scanspec-specs-7.pdf differ diff --git a/main/_downloads/c2faec58e8a7872b8c73e00e780c4f8b/creating-a-spec-2.pdf b/main/_downloads/c2faec58e8a7872b8c73e00e780c4f8b/creating-a-spec-2.pdf index 465eef1b..f2af4ef4 100644 Binary files a/main/_downloads/c2faec58e8a7872b8c73e00e780c4f8b/creating-a-spec-2.pdf and b/main/_downloads/c2faec58e8a7872b8c73e00e780c4f8b/creating-a-spec-2.pdf differ diff --git a/main/_downloads/c65de3ee054161d71e4983fd6c960f0d/scanspec-plot-1.pdf b/main/_downloads/c65de3ee054161d71e4983fd6c960f0d/scanspec-plot-1.pdf new file mode 100644 index 00000000..75055a9f Binary files /dev/null and b/main/_downloads/c65de3ee054161d71e4983fd6c960f0d/scanspec-plot-1.pdf differ diff --git a/main/_downloads/cd8824473bc8e9eb03d621cae1bb4e55/scanspec-plot-1.py b/main/_downloads/cd8824473bc8e9eb03d621cae1bb4e55/scanspec-plot-1.py new file mode 100644 index 00000000..87c0f949 --- /dev/null +++ b/main/_downloads/cd8824473bc8e9eb03d621cae1bb4e55/scanspec-plot-1.py @@ -0,0 +1,9 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line +from scanspec.regions import Circle + +cube = Line("z", 1, 3, 3) * Line("y", 1, 3, 10) * ~Line("x", 0, 2, 10) +spec = cube & Circle("x", "y", 1, 2, 0.9) +plot_spec(spec) \ No newline at end of file diff --git a/main/_downloads/d07484c8c66eeff187b2311ac0fa61c9/scanspec-specs-2.png b/main/_downloads/d07484c8c66eeff187b2311ac0fa61c9/scanspec-specs-2.png new file mode 100644 index 00000000..7995543e Binary files /dev/null and b/main/_downloads/d07484c8c66eeff187b2311ac0fa61c9/scanspec-specs-2.png differ diff --git a/main/_downloads/d4a857bafcf09d241d2ddbc974c3eab8/scanspec-specs-1.pdf b/main/_downloads/d4a857bafcf09d241d2ddbc974c3eab8/scanspec-specs-1.pdf new file mode 100644 index 00000000..4662140a Binary files /dev/null and b/main/_downloads/d4a857bafcf09d241d2ddbc974c3eab8/scanspec-specs-1.pdf differ diff --git a/main/_downloads/d951a913ef49c5656aca72d4ec3d6127/scanspec-specs-11.pdf b/main/_downloads/d951a913ef49c5656aca72d4ec3d6127/scanspec-specs-11.pdf new file mode 100644 index 00000000..1b1d5edd Binary files /dev/null and b/main/_downloads/d951a913ef49c5656aca72d4ec3d6127/scanspec-specs-11.pdf differ diff --git a/main/_downloads/de54f45614a78e61d0c3cb83aa563617/scanspec-specs-15.hires.png b/main/_downloads/de54f45614a78e61d0c3cb83aa563617/scanspec-specs-15.hires.png new file mode 100644 index 00000000..bcff2c8a Binary files /dev/null and b/main/_downloads/de54f45614a78e61d0c3cb83aa563617/scanspec-specs-15.hires.png differ diff --git a/main/_downloads/dec387273dfbf951bfe5bf2f7358770c/scanspec-specs-2.hires.png b/main/_downloads/dec387273dfbf951bfe5bf2f7358770c/scanspec-specs-2.hires.png new file mode 100644 index 00000000..9d31c010 Binary files /dev/null and b/main/_downloads/dec387273dfbf951bfe5bf2f7358770c/scanspec-specs-2.hires.png differ diff --git a/main/_downloads/df3bd7e446b0e9d8f00890cc805d8d77/scanspec-specs-11.png b/main/_downloads/df3bd7e446b0e9d8f00890cc805d8d77/scanspec-specs-11.png new file mode 100644 index 00000000..521ccbff Binary files /dev/null and b/main/_downloads/df3bd7e446b0e9d8f00890cc805d8d77/scanspec-specs-11.png differ diff --git a/main/_downloads/df45abd6cf3a536dc40edb8915007a0e/scanspec-specs-9.pdf b/main/_downloads/df45abd6cf3a536dc40edb8915007a0e/scanspec-specs-9.pdf new file mode 100644 index 00000000..96d94ad8 Binary files /dev/null and b/main/_downloads/df45abd6cf3a536dc40edb8915007a0e/scanspec-specs-9.pdf differ diff --git a/main/_downloads/e34655e18a22651d180c110d404698d0/scanspec-specs-4.png b/main/_downloads/e34655e18a22651d180c110d404698d0/scanspec-specs-4.png new file mode 100644 index 00000000..15dd400a Binary files /dev/null and b/main/_downloads/e34655e18a22651d180c110d404698d0/scanspec-specs-4.png differ diff --git a/main/_downloads/e8de6363062664933e4168a9325eb35c/scanspec-specs-10.hires.png b/main/_downloads/e8de6363062664933e4168a9325eb35c/scanspec-specs-10.hires.png new file mode 100644 index 00000000..8caca7cd Binary files /dev/null and b/main/_downloads/e8de6363062664933e4168a9325eb35c/scanspec-specs-10.hires.png differ diff --git a/main/_downloads/e97e6c4ea088783de917c4b5859e1595/why-squash-can-change-path-1.pdf b/main/_downloads/e97e6c4ea088783de917c4b5859e1595/why-squash-can-change-path-1.pdf index 0910701a..dfdeee10 100644 Binary files a/main/_downloads/e97e6c4ea088783de917c4b5859e1595/why-squash-can-change-path-1.pdf and b/main/_downloads/e97e6c4ea088783de917c4b5859e1595/why-squash-can-change-path-1.pdf differ diff --git a/main/_downloads/ec8d232d0953cdaac6529cb67293b0e8/creating-a-spec-5.pdf b/main/_downloads/ec8d232d0953cdaac6529cb67293b0e8/creating-a-spec-5.pdf index b5795571..9d34a072 100644 Binary files a/main/_downloads/ec8d232d0953cdaac6529cb67293b0e8/creating-a-spec-5.pdf and b/main/_downloads/ec8d232d0953cdaac6529cb67293b0e8/creating-a-spec-5.pdf differ diff --git a/main/_downloads/edf4182424d7c4118287d2ea50bef6bb/why-squash-can-change-path-2.pdf b/main/_downloads/edf4182424d7c4118287d2ea50bef6bb/why-squash-can-change-path-2.pdf index 73f41a8c..f527cc32 100644 Binary files a/main/_downloads/edf4182424d7c4118287d2ea50bef6bb/why-squash-can-change-path-2.pdf and b/main/_downloads/edf4182424d7c4118287d2ea50bef6bb/why-squash-can-change-path-2.pdf differ diff --git a/main/_downloads/f5c85345bb31322c4ff4cfb2708fc297/scanspec-specs-3.py b/main/_downloads/f5c85345bb31322c4ff4cfb2708fc297/scanspec-specs-3.py new file mode 100644 index 00000000..00a898c7 --- /dev/null +++ b/main/_downloads/f5c85345bb31322c4ff4cfb2708fc297/scanspec-specs-3.py @@ -0,0 +1,7 @@ +# Example Spec + +from scanspec.plot import plot_spec +from scanspec.specs import Line, Repeat + +spec = Repeat(2, gap=False) * ~Line.bounded("x", 3, 4, 1) +plot_spec(spec) \ No newline at end of file diff --git a/main/_images/inheritance-28c13a050e6432c87e6edb9e7bddd3f3ed2925d6.svg b/main/_images/inheritance-28c13a050e6432c87e6edb9e7bddd3f3ed2925d6.svg new file mode 100644 index 00000000..81eb8d55 --- /dev/null +++ b/main/_images/inheritance-28c13a050e6432c87e6edb9e7bddd3f3ed2925d6.svg @@ -0,0 +1,164 @@ + + +inheritancec3ef874399 + + +Concat + + +Concat + + + + + +Spec + + +Spec + + + + + +Spec->Concat + + + + + +Line + + +Line + + + + + +Spec->Line + + + + + +Mask + + +Mask + + + + + +Spec->Mask + + + + + +Product + + +Product + + + + + +Spec->Product + + + + + +Repeat + + +Repeat + + + + + +Spec->Repeat + + + + + +Snake + + +Snake + + + + + +Spec->Snake + + + + + +Spiral + + +Spiral + + + + + +Spec->Spiral + + + + + +Squash + + +Squash + + + + + +Spec->Squash + + + + + +Static + + +Static + + + + + +Spec->Static + + + + + +Zip + + +Zip + + + + + +Spec->Zip + + + + + \ No newline at end of file diff --git a/main/_images/inheritance-d6c9411b15bb8ebbc1df3c96fe54b3762b90c335.svg b/main/_images/inheritance-d6c9411b15bb8ebbc1df3c96fe54b3762b90c335.svg new file mode 100644 index 00000000..a9870d2d --- /dev/null +++ b/main/_images/inheritance-d6c9411b15bb8ebbc1df3c96fe54b3762b90c335.svg @@ -0,0 +1,164 @@ + + +inheritance392524bb86 + + +Circle + + +Circle + + + + + +Region + + +Region + + + + + +Region->Circle + + + + + +CombinationOf + + +CombinationOf + + + + + +Region->CombinationOf + + + + + +Ellipse + + +Ellipse + + + + + +Region->Ellipse + + + + + +Polygon + + +Polygon + + + + + +Region->Polygon + + + + + +Range + + +Range + + + + + +Region->Range + + + + + +Rectangle + + +Rectangle + + + + + +Region->Rectangle + + + + + +DifferenceOf + + +DifferenceOf + + + + + +CombinationOf->DifferenceOf + + + + + +IntersectionOf + + +IntersectionOf + + + + + +CombinationOf->IntersectionOf + + + + + +SymmetricDifferenceOf + + +SymmetricDifferenceOf + + + + + +CombinationOf->SymmetricDifferenceOf + + + + + +UnionOf + + +UnionOf + + + + + +CombinationOf->UnionOf + + + + + \ No newline at end of file diff --git a/main/_images/scanspec-plot-1.png b/main/_images/scanspec-plot-1.png new file mode 100644 index 00000000..094d4d1a Binary files /dev/null and b/main/_images/scanspec-plot-1.png differ diff --git a/main/_images/scanspec-regions-1.png b/main/_images/scanspec-regions-1.png new file mode 100644 index 00000000..993c099c Binary files /dev/null and b/main/_images/scanspec-regions-1.png differ diff --git a/main/_images/scanspec-regions-2.png b/main/_images/scanspec-regions-2.png new file mode 100644 index 00000000..bb1adfdb Binary files /dev/null and b/main/_images/scanspec-regions-2.png differ diff --git a/main/_images/scanspec-regions-3.png b/main/_images/scanspec-regions-3.png new file mode 100644 index 00000000..a8acaf00 Binary files /dev/null and b/main/_images/scanspec-regions-3.png differ diff --git a/main/_images/scanspec-regions-4.png b/main/_images/scanspec-regions-4.png new file mode 100644 index 00000000..27e11047 Binary files /dev/null and b/main/_images/scanspec-regions-4.png differ diff --git a/main/_images/scanspec-specs-1.png b/main/_images/scanspec-specs-1.png new file mode 100644 index 00000000..812fd598 Binary files /dev/null and b/main/_images/scanspec-specs-1.png differ diff --git a/main/_images/scanspec-specs-10.png b/main/_images/scanspec-specs-10.png new file mode 100644 index 00000000..d6daa85e Binary files /dev/null and b/main/_images/scanspec-specs-10.png differ diff --git a/main/_images/scanspec-specs-11.png b/main/_images/scanspec-specs-11.png new file mode 100644 index 00000000..521ccbff Binary files /dev/null and b/main/_images/scanspec-specs-11.png differ diff --git a/main/_images/scanspec-specs-12.png b/main/_images/scanspec-specs-12.png new file mode 100644 index 00000000..9e71796d Binary files /dev/null and b/main/_images/scanspec-specs-12.png differ diff --git a/main/_images/scanspec-specs-13.png b/main/_images/scanspec-specs-13.png new file mode 100644 index 00000000..64604a20 Binary files /dev/null and b/main/_images/scanspec-specs-13.png differ diff --git a/main/_images/scanspec-specs-14.png b/main/_images/scanspec-specs-14.png new file mode 100644 index 00000000..88156f69 Binary files /dev/null and b/main/_images/scanspec-specs-14.png differ diff --git a/main/_images/scanspec-specs-15.png b/main/_images/scanspec-specs-15.png new file mode 100644 index 00000000..cd0b2340 Binary files /dev/null and b/main/_images/scanspec-specs-15.png differ diff --git a/main/_images/scanspec-specs-16.png b/main/_images/scanspec-specs-16.png new file mode 100644 index 00000000..74202a1d Binary files /dev/null and b/main/_images/scanspec-specs-16.png differ diff --git a/main/_images/scanspec-specs-2.png b/main/_images/scanspec-specs-2.png new file mode 100644 index 00000000..7995543e Binary files /dev/null and b/main/_images/scanspec-specs-2.png differ diff --git a/main/_images/scanspec-specs-3.png b/main/_images/scanspec-specs-3.png new file mode 100644 index 00000000..b1a79d80 Binary files /dev/null and b/main/_images/scanspec-specs-3.png differ diff --git a/main/_images/scanspec-specs-4.png b/main/_images/scanspec-specs-4.png new file mode 100644 index 00000000..15dd400a Binary files /dev/null and b/main/_images/scanspec-specs-4.png differ diff --git a/main/_images/scanspec-specs-5.png b/main/_images/scanspec-specs-5.png new file mode 100644 index 00000000..5f74c50b Binary files /dev/null and b/main/_images/scanspec-specs-5.png differ diff --git a/main/_images/scanspec-specs-6.png b/main/_images/scanspec-specs-6.png new file mode 100644 index 00000000..bd687072 Binary files /dev/null and b/main/_images/scanspec-specs-6.png differ diff --git a/main/_images/scanspec-specs-7.png b/main/_images/scanspec-specs-7.png new file mode 100644 index 00000000..62fc4e12 Binary files /dev/null and b/main/_images/scanspec-specs-7.png differ diff --git a/main/_images/scanspec-specs-8.png b/main/_images/scanspec-specs-8.png new file mode 100644 index 00000000..6cf49529 Binary files /dev/null and b/main/_images/scanspec-specs-8.png differ diff --git a/main/_images/scanspec-specs-9.png b/main/_images/scanspec-specs-9.png new file mode 100644 index 00000000..04509ee2 Binary files /dev/null and b/main/_images/scanspec-specs-9.png differ diff --git a/main/_modules/index.html b/main/_modules/index.html index 9ebe7f97..402745b6 100644 --- a/main/_modules/index.html +++ b/main/_modules/index.html @@ -7,7 +7,7 @@ - Overview: module code — scanspec 0.7.3.dev3+gf5251e88 documentation + Overview: module code — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -28,6 +28,7 @@ + @@ -38,7 +39,7 @@ - + @@ -471,7 +472,6 @@

All modules for which code is available

  • scanspec.core
  • scanspec.plot
  • scanspec.regions
  • -
  • scanspec.service
  • scanspec.specs
  • diff --git a/main/_modules/scanspec/core.html b/main/_modules/scanspec/core.html index c43fc1ef..e3788a81 100644 --- a/main/_modules/scanspec/core.html +++ b/main/_modules/scanspec/core.html @@ -7,7 +7,7 @@ - scanspec.core — scanspec 0.7.3.dev3+gf5251e88 documentation + scanspec.core — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -28,6 +28,7 @@ + @@ -38,7 +39,7 @@ - + @@ -470,7 +471,9 @@

    Source code for scanspec.core

    -from __future__ import annotations
    +"""Core classes like `Frames` and `Path`."""
    +
    +from __future__ import annotations
     
     from collections.abc import Callable, Iterable, Iterator, Sequence
     from functools import lru_cache
    @@ -497,15 +500,14 @@ 

    Source code for scanspec.core

         "StrictConfig",
     ]
     
    -
    +#: Used to ensure pydantic dataclasses error if given extra arguments
     StrictConfig: ConfigDict = {"extra": "forbid"}
     
     C = TypeVar("C")
    -T = TypeVar("T", type, Callable)
     
     
     
    -[docs] +[docs] def discriminated_union_of_subclasses( super_cls: type[C], discriminator: str = "type", @@ -518,8 +520,7 @@

    Source code for scanspec.core

     
         Subclasses that extend this class must be Pydantic dataclasses, and types that
         need their schema to be updated when a new type that extends super_cls is
    -    created must be either Pydantic dataclasses or BaseModels, and must be decorated
    -    with @uses_tagged_union.
    +    created must be either Pydantic dataclasses or BaseModels.
     
         Example::
     
    @@ -580,6 +581,7 @@ 

    Source code for scanspec.core

         Returns:
             Type: decorated superclass with handling for subclasses to be added
                 to its discriminated union for deserialization
    +
         """
         tagged_union = _TaggedUnion(super_cls, discriminator)
         _tagged_unions[super_cls] = tagged_union
    @@ -649,7 +651,7 @@ 

    Source code for scanspec.core

     
     
     
    -[docs] +[docs] def if_instance_do(x: Any, cls: type, func: Callable): """If x is of type cls then return func(x), otherwise return NotImplemented. @@ -671,7 +673,7 @@

    Source code for scanspec.core

     
     
     
    -[docs] +[docs] class Frames(Generic[Axis]): """Represents a series of scan frames along a number of axes. @@ -697,6 +699,7 @@

    Source code for scanspec.core

     
         See Also:
             `technical-terms`
    +
         """
     
         def __init__(
    @@ -741,7 +744,7 @@ 

    Source code for scanspec.core

             assert len(lengths) <= 1, f"Mismatching lengths {list(lengths)}"
     
     
    -[docs] +[docs] def axes(self) -> list[Axis]: """The axes which will move during the scan. @@ -756,7 +759,7 @@

    Source code for scanspec.core

             return len(self.gap)
     
     
    -[docs] +[docs] def extract(self, indices: np.ndarray, calculate_gap=True) -> Frames[Axis]: """Return a new Frames object restricted to the indices provided. @@ -767,6 +770,7 @@

    Source code for scanspec.core

             >>> frames = Frames({"x": np.array([1, 2, 3])})
             >>> frames.extract(np.array([1, 0, 1])).midpoints
             {'x': array([2, 1, 2])}
    +
             """
             dim_indices = indices % len(self)
     
    @@ -785,7 +789,7 @@ 

    Source code for scanspec.core

     
     
     
    -[docs] +[docs] def concat(self, other: Frames[Axis], gap: bool = False) -> Frames[Axis]: """Return a new Frames object concatenating self and other. @@ -800,6 +804,7 @@

    Source code for scanspec.core

             >>> frames2 = Frames({"y": np.array([3, 2, 1]), "x": np.array([4, 5, 6])})
             >>> frames.concat(frames2).midpoints
             {'x': array([1, 2, 3, 4, 5, 6]), 'y': array([6, 5, 4, 3, 2, 1])}
    +
             """
             assert set(self.axes()) == set(
                 other.axes()
    @@ -822,7 +827,7 @@ 

    Source code for scanspec.core

     
     
     
    -[docs] +[docs] def zip(self, other: Frames[Axis]) -> Frames[Axis]: """Return a new Frames object merging self and other. @@ -875,7 +880,7 @@

    Source code for scanspec.core

     
     
     
    -[docs] +[docs] class SnakedFrames(Frames[Axis]): """Like a `Frames` object, but each alternate repetition will run in reverse.""" @@ -892,7 +897,7 @@

    Source code for scanspec.core

             self.gap[0] = False
     
     
    -[docs] +[docs] @classmethod def from_frames(cls, frames: Frames[Axis]) -> SnakedFrames[Axis]: """Create a snaked version of a `Frames` object.""" @@ -900,7 +905,7 @@

    Source code for scanspec.core

     
     
     
    -[docs] +[docs] def extract(self, indices: np.ndarray, calculate_gap=True) -> Frames[Axis]: """Return a new Frames object restricted to the indices provided. @@ -911,6 +916,7 @@

    Source code for scanspec.core

             >>> frames = SnakedFrames({"x": np.array([1, 2, 3])})
             >>> frames.extract(np.array([0, 1, 2, 3, 4, 5])).midpoints
             {'x': array([1, 2, 3, 3, 2, 1])}
    +
             """
             # Calculate the indices
             # E.g for len = 4
    @@ -952,7 +958,7 @@ 

    Source code for scanspec.core

     
     
     
    -[docs] +[docs] def gap_between_frames(frames1: Frames[Axis], frames2: Frames[Axis]) -> bool: """Is there a gap between end of frames1 and start of frames2.""" return any(frames1.upper[a][-1] != frames2.lower[a][0] for a in frames1.axes())
    @@ -960,7 +966,7 @@

    Source code for scanspec.core

     
     
     
    -[docs] +[docs] def squash_frames(stack: list[Frames[Axis]], check_path_changes=True) -> Frames[Axis]: """Squash a stack of nested Frames into a single one. @@ -977,6 +983,7 @@

    Source code for scanspec.core

         >>> fy = Frames({"y": np.array([3, 4])})
         >>> squash_frames([fy, fx]).midpoints
         {'y': array([3, 3, 4, 4]), 'x': array([1, 2, 2, 1])}
    +
         """
         path = Path(stack)
         # Consuming a Path through these Frames performs the squash
    @@ -1014,7 +1021,7 @@ 

    Source code for scanspec.core

     
     
     
    -[docs] +[docs] class Path(Generic[Axis]): """A consumable route through a stack of Frames, representing a scan path. @@ -1027,6 +1034,7 @@

    Source code for scanspec.core

     
         See Also:
             `iterate-a-spec`
    +
         """
     
         def __init__(
    @@ -1045,7 +1053,7 @@ 

    Source code for scanspec.core

                 self.end_index = start + num
     
     
    -[docs] +[docs] def consume(self, num: int | None = None) -> Frames[Axis]: """Consume at most num frames from the Path and return as a Frames object. @@ -1102,7 +1110,7 @@

    Source code for scanspec.core

     
     
     
    -[docs] +[docs] class Midpoints(Generic[Axis]): """Convenience iterable that produces the scan midpoints for each axis. @@ -1123,6 +1131,7 @@

    Source code for scanspec.core

         {'y': np.int64(3), 'x': np.int64(2)}
         {'y': np.int64(4), 'x': np.int64(2)}
         {'y': np.int64(4), 'x': np.int64(1)}
    +
         """
     
         def __init__(self, stack: list[Frames[Axis]]):
    diff --git a/main/_modules/scanspec/plot.html b/main/_modules/scanspec/plot.html
    index 4e512b0c..e344bfd2 100644
    --- a/main/_modules/scanspec/plot.html
    +++ b/main/_modules/scanspec/plot.html
    @@ -7,7 +7,7 @@
       
         
         
    -    scanspec.plot — scanspec 0.7.3.dev3+gf5251e88 documentation
    +    scanspec.plot — scanspec 0.7.3.dev9+ge42f8eba documentation
       
       
       
    @@ -28,6 +28,7 @@
     
     
         
    +    
         
         
         
    @@ -38,7 +39,7 @@
     
       
     
    -    
    +    
         
         
         
    @@ -470,7 +471,9 @@
                     

    Source code for scanspec.plot

    -from collections.abc import Iterator
    +"""`plot_spec` to visualize a scan."""
    +
    +from collections.abc import Iterator
     from itertools import cycle
     from typing import Any
     
    @@ -559,7 +562,7 @@ 

    Source code for scanspec.plot

     
     
     
    -[docs] +[docs] def plot_spec(spec: Spec[Any], title: str | None = None): """Plot a spec, drawing the path taken through the scan. diff --git a/main/_modules/scanspec/regions.html b/main/_modules/scanspec/regions.html index c8add467..4a0215f8 100644 --- a/main/_modules/scanspec/regions.html +++ b/main/_modules/scanspec/regions.html @@ -7,7 +7,7 @@ - scanspec.regions — scanspec 0.7.3.dev3+gf5251e88 documentation + scanspec.regions — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -28,6 +28,7 @@ + @@ -38,7 +39,7 @@ - + @@ -470,7 +471,14 @@

    Source code for scanspec.regions

    -from __future__ import annotations
    +"""`Region` and its subclasses.
    +
    +.. inheritance-diagram:: scanspec.regions
    +    :top-classes: scanspec.regions.Region
    +    :parts: 1
    +"""
    +
    +from __future__ import annotations
     
     from collections.abc import Iterator, Mapping
     from dataclasses import is_dataclass
    @@ -506,7 +514,7 @@ 

    Source code for scanspec.regions

     
     
     
    -[docs] +[docs] @discriminated_union_of_subclasses class Region(Generic[Axis]): """Abstract baseclass for a Region that can `Mask` a `Spec`. @@ -520,15 +528,15 @@

    Source code for scanspec.regions

         """
     
     
    -[docs] - def axis_sets(self) -> list[set[Axis]]: +[docs] + def axis_sets(self) -> list[set[Axis]]: # noqa: D102 """Produce the non-overlapping sets of axes this region spans.""" raise NotImplementedError(self)
    -[docs] - def mask(self, points: AxesPoints[Axis]) -> np.ndarray: +[docs] + def mask(self, points: AxesPoints[Axis]) -> np.ndarray: # noqa: D102 """Produce a mask of which points are in the region.""" raise NotImplementedError(self)
    @@ -546,14 +554,14 @@

    Source code for scanspec.regions

             return if_instance_do(other, Region, lambda o: SymmetricDifferenceOf(self, o))
     
     
    -[docs] +[docs] def serialize(self) -> Mapping[str, Any]: """Serialize the Region to a dictionary.""" return TypeAdapter(Region).dump_python(self)
    -[docs] +[docs] @staticmethod def deserialize(obj) -> Region: """Deserialize a Region from a dictionary.""" @@ -563,7 +571,7 @@

    Source code for scanspec.regions

     
     
     
    -[docs] +[docs] def get_mask(region: Region[Axis], points: AxesPoints[Axis]) -> np.ndarray: """Return a mask of the points inside the region. @@ -595,7 +603,7 @@

    Source code for scanspec.regions

     
     
     
    -[docs] +[docs] @dataclass(config=StrictConfig) class CombinationOf(Region[Axis]): """Abstract baseclass for a combination of two regions, left and right.""" @@ -603,17 +611,20 @@

    Source code for scanspec.regions

         left: Region[Axis] = Field(description="The left-hand Region to combine")
         right: Region[Axis] = Field(description="The right-hand Region to combine")
     
    -    def axis_sets(self) -> list[set[Axis]]:
    +
    +[docs] + def axis_sets(self) -> list[set[Axis]]: # noqa: D102 axis_sets = list( _merge_axis_sets(self.left.axis_sets() + self.right.axis_sets()) ) return axis_sets
    +
    # Naming so we don't clash with typing.Union
    -[docs] +[docs] @dataclass(config=StrictConfig) class UnionOf(CombinationOf[Axis]): """A point is in UnionOf(a, b) if in either a or b. @@ -625,14 +636,17 @@

    Source code for scanspec.regions

         array([False,  True,  True,  True, False])
         """
     
    -    def mask(self, points: AxesPoints[Axis]) -> np.ndarray:
    +
    +[docs] + def mask(self, points: AxesPoints[Axis]) -> np.ndarray: # noqa: D102 mask = get_mask(self.left, points) | get_mask(self.right, points) return mask
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class IntersectionOf(CombinationOf[Axis]): """A point is in IntersectionOf(a, b) if in both a and b. @@ -644,14 +658,17 @@

    Source code for scanspec.regions

         array([False, False,  True, False, False])
         """
     
    -    def mask(self, points: AxesPoints[Axis]) -> np.ndarray:
    +
    +[docs] + def mask(self, points: AxesPoints[Axis]) -> np.ndarray: # noqa: D102 mask = get_mask(self.left, points) & get_mask(self.right, points) return mask
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class DifferenceOf(CombinationOf[Axis]): """A point is in DifferenceOf(a, b) if in a and not in b. @@ -663,16 +680,19 @@

    Source code for scanspec.regions

         array([False,  True, False, False, False])
         """
     
    -    def mask(self, points: AxesPoints[Axis]) -> np.ndarray:
    +
    +[docs] + def mask(self, points: AxesPoints[Axis]) -> np.ndarray: # noqa: D102 left_mask = get_mask(self.left, points) # Return the xor restricted to the left region mask = left_mask ^ get_mask(self.right, points) & left_mask return mask
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class SymmetricDifferenceOf(CombinationOf[Axis]): """A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both. @@ -684,14 +704,17 @@

    Source code for scanspec.regions

         array([False,  True, False,  True, False])
         """
     
    -    def mask(self, points: AxesPoints[Axis]) -> np.ndarray:
    +
    +[docs] + def mask(self, points: AxesPoints[Axis]) -> np.ndarray: # noqa: D102 mask = get_mask(self.left, points) ^ get_mask(self.right, points) return mask
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class Range(Region[Axis]): """Mask contains points of axis >= min and <= max. @@ -705,18 +728,24 @@

    Source code for scanspec.regions

         min: float = Field(description="The minimum inclusive value in the region")
         max: float = Field(description="The minimum inclusive value in the region")
     
    -    def axis_sets(self) -> list[set[Axis]]:
    -        return [{self.axis}]
    +
    +[docs] + def axis_sets(self) -> list[set[Axis]]: # noqa: D102 + return [{self.axis}]
    - def mask(self, points: AxesPoints[Axis]) -> np.ndarray: + +
    +[docs] + def mask(self, points: AxesPoints[Axis]) -> np.ndarray: # noqa: D102 v = points[self.axis] mask = np.bitwise_and(v >= self.min, v <= self.max) return mask
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class Rectangle(Region[Axis]): """Mask contains points of axis within a rotated xy rectangle. @@ -740,10 +769,15 @@

    Source code for scanspec.regions

             description="Clockwise rotation angle of the rectangle", default=0.0
         )
     
    -    def axis_sets(self) -> list[set[Axis]]:
    -        return [{self.x_axis, self.y_axis}]
    +
    +[docs] + def axis_sets(self) -> list[set[Axis]]: # noqa: D102 + return [{self.x_axis, self.y_axis}]
    - def mask(self, points: AxesPoints[Axis]) -> np.ndarray: + +
    +[docs] + def mask(self, points: AxesPoints[Axis]) -> np.ndarray: # noqa: D102 x = points[self.x_axis] - self.x_min y = points[self.y_axis] - self.y_min if self.angle != 0: @@ -756,11 +790,12 @@

    Source code for scanspec.regions

             mask_x = np.bitwise_and(x >= 0, x <= (self.x_max - self.x_min))
             mask_y = np.bitwise_and(y >= 0, y <= (self.y_max - self.y_min))
             return mask_x & mask_y
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class Polygon(Region[Axis]): """Mask contains points of axis within a rotated xy polygon. @@ -783,10 +818,15 @@

    Source code for scanspec.regions

             description="The Nx1 y coordinates of the polygons vertices", min_length=3
         )
     
    -    def axis_sets(self) -> list[set[Axis]]:
    -        return [{self.x_axis, self.y_axis}]
    +
    +[docs] + def axis_sets(self) -> list[set[Axis]]: # noqa: D102 + return [{self.x_axis, self.y_axis}]
    - def mask(self, points: AxesPoints[Axis]) -> np.ndarray: + +
    +[docs] + def mask(self, points: AxesPoints[Axis]) -> np.ndarray: # noqa: D102 x = points[self.x_axis] y = points[self.y_axis] v1x, v1y = self.x_verts[-1], self.y_verts[-1] @@ -802,11 +842,12 @@

    Source code for scanspec.regions

                     mask ^= vmask
                 v1x, v1y = v2x, v2y
             return mask
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class Circle(Region[Axis]): """Mask contains points of axis within an xy circle of given radius. @@ -826,19 +867,25 @@

    Source code for scanspec.regions

         y_middle: float = Field(description="The central y point of the circle")
         radius: float = Field(description="Radius of the circle", gt=0)
     
    -    def axis_sets(self) -> list[set[Axis]]:
    -        return [{self.x_axis, self.y_axis}]
    +
    +[docs] + def axis_sets(self) -> list[set[Axis]]: # noqa: D102 + return [{self.x_axis, self.y_axis}]
    - def mask(self, points: AxesPoints[Axis]) -> np.ndarray: + +
    +[docs] + def mask(self, points: AxesPoints[Axis]) -> np.ndarray: # noqa: D102 x = points[self.x_axis] - self.x_middle y = points[self.y_axis] - self.y_middle mask = x * x + y * y <= (self.radius * self.radius) return mask
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class Ellipse(Region[Axis]): """Mask contains points of axis within an xy ellipse of given radius. @@ -864,10 +911,15 @@

    Source code for scanspec.regions

         )
         angle: float = Field(description="The angle of the ellipse (degrees)", default=0.0)
     
    -    def axis_sets(self) -> list[set[Axis]]:
    -        return [{self.x_axis, self.y_axis}]
    +
    +[docs] + def axis_sets(self) -> list[set[Axis]]: # noqa: D102 + return [{self.x_axis, self.y_axis}]
    - def mask(self, points: AxesPoints[Axis]) -> np.ndarray: + +
    +[docs] + def mask(self, points: AxesPoints[Axis]) -> np.ndarray: # noqa: D102 x = points[self.x_axis] - self.x_middle y = points[self.y_axis] - self.y_middle if self.angle != 0: @@ -879,12 +931,13 @@

    Source code for scanspec.regions

                 y = ty
             mask = (x / self.x_radius) ** 2 + (y / self.y_radius) ** 2 <= 1
             return mask
    +
    -[docs] -def find_regions(obj) -> Iterator[Region[Axis]]: +[docs] +def find_regions(obj) -> Iterator[Region]: """Recursively yield Regions from obj and its children.""" if ( hasattr(obj, "__pydantic_model__") @@ -894,7 +947,7 @@

    Source code for scanspec.regions

             if isinstance(obj, Region):
                 yield obj
             for name in obj.__dict__.keys():
    -            regions: Iterator[Region[Axis]] = find_regions(getattr(obj, name))
    +            regions: Iterator[Region] = find_regions(getattr(obj, name))
                 yield from regions
    diff --git a/main/_modules/scanspec/specs.html b/main/_modules/scanspec/specs.html index abde125d..4aad2087 100644 --- a/main/_modules/scanspec/specs.html +++ b/main/_modules/scanspec/specs.html @@ -7,7 +7,7 @@ - scanspec.specs — scanspec 0.7.3.dev3+gf5251e88 documentation + scanspec.specs — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -28,6 +28,7 @@ + @@ -38,7 +39,7 @@ - + @@ -470,7 +471,14 @@

    Source code for scanspec.specs

    -from __future__ import annotations
    +"""`Spec` and its subclasses.
    +
    +.. inheritance-diagram:: scanspec.specs
    +    :top-classes: scanspec.specs.Spec
    +    :parts: 1
    +"""
    +
    +from __future__ import annotations
     
     from collections.abc import Callable, Mapping
     from typing import (
    @@ -519,7 +527,7 @@ 

    Source code for scanspec.specs

     
     
     
    -[docs] +[docs] @discriminated_union_of_subclasses class Spec(Generic[Axis]): """A serializable representation of the type and parameters of a scan. @@ -533,8 +541,8 @@

    Source code for scanspec.specs

         """
     
     
    -[docs] - def axes(self) -> list[Axis]: +[docs] + def axes(self) -> list[Axis]: # noqa: D102 """Return the list of axes that are present in the scan. Ordered from slowest moving to fastest moving. @@ -543,8 +551,8 @@

    Source code for scanspec.specs

     
     
     
    -[docs] - def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: +[docs] + def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: # noqa: D102 """Produce a stack of nested `Frames` that form the scan. Ordered from slowest moving to fastest moving. @@ -553,21 +561,21 @@

    Source code for scanspec.specs

     
     
     
    -[docs] +[docs] def frames(self) -> Frames[Axis]: """Expand all the scan `Frames` and return them.""" return Path(self.calculate()).consume()
    -[docs] +[docs] def midpoints(self) -> Midpoints[Axis]: """Return `Midpoints` that can be iterated point by point.""" return Midpoints(self.calculate(bounds=False))
    -[docs] +[docs] def shape(self) -> tuple[int, ...]: """Return the final, simplified shape of the scan.""" return tuple(len(dim) for dim in self.calculate())
    @@ -586,28 +594,28 @@

    Source code for scanspec.specs

             return Snake(self)
     
     
    -[docs] +[docs] def zip(self, other: Spec) -> Zip[Axis]: """`Zip` the Spec with another, iterating in tandem.""" return Zip(self, other)
    -[docs] +[docs] def concat(self, other: Spec) -> Concat[Axis]: """`Concat` the Spec with another, iterating one after the other.""" return Concat(self, other)
    -[docs] +[docs] def serialize(self) -> Mapping[str, Any]: """Serialize the Spec to a dictionary.""" return TypeAdapter(Spec).dump_python(self)
    -[docs] +[docs] @staticmethod def deserialize(obj) -> Spec: """Deserialize a Spec from a dictionary.""" @@ -617,7 +625,7 @@

    Source code for scanspec.specs

     
     
     
    -[docs] +[docs] @dataclass(config=StrictConfig) class Product(Spec[Axis]): """Outer product of two Specs, nesting inner within outer. @@ -634,18 +642,24 @@

    Source code for scanspec.specs

         outer: Spec[Axis] = Field(description="Will be executed once")
         inner: Spec[Axis] = Field(description="Will be executed len(outer) times")
     
    -    def axes(self) -> list:
    -        return self.outer.axes() + self.inner.axes()
    +
    +[docs] + def axes(self) -> list[Axis]: # noqa: D102 + return self.outer.axes() + self.inner.axes()
    + - def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: +
    +[docs] + def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: # noqa: D102 frames_outer = self.outer.calculate(bounds=False, nested=nested) frames_inner = self.inner.calculate(bounds, nested=True) return frames_outer + frames_inner
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class Repeat(Spec[Axis]): """Repeat an empty frame num times. @@ -676,16 +690,22 @@

    Source code for scanspec.specs

             default=True,
         )
     
    -    def axes(self) -> list:
    -        return []
    +
    +[docs] + def axes(self) -> list[Axis]: # noqa: D102 + return []
    + - def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: +
    +[docs] + def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: # noqa: D102 return [Frames({}, gap=np.full(self.num, self.gap))]
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class Zip(Spec[Axis]): """Run two Specs in parallel, merging their midpoints together. @@ -716,10 +736,15 @@

    Source code for scanspec.specs

             description="The right-hand Spec to Zip, will appear later in axes"
         )
     
    -    def axes(self) -> list:
    -        return self.left.axes() + self.right.axes()
    +
    +[docs] + def axes(self) -> list[Axis]: # noqa: D102 + return self.left.axes() + self.right.axes()
    + - def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: +
    +[docs] + def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: # noqa: D102 frames_left = self.left.calculate(bounds, nested) frames_right = self.right.calculate(bounds, nested) assert len(frames_left) >= len( @@ -755,11 +780,12 @@

    Source code for scanspec.specs

                 ), f"Padding went wrong {frames_left} {padded_right}"
                 frames.append(combined)
             return frames
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class Mask(Spec[Axis]): """Restrict Spec to only midpoints that fall inside the given Region. @@ -787,10 +813,15 @@

    Source code for scanspec.specs

             default=True,
         )
     
    -    def axes(self) -> list:
    -        return self.spec.axes()
    +
    +[docs] + def axes(self) -> list[Axis]: # noqa: D102 + return self.spec.axes()
    + - def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: +
    +[docs] + def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: # noqa: D102 frames = self.spec.calculate(bounds, nested) for axis_set in self.region.axis_sets(): # Find the start and end index of any dimensions containing these axes @@ -809,7 +840,8 @@

    Source code for scanspec.specs

             for f in frames:
                 indices = get_mask(self.region, f.midpoints).nonzero()[0]
                 masked_frames.append(f.extract(indices))
    -        return masked_frames
    +        return masked_frames
    + # *+ bind more tightly than &|^ so without these overrides we # would need to add brackets to all combinations of Regions @@ -830,7 +862,7 @@

    Source code for scanspec.specs

     
     
     
    -[docs] +[docs] @dataclass(config=StrictConfig) class Snake(Spec[Axis]): """Run the Spec in reverse on every other iteration when nested. @@ -848,19 +880,25 @@

    Source code for scanspec.specs

             description="The Spec to run in reverse every other iteration"
         )
     
    -    def axes(self) -> list:
    -        return self.spec.axes()
    +
    +[docs] + def axes(self) -> list[Axis]: # noqa: D102 + return self.spec.axes()
    + - def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: +
    +[docs] + def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: # noqa: D102 return [ SnakedFrames.from_frames(segment) for segment in self.spec.calculate(bounds, nested) ]
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class Concat(Spec[Axis]): """Concatenate two Specs together, running one after the other. @@ -890,14 +928,19 @@

    Source code for scanspec.specs

             default=True,
         )
     
    -    def axes(self) -> list:
    +
    +[docs] + def axes(self) -> list[Axis]: # noqa: D102 left_axes, right_axes = self.left.axes(), self.right.axes() # Assuming the axes are the same, the order does not matter, we inherit the # order from the left-hand side. See also scanspec.core.concat. assert set(left_axes) == set(right_axes), f"axes {left_axes} != {right_axes}" - return left_axes + return left_axes
    + - def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: +
    +[docs] + def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: # noqa: D102 dim_left = squash_frames( self.left.calculate(bounds, nested), nested and self.check_path_changes ) @@ -906,11 +949,12 @@

    Source code for scanspec.specs

             )
             dim = dim_left.concat(dim_right, self.gap)
             return [dim]
    +
    -[docs] +[docs] @dataclass(config=StrictConfig) class Squash(Spec[Axis]): """Squash a stack of Frames together into a single expanded Frames object. @@ -923,6 +967,7 @@

    Source code for scanspec.specs

             from scanspec.specs import Line, Squash
     
             spec = Squash(Line("y", 1, 2, 3) * Line("x", 0, 1, 4))
    +
         """
     
         spec: Spec[Axis] = Field(description="The Spec to squash the dimensions of")
    @@ -931,13 +976,19 @@ 

    Source code for scanspec.specs

             default=True,
         )
     
    -    def axes(self) -> list:
    -        return self.spec.axes()
    +
    +[docs] + def axes(self) -> list[Axis]: # noqa: D102 + return self.spec.axes()
    + - def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: +
    +[docs] + def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: # noqa: D102 dims = self.spec.calculate(bounds, nested) dim = squash_frames(dims, nested and self.check_path_changes) return [dim]
    +
    @@ -969,7 +1020,7 @@

    Source code for scanspec.specs

     
     
     
    -[docs] +[docs] @dataclass(config=StrictConfig) class Line(Spec[Axis]): """Linearly spaced frames with start and stop as first and last midpoints. @@ -986,8 +1037,11 @@

    Source code for scanspec.specs

         stop: float = Field(description="Midpoint of the last point of the line")
         num: int = Field(ge=1, description="Number of frames to produce")
     
    -    def axes(self) -> list:
    -        return [self.axis]
    +
    +[docs] + def axes(self) -> list[Axis]: # noqa: D102 + return [self.axis]
    + def _line_from_indexes(self, indexes: np.ndarray) -> dict[Axis, np.ndarray]: if self.num == 1: @@ -1001,13 +1055,16 @@

    Source code for scanspec.specs

             first = self.start - step / 2
             return {self.axis: indexes * step + first}
     
    -    def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]:
    +
    +[docs] + def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: # noqa: D102 return _dimensions_from_indexes( self._line_from_indexes, self.axes(), self.num, bounds - ) + )
    +
    -[docs] +[docs] @classmethod def bounded( cls, @@ -1044,7 +1101,7 @@

    Source code for scanspec.specs

     
     
     
    -[docs] +[docs] @dataclass(config=StrictConfig) class Static(Spec[Axis]): """A static frame, repeated num times, with axis at value. @@ -1063,7 +1120,7 @@

    Source code for scanspec.specs

         num: int = Field(ge=1, description="Number of frames to produce", default=1)
     
     
    -[docs] +[docs] @classmethod def duration( cls: type[Static], @@ -1081,16 +1138,22 @@

    Source code for scanspec.specs

             return cls(DURATION, duration, num)
    - def axes(self) -> list: - return [self.axis] +
    +[docs] + def axes(self) -> list[Axis]: # noqa: D102 + return [self.axis]
    + def _repeats_from_indexes(self, indexes: np.ndarray) -> dict[Axis, np.ndarray]: return {self.axis: np.full(len(indexes), self.value)} - def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: +
    +[docs] + def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: # noqa: D102 return _dimensions_from_indexes( self._repeats_from_indexes, self.axes(), self.num, bounds )
    +
    @@ -1098,7 +1161,7 @@

    Source code for scanspec.specs

     
     
     
    -[docs] +[docs] @dataclass(config=StrictConfig) class Spiral(Spec[Axis]): """Archimedean spiral of "x_axis" and "y_axis". @@ -1126,9 +1189,12 @@

    Source code for scanspec.specs

             description="How much to rotate the angle of the spiral", default=0.0
         )
     
    -    def axes(self) -> list[Axis]:
    +
    +[docs] + def axes(self) -> list[Axis]: # noqa: D102 # TODO: reversed from __init__ args, a good idea? - return [self.y_axis, self.x_axis] + return [self.y_axis, self.x_axis]
    + def _spiral_from_indexes(self, indexes: np.ndarray) -> dict[Axis, np.ndarray]: # simplest spiral equation: r = phi @@ -1147,13 +1213,16 @@

    Source code for scanspec.specs

                 self.x_axis: self.x_start + x_scale * phi * np.sin(phi + self.rotate),
             }
     
    -    def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]:
    +
    +[docs] + def calculate(self, bounds=True, nested=False) -> list[Frames[Axis]]: # noqa: D102 return _dimensions_from_indexes( self._spiral_from_indexes, self.axes(), self.num, bounds - ) + )
    +
    -[docs] +[docs] @classmethod def spaced( cls, @@ -1192,7 +1261,7 @@

    Source code for scanspec.specs

     
     
     
    -[docs] +[docs] def fly(spec: Spec[Axis], duration: float) -> Spec[Axis]: """Flyscan, zipping with fixed duration for every frame. @@ -1205,13 +1274,14 @@

    Source code for scanspec.specs

             from scanspec.specs import Line, fly
     
             spec = fly(Line("x", 1, 2, 3), 0.1)
    +
         """
         return spec.zip(Static.duration(duration))
    -[docs] +[docs] def step(spec: Spec[Axis], duration: float, num: int = 1) -> Spec[Axis]: """Step scan, with num frames of given duration at each frame in the spec. @@ -1226,14 +1296,14 @@

    Source code for scanspec.specs

             from scanspec.specs import Line, step
     
             spec = step(Line("x", 1, 2, 3), 0.1)
    +
         """
         return spec * Static.duration(duration, num)
    def get_constant_duration(frames: list[Frames]) -> float | None: - """ - Returns the duration of a number of ScanSpec frames, if known and consistent. + """Returns the duration of a number of ScanSpec frames, if known and consistent. Args: frames (List[Frames]): A number of Frame objects diff --git a/main/_sources/_api.rst.txt b/main/_sources/_api.rst.txt new file mode 100644 index 00000000..b4b0a6c9 --- /dev/null +++ b/main/_sources/_api.rst.txt @@ -0,0 +1,16 @@ +:orphan: + +.. + This page is not included in the TOC tree, but must exist so that the + autosummary pages are generated for scanspec and all its + subpackages + +API +=== + +.. autosummary:: + :toctree: _api + :template: custom-module-template.rst + :recursive: + + scanspec diff --git a/main/_sources/_api/scanspec.core.rst.txt b/main/_sources/_api/scanspec.core.rst.txt new file mode 100644 index 00000000..af3829ea --- /dev/null +++ b/main/_sources/_api/scanspec.core.rst.txt @@ -0,0 +1,41 @@ +``scanspec.core`` +================= + + + + + + + + + + + + +.. automodule:: scanspec.core + :members: + + + + + + + + .. rubric:: Members + + .. autosummary:: + :nosignatures: + + if_instance_do + Axis + AxesPoints + Frames + SnakedFrames + gap_between_frames + squash_frames + Path + Midpoints + discriminated_union_of_subclasses + StrictConfig + + \ No newline at end of file diff --git a/main/_sources/_api/scanspec.plot.rst.txt b/main/_sources/_api/scanspec.plot.rst.txt new file mode 100644 index 00000000..b4731281 --- /dev/null +++ b/main/_sources/_api/scanspec.plot.rst.txt @@ -0,0 +1,21 @@ +``scanspec.plot`` +================= + + +.. automodule:: scanspec.plot + :members: + + + + + + + + .. rubric:: Members + + .. autosummary:: + :nosignatures: + + plot_spec + + \ No newline at end of file diff --git a/main/_sources/_api/scanspec.regions.rst.txt b/main/_sources/_api/scanspec.regions.rst.txt new file mode 100644 index 00000000..833ab212 --- /dev/null +++ b/main/_sources/_api/scanspec.regions.rst.txt @@ -0,0 +1,25 @@ +``scanspec.regions`` +==================== + + + + +.. automodule:: scanspec.regions + :members: + + + + + + + + .. rubric:: Members + + .. autosummary:: + :nosignatures: + + Region + get_mask + find_regions + + \ No newline at end of file diff --git a/main/_sources/_api/scanspec.rst.txt b/main/_sources/_api/scanspec.rst.txt new file mode 100644 index 00000000..a17df606 --- /dev/null +++ b/main/_sources/_api/scanspec.rst.txt @@ -0,0 +1,25 @@ +``scanspec`` +============ + +.. automodule:: scanspec + :members: + + + + .. rubric:: Submodules + + .. autosummary:: + :toctree: + :template: custom-module-template.rst + :recursive: + + core + specs + regions + plot + + + + + + \ No newline at end of file diff --git a/main/_sources/_api/scanspec.specs.rst.txt b/main/_sources/_api/scanspec.specs.rst.txt new file mode 100644 index 00000000..18d65b9b --- /dev/null +++ b/main/_sources/_api/scanspec.specs.rst.txt @@ -0,0 +1,27 @@ +``scanspec.specs`` +================== + + + + + +.. automodule:: scanspec.specs + :members: + + + + + + + + .. rubric:: Members + + .. autosummary:: + :nosignatures: + + DURATION + Spec + fly + step + + \ No newline at end of file diff --git a/main/_sources/explanations/why-stack-frames.rst.txt b/main/_sources/explanations/why-stack-frames.rst.txt index 48ef7930..815c93b9 100644 --- a/main/_sources/explanations/why-stack-frames.rst.txt +++ b/main/_sources/explanations/why-stack-frames.rst.txt @@ -1,12 +1,12 @@ Why create a stack of Frames? ============================= -If a `Spec` tells you the parameters of a scan, `Frames` gives you the `Points` -that will let you actually execute the scan. A stack of Frames is interpreted as -nested from slowest moving to fastest moving, so each faster Frames object will -iterate once per position of the slower Frames object. When fly-scanning the -axis will traverse lower-midpoint-upper on the fastest Frames object for each -point in the scan. +If a `Spec` tells you the parameters of a scan, `Frames` gives you the `Points +` that will let you actually execute the scan. A stack of Frames is +interpreted as nested from slowest moving to fastest moving, so each faster +Frames object will iterate once per position of the slower Frames object. When +fly-scanning the axis will traverse lower-midpoint-upper on the fastest Frames +object for each point in the scan. An Example ---------- @@ -63,4 +63,3 @@ which point it destroys the performance of the VDS. For this reason, it is advisable to `Squash` any snaking Specs with the first non-snaking axis above it so that the HDF Dimension will not be snaking. See `./why-squash-can-change-path` for some details on this. - diff --git a/main/_sources/how-to/contribute.md.txt b/main/_sources/how-to/contribute.md.txt index f9c4ca1d..6e419797 100644 --- a/main/_sources/how-to/contribute.md.txt +++ b/main/_sources/how-to/contribute.md.txt @@ -1,2 +1,2 @@ ```{include} ../../.github/CONTRIBUTING.md -``` \ No newline at end of file +``` diff --git a/main/_sources/how-to/run-container.md.txt b/main/_sources/how-to/run-container.md.txt index ab104335..8737adee 100644 --- a/main/_sources/how-to/run-container.md.txt +++ b/main/_sources/how-to/run-container.md.txt @@ -8,7 +8,7 @@ installed are available on [Github Container Registry](https://ghcr.io/bluesky/s To pull the container from github container registry and run: ``` -$ docker run ghcr.io/bluesky/scanspec:main --version +$ docker run ghcr.io/bluesky/scanspec:latest --version ``` -To get a released version, use a numbered release instead of `main`. +To get a released version, use a numbered release instead of `latest`. diff --git a/main/_sources/reference.md.txt b/main/_sources/reference.md.txt index ff85f567..e2ef404a 100644 --- a/main/_sources/reference.md.txt +++ b/main/_sources/reference.md.txt @@ -6,6 +6,7 @@ Technical reference material including APIs and release notes. :maxdepth: 1 :glob: +API <_api/scanspec> reference/* genindex Release Notes diff --git a/main/_sources/tutorials/installation.md.txt b/main/_sources/tutorials/installation.md.txt index 592c9da3..b9c85bd1 100644 --- a/main/_sources/tutorials/installation.md.txt +++ b/main/_sources/tutorials/installation.md.txt @@ -2,7 +2,7 @@ ## Check your version of python -You will need python 3.8 or later. You can check your version of python by +You will need python 3.10 or later. You can check your version of python by typing into a terminal: ``` diff --git a/main/_static/autodoc_pydantic.css b/main/_static/autodoc_pydantic.css new file mode 100644 index 00000000..994a3e54 --- /dev/null +++ b/main/_static/autodoc_pydantic.css @@ -0,0 +1,11 @@ +.autodoc_pydantic_validator_arrow { + padding-left: 8px; + } + +.autodoc_pydantic_collapsable_json { + cursor: pointer; + } + +.autodoc_pydantic_collapsable_erd { + cursor: pointer; + } \ No newline at end of file diff --git a/main/_static/documentation_options.js b/main/_static/documentation_options.js index 753d7389..af8d3394 100644 --- a/main/_static/documentation_options.js +++ b/main/_static/documentation_options.js @@ -1,5 +1,5 @@ const DOCUMENTATION_OPTIONS = { - VERSION: '0.7.3.dev3+gf5251e88', + VERSION: '0.7.3.dev9+ge42f8eba', LANGUAGE: 'en', COLLAPSE_INDEX: false, BUILDER: 'html', diff --git a/main/explanations.html b/main/explanations.html index e61183a7..88451ee9 100644 --- a/main/explanations.html +++ b/main/explanations.html @@ -8,7 +8,7 @@ - Explanations — scanspec 0.7.3.dev3+gf5251e88 documentation + Explanations — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -29,6 +29,7 @@ + @@ -39,7 +40,7 @@ - + diff --git a/main/explanations/decisions.html b/main/explanations/decisions.html index 072cc841..d1619fc6 100644 --- a/main/explanations/decisions.html +++ b/main/explanations/decisions.html @@ -8,7 +8,7 @@ - Architectural Decision Records — scanspec 0.7.3.dev3+gf5251e88 documentation + Architectural Decision Records — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -29,6 +29,7 @@ + @@ -39,7 +40,7 @@ - + diff --git a/main/explanations/decisions/0001-record-architecture-decisions.html b/main/explanations/decisions/0001-record-architecture-decisions.html index 4eb83000..96538213 100644 --- a/main/explanations/decisions/0001-record-architecture-decisions.html +++ b/main/explanations/decisions/0001-record-architecture-decisions.html @@ -8,7 +8,7 @@ - 1. Record architecture decisions — scanspec 0.7.3.dev3+gf5251e88 documentation + 1. Record architecture decisions — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -29,6 +29,7 @@ + @@ -39,7 +40,7 @@ - + diff --git a/main/explanations/decisions/0002-switched-to-python-copier-template.html b/main/explanations/decisions/0002-switched-to-python-copier-template.html index 51cb0b30..3e6de25a 100644 --- a/main/explanations/decisions/0002-switched-to-python-copier-template.html +++ b/main/explanations/decisions/0002-switched-to-python-copier-template.html @@ -8,7 +8,7 @@ - 2. Adopt python-copier-template for project structure — scanspec 0.7.3.dev3+gf5251e88 documentation + 2. Adopt python-copier-template for project structure — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -29,6 +29,7 @@ + @@ -39,7 +40,7 @@ - + diff --git a/main/explanations/technical-terms.html b/main/explanations/technical-terms.html index 433d5517..edc9b22e 100644 --- a/main/explanations/technical-terms.html +++ b/main/explanations/technical-terms.html @@ -8,7 +8,7 @@ - Technical Terms — scanspec 0.7.3.dev3+gf5251e88 documentation + Technical Terms — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -29,6 +29,7 @@ + @@ -39,7 +40,7 @@ - + @@ -500,16 +501,16 @@ ../_images/definitions.png

    Axis#

    -

    A fixed reference that can be scanned, i.e. a motor or the Frame DURATION. -In the diagram above, the Axis is x. Spec.axes will return the Axes that +

    A fixed reference that can be scanned, i.e. a motor or the Frame DURATION. +In the diagram above, the Axis is x. Spec.axes will return the Axes that should be scanned for a given Spec.

    Point#

    A single or multidimensional location in scan space. In the diagram above, each -Frame is made up of lower, midpoint and upper Points. Midpoints are -available as an interator from Spec.midpoints. Arrays of these are available -as Frames.lower, Frames.midpoints and Frames.upper.

    +Frame is made up of lower, midpoint and upper Points. Midpoints are +available as an interator from Spec.midpoints. Arrays of these are available +as Frames.lower, Frames.midpoints and Frames.upper.

    Frame#

    @@ -520,9 +521,9 @@

    Stack of Frames#

    -

    A repeatable, possibly snaking, series of Frames along a number of Axes. In the diagram above, the whole Line produces a single Frames -object, while a grid scan would be a stack of two Frames objects. A stack of -Frames objects are produced by Spec.calculate.

    +

    A repeatable, possibly snaking, series of Frames along a number of Axes. In the diagram above, the whole Line produces a single Frames +object, while a grid scan would be a stack of two Frames objects. A stack of +Frames objects are produced by Spec.calculate.

    See also

    Why create a stack of Frames?

    @@ -531,7 +532,7 @@

    Path#

    A consumable route through a _stack_. If the Line in the above diagram was -stacked within another line of length 5, then the Path would contain 15 Frames. A Path is created from a list of Frames objects.

    +stacked within another line of length 5, then the Path would contain 15 Frames. A Path is created from a list of Frames objects.

    diff --git a/main/explanations/why-squash-can-change-path.html b/main/explanations/why-squash-can-change-path.html index c575ec3e..345f6c0b 100644 --- a/main/explanations/why-squash-can-change-path.html +++ b/main/explanations/why-squash-can-change-path.html @@ -8,7 +8,7 @@ - Why Squash (and Mask) can change the Path — scanspec 0.7.3.dev3+gf5251e88 documentation + Why Squash (and Mask) can change the Path — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -29,6 +29,7 @@ + @@ -39,7 +40,7 @@ - + @@ -495,8 +496,8 @@

    Why Squash (and Mask) can change the Path#

    -

    A Spec creates a stack of Frames, some of which can be snaking. When snaking, -every other run of that Frames object will run in reverse. Squash and Mask +

    A Spec creates a stack of Frames, some of which can be snaking. When snaking, +every other run of that Frames object will run in reverse. Squash and Mask will merge Frames objects together. This may change the Path compared with the unsquashed version if the squashed Frames objects are nested within a slower Frames object, and so run more than once. This page lists the times where this @@ -504,7 +505,7 @@

    Note

    The cases illustrated below will only be produced if -check_path_changes=False is passed to Squash or Mask, otherwise they +check_path_changes=False is passed to Squash or Mask, otherwise they will fail with ValueError

    diff --git a/main/explanations/why-stack-frames.html b/main/explanations/why-stack-frames.html index b8278d73..1e0b2e8d 100644 --- a/main/explanations/why-stack-frames.html +++ b/main/explanations/why-stack-frames.html @@ -8,7 +8,7 @@ - Why create a stack of Frames? — scanspec 0.7.3.dev3+gf5251e88 documentation + Why create a stack of Frames? — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -29,6 +29,7 @@ + @@ -39,7 +40,7 @@ - + @@ -495,12 +496,11 @@

    Why create a stack of Frames?#

    -

    If a Spec tells you the parameters of a scan, Frames gives you the Points -that will let you actually execute the scan. A stack of Frames is interpreted as -nested from slowest moving to fastest moving, so each faster Frames object will -iterate once per position of the slower Frames object. When fly-scanning the -axis will traverse lower-midpoint-upper on the fastest Frames object for each -point in the scan.

    +

    If a Spec tells you the parameters of a scan, Frames gives you the Points that will let you actually execute the scan. A stack of Frames is +interpreted as nested from slowest moving to fastest moving, so each faster +Frames object will iterate once per position of the slower Frames object. When +fly-scanning the axis will traverse lower-midpoint-upper on the fastest Frames +object for each point in the scan.

    An Example#

    >>> from scanspec.specs import Line
    @@ -518,7 +518,7 @@ 

    An Example{'x': array([1. , 1.5, 2. ])}

    -

    So the Product of two Lines creates a stack of 2 Frames objects, the first +

    So the Product of two Lines creates a stack of 2 Frames objects, the first having the same size as the outer line, and the second having the same size as the inner. Executing the scan will iterate the inner Frames object 6 times, once for each point in the outer Frames object, 18 points in all.

    @@ -537,9 +537,9 @@

    Why not squash them into a flat sequence?#

    Regions will stop the regularity of the nested Frames objects, so will cause them to be squashed into a single Frames object. Taking our example above, if we -Mask the grid with a Circle, then the Line in x won’t have 3 points in +Mask the grid with a Circle, then the Line in x won’t have 3 points in each iteration, the number of points is dependent on y. This means that a -Mask will squash any Specs together referred to by its Regions.

    +Mask will squash any Specs together referred to by its Regions.

    How does this stack relate to HDF5 Dimensions?#

    @@ -550,7 +550,7 @@

    How does this stack relate to HDF5 Dimensions?Squash any snaking Specs with the first non-snaking axis above it +advisable to Squash any snaking Specs with the first non-snaking axis above it so that the HDF Dimension will not be snaking. See Why Squash (and Mask) can change the Path for some details on this.

    diff --git a/main/genindex.html b/main/genindex.html index bce547c6..c9b23576 100644 --- a/main/genindex.html +++ b/main/genindex.html @@ -7,7 +7,7 @@ - Index — scanspec 0.7.3.dev3+gf5251e88 documentation + Index — scanspec 0.7.3.dev9+ge42f8eba documentation @@ -28,6 +28,7 @@ + @@ -38,7 +39,7 @@ - + @@ -421,7 +422,13 @@ aria-label="Section Navigation">