From 4011c87131f4c79cb8574db063daf8e5b12fced8 Mon Sep 17 00:00:00 2001 From: Lukas Rambold Date: Mon, 24 Feb 2020 16:54:27 +0100 Subject: [PATCH 01/11] disconnect two sides in modelica seesaw model --- src/system_simulation/seesaw3.mo | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/system_simulation/seesaw3.mo b/src/system_simulation/seesaw3.mo index 84ea53da..bdb77e62 100644 --- a/src/system_simulation/seesaw3.mo +++ b/src/system_simulation/seesaw3.mo @@ -58,11 +58,11 @@ model seesaw3 Placement(visible = true, transformation(origin = {-108, -108}, extent = {{-10, -10}, {10, 10}}, rotation = 90))); Modelica.Mechanics.MultiBody.Parts.Fixed fixed2(animation = false, r = N[2]) annotation( Placement(visible = true, transformation(origin = {74, -108}, extent = {{-10, -10}, {10, 10}}, rotation = 90))); - Modelica.Mechanics.MultiBody.Joints.Revolute revLeft(a(fixed = true), n = N[5] - N[7], w(fixed = true)) annotation( + Modelica.Mechanics.MultiBody.Joints.Revolute revLeft(a(fixed = false), n = N[5] - N[7], w(fixed = false)) annotation( Placement(visible = true, transformation(origin = {-72, -52}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Mechanics.MultiBody.Joints.Revolute revRight(n = N[6] - N[4]) annotation( Placement(visible = true, transformation(origin = {98, -36}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); - Modelica.Mechanics.MultiBody.Parts.PointMass childLeft(a_0(fixed = false),m = 70, r_0(fixed = false, start = N[20]), v_0(fixed = false, start = {0, 0, 0})) annotation( + Modelica.Mechanics.MultiBody.Parts.PointMass childLeft(a_0(fixed = false),m = 70, r_0(fixed = true, start = N[20]), v_0(fixed = false, start = {0, 0, 0})) annotation( Placement(visible = true, transformation(origin = {-6, -40}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Mechanics.MultiBody.Parts.FixedTranslation fixedTranslation(animation = false, r = N[5] - N[3]) annotation( Placement(visible = true, transformation(origin = {-108, -74}, extent = {{10, -10}, {-10, 10}}, rotation = -90))); @@ -78,16 +78,14 @@ model seesaw3 Placement(visible = true, transformation(origin = {142, -46}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Mechanics.MultiBody.Parts.BodyCylinder bodyCylinder4(r = (-N[5]) + N[10]) annotation( Placement(visible = true, transformation(origin = {-32, -80}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); - Modelica.Mechanics.MultiBody.Forces.SpringDamperParallel springDamperParallel1(c = 20000, d = 100, s_unstretched = .3) annotation( + Modelica.Mechanics.MultiBody.Forces.SpringDamperParallel springDamperParallel1(c = 7000, d = 100, s_unstretched = .7) annotation( Placement(visible = true, transformation(origin = {-62, -106}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Mechanics.MultiBody.Parts.BodyCylinder bodyCylinder5(r = (-N[6]) + N[9]) annotation( Placement(visible = true, transformation(origin = {136, -64}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); - Modelica.Mechanics.MultiBody.Forces.SpringDamperParallel springDamperParallel2(c = 20000, d = 100, s_unstretched = .3) annotation( + Modelica.Mechanics.MultiBody.Forces.SpringDamperParallel springDamperParallel2(c = 7000, d = 100, s_unstretched = .7) annotation( Placement(visible = true, transformation(origin = {138, -98}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Mechanics.MultiBody.Parts.FixedTranslation fixedTranslation1(animation = false, r = N[6] -N[2]) annotation( Placement(visible = true, transformation(origin = {74, -56}, extent = {{10, -10}, {-10, 10}}, rotation = -90))); - Modelica.Mechanics.MultiBody.Forces.SpringDamperParallel springDamperParallel(c = 10000, d = 10, s_unstretched = 1) annotation( - Placement(visible = true, transformation(origin = {48, -76}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Mechanics.MultiBody.Visualizers.Advanced.Arrow arrow( r_head = node_pos[15] - N[15], r_tail = N[15]) annotation( Placement(visible = true, transformation(origin = {-38, 2}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Mechanics.MultiBody.Visualizers.Advanced.Arrow arrow1(r_head = node_pos[14] - N[14], r_tail = N[14]) annotation( @@ -125,10 +123,6 @@ equation Line(points = {{74, -66}, {74, -66}, {74, -98}, {74, -98}})); connect(fixedTranslation1.frame_b, revRight.frame_a) annotation( Line(points = {{74, -46}, {74, -46}, {74, -36}, {88, -36}, {88, -36}})); - connect(bodyCylinder3.frame_b, springDamperParallel.frame_b) annotation( - Line(points = {{152, -46}, {156, -46}, {156, -76}, {58, -76}}, color = {95, 95, 95})); - connect(bodyCylinder.frame_b, springDamperParallel.frame_a) annotation( - Line(points = {{-22, -60}, {24, -60}, {24, -78}, {38, -78}, {38, -76}}, color = {95, 95, 95})); annotation( uses(Modelica(version = "3.2.2"))); end seesaw3; \ No newline at end of file From 987f28a37b6d2c021b432f4121555ddabcda8527 Mon Sep 17 00:00:00 2001 From: birneamstiel Date: Wed, 26 Feb 2020 20:00:10 +0100 Subject: [PATCH 02/11] Move spring pane into top level truss fab class. --- src/ui/dialogs/sidebar.rb | 1 + truss_fab.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/ui/dialogs/sidebar.rb b/src/ui/dialogs/sidebar.rb index 409fecef..4dc030e9 100644 --- a/src/ui/dialogs/sidebar.rb +++ b/src/ui/dialogs/sidebar.rb @@ -5,6 +5,7 @@ class Sidebar attr_reader :width, :height, :top, :left attr_accessor :animation_pane + attr_accessor :spring_pane HTML_FILE = '../sidebar/index.html'.freeze diff --git a/truss_fab.rb b/truss_fab.rb index 8b834d2c..d9af0a7f 100644 --- a/truss_fab.rb +++ b/truss_fab.rb @@ -31,6 +31,7 @@ # ui files require 'src/ui/dialogs/animation_pane' +require 'src/ui/dialogs/spring_pane' require 'src/ui/dialogs/sidebar' require 'src/ui/dialogs/component_properties' require 'src/models/spirix' @@ -43,6 +44,7 @@ module TrussFab @reloader = Reloader.new @sidebar_menu = Sidebar.new @animation_pane = AnimationPane.new + @spring_pane = SpringPane.new @store_sensor_output = false class << self @@ -60,9 +62,12 @@ def start @sidebar_menu.open_dialog @animation_pane.open_dialog(@sidebar_menu.width + @sidebar_menu.left, @sidebar_menu.height + @sidebar_menu.top) + @spring_pane.open_dialog @sidebar_menu.animation_pane = @animation_pane @animation_pane.sidebar_menu = @sidebar_menu + + @sidebar_menu.spring_pane = @spring_pane end def stop From da0f27bf21007e2d2895d3f4aa2fc4673edeb48e Mon Sep 17 00:00:00 2001 From: birneamstiel Date: Wed, 26 Feb 2020 20:01:54 +0100 Subject: [PATCH 03/11] Make spring pane responsible for updating springs in graph. --- src/sketchup_objects/spring_link.rb | 3 +- src/tools/demonstrate_amplitude_tool.rb | 32 ++++++++-- src/tools/spring_tool.rb | 5 ++ src/ui/dialogs/spring_pane.rb | 81 +++++++++++++++++++++---- src/ui/spring-pane/index.erb | 17 ++++-- 5 files changed, 116 insertions(+), 22 deletions(-) diff --git a/src/sketchup_objects/spring_link.rb b/src/sketchup_objects/spring_link.rb index b6bcbc3d..860ccbac 100644 --- a/src/sketchup_objects/spring_link.rb +++ b/src/sketchup_objects/spring_link.rb @@ -3,7 +3,8 @@ # PhysicsLink that behaves like a gas spring class SpringLink < ActuatorLink - attr_reader :spring_parameter_k + attr_accessor :spring_parameter_k + attr_reader :edge def initialize(first_node, second_node, edge, id: nil) @spring_parameter_k = 200 diff --git a/src/tools/demonstrate_amplitude_tool.rb b/src/tools/demonstrate_amplitude_tool.rb index 540949b9..db9cfa0b 100644 --- a/src/tools/demonstrate_amplitude_tool.rb +++ b/src/tools/demonstrate_amplitude_tool.rb @@ -35,24 +35,29 @@ def onLButtonUp(_flags, x, y, view) update(view, x, y) return unless @moving return if @start_node.nil? + edge_id_to_spring_id = {13 => 25, 6 => 21} + + hinge_edge = get_hinge_edge(@start_node) + hinge_center = hinge_edge.mid_point - hinge_center = get_hinge_edge(@start_node).mid_point initial_vector = @start_position - hinge_center max_amplitude_vector = @end_position - hinge_center # user inputs only half of the amplitude since we want to have the oscillation symmetric around the equililbirum. angle = 2 * initial_vector.angle_between(max_amplitude_vector) - @constant = @simulation_runner.constant_for_constrained_angle(angle) + constant = @simulation_runner.constant_for_constrained_angle(angle) + @ui.spring_pane.update_constant_for_spring(relevant_spring_id_for_node(@start_node), constant) simulate - equilibrium_index = @simulation_runner.find_equilibrium(@constant) - set_graph_to_data_sample(equilibrium_index) + #equilibrium_index = @simulation_runner.find_equilibrium(@constant) + #set_graph_to_data_sample(equilibrium_index) + @trace_visualization.reset_trace @trace_visualization.add_trace(['18', '20'], 4, @simulation_data) - view.invalidate reset end + def update(view, x, y) @mouse_input.update_positions(view, x, y, point_on_plane_from_camera_normal: @start_position || nil) @@ -80,6 +85,23 @@ def draw(view) private + # TODO: probably a bit inefficient, we should think about a hash like data structure to store springs for static groups + # Returns the spring that makes the static group the node is in movable. + def relevant_spring_id_for_node(node) + static_groups = StaticGroupAnalysis.get_static_groups_for_node(node) + raise 'No satic groups detected' unless static_groups + + static_group = static_groups[0] + all_spring_edges = Graph.instance.edges.values.select { |edge| edge.link_type == 'spring' } + spring_edges = all_spring_edges.select do |spring_edge| + static_group.include?(spring_edge.first_node) || static_group.include?(spring_edge.second_node) + end + raise 'no spring found for group' if spring_edges.empty? + raise 'more than one spring found for group' if spring_edges.size > 1 + spring_edges[0].id + + end + def get_hinge_edge(node) static_groups = StaticGroupAnalysis.find_static_groups # get first static group the node is in, should be only one anyways diff --git a/src/tools/spring_tool.rb b/src/tools/spring_tool.rb index 2b0d4004..c1e93188 100644 --- a/src/tools/spring_tool.rb +++ b/src/tools/spring_tool.rb @@ -5,4 +5,9 @@ class SpringTool < ActuatorTool def initialize(ui) super(ui, 'spring') end + + def onLButtonDown(flags, x, y, view) + super(flags, x, y, view) + @ui.spring_pane.update_springs + end end diff --git a/src/ui/dialogs/spring_pane.rb b/src/ui/dialogs/spring_pane.rb index ee59825d..12027151 100644 --- a/src/ui/dialogs/spring_pane.rb +++ b/src/ui/dialogs/spring_pane.rb @@ -2,22 +2,50 @@ class SpringPane INSIGHTS_HTML_FILE = '../spring-pane/index.erb'.freeze - def initialize(refresh_callback, toggle_animation_callback) - @refresh_callback = refresh_callback - @toggle_animation_callback = toggle_animation_callback + def initialize + @refresh_callback = nil + @toggle_animation_callback = nil - @spring_links = Graph.instance.edges.values. - select { |edge| edge.link_type == 'spring' }. - map(&:link) + update_springs + + @animation = nil + @simulation_runner = nil @dialog = nil open_dialog + + end + + def update_constant_for_spring(spring_id, new_constant) + edge = @spring_edges.find { |edge| edge.id == spring_id } + edge.link.spring_parameter_k = new_constant + update_springs + end + + def update_springs + @spring_edges = Graph.instance.edges.values.select { |edge| edge.link_type == 'spring' } + update_dialog if @dialog end def set_period(value) @dialog.execute_script("set_period(#{value})") end + def set_constant(value, spring_id = 25) + @dialog.execute_script("set_constant(#{spring_id},#{value})") + end + + # TODO: should probably always be called when a link is changed... e.g also in actuator tool + def update_dialog + # load updated html + file_path = File.join(File.dirname(__FILE__), INSIGHTS_HTML_FILE) + content = File.read(file_path) + t = ERB.new(content) + + # display updated html + @dialog.set_html(t.result(binding)) + end + def open_dialog return if @dialog && @dialog.visible? @@ -25,7 +53,7 @@ def open_dialog resizable: true, preferences_key: 'com.trussfab.spring_insights', width: 200, - height: 50 + @spring_links.length * 200, + height: 50 + @spring_edges.length * 200, left: 5, top: 5, # max_height: @height @@ -39,19 +67,48 @@ def open_dialog @dialog.set_html(t.result(binding)) @dialog.set_position(500, 500) @dialog.show - register_insights_callbacks + register_callbacks end private - def register_insights_callbacks - @dialog.add_action_callback('spring_insights_change') do |_, spring_id, value| - @refresh_callback.call(spring_id, value) + def register_callbacks + @dialog.add_action_callback('spring_constants_change') do |_, spring_id, value| + update_constant_for_spring(spring_id, value.to_i) + end + + @dialog.add_action_callback('spring_insights_compile') do + try_compile end @dialog.add_action_callback('spring_insights_toggle_play') do - @toggle_animation_callback.call + toggle_animation + end + end + + def try_compile + @simulation_runner ||= SimulationRunner.instance + @simulation_data ||= simulate + end + + def simulate + @simulation_data = @simulation_runner.get_hub_time_series + end + + + def toggle_animation + simulate + if @animation && @animation.running + @animation.toggle_running + else + create_animation end + + end + + def create_animation + @animation = GeometryAnimation.new(@simulation_data) + Sketchup.active_model.active_view.animation = @animation end end diff --git a/src/ui/spring-pane/index.erb b/src/ui/spring-pane/index.erb index 5b766b4f..cd828732 100644 --- a/src/ui/spring-pane/index.erb +++ b/src/ui/spring-pane/index.erb @@ -11,7 +11,8 @@ document.getElementById('period').value = value; } function set_constant(id, value) { - document.getElementById('spring_constant_' + id).value = value; + document.getElementById('spring_constant_' + id).placeholder = value; + document.getElementById('spring_constant_range_' + id).value = value; } @@ -21,21 +22,21 @@
Modify spring parameters
- <% @spring_links.each { |spring_link| %> + <% @spring_edges.each { |spring_edge| %>
k
- +
N/m
- +
@@ -59,6 +60,14 @@
+
+
+ +
+
+ From d5a1cf5f0a754cf525fede9241ded9a602671574 Mon Sep 17 00:00:00 2001 From: birneamstiel Date: Wed, 26 Feb 2020 20:02:29 +0100 Subject: [PATCH 04/11] Update simulation runner to changed spring update control flow. --- src/system_simulation/simulation_runner.rb | 30 +++++++++++++++++----- src/tools/spring_simulation_tool.rb | 7 ++--- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/system_simulation/simulation_runner.rb b/src/system_simulation/simulation_runner.rb index c1d5ee87..2d5005c3 100755 --- a/src/system_simulation/simulation_runner.rb +++ b/src/system_simulation/simulation_runner.rb @@ -25,11 +25,15 @@ def initialize(suppress_compilation = false, keep_temp_dir = false) run_compilation end + + # maps spring edge id => spring constant + @constants_for_springs = { 21 => 7000, 23 => 7000 } end - def get_hub_time_series(hub_ids, step_size, mass, constant = 50) + def get_hub_time_series data = [] - simulation_time = Benchmark.realtime { run_simulation(constant, mass, 'node_pos.*') } + update_spring_data_from_graph + simulation_time = Benchmark.realtime { run_simulation('node_pos.*') } import_time = Benchmark.realtime { data = parse_data(read_csv) } puts("simulation time: #{simulation_time}s csv parsing time: #{import_time}s") data @@ -78,8 +82,8 @@ def find_equilibrium(constant = 50, mass = 20) raw_data.index(equilibrium_data_row) end - def constant_for_constrained_angle(allowed_angle_delta = Math::PI / 2.0, initial_constant = 500, mass = 20, - spring_id = 0, angle_id = 0) + def constant_for_constrained_angle(allowed_angle_delta = Math::PI / 2.0, spring_id = 25, initial_constant = 500, mass = 20, + angle_id = 0) # steps which the algorithm uses to approximate the valid spring constant step_sizes = [1500, 1000, 200, 50, 5] constant = initial_constant @@ -88,7 +92,8 @@ def constant_for_constrained_angle(allowed_angle_delta = Math::PI / 2.0, initial abort_threshold = 50_000 while keep_searching # puts "Current k: #{constant} Step size: #{step_size}" - run_simulation(constant, mass, 'revLeft.phi') + @constants_for_springs[spring_id] = constant + run_simulation('revLeft.phi') if !angle_valid(read_csv, allowed_angle_delta) # increase spring constant to decrease angle delta constant += step_size @@ -110,6 +115,15 @@ def constant_for_constrained_angle(allowed_angle_delta = Math::PI / 2.0, initial private + def update_spring_data_from_graph + spring_links = Graph.instance.edges.values. + select { |edge| edge.link_type == 'spring' }. + map(&:link) + spring_links.each do |link| + @constants_for_springs[link.edge.id] = link.spring_parameter_k + end + end + def angle_valid(data, max_allowed_delta = Math::PI / 2.0) data = data.map { |data_sample| data_sample[1].to_f } # remove initial data point @@ -128,9 +142,11 @@ def run_compilation p output end - def run_simulation(constant, mass, filter = '*') + def run_simulation(filter = '*') # TODO adjust sampling rate dynamically - overrides = "outputFormat='csv',variableFilter='#{filter}',startTime=0.3,stopTime=10,stepSize=0.1,springDamperParallel1.c='#{constant}'" + constant1 = @constants_for_springs[25] + constant2 = @constants_for_springs[21] + overrides = "outputFormat='csv',variableFilter='#{filter}',startTime=0.0,stopTime=10,stepSize=0.1,springDamperParallel1.c='#{constant1}',springDamperParallel2.c='#{constant2}'" command = "./#{@model_name} -override #{overrides}" puts(command) Open3.popen2e(command, chdir: @directory) do |i, o, t| diff --git a/src/tools/spring_simulation_tool.rb b/src/tools/spring_simulation_tool.rb index b191c9b9..b73084ea 100644 --- a/src/tools/spring_simulation_tool.rb +++ b/src/tools/spring_simulation_tool.rb @@ -20,17 +20,14 @@ def initialize(ui) def activate # Instantiates SimulationRunner and compiles model. - @simulation_runner = SimulationRunner.instance unless @simulation_runner - @spring_links = Graph.instance.edges.values. - select { |edge| edge.link_type == 'spring' }. - map(&:link) + @simulation_runner ||= SimulationRunner.instance end private def simulate - @simulation_data = @simulation_runner.get_hub_time_series(nil, 0, 0, @constant.to_i) + @simulation_data = @simulation_runner.get_hub_time_series end def set_graph_to_data_sample(index) From 72afefda9ff84522f65c68a36d5dda4f2633d4cf Mon Sep 17 00:00:00 2001 From: birneamstiel Date: Thu, 27 Feb 2020 16:00:19 +0100 Subject: [PATCH 05/11] Move responsibility of animating/simulating/visualizing from tools into spring pane. --- src/system_simulation/simulation_runner.rb | 19 ++++-- src/tools/demonstrate_amplitude_tool.rb | 25 ++++---- src/tools/place_user_tool.rb | 67 +--------------------- src/tools/spring_simulation_tool.rb | 48 ---------------- src/ui/dialogs/spring_pane.rb | 60 +++++++++++++++++-- 5 files changed, 80 insertions(+), 139 deletions(-) delete mode 100644 src/tools/spring_simulation_tool.rb diff --git a/src/system_simulation/simulation_runner.rb b/src/system_simulation/simulation_runner.rb index 2d5005c3..720f00d6 100755 --- a/src/system_simulation/simulation_runner.rb +++ b/src/system_simulation/simulation_runner.rb @@ -27,7 +27,10 @@ def initialize(suppress_compilation = false, keep_temp_dir = false) end # maps spring edge id => spring constant - @constants_for_springs = { 21 => 7000, 23 => 7000 } + @constants_for_springs = { 21 => 7000, 25 => 7000 } + # TODO: this just mocks the mapping between springs and the corresponding revolute joint angles. Will be changed + # TODO: as soon as we generate the geometry dynamically. + @angles_for_springs = { 21 => 'revRight.phi', 25 => 'revLeft.phi' } end def get_hub_time_series @@ -63,8 +66,8 @@ def get_period(mass = 20, constant = 5000) end # Returns index of animation frame when system is in equilibrium i.e. the mean of the angle difference - def find_equilibrium(constant = 50, mass = 20) - run_simulation(constant, mass, 'revLeft.phi') + def find_equilibrium(spring_id) + run_simulation(@angles_for_springs[spring_id]) raw_data = read_csv # remove initial data point, the header @@ -82,9 +85,11 @@ def find_equilibrium(constant = 50, mass = 20) raw_data.index(equilibrium_data_row) end - def constant_for_constrained_angle(allowed_angle_delta = Math::PI / 2.0, spring_id = 25, initial_constant = 500, mass = 20, - angle_id = 0) + def constant_for_constrained_angle(allowed_angle_delta = Math::PI / 2.0, spring_id = 25, initial_constant = 500) # steps which the algorithm uses to approximate the valid spring constant + + update_spring_data_from_graph + angle_filter = @angles_for_springs[spring_id] step_sizes = [1500, 1000, 200, 50, 5] constant = initial_constant step_size = step_sizes.shift @@ -93,7 +98,7 @@ def constant_for_constrained_angle(allowed_angle_delta = Math::PI / 2.0, spring_ while keep_searching # puts "Current k: #{constant} Step size: #{step_size}" @constants_for_springs[spring_id] = constant - run_simulation('revLeft.phi') + run_simulation(angle_filter) if !angle_valid(read_csv, allowed_angle_delta) # increase spring constant to decrease angle delta constant += step_size @@ -102,6 +107,8 @@ def constant_for_constrained_angle(allowed_angle_delta = Math::PI / 2.0, spring_ constant -= step_size # reduce step size and continue step_size = step_sizes.shift + # make sure we don't exceed the sample space + constant = initial_constant if constant < initial_constant else # we reached smallest step size and found a valid spring constant, so we're done keep_searching = false diff --git a/src/tools/demonstrate_amplitude_tool.rb b/src/tools/demonstrate_amplitude_tool.rb index db9cfa0b..b9ee2ad0 100644 --- a/src/tools/demonstrate_amplitude_tool.rb +++ b/src/tools/demonstrate_amplitude_tool.rb @@ -1,8 +1,7 @@ -require_relative 'spring_simulation_tool.rb' require 'src/system_simulation/trace_visualization.rb' # Enables users to drag a line starting from a node to demonstrate the amplitude they want for the oscillation. -class DemonstrateAmplitudeTool < SpringSimulationTool +class DemonstrateAmplitudeTool < Tool def initialize(ui) super(ui) @mouse_input = MouseInput.new(snap_to_edges: true, snap_to_nodes: true) @@ -12,7 +11,12 @@ def initialize(ui) @end_position = nil @moving = false - @trace_visualization = TraceVisualization.new + @simulation_runner = nil + end + + def activate + # Instantiates SimulationRunner and compiles model. + @simulation_runner = @ui.spring_pane.try_compile end def onLButtonDown(_flags, x, y, view) @@ -22,8 +26,6 @@ def onLButtonDown(_flags, x, y, view) @moving = true @start_node = obj @start_position = @end_position = obj.position - - @trace_visualization.reset_trace end end @@ -35,7 +37,6 @@ def onLButtonUp(_flags, x, y, view) update(view, x, y) return unless @moving return if @start_node.nil? - edge_id_to_spring_id = {13 => 25, 6 => 21} hinge_edge = get_hinge_edge(@start_node) hinge_center = hinge_edge.mid_point @@ -45,14 +46,10 @@ def onLButtonUp(_flags, x, y, view) # user inputs only half of the amplitude since we want to have the oscillation symmetric around the equililbirum. angle = 2 * initial_vector.angle_between(max_amplitude_vector) - constant = @simulation_runner.constant_for_constrained_angle(angle) - @ui.spring_pane.update_constant_for_spring(relevant_spring_id_for_node(@start_node), constant) + spring_id = relevant_spring_id_for_node(@start_node) + constant = @simulation_runner.constant_for_constrained_angle(angle, spring_id) + @ui.spring_pane.update_constant_for_spring(spring_id, constant) - simulate - #equilibrium_index = @simulation_runner.find_equilibrium(@constant) - #set_graph_to_data_sample(equilibrium_index) - @trace_visualization.reset_trace - @trace_visualization.add_trace(['18', '20'], 4, @simulation_data) view.invalidate reset end @@ -89,7 +86,7 @@ def draw(view) # Returns the spring that makes the static group the node is in movable. def relevant_spring_id_for_node(node) static_groups = StaticGroupAnalysis.get_static_groups_for_node(node) - raise 'No satic groups detected' unless static_groups + raise 'No static groups detected' unless static_groups static_group = static_groups[0] all_spring_edges = Graph.instance.edges.values.select { |edge| edge.link_type == 'spring' } diff --git a/src/tools/place_user_tool.rb b/src/tools/place_user_tool.rb index 8b5e46b2..09242282 100644 --- a/src/tools/place_user_tool.rb +++ b/src/tools/place_user_tool.rb @@ -1,4 +1,3 @@ -require_relative 'spring_simulation_tool.rb' require 'csv' require 'src/spring_animation.rb' require 'src/system_simulation/modellica_export.rb' @@ -9,8 +8,8 @@ require 'src/ui/dialogs/spring_pane.rb' # Places a user into the geometry i.e. someone who is injecting force into the system. This tool simulates the system -# and opens a panel that shows information and the possiblity to change parameters of the springs. -class PlaceUserTool < SpringSimulationTool +# and opens a panel that shows information and the possibility to change parameters of the springs. +class PlaceUserTool < Tool def initialize(ui) super(ui) @@ -31,69 +30,7 @@ def onLButtonDown(_flags, x, y, view) obj = @mouse_input.snapped_object if !obj.nil? && obj.is_a?(Node) # && obj.link_type == 'spring' obj.hub.toggle_attached_user - - # Populate simulation data. - simulate - - # Set geometry into equilibrium. - set_graph_to_data_sample(0) - - # Visualize for current spring constant. - @trace_visualization = TraceVisualization.new - @trace_visualization.add_trace(['18', '20'], 4, @simulation_data) - - # Open spring insights dialog. - if @insights_pane == nil - @insights_pane = SpringPane.new(Proc.new{|spring_id, value| spring_constant_changed(spring_id, value)}, - Proc.new{toggle_animation}) - end - - else - @trace_visualization.reset_trace - toggle_animation - end - - - end - - def spring_constant_changed(spring_id, value) - puts(value) - @spring_links.select{ |spring| spring.id == spring_id }[0].spring_parameter_k = value.to_f - @trace_visualization.reset_trace - @constant = value - simulate - drawing_time = Benchmark.realtime { @trace_visualization.add_trace(["18", "20"], 4, @simulation_data) } - puts("drawing time: " + drawing_time.to_s + "s") - end - - def onMouseMove(_flags, x, y, view) - @mouse_input.update_positions(view, x, y) - end - - private - - def toggle_animation - if @animation && @animation.running - @animation.toggle_running - else - create_animation end - - end - - def create_animation - @animation = GeometryAnimation.new(@simulation_data) - Sketchup.active_model.active_view.animation = @animation - end - - def get_period(constant=2000) - period = @simulation_runner.get_period(constant) - update_period(period) - end - - def update_period(value) - @insights_pane.set_period(value) - end end diff --git a/src/tools/spring_simulation_tool.rb b/src/tools/spring_simulation_tool.rb deleted file mode 100644 index b73084ea..00000000 --- a/src/tools/spring_simulation_tool.rb +++ /dev/null @@ -1,48 +0,0 @@ -require_relative 'tool.rb' -class SpringSimulationTool < Tool - def initialize(ui) - super(ui) - - # TODO replace by map edgeID => springConstant to support multiple springs - # Spring constant - @constant = 20000 - - # Array of AnimationDataSamples, each containing geometry information for hubs for a certain point in time. - @simulation_data = nil - - # Instance of the simulation runner used as an interface to the system simulation. - @simulation_runner = nil - - # All spring links in the scene right now - @spring_links = [] - - end - - def activate - # Instantiates SimulationRunner and compiles model. - @simulation_runner ||= SimulationRunner.instance - end - - - private - - def simulate - @simulation_data = @simulation_runner.get_hub_time_series - end - - def set_graph_to_data_sample(index) - current_data_sample = @simulation_data[index] - - Graph.instance.nodes.each do | node_id, node| - node.update_position(current_data_sample.position_data[node_id.to_s]) - node.hub.update_position(current_data_sample.position_data[node_id.to_s]) - node.hub.update_user_indicator() - end - - Graph.instance.edges.each do |_, edge| - link = edge.link - link.update_link_transformations - end - end - -end diff --git a/src/ui/dialogs/spring_pane.rb b/src/ui/dialogs/spring_pane.rb index 12027151..a2bb0b02 100644 --- a/src/ui/dialogs/spring_pane.rb +++ b/src/ui/dialogs/spring_pane.rb @@ -8,18 +8,32 @@ def initialize update_springs - @animation = nil + # Instance of the simulation runner used as an interface to the system simulation. @simulation_runner = nil + # Array of AnimationDataSamples, each containing geometry information for hubs for a certain point in time. + @simulation_data = nil + # Sketchup animation object which animates the graph according to simulation data frames. + @animation = nil + # A simple visualization for simulation data, plotting circles into the scene. + @trace_visualization = nil @dialog = nil open_dialog end + # spring / graph manipulation logic: + def update_constant_for_spring(spring_id, new_constant) edge = @spring_edges.find { |edge| edge.id == spring_id } edge.link.spring_parameter_k = new_constant - update_springs + + # update simulation data and visualizations with adjusted results + simulate + put_geometry_into_equilibrium(spring_id) + update_trace_visualization + + update_dialog if @dialog end def update_springs @@ -27,6 +41,34 @@ def update_springs update_dialog if @dialog end + def update_trace_visualization + @trace_visualization ||= TraceVisualization.new + @trace_visualization.reset_trace + @trace_visualization.add_trace(['18', '20'], 4, @simulation_data) + end + + def put_geometry_into_equilibrium(spring_id) + equilibrium_index = @simulation_runner.find_equilibrium(spring_id) + set_graph_to_data_sample(equilibrium_index) + end + + def set_graph_to_data_sample(index) + current_data_sample = @simulation_data[index] + + Graph.instance.nodes.each do | node_id, node| + node.update_position(current_data_sample.position_data[node_id.to_s]) + node.hub.update_position(current_data_sample.position_data[node_id.to_s]) + node.hub.update_user_indicator() + end + + Graph.instance.edges.each do |_, edge| + link = edge.link + link.update_link_transformations + end + end + + # dialog logic: + def set_period(value) @dialog.execute_script("set_period(#{value})") end @@ -70,6 +112,14 @@ def open_dialog register_callbacks end + # compilation / simulation logic: + + def try_compile + @simulation_runner ||= SimulationRunner.instance + @simulation_data ||= simulate + @simulation_runner + end + private def register_callbacks @@ -86,15 +136,13 @@ def register_callbacks end end - def try_compile - @simulation_runner ||= SimulationRunner.instance - @simulation_data ||= simulate - end + # compilation / simulation logic: def simulate @simulation_data = @simulation_runner.get_hub_time_series end + # animation logic: def toggle_animation simulate From a7918ca3b23a824f334ba3bd9d7f475584e5514e Mon Sep 17 00:00:00 2001 From: birneamstiel Date: Thu, 27 Feb 2020 16:00:55 +0100 Subject: [PATCH 06/11] Adjust default k to a realistic value for seesaw3 model. --- src/sketchup_objects/spring_link.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sketchup_objects/spring_link.rb b/src/sketchup_objects/spring_link.rb index 860ccbac..b078d55d 100644 --- a/src/sketchup_objects/spring_link.rb +++ b/src/sketchup_objects/spring_link.rb @@ -7,7 +7,7 @@ class SpringLink < ActuatorLink attr_reader :edge def initialize(first_node, second_node, edge, id: nil) - @spring_parameter_k = 200 + @spring_parameter_k = 2000 super(first_node, second_node, edge, id: id) persist_entity end From 1e043189c93ec6ed291f4e70911596365eff0ad8 Mon Sep 17 00:00:00 2001 From: birneamstiel Date: Thu, 27 Feb 2020 16:23:23 +0100 Subject: [PATCH 07/11] Cleanup animation files. --- src/spring_animation.rb | 58 ------------------- .../geometry_animation.rb | 0 src/trace_animation.rb | 56 ------------------ src/ui/dialogs/spring_pane.rb | 3 + 4 files changed, 3 insertions(+), 114 deletions(-) delete mode 100644 src/spring_animation.rb rename src/{ => system_simulation}/geometry_animation.rb (100%) delete mode 100644 src/trace_animation.rb diff --git a/src/spring_animation.rb b/src/spring_animation.rb deleted file mode 100644 index d99f9e1e..00000000 --- a/src/spring_animation.rb +++ /dev/null @@ -1,58 +0,0 @@ -class SpringAnimation - attr_accessor :factor - def initialize(data, first_vector, second_vector, initial_edge_position, edge) - @data = data - @first_vector = first_vector - @second_vector = second_vector - @initial_edge_position = initial_edge_position - @edge = edge - @index = 1 - @running = true - @factor = 16 - - end - - def halt - @running = false; - end - - def nextFrame(view) - value = @data[@index] - - # new_position = Geom::Point3d.new(value[1].to_f().mm * 1000, value[2].to_f().mm * 1000, value[3].to_f().mm * 1000) - new_position = Geom::Point3d.new(value[1], value[2], value[3]) - - # scaled_first_vector = @first_vector.clone - # scaled_first_vector.length = @first_vector.length * value[1].to_f.abs - - scaled_second_vector = @second_vector.clone - scaled_second_vector.length = ((@second_vector.length * 2) * (1.0 + value[1].to_f) * @factor) - @second_vector.length - - # @edge.first_node.update_position(@initial_edge_position + scaled_first_vector) - # @edge.first_node.hub.update_position(@edge.first_node.hub.position) - - @edge.second_node.update_position(new_position) - puts(@edge.second_node.id) - # @edge.second_node.update_position(@initial_edge_position + scaled_second_vector) - @edge.second_node.hub.update_position(@edge.second_node.hub.position) - # @edge.link.update_positions(@initial_edge_position + scaled_first_vector, @initial_edge_position + scaled_second_vector) - - # @edge.update_sketchup_object - # @edge.first_node.update_sketchup_object - # @edge.second_node.update_sketchup_object - - Graph.instance.edges.each do |_, edge| - link = edge.link - link.update_link_transformations - # edge.update_sketchup_object - end - view.refresh - @index = @index + @factor - if @index + @factor >= @data.length - @index = 0 - sleep(1) - end - - return @running - end -end diff --git a/src/geometry_animation.rb b/src/system_simulation/geometry_animation.rb similarity index 100% rename from src/geometry_animation.rb rename to src/system_simulation/geometry_animation.rb diff --git a/src/trace_animation.rb b/src/trace_animation.rb deleted file mode 100644 index e77e7779..00000000 --- a/src/trace_animation.rb +++ /dev/null @@ -1,56 +0,0 @@ -class TraceAnimation - attr_accessor :factor, :running - def initialize(data, index = 0) - @data = data - @index = index - @running = true - @factor = 75 - @points = [] - - end - - def toggle_running - @running = !@running; - end - - def nextFrame(view) - unless (@running) - - # last frame before animation stops – so we set value to last data sample and reset index to reset animation - @index = 0 - end - current_data_sample = @data[@index] - - model = Sketchup.active_model - entities = model.active_entities - @points << entities.add_cpoint(current_data_sample.position_data["18"]) - @points << entities.add_cpoint(current_data_sample.position_data["20"]) - puts(current_data_sample.time_stamp) - - unless (@running) - entities.erase_entities(@points) - end - - ## new_position = Geom::Point3d.new(value[1].to_f().mm * 1000, value[2].to_f().mm * 1000, value[3].to_f().mm * 1000) - #new_position = Geom::Point3d.new(value[1], value[2], value[3]) - # - #scaled_second_vector = @second_vector.clone - #scaled_second_vector.length = ((@second_vector.length * 2) * (1.0 + value[1].to_f) * @factor) - @second_vector.length - # - #@edge.second_node.update_position(new_position) - #@edge.second_node.hub.update_position(@edge.second_node.hub.position) - # - #Graph.instance.edges.each do |_, edge| - # link = edge.link - # link.update_link_transformations - #end - view.refresh - @index = @index + @factor - if @index + @factor >= @data.length - @index = 0 - sleep(1) - end - - return @running - end -end diff --git a/src/ui/dialogs/spring_pane.rb b/src/ui/dialogs/spring_pane.rb index a2bb0b02..92d031e8 100644 --- a/src/ui/dialogs/spring_pane.rb +++ b/src/ui/dialogs/spring_pane.rb @@ -1,3 +1,6 @@ +require 'src/system_simulation/simulation_runner.rb' +require 'src/system_simulation/geometry_animation.rb' + # Ruby integration for spring insights dialog class SpringPane INSIGHTS_HTML_FILE = '../spring-pane/index.erb'.freeze From c947b6903935521a5d09df4f812da7e67a2f7093 Mon Sep 17 00:00:00 2001 From: birneamstiel Date: Thu, 27 Feb 2020 16:23:39 +0100 Subject: [PATCH 08/11] Further increase default spring constant. --- src/sketchup_objects/spring_link.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sketchup_objects/spring_link.rb b/src/sketchup_objects/spring_link.rb index f0135803..4aa4feb1 100644 --- a/src/sketchup_objects/spring_link.rb +++ b/src/sketchup_objects/spring_link.rb @@ -7,7 +7,7 @@ class SpringLink < ActuatorLink attr_reader :edge, :initial_spring_length def initialize(first_node, second_node, edge, id: nil) - @spring_parameter_k = 2000 + @spring_parameter_k = 7000 super(first_node, second_node, edge, id: id) @first_elongation_length = @second_elongation_length = Configuration::MINIMUM_ELONGATION From 80ad4e5cfe144871fe5f442ae07306a33e3097e5 Mon Sep 17 00:00:00 2001 From: "dominik.meier" Date: Fri, 28 Feb 2020 18:55:26 +0100 Subject: [PATCH 09/11] Add first rough version --- src/system_simulation/geometry_animation.rb | 108 ++++++++++++++++++- src/system_simulation/modelica_simulation.rb | 2 + src/system_simulation/simulation_runner.rb | 10 +- src/ui/dialogs/spring_pane.rb | 2 + 4 files changed, 113 insertions(+), 9 deletions(-) diff --git a/src/system_simulation/geometry_animation.rb b/src/system_simulation/geometry_animation.rb index 6de49cfa..98af1769 100644 --- a/src/system_simulation/geometry_animation.rb +++ b/src/system_simulation/geometry_animation.rb @@ -6,23 +6,120 @@ def initialize(data, index = 0) @running = true @factor = 1 + ragdoll + end + + def frontBodyPosition position + position + end + + def backBodyPosition position + position + Geom::Vector3d.new(-10, 0, 0) + end + + def downBodyPosition position + position + Geom::Vector3d.new(-5, 0, -10) + end + + def rightArmBodyPosition position + position + Geom::Vector3d.new(-5, -10, -5) + end + + def ragdoll + @world = TrussFab::World.new + @world.set_gravity(0.0, 0.0, -9.81) + + position = Geom::Point3d.new( 0, 0, 0) + + frontHub = Hub.new(frontBodyPosition(position), incidents: []) + frontHub.create_body @world + frontHub.body.static = true + frontHub.body.mass = 10 + backHub = Hub.new(backBodyPosition(position), incidents: []) + backHub.create_body @world + backHub.body.static = true + backHub.body.mass = 10 + downHub = Hub.new(downBodyPosition(position), incidents: []) + downHub.create_body @world + downHub.body.static = true + downHub.body.mass = 10 + + rightArmHub = Hub.new(rightArmBodyPosition(position), incidents: []) + rightArmHub.create_body @world + rightArmHub.body.static = false + rightArmHub.body.mass = 10 + + @frontRightJoint = TrussFab::PointToPoint.new(@world, frontHub.body, rightArmHub.body, frontBodyPosition(position), rightArmBodyPosition(position), nil) + @backRightJoint = TrussFab::PointToPoint.new(@world, backHub.body, rightArmHub.body, backBodyPosition(position), rightArmBodyPosition(position), nil) + @rightArmDownJoint = TrussFab::PointToPointGasSpring.new(@world, rightArmHub.body, downHub.body, rightArmBodyPosition(position), downBodyPosition(position), nil) + + [@frontRightJoint, @backRightJoint].each do |joint| + joint.solver_model = Configuration::JOINT_SOLVER_MODEL + joint.stiffness = Configuration::JOINT_STIFFNESS + joint.breaking_force = 0 + joint.start_distance = 0.26 + joint.bodies_collidable = false + end + + @rightArmDownJoint.solver_model = Configuration::JOINT_SOLVER_MODEL + @rightArmDownJoint.extended_length = 0.5 + @rightArmDownJoint.stroke_length = 0.3 + @rightArmDownJoint.extended_force = 0.00001 + @rightArmDownJoint.threshold = 0.1 + @rightArmDownJoint.damp = 0.1 end def toggle_running - @running = !@running; + puts "Toggle running, before toggle: #{@running}" + @running = !@running + ragdoll + @factor = 1 end def nextFrame(view) + Sketchup.active_model.start_operation('visualize export result', true) unless (@running) # last frame before animation stops – so we set value to last data sample and reset index to reset animation @index = 0 end current_data_sample = @data[@index] - Graph.instance.nodes.each do | node_id, node| - node.update_position(current_data_sample.position_data[node_id.to_s]) - node.hub.update_position(current_data_sample.position_data[node_id.to_s]) - node.hub.update_user_indicator() + # Graph.instance.nodes.each do |node_id, node| + # node.update_position(current_data_sample.position_data[node_id.to_s]) + # node.hub.update_position(current_data_sample.position_data[node_id.to_s]) + # node.hub.update_user_indicator + # end + + if @frontRightJoint + 1.times do + puts "World advance" + @world.advance + end + z = 20 * Math.sin( @index.to_f / @data.length.to_f * 2* Math::PI * 2) + if z > 19 + z = 20 + end + position = Geom::Point3d.new(0, 0, z) + # puts "Position: #{position}" + # + # puts "frontRightJ valid? #{@frontRightJoint.valid?}" + # puts "backRightJ valid? #{@backRightJoint.valid?}" + # puts "force on spring: #{@rightArmDownJoint.linear_tension}" + # puts "spring valid?: #{@rightArmDownJoint.valid?}" + # puts "spring distance: #{@rightArmDownJoint.cur_length}" + + @rightArmDownJoint.set_point2 downBodyPosition position + @frontRightJoint.set_point1 frontBodyPosition position + @backRightJoint.set_point1 backBodyPosition position + + Sketchup.active_model.active_entities.erase_entities(@group.entities.to_a) if @group && !@group.deleted? + @group = Sketchup.active_model.entities.add_group + entities = @group.entities + entities.add_line(@frontRightJoint.get_point1, @frontRightJoint.get_point2) + entities.add_line(@backRightJoint.get_point1, @backRightJoint.get_point2) + entities.add_line(@backRightJoint.get_point1, @rightArmDownJoint.get_point1) + entities.add_line(@frontRightJoint.get_point1, @rightArmDownJoint.get_point1) + entities.add_line(@rightArmDownJoint.get_point1, @rightArmDownJoint.get_point2) end Graph.instance.edges.each do |_, edge| @@ -44,6 +141,7 @@ def nextFrame(view) # link = edge.link # link.update_link_transformations #end + Sketchup.active_model.commit_operation view.refresh @index = @index + @factor if @index + @factor >= @data.length diff --git a/src/system_simulation/modelica_simulation.rb b/src/system_simulation/modelica_simulation.rb index dc5142c8..5245b193 100644 --- a/src/system_simulation/modelica_simulation.rb +++ b/src/system_simulation/modelica_simulation.rb @@ -6,7 +6,9 @@ def self.run_simulation(simulation_name="seesaw3") result_file = simulation_name + "_res.csv" + puts 'Searching in:' directory = File.dirname(__FILE__) + puts directory Open3.popen2e("rm #{result_file}", :chdir => directory) do |i, o, t| o.each {|l| puts l } diff --git a/src/system_simulation/simulation_runner.rb b/src/system_simulation/simulation_runner.rb index e0474dfb..3646d77c 100755 --- a/src/system_simulation/simulation_runner.rb +++ b/src/system_simulation/simulation_runner.rb @@ -16,7 +16,9 @@ class SimulationRunner def initialize(suppress_compilation = false, keep_temp_dir = false) @model_name = 'seesaw3' - if suppress_compilation + puts "suppress_compilation: #{suppress_compilation}" + + if suppress_compilation || true @directory = File.dirname(__FILE__) else @directory = Dir.mktmpdir @@ -36,9 +38,9 @@ def initialize(suppress_compilation = false, keep_temp_dir = false) def get_hub_time_series data = [] update_spring_data_from_graph - simulation_time = Benchmark.realtime { run_simulation('node_pos.*') } + #simulation_time = Benchmark.realtime { run_simulation('node_pos.*') } import_time = Benchmark.realtime { data = parse_data(read_csv) } - puts("simulation time: #{simulation_time}s csv parsing time: #{import_time}s") + # puts("simulation time: #{simulation_time}s csv parsing time: #{import_time}s") data end @@ -164,7 +166,7 @@ def run_simulation(filter = '*') end def read_csv - CSV.read(File.join(@directory, "#{@model_name}_res.csv")) + CSV.read(File.join("D:/TrussFab/TrussFab_new/src/system_simulation", "seesaw3_res.csv")) end def parse_data(raw_data) diff --git a/src/ui/dialogs/spring_pane.rb b/src/ui/dialogs/spring_pane.rb index 92d031e8..a724f7cd 100644 --- a/src/ui/dialogs/spring_pane.rb +++ b/src/ui/dialogs/spring_pane.rb @@ -148,6 +148,7 @@ def simulate # animation logic: def toggle_animation + puts "toggle_animation" simulate if @animation && @animation.running @animation.toggle_running @@ -158,6 +159,7 @@ def toggle_animation end def create_animation + puts "create_animation" @animation = GeometryAnimation.new(@simulation_data) Sketchup.active_model.active_view.animation = @animation end From 4cee7403b6a3a06b678d18721d029ac6df34b10e Mon Sep 17 00:00:00 2001 From: "dominik.meier" Date: Thu, 5 Mar 2020 16:28:30 +0100 Subject: [PATCH 10/11] Refactor into own ragdoll class --- src/system_simulation/geometry_animation.rb | 117 +++---------------- src/system_simulation/ragdoll.rb | 120 ++++++++++++++++++++ 2 files changed, 136 insertions(+), 101 deletions(-) create mode 100644 src/system_simulation/ragdoll.rb diff --git a/src/system_simulation/geometry_animation.rb b/src/system_simulation/geometry_animation.rb index 98af1769..3f24b371 100644 --- a/src/system_simulation/geometry_animation.rb +++ b/src/system_simulation/geometry_animation.rb @@ -1,3 +1,5 @@ +require 'src/system_simulation/ragdoll.rb' + class GeometryAnimation attr_accessor :factor, :running def initialize(data, index = 0) @@ -6,74 +8,15 @@ def initialize(data, index = 0) @running = true @factor = 1 - ragdoll - end - - def frontBodyPosition position - position - end - - def backBodyPosition position - position + Geom::Vector3d.new(-10, 0, 0) - end - - def downBodyPosition position - position + Geom::Vector3d.new(-5, 0, -10) - end - - def rightArmBodyPosition position - position + Geom::Vector3d.new(-5, -10, -5) - end - - def ragdoll - @world = TrussFab::World.new - @world.set_gravity(0.0, 0.0, -9.81) - - position = Geom::Point3d.new( 0, 0, 0) - - frontHub = Hub.new(frontBodyPosition(position), incidents: []) - frontHub.create_body @world - frontHub.body.static = true - frontHub.body.mass = 10 - backHub = Hub.new(backBodyPosition(position), incidents: []) - backHub.create_body @world - backHub.body.static = true - backHub.body.mass = 10 - downHub = Hub.new(downBodyPosition(position), incidents: []) - downHub.create_body @world - downHub.body.static = true - downHub.body.mass = 10 - - rightArmHub = Hub.new(rightArmBodyPosition(position), incidents: []) - rightArmHub.create_body @world - rightArmHub.body.static = false - rightArmHub.body.mass = 10 - - @frontRightJoint = TrussFab::PointToPoint.new(@world, frontHub.body, rightArmHub.body, frontBodyPosition(position), rightArmBodyPosition(position), nil) - @backRightJoint = TrussFab::PointToPoint.new(@world, backHub.body, rightArmHub.body, backBodyPosition(position), rightArmBodyPosition(position), nil) - @rightArmDownJoint = TrussFab::PointToPointGasSpring.new(@world, rightArmHub.body, downHub.body, rightArmBodyPosition(position), downBodyPosition(position), nil) - - [@frontRightJoint, @backRightJoint].each do |joint| - joint.solver_model = Configuration::JOINT_SOLVER_MODEL - joint.stiffness = Configuration::JOINT_STIFFNESS - joint.breaking_force = 0 - joint.start_distance = 0.26 - joint.bodies_collidable = false - end - - @rightArmDownJoint.solver_model = Configuration::JOINT_SOLVER_MODEL - @rightArmDownJoint.extended_length = 0.5 - @rightArmDownJoint.stroke_length = 0.3 - @rightArmDownJoint.extended_force = 0.00001 - @rightArmDownJoint.threshold = 0.1 - @rightArmDownJoint.damp = 0.1 + @ragdoll = Ragdoll.new end def toggle_running - puts "Toggle running, before toggle: #{@running}" @running = !@running - ragdoll - @factor = 1 + + @ragdoll.clear + @ragdoll = Ragdoll.new + puts "new ragdoll" end def nextFrame(view) @@ -82,46 +25,18 @@ def nextFrame(view) # last frame before animation stops – so we set value to last data sample and reset index to reset animation @index = 0 end + @index = 8 current_data_sample = @data[@index] - # Graph.instance.nodes.each do |node_id, node| - # node.update_position(current_data_sample.position_data[node_id.to_s]) - # node.hub.update_position(current_data_sample.position_data[node_id.to_s]) - # node.hub.update_user_indicator - # end - - if @frontRightJoint - 1.times do - puts "World advance" - @world.advance - end - z = 20 * Math.sin( @index.to_f / @data.length.to_f * 2* Math::PI * 2) - if z > 19 - z = 20 - end - position = Geom::Point3d.new(0, 0, z) - # puts "Position: #{position}" - # - # puts "frontRightJ valid? #{@frontRightJoint.valid?}" - # puts "backRightJ valid? #{@backRightJoint.valid?}" - # puts "force on spring: #{@rightArmDownJoint.linear_tension}" - # puts "spring valid?: #{@rightArmDownJoint.valid?}" - # puts "spring distance: #{@rightArmDownJoint.cur_length}" - - @rightArmDownJoint.set_point2 downBodyPosition position - @frontRightJoint.set_point1 frontBodyPosition position - @backRightJoint.set_point1 backBodyPosition position - - Sketchup.active_model.active_entities.erase_entities(@group.entities.to_a) if @group && !@group.deleted? - @group = Sketchup.active_model.entities.add_group - entities = @group.entities - entities.add_line(@frontRightJoint.get_point1, @frontRightJoint.get_point2) - entities.add_line(@backRightJoint.get_point1, @backRightJoint.get_point2) - entities.add_line(@backRightJoint.get_point1, @rightArmDownJoint.get_point1) - entities.add_line(@frontRightJoint.get_point1, @rightArmDownJoint.get_point1) - entities.add_line(@rightArmDownJoint.get_point1, @rightArmDownJoint.get_point2) + Graph.instance.nodes.each do |node_id, node| + node.update_position(current_data_sample.position_data[node_id.to_s]) + node.hub.update_position(current_data_sample.position_data[node_id.to_s]) + node.hub.update_user_indicator end + @ragdoll.position = Graph.instance.nodes.values[0].position + @ragdoll.advance + Graph.instance.edges.each do |_, edge| link = edge.link link.update_link_transformations @@ -149,6 +64,6 @@ def nextFrame(view) sleep(1) end - return @running + @running end end diff --git a/src/system_simulation/ragdoll.rb b/src/system_simulation/ragdoll.rb new file mode 100644 index 00000000..ab408318 --- /dev/null +++ b/src/system_simulation/ragdoll.rb @@ -0,0 +1,120 @@ +class Ragdoll + attr_reader :position + + def initialize + @group = Sketchup.active_model.entities.add_group + @world = TrussFab::World.new + @world.set_gravity(0.0, 0.0, -9.81) + + position = Geom::Point3d.new( 0, 0, 0) + + frontHub = Hub.new(frontBodyPosition(position), incidents: []) + frontHub.create_body @world + frontHub.body.static = true + frontHub.body.mass = 10 + backHub = Hub.new(backBodyPosition(position), incidents: []) + backHub.create_body @world + backHub.body.static = true + backHub.body.mass = 10 + downHub = Hub.new(downBodyPosition(position), incidents: []) + downHub.create_body @world + downHub.body.static = true + downHub.body.mass = 10 + + rightArmHub = Hub.new(rightArmBodyPosition(position), incidents: []) + rightArmHub.create_body @world + rightArmHub.body.static = false + rightArmHub.body.mass = 10 + + @frontRightJoint = TrussFab::PointToPoint.new(@world, frontHub.body, rightArmHub.body, frontBodyPosition(position), rightArmBodyPosition(position), nil) + @backRightJoint = TrussFab::PointToPoint.new(@world, backHub.body, rightArmHub.body, backBodyPosition(position), rightArmBodyPosition(position), nil) + @rightArmDownJoint = TrussFab::PointToPointGasSpring.new(@world, rightArmHub.body, downHub.body, rightArmBodyPosition(position), downBodyPosition(position), nil) + + [@frontRightJoint, @backRightJoint].each do |joint| + joint.solver_model = Configuration::JOINT_SOLVER_MODEL + joint.stiffness = 0.9 + joint.breaking_force = 0 + joint.start_distance = 0.4 + joint.bodies_collidable = false + end + + @rightArmDownJoint.solver_model = Configuration::JOINT_SOLVER_MODEL + @rightArmDownJoint.extended_length = 0.6 + @rightArmDownJoint.stroke_length = 0.2 + @rightArmDownJoint.extended_force = 0.0001 + @rightArmDownJoint.threshold = 0.2 + @rightArmDownJoint.damp = 999 + puts "Current length: #{@rightArmDownJoint.cur_length}" + end + + def position=(position) + @position = position + # @rightArmDownJoint.set_point2 downBodyPosition @position + @frontRightJoint.set_point1 frontBodyPosition @position + @backRightJoint.set_point1 backBodyPosition @position + end + + def clear + Sketchup.active_model.active_entities.erase_entities(@group.entities.to_a) unless @group.deleted? + end + + def redraw + Sketchup.active_model.start_operation('', true) + clear + draw + Sketchup.active_model.commit_operation + end + + def draw + @group = Sketchup.active_model.entities.add_group + + @group.entities.add_face(@frontRightJoint.get_point1, @frontRightJoint.get_point2, @backRightJoint.get_point1) + puts "Lengt: #{(@frontRightJoint.get_point1 - @frontRightJoint.get_point2).length}" + # cylinder_from_points(@backRightJoint.get_point1, @backRightJoint.get_point2, 2) + # cylinder_from_points(@backRightJoint.get_point1, @rightArmDownJoint.get_point1, 2) + # cylinder_from_points(@frontRightJoint.get_point1, @rightArmDownJoint.get_point1, 2) + # This would visualize the spring: + @group.entities.add_line(@rightArmDownJoint.get_point1, @rightArmDownJoint.get_point2) + end + + def cylinder_from_points(point1, point2, radius) + vec = point2 - point1 + draw_cylinder point1, radius, vec if vec.length > 1 + end + + def draw_cylinder(center, radius, direction) + # This is actually harder than I thought, cause after pushpull, the references + # to the faces are gone + @group = Sketchup.active_model.entities.add_group if @group.deleted? + entities = @group.entities + normal = direction.normalize + edges = entities.add_circle(center, normal, radius, 10) + face = entities.add_face(edges) + face.pushpull direction.length if face + end + + def advance + 2.times do + @world.advance + puts "Current force: #{@rightArmDownJoint.linear_tension}" + end + redraw + end + + def frontBodyPosition position + position + end + + def backBodyPosition position + position + Geom::Vector3d.new(-10, 0, 0) + end + + def downBodyPosition position + position + Geom::Vector3d.new(-5, -10, -10) + end + + def rightArmBodyPosition position + position + Geom::Vector3d.new(-5, -20, 0) + end + +end From 96c51f6c053f7522f6458da32b57c1212165a0c2 Mon Sep 17 00:00:00 2001 From: "dominik.meier" Date: Thu, 5 Mar 2020 16:49:00 +0100 Subject: [PATCH 11/11] Small fixes to make saner animation --- src/configuration/configuration.rb | 2 +- src/system_simulation/geometry_animation.rb | 2 +- src/system_simulation/ragdoll.rb | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/configuration/configuration.rb b/src/configuration/configuration.rb index 94890d3d..32ec39b2 100644 --- a/src/configuration/configuration.rb +++ b/src/configuration/configuration.rb @@ -90,7 +90,7 @@ module Configuration # Simulation Properties WORLD_GRAVITY = -9.8 # in m/s/s WORLD_SOLVER_MODEL = 8 # 1 - 64 - WORLD_TIMESTEP = 1.0 / 60 # in seconds + WORLD_TIMESTEP = 1.0 / 120 # in seconds WORLD_NUM_ITERATIONS = ((1.0 / 60) / WORLD_TIMESTEP).to_i JOINT_SOLVER_MODEL = 2 # 0 or 2 JOINT_STIFFNESS = 0.95 # ratio (0.0 - 1.0) diff --git a/src/system_simulation/geometry_animation.rb b/src/system_simulation/geometry_animation.rb index 3f24b371..db779c37 100644 --- a/src/system_simulation/geometry_animation.rb +++ b/src/system_simulation/geometry_animation.rb @@ -25,7 +25,7 @@ def nextFrame(view) # last frame before animation stops – so we set value to last data sample and reset index to reset animation @index = 0 end - @index = 8 + # @index = 8 # Disables animation to debug ragdoll current_data_sample = @data[@index] Graph.instance.nodes.each do |node_id, node| diff --git a/src/system_simulation/ragdoll.rb b/src/system_simulation/ragdoll.rb index ab408318..a1986d5c 100644 --- a/src/system_simulation/ragdoll.rb +++ b/src/system_simulation/ragdoll.rb @@ -39,17 +39,17 @@ def initialize end @rightArmDownJoint.solver_model = Configuration::JOINT_SOLVER_MODEL - @rightArmDownJoint.extended_length = 0.6 - @rightArmDownJoint.stroke_length = 0.2 + @rightArmDownJoint.extended_length = 0.56 + @rightArmDownJoint.stroke_length = 0.15 @rightArmDownJoint.extended_force = 0.0001 @rightArmDownJoint.threshold = 0.2 - @rightArmDownJoint.damp = 999 + @rightArmDownJoint.damp = 0 puts "Current length: #{@rightArmDownJoint.cur_length}" end def position=(position) @position = position - # @rightArmDownJoint.set_point2 downBodyPosition @position + @rightArmDownJoint.set_point2 downBodyPosition @position @frontRightJoint.set_point1 frontBodyPosition @position @backRightJoint.set_point1 backBodyPosition @position end @@ -83,7 +83,8 @@ def cylinder_from_points(point1, point2, radius) end def draw_cylinder(center, radius, direction) - # This is actually harder than I thought, cause after pushpull, the references + # This is actually harder than I thought, and does not work right now, + # cause after pushpull, the references # to the faces are gone @group = Sketchup.active_model.entities.add_group if @group.deleted? entities = @group.entities @@ -110,7 +111,7 @@ def backBodyPosition position end def downBodyPosition position - position + Geom::Vector3d.new(-5, -10, -10) + position + Geom::Vector3d.new(-5, 0, -10) end def rightArmBodyPosition position