motel 0.2
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/COPYING +8 -0
- data/LICENSE +661 -0
- data/README +0 -0
- data/conf/amqp.yml +13 -0
- data/conf/database.yml +41 -0
- data/db/migrate/001_create_locations_and_movement_strategies.rb +45 -0
- data/db/migrate/002_create_linear_movement_strategy.rb +23 -0
- data/db/migrate/003_create_elliptical_movement_strategy.rb +52 -0
- data/lib/motel/common.rb +54 -0
- data/lib/motel/loader.rb +47 -0
- data/lib/motel/models/elliptical.rb +259 -0
- data/lib/motel/models/linear.rb +76 -0
- data/lib/motel/models/location.rb +109 -0
- data/lib/motel/models/movement_strategy.rb +82 -0
- data/lib/motel/models/stopped.rb +16 -0
- data/lib/motel/runner.rb +135 -0
- data/lib/motel/semaphore.rb +58 -0
- data/lib/motel.rb +13 -0
- metadata +82 -0
data/conf/amqp.yml
ADDED
data/conf/database.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# motel database config
|
2
|
+
#
|
3
|
+
# Fedora Install Instructions:
|
4
|
+
# sudo yum install ruby-postgres postgresql-server
|
5
|
+
# sudo /sbin/service postgresql initdb
|
6
|
+
# sudo /sbin/service postgresql start
|
7
|
+
# sudo su - postgres
|
8
|
+
# createdb motel
|
9
|
+
# $ psql motel
|
10
|
+
# $-# CREATE USER motel WITH PASSWORD 'motel';
|
11
|
+
# $-# GRANT ALL PRIVILEGES ON DATABASE motel to motel;
|
12
|
+
# $-# CREATE DATABASE motel_test;
|
13
|
+
# $-# GRANT ALL PRIVILEGES ON DATABASE motel_test to motel;
|
14
|
+
# $-# CREATE DATABASE motel_development;
|
15
|
+
# $-# GRANT ALL PRIVILEGES ON DATABASE motel_development to motel;
|
16
|
+
# $-# \q
|
17
|
+
# $ exit
|
18
|
+
|
19
|
+
production:
|
20
|
+
adapter: postgresql
|
21
|
+
database: motel
|
22
|
+
username: motel
|
23
|
+
password: motel
|
24
|
+
host: localhost
|
25
|
+
pool: 20
|
26
|
+
|
27
|
+
development:
|
28
|
+
adapter: postgresql
|
29
|
+
database: motel_development
|
30
|
+
username: motel
|
31
|
+
password: motel
|
32
|
+
host: localhost
|
33
|
+
pool: 20
|
34
|
+
|
35
|
+
test:
|
36
|
+
adapter: postgresql
|
37
|
+
database: motel_test
|
38
|
+
username: motel
|
39
|
+
password: motel
|
40
|
+
host: localhost
|
41
|
+
pool: 20
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# creates the locations and movement_strategies tables
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# See COPYING for the License of this software
|
5
|
+
|
6
|
+
require 'motel'
|
7
|
+
|
8
|
+
# ActiveRecord::Migration 001
|
9
|
+
class CreateLocationsAndMovementStrategies < ActiveRecord::Migration
|
10
|
+
def self.up
|
11
|
+
|
12
|
+
create_table :movement_strategies do |t|
|
13
|
+
t.string :type, :null => false, :size => 50
|
14
|
+
t.float :step_delay, :null => false
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :locations do |t|
|
18
|
+
t.float :x, :default => nil
|
19
|
+
t.float :y, :default => nil
|
20
|
+
t.float :z, :default => nil
|
21
|
+
t.integer :movement_strategy_id, :null => false
|
22
|
+
|
23
|
+
t.integer :parent_id, :default => nil
|
24
|
+
end
|
25
|
+
|
26
|
+
execute "alter table locations add constraint fk_location_parent
|
27
|
+
foreign key(parent_id) references locations(id)"
|
28
|
+
|
29
|
+
execute "alter table locations add constraint fk_location_movement_strategy
|
30
|
+
foreign key(movement_strategy_id) references movement_strategies(id)"
|
31
|
+
|
32
|
+
execute "alter table locations add constraint root_or_child
|
33
|
+
check (parent_id IS NULL AND x IS NULL AND y IS NULL AND z IS NULL OR
|
34
|
+
parent_id IS NOT NULL AND x IS NOT NULL AND y IS NOT NULL AND z IS NOT NULL)"
|
35
|
+
|
36
|
+
|
37
|
+
# create the first / default movement strategy 'stopped'
|
38
|
+
Motel::Models::Stopped.new(:step_delay => 5).save!
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.down
|
42
|
+
drop_table :locations
|
43
|
+
drop_table :movement_strategies
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# creates the neccessary fields for the Linear MovementStrategy
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# See COPYING for the License of this software
|
5
|
+
|
6
|
+
require 'motel'
|
7
|
+
|
8
|
+
# ActiveRecord::Migration 002
|
9
|
+
class CreateLinearMovementStrategy < ActiveRecord::Migration
|
10
|
+
def self.up
|
11
|
+
add_column :movement_strategies, :speed, :float
|
12
|
+
add_column :movement_strategies, :direction_vector_x, :float
|
13
|
+
add_column :movement_strategies, :direction_vector_y, :float
|
14
|
+
add_column :movement_strategies, :direction_vector_z, :float
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.down
|
18
|
+
remove_column :movement_strategies, :speed
|
19
|
+
remove_column :movement_strategies, :direction_vector_x
|
20
|
+
remove_column :movement_strategies, :direction_vector_y
|
21
|
+
remove_column :movement_strategies, :direction_vector_z
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# creates the neccessary fields for the Elliptical MovementStrategy
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# See COPYING for the License of this software
|
5
|
+
|
6
|
+
require 'motel'
|
7
|
+
|
8
|
+
# ActiveRecord::Migration 003
|
9
|
+
class CreateEllipticalMovementStrategy < ActiveRecord::Migration
|
10
|
+
def self.up
|
11
|
+
# we already have speed from linear
|
12
|
+
#add_column :movement_strategies, :speed, :float
|
13
|
+
|
14
|
+
# relative_to field indicates what type of parent
|
15
|
+
# this movement stategy is relative to, for Ellilptical
|
16
|
+
# strategies, this can be 'center' indicating the parent
|
17
|
+
# is the center of the ellipse or 'foci' indicating the
|
18
|
+
# parent is on of the ellipse foci's
|
19
|
+
add_column :movement_strategies, :relative_to, :string, :size => 50
|
20
|
+
|
21
|
+
# eccentricity of the ellipse
|
22
|
+
add_column :movement_strategies, :eccentricity, :float
|
23
|
+
|
24
|
+
# semi_latus_rectum of the ellipse
|
25
|
+
add_column :movement_strategies, :semi_latus_rectum, :float
|
26
|
+
|
27
|
+
# unit direction vector of the major axis
|
28
|
+
add_column :movement_strategies, :direction_major_x, :float
|
29
|
+
add_column :movement_strategies, :direction_major_y, :float
|
30
|
+
add_column :movement_strategies, :direction_major_z, :float
|
31
|
+
|
32
|
+
# unit direction vector of the minor axis
|
33
|
+
add_column :movement_strategies, :direction_minor_x, :float
|
34
|
+
add_column :movement_strategies, :direction_minor_y, :float
|
35
|
+
add_column :movement_strategies, :direction_minor_z, :float
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.down
|
39
|
+
# remove in linear strategy
|
40
|
+
#remove_column :movement_strategies, :speed
|
41
|
+
|
42
|
+
remove_column :movement_strategies, :relative_to
|
43
|
+
remove_column :movement_strategies, :eccentricity
|
44
|
+
remove_column :movement_strategies, :semi_latus_rectum
|
45
|
+
remove_column :movement_strategies, :direction_major_x
|
46
|
+
remove_column :movement_strategies, :direction_major_y
|
47
|
+
remove_column :movement_strategies, :direction_major_z
|
48
|
+
remove_column :movement_strategies, :direction_minor_x
|
49
|
+
remove_column :movement_strategies, :direction_minor_y
|
50
|
+
remove_column :movement_strategies, :direction_minor_z
|
51
|
+
end
|
52
|
+
end
|
data/lib/motel/common.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# common & useful methods and other.
|
2
|
+
#
|
3
|
+
# Things that don't fit elsewhere
|
4
|
+
#
|
5
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
6
|
+
# See COPYING for the License of this software
|
7
|
+
|
8
|
+
module Motel
|
9
|
+
|
10
|
+
# generate a random id
|
11
|
+
def gen_uuid
|
12
|
+
["%02x"*4, "%02x"*2, "%02x"*2, "%02x"*2, "%02x"*6].join("-") %
|
13
|
+
Array.new(16) {|x| rand(0xff) }
|
14
|
+
end
|
15
|
+
|
16
|
+
# normalize a vector if not normal already,
|
17
|
+
# eg divide each component x,y,z by the
|
18
|
+
# vector length and return them
|
19
|
+
def normalize(x,y,z)
|
20
|
+
return x,y,z if x.nil? || y.nil? || z.nil?
|
21
|
+
|
22
|
+
l = Math.sqrt(x**2 + y**2 + z**2)
|
23
|
+
if l != 1
|
24
|
+
x /= l
|
25
|
+
y /= l
|
26
|
+
z /= l
|
27
|
+
end
|
28
|
+
return x,y,z
|
29
|
+
end
|
30
|
+
|
31
|
+
# determine if two vectors are orthogonal
|
32
|
+
def orthogonal?(x1,y1,z1, x2,y2,z2)
|
33
|
+
return false if x1.nil? || y1.nil? || z1.nil? || x2.nil? || y2.nil? || z2.nil?
|
34
|
+
return (x1 * x2 + y1 * y2 + z1 * z2).abs == 0
|
35
|
+
end
|
36
|
+
|
37
|
+
end # module Motel
|
38
|
+
|
39
|
+
# provide floating point rounding mechanism
|
40
|
+
class Float
|
41
|
+
def round_to(precision)
|
42
|
+
return nil if precision <= 0
|
43
|
+
return (self * 10 ** precision).round.to_f / (10 ** precision)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# try to find key equal to method, returning value if found
|
48
|
+
#class Hash
|
49
|
+
# def method_missing(method, *params)
|
50
|
+
# method = method.to_sym
|
51
|
+
# return self[method] if self.keys.collect{ |k| k.to_sym }.include?(method)
|
52
|
+
# super
|
53
|
+
# end
|
54
|
+
#end
|
data/lib/motel/loader.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# The Loader class definition
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# See COPYING for the License of this software
|
5
|
+
|
6
|
+
require 'motel/runner'
|
7
|
+
|
8
|
+
module Motel
|
9
|
+
|
10
|
+
# A Loader loads instances of Location from the db
|
11
|
+
# according to any specified conditions, instantiates
|
12
|
+
# a new Runner for each of those locations and returns them
|
13
|
+
class Loader
|
14
|
+
|
15
|
+
public
|
16
|
+
|
17
|
+
# Default class constructor
|
18
|
+
def initialize
|
19
|
+
end
|
20
|
+
|
21
|
+
# Static member to load all locations that match a specified
|
22
|
+
# condition (ala activerecord) and add it to the singleton Runner
|
23
|
+
# instance using it to run the locations.
|
24
|
+
def self.Load(conditions = 'parent_id IS NULL')
|
25
|
+
locations = Location.find(:all, :conditions => conditions)
|
26
|
+
return nil if locations.size == 0
|
27
|
+
|
28
|
+
locations.each { |location|
|
29
|
+
run_location(location)
|
30
|
+
}
|
31
|
+
return locations.size
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# Static internal helper method that adds a location and
|
37
|
+
# all its children to the Runner
|
38
|
+
def self.run_location(location)
|
39
|
+
Runner.get.run location
|
40
|
+
location.children.each { |child|
|
41
|
+
run_location child
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
# The Elliptcial MovementStrategy model definition
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# See COPYING for the License of this software
|
5
|
+
|
6
|
+
require 'motel/common'
|
7
|
+
require 'motel/models/movement_strategy'
|
8
|
+
|
9
|
+
module Motel
|
10
|
+
module Models
|
11
|
+
|
12
|
+
# The Elliptical MovementStrategy moves a location
|
13
|
+
# on an elliptical path described by major/minor
|
14
|
+
# axis direction vectors, and an eccentricity /
|
15
|
+
# semi_latus_rectum. The path equation will
|
16
|
+
# also depend on the value of the relative_to
|
17
|
+
# field indicating if the parent location
|
18
|
+
# is the center or a foci of the ellipse.
|
19
|
+
# Lastly a speed value is required indicating the
|
20
|
+
# angular velocity of the location.
|
21
|
+
class Elliptical < MovementStrategy
|
22
|
+
|
23
|
+
# Elliptical MovementStrategy must specify x,y,z components of
|
24
|
+
# the major and minor axis direction vectors
|
25
|
+
validates_presence_of [:direction_major_x,
|
26
|
+
:direction_major_y,
|
27
|
+
:direction_major_z,
|
28
|
+
:direction_minor_x,
|
29
|
+
:direction_minor_y,
|
30
|
+
:direction_minor_z]
|
31
|
+
|
32
|
+
# make sure the unit direction vectors are normal
|
33
|
+
before_validation :normalize_direction_vectors
|
34
|
+
def normalize_direction_vectors
|
35
|
+
dx, dy, dz =
|
36
|
+
normalize(direction_major_x, direction_major_y, direction_major_z)
|
37
|
+
self.direction_major_x, self.direction_major_y, self.direction_major_z = dx, dy, dz
|
38
|
+
|
39
|
+
dx, dy, dz =
|
40
|
+
normalize(direction_minor_x, direction_minor_y, direction_minor_z)
|
41
|
+
self.direction_minor_x, self.direction_minor_y, self.direction_minor_z = dx, dy, dz
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# Elliptical MovementStrategy must specify the angular velocity
|
46
|
+
# at which the location is moving
|
47
|
+
validates_presence_of :speed
|
48
|
+
validates_numericality_of :speed,
|
49
|
+
:greater_than_or_equal_to => 0,
|
50
|
+
:less_than_or_equal_to => 2 * Math::PI
|
51
|
+
|
52
|
+
# the eccentricity of the ellipse
|
53
|
+
validates_presence_of :eccentricity
|
54
|
+
validates_numericality_of :eccentricity,
|
55
|
+
:greater_than_or_equal_to => 0,
|
56
|
+
:less_than_or_equal_to => 1
|
57
|
+
|
58
|
+
def e
|
59
|
+
eccentricity
|
60
|
+
end
|
61
|
+
def e=(v)
|
62
|
+
eccentricity= v
|
63
|
+
end
|
64
|
+
|
65
|
+
# the semi_latus_rectum of the ellipse
|
66
|
+
validates_presence_of :semi_latus_rectum
|
67
|
+
validates_numericality_of :semi_latus_rectum,
|
68
|
+
:greater_than_or_equal_to => 0
|
69
|
+
|
70
|
+
def p
|
71
|
+
semi_latus_rectum
|
72
|
+
end
|
73
|
+
def p=(v)
|
74
|
+
semi_latus_rectum = v
|
75
|
+
end
|
76
|
+
|
77
|
+
# the possible relative_to values
|
78
|
+
RELATIVE_TO_CENTER = "center"
|
79
|
+
RELATIVE_TO_FOCI = "foci"
|
80
|
+
|
81
|
+
# must be relative to a parent center or foci
|
82
|
+
validates_presence_of :relative_to
|
83
|
+
validates_inclusion_of :relative_to,
|
84
|
+
:in => [ RELATIVE_TO_CENTER, RELATIVE_TO_FOCI ]
|
85
|
+
|
86
|
+
# ActiveRecord::Base::validate
|
87
|
+
def validate
|
88
|
+
errors.add("direction vectors must be orthogonal") unless orthogonal?(direction_major_x, direction_major_y, direction_major_z,
|
89
|
+
direction_minor_x, direction_minor_y, direction_minor_z)
|
90
|
+
end
|
91
|
+
|
92
|
+
# convert non-nil elliptical movement strategy attributes to a hash
|
93
|
+
def to_h
|
94
|
+
result = {}
|
95
|
+
result[:speed] = speed unless speed.nil?
|
96
|
+
result[:eccentricity] = eccentricity unless eccentricity.nil?
|
97
|
+
result[:semi_latus_rectum] = semi_latus_rectum unless semi_latus_rectum.nil?
|
98
|
+
result[:relative_to] = relative_to unless relative_to.nil?
|
99
|
+
result[:direction_major_x] = direction_major_x unless direction_major_x.nil?
|
100
|
+
result[:direction_major_y] = direction_major_y unless direction_major_y.nil?
|
101
|
+
result[:direction_major_z] = direction_major_z unless direction_major_z.nil?
|
102
|
+
result[:direction_minor_x] = direction_minor_x unless direction_minor_x.nil?
|
103
|
+
result[:direction_minor_y] = direction_minor_y unless direction_minor_y.nil?
|
104
|
+
result[:direction_minor_z] = direction_minor_z unless direction_minor_z.nil?
|
105
|
+
return result
|
106
|
+
end
|
107
|
+
|
108
|
+
# convert elliptical movement strategy to a string
|
109
|
+
def to_s
|
110
|
+
super + "; speed:#{speed}; eccentricity:#{eccentricity}; semi_latus_rectum:#{semi_latus_rectum}; relative_to:#{relative_to}; " +
|
111
|
+
"direction_major_x:#{direction_major_x}; direction_major_y:#{direction_major_y}; direction_major_z:#{direction_major_z}; " +
|
112
|
+
"direction_minor_x:#{direction_minor_x}; direction_minor_y:#{direction_minor_y}; direction_minor_z:#{direction_minor_z}"
|
113
|
+
end
|
114
|
+
|
115
|
+
# Motel::Models::MovementStrategy::move
|
116
|
+
def move(location, elapsed_seconds)
|
117
|
+
# make sure this movement strategy is valid
|
118
|
+
unless valid?
|
119
|
+
Logger.warn "elliptical movement strategy not valid, not proceeding with move"
|
120
|
+
return
|
121
|
+
end
|
122
|
+
|
123
|
+
# make sure location is on ellipse
|
124
|
+
unless location_valid? location
|
125
|
+
cx,cy,cz = closest_coordinates location
|
126
|
+
Logger.warn "location #{location} not on ellipse, the closest location is #{cl}, not proceeding with move"
|
127
|
+
return
|
128
|
+
end
|
129
|
+
|
130
|
+
Logger.debug "moving location #{location} via elliptical movement strategy"
|
131
|
+
|
132
|
+
# calculate distance moved and update x,y,z accordingly
|
133
|
+
distance = speed * elapsed_seconds
|
134
|
+
|
135
|
+
nX,nY,nZ = coordinates_from_theta(theta(location) + distance)
|
136
|
+
location.x = nX
|
137
|
+
location.y = nY
|
138
|
+
location.z = nZ
|
139
|
+
|
140
|
+
Logger.debug "moved location #{location} via elliptical movement strategy"
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
### internal helper movement methods
|
146
|
+
|
147
|
+
# return the a,b intercepts of the ellipse
|
148
|
+
# p = a(1 - e^2) = b^2 / a
|
149
|
+
# e = sqrt(1 - (b/a)^2)
|
150
|
+
def intercepts
|
151
|
+
a = p / (1 - e**2)
|
152
|
+
b = Math.sqrt(p * a)
|
153
|
+
return a,b
|
154
|
+
end
|
155
|
+
|
156
|
+
# return the linear eccentricity of the ellipse
|
157
|
+
# le = sqrt(a^2 - b^2)
|
158
|
+
def linear_eccentricity
|
159
|
+
a,b = intercepts
|
160
|
+
Math.sqrt(a**2 - b**2);
|
161
|
+
end
|
162
|
+
|
163
|
+
# return the coordinates of the center position
|
164
|
+
# C = (-direction_major) * le
|
165
|
+
def center
|
166
|
+
return 0,0,0 if relative_to == RELATIVE_TO_CENTER
|
167
|
+
|
168
|
+
a,b = intercepts
|
169
|
+
le = linear_eccentricity
|
170
|
+
|
171
|
+
centerX = -1 * direction_major_x * le;
|
172
|
+
centerY = -1 * direction_major_y * le;
|
173
|
+
centerZ = -1 * direction_major_z * le;
|
174
|
+
return centerX, centerY, centerZ
|
175
|
+
end
|
176
|
+
|
177
|
+
# return the coordinates of a focus position
|
178
|
+
# F = direction_major * le
|
179
|
+
def focus
|
180
|
+
return 0,0,0 if relative_to == RELATIVE_TO_FOCI
|
181
|
+
|
182
|
+
a,b = intercepts
|
183
|
+
le = linear_eccentricity
|
184
|
+
|
185
|
+
focusX = direction_major_x * le;
|
186
|
+
focusY = direction_major_y * le;
|
187
|
+
focusZ = direction_major_z * le;
|
188
|
+
return focusX, focusY, focusZ
|
189
|
+
end
|
190
|
+
|
191
|
+
# return the origin centered coordiates of a location
|
192
|
+
def origin_centered_coordinates(location)
|
193
|
+
cX,cY,cZ = center
|
194
|
+
return location.x - cX,
|
195
|
+
location.y - cY,
|
196
|
+
location.z - cZ
|
197
|
+
end
|
198
|
+
|
199
|
+
# return the theta corresponding to the position of a
|
200
|
+
# location on the elliptical path.
|
201
|
+
#
|
202
|
+
# derived formula for theta from x,y,z elliptical path equations (see below) and linear transformations:
|
203
|
+
# theta = acos((minY * (x-cX) - minX * (y-cY))/(a*(minY * majX - minX * majY)))
|
204
|
+
def theta(location)
|
205
|
+
a,b = intercepts
|
206
|
+
ocX,ocY,ocZ = origin_centered_coordinates location
|
207
|
+
|
208
|
+
t = (direction_minor_y * ocX - direction_minor_x * ocY) /
|
209
|
+
(a * (direction_minor_y * direction_major_x - direction_minor_x * direction_major_y))
|
210
|
+
t= 1.0 if(t>1.0)
|
211
|
+
theta = Math.acos(t)
|
212
|
+
|
213
|
+
# determine if current point is in negative quadrants of min axis coordinate system
|
214
|
+
below = ocY < ((direction_minor_x * ocX + direction_minor_z * ocZ) / (-direction_minor_y))
|
215
|
+
|
216
|
+
# adjust to compenate for acos loss if necessary
|
217
|
+
theta = (3 * Math::PI / 2) + (Math::PI / 2 - theta) if (below)
|
218
|
+
|
219
|
+
return theta;
|
220
|
+
end
|
221
|
+
|
222
|
+
# calculate the x,y,z coordinates of a location on the elliptical
|
223
|
+
# path given its theta
|
224
|
+
#
|
225
|
+
# Elliptical path equation:
|
226
|
+
# [x,y,z] = a * cos(theta) * maj + b * sin(theta) * min
|
227
|
+
# (if centered at origin)
|
228
|
+
def coordinates_from_theta(theta)
|
229
|
+
a,b = intercepts
|
230
|
+
cX,cY,cZ = center
|
231
|
+
|
232
|
+
x = cX + a * Math.cos(theta) * direction_major_x + b * Math.sin(theta) * direction_minor_x
|
233
|
+
y = cY + a * Math.cos(theta) * direction_major_y + b * Math.sin(theta) * direction_minor_y
|
234
|
+
z = cZ + a * Math.cos(theta) * direction_major_z + b * Math.sin(theta) * direction_minor_z
|
235
|
+
return x,y,z
|
236
|
+
end
|
237
|
+
|
238
|
+
# return x,y,z coordinates of the closest point on the ellipse to the given location
|
239
|
+
def closest_coordinates(location)
|
240
|
+
t = theta location
|
241
|
+
|
242
|
+
return nil if t.nan?
|
243
|
+
|
244
|
+
return coordinates_from_theta(t)
|
245
|
+
end
|
246
|
+
|
247
|
+
# return boolean indicating if the given location is on the ellipse or not
|
248
|
+
def location_valid?(location)
|
249
|
+
x,y,z = closest_coordinates(location)
|
250
|
+
|
251
|
+
return (x - location.x).round_to(4) == 0 &&
|
252
|
+
(y - location.y).round_to(4) == 0 &&
|
253
|
+
(z - location.z).round_to(4) == 0
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
end # module Models
|
259
|
+
end # module Motel
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# The Linear MovementStrategy model definition
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# See COPYING for the License of this software
|
5
|
+
|
6
|
+
require 'motel/common'
|
7
|
+
require 'motel/models/movement_strategy'
|
8
|
+
|
9
|
+
module Motel
|
10
|
+
module Models
|
11
|
+
|
12
|
+
# The Linear MovementStrategy moves a location
|
13
|
+
# in a linear manner as defined by a
|
14
|
+
# unit direction vector and a floating point
|
15
|
+
# speed
|
16
|
+
class Linear < MovementStrategy
|
17
|
+
|
18
|
+
# Linear MovementStrategy must specify x,y,z components of
|
19
|
+
# a unit direction vector
|
20
|
+
validates_presence_of [:direction_vector_x,
|
21
|
+
:direction_vector_y,
|
22
|
+
:direction_vector_z]
|
23
|
+
|
24
|
+
# make sure the unit direction vector is normal
|
25
|
+
before_validation :normalize_direction_vector
|
26
|
+
def normalize_direction_vector
|
27
|
+
dx, dy, dz =
|
28
|
+
normalize(direction_vector_x, direction_vector_y, direction_vector_z)
|
29
|
+
self.direction_vector_x, self.direction_vector_y, self.direction_vector_z = dx, dy, dz
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# Linear MovementStrategy must specify the speed
|
34
|
+
# at which the location is moving
|
35
|
+
validates_presence_of :speed
|
36
|
+
validates_numericality_of :speed,
|
37
|
+
:greater_than_or_equal_to => 0
|
38
|
+
|
39
|
+
# Motel::Models::MovementStrategy::move
|
40
|
+
def move(location, elapsed_seconds)
|
41
|
+
unless valid?
|
42
|
+
Logger.warn "linear movement strategy not valid, not proceeding with move"
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
Logger.debug "moving location #{location.to_s} via linear movement strategy"
|
47
|
+
|
48
|
+
# calculate distance and update x,y,z accordingly
|
49
|
+
distance = speed * elapsed_seconds
|
50
|
+
|
51
|
+
location.x += distance * direction_vector_x
|
52
|
+
location.y += distance * direction_vector_y
|
53
|
+
location.z += distance * direction_vector_z
|
54
|
+
|
55
|
+
Logger.debug "moved location #{location} via linear movement strategy"
|
56
|
+
end
|
57
|
+
|
58
|
+
# convert non-nil linear movement strategy attributes to a hash
|
59
|
+
def to_h
|
60
|
+
result = {}
|
61
|
+
result[:speed] = speed unless speed.nil?
|
62
|
+
result[:direction_vector_x] = direction_vector_x unless direction_vector_x.nil?
|
63
|
+
result[:direction_vector_y] = direction_vector_y unless direction_vector_y.nil?
|
64
|
+
result[:direction_vector_z] = direction_vector_z unless direction_vector_z.nil?
|
65
|
+
return result
|
66
|
+
end
|
67
|
+
|
68
|
+
# convert linear movement strategy to a string
|
69
|
+
def to_s
|
70
|
+
super + "; speed: #{speed}; direction_vector_x:#{direction_vector_x}; " +
|
71
|
+
"direction_vector_y:#{direction_vector_y}; direction_vector_z:#{direction_vector_z}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end # module Models
|
76
|
+
end # module Motel
|