ein 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: