Skip to content

compas_timber.planning ¤

Classes¤

Actor ¤

Enum representing the types of actor which could execute an model instruction.

Methods:¤

get_name classmethod ¤
get_name(value)

Returns the string representation of given actor value.

For use in logging.

Parameters:

  • value (int) –

    One of [Actor.HUMAN, Actor.ROBOT]

Returns:

  • str –

    One of ["HUMAN", "ROBOT"]

Source code in src/compas_timber/planning/sequencer.py
@classmethod
def get_name(cls, value):
    """Returns the string representation of given actor value.

    For use in logging.

    Parameters
    ----------
    value : int
        One of [Actor.HUMAN, Actor.ROBOT]

    Returns
    -------
    str
        One of ["HUMAN", "ROBOT"]

    """
    try:
        return {v: k for k, v in Actor.__dict__.items() if not k.startswith("_")}[value]
    except KeyError:
        return "UNKNOWN_ACTOR"

BeamNester ¤

BeamNester(model, stock_catalog, spacing=0.0, per_group=False)

A class for optimizing 1D nesting of beams into stock pieces.

This class implements algorithms to efficiently nest beams from a TimberModel into available stock pieces, minimizing waste and cost.

Parameters:

  • model (:class:`~compas_timber.model.TimberModel`) –

    The timber model containing beams to nest

  • stock_catalog (list[:class:`BeamStock`]) –

    Available BeamStock pieces for nesting.

  • spacing (float, default: 0.0 ) –

    Spacing tolerance for cutting operations (kerf width, etc.)

  • per_group (bool, default: False ) –

    Whether to nest beams per group or all together. Default is False (all together).

Attributes:

  • model (:class:`~compas_timber.model.TimberModel`) –

    The timber model

  • stock_catalog (list[:class:`BeamStock`]) –

    Available BeamStock pieces for nesting

  • spacing (float) –

    Spacing tolerance for cutting operations (kerf width, etc.)

  • per_group (bool) –

    Whether to nest beams per group or all together. Default is False (all together).

Source code in src/compas_timber/planning/nesting.py
def __init__(self, model, stock_catalog, spacing=0.0, per_group=False):
    self.model = model
    self.spacing = spacing
    self.per_group = per_group
    self.stock_catalog = stock_catalog if isinstance(stock_catalog, list) else [stock_catalog]

Attributes¤

stock_catalog property writable ¤
stock_catalog

Get the stock catalog.

Methods:¤

nest ¤
nest(fast=True)

Perform 1D nesting of all beams in the model.

Parameters:

  • fast (bool, default: True ) –

    Whether to use a fast nesting algorithm (First Fit Decreasing) or a more accurate one (Best Fit Decreasing). Default is True (fast).

Returns:

  • class:`NestingResult` –

    Nesting result containing stocks with assigned beams and metadata

Source code in src/compas_timber/planning/nesting.py
def nest(self, fast=True):
    """
    Perform 1D nesting of all beams in the model.

    Parameters
    ----------
    fast : bool, optional
        Whether to use a fast nesting algorithm (First Fit Decreasing) or a more
        accurate one (Best Fit Decreasing). Default is True (fast).

    Returns
    -------
    :class:`NestingResult`
        Nesting result containing stocks with assigned beams and metadata
    """
    nesting_stocks = []
    if self.per_group:
        # Collect beam groups
        beam_groups = []  # list of lists of beams per group
        standalone_beams = []
        for element in self.model.elements():
            if element.is_group_element:
                group_children = list(self.model.get_elements_in_group(element, filter_=lambda e: e.is_beam))
                if group_children:
                    beam_groups.append(group_children)

            elif element.is_beam and element.parent is None:
                # Handle standalone beams not in a group
                standalone_beams.append(element)
        if standalone_beams:
            beam_groups.append(standalone_beams)

        # Nest each group separately
        for beams in beam_groups:
            stocks = self._nest_beam_collection(beams, fast)
            nesting_stocks.extend(stocks)
    else:
        # Nest ALL beams together
        stocks = self._nest_beam_collection(self.model.beams, fast)
        nesting_stocks.extend(stocks)

    return NestingResult(nesting_stocks, tolerance=self.model.tolerance)

