Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"files.associations": {
"cmath": "cpp",
"algorithm": "cpp",
"format": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xutility": "cpp",
"system_error": "cpp"
}
}
90 changes: 90 additions & 0 deletions include/gdstk/layername.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
Copyright 2020 Lucas Heitzmann Gabrielli.
This file is part of gdstk, distributed under the terms of the
Boost Software License - Version 1.0. See the accompanying
LICENSE file or <http://www.boost.org/LICENSE_1_0.txt>
*/

#ifndef GDSTK_HEADER_LAYERNAME
#define GDSTK_HEADER_LAYERNAME

#define __STDC_FORMAT_MACROS 1
#define _USE_MATH_DEFINES

#include <stdint.h>
#include <stdio.h>

#include "utils.hpp"
// #include "oasis.hpp"

namespace gdstk {

struct OasisStream;
struct OasisState;

// based roughly on property.hpp, please see that file for someone who knows what they're doing

// the spec lists tag types 11 and 12 which are representing layer and textlayer data respectively
enum struct LayerNameType : uint8_t {
DATA = static_cast<uint8_t>(OasisRecord::LAYERNAME_DATA),
TEXT = static_cast<uint8_t>(OasisRecord::LAYERNAME_TEXT),
};
// only bound type index 4 (bound to bound) requires two bound values
struct Interval {
OasisInterval type;
union {
uint32_t bound;
struct {
uint32_t bound_a;
uint32_t bound_b;
};
};
};

// Properties are stored as a NULL-terminated linked list. Their name is a
// NULL-terminated string. Property names in the OASIS should be strings of
// characters within the range 0x21 and 0x7E with at least 1 character.
struct LayerName {
LayerNameType type;
char* name;
Interval* LayerInterval;
Interval* TypeInterval;
LayerName* next;
};

// The set_layername functions add layer mappings to the library with the
// given name. A new one is created if none exists or create_new == true.
// New values are added to the start of the value list, so in the OASIS file,
// they will appear in reverse of the insertion order (last added will appear
// first). The NULL byte at the end of string is NOT included in the layer
// name.
void set_layername(LayerName*& layer_names, const char* name, uint32_t layertype,
uint32_t datatype);
void set_textlayername(LayerName*& layer_names, const char* name, uint32_t layertype,
uint32_t datatype);
void set_layername(LayerName*& layer_names, const char* name, Interval* layertype,
Interval* datatype);
void set_textlayername(LayerName*& layer_names, const char* name, Interval* layertype,
Interval* datatype);
// Still thinking about how to deal with bounds that aren't type 3

// removes first instance of layername present in the linked list or all occourances if
// all occourences is set
void remove_layername(LayerName*& layer_names, const char* name, bool all_occurences);

LayerName* get_mapped_layers(LayerName* layer_names, const char* name);

bool layername_contains_layer_and_type(const LayerName* layer_name, uint32_t layertype,
uint32_t datatype);

LayerName* get_names_from_layers(LayerName* layer_names, uint32_t layertype, uint32_t datatype);

void layernames_clear(LayerName*& layer_names);

// These functions output the properties in the OASIS format. They
// are not supposed to be called by the user.
ErrorCode layernames_to_oas(const LayerName* layer_names, OasisStream& out, OasisState& state);

} // namespace gdstk

#endif
4 changes: 4 additions & 0 deletions include/gdstk/library.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ LICENSE file or <http://www.boost.org/LICENSE_1_0.txt>

#include "array.hpp"
#include "cell.hpp"
#include "layername.hpp"

