diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 89b1eccde..94c5d291b 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -185,6 +185,7 @@ SOURCES = \ gsiDeclDbNetlistCrossReference.cc \ gsiDeclDbLayoutVsSchematic.cc \ dbNetlistObject.cc \ + dbD25TechnologyComponent.cc \ gsiDeclDbTexts.cc \ dbTexts.cc \ dbDeepTexts.cc \ @@ -345,6 +346,7 @@ HEADERS = \ dbLayoutVsSchematicFormatDefs.h \ dbLayoutVsSchematic.h \ dbNetlistObject.h \ + dbD25TechnologyComponent.h \ dbTexts.h \ dbDeepTexts.h \ dbAsIfFlatTexts.h \ diff --git a/src/db/db/dbD25TechnologyComponent.cc b/src/db/db/dbD25TechnologyComponent.cc new file mode 100644 index 000000000..1ab0ed2ea --- /dev/null +++ b/src/db/db/dbD25TechnologyComponent.cc @@ -0,0 +1,332 @@ + +/* + + 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 "dbD25TechnologyComponent.h" +#include "tlClassRegistry.h" +#include "tlString.h" + +namespace db +{ + +std::string d25_component_name () +{ + return std::string ("d25"); +} + +std::string d25_description () +{ + return tl::to_string (tr ("Z stack (2.5d)")); +} + +// ----------------------------------------------------------------------------------------- +// D25LayerInfo implementation + +D25LayerInfo::D25LayerInfo () + : m_layer (), m_zstart (0.0), m_zstop (0.0) +{ + // .. nothing yet .. +} + +D25LayerInfo::~D25LayerInfo () +{ + // .. nothing yet .. +} + +D25LayerInfo::D25LayerInfo (const D25LayerInfo &other) +{ + operator= (other); +} + +D25LayerInfo & +D25LayerInfo::operator= (const D25LayerInfo &other) +{ + if (this != &other) { + m_layer = other.m_layer; + m_zstart = other.m_zstart; + m_zstop = other.m_zstop; + } + return *this; +} + +bool +D25LayerInfo::operator== (const D25LayerInfo &other) const +{ + return fabs (m_zstart - other.m_zstart) < db::epsilon && fabs (m_zstop - other.m_zstop) < db::epsilon; +} + +void +D25LayerInfo::set_layer (const db::LayerProperties &l) +{ + m_layer = l; +} + +void +D25LayerInfo::set_layer_from_string (const std::string &l) +{ + db::LayerProperties lp; + tl::Extractor ex (l.c_str ()); + try { + lp.read (ex); + } catch (tl::Exception &) { + // ignore errors for now. + } + m_layer = lp; +} + +std::string +D25LayerInfo::layer_as_string () const +{ + return m_layer.to_string (); +} + +void +D25LayerInfo::set_zstart (double z0) +{ + m_zstart = z0; +} + +void +D25LayerInfo::set_zstop (double z1) +{ + m_zstop = z1; +} + +// ----------------------------------------------------------------------------------- +// D25TechnologyComponent implementation + +D25TechnologyComponent::D25TechnologyComponent () + : db::TechnologyComponent (d25_component_name (), d25_description ()) +{ + // provide some explanation for the initialization + m_src = + "# Provide z stack information here\n" + "# Each line is one layer. The specification consists of a layer specification, a colon and arguments.\n" + "# The arguments are named (like \"x=...\") or in serial. Parameters are separated by comma or blanks.\n" + "# Named arguments are:\n" + "#\n" + "# zstart The lower z position of the extruded layer in µm\n" + "# zstop The upper z position of the extruded layer in µm\n" + "# height The height of the extruded layer in µm\n" + "#\n" + "# 'height', 'zstart' and 'zstop' can be used in any combination. If no value is given for 'zstart', " + "# the upper level of the previous layer will be used.\n" + "#\n" + "# If a single unnamed parameter is given, it corresponds to 'height'. Two parameters correspond to\n" + "# 'zstart' and 'zstop'.\n" + "#\n" + "# Examples:\n" + "# 1: 0.5 1.5 # extrude layer 1/0 from 0.5 to 1.5 vertically\n" + "# 1/0: 0.5 1.5 # same with explicit datatype\n" + "# 1: zstop=1.5, zstart=0.5 # same with named parameters\n" + "# 1: height=1.0, zstop=1.5 # same with z stop minus height\n" + "# 1: 1.0 zstop=1.5 # same with height as unnamed parameter\n" + ; +} + +D25TechnologyComponent::D25TechnologyComponent (const D25TechnologyComponent &d) + : db::TechnologyComponent (d25_component_name (), d25_description ()) +{ + m_layers = d.m_layers; + m_src = d.m_src; +} + +void +D25TechnologyComponent::compile_from_source (const std::string &src) +{ + int current_line = 0; + m_layers.clear (); + + try { + + std::vector lines = tl::split (src, "\n"); + for (std::vector::const_iterator l = lines.begin (); l != lines.end (); ++l) { + + ++current_line; + + tl::Extractor ex (l->c_str ()); + + if (ex.test ("#")) { + // ignore comments + } else if (ex.at_end ()) { + // ignore empty lines + } else { + + db::D25LayerInfo info; + if (! m_layers.empty ()) { + info.set_zstart (m_layers.back ().zstop ()); + info.set_zstop (m_layers.back ().zstop ()); + } + + tl::Variant z0, z1, h; + std::vector args; + + db::LayerProperties lp; + lp.read (ex); + info.set_layer (lp); + + ex.expect (":"); + + while (! ex.at_end ()) { + + if (ex.test ("#")) { + break; + } + + double pv = 0.0; + + std::string pn; + if (ex.try_read_name (pn)) { + ex.expect ("="); + ex.read (pv); + } else { + ex.read (pv); + } + + ex.test (","); + + if (pn.empty ()) { + args.push_back (pv); + } else if (pn == "zstart") { + z0 = pv; + } else if (pn == "zstop") { + z1 = pv; + } else if (pn == "height") { + h = pv; + } else { + throw tl::Exception (tl::to_string (tr ("Invalid parameter name: ")) + pn); + } + + } + + if (args.size () == 0) { + if (z0.is_nil () && z1.is_nil ()) { + if (! h.is_nil ()) { + info.set_zstop (info.zstart () + h.to_double ()); + } + } else if (z0.is_nil ()) { + info.set_zstop (z1.to_double ()); + if (! h.is_nil ()) { + info.set_zstart (info.zstop () - h.to_double ()); + } + } else if (z1.is_nil ()) { + info.set_zstart (z0.to_double ()); + if (! h.is_nil ()) { + info.set_zstop (info.zstart () + h.to_double ()); + } + } else { + info.set_zstart (z0.to_double ()); + info.set_zstop (z1.to_double ()); + } + } else if (args.size () == 1) { + if (! h.is_nil ()) { + if (! z0.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstart already given"))); + } + if (! z1.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop implicitly given"))); + } + info.set_zstart (args[0]); + info.set_zstop (args[0] + h.to_double ()); + } else { + if (! z1.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop implicitly given"))); + } + info.set_zstop ((! z0.is_nil () ? z0.to_double () : info.zstart ()) + args[0]); + } + } else if (args.size () == 2) { + if (! z0.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstart already given"))); + } + if (! z1.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop already given"))); + } + if (! h.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: height implicitly given"))); + } + info.set_zstart (args[0]); + info.set_zstop (args[1]); + } else { + throw tl::Exception (tl::to_string (tr ("Too many parameters (max 2)"))); + } + + m_layers.push_back (info); + + } + + } + + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (" in line %d")), current_line)); + } + + m_src = src; +} + +std::string +D25TechnologyComponent::to_string () const +{ + std::string res; + + for (const_iterator i = begin (); i != end (); ++i) { + if (! res.empty ()) { + res += "\n"; + } + res += i->layer ().to_string () + ": zstart=" + tl::to_string (i->zstart ()) + ", zstop=" + tl::to_string (i->zstop ()); + } + + return res; +} + +// ----------------------------------------------------------------------------------- +// D25TechnologyComponent technology component registration + +class D25TechnologyComponentProvider + : public db::TechnologyComponentProvider +{ +public: + D25TechnologyComponentProvider () + : db::TechnologyComponentProvider () + { + // .. nothing yet .. + } + + virtual db::TechnologyComponent *create_component () const + { + return new D25TechnologyComponent (); + } + + virtual tl::XMLElementBase *xml_element () const + { + return new db::TechnologyComponentXMLElement (d25_component_name (), + tl::make_element ((D25TechnologyComponent::const_iterator (D25TechnologyComponent::*) () const) &D25TechnologyComponent::begin, (D25TechnologyComponent::const_iterator (D25TechnologyComponent::*) () const) &D25TechnologyComponent::end, &D25TechnologyComponent::add, "layer", + tl::make_member (&D25LayerInfo::layer_as_string, &D25LayerInfo::set_layer_from_string, "layer") + + tl::make_member (&D25LayerInfo::zstart, &D25LayerInfo::set_zstart, "zstart") + + tl::make_member (&D25LayerInfo::zstop, &D25LayerInfo::set_zstop, "zstop") + ) + + tl::make_member (&D25TechnologyComponent::src, &D25TechnologyComponent::set_src, "src") + ); + } +}; + +static tl::RegisteredClass tc_decl (new D25TechnologyComponentProvider (), 3100, d25_component_name ().c_str ()); + +} diff --git a/src/db/db/dbD25TechnologyComponent.h b/src/db/db/dbD25TechnologyComponent.h new file mode 100644 index 000000000..4c31363c1 --- /dev/null +++ b/src/db/db/dbD25TechnologyComponent.h @@ -0,0 +1,154 @@ + +/* + + 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_dbD25TechnologyComponent +#define HDR_dbD25TechnologyComponent + +#include "dbTechnology.h" +#include "dbLayerProperties.h" + +namespace db +{ + +class DB_PUBLIC D25LayerInfo +{ +public: + D25LayerInfo (); + ~D25LayerInfo (); + D25LayerInfo (const D25LayerInfo &other); + D25LayerInfo &operator= (const D25LayerInfo &other); + + bool operator== (const D25LayerInfo &other) const; + + const db::LayerProperties &layer () const + { + return m_layer; + } + + void set_layer_from_string (const std::string &l); + std::string layer_as_string () const; + + void set_layer (const db::LayerProperties &l); + + double zstart () const + { + return m_zstart; + } + + void set_zstart (double z0); + + double zstop () const + { + return m_zstop; + } + + void set_zstop (double z1); + +private: + db::LayerProperties m_layer; + double m_zstart, m_zstop; +}; + +class DB_PUBLIC D25TechnologyComponent + : public db::TechnologyComponent +{ +public: + D25TechnologyComponent (); + D25TechnologyComponent (const D25TechnologyComponent &d); + + typedef std::list layers_type; + typedef layers_type::const_iterator const_iterator; + typedef layers_type::iterator iterator; + + void compile_from_source (const std::string &src); + + const_iterator begin () const + { + return m_layers.begin (); + } + + iterator begin () + { + return m_layers.begin (); + } + + const_iterator end () const + { + return m_layers.end (); + } + + iterator end () + { + return m_layers.end (); + } + + void clear () + { + m_layers.clear (); + } + + void erase (iterator p) + { + m_layers.erase (p); + } + + void insert (iterator p, const D25LayerInfo &info) + { + m_layers.insert (p, info); + } + + void add (const D25LayerInfo &info) + { + m_layers.push_back (info); + } + + size_t size () const + { + return m_layers.size (); + } + + const std::string &src () const + { + return m_src; + } + + // for persistency only, use "compile_from_source" to read from a source string + void set_src (const std::string &s) + { + m_src = s; + } + + std::string to_string () const; + + db::TechnologyComponent *clone () const + { + return new D25TechnologyComponent (*this); + } + +private: + layers_type m_layers; + std::string m_src; +}; + +} + +#endif diff --git a/src/db/unit_tests/dbD25TechnologyComponentTests.cc b/src/db/unit_tests/dbD25TechnologyComponentTests.cc new file mode 100644 index 000000000..181ccb92a --- /dev/null +++ b/src/db/unit_tests/dbD25TechnologyComponentTests.cc @@ -0,0 +1,73 @@ + +/* + + 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 "dbD25TechnologyComponent.h" +#include "tlUnitTest.h" + + +TEST(1) +{ + db::D25TechnologyComponent comp; + + comp.compile_from_source ("1/0: 1.0 1.5 # a comment"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); + + comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); + + comp.compile_from_source ("1/0: zstart=1.0 height=0.5"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); + + comp.compile_from_source ("1/0: 1.0 height=0.5"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); + + comp.compile_from_source ("1/0: zstop=1.5 height=0.5"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); + + comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5\nname: height=3"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=1.5, zstop=4.5"); + + comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5\nname: zstart=4.0 height=3\n\n# a comment line"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=4, zstop=7"); + + try { + comp.compile_from_source ("blabla"); + EXPECT_EQ (false, true); + } catch (...) { } + + try { + comp.compile_from_source ("1/0: 1 2 3"); + EXPECT_EQ (false, true); + } catch (...) { } + + try { + comp.compile_from_source ("1/0: foo=1 bar=2"); + EXPECT_EQ (false, true); + } catch (...) { } + + try { + comp.compile_from_source ("1/0: 1;2"); + EXPECT_EQ (false, true); + } catch (...) { } +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index d2753f09a..4efc7e5ad 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -34,6 +34,7 @@ SOURCES = \ dbPolygonToolsTests.cc \ dbTechnologyTests.cc \ dbStreamLayerTests.cc \ + dbD25TechnologyComponentTests.cc \ dbVectorTests.cc \ dbVariableWidthPathTests.cc \ dbTransTests.cc \ diff --git a/src/lay/lay/images/fit_back.png b/src/lay/lay/images/fit_back.png new file mode 100644 index 000000000..cbfbbad30 Binary files /dev/null and b/src/lay/lay/images/fit_back.png differ diff --git a/src/lay/lay/images/fit_bottom.png b/src/lay/lay/images/fit_bottom.png new file mode 100644 index 000000000..7308be3a2 Binary files /dev/null and b/src/lay/lay/images/fit_bottom.png differ diff --git a/src/lay/lay/images/fit_front.png b/src/lay/lay/images/fit_front.png new file mode 100644 index 000000000..5034b975b Binary files /dev/null and b/src/lay/lay/images/fit_front.png differ diff --git a/src/lay/lay/images/fit_left.png b/src/lay/lay/images/fit_left.png new file mode 100644 index 000000000..36dc09f5c Binary files /dev/null and b/src/lay/lay/images/fit_left.png differ diff --git a/src/lay/lay/images/fit_right.png b/src/lay/lay/images/fit_right.png new file mode 100644 index 000000000..d422ad54c Binary files /dev/null and b/src/lay/lay/images/fit_right.png differ diff --git a/src/lay/lay/images/fit_top.png b/src/lay/lay/images/fit_top.png new file mode 100644 index 000000000..843fd32d6 Binary files /dev/null and b/src/lay/lay/images/fit_top.png differ diff --git a/src/lay/lay/layResources.qrc b/src/lay/lay/layResources.qrc index 799f3f325..c3a136d87 100644 --- a/src/lay/lay/layResources.qrc +++ b/src/lay/lay/layResources.qrc @@ -127,6 +127,12 @@ images/folder_12.png images/file_12.png images/empty_12.png + images/fit_front.png + images/fit_back.png + images/fit_left.png + images/fit_right.png + images/fit_top.png + images/fit_bottom.png images/unlocked_16.png images/locked_16.png diff --git a/src/laybasic/laybasic/D25TechnologyComponentEditor.ui b/src/laybasic/laybasic/D25TechnologyComponentEditor.ui new file mode 100644 index 000000000..b1901ba63 --- /dev/null +++ b/src/laybasic/laybasic/D25TechnologyComponentEditor.ui @@ -0,0 +1,54 @@ + + + D25TechnologyComponentEditor + + + + 0 + 0 + 549 + 434 + + + + Settings + + + + + + 2.5d Vertical Stack Information + + + + + + + + 0 + 0 + + + + Line + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Monospace + + + + + + + + + + diff --git a/src/laybasic/laybasic/layD25TechnologyComponent.cc b/src/laybasic/laybasic/layD25TechnologyComponent.cc new file mode 100644 index 000000000..c004b4a1f --- /dev/null +++ b/src/laybasic/laybasic/layD25TechnologyComponent.cc @@ -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 "laybasicConfig.h" +#include "dbD25TechnologyComponent.h" +#include "layD25TechnologyComponent.h" + +#include +#include + +namespace lay +{ + +D25TechnologyComponentEditor::D25TechnologyComponentEditor (QWidget *parent) + : TechnologyComponentEditor (parent) +{ + setupUi (this); + + // TODO: activate_help_links (mp_ui->help_label); + + QResource res (tl::to_qstring (":/syntax/d25_text.xml")); + QByteArray data ((const char *) res.data (), int (res.size ())); + if (res.isCompressed ()) { + data = qUncompress (data); + } + + QBuffer input (&data); + input.open (QIODevice::ReadOnly); + mp_hl_basic_attributes.reset (new GenericSyntaxHighlighterAttributes ()); + mp_hl_attributes.reset (new GenericSyntaxHighlighterAttributes (mp_hl_basic_attributes.get ())); + lay::GenericSyntaxHighlighter *hl = new GenericSyntaxHighlighter (src_te, input, mp_hl_attributes.get ()); + input.close (); + + hl->setDocument (src_te->document ()); + + connect (src_te, SIGNAL (cursorPositionChanged ()), this, SLOT (cursor_position_changed ())); +} + +void +D25TechnologyComponentEditor::cursor_position_changed () +{ + int line = src_te->textCursor ().block ().firstLineNumber () + 1; + lnum_label->setText (tl::to_qstring (tl::sprintf (tl::to_string (tr ("Line %d")), line))); +} + +void +D25TechnologyComponentEditor::commit () +{ + db::D25TechnologyComponent *data = dynamic_cast (tech_component ()); + if (! data) { + return; + } + + std::string src = tl::to_string (src_te->toPlainText ()); + data->compile_from_source (src); +} + +void +D25TechnologyComponentEditor::setup () +{ + db::D25TechnologyComponent *data = dynamic_cast (tech_component ()); + if (! data) { + return; + } + + src_te->setPlainText (tl::to_qstring (data->src ())); +} + +class D25TechnologyComponentEditorProvider + : public lay::TechnologyEditorProvider +{ +public: + virtual lay::TechnologyComponentEditor *create_editor (QWidget *parent) const + { + return new D25TechnologyComponentEditor (parent); + } +}; + +static tl::RegisteredClass editor_decl (new D25TechnologyComponentEditorProvider (), 3100, "d25"); + +} // namespace lay + diff --git a/src/laybasic/laybasic/layD25TechnologyComponent.h b/src/laybasic/laybasic/layD25TechnologyComponent.h new file mode 100644 index 000000000..206838b70 --- /dev/null +++ b/src/laybasic/laybasic/layD25TechnologyComponent.h @@ -0,0 +1,57 @@ + +/* + + 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_layD25TechnologyComponent +#define HDR_layD25TechnologyComponent + +#include "ui_D25TechnologyComponentEditor.h" +#include "layTechnology.h" +#include "layGenericSyntaxHighlighter.h" + +#include + +namespace lay { + +class D25TechnologyComponentEditor + : public lay::TechnologyComponentEditor, + public Ui::D25TechnologyComponentEditor +{ +Q_OBJECT + +public: + D25TechnologyComponentEditor (QWidget *parent); + + void commit (); + void setup (); + +private slots: + void cursor_position_changed (); + +private: + std::auto_ptr mp_hl_attributes, mp_hl_basic_attributes; +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 2e3e2b4c3..e2fe22498 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -73,7 +73,8 @@ FORMS = \ NetInfoDialog.ui \ NetExportDialog.ui \ SelectCellViewForm.ui \ - LayoutStatistics.ui + LayoutStatistics.ui \ + D25TechnologyComponentEditor.ui RESOURCES = \ laybasicResources.qrc \ @@ -184,6 +185,7 @@ SOURCES = \ layDispatcher.cc \ laySelectCellViewForm.cc \ layLayoutStatisticsForm.cc \ + layD25TechnologyComponent.cc \ gsiDeclLayNetlistBrowserDialog.cc HEADERS = \ @@ -285,7 +287,8 @@ HEADERS = \ layGenericSyntaxHighlighter.h \ layDispatcher.h \ laySelectCellViewForm.h \ - layLayoutStatisticsForm.h + layLayoutStatisticsForm.h \ + layD25TechnologyComponent.h INCLUDEPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC DEPENDPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC diff --git a/src/laybasic/laybasic/laybasicResources.qrc b/src/laybasic/laybasic/laybasicResources.qrc index 86e08c975..f9243ccc4 100644 --- a/src/laybasic/laybasic/laybasicResources.qrc +++ b/src/laybasic/laybasic/laybasicResources.qrc @@ -45,5 +45,6 @@ images/icon_device_bjt_24.png images/icon_device_bjt_16.png syntax/ur_text.xml + syntax/d25_text.xml diff --git a/src/laybasic/laybasic/syntax/d25_text.xml b/src/laybasic/laybasic/syntax/d25_text.xml new file mode 100644 index 000000000..5e0877b74 --- /dev/null +++ b/src/laybasic/laybasic/syntax/d25_text.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui new file mode 100644 index 000000000..f09a1a654 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -0,0 +1,325 @@ + + + D25View + + + + 0 + 0 + 854 + 665 + + + + 2.5d View + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 150 + 16777215 + + + + -300 + + + 300 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + % + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 5 + 20 + + + + + + + + ... + + + + :/fit_left.png:/fit_left.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_front.png:/fit_front.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_right.png:/fit_right.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_back.png:/fit_back.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_top.png:/fit_top.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_bottom.png:/fit_bottom.png + + + + 24 + 24 + + + + true + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Press and hold SHIFT for top view + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + + lay::D25ViewWidget + QOpenGLWidget +
layD25ViewWidget.h
+
+
+ + + + + + buttonBox + rejected() + D25View + accept() + + + 530 + 626 + + + 443 + 643 + + + + +
diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc new file mode 100644 index 000000000..f2d358c8d --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc @@ -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 + +#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; +} + +} + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h new file mode 100644 index 000000000..06680ef47 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h @@ -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 +#include + +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 + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc new file mode 100644 index 000000000..dfe8fb146 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc @@ -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" + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h new file mode 100644 index 000000000..945d1338d --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/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 +#include + +#include "tlObject.h" +#include // for memcpy + +namespace lay +{ + +template +struct gl_type2enum +{ + GLenum operator() () const; +}; + +template <> +struct gl_type2enum +{ + 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 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 () (), GL_FALSE, 0, c->front ()); + ctx->glDrawArrays (mode, 0, c->size () / 3); + } + } + +private: + chunk *mp_chunks; + chunk *mp_last_chunk; +}; + +} + +#endif + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc new file mode 100644 index 000000000..c2042a2f4 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc @@ -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 + +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 > & /*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 &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 config_decl (new lay::D25PluginDeclaration (), 3100, "lay::D25Plugin"); + +} + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc new file mode 100644 index 000000000..fd8d57bac --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -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 + +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 (); +} + +} + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.h b/src/plugins/tools/view_25d/lay_plugin/layD25View.h new file mode 100644 index 000000000..6f776ed3c --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.h @@ -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 + +#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 mp_view; +}; + +} + +#endif + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc new file mode 100644 index 000000000..3fb6e8dca --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc @@ -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 +#include + +#include + +namespace lay +{ + +const double epsilon = 1e-10; + +std::pair +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 +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 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 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 +hit_point_with_cuboid (const QVector3D &line, const QVector3D &line_dir, const QVector3D &corner, const QVector3D &dim) +{ + std::vector > 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 >::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 +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); + } +} + +} diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.h new file mode 100644 index 000000000..b4bc140ea --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.h @@ -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 +#include +#include + +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 +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 +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 +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 +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 + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc new file mode 100644 index 000000000..ff9fc8f2a --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -0,0 +1,1119 @@ + +/* + + 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 "layD25ViewWidget.h" +#include "layD25ViewUtils.h" +#include "layLayoutView.h" + +#include "dbRecursiveShapeIterator.h" +#include "dbD25TechnologyComponent.h" +#include "dbEdgeProcessor.h" +#include "dbPolygonGenerators.h" +#include "dbPolygonTools.h" + +#include "tlException.h" + +#include +#include +#include + +#include "math.h" + +namespace lay +{ + +// ------------------------------------------------------------------------------ + +D25InteractionMode::D25InteractionMode (D25ViewWidget *view) + : mp_view (view) +{ + // .. nothing yet .. +} + +D25InteractionMode::~D25InteractionMode () +{ + // .. nothing yet .. +} + + +// ------------------------------------------------------------------------------ + +class D25PanInteractionMode + : public D25InteractionMode +{ +public: + D25PanInteractionMode (D25ViewWidget *widget, const QPoint &pos) + : D25InteractionMode (widget), m_start_pos (pos) + { + m_start_displacement = widget->displacement (); + + double px = (pos.x () - widget->width () / 2) * 2.0 / widget->width (); + double py = -(pos.y () - widget->height () / 2) * 2.0 / widget->height (); + + // compute vector of line of sight + std::pair ray = camera_normal (view ()->cam_perspective () * view ()->cam_trans (), px, py); + + // by definition the ray goes through the camera position + QVector3D hp = widget->hit_point_with_scene (ray.second); + + m_focus_dist = (widget->cam_position () - hp).length (); + } + + virtual ~D25PanInteractionMode () + { + // .. nothing yet .. + } + + virtual void mouse_move (QMouseEvent *event) + { + QPoint d = event->pos () - m_start_pos; + double f = tan ((view ()->cam_fov () / 2) / 180.0 * M_PI) * m_focus_dist * 2.0 / double (view ()->height ()); + double dx = d.x () * f; + double dy = -d.y () * f; + + QVector3D xv (cos (view ()->cam_azimuth () * M_PI / 180.0), 0.0, sin (view ()->cam_azimuth () * M_PI / 180.0)); + double re = sin (view ()->cam_elevation () * M_PI / 180.0); + QVector3D yv (-re * xv.z (), cos (view ()->cam_elevation () * M_PI / 180.0), re * xv.x ()); + QVector3D drag = xv * dx + yv * dy; + + view ()->set_displacement (m_start_displacement + drag / view ()->scale_factor ()); + } + +private: + QPoint m_start_pos; + double m_focus_dist; + QVector3D m_start_displacement; +}; + +// ------------------------------------------------------------------------------ + +class D25Rotate2DInteractionMode + : public D25InteractionMode +{ +public: + D25Rotate2DInteractionMode (D25ViewWidget *widget, const QPoint &pos) + : D25InteractionMode (widget), m_start_pos (pos) + { + m_start_cam_azimuth = widget->cam_azimuth (); + m_start_cam_elevation = widget->cam_elevation (); + } + + virtual ~D25Rotate2DInteractionMode () + { + // .. nothing yet .. + } + + virtual void mouse_move (QMouseEvent *event) + { + // fixed focus point for rotation + double focus_dist = 2.0; + + QPoint d = event->pos () - m_start_pos; + double f = tan ((view ()->cam_fov () / 2) / 180.0 * M_PI) * focus_dist * 2.0 / double (view ()->height ()); + double dx = d.x () * f; + double dy = -d.y () * f; + + double da = dx / (view ()->cam_dist () - focus_dist) * 180.0 / M_PI; + view ()->set_cam_azimuth (m_start_cam_azimuth + da); + + double de = dy / (view ()->cam_dist () - focus_dist) * 180.0 / M_PI; + view ()->set_cam_elevation (m_start_cam_elevation + de); + } + +private: + QPoint m_start_pos; + double m_start_cam_azimuth, m_start_cam_elevation; +}; + +// ------------------------------------------------------------------------------ + +class D25RotateAzimuthInteractionMode + : public D25InteractionMode +{ +public: + D25RotateAzimuthInteractionMode (D25ViewWidget *widget, const QPoint &pos) + : D25InteractionMode (widget), m_start_pos (pos) + { + // .. nothing yet .. + } + + virtual ~D25RotateAzimuthInteractionMode () + { + // .. nothing yet .. + } + + virtual void mouse_move (QMouseEvent *event) + { + // simple change of azimuth only - with center in the middle + + QPoint m = event->pos () - m_start_pos; + QVector3D p (m_start_pos.x () - view ()->width () / 2, -m_start_pos.y () + view ()->height () / 2, 0); + QVector3D d (m.x (), -m.y (), 0); + + double cp = QVector3D::crossProduct (p, p + d).z () / p.length () / (p + d).length (); + cp = std::max (-1.0, std::min (1.0, cp)); + double da = asin (cp) * 180.0 / M_PI; + + view ()->set_cam_azimuth (view ()->cam_azimuth () + da); + m_start_pos = event->pos (); + } + +private: + QPoint m_start_pos; +}; + +// ------------------------------------------------------------------------------ + +D25ViewWidget::D25ViewWidget (QWidget *parent) + : QOpenGLWidget (parent), + m_shapes_program (0) +{ + QSurfaceFormat format; + format.setDepthBufferSize (24); + format.setSamples (4); // more -> widget extends beyond boundary! + format.setStencilBufferSize (8); + format.setProfile (QSurfaceFormat::CoreProfile); + setFormat (format); + + m_zmin = m_zmax = 0.0; + mp_view = 0; +} + +D25ViewWidget::~D25ViewWidget () +{ + // Make sure the context is current and then explicitly + // destroy all underlying OpenGL resources. + makeCurrent (); + + delete m_shapes_program; + + doneCurrent (); +} + +void +D25ViewWidget::reset () +{ + m_scale_factor = 1.0; + mp_mode.reset (0); + + camera_reset (); +} + +void +D25ViewWidget::wheelEvent (QWheelEvent *event) +{ + if (event->angleDelta ().y () == 0) { + return; + } + + double px = (event->pos ().x () - width () / 2) * 2.0 / width (); + double py = -(event->pos ().y () - height () / 2) * 2.0 / height (); + + if (top_view ()) { + + // Plain zoom + + QVector3D hp (px, py, 0.0); + + double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8))); + + m_scale_factor *= f; + m_displacement += hp * (1.0 - f) / m_scale_factor; + + emit scale_factor_changed (m_scale_factor); + + } else { + + // compute vector of line of sight + std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); + + // by definition the ray goes through the camera position + QVector3D hp = hit_point_with_scene (ray.second); + + if (! (event->modifiers () & Qt::ControlModifier)) { + + // No Ctrl is closeup + + double f = event->angleDelta ().y () * (1.0 / (90 * 8)); + m_displacement += -((f / m_scale_factor) * std::min (cam_dist (), double ((cam_position () - hp).length ()))) * ray.second; + + } else { + + // "Ctrl" is zoom + + double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8))); + + QVector3D initial_displacement = m_displacement; + QVector3D displacement = m_displacement; + + m_scale_factor *= f; + displacement += hp * (1.0 - f) / m_scale_factor; + + // normalize the scene translation so the scene does not "flee" + + QMatrix4x4 ct = cam_trans (); + initial_displacement = ct.map (initial_displacement); + displacement = ct.map (displacement); + + lay::normalize_scene_trans (cam_perspective (), displacement, m_scale_factor, initial_displacement.z ()); + + m_displacement = ct.inverted ().map (displacement); + + emit scale_factor_changed (m_scale_factor); + + } + + } + + refresh (); +} + +void +D25ViewWidget::keyPressEvent (QKeyEvent *event) +{ + if (event->key () == Qt::Key_Shift) { + mp_mode.reset (0); + set_top_view (true); + } +} + +void +D25ViewWidget::keyReleaseEvent (QKeyEvent *event) +{ + if (event->key () == Qt::Key_Shift) { + mp_mode.reset (0); + set_top_view (false); + } +} + +QVector3D +D25ViewWidget::hit_point_with_scene (const QVector3D &line_dir) +{ + double min_focus_dist = 0.5; + + QVector3D corner = (QVector3D (m_bbox.left (), m_zmin, -(m_bbox.bottom () + m_bbox.height ())) + m_displacement) * m_scale_factor; + QVector3D dim = QVector3D (m_bbox.width (), m_zmax - m_zmin, m_bbox.height ()) * m_scale_factor; + QVector3D line = cam_position (); + + std::pair hp = lay::hit_point_with_cuboid (line, line_dir, corner, dim); + if (! hp.first) { + return line + line_dir * min_focus_dist; + } else if (QVector3D::dotProduct (line_dir, hp.second - line) < min_focus_dist) { + // limit to min focus distance (not behind) + return line + line_dir * min_focus_dist; + } else { + return hp.second; + } +} + +void +D25ViewWidget::mousePressEvent (QMouseEvent *event) +{ + mp_mode.reset (0); + + if (event->button () == Qt::MidButton) { + mp_mode.reset (new D25PanInteractionMode (this, event->pos ())); + } else if (event->button () == Qt::LeftButton) { + if (! top_view ()) { + mp_mode.reset (new D25Rotate2DInteractionMode (this, event->pos ())); + } else { + mp_mode.reset (new D25RotateAzimuthInteractionMode (this, event->pos ())); + } + } +} + +void +D25ViewWidget::mouseReleaseEvent (QMouseEvent * /*event*/) +{ + mp_mode.reset (0); +} + +void +D25ViewWidget::mouseMoveEvent (QMouseEvent *event) +{ + if (mp_mode.get ()) { + mp_mode->mouse_move (event); + } +} + +inline double square (double x) { return x * x; } + +void +D25ViewWidget::fit () +{ + // we pick a scale factor to adjust the z dimension roughly to 1/4 of the screen height at a focus distance of 2 + double norm_height = 0.5; + double in_focus = 2.0; + m_scale_factor = (tan (cam_fov () * M_PI / 180.0 / 2.0) * 2.0) * in_focus * norm_height / std::max (0.001, (m_zmax - m_zmin)); + + QVector3D dim = QVector3D (m_bbox.width (), m_zmax - m_zmin, m_bbox.height ()) * m_scale_factor; + QVector3D bll = QVector3D (m_bbox.left (), m_zmin, -(m_bbox.bottom () + m_bbox.height ())); + + // now we pick a displacement which moves the center to y = 0 and x, y in a way that the dimension covers the field of + // view and is centered. + // (we use the elliptic approximation which works exactly for azimuth angles which are a multiple of 90 degree) + + double dh = sqrt (square (cos (cam_azimuth () * M_PI / 180.0) * dim.x ()) + square (sin (cam_azimuth () * M_PI / 180.0) * dim.z ())); + double dv = sqrt (square (cos (cam_azimuth () * M_PI / 180.0) * dim.z ()) + square (sin (cam_azimuth () * M_PI / 180.0) * dim.x ())); + + double d = std::max (dh, fabs (sin (cam_elevation ()) * dv)); + + QVector3D new_center (0.0, 0.0, -dv / 2.0 + std::max (0.0, -d / (2.0 * tan (cam_fov () * M_PI / 180.0 / 2.0)) + cam_dist ())); + QVector3D new_center_in_scene = cam_trans ().inverted ().map (new_center); + + new_center_in_scene.setY (0.0); + + m_displacement = (new_center_in_scene - dim * 0.5) / m_scale_factor - bll; + + refresh (); + + emit scale_factor_changed (m_scale_factor); +} + +void +D25ViewWidget::refresh () +{ + update (); +} + +void +D25ViewWidget::camera_changed () +{ + refresh (); +} + +double +D25ViewWidget::aspect_ratio () const +{ + return double (width ()) / double (height ()); +} + +bool +D25ViewWidget::attach_view (LayoutView *view) +{ + bool any = false; + + if (mp_view != view) { + + mp_view = view; + + any = prepare_view (); + reset (); + + } + + return any; +} + +namespace { + + class ZDataCache + { + public: + ZDataCache () { } + + const db::D25LayerInfo *operator() (lay::LayoutView *view, int cv_index, int layer_index) + { + std::map >::const_iterator c = m_cache.find (cv_index); + if (c != m_cache.end ()) { + std::map::const_iterator l = c->second.find (layer_index); + if (l != c->second.end ()) { + return l->second; + } else { + return 0; + } + } + + std::map &lcache = m_cache [cv_index]; + + const db::D25TechnologyComponent *comp = 0; + + const lay::CellView &cv = view->cellview (cv_index); + if (cv.is_valid () && cv->technology ()) { + const db::Technology *tech = cv->technology (); + comp = dynamic_cast (tech->component_by_name ("d25")); + } + + if (comp) { + + std::map zi_by_lp; + for (db::D25TechnologyComponent::const_iterator i = comp->begin (); i != comp->end (); ++i) { + zi_by_lp.insert (std::make_pair (i->layer (), i.operator-> ())); + } + + const db::Layout &ly = cv->layout (); + for (int l = 0; l < int (ly.layers ()); ++l) { + if (ly.is_valid_layer (l)) { + const db::LayerProperties &lp = ly.get_properties (l); + std::map::const_iterator z = zi_by_lp.find (lp); + if (z == zi_by_lp.end () && ! lp.name.empty ()) { + // If possible, try by name only + z = zi_by_lp.find (db::LayerProperties (lp.name)); + } + if (z != zi_by_lp.end ()) { + lcache[l] = z->second; + } + } + } + + } + + return operator() (view, cv_index, layer_index); + + } + + + private: + std::map > m_cache; + }; + +} + +bool +D25ViewWidget::prepare_view () +{ + m_layers.clear (); + m_vertex_chunks.clear (); + + m_bbox = db::DBox (); + bool zset = false; + m_zmin = m_zmax = 0.0; + + if (! mp_view) { + m_bbox = db::DBox (-1.0, -1.0, 1.0, 1.0); + return false; + } + + ZDataCache zdata; + bool any = false; + + for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) { + + const db::D25LayerInfo *zi = 0; + if (! lp->has_children () && lp->visible (true)) { + zi = zdata (mp_view, lp->cellview_index (), lp->layer_index ()); + } + + if (zi) { + + any = true; + + double z0 = zi->zstart (); + double z1 = zi->zstop (); + + lay::color_t color = lp->fill_color (true); + + m_vertex_chunks.push_back (chunks_type ()); + + LayerInfo info; + info.color[0] = ((color >> 16) & 0xff) / 255.0f; + info.color[1] = ((color >> 8) & 0xff) / 255.0f; + info.color[2] = (color & 0xff) / 255.0f; + info.vertex_chunk = &m_vertex_chunks.back (); + + m_layers.push_back (info); + + const lay::CellView &cv = mp_view->cellview ((unsigned int) lp->cellview_index ()); + render_layout (m_vertex_chunks.back (), cv->layout (), *cv.cell (), (unsigned int) lp->layer_index (), z0, z1); + + m_bbox += db::DBox (cv.cell ()->bbox ((unsigned int) lp->layer_index ())) * cv->layout ().dbu (); + + if (! zset) { + m_zmin = z0; + m_zmax = z1; + zset = true; + } else { + m_zmin = std::min (z0, m_zmin); + m_zmax = std::max (z1, m_zmax); + } + + } + + } + + return any; +} + +void +D25ViewWidget::render_polygon (D25ViewWidget::chunks_type &chunks, const db::Polygon &poly, double dbu, double zstart, double zstop) +{ + if (poly.hull ().size () > 4) { + + std::vector poly_heap; + + db::split_polygon (poly, poly_heap); + for (std::vector::const_iterator p = poly_heap.begin (); p != poly_heap.end (); ++p) { + render_polygon (chunks, *p, dbu, zstart, zstop); + } + + } else if (poly.hull ().size () >= 3) { + + db::Point pts [4]; + std::copy (poly.hull ().begin (), poly.hull ().end (), &pts [0]); + + // triangle bottom + + chunks.add (pts[0].x () * dbu, zstart, pts[0].y () * dbu); + chunks.add (pts[2].x () * dbu, zstart, pts[2].y () * dbu); + chunks.add (pts[1].x () * dbu, zstart, pts[1].y () * dbu); + + // triangle top + chunks.add (pts[0].x () * dbu, zstop, pts[0].y () * dbu); + chunks.add (pts[1].x () * dbu, zstop, pts[1].y () * dbu); + chunks.add (pts[2].x () * dbu, zstop, pts[2].y () * dbu); + + if (poly.hull ().size () == 4) { + + // triangle bottom + chunks.add (pts[0].x () * dbu, zstart, pts[0].y () * dbu); + chunks.add (pts[3].x () * dbu, zstart, pts[3].y () * dbu); + chunks.add (pts[2].x () * dbu, zstart, pts[2].y () * dbu); + + // triangle top + chunks.add (pts[0].x () * dbu, zstop, pts[0].y () * dbu); + chunks.add (pts[2].x () * dbu, zstop, pts[2].y () * dbu); + chunks.add (pts[3].x () * dbu, zstop, pts[3].y () * dbu); + + } + + } +} + +void +D25ViewWidget::render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &edge, double dbu, double zstart, double zstop) +{ + chunks.add (edge.p1 ().x () * dbu, zstart, edge.p1 ().y () * dbu); + chunks.add (edge.p2 ().x () * dbu, zstop, edge.p2 ().y () * dbu); + chunks.add (edge.p1 ().x () * dbu, zstop, edge.p1 ().y () * dbu); + chunks.add (edge.p1 ().x () * dbu, zstart, edge.p1 ().y () * dbu); + chunks.add (edge.p2 ().x () * dbu, zstart, edge.p2 ().y () * dbu); + chunks.add (edge.p2 ().x () * dbu, zstop, edge.p2 ().y () * dbu); +} + +void +D25ViewWidget::render_layout (D25ViewWidget::chunks_type &chunks, const db::Layout &layout, const db::Cell &cell, unsigned int layer, double zstart, double zstop) +{ + db::EdgeProcessor ep; + std::vector poly_heap; + + // TODO: hidden cells, hierarchy depth ... + db::RecursiveShapeIterator s (layout, cell, layer); + s.shape_flags (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); + for ( ; ! s.at_end (); ++s) { + + db::Polygon polygon; + s->polygon (polygon); + polygon.transform (s.trans ()); + + if (polygon.holes () == 0 && polygon.hull ().size () <= 4) { + + render_polygon (chunks, polygon, layout.dbu (), zstart, zstop); + + for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + render_wall (chunks, *e, layout.dbu (), zstart, zstop); + } + + } else { + + poly_heap.clear (); + ep.clear (); + + ep.insert_sequence (polygon.begin_edge ()); + { + db::PolygonContainer pc (poly_heap); + db::PolygonGenerator out (pc, true /*resolve holes*/, false /*min coherence for splitting*/); + db::SimpleMerge op; + ep.process (out, op); + } + + for (std::vector::const_iterator p = poly_heap.begin (); p != poly_heap.end (); ++p) { + render_polygon (chunks, *p, layout.dbu (), zstart, zstop); + } + + poly_heap.clear (); + ep.clear (); + + ep.insert_sequence (polygon.begin_edge ()); + { + db::PolygonContainer pc (poly_heap); + db::PolygonGenerator out (pc, false /*don't resolve holes*/, false /*min coherence for splitting*/); + db::SimpleMerge op; + ep.process (out, op); + } + + for (std::vector::const_iterator p = poly_heap.begin (); p != poly_heap.end (); ++p) { + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + render_wall (chunks, *e, layout.dbu (), zstart, zstop); + } + } + + } + + } +} + +static std::pair find_grid (double v) +{ + for (int p = -12; p < 12; ++p) { + double g10 = pow (10, double (p)); + if (v > 100 * g10) { + continue; + } else if (v < 10 * g10) { + return std::make_pair (g10, g10); + } else if (v < 20 * g10) { + return std::make_pair (g10, g10 * 0.1); + } else if (v < 50 * g10) { + return std::make_pair (2.0 * g10, g10); + } else { + return std::make_pair (5.0 * g10, g10); + } + } + + return std::make_pair (v, v); +} + +void +D25ViewWidget::initializeGL () +{ + QOpenGLFunctions::initializeOpenGLFunctions(); + + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + static const char *shapes_vertex_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "layout (location = 0) in vec4 posAttr;\n" + "\n" + "void main() {\n" + " gl_Position = posAttr;\n" + "}\n"; + + static const char *shapes_geometry_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "\n" + "uniform vec4 color;\n" + "uniform vec4 ambient;\n" + "uniform vec3 illum;\n" + "out lowp vec4 vertexColor;\n" + "uniform mat4 matrix;\n" + "layout (triangles) in;\n" + "layout (triangle_strip, max_vertices = 3) out;\n" + "\n" + "void main() {\n" + " vec4 p0 = gl_in[0].gl_Position;\n" + " vec4 p1 = gl_in[1].gl_Position;\n" + " vec4 p2 = gl_in[2].gl_Position;\n" + " vec3 n = cross(p2.xyz - p0.xyz, p1.xyz - p0.xyz);\n" + " float dp = dot(normalize(n), illum);\n" + " vertexColor = color * (dp * 0.5 + 0.5) - (min(0.0, dp) * ambient);\n" + " vertexColor.a = 1.0;\n" + " gl_Position = matrix * p0;\n" + " EmitVertex();\n" + " gl_Position = matrix * p1;\n" + " EmitVertex();\n" + " gl_Position = matrix * p2;\n" + " EmitVertex();\n" + " EndPrimitive();\n" + "}\n"; + + static const char *shapes_fragment_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "in lowp vec4 vertexColor;\n" + "out lowp vec4 fragColor;\n" + "uniform highp float mist_factor;\n" + "uniform highp float mist_add;\n" + "\n" + "lowp vec4 color_by_z(lowp vec4 c, highp float z) {\n" + " highp float mist_rgb = c.g * mist_factor + mist_add;\n" + " lowp vec4 mist_color = vec4(mist_rgb, mist_rgb, mist_rgb, 1.0);\n" + " highp float d = 0.12;\n" // d + dd/2 = 0.15 = 1/? + " highp float dd = 0.06;\n" + " highp float f = 1.0;\n" + " if (z < d - dd) {\n" + " f = 0.0;\n" + " } else if (z < d + dd) {\n" + " f = (z - (d - dd)) / (2.0 * dd);\n" + " }\n" + " return (1.0 - f) * mist_color + f * c;\n" + "};\n" + "\n" + "void main() {\n" + " fragColor = color_by_z(vertexColor, gl_FragCoord.w);\n" + "}\n"; + + m_shapes_program = new QOpenGLShaderProgram (this); + if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Vertex, shapes_vertex_shader_source)) { + throw tl::Exception (std::string ("Shapes vertex shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); + } + if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Geometry, shapes_geometry_shader_source)) { + throw tl::Exception (std::string ("Shapes geometry shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); + } + if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Fragment, shapes_fragment_shader_source)) { + throw tl::Exception (std::string ("Shapes fragment shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); + } + if (! m_shapes_program->link ()) { + throw tl::Exception (std::string ("Shapes shader program linking failed failed:\n") + tl::to_string (m_shapes_program->log ())); + } + + // grid plane shader source + + static const char *gridplan_vertex_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "layout (location = 0) in vec4 posAttr;\n" + "uniform mat4 matrix;\n" + "\n" + "void main() {\n" + " gl_Position = matrix * posAttr;\n" + "}\n"; + + static const char *gridplan_fragment_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "uniform lowp vec4 color;\n" + "out lowp vec4 fragColor;\n" + "void main() {\n" + " fragColor = color;\n" + "}\n"; + + m_gridplane_program = new QOpenGLShaderProgram (this); + if (! m_gridplane_program->addShaderFromSourceCode (QOpenGLShader::Vertex, gridplan_vertex_shader_source)) { + throw tl::Exception (std::string ("Grid plane vertex shader compilation failed:\n") + tl::to_string (m_gridplane_program->log ())); + } + if (! m_gridplane_program->addShaderFromSourceCode (QOpenGLShader::Fragment, gridplan_fragment_shader_source)) { + throw tl::Exception (std::string ("Grid plane fragment shader compilation failed:\n") + tl::to_string (m_gridplane_program->log ())); + } + if (! m_gridplane_program->link ()) { + throw tl::Exception (std::string ("Grid plane shader program linking failed:\n") + tl::to_string (m_gridplane_program->log ())); + } +} + +void +D25ViewWidget::paintGL () +{ + const qreal retinaScale = devicePixelRatio (); + glViewport (0, 0, width () * retinaScale, height () * retinaScale); + + QColor c = mp_view->background_color (); + float foreground_rgb = (c.green () > 128 ? 0.0f : 1.0f); + float ambient = (c.green () > 128 ? 0.8f : 0.25f); + float mist_factor = (c.green () > 128 ? 0.2f : 0.4f); + float mist_add = (c.green () > 128 ? 0.8f : 0.2f); + glClearColor (float (c.red ()) / 255.0f, float (c.green ()) / 255.0f, float (c.blue ()) / 255.0f, 1.0); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + QMatrix4x4 scene_trans, scene_trans_wo_y; + + // provide the displacement and scaling (in this order!) + scene_trans.scale (m_scale_factor); + scene_trans.translate (m_displacement); + // this way we can use y as z coordinate when drawing + scene_trans.scale (1.0, 1.0, -1.0); + + scene_trans_wo_y = scene_trans; + scene_trans_wo_y.translate (QVector3D (0.0, -m_displacement.y (), 0.0)); + + const int positions = 0; + + m_shapes_program->bind (); + + // draw the actual layout + + m_shapes_program->setUniformValue ("matrix", cam_perspective () * cam_trans () * scene_trans); + + // NOTE: z axis of illum points towards the scene because we include the z inversion in the scene transformation matrix + m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, 2.0).normalized ()); + + m_shapes_program->setUniformValue ("ambient", QVector4D (ambient, ambient, ambient, 1.0)); + m_shapes_program->setUniformValue ("mist_factor", mist_factor); + m_shapes_program->setUniformValue ("mist_add", mist_add); + + glEnable (GL_DEPTH_TEST); + glEnableVertexAttribArray (positions); + + for (std::list::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { + m_shapes_program->setUniformValue ("color", l->color [0], l->color [1], l->color [2], l->color [3]); + l->vertex_chunk->draw_to (this, positions, GL_TRIANGLES); + } + + glDisableVertexAttribArray (positions); + + m_shapes_program->release (); + + // decoration + + // a vertex buffer for the decoration + lay::mem_chunks vertexes; + + m_gridplane_program->bind (); + + glEnable (GL_DEPTH_TEST); + glEnableVertexAttribArray (positions); + + // draw pivot compass + + m_gridplane_program->setUniformValue ("matrix", cam_perspective () * cam_trans ()); + + double compass_rad = 0.3; + double compass_bars = 0.4; + + vertexes.add (-compass_bars, 0.0, 0.0); + vertexes.add (compass_bars, 0.0, 0.0); + + vertexes.add (0.0, 0.0, -compass_bars); + vertexes.add (0.0, 0.0, compass_bars); + + int ncircle = 64; + double x = compass_rad, z = 0.0; + double da = 1.0 / double (ncircle) * M_PI * 2.0; + + for (int i = 0; i < ncircle; ++i) { + + double a = double (i + 1) * da; + double xx = compass_rad * cos (a); + double zz = compass_rad * sin (a); + + vertexes.add (x, 0.0, z); + vertexes.add (xx, 0.0, zz); + + x = xx; + z = zz; + + } + + m_gridplane_program->setUniformValue ("color", foreground_rgb, foreground_rgb, foreground_rgb, 0.25f); + + glLineWidth (2.0); + vertexes.draw_to (this, positions, GL_LINES); + + // arrow + + vertexes.clear (); + + vertexes.add (-0.25 * compass_rad, 0.0, 0.6 * compass_rad); + vertexes.add (0.0, 0.0, -0.8 * compass_rad); + vertexes.add (0.25 * compass_rad, 0.0, 0.6 * compass_rad); + + vertexes.draw_to (this, positions, GL_TRIANGLES); + + // draw base plane + + m_gridplane_program->setUniformValue ("matrix", cam_perspective () * cam_trans () * scene_trans_wo_y); + + std::pair gg = find_grid (std::max (m_bbox.width (), m_bbox.height ())); + double gminor = gg.second, gmajor = gg.first; + + double margin = std::max (m_bbox.width (), m_bbox.height ()) * 0.02; + + double l = m_bbox.left (); + double r = m_bbox.right (); + double b = m_bbox.bottom (); + double t = m_bbox.top (); + + // major and minor grid lines + + vertexes.clear (); + + const double epsilon = 1e-6; + + for (int major = 0; major < 2; ++major) { + + m_gridplane_program->setUniformValue ("color", foreground_rgb, foreground_rgb, foreground_rgb, major ? 0.25f : 0.15f); + + double x, y; + double step = (major ? gmajor : gminor); + + x = ceil (l / step) * step; + for ( ; x < r - step * epsilon; x += step) { + if ((fabs (floor (x / gmajor + 0.5) * gmajor - x) < epsilon) == (major != 0)) { + vertexes.add (x, 0.0, b - margin); + vertexes.add (x, 0.0, t + margin); + } + } + + y = ceil (b / step) * step; + for ( ; y < t - step * epsilon; y += step) { + if ((fabs (floor (y / gmajor + 0.5) * gmajor - y) < epsilon) == (major != 0)) { + vertexes.add (l - margin, 0.0, y); + vertexes.add (r + margin, 0.0, y); + } + } + + glLineWidth (2.0); + vertexes.draw_to (this, positions, GL_LINES); + + } + + // the plane itself + + vertexes.clear (); + + vertexes.add (l, 0.0, b); + vertexes.add (l, 0.0, t); + vertexes.add (r, 0.0, t); + + vertexes.add (l, 0.0, b); + vertexes.add (r, 0.0, b); + vertexes.add (r, 0.0, t); + + m_gridplane_program->setUniformValue ("color", foreground_rgb, foreground_rgb, foreground_rgb, 0.1f); + + vertexes.draw_to (this, positions, GL_TRIANGLES); + + // the orientation cube + + if (! top_view ()) { + + glDisable (GL_DEPTH_TEST); + + int cube_size = 64; + int cube_margin = 20; + + QMatrix4x4 into_top_right_corner; + into_top_right_corner.translate (1.0 - 2.0 / width () * (cube_margin + cube_size / 2), 1.0 - 2.0 / height () * (cube_margin + cube_size / 2)); + // into_top_right_corner.translate (0.5, 0.5, 0.0); + into_top_right_corner.scale (2.0 * cube_size / double (height ()), 2.0 * cube_size / double (height ()), 1.0); + + m_gridplane_program->setUniformValue ("matrix", into_top_right_corner * cam_perspective () * cam_trans ()); + + vertexes.clear (); + + vertexes.add (-1.0, -1.0, 1.0); + vertexes.add (-1.0, -1.0, -1.0); + + vertexes.add (-1.0, 1.0, 1.0); + vertexes.add (-1.0, 1.0, -1.0); + + vertexes.add (1.0, -1.0, 1.0); + vertexes.add (1.0, -1.0, -1.0); + + vertexes.add (1.0, 1.0, 1.0); + vertexes.add (1.0, 1.0, -1.0); + + vertexes.add (-1.0, -1.0, 1.0); + vertexes.add (-1.0, 1.0, 1.0); + + vertexes.add (1.0, -1.0, 1.0); + vertexes.add (1.0, 1.0, 1.0); + + vertexes.add (-1.0, -1.0, 1.0); + vertexes.add (1.0, -1.0, 1.0); + + vertexes.add (-1.0, 1.0, 1.0); + vertexes.add (1.0, 1.0, 1.0); + + vertexes.add (-1.0, -1.0, -1.0); + vertexes.add (-1.0, 1.0, -1.0); + + vertexes.add (1.0, -1.0, -1.0); + vertexes.add (1.0, 1.0, -1.0); + + vertexes.add (-1.0, -1.0, -1.0); + vertexes.add (1.0, -1.0, -1.0); + + vertexes.add (-1.0, 1.0, -1.0); + vertexes.add (1.0, 1.0, -1.0); + + m_gridplane_program->setUniformValue ("color", foreground_rgb, foreground_rgb, foreground_rgb, 0.2f); + + vertexes.draw_to (this, positions, GL_LINES); + + vertexes.clear (); + + // A "K" at the front + vertexes.add (-0.8, -0.8, 1.0); + vertexes.add (-0.8, 0.8, 1.0); + vertexes.add (-0.2, 0.8, 1.0); + + vertexes.add (-0.8, -0.8, 1.0); + vertexes.add (-0.2, -0.8, 1.0); + vertexes.add (-0.2, 0.8, 1.0); + + vertexes.add (0.2, 0.8, 1.0); + vertexes.add (0.8, 0.8, 1.0); + vertexes.add (0.8, 0.6, 1.0); + + vertexes.add (0.2, 0.8, 1.0); + vertexes.add (0.8, 0.6, 1.0); + vertexes.add (0.6, 0.4, 1.0); + + vertexes.add (-0.2, 0.4, 1.0); + vertexes.add (0.2, 0.8, 1.0); + vertexes.add (0.6, 0.4, 1.0); + + vertexes.add (-0.2, 0.4, 1.0); + vertexes.add (0.6, 0.4, 1.0); + vertexes.add (0.2, 0.0, 1.0); + + vertexes.add (-0.2, 0.4, 1.0); + vertexes.add (0.2, 0.0, 1.0); + vertexes.add (-0.2, -0.4, 1.0); + + vertexes.add (-0.2, -0.4, 1.0); + vertexes.add (0.6, -0.4, 1.0); + vertexes.add (0.2, -0.0, 1.0); + + vertexes.add (-0.2, -0.4, 1.0); + vertexes.add (0.2, -0.8, 1.0); + vertexes.add (0.6, -0.4, 1.0); + + vertexes.add (0.2, -0.8, 1.0); + vertexes.add (0.8, -0.6, 1.0); + vertexes.add (0.6, -0.4, 1.0); + + vertexes.add (0.2, -0.8, 1.0); + vertexes.add (0.8, -0.8, 1.0); + vertexes.add (0.8, -0.6, 1.0); + + m_gridplane_program->setUniformValue ("color", foreground_rgb, foreground_rgb, foreground_rgb, 0.3f); + + vertexes.draw_to (this, positions, GL_TRIANGLES); + + } + + glDisableVertexAttribArray (positions); + + m_shapes_program->release (); +} + +void +D25ViewWidget::resizeGL (int /*w*/, int /*h*/) +{ + refresh (); +} + +} + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h new file mode 100644 index 000000000..e23b068a5 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include + +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 chunks_type; + + std::auto_ptr 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 m_vertex_chunks; + + struct LayerInfo { + const chunks_type *vertex_chunk; + GLfloat color [4]; + }; + + std::list 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 + diff --git a/src/plugins/tools/view_25d/lay_plugin/layXORPlugin.cc b/src/plugins/tools/view_25d/lay_plugin/layXORPlugin.cc new file mode 100644 index 000000000..f5bd4895e --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layXORPlugin.cc @@ -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 > &options) const + { + options.push_back (std::pair (cfg_xor_input_mode, "all")); + options.push_back (std::pair (cfg_xor_output_mode, "rdb")); + options.push_back (std::pair (cfg_xor_nworkers, "1")); + options.push_back (std::pair (cfg_xor_layer_offset, "")); + options.push_back (std::pair (cfg_xor_axorb, "true")); + options.push_back (std::pair (cfg_xor_anotb, "false")); + options.push_back (std::pair (cfg_xor_bnota, "false")); + options.push_back (std::pair (cfg_xor_summarize, "false")); + options.push_back (std::pair (cfg_xor_tolerances, "")); + options.push_back (std::pair (cfg_xor_tiling, "")); + options.push_back (std::pair (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 &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 config_decl (new lay::XORPluginDeclaration (), 3000, "lay::XORPlugin"); + +} + diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro new file mode 100644 index 000000000..2ef786e9b --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -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 \ diff --git a/src/plugins/tools/view_25d/unit_tests/layD25CameraTests.cc b/src/plugins/tools/view_25d/unit_tests/layD25CameraTests.cc new file mode 100644 index 000000000..2934a9a9c --- /dev/null +++ b/src/plugins/tools/view_25d/unit_tests/layD25CameraTests.cc @@ -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"); +} diff --git a/src/plugins/tools/view_25d/unit_tests/layD25MemChunksTests.cc b/src/plugins/tools/view_25d/unit_tests/layD25MemChunksTests.cc new file mode 100644 index 000000000..5482762a0 --- /dev/null +++ b/src/plugins/tools/view_25d/unit_tests/layD25MemChunksTests.cc @@ -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 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::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 ch1; + ch1.add (1); + ch1.add (17); + ch1.add (42); + + lay::mem_chunks ch (ch1); + + lay::mem_chunks::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); +} diff --git a/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc b/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc new file mode 100644 index 000000000..4e428c818 --- /dev/null +++ b/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc @@ -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 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 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 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 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 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); +} diff --git a/src/plugins/tools/view_25d/unit_tests/unit_tests.pro b/src/plugins/tools/view_25d/unit_tests/unit_tests.pro new file mode 100644 index 000000000..c3e4e8e21 --- /dev/null +++ b/src/plugins/tools/view_25d/unit_tests/unit_tests.pro @@ -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 diff --git a/src/plugins/tools/view_25d/view_25d.pro b/src/plugins/tools/view_25d/view_25d.pro new file mode 100644 index 000000000..30278d0c8 --- /dev/null +++ b/src/plugins/tools/view_25d/view_25d.pro @@ -0,0 +1,6 @@ + +TEMPLATE = subdirs + +equals(HAVE_QT5, "1") { + SUBDIRS = lay_plugin unit_tests +}