Merge branch 'master' of github.com:KLayout/klayout

This commit is contained in:
Matthias Koefferlein 2026-04-11 00:19:39 +02:00
commit 63a948a77b
57 changed files with 1763 additions and 655 deletions

View File

@ -48,7 +48,7 @@ jobs:
large-packages: true
- uses: hmarr/debug-action@v3
- name: Cancel Workflow Action
uses: styfle/cancel-workflow-action@0.13.0
uses: styfle/cancel-workflow-action@0.13.1
- uses: actions/checkout@v6
- name: ccache
if: matrix.os != 'ubuntu-24.04-arm'
@ -66,7 +66,7 @@ jobs:
mkdir -p $HOST_CCACHE_DIR
- name: Build wheels (ARM)
if: matrix.os == 'ubuntu-24.04-arm'
uses: pypa/cibuildwheel@v3.3.1
uses: pypa/cibuildwheel@v3.4.0
env:
# override the default CentOS “yum install … ccache” and drop ccache
CIBW_BEFORE_ALL_LINUX: |
@ -81,7 +81,7 @@ jobs:
- name: Build wheels (all other platforms)
if: matrix.os != 'ubuntu-24.04-arm'
uses: pypa/cibuildwheel@v3.3.1
uses: pypa/cibuildwheel@v3.4.0
env:
CIBW_BUILD: ${{ matrix.cibuild }}
CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }}

View File

@ -41,7 +41,9 @@ INCLUDEPATH += $$RBA_INC
DEPENDPATH += $$RBA_INC
equals(HAVE_RUBY, "1") {
LIBS += -lklayout_rba
INCLUDEPATH += $$DRC_INC $$LVS_INC
DEPENDPATH += $$DRC_INC $$LVS_INC
LIBS += -lklayout_rba -lklayout_drc -lklayout_lvs
} else {
LIBS += -lklayout_rbastub
}

View File

@ -153,10 +153,10 @@ void clip (ClipData &data)
// write the layout
db::SaveLayoutOptions save_options;
save_options.set_format_from_filename (data.file_out);
std::string of = save_options.set_format_from_filename (data.file_out).second;
data.writer_options.configure (save_options, target_layout);
tl::OutputStream stream (data.file_out);
tl::OutputStream stream (of);
db::Writer writer (save_options);
writer.write (target_layout, stream);
}
@ -176,7 +176,9 @@ BD_PUBLIC int strmclip (int argc, char *argv[])
<< tl::arg ("output", &data.file_out, "The output file",
"The output format is determined from the suffix of the file. If the suffix indicates "
"gzip compression, the file will be compressed on output. Examples for recognized suffixes are "
"\".oas\", \".gds.gz\", \".dxf\" or \".gds2\"."
"\".oas\", \".gds.gz\", \".dxf\" or \".gds2\". You can also use any name, and specify the "
"desired suffix in square brackets after the file name. For example, 'file.clip[oas]' will "
"create an OASIS file called 'file.clip'."
)
<< tl::arg ("-l|--clip-layer=spec", &data, &ClipData::set_clip_layer, "Specifies a layer to take the clip regions from",
"If this option is given, the clip rectangles are taken from the given layer."

View File

@ -36,6 +36,8 @@
#include "libForceLink.h"
#include "rdbForceLink.h"
#include "pexForceLink.h"
#include "drcForceLink.h"
#include "lvsForceLink.h"
#include "lymMacro.h"
#include "lymMacroCollection.h"

View File

@ -408,7 +408,9 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
<< tl::arg ("?output", &output, "The output file to which the XOR differences are written",
"This argument is optional. If not given, the exit status alone will indicate whether the layouts "
"are identical or not. The output is a layout file. The format of the file is derived "
"from the file name's suffix (.oas[.gz] for (gzipped) OASIS, .gds[.gz] for (gzipped) GDS2 etc.)."
"from the file name's suffix (.oas for OASIS, .gds[.gz] for (gzipped) GDS2 etc.). "
"You can also use any name, and specify the desired suffix in square brackets after the file name. "
"For example, 'file.xor[oas]' will create an OASIS file called 'file.xor'."
)
<< tl::arg ("-ta|--top-a=name", &top_a, "Specifies the top cell for the first layout",
"Use this option to take a specific cell as the top cell from the first layout. All "
@ -587,10 +589,10 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
if (output_layout.get ()) {
db::SaveLayoutOptions save_options;
save_options.set_format_from_filename (output);
std::string of = save_options.set_format_from_filename (output).second;
writer_options.configure (save_options, *output_layout);
tl::OutputStream stream (output);
tl::OutputStream stream (of);
db::Writer writer (save_options);
writer.write (*output_layout, stream);

View File

@ -25,7 +25,7 @@ INCLUDEPATH += $$RBA_INC
DEPENDPATH += $$RBA_INC
equals(HAVE_RUBY, "1") {
LIBS += -lklayout_rba
LIBS += -lklayout_rba -lklayout_drc -lklayout_lvs
} else {
LIBS += -lklayout_rbastub
}

View File

@ -212,6 +212,34 @@ TEST(7)
db::compare_layouts (this, layout, input, db::NoNormalization);
}
// Small DEF example with explicit format
TEST(8)
{
std::string input = tl::testdata ();
input += "/lefdef/strm2oas_small/in.defok[def]";
std::string input_au = tl::testdata ();
input_au += "/lefdef/strm2oas_small/au.gds";
std::string output = this->tmp_file ("small_def");
const char *argv[] = { "x", input.c_str (), output.c_str () };
EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::LoadLayoutOptions options;
db::Reader reader (stream);
reader.read (layout, options);
EXPECT_EQ (reader.format (), "OASIS");
}
db::compare_layouts (this, layout, input_au, db::WriteGDS2);
}
// Large LEF/DEF to OAS converter test
TEST(10)
{

View File

@ -97,6 +97,34 @@ TEST(2)
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
EXPECT_EQ (reader.format (), "GDS2");
}
db::compare_layouts (this, layout, au, db::NoNormalization);
}
// with explicit output format
TEST(3)
{
std::string input = tl::testdata ();
input += "/bd/strm2clip_in.gds";
std::string au = tl::testdata ();
au += "/bd/strm2clip_au2.gds";
std::string output = this->tmp_file () + "[oas]";
const char *argv[] = { "x", input.c_str (), output.c_str (), "-r=0,-2,9,5", "-t", "INV2", "-x=CLIP_OUT" };
EXPECT_EQ (strmclip (sizeof (argv) / sizeof (argv[0]), (char **) argv), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
EXPECT_EQ (reader.format (), "OASIS");
}
db::compare_layouts (this, layout, au, db::NoNormalization);

View File

@ -28,10 +28,7 @@
TEST(1)
{
#if defined(HAVE_PYTHON)
std::string fp (tl::testsrc ());
fp += "/testdata/bd/strmrun.py";
std::string cmd;
std::string cmd_call;
#if defined(__APPLE__)
// NOTE: because of system integrity, MacOS does not inherit DYLD_LIBRARY_PATH to child
@ -39,19 +36,42 @@ TEST(1)
const char *ldpath_name = "DYLD_LIBRARY_PATH";
const char *ldpath = getenv (ldpath_name);
if (ldpath) {
cmd += std::string (ldpath_name) + "=\"" + ldpath + "\"; export " + ldpath_name + "; ";
cmd_call += std::string (ldpath_name) + "=\"" + ldpath + "\"; export " + ldpath_name + "; ";
}
#endif
cmd += tl::combine_path (tl::get_inst_path (), "strmrun ") + fp;
tl::info << cmd;
cmd_call += tl::combine_path (tl::get_inst_path (), "strmrun");
tl::InputPipe pipe (cmd);
tl::InputStream is (pipe);
std::string data = is.read_all ();
tl::info << data;
{
std::string fp (tl::testsrc ());
fp += "/testdata/bd/strmrun.py";
std::string cmd = cmd_call + " " + fp;
tl::info << cmd;
tl::InputPipe pipe (cmd);
tl::InputStream is (pipe);
std::string data = is.read_all ();
tl::info << data;
EXPECT_EQ (data, "Hello, world (0,-42;42,0)!\n");
}
{
std::string fp (tl::testsrc ());
fp += "/testdata/bd/strmrun.drc";
std::string cmd = cmd_call + " " + fp;
tl::info << cmd;
tl::InputPipe pipe (cmd);
tl::InputStream is (pipe);
std::string data = is.read_all ();
tl::info << data;
EXPECT_EQ (data, "This is DRC.\n");
}
EXPECT_EQ (data, "Hello, world (0,-42;42,0)!\n");
#endif
}

View File

@ -97,6 +97,52 @@ TEST(1A_Flat)
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
EXPECT_EQ (std::string (reader.format ()), "OASIS");
}
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
"Result summary (layers without differences are not shown):\n"
"\n"
" Layer Output Differences (shape count)\n"
" ----------------------------------------------------------------\n"
" 3/0 3/0 30\n"
" 6/0 6/0 41\n"
" 8/1 8/1 1\n"
" 10/0 - (no such layer in first layout)\n"
"\n"
);
}
TEST(1A_FlatWithExplicitOutputFormat)
{
tl::CaptureChannel cap;
std::string input_a = tl::testdata ();
input_a += "/bd/strmxor_in1.gds";
std::string input_b = tl::testdata ();
input_b += "/bd/strmxor_in2.gds";
std::string au = tl::testdata ();
au += "/bd/strmxor_au1.oas";
std::string output = this->tmp_file ("tmp.xxx[oas]");
const char *argv[] = { "x", input_a.c_str (), input_b.c_str (), output.c_str () };
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
EXPECT_EQ (std::string (reader.format ()), "OASIS");
}
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));

View File

@ -152,6 +152,7 @@ SOURCES = \
gsiDeclDbRecursiveInstanceIterator.cc \
gsiDeclDbRecursiveShapeIterator.cc \
gsiDeclDbRegion.cc \
gsiDeclDbSaveLayoutOptions.cc \
gsiDeclDbShape.cc \
gsiDeclDbShapeProcessor.cc \
gsiDeclDbShapes.cc \

View File

@ -113,9 +113,8 @@ EdgePairs::write (const std::string &fn) const
unsigned int li = layout.insert_layer (db::LayerProperties (0, 0));
insert_into (&layout, top.cell_index (), li);
tl::OutputStream os (fn);
db::SaveLayoutOptions opt;
opt.set_format_from_filename (fn);
tl::OutputStream os (opt.set_format_from_filename (fn).second);
db::Writer writer (opt);
writer.write (layout, os);
}

View File

@ -145,9 +145,8 @@ Edges::write (const std::string &fn) const
unsigned int li = layout.insert_layer (db::LayerProperties (0, 0));
insert_into (&layout, top.cell_index (), li);
tl::OutputStream os (fn);
db::SaveLayoutOptions opt;
opt.set_format_from_filename (fn);
tl::OutputStream os (opt.set_format_from_filename (fn).second);
db::Writer writer (opt);
writer.write (layout, os);
}

View File

