Skip to content

panchi.visualizations

The visualization module provides Animator2D and Animator3D, the entry points for 2D and 3D visualizations respectively.

from panchi.visualizations import Animator2D, Animator3D

Animator2D

panchi.visualizations.Animator2D

2D visualization of vectors, matrices, and linear transformations.

Parameters:

Name Type Description Default
backend str

Visualization backend: "matplotlib" (default) or "manim".

'matplotlib'
save_path str or Path

Directory for saving output. If None, matplotlib displays interactively and manim saves to ./media.

None
quality str

Render quality: "low", "medium" (default), "high", or "production" (manim only).

'medium'
figsize tuple[int, int]

Figure size in inches (matplotlib only). Default (8, 8).

(8, 8)

Examples:

>>> from panchi import Vector, Matrix
>>> from panchi.visualizations import Animator2D
>>>
>>> animator = Animator2D()
>>> animator.plot_vectors([Vector([3, 2]), Vector([1, 3])])
>>> animator.animate_transform(Matrix([[0, -1], [1, 0]]))
Source code in panchi/visualizations/animator.py
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
class Animator2D:
    """2D visualization of vectors, matrices, and linear transformations.

    Parameters
    ----------
    backend : str
        Visualization backend: ``"matplotlib"`` (default) or ``"manim"``.
    save_path : str or Path, optional
        Directory for saving output. If ``None``, matplotlib displays
        interactively and manim saves to ``./media``.
    quality : str
        Render quality: ``"low"``, ``"medium"`` (default), ``"high"``,
        or ``"production"`` (manim only).
    figsize : tuple[int, int]
        Figure size in inches (matplotlib only). Default ``(8, 8)``.

    Examples
    --------
    >>> from panchi import Vector, Matrix
    >>> from panchi.visualizations import Animator2D
    >>>
    >>> animator = Animator2D()
    >>> animator.plot_vectors([Vector([3, 2]), Vector([1, 3])])
    >>> animator.animate_transform(Matrix([[0, -1], [1, 0]]))
    """

    def __init__(
        self,
        backend: str = "matplotlib",
        save_path: str | Path | None = None,
        quality: str = "medium",
        figsize: tuple[int, int] = (8, 8),
        include_extra_files: bool = False,
    ) -> None:
        resolved_path = Path(save_path) if save_path else None
        self._backend = _build_backend(
            backend, resolved_path, quality, figsize, include_extra_files
        )
        self.backend = backend

    def plot_vectors(
        self,
        vectors: list[Vector],
        *,
        colors: list[str] | None = None,
        labels: list[str] | None = None,
        grid: bool = True,
        name: str | None = None,
    ) -> None:
        """Plot a list of 2D vectors.

        Parameters
        ----------
        vectors : list[Vector]
            2D vectors to plot.
        colors : list[str], optional
            Colors for each vector.
        labels : list[str], optional
            Labels for each vector.
        grid : bool
            Whether to show grid lines. Default ``True``.
        name : str, optional
            Output filename (without extension) when saving.
        """
        self._validate_2d(vectors)
        self._backend.plot_vectors(
            vectors,
            colors=colors,
            labels=labels,
            grid=grid,
            name=name or "plot_vectors",
        )

    def animate_addition(
        self,
        v1: Vector,
        v2: Vector,
        frames: int = 60,
        interval: int = 30,
        name: str | None = None,
        colors: list[str] | None = None,
    ) -> object | None:
        """Animate the addition of two 2D vectors.

        Parameters
        ----------
        v1 : Vector
            First vector.
        v2 : Vector
            Second vector.
        frames : int
            Number of animation frames. Default ``60``.
        interval : int
            Milliseconds between frames. Default ``30``.
        name : str, optional
            Output filename (without extension) when saving.
        colors : list[str], optional
            Up to three colors for ``[v1, v2, v1 + v2]``. Any omitted role
            keeps its default. ``None`` uses the default palette.
        """
        self._validate_2d([v1, v2])
        return self._backend.animate_addition(
            v1,
            v2,
            frames=frames,
            interval=interval,
            name=name or "animate_addition",
            colors=colors,
        )

    def animate_scaling(
        self,
        vector: Vector,
        scale_factor: float,
        frames: int = 60,
        interval: int = 30,
        name: str | None = None,
        colors: list[str] | None = None,
    ) -> object | None:
        """Animate scalar multiplication of a 2D vector.

        Parameters
        ----------
        vector : Vector
            Vector to scale.
        scale_factor : float
            Scaling factor.
        frames : int
            Number of animation frames. Default ``60``.
        interval : int
            Milliseconds between frames. Default ``30``.
        name : str, optional
            Output filename (without extension) when saving.
        colors : list[str], optional
            Up to two colors for ``[original, scaled]``. Any omitted role
            keeps its default. ``None`` uses the default palette.
        """
        self._validate_2d([vector])
        return self._backend.animate_scaling(
            vector,
            scale_factor,
            frames=frames,
            interval=interval,
            name=name or "animate_scaling",
            colors=colors,
        )

    def animate_transform(
        self,
        matrix: Matrix,
        vectors: list[Vector] | None = None,
        frames: int = 60,
        interval: int = 30,
        name: str | None = None,
        colors: list[str] | None = None,
    ) -> object | None:
        """Animate a 2x2 linear transformation with full grid deformation.

        The coordinate grid morphs smoothly from the identity to the given
        matrix. Each vector in ``vectors`` slides from ``v`` to ``matrix @ v``;
        when ``vectors`` is ``None`` the standard basis ``[e1, e2]`` is shown.

        Parameters
        ----------
        matrix : Matrix
            A 2x2 transformation matrix.
        vectors : list[Vector], optional
            2D vectors to transform. ``None`` uses the standard basis.
        frames : int
            Number of animation frames. Default ``60``.
        interval : int
            Milliseconds between frames. Default ``30``.
        name : str, optional
            Output filename (without extension) when saving.
        colors : list[str], optional
            One color per transformed vector, cycling if fewer are supplied.
            ``None`` uses the default palette.
        """
        self._validate_2x2(matrix)
        if vectors is not None:
            self._validate_2d(vectors)
        return self._backend.animate_transform(
            matrix,
            vectors=vectors,
            frames=frames,
            interval=interval,
            name=name or "animate_transform",
            colors=colors,
        )

    def plot_span(
        self,
        vectors_or_space: list[Vector] | VectorSpace | list[list[Vector] | VectorSpace],
        *,
        colors: list[str] | None = None,
        labels: list[str] | None = None,
        grid: bool = True,
        name: str | None = None,
        span_color: str | None = None,
    ) -> None:
        """Visualize the span of vectors with a shaded region and basis arrows.

        Accepts a single span (a ``VectorSpace`` or a list of ``Vector``) or
        several spans to compare (a list whose items are each a ``VectorSpace``
        or a list of ``Vector``). A 1D subspace is drawn as a line through the
        origin; a 2D subspace shades the entire plane. With multiple spans each
        is drawn in its own color with a legend.

        Parameters
        ----------
        vectors_or_space : list[Vector], VectorSpace, or list of those
            One span, or a list of spans to compare.
        colors : list[str], optional
            For a single span, colors for the basis vectors; for multiple
            spans, one color per span.
        labels : list[str], optional
            For a single span, labels for the basis vectors; for multiple
            spans, one legend label per span.
        grid : bool
            Whether to show grid lines. Default ``True``.
        name : str, optional
            Output filename (without extension) when saving.
        span_color : str, optional
            Color of the shaded region (single span only). ``None`` uses the
            default.
        """
        spans = _resolve_span_inputs(vectors_or_space)
        for vectors, _ in spans:
            self._validate_2d(vectors)
        self._backend.plot_span(
            [space for _, space in spans],
            colors=colors,
            labels=labels,
            grid=grid,
            name=name or "plot_span",
            span_color=span_color,
        )

    @staticmethod
    def _validate_2d(vectors: list[Vector]) -> None:
        if not isinstance(vectors, list):
            raise TypeError(
                f"Vectors must be passed as a list. Wrap a single vector as [v]. "
                f"Got {type(vectors).__name__}."
            )
        for v in vectors:
            if v.dims != 2:
                raise ValueError(
                    f"Only 2D vectors are supported for visualization. "
                    f"Got {v.dims}D vector: {v}"
                )

    @staticmethod
    def _validate_2x2(matrix: Matrix) -> None:
        if matrix.shape != (2, 2):
            raise ValueError(
                f"Only 2x2 matrices are supported for transformation "
                f"visualization. Got shape {matrix.rows}x{matrix.cols}."
            )

