kristal 0.0.1

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,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: