Merge branch 'master' into complex_drc_ops

This commit is contained in:
Matthias Koefferlein
2020-11-22 09:31:15 +01:00
266 changed files with 20889 additions and 8198 deletions
@@ -61,7 +61,7 @@ GDS2ReaderText::read (db::Layout &layout, const db::LoadLayoutOptions &options)
db::GDS2ReaderOptions gds2_options = options.get_options<db::GDS2ReaderOptions> ();
db::CommonReaderOptions common_options = options.get_options<db::CommonReaderOptions> ();
return basic_read (layout, common_options.layer_map, common_options.create_other_layers, common_options.enable_text_objects, common_options.enable_properties, false, gds2_options.box_mode);
return basic_read (layout, common_options.layer_map, common_options.create_other_layers, common_options.enable_text_objects, common_options.enable_properties, false, gds2_options.box_mode, common_options.cell_conflict_resolution);
}
const LayerMap &
@@ -259,7 +259,7 @@ GDS2ReaderText::get_double ()
}
void
GDS2ReaderText::get_string (tl::string &s) const
GDS2ReaderText::get_string (std::string &s) const
{
// TODO: get rid of this const_cast hack
s = (const_cast<GDS2ReaderText *> (this))->reader.skip ();
@@ -112,7 +112,7 @@ private:
virtual std::string path () const;
const char *get_string ();
void get_string (tl::string &s) const;
void get_string (std::string &s) const;
int get_int ();
short get_short ();
unsigned short get_ushort ();
@@ -64,7 +64,7 @@ GDS2Reader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
--m_recnum;
m_reclen = 0;
return basic_read (layout, m_common_options.layer_map, m_common_options.create_other_layers, m_common_options.enable_text_objects, m_common_options.enable_properties, m_options.allow_multi_xy_records, m_options.box_mode);
return basic_read (layout, m_common_options.layer_map, m_common_options.create_other_layers, m_common_options.enable_text_objects, m_common_options.enable_properties, m_options.allow_multi_xy_records, m_options.box_mode, m_common_options.cell_conflict_resolution);
}
const LayerMap &
@@ -210,7 +210,7 @@ GDS2Reader::get_string ()
}
void
GDS2Reader::get_string (tl::string &s) const
GDS2Reader::get_string (std::string &s) const
{
s.assign ((const char *) mp_rec_buf, 0, m_reclen);
}
@@ -126,7 +126,7 @@ private:
virtual std::string path () const;
virtual const char *get_string ();
virtual void get_string (tl::string &s) const;
virtual void get_string (std::string &s) const;
virtual int get_int ();
virtual short get_short ();
virtual unsigned short get_ushort ();
@@ -84,7 +84,7 @@ GDS2ReaderBase::~GDS2ReaderBase ()
}
const LayerMap &
GDS2ReaderBase::basic_read (db::Layout &layout, const LayerMap &layer_map, bool create_other_layers, bool enable_text_objects, bool enable_properties, bool allow_multi_xy_records, unsigned int box_mode)
GDS2ReaderBase::basic_read (db::Layout &layout, const LayerMap &layer_map, bool create_other_layers, bool enable_text_objects, bool enable_properties, bool allow_multi_xy_records, unsigned int box_mode, db::CommonReader::CellConflictResolution cc_resolution)
{
m_layer_map = layer_map;
m_layer_map.prepare (layout);
@@ -95,9 +95,17 @@ GDS2ReaderBase::basic_read (db::Layout &layout, const LayerMap &layer_map, bool
m_box_mode = box_mode;
m_create_layers = create_other_layers;
set_cell_conflict_resolution (cc_resolution);
layout.start_changes ();
do_read (layout);
layout.end_changes ();
try {
do_read (layout);
finish (layout);
layout.end_changes ();
} catch (...) {
layout.end_changes ();
throw;
}
return m_layer_map;
}
@@ -235,7 +243,6 @@ GDS2ReaderBase::do_read (db::Layout &layout)
{
m_cellname = "";
m_libname = "";
m_mapped_cellnames.clear ();
// read header
if (get_record () != sHEADER) {
@@ -349,7 +356,7 @@ GDS2ReaderBase::do_read (db::Layout &layout)
} else {
db::cell_index_type cell_index = make_cell (layout, m_cellname.c_str (), false);
db::cell_index_type cell_index = make_cell (layout, m_cellname);
db::Cell *cell = &layout.cell (cell_index);
@@ -359,8 +366,6 @@ GDS2ReaderBase::do_read (db::Layout &layout)
if (layout.recover_proxy_as (cell_index, ctx->second.begin (), ctx->second.end (), &layer_mapping)) {
// ignore everything in that cell since it is created by the import:
cell = 0;
// marks the cell for begin addressed by REF's despite being a proxy:
m_mapped_cellnames.insert (std::make_pair (m_cellname, m_cellname));
}
}
@@ -973,54 +978,6 @@ GDS2ReaderBase::read_box (db::Layout &layout, db::Cell &cell)
}
}
db::cell_index_type
GDS2ReaderBase::make_cell (db::Layout &layout, const char *cn, bool for_instance)
{
db::cell_index_type ci = 0;
// map to the real name which maybe a different one due to localization
// of proxy cells (they are not to be reopened)
bool is_mapped = false;
if (! m_mapped_cellnames.empty ()) {
std::map<tl::string, tl::string>::const_iterator n = m_mapped_cellnames.find (cn);
if (n != m_mapped_cellnames.end ()) {
cn = n->second.c_str ();
is_mapped = true;
}
}
std::pair<bool, db::cell_index_type> c = layout.cell_by_name (cn);
if (c.first && (is_mapped || ! layout.cell (c.second).is_proxy ())) {
// cell already there: just add instance (cell might have been created through forward reference)
// NOTE: we don't address "reopened" proxies as proxies are always local to a layout
ci = c.second;
// mark the cell as read
if (! for_instance) {
layout.cell (ci).set_ghost_cell (false);
}
} else {
ci = layout.add_cell (cn);
if (for_instance) {
// mark this cell a "ghost cell" until it's actually read
layout.cell (ci).set_ghost_cell (true);
}
if (c.first) {
// this cell has been given a new name: remember this name for localization
m_mapped_cellnames.insert (std::make_pair (cn, layout.cell_name (ci)));
}
}
return ci;
}
void
GDS2ReaderBase::read_ref (db::Layout &layout, db::Cell & /*cell*/, bool array, tl::vector<db::CellInstArray> &instances, tl::vector<db::CellInstArrayWithProperties> &instances_with_props)
{
@@ -1033,7 +990,7 @@ GDS2ReaderBase::read_ref (db::Layout &layout, db::Cell & /*cell*/, bool array, t
error (tl::to_string (tr ("SNAME record expected")));
}
db::cell_index_type ci = make_cell (layout, get_string (), true);
db::cell_index_type ci = cell_for_instance (layout, get_string ());
bool mirror = false;
int angle = 0;
@@ -29,6 +29,7 @@
#include "dbLayout.h"
#include "dbReader.h"
#include "dbStreamLayers.h"
#include "dbCommonReader.h"
#include "tlException.h"
#include "tlInternational.h"
@@ -49,7 +50,7 @@ struct GDS2XY
* @brief The GDS2 format basic stream reader
*/
class DB_PLUGIN_PUBLIC GDS2ReaderBase
: public ReaderBase
: public CommonReader
{
public:
/**
@@ -86,20 +87,21 @@ protected:
* @param enable_properties A flag indicating whether to read user properties
* @param allow_multi_xy_records If true, tries to check for multiple XY records for BOUNDARY elements
* @param box_mode How to treat BOX records (0: ignore, 1: as rectangles, 2: as boundaries, 3: error)
* @param cc_resolution The cell name conflict resolution mode
* @return The LayerMap object that tells where which layer was loaded
*/
const LayerMap &basic_read (db::Layout &layout, const LayerMap &layer_map, bool create_other_layers, bool enable_text_objects, bool enable_properties, bool allow_multi_xy_records, unsigned int box_mode);
const LayerMap &basic_read (db::Layout &layout, const LayerMap &layer_map, bool create_other_layers, bool enable_text_objects, bool enable_properties, bool allow_multi_xy_records, unsigned int box_mode, db::CommonReader::CellConflictResolution cc_resolution);
/**
* @brief Accessor method to the current cellname
*/
const tl::string &cellname () const { return m_cellname; }
const std::string &cellname () const { return m_cellname; }
private:
friend class GDS2ReaderLayerMapping;
LayerMap m_layer_map;
tl::string m_cellname;
std::string m_cellname;
std::string m_libname;
double m_dbu, m_dbuu;
bool m_create_layers;
@@ -109,7 +111,6 @@ private:
unsigned int m_box_mode;
std::map <tl::string, std::vector<std::string> > m_context_info;
std::vector <db::Point> m_all_points;
std::map <tl::string, tl::string> m_mapped_cellnames;
void read_context_info_cell ();
void read_boundary (db::Layout &layout, db::Cell &cell, bool from_box_record);
@@ -117,7 +118,6 @@ private:
void read_text (db::Layout &layout, db::Cell &cell);
void read_box (db::Layout &layout, db::Cell &cell);
void read_ref (db::Layout &layout, db::Cell &cell, bool array, tl::vector<db::CellInstArray> &instances, tl::vector<db::CellInstArrayWithProperties> &insts_wp);
db::cell_index_type make_cell (db::Layout &layout, const char *cn, bool for_instance);
void do_read (db::Layout &layout);
@@ -125,12 +125,15 @@ private:
std::pair <bool, db::properties_id_type> finish_element (db::PropertiesRepository &rep);
void finish_element ();
virtual void common_reader_error (const std::string &msg) { error (msg); }
virtual void common_reader_warn (const std::string &msg) { warn (msg); }
virtual void error (const std::string &txt) = 0;
virtual void warn (const std::string &txt) = 0;
virtual std::string path () const = 0;
virtual const char *get_string () = 0;
virtual void get_string (tl::string &s) const = 0;
virtual void get_string (std::string &s) const = 0;
virtual int get_int () = 0;
virtual short get_short () = 0;
virtual unsigned short get_ushort () = 0;
@@ -366,6 +366,8 @@ TEST(Bug_121_1)
reader.read (layout);
}
fflush(stdout);
std::string fn_au (tl::testsrc () + "/testdata/gds/bug_121_au1.gds");
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
}
@@ -434,3 +436,100 @@ TEST(3_AdvancedMapping)
std::string fn_au (tl::testsrc () + "/testdata/gds/alm_au.gds");
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
}
TEST(4_CollectModeRename)
{
db::Manager m (false);
db::Layout layout (&m);
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().cell_conflict_resolution = db::CommonReader::RenameCell;
{
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds");
db::Reader reader (file);
reader.read (layout, options);
}
{
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds");
db::Reader reader (file);
reader.read (layout, options);
}
std::string fn_au (tl::testsrc () + "/testdata/gds/collect_rename_au.gds");
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
}
TEST(4_CollectModeOverwrite)
{
db::Manager m (false);
db::Layout layout (&m);
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().cell_conflict_resolution = db::CommonReader::OverwriteCell;
{
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds");
db::Reader reader (file);
reader.read (layout, options);
}
{
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds");
db::Reader reader (file);
reader.read (layout, options);
}
std::string fn_au (tl::testsrc () + "/testdata/gds/collect_overwrite_au.gds");
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
}
TEST(4_CollectModeSkip)
{
db::Manager m (false);
db::Layout layout (&m);
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().cell_conflict_resolution = db::CommonReader::SkipNewCell;
{
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds");
db::Reader reader (file);
reader.read (layout, options);
}
{
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds");
db::Reader reader (file);
reader.read (layout, options);
}
std::string fn_au (tl::testsrc () + "/testdata/gds/collect_skip_au.gds");
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
}
TEST(4_CollectModeAdd)
{
db::Manager m (false);
db::Layout layout (&m);
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().cell_conflict_resolution = db::CommonReader::AddToCell;
{
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds");
db::Reader reader (file);
reader.read (layout, options);
}
{
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds");
db::Reader reader (file);
reader.read (layout, options);
}
std::string fn_au (tl::testsrc () + "/testdata/gds/collect_add_au.gds");
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
}
@@ -340,26 +340,6 @@ END_PROTECTED
// -----------------------------------------------------------------------------------------------
// LEFDEF technology components editor
static void
indicate_error (QWidget *le, const tl::Exception *ex)
{
// by the way, update the foreground color of the cell edit box as well (red, if not valid)
QPalette pl = le->palette ();
if (ex) {
pl.setColor (QPalette::Active, QPalette::Text, Qt::red);
pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180));
le->setToolTip (tl::to_qstring (ex->msg ()));
} else {
QWidget *pw = dynamic_cast<QWidget *> (le->parent ());
tl_assert (pw != 0);
pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text));
pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base));
le->setToolTip (QString ());
}
le->setPalette (pl);
}
LEFDEFReaderOptionsEditor::LEFDEFReaderOptionsEditor (QWidget *parent)
: lay::StreamReaderOptionsPage (parent), mp_tech (0)
{
@@ -144,9 +144,12 @@ OASISReader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
m_read_all_properties = oasis_options.read_all_properties;
m_expect_strict_mode = oasis_options.expect_strict_mode;
set_cell_conflict_resolution (common_options.cell_conflict_resolution);
layout.start_changes ();
try {
do_read (layout);
finish (layout);
layout.end_changes ();
} catch (...) {
layout.end_changes ();
@@ -758,17 +761,11 @@ OASISReader::do_read (db::Layout &layout)
id_mode propstring_id_mode = any;
id_mode propname_id_mode = any;
m_cellnames.clear ();
m_cellname_properties.clear ();
m_textstrings.clear ();
m_propstrings.clear ();
m_propnames.clear ();
m_layernames.clear ();
m_cells_by_id.clear ();
m_cells_by_name.clear ();
m_defined_cells_by_id.clear ();
m_defined_cells_by_name.clear ();
m_mapped_cellnames.clear ();
m_instances.clear ();
m_instances_with_props.clear ();
@@ -826,9 +823,7 @@ OASISReader::do_read (db::Layout &layout)
get (id);
}
if (! m_cellnames.insert (std::make_pair (id, name)).second) {
error (tl::sprintf (tl::to_string (tr ("A CELLNAME with id %ld is present already")), id));
}
rename_cell (layout, id, name);
reset_modal_variables ();
@@ -1150,36 +1145,17 @@ OASISReader::do_read (db::Layout &layout)
unsigned long id = 0;
get (id);
if (! m_defined_cells_by_id.insert (id).second) {
std::pair<bool, db::cell_index_type> cc = cell_by_id (id);
if (cc.first && ! layout.cell (cc.second).is_ghost_cell ()) {
error (tl::sprintf (tl::to_string (tr ("A cell with id %ld is defined already")), id));
}
std::map <unsigned long, db::cell_index_type>::const_iterator c = m_cells_by_id.find (id);
if (c != m_cells_by_id.end ()) {
cell_index = c->second;
layout.cell (cell_index).set_ghost_cell (false);
} else {
std::map <unsigned long, std::string>::const_iterator name = m_cellnames.find (id);
if (name == m_cellnames.end ()) {
cell_index = layout.add_cell ();
// force a cell rename to empty to avoid name clashes of the generated
// $x names with the same inside the OASIS file.
layout.rename_cell (cell_index, "");
m_forward_references.insert (std::make_pair (id, cell_index));
} else {
cell_index = make_cell (layout, name->second.c_str (), false);
m_cells_by_name.insert (std::make_pair (name->second, cell_index));
}
m_cells_by_id.insert (std::make_pair (id, cell_index));
cell_index = make_cell (layout, id);
m_cellname = name_for_id (id);
if (m_cellname.empty ()) {
m_cellname = std::string ("#") + tl::to_string (id);
}
} else {
@@ -1189,22 +1165,15 @@ OASISReader::do_read (db::Layout &layout)
}
std::string name = get_str ();
if (! m_defined_cells_by_name.insert (name).second) {
std::pair<bool, db::cell_index_type> cc = cell_by_name (name);
if (cc.first && ! layout.cell (cc.second).is_ghost_cell ()) {
error (tl::sprintf (tl::to_string (tr ("A cell with name %s is defined already")), name.c_str ()));
}
std::map <std::string, db::cell_index_type>::const_iterator c = m_cells_by_name.find (name);
if (c != m_cells_by_name.end ()) {
cell_index = make_cell (layout, name);
cell_index = c->second;
layout.cell (cell_index).set_ghost_cell (false);
} else {
cell_index = make_cell (layout, name.c_str (), false);
m_cells_by_name.insert (std::make_pair (name, cell_index));
}
m_cellname = name;
}
@@ -1326,72 +1295,16 @@ OASISReader::do_read (db::Layout &layout)
}
for (std::map <unsigned long, db::cell_index_type>::const_iterator fw = m_forward_references.begin (); fw != m_forward_references.end (); ++fw) {
std::map <unsigned long, std::string>::const_iterator cn = m_cellnames.find (fw->first);
if (cn == m_cellnames.end ()) {
error (tl::sprintf (tl::to_string (tr ("No cellname defined for cell name id %ld")), fw->first));
} else {
std::pair<bool, db::cell_index_type> c = layout.cell_by_name (cn->second.c_str ());
if (c.first) {
// needed, since we have disabled updates
layout.force_update ();
// add-on reading of forward-referenced cell: need to copy the new cell to the original one plus
// change instances of the new cell and delete the new cell then.
const db::Cell &new_cell = layout.cell (fw->second);
db::Cell &org_cell = layout.cell (c.second);
// copy over the instances
for (db::Cell::const_iterator i = new_cell.begin (); ! i.at_end (); ++i) {
org_cell.insert (*i);
}
// copy over the shapes
for (unsigned int l = 0; l < layout.layers (); ++l) {
if (layout.is_valid_layer (l) && ! new_cell.shapes (l).empty ()) {
org_cell.shapes (l).insert (new_cell.shapes (l));
}
}
// replace all instances of the new cell with the original one
std::vector<std::pair<db::cell_index_type, db::Instance> > parents;
for (db::Cell::parent_inst_iterator pi = new_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ()));
}
for (std::vector<std::pair<db::cell_index_type, db::Instance> >::const_iterator p = parents.begin (); p != parents.end (); ++p) {
db::CellInstArray ia = p->second.cell_inst ();
ia.object ().cell_index (org_cell.cell_index ());
layout.cell (p->first).replace (p->second, ia);
}
// finally delete the new cell
layout.delete_cell (new_cell.cell_index ());
} else {
layout.rename_cell (fw->second, cn->second.c_str ());
}
}
}
// attach the properties found in CELLNAME to the cells (which may have other properties)
for (std::map<unsigned long, db::properties_id_type>::const_iterator p = m_cellname_properties.begin (); p != m_cellname_properties.end (); ++p) {
std::map <unsigned long, db::cell_index_type>::const_iterator c = m_cells_by_id.find (p->first);
if (c != m_cells_by_id.end ()) {
std::pair<bool, db::cell_index_type> c = cell_by_id (p->first);
if (c.first) {
db::PropertiesRepository::properties_set cnp = layout.properties_repository ().properties (p->second);
// Merge existing properties with the ones from CELLNAME
db::Cell &cell = layout.cell (c->second);
db::Cell &cell = layout.cell (c.second);
if (cell.prop_id () != 0) {
db::PropertiesRepository::properties_set cp = layout.properties_repository ().properties (cell.prop_id ());
cnp.insert (cp.begin (), cp.end ());
@@ -1844,54 +1757,6 @@ OASISReader::read_repetition ()
return mm_repetition.get ().size () > 1;
}
db::cell_index_type
OASISReader::make_cell (db::Layout &layout, const char *cn, bool for_instance)
{
db::cell_index_type ci = 0;
// map to the real name which maybe a different one due to localization
// of proxy cells (they are not to be reopened)
bool is_mapped = false;
if (! m_mapped_cellnames.empty ()) {
std::map<tl::string, tl::string>::const_iterator n = m_mapped_cellnames.find (cn);
if (n != m_mapped_cellnames.end ()) {
cn = n->second.c_str ();
is_mapped = true;
}
}
std::pair<bool, db::cell_index_type> c = layout.cell_by_name (cn);
if (c.first && (is_mapped || ! layout.cell (c.second).is_proxy ())) {
// cell already there: just add instance (cell might have been created through forward reference)
// NOTE: we don't address "reopened" proxies as proxies are always local to a layout
ci = c.second;
// mark the cell as read
if (! for_instance) {
layout.cell (ci).set_ghost_cell (false);
}
} else {
ci = layout.add_cell (cn);
if (for_instance) {
// mark this cell a "ghost cell" until it's actually read
layout.cell (ci).set_ghost_cell (true);
}
if (c.first) {
// this cell has been given a new name: remember this name for localization
m_mapped_cellnames.insert (std::make_pair (cn, layout.cell_name (ci)));
}
}
return ci;
}
void
OASISReader::do_read_placement (unsigned char r,
bool xy_absolute,
@@ -1909,31 +1774,8 @@ OASISReader::do_read_placement (unsigned char r,
// cell by id
unsigned long id;
get (id);
std::map <unsigned long, db::cell_index_type>::const_iterator cid = m_cells_by_id.find (id);
if (cid == m_cells_by_id.end ()) {
// create the cell
std::map <unsigned long, std::string>::const_iterator name = m_cellnames.find (id);
if (name == m_cellnames.end ()) {
mm_placement_cell = layout.add_cell ();
m_forward_references.insert (std::make_pair (id, mm_placement_cell.get ()));
// temporarily mark as "ghost cell"
layout.cell (mm_placement_cell.get ()).set_ghost_cell (true);
} else {
mm_placement_cell = make_cell (layout, name->second.c_str (), true);
m_cells_by_name.insert (std::make_pair (name->second, mm_placement_cell.get ()));
}
m_cells_by_id.insert (std::make_pair (id, mm_placement_cell.get ()));
} else {
mm_placement_cell = cid->second;
}
mm_placement_cell = cell_for_instance (layout, id);
} else {
@@ -1941,15 +1783,7 @@ OASISReader::do_read_placement (unsigned char r,
std::string name;
get_str (name);
std::map <std::string, db::cell_index_type>::const_iterator cid = m_cells_by_name.find (name);
if (cid == m_cells_by_name.end ()) {
mm_placement_cell = make_cell (layout, name.c_str (), true);
m_cells_by_name.insert (std::make_pair (name, mm_placement_cell.get ()));
} else {
mm_placement_cell = cid->second;
}
mm_placement_cell = cell_for_instance (layout, name);
}
@@ -3433,7 +3267,6 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout)
m_instances_with_props.clear ();
m_progress.set (m_stream.pos ());
m_cellname = layout.cell_name (cell_index);
bool xy_absolute = true;
@@ -33,6 +33,7 @@
#include "dbOASISFormat.h"
#include "dbStreamLayers.h"
#include "dbPropertiesRepository.h"
#include "dbCommonReader.h"
#include "tlException.h"
#include "tlInternational.h"
@@ -62,7 +63,7 @@ public:
* @brief The OASIS format stream reader
*/
class DB_PLUGIN_PUBLIC OASISReader
: public ReaderBase,
: public CommonReader,
public OASISDiagnostics
{
public:
@@ -118,6 +119,7 @@ public:
*/
virtual const char *format () const { return "OASIS"; }
protected:
/**
* @brief Issue an error with positional information
*
@@ -132,6 +134,9 @@ public:
*/
virtual void warn (const std::string &txt);
virtual void common_reader_error (const std::string &msg) { error (msg); }
virtual void common_reader_warn (const std::string &msg) { warn (msg); }
private:
friend class OASISReaderLayerMapping;
@@ -194,7 +199,6 @@ private:
modal_variable<bool> mm_last_property_is_sprop;
modal_variable<property_value_list> mm_last_value_list;
std::map <unsigned long, std::string> m_cellnames;
std::map <unsigned long, db::properties_id_type> m_cellname_properties;
std::map <unsigned long, std::string> m_textstrings;
std::map <unsigned long, const db::StringRef *> m_text_forward_references;
@@ -205,18 +209,11 @@ private:
tl::vector<db::CellInstArray> m_instances;
tl::vector<db::CellInstArrayWithProperties> m_instances_with_props;
std::map <unsigned long, db::cell_index_type> m_cells_by_id;
std::map <unsigned long, db::cell_index_type> m_forward_references;
std::map <std::string, db::cell_index_type> m_cells_by_name;
bool m_create_layers;
bool m_read_texts;
bool m_read_properties;
bool m_read_all_properties;
std::set <unsigned long> m_defined_cells_by_id;
std::set <std::string> m_defined_cells_by_name;
std::map <tl::string, tl::string> m_mapped_cellnames;
std::map <unsigned long, db::property_names_id_type> m_propname_forward_references;
std::map <unsigned long, std::string> m_propvalue_forward_references;
db::property_names_id_type m_s_gds_property_name_id;
@@ -238,7 +235,6 @@ private:
void do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout);
void do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout);
void do_read_circle (bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout);
db::cell_index_type make_cell (db::Layout &layout, const char *cn, bool for_instance);
void reset_modal_variables ();
@@ -1150,6 +1150,18 @@ OASISWriter::reset_modal_variables ()
mm_last_value_list.reset ();
}
static bool must_write_cell (const db::Cell &cref)
{
// Don't write proxy cells which are not employed
return ! cref.is_proxy () || ! cref.is_top ();
}
static bool skip_cell_body (const db::Cell &cref)
{
// Skip cell bodies for ghost cells unless empty (they are not longer ghost cells in this case)
return cref.is_ghost_cell () && cref.empty ();
}
void
OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
{
@@ -1184,13 +1196,13 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
cells_by_index.reserve (cell_set.size ());
for (db::Layout::bottom_up_const_iterator cell = layout.begin_bottom_up (); cell != layout.end_bottom_up (); ++cell) {
if (cell_set.find (*cell) != cell_set.end ()) {
if (cell_set.find (*cell) != cell_set.end () && must_write_cell (layout.cell (*cell))) {
cells.push_back (*cell);
}
}
for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) {
if (cell_set.find (cell->cell_index ()) != cell_set.end ()) {
if (cell_set.find (cell->cell_index ()) != cell_set.end () && must_write_cell (layout.cell (cell->cell_index ()))) {
cells_by_index.push_back (cell->cell_index ());
}
}
@@ -1594,80 +1606,79 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
const db::Cell &cref (layout.cell (*cell));
mp_cell = &cref;
// don't write ghost cells unless they are not empty (any more)
// also don't write proxy cells which are not employed
if ((! cref.is_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) {
// skip cell body if the cell is not to be written
if (skip_cell_body (cref)) {
continue;
}
// cell header
// cell header
cell_positions.insert (std::make_pair (*cell, mp_stream->pos ()));
cell_positions.insert (std::make_pair (*cell, mp_stream->pos ()));
write_record_id (13); // CELL
write ((unsigned long) *cell);
write_record_id (13); // CELL
write ((unsigned long) *cell);
reset_modal_variables ();
reset_modal_variables ();
if (m_options.write_cblocks) {
begin_cblock ();
}
if (m_options.write_cblocks) {
begin_cblock ();
}
// context information as property named KLAYOUT_CONTEXT
if (cref.is_proxy () && options.write_context_info ()) {
// context information as property named KLAYOUT_CONTEXT
if (cref.is_proxy () && options.write_context_info ()) {
context_prop_strings.clear ();
context_prop_strings.clear ();
if (layout.get_context_info (*cell, context_prop_strings)) {
if (layout.get_context_info (*cell, context_prop_strings)) {
write_record_id (28);
write_byte (char (0xf6));
std::map <std::string, unsigned long>::const_iterator pni = m_propnames.find (klayout_context_name);
tl_assert (pni != m_propnames.end ());
write (pni->second);
write_record_id (28);
write_byte (char (0xf6));
std::map <std::string, unsigned long>::const_iterator pni = m_propnames.find (klayout_context_name);
tl_assert (pni != m_propnames.end ());
write (pni->second);
write ((unsigned long) context_prop_strings.size ());
for (std::vector <std::string>::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
write_byte (14); // b-string by reference number
std::map <std::string, unsigned long>::const_iterator psi = m_propstrings.find (*c);
tl_assert (psi != m_propstrings.end ());
write (psi->second);
}
mm_last_property_name = klayout_context_name;
mm_last_property_is_sprop = false;
mm_last_value_list.reset ();
write ((unsigned long) context_prop_strings.size ());
for (std::vector <std::string>::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
write_byte (14); // b-string by reference number
std::map <std::string, unsigned long>::const_iterator psi = m_propstrings.find (*c);
tl_assert (psi != m_propstrings.end ());
write (psi->second);
}
mm_last_property_name = klayout_context_name;
mm_last_property_is_sprop = false;
mm_last_value_list.reset ();
}
if (cref.prop_id () != 0) {
write_props (cref.prop_id ());
}
// instances
if (cref.cell_instances () > 0) {
write_insts (cell_set);
}
// shapes
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
const db::Shapes &shapes = cref.shapes (l->first);
if (! shapes.empty ()) {
write_shapes (l->second, shapes);
m_progress.set (mp_stream->pos ());
}
}
// end CBLOCK if required
if (m_options.write_cblocks) {
end_cblock ();
}
// end of cell
}
if (cref.prop_id () != 0) {
write_props (cref.prop_id ());
}
// instances
if (cref.cell_instances () > 0) {
write_insts (cell_set);
}
// shapes
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
const db::Shapes &shapes = cref.shapes (l->first);
if (! shapes.empty ()) {
write_shapes (l->second, shapes);
m_progress.set (mp_stream->pos ());
}
}
// end CBLOCK if required
if (m_options.write_cblocks) {
end_cblock ();
}
// end of cell
}
// write cell table at the end in strict mode (in that mode we need the cell positions
@@ -570,11 +570,11 @@ TEST(100)
const char *expected =
"begin_lib 0.001\n"
"begin_cell {$4}\n"
"end_cell\n"
"begin_cell {$1}\n"
"box 1 0 {0 100} {1000 1200}\n"
"end_cell\n"
"begin_cell {$4}\n"
"end_cell\n"
"begin_cell {$3}\n"
"sref {$1} 90 1 1 {-10 20}\n"
"sref {$4} 90 1 1 {-10 20}\n"
@@ -1327,14 +1327,14 @@ TEST(116)
" {{name} {117}}\n"
"}\n"
"begin_libp $props 0.001\n"
"begin_cell {$2}\n"
"end_cell\n"
"set props {\n"
" {42 {42}}\n"
"}\n"
"begin_cellp $props {$1}\n"
"path 1 0 0 0 0 {0 100} {1000 1200}\n"
"end_cell\n"
"begin_cell {$2}\n"
"end_cell\n"
"end_lib\n"
;
@@ -1376,14 +1376,14 @@ TEST(116)
" {{name} {117}}\n"
"}\n"
"begin_libp $props 0.001\n"
"begin_cell {$2}\n"
"end_cell\n"
"set props {\n"
" {42 {42}}\n"
"}\n"
"begin_cellp $props {$1}\n"
"path 1 0 0 0 0 {0 100} {1000 1200}\n"
"end_cell\n"
"begin_cell {$2}\n"
"end_cell\n"
"end_lib\n"
;
@@ -1431,17 +1431,17 @@ TEST(116)
"}\n"
"begin_libp $props 0.001\n"
"set props {\n"
" {{S_BOUNDING_BOX} {2,0,0,0,0}}\n"
"}\n"
"begin_cellp $props {$2}\n"
"end_cell\n"
"set props {\n"
" {{S_BOUNDING_BOX} {0,0,100,1000,1100}}\n"
" {42 {42}}\n"
"}\n"
"begin_cellp $props {$1}\n"
"path 1 0 0 0 0 {0 100} {1000 1200}\n"
"end_cell\n"
"set props {\n"
" {{S_BOUNDING_BOX} {2,0,0,0,0}}\n"
"}\n"
"begin_cellp $props {$2}\n"
"end_cell\n"
"end_lib\n"
;
@@ -0,0 +1,325 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>D25View</class>
<widget class="QDialog" name="D25View">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>854</width>
<height>665</height>
</rect>
</property>
<property name="windowTitle">
<string>2.5d View</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<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="spacing">
<number>6</number>
</property>
<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="QSlider" name="zoom_slider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>16777215</height>
</size>
</property>
<property name="minimum">
<number>-300</number>
</property>
<property name="maximum">
<number>300</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="zoom_factor">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>%</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="fit_left">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_left.png</normaloff>:/fit_left.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fit_front">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_front.png</normaloff>:/fit_front.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fit_right">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_right.png</normaloff>:/fit_right.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fit_back">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_back.png</normaloff>:/fit_back.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fit_top">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_top.png</normaloff>:/fit_top.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fit_bottom">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_bottom.png</normaloff>:/fit_bottom.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="lay::D25ViewWidget" name="d25_view"/>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<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">
<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">
<property name="text">
<string>Press and hold SHIFT for top view</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>lay::D25ViewWidget</class>
<extends>QOpenGLWidget</extends>
<header>layD25ViewWidget.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../../../lay/lay/layResources.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>D25View</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>530</x>
<y>626</y>
</hint>
<hint type="destinationlabel">
<x>443</x>
<y>643</y>
</hint>
</hints>
</connection>
</connections>
</ui>
@@ -0,0 +1,113 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "layD25Camera.h"
#include <QWidget>
#include "math.h"
namespace lay
{
D25Camera::D25Camera ()
{
init ();
}
D25Camera::~D25Camera ()
{
// .. nothing yet ..
}
void
D25Camera::init ()
{
m_fov = 90.0;
m_cam_azimuth = m_cam_elevation = 0.0;
m_top_view = false;
}
void
D25Camera::camera_reset ()
{
init ();
camera_changed ();
}
double
D25Camera::cam_fov () const
{
return m_fov;
}
double
D25Camera::cam_dist () const
{
return 4.0;
}
QVector3D
D25Camera::cam_direction () const
{
return cam_trans ().inverted ().map (QVector3D (0, 0, -1));
}
QVector3D
D25Camera::cam_position () const
{
return cam_direction () * -cam_dist ();
}
double
D25Camera::cam_azimuth () const
{
return m_cam_azimuth;
}
double
D25Camera::cam_elevation () const
{
return m_top_view ? -90.0 : m_cam_elevation;
}
QMatrix4x4
D25Camera::cam_perspective () const
{
QMatrix4x4 t;
t.perspective (cam_fov (), aspect_ratio (), 0.1f, 10000.0f);
t.translate (QVector3D (0.0, 0.0, -cam_dist ()));
return t;
}
QMatrix4x4
D25Camera::cam_trans () const
{
QMatrix4x4 t;
t.rotate (-cam_elevation (), 1.0, 0.0, 0.0);
t.rotate (cam_azimuth (), 0.0, 1.0, 0.0);
return t;
}
}
@@ -0,0 +1,142 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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
*/
#ifndef HDR_layD25Camera
#define HDR_layD25Camera
#include "layPluginCommon.h"
#include <QMatrix4x4>
#include <QVector3D>
namespace lay
{
class LAY_PLUGIN_PUBLIC D25Camera
{
public:
D25Camera ();
virtual ~D25Camera ();
/**
* @brief Gets the position of the camera objective in the scene coordinate system
*/
QVector3D cam_position () const;
/**
* @brief Gets the direction the camera looks into in the scene coordinate system
*/
QVector3D cam_direction () const;
/**
* @brief Gets the perspective part of the transformation applied transform scene coordinates into the image plane
* The full transformation for scene to image plane is cam_perspective * cam_trans.
*/
QMatrix4x4 cam_perspective () const;
/**
* @brief Gets the azimuth/elevation part of the transformation applied transform scene coordinates into the image plane
* The full transformation for scene to image plane is cam_perspective * cam_trans.
*/
QMatrix4x4 cam_trans () const;
/**
* @brief Gets the distance of the objective in scene coordinates
*/
double cam_dist () const;
/**
* @brief Gets the field of view of the camera
* The field of view is the objective opening angle.
*/
double cam_fov () const;
/**
* @brief Gets a flag indicating whether top view is enabled
* In "top view" mode, the elevation is fixed to -90 degree.
*/
bool top_view () const
{
return m_top_view;
}
/**
* @brief Sets a flag indicating whether top view is enabled
*/
void set_top_view (bool f)
{
m_top_view = f;
camera_changed ();
}
/**
* @brief Gets the elevation angle
* A negative angle means the camera looks down, a positive angle means it looks up.
*/
double cam_elevation () const;
/**
* @brief Sets the elevation angle
*/
void set_cam_elevation (double e)
{
m_cam_elevation = e;
camera_changed ();
}
/**
* @brief Gets the azimuth angle
* A positive angle means we look from the left. A negative means we look from the right.
*/
double cam_azimuth () const;
/**
* @brief Sets the azimuth angle
*/
void set_cam_azimuth (double a)
{
m_cam_azimuth = a;
camera_changed ();
}
/**
* @brief Resets the camera's orientation
*/
void camera_reset ();
protected:
virtual void camera_changed () { }
virtual double aspect_ratio () const { return 1.0; }
private:
double m_cam_azimuth, m_cam_elevation;
bool m_top_view;
QVector3D m_displacement;
double m_fov;
void init ();
};
}
#endif
@@ -0,0 +1,24 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "layD25MemChunks.h"
@@ -0,0 +1,266 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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
*/
#ifndef HDR_layD25MemChunks
#define HDR_layD25MemChunks
#include <QDialog>
#include <QOpenGLFunctions>
#include "tlObject.h"
#include <string.h> // for memcpy
namespace lay
{
template <class Obj>
struct gl_type2enum
{
GLenum operator() () const;
};
template <>
struct gl_type2enum<float>
{
GLenum operator() () const { return GL_FLOAT; }
};
/**
* @brief Provides a semi-contiguous array of objects
*
* The objects are kept in chunks of ChunkLen items.
* The blocks can be accessed individually. The array can be
* cleared and new items can be added. No insert or delete.
*
* This object is intended to be used for keeping vertex,
* color or point data for OpenGL.
*/
template <class Obj, size_t ChunkLen = 1024>
class mem_chunks
: public QDialog
{
public:
struct chunk {
public:
chunk ()
: m_len (0), m_next (0)
{
// .. nothing yet ..
}
chunk (const chunk &other)
: m_len (0), m_next (0)
{
operator= (other);
}
chunk &operator= (const chunk &other)
{
if (this != &other) {
memcpy (&m_objects, &other.m_objects, sizeof (m_objects));
m_len = other.m_len;
}
return *this;
}
const Obj *front () const { return m_objects; }
size_t size () const { return m_len; }
const chunk *next () const { return m_next; }
private:
friend class mem_chunks;
Obj m_objects [ChunkLen];
size_t m_len;
chunk *m_next;
};
class iterator
{
public:
iterator (chunk *ch = 0)
: mp_chunk (ch)
{
// .. nothing yet ..
}
bool operator== (iterator other) const
{
return mp_chunk == other.mp_chunk;
}
bool operator!= (iterator other) const
{
return mp_chunk != other.mp_chunk;
}
const chunk &operator* () const
{
return *mp_chunk;
}
const chunk *operator-> () const
{
return mp_chunk;
}
void operator++ ()
{
mp_chunk = mp_chunk->next ();
}
private:
const chunk *mp_chunk;
};
/**
* @brief Default constructor
* Creates an empty array
*/
mem_chunks ()
: mp_chunks (0), mp_last_chunk (0)
{
// .. nothing yet ..
}
/**
* @brief Destructor
*/
~mem_chunks ()
{
clear ();
}
/**
* @brief Copy constructor
*/
mem_chunks (const mem_chunks &other)
: mp_chunks (0), mp_last_chunk (0)
{
operator= (other);
}
/**
* @brief Assignment
*/
mem_chunks &operator= (const mem_chunks &other)
{
if (this != &other) {
clear ();
const chunk *ch = other.mp_chunks;
while (ch) {
if (! mp_chunks) {
mp_last_chunk = mp_chunks = new chunk (*ch);
} else {
mp_last_chunk->m_next = new chunk (*ch);
mp_last_chunk = mp_last_chunk->m_next;
}
ch = ch->next ();
}
}
return *this;
}
/**
* @brief Clears the array
*/
void clear ()
{
chunk *ch = mp_chunks;
mp_chunks = 0;
mp_last_chunk = 0;
while (ch) {
chunk *del = ch;
ch = ch->m_next;
delete del;
}
}
/**
* @brief Adds an element to the array
*/
void add (const Obj &element)
{
if (! mp_last_chunk) {
mp_chunks = mp_last_chunk = new chunk ();
}
if (mp_last_chunk->m_len >= ChunkLen) {
mp_last_chunk->m_next = new chunk ();
mp_last_chunk = mp_last_chunk->m_next;
}
mp_last_chunk->m_objects [mp_last_chunk->m_len++] = element;
}
/**
* @brief Adds two elements
*/
void add (const Obj &e1, const Obj &e2)
{
add (e1);
add (e2);
}
/**
* @brief Adds three elements
*/
void add (const Obj &e1, const Obj &e2, const Obj &e3)
{
add (e1);
add (e2);
add (e3);
}
/**
* @brief begin iterator
*/
iterator begin () const { return iterator (mp_chunks); }
/**
* @brief end iterator
*/
iterator end () const { return iterator (); }
/**
* @brief Draw to the given context
*/
void draw_to (QOpenGLFunctions *ctx, GLuint location, GLenum mode) const
{
for (iterator c = begin (); c != end (); ++c) {
ctx->glVertexAttribPointer (location, 3, gl_type2enum<Obj> () (), GL_FALSE, 0, c->front ());
ctx->glDrawArrays (mode, 0, c->size () / 3);
}
}
private:
chunk *mp_chunks;
chunk *mp_last_chunk;
};
}
#endif
@@ -0,0 +1,114 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "layD25View.h"
#include "layDispatcher.h"
#include "layPlugin.h"
#include <QSurfaceFormat>
namespace lay
{
class D25Plugin
: public lay::Plugin
{
public:
D25Plugin (Plugin *parent, lay::LayoutView *view)
: lay::Plugin (parent), mp_view (view)
{
mp_dialog = new lay::D25View (0);
}
~D25Plugin ()
{
delete mp_dialog;
mp_dialog = 0;
}
void menu_activated (const std::string &symbol)
{
if (symbol == "lay::d25_view") {
if (mp_dialog->exec_dialog (mp_view)) {
// ... implementation is in layD25ToolDialog.cc ...
}
}
}
private:
lay::LayoutView *mp_view;
lay::D25View *mp_dialog;
};
class D25PluginDeclaration
: public lay::PluginDeclaration
{
public:
D25PluginDeclaration ()
{
// .. nothing yet ..
}
virtual void get_options (std::vector < std::pair<std::string, std::string> > & /*options*/) const
{
// .. nothing yet ..
}
virtual lay::ConfigPage *config_page (QWidget * /*parent*/, std::string & /*title*/) const
{
// .. nothing yet ..
return 0;
}
virtual void get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const
{
lay::PluginDeclaration::get_menu_entries (menu_entries);
menu_entries.push_back (lay::menu_item ("lay::d25_view", "d25_view:edit", "tools_menu.post_verification_group", tl::to_string (QObject::tr ("2.5d View"))));
}
virtual bool configure (const std::string & /*name*/, const std::string & /*value*/)
{
return false;
}
virtual void config_finalize ()
{
// .. nothing yet ..
}
lay::Plugin *create_plugin (db::Manager *, lay::Dispatcher *root, lay::LayoutView *view) const
{
return new D25Plugin (root, view);
}
};
static tl::RegisteredClass<lay::PluginDeclaration> config_decl (new lay::D25PluginDeclaration (), 3100, "lay::D25Plugin");
}
@@ -0,0 +1,151 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "layD25View.h"
#include "layLayoutView.h"
#include "ui_D25View.h"
#include <stdio.h>
namespace lay
{
const double initial_elevation = 3.0;
D25View::D25View (QWidget *parent)
: QDialog (parent)
{
mp_ui = new Ui::D25View ();
mp_ui->setupUi (this);
mp_ui->d25_view->setFocusPolicy (Qt::StrongFocus);
mp_ui->d25_view->setFocus ();
connect (mp_ui->fit_back, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ()));
connect (mp_ui->fit_front, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ()));
connect (mp_ui->fit_left, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ()));
connect (mp_ui->fit_right, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ()));
connect (mp_ui->fit_top, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ()));
connect (mp_ui->fit_bottom, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ()));
connect (mp_ui->zoom_slider, SIGNAL (valueChanged (int)), this, SLOT (scale_slider_changed (int)));
connect (mp_ui->d25_view, SIGNAL (scale_factor_changed (double)), this, SLOT (scale_factor_changed (double)));
}
D25View::~D25View ()
{
delete mp_ui;
mp_ui = 0;
}
static QString scale_factor_to_string (double f)
{
QString s;
s.sprintf ("x %.3g", f);
return s;
}
void
D25View::scale_slider_changed (int value)
{
double f = exp (log (10.0) * -0.01 * value);
mp_ui->zoom_factor->setText (scale_factor_to_string (f));
mp_ui->d25_view->set_scale_factor (f);
}
void
D25View::scale_factor_changed (double f)
{
mp_ui->zoom_factor->setText (scale_factor_to_string (f));
int v = floor (0.5 - log10 (f) * 100.0);
mp_ui->zoom_slider->blockSignals (true);
mp_ui->zoom_slider->setValue (v);
mp_ui->zoom_slider->blockSignals (false);
}
int
D25View::exec_dialog (lay::LayoutView *view)
{
mp_view.reset (view);
bool any = mp_ui->d25_view->attach_view (view);
if (! any) {
mp_view.reset (0);
mp_ui->d25_view->attach_view (0);
throw tl::Exception (tl::to_string (tr ("No z data configured for the layers in the view.\nUse \"Tools/Manage Technologies\" to set up a z stack.")));
}
mp_ui->d25_view->reset ();
mp_ui->d25_view->set_cam_azimuth (0.0);
mp_ui->d25_view->set_cam_elevation (initial_elevation);
mp_ui->d25_view->fit ();
int ret = QDialog::exec ();
mp_ui->d25_view->attach_view (0);
mp_view.reset (0);
return ret;
}
void
D25View::fit_button_clicked ()
{
double azimuth = mp_ui->d25_view->cam_azimuth ();
double elevation = mp_ui->d25_view->cam_elevation ();
if (sender () == mp_ui->fit_back) {
azimuth = -180.0;
elevation = -initial_elevation;
} else if (sender () == mp_ui->fit_front) {
azimuth = 0.0;
elevation = -initial_elevation;
} else if (sender () == mp_ui->fit_left) {
azimuth = 90.0;
elevation = -initial_elevation;
} else if (sender () == mp_ui->fit_right) {
azimuth = -90.0;
elevation = -initial_elevation;
} else if (sender () == mp_ui->fit_top) {
elevation = -90;
} else if (sender () == mp_ui->fit_bottom) {
elevation = 90;
}
mp_ui->d25_view->set_cam_azimuth (azimuth);
mp_ui->d25_view->set_cam_elevation (elevation);
mp_ui->d25_view->fit ();
}
void
D25View::accept ()
{
QDialog::accept ();
}
}
@@ -0,0 +1,70 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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
*/
#ifndef HDR_layD25View
#define HDR_layD25View
#include <QDialog>
#include "tlObject.h"
namespace Ui
{
class D25View;
}
namespace lay
{
class LayoutView;
}
namespace lay
{
class D25View
: public QDialog
{
Q_OBJECT
public:
D25View (QWidget *parent);
~D25View ();
int exec_dialog (lay::LayoutView *view);
protected:
void accept ();
private slots:
void fit_button_clicked ();
void scale_factor_changed (double f);
void scale_slider_changed (int value);
private:
Ui::D25View *mp_ui;
tl::weak_ptr<lay::LayoutView> mp_view;
};
}
#endif
@@ -0,0 +1,228 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "layD25ViewUtils.h"
#include <QMatrix4x4>
#include <QMatrix3x3>
#include <math.h>
namespace lay
{
const double epsilon = 1e-10;
std::pair<bool, QVector3D>
cutpoint_line_with_plane (const QVector3D &line, const QVector3D &dir, const QVector3D &plane, const QVector3D &plane_normal)
{
double dn = QVector3D::dotProduct (dir, plane_normal);
if (fabs (dn) < epsilon) {
return std::make_pair (false, QVector3D ());
} else {
return std::make_pair (true, line + dir * QVector3D::dotProduct (plane - line, plane_normal) / dn);
}
}
std::pair<bool, QVector3D>
cutpoint_line_with_face (const QVector3D &line, const QVector3D &dir, const QVector3D &plane, const QVector3D &u, const QVector3D &v)
{
QVector3D n = QVector3D::crossProduct (u, v);
std::pair<bool, QVector3D> r = cutpoint_line_with_plane (line, dir, plane, n);
if (! r.first) {
return r;
}
double pu = QVector3D::dotProduct (r.second - plane, u);
double pv = QVector3D::dotProduct (r.second - plane, v);
// test whether the cut point is inside the face
if (pu < -epsilon || pu > u.lengthSquared () + epsilon || pv < -epsilon || pv > v.lengthSquared () + epsilon) {
return std::make_pair (false, QVector3D ());
} else {
return r;
}
}
static bool somewhat_perpendicular (const QVector3D &a, const QVector3D &b)
{
// returns true if a and b are perpendicular within +/-30 degree
return fabs (QVector3D::dotProduct (a, b)) < 0.5 * a.length () * b.length ();
}
static std::pair<bool, QVector3D> plane_or_face (const QVector3D &line, const QVector3D &line_dir, const QVector3D &corner, const QVector3D &u, const QVector3D &v, bool face)
{
if (face) {
return cutpoint_line_with_face (line, line_dir, corner, u, v);
} else if (somewhat_perpendicular (u, line_dir) && somewhat_perpendicular (v, line_dir)) {
return cutpoint_line_with_plane (line, line_dir, corner, QVector3D::crossProduct (u, v));
} else {
return std::make_pair (false, QVector3D ());
}
}
std::pair<bool, QVector3D>
hit_point_with_cuboid (const QVector3D &line, const QVector3D &line_dir, const QVector3D &corner, const QVector3D &dim)
{
std::vector<std::pair<bool, QVector3D> > cutpoints;
cutpoints.reserve (6); // 6 faces
for (int pass = 0; pass < 2; ++pass) {
bool face = (pass == 0);
if (face) {
bool in_x = (line.x () > corner.x () - epsilon) && (line.x () < corner.x () + dim.x () + epsilon);
bool in_y = (line.y () > corner.y () - epsilon) && (line.y () < corner.y () + dim.y () + epsilon);
bool in_z = (line.z () > corner.z () - epsilon) && (line.z () < corner.z () + dim.z () + epsilon);
if (in_x && in_y && in_z) {
// inside cuboid
return std::make_pair (true, line);
}
}
cutpoints.clear ();
// front
cutpoints.push_back (plane_or_face (line, line_dir, corner, QVector3D (dim.x (), 0, 0), QVector3D (0, dim.y (), 0), face));
// back
cutpoints.push_back (plane_or_face (line, line_dir, corner + QVector3D (0, 0, dim.z ()), QVector3D (dim.x (), 0, 0), QVector3D (0, dim.y (), 0), face));
// bottom
cutpoints.push_back (plane_or_face (line, line_dir, corner, QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), face));
// top
cutpoints.push_back (plane_or_face (line, line_dir, corner + QVector3D (0, dim.y (), 0), QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), face));
// left
cutpoints.push_back (plane_or_face (line, line_dir, corner, QVector3D (0, 0, dim.z ()), QVector3D (0, dim.y (), 0), face));
// right
cutpoints.push_back (plane_or_face (line, line_dir, corner + QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), QVector3D (0, dim.y (), 0), face));
double min_dist = 0.0;
int min_dist_index = -1;
QVector3D ld_norm = line_dir.normalized ();
for (std::vector<std::pair<bool, QVector3D> >::const_iterator i = cutpoints.begin (); i != cutpoints.end (); ++i) {
if (i->first) {
double dist = QVector3D::dotProduct (i->second - line, ld_norm);
if (min_dist_index < 0) {
min_dist = dist;
min_dist_index = int (i - cutpoints.begin ());
} else if (dist < min_dist) {
min_dist = dist;
min_dist_index = int (i - cutpoints.begin ());
}
}
}
if (min_dist_index >= 0) {
return cutpoints [min_dist_index];
}
}
return std::make_pair (false, QVector3D ());
}
std::pair<QVector3D, QVector3D>
camera_normal (const QMatrix4x4 &camera_trans, double x, double y)
{
QVector3D p = camera_trans.inverted ().map (QVector3D (x, y, 1.0));
QVector4D pv = camera_trans.row (3);
QMatrix4x4 m (camera_trans);
float values[] = {
float (x * pv.x ()), float (x * pv.y ()), float (x * pv.z ()), 0.0f,
float (y * pv.x ()), float (y * pv.y ()), float (y * pv.z ()), 0.0f,
float (pv.x ()), float (pv.y ()), float (pv.z ()), 0.0f,
0.0f, 0.0f, 0.0f, 0.0f
};
m -= QMatrix4x4 (values);
QMatrix3x3 nm = m.normalMatrix ();
QVector3D u (nm (2, 0), nm (2, 1), nm (2, 2));
return (std::make_pair (p, u.normalized ()));
}
void
normalize_scene_trans (const QMatrix4x4 &cam_trans, QVector3D &displacement, double &scale, double ztarget)
{
// Here is the theory:
// Let:
// cam = ( M t ) M = 3x3 matrix, t = 3x1 translation vector, z = scalar, p = 1x3 perspective
// ( p z )
// and:
// scene = ( S d*s ) S = s*U1 (s = scale factor, U1 = 3x3 unit matrix), d = 3x1 displacement vector
// ( 0 1 )
// then:
// cam * scene = ( M*s M*d*s+t )
// ( p*s p*d*s+z ) (p*d = dot product)
//
// this is image invariant (only x,y results are considered) against changes of s (s->s') if
//
// 1.) (p*d*s+z)/s = (p*d'*s'+z)/s' (because x and y will be devided by this value)
// 2.) (M*d*s+t)/s = (M*d'*s'+t)/s' for [x] and [y]
//
// or
//
// 1.) p*d+z/s = p*d'+z/s'
// 2.) M*d+t/s = M*d'+t/s'
//
// If we seek a solution with d'[z] == b (b = ztarget), we get these equations (f:=1/s')
//
// 2.) M[xx] * d'[x] + M[xy] * d'[y] + t[x] * f = (M*d)[x] + t[x]/s - M[xz]*b
// M[yx] * d'[x] + M[yy] * d'[y] + t[y] * f = (M*d)[y] + t[y]/s - M[yz]*b
// 1.) p[x] * d'[x] + p[y] * d'[y] + z * f = p*d + z/s - p[z]*b
//
// we can solve these equations for d'[x], d'[y] and f.
// With p[x]=M[wx], p[y]=M[wy] and z=t[w], the above equation system can be written as
//
// M[ix] * d'[x] + M[iy] * d'[y] + t[i] * f = (M*d)[i] - M[iz]*b + t[i]/s i = x,y,w
//
QMatrix4x4 m;
for (int i = 0; i < 4; ++i) {
if (i != 2) {
m (i, 0) = cam_trans (i, 0);
m (i, 1) = cam_trans (i, 1);
m (i, 3) = cam_trans (i, 3);
}
}
bool invertable = false;
QMatrix4x4 minv = m.inverted (&invertable);
if (! invertable) {
return;
}
QVector4D rhs = cam_trans.map (QVector4D (displacement.x (), displacement.y (), displacement.z () - ztarget, 1.0 / scale));
QVector4D sol = minv.map (rhs);
double f = sol.w ();
if (f > 1e-6 /*skip weird solutions*/) {
scale = 1.0 / f;
displacement = QVector3D (sol.x (), sol.y (), ztarget);
}
}
}
@@ -0,0 +1,92 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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
*/
#ifndef HDR_layD25ViewUtils
#define HDR_layD25ViewUtils
#include "layPluginCommon.h"
#include <QVector3D>
#include <QMatrix4x4>
#include <algorithm>
namespace lay
{
/**
* @brief Computes the cutpoint between a line and a plane
*
* The line is given by a point and a direction (line, line_dir).
* The plane is given by a point and a normal vector (plane, plane_normal)
* The "first" component of the returned pair is false if not hit is present.
*/
LAY_PLUGIN_PUBLIC std::pair<bool, QVector3D>
cutpoint_line_with_plane (const QVector3D &line, const QVector3D &line_dir, const QVector3D &plane, const QVector3D &plane_normal);
/**
* @brief Computes the cutpoint between a line and a face
*
* The line is given by a point and a direction (line, line_dir).
* The face is given by a plane point and two vectors spanning the face.
* The "first" component of the returned pair is false if not hit is present.
*/
LAY_PLUGIN_PUBLIC std::pair<bool, QVector3D>
cutpoint_line_with_face (const QVector3D &line, const QVector3D &dir, const QVector3D &plane, const QVector3D &u, const QVector3D &v);
/**
* @brief Determines a good hit point of a view line and a cuboid
*
* "corner, dim" are the coordinates for the cuboid (corner is the bottom, left, foremost corner, dim
* is (width, height, depth)
* "line, line_dir" is the view line where "line_dir" is pointing from the camera to the object.
* The returned point is a suitable hit point.
* The "first" component of the returned pair is false if no hit is present.
*/
LAY_PLUGIN_PUBLIC std::pair<bool, QVector3D>
hit_point_with_cuboid (const QVector3D &line, const QVector3D &line_dir, const QVector3D &corner, const QVector3D &dim);
/**
* @brief For a given pixel coordinate and camera transformation matrix compute a line containing all points corresponding to this pixel
*
* The returned pair contains a point and a direction vector describing the line.
*/
LAY_PLUGIN_PUBLIC std::pair<QVector3D, QVector3D>
camera_normal (const QMatrix4x4 &camera_trans, double x, double y);
/**
* @brief Normalizes a scene transformation
*
* Scene transformations consist of a scaling and displacement. Both are
* interchangeable to some extent under the presence of a perspective
* transformation (further away makes the scene smaller). This normalization
* tries to find a displacement which has "ztarget" target value for z. Without normalization
* the scene tends to "move away" with respect to z.
*/
LAY_PLUGIN_PUBLIC void
normalize_scene_trans (const QMatrix4x4 &cam_trans, QVector3D &displacement, double &scale, double ztarget = 0.0);
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,150 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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
*/
#ifndef HDR_layD25ViewWidget
#define HDR_layD25ViewWidget
#include "dbPolygon.h"
#include "layD25MemChunks.h"
#include "layD25Camera.h"
#include <QOpenGLWidget>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include <QMatrix4x4>
#include <QPoint>
#include <QVector3D>
#include <memory>
namespace db
{
class Layout;
class Cell;
}
namespace lay
{
class LayoutView;
class D25ViewWidget;
class D25InteractionMode
{
public:
D25InteractionMode (D25ViewWidget *widget);
virtual ~D25InteractionMode ();
D25ViewWidget *view () { return mp_view; }
virtual void mouse_move (QMouseEvent *event) = 0;
private:
D25ViewWidget *mp_view;
};
class D25ViewWidget
: public QOpenGLWidget,
private QOpenGLFunctions,
public D25Camera
{
Q_OBJECT
public:
D25ViewWidget (QWidget *parent);
~D25ViewWidget ();
void keyPressEvent (QKeyEvent *event);
void keyReleaseEvent (QKeyEvent *event);
void wheelEvent (QWheelEvent *event);
void mousePressEvent (QMouseEvent *event);
void mouseReleaseEvent (QMouseEvent *event);
void mouseMoveEvent (QMouseEvent *event);
bool attach_view(lay::LayoutView *view);
QVector3D hit_point_with_scene(const QVector3D &line_dir);
void refresh ();
void reset ();
QVector3D displacement () const { return m_displacement; }
void set_displacement (const QVector3D &d)
{
m_displacement = d;
refresh ();
}
double scale_factor () const { return m_scale_factor; }
void set_scale_factor (double f)
{
m_scale_factor = f;
refresh ();
}
signals:
void scale_factor_changed (double f);
protected:
virtual void camera_changed ();
virtual double aspect_ratio () const;
public slots:
void fit ();
private:
typedef lay::mem_chunks<GLfloat, 1024 * 18> chunks_type;
std::auto_ptr<D25InteractionMode> mp_mode;
QOpenGLShaderProgram *m_shapes_program, *m_gridplane_program;
double m_scale_factor;
QVector3D m_displacement;
lay::LayoutView *mp_view;
db::DBox m_bbox;
double m_zmin, m_zmax;
std::list<chunks_type> m_vertex_chunks;
struct LayerInfo {
const chunks_type *vertex_chunk;
GLfloat color [4];
};
std::list<LayerInfo> m_layers;
void initializeGL ();
void paintGL ();
void resizeGL (int w, int h);
bool prepare_view();
void render_layout (D25ViewWidget::chunks_type &chunks, const db::Layout &layout, const db::Cell &cell, unsigned int layer, double zstart, double zstop);
void render_polygon (D25ViewWidget::chunks_type &chunks, const db::Polygon &poly, double dbu, double zstart, double zstop);
void render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &poly, double dbu, double zstart, double zstop);
};
}
#endif
@@ -0,0 +1,122 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "layXORToolDialog.h"
#include "layDispatcher.h"
#include "layPlugin.h"
namespace lay
{
class XORPlugin
: public lay::Plugin
{
public:
XORPlugin (Plugin *parent, lay::LayoutView *view)
: lay::Plugin (parent), mp_view (view)
{
mp_dialog = new lay::XORToolDialog (0);
}
~XORPlugin ()
{
delete mp_dialog;
mp_dialog = 0;
}
void menu_activated (const std::string &symbol)
{
if (symbol == "lay::xor_tool") {
if (mp_dialog->exec_dialog (mp_view)) {
// ... implementation is in layXORToolDialog.cc ...
}
}
}
private:
lay::LayoutView *mp_view;
lay::XORToolDialog *mp_dialog;
};
class XORPluginDeclaration
: public lay::PluginDeclaration
{
public:
XORPluginDeclaration ()
{
// .. nothing yet ..
}
virtual void get_options (std::vector < std::pair<std::string, std::string> > &options) const
{
options.push_back (std::pair<std::string, std::string> (cfg_xor_input_mode, "all"));
options.push_back (std::pair<std::string, std::string> (cfg_xor_output_mode, "rdb"));
options.push_back (std::pair<std::string, std::string> (cfg_xor_nworkers, "1"));
options.push_back (std::pair<std::string, std::string> (cfg_xor_layer_offset, ""));
options.push_back (std::pair<std::string, std::string> (cfg_xor_axorb, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_xor_anotb, "false"));
options.push_back (std::pair<std::string, std::string> (cfg_xor_bnota, "false"));
options.push_back (std::pair<std::string, std::string> (cfg_xor_summarize, "false"));
options.push_back (std::pair<std::string, std::string> (cfg_xor_tolerances, ""));
options.push_back (std::pair<std::string, std::string> (cfg_xor_tiling, ""));
options.push_back (std::pair<std::string, std::string> (cfg_xor_region_mode, "all"));
}
virtual lay::ConfigPage *config_page (QWidget * /*parent*/, std::string & /*title*/) const
{
// .. nothing yet ..
return 0;
}
virtual void get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const
{
lay::PluginDeclaration::get_menu_entries (menu_entries);
menu_entries.push_back (lay::menu_item ("lay::xor_tool", "xor_tool:edit", "tools_menu.post_verification_group", tl::to_string (QObject::tr ("XOR Tool"))));
}
virtual bool configure (const std::string & /*name*/, const std::string & /*value*/)
{
return false;
}
virtual void config_finalize ()
{
// .. nothing yet ..
}
lay::Plugin *create_plugin (db::Manager *, lay::Dispatcher *root, lay::LayoutView *view) const
{
return new XORPlugin (root, view);
}
};
static tl::RegisteredClass<lay::PluginDeclaration> config_decl (new lay::XORPluginDeclaration (), 3000, "lay::XORPlugin");
}
@@ -0,0 +1,27 @@
TARGET = d25_ui
DESTDIR = $$OUT_PWD/../../../../lay_plugins
include($$PWD/../../../lay_plugin.pri)
INCLUDEPATH += $$RDB_INC $$ANT_INC
DEPENDPATH += $$RDB_INC $$ANT_INC
LIBS += -L$$DESTDIR/.. -lklayout_rdb -lklayout_ant
HEADERS = \
layD25View.h \
layD25ViewWidget.h \
layD25MemChunks.h \
layD25ViewUtils.h \
layD25Camera.h
SOURCES = \
layD25View.cc \
layD25ViewWidget.cc \
layD25Plugin.cc \
layD25MemChunks.cc \
layD25ViewUtils.cc \
layD25Camera.cc
FORMS = \
D25View.ui \
@@ -0,0 +1,102 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "layD25Camera.h"
#include "tlUnitTest.h"
static std::string v2s (const QVector4D &v)
{
return tl::to_string (v.x ()) + "," + tl::to_string (v.y ()) + "," + tl::to_string (v.z ());
}
static std::string v2s (const QVector3D &v)
{
return tl::to_string (v.x ()) + "," + tl::to_string (v.y ()) + "," + tl::to_string (v.z ());
}
static std::string v2s_2d (const QVector3D &v)
{
return tl::to_string (v.x ()) + "," + tl::to_string (v.y ());
}
TEST(1_Transformations)
{
lay::D25Camera cam;
cam.set_cam_azimuth (45.0);
EXPECT_EQ (cam.cam_azimuth (), 45.0);
cam.set_cam_elevation (22.0);
EXPECT_EQ (cam.cam_elevation (), 22.0);
cam.camera_reset ();
EXPECT_EQ (cam.cam_azimuth (), 0.0);
EXPECT_EQ (cam.cam_elevation (), 0.0);
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "1,0,0");
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,1,0");
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "0,0,1");
EXPECT_EQ (v2s (cam.cam_direction ()), "0,0,-1");
EXPECT_EQ (v2s (cam.cam_position ()), "0,0,4");
// looking up from the bottom, x axis stays the same (azimuth = 0)
cam.set_cam_elevation (90.0);
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "1,0,0");
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,0,-1");
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "0,1,0");
EXPECT_EQ (v2s (cam.cam_direction ()), "0,1,0");
EXPECT_EQ (v2s (cam.cam_position ()), "0,-4,0");
// looking down from the top, x axis stays the same (azimuth = 0)
cam.set_cam_elevation (-90.0);
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "1,0,0");
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,0,1");
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "0,-1,0");
EXPECT_EQ (v2s (cam.cam_direction ()), "0,-1,0");
EXPECT_EQ (v2s (cam.cam_position ()), "0,4,0");
// looking from the left, y axis stays the same (elevation = 0)
cam.set_cam_elevation (0.0);
cam.set_cam_azimuth (90.0);
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "0,0,-1");
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,1,0");
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "1,0,0");
EXPECT_EQ (v2s (cam.cam_direction ()), "1,0,0");
EXPECT_EQ (v2s (cam.cam_position ()), "-4,0,0");
// looking from the right, y axis stays the same (elevation = 0)
cam.set_cam_elevation (0.0);
cam.set_cam_azimuth (-90.0);
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "0,0,1");
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,1,0");
EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "-1,0,0");
EXPECT_EQ (v2s (cam.cam_direction ()), "-1,0,0");
EXPECT_EQ (v2s (cam.cam_position ()), "4,0,0");
}
@@ -0,0 +1,90 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "layD25MemChunks.h"
#include "tlUnitTest.h"
TEST(1_Basic)
{
lay::mem_chunks<int, 2> ch;
EXPECT_EQ (ch.begin () == ch.end (), true);
ch.add (1);
EXPECT_EQ (ch.begin () == ch.end (), false);
EXPECT_EQ (ch.begin ()->size (), size_t (1));
EXPECT_EQ (ch.begin ()->front () [0], 1);
ch.add (17);
EXPECT_EQ (ch.begin () == ch.end (), false);
EXPECT_EQ (ch.begin ()->size (), size_t (2));
EXPECT_EQ (ch.begin ()->front () [0], 1);
EXPECT_EQ (ch.begin ()->front () [1], 17);
lay::mem_chunks<int, 2>::iterator c = ch.begin ();
EXPECT_EQ (c == ch.end (), false);
++c;
EXPECT_EQ (c == ch.end (), true);
ch.add (42);
c = ch.begin ();
EXPECT_EQ (c == ch.end (), false);
EXPECT_EQ (c->size (), size_t (2));
EXPECT_EQ (c->front () [0], 1);
EXPECT_EQ (c->front () [1], 17);
++c;
EXPECT_EQ (c == ch.end (), false);
EXPECT_EQ (c->size (), size_t (1));
EXPECT_EQ (c->front () [0], 42);
++c;
EXPECT_EQ (c == ch.end (), true);
ch.clear ();
EXPECT_EQ (ch.begin () == ch.end (), true);
}
TEST(2_Copy)
{
lay::mem_chunks<int, 2> ch1;
ch1.add (1);
ch1.add (17);
ch1.add (42);
lay::mem_chunks<int, 2> ch (ch1);
lay::mem_chunks<int, 2>::iterator c = ch.begin ();
EXPECT_EQ (c == ch.end (), false);
EXPECT_EQ (c->size (), size_t (2));
EXPECT_EQ (c->front () [0], 1);
EXPECT_EQ (c->front () [1], 17);
++c;
EXPECT_EQ (c == ch.end (), false);
EXPECT_EQ (c->size (), size_t (1));
EXPECT_EQ (c->front () [0], 42);
++c;
EXPECT_EQ (c == ch.end (), true);
ch1.clear ();
ch = ch1;
EXPECT_EQ (ch.begin () == ch.end (), true);
}
@@ -0,0 +1,240 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "layD25ViewUtils.h"
#include "tlUnitTest.h"
static std::string v2s (const QVector3D &v)
{
return tl::to_string (v.x ()) + "," + tl::to_string (v.y ()) + "," + tl::to_string (v.z ());
}
static std::string v2s_2d (const QVector3D &v)
{
return tl::to_string (v.x ()) + "," + tl::to_string (v.y ());
}
TEST(1_CutPoint)
{
std::pair<bool, QVector3D> r;
r = lay::cutpoint_line_with_plane (QVector3D (0, 0, 0), QVector3D (0, 0, 1), QVector3D (0, 0, 0), QVector3D (1, 0, 0));
EXPECT_EQ (r.first, false);
r = lay::cutpoint_line_with_plane (QVector3D (1, 2, 3), QVector3D (0, 0, 2), QVector3D (4, 5, 6), QVector3D (0, 0, 1));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "1,2,6");
r = lay::cutpoint_line_with_plane (QVector3D (1, 2, 3), QVector3D (0, 0, -1), QVector3D (4, 5, 6), QVector3D (1, 1, 1));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "1,2,12");
}
TEST(2_Face)
{
std::pair<bool, QVector3D> r;
r = lay::cutpoint_line_with_face (QVector3D (0, 0, 0), QVector3D (0, 0, 1), QVector3D (0, 0, 0), QVector3D (0, 1, 0), QVector3D (0, 0, 1));
EXPECT_EQ (r.first, false);
r = lay::cutpoint_line_with_face (QVector3D (1, 2, 3), QVector3D (0, 0, 2), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0));
EXPECT_EQ (r.first, false);
r = lay::cutpoint_line_with_face (QVector3D (4, 5, 3), QVector3D (0, 0, 3), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "4,5,6");
r = lay::cutpoint_line_with_face (QVector3D (4, 7, 3), QVector3D (0, 0, 1), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0));
EXPECT_EQ (r.first, false);
r = lay::cutpoint_line_with_face (QVector3D (4, 6, 3), QVector3D (0, 0, 2), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "4,6,6");
r = lay::cutpoint_line_with_face (QVector3D (5, 6, 3), QVector3D (0, 0, -1), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "5,6,6");
r = lay::cutpoint_line_with_face (QVector3D (6, 6, 3), QVector3D (0, 0, 1), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0));
EXPECT_EQ (r.first, false);
}
TEST(3_HitWithCuboid)
{
std::pair<bool, QVector3D> r;
r = lay::hit_point_with_cuboid (QVector3D (0, 0, 0), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "0,0,3");
r = lay::hit_point_with_cuboid (QVector3D (1, 1, 4), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "1,1,4");
r = lay::hit_point_with_cuboid (QVector3D (1, 1, 6), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "1,1,3");
r = lay::hit_point_with_cuboid (QVector3D (5, -6, 0), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "5,-6,3");
r = lay::hit_point_with_cuboid (QVector3D (5, -6, 4), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "5,-6,3");
r = lay::hit_point_with_cuboid (QVector3D (5, -6, 6), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "5,-6,3");
r = lay::hit_point_with_cuboid (QVector3D (5, 0, 0), QVector3D (-1, 0, 0), QVector3D (-1, -1, 3), QVector3D (2, 2, 2));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "1,0,0");
r = lay::hit_point_with_cuboid (QVector3D (-5, 0, 0), QVector3D (1, 0, 0), QVector3D (-1, -1, 3), QVector3D (2, 2, 2));
EXPECT_EQ (r.first, true);
EXPECT_EQ (v2s (r.second), "-1,0,0");
}
TEST(4_CameraNormal)
{
QMatrix4x4 matrix;
matrix.perspective (60.0f, 1.5, 0.1f, 100.0f);
std::pair<QVector3D, QVector3D> ray;
QVector3D p;
ray = lay::camera_normal (matrix, 0.0, 0.0);
EXPECT_EQ (v2s (ray.second.normalized ()), "0,0,-1");
ray = lay::camera_normal (matrix, 1.0, 0.0);
EXPECT_EQ (v2s (ray.second), "0.654654,0,-0.755929");
p = matrix.map (ray.first);
EXPECT_EQ (v2s_2d (p), "1,0");
p = matrix.map (ray.first + ray.second);
EXPECT_EQ (v2s_2d (p), "1,0");
p = matrix.map (ray.first + ray.second * 1000.0);
EXPECT_EQ (v2s_2d (p), "1,0");
ray = lay::camera_normal (matrix, 0.0, -1.0);
EXPECT_EQ (v2s (ray.second), "0,-0.5,-0.866025");
p = matrix.map (ray.first);
EXPECT_EQ (v2s_2d (p), "0,-1");
p = matrix.map (ray.first + ray.second);
EXPECT_EQ (v2s_2d (p), "0,-1");
p = matrix.map (ray.first + ray.second * 1000.0);
EXPECT_EQ (v2s_2d (p), "0,-1");
}
TEST(5_CameraNormal)
{
QMatrix4x4 matrix;
QVector3D p;
matrix.perspective (60.0f, 1.5, 0.1f, 100.0f);
matrix.rotate (22.0, 1.0, 0.0, 0.0);
matrix.rotate (-15.0, 0.0, 1.0, 0.0);
matrix.translate (QVector3D (0.0, 0.0, 4.0));
std::pair<QVector3D, QVector3D> ray;
ray = lay::camera_normal (matrix, 0.0, 1.0);
EXPECT_EQ (v2s (ray.second), "-0.2563,0.139173,-0.956526");
p = matrix.map (ray.first);
EXPECT_EQ (v2s_2d (p), "0,1");
p = matrix.map (ray.first + ray.second);
EXPECT_EQ (v2s_2d (p), "0,1");
p = matrix.map (ray.first + ray.second * 1000.0);
EXPECT_EQ (v2s_2d (p), "0,1");
}
TEST(6_NormalizeSceneTrans)
{
QMatrix4x4 cam;
cam.perspective (60.0f, 1.5, 0.1f, 100.0f);
cam.rotate (22.0, 1.0, 0.0, 0.0);
cam.rotate (-15.0, 0.0, 1.0, 0.0);
cam.translate (QVector3D (0.0, 0.0, 4.0));
double scale = 0.1;
QVector3D displacement (-5.0, 2.0, 20.0);
QMatrix4x4 scene1;
scene1.scale (scale);
scene1.translate (displacement);
QVector3D v1 = (cam * scene1).map (QVector3D (1.0, -1.0, 2.0));
v1.setZ (0);
QVector3D v2 = (cam * scene1).map (QVector3D (0.0, 0.0, 5.0));
v2.setZ (0);
QVector3D v3 = (cam * scene1).map (QVector3D (-1.0, 0.0, 1.0));
v3.setZ (0);
lay::normalize_scene_trans (cam, displacement, scale);
QMatrix4x4 scene2;
scene2.scale (scale);
scene2.translate (displacement);
EXPECT_EQ (tl::sprintf ("%.4f", scale), "0.0667");
QVector3D u1 = (cam * scene2).map (QVector3D (1.0, -1.0, 2.0));
u1.setZ (0);
QVector3D u2 = (cam * scene2).map (QVector3D (0.0, 0.0, 5.0));
u2.setZ (0);
QVector3D u3 = (cam * scene2).map (QVector3D (-1.0, 0.0, 1.0));
u3.setZ (0);
EXPECT_EQ ((u1 - v1).length () < 1e-4, true);
EXPECT_EQ ((u2 - v2).length () < 1e-4, true);
EXPECT_EQ ((u3 - v3).length () < 1e-4, true);
lay::normalize_scene_trans (cam, displacement, scale, 10.0);
QMatrix4x4 scene3;
scene3.scale (scale);
scene3.translate (displacement);
EXPECT_EQ (tl::sprintf ("%.4f", scale), "0.0800");
EXPECT_EQ (tl::to_string (displacement.z ()), "10");
QVector3D uu1 = (cam * scene2).map (QVector3D (1.0, -1.0, 2.0));
uu1.setZ (0);
QVector3D uu2 = (cam * scene2).map (QVector3D (0.0, 0.0, 5.0));
uu2.setZ (0);
QVector3D uu3 = (cam * scene2).map (QVector3D (-1.0, 0.0, 1.0));
uu3.setZ (0);
EXPECT_EQ ((uu1 - v1).length () < 1e-4, true);
EXPECT_EQ ((uu2 - v2).length () < 1e-4, true);
EXPECT_EQ ((uu3 - v3).length () < 1e-4, true);
}
@@ -0,0 +1,21 @@
DESTDIR_UT = $$OUT_PWD/../../../..
TARGET = view_25d_tests
include($$PWD/../../../../lib_ut.pri)
SOURCES = \
layD25MemChunksTests.cc \
layD25ViewUtilsTests.cc \
layD25CameraTests.cc
INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common
DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common
LIBS += -L$$DESTDIR_UT -lklayout_db -lklayout_tl -lklayout_gsi
PLUGINPATH = $$OUT_PWD/../../../../lay_plugins
QMAKE_RPATHDIR += $$PLUGINPATH
LIBS += -L$$PLUGINPATH -ld25_ui
+6
View File
@@ -0,0 +1,6 @@
TEMPLATE = subdirs
equals(HAVE_QT5, "1") {
SUBDIRS = lay_plugin unit_tests
}