Merge branch 'master' into wip2

This commit is contained in:
Matthias Koefferlein 2023-08-01 21:48:00 +02:00
commit 476c7156ab
26 changed files with 748 additions and 215 deletions

View File

@ -128,6 +128,19 @@ size_t hash_value (const db::LayerProperties *l)
return std::hfunc (*l);
}
static bool log_equal_ext (const db::LayerProperties *lp1, const db::LayerProperties &lp2)
{
if (lp1->log_equal (lp2)) {
return true;
}
// compare by name as fallback if one argument is named and the other is not
// (this gives a way to look up
if ((lp1->is_named () || lp2.is_named()) && ! lp1->name.empty () && ! lp2.name.empty ()) {
return lp1->name == lp2.name;
}
return false;
}
// since there already exists a "LayerProperties" object, we call this one "LayerInfo"
Class<db::LayerProperties> decl_LayerInfo ("db", "LayerInfo",
gsi::constructor ("new", &ctor_layer_info_default,
@ -194,14 +207,31 @@ Class<db::LayerProperties> decl_LayerInfo ("db", "LayerInfo",
"\n"
"This method was added in version 0.18.\n"
) +
gsi::method ("is_equivalent?", &db::LayerProperties::log_equal, gsi::arg ("b"),
gsi::method_ext ("is_equivalent?", &log_equal_ext, gsi::arg ("b"),
"@brief Equivalence of two layer info objects\n"
"@return True, if both are equivalent\n"
"\n"
"First, layer and datatype are compared. The name is of second order and used only if no layer or datatype is given.\n"
"This is basically a weak comparison that reflects the search preferences.\n"
"First, layer and datatype are compared. The name is of second order and used only if no layer or datatype is given "
"for one of the operands.\n"
"This is basically a weak comparison that reflects the search preferences. It is the basis for \\Layout#find_layer.\n"
"Here are some examples:\n"
"\n"
"This method was added in version 0.18.\n"
"@code\n"
"# no match as layer/datatypes or names differ:\n"
"RBA::LayerInfo::new(1, 17).is_equivalent?(RBA::LayerInfo::new(1, 18)) -> false\n"
"RBA::LayerInfo::new('metal1').is_equivalent?(RBA::LayerInfo::new('m1')) -> false\n"
"# exact match for numbered or named layers:\n"
"RBA::LayerInfo::new(1, 17).is_equivalent?(RBA::LayerInfo::new(1, 17)) -> true\n"
"RBA::LayerInfo::new('metal1').is_equivalent?(RBA::LayerInfo::new('metal1')) -> true\n"
"# match as names are second priority over layer/datatypes:\n"
"RBA::LayerInfo::new(1, 17, 'metal1').is_equivalent?(RBA::LayerInfo::new(1, 17, 'm1')) -> true\n"
"# match as name matching is fallback:\n"
"RBA::LayerInfo::new(1, 17, 'metal1').is_equivalent?(RBA::LayerInfo::new('metal1')) -> true\n"
"# no match as neither names or layer/datatypes match:\n"
"RBA::LayerInfo::new(1, 17, 'metal1').is_equivalent?(RBA::LayerInfo::new('m1')) -> false\n"
"@/code\n"
"\n"
"This method was added in version 0.18 and modified to compare non-named vs. named layers in version 0.28.11.\n"
) +
gsi::method_ext ("hash", &hash_value,
"@brief Computes a hash value\n"
@ -504,9 +534,15 @@ static tl::Variant find_layer (db::Layout *l, const db::LayerProperties &lp)
// for a null layer info always return nil
return tl::Variant ();
} else {
// if we have a layer with the requested properties already, return this.
// if we have a layer with the requested properties already, return this one.
// first pass: exact match
int index = l->get_layer_maybe (lp);
if (index >= 0) {
return tl::Variant ((unsigned int) index);
}
// second pass: relaxed match
for (db::Layout::layer_iterator li = l->begin_layers (); li != l->end_layers (); ++li) {
if ((*li).second->log_equal (lp)) {
if (log_equal_ext ((*li).second, lp)) {
return tl::Variant ((*li).first);
}
}
@ -530,7 +566,7 @@ static tl::Variant find_layer3 (db::Layout *l, int ln, int dn, const std::string
return find_layer (l, db::LayerProperties (ln, dn, name));
}
static std::vector<unsigned int> layer_indices (const db::Layout *l)
static std::vector<unsigned int> layer_indexes (const db::Layout *l)
{
std::vector<unsigned int> layers;
for (unsigned int i = 0; i < l->layers (); ++i) {
@ -1555,7 +1591,19 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"If a layer with the given properties already exists, this method will return the index of that layer."
"If no such layer exists, it will return nil.\n"
"\n"
"This method has been introduced in version 0.23.\n"
"In contrast to \\layer, this method will also find layers matching by name only. "
"For example:\n"
"\n"
"@code\n"
"# finds layer '17/0' and 'name (17/0)':\n"
"index = layout.find_layer(RBA::LayerInfo::new(17, 0))\n"
"# finds layer 'name' (first priority), but also 'name (17/0)' (second priority):\n"
"index = layout.find_layer(RBA::LayerInfo::new('name'))\n"
"# note that this will not match layer 'name (17/0)' and create a new name-only layer:\n"
"index = layout.layer(RBA::LayerInfo::new('name'))\n"
"@/code\n"
"\n"
"This method has been introduced in version 0.23 and has been extended to name queries in version 0.28.11.\n"
) +
gsi::method_ext ("find_layer", &find_layer1, gsi::arg ("name"),
"@brief Finds a layer with the given name\n"
@ -1563,7 +1611,17 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"If a layer with the given name already exists, this method will return the index of that layer."
"If no such layer exists, it will return nil.\n"
"\n"
"This method has been introduced in version 0.23.\n"
"In contrast to \\layer, this method will also find numbered layers if the name matches. "
"For example:\n"
"\n"
"@code\n"
"# finds layer 'name' (first priority), but also 'name (17/0)' (second priority):\n"
"index = layout.find_layer('name')\n"
"# note that this will not match layer 'name (17/0)' and create a new name-only layer:\n"
"index = layout.layer('name')\n"
"@/code\n"
"\n"
"This method has been introduced in version 0.23 and has been extended to name queries in version 0.28.11.\n"
) +
gsi::method_ext ("find_layer", &find_layer2, gsi::arg ("layer"), gsi::arg ("datatype"),
"@brief Finds a layer with the given layer and datatype number\n"
@ -1755,7 +1813,7 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"@param layer_index The index of the layer to delete.\n"
) +
gsi::method_ext ("layer_indexes|#layer_indices", &layer_indices,
gsi::method_ext ("layer_indexes|#layer_indices", &layer_indexes,
"@brief Gets a list of valid layer's indices\n"
"This method returns an array with layer indices representing valid layers.\n"
"\n"

View File

@ -30,6 +30,10 @@
#include "dbTilingProcessor.h"
#include "layLayoutViewBase.h"
#if defined(HAVE_QT)
# include <QImage>
#endif
namespace gsi
{
@ -437,27 +441,22 @@ static ImageRef *new_image ()
return new ImageRef ();
}
static ImageRef *new_image_f (const std::string &filename)
{
return new ImageRef (img::Object (filename, db::DCplxTrans ()));
}
static ImageRef *new_image_ft (const std::string &filename, const db::DCplxTrans &trans)
{
return new ImageRef (img::Object (filename, trans));
}
/*
static ImageRef *new_image_whc (size_t w, size_t h, bool color)
static ImageRef *new_image_pbt (const tl::PixelBuffer &pixel_buffer, const db::DCplxTrans &trans)
{
return new ImageRef (img::Object (w, h, db::DCplxTrans (), color));
return new ImageRef (img::Object (pixel_buffer, trans));
}
static ImageRef *new_image_whtc (size_t w, size_t h, const db::DCplxTrans &trans, bool color)
#if HAVE_QT
static ImageRef *new_image_qit (const QImage &image, const db::DCplxTrans &trans)
{
return new ImageRef (img::Object (w, h, trans, color));
return new ImageRef (img::Object (image, trans));
}
*/
#endif
static ImageRef *new_image_whd (size_t w, size_t h, const std::vector<double> &data)
{
@ -576,17 +575,8 @@ gsi::Class<ImageRef> decl_Image (decl_BasicImage, "lay", "Image",
"This will create an empty image without data and no particular pixel width or related.\n"
"Use the \\read_file or \\set_data methods to set image properties and pixel values.\n"
) +
gsi::constructor ("new", &gsi::new_image_f, gsi::arg ("filename"),
"@brief Constructor from a image file \n"
"\n"
"This constructor creates an image object from a file (which can have any format supported by Qt) and \n"
"a unit transformation. The image will originally be put to position 0,0 (lower left corner) and each pixel\n"
"will have a size of 1 (micron). \n"
"\n"
"@param filename The path to the image file to load.\n"
) +
gsi::constructor ("new", &gsi::new_image_ft, gsi::arg ("filename"), gsi::arg ("trans"),
"@brief Constructor from a image file \n"
gsi::constructor ("new", &gsi::new_image_ft, gsi::arg ("filename"), gsi::arg ("trans", db::DCplxTrans (), "unity"),
"@brief Constructor from a image file\n"
"\n"
"This constructor creates an image object from a file (which can have any format supported by Qt) and \n"
"a transformation. The image will originally be put to position 0,0 (lower left corner) and each pixel\n"
@ -595,6 +585,31 @@ gsi::Class<ImageRef> decl_Image (decl_BasicImage, "lay", "Image",
"@param filename The path to the image file to load.\n"
"@param trans The transformation to apply to the image when displaying it.\n"
) +
gsi::constructor ("new", &gsi::new_image_pbt, gsi::arg ("pixels"), gsi::arg ("trans", db::DCplxTrans (), "unity"),
"@brief Constructor from a image pixel buffer\n"
"\n"
"This constructor creates an image object from a pixel buffer object. This object holds RGB or mono image data similar to "
"QImage, except it is available also when Qt is not available (e.g. inside the Python module).\n"
"\n"
"The image will originally be put to position 0,0 (lower left corner) and each pixel\n"
"will have a size of 1. The transformation describes how to transform this image into micron space.\n"
"\n"
"@param filename The path to the image file to load.\n"
"@param trans The transformation to apply to the image when displaying it.\n"
) +
#if defined(HAVE_QT)
gsi::constructor ("new", &gsi::new_image_qit, gsi::arg ("image"), gsi::arg ("trans", db::DCplxTrans (), "unity"),
"@brief Constructor from a image pixel buffer\n"
"\n"
"This constructor creates an image object from a pixel QImage object and uses RGB or mono image data to generate the image.\n"
"\n"
"The image will originally be put to position 0,0 (lower left corner) and each pixel\n"
"will have a size of 1. The transformation describes how to transform this image into micron space.\n"
"\n"
"@param filename The path to the image file to load.\n"
"@param trans The transformation to apply to the image when displaying it.\n"
) +
#endif
gsi::constructor ("new", &gsi::new_image_whd, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("data"),
"@brief Constructor for a monochrome image with the given pixel values\n"
"\n"
@ -624,39 +639,6 @@ gsi::Class<ImageRef> decl_Image (decl_BasicImage, "lay", "Image",
"@param trans The transformation from pixel space to micron space\n"
"@param d The data (see method description)\n"
) +
/* HINT: these declarations cannot be used currently since any array is case to the boolean color parameter
gsi::constructor ("new", &img::new_image_whc, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("color"),
"@brief Constructor for monochrome or color images with zero pixel values\n"
"\n"
"This constructor creates an image object from a data set describing one monochrome channel\n"
"or three color channels.\n"
"Each channel consists of an array of x*y values where the first \"x\" values describe the first (lowest!) row\n"
"and so on. Note, that the rows are oriented in the mathematical sense (first one is the lowest) contrary to \n"
"the common convention for image data.\n"
"The data fields can be accessed with the \"data\", \"set_data\", \"pixel\" or \"set_pixel\" methods.\n"
"Initially the pixel width and height will be 1 micron and the data range will be 0 to 1.0 (black to white level). \n"
"\n"
"@param w The width of the image\n"
"@param h The height of the image\n"
"@param color True to create a color image.\n"
) +
gsi::constructor ("new", &img::new_image_whtc, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("trans"), gsi::arg ("color"),
"@brief Constructor for monochrome or color images with zero pixel values\n"
"\n"
"This constructor creates an image object from a data set describing one monochrome channel\n"
"or three color channels.\n"
"Each channel consists of an array of x*y values where the first \"x\" values describe the first (lowest!) row\n"
"and so on. Note, that the rows are oriented in the mathematical sense (first one is the lowest) contrary to \n"
"the common convention for image data.\n"
"The data fields can be accessed with the \"data\", \"set_data\", \"pixel\" or \"set_pixel\" methods.\n"
"Initially the pixel width and height will be 1 micron and the data range will be 0 to 1.0 (black to white level). \n"
"\n"
"@param w The width of the image\n"
"@param h The height of the image\n"
"@param trans The transformation to apply to the image when displaying it\n"
"@param color True to create a color image.\n"
) +
*/
gsi::constructor ("new", &gsi::new_image_whrgb, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("red"), gsi::arg ("green"), gsi::arg ("blue"),
"@brief Constructor for a color image with the given pixel values\n"
"\n"

View File

@ -851,6 +851,30 @@ Object::Object (const std::string &filename, const db::DCplxTrans &trans)
m_updates_enabled = true;
}
Object::Object (const tl::PixelBuffer &pixel_buffer, const db::DCplxTrans &trans)
: m_filename ("<object>"), m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
{
m_updates_enabled = false;
mp_pixel_data = 0;
mp_data = 0;
create_from_pixel_buffer (pixel_buffer);
m_updates_enabled = true;
}
#if defined(HAVE_QT)
Object::Object (const QImage &qimage, const db::DCplxTrans &trans)
: m_filename ("<object>"), m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
{
m_updates_enabled = false;
mp_pixel_data = 0;
mp_data = 0;
create_from_qimage (qimage);
m_updates_enabled = true;
}
#endif
Object::Object (size_t w, size_t h, const db::Matrix3d &trans, bool color, bool byte_data)
: m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
{
@ -934,6 +958,31 @@ Object::Object (const std::string &filename, const db::Matrix3d &trans)
m_updates_enabled = true;
}
Object::Object (const tl::PixelBuffer &pixel_buffer, const db::Matrix3d &trans)
: m_filename ("<object>"), m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
{
m_updates_enabled = false;
mp_pixel_data = 0;
mp_data = 0;
create_from_pixel_buffer (pixel_buffer);
read_file ();
m_updates_enabled = true;
}
#if defined(HAVE_QT)
Object::Object (const QImage &qimage, const db::Matrix3d &trans)
: m_filename ("<object>"), m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
{
m_updates_enabled = false;
mp_pixel_data = 0;
mp_data = 0;
create_from_qimage (qimage);
m_updates_enabled = true;
}
#endif
Object::Object (const img::Object &d)
{
m_updates_enabled = false;
@ -1566,65 +1615,7 @@ Object::read_file ()
#if defined(HAVE_QT)
QImage qimage (tl::to_qstring (m_filename));
if (! qimage.isNull ()) {
if (! m_min_value_set) {
m_min_value = 0.0;
}
if (! m_max_value_set) {
m_max_value = 255.0;
}
m_min_value_set = true;
m_max_value_set = true;
size_t w = qimage.width (), h = qimage.height ();
mp_data = new DataHeader (w, h, ! qimage.isGrayscale (), true);
mp_data->add_ref ();
size_t i = 0;
if (is_color ()) {
unsigned char *red = mp_data->byte_data (0);
unsigned char *green = mp_data->byte_data (1);
unsigned char *blue = mp_data->byte_data (2);
unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0;
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x) {
QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1)));
red[i] = qRed (rgb);
green[i] = qGreen (rgb);
blue[i] = qBlue (rgb);
if (msk) {
msk[i] = qAlpha (rgb) > 128;
}
++i;
}
}
} else {
unsigned char *d = mp_data->byte_data ();
unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0;
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x) {
QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1)));
*d++ = qGreen (rgb);
if (msk) {
msk[i] = qAlpha (rgb) > 128;
}
}
}
}
}
create_from_qimage (qimage);
#elif defined(HAVE_PNG)
@ -1635,6 +1626,82 @@ Object::read_file ()
img = tl::PixelBuffer::read_png (stream);
}
create_from_pixel_buffer (img);
#else
throw tl::Exception ("No PNG support compiled in - cannot load PNG files");
#endif
}
#if defined(HAVE_QT)
void
Object::create_from_qimage (const QImage &qimage)
{
if (qimage.isNull ()) {
return;
}
if (! m_min_value_set) {
m_min_value = 0.0;
}
if (! m_max_value_set) {
m_max_value = 255.0;
}
m_min_value_set = true;
m_max_value_set = true;
size_t w = qimage.width (), h = qimage.height ();
mp_data = new DataHeader (w, h, ! qimage.isGrayscale (), true);
mp_data->add_ref ();
size_t i = 0;
if (is_color ()) {
unsigned char *red = mp_data->byte_data (0);
unsigned char *green = mp_data->byte_data (1);
unsigned char *blue = mp_data->byte_data (2);
unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0;
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x) {
QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1)));
red[i] = qRed (rgb);
green[i] = qGreen (rgb);
blue[i] = qBlue (rgb);
if (msk) {
msk[i] = qAlpha (rgb) > 128;
}
++i;
}
}
} else {
unsigned char *d = mp_data->byte_data ();
unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0;
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x) {
QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1)));
*d++ = qGreen (rgb);
if (msk) {
msk[i] = qAlpha (rgb) > 128;
}
}
}
}
}
#endif
void
Object::create_from_pixel_buffer (const tl::PixelBuffer &img)
{
bool is_color = false;
for (unsigned int i = 0; i < img.height () && ! is_color; ++i) {
const tl::color_t *d = img.scan_line (i);
@ -1700,10 +1767,6 @@ Object::read_file ()
}
}
#else
throw tl::Exception ("No PNG support compiled in - cannot load PNG files");
#endif
}
void

