kristal 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,63 @@
1
+ require_relative 'lib/helper/all'
2
+ require_relative 'lib/collection/collection'
3
+ require_relative 'lib/surface/surface'
4
+
5
+ require_relative 'lib/crystal/catom'
6
+ require_relative 'lib/crystal/surface'
7
+
8
+ class Crystal
9
+ #TODO define select/reject etc for crystal module.
10
+ attr_reader :a, :b, :c, :alpha, :beta, :gamma,:atoms
11
+ def initialize *args
12
+ @a = @b = @c = args[0].to_f
13
+ @alpha = @beta = @gamma = 90.0
14
+ if args.length > 2
15
+ @b = args[1]
16
+ @c = args[2]
17
+ end
18
+ case args.length
19
+ when 4
20
+ @beta = args[3]
21
+ when 6
22
+ @alpha = args[3]
23
+ @beta = args[4]
24
+ @gamma = args[5]
25
+ end
26
+ @abc2xyz = conversion_matrix
27
+ @xyz2abc = @abc2xyz.inv
28
+ @atoms = Collection.new
29
+ @crystal = []
30
+ end
31
+ def << arg
32
+ case arg
33
+ when Atom
34
+ arg.crystal = self
35
+ @atoms << arg
36
+ @crystal << CAtom.new(arg)
37
+ when CAtom
38
+ arg.crystal = self
39
+ @atoms << arg.atom
40
+ @crystal << arg
41
+ when Collection
42
+ arg.crystal = self
43
+ @atoms = arg
44
+ @crystal = [].tap{|t| @atoms.each{|a| t << CAtom.new(a)}}
45
+ end
46
+ return self
47
+ end
48
+ def frac(v) @xyz2abc * v.to_v end
49
+ def real(v) @abc2xyz * v.to_v end
50
+ def [] i, j = nil
51
+ j.nil?? @crystal[i] : @crystal[i,j]
52
+ end
53
+ def []= i, v
54
+ @crystal[i] = CAtom.new(arg) if v.is_a? Atom
55
+ end
56
+ def to_s
57
+ String.new.tap{|s| @crystal.each{|e| s << e.to_s << "\n"}}
58
+ end
59
+ private
60
+ def method_missing *args, &block
61
+ @atoms.send(*args, &block)
62
+ end
63
+ end
@@ -0,0 +1,34 @@
1
+ require_relative "lib/atom"
2
+ require_relative "lib/elements"
3
+ class Atom
4
+ attr_reader :pos, :crystal, :type, :fixed
5
+ def initialize params = {}
6
+ @type = Elements[params[:type]] || Elements[:X]
7
+ @name = (params[:name]||@type.name).to_sym
8
+ @pos = (params[:pos].to_v rescue nil)
9
+ @crystal = params[:crystal]
10
+ @fixed = params[:fixed]
11
+ end
12
+ def type= (el)
13
+ @type = Elements[el.to_sym]
14
+ @name = @type.name
15
+ end
16
+ def name() @name.to_s end
17
+ def name=(s)
18
+ @name = s.to_sym
19
+ end
20
+ def pos=(v)
21
+ @pos = v.to_v
22
+ end
23
+ def fixed=(*a)
24
+ a.flatten!
25
+ @fixed = a.length == 3 ? a : a * 3
26
+ end
27
+ def crystal=(c)
28
+ @crystal = c if (c.is_a? Crystal rescue false)
29
+ end
30
+ def evaluate str, b=nil
31
+ str = str.gsub(/={1,2}/,"==").gsub(/(\[[^\]]*\])/,'\1.to_v')
32
+ eval(str) ? true : false
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ class Atom
2
+ def x() @pos[0] end
3
+ def y() @pos[1] end
4
+ def z() @pos[2] end
5
+ def x=(p)
6
+ @pos = Vector[p,y,z]
7
+ end
8
+ def y=(p)
9
+ @pos = Vector[x,p,z]
10
+ end
11
+ def z=(p)
12
+ @pos = Vector[x,y,p]
13
+ end
14
+ def has_crystal?() !(@crystal.nil?) end
15
+ def move! v, n = 1
16
+ @pos += v.to_v * n
17
+ self
18
+ end
19
+ def move v, n = 1
20
+ ret = self.dup
21
+ ret.move! v,n
22
+ end
23
+ def dist other
24
+ pos = other.is_a?(Atom) ? other.pos : other.to_v
25
+ has_crystal? ? crystal.dist(self, other):(@pos - pos).magnitude
26
+ end
27
+ def dup
28
+ Atom.new(to_hash)
29
+ end
30
+ def to_hash
31
+ { :type => @type.name.to_sym, :name => @name, :pos => @pos, :fixed => @fixed, :crystal => @crystal }
32
+ end
33
+ def to_s f = '%8.4f'
34
+ ("%-6s" + "#{f} " * 3) % ([name] + pos.to_a) + (fixed.nil? ? "" : fixed.map{|v| v ? 0 : 1}.join(" "))
35
+ end
36
+ def inspect
37
+ to_hash.to_s
38
+ end
39
+ end
@@ -0,0 +1,27 @@
1
+ class Atom
2
+ Elements = {
3
+ :O => {
4
+ :z => 8,
5
+ :m => 15.999,
6
+ :fname => "Oxygen",
7
+ :name => :O,
8
+ :pp => []
9
+ },
10
+ :Ti => {
11
+ :z => 22,
12
+ :m => 47.867,
13
+ :fname => "Titanium",
14
+ :name => :Ti,
15
+ :pp => []
16
+ },
17
+ :X => {
18
+ :z => 1,
19
+ :m => 1,
20
+ :fname => "Unspecified",
21
+ :name => :X,
22
+ :pp => []
23
+ }
24
+ }
25
+ Elements.method_access = true
26
+ Elements.each{ |_,v| v.method_access = true }
27
+ end
@@ -0,0 +1,22 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+ require_relative "../atom.rb"
4
+
5
+ describe "Atom" do
6
+ before do
7
+ @atom = Atom.new :element => :Os, :pos => [0,0,0]
8
+ @atom2 = Atom.new :pos => [0,3,4]
9
+ end
10
+ it "can be copied around" do
11
+ @atom2 = @atom.move([2,3,4])
12
+ @atom2.pos.must_equal(Vector[2,3,4])
13
+ @atom.pos.must_equal(Vector[0,0,0])
14
+ end
15
+ it "and can be moved" do
16
+ @atom.move!(Vector[1,0,0],2).pos.must_equal(Vector[2,0,0])
17
+ end
18
+ it "can calculate distances" do
19
+ @atom.dist(@atom2).must_equal(5)
20
+ @atom2.dist([2,3,4]).must_equal(2)
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ require_relative "../atom/atom"
2
+ require_relative "lib/enum"
3
+ require_relative "lib/selector"
4
+ require_relative "lib/sort"
5
+ require_relative "lib/atom"
6
+ require_relative "lib/rotate"
7
+
8
+ class Collection
9
+ attr_reader :crystal
10
+ def initialize *args
11
+ @collection = []
12
+ @tol = 0
13
+ [*args].each{ |arg| self << arg }
14
+ end
15
+ def << arg
16
+ if arg.is_a? Atom
17
+ @collection << arg unless @collection.include? arg
18
+ return self
19
+ elsif arg.is_a? Collection
20
+ @collection += arg.to_a
21
+ @collection.uniq!
22
+ return self
23
+ elsif arg.is_a? Array
24
+ @collection += arg if arg.all?{|v| v.is_a? Atom}
25
+ @collection.uniq!
26
+ return self
27
+ end
28
+ return false
29
+ end
30
+ def length() @collection.length end
31
+ def pop n = 1
32
+ @collection.pop n
33
+ end
34
+ def [] i, j = nil
35
+ j.nil?? @collection[i] : Collection.new.tap{|t| t << @collection[i,j]}
36
+ end
37
+ def []= index, atom
38
+ if atom.is_a?(Atom)
39
+ @collection[index]=atom
40
+ @collection = @collection.uniq.compact
41
+ true
42
+ end
43
+ false
44
+ end
45
+ def to_s
46
+ String.new.tap{|s| @collection.each{|e| s << e.to_s << "\n"}}
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ class Collection
2
+ def move! v, n=1
3
+ @collection.each{|atom| atom.move! v, n}
4
+ return self
5
+ end
6
+ def move v, n=1
7
+ dup.move! v, n
8
+ end
9
+ def fixed= bool = true
10
+ @collection.each{|atom| atom.fixed = bool}
11
+ end
12
+ def type= str
13
+ @collection.each{|atom| atom.type = str}
14
+ end
15
+ def name= str
16
+ @collection.each{|atom| atom.name = str}
17
+ end
18
+ def crystal= crystal
19
+ @collection.each{|atom| atom.crystal = crystal}
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ class Collection
2
+ # include Enumerable
3
+ def each
4
+ if block_given?
5
+ @collection.each { |e| yield(e) }
6
+ else
7
+ Enumerator.new(self, :each)
8
+ end
9
+ end
10
+ def map!
11
+ @collection.map!{|c| yield(c); c}
12
+ end
13
+ def map &block
14
+ dup.map!(&block)
15
+ end
16
+ def clone
17
+ Collection.new.tap{|t| t << @collection }
18
+ end
19
+ def dup
20
+ Collection.new.tap do |t|
21
+ @collection.each do |e|
22
+ t << e.dup
23
+ end
24
+ end
25
+ end
26
+ def to_a
27
+ @collection
28
+ end
29
+ def remove arg
30
+ arg.is_a?(Atom)? @collection.delete(arg): @collection.delete_at(arg)
31
+ end
32
+ alias :delete :remove
33
+ end
@@ -0,0 +1,9 @@
1
+ class Collection
2
+ def rotate! d, o = Vector[0,0,0], a
3
+ @collection.each{|v| v.pos = v.pos.rotate(d.to_v,o.to_v,a)}
4
+ self
5
+ end
6
+ def rotate *args
7
+ dup.rotate! *args
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ class Collection
2
+ def select! str = nil, &block
3
+ @collection.select!{|v| v.evaluate(str)} unless str.nil?
4
+ @collection.select!(&block) if block_given?
5
+ self
6
+ end
7
+ def reject! str = nil, &block
8
+ @collection.reject!{|v| v.evaluate(str)} unless str.nil?
9
+ @collection.reject!(&block) if block_given?
10
+ self
11
+ end
12
+ def select str = nil, &block
13
+ dup.select! str, &block
14
+ end
15
+ def reject str = nil, &block
16
+ dup.select! str, &block
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ class Collection
2
+ def sort! *keys
3
+ tol = []
4
+ tol << keys.pop while keys[-1].is_a? Numeric
5
+ @collection.sort! do |a,b|
6
+ result = 0
7
+ ks = keys.dup
8
+ t = tol.dup
9
+ while result == 0 and ks.length > 0
10
+ key = ks.shift
11
+ t.push @tol if t.length == 0
12
+ case key
13
+ when :type
14
+ result = (a.type.name <=> b.type.name)
15
+ when Symbol
16
+ result = a.send(key)
17
+ if result.is_a? Numeric
18
+ result = result.approx_sort(b.send(key), t.pop)
19
+ else
20
+ result = result <=> b.send(key)
21
+ end
22
+ else
23
+ result = (key.to_a * a.pos).approx_sort(key.to_a * b.pos, t.pop)
24
+ end
25
+ end
26
+ result
27
+ end
28
+ return @collection
29
+ end
30
+ def sort *keys
31
+ dup.sort! *keys
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+ require_relative "../../helper/lib/array"
4
+
5
+ describe "Selector" do
6
+ before "parser and yielder" do
7
+ def parse arg
8
+ conj = arg.scan(/and|or/).map{|v| v == 'and' ? :& : :|}
9
+ args = arg.split /and|or/
10
+ procs = []
11
+ args.each do |arg|
12
+ operator = arg.scan(/[>=<]{1,2}/)[0]
13
+ arg = arg.split(operator).map{|v| v.numeric?? Float(v) : v} #.to_sym}
14
+ procs.push Proc.new{|x| x.send(operator, arg[1])}
15
+ end
16
+ var = a
17
+ return Pr
18
+ end
19
+ def calc *args
20
+ yield *args
21
+ end
22
+ end
23
+ it "parses (and/or)s" do
24
+ c = "x < 10 and y > 10 or z > 1"
25
+ assert calc(11,1,2, &parse(c))
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+ require_relative "../Collection.rb"
4
+
5
+ describe "Simple Cubic" do
6
+ before do
7
+ a = Atom.new :element => :Ti, :pos => [0,0,0]
8
+ @coll = Collection.new
9
+ @coll << a << a.move([0,0,1])
10
+ @coll << @coll.move([0,1,0])
11
+ @coll << @coll.move([1,1,0])
12
+ end
13
+ it "can be cloned and deep-cloned" do
14
+ clone = @coll.clone
15
+ deep_clone = @coll.dup
16
+ assert clone.object_id != @coll.object_id
17
+ assert clone[0].object_id == @coll[0].object_id
18
+ assert deep_clone.object_id != @coll.object_id
19
+ assert deep_clone[0].object_id != @coll[0].object_id
20
+ end
21
+ it "can be sorted in arbitrary directions" do
22
+
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ class CAtom
2
+ attr_reader :atom
3
+ def initialize arg
4
+ if arg.is_a? Atom
5
+ @atom = arg
6
+ else
7
+ @atom = Atom.new(arg)
8
+ end
9
+ end
10
+ def pos= arg
11
+ @atom.pos = @atom.crystal.real(arg)
12
+ end
13
+ def pos
14
+ @atom.crystal.frac(@atom.pos)
15
+ end
16
+ def x() pos[0] end
17
+ def y() pos[0] end
18
+ def z() pos[0] end
19
+ def x=(p)
20
+ @atom.pos = @atom.crystal.real([p,y,z])
21
+ end
22
+ def y=(p)
23
+ @atom.pos = @atom.crystal.real([x,p,z])
24
+ end
25
+ def z=(p)
26
+ @atom.pos = @atom.crystal.real([x,y,p])
27
+ end
28
+ def move!(v, n = 1)
29
+ @atom.move! @atom.crystal.real(v), n
30
+ end
31
+ def move(v, n)
32
+ @atom.move @atom.crystal.real(v), n
33
+ end
34
+ def to_s f = '%8.4f'
35
+ ("%-6s" + "#{f} " * 3) % ([@atom.name] + pos.to_a) + (fixed.nil? ? "" : fixed.map{|v| v ? 0 : 1}.join(" "))
36
+ end
37
+ private
38
+ def method_missing *args
39
+ @atom.send(*args)
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ class Crystal
2
+ def surface v, p, tol = 0
3
+ Surface.new(surface_converter(v), real(p), tol)
4
+ end
5
+ private
6
+ def conversion_matrix
7
+ cos = -> x {Math.cos( x * Math::PI / 180 ).round(10)}
8
+ sin = -> x {Math.sin( x * Math::PI / 180 ).round(10)}
9
+ Matrix[
10
+ [@a, @b * cos[@gamma], @c * cos[@beta]],
11
+ [0, @b * sin[@gamma], @c * (cos[@alpha] - cos[@beta] * cos[@gamma]) / sin[@gamma]],
12
+ [0, 0, @c * (1 - cos[@alpha] ** 2 - cos[@beta] ** 2 - cos[@gamma] ** 2 + 2 * cos[@gamma] * cos[@alpha] * cos[@beta])]
13
+ ]
14
+ end
15
+ def surface_converter arr
16
+ x = arr[0] == 0 ? Vector[1.0 / 0, 0, 0] : real([1.0 / arr[0], 0, 0])
17
+ y = arr[1] == 0 ? Vector[0,1.0/0,0] : real([0, 1.0 / arr[1], 0])
18
+ z = arr[2] == 0 ? Vector[0,0,1.0/0] : real([0, 0, 1.0 / arr[2]])
19
+ hxy = get_h x, y
20
+ get_h hxy, z
21
+ end
22
+ def get_h v1, v2
23
+ m = [v1.magnitude, v2.magnitude, (v1-v2).magnitude]
24
+ if m[0] == 1.0/0
25
+ v2
26
+ elsif m[1] == 1.0/0
27
+ v1
28
+ else
29
+ s = m.reduce(:+)/2
30
+ h = 2 * (s * m.map{|v| s - v}.reduce(:*)).sqrt / m[2]
31
+ angle = (Math.acos(h/m[0]) * 180 / Math::PI).round(10)
32
+ (v1/m[0]).rotate(v1.cross(v2), angle)*h
33
+ end
34
+ end
35
+ end
@@ -0,0 +1 @@
1
+ Dir[File.dirname(__FILE__) + "/lib/*.rb"].each {|file| require_relative file }
@@ -0,0 +1,55 @@
1
+ require_relative 'string'
2
+ require_relative 'object'
3
+ require_relative 'numeric'
4
+ class Array
5
+ #exclusive elements
6
+ def ^(other)
7
+ (self - other) | (other - self)
8
+ end
9
+ #new array from two arrays.
10
+ def combine(other, op=nil)
11
+ return [] if self.empty? || other.empty?
12
+ clipped = self[0..other.length-1]
13
+ zipped = clipped.zip(other)
14
+ if op
15
+ zipped.map { |a, b| a.send(op, b) }
16
+ else
17
+ zipped.map { |a, b| yield(a, b) }
18
+ end
19
+ end
20
+ alias :old_x :*
21
+ def *(x)
22
+ if (x.is_a? Vector rescue false)
23
+ (Matrix[self] * x)[0]
24
+ else
25
+ old_x x
26
+ end
27
+ end
28
+ def to_v
29
+ Vector[*self]
30
+ end
31
+ #TODO Rewrite/Rename these:
32
+ def numbers!
33
+ i = length - 1
34
+ self.reverse_each do |elem|
35
+ if elem.is_a? Array
36
+ self[i] = elem.numbers!
37
+ else
38
+ elem.numeric? ? self[i] = Float(elem):self.delete_at(i)
39
+ end
40
+ i -= 1
41
+ end
42
+ return self
43
+ end
44
+ def numerify!
45
+ self.each_index do |i|
46
+ if self[i].is_a? Array
47
+ self[i] = self[i].numerify!
48
+ else
49
+ self[i] = Float(self[i]) if self[i].numeric?
50
+ end
51
+ end
52
+ return self
53
+ end
54
+
55
+ end
@@ -0,0 +1,152 @@
1
+ class File
2
+ # repeat last ff/rew command
3
+ def repeat
4
+ if @find_registry[:method] == :ff
5
+ seek(1,1)
6
+ ff(@find_registry[:re])
7
+ else
8
+ rew(@find_registry[:re])
9
+ end
10
+ end
11
+ # find all occurences of args (with or)
12
+ # in the file egardless of the current
13
+ # position
14
+ def findall *args
15
+ p = pos
16
+ seek(0)
17
+ results = [ ff(*args).pos ]
18
+ while !repeat.nil?
19
+ results.push pos
20
+ end
21
+ seek(p)
22
+ return results
23
+ end
24
+ # do not rewind, just give
25
+ # the position that rew would
26
+ # rewind to.
27
+ def rew? *args
28
+ p = pos
29
+ p1 = rew(*args)
30
+ p1.nil? ? p1 = nil : p1 = pos
31
+ seek p
32
+ return p1
33
+ end
34
+ # do not fast forward, just give
35
+ # the position that ff would fast
36
+ # forward to.
37
+ def ff? *args
38
+ p = pos
39
+ p1 = ff(*args)
40
+ p1.nil? ? p1 = nil : p1 = pos
41
+ seek p
42
+ return p1
43
+ end
44
+ # rewind until it finds one of args
45
+ # supplied as string or regex
46
+ # returns nil if nothing found
47
+ def rew *args
48
+ p = pos
49
+ psearch = p
50
+ re = Regexp.union(args)
51
+ @find_registry = { :method => :rew, :re => re }
52
+ begin
53
+ break if psearch == 0
54
+ 512 > pos ? buffer = pos : buffer = 512 # buffer = [512, p].min
55
+ seek(-buffer, 1)
56
+ psearch = pos
57
+ chunk = read(buffer+80)
58
+ seek(-chunk.length, 1)
59
+ check = chunk.rindex(re)
60
+ if !check.nil? and buffer <= check
61
+ chunk.slice!(-(chunk.length-buffer)..-1)
62
+ check = chunk.rindex(re)
63
+ end
64
+ end while check.nil? or buffer <= check
65
+ begin
66
+ if buffer <= check
67
+ seek p
68
+ return nil
69
+ else
70
+ seek(psearch + check)
71
+ return self
72
+ end
73
+ rescue
74
+ seek p
75
+ return nil
76
+ end
77
+ end
78
+ # fast forward until it finds one of args
79
+ # supplied as string or regex
80
+ # returns nil if nothing found
81
+ def ff *args
82
+ p = pos
83
+ re = Regexp.union(args)
84
+ @find_registry = { :method => :ff, :re => re }
85
+ buffer = 512
86
+ begin
87
+ psearch = pos
88
+ chunk = read(buffer)
89
+ seek(-80,1) unless eof?
90
+ check = chunk.index(re)
91
+ end while check.nil? and !eof?
92
+ begin
93
+ seek(psearch + check)
94
+ return self
95
+ rescue
96
+ seek p
97
+ return nil
98
+ end
99
+ end
100
+ # unix head like utility, returns lines
101
+ # as an array. optional arguments cur for
102
+ # start from current line (default is start)
103
+ # from beggining; reset for do not change
104
+ # current position (default).
105
+ def head(n, cur=false, reset=true)
106
+ #eof guard
107
+ p = pos
108
+ line_start
109
+ lines = []
110
+ seek(0) unless cur
111
+ for i in 1..n
112
+ break if eof?
113
+ lines.push(readline.chomp)
114
+ end
115
+ seek(p) if reset
116
+ return lines
117
+ end
118
+ # unix tail like utility, returns lines
119
+ # as an array. optional arguments cur for
120
+ # start from current line (default is start)
121
+ # from end; reset for do not change
122
+ # current position (default).
123
+ def tail(n, cur=false, reset=true)
124
+ p = pos
125
+ chunks = ''
126
+ lines = 0
127
+ cur ? line_end : seek(size)
128
+ begin
129
+ p1 = pos
130
+ p1 < 512 ? buffer = p1 : buffer = 512
131
+ seek(-buffer, 1)
132
+ chunk = read(buffer)
133
+ lines += chunk.count("\n")
134
+ chunks = chunk + chunks
135
+ seek(-chunk.size,1)
136
+ end while lines < ( n + 1 ) && p1 != 0
137
+ ary = chunks.split(/\n/)
138
+ reset ? seek(p) : seek(p1-1-(ary.last(n).join("\n").length))
139
+ return ary.last(n)
140
+ end
141
+ # move to the first char of current line
142
+ def line_start
143
+ unless readchar == "\n"
144
+ return rew(/^/)
145
+ end
146
+ return self
147
+ end
148
+ # move to the last char of current line
149
+ def line_end
150
+ return ff(/$/)
151
+ end
152
+ end
@@ -0,0 +1,8 @@
1
+ class Fixnum
2
+ def zero_pad total
3
+ "%0#{total}d" % self
4
+ end
5
+ def length
6
+ self.to_s.length
7
+ end
8
+ end
@@ -0,0 +1,27 @@
1
+ class Hash
2
+ def extract(*ks)
3
+ existing = keys & ks
4
+ Hash[existing.zip(values_at(*existing))]
5
+ end
6
+ def has_shape?(shape)
7
+ all? do |k, v|
8
+ Hash === v ? v.has_shape?(shape[k]) : shape[k] === v
9
+ end
10
+ end
11
+ attr_accessor :method_access
12
+ alias :old_m :method_missing
13
+ def method_missing(m)
14
+ if (@method_access.nil? ? (@@method_access rescue false) : @method_access)
15
+ if has_key? m
16
+ return self[m]
17
+ elsif has_key? m.to_s
18
+ return self[m.to_s]
19
+ end
20
+ end
21
+ return old_m(m)
22
+ end
23
+ class << self
24
+ def method_access=(bool) @@method_access = bool end
25
+ def method_access() @@method_access end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ module Kernel
2
+ def fn(*funs)
3
+ -> x do
4
+ funs.inject(x) do |v,f|
5
+ Proc === f ? f.call(v) : v.send(f)
6
+ end
7
+ end
8
+ end
9
+ def prompt(text='', conversion=nil)
10
+ print text unless text.empty?
11
+ input = gets.chomp
12
+ CONVERSIONS.include?(conversion) ? input.send(conversion) : input
13
+ end
14
+ def with(object,&block)
15
+ object.instance_eval(&block)
16
+ end
17
+ # def with(o, &blk)
18
+ # o.tap(&blk)
19
+ # end
20
+ end
@@ -0,0 +1,12 @@
1
+ require 'matrix'
2
+ class Matrix
3
+ def to_s format='%.4f'
4
+ return "" if empty?
5
+ f = column_vectors.map(&->x{"%#{x.longest_string(format)}s"}).join(" ")
6
+ map{|v| format % v}.row_vectors.map{|v| f % v.to_a}.join("\n")
7
+ end
8
+ def inspect format = '%.4f'
9
+ return "Matrix[]" if empty?
10
+ (["\nMatrix[ "] + [" "]*(row_size-1)).zip(to_s.lines).map{|v| v.join}.join() + " ]"
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ class Numeric
2
+ (Math.methods - Module.methods - ["hypot", "ldexp"]).each do |method|
3
+ define_method method do
4
+ Math.send method, self
5
+ end
6
+ end
7
+ def approx_sort other, tol = self.to_f/1000
8
+ d = self - other
9
+ d > tol ? 1 : (-d > tol ? -1 : 0)
10
+ end
11
+ def numeric?
12
+ true
13
+ end
14
+ end
@@ -0,0 +1,35 @@
1
+ class Object
2
+ alias :old_display :display
3
+ def display(new_line = true)
4
+ m = new_line ? :puts : :print
5
+ self.tap { |o| send(m, o) }
6
+ end
7
+ def it
8
+ self
9
+ end
10
+ def numeric?
11
+ false
12
+ end
13
+ def deep_clone
14
+ return @deep_cloning_obj if @deep_cloning
15
+ @deep_cloning_obj = clone
16
+ @deep_cloning_obj.instance_variables.each do |var|
17
+ val = @deep_cloning_obj.instance_variable_get(var)
18
+ begin
19
+ @deep_cloning = true
20
+ val = val.deep_clone
21
+ rescue TypeError
22
+ next
23
+ ensure
24
+ @deep_cloning = false
25
+ end
26
+ @deep_cloning_obj.instance_variable_set(var, val)
27
+ end
28
+ deep_cloning_obj = @deep_cloning_obj
29
+ @deep_cloning_obj = nil
30
+ deep_cloning_obj
31
+ end
32
+ def to_sym
33
+ self.to_s.to_sym
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ class String
2
+ def paste str, j = ' ', align = :left
3
+ if str.is_a? Array
4
+ tmp = self.dup
5
+ str.each do |s|
6
+ tmp = tmp.paste s, j, align
7
+ end
8
+ tmp
9
+ else
10
+ slines = self.lines.map(&:chomp)
11
+ s1lines = str.lines.map(&:chomp)
12
+ (s1lines.count - slines.count).times{slines.push ""}
13
+ l = slines.map(&:length).max
14
+ l1 = s1lines.map(&:length).max
15
+ case align
16
+ when :left
17
+ f = "%-#{l}s"
18
+ f1 = "%-#{l1}s"
19
+ when :right
20
+ f = "%#{l}s"
21
+ f1 = "%#{l1}s"
22
+ else
23
+ f = '%s'
24
+ f1 = '%s'
25
+ end
26
+ slines.map{|v| f % v}.zip(s1lines.map{|v| f1 % v}).map{|v| v.join(j)}.join("\n")
27
+ end
28
+ end
29
+ def numeric?
30
+ Float(self) != nil rescue false
31
+ end
32
+ end
@@ -0,0 +1,54 @@
1
+ class Vector
2
+ def to_s format = '%.4f'
3
+ return "" if size == 0
4
+ w = longest_string format
5
+ map{|v| "%#{w}s" % (format % v)}.to_a.join("\n")
6
+ end
7
+ def inspect format = '%.4f'
8
+ return "Vector[]" if size == 0
9
+ (["\nVector[ "] + [" "]*(size-1)).zip(to_s.lines).map{|v| v.join}.join() +" ]"
10
+ end
11
+ def longest_string format
12
+ map{|x| (format % x).length}.max
13
+ end
14
+ def to_v
15
+ self
16
+ end
17
+ def rotate direction, origin = Vector[0,0,0], angle
18
+ Vector[*(rotation_matrix(direction, origin, angle) * Vector[*(self),1]).to_a[0,3]]
19
+ end
20
+ def cross v2
21
+ raise ArgumentError, "Vectors should be 3D" if self.size != 3 or v2.size != 3
22
+ Vector[
23
+ self[1]*v2[2] - self[2]*v2[1],
24
+ self[2]*v2[0] - self[0]*v2[2],
25
+ self[0]*v2[1] - self[1]*v2[0]
26
+ ]
27
+ end
28
+ private
29
+ def rotation_matrix direction, origin, theta
30
+ u,v,w = direction.normalize.to_a
31
+ a,b,c = origin.to_a
32
+ cos = Math.cos(theta / 180.0 * Math::PI).round(10)
33
+ sin = Math.sin(theta / 180.0 * Math::PI).round(10)
34
+ return Matrix.columns([
35
+ [ u**2+cos*(v**2+w**2),
36
+ u*v*(1-cos)+w*sin,
37
+ u*w*(1-cos)-v*sin,
38
+ 0 ],
39
+ [ v*u*(1-cos)-w*sin,
40
+ v**2+cos*(u**2+w**2),
41
+ v*w*(1-cos)+u*sin,
42
+ 0],
43
+ [ u*w*(1-cos)+v*sin,
44
+ w*v*(1-cos)-u*sin,
45
+ w**2+cos*(v**2+u**2),
46
+ 0 ],
47
+ [ (a*(v**2+w**2)-u*(b*v+c*w))*(1-cos)+(b*w-c*v)*sin,
48
+ (b*(u**2+w**2)-v*(a*u+c*w))*(1-cos)+(c*u-a*w)*sin,
49
+ (c*(u**2+v**2)-w*(a*u+b*v))*(1-cos)+(a*v-b*u)*sin,
50
+ 1 ]
51
+ ])
52
+ end
53
+
54
+ end
@@ -0,0 +1,30 @@
1
+ class Surface
2
+ attr_accessor :tol
3
+ attr_reader :normal, :p
4
+ def initialize v, p, tol = 0
5
+ @normal = v.to_v || nil
6
+ @p = p.to_v || Vector[0,0,0]
7
+ @tol = tol
8
+ end
9
+ def normal=(a) @normal=a.to_v end
10
+ def p=(a) @p=a.to_v end
11
+ def above? p, tol = @tol
12
+ dist(p) + tol < 0
13
+ end
14
+ def below? p, tol = @tol
15
+ dist(p) - tol > 0
16
+ end
17
+ def include? p, tol = @tol
18
+ dist(p).abs <= tol
19
+ end
20
+ def dist p
21
+ p = p.pos if p.is_a? Atom
22
+ (p.to_v- @p).to_a * @normal / @normal.magnitude
23
+ end
24
+ def rotate! d,o,a
25
+ @normal = @normal.rotate!(d.to_v,o.to_v,a)
26
+ end
27
+ def move! v
28
+ @p += v.to_v
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+ require_relative "../surface.rb"
4
+
5
+ describe "Surface" do
6
+ before do
7
+ @surface = Surface.new [0,0,1], [1,1,1], 0.5
8
+ end
9
+ it "know up and down" do
10
+ assert @surface.below? [-1,-3,5]
11
+ assert @surface.above? [5,13,0]
12
+ end
13
+ it "tolerates deviations" do
14
+ assert @surface.include? [2,5,0.5]
15
+ end
16
+ it "gives the distances" do
17
+ @surface.dist([0,0,0]).must_equal -1
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kristal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sencer Selcuk
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-10 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: A crystal structure manipulator wanna-be
15
+ email: sencerselcuk@twitter
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - crystal/crystal.rb
21
+ - crystal/lib/atom/atom.rb
22
+ - crystal/lib/atom/lib/atom.rb
23
+ - crystal/lib/atom/lib/elements.rb
24
+ - crystal/lib/atom/test/test.rb
25
+ - crystal/lib/collection/collection.rb
26
+ - crystal/lib/collection/lib/atom.rb
27
+ - crystal/lib/collection/lib/enum.rb
28
+ - crystal/lib/collection/lib/rotate.rb
29
+ - crystal/lib/collection/lib/selector.rb
30
+ - crystal/lib/collection/lib/sort.rb
31
+ - crystal/lib/collection/test/selector.rb
32
+ - crystal/lib/collection/test/test.rb
33
+ - crystal/lib/crystal/catom.rb
34
+ - crystal/lib/crystal/surface.rb
35
+ - crystal/lib/helper/all.rb
36
+ - crystal/lib/helper/lib/array.rb
37
+ - crystal/lib/helper/lib/file.rb
38
+ - crystal/lib/helper/lib/fixnum.rb
39
+ - crystal/lib/helper/lib/hash.rb
40
+ - crystal/lib/helper/lib/kernel.rb
41
+ - crystal/lib/helper/lib/matrix.rb
42
+ - crystal/lib/helper/lib/numeric.rb
43
+ - crystal/lib/helper/lib/object.rb
44
+ - crystal/lib/helper/lib/string.rb
45
+ - crystal/lib/helper/lib/vector.rb
46
+ - crystal/lib/surface/surface.rb
47
+ - crystal/lib/surface/test/test.rb
48
+ homepage:
49
+ licenses: []
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 1.8.23
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: A crystal structure manipulator wanna-be
72
+ test_files: []
73
+ has_rdoc: