Skip to content
Permalink
main
Go to file
 
 
Cannot retrieve contributors at this time
375 lines (328 sloc) 9.14 KB
-- - draw scaled rings
-- - draw paginated rings
-- - wraping
-- - "snap" led aliasing
-- - variable min/max
-- - variable sensitivity
--
-- "arc study"
_arc = arc.connect()
function init()
globals = {
arc_dirty = true,
screen_dirty = true,
encs = {},
animations = {}
}
-- defaults
for i = 1, 4 do
globals.encs[i] = {
style = "divided",
value = 0,
min = 0,
max = 64,
sensitivity = .25,
wrap = false,
clock_active = false,
this_clock = nil,
sleep_time = .5
}
globals.animations[i] = {}
globals.animations[i].active = false
end
-- overrides
globals.encs[1].min = 0
globals.encs[1].max = 300
globals.encs[1].sensitivity = 1
globals.encs[2].wrap = true
globals.encs[2].min = 50
globals.encs[2].max = 100
globals.encs[3].style = "scaled"
globals.encs[4].style = "cloakroom"
globals.encs[4].max = 3
globals.encs[4].min = -3
globals.encs[4].sensitivity = .01
-- demo
enc_1_items = 3
enc_2_items = math.random(3, 20)
-- cloakroom graphics
cloakroom_frames = {}
cloakroom_frames[-3] = "||||..."
cloakroom_frames[-2] = ".|||..."
cloakroom_frames[-1] = "..||..."
cloakroom_frames[0] = "...|..."
cloakroom_frames[1] = "...||.."
cloakroom_frames[2] = "...|||."
cloakroom_frames[3] = "...||||"
-- cloakroom degrees
cloakroom_degrees = {}
cloakroom_degrees[-3] = { from = 240, to = 359 }
cloakroom_degrees[-2] = { from = 300, to = 359 }
cloakroom_degrees[-1] = { from = 330, to = 359 }
cloakroom_degrees[0] = { from = 359, to = 1 }
cloakroom_degrees[1] = { from = 0 , to = 30 }
cloakroom_degrees[2] = { from = 0 , to = 60 }
cloakroom_degrees[3] = { from = 0 , to = 120 }
arc_clock_id = clock.run(arc_redraw_clock)
redraw_clock_id = clock.run(redraw_clock)
redraw()
end
function draw_rings()
-- ring 1
if not globals.animations[1].active then
local segs1 = divided_ring(globals.encs[1], 240, 240, enc_1_items, true)
_arc:segment(1, segs1.from, segs1.to, 15)
end
-- ring 2
if not globals.animations[2].active then
local segs2 = divided_ring(globals.encs[2], 360, 270, enc_2_items, false)
_arc:segment(2, segs2.from, segs2.to, 15)
end
-- ring 3
if not globals.animations[3].active then
local segs3 = scaled_ring(globals.encs[3], 360, 180, false)
_arc:segment(3, segs3.from, segs3.to, 15)
end
-- ring 4
if not globals.animations[4].active then
local segs4 = cloakroom_ring(globals.encs[4])
_arc:segment(4, segs4.from, segs4.to, 15)
end
end
-- enc an enc table
function cloakroom_ring(enc)
local from, to = 0, 0
local values = cloakroom_degrees[round(enc.value)]
local segments = {}
segments.from = degs_to_rads(values.from, true)
segments.to = degs_to_rads(values.to, true)
return segments
end
-- for creating a linear scale
-- enc an enc table
-- max [0-360] how much of the ring do you want to take up?
-- offset [0-360] where do you want the scale to start?
-- snap [bool] do you want to snap to the arc leds
function scaled_ring(enc, max, offset, snap)
max = (max == 360) and 359.9 or max -- compensate for circles, 0 == 360, etc.
local from = offset
local to = cycle_degrees(util.linlin(0, 360, 0, max, scale_to_degrees(enc)) + offset)
local segments = {}
segments.from = degs_to_rads(from, snap)
segments.to = degs_to_rads(to, snap)
return segments
end
-- for creating a chunky linear pagination effect
-- enc an enc table
-- max [0-360] how much of the ring do you want to take up?
-- offset [0-360] where do you want the chunk to start?
-- divisor [n] how many chunks do you want?
-- snap [bool] do you want to snap to the arc leds?
function divided_ring(enc, max, offset, divisor, snap)
local segment_size = max / divisor
local segments = {}
for i = 1, divisor do
local from_raw = offset + (segment_size * (i - 1))
local from = cycle_degrees(from_raw)
local to = cycle_degrees(from_raw + segment_size)
segments[i] = {}
segments[i].from = degs_to_rads(from, snap)
segments[i].to = degs_to_rads(to, snap)
end
local current = map_to_segment(enc, divisor)
return segments[current]
end
function map_to_segment(enc, divisor)
local segment_size = 360 / divisor
local test = util.linlin(enc.min, enc.max, 0, 360, enc.value)
if test == 360 then -- compensate for circles, 0 == 360, etc.
return divisor
else
local match = 1
for i = 1, divisor do
if (test >= segment_size * (i - 1)) and (test < segment_size * i) then
match = i
end
end
return match
end
end
function arc_redraw_clock()
while true do
draw_animations()
if globals.arc_dirty then
draw_rings()
_arc:refresh()
globals.arc_dirty = false
end
clock.sleep(1/30)
end
end
function draw_animations()
for n, v in pairs(globals.animations) do
if v.active then
if v.current <= 0 or v.current >= 360 then
globals.animations[n] = {}
globals.animations[n].active = false
else
if v.style == "winddown" then
local multiple = 15
if v.clockwise then
v.current = v.current + multiple
_arc:segment(v.enc, degs_to_rads(v.current, false), 0, 15)
else
v.current = v.current - multiple
_arc:segment(v.enc, 0, degs_to_rads(v.current, false), 15)
end
end
end
globals.arc_dirty = true
end
end
end
function register_animation(n, style, values)
globals.animations[n] = {}
globals.animations[n].enc = n
globals.animations[n].active = true
globals.animations[n].style = style
globals.animations[n].current = 0
globals.animations[n].clockwise = true
if style == "winddown" and values.from >= 180 then
globals.animations[n].current = values.from
globals.animations[n].clockwise = true
else
globals.animations[n].current = values.to
globals.animations[n].clockwise = false
end
end
function scale_to_radians(enc)
return degs_to_rads(scale_to_degrees(enc), false)
end
function scale_to_degrees(enc)
return util.linlin(enc.min, enc.max, 0, 360, enc.value)
end
function degs_to_rads(d, snap)
if snap then
d = snap_degrees_to_leds(d)
end
return d * (3.14 / 180)
end
-- to stop arc anti-aliasing
function snap_degrees_to_leds(d)
return util.linlin(0, 64, 0, 360, round(util.linlin(0, 360, 0, 64, d)))
end
function cycle_degrees(d)
if d > 360 then
return cycle_degrees(d - 360)
elseif d < 0 then
return cycle_degrees(360 - d)
else
return d
end
end
function cycle(i, min, max)
if i < min then
return max
elseif i > max then
return min
else
return i
end
end
function update_enc(n, delta)
-- which enc are we operating on
local this_enc = globals.encs[n]
-- cancel any animations for this enc
globals.animations[n] = {}
globals.animations[n].active = false
-- end the clock for this encoder
if this_enc.this_clock ~= nil then
this_enc.clock_active = false
clock.cancel(this_enc.this_clock)
this_enc.this_clock = nil
end
if this_enc.wrap then
this_enc.value = util.clamp(
cycle(
this_enc.value + (this_enc.sensitivity * delta),
this_enc.min,
this_enc.max
),
this_enc.min,
this_enc.max
)
else
this_enc.value = util.clamp(
this_enc.value + (this_enc.sensitivity * delta),
this_enc.min,
this_enc.max
)
end
globals.screen_dirty = true
screen.ping()
-- start the clock for this encoder
if this_enc.this_clock == nil then
this_enc.clock_active = true
this_enc.this_clock = clock.run(enc_wait, n)
end
end
function enc_wait(n)
clock.sleep(globals.encs[n].sleep_time)
globals.encs[n].clock_active = false
enc_cleanup(n)
end
function enc_cleanup(n)
if globals.encs[n].style == "cloakroom" then
local values = cloakroom_degrees[round(globals.encs[n].value)]
globals.encs[n].value = 0
globals.screen_dirty = true
register_animation(n, "winddown", values)
end
end
function round(f)
return f > 0 and math.ceil(f) or math.floor(f)
end
function _arc.delta(n, delta)
update_enc(n, delta)
globals.arc_dirty = true
end
function redraw_clock()
while true do
if globals.screen_dirty then
redraw()
globals.screen_dirty = false
end
clock.sleep(1/30)
end
end
function redraw()
screen.clear()
for i = 1, #globals.encs do
local col = (i - 1) * 32
screen.level(5)
screen.move(col, 10)
screen.text("ENC " .. i)
screen.level(15)
screen.move(col, 20)
screen.text(globals.encs[i].value)
screen.level(5)
screen.move(col, 30)
screen.text(globals.encs[i].min)
screen.move(col, 40)
screen.text(globals.encs[i].max)
screen.move(col, 50)
screen.text(globals.encs[i].sensitivity)
screen.move(col, 60)
if i == 1 then
screen.text(map_to_segment(globals.encs[i], enc_1_items) .. "/" .. enc_1_items)
elseif i == 2 then
screen.text(map_to_segment(globals.encs[i], enc_2_items) .. "/" .. enc_2_items)
elseif i == 4 then
screen.text(cloakroom_frames[round(globals.encs[i].value)])
end
screen.update()
end
end
function rerun()
norns.script.load(norns.state.script)
end