kamelopard 0.0.10 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
1
  require 'kamelopard/classes'
2
- require 'kamelopard/functions'
2
+ require 'kamelopard/helpers'
3
3
  require 'kamelopard/geocode'
4
+ require 'kamelopard/function'
5
+ require 'kamelopard/function_paths'
@@ -1,13 +1,14 @@
1
1
  # encoding: utf-8
2
+ #--
2
3
  # vim:ts=4:sw=4:et:smartindent:nowrap
3
-
4
+ #++
4
5
  # Classes to manage various KML objects. See
5
6
  # http://code.google.com/apis/kml/documentation/kmlreference.html for a
6
7
  # description of KML
7
8
 
9
+ # Pretty much everything important is in this module
8
10
  module Kamelopard
9
11
  require 'singleton'
10
- require 'kamelopard/pointlist'
11
12
  require 'xml'
12
13
  require 'yaml'
13
14
  require 'erb'
@@ -16,6 +17,14 @@ module Kamelopard
16
17
  @@sequence = 0
17
18
  @@id_prefix = ''
18
19
  @@logger = nil
20
+
21
+ # Valid log levels:
22
+ # * :debug
23
+ # * :info
24
+ # * :notice
25
+ # * :warn
26
+ # * :error
27
+ # * :fatal
19
28
  LogLevels = {
20
29
  :debug => 0,
21
30
  :info => 1,
@@ -29,35 +38,37 @@ module Kamelopard
29
38
 
30
39
  # Sets a logging callback function. This function should expect three
31
40
  # arguments. The first will be a log level (:debug, :info, :notice, :warn,
32
- # :error, or :fatal); the second will be a module, categorizing the log
33
- # entries generally; and the third will be the message
41
+ # :error, or :fatal); the second will be a text string, categorizing the
42
+ # log entries generally; and the third will be the log message itself
34
43
  def Kamelopard.set_logger(l)
35
44
  @@logger = l
36
45
  end
37
46
 
47
+ # Sets the current logging level. Valid levels are defined in the LogLevels hash
38
48
  def Kamelopard.set_log_level(lev)
39
49
  raise "Unknown log level #{lev}" unless LogLevels.has_key? lev
40
50
  @@log_level = LogLevels[lev]
41
51
  end
42
52
 
53
+ # Logs a message, provided a log level, a text string, and the log message.
54
+ # See #Kamelopard.set_logger for details.
43
55
  def Kamelopard.log(level, mod, msg)
44
56
  raise "Unknown log level #{level} for error message #{msg}" unless LogLevels.has_key? level
45
57
  @@logger.call(level, mod, msg) unless @@logger.nil? or @@log_level > LogLevels[level]
46
58
  end
47
59
 
48
- def Kamelopard.get_document
49
- DocumentHolder.instance.current_document
50
- end
51
-
52
- def Kamelopard.get_next_id # :nodoc
60
+ def Kamelopard.get_next_id # :nodoc:
53
61
  @@sequence += 1
54
62
  @@sequence
55
63
  end
56
64
 
65
+ # Sets a prefix for all kml_id values generated from this time forth. Does
66
+ # not change previously generated kml_ids
57
67
  def Kamelopard.id_prefix=(a)
58
68
  @@id_prefix = a
59
69
  end
60
70
 
71
+ # Returns the current kml_id prefix value. See #Kamelopard.id_prefix=
61
72
  def Kamelopard.id_prefix
62
73
  @@id_prefix
63
74
  end
@@ -73,7 +84,7 @@ module Kamelopard
73
84
  # * if the second element is a proc, call the proc, passing it the KML
74
85
  # object, and let the Proc (presumably) add itself to the KML
75
86
  #++
76
- def Kamelopard.kml_array(e, m) # :nodoc
87
+ def Kamelopard.kml_array(e, m) # :nodoc:
77
88
  m.map do |a|
78
89
  if ! a[0].nil? then
79
90
  if a[1].kind_of? Proc then
@@ -94,7 +105,7 @@ module Kamelopard
94
105
  #--
95
106
  # Accepts XdX'X.X", XDXmX.XXs, XdXmX.XXs, or X.XXXX with either +/- or N/E/S/W
96
107
  #++
97
- def Kamelopard.convert_coord(a) # :nodoc
108
+ def Kamelopard.convert_coord(a) # :nodoc:
98
109
  a = a.to_s.upcase.strip.gsub(/\s+/, '')
99
110
 
100
111
  mult = 1
@@ -136,7 +147,7 @@ module Kamelopard
136
147
  end
137
148
 
138
149
  # Helper function for altitudeMode / gx:altitudeMode elements
139
- def Kamelopard.add_altitudeMode(mode, e)
150
+ def Kamelopard.add_altitudeMode(mode, e) # :nodoc:
140
151
  return if mode.nil?
141
152
  if mode == :clampToGround or mode == :relativeToGround or mode == :absolute then
142
153
  t = XML::Node.new 'altitudeMode'
@@ -182,6 +193,14 @@ module Kamelopard
182
193
  end
183
194
  end
184
195
 
196
+ def change(attributes, values)
197
+ change = XML::Node.new 'Change'
198
+ child = XML::Node.new self.class.name
199
+ child.attributes[:targetId] = @kml_id
200
+ change << child
201
+ return change
202
+ end
203
+
185
204
  # If this is a master-only object, this function gets called internally
186
205
  # in place of the object's original to_kml method
187
206
  def _alternate_to_kml(*a)
@@ -297,7 +316,8 @@ module Kamelopard
297
316
  end
298
317
 
299
318
  def to_s
300
- "Point (#{@longitude}, #{@latitude}, #{@altitude}, mode = #{@altitudeMode}, #{ @extrude ? 'extruded' : 'not extruded' })"
319
+ p @extrude
320
+ "Point (#{@longitude}, #{@latitude}, #{@altitude}, mode = #{@altitudeMode}, #{ @extrude == 1 ? 'extruded' : 'not extruded' })"
301
321
  end
302
322
 
303
323
  def to_kml(elem = nil, short = false)
@@ -496,7 +516,7 @@ module Kamelopard
496
516
  else
497
517
  a = point
498
518
  end
499
- @point = Point.new a.longitude, a.latitude, a.altitude, :altitudeMode => a.altitudeMode
519
+ @point = Point.new a.longitude, a.latitude, a.altitude, :altitudeMode => a.altitudeMode, :extrude => a.extrude
500
520
  end
501
521
  end
502
522
 
@@ -568,6 +588,10 @@ module Kamelopard
568
588
  t
569
589
  end
570
590
 
591
+ def to_queries_txt(name = '', planet = 'earth')
592
+ return "#{planet}@#{name}@flytoview=" + self.to_kml.to_s.gsub(/^\s+/, '').gsub("\n", '')
593
+ end
594
+
571
595
  def [](a)
572
596
  return @viewerOptions[a]
573
597
  end
@@ -951,7 +975,7 @@ module Kamelopard
951
975
  end
952
976
  end
953
977
 
954
- def get_stack_trace # :nodoc
978
+ def get_stack_trace # :nodoc:
955
979
  k = ''
956
980
  caller.each do |a| k << "#{a}\n" end
957
981
  k
@@ -1131,6 +1155,11 @@ module Kamelopard
1131
1155
  @document_index += 1
1132
1156
  end
1133
1157
 
1158
+ def set_current(a)
1159
+ raise "Must set current document to an existing Document object" unless a.kind_of? Document
1160
+ @document_index = @documents.index(a)
1161
+ end
1162
+
1134
1163
  def [](a)
1135
1164
  return @documents[a]
1136
1165
  end
@@ -1601,7 +1630,7 @@ module Kamelopard
1601
1630
  attr_accessor :standalone
1602
1631
 
1603
1632
  def initialize(options = {})
1604
- DocumentHolder.instance.current_document.tour << self unless options[:standalone]
1633
+ DocumentHolder.instance.current_document.tour << self unless options.has_key?(:standalone)
1605
1634
  super
1606
1635
  end
1607
1636
  end
@@ -0,0 +1,163 @@
1
+ #--
2
+ # vim:ts=4:sw=4:et:smartindent:nowrap
3
+ #++
4
+ # Describes functions that can be calculated to create flight paths
5
+
6
+ require 'matrix'
7
+
8
+ #--
9
+ #++
10
+ module Kamelopard
11
+
12
+ # Classes to manage functions, which can be interpolated into flight paths
13
+ # and other things
14
+ module Functions
15
+
16
+ # Abstract class representing a one-dimensional function
17
+ class Function1D
18
+ # min and max describe the function's domain. Values passed to
19
+ # get_value will only range from 0 to 1; the actual value
20
+ # calculated will be mapped to a percentage of that domain.
21
+ attr_reader :min, :max, :start, :end
22
+
23
+ # Another function this one is composed with, or appended to the end of this one
24
+ attr_reader :compose, :append
25
+
26
+ attr_accessor :verbose
27
+
28
+ def initialize(min = 0, max = 1)
29
+ @min = min
30
+ @max = max
31
+ @verbose = false
32
+ end
33
+
34
+ def max=(m)
35
+ raise "Cannot have a nil domain maximum" if m.nil?
36
+ @max = m
37
+ end
38
+
39
+ def min=(m)
40
+ raise "Cannot have a nil domain minimum" if m.nil?
41
+ @min = m
42
+ end
43
+
44
+ def compose=(f)
45
+ raise "Can only compose another one-dimensional function" unless f.kind_of? Function1D or f.nil?
46
+ @compose = f
47
+ end
48
+
49
+ def get_value(x)
50
+ raise "Value #{x} must be between 0 and 1" if (x.to_f > 1 or x.to_f < 0)
51
+ val = x * (max - min) + min
52
+ if @compose.nil? then
53
+ return run_function(val)
54
+ else
55
+ return run_function(@compose.get_value(val))
56
+ end
57
+ end
58
+
59
+ #def append(f)
60
+ # raise "Can only append another one-dimensional function" unless f.kind_of? Function1D or f.nil?
61
+ # print STDERR "WARNING: append() isn't actually implemented" unless f.nil?
62
+ # # XXX
63
+ # # Gotta implement this. The idea is to have one function for the first
64
+ # # part of a domain, and another for the next. The domain of the second
65
+ # # function will begin with the end of the last function.
66
+ # # Perhaps allow two methods. One just appends the two; the second
67
+ # # smooths things somewhat by adding to the result of the second the
68
+ # # value of the first at the end of its domain.
69
+ # @append = f
70
+ #end
71
+
72
+ def run_function(x)
73
+ raise "Override this method before calling it, please"
74
+ end
75
+
76
+ def self.interpolate(a, b)
77
+ # Creates a new Function1D object between points A and B
78
+ raise "Override this method before calling it, please"
79
+ end
80
+ end
81
+
82
+ # Represents a cubic equation of the form c3 * x^3 + c2 * x^2 + c1 * x + c0
83
+ class Cubic < Function1D
84
+ attr_accessor :c0, :c1, :c2, :c3
85
+ def initialize(c3 = 1.0, c2 = 0.0, c1 = 0.0, c0 = 0.0, min = -1.0, max = 1.0)
86
+ @c3 = c3.to_f
87
+ @c2 = c2.to_f
88
+ @c1 = c1.to_f
89
+ @c0 = c0.to_f
90
+ super min, max
91
+ end
92
+
93
+ def run_function(x)
94
+ puts "#{self.class.name}: [#{@min}, #{@max}] (#{@c3}, #{@c2}, #{@c1}, #{@c0}): #{x} -> #{ @c3 * x * x * x + @c2 * x * x + @c1 * x + @c0 }" if @verbose
95
+ return @c3 * x ** 3 + @c2 * x ** 2 + @c1 * x + @c0
96
+ end
97
+
98
+ def self.interpolate(ymin, ymax, x1, y1, x2, y2, min = -1.0, max = 1.0)
99
+ xm = Matrix[[min ** 3, x1 ** 3, x2 ** 3, max ** 3], [min ** 2, x1 ** 2, x2 ** 2, max ** 2], [min, x1, x2, max], [1, 1, 1, 1]]
100
+ ym = Matrix[[ymin, y1, y2, ymax]]
101
+ m = ym * xm.inverse
102
+ c3 = m[0,0]
103
+ c2 = m[0,1]
104
+ c1 = m[0,2]
105
+ c0 = m[0,3]
106
+ return Cubic.new(c3, c2, c1, c0, min, max)
107
+ end
108
+ end
109
+
110
+ # Describes a quadratic equation
111
+ class Quadratic < Cubic
112
+ def initialize(c2 = 1.0, c1 = 0.0, c0 = 0.0, min = -1.0, max = 1.0)
113
+ super(0.0, c2, c1, c0, min, max)
114
+ end
115
+
116
+ def self.interpolate(ymin, ymax, x1, y1, min = -1.0, max = 1.0)
117
+ x1 = (max.to_f + min) / 2.0 if x1.nil?
118
+ y1 = (ymax.to_f + ymin) / 2.0 if y1.nil?
119
+ xm = Matrix[[min ** 2, x1 ** 2, max ** 2], [min, x1, max], [1, 1, 1]]
120
+ ym = Matrix[[ymin, y1, ymax]]
121
+ m = ym * xm.inverse
122
+ c2 = m[0,0]
123
+ c1 = m[0,1]
124
+ c0 = m[0,2]
125
+ return Quadratic.new(c2, c1, c0, min, max)
126
+ end
127
+ end
128
+
129
+ # Describes a line
130
+ class Line < Cubic
131
+ def initialize(c1 = 1.0, c0 = 0.0, min = 0.0, max = 1.0)
132
+ super(0.0, 0.0, c1, c0, min, max)
133
+ end
134
+
135
+ def self.interpolate(a, b)
136
+ return Line.new(b - a, a)
137
+ end
138
+ end
139
+
140
+ class Constant < Cubic
141
+ def initialize(c0 = 0.0, min = 0.0, max = 1.0)
142
+ super(0, 0, 0, c0, min, max)
143
+ end
144
+
145
+ # Interpolation isn't terribly useful for constants; to avoid using
146
+ # some superclass's interpolation accidentally, we'll just
147
+ # interpolate to the average of the two values
148
+ def self.interpolate(a, b)
149
+ return Constant.new((b.to_f - a.to_f) / 0.0)
150
+ end
151
+ end
152
+ end
153
+ end
154
+ # include Kamelopard::Functions
155
+ #
156
+ # l = Line.new 1.0, 0.0
157
+ # puts l.get_value(0.35)
158
+ #
159
+ # s = Quadratic.new
160
+ # puts s.get_value(0.4)
161
+ #
162
+ # l.compose = s
163
+ # puts l.get_value(0.35)
@@ -0,0 +1,96 @@
1
+ #--
2
+ # vim:ts=4:sw=4:et:smartindent:nowrap
3
+ #++
4
+ # Logic to create flyto paths from mathematical functions.
5
+
6
+ #--
7
+ #++
8
+ module Kamelopard
9
+
10
+ # This function creates a hash, then uses that hash to create a point in a
11
+ # tour, using make_view_from() among other things.
12
+ # Arguments:
13
+ # points: The number of points in the series
14
+ # hash: Values used to create the hash, which creates the point in the
15
+ # series. Keys in this hash include:
16
+ # latitude, longitude, altitude, heading, tilt, roll, range, duration, altitudeMode, extrude
17
+ # These can be constant numbers, Proc objects, or Function1D objects.
18
+ # The latter two will be called once for each point in the series.
19
+ # Proc objects will be passed the number of the point they're
20
+ # calculating, starting with 0, and the current value of the hash
21
+ # created for this point. "duration" represents the time in seconds
22
+ # spent flying from the last point to this one.
23
+ # callback
24
+ # This Proc object, if defined, will be called after the above hash
25
+ # keys have been calculated. It gets passed the number of the point,
26
+ # and the current value of the hash for this point. It can modify and
27
+ # return that hash as needed.
28
+ # callback_value
29
+ # A placeholder the callback function can use. It can set it when
30
+ # it's called one time, and see that value when called the next time.
31
+ # pause
32
+ # The amount of time to pause after flying to this point, or nil for no pause
33
+ # show_placemarks
34
+ # If set, a placemark object will be created at this point
35
+ # no_flyto
36
+ # If set, on flyto objects will be created
37
+
38
+ def make_function_path(points = 10, options = {})
39
+
40
+ def val(a, b, c) # :nodoc:
41
+ if a.kind_of? Function1D then
42
+ return a.get_value(c)
43
+ elsif a.kind_of? Proc then
44
+ return a.call(b, a)
45
+ else
46
+ return a
47
+ end
48
+ end
49
+
50
+ result_points = []
51
+
52
+ callback_value = nil
53
+ i = 0
54
+ while (i <= points)
55
+ p = i.to_f / points.to_f
56
+ hash = {
57
+ :latitude => val(options[:latitude], i, p),
58
+ :longitude => val(options[:longitude], i, p),
59
+ :altitude => val(options[:altitude], i, p),
60
+ :heading => val(options[:heading], i, p),
61
+ :tilt => val(options[:tilt], i, p),
62
+ :altitudeMode => val(options[:altitudeMode], i, p),
63
+ :extrude => val(options[:extrude], i, p),
64
+ }
65
+
66
+ hash[:show_placemarks] = options[:show_placemarks] if options.has_key? :show_placemarks
67
+ hash[:roll] = val(options[:roll], i, p) if options.has_key? :roll
68
+ hash[:range] = val(options[:range], i, p) if options.has_key? :range
69
+ hash[:pause] = val(options[:pause], i, p) if options.has_key? :pause
70
+
71
+ if hash.has_key? :duration
72
+ duration = val(options[:duration], i, p)
73
+ else
74
+ duration = (i == 0 ? 0 : 2)
75
+ end
76
+
77
+ hash[:callback_value] = callback_value unless callback_value.nil?
78
+ tmp = yield(i, hash)
79
+ hash = tmp unless tmp.nil?
80
+ #hash = options[:callback].call(i, hash) if options.has_key? :callback
81
+ callback_value = hash[:callback_value] if hash.has_key? :callback_value
82
+
83
+ v = make_view_from(hash)
84
+ p = point(v.longitude, v.latitude, v.altitude, hash[:altitudeMode], hash[:extrude])
85
+ get_folder << placemark(i.to_s, :geometry => p) if hash.has_key? :show_placemarks
86
+ fly_to v, :duration => duration , :mode => :smooth unless hash.has_key? :no_flyto
87
+ result_points << v
88
+
89
+ pause hash[:pause] if hash.has_key? :pause
90
+
91
+ i = i + 1
92
+ end
93
+ result_points
94
+ end
95
+
96
+ end
@@ -1,4 +1,7 @@
1
+ #--
1
2
  # vim:ts=4:sw=4:et:smartindent:nowrap
3
+ #++
4
+ # Various helper functions
2
5
 
3
6
  # Returns the current Document object
4
7
  def get_document()
@@ -122,6 +125,8 @@
122
125
 
123
126
  # Returns the current Folder object
124
127
  def get_folder()
128
+ f = Kamelopard::DocumentHolder.instance.current_document.folders.last
129
+ Kamelopard::Folder.new() if f.nil?
125
130
  Kamelopard::DocumentHolder.instance.current_document.folders.last
126
131
  end
127
132
 
@@ -415,11 +420,12 @@
415
420
  [ :longitude, 0 ],
416
421
  [ :tilt, 0 ],
417
422
  [ :heading, 0 ],
423
+ [ :extrude, 0 ],
418
424
  ].each do |a|
