floorplanner-fml 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,38 @@
1
+ module Geom
2
+ class Intersection
3
+ INTERSECTION = 1
4
+ NO_INTERSECTION = 0
5
+ attr_accessor(:alpha,:points,:status)
6
+ def initialize(status=NO_INTERSECTION)
7
+ @alpha = Array.new
8
+ @points = Array.new
9
+ @status = status
10
+ end
11
+
12
+ def self.line_line(a1,a2,b1,b2,infinite=true)
13
+ result = Intersection.new
14
+
15
+ x1, y1 = a1.x, a1.y
16
+ x2, y2 = a2.x, a2.y
17
+ x3, y3 = b1.x, b1.y
18
+ x4, y4 = b2.x, b2.y
19
+
20
+ d = ((y4-y3)*(x2-x1)-(x4-x3)*(y2-y1))
21
+
22
+ unless d.zero?
23
+ # The lines intersect at a point somewhere
24
+ ua = ((x4-x3)*(y1-y3)-(y4-y3)*(x1-x3)) / d
25
+ ub = ((x2-x1)*(y1-y3)-(y2-y1)*(x1-x3)) / d
26
+
27
+ result.alpha.push(ua,ub)
28
+ if infinite || ((ua > 0 && ua < 1) && (ub > 0 && ub < 1))
29
+ x = x1 + ua*(x2-x1)
30
+ y = y1 + ua*(y2-y1)
31
+ result.points << Number3D.new(x,y)
32
+ result.status = INTERSECTION;
33
+ end
34
+ end
35
+ result
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,141 @@
1
+ module Geom
2
+ class Matrix3D < Matrix
3
+ def self.identity
4
+ Matrix3D[
5
+ [1,0,0,0],
6
+ [0,1,0,0],
7
+ [0,0,1,0],
8
+ [0,0,0,1]
9
+ ]
10
+ end
11
+
12
+ def self.rotation_matrix(x,y,z,rad)
13
+ n_cos = Math.cos(rad)
14
+ n_sin = Math.sin(rad)
15
+ s_cos = 1 - n_cos
16
+
17
+ sxy = x * y * s_cos
18
+ syz = y * z * s_cos
19
+ sxz = x * z * s_cos
20
+
21
+ sz = n_sin * z
22
+ sy = n_sin * y
23
+ sx = n_sin * x
24
+
25
+ n11 = n_cos + x * x * s_cos
26
+ n12 = -sz + sxy
27
+ n13 = sy + sxz
28
+ n14 = 0
29
+
30
+ n21 = sz + sxy
31
+ n22 = n_cos + y * y * s_cos
32
+ n23 = -sx + syz
33
+ n24 = 0
34
+
35
+ n31 = -sy + sxz
36
+ n32 = sx + syz
37
+ n33 = n_cos + z * z * s_cos
38
+ n34 = 0
39
+
40
+ self[
41
+ [n11,n12,n13,n14],
42
+ [n21,n22,n23,n24],
43
+ [n31,n32,n33,n34],
44
+ [0,0,0,1]
45
+ ]
46
+ end
47
+
48
+ def self.rotationX(rad)
49
+ c = Math.cos(rad)
50
+ s = Math.sin(rad)
51
+
52
+ self[
53
+ [1,0, 0,0],
54
+ [0,c,-s,0],
55
+ [0,s, c,0],
56
+ [0,0, 0,1],
57
+ ]
58
+ end
59
+
60
+ def self.rotationZ(rad)
61
+ c = Math.cos(rad)
62
+ s = Math.sin(rad)
63
+
64
+ self[
65
+ [c,-s,0,0],
66
+ [s, c,0,0],
67
+ [0, 0,1,0],
68
+ [0, 0,0,1]
69
+ ]
70
+ end
71
+
72
+ def self.translation(x,y,z)
73
+ self[
74
+ [1,0,0,x],
75
+ [0,1,0,y],
76
+ [0,0,1,z],
77
+ [0,0,0,1]
78
+ ]
79
+ end
80
+
81
+ def self.scale(x,y,z)
82
+ self[
83
+ [x,0,0,0],
84
+ [0,y,0,0],
85
+ [0,0,z,0],
86
+ [0,0,0,1]
87
+ ]
88
+ end
89
+
90
+ def self.reflection(plane)
91
+ a = plane.normal.x
92
+ b = plane.normal.y
93
+ c = plane.normal.z
94
+
95
+ self[
96
+ [1-(2*a*a) , 0-(2*a*b) , 0-(2*a*c) , 0],
97
+ [0-(2*a*b) , 1-(2*b*b) , 0-(2*b*c) , 0],
98
+ [0-(2*a*c) , 0-(2*b*c) , 1-(2*c*c) , 0],
99
+ [0 , 0 , 0 , 1]
100
+ ]
101
+ end
102
+
103
+ def self.multiply_vector_3x3(m,v)
104
+ ma = m.to_a
105
+ vx = v.x
106
+ vy = v.y
107
+ vz = v.z
108
+
109
+ v.x = vx * ma[0][0] + vy * ma[0][1] + vz * ma[0][2]
110
+ v.y = vx * ma[1][0] + vy * ma[1][1] + vz * ma[1][2]
111
+ v.z = vx * ma[2][0] + vy * ma[2][1] + vz * ma[2][2]
112
+ end
113
+
114
+ def multiply(other)
115
+ # matrix multiplication is m[r][c] = (row[r]).(col[c])
116
+ s = to_a
117
+ o = other.to_a
118
+ rm00 = s[0][0] * o[0][0] + s[0][1] * o[1][0] + s[0][2] * o[2][0]
119
+ rm01 = s[0][0] * o[0][1] + s[0][1] * o[1][1] + s[0][2] * o[2][1]
120
+ rm02 = s[0][0] * o[0][2] + s[0][1] * o[1][2] + s[0][2] * o[2][2]
121
+ rm03 = s[0][0] * o[0][3] + s[0][1] * o[1][3] + s[0][2] * o[2][3] + s[0][3]
122
+
123
+ rm10 = s[1][0] * o[0][0] + s[1][1] * o[1][0] + s[1][2] * o[2][0]
124
+ rm11 = s[1][0] * o[0][1] + s[1][1] * o[1][1] + s[1][2] * o[2][1]
125
+ rm12 = s[1][0] * o[0][2] + s[1][1] * o[1][2] + s[1][2] * o[2][2]
126
+ rm13 = s[1][0] * o[0][3] + s[1][1] * o[1][3] + s[1][2] * o[2][3] + s[1][3]
127
+
128
+ rm20 = s[2][0] * o[0][0] + s[2][1] * o[1][0] + s[2][2] * o[2][0]
129
+ rm21 = s[2][0] * o[0][1] + s[2][1] * o[1][1] + s[2][2] * o[2][1]
130
+ rm22 = s[2][0] * o[0][2] + s[2][1] * o[1][2] + s[2][2] * o[2][2]
131
+ rm23 = s[2][0] * o[0][3] + s[2][1] * o[1][3] + s[2][2] * o[2][3] + s[2][3]
132
+
133
+ Matrix3D[
134
+ [rm00, rm01, rm02, rm03],
135
+ [rm10, rm11, rm12, rm13],
136
+ [rm20, rm21, rm22, rm23]
137
+ ]
138
+ end
139
+
140
+ end
141
+ end
@@ -0,0 +1,104 @@
1
+ module Geom
2
+
3
+ class Number3D
4
+
5
+ attr_accessor(:x,:y,:z)
6
+
7
+ def initialize(x=0,y=0,z=0)
8
+ @x, @y, @z = x, y, z
9
+ end
10
+
11
+ def self.sub(v,w)
12
+ Number3D.new(
13
+ v.x - w.x,
14
+ v.y - w.y,
15
+ v.z - w.z
16
+ )
17
+ end
18
+
19
+ def self.cross(v,w,target=nil)
20
+ target ||= Number3D.new
21
+ target.reset((w.y * v.z) - (w.z * v.y), (w.z * v.x) - (w.x * v.z), (w.x * v.y) - (w.y * v.x))
22
+ target
23
+ end
24
+
25
+ def self.dot(v,w)
26
+ v.x * w.x + v.y * w.y + w.z * v.z
27
+ end
28
+
29
+ def self.from_str(str)
30
+ x,y,z = str.split(' ') if str
31
+ str.nil? ? Number3D.new : Number3D.new(x.to_f,y.to_f,z.to_f)
32
+ end
33
+
34
+ def reset(nx=nil,ny=nil,nz=nil)
35
+ @x = nx if nx
36
+ @y = ny if ny
37
+ @z = nz if nz
38
+ end
39
+
40
+ def normalize
41
+ mod = Math.sqrt( @x**2 + @y**2 + @z**2 )
42
+ if mod != 0 && mod != 1
43
+ mod = 1 / mod # mults are cheaper then divs
44
+ @x *= mod
45
+ @y *= mod
46
+ @z *= mod
47
+ end
48
+ end
49
+
50
+ def minus_eq(v)
51
+ @x -= v.x
52
+ @y -= v.y
53
+ @z -= v.z
54
+ end
55
+
56
+ def distance_x(other)
57
+ (@x - other.x).abs
58
+ end
59
+
60
+ def distance_y(other)
61
+ (@y - other.y).abs
62
+ end
63
+
64
+ def distance_z(other)
65
+ (@z - other.z).abs
66
+ end
67
+
68
+ def distance(other)
69
+ Math.sqrt(
70
+ distance_z(other) ** 2 +
71
+ Math.sqrt(distance_x(other)**2 + distance_y(other)**2) ** 2
72
+ )
73
+ end
74
+
75
+ def == (other)
76
+ @x == other.x &&
77
+ @y == other.y &&
78
+ @z == other.z
79
+ end
80
+
81
+ def to_s
82
+ "#<Geom::Number3D:#{@x},#{@y},#{@z}>"
83
+ end
84
+
85
+ def to_floats
86
+ [@x,@y,@z].join ' '
87
+ end
88
+ end
89
+
90
+ class NumberUV
91
+
92
+ attr_accessor :u, :v
93
+
94
+ def initialize(u=0,v=0)
95
+ @u = u
96
+ @v = v
97
+ end
98
+
99
+ def clone
100
+ NumberUV.new(u,v)
101
+ end
102
+ end
103
+
104
+ end
data/lib/geom/plane.rb ADDED
@@ -0,0 +1,36 @@
1
+ module Geom
2
+ class Plane
3
+ attr_accessor(:normal)
4
+ def initialize(normal=nil,point=nil)
5
+ if normal && point
6
+ @normal = normal
7
+ @d = -Number3D.dot(normal,point)
8
+ else
9
+ @normal = Number3D.new
10
+ @d = 0
11
+ end
12
+ end
13
+
14
+ def self.three_points(p0,p1,p2)
15
+ plane = Plane.new
16
+ n0 = p0.instance_of?(Number3D) ? p0 : p0.position
17
+ n1 = p1.instance_of?(Number3D) ? p1 : p1.position
18
+ n2 = p2.instance_of?(Number3D) ? p2 : p2.position
19
+ plane.set_three_points(n0,n1,n2)
20
+ plane
21
+ end
22
+
23
+ def set_three_points(p0,p1,p2)
24
+ ab = Number3D.sub(p1,p0)
25
+ ac = Number3D.sub(p2,p0)
26
+ @normal = Number3D.cross(ab,ac)
27
+ @normal.normalize
28
+ @d = -Number3D.dot(@normal,p0)
29
+ end
30
+
31
+ def distance(point)
32
+ p = point.instance_of?(Vertex) ? point.position : point
33
+ Number3D.dot(p,@normal) + @d
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,264 @@
1
+ module Geom
2
+ class Polygon < TriangleMesh
3
+
4
+ WINDING_CW = 0
5
+ WINDING_CCW = 1
6
+
7
+ AXIS_X = 1
8
+ AXIS_Y = 2
9
+ AXIS_Z = 3
10
+
11
+ CAP_TOP = 4
12
+ CAP_BASE = 5
13
+ CAP_BOTH = 6
14
+
15
+ def clone
16
+ vertices = @vertices.collect{|v| v.clone}
17
+ texcoord = @texcoord ? @texcoord.collect{|uv| uv.clone} : nil
18
+ faces = @faces.collect do |f|
19
+ if texcoord
20
+ Triangle.new([
21
+ vertices[ @vertices.index(f.vertices[0]) ],
22
+ vertices[ @vertices.index(f.vertices[1]) ],
23
+ vertices[ @vertices.index(f.vertices[2]) ]
24
+ ],[
25
+ texcoord[ @texcoord.index(f.texcoord[0]) ],
26
+ texcoord[ @texcoord.index(f.texcoord[1]) ],
27
+ texcoord[ @texcoord.index(f.texcoord[2]) ]
28
+ ])
29
+ else
30
+ Triangle.new([
31
+ vertices[ @vertices.index(f.vertices[0]) ],
32
+ vertices[ @vertices.index(f.vertices[1]) ],
33
+ vertices[ @vertices.index(f.vertices[2]) ]
34
+ ])
35
+ end
36
+ end
37
+ result = Polygon.new(vertices,faces,@data.dup)
38
+ result.texcoord = texcoord
39
+ result
40
+ end
41
+
42
+ def update
43
+ return false if @vertices.length < 3
44
+ triangles = @tess.triangulate(self)
45
+ unless triangles
46
+ @vertices.reverse!
47
+ triangles = @tess.triangulate(self)
48
+ return false unless triangles
49
+ end
50
+
51
+ @texcoord = calc_uv
52
+ triangles.each do |t|
53
+ v0 = @vertices[t[0]]
54
+ v1 = @vertices[t[1]]
55
+ v2 = @vertices[t[2]]
56
+
57
+ t0 = @texcoord[t[0]]
58
+ t1 = @texcoord[t[1]]
59
+ t2 = @texcoord[t[2]]
60
+ @faces.push(Triangle.new([v2,v1,v0],[t0,t1,t2]))
61
+ end
62
+ true
63
+ end
64
+
65
+ def area
66
+ # remove duplicates and invisibles
67
+ return nil if @vertices.length < 3
68
+ @vertices.each{|v| return 0 if @vertices.grep(v).length>1}
69
+
70
+ result = 0
71
+ points = @vertices.dup
72
+ plane = self.plane
73
+
74
+ ax = plane.normal.x > 0 ? plane.normal.x : -plane.normal.x
75
+ ay = plane.normal.y > 0 ? plane.normal.y : -plane.normal.y
76
+ az = plane.normal.z > 0 ? plane.normal.z : -plane.normal.z
77
+
78
+ coord = AXIS_Z
79
+
80
+ if ax > ay
81
+ if ax > az
82
+ coord = AXIS_X
83
+ end
84
+ elsif ay > az
85
+ coord = AXIS_Y
86
+ end
87
+
88
+ points.push(points[0],points[1])
89
+
90
+ # compute area of the 2D projection
91
+ points.each_with_index do |point,i|
92
+ next if i.zero?
93
+ j = (i+1) % points.length
94
+ k = (i-1) % points.length
95
+
96
+ case coord
97
+ when AXIS_X
98
+ result += (point.y * (points[j].z - points[k].z))
99
+ when AXIS_Y
100
+ result += (point.x * (points[j].z - points[k].z))
101
+ else
102
+ result += (point.x * (points[j].y - points[k].y))
103
+ end
104
+ end
105
+
106
+ # scale to get area before projection
107
+ an = Math.sqrt(ax**2 + ay**2 + az**2) # length of normal vector
108
+ case coord
109
+ when AXIS_X
110
+ result *= (an / (2*ax))
111
+ when AXIS_Y
112
+ result *= (an / (2*ay))
113
+ when AXIS_Z
114
+ result *= (an / (2*az))
115
+ end
116
+ 2.times {points.pop}
117
+ result
118
+ end
119
+
120
+ # TODO real plane, not just from first 3 vertices
121
+ def plane
122
+ return nil if @vertices.length < 3
123
+ Plane.three_points(*@vertices[0..2])
124
+ end
125
+
126
+ def point_inside(pt)
127
+ x = "x"
128
+ y = "y"
129
+ n = @vertices.length
130
+ dominant = dominant_axis
131
+ dist = self.plane.distance(pt)
132
+ result = false
133
+
134
+ return false if dist.abs > 0.01
135
+ case dominant
136
+ when AXIS_X
137
+ x = "y"
138
+ y = "z"
139
+ when AXIS_Y
140
+ y = "z"
141
+ end
142
+
143
+ @vertices.each_with_index do |v,i|
144
+ vn = @vertices[(i+1)%n] # next
145
+ if (((v.send(y) <= pt.send(y)) && (pt.send(y) < vn.send(y))) || ((vn.send(y) <= pt.send(y)) && (pt.send(y) < v.send(y)))) &&
146
+ (pt.send(x) < (vn.send(x) - v.send(x)) * (pt.send(y) - v.send(y)) / (vn.send(y) - v.send(y)) + v.send(x))
147
+ result = !result
148
+ end
149
+ end
150
+ result
151
+ end
152
+
153
+ def winding
154
+ area < 0 ? WINDING_CW : WINDING_CCW
155
+ end
156
+
157
+ def extrude(distance,direction,cap=CAP_BOTH,update=true)
158
+ direction.normalize
159
+ top_cap = clone
160
+ top_cap.vertices.each do |v|
161
+ v.x += distance*direction.x
162
+ v.y += distance*direction.y
163
+ v.z += distance*direction.z
164
+ end
165
+ top_cap.faces.each {|f| f.normal = direction }
166
+ num = @vertices.length
167
+
168
+ sides = Array.new(@vertices.length).map!{ Polygon.new }
169
+ sides.each_with_index do |side,i|
170
+ j = (i+1) % num
171
+ side.vertices.push(top_cap.vertices[i],top_cap.vertices[j])
172
+ side.vertices.push(@vertices[j],@vertices[i])
173
+ side.data[:side] = true
174
+ side.update if update
175
+ end
176
+
177
+ case cap
178
+ when CAP_BASE
179
+ top_cap.faces.clear
180
+ when CAP_TOP
181
+ self.faces.clear
182
+ when CAP_BOTH
183
+ self.faces.each do |f|
184
+ f.flip_normal
185
+ end
186
+ end
187
+
188
+ sides + [top_cap]
189
+ end
190
+
191
+ def dominant_axis
192
+ plane = self.plane
193
+ return 0 unless plane
194
+
195
+ ax = (plane.normal.x > 0 ? plane.normal.x : -plane.normal.x)
196
+ ay = (plane.normal.y > 0 ? plane.normal.y : -plane.normal.y)
197
+ az = (plane.normal.z > 0 ? plane.normal.z : -plane.normal.z)
198
+
199
+ axis = AXIS_Z
200
+ if ax > ay
201
+ if ax > az
202
+ axis = AXIS_X
203
+ end
204
+ elsif ay > az
205
+ axis = AXIS_Y
206
+ end
207
+ axis
208
+ end
209
+
210
+ def calc_uv
211
+ result = []
212
+ plane = self.plane
213
+ up = Number3D.new( 0, 1, 0 )
214
+
215
+ # get side vector
216
+ side = Number3D.cross(up, plane.normal)
217
+ side.normalize
218
+
219
+ # adjust up vector
220
+ up = Number3D.cross(self.plane.normal, side)
221
+ up.normalize
222
+
223
+ matrix = Matrix3D[
224
+ [side.x, up.x, plane.normal.x, 0],
225
+ [side.y, up.y, plane.normal.y, 0],
226
+ [side.z, up.z, plane.normal.z, 0],
227
+ [0, 0, 0, 1]]
228
+
229
+ v, n, t = nil, nil, nil
230
+ min = Number3D.new(1000,1000,1000)
231
+ max = Number3D.new(-min.x, -min.y, -min.z)
232
+ pts = []
233
+
234
+ @vertices.each do |v|
235
+ n = v.position
236
+
237
+ # Matrix3D.multiplyVector3x3( matrix, n );
238
+
239
+ min.x = n.x if n.x < min.x
240
+ min.y = n.y if n.y < min.y
241
+ max.x = n.x if n.x > max.x
242
+ max.y = n.y if n.y > max.y
243
+
244
+ pts << n
245
+ result << NumberUV.new
246
+ end
247
+
248
+ w = max.x - min.x
249
+ h = max.y - min.y
250
+ size = w < h ? h : w
251
+
252
+ @vertices.each_with_index do |v,i|
253
+ n = pts[i]
254
+ t = result[i]
255
+
256
+ t.u = ((n.x - min.x) / size) * size
257
+ t.v = ((n.y - min.y) / size) * size
258
+ end
259
+
260
+ result
261
+ end
262
+
263
+ end
264
+ end