View File

@ -21,7 +21,6 @@
*/
#ifndef HDR_imgObject
#define HDR_imgObject
@ -34,10 +33,15 @@
#include "dbPolygon.h"
#include "tlDataMapping.h"
#include "tlColor.h"
#include "tlPixelBuffer.h"
#include <string>
#include <vector>
#if defined(HAVE_QT)
class QImage;
#endif
namespace img {
class DataHeader;
@ -293,6 +297,26 @@ public:
*/
Object (const std::string &filename, const db::DCplxTrans &trans);
/**
* @brief Constructor from a PixelBuffer object
*
* This constructor creates an image object from a PixelBuffer object.
* The image will originally be put to position 0, 0 (lower left corner) and each pixel
* will have a size of 1. The transformation describes how to transform this image into micron space.
*/
Object (const tl::PixelBuffer &pixel_buffer, const db::DCplxTrans &trans);
#if defined(HAVE_QT)
/**
* @brief Constructor from a QImage object
*
* This constructor creates an image object from a QImage object.
* The image will originally be put to position 0, 0 (lower left corner) and each pixel
* will have a size of 1. The transformation describes how to transform this image into micron space.
*/
Object (const QImage &image, const db::DCplxTrans &trans);
#endif
/**
* @brief Constructor for monochrome or color images with zero pixel values
*
@ -418,6 +442,26 @@ public:
*/
Object (const std::string &filename, const db::Matrix3d &trans);
/**
* @brief Constructor from a PixelBuffer object
*
* This constructor creates an image object from a PixelBuffer object.
* The image will originally be put to position 0, 0 (lower left corner) and each pixel
* will have a size of 1. The transformation describes how to transform this image into micron space.
*/
Object (const tl::PixelBuffer &pixel_buffer, const db::Matrix3d &trans);
#if defined(HAVE_QT)
/**
* @brief Constructor from a QImage object
*
* This constructor creates an image object from a QImage object.
* The image will originally be put to position 0, 0 (lower left corner) and each pixel
* will have a size of 1. The transformation describes how to transform this image into micron space.
*/
Object (const QImage &image, const db::Matrix3d &trans);
#endif
/**
* @brief Copy constructor
*/
@ -984,6 +1028,10 @@ private:
void validate_pixel_data () const;
void allocate (bool color);
void read_file ();
#if defined(HAVE_QT)
void create_from_qimage (const QImage &qimage);
#endif
void create_from_pixel_buffer (const tl::PixelBuffer &img);
};
}