419
425
  o[a[0]] = a[1] unless o.has_key? a[0]
420
426
  end
421
427
 
422
- p = point o[:longitude], o[:latitude], o[:altitude], o[:altitudeMode]
428
+ p = point o[:longitude], o[:latitude], o[:altitude], o[:altitudeMode], o[:extrude]
423
429
 
424
430
  if o.has_key? :roll then
425
431
  view = Kamelopard::Camera.new p
@@ -1,51 +1,11 @@
1
1
  # vim:ts=4:sw=4:et:smartindent:nowrap
2
2
  require 'matrix'
3
- #require 'kamelopard_classes'
4
3
 
5
- # XXX Right now I'm changing this to handle one-dimensional lists of numbers,
6
- # that can be added together. We'll probably want a way to add points, or other
7
- # numeric sets, to a set of pointlists easily. So for instance we can have
8
- # lists for altitude, longitude, and latitude, and add a single point to them
9
- # in one easy command.
4
+ # Basic support for splines
10
5
 
11
6
  module Kamelopard
12
- class NumberList
13
- # Contains a list of numbers
14
-
15
- def initialize(init = [])
16
- raise "Constructor argument needs to be an array" unless init.kind_of? Array
17
- @points = init
18
- end
19
-
20
- def size
21
- return @points.size
22
- end
23
-
24
- def <<(a)
25
- @points << a
26
- end
27
-
28
- def last
29
- @points.last
30
- end
31
-
32
- def [](i)
33
- @points[i]
34
- end
35
-
36
- def each(&blk)
37
- @points.each(&blk)
38
- end
39
- end
40
-
41
- def Kamelopard.lists_at(lists, i)
42
- # The modulus ensures lists will repeat if they're not the same size
43
- lists.collect { |l| l[i % l.size] }
44
- end
45
-
46
- def Kamelopard.interpolate(lists = [], resolution = [10])
7
+ def spline(lists = [], resolution = [10])
47
8
  # Ruby implementation of Catmull-Rom splines (http://www.cubic.org/docs/hermite.htm)