BeamStock ¤

BeamStock(length, cross_section, spacing=0.0, element_data=None)

A class to represent a stock piece for nesting beams.

Similar to a Beam but represents raw material stock that can have beams assigned to it.

Parameters:

  • length (float) –

    Length of the stock piece.

  • cross_section (tuple of float) –

    Cross-section dimensions (width, height).

  • spacing (float, default: 0.0 ) –

    Spacing tolerance for cutting operations (kerf width, etc.).

  • element_data (dict[str, :class:`NestedElementData`], default: None ) –

    Dictionary mapping element GUID (str) to nested element data.

Attributes:

  • length (float) –

    Length of the stock piece.

  • cross_section (tuple of float) –

    Cross-section dimensions sorted in ascending order for consistent comparison.

  • spacing ((float, optional)) –

    Spacing tolerance for cutting operations (kerf width, etc.).

  • element_data (dict[str, :class:`NestedElementData`]) –

    Dictionary mapping element GUID (str) to nested element data.

Source code in src/compas_timber/planning/nesting.py
def __init__(self, length, cross_section, spacing=0.0, element_data=None):
    # Validate cross_section before passing to parent constructor
    if not isinstance(cross_section, (list, tuple)) or len(cross_section) != 2:
        raise ValueError("cross_section must be a tuple or list of 2 dimensions")
    super(BeamStock, self).__init__(length=length, width=cross_section[0], height=cross_section[1], spacing=spacing, element_data=element_data)
    self.cross_section = tuple(cross_section)
    self._current_x_position = 0.0  # Track current position along length for placing beams

Methods:¤

add_element ¤
add_element(beam)

Add a beam to this stock assignment.

Parameters:

  • beam (:class:`~compas_timber.elements.Beam`) –

    The beam to add

Raises:

  • ValueError –

    If beam doesn't fit in remaining space

Source code in src/compas_timber/planning/nesting.py
def add_element(self, beam):
    """
    Add a beam to this stock assignment.

    Parameters
    ----------
    beam : :class:`~compas_timber.elements.Beam`
        The beam to add

    Raises
    ------
    ValueError
        If beam doesn't fit in remaining space
    """
    if not self.can_fit_element(beam):
        warn(f"Beam with length {beam.blank_length} doesn't fit in remaining space {self._remaining_length}")
        return
    # Get position frame based on orientation
    position_frame = self._get_position_frame(beam)
    self._current_x_position += beam.blank_length + self.spacing  # Update position for next beam
    # Store element data using NestedElementData type
    self.element_data[str(beam.guid)] = NestedElementData(
        frame=position_frame,
        key=beam.name + "-" + str(beam.guid)[:4],
        length=beam.blank_length,
    )
can_fit_element ¤
can_fit_element(beam)

Check if a beam can fit in the remaining space.

Parameters:

  • beam (:class:`~compas_timber.elements.Beam`) –

    The beam to check

Returns:

  • bool –

    True if beam fits in remaining length, False otherwise

Source code in src/compas_timber/planning/nesting.py
def can_fit_element(self, beam):
    """
    Check if a beam can fit in the remaining space.

    Parameters
    ----------
    beam : :class:`~compas_timber.elements.Beam`
        The beam to check

    Returns
    -------
    bool
        True if beam fits in remaining length, False otherwise
    """
    return self._remaining_length >= beam.blank_length
is_compatible_with ¤
is_compatible_with(beam)

Check if this stock can accommodate the beam type and dimensions.

For 1D nesting, we only optimize along the length dimension. The cross-sections must be identical.

Parameters:

  • beam (:class:`~compas_timber.elements.Beam`) –

    The beam to check

Returns:

  • bool –

    True if beam is compatible with this stock, False otherwise

