ShapeMachine

class ShapeMachine(communication_layer: CommunicationLayer, initial_design: Shape | None = None)

ShapeMachine is the core engine of Shape Machine that enables querying and updating of shapes by Rule objects.

The engine requires a CommunicationLayer to allow for communication between the engine and another program. By default, communication layers initialize their own instance of the engine. This means you don’t have to initialize the engine directly, you should instead initialize a CommunicationLayer and use its engine attribute when you need to work with an instance of ShapeMachine.

Internally, the initial_design is used to shift the entire design (and modified designs) towards the origin. This reduces floating-point error in query computations. This shift is not reflected in the output of engine functions (i.e.: all outputs are shifted back to where the initial design was).

Attributes

initial_design

The initial design tracked by the engine.

current_design

The current design tracked by the engine.

on_next_rule_in_sequence

Optional callable that gets called right before the execution of a new rule during apply_rule_sequence().

on_next_rule_in_program

Optional callable that gets called right before the execution of a new rule during apply_program().

Methods

apply_program

Apply a Program to the current_design by the following process:

apply_rule

Apply a Rule to the current_design by the following process:

apply_rule_sequence

Apply a RuleSequence to the current_design by the following process:

apply_program(program: Program) List[Tuple[Rule, List[Tuple[Shape, Shape]], Shape, Shape, Shape, Shape]]

Apply a Program to the current_design by the following process:

  1. If initial_design is None, set the initial shape from the engine’s CommunicationLayer with import_shape(). This then becomes the current_design and is internally shifted to the origin.

  2. Add the rules within the entry-point RuleSequence to the rule queue.

    • The entry-point sequence is indicated by the program’s entry_point. If this key cannot be found in the program, the engine will request the user to input the entry-point from the set of sequence keys in the program.

  3. While the rule queue is not empty:

    1. Grab the next Rule in the queue.

    2. If this rule is a JumpRule:

      1. Query the current design with the rule (see JumpRule for more information).

      2. If the query is successful, “jump” to the sequence indicated by the rule’s jump_target. This involves inserting all of the rules in the sequence designated by the jump target to the beginning of the rule queue.

    3. Otherwise:

      1. Apply it with apply_rule() with in_script=True.

      2. Add a (rule_applied, updated_design) tuple to the design history.

  4. Return the design history.

This process keeps track of a design history that contains tuples of rules applied and the updated design created after the application of the applied rule. The first entry in the history is (None, initial_design).

Parameters:

program – The program to apply to the current_design.

Returns:

The design history (see above).

apply_rule(rule: Rule, in_script: bool = False) Tuple[Rule, List[Tuple[Shape, Shape]], Shape, Shape, Shape, Shape]

Apply a Rule to the current_design by the following process:

  1. If initial_design is None, set the initial shape from the engine’s CommunicationLayer with import_shape(). This then becomes the current_design and is internally shifted to the origin.

  2. Apply the rule to the current_design.

  3. Replace the current design with the updated design (shifted back to the initial position) using the communication layer’s replace_current_design().

  4. Return the shifted-back updated design.

Parameters:
  • rule – The rule to apply to the current_design.

  • in_script – Passed to the rule’s apply_to() method. See its documentation for more details.

Returns:

The updated design.

apply_rule_sequence(sequence: RuleSequence) List[Tuple[Rule, List[Tuple[Shape, Shape]], Shape, Shape, Shape, Shape]]

Apply a RuleSequence to the current_design by the following process:

  1. If initial_design is None, set the initial shape from the engine’s CommunicationLayer with import_shape(). This then becomes the current_design and is internally shifted to the origin.

  2. For each Rule in the sequence:

    1. Apply it with apply_rule() with in_script=True.

    2. Add a (rule_applied, updated_design) tuple to the design history.

  3. Return the design history.

This process keeps track of a design history that contains tuples of rules applied and the updated design created after the application of the applied rule. The first entry in the history is (None, initial_design).

Parameters:

sequence – The rule sequence to apply to the current_design.

Returns:

The design history (see above).

current_design

The current design tracked by the engine. This is modified by engine functions.

initial_design

The initial design tracked by the engine. Internally, both this and current_design are shifted to the origin to reduce floating-point error in queries. This shift is not reflected in the output of engine functions (i.e.: all outputs are shifted back to where the initial design was).

on_next_rule_in_program: Callable[[str, int], None] | None

Optional callable that gets called right before the execution of a new rule during apply_program(). The parameters passed in are the label of the RuleSequence the rule is in and the line number of the rule in the sequence (starting at 1), respectively. The callable gets called each rule loop.

on_next_rule_in_sequence: Callable[[int], None] | None

Optional callable that gets called right before the execution of a new rule during apply_rule_sequence(). The parameter passed in is the line number of the rule in the sequence (starting at 1). The callable does not get called each rule loop, just when changing rules.