animate_addition(v1, v2, frames=60, interval=30, name=None, colors=None)

Animate the addition of two 2D vectors.

Parameters:

Name Type Description Default
v1 Vector

First vector.

required
v2 Vector

Second vector.

required
frames int

Number of animation frames. Default 60.

60
interval int

Milliseconds between frames. Default 30.

30
name str

Output filename (without extension) when saving.

None
colors list[str]

Up to three colors for [v1, v2, v1 + v2]. Any omitted role keeps its default. None uses the default palette.

None
Source code in panchi/visualizations/animator.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
def animate_addition(
    self,
    v1: Vector,
    v2: Vector,
    frames: int = 60,
    interval: int = 30,
    name: str | None = None,
    colors: list[str] | None = None,
) -> object | None:
    """Animate the addition of two 2D vectors.

    Parameters
    ----------
    v1 : Vector
        First vector.
    v2 : Vector
        Second vector.
    frames : int
        Number of animation frames. Default ``60``.
    interval : int
        Milliseconds between frames. Default ``30``.
    name : str, optional
        Output filename (without extension) when saving.
    colors : list[str], optional
        Up to three colors for ``[v1, v2, v1 + v2]``. Any omitted role
        keeps its default. ``None`` uses the default palette.
    """
    self._validate_2d([v1, v2])
    return self._backend.animate_addition(
        v1,
        v2,
        frames=frames,
        interval=interval,
        name=name or "animate_addition",
        colors=colors,
    )

