You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

157 lines
5.7KB

  1. require 'color'
  2. require_relative 'controls/button'
  3. require_relative 'controls/counter'
  4. require_relative 'controls/knob'
  5. require_relative 'controls/label'
  6. require_relative 'controls/line'
  7. require_relative 'controls/port'
  8. require_relative 'controls/toggle'
  9. require_relative 'svg_file'
  10. module DHE
  11. JSON_PARSING_OPTIONS = { symbol_keys: true }
  12. class Module
  13. attr_reader :name, :slug, :foreground, :background, :controls
  14. def initialize(name:, hp:, foreground:, background:)
  15. @name = name
  16. @slug = Pathname(@name.downcase.sub(' ', '-'))
  17. @width = hp * MM_PER_HP
  18. @width_px = @width * PX_PER_MM
  19. @height_px = PANEL_HEIGHT * PX_PER_MM
  20. @foreground = "##{Color::HSL.new(*foreground).to_rgb.hex}"
  21. @background = "##{Color::HSL.new(*background).to_rgb.hex}"
  22. @faceplate_items = [
  23. Label.new(faceplate: self, text: @name, size: :title, x: @width / 2, y: PANEL_LABEL_INSET),
  24. Label.new(faceplate: self, text: 'DHE', size: :title, alignment: :below, x: @width / 2, y: PANEL_HEIGHT - PANEL_LABEL_INSET)
  25. ]
  26. @controls = []
  27. end
  28. def faceplate_file
  29. SvgFile.new(path: slug, width: @width_px, height: @height_px, has_text: true) do |svg|
  30. svg.g(transform: "scale(#{PX_PER_MM})") do |g|
  31. g.rect(x: 0, y: 0, width: @width, height: PANEL_HEIGHT,
  32. stroke: @foreground, fill: @background, 'stroke-width' => 1)
  33. @faceplate_items.each do |item|
  34. item.draw_faceplate(svg: g)
  35. end
  36. end
  37. end
  38. end
  39. def image_file
  40. SvgFile.new(path: slug, width: @width_px, height: @height_px, has_text: true) do |svg|
  41. svg.g(transform: "scale(#{PX_PER_MM})") do |g|
  42. g.rect(x: 0, y: 0, width: @width, height: PANEL_HEIGHT,
  43. stroke: @foreground, fill: @background, 'stroke-width' => 1)
  44. @faceplate_items.each do |item|
  45. item.draw_faceplate(svg: g)
  46. end
  47. @controls.each do |control|
  48. control.draw_faceplate(svg: g)
  49. end
  50. end
  51. end
  52. end
  53. def control_files
  54. @controls.flat_map(&:svg_files)
  55. end
  56. def attenuverter(x:, y:)
  57. knob(x: x, y: y, size: :tiny, label: '<tspan font-size="larger">-&#160;&#160;+</tspan>', label_size: :large)
  58. end
  59. def button(x:, y:, label:)
  60. button = Button.new(faceplate: self, x: x, y: y)
  61. label = Label.new(faceplate: self, text: label, size: :small, x: x, y: button.top - PADDING)
  62. @faceplate_items << label
  63. @controls << button
  64. end
  65. def connector(left:, right:, y:)
  66. @faceplate_items << Line.new(faceplate: self, x1: left, y1: y, x2: right, y2: y)
  67. end
  68. def counter(x:, y:, name:, labels:, selection: 1, enabled: true)
  69. counter = Counter.new(faceplate: self, x: x, y: y, name: name, labels: labels,
  70. selection: selection, enabled: enabled)
  71. @controls << counter
  72. end
  73. def cv_port(x:, y:)
  74. port = Port.new(faceplate: self, x: x, y: y)
  75. label = Label.new(faceplate: self, text: 'CV', size: :small, x: x, y: port.top - PADDING)
  76. @faceplate_items << label
  77. @controls << port
  78. end
  79. def duration_toggle(x:, y:)
  80. toggle(x: x, y: y, labels: %w(1 10 100), selection: 2)
  81. end
  82. def input_port(x:, y:, label: 'IN')
  83. port = Port.new(faceplate: self, x: x, y: y)
  84. label = Label.new(faceplate: self, text: label, size: :small, x: x, y: port.top - PADDING)
  85. @faceplate_items << Box.new(faceplate: self, top: label.top, right: port.right, bottom: port.bottom, left: port.left)
  86. @faceplate_items << label
  87. @controls << port
  88. end
  89. def knob(x:, y:, label:, size:, label_size:)
  90. knob = Knob.new(faceplate: self, size: size, x: x, y: y)
  91. @controls << knob
  92. @faceplate_items << Label.new(faceplate: self, text: label, size: label_size, x: x, y: knob.top - PADDING)
  93. end
  94. def large_knob(x:, y:, label:)
  95. knob(x: x, y: y, size: :large, label: label, label_size: :large)
  96. end
  97. def medium_knob(x:, y:, label:)
  98. knob(x: x, y: y, size: :medium, label: label, label_size: :small)
  99. end
  100. def output_port(x:, y:, label: 'OUT')
  101. port = Port.new(faceplate: self, x: x, y: y)
  102. label = Label.new(faceplate: self, text: label, size: :small, x: x, y: port.top - PADDING, style: :reversed)
  103. @faceplate_items << Box.new(faceplate: self, style: :reversed, top: label.top, right: port.right, bottom: port.bottom, left: port.left)
  104. @faceplate_items << label
  105. @controls << port
  106. end
  107. def polarity_toggle(x:, y:, selection: 1)
  108. toggle(x: x, y: y, labels: %w(BI UNI), selection: selection)
  109. end
  110. def separator(y:)
  111. @faceplate_items << Line.new(faceplate: self, x1: 0, y1: y, x2: @width, y2: y)
  112. end
  113. def shape_toggle(x:, y:)
  114. toggle(x: x, y: y, labels: %w(J S), selection: 1)
  115. end
  116. def small_knob(x:, y:, label:)
  117. knob(x: x, y: y, size: :small, label: label, label_size: :small)
  118. end
  119. def toggle(x:, y:, labels:, selection:)
  120. toggle = Toggle.new(faceplate: self, x: x, y: y, size: labels.size, selection: selection)
  121. @controls << toggle
  122. @faceplate_items << Label.new(faceplate: self, text: labels.first, size: :small,
  123. x: x, y: toggle.bottom + PADDING,
  124. alignment: :below)
  125. @faceplate_items << Label.new(faceplate: self, text: labels[1], size: :small,
  126. x: toggle.right + PADDING / 2.0, y: y,
  127. alignment: :right_of) if labels.size == 3
  128. @faceplate_items << Label.new(faceplate: self, text: labels.last, size: :small,
  129. x: x, y: toggle.top - PADDING,
  130. alignment: :above)
  131. end
  132. end
  133. end