Class: MSPhysics::Simulation

Inherits:
Entity
  • Object
show all
Defined in:
RubyExtension/MSPhysics/simulation.rb

Overview

Since:

  • 1.0.0

Constant Summary collapse

@@instance =

Since:

  • 1.0.0

nil

Instance Attribute Summary collapse

Simulation Control Functions collapse

Cursor Functions collapse

Mode and Debug Draw Functions collapse

Body/Group Functions collapse

Text Control Functions collapse

Viewport Drawing Functions collapse

Music, Sound, and MIDI Functions collapse

Particle Effects collapse

Advanced collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Entity

#inspect, #to_s

Constructor Details

#initialize(from_selection = false) ⇒ Simulation

Returns a new instance of Simulation.

Parameters:

  • from_selection (Boolean) (defaults to: false)

    Pass true to start simulation from selection.

Since:

  • 1.0.0



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'RubyExtension/MSPhysics/simulation.rb', line 61

def initialize(from_selection = false)
  @started_from_selection = from_selection ? true : false
  @selected_ents = []
  @world = nil
  @update_rate = MSPhysics::DEFAULT_SIMULATION_SETTINGS[:update_rate]
  @update_timestep = MSPhysics::DEFAULT_SIMULATION_SETTINGS[:update_timestep]
  @update_timestep_inv = 1.0 / @update_timestep
  @mode = 0
  @frame = 0
  @time_info = { :start => 0, :end => 0, :last => 0, :sim => 0, :total => 0 }
  @fps_info = { :fps => 0, :update_rate => 10, :last => 0, :change => 0 }
  @animation_enabled = true
  @animation_stopped = false
  @camera = { :original => {}, :follow => nil, :target => nil, :offset => nil }
  @cursor_id = MSPhysics::CURSORS[:hand]
  @original_cursor_id = @cursor_id
  @cursor_pos = Geom::Point3d.new(0,0,0)
  @interactive_note = "Interactive mode: Click and drag a physics body to move. Hold SHIFT while dragging to lift."
  @game_note = "Game mode: All control over bodies and camera via mouse is restricted as the mouse is reserved for gaming."
  @general_note = "PAUSE - toggle play  ESC - reset"
  @fullscreen_note = {
    :time => nil,
    :text => "Fullscreen Detected\nPress ESC to Reset Simulation",
    :font => "Verdana",
    :align => TextAlignCenter,
    :color => Sketchup::Color.new(255,255,255),
    :size => 12,
    :italic => false,
    :bold => false,
    :duration => 5,
    :background => Sketchup::Color.new(10,10,10, 200),
    :bgw => 300,
    :bgh => 80,
    :bvo => 20,
    :hratio => 0.5,
    :vratio => 0.05
  }
  @paused = false
  @pause_updated = false
  @suspended = false
  @mouse_over = true
  @menu_entered = false
  @menu_entered2 = false
  @ip = Sketchup::InputPoint.new
  @picked = {}
  @clicked = nil
  @error = nil
  @saved_transformations = {}
  @log_line = { :ent => nil, :mat => nil, :log => [], :limit => 20 }
  @display_note = { :ent => nil, :mat => nil }
  @fancy_note_defaults = {
    :font => "Ariel",
    :size => 11,
    :bold => false,
    :italic => false,
    :align => TextAlignLeft,
    :color => Sketchup::Color.new(255,255,255,255),
    :background => Sketchup::Color.new(0,140,255,220),
    :padding => 10,
    :hratio => 0.0,
    :vratio => 0.0,
    :duration => 5,
    :fade => 0.25,
    :twr => 0.6,
    :thr => 1.5
  }
  @fancy_note = nil
  @prev_fancy_note = nil
  @emitted_bodies = {}
  @created_entities = {}
  @bb = Geom::BoundingBox.new
  @draw_queue = []
  @points_queue = []
  @ccm = false
  @show_bodies = true
  @hidden_entities = []
  @timers_started = false
  @contact_points = {
    :show           => false,
    :point_size     => 3,
    :point_style    => 2,
    :point_color    => Sketchup::Color.new(109, 206, 255)
  }
  @contact_forces = {
    :show           => false,
    :line_width     => 1,
    :line_stipple   => '',
    :line_color     => Sketchup::Color.new(247, 40, 85)
  }
  @aabb = {
    :show           => false,
    :line_width     => 1,
    :line_stipple   => '',
    :line_color     => Sketchup::Color.new(68, 53, 165)
  }
  @collision_wireframe = {
    :show           => false,
    :line_width     => 1,
    :line_stipple   => '',
    :active         => Sketchup::Color.new(221, 38, 165),
    :sleeping       => Sketchup::Color.new(255, 255, 100),
    :show_edges     => nil,
    :show_profiles  => nil
  }
  @axes = {
    :show           => false,
    :line_width     => 2,
    :line_stipple   => '',
    :size           => 20,
    :xaxis          => Sketchup::Color.new(255, 0, 0),
    :yaxis          => Sketchup::Color.new(0, 255, 0),
    :zaxis          => Sketchup::Color.new(0, 0, 255)
  }
  @pick_and_drag = {
    :line_width     => 1,
    :line_stipple   => '_',
    :line_color     => Sketchup::Color.new(250, 10, 10),
    :point_width    => 2,
    :point_size     => 10,
    :point_style    => 4,
    :point_color    => Sketchup::Color.new(4, 4, 4),
    :vline_width    => 1,
    :vline_stipple1 => '',
    :vline_stipple2 => '-',
    :vline_color    => Sketchup::Color.new(0, 40, 255)
  }
  @controller_context = MSPhysics::ControllerContext.new
  @thrusters = {}
  @emitters = {}
  @buoyancy_planes = {}
  @controlled_joints = {}
  @scene_info = { :active => false, :data1 => nil, :data2 => nil, :transition_time => 0, :elasted_time => 0, :timer => nil }
  @scene_anim_info = { :state => 0, :data => {}, :transition_time => 0, :elasted_time => 0, :ref_time => 0, :tabs_size => nil, :active_tab => nil, :tab_dir => 1 }
  @cc_bodies = []
  @particles = []
  @particle_def2d = {}
  @particle_def3d = {}
  @dp_particle_instances = []
  @particles_visible = true
  @undo_on_reset = false
  @joystick_data = {}
  @joybutton_data = {}
  @joypad_data = 0
  @simulation_started = false
  @reset_positions_on_end = true
  @erase_instances_on_end = true
  @reset_camera_on_end = true
  @joint_layer_orig_visible = true
  @frame_change_observer_id = nil
  @key_nav_veloc = Geom::Vector3d.new(0,0,0)
  @key_nav_omega = Geom::Vector3d.new(0,0,0)
end

Instance Attribute Details

#fpsInteger (readonly)

Get simulation update rate in frames per second.

Returns:

  • (Integer)

Since:

  • 1.0.0



# File 'RubyExtension/MSPhysics/simulation.rb', line 222

#frameInteger (readonly)

Get simulation frame.

Returns:

  • (Integer)

Since:

  • 1.0.0



# File 'RubyExtension/MSPhysics/simulation.rb', line 218

#worldWorld (readonly)

Get simulation world.

Returns:

Since:

  • 1.0.0



# File 'RubyExtension/MSPhysics/simulation.rb', line 214

Class Method Details

.active?Boolean

Determine if simulation is running.

Returns:

  • (Boolean)

Since:

  • 1.0.0



16
17
18
# File 'RubyExtension/MSPhysics/simulation.rb', line 16

def active?
  @@instance ? true : false
end

.external_control(from_selection = false) ⇒ Simulation?

Create a Simulation instance for external frame-by-frame.

Parameters:

  • from_selection (Boolean) (defaults to: false)
    • Pass true to start simulation from selection and have all non-selected groups act stationary.

    • Pass false to start simulation will all bodies being considered.

Returns:

  • (Simulation, nil)

    A Simulation instance if successful.

Since:

  • 1.0.0



49
50
51
52
53
54
55
56
# File 'RubyExtension/MSPhysics/simulation.rb', line 49

def external_control(from_selection = false)
  return if @@instance
  MSPhysics::Replay.reset
  @@instance = self.new(from_selection)
  @@instance.configure_for_external_control
  Sketchup.active_model.select_tool(@@instance)
  @@instance
end

.instanceSimulation?

Get MSPhysics::Simulation instance.

Returns:

Since:

  • 1.0.0



10
11
12
# File 'RubyExtension/MSPhysics/simulation.rb', line 10

def instance
  @@instance
end

.resetBoolean

End simulation.

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



36
37
38
39
40
41
# File 'RubyExtension/MSPhysics/simulation.rb', line 36

def reset
  return false unless @@instance
  Sketchup.active_model.select_tool(nil)
  @@instance = nil
  true
end

.start(from_selection = false) ⇒ Boolean

Start simulation.

Parameters:

  • from_selection (Boolean) (defaults to: false)
    • Pass true to start simulation from selection and have all non-selected groups act stationary.

    • Pass false to start simulation will all bodies being considered.

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



26
27
28
29
30
31
32
# File 'RubyExtension/MSPhysics/simulation.rb', line 26

def start(from_selection = false)
  return false if @@instance
  MSPhysics::Replay.reset
  @@instance = self.new(from_selection)
  Sketchup.active_model.select_tool(@@instance)
  true
end

Instance Method Details

#aabb_visible=(state) ⇒ Object

Enable/disable the drawing of world axes aligned bounding box for all bodies.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



542
543
544
# File 'RubyExtension/MSPhysics/simulation.rb', line 542

def aabb_visible=(state)
  @aabb[:show] = state ? true : false
end

#aabb_visible?Boolean

Determine whether the drawing of world axes aligned bounding box, for all bodies, is enabled.

Returns:

  • (Boolean)

Since:

  • 1.0.0



549
550
551
# File 'RubyExtension/MSPhysics/simulation.rb', line 549

def aabb_visible?
  @aabb[:show]
end

#add_group(group) ⇒ Body

Add a group/component to simulation.

Parameters:

  • group (Sketchup::Group, Sketchup::ComponentInstance)

Returns:

Raises:

  • (TypeError)

    if the specified entity is already part of simulation.

  • (TypeError)

    if the entity doesn’t meet demands for being a valid physics body.

  • (MSPhysics::ScriptException)

    if there is an error in body script.

Since:

  • 1.0.0



766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
# File 'RubyExtension/MSPhysics/simulation.rb', line 766

