(root)/wip/src/curve_editor.cc - Rev 1524
Rev 1495 |
Rev 1533 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* curve_editor.cc
* DIN Is Noise is copyright (c) 2006-2020 Jagannathan Sampath
* 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;
extern int GOT_FPS;
extern char BUFFER [];
extern instrument* get_current_instrument ();
void show_curve_picker () {
curve_picker.show ();
}
curve_editor::curve_editor (const std::string& settingsf, const std::string& helpf) : helptext (helpf) {
curcrv = 0;
do_nothing ();
is_waveform_editor = sine_enabled = fft_enabled = samples_enabled = draw_curve_only = 0;
load (settingsf);
lmb_move = 0;
lmb_clicked = 0;
angle = 0;
last_rpm = 0;
gl_pts = 0;
n_pts = 0;
hlabel_only = 0;
mark_segments = 0;
}
curve_editor::~curve_editor () {
save ();
dlog << "-- destroyed curve editor: " << name << " ---" << endl;
}
void curve_editor::load (const std::string& fname) {
// load settings from file
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);
int ct; file >> ignore >> ct;
carry_tangents = ct;
int mt; file >> ignore >> mt;
mirror_tangents = mt;
file >> ignore >> sine_enabled;
file >> ignore >> label_vertices;
tb.load (file);
file >> ignore >> fft_enabled;
file >> ignore >> samples_enabled;
file >> ignore >> hz >> ignore >> nperiods;
cs.set (hz, nperiods);
file >> ignore >> is_waveform_editor;
file >> ignore >> draw_curve_only;
file >> ignore >> kbkb_attack_editor;
file >> ignore >> overlay;
file >> ignore >> curcrv;
file >> ignore >> axis;
file >> ignore >> draw_plugin_output;
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;
file << "sine_enabled " << sine_enabled << endl;
file << "label_vertex " << label_vertices << endl;
tb.clear ();
tb.save (file);
file << "fft_enabled " << fft_enabled << endl;
file << "samples_enabled " << samples_enabled << endl;
file << "hz " << hz << endl;
file << "nperiods " << nperiods << endl;
file << "is_waveform_editor " << is_waveform_editor << endl;
file << "draw_curve_only " << draw_curve_only << endl;
file << "sustain_editing " << kbkb_attack_editor << endl;
file << "overlay " << overlay << endl;
file << "curcrv " << curcrv << endl;
file << "mirror_axis " << axis << endl;
file << "draw_plugin_output " << draw_plugin_output << endl;
}
void curve_editor::prep_move () {
undos.push_back (undo_t (pik.crv_id, *pik.crv, win));
curveinfo[pik.crv_id].lisner->selected (pik.crv_id);
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) { // first lmb
lmb_clicked = 1;
if (lmb_move == FINISH_ON_CLICK) {
cons << RED << "Finished moving " << pik << eol;
do_nothing ();
return 1;
}
if (todo == NOTHING) { // try to hit something to move
hittest ();
if (pik()) { // hit
if (pomo.hit (pik))
;
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 FOLD_VERTEX:
hittest ();
if (pik()) fold_tangents_of_vertex (pik);
return 1;
case UNFOLD_VERTEX:
hittest ();
if (pik()) unfold_tangents_of_vertex (pik);
return 1;
case MIRROR_VERTEX:
hittest ();
if (pik()) mirror ();
return 1;
case START_CAPTURE:
do_nothing ();
return 1;
case ASSIGN_CAPTURE:
hittest ();
assign_mouse_capture ();
return 1;
case PICK_CURVE:
hittest ();
if (pik()) curve_picked ();
return 1;
}
}
}
} else {
lmb_clicked = 0;
if (lmb_move && !MENU.show) {
if (todo == MOVE_PICKED) move (); else
if (todo == MOVE_ALL) move (1); // all
lmb_move = FINISH_ON_CLICK;
return 1;
}
}
if (library) {
if (library->num_curves ()) {
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 (keypressed (SDLK_i)) insert_using_menu ();
else if (keypressed (SDLK_v)) {
if (CTRL) { // ctrl + v ie paste
paste_using_menu ();
} else { // delete vertex or tanget
todo = REMOVE_VERTEX;
hittest ();
if (pik()) remove ();
todo = NOTHING;
}
}
else if (keypressedd (SDLK_z)) { // undo & redo
if (SHIFT) // l_shift + z - redo
do_redo ();
else
do_undo ();
}
else if (keypressedd (SDLK_r)) apply_plugin (uis.plugin__browser.get_cur ()); // apply current plugin
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_COMMA)) {
mark_segments = !mark_segments;
dont_call_listener (MENU.cb_mark_segments, mark_segments);
}
else if (keypressedd (SDLK_PERIOD)) {
draw_curve_only = !draw_curve_only;
dont_call_listener (MENU.cb_draw_curve, draw_curve_only);
}
else if (keypressed (SDLK_c)) {
if (CTRL) { // l_ctrl + c - copy curve
if (num_curves == 1) {
pik = pick_cur_curve ();
copy_curve ();
} else {
hittest ();
if (pik()) copy_curve (); else if (hitlist.size()) copy_using_menu ();
}
} else { // carry tangents?
carry_tangents = !carry_tangents;
if (carry_tangents)
cons << YELLOW << "vertices carry their tangents" << eol;
else
cons << YELLOW << "vertices desert their tangents" << eol;
MENU.set_vertices_carry_tangents (carry_tangents);
}
}
else if (keypressed (SDLK_SLASH)) {
toggle_waveform_samples_display ();
} else if (keypressed (SDLK_l)) {
label_vertices = !label_vertices;
dont_call_listener (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 current instrument
overlay = !overlay;
if (overlay)
cons << GREEN << "Overlaid the " << get_current_instrument()->name << eol;
else
cons << GREEN << "Removed the " << get_current_instrument()->name << " from overlay" << eol;
MENU.cb_overlay_instrument.set_state (overlay, 0);
}
else if (keypressed (SDLK_F1)) helptext(); // show help
return 1;
}
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::pick (int i) {
int ncurves = curveinfo.size ();
if (i > -1 && i < ncurves) {
curve_info& ci = curveinfo[i];
ci.lisner->selected (i);
}
}
void curve_editor::calc_hit_params (hit_t& hit) {
if (!hit()) return;
multi_curve* crv = get_curve (hit.crv_id);
float vx, vy, lx, ly, rx, ry;
int id = hit.id;
crv->get_vertex (id, vx, vy);
crv->get_left_tangent (id, lx, ly);
crv->get_right_tangent (id, rx, ry);
direction (hit.left_tangent.x, hit.left_tangent.y, vx, vy, lx, ly);
direction (hit.right_tangent.x, hit.right_tangent.y, vx, vy, rx, ry);
hit.left_tangent_magnitude = (float) magnitude (hit.left_tangent.x, hit.left_tangent.y);
hit.right_tangent_magnitude = (float) magnitude (hit.right_tangent.x, hit.right_tangent.y);
const point<float>& hp = hit.get (); obj2mouse (hp.x, hp.y);
curcrv = hit.crv_id;
}
void curve_editor::hittest () {
hitlist.clear ();
for (int i = 0, j = curveinfo.size (); i < j; ++i) {
curve_info& ci = curveinfo[i];
multi_curve* crv = ci.curve;
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 ();
if (n == 1) {
pik = hitlist [0];
calc_hit_params (pik);
} else {
pik = hit_t ();
if (n > 1) show_curve_picker ();
}
}
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) hitlist.push_back (hit_t (crv, crv_id, what, i));
}
}
int curve_editor::filter_hitlist () {
for (int i = 0, j = curveinfo.size (); 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 {
++i;
}
}
return hitlist.size ();
}
void curve_editor::set_pick_from_hitlist (int i) {
pik = hitlist [i];
calc_hit_params (pik);
curveinfo [curcrv].lisner->selected (curcrv);
cons << GREEN << "Picked " << pik << eol;
}
int curve_editor::move (hit_t& hit, float x, float y, int eval_now) {
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, carry_tangents);
break;
case hit_t::LEFT_TANGENT:
need_eval = crv->set_left_tangent (hit.id, x, y);
if (mirror_tangents && need_eval) {
float vx, vy, lvx, lvy;
crv->get_vertex (hit.id, vx, vy);
unit_vector (lvx, lvy, vx, vy, x, y);
crv->set_right_tangent (hit.id, vx - hit.right_tangent_magnitude * lvx, vy - hit.right_tangent_magnitude * lvy);
}
break;
case hit_t::RIGHT_TANGENT:
need_eval = crv->set_right_tangent (hit.id, x, y);
if (mirror_tangents && need_eval) {
float vx, vy, rvx, rvy;
crv->get_vertex (hit.id, vx, vy);
unit_vector (rvx, rvy, vx, vy, x, y);
crv->set_left_tangent (hit.id, vx - hit.left_tangent_magnitude * rvx, vy - hit.left_tangent_magnitude * rvy);
}
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 vx, vy;
float dx, dy;
switch (pik.what) {
case hit_t::VERTEX:
crv->get_vertex (pik.id, vx, vy);
break;
case hit_t::LEFT_TANGENT:
crv->get_left_tangent (pik.id, vx, vy);
break;
case hit_t::RIGHT_TANGENT:
crv->get_right_tangent (pik.id, vx, vy);
break;
}
dx = cx - vx;
dy = cy - vy;
int result = 0;
for (int i = 0, j = crv->num_vertices; i < j; ++i) {
crv->get_vertex (i, vx, vy);
result |= crv->set_vertex (i, vx + dx, vy + dy);
crv->get_left_tangent (i, vx, vy);
result |= crv->set_left_tangent (i, vx + dx, vy + dy);
crv->get_right_tangent (i, vx, vy);
result |= crv->set_right_tangent (i, vx + dx, vy + dy);
}
if (result) {
crv->evaluate();
ci.lisner->edited (this, pik.crv_id);
}
return result;
}
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 << "cant delete curve: " << crv->name << eol;
undos.pop_back ();
}
clear_hit (pik);
}
void curve_editor::swap () {
if (num_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 = get_curve_info (0), &ci1 = get_curve_info (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);
crv->get_left_tangent (i, xx, yy); dx = _2mx - xx; crv->set_left_tangent (i, dx, yy);
crv->get_right_tangent (i, xx, yy); dx = _2mx - xx; crv->set_right_tangent (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);
crv->get_left_tangent (i, xx, yy); dy = _2my - yy; crv->set_left_tangent (i, xx, dy);
crv->get_right_tangent (i, xx, yy); dy = _2my - yy; crv->set_right_tangent (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 ();
draw_all ();
if (draw_plugin_output) uis.plugin__browser.draw (this);
if (show_scratch_curve) draw_scratch_curve ();
if (curve_picker.visible) hilite_item (curve_picker.id);
unproject ();
if (overlay) overlay_instrument ();
}
void curve_editor::draw_scratch_curve () {
glColor3f (1, 1, 0.7f);
int n = win_scratch_points.size () + 1;
alloc_gl_pts (n);
int k = 0;
for (int i = 0, j = n - 1; i < j; ++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 (mark_segments) mark_curve_segments ();
// draw curve profile, vertices, tangents and handles
int j = curveinfo.size ();
int j1 = j > 1;
for (int i = 0; i < j; ++i) {
curve_info& ci = curveinfo[i];
multi_curve* crv = ci.curve;
if (i == curcrv && j1) glLineWidth (2); // hilite current
if (draw_curve_only) {
glColor3f (crv->r, crv->g, crv->b);
draw_curve (crv);
} else {
glColor3f (crv->rt, crv->gt, crv->bt);
draw_tangents (crv);
glColor3f (crv->r, crv->g, crv->b);
draw_vertices (crv);
draw_curve (crv);
}
glLineWidth (1);
}
// mark beat/value (gater, fm/am, octave shift)
//
int n = bv.size ();
if (n) {
float nowx, nowy;
float x, y;
static const int marker_size = 35;
for (int i = 0; i < n; ++i) {
beat2value* bvi = bv[i];
multi_curve& crv = *bvi->crv;
if (crv.shapeform) crv.get_xy (bvi->now, nowx, nowy);
else {
nowx = bvi->now;
nowy = bvi->sol (nowx);
}
obj2win (nowx, nowy, x, y);
glColor3f (crv.r, crv.g, 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);
glLineWidth (1);
}
}
// label vertices
if (label_vertices) {
for (int i = 0, j = curveinfo.size (); 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 && curveinfo.size()) 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);
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_curve_segments () {
// drop vertical lines from current curve's profile points
glColor3f (0.2f, 0.2f, 0.2f);
curve_info& ci = curveinfo [curcrv];
multi_curve* crv = ci.curve;
vector<curve>& curv = crv->curv;
int m = 0;
for (int i = 0, j = curv.size(); i < j; ++i) m += curv[i].vpts.size ();
int e = 2 * m;
int n = 2 * e;
alloc_gl_pts (n);
for (int i = 0, j = curv.size(), k = 0; i < j; ++i) {
vector<crvpt>& vpts = curv[i].vpts;
for (int p = 0, q = vpts.size (); p < q; ++p) {
float dx, dy; obj2win (vpts[p].x, vpts[p].y, dx, dy);
gl_pts [k++]=dx;
gl_pts [k++]=dy;
gl_pts [k++]=dx;
gl_pts [k++]=0;
}
}
glVertexPointer (2, GL_FLOAT, 0, gl_pts);
glDrawArrays (GL_LINES, 0, e);
}
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);
}
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 < num_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);
pomo.validate ();
ci.lisner->edited (this, last.i);
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) {
curveinfo.push_back (curve_info (crv, lsnr));
if (++num_curves == 1) {
render_curve_samples ();
hlabel_only = crv->shapeform;
}
}
void curve_editor::clear () {
curveinfo.clear ();
hitlist.clear ();
clear_hit (pik);
num_curves = 0;
}
multi_curve* curve_editor::get_curve (int i) {
if (i > -1 && i < (int) curveinfo.size()) {
curve_info& ci = curveinfo [i];
return ci.curve;
}
return 0;
}
curve_info& curve_editor::get_curve_info (int i) {
return curveinfo[i];
}
void curve_editor::enter () {
calc_visual_params ();
if (is_waveform_editor) uis.dofft ();
setup_tools_menu ();
MENU.set_snap (snap_what);
MENU.set_vertices_carry_tangents (carry_tangents);
MENU.set_mirror_tangents (mirror_tangents);
MENU.sp_waveform_hz.set_value (hz);
MENU.sp_waveform_periods.set_value (nperiods);
MENU.sp_curve_rpm.set_value (last_rpm);
dont_call_listener (MENU.cb_show_waveform_samples, samples_enabled);
dont_call_listener (MENU.cb_label_vertices, label_vertices);
dont_call_listener (MENU.cb_draw_curve, draw_curve_only);
dont_call_listener (MENU.cb_overlay_instrument, overlay);
if (uis.is_widget_on_screen (&uis.plugin__browser, this)) {
uis.plugin__browser.set_ed (this);
uis.plugin__browser.set_fold (!draw_plugin_output);
}
}
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::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 ();
}
curve_info::curve_info (multi_curve* c, curve_listener* l, int p) {
curve = c;
lisner = l;
picked = p;
}
curve_info::curve_info () {
curve = 0;
lisner = 0;
picked = 0;
}
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;
setup_tools_menu ();
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);
*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;
do_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 ();
}
curve_samples::curve_samples () {
m = 0;
dm = 0;
n = 0;
n_reached = 0;
x = xp = y = xy = 0;
hz = step = 0;
nperiods = 3;
}
curve_samples::~curve_samples () {
if (x) delete[] x;
if (xp) delete[] xp;
if (y) delete[] y;
if (xy) delete[] xy;
}
/*void curve_samples::calc_dm (int fps, int prev_fps) {
if (prev_fps == 0) prev_fps = fps;
const int dm0 = 2;
dm = max (dm0, int (dm0 * prev_fps * 1.0f / fps));
cons << "prev: " << prev_fps << " fps: " << fps << " dm = " << dm << eol;
}*/
void curve_samples::render (multi_curve* crv) {
solver ss (crv);
float dxx = step, xx = -dxx;
ss (xx, dxx, n, y);
float dx = step;
box<float> bbox; crv->calc_bbox (bbox);
for (int i = 0; i < n; ++i) {
x[i] = bbox.right + dx;
dx += step;
}
if (crv->shapeform) {
vector<curve>& curv = crv->curv;
int ncrvs = curv.size ();
int pid = 0, cid = 0;
double dxp = 0;
for (int i = 0; i < n;) {
vector<crvpt>& vpts = curv[cid].vpts;
vector<crvpt>& dpts = curv[cid].dpts;
int pid1 = pid+1;
crvpt& pp = dpts [pid];
crvpt& pp1 = dpts [pid1];
double left = pp.x, right = pp1.x;
if (dxp <= right) {
float delta = right - left;
float amt = (dxp - pp.x) * 1. / delta;
crvpt& vp = vpts [pid];
crvpt& vp1 = vpts [pid1];
xp[i++] = vp.x + amt * (vp1.x - vp.x);
dxp += step;
} else {
int last2 = dpts.size () - 2;
if (++pid > last2) {
double lastx = curv[cid].dpts[pid].x;
pid = 0;
if (++cid >= ncrvs) {
cid = 0;
dxp -= lastx;
}
}
}
}
}
}
void curve_samples::draw (curve_editor* ed, multi_curve* crv) {
glColor3f (1, 0.25, 0.25);
float dx, dxp, dy;
float ox, oy;
ed->obj2win (0, 0, ox, oy);
dm = n * 1.0f / (nperiods * nsec * min (FPS, GOT_FPS)); // 1 period in n seconds
m += dm;
if (m >= n) {
if (n_reached == 0) {
m = n - 1;
n_reached = 1;
} else {
m = 0;
n_reached = 0;
}
}
int j = 0, k = m;
for (int i = 0; i <= k; ++i) {
ed->obj2win (x[i], y[i], dx, dy);
xy[j++] = dx;
xy[j++] = dy;
}
glVertexPointer (2, GL_FLOAT, 0, xy);
glDrawArrays (GL_LINE_STRIP, 0, k+1);
if (crv->shapeform) { // mark position
ed->obj2win (xp[k], y[k], dxp, dy);
float sh[4];
sh[0]=dxp;sh[1]=dy;
glPointSize (6);
glVertexPointer (2, GL_FLOAT, 0, sh);
glDrawArrays (GL_POINTS, 0, 1);
glPointSize (1);
sh[0]=dxp;sh[1]=dy;sh[2]=dx;sh[3]=dy;
glVertexPointer (2, GL_FLOAT, 0, sh);
glDrawArrays (GL_LINES, 0, 2);
}
}
void curve_samples::set (float zh, int p) {
if (zh <= 0) zh = 440;
if (p <= 0) p = 1;
nperiods = p;
hz = zh;
hz2step (hz, step);
int oldn = n;
n = p * (1 + 1.0f / step) + 0.5;
if (x) {
if (n > oldn) {
delete[] x; x = new float [n];
delete[] xp; xp = new float [n];
delete[] y; y = new float [n];
delete[] xy; xy = new float [2 * n];
}
} else {
x = new float [n];
xp = new float [n];
y = new float [n];
xy = new float [2 * n];
}
int nsz = sizeof (float) * n;
memset (x, 0, nsz);
memset (xp, 0, nsz);
memset (y, 0, nsz);
memset (xy, 0, 2 * nsz);
}
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 REMOVE_VERTEX:
remove ();
break;
case FOLD_VERTEX:
fold_tangents_of_vertex (pik);
break;
case UNFOLD_VERTEX:
unfold_tangents_of_vertex (pik);
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_mesg = " Click on vertex to delete. ESC to stop.";
}
void curve_editor::modulate_point () {
todo = MODULATE_POINT;
cursor_mesg = " 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 (num_curves == 1) {
todo = INSERT_VERTEX;
cursor_mesg = " Click to insert vertex. ESC to stop.";
} else {
todo = PICK_CURVE;
cursor_mesg = " Pick curve to insert vertex. ESC to abort!";
next_todo = INSERT_VERTEX;
next_cursor_mesg = " 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_VERTEX;
cursor_mesg = " Click on vertex to fold tangents. ESC to stop.";
} else {
if (num_curves == 1) {
pik = pick_cur_curve ();
fold_all_tangents (pik);
do_nothing ();
} else {
todo = PICK_CURVE;
next_todo = FOLD_ALL;
next_cursor_mesg = "";
cursor_mesg = " Pick curve to fold tangents. ESC to stop.";
}
}
}
void curve_editor::unfold_tangents_using_menu () {
int unfold_vertex = MENU.cb_selection_only.state;
if (unfold_vertex) {
todo = UNFOLD_VERTEX;
cursor_mesg = " Click on vertex to unfold tangents. ESC to stop.";
} else {
if (num_curves == 1) {
pik = pick_cur_curve ();
unfold_all_tangents (pik);
do_nothing ();
} else {
todo = PICK_CURVE;
cursor_mesg = " Pick curve to unfold tangents. ESC to stop.";
next_todo = UNFOLD_ALL;
next_cursor_mesg = "";
}
}
}
void curve_editor::mirror_using_menu () {
int mirror_vertex = MENU.cb_selection_only.state;
if (mirror_vertex) {
todo = MIRROR_VERTEX;
cursor_mesg = " Click on vertex or tangent to flip. ESC to stop.";
} else {
if (num_curves == 1) {
pik = pick_cur_curve ();
int whole_curve = 1;
mirror (whole_curve);
do_nothing ();
} else {
todo = PICK_CURVE;
cursor_mesg = " Pick the curve to flip. ESC to stop.";
next_todo = MIRROR_ALL;
next_cursor_mesg = "";
}
}
}
void curve_editor::copy_using_menu () {
if (num_curves == 1) {
copy = *curveinfo[0].curve;
cons << GREEN << "Copied " << copy.name << eol;
} else {
todo = PICK_CURVE;
cursor_mesg = " Pick the curve to copy. ESC to stop.";
next_todo = COPY;
next_cursor_mesg = "";
}
}
void curve_editor::paste_using_menu () {
if (num_curves == 1) {
pik = pick_cur_curve ();
paste (pik);
} else {
todo = PICK_CURVE;
cursor_mesg = " Pick the curve to paste. ESC to stop.";
next_todo = PASTE;
next_cursor_mesg = "";
}
}
void curve_editor::fold_all_tangents (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));
convert2_polyline (crv);
ci.lisner->edited (this, hit.crv_id);
}
void curve_editor::unfold_all_tangents (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));
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
ci.lisner->edited (this, hit.crv_id);
}
void curve_editor::fold_tangents_of_vertex (hit_t& hit) {
if (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));
float vx, vy; crv.get_vertex (hit.id, vx, vy);
crv.set_left_tangent (hit.id, vx, vy);
crv.set_right_tangent (hit.id, vx, vy);
crv.evaluate ();
ci.lisner->edited (this, hit.crv_id);
do_nothing ();
}
}
void curve_editor::unfold_tangents_of_vertex (hit_t& hit) {
if (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));
const point<float>& obj = get_obj_chunk ();
float vx, vy; crv.get_vertex (hit.id, vx, vy);
crv.set_left_tangent (hit.id, vx - obj.x, vy);
crv.set_right_tangent (hit.id, vx + obj.x, vy);
crv.evaluate ();
ci.lisner->edited (this, hit.crv_id);
do_nothing ();
}
}
void curve_editor::render_curve_samples () {
if (samples_enabled) {
if (!pik()) pik = pick_cur_curve ();
cs.render (pik.crv);
}
}
void curve_editor::toggle_waveform_samples_display () {
if (is_waveform_editor) {
samples_enabled = !samples_enabled;
render_curve_samples ();
dont_call_listener (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::do_nothing () {
stop_mouse_capture ();
lmb_move = 0;
if (todo == ADD_VERTEX) {
if (MENU.show == 0)
replace ();
else
clear_scratch_curve ();
}
todo = next_todo = NOTHING;
cursor_mesg = next_cursor_mesg = "";
}
void curve_editor::copy_curve () {
if (pik()) {
copy = *get_curve(pik.crv_id);
cons << GREEN << "Copied "<< copy.name << "." << eol;
do_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);
todo = PICK_CURVE;
cursor_mesg = " Click to pick curve, ESC to abort!";
next_todo = NOTHING;
next_cursor_mesg = "";
}
hit_t curve_editor::pick_cur_curve () {
multi_curve* crv = curveinfo[curcrv].curve;
return hit_t (crv, 0);
}
multi_curve* curve_editor::get_picked_curve () {
if (!pik()) {
pik = pick_cur_curve ();
return pik.crv;
} else
return pik.crv;
}
void curve_editor::set_picked_curve_name (const string& name) {
if (pik()) pik.crv->set_name (name);
}
void curve_editor::delete_curve () {
if (library) library->del ();
}
void curve_editor::draw_replacement_curve_using_menu () {
if (num_curves == 1) {
cons << YELLOW << "Replacing the viewed curve" << eol;
pik = pick_cur_curve ();
show_scratch_curve = 1;
todo = ADD_VERTEX;
cursor_mesg = " Click to add vertex. ESC to stop.";
} else {
todo = PICK_CURVE;
cursor_mesg = " Pick curve to replace. ESC to abort!";
next_todo = ADD_VERTEX;
next_cursor_mesg = " 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 () < 2) cursor_mesg = "Click to add vertex. ESC to abort!"; else cursor_mesg = "Click to add vertex. ESC 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;
do_nothing ();
}
}
void curve_editor::assign_mouse_capture_from_menu () {
if (mocap0()) {
todo = ASSIGN_CAPTURE;
next_todo = NOTHING;
cursor_mesg = " 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()) {
do_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_mesg = " Capturing mouse. Click or ESC to finish!";
}
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);
}
void curve_editor::setup_tools_menu () {
multi_curve* crv = get_curve (curcrv);
MENU.lf_curve_name.set_text (crv->name);
MENU.sp_curve_limit.set_value (crv->limit);
MENU.picked (MENU.ol_mirror.option, 0);
set_curve_style ();
}
void curve_editor::curve_picked () {
setup_tools_menu ();
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_mesg = next_cursor_mesg;
}
}
void curve_editor::set_curve_style () {
multi_curve* crv = get_curve (curcrv);
string pre (" Curve is "); if (is_waveform_editor) pre = " Waveform is ";
if (crv->shapeform) MENU.ol_curve_style.set_text(pre + "a shape"); else MENU.ol_curve_style.set_text (pre + "classical");
}
void curve_editor::toggle_curve_style () {
multi_curve* crv = get_curve (curcrv);
mix = *crv;
crv->set_shapeform (!crv->shapeform);
set_curve_style ();
render_curve_samples ();
if (crv->shapeform && is_waveform_editor && !samples_enabled) toggle_waveform_samples_display ();
curveinfo[curcrv].lisner->edited (this, curcrv);
}
void curve_editor::set_rpm (float _rpm) {
if (_rpm == 0) {
rpm = hit_t ();
} else {
if (!pik()) pik = pick_cur_curve ();
rpm = pik;
float rps = _rpm / 60.0;
float rpf = rps / FPS;
angle = rpf * TWO_PI;
if (last_rpm == 0) {
undos.push_back (undo_t (rpm.crv_id, *rpm.crv, win));
rpm.crv->calc_centroid ();
}
startt = ui_clk() - TIME_PER_FRAME;
}
last_rpm = _rpm;
}
void curve_editor::rotate () {
if (rpm()) {
float now = ui_clk();
float elapsed = now - startt;
if (elapsed >= TIME_PER_FRAME) {
float mult = elapsed / TIME_PER_FRAME;
rpm.crv->rotate (mult * angle);
curveinfo[rpm.crv_id].lisner->edited (this, rpm.crv_id);
startt = ui_clk();
}
}
}
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];
glLineWidth (2);
point<float>& v = hit.crv->vertices [hit.id];
point<float>& lt = hit.crv->left_tangents [hit.id];
point<float>& rt = hit.crv->right_tangents [hit.id];
glColor3f (0, 1, 0);
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) {
multi_curve* crv = curveinfo [curcrv].curve;
if (p->mix) mix = *crv;
if (p->undo) undos.push_back (undo_t (curcrv, *crv, win));
if (!p->apply (*crv) && p->undo) undos.pop_back ();
else {
if (p == &spiraler_ || p == &rosemilker)
crv->centroid_style = multi_curve::SET;
else
crv->centroid_style = multi_curve::CALC;
pomo.validate ();
curveinfo[curcrv].lisner->edited (this, curcrv);
if (last_rpm && rpm.crv == crv) rpm.crv->calc_centroid ();
}
}
int curve_editor::stop_todo () {
if (todo != curve_editor::NOTHING) {
do_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) {
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, dy; obj2win (vpts[p].x, vpts[p].y, dx, dy);
//dy = win.top - dy;
svg << w * (dx - win.left) / win.width << "," << h - 1 - h * (dy - win.bottom) / win.height << SPC;
}
svg << "\"/>" << endl;
}
void curve_editor::write_svg (float h, float t, const string& fn) {
multi_curve* crv = get_picked_curve ();
string fname = user_data_dir + fn;
ofstream svg (fname.c_str(), ios::out);
float w = win.width / win.height * h;
svg << "<?xml version=\"1.0\"?>" << endl;
svg << "<svg xmlns=\"https://www.w3.org/2000/svg\" width=\"" << w /*win.width*/ << "\" height=\"" << h /*win.height*/ << "\" viewBox=\"" << "0 0 " << w << ' ' << h << "\">" << endl;
write_curve (crv, svg, w, h, t);
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.sine_levels.editing) {
sinemixer.sine_levels.stop_editing ();
ret = 1;
}
return ret;
}