feat: Add timestamp argument to STEP export

Allow passing a timestamp value when export STEP files, to generate STEP
files with a specific timestamp value in the file header.

For example, a null timestamp (`0000-00-00T00:00:00`), or a static
timestamp can be used when generated files should be equal if there are
no visual changes, such as for file versioning.

This commit extends the `#export_step` function, to accept a `timestamp`
keyword argument, that can be a string or a `datetime` object. A
`datetime` is easier to use from Python.
This commit is contained in:
Jan Graichen 2025-05-06 22:35:59 +02:00
parent cec429c5cc
commit e6c33137b3
2 changed files with 41 additions and 5 deletions

View file

@ -29,10 +29,10 @@ license:
# pylint has trouble with the OCP imports
# pylint: disable=no-name-in-module, import-error
from io import BytesIO
from datetime import datetime
import warnings
from io import BytesIO
from os import PathLike, fsdecode, fspath
from typing import Union
import OCP.TopAbs as ta
from anytree import PreOrderIter
@ -47,7 +47,11 @@ from OCP.RWGltf import RWGltf_CafWriter
from OCP.STEPCAFControl import STEPCAFControl_Controller, STEPCAFControl_Writer
from OCP.STEPControl import STEPControl_Controller, STEPControl_StepModelType
from OCP.StlAPI import StlAPI_Writer
from OCP.TCollection import TCollection_AsciiString, TCollection_ExtendedString, TCollection_HAsciiString
from OCP.TCollection import (
TCollection_AsciiString,
TCollection_ExtendedString,
TCollection_HAsciiString,
)
from OCP.TColStd import TColStd_IndexedDataMapOfStringString
from OCP.TDataStd import TDataStd_Name
from OCP.TDF import TDF_Label
@ -262,6 +266,8 @@ def export_step(
unit: Unit = Unit.MM,
write_pcurves: bool = True,
precision_mode: PrecisionMode = PrecisionMode.AVERAGE,
*, # Too many positional arguments
timestamp: str | datetime | None = None,
) -> bool:
"""export_step
@ -302,6 +308,11 @@ def export_step(
header = APIHeaderSection_MakeHeader(writer.Writer().Model())
if to_export.label:
header.SetName(TCollection_HAsciiString(to_export.label))
if timestamp is not None:
if isinstance(timestamp, datetime):
header.SetTimeStamp(TCollection_HAsciiString(timestamp.isoformat()))
else:
header.SetTimeStamp(TCollection_HAsciiString(timestamp))
# consider using e.g. the non *Value versions instead
# header.SetAuthorValue(1, TCollection_HAsciiString("Volker"));
# header.SetOrganizationValue(1, TCollection_HAsciiString("myCompanyName"));

View file

@ -30,8 +30,10 @@ import json
import os
import re
import unittest
from typing import Optional
from datetime import datetime
from pathlib import Path
from typing import Optional
from zoneinfo import ZoneInfo
import pytest
@ -39,7 +41,7 @@ from build123d.build_common import GridLocations
from build123d.build_enums import Unit
from build123d.build_line import BuildLine
from build123d.build_sketch import BuildSketch
from build123d.exporters3d import export_gltf, export_step, export_brep, export_stl
from build123d.exporters3d import export_brep, export_gltf, export_step, export_stl
from build123d.geometry import Color, Pos, Vector, VectorLike
from build123d.objects_curve import Line
from build123d.objects_part import Box, Sphere
@ -144,6 +146,29 @@ class TestExportStep(DirectApiTestCase):
os.chmod("box_read_only.step", 0o777) # Make the file read/write
os.remove("box_read_only.step")
def test_export_step_timestamp_datetime(self):
b = Box(1, 1, 1)
t = datetime(2025, 5, 6, 21, 30, 25)
self.assertTrue(export_step(b, "box.step", timestamp=t))
with open("box.step", "r") as file:
step_data = file.read()
os.remove("box.step")
self.assertEqual(
re.findall("FILE_NAME\\('[^']*','([^']*)'", step_data),
["2025-05-06T21:30:25"],
)
def test_export_step_timestamp_str(self):
b = Box(1, 1, 1)
self.assertTrue(export_step(b, "box.step", timestamp="0000-00-00T00:00:00"))
with open("box.step", "r") as file:
step_data = file.read()
os.remove("box.step")
self.assertEqual(
re.findall("FILE_NAME\\('[^']*','([^']*)'", step_data),
["0000-00-00T00:00:00"],
)
class TestExportGltf(DirectApiTestCase):
def test_export_gltf(self):