Rev 2302 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* mondrian.cc
* inspired by the works of Piet Mondrian
* DIN Is Noise is released under GNU Public License 2.0
* DIN Is Noise is copyright (c) 2006-2025 Jagannathan Sampath
* For more information, please visit https://dinisnoise.org/
*/
#include <fstream>
#include <iostream>
#include <sstream>
#include "ui_list.h"
#include "main.h"
#include "font.h"
#include "console.h"
#include "mondrian.h"
#include "solver.h"
#include "random.h"
#include "utils.h"
#include "vector2d.h"
#include "font.h"
#include "audio.h"
#include "curve_library.h"
#include "container.h"
#include "rect.h"
#include "ball.h"
#include "slit.h"
#include "oscilloscope.h"
#include <algorithm>
#include <math.h>
using namespace std;
extern std::string user_data_dir;
extern viewport view;
extern font fnt;
extern mondrian mondrian0;
extern audio_out aout;
extern curve_library wav_lib, attack_lib, decay_lib;
extern gotog _gotomax;
extern int quit;
extern beat2value octave_shift;
extern float GOLDEN_RATIO;
extern const float PI_BY_180;
extern int IPS;
extern std::map <string, float> INTERVALS;
extern oscilloscope scope;
extern char BUFFER [];
extern float MIN_TIME;
extern int line_height;
mondrian::mondrian () :
wave ("mondrian_waveform.crv"), waved ("mondrian_waveform.ed"), wavlis (wave_listener::MONDRIAN),
attack ("mondrian_attack.crv"), decay ("mondrian_decay.crv"), attacked ("mondrian_attack.ed"),
decayed ("mondrian_decay.ed"), fdr (slit::INITIAL_OPEN_CLOSE_TIME), _help ("mondrian.hlp") {
pan = zoom = 1;
root = 0;
hit = 0;
edge = edge::NONE;
num_balls = 0;
adding_balls = 0;
moving_balls = 0;
editing_edge = 0;
editing_slit = 0;
slitting = NOTHING;
started_making_ball = 0;
new_ball = 0;
n_pts = 0;
pts = 0;
pts_d = 0;
pts_clr = 0;
n_mrk = 0;
mrk = 0;
cursor = 0;
num_triggered_notes = 0;
note_volume = 0;
voices = 0;
min_voices = 0;
auto_adjust_voices = 0;
delta_attack_time = delta_decay_time = MIN_TIME;
lmb_clicked = 0;
num_boxes = 8;
label_notes = 1;
label_hz_vol = 0;
fill_boxes = 1;
draw__boxes = 1;
draw__notes = 1;
draw_slit_cutter = 0;
num_selected_slits = 0;
num_selected_balls = 0;
sel_tar = SELECT_BALLS;
nleaves = 0;
// box auto splitting
splitting_rects = 0;
auto_split_orient = split::BOTH;
auto_split_at = split::NOTES;
// both [auto]split, delete
split_leaf = rect::BIGGEST;
delete_leaf = rect::RANDOM;
delete_all_rects = 0;
inst = 1;
}
mondrian::~mondrian () {
wave.save ("mondrian_waveform.crv");
attack.save ("mondrian_attack.crv");
decay.save ("mondrian_decay.crv");
ofstream edf ((user_data_dir + "mondrian.ed").c_str (), ios::out); save_settings (edf);
ofstream dataf ((user_data_dir + "mondrian.data").c_str(), ios::out);
string R("R");
save_boxes (dataf, root, R);
save_balls (dataf);
dataf << "poly radius " << poly.radius << " points " << poly.points << endl;
save_slits (dataf);
if (pts) delete[] pts;
if (pts) delete[] pts_d;
if (pts_clr) delete[] pts_clr;
if (mrk) delete[] mrk;
for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) delete *p;
if (root) delete_children (root);
if (new_ball) delete new_ball;
dlog << "--- destroyed Mondrian --" << endl;
}
void mondrian::enter () {}
void mondrian::leave () {
stop_doing_stuff ();
ui::leave ();
}
void mondrian::launch_note (ball* _ball, float t, float t0, float dt, const pair<float, float>& invl) {
if (quit != DONT || (lmb && _ball == new_ball)) return;
float mid = (invl.first + invl.second) / 2.0f;
if (t < mid) t = invl.first; else t = invl.second;
float interval = 1 + (t - t0) * dt;
float frequency = get_tonic (this) * interval * _ball->pitch_mult;
N.set_freq (frequency);
++num_triggered_notes;
if (auto_adjust_voices)
voices = max (min_voices, min_voices + num_triggered_notes);
else
voices = min_voices;
note_volume = 1.0f / voices * _ball->vol_mult;
triggered_notes.push_back (
triggered_note (N, note_volume, _ball->x, _ball->y, _ball->trig_what,
_ball->binaural, _ball->just, _ball->sep
)
);
triggering_balls.push_back (_ball);
++_ball->num_notes;
triggered_note& last_triggered_note = triggered_notes.back ();
last_triggered_note.setup (&wave, &attack, &decay);
print_num_triggered_notes ();
rnd<unsigned char> rd (0, 255);
}
void mondrian::print_num_triggered_notes () {
stringstream ss; ss << "Voices: " << num_triggered_notes << "/" << voices;
uis.l_mondrian_voices.set_text (ss.str());
}
void mondrian::fillpatbuf () {
memset (patbuf, 0, 1024);
if (patlen) {
for (int i = 0, j = 0; i < 1024; i += patstep) {
if (j >= patlen) j = 0;
patbuf[i]=patstr[j++];
}
}
}
void mondrian::randomise_box_color () {
finding f; find (root, win.mousex, win.mousey, f);
rect* found = f.found;
if (found) found->make_random_color ();
}
list<ball*>& mondrian::get_box_balls () { // get balls in box or all balls
finding f; find (root, win.mousex, win.mousey, f);
rect* found = f.found;
if (found) return found->balls; else return balls;
}
list<ball*>& mondrian::get_balls () { // get selected or balls in box or all balls
if (num_selected_balls) return selected_balls; else return get_box_balls ();
}
list<ball*>& mondrian::get_balls (int) { // get selected or all balls
if (num_selected_balls) return selected_balls; else return balls;
}
void mondrian::freeze_thaw_balls (list<ball*>& _balls) {
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* b = *p;
b->frozen = !b->frozen;
if (b->frozen == 0 && b->V == 0) cons << YELLOW << "Defrosted ball [" << (uintptr_t) b << "] cant move [0 speed]" << eol;
}
}
void mondrian::freeze_balls (list<ball*>& _balls) {
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* b = *p;
b->frozen = 1;
if (b->V == 0) cons << YELLOW << "Ball [" << (uintptr_t) b << "] cant move already [0 speed]" << eol;
}
}
void mondrian::thaw_balls (list<ball*>& _balls) {
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* b = *p;
b->frozen = 0;
if (b->V == 0) cons << YELLOW << "Defrosted ball [" << (uintptr_t) b << "] cant move [0 speed]" << eol;
}
}
void mondrian::clear_modulations (list<ball*>& _balls) {
if (num_selected_balls == 0) {
cons << YELLOW << "Please select some balls!" << eol;
return;
}
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* b = *p;
b->pitch_mult = 1;
b->mod = 0;
b->color_using_modulation ();
}
cons << GREEN << "Cleared modulation on " << num_selected_balls << " balls" << eol;
}
void mondrian::update_parent (rect* C) {
rect* P = C->parent;
if (P) {
rect* child1 = P->child1, *child2 = P->child2;
box<float>& el = child1->extents;
box<float>& er = child2->extents;
box<float>& e = P->extents;
box<float> e0 (e);
if (P->split == split::VERTICAL) {
if (C == child1) {
er.left = el.right;
er.bottom = el.bottom;
er.top = el.top;
er.calc ();
} else {
el.right = er.left;
el.bottom = er.bottom;
el.top = er.top;
el.calc ();
}
} else {
if (C == child1) {
er.left = el.left;
er.right = el.right;
er.bottom = el.top;
er.calc ();
} else {
el.left = er.left;
el.right = er.right;
el.top = er.bottom;
el.calc ();
}
}
e.left = el.left;
e.right = er.right;
e.bottom = el.bottom;
e.top = er.top;
e.calc ();
if (e == e0) {
update_children (P->child1);
update_children (P->child2);
} else update_parent (P);
} else {
update_children (C);
calc_visual_params ();
}
}
void mondrian::set_edge (rect* R, int e, float x, float y) {
R->extents.set_edge (e, x, y);
update_parent (R);
}
void mondrian::obj2win (const point<float>& v, float& wx, float& wy) {
wx = win_per_obj.x * v.x;
wy = win_per_obj.y * v.y;
}
void mondrian::obj2win (const float& ox, const float& oy, float& wx, float& wy) {
wx = win_per_obj.x * ox;
wy = win_per_obj.y * oy;
}
void mondrian::win2obj (const float& wx, const float& wy, float& ox, float& oy) {
ox = obj_per_win.x * wx;
oy = obj_per_win.y * wy;
}
void mondrian::load_settings () {
ifstream file ((user_data_dir + "mondrian.ed").c_str (), ios::in);
if (!file) return;
load_settings (file);
}
void mondrian::save_settings (ofstream& file) {
file << "window " << win.left << spc << win.bottom << spc << win.right << spc << win.top << endl;
file << "win_chunk " << win_chunk.x << spc << win_chunk.y << endl;
file << "obj_chunk " << obj_chunk.x << spc << obj_chunk.y << endl;
file << "win_resolution " << win_resolution << endl;
file << "label_hz_vol " << label_hz_vol << endl;
file << "min_voices " << min_voices << endl;
file << "auto_delete_rect " << auto_del_rect.active << endl;
file << "auto_delete_rect_time " << auto_del_rect.triggert << endl;
file << "auto_split_rect " << auto_split_rect.active << endl;
file << "auto_split_time " << auto_split_rect.triggert << endl;
file << "auto_split_orient " << auto_split_orient << endl;
file << "auto_split_at " << auto_split_at << endl;
file << "pick_box_split " << split_leaf << endl;
file << "pick_box_delete " << delete_leaf << endl;
file << "min_split_size " << mondrian::min_split_size << endl;
file << "draw_boxes " << draw__boxes << endl;
file << "draw_ball_pos " << draw_ball.position << endl;
file << "draw_ball_heading " << draw_ball.heading << endl;
file << "draw_notes " << draw__notes << endl;
file << "label_notes " << label_notes << endl;
file << "fill_boxes " << fill_boxes << endl;
file << "num_boxes " << num_boxes << endl;
file << "added_ball_type " << added_ball_type << endl;
file << "cursor " << cursor << endl;
file << "auto_adjust_voices " << auto_adjust_voices << endl;
file << "half_slit_size " << slit::HALF_SIZE << endl;
file << "turn_sync " << MENU.cb_turn_sync.state << endl;
file << "speed_sync " << MENU.cb_speed_sync.state << endl;
file << "texture " << MENU.texture.state << endl;
}
void mondrian::calc_win_mouse () {
if (MENU.show == 0) win.update_mouse ();
}
void mondrian::toggle_triggered_sound () {
list<ball*>& _balls = get_balls ();
int n = 0;
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* b = *p;
int& tw = b->trig_what;
tw = !tw;
cons << "Ball " << ++n;
if (tw) cons << " triggers noise"; else cons << " triggers note";
cons << eol;
}
}
void mondrian::clear_selected_targets () {
if (sel_tar == SELECT_BALLS) {
browse.clear ();
clear_selected<ball> (selected_balls, num_selected_balls);
}
else
clear_selected<slit> (selected_slits, num_selected_slits);
}
void mondrian::select_all_targets () {
if (sel_tar == SELECT_BALLS) {
browse.clear ();
select_all<ball> (balls, selected_balls, num_selected_balls);
}
else
select_all<slit> (slits, selected_slits, num_selected_slits);
}
void mondrian::invert_selected_targets () {
if (sel_tar == SELECT_BALLS) {
browse.clear ();
invert_selection<ball> (balls, selected_balls, num_selected_balls);
}
else
invert_selection<slit> (slits, selected_slits, num_selected_slits);
}
void mondrian::select_box_targets () {
if (sel_tar == SELECT_BALLS) {
select_box_balls ();
}
else
select_box_slits ();
}
void mondrian::browse_ball (int i) {
if (browse.n) {
clear_selected<ball> (selected_balls, num_selected_balls, 0);
browse.which += i;
wrap (0, browse.which, browse.last);
ball* bb = browse.balls [browse.which];
bb->select = 1;
selected_balls.push_back (bb);
++num_selected_balls;
after_selection ();
}
}
void mondrian::delete_selected_targets () {
if (sel_tar == SELECT_BALLS)
delete_selected_balls ();
else
remove_selected_slits ();
}
void mondrian::delete_all_targets () {
if (sel_tar == SELECT_BALLS)
delete_all_balls ();
else
remove_all_slits ();
}
void mondrian::update_attack () {
float x, y; attack.get_vertex (attack.last_vertex, x, y); _gotomax.set (x);
for (note_iterator i = triggered_notes.begin (), j = triggered_notes.end (); i != j; ++i) (*i).attack.update ();
}
void mondrian::update_decay () {
for (note_iterator i = triggered_notes.begin (), j = triggered_notes.end (); i != j; ++i) {
triggered_note& ti = *i;
solver& sol = ti.decay;
sol.update ();
//float y; sol.mcrv->get_vertex (sol.mcrv->last_vertex, ti.decay_lastx, y);
}
}
void mondrian::update_waveform (multi_curve& crv) {
static const string nam ("mondrian-waveform");
for (note_iterator i = triggered_notes.begin (), j = triggered_notes.end (); i != j; ++i)
(*i).update_solver (crv, nam);
}
void mondrian::draw_rect (rect* what) {
box<float>& extents = what->extents;
if (fill_boxes) {
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
int texture = MENU.texture.state;
if (texture) {
glEnable (GL_POLYGON_STIPPLE);
glPolygonStipple (patbuf);
}
glColor4f (what->r, what->g, what->b, 0.5);
glRectf (extents.left, extents.bottom, extents.right, extents.top);
glDisable (GL_BLEND);
if (texture) glDisable (GL_POLYGON_STIPPLE);
}
// draw slits
glColor3f (1, 1, 1);
if (what->total_slits == 0) { // no slits
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
glRectf (extents.left, extents.bottom, extents.right, extents.top); // outline rect
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
} else {
float startt [] = {extents.left, extents.bottom, extents.left, extents.bottom};
float endd [] = {extents.right, extents.top, extents.right, extents.top};
float levell [] = {extents.bottom, extents.right, extents.top, extents.left};
int typp [] = {slit::HORIZONTAL, slit::VERTICAL, slit::HORIZONTAL, slit::VERTICAL};
for (int i = 0; i < rect::nedges; ++i) {
if (what->nslits[i]) {
draw_slits (startt[i], endd[i], levell[i], typp[i], what->slits[i], slit_drawerr);
} else {
float li = levell[i];
float si = startt[i], ei = endd[i];
if (typp[i] == slit::HORIZONTAL) {
slit_drawerr.add (si, li);
slit_drawerr.add (ei, li);
} else {
slit_drawerr.add (li, si);
slit_drawerr.add (li, ei);
}
}
}
slit_drawerr.draw ();
}
}
void mondrian::draw_leaves () {
for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
rect* bi = *i;
draw_rect (bi);
}
}
void mondrian::draw () {
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
glOrtho (win.left, win.right, win.bottom, win.top, -1, 1);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
draw_notes ();
if (label_notes) {
tb.draw (); // note labels
glColor3f (0.9, 0.9, 1);
glVertexPointer (2, GL_FLOAT, 0, mrk);
glDrawArrays (GL_LINES, 0, n_mrk); // note markers
}
// draw cursor
box<float>& rex = root->extents;
if (inbox (rex, win.mousex, win.mousey)) {
static const float cc = 0.5f;
glColor3f (cc, cc, cc);
crsr[0]=rex.left; crsr[1]=win.mousey;
crsr[2]=rex.right; crsr[3]=win.mousey;
crsr[4]=win.mousex; crsr[5]=rex.bottom;
crsr[6]=win.mousex;crsr[7]=rex.top;
glVertexPointer (2, GL_FLOAT, 0, crsr);
glDrawArrays (GL_LINES, 0, 4);
}
if (draw_slit_cutter) {
glColor3f (1, 1, 0);
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
glRectf (win.mousex - slit::HALF_SIZE, win.mousey - slit::HALF_SIZE, win.mousex + slit::HALF_SIZE, win.mousey + slit::HALF_SIZE);
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
}
if (draw__boxes) draw_leaves ();
draw_balls ();
mon_selector.draw (rgn);
//selector.draw ();
mark_selected_slits ();
/*glPointSize (5);
glColor3f (0.0, 1.0, 1.0);
for (int i = 0, j = marks.size (); i < j; ++i) {
point<float>& pi = marks[i];
glBegin (GL_POINTS);
glVertex2f (pi.x, pi.y);
glEnd ();
}
glPointSize (1);*/
}
void mondrian::calc_visual_params () {
win.calc ();
make_notes ();
tb.refresh (this);
}
void mondrian::do_panx (int dir) {
win.panx (dir);
calc_visual_params ();
}
void mondrian::do_pany (int dir) {
win.pany (dir);
calc_visual_params ();
}
void mondrian::do_zoom (int dir) {
win.zoom (dir);
calc_visual_params ();
}
void mondrian::setup () {
dlog << "*** setting up Mondrian ***" << endl;
load_settings ();
load_boxes_and_balls ();
if (root == 0) {
root = new rect;
const int xsize = 800;
root->extents (0, 0, xsize, xsize / GOLDEN_RATIO);
root->calc_intervals ();
poly.radius = root->extents.height / 4.0f;
set_note_poly_points (24); cons.clear ();
add_leaf (root);
}
poly.delta_radius = 1;
// setup waveform
waved.add (&wave, &wavlis);
waved.attach_library (&wav_lib);
// setup attack curve
attacked.add (&attack, &attacklis);
attacklis.inst = this;
attacked.attach_library (&attack_lib);
// setup decay curve
decayed.add (&decay, &decaylis);
decaylis.inst = this;
decayed.attach_library (&decay_lib);
scaleinfo.scl = this;
dlog << "+++ Mondrian setup complete +++" << endl;
}
rect* mondrian::get_sibling (rect* R) {
rect* P = R->parent;
if (P != 0) {
if (P->child1 == R)
return P->child2;
else
return P->child1;
}
return 0;
}
void mondrian::find (rect* R, float x, float y, finding& fnd) {
if (inbox (R->extents, x, y)) {
if (R->child1 != 0) {
find (R->child1, x, y, fnd);
if (fnd.found == 0) find (R->child2, x, y, fnd);
} else {
fnd.found = R;
fnd.sibling = get_sibling (R);
}
}
}
finding mondrian::make_finding (rect* R) {
finding result;
result.found = R;
result.sibling = get_sibling (R);
return result;
}
rect* mondrian::box_under_cursor () {
finding result; find (root, win.mousex, win.mousey, result);
rect* F = result.found;
if (F == 0) {
cons << YELLOW << "Sorry cant split because you are outside all boxes!" << eol;
return 0;
} else return F;
}
int mondrian::find_hit_slit (float x, float y) {
rect* R = 0;
for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) { // slit can only be on leaves
rect* li = *i;
box<float> exts = li->extents;
exts.resize (gutter, gutter);
if (inbox(exts, x, y)) { // finding 1 box is enough
R = li;
break;
}
}
if (R) {
int e = R->extents.get_edge_hit (x, y, gutter2);
if (e != edge::NONE) {
float v[] = {x, y, x, y};
slit* hs = slit_hit (R, e, v[e]); // hit slit
if (hs) {
push_back (selected_slits, hs);
++num_selected_slits;
return 1;
}
}
}
return 0;
}
int mondrian::add_remove_slit (float x, float y, fader* fdr, float sz) {
int result = 0;
int boxes_hit = 0;
int k = 0;
// slit can only be on boxes that are leaves
for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
rect* li = *i;
box<float> lie = li->extents;
lie.resize (gutter, gutter);
if (inbox(lie, x, y)) {
if (++boxes_hit > 2) break; // too many!
bxs[k++] = li;
}
}
if (boxes_hit == 2) { // slit can only be on 2 leaves
slit* hs = slit_hit (bxs, x, y); // did we hit a slit?
if (hs == 0) { // no slit hit
slit* s = new slit (bxs, x, y, sz, fdr); // so make slit
rect** boxes = s->boxes;
int* edges = s->edges;
if (edge_on_root (root, boxes, edges) == 0) { // balls wont escape
// add slit to edges of the 2 leaves
int result0 = boxes[0]->add_slit (s, edges[0]);
int result1 = boxes[1]->add_slit (s, edges[1]);
if (result0 && result1) { // added successfully
++num_slits;
slits.push_back (s); // add to global slits
} else {
// not added so discard
remove_slit (s);
}
} else {
// balls will escape playing area so discard
remove_slit (s);
}
} else {
remove_slit (hs);
}
result = 1;
}
return result;
}
rect* mondrian::split_rect (int type, float t, rect* B) {
if (B == 0) {
B = box_under_cursor ();
if (B == 0) return 0;
}
stop_editing_slit ();
stop_editing_edge ();
box<float>& extents = B->extents;
B->split = type;
rect* v1 = new rect;
rect* v2 = new rect;
box<float>& v1e = v1->extents;
box<float>& v2e = v2->extents;
if (type == split::VERTICAL) {
v1e (extents.left, extents.bottom, t, extents.top);
v2e (t, extents.bottom, extents.right, extents.top);
} else {
v1e (extents.left, extents.bottom, extents.right, t);
v2e (extents.left, t, extents.right, extents.top);
}
v1->parent = B;
v2->parent = B;
B->child1 = v1;
B->child2 = v2;
v1->calc_intervals ();
v2->calc_intervals ();
split_balls (B, v1, v2);
remove_leaf (B);
add_leaf (v1);
add_leaf (v2);
recreate_slits (B);
return B;
}
void mondrian::recreate_slits (rect* R) {
box<float>& e = R->extents;
float fixed_val [rect::nedges] = {e.bottom, e.right, e.top, e.left};
list<slit_info> reslits;
for (int i = 0; i < rect::nedges; ++i) {
list<slit*>& slits = R->slits[i];
int ns = R->nslits [i];
if (ns) {
slit_info sf;
float* moving [rect::nedges] = {&sf.x, &sf.y, &sf.x, &sf.y};
float* fixed [rect::nedges] = {&sf.y, &sf.x, &sf.y, &sf.x};
float* movingi = moving [i];
float* fixedi = fixed [i];
*fixedi = fixed_val [i];
for (slit_iterator si = slits.begin (), sj = slits.end (); si != sj;) {
slit* s = *si;
if (s->fdr) {
s->start = s->anim_start;
s->end = s->anim_end;
s->calc_mid ();
sf.anim = 1;
sf.fdr.copy (s->fdr);
} else sf.anim = 0;
*movingi = s->mid;
sf.half_size = s->mid - s->start;
reslits.push_back (sf);
remove_slit (s);
si = slits.begin ();
sj = slits.end ();
}
}
}
for (list<slit_info>::iterator i = reslits.begin (), j = reslits.end(); i != j; ++i) {
slit_info& si = *i;
if (si.anim)
add_remove_slit (si.x, si.y, &si.fdr, si.half_size);
else
add_remove_slit (si.x, si.y, 0, si.half_size);
}
}
void mondrian::add_leaf (rect* L) {
leaves.push_back (L);
++nleaves;
}
void mondrian::remove_leaf (rect* L) {
erase (leaves, L);
--nleaves;
}
rect* mondrian::earliest_leaf () {
return *(leaves.begin());
}
rect* mondrian::latest_leaf () {
return *(--leaves.end());
}
rect* mondrian::balled_leaf () {
list<ball*>& balls = get_balls (0);
int sz = balls.size ();
if (sz) {
rnd<float> rd (0, balls.size()-1);
int r = rd() + 0.5;
ball* b = get <list<ball*>, ball*> (balls, r);
return b->R;
} else return rnd_leaf();
}
rect* mondrian::rnd_leaf () {
rnd<float> rd (0, nleaves-1);
return get< list<rect*>, rect*> (leaves, int(rd()+0.5));
}
rect* mondrian::biggest_leaf () {
rect* mal = earliest_leaf ();
float max_area = mal->get_area ();
for (box_iterator i = ++leaves.begin (), j = leaves.end (); i != j; ++i) {
rect* li = *i;
float li_area = li->get_area ();
if (li_area > max_area) {
max_area = li_area;
mal = li;
}
}
return mal;
}
int mondrian::delete_rect (const finding& f) { // delete found box
rect* found = f.found;
rect* sibling = f.sibling;
// turn sibling into parent, kill parent
rect* parent = found->parent;
rect* grand_parent = parent->parent;
if (grand_parent) {
if (grand_parent->child1 == parent)
grand_parent->child1 = sibling;
else
grand_parent->child2 = sibling;
} else root = sibling;
sibling->extents = parent->extents;
sibling->parent = grand_parent;
update_children (sibling);
finding fnd;
for (balls_iterator p = found->balls.begin (), q = found->balls.end (); p != q; ++p) { // find new home for balls of found
ball* b = *p;
find (sibling, b->x, b->y, fnd);
b->R = fnd.found;
if (b->R == 0) b->R = sibling;
b->R->balls.push_back (b);
fnd.clear ();
}
delete parent;
remove_leaf (found);
recreate_slits (found);
delete found;
return 1;
}
void mondrian::delete_current_rect () { // delete box under cursor
finding f; find (root, win.mousex, win.mousey, f);
if (bad_rect (f.found)) return;
delete_rect (f);
}
void mondrian::delete_children (rect* _root) {
rect* left = _root->child1;
rect* right = _root->child2;
if (left) {
delete_children (left);
delete_children (right);
}
delete _root;
}
void mondrian::update_children (rect* R) {
box<float>& extents = R->extents;
R->calc_intervals ();
if (R->child1 && R->child2) {
box<float>& c1e = R->child1->extents;
if (R->split == split::HORIZONTAL) {
R->child1->extents (extents.left, extents.bottom, extents.right, c1e.top);
R->child2->extents (extents.left, c1e.top, extents.right, extents.top);
} else if (R->split == split::VERTICAL) {
R->child1->extents (extents.left, extents.bottom, c1e.right, extents.top);
R->child2->extents (c1e.right, extents.bottom, extents.right, extents.top);
}
update_children (R->child1);
update_children (R->child2);
} else {
if (edge != edge::NONE) {
if (edge == edge::LEFT || edge == edge::RIGHT) {
R->update_slits (edge::BOTTOM, R->extents.left, R->extents.right);
R->update_slits (edge::TOP, R->extents.left, R->extents.right);
} else {
R->update_slits (edge::LEFT, R->extents.bottom, R->extents.top);
R->update_slits (edge::RIGHT, R->extents.bottom, R->extents.top);
}
}
}
}
rect* mondrian::pick_leaf (int& what) {
rect* asr = 0;
if (what == rect::RANDOM)
asr = rnd_leaf ();
else if (what == rect::BIGGEST)
asr = biggest_leaf ();
else if (what == rect::LATEST)
asr = latest_leaf ();
else if (what == rect::EARLIEST)
asr = earliest_leaf ();
else {
asr = balled_leaf ();
}
return asr;
}
void mondrian::bg () {
remove_finished_notes ();
if (quit) return;
if (splitting_rects) {
for (split_iterator m = lsd.begin (), n = lsd.end (); m != n;) {
if (multi_split_rect (*m)) {
m = lsd.erase (m);
n = lsd.end ();
splitting_rects = !(m == n);
} else ++m;
}
} else {
if (delete_all_rects) {
if (nleaves > 1) {
rect* pl = pick_leaf (delete_leaf);
int good_rect = !bad_rect (pl);
if (good_rect) delete_rect ( make_finding (pl) );
} else {
delete_all_rects = 0;
}
} else {
if (auto_del_rect.active ) {
if (auto_del_rect (ui_clk())) {
if (!editing_edge && !editing_slit) {
rect* adr = pick_leaf (delete_leaf);
if (!bad_rect (adr)) delete_rect (make_finding (adr));
}
}
}
}
if (auto_split_rect.active) { // auto split box?
if (auto_split_rect (ui_clk())) { // reached time to auto split
// pick box to split
rect* asr = pick_leaf (split_leaf);
int split_orient;
if (auto_split_orient == split::BOTH) {
rnd<float> rd (split::HORIZONTAL, split::VERTICAL); // horizontal or vertical at random
split_orient = rd () + 0.5;
} else
split_orient = auto_split_orient;
if (auto_split_at == split::NOTES) { // split at notes of the scale
pair<int, int> note_ids;
if (get_note_ids (note_ids, asr, split_orient)) {
rnd<float> rd (note_ids.first, note_ids.second - 1);
int i = rd() + 0.5;
split_data sd (i, i + 1, auto_split_at, split_orient, asr);
lsd.push_back (sd); // split in bg!
splitting_rects = 1;
}
} else { // split at any microtone
float delta;
float low, high;
if (split_orient == split::HORIZONTAL) {
low = asr->extents.bottom;
high = asr->extents.top;
delta = asr->extents.height;
} else {
low = asr->extents.left;
high = asr->extents.right;
delta = asr->extents.width;
}
float _2_min_split_size = 2 * min_split_size;
if (delta > _2_min_split_size) {
rnd<float> rd (min_split_size, max (min_split_size, delta - min_split_size));
float sz = rd ();
int cl = 0;
float pos = low + sz;
cl = clamp (low, pos, high);
if (!cl) {
split_data sd (0, 1, auto_split_at, split_orient, asr);
sd.sz = sz;
lsd.push_back (sd); // split in bg!
splitting_rects = 1;
}
}
}
}
}
}
for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i) (*i)->eval_anim ();
for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
ball* b = *p;
b->eval_ops ();
b->update ();
}
}
int mondrian::render_audio (float* L, float* R) {
int ret = 0;
balls_iterator bter = triggering_balls.begin ();
for (note_iterator i = triggered_notes.begin (), j = triggered_notes.end (); i != j; ++i, ++bter) {
triggered_note& ti = *i;
ball* bi = *bter;
if (ti.state != triggered_note::FINISHED) {
ti.eval (L, R, aout.result, aout.vol, aout.samples_per_channel, bi->attack_time, bi->decay_time, _gotomax);
ti.eval (bi->attack_time);
ret += ti.player.mixer.active;
}
}
return ret;
}
void mondrian::remove_finished_notes () {
balls_iterator bter = triggering_balls.begin ();
for (note_iterator i = triggered_notes.begin (); i != triggered_notes.end();) {
triggered_note& ti = *i;
if (ti.state == triggered_note::FINISHED) {
i = triggered_notes.erase (i);
ball* b = *bter; if (--b->num_notes <= 0 && b->del) delete b;
bter = triggering_balls.erase (bter);
--num_triggered_notes;
print_num_triggered_notes ();
} else {
++i;
++bter;
}
}
}
ball* mondrian::get_one_selected_ball () {
if (num_selected_balls == 1) return *selected_balls.begin (); else return 0;
}
int mondrian::change_param (float& param, float value, float& recent) {
static const float minval = 0.0f;
int atminval = (param == minval);
param += value;
int ret = 1;
if (param < minval) {
param = minval;
if (atminval) ret = 0;
}
recent = param;
return ret;
}
void mondrian::change_attack_time_kb (spinner<float>& s) {
int n = 0;
list<ball*>& _balls = get_balls ();
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* b = *p;
change_param (b->attack_time, s(), ball::recent_attack_time);
sprintf (BUFFER, "Ball %d, attack time = %0.3f secs", ++n, b->attack_time);
cons << BUFFER << eol;
}
}
void mondrian::change_decay_time_kb (spinner<float>& s) {
list<ball*>& _balls = get_balls ();
int n = 0;
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* b = *p;
change_param (b->decay_time, s(), ball::recent_decay_time);
sprintf (BUFFER, "Ball %d, decay time = %0.3f secs", ++n, b->decay_time);
cons << BUFFER << eol;
}
}
void mondrian::draw_balls () {
int num_balls_ = num_balls;
if (new_ball) ++num_balls_;
if (num_balls_ == 0) return;
if (num_balls_ > n_pts) {
if (pts) delete[] pts;
if (pts_d) delete[] pts_d;
if (pts_clr) delete[] pts_clr;
pts = new float [2 * num_balls_];
pts_d = new float [4 * num_balls_];
pts_clr = new float [3 * num_balls_];
n_pts = num_balls_;
}
int i = 0, j = 0, k = 0;
float r, g, cb;
static const int S = 25;
for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
ball* b = *p;
if (b->select) {r = 0; g = 1; cb = 0;} else {r = b->r; g = b->g; cb = b->b;}
if (draw_ball.trails) {
glColor3f (r, g, cb);
b->trail.draw ();
}
// position
pts[i++] = b->x;
pts[i++] = b->y;
// color
pts_clr[j++]=r;
pts_clr[j++]=g;
pts_clr[j++]=cb;
// direction
pts_d[k++]=b->x+S*b->vx;
pts_d[k++]=b->y+S*b->vy;
pts_d[k++]=b->x;
pts_d[k++]=b->y;
}
if (new_ball) {
pts[i++]=new_ball->x;
pts[i++]=new_ball->y;
pts_clr[j++]=new_ball->r;
pts_clr[j++]=new_ball->g;
pts_clr[j++]=new_ball->b;
}
if (draw_ball.heading) {
glColor3f (1, 1, 1);
glVertexPointer (2, GL_FLOAT, 0, pts_d);
glDrawArrays (GL_LINES, 0, 2 * num_balls);
}
if (draw_ball.position) {
glPointSize (5);
glEnableClientState (GL_COLOR_ARRAY);
glColorPointer (3, GL_FLOAT, 0, pts_clr);
glVertexPointer (2, GL_FLOAT, 0, pts);
glDrawArrays (GL_POINTS, 0, num_balls_);
glDisableClientState (GL_COLOR_ARRAY);
glPointSize (1);
}
}
void mondrian::draw_notes () {
int n = poly.points;
float xy [2*n];
if (label_hz_vol) tb_hz_vol.clear ();
for (note_iterator i = triggered_notes.begin (), j = triggered_notes.end (); i != j; ++i) {
triggered_note& ti = *i;
float r = poly.radius * ti.volume.now / ti.volume.max;
if (draw__notes) {
for (int i = 0, j = 0; i < n; ++i) {
xy[j++]=ti.x+r*poly.coss[i];
xy[j++]=ti.y+r*poly.sinn[i];
}
if (scope.limit) glColor3f (1.0, 0.1, 0.1); else glColor3f (ti.r, ti.g, ti.b);
glVertexPointer (2, GL_FLOAT, 0, xy);
glDrawArrays (GL_LINE_LOOP, 0, n);
}
if (label_hz_vol) {
xy[0]=ti.x+r*poly.coss[0];
xy[1]=ti.y+r*poly.sinn[0];
float wtx = xy[0] + gutter, wty = xy[1] + gutter;
float clr = ti.volume.now * 1.0 / ti.volume.max;
sprintf (BUFFER, "%0.3f @ %03d%%", ti.start_hz, int(clr * 100.0 + 0.5));
tb_hz_vol.add (text (BUFFER, wtx, wty, ti.r, ti.g, ti.b));
}
}
if (label_hz_vol) {
tb_hz_vol.refresh (this);
tb_hz_vol.draw ();
}
}
void mondrian::split_balls (rect* P, rect* C1, rect* C2) {
list<ball*>& P_balls = P->balls;
for (balls_iterator p = P_balls.begin (); p != P_balls.end ();) {
ball* b = *p;
if (inbox (C1->extents, b->x, b->y)) {
C1->balls.push_back (b);
b->R = C1;
} else {
C2->balls.push_back (b);
b->R = C2;
}
p = P_balls.erase (p);
}
}
void mondrian::change_speed (spinner<float>& s, float d) {
list<ball*>& _balls = get_balls ();
int n = 0;
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* b = *p;
float& bv = b->V;
bv += s(d);
if (bv <= 0) bv = 0.0f;
/*float& maxx = b->op_speed.max;
float omaxx = maxx;
maxx = max (maxx, bv);
if ((num_selected_balls == 1) && (maxx != omaxx)) MENU.sp_max_speed.set_value (maxx);*/
sprintf (BUFFER, "Ball %d, speed = %0.3f", ++n, bv);
cons << YELLOW << BUFFER << eol;
}
}
void mondrian::toggle_slit_anim () {
int hs = 0;
if (num_selected_slits == 0) hs = find_hit_slit (win.mousex, win.mousey);
for (slit_iterator s = selected_slits.begin (), t = selected_slits.end (); s != t; ++s) {
slit* si = *s;
si->toggle_anim ();
}
if (hs) clear_selected<slit> (selected_slits, num_selected_slits, 0);
}
void mondrian::select_balls (const box<float>& rgn) {
browse.clear ();
if (SHIFT || CTRL) ; else clear_selected<ball> (selected_balls, num_selected_balls);
for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
ball* b = *p;
if (inbox (rgn, b->x, b->y)) select_using_modifiers<ball> (b, CTRL, selected_balls, num_selected_balls);
}
after_selection ();
}
void mondrian::after_selection () {
if (sel_tar == SELECT_BALLS) {
if (num_selected_balls) {
cons << GREEN << "Selected " << num_selected_balls << " balls" << eol;
if (num_selected_balls == 1) {
ball* bsel = *selected_balls.begin();
bsel->print ();
MENU.set_ball_ops (bsel);
if (browse.n) {
sprintf (BUFFER, " Ball %d of %d", browse.which+1, browse.n);
MENU.ol_browse_balls.set_text (BUFFER);
}
} else {
if (browse.n == 0) {
browse.balls.resize (num_selected_balls);
copy (selected_balls.begin (), selected_balls.end (), browse.balls.begin ());
browse.n = browse.balls.size ();
browse.last = browse.n - 1;
browse.which = -1;
}
MENU.ol_browse_balls.set_text (" Click arrow to load ball");
}
} else {
cons << RED << "No balls selected" << eol;
MENU.clear_ball_ops ();
}
} else {
if (num_selected_slits)
cons << GREEN << "Selected " << num_selected_slits << " slits" << eol;
else
cons << RED << "No slits selected" << eol;
}
}
void mondrian::select_box_balls () {
finding f; find (root, win.mousex, win.mousey, f);
rect* R = f.found;
if (R) {
list<ball*>& b_balls = R->balls;
browse.clear ();
if (SHIFT || CTRL); else clear_selected<ball> (selected_balls, num_selected_balls);
for (balls_iterator p = b_balls.begin (), q = b_balls.end (); p != q; ++p) select_using_modifiers<ball> (*p, CTRL, selected_balls, num_selected_balls);
after_selection ();
}
}
void mondrian::select_box_slits () {
finding f; find (root, win.mousex, win.mousey, f);
rect* R = f.found;
if (R) {
if (SHIFT || CTRL); else clear_selected<slit> (selected_slits, num_selected_slits);
for (int i = 0; i < 4; ++i) {
list<slit*>& slits = R->slits[i];
for (slit_iterator s = slits.begin (), t = slits.end (); s != t; ++s) select_using_modifiers<slit> (*s, CTRL, selected_slits, num_selected_slits);
}
after_selection ();
}
}
void mondrian::delete_ball (ball* b) {
if (::erase (balls, b)) --num_balls;
if (::erase (selected_balls, b)) --num_selected_balls;
if (::erase (browse.balls, b)) {
--browse.n;
if (browse.n) {
browse.last = browse.n - 1;
MENU.ol_browse_balls.set_text (" Click arrow to load ball");
} else {
browse.last = 0;
}
}
b->R->erase (b);
if (b->num_notes == 0) delete b; else b->del = 1;
}
void mondrian::delete_selected_balls () {
int nb = num_balls;
while (num_selected_balls) delete_ball (*selected_balls.begin());
cons << YELLOW << "Deleted " << (nb - num_balls) << " balls" << eol;
}
void mondrian::locate_ball (ball* b) {
if (!inbox (b->R->extents, b->x, b->y)) {
locator:
finding fnd; find (root, b->x, b->y, fnd);
rect* nbr = fnd.found;
if (nbr) {
b->R->erase (b);
b->R = nbr;
b->R->balls.push_back (b);
} else {
clamp<float>(root->extents.left, b->x, root->extents.right);
clamp<float>(root->extents.bottom, b->y, root->extents.top);
goto locator;
}
}
}
void mondrian::move_balls (float dx, float dy) {
for (balls_iterator p = selected_balls.begin (), q = selected_balls.end (); p != q; ++p) {
ball* b = *p;
b->x += dx;
b->y += dy;
locate_ball (b);
}
}
void mondrian::toggle_flag (int& flag, const string& poz, const string& neg) {
flag = !flag;
if (flag)
cons.set_cmd_line (poz, GREEN);
else {
cons.set_cmd_line ("");
cons << YELLOW << neg << eol;
}
}
void mondrian::do_add_balls (int type) {
adding_balls = 1;
added_ball_type = type;
if (new_ball) new_ball->set_type (added_ball_type);
string bt (ball::types_str[added_ball_type]);
stringstream ss; ss << "Click, drag and release to launch " << bt << ". ESC to stop.";
cons.set_cmd_line (ss.str(), GREEN);
MENU.ol_ball_types.set_text (bt);
}
void mondrian::do_move_balls () {
if (stop_moving_balls()) return;
if (num_selected_balls) {
stop_adding_balls ();
toggle_flag (moving_balls, "Just move your mouse to move the balls. ESC to stop.");
}
else
cons << YELLOW << "Please select some balls!" << eol;
}
void mondrian::delete_all_balls () {
select_all<ball> (balls, selected_balls, num_selected_balls);
delete_selected_balls ();
}
void mondrian::make_notes () {
vector<string> notes;
extern int NOTATION;
extern int NUM_INTERVALS;
extern const char* WESTERN_FLAT [];
extern vector<string> INTERVAL_NAMES;
extern vector<float> INTERVAL_VALUES;
extern map<string,string> INT2IND;
int western = scaleinfo.western;
if (NOTATION == WESTERN) {
for (int i = 0; i < NUM_INTERVALS; ++i) notes.push_back (WESTERN_FLAT[(western + i) % 12]);
} else if (NOTATION == NUMERIC) {
for (int i = 0; i < NUM_INTERVALS; ++i) notes.push_back (INTERVAL_NAMES[i]);
} else {
for (int i = 0; i < NUM_INTERVALS; ++i) notes.push_back (INT2IND[INTERVAL_NAMES[i]]);
}
int lh = line_height, lh1 = 2 * lh, mcw = fnt.charwidth.max;
box<float>& br = root->extents;
float x = br.left, y1 = br.bottom - lh1, y2 = br.top + lh;
int ks = 10, ksx = ks + 7;
float y = br.bottom, x1 = br.left - ksx - 2 * mcw, x2 = br.right + ks + mcw;
static const int nsides = 4, verts_per_mark = 2;
int _n_mrk = NUM_INTERVALS * nsides * verts_per_mark;
if (_n_mrk > n_mrk) {
if (mrk) delete[] mrk;
n_mrk = _n_mrk;
mrk = new float [2 * n_mrk];
}
int k = 0;
tb.clear ();
for (int i = 0; i < NUM_INTERVALS; ++i) {
float iv = INTERVAL_VALUES[i] - 1;
float xn = x + iv * br.width;
float yn = y + iv * br.height;
const string& ni = notes[i];
mrk[k++]=xn;
mrk[k++]=br.bottom;
mrk[k++]=xn;
mrk[k++]=br.bottom-ks;
mrk[k++]=br.right;
mrk[k++]=yn;
mrk[k++]=br.right+ks;
mrk[k++]=yn;
mrk[k++]=xn;
mrk[k++]=br.top;
mrk[k++]=xn;
mrk[k++]=br.top+ks;
mrk[k++]=br.left;
mrk[k++]=yn;
mrk[k++]=br.left-ks;
mrk[k++]=yn;
tb.add (text (ni, xn, y1));
yn -= fnt.lift;
tb.add (text (ni, x1, yn));
tb.add (text (ni, x2, yn));
tb.add (text (ni, xn, y1));
tb.add (text (ni, xn, y2));
}
}
void mondrian::save_boxes (ofstream& f, rect* R, string& id) {
R->id = id;
rect* child1 = R->child1;
rect* child2 = R->child2;
box<float>& e = R->extents;
rect* P = R->parent;
string c1 ("-"), c2 ("-");
if (child1 != 0) {
string L (id + 'L'), R(id + 'R');
save_boxes (f, child1, L);
save_boxes (f, child2, R);
c1 = child1->id;
c2 = child2->id;
}
f << "box ";
if (P) f << P->id; else f << "0";
f << spc << R->id << spc << e.left << spc << e.bottom << spc << e.right << spc << e.top << spc << R->split << spc << R->r << spc << R->g << spc << R->b << spc << c1 << spc << c2 << spc;
int leaf = R->is_leaf ();
f << " leaf " << leaf << spc;
if (leaf) { // only leaf can have slits
int total_slits = R->total_slits;
f << "slits " << total_slits << spc;
// box can be leaf and not have any slits
if (total_slits) { // this leaf has slits
for (int i = 0; i < rect::nedges; ++i) {
int& nse = R->nslits[i];
f << nse << spc;
// not all of the leaf's edges need have slits
if (nse) { // this edge has slits
list<slit*>& si = R->slits[i];
for (slit_iterator p = si.begin (), q = si.end (); p != q; ++p) {
f << get_index_of_slit (*p) << spc;
}
}
}
}
}
f << endl;
}
box_from_disk_t mondrian::get_box_from_disk (list<box_from_disk_t>& boxes, const string& id) {
for (box_from_disk_iterator p = boxes.begin (), q = boxes.end (); p != q; ++p) {
box_from_disk_t& rp = *p;
if (rp.R->id == id) {
return rp;
}
}
return box_from_disk_t ();
}
void mondrian::load_boxes_and_balls () {
list<box_from_disk_t> boxes;
box_from_disk_t box_from_disk;
string fname ("mondrian.data");
ifstream f ((user_data_dir + fname).c_str (), ios::in);
if (!f) {
dlog << "!!! Failed loading boxes & balls from file: " << fname << " !!!" << endl;
return;
}
while (!f.eof()) {
string what, ignore;
f >> what;
if (what == "box") { // load box
rect *R = new rect;
box_from_disk.R = R;
f >> box_from_disk.parent;
f >> R->id;
float l, b, r, t;
f >> l >> b >> r >> t;
R->extents (l, b, r, t);
f >> R->split;
f >> R->r >> R->g >> R->b;
f >> box_from_disk.child1 >> box_from_disk.child2;
int is_leaf; f >> ignore >> is_leaf;
if (is_leaf) {
add_leaf (R);
f >> ignore >> R->total_slits;
if (R->total_slits) { // can have slits
for (int i = 0; i < rect::nedges; ++i) {
list<slit*>& ls = R->slits [i]; // slits on edge
int& nse = R->nslits [i]; // number of slits on edge
f >> nse;
if (nse) {
for (int p = 0; p < nse; ++p) {
int ix; f >> ix; // get slit index
ls.push_back ((slit *) ix); // will resolve to slit* after slits are loaded
}
}
}
}
}
boxes.push_back (box_from_disk);
if (box_from_disk.parent == "0") root = R;
} else if (what == "ball") {
ball* b = new ball;
f >> b->x >> b->y >> b->V >> b->vx >> b->vy; b->set_velocity (b->V * b->vx, b->V * b->vy);
string rid; f >> rid >> b->frozen >> b->pitch_mult >> b->mod >> b->r >> b->g >> b->b >> b->attack_time >> b->decay_time;
int t; f >> t; b->trail.total = t; trail_t::alloc (t);
f >> b->auto_rotate >> b->dtheta >> b->type >> b->vol_mult >> b->trig_what;
float minn, maxx;
f >> b->op_turn.alarm.active >> b->op_turn.alarm.triggert;
f >> minn >> maxx; b->op_turn.rd.set (minn, maxx);
f >> b->op_turn.vx >> b->op_turn.vy >> b->op_turn.angle;
float turn_alpha; f >> turn_alpha;
f >> b->op_speed.alarm.active >> b->op_speed.alarm.triggert;
f >> minn >> maxx; b->op_speed.rd.set (minn, maxx);
f >> b->op_speed.max;
f >> b->op_speed.start >> b->op_speed.delta;
float speed_alpha; f >> speed_alpha;
f >> b->op_teleport.alarm.active >> b->op_teleport.alarm.triggert;
f >> b->op_teleport.radius;
f >> b->op_clone.alarm.active >> b->op_clone.alarm.triggert;
f >> b->op_clone.n >> b->op_clone.max >> b->op_clone.offset >> b->op_clone.clone_can_clone;
f >> b->op_transform.alarm.active >> b->op_transform.alarm.triggert;
ball_op* bops [] = {&b->op_turn, &b->op_speed, &b->op_teleport, &b->op_clone, &b->op_transform};
for (int i = 0; i < ball_op::NUM_OPS; ++i) {
ball_op* bopi = bops[i];
if (bopi->alarm.active) bopi->alarm.start ();
}
b->op_turn.alarm.startt += (turn_alpha * b->op_turn.alarm.triggert);
b->op_speed.alarm.startt += (speed_alpha * b->op_speed.alarm.triggert);
rect* R = get_box_from_disk (boxes, rid).R;
b->R = R;
R->balls.push_back (b);
balls.push_back (b);
++num_balls;
} else if (what == "rules") {
for (int i = 0; i < 3; ++i) f >> Transform::rules[i];
} else if (what == "poly") {
f >> ignore >> poly.radius >> ignore >> poly.points;
set_note_poly_points (poly.points); cons.clear ();
} else if (what == "slits") {
load_slits (f, boxes);
} else if (what == "max_balls") {
f >> Clone::max_balls;
} else break;
}
for (box_from_disk_iterator i = boxes.begin (), j = boxes.end (); i != j; ++i) {
box_from_disk_t& ri = *i;
rect* R = ri.R;
rect* P = get_box_from_disk (boxes, ri.parent).R;
rect* C1 = get_box_from_disk (boxes, ri.child1).R;
rect* C2 = get_box_from_disk (boxes, ri.child2).R;
R->parent = P;
R->child1 = C1;
R->child2 = C2;
R->calc_intervals ();
}
for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
rect* R = *i;
if (R->total_slits) {
for (int p = 0; p < rect::nedges; ++p) {
list<slit*>& ls = R->slits[p];
for (slit_iterator r = ls.begin (), s = ls.end (); r != s; ++r) {
int ix = (uintptr_t) *r;
*r = get_slit_from_index (ix);
}
}
}
}
}
void mondrian::save_balls (ofstream& f) {
f << "max_balls " << Clone::max_balls << endl;
for (balls_iterator i = balls.begin (), j = balls.end (); i != j; ++i) {
ball* pb = *i;
ball& b = *pb;
f << "ball " << b.x << spc << b.y << spc << b.V << spc << b.vx << spc << b.vy << spc << b.R->id << spc << b.frozen << spc << b.pitch_mult << spc << b.mod << spc << b.r << spc << b.g << spc << b.b << spc << b.attack_time << spc << b.decay_time << spc << b.trail.total << spc << b.auto_rotate << spc << b.dtheta << spc << b.type << spc << b.vol_mult << spc << b.trig_what << spc << b.op_turn.alarm.active << spc << b.op_turn.alarm.triggert << spc << b.op_turn.rd.min << spc << b.op_turn.rd.max << spc << b.op_turn.vx << spc << b.op_turn.vy << spc << b.op_turn.angle << spc << b.op_turn.alarm () << spc << b.op_speed.alarm.active << spc << b.op_speed.alarm.triggert << spc << b.op_speed.rd.min << spc << b.op_speed.rd.max << spc << b.op_speed.max << spc << b.op_speed.start << spc << b.op_speed.delta << spc << b.op_speed.alarm () << spc << b.op_teleport.alarm.active << spc << b.op_teleport.alarm.triggert << spc << b.op_teleport.radius << spc << b.op_clone.alarm.active << spc << b.op_clone.alarm.triggert << spc << b.op_clone.n << spc << b.op_clone.max << spc << b.op_clone.offset << spc << b.op_clone.clone_can_clone << spc << b.op_transform.alarm.active << spc << b.op_transform.alarm.triggert << endl;
}
f << "rules ";
for (int i = 0; i < 3; ++i) f << Transform::rules[i] << spc;
f << endl;
}
void mondrian::save_slits (ofstream& f) {
f << "slits " << num_slits << endl;
for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i) {
slit* si = *i;
for (int i = 0; i < 2; ++i) {
rect* ri = si->boxes[i];
int ei = si->edges[i];
f << ri->id << spc << ei << spc; // nb: call after save_boxes for proper rect id
}
int _fdr = 0;
if (si->fdr) {
si->start = si->anim_start;
si->end = si->anim_end;
_fdr = 1;
}
f << si->type << spc << si->start << spc << si->end << spc << si->mid << spc;
if (_fdr) {
f << _fdr << spc;
si->fdr->save (f);
} else {
f << _fdr;
}
f << endl;
}
for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i) delete *i; // free mem of all slits
}
void mondrian::load_slits (ifstream& f, list<box_from_disk_t>& boxes) {
f >> num_slits;
string r0, r1;
for (int i = 0; i < num_slits; ++i) {
slit* s = new slit;
f >> r0 >> s->edges[0] >> r1 >> s->edges[1];
s->boxes[0] = get_box_from_disk (boxes, r0).R;
s->boxes[1] = get_box_from_disk (boxes, r1).R;
f >> s->type >> s->start >> s->end >> s->mid;
int fdr_exists; f >> fdr_exists;
if (fdr_exists) {
s->toggle_anim ();
s->fdr->load (f);
}
slits.push_back (s);
}
}
void mondrian::toggle_balls_type (int T) {
static const char* names [] = {" bouncers ", " wreckers ", " healers "};
if (num_selected_balls) {
list<ball*>& _balls = get_balls ();
int t = 0, b = 0;
int Tt;
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* pb = *p;
if (pb->type == T) {
Tt = ball::BOUNCER;
++b;
}
else {
Tt = T;
++t;
}
pb->set_type (Tt);
}
cons << GREEN << "Made " << t << names[T] << "and " << b << names[0] << eol;
} else cons << YELLOW << "Please select some balls!" << eol;
}
int mondrian::modulate_balls (int w) {
if (num_selected_balls) {
list<ball*>& _balls = get_balls ();
int b = 0;
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* pb = *p;
pb->pitch_mult *= pow (octave_shift.sol.lasty, w);
ball::recent_pitch_mult = pb->pitch_mult;
sprintf (BUFFER, "Ball %d, pitch_multiplier = %0.3f", ++b, pb->pitch_mult);
cons << BUFFER << eol;
pb->mod += w;
pb->color_using_modulation ();
}
}
return num_selected_balls;
}
/*void mondrian::rotate_velocity (spinner<int>& s) {
list<ball*>& _balls = get_balls ();
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* pb = *p;
pb->rotate_velocity (dir);
}
}*/
void mondrian::rotate_velocity (int dir) {
list<ball*>& _balls = get_balls ();
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* pb = *p;
pb->rotate_velocity (dir);
}
}
void mondrian::change_ball_dtheta (int dir) {
int n = 0;
list<ball*>& _balls = get_balls ();
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* pb = *p;
pb->dtheta += (dir * PI_BY_180);
if (pb->dtheta <= 0) pb->dtheta = 0;
sprintf (BUFFER, "Ball %d, delta rotate velocity = %0.3f", ++n, pb->dtheta);
cons << BUFFER << eol;
}
}
void mondrian::change_trail_size (spinner<int>& s) {
int n = 0;
list<ball*>& _balls = get_balls ();
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* pb = *p;
pb->trail.change (s());
cons << YELLOW << "Ball " << ++n << ", trail points = " << pb->trail.total << eol;
}
}
int mondrian::set_note_poly_points (int p) {
int ret = 1;
poly.points = p;
if (poly.points < 2) {
poly.points = 2;
ret = 0;
}
poly.coss.resize (poly.points);
poly.sinn.resize (poly.points);
int n = poly.points;
extern const float TWO_PI;
float dtheta = TWO_PI / n;
float theta = TWO_PI / 4.0f;
for (int i = 0; i < n; ++i) {
poly.coss[i] = cos(theta);
poly.sinn[i] = sin(theta);
theta += dtheta;
}
cons << YELLOW << "Note Polygon points = " << poly.points << eol;
return ret;
}
int mondrian::set_note_poly_radius (float r) {
int ret = 1;
poly.radius = r;
if (poly.radius < 0) {
poly.radius = 0;
ret = 0;
}
sprintf (BUFFER, "Note polygon radius = %0.3f", poly.radius);
cons << YELLOW << BUFFER << eol;
return ret;
}
void mondrian::flip_velocity () {
list<ball*>& _balls = get_balls ();
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* pb = *p;
pb->vx = -pb->vx;
pb->vy = -pb->vy;
pb->calc_velocity_slope ();
if (pb->auto_rotate) {
pb->auto_rotate = -pb->auto_rotate;
rotate_velocity (pb->auto_rotate);
}
}
}
void mondrian::set_auto_rotate (int ar) {
list<ball*>& _balls = get_balls ();
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) (*p)->auto_rotate = ar;
}
void mondrian::toggle_auto_rotate (int ar) {
list<ball*>& _balls = get_balls ();
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* pb = *p;
if (pb->auto_rotate) pb->auto_rotate = 0; else pb->auto_rotate = ar;
}
}
void mondrian::tonic_changed () {calc_visual_params ();}
void mondrian::scale_loaded () {}
void mondrian::scale_changed () {}
void mondrian::remove_slit (slit* s) {
for (int i = 0; i < 2; ++i) { // slit is in 2 boxes
rect* ri = s->boxes[i]; // box
int ei = s->edges[i]; // edge
int& nse = ri->nslits[ei]; // num slits on edge
list<slit*>& si = ri->slits[ei]; // slits of edge
if (erase (si, s)) { // erase slit
--nse;
--ri->total_slits;
}
}
if (s == slit_lip.slitt) {
if (editing_slit) toggle_flag (editing_slit, "", "Stopped editing deleted slit");
slit_lip.clear ();
}
if (erase (slits, s)) --num_slits; // erase from global slits list
if (erase (selected_slits, s)) --num_selected_slits; // erase from selected slits list
delete s; // free mem
s = 0;
}
int mondrian::try_slitting () {
if (slitting == NOTHING) return 0;
else {
add_remove_slit (win.mousex, win.mousey);
return mon_selector.abort ();
}
return 0;
}
int mondrian::stop_adding_balls () {
if (adding_balls) {
stringstream ss; ss << "Stopped adding " << ball::types_str [added_ball_type];
toggle_flag (adding_balls, "", ss.str());
started_making_ball = 0;
if (new_ball) delete new_ball;
new_ball = 0;
return 1;
}
return 0;
}
int mondrian::stop_moving_balls () {
if (moving_balls) {
toggle_flag (moving_balls, "", "Stopped moving balls");
mon_selector.abort ();
return 1;
}
return 0;
}
int mondrian::stop_slitting () {
int ret = draw_slit_cutter;
if (slitting) {
toggle_flag (slitting, "", "Stopped slitting");
ret = 1;
}
draw_slit_cutter = 0;
return ret;
}
int mondrian::stop_editing_slit () {
if (editing_slit) {
toggle_flag (editing_slit, "", "Stopped editing slit.");
if (slit_lip.slitt->is_too_small()) {
cons << YELLOW << "Closed slit because it was too small" << eol;
remove_slit (slit_lip.slitt);
}
mon_selector.abort();
return 1;
}
return 0;
}
int mondrian::stop_editing_edge () {
if (editing_edge) {
hit = 0;
edge = edge::NONE;
toggle_flag (editing_edge, "", "Stopped editing edge.");
mon_selector.abort();
return 1;
}
return 0;
}
int mondrian::stop_doing_stuff () {
int result = 0;
result |= stop_adding_balls ();
result |= stop_moving_balls ();
result |= stop_slitting ();
result |= stop_editing_slit ();
result |= stop_editing_edge ();
return result;
}
void mondrian::select_type (int t) {
browse.clear ();
clear_selected<ball> (selected_balls, num_selected_balls);
for (balls_iterator p = balls.begin (), q = balls.end(); p != q; ++p) {
ball* b = *p;
if (b->type == t) {
b->select = 1;
selected_balls.push_back (b);
++num_selected_balls;
}
}
after_selection ();
}
void mondrian::switch_balls_type () {
// all balls: wreckers < > healers
for (balls_iterator p = balls.begin (), q = balls.end(); p != q; ++p) {
ball* b = *p;
if (b->type != ball::BOUNCER) {
if (b->type == ball::WRECKER)
b->set_type (ball::HEALER);
else
b->set_type (ball::WRECKER);
}
}
}
void mondrian::start_slitting () {
if (slitting) {
stop_slitting ();
return;
}
cons.set_cmd_line ("Click on edge of a box to add or remove slit. ESC to stop.", GREEN);
draw_slit_cutter = slitting = JUST_SLIT;
}
void mondrian::change_slit_size (spinner<float>& s) {
slit::HALF_SIZE += s();
if (slit::HALF_SIZE < slit::MIN_HALF_SIZE) slit::HALF_SIZE = slit::MIN_HALF_SIZE;
draw_slit_cutter = 1;
cons << GREEN << "Default slit size = " << slit::HALF_SIZE << eol;
}
void mondrian::change_slit_anim_time (spinner<float>& sp) {
int hs = 0;
if (num_selected_slits == 0) hs = find_hit_slit (win.mousex, win.mousey);
int i = 0;
for (slit_iterator s = selected_slits.begin (), t = selected_slits.end (); s != t; ++s) {
slit* S = *s;
if (S->fdr) {
double& dt = S->fdr->delta_time;
dt += sp();
if (dt < 0) dt = 0;
S->animt = dt;
sprintf (BUFFER, "Slit %d, open|close time = %0.3f", ++i, dt);
cons << GREEN << BUFFER << eol;
} else S->toggle_anim ();
}
if (hs) clear_selected<slit> (selected_slits, num_selected_slits, 0);
}
void mondrian::remove_slits (rect* R) {
if (R->total_slits) {
for (int e = 0; e < rect::nedges; ++e) remove_slits_on_edge (R, e);
}
}
void mondrian::remove_slits_on_edge (rect* R, int e) {
int nse = R->nslits [e];
if (nse) {
list<slit*>& slits = R->slits[e];
while (slits.begin () != slits.end ()) {
slit* sf = slits.front ();
remove_slit (sf);
}
}
}
void mondrian::remove_slits_on_current_edge () {
rect* hit = 0;
for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
rect* li = *i;
if (li->total_slits && inbox (li->extents, win.mousex, win.mousey)) {
hit = li;
box<float>& bf = hit->extents;
edge = bf.get_edge_hit (win.mousex, win.mousey, gutter2); // find edge hit
if (edge != edge::NONE) {
remove_slits_on_edge (hit, edge); // remove slits
return;
} else {
cons << YELLOW << "Please get on an edge of this box to remove its slits" << eol;
return;
}
}
}
cons << YELLOW << "Please get on an edge of a box that has slits" << eol;
}
void mondrian::remove_slits_on_current_box () {
for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
rect* li = *i;
if (li->total_slits && inbox (li->extents, win.mousex, win.mousey)) {
remove_slits (li);
return;
}
}
}
void mondrian::remove_slits_on_boxes_with_balls () {
list<ball*>& balls = get_balls (0);
map<rect*, int> m;
for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
ball* b = *p;
m[b->R] = 1;
}
for (map<rect*, int>::iterator i = m.begin (), j = m.end (); i != j; ++i) remove_slits ((*i).first);
}
void mondrian::remove_all_slits () { // in all boxes
for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
rect* li = *i;
if (li->total_slits) remove_slits (li);
}
}
void mondrian::remove_selected_slits () {
while (1) {
slit_iterator i = selected_slits.begin ();
if (i != selected_slits.end ()) remove_slit (*i); else break;
}
}
int mondrian::get_index_of_slit (slit* s) {
int k = 0;
for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i, ++k) {
if (*i == s) return k;
}
return -1;
}
slit* mondrian::get_slit_from_index (int q) {
int p = 0;
for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i, ++p) {
if (p == q) return *i;
}
return 0;
}
int mondrian::get_note_ids (pair<int, int>& nids, rect* R, int type) {
pair<float, float> intervals;
if (type == split::VERTICAL)
R->get_vertical_interval (intervals, root);
else
R->get_horizontal_interval (intervals, root);
get_note_ids_from_intervals (nids, intervals);
return ((nids.first != -1) && (nids.second != -1));
}
void mondrian::get_note_ids_from_intervals (pair<int, int>& note_ids, const pair<float, float>& intervals) {
note_ids.first = note_ids.second = -1;
int n = scaleinfo.num_notes;
static const float E = 0.0001;
for (int i = 0; i < n; ++i) {
const string& name = scaleinfo.notes[i];
float value = INTERVALS [name];
float delta = value - intervals.first;
if (delta > E) {
note_ids.first = i;
break;
}
}
if (note_ids.first != -1) {
for (int j = note_ids.first; j < n; ++j) {
const string& name = scaleinfo.notes [j];
float value = INTERVALS [name];
float delta = intervals.second - value;
if (delta > E) {
note_ids.second = j + 1;
} else break;
}
}
}
int mondrian::multi_split_rect (split_data& sd) {
if (sd.start < sd.end) {
float pos;
if (sd.split_at == split::NOTES) {
int i = sd.start;
const string& name = scaleinfo.notes[i];
float value = INTERVALS [name];
float delta = value - 1.0f;
if (sd.split_type == split::VERTICAL) pos = root->extents.left + delta * root->extents.width;
else pos = root->extents.bottom + delta * root->extents.height;
} else {
if (sd.split_type == split::VERTICAL)
pos = sd.R->extents.left + sd.sz;
else
pos = sd.R->extents.bottom + sd.sz;
}
split_rect (sd.split_type, pos, sd.R);
if (sd.lr) sd.lr->push_back (sd.R->child1);
sd.R = sd.R->child2;
++sd.start;
++sd.nsplits;
return 0;
} else {
if (sd.lr && sd.R && sd.nsplits) sd.lr->push_back (sd.R);
if (sd.lis) sd.lis->split_over (sd);
return 1;
}
}
void mondrian::multi_split_rect (int n, int type, rect* B, list<rect*> *lr, split_listener* sl) { // split into n boxes
if (B == 0) {
B = box_under_cursor ();
if (B == 0) {
cons << YELLOW << "Cant split as you are outside all boxes :(" << eol;
return;
}
}
float amt;
if (type == split::VERTICAL) amt = B->extents.width; else amt = B->extents.height;
float sz = amt / n;
if (sz < min_split_size) {
cons << YELLOW << "Cannot split as the boxes will be too small [" << " < " << min_split_size << " ] :(" << eol;
return;
}
// 1 split every frame
split_data sd (0, n - 1, split::ANYWHERE, type, B, lr, sl, sz, n);
lsd.push_back (sd);
splitting_rects = 1;
}
void mondrian::multi_split_rect (int type, rect* B, list<rect*> *lr, split_listener* sl) { // split at notes
if (B == 0) B = box_under_cursor ();
if (B) {
pair<int, int> note_ids;
if (get_note_ids (note_ids, B, type)) {
// 1 split every frame
split_data sd (note_ids.first, note_ids.second, split::NOTES, type, B, lr, sl);
lsd.push_back (sd);
splitting_rects = 1;
}
} else {
cons << YELLOW << "Cant split as you are outside all boxes :(" << eol;
}
}
void mondrian::make_note_grid () {
if (root->child1 == 0 && root->child2 == 0) {
rect* B = root;
columns.clear ();
multi_split_rect (split::VERTICAL, B, &columns, this); // split on notes along horizontal axis
} else {
cons << YELLOW << "Please delete all boxes, cannot create note grid :(" << eol;
}
}
void mondrian::make_nxn_grid () {
if (root->child1 == 0 && root->child2 == 0) {
rect* B = root;
columns.clear ();
multi_split_rect (num_boxes, split::VERTICAL, B, &columns, this);
} else {
cons << YELLOW << "Please delete all boxes, cannot create box grid :(" << eol;
}
}
void mondrian::split_over (split_data& sd) {
if (sd.split_at == split::NOTES) {
for (box_iterator s = sd.lr->begin (), t = sd.lr->end (); s != t; ++s) {
multi_split_rect (split::HORIZONTAL, *s); // at notes
}
} else {
for (box_iterator s = sd.lr->begin (), t = sd.lr->end (); s != t; ++s) {
multi_split_rect (sd.n, split::HORIZONTAL, *s); // n x n
}
}
}
void mondrian::set_ball_param (int what, float v) {
list<ball*>& _balls = get_balls ();
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* b = *p;
switch (what) {
case ball::SET_DECAY_TIME:
b->decay_time = v;
break;
case ball::SET_ATTACK_TIME:
b->attack_time = v;
break;
case ball::SET_VOL_MULT:
b->vol_mult = v;
}
}
}
void mondrian::change_ball_vol_mult (spinner<float>& s) {
int n = 0;
list<ball*>& _balls = get_balls ();
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* b = *p;
b->vol_mult += s();
// if (b->vol_mult < 0) b->vol_mult = 0.0f;
sprintf (BUFFER, "Ball %d, volume multiplier = %0.3f", ++n, b->vol_mult);
cons << GREEN << BUFFER << eol;
}
}
void mondrian::change_min_voices (int d) {
min_voices += d;
if (min_voices < 1) min_voices = 1;
cons << YELLOW << "Min Voices = " << min_voices << eol;
MENU.sp_mondrian_min_voices.set_value (min_voices);
}
int edge_on_root (rect* root, rect** boxes, int* edges) {
float edge_vals_root [rect::nedges] = {root->extents.bottom, root->extents.right, root->extents.top, root->extents.left};
for (int i = 0; i < 2; ++i) {
rect* ri = boxes[i];
int ei = edges[i];
float edge_vals_ri [rect::nedges] = {ri->extents.bottom, ri->extents.right, ri->extents.top, ri->extents.left};
if (edge_vals_ri [ei] == edge_vals_root [ei]) return 1;
}
return 0;
}
void mondrian::mark_selected_slits () {
if (num_selected_slits) {
glEnable (GL_LINE_STIPPLE);
glLineStipple (1, 0xF0F0);
glColor3f (0, 1, 0);
for (slit_iterator si = selected_slits.begin (), sj = selected_slits.end (); si != sj; ++si) {
slit* s = *si;
rect* r = s->boxes[0];
int e = s->edges[0];
box<float>& x = r->extents;
float l [] = {x.bottom, x.right, x.top, x.left};
int t [] = {slit::HORIZONTAL, slit::VERTICAL, slit::HORIZONTAL, slit::VERTICAL};
glBegin (GL_LINES);
float le = l[e];
if (t[e] == slit::HORIZONTAL) {
glVertex2f (s->start, le);
glVertex2f (s->end, le);
} else {
glVertex2f (le, s->start);
glVertex2f (le, s->end);
}
glEnd ();
}
}
glDisable (GL_LINE_STIPPLE);
}
void mondrian::region_begin () {
rgn.left = rgn.right = win.mousex;
rgn.bottom = rgn.top = win.mousey;
}
void mondrian::region_end () {
rgn.calc ();
select_balls (rgn);
}
const box<float>& mondrian::region_update () {
rgn.right = win.mousex;
rgn.top = win.mousey;
return rgn;
}
void draw_slit_cutter (int yesno) {
mondrian0.draw_slit_cutter = yesno;
}
mondrian::browse_t::browse_t () {
n = which = last = 0;
}
void mondrian::browse_t::clear () {
n = which = last = 0;
balls.clear ();
MENU.ol_browse_balls.set_text ("");
}
void mondrian::typing (field& f) {
patstr = f.text;
patlen = patstr.length ();
fillpatbuf ();
}