OSDN Git Service

Improve JANPA integration: show orbital descriptions etc.
[molby/Molby.git] / Scripts / commands.rb
index 31efc59..86b399e 100755 (executable)
@@ -1,3 +1,4 @@
+# coding: utf-8
 #
 #  commands.rb
 #
@@ -120,6 +121,108 @@ class Molecule
        end
   end
 
+  def cmd_concat_frames
+      n = nframes
+      return if n == 0
+      if Molecule.list.length == 1
+          message_box("No other molecule.", "", :ok)
+          return
+      end
+      ls = (0...Molecule.list.length).select { |i|
+          m = Molecule[i]
+          if m == self || m.natoms != self.natoms
+              false
+              else
+              if nil != (0...m.natoms).find { |n| m.atoms[n].atomic_number != self.atoms[n].atomic_number }
+                  false
+                  else
+                  true
+              end
+          end
+      }
+      if ls.length == 0
+          message_box("No molecule has the same atomic sequence as the present one.", "", :ok)
+          return
+      end
+      labels = []
+      label_hash = Hash.new   #  Check if documents with the same name are present
+      ls.each_with_index { |i, idx|  #  i is an index to Molecule, idx is an index to ls
+          m = Molecule[i]
+          label = m.name
+          idx_h = label_hash[label]  #  If non-nil, then duplicate name
+          label_hash[label] = idx
+          if idx_h
+              if labels[idx_h] == label
+                  labels[idx_h] = label + " (" + Molecule[ls[idx_h]].dir + ")"
+              end
+              label = label + " (" + Molecule[i].dir + ")"
+          end
+          labels.push(label)
+      }
+      nf = Molecule[ls[0]].nframes
+      hash = Dialog.run("Concatenate Frames") {
+          layout(2,
+                 item(:text, :title=>"From Molecule:"),
+                 item(:popup, :subitems=>labels, :tag=>"mol", :width=>240,
+                      :action=>lambda { |it|
+                      nf = Molecule[ls[it[:value]]].nframes
+                      set_attr("start_title", :title=>"Start (0-#{nf})")
+                      set_attr("end_title", :title=>"End (0-#{nf})")
+                      }),
+                 item(:radio, :title=>"All Frames", :tag=>"all_frames", :value=>1,
+                      :action=>lambda { |it|
+                      flag = (it[:value] == 0)
+                      set_attr("start_frame", :enabled=>flag)
+                      set_attr("end_frame", :enabled=>flag)
+                      }), -1,
+                 item(:radio, :title=>"Select Frames", :tag=>"select_frames",
+                      :action=>lambda { |it|
+                      flag = (it[:value] != 0)
+                      set_attr("start_frame", :enabled=>flag)
+                      set_attr("end_frame", :enabled=>flag)
+                      }), -1,
+                 item(:text, :title=>"Start (0-#{nf})", :tag=>"start_title"),
+                 item(:textfield, :value=>"0", :tag=>"start_frame", :enabled=>false),
+                 item(:text, :title=>"End (0-#{nf})", :tag=>"end_title"),
+                 item(:textfield, :value=>"0", :tag=>"end_frame", :enabled=>false))
+                 radio_group("all_frames", "select_frames")
+      }
+      if hash[:status] == 0
+          idx_h = Integer(hash["mol"])
+          m = Molecule[ls[idx_h]]
+          f = m.frame  #  Save the current frame number
+          nf = m.nframes
+          if hash["all_frame"] != 0
+              f1 = 0
+              f2 = nf - 1
+              else
+              f1 = Integer(hash["start_frame"])
+              f2 = Integer(hash["end_frame"])
+              f1 = 0 if f1 < 0
+              f1 = nf - 1 if f1 > nf - 1
+              f2 = 0 if f2 < 0
+              f2 = nf - 1 if f2 > nf - 1
+          end
+          if f1 <= f2
+              a = (f1..f2).to_a
+              else
+              a = (f2..f1).to_a.reverse
+          end
+          prop = m.property_names
+          na = m.natoms
+          a.each { |n|
+              self.create_frame()
+              m.select_frame(n)
+              na.times { |i|
+                  self.atoms[i].r = m.atoms[i].r
+              }
+              prop.each { |pr|
+                  self.set_property(pr, m.get_property(pr))
+              }
+          }
+      end
+  end
+
   def cmd_extra_properties
     mol = self
        get_count = lambda { |it| mol.nframes }
