analog 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d406496d1f9d968519029f39c59cfd4de9d89959
4
+ data.tar.gz: 6c6594988cb9a366f6125f21a340d1782ee1930c
5
+ SHA512:
6
+ metadata.gz: fd71860f234567f1f0111c7b723a0b0760ccf02645a1ea6f85f678dbd1e863974af788b45429a93c2708873d128c8ddb72e29597b8b7a2c96266e865dce1ebe7
7
+ data.tar.gz: 92ef67f94ef2ff5049da82808d8046d4cae7e11a836ea3ee546914d1e0b3dbdfcfa6db991ce401f20bcfcf4fe8b29eec4f55548f63d3b4d5dd87158608ce45ef
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2014 Ari Russo
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # analog
2
+
3
+ A Ruby helper for scaling numbers.
4
+
5
+ #### Background
6
+
7
+ This helper provides a quick way to take a number and scale it given a source and destination range/set/etc.
8
+
9
+ It's useful for converting data from raw values to a range used for display or other kinds of output.
10
+
11
+ I use this all the time in music programming, converting OSC messages to musical notes and things like that and figure it might be useful to others.
12
+
13
+ A simple example is:
14
+
15
+ If you want to plot a point on a 500px graph using a data that lies in the 0..1 range, do
16
+
17
+ ```ruby
18
+ Scale.transform(0.5).using(0..1, 0..500)
19
+ => 250
20
+ ```
21
+
22
+ Please note: I am not a Math Person. If I've named any concepts here poorly, [please let me know](https://github.com/arirusso/analog/issues). Or [submit a pull request](https://github.com/arirusso/analog/pulls)
23
+
24
+ #### Usage
25
+
26
+ ```ruby
27
+ require "scale"
28
+ ```
29
+
30
+ This example will scale a number down by 1/10th`
31
+
32
+ ```ruby
33
+ Scale.transform(22).using(0..150, 0..15)
34
+ => 2
35
+
36
+ ```
37
+
38
+ You can use Arrays and Sets
39
+ ```ruby
40
+ Scale.transform(8).using(Set.new([0, 2, 4, 8, 16, 64]), 0..10)
41
+ => 6
42
+
43
+ Scale.transform(0.40).using(0..1, [0, 2, 4, 8, 12, 16, 32, 64, 128, 512])
44
+ => 8
45
+ ```
46
+
47
+ See the [examples](https://github.com/arirusso/analog/tree/master/examples) for more
48
+
49
+ ###### Core Extension
50
+
51
+ There is a Numeric extension that you can optionally include.
52
+
53
+ ```ruby
54
+ require "scale/core_ext"
55
+
56
+ 0.40.scaled_from(0..1).to([0, 2, 4, 8, 12, 16, 32, 64, 128, 512])
57
+ => 8
58
+ ```
59
+
60
+ See the [core_ext example](https://github.com/arirusso/analog/blob/master/examples/core_ext.rb) for more examples.
61
+
62
+ #### Installation
63
+
64
+ gem install analog
65
+
66
+ or with Bundler
67
+
68
+ gem "analog"
69
+
70
+ #### License
71
+
72
+ Licensed under Apache 2.0, See the file LICENSE
73
+ Copyright (c) 2014 [Ari Russo](http://arirusso.com)
74
+
data/lib/scale.rb ADDED
@@ -0,0 +1,11 @@
1
+ # classes
2
+ require "scale/destination"
3
+ require "scale/scheme"
4
+ require "scale/source"
5
+
6
+ # A Ruby helper for scaling numbers
7
+ module Scale
8
+
9
+ VERSION = "0.1"
10
+
11
+ end
@@ -0,0 +1,46 @@
1
+ require "scale"
2
+
3
+ # Extend the Numeric class to add a scaling helper.
4
+ # eg: 0.40.scaled_from(0..1).to([512, 128, 64, 32, 16, 12, 8, 4, 2, 0])
5
+ #
6
+ # This is not included by default with the rest of the library. use require "scale/core_ext"
7
+ #
8
+ class Numeric
9
+
10
+ # Build a Scheme for this numeric as input.
11
+ # @return [Scale::Scheme]
12
+ def scaled
13
+ Scale.transform(self)
14
+ end
15
+
16
+ # Scale using the given source and destination, with this numeric as input.
17
+ # @param [Scale::Source] source
18
+ # @param [Scale::Destination] destination
19
+ # @return [Numeric]
20
+ def scaled_using(source, destination)
21
+ Scale.transform(self).using(source, destination)
22
+ end
23
+
24
+ # Build a scheme with this numeric as input and add the given source to it. If
25
+ # on calling this method, the scheme has all of its needed properties, the scaled
26
+ # value will be returned. Otherwise # this method will return the updated Scheme
27
+ # object.
28
+ #
29
+ # @param [Scale::Source] source
30
+ # @return [Numeric, Scale::Scheme]
31
+ def scaled_from(source)
32
+ Scale.transform(self).from(source)
33
+ end
34
+
35
+ # Build a scheme with this numeric as input and add the given destination to it. If
36
+ # on calling this method, the scheme has all of its needed properties, the scaled
37
+ # value will be returned. Otherwise # this method will return the updated Scheme
38
+ # object.
39
+ #
40
+ # @param [Scale::Destination] destination
41
+ # @return [Numeric, Scale::Scheme]
42
+ def scaled_to(destination)
43
+ Scale.transform(self).to(destination)
44
+ end
45
+
46
+ end
@@ -0,0 +1,73 @@
1
+ module Scale
2
+
3
+ # These are the classes that describe what range the transformed number
4
+ # will wind up in. They're named after the core Ruby class that the input closest
5
+ # resembles.
6
+ module Destination
7
+
8
+ # Contains logic for dealing with Ruby's core ::Range as input
9
+ class Range
10
+
11
+ # @param [::Range] range A range to operate with
12
+ def initialize(range)
13
+ @range = range
14
+ end
15
+
16
+ # Scale the given input and source using this destination
17
+ # @param [Numeric] input A numeric value to scale
18
+ # @param [Scale::Source] source The source for the input value
19
+ # @return [Numeric]
20
+ def scale(input, source)
21
+ to_range_len = (@range.last - @range.first).abs
22
+
23
+ proportion = to_range_len.to_f / source.denominator
24
+ abs_output = proportion.to_f * source.numerator(input)
25
+ output = abs_output + @range.first
26
+
27
+ float_requested = [@range.first, @range.last].any? { |n| n.kind_of?(::Float) }
28
+ float_requested ? output : output.to_i
29
+ end
30
+
31
+ end
32
+
33
+ # Contains logic for dealing with input that includes Ruby's core ::Enumerable
34
+ class Enumerable
35
+
36
+ # @param [::Enumerable] enum An enumerable (eg Array, Set) to operate with
37
+ def initialize(enum)
38
+ @enum = enum
39
+ end
40
+
41
+ # Scale the given input and source using this destination
42
+ # @param [Numeric] input A numeric value to scale
43
+ # @param [Scale::Source] source The source for the input value
44
+ # @return [Numeric]
45
+ def scale(input, source)
46
+ proportion = source.numerator(input) / source.denominator
47
+ index = [((proportion * @enum.size).to_i - 1), 0].max
48
+ @enum.to_a.at(index)
49
+ end
50
+
51
+ end
52
+
53
+ # Map Ruby classes/modules to scaling destination classes/modules
54
+ MAP = {
55
+ ::Enumerable => Destination::Enumerable,
56
+ ::Range => Destination::Range
57
+ }
58
+
59
+ # Build the appropriate scaling destination class for the given Ruby object
60
+ # @param [::Enumerable] destination
61
+ # @return [Scale::Destination::Enumerable, Scale::Destination::Range]
62
+ def self.new(destination)
63
+ klass = MAP[destination.class]
64
+ if klass.nil?
65
+ klasses = MAP.select { |k,v| destination.kind_of?(k) }
66
+ klass = klasses.values.first
67
+ end
68
+ klass.new(destination) unless klass.nil?
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,65 @@
1
+ module Scale
2
+
3
+ # Build a scaling scheme for the given input
4
+ # @param [Numeric] input
5
+ # @return [Scale::Scheme]
6
+ def self.transform(input)
7
+ Scheme.new(:input => input)
8
+ end
9
+
10
+ # Describes a particular scaling scenario
11
+ class Scheme
12
+
13
+ attr_reader :destination, :input, :source
14
+
15
+ # @param [Hash] options
16
+ # @option options [Scale::Destination] :destination The destination for this scaling scenario
17
+ # @option options [Scale::Source] :source The source for this scaling scenario
18
+ def initialize(options = {})
19
+ @input = options[:input]
20
+ @source = Source.new(options[:source]) unless options[:source].nil?
21
+ @destination = Destination.new(options[:destination]) unless options[:destination].nil?
22
+ end
23
+
24
+ # Set the source for this scaling scenario. If on calling this method, the scenario
25
+ # has all of its needed properties, the scaled value will be returned. Otherwise
26
+ # this method will return the updated Scheme object.
27
+ #
28
+ # @param [Scale::Source] source
29
+ # @return [Fixnum, Scale::Scheme]
30
+ def from(source)
31
+ @source = Source.new(source)
32
+ if @input.nil? || @destination.nil?
33
+ self
34
+ else
35
+ @destination.scale(@input, @source)
36
+ end
37
+ end
38
+
39
+ # Set the destination for this scaling scenario. If on calling this method, the
40
+ # scenario has all of its needed properties, the scaled value will be returned.
41
+ # Otherwise this method will return the updated Scheme object.
42
+ #
43
+ # @param [Scale::Destination] destination
44
+ # @return [Fixnum, Scale::Scheme]
45
+ def to(destination)
46
+ @destination = Destination.new(destination)
47
+ if @input.nil? || @source.nil?
48
+ self
49
+ else
50
+ @destination.scale(@input, @source)
51
+ end
52
+ end
53
+
54
+ # Set both the source and destination on this scheme
55
+ # @param [Scale::Source] source
56
+ # @param [Scale::Destination] destination
57
+ def using(source, destination)
58
+ @source = Source.new(source)
59
+ @destination = Destination.new(destination)
60
+ @destination.scale(@input, @source)
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,70 @@
1
+ module Scale
2
+
3
+ # These are the classes that describe what range the transformed number
4
+ # starts in. They're named after the core Ruby class that the input closest
5
+ # resembles.
6
+ module Source
7
+
8
+ # Contains logic for dealing with Ruby's core ::Range as input
9
+ class Range
10
+
11
+ # @param [::Range] range A range to operate on
12
+ def initialize(range)
13
+ @range = range
14
+ end
15
+
16
+ # @param [Numeric] input
17
+ # @return [Float]
18
+ def numerator(input)
19
+ (input - @range.first).to_f
20
+ end
21
+
22
+ # @return [Float]
23
+ def denominator
24
+ (@range.last - @range.first).abs.to_f
25
+ end
26
+
27
+ end
28
+
29
+ # Contains logic for dealing with input that includes Ruby's core ::Enumerable
30
+ class Enumerable
31
+
32
+ # @param [::Enumerable] enum An enumerable (Array, Set, etc) to operate on
33
+ def initialize(enum)
34
+ @enum = enum
35
+ end
36
+
37
+ # @param [Numeric] input
38
+ # @return [Float]
39
+ def numerator(input)
40
+ @enum.to_a.index(input).to_f
41
+ end
42
+
43
+ # @return [Float]
44
+ def denominator
45
+ (@enum.size - 1).to_f
46
+ end
47
+
48
+ end
49
+
50
+ # Map Ruby classes/modules to scaling source classes/modules
51
+ MAP = {
52
+ ::Enumerable => Source::Enumerable,
53
+ ::Range => Source::Range
54
+ }
55
+
56
+ # Build the appropriate scaling source class for the given Ruby object
57
+ # @param [::Enumerable] source
58
+ # @return [Scale::Source::Enumerable, Scale::Source::Range]
59
+ def self.new(source)
60
+ klass = MAP[source.class]
61
+ if klass.nil?
62
+ klasses = MAP.select { |k,v| source.kind_of?(k) }
63
+ klass = klasses.values.first
64
+ end
65
+ klass.new(source) unless klass.nil?
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,84 @@
1
+ require "helper"
2
+
3
+ class CoreExtTest < Test::Unit::TestCase
4
+
5
+ context "Numeric" do
6
+
7
+ setup do
8
+ require "scale/core_ext"
9
+ end
10
+
11
+ context "#scaled" do
12
+
13
+ should "amplify" do
14
+ output = 0.10.scaled.using(0..1, 0..127)
15
+ assert_equal(12, output)
16
+ end
17
+
18
+ should "deamplify" do
19
+ output = 22.scaled.using(0..150, 0..15)
20
+ assert_equal(2, output)
21
+ end
22
+
23
+ should "scale neg/pos into pos/pos" do
24
+ output = 10.scaled.using(-24..24, 0..3.0)
25
+ assert_equal(2.125, output)
26
+ end
27
+
28
+ should "scale neg/pos into another neg/pos" do
29
+ output = 10.scaled.using(-24..24, -3..3.0)
30
+ assert_equal(1.25, output)
31
+ end
32
+
33
+ end
34
+
35
+ context "#scaled_using" do
36
+
37
+ should "scale neg/neg into neg/pos" do
38
+ output = -14.scaled_using(-24..-12, -3..3.0)
39
+ assert_equal(2, output)
40
+ end
41
+
42
+ should "scale neg/neg to another neg/neg" do
43
+ output = -16.scaled_using(-24..-12, -5..-3.0)
44
+ assert_equal(-3.666666666666667, output)
45
+ end
46
+
47
+ should "scale neg/neg to pos/pos" do
48
+ output = -18.scaled_using(-24..-12, 1..3.0)
49
+ assert_equal(2, output)
50
+ end
51
+
52
+ end
53
+
54
+ context "#scaled_to" do
55
+
56
+ should "take an array as the source" do
57
+ output = 8.scaled_to(0..10).from([0, 2, 4, 8, 16, 64])
58
+ assert_equal(6, output)
59
+ end
60
+
61
+ should "output a float when specified" do
62
+ output = 12.scaled_to(0..1.0).from(0..120)
63
+ assert_equal(0.10, output)
64
+ end
65
+
66
+ end
67
+
68
+ context "#scaled_from" do
69
+
70
+ should "take an ascending array as the destination" do
71
+ output = 0.40.scaled_from(0..1).to([0, 2, 4, 8, 12, 16, 32, 64, 128, 512])
72
+ assert_equal(8, output)
73
+ end
74
+
75
+ should "take a descending array as the destination" do
76
+ output = 0.40.scaled_from(0..1).to([512, 128, 64, 32, 16, 12, 8, 4, 2, 0])
77
+ assert_equal(32, output)
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+
84
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,8 @@
1
+ dir = File.dirname(File.expand_path(__FILE__))
2
+ $LOAD_PATH.unshift dir + '/../lib'
3
+
4
+ require 'test/unit'
5
+ require "mocha/test_unit"
6
+ require "shoulda-context"
7
+
8
+ require "scale"
@@ -0,0 +1,92 @@
1
+ require "helper"
2
+
3
+ class ScaleTest < Test::Unit::TestCase
4
+
5
+ context "Scale" do
6
+
7
+ context "#initialize" do
8
+
9
+ should "recognize a Range" do
10
+ scheme = Scale::Scheme.new(:source => 0..1, :destination => 0..100)
11
+ assert_equal(Scale::Destination::Range, scheme.destination.class)
12
+ end
13
+
14
+ should "recognize an Array" do
15
+ scheme = Scale::Scheme.new(:source => 0..1, :destination => [0, 10, 20, 50])
16
+ assert_equal(Scale::Destination::Enumerable, scheme.destination.class)
17
+ end
18
+
19
+ end
20
+
21
+ context "#scale" do
22
+
23
+ should "amplify" do
24
+ output = Scale.transform(0.10).using(0..1, 0..127)
25
+ assert_equal(12, output)
26
+ end
27
+
28
+ should "deamplify" do
29
+ output = Scale.transform(22).using(0..150, 0..15)
30
+ assert_equal(2, output)
31
+ end
32
+
33
+ should "scale neg/pos into pos/pos" do
34
+ output = Scale.transform(10).using(-24..24, 0..3.0)
35
+ assert_equal(2.125, output)
36
+ end
37
+
38
+ should "scale neg/pos into another neg/pos" do
39
+ output = Scale.transform(10).using(-24..24, -3..3.0)
40
+ assert_equal(1.25, output)
41
+ end
42
+
43
+ should "scale neg/neg into neg/pos" do
44
+ output = Scale.transform(-14).using(-24..-12, -3..3.0)
45
+ assert_equal(2, output)
46
+ end
47
+
48
+ should "scale neg/neg to another neg/neg" do
49
+ output = Scale.transform(-16).using(-24..-12, -5..-3.0)
50
+ assert_equal(-3.666666666666667, output)
51
+ end
52
+
53
+ should "scale neg/neg to pos/pos" do
54
+ output = Scale.transform(-18).using(-24..-12, 1..3.0)
55
+ assert_equal(2, output)
56
+ end
57
+
58
+ should "take an array as the source" do
59
+ output = Scale.transform(8).using([0, 2, 4, 8, 16, 64], 0..10)
60
+ assert_equal(6, output)
61
+ end
62
+
63
+ should "take an set as the source" do
64
+ output = Scale.transform(8).using(Set.new([0, 2, 4, 8, 16, 64]), 0..10)
65
+ assert_equal(6, output)
66
+ end
67
+
68
+ should "output a float when specified" do
69
+ output = Scale.transform(12).using(0..120, 0..1.0)
70
+ assert_equal(0.10, output)
71
+ end
72
+
73
+ should "take an ascending array as the destination" do
74
+ output = Scale.transform(0.40).using(0..1, [0, 2, 4, 8, 12, 16, 32, 64, 128, 512])
75
+ assert_equal(8, output)
76
+ end
77
+
78
+ should "take a descending array as the destination" do
79
+ output = Scale.transform(0.40).using(0..1, [512, 128, 64, 32, 16, 12, 8, 4, 2, 0])
80
+ assert_equal(32, output)
81
+ end
82
+
83
+ should "take a set as the destination" do
84
+ output = Scale.transform(0.40).using(0..1, Set.new([0, 2, 4, 8, 12, 16, 32, 64, 128, 512]))
85
+ assert_equal(8, output)
86
+ end
87
+
88
+ end
89
+
90
+ end
91
+
92
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: analog
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Ari Russo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mocha
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: shoulda-context
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Provides a quick way to take a number and scale it given a source and
56
+ destination Array, Range, Set, etc
57
+ email:
58
+ - ari.russo@gmail.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - LICENSE
64
+ - README.md
65
+ - lib/scale.rb
66
+ - lib/scale/core_ext.rb
67
+ - lib/scale/destination.rb
68
+ - lib/scale/scheme.rb
69
+ - lib/scale/source.rb
70
+ - test/core_ext_test.rb
71
+ - test/helper.rb
72
+ - test/scale_test.rb
73
+ homepage: http://github.com/arirusso/analog
74
+ licenses:
75
+ - Apache License (2.0)
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 1.3.6
91
+ requirements: []
92
+ rubyforge_project: analog
93
+ rubygems_version: 2.2.2
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: A Ruby helper for scaling numbers
97
+ test_files:
98
+ - test/core_ext_test.rb
99
+ - test/scale_test.rb