ein 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/ein/angle.rb ADDED
@@ -0,0 +1,10 @@
1
+ ################
2
+ # A class with convenience method related to angles
3
+ ##########
4
+ module Ein
5
+ class Angle
6
+ def self.degrees_to_radians(degrees)
7
+ degrees * Math::PI / 180
8
+ end
9
+ end
10
+ end
data/lib/ein/circle.rb ADDED
@@ -0,0 +1,38 @@
1
+ ###################
2
+ # Our obstacles are circles, but other shapes may come later
3
+ ###################
4
+ module Ein
5
+ class Circle
6
+
7
+ attr_reader :position, :radius
8
+
9
+ def initialize(position, radius)
10
+ @position, @radius = position, radius
11
+ end
12
+
13
+ def pos
14
+ @position.round
15
+ end
16
+
17
+ def distance_to(object)
18
+ case object_physics(object)
19
+ when :circle
20
+ @position.distance_to(object.pos) - ( @radius + object.radius)
21
+ when :position
22
+ @position.distance_to(object) - ( @radius )
23
+ else
24
+ raise "can not calculate distance from Circle to #{object.class}"
25
+ end
26
+ end
27
+
28
+ private
29
+ def object_physics(object)
30
+ if object.respond_to?(:physical_shape)
31
+ object.physical_shape
32
+ else
33
+ object.class.to_s.split('::').last.downcase.to_sym
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,48 @@
1
+ ###############
2
+ # One of the possible formatters of our information.
3
+ # It outputs to the console. By default only info messages will be output
4
+ # create it with new(:debug) to make it much more verbose
5
+ ###############
6
+ module Ein
7
+ class Console
8
+
9
+ def initialize(level=:info)
10
+ if available?(level)
11
+ @level = level
12
+ else
13
+ @level = :info
14
+ end
15
+ end
16
+
17
+ def info(text)
18
+ puts(text) if should_print(:info)
19
+ end
20
+
21
+ def debug(text)
22
+ puts(text) if should_print(:debug)
23
+ end
24
+
25
+ private
26
+ def available_levels
27
+ [:debug, :info, :quiet]
28
+ end
29
+
30
+ def available?(level)
31
+ available_levels.include?(level)
32
+ end
33
+
34
+ def priority(level)
35
+ available_levels.index(level)
36
+ end
37
+
38
+ def should_print(level)
39
+ priority(level) >= priority(@level)
40
+ end
41
+
42
+ def puts(text)
43
+ return if defined?(Rails) && Rails.env == :test
44
+ Kernel.puts text
45
+ end
46
+
47
+ end
48
+ end
data/lib/ein/plane.rb ADDED
@@ -0,0 +1,43 @@
1
+ #######################################
2
+ # This is an infinite 2D plane with infinite mass and infinitely strong
3
+ # Objects and entities can collide with it.
4
+ # normal is the normal of the plane towards the origin
5
+ # distance_to_origin is the distance from the plane to the origin
6
+ #######################################
7
+ module Ein
8
+ class Plane
9
+ attr_reader :normal, :distance_to_origin
10
+
11
+ def initialize(normal, distance_to_origin)
12
+ @normal = normal
13
+ @distance_to_origin = distance_to_origin
14
+ end
15
+
16
+ def distance_to(object)
17
+ case object_physics(object)
18
+ when :circle
19
+ distance_to_point(object.pos) - object.radius
20
+ when :position
21
+ distance_to_point(object)
22
+ else
23
+ raise "can not calculate distance from Plane to #{object.class}"
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def object_physics(object)
30
+ if object.respond_to?(:physical_shape)
31
+ object.physical_shape
32
+ else
33
+ object.class.to_s.split('::').last.downcase.to_sym
34
+ end
35
+ end
36
+
37
+ def distance_to_point(point)
38
+ projected_point = normal.project(point)
39
+ projected_point.x + projected_point.y + @distance_to_origin
40
+ end
41
+
42
+ end
43
+ end
data/lib/ein/pose.rb ADDED
@@ -0,0 +1,29 @@
1
+ ################################
2
+ # Pose of an object in a 2D environment
3
+ # Its position in 2D coordinates and its angle in degrees
4
+ # angle=0 => +y, angle=90 => +x, angle=180 => -y, angle=270 => -x
5
+ ################################
6
+ module Ein
7
+ class Pose
8
+
9
+ attr_reader :position
10
+
11
+ def initialize(position, angle)
12
+ @position = position
13
+ @angle = angle
14
+ end
15
+
16
+ #Create a new pose, modifying previous with a relative position and angle
17
+ def advance(distance, angle)
18
+ new_angle = @angle + angle
19
+ new_y = @position.y + distance * Math.cos(Angle.degrees_to_radians(new_angle))
20
+ new_x = @position.x + distance * Math.sin(Angle.degrees_to_radians(new_angle))
21
+ Pose.new(Position.new(new_x, new_y), new_angle)
22
+ end
23
+
24
+ def to_s
25
+ "N: #@angle, #@position"
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ ########################
2
+ # A 2D position
3
+ # Positions of objects and entities always represent
4
+ # the position if their centers
5
+ ########################
6
+ module Ein
7
+ class Position
8
+ attr_accessor :x, :y
9
+
10
+ def initialize(x,y)
11
+ @x, @y = x,y
12
+ end
13
+
14
+ def distance_to(position)
15
+ Math.sqrt((@x - position.x)**2 + (@y - position.y)**2) # Pythagoras, miss you buddy. RIP
16
+ end
17
+
18
+ def round
19
+ Position.new(@x.round, @y.round)
20
+ end
21
+
22
+ def to_s
23
+ "x: #@x, y: #@y"
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,53 @@
1
+ require 'console'
2
+ require 'world'
3
+
4
+ ##############################
5
+ #
6
+ #This class is the highest level entity, controls how
7
+ #the simulation behaves, contains the world and the entities
8
+ #
9
+ ###############################
10
+ module Ein
11
+ class Simulator
12
+ attr_reader :world, :current_time, :formatter
13
+
14
+ def initialize(formatter = nil)
15
+ @current_time = Time.now
16
+ @formatter = formatter || Console.new
17
+ @world = World.new
18
+ @running = false
19
+ end
20
+
21
+ #TODO: should these methods add ! because they modify the simulation ?
22
+ def start
23
+ @running = true
24
+ Thread.abort_on_exception = true
25
+ Thread.new { run }
26
+ self
27
+ end
28
+
29
+ def stop
30
+ @running = false
31
+ self
32
+ end
33
+
34
+ def running?
35
+ @running
36
+ end
37
+
38
+ private
39
+
40
+ STEP = 0.01
41
+
42
+ def run
43
+ @formatter.info "Simulation started"
44
+ while (@running)
45
+ @current_time += STEP
46
+ @world.step(STEP)
47
+ sleep(STEP)
48
+ end
49
+ @formatter.info "Simulation terminated"
50
+ end
51
+
52
+ end
53
+ end
data/lib/ein/vector.rb ADDED
@@ -0,0 +1,30 @@
1
+ ################################
2
+ # A vector in 2D space
3
+ ################################
4
+ module Ein
5
+ class Vector
6
+ attr_accessor :x, :y
7
+
8
+ def initialize(x,y)
9
+ @x, @y = x,y
10
+ end
11
+
12
+ def project(position)
13
+ Vector.new(@x * position.x, @y * position.y)
14
+ end
15
+
16
+ def length
17
+ Math.sqrt(@x**2 + @y**2)
18
+ end
19
+
20
+ def *(scalar)
21
+ @x += @x * scalar
22
+ @y += @y * scalar
23
+ end
24
+
25
+ def to_s
26
+ "x: #@x, y: #@y"
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module Ein
2
+ VERSION = "0.0.1"
3
+ end
data/lib/ein/world.rb ADDED
@@ -0,0 +1,75 @@
1
+ ##############################
2
+ #
3
+ # This class represents the virtual world our simulation runs in
4
+ #
5
+ ##############################
6
+ module Ein
7
+ class World
8
+
9
+ def initialize
10
+ @entities = []
11
+ read_world
12
+ self
13
+ end
14
+
15
+ def render
16
+ world_ui = {}
17
+ world_ui['boundaries'] = @boundaries
18
+ world_ui['obstacles'] = @obstacles
19
+ @entities.each do |entity|
20
+ if entity.respond_to?(:render)
21
+ world_ui['entity'] = entity.render
22
+ end
23
+ end
24
+ world_ui.to_json
25
+ end
26
+
27
+ def spawn(*entities)
28
+ entities.each do |entity|
29
+ @entities.push(entity)
30
+ end
31
+ end
32
+
33
+ def step (time_step)
34
+ @entities.each do |entity|
35
+ entity.step(time_step)
36
+ if collision_with?(entity)
37
+ entity.step_back
38
+ end
39
+ end
40
+ end
41
+
42
+
43
+ def collision_with?(object)
44
+
45
+ @boundaries.each do |boundary|
46
+ return true if boundary.distance_to(object) <= 0
47
+ end
48
+
49
+ @obstacles.each do |obstacle|
50
+ return true if obstacle.distance_to(object) <= 0
51
+ end
52
+ false
53
+ end
54
+
55
+ private
56
+
57
+ def read_world
58
+ #TODO: read from external .yml or something
59
+ #TODO: mass and shape for the obstacles
60
+ #boundaries from left boundary clockwise.
61
+ @boundaries = [
62
+ Plane.new(Vector.new(1,0), 1000),
63
+ Plane.new(Vector.new(0,-1), 800),
64
+ Plane.new(Vector.new(-1,0), 1000),
65
+ Plane.new(Vector.new(0,1), 800)
66
+ ]
67
+ @obstacles = [
68
+ Circle.new(Position.new(0,500), 20),
69
+ Circle.new(Position.new(300,0), 20),
70
+ Circle.new(Position.new(-900,-700), 10)
71
+ ]
72
+ end
73
+
74
+ end
75
+ end
data/lib/ein.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "ein/version"
2
+
3
+ module Ein
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ module Ein
4
+ describe Circle do
5
+ it "calculates the distance to other circle" do
6
+ circle1 = Circle.new(Position.new(0,0), 100)
7
+ circle2 = Circle.new(Position.new(300,0), 100)
8
+ expect(circle1.distance_to(circle2)).to eq(100)
9
+
10
+ circle2 = Circle.new(Position.new(200,0), 100)
11
+ expect(circle1.distance_to(circle2)).to eq(0)
12
+
13
+ circle2 = Circle.new(Position.new(100,0), 100)
14
+ expect(circle1.distance_to(circle2)).to eq(-100)
15
+
16
+ circle2 = Circle.new(Position.new(200,0),0)
17
+ expect(circle1.distance_to(circle2)).to eq(100)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ module Ein
4
+ describe Console do
5
+ it "prints when the message priority is bigger than the default priority" do
6
+ console = Console.new(:debug)
7
+ Kernel.should_receive(:puts).with("trusmis")
8
+ console.info("trusmis")
9
+ end
10
+ it "prints the message when priority is equal to the default priority" do
11
+ console = Console.new(:debug)
12
+ Kernel.should_receive(:puts).with("trusmis")
13
+ console.debug("trusmis")
14
+ end
15
+ it "does not print when the priority is less than the default priority" do
16
+ console = Console.new(:info)
17
+ Kernel.should_not_receive(:puts).with("trusmis")
18
+ console.debug("trusmis")
19
+ end
20
+ it "prints nothing if we chose a quite level" do
21
+ console = Console.new(:quiet)
22
+ Kernel.should_not_receive(:puts).with("trusmis")
23
+ console.debug("trusmis")
24
+ console.info("trusmis")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ #encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ module Ein
5
+ describe Simulator do
6
+
7
+ context "new simulator with no params" do
8
+ subject {Simulator.new}
9
+ it { should_not be_running }
10
+ its(:world) {should be}
11
+ end
12
+
13
+ context "it can run simulations" do
14
+ subject(:simulator) {Simulator.new(Console.new(:quiet)) }
15
+
16
+ it "can be started and stopped" do
17
+ simulator.start
18
+ expect(simulator).to be_running
19
+ simulator.stop
20
+ expect(simulator).not_to be_running
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ RSpec.configure do |config|
2
+ config.treat_symbols_as_metadata_keys_with_true_values = true
3
+ config.run_all_when_everything_filtered = true
4
+ config.filter_run :focus
5
+ config.order = 'random'
6
+ end
7
+ $LOAD_PATH << File.expand_path( 'lib/ein/')
8
+ Dir['lib/ein/*.rb'].each do |file|
9
+ require File.basename(file, '.rb')
10
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ein
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jordi Polo Carres
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.13'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.13'
30
+ description: Very simple 2D physics simulator
31
+ email:
32
+ - mumismo@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/ein/angle.rb
38
+ - lib/ein/circle.rb
39
+ - lib/ein/console.rb
40
+ - lib/ein/plane.rb
41
+ - lib/ein/pose.rb
42
+ - lib/ein/position.rb
43
+ - lib/ein/simulator.rb
44
+ - lib/ein/vector.rb
45
+ - lib/ein/version.rb
46
+ - lib/ein/world.rb
47
+ - lib/ein.rb
48
+ - spec/lib/ein/circle_spec.rb
49
+ - spec/lib/ein/console_spec.rb
50
+ - spec/lib/ein/simulator_spec.rb
51
+ - spec/spec_helper.rb
52
+ homepage: https://github.com/jordiPolo/ein
53
+ licenses: []
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 1.8.25
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: Very simple 2D physics simulator
76
+ test_files:
77
+ - spec/lib/ein/circle_spec.rb
78
+ - spec/lib/ein/console_spec.rb
79
+ - spec/lib/ein/simulator_spec.rb
80
+ - spec/spec_helper.rb
81
+ has_rdoc: