From d0d3d2a8beab0dc89f60bbe6769bb572b2f79718 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 24 Feb 2022 00:45:02 +0100 Subject: [PATCH] New integration, WIP --- .../lay_plugin/built-in-macros/_d25_engine.rb | 249 ++++++++++++++++++ .../built-in-macros/d25_install.lym | 51 ++++ .../built-in-macros/d25_interpreters.lym | 170 ++++++++++++ .../view_25d/lay_plugin/layD25Resources.qrc | 10 + .../tools/view_25d/lay_plugin/lay_plugin.pro | 3 + .../view_25d/lay_plugin/templates/d25.lym | 59 +++++ 6 files changed, 542 insertions(+) create mode 100644 src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb create mode 100644 src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym create mode 100644 src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25Resources.qrc create mode 100644 src/plugins/tools/view_25d/lay_plugin/templates/d25.lym diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb new file mode 100644 index 000000000..591ea4085 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb @@ -0,0 +1,249 @@ +# $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 + + def initialize + self.fill = nil + self.frame = nil + self.like = nil + 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 + self.clip(RBA::LayoutView::current.box) + end + + end + + def z(*args) + + self._context("z") do + + layer = nil + zstart = nil + zstop = nil + height = nil + + 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 + + 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 + invalid_keys = a.keys.select { |k| ![ :height, :zstart, :zstop ].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.data, zstart, zstop, @display || D25Display::new) + @zstack << info + + return info + + end + + end + + def display(*args, &block) + + display = D25Display::new + + 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) + + hollow_fill = 0xffffffff + + if a[:color] + if !a[:color].is_a?(0xffffff.class) + raise("'color' must be a color value (an integer)") + end + display.fill = a[:color] + display.frame = a[:color] + end + if a[:frame] + if !a[:frame].is_a?(0xffffff.class) + raise("'frame' must be a color value (an integer)") + end + display.frame = a[:frame] + end + if a[:fill] + if !a[:fill].is_a?(0xffffff.class) + raise("'fill' must be a color value (an integer)") + end + display.fill = a[:fill] + end + if a[:hollow] + if a[:hollow] + display.fill = hollow_fill + end + end + + if a[:like] + li = nil + if a[:like].is_a?(String) + li = RBA::LayerInfo::from_string(a[:like]) + elsif a[:like].is_a?(RBA::LayerInfo) + li = a[:like] + else + raise("'like' must be a string or LayerInfo object") + end + display.like = li + end + + invalid_keys = a.keys.select { |k| ![ :fill, :frame, :color, :hollow, :like ].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 + + end + + def _finish(final = true) + + if final && @zstack.empty? + raise("No z calls made in 2.5d script") + end + + super(final) + + if final + + view = RBA::LayoutView::current.open_d25_view + + begin + + 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) + zz.each do |z| + view.entry(z.layer, z.start, z.zstop) + end + view.close_display + end + + view.finish + + rescue => ex + view.close + raise ex + end + + end + + end + + end + +end + diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym new file mode 100644 index 000000000..6e0ebc18f --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym @@ -0,0 +1,51 @@ + + + + + + + + + true + false + + false + + + ruby + + +module D25 + + # Installs the home menu entries (needs to be done on autorun, not autorun-early) + + if RBA::Application::instance && 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) + + end + +end + + + diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym new file mode 100644 index 000000000..fbf0dc9ef --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym @@ -0,0 +1,170 @@ + + + + + + + + + false + true + + false + + + ruby + + +module D25 + + class D25Executable < RBA::Executable + + def initialize(macro, generator, rdb_index = nil) + + @d25 = D25Engine::new + @d25._rdb_index = rdb_index + @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 >= 10 && RBA::Logger::info("Running #{@macro.path}") + @d25.instance_eval(@macro.text, @macro.path) + + rescue => ex + + @d25.error("In #{@macro.path}: #{ex.to_s}") + RBA::MacroExecutionContext::ignore_next_exception + raise ex + + end + + 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 < 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 && 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 < 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 < 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), params["rdb_index"]) + + 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 + + + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Resources.qrc b/src/plugins/tools/view_25d/lay_plugin/layD25Resources.qrc new file mode 100644 index 000000000..be37f46fe --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Resources.qrc @@ -0,0 +1,10 @@ + + + built-in-macros/_d25_engine.rb + built-in-macros/d25_interpreters.lym + built-in-macros/d25_install.lym + + + templates/d25.lym + + diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro index b05f93e94..cc6063840 100644 --- a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -26,6 +26,9 @@ SOURCES = \ FORMS = \ D25View.ui \ +RESOURCES = \ + layD25Resources.qrc \ + greaterThan(QT_MAJOR_VERSION, 5) { QT += openglwidgets } diff --git a/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym b/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym new file mode 100644 index 000000000..ac0468824 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym @@ -0,0 +1,59 @@ + + + General;;2.5d view generator script (*.lyd25)\nA script generating a 2.5d view + + d25 + + + + false + false + + true + d25_scripts + tools_menu.d25.end + ruby + + +# 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. +# The "options" describe the z extension. Valid forms are: +# +# 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 +# +# If layer is an original layer, the display options (colors, hollow) are taken +# from the original layer's display style. Otherwise KLayout decides about the +# initial display options. Use "display(...)" to control the display options. +# +# display(options) { block } +# display(z(...), z(...), ..., options): +# +# Specify the display options to use for the layers generated inside the block +# (which must contains at least one "z" call) or the given z calls: +# +# Options are: +# +# display(..., color: 0xff0000) use bright red for the material color (RGB) +# display(..., frame: 0xff0000) use bright red for the frame color (combine with "color" for the fill) +# display(..., hollow: true) use hollow style (only frame is drawn) +# display(..., like: "7/0") borrow style from layout view's style for layer "7/0" +# + +z(input(1, 0), 0.1 .. 0.2) + +display(like: 7/0) do + z(input(7, 0), height: 100.nm) + z(input(8, 0), height: 150.nm) +end + + + +