Top Level Namespace

Defined Under Namespace

Modules: Kamelopard, TelemetryProcessor Classes: Geocoder, YahooGeocoder

Instance Method Summary collapse

Instance Method Details

#balloonstyle(text, options = {}) ⇒ Object

Creates an BalloonStyle object.



480
481
482
# File 'lib/kamelopard/helpers.rb', line 480

def balloonstyle(text, options = {})
    Kamelopard::BalloonStyle.new text, options
end

#band(l, p) ⇒ Object

Returns an array of two values, equal to l +/- p%, defining a “band” around the central value l NB! p is interpreted as a percentage, not a fraction. IOW the result is divided by 100.0.



556
557
558
559
# File 'lib/kamelopard/helpers.rb', line 556

def band(l, p)
    f = l * p / 100.0
    [ l - f, l + f ]
end

#bounce(a, b, duration, points, options = {}) ⇒ Object

Generates a series of points in a path that will simulate Earth’s FlyTo in bounce mode, from one view to another. Note that the view objects must be the same time: either LookAt, or Camera – XXX Fix the limitation that the views must be the same type XXX Make the height of the bounce relate to the distance of the travel XXX Make the direction of change for elements that cycle smart enough to

choose the shortest direction around the circle

++



625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
# File 'lib/kamelopard/helpers.rb', line 625

def bounce(a, b, duration, points, options = {})
    raise "Arguments to bounce() must either be Camera or LookAt objects, and must be the same type" unless
        ((a.kind_of? Kamelopard::Camera and b.kind_of? Kamelopard::Camera) or
         (a.kind_of? Kamelopard::LookAt and b.kind_of? Kamelopard::LookAt))
    # The idea here is just to generate a function; the hard bit is finding
    # control points.
    include Kamelopard
    include Kamelopard::Functions

    max_alt = a.altitude
    max_alt = b.altitude if b.altitude > max_alt

    opts = {
        :latitude => Line.interpolate(a.latitude, b.latitude),
        :longitude => Line.interpolate(a.longitude, b.longitude),
        :heading => Line.interpolate(a.heading, b.heading),
        :tilt => Line.interpolate(a.tilt, b.tilt),
            # XXX This doesn't really work. An actual altitude requires a
            # value, and a mode, and we ignore the modes because there's no
            # way for us to figure out absolute altitudes given, say,
            # :relativeToGround
        :altitude => Quadratic.interpolate(a.altitude, b.altitude, 0.3 / 1.6, 1.3 * (b.altitude - a.altitude).abs),
#            def self.interpolate(ymin, ymax, x1, y1, min = -1.0, max = 1.0)
        :altitudeMode => a.altitudeMode,
        :duration => duration * 1.0 / points,
    }
    opts[:no_flyto] = 1 if options.has_key?(:no_flyto)
    opts[:show_placemarks] = 1 if options.has_key?(:show_placemarks)

    if a.kind_of? Camera then
        opts[:roll] = Line.interpolate(a.roll, b.roll)
    else
        opts[:range] = Line.interpolate(a.range, b.range)
    end
    return make_function_path(points, opts)
end

#camera(point = nil, options = {}) ⇒ Object

Creates a Camera object focused on the given point



495
496
497
# File 'lib/kamelopard/helpers.rb', line 495

def camera(point = nil, options = {})
    Kamelopard::Camera.new point, options
end

#cdata(text) ⇒ Object

Creates a CDATA XML::Node. This is useful for, among other things, ExtendedData values



550
551
552
# File 'lib/kamelopard/helpers.rb', line 550

def cdata(text)
    XML::Node.new_cdata text.to_s
end

#circ_bounds(v, max, min) ⇒ Object

Ensures v is within the range [min, max]. Modifies v to be within that range, assuming the number line is circular (as with latitude or longitude)



564
565
566
567
568
569
570
571
572
573
574
575
576
# File 'lib/kamelopard/helpers.rb', line 564

def circ_bounds(v, max, min)
    w = max - min
    if v > max then
        while (v > max) do
            v = v - w
        end
    elsif v < min then
        while (v < min) do
            v = v + w
        end
    end
    v
end

