We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bug: Adding a pipeline command after `show` results in a stack trace error
How to reproduce:

```
$ vpype read LabradorLowPoly.svg show stat
========= Stats ========= 
Traceback (most recent call last):
  File "/home/dominik/.local/bin/vpype", line 8, in <module>
    sys.exit(cli())
  File "/home/dominik/.local/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py", line 74, in main
    return super().main(args=preprocess_argument_list(args), **extra)
  File "/home/dominik/.local/lib/python3.8/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/home/dominik/.local/lib/python3.8/site-packages/click/core.py", line 1290, in invoke
    return _process_result(rv)
  File "/home/dominik/.local/lib/python3.8/site-packages/click/core.py", line 1224, in _process_result
    value = ctx.invoke(self.result_callback, value, **ctx.params)
  File "/home/dominik/.local/lib/python3.8/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py", line 128, in process_pipeline
    execute_processors(processors)
  File "/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py", line 212, in execute_processors
    state = proc(state)
  File "/home/dominik/.local/lib/python3.8/site-packages/vpype/decorators.py", line 150, in global_processor
    state.document = f(state.document, *args, **kwargs)
  File "/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/debug.py", line 135, in stat
    print(f"Current page size: {document.page_size}")
AttributeError: 'NoneType' object has no attribute 'page_size'
```

Also true with the classic show:
```
$ vpype read LabradorLowPoly.svg show --classic stat
========= Stats ========= 
Traceback (most recent call last):
  File "/home/dominik/.local/bin/vpype", line 8, in <module>
    sys.exit(cli())
  File "/home/dominik/.local/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py", line 74, in main
    return super().main(args=preprocess_argument_list(args), **extra)
  File "/home/dominik/.local/lib/python3.8/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/home/dominik/.local/lib/python3.8/site-packages/click/core.py", line 1290, in invoke
    return _process_result(rv)
  File "/home/dominik/.local/lib/python3.8/site-packages/click/core.py", line 1224, in _process_result
    value = ctx.invoke(self.result_callback, value, **ctx.params)
  File "/home/dominik/.local/lib/python3.8/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py", line 128, in process_pipeline
    execute_processors(processors)
  File "/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py", line 212, in execute_processors
    state = proc(state)
  File "/home/dominik/.local/lib/python3.8/site-packages/vpype/decorators.py", line 150, in global_processor
    state.document = f(state.document, *args, **kwargs)
  File "/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/debug.py", line 135, in stat
    print(f"Current page size: {document.page_size}")
AttributeError: 'NoneType' object has no attribute 'page_size'
```

