floorplanner-fml 0.2

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