@@ -130,7 +233,7 @@ class Molecule
            sprintf("%.8f", mol.get_property(col - 1, row)) rescue ""
          end
     }
-    mol.open_auxiliary_window("Extra Props", nil, nil, :resizable=>true, :has_close_box=>true) {
+    mol.open_auxiliary_window("Extra Props", :resizable=>true, :has_close_box=>true) {
          names = nil
          @on_document_modified = lambda { |m|
            if (n = m.property_names) != names
@@ -149,47 +252,83 @@ class Molecule
          set_min_size(320, 200)
          # listen(mol, "documentModified", on_document_modified)
          # listen(mol, "documentWillClose", lambda { |m| close } )
-         on_document_modified.call(mol)
+         @on_document_modified.call(mol)
          show
        }
   end
   
   def cmd_show_energy
-       wave = [0.0, 0.0]
-       cur = 0
-       mol = self
-       d = open_auxiliary_window("Energy", nil, nil, :resizable=>true, :has_close_box=>true) {
-         graph_item = nil   #  Forward declaration
-         target_mol = nil
-         draw_graph = lambda { |it|
-               clear
-               f = graph_item[:frame]
-               draw_rectangle(0, 0, f[2], f[3])
-               width = f[2] - 25
-               height = f[3] - 25
-               draw_line(16, 0, 16, height + 12, width + 20, height + 12)
-               xx = yy = nil
-               min = wave.min
-               h = wave.max - min
-               h = (h == 0.0 ? 1.0 : height / h)
-               w = wave.count
-               w = (w == 0 ? 1.0 : Float(width) / w)
-               a = []
-               wave.each_with_index { |d, i|
-                 a.push(i * w + 16)
-                 a.push(height - (d - min) * h + 12)
-               }
-               if wave.count == 1
-                 a.push(w + 16)
-                 a.push(height - (wave[0] - min) * h + 12)
-               end
-               draw_line(a)
-               brush(:color=>[0.2, 0.2, 1.0])
-               y = wave[cur] || 0.0
-               xx = cur * w + 16
-               yy = height - (y - min) * h + 12
-               draw_ellipse(cur * w + 16, height - (y - min) * h + 12, 6)
-         }
+    wave = [0.0, 0.0]
+    cur = 0
+    mol = self
+    wave_min = lambda { m = 1e8; wave.each { |x| if x != 0.0 && x < m; m = x; end }; m }
+    wave_max = lambda { m = -1e8; wave.each { |x| if x != 0.0 && x > m; m = x; end }; m }
+    d = open_auxiliary_window("Energy", :resizable=>true, :has_close_box=>true) {
+    graph_item = nil   #  Forward declaration
+    target_mol = nil
+    draw_graph = lambda { |it|
+      clear
+      f = graph_item[:frame]
+      draw_rectangle(0, 0, f[2], f[3])
+      width = f[2] - 25
+      height = f[3] - 25
+      draw_line(16, 0, 16, height + 12, width + 20, height + 12)
+      xx = yy = nil
+      min = wave_min.call
+      max = wave_max.call
+      h = max - min
+      h = (h == 0.0 ? 1.0 : height / h)
+      c = wave.count
+      if c == 0
+        w = 1.0
+      elsif c == 1
+        w = Float(width)
+      else
+        w = Float(width) / (c - 1)
+      end
+      lines = []
+      a = []
+      #  Skip the points that have exactly 0.0 value
+      wave.each_with_index { |d, i|
+        if d != 0.0
+          a.push(i * w + 16)
+          a.push(height - (d - min) * h + 12)
+        end
+        if d == 0.0 || i == wave.length - 1
+          #  End of this curve fragment
+          if a.length == 0
+            #  Do nothing
+          else
+            if a.length == 2
+              if wave.count == 1
+                #  If wave has only one point, then draw a horizontal line
+                a.push(a[0] + 16)
+                a.push(a[1])
+              else
+                #  Otherwise, draw a zero-length line
+                a.push(a[0])
+                a.push(a[1])
+              end
+            end
+            lines.push(a)  #  Store this line fragment
+            a = []
+          end
+        end
+      }
+      lines.each { |a|
+        draw_line(a)
+      }
+      brush(:color=>[0.2, 0.2, 1.0])
+      y = wave[cur] || 0.0
+      if y < min
+        y = min
+      elsif y > max
+        y = max
+      end
+      xx = cur * w + 16
+      yy = height - (y - min) * h + 12
+      draw_ellipse(cur * w + 16, height - (y - min) * h + 12, 6)
+    }
          @on_document_modified = lambda { |m|
                cur = mol.frame
                wave.clear
@@ -220,8 +359,14 @@ class Molecule
   end
   
   def cmd_create_surface
+    if @surface_dialog_attr == nil
+         @surface_dialog_attr = Hash.new
+         @surface_dialog_attr["hidden"] = -1
+       end
+    surface_dialog_attr = @surface_dialog_attr
     mol = self
-       mol.open_auxiliary_window("MO Surface", nil, nil, :resizable=>true, :has_close_box=>true) {
+       mol.open_auxiliary_window("MO Surface", :resizable=>true, :has_close_box=>true) {
+         tags = ["mo_ao", "mo", "color", "opacity", "threshold", "expand", "grid"]
          motype = mol.get_mo_info(:type)
          alpha = mol.get_mo_info(:alpha)
          beta = mol.get_mo_info(:beta)
@@ -248,6 +393,18 @@ class Molecule
                          name2 = "Pre-orthogonal Natural Atomic Orbitals"
                        elsif key2 == "PNHO"
                          name2 = "Pre-orthogonal Natural Hybrid Orbitals"
+                       elsif key2 == "PNBO"
+                         name2 = "Pre-orthogonal Natural Bond Orbitals"
+            elsif key2 == "AHO"
+              name2 = "Atomic Hybrid Orbitals"
+            elsif key2 == "LHO"
+              name2 = "Lewis Hybrid Orbitals"
+            elsif key2 == "PLHO"
+              name2 = "Pre-orthogonal Lewis Hybrid Orbitals"
+            elsif key2 == "LPO"
+              name2 = "Localized Property-optimized Orbitals"
+            elsif key2 == "CLPO"
+              name2 = "Chemist's Localized Property-optimized Orbitals"
                        end
                        mo_ao_items.push(name2)
                        mo_ao_keys.push(key2)
@@ -271,6 +428,23 @@ class Molecule
                end
                tabvals.push([a, n, label, a_idx])
          }
+         on_update_attr = lambda {
+           tags.each { |tag|
+                 surface_dialog_attr[tag] = item_with_tag(tag)[:value]
+               }
+               it = item_with_tag("hide")
+               case surface_dialog_attr["hidden"]
+               when -1
+                 it[:title] = "Hide"
+                 it[:enabled] = false
+               when 0
+                 it[:title] = "Hide"
+                 it[:enabled] = true
+               when 1
+                 it[:title] = "Show"
+                 it[:enabled] = true
+               end
+         }
          on_get_value = lambda { |it, row, col|
            if col < 3
                  tabvals[row][col]
@@ -318,6 +492,7 @@ class Molecule
                  }
                  item_with_tag("update")[:enabled] = should_update
                end
+               on_update_attr.call
          }
          on_mo_action = lambda { |it|
            mo = it[:value]
@@ -353,6 +528,7 @@ class Molecule
                end
                item_with_tag("table")[:refresh] = true
            on_action.call(it)
+               on_update_attr.call
          }
          on_set_action = lambda { |it|
            if mo_ao != it[:value]
@@ -387,8 +563,16 @@ class Molecule
                  else
                    mo_menu = []
                        key = mo_ao_keys[mo_ao]
+                       labels = nbo[key + "_L"]
+                       if labels == nil && key[0] == ?P
+                         labels = nbo[key[1..-1] + "_L"]
+                       end
                        ncomps.times { |i|
-                         mo_menu[i] = sprintf("#{key}%d", i + 1)
+                         lab = sprintf("%s%d", key, i + 1)
+                         if labels
+                           lab += ":" + labels[i]
+                         end
+                         mo_menu[i] = lab
                        }
                  end
                  it0 = item_with_tag("mo")
@@ -397,6 +581,7 @@ class Molecule
                  h["mo"] = nil  # "Update" button is forced to be enabled
                  on_mo_action.call(it0)
                end
+               on_update_attr.call
          }
          on_update = lambda { |it|
            h.each_key { |key|
@@ -423,8 +608,19 @@ class Molecule
                  idx = 0
                  mol.set_mo_coefficients(0, 0.0, coeffs)
                end
-               mol.create_surface(idx, :npoints=>grid, :color=>color, :thres=>thres, :expand=>expand, :color0=>color0)
-               on_action.call(it)
+               if it[:tag] == "create_cube"
+                 basename = File.basename(mol.path || mol.name, ".*")
+             fname1 = Dialog.save_panel("Cube file name", mol.dir || Dir.pwd, basename + ".cube", "Gaussian cube file (*.cube)|*.cube")
+                 if fname1
+                   mol.cubegen(fname1, idx, grid, true)
+                 end
+               else
+                 mol.create_surface(idx, :npoints=>grid, :color=>color, :thres=>thres, :expand=>expand, :color0=>color0)
+                 mol.show_surface
+                 on_action.call(it)
+                 surface_dialog_attr["hidden"] = 0
+                 on_update_attr.call
+               end
          }
          layout(1,
            layout(2,
@@ -436,7 +632,7 @@ class Molecule
                  item(:text, :title=>"Color"),
                  item(:popup, :tag=>"color", :subitems=>["blue", "red", "green", "yellow", "cyan", "magenta", "black"], :action=>on_action),
                  item(:text, :title=>"Opacity"),
-                 item(:textfield, :tag=>"opacity", :width=>80, :value=>"0.6", :action=>on_action),
+                 item(:textfield, :tag=>"opacity", :width=>80, :value=>"0.8", :action=>on_action),
                  item(:text, :title=>"Threshold"),
                  item(:textfield, :tag=>"threshold", :width=>80, :value=>"0.05", :action=>on_action),
                  item(:text, :title=>"Box Limit"),
@@ -449,10 +645,38 @@ class Molecule
                  :on_count=> lambda { |it| tabvals.count },
                  :on_get_value=>on_get_value,
                  :flex=>[0,0,0,0,1,1]),
-               item(:button, :tag=>"update", :title=>"Update", :action=>on_update, :flex=>[0,1,0,0,0,0]),
+               layout(3,
+                 item(:button, :tag=>"update", :title=>"Update", :action=>on_update),
+                 item(:button, :tag=>"clear", :title=>"Clear", :action=>lambda { |it|
+                   mol.clear_surface
+                       item_with_tag("update")[:enabled] = true
+                       surface_dialog_attr["hidden"] = -1
+                       on_update_attr.call
+                   } ),
+                 item(:button, :tag=>"hide", :title=>"Hide", :action=>lambda { |it|
+                   case surface_dialog_attr["hidden"]
+                       when 0
+                         surface_dialog_attr["hidden"] = 1
+                         mol.hide_surface
+                       when 1
+                         surface_dialog_attr["hidden"] = 0
+                         mol.show_surface
+                       end
+                       on_update_attr.call
+                       } ),
+                 item(:button, :tag=>"create_cube", :title=>"Create Cube", :action=>on_update),
+                 :flex=>[0,1,0,0,0,0]),
                :flex=>[0,0,0,0,1,1]
          )
+         tags.each { |tag|
+           if (val = surface_dialog_attr[tag]) != nil
+                 item_with_tag(tag)[:value] = val
+               end
+         }
+         mo_idx = surface_dialog_attr["mo"]
+         on_update_attr.call
          on_set_action.call(item_with_tag("mo_ao"))
+         item_with_tag("mo")[:value] = surface_dialog_attr["mo"] = mo_idx
          size = self.size
          set_min_size(size[0], 250)
          item_with_tag("table")[:refresh] = true
@@ -515,6 +739,7 @@ register_menu("Sort by residue", :cmd_sort_by_residue, :non_empty)
 register_menu("", "")
 register_menu("Delete Frames...", :cmd_delete_frames, lambda { |m| m && m.nframes > 1 } )
 register_menu("Reverse Frames...", :cmd_reverse_frames, lambda { |m| m && m.nframes > 1 } )
+register_menu("Concatenate Frames...", :cmd_concat_frames, lambda { |m| m && m.nframes > 1 } )
 register_menu("", "")
 register_menu("Show Energy Window...", :cmd_show_energy, lambda { |m| m && m.property_names.include?("energy") } )
 register_menu("Show MO Surface...", :cmd_create_surface, lambda { |m| m && m.get_mo_info(:type) != nil } )