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,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'