Compiling#

The compiler itself. Source or IR in, Panel Core out.

Every entry point takes the same two optional keyword arguments – library and metrics – so the character set and the font are both injectable, and neither is read from the environment.

scenet.pipeline#

.. py:module:: scenet.pipeline

The compiler: source through IR to Panel Core.

Each stage is separable and independently testable, which is the point of having tiers at all. The frontend never computes a coordinate, the solver never touches artwork, and the emitter never makes a layout decision.

.. py:class:: CompileResult

module:

scenet.pipeline

Bases: :py:class:object

The compiled panel, with the intermediate results kept for inspection.

Diagnostics – notably whether the camera had to retreat to fit the cast – are part of the result rather than log output, so tooling can surface them.

.. py:attribute:: CompileResult.core

module:

scenet.pipeline

type:

~scenet.core.PanelCore

.. py:attribute:: CompileResult.camera

module:

scenet.pipeline

type:

~scenet.solve.camera.CameraSolution

.. py:attribute:: CompileResult.placements

module:

scenet.pipeline

type:

tuple[~scenet.solve.staging.Placement, …]

.. py:attribute:: CompileResult.posed

module:

scenet.pipeline

type:

dict[str, ~scenet.assets.kinematics.ResolvedPuppet]

.. py:property:: CompileResult.notes

module:

scenet.pipeline

type:

tuple[str, …]

Human-readable diagnostics about how this panel was compiled.

returns:

Zero or more sentences describing decisions the compiler had to make that were not literally what the source asked for – a camera that retreated to fit the cast, a balloon tail that had to bend around a face.

These are returned rather than logged so that tooling can put them in front of the person who wrote the panel. A camera that silently retreats leaves you with a panel that is quietly not the shot you asked for, which you would eventually notice and have no way to explain.

.. admonition:: Example

from scenet import compile_source crowded = compile_source( … “{panel: {size: [600.0, 400.0]}, camera: {shot: close_up},” … “ cast: {a: {reference: alice}, b: {reference: bob}},” … “ staging: [a left_of b]}” … ) any(“camera retreated” in note for note in crowded.notes) True

.. py:method:: CompileResult.init(core, camera, placements, posed)

module:

scenet.pipeline

.. py:function:: compile_ir(panel, *, library=None, metrics=None)

module:

scenet.pipeline

Compile validated IR into Panel Core.

rtype:
sphinx_autodoc_typehints_type:

\:py\:class\:\~scenet.pipeline.CompileResult``

.. py:function:: compile_source(text, *, source=None, library=None, metrics=None)

module:

scenet.pipeline

Compile one panel from a source string.

The usual entry point, and the one to reach for first.

type text:
sphinx_autodoc_typehints_type:

\:py\:class\:\str``

param text:

A single-panel document in the YAML surface syntax.

type source:
sphinx_autodoc_typehints_type:

\:py\:class\:\~pathlib.Path` | :py:obj:`None``

param source:

Path the text came from, used only to prefix error messages. Pass it when you have one; the diagnostics are much more useful with it.

type library:
sphinx_autodoc_typehints_type:

\:py\:class\:\~scenet.assets.contract.PuppetLibrary` | :py:obj:`None``

param library:

Characters to draw from. Defaults to the two shipped puppets.

type metrics:
sphinx_autodoc_typehints_type:

\:py\:class\:\~scenet.solve.text.FontMetrics` | :py:obj:`None``

param metrics:

Font to measure lettering against. Defaults to the font that ships as a dependency of this package.

rtype:
sphinx_autodoc_typehints_type:

\:py\:class\:\~scenet.pipeline.CompileResult``

returns:

The compiled panel, with the intermediate results kept for inspection.

raises PanelSyntaxError:

The document is malformed or invalid.

raises UnknownPuppetError:

A cast member references a character the library lacks.

raises LayoutError:

The required constraints cannot all be satisfied.

raises BalloonPlacementError:

A balloon has no legal position.

.. admonition:: Example

from scenet import compile_source, render result = compile_source( … “{cast: {alice: {reference: alice}}, script: [{say: {by: alice, text: Hello.}}]}” … ) len(result.core.balloons) 1 result.core.balloons[0].lines (‘Hello.’,) svg = render(result.core)

.. seealso::

func:

compile_file <scenet.pipeline.compile_file>, to read from disk.

func:

compile_scene <scenet.pipeline.compile_scene>, for multi-panel documents.

func:

compile_document <scenet.pipeline.compile_document>, to dispatch on extension and accept any supported syntax.

.. py:function:: compile_file(path, *, library=None, metrics=None)