animate_scaling(vector, scale_factor, frames=60, interval=30, name=None, colors=None)

Animate scalar multiplication of a 2D vector.

Parameters:

Name Type Description Default
vector Vector

Vector to scale.

required
scale_factor float

Scaling factor.

required
frames int

Number of animation frames. Default 60.

60
interval int

Milliseconds between frames. Default 30.

30
name str

Output filename (without extension) when saving.

None
colors list[str]

Up to two colors for [original, scaled]. Any omitted role keeps its default. None uses the default palette.

None
Source code in panchi/visualizations/animator.py
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
def animate_scaling(
    self,
    vector: Vector,
    scale_factor: float,
    frames: int = 60,
    interval: int = 30,
    name: str | None = None,
    colors: list[str] | None = None,
) -> object | None:
    """Animate scalar multiplication of a 2D vector.

    Parameters
    ----------
    vector : Vector
        Vector to scale.
    scale_factor : float
        Scaling factor.
    frames : int
        Number of animation frames. Default ``60``.
    interval : int
        Milliseconds between frames. Default ``30``.
    name : str, optional
        Output filename (without extension) when saving.
    colors : list[str], optional
        Up to two colors for ``[original, scaled]``. Any omitted role
        keeps its default. ``None`` uses the default palette.
    """
    self._validate_2d([vector])
    return self._backend.animate_scaling(
        vector,
        scale_factor,
        frames=frames,
        interval=interval,
        name=name or "animate_scaling",
        colors=colors,
    )

animate_transform(matrix, vectors=None, frames=60, interval=30, name=None, colors=None)

Animate a 2x2 linear transformation with full grid deformation.

The coordinate grid morphs smoothly from the identity to the given matrix. Each vector in vectors slides from v to matrix @ v; when vectors is None the standard basis [e1, e2] is shown.

Parameters:

Name Type Description Default
matrix Matrix

A 2x2 transformation matrix.

required
vectors list[Vector]

2D vectors to transform. None uses the standard basis.

None
frames int

Number of animation frames. Default 60.

60
interval int

Milliseconds between frames. Default 30.

30
name str

Output filename (without extension) when saving.

None
colors list[str]

One color per transformed vector, cycling if fewer are supplied. None uses the default palette.

None
Source code in panchi/visualizations/animator.py
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
def animate_transform(
    self,
    matrix: Matrix,
    vectors: list[Vector] | None = None,
    frames: int = 60,
    interval: int = 30,
    name: str | None = None,
    colors: list[str] | None = None,
) -> object | None:
    """Animate a 2x2 linear transformation with full grid deformation.

    The coordinate grid morphs smoothly from the identity to the given
    matrix. Each vector in ``vectors`` slides from ``v`` to ``matrix @ v``;
    when ``vectors`` is ``None`` the standard basis ``[e1, e2]`` is shown.

    Parameters
    ----------
    matrix : Matrix
        A 2x2 transformation matrix.
    vectors : list[Vector], optional
        2D vectors to transform. ``None`` uses the standard basis.
    frames : int
        Number of animation frames. Default ``60``.
    interval : int
        Milliseconds between frames. Default ``30``.
    name : str, optional
        Output filename (without extension) when saving.
    colors : list[str], optional
        One color per transformed vector, cycling if fewer are supplied.
        ``None`` uses the default palette.
    """
    self._validate_2x2(matrix)
    if vectors is not None:
        self._validate_2d(vectors)
    return self._backend.animate_transform(
        matrix,
        vectors=vectors,
        frames=frames,
        interval=interval,
        name=name or "animate_transform",
        colors=colors,
    )

