fml 0.2.3 → 0.2.4
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.
- data/.gitignore +1 -0
- data/TODO +2 -0
- data/bin/fml2dae.rb +14 -9
- data/fml.gemspec +10 -7
- data/lib/collada/document.rb +21 -23
- data/lib/collada/geometry.rb +22 -23
- data/lib/config.yml +4 -4
- data/lib/floorplanner/area_builder.rb +3 -8
- data/lib/floorplanner/asset.rb +32 -27
- data/lib/floorplanner/collada_export.rb +76 -71
- data/lib/floorplanner/design.rb +42 -76
- data/lib/floorplanner/document.rb +16 -43
- data/lib/floorplanner/opening3d.rb +5 -4
- data/lib/floorplanner/wall_builder.rb +6 -5
- data/lib/floorplanner/xml.rb +151 -0
- data/lib/floorplanner.rb +11 -9
- data/lib/geom/edge.rb +1 -1
- data/lib/geom/matrix3d.rb +7 -2
- data/lib/geom/number.rb +8 -0
- data/lib/geom/polygon.rb +18 -22
- data/lib/geom/vertex.rb +8 -0
- data/tasks/github-gem.rake +47 -4
- data/views/design.dae.erb +37 -92
- metadata +53 -40
- data/bin/fml2obj.rb +0 -10
- data/lib/floorplanner/obj_export.rb +0 -24
- data/lib/floorplanner/rib_export.rb +0 -24
- data/views/design.obj.erb +0 -17
- data/views/design.rib.erb +0 -17
data/lib/floorplanner/design.rb
CHANGED
@@ -1,35 +1,14 @@
|
|
1
1
|
module Floorplanner
|
2
2
|
class Design
|
3
|
-
DESIGN_QUERY = "/project/floors/floor/designs/design[id='%s']"
|
4
|
-
DESIGN_N_QUERY = "/project/floors/floor/designs/design[name='%s']"
|
5
|
-
ASSET_QUERY = DESIGN_QUERY+"/assets/asset[@id='%s']"
|
6
|
-
ASSET_URL2D = ASSET_QUERY+"/url2d"
|
7
|
-
LINES_QUERY = DESIGN_QUERY+"/lines/line"
|
8
|
-
OPENINGS_QUERY = DESIGN_QUERY+"/objects/object[type='opening']"
|
9
|
-
AREAS_QUERY = DESIGN_QUERY+"/areas/area"
|
10
|
-
NAME_QUERY = DESIGN_QUERY+"/name"
|
11
3
|
|
12
4
|
include ColladaExport
|
13
|
-
include ObjExport
|
14
|
-
include RibExport
|
15
5
|
include SvgExport
|
16
6
|
|
17
7
|
##
|
18
8
|
# Constructs new floorplan design from FML
|
19
9
|
##
|
20
|
-
def initialize(
|
21
|
-
|
22
|
-
@xml = fml
|
23
|
-
unless fml.find(DESIGN_QUERY % design_id).length.zero?
|
24
|
-
@design_id = design_id
|
25
|
-
else
|
26
|
-
@design_id = fml.find(DESIGN_N_QUERY % design_id).first.find("id").first.content
|
27
|
-
end
|
28
|
-
@name = @xml.find(NAME_QUERY % @design_id).first.content
|
29
|
-
@author = "John Doe" # TODO from <author> element if included in FML
|
30
|
-
rescue NoMethodError
|
31
|
-
$stderr.puts "Can't find Design with ID or name: %s" % design_id
|
32
|
-
end
|
10
|
+
def initialize(doc)
|
11
|
+
@doc = doc
|
33
12
|
end
|
34
13
|
|
35
14
|
##
|
@@ -37,71 +16,58 @@ module Floorplanner
|
|
37
16
|
##
|
38
17
|
def build_geometries
|
39
18
|
@areas = AreaBuilder.new do |b|
|
40
|
-
@
|
41
|
-
|
42
|
-
|
43
|
-
type = area.find('type').first.content
|
44
|
-
|
45
|
-
asset_id = area.find('asset').first.attributes['refid']
|
46
|
-
texture_url = @xml.find(ASSET_URL2D % [@design_id,asset_id]).first.content
|
47
|
-
|
48
|
-
vertices = Array.new
|
49
|
-
area.find('points').first.content.split(',').each do |str_v|
|
50
|
-
floats = str_v.strip.split(/\s/).map! {|f| f.to_f}
|
51
|
-
|
52
|
-
# TODO: fix y coords in Flash app
|
53
|
-
floats[1] *= -1.0; floats[4] *= -1.0
|
54
|
-
|
55
|
-
vertices << b.vertex(Geom::Vertex.new(*floats[0..2]))
|
56
|
-
vertices << b.vertex(Geom::Vertex.new(*floats[3..5]))
|
19
|
+
@doc.areas.each do |area|
|
20
|
+
if area.asset
|
21
|
+
texture_url = @doc.asset(area.asset).url2d
|
57
22
|
end
|
58
23
|
|
59
|
-
b.area(vertices,
|
60
|
-
:color
|
61
|
-
:name
|
24
|
+
b.area(area.vertices,
|
25
|
+
:color => area.color,
|
26
|
+
:name => area.name,
|
62
27
|
:texture => texture_url,
|
63
|
-
:type
|
64
|
-
|
28
|
+
:type => area.type)
|
65
29
|
end
|
66
30
|
end
|
67
|
-
min_height = 10
|
68
|
-
@walls = WallBuilder.new do |b|
|
69
|
-
@xml.find(LINES_QUERY % @design_id).each do |line|
|
70
|
-
floats = line.find('points').first.get_floats
|
71
|
-
|
72
|
-
thickness = line.find('thickness').first.content.to_f
|
73
|
-
height = line.find('height').first.content.to_f
|
74
|
-
|
75
|
-
# TODO: fix this in Flash app
|
76
|
-
floats[1] *= -1.0; floats[4] *= -1.0
|
77
31
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
b.
|
83
|
-
|
32
|
+
heights = Hash.new
|
33
|
+
@walls = WallBuilder.new do |b|
|
34
|
+
@doc.lines.select{|l| l.type == :default_wall}.each do |line|
|
35
|
+
sp = b.vertex(line.vertices[0])
|
36
|
+
ep = b.vertex(line.vertices[1])
|
37
|
+
b.wall(sp, ep, line.thickness, line.height)
|
38
|
+
|
39
|
+
if heights.include?(line.height)
|
40
|
+
heights[line.height] += 1
|
41
|
+
else
|
42
|
+
heights[line.height] = 1
|
43
|
+
end
|
84
44
|
end
|
85
45
|
end
|
86
|
-
|
46
|
+
# get the most used (common) height accross linears
|
47
|
+
@areas.update(heights.sort{|a,b| a[1]<=>b[1]}.last[0]-0.02)
|
87
48
|
|
88
49
|
@walls.prepare
|
89
|
-
@
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
pos_floats[1] *= -1
|
94
|
-
|
95
|
-
size_floats = opening.find('size').first.get_floats
|
96
|
-
position = Geom::Number3D.new(*pos_floats)
|
97
|
-
size = Geom::Number3D.new(*size_floats)
|
98
|
-
|
99
|
-
asset_id = opening.find('asset').first.attributes['refid']
|
100
|
-
asset = @xml.find(ASSET_QUERY % [@design_id,asset_id]).first
|
101
|
-
type = asset.find('url2d').first.content.match(/door/i) ? Opening3D::TYPE_DOOR : Opening3D::TYPE_WINDOW
|
102
|
-
@walls.opening(position,size,type)
|
50
|
+
@doc.openings.each do |opening|
|
51
|
+
asset = @doc.asset(opening.asset)
|
52
|
+
type = which_opening(opening, asset)
|
53
|
+
@walls.opening(opening.position ,opening.size, type)
|
103
54
|
end
|
104
55
|
@walls.update
|
105
56
|
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def which_opening(opening, asset)
|
61
|
+
case opening.type
|
62
|
+
when :door
|
63
|
+
type = Opening3D::TYPE_DOOR
|
64
|
+
when :window
|
65
|
+
type = Opening3D::TYPE_WINDOW
|
66
|
+
else
|
67
|
+
type = asset.url2d.match(/door/i) ?
|
68
|
+
Opening3D::TYPE_DOOR : Opening3D::TYPE_WINDOW
|
69
|
+
end
|
70
|
+
type
|
71
|
+
end
|
106
72
|
end
|
107
73
|
end
|
@@ -1,59 +1,33 @@
|
|
1
1
|
module Floorplanner
|
2
2
|
|
3
3
|
class Document
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def initialize(fml_fn)
|
8
|
-
@xml = LibXML::XML::Document.file(fml_fn)
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.validate(doc)
|
12
|
-
schema = LibXML::XML::RelaxNG.document(
|
13
|
-
LibXML::XML::Document.file(File.join(File.dirname(__FILE__), "..", "..", "xml", "fml.rng"))
|
14
|
-
)
|
15
|
-
doc = LibXML::XML::Document.file(doc) if doc.instance_of?(String)
|
16
|
-
doc.validate_relaxng(schema) do |message,error|
|
17
|
-
# TODO throw an exception
|
18
|
-
puts message if error
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def self.validate_line_points(doc)
|
25
|
-
doc.find(POINTS_QUERY).each do |points_node|
|
26
|
-
unless LINE_POINTS_REGEXP =~ points_node.children.to_s
|
27
|
-
# TODO throw an exception
|
28
|
-
puts "Elements points inside area's line failed to validate content."
|
29
|
-
end
|
30
|
-
end
|
4
|
+
def initialize fml_fn
|
5
|
+
@xml = Nokogiri::XML.parse(open(fml_fn))
|
31
6
|
end
|
32
7
|
end
|
33
8
|
|
34
9
|
class DesignDocument
|
35
|
-
|
36
|
-
def initialize(fml)
|
10
|
+
def initialize fml
|
37
11
|
if fml.kind_of? String # filename
|
38
|
-
@xml =
|
39
|
-
elsif fml.kind_of?
|
12
|
+
@xml = Nokogiri::XML.parse(fml)
|
13
|
+
elsif fml.kind_of? Nokogiri::XML::Document
|
40
14
|
@xml = fml
|
41
15
|
elsif fml.respond_to?(:read) # IO
|
42
|
-
@xml =
|
16
|
+
@xml = Nokogiri::XML.parse(fml)
|
43
17
|
else
|
44
18
|
raise ArgumentError.new("values must be one of: filename, IO, LibXML::XML::Document")
|
45
19
|
end
|
46
20
|
end
|
47
21
|
|
48
|
-
def update_heights
|
49
|
-
lines = @xml.
|
22
|
+
def update_heights new_height
|
23
|
+
lines = @xml.xpath("/design/lines/line[type='default_wall' or type='normal_wall' or contains(type,'hedge') or contains(type,'fence')]")
|
50
24
|
lines.each do |line|
|
51
25
|
begin
|
52
|
-
points = line.
|
26
|
+
points = line.xpath("points").first
|
53
27
|
next unless points.content.include? ","
|
54
28
|
|
55
29
|
coords = points.content.strip.split(",")
|
56
|
-
top_coords = coords[1].strip.split(/\s/).map
|
30
|
+
top_coords = coords[1].strip.split(/\s/).map(&:to_f)
|
57
31
|
|
58
32
|
top_coords[2] = new_height
|
59
33
|
top_coords[5] = new_height
|
@@ -67,11 +41,11 @@ module Floorplanner
|
|
67
41
|
end
|
68
42
|
end
|
69
43
|
|
70
|
-
def update_thumb_2d_url
|
71
|
-
if thumb_node = @xml.
|
44
|
+
def update_thumb_2d_url thumb_2d_url
|
45
|
+
if thumb_node = @xml.xpath('/design/thumb-2d-url').first
|
72
46
|
thumb_node.content = thumb_2d_url
|
73
|
-
elsif design_node = @xml.
|
74
|
-
thumb_node =
|
47
|
+
elsif design_node = @xml.xpath('/design').first
|
48
|
+
thumb_node = @xml.create_element('thumb-2d-url')
|
75
49
|
thumb_node.content = thumb_2d_url
|
76
50
|
design_node << thumb_node
|
77
51
|
else
|
@@ -79,8 +53,8 @@ module Floorplanner
|
|
79
53
|
end
|
80
54
|
end
|
81
55
|
|
82
|
-
def save
|
83
|
-
@xml.
|
56
|
+
def save path
|
57
|
+
@xml.write_to open(path, 'w')
|
84
58
|
end
|
85
59
|
|
86
60
|
def to_xml
|
@@ -91,5 +65,4 @@ module Floorplanner
|
|
91
65
|
@xml.to_s
|
92
66
|
end
|
93
67
|
end
|
94
|
-
|
95
68
|
end
|
@@ -8,20 +8,21 @@ module Floorplanner
|
|
8
8
|
|
9
9
|
def initialize(baseline,thickness,opening)
|
10
10
|
super()
|
11
|
+
base_z = opening[:position].z
|
11
12
|
@position = baseline.snap(opening[:position])
|
12
13
|
@type = opening[:type]
|
14
|
+
@size = opening[:size]
|
13
15
|
dir = baseline.direction
|
14
16
|
angle = Math.atan2(dir.y,dir.x)
|
15
17
|
width = opening[:size].x
|
16
|
-
height = 0
|
17
18
|
|
18
19
|
case @type
|
19
20
|
when TYPE_DOOR
|
20
21
|
@position.z = 0.01
|
21
|
-
height = Floorplanner.config['openings']['door_height']
|
22
|
+
height = @size.z == 0 ? Floorplanner.config['openings']['door_height'] : @size.z
|
22
23
|
else
|
23
|
-
@position.z = Floorplanner.config['openings']['window_base']
|
24
|
-
height = Floorplanner.config['openings']['window_height']
|
24
|
+
@position.z = base_z == 0 ? Floorplanner.config['openings']['window_base'] : base_z
|
25
|
+
height = @size.z == 0 ? Floorplanner.config['openings']['window_height'] : @size.z
|
25
26
|
end
|
26
27
|
|
27
28
|
v1 = Geom::Vertex.new(-width/2,0,0)
|
@@ -26,7 +26,7 @@ module Floorplanner
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
def wall(sp,ep,thickness,height)
|
29
|
+
def wall(sp, ep, thickness, height)
|
30
30
|
@connections[sp] = Array.new unless @connections.include?(sp)
|
31
31
|
@connections[ep] = Array.new unless @connections.include?(ep)
|
32
32
|
cs = Geom::Connection.new(ep, 0.0)
|
@@ -37,10 +37,11 @@ module Floorplanner
|
|
37
37
|
end
|
38
38
|
|
39
39
|
# call after adding walls
|
40
|
-
def opening(position,size,type)
|
40
|
+
def opening(position, size, type)
|
41
41
|
@walls.each do |wall|
|
42
42
|
if wall.outline.point_inside(position)
|
43
43
|
wall.opening(position,size,type)
|
44
|
+
break
|
44
45
|
end
|
45
46
|
end
|
46
47
|
end
|
@@ -51,9 +52,9 @@ module Floorplanner
|
|
51
52
|
next if connections.length.zero?
|
52
53
|
|
53
54
|
connections.each do |c|
|
54
|
-
|
55
|
-
|
56
|
-
|
55
|
+
begin
|
56
|
+
c.angle = Math.atan2(c.point.x - v.x, c.point.y - v.y)
|
57
|
+
rescue; end
|
57
58
|
end
|
58
59
|
connections.sort! {|a,b| a.angle <=> b.angle}
|
59
60
|
connections.each_index do |i|
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Floorplanner::XML
|
2
|
+
|
3
|
+
class Asset
|
4
|
+
include ROXML
|
5
|
+
|
6
|
+
xml_accessor :id, :from => :attr
|
7
|
+
xml_accessor :name
|
8
|
+
xml_accessor :url2d_str, :from => 'url2d'
|
9
|
+
xml_accessor :url3d
|
10
|
+
|
11
|
+
def url2d
|
12
|
+
url2d_str.match(/swf$/) ? url2d_str.gsub(/swf$/, 'png') : url2d_str
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Line
|
17
|
+
include ROXML
|
18
|
+
|
19
|
+
DEFAULT_HEIGHT = 2.4
|
20
|
+
|
21
|
+
xml_accessor :points
|
22
|
+
xml_accessor :type_str, :from => 'type'
|
23
|
+
xml_accessor :asset, :from => 'asset/@refid'
|
24
|
+
xml_accessor :thickness_str, :from => 'thickness'
|
25
|
+
xml_accessor :height_str, :from => 'height'
|
26
|
+
|
27
|
+
def type
|
28
|
+
type_str.to_sym
|
29
|
+
end
|
30
|
+
|
31
|
+
def height
|
32
|
+
height_str ? height_str.to_f : (
|
33
|
+
vertices[3] ? vertices[3].z : DEFAULT_HEIGHT)
|
34
|
+
end
|
35
|
+
|
36
|
+
def thickness
|
37
|
+
thickness_str.to_f
|
38
|
+
end
|
39
|
+
|
40
|
+
def vertices
|
41
|
+
@vertices ||= Floorplanner::XML.parse_points(points)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Area
|
46
|
+
include ROXML
|
47
|
+
|
48
|
+
xml_accessor :points
|
49
|
+
xml_accessor :color
|
50
|
+
xml_accessor :name
|
51
|
+
xml_accessor :asset, :from => 'asset/@refid'
|
52
|
+
xml_accessor :type_str, :from => 'type'
|
53
|
+
|
54
|
+
def type
|
55
|
+
type_str.to_sym if type_str
|
56
|
+
end
|
57
|
+
|
58
|
+
def vertices
|
59
|
+
@vertices ||= Floorplanner::XML.parse_points(points)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Item
|
64
|
+
include ROXML
|
65
|
+
|
66
|
+
xml_accessor :asset, :from => 'asset/@refid'
|
67
|
+
xml_accessor :type_str, :from => 'type'
|
68
|
+
xml_accessor :color
|
69
|
+
|
70
|
+
xml_accessor :points
|
71
|
+
xml_accessor :size_str, :from => 'size'
|
72
|
+
xml_accessor :rotation_str, :from => 'rotation'
|
73
|
+
xml_accessor :mirrored_str, :from => 'mirrored'
|
74
|
+
|
75
|
+
def type
|
76
|
+
type_str.to_sym
|
77
|
+
end
|
78
|
+
|
79
|
+
def size
|
80
|
+
@scale ||= Floorplanner::XML.parse_vector(size_str)
|
81
|
+
end
|
82
|
+
|
83
|
+
def position
|
84
|
+
@position ||= Floorplanner::XML.parse_point(points)
|
85
|
+
end
|
86
|
+
|
87
|
+
def rotation
|
88
|
+
@rotation ||= Floorplanner::XML.parse_rotation(rotation_str)
|
89
|
+
end
|
90
|
+
|
91
|
+
def mirrored
|
92
|
+
@mirrored ||= Floorplanner::XML.parse_vector(mirrored_str)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class Document
|
97
|
+
include ROXML
|
98
|
+
|
99
|
+
xml_accessor :name
|
100
|
+
xml_accessor :assets, :as => [Asset]
|
101
|
+
xml_accessor :lines, :as => [Line]
|
102
|
+
xml_accessor :areas, :as => [Area]
|
103
|
+
xml_accessor :objects, :as => [Item]
|
104
|
+
xml_accessor :openings, :as => [Item],
|
105
|
+
:from => "objects/object[(type='opening') or (type='window') or (type='door')]"
|
106
|
+
|
107
|
+
def asset(refid)
|
108
|
+
assets.detect { |a| a.id == refid }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
|
114
|
+
# We are inverting Y coordinate here because of Flash
|
115
|
+
# inverted coordinate system
|
116
|
+
|
117
|
+
def self.parse_point(val)
|
118
|
+
f = val.split(/\s/).map{ |s| s.to_f }
|
119
|
+
Geom::Vertex.new(f[0], -f[1], f[2])
|
120
|
+
rescue
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
# Inverting Z rotation (Flash again)
|
125
|
+
|
126
|
+
def self.parse_rotation(val)
|
127
|
+
f = val.split(/\s/).map{ |s| s.to_f }
|
128
|
+
Geom::Vertex.new(f[0], f[1], -f[2])
|
129
|
+
rescue
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.parse_vector(val)
|
134
|
+
Geom::Vertex.new(*val.split(/\s/).map{ |s| s.to_f })
|
135
|
+
rescue
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.parse_points(val)
|
140
|
+
points = val.split(/\s|,/)
|
141
|
+
raise "Invalid coords. count for points" unless points.length % 3 == 0
|
142
|
+
|
143
|
+
result = []
|
144
|
+
points.each_with_index do |x,i|
|
145
|
+
result << [] if i % 3 == 0
|
146
|
+
result.last << x.to_f
|
147
|
+
end
|
148
|
+
result.map { |f| Geom::Vertex.new(f[0], -f[1], f[2]) }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
data/lib/floorplanner.rb
CHANGED
@@ -7,7 +7,8 @@ require 'fileutils'
|
|
7
7
|
|
8
8
|
require 'rubygems'
|
9
9
|
require 'zip/zip'
|
10
|
-
require '
|
10
|
+
require 'nokogiri'
|
11
|
+
require 'roxml'
|
11
12
|
|
12
13
|
$LOAD_PATH.push(File.dirname(__FILE__))
|
13
14
|
|
@@ -16,25 +17,26 @@ module Floorplanner
|
|
16
17
|
@@config ||= YAML.load_file(File.join(File.dirname(__FILE__),'config.yml'))
|
17
18
|
end
|
18
19
|
|
19
|
-
def self.config=
|
20
|
+
def self.config= yaml
|
20
21
|
@@config = yaml
|
21
22
|
end
|
22
|
-
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
HEX_RE = "(?i:[a-f\\d])"
|
25
|
+
|
26
|
+
def self.read_color hexstring
|
27
|
+
if hexstring =~ /\A#((?:#{HEX_RE}{2,2}){3,4})\z/
|
28
|
+
return [*$1.scan(/.{2,2}/).collect {|value| value.hex / 255.0}]
|
29
|
+
else
|
30
|
+
return [1,1,1]
|
28
31
|
end
|
29
32
|
end
|
30
33
|
end
|
31
34
|
|
32
35
|
require 'geom'
|
36
|
+
require 'floorplanner/xml'
|
33
37
|
require 'floorplanner/asset'
|
34
38
|
require 'floorplanner/document'
|
35
39
|
require 'floorplanner/collada_export'
|
36
|
-
require 'floorplanner/rib_export'
|
37
|
-
require 'floorplanner/obj_export'
|
38
40
|
require 'floorplanner/svg_export'
|
39
41
|
require 'floorplanner/design'
|
40
42
|
require 'floorplanner/wall3d'
|
data/lib/geom/edge.rb
CHANGED
@@ -22,7 +22,7 @@ module Geom
|
|
22
22
|
edge = clone
|
23
23
|
dir = direction
|
24
24
|
|
25
|
-
Matrix3D.multiply_vector_3x3(Matrix3D.
|
25
|
+
Matrix3D.multiply_vector_3x3(Matrix3D.rotation(up.x,up.y,up.z, -Math::PI/2),dir)
|
26
26
|
dir.normalize
|
27
27
|
|
28
28
|
dir.x *= distance
|
data/lib/geom/matrix3d.rb
CHANGED
@@ -9,7 +9,7 @@ module Geom
|
|
9
9
|
]
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.
|
12
|
+
def self.rotation(x,y,z,rad)
|
13
13
|
n_cos = Math.cos(rad)
|
14
14
|
n_sin = Math.sin(rad)
|
15
15
|
s_cos = 1 - n_cos
|
@@ -111,6 +111,10 @@ module Geom
|
|
111
111
|
v.z = vx * ma[2][0] + vy * ma[2][1] + vz * ma[2][2]
|
112
112
|
end
|
113
113
|
|
114
|
+
def *(other)
|
115
|
+
multiply(other)
|
116
|
+
end
|
117
|
+
|
114
118
|
def multiply(other)
|
115
119
|
# matrix multiplication is m[r][c] = (row[r]).(col[c])
|
116
120
|
s = to_a
|
@@ -133,7 +137,8 @@ module Geom
|
|
133
137
|
Matrix3D[
|
134
138
|
[rm00, rm01, rm02, rm03],
|
135
139
|
[rm10, rm11, rm12, rm13],
|
136
|
-
[rm20, rm21, rm22, rm23]
|
140
|
+
[rm20, rm21, rm22, rm23],
|
141
|
+
[ 0, 0, 0, 1]
|
137
142
|
]
|
138
143
|
end
|
139
144
|
|
data/lib/geom/number.rb
CHANGED
data/lib/geom/polygon.rb
CHANGED
@@ -124,30 +124,26 @@ module Geom
|
|
124
124
|
end
|
125
125
|
|
126
126
|
def point_inside(pt)
|
127
|
-
x =
|
128
|
-
y =
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
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
|
127
|
+
x = pt.x;
|
128
|
+
y = pt.y;
|
129
|
+
c = false;
|
130
|
+
length = @vertices.length
|
131
|
+
|
132
|
+
length.times do |i|
|
133
|
+
j = (i+1) % length
|
134
|
+
|
135
|
+
a = @vertices[i]
|
136
|
+
b = @vertices[j]
|
137
|
+
|
138
|
+
xpi = a.x
|
139
|
+
ypi = a.y
|
140
|
+
xpj = b.x
|
141
|
+
ypj = b.y
|
142
|
+
if ((((ypi<=y) && (y<ypj)) || ((ypj<=y) && (y<ypi))) && (x < (xpj-xpi)*(y-ypi)/(ypj-ypi)+xpi))
|
143
|
+
c = !c
|
148
144
|
end
|
149
145
|
end
|
150
|
-
|
146
|
+
return c;
|
151
147
|
end
|
152
148
|
|
153
149
|
def winding
|