|
- class Bounded
- attr_reader :top, :right, :bottom, :left
-
- def initialize(top:, right:, bottom:, left:)
- @top = top
- @right = right
- @bottom = bottom
- @left = left
- end
-
- def width
- @right - @left
- end
-
- def height
- @bottom - @top
- end
-
- def center
- OpenStruct.new(x: width / 2.0 + left, y: height / 2.0 + top)
- end
-
- def translate(delta_x: 0, delta_y: 0)
- @top += delta_y
- @right += delta_x
- @bottom += delta_y
- @left += delta_x
- end
-
- def move_center_to(x: center.x, y: center.y)
- delta_x = x - center.x
- delta_y = y - center.y
- translate(delta_x: delta_x, delta_y: delta_y)
- end
- end
-
- class Control < Bounded
- def initialize(x:, y:, width:, height: width)
- super(top: y - height / 2.0, right: x + width / 2.0, bottom: y + height / 2.0, left: x - width / 2.0)
- end
- end
-
- class RoundControl < Control
- attr_reader :diameter
-
- def initialize(x:, y:, diameter:)
- super(x: x, y: y, width: diameter)
- @diameter = diameter
- end
-
- def radius
- diameter / 2.0
- end
- end
-
- class ButtonControl < RoundControl
- DIAMETER = 6.0
-
- def initialize(x: 0.0, y: 0.0, style:, state:, dark:, light:)
- super(x: x, y: y, diameter: DIAMETER)
- @style = style.to_sym
- @state = state.to_sym
- @button_color = @style == :dark ? dark : light
- if @style == :dark
- @state_color = @state == :on ? light : dark
- else
- @state_color = @state == :on ? dark : light
- end
- end
-
- def name
- "button-#{@style}-#{@state}"
- end
-
- def align(padding, alignment, other)
- new_x = case alignment
- when :right_of
- other.right + padding + radius
- when :left_of
- other.left - padding - radius
- else
- center.x
- end
- move_center_to(x: new_x, y: other.center.y)
- end
-
- def svg
- stroke_width = diameter / 6.0
- circle_diameter = diameter - stroke_width
- circle_radius = circle_diameter / 2.0
- %Q[
- <circle cx="#{center.x}" cy="#{center.y}" r="#{circle_radius}" stroke-width="#{stroke_width}" fill="#{@state_color}" stroke="#{@button_color}"/>
- ]
- end
- end
-
- class KnobControl < RoundControl
- DIAMETER = 12.7
-
- def initialize(x: 0.0, y: 0.0, knob_color:, pointer_color:)
- super(x: x, y: y, diameter: 12.7)
- @knob_color = knob_color
- @pointer_color = pointer_color
- end
-
- def name
- 'knob-large'
- end
-
- def svg
- pointer_width = radius / 8.0
- pointer_length = radius - pointer_width
- %Q[
- <g transform="translate(#{center.x} #{center.y})" stroke="#{@pointer_color}" fill="#{@knob_color }">
- <circle r="#{radius}" stroke="none"/>
- <line y2="-#{pointer_length}" stroke-width="#{pointer_width }" stroke-linecap="round"/>
- </g>
- ]
- end
- end
-
- class PortControl < RoundControl
- DIAMETER = 8.4
-
- def initialize(x: 0.0, y: 0.0, metal_color:, shadow_color:)
- super(x: x, y: y, diameter: 8.4)
- @metal_color = metal_color
- @shadow_color = shadow_color
- end
-
- def name
- 'port'
- end
-
- def svg
- stroke_width = diameter * 0.025
- sleeve_diameter = diameter - stroke_width
- step = sleeve_diameter / 7.0
- sleeve_radius = sleeve_diameter / 2.0
- ring_radius = sleeve_radius - step
- tip_radius = ring_radius - step
- %Q[
- <g transform="translate(#{center.x} #{center.y})" stroke="#{@shadow_color}" fill="#{@metal_color}" stroke-width="#{stroke_width}">
- <circle r="#{sleeve_radius}"/>
- <circle r="#{ring_radius }"/>
- <circle r="#{tip_radius}" fill="#{@shadow_color}"/>
- </g>
- ]
- end
- end
-
- class SwitchControl < Control
- WIDTH = 3.0
-
- def initialize(x: 0.0, y: 0.0, positions:, state:, dark:, light:)
- super(x: x, y: y, width: WIDTH, height: positions * WIDTH)
- @positions = positions
- @state = state
- @dark = dark
- @light = light
- @position =
- case @state
- when :high
- 1.0
- when :low
- -1.0
- else
- 0.0
- end
- end
-
- def name
- "switch-#{@positions}-#{@state}"
- end
-
- def svg
- box_stroke_width = width / 8.0
- interior_inset = box_stroke_width / 2.0
-
- box_width = width - box_stroke_width
- box_height = height - box_stroke_width
- box_left = -width / 2.0 + interior_inset
- box_top = -height / 2.0 + interior_inset
-
- interior_width = box_width - box_stroke_width
- interior_height = box_height - box_stroke_width
- corner_radius = interior_inset
-
- knurl_stroke_width = 0.25
- knurl_inset = knurl_stroke_width * 2.0
- knurl_length = interior_width - knurl_inset
- knurl_left = knurl_length / -2.0
- knurl_right = knurl_left + knurl_length
- knurl_spacing = knurl_stroke_width * 2.0
-
- lever_height = knurl_spacing * 4.0 + knurl_stroke_width
- lever_inset = knurl_stroke_width
- lever_distance = (interior_height - lever_height) / 2.0 - lever_inset
- lever_offset = lever_distance * -@position
- lever = (-2..2)
- .map {|index| knurl_spacing * index + lever_offset}
- .map {|y| %Q[<line x1="#{knurl_left}" x2="#{knurl_right}" y1="#{y}" y2="#{y}" stroke-width="#{knurl_stroke_width}" stroke-linecap="round"/>]}
- .join("\n")
- %Q[
- <g transform="translate(#{center.x} #{center.y})" fill="#{@light}" stroke="#{@dark}">
- <rect x="#{box_left}" y="#{box_top}" width="#{box_width}" height="#{box_height}"
- rx="#{corner_radius}" ry="#{corner_radius}"
- stroke-width="#{box_stroke_width}"/>
- #{lever}
- </g>
- ]
- end
- end
|