plot_span(vectors_or_space, *, colors=None, labels=None, grid=True, name=None, span_color=None)

Visualize the span of vectors with a shaded region and basis arrows.

Accepts a single span (a VectorSpace or a list of Vector) or several spans to compare (a list whose items are each a VectorSpace or a list of Vector). A 1D subspace is drawn as a line through the origin; a 2D subspace shades the entire plane. With multiple spans each is drawn in its own color with a legend.

Parameters:

Name Type Description Default
vectors_or_space list[Vector], VectorSpace, or list of those

One span, or a list of spans to compare.

required
colors list[str]

For a single span, colors for the basis vectors; for multiple spans, one color per span.

None
labels list[str]

For a single span, labels for the basis vectors; for multiple spans, one legend label per span.

None
grid bool

Whether to show grid lines. Default True.

True
name str

Output filename (without extension) when saving.

None
span_color str

Color of the shaded region (single span only). None uses the default.

None
Source code in panchi/visualizations/animator.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
def plot_span(
    self,
    vectors_or_space: list[Vector] | VectorSpace | list[list[Vector] | VectorSpace],
    *,
    colors: list[str] | None = None,
    labels: list[str] | None = None,
    grid: bool = True,
    name: str | None = None,
    span_color: str | None = None,
) -> None:
    """Visualize the span of vectors with a shaded region and basis arrows.

    Accepts a single span (a ``VectorSpace`` or a list of ``Vector``) or
    several spans to compare (a list whose items are each a ``VectorSpace``
    or a list of ``Vector``). A 1D subspace is drawn as a line through the
    origin; a 2D subspace shades the entire plane. With multiple spans each
    is drawn in its own color with a legend.

    Parameters
    ----------
    vectors_or_space : list[Vector], VectorSpace, or list of those
        One span, or a list of spans to compare.
    colors : list[str], optional
        For a single span, colors for the basis vectors; for multiple
        spans, one color per span.
    labels : list[str], optional
        For a single span, labels for the basis vectors; for multiple
        spans, one legend label per span.
    grid : bool
        Whether to show grid lines. Default ``True``.
    name : str, optional
        Output filename (without extension) when saving.
    span_color : str, optional
        Color of the shaded region (single span only). ``None`` uses the
        default.
    """
    spans = _resolve_span_inputs(vectors_or_space)
    for vectors, _ in spans:
        self._validate_2d(vectors)
    self._backend.plot_span(
        [space for _, space in spans],
        colors=colors,
        labels=labels,
        grid=grid,
        name=name or "plot_span",
        span_color=span_color,
    )

plot_vectors(vectors, *, colors=None, labels=None, grid=True, name=None)

Plot a list of 2D vectors.

Parameters:

Name Type Description Default
vectors list[Vector]

2D vectors to plot.

required
colors list[str]

Colors for each vector.

None
labels list[str]

Labels for each vector.

None
grid bool

Whether to show grid lines. Default True.

True
name str

Output filename (without extension) when saving.

None
Source code in panchi/visualizations/animator.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def plot_vectors(
    self,
    vectors: list[Vector],
    *,
    colors: list[str] | None = None,
    labels: list[str] | None = None,
    grid: bool = True,
    name: str | None = None,
) -> None:
    """Plot a list of 2D vectors.

    Parameters
    ----------
    vectors : list[Vector]
        2D vectors to plot.
    colors : list[str], optional
        Colors for each vector.
    labels : list[str], optional
        Labels for each vector.
    grid : bool
        Whether to show grid lines. Default ``True``.
    name : str, optional
        Output filename (without extension) when saving.
    """
    self._validate_2d(vectors)
    self._backend.plot_vectors(
        vectors,
        colors=colors,
        labels=labels,
        grid=grid,
        name=name or "plot_vectors",
    )

Animator3D

panchi.visualizations.Animator3D

3D visualization of vectors in R³.

Mirrors :class:Animator2D for three-dimensional vectors.

Parameters:

Name Type Description Default
backend str