def add_group(group)
  AMS.validate_type(group, Sketchup::Group, Sketchup::ComponentInstance)
  if find_body_by_group(group)
    raise(TypeError, "Entity #{group} is already part of simulation!", caller)
  end
  default = MSPhysics::DEFAULT_BODY_SETTINGS
  bdict = 'MSPhysics Body'
  btype = group.get_attribute(bdict, 'Type', default[:type]).to_i
  shape = group.get_attribute(bdict, 'Shape', default[:shape_id])
  if shape.is_a?(String)
    MSPhysics::SHAPES.each { |k, v|
      if shape == v
        shape = k
        break
      end
    }
    shape = default[:shape_id] if shape.is_a?(String)
  end
  shape_dir = group.get_attribute(bdict, 'Shape Dir', default[:shape_dir]).to_i
  if shape_dir == 1
    collision_transform = Geom::Transformation.new(Y_AXIS.reverse, X_AXIS, Z_AXIS, ORIGIN)
  elsif shape_dir == 2
    collision_transform = Geom::Transformation.new(Z_AXIS, Y_AXIS, X_AXIS.reverse, ORIGIN)
  else
    collision_transform = nil
  end
  body = MSPhysics::Body.new(@world, group, shape, collision_transform, btype)
  if group.get_attribute(bdict, 'Mass Control', default[:mass_control]).to_i == 2
    body.mass = group.get_attribute(bdict, 'Mass', default[:mass]).to_f
  else
    body.density = group.get_attribute(bdict, 'Density', default[:density]).to_f
  end
  body.static_friction = group.get_attribute(bdict, 'Static Friction', default[:static_friction]).to_f
  body.kinetic_friction = group.get_attribute(bdict, 'Kinetic Friction', default[:kinetic_friction]).to_f
  body.elasticity = group.get_attribute(bdict, 'Elasticity', default[:elasticity]).to_f
  body.softness = group.get_attribute(bdict, 'Softness', default[:softness]).to_f
  body.friction_enabled = group.get_attribute(bdict, 'Enable Friction', default[:enable_friction])
  body.magnet_mode = group.get_attribute(bdict, 'Magnet Mode', default[:magnet_mode])
  body.magnet_force = group.get_attribute(bdict, 'Magnet Force', default[:magnet_force]).to_f
  body.magnet_range = group.get_attribute(bdict, 'Magnet Range', default[:magnet_range]).to_f
  body.magnet_strength = group.get_attribute(bdict, 'Magnet Strength', default[:magnet_strength]).to_f
  body.static = group.get_attribute(bdict, 'Static', default[:static])
  body.frozen = group.get_attribute(bdict, 'Frozen', default[:frozen])
  body.magnetic = group.get_attribute(bdict, 'Magnetic', default[:magnetic])
  body.collidable = group.get_attribute(bdict, 'Collidable', default[:collidable])
  body.auto_sleep_enabled = group.get_attribute(bdict, 'Auto Sleep', default[:auto_sleep])
  body.continuous_collision_check_enabled = group.get_attribute(bdict, 'Continuous Collision', default[:continuous_collision])
  ld = group.get_attribute(bdict, 'Linear Damping', default[:linear_damping]).to_f
  body.set_linear_damping(ld,ld,ld)
  ad = group.get_attribute(bdict, 'Angular Damping', default[:angular_damping]).to_f
  body.set_angular_damping(ad,ad,ad)
  body.gravity_enabled = group.get_attribute(bdict, 'Enable Gravity', default[:enable_gravity])
  if group.get_attribute(bdict, 'Enable Script', default[:enable_script])
    script = group.get_attribute('MSPhysics Script', 'Value')
    begin
      body.context.eval_script(script, MSPhysics::SCRIPT_NAME, 1)
    rescue Exception => err
      ref = nil
      test = MSPhysics::SCRIPT_NAME + ':'
      err_message = err.message
      err_backtrace = err.backtrace
      unless AMS::IS_RUBY_VERSION_18
        err_message.force_encoding('UTF-8')
        err_backtrace.each { |i| i.force_encoding('UTF-8') }
      end
      err_backtrace.each { |location|
        if location.include?(test)
          ref = location
          break
        end
      }
      ref = err_message if !ref && err_message.include?(test)
      line = ref ? ref.split(test, 2)[1].split(/\:/, 2)[0].to_i : nil
      msg = "#{err.class.to_s[0] =~ /a|e|i|o|u/i ? 'An' : 'A'} #{err.class} has occurred while evaluating entity script#{line ? ', line ' + line.to_s : nil}:\n#{err_message}"
      raise MSPhysics::ScriptException.new(msg, err_backtrace, group, line)
    end if script.is_a?(String)
  end
  if group.get_attribute(bdict, 'Enable Thruster', default[:enable_thruster])
    controller = group.get_attribute(bdict, 'Thruster Controller')
    if controller.is_a?(String) && !controller.empty?
      @thrusters[body] = {
        :controller => controller,
        :lock_axes => group.get_attribute(bdict, 'Thruster Lock Axes', default[:thruster_lock_axes])
      }
    end
  end
  if group.get_attribute(bdict, 'Enable Emitter', default[:enable_emitter])
    controller = group.get_attribute(bdict, 'Emitter Controller')
    if controller.is_a?(String) && !controller.empty?
      @emitters[body] = {
        :controller => controller,
        :lock_axes => group.get_attribute(bdict, 'Emitter Lock Axes', default[:emitter_lock_axes]),
        :recoil => group.get_attribute(bdict, 'Emitter Recoil', default[:emitter_recoil]),
        :rate => AMS.clamp(group.get_attribute(bdict, 'Emitter Rate', default[:emitter_rate]), MSPhysics::EPSILON, nil),
        :lifetime => AMS.clamp(group.get_attribute(bdict, 'Emitter Lifetime', default[:emitter_lifetime]), 0.0, nil),
        :delay => AMS.clamp(group.get_attribute(bdict, 'Emitter Delay', default[:emitter_delay]), 0.0, nil),
        :flags => nil
      }
    end
  end
  @saved_transformations[group] = group.transformation
  body
end

#animate_scenes(state) ⇒ Boolean

Note:

Other settings like, delay and reverse mode, are acquired from the settings.

Stimulate automatic scene transitioning.

Parameters:

  • state (Integer)
    • 0 - off/stop

    • 1 - one way

    • 2 - repeat forth and back

    • 3 - loop around

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



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
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
# File 'RubyExtension/MSPhysics/simulation.rb', line 419

def animate_scenes(state)
  state = AMS.clamp(state.to_i, 0, 3)
  return false if state == @scene_anim_info[:state]
  if state == 0
    @scene_anim_info[:state] = state
    @scene_anim_info[:data].clear
    @scene_anim_info[:transition_time] = 0
    @scene_anim_info[:elasted_time] = 0
    @scene_anim_info[:ref_time] = 0
    @scene_anim_info[:tabs_size] = nil
    @scene_anim_info[:active_tab] = nil
    @scene_anim_info[:tab_dir] = 1
    return true
  elsif @scene_anim_info[:state] != 0
    @scene_anim_info[:state] = state
    @scene_anim_info[:tab_dir] = 1
    return true
  else
    # Obtain default page parameters
    model = Sketchup.active_model
    default_delay_time = model.options['SlideshowOptions']['SlideTime']
    default_transition_time = model.options['PageOptions']['TransitionTime']
    # Abort any current scene transitioning
    @scene_info[:active] = false
    @scene_info[:data1] = nil
    @scene_info[:data2] = nil
    @scene_info[:transition_time] = 0
    @scene_info[:elasted_time] = 0
    # Gather scene information
    @scene_anim_info[:transition_time] = 0
    tab_index = 0
    tabs_size = model.pages.size
    model.pages.each { |page|
      dt = (page.delay_time < 0 ? default_delay_time : page.delay_time).to_f
      tt = (page.transition_time < 0 ? default_transition_time : page.transition_time).to_f
      @scene_anim_info[:data][tab_index] = {
        :sdelay => @scene_anim_info[:transition_time],
        :edelay => @scene_anim_info[:transition_time] + dt,
        :stransition => @scene_anim_info[:transition_time] + dt,
        :etransition => @scene_anim_info[:transition_time] + dt + tt,
        :scene => MSPhysics::SceneData.new(page),
        :page => page,
        :delay_time => dt,
        :transition_time => tt
      }
      @scene_anim_info[:transition_time] += dt + tt
      tab_index += 1
    }
    # Verify
    if @scene_anim_info[:transition_time] < MSPhysics::EPSILON
      @scene_anim_info[:transition_time] = 0
      @scene_anim_info[:data].clear
      return false
    end
    # Record additional info
    @scene_anim_info[:state] = state
    @scene_anim_info[:elasted_time] = MSPhysics::Settings.animate_scenes_reversed? ? @scene_anim_info[:transition_time] : 0
    @scene_anim_info[:ref_time] = 0
    @scene_anim_info[:tabs_size] = tabs_size
    @scene_anim_info[:active_tab] = nil
    @scene_anim_info[:tab_dir] = 1
    # Return success
    return true
  end
end

#axes_visible=(state) ⇒ Object

Enable/disable the drawing of centre of mass axes for all bodies.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



580
581
582
# File 'RubyExtension/MSPhysics/simulation.rb', line 580

def axes_visible=(state)
  @axes[:show] = state ? true : false
end

#axes_visible?Boolean

Determine whether the drawing of centre of mass axes, for all bodies, is enabled.

Returns:

  • (Boolean)

Since:

  • 1.0.0



587
588
589
# File 'RubyExtension/MSPhysics/simulation.rb', line 587

def axes_visible?
  @axes[:show]
end

#bodies_visible=(state) ⇒ Object

Show/hide all groups/components associated with the bodies.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
# File 'RubyExtension/MSPhysics/simulation.rb', line 627

def bodies_visible=(state)
  @show_bodies = state ? true : false
  if @show_bodies
    @hidden_entities.each { |e|
      e.visible = true if e.valid?
    }
    @hidden_entities.clear
  else
    world_address = @world.address
    MSPhysics::Newton.enable_object_validation(false)
    body_address = MSPhysics::Newton::World.get_first_body(world_address)
    while body_address
      data = MSPhysics::Newton::Body.get_user_data(body_address)
      if data.is_a?(MSPhysics::Body) && data.group.visible?
        data.group.visible = false
        @hidden_entities << data.group
      end
      body_address = MSPhysics::Newton::World.get_next_body(world_address, body_address)
    end
    MSPhysics::Newton.enable_object_validation(true)
  end
  @show_bodies
end

#bodies_visible?Boolean

Determine whether all groups/components associated with the bodies are intended to be visible.

Returns:

  • (Boolean)

