Rev 2339 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* curve_editor.cc
* DIN Is Noise is copyright (c) 2006-2025 Jagannathan Sampath
* DIN Is Noise is released under GNU Public License 2.0
* For more information, please visit https://dinisnoise.org/
*/
#include "input.h"
#include "curve_editor.h"
#include "curve_library.h"
#include "console.h"
#include "font.h"
#include "utils.h"
#include "random.h"
#include "sine_mixer.h"
#include "beat2value.h"
#include "vector2d.h"
#include "fft.h"
#include "main.h"
#include "ui_list.h"
#include "chrono.h"
#include "viewwin.h"
#include "spiraler.h"
#include "rose_milker.h"
#include "instrument.h"
#include "capturer.h"
using namespace std;
extern int lmb, rmb;
extern ofstream dlog;
extern string user_data_dir;
extern int SAMPLE_RATE;
extern int FPS;
extern double TIME_PER_FRAME;
extern const float TWO_PI, PI;
extern char BUFFER [];
extern instrument* get_current_instrument ();
extern int BEATER;
extern float BPM_MULT;
curve_editor::curve_editor (const std::string& settingsf) {
todo = next_todo = NOTHING;
curcrvchgd = 1;
curves = curcrv = savedrots = \
beater = waved = \
fft_enabled = samples_enabled = \
drawcurve = draweditables = marksegments = \
lmb_move = lmb_clicked = n_pts = hlabel_only \
= 0;
gl_pts = 0;
load (settingsf);
}
curve_editor::~curve_editor () {
save ();
dlog << "-- destroyed curve editor: " << name << " ---" << endl;
}
void curve_editor::load (const std::string& fname) {
std::string feditor = user_data_dir + fname;
ifstream file (feditor.c_str (), ios::in);
if (!file) {
dlog << "!!! cannot open curve editor settings from: " << fname << " !!!" << endl;
return;
} else dlog << "<<< loading curve editor settings from: " << feditor;
settings_filename = fname;
std::string ignore;
basic_editor::load (file);
file >> ignore >> carry_tangents;
file >> ignore >> mirror_tangents;
tb.load (file);
file >> ignore >> waved;
if (waved) {
file >> ignore >> fft_enabled;
file >> ignore >> samples_enabled;
file >> ignore >> hz >> ignore >> nperiods;
cs.set (hz, nperiods);
}
file >> ignore >> drawcurve >> draweditables >> marksegments >> label_vertices;
file >> ignore >> kbkb_attack_editor;
file >> ignore >> overlay;
file >> ignore >> axis;
file >> ignore >> draw_plugin_output;
file >> ignore >> savedrots;
visibles = 0;
if (savedrots) {
curveinfo.resize (savedrots);
float deg;
for (int i = 0; i < savedrots; ++i) {
curve_info& ci = curveinfo[i];
float s, t;
file >> ci.lastrpm >> ci.angle >> ci.totang >> ci.dir >> deg >> ci.autoflip >> ci.randoflip >> s >> t >> ci.visible;
ci.rd.set (s, t);
ci.every0 = deg;
ci.every = ci.every0;
ci.startt = 0;
visibles += ci.visible;
}
}
file >> ignore >> beater;
dlog << ", done >>>" << endl;
}
void curve_editor::save () {
// save settings to file
//
ofstream file ((user_data_dir + settings_filename).c_str(), ios::out);
if (!file) {
dlog << "cannot save " << settings_filename << endl;
return;
}
basic_editor::save (file);
file << "carry-tangents " << carry_tangents << endl;
file << "mirror-tangents " << mirror_tangents << endl;
tb.clear ();
tb.save (file);
file << "waved " << waved << endl;
if (waved) {
file << "fft_enabled " << fft_enabled << endl;
file << "samples_enabled " << samples_enabled << endl;
file << "hz " << hz << endl;
file << "nperiods " << nperiods << endl;
}
file << "draw=curve+editables+marksegments+labelvertex " << drawcurve << spc << draweditables << spc << marksegments << spc << label_vertices << endl;
file << "sustain_editing " << kbkb_attack_editor << endl;
file << "overlay " << overlay << endl;
file << "mirror_axis " << axis << endl;
file << "draw_plugin_output " << draw_plugin_output << endl;
file << "curvesinfo " << curves << endl;
for (int i = 0; i < curves; ++i) {
curve_info& ci = curveinfo[i];
file << ci.lastrpm << spc << ci.angle << spc << ci.totang << spc << ci.dir \
<< spc << ci.every0.deg << spc << ci.autoflip << spc << ci.randoflip \
<< spc << ci.rd.min << spc << ci.rd.max << spc << ci.visible << spc;
}
file << endl << "beater " << beater << endl;
}
void curve_editor::prep_move () {
undos.push_back (undo_t (pik.crv_id, *pik.crv, win));
if (keydown (SDLK_SPACE)) {
cons << YELLOW << "Moving whole curve";
todo = MOVE_ALL;
} else {
todo = MOVE_PICKED;
cons << YELLOW << "Moving " << pik;
}
cons << eol;
lmb_move = PREP;
}
int curve_editor::handle_input () {
basic_editor::handle_input (); // pan, zoom, snap etc
if (lmb) {
if (lmb_clicked == 0) {
lmb_clicked = 1;
if (lmb_move == FINISH_ON_CLICK) {
cons << RED << "Finished moving " << pik << eol;
quick_nothing ();
mouse_slider0.deactivate ();
return 1;
}
if (todo == NOTHING) { // try to hit something to move
hittest ();
if (pik()) { // hit
if (pomo.hit (pik))
; // point modulator is moving hit element so ignore
else
prep_move ();
return 1;
}
} else {
switch (todo) {
case ADD_VERTEX:
add_vertex ();
return 1;
case INSERT_VERTEX:
if (!pik()) pik = pick_cur_curve ();
insert ();
return 1;
case REMOVE_VERTEX:
hittest ();
if (pik ()) remove ();
return 1;
case MODULATE_POINT:
hittest ();
modulate_point (1);
return 1;
case PINUNPIN:
hittest ();
pinunpin ();
return 1;
case FOLD_SELECTED:
hittest ();
if (pik()) fold_tangents_of_vertex (pik);
return 1;
/*case UNFOLD_SELECTED:
hittest ();
if (pik()) unfold_tangents_of_vertex (pik);
return 1;*/
case MIRROR_VERTEX:
hittest ();
if (pik()) mirror ();
return 1;
case START_CAPTURE:
abs_nothing ();
return 1;
case ASSIGN_CAPTURE:
hittest ();
assign_mouse_capture ();
return 1;
case PICK_CURVE:
case PICK_TANGENT:
hittest ();
if (pik()) curve_picked ();
return 1;
}
}
}
} else {
lmb_clicked = 0;
if (lmb_move && !MENU.show) {
lmb_move = FINISH_ON_CLICK;
if (todo == MOVE_PICKED)
move ();
else if (todo == MOVE_ALL)
move (MOVE_ALL);
if (keypressed (SDLK_f) && pik.what > hit_t::VERTEX && !mouse_slider0.active) {
calc_hit_params (pik);
start_sizing_tangent ();
}
if (keypressed (SDLK_p)) pinunpin ();
return 1;
} else if (todo == SIZE_TANGENT) start_sizing_tangent ();
}
if (library) {
if (library->num_curves () && !mouse_slider0.active) {
if (keypressedd (SDLK_9)) {
load_curve (-1);
return 1;
} else
if (keypressedd (SDLK_0)) {
load_curve (1);
return 1;
} else
if (keypressed (SDLK_MINUS)) { delete_curve (); return 1;}
}
if (keypressed (SDLK_EQUALS)) { add_curve (); return 1; }
}
if (keypressedd (SDLK_z)) { // undo & redo
if (SHIFT) // l_shift + z - redo
do_redo ();
else
do_undo ();
}
else if (keypressed (SDLK_u)) {
MENU.viewtoo.toggle ();
}
else if (keypressedd (SDLK_r)) apply_plugin (uis.plugin__browser.get_cur ()); // apply current plugin
else if (keypressed (SDLK_v)) {
if (CTRL) { // ctrl + v ie paste
paste_using_menu ();
} else { // delete vertex or tanget
todo = REMOVE_VERTEX;
cursor = " Click on vertex to delete. ESC to stop.";
hittest ();
if (pik()) {
remove ();
quick_nothing ();
}
}
}
else if (keypressed (SDLK_i)) insert_using_menu ();
else if (keypressed (SDLK_m)) { // mirror tangents ie move one tangent and the other mirrors it
mirror_tangents = !mirror_tangents;
if (mirror_tangents) cons << YELLOW << "tangents are mirrored" << eol; else cons << YELLOW<< "tangents are not mirrored" << eol;
MENU.set_mirror_tangents (mirror_tangents);
}
else if (keypressed (SDLK_SLASH)) {
if (!mouse_slider0.active) toggle_waveform_samples_display ();
}
else if (keypressedd (SDLK_COMMA)) {
if (!mouse_slider0.active) {
drawcurve = !drawcurve;
justset (MENU.cb_draw_curve, drawcurve);
}
}
else if (keypressed (SDLK_PERIOD)) {
draweditables = !draweditables;
justset (MENU.draweditables, draweditables);
}
else if (keypressed (SDLK_SEMICOLON)) {
if (!mouse_slider0.active) {
marksegments = !marksegments;
justset (MENU.cb_mark_segments, marksegments);
}
}
else if (keypressed (SDLK_c)) {
if (CTRL) { // l_ctrl + c - copy curve
if (onecurve()) {
pik = pick_cur_curve ();
copy_curve ();
} else {
copy_using_menu ();
}
}
}
else if (keypressed (SDLK_l)) {
label_vertices = !label_vertices;
justset (MENU.cb_label_vertices, label_vertices);
}
else if (keypressed (SDLK_F8)) { // apply mouse capture
if (mocap0()) {
hittest ();
todo = ASSIGN_CAPTURE;
next_todo = NOTHING;
assign_mouse_capture ();
}
} else if (keypressed (SDLK_o)) { // toggle overlay of instrument/editor
overlay = !overlay;
if (overlay)
cons << GREEN << "Overlaid the " << uis.over->name << eol;
else
cons << GREEN << "Removed the " << uis.over->name << " from overlay" << eol;
MENU.cb_overlay.set_state (overlay, 0);
}
else if (keypressed (SDLK_F3)) do_powbeat (1.0f / BPM_MULT);
else if (keypressed (SDLK_F4)) do_powbeat (BPM_MULT);
else if (keypressed (SDLK_F5)) {
if (beater) modulate_down ();
}
else if (keypressed (SDLK_F6)) {
if (beater) modulate_up ();
}
else if (keypressed (SDLK_F1)) helptext();
return 1;
}
void curve_editor::pinunpin () {
static const char* pup [] = {"Unpinned ", "Pinned "};
if (pik (1)) {
pointinfo<float>& p = pik.get ();
p.pin = !p.pin;
cons << pup[p.pin] << pik << eol;
} else {
int n = hitlist.size ();
if (n && curve_picker.visible == 0) {
for (int m = 0; m < n; ++m) {
hit_t& h = hitlist[m];
pointinfo<float>& p = h.get ();
p.pin = !p.pin;
cons << pup[p.pin] << h << eol;
}
}
}
}
void curve_editor::apply_mocap () {
float x, y;
int vx, vy;
for (int i = 0, j = macros.size(); i < j; ++i) {
mouse_macro& m = macros[i];
if (m.paused == 0) {
hit_t& h = m.hit;
if (h(1)) {
m.mo.get (x, y);
move (h, x, y);
obj2view (x, y, vx, vy);
capturer.set (m, vx, vy);
}
}
}
}
void curve_editor::calc_hit_params (hit_t& hit) {
if (!hit()) return;
multi_curve* crv = get_curve (hit.crv_id);
float vx0, vy0, vx, vy, lx, ly, rx, ry;
int id = hit.id, lv = crv->last_vertex;
crv->get_vertex (id, vx0, vy0);
if (id == 0) {
crv->get_vertex (lv, vx, vy);
crv->get_left_tangent (lv, lx, ly);
}
else {
vx = vx0; vy = vy0;
crv->get_left_tangent (id, lx, ly);
}
hit.left_tangent_magnitude = unit_vector (hit.left_tangent.x, hit.left_tangent.y, vx, vy, lx, ly);
if (id == lv) {
crv->get_vertex (0, vx, vy);
crv->get_right_tangent (0, rx, ry);
}
else {
vx = vx0; vy = vy0;
crv->get_right_tangent (id, rx, ry);
}
hit.right_tangent_magnitude = unit_vector (hit.right_tangent.x, hit.right_tangent.y, vx, vy, rx, ry);
const point<float>& hp = hit.get ();
obj2mouse (hp.x, hp.y);
curcrv = hit.crv_id;
curcrvchgd = 1;
}
void curve_editor::hittest () {
hitlist.clear ();
for (int i = 0, j = curves; i < j; ++i) {
curve_info& ci = curveinfo[i];
multi_curve* crv = ci.curve;
if (ci.visible) {
hittest (crv, i, crv->vertices, hit_t::VERTEX);
hittest (crv, i, crv->left_tangents, hit_t::LEFT_TANGENT);
hittest (crv, i, crv->right_tangents, hit_t::RIGHT_TANGENT);
}
}
int n = hitlist.size();
if (n)
n = filter_hitlist ();
else {
pik = hit_t ();
return;
}
if (n == 1) {
pik = hitlist [0];
calc_hit_params (pik);
} else {
pik = hit_t ();
if (n > 1 && SHIFT == 0) curve_picker.show ();
}
}
void curve_editor::hittest (multi_curve* crv, int crv_id, const points_array& points, unsigned int what) {
for (int i = 0, j = points.size (); i < j; ++i) {
const point<float>& v = points[i];
float vx, vy; obj2win (v, vx, vy);
float d2 = magnitude2<float> (vx, vy, win.mousex, win.mousey);
if (d2 > win.handle_radius2) ; else hitlist.push_back (hit_t (crv, crv_id, what, i));
}
}
int curve_editor::filter_hitlist () {
for (int i = 0, j = curves; i < j; ++i) curveinfo[i].picked = 0;
for (vector<hit_t>::iterator i = hitlist.begin (), j = hitlist.end (); i != j;) {
hit_t& hit = *i;
if (todo == PICK_CURVE) {
int& picked = curveinfo[hit.crv_id].picked;
if (picked) { // picked already, so remove
hitlist.erase (i);
j = hitlist.end ();
} else { // pick one item per curve
picked = 1;
++i;
}
} else
if (todo == REMOVE_VERTEX && hit.what != hit_t::VERTEX) { // have to select vertex to delete
i = hitlist.erase (i);
j = hitlist.end ();
} else
if ( (hit.what == hit_t::LEFT_TANGENT && hit.id == 0) || (hit.what == hit_t::RIGHT_TANGENT && hit.id == (hit.crv->last_vertex)) ) {
// remove 1st vertex's left tangent and last vertex's right tangent
i = hitlist.erase (i);
j = hitlist.end ();
} else if (todo == PICK_TANGENT && hit.what == hit_t::VERTEX) {
i = hitlist.erase (i);
j = hitlist.end ();
}
else {
++i;
}
}
return hitlist.size ();
}
void curve_editor::set_pick_from_hitlist (int i) {
pik = hitlist [i];
calc_hit_params (pik);
cons << GREEN << "Picked " << pik << eol;
}
int curve_editor::move (hit_t& hit, float x, float y, int eval_now) {
// move hit element to x,y
curve_info& ci = curveinfo [hit.crv_id];
multi_curve* crv = ci.curve;
int need_eval = 0;
switch (hit.what) {
case hit_t::VERTEX:
need_eval = crv->set_vertex (hit.id, x, y);
break;
case hit_t::RIGHT_TANGENT:
need_eval = crv->set_right_tangent (hit.id, x, y);
if (mirror_tangents && need_eval) {
float vx, vy, rux, ruy;
crv->get_vertex (hit.id, vx, vy);
unit_vector (rux, ruy, vx, vy, x, y);
if (hit.id == 0) { // first right tangent of curve, mirror with left tangent of last vertex
int lv = crv->last_vertex;
crv->get_vertex (lv, vx, vy);
float m = crv->get_left_tangent_mag (lv);
crv->set_left_tangent (lv, vx - m * rux, vy - m * ruy);
} else
crv->set_left_tangent (hit.id, vx - hit.left_tangent_magnitude * rux, vy - hit.left_tangent_magnitude * ruy);
}
break;
case hit_t::LEFT_TANGENT:
need_eval = crv->set_left_tangent (hit.id, x, y);
if (mirror_tangents && need_eval) {
float vx, vy, lux, luy;
crv->get_vertex (hit.id, vx, vy);
unit_vector (lux, luy, vx, vy, x, y);
int lv = crv->last_vertex;
if (hit.id == lv) {
crv->get_vertex (0, vx, vy);
float m = crv->get_right_tangent_mag (0);
crv->set_right_tangent (0, vx - m * lux, vy - m * luy);
} else
crv->set_right_tangent (hit.id, vx - hit.right_tangent_magnitude * lux, vy - hit.right_tangent_magnitude * luy);
}
break;
}
if (need_eval && eval_now) {
crv->evaluate();
ci.lisner->edited (this, hit.crv_id);
}
return need_eval;
}
int curve_editor::move () {
float sx, sy; snap (sx, sy);
float cx, cy; win2obj (sx, sy, cx, cy);
return move (pik, cx, cy);
}
int curve_editor::move (int) {
float sx, sy;
snap (sx, sy);
float cx, cy;
win2obj (sx, sy, cx, cy);
curve_info& ci = curveinfo [pik.crv_id];
multi_curve& crv = *ci.curve;
float dx, dy;
pointinfo<float>& p = pik.get ();
dx = cx - p.x;
dy = cy - p.y;
crv.move (dx, dy);
ci.lisner->edited (this, pik.crv_id);
return 1;
}
void curve_editor::remove () {
curve_info& ci = curveinfo [pik.crv_id];
multi_curve* crv = ci.curve;
mix = *crv;
undos.push_back (undo_t (pik.crv_id, *crv, win));
if (crv->remove (pik.id)) {
pomo.remove (pik);
crv->evaluate();
ci.lisner->edited (this, pik.crv_id);
} else {
cons << RED << "Cannot delete curve: " << crv->name << eol;
undos.pop_back ();
}
clear_hit (pik);
}
void curve_editor::swap () {
if (curves == 2) {
multi_curve& crv0 = *get_curve (0);
multi_curve& crv1 = *get_curve (1);
multi_curve swp (crv0);
undos.push_back (undo_t (0, crv0, win));
string name0 (crv0.name);
float r0 = crv0.r, g0 = crv0.g, b0 = crv0.b;
crv0 = crv1;
crv0.name = name0;
crv0.set_color (r0, g0, b0);
undos.push_back (undo_t (1, crv1, win));
string name1 (crv1.name);
float r1 = crv1.r, g1 = crv1.g, b1 = crv1.b;
crv1 = swp;
crv1.name = name1;
crv1.set_color (r1, g1, b1);
cons << "Swapped curve " << crv0.name << " with " << crv1.name << eol;
curve_info &ci0 = curveinfo[0], &ci1 = curveinfo[1];
pomo.validate ();
ci0.lisner->edited (this, 0);
ci1.lisner->edited (this, 1);
}
}
void curve_editor::insert () {
curve_info& ci = curveinfo [pik.crv_id];
multi_curve* crv = ci.curve;
float sx, sy; snap (sx, sy);
float cx, cy; win2obj (sx, sy, cx, cy);
mix = *crv;
undos.push_back (undo_t (pik.crv_id, *crv, win));
const point<float>& obj = get_obj_chunk ();
if (crv->insert (cx, cy, obj.x, obj.y)) {
crv->evaluate();
ci.lisner->edited (this, pik.crv_id);
} else undos.pop_back ();
}
void curve_editor::replace () {
if (curv_scratch_points.size () > 1) {
curve_info& ci = curveinfo [pik.crv_id];
multi_curve& crv = *ci.curve;
int shapeform = crv.shapeform;
mix = crv;
undos.push_back (undo_t (pik.crv_id, crv, win));
create_polyline (crv, curv_scratch_points);
if (shapeform) crv.set_shapeform (1);
pomo.validate ();
ci.lisner->edited (this, pik.crv_id);
cons << GREEN << "Replaced curve." << eol;
} else cons << RED << "Aborted drawn curve!" << eol;
clear_scratch_curve ();
}
void curve_editor::mirror (int whole_curve) {
// mirrors vertex (or curve) about global x-axis, or local axes of bounding box
curve_info& ci = curveinfo [pik.crv_id];
multi_curve* crv = ci.curve;
undos.push_back (undo_t (pik.crv_id, *crv, win));
box<float> bbox;
if (axis > MIRROR_Y) crv->calc_bbox (bbox);
float xx, yy, dx, dy, _2mx = 2 * bbox.midx, _2my = 2 * bbox.midy;
if (whole_curve) {
if ((axis == MIRROR_Y) || (axis == MIRROR_BBY)) {
for (int i = 0; i < crv->num_vertices; ++i) {
crv->get_vertex (i, xx, yy); dx = _2mx - xx; crv->set_vertex (i, dx, yy);
}
} else {
for (int i = 0; i < crv->num_vertices; ++i) {
crv->get_vertex (i, xx, yy); dy = _2my - yy; crv->set_vertex (i, xx, dy);
}
}
mix = *crv;
crv->evaluate();
ci.lisner->edited (this, pik.crv_id);
} else {
switch (pik.what) {
case hit_t::VERTEX:
crv->get_vertex (pik.id, xx, yy);
break;
case hit_t::LEFT_TANGENT:
crv->get_left_tangent (pik.id, xx, yy);
break;
case hit_t::RIGHT_TANGENT:
crv->get_right_tangent (pik.id, xx, yy);
break;
}
mix = *crv;
if ((axis == MIRROR_Y) || (axis == MIRROR_BBY)) {
dx = _2mx - xx;
move (pik, dx, yy);
} else {
dy = _2my - yy;
move (pik, xx, dy);
}
}
}
void curve_editor::draw () {
project ();
basic_editor::draw ();
if (draw_plugin_output) uis.plugin__browser.draw (this);
draw_all ();
if (show_scratch_curve) draw_scratch_curve ();
if (curve_picker.visible) hilite_item (curve_picker.id);
unproject ();
if (overlay) ui::over->drawerlay ();
}
void curve_editor::draw_scratch_curve () {
int spn = win_scratch_points.size ();
if (spn == 0) return;
int n = spn + 1;
alloc_gl_pts (n);
glColor3f (1, 1, 0.7f);
int k = 0;
for (int i = 0; i < spn; ++i) {
const point<float>& p = win_scratch_points[i];
gl_pts[k++]=p.x;
gl_pts[k++]=p.y;
}
float sx, sy;
snap (sx, sy);
gl_pts[k++]=sx;
gl_pts[k++]=sy;
glVertexPointer (2, GL_FLOAT, 0, gl_pts);
glDrawArrays (GL_LINE_STRIP, 0, n);
}
void curve_editor::draw_all () {
// draw labels
tb.draw (hlabel_only);
tb.clear ();
if (marksegments) mark_segments ();
// draw curve profile, vertices, tangents and handles
int j = curves;
for (int i = 0; i < j; ++i) {
curve_info& ci = curveinfo [i];
if (ci.visible) {
multi_curve* crv = ci.curve;
if (draweditables) {
glColor3f (crv->rt, crv->gt, crv->bt);
draw_tangents (crv);
glColor3f (crv->r, crv->g, crv->b);
draw_vertices (crv);
}
if (drawcurve) {
glColor3f (crv->r, crv->g, crv->b);
draw_curve (crv);
}
}
}
// mark beat/value
//
int n = bv.size ();
if (n) {
float nowx, nowy;
float x, y;
static const int marker_size = 250;
for (int i = 0; i < n; ++i) {
curve_info& ci = curveinfo[i];
if (ci.visible) {
beat2value* bvi = bv[i];
multi_curve& crv = *bvi->crv;
if (crv.shapeform) crv.get_xy (bvi->now, nowx, nowy);
else {
/*if (bvi->swing) {
nowx = bvi->swingsol (bvi->now);
} else */
nowx = bvi->now;
nowy = bvi->sol (nowx);
}
obj2win (nowx, nowy, x, y);
static const float cmul = 2;
glColor3f (cmul * crv.r, cmul * crv.g, cmul * crv.b);
glLineWidth (2);
mkr[0]=x-marker_size;mkr[1]=y;
mkr[2]=x;mkr[3]=y-marker_size;
mkr[4]=x+marker_size;mkr[5]=y;
mkr[6]=x;mkr[7]=y+marker_size;
glVertexPointer (2, GL_INT, 0, mkr);
glDrawArrays (GL_LINE_LOOP, 0, 4);
/*glBegin (GL_LINES);
glVertex2f (x, mkr[3]);
glVertex2f (x, mkr[7]);
glVertex2f (mkr[0], y);
glVertex2f (mkr[4], y);
glEnd ();*/
glLineWidth (1);
}
}
}
// label vertices
if (label_vertices) {
for (int i = 0, j = curves; i < j; ++i) {
curve_info& ci = curveinfo[i];
multi_curve* crv = ci.curve;
for (int m = 0, n = crv->num_vertices; m < n; ++m) {
point<float>& vm = crv->vertices[m];
sprintf (BUFFER, "%d", m);
tb.add (text (BUFFER, vm.x, vm.y, vtxlbl.r, vtxlbl.g, vtxlbl.b));
}
tb.refresh (this);
}
}
if (samples_enabled && curves) cs.draw (this, curveinfo[0].curve);
if (kbkb_attack_editor) {
mkr[0]=susx;mkr[1]=susbox.bottom;
mkr[2]=susx;mkr[3]=win.bottom;
glEnable (GL_LINE_STIPPLE);
glLineStipple (1, 0x00ff);
glColor3f (0, 0.5, 1);
glBegin (GL_LINES);
glVertex2i (susx, susbox.bottom);
glVertex2i (susx, win.bottom);
glEnd ();
/*glVertexPointer (2, GL_INT, 0, mkr);
glDrawArrays (GL_LINES, 0, 2);*/
glDisable (GL_LINE_STIPPLE);
mkr[0]=susbox.left;mkr[1]=susbox.top;
mkr[2]=susbox.midx;mkr[3]=susbox.bottom;
mkr[4]=susbox.right;mkr[5]=susbox.top;
glVertexPointer (2, GL_INT, 0, mkr);
glDrawArrays (GL_LINE_LOOP, 0, 3);
}
draw_cursor ();
}
void curve_editor::mark_segments () {
// drop vertical lines from current curve's profile points
curve_info& ci = curveinfo [curcrv];
if (ci.visible) {
multi_curve* crv = ci.curve;
if (!crv->shapeform) {
const float cc = 0.5f;
float cr = cc, cg = cr, cb = cr;
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
vector<curve>& curv = crv->curv;
int m = 0;
for (int i = 0, j = curv.size(); i < j; ++i) m += curv[i].vpts.size ();
int n = 2 * m;
alloc_gl_pts (n);
for (int i = 0, j = curv.size(), k = 0, l = 0; i < j; ++i) {
vector<crvpt>& vpts = curv[i].vpts;
float pox = -MILLION;
for (int p = 0, q = vpts.size (); p < q; ++p) {
float ox = vpts[p].x, oy = vpts[p].y;
cr = cg = cb = cc;
if (ox < pox) {cr = 1.0f; cg = cb = 0.0f;}
pox = ox;
if (waved && ( oy < -1 || oy > 1 )) {cr = 1.0f; cg = cb = 0.0f;}
float x, y; obj2win (ox, oy, x, y);
gl_pts [k++]=x;
gl_pts [k++]=y;
gl_pts [k++]=x;
gl_pts [k++]=0;
gl_clr [l++]=cr;
gl_clr [l++]=cg;
gl_clr [l++]=cb;
gl_clr [l++]=0.5f;
gl_clr [l++]=cr;
gl_clr [l++]=cg;
gl_clr [l++]=cb;
gl_clr [l++]=0.5f;
}
}
glEnableClientState (GL_COLOR_ARRAY);
glColorPointer (4, GL_FLOAT, 0, gl_clr);
glVertexPointer (2, GL_FLOAT, 0, gl_pts);
glDrawArrays (GL_LINES, 0, n);
glDisable (GL_BLEND);
glDisableClientState (GL_COLOR_ARRAY);
}
}
}
void curve_editor::draw_handle (const point<float>& p) {
float x, y; obj2win (p, x, y);
float handle [] = {x - win.handle_radius, y, x, y + win.handle_radius, x + win.handle_radius, y, x, y - win.handle_radius};
glVertexPointer (2, GL_FLOAT, 0, handle);
glDrawArrays (GL_LINE_LOOP, 0, 4);
}
void curve_editor::draw_tangent (const point<float>& p, const point<float>& t) {
float x, y; obj2win (p, x, y);
float tx, ty; obj2win (t, tx, ty);
/*float pts [4] = {x, y, tx, ty};
glVertexPointer (2, GL_FLOAT, 0, pts);
glDrawArrays (GL_LINES, 0, 2);*/
glBegin (GL_LINES);
glVertex2f (x, y);
glVertex2f (tx, ty);
glEnd ();
}
void curve_editor::draw_curve (multi_curve* crv) {
int r = 0, n = crv->get_total_points ();
alloc_gl_pts (n);
vector<curve>& curv = crv->curv;
int j = curv.size (), k = j - 1;
float dx, dy;
for (int i = 0; i < j; ++i) {
vector<crvpt>& vpts = curv[i].vpts;
for (int p = 0, q = vpts.size () - 1; p < q; ++p) {
obj2win (vpts[p].x, vpts[p].y, dx, dy);
gl_pts[r++] = dx;
gl_pts[r++] = dy;
}
}
// last point
int l = curv[k].vpts.size () - 1;
crvpt& pl = curv[k].vpts[l];
obj2win (pl.x, pl.y, dx, dy);
gl_pts[r++]=dx;
gl_pts[r++]=dy;
glVertexPointer (2, GL_FLOAT, 0, gl_pts);
glDrawArrays (GL_LINE_STRIP, 0, n);
}
void curve_editor::draw_vertices (multi_curve* crv) {
const points_array& vertices = crv->vertices;
for (int p = 0, q = vertices.size(); p < q; ++p) {
const point<float>& v = vertices[p];
draw_handle (v);
}
}
void curve_editor::draw_tangents (multi_curve* crv) {
// draw tangents of ith curve
const points_array& vertices = crv->vertices;
const points_array& left_tangents = crv->left_tangents;
const points_array& right_tangents = crv->right_tangents;
for (int p = 0, q = vertices.size(); p < q; ++p) {
const point<float>& v = vertices[p];
const point<float>& lt = left_tangents[p];
const point<float>& rt = right_tangents[p];
draw_tangent (v, lt);
draw_handle (lt);
draw_tangent (v, rt);
draw_handle (rt);
}
}
void curve_editor::dodo (list<undo_t>& do1, list<undo_t>& do2, std::string mesg) {
if (do1.size() > 0) {
undo_t& last = do1.back ();
if (last.i < curves) {
curve_info& ci = curveinfo[last.i];
multi_curve* crv = ci.curve;
mix = *crv;
do2.push_back (undo_t (last.i, *crv, win));
float r, g, b;
crv->get_color (r, g, b);
*crv = last.curve;
crv->set_color (r, g, b);
if (MENU.show) setup_tools_menu (); else curcrvchgd = 1;
pomo.validate ();
ci.lisner->edited (this, last.i);
if (undo_redo_win) win = last.win;
do1.pop_back ();
}
hitlist.clear ();
calc_visual_params ();
} else cons << RED << mesg << eol;
}
void curve_editor::add (multi_curve* crv, curve_listener* lsnr) {
if (curves < savedrots) {
curve_info& cc = curveinfo [curves];
cc.curve = crv;
cc.lisner = lsnr;
} else {
curveinfo.push_back (curve_info (crv, lsnr));
}
if (++curves == 1) {
render_curve_samples ();
hlabel_only = crv->shapeform;
}
}
void curve_editor::clear () {
curveinfo.clear ();
hitlist.clear ();
clear_hit (pik);
curves = 0;
}
multi_curve* curve_editor::get_curve (int i) {
curve_info& ci = curveinfo [i];
return ci.curve;
}
void curve_editor::calc_visual_params () {
float var = view.height * 1.0 / view.width;
float wh = win.width * var, wh2 = wh / 2.0f;
win.set (win.left, win.midy - wh2, win.right, win.midy + wh2);
win.calc ();
basic_editor::calc_visual_params ();
}
void curve_editor::enter () {
calc_visual_params ();
MENU.populatecurvestab (this);
MENU.set_mirror_tangents (mirror_tangents);
MENU.crvwav.hz.set_value (hz);
MENU.crvwav.periods.set_value (nperiods);
MENU.crvwav.time.set_value (curve_samples::nsec);
curcrvchgd = 1;
justset (MENU.cb_show_waveform_samples, samples_enabled);
justset (MENU.cb_label_vertices, label_vertices);
justset (MENU.cb_draw_curve, drawcurve);
justset (MENU.draweditables, draweditables);
justset (MENU.cb_mark_segments, marksegments);
justset (MENU.cb_label_vertices, label_vertices);
justset (MENU.cb_overlay, overlay);
justset (MENU.drawsnaps, draww.snaps);
justset (MENU.drawcursor, draww.guide);
justset (MENU.viewtoo, undo_redo_win);
if (uis.is_widget_on_screen (&uis.plugin__browser, this)) {
uis.plugin__browser.set_ed (this);
uis.plugin__browser.set_fold (!draw_plugin_output);
}
ui::enter ();
}
void curve_editor::clear_scratch_curve () {
win_scratch_points.clear ();
curv_scratch_points.clear ();
scratch_curve.clear ();
show_scratch_curve = 0;
}
void curve_editor::attach_library (curve_library* lib) {
library = lib;
}
void curve_editor::bg () {
apply_mocap ();
rotate ();
}
void curve_editor::clear_hit (hit_t& h) {
h.clear ();
}
void curve_editor::load_curve (int dir) {
if (!pik()) pik = pick_cur_curve ();
multi_curve& crv = *pik.crv;
mix = crv;
undos.push_back (undo_t (pik.crv_id, crv, win));
if (dir == 1) crv = library->next (); else crv = library->prev();
crv.set_color (mix.r, mix.g, mix.b);
//crv.name = mix.name;
if (MENU.show) setup_tools_menu (); else curcrvchgd = 1;
pomo.validate ();
curveinfo[pik.crv_id].lisner->edited (this, pik.crv_id);
cons.rollup (1);
}
void curve_editor::add_curve () {
if (!library) return;
if (!pik()) pik = pick_cur_curve ();
curve_info& ci = curveinfo [pik.crv_id];
library->add (*ci.curve);
}
void curve_editor::replace_curve () {
if (!library) return;
if (!pik()) pik = pick_cur_curve ();
library->replace (*get_curve (pik.crv_id));
}
void curve_editor::insert_curve () {
if (!library) return;
if (!pik()) pik = pick_cur_curve ();
library->insert (*get_curve (pik.crv_id));
}
void curve_editor::paste (hit_t& hit) {
if (hit()) {
curve_info& ci = curveinfo [hit.crv_id];
multi_curve* crv = ci.curve;
if (copy.num_vertices) {
mix = *crv;
undos.push_back (undo_t (hit.crv_id, *crv, win));
std::string crv_name (crv->name);
float cr, cg, cb;
crv->get_color (cr, cg, cb);
if (SHIFT || MENU.ol_paste.id) paste_append (*crv, copy); else *crv = copy;
crv->set_color (cr, cg, cb);
crv->name = crv_name;
pomo.validate ();
ci.lisner->edited (this, hit.crv_id);
cons << GREEN << "Pasted into " << crv->name << eol;
} else
cons << RED << "Nothing to paste!" << eol;
quick_nothing ();
} else cons << RED << "Pick a curve to paste." << eol;
}
std::string curve_editor::selection () {
std::stringstream ss;
const char* what [] = {"invalid", "v", "lt", "rt"};
for (int i = 0, j = hitlist.size (); i < j; ++i) {
hit_t& h = hitlist[i];
multi_curve* c = h.crv;
float x, y;
switch (h.what) {
case hit_t::VERTEX:
c->get_vertex (h.id, x, y);
break;
case hit_t::LEFT_TANGENT:
c->get_left_tangent (h.id, x, y);
break;
case hit_t::RIGHT_TANGENT:
c->get_right_tangent (h.id, x, y);
break;
}
ss << '{' << c->name << ' ' << what[h.what] << ' ' << h.id << ' ' << x << ' ' << y << "} ";
}
return ss.str ();
}
void curve_editor::picked_using_picker (int id) {
set_pick_from_hitlist (id);
switch (todo) {
case NOTHING:
prep_move ();
break;
case PICK_CURVE:
curve_picked ();
break;
case PICK_TANGENT:
curve_picked ();
break;
case REMOVE_VERTEX:
remove ();
break;
case FOLD_SELECTED:
fold_tangents_of_vertex (pik);
break;
/*case UNFOLD_SELECTED:
unfold_tangents_of_vertex (pik);
break;*/
case PINUNPIN:
pinunpin ();
break;
case MIRROR_VERTEX:
mirror ();
break;
case ASSIGN_CAPTURE:
assign_mouse_capture ();
break;
case MODULATE_POINT:
modulate_point (1);
break;
}
}
void curve_editor::remove_using_menu () {
todo = REMOVE_VERTEX;
cursor = " Click on vertex to delete. ESC to stop.";
}
void curve_editor::modulate_point () {
todo = MODULATE_POINT;
cursor = " Click on vertex or tangent to modulate. ESC to stop.";
}
void curve_editor::modulate_point (int) {
if (pik()) {
undos.push_back (undo_t (pik.crv_id, *pik.crv, win));
pomo.add (pik);
}
}
void curve_editor::insert_using_menu () {
if (onecurve()) {
todo = INSERT_VERTEX;
cursor = " Click to insert vertex. ESC to stop.";
} else {
todo = PICK_CURVE;
cursor = " Pick curve to insert vertex. ESC to abort!";
next_todo = INSERT_VERTEX;
cursor += " Click to insert vertex. ESC to stop.";
}
}
void curve_editor::fold_tangents_using_menu () {
int fold_vertex = MENU.cb_selection_only.state;
if (fold_vertex) {
todo = FOLD_SELECTED;
cursor = " Click on a tangent or vertex to fold its tangents. ESC to stop.";
} else {
if (onecurve()) {
pik = pick_cur_curve ();
fold_all_tangents (pik);
quick_nothing ();
} else {
next_todo = FOLD_ALL;
cursor += "";
todo = PICK_CURVE;
cursor = " Pick curve to fold tangents. ESC to stop.";
}
}
}
void curve_editor::pinunpin_using_menu () {
todo = PINUNPIN;
cursor = " Click on vertex or tangent to Pin / Unpin. ESC to stop.";
}
/*void curve_editor::unfold_tangents_using_menu () {
int unfold_vertex = MENU.cb_selection_only.state;
if (unfold_vertex) {
todo = UNFOLD_SELECTED;
cursor = " Click on vertex to unfold tangents. ESC to stop.";
} else {
if (onecurve()) {
pik = pick_cur_curve ();
unfold_all_tangents (pik);
quick_nothing ();
} else {
todo = PICK_CURVE;
cursor = " Pick curve to unfold tangents. ESC to stop.";
next_todo = UNFOLD_ALL;
cursor += "";
}
}
}*/
void curve_editor::mirror_using_menu () {
int mirror_vertex = MENU.cb_selection_only.state;
if (mirror_vertex) {
todo = MIRROR_VERTEX;
cursor = " Click on vertex or tangent to flip. ESC to stop.";
} else {
if (onecurve()) {
pik = pick_cur_curve ();
int whole_curve = 1;
mirror (whole_curve);
quick_nothing ();
} else {
todo = PICK_CURVE;
cursor = " Pick the curve to flip. ESC to stop.";
next_todo = MIRROR_ALL;
cursor += "";
}
}
}
void curve_editor::copy_using_menu () {
if (onecurve()) {
copy = *curveinfo[curcrv].curve;
cons << GREEN << "Copied " << copy.name << eol;
} else {
todo = PICK_CURVE;
cursor = " Pick the curve to copy. ESC to stop.";
next_todo = COPY;
cursor += "";
}
}
void curve_editor::paste_using_menu () {
if (onecurve()) {
pik = pick_cur_curve ();
paste (pik);
} else {
todo = PICK_CURVE;
cursor = " Pick the curve to paste. ESC to stop.";
next_todo = PASTE;
cursor += "";
}
}
multi_curve& curve_editor::set_mix_undo (hit_t& hit) {
curve_info& ci = curveinfo [hit.crv_id];
multi_curve& crv = *ci.curve;
mix = crv;
undos.push_back (undo_t (hit.crv_id, crv, win));
return crv;
}
void curve_editor::fold_all_tangents (hit_t& hit) {
multi_curve& crv = set_mix_undo (hit);
convert2_polyline (crv);
curveinfo[hit.crv_id].lisner->edited (this, hit.crv_id);
}
/*void curve_editor::unfold_all_tangents (hit_t& hit) {
multi_curve& crv = set_mix_undo (hit);
const point<float>& obj = get_obj_chunk ();
convert2_catmull_rom (crv, max (obj.x, obj.y)); // turn bezier curve into 'catmull-rom' spline; still a bezier curve
curveinfo[hit.crv_id].lisner->edited (this, hit.crv_id);
}*/
void curve_editor::fold_tangents_of_vertex (hit_t& hit) {
multi_curve& crv = set_mix_undo (hit);
float vx, vy;
crv.get_vertex (hit.id, vx, vy);
if (hit.what == hit_t::VERTEX) {
crv.set_left_tangent (hit.id, vx, vy);
crv.set_right_tangent (hit.id, vx, vy);
} else if (hit.what == hit_t::LEFT_TANGENT) {
crv.set_left_tangent (hit.id, vx, vy);
} else {
crv.set_right_tangent (hit.id, vx, vy);
}
crv.evaluate ();
curveinfo[hit.crv_id].lisner->edited (this, hit.crv_id);
}
/*void curve_editor::unfold_tangents_of_vertex (hit_t& hit) {
multi_curve& crv = set_mix_undo (hit);
float vx, vy; crv.get_vertex (hit.id, vx, vy);
const point<float>& obj = get_obj_chunk ();
if (hit.what == hit_t::VERTEX) {
crv.set_left_tangent (hit.id, vx - obj.x, vy);
crv.set_right_tangent (hit.id, vx + obj.x, vy);
} else if (hit.what == hit_t::LEFT_TANGENT) {
crv.set_left_tangent (hit.id, vx - obj.x, vy);
} else {
crv.set_right_tangent (hit.id, vx + obj.x, vy);
}
crv.evaluate ();
curveinfo[hit.crv_id].lisner->edited (this, hit.crv_id);
}*/
void curve_editor::render_curve_samples () {
if (samples_enabled) {
if (!pik()) pik = pick_cur_curve ();
cs.render (pik.crv, this);
}
}
void curve_editor::toggle_waveform_samples_display () {
if (waved) {
samples_enabled = !samples_enabled;
render_curve_samples ();
justset (MENU.cb_show_waveform_samples, samples_enabled);
}
}
void curve_editor::set_hz (float zh) {
hz = zh;
if (hz < 0) hz = SAMPLE_RATE / 100;
cs.set (hz, cs.nperiods);
render_curve_samples ();
}
void curve_editor::set_periods (int p) {
cs.set (cs.hz, p);
render_curve_samples ();
}
void curve_editor::abs_nothing () {
stop_mouse_capture ();
if (todo == ADD_VERTEX) {
if (rmb)
replace ();
else
clear_scratch_curve ();
}
quick_nothing ();
}
void curve_editor::quick_nothing (int lm) {
todo = next_todo = NOTHING;
cursor = "";
cursor += "";
lmb_move = lm;
}
void curve_editor::copy_curve () {
if (pik()) {
copy = *get_curve(pik.crv_id);
cons << GREEN << "Copied "<< copy.name << "." << eol;
quick_nothing ();
}
}
void curve_editor::do_load_curve (int dir) {
if (library && library->num_curves()) load_curve (dir);
}
void curve_editor::do_redo () {
dodo (redos, undos, "no more redos.");
}
void curve_editor::do_undo () {
dodo (undos, redos, "no more undos.");
}
void curve_editor::do_pick_curve () {
clear_hit (pik);
if (onecurve()) {
pik = pick_cur_curve ();
cons << "Picked " << pik.crv->name << eol;
quick_nothing ();
return;
} else {
todo = PICK_CURVE;
cursor = " Click to pick curve, ESC to abort!";
next_todo = NOTHING;
cursor += "";
}
}
hit_t curve_editor::pick_cur_curve () {
multi_curve* crv = curveinfo[curcrv].curve;
curcrvchgd = 1;
return hit_t (crv, curcrv);
}
multi_curve* curve_editor::get_picked_curve () {
if (!pik()) {
pik = pick_cur_curve ();
return pik.crv;
} else
return pik.crv;
}
void curve_editor::delete_curve () {
if (library) library->del ();
}
void curve_editor::draw_replacement_curve_using_menu () {
if (onecurve()) {
pik = pick_cur_curve ();
show_scratch_curve = 1;
todo = ADD_VERTEX;
cursor = " Click to add vertex. ESC to abort!";
} else {
todo = PICK_CURVE;
cursor = " Pick curve to replace. ESC to abort!";
next_todo = ADD_VERTEX;
cursor += " Click to add vertex. ESC to abort!";
}
}
void curve_editor::add_vertex () {
show_scratch_curve = 1;
float sx, sy; snap (sx, sy);
win_scratch_points.push_back (point<float> (sx, sy));
float curvx, curvy; win2obj (sx, sy, curvx, curvy);
curv_scratch_points.push_back (point<float>(curvx, curvy));
if (curv_scratch_points.size () > 1) cursor = "Click to add vertex. Right click to complete.";
}
void curve_editor::assign_mouse_capture () {
if (pik()) {
undos.push_back (undo_t (pik.crv_id, *get_curve(pik.crv_id), win));
macros.push_back (mouse_macro (mocap0, pik, capturer.add ()));
cons << GREEN << "Assigned mouse capture to " << pik << eol;
quick_nothing ();
}
}
void curve_editor::assign_mouse_capture_from_menu () {
if (mocap0()) {
todo = ASSIGN_CAPTURE;
next_todo = NOTHING;
cursor = " Pick vertex or tangent to assign mouse capture. ESC to abort!";
} else cons << RED << "No mouse capture found!" << eol;
}
void curve_editor::remove_mouse_capture (state_button* sb) {
for (vector<mouse_macro>::iterator i = macros.begin(), j = macros.end(); i != j;) {
mouse_macro& m = *i;
if (m.sb == sb) {
cons << GREEN << "Removed mouse capture from " << m.hit << eol;
i = macros.erase (i);
j = macros.end ();
if (macros.empty()) {
quick_nothing ();
return;
}
} else ++i;
}
}
void curve_editor::toggle_mouse_capture (vector<state_button*>& caps) {
int p = 0, q = 0;
for (vector<mouse_macro>::iterator i = macros.begin(), j = macros.end(); i != j; ++i) {
mouse_macro& mm = *i;
for (int i = 0, ncaps = caps.size (); i < ncaps; ++i) {
state_button* sb = caps[i];
if (sb->state && mm.sb == sb) {
mm.paused = !mm.paused;
if (mm.paused) ++p; else ++q;
}
}
}
cons << GREEN << "Played " << q << " and Paused " << p << " mouse captures" << eol;
}
void curve_editor::start_mouse_capture_from_menu () {
start_mouse_capture ();
todo = START_CAPTURE;
cursor = " Capturing mouse. Click or ESC to finish!";
}
void curve_editor::set_picked_curve_name (const string& name) {
if (!pik()) pik = pick_cur_curve ();
cons << "Curve " << pik.crv->name;
pik.crv->set_name (name);
cons << " changed to " << name << eol;
}
void curve_editor::set_limit (float f) {
if (!pik()) pik = pick_cur_curve ();
curve_info& ci = curveinfo [pik.crv_id];
multi_curve* crv = ci.curve;
crv->set_limit (f);
crv->evaluate ();
ci.lisner->edited (this, pik.crv_id);
cons << "Curve roughness = " << f << eol;
}
void curve_editor::set_curve_style (multi_curve* crv) {
const char* pre [] = {" Curve is", " Waveform is"};
int i = waved;
int s = crv->shapeform;
const char* awhat [] = {"classical", "a shape" };
sprintf (BUFFER, "%s %s", pre[i], awhat[s]);
MENU.ol_curve_style.set_text(BUFFER);
float cx [] = {0.0f, 0.5f};
crv->cx = cx[s];
crv->cy = 0.0f;
}
void curve_editor::toggle_curve_style () {
multi_curve* crv = get_curve (curcrv);
mix = *crv;
crv->set_shapeform (!crv->shapeform);
set_curve_style (crv);
render_curve_samples ();
curveinfo[curcrv].lisner->edited (this, curcrv);
}
void curve_editor::curve_picked () {
if (next_todo == FOLD_ALL) fold_all_tangents (pik);
//else if (next_todo == UNFOLD_ALL) unfold_all_tangents (pik);
else if (next_todo == MIRROR_ALL) {int whole_curve = 1; mirror (whole_curve);}
else if (next_todo == COPY) copy_curve ();
else if (next_todo == PASTE) paste (pik);
else {
todo = next_todo;
cursor = cursor.next_mesg;
cons << GREEN << "Picked " << pik.crv->name << eol;
}
}
void curve_editor::try_sizing_tangent () {
todo = PICK_TANGENT;
cursor = "Pick a tangent on a curve";
next_todo = SIZE_TANGENT;
cursor += "";
}
void curve_editor::start_sizing_tangent () {
mouse_slider0.add (MENUP.col);
activate_mouse_slider (-2);
quick_nothing (lmb_move);
}
void curve_editor::size_tangent (float d) {
curve_info& ci = curveinfo [pik.crv_id];
multi_curve& crv = *ci.curve;
float vx, vy; crv.get_vertex (pik.id, vx, vy);
float m = 0.0f, x = 0.0f, y = 0.0f;
switch (pik.what) {
case hit_t::RIGHT_TANGENT:
pik.right_tangent_magnitude += d;
m = pik.right_tangent_magnitude;
x = vx + m * pik.right_tangent.x;
y = vy + m * pik.right_tangent.y;
break;
case hit_t::LEFT_TANGENT:
pik.left_tangent_magnitude += d;
m = pik.left_tangent_magnitude;
x = vx + m * pik.left_tangent.x;
y = vy + m * pik.left_tangent.y;
break;
}
move (pik, x, y);
cursor.set (x, y, this);
}
void curve_editor::set_rpm (float r) {
cons << "RPM = " << r << eol;
curve_info& ci = curveinfo [curcrv];
if (r != 0) {
float rps = r / 60.0;
float rpf = rps / FPS;
ci.angle = rpf * TWO_PI;
if (ci.lastrpm == 0) {
undos.push_back (undo_t (curcrv, *ci.curve, win));
ci.totang = 0.0f;
}
ci.startt = ui_clk() - TIME_PER_FRAME;
}
ci.lastrpm = r;
}
void curve_editor::rotate () {
for (int i = 0; i < curves; ++i) {
curve_info& ci = curveinfo[i];
if (ci.lastrpm) {
float now = ui_clk();
float elapsed = now - ci.startt;
if (elapsed >= TIME_PER_FRAME) {
ci.startt = ui_clk ();
float mult = elapsed / TIME_PER_FRAME;
float byangle = mult * ci.angle;
float sum = ci.totang + byangle;
if (ci.autoflip && sum >= ci.every.rad) {
float finrot = ci.every.rad - ci.totang;
ci.curve->rotate (ci.dir * finrot);
ci.totang = 0.0f;
ci.dir *= -1;
if (ci.randoflip) ci.randeg ();
} else {
ci.totang = sum;
ci.curve->rotate (ci.dir * byangle);
}
ci.lisner->edited (this, i);
}
}
}
}
void curve_editor::scale (float sx, float sy) {
get_picked_curve ();
undos.push_back (undo_t (pik.crv_id, *pik.crv, win));
pik.crv->scale (sx, sy);
}
void curve_editor::hilite_item (int id) {
hit_t& hit = hitlist [id];
multi_curve& crv = *hit.crv;
point<float>& v = crv.vertices [hit.id];
point<float>& lt = crv.left_tangents [hit.id];
point<float>& rt = crv.right_tangents [hit.id];
static const float scl = 2.0f;
glLineWidth (3);
glColor3f (scl * crv.r, scl * crv.g, scl * crv.b);
draw_handle (v);
switch (hit.what) {
case hit_t::LEFT_TANGENT:
draw_tangent (v, lt);
draw_handle (lt);
break;
case hit_t::RIGHT_TANGENT:
draw_tangent (v, rt);
draw_handle (rt);
break;
}
glLineWidth (1);
}
void curve_editor::apply_plugin (plugin* p) {
int r = 0;
curve_info& ci = curveinfo [curcrv];
multi_curve* crv = ci.curve;
if (p->mix) mix = *crv;
if (p->undo) {
undos.push_back (undo_t (curcrv, *crv, win));
r = p->apply (*crv);
if (r == 0) {
undos.pop_back ();
return;
}
} else {
r = p->apply (*crv);
}
if (r) {
pomo.validate ();
ci.lisner->edited (this, curcrv);
}
ci.totang = 0.0f;
}
int curve_editor::stop_todo () {
if (todo != curve_editor::NOTHING) {
abs_nothing ();
return 1;
}
return 0;
}
#ifdef __SVG__
void curve_editor::write_samples (ofstream& svg) {
float x = 0, y = 0;
float ox, oy;
obj2win (x, y, ox, oy);
static float cx = cs.x[0];
float dx, dy;
for (int i = 0; i < cs.n; ++i) {
obj2win (cs.x[i], cs.y[i], dx, dy);
svg << "<polyline fill=\"none\" stroke=\"black\" points=\"";
float cdx = cx + dx;
svg << cdx << ',' << dy << ' ' << cdx << ',' << oy << endl;
svg << "\"/>" << endl;
}
cx += dx;
}
void curve_editor::write_curve (multi_curve* crv, ofstream& svg, float w, float h, float t, float left, float top, box<float>& bbox) {
svg << "<polyline style=\"stroke-width:" << t << ";stroke-linecap:round;stroke-miterlimit:" << t << ";\" fill=\"none\" stroke=\"black\" points=\"";
vector<crvpt> vpts; crv->get_profile_points (vpts);
for (int p = 0, q = vpts.size (); p < q; ++p) {
float dx = vpts[p].x, dy = vpts[p].y;
svg << left + w * (dx - bbox.left) / bbox.width << "," << top + h * (dy - bbox.bottom) / bbox.height << spc;
}
svg << "\"/>" << endl;
}
void curve_editor::write_svg (float h, float t, const string& fn, float left, float top) {
multi_curve* crv = get_picked_curve ();
box<float> bbox; crv->calc_bbox (bbox);
string fname = user_data_dir + fn;
ofstream svg (fname.c_str(), ios::out);
float w = bbox.width / bbox.height * h;
cons << "w = " << w << " h = " << h << "bbox: " << bbox << eol;
svg << "<?xml version=\"1.0\"?>" << endl;
svg << "<svg xmlns=\"https://www.w3.org/2000/svg\" width=\"" << w << "\" height=\"" << h << "\" viewBox=\"" << left << spc << top << spc << w << spc << h << "\">" << endl;
write_curve (crv, svg, w, h, t, left, top, bbox);
svg << "</svg>" << endl;
cons << GREEN << "written curve to: " << fname << eol;
}
#endif
#ifdef __HPGL__
void curve_editor::write_curve (multi_curve* crv, ofstream& hpgl, float scale, float penmag) {
vector<crvpt> vpts; crv->get_profile_points (vpts);
float minx = -1.0f, miny = -1.0f, maxx = 1.0f, maxy = 1.0f, width = maxx - minx, height = maxy - miny;
for (int i = 0, j = vpts.size (); i < j; ++i) {
float x = vpts[i].x, y = vpts[i].y;
float ox = (x - minx) * scale / width;
float oy = (y - miny) * scale / height;
vpts[i].x = ox;
vpts[i].y = oy;
}
float lx = vpts[0].x, ly = vpts[0].y;
float m = 0;
rnd<int> pen (1, 8);
static const char semicolon = ';';
for (int p = 1, q = vpts.size (); p < q; ++p) {
float px = vpts[p].x, py = vpts[p].y;
float dx = (px - lx), dy = (py - ly);
m += magnitude (dx, dy);
if (m >= penmag) { // switch pen for fun
m -= penmag;
hpgl << "SP" << pen () << semicolon << eol;
}
hpgl << "PR" << dx << ", " << dy << semicolon << eol;
lx = px;
ly = py;
}
}
void curve_editor::write_hpgl (float scale, float penmag) {
multi_curve* crv = get_picked_curve ();
string fname = user_data_dir + crv->name + ".hpgl";
ofstream hpgl (fname.c_str(), ios::out);
hpgl << "IN;" << eol;
hpgl << "PD;" << eol;
write_curve (crv, hpgl, scale, penmag);
hpgl << "PU;" << eol;
cons << GREEN << "written curve to: " << fname << eol;
}
#endif
int curve_editor::esc () {
int ret = 0;
if (stop_todo ())
ret = 1;
else {
if (!rmb) {
load_instrument ();
ret = 1;
} else
ret = 0;
}
if (sinemixer.harms.editing) {
sinemixer.harms.stop_editing ();
ret = 1;
}
return ret;
}
void curve_editor::drawerlay () {
// called from plugin sin/cos/radius editors
project ();
if (over) uis.plugin__browser.plugins[uis.plugin__browser.cur]->draw (dynamic_cast<curve_editor*>(over));
unproject ();
}
int curve_editor::setcrvvis (int i, int v) {
int& vi = curveinfo[i].visible;
vi = v;
static const int d [] = {-1, 1};
visibles += d[v];
if (visibles == 0) {
visibles = 1;
vi = 1;
curcrv = i;
pik = pick_cur_curve ();
setup_tools_menu ();
return 0;
} else if (visibles == 1) {
curcrv = -1;
for (int i = 0; i < curves; ++i) {
curve_info& ci = curveinfo[i];
if (ci.visible) {
curcrv = i;
break;
}
}
pik = pick_cur_curve ();
setup_tools_menu ();
}
return 1;
}
curve_info::curve_info (multi_curve* c, curve_listener* l, int p) {
init ();
curve = c;
lisner = l;
picked = p;
}
curve_info::curve_info () {
init ();
curve = 0;
lisner = 0;
}
void curve_info::init () {
picked = 0;
visible = 1;
angle = 0;
lastrpm = 0;
dir = 1;
autoflip = 0;
randoflip = 0;
rd.set (0.0f, 1.0f);
totang = 0.0f;
every0 = 180.0f;
every = every0;
startt = 0.0;
}
void curve_editor::paste_append (multi_curve& to, multi_curve& from) {
int n = from.num_vertices;
int lv = to.last_vertex;
point<float>& v0 = from.vertices[0];
point<float>& vl = to.vertices[lv];
point<float> delta; direction (delta, v0, vl);
point<float>& rtl = to.right_tangents[lv];
point<float>& rt0 = from.right_tangents[0];
rtl.x = rt0.x + delta.x;
rtl.y = rt0.y + delta.y;
for (int i = 1; i < n; ++i) {
point<float>& vi = from.vertices[i];
point<float>& lt = from.left_tangents[i];
point<float>& rt = from.right_tangents[i];
to.add_vertex (vi.x + delta.x, vi.y + delta.y);
to.add_left_tangent (lt.x + delta.x, lt.y + delta.y);
to.add_right_tangent (rt.x + delta.x, rt.y + delta.y);
}
to.evaluate ();
}
void curve_editor::do_powbeat (float m) {
if (beater) {
if (!pik()) pik = pick_cur_curve ();
powbeat (pik.crv->name, *bv[pik.crv_id], m);
}
}