View File

@ -62,7 +62,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset>
<iconset resource="../../icons/icons.qrc">
<normaloff>:/run_16px.png</normaloff>:/run_16px.png</iconset>
</property>
<property name="shortcut">
@ -480,27 +480,6 @@
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0" colspan="5">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Info</string>
</property>
</widget>
</item>
<item row="2" column="6">
<widget class="QToolButton" name="photo_pb">
<property name="toolTip">
<string>Add snapshot</string>
</property>
<property name="text">
<string>Photo</string>
</property>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/photo_16px.png</normaloff>:/photo_16px.png</iconset>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QToolButton" name="waive_pb">
<property name="toolTip">
@ -515,40 +494,6 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="info_label">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="flags_pb">
<property name="toolTip">
<string>Set or reset flag</string>
</property>
<property name="text">
<string>Flag</string>
</property>
<property name="popupMode">
<enum>QToolButton::MenuButtonPopup</enum>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QToolButton" name="important_pb">
<property name="toolTip">
<string>Important</string>
</property>
<property name="text">
<string>Imp</string>
</property>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/important_16px.png</normaloff>:/important_16px.png</iconset>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QToolButton" name="nophoto_pb">
<property name="toolTip">
@ -566,6 +511,54 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="flags_pb">
<property name="toolTip">
<string>Set or reset flag</string>
</property>
<property name="text">
<string>Flag</string>
</property>
<property name="popupMode">
<enum>QToolButton::MenuButtonPopup</enum>
</property>
</widget>
</item>
<item row="2" column="6">
<widget class="QToolButton" name="photo_pb">
<property name="toolTip">
<string>Add snapshot</string>
</property>
<property name="text">
<string>Photo</string>
</property>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/photo_16px.png</normaloff>:/photo_16px.png</iconset>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QToolButton" name="important_pb">
<property name="toolTip">
<string>Important</string>
</property>
<property name="text">
<string>Imp</string>
</property>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/important_16px.png</normaloff>:/important_16px.png</iconset>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="info_label">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0" colspan="7">
<widget class="QFrame" name="frame_4">
<property name="sizePolicy">
@ -612,6 +605,63 @@ p, li { white-space: pre-wrap; }
</layout>
</widget>
</item>
<item row="0" column="0" colspan="7">
<widget class="QFrame" name="frame_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Info</string>
</property>
</widget>
</item>
<item>
<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>
<item>
<widget class="QCheckBox" name="list_shapes_cb">
<property name="text">
<string>list shapes</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>