Source code in src/compas_timber/planning/nesting.py
def is_compatible_with(self, beam):
    """
    Check if this stock can accommodate the beam type and dimensions.

    For 1D nesting, we only optimize along the length dimension.
    The cross-sections must be identical.

    Parameters
    ----------
    beam : :class:`~compas_timber.elements.Beam`
        The beam to check

    Returns
    -------
    bool
        True if beam is compatible with this stock, False otherwise
    """
    # Use a tolerance-based comparison for cross-section dimensions, regardless of order.
    # Compare as sets, but with tolerance: both must have the same two values, order-insensitive.
    a, b = sorted(self.cross_section)
    x, y = sorted([beam.width, beam.height])
    return TOL.is_close(a, x) and TOL.is_close(b, y)

BuildingPlan ¤

BuildingPlan(steps=None)

Container for building steps, each steps is a collection of instructions which can be visualized

Source code in src/compas_timber/planning/sequencer.py
def __init__(self, steps=None):
    super(BuildingPlan, self).__init__()
    self.steps = steps or []

BuildingPlanParser ¤

Provides class methods to parse and serialize building plans from and to json files.

This implementation does it the COMPAS way. Implemet your own parse() and serialize() methods if you want to use a different format.

Methods:¤

parse classmethod ¤
parse(filepath)

Parses building plan from json file.

Parameters:

  • filepath (str) –

    Path to json file

Returns:

  • class:`BuildingPlan` –
Source code in src/compas_timber/planning/sequencer.py
@classmethod
def parse(cls, filepath):
    """Parses building plan from json file.

    Parameters
    ----------
    filepath : str
        Path to json file

    Returns
    -------
    :class:`BuildingPlan`

    """
    return json_load(filepath)
serialize classmethod ¤
serialize(building_plan, filepath)

Writes building plan to json file.

Parameters:

  • building_plan (:class:`BuildingPlan`) –

    Building plan to be serialized.

  • filepath (str) –

    Path to json file.

Source code in src/compas_timber/planning/sequencer.py
@classmethod
def serialize(cls, building_plan, filepath):
    """Writes building plan to json file.

    Parameters
    ----------
    building_plan : :class:`BuildingPlan`
        Building plan to be serialized.
    filepath : str
        Path to json file.

    """
    json_dump(building_plan, filepath)

Instruction ¤

Instruction(id, location)

Base class for instructions

Source code in src/compas_timber/planning/sequencer.py
def __init__(self, id, location):
    super(Instruction, self).__init__()
    self.id = id
    self.location = location

LinearDimension ¤

LinearDimension(id, location, start, end, char_size, offset)

3d linear dimension

Source code in src/compas_timber/planning/sequencer.py
def __init__(self, id, location, start, end, char_size, offset):
    super(LinearDimension, self).__init__(id, location)
    self.start = start
    self.end = end
    self.char_size = char_size
    self.offset = offset

Model3d ¤

Model3d(id, location, geometry, element_id, obj_filepath)

Instruction which incorporates a 3d model (beam, screw etc.)

Source code in src/compas_timber/planning/sequencer.py
def __init__(self, id, location, geometry, element_id, obj_filepath):
    super(Model3d, self).__init__(id, location)
    self.geometry = geometry
    self.element_id = element_id
    self.obj_filepath = obj_filepath

NestedElementData ¤

NestedElementData(frame, key=None, length=None)

Data container for elements nested within stock pieces.

Parameters:

  • frame (:class:`~compas.geometry.Frame`) –

    The position frame of the element within the stock.

  • key (str, default: None ) –

    A human-readable identifier for the element.

  • length (float, default: None ) –

    The length of the element (for beams).

Attributes:

  • frame (:class:`~compas.geometry.Frame`) –

    The position frame of the element within the stock.

  • key (str or None) –

    A human-readable identifier for the element.

  • length (float or None) –

    The length of the element (for beams), None if not applicable.

Source code in src/compas_timber/planning/nesting.py
def __init__(self, frame, key=None, length=None):
    super(NestedElementData, self).__init__()
    self.frame = frame
    self.key = key
    self.length = length

NestingResult ¤

NestingResult(stocks, tolerance=None)

A wrapper class for nesting results that provides serialization capabilities.