#do_action(cmd, options = {}) ⇒ Object

Adds a VSRAction object (a viewsyncrelay action) to the document, for viewsyncrelay configuration



597
598
599
# File 'lib/kamelopard/helpers.rb', line 597

def do_action(cmd, options = {})
  # XXX Finish this
end

#each_placemark(d) ⇒ Object

Pulls the Placemarks from the KML document d and yields each in turn to the caller k = an XML::Document containing KML



506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
# File 'lib/kamelopard/helpers.rb', line 506

def each_placemark(d)
    i = 0
    d.find('//kml:Placemark').each do |p|
        all_values = {}

        # These fields are part of the abstractview
        view_fields = %w{ latitude longitude heading range tilt roll altitude altitudeMode gx:altitudeMode }
        # These are other field I'm interested in
        other_fields = %w{ description name }
        all_fields = view_fields.clone
        all_fields.concat(other_fields.clone)
        all_fields.each do |k|
            if k == 'gx:altitudeMode' then
                ix = k
                next unless p.find_first('kml:altitudeMode').nil?
            else
                ix = "kml:#{k}"
            end
            r = k == "gx:altitudeMode" ? :altitudeMode : k.to_sym 
            tmp = p.find_first("descendant::#{ix}")
            next if tmp.nil?
            all_values[k == "gx:altitudeMode" ? :altitudeMode : k.to_sym ] = tmp.content
        end
        view_values = {}
        view_fields.each do |v| view_values[v.to_sym] = all_values[v.to_sym].clone if all_values.has_key? v.to_sym end
        yield make_view_from(view_values), all_values
    end
end

#fade_balloon_for(obj, value, options = {}) ⇒ Object

Fades a placemark’s popup balloon in or out. Takes as arguments the placemark object, 0 or 1 to hide or show the balloon, respectively, and a has of options to be passed to the AnimatedUpdate object created by this function. In order to have the balloon fade over some noticeable time, at minimum the :duration attribute in this hash should be set to some meaningful number of seconds.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/kamelopard/helpers.rb', line 63

def fade_balloon_for(obj, value, options = {})
    au = Kamelopard::AnimatedUpdate.new [], options
    if ! obj.is_a? Kamelopard::Placemark then
        raise "Can't show balloons for things that aren't placemarks"
    end
    a = XML::Node.new 'Change'
    b = XML::Node.new 'Placemark'
    b.attributes['targetId'] = obj.kml_id
    c = XML::Node.new 'color'
    c << XML::Node.new_text(value.to_s)
    b << c
    a << b
    au << a
end

#fade_in_balloon_for(p, options = {}) ⇒ Object

Refer to fade_balloon_for. This function only fades the balloon in.



84
85
86
# File 'lib/kamelopard/helpers.rb', line 84

def fade_in_balloon_for(p, options = {})
    fade_balloon_for(p, 'ffffffff', options)
end

#fade_out_balloon_for(obj, options = {}) ⇒ Object

Refer to fade_balloon_for. This function only fades the balloon out.



79
80
81
# File 'lib/kamelopard/helpers.rb', line 79

def fade_out_balloon_for(obj, options = {})
    fade_balloon_for(obj, '00ffffff', options)
end

#fade_overlay(ov, show, options = {}) ⇒ Object

Fades a screen overlay in or out. The show argument is boolean; true to show the overlay, or false to hide it. The fade will happen smoothly (as opposed to immediately) if the options hash includes a :duration element set to some positive number of seconds.



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/kamelopard/helpers.rb', line 243

def fade_overlay(ov, show, options = {})
    color = '00ffffff'
    color = 'ffffffff' if show
    if ov.is_a? String then
        id = ov  
    else
        id = ov.kml_id
    end

    a = XML::Node.new 'Change'
    b = XML::Node.new 'ScreenOverlay'
    b.attributes['targetId'] = id
    c = XML::Node.new 'color'
    c << XML::Node.new_text(color)
    b << c
    a << b
    k = Kamelopard::AnimatedUpdate.new [a], options 
end

#fly_to(view = nil, options = {}) ⇒ Object

Creates a FlyTo object flying to the given AbstractView



