TFLCL
1 year ago
1 changed files with 382 additions and 0 deletions
@ -0,0 +1,382 @@ |
|||||||
|
// Théophile Clet - september 2023
|
||||||
|
// contact@tflcl.xyz - https://tflcl.xyz
|
||||||
|
// Max jsui knob with range and randomization features.
|
||||||
|
// Still a work in progress.
|
||||||
|
// License CC-BY-4.0
|
||||||
|
|
||||||
|
|
||||||
|
mgraphics.init(); |
||||||
|
mgraphics.relative_coords = 1; |
||||||
|
mgraphics.autofill = 0; |
||||||
|
|
||||||
|
outlets = 3; |
||||||
|
|
||||||
|
//UI variables
|
||||||
|
var knob_color = [0.2, 0.2, 0.2, 1.]; |
||||||
|
var pointer_color = [1., 1., 1., 1.]; |
||||||
|
var range_color = [0.702, 0.416, 0.886, 0.9]; |
||||||
|
var range_color_locked = [0.74, 0.63, 0.74, 0.9]; |
||||||
|
var indicator_color = [0., 0., 0., 1]; |
||||||
|
var lock_color = [1., 1., 1., 1]; |
||||||
|
var text_color = [1., 1., 1., 1.]; |
||||||
|
|
||||||
|
var knob_radius = 0.8; |
||||||
|
var knob_width = 0.1; |
||||||
|
var pointer_length = 0.9; |
||||||
|
var pointer_offset = 0.; |
||||||
|
var pointer_width = 0.15; |
||||||
|
var range_width = 0.2; |
||||||
|
var range_radius = 0.85; |
||||||
|
var indicator_lenght = 0.1; |
||||||
|
// var indicator_width = 0.03;
|
||||||
|
var dead_angle = 0.23; |
||||||
|
|
||||||
|
var lock_pos = [0.7, 0.75]; |
||||||
|
var lock_dim = [0.25, 0.17]; |
||||||
|
var lock_width = 0.03; |
||||||
|
|
||||||
|
var font_name ="Ableton Sans Bold"; |
||||||
|
var font_size = 9.; |
||||||
|
|
||||||
|
//Knob variables
|
||||||
|
var val, val_scaled, val_default = 0; |
||||||
|
var locked = 0; |
||||||
|
|
||||||
|
var range_offset = 0.2; |
||||||
|
var range_length = 0.5; |
||||||
|
var range = [0., 1.]; |
||||||
|
var range_scaled = []; |
||||||
|
var output_range = [0., 1.]; |
||||||
|
|
||||||
|
//Utility variables
|
||||||
|
var width = this.box.rect[2] - this.box.rect[0]; |
||||||
|
var height = this.box.rect[3] - this.box.rect[1]; |
||||||
|
var ratio = width/height; |
||||||
|
var TWOPI = 2 * Math.PI; |
||||||
|
var rdm = new Rx256(); //random engine
|
||||||
|
var last_x, last_y = 0; |
||||||
|
var theta, rtheta1, rtheta2; |
||||||
|
var theta_s = (0.75 - dead_angle * 0.5) * TWOPI; |
||||||
|
var theta_e = (dead_angle * 0.5 - 0.25) * TWOPI; |
||||||
|
var cos_s = Math.cos(theta_s); |
||||||
|
var sin_s = Math.sin(theta_s); |
||||||
|
var cos_e = Math.cos(theta_e); |
||||||
|
var sin_e = Math.sin(theta_e); |
||||||
|
var text; |
||||||
|
var t_size = []; |
||||||
|
var pattrVar = []; |
||||||
|
|
||||||
|
// arg 0: filename
|
||||||
|
// arg 1: default value
|
||||||
|
// arg 2: min output range
|
||||||
|
// arg 3: max output range
|
||||||
|
// arg 4: min random range
|
||||||
|
// arg 5: max random range
|
||||||
|
if (jsarguments.length == 6) { |
||||||
|
val_default = jsarguments[1]; |
||||||
|
output_range = [jsarguments[2], jsarguments[3]]; |
||||||
|
range = calc_range_2_norm([jsarguments[4],jsarguments[5]]); |
||||||
|
} |
||||||
|
if (jsarguments.length == 5) { |
||||||
|
output_range = [jsarguments[1], jsarguments[2]]; |
||||||
|
range = calc_range_2_norm([jsarguments[3],jsarguments[4]]); |
||||||
|
} |
||||||
|
if (jsarguments.length == 4) { |
||||||
|
val_default = jsarguments[1]; |
||||||
|
output_range = [jsarguments[2], jsarguments[3]]; |
||||||
|
} |
||||||
|
if (jsarguments.length == 3) { |
||||||
|
output_range = [jsarguments[1], jsarguments[2]]; |
||||||
|
} |
||||||
|
if (jsarguments.length == 2) { |
||||||
|
val_default = jsarguments[1]; |
||||||
|
} |
||||||
|
set_scaled(val_default); |
||||||
|
|
||||||
|
|
||||||
|
function loadbang() { |
||||||
|
calc_font_size(); |
||||||
|
update(); |
||||||
|
} |
||||||
|
|
||||||
|
// loadbang();
|
||||||
|
function paint() { |
||||||
|
with (mgraphics) { |
||||||
|
save(); |
||||||
|
scale(0.9, 0.9); |
||||||
|
translate(0.1, 0.3); |
||||||
|
|
||||||
|
//DRAW RANGE
|
||||||
|
set_source_rgba(locked ? range_color_locked : range_color); |
||||||
|
set_line_width(range_width); |
||||||
|
rtheta1 = (0.25 + range[0] * (1 - dead_angle) + dead_angle * 0.5) * TWOPI; |
||||||
|
rtheta2 = (0.25 + range[1] * (1 - dead_angle) + dead_angle * 0.5) * TWOPI; |
||||||
|
arc(0, 0, range_radius, rtheta1, rtheta2); |
||||||
|
stroke(); |
||||||
|
|
||||||
|
//DRAW POT BODY
|
||||||
|
set_source_rgba(knob_color); |
||||||
|
set_line_width(knob_width); |
||||||
|
set_line_cap('round'); |
||||||
|
move_to(cos_s * (knob_radius + indicator_lenght), sin_s * (knob_radius + indicator_lenght)); |
||||||
|
rel_line_to(-cos_s * indicator_lenght, -sin_s * indicator_lenght); |
||||||
|
arc(0, 0, knob_radius - knob_width / 2, -theta_s, -theta_e); |
||||||
|
rel_line_to((indicator_lenght + knob_width/2) * cos_e, (indicator_lenght + knob_width/2) * sin_e); |
||||||
|
stroke(); |
||||||
|
|
||||||
|
//DRAW POINTER
|
||||||
|
set_source_rgba(pointer_color); |
||||||
|
set_line_width(pointer_width); |
||||||
|
theta = (0.75 - val * (1-dead_angle) - dead_angle * 0.5) * TWOPI; |
||||||
|
var cos_t = Math.cos(theta); |
||||||
|
var sin_t = Math.sin(theta); |
||||||
|
move_to(pointer_offset * knob_radius * cos_t, pointer_offset * knob_radius * sin_t); |
||||||
|
rel_line_to(pointer_length * knob_radius * cos_t, pointer_length * knob_radius * sin_t); |
||||||
|
stroke(); |
||||||
|
|
||||||
|
restore(); |
||||||
|
|
||||||
|
//DRAW LOCK
|
||||||
|
translate(lock_pos[0], -lock_pos[1]); |
||||||
|
set_source_rgba(lock_color); |
||||||
|
set_line_width(lock_width); |
||||||
|
rectangle(0, 0, lock_dim[0], lock_dim[1]); |
||||||
|
fill(); |
||||||
|
move_to(lock_width * 0.5, 0); |
||||||
|
if (!locked) { |
||||||
|
rel_line_to(0, lock_dim[1] * 0.5); |
||||||
|
} |
||||||
|
rel_curve_to(0, lock_dim[1], lock_dim[0] - lock_width, lock_dim[1], lock_dim[0] - lock_width, 0); |
||||||
|
stroke(); |
||||||
|
|
||||||
|
restore(); |
||||||
|
|
||||||
|
//DRAW VALUE
|
||||||
|
select_font_face(font_name); |
||||||
|
set_font_size(font_size); |
||||||
|
text = (Math.round(val_scaled * 100) / 100).toString(); |
||||||
|
t_size = text_measure(text); |
||||||
|
translate(-t_size[0]/width, 1-0.1*t_size[1]/height); |
||||||
|
move_to(0, 0); |
||||||
|
set_source_rgba(text_color);
|
||||||
|
show_text(text); |
||||||
|
} |
||||||
|
updatePattr(); |
||||||
|
} |
||||||
|
|
||||||
|
// function init(args) {
|
||||||
|
// // TOO MUCH MESS HERE, DO NOT USE
|
||||||
|
// if (arguments.length) {
|
||||||
|
// args = arrayfromargs(arguments);
|
||||||
|
// } else {
|
||||||
|
// args = init_args;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (args.length >= 5) {
|
||||||
|
// val_default = args.shift();
|
||||||
|
// }
|
||||||
|
// if (args.length == 4) {
|
||||||
|
// output_range = [args.shift(), args.shift()];
|
||||||
|
// range = [scale2norm(args.shift()), scale2norm(args)];
|
||||||
|
// } else if (args.length == 3) {
|
||||||
|
// val_default = args.shift();
|
||||||
|
// output_range = args;
|
||||||
|
// } else if (args.length == 2) {
|
||||||
|
// output_range = args;
|
||||||
|
// } else if (args.length == 1) {
|
||||||
|
// val_default = args;
|
||||||
|
// }
|
||||||
|
// set_scaled(val_default);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
function update() { |
||||||
|
outlet(0,val); |
||||||
|
outlet(1, val_scaled); |
||||||
|
mgraphics.redraw(); |
||||||
|
} |
||||||
|
|
||||||
|
function msg_float(v) { |
||||||
|
val = Math.min(Math.max(0,v),1); |
||||||
|
val_scaled = scale2outrange(val); |
||||||
|
update(); |
||||||
|
} |
||||||
|
|
||||||
|
function bang() { |
||||||
|
update(); |
||||||
|
} |
||||||
|
|
||||||
|
function randomize() { |
||||||
|
if (!locked) { |
||||||
|
val = scale(rdm.nextfloat_unipolar(), [0, 1], range); |
||||||
|
val_scaled = scale2outrange(val); |
||||||
|
update(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function set_range(args) { |
||||||
|
//dirty way to allow that function to run with either jsui args or passed variables
|
||||||
|
if (arguments.length) { |
||||||
|
args = arrayfromargs(arguments); |
||||||
|
} |
||||||
|
|
||||||
|
//dirty way to keep a minimum range > 0
|
||||||
|
range[0] = Math.min(Math.max(0, args[0]),0.99); |
||||||
|
range[1] = Math.min(Math.max(0.01, args[1]),1); |
||||||
|
|
||||||
|
if (range[0] > range[1]) { |
||||||
|
var tmp = range[0]; |
||||||
|
range[0] = range[1]; |
||||||
|
range[1] = tmp; |
||||||
|
} |
||||||
|
range_scaled = calc_range_2_outrange(range); |
||||||
|
outlet(2, "range", range); |
||||||
|
outlet(2, "range_scaled", range_scaled); |
||||||
|
mgraphics.redraw(); |
||||||
|
} |
||||||
|
|
||||||
|
function set_scaled(v) { |
||||||
|
val = scale2norm(v); |
||||||
|
val_scaled = v; |
||||||
|
mgraphics.redraw(); |
||||||
|
} |
||||||
|
|
||||||
|
function set(v) { |
||||||
|
val = v; |
||||||
|
val_scaled = scale2outrange(val); |
||||||
|
mgraphics.redraw(); |
||||||
|
} |
||||||
|
|
||||||
|
function set_lock(v) { |
||||||
|
locked = v; |
||||||
|
mgraphics.redraw(); |
||||||
|
} |
||||||
|
|
||||||
|
function lock(v) { |
||||||
|
locked = v; |
||||||
|
outlet(2, "lock", v); |
||||||
|
mgraphics.redraw(); |
||||||
|
} |
||||||
|
|
||||||
|
// UTILITIES
|
||||||
|
function scale(value, inRange, outRange) { |
||||||
|
var result = (value - inRange[0]) * (outRange[1] - outRange[0]) / (inRange[1] - inRange[0]) + outRange[0]; |
||||||
|
|
||||||
|
if (result < outRange[0]) { |
||||||
|
return outRange[0]; |
||||||
|
} else if (result > outRange[1]) { |
||||||
|
return outRange[1]; |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
scale.local = 1; |
||||||
|
|
||||||
|
function scale2norm(v) { |
||||||
|
return scale(v, output_range, [0., 1.]); |
||||||
|
} |
||||||
|
scale2norm.local = 1; |
||||||
|
|
||||||
|
function scale2outrange(v) { |
||||||
|
return scale(v, [0., 1.], output_range); |
||||||
|
} |
||||||
|
scale2outrange.local = 1; |
||||||
|
|
||||||
|
function calc_range_2_norm(r) { |
||||||
|
return [scale2norm(r[0]), scale2norm(r[1])]; |
||||||
|
} |
||||||
|
calc_range_2_norm.local = 1; |
||||||
|
|
||||||
|
function calc_range_2_outrange(r) { |
||||||
|
return [scale2outrange(r[0]), scale2outrange(r[1])]; |
||||||
|
} |
||||||
|
calc_range_2_outrange.local = 1; |
||||||
|
|
||||||
|
function calc_font_size() { |
||||||
|
font_size = width/5.8; |
||||||
|
}; |
||||||
|
calc_font_size.local = 1; |
||||||
|
|
||||||
|
// PATTR HANDLING
|
||||||
|
function updatePattr() { //SHOULD WORK ONLY WITH SCALED VALUES TO IMPROVE UX
|
||||||
|
pattrVar[0] = val_scaled; |
||||||
|
pattrVar[1] = range_scaled[0]; |
||||||
|
pattrVar[2] = range_scaled[1]; |
||||||
|
pattrVar[3] = locked; |
||||||
|
notifyclients(); |
||||||
|
} |
||||||
|
|
||||||
|
function getvalueof() { |
||||||
|
return pattrVar; |
||||||
|
} |
||||||
|
|
||||||
|
function setvalueof() { |
||||||
|
val_scaled = arguments[0]; |
||||||
|
val = scale2norm(val_scaled); |
||||||
|
range_scaled = [arguments[1], arguments[2]]; |
||||||
|
range = calc_range_2_norm(range_scaled); |
||||||
|
locked = arguments[3]; |
||||||
|
update(); |
||||||
|
} |
||||||
|
|
||||||
|
// MOUSE AND RESIZE INTERACTIONS
|
||||||
|
function onclick(x,y,but,cmd,shift,capslock,option,ctrl) { |
||||||
|
x_snorm = 2 * x / width - 1; |
||||||
|
y_snorm = 2 * y / height - 1; |
||||||
|
// post(x_snorm, y_snorm); post();
|
||||||
|
if (x_snorm > lock_pos[0] && y_snorm < - lock_pos[1] + lock_dim[1] * 2.5 ) { |
||||||
|
lock(!locked); |
||||||
|
} |
||||||
|
// cache mouse position for tracking delta movements
|
||||||
|
last_x = x; |
||||||
|
last_y = y; |
||||||
|
|
||||||
|
} |
||||||
|
onclick.local = 1; |
||||||
|
|
||||||
|
function ondrag(x,y,but,cmd,shift,capslock,option,ctrl) { |
||||||
|
var f,dy; |
||||||
|
dy = y - last_y; // calculate vertical delta movements
|
||||||
|
if (ctrl) { // ctrl + drag to offset range
|
||||||
|
var mult = shift ? 0.001 : 0.01; // fine tune if shift key is down
|
||||||
|
set_range(range[0] - dy * mult, range[1] - dy * mult); |
||||||
|
} else if (option) { // opttion/alt + drag to change range width
|
||||||
|
var mult = shift ? 0.0005 : 0.005; |
||||||
|
set_range(range[0] + dy * mult, range[1] - dy * mult); |
||||||
|
} else { //drag to change val
|
||||||
|
var mult = shift ? 0.001 : 0.01; |
||||||
|
f = val - dy * mult;
|
||||||
|
msg_float(f); //set new value with clipping + refresh
|
||||||
|
} |
||||||
|
// cache mouse position for tracking delta movements
|
||||||
|
last_x = x; |
||||||
|
last_y = y; |
||||||
|
} |
||||||
|
ondrag.local = 1; |
||||||
|
|
||||||
|
function ondblclick(x,y,but,cmd,shift,capslock,option,ctrl) { |
||||||
|
last_x = x; |
||||||
|
last_y = y; |
||||||
|
set_scaled(val_default); |
||||||
|
update(); |
||||||
|
// reset();
|
||||||
|
} |
||||||
|
ondblclick.local = 1; |
||||||
|
|
||||||
|
function forcesize(w,h) { |
||||||
|
if (w!=h) { |
||||||
|
h = w; |
||||||
|
box.size(w,h); |
||||||
|
} |
||||||
|
width = w; |
||||||
|
height = h; |
||||||
|
ratio = width/height; |
||||||
|
calc_font_size(); |
||||||
|
} |
||||||
|
forcesize.local = 1; |
||||||
|
|
||||||
|
function onresize(w,h) { |
||||||
|
forcesize(w,h); |
||||||
|
mgraphics.redraw(); |
||||||
|
} |
||||||
|
onresize.local = 1; |
Loading…
Reference in new issue