Version:
```
$ vpype --version
vpype 1.3.0a0
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `vpype_cli/show.py`
Content:
```
1 import logging
2 
3 import click
4 import numpy as np
5 
6 import vpype as vp
7 import vpype_viewer
8 from vpype_viewer import ViewMode
9 
10 from .cli import cli
11 
12 __all__ = ["show"]
13 
14 COLORS = [
15     (0, 0, 1),
16     (0, 0.5, 0),
17     (1, 0, 0),
18     (0, 0.75, 0.75),
19     (0, 1, 0),
20     (0.75, 0, 0.75),
21     (0.75, 0.75, 0),
22     (0, 0, 0),
23 ]
24 
25 
26 @cli.command(group="Output")
27 @click.option("--classic", is_flag=True, help="Use the classic viewer.")
28 @click.option("--force", is_flag=True, hidden=True, help="Force modern viewer")
29 @click.option("-p", "--show-pen-up", is_flag=True, help="Display pen-up trajectories.")
30 @click.option("-d", "--show-points", is_flag=True, help="Display paths points with dots.")
31 @click.option("-o", "--outline", is_flag=True, help="Display in outline mode (modern only).")
32 @click.option(
33     "-c",
34     "--colorful",
35     is_flag=True,
36     help="Display in outline colorful mode (takes precedence over --outline).",
37 )
38 @click.option("-a", "--show-axes", is_flag=True, help="Display axes (classic only).")
39 @click.option(
40     "-g", "--show-grid", is_flag=True, help="Display grid (implies -a, classic only)."
41 )
42 @click.option(
43     "-h", "--hide-legend", is_flag=True, help="Do not display the legend (classic only)."
44 )
45 @click.option(
46     "-u",
47     "--unit",
48     type=str,
49     default="px",
50     help="Units of the plot (when --show-grid is active, classic only).",
51 )
52 @vp.global_processor
53 def show(
54     document: vp.Document,
55     classic: bool,
56     force: bool,
57     show_pen_up: bool,
58     show_points: bool,
59     outline: bool,
60     colorful: bool,
61     show_axes: bool,
62     show_grid: bool,
63     hide_legend: bool,
64     unit: str,
65 ):
66     """Display the geometry in an graphical user interface.
67 
68     By default, this command use a modern, hardware-accelerated viewer (currently in beta) with
69     a preview mode (adjustable pen width and opacity) and interactive controls to adjust
70     display options. This viewer requires OpenGL 3.3 support.
71 
72     The original, Matplotlib-based viewer is still available with the `--classic` option. The
73     classic viewer does not have interactive controls for display options. Use the command-line
74     options to customize the display.
75     """
76 
77     if not classic:
78         mgl_ok = _test_mgl()
79         if not mgl_ok and not force:
80             classic = True
81             logging.warning("!!! show: ModernGL not available, reverting to classic mode.")
82         elif not mgl_ok and force:
83             logging.warning("!!! show: ModernGL not available but forced to modern mode.")
84 
85     if classic:
86         _show_mpl(
87             document,
88             show_axes,
89             show_grid,
90             show_pen_up,
91             show_points,
92             hide_legend,
93             colorful,
94             unit,
95         )
96     else:
97         view_mode = ViewMode.PREVIEW
98         if outline or show_points:
99             view_mode = ViewMode.OUTLINE
100         if colorful:
101             view_mode = ViewMode.OUTLINE_COLORFUL
102 
103         vpype_viewer.show(
104             document, view_mode=view_mode, show_pen_up=show_pen_up, show_points=show_points
105         )
106 
107 
108 def _test_mgl() -> bool:
109     """Tests availability of ModernGL."""
110     # noinspection PyBroadException
111     try:
112         import glcontext
113 
114         backend = glcontext.default_backend()
115         ctx = backend(mode="standalone", glversion=330)
116         if ctx.load("glProgramUniform1iv") == 0:
117             return False
118     except Exception:
119         return False
120 
121     return True
122 
123 
124 def _show_mpl(
125     document: vp.Document,
126     show_axes: bool,
127     show_grid: bool,
128     show_pen_up: bool,
129     show_points: bool,
130     hide_legend: bool,
131     colorful: bool,
132     unit: str,
133 ):
134     """Display the geometry using matplotlib.
135 
136     By default, only the geometries are displayed without the axis. All geometries are
137     displayed with black. When using the `--colorful` flag, each segment will have a different
138     color (default matplotlib behaviour). This can be useful for debugging purposes.
139     """
140 
141     # deferred import to optimise startup time
142     import matplotlib.collections
143     import matplotlib.pyplot as plt
144 
145     scale = 1 / vp.convert_length(unit)
146 
147     fig = plt.figure()
148     color_idx = 0
149     collections = {}
150 
151     # draw page boundaries
152     if document.page_size:
153         w = document.page_size[0] * scale
154         h = document.page_size[1] * scale
155         dw = 10 * scale
156         plt.plot(
157             np.array([0, 1, 1, 0, 0]) * w,
158             np.array([0, 0, 1, 1, 0]) * h,
159             "-k",
160             lw=0.25,
161             label=None,
162         )
163         plt.fill(
164             np.array([w, w + dw, w + dw, dw, dw, w]),
165             np.array([dw, dw, h + dw, h + dw, h, h]),
166             "k",
167             alpha=0.3,
168             label=None,
169         )
170 
171     for layer_id, lc in document.layers.items():
172         if colorful:
173             color = COLORS[color_idx:] + COLORS[:color_idx]
174             marker_color = "k"
175             color_idx += len(lc)
176         else:
177             color = COLORS[color_idx]  # type: ignore
178             marker_color = [color]  # type: ignore
179             color_idx += 1
180         if color_idx >= len(COLORS):
181             color_idx = color_idx % len(COLORS)
182 
183         layer_lines = matplotlib.collections.LineCollection(
184             (vp.as_vector(line) * scale for line in lc),
185             color=color,
186             lw=1,
187             alpha=0.5,
188             label=str(layer_id),
189         )
190         collections[layer_id] = [layer_lines]
191         plt.gca().add_collection(layer_lines)
192 
193         if show_points:
194             points = np.hstack([line for line in lc]) * scale
195             layer_points = plt.gca().scatter(
196                 points.real, points.imag, marker=".", c=marker_color, s=16
197             )
198             collections[layer_id].append(layer_points)
199 
200         if show_pen_up:
201             pen_up_lines = matplotlib.collections.LineCollection(
202                 (
203                     (vp.as_vector(lc[i])[-1] * scale, vp.as_vector(lc[i + 1])[0] * scale)
204                     for i in range(len(lc) - 1)
205                 ),
206                 color=(0, 0, 0),
207                 lw=0.5,
208                 alpha=0.5,
209             )
210             collections[layer_id].append(pen_up_lines)
211             plt.gca().add_collection(pen_up_lines)
212 
213     plt.gca().invert_yaxis()
214     plt.axis("equal")
215     plt.margins(0, 0)
216 
217     if not hide_legend:
218         lgd = plt.legend(loc="upper right")
219         # we will set up a dict mapping legend line to orig line, and enable
220         # picking on the legend line
221         line_dict = {}
222         for lgd_line, lgd_text in zip(lgd.get_lines(), lgd.get_texts()):
223             lgd_line.set_picker(True)  # 5 pts tolerance
224             lgd_line.set_pickradius(5)
225             layer_id = int(lgd_text.get_text())
226             if layer_id in collections:
227                 line_dict[lgd_line] = collections[layer_id]
228 
229         def on_pick(event):
230             line = event.artist
231             vis = not line_dict[line][0].get_visible()
232             for ln in line_dict[line]:
233                 ln.set_visible(vis)
234 
235             if vis:
236                 line.set_alpha(1.0)
237             else:
238                 line.set_alpha(0.2)
239             fig.canvas.draw()
240 
241         fig.canvas.mpl_connect("pick_event", on_pick)
242 
243     if show_axes or show_grid:
244         plt.axis("on")
245         plt.xlabel(f"[{unit}]")
246         plt.ylabel(f"[{unit}]")
247     else:
248         plt.axis("off")
249     if show_grid:
250         plt.grid("on")
251     plt.show()
252 
253     return document
254
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/vpype_cli/show.py b/vpype_cli/show.py
--- a/vpype_cli/show.py
+++ b/vpype_cli/show.py
@@ -104,6 +104,8 @@
             document, view_mode=view_mode, show_pen_up=show_pen_up, show_points=show_points
         )
 