500
501
502
# File 'lib/kamelopard/helpers.rb', line 500

def fly_to(view = nil, options = {})
    Kamelopard::FlyTo.new view, options
end

#folder(name) ⇒ Object

Creates a new Folder with the current name



134
135
136
# File 'lib/kamelopard/helpers.rb', line 134

def folder(name)
    Kamelopard::Folder.new(name)
end

#get_actionsObject

Returns the Document’s VSRActions as a YAML string, suitable for writing to a viewsyncrelay configuration file



603
604
605
# File 'lib/kamelopard/helpers.rb', line 603

def get_actions
  get_document.get_actions_yaml
end

#get_doc_holderObject



612
613
614
# File 'lib/kamelopard/helpers.rb', line 612

def get_doc_holder
  return Kamelopard::DocumentHolder.instance
end

#get_documentObject

Returns the current Document object



7
8
9
# File 'lib/kamelopard/helpers.rb', line 7

def get_document()
    Kamelopard::DocumentHolder.instance.current_document
end

#get_folderObject

Returns the current Folder object



127
128
129
130
131
# File 'lib/kamelopard/helpers.rb', line 127

def get_folder()
    f = Kamelopard::DocumentHolder.instance.current_document.folders.last
    Kamelopard::Folder.new() if f.nil?
    Kamelopard::DocumentHolder.instance.current_document.folders.last
end

#get_kmlObject

Returns the KML that makes up the current Kamelopard::Document



102
103
104
# File 'lib/kamelopard/helpers.rb', line 102

def get_kml
    Kamelopard::DocumentHolder.instance.current_document.get_kml_document
end

#get_kml_stringObject

Returns the KML that makes up the current Document, as a string



107
108
109
# File 'lib/kamelopard/helpers.rb', line 107

def get_kml_string
    get_kml.to_s
end

#get_tourObject

Returns the current Tour object



117
118
119
# File 'lib/kamelopard/helpers.rb', line 117

def get_tour()
    Kamelopard::DocumentHolder.instance.current_document.tour
end

#hide_balloon_for(obj, options = {}) ⇒ Object

Hides the popup balloon for a Placemark or ScreenOverlay object. Require the object as the first argument, and takes a hash of options passed to the AnimatedUpdate object this functino creates. See also show_balloon_for and toggle_balloon_for



45
46
47
# File 'lib/kamelopard/helpers.rb', line 45

def hide_balloon_for(obj, options = {})
    toggle_balloon_for(obj, 0, options)
end

#iconstyle(href = nil, options = {}) ⇒ Object

Creates an IconStyle object.



470
471
472
# File 'lib/kamelopard/helpers.rb', line 470

def iconstyle(href = nil, options = {})
    Kamelopard::IconStyle.new href, options
end

#labelstyle(scale = 1, options = {}) ⇒ Object

Creates an LabelStyle object.



475
476
477
# File 'lib/kamelopard/helpers.rb', line 475

def labelstyle(scale = 1, options = {})
    Kamelopard::LabelStyle.new scale, options
end

#lat_check(l) ⇒ Object

These functions ensure the given value is within appropriate bounds for a latitude or longitude. Modifies it as necessary if it’s not.



580
581
582
# File 'lib/kamelopard/helpers.rb', line 580

def lat_check(l)
    circ_bounds(l * 1.0, 90.0, -90.0)
end

#long_check(l) ⇒ Object

See lat_check()



585
586
587
# File 'lib/kamelopard/helpers.rb', line 585

def long_check(l)
    circ_bounds(l * 1.0, 180.0, -180.0)
end

#look_at(point = nil, options = {}) ⇒ Object

Creates a LookAt object focused on the given point



490
491
492
# File 'lib/kamelopard/helpers.rb', line 490

def look_at(point = nil, options = {})
    Kamelopard::LookAt.new point, options
end

#make_tour_index(erb = nil, options = {}) ⇒ Object

Makes an HTML tour index, linked to a one-pixel screen overlay. The HTML contains links to start each tour.



537
538
539
# File 'lib/kamelopard/helpers.rb', line 537

def make_tour_index(erb = nil, options = {})
    get_document.make_tour_index(erb, options)
end

