From 2c963bae25eda75b4aa1580d96cbde93cacefe8c Mon Sep 17 00:00:00 2001 From: Lucas Heitzmann Gabrielli Date: Thu, 4 Jul 2024 10:14:15 -0300 Subject: [PATCH] Add support for Raith MBMS paths (#252) Also updates pki file. Closes #255 Signed-off-by: Lucas Heitzmann Gabrielli --- CHANGELOG.md | 4 + CMakeLists.txt | 2 +- gdstk/_gdstk.pyi | 100 ++++++++++------- include/gdstk/flexpath.hpp | 5 +- include/gdstk/gdsii.hpp | 4 +- include/gdstk/gdstk.hpp | 1 + include/gdstk/raithdata.hpp | 52 +++++++++ include/gdstk/utils.hpp | 1 - python/cell_object.cpp | 2 +- python/docstrings.cpp | 41 ++++++- python/flexpath_object.cpp | 26 ++++- python/gdstk_module.cpp | 69 ++++++++++-- python/label_object.cpp | 3 +- python/polygon_object.cpp | 3 +- python/raithdata_object.cpp | 211 +++++++++++++++++++++++++++++++++++ python/repetition_object.cpp | 3 +- src/CMakeLists.txt | 2 + src/flexpath.cpp | 24 +++- src/library.cpp | 23 +++- src/raithdata.cpp | 60 ++++++++++ src/utils.cpp | 2 - tests/flexpath_test.py | 17 +++ 22 files changed, 589 insertions(+), 66 deletions(-) create mode 100644 include/gdstk/raithdata.hpp create mode 100644 python/raithdata_object.cpp create mode 100644 src/raithdata.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index a37a1a33e..0849a3b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased +### Added +- Support for Raith MBMS path data (thanks Matthew Mckee) + ## 0.9.52 - 2024-04-18 ### Fixed - Infinite loop in `Cell::remap_tags` (#246, thanks dtzitkas!) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ce4f5553..f0270abc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Zi") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Ox /GL -DNDEBUG") else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wshadow -Wvla -Wformat -Wno-missing-field-initializers -Wno-missing-braces") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wshadow -Wvla -Wformat -Wno-missing-field-initializers -Wno-missing-braces -Wno-cast-function-type -Wno-unused-parameter") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -DNDEBUG") endif() diff --git a/gdstk/_gdstk.pyi b/gdstk/_gdstk.pyi index b43fd15d2..3fa18b39c 100644 --- a/gdstk/_gdstk.pyi +++ b/gdstk/_gdstk.pyi @@ -1,7 +1,7 @@ import pathlib import datetime import sys -from typing import Optional, Iterable +from typing import Optional, Iterable, Any from collections.abc import Callable, Sequence if sys.version_info >= (3, 8): @@ -15,7 +15,7 @@ else: from typing_extensions import Self import numpy -from numpy.typing import ArrayLike +from numpy.typing import ArrayLike # type: ignore class Cell: labels: list[Label] @@ -24,11 +24,11 @@ class Cell: polygons: list[Polygon] properties: list[list[str | bytes | float]] references: list[Reference] - def __init__(self, name: str): ... + def __init__(self, name: str) -> None: ... def add(self, *elements: Polygon | FlexPath | RobustPath | Label | Reference) -> Self: ... def area(self, by_spec: bool = False) -> float | dict[tuple[int, int], float]: ... def bounding_box(self) -> Optional[tuple[tuple[float, float], tuple[float, float]]]: ... - def convex_hull(self) -> numpy.ndarray: ... + def convex_hull(self) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... def copy( self, name: str, @@ -90,7 +90,7 @@ class Cell: class Curve: tolerance: float - def __init__(self, xy: tuple[float, float] | complex, tolerance: float = 0.01): ... + def __init__(self, xy: tuple[float, float] | complex, tolerance: float = 0.01) -> None: ... def arc( self, radius: float | tuple[float, float], @@ -125,7 +125,7 @@ class Curve: curve_function: Callable[[float], tuple[float, float] | complex], relative: bool = True, ) -> Self: ... - def points(self) -> numpy.ndarray: ... + def points(self) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... def quadratic( self, xy: Sequence[tuple[float, float] | complex], relative: bool = False ) -> Self: ... @@ -140,6 +140,27 @@ class Curve: def turn(self, radius: float, angle: float) -> Self: ... def vertical(self, y: float | Sequence[float], relative: bool = False) -> Self: ... +class RaithData: + base_cell_name: str + dwelltime_selection: int + pitch_parallel_to_path: float + pitch_perpendicular_to_path: float + pitch_scale: float + periods: int + grating_type: int + dots_per_cycle: int + def __init__( + self, + base_cell_name: str, + dwelltime_selection: int, + pitch_parallel_to_path: float, + pitch_perpendicular_to_path: float, + pitch_scale: float, + periods: int, + grating_type: int, + dots_per_cycle: int, + ) -> None: ... + class FlexPath: bend_function: tuple[ Optional[Callable[[float, float, float, float], list[tuple[float, float]]]], ... @@ -165,6 +186,7 @@ class FlexPath: simple_path: bool size: int tolerance: float + raith_data: RaithData def __init__( self, points: tuple[float, float] | complex | Sequence[tuple[float, float] | complex], @@ -200,8 +222,8 @@ class FlexPath: scale_width: bool = True, layer: int | Sequence[int] = 0, datatype: int | Sequence[int] = 0, - ): ... - def apply_repetition(self) -> list: ... + ) -> None: ... + def apply_repetition(self) -> list[Self]: ... def arc( self, radius: float | tuple[float, float], @@ -261,7 +283,7 @@ class FlexPath: def mirror( self, p1: tuple[float, float] | complex, p2: tuple[float, float] | complex = (0, 0) ) -> Self: ... - def offsets(self) -> numpy.ndarray: ... + def offsets(self) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... def parametric( self, path_function: Callable[[float], tuple[float, float] | complex], @@ -269,7 +291,7 @@ class FlexPath: offset: Optional[float] | Sequence[float] = None, relative: bool = True, ) -> Self: ... - def path_spines(self) -> list[numpy.ndarray]: ... + def path_spines(self) -> list[numpy.ndarray[Any, numpy.dtype[numpy.float64]]]: ... def quadratic( self, xy: Sequence[tuple[float, float] | complex], @@ -317,7 +339,7 @@ class FlexPath: def set_property( self, name: str, value: str | bytes | float | Sequence[str | bytes | float] ) -> Self: ... - def spine(self) -> numpy.ndarray: ... + def spine(self) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... def to_polygons(self) -> list[Polygon]: ... def translate( self, dx: float | tuple[float, float] | complex, dy: Optional[float] = None @@ -336,7 +358,7 @@ class FlexPath: offset: Optional[float] | Sequence[float] = None, relative: bool = False, ) -> Self: ... - def widths(self) -> numpy.ndarray: ... + def widths(self) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... class GdsWriter: def __init__( @@ -347,8 +369,8 @@ class GdsWriter: precision: float = 1e-9, max_points: int = 199, timestamp: Optional[datetime.datetime] = None, - ): ... - def close(self): ... + ) -> None: ... + def close(self) -> None: ... def write(self, *cells: Cell | RawCell) -> Self: ... class Label: @@ -372,7 +394,7 @@ class Label: x_reflection: bool = False, layer: int = 0, texttype: int = 0, - ): ... + ) -> None: ... def apply_repetition(self) -> list[Self]: ... def copy(self) -> Self: ... def delete_gds_property(self, attr: int) -> Self: ... @@ -390,7 +412,9 @@ class Library: precision: float properties: list[list[str | bytes | float]] unit: float - def __init__(self, name: str = "library", unit: float = 1e-6, precision: float = 1e-9): ... + def __init__( + self, name: str = "library", unit: float = 1e-6, precision: float = 1e-9 + ) -> None: ... def add(self, *cells: Cell | RawCell) -> Self: ... def delete_property(self, name: str) -> Self: ... def get_property(self, name: str) -> Optional[list[list[str | bytes | float]]]: ... @@ -409,7 +433,7 @@ class Library: outfile: str | pathlib.Path, max_points: int = 199, timestamp: Optional[datetime.datetime] = None, - ): ... + ) -> None: ... def write_oas( self, outfile: str | pathlib.Path, @@ -419,7 +443,7 @@ class Library: circletolerance: float = 0, standard_properties: bool = False, validation: Optional[Literal["crc32", "checksum32"]] = None, - ): ... + ) -> None: ... class Polygon: datatype: int @@ -430,7 +454,7 @@ class Polygon: size: int def __init__( self, points: Sequence[tuple[float, float] | complex], layer: int = 0, datatype: int = 0 - ): ... + )-> None: ... def apply_repetition(self) -> list[Self]: ... def area(self) -> float: ... def perimeter(self) -> float: ... @@ -462,7 +486,7 @@ class Polygon: x_reflection: bool = False, rotation: float = 0, translation: Optional[tuple[float, float] | complex] = None, - matrix: Optional[ArrayLike] = None, + matrix: Optional[ArrayLike] = None, # type: ignore ) -> Self: ... def translate( self, dx: float | tuple[float, float] | complex, dy: Optional[float] = None @@ -471,7 +495,7 @@ class Polygon: class RawCell: name: str size: int - def __init__(self, name: str): ... + def __init__(self, name: str)-> None: ... def dependencies(self, recursive: bool) -> list[RawCell]: ... class Reference: @@ -493,10 +517,10 @@ class Reference: columns: int = 1, rows: int = 1, spacing: Optional[Sequence[float]] = ..., - ): ... + )-> None: ... def apply_repetition(self) -> list[Self]: ... def bounding_box(self) -> tuple[tuple[float, float], tuple[float, float]]: ... - def convex_hull(self) -> numpy.ndarray: ... + def convex_hull(self) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... def copy(self) -> Self: ... def delete_gds_property(self, attr: int) -> Self: ... def delete_property(self, name: str) -> Self: ... @@ -531,14 +555,14 @@ class Reference: class Repetition: columns: Optional[int] - offsets: Optional[numpy.ndarray] + offsets: Optional[numpy.ndarray[Any, numpy.dtype[numpy.float64]]] rows: Optional[int] size: int spacing: Optional[tuple[float, float]] v1: Optional[tuple[float, float]] v2: Optional[tuple[float, float]] - x_offsets: Optional[numpy.ndarray] - y_offsets: Optional[numpy.ndarray] + x_offsets: Optional[numpy.ndarray[Any, numpy.dtype[numpy.float64]]] + y_offsets: Optional[numpy.ndarray[Any, numpy.dtype[numpy.float64]]] def __init__( self, columns: Optional[int] = None, @@ -549,8 +573,8 @@ class Repetition: offsets: Optional[Sequence[tuple[float, float] | complex]] = None, x_offsets: Optional[Sequence[float]] = None, y_offsets: Optional[Sequence[float]] = None, - ): ... - def get_offsets(self) -> numpy.ndarray: ... + )-> None: ... + def get_offsets(self) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... class RobustPath: datatypes: tuple[int, ...] @@ -588,7 +612,7 @@ class RobustPath: scale_width: bool = True, layer: int | list[int] = 0, datatype: int | list[int] = 0, - ): ... + )->None: ... def apply_repetition(self) -> list[Self]: ... def arc( self, @@ -666,7 +690,7 @@ class RobustPath: def delete_property(self, name: str) -> Self: ... def get_gds_property(self, attr: int) -> Optional[str]: ... def get_property(self, name: str) -> Optional[list[list[str | bytes | float]]]: ... - def gradient(self, u: float, from_below: bool = True) -> numpy.ndarray: ... + def gradient(self, u: float, from_below: bool = True) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... def horizontal( self, x: float, @@ -710,7 +734,7 @@ class RobustPath: def mirror( self, p1: tuple[float, float] | complex, p2: tuple[float, float] | complex = (0, 0) ) -> Self: ... - def offsets(self, u: float, from_below: bool = True) -> numpy.ndarray: ... + def offsets(self, u: float, from_below: bool = True) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... def parametric( self, path_function: Callable[[float], tuple[float, float] | complex], @@ -729,8 +753,8 @@ class RobustPath: ] = None, relative: bool = True, ) -> Self: ... - def path_spines(self) -> list[numpy.ndarray]: ... - def position(self, u: float, from_below: bool = True) -> numpy.ndarray: ... + def path_spines(self) -> list[numpy.ndarray[Any, numpy.dtype[numpy.float64]]]: ... + def position(self, u: float, from_below: bool = True) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... def quadratic( self, xy: Sequence[tuple[float, float] | complex], @@ -796,7 +820,7 @@ class RobustPath: def set_property( self, name: str, value: str | bytes | float | Sequence[str | bytes | float] ) -> Self: ... - def spine(self) -> numpy.ndarray: ... + def spine(self) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... def to_polygons(self) -> list[Polygon]: ... def translate( self, dx: float | tuple[float, float] | complex, dy: Optional[float] = None @@ -825,7 +849,7 @@ class RobustPath: ] = None, relative: bool = False, ) -> Self: ... - def widths(self, u: float, from_below: bool = True) -> numpy.ndarray: ... + def widths(self, u: float, from_below: bool = True) -> numpy.ndarray[Any, numpy.dtype[numpy.float64]]: ... def all_inside( points: Sequence[tuple[float, float] | complex], @@ -860,7 +884,7 @@ def boolean( datatype: int = 0, ) -> list[Polygon]: ... def contour( - data: ArrayLike, + data: ArrayLike, # type: ignore level: int = 0, length_scale: float = 1, precision: float = 0.01, @@ -884,7 +908,7 @@ def ellipse( layer: int = 0, datatype: int = 0, ) -> Polygon: ... -def gds_info(infile: str | pathlib.Path) -> dict: ... +def gds_info(infile: str | pathlib.Path) -> dict[str, Any]: ... # def gds_timestamp(filename: str | pathlib.Path, timestamp:Optional[datetime.datetime]=None) -> datetime.datetime: ... def gds_units(infile: str | pathlib.Path) -> tuple[float, float]: ... @@ -929,7 +953,7 @@ def read_gds( filter: Optional[Iterable[tuple[int, int]]] = None, ) -> Library: ... def read_oas(infile: str | pathlib.Path, unit: float = 0, tolerance: float = 0) -> Library: ... -def read_rawcells(infile: str | pathlib.Path) -> dict: ... +def read_rawcells(infile: str | pathlib.Path) -> dict[str, RawCell]: ... def rectangle( corner1: tuple[float, float] | complex, corner2: tuple[float, float] | complex, diff --git a/include/gdstk/flexpath.hpp b/include/gdstk/flexpath.hpp index f22fdcc45..3fe2ebb18 100644 --- a/include/gdstk/flexpath.hpp +++ b/include/gdstk/flexpath.hpp @@ -12,7 +12,6 @@ LICENSE file or #define _USE_MATH_DEFINES #include -#include #include "array.hpp" #include "curve.hpp" @@ -20,6 +19,7 @@ LICENSE file or #include "pathcommon.hpp" #include "polygon.hpp" #include "property.hpp" +#include "raithdata.hpp" #include "repetition.hpp" #include "utils.hpp" @@ -72,6 +72,9 @@ struct FlexPath { Repetition repetition; Property* properties; + + RaithData raith_data; + // Used by the python interface to store the associated PyObject* (if any). // No functions in gdstk namespace should touch this value! void* owner; diff --git a/include/gdstk/gdsii.hpp b/include/gdstk/gdsii.hpp index 274323d2a..609ef8241 100644 --- a/include/gdstk/gdsii.hpp +++ b/include/gdstk/gdsii.hpp @@ -78,7 +78,9 @@ enum struct GdsiiRecord : uint8_t { ENDMASKS = 0X38, LIBDIRSIZE = 0X39, SRFNAME = 0X3A, - LIBSECUR = 0X3B + LIBSECUR = 0X3B, + RAITHMBMSPATH = 0x5A, + RAITHPXXDATA = 0x62, }; enum struct GdsiiDataType : uint8_t { diff --git a/include/gdstk/gdstk.hpp b/include/gdstk/gdstk.hpp index b5c12e0a1..adc6289b2 100644 --- a/include/gdstk/gdstk.hpp +++ b/include/gdstk/gdstk.hpp @@ -40,6 +40,7 @@ LICENSE file or #include "oasis.hpp" #include "pathcommon.hpp" #include "polygon.hpp" +#include "raithdata.hpp" #include "rawcell.hpp" #include "reference.hpp" #include "repetition.hpp" diff --git a/include/gdstk/raithdata.hpp b/include/gdstk/raithdata.hpp new file mode 100644 index 000000000..8c10b6aaf --- /dev/null +++ b/include/gdstk/raithdata.hpp @@ -0,0 +1,52 @@ +#ifndef GDSTK_HEADER_RAITHDATA +#define GDSTK_HEADER_RAITHDATA + +#include + +namespace gdstk { + +#pragma pack(push, 1) +struct PXXData { + uint8_t calc_only; + uint8_t dwelltime_selection; + uint16_t unused; + double pitch_parallel_to_path; + double pitch_perpendicular_to_path; + double pitch_scale; + int32_t periods; + int32_t grating_type; + int32_t dots_per_cycle; + int32_t ret_base_pixel_count; + int32_t ret_pixel_count; + double ret_stage_speed; + double ret_dwell_time; + uint8_t free[190]; + uint16_t revision; + + void little_endian_swap(); +}; +#pragma pack(pop) + +struct RaithData { + double pitch_parallel_to_path; + double pitch_perpendicular_to_path; + double pitch_scale; + int32_t periods; + int32_t grating_type; + int32_t dots_per_cycle; + uint8_t dwelltime_selection; + char* base_cell_name; + + void* owner; + + void clear(); + + void copy_from(const RaithData& raith_data); + + PXXData to_pxxdata() const; + void from_pxxdata(PXXData const& pxxdata); +}; + +} // namespace gdstk + +#endif diff --git a/include/gdstk/utils.hpp b/include/gdstk/utils.hpp index f7f77d2ea..35ec27292 100644 --- a/include/gdstk/utils.hpp +++ b/include/gdstk/utils.hpp @@ -56,7 +56,6 @@ LICENSE file or #define FSEEK64 fseek #endif -#include #include #include diff --git a/python/cell_object.cpp b/python/cell_object.cpp index 6cf69b800..f9ed45533 100644 --- a/python/cell_object.cpp +++ b/python/cell_object.cpp @@ -133,7 +133,7 @@ static PyObject* cell_object_area(CellObject* self, PyObject* args) { return NULL; } Polygon** p_item = array.items; - for (uint64_t i = 0; i < array.count; i++, p_item++) { + for (uint64_t k = 0; k < array.count; k++, p_item++) { Polygon* poly = *p_item; PyObject* area = PyFloat_FromDouble(poly->area()); if (!area) { diff --git a/python/docstrings.cpp b/python/docstrings.cpp index 1cbda1cac..fda7924fa 100644 --- a/python/docstrings.cpp +++ b/python/docstrings.cpp @@ -796,6 +796,44 @@ PyDoc_STRVAR(reference_object_magnification_doc, R"!(Reference magnification.)!" PyDoc_STRVAR(reference_object_x_reflection_doc, R"!(Reference reflection across the x axis.)!"); +// RaithData + +PyDoc_STRVAR( + raithdata_object_type_doc, + R"!(RaithData(base_cell_name, dwelltime_selection=0, pitch_parallel_to_path=0, pitch_perpendicular_to_path=0, pitch_scale=0, periods=0, grating_type=0, dots_per_cycle=0) + +Raith electron beam lithography data for MBMS paths. + +Args: + base_cell_name (str): Base cell name. + dwelltime_selection (int): Dwell time selection. + pitch_parallel_to_path (float): Pitch parallel to path. + pitch_perpendicular_to_path (float): Pitch perpendicular to path. + pitch_scale (float): Pitch scale. + periods (int): Number of periods. + grating_type (int): Graing type. + dots_per_cycle (int): Number of dots per cycle. + +Notes: + All attributes of RaithData objects are read-only.)!"); + + +PyDoc_STRVAR(raithdata_object_dwelltime_selection_doc, R"!(Dwell time selection.)!"); + +PyDoc_STRVAR(raithdata_object_pitch_parallel_to_path_doc, R"!(Pitch parallel to path.)!"); + +PyDoc_STRVAR(raithdata_object_pitch_perpendicular_to_path_doc, R"!(Pitch perpendicular to path.)!"); + +PyDoc_STRVAR(raithdata_object_pitch_scale_doc, R"!(Pitch scale.)!"); + +PyDoc_STRVAR(raithdata_object_periods_doc, R"!(Number of periods.)!"); + +PyDoc_STRVAR(raithdata_object_grating_type_doc, R"!(Grating type.)!"); + +PyDoc_STRVAR(raithdata_object_dots_per_cycle_doc, R"!(Number of dots per cycle.)!"); + +PyDoc_STRVAR(raithdata_object_base_cell_name_doc, R"!(Base cell name.)!"); + // FlexPath PyDoc_STRVAR( @@ -1550,6 +1588,8 @@ PyDoc_STRVAR(path_object_simple_path_doc, R"!(Simple path flag.)!"); PyDoc_STRVAR(path_object_scale_width_doc, R"!(Scale width flag.)!"); +PyDoc_STRVAR(flexpath_object_raith_data_doc, R"!(Raith electron beam lithography data.)!"); + // RobustPath PyDoc_STRVAR( @@ -2353,7 +2393,6 @@ Create a copy this label. Returns: Copy of this label.)!"); - PyDoc_STRVAR(label_object_apply_repetition_doc, R"!(apply_repetition() -> list Create new labels based on this object's ``repetition`` attribute. diff --git a/python/flexpath_object.cpp b/python/flexpath_object.cpp index 9b054e8ed..90c454254 100644 --- a/python/flexpath_object.cpp +++ b/python/flexpath_object.cpp @@ -1970,7 +1970,8 @@ static PyObject* flexpath_object_delete_gds_property(FlexPathObject* self, PyObj static PyMethodDef flexpath_object_methods[] = { {"copy", (PyCFunction)flexpath_object_copy, METH_NOARGS, flexpath_object_copy_doc}, - {"__deepcopy__", (PyCFunction)flexpath_object_deepcopy, METH_VARARGS | METH_KEYWORDS, flexpath_object_deepcopy_doc}, + {"__deepcopy__", (PyCFunction)flexpath_object_deepcopy, METH_VARARGS | METH_KEYWORDS, + flexpath_object_deepcopy_doc}, {"spine", (PyCFunction)flexpath_object_spine, METH_NOARGS, flexpath_object_spine_doc}, {"path_spines", (PyCFunction)flexpath_object_path_spines, METH_NOARGS, flexpath_object_path_spines_doc}, @@ -2293,6 +2294,27 @@ int flexpath_object_set_repetition(FlexPathObject* self, PyObject* arg, void*) { return 0; } +static PyObject* flexpath_object_get_raith_data(FlexPathObject* self, void*) { + RaithDataObject* obj = PyObject_New(RaithDataObject, &raithdata_object_type); + obj = (RaithDataObject*)PyObject_Init((PyObject*)obj, &raithdata_object_type); + obj->raith_data.copy_from(self->flexpath->raith_data); + return (PyObject*)obj; +} + +int flexpath_object_set_raith_data(FlexPathObject* self, PyObject* arg, void*) { + if (arg == Py_None) { + self->flexpath->raith_data.clear(); + return 0; + } + if (!RaithDataObject_Check(arg)) { + PyErr_SetString(PyExc_TypeError, "Value must be a RaithData object."); + return -1; + } + RaithDataObject* raith_data_obj = (RaithDataObject*)arg; + self->flexpath->raith_data.copy_from(raith_data_obj->raith_data); + return 0; +} + static PyGetSetDef flexpath_object_getset[] = { {"layers", (getter)flexpath_object_get_layers, NULL, flexpath_object_layers_doc, NULL}, {"datatypes", (getter)flexpath_object_get_datatypes, NULL, flexpath_object_datatypes_doc, NULL}, @@ -2314,4 +2336,6 @@ static PyGetSetDef flexpath_object_getset[] = { object_properties_doc, NULL}, {"repetition", (getter)flexpath_object_get_repetition, (setter)flexpath_object_set_repetition, object_repetition_doc, NULL}, + {"raith_data", (getter)flexpath_object_get_raith_data, (setter)flexpath_object_set_raith_data, + flexpath_object_raith_data_doc, NULL}, {NULL}}; diff --git a/python/gdstk_module.cpp b/python/gdstk_module.cpp index 13972db38..850cb9cb8 100644 --- a/python/gdstk_module.cpp +++ b/python/gdstk_module.cpp @@ -23,6 +23,7 @@ LICENSE file or #endif #define CellObject_Check(o) PyObject_TypeCheck((o), &cell_object_type) +#define RaithDataObject_Check(o) PyObject_TypeCheck((o), &raithdata_object_type) #define FlexPathObject_Check(o) PyObject_TypeCheck((o), &flexpath_object_type) #define LabelObject_Check(o) PyObject_TypeCheck((o), &label_object_type) #define LibraryObject_Check(o) PyObject_TypeCheck((o), &library_object_type) @@ -161,6 +162,11 @@ struct RepetitionObject { Repetition repetition; }; +struct RaithDataObject { + PyObject_HEAD; + RaithData raith_data; +}; + static PyTypeObject curve_object_type = {PyVarObject_HEAD_INIT(NULL, 0) "gdstk.Curve", sizeof(CurveObject), 0, @@ -441,6 +447,46 @@ static PyTypeObject repetition_object_type = {PyVarObject_HEAD_INIT(NULL, 0) "gd 0, 0}; +static PyTypeObject raithdata_object_type = {PyVarObject_HEAD_INIT(NULL, 0) "gdstk.RaithData", + sizeof(RaithDataObject), + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + raithdata_object_type_doc, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + PyType_GenericNew, + 0, + 0}; + static PyTypeObject cell_object_type = {PyVarObject_HEAD_INIT(NULL, 0) "gdstk.Cell", sizeof(CellObject), 0, @@ -746,6 +792,7 @@ static Array custom_bend_function(double radius, double initial_angle, dou #include "label_object.cpp" #include "library_object.cpp" #include "polygon_object.cpp" +#include "raithdata_object.cpp" #include "rawcell_object.cpp" #include "reference_object.cpp" #include "repetition_object.cpp" @@ -1871,6 +1918,12 @@ static int gdstk_exec(PyObject* module) { reference_object_type.tp_getset = reference_object_getset; reference_object_type.tp_str = (reprfunc)reference_object_str; + raithdata_object_type.tp_dealloc = (destructor)raithdata_object_dealloc; + raithdata_object_type.tp_init = (initproc)raithdata_object_init; + // raithdata_object_type.tp_methods = raithdata_object_methods; + raithdata_object_type.tp_getset = raithdata_object_getset; + raithdata_object_type.tp_str = (reprfunc)raithdata_object_str; + flexpath_object_type.tp_dealloc = (destructor)flexpath_object_dealloc; flexpath_object_type.tp_init = (initproc)flexpath_object_init; flexpath_object_type.tp_methods = flexpath_object_methods; @@ -1920,16 +1973,14 @@ static int gdstk_exec(PyObject* module) { repetition_object_type.tp_getset = repetition_object_getset; repetition_object_type.tp_str = (reprfunc)repetition_object_str; - char const* names[] = { - "Library", "Cell", "Polygon", "FlexPath", "RobustPath", "Label", - "Reference", "Repetition", "Curve", "RawCell", "GdsWriter", - }; + char const* names[] = {"Library", "Cell", "Polygon", "RaithData", + "FlexPath", "RobustPath", "Label", "Reference", + "Repetition", "Curve", "RawCell", "GdsWriter"}; PyTypeObject* types[] = { - &library_object_type, &cell_object_type, &polygon_object_type, - &flexpath_object_type, &robustpath_object_type, &label_object_type, - &reference_object_type, &repetition_object_type, &curve_object_type, - &rawcell_object_type, &gdswriter_object_type, - }; + &library_object_type, &cell_object_type, &polygon_object_type, + &raithdata_object_type, &flexpath_object_type, &robustpath_object_type, + &label_object_type, &reference_object_type, &repetition_object_type, + &curve_object_type, &rawcell_object_type, &gdswriter_object_type}; for (unsigned long i = 0; i < sizeof(types) / sizeof(types[0]); ++i) { if (PyType_Ready(types[i]) < 0) { Py_DECREF(module); diff --git a/python/label_object.cpp b/python/label_object.cpp index ef048a46c..64b67affc 100644 --- a/python/label_object.cpp +++ b/python/label_object.cpp @@ -196,7 +196,8 @@ static PyObject* label_object_delete_gds_property(LabelObject* self, PyObject* a static PyMethodDef label_object_methods[] = { {"copy", (PyCFunction)label_object_copy, METH_NOARGS, label_object_copy_doc}, - {"__deepcopy__", (PyCFunction)label_object_deepcopy, METH_VARARGS | METH_KEYWORDS, label_object_deepcopy_doc}, + {"__deepcopy__", (PyCFunction)label_object_deepcopy, METH_VARARGS | METH_KEYWORDS, + label_object_deepcopy_doc}, {"apply_repetition", (PyCFunction)label_object_apply_repetition, METH_NOARGS, label_object_apply_repetition_doc}, {"set_property", (PyCFunction)label_object_set_property, METH_VARARGS, object_set_property_doc}, diff --git a/python/polygon_object.cpp b/python/polygon_object.cpp index 987d930c3..4fed594cb 100644 --- a/python/polygon_object.cpp +++ b/python/polygon_object.cpp @@ -430,7 +430,8 @@ static PyObject* polygon_object_delete_gds_property(PolygonObject* self, PyObjec static PyMethodDef polygon_object_methods[] = { {"copy", (PyCFunction)polygon_object_copy, METH_NOARGS, polygon_object_copy_doc}, - {"__deepcopy__", (PyCFunction)polygon_object_deepcopy, METH_VARARGS | METH_KEYWORDS, polygon_object_deepcopy_doc}, + {"__deepcopy__", (PyCFunction)polygon_object_deepcopy, METH_VARARGS | METH_KEYWORDS, + polygon_object_deepcopy_doc}, {"area", (PyCFunction)polygon_object_area, METH_NOARGS, polygon_object_area_doc}, {"perimeter", (PyCFunction)polygon_object_perimeter, METH_NOARGS, polygon_object_perimeter_doc}, {"bounding_box", (PyCFunction)polygon_object_bounding_box, METH_NOARGS, diff --git a/python/raithdata_object.cpp b/python/raithdata_object.cpp new file mode 100644 index 000000000..d95750f04 --- /dev/null +++ b/python/raithdata_object.cpp @@ -0,0 +1,211 @@ +static PyObject* raithdata_object_str(RaithDataObject* self) { + RaithData& raith_data = self->raith_data; + char buffer[GDSTK_PRINT_BUFFER_COUNT]; + snprintf( + buffer, COUNT(buffer), + "RaithData(base_cell_name='%s', dwelltime_selection=%" PRIu8 + ", pitch_parallel_to_path=%lg, pitch_perpendicular_to_path=%lg, pitch_scale=%lg, periods=%" PRIu32 + ", grating_type=%" PRIu32 ", dots_per_cycle=%" PRIu32 ")", + raith_data.base_cell_name ? raith_data.base_cell_name : "", raith_data.dwelltime_selection, + raith_data.pitch_parallel_to_path, raith_data.pitch_perpendicular_to_path, + raith_data.pitch_scale, raith_data.periods, raith_data.grating_type, raith_data.dots_per_cycle); + return PyUnicode_FromString(buffer); +} + +static void raithdata_object_dealloc(RaithDataObject* self) { + self->raith_data.clear(); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static int raithdata_object_init(RaithDataObject* self, PyObject* args, PyObject* kwds) { + const char* keywords[] = {"base_cell_name", + "dwelltime_selection", + "pitch_parallel_to_path", + "pitch_perpendicular_to_path", + "pitch_scale", + "periods", + "grating_type", + "dots_per_cycle", + NULL}; + char const* base_cell_name = NULL; + unsigned int dwelltime_selection = 0; + double pitch_parallel_to_path = 0; + double pitch_perpendicular_to_path = 0; + double pitch_scale = 0; + int periods = 0; + int grating_type = 0; + int dots_per_cycle = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|Idddiii:RaithData", (char**)keywords, + &base_cell_name, &dwelltime_selection, &pitch_parallel_to_path, + &pitch_perpendicular_to_path, &pitch_scale, &periods, + &grating_type, &dots_per_cycle)) + return -1; + + RaithData& raith_data = self->raith_data; + raith_data.clear(); + + raith_data.base_cell_name = copy_string(base_cell_name, NULL); + raith_data.pitch_parallel_to_path = pitch_parallel_to_path; + raith_data.pitch_perpendicular_to_path = pitch_perpendicular_to_path; + raith_data.pitch_scale = pitch_scale; + raith_data.periods = (int32_t)periods; + raith_data.grating_type = (int32_t)grating_type; + raith_data.dots_per_cycle = (int32_t)dots_per_cycle; + raith_data.dwelltime_selection = (uint8_t)dwelltime_selection; + raith_data.owner = self; + return 0; +} + +static PyObject* raithdata_object_get_dwelltime_selection(RaithDataObject* self, void*) { + return PyLong_FromUnsignedLong(self->raith_data.dwelltime_selection); +} + +// int raithdata_object_set_dwelltime_selection(RaithDataObject* self, PyObject* value, void*) { +// if (!PyLong_Check(value)) { +// PyErr_SetString(PyExc_TypeError, +// "The dwelltime_selection attribute value must be an integer."); +// return -1; +// } +// self->raith_data.dwelltime_selection = (uint8_t)PyLong_AsUnsignedLong(value); +// return 0; +// } + +static PyObject* raithdata_object_get_pitch_parallel_to_path(RaithDataObject* self, void*) { + return PyFloat_FromDouble(self->raith_data.pitch_parallel_to_path); +} + +// int raithdata_object_set_pitch_parallel_to_path(RaithDataObject* self, PyObject* value, void*) { +// double new_value = PyFloat_AsDouble(value); +// if (PyErr_Occurred()) { +// PyErr_SetString(PyExc_TypeError, +// "The pitch_parallel_to_path attribute value must be a float."); +// return -1; +// } +// self->raith_data.pitch_parallel_to_path = new_value; +// return 0; +// } + +static PyObject* raithdata_object_get_pitch_perpendicular_to_path(RaithDataObject* self, void*) { + return PyFloat_FromDouble(self->raith_data.pitch_perpendicular_to_path); +} + +// int raithdata_object_set_pitch_perpendicular_to_path(RaithDataObject* self, PyObject* value, +// void*) { +// double new_value = PyFloat_AsDouble(value); +// if (PyErr_Occurred()) { +// PyErr_SetString(PyExc_TypeError, +// "The pitch_perpendicular_to_path attribute value must be a float."); +// return -1; +// } +// self->raith_data.pitch_perpendicular_to_path = new_value; +// return 0; +// } + +static PyObject* raithdata_object_get_pitch_scale(RaithDataObject* self, void*) { + return PyFloat_FromDouble(self->raith_data.pitch_scale); +} + +// int raithdata_object_set_pitch_scale(RaithDataObject* self, PyObject* value, void*) { +// double new_value = PyFloat_AsDouble(value); +// if (PyErr_Occurred()) { +// PyErr_SetString(PyExc_TypeError, "The pitch_scale attribute value must be a float."); +// return -1; +// } +// self->raith_data.pitch_scale = new_value; +// return 0; +// } + +static PyObject* raithdata_object_get_periods(RaithDataObject* self, void*) { + return PyLong_FromLong(self->raith_data.periods); +} + +// int raithdata_object_set_periods(RaithDataObject* self, PyObject* value, void*) { +// if (!PyLong_Check(value)) { +// PyErr_SetString(PyExc_TypeError, "The periods attribute value must be an integer."); +// return -1; +// } +// self->raith_data.periods = (int32_t)PyLong_AsLong(value); +// return 0; +// } + +static PyObject* raithdata_object_get_grating_type(RaithDataObject* self, void*) { + return PyLong_FromLong(self->raith_data.grating_type); +} + +// int raithdata_object_set_grating_type(RaithDataObject* self, PyObject* value, void*) { +// if (!PyLong_Check(value)) { +// PyErr_SetString(PyExc_TypeError, "The grating_type attribute value must be an integer."); +// return -1; +// } +// self->raith_data.grating_type = (int32_t)PyLong_AsLong(value); +// return 0; +// } + +static PyObject* raithdata_object_get_dots_per_cycle(RaithDataObject* self, void*) { + return PyLong_FromLong(self->raith_data.dots_per_cycle); +} + +// int raithdata_object_set_dots_per_cycle(RaithDataObject* self, PyObject* value, void*) { +// if (!PyLong_Check(value)) { +// PyErr_SetString(PyExc_TypeError, "The dots_per_cycle attribute value must be an +// integer."); return -1; +// } +// self->raith_data.dots_per_cycle = (int32_t)PyLong_AsLong(value); +// return 0; +// } + +static PyObject* raithdata_object_get_base_cell_name(RaithDataObject* self, void*) { + PyObject* result = self->raith_data.base_cell_name + ? PyUnicode_FromString(self->raith_data.base_cell_name) + : Py_None; + if (!result) { + PyErr_SetString(PyExc_TypeError, "Unable to convert value to string."); + return NULL; + } + return result; +} + +// int raithdata_object_set_base_cell_name(RaithDataObject* self, PyObject* arg, void*) { +// RaithData& raith_data = self->raith_data; +// +// if (arg == Py_None) { +// if (raith_data.base_cell_name) free_allocation(raith_data.base_cell_name); +// raith_data.base_cell_name = NULL; +// return 0; +// } +// +// if (!PyUnicode_Check(arg)) { +// PyErr_SetString(PyExc_TypeError, "Name must be a string."); +// return -1; +// } +// Py_ssize_t len = 0; +// const char* src = PyUnicode_AsUTF8AndSize(arg, &len); +// if (!src) return -1; +// if (len <= 0) { +// PyErr_SetString(PyExc_ValueError, "Empty cell name."); +// return -1; +// } +// +// if (raith_data.base_cell_name) free_allocation(raith_data.base_cell_name); +// raith_data.base_cell_name = copy_string(src, NULL); +// return 0; +// } + +static PyGetSetDef raithdata_object_getset[] = { + {"dwelltime_selection", (getter)raithdata_object_get_dwelltime_selection, NULL, + raithdata_object_dwelltime_selection_doc, NULL}, + {"pitch_parallel_to_path", (getter)raithdata_object_get_pitch_parallel_to_path, NULL, + raithdata_object_pitch_parallel_to_path_doc, NULL}, + {"pitch_perpendicular_to_path", (getter)raithdata_object_get_pitch_perpendicular_to_path, NULL, + raithdata_object_pitch_parallel_to_path_doc, NULL}, + {"pitch_scale", (getter)raithdata_object_get_pitch_scale, NULL, + raithdata_object_pitch_scale_doc, NULL}, + {"periods", (getter)raithdata_object_get_periods, NULL, raithdata_object_periods_doc, NULL}, + {"grating_type", (getter)raithdata_object_get_grating_type, NULL, + raithdata_object_grating_type_doc, NULL}, + {"dots_per_cycle", (getter)raithdata_object_get_dots_per_cycle, NULL, + raithdata_object_dots_per_cycle_doc, NULL}, + {"base_cell_name", (getter)raithdata_object_get_base_cell_name, NULL, + raithdata_object_base_cell_name_doc, NULL}, + {NULL}}; diff --git a/python/repetition_object.cpp b/python/repetition_object.cpp index 8773b6de3..a7d5f17cb 100644 --- a/python/repetition_object.cpp +++ b/python/repetition_object.cpp @@ -35,8 +35,7 @@ static PyObject* repetition_object_str(RepetitionObject* self) { } static void repetition_object_dealloc(RepetitionObject* self) { - Repetition repetition = self->repetition; - repetition.clear(); + self->repetition.clear(); Py_TYPE(self)->tp_free((PyObject*)self); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 911ae7ce2..a1da39ce7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -40,6 +40,7 @@ set(HEADER_LIST "${gdstk_SOURCE_DIR}/include/gdstk/pathcommon.hpp" "${gdstk_SOURCE_DIR}/include/gdstk/polygon.hpp" "${gdstk_SOURCE_DIR}/include/gdstk/property.hpp" + "${gdstk_SOURCE_DIR}/include/gdstk/raithdata.hpp" "${gdstk_SOURCE_DIR}/include/gdstk/rawcell.hpp" "${gdstk_SOURCE_DIR}/include/gdstk/reference.hpp" "${gdstk_SOURCE_DIR}/include/gdstk/repetition.hpp" @@ -62,6 +63,7 @@ set(SOURCE_LIST oasis.cpp polygon.cpp property.cpp + raithdata.cpp rawcell.cpp reference.cpp repetition.cpp diff --git a/src/flexpath.cpp b/src/flexpath.cpp index 5adff749c..f7fe51376 100644 --- a/src/flexpath.cpp +++ b/src/flexpath.cpp @@ -14,6 +14,7 @@ LICENSE file or #include #include +#include #include #include #include @@ -94,6 +95,7 @@ void FlexPath::print(bool all) const { void FlexPath::clear() { spine.clear(); + raith_data.clear(); FlexPathElement* el = elements; for (uint64_t ne = 0; ne < num_elements; ne++, el++) el->half_width_and_offset.clear(); free_allocation(elements); @@ -110,6 +112,7 @@ void FlexPath::copy_from(const FlexPath& path) { scale_width = path.scale_width; simple_path = path.simple_path; num_elements = path.num_elements; + raith_data.copy_from(path.raith_data); elements = (FlexPathElement*)allocate_clear(num_elements * sizeof(FlexPathElement)); FlexPathElement* src = path.elements; @@ -809,8 +812,9 @@ ErrorCode FlexPath::to_gds(FILE* out, double scaling) { end_type = 0; } + uint16_t path_type = raith_data.base_cell_name ? 0x5A00 : 0x0900; uint16_t buffer_start[] = {4, - 0x0900, + path_type, 6, 0x0D02, (uint16_t)get_layer(el->tag), @@ -822,6 +826,15 @@ ErrorCode FlexPath::to_gds(FILE* out, double scaling) { end_type, 8, 0x0F03}; + + PXXData pxxdata = raith_data.to_pxxdata(); + pxxdata.little_endian_swap(); + + uint64_t len = raith_data.base_cell_name ? strlen(raith_data.base_cell_name) : 0; + if (len % 2) len++; + uint16_t sname_start[] = {(uint16_t)(4 + len), 0x1206}; + big_endian_swap16(sname_start, COUNT(sname_start)); + int32_t width = (scale_width ? 1 : -1) * (int32_t)lround(2 * el->half_width_and_offset[0].u * scaling); big_endian_swap16(buffer_start, COUNT(buffer_start)); @@ -847,6 +860,15 @@ ErrorCode FlexPath::to_gds(FILE* out, double scaling) { for (uint64_t offset_count = offsets.count; offset_count > 0; offset_count--) { fwrite(buffer_start, sizeof(uint16_t), COUNT(buffer_start), out); fwrite(&width, sizeof(int32_t), 1, out); + if (raith_data.base_cell_name) { + fwrite(sname_start, sizeof(uint16_t), COUNT(sname_start), out); + fwrite(raith_data.base_cell_name, 1, len, out); + uint16_t buffer_pxx[] = {(uint16_t)(4 + sizeof(PXXData)), 0x6206}; + big_endian_swap16(buffer_pxx, COUNT(buffer_pxx)); + fwrite(buffer_pxx, sizeof(uint16_t), COUNT(buffer_pxx), out); + fwrite(&pxxdata, 1, sizeof(PXXData), out); + } + if (end_type == 4) { fwrite(buffer_ext1, sizeof(uint16_t), COUNT(buffer_ext1), out); fwrite(ext_size, sizeof(int32_t), 1, out); diff --git a/src/library.cpp b/src/library.cpp index 371c8be09..f6c27c477 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -8,7 +8,6 @@ LICENSE file or #define __STDC_FORMAT_MACROS 1 #define _USE_MATH_DEFINES -#include #include #include #include @@ -36,7 +35,7 @@ struct ByteArray { uint64_t count; uint8_t* bytes; Property* properties; -}; +}; // namespace gdstk void Library::print(bool all) const { printf("Library <%p> %s, unit %lg, precision %lg, %" PRIu64 " cells, %" PRIu64 @@ -961,7 +960,8 @@ Library read_gds(const char* filename, double unit, double tolerance, const Set< break; } - // printf("0x%02X %s (%" PRIu32 " bytes)", buffer[2], gdsii_record_names[buffer[2]], + // printf("0x%02X %s (%" PRIu64 " bytes)", buffer[2], + // buffer[2] < COUNT(gdsii_record_names) ? gdsii_record_names[buffer[2]] : "", // record_length); uint64_t data_length; @@ -1064,12 +1064,20 @@ Library read_gds(const char* filename, double unit, double tolerance, const Set< if (cell) cell->polygon_array.append(polygon); break; case GdsiiRecord::PATH: + case GdsiiRecord::RAITHMBMSPATH: path = (FlexPath*)allocate_clear(sizeof(FlexPath)); path->num_elements = 1; path->elements = (FlexPathElement*)allocate_clear(sizeof(FlexPathElement)); path->simple_path = true; if (cell) cell->flexpath_array.append(path); break; + case GdsiiRecord::RAITHPXXDATA: + if (path) { + PXXData pxxdata; + memcpy(&pxxdata, buffer + 4, record_length); + path->raith_data.from_pxxdata(pxxdata); + } + break; case GdsiiRecord::SREF: case GdsiiRecord::AREF: reference = (Reference*)allocate_clear(sizeof(Reference)); @@ -1191,15 +1199,20 @@ Library read_gds(const char* filename, double unit, double tolerance, const Set< reference = NULL; label = NULL; break; - case GdsiiRecord::SNAME: { + case GdsiiRecord::SNAME: if (reference) { if (str[data_length - 1] == 0) data_length--; reference->name = (char*)allocate(data_length + 1); memcpy(reference->name, str, data_length); reference->name[data_length] = 0; reference->type = ReferenceType::Name; + } else if (path) { + if (str[data_length - 1] == 0) data_length--; + path->raith_data.base_cell_name = (char*)allocate(data_length + 1); + memcpy(path->raith_data.base_cell_name, str, data_length); + path->raith_data.base_cell_name[data_length] = 0; } - } break; + break; case GdsiiRecord::COLROW: if (reference) { Repetition* repetition = &reference->repetition; diff --git a/src/raithdata.cpp b/src/raithdata.cpp new file mode 100644 index 000000000..f5d3913f3 --- /dev/null +++ b/src/raithdata.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include +#include + +namespace gdstk { + +void PXXData::little_endian_swap() { + little_endian_swap16((uint16_t*)(this + offsetof(PXXData, unused)), 1); + little_endian_swap64((uint64_t*)(this + offsetof(PXXData, pitch_parallel_to_path)), 3); + little_endian_swap32((uint32_t*)(this + offsetof(PXXData, periods)), 5); + little_endian_swap64((uint64_t*)(this + offsetof(PXXData, ret_stage_speed)), 2); + little_endian_swap16((uint16_t*)(this + offsetof(PXXData, revision)), 1); +} + +void RaithData::clear() { + if (base_cell_name) { + free_allocation(base_cell_name); + base_cell_name = NULL; + } +} + +void RaithData::copy_from(const RaithData& raith_data) { + pitch_parallel_to_path = raith_data.pitch_parallel_to_path; + pitch_perpendicular_to_path = raith_data.pitch_perpendicular_to_path; + pitch_scale = raith_data.pitch_scale; + periods = raith_data.periods; + grating_type = raith_data.grating_type; + dots_per_cycle = raith_data.dots_per_cycle; + dwelltime_selection = raith_data.dwelltime_selection; + if (base_cell_name) free_allocation(base_cell_name); + if (raith_data.base_cell_name) base_cell_name = copy_string(raith_data.base_cell_name, NULL); +} + +PXXData RaithData::to_pxxdata() const { + PXXData pd{}; + pd.dwelltime_selection = dwelltime_selection; + pd.pitch_parallel_to_path = pitch_parallel_to_path; + pd.pitch_perpendicular_to_path = pitch_perpendicular_to_path; + pd.pitch_scale = pitch_scale; + pd.periods = periods; + pd.grating_type = grating_type; + pd.dots_per_cycle = dots_per_cycle; + memset(pd.free, 0, sizeof(pd.free)); + pd.revision = 1; + return pd; +} + +void RaithData::from_pxxdata(PXXData const& pxxdata) { + pitch_parallel_to_path = pxxdata.pitch_parallel_to_path; + pitch_perpendicular_to_path = pxxdata.pitch_perpendicular_to_path; + pitch_scale = pxxdata.pitch_scale; + periods = pxxdata.periods; + grating_type = pxxdata.grating_type; + dots_per_cycle = pxxdata.dots_per_cycle; + dwelltime_selection = pxxdata.dwelltime_selection; +} + +} // namespace gdstk diff --git a/src/utils.cpp b/src/utils.cpp index 1c2c02e8d..ce0339c0a 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -9,8 +9,6 @@ LICENSE file or #define _USE_MATH_DEFINES #include -#include -#include #include #include #include diff --git a/tests/flexpath_test.py b/tests/flexpath_test.py index da84abd71..f598d5ab6 100644 --- a/tests/flexpath_test.py +++ b/tests/flexpath_test.py @@ -149,3 +149,20 @@ def test_deepcopy(): path2.set_layers(1) assert path.layers == (0,) assert path2.layers == (1,) + + +def test_raith_data(): + path = gdstk.FlexPath(0j, 2) + raith_data = path.raith_data + assert raith_data is not path.raith_data + assert raith_data.base_cell_name is None + + path.raith_data = gdstk.RaithData("CELL", 1, 2, 3, 4, 5, 6, 7) + assert path.raith_data.base_cell_name == "CELL" + assert path.raith_data.dwelltime_selection == 1 + assert path.raith_data.pitch_parallel_to_path == 2 + assert path.raith_data.pitch_perpendicular_to_path == 3 + assert path.raith_data.pitch_scale == 4 + assert path.raith_data.periods == 5 + assert path.raith_data.grating_type == 6 + assert path.raith_data.dots_per_cycle == 7