+    return document
+
 
 def _test_mgl() -> bool:
     """Tests availability of ModernGL."""
@@ -249,5 +251,3 @@
     if show_grid:
         plt.grid("on")
     plt.show()
-
-    return document

{"golden_diff": "diff --git a/vpype_cli/show.py b/vpype_cli/show.py\n--- a/vpype_cli/show.py\n+++ b/vpype_cli/show.py\n@@ -104,6 +104,8 @@\n             document, view_mode=view_mode, show_pen_up=show_pen_up, show_points=show_points\n         )\n \n+    return document\n+\n \n def _test_mgl() -> bool:\n     \"\"\"Tests availability of ModernGL.\"\"\"\n@@ -249,5 +251,3 @@\n     if show_grid:\n         plt.grid(\"on\")\n     plt.show()\n-\n-    return document\n", "issue": "Bug: Adding a pipeline command after `show` results in a stack trace error\nHow to reproduce:\r\n\r\n```\r\n$ vpype read LabradorLowPoly.svg show stat\r\n========= Stats ========= \r\nTraceback (most recent call last):\r\n  File \"/home/dominik/.local/bin/vpype\", line 8, in <module>\r\n    sys.exit(cli())\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/click/core.py\", line 829, in __call__\r\n    return self.main(*args, **kwargs)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py\", line 74, in main\r\n    return super().main(args=preprocess_argument_list(args), **extra)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/click/core.py\", line 782, in main\r\n    rv = self.invoke(ctx)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/click/core.py\", line 1290, in invoke\r\n    return _process_result(rv)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/click/core.py\", line 1224, in _process_result\r\n    value = ctx.invoke(self.result_callback, value, **ctx.params)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/click/core.py\", line 610, in invoke\r\n    return callback(*args, **kwargs)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py\", line 128, in process_pipeline\r\n    execute_processors(processors)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py\", line 212, in execute_processors\r\n    state = proc(state)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/vpype/decorators.py\", line 150, in global_processor\r\n    state.document = f(state.document, *args, **kwargs)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/debug.py\", line 135, in stat\r\n    print(f\"Current page size: {document.page_size}\")\r\nAttributeError: 'NoneType' object has no attribute 'page_size'\r\n```\r\n\r\nAlso true with the classic show:\r\n```\r\n$ vpype read LabradorLowPoly.svg show --classic stat\r\n========= Stats ========= \r\nTraceback (most recent call last):\r\n  File \"/home/dominik/.local/bin/vpype\", line 8, in <module>\r\n    sys.exit(cli())\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/click/core.py\", line 829, in __call__\r\n    return self.main(*args, **kwargs)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py\", line 74, in main\r\n    return super().main(args=preprocess_argument_list(args), **extra)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/click/core.py\", line 782, in main\r\n    rv = self.invoke(ctx)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/click/core.py\", line 1290, in invoke\r\n    return _process_result(rv)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/click/core.py\", line 1224, in _process_result\r\n    value = ctx.invoke(self.result_callback, value, **ctx.params)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/click/core.py\", line 610, in invoke\r\n    return callback(*args, **kwargs)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py\", line 128, in process_pipeline\r\n    execute_processors(processors)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/cli.py\", line 212, in execute_processors\r\n    state = proc(state)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/vpype/decorators.py\", line 150, in global_processor\r\n    state.document = f(state.document, *args, **kwargs)\r\n  File \"/home/dominik/.local/lib/python3.8/site-packages/vpype_cli/debug.py\", line 135, in stat\r\n    print(f\"Current page size: {document.page_size}\")\r\nAttributeError: 'NoneType' object has no attribute 'page_size'\r\n```\r\n\r\nVersion:\r\n```\r\n$ vpype --version\r\nvpype 1.3.0a0\r\n```\n", "before_files": [{"content": "import logging\n\nimport click\nimport numpy as np\n\nimport vpype as vp\nimport vpype_viewer\nfrom vpype_viewer import ViewMode\n\nfrom .cli import cli\n\n__all__ = [\"show\"]\n\nCOLORS = [\n    (0, 0, 1),\n    (0, 0.5, 0),\n    (1, 0, 0),\n    (0, 0.75, 0.75),\n    (0, 1, 0),\n    (0.75, 0, 0.75),\n    (0.75, 0.75, 0),\n    (0, 0, 0),\n]\n\n\n@cli.command(group=\"Output\")\n@click.option(\"--classic\", is_flag=True, help=\"Use the classic viewer.\")\n@click.option(\"--force\", is_flag=True, hidden=True, help=\"Force modern viewer\")\n@click.option(\"-p\", \"--show-pen-up\", is_flag=True, help=\"Display pen-up trajectories.\")\n@click.option(\"-d\", \"--show-points\", is_flag=True, help=\"Display paths points with dots.\")\n@click.option(\"-o\", \"--outline\", is_flag=True, help=\"Display in outline mode (modern only).\")\n@click.option(\n    \"-c\",\n    \"--colorful\",\n    is_flag=True,\n    help=\"Display in outline colorful mode (takes precedence over --outline).\",\n)\n@click.option(\"-a\", \"--show-axes\", is_flag=True, help=\"Display axes (classic only).\")\n@click.option(\n    \"-g\", \"--show-grid\", is_flag=True, help=\"Display grid (implies -a, classic only).\"\n)\n@click.option(\n    \"-h\", \"--hide-legend\", is_flag=True, help=\"Do not display the legend (classic only).\"\n)\n@click.option(\n    \"-u\",\n    \"--unit\",\n    type=str,\n    default=\"px\",\n    help=\"Units of the plot (when --show-grid is active, classic only).\",\n)\n@vp.global_processor\ndef show(\n    document: vp.Document,\n    classic: bool,\n    force: bool,\n    show_pen_up: bool,\n    show_points: bool,\n    outline: bool,\n    colorful: bool,\n    show_axes: bool,\n    show_grid: bool,\n    hide_legend: bool,\n    unit: str,\n):\n    \"\"\"Display the geometry in an graphical user interface.\n\n    By default, this command use a modern, hardware-accelerated viewer (currently in beta) with\n    a preview mode (adjustable pen width and opacity) and interactive controls to adjust\n    display options. This viewer requires OpenGL 3.3 support.\n\n    The original, Matplotlib-based viewer is still available with the `--classic` option. The\n    classic viewer does not have interactive controls for display options. Use the command-line\n    options to customize the display.\n    \"\"\"\n\n    if not classic:\n        mgl_ok = _test_mgl()\n        if not mgl_ok and not force:\n            classic = True\n            logging.warning(\"!!! show: ModernGL not available, reverting to classic mode.\")\n        elif not mgl_ok and force:\n            logging.warning(\"!!! show: ModernGL not available but forced to modern mode.\")\n\n    if classic:\n        _show_mpl(\n            document,\n            show_axes,\n            show_grid,\n            show_pen_up,\n            show_points,\n            hide_legend,\n            colorful,\n            unit,\n        )\n    else:\n        view_mode = ViewMode.PREVIEW\n        if outline or show_points:\n            view_mode = ViewMode.OUTLINE\n        if colorful:\n            view_mode = ViewMode.OUTLINE_COLORFUL\n\n        vpype_viewer.show(\n            document, view_mode=view_mode, show_pen_up=show_pen_up, show_points=show_points\n        )\n\n\ndef _test_mgl() -> bool:\n    \"\"\"Tests availability of ModernGL.\"\"\"\n    # noinspection PyBroadException\n    try:\n        import glcontext\n\n        backend = glcontext.default_backend()\n        ctx = backend(mode=\"standalone\", glversion=330)\n        if ctx.load(\"glProgramUniform1iv\") == 0:\n            return False\n    except Exception:\n        return False\n\n    return True\n\n\ndef _show_mpl(\n    document: vp.Document,\n    show_axes: bool,\n    show_grid: bool,\n    show_pen_up: bool,\n    show_points: bool,\n    hide_legend: bool,\n    colorful: bool,\n    unit: str,\n):\n    \"\"\"Display the geometry using matplotlib.\n\n    By default, only the geometries are displayed without the axis. All geometries are\n    displayed with black. When using the `--colorful` flag, each segment will have a different\n    color (default matplotlib behaviour). This can be useful for debugging purposes.\n    \"\"\"\n\n    # deferred import to optimise startup time\n    import matplotlib.collections\n    import matplotlib.pyplot as plt\n\n    scale = 1 / vp.convert_length(unit)\n\n    fig = plt.figure()\n    color_idx = 0\n    collections = {}\n\n    # draw page boundaries\n    if document.page_size:\n        w = document.page_size[0] * scale\n        h = document.page_size[1] * scale\n        dw = 10 * scale\n        plt.plot(\n            np.array([0, 1, 1, 0, 0]) * w,\n            np.array([0, 0, 1, 1, 0]) * h,\n            \"-k\",\n            lw=0.25,\n            label=None,\n        )\n        plt.fill(\n            np.array([w, w + dw, w + dw, dw, dw, w]),\n            np.array([dw, dw, h + dw, h + dw, h, h]),\n            \"k\",\n            alpha=0.3,\n            label=None,\n        )\n\n    for layer_id, lc in document.layers.items():\n        if colorful:\n            color = COLORS[color_idx:] + COLORS[:color_idx]\n            marker_color = \"k\"\n            color_idx += len(lc)\n        else:\n            color = COLORS[color_idx]  # type: ignore\n            marker_color = [color]  # type: ignore\n            color_idx += 1\n        if color_idx >= len(COLORS):\n            color_idx = color_idx % len(COLORS)\n\n        layer_lines = matplotlib.collections.LineCollection(\n            (vp.as_vector(line) * scale for line in lc),\n            color=color,\n            lw=1,\n            alpha=0.5,\n            label=str(layer_id),\n        )\n        collections[layer_id] = [layer_lines]\n        plt.gca().add_collection(layer_lines)\n\n        if show_points:\n            points = np.hstack([line for line in lc]) * scale\n            layer_points = plt.gca().scatter(\n                points.real, points.imag, marker=\".\", c=marker_color, s=16\n            )\n            collections[layer_id].append(layer_points)\n\n        if show_pen_up:\n            pen_up_lines = matplotlib.collections.LineCollection(\n                (\n                    (vp.as_vector(lc[i])[-1] * scale, vp.as_vector(lc[i + 1])[0] * scale)\n                    for i in range(len(lc) - 1)\n                ),\n                color=(0, 0, 0),\n                lw=0.5,\n                alpha=0.5,\n            )\n            collections[layer_id].append(pen_up_lines)\n            plt.gca().add_collection(pen_up_lines)\n\n    plt.gca().invert_yaxis()\n    plt.axis(\"equal\")\n    plt.margins(0, 0)\n\n    if not hide_legend:\n        lgd = plt.legend(loc=\"upper right\")\n        # we will set up a dict mapping legend line to orig line, and enable\n        # picking on the legend line\n        line_dict = {}\n        for lgd_line, lgd_text in zip(lgd.get_lines(), lgd.get_texts()):\n            lgd_line.set_picker(True)  # 5 pts tolerance\n            lgd_line.set_pickradius(5)\n            layer_id = int(lgd_text.get_text())\n            if layer_id in collections:\n                line_dict[lgd_line] = collections[layer_id]\n\n        def on_pick(event):\n            line = event.artist\n            vis = not line_dict[line][0].get_visible()\n            for ln in line_dict[line]:\n                ln.set_visible(vis)\n\n            if vis:\n                line.set_alpha(1.0)\n            else:\n                line.set_alpha(0.2)\n            fig.canvas.draw()\n\n        fig.canvas.mpl_connect(\"pick_event\", on_pick)\n\n    if show_axes or show_grid:\n        plt.axis(\"on\")\n        plt.xlabel(f\"[{unit}]\")\n        plt.ylabel(f\"[{unit}]\")\n    else:\n        plt.axis(\"off\")\n    if show_grid:\n        plt.grid(\"on\")\n    plt.show()\n\n    return document\n", "path": "vpype_cli/show.py"}], "after_files": [{"content": "import logging\n\nimport click\nimport numpy as np\n\nimport vpype as vp\nimport vpype_viewer\nfrom vpype_viewer import ViewMode\n\nfrom .cli import cli\n\n__all__ = [\"show\"]\n\nCOLORS = [\n    (0, 0, 1),\n    (0, 0.5, 0),\n    (1, 0, 0),\n    (0, 0.75, 0.75),\n    (0, 1, 0),\n    (0.75, 0, 0.75),\n    (0.75, 0.75, 0),\n    (0, 0, 0),\n]\n\n\n@cli.command(group=\"Output\")\n@click.option(\"--classic\", is_flag=True, help=\"Use the classic viewer.\")\n@click.option(\"--force\", is_flag=True, hidden=True, help=\"Force modern viewer\")\n@click.option(\"-p\", \"--show-pen-up\", is_flag=True, help=\"Display pen-up trajectories.\")\n@click.option(\"-d\", \"--show-points\", is_flag=True, help=\"Display paths points with dots.\")\n@click.option(\"-o\", \"--outline\", is_flag=True, help=\"Display in outline mode (modern only).\")\n@click.option(\n    \"-c\",\n    \"--colorful\",\n    is_flag=True,\n    help=\"Display in outline colorful mode (takes precedence over --outline).\",\n)\n@click.option(\"-a\", \"--show-axes\", is_flag=True, help=\"Display axes (classic only).\")\n@click.option(\n    \"-g\", \"--show-grid\", is_flag=True, help=\"Display grid (implies -a, classic only).\"\n)\n@click.option(\n    \"-h\", \"--hide-legend\", is_flag=True, help=\"Do not display the legend (classic only).\"\n)\n@click.option(\n    \"-u\",\n    \"--unit\",\n    type=str,\n    default=\"px\",\n    help=\"Units of the plot (when --show-grid is active, classic only).\",\n)\n@vp.global_processor\ndef show(\n    document: vp.Document,\n    classic: bool,\n    force: bool,\n    show_pen_up: bool,\n    show_points: bool,\n    outline: bool,\n    colorful: bool,\n    show_axes: bool,\n    show_grid: bool,\n    hide_legend: bool,\n    unit: str,\n):\n    \"\"\"Display the geometry in an graphical user interface.\n\n    By default, this command use a modern, hardware-accelerated viewer (currently in beta) with\n    a preview mode (adjustable pen width and opacity) and interactive controls to adjust\n    display options. This viewer requires OpenGL 3.3 support.\n\n    The original, Matplotlib-based viewer is still available with the `--classic` option. The\n    classic viewer does not have interactive controls for display options. Use the command-line\n    options to customize the display.\n    \"\"\"\n\n    if not classic:\n        mgl_ok = _test_mgl()\n        if not mgl_ok and not force:\n            classic = True\n            logging.warning(\"!!! show: ModernGL not available, reverting to classic mode.\")\n        elif not mgl_ok and force:\n            logging.warning(\"!!! show: ModernGL not available but forced to modern mode.\")\n\n    if classic:\n        _show_mpl(\n            document,\n            show_axes,\n            show_grid,\n            show_pen_up,\n            show_points,\n            hide_legend,\n            colorful,\n            unit,\n        )\n    else:\n        view_mode = ViewMode.PREVIEW\n        if outline or show_points:\n            view_mode = ViewMode.OUTLINE\n        if colorful:\n            view_mode = ViewMode.OUTLINE_COLORFUL\n\n        vpype_viewer.show(\n            document, view_mode=view_mode, show_pen_up=show_pen_up, show_points=show_points\n        )\n\n    return document\n\n\ndef _test_mgl() -> bool:\n    \"\"\"Tests availability of ModernGL.\"\"\"\n    # noinspection PyBroadException\n    try:\n        import glcontext\n\n        backend = glcontext.default_backend()\n        ctx = backend(mode=\"standalone\", glversion=330)\n        if ctx.load(\"glProgramUniform1iv\") == 0:\n            return False\n    except Exception:\n        return False\n\n    return True\n\n\ndef _show_mpl(\n    document: vp.Document,\n    show_axes: bool,\n    show_grid: bool,\n    show_pen_up: bool,\n    show_points: bool,\n    hide_legend: bool,\n    colorful: bool,\n    unit: str,\n):\n    \"\"\"Display the geometry using matplotlib.\n\n    By default, only the geometries are displayed without the axis. All geometries are\n    displayed with black. When using the `--colorful` flag, each segment will have a different\n    color (default matplotlib behaviour). This can be useful for debugging purposes.\n    \"\"\"\n\n    # deferred import to optimise startup time\n    import matplotlib.collections\n    import matplotlib.pyplot as plt\n\n    scale = 1 / vp.convert_length(unit)\n\n    fig = plt.figure()\n    color_idx = 0\n    collections = {}\n\n    # draw page boundaries\n    if document.page_size:\n        w = document.page_size[0] * scale\n        h = document.page_size[1] * scale\n        dw = 10 * scale\n        plt.plot(\n            np.array([0, 1, 1, 0, 0]) * w,\n            np.array([0, 0, 1, 1, 0]) * h,\n            \"-k\",\n            lw=0.25,\n            label=None,\n        )\n        plt.fill(\n            np.array([w, w + dw, w + dw, dw, dw, w]),\n            np.array([dw, dw, h + dw, h + dw, h, h]),\n            \"k\",\n            alpha=0.3,\n            label=None,\n        )\n\n    for layer_id, lc in document.layers.items():\n        if colorful:\n            color = COLORS[color_idx:] + COLORS[:color_idx]\n            marker_color = \"k\"\n            color_idx += len(lc)\n        else:\n            color = COLORS[color_idx]  # type: ignore\n            marker_color = [color]  # type: ignore\n            color_idx += 1\n        if color_idx >= len(COLORS):\n            color_idx = color_idx % len(COLORS)\n\n        layer_lines = matplotlib.collections.LineCollection(\n            (vp.as_vector(line) * scale for line in lc),\n            color=color,\n            lw=1,\n            alpha=0.5,\n            label=str(layer_id),\n        )\n        collections[layer_id] = [layer_lines]\n        plt.gca().add_collection(layer_lines)\n\n        if show_points:\n            points = np.hstack([line for line in lc]) * scale\n            layer_points = plt.gca().scatter(\n                points.real, points.imag, marker=\".\", c=marker_color, s=16\n            )\n            collections[layer_id].append(layer_points)\n\n        if show_pen_up:\n            pen_up_lines = matplotlib.collections.LineCollection(\n                (\n                    (vp.as_vector(lc[i])[-1] * scale, vp.as_vector(lc[i + 1])[0] * scale)\n                    for i in range(len(lc) - 1)\n                ),\n                color=(0, 0, 0),\n                lw=0.5,\n                alpha=0.5,\n            )\n            collections[layer_id].append(pen_up_lines)\n            plt.gca().add_collection(pen_up_lines)\n\n    plt.gca().invert_yaxis()\n    plt.axis(\"equal\")\n    plt.margins(0, 0)\n\n    if not hide_legend:\n        lgd = plt.legend(loc=\"upper right\")\n        # we will set up a dict mapping legend line to orig line, and enable\n        # picking on the legend line\n        line_dict = {}\n        for lgd_line, lgd_text in zip(lgd.get_lines(), lgd.get_texts()):\n            lgd_line.set_picker(True)  # 5 pts tolerance\n            lgd_line.set_pickradius(5)\n            layer_id = int(lgd_text.get_text())\n            if layer_id in collections:\n                line_dict[lgd_line] = collections[layer_id]\n\n        def on_pick(event):\n            line = event.artist\n            vis = not line_dict[line][0].get_visible()\n            for ln in line_dict[line]:\n                ln.set_visible(vis)\n\n            if vis:\n                line.set_alpha(1.0)\n            else:\n                line.set_alpha(0.2)\n            fig.canvas.draw()\n\n        fig.canvas.mpl_connect(\"pick_event\", on_pick)\n\n    if show_axes or show_grid:\n        plt.axis(\"on\")\n        plt.xlabel(f\"[{unit}]\")\n        plt.ylabel(f\"[{unit}]\")\n    else:\n        plt.axis(\"off\")\n    if show_grid:\n        plt.grid(\"on\")\n    plt.show()\n", "path": "vpype_cli/show.py"}]}