From d71de759566bb4ac1673471e8fd25880e0add7a1 Mon Sep 17 00:00:00 2001 From: tfl Date: Fri, 22 Sep 2023 11:32:32 +0200 Subject: [PATCH] added missing js --- knob-range.js | 382 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 knob-range.js diff --git a/knob-range.js b/knob-range.js new file mode 100644 index 0000000..704a88c --- /dev/null +++ b/knob-range.js @@ -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; \ No newline at end of file