#make_view_from(options = {}) ⇒ Object

Given a hash of values, this creates an AbstractView object. Possible values in the hash are :latitude, :longitude, :altitude, :altitudeMode, :tilt, :heading, :roll, :range, :begin, :end, and :when. If the hash specifies :roll, a Camera object will result; otherwise, a LookAt object will result. Specifying both :roll and :range will still result in a Camera object, and the :range option will be ignored.

:begin, :end, and :when are used to create the view’s timestamp or timespan

:roll, :range, and the timestamp / timespan options have no default; all other values default to 0 except :altitudeMode, which defaults to :relativeToGround.



414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
# File 'lib/kamelopard/helpers.rb', line 414

def make_view_from(options = {})
    o = {}
    o.merge! options
    options.each do |k, v|
        o[k.to_sym] = v unless k.kind_of? Symbol
    end

    # Set defaults
    [
        [ :altitude, 0 ],
        [ :altitudeMode, :relativeToGround ],
        [ :latitude, 0 ],
        [ :longitude, 0 ],
        [ :tilt, 0 ],
        [ :heading, 0 ],
        [ :extrude, 0 ],
    ].each do |a|
        o[a[0]] = a[1] unless o.has_key? a[0]
    end

    p = point o[:longitude], o[:latitude], o[:altitude], o[:altitudeMode], o[:extrude]

    if o.has_key? :roll then
        view = Kamelopard::Camera.new p
    else
        view = Kamelopard::LookAt.new p
    end

    if o.has_key? :when then
        o[:timestamp] = Kamelopard::TimeStamp.new(o[:when])
    elsif o.has_key? :begin or o.has_key? :end then
        (b, e) = [nil, nil]
        b = o[:begin] if o.has_key? :begin
        e = o[:end] if o.has_key? :end
        o[:timespan] = Kamelopard::TimeSpan.new(b, e)
    end

    [ :altitudeMode, :tilt, :heading, :timespan, :timestamp, :range, :roll, :viewerOptions ].each do |a|
        #p o[a] if o.has_key? a and a == :timestamp
        view.method("#{a.to_s}=").call(o[a]) if o.has_key? a
    end

    view
end

#name_document(name) ⇒ Object

Names (or renames) the current Document object, and returns it



145
146
147
148
# File 'lib/kamelopard/helpers.rb', line 145

def name_document(name)
    Kamelopard::DocumentHolder.instance.current_document.name = name
    return Kamelopard::DocumentHolder.instance.current_document
end

#name_folder(name) ⇒ Object

Names (or renames) the current Folder, and returns it



139
140
141
142
# File 'lib/kamelopard/helpers.rb', line 139

def name_folder(name)
    Kamelopard::DocumentHolder.instance.current_document.folder.name = name
    return Kamelopard::DocumentHolder.instance.current_document.folder
end

#name_tour(name) ⇒ Object

Sets a name for the current Tour



122
123
124
# File 'lib/kamelopard/helpers.rb', line 122

def name_tour(name)
    Kamelopard::DocumentHolder.instance.current_document.tour.name = name
end

#orbit(center, range = 100, tilt = 0, startHeading = 0, endHeading = 360) ⇒ Object

Creates a list of FlyTo elements to orbit and look at a given point (center), at a given range (in meters), starting and ending at given angles (in degrees) from the center, where 0 and 360 (and -360, and 720, and -980, etc.) are north. To orbit clockwise, make startHeading less than endHeading. Otherwise, it will orbit counter-clockwise. To orbit multiple times, add or subtract 360 from the endHeading. The tilt argument matches the KML LookAt tilt argument



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/kamelopard/helpers.rb', line 164