Visualization backend: "matplotlib" (default) or "manim".

'matplotlib'
save_path str or Path

Directory for saving output. If None, matplotlib displays interactively and manim saves to ./media.

None
quality str

Render quality: "low", "medium" (default), or "high".

'medium'
figsize tuple[int, int]

Figure size in inches. Default (8, 8).

(8, 8)

Examples:

>>> from panchi import Vector
>>> from panchi.visualizations import Animator3D
>>>
>>> animator = Animator3D()
>>> animator.plot_vectors([Vector([3, 2, 1]), Vector([1, 3, 2])])
Source code in panchi/visualizations/animator.py
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
class Animator3D:
    """3D visualization of vectors in R³.

    Mirrors :class:`Animator2D` for three-dimensional vectors.

    Parameters
    ----------
    backend : str
        Visualization backend: ``"matplotlib"`` (default) or ``"manim"``.
    save_path : str or Path, optional
        Directory for saving output. If ``None``, matplotlib displays
        interactively and manim saves to ``./media``.
    quality : str
        Render quality: ``"low"``, ``"medium"`` (default), or ``"high"``.
    figsize : tuple[int, int]
        Figure size in inches. Default ``(8, 8)``.

    Examples
    --------
    >>> from panchi import Vector
    >>> from panchi.visualizations import Animator3D
    >>>
    >>> animator = Animator3D()
    >>> animator.plot_vectors([Vector([3, 2, 1]), Vector([1, 3, 2])])
    """

    def __init__(
        self,
        backend: str = "matplotlib",
        save_path: str | Path | None = None,
        quality: str = "medium",
        figsize: tuple[int, int] = (8, 8),
        include_extra_files: bool = False,
    ) -> None:
        resolved_path = Path(save_path) if save_path else None

        if backend == "matplotlib":
            from panchi.visualizations.backends.matplotlib_3d import (
                _MatplotlibBackend3D,
            )

            self._backend = _MatplotlibBackend3D(
                save_path=resolved_path,
                quality=quality,
                figsize=figsize,
            )
        elif backend == "manim":
            from panchi.visualizations.backends.manim_3d import _ManimBackend3D

            self._backend = _ManimBackend3D(
                save_path=resolved_path or Path("./media"),
                quality=quality,
                include_extra_files=include_extra_files,
            )
        else:
            raise ValueError(
                f"Unknown backend '{backend}'. Choose 'matplotlib' or 'manim'."
            )

        self.backend = backend

    def plot_vectors(
        self,
        vectors: list[Vector],
        *,
        colors: list[str] | None = None,
        labels: list[str] | None = None,
        grid: bool = True,
        name: str | None = None,
    ) -> None:
        """Plot a list of 3D vectors.

        Parameters
        ----------
        vectors : list[Vector]
            3D vectors to plot.
        colors : list[str], optional
            Colors for each vector.
        labels : list[str], optional
            Labels for each vector.
        grid : bool
            Whether to show grid lines. Default ``True``.
        name : str, optional
            Output filename (without extension) when saving.
        """
        self._validate_3d(vectors)
        self._backend.plot_vectors(
            vectors,
            colors=colors,
            labels=labels,
            grid=grid,
            name=name or "plot_vectors",
        )

    def animate_addition(
        self,
        v1: Vector,
        v2: Vector,
        frames: int = 60,
        interval: int = 30,
        name: str | None = None,
        colors: list[str] | None = None,
    ) -> object | None:
        """Animate the addition of two 3D vectors.

        Parameters
        ----------
        v1 : Vector
            First vector.
        v2 : Vector
            Second vector.
        frames : int
            Number of animation frames. Default ``60``.
        interval : int
            Milliseconds between frames. Default ``30``.
        name : str, optional
            Output filename (without extension) when saving.
        colors : list[str], optional
            Up to three colors for ``[v1, v2, v1 + v2]``. Any omitted role
            keeps its default. ``None`` uses the default palette.
        """
        self._validate_3d([v1, v2])
        return self._backend.animate_addition(
            v1,
            v2,
            frames=frames,
            interval=interval,
            name=name or "animate_addition",
            colors=colors,
        )

    def animate_scaling(
        self,
        vector: Vector,
        scale_factor: float,
        frames: int = 60,
        interval: int = 30,
        name: str | None = None,
        colors: list[str] | None = None,
    ) -> object | None:
        """Animate scalar multiplication of a 3D vector.

        Parameters
        ----------
        vector : Vector
            Vector to scale.
        scale_factor : float
            Scaling factor.
        frames : int
            Number of animation frames. Default ``60``.
        interval : int
            Milliseconds between frames. Default ``30``.
        name : str, optional
            Output filename (without extension) when saving.
        colors : list[str], optional
            Up to two colors for ``[original, scaled]``. Any omitted role
            keeps its default. ``None`` uses the default palette.
        """
        self._validate_3d([vector])
        return self._backend.animate_scaling(
            vector,
            scale_factor,
            frames=frames,
            interval=interval,
            name=name or "animate_scaling",
            colors=colors,
        )

    def animate_transform(
        self,
        matrix: Matrix,
        vectors: list[Vector] | None = None,
        frames: int = 60,
        interval: int = 30,
        name: str | None = None,
        colors: list[str] | None = None,
    ) -> object | None:
        """Animate a 3x3 linear transformation of R³.

        Each vector in ``vectors`` slides smoothly from ``v`` to ``matrix @ v``;
        when ``vectors`` is ``None`` the standard basis ``[e1, e2, e3]`` is
        shown (its image is the parallelepiped spanned by the matrix columns).

        Parameters
        ----------
        matrix : Matrix
            A 3x3 transformation matrix.
        vectors : list[Vector], optional
            3D vectors to transform. ``None`` uses the standard basis.
        frames : int
            Number of animation frames. Default ``60``.
        interval : int
            Milliseconds between frames. Default ``30``.
        name : str, optional
            Output filename (without extension) when saving.
        colors : list[str], optional
            One color per transformed vector, cycling if fewer are supplied.
            ``None`` uses the default palette.
        """
        self._validate_3x3(matrix)
        if vectors is not None:
            self._validate_3d(vectors)
        return self._backend.animate_transform(
            matrix,
            vectors=vectors,
            frames=frames,
            interval=interval,
            name=name or "animate_transform",
            colors=colors,
        )

    def plot_span(
        self,
        vectors_or_space: list[Vector] | VectorSpace | list[list[Vector] | VectorSpace],
        *,
        colors: list[str] | None = None,
        labels: list[str] | None = None,
        grid: bool = True,
        name: str | None = None,
        span_color: str | None = None,
    ) -> None:
        """Visualize the span of 3D vectors with a shaded region and basis arrows.

        Accepts a single span (a ``VectorSpace`` or a list of ``Vector``) or
        several spans to compare (a list whose items are each a ``VectorSpace``
        or a list of ``Vector``). A 1D subspace is drawn as a line through the
        origin, a 2D subspace as a plane patch, and a 3D subspace as a
        translucent volume. With multiple spans each is drawn in its own color
        with a legend.

        Parameters
        ----------
        vectors_or_space : list[Vector], VectorSpace, or list of those
            One span, or a list of spans to compare.
        colors : list[str], optional
            For a single span, colors for the basis vectors; for multiple
            spans, one color per span.
        labels : list[str], optional
            For a single span, labels for the basis vectors; for multiple
            spans, one legend label per span.
        grid : bool
            Whether to show grid lines. Default ``True``.
        name : str, optional
            Output filename (without extension) when saving.
        span_color : str, optional
            Color of the shaded region (single span only). ``None`` uses the
            default.
        """
        spans = _resolve_span_inputs(vectors_or_space)
        for vectors, _ in spans:
            self._validate_3d(vectors)
        self._backend.plot_span(
            [space for _, space in spans],
            colors=colors,
            labels=labels,
            grid=grid,
            name=name or "plot_span",
            span_color=span_color,
        )

    @staticmethod
    def _validate_3d(vectors: list[Vector]) -> None:
        if not isinstance(vectors, list):
            raise TypeError(
                f"Vectors must be passed as a list. Wrap a single vector as [v]. "
                f"Got {type(vectors).__name__}."
            )
        for v in vectors:
            if v.dims != 3:
                raise ValueError(
                    f"Only 3D vectors are supported for Animator3D. "
                    f"Got {v.dims}D vector: {v}"
                )

    @staticmethod
    def _validate_3x3(matrix: Matrix) -> None:
        if matrix.shape != (3, 3):
            raise ValueError(
                f"Only 3x3 matrices are supported for transformation "
                f"visualization. Got shape {matrix.rows}x{matrix.cols}."
            )

animate_addition(v1, v2, frames=60, interval=30, name=None, colors=None)

Animate the addition of two 3D vectors.

Parameters:

Name Type Description Default
v1 Vector

First vector.

required
v2 Vector

Second vector.

required
frames int

Number of animation frames. Default 60.

60
interval int

Milliseconds between frames. Default 30.

30
name str

Output filename (without extension) when saving.

None
colors list[str]

Up to three colors for [v1, v2, v1 + v2]. Any omitted role keeps its default. None uses the default palette.

None
Source code in panchi/visualizations/animator.py
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
def animate_addition(
    self,
    v1: Vector,
    v2: Vector,
    frames: int = 60,
    interval: int = 30,
    name: str | None = None,
    colors: list[str] | None = None,
) -> object | None:
    """Animate the addition of two 3D vectors.

    Parameters
    ----------
    v1 : Vector
        First vector.
    v2 : Vector
        Second vector.
    frames : int
        Number of animation frames. Default ``60``.
    interval : int
        Milliseconds between frames. Default ``30``.
    name : str, optional
        Output filename (without extension) when saving.
    colors : list[str], optional
        Up to three colors for ``[v1, v2, v1 + v2]``. Any omitted role
        keeps its default. ``None`` uses the default palette.
    """
    self._validate_3d([v1, v2])
    return self._backend.animate_addition(
        v1,
        v2,
        frames=frames,
        interval=interval,
        name=name or "animate_addition",
        colors=colors,
    )

