fml 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.gitignore +8 -0
  2. data/README +1 -0
  3. data/Rakefile +3 -0
  4. data/bin/fml2dae.rb +15 -0
  5. data/bin/fml2obj.rb +10 -0
  6. data/fml.gemspec +36 -0
  7. data/lib/collada/document.rb +101 -0
  8. data/lib/collada/geometry.rb +110 -0
  9. data/lib/config.yml +17 -0
  10. data/lib/floorplanner/area_builder.rb +42 -0
  11. data/lib/floorplanner/asset.rb +185 -0
  12. data/lib/floorplanner/collada_export.rb +118 -0
  13. data/lib/floorplanner/design.rb +108 -0
  14. data/lib/floorplanner/document.rb +68 -0
  15. data/lib/floorplanner/obj_export.rb +24 -0
  16. data/lib/floorplanner/opening3d.rb +140 -0
  17. data/lib/floorplanner/rib_export.rb +24 -0
  18. data/lib/floorplanner/svg_export.rb +25 -0
  19. data/lib/floorplanner/wall3d.rb +97 -0
  20. data/lib/floorplanner/wall_builder.rb +165 -0
  21. data/lib/floorplanner.rb +52 -0
  22. data/lib/geom/connection.rb +14 -0
  23. data/lib/geom/ear_trim.rb +52 -0
  24. data/lib/geom/edge.rb +89 -0
  25. data/lib/geom/glu_tess.rb +34 -0
  26. data/lib/geom/intersection.rb +38 -0
  27. data/lib/geom/matrix3d.rb +141 -0
  28. data/lib/geom/number.rb +104 -0
  29. data/lib/geom/plane.rb +36 -0
  30. data/lib/geom/polygon.rb +264 -0
  31. data/lib/geom/triangle.rb +38 -0
  32. data/lib/geom/triangle_mesh.rb +94 -0
  33. data/lib/geom/vertex.rb +33 -0
  34. data/lib/geom.rb +13 -0
  35. data/lib/keyhole/archive.rb +36 -0
  36. data/tasks/github-gem.rake +315 -0
  37. data/views/design.dae.erb +439 -0
  38. data/views/design.obj.erb +17 -0
  39. data/views/design.rib.erb +17 -0
  40. data/views/design.svg.erb +42 -0
  41. data/xml/collada_schema_1_4.xsd +11046 -0
  42. data/xml/fml.rng +268 -0
  43. data/xml/fml2kml.xsl +59 -0
  44. metadata +117 -0
