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