animate_scaling(vector, scale_factor, frames=60, interval=30, name=None, colors=None)

Animate scalar multiplication of a 3D vector.

Parameters:

Name Type Description Default
vector Vector

Vector to scale.

required
scale_factor float

Scaling factor.

required
frames int

Number of animation frames. Default 60.

60
interval int

Milliseconds between frames. Default 30.

30
name str

Output filename (without extension) when saving.

None
colors list[str]

Up to two colors for [original, scaled]. Any omitted role keeps its default. None uses the default palette.

None
Source code in panchi/visualizations/animator.py
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
def animate_scaling(
    self,
    vector: Vector,
    scale_factor: float,
    frames: int = 60,
    interval: int = 30,
    name: str | None = None,
    colors: list[str] | None = None,
) -> object | None:
    """Animate scalar multiplication of a 3D vector.

    Parameters
    ----------
    vector : Vector
        Vector to scale.
    scale_factor : float
        Scaling factor.
    frames : int
        Number of animation frames. Default ``60``.
    interval : int
        Milliseconds between frames. Default ``30``.
    name : str, optional
        Output filename (without extension) when saving.
    colors : list[str], optional
        Up to two colors for ``[original, scaled]``. Any omitted role
        keeps its default. ``None`` uses the default palette.
    """
    self._validate_3d([vector])
    return self._backend.animate_scaling(
        vector,
        scale_factor,
        frames=frames,
        interval=interval,
        name=name or "animate_scaling",
        colors=colors,
    )

animate_transform(matrix, vectors=None, frames=60, interval=30, name=None, colors=None)

Animate a 3x3 linear transformation of R³.

Each vector in vectors slides smoothly from v to matrix @ v; when vectors is None the standard basis [e1, e2, e3] is shown (its image is the parallelepiped spanned by the matrix columns).

Parameters:

Name Type Description Default
matrix Matrix

A 3x3 transformation matrix.

required
vectors list[Vector]

3D vectors to transform. None uses the standard basis.

None
frames int

Number of animation frames. Default 60.

60
interval int

Milliseconds between frames. Default 30.

30
name str

Output filename (without extension) when saving.

None
colors list[str]

One color per transformed vector, cycling if fewer are supplied. None uses the default palette.

None
Source code in panchi/visualizations/animator.py
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
def animate_transform(
    self,
    matrix: Matrix,
    vectors: list[Vector] | None = None,
    frames: int = 60,
    interval: int = 30,
    name: str | None = None,
    colors: list[str] | None = None,
) -> object | None:
    """Animate a 3x3 linear transformation of R³.

    Each vector in ``vectors`` slides smoothly from ``v`` to ``matrix @ v``;
    when ``vectors`` is ``None`` the standard basis ``[e1, e2, e3]`` is
    shown (its image is the parallelepiped spanned by the matrix columns).

    Parameters
    ----------
    matrix : Matrix
        A 3x3 transformation matrix.
    vectors : list[Vector], optional
        3D vectors to transform. ``None`` uses the standard basis.
    frames : int
        Number of animation frames. Default ``60``.
    interval : int
        Milliseconds between frames. Default ``30``.
    name : str, optional
        Output filename (without extension) when saving.
    colors : list[str], optional
        One color per transformed vector, cycling if fewer are supplied.
        ``None`` uses the default palette.
    """
    self._validate_3x3(matrix)
    if vectors is not None:
        self._validate_3d(vectors)
    return self._backend.animate_transform(
        matrix,
        vectors=vectors,
        frames=frames,
        interval=interval,
        name=name or "animate_transform",
        colors=colors,
    )

