module CosSinCalc class Triangle class Drawing module Svg VERTEX_LABEL_MARGIN = 10 # The distance between the middle of the vertex label and the vertex itself. VERTEX_VALUE_MARGIN = 55 # The distance between the middle of the vertex value and the vertex itself. FONT_SIZE = 12 # The font size of the labels. ARC_RADIUS = 25 # The radius of the circular arcs at the vertices. NEXT_VARIABLE = { :a => :b, :b => :c, :c => :a } # The association between a variable and the next. # Returns a drawing of the triangle in SVG (Scalable Vector Graphics) format. def to_svg draw invert_coords polygon = @coords.values.map { |c| c.join(',') }.join(' ') labels = '' t.each { |v| labels << vertex_label(v) << vertex_arc(v) << vertex_value(v) } t.each { |v| labels << edge_label(v) } # Needs to be drawn last in order to make ImageMagick render it correctly. <<-EOT #{labels} EOT end # Saves a drawing of the triangle as an PNG file. # The filename should be provided without the .png extension. def save_png(filename) save_svg(filename) system "convert \"#{filename}.svg\" \"#{filename}.png\"" end # Saves a drawing of the triangle as an SVG file. # The filename should be provided without the .svg extension. def save_svg(filename) File.open("#{filename}.svg", 'w') { |f| f.write(to_svg) } end private # Returns the SVG code for a label containing the given text at the given position. def label(x, y, text, attributes = nil) %[#{text}\n] end # Returns the equivalent cartesian coordiantes (x and y) of the given polar coordiantes (angle and distance). def polar_to_cartesian(angle, distance) [ Math.cos(angle), Math.sin(angle) ].map { |val| distance * val } end # Returns the coordiantes of the vertex label of the given variable. def vertex_label_coords(v) x, y = *polar_to_cartesian(bisector_angle_to_x(v), VERTEX_LABEL_MARGIN) [ @coords[v][0] - x, @coords[v][1] + y ] end # Returns the SVG code for the vertex label of the given variable. def vertex_label(v) x, y = *vertex_label_coords(v) label(x, y, v.to_s.upcase) end # Returns the angle between the first edge (the right relative to the angle) connected to the given verted and the x-axis. def angle_to_x(v) case v when :a then 0.0 when :b then -(t.angle(:b) + t.angle(:c)) when :c then Math::PI - t.angle(:c) end end # Returns the angle between the angle bisector of the given vertex and the x-axis. def bisector_angle_to_x(v) t.angle(v) / 2 + angle_to_x(v) end # Returns the SVG code for the circular arcs at the given vertex. def vertex_arc(v) x1, y1 = *polar_to_cartesian(angle_to_x(v), ARC_RADIUS) x2, y2 = *polar_to_cartesian(angle_to_x(v) + t.angle(v), ARC_RADIUS) %[\n] end # Returns the coordiantes of the vertex value label of the given variable. def vertex_value_coords(v) x, y = *polar_to_cartesian(bisector_angle_to_x(v), VERTEX_VALUE_MARGIN) [ @coords[v][0] + x, @coords[v][1] - y ] end # Returns the SVG code for the vertex label containing the value of the angle including the unit. def vertex_value(v) x, y = *vertex_value_coords(v) label(x, y, format_angle(f.angle(v), t.angles.unit)) end # Adds the appropriate unit to the angle value. def format_angle(value, unit) if unit == :degree value + '°' else value + ' gon' end end # Returns the SVG code for the given edge label. def edge_label(v) r = t.rest(v) x = (@coords[r[1]][0] - @coords[r[0]][0]) / 2 + @coords[r[0]][0] y = (@coords[r[1]][1] - @coords[r[0]][1]) / 2 + @coords[r[0]][1] v2 = NEXT_VARIABLE[v] angle = CosSinCalc::Triangle::Formatter.convert_angle(angle_to_x(v2) + t.angle(v2), :degree, true) text = "#{v} = #{f.side(v)}" if angle < 90 && angle > -90 label(x, y - FONT_SIZE, text, %[transform="rotate(#{-angle} #{x},#{y})"]) else label(x, y + FONT_SIZE, text, %[transform="rotate(#{180 - angle}, #{x},#{y})"]) end end end end end end