Since:

  • 1.0.0



654
655
656
# File 'RubyExtension/MSPhysics/simulation.rb', line 654

def bodies_visible?
  @show_bodies
end

#clear_display_notevoid

This method returns an undefined value.

Clear display-note text.

Since:

  • 1.0.0



1034
1035
1036
1037
1038
# File 'RubyExtension/MSPhysics/simulation.rb', line 1034

def clear_display_note
  if @display_note[:ent] != nil && @display_note[:ent].valid?
    @display_note[:ent].set_text("")
  end
end

#clear_fancy_notevoid

This method returns an undefined value.

Clear fancy note.

Since:

  • 1.0.0



1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
# File 'RubyExtension/MSPhysics/simulation.rb', line 1111

def clear_fancy_note
  return unless @fancy_note
  @prev_fancy_note = @fancy_note
  @fancy_note = nil
  if @prev_fancy_note.empty? || @prev_fancy_note[:fade] < MSPhysics::EPSILON
    @prev_fancy_note = nil
  elsif @prev_fancy_note[:time] > @prev_fancy_note[:fade]
    @prev_fancy_note[:time] = @prev_fancy_note[:fade]
  end
end

#clear_log_linevoid

This method returns an undefined value.

Clear log-line text.

Since:

  • 1.0.0



1007
1008
1009
1010
1011
1012
# File 'RubyExtension/MSPhysics/simulation.rb', line 1007

def clear_log_line
  if @log_line[:ent] != nil && @log_line[:ent].valid?
    @log_line[:ent].set_text("")
  end
  @log_line[:log].clear
end

#clear_particlesObject

Remove all particles.

Since:

  • 1.0.0



1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
# File 'RubyExtension/MSPhysics/simulation.rb', line 1633

def clear_particles
  model = Sketchup.active_model
  mats = model.materials
  @particles.each { |opts|
    if opts[:group] && opts[:group].valid?
      opts[:group].material = nil
      opts[:group].erase!
    end
    mats.remove(opts[:material]) if mats.respond_to?(:remove)
  }
  @particles.clear
  MSPhysics::C::Particle.destroy_all
end

#collision_wireframe_visible=(state) ⇒ Object

Enable/disable the drawing of collision wireframe for all bodies.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
# File 'RubyExtension/MSPhysics/simulation.rb', line 555

def collision_wireframe_visible=(state)
  state = state ? true : false
  return state if state == @collision_wireframe[:show]
  ro = Sketchup.active_model.rendering_options
  if state
    @collision_wireframe[:show_edges] = ro['EdgeDisplayMode']
    @collision_wireframe[:show_profiles] = ro['DrawSilhouettes']
    ro['EdgeDisplayMode'] = false
    ro['DrawSilhouettes'] = false
  else
    ro['EdgeDisplayMode'] = @collision_wireframe[:show_edges]
    ro['DrawSilhouettes'] = @collision_wireframe[:show_profiles]
  end
  @collision_wireframe[:show] = state
end

#collision_wireframe_visible?Boolean

Determine whether the drawing of collision wireframe, for all bodies, is enabled.

Returns:

  • (Boolean)

Since:

  • 1.0.0



574
575
576
# File 'RubyExtension/MSPhysics/simulation.rb', line 574

def collision_wireframe_visible?
  @collision_wireframe[:show]
end

#contact_forces_visible=(state) ⇒ Object

Enable/disable the drawing of collision contact forces.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



529
530
531
# File 'RubyExtension/MSPhysics/simulation.rb', line 529

def contact_forces_visible=(state)
  @contact_forces[:show] = state ? true : false
end

#contact_forces_visible?Boolean

Determine whether the drawing of collision contact forces is enabled.

Returns:

  • (Boolean)

Since:

  • 1.0.0



535
536
537
# File 'RubyExtension/MSPhysics/simulation.rb', line 535

def contact_forces_visible?
  @contact_forces[:show]
end

#contact_points_visible=(state) ⇒ Object

Enable/disable the drawing of collision contact points.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



517
518
519
# File 'RubyExtension/MSPhysics/simulation.rb', line 517

def contact_points_visible=(state)
  @contact_points[:show] = state ? true : false
end

#contact_points_visible?Boolean

Determine whether the drawing of collision contact points is enabled.

Returns:

  • (Boolean)

Since:

  • 1.0.0



523
524
525
# File 'RubyExtension/MSPhysics/simulation.rb', line 523

def contact_points_visible?
  @contact_points[:show]
end

#continuous_collision_check_enabled=(state) ⇒ Object

Enable/disable the continuous collision check for all bodies. Continuous collision check prevents bodies from passing each other at high speeds.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
# File 'RubyExtension/MSPhysics/simulation.rb', line 594

def continuous_collision_check_enabled=(state)
  @ccm = state ? true : false
  world_address = @world.address
  MSPhysics::Newton.enable_object_validation(false)
  if @ccm
    body_address = MSPhysics::Newton::World.get_first_body(world_address)
    while body_address
      unless MSPhysics::Newton::Body.get_continuous_collision_state(body_address)
        MSPhysics::Newton::Body.set_continuous_collision_state(body_address, true)
        @cc_bodies << body_address unless @cc_bodies.include?(body_address)
      end
      body_address = MSPhysics::Newton::World.get_next_body(world_address, body_address)
    end
  else
    @cc_bodies.each { |body_address|
      next unless MSPhysics::Newton::Body.is_valid?(body_address)
      MSPhysics::Newton::Body.set_continuous_collision_state(body_address, false)
    }
    @cc_bodies.clear
  end
  MSPhysics::Newton.enable_object_validation(true)
end

#continuous_collision_check_enabled?Boolean

Determine whether the continuous collision check, for all bodies, is intended to be enabled. Continuous collision check prevents bodies from passing each other at high speeds.

Returns:

  • (Boolean)

Since:

  • 1.0.0



621
622
623
# File 'RubyExtension/MSPhysics/simulation.rb', line 621

def continuous_collision_check_enabled?
  @ccm
end

#create_particle(opts) ⇒ void

This method returns an undefined value.

Create a new particle.

Parameters:

  • opts (Hash)

    Particle options.

Options Hash (opts):

  • :position (Geom::Point3d, Array) — default: ORIGIN

    Starting position. Position is altered by particle velocity and time.

  • :velocity (Geom::Vector3d, Array) — default: nil

    Starting velocity in inches per second. Pass nil if velocity is not necessary.

  • :velocity_damp (Numeric) — default: 0.0

    Velocity damping, a value between 0.0 and 1.0.

  • :gravity (Geom::Vector3d, Array) — default: nil

    Gravitational acceleration in inches per second per second. Pass nil if gravity is not necessary.

  • :radius (Numeric) — default: 0.1

    Starting radius in inches, a value between 1.0e-4 and 1.0e6. Radius is influenced by the scale option.

  • :scale (Numeric) — default: 1.01

    Radius scale ratio per second, a value between 1.0e-4 and 1.0e6. If radius becomes less than 0.001 or more than 10000, the particle is automatically destroyed.

  • :color1 (Sketchup::Color, Array, String, Integer) — default: 'Gray'

    Starting color.

  • :color2 (Sketchup::Color, Array, String, Integer) — default: nil

    Ending color. Pass nil to have the ending color remain same as the starting color.

  • :alpha1 (Numeric) — default: 1.0

    Starting opacity, a value between 0.0 and 1.0.

  • :alpha2 (Numeric) — default: nil

    Ending opacity, a value between 0.0 and 1.0. Pass nil if ending opacity is ought to be the same as the starting opacity.

  • :fade (Numeric) — default: 0.0

    A time ratio it should take the effect to fade into the starting opacity and fade out from the ending opacity, a value between 0.0 and 1.0.

  • :lifetime (Integer) — default: 3.0

    Particle lifetime in seconds, a value greater than zero.

  • :num_seg (Integer) — default: 16

    Number of segments the particle is to consist of, a value between 3 and 360.

  • :rot_angle (Numeric) — default: 0.0

    Rotate angle in radians.

  • :type (Integer) — default: 1
    1. Defines a 2D circular particle that is drawn through view drawing functions. This type is fast, but particle shade and shadow is not present. Also, this particle doesn’t blend quite well with other particles of this type.

    2. Defines a 2D circular particle that is created from SketchUp geometry. This type is normal, and guarantees good, balanced results.

    3. Defines a 3D spherical particle that is crated from SketchUp geometry. This type is slow, but it guarantees best results.

Since:

  • 1.0.0



1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
# File 'RubyExtension/MSPhysics/simulation.rb', line 1540