namespace gdstk {

Expand All @@ -38,6 +39,8 @@ struct Library {
Array<RawCell*> rawcell_array;

Property* properties;

LayerName* layer_names;

// Used by the python interface to store the associated PyObject* (if any).
// No functions in gdstk namespace should touch this value!
Expand All @@ -57,6 +60,7 @@ struct Library {
cell_array.clear();
rawcell_array.clear();
properties_clear(properties);
layernames_clear(layer_names);
}

// Clear and free the memory of the whole library (this should be used with
Expand Down
3 changes: 3 additions & 0 deletions python/docstrings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2910,6 +2910,9 @@ PyDoc_STRVAR(library_object_layers_and_texttypes_doc, R"!(layers_and_texttypes()

Return a set of tuples with the layer and text types in the library.)!");

PyDoc_STRVAR(library_object_layer_datatype_names_doc, R"!(layer_datatype_names() -> list
Return a list of tuples with name, layer range and datatype ranges library.)!");

PyDoc_STRVAR(library_object_remap_doc, R"!(remap(layer_type_map) -> self

Remap layers and data/text types for all elements in this library.
Expand Down
7 changes: 7 additions & 0 deletions python/library_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,11 @@ static PyObject* library_object_layers_and_texttypes(LibraryObject* self, PyObje
return result;
}

static PyObject* library_object_layer_datatype_names(LibraryObject* self, PyObject*) {
PyObject* result = build_layername_list(tags);
return result;
}

static PyObject* library_object_remap(LibraryObject* self, PyObject* args, PyObject* kwds) {
PyObject* py_map = NULL;
const char* keywords[] = {"layer_type_map", NULL};
Expand Down Expand Up @@ -492,6 +497,8 @@ static PyMethodDef library_object_methods[] = {
library_object_layers_and_datatypes_doc},
{"layers_and_texttypes", (PyCFunction)library_object_layers_and_texttypes, METH_NOARGS,
library_object_layers_and_texttypes_doc},
{"layer_datatype_names", (PyCFunction)library_object_layer_datatype_names, METH_NOARGS,
library_object_layer_datatype_names_doc},
{"remap", (PyCFunction)library_object_remap, METH_VARARGS | METH_KEYWORDS,
library_object_remap_doc},
{"write_gds", (PyCFunction)library_object_write_gds, METH_VARARGS | METH_KEYWORDS,
Expand Down
78 changes: 78 additions & 0 deletions python/parsing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -633,3 +633,81 @@ static PyObject* build_tag_set(const Set<Tag>& tags) {
}
return result;
}

// not sure if range or slcie is better, using range since you can use the
// in keword with it to determine membership
PyObject* create_range_object(long start, long stop, long step) {
// Import the built-in "range" type

PyObject* range_func = PyObject_GetAttrString(PyEval_GetBuiltins(), "range");
if (!range_func || !PyCallable_Check(range_func)) {
PyErr_SetString(PyExc_RuntimeError, "Could not find built-in 'range'");
Py_XDECREF(range_func);
return NULL;
}

// Call range(start, stop, step)
PyObject* range_args = Py_BuildValue("(lll)", start, stop, step);
PyObject* range_obj = PyObject_CallObject(range_func, range_args);

// Cleanup
Py_DECREF(range_func);
Py_DECREF(range_args);

return range_obj; // May be nullptr if call failed
}

static PyObject* build_layer_interval(const Interval* interval){

if (interval == NULL) {
PyErr_SetString(PyExc_RuntimeError, "Invalid interval.");
return NULL;
}
if (interval->type == OasisInterval::AllValues){
return create_range_object(0,65536,1);
} else if (interval->type == OasisInterval::UpperBound){
return create_range_object(0,interval->bound,1);
} else if (interval->type == OasisInterval::LowerBound){
return create_range_object(interval->bound,65536,1);
} else if (interval->type == OasisInterval::SingleValue){
return create_range_object(interval->bound,interval->bound+1,1);
} else if (interval->type == OasisInterval::Bounded){
return create_range_object(interval->bound_a,interval->bound_b+1,1);
} else {
PyErr_SetString(PyExc_RuntimeError, "Unknown interval type.");
return NULL;
}
}



static PyObject* build_layername_list(const LayerName* layer_names) {
PyObject* result = PyList_New(NULL);
if (!result) {
PyErr_SetString(PyExc_RuntimeError, "Unable to create dict object.");
return NULL;
}
while(layer_names){
PyObject* entry = PyTuple_New(3);
if (!entry) {
PyErr_SetString(PyExc_RuntimeError, "Unable to create (name, layer, type) tuple.");
Py_DECREF(result);
return NULL;
}
// this might need to be a byte string instead of unicode
PyTuple_SET_ITEM(entry, 0, PyUnicode_FromString(layer_names->name));
PyTuple_SET_ITEM(entry, 1, build_layer_interval(&layer_names->layer));
PyTuple_SET_ITEM(entry, 2, build_layer_interval(&layer_names->datatype));
if (PyList_Append(result, entry) < 0) {
PyErr_SetString(PyExc_RuntimeError, "Unable to add item to list.");
Py_DECREF(entry);
Py_DECREF(result);
return NULL;
}
Py_DECREF(entry);
layer_names = layer_names->next;
}
return result;
}


2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ set(HEADER_LIST
"${gdstk_SOURCE_DIR}/include/gdstk/gdstk.hpp"
"${gdstk_SOURCE_DIR}/include/gdstk/gdswriter.hpp"
"${gdstk_SOURCE_DIR}/include/gdstk/label.hpp"
"${gdstk_SOURCE_DIR}/include/gdstk/layername.hpp"
"${gdstk_SOURCE_DIR}/include/gdstk/library.hpp"
"${gdstk_SOURCE_DIR}/include/gdstk/map.hpp"
"${gdstk_SOURCE_DIR}/include/gdstk/oasis.hpp"
Expand All @@ -59,6 +60,7 @@ set(SOURCE_LIST
flexpath.cpp
gdsii.cpp
label.cpp
layername.cpp
library.cpp
oasis.cpp
polygon.cpp
Expand Down
Loading