Parameters:

  • stocks (list[:class:`Stock`]) –

    List of stock pieces with assigned beams

  • tolerance (:class:`~compas.tolerance.Tolerance`, default: None ) –

    The tolerance configuration used for this model. TOL if none provided.

Attributes:

  • stocks (list[:class:`Stock`]) –

    List of stock pieces with assigned beams

  • tolerance (:class:`~compas.tolerance.Tolerance`) –

    The tolerance configuration used for this model. TOL if none provided.

  • total_material_volume (float) –

    Total material volume across all stocks in cubic millimeters

  • total_stock_pieces (dict) –

    Detailed report of stock pieces needed with their dimensions

  • summary (str) –

    Human-readable summary of the nesting result

Source code in src/compas_timber/planning/nesting.py
def __init__(self, stocks, tolerance=None):
    super(NestingResult, self).__init__()
    self.stocks = stocks if isinstance(stocks, list) else [stocks]
    self._tolerance = tolerance or TOL

Attributes¤

summary property ¤
summary

Return a human-readable summary of the nesting result.

total_material_volume property ¤
total_material_volume

Calculate total material volume across all stocks in cubic millimeters.

total_stock_pieces property ¤
total_stock_pieces

Generate a detailed report of stock pieces needed with their dimensions.

PlateStock ¤

PlateStock(dimensions, thickness, spacing=0.0, element_data=None)

A class to represent a stock piece for nesting plates.

Similar to a Plate but represents raw material stock that can have plates assigned to it.

Parameters:

  • dimensions –

    Dimensions of the stock piece (length, width).

  • thickness (float) –

    Thickness of the stock piece.

  • spacing (float, default: 0.0 ) –

    Spacing tolerance for cutting operations (kerf width, etc.).

  • element_data (dict[str, :class:`NestedElementData`], default: None ) –

    Dictionary mapping element GUID (str) to nested element data.

Attributes:

  • dimensions (tuple of float) –

    Dimensions of the stock piece (length, width).

  • thickness (float) –

    Thickness of the stock piece

  • spacing ((float, optional)) –

    Spacing tolerance for cutting operations (kerf width, etc.).

  • element_data (dict[str, :class:`NestedElementData`]) –

    Dictionary mapping element GUID (str) to nested element data.

Source code in src/compas_timber/planning/nesting.py
def __init__(self, dimensions, thickness, spacing=0.0, element_data=None):
    # Validate dimensions before passing to parent constructor
    if not isinstance(dimensions, (list, tuple)) or len(dimensions) != 2:
        raise ValueError("dimensions must be a tuple or list of 2 dimensions")
    super(PlateStock, self).__init__(length=dimensions[0], width=dimensions[1], height=thickness, spacing=spacing, element_data=element_data)
    self.dimensions = tuple(dimensions)
    self.thickness = thickness

SimpleSequenceGenerator ¤

SimpleSequenceGenerator(model)

Generates a simple sequence of steps, one step per element. Order of steps is the same as order of elements in model.

Parameters:

  • model (:class:'compas_model.Model') –

    Model to be sequenced.

Attributes:

  • result (:class:`BuildingPlan`) –

    Resulting building plan.

Source code in src/compas_timber/planning/sequencer.py
def __init__(self, model):
    self.model = model

Step ¤

Step(
    element_ids,
    actor=None,
    location=None,
    geometry=None,
    instructions=None,
    is_built=False,
    is_planned=False,
    elements_held=None,
    priority=0,
)

Container for building instructions which assemble a single element

Attributes:

  • actor (:class:`Actor`) –

    Actor which executes the step. one of [Actor.HUMAN, Actor.ROBOT]

  • element_ids (list(int)) –

    List of cad element ids which are associated with the step.

  • elements_held (list(int)) –

    List of cad element ids which are held by the actor (typically a robot) during this step.

  • location (:class:`compas.geometry.Frame`) –

    Location of the step.

  • instructions (list(:class:`Instruction`)) –

    List of instructions which support the step.

  • is_built (bool) –

    Whether the step has been executed.

  • is_planned (bool) –

    Whether the step has been planned. Allows for incremental planning.

  • geometry (str) –

    Geometry type of the element to be assembled. One of ["obj", "cylinder", "box"]]. Used for visualization.

  • priority (int) –

    Priority of the step. Steps within the same priority can be executed in parallel.

