kamelopard 0.0.10 → 0.0.11

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.
@@ -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: []