plot_span(vectors_or_space, *, colors=None, labels=None, grid=True, name=None, span_color=None)

Visualize the span of 3D vectors with a shaded region and basis arrows.

Accepts a single span (a VectorSpace or a list of Vector) or several spans to compare (a list whose items are each a VectorSpace or a list of Vector). A 1D subspace is drawn as a line through the origin, a 2D subspace as a plane patch, and a 3D subspace as a translucent volume. With multiple spans each is drawn in its own color with a legend.

Parameters:

Name Type Description Default
vectors_or_space list[Vector], VectorSpace, or list of those

One span, or a list of spans to compare.

required
colors list[str]

For a single span, colors for the basis vectors; for multiple spans, one color per span.

None
labels list[str]

For a single span, labels for the basis vectors; for multiple spans, one legend label per span.

None
grid bool

Whether to show grid lines. Default True.

True
name str

Output filename (without extension) when saving.

None
span_color str

Color of the shaded region (single span only). None uses the default.

None
Source code in panchi/visualizations/animator.py
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
def plot_span(
    self,
    vectors_or_space: list[Vector] | VectorSpace | list[list[Vector] | VectorSpace],
    *,
    colors: list[str] | None = None,
    labels: list[str] | None = None,
    grid: bool = True,
    name: str | None = None,
    span_color: str | None = None,
) -> None:
    """Visualize the span of 3D vectors with a shaded region and basis arrows.

    Accepts a single span (a ``VectorSpace`` or a list of ``Vector``) or
    several spans to compare (a list whose items are each a ``VectorSpace``
    or a list of ``Vector``). A 1D subspace is drawn as a line through the
    origin, a 2D subspace as a plane patch, and a 3D subspace as a
    translucent volume. With multiple spans each is drawn in its own color
    with a legend.

    Parameters
    ----------
    vectors_or_space : list[Vector], VectorSpace, or list of those
        One span, or a list of spans to compare.
    colors : list[str], optional
        For a single span, colors for the basis vectors; for multiple
        spans, one color per span.
    labels : list[str], optional
        For a single span, labels for the basis vectors; for multiple
        spans, one legend label per span.
    grid : bool
        Whether to show grid lines. Default ``True``.
    name : str, optional
        Output filename (without extension) when saving.
    span_color : str, optional
        Color of the shaded region (single span only). ``None`` uses the
        default.
    """
    spans = _resolve_span_inputs(vectors_or_space)
    for vectors, _ in spans:
        self._validate_3d(vectors)
    self._backend.plot_span(
        [space for _, space in spans],
        colors=colors,
        labels=labels,
        grid=grid,
        name=name or "plot_span",
        span_color=span_color,
    )

plot_vectors(vectors, *, colors=None, labels=None, grid=True, name=None)

Plot a list of 3D vectors.

Parameters:

Name Type Description Default
vectors list[Vector]

3D vectors to plot.

required
colors list[str]

Colors for each vector.

None
labels list[str]

Labels for each vector.

None
grid bool

Whether to show grid lines. Default True.

True
name str

Output filename (without extension) when saving.

None
Source code in panchi/visualizations/animator.py
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
def plot_vectors(
    self,
    vectors: list[Vector],
    *,
    colors: list[str] | None = None,
    labels: list[str] | None = None,
    grid: bool = True,
    name: str | None = None,
) -> None:
    """Plot a list of 3D vectors.

    Parameters
    ----------
    vectors : list[Vector]
        3D vectors to plot.
    colors : list[str], optional
        Colors for each vector.
    labels : list[str], optional
        Labels for each vector.
    grid : bool
        Whether to show grid lines. Default ``True``.
    name : str, optional
        Output filename (without extension) when saving.
    """
    self._validate_3d(vectors)
    self._backend.plot_vectors(
        vectors,
        colors=colors,
        labels=labels,
        grid=grid,
        name=name or "plot_vectors",
    )