BTLx Contribution Guide
BTLx processings are machining operations that can be applied to timber elements. This guide provides step-by-step instructions for creating new BTLx processings and integrating them with the COMPAS Timber framework.
Note
For implementing new joint types from already existing BTLx Processings, see the Joint Contribution Guide.
Adding a new BTLx Processing
1. Identify the BTLx Processing and Parameters
First, identify the specific BTLx processing you want to implement from the official BTLx specification: https://design2machine.com/btlx/btlx_2_3_0.pdf
Study the processing definition to understand:
All required parameters and their data types
Parameter constraints and valid ranges
The geometric meaning of each parameter
2. Create the Processing Class
Create a new module in src/compas_timber/fabrication/ that inherits from BTLxProcessing.
The following methods and attributes are the absolute minimum required to implement a processing:
PROCESSING_NAME: Class attribute matching BTLx specification__init__(): Method with meaningful defaults set by the BTLx specification__data__: Property returning a dictionary of processing data for serializationparams: Property returning a parameters instance for BTLx serializationscale(): Method for scaling parameters when units are not set in mm
Additionally, within the same module you need to implement the equivalent parameters class that inherits from BTLxProcessingParams.
The following method is required:
as_dict(): This method converts your processing instance intoOrderedDictwith BTLx parameter names and values as keys and values and is later used by theBTLxWriterto serialize the processing to XML.
Example:
class NewProcessing(BTLxProcessing):
PROCESSING_NAME = "HypotheticalProcessing" # need to match the name in the BTLx specification
@property
def __data__(self):
data = super(NewProcessing, self).__data__
data["arg_a"] = self.arg_a
data["arg_b"] = self.arg_b
return data
def __init__(self, arg_a, arg_b, **kwargs):
super(NewProcessing, self).__init__(**kwargs)
self.arg_a = arg_a
self.arg_b = arg_b
@property
def params(self):
return NewProcessingParams(self)
def scale(self, factor):
self.arg_a *= factor
self.arg_b *= factor
class NewProcessingParams(BTLxProcessingParams):
def as_dict(self):
# ordered, string representation which corresponds with the BTLx specification
result = OrderedDict()
result["ArgA"] = "{:.{prec}f}".format(float(self._instance.arg_a), prec=TOL.precision)
result["ArgB"] = "{:.{prec}f}".format(float(self._instance.arg_b), prec=TOL.precision)
return result
See also
3. Add Alternative Constructors in Processing Class
Implement class methods to create processings from geometric inputs.
This is the geometry → parameters conversion used in joint implementations.
What to implement:
At least one alternative constructor that takes geometric objects and the target element
Extract BTLx parameters from the geometry-element relationship
Return a new processing instance with calculated parameters
A generic
from_shapes_and_element()method that calls your specific alternative constructor
Example:
class NewProcessing(BTLxProcessing):
@classmethod
def from_anygeometry_and_element(cls, geometry, element, additional_arg_1=None, additional_arg_2=None):
# Extract parameters from the geometry and element
arg_a = calculate_arg_a(geometry, element)
arg_b = calculate_arg_b(geometry, element)
return cls(arg_a=arg_a, arg_b=arg_b, arg_c=additional_arg_1, arg_d=additional_arg_2)
@classmethod
def from_shapes_and_element(cls, geometry, element, **kwargs):
# Call the specific constructor implemented above
return cls.from_anygeometry_and_element(geometry, element, **kwargs)
Naming convention: Use descriptive method names that specify the geometric input and target element.
4. Add Geometry Generation Method in Processing Class
Implement a method to convert BTLx parameters back to geometry.
This is the parameters → geometry conversion used in the apply() method and could be the inverse of the alternative constructor.
What to implement:
A method that returns the geometric object needed for the processing operation (cutting plane, mill volume, etc.)
This geometry will be used by the
apply()method to modify the element’s geometry and return the resultUse appropriate error handling with
FeatureApplicationErrorto manage exceptions during geometry generation or application
Example:
class NewProcessing(BTLxProcessing):
def geometry_from_params_and_element(self, element):
# Convert parameters to geometry
# ... implementation of the parameter-to-geometry conversion ...
return feature_geometry_generated_from_params
def apply(self, element_geometry, element):
# Modify the element's geometry using the generated shape
try:
feature_geometry = self.geometry_from_params_and_element(element)
except Exception as e:
raise FeatureApplicationError(feature_geometry=feature_geometry, message=f"Failed to generate geometry from parameters: {e}")
try:
# ... apply the shape to the element geometry
except Exception as e:
raise FeatureApplicationError(feature_geometry=feature_geometry, element_geometry=element_geometry, message=f"Failed to apply geometry to element: {e}")
return modified_element_geometry
Note
While this method is typically the inverse of the alternative constructor, some processings may require generating different geometry for visualization or application purposes.
5. Update Module Imports
Add your new processing to src/compas_timber/fabrication/__init__.py so it can be imported by other modules.
6. Add Tests
Add unit tests in tests/compas_timber/ to verify your processing works correctly. Ensure you cover:
- Parameter validation
- Geometry conversion methods
- Geometry modification in the apply() method
Key Considerations
Reference Sides:
BTLx uses reference sides (RS1-RS6) to define coordinate systems. Use the ref_side_index parameter to specify which face of the element is the reference.
Important
The BTLx specification uses 1-based indexing for reference sides (RS1-RS6), but compas_timber uses 0-based indexing internally (0-5). The BTLxWriter automatically converts from 0-based to 1-based indexing when serializing to BTLx XML format.
Local Coordinate System: All BTLx parameters must be defined in the local coordinate system of the element’s ReferenceSide. When implementing alternative constructors, ensure geometric calculations are converted to the element’s local space.
Bidirectional Geometry-Parameter Conversion: Implement both directions of conversion:
Alternative constructors convert geometry → BTLx parameters
Geometry generation methods convert BTLx parameters → geometry
These methods are inverse operations and should be consistent with each other.
Documentation: Ensure your processing class and parameters class are well-documented, including method docstrings and parameter descriptions.