kristal 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/crystal/crystal.rb +63 -0
- data/crystal/lib/atom/atom.rb +34 -0
- data/crystal/lib/atom/lib/atom.rb +39 -0
- data/crystal/lib/atom/lib/elements.rb +27 -0
- data/crystal/lib/atom/test/test.rb +22 -0
- data/crystal/lib/collection/collection.rb +48 -0
- data/crystal/lib/collection/lib/atom.rb +21 -0
- data/crystal/lib/collection/lib/enum.rb +33 -0
- data/crystal/lib/collection/lib/rotate.rb +9 -0
- data/crystal/lib/collection/lib/selector.rb +18 -0
- data/crystal/lib/collection/lib/sort.rb +33 -0
- data/crystal/lib/collection/test/selector.rb +27 -0
- data/crystal/lib/collection/test/test.rb +24 -0
- data/crystal/lib/crystal/catom.rb +41 -0
- data/crystal/lib/crystal/surface.rb +35 -0
- data/crystal/lib/helper/all.rb +1 -0
- data/crystal/lib/helper/lib/array.rb +55 -0
- data/crystal/lib/helper/lib/file.rb +152 -0
- data/crystal/lib/helper/lib/fixnum.rb +8 -0
- data/crystal/lib/helper/lib/hash.rb +27 -0
- data/crystal/lib/helper/lib/kernel.rb +20 -0
- data/crystal/lib/helper/lib/matrix.rb +12 -0
- data/crystal/lib/helper/lib/numeric.rb +14 -0
- data/crystal/lib/helper/lib/object.rb +35 -0
- data/crystal/lib/helper/lib/string.rb +32 -0
- data/crystal/lib/helper/lib/vector.rb +54 -0
- data/crystal/lib/surface/surface.rb +30 -0
- data/crystal/lib/surface/test/test.rb +19 -0
- metadata +73 -0
data/crystal/crystal.rb
ADDED
@@ -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,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,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:
|