def create_particle(opts)
  if opts[:type] == 1
    MSPhysics::C::Particle.create(opts)
    return
  end
  opts2 = {
    :position       => opts[:position] ? Geom::Point3d.new(opts[:position]) : Geom::Point3d.new(0, 0, 0),
    :velocity       => opts[:velocity] ? Geom::Vector3d.new(opts[:velocity]) : nil,
    :velocity_damp  => opts[:velocity_damp] ? AMS.clamp(opts[:velocity_damp].to_f, 0.0, 1.0) : 0.0,
    :gravity        => opts[:gravity] ? Geom::Vector3d.new(opts[:gravity]) : nil,
    :radius         => opts[:radius] ? AMS.clamp(opts[:radius].to_f, 1.0e-4, 1.0e6) : 0.1,
    :scale          => opts[:scale] ? AMS.clamp(opts[:scale].to_f, 1.0e-4, 1.0e6) : 1.01,
    :color1         => opts[:color1] ? Sketchup::Color.new(opts[:color1]) : Sketchup::Color.new('Gray'),
    :color2         => opts[:color2] ? Sketchup::Color.new(opts[:color2]) : nil,
    :alpha1         => opts[:alpha1] ? AMS.clamp(opts[:alpha1].to_f, 0.0, 1.0) : 1.0,
    :alpha2         => opts[:alpha2] ? AMS.clamp(opts[:alpha2].to_f, 0.0, 1.0) : nil,
    :fade           => opts[:fade] ? AMS.clamp(opts[:fade].to_f, 0.0, 1.0) : 0.0,
    :lifetime       => opts[:lifetime] ? AMS.clamp(opts[:lifetime].to_f, 1.0e-6, nil) : 3.0,
    :num_seg        => opts[:num_seg] ? AMS.clamp(opts[:num_seg].to_i, 3, 360) : 16,
    :rot_angle      => opts[:rot_angle] ? opts[:rot_angle].to_f : 0.0,
    :type           => opts[:type] ? AMS.clamp(opts[:type].to_i, 1, 3) : 1
  }
  opts2[:life_start] = @world.time
  opts2[:life_end] = opts2[:life_start] + opts2[:lifetime]
  opts2[:color] = Sketchup::Color.new(opts2[:color1])
  opts2[:color].alpha = opts2[:fade].zero? ? opts2[:alpha1] : 0.0
  @particles << opts2
  model = Sketchup.active_model
  default_layer = model.layers[0]
  cd = nil
  if opts2[:type] == 3 # 3D entity
    cd = @particle_def3d[opts2[:num_seg]]
    if cd.nil? || cd.deleted?
      cd = model.definitions.add("AP3D_#{opts2[:num_seg]}")
      @particle_def3d[opts2[:num_seg]] = cd
      e = cd.entities
      c1 = e.add_circle(ORIGIN, X_AXIS, 1, opts2[:num_seg])
      c2 = e.add_circle([0,0,-10], Z_AXIS, 1, opts2[:num_seg])
      c1.each { |edge| edge.hidden = true }
      f = e.add_face(c1)
      f.followme(c2)
      c2.each { |edge| edge.erase! }
      # Create an instance to ensure the definition is not erased.
      instance = model.entities.add_instance(cd, Geom::Transformation.new())
      instance.layer = default_layer if (instance.layer != default_layer)
      instance.visible = false
      instance.set_attribute('MSPhysics', 'Type', 'Particle')
      instance.set_attribute('MSPhysics Body', 'Ignore', true)
      @dp_particle_instances << instance
    end
    normal = Geom::Vector3d.new(Math.cos(opts2[:rot_angle]), Math.sin(opts2[:rot_angle]), 0)
  else # 2D entity
    cd = @particle_def2d[opts2[:num_seg]]
    if cd.nil? || cd.deleted?
      cd = model.definitions.add("MSP_P2D_#{opts2[:num_seg]}")
      @particle_def2d[opts2[:num_seg]] = cd
      e = cd.entities
      c = e.add_circle(ORIGIN, Z_AXIS, 1, opts2[:num_seg])
      c.each { |edge| edge.hidden = true }
      e.add_face(c)
      # Create an instance to ensure the definition is not erased.
      instance = model.entities.add_instance(cd, Geom::Transformation.new())
      instance.layer = default_layer if (instance.layer != default_layer)
      instance.visible = false
      instance.set_attribute('MSPhysics', 'Type', 'Particle')
      instance.set_attribute('MSPhysics Body', 'Ignore', true)
      @dp_particle_instances << instance
    end
    eye = model.active_view.camera.eye
    normal = (eye == opts2[:position]) ? Z_AXIS : opts2[:position].vector_to(eye)
  end
  tra1 = Geom::Transformation.new(opts[:position], normal)
  tra2 = Geom::Transformation.rotation(ORIGIN, Z_AXIS, opts[:rot_angle])
  tra3 = Geom::Transformation.scaling(opts[:radius])
  tra = tra1*tra2*tra3
  opts2[:material] = model.materials.add('FX')
  opts2[:material].color = opts2[:color]
  opts2[:material].alpha = opts2[:color].alpha / 255.0
  opts2[:group] = model.entities.add_instance(cd, tra)
  opts2[:group].material = opts2[:material]
  opts2[:group].layer = default_layer if (opts2[:group].layer != default_layer)
  opts2[:group].visible = false unless @particles_visible
  #opts2[:group].receives_shadows = false if opts2[:group].receives_shadows?
  #opts2[:group].casts_shadows = false if opts2[:group].casts_shadows?
end

#cursorInteger

Get active cursor.

Returns:

  • (Integer)

    Cursor id.

Since:

  • 1.0.0



336
337
338
# File 'RubyExtension/MSPhysics/simulation.rb', line 336

def cursor
  @cursor_id
end

#cursor=(id) ⇒ Integer

Set active cursor.

Examples:

onStart {
  # Set game mode.
  simulation.mode = 1
  # Set target cursor.
  simulation.cursor = MSPhysics::CURSORS[:target]
}

Parameters:

  • id (Integer)

    Cursor id.

Returns:

  • (Integer)

    The new cursor id.

See Also:

Since:

  • 1.0.0



351
352
353
354
355
# File 'RubyExtension/MSPhysics/simulation.rb', line 351

def cursor=(id)
  @cursor_id = id.to_i
  onSetCursor
  @cursor_id
end

#cursor_visible=(state) ⇒ Object

Note:

Windows only!

Show/hide mouse cursor.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



378
379
380
381
382
# File 'RubyExtension/MSPhysics/simulation.rb', line 378

def cursor_visible=(state)
  if AMS::IS_PLATFORM_WINDOWS
    AMS::Cursor.show(state)
  end
end

#cursor_visible?Boolean

Note:

Windows only!

Determine whether cursor is visible.

Returns:

  • (Boolean)

Since:

  • 1.0.0



387
388
389
390
391
# File 'RubyExtension/MSPhysics/simulation.rb', line 387

def cursor_visible?
  if AMS::IS_PLATFORM_WINDOWS
    AMS::Cursor.is_visible?
  end
end

#destroy_all_emitted_bodiesInteger

Destroy all emitted bodies and the entities associated with them.

Returns:

  • (Integer)

    The number of emitted bodies destroyed.

Since:

  • 1.0.0



926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
# File 'RubyExtension/MSPhysics/simulation.rb', line 926

def destroy_all_emitted_bodies
  count = 0
  @emitted_bodies.each { |body, life|
    if body.valid?
      body.destroy
      count += 1
    end
  }
  @emitted_bodies.clear
  @created_entities.each { |e, s|
    e.erase! if e.valid?
  }
  @created_entities.clear
  count
end

#display_note(text, color = MSPhysics::WATERMARK_COLOR) ⇒ String

Display text on screen.

Parameters:

  • text (String)

    A text to display.

  • color (Sketchup::Color) (defaults to: MSPhysics::WATERMARK_COLOR)

    Text color.

Returns:

  • (String)

    Displayed text

Since:

  • 1.0.0



1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
# File 'RubyExtension/MSPhysics/simulation.rb', line 1018

def display_note(text, color = MSPhysics::WATERMARK_COLOR)
  model = Sketchup.active_model
  if @display_note[:mat].nil? || @display_note[:mat].deleted?
    @display_note[:mat] = model.materials.add('MSPDisplayNote')
  end
  if @display_note[:ent].nil? || @display_note[:ent].deleted?
    @display_note[:ent] = MSPhysics.add_watermark_text2(10, 30, '', 'DisplayNote')
    @display_note[:ent].material = @display_note[:mat]
  end
  color = Sketchup::Color.new(color) unless color.is_a?(Sketchup::Color)
  @display_note[:mat].color = color if @display_note[:mat].color.to_i != color.to_i
  @display_note[:ent].set_text(text.to_s)
end

#draw2d(type, points, color = 'black', width = 1, stipple = '') ⇒ void

This method returns an undefined value.

Draw 2D geometry into view.

Parameters:

  • type (String, Symbol)

    Drawing type. Use one of the following:

    • "points" - Draw a collection of points. Each vertex is treated as a single point. Vertex n defines point n. N points are drawn.

    • "lines" - Draw a collection of independent lines. Each pair of vertices is treated as a single line. Vertices 2n-1 and 2n define line n. N/2 lines are drawn.

    • "line_strip" - Draw a connected group of line segments from the first vertex to the last. Vertices n and n+1 define line n. N-1 lines are drawn.

    • "line_loop" - Draw a connected group of line segments from the first vertex to the last, then back to the first. Vertices n and n+1 define line n. The last line, however, is defined by vertices N and 1. N lines are drawn.

    • "triangles" - Draw a group of independent triangles. Each triplet of vertices is considered a single triangle. Vertices 3n-2, 3n-1, and 3n define triangle n. N/3 triangles are drawn.

    • "triangle_strip" - Draw a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. For odd n, vertices n, n+1, and n+2 define triangle n. For even n, vertices n+1, n, and n+2 define triangle n. N-2 triangles are drawn.

    • "triangle_fan" - Draw a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. Vertices 1, n+1, and n+2 define triangle n. N-2 triangles are drawn.

    • "quads" - Draw a collection of independent quadrilaterals. A group of four vertices is treated as a single quadrilateral. Vertices 4n-3, 4n-2, 4n-1, and 4n define quadrilateral n. N/4 quadrilaterals are drawn.

    • "quad_strip" - Draw a collection of connected quadrilaterals. One quadrilateral is defined for each pair of vertices presented after the first pair. Vertices 2n-1, 2n, 2n+2, and 2n+1 define quadrilateral n. N/2-1 quadrilaterals are drawn. Note that the order in which vertices are used to construct a quadrilateral from strip data is different from that used with independent data.

    • "polygon" - Draws a single convex polygon. Vertices 1 through N define this polygon.

  • points (Array<Geom::Point3d, Array<Numeric>>)

    An array of points.

  • color (Sketchup::Color, Array, String) (defaults to: 'black')

    Drawing color.

  • width (Integer) (defaults to: 1)

    Line width in pixels.

  • stipple (String) (defaults to: '')

    Line stipple. Use one of the following:

    • "." - dotted line

    • "-" - short-dashed line

    • "_" - long-dashed line

    • "-.-" - dash dot dash line

    • "" - solid line

Since:

  • 1.0.0



1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
# File 'RubyExtension/MSPhysics/simulation.rb', line 1174

def draw2d(type, points, color = 'black', width = 1, stipple = '')
  type = case type.to_s.downcase.gsub(/\s/, '_').to_sym
    when :points
      GL_POINTS
    when :lines
      GL_LINES
    when :line_strip
      GL_LINE_STRIP
    when :line_loop
      GL_LINE_LOOP
    when :triangles
      GL_TRIANGLES
    when :triangle_strip
      GL_TRIANGLE_STRIP
    when :triangle_fan
      GL_TRIANGLE_FAN
    when :quads
      GL_QUADS
    when :quad_strip
      GL_QUAD_STRIP
    when :polygon
      GL_POLYGON
  else
    raise(TypeError, 'Invalid type!', caller)
  end
  @draw_queue << [type, points, color, width, stipple, 0]
end

#draw3d(type, points, color = 'black', width = 1, stipple = '') ⇒ void

This method returns an undefined value.

Draw 3D geometry into view.

