mathviz 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +18 -0
- data/Manifest.txt +16 -0
- data/README.rdoc +51 -0
- data/Rakefile +25 -0
- data/examples/E_mc2.png +0 -0
- data/examples/E_mc2.rb +19 -0
- data/examples/dc.rb +58 -0
- data/examples/first.rb +35 -0
- data/examples/mathviz.rb +1 -0
- data/lib/mathviz.rb +594 -0
- data/spec/measurable_spec.rb +11 -0
- data/spec/measured_spec.rb +42 -0
- data/spec/operation_spec.rb +31 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit_spec.rb +189 -0
- data/tasks/idocs.rake +4 -0
- metadata +93 -0
data/History.txt
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
=== 1.0.0 2009-12-23
|
2
|
+
|
3
|
+
* Rename from MatLat to MathViz
|
4
|
+
* Default output file name
|
5
|
+
* Add rdoc comments and examples
|
6
|
+
* Package as gem
|
7
|
+
* Included classes/modules under MathViz namespace
|
8
|
+
|
9
|
+
=== 0.1.0 2009-12-18
|
10
|
+
|
11
|
+
* Support unit tracking
|
12
|
+
* Unary operators
|
13
|
+
* Moved operator out of box
|
14
|
+
|
15
|
+
=== 0.0.1 2009-02-12
|
16
|
+
|
17
|
+
* Generate Graphs
|
18
|
+
* Track values
|
data/Manifest.txt
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
examples/E_mc2.png
|
6
|
+
examples/E_mc2.rb
|
7
|
+
examples/dc.rb
|
8
|
+
examples/first.rb
|
9
|
+
examples/mathviz.rb
|
10
|
+
lib/mathviz.rb
|
11
|
+
spec/measurable_spec.rb
|
12
|
+
spec/measured_spec.rb
|
13
|
+
spec/operation_spec.rb
|
14
|
+
spec/spec_helper.rb
|
15
|
+
spec/unit_spec.rb
|
16
|
+
tasks/idocs.rake
|
data/README.rdoc
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
-*- rdoc -*-
|
2
|
+
|
3
|
+
= MathViz
|
4
|
+
|
5
|
+
* http://github.com/JustinLove/mathviz
|
6
|
+
|
7
|
+
== DESCRIPTION:
|
8
|
+
|
9
|
+
Turn simple equations (a = b * c) into GraphViz dot files showing relationships, values, and units.
|
10
|
+
|
11
|
+
== FEATURES/PROBLEMS:
|
12
|
+
|
13
|
+
* Adds one method to Object and several to Numeric (some direct, some by Measurable and Unit.) If you use units of measure, each unit will appear on Numeric via module Unit.
|
14
|
+
* MathViz produces textual .dot files. You will need a viewer which supports dot files directly, or Graphviz to convert them to images yourself.
|
15
|
+
|
16
|
+
== SYNOPSIS:
|
17
|
+
|
18
|
+
require 'rubygems'
|
19
|
+
require 'mathviz'
|
20
|
+
|
21
|
+
# No units are provided by default.
|
22
|
+
# You can also use 1.unit(:m).per.unit(:s) etc.
|
23
|
+
module MathViz::Units
|
24
|
+
new_units :m, :s, :kg, :lb, :joule
|
25
|
+
end
|
26
|
+
|
27
|
+
MathViz.new {
|
28
|
+
# Alternate form: MathViz.new('optional_output_filename', binding_object)
|
29
|
+
|
30
|
+
m = 140.lb * 0.45359237.kg.per.lb
|
31
|
+
c = 299_792_458.m.per.s
|
32
|
+
# capitalized values are constants in ruby, so we need the underscore
|
33
|
+
_E = (m * (c * c)) * 1.joule.per.kg.m.m.per.s.s
|
34
|
+
|
35
|
+
binding # Don't forget to return the binding
|
36
|
+
}.dot # Required to produces the actual .dot file
|
37
|
+
|
38
|
+
link:examples/E_mc2.png
|
39
|
+
== REQUIREMENTS:
|
40
|
+
|
41
|
+
* GraphvizR (aka graphviz_r)
|
42
|
+
* Graphviz - http://www.graphviz.org/
|
43
|
+
|
44
|
+
== INSTALL:
|
45
|
+
|
46
|
+
* sudo gem install mathviz
|
47
|
+
|
48
|
+
== LICENSE:
|
49
|
+
|
50
|
+
Creative Commons Attribution-Share Alike 3.0 Unported Licence
|
51
|
+
http://creativecommons.org/licenses/by-sa/3.0/
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'hoe', '>= 2.1.0'
|
3
|
+
require 'hoe'
|
4
|
+
require 'fileutils'
|
5
|
+
require './lib/mathviz'
|
6
|
+
|
7
|
+
Hoe.plugin :newgem
|
8
|
+
# Hoe.plugin :website
|
9
|
+
# Hoe.plugin :cucumberfeatures
|
10
|
+
|
11
|
+
# Generate all the Rake tasks
|
12
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
13
|
+
$hoe = Hoe.spec 'mathviz' do
|
14
|
+
self.developer 'Justin Love', 'git@JustinLove.name'
|
15
|
+
self.rubyforge_name = self.name # TODO this is default value
|
16
|
+
self.extra_deps = [['GraphvizR','>= 0.5.1']]
|
17
|
+
self.extra_rdoc_files = ['README.rdoc']
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'newgem/tasks'
|
21
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
22
|
+
|
23
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
24
|
+
# remove_task :default
|
25
|
+
# task :default => [:spec, :features]
|
data/examples/E_mc2.png
ADDED
Binary file
|
data/examples/E_mc2.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'mathviz'
|
3
|
+
|
4
|
+
# No units are provided by default.
|
5
|
+
# You can also use 1.unit(:m).per.unit(:s) etc.
|
6
|
+
module MathViz::Units
|
7
|
+
new_units :m, :s, :kg, :lb, :joule
|
8
|
+
end
|
9
|
+
|
10
|
+
MathViz.new {
|
11
|
+
# Alternate form: MathViz.new('optional_output_filename', binding_object)
|
12
|
+
|
13
|
+
m = 140.lb * 0.45359237.kg.per.lb
|
14
|
+
c = 299_792_458.m.per.s
|
15
|
+
# capitalized values are constants in ruby, so we need the underscore
|
16
|
+
_E = (m * (c * c)) * 1.joule.per.kg.m.m.per.s.s
|
17
|
+
|
18
|
+
binding # Don't forget to return the binding
|
19
|
+
}.dot # Required to produces the actual .dot file
|
data/examples/dc.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'mathviz'
|
3
|
+
|
4
|
+
module MathViz::Units
|
5
|
+
new_units :ms, :s, :frame
|
6
|
+
new_units :rev, :xx
|
7
|
+
new_units :pixel, :timel
|
8
|
+
end
|
9
|
+
|
10
|
+
MathViz.new {
|
11
|
+
pi = const 3.14159
|
12
|
+
second = 1000.ms.per.s
|
13
|
+
|
14
|
+
scale = input 1
|
15
|
+
diameter = 172.pixel
|
16
|
+
count = 10.xx.per.rev
|
17
|
+
unit = 1.per.xx
|
18
|
+
resolution = 100.ms
|
19
|
+
timeMultiplier = const 1
|
20
|
+
time = input((Time.now.to_f * 1000).floor).ms
|
21
|
+
frameRate = input 1.frame.per.s
|
22
|
+
|
23
|
+
root_position = time / resolution
|
24
|
+
unit_position = root_position / unit
|
25
|
+
un_position = unit_position / count
|
26
|
+
|
27
|
+
timels = unit * count
|
28
|
+
ms_rev = resolution * timels
|
29
|
+
rev_s = const(1000.ms.per.s) / ms_rev
|
30
|
+
real_rev_s = rev_s * timeMultiplier
|
31
|
+
rev_timel = const(1) / timels
|
32
|
+
configPerimeter = diameter * pi
|
33
|
+
perimeter = configPerimeter * scale
|
34
|
+
rev_pixel = const(1.rev) / perimeter
|
35
|
+
tick = rev_timel.min((rev_pixel * 1.pixel).max(rev_s * 1.s))
|
36
|
+
threshold = tick * 2
|
37
|
+
real_ms_rev = ms_rev / timeMultiplier
|
38
|
+
delay = tick * real_ms_rev
|
39
|
+
|
40
|
+
step = second / frameRate
|
41
|
+
passed = step * (timeMultiplier * 1.frame)
|
42
|
+
#passed = delay * timeMultiplier
|
43
|
+
ms = time + passed
|
44
|
+
|
45
|
+
root_to = ms / resolution
|
46
|
+
unit_to = root_to / unit
|
47
|
+
un_to = unit_to / count
|
48
|
+
|
49
|
+
delta = un_to - un_position
|
50
|
+
big = delta > threshold
|
51
|
+
visible = delta > rev_pixel
|
52
|
+
animatable = real_rev_s < 0.01.rev.per.s
|
53
|
+
jump = big & animatable
|
54
|
+
superfast = real_rev_s > 1.5.rev.per.s
|
55
|
+
|
56
|
+
binding
|
57
|
+
}.dot
|
58
|
+
|
data/examples/first.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'mathviz'
|
3
|
+
|
4
|
+
MathViz.new('first') {
|
5
|
+
pi = const 3.14159
|
6
|
+
|
7
|
+
scale = input 1.0
|
8
|
+
size = const 172
|
9
|
+
count = const 60
|
10
|
+
unit = const 1
|
11
|
+
calcUnit = const 1000
|
12
|
+
timeMultiplier = const 1
|
13
|
+
time = input 859294
|
14
|
+
ms = time + 500
|
15
|
+
|
16
|
+
root_position = time / calcUnit
|
17
|
+
unit_position = root_position / unit
|
18
|
+
un_position = unit_position / count
|
19
|
+
|
20
|
+
root_to = ms / calcUnit
|
21
|
+
unit_to = root_to / unit
|
22
|
+
un_to = unit_to / count
|
23
|
+
|
24
|
+
relativeTime = count * unit * calcUnit
|
25
|
+
perimeter = size * scale * pi
|
26
|
+
pixel = const(1) / perimeter
|
27
|
+
tick = relativeTime * pixel
|
28
|
+
realTime = calcUnit.min(tick.max(1000))
|
29
|
+
delay = realTime / timeMultiplier
|
30
|
+
threashold = delay * 2
|
31
|
+
delta = un_to - un_position
|
32
|
+
timeDelta = relativeTime * delta
|
33
|
+
|
34
|
+
binding
|
35
|
+
}.dot
|
data/examples/mathviz.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'mathviz')
|
data/lib/mathviz.rb
ADDED
@@ -0,0 +1,594 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'graphviz_r'
|
3
|
+
|
4
|
+
# Top level object.
|
5
|
+
class MathViz
|
6
|
+
# RubyGem version
|
7
|
+
VERSION = '1.0.0'
|
8
|
+
|
9
|
+
# Something to return instead of dividing by zero, etc.
|
10
|
+
Infinity = 1.0/0
|
11
|
+
|
12
|
+
# * base name of the output file. If omitted(falsy) it will use the top level program name.
|
13
|
+
# * Binding object, as if from 'binding'
|
14
|
+
# * A proc which returns a binding.
|
15
|
+
#
|
16
|
+
# If bind is passed, the proc will not be executed. If bind is falsy, the proc will be executed and it's return value stored.
|
17
|
+
#
|
18
|
+
# The proc is evaluated in the context of the new MathViz instance, making #const and #input directly available
|
19
|
+
def initialize(name = nil, bind = nil, &proc)
|
20
|
+
@name = name || File.basename($PROGRAM_NAME, '.rb')
|
21
|
+
@env = bind || instance_eval(&proc)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Convert a basic value (typically Numeric) into a MathViz::Term (MathViz::Constant)
|
25
|
+
def const(x)
|
26
|
+
MathViz::Constant.new(x)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Convert a basic value (typically Numeric) into a MathViz::Term (MathViz::Input)
|
30
|
+
def input(x)
|
31
|
+
MathViz::Input.new(x)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Save a Graphviz .dot file in the current directory, with name specified in the constructor. Triggers most of the actual processsing.
|
35
|
+
def dot
|
36
|
+
MathViz::Term.name_terms!(@env)
|
37
|
+
#puts MathViz::Term.list_terms(@env).map {|t| t.long}
|
38
|
+
graph = GraphvizR.new @name
|
39
|
+
graph = MathViz::Term.list_terms(@env).inject(graph) {|g, t|
|
40
|
+
t.to_dot(g)
|
41
|
+
g
|
42
|
+
}
|
43
|
+
|
44
|
+
#puts graph.to_dot
|
45
|
+
graph.output(@name + '.dot', 'dot')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Value objects that do the actual unit tracking.
|
50
|
+
#
|
51
|
+
# Contains all the interesting power tracking and cancellation.
|
52
|
+
class MathViz::Unit
|
53
|
+
# The interal representation. Current implementation method is hash-of-powers; e.g. {:m => 2, :s => -1} represents m*m/s
|
54
|
+
attr_reader :unit
|
55
|
+
|
56
|
+
# * With a symbol, creates a simple unit.
|
57
|
+
# * With a hash-of-powers, it simply copies those values.
|
58
|
+
# * Otherwise, it becomes a dimensionless unit.
|
59
|
+
def initialize(h = nil)
|
60
|
+
@unit = Hash.new(0)
|
61
|
+
case h
|
62
|
+
when Hash; @unit.merge!(h); normalize!
|
63
|
+
when Symbol: @unit[h] = 1
|
64
|
+
end
|
65
|
+
@unit.freeze
|
66
|
+
freeze
|
67
|
+
end
|
68
|
+
|
69
|
+
# Implement a simple binary operation. It verifies that the units match and returns the unit ERROR if not.
|
70
|
+
def binop(other)
|
71
|
+
if (unit != other.unit)
|
72
|
+
#p "#{to_s} !+- #{other.to_s}"
|
73
|
+
return MathViz::Unit.new(:ERROR)
|
74
|
+
end
|
75
|
+
return self
|
76
|
+
end
|
77
|
+
|
78
|
+
alias_method :+, :binop
|
79
|
+
alias_method :-, :binop
|
80
|
+
alias_method :<, :binop
|
81
|
+
alias_method :>, :binop
|
82
|
+
alias_method :==, :binop
|
83
|
+
alias_method :max, :binop
|
84
|
+
alias_method :min, :binop
|
85
|
+
alias_method :&, :binop
|
86
|
+
alias_method :|, :binop
|
87
|
+
|
88
|
+
def *(other)
|
89
|
+
x = @unit.dup
|
90
|
+
other.unit.each do |u,power|
|
91
|
+
x[u] += power
|
92
|
+
end
|
93
|
+
MathViz::Unit.new(x)
|
94
|
+
end
|
95
|
+
|
96
|
+
def /(other)
|
97
|
+
x = @unit.dup
|
98
|
+
other.unit.each do |u,power|
|
99
|
+
x[u] -= power
|
100
|
+
end
|
101
|
+
MathViz::Unit.new(x)
|
102
|
+
end
|
103
|
+
|
104
|
+
def numerator
|
105
|
+
unit.reject {|u,power| power < 0}
|
106
|
+
end
|
107
|
+
|
108
|
+
def denominator
|
109
|
+
unit.reject {|u,power| power > 0}
|
110
|
+
end
|
111
|
+
|
112
|
+
def to_s
|
113
|
+
n = stream(numerator)
|
114
|
+
d = stream(denominator)
|
115
|
+
return '' unless (n || d)
|
116
|
+
return "#{n||1}/#{d}" if d
|
117
|
+
return n
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
# Produce a string of multiplied terms
|
123
|
+
def stream(units)
|
124
|
+
x = units.map {|u,power| [u] * power.abs }.flatten.join('*')
|
125
|
+
if (x.empty?)
|
126
|
+
return nil
|
127
|
+
else
|
128
|
+
x
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Remove zero-powers
|
133
|
+
def normalize!
|
134
|
+
unit.reject! { |u,power| power == 0 }
|
135
|
+
self
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Common container for defined units.
|
140
|
+
#
|
141
|
+
# including MathViz::Units triggers extension by MathViz::Units::Class. MathViz::Units includes MathViz::Units::Class itself, so all those methods are available.
|
142
|
+
module MathViz::Units
|
143
|
+
# Provides the new_units class method to all classes with units
|
144
|
+
module Class
|
145
|
+
# Define new units (instance methods) on module MathViz::Units (where they will be picked up by everything including the module)
|
146
|
+
# Defined methods are essentialy aliases for #unit(name); see MathViz::Measurable / MathViz::Measured
|
147
|
+
def new_units(*units)
|
148
|
+
units.each do |u|
|
149
|
+
MathViz::Units.__send__ :define_method, u do
|
150
|
+
unit(u)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# extend MathViz::Units::Class
|
156
|
+
def included(host)
|
157
|
+
host.extend(MathViz::Units::Class)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
extend MathViz::Units::Class
|
162
|
+
end
|
163
|
+
|
164
|
+
# Something (i.e. Numeric) which does not have MathViz::Units, but can be turned into something which does (i.e., MathViz::Constant)
|
165
|
+
module MathViz::Measurable
|
166
|
+
include MathViz::Units
|
167
|
+
|
168
|
+
# return constant wrapping self with the specified units; see also MathViz::Units::Class#new_units
|
169
|
+
def unit(x)
|
170
|
+
MathViz::Constant.new(self).unit(x)
|
171
|
+
end
|
172
|
+
|
173
|
+
# return constant wrapping self with new units assigned to the denominator
|
174
|
+
def per
|
175
|
+
MathViz::Constant.new(self).per
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Something (i.e. MathViz::Term) which has MathViz::Units.
|
180
|
+
module MathViz::Measured
|
181
|
+
include MathViz::Units
|
182
|
+
|
183
|
+
# Return a string representation of the units portion, with space if applicable
|
184
|
+
def with_units
|
185
|
+
u = units.to_s
|
186
|
+
if (u.empty?)
|
187
|
+
u
|
188
|
+
else
|
189
|
+
' ' + u
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Add the named unit to our units and return self. See also MathViz::Units::Class#new_units
|
194
|
+
def unit(x)
|
195
|
+
@unit ||= MathViz::Unit.new
|
196
|
+
@unit_sign ||= 1
|
197
|
+
if (@unit_sign > 0)
|
198
|
+
@unit *= MathViz::Unit.new(x)
|
199
|
+
else
|
200
|
+
@unit /= MathViz::Unit.new(x)
|
201
|
+
end
|
202
|
+
self
|
203
|
+
end
|
204
|
+
|
205
|
+
# Statefull toggle numerator/denominator of unit assignment; e.g. m/s = .m.per.s
|
206
|
+
def per
|
207
|
+
@unit_sign ||= 1
|
208
|
+
@unit_sign *= -1
|
209
|
+
self
|
210
|
+
end
|
211
|
+
|
212
|
+
# attr_reader
|
213
|
+
def units
|
214
|
+
@unit || MathViz::Unit.new
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
class Numeric
|
219
|
+
include MathViz::Measurable
|
220
|
+
|
221
|
+
# Provide in operator form
|
222
|
+
def max(b)
|
223
|
+
[self, b].max
|
224
|
+
end
|
225
|
+
|
226
|
+
# Provide in operator form
|
227
|
+
def min(b)
|
228
|
+
[self, b].min
|
229
|
+
end
|
230
|
+
|
231
|
+
# Dummy defintion for all numerics. Normally only defined on things where it can possible return false.
|
232
|
+
def finite?
|
233
|
+
true
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
class Object
|
238
|
+
# Representation used for graphviz node names
|
239
|
+
def node
|
240
|
+
to_s
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Base class for graphable objects. It also contain the operators, which return MathViz::Operation subclasses.
|
245
|
+
class MathViz::Term
|
246
|
+
include MathViz::Measured
|
247
|
+
|
248
|
+
# Assign names to named MathViz::Terms, so the name can be effiently looked up from the MathViz::Term object.
|
249
|
+
def self.name_terms!(env)
|
250
|
+
eval("local_variables", env).each do |var|
|
251
|
+
value = eval(var, env)
|
252
|
+
if value.respond_to? :name=
|
253
|
+
value.name = var
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Return a list of all MathViz::Terms accessible from a binding
|
259
|
+
def self.list_terms(env)
|
260
|
+
eval("local_variables", env).map { |var|
|
261
|
+
value = eval(var, env)
|
262
|
+
if (value.kind_of?(MathViz::Term))
|
263
|
+
value
|
264
|
+
else
|
265
|
+
nil
|
266
|
+
end
|
267
|
+
}.compact
|
268
|
+
end
|
269
|
+
|
270
|
+
# Define op as a binary operator
|
271
|
+
def self.binop(op)
|
272
|
+
define_method(op) do |c|
|
273
|
+
MathViz::Operation::Binary.new(self, op, c)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# Define op as an unary operator
|
278
|
+
def self.unop(op)
|
279
|
+
define_method(op) do
|
280
|
+
MathViz::Operation::Unary.new(self, op)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
# Graphviz node name; see MathViz::Term#name_terms!
|
285
|
+
attr_accessor :name
|
286
|
+
|
287
|
+
def to_s
|
288
|
+
@name || anon
|
289
|
+
end
|
290
|
+
|
291
|
+
# A string representation of the node's data, typically calculated value with units.
|
292
|
+
def data
|
293
|
+
f = to_f
|
294
|
+
if (f.kind_of?(TrueClass) || f.kind_of?(FalseClass))
|
295
|
+
f.to_s
|
296
|
+
elsif (!f.respond_to? :finite?)
|
297
|
+
f.to_s + with_units
|
298
|
+
elsif (!f.finite?)
|
299
|
+
MathViz::Infinity.to_s
|
300
|
+
elsif (f.floor == f)
|
301
|
+
f.to_i.to_s + with_units
|
302
|
+
else
|
303
|
+
f.to_s + with_units
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def to_i
|
308
|
+
f = to_f
|
309
|
+
return MathViz::Infinity unless f.finite?
|
310
|
+
f.to_i
|
311
|
+
end
|
312
|
+
|
313
|
+
# Text label for graph nodes
|
314
|
+
def label
|
315
|
+
if (@name)
|
316
|
+
[data, node].join("\n")
|
317
|
+
else
|
318
|
+
data
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Graphviz node shape
|
323
|
+
def shape
|
324
|
+
:ellipse
|
325
|
+
end
|
326
|
+
|
327
|
+
# Graphviz node color
|
328
|
+
def color
|
329
|
+
:black
|
330
|
+
end
|
331
|
+
|
332
|
+
# Graphviz node line style
|
333
|
+
def style
|
334
|
+
if anonymous?
|
335
|
+
:dotted
|
336
|
+
elsif constant?
|
337
|
+
:solid
|
338
|
+
else
|
339
|
+
:dashed
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
# Extend Graphviz g with a representation of this object
|
344
|
+
def to_dot(g)
|
345
|
+
g[node] [:label => label, :shape => shape, :color => color, :style => style]
|
346
|
+
end
|
347
|
+
|
348
|
+
private
|
349
|
+
@@anon_master = 'A'
|
350
|
+
|
351
|
+
# Produces an unique name for #anonymous? nodes. Results are memoized for each instance.
|
352
|
+
def anon
|
353
|
+
if (@anon)
|
354
|
+
@anon
|
355
|
+
else
|
356
|
+
@anon = @@anon_master
|
357
|
+
@@anon_master = @@anon_master.succ
|
358
|
+
#puts "#{self.object_id} anon #{@anon}"
|
359
|
+
@anon
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def anonymous?
|
364
|
+
!@name
|
365
|
+
end
|
366
|
+
|
367
|
+
public
|
368
|
+
|
369
|
+
##
|
370
|
+
unop :floor
|
371
|
+
|
372
|
+
##
|
373
|
+
unop :ceil
|
374
|
+
|
375
|
+
|
376
|
+
##
|
377
|
+
binop :+
|
378
|
+
|
379
|
+
##
|
380
|
+
binop :-
|
381
|
+
|
382
|
+
##
|
383
|
+
binop :*
|
384
|
+
|
385
|
+
##
|
386
|
+
binop :/
|
387
|
+
|
388
|
+
##
|
389
|
+
binop :max
|
390
|
+
|
391
|
+
##
|
392
|
+
binop :min
|
393
|
+
|
394
|
+
##
|
395
|
+
binop :>
|
396
|
+
|
397
|
+
##
|
398
|
+
binop :<
|
399
|
+
|
400
|
+
##
|
401
|
+
binop :<=
|
402
|
+
|
403
|
+
##
|
404
|
+
binop :>=
|
405
|
+
|
406
|
+
##
|
407
|
+
binop :&
|
408
|
+
|
409
|
+
##
|
410
|
+
binop :|
|
411
|
+
|
412
|
+
##
|
413
|
+
binop :==
|
414
|
+
end
|
415
|
+
|
416
|
+
# A simple number.
|
417
|
+
#
|
418
|
+
# Also identifies the number as true constant, which affects nodes display style, so that opportunities for constant-folding can be idenified.
|
419
|
+
class MathViz::Constant < MathViz::Term
|
420
|
+
# wraps a primitive value
|
421
|
+
def initialize(a)
|
422
|
+
super()
|
423
|
+
@a = a
|
424
|
+
end
|
425
|
+
|
426
|
+
# Debugging method; string with both name and value
|
427
|
+
def long
|
428
|
+
n = @name && (@name + " = ")
|
429
|
+
"(#{n}#{to_f})"
|
430
|
+
end
|
431
|
+
|
432
|
+
# Forward to contained object
|
433
|
+
def to_f
|
434
|
+
@a.to_f
|
435
|
+
end
|
436
|
+
|
437
|
+
# Returns the units of the contained object (if any) or else it's own.
|
438
|
+
def units
|
439
|
+
if @a.respond_to? :units
|
440
|
+
@a.units
|
441
|
+
else
|
442
|
+
super
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
# Graphviz node shape
|
447
|
+
def shape
|
448
|
+
:plaintext
|
449
|
+
end
|
450
|
+
|
451
|
+
def constant?
|
452
|
+
true
|
453
|
+
end
|
454
|
+
|
455
|
+
# Forward to contained object
|
456
|
+
def finite?
|
457
|
+
@a.finite?
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
# A simple number.
|
462
|
+
#
|
463
|
+
# Derives most of it's behavior from MathViz::Constant, but also identifies the number as variable, which affects nodes display style, so that opportunities for constant-folding can be idenified.
|
464
|
+
class MathViz::Input < MathViz::Constant
|
465
|
+
# Graphiviz node shape
|
466
|
+
def shape
|
467
|
+
:ellipse
|
468
|
+
end
|
469
|
+
|
470
|
+
# false
|
471
|
+
def constant?
|
472
|
+
false
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
# Base class for MathViz::Operation::Binary and MathViz::Operation::Unary
|
477
|
+
class MathViz::Operation < MathViz::Term
|
478
|
+
# Turn the object into a MathViz::Term (MathViz::Constant) if isn't already a MathViz::Term. This allows for operator parameters to be primitive values without needing MathViz#const, MathViz#input, or units.
|
479
|
+
def term(x)
|
480
|
+
if (x.kind_of?(MathViz::Term))
|
481
|
+
x
|
482
|
+
else
|
483
|
+
MathViz::Constant.new(x)
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
# Graphviz node shape
|
488
|
+
def shape
|
489
|
+
:box
|
490
|
+
end
|
491
|
+
|
492
|
+
# Default Graphviz node color.
|
493
|
+
def color
|
494
|
+
:red
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
# Display and processing for single-value operators
|
499
|
+
class MathViz::Operation::Unary < MathViz::Operation
|
500
|
+
def initialize(a, op)
|
501
|
+
super()
|
502
|
+
@a = term(a)
|
503
|
+
@op = op
|
504
|
+
end
|
505
|
+
|
506
|
+
# Debugging method; return string of name and value.
|
507
|
+
def long
|
508
|
+
n = @name && (@name + " = ")
|
509
|
+
"(#{n}#{@a} #{@op} = #{to_f})"
|
510
|
+
end
|
511
|
+
|
512
|
+
# Extend Graphviz g with a representation of this object, and incoming connections
|
513
|
+
def to_dot(g)
|
514
|
+
super
|
515
|
+
(g[@a.node] >> g[node]) [:arrowhead => :normal, :headlabel => @op.to_s, :labeldistance => '2']
|
516
|
+
@a.to_dot(g) if (@a.respond_to?(:name) && @a.name.nil?)
|
517
|
+
end
|
518
|
+
|
519
|
+
# Apply the operator to create the derived value.
|
520
|
+
def to_f
|
521
|
+
return MathViz::Infinity unless @a.to_f.finite?
|
522
|
+
@a.to_f.__send__(@op)
|
523
|
+
end
|
524
|
+
|
525
|
+
# Forward to contained value
|
526
|
+
def units
|
527
|
+
@a.units
|
528
|
+
end
|
529
|
+
|
530
|
+
# Forward to contained value
|
531
|
+
def constant?
|
532
|
+
@a.constant?
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
# Display and processing for two-value operators
|
537
|
+
class MathViz::Operation::Binary < MathViz::Operation
|
538
|
+
def initialize(a, op, b)
|
539
|
+
super()
|
540
|
+
@a = term(a)
|
541
|
+
@op = op
|
542
|
+
@b = term(b)
|
543
|
+
end
|
544
|
+
|
545
|
+
# Debugging method; returns string of names and values
|
546
|
+
def long
|
547
|
+
n = @name && (@name + " = ")
|
548
|
+
"(#{n}#{@a} #{@op} #{@b} = #{to_f})"
|
549
|
+
end
|
550
|
+
|
551
|
+
# Graphviz node shape; differentiates comparison operators
|
552
|
+
def shape
|
553
|
+
if ([:>, :<, :>=, :<=, :&, :|, :==].include? @op)
|
554
|
+
:ellipse
|
555
|
+
else
|
556
|
+
:box
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
# Graphviz node color; differentiates basic mathematical operators (+, -, *, /)
|
561
|
+
def color
|
562
|
+
case @op
|
563
|
+
when :+: :green;
|
564
|
+
when :-: :yellow;
|
565
|
+
when :*: :blue;
|
566
|
+
when :/: :cyan;
|
567
|
+
else :red;
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
# Extend Graphviz g with a representation of this object, and incoming connections
|
572
|
+
def to_dot(g)
|
573
|
+
super
|
574
|
+
(g[@a.node] >> g[node]) [:arrowhead => :normal, :headlabel => @op.to_s, :labeldistance => '2']
|
575
|
+
(g[@b.node] >> g[node]) [:arrowhead => :onormal]
|
576
|
+
@a.to_dot(g) if (@a.respond_to?(:name) && @a.name.nil?)
|
577
|
+
@b.to_dot(g) if (@b.respond_to?(:name) && @b.name.nil?)
|
578
|
+
end
|
579
|
+
|
580
|
+
# Apply the operator to create the derived value.
|
581
|
+
def to_f
|
582
|
+
@a.to_f.__send__(@op, @b.to_f)
|
583
|
+
end
|
584
|
+
|
585
|
+
# Apply the operator to create the derived units.
|
586
|
+
def units
|
587
|
+
@a.units.__send__(@op, @b.units)
|
588
|
+
end
|
589
|
+
|
590
|
+
# True only if both operands are #constant?
|
591
|
+
def constant?
|
592
|
+
@a.constant? && @b.constant?
|
593
|
+
end
|
594
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
class Subject
|
4
|
+
include MathViz::Measured
|
5
|
+
|
6
|
+
new_units :s
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
"1"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe MathViz::Measured do
|
14
|
+
before do
|
15
|
+
@subject = Subject.new.s
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be created" do
|
19
|
+
@subject.should_not be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have a unit in it's string" do
|
23
|
+
@subject.with_units.should == " s"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should not have extra space if no unit" do
|
27
|
+
Subject.new.with_units.should == ""
|
28
|
+
end
|
29
|
+
|
30
|
+
it "can have different units" do
|
31
|
+
Subject.new.unit(:x).with_units.should == " x"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should create denominators" do
|
35
|
+
Subject.new.per.s.with_units.should == " 1/s"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should have a shorthand for new units" do
|
39
|
+
Subject.new_units(:m, :h)
|
40
|
+
Subject.new.m.per.h.with_units.should == " m/h"
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
module MathViz::Units
|
4
|
+
new_units :s
|
5
|
+
end
|
6
|
+
|
7
|
+
describe MathViz::Operation do
|
8
|
+
describe MathViz::Operation::Unary do
|
9
|
+
it "can floor infinity" do
|
10
|
+
(MathViz::Constant.new(1.0/0).floor).data.should == "Infinity"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "preserves units" do
|
14
|
+
(MathViz::Constant.new(1).s.floor).data.should == "1 s"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe MathViz::Operation::Binary do
|
19
|
+
it "reports ints as ints" do
|
20
|
+
(MathViz::Constant.new(1) * 2).data.should == "2"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "doesn't croak on divide by zero" do
|
24
|
+
(MathViz::Constant.new(220) / 0).data.should == "Infinity"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "preserves units" do
|
28
|
+
(1.s * 2).data.should == "2 s"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/unit_spec.rb
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
shared_examples_for "common combinations" do
|
4
|
+
it "can be created" do
|
5
|
+
@unit.should_not be_nil
|
6
|
+
end
|
7
|
+
|
8
|
+
it "flags a mismatch" do
|
9
|
+
(@unit + MathViz::Unit.new(:x)).to_s.should == 'ERROR'
|
10
|
+
end
|
11
|
+
|
12
|
+
it "adds with same" do
|
13
|
+
(@unit + @unit).to_s.should == @unit.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
it "multiplies with nothing" do
|
17
|
+
(@unit * MathViz::Unit.new).to_s.should == @unit.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
it "divides with nothing" do
|
21
|
+
(@unit / MathViz::Unit.new).to_s.should == @unit.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
it "cancels on division" do
|
25
|
+
(@unit / @unit).should.to_s == ''
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "MathViz::Unit" do
|
30
|
+
context "with no argument" do
|
31
|
+
before do
|
32
|
+
@unit = MathViz::Unit.new
|
33
|
+
end
|
34
|
+
|
35
|
+
it_should_behave_like "common combinations"
|
36
|
+
|
37
|
+
it "has a blank representation" do
|
38
|
+
@unit.to_s.should == ''
|
39
|
+
end
|
40
|
+
|
41
|
+
it "multiplies with numerator" do
|
42
|
+
(@unit * MathViz::Unit.new(:x)).to_s.should == 'x'
|
43
|
+
end
|
44
|
+
|
45
|
+
it "divides with numerator" do
|
46
|
+
(@unit / MathViz::Unit.new(:x)).to_s.should == '1/x'
|
47
|
+
end
|
48
|
+
|
49
|
+
it "multiplies with denominator" do
|
50
|
+
(@unit * MathViz::Unit.new(:x => -1)).to_s.should == '1/x'
|
51
|
+
end
|
52
|
+
|
53
|
+
it "divides with denominator" do
|
54
|
+
(@unit / MathViz::Unit.new(:x => -1)).to_s.should == 'x'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "with a single argument" do
|
59
|
+
before do
|
60
|
+
@unit = MathViz::Unit.new(:s)
|
61
|
+
end
|
62
|
+
|
63
|
+
it_should_behave_like "common combinations"
|
64
|
+
|
65
|
+
it "has a simple representation" do
|
66
|
+
@unit.to_s.should == "s"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "with a numerator argument" do
|
71
|
+
before do
|
72
|
+
@unit = MathViz::Unit.new(:s => 1)
|
73
|
+
end
|
74
|
+
|
75
|
+
it_should_behave_like "common combinations"
|
76
|
+
|
77
|
+
it "has a simple representation" do
|
78
|
+
@unit.to_s.should == "s"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "multiplies with numerator" do
|
82
|
+
['s*x', 'x*s'].should include((@unit * MathViz::Unit.new(:x)).to_s)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "divides with numerator" do
|
86
|
+
(@unit / MathViz::Unit.new(:x)).to_s.should == 's/x'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "multiplies with denominator" do
|
90
|
+
(@unit * MathViz::Unit.new(:x => -1)).to_s.should == 's/x'
|
91
|
+
end
|
92
|
+
|
93
|
+
it "divides with denominator" do
|
94
|
+
['s*x', 'x*s'].should include((@unit / MathViz::Unit.new(:x => -1)).to_s)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "cancels on multiplication" do
|
98
|
+
(@unit * MathViz::Unit.new(:s => -1)).to_s.should == ''
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "with a numerator squared argument" do
|
103
|
+
before do
|
104
|
+
@unit = MathViz::Unit.new(:s => 2)
|
105
|
+
end
|
106
|
+
|
107
|
+
it_should_behave_like "common combinations"
|
108
|
+
|
109
|
+
it "has a double representation" do
|
110
|
+
@unit.to_s.should == "s*s"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "multiplies with numerator" do
|
114
|
+
['s*s*x', 'x*s*s'].should include((@unit * MathViz::Unit.new(:x)).to_s)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "divides with numerator" do
|
118
|
+
(@unit / MathViz::Unit.new(:x)).to_s.should == 's*s/x'
|
119
|
+
end
|
120
|
+
|
121
|
+
it "multiplies with denominator" do
|
122
|
+
(@unit * MathViz::Unit.new(:x => -1)).to_s.should == 's*s/x'
|
123
|
+
end
|
124
|
+
|
125
|
+
it "divides with denominator" do
|
126
|
+
['s*s*x', 'x*s*s'].should include((@unit / MathViz::Unit.new(:x => -1)).to_s)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "cancels on multiplication" do
|
130
|
+
(@unit * MathViz::Unit.new(:s => -1)).to_s.should == 's'
|
131
|
+
end
|
132
|
+
|
133
|
+
it "increases on multiplication" do
|
134
|
+
(@unit * MathViz::Unit.new(:s => 1)).to_s.should == 's*s*s'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "with a denominator argument" do
|
139
|
+
before do
|
140
|
+
@unit = MathViz::Unit.new(:s => -1)
|
141
|
+
end
|
142
|
+
|
143
|
+
it_should_behave_like "common combinations"
|
144
|
+
|
145
|
+
it "has a 1 in the numerator position" do
|
146
|
+
@unit.to_s.should == "1/s"
|
147
|
+
end
|
148
|
+
|
149
|
+
it "multiplies with numerator" do
|
150
|
+
['x/s'].should include((@unit * MathViz::Unit.new(:x)).to_s)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "divides with numerator" do
|
154
|
+
['1/s*x', '1/x*s'].should include((@unit / MathViz::Unit.new(:x)).to_s)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "multiplies with denominator" do
|
158
|
+
['1/s*x', '1/x*s'].should include((@unit * MathViz::Unit.new(:x => -1)).to_s)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "divides with denominator" do
|
162
|
+
['x/s'].should include((@unit / MathViz::Unit.new(:x => -1)).to_s)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "cancels on multiplication" do
|
166
|
+
(@unit * MathViz::Unit.new(:s => 1)).to_s.should == ''
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context "with a complex argument" do
|
171
|
+
before do
|
172
|
+
@unit = MathViz::Unit.new(:V => 1, :A => 1, :h => -1)
|
173
|
+
end
|
174
|
+
|
175
|
+
it_should_behave_like "common combinations"
|
176
|
+
|
177
|
+
it "has a complex representation" do
|
178
|
+
["V*A/h", "A*V/h"].should include(@unit.to_s)
|
179
|
+
end
|
180
|
+
|
181
|
+
it "cancels on multiplication" do
|
182
|
+
["V*A", "A*V"].should include((@unit * MathViz::Unit.new(:h)).to_s)
|
183
|
+
end
|
184
|
+
|
185
|
+
it "cancels on multiplication" do
|
186
|
+
["V/h"].should include((@unit * MathViz::Unit.new(:A => -1)).to_s)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
data/tasks/idocs.rake
ADDED
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mathviz
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Justin Love
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-25 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: GraphvizR
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.5.1
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: hoe
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.3.3
|
34
|
+
version:
|
35
|
+
description: Turn simple equations (a = b * c) into GraphViz dot files showing relationships, values, and units.
|
36
|
+
email:
|
37
|
+
- git@JustinLove.name
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files:
|
43
|
+
- History.txt
|
44
|
+
- Manifest.txt
|
45
|
+
- README.rdoc
|
46
|
+
files:
|
47
|
+
- History.txt
|
48
|
+
- Manifest.txt
|
49
|
+
- README.rdoc
|
50
|
+
- Rakefile
|
51
|
+
- examples/E_mc2.png
|
52
|
+
- examples/E_mc2.rb
|
53
|
+
- examples/dc.rb
|
54
|
+
- examples/first.rb
|
55
|
+
- examples/mathviz.rb
|
56
|
+
- lib/mathviz.rb
|
57
|
+
- spec/measurable_spec.rb
|
58
|
+
- spec/measured_spec.rb
|
59
|
+
- spec/operation_spec.rb
|
60
|
+
- spec/spec_helper.rb
|
61
|
+
- spec/unit_spec.rb
|
62
|
+
- tasks/idocs.rake
|
63
|
+
has_rdoc: true
|
64
|
+
homepage: http://github.com/JustinLove/mathviz
|
65
|
+
licenses: []
|
66
|
+
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options:
|
69
|
+
- --main
|
70
|
+
- README.rdoc
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
version:
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "0"
|
84
|
+
version:
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project: mathviz
|
88
|
+
rubygems_version: 1.3.5
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: Turn simple equations (a = b * c) into GraphViz dot files showing relationships, values, and units.
|
92
|
+
test_files: []
|
93
|
+
|