gmath3D 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rectangle.rb ADDED
@@ -0,0 +1,89 @@
1
+ require 'matrix'
2
+
3
+ require 'gmath3D'
4
+
5
+ module GMath3D
6
+ class Rectangle < Geom
7
+ public
8
+ attr_accessor:base_point
9
+ attr_accessor:u_vector
10
+ attr_accessor:v_vector
11
+
12
+ def initialize(base_point_arg = Vector3.new(), u_vector_arg = Vector3.new(1,0,0), v_vector_arg = Vector3.new(0,1,0))
13
+ Util.check_arg_type(::Vector3, base_point_arg)
14
+ Util.check_arg_type(::Vector3, u_vector_arg)
15
+ Util.check_arg_type(::Vector3, v_vector_arg)
16
+ super()
17
+ self.base_point = base_point_arg
18
+ self.u_vector = u_vector_arg
19
+ self.v_vector = v_vector_arg
20
+ end
21
+
22
+ def point(u, v)
23
+ Util.check_arg_type(::Numeric, u)
24
+ Util.check_arg_type(::Numeric, v)
25
+ return base_point + u_vector*u + v_vector*v
26
+ end
27
+
28
+ def edges
29
+ edge_ary = Array.new(4)
30
+ edge_ary[0] = FiniteLine.new( base_point, base_point+u_vector)
31
+ edge_ary[1] = FiniteLine.new( base_point+u_vector, base_point+u_vector+v_vector)
32
+ edge_ary[2] = FiniteLine.new( base_point+u_vector+v_vector, base_point+v_vector)
33
+ edge_ary[3] = FiniteLine.new( base_point+v_vector, base_point)
34
+ return edge_ary
35
+ end
36
+
37
+ def normal
38
+ return (u_vector.cross(v_vector)).normalize()
39
+ end
40
+
41
+ def opposite_point
42
+ return base_point + u_vector + v_vector
43
+ end
44
+
45
+ def center_point
46
+ return base_point + u_vector*0.5 + v_vector*0.5
47
+ end
48
+
49
+ def area
50
+ return (u_vector.cross(v_vector)).length
51
+ end
52
+
53
+ def uv_parameter(check_point)
54
+ Util.check_arg_type(::Vector3, check_point)
55
+ mat = Matrix[[u_vector.x, u_vector.y, u_vector.z],
56
+ [v_vector.x, v_vector.y, v_vector.z],
57
+ [normal.x, normal.y, normal.z]]
58
+ vec = (check_point - base_point).to_column_vector
59
+ ans = mat.t.inv*vec
60
+ return ans[0,0], ans[1,0]
61
+ end
62
+
63
+ def distance(target)
64
+ # with Point
65
+ if(target.kind_of?(Vector3))
66
+ return distance_to_point(target)
67
+ elsif(target.kind_of?(Line))
68
+ #with Line
69
+ # return distance_to_line(target)
70
+ end
71
+ Util.raise_argurment_error(target)
72
+ end
73
+
74
+ private
75
+ def distance_to_point(check_point)
76
+ u,v = self.uv_parameter(check_point)
77
+ if(u >= 0 && u <= 1 && v >= 0 && v <= 1)
78
+ point_on_rect = self.point( u, v )
79
+ distance = point_on_rect.distance(check_point)
80
+ return distance, point_on_rect
81
+ end
82
+ # rectangle does not contain projected point
83
+ # check distance to FiniteLines
84
+ finite_lines = self.edges
85
+ return FiniteLine.ary_distanc_to_point(finite_lines, check_point)
86
+ end
87
+ end
88
+ end
89
+
data/lib/triangle.rb ADDED
@@ -0,0 +1,133 @@
1
+ require 'gmath3D'
2
+
3
+ module GMath3D
4
+ class Triangle < Geom
5
+ public
6
+ attr_accessor:vertices
7
+
8
+ def initialize(vertex_arg1 = Vector3.new(), vertex_arg2 = Vector3.new(1,0,0), vertex_arg3 = Vector3.new(0,1,0))
9
+ Util.check_arg_type(::Vector3, vertex_arg1)
10
+ Util.check_arg_type(::Vector3, vertex_arg2)
11
+ Util.check_arg_type(::Vector3, vertex_arg3)
12
+ super()
13
+ @vertices = Array.new([vertex_arg1, vertex_arg2, vertex_arg3])
14
+ end
15
+
16
+ def point( parameter )
17
+ Util.check_arg_type(::Array, parameter )
18
+ # TODO Argument check
19
+ return self.vertices[0]*parameter[0] + self.vertices[1]*parameter[1] + self.vertices[2]*parameter[2]
20
+ end
21
+
22
+ def edges
23
+ return_edges = Array.new(3)
24
+ return_edges[0] = FiniteLine.new(self.vertices[0], self.vertices[1])
25
+ return_edges[1] = FiniteLine.new(self.vertices[1], self.vertices[2])
26
+ return_edges[2] = FiniteLine.new(self.vertices[2], self.vertices[0])
27
+ return return_edges
28
+ end
29
+
30
+ def area
31
+ vec1 = vertices[1] - vertices[0]
32
+ vec2 = vertices[2] - vertices[0]
33
+ outer_product = vec1.cross(vec2)
34
+ return outer_product.length / 2.0
35
+ end
36
+
37
+ def center
38
+ return vertices.avg
39
+ end
40
+
41
+ def normal
42
+ vec1 = self.vertices[1] - self.vertices[0]
43
+ vec2 = self.vertices[2] - self.vertices[0]
44
+ return (vec1.cross(vec2).normalize)
45
+ end
46
+
47
+ def barycentric_coordinate( check_point )
48
+ Util.check_arg_type(::Vector3, check_point)
49
+
50
+ v0 = @vertices[0]
51
+ v1 = @vertices[1]
52
+ v2 = @vertices[2]
53
+
54
+ d1 = v1 - v0
55
+ d2 = v2 - v1
56
+ n = d1.cross(d2);
57
+ if((n.x).abs >= (n.y).abs && (n.x).abs >= (n.z).abs)
58
+ uu1 = v0.y - v2.y;
59
+ uu2 = v1.y - v2.y;
60
+ uu3 = check_point.y - v0.y;
61
+ uu4 = check_point.y - v2.y;
62
+ vv1 = v0.z - v2.z;
63
+ vv2 = v1.z - v2.z;
64
+ vv3 = check_point.z - v0.z;
65
+ vv4 = check_point.z - v2.z;
66
+ elsif((n.y).abs >= (n.z).abs)
67
+ uu1 = v0.z - v2.z;
68
+ uu2 = v1.z - v2.z;
69
+ uu3 = check_point.z - v0.z;
70
+ uu4 = check_point.z - v2.z;
71
+ vv1 = v0.x - v2.x;
72
+ vv2 = v1.x - v2.x;
73
+ vv3 = check_point.x - v0.x;
74
+ vv4 = check_point.x - v2.x;
75
+ else
76
+ uu1 = v0.x - v2.x;
77
+ uu2 = v1.x - v2.x;
78
+ uu3 = check_point.x - v0.x;
79
+ uu4 = check_point.x - v2.x;
80
+ vv1 = v0.y - v2.y;
81
+ vv2 = v1.y - v2.y;
82
+ vv3 = check_point.y - v0.y;
83
+ vv4 = check_point.y - v2.y;
84
+ end
85
+
86
+ denom = vv1 * uu2 - vv2* uu1
87
+ if(denom == 0.0)
88
+ return nil
89
+ end
90
+ b = Array.new(3)
91
+ oneOverDenom = 1.0 / denom ;
92
+ b[0] = (vv4*uu2 - vv2*uu4) * oneOverDenom;
93
+ b[1] = (vv1*uu3 - vv3*uu1) * oneOverDenom;
94
+ b[2] = 1.0 - b[0] - b[1];
95
+ return b;
96
+ end
97
+
98
+ def distance(target)
99
+ # with Point
100
+ if(target.kind_of?(Vector3))
101
+ return distance_to_point(target)
102
+ elsif(target.kind_of?(Line))
103
+ #with Line
104
+ # return distance_to_line(target)
105
+ end
106
+ Util.raise_argurment_error(target)
107
+ end
108
+
109
+ def contains( check_point )
110
+ Util.check_arg_type(Vector3, check_point )
111
+ plane = Plane.new( vertices[0], self.normal)
112
+ distance, projected_point = plane.distance(check_point)
113
+ return false if( distance > self.tolerance )
114
+ g_coord = self.barycentric_coordinate(check_point)
115
+ g_coord.each do |item|
116
+ return false if( item < 0 or 1 < item)
117
+ end
118
+ return true
119
+ end
120
+ private
121
+ def distance_to_point(target_point)
122
+ plane = Plane.new( vertices[0], self.normal)
123
+ distance, projected_point = plane.distance(target_point)
124
+ if( self.contains(projected_point))
125
+ return distance, projected_point
126
+ end
127
+ #check distance to FiniteLines
128
+ finite_lines = self.edges
129
+ return FiniteLine.ary_distanc_to_point(finite_lines, target_point)
130
+ end
131
+ end
132
+ end
133
+
data/lib/util.rb ADDED
@@ -0,0 +1,41 @@
1
+ module GMath3D
2
+ class Util
3
+ def self.check_arg_type(type, instance)
4
+ unless(instance.kind_of?(type))
5
+ raise(ArgumentError::new("type mismatch: #{instance.class} for #{type}"))
6
+ end
7
+ end
8
+
9
+ def self.raise_argurment_error(instance)
10
+ raise(ArgumentError::new("type mismatch: #{instance.class}"))
11
+ end
12
+ end
13
+ end
14
+
15
+ class Array
16
+ public
17
+ def sum
18
+ s, n = self.sum_with_number
19
+ return s
20
+ end
21
+ def avg
22
+ s, n = self.sum_with_number
23
+ return s / n
24
+ end
25
+
26
+ def sum_with_number
27
+ return nil, 0 if(self.size <= 0)
28
+ s = nil
29
+ n = 0
30
+ self.each do |v|
31
+ next if v.nil?
32
+ if(s==nil)
33
+ s = v
34
+ else
35
+ s += v
36
+ end
37
+ n += 1
38
+ end
39
+ return s, n
40
+ end
41
+ end
data/lib/vector3.rb ADDED
@@ -0,0 +1,116 @@
1
+ require 'gmath3D'
2
+
3
+ module GMath3D
4
+ class Vector3 < Geom
5
+ public
6
+ attr_accessor :x
7
+ attr_accessor :y
8
+ attr_accessor :z
9
+
10
+ def initialize(x=0.0,y=0.0,z=0.0)
11
+ super()
12
+ @x = x
13
+ @y = y
14
+ @z = z
15
+ end
16
+ def ==(rhs)
17
+ equals_inner(rhs)
18
+ end
19
+ def +(rhs)
20
+ add(rhs)
21
+ end
22
+ def -(rhs)
23
+ subtract(rhs)
24
+ end
25
+ def *(rhs)
26
+ multiply(rhs)
27
+ end
28
+ def /(rhs)
29
+ divide(rhs)
30
+ end
31
+
32
+ def dot(rhs)
33
+ Util.check_arg_type(Vector3, rhs)
34
+ self.x*rhs.x + self.y*rhs.y + self.z*rhs.z
35
+ end
36
+ def cross(rhs)
37
+ Util.check_arg_type(Vector3, rhs)
38
+ Vector3.new(
39
+ self.y*rhs.z - self.z*rhs.y,
40
+ self.z*rhs.x - self.x*rhs.z,
41
+ self.x*rhs.y - self.y*rhs.x)
42
+ end
43
+
44
+ def length
45
+ Math::sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
46
+ end
47
+ def distance(rhs)
48
+ Util.check_arg_type(Vector3, rhs)
49
+ Math::sqrt((self.x - rhs.x)*(self.x - rhs.x) + (self.y - rhs.y)*(self.y - rhs.y) + (self.z - rhs.z)*(self.z - rhs.z))
50
+ end
51
+ def distance(rhs)
52
+ Util.check_arg_type(Vector3, rhs)
53
+ Math::sqrt((self.x - rhs.x)*(self.x - rhs.x) + (self.y - rhs.y)*(self.y - rhs.y) + (self.z - rhs.z)*(self.z - rhs.z))
54
+ end
55
+
56
+ def angle(rhs)
57
+ Util.check_arg_type(Vector3, rhs)
58
+ vec1Length = self.length ;
59
+ vec2Length = rhs.length ;
60
+ return 0.0 if(vec1Length*vec2Length < self.tolerance )
61
+ v = self.dot(rhs)/(vec1Length*vec2Length)
62
+ Math::acos( v )
63
+ end
64
+ def normalize()
65
+ self / self.length.to_f
66
+ end
67
+
68
+ def parallel?(rhs)
69
+ Util.check_arg_type(Vector3, rhs)
70
+ return false if(self.length < self.tolerance or rhs.length < rhs.tolerance)
71
+ return false if(self.cross(rhs).length > self.tolerance)
72
+ return true
73
+ end
74
+ def same_direction?(rhs)
75
+ Util.check_arg_type(Vector3, rhs)
76
+ return false if(!parallel?(rhs))
77
+ return false if(self.dot(rhs) < self.tolerance)
78
+ return true
79
+ end
80
+
81
+ def project_to(rhs)
82
+ Util.check_arg_type(Vector3, rhs)
83
+ return Vector3.new, 0.0 if( rhs.length < rhs.tolerance )
84
+ parameter = self.dot( rhs ) / ( rhs.x * rhs.x + rhs.y * rhs.y + rhs.z * rhs.z ).to_f
85
+ return rhs*parameter, parameter
86
+ end
87
+ def to_column_vector
88
+ return Matrix.column_vector([x,y,z])
89
+ end
90
+ private
91
+ def equals_inner(rhs)
92
+ return false if( !rhs.kind_of?(Vector3) )
93
+ return false if((self.x - rhs.x).abs > @tolerance)
94
+ return false if((self.y - rhs.y).abs > @tolerance)
95
+ return false if((self.z - rhs.z).abs > @tolerance)
96
+ true
97
+ end
98
+ def add(rhs)
99
+ Util.check_arg_type(Vector3, rhs)
100
+ Vector3.new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
101
+ end
102
+ def subtract(rhs)
103
+ Util.check_arg_type(Vector3, rhs)
104
+ Vector3.new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
105
+ end
106
+ def multiply(rhs)
107
+ Util.check_arg_type(::Numeric, rhs)
108
+ Vector3.new(self.x * rhs, self.y * rhs, self.z * rhs)
109
+ end
110
+ def divide(rhs)
111
+ Util.check_arg_type(::Numeric, rhs)
112
+ Vector3.new(self.x.to_f / rhs, self.y / rhs.to_f, self.z / rhs.to_f)
113
+ end
114
+ end
115
+ end
116
+
data/test/helper.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'minitest/unit'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+
15
+ require 'gmath3D'
data/test/test_box.rb ADDED
@@ -0,0 +1,90 @@
1
+ require 'box'
2
+
3
+ include GMath3D
4
+
5
+ MiniTest::Unit.autorun
6
+
7
+ class BoxTestCase < MiniTest::Unit::TestCase
8
+ def setup
9
+ @box_default = Box.new()
10
+ @box = Box.new(Vector3.new(-3,2,5), Vector3.new(2,-2.5, 0))
11
+ end
12
+
13
+ def test_initalize
14
+ assert_equal(0, @box_default.min_point.x)
15
+ assert_equal(0, @box_default.min_point.y)
16
+ assert_equal(0, @box_default.min_point.z)
17
+ assert_equal(1, @box_default.max_point.x)
18
+ assert_equal(1, @box_default.max_point.y)
19
+ assert_equal(1, @box_default.max_point.z)
20
+
21
+ assert_equal(-3.0, @box.min_point.x)
22
+ assert_equal(-2.5, @box.min_point.y)
23
+ assert_equal(0.0 , @box.min_point.z)
24
+ assert_equal(2.0 , @box.max_point.x)
25
+ assert_equal(2.0 , @box.max_point.y)
26
+ assert_equal(5.0 , @box.max_point.z)
27
+
28
+ assert_equal(Geom.default_tolerance, @box_default.tolerance)
29
+
30
+ assert_raises ArgumentError do
31
+ invalidResult = Box.new(nil)
32
+ end
33
+ assert_raises ArgumentError do
34
+ invalidResult = Box.new(Vector3.new(), 4.0)
35
+ end
36
+ end
37
+
38
+ def test_equal
39
+ shallow_copied = @box
40
+ assert( shallow_copied == @box )
41
+ assert( shallow_copied.equal?( @box ) )
42
+
43
+ deep_copied = Box.new(Vector3.new(-3,2,5), Vector3.new(2,-2.5, 0))
44
+ assert( deep_copied == @box )
45
+ assert( !deep_copied.equal?( @box ) )
46
+
47
+ assert( @box != 5 )
48
+ end
49
+
50
+ def test_add
51
+ added_box = @box_default + Vector3.new(-2, 0.5, 3)
52
+ assert_equal(Vector3.new(-2,0,0), added_box.min_point)
53
+ assert_equal(Vector3.new(1,1,3) , added_box.max_point)
54
+
55
+ added_box = @box_default + Box.new( Vector3.new(-2,0.5, 3), Vector3.new(4, 0, 2.0))
56
+ assert_equal(Vector3.new(-2,0,0), added_box.min_point)
57
+ assert_equal(Vector3.new(4,1,3) , added_box.max_point)
58
+
59
+ point_ary = [Vector3.new(1,4,9), Vector3.new(4,-2,4), Vector3.new(2,-5,0)]
60
+ added_box = @box + point_ary
61
+ assert_equal(Vector3.new(-3,-5, 0), added_box.min_point)
62
+ assert_equal(Vector3.new( 4, 4, 9), added_box.max_point)
63
+
64
+ assert_raises ArgumentError do
65
+ invalidResult = @box + 4
66
+ end
67
+ end
68
+
69
+ def test_center
70
+ assert_equal( Vector3.new(0.5, 0.5, 0.5), @box_default.center)
71
+ assert_equal( Vector3.new(-0.5,-0.25,2.5), @box.center)
72
+ end
73
+
74
+ def test_length
75
+ width, height, depth = @box_default.length
76
+ assert_in_delta( 1, width , @box_default.tolerance)
77
+ assert_in_delta( 1, height, @box_default.tolerance)
78
+ assert_in_delta( 1, depth , @box_default.tolerance)
79
+
80
+ width, height, depth = @box.length
81
+ assert_in_delta( 5.0, width , @box.tolerance)
82
+ assert_in_delta( 4.5, height, @box.tolerance)
83
+ assert_in_delta( 5.0, depth , @box.tolerance)
84
+ end
85
+
86
+ def test_volume
87
+ assert_in_delta( 1.0, @box_default.volume, @box_default.tolerance )
88
+ assert_in_delta( 112.5, @box.volume, @box.tolerance )
89
+ end
90
+ end