Parameters:

  • type (String, Symbol)

    Drawing type. Use one of the following:

    • "points" - Draw a collection of points. Each vertex is treated as a single point. Vertex n defines point n. N points are drawn.

    • "lines" - Draw a collection of independent lines. Each pair of vertices is treated as a single line. Vertices 2n-1 and 2n define line n. N/2 lines are drawn.

    • "line_strip" - Draw a connected group of line segments from the first vertex to the last. Vertices n and n+1 define line n. N-1 lines are drawn.

    • "line_loop" - Draw a connected group of line segments from the first vertex to the last, then back to the first. Vertices n and n+1 define line n. The last line, however, is defined by vertices N and 1. N lines are drawn.

    • "triangles" - Draw a group of independent triangles. Each triplet of vertices is considered a single triangle. Vertices 3n-2, 3n-1, and 3n define triangle n. N/3 triangles are drawn.

    • "triangle_strip" - Draw a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. For odd n, vertices n, n+1, and n+2 define triangle n. For even n, vertices n+1, n, and n+2 define triangle n. N-2 triangles are drawn.

    • "triangle_fan" - Draw a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. Vertices 1, n+1, and n+2 define triangle n. N-2 triangles are drawn.

    • "quads" - Draw a collection of independent quadrilaterals. A group of four vertices is treated as a single quadrilateral. Vertices 4n-3, 4n-2, 4n-1, and 4n define quadrilateral n. N/4 quadrilaterals are drawn.

    • "quad_strip" - Draw a collection of connected quadrilaterals. One quadrilateral is defined for each pair of vertices presented after the first pair. Vertices 2n-1, 2n, 2n+2, and 2n+1 define quadrilateral n. N/2-1 quadrilaterals are drawn. Note that the order in which vertices are used to construct a quadrilateral from strip data is different from that used with independent data.

    • "polygon" - Draws a single convex polygon. Vertices 1 through N define this polygon.

  • points (Array<Geom::Point3d, Array<Numeric>>)

    An array of points.

  • color (Sketchup::Color, Array, String) (defaults to: 'black')

    Drawing color.

  • width (Integer) (defaults to: 1)

    Line width in pixels.

  • stipple (String) (defaults to: '')

    Line stipple. Use one of the following:

    • "." - dotted line

    • "-" - short-dashed line

    • "_" - long-dashed line

    • "-.-" - dash dot dash line

    • "" - solid line

Since:

  • 1.0.0



1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
# File 'RubyExtension/MSPhysics/simulation.rb', line 1205

def draw3d(type, points, color = 'black', width = 1, stipple = '')
  type = case type.to_s.downcase.gsub(/\s/, '_').to_sym
    when :points
      GL_POINTS
    when :lines
      GL_LINES
    when :line_strip
      GL_LINE_STRIP
    when :line_loop
      GL_LINE_LOOP
    when :triangles
      GL_TRIANGLES
    when :triangle_strip
      GL_TRIANGLE_STRIP
    when :triangle_fan
      GL_TRIANGLE_FAN
    when :quads
      GL_QUADS
    when :quad_strip
      GL_QUAD_STRIP
    when :polygon
      GL_POLYGON
  else
    raise(TypeError, 'Invalid type!', caller)
  end
  @draw_queue << [type, points, color, width, stipple, 1]
end

#draw_points(points, size = 1, style = 0, color = 'black', width = 1, stipple = '') ⇒ void

This method returns an undefined value.

Draw 3D points with custom style.

Parameters:

  • points (Array<Geom::Point3d, Array<Numeric>>)

    An array of points.

  • size (Integer) (defaults to: 1)

    Point size in pixels.

  • style (Integer) (defaults to: 0)

    Point style. Use one of the following:

    1. none

    2. open square

    3. filled square

    4. + cross

    5. x cross

    6. star

    7. open triangle

    8. filled triangle

  • color (Sketchup::Color, Array, String) (defaults to: 'black')

    Point color.

  • width (Integer) (defaults to: 1)

    Line width in pixels.

  • stipple (String) (defaults to: '')

    Line stipple. Use one of the following:

    • "." - dotted line

    • "-" - short-dashed line

    • "_" - long-dashed line

    • "-.-" - dash dot dash line

    • "" - solid line

Since:

  • 1.0.0



1254
1255
1256
# File 'RubyExtension/MSPhysics/simulation.rb', line 1254

def draw_points(points, size = 1, style = 0, color = 'black', width = 1, stipple = '')
  @points_queue << [points, size, style, color, width, stipple]
end

#emit_body(body, force, lifetime) ⇒ Body #emit_body(body, transformation, force, lifetime) ⇒ Body

Returns A new, emitted body.

Examples:

onTick {
  # Emit body every 0.25 seconds if key 'space' is down.
  if key('space') == 1 && singular_repeater(0.25) == 1
    force = AMS::Geometry.scale_vector(this.get_matrix.yaxis, this.mass * 100)
    simulation.emit_body(this, force, 3)
  end
}

Overloads:

  • #emit_body(body, force, lifetime) ⇒ Body

    Create a copy of a body and apply force to it.

    Parameters:

    • body (Body)

      The body to emit.

    • force (Geom::Vector3d, Array<Numeric>)

      The force to apply in Newtons.

    • lifetime (Numeric)

      The lifetime in seconds. Pass zero to have the emitted body live endlessly.

  • #emit_body(body, transformation, force, lifetime) ⇒ Body

    Create a copy of a body at a specific transformation and apply force to it.

    Parameters:

    • body (Body)

      The body to emit.

    • transformation (Geom::Transformation, Array<Numeric>)
    • force (Geom::Vector3d, Array<Numeric>)

      The force to apply in Newtons.

    • lifetime (Numeric)

      The lifetime in seconds. Pass zero to have the emitted body live endlessly.

Returns:

  • (Body)

    A new, emitted body.

Since:

  • 1.0.0



906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
# File 'RubyExtension/MSPhysics/simulation.rb', line 906

def emit_body(*args)
  if args.size == 3
    body, force, lifetime = args
  elsif args.size == 4
    body, tra, force, lifetime = args
  else
    raise(ArgumentError, "Wrong number of arguments! Expected 3..4 arguments but got #{args.size}.", caller)
  end
  new_body = args.size == 3 ? body.copy(true, 0) : body.copy(tra, true, 0)
  new_body.static = false
  new_body.collidable = true
  new_body.continuous_collision_check_enabled = true
  new_body.add_force(force)
  @emitted_bodies[new_body] = lifetime.zero? ? nil : @world.time + lifetime
  @created_entities[new_body.group] = true
  new_body
end

#erase_instances_on_end=(state) ⇒ Object

Enable/disable the deletion of emitted/copied/split bodies and particles when simulation ends.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



1717
1718
1719
# File 'RubyExtension/MSPhysics/simulation.rb', line 1717

def erase_instances_on_end=(state)
  @erase_instances_on_end = state ? true : false
end

#erase_instances_on_end?Boolean

Determine whether the deletion of emitted/copied/split bodies and particles when simulation ends is enabled.

Returns:

  • (Boolean)

Since:

  • 1.0.0



1710
1711
1712
# File 'RubyExtension/MSPhysics/simulation.rb', line 1710

def erase_instances_on_end?
  @erase_instances_on_end
end

#erase_on_end(entity) ⇒ void

This method returns an undefined value.

Erase group/component when simulation resets. This method is commonly used for copied bodies. Body.#copy method doesn’t register created entity to the “erase” queue. When simulation resets created entities remain un-deleted. To erase these entities, one could simply use this method.

Examples:

Erasing copied entities.

onTick {
  if frame % 10 == 0 && key('space') == 1
    pt = Geom::Point3d.new(rand(1000), rand(1000), rand(1000))
    tra = Geom::Transformation.new(pt)
    body = this.copy(tra, true, 0)
    simulation.erase_on_end(body.group)
  end
}

Parameters:

  • entity (Sketchup::Drawingelement)

Since:

  • 1.0.0



958
959
960
961
# File 'RubyExtension/MSPhysics/simulation.rb', line 958

def erase_on_end(entity)
  AMS.validate_type(entity, Sketchup::Drawingelement)
  @created_entities[entity] = true
end

#fancy_note(text, opts = {}) ⇒ void

This method returns an undefined value.

Display a fancy text on screen.

Parameters:

  • text (String)
  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :font (String) — default: "Ariel"

    Text font.

  • :size (Integer) — default: 11

    Font size in pixels.

  • :bold (Boolean) — default: false

    Whether to have the text bold.

  • :italic (Boolean) — default: false

    Whether to have the text italicized.

  • :align (Integer) — default: TextAlignLeft

    Text float: TextAlignLeft, TextAlignCenter, or TextAlignRight.

  • :color (Sketchup::Color)

    Text color.

  • :background (Sketchup::Color, nil)

    Background color. Pass nil to have a text without background.

  • :padding (Integer)

    Background padding in pixels.

  • :hratio (Numeric) — default: 0.0

    Horizontal position ratio on screen.

  • :vratio (Numeric) — default: 0.0

    Vertical position ratio on screen.

  • :duration (Numeric) — default: 5

    Display duration in seconds.

  • :fade (Numreic) — default: 0.25

    Fade in and fade out times in seconds.

  • :twr (Numeric) — default: 0.6

    Text width ratio in pixels in case size computation is not successful.

  • :thr (Numeric) — default: 1.5

    Text height ratio in pixels in case size computation is not successful.

Since:

  • 1.0.0



1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
# File 'RubyExtension/MSPhysics/simulation.rb', line 1066

