API enhanced for new properties ID scheme, updated doc and tests

This commit is contained in:
Matthias Koefferlein 2024-12-28 17:13:43 +01:00
parent e6ac66f8aa
commit f2dc78f438
4 changed files with 315 additions and 105 deletions

View File

@ -331,12 +331,6 @@ static tl::Variant get_layout_property (const db::Layout *l, const tl::Variant &
return props.value (key);
}
// @@@ should not be needed
static tl::Variant get_properties_hash (const db::Layout * /*layout*/, db::properties_id_type id)
{
return db::properties (id).to_dict_var ();
}
static tl::Variant get_layout_properties (const db::Layout *layout)
{
const db::PropertiesSet &props = db::properties (layout->prop_id ());
@ -565,8 +559,7 @@ static std::vector<db::LayerProperties> layer_infos (const db::Layout *l)
return layers;
}
// @@@ Should be static
static db::properties_id_type properties_id (db::Layout * /*layout*/, const std::vector<tl::Variant> &properties)
static db::properties_id_type properties_id_from_list (const std::vector<tl::Variant> &properties)
{
db::PropertiesSet props;
@ -580,8 +573,13 @@ static db::properties_id_type properties_id (db::Layout * /*layout*/, const std:
return db::properties_id (props);
}
// @@@ Should be static
static db::properties_id_type properties_id_from_hash (db::Layout * /*layout*/, const std::map<tl::Variant, tl::Variant> &properties)
// for backward compatibility
static db::properties_id_type properties_id_from_list_meth (const db::Layout *, const std::vector<tl::Variant> &properties)
{
return properties_id_from_list (properties);
}
static db::properties_id_type properties_id_from_hash (const std::map<tl::Variant, tl::Variant> &properties)
{
db::PropertiesSet props;
@ -592,13 +590,40 @@ static db::properties_id_type properties_id_from_hash (db::Layout * /*layout*/,
return db::properties_id (props);
}
// @@@ Should be static
static tl::Variant properties (const db::Layout * /*layout*/, db::properties_id_type id)
// for backward compatibility
static db::properties_id_type properties_id_from_hash_meth (const db::Layout *, const std::map<tl::Variant, tl::Variant> &properties)
{
return properties_id_from_hash (properties);
}
static tl::Variant get_properties_list (db::properties_id_type id)
{
return db::properties (id).to_list_var ();
}
static void
// for backward compatibility
static tl::Variant get_properties_list_meth (const db::Layout *, db::properties_id_type id)
{
return db::properties (id).to_list_var ();
}
static tl::Variant get_properties_hash (db::properties_id_type id)
{
return db::properties (id).to_dict_var ();
}
// for backward compatibility
static tl::Variant get_properties_hash_meth (const db::Layout *, db::properties_id_type id)
{
return db::properties (id).to_dict_var ();
}
static tl::Variant get_property_from_id (db::properties_id_type id, const tl::Variant &key)
{
return db::properties (id).value (key);
}
static void
delete_cells (db::Layout *layout, const std::vector<db::cell_index_type> &cell_indices)
{
layout->delete_cells (cell_indices.begin (), cell_indices.end ());
@ -1309,18 +1334,40 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"This method has been introduced in version 0.29.5."
) +
gsi::method_ext ("properties_id", &properties_id, gsi::arg ("properties"),
// backward-compatible version as method
gsi::method_ext ("properties_id", &properties_id_from_list_meth, gsi::arg ("properties"),
"@hide"
) +
gsi::method ("properties_id", &properties_id_from_list, gsi::arg ("properties"),
"@brief Gets the properties ID for a given properties set\n"
"\n"
"Before a set of properties can be attached to a shape, it must be converted into an ID that "
"is unique for that set. The properties set must be given as a list of pairs of variants, "
"each pair describing a name and a value. The name acts as the key for the property and does not need to be a string (it can be an integer or double value as well).\n"
"The backward conversion can be performed with the 'properties' method.\n"
"In most places within the system, properties are stored as properties IDs. These are numbers representative for "
"a specific set of properties. This method allows deriving a properties ID from a list of key/value pairs.\n"
"It delivers a unique number for this set. A variant exists that takes a dict object instead of a list of key/value pairs.\n"
"\n"
"The \\properties_array and \\properties_hash methods allow converting the properties ID back into a list or dict object.\n"
"Individual values for a given key can be extracted using \\property in the static (class) method variant.\n"
"\n"
"@code\n"
"pid = RBA::Layout::properties_id([[1, \"one\"], [\"key\", \"value\"]])\n"
"# same as:\n"
"# pid = RBA::Layout::properties_id({ 1 => \"one\", \"key\" => \"value\" })\n"
"\n"
"RBA::Layout::properties_hash(pid) # -> { 1 => \"one\", \"key\" => \"value\" }\n"
"RBA::Layout::property(pid, 1) # -> \"one\"\n"
"@/code\n"
"\n"
"In previous versions, these function were methods of the Layout object. Since version 0.30, they are static (class) methods. "
"This means, that they provide a universal way of converting property sets into IDs and back, without need for a Layout object.\n"
"\n"
"@param properties The array of pairs of variants (both elements can be integer, double or string)\n"
"@return The unique properties ID for that set"
) +
gsi::method_ext ("properties_id", &properties_id_from_hash, gsi::arg ("properties"),
// backward-compatible version as method
gsi::method_ext ("properties_id", &properties_id_from_hash_meth, gsi::arg ("properties"),
"@hide"
) +
gsi::method ("properties_id", &properties_id_from_hash, gsi::arg ("properties"),
"@brief Gets the properties ID for a given properties set\n"
"\n"
"This variant accepts a hash of value vs. key for the properties instead of array of key/value pairs. "
@ -1329,9 +1376,26 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"@param properties A hash of property keys/values (both keys and values can be integer, double or string)\n"
"@return The unique properties ID for that set\n"
"\n"
"This variant has been introduced in version 0.29.7."
"This variant has been introduced in version 0.29.7 and was turned in a static (class) method in 0.30."
) +
gsi::method_ext ("properties_array|#properties", &properties, gsi::arg ("properties_id"),
gsi::method ("property", &get_property_from_id, gsi::arg ("properties_id"), gsi::arg ("key"),
"@brief Extracts a property value for a given key from the properties ID\n"
"\n"
"From a given properties ID, retrieves the value for a given key. If no value for this particular "
"key exists, 'nil' is returned.\n"
"\n"
"For details about the properties ID concept see \\properties_id.\n"
"\n"
"Note, that this is a static (class) method that provides a universal way of extracting property values "
"from IDs without need for a Layout object.\n"
"\n"
"This method has been introduced in version 0.30."
) +
// backward-compatible version as method
gsi::method_ext ("properties_array|properties", &get_properties_list_meth, gsi::arg ("properties_id"),
"@hide"
) +
gsi::method ("properties_array", &get_properties_list, gsi::arg ("properties_id"),
"@brief Gets the properties set for a given properties ID\n"
"\n"
"Basically this method performs the backward conversion of the 'properties_id' method. "
@ -1340,22 +1404,32 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"If the properties ID is not valid, an empty array is returned.\n"
"A version that returns a hash instead of pairs of key/values, is \\properties_hash.\n"
"\n"
"For details about the properties ID concept see \\properties_id.\n"
"\n"
"@param properties_id The properties ID to get the properties for\n"
"@return An array of key/value pairs (see \\properties_id)\n"
"\n"
"The 'properties_array' alias was introduced in version 0.29.7 and the plain 'properties' alias was deprecated."
"The 'properties_array' alias was introduced in version 0.29.7 and the plain 'properties' alias was deprecated. "
"In version 0.30, this method was turned into a static (class method), providing universal conversions without need for a Layout object."
) +
gsi::method_ext ("properties_hash", &get_properties_hash, gsi::arg ("properties_id"),
// backward-compatible version as method
gsi::method_ext ("properties_hash", &get_properties_hash_meth, gsi::arg ("properties_id"),
"@hide"
) +
gsi::method ("properties_hash", &get_properties_hash, gsi::arg ("properties_id"),
"@brief Gets the properties set for a given properties ID as a hash\n"
"\n"
"Returns the properties for a given properties ID as a hash.\n"
"It is a convenient alternative to \\properties_array, which returns "
"an array of key/value pairs.\n"
"\n"
"For details about the properties ID concept see \\properties_id.\n"
"\n"
"@param properties_id The properties ID to get the properties for\n"
"@return The hash representing the properties for the given ID (values vs. key)\n"
"\n"
"This method has been introduced in version 0.29.7."
"This method has been introduced in version 0.29.7. "
"In version 0.30, this method was turned into a static (class method), providing universal conversions without need for a Layout object."
) +
gsi::method ("unique_cell_name", &db::Layout::uniquify_cell_name, gsi::arg ("name"),
"@brief Creates a new unique cell name from the given name\n"

View File

@ -77,6 +77,7 @@ cell.shapes(layer_index).insert(RBA::Box::new(0, 0, 1000, 2000))
layout.write("my_layout.gds")</pre>
<h3>Overview over the Layout object</h3>
<keyword name="Layout Overview"/>
<p>
The basic building blocks of layouts are layers and cells. Layers are not individual objects. Instead, a layer
@ -101,6 +102,30 @@ layout.write("my_layout.gds")</pre>
but requires some careful consideration.
</p>
<p>
KLayout provides certain "working objects" - i.e. standalone geometry primitives that can be used in a number
of low-level algorithms. For example, for rectangles, the <class_doc href="Box"/> object is provided. These objects
represent coordinates as integer units as they are compatible with the internal objects from the database.
By convention, these coordinates are interpreted as multiple of database units, i.e. a width of "100" for a box
converts to "100 nm" for a database unit of 1 nm.
These working objects are described in the "Geometry API" section.
</p>
<p>
In parallel to these objects, there exist floating-point coordinate objects. They are identified by a "D" prefix in
the class name. For example, the floating-point twin of "Box" is <class_doc href="DBox"/>. By convention, the
coordinates of these objects are interpreted as micrometers. Hence, these objects represent physical dimensions
without needing a database unit.
</p>
<p>
Some algorithms, as the ones provided by the "Region" class, cannot act on floating-point objects. Hence their
use is somewhat limited. Still is it convenient to work with such objects in coding layout generators for example.
The Layout database allows retrieving primitives in both integer (database unit) and floating-point (micrometer forms)
and also accepts both forms in most places. Conversion to the internal integer-coordinate representation happens
automatically.
</p>
<p>
The Layout object keeps shapes (texts, polygons, boxes, paths etc.) on "layers". A layer is a collection of
shapes. A layout features a set of layers and each cell provides space for each layer. The shapes are stored
@ -207,6 +232,7 @@ t = RBA::Trans::new(RBA::Trans::r90, 0, 0)
pcell_inst = ly.cell(top).insert(RBA::CellInstArray::new(pcell_var, t))</pre>
<h3>Editable mode</h3>
<keyword name="Editable Mode"/>
<p>
A layout can exist in two flavors: editable and non-editable. In editable mode, some optimisations
@ -232,8 +258,10 @@ non_editable_layout = RBA::Layout.new(false)</pre>
</p>
<h3>Meta information</h3>
<keyword name="Meta"/>
<keyword name="Meta Information"/>
<p>A layout object can keep arbitrary meta data in the form of key/value pairs. This meta data is
<p>A layout object and cell objects can keep arbitrary meta data in the form of key/value pairs. This meta data is
extracted during the reading of a layout and will reflect special properties of the layout file.
For example, the GDS2 library name is available as meta information with key "libname".
</p>
@ -242,13 +270,25 @@ non_editable_layout = RBA::Layout.new(false)</pre>
iterate over the meta data (returning a <class_doc href="LayoutMetaInfo"/> object). <class_doc href="Layout#meta_info_value"/>
will get the value for a given name. <class_doc href="Layout#add_meta_info"/> will add a new meta information object
and <class_doc href="Layout#remove_meta_info"/> will delete one.
Equivalent methods exist for the "Cell" class.
</p>
<p>
Meta information is a different concept than properties.
Meta information is a different concept than properties. While properties are native to the stream format
used, meta information is a KLayout extension. It is mapped to properties in OASIS in the same way, PCell
context information is stored. For GDS2, KLayout provides a special context container inside the GDS2 file
to store that information.
</p>
<p>
Meta information is not subject to the stream format's limitations. For example, keys can be of any type
and values can be arrays, dicts or even higher-level, string-serializable objects such as "Box".
There is also no restriction on size of the attached data, in contrast to properties, where GDS2 imposes
some limitations on the string length for example.
</p>
<h3>Cell related methods</h3>
<keyword name="Cell"/>
<p>Cells can be created using the <class_doc href="Layout#create_cell"/> method. This method expects a cell name. If a cell with that
name already exists, a new name is generated by appending a suffix. The method returns the Cell object of the new
@ -287,6 +327,7 @@ non_editable_layout = RBA::Layout.new(false)</pre>
</p>
<h3>Layer related methods</h3>
<keyword name="Layer"/>
<p><class_doc href="Layout#insert_layer"/> creates a new layer in the layout. The layer will be available to all cells. This method
receives a <class_doc href="LayerInfo"/> object which holds the information about the layer's name, layer
@ -330,6 +371,8 @@ lv.remove_unused_layers</pre>
</p>
<h3>Recursive full or region queries</h3>
<keyword name="Region Query"/>
<keyword name="Recursive Region Query"/>
<p>
A layout provides methods to retrieve shapes recursively. That means, that the shapes are delivered from all
@ -400,84 +443,116 @@ end</pre>
</p>
<h3>Properties</h3>
<keyword name="Properties"/>
<p>
As stated earlier, shapes can carry an arbitrary number of user properties in form of key/value pairs.
For efficiency, these properties are not stored directly but in form of a property ID which identifies
a unique set of properties. Retrieving a property hence requires an indirection over the property ID:
</p>
<pre>layout = RBA::Application::instance.main_window.current_view.active_cellview.layout
# first shape of cell "TOP", layer index 0
layer_index = 0
iter = layout.begin_shapes(layout.cell("TOP").cell_index, layer_index)
shape = iter.shape
# create a hash from the properties of that shape
props = Hash[*layout.properties(shape.prop_id).flatten]
# print the value of the property with key 1
puts props[1]</pre>
<p>
Since that scheme is somewhat tedious to use, a nice shortcut exists by using the
"properties" method on the shape reference. This method implicitly modifies the property
set and assigns a new property ID:
</p>
<pre>layout = RBA::Application::instance.main_window.current_view.active_cellview.layout
# first shape of cell "TOP", layer index 0
layer_index = 0
iter = layout.begin_shapes(layout.cell("TOP").cell_index, layer_index)
shape = iter.shape
# print the value of the property with key 1
puts shape.properties(1)</pre>
<p>
Changing a property requires to obtain a new property ID for the changed set:
</p>
<pre>layout = RBA::Application::instance.main_window.current_view.active_cellview.layout
# first shape of cell "TOP", layer index 0
layer_index = 0
iter = layout.begin_shapes(layout.cell("TOP").cell_index, layer_index)
shape = iter.shape
cell = layout.cell(iter.cell_index)
# create a hash from the properties of that shape
props = Hash[*layout.properties(shape.prop_id).flatten]
# change or add a property with key 1
props[1] = "NewValue"
# store the new properties
shape.prop_id = layout.properties_id(props.to_a)</pre>
<p>
For that problem also a shortcut exists. Use the "set_properties" method on the
shape reference. This method implicitly modifies the property
set and assigns a new property ID:
</p>
<pre>layout = RBA::Application::instance.main_window.current_view.active_cellview.layout
# first shape of cell "TOP", layer index 0
layer_index = 0
iter = layout.begin_shapes(layout.cell("TOP").cell_index, layer_index)
shape = iter.shape
# change or add a property with key 1 and value "NewValue"
shape.set_property(1, "NewValue")</pre>
<p>
A property ID of 0 in general indicates that no properties are attached. Please note that replacing a property ID
and modifying the properties also invalidates any iterators and should not be done in a loop over shapes.
As stated earlier, shapes and other objects can carry an arbitrary number of user properties in form of key/value pairs.
For efficiency, these properties are not stored directly, but in form of a properties ID, which identifies
a unique set of properties.
</p>
<p>
Cell instances and cells also carry a properties ID which can be used to assign user properties to cell instances.
The cell instance properties ID is used like the shape properties ID. The shortcut methods "property", "set_property"
and "delete_property" also are provided for cells and cell instances (<class_doc href="Cell"/> and <class_doc href="Instance"/>).
The objects that can carry properties are shapes (<class_doc href="Shape"/>), instances (<class_doc href="Instance"/>),
Cells (<class_doc href="Cell"/>) and the Layout object itself (<class_doc href="Layout"/>). All these objects
have a "prop_id" method that allows retrieving the properties ID. There is also a setter to change the properties ID.
</p>
<p>
<b>Note:</b> The GDS format does not have string names for properties. As a result GDS only supports numeric property keys.
OASIS, on the other hand, can handle string and numeric property "names". When saving layouts in GDS format, KLayout tries
As it is not quite convenient to work with properties IDs, there are methods to get or manipulate the properties directly.
Those are:
</p>
<ul>
<li> "property" (<class_doc href="Shape#property"/>, <class_doc href="Instance#property"/>,
<class_doc href="Cell#property"/> and <class_doc href="Layout#property"/>) to get the property value
for a specific key.
</li>
<li> "set_property" (<class_doc href="Shape#set_property"/>, <class_doc href="Instance#set_property"/>,
<class_doc href="Cell#set_property"/> and <class_doc href="Layout#set_property"/>) to change or set the property value
for a specific key.
</li>
<li> "delete_property" (<class_doc href="Shape#delete_property"/>, <class_doc href="Instance#delete_property"/>,
<class_doc href="Cell#delete_property"/> and <class_doc href="Layout#delete_property"/>) to remove a specific key
from the properties.
</li>
<li> "properties" (<class_doc href="Shape#properties"/>, <class_doc href="Instance#properties"/>,
and <class_doc href="Cell#properties"/>) to get the properties as a hash (dict).
For the "Layout" object this method is called "properties_hash" (<class_doc href="Layout#properties_hash"/>).
For historical reasons, this class also has a method "properties_array" (<class_doc href="Layout#properties_array"/>)
that delivers the properties as an array of key/value pairs.
</li>
<li> "has_prop_id?" (<class_doc href="Shape#has_prop_id?"/>, <class_doc href="Instance#has_prop_id?"/>,
<class_doc href="Cell#has_prop_id?"/> and <class_doc href="Layout#has_prop_id?"/>) is a predicate indicating
that properties are attached to the object.
</li>
</ul>
<p>
Properties IDs identify a property set uniquely, so identity of the IDs implies identity of the properties set.
In the same fashion, different IDs mean different properties sets.
The ID is an arbitrary integer value and will change between invocations of the program.
The ID value 0 is reserved for "no properties" or an empty property set.
</p>
<p>
Sometimes it is required to work with properties IDs directly.
The Layout object provides four class methods to convert properties IDs to hashes (dicts) or lists
back and forth:
</p>
<ul>
<li> "properties_id" gets the properties ID from an array or hash of properties (<class_doc href="Layout#properties_id"/>).
If used with an array, the array elements need to be key/value pairs.</li>
<li> "properties_array" gets the properties as an array of key/value pairs from a properties ID (<class_doc href="Layout#properties_array"/>).</li>
<li> "properties_dict" gets the properties as a hash (dict) from a properties ID (<class_doc href="Layout#properties_dict"/>).</li>
<li> "property" gets a property value for a given key from a properties ID (<class_doc href="Layout#property"/>).</li>
</ul>
<p>
Here is a sample for working with properties IDs:
</p>
<pre>layout = RBA::Layout::new
# Populate the layout with a layer, a cell and one shape
l1 = layout.layer(1, 0)
top = layout.create_cell("TOP")
box = top.shapes(l1).insert(RBA::Box::new(0, 0, 100, 200))
# Use properties IDs to set initial properties
props = { 1 => "value for key #1", 2 => "value for key #2" }
prop_id = RBA::Layout::properties_id(props)
# same as:
# box.set_property(1, "value for key #1")
# box.set_property(2, "value for key #2")
box.prop_id = prop_id
# Change key 1 property
# same as:
# box.set_property(1, "one")
props = RBA::Layout::properties_hash(box.prop_id)
props[1] = "one"
box.prop_id = RBA::Layout::properties_id(props)</pre>
<p>
Please note that replacing a property ID and modifying the properties usually invalidates any iterators and should
not be done in a loop over shapes.
</p>
<p>
<b>Note:</b> Although property keys can basically be any type, the GDS2 format does not have string names for properties.
Property keys should be positive integers for GDS2.
OASIS, on the other hand, can handle string and numeric property "names". When saving layouts in GDS2 format, KLayout tries
to convert the properties' names into numbers if possible (i.e. if it sees that the string is a number). If it can't, then
the property is not saved. When the layout is read by KLayout upon opening, it gets converted to integer.
the property is not saved. When the layout is read by KLayout upon opening, the property keys will be read as integers.
</p>
<p>
Values can be strings or numerical for OASIS. In GDS2, values are converted to strings always.
</p>
<h2>The LayerInfo class</h2>
@ -485,13 +560,13 @@ shape.set_property(1, "NewValue")</pre>
<keyword name="LayerInfo"/>
<p>
The <class_doc href="LayerInfo"/> object encapsulates the layer's naming properties.
In GDS, a layer is described by a layer number and datatype number. In OASIS, a text name can be added
In GDS2, a layer is described by a layer number and datatype number. In OASIS, a text name can be added
to that description. In other formats like DXF, a layer has just a text name.
</p>
<p>
The LayerInfo object thus has a twofold identity: a numeric identity (layer and datatype number) and
a text layer name. Both properties can be specified. In that case, the numeric identity has precendence
a text layer name. Both properties can be specified. In that case, the numeric identity has precedence
over the text name.
</p>
@ -512,7 +587,7 @@ shape.set_property(1, "NewValue")</pre>
attribute for the datatype number. <class_doc href="LayerInfo#name"/> gives access to the text name. <class_doc href="LayerInfo#is_named?"/> returns
true, if the LayerInfo object represents a named layer (no layer or datatype number are specified).
<class_doc href="LayerInfo#is_equivalent?"/> compares two LayerInfo objects and returns true, if both denote the same
layer. This is not exact equivalence but follows the logical precendence: two layers are equivalent
layer. This is not exact equivalence but follows the logical precedence: two layers are equivalent
if layer or datatype number match (in that case the text name is ignored) or, if no layer and datatype
number are specified, the name matches exactly.
</p>
@ -562,7 +637,7 @@ shape.set_property(1, "NewValue")</pre>
<p>
A cell can be a "ghost cell". A ghost cell is an empty cell which is not written to a layout write and
created when a layout file is read with an unsatisfied reference. Unsatisfied references are present
in some GDS files which represent partial layouts. By simple merging of two GDS files, such references can be
in some GDS2 files which represent partial layouts. By simple merging of two GDS2 files, such references can be
made true instances, when another file contributes the cell for that reference. KLayout supports such
unsatisfied references by providing the "ghost cells" which serve as a instance target but are not written.
Ghost cells are simply cells where the <class_doc href="Cell#is_ghost_cell?"/> attribute is true. An empty cell can be made
@ -578,7 +653,7 @@ shape.set_property(1, "NewValue")</pre>
<p>
Cells can be marked as "ghost cells" using the <class_doc href="Cell#ghost_cell="/>. Ghost cells
are not saved into GDS files (but their references are). Also, ghost cells act as "placeholders" for cells -
are not saved into GDS2 files (but their references are). Also, ghost cells act as "placeholders" for cells -
for example if a cell is pasted into a layout, it will replace any ghost cell with the same name.
If a normal cell with the same name exists, a copy will be created instead. A cell can be asked whether
it is a ghost cell using <class_doc href="Cell#is_ghost_cell?"/>.
@ -586,8 +661,8 @@ shape.set_property(1, "NewValue")</pre>
<p>
Starting with version 0.23, cells can have properties as well, but writing cell properties to layout
files is subject to some restrictions. Properties are only written to GDS if a special option is enabled
because a potentially incompatible extension of GDS is used to store the properties. OASIS files
files is subject to some restrictions. Properties are only written to GDS2 if a special option is enabled
because a potentially incompatible extension of GDS2 is used to store the properties. OASIS files
support cell properties without restrictions.
</p>
@ -829,7 +904,7 @@ end</pre>
<p>
The CellInstArray object represents either single instances or array instances. Array instances correspond
to GDS AREF records and are regular, two-dimensional (not necessarily orthogonal) arrays of instances.
to GDS2 AREF records and are regular, two-dimensional (not necessarily orthogonal) arrays of instances.
A single instance consist of a cell index, denoting the cell that is instantiated and a single transformation,
which can be either a simple, orthogonal affine transformation without a magnification (a Trans object, see
<class_doc href="Trans"/>) or a general affine transformation (a CplxTrans object, see
@ -913,7 +988,7 @@ end</pre>
</p>
<p>
An instance has an equality operator. That operator returns true, if the Instances indentify the same object.
An instance has an equality operator. That operator returns true, if the Instances identify the same object.
</p>
<h2>The Shapes class</h2>
@ -1239,7 +1314,7 @@ end</pre>
<p>
A Shape object represents an edge if it returns true on <class_doc href="Shape#is_edge?"/>.
Edge objects in general are not well supported in KLayout currently. They can be created and manipulated
by scripts, but cannot be drawn or modified on the user interface. In GDS files, edges
by scripts, but cannot be drawn or modified on the user interface. In GDS2 files, edges
are represented by zero-width paths which is sometimes breaking the conventions of other tools.
</p>

View File

@ -1229,6 +1229,35 @@ class DBLayoutTest(unittest.TestCase):
l2 = ly2.layer(1, 0)
self.assertEqual(ly2.top_cell().bbox().to_s(), "(0,10;20,30)")
# Properties IDs
def test_issue1549(self):
ly = pya.Layout.new()
ps1 = { 1: "one", "key": 17 }
ps2 = [ ( 2, "two" ), ( "key", 42 ) ]
pid1 = pya.Layout.properties_id(ps1)
# deprecated, for backward compatibility:
self.assertEqual(ly.properties_array(pid1).__repr__(), "[[1, 'one'], ['key', 17]]")
self.assertEqual(ly.properties_hash(pid1).__repr__(), "{1: 'one', 'key': 17}")
self.assertEqual(pid1, ly.properties_id(ps1))
# static method versions
self.assertEqual(pya.Layout.properties_array(pid1).__repr__(), "[[1, 'one'], ['key', 17]]")
self.assertEqual(pya.Layout.properties_hash(pid1).__repr__(), "{1: 'one', 'key': 17}")
self.assertEqual(pya.Layout.property(pid1, 42).__repr__(), "None")
self.assertEqual(pya.Layout.property(pid1, 1).__repr__(), "'one'")
pid2 = pya.Layout.properties_id(ps2)
# deprecated, for backward compatibility:
self.assertEqual(pid2, ly.properties_id(ps2))
self.assertEqual(ly.properties_array(pid2).__repr__(), "[[2, 'two'], ['key', 42]]")
self.assertEqual(ly.properties_hash(pid2).__repr__(), "{2: 'two', 'key': 42}")
# static method versions
self.assertEqual(pya.Layout.properties_array(pid2).__repr__(), "[[2, 'two'], ['key', 42]]")
self.assertEqual(pya.Layout.properties_hash(pid2).__repr__(), "{2: 'two', 'key': 42}")
self.assertEqual(pya.Layout.property(pid2, 42).__repr__(), "None")
self.assertEqual(pya.Layout.property(pid2, 2).__repr__(), "'two'")
# run unit tests
if __name__ == '__main__':

View File

@ -2334,6 +2334,38 @@ class DBLayoutTests1_TestClass < TestBase
end
# Properties IDs
def test_issue1549
ly = RBA::Layout::new
ps1 = { 1 => "one", "key" => 17 }
ps2 = [ [ 2, "two" ], [ "key", 42 ] ]
pid1 = RBA::Layout::properties_id(ps1)
# deprecated, for backward compatibility:
assert_equal(ly.properties_array(pid1).inspect, "[[1, \"one\"], [\"key\", 17]]")
assert_equal(ly.properties_hash(pid1).inspect, "{1=>\"one\", \"key\"=>17}")
assert_equal(pid1, ly.properties_id(ps1))
# static method versions
assert_equal(RBA::Layout::properties_array(pid1).inspect, "[[1, \"one\"], [\"key\", 17]]")
assert_equal(RBA::Layout::properties_hash(pid1).inspect, "{1=>\"one\", \"key\"=>17}")
assert_equal(RBA::Layout::property(pid1, 42).inspect, "nil")
assert_equal(RBA::Layout::property(pid1, 1).inspect, "\"one\"")
pid2 = RBA::Layout::properties_id(ps2)
# deprecated, for backward compatibility:
assert_equal(pid2, ly.properties_id(ps2))
assert_equal(ly.properties_array(pid2).inspect, "[[2, \"two\"], [\"key\", 42]]")
assert_equal(ly.properties_hash(pid2).inspect, "{2=>\"two\", \"key\"=>42}")
# static method versions
assert_equal(RBA::Layout::properties_array(pid2).inspect, "[[2, \"two\"], [\"key\", 42]]")
assert_equal(RBA::Layout::properties_hash(pid2).inspect, "{2=>\"two\", \"key\"=>42}")
assert_equal(RBA::Layout::property(pid2, 42).inspect, "nil")
assert_equal(RBA::Layout::property(pid2, 2).inspect, "\"two\"")
end
end
load("test_epilogue.rb")