def orbit(center, range = 100, tilt = 0, startHeading = 0, endHeading = 360)
    fly_to Kamelopard::LookAt.new(center, startHeading, tilt, range), 2, nil

    # We want at least 5 points (arbitrarily chosen value), plus at least 5 for
    # each full revolution

    # When I tried this all in one step, ruby told me 360 / 10 = 1805. I'm sure
    # there's some reason why this is a feature and not a bug, but I'd rather
    # not look it up right now.
    num = (endHeading - startHeading).abs
    den = ((endHeading - startHeading) / 360.0).to_i.abs * 5 + 5
    step = num / den
    step = 1 if step < 1
    step = step * -1 if startHeading > endHeading

    lastval = startHeading
    startHeading.step(endHeading, step) do |theta|
        lastval = theta
        fly_to Kamelopard::LookAt.new(center, theta, tilt, range), 2, nil, 'smooth'
    end
    if lastval != endHeading then
        fly_to Kamelopard::LookAt.new(center, endHeading, tilt, range), 2, nil, 'smooth'
    end
end

#pause(p) ⇒ Object

Inserts a KML gx:Wait element



112
113
114
# File 'lib/kamelopard/helpers.rb', line 112

def pause(p)
    Kamelopard::Wait.new p
end

#placemark(name = nil, options = {}) ⇒ Object

Creates a Placemark with the given name. Other Placemark attributes are set in the options hash.



97
98
99
# File 'lib/kamelopard/helpers.rb', line 97

def placemark(name = nil, options = {})
    Kamelopard::Placemark.new name, options
end

#point(lo, la, alt = 0, mode = nil, extrude = false) ⇒ Object

Creates a Point object. Arguments are latitude, longitude, altitude, altitude mode, and extrude



90
91
92
93
# File 'lib/kamelopard/helpers.rb', line 90

def point(lo, la, alt=0, mode=nil, extrude = false)
    m = ( mode.nil? ? :clampToGround : mode )
    Kamelopard::Point.new(lo, la, alt, :altitudeMode => m, :extrude => extrude)
end

#screenoverlay(options = {}) ⇒ Object

Creates a ScreenOverlay object



460
461
462
# File 'lib/kamelopard/helpers.rb', line 460

def screenoverlay(options = {})
    Kamelopard::ScreenOverlay.new options
end

#set_flyto_mode_to(mode) ⇒ Object

Changes the default FlyTo mode. Possible values are :smooth and :bounce



12
13
14
# File 'lib/kamelopard/helpers.rb', line 12

def set_flyto_mode_to(mode)
    Kamelopard::DocumentHolder.instance.current_document.flyto_mode = mode
end

#set_prefix_to(a) ⇒ Object

Sets a prefix for all kml_id objects. Note that this does not change previously created objects’ kml_ids… just new kml_ids going forward.



221
222
223
# File 'lib/kamelopard/helpers.rb', line 221

def set_prefix_to(a)
    Kamelopard.id_prefix = a
end

#show_balloon_for(obj, options = {}) ⇒ Object

Displays the popup balloon for a Placemark or ScreenOverlay object. Require the object as the first argument, and takes a hash of options passed to the AnimatedUpdate object this functino creates. See also show_balloon_for and toggle_balloon_for



53
54
55
# File 'lib/kamelopard/helpers.rb', line 53

def show_balloon_for(obj, options = {})
    toggle_balloon_for(obj, 1, options)
end

#show_hide_balloon(p, wait, options = {}) ⇒ Object

Superceded by toggle_balloon_for, but retained for backward compatibility



542
543
544
545
546
# File 'lib/kamelopard/helpers.rb', line 542

def show_hide_balloon(p, wait, options = {})
    show_balloon_for p, options
    pause wait
    hide_balloon_for p, options
end

#sound_cue(href, ds = nil) ⇒ Object

Adds a SoundCue object.



190
191
192
# File 'lib/kamelopard/helpers.rb', line 190

def sound_cue(href, ds = nil)
    Kamelopard::SoundCue.new href, ds
end

#style(options = {}) ⇒ Object

Creates an Style object.



485
486
487
# File 'lib/kamelopard/helpers.rb', line 485

def style(options = {})
    Kamelopard::Style.new options
end

#to_constraint(arr) ⇒ Object

Turns an array of two values (min, max) into a string suitable for use as a viewsyncrelay constraint



591
592
593
# File 'lib/kamelopard/helpers.rb', line 591

def to_constraint(arr)
  "[#{arr[0]}, #{arr[1]}]"
end

#toggle_balloon_for(obj, value, options = {}) ⇒ Object

