Merge pull request #1019 from KLayout/25d-with-booleans

25d with booleans
This commit is contained in:
Matthias Köfferlein
2022-03-10 19:15:16 +01:00
committed by GitHub
50 changed files with 2326 additions and 1307 deletions
@@ -59,6 +59,42 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="rerun_button">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Execute script again</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/run.png</normaloff>:/run.png</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSlider" name="zoom_slider">
<property name="sizePolicy">
@@ -343,6 +379,9 @@
</item>
<item>
<widget class="QStackedWidget" name="gl_stack">
<property name="currentIndex">
<number>2</number>
</property>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
@@ -358,7 +397,60 @@
<number>0</number>
</property>
<item>
<widget class="lay::D25ViewWidget" name="d25_view"/>
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="lay::D25ViewWidget" name="d25_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
<widget class="QListWidget" name="material_list">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="spacing">
<number>0</number>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
@@ -419,6 +511,21 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page_3">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="empty_label">
<property name="text">
<string>In order to use the 2.5d view you will need a script which generates the view.&lt;br/&gt;
See here for more information: &lt;a href=&quot;int:/about/25d_view.xml&quot;&gt;The 2.5d View&lt;/a&gt;.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
@@ -482,6 +589,36 @@
</widget>
</item>
</layout>
<action name="select_all_action">
<property name="text">
<string>Select All</string>
</property>
</action>
<action name="unselect_all_action">
<property name="text">
<string>Unselect All</string>
</property>
</action>
<action name="show_all_action">
<property name="text">
<string>Show All</string>
</property>
</action>
<action name="hide_all_action">
<property name="text">
<string>Hide All</string>
</property>
</action>
<action name="show_selected_action">
<property name="text">
<string>Show Selected</string>
</property>
</action>
<action name="hide_selected_action">
<property name="text">
<string>Hide Selected</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
@@ -510,5 +647,37 @@
</hint>
</hints>
</connection>
<connection>
<sender>select_all_action</sender>
<signal>triggered()</signal>
<receiver>material_list</receiver>
<slot>selectAll()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>716</x>
<y>336</y>
</hint>
</hints>
</connection>
<connection>
<sender>unselect_all_action</sender>
<signal>triggered()</signal>
<receiver>material_list</receiver>
<slot>clearSelection()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>716</x>
<y>336</y>
</hint>
</hints>
</connection>
</connections>
</ui>
@@ -0,0 +1,288 @@
# $autorun-early
# $priority: 1
require 'pathname'
module D25
class D25ZInfo
attr_accessor :layer, :zstart, :zstop, :display
def initialize(_layer, _zstart, _zstop, _display)
self.layer = _layer
self.zstart = _zstart
self.zstop = _zstop
self.display = _display
end
end
class D25Display
attr_accessor :fill, :frame, :like, :name
def initialize
self.fill = nil
self.frame = nil
self.like = nil
self.name = nil
end
def set_fill(arg)
if !arg.is_a?(0xffffff.class)
raise("'fill' must be a color value (an integer)")
end
self.fill = arg
end
def set_frame(arg)
if !arg.is_a?(0xffffff.class)
raise("'frame' must be a color value (an integer)")
end
self.frame = arg
end
def set_color(arg)
if !arg.is_a?(0xffffff.class)
raise("'color' must be a color value (an integer)")
end
self.fill = arg
self.frame = nil
end
def set_like(arg)
li = nil
if arg.is_a?(String)
li = RBA::LayerInfo::from_string(arg)
elsif arg.is_a?(RBA::LayerInfo)
li = arg
else
raise("'like' must be a string or LayerInfo object")
end
self.like = li
end
end
# The D25 engine
class D25Engine < DRC::DRCEngine
def initialize
super
@current_z = 0.0
@zstack = []
# clip to layout view
if ! RBA::LayoutView::current
raise "No layout loaded for running 2.5d view on"
end
self.region_overlap(RBA::LayoutView::current.box)
end
def z(*args)
self._context("z") do
layer = nil
zstart = nil
zstop = nil
height = nil
display = D25Display::new
args.each do |a|
if a.is_a?(Range)
zstart = a.min
zstop = a.max
elsif a.is_a?(DRC::DRCLayer)
if layer
raise("Duplicate layer argument")
end
layer = a
if ! layer.data.is_a?(RBA::Region) && ! layer.data.is_a?(RBA::Edges) && ! layer.data.is_a?(RBA::EdgePairs)
raise("Expected a polygon, edge or edge pair layer")
end
elsif a.is_a?(1.class) || a.is_a?(1.0.class)
if height
raise("Duplicate height specification")
end
height = a
elsif a.is_a?(Hash)
if a[:height]
if height
raise("Duplicate height specification")
end
height = a[:height]
end
if a[:zstart]
if zstart
raise("Duplicate zstart specification")
end
zstart = a[:zstart]
end
if a[:zstop]
if zstop
raise("Duplicate zstop specification")
end
zstop = a[:zstop]
end
a[:color] && display.set_color(a[:color])
a[:frame] && display.set_frame(a[:frame])
a[:fill] && display.set_fill(a[:fill])
a[:like] && display.set_like(a[:like])
if a[:name]
display.name = a[:name].to_s
end
invalid_keys = a.keys.select { |k| ![ :height, :zstart, :zstop, :color, :frame, :fill, :like, :name ].member?(k) }
if invalid_keys.size > 0
raise("Keyword argument(s) not understood: #{invalid_keys.collect(&:to_s).join(',')}")
end
else
raise("Argument not understood: #{a.inspect}")
end
end
if ! zstart
zstart = @current_z
end
if ! zstop && ! height
raise("Either height or zstop must be specified")
elsif zstop && height
raise("Either height or zstop must be specified, not both")
end
if height
zstop = zstart + height
end
@current_z = zstop
if ! layer
raise("No layer specified")
end
info = D25ZInfo::new(layer, zstart, zstop, @display || display)
@zstack << info
return info
end
end
def zz(*args, &block)
begin
display = D25Display::new
@display = display
args.each do |a|
if a.is_a?(D25ZInfo)
@zstack.each do |z|
if z == a
z.display = display
end
end
elsif a.is_a?(Hash)
a[:color] && display.set_color(a[:color])
a[:frame] && display.set_frame(a[:frame])
a[:fill] && display.set_fill(a[:fill])
a[:like] && display.set_like(a[:like])
if a[:name]
display.name = a[:name].to_s
end
invalid_keys = a.keys.select { |k| ![ :fill, :frame, :color, :hollow, :like, :name ].member?(k) }
if invalid_keys.size > 0
raise("Keyword argument(s) not understood: #{invalid_keys.collect(&:to_s).join(',')}")
end
else
raise("Argument not understood: #{a.inspect}")
end
end
block && yield
ensure
@display = nil
end
end
def _check
if @zstack.empty?
raise("No z calls made in 2.5d script")
end
end
def _finish(final = true)
super(final)
if final
view = RBA::LayoutView::current.open_d25_view
begin
view.begin(self._generator)
displays = {}
@zstack.each do |z|
(displays[z.display.object_id] ||= []) << z
end
displays.each do |k,zz|
display = zz[0].display
view.open_display(display.frame, display.fill, display.like, display.name)
zz.each do |z|
view.entry(z.layer.data, self.dbu, z.zstart, z.zstop)
end
view.close_display
end
view.finish
rescue => ex
view.clear
view.close
raise ex
end
end
end
end
end
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description/>
<version/>
<category/>
<prolog/>
<epilog/>
<doc/>
<autorun>true</autorun>
<autorun-early>false</autorun-early>
<shortcut/>
<show-in-menu>false</show-in-menu>
<group-name/>
<menu-path/>
<interpreter>ruby</interpreter>
<dsl-interpreter-name/>
<text>
module D25
# Installs the home menu entries (needs to be done on autorun, not autorun-early)
if RBA::Application::instance &amp;&amp; RBA::Application::instance.main_window
cat = "d25"
name = "2.5d"
mw = RBA::Application::instance.main_window
mw.menu.insert_menu("tools_menu.verification_group+", "d25", "2.5d View")
@new_action = RBA::Action::new
@new_action.title = "New #{name} Script"
@new_action.on_triggered do
mw.show_macro_editor(cat, true)
end
mw.menu.insert_item("tools_menu.#{cat}.end", "new_script", @new_action)
@edit_action = RBA::Action::new
@edit_action.title = "Edit #{name} Script"
@edit_action.on_triggered do
mw.show_macro_editor(cat, false)
end
mw.menu.insert_item("tools_menu.#{cat}.end", "edit_script", @edit_action)
@open_action = RBA::Action::new
@open_action.title = "Open Window"
@open_action.on_triggered do
if ! RBA::LayoutView::current
RBA::MessageBox::critical("Error", "No layout loaded for running 2.5d view on", RBA::MessageBox::Ok)
else
RBA::LayoutView::current.open_d25_view
end
end
mw.menu.insert_item("tools_menu.#{cat}.end", "open_window", @open_action)
end
end
</text>
</klayout-macro>
@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description/>
<version/>
<category/>
<prolog/>
<epilog/>
<doc/>
<autorun>false</autorun>
<autorun-early>true</autorun-early>
<shortcut/>
<show-in-menu>false</show-in-menu>
<group-name/>
<menu-path/>
<interpreter>ruby</interpreter>
<dsl-interpreter-name/>
<text>
module D25
class D25Executable &lt; RBA::Executable
def initialize(macro, generator)
@d25 = D25Engine::new
@d25._generator = generator
@macro = macro
end
def execute
@d25._start("D25: " + @macro.path)
# Set a debugger scope so that our errors end up with the debugger set to the D25's line
RBA::MacroExecutionContext::set_debugger_scope(@macro.path)
begin
# No verbosity set in d25 engine - we cannot use the engine's logger
RBA::Logger::verbosity &gt;= 10 &amp;&amp; RBA::Logger::info("Running #{@macro.path}")
@d25.instance_eval(@macro.text, @macro.path)
rescue =&gt; ex
@d25.error("In #{@macro.path}: #{ex.to_s}")
RBA::MacroExecutionContext::ignore_next_exception
raise ex
end
@d25._check
nil
end
def cleanup
# Remove the debugger scope
RBA::MacroExecutionContext::remove_debugger_scope
# cleans up and creates layout and report views
@d25._finish
end
end
# A DSL implementation for a D25 language (XML format)
class D25Interpreter &lt; RBA::MacroInterpreter
# Constructor
def initialize(recipe)
@recipe = recipe
# Make the DSL use ruby syntax highlighting
self.syntax_scheme = "ruby"
self.suffix = "lyd25"
self.debugger_scheme = RBA::MacroInterpreter::RubyDebugger
self.storage_scheme = RBA::MacroInterpreter::MacroFormat
self.description = "D25"
# Registers the new interpreter
register("d25-dsl-xml")
# create a template for the macro editor:
create_template(":/d25-templates/d25.lym")
# if available, create a menu branch
if RBA::Application::instance &amp;&amp; RBA::Application::instance.main_window
mw = RBA::Application::instance.main_window
mw.menu.insert_menu("tools_menu.verification_group+", "d25", "2.5d View")
end
end
# Implements the execute method
def executable(macro)
D25Executable::new(macro, @recipe.generator("script" => macro.path))
end
end
# A DSL implementation for a D25 language (Plain text format)
class D25PlainTextInterpreter &lt; RBA::MacroInterpreter
# Constructor
def initialize(recipe)
@recipe = recipe
# Make the DSL use ruby syntax highlighting
self.syntax_scheme = "ruby"
self.suffix = "d25"
self.debugger_scheme = RBA::MacroInterpreter::RubyDebugger
self.storage_scheme = RBA::MacroInterpreter::PlainTextWithHashAnnotationsFormat
self.description = "D25 (Text)"
# Registers the new interpreter
register("d25-dsl")
end
# Implements the execute method
def executable(macro)
D25Executable::new(macro, @recipe.generator("script" => macro.path))
end
end
# A recipe implementation allowing the D25 run to be redone
class D25Recipe &lt; RBA::Recipe
def initialize
super("d25", "D25 recipe")
end
def executable(params)
script = params["script"]
if ! script
return
end
macro = RBA::Macro::macro_by_path(script)
macro || raise("Can't find D25 script #{script} - unable to re-run")
D25Executable::new(macro, self.generator("script" => script))
end
end
# Register the recipe
d25_recipe = D25Recipe::new
# Register the new interpreters
D25Interpreter::new(d25_recipe)
D25PlainTextInterpreter::new(d25_recipe)
# Creates a new macro category
if RBA::Application::instance
RBA::Application::instance.add_macro_category("d25", "2.5d View", [ "d25" ])
end
end
</text>
</klayout-macro>
@@ -0,0 +1,96 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2022 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "layD25View.h"
#include "layLayoutView.h"
#include "dbLayerProperties.h"
#include "dbRegion.h"
#include <limits>
#if defined(HAVE_QTBINDINGS)
# include "gsiQtGuiExternals.h"
# include "gsiQtWidgetsExternals.h"
FORCE_LINK_GSI_QTGUI
FORCE_LINK_GSI_QTWIDGETS // for Qt5
#else
# define QT_EXTERNAL_BASE(x)
#endif
namespace gsi
{
static lay::D25View *open_d25_view (lay::LayoutView *view)
{
return lay::D25View::open (view);
}
ClassExt<lay::LayoutView> decl_LayoutViewExt (
gsi::method_ext ("open_d25_view", &open_d25_view,
"@brief Opens the 2.5d view window and returns a reference to the D25View object.\n"
"This method has been introduced in version 0.28.\n"
)
);
Class<lay::D25View> decl_D25View (QT_EXTERNAL_BASE (QDialog) "lay", "D25View",
gsi::method ("clear", &lay::D25View::clear,
"@brief Clears all display entries in the view"
) +
gsi::method ("begin", &lay::D25View::begin, gsi::arg ("generator"),
"@brief Initiates delivery of display groups"
) +
gsi::method ("open_display", &lay::D25View::open_display, gsi::arg ("frame_color"), gsi::arg ("fill_color"), gsi::arg ("like"), gsi::arg ("name"),
"@brief Creates a new display group"
) +
gsi::method ("entry", &lay::D25View::entry, gsi::arg ("data"), gsi::arg ("dbu"), gsi::arg ("zstart"), gsi::arg ("zstop"),
"@brief Creates a new display entry in the group opened with \\open_display"
) +
gsi::method ("entry", &lay::D25View::entry_edge, gsi::arg ("data"), gsi::arg ("dbu"), gsi::arg ("zstart"), gsi::arg ("zstop"),
"@brief Creates a new display entry in the group opened with \\open_display"
) +
gsi::method ("entry", &lay::D25View::entry_edge_pair, gsi::arg ("data"), gsi::arg ("dbu"), gsi::arg ("zstart"), gsi::arg ("zstop"),
"@brief Creates a new display entry in the group opened with \\open_display"
) +
gsi::method ("close_display", &lay::D25View::close_display,
"@brief Finishes the display group"
) +
gsi::method ("finish", &lay::D25View::finish,
"@brief Finishes the view - call this after the display groups have been created"
) +
gsi::method ("close", &lay::D25View::close,
"@brief Closes the view"
),
"@brief The 2.5d View Dialog\n"
"\n"
"This class is used internally to implement the 2.5d feature.\n"
"\n"
"This class has been introduced in version 0.28."
);
}
@@ -52,10 +52,9 @@ public:
return 0;
}
virtual void get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const
virtual void get_menu_entries (std::vector<lay::MenuEntry> & /*menu_entries*/) const
{
lay::PluginDeclaration::get_menu_entries (menu_entries);
menu_entries.push_back (lay::menu_item ("lay::d25_view", "d25_view:edit", "tools_menu.post_verification_group", tl::to_string (QObject::tr ("2.5d View - experimental"))));
// .. nothing yet ..
}
virtual bool configure (const std::string & /*name*/, const std::string & /*value*/)
@@ -0,0 +1,10 @@
<RCC>
<qresource prefix="/built-in-macros">
<file alias="_d25_engine.rb">built-in-macros/_d25_engine.rb</file>
<file alias="d25_interpreters.lym">built-in-macros/d25_interpreters.lym</file>
<file alias="d25_install.lym">built-in-macros/d25_install.lym</file>
</qresource>
<qresource prefix="/d25-templates">
<file alias="d25.lym">templates/d25.lym</file>
</qresource>
</RCC>
@@ -20,6 +20,8 @@
*/
#include "tlExceptions.h"
#include "tlRecipe.h"
#include "layD25View.h"
#include "layLayoutView.h"
@@ -29,13 +31,17 @@
#include <stdio.h>
#include <QFontMetrics>
namespace lay
{
const double initial_elevation = 15.0;
D25View::D25View (lay::Dispatcher *root, lay::LayoutView *view)
: lay::Browser (root, view, "d25_view")
: lay::Browser (root, view, "d25_view"),
dm_rerun_macro (this, &D25View::rerun_macro),
dm_fit (this, &D25View::fit)
{
mp_ui = new Ui::D25View ();
mp_ui->setupUi (this);
@@ -43,26 +49,52 @@ D25View::D25View (lay::Dispatcher *root, lay::LayoutView *view)
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->vzoom_slider, SIGNAL (valueChanged (int)), this, SLOT (vscale_slider_changed (int)));
connect (mp_ui->zoom_factor, SIGNAL (editingFinished ()), this, SLOT (scale_value_edited ()));
connect (mp_ui->vzoom_factor, SIGNAL (editingFinished ()), this, SLOT (vscale_value_edited ()));
connect (mp_ui->d25_view, SIGNAL (scale_factor_changed (double)), this, SLOT (scale_factor_changed (double)));
connect (mp_ui->d25_view, SIGNAL (vscale_factor_changed (double)), this, SLOT (vscale_factor_changed (double)));
connect (mp_ui->d25_view, SIGNAL (init_failed ()), this, SLOT (init_failed ()));
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->vzoom_slider, SIGNAL (valueChanged(int)), this, SLOT (vscale_slider_changed(int)));
connect (mp_ui->zoom_factor, SIGNAL (editingFinished()), this, SLOT (scale_value_edited()));
connect (mp_ui->vzoom_factor, SIGNAL (editingFinished()), this, SLOT (vscale_value_edited()));
connect (mp_ui->d25_view, SIGNAL (scale_factor_changed(double)), this, SLOT (scale_factor_changed(double)));
connect (mp_ui->d25_view, SIGNAL (vscale_factor_changed(double)), this, SLOT (vscale_factor_changed(double)));
connect (mp_ui->d25_view, SIGNAL (init_failed()), this, SLOT (init_failed()));
connect (mp_ui->rerun_button, SIGNAL (clicked()), this, SLOT (rerun_button_pressed()));
connect (mp_ui->hide_all_action, SIGNAL (triggered()), this, SLOT (hide_all_triggered()));
connect (mp_ui->hide_selected_action, SIGNAL (triggered()), this, SLOT (hide_selected_triggered()));
connect (mp_ui->show_all_action, SIGNAL (triggered()), this, SLOT (show_all_triggered()));
connect (mp_ui->show_selected_action, SIGNAL (triggered()), this, SLOT (show_selected_triggered()));
mp_ui->gl_stack->setCurrentIndex (0);
mp_ui->gl_stack->setCurrentIndex (2);
mp_ui->rerun_button->setEnabled (false);
lay::activate_help_links (mp_ui->doc_label);
lay::activate_help_links (mp_ui->empty_label);
view->cellviews_changed_event.add (this, &D25View::cellviews_changed);
view->layer_list_changed_event.add (this, &D25View::layer_properties_changed);
QPalette palette = mp_ui->material_list->palette ();
palette.setColor (QPalette::Base, Qt::black);
palette.setColor (QPalette::Text, Qt::white);
mp_ui->material_list->setPalette (palette);
QFont font = mp_ui->material_list->font ();
font.setWeight (QFont::Bold);
mp_ui->material_list->setFont (font);
mp_ui->material_list->addAction (mp_ui->select_all_action);
mp_ui->material_list->addAction (mp_ui->unselect_all_action);
mp_ui->material_list->addAction (mp_ui->show_all_action);
mp_ui->material_list->addAction (mp_ui->show_selected_action);
mp_ui->material_list->addAction (mp_ui->hide_all_action);
mp_ui->material_list->addAction (mp_ui->hide_selected_action);
mp_ui->material_list->setContextMenuPolicy (Qt::ActionsContextMenu);
connect (mp_ui->material_list, SIGNAL (itemChanged (QListWidgetItem *)), this, SLOT (material_item_changed (QListWidgetItem *)));
}
D25View::~D25View ()
@@ -84,7 +116,7 @@ D25View::cellviews_changed ()
void
D25View::layer_properties_changed (int)
{
mp_ui->d25_view->refresh_view ();
// .. nothing yet ..
}
void
@@ -113,6 +145,164 @@ D25View::menu_activated (const std::string &symbol)
}
}
D25View *
D25View::open (lay::LayoutView *view)
{
D25View *d25_view = view->get_plugin<lay::D25View> ();
if (d25_view) {
d25_view->show ();
d25_view->activateWindow ();
d25_view->raise ();
try {
d25_view->activate ();
} catch (...) {
d25_view->deactivate ();
throw;
}
}
return d25_view;
}
void
D25View::close ()
{
hide ();
}
void
D25View::clear ()
{
if (! mp_ui->d25_view->has_error ()) {
mp_ui->gl_stack->setCurrentIndex (2);
mp_ui->d25_view->clear ();
}
mp_ui->rerun_button->setEnabled (false);
m_generator.clear ();
}
void
D25View::begin (const std::string &generator)
{
clear ();
if (! mp_ui->d25_view->has_error ()) {
m_generator = generator;
}
}
void
D25View::open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name)
{
if (! mp_ui->d25_view->has_error ()) {
mp_ui->d25_view->open_display (frame_color, fill_color, like, name);
}
}
void
D25View::close_display ()
{
if (! mp_ui->d25_view->has_error ()) {
mp_ui->d25_view->close_display ();
}
}
void
D25View::entry (const db::Region &data, double dbu, double zstart, double zstop)
{
if (! mp_ui->d25_view->has_error ()) {
mp_ui->d25_view->entry (data, dbu, zstart, zstop);
}
}
void
D25View::entry_edge (const db::Edges &data, double dbu, double zstart, double zstop)
{
if (! mp_ui->d25_view->has_error ()) {
mp_ui->d25_view->entry (data, dbu, zstart, zstop);
}
}
void
D25View::entry_edge_pair (const db::EdgePairs &data, double dbu, double zstart, double zstop)
{
if (! mp_ui->d25_view->has_error ()) {
mp_ui->d25_view->entry (data, dbu, zstart, zstop);
}
}
static void layer_info_to_item (const lay::D25ViewWidget::LayerInfo &info, QListWidgetItem *item, size_t index, QSize icon_size)
{
if (info.has_name) {
item->setText (tl::to_qstring (info.name));
} else {
item->setText (tl::to_qstring ("#" + tl::to_string (index + 1)));
}
QImage img (icon_size, QImage::Format_ARGB32);
img.fill (QColor (floor (info.fill_color [0] * 255 + 0.5), floor (info.fill_color [1] * 255 + 0.5), floor (info.fill_color [2] * 255 + 0.5), floor (info.fill_color [3] * 255 + 0.5)));
QColor fc (floor (info.frame_color [0] * 255 + 0.5), floor (info.frame_color [1] * 255 + 0.5), floor (info.frame_color [2] * 255 + 0.5), floor (info.frame_color [3] * 255 + 0.5));
if (fc.alpha () > 0) {
QRgb fc_rgb = fc.rgba ();
for (int x = 0; x < icon_size.width (); ++x) {
img.setPixel (x, 0, fc_rgb);
img.setPixel (x, icon_size.height () - 1, fc_rgb);
}
for (int y = 0; y < icon_size.height (); ++y) {
img.setPixel (0, y, fc_rgb);
img.setPixel (icon_size.width () - 1, y, fc_rgb);
}
}
QIcon icon;
icon.addPixmap (QPixmap::fromImage (img));
item->setIcon (icon);
}
void
D25View::finish ()
{
if (! mp_ui->d25_view->has_error ()) {
mp_ui->d25_view->finish ();
QFontMetrics fm (mp_ui->material_list->font ());
QSize icon_size = fm.size (Qt::TextSingleLine, "WW");
icon_size.setHeight (icon_size.height () - 2);
mp_ui->material_list->setIconSize (icon_size);
mp_ui->material_list->clear ();
const std::vector<lay::D25ViewWidget::LayerInfo> &layers = mp_ui->d25_view->layers ();
for (auto l = layers.begin (); l != layers.end (); ++l) {
QListWidgetItem *item = new QListWidgetItem (mp_ui->material_list);
item->setFlags (item->flags () | Qt::ItemIsUserCheckable);
item->setCheckState (Qt::Checked);
layer_info_to_item (*l, item, l - layers.begin (), icon_size);
}
mp_ui->d25_view->reset ();
mp_ui->d25_view->set_cam_azimuth (0.0);
mp_ui->d25_view->set_cam_elevation (-initial_elevation);
// NOTE: needs to be delayed to allow the geometry to be updated before (initial call)
dm_fit ();
mp_ui->rerun_button->setEnabled (true);
mp_ui->gl_stack->setCurrentIndex (0);
}
}
void
D25View::fit ()
{
mp_ui->d25_view->fit ();
}
static QString scale_factor_to_string (double f)
{
return QString (QString::fromUtf8 ("%1")).arg (f, 0, 'g', 3);
@@ -123,6 +313,7 @@ D25View::init_failed ()
{
mp_ui->error_text->setPlainText (tl::to_qstring (mp_ui->d25_view->error ()));
mp_ui->gl_stack->setCurrentIndex (1);
mp_ui->rerun_button->setEnabled (false);
}
void
@@ -189,6 +380,15 @@ D25View::vscale_factor_changed (double f)
mp_ui->vzoom_slider->blockSignals (false);
}
void
D25View::material_item_changed (QListWidgetItem *item)
{
int index = mp_ui->material_list->row (item);
if (index >= 0) {
mp_ui->d25_view->set_material_visible (size_t (index), item->checkState () == Qt::Checked);
}
}
void
D25View::deactivated ()
{
@@ -198,18 +398,33 @@ D25View::deactivated ()
void
D25View::activated ()
{
bool any = mp_ui->d25_view->attach_view (view ());
if (! any) {
mp_ui->d25_view->attach_view (0);
throw tl::Exception (tl::to_string (tr ("No z data configured for the layers in this view.\nUse \"Tools/Manage Technologies\" to set up a z stack or check if it applies to the layers here.")));
}
mp_ui->d25_view->attach_view (view ());
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 ();
}
void
D25View::rerun_button_pressed ()
{
// NOTE: we use deferred execution, because otherwise the button won't get repainted properly
dm_rerun_macro ();
}
void
D25View::rerun_macro ()
{
BEGIN_PROTECTED
if (! m_generator.empty ()) {
std::map<std::string, tl::Variant> add_pars;
tl::Recipe::make (m_generator, add_pars);
}
END_PROTECTED
}
void
D25View::fit_button_clicked ()
{
@@ -242,6 +457,42 @@ D25View::fit_button_clicked ()
mp_ui->d25_view->fit ();
}
void
D25View::hide_all_triggered ()
{
for (int i = 0; i < mp_ui->material_list->count (); ++i) {
mp_ui->material_list->item (i)->setCheckState (Qt::Unchecked);
}
}
void
D25View::hide_selected_triggered ()
{
for (int i = 0; i < mp_ui->material_list->count (); ++i) {
if (mp_ui->material_list->item (i)->isSelected ()) {
mp_ui->material_list->item (i)->setCheckState (Qt::Unchecked);
}
}
}
void
D25View::show_all_triggered ()
{
for (int i = 0; i < mp_ui->material_list->count (); ++i) {
mp_ui->material_list->item (i)->setCheckState (Qt::Checked);
}
}
void
D25View::show_selected_triggered ()
{
for (int i = 0; i < mp_ui->material_list->count (); ++i) {
if (mp_ui->material_list->item (i)->isSelected ()) {
mp_ui->material_list->item (i)->setCheckState (Qt::Checked);
}
}
}
void
D25View::accept ()
{
@@ -24,9 +24,11 @@
#define HDR_layD25View
#include <QDialog>
#include <QListWidgetItem>
#include "tlObject.h"
#include "layBrowser.h"
#include "layViewOp.h"
namespace Ui
{
@@ -38,6 +40,14 @@ namespace lay
class LayoutView;
}
namespace db
{
class Region;
class Edges;
class EdgePairs;
struct LayerProperties;
}
namespace lay
{
@@ -54,6 +64,17 @@ public:
virtual void deactivated ();
virtual void activated ();
static D25View *open (lay::LayoutView *view);
void close ();
void clear ();
void begin (const std::string &generator);
void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name);
void close_display ();
void entry (const db::Region &data, double dbu, double zstart, double zstop);
void entry_edge (const db::Edges &data, double dbu, double zstart, double zstop);
void entry_edge_pair (const db::EdgePairs &data, double dbu, double zstart, double zstop);
void finish ();
protected:
void accept ();
void reject ();
@@ -67,12 +88,23 @@ private slots:
void vscale_slider_changed (int value);
void vscale_value_edited ();
void init_failed ();
void rerun_button_pressed ();
void material_item_changed (QListWidgetItem *);
void hide_all_triggered ();
void hide_selected_triggered ();
void show_all_triggered ();
void show_selected_triggered ();
private:
Ui::D25View *mp_ui;
tl::DeferredMethod<D25View> dm_rerun_macro;
tl::DeferredMethod<D25View> dm_fit;
std::string m_generator;
void cellviews_changed ();
void layer_properties_changed (int);
void rerun_macro ();
void fit ();
};
}
@@ -26,11 +26,16 @@
#include "layLayoutView.h"
#include "dbRecursiveShapeIterator.h"
#include "dbD25TechnologyComponent.h"
#include "dbEdgeProcessor.h"
#include "dbPolygonGenerators.h"
#include "dbPolygonTools.h"
#include "dbClip.h"
#include "dbRegion.h"
#include "dbEdges.h"
#include "dbEdgePairs.h"
#include "dbOriginalLayerRegion.h"
#include "dbOriginalLayerEdges.h"
#include "dbOriginalLayerEdgePairs.h"
#include "tlException.h"
#include "tlProgress.h"
@@ -200,7 +205,10 @@ D25ViewWidget::D25ViewWidget (QWidget *parent)
setFormat (format);
m_zmin = m_zmax = 0.0;
m_zset = false;
m_display_open = false;
mp_view = 0;
m_has_error = false;
reset_viewport ();
}
@@ -477,6 +485,15 @@ D25ViewWidget::refresh ()
update ();
}
void
D25ViewWidget::set_material_visible (size_t index, bool visible)
{
if (index < m_layers.size () && m_layers [index].visible != visible) {
m_layers [index].visible = visible;
update ();
}
}
void
D25ViewWidget::showEvent (QShowEvent *)
{
@@ -496,86 +513,22 @@ D25ViewWidget::aspect_ratio () const
return double (width ()) / double (height ());
}
bool
D25ViewWidget::attach_view (LayoutView *view)
void
D25ViewWidget::clear ()
{
mp_view = view;
m_layers.clear ();
m_vertex_chunks.clear ();
m_line_chunks.clear ();
bool any = prepare_view ();
reset ();
return any;
}
namespace {
class ZDataCache
{
public:
ZDataCache () { }
std::vector<db::D25LayerInfo> operator() (lay::LayoutView *view, int cv_index, int layer_index)
{
std::map<int, std::map<int, std::vector<db::D25LayerInfo> > >::const_iterator c = m_cache.find (cv_index);
if (c != m_cache.end ()) {
std::map<int, std::vector<db::D25LayerInfo> >::const_iterator l = c->second.find (layer_index);
if (l != c->second.end ()) {
return l->second;
} else {
return std::vector<db::D25LayerInfo> ();
}
}
std::map<int, std::vector<db::D25LayerInfo> > &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<const db::D25TechnologyComponent *> (tech->component_by_name ("d25"));
}
if (comp) {
std::multimap<db::LayerProperties, db::D25LayerInfo, db::LPLogicalLessFunc> zi_by_lp;
db::D25TechnologyComponent::layers_type layers = comp->compile_from_source ();
for (db::D25TechnologyComponent::layers_type::const_iterator i = layers.begin (); i != layers.end (); ++i) {
zi_by_lp.insert (std::make_pair (i->layer (), *i));
}
const db::Layout &ly = cv->layout ();
for (int l = 0; l < int (ly.layers ()); ++l) {
if (ly.is_valid_layer (l)) {
db::LayerProperties lp = ly.get_properties (l);
std::multimap<db::LayerProperties, db::D25LayerInfo, db::LPLogicalLessFunc>::const_iterator z = zi_by_lp.find (lp);
if ((z == zi_by_lp.end () || ! z->first.log_equal (lp)) && ! lp.name.empty ()) {
// If possible, try by name only
lp = db::LayerProperties (lp.name);
z = zi_by_lp.find (lp);
}
while (z != zi_by_lp.end () && z->first.log_equal (lp)) {
lcache[l].push_back (z->second);
++z;
}
}
}
}
std::map<int, std::vector<db::D25LayerInfo> >::const_iterator l = lcache.find (layer_index);
if (l != lcache.end ()) {
return l->second;
} else {
return std::vector<db::D25LayerInfo> ();
}
}
private:
std::map<int, std::map<int, std::vector<db::D25LayerInfo> > > m_cache;
};
m_zset = false;
m_zmin = m_zmax = 0.0;
m_display_open = false;
if (! mp_view) {
m_bbox = db::DBox (-1.0, -1.0, 1.0, 1.0);
} else {
m_bbox = mp_view->viewport ().box ();
}
}
static void color_to_gl (color_t color, GLfloat (&gl_color) [4])
@@ -586,130 +539,181 @@ static void color_to_gl (color_t color, GLfloat (&gl_color) [4])
gl_color[3] = 1.0f;
}
void
D25ViewWidget::lp_to_info (const lay::LayerPropertiesNode &lp, LayerInfo &info)
static void color_to_gl (const color_t *color, GLfloat (&gl_color) [4])
{
color_to_gl (lp.fill_color (true), info.color);
if (! color) {
for (unsigned int i = 0; i < 4; ++i) {
gl_color [i] = 0.0;
}
} else {
color_to_gl (*color, gl_color);
}
}
static void lp_to_info (const lay::LayerPropertiesNode &lp, D25ViewWidget::LayerInfo &info)
{
color_to_gl (lp.fill_color (true), info.fill_color);
if (lp.dither_pattern (true) == 1 /*hollow*/) {
info.color [3] = 0.0f;
info.fill_color [3] = 0.0f;
}
color_to_gl (lp.frame_color (true), info.frame_color);
if (lp.frame_color (true) == lp.fill_color (true) && info.color [3] > 0.5) {
if (lp.frame_color (true) == lp.fill_color (true) && info.fill_color [3] > 0.5) {
// optimize: don't draw wire frame unless required
info.frame_color [3] = 0.0f;
}
info.visible = lp.visible (true);
}
bool
D25ViewWidget::prepare_view ()
{
m_layers.clear ();
m_layer_to_info.clear ();
m_vertex_chunks.clear ();
m_line_chunks.clear ();
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;
}
m_bbox = mp_view->viewport ().box ();
ZDataCache zdata;
// collect and confine to cell bbox
db::DBox cell_bbox;
for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) {
std::vector<db::D25LayerInfo> zinfo;
if (! lp->has_children ()) {
zinfo = zdata (mp_view, lp->cellview_index (), lp->layer_index ());
}
for (std::vector<db::D25LayerInfo>::const_iterator zi = zinfo.begin (); zi != zinfo.end (); ++zi) {
const lay::CellView &cv = mp_view->cellview ((unsigned int) lp->cellview_index ());
cell_bbox += db::CplxTrans (cv->layout ().dbu ()) * cv.cell ()->bbox ((unsigned int) lp->layer_index ());
}
}
bool any = false;
tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ...")));
for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) {
std::vector<db::D25LayerInfo> zinfo;
if (! lp->has_children ()) {
zinfo = zdata (mp_view, lp->cellview_index (), lp->layer_index ());
}
for (std::vector<db::D25LayerInfo>::const_iterator zi = zinfo.begin (); zi != zinfo.end (); ++zi) {
any = true;
double z0 = zi->zstart ();
double z1 = zi->zstop ();
m_vertex_chunks.push_back (triangle_chunks_type ());
m_line_chunks.push_back (line_chunks_type ());
LayerInfo info;
lp_to_info (*lp, info);
info.vertex_chunk = &m_vertex_chunks.back ();
info.line_chunk = &m_line_chunks.back ();
m_layer_to_info.insert (std::make_pair (std::make_pair (lp->cellview_index (), lp->layer_index ()), m_layers.size ()));
m_layers.push_back (info);
const lay::CellView &cv = mp_view->cellview ((unsigned int) lp->cellview_index ());
render_layout (progress, m_vertex_chunks.back (), m_line_chunks.back (), cv->layout (), *cv.cell (), db::CplxTrans (cv->layout ().dbu ()).inverted () * m_bbox, (unsigned int) lp->layer_index (), z0, z1);
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;
info.visible = true;
}
void
D25ViewWidget::refresh_view ()
D25ViewWidget::open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name)
{
if (! mp_view) {
return;
m_vertex_chunks.push_back (triangle_chunks_type ());
m_line_chunks.push_back (line_chunks_type ());
LayerInfo info;
info.visible = true;
color_to_gl (frame_color, info.frame_color);
color_to_gl (fill_color, info.fill_color);
info.has_name = (name != 0 || like != 0);
if (name) {
info.name = *name;
} else if (like) {
info.name = like->to_string ();
}
for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) {
info.vertex_chunk = &m_vertex_chunks.back ();
info.line_chunk = &m_line_chunks.back ();
std::pair<size_t, size_t> key = std::make_pair (lp->cellview_index (), lp->layer_index ());
std::multimap<std::pair<size_t, size_t>, size_t>::const_iterator l = m_layer_to_info.find (key);
while (l != m_layer_to_info.end () && l->first == key) {
if (l->second < m_layers.size ()) {
lp_to_info (*lp, m_layers [l->second]);
if (like && mp_view) {
for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) {
if (! lp->has_children () && lp->source (true).layer_props ().log_equal (*like)) {
lp_to_info (*lp, info);
break;
}
++l;
}
}
m_layers.push_back (info);
m_display_open = true;
}
void
D25ViewWidget::close_display ()
{
m_display_open = false;
}
void
D25ViewWidget::enter (const db::RecursiveShapeIterator *iter, double zstart, double zstop)
{
tl_assert (m_display_open);
if (! m_zset) {
m_zmin = std::min (zstart, zstop);
m_zmax = std::max (zstart, zstop);
m_zset = true;
} else {
m_zmin = std::min (m_zmin, std::min (zstart, zstop));
m_zmax = std::min (m_zmax, std::max (zstart, zstop));
}
LayerInfo &info = m_layers.back ();
// try to establish a default color from the region's origin if required
if (mp_view && info.fill_color [3] == 0.0 && info.frame_color [3] == 0.0) {
if (iter) {
if (iter && iter->layout () && iter->layout ()->is_valid_layer (iter->layer ())) {
db::LayerProperties like = iter->layout ()->get_properties (iter->layer ());
for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) {
if (! lp->has_children () && lp->source (true).layer_props ().log_equal (like)) {
lp_to_info (*lp, info);
if (! info.has_name) {
info.name = like.to_string ();
info.has_name = true;
}
break;
}
}
}
} else {
// sequential assignment
lay::color_t color = mp_view->get_palette ().luminous_color_by_index (m_layers.size ());
color_to_gl (color, info.fill_color);
}
}
}
refresh ();
void
D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double zstop)
{
// try to establish a default color from the region's origin if required
const db::RecursiveShapeIterator *iter = 0;
const db::OriginalLayerRegion *original = dynamic_cast<db::OriginalLayerRegion *> (data.delegate ());
if (original) {
iter = original->iter ();
}
enter (iter, zstart, zstop);
tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ...")));
render_region (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop);
}
void
D25ViewWidget::entry (const db::Edges &data, double dbu, double zstart, double zstop)
{
// try to establish a default color from the region's origin if required
const db::RecursiveShapeIterator *iter = 0;
const db::OriginalLayerEdges *original = dynamic_cast<db::OriginalLayerEdges *> (data.delegate ());
if (original) {
iter = original->iter ();
}
enter (iter, zstart, zstop);
tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ...")));
render_edges (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop);
}
void
D25ViewWidget::entry (const db::EdgePairs &data, double dbu, double zstart, double zstop)
{
// try to establish a default color from the region's origin if required
const db::RecursiveShapeIterator *iter = 0;
const db::OriginalLayerEdgePairs *original = dynamic_cast<db::OriginalLayerEdgePairs *> (data.delegate ());
if (original) {
iter = original->iter ();
}
enter (iter, zstart, zstop);
tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ...")));
render_edge_pairs (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop);
}
void
D25ViewWidget::finish ()
{
// .. nothing yet ..
}
void
D25ViewWidget::attach_view (LayoutView *view)
{
mp_view = view;
}
void
@@ -793,31 +797,23 @@ D25ViewWidget::render_wall (D25ViewWidget::triangle_chunks_type &chunks, D25View
}
void
D25ViewWidget::render_layout (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Layout &layout, const db::Cell &cell, const db::Box &clip_box, unsigned int layer, double zstart, double zstop)
D25ViewWidget::render_region (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Region &region, double dbu, const db::Box &clip_box, double zstart, double zstop)
{
std::vector<db::Polygon> poly_heap;
// TODO: hidden cells, hierarchy depth ...
db::RecursiveShapeIterator s (layout, cell, layer, clip_box);
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 ());
for (db::Region::const_iterator p = region.begin (); !p.at_end (); ++p) {
poly_heap.clear ();
db::clip_poly (polygon, clip_box, poly_heap, false /*keep holes*/);
db::clip_poly (*p, clip_box, poly_heap, false /*keep holes*/);
for (std::vector<db::Polygon>::const_iterator p = poly_heap.begin (); p != poly_heap.end (); ++p) {
++progress;
render_polygon (chunks, line_chunks, *p, layout.dbu (), zstart, zstop);
render_polygon (chunks, line_chunks, *p, dbu, zstart, zstop);
for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) {
render_wall (chunks, line_chunks, *e, layout.dbu (), zstart, zstop);
render_wall (chunks, line_chunks, *e, dbu, zstart, zstop);
}
}
@@ -825,6 +821,43 @@ D25ViewWidget::render_layout (tl::AbsoluteProgress &progress, D25ViewWidget::tri
}
}
void
D25ViewWidget::render_edges (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Edges &edges, double dbu, const db::Box &clip_box, double zstart, double zstop)
{
for (db::Edges::const_iterator e = edges.begin (); !e.at_end (); ++e) {
++progress;
std::pair<bool, db::Edge> ec = e->clipped (clip_box);
if (ec.first) {
render_wall (chunks, line_chunks, ec.second, dbu, zstart, zstop);
}
}
}
void
D25ViewWidget::render_edge_pairs (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::EdgePairs &edge_pairs, double dbu, const db::Box &clip_box, double zstart, double zstop)
{
for (db::EdgePairs::const_iterator e = edge_pairs.begin (); !e.at_end (); ++e) {
++progress;
std::pair<bool, db::Edge> ec;
ec = e->first ().clipped (clip_box);
if (ec.first) {
render_wall (chunks, line_chunks, ec.second, dbu, zstart, zstop);
}
ec = e->second ().clipped (clip_box);
if (ec.first) {
render_wall (chunks, line_chunks, ec.second, dbu, zstart, zstop);
}
}
}
static std::pair<double, double> find_grid (double v)
{
for (int p = -12; p < 12; ++p) {
@@ -852,22 +885,22 @@ D25ViewWidget::initializeGL ()
tl_assert (m_gridplane_program == 0);
tl_assert (m_lines_program == 0);
bool error = false;
m_has_error = false;
try {
do_initialize_gl ();
} catch (tl::Exception &ex) {
m_error = ex.msg ();
error = true;
m_has_error = true;
} catch (std::exception &ex) {
m_error = ex.what ();
error = true;
m_has_error = true;
} catch (...) {
m_error = "(unspecific error)";
error = true;
m_has_error = true;
}
if (error) {
if (m_has_error) {
delete m_shapes_program;
m_shapes_program = 0;
@@ -1098,8 +1131,8 @@ D25ViewWidget::paintGL ()
glEnableVertexAttribArray (positions);
for (std::vector<LayerInfo>::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) {
if (l->visible && l->color [3] > 0.5) {
m_shapes_program->setUniformValue ("color", l->color [0], l->color [1], l->color [2], l->color [3]);
if (l->visible && l->fill_color [3] > 0.5) {
m_shapes_program->setUniformValue ("color", l->fill_color [0], l->fill_color [1], l->fill_color [2], l->fill_color [3]);
l->vertex_chunk->draw_to (this, positions, GL_TRIANGLES);
}
}
@@ -27,6 +27,7 @@
#include "layD25MemChunks.h"
#include "layD25Camera.h"
#include "layViewOp.h"
#include <QOpenGLWidget>
#include <QOpenGLVertexArrayObject>
@@ -41,8 +42,11 @@
namespace db
{
class Layout;
class Cell;
class Region;
class Edges;
class EdgePairs;
class RecursiveShapeIterator;
struct LayerProperties;
}
namespace tl
@@ -78,6 +82,20 @@ class D25ViewWidget
Q_OBJECT
public:
typedef lay::mem_chunks<GLfloat, 1024 * 18> triangle_chunks_type;
typedef lay::mem_chunks<GLfloat, 1024 * 6> line_chunks_type;
struct LayerInfo
{
triangle_chunks_type *vertex_chunk;
line_chunks_type *line_chunk;
GLfloat fill_color [4];
GLfloat frame_color [4];
bool visible;
std::string name;
bool has_name;
};
D25ViewWidget (QWidget *parent);
~D25ViewWidget ();
@@ -88,8 +106,7 @@ public:
void mouseReleaseEvent (QMouseEvent *event);
void mouseMoveEvent (QMouseEvent *event);
bool attach_view(lay::LayoutView *view);
void refresh_view ();
void attach_view(lay::LayoutView *view);
QVector3D hit_point_with_scene(const QVector3D &line_dir);
void refresh ();
@@ -129,6 +146,26 @@ public:
return m_error;
}
bool has_error () const
{
return m_has_error;
}
const std::vector<LayerInfo> &layers () const
{
return m_layers;
}
void set_material_visible (size_t index, bool visible);
void clear ();
void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name);
void close_display ();
void entry (const db::Region &data, double dbu, double zstart, double zstop);
void entry (const db::Edges &data, double dbu, double zstart, double zstop);
void entry (const db::EdgePairs &data, double dbu, double zstart, double zstop);
void finish ();
signals:
void scale_factor_changed (double f);
void vscale_factor_changed (double f);
@@ -143,44 +180,36 @@ public slots:
void fit ();
private:
typedef lay::mem_chunks<GLfloat, 1024 * 18> triangle_chunks_type;
typedef lay::mem_chunks<GLfloat, 1024 * 6> line_chunks_type;
std::unique_ptr<D25InteractionMode> mp_mode;
QOpenGLShaderProgram *m_shapes_program, *m_lines_program, *m_gridplane_program;
std::string m_error;
bool m_has_error;
double m_scale_factor;
double m_vscale_factor;
QVector3D m_displacement;
lay::LayoutView *mp_view;
db::DBox m_bbox;
double m_zmin, m_zmax;
bool m_zset;
bool m_display_open;
std::list<triangle_chunks_type> m_vertex_chunks;
std::list<line_chunks_type> m_line_chunks;
struct LayerInfo {
const triangle_chunks_type *vertex_chunk;
const line_chunks_type *line_chunk;
GLfloat color [4];
GLfloat frame_color [4];
bool visible;
};
std::vector<LayerInfo> m_layers;
std::multimap<std::pair<size_t, size_t>, size_t> m_layer_to_info;
void initializeGL ();
void paintGL ();
void resizeGL (int w, int h);
void do_initialize_gl ();
bool prepare_view();
void render_layout (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Layout &layout, const db::Cell &cell, const db::Box &clip_box, unsigned int layer, double zstart, double zstop);
void render_region (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Region &region, double dbu, const db::Box &clip_box, double zstart, double zstop);
void render_edges (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Edges &region, double dbu, const db::Box &clip_box, double zstart, double zstop);
void render_edge_pairs (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::EdgePairs &region, double dbu, const db::Box &clip_box, double zstart, double zstop);
void render_polygon (D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Polygon &poly, double dbu, double zstart, double zstop);
void render_wall (D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Edge &poly, double dbu, double zstart, double zstop);
void reset_viewport ();
static void lp_to_info (const lay::LayerPropertiesNode &lp, D25ViewWidget::LayerInfo &info);
void enter (const db::RecursiveShapeIterator *iter, double zstart, double zstop);
};
}
@@ -4,9 +4,13 @@ DESTDIR = $$OUT_PWD/../../../../lay_plugins
include($$PWD/../../../lay_plugin.pri)
INCLUDEPATH += $$RDB_INC $$ANT_INC
DEPENDPATH += $$RDB_INC $$ANT_INC
INCLUDEPATH += $$RDB_INC $$ANT_INC $$QTBASIC_INC
DEPENDPATH += $$RDB_INC $$ANT_INC $$QTBASIC_INC
LIBS += -L$$DESTDIR/.. -lklayout_rdb -lklayout_ant
equals(HAVE_QTBINDINGS, "1") {
LIBS += -lklayout_qtbasic -lklayout_QtGui -lklayout_QtCore
}
HEADERS = \
layD25View.h \
@@ -16,6 +20,7 @@ HEADERS = \
layD25Camera.h
SOURCES = \
gsiDeclLayD25View.cc \
layD25View.cc \
layD25ViewWidget.cc \
layD25Plugin.cc \
@@ -26,6 +31,9 @@ SOURCES = \
FORMS = \
D25View.ui \
RESOURCES = \
layD25Resources.qrc \
greaterThan(QT_MAJOR_VERSION, 5) {
QT += openglwidgets
}
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description>General;;2.5d view generator script (*.lyd25)\nA script generating a 2.5d view</description>
<version/>
<category>d25</category>
<prolog/>
<epilog/>
<doc/>
<autorun>false</autorun>
<autorun-early>false</autorun-early>
<shortcut/>
<show-in-menu>true</show-in-menu>
<group-name>d25_scripts</group-name>
<menu-path>tools_menu.d25.end</menu-path>
<interpreter>ruby</interpreter>
<dsl-interpreter-name/>
<text>
# Read about 2.5d generator scripts in the User Manual in "Various Topics/The 2.5d View"
# The script utilizes the DRC language with these two extensions
#
# z(layer, options ...):
#
# This function generates a extruded view from the given layer.
#
# Some options control the z extrusion:
#
# z(layer, 0.1 .. 0.2) extrude layer to z = 0.1 to 0.2 um
# z(layer, zstart: 0.1, zstop: 0.2) same as above
# z(layer, zstart: 0.1, height: 0.1) same as above, but with height instead of zstop
# z(layer, height: 200.nm) extrude layer from last z position with a height of 200nm
#
# You can specify display options:
#
# z(..., color: 0xff0000) use bright red for the material color (RGB)
# z(..., frame: 0xff0000) use bright red for the frame color (combine with "fill" for the fill color)
# z(..., fill: 0x00ff00) use bright green for the fill color along (combine with "frame" for the frame color)
# z(..., like: "7/0") borrow style from layout view's style for layer "7/0"
# z(..., name: "M1") assigns a name to show for the material
#
# If no display options are given and layer is an original layer, the colors are taken
# from the original layer's display style. Otherwise KLayout decides about the
# colors itself using the application's palette.
#
# zz(options) { block }
# zz(z(...), z(...), ..., options):
#
# Creates a display group. The display options are the same for all
# extrusion specs within the group.
#
# The options of "zz" are "name", "color", "frame", "fill" and "like".
z(input(1, 0), 0.1 .. 0.2)
z(input(2, 0), height: 250.nm, color: 0xffbc80)
zz(like: 7/0, name: "Metal") do
z(input(7, 0), height: 100.nm)
z(input(8, 0), height: 150.nm)
end
</text>
</klayout-macro>