View File

@ -42,6 +42,7 @@ namespace rdb
std::string cfg_rdb_context_mode ("rdb-context-mode");
std::string cfg_rdb_show_all ("rdb-show-all");
std::string cfg_rdb_list_shapes ("rdb-list-shapes");
std::string cfg_rdb_window_state ("rdb-window-state-v2"); // v2: 0.24++
std::string cfg_rdb_window_mode ("rdb-window-mode");
std::string cfg_rdb_window_dim ("rdb-window-dim");

View File

@ -48,6 +48,7 @@ namespace rdb
extern std::string cfg_rdb_context_mode;
extern std::string cfg_rdb_show_all;
extern std::string cfg_rdb_list_shapes;
extern std::string cfg_rdb_window_state;
extern std::string cfg_rdb_window_mode;
extern std::string cfg_rdb_window_dim;
@ -445,6 +446,7 @@ MarkerBrowserDialog::configure (const std::string &name, const std::string &valu
bool need_update = false;
bool taken = true;
bool show_all = mp_ui->browser_frame->show_all ();
bool list_shapes = mp_ui->browser_frame->list_shapes ();
if (name == cfg_rdb_context_mode) {
@ -452,6 +454,10 @@ MarkerBrowserDialog::configure (const std::string &name, const std::string &valu
MarkerBrowserContextModeConverter ().from_string (value, context);
need_update = lay::test_and_set (m_context, context);
} else if (name == cfg_rdb_list_shapes) {
tl::from_string (value, list_shapes);
} else if (name == cfg_rdb_show_all) {
tl::from_string (value, show_all);
@ -540,6 +546,7 @@ MarkerBrowserDialog::configure (const std::string &name, const std::string &valu
}
mp_ui->browser_frame->show_all (show_all);
mp_ui->browser_frame->list_shapes (list_shapes);
return taken;
}

View File

@ -44,6 +44,7 @@ namespace rdb
{
extern std::string cfg_rdb_show_all;
extern std::string cfg_rdb_list_shapes;
struct FlagDescriptor
{
@ -1447,6 +1448,7 @@ MarkerBrowserPage::MarkerBrowserPage (QWidget * /*parent*/)
m_update_needed (false),
mp_database (0),
m_show_all (true),
m_list_shapes (true),
mp_view (0),
m_cv_index (0),
m_num_items (0),
@ -1504,6 +1506,8 @@ MarkerBrowserPage::MarkerBrowserPage (QWidget * /*parent*/)
markers_list->header ()->setSortIndicatorShown (true);
markers_list->header ()->setMinimumSectionSize (24);
list_shapes_cb->setChecked (m_list_shapes);
connect (markers_list, SIGNAL (doubleClicked (const QModelIndex &)), this, SLOT (marker_double_clicked (const QModelIndex &)));
connect (dir_up_pb, SIGNAL (clicked ()), this, SLOT (dir_up_clicked ()));
@ -1519,6 +1523,7 @@ MarkerBrowserPage::MarkerBrowserPage (QWidget * /*parent*/)
connect (cat_filter, SIGNAL (textEdited (const QString &)), this, SLOT (filter_changed ()));
connect (cell_filter, SIGNAL (textEdited (const QString &)), this, SLOT (filter_changed ()));
connect (rerun_button, SIGNAL (pressed ()), this, SLOT (rerun_button_pressed ()));
connect (list_shapes_cb, SIGNAL (clicked ()), this, SLOT (list_shapes_clicked ()));
m_show_all_action = new QAction (QObject::tr ("Show All"), this);
m_show_all_action->setCheckable (true);
@ -1674,7 +1679,20 @@ MarkerBrowserPage::show_all (bool f)
}
}
void
void
MarkerBrowserPage::list_shapes (bool f)
{
if (f != m_list_shapes) {
m_list_shapes = f;
list_shapes_cb->setChecked (f);
update_info_text ();
}
}
void
MarkerBrowserPage::set_rdb (rdb::Database *database)
{
if (database != mp_database) {
@ -1975,7 +1993,7 @@ MarkerBrowserPage::update_info_text ()
for (rdb::Values::const_iterator v = item->values ().begin (); v != item->values ().end (); ++v) {
if (v->get () != 0) {
if (v->get () != 0 && (m_list_shapes || ! v->get ()->is_shape ())) {
if (v->tag_id () != 0) {
const rdb::Tag &tag = mp_database->tags ().tag (v->tag_id ());
@ -2851,7 +2869,15 @@ MarkerBrowserPage::show_all_clicked ()
}
}
void
void
MarkerBrowserPage::list_shapes_clicked ()
{
if (mp_plugin_root) {
mp_plugin_root->config_set (cfg_rdb_list_shapes, tl::to_string (list_shapes_cb->isChecked ()));
}
}
void
MarkerBrowserPage::unwaive_all ()
{
if (! mp_database) {

View File

@ -103,6 +103,21 @@ public:
*/
void show_all (bool f);
/**
* @brief Gets a value indicating whether to list the shapes in the info panel
*/
bool list_shapes () const
{
return m_list_shapes;
}
/**
* @brief Sets a value indicating whether to list the shapes in the info panel
*
* If this property is set to false, shapes will not be listed in the info panel.
*/
void list_shapes (bool f);
/**
* @brief Update the contents
*
@ -176,6 +191,7 @@ public slots:
void waive ();
void unwaive ();
void show_all_clicked ();
void list_shapes_clicked ();
void info_anchor_clicked (const QUrl &link);
void filter_changed ();
@ -184,6 +200,7 @@ private:
bool m_update_needed;
rdb::Database *mp_database;
bool m_show_all;
bool m_list_shapes;
QAction *m_show_all_action;
lay::LayoutViewBase *mp_view;
unsigned int m_cv_index;

View File

@ -227,6 +227,7 @@ void Macro::save_to (const std::string &path)
void Macro::load_from (const std::string &fn)
{
m_format = NoFormat;
m_interpreter = None;
std::pair<bool, std::string> f = format_from_filename (fn, m_interpreter, m_dsl_interpreter, m_autorun_default, m_format);
if (f.first) {
@ -252,12 +253,23 @@ void Macro::load_from (const std::string &fn)
tl::InputStream stream (path);
tl::TextInputStream text_stream (stream);
m_text = text_stream.read_all ();
sync_properties_with_text ();
if (m_format == PlainTextWithHashAnnotationsFormat) {
sync_properties_with_text ();
}
}
} else {
throw tl::Exception (tl::to_string (tr ("Unable to determine format for file from suffix or format spec ")) + fn);
if (tl::verbosity () >= 20) {
tl::log << "Loading macro from " << fn;
}
tl::InputStream stream (fn);
tl::TextInputStream text_stream (stream);
m_text = text_stream.read_all ();
}
m_modified = true;
@ -268,6 +280,7 @@ void Macro::load_from (const std::string &fn)
void Macro::load_from_string (const std::string &text, const std::string &url)
{
m_format = NoFormat;
m_interpreter = None;
if (tl::verbosity () >= 20) {
tl::log << "Loading macro from " << url;
@ -294,7 +307,7 @@ void Macro::load_from_string (const std::string &text, const std::string &url)
}
} else {
throw tl::Exception (tl::to_string (tr ("Unable to determine format for file from suffix ")) + url);
m_text = text;
}
m_modified = true;

View File

@ -51,11 +51,35 @@ MacroInterpreter::can_run (const lym::Macro *macro)
return false;
}
namespace
{
class MacroIncludeFileResolver
: public tl::IncludeFileResolver
{
public:
MacroIncludeFileResolver () { }
std::string get_text (const std::string &path) const
{
// Use lym::Macro to resolve texts - this strips the XML envelope.
// Intentionally not compatibility check is made to allow using any
// type of input and specifically any extension.
lym::Macro macro;
macro.load_from (path);
return macro.text ();
}
};
}
std::pair<std::string, std::string>
MacroInterpreter::include_expansion (const lym::Macro *macro)
{
MacroIncludeFileResolver include_file_resolver;
std::pair<std::string, std::string> res;
res.first = tl::IncludeExpander::expand (macro->path (), macro->text (), res.second).to_string ();
res.first = tl::IncludeExpander::expand (macro->path (), macro->text (), res.second, &include_file_resolver).to_string ();
if (res.first != macro->path ()) {

View File

@ -129,6 +129,29 @@ TEST(3_RubyInclude)
EXPECT_EQ (np (console.text ()), np ("An error in " + tl::testsrc () + "/testdata/lym/b_inc.rb:3\n"));
}
TEST(4_RubyIncludeFromXML)
{
tl_assert (rba::RubyInterpreter::instance () != 0);
lym::Macro macro;
macro.set_file_path (tl::testsrc () + "/testdata/lym/m4.rb");
macro.set_interpreter (lym::Macro::Ruby);
macro.load ();
TestCollectorConsole console;
rba::RubyInterpreter::instance ()->push_console (&console);
try {
EXPECT_EQ (macro.run (), 0);
rba::RubyInterpreter::instance ()->remove_console (&console);
} catch (...) {
rba::RubyInterpreter::instance ()->remove_console (&console);
throw;
}
EXPECT_EQ (np (console.text ()), np ("An error in " + tl::testsrc () + "/testdata/lym/b_inc.lym:3\n"));
}
TEST(11_DRCBasic)
{
tl_assert (rba::RubyInterpreter::instance () != 0);

View File

@ -588,9 +588,6 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C
error (tl::to_string (tr ("RECT routing specification not followed by coordinate list")));
}
// breaks wiring
pts.clear ();
// rect spec
double x1 = get_double ();

View File

@ -986,3 +986,9 @@ TEST(207_joined_paths)
run_test (_this, "issue-1345", "lef:in.lef+def:in.def", "au-nojoin.oas.gz", default_options (), false);
}
// issue-1432
TEST(208_nets_and_rects)
{
run_test (_this, "issue-1432", "map:test.map+lef:test.lef+def:test.def", "au.oas", default_options (), false);
}

View File

@ -156,6 +156,49 @@ template <> RDB_PUBLIC std::string Value<db::DText>::to_display_string () const
return to_string ();
}
// is_shape implementations
template <> RDB_PUBLIC bool Value<double>::is_shape () const
{
return false;
}
template <> RDB_PUBLIC bool Value<std::string>::is_shape () const
{
return false;
}
template <> RDB_PUBLIC bool Value<db::DPolygon>::is_shape () const
{
return true;
}
template <> RDB_PUBLIC bool Value<db::DEdge>::is_shape () const
{
return true;
}
template <> RDB_PUBLIC bool Value<db::DEdgePair>::is_shape () const
{
return true;
}
template <> RDB_PUBLIC bool Value<db::DBox>::is_shape () const
{
return true;
}
template <> RDB_PUBLIC bool Value<db::DPath>::is_shape () const
{
return true;
}
template <> RDB_PUBLIC bool Value<db::DText>::is_shape () const
{
return true;
}
bool ValueBase::compare (const ValueBase *a, const ValueBase *b)
{
// compare is the intrinsic compare of equal type and type index for different types.

View File

@ -438,6 +438,8 @@ public:
virtual std::string to_display_string () const = 0;
virtual bool is_shape () const = 0;
virtual ValueBase *clone () const = 0;
virtual int type_index () const = 0;
@ -508,6 +510,8 @@ public:
return m_value < static_cast<const Value<C> *> (other)->m_value;
}
bool is_shape () const;
std::string to_string () const;
std::string to_display_string () const;

View File

@ -32,6 +32,9 @@
namespace tl
{
// -----------------------------------------------------------------------------------------------------
// IncludeExpander implementation
static const char *valid_fn_chars = "@_:,.\\/-+";
IncludeExpander::IncludeExpander ()
@ -40,30 +43,30 @@ IncludeExpander::IncludeExpander ()
}
IncludeExpander
IncludeExpander::expand (const std::string &path, std::string &expanded_text)
IncludeExpander::expand (const std::string &path, std::string &expanded_text, const IncludeFileResolver *resolver)
{
IncludeExpander ie;
int lc = 1;
tl::InputStream is (path);
ie.read (path, is, expanded_text, ie, lc);
ie.read (path, is, expanded_text, lc, resolver);
return ie;
}
IncludeExpander
IncludeExpander::expand (const std::string &path, const std::string &original_text, std::string &expanded_text)
IncludeExpander::expand (const std::string &path, const std::string &original_text, std::string &expanded_text, const IncludeFileResolver *resolver)
{
IncludeExpander ie;
int lc = 1;
tl::InputMemoryStream ms (original_text.c_str (), original_text.size ());
tl::InputStream is (ms);
ie.read (path, is, expanded_text, ie, lc);
ie.read (path, is, expanded_text, lc, resolver);
return ie;
}
void
IncludeExpander::read (const std::string &path, tl::InputStream &is, std::string &expanded_text, IncludeExpander &ie, int &line_counter)
IncludeExpander::read (const std::string &path, tl::InputStream &is, std::string &expanded_text, int &line_counter, const IncludeFileResolver *resolver)
{
ie.m_sections [line_counter] = std::make_pair (path, 1 - line_counter);
m_sections [line_counter] = std::make_pair (path, 1 - line_counter);
tl::TextInputStream text (is);
@ -100,8 +103,17 @@ IncludeExpander::read (const std::string &path, tl::InputStream &is, std::string
include_path = current_uri.resolved (new_uri).to_abstract_path ();
}
tl::InputStream is (include_path);
read (include_path, is, expanded_text, ie, line_counter);
std::string include_text;
if (resolver) {
include_text = resolver->get_text (include_path);
} else {
tl::InputStream iis (include_path);
include_text = iis.read_all ();
}
tl::InputMemoryStream ms (include_text.c_str (), include_text.size ());
tl::InputStream is (ms);
read (include_path, is, expanded_text, line_counter, resolver);
emit_section = true;
@ -109,7 +121,7 @@ IncludeExpander::read (const std::string &path, tl::InputStream &is, std::string
if (emit_section) {
emit_section = false;
ie.m_sections [line_counter] = std::make_pair (path, lnum - line_counter);
m_sections [line_counter] = std::make_pair (path, lnum - line_counter);
}
expanded_text += l;

View File

@ -34,6 +34,21 @@ namespace tl
class InputStream;
/**
* @brief An interface providing the include file resolver
*
* The task of this object is to obtain the text for an include file path.
* The path already underwent variable interpolation and relative path resolution.
*/
class TL_PUBLIC IncludeFileResolver
{
public:
IncludeFileResolver () { }
virtual ~IncludeFileResolver () { }
virtual std::string get_text (const std::string &path) const = 0;
};
/**
* @brief Provide the basic include expansion and file/line mapping mechanism
*
@ -59,7 +74,7 @@ public:
*
* This method will deliver the expanded text and the include expander object.
*/
static IncludeExpander expand (const std::string &path, std::string &expanded_text);
static IncludeExpander expand (const std::string &path, std::string &expanded_text, const IncludeFileResolver *resolver = 0);
/**
* @brief Provides include expansion
@ -67,7 +82,7 @@ public:
* This method will deliver the expanded text and the include expander object.
* This version also takes the actual text of the original file.
*/
static IncludeExpander expand (const std::string &path, const std::string &original_text, std::string &expanded_text);
static IncludeExpander expand (const std::string &path, const std::string &original_text, std::string &expanded_text, const IncludeFileResolver *resolver = 0);
/**
* @brief Serializes the include expander information into a string
@ -98,7 +113,7 @@ public:
private:
std::map<int, std::pair<std::string, int> > m_sections;
void read (const std::string &path, tl::InputStream &is, std::string &expanded_text, IncludeExpander &ie, int &line_counter);
void read (const std::string &path, tl::InputStream &is, std::string &expanded_text, int &line_counter, const IncludeFileResolver *mp_resolver);
};
}

BIN
testdata/lefdef/issue-1432/au.oas vendored Normal file

Binary file not shown.

8
testdata/lefdef/issue-1432/test.def vendored Normal file
View File

@ -0,0 +1,8 @@
VERSION 5.8 ;
DESIGN test ;
UNITS DISTANCE MICRONS 1000 ;
DIEAREA ( -1 -1 ) ( 8 8 ) ;
NETS 1 ;
- dummy + ROUTED M1 ( 0 0 ) ( 5 0 ) VIRTUAL ( 7 1 ) RECT ( -3 0 -1 2 ) ( 7 7 ) ;
END NETS
END DESIGN

11
testdata/lefdef/issue-1432/test.lef vendored Normal file
View File

@ -0,0 +1,11 @@
UNITS
DATABASE MICRONS 1000 ;
END UNITS
MANUFACTURINGGRID 0.001 ;
LAYER M1
TYPE ROUTING ;
WIDTH 0.002 ;
END M1

2
testdata/lefdef/issue-1432/test.map vendored Normal file
View File

@ -0,0 +1,2 @@
DIEAREA ALL 108 0
M1 NET 31 0

23
testdata/lym/b_inc.lym vendored Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description/>
<version/>
<category/>
<prolog/>
<epilog/>
<doc/>
<autorun>false</autorun>
<autorun-early>false</autorun-early>
<priority>0</priority>
<shortcut/>
<show-in-menu>false</show-in-menu>
<group-name/>
<menu-path/>
<interpreter>ruby</interpreter>
<dsl-interpreter-name/>
<text>
def f
raise("An error")
end
</text>
</klayout-macro>

12
testdata/lym/m4.rb vendored Normal file
View File

@ -0,0 +1,12 @@
# %include b_inc.lym
begin
puts f
rescue => ex
ln = ex.backtrace[0].split(":")
# NOTE: as the backtrace is a native Ruby feature, include file translation
# does not happen. We need to do this explicitly here:
puts ex.to_s + " in " + RBA::Macro::real_path(ln[0], ln[1].to_i) + ":" + RBA::Macro::real_line(ln[0], ln[1].to_i).to_s
end

View File

@ -64,16 +64,38 @@ class DBLayoutTests1_TestClass < TestBase
assert_equal(l3.anonymous?, false)
assert_equal(l3.is_named?, true)
assert_equal(l3.is_equivalent?(l2), false)
l3.layer = 1
l3.datatype = 100
assert_equal(l3.is_named?, false)
assert_equal(l3.is_equivalent?(l2), true)
assert_equal(l2.is_equivalent?(l3), false)
l4 = RBA::LayerInfo::new(1, 100, "aber")
assert_equal(l4.to_s, "aber (1/100)")
assert_equal(l4.anonymous?, false)
assert_equal(l4.is_named?, false)
assert_equal(l4.is_equivalent?(l2), true)
assert_equal(l2.is_equivalent?(l4), true)
assert_equal(l4.is_equivalent?(l3), true)
assert_equal(l3.is_equivalent?(l4), true)
assert_equal(l4.is_equivalent?(RBA::LayerInfo::new(1, 100, "xyz")), true)
assert_equal(l4.is_equivalent?(RBA::LayerInfo::new(1, 101, "aber")), false)
l1.assign(l4)
l1.assign(l3)
assert_equal(l1.to_s, "aber (1/100)")
assert_equal(l1.is_named?, false)
assert_equal(l1.is_equivalent?(l3), true)
assert_equal(l1 == l3, true)
assert_equal(l1.is_equivalent?(l4), true)
assert_equal(l1 == l4, true)
l1.layer = -1
l1.datatype = -1
assert_equal(l1.is_named?, true)
assert_equal(l1.to_s, "aber")
l1.name = "xyz"
assert_equal(l1.is_named?, true)
assert_equal(l1.to_s, "xyz")
l1.layer = 100
l1.datatype = 0
assert_equal(l1.is_named?, false)
assert_equal(l1.to_s, "xyz (100/0)")
end
@ -164,16 +186,50 @@ class DBLayoutTests1_TestClass < TestBase
assert_equal(a, nil)
a = ly.find_layer(RBA::LayerInfo.new(2, 0))
assert_equal(a, li)
a = ly.find_layer(3, 0, "hallo")
assert_equal(a, nil)
li2 = ly.layer("hallo")
a = ly.find_layer("hillo")
assert_equal(a, nil)
a = ly.find_layer("hallo")
assert_equal(a, li2)
a = ly.find_layer(3, 0, "hallo")
assert_equal(a, nil)
assert_equal(a, li2)
a = ly.find_layer(2, 0, "hallo")
assert_equal(a, li)
ly = RBA::Layout.new
li = ly.layer(2, 0, "hallo")
a = ly.find_layer(3, 0)
assert_equal(a, nil)
a = ly.find_layer(2, 0)
assert_equal(a, li)
a = ly.find_layer("hallo")
assert_equal(a, li)
a = ly.find_layer(2, 0, "hillo")
assert_equal(a, li)
a = ly.find_layer(1, 0, "hallo")
assert_equal(a, nil)
ly = RBA::Layout.new
li0 = ly.layer("hello")
li = ly.layer("hallo")
a = ly.find_layer(3, 0)
assert_equal(a, nil)
a = ly.find_layer(2, 0)
assert_equal(a, nil)
a = ly.find_layer("hallo")
assert_equal(a, li)
a = ly.find_layer(2, 0, "hillo")
assert_equal(a, nil)
a = ly.find_layer(1, 0, "hallo")
assert_equal(a, li)
li2 = ly.layer(1, 0, "hello")
a = ly.find_layer(1, 0, "hello")
assert_equal(a, li2)
a = ly.find_layer("hello")
assert_equal(a, li0)
end
def collect(s, l)

View File

@ -415,6 +415,38 @@ class IMG_TestClass < TestBase
end
# Construction from PixelBuffer
def test_6
fn = ENV["TESTSRC"] + "/testdata/img/gs.png"
pb = RBA::PixelBuffer::read_png(fn)
image = RBA::Image.new(pb)
assert_equal(image.trans.to_s, "r0 *1 -513.5,-349")
assert_equal(image.width, 1027)
assert_equal(image.height, 698)
end
# Construction from QImage
def test_7
if RBA.constants.find { |x| x == :QImage }
fn = ENV["TESTSRC"] + "/testdata/img/gs.png"
qimage = RBA::QImage::new(fn)
image = RBA::Image.new(qimage)
assert_equal(image.trans.to_s, "r0 *1 -513.5,-349")
assert_equal(image.width, 1027)
assert_equal(image.height, 698)
end
end
end
load("test_epilogue.rb")