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[
]
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[
]
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[
]
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[]}
.join("\n")
%Q[
#{lever}
]
end
end