@@ -0,0 +1,14 @@
1
+ module Geom
2
+ class Connection
3
+ alias_method(:==, :equal?)
4
+ attr_accessor(:point,:angle)
5
+ def initialize(point,angle)
6
+ @point = point
7
+ @angle = angle
8
+ end
9
+
10
+ def equal?(other)
11
+ other.point == @point && other.angle == @angle
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,52 @@
1
+ #
2
+ # Implements "Ear trimming" triangulation algorithm
3
+ #
4
+ module Geom
5
+ class EarTrim
6
+ def self.triangulate(poly)
7
+ result = Array.new
8
+ points = poly.vertices.dup
9
+ num = points.length
10
+ count = num*2
11
+ indices = Hash.new
12
+
13
+ return [ [0,1,2] ] if num == 3
14
+ points.reverse! if poly.winding == Polygon::WINDING_CW
15
+ points.each_with_index do |p,i|
16
+ indices[p] = i
17
+ end
18
+
19
+ while num > 2
20
+ return nil if count > num*2 # overflow
21
+ count += 1
22
+
23
+ i = 0
24
+ while i < num
25
+ j = (i+num-1) % num
26
+ k = (i+1) % num
27
+
28
+ if is_ear(points,j,i,k)
29
+ # save triangle
30
+ result.push([indices[points[j]],indices[points[i]],indices[points[k]]])
31
+ # remove vertex
32
+ points.delete_at(i)
33
+ num = points.length
34
+ count = 0
35
+ end
36
+ i += 1
37
+ end
38
+ end
39
+ result
40
+ end
41
+
42
+ def self.is_ear(points,u,v,w)
43
+ poly = Polygon.new([points[u],points[v],points[w]])
44
+ return false if poly.area < 0
45
+ points.length.times do |i|
46
+ next if i == u || i == v || i == w
47
+ return false if poly.point_inside(points[i])
48
+ end
49
+ true
50
+ end
51
+ end
52
+ end
data/lib/geom/edge.rb ADDED
@@ -0,0 +1,89 @@
1
+ module Geom
2
+ class Edge
3
+ attr_accessor(:start_point,:end_point)
4
+ def initialize(sp,ep)
5
+ @start_point = sp
6
+ @end_point = ep
7
+ end
8
+
9
+ def direction
10
+ Number3D.sub(@start_point.position,@end_point.position)
11
+ end
12
+
13
+ def length
14
+ x = @end_point.x - @start_point.x;
15
+ y = @end_point.y - @start_point.y;
16
+ z = @end_point.z - @start_point.z;
17
+ Math.sqrt(x*x + y*y + z*z);
18
+ end
19
+
20
+ def offset(distance,up)
21
+ up.normalize
22
+ edge = clone
23
+ dir = direction
24
+
25
+ Matrix3D.multiply_vector_3x3(Matrix3D.rotation_matrix(up.x,up.y,up.z, -Math::PI/2),dir)
26
+ dir.normalize
27
+
28
+ dir.x *= distance
29
+ dir.y *= distance
30
+ dir.z *= distance
31
+
32
+ edge.start_point.x += dir.x
33
+ edge.start_point.y += dir.y
34
+ edge.start_point.z += dir.z
35
+ edge.end_point.x += dir.x
36
+ edge.end_point.y += dir.y
37
+ edge.end_point.z += dir.z
38
+
39
+ edge
40
+ end
41
+
42
+ def extrude(distance,direction)
43
+ edge = clone
44
+ [edge.start_point,edge.end_point].each do |v|
45
+ v.x += distance*direction.x
46
+ v.y += distance*direction.y
47
+ v.z += distance*direction.z
48
+ end
49
+
50
+ poly = Polygon.new
51
+ poly.vertices.push(
52
+ edge.end_point , edge.start_point,
53
+ @start_point , @end_point)
54
+ poly
55
+ end
56
+
57
+ def snap(point)
58
+ x1 = @start_point.x
59
+ y1 = @start_point.y
60
+ z1 = @start_point.z
61
+ x2 = @end_point.x
62
+ y2 = @end_point.y
63
+ z2 = @end_point.z
64
+ x3 = point.x
65
+ y3 = point.y
66
+ z3 = point.z
67
+ dx = x2-x1
68
+ dy = y2-y1
69
+ dz = z2-z1
70
+ if dx == 0 && dy == 0 && dz == 0
71
+ return @start_point
72
+ else
73
+ t = ((x3 - x1) * dx + (y3 - y1) * dy + (z3 - z1) * dz) / (dx**2 + dy**2 + dz**2)
74
+ x0 = x1 + t * dx
75
+ y0 = y1 + t * dy
76
+ z0 = z1 + t * dz
77
+ return Vertex.new(x0,y0,z0)
78
+ end
79
+ end
80
+
81
+ def clone
82
+ Edge.new(@start_point.clone,@end_point.clone)
83
+ end
84
+
85
+ def to_s
86
+ "#<Geom::Edge:#{@start_point.to_s},#{@end_point.to_s}>"
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,34 @@
1
+ require 'opengl'
2
+
3
+ module Geom
4
+ class GluTesselator
5
+ include Gl,Glu,Glut
6
+ def self.triangulate(poly)
7
+ result = Array.new
8
+
9
+ t = gluNewTess
10
+ gluTessCallback(t,GLU_TESS_VERTEX,method(:vertex_callback).to_proc)
11
+ gluTessCallback(t,GLU_TESS_BEGIN,method(:begin_callback).to_proc)
12
+ gluTessCallback(t,GLU_TESS_EDGE_FLAG,proc{})
13
+ gluTessBeginPolygon(t,self)
14
+ gluTessBeginContour(t)
15
+
16
+ poly.vertices.each_with_index do |v,i|
17
+ gluTessVertex(t,[v.x,v.y,v.z],v)
18
+ end
19
+
20
+ gluTessEndContour(t)
21
+ gluTessEndPolygon(t)
22
+ end
23
+
24
+ private
25
+
26
+ def self.vertex_callback(v)
27
+ puts v
28
+ end
29
+
30
+ def self.begin_callback(a)
31
+ puts "begin"
32
+ end
33
+ end
34
+ end
@@ -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