# frozen_string_literal: false # Trident specification sheet arrows. # # These methods should never need to be called directly. # @private class USPSFlags class Helpers class SpecArrows class << self # Creates a vertical arrow for the trident spec sheet. # # This is used USPSFlags::Core.trident_spec, and should never need to be called directly. # @private def vertical(x, top, bottom, **options) options = defaults.merge(options) load_common_config(options[:fly]) label, label_fraction = get_labels(bottom, top) options = options.merge(x: x, top: top, bottom: bottom, label: label, label_fraction: label_fraction) vertical_svg_group(options) end # Creates a horizontal arrow for the trident spec sheet. # # This is used USPSFlags::Core.trident_spec, and should never need to be called directly. # @private def horizontal(y, left, right, **options) options = defaults(:h).merge(options) load_common_config(options[:fly]) label, label_fraction = get_labels(right, left) options = options.merge(y: y, left: left, right: right, label: label, label_fraction: label_fraction) horizontal_svg_group(options) end private def defaults(mode = :v) { pointer_top: nil, pointer_bottom: nil, pointer_left: nil, pointer_right: nil, fly: nil, unit: nil, label_offset: (USPSFlags::Config::BASE_FLY / (mode == :v ? 120 : 45)), label_offset_x: 0, label_offset_y: 0, label_align: (mode == :v ? 'left' : 'middle') } end def vertical_svg_group(options) svg = +'' svg << arrow_pointer(options) unless options[:pointer_top].nil? svg << arrow_pointer(options) unless options[:pointer_bottom].nil? svg << vertical_svg(options) svg end def vertical_svg(options) <<~SVG <path d="#{vertical_svg_path(options)}" fill="none" /> <g> <style><![CDATA[tspan{font-size: #{USPSFlags::Config::FRACTION_SCALE}%;}]]></style> #{vertical_svg_text(options)} </g> SVG end def vertical_svg_path(options) <<~SVG M#{options[:x]} #{options[:top]} l #{@arrow_size} #{@arrow_size} M#{options[:x]} #{options[:top]} l -#{@arrow_size} #{@arrow_size} M#{options[:x]} #{options[:top]} l 0 #{options[:bottom] - options[:top]} l #{@arrow_size} -#{@arrow_size} M#{options[:x]} #{options[:bottom]} l -#{@arrow_size} -#{@arrow_size}" stroke="#{@color}" stroke-width="#{@stroke_width} SVG end def vertical_svg_text(options) <<~SVG <text x="#{options[:x] + options[:label_offset]}" y="#{(options[:top] + options[:bottom]) / 2 + (USPSFlags::Config::BASE_HOIST / 150) + options[:label_offset_y]}" font-family="sans-serif" font-size="#{@font_size}px" fill="#041E42" text-anchor="#{options[:label_align]}">#{options[:label]} <tspan>#{options[:label_fraction]}</tspan> #{options[:unit]}</text> SVG end def horizontal_svg_group(options) svg = +'' svg << arrow_pointer(options) unless options[:pointer_left].nil? svg << arrow_pointer(options) unless options[:pointer_right].nil? svg << horizontal_svg(options) svg end def horizontal_svg(options) <<~SVG <path d="#{horizontal_svg_path(options)}" stroke="#{@color}" stroke-width="#{@stroke_width}" fill="none" /> <g> <style><![CDATA[tspan{font-size: #{USPSFlags::Config::FRACTION_SCALE}%;}]]></style> #{horizontal_svg_text(options)} </g> SVG end def horizontal_svg_path(options) <<~SVG M#{options[:left]} #{options[:y]} l #{@arrow_size} #{@arrow_size} M#{options[:left]} #{options[:y]} l #{@arrow_size} -#{@arrow_size} M#{options[:left]} #{options[:y]} l #{options[:right] - options[:left]} 0 l -#{@arrow_size} -#{@arrow_size} M#{options[:right]} #{options[:y]} l -#{@arrow_size} #{@arrow_size} SVG end def horizontal_svg_text(options) <<~SVG <text x="#{(options[:left] + options[:right]) / 2 + options[:label_offset_x]}" y="#{options[:y] + options[:label_offset]}" font-family="sans-serif" font-size="#{@font_size}px" fill="#041E42" text-anchor="#{options[:label_align]}">#{options[:label]} <tspan>#{options[:label_fraction]}</tspan> #{options[:unit]}</text> SVG end def load_common_config(fly) @color = '#CCCCCC' @stroke_width = (USPSFlags::Config::BASE_FLY / 600) @stroke_dash = '10, 10' @font_size = (USPSFlags::Config::BASE_FLY / 60) @arrow_size = (USPSFlags::Config::BASE_FLY / 120) @fly = fly || USPSFlags::Config::BASE_FLY end def arrow_pointer(options) <<~SVG <line x1="#{options[:x1]}" y1="#{options[:y1]}" x2="#{options[:x2]}" y2="#{options[:y2]}" stroke="#{@color}" stroke-width="#{@stroke_width}" stroke-dasharray="#{@stroke_dash}" /> SVG end def get_labels(a, b) label = (a - b) * Rational(@fly, USPSFlags::Config::BASE_FLY) if label == label.to_i label = label.to_i label_fraction = '' else label, label_fraction = USPSFlags::Rational.new(label).to_simplified_a end [label, label_fraction] end end end end end