Source code in src/compas_timber/planning/sequencer.py
def __init__(
    self,
    element_ids,
    actor=None,
    location=None,
    geometry=None,
    instructions=None,
    is_built=False,
    is_planned=False,
    elements_held=None,
    priority=0,
):
    super(Step, self).__init__()
    self.element_ids = element_ids or []
    self.location = location or Frame.worldXY()
    self.geometry = geometry
    self.priority = priority
    self.instructions = instructions or []
    self.is_built = is_built
    self.is_planned = is_planned
    self.elements_held = elements_held or []
    self._actor = actor

Stock ¤

Stock(length, width, height, spacing=0.0, element_data=None)

A base class to represent a stock piece for nesting.

Parameters:

  • length (float) –

    Length of the stock piece.

  • width (float) –

    Width of the stock piece.

  • height (float) –

    Height of the stock piece.

  • spacing (float, default: 0.0 ) –

    Spacing tolerance for cutting operations (kerf width, etc.).

  • element_data (dict[str, :class:`NestedElementData`], default: None ) –

    Dictionary mapping element GUID (str) to nested element data.

Attributes:

  • length (float) –

    Length of the stock piece.

  • width (float) –

    Width of the stock piece.

  • height (float) –

    Height of the stock piece.

  • spacing ((float, optional)) –

    Spacing tolerance for cutting operations (kerf width, etc.).

  • element_data (dict[str, :class:`NestedElementData`]) –

    Dictionary mapping element GUID (str) to nested element data.

Source code in src/compas_timber/planning/nesting.py
def __init__(self, length, width, height, spacing=0.0, element_data=None):
    super(Stock, self).__init__()
    self.length = length
    self.width = width
    self.height = height
    self.spacing = spacing
    self.element_data = element_data or {}

Methods:¤

add_element ¤
add_element(element)

Add an element to this stock assignment.

Parameters:

  • element (:class:`~compas_timber.elements.Element`) –

    The element to add.

Raises:

  • ValueError –

    If element doesn't fit in remaining space.

Source code in src/compas_timber/planning/nesting.py
def add_element(self, element):
    """
    Add an element to this stock assignment.

    Parameters
    ----------
    element : :class:`~compas_timber.elements.Element`
        The element to add.

    Raises
    ------
    ValueError
        If element doesn't fit in remaining space.
    """
    raise NotImplementedError("This method should be implemented in subclasses.")
can_fit_element ¤
can_fit_element(element)

Check if an element can fit in the remaining space.

Parameters:

  • element (:class:`~compas_timber.elements.Element`) –

    The element to check

Returns:

  • bool –

    True if element fits in remaining space, False otherwise

Source code in src/compas_timber/planning/nesting.py
def can_fit_element(self, element):
    """
    Check if an element can fit in the remaining space.

    Parameters
    ----------
    element : :class:`~compas_timber.elements.Element`
        The element to check

    Returns
    -------
    bool
        True if element fits in remaining space, False otherwise
    """
    raise NotImplementedError("This method should be implemented in subclasses.")
is_compatible_with ¤
is_compatible_with(element)

Check if this stock can accommodate the element type and dimensions.

Parameters:

  • element (:class:`~compas_timber.elements.Element`) –

    The element to check

Returns:

  • bool –

    True if element is compatible with this stock, False otherwise

Source code in src/compas_timber/planning/nesting.py
def is_compatible_with(self, element):
    """
    Check if this stock can accommodate the element type and dimensions.

    Parameters
    ----------
    element : :class:`~compas_timber.elements.Element`
        The element to check

    Returns
    -------
    bool
        True if element is compatible with this stock, False otherwise
    """
    raise NotImplementedError("This method should be implemented in subclasses.")

Text3d ¤

Text3d(id, location, text, size)

Text overlay

Source code in src/compas_timber/planning/sequencer.py
def __init__(self, id, location, text, size):
    super(Text3d, self).__init__(id, location)
    self.text = text
    self.size = size