@ -134,13 +134,26 @@ ReaderBase::check_dbu (double dbu) const
Reader::Reader (tl::InputStream &stream)
: mp_actual_reader (0), m_stream (stream)
{
// Detect the format by asking all reader declarations
for (tl::Registrar<db::StreamFormatDeclaration>::iterator rdr = tl::Registrar<db::StreamFormatDeclaration>::begin (); rdr != tl::Registrar<db::StreamFormatDeclaration>::end () && ! mp_actual_reader; ++rdr) {
m_stream.reset ();
if (rdr->detect (m_stream)) {
m_stream.reset ();
mp_actual_reader = rdr->create_reader (m_stream);
if (stream.is_explicit_suffix ()) {
// If an explicit suffix is given for the stream, use it to find the reader
for (tl::Registrar<db::StreamFormatDeclaration>::iterator rdr = tl::Registrar<db::StreamFormatDeclaration>::begin (); rdr != tl::Registrar<db::StreamFormatDeclaration>::end () && ! mp_actual_reader; ++rdr) {
if (tl::match_filename_to_format ("." + stream.suffix (), rdr->file_format ())) {
mp_actual_reader = rdr->create_reader (m_stream);
}
}
} else {
// Detect the format by asking all reader declarations
for (tl::Registrar<db::StreamFormatDeclaration>::iterator rdr = tl::Registrar<db::StreamFormatDeclaration>::begin (); rdr != tl::Registrar<db::StreamFormatDeclaration>::end () && ! mp_actual_reader; ++rdr) {
m_stream.reset ();
if (rdr->detect (m_stream)) {
m_stream.reset ();
mp_actual_reader = rdr->create_reader (m_stream);
}
}
}
if (! mp_actual_reader) {

View File

@ -142,9 +142,8 @@ Region::write (const std::string &fn) const
unsigned int li = layout.insert_layer (db::LayerProperties (0, 0));
insert_into (&layout, top.cell_index (), li);
tl::OutputStream os (fn);
db::SaveLayoutOptions opt;
opt.set_format_from_filename (fn);
tl::OutputStream os (opt.set_format_from_filename (fn).second);
db::Writer writer (opt);
writer.write (layout, os);
}

View File

@ -27,6 +27,7 @@
#include "tlStream.h"
#include "tlExpression.h"
#include "tlInternational.h"
#include "tlGlobPattern.h"
namespace db
{
@ -444,16 +445,33 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set <db::cell_index
}
}
bool
std::pair<bool, std::string>
SaveLayoutOptions::set_format_from_filename (const std::string &fn)
{
for (tl::Registrar<db::StreamFormatDeclaration>::iterator fmt = tl::Registrar<db::StreamFormatDeclaration>::begin (); fmt != tl::Registrar<db::StreamFormatDeclaration>::end (); ++fmt) {
if (tl::match_filename_to_format (fn, fmt->file_format ())) {
m_format = fmt->format_name ();
return true;
tl::GlobPattern pat ("(*)\\[(*)\\]");
std::vector<std::string> pat_parts;
if (pat.match (fn, pat_parts) && pat_parts.size () == 2) {
for (tl::Registrar<db::StreamFormatDeclaration>::iterator fmt = tl::Registrar<db::StreamFormatDeclaration>::begin (); fmt != tl::Registrar<db::StreamFormatDeclaration>::end (); ++fmt) {
if (tl::match_filename_to_format ("." + pat_parts[1], fmt->file_format ())) {
m_format = fmt->format_name ();
return std::make_pair (true, pat_parts[0]);
}
}
} else {
for (tl::Registrar<db::StreamFormatDeclaration>::iterator fmt = tl::Registrar<db::StreamFormatDeclaration>::begin (); fmt != tl::Registrar<db::StreamFormatDeclaration>::end (); ++fmt) {
if (tl::match_filename_to_format (fn, fmt->file_format ())) {
m_format = fmt->format_name ();
return std::make_pair (true, fn);
}
}
}
return false;
return std::make_pair (false, fn);
}
}

View File

@ -268,7 +268,7 @@ public:
*
* Returns true, if the suffix indicates a known format.
*/
bool set_format_from_filename (const std::string &fn);
std::pair<bool, std::string> set_format_from_filename(const std::string &fn);
/**
* @brief Sets specific options for the given format

View File

@ -71,7 +71,7 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std::
} else {
// write the temp file in the same format than the au file
tmp_file = _this->tmp_file (tl::sprintf ("tmp_%x." + tl::extension (au_file), hash));
options.set_format_from_filename (tmp_file);
tmp_file = options.set_format_from_filename (tmp_file).second;
}
if ((norm & NoContext) != 0) {

View File

@ -109,9 +109,8 @@ Texts::write (const std::string &fn) const
unsigned int li = layout.insert_layer (db::LayerProperties (0, 0));
insert_into (&layout, top.cell_index (), li);
tl::OutputStream os (fn);
db::SaveLayoutOptions opt;
opt.set_format_from_filename (fn);
tl::OutputStream os (opt.set_format_from_filename (fn).second);
db::Writer writer (opt);
writer.write (layout, os);
}

View File

@ -1310,10 +1310,10 @@ write_simple (const db::Cell *cell, const std::string &filename)
db::SaveLayoutOptions options;
options.clear_cells ();
options.add_cell (cell->cell_index ());
options.set_format_from_filename (filename);
std::string fn = options.set_format_from_filename (filename).second;
db::Writer writer (options);
tl::OutputStream stream (filename);
tl::OutputStream stream (fn);
writer.write (*layout, stream);
}

View File

@ -690,12 +690,13 @@ static void
write_simple (db::Layout *layout, const std::string &filename)
{
db::SaveLayoutOptions options;
if (! options.set_format_from_filename (filename)) {
auto ff = options.set_format_from_filename (filename);
if (! ff.first) {
throw tl::Exception (tl::to_string (tr ("Cannot determine format from filename")));
}
db::Writer writer (options);
tl::OutputStream stream (filename);
tl::OutputStream stream (ff.second);
writer.write (*layout, stream);
}
@ -2863,231 +2864,4 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"@/code\n"
);
static db::SaveLayoutOptions *new_v ()
{
return new db::SaveLayoutOptions ();
}
static bool set_format_from_filename (db::SaveLayoutOptions *opt, const std::string &fn)
{
if (! opt->set_format_from_filename (fn)) {
throw tl::Exception (tl::to_string (tr ("Cannot determine format from filename")));
}
return true;
}
Class<db::SaveLayoutOptions> decl_SaveLayoutOptions ("db", "SaveLayoutOptions",
gsi::constructor ("new", &new_v,
"@brief Default constructor\n"
"\n"
"This will initialize the scale factor to 1.0, the database unit is set to\n"
"\"same as original\" and all layers are selected as well as all cells.\n"
"The default format is GDS2."
) +
gsi::method_ext ("set_format_from_filename", &set_format_from_filename, gsi::arg ("filename"),
"@brief Select a format from the given file name\n"
"\n"
"This method will set the format according to the file's extension.\n"
"\n"
"This method has been introduced in version 0.22. "
"Beginning with version 0.23, this method always returns true, since the "
"only consumer for the return value, Layout#write, now ignores that "
"parameter and automatically determines the compression mode from the file name.\n"
) +
gsi::method ("format=", &db::SaveLayoutOptions::set_format, gsi::arg ("format"),
"@brief Select a format\n"
"The format string can be either \"GDS2\", \"OASIS\", \"CIF\" or \"DXF\". Other formats may be available if\n"
"a suitable plugin is installed."
) +
gsi::method ("format", &db::SaveLayoutOptions::format,
"@brief Gets the format name\n"
"\n"
"See \\format= for a description of that method.\n"
) +
gsi::method ("add_layer", &db::SaveLayoutOptions::add_layer, gsi::arg ("layer_index"), gsi::arg ("properties"),
"@brief Add a layer to be saved \n"
"\n"
"\n"
"Adds the layer with the given index to the layer list that will be written.\n"
"If all layers have been selected previously, all layers will \n"
"be unselected first and only the new layer remains.\n"
"\n"
"The 'properties' argument can be used to assign different layer properties than the ones\n"
"present in the layout. Pass a default \\LayerInfo object to this argument to use the\n"
"properties from the layout object. Construct a valid \\LayerInfo object with explicit layer,\n"
"datatype and possibly a name to override the properties stored in the layout.\n"
) +
gsi::method ("select_all_layers", &db::SaveLayoutOptions::select_all_layers,
"@brief Select all layers to be saved\n"
"\n"
"This method will clear all layers selected with \\add_layer so far and set the 'select all layers' flag.\n"
"This is the default.\n"
) +
gsi::method ("deselect_all_layers", &db::SaveLayoutOptions::deselect_all_layers,
"@brief Unselect all layers: no layer will be saved\n"
"\n"
"This method will clear all layers selected with \\add_layer so far and clear the 'select all layers' flag.\n"
"Using this method is the only way to save a layout without any layers."
) +
gsi::method ("select_cell", &db::SaveLayoutOptions::select_cell, gsi::arg ("cell_index"),
"@brief Selects a cell to be saved (plus hierarchy below)\n"
"\n"
"\n"
"This method is basically a convenience method that combines \\clear_cells and \\add_cell.\n"
"This method clears the 'select all cells' flag.\n"
"\n"
"This method has been added in version 0.22.\n"
) +
gsi::method ("select_this_cell", &db::SaveLayoutOptions::select_this_cell, gsi::arg ("cell_index"),
"@brief Selects a cell to be saved\n"
"\n"
"\n"
"This method is basically a convenience method that combines \\clear_cells and \\add_this_cell.\n"
"This method clears the 'select all cells' flag.\n"
"\n"
"This method has been added in version 0.23.\n"
) +
gsi::method ("clear_cells", &db::SaveLayoutOptions::clear_cells,
"@brief Clears all cells to be saved\n"
"\n"
"This method can be used to ensure that no cell is selected before \\add_cell is called to specify a cell.\n"
"This method clears the 'select all cells' flag.\n"
"\n"
"This method has been added in version 0.22.\n"
) +
gsi::method ("add_this_cell", &db::SaveLayoutOptions::add_this_cell, gsi::arg ("cell_index"),
"@brief Adds a cell to be saved\n"
"\n"
"\n"
"The index of the cell must be a valid index in the context of the layout that will be saved.\n"
"This method clears the 'select all cells' flag.\n"
"Unlike \\add_cell, this method does not implicitly add all children of that cell.\n"
"\n"
"This method has been added in version 0.23.\n"
) +
gsi::method ("add_cell", &db::SaveLayoutOptions::add_cell, gsi::arg ("cell_index"),
"@brief Add a cell (plus hierarchy) to be saved\n"
"\n"
"\n"
"The index of the cell must be a valid index in the context of the layout that will be saved.\n"
"This method clears the 'select all cells' flag.\n"
"\n"
"This method also implicitly adds the children of that cell. A method that does not add the "
"children in \\add_this_cell.\n"
) +
gsi::method ("select_all_cells", &db::SaveLayoutOptions::select_all_cells,
"@brief Select all cells to save\n"
"\n"
"This method will clear all cells specified with \\add_cells so far and set the 'select all cells' flag.\n"
"This is the default.\n"
) +
gsi::method ("write_context_info=", &db::SaveLayoutOptions::set_write_context_info, gsi::arg ("flag"),
"@brief Enables or disables context information\n"
"\n"
"If this flag is set to false, no context information for PCell or library cell instances is written. "
"Those cells will be converted to plain cells and KLayout will not be able to restore the identity of "
"those cells. Use this option to enforce compatibility with other tools that don't understand the "
"context information of KLayout.\n"
"\n"
"The default value is true (context information is stored). Not all formats support context information, hence "
"that flag has no effect for formats like CIF or DXF.\n"
"\n"
"This method was introduced in version 0.23.\n"
) +
gsi::method ("write_context_info?", &db::SaveLayoutOptions::write_context_info,
"@brief Gets a flag indicating whether context information will be stored\n"
"\n"
"See \\write_context_info= for details about this flag.\n"
"\n"
"This method was introduced in version 0.23.\n"
) +
gsi::method ("keep_instances=", &db::SaveLayoutOptions::set_keep_instances, gsi::arg ("flag"),
"@brief Enables or disables instances for dropped cells\n"
"\n"
"If this flag is set to true, instances for cells will be written, even if the cell is dropped. "
"That may happen, if cells are selected with \\select_this_cell or \\add_this_cell or \\no_empty_cells is used. "
"Even if cells called by such cells are not selected, instances will be written for that "
"cell if \"keep_instances\" is true. That feature is supported by the GDS format currently and "
"results in \"ghost cells\" which have instances but no cell definition.\n"
"\n"
"The default value is false (instances of dropped cells are not written).\n"
"\n"
"This method was introduced in version 0.23.\n"
) +
gsi::method ("keep_instances?", &db::SaveLayoutOptions::keep_instances,
"@brief Gets a flag indicating whether instances will be kept even if the target cell is dropped\n"
"\n"
"See \\keep_instances= for details about this flag.\n"
"\n"
"This method was introduced in version 0.23.\n"
) +
gsi::method ("dbu=", &db::SaveLayoutOptions::set_dbu, gsi::arg ("dbu"),
"@brief Sets the database unit to be used in the stream file\n"
"\n"
"By default, the database unit of the layout is used. This method allows one to explicitly use a different\n"
"database unit. A scale factor is introduced automatically which scales all layout objects accordingly so their physical dimensions remain the same. "
"When scaling to a larger database unit or one that is not an integer fraction of the original one, rounding errors may occur and the "
"layout may become slightly distorted."
) +
gsi::method ("dbu", &db::SaveLayoutOptions::dbu,
"@brief Gets the explicit database unit if one is set\n"
"\n"
"See \\dbu= for a description of that attribute.\n"
) +
gsi::method ("no_empty_cells=", &db::SaveLayoutOptions::set_dont_write_empty_cells, gsi::arg ("flag"),
"@brief Don't write empty cells if this flag is set\n"
"\n"
"By default, all cells are written (no_empty_cells is false).\n"
"This applies to empty cells which do not contain shapes for the specified layers "
"as well as cells which are empty because they reference empty cells only.\n"
) +
gsi::method ("no_empty_cells?", &db::SaveLayoutOptions::dont_write_empty_cells,
"@brief Returns a flag indicating whether empty cells are not written.\n"
) +
gsi::method ("libname=|#gds2_libname=", &db::SaveLayoutOptions::set_libname, gsi::arg ("libname"),
"@brief Sets the library name\n"
"\n"
"The library name is an attribute and specifies a formal name for a library, if the layout files is to be used as one.\n"
"Currently, this attribute is only supported by the GDS2 format. Hence the alias.\n"
"\n"
"By default or if the libname is an empty string, the current library name of the layout or 'LIB' is used.\n"
"\n"
"The 'libname' alias has been introduced in version 0.30.5. The original name \\gds2_libname= is still available."
) +
gsi::method ("libname|#gds2_libname", &db::SaveLayoutOptions::libname,
"@brief Gets the library name\n"
"\n"
"See \\libname= for details.\n"
"The 'libname' alias has been introduced in version 0.30.5. The original name \\gds2_libname is still available."
) +
gsi::method ("scale_factor=", &db::SaveLayoutOptions::set_scale_factor, gsi::arg ("scale_factor"),
"@brief Sets the scaling factor for the saving \n"
"\n"
"Using a scaling factor will scale all objects accordingly. "
"This scale factor adds to a potential scaling implied by using an explicit database unit.\n"
"\n"
"Be aware that rounding effects may occur if fractional scaling factors are used.\n"
"\n"
"By default, no scaling is applied."
) +
gsi::method ("scale_factor", &db::SaveLayoutOptions::scale_factor,
"@brief Gets the scaling factor currently set\n"
),
"@brief Options for saving layouts\n"
"\n"
"This class describes the various options for saving a layout to a stream file (GDS2, OASIS and others).\n"
"There are: layers to be saved, cell or cells to be saved, scale factor, format, database unit\n"
"and format specific options.\n"
"\n"
"Usually the default constructor provides a suitable object. Please note, that the format written is \"GDS2\" by default. Either explicitly set a "
"format using \\format= or derive the format from the file name using \\set_format_from_filename.\n"
"\n"
"The layers are specified by either selecting all layers or by defining layer by layer using the\n"
"\\add_layer method. \\select_all_layers will explicitly select all layers for saving, \\deselect_all_layers will explicitly clear the list of layers.\n"
"\n"
"Cells are selected in a similar fashion: by default, all cells are selected. Using \\add_cell, specific\n"
"cells can be selected for saving. All these cells plus their hierarchy will then be written to the stream file.\n"
"\n"
);
}

View File

@ -0,0 +1,262 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2026 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gsiDecl.h"
#include "dbSaveLayoutOptions.h"
namespace gsi
{
static db::SaveLayoutOptions *new_v ()
{
return new db::SaveLayoutOptions ();
}
static std::string set_format_from_filename (db::SaveLayoutOptions *opt, const std::string &fn)
{
auto ff = opt->set_format_from_filename (fn);
if (! ff.first) {
throw tl::Exception (tl::to_string (tr ("Cannot determine format from filename")));
}
return ff.second;
}
Class<db::SaveLayoutOptions> decl_SaveLayoutOptions ("db", "SaveLayoutOptions",
gsi::constructor ("new", &new_v,
"@brief Default constructor\n"
"\n"
"This will initialize the scale factor to 1.0, the database unit is set to\n"
"\"same as original\" and all layers are selected as well as all cells.\n"
"The default format is GDS2."
) +
gsi::method_ext ("set_format_from_filename", &set_format_from_filename, gsi::arg ("filename"),
"@brief Select a format from the given file name\n"
"\n"
"This method will set the format according to the file's extension.\n"
"\n"
"This method has been introduced in version 0.22. "
"Beginning with version 0.23, this method always returns true, since the "
"only consumer for the return value, Layout#write, now ignores that "
"parameter and automatically determines the compression mode from the file name.\n"
"\n"
"Starting with version 0.30.8, this method allows specifying the desired format's extension in square brackets "
"after the file name (e.g. 'file.txt[def]'). This allows writing files with non-standard extensions. "
"The return value of this function now is the actual file name used without the square brackets ('file.txt' in the example case)."
) +
gsi::method ("format=", &db::SaveLayoutOptions::set_format, gsi::arg ("format"),
"@brief Select a format\n"
"The format string can be either \"GDS2\", \"OASIS\", \"CIF\" or \"DXF\". Other formats may be available if\n"
"a suitable plugin is installed."
) +
gsi::method ("format", &db::SaveLayoutOptions::format,
"@brief Gets the format name\n"
"\n"
"See \\format= for a description of that method.\n"
) +
gsi::method ("add_layer", &db::SaveLayoutOptions::add_layer, gsi::arg ("layer_index"), gsi::arg ("properties"),
"@brief Add a layer to be saved \n"
"\n"
"\n"
"Adds the layer with the given index to the layer list that will be written.\n"
"If all layers have been selected previously, all layers will \n"
"be unselected first and only the new layer remains.\n"
"\n"
"The 'properties' argument can be used to assign different layer properties than the ones\n"
"present in the layout. Pass a default \\LayerInfo object to this argument to use the\n"
"properties from the layout object. Construct a valid \\LayerInfo object with explicit layer,\n"
"datatype and possibly a name to override the properties stored in the layout.\n"
) +
gsi::method ("select_all_layers", &db::SaveLayoutOptions::select_all_layers,
"@brief Select all layers to be saved\n"
"\n"
"This method will clear all layers selected with \\add_layer so far and set the 'select all layers' flag.\n"
"This is the default.\n"
) +
gsi::method ("deselect_all_layers", &db::SaveLayoutOptions::deselect_all_layers,
"@brief Unselect all layers: no layer will be saved\n"
"\n"
"This method will clear all layers selected with \\add_layer so far and clear the 'select all layers' flag.\n"
"Using this method is the only way to save a layout without any layers."
) +
gsi::method ("select_cell", &db::SaveLayoutOptions::select_cell, gsi::arg ("cell_index"),
"@brief Selects a cell to be saved (plus hierarchy below)\n"
"\n"
"\n"
"This method is basically a convenience method that combines \\clear_cells and \\add_cell.\n"
"This method clears the 'select all cells' flag.\n"
"\n"
"This method has been added in version 0.22.\n"
) +
gsi::method ("select_this_cell", &db::SaveLayoutOptions::select_this_cell, gsi::arg ("cell_index"),
"@brief Selects a cell to be saved\n"
"\n"
"\n"
"This method is basically a convenience method that combines \\clear_cells and \\add_this_cell.\n"
"This method clears the 'select all cells' flag.\n"
"\n"
"This method has been added in version 0.23.\n"
) +
gsi::method ("clear_cells", &db::SaveLayoutOptions::clear_cells,
"@brief Clears all cells to be saved\n"
"\n"
"This method can be used to ensure that no cell is selected before \\add_cell is called to specify a cell.\n"
"This method clears the 'select all cells' flag.\n"
"\n"
"This method has been added in version 0.22.\n"
) +
gsi::method ("add_this_cell", &db::SaveLayoutOptions::add_this_cell, gsi::arg ("cell_index"),
"@brief Adds a cell to be saved\n"
"\n"
"\n"
"The index of the cell must be a valid index in the context of the layout that will be saved.\n"
"This method clears the 'select all cells' flag.\n"
"Unlike \\add_cell, this method does not implicitly add all children of that cell.\n"
"\n"
"This method has been added in version 0.23.\n"
) +
gsi::method ("add_cell", &db::SaveLayoutOptions::add_cell, gsi::arg ("cell_index"),
"@brief Add a cell (plus hierarchy) to be saved\n"
"\n"
"\n"
"The index of the cell must be a valid index in the context of the layout that will be saved.\n"
"This method clears the 'select all cells' flag.\n"
"\n"
"This method also implicitly adds the children of that cell. A method that does not add the "
"children in \\add_this_cell.\n"
) +
gsi::method ("select_all_cells", &db::SaveLayoutOptions::select_all_cells,
"@brief Select all cells to save\n"
"\n"
"This method will clear all cells specified with \\add_cells so far and set the 'select all cells' flag.\n"
"This is the default.\n"
) +
gsi::method ("write_context_info=", &db::SaveLayoutOptions::set_write_context_info, gsi::arg ("flag"),
"@brief Enables or disables context information\n"
"\n"
"If this flag is set to false, no context information for PCell or library cell instances is written. "
"Those cells will be converted to plain cells and KLayout will not be able to restore the identity of "
"those cells. Use this option to enforce compatibility with other tools that don't understand the "
"context information of KLayout.\n"
"\n"
"The default value is true (context information is stored). Not all formats support context information, hence "
"that flag has no effect for formats like CIF or DXF.\n"
"\n"
"This method was introduced in version 0.23.\n"
) +
gsi::method ("write_context_info?", &db::SaveLayoutOptions::write_context_info,
"@brief Gets a flag indicating whether context information will be stored\n"
"\n"
"See \\write_context_info= for details about this flag.\n"
"\n"
"This method was introduced in version 0.23.\n"
) +
gsi::method ("keep_instances=", &db::SaveLayoutOptions::set_keep_instances, gsi::arg ("flag"),
"@brief Enables or disables instances for dropped cells\n"
"\n"
"If this flag is set to true, instances for cells will be written, even if the cell is dropped. "
"That may happen, if cells are selected with \\select_this_cell or \\add_this_cell or \\no_empty_cells is used. "
"Even if cells called by such cells are not selected, instances will be written for that "
"cell if \"keep_instances\" is true. That feature is supported by the GDS format currently and "
"results in \"ghost cells\" which have instances but no cell definition.\n"
"\n"
"The default value is false (instances of dropped cells are not written).\n"
"\n"
"This method was introduced in version 0.23.\n"
) +
gsi::method ("keep_instances?", &db::SaveLayoutOptions::keep_instances,
"@brief Gets a flag indicating whether instances will be kept even if the target cell is dropped\n"
"\n"
"See \\keep_instances= for details about this flag.\n"
"\n"
"This method was introduced in version 0.23.\n"
) +
gsi::method ("dbu=", &db::SaveLayoutOptions::set_dbu, gsi::arg ("dbu"),
"@brief Sets the database unit to be used in the stream file\n"
"\n"
"By default, the database unit of the layout is used. This method allows one to explicitly use a different\n"
"database unit. A scale factor is introduced automatically which scales all layout objects accordingly so their physical dimensions remain the same. "
"When scaling to a larger database unit or one that is not an integer fraction of the original one, rounding errors may occur and the "
"layout may become slightly distorted."
) +
gsi::method ("dbu", &db::SaveLayoutOptions::dbu,
"@brief Gets the explicit database unit if one is set\n"
"\n"
"See \\dbu= for a description of that attribute.\n"
) +
gsi::method ("no_empty_cells=", &db::SaveLayoutOptions::set_dont_write_empty_cells, gsi::arg ("flag"),
"@brief Don't write empty cells if this flag is set\n"
"\n"
"By default, all cells are written (no_empty_cells is false).\n"
"This applies to empty cells which do not contain shapes for the specified layers "
"as well as cells which are empty because they reference empty cells only.\n"
) +
gsi::method ("no_empty_cells?", &db::SaveLayoutOptions::dont_write_empty_cells,
"@brief Returns a flag indicating whether empty cells are not written.\n"
) +
gsi::method ("libname=|#gds2_libname=", &db::SaveLayoutOptions::set_libname, gsi::arg ("libname"),
"@brief Sets the library name\n"
"\n"
"The library name is an attribute and specifies a formal name for a library, if the layout files is to be used as one.\n"
"Currently, this attribute is only supported by the GDS2 format. Hence the alias.\n"
"\n"
"By default or if the libname is an empty string, the current library name of the layout or 'LIB' is used.\n"
"\n"
"The 'libname' alias has been introduced in version 0.30.5. The original name \\gds2_libname= is still available."
) +
gsi::method ("libname|#gds2_libname", &db::SaveLayoutOptions::libname,
"@brief Gets the library name\n"
"\n"
"See \\libname= for details.\n"
"The 'libname' alias has been introduced in version 0.30.5. The original name \\gds2_libname is still available."
) +
gsi::method ("scale_factor=", &db::SaveLayoutOptions::set_scale_factor, gsi::arg ("scale_factor"),
"@brief Sets the scaling factor for the saving \n"
"\n"
"Using a scaling factor will scale all objects accordingly. "
"This scale factor adds to a potential scaling implied by using an explicit database unit.\n"
"\n"
"Be aware that rounding effects may occur if fractional scaling factors are used.\n"
"\n"
"By default, no scaling is applied."
) +
gsi::method ("scale_factor", &db::SaveLayoutOptions::scale_factor,
"@brief Gets the scaling factor currently set\n"
),
"@brief Options for saving layouts\n"
"\n"
"This class describes the various options for saving a layout to a stream file (GDS2, OASIS and others).\n"
"There are: layers to be saved, cell or cells to be saved, scale factor, format, database unit\n"
"and format specific options.\n"
"\n"
"Usually the default constructor provides a suitable object. Please note, that the format written is \"GDS2\" by default. Either explicitly set a "
"format using \\format= or derive the format from the file name using \\set_format_from_filename.\n"
"\n"
"The layers are specified by either selecting all layers or by defining layer by layer using the\n"
"\\add_layer method. \\select_all_layers will explicitly select all layers for saving, \\deselect_all_layers will explicitly clear the list of layers.\n"
"\n"
"Cells are selected in a similar fashion: by default, all cells are selected. Using \\add_cell, specific\n"
"cells can be selected for saving. All these cells plus their hierarchy will then be written to the stream file.\n"
"\n"
);
}

View File

@ -102,3 +102,17 @@ TEST(1)
EXPECT_EQ (opt.get_option_by_name ("mywriter_value").to_string (), "17");
}
TEST(2)
{
db::SaveLayoutOptions opt;
auto ff = opt.set_format_from_filename ("/home/xyz/test.def");
EXPECT_EQ (ff.first, true);
EXPECT_EQ (ff.second, "/home/xyz/test.def");
EXPECT_EQ (opt.format (), "LEFDEF");
opt = db::SaveLayoutOptions ();
ff = opt.set_format_from_filename ("/home/xyz/test.txt[def]");
EXPECT_EQ (ff.first, true);
EXPECT_EQ (ff.second, "/home/xyz/test.txt");
EXPECT_EQ (opt.format (), "LEFDEF");
}

View File

@ -164,7 +164,7 @@ module DRC
def initialize
cv = RBA::CellView::active
cv = RBA.const_defined?(:CellView) && RBA::CellView::active
@time = Time::now
@force_gc = ($drc_force_gc == true) # for testing, $drc_force_gc can be set to true
@ -1444,7 +1444,7 @@ module DRC
if arg =~ /^@(\d+)/
n = $1.to_i - 1
view = RBA::LayoutView::current
view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current
view || raise("No view open")
(n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}")
cv = view.cellview(n)
@ -1543,7 +1543,7 @@ module DRC
if arg =~ /^@(\d+)/
n = $1.to_i - 1
view = RBA::LayoutView::current
view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current
view || raise("No view open")
(n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}")
cv = view.cellview(n)
@ -1608,7 +1608,7 @@ module DRC
self._context("report") do
# finish what we got so far
view = RBA::LayoutView::current
view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current
@def_output && @def_output.finish(false, view)
@def_output = nil
@ -2925,7 +2925,7 @@ CODE
def _start(job_description)
# clearing the selection avoids some nasty problems
view = RBA::LayoutView::current
view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current
view && view.cancel
@total_timer = RBA::Timer::new
@ -2950,7 +2950,7 @@ CODE
_flush
view = RBA::LayoutView::current
view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current
@def_output && @def_output.finish(final, view)
@ -3484,7 +3484,7 @@ CODE
output_rdb_index = nil
view = RBA::LayoutView::current
view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current
if view
if self._rdb_index
output_rdb = RBA::ReportDatabase::new("") # reuse existing name
@ -3518,7 +3518,7 @@ CODE
if arg =~ /^@(\d+|\+)/
view = RBA::LayoutView::current
view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current
view || raise("No view open")
if $1 == "+"
prev_cv = view.active_cellview_index

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>561</width>
<height>323</height>
<width>586</width>
<height>431</height>
</rect>
</property>
<property name="windowTitle">
@ -20,32 +20,6 @@
<property name="margin" stdset="0">
<number>9</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>The selection will be copied row * columns times. Each copy will be displaced by a vector given by the row vector times the row index and the column vector times the column index.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="array_grp">
<property name="sizePolicy">
@ -61,144 +35,248 @@
<bool>false</bool>
</property>
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="3" column="3">
<widget class="QLineEdit" name="row_x_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<item row="3" column="0" colspan="5">
<widget class="QTabWidget" name="mode_tab">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Array with pitch vectors</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="5">
<widget class="QLabel" name="label_3">
<property name="text">
<string> Each copy will be displaced by a vector given by the row vector times the row index and the column vector times the column index.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label_15">
<property name="text">
<string>y =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="column_x_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string> Column vector (x,y)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_14">
<property name="text">
<string>x =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLineEdit" name="column_y_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="row_x_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_11">
<property name="text">
<string>x =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="label_10">
<property name="text">
<string>y =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QLineEdit" name="row_y_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string> Row vector (x,y)</string>
</property>
</widget>
</item>
<item row="1" column="5">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Array with space</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Space</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="space_x_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLineEdit" name="space_y_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="5">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_18">
<property name="text">
<string>horizonal =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label_13">
<property name="text">
<string>vertical =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="0" colspan="6">
<widget class="QCheckBox" name="vis_only_cbx">
<property name="text">
<string>Use visible layers only for bounding boxes of instances</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="6">
<widget class="QLabel" name="label_4">
<property name="text">
<string>The selection is placed in a rectangular array, with the bounding box defining the pitch. Additional space can be added between columns and rows.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_11">
<item row="0" column="0" colspan="5">
<widget class="QLabel" name="label_2">
<property name="text">
<string>x =</string>
<string>The selection will be copied row * columns times.</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QLineEdit" name="column_y_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Columns =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Rows =</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="columns_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="label_9">
<property name="text">
<string> Row vector (x,y)</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="label_16">
<property name="text">
<string> Column vector (x,y)</string>
</property>
</widget>
</item>
<item row="3" column="5">
<widget class="QLineEdit" name="row_y_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_14">
<property name="text">
<string>x =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLineEdit" name="column_x_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QLineEdit" name="rows_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QLabel" name="label_15">
<property name="text">
<string>y =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QLabel" name="label_10">
<property name="text">
<string>y =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0" colspan="6">
<item row="2" column="0" colspan="5">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -214,6 +292,59 @@
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Columns =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="columns_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Rows =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLineEdit" name="rows_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="4">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
@ -271,10 +402,14 @@
<tabstops>
<tabstop>columns_le</tabstop>
<tabstop>rows_le</tabstop>
<tabstop>mode_tab</tabstop>
<tabstop>column_x_le</tabstop>
<tabstop>column_y_le</tabstop>
<tabstop>row_x_le</tabstop>
<tabstop>row_y_le</tabstop>
<tabstop>space_x_le</tabstop>
<tabstop>space_y_le</tabstop>
<tabstop>vis_only_cbx</tabstop>
</tabstops>
<resources/>
<connections>

View File

@ -26,6 +26,7 @@
#include "dbLayout.h"
#include "edtDialogs.h"
#include "edtMainService.h"
#include "layEditorUtils.h"
#include "layObjectInstPath.h"
#include "layCellView.h"
@ -646,29 +647,41 @@ MakeArrayOptionsDialog::MakeArrayOptionsDialog (QWidget *parent)
}
bool
MakeArrayOptionsDialog::exec_dialog (db::DVector &a, unsigned int &na, db::DVector &b, unsigned int &nb)
MakeArrayOptionsDialog::exec_dialog (ArrayOptions &options)
{
rows_le->setText (tl::to_qstring (tl::to_string (na)));
columns_le->setText (tl::to_qstring (tl::to_string (nb)));
row_x_le->setText (tl::to_qstring (tl::micron_to_string (a.x ())));
row_y_le->setText (tl::to_qstring (tl::micron_to_string (a.y ())));
column_x_le->setText (tl::to_qstring (tl::micron_to_string (b.x ())));
column_y_le->setText (tl::to_qstring (tl::micron_to_string (b.y ())));
rows_le->setText (tl::to_qstring (tl::to_string (options.na)));
columns_le->setText (tl::to_qstring (tl::to_string (options.nb)));
row_x_le->setText (tl::to_qstring (tl::micron_to_string (options.a.x ())));
row_y_le->setText (tl::to_qstring (tl::micron_to_string (options.a.y ())));
column_x_le->setText (tl::to_qstring (tl::micron_to_string (options.b.x ())));
column_y_le->setText (tl::to_qstring (tl::micron_to_string (options.b.y ())));
space_x_le->setText (tl::to_qstring (tl::micron_to_string (options.space.x ())));
space_y_le->setText (tl::to_qstring (tl::micron_to_string (options.space.y ())));
mode_tab->setCurrentIndex (options.mode == ArrayOptions::PitchVectors ? 0 : 1);
vis_only_cbx->setChecked (options.use_visible_layers);
if (QDialog::exec ()) {
options.mode = mode_tab->currentIndex () == 0 ? ArrayOptions::PitchVectors : ArrayOptions::Spaced;
options.use_visible_layers = vis_only_cbx->isChecked ();
double bx = 0.0, by = 0.0;
double ax = 0.0, ay = 0.0;
double sx = 0.0, sy = 0.0;
tl::from_string_ext (tl::to_string (column_x_le->text ()), bx);
tl::from_string_ext (tl::to_string (column_y_le->text ()), by);
tl::from_string_ext (tl::to_string (columns_le->text ()), nb);
tl::from_string_ext (tl::to_string (columns_le->text ()), options.nb);
tl::from_string_ext (tl::to_string (row_x_le->text ()), ax);
tl::from_string_ext (tl::to_string (row_y_le->text ()), ay);
tl::from_string_ext (tl::to_string (rows_le->text ()), na);
tl::from_string_ext (tl::to_string (rows_le->text ()), options.na);
tl::from_string_ext (tl::to_string (space_x_le->text ()), sx);
tl::from_string_ext (tl::to_string (space_y_le->text ()), sy);
a = db::DVector (ax, ay);
b = db::DVector (bx, by);
options.a = db::DVector (ax, ay);
options.b = db::DVector (bx, by);
options.space = db::DVector (sx, sy);
return true;
@ -758,7 +771,7 @@ popup_tap_layer_menu (lay::LayoutViewBase *view, const std::set<db::LayerPropert
std::set<std::pair<unsigned int, unsigned int> > layers_in_selection;
for (lay::ShapeFinder::iterator f = finder.begin (); f != finder.end (); ++f) {
if (cv_index < 0 || f->cv_index () == cv_index) {
if (cv_index < 0 || int (f->cv_index ()) == cv_index) {
const db::Layout &ly = view->cellview (f->cv_index ())->layout ();
// ignore guiding shapes and only provide layers from the filter
if (f->layer () != ly.guiding_shape_layer ()

View File

@ -56,6 +56,8 @@ namespace lay
namespace edt {
class ArrayOptions;
/**
* @brief The copy mode dialog
*/
@ -173,10 +175,10 @@ class MakeArrayOptionsDialog
private Ui::MakeArrayOptionsDialog
{
Q_OBJECT
public:
MakeArrayOptionsDialog (QWidget *parent);
bool exec_dialog (db::DVector &a, unsigned int &na, db::DVector &b, unsigned int &nb);
bool exec_dialog (ArrayOptions &options);
virtual void accept ();
};

View File

@ -81,8 +81,6 @@ MainService::MainService (db::Manager *manager, lay::LayoutViewBase *view, lay::
m_distribute_vmode (1), m_distribute_vpitch (0.0), m_distribute_vspace (0.0),
m_distribute_visible_layers (false),
m_origin_mode_x (-1), m_origin_mode_y (-1), m_origin_visible_layers_for_bbox (false),
m_array_a (0.0, 1.0), m_array_b (1.0, 0.0),
m_array_na (1), m_array_nb (1),
m_router (0.0), m_rinner (0.0), m_npoints (64), m_undo_before_apply (true)
{
#if defined(HAVE_QT)
@ -2251,32 +2249,66 @@ MainService::cm_make_array ()
tl_assert (false); // see TODO
#endif
size_t n = 0;
check_no_guiding_shapes ();
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
++n;
}
bool any = false;
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end () && ! any; ++es) {
any = ! (*es)->begin_selection ().at_end ();
}
if (n == 0) {
if (! any) {
throw tl::Exception (tl::to_string (tr ("Nothing selected to make arrays of")));
}
#if defined(HAVE_QT)
// TODO: make parameters persistent so we can set them externally
if (! make_array_options_dialog ()->exec_dialog (m_array_a, m_array_na, m_array_b, m_array_nb)) {
if (! make_array_options_dialog ()->exec_dialog (m_array_options)) {
return;
}
#endif
unsigned int na = m_array_options.na;
unsigned int nb = m_array_options.nb;
db::DVector a = m_array_options.a;
db::DVector b = m_array_options.b;
if (m_array_options.mode == ArrayOptions::Spaced) {
db::DBox bbox;
// get the selection bbox
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
const db::Layout &layout = view ()->cellview (s->cv_index ())->layout ();
db::CplxTrans tr = db::CplxTrans (layout.dbu ()) * s->trans ();
if (! s->is_cell_inst ()) {
bbox += tr * s->shape ().bbox ();
} else {
bbox += inst_bbox (tr, view (), s->cv_index (), s->back (), m_array_options.use_visible_layers);
}
}
}
// compute pitch vectors
a = db::DVector (0.0, m_array_options.space.y () + bbox.height ()); // row-pitch
b = db::DVector (m_array_options.space.x () + bbox.width (), 0.0); // column-pitch
}
make_array (na, nb, a, b);
}
void
MainService::make_array (unsigned int na, unsigned int nb, const db::DVector &a, const db::DVector &b)
{
view ()->cancel_edits ();
// undo support for small arrays only
bool has_undo = (m_array_na * m_array_nb < 1000);
bool has_undo = (na * nb < 1000);
// No undo support currently - the undo buffering is pretty inefficient right now.
if (manager ()) {
@ -2287,7 +2319,17 @@ MainService::cm_make_array ()
}
}
tl::RelativeProgress progress (tl::to_string (tr ("Make array")), (size_t (m_array_na) * size_t (m_array_nb) - 1) * n, 1000);
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
// count selected items for progress
size_t n = 0;
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
++n;
}
}
tl::RelativeProgress progress (tl::to_string (tr ("Make array")), (size_t (na) * size_t (nb) - 1) * n, 1000);
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
@ -2302,15 +2344,15 @@ MainService::cm_make_array ()
if (s->is_cell_inst ()) {
for (unsigned int ia = 0; ia < m_array_na; ++ia) {
for (unsigned int ib = 0; ib < m_array_nb; ++ib) {
for (unsigned int ia = 0; ia < na; ++ia) {
for (unsigned int ib = 0; ib < nb; ++ib) {
// don't create a copy
if (ia == 0 && ib == 0) {
continue;
}
db::DCplxTrans dtrans (m_array_a * double (ia) + m_array_b * double (ib));
db::DCplxTrans dtrans (a * double (ia) + b * double (ib));
db::ICplxTrans itrans (db::DCplxTrans (s->trans ()).inverted () * db::DCplxTrans (1.0 / cv->layout ().dbu ()) * dtrans * db::DCplxTrans (cv->layout ().dbu ()) * db::DCplxTrans (s->trans ()));
db::Instance new_inst = target_cell.insert (s->back ().inst_ptr);
@ -2326,15 +2368,15 @@ MainService::cm_make_array ()
db::Shapes &target_shapes = target_cell.shapes (s->layer ());
for (unsigned int ia = 0; ia < m_array_na; ++ia) {
for (unsigned int ib = 0; ib < m_array_nb; ++ib) {
for (unsigned int ia = 0; ia < na; ++ia) {
for (unsigned int ib = 0; ib < nb; ++ib) {
// don't create a copy
if (ia == 0 && ib == 0) {
continue;
}
db::DCplxTrans dtrans (m_array_a * double (ia) + m_array_b * double (ib));
db::DCplxTrans dtrans (a * double (ia) + b * double (ib));
db::ICplxTrans itrans (db::DCplxTrans (s->trans ()).inverted () * db::DCplxTrans (1.0 / cv->layout ().dbu ()) * dtrans * db::DCplxTrans (cv->layout ().dbu ()) * db::DCplxTrans (s->trans ()));
db::Shape new_shape = target_shapes.insert (s->shape ());

View File

@ -56,6 +56,21 @@ class DistributeOptionsDialog;
// -------------------------------------------------------------
struct ArrayOptions
{
enum mode_type { PitchVectors, Spaced };
ArrayOptions () : mode (PitchVectors), a (0.0, 1.0), b (1.0, 0.0), na (1), nb (1) { }
mode_type mode;
db::DVector a, b;
unsigned int na, nb;
db::DVector space;
bool use_visible_layers;
};
// -------------------------------------------------------------
class MainService
: public lay::Plugin,
public lay::Editable,
@ -155,6 +170,11 @@ public:
*/
void cm_make_array ();
/**
* @brief The parameterized function for making arrays
*/
void make_array (unsigned na, unsigned nb, const db::DVector &a, const db::DVector &b);
/**
* @brief Align the selected shapes and instances
*/
@ -242,11 +262,11 @@ private:
std::string m_make_cell_name;
int m_origin_mode_x, m_origin_mode_y;
bool m_origin_visible_layers_for_bbox;
db::DVector m_array_a, m_array_b;
unsigned int m_array_na, m_array_nb;
double m_router, m_rinner;
unsigned int m_npoints;
bool m_undo_before_apply;
ArrayOptions m_array_options;
#if defined(HAVE_QT)
edt::RoundCornerOptionsDialog *mp_round_corners_dialog;
edt::AreaAndPerimeterDialog *mp_area_and_perimeter_dialog;

View File

@ -1060,47 +1060,72 @@ ApplicationBase::usage ()
{
std::string r;
r = std::string (lay::Version::exe_name ()) + " [<options>] [<file>] ..\n";
r += tl::to_string (QObject::tr ("options")) + "\n";
r += tl::to_string (QObject::tr (" -b Batch mode (same as -zz -nc -rx)")) + "\n";
r += tl::to_string (QObject::tr (" -c <config file> Use this configuration file")) + "\n";
r += tl::to_string (QObject::tr (" -nc Don't use a configuration file (implies -t)")) + "\n";
r += tl::to_string (QObject::tr (" -d <log level> Set log level")) + "\n";
r += tl::to_string (QObject::tr (" -e Editable mode (allow editing of files)")) + "\n";
r += tl::to_string (QObject::tr (" -ne Readonly mode (editing of files is disabled)")) + "\n";
r += tl::to_string (QObject::tr (" -gr <file name> Record GUI test file")) + "\n";
r += tl::to_string (QObject::tr (" -gp <file name> Replay GUI test file")) + "\n";
r += tl::to_string (QObject::tr (" -gb <line number> Replay GUI test file up to (including) line")) + "\n";
r += tl::to_string (QObject::tr (" -gx <millisec> Replay rate for GUI test file")) + "\n";
r += tl::to_string (QObject::tr (" -gi Incremental logs for GUI test file")) + "\n";
r += tl::to_string (QObject::tr (" -i Disable undo buffering (less memory requirements)")) + "\n";
r += tl::to_string (QObject::tr (" -ni Enable undo buffering (default, overrides previous -i option)")) + "\n";
r += tl::to_string (QObject::tr (" -j <path> Add the given path to the macro project paths")) + "\n";
r += tl::to_string (QObject::tr (" -k <log file> Write log to the given file plus stdout/stderr")) + "\n";
r += tl::to_string (QObject::tr (" -l <lyp file> Use layer properties file")) + "\n";
r += tl::to_string (QObject::tr (" -lx With -l: add other layers as well")) + "\n";
r += tl::to_string (QObject::tr (" -lf With -l: use the lyp file as it is (no expansion to multiple layouts)")) + "\n";
r += tl::to_string (QObject::tr (" -m <database file> Load RDB (report database) file (into previous layout view)")) + "\n";
r += tl::to_string (QObject::tr (" -mn <database file> Load L2NDB (layout to netlist database) file (into previous layout view)")) + "\n";
r += tl::to_string (QObject::tr (" -n <technology> Technology to use for next layout(s) on command line")) + "\n";
r += tl::to_string (QObject::tr (" -nn <tech file> Technology file (.lyt) to use for next layout(s) on command line")) + "\n";
r += tl::to_string (QObject::tr (" -p <plugin> Load the plugin (can be used multiple times)")) + "\n";
r += tl::to_string (QObject::tr (" -r <script> Execute main script on startup (after having loaded files etc.)")) + "\n";
r += tl::to_string (QObject::tr (" -rr <script> Like -r, but does not exit after executing the script")) + "\n";
r += tl::to_string (QObject::tr (" -rm <script> Execute script on startup before loading files (can be used multiple times)")) + "\n";
r += tl::to_string (QObject::tr (" -rd <name>=<value> Specify script variable")) + "\n";
r += tl::to_string (QObject::tr (" -rx Ignore all implicit macros (*.rbm, rbainit, *.lym)")) + "\n";
r += tl::to_string (QObject::tr (" -s Load files into same view")) + "\n";
r += tl::to_string (QObject::tr (" -t Don't update the configuration file on exit")) + "\n";
r += tl::to_string (QObject::tr (" -nt Update the configuration file on exit (default, overrides previous -t option)")) + "\n";
r += tl::to_string (QObject::tr (" -u <file name> Restore session from given file")) + "\n";
r += tl::to_string (QObject::tr (" -v Print program version and exit (also available as --version)")) + "\n";
r += tl::to_string (QObject::tr (" -wd <name>=<value> Define a variable within expressions")) + "\n";
r += tl::to_string (QObject::tr (" -x Synchronous drawing mode")) + "\n";
r += tl::to_string (QObject::tr (" -y <package> Package installation: install package(s) and exit - can be used more than once")) + "\n";
r += tl::to_string (QObject::tr (" ('package' is a name, an URL and optionally a version in round brackets)")) + "\n";
r += tl::to_string (QObject::tr (" -yd With -y: include dependencies")) + "\n";
r += tl::to_string (QObject::tr (" -z Non-GUI mode (hidden views)")) + "\n";
r += tl::to_string (QObject::tr (" -zz Non-GUI mode (database only, implies -nc)")) + "\n";
r += tl::to_string (tr (
"\n"
"The format of the input files is usually detected automatically. In some cases, the format is derived from\n"
"the file's suffix. If your file does not have the neccessary suffix, you can explicitly specify the format\n"
"by adding the respective suffix to the file name in square brackets - e.g. 'file.data[def]' will read\n"
"the file 'file.data' as a DEF file.\n"
"\n"
"Options:\n"
" -b Batch mode (same as -zz -nc -rx)\n"
" -c <config file> Use this configuration file\n"
" -nc Don't use a configuration file (implies -t)\n"
" -d <log level> Set log level\n"
" -e Editable mode (allow editing of files)\n"
" -ne Readonly mode (editing of files is disabled)\n"
" -gr <file name> Record GUI test file\n"
" -gp <file name> Replay GUI test file\n"
" -gb <line number> Replay GUI test file up to (including) line\n"
" -gx <millisec> Replay rate for GUI test file\n"
" -gi Incremental logs for GUI test file\n"
" -i Disable undo buffering (less memory requirements)\n"
" -ni Enable undo buffering (default, overrides previous -i option)\n"
" -j <path> Add the given path to the macro project paths\n"
" -k <log file> Write log to the given file plus stdout/stderr\n"
" -l <lyp file> Use layer properties file\n"
" -lx With -l: add other layers as well\n"
" -lf With -l: use the lyp file as it is (no expansion to multiple layouts)\n"
" -m <database file> Load RDB (report database) file (into previous layout view)\n"
" -mn <database file> Load L2NDB (layout to netlist database) file (into previous layout view)\n"
" -n <technology> Technology to use for next layout(s) on command line\n"
" -nn <tech file> Technology file (.lyt) to use for next layout(s) on command line\n"
" -p <plugin> Load the plugin (can be used multiple times)\n"
" -r <script> Execute main script on startup (after having loaded files etc.)\n"
" -rr <script> Like -r, but does not exit after executing the script\n"
" -rm <script> Execute script on startup before loading files (can be used multiple times)\n"
" -rd <name>=<value> Specify script variable\n"
" -rx Ignore all implicit macros (*.rbm, rbainit, *.lym)\n"
" -s Load files into same view\n"
" -t Don't update the configuration file on exit\n"
" -nt Update the configuration file on exit (default, overrides previous -t option)\n"
" -u <file name> Restore session from given file\n"
" -v Print program version and exit (also available as --version)\n"
" -wd <name>=<value> Define a variable within expressions\n"
" -x Synchronous drawing mode\n"
" -y <package> Package installation: install package(s) and exit - can be used more than once\n"
" ('package' is a name, an URL and optionally a version in round brackets)\n"
" -yd With -y: include dependencies\n"
" -z Non-GUI mode (hidden views)\n"
" -zz Non-GUI mode (database only, implies -nc)\n"
"\n"
"Environment variables:\n"
" KLAYOUT_HOME The path to KLayout's data (configuration, macros, libraries etc.).\n"
" Default path is '~/.klayout' on Linux and '%USERPROFILE%\\KLayout' on Windows.\n"
" KLAYOUT_PATH A list of locations to look into additionally to KLAYOUT_HOME for macros, libraries etc.\n"
" Separator for the locations is ':' on Linux and ';' on Windows.\n"
" KLAYOUT_SALT_MINE The package manager repository URL. Default is 'http://sami.klayout.org/repository.xml'.\n"
" KLAYOUT_IGNORE_MACRO_DIRS A colon-separated list of folder names to ignore in the macro editor tree.\n"
" By default, only '__pycache__' is ignored.\n"
" KLAYOUT_LEF_FORMAT A space-separated list of formats to be recognized as LEF files.\n"
" By default this format list is '*.lef *.LEF *.lef.gz *.LEF.gz'.\n"
" KLAYOUT_DEF_FORMAT A space-separated list of formats to be recognized as DEF files.\n"
" By default this format list is '*.def *.DEF *.def.gz *.DEF.gz'.\n"
" KLAYOUT_PYTHONPATH The equivalent of PYTHONPATH for KLayout.\n"
" KLAYOUT_PYTHONHOME The equivalent of PYTHONHOME for KLayout.\n"
" KLAYOUT_HTTP_TIMEOUT The timeout for HTTP(S) requests in seconds. The default value is 10.\n"
" KLAYOUT_VERBOSITY The default verbosity (see -d option).\n"
));
return r;
}

View File

@ -124,10 +124,14 @@ MacroEditorNotificationWidget::MacroEditorNotificationWidget (MacroEditorPage *p
void
MacroEditorNotificationWidget::action_triggered ()
{
BEGIN_PROTECTED
auto a = m_action_buttons.find (sender ());
if (a != m_action_buttons.end ()) {
mp_parent->notification_action (*mp_notification, a->second);
}
END_PROTECTED_W (this)
}
void
@ -544,7 +548,9 @@ void MacroEditorSidePanel::paintEvent (QPaintEvent *)
// MacroEditorPage implementation
MacroEditorPage::MacroEditorPage (QWidget * /*parent*/, MacroEditorHighlighters *highlighters)
: mp_macro (0), mp_highlighters (highlighters), mp_highlighter (0), m_error_line (-1), m_ntab (8), m_nindent (2), m_ignore_cursor_changed_event (false)
: mp_macro (0), mp_highlighters (highlighters), mp_highlighter (0),
m_error_line (-1), m_ntab (8), m_nindent (2), m_ignore_cursor_changed_event (false),
dm_run_mode_changed (this, &MacroEditorPage::do_run_mode_changed)
{
mp_layout = new QVBoxLayout (this);
mp_layout->setContentsMargins (0, 0, 0, 0);
@ -658,6 +664,11 @@ void MacroEditorPage::current_line_changed ()
}
void MacroEditorPage::run_mode_changed ()
{
dm_run_mode_changed ();
}
void MacroEditorPage::do_run_mode_changed ()
{
// this prevents recursion when the following lines trigger anything that routes through the interpreter
bool bl = mp_exec_model->blockSignals (true);

View File

@ -28,6 +28,7 @@
#include "lymMacro.h"
#include "layGenericSyntaxHighlighter.h"
#include "tlVariant.h"
#include "tlDeferredExecution.h"
#include <QDialog>
#include <QPixmap>
@ -417,12 +418,14 @@ private:
QListWidget *mp_completer_list;
std::list<MacroEditorNotification> m_notifications;
std::map<const MacroEditorNotification *, QWidget *, CompareNotificationPointers> m_notification_widgets;
tl::DeferredMethod<lay::MacroEditorPage> dm_run_mode_changed;
void update_extra_selections ();
bool return_pressed ();
bool backspace_pressed ();
bool back_tab_key_pressed ();
bool tab_key_pressed ();
void do_run_mode_changed ();
void fill_completer_list ();
void complete ();
QTextCursor get_completer_cursor (int &pos0, int &pos);

View File

@ -2270,7 +2270,7 @@ MainWindow::cm_save_current_cell_as ()
db::SaveLayoutOptions options (cv->save_options ());
options.set_dbu (cv->layout ().dbu ());
options.set_format_from_filename (fn);
fn = options.set_format_from_filename (fn).second;
tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto;
if (mp_layout_save_as_options->get_options (current_view (), cv_index, fn, om, options)) {
@ -2370,7 +2370,7 @@ MainWindow::do_save (bool as)
db::SaveLayoutOptions options = get_save_options_from_cv (cv);
if (as || options.format ().empty ()) {
options.set_format_from_filename (fn);
fn = options.set_format_from_filename (fn).second;
}
tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto;
@ -2419,7 +2419,7 @@ MainWindow::cm_save_all ()
db::SaveLayoutOptions options = get_save_options_from_cv (cv);
if (options.format ().empty ()) {
options.set_format_from_filename (fn);
fn = options.set_format_from_filename (fn).second;
}
tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto;

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>432</width>
<height>349</height>
<width>449</width>
<height>370</height>
</rect>
</property>
<property name="windowTitle">
@ -405,6 +405,13 @@
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
@ -429,44 +436,12 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>209</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Ok</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>pushButton</tabstop>
<tabstop>pushButton_2</tabstop>
<tabstop>lt</tabstop>
<tabstop>ct</tabstop>
<tabstop>rt</tabstop>
@ -486,34 +461,34 @@
</resources>
<connections>
<connection>
<sender>pushButton</sender>
<signal>clicked()</signal>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AlignCellOptionsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>237</x>
<y>203</y>
<x>224</x>
<y>295</y>
</hint>
<hint type="destinationlabel">
<x>147</x>
<y>81</y>
<x>224</x>
<y>176</y>
</hint>
</hints>
</connection>
<connection>
<sender>pushButton_2</sender>
<signal>clicked()</signal>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AlignCellOptionsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>325</x>
<y>202</y>
<x>224</x>
<y>295</y>
</hint>
<hint type="destinationlabel">
<x>325</x>
<y>57</y>
<x>224</x>
<y>176</y>
</hint>
</hints>
</connection>

View File

@ -136,6 +136,83 @@ static lay::NetlistObjectPath second (const lay::NetlistObjectsPath *pp)
return pp->second ();
}
namespace {
struct First
{
lay::NetlistObjectsPath operator() (const lay::NetlistObjectPath &p)
{
return lay::NetlistObjectsPath::from_first (p);
}
};
struct Second
{
lay::NetlistObjectsPath operator() (const lay::NetlistObjectPath &p)
{
return lay::NetlistObjectsPath::from_second (p);
}
};
}
template <class Which>
static lay::NetlistObjectsPath from (const lay::NetlistObjectPath &p)
{
return Which () (p);
}
template <class Which>
static lay::NetlistObjectsPath from_net (const db::Net *net)
{
if (! net) {
return lay::NetlistObjectsPath ();
}
lay::NetlistObjectPath p;
p.root = net->circuit ();
p.net = net;
return Which () (p);
}
template <class Which>
static lay::NetlistObjectsPath from_device (const db::Device *device)
{
if (! device) {
return lay::NetlistObjectsPath ();
}
lay::NetlistObjectPath p;
p.root = device->circuit ();
p.device = device;
return Which () (p);
}
template <class Which>
static lay::NetlistObjectsPath from_circuit (const db::Circuit *circuit)
{
if (! circuit) {
return lay::NetlistObjectsPath ();
}
lay::NetlistObjectPath p;
p.root = circuit;
return Which () (p);
}
template <class Which>
static lay::NetlistObjectsPath from_subcircuit (const db::SubCircuit *subcircuit)
{
if (! subcircuit) {
return lay::NetlistObjectsPath ();
}
lay::NetlistObjectPath p;
p.root = subcircuit->circuit ();
p.path.push_back (subcircuit);
return Which () (p);
}
Class<lay::NetlistObjectsPath> decl_NetlistObjectsPath ("lay", "NetlistObjectsPath",
gsi::method_ext ("first", &first,
"@brief Gets the first object's path.\n"
@ -146,6 +223,67 @@ Class<lay::NetlistObjectsPath> decl_NetlistObjectsPath ("lay", "NetlistObjectsPa
"@brief Gets the second object's path.\n"
"In cases of paired netlists (LVS database), the first path points to the schematic netlist object.\n"
"For the single netlist, the second path is always a null path."
) +
gsi::method ("from_first", &from<First>, gsi::arg ("p"),
"@brief Creates a path from given first coordinates.\n"
"Use this constructor to create a paired path from a first set of coordinates (net, circuit, device).\n"
"For single-sided databases (i.e. extracted netlist or schematic only), use the first coordinates. "
"For two-sided database (i.e. LVS cross references), use the first coordinates when you refer to "
"layout netlist objects.\n"
"\n"
"In this version, the minimum requirement for the path is to have a root.\n"
"\n"
"This constructor has been added in version 0.30.8."
) +
gsi::method ("from_first", &from_net<First>, gsi::arg ("net"),
"@brief Creates a path from a given net in the first netlist space.\n"
"\n"
"This constructor has been added in version 0.30.8."
) +
gsi::method ("from_first", &from_device<First>, gsi::arg ("device"),
"@brief Creates a path from a given device in the first netlist space.\n"
"\n"
"This constructor has been added in version 0.30.8."
) +
gsi::method ("from_first", &from_circuit<First>, gsi::arg ("circuit"),
"@brief Creates a path from a given circuit in the first netlist space.\n"
"\n"
"This constructor has been added in version 0.30.8."
) +
gsi::method ("from_first", &from_subcircuit<First>, gsi::arg ("subcircuit"),
"@brief Creates a path from a given subcircuit in the first netlist space.\n"
"\n"
"This constructor has been added in version 0.30.8."
) +
gsi::method ("from_second", &from<Second>, gsi::arg ("p"),
"@brief Creates a path from given second coordinates.\n"
"Use this constructor to create a paired path from a second set of coordinates (net, circuit, device).\n"
"For two-sided database (i.e. LVS cross references), use the second coordinates when you refer to "
"schematic netlist objects.\n"
"\n"
"In this version, the minimum requirement for the path is to have a root.\n"
"\n"
"This constructor has been added in version 0.30.8."
) +
gsi::method ("from_second", &from_net<Second>, gsi::arg ("net"),
"@brief Creates a path from a given net in the second netlist space.\n"
"\n"
"This constructor has been added in version 0.30.8."
) +
gsi::method ("from_second", &from_device<Second>, gsi::arg ("device"),
"@brief Creates a path from a given device in the second netlist space.\n"
"\n"
"This constructor has been added in version 0.30.8."
) +
gsi::method ("from_second", &from_circuit<Second>, gsi::arg ("circuit"),
"@brief Creates a path from a given circuit in the second netlist space.\n"
"\n"
"This constructor has been added in version 0.30.8."
) +
gsi::method ("from_second", &from_subcircuit<Second>, gsi::arg ("subcircuit"),
"@brief Creates a path from a given subcircuit in the second netlist space.\n"
"\n"
"This constructor has been added in version 0.30.8."
),
"@brief An object describing the instantiation of a single netlist object or a pair of those.\n"
"This class is basically a pair of netlist object paths (see \\NetlistObjectPath). When derived from a single netlist view, "
@ -156,7 +294,16 @@ Class<lay::NetlistObjectsPath> decl_NetlistObjectsPath ("lay", "NetlistObjectsPa
"If the selected object isn't a matched one, either the first or second path may be a null or a partial path without a final net or device object "
"or a partial path.\n"
"\n"
"This class has been introduced in version 0.27.\n"
"To create a paired path from an existing object (net, device, circuit, subcircuit), use \\from_first and \\from_second.\n"
"Use \\from_first, if the object belongs to the first netlist of the database ('netlist', layout netlist in the LVS context).\n"
"Use \\from_second, if the object belongs to the second netlist of the database ('reference', schematic netlist in the LVS context).\n"
"\n"
"It is also possible to create a paired path from a full path object (including root, a subcircuit path and a target object), either "
"for the first or second side. Use \\from_first and \\from_second with a \\NetlistObjectPath argument in that case.\n"
"A minimum requirement in that case is to set the root (the origin of the path). If no subcircuit path is "
"given (see \\NetlistObjectPath#path), a suitable path leading from the target object (net or device) to the root will be constructed.\n"
"\n"
"This class has been introduced in version 0.27 and has been extended in version 0.30.8.\n"
);
static lay::NetlistObjectPath current_path_first (lay::NetlistBrowserDialog *dialog)
@ -169,6 +316,20 @@ static lay::NetlistObjectPath current_path_second (lay::NetlistBrowserDialog *di
return dialog->current_path ().second ();
}
static void set_current_path (lay::NetlistBrowserDialog *dialog, const lay::NetlistObjectsPath *path)
{
if (! path) {
dialog->set_current_path (lay::NetlistObjectsPath ());
} else {
dialog->set_current_path (*path);
}
}
static lay::LayoutViewBase *get_view (lay::NetlistBrowserDialog *dialog)
{
return dialog->view ();
}
Class<lay::NetlistBrowserDialog> decl_NetlistBrowserDialog ("lay", "NetlistBrowserDialog",
gsi::event ("on_current_db_changed", &lay::NetlistBrowserDialog::current_db_changed_event,
"@brief This event is triggered when the current database is changed.\n"
@ -187,6 +348,16 @@ Class<lay::NetlistBrowserDialog> decl_NetlistBrowserDialog ("lay", "NetlistBrows
gsi::method ("db", &lay::NetlistBrowserDialog::db,
"@brief Gets the database the browser is connected to.\n"
) +
gsi::method ("db_index", &lay::NetlistBrowserDialog::l2n_index,
"@brief Gets the database index inside the view the browser is connected to.\n"
"\n"
"This attribute has been introduced in version 0.30.8.\n"
) +
gsi::method_ext ("view", &get_view,
"@brief Gets the view the browser is connected to.\n"
"\n"
"This attribute has been introduced in version 0.30.8.\n"
) +
gsi::method_ext ("current_path_first", &current_path_first,
"@brief Gets the path of the current object on the first (layout in case of LVS database) side.\n"
) +
@ -196,6 +367,16 @@ Class<lay::NetlistBrowserDialog> decl_NetlistBrowserDialog ("lay", "NetlistBrows
gsi::method ("current_path", &lay::NetlistBrowserDialog::current_path,
"@brief Gets the path of the current object as a path pair (combines layout and schematic object paths in case of a LVS database view).\n"
) +
gsi::method_ext ("current_path=", &set_current_path,
"@brief Sets the current path.\n"
"This is the setter corresponding to the 'current_path' getter. Passing nil clears the selection.\n"
"Changing the selection will update the highlights and navigate to the location depending on the "
"settings. An \\on_selection_changed signal is not emitted.\n"
"\n"
"To get a suitable path, see the \\NetlistObjectsPath#from_first and \\NetlistObjectsPath#from_second generators.\n"
"\n"
"This setter has been introduced in version 0.30.8.\n"
) +
gsi::method ("selected_paths", &lay::NetlistBrowserDialog::selected_paths,
"@brief Gets the nets currently selected objects (paths) in the netlist database browser.\n"
"The result is an array of path pairs. See \\NetlistObjectsPath for details about these pairs."

View File

@ -306,28 +306,28 @@ SingleIndexedNetlistModel::parent_of (const subcircuit_pair &subcircuits) const
return std::make_pair (subcircuits.first ? subcircuits.first->circuit () : 0, (const db::Circuit *) 0);
}
std::pair<IndexedNetlistModel::circuit_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::top_circuit_from_index(size_t index) const
std::pair<IndexedNetlistModel::circuit_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::top_circuit_from_index (size_t index) const
{
db::Netlist::const_top_down_circuit_iterator none;
return std::make_pair (attr_by_object_and_index (std::make_pair ((const db::Circuit *) 0, (const db::Circuit *) 0), index, mp_netlist->begin_top_down (), mp_netlist->begin_top_down () + mp_netlist->top_circuit_count (), none, none, m_child_circuit_by_circuit_and_index, sort_by_name<db::Circuit> ()), std::make_pair (db::NetlistCrossReference::None, std::string ()));
}
std::pair<IndexedNetlistModel::circuit_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::child_circuit_from_index(const circuit_pair &circuits, size_t index) const
std::pair<IndexedNetlistModel::circuit_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::child_circuit_from_index (const circuit_pair &circuits, size_t index) const
{
db::Circuit::const_child_circuit_iterator none;
return std::make_pair (attr_by_object_and_index (circuits, index, circuits.first->begin_children (), circuits.first->end_children (), none, none, m_child_circuit_by_circuit_and_index, sort_by_name<db::Circuit> ()), std::make_pair (db::NetlistCrossReference::None, std::string ()));
}
std::pair<IndexedNetlistModel::circuit_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::circuit_from_index(size_t index) const
std::pair<IndexedNetlistModel::circuit_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::circuit_from_index (size_t index) const
{
db::Netlist::const_circuit_iterator none;
return std::make_pair (attr_by_object_and_index (std::make_pair (mp_netlist, (const db::Netlist *) 0), index, mp_netlist->begin_circuits (), mp_netlist->end_circuits (), none, none, m_circuit_by_index, sort_by_name<db::Circuit> ()), std::make_pair (db::NetlistCrossReference::None, std::string ()));
}
std::pair<IndexedNetlistModel::net_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::net_from_index(const circuit_pair &circuits, size_t index) const
std::pair<IndexedNetlistModel::net_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::net_from_index (const circuit_pair &circuits, size_t index) const
{
db::Circuit::const_net_iterator none;
return std::make_pair (attr_by_object_and_index (circuits, index, circuits.first->begin_nets (), circuits.first->end_nets (), none, none, m_net_by_circuit_and_index, sort_by_expanded_name<db::Net> ()), std::make_pair (db::NetlistCrossReference::None, std::string ()));
return std::make_pair (attr_by_object_and_index (std::make_pair (circuits.first, (const db::Circuit *) 0), index, circuits.first->begin_nets (), circuits.first->end_nets (), none, none, m_net_by_circuit_and_index, sort_by_expanded_name<db::Net> ()), std::make_pair (db::NetlistCrossReference::None, std::string ()));
}
const db::Net *
@ -346,12 +346,15 @@ IndexedNetlistModel::net_subcircuit_pin_pair
SingleIndexedNetlistModel::net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const
{
db::Net::const_subcircuit_pin_iterator none;
return attr_by_object_and_index (nets, index, nets.first->begin_subcircuit_pins (), nets.first->end_subcircuit_pins (), none, none, m_subcircuit_pinref_by_net_and_index, sort_by_pin_name<db::NetSubcircuitPinRef> ());
return attr_by_object_and_index (std::make_pair (nets.first, (const db::Net *) 0), index, nets.first->begin_subcircuit_pins (), nets.first->end_subcircuit_pins (), none, none, m_subcircuit_pinref_by_net_and_index, sort_by_pin_name<db::NetSubcircuitPinRef> ());
}
IndexedNetlistModel::net_subcircuit_pin_pair
SingleIndexedNetlistModel::subcircuit_pinref_from_index (const subcircuit_pair &subcircuits, size_t index) const
SingleIndexedNetlistModel::subcircuit_pinref_from_index (const subcircuit_pair &subcircuits_in, size_t index) const
{
subcircuit_pair subcircuits = subcircuits_in;
subcircuits.second = 0;
if (! subcircuits.first) {
return IndexedNetlistModel::net_subcircuit_pin_pair ((const db::NetSubcircuitPinRef *) 0, (const db::NetSubcircuitPinRef *) 0);
}
@ -381,62 +384,68 @@ IndexedNetlistModel::net_terminal_pair
SingleIndexedNetlistModel::net_terminalref_from_index (const net_pair &nets, size_t index) const
{
db::Net::const_terminal_iterator none;
return attr_by_object_and_index (nets, index, nets.first->begin_terminals (), nets.first->end_terminals (), none, none, m_terminalref_by_net_and_index, sort_by_terminal_id<db::NetTerminalRef> ());
return attr_by_object_and_index (std::make_pair (nets.first, (const db::Net *) 0), index, nets.first->begin_terminals (), nets.first->end_terminals (), none, none, m_terminalref_by_net_and_index, sort_by_terminal_id<db::NetTerminalRef> ());
}
IndexedNetlistModel::net_pin_pair
SingleIndexedNetlistModel::net_pinref_from_index (const net_pair &nets, size_t index) const
{
db::Net::const_pin_iterator none;
return attr_by_object_and_index (nets, index, nets.first->begin_pins (), nets.first->end_pins (), none, none, m_pinref_by_net_and_index, sort_by_pin_name<db::NetPinRef> ());
return attr_by_object_and_index (std::make_pair (nets.first, (const db::Net *) 0), index, nets.first->begin_pins (), nets.first->end_pins (), none, none, m_pinref_by_net_and_index, sort_by_pin_name<db::NetPinRef> ());
}
std::pair<IndexedNetlistModel::device_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::device_from_index(const circuit_pair &circuits, size_t index) const
std::pair<IndexedNetlistModel::device_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::device_from_index (const circuit_pair &circuits, size_t index) const
{
db::Circuit::const_device_iterator none;
return std::make_pair (attr_by_object_and_index (circuits, index, circuits.first->begin_devices (), circuits.first->end_devices (), none, none, m_device_by_circuit_and_index, sort_by_expanded_name<db::Device> ()), std::make_pair (db::NetlistCrossReference::None, std::string ()));
return std::make_pair (attr_by_object_and_index (std::make_pair (circuits.first, (const db::Circuit *) 0), index, circuits.first->begin_devices (), circuits.first->end_devices (), none, none, m_device_by_circuit_and_index, sort_by_expanded_name<db::Device> ()), std::make_pair (db::NetlistCrossReference::None, std::string ()));
}
std::pair<IndexedNetlistModel::pin_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::pin_from_index(const circuit_pair &circuits, size_t index) const
std::pair<IndexedNetlistModel::pin_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::pin_from_index (const circuit_pair &circuits, size_t index) const
{
db::Circuit::const_pin_iterator none;
return std::make_pair (attr_by_object_and_index (circuits, index, circuits.first->begin_pins (), circuits.first->end_pins (), none, none, m_pin_by_circuit_and_index, Unsorted ()), std::make_pair (db::NetlistCrossReference::None, std::string ()));
return std::make_pair (attr_by_object_and_index (std::make_pair (circuits.first, (const db::Circuit *) 0), index, circuits.first->begin_pins (), circuits.first->end_pins (), none, none, m_pin_by_circuit_and_index, Unsorted ()), std::make_pair (db::NetlistCrossReference::None, std::string ()));
}
std::pair<IndexedNetlistModel::subcircuit_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::subcircuit_from_index(const circuit_pair &circuits, size_t index) const
std::pair<IndexedNetlistModel::subcircuit_pair, std::pair<IndexedNetlistModel::Status, std::string> > SingleIndexedNetlistModel::subcircuit_from_index (const circuit_pair &circuits, size_t index) const
{
db::Circuit::const_subcircuit_iterator none;
return std::make_pair (attr_by_object_and_index (circuits, index, circuits.first->begin_subcircuits (), circuits.first->end_subcircuits (), none, none, m_subcircuit_by_circuit_and_index, sort_by_expanded_name<db::SubCircuit> ()), std::make_pair (db::NetlistCrossReference::None, std::string ()));
return std::make_pair (attr_by_object_and_index (std::make_pair (circuits.first, (const db::Circuit *) 0), index, circuits.first->begin_subcircuits (), circuits.first->end_subcircuits (), none, none, m_subcircuit_by_circuit_and_index, sort_by_expanded_name<db::SubCircuit> ()), std::make_pair (db::NetlistCrossReference::None, std::string ()));
}
size_t
SingleIndexedNetlistModel::circuit_index (const circuit_pair &circuits) const
{
db::Netlist::const_circuit_iterator none;
return index_from_attr (circuits, mp_netlist->begin_circuits (), mp_netlist->end_circuits (), none, none, m_circuit_index_by_object, sort_by_name<db::Circuit> ());
return index_from_attr (std::make_pair (circuits.first, (const db::Circuit *) 0), mp_netlist->begin_circuits (), mp_netlist->end_circuits (), none, none, m_circuit_index_by_object, sort_by_name<db::Circuit> ());
}
size_t
SingleIndexedNetlistModel::net_index (const net_pair &nets) const
SingleIndexedNetlistModel::net_index (const net_pair &nets_in) const
{
db::Circuit::const_net_iterator none;
net_pair nets = nets_in;
nets.second = 0;
circuit_pair circuits = parent_of (nets);
return index_from_attr (nets,
circuits.first ? circuits.first->begin_nets () : none, circuits.first ? circuits.first->end_nets () : none,
circuits.second ? circuits.second->begin_nets () : none, circuits.second ? circuits.second->end_nets () : none,
none, none,
m_net_index_by_object, sort_by_expanded_name<db::Net> ());
}
size_t
SingleIndexedNetlistModel::device_index (const device_pair &devices) const
SingleIndexedNetlistModel::device_index (const device_pair &devices_in) const
{
db::Circuit::const_device_iterator none;
device_pair devices = devices_in;
devices.second = 0;
circuit_pair circuits = parent_of (devices);
return index_from_attr (devices,
circuits.first ? circuits.first->begin_devices () : none, circuits.first ? circuits.first->end_devices () : none,
circuits.second ? circuits.second->begin_devices () : none, circuits.second ? circuits.second->end_devices () : none,
none, none,
m_device_index_by_object, sort_by_expanded_name<db::Device> ());
}
@ -445,21 +454,24 @@ SingleIndexedNetlistModel::pin_index (const pin_pair &pins, const circuit_pair &
{
db::Circuit::const_pin_iterator none;
return index_from_attr (pins,
return index_from_attr (std::make_pair (pins.first, (const db::Pin *) 0),
circuits.first ? circuits.first->begin_pins () : none, circuits.first ? circuits.first->end_pins () : none,
circuits.second ? circuits.second->begin_pins () : none, circuits.second ? circuits.second->end_pins () : none,
none, none,
m_pin_index_by_object, sort_by_expanded_name<db::Pin> ());
}
size_t
SingleIndexedNetlistModel::subcircuit_index (const subcircuit_pair &subcircuits) const
SingleIndexedNetlistModel::subcircuit_index (const subcircuit_pair &subcircuits_in) const
{
db::Circuit::const_subcircuit_iterator none;
subcircuit_pair subcircuits = subcircuits_in;
subcircuits.second = 0;
circuit_pair circuits = parent_of (subcircuits);
return index_from_attr (subcircuits,
circuits.first ? circuits.first->begin_subcircuits () : none, circuits.first ? circuits.first->end_subcircuits () : none,
circuits.second ? circuits.second->begin_subcircuits () : none, circuits.second ? circuits.second->end_subcircuits () : none,
none, none,
m_subcircuit_index_by_object, sort_by_expanded_name<db::SubCircuit> ());
}

View File

@ -141,6 +141,12 @@ NetlistBrowserDialog::db ()
return mp_ui->browser_page->db ();
}
int
NetlistBrowserDialog::l2n_index ()
{
return m_l2n_index;
}
const lay::NetlistObjectsPath &
NetlistBrowserDialog::current_path () const
{
@ -152,6 +158,14 @@ NetlistBrowserDialog::current_path () const
}
}
void
NetlistBrowserDialog::set_current_path (const lay::NetlistObjectsPath &path)
{
if (mp_ui->browser_page) {
mp_ui->browser_page->select_path (path);
}
}
const std::vector<lay::NetlistObjectsPath> &
NetlistBrowserDialog::selected_paths () const
{

View File

@ -77,11 +77,26 @@ public:
*/
db::LayoutToNetlist *db ();
/**
* @brief Gets the index of the currently selected database in the view
*/
int l2n_index ();
/**
* @brief Gets the current object's path
*/
const lay::NetlistObjectsPath &current_path () const;
/**
* @brief Sets the current object's path
*
* This will make the given netlist object the current one and change the
* selection to this one.
*
* Passing a null path will clear the selection.
*/
void set_current_path (const lay::NetlistObjectsPath &path);
/**
* @brief Gets the selected nets
*/

View File

@ -3025,9 +3025,9 @@ NetlistBrowserModel::index_from_subcircuit (const std::pair<const db::SubCircuit
}
QModelIndex
NetlistBrowserModel::index_from_circuit (const db::Circuit *net) const
NetlistBrowserModel::index_from_circuit (const db::Circuit *circuit) const
{
return index_from_circuit (std::make_pair (net, mp_indexer->second_circuit_for (net)));
return index_from_circuit (std::make_pair (circuit, mp_indexer->second_circuit_for (circuit)));
}
std::pair<const db::Circuit *, const db::Circuit *>

View File

@ -502,9 +502,9 @@ NetlistBrowserPage::select_path (const lay::NetlistObjectsPath &path)
{
if (path.is_null ()) {
nl_directory_tree->clearSelection ();
sch_directory_tree->clearSelection ();
xref_directory_tree->clearSelection ();
nl_directory_tree->setCurrentIndex (QModelIndex ());
sch_directory_tree->setCurrentIndex (QModelIndex ());
xref_directory_tree->setCurrentIndex (QModelIndex ());
} else {
@ -514,17 +514,33 @@ NetlistBrowserPage::select_path (const lay::NetlistObjectsPath &path)
model = dynamic_cast<NetlistBrowserModel *> (nl_directory_tree->model ());
if (model) {
nl_directory_tree->setCurrentIndex (model->index_from_path (path));
if (lvsdb && lvsdb->cross_ref () && ! path.root.first) {
lay::NetlistObjectsPath nl_path = lay::NetlistObjectsPath::from_first (path.second ());
// Note: translation helps generating a layout-netlist index to
// naviate to the layout netlist in case of schematic probing for example (only
// works if all path components can be translated)
if (lay::NetlistObjectsPath::translate (nl_path, *lvsdb->cross_ref ())) {
nl_directory_tree->setCurrentIndex (model->index_from_path (nl_path));
}
} else {
nl_directory_tree->setCurrentIndex (model->index_from_path (path));
}
}
model = dynamic_cast<NetlistBrowserModel *> (sch_directory_tree->model ());
if (model && lvsdb && lvsdb->cross_ref ()) {
lay::NetlistObjectsPath sch_path = path;
// Note: translation helps generating a schematic-netlist index to
// naviate to the schematic netlist in case of probing for example (only
// works if all path components can be translated)
if (lay::NetlistObjectsPath::translate (sch_path, *lvsdb->cross_ref ())) {
if (! path.root.first) {
// No first path given: use second one for schematic directly.
lay::NetlistObjectsPath sch_path = lay::NetlistObjectsPath::from_first (path.second ());
sch_directory_tree->setCurrentIndex (model->index_from_path (sch_path));
} else {
lay::NetlistObjectsPath sch_path = path;
// Note: translation helps generating a schematic-netlist index to
// naviate to the schematic netlist in case of probing for example (only
// works if all path components can be translated)
if (lay::NetlistObjectsPath::translate (sch_path, *lvsdb->cross_ref ())) {
sch_directory_tree->setCurrentIndex (model->index_from_path (sch_path));
}
}
}
@ -1315,7 +1331,7 @@ NetlistBrowserPage::clear_highlights ()
void
NetlistBrowserPage::highlight (const NetlistObjectsPath &current_path, const std::vector<NetlistObjectsPath> &selected_paths)
{
if (current_path != m_current_path && selected_paths != m_selected_paths) {
if (current_path != m_current_path || selected_paths != m_selected_paths) {
m_current_path = current_path;
m_selected_paths = selected_paths;

View File

@ -169,6 +169,11 @@ public:
*/
void update_highlights ();
/**
* @brief Adjusts the view depending on the current settings
*/
void adjust_view ();
/**
* @brief Gets the current object's path
*/
@ -255,7 +260,6 @@ private:
void setup_trees ();
void add_to_history (const QModelIndex &index, bool fwd);
void navigate_to (const QModelIndex &index, bool forward = true);
void adjust_view ();
void clear_markers ();
void highlight (const NetlistObjectsPath &current_path, const std::vector<NetlistObjectsPath> &selected_paths);
void clear_highlights ();

View File

@ -139,10 +139,14 @@ LayoutViewNotificationWidget::LayoutViewNotificationWidget (LayoutViewWidget *pa
void
LayoutViewNotificationWidget::action_triggered ()
{
BEGIN_PROTECTED
auto a = m_action_buttons.find (sender ());
if (a != m_action_buttons.end ()) {
mp_parent->notification_action (*mp_notification, a->second);
}
END_PROTECTED_W (this)
}
void

View File

@ -24,6 +24,7 @@
#include "tlTimer.h"
#include "tlStream.h"
#include "tlFileUtils.h"
#include "tlEnv.h"
#include "dbReader.h"
#include "dbStream.h"
@ -40,44 +41,31 @@ namespace db
// ---------------------------------------------------------------
// Plugin for the stream reader
static std::string lef_file_formats ()
{
static std::string s;
if (s.empty ()) {
s = tl::get_env ("KLAYOUT_LEF_FORMAT", "*.lef *.LEF *.lef.gz *.LEF.gz");
}
return s;
}
static std::string def_file_formats ()
{
static std::string s;
if (s.empty ()) {
s = tl::get_env ("KLAYOUT_DEF_FORMAT", "*.def *.DEF *.def.gz *.DEF.gz");
}
return s;
}
/**
* @brief Determines the format of the given stream
* Returns true, if the stream has LEF format
*/
static bool is_lef_format (const std::string &fn)
{
static const char *suffixes[] = { ".lef", ".LEF", ".lef.gz", ".LEF.gz" };
// NOTE: there is no reliable way of (easily) detecting the format. Hence we use the file
// name's suffix for the format hint.
for (size_t i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); ++i) {
std::string suffix = suffixes [i];
if (fn.size () > suffix.size () && fn.find (suffix) == fn.size () - suffix.size ()) {
return true;
}
}
return false;
}
/**
* @brief Determines the format of the given stream
* Returns true, if the stream has DEF format
*/
static bool is_def_format (const std::string &fn)
{
static const char *suffixes[] = { ".def", ".DEF", ".def.gz", ".DEF.gz" };
// NOTE: there is no reliable way of (easily) detecting the format. Hence we use the file
// name's suffix for the format hint.
for (size_t i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); ++i) {
std::string suffix = suffixes [i];
if (fn.size () > suffix.size () && fn.find (suffix) == fn.size () - suffix.size ()) {
return true;
}
}
return false;
return tl::match_filename_to_format (fn, std::string ("LEF files (") + lef_file_formats () + ")");
}
// ---------------------------------------------------------------
@ -92,13 +80,13 @@ LEFDEFReader::LEFDEFReader (tl::InputStream &s)
const db::LayerMap &
LEFDEFReader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
{
return read_lefdef (layout, options, is_lef_format (m_stream.filename ()));
return read_lefdef (layout, options, is_lef_format ("." + m_stream.suffix ()));
}
const db::LayerMap &
LEFDEFReader::read (db::Layout &layout)
{
return read_lefdef (layout, db::LoadLayoutOptions (), is_lef_format (m_stream.filename ()));
return read_lefdef (layout, db::LoadLayoutOptions (), is_lef_format ("." + m_stream.suffix ()));
}
const char *
@ -283,11 +271,11 @@ class LEFDEFFormatDeclaration
virtual std::string format_name () const { return "LEFDEF"; }
virtual std::string format_desc () const { return "LEF/DEF"; }
virtual std::string format_title () const { return "LEF/DEF (unified reader)"; }
virtual std::string file_format () const { return "LEF/DEF files (*.lef *.LEF *.lef.gz *.LEF.gz *.def *.DEF *.def.gz *.DEF.gz)"; }
virtual std::string file_format () const { return std::string ("LEF/DEF files (") + lef_file_formats () + " " + def_file_formats () + ")"; }
virtual bool detect (tl::InputStream &stream) const
{
return is_lef_format (stream.filename ()) || is_def_format (stream.filename ());
return tl::match_filename_to_format (stream.filename (), file_format ());
}
virtual db::ReaderBase *create_reader (tl::InputStream &s) const

View File

@ -467,15 +467,27 @@ TEST(100)
run_test (_this, "issue-172", "lef:in.lef+def:in.def", "au.oas.gz", default_options (), false);
}
TEST(101)
TEST(101a)
{
db::LEFDEFReaderOptions opt = default_options ();
opt.set_produce_pin_names (true);
opt.set_pin_property_name (2);
opt.set_cell_outline_layer ("OUTLINE (13/0)");
run_test (_this, "issue-489", "lef:in.lef+def:in.def", "au.oas", opt, false);
}
TEST(101b)
{
db::LEFDEFReaderOptions opt = default_options ();
opt.set_produce_pin_names (true);
opt.set_pin_property_name (2);
opt.set_cell_outline_layer ("OUTLINE (13/0)");
// also with fake suffix
run_test (_this, "issue-489", "lef:'in.l[lef]'+def:'in.d[def]'", "au.oas", opt, false);
}
TEST(102)
{
db::LEFDEFReaderOptions opt = default_options ();

View File

@ -25,6 +25,7 @@
#include "dbOASISWriter.h"
#include "dbTextWriter.h"
#include "dbTestSupport.h"
#include "dbLayoutDiff.h"
#include "tlLog.h"
#include "tlUnitTest.h"
#include "tlStream.h"
@ -696,3 +697,49 @@ TEST(BlendCrash)
std::string fn_au (tl::testdata () + "/oasis/blend_crash_au.gds.gz");
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
}
TEST(CBlockLargePropertyString)
{
// NOTE: we try different blob sizes for triggering various reallocation szenarios
for (size_t f = 0; f < 50; ++f) {
size_t blob_size = size_t (12345) * f;
tl::info << "Trying blob size of " << blob_size;
db::Layout layout_org (false);
unsigned int layer = layout_org.insert_layer (db::LayerProperties (1, 0));
db::Cell &top = layout_org.cell (layout_org.add_cell ("TOP"));
std::string large_value (blob_size, 'a');
db::PropertiesSet ps;
ps.insert (db::property_names_id (tl::Variant ("blob")), tl::Variant (large_value));
top.shapes (layer).insert (db::BoxWithProperties (db::Box (0, 0, 100, 100), db::properties_id (ps)));
std::string tmp_file = tl::TestBase::tmp_file ("tmp_OASISReaderLargeString.oas");
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
db::OASISWriterOptions &oasis_options = options.get_options<db::OASISWriterOptions> ();
oasis_options.write_cblocks = true;
oasis_options.strict_mode = false;
db::OASISWriter writer;
writer.write (layout_org, out, options);
}
db::Layout layout_read;
{
tl::InputStream in (tmp_file);
db::OASISReader reader (in);
reader.read (layout_read);
}
EXPECT_EQ (db::compare_layouts (layout_org, layout_read, db::layout_diff::f_verbose, 0), true);
}
}

View File

@ -149,6 +149,7 @@ RUBYTEST (extNetTracer, "extNetTracer.rb")
RUBYTEST (imgObject, "imgObject.rb")
RUBYTEST (layLayers, "layLayers.rb")
RUBYTEST (layLayoutView, "layLayoutView.rb")
RUBYTEST (layNetlistBrowser, "layNetlistBrowser.rb")
RUBYTEST (layMainWindow, "layMainWindow.rb")
RUBYTEST (layMarkers, "layMarkers.rb")
RUBYTEST (layMacro, "layMacro.rb")

View File

@ -26,6 +26,7 @@
#include "tlAssert.h"
#include <algorithm>
#include <cstring>
#include <zlib.h>
@ -227,9 +228,12 @@ InflateFilter::InflateFilter (tl::InputStream &input)
m_last_block (false),
m_uncompressed_length (0) // this forces a new block on "process()"
{
for (size_t i = 0; i < sizeof (m_buffer) / sizeof (m_buffer [0]); ++i) {
m_buffer[i] = 0;
}
// NOTE: the minimum buffer size of 65536 corresponds to the maximum block size
// of the block repetition decoder
m_blen = 65536; // initially
m_buffer = new char [m_blen];
std::memset (m_buffer, 0, m_blen);
mp_dist_decoder = new HuffmannDecoder ();
mp_lit_decoder = new HuffmannDecoder ();
@ -237,6 +241,9 @@ InflateFilter::InflateFilter (tl::InputStream &input)
InflateFilter::~InflateFilter ()
{
delete[] m_buffer;
m_buffer = 0;
m_blen = 0;
delete mp_dist_decoder;
mp_dist_decoder = 0;
delete mp_lit_decoder;
@ -246,9 +253,36 @@ InflateFilter::~InflateFilter ()
const char *
InflateFilter::get (size_t n)
{
tl_assert (n < sizeof (m_buffer) / 2);
size_t blen = m_blen;
while (n >= blen / 2) {
blen *= 2;
}
while ((m_b_insert + sizeof (m_buffer) - m_b_read) % sizeof (m_buffer) < n) {
// buffer needs to be enlarged - reallocate
if (blen != m_blen) {
// NOTE: the deflate implementation actually looks back past the read pointer
// (in put_byte_dist), so we have to maintain the bytes between m_b_read
// and m_b_insert too.
char *new_buffer = new char[blen];
// place the current block twice at start and end of the block
std::memcpy (new_buffer, m_buffer, m_blen);
std::memcpy (new_buffer + blen - m_blen, m_buffer, m_blen);
// adjust read pointer if the stored byte array wrapped around the buffer
if (m_b_insert < m_b_read) {
m_b_read += blen - m_blen;
}
delete[] m_buffer;
m_buffer = new_buffer;
m_blen = blen;
}
while ((m_b_insert + m_blen - m_b_read) % m_blen < n) {
if (! process ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file (DEFLATE implementation)")));
}
@ -257,14 +291,14 @@ InflateFilter::get (size_t n)
tl_assert (m_b_read != m_b_insert);
// ensure the block is accessible as a coherent chunk:
if (m_b_read + n >= sizeof (m_buffer)) {
std::rotate (m_buffer, m_buffer + m_b_read, m_buffer + sizeof (m_buffer));
m_b_insert = (m_b_insert - m_b_read + sizeof (m_buffer)) % sizeof (m_buffer);
if (m_b_read + n >= m_blen) {
std::rotate (m_buffer, m_buffer + m_b_read, m_buffer + m_blen);
m_b_insert = (m_b_insert - m_b_read + m_blen) % m_blen;
m_b_read = 0;
}
const char *r = m_buffer + m_b_read;
m_b_read = (m_b_read + n) % sizeof (m_buffer);
m_b_read = (m_b_read + n) % m_blen;
return r;
}
@ -290,13 +324,16 @@ void
InflateFilter::put_byte (char b)
{
m_buffer [m_b_insert] = b;
m_b_insert = (m_b_insert + 1) % sizeof (m_buffer);
m_b_insert = (m_b_insert + 1) % m_blen;
// buffer overrun
tl_assert (m_b_insert != m_b_read);
}
void
InflateFilter::put_byte_dist (unsigned int d)
{
put_byte (m_buffer [(m_b_insert - d) % sizeof (m_buffer)]);
tl_assert (d < m_blen);
put_byte (m_buffer [(m_b_insert + m_blen - d) % m_blen]);
}
bool

View File

@ -232,7 +232,8 @@ public:
private:
BitStream m_input;
char m_buffer[65536];
char *m_buffer;
size_t m_blen;
unsigned int m_b_insert;
unsigned int m_b_read;
bool m_at_end;

View File

@ -48,6 +48,7 @@
#include "tlString.h"
#include "tlUri.h"
#include "tlHttpStream.h"
#include "tlGlobPattern.h"
#if defined(HAVE_QT)
# include <QByteArray>
@ -304,6 +305,9 @@ InputStream::InputStream (InputStreamBase &delegate)
m_bcap = 4096; // initial buffer capacity
m_blen = 0;
mp_buffer = new char [m_bcap];
m_explicit_suffix = false;
m_suffix = tl::extension (delegate.filename ());
}
InputStream::InputStream (InputStreamBase *delegate)
@ -312,9 +316,14 @@ InputStream::InputStream (InputStreamBase *delegate)
m_bcap = 4096; // initial buffer capacity
m_blen = 0;
mp_buffer = new char [m_bcap];
m_explicit_suffix = false;
if (delegate) {
m_suffix = tl::extension (delegate->filename ());
}
}
InputStream::InputStream (const std::string &abstract_path)
InputStream::InputStream (const std::string &abstract_path_in, bool allow_explicit_suffix)
: m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false)
{
m_bcap = 4096; // initial buffer capacity
@ -323,6 +332,22 @@ InputStream::InputStream (const std::string &abstract_path)
bool needs_inflate = false;
std::string abstract_path = abstract_path_in;
m_explicit_suffix = false;
// extract override suffix if needed
if (allow_explicit_suffix) {
tl::GlobPattern pat ("(*)\\[(*)\\]");
std::vector<std::string> pat_parts;
if (pat.match (abstract_path_in, pat_parts) && pat_parts.size () == 2) {
abstract_path = pat_parts[0];
m_suffix = pat_parts[1];
m_explicit_suffix = true;
}
}
tl::Extractor ex (abstract_path.c_str ());
if (ex.test (":")) {
@ -404,6 +429,10 @@ InputStream::InputStream (const std::string &abstract_path)
m_owns_delegate = true;
if (mp_delegate && ! m_explicit_suffix) {
m_suffix = tl::extension (mp_delegate->filename ());
}
if (needs_inflate) {
inflate_always ();
}
@ -1696,7 +1725,7 @@ match_filename_to_format (const std::string &fn, const std::string &fmt)
while (*fpp && *fpp != ' ' && *fpp != ')') {
++fpp;
}
if (fn.size () > (unsigned int) (fpp - fp) && strncmp (fn.c_str () + fn.size () - (fpp - fp), fp, fpp - fp) == 0) {
if (fn.size () >= (unsigned int) (fpp - fp) && strncmp (fn.c_str () + fn.size () - (fpp - fp), fp, fpp - fp) == 0) {
return true;
}
fp = fpp;

View File

@ -408,9 +408,14 @@ public:
* This will automatically create the appropriate delegate and
* delete it later.
*
* The abstract path
* @param abstract_path The abstract path (can be "pipe:<cmd>", "data:<base64-data>", "file:...", "http(s):...".
* @param allow_explicit_suffix If true, extracts the suffix from the abstract path
*
* With explicit suffix, the abstract path can be appended an override suffix in the
* form "path[suffix]" (e.g. "file.any[gds]"). The override suffix can be accessed with
* the "suffix" accessor. If an explicit suffix is given, "is_explicit_suffix" is true.
*/
InputStream (const std::string &abstract_path);
InputStream (const std::string &abstract_path, bool allow_explicit_suffix = true);
/**
* @brief Destructor
@ -528,6 +533,25 @@ public:
return mp_delegate->absolute_path ();
}
/**
* @brief Gets the suffix
*
* The suffix is either the override suffix or the
* filename's suffix if no override is given.
*/
const std::string &suffix () const
{
return m_suffix;
}
/**
* @brief Gets a value indicating if an explicit suffix is given
*/
bool is_explicit_suffix () const
{
return m_explicit_suffix;
}
/**
* @brief Reset to the initial position
*/
@ -593,6 +617,9 @@ private:
InputStreamBase *mp_delegate;
bool m_owns_delegate;
std::string m_suffix;
bool m_explicit_suffix;
// inflate support
InflateFilter *mp_inflate;
bool m_inflate_always;

View File

@ -187,6 +187,29 @@ TEST(DataInputStream)
EXPECT_EQ (tis.get_line (), "separated by a LFCR and CRLF.");
EXPECT_EQ (tis.line_number (), size_t (4));
EXPECT_EQ (tis.at_end (), true);
EXPECT_EQ (is.is_explicit_suffix (), false);
EXPECT_EQ (is.suffix (), "");
}
TEST(DataInputStreamWithSuffix)
{
tl::InputStream is ("data:SGVsbG8sIHdvcmxkIQpXaXRoIGFub3RoZXIgbGluZQoNDQpzZXBhcmF0ZWQgYnkgYSBMRkNSIGFuZCBDUkxGLg==[txt]");
tl::TextInputStream tis (is);
EXPECT_EQ (tis.get_line (), "Hello, world!");
EXPECT_EQ (tis.line_number (), size_t (1));
EXPECT_EQ (tis.get_line (), "With another line");
EXPECT_EQ (tis.line_number (), size_t (2));
EXPECT_EQ (tis.peek_char (), '\n');
EXPECT_EQ (tis.get_line (), "");
EXPECT_EQ (tis.line_number (), size_t (3));
EXPECT_EQ (tis.peek_char (), 's');
EXPECT_EQ (tis.get_line (), "separated by a LFCR and CRLF.");
EXPECT_EQ (tis.line_number (), size_t (4));
EXPECT_EQ (tis.at_end (), true);
EXPECT_EQ (is.is_explicit_suffix (), true);
EXPECT_EQ (is.suffix (), "txt");
}
namespace
@ -588,3 +611,16 @@ TEST(AbstractPathFunctions)
EXPECT_EQ (tl::InputStream::as_file_path ("a\\b\\c"), "a\\b\\c");
tl::file_utils_force_reset ();
}
TEST(MatchFormat)
{
EXPECT_EQ (tl::match_filename_to_format ("abc.txt", "Text files (*.txt *.TXT)"), true);
EXPECT_EQ (tl::match_filename_to_format ("abc.txt", "Text files (*.txt)"), true);
EXPECT_EQ (tl::match_filename_to_format ("abc.txt", "Text files (*.TXT)"), false);
EXPECT_EQ (tl::match_filename_to_format (".txt", "Text files (*.txt *.TXT)"), true);
EXPECT_EQ (tl::match_filename_to_format ("/home/xyz/abc.txt", "Text files (*.txt *.TXT)"), true);
EXPECT_EQ (tl::match_filename_to_format ("txt", "Text files (*.txt *.TXT)"), false);
EXPECT_EQ (tl::match_filename_to_format ("abc.TXT", "Text files (*.txt *.TXT)"), true);
EXPECT_EQ (tl::match_filename_to_format ("abc.TEXT", "Text files (*.txt *.TXT)"), false);
EXPECT_EQ (tl::match_filename_to_format ("abc.TEXT", "Text files (*)"), true);
}

6
testdata/bd/strmrun.drc vendored Normal file
View File

@ -0,0 +1,6 @@
# This is a smoke test
deep
puts("This is DRC.")

34
testdata/lefdef/issue-489/in.d vendored Normal file
View File

@ -0,0 +1,34 @@
VERSION 5.7 ;
DIVIDERCHAR "/" ;
BUSBITCHARS "[]" ;
DESIGN test ;
UNITS DISTANCE MICRONS 2000 ;
DIEAREA ( 0 0 ) ( 37520 7840 ) ;
PINS 12 ;
- VDD + NET VDD + SPECIAL + DIRECTION INOUT + USE POWER
+ LAYER M2 ( -320 0 ) ( 320 37520 )
+ FIXED ( 37520 3920 ) W ;
- VSS + NET VSS + SPECIAL + DIRECTION INOUT + USE GROUND
+ PORT
+ LAYER M2 ( -18760 0 ) ( 18760 640 )
+ FIXED ( 18760 -320 ) N
+ PORT
+ LAYER M2 ( -18760 0 ) ( 18760 640 )
+ FIXED ( 18760 8160 ) S
;
END PINS
SPECIALNETS 4 ;
- VSS ( * VSS )
+ ROUTED M2 640 + SHAPE FOLLOWPIN ( 0 0 ) ( 37520 * )
NEW M2 640 + SHAPE FOLLOWPIN ( 0 7840 ) ( 37520 * )
+ USE GROUND
;
- VDD ( * VDD )
+ ROUTED M2 640 + SHAPE FOLLOWPIN ( 0 3920 ) ( 37520 * )
+ USE POWER
;
END SPECIALNETS
END DESIGN

63
testdata/lefdef/issue-489/in.l vendored Normal file
View File

@ -0,0 +1,63 @@
VERSION 5.7 ;
BUSBITCHARS "[]" ;
DIVIDERCHAR "/" ;
UNITS
DATABASE MICRONS 1000 ;
END UNITS
MANUFACTURINGGRID 0.002 ;
USEMINSPACING OBS OFF ;
LAYER overlap
TYPE OVERLAP ;
END overlap
LAYER contact
TYPE CUT ;
END contact
LAYER metal1
TYPE ROUTING ;
DIRECTION HORIZONTAL ;
END metal1
LAYER via1
TYPE CUT ;
END via1
LAYER metal2
TYPE ROUTING ;
DIRECTION VERTICAL ;
END metal2
LAYER via2
TYPE CUT ;
END via2
LAYER metal3
TYPE ROUTING ;
DIRECTION HORIZONTAL ;
END metal3
LAYER via3
TYPE CUT ;
END via3
LAYER metal4
TYPE ROUTING ;
DIRECTION VERTICAL ;
END metal4
LAYER via4
TYPE CUT ;
END via4
LAYER metal5
TYPE ROUTING ;
DIRECTION HORIZONTAL ;
END metal5
END LIBRARY

BIN
testdata/lefdef/strm2oas_small/au.gds vendored Normal file

Binary file not shown.

29
testdata/lefdef/strm2oas_small/in.defok vendored Normal file
View File

@ -0,0 +1,29 @@
VERSION 5.6 ;
NAMESCASESENSITIVE ON ;
DIVIDERCHAR "/" ;
BUSBITCHARS "<>" ;
DESIGN SMALL ;
UNITS DISTANCE MICRONS 100 ;
DIEAREA ( -30 -30 ) ( 1030 1030 ) ;
VIAS 1 ;
- via1_960x340
+ VIARULE Via1Array-0
+ CUTSIZE 140 140
+ LAYERS metal1 via1 metal2
+ CUTSPACING 160 160
+ ENCLOSURE 110 100 110 100
+ ROWCOL 1 3
;
END VIAS
SPECIALNETS 1 ;
- VDD ( * VDD )
+ ROUTED metal1 0 + SHAPE STRIPE ( 500 500 ) via1_960x340
+ USE POWER ;
END SPECIALNETS
END DESIGN

118
testdata/ruby/layNetlistBrowser.rb vendored Normal file
View File

@ -0,0 +1,118 @@
# encoding: UTF-8
# KLayout Layout Viewer
# Copyright (C) 2006-2026 Matthias Koefferlein
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
if !$:.member?(File::dirname($0))
$:.push(File::dirname($0))
end
load("test_prologue.rb")
class LAYNetlistBrowser_TestClass < TestBase
# Basic view creation and MainWindow events
def test_1
if !RBA.constants.member?(:Application)
return
end
app = RBA::Application.instance
mw = app.main_window
mw.close_all
cv = mw.load_layout(ENV["TESTSRC"] + "/testdata/lvs/ringo_mixed_hierarchy.gds", 0)
lv = cv.view
nb = lv.netlist_browser
db = lv.lvsdb(lv.create_lvsdb("DB1"))
db.read(ENV["TESTSRC"] + "/testdata/lvs/ringo_mixed_hierarchy.lvsdb")
lv.show_lvsdb(0, 0)
# Test path selections
nl = nb.db.netlist
# net
n = nl.circuit_by_name("INVX1").net_by_name("OUT")
pth = RBA::NetlistObjectsPath.from_first(n)
nb.current_path = pth
assert_equal(nb.current_path.first.net.to_s, "INVX1:OUT")
assert_equal(nb.current_path.second.net.to_s, "INVX1:OUT")
# device
n = nl.circuit_by_name("INVX1").device_by_id(1)
pth = RBA::NetlistObjectsPath.from_first(n)
nb.current_path = pth
assert_equal(nb.current_path.first.device.expanded_name, "$1")
assert_equal(nb.current_path.second.device.expanded_name, "$1")
# subcircuit
n = nl.circuit_by_name("RINGO").subcircuit_by_id(2)
pth = RBA::NetlistObjectsPath.from_first(n)
nb.current_path = pth
assert_equal(nb.current_path.first.path[-1].expanded_name, "$2")
assert_equal(nb.current_path.second.path[-1].expanded_name, "$11")
# circuit
n = nl.circuit_by_name("INVX1")
pth = RBA::NetlistObjectsPath.from_first(n)
nb.current_path = pth
assert_equal(nb.current_path.first.root.name, "INVX1")
assert_equal(nb.current_path.second.root.name, "INVX1")
nl = nb.db.reference
# net:
n = nl.circuit_by_name("INVX1").net_by_name("OUT")
pth = RBA::NetlistObjectsPath.from_second(n)
nb.current_path = pth
assert_equal(nb.current_path.first.net.to_s, "INVX1:OUT")
assert_equal(nb.current_path.second.net.to_s, "INVX1:OUT")
# device
n = nl.circuit_by_name("INVX1").device_by_name("$2")
pth = RBA::NetlistObjectsPath.from_second(n)
nb.current_path = pth
assert_equal(nb.current_path.first.device.expanded_name, "$2")
assert_equal(nb.current_path.second.device.expanded_name, "$2")
# subcircuit
n = nl.circuit_by_name("RINGO").subcircuit_by_name("$3")
pth = RBA::NetlistObjectsPath.from_second(n)
nb.current_path = pth
assert_equal(nb.current_path.first.path[-1].expanded_name, "$4")
assert_equal(nb.current_path.second.path[-1].expanded_name, "$3")
# circuit
n = nl.circuit_by_name("INVX1")
pth = RBA::NetlistObjectsPath.from_second(n)
nb.current_path = pth
assert_equal(nb.current_path.first.root.name, "INVX1")
assert_equal(nb.current_path.second.root.name, "INVX1")
mw.close_all
end
end
load("test_epilogue.rb")