def fancy_note(text, opts = {})
  clear_fancy_note
  data = {}
  data[:text] = text.to_s
  data[:font] = opts.has_key?(:font) ? opts[:font].to_s : @fancy_note_defaults[:font]
  data[:size] = opts.has_key?(:size) ? opts[:size].to_i : @fancy_note_defaults[:size]
  data[:bold] = opts.has_key?(:bold) ? opts[:bold] : @fancy_note_defaults[:bold]
  data[:italic] = opts.has_key?(:italic) ? opts[:italic] : @fancy_note_defaults[:italic]
  data[:align] = opts.has_key?(:align) ? opts[:align].to_i : @fancy_note_defaults[:align]
  data[:color] = opts.has_key?(:color) ? Sketchup::Color.new(opts[:color]) : @fancy_note_defaults[:color]
  if opts.has_key?(:background)
    data[:background] = opts[:background] ? Sketchup::Color.new(opts[:background]) : nil
  else
    data[:background] = @fancy_note_defaults[:background]
  end
  data[:padding] = opts.has_key?(:padding) ? opts[:padding].to_i : @fancy_note_defaults[:padding]
  data[:hratio] = opts.has_key?(:hratio) ? opts[:hratio].to_f : @fancy_note_defaults[:hratio]
  data[:vratio] = opts.has_key?(:vratio) ? opts[:vratio].to_f : @fancy_note_defaults[:vratio]
  data[:duration] = opts.has_key?(:duration) ? AMS.clamp(opts[:duration].to_f, 0.01, nil) : @fancy_note_defaults[:duration]
  data[:fade] = opts.has_key?(:fade) ? AMS.clamp(opts[:fade].to_f, 0.0, nil) : @fancy_note_defaults[:fade]
  data[:twr] = opts.has_key?(:twr) ? opts[:twr].to_f : @fancy_note_defaults[:twr]
  data[:thr] = opts.has_key?(:thr) ? opts[:thr].to_f : @fancy_note_defaults[:thr]
  unless MSPhysics::ControlPanel.open?
    MSPhysics::ControlPanel.open
    MSPhysics::ControlPanel.hide
  end
  size = MSPhysics::ControlPanel.compute_text_size(data[:text], data)
  unless size
    num_lines = 1
    for i in 0...data[:text].size
      num_lines += 1 if data[:text][i] == "\n"
    end
    size = [data[:twr] * data[:size] * data[:text].size, data[:thr] * data[:size] * num_lines]
  end
  data[:time] = 0.0
  data[:sx] = size.x + data[:padding] * 2
  data[:sy] = size.y + data[:padding] * 2
  data[:color2] = Sketchup::Color.new(data[:color])
  data[:background2] = data[:background] ? Sketchup::Color.new(data[:background]) : nil
  data[:alpha] = 0.0
  @fancy_note = data
end

#find_bodies_by_name(name) ⇒ Array<Body>

Reference all bodies by group name.

Parameters:

  • name (String)

    Group name.

Returns:

Since:

  • 1.0.0



684
685
686
687
688
# File 'RubyExtension/MSPhysics/simulation.rb', line 684

def find_bodies_by_name(name)
  MSPhysics::Newton::World.get_bodies(@world.address) { |ptr, data|
    data.is_a?(MSPhysics::Body) && data.group && data.group.valid? && data.group.name == name ? data : nil
  }
end

#find_body_by_group(group) ⇒ Body?

Reference body by group/component.

Parameters:

  • group (Sketchup::Group, Sketchup::ComponentInstance)

Returns:

Since:

  • 1.0.0



664
665
666
667
668
# File 'RubyExtension/MSPhysics/simulation.rb', line 664

def find_body_by_group(group)
  AMS.validate_type(group, Sketchup::Group, Sketchup::ComponentInstance)
  data = MSPhysics::Newton::Body.get_body_data_by_group(@world.address, group)
  data.is_a?(MSPhysics::Body) ? data : nil
end

#find_body_by_name(name) ⇒ Body?

Reference body by group name.

Parameters:

  • name (String)

    Group name.

Returns:

  • (Body, nil)

    A body or nil if not found.

Since:

  • 1.0.0



673
674
675
676
677
678
679
# File 'RubyExtension/MSPhysics/simulation.rb', line 673

def find_body_by_name(name)
  MSPhysics::Newton::World.get_bodies(@world.address) { |ptr, data|
    return data if data.is_a?(MSPhysics::Body) && data.group && data.group.valid? && data.group.name == name
    nil
  }
  nil
end

#find_group_by_name(name, entities = nil) ⇒ Sketchup::Group, ...

Reference group by name.

Parameters:

  • name (String)

    Group name.

  • entities (Sketchup::Entities, nil) (defaults to: nil)

    Entities to search within. Pass nil to search within the model entities.

Returns:

  • (Sketchup::Group, Sketchup::ComponentInstance, nil)

    A group or nil if not found.

Since:

  • 1.0.0



696
697
698
699
700
701
# File 'RubyExtension/MSPhysics/simulation.rb', line 696

def find_group_by_name(name, entities = nil)
  (entities ? entities : Sketchup.active_model.entities).each { |e|
    return e if (e.is_a?(Sketchup::Group) || e.is_a?(Sketchup::ComponentInstance)) && e.name == name
  }
  nil
end

#find_groups_by_name(name, entities = nil) ⇒ Array<Sketchup::Group, Sketchup::ComponentInstance>

Reference all groups by name.

Parameters:

  • name (String)

    Group name.

  • entities (Sketchup::Entities, nil) (defaults to: nil)

    Entities to search within. Pass nil to search within the model entities.

Returns:

  • (Array<Sketchup::Group, Sketchup::ComponentInstance>)

    An array of groups/components.

Since:

  • 1.0.0



709
710
711
712
713
714
715
# File 'RubyExtension/MSPhysics/simulation.rb', line 709

def find_groups_by_name(name, entities = nil)
  groups = []
  (entities ? entities : Sketchup.active_model.entities).each { |e|
    groups << e if (e.is_a?(Sketchup::Group) || e.is_a?(Sketchup::ComponentInstance)) && e.name == name
  }
  groups
end

#find_joint_by_group(group) ⇒ Joint?

Reference joint associated with a group.

Parameters:

  • group (Sketchup::Group, Sketchup::ComponentInstance)

Returns:

  • (Joint, nil)

    A joint or nil if not found.

Since:

  • 1.0.0



720
721
722
723
724
725
726
727
# File 'RubyExtension/MSPhysics/simulation.rb', line 720

def find_joint_by_group(group)
  AMS.validate_type(group, Sketchup::Group, Sketchup::ComponentInstance)
  MSPhysics::Newton::Joint.get_joints_by_group(group) { |ptr, data|
    return data if data.is_a?(MSPhysics::Joint) && data.world == @world
    nil
  }
  nil
end

#find_joint_by_name(name) ⇒ Joint?

Reference joint by name.

Parameters:

  • name (String)

    Joint Name.

Returns:

  • (Joint, nil)

    A joint or nil if not found.

Since:

  • 1.0.0



742
743
744
745
746
747
748
# File 'RubyExtension/MSPhysics/simulation.rb', line 742

def find_joint_by_name(name)
  MSPhysics::Newton::World.get_joints(@world.address) { |ptr, data|
    return data if data.is_a?(MSPhysics::Joint) && data.name == name
    nil
  }
  nil
end

#find_joints_by_group(group) ⇒ Array<Joint>

Reference all joints associated with a group.

Parameters:

  • group (Sketchup::Group, Sketchup::ComponentInstance)

Returns:

Since:

  • 1.0.0



732
733
734
735
736
737
# File 'RubyExtension/MSPhysics/simulation.rb', line 732

def find_joints_by_group(group)
  AMS.validate_type(group, Sketchup::Group, Sketchup::ComponentInstance)
  MSPhysics::Newton::Joint.get_joints_by_group(group) { |ptr, data|
    data.is_a?(MSPhysics::Joint) && data.world == @world ? data : nil
  }
end

#find_joints_by_name(name) ⇒ Array<Joint>

Reference all joints by name.

Parameters:

  • name (String)

    Joint Name.

Returns:

Since:

  • 1.0.0



753
754
755
756
757
# File 'RubyExtension/MSPhysics/simulation.rb', line 753

def find_joints_by_name(name)
  MSPhysics::Newton::World.get_joints(@world.address) { |ptr, data|
    data.is_a?(MSPhysics::Joint) && data.name == name ? data : nil
  }
end

#get_cursor_posArray<Integer>

Get cursor position in view coordinates.

Returns:

  • (Array<Integer>)

    [x,y]

Since:

  • 1.0.0



359
360
361
# File 'RubyExtension/MSPhysics/simulation.rb', line 359

def get_cursor_pos
  [@cursor_pos.x, @cursor_pos.y]
end

#log_line(text, color = MSPhysics::WATERMARK_COLOR) ⇒ String

Display text on screen in logged form.

Parameters:

  • text (String)
  • color (Sketchup::Color) (defaults to: MSPhysics::WATERMARK_COLOR)

    Text color.

Returns:

  • (String)

    Displayed text

Since:

  • 1.0.0



970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
# File 'RubyExtension/MSPhysics/simulation.rb', line 970

def log_line(text, color = MSPhysics::WATERMARK_COLOR)
  model = Sketchup.active_model
  if @log_line[:mat].nil? || @log_line[:mat].deleted?
    @log_line[:mat] = model.materials.add('MSPLogLine')
  end
  if @log_line[:ent].nil? || @log_line[:ent].deleted?
    @log_line[:ent] = MSPhysics.add_watermark_text2(10, 50, '', 'LogLine')
    @log_line[:ent].material = @log_line[:mat]
  end
  color = Sketchup::Color.new(color) unless color.is_a?(Sketchup::Color)
  @log_line[:mat].color = color if @log_line[:mat].color.to_i != color.to_i
  @log_line[:log] << text.to_s
  @log_line[:log].shift if @log_line[:log].size > @log_line[:limit]
  @log_line[:ent].set_text(@log_line[:log].join("\n"))
end

#log_line_limitInteger

Get log-line text limit.

Returns:

  • (Integer)

Since:

  • 1.0.0



988
989
990
# File 'RubyExtension/MSPhysics/simulation.rb', line 988

def log_line_limit
  @log_line[:limit]
end

#log_line_limit=(limit) ⇒ Object

Set log-line text limit.

Parameters:

  • limit (Integer)

    Desired limit, a value between 1 and 1000.

Since:

  • 1.0.0



994
995
996
997
998
999
1000
1001
1002
1003
# File 'RubyExtension/MSPhysics/simulation.rb', line 994

def log_line_limit=(limit)
  @log_line[:limit] = AMS.clamp(limit, 1, 1000)
  ls = @log_line[:log].size
  if ls > @log_line[:limit]
    @log_line[:log] = @log_line[:log][ls-@log_line[:limit]...ls]
    if @log_line[:ent] != nil && @log_line[:ent].valid?
      @log_line[:ent].set_text(@log_line[:log].join("\n"))
    end
  end
end

#modeInteger

Get simulation mode.

  • 0 - Interactive mode: The pick and drag tool and orbiting camera via the middle mouse button is enabled.

  • 1 - Game mode: The pick and drag tool and orbiting camera via the middle mouse button is disabled.

Returns:

  • (Integer)

    Returns one of the following values:

Since:

  • 1.0.0



317
318
319
# File 'RubyExtension/MSPhysics/simulation.rb', line 317

def mode
  @mode
end

#mode=(value) ⇒ Object

Set simulation mode.

  • 0 - Interactive mode: The pick and drag tool and orbiting camera via the middle mouse button is enabled.

  • 1 - Game mode: The pick and drag tool and orbiting camera via the middle mouse button is disabled.

Parameters:

  • value (Integer)

    Pass one of the following values:

Since:

  • 1.0.0