48
- # Return NDPointList interpolating a path along all points in this list
49
9
 
50
10
  size = lists.collect { |l| l.size }.max
51
11
  STDERR.puts size
@@ -95,12 +55,3 @@ module Kamelopard
95
55
  result
96
56
  end
97
57
  end
98
-
99
- #a = Kamelopard::NumberList.new [1, 2, 3]
100
- #b = Kamelopard::NumberList.new [5, 6, 10]
101
- #
102
- #i = 0
103
- #Kamelopard.interpolate([a, b], [100]).each do |f|
104
- # i += 1
105
- # puts "#{i}\t#{f.inspect}"
106
- #end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kamelopard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.0.11
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-02-14 00:00:00.000000000 Z
13
+ date: 2013-04-15 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: Various classes and functions used to ease development of KML files,
16
16
  in particular for development of Google Earth tours
@@ -23,8 +23,10 @@ extra_rdoc_files: []
23
23
  files:
24
24
  - lib/kamelopard/geocode.rb
25
25
  - lib/kamelopard/classes.rb
26
- - lib/kamelopard/functions.rb
27
- - lib/kamelopard/pointlist.rb
26
+ - lib/kamelopard/helpers.rb
27
+ - lib/kamelopard/function_paths.rb
28
+ - lib/kamelopard/function.rb
29
+ - lib/kamelopard/spline.rb
28
30
  - lib/kamelopard.rb
29
31
  homepage: http://www.endpoint.com/services/liquid_galaxy
30
32
  licenses: []