fml 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|