gmath3D 0.1.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/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