Shows or hides the popup balloon for Placemark and ScreenOverlay objects. Arguments are the object; 0 or 1 to hide or show the balloon, respectively; and a hash of options to be added to the AnimatedUpdate object this function creates. Refer to the AnimatedUpdate documentation for details on possible options.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/kamelopard/helpers.rb', line 21

def toggle_balloon_for(obj, value, options = {})
    au = Kamelopard::AnimatedUpdate.new [], options
    if ! obj.kind_of? Kamelopard::Placemark and ! obj.kind_of? Kamelopard::ScreenOverlay then
        raise "Can't show balloons for things that aren't Placemarks or ScreenOverlays"
    end
    a = XML::Node.new 'Change'
    # XXX This can probably be more robust, based on just the class's name
    if obj.kind_of? Kamelopard::Placemark then
        b = XML::Node.new 'Placemark'
    else
        b = XML::Node.new 'ScreenOverlay'
    end
    b.attributes['targetId'] = obj.kml_id
    c = XML::Node.new 'gx:balloonVisibility'
    c << XML::Node.new_text(value.to_s)
    b << c
    a << b
    au << a
end

#tour_from_points(points, options = {}) ⇒ Object

Creates a tour from a series of points, using TelemetryProcessor::add_flyto.

The first argument is an ordered array of points, where each point is represented as an array of longitude, latitude, and altitude (in meters), in that order. The only options currently recognized are :pause and :exaggerate. :pause controls the flight speed by specifying the duration of each FlyTo element. Its default is 1 second. There is currently no mechanism for having anything other than constant durations between points. :exaggerate is an numeric value that defaults to 1; when set to larger values, it will exaggerate tilt and roll values, because they’re sort of boring at normal scale.



391
392
393
394
395
396
397
398
399
400
# File 'lib/kamelopard/helpers.rb', line 391

def tour_from_points(points, options = {})
    options.merge!({
        :pause => 1,
        :exaggerate => 1
    }) { |key, old, new| old }
    TelemetryProcessor.options = options
    (0..(points.size-3)).each do |i|
        TelemetryProcessor::add_flyto points[i,3]
    end
end

#write_actions_to(filename = 'actions.yml') ⇒ Object

Writes actions to a viewsyncrelay config file



608
609
610
# File 'lib/kamelopard/helpers.rb', line 608

def write_actions_to(filename = 'actions.yml')
  File.open(filename, 'w') do |f| f.write get_actions end
end

#write_kml_to(file = 'doc.kml', actions_file = 'actions.yml') ⇒ Object

Writes KML output (and if applicable, viewsyncrelay configuration) to files. Include a file name for the actions_file argument to get viewsyncrelay configuration output as well. Note that this configuration includes only the actions section; users are responsible for creating appropriate linkages, inputs and outputs, and transformations, on their own, presumably in a separate file.



231
232
233
234
235
236
237
# File 'lib/kamelopard/helpers.rb', line 231

def write_kml_to(file = 'doc.kml', actions_file = 'actions.yml')
    File.open(file, 'w') do |f| f.write get_kml.to_s end
    if (get_document.vsr_actions.size > 0) then
        File.open(actions_file, 'w') do |f| f.write get_document.get_actions end
    end
    #File.open(file, 'w') do |f| f.write get_kml.to_s.gsub(/balloonVis/, 'gx:balloonVis') end
end

#xy(x = 0.5, y = 0.5, xt = :fraction, yt = :fraction) ⇒ Object

Creates an XY object, for use when building Overlay objects



465
466
467
# File 'lib/kamelopard/helpers.rb', line 465

def xy(x = 0.5, y = 0.5, xt = :fraction, yt = :fraction)
    Kamelopard::XY.new x, y, xt, yt
end

#zoom_out(dist = 1000, dur = 0, mode = nil) ⇒ Object



150
151
152
153
154
155
# File 'lib/kamelopard/helpers.rb', line 150

def zoom_out(dist = 1000, dur = 0, mode = nil)
    l = Kamelopard::DocumentHolder.instance.current_document.tour.last_abs_view
    raise "No current position to zoom out from\n" if l.nil?
    l.range += dist
    Kamelopard::FlyTo.new(l, nil, dur, mode)
end