cossincalc 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,73 @@
1
+ module CosSinCalc
2
+ class Triangle
3
+ class Formatter
4
+ UNIT_FACTORS = { :radian => Math::PI, :degree => 180.0, :gon => 200.0 }
5
+
6
+ attr_reader :triangle, :precision # References to associated triangle object and data precision.
7
+
8
+ def initialize(triangle, precision = 2)
9
+ @triangle = triangle
10
+ @precision = precision
11
+ end
12
+
13
+ # Converts an input value to a number.
14
+ def self.parse(value)
15
+ return value if value.is_a? Float
16
+ return nil if value.nil? || value.to_s !~ /\S/
17
+
18
+ value = value.to_s.gsub(/[^\d.,]+/, '').split(/[^\d]+/)
19
+ value.push('.' + value.pop) if value.length > 1
20
+ value.join.to_f
21
+ end
22
+
23
+ # Converts an input angle value in some unit to a radian number.
24
+ def self.parse_angle(value, from_unit)
25
+ value.is_a?(Float) ? value : convert_angle(parse(value), from_unit)
26
+ end
27
+
28
+ # Converts the given value from the unit specified to radians.
29
+ # If reverse is true, the value will be converted from radians to the specified unit.
30
+ def self.convert_angle(value, unit, reverse = false)
31
+ return nil if value.nil?
32
+
33
+ factor = (Math::PI / UNIT_FACTORS[unit.to_sym])
34
+ value * (reverse ? 1.0 / factor : factor)
35
+ end
36
+
37
+ # Returns the size of the angle to the given variable, rounded and converted to the prefered unit.
38
+ def angle(v)
39
+ format(Formatter.convert_angle(t.angle(v), t.angles.unit, true), @precision)
40
+ end
41
+
42
+ private
43
+ # Shorthand-method referencing the associated triangle object.
44
+ def t
45
+ @triangle
46
+ end
47
+
48
+ # Rounds the value down to the specified amount of decimals.
49
+ def round(value, decimals)
50
+ multiplier = 10 ** decimals
51
+ (value * multiplier).round.to_f / multiplier
52
+ end
53
+
54
+ # Formats the given value to improve human readability.
55
+ def format(value, decimals)
56
+ "%.#{decimals}f" % round(value, decimals)
57
+ end
58
+
59
+ # Returns the number of significant digits of the given value.
60
+ def significant_digits(number)
61
+ number.to_s.gsub(/^[0.]+/, '').length
62
+ end
63
+
64
+ def method_missing(name, *args)
65
+ if [:side, :altitude, :median, :angle_bisector, :area, :circumference].include?(name)
66
+ format(t.send(name, *args), @precision)
67
+ else
68
+ super(name, *args)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,134 @@
1
+ module CosSinCalc
2
+ class Triangle
3
+ class Formatter
4
+ class Latex
5
+ # Initializes the LaTeX renderer.
6
+ def initialize(formatter)
7
+ @formatter = formatter
8
+ @triangle = formatter.triangle
9
+ end
10
+
11
+ # Returns the generated LaTeX code.
12
+ def to_tex(filename = nil)
13
+ document(@triangle.alt ? (<<-EOT) : document_content(filename))
14
+ #{document_content(filename ? filename + '-1' : nil)}
15
+
16
+ \\newpage
17
+ \\section*{Alternative triangle}
18
+ Another triangle can be constructed based on the variables given.\\\\[0.2 cm]
19
+
20
+ #{Latex.new(@triangle.alt.humanize(f.precision)).document_content(filename ? filename + '-2' : nil)}
21
+ EOT
22
+ end
23
+
24
+ # Saves the generated LaTeX to a TeX file.
25
+ # The filename should be provided without the .tex extension.
26
+ def save_tex(filename)
27
+ File.open("#{filename}.tex", 'w') { |f| f.write(to_tex(filename)) }
28
+ end
29
+
30
+ # Saves the generated LaTeX to a TeX file and then converts it using pdflatex.
31
+ # The filename should be provided without the .pdf extension.
32
+ def save_pdf(filename)
33
+ save_tex(filename)
34
+ `pdflatex \"#{filename}.tex\" --output-directory #{File.dirname(filename)}`
35
+ end
36
+
37
+ # Returns the content of the LaTeX document.
38
+ def document_content(image_filename = nil)
39
+ variable_table + "\\\\[0.2 cm]\n\n" + equations + "\n\n" + drawing(image_filename)
40
+ end
41
+
42
+ private
43
+ # Shorthand-method referencing the associated formatter object.
44
+ def f
45
+ @formatter
46
+ end
47
+
48
+ # Wraps the variables calculated in a LaTeX table.
49
+ def variable_table
50
+ table = "\\begin{tabular}{ r r r r r }\n"
51
+ table << ['Angles', 'Sides', 'Altitudes', 'Medians', 'Angle bisectors'].join(' & ') + " \\\\ \\hline\n"
52
+
53
+ @triangle.each do |v|
54
+ table << [ "$#{v.to_s.upcase}=#{format_angle(f.angle(v), @triangle.angles.unit)}$", "$#{v}=#{f.side(v)}$",
55
+ "$h_#{v.to_s.upcase}=#{f.altitude(v)}$", "$m_#{v}=#{f.median(v)}$",
56
+ "$t_#{v.to_s.upcase}=#{f.angle_bisector(v)}$" ].join(' & ') + " \\\\\n"
57
+ end
58
+
59
+ table << "\\end{tabular}"
60
+ end
61
+
62
+ # Adds the appropriate unit to the angle value.
63
+ def format_angle(value, unit)
64
+ value + case unit
65
+ when :degree then '\degree'
66
+ when :gon then '\unit{gon}'
67
+ when :radian then '\unit{radian}'
68
+ end
69
+ end
70
+
71
+ # Formats the equation for embedding into a LaTeX document.
72
+ def format_equation(latex, variables, angle_unit)
73
+ latex.gsub! /@pi/, (angle_unit == :radian ? '\pi' :
74
+ format_angle(CosSinCalc::Triangle::Formatter::UNIT_FACTORS[angle_unit].to_i.to_s, angle_unit))
75
+
76
+ symbols = latex.gsub /[$@]\d/ do |match|
77
+ v = variables[match[1..-1].to_i - 1]
78
+ (match[0] == ?@ ? v.to_s.upcase : v.to_s)
79
+ end
80
+
81
+ values = latex.gsub /[$@]\d/ do |match|
82
+ v = variables[match[1..-1].to_i - 1]
83
+ (match[0] == ?@ ? format_angle(f.angle(v), angle_unit) : f.side(v))
84
+ end.split('=').reverse.join('=')
85
+
86
+ symbols + ' &= ' + values
87
+ end
88
+
89
+ # Returns the list of equations performed during calculation.
90
+ def equations
91
+ "\\begin{align*}\n" +
92
+ @triangle.equations.map { |latex, vars| format_equation(latex, vars, @triangle.angles.unit) }.join("\\\\\n") +
93
+ "\n\\end{align*}"
94
+ end
95
+
96
+ # Saves the associated drawing and returns the embedding LaTeX code.
97
+ # If no filename is given no image will be saved (can be used in testing).
98
+ def drawing(filename = nil)
99
+ CosSinCalc::Triangle::Drawing.new(f).save_png(filename) if filename
100
+ "\\begin{center}\n\\includegraphics[scale=0.4]{#{filename || 'placeholder'}}\n\\end{center}"
101
+ end
102
+
103
+ # Wraps the given LaTeX content into a LaTeX document ready to write to filesystem.
104
+ def document(content)
105
+ <<-EOT
106
+ \\documentclass{article}
107
+ \\usepackage{amsmath}
108
+ \\usepackage{amsfonts}
109
+ \\usepackage{graphicx}
110
+
111
+ \\DeclareMathSymbol{*}{\\mathbin}{symbols}{"01}
112
+ \\newcommand{\\unit}[1]{\\ensuremath{\\, \\mathrm{#1}}}
113
+ \\newcommand{\\degree}{\\ensuremath{^{\\circ}}}
114
+
115
+ % Uncomment to use a comma as the decimal separator (you would still write a period in the source).
116
+ % \\DeclareMathSymbol{,}{\\mathpunct}{letters}{"3B}
117
+ % \\DeclareMathSymbol{.}{\\mathord}{letters}{"3B}
118
+ % \\DeclareMathSymbol{\\decimal}{\\mathord}{letters}{"3A}
119
+
120
+ \\title{CosSinCalc Calculation Results}
121
+ \\author{Calculated by the CosSinCalc Triangle Calculator}
122
+
123
+ \\begin{document}
124
+ \\maketitle
125
+
126
+ #{content}
127
+
128
+ \\end{document}
129
+ EOT
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,87 @@
1
+ module CosSinCalc
2
+ class Triangle
3
+ class Validator
4
+ NOT_ENOUGH_VARIABLES = '3 values must be specified.'
5
+ TOO_MANY_VARIABLES = 'Only 3 values should be specified.'
6
+ NO_SIDES = 'At least one side must be given.'
7
+ INVALID_SIDE = 'Only numbers (above zero) are accepted values for a side.'
8
+ INVALID_ANGLE = 'Only numbers (above zero) are accepted values for an angle. Furthermore the angle must remain inside the scope of the sum of all angles in the triangle.'
9
+ INVALID_TRIANGLE = 'The specified values do not match a valid triangle.'
10
+ CALCULATOR_PRECISION = 0.01
11
+
12
+ class ValidationError < Exception
13
+ attr_reader :messages, :sides_valid, :angles_valid
14
+
15
+ def initialize(messages, sides_valid = {}, angles_valid = {})
16
+ @messages, @sides_valid, @angles_valid = messages, sides_valid, angles_valid
17
+ end
18
+ end
19
+
20
+ def initialize(triangle)
21
+ @triangle = triangle
22
+ end
23
+
24
+ # Validates the triangle before calculation and raises an exception on errors.
25
+ def validate
26
+ @sides_valid, @angles_valid = {}, {}
27
+
28
+ t.each do |v|
29
+ @sides_valid[v] = t.side(v).nil? || valid_side?(t.side(v))
30
+ @angles_valid[v] = t.angle(v).nil? || valid_angle?(t.angle(v))
31
+ end
32
+
33
+ errors = error_messages
34
+ errors.empty? || (raise ValidationError.new(errors, @sides_valid, @angles_valid))
35
+ end
36
+
37
+ # Checks whether the calculation was successful and the values given/calculated match a triangle.
38
+ def validate_calculation
39
+ t.each do |v, r|
40
+ raise ValidtionError.new([INVALID_TRIANGLE]) unless valid_side?(t.side(v)) && valid_angle?(t.angle(v))
41
+
42
+ angle = t.calculate_angle_by_sides(v, r)
43
+ unless angle > t.angle(v) - CALCULATOR_PRECISION && angle < t.angle(v) + CALCULATOR_PRECISION
44
+ raise ValidtionError.new([INVALID_TRIANGLE])
45
+ end
46
+ end
47
+ return true
48
+ end
49
+
50
+ private
51
+ # Shorthand-method referencing the associated triangle object.
52
+ def t
53
+ @triangle
54
+ end
55
+
56
+ # Returns an array of unique errors raised when validating the triangle.
57
+ def error_messages
58
+ errors = []
59
+ errors << INVALID_SIDE if @sides_valid.values.include?(false)
60
+ errors << INVALID_ANGLE if @angles_valid.values.include?(false)
61
+
62
+ if errors.empty?
63
+ errors << NOT_ENOUGH_VARIABLES if total_values < 3
64
+ errors << TOO_MANY_VARIABLES if total_values > 3
65
+ errors << NO_SIDES if t.sides.amount < 1
66
+ end
67
+
68
+ return errors
69
+ end
70
+
71
+ # Returns the total number of variables given.
72
+ def total_values
73
+ @total_values ||= t.sides.amount + t.angles.amount
74
+ end
75
+
76
+ # Returns whether the value is a valid side.
77
+ def valid_side?(value)
78
+ value.is_a?(Float) && value.finite? && value > 0
79
+ end
80
+
81
+ # Returns whether the value is a valid angle.
82
+ def valid_angle?(value)
83
+ valid_side?(value) && value < Math::PI
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,24 @@
1
+ module CosSinCalc
2
+ class Triangle
3
+ class VariableHash < Hash
4
+ attr_accessor :unit
5
+
6
+ # Initializes the variables.
7
+ def initialize
8
+ super()
9
+ CosSinCalc::Triangle::VARIABLES.each { |v| self[v] = nil }
10
+ end
11
+
12
+ # If a symbol is given, returns the associated value.
13
+ # If an array of symbols is given, returns an array of the associated values.
14
+ def [](vars)
15
+ vars.is_a?(Array) ? vars.map { |v| super(v) } : super(vars)
16
+ end
17
+
18
+ # Returns the amount of variables that have a value.
19
+ def amount
20
+ self.values.compact.size
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module CosSinCalc
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cossincalc
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Molte Emil Strange Andersen
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-06-30 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: |
23
+ CosSinCalc is a web application able to calculate the variables of a triangle. The live site is located at http://cossincalc.com/. This is an offline version of the calculator.
24
+
25
+ You can use the included command line utility to generate a PDF page containing all the results, formulae and a drawing of the triangle, or you can include it as a library in your Ruby application and use just the features you care about.
26
+
27
+ email: molte@cossincalc.com
28
+ executables:
29
+ - cossincalc
30
+ extensions: []
31
+
32
+ extra_rdoc_files: []
33
+
34
+ files:
35
+ - MIT-LICENSE
36
+ - Rakefile
37
+ - lib/core_ext.rb
38
+ - lib/cossincalc/triangle/calculator.rb
39
+ - lib/cossincalc/triangle/drawing/svg.rb
40
+ - lib/cossincalc/triangle/drawing.rb
41
+ - lib/cossincalc/triangle/formatter/latex.rb
42
+ - lib/cossincalc/triangle/formatter.rb
43
+ - lib/cossincalc/triangle/validator.rb
44
+ - lib/cossincalc/triangle/variable_hash.rb
45
+ - lib/cossincalc/triangle.rb
46
+ - lib/cossincalc/version.rb
47
+ - lib/cossincalc.rb
48
+ - bin/cossincalc
49
+ - bin/trollop.rb
50
+ has_rdoc: true
51
+ homepage: http://cossincalc.com/
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 3
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.3.7
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Triangle calculator
84
+ test_files: []
85
+