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,108 @@
1
+ module Floorplanner
2
+ class Design
3
+ DESIGN_QUERY = "/design"
4
+ ASSET_QUERY = DESIGN_QUERY+"/assets/asset[@id='%s']"
5
+ ASSET_URL2D = ASSET_QUERY+"/url2d"
6
+ LINES_QUERY = DESIGN_QUERY+"/lines/line"
7
+ OPENINGS_QUERY = DESIGN_QUERY+"/objects/object[type='opening']"
8
+ AREAS_QUERY = DESIGN_QUERY+"/areas/area"
9
+ NAME_QUERY = DESIGN_QUERY+"/name"
10
+
11
+ include ColladaExport
12
+ include ObjExport
13
+ include RibExport
14
+ include SvgExport
15
+
16
+ ##
17
+ # Constructs new floorplan design from FML
18
+ ##
19
+ def initialize(fml)
20
+ begin
21
+ @xml = fml
22
+ @name = @xml.find(NAME_QUERY).first.content
23
+ @author = "John Doe" # TODO from <author> element if included in FML
24
+ rescue NoMethodError
25
+ Log.error "Can't load FML"
26
+ end
27
+ end
28
+
29
+ ##
30
+ # Builds geometries of walls and areas.
31
+ ##
32
+ def build_geometries
33
+ @areas = AreaBuilder.new do |b|
34
+ @xml.find(AREAS_QUERY).each do |area|
35
+ name = area.find('name').first
36
+ name = name.content if name
37
+ color = area.find('color').first
38
+ color = color.content if color
39
+ type = area.find('type').first.content
40
+
41
+ asset_id = area.find('asset').first
42
+ asset_id = asset_id.attributes['refid'] if asset_id
43
+ if asset_id
44
+ texture_url = @xml.find(ASSET_URL2D % [asset_id]).first.content
45
+ end
46
+
47
+ vertices = Array.new
48
+ area.find('points').first.content.split(',').each do |str_v|
49
+ floats = str_v.strip.split(/\s/).map! {|f| f.to_f}
50
+
51
+ # TODO: fix y coords in Flash app
52
+ floats[1] *= -1.0; floats[4] *= -1.0
53
+
54
+ vertices << b.vertex(Geom::Vertex.new(*floats[0..2]))
55
+ vertices << b.vertex(Geom::Vertex.new(*floats[3..5]))
56
+ end
57
+
58
+ b.area(vertices,
59
+ :color => color,
60
+ :name => name,
61
+ :texture => texture_url,
62
+ :type => type)
63
+
64
+ end
65
+ end
66
+ min_height = 10
67
+ @walls = WallBuilder.new do |b|
68
+ @xml.find(LINES_QUERY).each do |line|
69
+ floats = line.find('points').first.get_floats
70
+
71
+ thickness = line.find('thickness').first
72
+ thickness = thickness ? thickness.content.to_f : Floorplanner.config['wall_thickness']
73
+ height = line.find('height').first
74
+ height = height ? height.content.to_f : Floorplanner.config['wall_height']
75
+
76
+ # TODO: fix this in Flash app
77
+ floats[1] *= -1.0; floats[4] *= -1.0
78
+
79
+ sp = Geom::Vertex.new(*floats[0..2])
80
+ ep = Geom::Vertex.new(*floats[3..5])
81
+ sp = b.vertex(sp)
82
+ ep = b.vertex(ep)
83
+ b.wall(sp,ep,thickness,height)
84
+ min_height = height if height < min_height
85
+ end
86
+ end
87
+ @areas.update min_height
88
+
89
+ @walls.prepare
90
+ @xml.find(OPENINGS_QUERY).each do |opening|
91
+ pos_floats = opening.find('points').first.get_floats
92
+
93
+ # TODO: fix y coord in Flash app
94
+ pos_floats[1] *= -1
95
+
96
+ size_floats = opening.find('size').first.get_floats
97
+ position = Geom::Number3D.new(*pos_floats)
98
+ size = Geom::Number3D.new(*size_floats)
99
+
100
+ asset_id = opening.find('asset').first.attributes['refid']
101
+ asset = @xml.find(ASSET_QUERY % [asset_id]).first
102
+ type = asset.find('url2d').first.content.match(/door/i) ? Opening3D::TYPE_DOOR : Opening3D::TYPE_WINDOW
103
+ @walls.opening(position,size,type)
104
+ end
105
+ @walls.update
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,68 @@
1
+ module Floorplanner
2
+
3
+ class Document
4
+
5
+ POINTS_QUERY = "/project/floors/floor/designs/design/area/line/points"
6
+ LINE_POINTS_REGEXP = /^((\s*[-+]?[0-9]*\.?[0-9]+\s+){5,8}\s*[-+]?[0-9]*\.?[0-9]+\s*?(?:,)?)*$/
7
+
8
+ def initialize(fml_fn)
9
+ @xml = XML::Document.file(fml_fn)
10
+ end
11
+
12
+ def self.validate(doc)
13
+ schema = XML::Schema.document(
14
+ XML::Document.file(File.join(File.dirname(__FILE__), "..", "..", "xml", "fml-permissive.xsd"))
15
+ )
16
+ doc = XML::Document.file(doc) if doc.instance_of?(String)
17
+ doc.validate_schema(schema) do |message,error|
18
+ # TODO throw an exception
19
+ puts message if error
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def self.validate_line_points(doc)
26
+ doc.find(POINTS_QUERY).each do |points_node|
27
+ unless LINE_POINTS_REGEXP =~ points_node.children.to_s
28
+ # TODO throw an exception
29
+ puts "Elements points inside area's line failed to validate content."
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ class DesignDocument
36
+
37
+ def initialize(fml_fn)
38
+ @xml = LibXML::XML::Document.file(fml_fn)
39
+ end
40
+
41
+ def update_heights(new_height)
42
+ lines = @xml.find("/design/lines/line[type='default_wall' or type='normal_wall' or contains(type,'hedge') or contains(type,'fence')]")
43
+ lines.each do |line|
44
+ begin
45
+ points = line.find("points").first
46
+ next unless points.content.include? ","
47
+
48
+ coords = points.content.strip.split(",")
49
+ top_coords = coords[1].strip.split(/\s/).map{|c| c.to_f}
50
+
51
+ top_coords[2] = new_height
52
+ top_coords[5] = new_height
53
+ if top_coords.length > 6
54
+ top_coords[8] = new_height
55
+ end
56
+
57
+ coords[1] = top_coords.join(" ")
58
+ points.content = coords.join(",")
59
+ rescue; end
60
+ end
61
+ end
62
+
63
+ def save(fn)
64
+ @xml.save fn
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,24 @@
1
+ module Floorplanner
2
+ class Document
3
+
4
+ def to_obj(design_id,out_path)
5
+ @design = Design.new(@xml,design_id)
6
+ @design.build_geometries
7
+ obj = File.new(out_path,'w')
8
+ obj.write @design.to_obj
9
+ obj.close
10
+ end
11
+
12
+ end
13
+
14
+ module ObjExport
15
+ def to_obj
16
+ raise "No geometries to export. Call build_geometries first" unless @areas && @walls
17
+
18
+ template = ERB.new(
19
+ File.read(
20
+ File.join(Floorplanner.config['views_path'],'design.obj.erb')))
21
+ template.result(binding)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,140 @@
1
+ module Floorplanner
2
+ class Opening3D < Geom::TriangleMesh
3
+
4
+ TYPE_DOOR = 1
5
+ TYPE_WINDOW = 2
6
+
7
+ attr_accessor(:position,:window)
8
+
9
+ def initialize(baseline,thickness,opening)
10
+ super()
11
+ @position = baseline.snap(opening[:position])
12
+ @type = opening[:type]
13
+ dir = baseline.direction
14
+ angle = Math.atan2(dir.y,dir.x)
15
+ width = opening[:size].x
16
+ height = 0
17
+
18
+ case @type
19
+ when TYPE_DOOR
20
+ @position.z = 0.01
21
+ height = Floorplanner.config['openings']['door_height']
22
+ else
23
+ @position.z = Floorplanner.config['openings']['window_base']
24
+ height = Floorplanner.config['openings']['window_height']
25
+ end
26
+
27
+ v1 = Geom::Vertex.new(-width/2,0,0)
28
+ v2 = Geom::Vertex.new( width/2,0,0)
29
+ o_base = Geom::Edge.new(v1,v2)
30
+
31
+ # create opening side
32
+ o_inner = o_base.offset(thickness/2.0,Wall3D::UP)
33
+ o_outer = o_base.offset(-thickness/2.0,Wall3D::UP)
34
+
35
+ @base = Geom::Polygon.new([
36
+ o_inner.end_point, o_inner.start_point,
37
+ o_outer.start_point , o_outer.end_point
38
+ ])
39
+
40
+ # rotate in wall's direction
41
+ @base.transform_vertices(Geom::Matrix3D.rotationZ(angle))
42
+ # move to position
43
+ @base.transform_vertices(Geom::Matrix3D.translation(@position.x,@position.y,@position.z))
44
+
45
+ extrusion = @base.extrude(height,Wall3D::UP,nil,false)
46
+
47
+ # delete sides
48
+ extrusion.delete_at(0)
49
+ extrusion.delete_at(1)
50
+
51
+ # flip top cap
52
+ extrusion.last.reverse
53
+
54
+ @meshes << @base
55
+ @meshes.concat(extrusion)
56
+
57
+ # create glass
58
+ if @type == TYPE_WINDOW
59
+ g_inner = o_base.offset( 0.02, Wall3D::UP)
60
+ g_outer = o_base.offset(-0.02, Wall3D::UP)
61
+
62
+ glass_base = Geom::Polygon.new([
63
+ g_inner.end_point, g_inner.start_point,
64
+ g_outer.start_point , g_outer.end_point
65
+ ])
66
+
67
+ # rotate in wall's direction
68
+ glass_base.transform_vertices(Geom::Matrix3D.rotationZ(angle))
69
+ # move to position
70
+ glass_base.transform_vertices(Geom::Matrix3D.translation(@position.x,@position.y,@position.z))
71
+
72
+ extrusion = glass_base.extrude(height,Wall3D::UP,nil,false)
73
+
74
+ # flip base cap
75
+ glass_base.reverse
76
+
77
+ @window = Geom::TriangleMesh.new
78
+ @window.meshes.concat extrusion
79
+ @window << glass_base
80
+ @window.update
81
+ end
82
+ end
83
+
84
+ # drill hole to sides
85
+ def drill(mesh,outer)
86
+ side = outer ? mesh.meshes.first : mesh.meshes.last
87
+
88
+ # opening start
89
+ t1 = @meshes.first.vertices[outer ? 0 : 3].clone
90
+ t1.z = side.vertices[0].z
91
+ t1b = @meshes[3].vertices[outer ? 0 : 3]
92
+
93
+ b1 = @meshes.first.vertices[outer ? 0 : 3].clone
94
+ b1.z = side.vertices[2].z
95
+ b1t = @meshes.first.vertices[outer ? 0 : 3]
96
+
97
+ # opening end
98
+ t2 = @meshes.first.vertices[outer ? 1 : 2].clone
99
+ t2.z = side.vertices[0].z
100
+ t2b = @meshes[3].vertices[outer ? 1 : 2]
101
+
102
+ b2 = @meshes.first.vertices[outer ? 1 : 2].clone
103
+ b2.z = side.vertices[2].z
104
+ b2t = @meshes.first.vertices[outer ? 1 : 2]
105
+
106
+ # old side vertices
107
+ ot = side.vertices[1]
108
+ ob = side.vertices[2]
109
+ side.vertices[1] = outer ? t2 : t1
110
+ side.vertices[2] = outer ? b2 : b1
111
+
112
+ # polygon above opening
113
+ op_top = Geom::Polygon.new
114
+ if outer
115
+ op_top.vertices.push(t2,t1,t1b,t2b)
116
+ else
117
+ op_top.vertices.push(t1,t2,t2b,t1b)
118
+ end
119
+
120
+ # polygon below opening
121
+ op_bot = Geom::Polygon.new
122
+ if outer
123
+ op_bot.vertices.push(b2t,b1t,b1,b2)
124
+ else
125
+ op_bot.vertices.push(b1t,b2t,b2,b1)
126
+ end
127
+
128
+ rest = Geom::Polygon.new
129
+ if outer
130
+ rest.vertices.push(t1,ot,ob,b1)
131
+ else
132
+ rest.vertices.push(t2,ot,ob,b2)
133
+ end
134
+
135
+ mesh.meshes.push(op_top)
136
+ mesh.meshes.push(op_bot)
137
+ mesh.meshes.push(rest)
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,24 @@
1
+ module Floorplanner
2
+ class Document
3
+
4
+ def to_rib(design_id,out_path)
5
+ @design = Design.new(@xml,design_id)
6
+ @design.build_geometries
7
+ rib = File.new(out_path,'w')
8
+ rib.write @design.to_rib
9
+ rib.close
10
+ end
11
+
12
+ end
13
+
14
+ module RibExport
15
+ def to_rib
16
+ raise "No geometries to export. Call build_geometries first" unless @areas && @walls
17
+
18
+ template = ERB.new(
19
+ File.read(
20
+ File.join(Floorplanner.config['views_path'],'design.rib.erb')))
21
+ template.result(binding)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ module Floorplanner
2
+ module SvgExport
3
+ def to_svg
4
+ # translate to x:0,y:0
5
+ bbox = @walls.bounding_box
6
+ dx = bbox[:min].distance_x(bbox[:max])
7
+ dy = bbox[:min].distance_y(bbox[:max])
8
+ min_x = -bbox[:min].x
9
+ min_y = -bbox[:min].y
10
+ # fit into document dimensions
11
+ width , height , padding = Floorplanner.config['svg']['width'],
12
+ Floorplanner.config['svg']['height'],
13
+ Floorplanner.config['svg']['padding']
14
+ ratio = ( width < height ? width : height ) * padding / ( dx > dy ? dx : dy )
15
+ # center on stage
16
+ mod_x = min_x + (width /ratio)/2 - dx/2
17
+ mod_y = min_y + (height/ratio)/2 - dy/2
18
+
19
+ template = ERB.new(
20
+ File.read(
21
+ File.join(Floorplanner.config['views_path'],'design.svg.erb')))
22
+ template.result(binding)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,97 @@
1
+ module Floorplanner
2
+ class Wall3D < Geom::TriangleMesh
3
+ UP = Geom::Number3D.new(0,0,1)
4
+ attr_accessor(:baseline,:outline,:inner,:outer,:name)
5
+ def initialize(baseline,thickness,height,name)
6
+ super()
7
+ @baseline = baseline
8
+ @thickness = thickness
9
+ @height = height
10
+ @name = name
11
+
12
+ # create inner and outer Edges of wall
13
+ @inner = @baseline.offset(@thickness/2.0,UP)
14
+ @outer = @baseline.offset(-@thickness/2.0,UP)
15
+ @openings = Array.new
16
+ end
17
+
18
+ def opening(position,size,type)
19
+ @openings << {:position => position, :size => size, :type => type}
20
+ end
21
+
22
+ def windows
23
+ @openings.collect{|o| o.window}.compact
24
+ end
25
+
26
+ # create base 'outline' polygon of wall
27
+ def prepare(num_start_connections,num_end_connections)
28
+ @outline = Geom::Polygon.new
29
+ if num_start_connections == 1 || num_start_connections == 2
30
+ @outline.vertices.push(
31
+ @outer.start_point,
32
+ @inner.start_point)
33
+ else
34
+ @outline.vertices.push(
35
+ @outer.start_point,
36
+ @baseline.start_point,
37
+ @inner.start_point)
38
+ end
39
+
40
+ if num_end_connections == 1 || num_end_connections == 2
41
+ @outline.vertices.push(
42
+ @inner.end_point,
43
+ @outer.end_point)
44
+ else
45
+ @outline.vertices.push(
46
+ @inner.end_point,
47
+ @baseline.end_point,
48
+ @outer.end_point)
49
+ end
50
+ @outline.vertices.reverse!
51
+ @outline.data[:color] = "#ff9999"
52
+ end
53
+
54
+ def update
55
+ @openings.each_with_index do |opening,i|
56
+ op = Opening3D.new(@baseline,@thickness,opening)
57
+ op.update
58
+ @meshes << op
59
+ @openings[i] = op
60
+ end
61
+ @openings = @openings.sort_by{|o| o.position.distance(@baseline.start_point.position)}
62
+ @outline.update
63
+ @meshes << @outline
64
+
65
+ # create top cap for wall
66
+ top_cap = @outline.clone
67
+ top_cap.transform_vertices(Geom::Matrix3D.translation(0,0,@height))
68
+ @meshes << top_cap
69
+ # flip bottom cap
70
+ @outline.reverse
71
+
72
+ # create walls side polygons
73
+ num = @outline.vertices.length
74
+ starts = [@outer.start_point,@inner.start_point,@baseline.start_point]
75
+ ends = [@outer.end_point, @inner.end_point, @baseline.end_point]
76
+ outs = [@outer.start_point,@outer.end_point]
77
+ @outline.vertices.each_with_index do |v,i|
78
+ j = @outline.vertices[(i+1) % num]
79
+ # omit starting and ending polygons
80
+ next if ( starts.include?(v) && starts.include?(j) ) ||
81
+ ( ends.include?(v) && ends.include?(j) )
82
+
83
+ edge = Geom::Edge.new(v,j)
84
+ poly = edge.extrude(@height,UP)
85
+ mesh = Geom::TriangleMesh.new
86
+ mesh << poly
87
+
88
+ # drill holes to side
89
+ @openings.each do |opening|
90
+ opening.drill(mesh,outs.include?(v))
91
+ end
92
+ mesh.update
93
+ @meshes << mesh
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,165 @@
1
+ module Floorplanner
2
+ class WallBuilder < Geom::TriangleMesh
3
+
4
+ def initialize(&block)
5
+ super()
6
+ @connections = Hash.new
7
+ @base_vertices = Array.new
8
+ @walls = Array.new
9
+ block.call(self)
10
+ end
11
+
12
+ def each(&block)
13
+ @walls.each{|w| block.call(w)}
14
+ end
15
+
16
+ def collect(&block)
17
+ @walls.collect{|w| block.call(w)}
18
+ end
19
+
20
+ def vertex(vertex)
21
+ if existing = find_vertex(@base_vertices,vertex,Floorplanner.config['geom_snap'])
22
+ existing
23
+ else
24
+ @base_vertices << vertex
25
+ vertex
26
+ end
27
+ end
28
+
29
+ def wall(sp,ep,thickness,height)
30
+ @connections[sp] = Array.new unless @connections.include?(sp)
31
+ @connections[ep] = Array.new unless @connections.include?(ep)
32
+ cs = Geom::Connection.new(ep, 0.0)
33
+ ce = Geom::Connection.new(sp, 0.0)
34
+ @connections[sp] << cs unless @connections[sp].include?(cs)
35
+ @connections[ep] << ce unless @connections[ep].include?(ce)
36
+ @walls << Wall3D.new(Geom::Edge.new(sp,ep), thickness, height, "wall_#{@walls.length}")
37
+ end
38
+
39
+ # call after adding walls
40
+ def opening(position,size,type)
41
+ @walls.each do |wall|
42
+ if wall.outline.point_inside(position)
43
+ wall.opening(position,size,type)
44
+ end
45
+ end
46
+ end
47
+
48
+ def prepare
49
+ @base_vertices.each do |v|
50
+ connections = @connections[v]
51
+ next if connections.length.zero?
52
+
53
+ connections.each do |c|
54
+ x = c.point.x - v.x
55
+ y = c.point.y - v.y
56
+ c.angle = Math.atan2(y,x)
57
+ end
58
+ connections.sort! {|a,b| a.angle <=> b.angle}
59
+ connections.each_index do |i|
60
+ j = (i+1) % connections.length
61
+
62
+ w0 , w1 = find_wall(v,connections[i].point),
63
+ find_wall(v,connections[j].point)
64
+
65
+ flipped0 , flipped1 = (w0.baseline.end_point == v),
66
+ (w1.baseline.end_point == v)
67
+
68
+ e0 , e1 = flipped0 ? w0.outer : w0.inner,
69
+ flipped1 ? w1.inner : w1.outer
70
+
71
+ isect = Geom::Intersection.line_line(e0.start_point.position,e0.end_point.position,e1.start_point.position,e1.end_point.position,true)
72
+
73
+ if isect.status == Geom::Intersection::INTERSECTION
74
+ # the two edges intersect!
75
+ # adjust the edges so they touch at the intersection.
76
+ if isect.alpha[0].abs < 2
77
+ if flipped0
78
+ e0.end_point.x = isect.points[0].x
79
+ e0.end_point.y = isect.points[0].y
80
+ else
81
+ e0.start_point.x = isect.points[0].x
82
+ e0.start_point.y = isect.points[0].y
83
+ end
84
+
85
+ if flipped1
86
+ e1.end_point.x = isect.points[0].x
87
+ e1.end_point.y = isect.points[0].y
88
+ else
89
+ e1.start_point.x = isect.points[0].x
90
+ e1.start_point.y = isect.points[0].y
91
+ end
92
+ else
93
+ # parallel
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ @walls.each do |wall|
100
+ num_start = @connections[wall.baseline.start_point].length
101
+ num_end = @connections[wall.baseline.end_point].length
102
+ wall.prepare(num_start,num_end)
103
+ end
104
+ end
105
+
106
+ def update
107
+ @walls.each do |wall|
108
+ # here comes the cache
109
+ wall.update
110
+ @vertices.concat(wall.vertices)
111
+ @faces.concat(wall.faces)
112
+ end
113
+
114
+ Log.info "Walls Vertices before: #{@vertices.length.to_s}"
115
+ # remove same instances
116
+ @vertices.uniq!
117
+ # remove same vertices
118
+ old = @vertices.dup
119
+ @vertices = Array.new
120
+ old.each do |v|
121
+ @vertices.push(v) unless @vertices.include?(v) # find_vertex(@vertices,v) #
122
+ end
123
+ Log.info "Walls Vertices: #{@vertices.length.to_s}"
124
+ Log.info "Walls Faces : #{@faces.length.to_s}"
125
+ end
126
+
127
+ # make use of cache
128
+ def vertices
129
+ @vertices
130
+ end
131
+
132
+ def faces
133
+ @faces
134
+ end
135
+
136
+ def windows
137
+ @walls.collect{|w| w.windows}.flatten
138
+ end
139
+
140
+ private
141
+
142
+ def find_wall(sp,ep)
143
+ @walls.each do |wall|
144
+ if wall.baseline.start_point.equal?(sp,Floorplanner.config['geom_snap']) &&
145
+ wall.baseline.end_point.equal?(ep,Floorplanner.config['geom_snap'])
146
+ return wall
147
+ elsif wall.baseline.end_point.equal?(sp,Floorplanner.config['geom_snap']) &&
148
+ wall.baseline.start_point.equal?(ep,Floorplanner.config['geom_snap'])
149
+ return wall
150
+ end
151
+ end
152
+ nil
153
+ end
154
+
155
+ def find_vertex(arr,v,snap=Floorplanner.config['uniq_snap'])
156
+ arr.each do |vertex|
157
+ if v.equal?(vertex,snap)
158
+ return vertex
159
+ end
160
+ end
161
+ nil
162
+ end
163
+
164
+ end
165
+ end
@@ -0,0 +1,52 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+ require 'find'
4
+ require 'open-uri'
5
+ require 'net/http'
6
+ require 'fileutils'
7
+ require 'logger'
8
+
9
+ require 'rubygems'
10
+ require 'zip/zip'
11
+ require 'xml'
12
+
13
+ $LOAD_PATH.push(File.dirname(__FILE__))
14
+
15
+ module Floorplanner
16
+
17
+ Log = Logger.new STDOUT
18
+ Log.level = Logger::WARN
19
+ Log.datetime_format = "%Y-%m-%d %H:%M:%S "
20
+
21
+ def self.config
22
+ @@config ||= YAML.load_file(File.join(File.dirname(__FILE__),'config.yml'))
23
+ end
24
+
25
+ def self.config=(yaml)
26
+ @@config = yaml
27
+ end
28
+ end
29
+
30
+ module XML
31
+ class Node
32
+ def get_floats
33
+ content.split(/\s/).delete_if {|s| s.empty?}.map! {|f| f.to_f}
34
+ end
35
+ end
36
+ end
37
+
38
+ require 'geom'
39
+ require 'floorplanner/asset'
40
+ require 'floorplanner/document'
41
+ require 'floorplanner/collada_export'
42
+ require 'floorplanner/rib_export'
43
+ require 'floorplanner/obj_export'
44
+ require 'floorplanner/svg_export'
45
+ require 'floorplanner/design'
46
+ require 'floorplanner/wall3d'
47
+ require 'floorplanner/opening3d'
48
+ require 'floorplanner/wall_builder'
49
+ require 'floorplanner/area_builder'
50
+ require 'collada/document'
51
+ require 'collada/geometry'
52
+ require 'keyhole/archive'