aims_project 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ require 'observer'
2
+
3
+ module AimsProject
4
+ class CrystalViewerOptions
5
+
6
+ include Wx
7
+ include Observable
8
+
9
+ def add_control(name, control)
10
+ instance_variable_set("@#{name.to_s}".to_sym, control)
11
+ instance_eval <<-EOS
12
+ def #{name.to_s}
13
+ @#{name.to_s}.get_value
14
+ end
15
+
16
+ def #{name.to_s}=(val)
17
+ @#{name.to_s}.set_value(val)
18
+ changed
19
+ notify_observers
20
+ end
21
+ EOS
22
+ control
23
+ end
24
+
25
+
26
+ def initialize(inspector_window)
27
+
28
+ # Default padding around each element
29
+ border = 5
30
+
31
+ # Main Sizer
32
+ inspector_window.sizer = VBoxSizer.new
33
+
34
+ # Panel 1
35
+ panel1 = VBoxSizer.new
36
+ panel1.add_item(add_control(:show_bonds, CheckBox.new(inspector_window, :label => 'Show Bonds')), :flag => ALL,:border => border)
37
+ panel1.add_item(add_control(:show_lighting, CheckBox.new(inspector_window, :label => 'Show Lighting')), :flag => ALL, :border => border)
38
+ panel1.add_item(add_control(:show_supercell, CheckBox.new(inspector_window, :label => "Show Unit Cell")), :flag => ALL, :border => border)
39
+ panel1.add_item(add_control(:correct, CheckBox.new(inspector_window, :label => "Correct Geometry")), :flag => ALL, :border => border)
40
+ panel1.add_item(add_control(:solid_bg, CheckBox.new(inspector_window, :label => "Solid BG")), :flag => ALL, :border => border)
41
+
42
+ gsizer = GridSizer.new(2,1)
43
+ gsizer.add(StaticText.new(inspector_window, :label => "Bond Length"))
44
+ gsizer.add_item(add_control(:bond_length, SpinCtrl.new(inspector_window, :min => 1, :max => 100, :value => "4", :name => "Name")), :flag => EXPAND)
45
+ panel1.add_item(gsizer)
46
+
47
+
48
+ # Panel 2 : Clip Planes
49
+ panel2 = VBoxSizer.new
50
+ panel2.add_item(StaticText.new(inspector_window, -1, "Clip Planes:", :style => ALIGN_LEFT))
51
+ panel2.add_item(add_control(:show_xclip, CheckBox.new(inspector_window, :label => 'x')), :flag => Wx::ALIGN_CENTER, :border => border)
52
+ panel2.add_item(add_control(:show_yclip, CheckBox.new(inspector_window, :label => 'y')), :flag => Wx::ALIGN_CENTER, :border => border)
53
+ panel2.add_item(add_control(:show_zclip, CheckBox.new(inspector_window, :label => 'z')), :flag => Wx::ALIGN_CENTER, :border => border)
54
+
55
+ # Panel 3: Repeat
56
+ panel3 = VBoxSizer.new
57
+ panel3.add_item(StaticText.new(inspector_window, -1, "Repeat:", :style => ALIGN_LEFT), :flag => EXPAND)
58
+
59
+ panel3grid = GridSizer.new(3,2, border, border)
60
+ panel3grid.add(StaticText.new(inspector_window, :label => "x", :style => ALIGN_RIGHT),1, EXPAND)
61
+ panel3grid.add(add_control(:x_repeat, SpinCtrl.new(inspector_window, :value => "1", :min => 1, :max => 100, :initial => 1)),2)
62
+
63
+ panel3grid.add(StaticText.new(inspector_window, :label => "y",:style => ALIGN_RIGHT),1, EXPAND)
64
+ panel3grid.add(add_control(:y_repeat, SpinCtrl.new(inspector_window, :value => "1", :min => 1, :max => 100, :initial => 1)),2)
65
+
66
+ panel3grid.add(StaticText.new(inspector_window, :label => "z",:style => ALIGN_RIGHT),1, EXPAND)
67
+ panel3grid.add(add_control(:z_repeat, SpinCtrl.new(inspector_window, :value => "1", :min => 1, :max => 100, :initial => 1)),2)
68
+
69
+ panel3.add_item(panel3grid, :flag => EXPAND | ALIGN_CENTER)
70
+
71
+ # Event Handling
72
+ inspector_window.evt_checkbox(@show_bonds) {|evt| changed; notify_observers}
73
+ inspector_window.evt_checkbox(@show_lighting) {|evt| changed; notify_observers}
74
+ inspector_window.evt_checkbox(@show_supercell) {|evt| changed; notify_observers}
75
+ inspector_window.evt_checkbox(@solid_bg) {|evt| changed; notify_observers}
76
+ inspector_window.evt_checkbox(@correct) {|evt| changed; notify_observers }
77
+ inspector_window.evt_spinctrl(@bond_length) {|evt| changed; notify_observers }
78
+ inspector_window.evt_checkbox(@show_xclip) {|evt| changed; notify_observers}
79
+ inspector_window.evt_checkbox(@show_yclip) {|evt| changed; notify_observers}
80
+ inspector_window.evt_checkbox(@show_zclip) {|evt| changed; notify_observers}
81
+ inspector_window.evt_spinctrl(@x_repeat) {|evt| changed; notify_observers}
82
+ inspector_window.evt_spinctrl(@y_repeat) {|evt| changed; notify_observers}
83
+ inspector_window.evt_spinctrl(@z_repeat) {|evt| changed; notify_observers}
84
+
85
+ # Add sub-panels
86
+ inspector_window.sizer.add_item(panel1, :flag => EXPAND, :proportion => 1)
87
+ inspector_window.sizer.add_spacer(5)
88
+ inspector_window.sizer.add_item(panel2, :flag => EXPAND, :proportion => 1)
89
+ inspector_window.sizer.add_spacer(5)
90
+ inspector_window.sizer.add_item(panel3, :flag => EXPAND, :proportion => 1)
91
+ inspector_window.layout
92
+ end
93
+
94
+ def set_properties(props)
95
+ props.each{|k,v|
96
+ instance_variable_get("@#{k.to_s}").set_value(v)
97
+ }
98
+ changed
99
+ notify_observers
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,111 @@
1
+ module AimsProject
2
+
3
+ class GeometryEditor < Wx::ScrolledWindow
4
+
5
+ include Wx
6
+ attr_accessor :app, :text_ctrl, :button_panel
7
+
8
+ @atom_ranges = {}
9
+
10
+ def initialize(app, window)
11
+
12
+ super(window)
13
+
14
+ @app = app
15
+
16
+ sizer = BoxSizer.new(VERTICAL)
17
+
18
+ @text_ctrl = StyledTextCtrl.new(self)
19
+ # @text_ctrl.set_sel_background(true, Colour.new("wheat"))
20
+
21
+ # Create the button panel (A toolbar for the top of this panel)
22
+ @button_panel = HBoxSizer.new
23
+
24
+ @toggle_source = CheckBox.new(self, ID_ANY, "Source")
25
+
26
+ basedir = File.dirname(__FILE__)
27
+ arrow_icon = Image.new(File.join(basedir,"green_arrow.jpg"), BITMAP_TYPE_JPEG)
28
+ @eval_button = BitmapButton.new(self, :bitmap => arrow_icon.rescale(16,15).convert_to_bitmap,:style=>BU_EXACTFIT, :name => "evaluate")
29
+
30
+ # error_icon = Image.new(File.join(basedir, "red_cross.jpeg"), BITMAP_TYPE_JPEG)
31
+ # @clear_errors_button = BitmapButton.new(self, :bitmap => error_icon.rescale(16,15).convert_to_bitmap,:style=>BU_EXACTFIT, :name => "Clear Errors")
32
+
33
+ @button_panel.add_item(@toggle_source,1)
34
+ @button_panel.add_stretch_spacer
35
+ @button_panel.add_item(@eval_button,3)
36
+
37
+ evt_checkbox(@toggle_source) {|evt|
38
+ if @toggle_source.is_checked
39
+ @text_ctrl.set_read_only(false)
40
+ @text_ctrl.set_text(@unit_cell.raw_input)
41
+ else
42
+ @text_ctrl.set_read_only(false)
43
+ @text_ctrl.set_text(@unit_cell.input_geometry)
44
+ @text_ctrl.set_read_only(true)
45
+ end
46
+ }
47
+
48
+ sizer.add_item(@button_panel, :flag => EXPAND)
49
+ sizer.add_item(@text_ctrl, :proportion => 1, :flag => EXPAND | ALL, :border => 5)
50
+
51
+ set_auto_layout(true)
52
+ set_sizer(sizer)
53
+
54
+
55
+ evt_button(@eval_button) {|event|
56
+ self.evaluate
57
+ }
58
+
59
+ end
60
+
61
+ # Apply the edits to the geometry_file and evaluate
62
+ def evaluate
63
+ @unit_cell.raw_input = @text_ctrl.get_text
64
+ begin
65
+ @unit_cell.evaluate
66
+ rescue
67
+ # @button_pane.add_item(@clear_errors_button, 3)
68
+ @text_ctrl.set_text($!.message + $!.backtrace.join("\n"))
69
+ end
70
+ end
71
+
72
+ def unit_cell=(uc)
73
+ @unit_cell = uc
74
+ if @toggle_source.is_checked
75
+ @text_ctrl.set_read_only(false)
76
+ @text_ctrl.set_text(@unit_cell.raw_input)
77
+ @text_ctrl.set_read_only(false)
78
+
79
+ else
80
+ @text_ctrl.set_read_only(false)
81
+ @text_ctrl.set_text(@unit_cell.input_geometry)
82
+ @text_ctrl.set_read_only(true)
83
+
84
+ end
85
+ end
86
+
87
+ def select_atom(atom)
88
+ selectionStyle = Wx::RichTextAttr.new(Wx::BLACK, Wx::Colour.new("YELLOW"))
89
+
90
+ pattern = atom.format_geometry_in
91
+ puts "GeometryEditor.select_atom: pattern=#{pattern}"
92
+
93
+ matchdata = self.text_ctrl.get_text.match(pattern)
94
+ if matchdata
95
+ lineStart = matchdata.begin(0)
96
+ lineEnd = matchdata.end(0)
97
+ # self.text_ctrl.set_style(lineStart...lineEnd, selectionStyle)
98
+ self.text_ctrl.goto_pos(lineStart)
99
+ self.text_ctrl.set_selection_start(lineStart)
100
+ self.text_ctrl.set_selection_end(lineEnd)
101
+ end
102
+ end
103
+
104
+
105
+ # Return the text contents of this control
106
+ # after evaluating it with ERB
107
+ def get_contents
108
+ ERB.new(@text_ctrl.get_value).result
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,184 @@
1
+ require 'erb'
2
+ require 'observer'
3
+
4
+ module AimsProject
5
+ # The geometry model utilized by this application
6
+ class GeometryFile
7
+
8
+ include Observable
9
+
10
+ # Include Aims here to ensure that ERB will evaluate ruby
11
+ # without prefixing Aims:: when using the builtin binding.
12
+ include Aims
13
+
14
+ # Read and parse a geometry file. Use the given binding to
15
+ # evaluate any embedded ruby
16
+ def GeometryFile.eval_geometry(str, _binding=nil)
17
+
18
+ b = if _binding
19
+ _binding
20
+ else
21
+ binding()
22
+ end
23
+
24
+ erb = ERB.new(str)
25
+ erb.result(b)
26
+
27
+ end
28
+
29
+ # The filename of the geometry file
30
+ attr_reader :file
31
+
32
+ # The Aims::Geometry object
33
+ attr_reader :aims_geometry
34
+
35
+ # The string representation of the parsed input geometry
36
+ attr_reader :input_geometry
37
+
38
+ # The raw unevaluated input.
39
+ attr_reader :raw_input
40
+
41
+ # Parse a geometry input
42
+ # Evaluate any embedded ruby using the given binding
43
+ # @param input If input is a File, the file is read and evaluated with ERB
44
+ # If input is a String, the input is directly evaluated with ERB
45
+ # @param _binding The binding to use when evaluating with EB
46
+ def initialize(input, _binding=nil)
47
+
48
+ if input.respond_to? :read
49
+ @file = input
50
+ @raw_input = @file.read
51
+ elsif input.is_a? String
52
+ @raw_input = input
53
+ elsif input.is_a? Aims::Geometry
54
+ @aims_geometry = input
55
+ @input_geometry = @aims_geometry.format_geometry_in
56
+ @raw_input = @input_geometry
57
+ end
58
+
59
+ begin
60
+ # Attempt to evaluate the raw input, but don't require it.
61
+ @binding = _binding
62
+ evaluate(@binding)
63
+ rescue
64
+ end
65
+ end
66
+
67
+ # Set the raw input
68
+ # @param str The string to set the raw input to
69
+ # @param notify Whether or not to notify observer. Set this to false if you intend to call evaluate immediately after
70
+ def raw_input=(str, notify = true)
71
+ @raw_input = str
72
+ if notify
73
+ changed
74
+ notify_observers
75
+ end
76
+ end
77
+
78
+ # Evaluate the raw input and return a geometry String formatted in the Aims geometry.in format
79
+ #
80
+ def evaluate(_binding=nil)
81
+ if _binding
82
+ @binding = _binding
83
+ end
84
+
85
+ @input_geometry = GeometryFile.eval_geometry(@raw_input, @binding)
86
+ @aims_geometry = GeometryParser.parse_string(@input_geometry)
87
+
88
+ changed
89
+ notify_observers
90
+
91
+ @aims_geometry
92
+ end
93
+
94
+ # Delegate calls to the Aims::Geometry object if it exists.
95
+ def method_missing(symbol, *args, &block)
96
+ if @aims_geometry.nil?
97
+ raise GeometryEvaluationException.new
98
+ else
99
+ @aims_geometry.send(symbol, *args, &block)
100
+ end
101
+ end
102
+
103
+ # Check the consistency between the raw, the evaluated and the object-space geometries
104
+ def is_valid?
105
+ begin
106
+ validate
107
+ return true
108
+ rescue GeometryValidationException => e
109
+ return false
110
+ end
111
+ end
112
+
113
+ # Check the consistency of all internal data models
114
+ # @return true, otherwise raises a GeoemtryValidationException
115
+ def validate
116
+ # The raw input evaluated must match the input_geometry
117
+ erb = ERB.new(@raw_input)
118
+ res = erb.result(@binding)
119
+ unless res == @input_geometry
120
+ raise GeometryValidationException.new("raw input doesn't match evaluated input")
121
+ end
122
+
123
+ # Also need to somehow validate against the Aims::Geometry
124
+ g = GeometryParser.parse_string(res)
125
+ unless g.atoms.size == @aims_geometry.atoms.size
126
+ raise GeometryValidationException("input geometry atom count doesn't match aims_geometry")
127
+ end
128
+
129
+ unless g.lattice_vectors.size == @aims_geometry.lattice_vectors.size
130
+ raise GeometryValidationException("input geometry lattice vector count doesn't match aims_geometry")
131
+ end
132
+
133
+ g.atoms.each{|a|
134
+ unless @aims_geometry.atoms.member?(a)
135
+ raise GeometryValidationException("atom mismatch")
136
+ end
137
+ }
138
+
139
+ g.lattice_vectors.each{|v|
140
+ unless @aims_geometry.lattice_vectors.member?(v)
141
+ raise GeometryValidationException("lattice vector mismatch")
142
+ end
143
+ }
144
+
145
+ return true
146
+ end
147
+
148
+ # Save this geometry
149
+ # raises an InvalidFilenameException unless @file is not nil
150
+ # @return self
151
+ def save
152
+ save_as(@file)
153
+ end
154
+
155
+ # Save the raw input to file if it passes validation
156
+ # raises an InvalidFilenameException unless @file is not nil
157
+ # raises a GeometryValidationException if it fails to pass Validation
158
+ # @return A new GeometryFile object representing the geometry in the newly created file
159
+ # or this GeometryFile object if file = nil
160
+ def save_as(file)
161
+
162
+ if file.nil?
163
+ raise InvalidFilenameException(nil) unless @file
164
+ file = @file
165
+ end
166
+
167
+ # validate
168
+
169
+ File.open(file, 'w') {|f|
170
+ f.write @raw_input
171
+ }
172
+
173
+ if file == @file
174
+ return self
175
+ else
176
+ GeometryFile.new(File.new(file.path, "r"))
177
+ end
178
+
179
+ end
180
+
181
+ end
182
+
183
+
184
+ end
@@ -0,0 +1,172 @@
1
+ module AimsProject
2
+
3
+ class GeometryWindow < Wx::Panel
4
+
5
+ include Wx
6
+
7
+ attr_accessor :update_unit_cell
8
+
9
+ def initialize(app, parent)
10
+
11
+ super(parent)
12
+ @app = app
13
+
14
+ # Initialize the selection of atoms
15
+ @selection = {}
16
+
17
+ # Tracks whether changes in inspector should update the unit cell
18
+ @update_unit_cell = false
19
+
20
+ # Get an inspector window to add into
21
+ @inspector_window = @app.inspector.add_inspector_window
22
+
23
+ # This is a model/controller for the inspector to pass to the crystal viewer
24
+ @options = CrystalViewerOptions.new(@inspector_window)
25
+
26
+ # Top level is a splitter
27
+ topSplitterWindow = SplitterWindow.new(self)
28
+ sizer = VBoxSizer.new
29
+ sizer.add_item(topSplitterWindow, :proportion => 1, :flag => EXPAND)
30
+
31
+ set_sizer(sizer)
32
+
33
+ # The top is a list control
34
+ @geomList = ListCtrl.new(topSplitterWindow)
35
+ app.project.geometries.sort{|a,b| a <=> b}.each{|geom|
36
+ li = ListItem.new
37
+ li.set_text(File.basename(geom))
38
+ li.set_data(geom)
39
+ @geomList.insert_item(li)
40
+ }
41
+
42
+ # The bottom is a vertical splitter
43
+ geomWindowSplitter = SplitterWindow.new(topSplitterWindow)
44
+ @geomEditor = GeometryEditor.new(self, geomWindowSplitter)
45
+ @geomViewer = CrystalViewer.new(self, geomWindowSplitter, @options)
46
+
47
+ geomWindowSplitter.split_vertically(@geomEditor, @geomViewer)
48
+
49
+ # Add top and bottom sides together
50
+ topSplitterWindow.split_horizontally(@geomList, geomWindowSplitter, 100)
51
+ layout
52
+
53
+ # Define events
54
+ evt_list_item_selected(@geomList) {|evt|
55
+ open_geometry_file(evt.get_item.get_data)
56
+ }
57
+
58
+
59
+ end
60
+
61
+ # Add a geometry with the given name
62
+ def add_geometry(geometry)
63
+ @app.project.geometries << geometry
64
+ li = ListItem.new
65
+ li.set_text(File.basename(geometry.file))
66
+ li.set_data(geometry.file)
67
+ li.set_state(LIST_STATE_SELECTED)
68
+ @geomList.insert_item(li)
69
+ @geomList.sort{|a,b| a <=> b}
70
+ end
71
+
72
+
73
+ def show_inspector
74
+ @app.inspector.show_inspector_window(@inspector_window)
75
+ end
76
+
77
+ # Display a file dialog and attempt to open and display the file
78
+ def open_geometry_file(file = nil)
79
+ begin
80
+ unless file
81
+ fd = FileDialog.new(@app.frame, :message => "Open", :style => FD_OPEN, :default_dir => @working_dir)
82
+ if ID_OK == fd.show_modal
83
+ file = fd.get_path
84
+ @working_dir = fd.get_directory
85
+ else
86
+ return
87
+ end
88
+ end
89
+ @app.set_status "Opening #{file}"
90
+
91
+ if (@app.project)
92
+ show_geometry GeometryFile.new(File.new(file), @app.project.get_binding)
93
+ else
94
+ show_geometry GeometryFile.new(File.new(file))
95
+ end
96
+
97
+
98
+ rescue Exception => dang
99
+ @app.error_dialog(dang)
100
+ end
101
+ end
102
+
103
+ def image
104
+ @geomViewer.image
105
+ end
106
+
107
+ # Get the currently displayed geometry
108
+ def geometry
109
+ @original_uc
110
+ end
111
+
112
+ # Display the given geometry
113
+ def show_geometry(geometry)
114
+ begin
115
+ @original_uc = geometry
116
+ @geomViewer.unit_cell = @original_uc
117
+ rescue AimsProjectException => e
118
+ @app.error_dialog(e)
119
+ ensure
120
+ @geomEditor.unit_cell = @original_uc
121
+ end
122
+ # @inspector.update(@geomViewer)
123
+ # @geomViewer.draw_scene
124
+ end
125
+
126
+ def select_atom(atom)
127
+ @selection[:atoms] = [atom]
128
+ puts "Selection:"
129
+ puts @selection[:atoms].each{|a| puts a.format_geometry_in}
130
+ @geomEditor.select_atom(atom)
131
+ end
132
+
133
+ def nudge_selected_atoms(x,y,z)
134
+ if @selection[:atoms]
135
+ @selection[:atoms].each{|a| a.displace!(x,y,z)}
136
+ end
137
+ @geomViewer.draw_scene
138
+ @geomEditor.update
139
+ end
140
+
141
+ # Apply UI settings to viewer and re-render
142
+ def update_viewer
143
+
144
+ puts "GeometryWindow.update_viewer"
145
+
146
+ if @original_uc and self.update_unit_cell
147
+ if @correct.get_value
148
+ # @geomViewer.unit_cell = @original_uc.correct
149
+ new_geom = @original_uc.repeat(@x_repeat.get_value, @y_repeat.get_value, @z_repeat.get_value).correct
150
+ else
151
+ # @geomViewer.unit_cell = @original_uc
152
+ new_geom = @original_uc.repeat(@x_repeat.get_value, @y_repeat.get_value, @z_repeat.get_value)
153
+ end
154
+ @geomViewer.unit_cell = new_geom
155
+ @geomEditor.unit_cell = new_geom
156
+ self.update_unit_cell = false
157
+ end
158
+
159
+ # @geomViewer.repeat = [@inspector.x_repeat, @inspector.y_repeat, @inspector.z_repeat]
160
+ @geomViewer.show_bonds = @show_bonds.get_value
161
+ @geomViewer.bond_length = @bond_length.get_value
162
+ @geomViewer.lighting = @show_lighting.get_value
163
+ @geomViewer.show_supercell = @show_cell.get_value
164
+ @geomViewer.show_xclip = @show_xclip.get_value
165
+ @geomViewer.show_yclip = @show_yclip.get_value
166
+ @geomViewer.show_zclip = @show_zclip.get_value
167
+ @geomViewer.background.alpha = ((@transparent_bg.get_value) ? 0 : 1)
168
+ @geomViewer.draw_scene
169
+ end
170
+
171
+ end
172
+ end