327
328
329
# File 'RubyExtension/MSPhysics/simulation.rb', line 327

def mode=(value)
  @mode = value == 1 ? 1 : 0
end

#particles_countInteger

Get number of particles.

Returns:

  • (Integer)

Since:

  • 1.0.0



1628
1629
1630
# File 'RubyExtension/MSPhysics/simulation.rb', line 1628

def particles_count
  @particles.size + MSPhysics::C::Particle.size
end

#particles_visible=(state) ⇒ Object

Show/hide particles.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



1649
1650
1651
1652
1653
1654
1655
1656
# File 'RubyExtension/MSPhysics/simulation.rb', line 1649

def particles_visible=(state)
  state = state ? true : false
  @particles_visible = state
  @particles.each { |opts|
    next if opts[:type] == 1
    opts[:group].visible = state if opts[:group].valid? && opts[:group].visible? != state
  }
end

#particles_visible?Boolean

Determine whether particles are visible.

Returns:

  • (Boolean)

Since:

  • 1.0.0



1660
1661
1662
# File 'RubyExtension/MSPhysics/simulation.rb', line 1660

def particles_visible?
  @particles_visible
end

#pauseBoolean

Pause simulation.

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



252
253
254
255
256
257
# File 'RubyExtension/MSPhysics/simulation.rb', line 252

def pause
  return false if @paused
  @paused = true
  call_event(:onPause)
  true
end

#paused?Boolean

Determine if simulation is paused.

Returns:

  • (Boolean)

Since:

  • 1.0.0



273
274
275
# File 'RubyExtension/MSPhysics/simulation.rb', line 273

def paused?
  @paused
end

#playBoolean

Play simulation.

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



243
244
245
246
247
248
# File 'RubyExtension/MSPhysics/simulation.rb', line 243

def play
  return false unless @paused
  @paused = false
  call_event(:onPlay)
  true
end

#play_midi_note(instrument, note = 63, channel = 0, volume = 127) ⇒ Integer?

Note:

Setting channel to 9 will play midi notes from the “General MIDI Percussion Key Map.” Any other channel will play midi notes from the “General MIDI Instrument Patch Map”. If channel is set to 9, the instrument parameter will have no effect and the note parameter will be used to play particular percussion sound, if note’s value is between 27 and 87. According to my experiments, values outside that 27-87 range won’t yield any sounds.

Note:

Some instruments have notes that never seem to end. For this reason it might come in handy to use #stop_midi_note function when needed.

Play MIDI note.

Parameters:

  • instrument (Integer)

    A value between 0 and 127. See link below for supported instruments and their identifiers.

  • note (Integer) (defaults to: 63)

    A value between 0 and 127. Each instrument has a maximum of 128 notes.

  • channel (Integer) (defaults to: 0)

    A value between 0 and 15. Each note has a maximum of 16 channels. To play multiple sounds of same type at the same time, change channel value to an unused one. Remember that channel 9 is subjected to different instrument patch and it will change the behaviour of this function; see note above.

  • volume (Integer) (defaults to: 127)

    A value between 0 and 127. 0 means quiet/far and 127 means close/loud.

Returns:

  • (Integer, nil)

    Midi note ID or nil if MIDI interface failed to play the note.

See Also:

Since:

  • 1.0.0



1421
1422
1423
# File 'RubyExtension/MSPhysics/simulation.rb', line 1421

def play_midi_note(instrument, note = 63, channel = 0, volume = 127)
  AMS::MIDI.play_note(instrument, note, channel, volume);
end

#play_music(name, repeat = 0) ⇒ Boolean

Play embedded music by name. This can load WAVE, AIFF, RIFF, OGG, FLAC, MOD, IT, XM, and S3M formats.

Examples:

Start playing music when simulation starts.

onStart {
  simulation.play_music("MyBackgroundMusic", -1)
}

Parameters:

  • name (String)

    The name of embedded music.

  • repeat (Integer) (defaults to: 0)

    The number of times to play the music plus one. Pass -1 to play music infinite times.

Returns:

  • (Boolean)

    success

Raises:

  • (TypeError)

    if music is invalid.

Since:

  • 1.0.0



1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
# File 'RubyExtension/MSPhysics/simulation.rb', line 1358

def play_music(name, repeat = 0)
  music = MSPhysics::Music.get_by_name(name)
  unless music
    type = Sketchup.active_model.get_attribute('MSPhysics Sound Types', name, nil)
    unless type
      raise(TypeError, "Music with name \"#{name}\" doesn't exist!", caller)
    end
    unless MSPhysics::EMBEDDED_MUSIC_FORMATS.include?(type)
      raise(TypeError, "Music format is not supported!", caller)
    end
    data = Sketchup.active_model.get_attribute('MSPhysics Sounds', name, nil)
    unless data
      raise(TypeError, "Music with name \"#{name}\" doesn't exist!", caller)
    end
    buf = data.pack('l*')
    music = MSPhysics::Music.create_from_buffer(buf, buf.size)
    MSPhysics::Music.set_name(music, name)
  end
  MSPhysics::Music.play(music, repeat)
end

#play_music2(path, repeat = 0) ⇒ Boolean

Play music from path. This can load WAVE, AIFF, RIFF, OGG, FLAC, MOD, IT, XM, and S3M formats.

Parameters:

  • path (String)

    Full path of the music.

  • repeat (Integer) (defaults to: 0)

    The number of times to play the music plus one. Pass -1 to play music infinite times.

Returns:

  • (Boolean)

    success

Raises:

  • (TypeError)

    if music is invalid.

Since:

  • 1.0.0



1386
1387
1388
1389
# File 'RubyExtension/MSPhysics/simulation.rb', line 1386

def play_music2(path, repeat = 0)
  music = MSPhysics::Music.create_from_dir(path)
  MSPhysics::Music.play(music, repeat)
end

#play_sound(name, channel = -1,, repeat = 0) ⇒ Integer?

Note:

If this function succeeds, it returns a channel the sound was registered to play on. The returned channel can be adjusted to desired volume and panning.

Note:

On Windows, this can load WAVE, AIFF, RIFF, OGG, and VOC formats. Mac OS X is limited to WAVE sounds.

Play embedded sound by name.

Examples:

Play 3D effect when space is pressed.

onKeyDown { |key, value, char|
  if key == 'space'
    channel = simulation.play_sound("MyEffect1", -1, 0)
    max_hearing_range = 1000 # Set hearing range to 1000 meters.
    simulation.position_sound(channel, this.get_position(1), max_hearing_range)
  end
}

Parameters:

  • name (String)

    The name of embedded sound.

  • channel (Integer) (defaults to: -1,)

    The channel to play the sound at. Pass -1 to play sound at the available channel.

  • repeat (Integer) (defaults to: 0)

    The number of times to play the sound plus one. Pass -1 to play sound infinite times.

Returns:

  • (Integer, nil)

    A channel the sound is set to be played on or nil if mixer failed to play sound.

Raises:

  • (TypeError)

    if sound is invalid.

Since:

  • 1.0.0



1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
# File 'RubyExtension/MSPhysics/simulation.rb', line 1284

def play_sound(name, channel = -1, repeat = 0)
  sound = MSPhysics::Sound.get_by_name(name)
  unless sound
    type = Sketchup.active_model.get_attribute('MSPhysics Sound Types', name, nil)
    unless type
      raise(TypeError, "Sound with name \"#{name}\" doesn't exist!", caller)
    end
    unless MSPhysics::EMBEDDED_SOUND_FORMATS.include?(type)
      raise(TypeError, "Sound format is not supported!", caller)
    end
    data = Sketchup.active_model.get_attribute('MSPhysics Sounds', name, nil)
    unless data
      raise(TypeError, "Sound with name \"#{name}\" doesn't exist!", caller)
    end
    buf = data.pack('l*')
    sound = MSPhysics::Sound.create_from_buffer(buf, buf.size)
    MSPhysics::Sound.set_name(sound, name)
  end
  MSPhysics::Sound.play(sound, channel, repeat)
end

#play_sound2(path, channel = -1,, repeat = 0) ⇒ Integer?

Note:

If this function succeeds, it returns a channel the sound was registered to play on. The returned channel can be adjusted to desired volume and panning.

Note:

On Windows, this can load WAVE, AIFF, RIFF, OGG, VOC, and FLAC formats. Mac OS X is limited to WAVE sounds.

Play sound from path.

Parameters:

  • path (String)

    Full path of the sound.

  • channel (Integer) (defaults to: -1,)

    The channel to play the sound at. Pass -1 to play sound at the available channel.

  • repeat (Integer) (defaults to: 0)

    The number of times to play the sound plus one. Pass -1 to play sound infinite times.

Returns:

  • (Integer, nil)

    A channel the sound is set to be played on or nil if mixer failed to play sound.

Raises:

  • (TypeError)

    if sound is invalid.

Since:

  • 1.0.0



1319
1320
1321
1322
# File 'RubyExtension/MSPhysics/simulation.rb', line 1319

def play_sound2(path, channel = -1, repeat = 0)
  sound = MSPhysics::Sound.create_from_dir(path)
  MSPhysics::Sound.play(sound, channel, repeat)
end

#playing?Boolean

Determine if simulation is playing.

Returns:

  • (Boolean)

Since:

  • 1.0.0



267
268
269
# File 'RubyExtension/MSPhysics/simulation.rb', line 267

def playing?
  !@paused
end

#position_midi_note(id, pos, max_hearing_range = 100) ⇒ Boolean

Note:

Sound volume and panning is not adjusted automatically with respect to camera orientation. It is required to manually call this function every frame until the note is stopped or has finished playing. Sometimes it’s just enough to call this function once after playing the note. Other times, when the note is endless or pretty long, it might be useful to update position of the note every frame until the note ends or is stopped. Meantime, there is no function to determine when the note ends. It is up to the user to decide for how long to call this function or when to stop calling this function.

Note:

When it comes to setting 3D positions of multiple sounds, make sure to play each sound on separate channel. That is, play sound 1 on channel 0, sound 2 on channel 1, sound 3 on channel 2, and etcetera until channel gets to 15, as there are only 15 channels available. Read the note below to find out why each sound is supposed to be played on separate channel. I think it would make more sense if the function was renamed to set_midi_channel_position and had the ‘id’ parameter replaced with ‘channel’.

Note:

This function works by adjusting panning and volume of the note’s and instrument’s channel, based on camera’s angle and distance to the origin of the sound. Now, there is only one function that adjusts stereo and panning, but it adjusts panning and volume of all notes and instruments that are played on same channel. As of my research, I haven’t found a way to adjust panning and volume of channel that belongs to particular note and instrument. There’s only a function that can adjust panning and volume of channel that belongs to all notes and instruments that are played on particular channel. For instance, if you play instrument 1 and instrument 2 both on channel zero, they will still play simultaneously, without cancelling out each other, as if they are playing on separate channels, but when it comes to adjusting panning and volume of one of them, the properties of both sounds will be adjusted. This means that this function is only limited to playing 16 3D sounds, with each sound played on different channel. Otherwise, sounds played on same channel at different locations, will endup being tuned as if they are playing from the same location.

Set MIDI note position in 3D space.

Examples:

Play 3D note.

onKeyDown { |k,v,c|
  if k == 'space'
    id = simulation.play_midi_note(2, 63, 0, 127)
    simulation.position_midi_note(id, this.get_position(1), 100) if id
  end
}

Parameters:

  • id (Integer)

    A MIDI note identifier returned by the #play_midi_note function.

  • pos (Geom::Point3d, Array<Numeric>)

    MIDI note position in global space.

  • max_hearing_range (Numeric) (defaults to: 100)

    MIDI note maximum hearing range in meters.

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



1489
1490
1491
# File 'RubyExtension/MSPhysics/simulation.rb', line 1489

def position_midi_note(id, pos, max_hearing_range = 100)
  AMS::MIDI.set_note_position(id, pos, max_hearing_range)
end

#position_sound(channel, pos, max_hearing_range = 100) ⇒ Boolean

Note:

Sound volume and panning is adjusted automatically with respect to camera orientation. You don’t need to call this function every frame if sound remains in constant position. You do, however, need to call this function if sound position changes.

Set sound 3D position.

Parameters:

  • channel (Integer)

    The channel the sound is being played on.

  • pos (Geom::Point3d, Array<Numeric>)

    Sound position in global space.

  • max_hearing_range (Numeric) (defaults to: 100)

    The maximum hearing range of the sound in meters.

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



1343
1344
1345
# File 'RubyExtension/MSPhysics/simulation.rb', line 1343

def position_sound(channel, pos, max_hearing_range = 100)
  MSPhysics::Sound.set_position_3d(channel, pos, max_hearing_range)
end

#remove_group(group) ⇒ Boolean

Remove a group/component from simulation.

Parameters:

  • group (Sketchup::Group, Sketchup::ComponentInstance)

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



873
874
875
876
877
878
879
# File 'RubyExtension/MSPhysics/simulation.rb', line 873

def remove_group(group)
  AMS.validate_type(group, Sketchup::Group, Sketchup::ComponentInstance)
  body = find_body_by_group(group)
  return false unless body
  body.destroy
  true
end

#reset_camera_on_end=(state) ⇒ Object

Enable/disable the reseting of camera when simulation ends.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



1703
1704
1705
# File 'RubyExtension/MSPhysics/simulation.rb', line 1703

def reset_camera_on_end=(state)
  @reset_camera_on_end = state ? true : false
end

#reset_camera_on_end?Boolean

Determine whether the reseting of camera when simulation ends is enabled.

Returns:

  • (Boolean)

Since:

  • 1.0.0



1697
1698
1699
# File 'RubyExtension/MSPhysics/simulation.rb', line 1697

def reset_camera_on_end?
  @reset_camera_on_end
end

#reset_positions_on_end=(state) ⇒ Object

Enable/disable the reseting of group/component transformations when simulation ends.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



1691
1692
1693
# File 'RubyExtension/MSPhysics/simulation.rb', line 1691

def reset_positions_on_end=(state)
  @reset_positions_on_end = state ? true : false
end

#reset_positions_on_end?Boolean

Determine whether the reseting of group/component transformations when simulation ends is enabled.

Returns:

  • (Boolean)

Since:

  • 1.0.0



1684
1685
1686
# File 'RubyExtension/MSPhysics/simulation.rb', line 1684

def reset_positions_on_end?
  @reset_positions_on_end
end

#scenes_animating?Boolean

Determine whether the scenes animation is active.

Returns:

  • (Boolean)

Since:

  • 1.0.0



398
399
400
401
402
# File 'RubyExtension/MSPhysics/simulation.rb', line 398

def scenes_animating?
  return false if @scene_anim_info[:state] == 0
  et = @scene_anim_info[:ref_time] - MSPhysics::Settings.animate_scenes_delay
  return et >= 0
end

#scenes_transitioning?Boolean

Determine whether the scene transitioning is active.

Returns:

  • (Boolean)

Since:

  • 1.0.0



406
407
408
# File 'RubyExtension/MSPhysics/simulation.rb', line 406

def scenes_transitioning?
  @scene_info[:active]
end

#set_cursor_pos(x, y) ⇒ Object

Note:

Windows only!

Set cursor position in view coordinates.

Parameters:

  • x (Integer)
  • y (Integer)

Since:

  • 1.0.0



367
368
369
370
371
372
373
# File 'RubyExtension/MSPhysics/simulation.rb', line 367

def set_cursor_pos(x, y)
  if AMS::IS_PLATFORM_WINDOWS
    @cursor_pos.x = x
    @cursor_pos.y = y
    AMS::Cursor.set_pos(x, y, 2)
  end
end

#started_from_selection?Boolean

Determine whether simulation started from selection.

Returns:

  • (Boolean)

    true if simulation started from selection; false if simulation started with all groups being considered.

Since:

  • 1.0.0



237
238
239
# File 'RubyExtension/MSPhysics/simulation.rb', line 237

def started_from_selection?
  @started_from_selection
end

#stop_midi_note(id) ⇒ Boolean

Stop MIDI note.

Parameters:

  • id (Integer)

    A MIDI note identifier returned by the #play_midi_note function. Pass -1 to stop all midi notes.

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
# File 'RubyExtension/MSPhysics/simulation.rb', line 1429

def stop_midi_note(id)
  if id == -1
    for i in 0..15
      AMS::MIDI.change_channel_controller(i, 0x7B, 0)
    end
    AMS::MIDI.reset
  else
    AMS::MIDI.stop_note(id)
  end
end

#stop_musicnil

Stop the currently playing music.

Returns:

  • (nil)

Since:

  • 1.0.0



1393
1394
1395
# File 'RubyExtension/MSPhysics/simulation.rb', line 1393

def stop_music
  MSPhysics::Music.stop
end

#stop_sound(channel) ⇒ Boolean

Stop the currently playing sound at channel.

Parameters:

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



1328
1329
1330
1331
# File 'RubyExtension/MSPhysics/simulation.rb', line 1328

def stop_sound(channel)
  MSPhysics::Sound.stop(channel)
  true
end

#toggle_playBoolean

Play/pause simulation.

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



261
262
263
# File 'RubyExtension/MSPhysics/simulation.rb', line 261

def toggle_play
  @paused ? play : pause
end

#undo_on_reset=(state) ⇒ Object

Enable/disable the undo commend that is ought to be triggered after the simulation resets.

Parameters:

  • state (Boolean)

Since:

  • 1.0.0



1677
1678
1679
# File 'RubyExtension/MSPhysics/simulation.rb', line 1677

def undo_on_reset=(state)
  @undo_on_reset = state ? true : false
end

#undo_on_reset?Boolean

Determine whether the undo command is ought to be triggered after the simulation resets.

Returns:

  • (Boolean)

Since:

  • 1.0.0



1670
1671
1672
# File 'RubyExtension/MSPhysics/simulation.rb', line 1670

def undo_on_reset?
  @undo_on_reset
end

#update_rateInteger

Get simulation update rate, the number of times to update newton world per frame.

Returns:

  • (Integer)

    A value between 1 and 100.

Since:

  • 1.0.0



280
281
282
# File 'RubyExtension/MSPhysics/simulation.rb', line 280

def update_rate
  @update_rate
end

#update_rate=(rate) ⇒ Object

Set simulation update rate, the number of times to update newton world per frame.

Parameters:

  • rate (Integer)

    A value between 1 and 100.

Since:

  • 1.0.0



287
288
289
# File 'RubyExtension/MSPhysics/simulation.rb', line 287

def update_rate=(rate)
  @update_rate = AMS.clamp(rate.to_i, 1, 100)
end

#update_timestepNumeric

Get simulation update timestep in seconds.

Returns:

  • (Numeric)

Since:

  • 1.0.0



299
300
301
# File 'RubyExtension/MSPhysics/simulation.rb', line 299

def update_timestep
  @update_timestep
end

#update_timestep=(timestep) ⇒ Object

Set simulation update time step in seconds.

Parameters:

  • timestep (Numeric)

    This value is clamped between 1/1200.0 and 1/30.0. Normal update time step is 1/60.0.

Since:

  • 1.0.0



306
307
308
309
# File 'RubyExtension/MSPhysics/simulation.rb', line 306

def update_timestep=(timestep)
  @update_timestep = AMS.clamp(timestep, 1/1200.0, 1/30.0)
  @update_timestep_inv = 1.0 / @update_timestep
end

#update_timestep_invNumeric

Get the inverse of simulation update timestep.

Returns:

  • (Numeric)

Since:

  • 1.0.0



293
294
295
# File 'RubyExtension/MSPhysics/simulation.rb', line 293

def update_timestep_inv
  @update_timestep_inv
end

#view_full_screen(state, include_floating_windows = true) ⇒ Boolean

Note:

Windows only!

Set view full screen.

Examples:

onStart {
  simulation.view_full_screen(true)
}
onEnd {
  simulation.view_full_screen(false)
}

Parameters:

  • state (Boolean)
  • include_floating_windows (Boolean) (defaults to: true)

    Whether to include floating toolbars and windows in the operation.

Returns:

  • (Boolean)

    success

Since:

  • 1.0.0



498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
# File 'RubyExtension/MSPhysics/simulation.rb', line 498

def view_full_screen(state, include_floating_windows = true)
  return false unless AMS::IS_PLATFORM_WINDOWS
  AMS::Sketchup.show_toolbar_container(5, !state, false)
  AMS::Sketchup.show_scenes_bar(!state, false)
  AMS::Sketchup.show_status_bar(!state, false)
  AMS::Sketchup.set_viewport_border(!state)
  r1 = AMS::Sketchup.set_menu_bar(!state)
  r2 = AMS::Sketchup.switch_full_screen(state)
  AMS::Sketchup.refresh unless r1 || r2
  if include_floating_windows
    AMS::Sketchup.show_dialogs(!state)
    AMS::Sketchup.show_toolbars(!state)
  end
  @fullscreen_note[:time] = state ? 0.0 : nil
  true
end