fml 0.2.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.
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