module:

scenet.pipeline

Compile one panel from a file.

type path:
sphinx_autodoc_typehints_type:

\:py\:class\:\~pathlib.Path``

param path:

A *.panel.yaml document.

type library:
sphinx_autodoc_typehints_type:

\:py\:class\:\~scenet.assets.contract.PuppetLibrary` | :py:obj:`None``

param library:

Characters to draw from. Defaults to the two shipped puppets.

type metrics:
sphinx_autodoc_typehints_type:

\:py\:class\:\~scenet.solve.text.FontMetrics` | :py:obj:`None``

param metrics:

Font to measure lettering against.

rtype:
sphinx_autodoc_typehints_type:

\:py\:class\:\~scenet.pipeline.CompileResult``

returns:

The compiled panel.

raises OSError:

The file cannot be read.

raises PanelSyntaxError:

The document is malformed or invalid. The path is included in the message.

.. py:function:: compile_scene(text, *, source=None, library=None, metrics=None)

module:

scenet.pipeline

Compile every panel in a multi-panel document.

Each panel is compiled independently. A panel’s composition must not depend on what sits beside it, or the same source would compile differently in isolation – which would make panels non-reusable and golden tests meaningless.

rtype:
sphinx_autodoc_typehints_type:

\:py\:class\:\dict`\ \[:py:class:`str`, :py:class:`~scenet.pipeline.CompileResult`]`

.. py:function:: compile_scene_file(path, *, library=None, metrics=None)

module:

scenet.pipeline

Compile every panel in a multi-panel file.

type path:
sphinx_autodoc_typehints_type:

\:py\:class\:\~pathlib.Path``

param path:

A *.scene.yaml document. A single-panel document also works and comes back as one entry named panel.

type library:
sphinx_autodoc_typehints_type:

\:py\:class\:\~scenet.assets.contract.PuppetLibrary` | :py:obj:`None``

param library:

Characters to draw from. Defaults to the two shipped puppets.

type metrics:
sphinx_autodoc_typehints_type:

\:py\:class\:\~scenet.solve.text.FontMetrics` | :py:obj:`None``

param metrics:

Font to measure lettering against.

rtype:
sphinx_autodoc_typehints_type:

\:py\:class\:\dict`\ \[:py:class:`str`, :py:class:`~scenet.pipeline.CompileResult`]`

returns:

Panel name to compiled panel, in declaration order – which is reading order.

raises OSError:

The file cannot be read.

raises PanelSyntaxError:

A panel is malformed or invalid.

raises CompositionError:

An over: chain refers to a panel that does not exist, or forms a cycle.

.. py:function:: compile_document(path, *, library=None, metrics=None)

module:

scenet.pipeline

Compile any supported document, choosing the frontend by extension.

rtype:
sphinx_autodoc_typehints_type:

\:py\:class\:\dict`\ \[:py:class:`str`, :py:class:`~scenet.pipeline.CompileResult`]`

scenet.compose#

.. py:module:: scenet.compose

Sparse override between panels.

Borrowed from OpenUSD’s composition arcs, and specifically from over: a panel names a parent and states only what differs from it.

This matters more for comics than it might look. Consecutive panels in a scene are mostly identical – the same cast, the same staging, the same camera – with one thing changed. Restating all of it per panel is both tedious and a place for inconsistencies to creep in, which is exactly the continuity error comics readers notice.

   panels:
     p1:
       camera: {shot: medium_shot}
       cast:
         alice: {reference: alice, at: left_third}
     p2:
       over: p1                      # everything from p1...
       camera: {shot: close_up}      # ...except the framing

.. py:function:: merge(base, override)

module:

scenet.compose

Deep-merge override onto base, with override winning.

Mappings merge recursively so that changing one actor’s pose leaves the rest of the cast alone. Lists replace wholesale rather than concatenating: script and staging are ordered wholes, and appending to an inherited script would make it impossible to write a panel where somebody says less than in the panel before.

rtype:
sphinx_autodoc_typehints_type:

\:py\:class\:\dict`\ \[:py:class:`str`, :py:data:`~typing.Any`]`

.. py:function:: resolve_overrides(panels)

module:

scenet.compose

Resolve every panel’s over chain into a self-contained document.

Panels are resolved lazily with memoisation, so a chain is walked once however many panels hang off it, and declaration order does not matter – a panel may inherit from one declared later.

rtype:
sphinx_autodoc_typehints_type:

\:py\:class\:\dict`\ \[:py:class:`str`, :py:class:`dict`\ \[:py:class:`str`, :py:data:`~typing.Any`]]`