Rev 55 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* mondrian.cc
* DIN Is Noise is copyright (c) 2006-2017 Jagannathan Sampath
* For more information, please visit http://dinisnoise.org/
*/
#include <fstream>
#include <iostream>
#include <sstream>
#include "main.h"
#include "input.h"
#include "font.h"
#include "console.h"
#include "mondrian.h"
#include "solver.h"
#include "widget.h"
#include "random.h"
#include "utils.h"
#include "vector2d.h"
#include "font.h"
#include "audio.h"
#include "ui_list.h"
#include "curve_library.h"
#include "container.h"
#include <algorithm>
#include <math.h>
using namespace std;
extern ofstream dlog;
extern console cons;
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 float PI_BY_180;
extern int IPS;
static const char spc = ' ';
extern std::map <string, float> INTERVALS;
mondrian::mondrian () :
_help ("mondrian.hlp"), 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")
{
name = "Mondrian";
pan = zoom = 1;
root = 0;
hit = 0;
edge = edge::NONE;
num_balls = 0;
num_selected = 0;
adding_balls = 0;
started_making_ball = 0;
new_ball = 0;
moving_balls = 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;
delta_attack_time = delta_decay_time = 0.01;
lmb_clicked = 0;
editing_edge = 0;
min_box_size = 2;
num_boxes = 8;
label_notes = 1;
fill_rects = 1;
draw__boxes = 1;
draw__notes = 1;
draw__balls = 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);
dlog << "--- destroyed Mondrian --" << endl;
}
void mondrian::enter () {}
void mondrian::leave () {
stop_doing_stuff ();
ui::leave ();
}
ball::ball (float _x, float _y, rect* _R) : x(_x), y(_y), R(_R) {init ();}
ball::ball (int _type) : trail (1, 10) { init (_type); }
void ball::init (int _type) {
V = 0;
vx = vy = 0;
select = 0;
frozen = 1;
pitch_mult = 1;
mod = 0;
r = g = b = 0.75f;
attack_time = recent_attack_time;
decay_time = recent_decay_time;
num_notes = 0;
del = 0;
auto_rotate = 0;
dtheta = PI_BY_180;
set_ball_type (this, _type);
++ref;
}
void ball::set_velocity (float dx, float dy) {
V = unit_vector (vx, vy, dx, dy);
if (V == 0) {
vx = 1;
vy = 1;
V = unit_vector (vx, vy, vx, vy);
}
calc_velocity_slope ();
}
void ball::rotate_velocity (int dir) {
rotate_vector (vx, vy, dir * dtheta);
calc_velocity_slope ();
}
void ball::calc_velocity_slope () {
if (vx != 0) {
vm = vy * 1.0f / vx;
vm_1 = 1.0f / vm;
vm_inf = 0;
} else {
vm = 0.0f;
vm_1 = 0.0f;
vm_inf = 1;
}
}
void ball::update () {
int edge_hit = 0;
if (!frozen) {
trail.add (x, y);
float px = x, py = y; // prev position
// move the ball by a constant speed we found when user made the ball
x += (V * vx);
y += (V * vy);
box<float>& e = R->extents; // get the box the ball is bouncing in
box<float>& eroot = mondrian0.root->extents; // get the root box
// check if the ball is still inside this box after move
if (inbox (e, x, y)) {
// ball is still inside the box so theres nothing to do
} else { // ball has hit a wall or ceiling of box
float xy, t0, dt;
pair<float, float> invl;
int clx = clamp<float> (e.left, x, e.right);
if (clx) { // hit wall
edge_hit = 1;
if (vm_inf == 0) y = py + vm * (x - px); else y = py;
clamp (e.bottom, y, e.top);
int walle [] = {edge::LEFT, 0, edge::RIGHT};
slit* S = 0;
rect* RO = get_other_rect_of_slit (R, walle[clx+1], y, &S);
if (RO) { // ball in the slit
if (type == HEALER) {
mondrian0.remove_slit (S); // close slit
//mondrian0.marks.push_back (point<float>(x, y));
}
R->erase (this);
R = RO; // ball is now in other box
R->balls.push_back (this);
edge_hit = 0;
} else { // ball not in slit
if (type == WRECKER) mondrian0.add_remove_slit (x, y); // make slit
vx = -vx; // flip horizontal component of velocity to rebound
calc_velocity_slope ();
xy = x;
t0 = eroot.left;
dt = eroot.width_1;
mondrian0.launch_note (this, xy, t0, dt, R->vint);
}
} else {
int cly = clamp<float> (e.bottom, y, e.top);
if (cly) { // hit ceiling
edge_hit = 1;
if (vm_inf == 0) x = px + vm_1 * (y - py); else x = px;
clamp (e.left, x, e.right);
int walle [] = {edge::BOTTOM, 0, edge::TOP};
slit* S = 0;
rect* RO = get_other_rect_of_slit (R, walle[cly+1], x, &S);
if (RO) { // ball in the slit
if (type == HEALER) {
mondrian0.remove_slit (S);
//mondrian0.marks.push_back (point<float>(x,y));
}
R->erase (this);
R = RO;
R->balls.push_back (this); // ball is now in other box
edge_hit = 0;
} else {
if (type == WRECKER) mondrian0.add_remove_slit (x, y); // make slit
vy = -vy; // flip vertical component of velocity to rebound
calc_velocity_slope ();
xy = y;
t0 = eroot.bottom;
dt = eroot.height_1;
mondrian0.launch_note (this, xy, t0, dt, R->hint);
}
}
}
}
}
if (auto_rotate && !edge_hit) rotate_velocity (auto_rotate); // auto_rotate = -1 is clockwise; auto_rotate = 1 is anti-clockwise
}
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_frequency (frequency);
note_volume = 1.0f / voices;
triggered_notes.push_back (triggered_note (N, 0, note_volume, _ball->x, _ball->y, 0, 0, 0, 0));
triggering_balls.push_back (_ball);
++_ball->num_notes;
triggered_note& last_triggered_note = triggered_notes.back ();
last_triggered_note.setup (&wave, &attack, &decay);
++num_triggered_notes;
print_num_triggered_notes ();
}
void mondrian::print_num_triggered_notes () {
stringstream ss; ss << "Voices: " << num_triggered_notes << "/" << voices;
uis.l_mondrian_voices.set_text (ss.str());
}
void mondrian::randomise_box_color () {
rect* found = find (root, win.mousex, win.mousey).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);
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) return selected; else return get_box_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 << console::yellow << "Defrosted ball [" << (unsigned int) 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 << console::yellow << "Ball [" << (unsigned int) 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 << console::yellow << "Defrosted ball [" << (unsigned int) b << "] cant move [0 speed]" << eol;
}
}
void mondrian::clear_modulations (list<ball*>& _balls) {
if (!num_selected) {
cons << console::red << "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->r = b->g = b->b = 1;
}
cons << console::green << "Cleared modulation on " << num_selected << " 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::load_settings (ifstream& file) {
std::string ignore;
file >> ignore >> name;
float l, b, r, t;
file >> ignore >> l >> b >> r >> t;
win.set (l, b, r, t);
file >> ignore >> win_chunk.x >> win_chunk.y;
file >> ignore >> obj_chunk.x >> obj_chunk.y;
win_per_obj (win_chunk.x / obj_chunk.x, win_chunk.y / obj_chunk.y);
obj_per_win (obj_chunk.x / win_chunk.x , obj_chunk.y / win_chunk.y);
file >> ignore >> win_resolution;
float dummy1 = 0, dummy2 = 0;
win2obj (win_resolution, dummy1, obj_resolution, dummy2);
}
void mondrian::save_settings (ofstream& file) {
file << "name " << name << endl;
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;
}
void mondrian::calc_win_mouse () {
if (uis.main_menu.show == 0) win.update_mouse ();
}
int mondrian::handle_input () {
if (moving_balls) move_balls (win.mousex - win.mousex_prev, win.mousey - win.mousey_prev);
if (editing_slit) slit_lip.edit (); else
if (editing_edge) set_edge (hit, edge, win.mousex, win.mousey);
if (lmb) {
if (lmb_clicked == 0) {
if (stop_moving_balls ());
else if (stop_editing_slit ());
else if (stop_editing_edge ());
else if (try_slitting ());
else if (!uis.main_menu.show && selector.exists) {
selector.handle_input (win.mousex, win.mousey);
select_balls (selector.region);
}
else {
hit = find (root, win.mousex, win.mousey).found;
if (hit) { // box hit
box<float>& bf = hit->extents;
edge = bf.get_edge_hit (win.mousex, win.mousey, gutter2);
if (edge != edge::NONE) { // edge hit
// check if slit hit
slit_lip.slitt = 0;
float* curs [rect::nedges] = {&win.mousex, &win.mousey, &win.mousex, &win.mousey};
slit_lip.cur = curs[edge];
if (get_slit_lip (slit_lip, hit, edge, *slit_lip.cur)) { // slit hit
float* prevs [rect::nedges] = {&win.mousex_prev, &win.mousey_prev, &win.mousex_prev, &win.mousey_prev};
slit_lip.prev = prevs[edge];
toggle_flag (editing_slit, "Just move mouse to edit slit. ESC to stop."); // edit this slit
} else toggle_flag (editing_edge, "Just move mouse to move edge. ESC or click to stop."); // edit this edge
} else {
if (!adding_balls) selector.handle_input (win.mousex, win.mousey);
}
}
}
}
if (adding_balls) { // user is adding balls
if (started_making_ball == 0) {
#ifdef __EVALUATION__
if (num_balls == 1) {
stop_adding_balls ();
cons << console::red << "You can launch more balls in the Licensed Version of DIN Is Noise" << eol;
return 1;
}
#endif
started_making_ball = 1;
new_ball = new ball (added_ball_type);
} else {
float dx, dy;
win.diff_mouse (dx, dy);
new_ball->set_velocity (dx, dy);
new_ball->x = win.mousex;
new_ball->y = win.mousey;
}
}
lmb_clicked = 1;
} else {
lmb_clicked = 0;
if (new_ball) {
new_ball->frozen = 0;
finding f = find (root, new_ball->x, new_ball->y);
if (f.found) {
new_ball->select = 1;
new_ball->R = f.found;
f.found->balls.push_back (new_ball);
balls.push_back (new_ball);
selected.push_back (new_ball);
++num_selected;
++num_balls;
} else {
delete new_ball;
}
started_making_ball = 0;
new_ball = 0;
}
if (selector.exists) selector.handle_input (win.mousex, win.mousey);
}
static const double reptf = 1./7, repts = 1./48.;
static double repts1 = 1./IPS;
// octave shift
if (keypressed (SDLK_z)) {if (!modulate_balls (-1)) modulate_down ();}
else if (keypressed (SDLK_x)) {if (!modulate_balls (1)) modulate_up ();}
// split box
if (keypressed (SDLK_r)) { // vertically
if (shift_down())
make_splits (split::VERTICAL); // vertically at notes
else if (ctrl_down())
make_splits (num_boxes, split::VERTICAL); // vertically make num_boxes
else
split_rect (split::VERTICAL, win.mousex); // into 2 new boxes
}
else if (keypressed (SDLK_f)) { // horizontally
if (shift_down ())
make_splits (split::HORIZONTAL); // horizontally at notes
else if (ctrl_down ())
make_splits (num_boxes, split::HORIZONTAL); // horizontally make num_boxes
else
split_rect (split::HORIZONTAL, win.mousey); // into 2 new boxes
}
else if (keypressed (SDLK_t)) {
if (shift_down () || ctrl_down ())
make_grid (); // make grid of num_boxes x num_boxes
else
make_note_grid (); // make note grid
}
// change ball speed
if (keypressedd (SDLK_LEFTBRACKET)) change_vel (-(float)uis.main_menu.sp_mondrian_change_vel.f_delta);
else if (keypressedd (SDLK_RIGHTBRACKET)) change_vel (+(float)uis.main_menu.sp_mondrian_change_vel.f_delta);
// change ball attack & decay time
static const float ddelta = 0.01;
if (keypressedd (SDLK_SEMICOLON, reptf, repts)) {
if (!shift_down()) change_attack_time_kb (-delta_attack_time); else {
delta_attack_time -= ddelta;
if (delta_attack_time < ddelta) delta_attack_time = ddelta;
cons << "delta attack time = " << delta_attack_time << eol;
}
}
else if (keypressedd (SDLK_QUOTE, reptf, repts)) {
if (!shift_down()) change_attack_time_kb (delta_attack_time); else {
delta_attack_time += ddelta;
cons << "delta attack time = " << delta_attack_time << eol;
}
}
else if (keypressedd (SDLK_COMMA, reptf, repts)) {
if (!shift_down()) change_decay_time_kb (-delta_decay_time); else {
delta_decay_time -= ddelta;
if (delta_decay_time < ddelta) delta_decay_time = ddelta;
cons << "delta decay time = " << delta_decay_time << eol;
}
}
else if (keypressedd (SDLK_PERIOD, reptf, repts)) {
if (!shift_down()) change_decay_time_kb (delta_decay_time); else {
delta_decay_time += ddelta;
cons << "delta decay time = " << delta_decay_time << eol;
}
}
// change ball course
if (keypressedd (SDLK_o, reptf, repts1)) {
if (ctrl_down())
toggle_auto_rotate (1); // change constantly anti-clockwise
else if (shift_down()) {
change_ball_dtheta (-1);
}
else
rotate_velocity (+1);
} else if (keypressedd (SDLK_p, reptf, repts1)) {
if (ctrl_down())
toggle_auto_rotate (-1); // change constantly clockwise
else if (shift_down()) {
change_ball_dtheta (+1);
}
else
rotate_velocity (-1); // rotate velocity vector clockwise
}
// ball selection
if (keypressed (SDLK_i)) invert_selected_balls ();
else if (keypressed (SDLK_k)) select_box_balls ();
else if (keypressed (SDLK_l)) select_all_balls ();
else if (keypressed (SDLK_n)) {
/*if (shift_down()) marks.clear ();
else*/ clear_selected_balls ();
}
// freeze/thaw balls
if (keypressed (SDLK_SPACE)) {
list<ball*>& balls = get_balls ();
if (shift_down()) freeze_balls (balls);
else if (ctrl_down()) thaw_balls (balls);
else freeze_thaw_balls (balls);
}
if (keypressed (SDLK_j)) flip_velocity (); // flip ball velocity ie flips ball direction
if (keypressedd (SDLK_KP_PLUS, reptf, repts)) change_slit_size (+1);
else if (keypressedd (SDLK_KP_MINUS, reptf, repts)) change_slit_size (-1);
if (keypressedd (SDLK_MINUS, reptf, repts)) change_trail_size (-1);
else if (keypressedd (SDLK_EQUALS, reptf, repts)) change_trail_size (+1);
if (keypressed (SDLK_b)) {
if (shift_down()) do_add_balls (ball::WRECKER);
else if (ctrl_down ()) do_add_balls (ball::HEALER);
else do_add_balls (); // add bouncers
}
if (keypressed (SDLK_m)) do_move_balls (); // move balls
if (keypressed (SDLK_c)) delete_selected_balls (); // delete balls
if (keypressedd (SDLK_y)) {
if (--voices <= 0) voices = 1;
cons << console::yellow << "Voices = " << voices << eol;
uis.main_menu.sp_mondrian_voices.set_value (voices);
}
else if (keypressedd (SDLK_u)) {
++voices;
cons << console::yellow << "Voices = " << voices << eol;
uis.main_menu.sp_mondrian_voices.set_value (voices);
}
if (keypressed (SDLK_F9)) remove_slits_on_current_edge ();
else if (keypressed (SDLK_F10)) remove_slits_on_current_box ();
else if (keypressed (SDLK_F11)) remove_all_slits ();
if (keypressed (SDLK_F3)) toggle_balls_type (ball::WRECKER);
else if (keypressed (SDLK_F4)) toggle_balls_type (ball::HEALER);
else if (keypressed (SDLK_F5)) toggle_balls_type (ball::BOUNCER);
else if (keypressed (SDLK_F6)) switch_balls_type ();
else if (keypressed (SDLK_F7)) select_type (ball::WRECKER);
else if (keypressed (SDLK_F8)) select_type (ball::HEALER);
if (keypressed (SDLK_v)) {
if (shift_down()) {
delete_all_rects ();
cons << console::green << "Deleted all boxes (except root :)" << eol;
} else {
delete_rect (); // delete current box
}
}
if (keypressed (SDLK_h)) randomise_box_color ();
if (keypressedd (SDLK_9)) {
if (shift_down()) change_poly_points (-1); else change_poly_radius (-1);
} else if (keypressedd (SDLK_0)) {
if (shift_down()) change_poly_points (1); else change_poly_radius (1);
}
if (keypressed (SDLK_SLASH)) start_slitting ();
if (keypressed (SDLK_F1)) _help (); // show help
// movement
double pan_rept = window::PAN_REPEAT, zoom_rept = window::ZOOM_REPEAT;
if (keypressedd (SDLK_a, pan_rept, pan_rept)) do_panx (-pan);
else if (keypressedd (SDLK_d, pan_rept, pan_rept)) do_panx (+pan);
else if (keypressedd (SDLK_w, pan_rept, pan_rept)) do_pany (+pan);
else if (keypressedd (SDLK_s, pan_rept, pan_rept)) do_pany (-pan);
// zoom
else if (keypressedd (SDLK_q, zoom_rept, zoom_rept)) do_zoom (+zoom);
else if (keypressedd (SDLK_e, zoom_rept, zoom_rept)) do_zoom (-zoom);
return 1;
}
void mondrian::update_attack () {
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->num_vertices () - 1, ti.decay_lastx, y);
}
}
void mondrian::update_waveform (multi_curve& crv) {
for (note_iterator i = triggered_notes.begin (), j = triggered_notes.end (); i != j; ++i) (*i).update_solver (crv);
}
void mondrian::draw_rect (rect* what) {
//if (what->child1 == 0 && what->child2 == 0) {
box<float>& extents = what->extents;
if (fill_rects) {
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
glColor4f (what->r, what->g, what->b, 0.5);
glRectf (extents.left, extents.bottom, extents.right, extents.top);
glDisable (GL_BLEND);
}
glColor3f (1, 1, 1);
if (what->total_slits == 0) {
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
glRectf (extents.left, extents.bottom, extents.right, extents.top);
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]);
} else {
if (typp[i] == slit::HORIZONTAL) {
glBegin (GL_LINES);
glVertex2f (startt[i], levell[i]);
glVertex2f (endd[i], levell[i]);
glEnd ();
} else {
glBegin (GL_LINES);
glVertex2f (levell[i], startt[i]);
glVertex2f (levell[i], endd[i]);
glEnd ();
}
}
}
}
/*return;
}
draw_rect (what->child1);
draw_rect (what->child2);*/
}
void draw_slits (float start, float end, float level, int type, list<slit*>& slits) {
float last_end = start;
if (type == slit::VERTICAL) {
for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i) {
slit* si = *i;
glBegin (GL_LINES);
glVertex2f (level, last_end);
glVertex2f (level, si->start);
/*glVertex2f (level - 5, si->start);
glVertex2f (level + 5, si->start);
glVertex2f (level - 5, si->end);
glVertex2f (level + 5, si->end);*/
glEnd ();
last_end = si->end;
}
glBegin (GL_LINES);
glVertex2f (level, last_end);
glVertex2f (level, end);
glEnd ();
} else {
for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i) {
slit* si = *i;
glBegin (GL_LINES);
glVertex2f (last_end, level);
glVertex2f (si->start, level);
/*glVertex2f (si->start, level - 5);
glVertex2f (si->start, level + 5);
glVertex2f (si->end, level - 5);
glVertex2f (si->end, level + 5);*/
glEnd ();
last_end = si->end;
}
glBegin (GL_LINES);
glVertex2f (last_end, level);
glVertex2f (end, level);
glEnd ();
}
}
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 ();
if (draw__notes) 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 (slitting) { // draw slit glyph
glColor3f (1, 1, 0);
glLineWidth (2);
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);
glLineWidth (1);
}
}
if (draw__boxes) draw_leaves ();
if (draw__balls) draw_balls ();
selector.draw ();
/*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 (root);
poly.radius = root->extents.height / 4.0f;
change_poly_points (+24);
leaves.push_back (root);
}
poly.delta_radius = 0.1 * poly.radius;
if (poly.delta_radius == 0) poly.delta_radius = 100;
// 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;
slitting = 0;
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;
}
finding mondrian::find (rect* R, float x, float y) {
finding result;
if (inbox (R->extents, x, y)) {
if (R->child1 != 0) {
finding lf = find (R->child1, x, y);
if (lf.found == 0) {
finding rf = find (R->child2, x, y);
return rf;
} else return lf;
} else {
result.found = R;
result.sibling = get_sibling (R);
}
}
return result;
}
rect* mondrian::find_box_to_split () {
finding result = find (root, win.mousex, win.mousey);
rect* F = result.found;
if (F == 0) {
cons << console::red << "Sorry cant split because you are outside all boxes!" << eol;
return 0;
} else return F;
}
int mondrian::add_remove_slit (float x, float y, float sz, int verbose) {
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> exts = li->extents;
exts.resize (gutter, gutter);
if (inbox(exts, 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); // 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
if (verbose) cons << console::green << "Created slit" << eol;
} else { // not added so delete
remove_slit (s);
if (verbose) cons << console::red << "Removed slit because its too small." << eol;
}
} else { // balls will escape playing area
remove_slit (s);
if (verbose) cons << console::red << "Removed slit because balls will escape!" << eol;
}
} else { // remove hit slit
remove_slit (hs);
if (verbose) cons << console::red << "Removed hit slit" << eol;
}
result = 1;
}
return result;
}
rect* mondrian::split_rect (int type, float t, rect* B) {
if (B == 0) {
B = find_box_to_split ();
if (B == 0) return 0;
}
stop_adding_balls ();
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 (root);
v2->calc_intervals (root);
split_balls (B, v1, v2);
remove_leaf (B);
add_leaf (v1);
add_leaf (v2);
recreate_slits (B);
return B;
}
template <class C, class V> int erase (C& c, V& v) {
typename C::iterator ce = c.end (), ci = std::find (c.begin (), ce, v);
if (ci != ce) {
c.erase (ci);
return 1;
}
return 0;
}
void mondrian::remove_leaf (rect* L) {
erase (leaves, L);
}
struct slit_info {
float x, y, half_size;
};
void mondrian::recreate_slits (rect* R) {
slit_info sf;
box<float>& e = R->extents;
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 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) {
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;
*movingi = s->mid;
sf.half_size = s->mid - s->start;
reslits.push_back (sf);
remove_slit (s);
si = slits.begin ();
sj = slits.end ();
}
}
}
int verbose = 0;
for (list<slit_info>::iterator i = reslits.begin (), j = reslits.end(); i != j; ++i) {
slit_info& si = *i;
add_remove_slit (si.x, si.y, si.half_size, verbose);
}
}
void mondrian::add_leaf (rect* L) {
leaves.push_back (L);
}
int mondrian::delete_rect () { // delete box under cursor
finding result = find (root, win.mousex, win.mousey);
rect* found = result.found;
rect* sibling = result.sibling;
if (found != 0) {
if (found == root) return 0;
// 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);
for (balls_iterator p = found->balls.begin (), q = found->balls.end (); p != q; ++p) { // find new home for balls of found
ball* b = *p;
b->R = find (sibling, b->x, b->y).found;
if (b->R == 0) b->R = sibling;
b->R->balls.push_back (b);
}
delete parent;
remove_leaf (found);
recreate_slits (found);
delete found;
return 1;
} else {
cons << console::red << "Cant delete. You are outside all boxes." << eol;
return 0;
}
}
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 (root);
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);
}
}
}
}
void mondrian::bg () {
for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) (*p)->update ();
remove_finished_notes ();
}
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 == 1) return *selected.begin (); else return 0;
}
int mondrian::change_param (float& param, float value, float& recent) {
static const float minval = 0.01f;
int atminval = (param == minval);
param += value;
int ret = 1;
if (param < minval) {
param = minval;
if (atminval) ret = 0;
}
recent = param;
return ret;
}
int mondrian::change_attack_time (float value) { // one selected ball only
ball* bsel = get_one_selected_ball ();
if (bsel) {
int ret = change_param (bsel->attack_time, value, ball::recent_attack_time);
cons << console::green << "Ball's attack time = " << bsel->attack_time << " secs." << eol;
return ret;
} else {
cons << console::red << "Please select 1 ball at a time to use this menu item" << eol;
return 0;
}
}
int mondrian::change_decay_time (float value) { // one selected ball only
ball* bsel = get_one_selected_ball ();
if (bsel) {
int ret = change_param (bsel->decay_time, value, ball::recent_decay_time);
cons << console::green << "Ball's decay time = " << bsel->decay_time << " secs." << eol;
return ret;
} else {
cons << console::red << "Please select 1 ball at a time to use this menu item" << eol;
return 0;
}
}
void mondrian::change_attack_time_kb (float value) { // selected or box or all balls, using keyboard short cut
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->attack_time, value, ball::recent_attack_time);
cons << console::green << "Ball " << ++n << ", attack time = " << b->attack_time << " secs." << eol;
}
}
void mondrian::change_decay_time_kb (float value) { // selected or box or all balls, using keyboard short cut
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, value, ball::recent_decay_time);
cons << console::green << "Ball " << ++n << ", decay time = " << b->decay_time << " secs." << 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;}
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;
}
glColor3f (1, 1, 1);
glVertexPointer (2, GL_FLOAT, 0, pts_d);
glDrawArrays (GL_LINES, 0, 2 * num_balls);
glPointSize (6);
glEnableClientState (GL_COLOR_ARRAY);
glVertexPointer (2, GL_FLOAT, 0, pts);
glColorPointer (3, GL_FLOAT, 0, pts_clr);
glDrawArrays (GL_POINTS, 0, num_balls_);
glDisableClientState (GL_COLOR_ARRAY);
glPointSize (1);
}
void mondrian::draw_notes () {
int n = poly.points;
float xy [2*n];
int tn = 1;
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;
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 (tn++ > voices) glColor3f (1.0, 0.25, 0.25); else glColor3f (0.9, 0.9, 1);
glVertexPointer (2, GL_FLOAT, 0, xy);
glDrawArrays (GL_LINE_LOOP, 0, n);
}
}
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_vel (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;
b->V += d;
if (b->V <= 0) {
b->V = 0;
}
cons << console::yellow << "Ball " << ++n << ", speed = " << b->V << eol;
}
}
void mondrian::clear_selected_balls () {
for (balls_iterator p = selected.begin (), q = selected.end (); p != q; ++p) {
ball* b = *p;
b->select = 0;
}
selected.clear ();
num_selected = 0;
}
void mondrian::select_using_modifiers (ball* b, int ctrl) {
balls_iterator se = selected.end (), f = ::find (selected.begin (), se, b);
if (ctrl) {
if (b->select) {
if (f != se) {
b->select = 0;
selected.erase (f);
--num_selected;
}
} else {
if (f == se) {
b->select = 1;
selected.push_back (b);
++num_selected;
}
}
} else {
if (f == se) {
b->select = 1;
selected.push_back (b);
++num_selected;
}
}
}
void mondrian::select_balls (box<int>& rgn) {
int shift = shift_down ();
int ctrl = ctrl_down ();
if (shift || ctrl); else clear_selected_balls ();
for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
ball* b = *p;
if (inbox<int> (rgn, b->x, b->y)) select_using_modifiers (b, ctrl);
}
after_selection ();
}
void mondrian::after_selection () {
if (num_selected) {
stop_adding_balls ();
cons << console::cyan << "Selected " << num_selected << " balls";
if (num_selected == 1) {
ball* bsel = *selected.begin();
cons << ", attack time = " << bsel->attack_time << ", decay time = " << bsel->decay_time << " secs.";
}
cons << eol;
} else {
cons << console::red << "No balls selected." << eol;
}
}
void mondrian::select_all_balls () {
clear_selected_balls ();
for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
ball* b = *p;
b->select = 1;
selected.push_back (b);
++num_selected;
}
after_selection ();
}
void mondrian::select_box_balls () {
rect* R = find (root, win.mousex, win.mousey).found;
if (R) {
list<ball*>& b_balls = R->balls;
int shift = shift_down ();
int ctrl = ctrl_down ();
if (shift || ctrl); else clear_selected_balls ();
for (balls_iterator p = b_balls.begin (), q = b_balls.end (); p != q; ++p) select_using_modifiers (*p, ctrl);
after_selection ();
}
}
void mondrian::invert_selected_balls () {
for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
ball* b = *p;
b->select = !b->select;
}
selected.clear ();
num_selected = 0;
for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
ball* b = *p;
if (b->select) {
selected.push_back (b);
++num_selected;
}
}
after_selection ();
}
void mondrian::delete_selected_balls () {
int nb = num_balls;
for (balls_iterator p = selected.begin (), q = selected.end(); p != q; ++p) {
ball* b = *p;
b->R->erase (b);
if (::erase (balls, b)) {
/*balls_iterator be = balls.end (), bf = ::find (balls.begin (), be, b);
if (bf != be) {
balls.erase (bf);*/
--num_balls;
}
if (b->num_notes == 0) delete b; else b->del = 1;
}
selected.clear ();
num_selected = 0;
cons << console::cyan << "Deleted " << (nb - num_balls) << " balls" << eol;
}
void mondrian::move_balls (float dx, float dy) {
for (balls_iterator p = selected.begin (), q = selected.end (); p != q; ++p) {
ball* b = *p;
b->x += dx;
b->y += dy;
if (inbox (b->R->extents, b->x, b->y)); else {
finder:
rect* nbr = find (root, b->x, b->y).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 finder;
}
}
}
}
void mondrian::toggle_flag (int& flag, const string& poz, const string& neg) {
flag = !flag;
if (flag)
cons.cmd_line = mesg (poz, console::green);
else {
cons.cmd_line = mesg ();
cons << console::red << neg << eol;
}
}
void mondrian::do_add_balls (int type) {
adding_balls = 1;
added_ball_type = type;
if (new_ball) set_ball_type (new_ball, added_ball_type);
stringstream ss; ss << "Click, drag and release to launch " << ball::types_str[added_ball_type] << "s. ESC to stop.";
cons.cmd_line = mesg (ss.str(), console::green);
}
void mondrian::do_move_balls () {
if (stop_moving_balls()) return;
if (num_selected) {
stop_adding_balls ();
toggle_flag (moving_balls, "Just move your mouse to move the balls. ESC to stop.");
}
else
cons << console::red << "Please select some balls!" << eol;
}
void mondrian::delete_all_balls () {
select_all_balls ();
delete_selected_balls ();
}
void mondrian::make_notes () {
vector<string> notes;
extern string NOTATION;
extern int NUM_INTERVALS;
extern const char* WESTERN_FLAT [];
extern vector<string> INTERVAL_NAMES;
extern vector<float> INTERVAL_VALUES;
int western = scaleinfo.western;
if (NOTATION == "western") {
for (int i = 0; i < NUM_INTERVALS; ++i) notes.push_back (WESTERN_FLAT[(western + i) % 12]);
} else {
for (int i = 0; i < NUM_INTERVALS; ++i) notes.push_back (INTERVAL_NAMES[i]);
}
int lh = get_line_height (), lh1 = 2 * lh, mcw = fnt.max_char_width;
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;
int _n_mrk = NUM_INTERVALS * 4 * 2;
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));
}
}
rect::rect () {
parent = child1 = child2 = 0;
total_slits = 0;
for (int i = 0; i < 4; ++i) nslits[i] = 0;
split = split::NONE;
make_random_color ();
++ref;
}
void rect::erase (ball* b) {
::erase (balls, b);
/*balls_iterator re = balls.end (), rf = std::find (balls.begin(), re, b);
if (rf != re) balls.erase (rf);*/
}
void rect::calc_intervals (rect* _root) {
hint.first = extents.bottom;
hint.second = extents.top;
vint.first = extents.left;
vint.second = extents.right;
}
void rect::get_vertical_interval (pair<float, float>& invl, rect* _root) {
invl.first = 1.0f + (vint.first - _root->extents.left) * _root->extents.width_1;
invl.second = 1.0f + (vint.second - _root->extents.left) * _root->extents.width_1;
}
void rect::get_horizontal_interval (pair<float, float>& invl, rect* _root) {
invl.first = 1.0f + (hint.first - _root->extents.bottom) * _root->extents.height_1;
invl.second = 1.0f + (hint.second - _root->extents.bottom) * _root->extents.height_1;
}
int rect::add_slit (slit* s, int e) {
int result = 1;
int& nse = nslits [e]; // number of slits on this edge
// for all edges
float lows [rect::nedges] = {extents.left, extents.bottom, extents.left, extents.bottom};
float highs [rect::nedges] = {extents.right, extents.top, extents.right, extents.top};
float low = lows[e];
if (nse) { // slits already on this edge
list<slit*>& ls = slits [e]; // slits of this edge
for (slit_iterator i = ls.begin (), j = ls.end (); i != j; ++i) {
slit* si = *i;
if (s->mid < si->start) {
s->start = max (s->start, low);
s->end = min (s->end, si->start);
i = ls.insert (i, s); // inserts sorted
// continue editing slit inspite of new inserted slit
//
if (mondrian0.editing_slit) {
slit* eds = mondrian0.slit_lip.slitt; // the edited slit
slit_iterator ip (i); --ip; // slit b4 inserted slit
slit_iterator in (i); ++in; // slit after inserted slit
int hl = 0;
if ((ip != j) && (*ip == eds)) { // edited slit is b4 inserted slit
// slits editable high is inserted slits low
mondrian0.slit_lip.set_high (s->start);
hl = 1;
} else {
if ((in != j) && (*in == eds)) { // slit after inserted slit is edited slit
// slit's editable low is inserted slits high
mondrian0.slit_lip.set_low (s->end);
hl = 1;
}
}
if (hl && mondrian0.slit_lip.slitt->is_too_small()) mondrian0.remove_slit (mondrian0.slit_lip.slitt);
}
goto adder;
}
low = si->end;
}
// append slit
// if last slit is currently edited slit, adjusts its high
//
slit* sl = ls.back ();
if (sl == mondrian0.slit_lip.slitt) mondrian0.slit_lip.set_high (s->start);
} else cons << console::cyan << "No slits on edge, adding first slit" << eol;
// append slit
s->start = max (s->start, low);
s->end = min (s->end, highs[e]);
slits[e].push_back (s);
adder:
++nse;
++total_slits;
cons << console::yellow << "Slit size = " << (s->end - s->start) << eol;
cons << console::yellow << "Slits on edge = " << nse << eol;
if (s->is_too_small()) result = 0; else s->calc_mid ();
return result;
}
void rect::update_slits (int e, float minn, float maxx) {
list<slit*>& ls = slits [e];
for (slit_iterator i = ls.begin (), j = ls.end (); i != j;) {
slit* si = *i;
clamp (minn, si->start, maxx);
clamp (minn, si->end, maxx);
if (si->is_too_small()) {
mondrian0.remove_slit (si);
i = ls.begin ();
j = ls.end ();
} else {
++i;
si->calc_mid ();
}
}
}
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) {
leaves.push_back (R); // a leaf
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.change_size (t);
f >> b->auto_rotate >> b->dtheta >> b->type;
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 == "poly") {
f >> ignore >> poly.radius >> ignore >> poly.points;
change_poly_points (0);
} else if (what == "slits") {
load_slits (f, boxes);
} 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 (root);
}
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 = (int) *r;
*r = get_slit_from_index (ix);
}
}
}
}
}
void mondrian::save_balls (ofstream& f) {
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.max_points << spc << b.auto_rotate << spc << b.dtheta << spc << b.type << 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
}
f << si->type << spc << si->start << spc << si->end << spc << si->mid << 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;
slits.push_back (s);
}
}
void mondrian::toggle_balls_type (int T) {
static const char* names [] = {" bouncers ", " wreckers ", " healers "};
if (num_selected) {
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;
}
set_ball_type (pb, Tt);
}
cons << console::green << "Made " << t << names[T] << "and " << b << names[0] << eol;
} else cons << console::red << "Please select some balls!" << eol;
}
int mondrian::modulate_balls (int w) {
if (num_selected) {
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);
cons << console::yellow << "ball " << ++b << ", pitch_multiplier = " << pb->pitch_mult << eol;
pb->mod += w;
color_ball_using_modulation (pb);
}
}
return num_selected;
}
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;
cons << "Ball " << ++n << ", delta_rotate_velocity = " << pb->dtheta << eol;
}
}
void mondrian::change_trail_size (int delta) {
list<ball*>& _balls = get_balls ();
int n = 0;
for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
ball* pb = *p;
pb->trail.change_size (delta);
cons << console::yellow << "Ball " << ++n << ", trail size = " << pb->trail.num_points << eol;
}
}
int mondrian::change_poly_points (int delta) {
int ret = 1;
poly.points += delta;
if (poly.points < 2) {
poly.points = 2;
ret = 0;
}
poly.coss.resize (poly.points);
poly.sinn.resize (poly.points);
int n = poly.points;
extern 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 << "Note polygon points = " << poly.points << eol;
return ret;
}
int mondrian::change_poly_radius (int dir) {
int ret = 1;
poly.radius += (dir * poly.delta_radius);
if (poly.radius < 0) {
poly.radius = 0;
ret = 0;
}
cons << "Note polygon radius = " << poly.radius << 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 () {}
slit::slit () {
boxes[0] = 0; edges[0] = -1;
boxes[1] = 0; edges[1] = -1;
start = end = mid = 0;
type = INVALID;
++ref;
}
slit::slit (rect** bxs, float x, float y, float sz) {
// slit can be on 2 boxes only
boxes [0] = bxs [0]; // box 0
boxes [1] = bxs [1]; // box 1
rect* b0 = boxes [0];
int e0 = b0->extents.get_edge_hit (x, y, mondrian::gutter2); // edge 0
edges [0] = e0;
edges [1] = (e0 + 2) % 4; // edge 1 = opposite edge on 2nd box
int typs [] = {HORIZONTAL, VERTICAL, HORIZONTAL, VERTICAL};
type = typs [e0];
if (type == VERTICAL) mid = y; else mid = x;
start = mid - sz;
end = mid + sz;
++ref;
}
slit::~slit () {
--ref;
}
int slit::is_too_small () {
float sz = end - start;
if (sz > MIN_SIZE) return 0; else return 1;
return 0;
}
rect* get_other_rect_of_slit (rect* ib, int e, float v, slit** os) {
list<slit*>& slits = ib->slits[e];
rect* or_ = 0;
for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i) {
slit* si = *i;
if (v >= si->start && v <= si->end) {
if (si->boxes[0] == ib) or_ = si->boxes[1]; else or_ = si->boxes[0];
*os = si;
return or_;
}
}
return 0;
}
rect* get_other_rect_of_slit (slit* s, rect* t) {
if (s->boxes[0] == t) return s->boxes[1]; else return s->boxes[0];
}
int get_slit_lip (slit_lip_t& slit_lip, rect* R, int e, float v) {
list<slit*>& slits = R->slits[e];
static const int tol = 4;
for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i) {
slit* si = *i;
if (abs (v - si->start) < tol) {
slit_lip.slitt = si;
slit_lip.lip = &si->start;
rect* R1 = get_other_rect_of_slit (si, R);
box<float>& RE = R->extents, RE1 = R1->extents;
float lows [rect::nedges] = {RE.left, RE.bottom, RE.left, RE.bottom};
float lows1 [rect::nedges] = {RE1.left, RE1.bottom, RE1.left, RE1.bottom};
float low = max (lows[e], lows1[e]);
slit_iterator i1 = i; --i1;
if (i1 != j) slit_lip.low = max ((*i1)->end, low); else slit_lip.low = low;
slit_lip.high = si->end;
return 1;
} else if (abs (si->end - v) < tol) {
slit_lip.slitt = si;
slit_lip.lip = &si->end;
slit_lip.low = si->start;
rect* R1 = get_other_rect_of_slit (si, R);
box<float>& RE = R->extents, RE1 = R1->extents;
float highs [rect::nedges] = {RE.right, RE.top, RE.right, RE.top};
float highs1 [rect::nedges] = {RE1.right, RE1.top, RE1.right, RE1.top};
float high = min (highs[e], highs1[e]);
slit_iterator i1 = i; ++i1;
if (i1 != j) slit_lip.high = min ((*i1)->start, high); else slit_lip.high = high;
return 1;
}
}
return 0;
}
slit* slit_hit (rect* R, int e, float v) {
list<slit*>& slits = R->slits[e];
for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i) {
slit* si = *i;
if (v >= si->start && v <= si->end) return si;
}
return 0;
}
slit* slit_hit (rect** bxs, float x, float y) {
rect* b0 = bxs [0];
int e0 = b0->extents.get_edge_hit (x, y, mondrian::gutter2);
int e1 = (e0 + 2) % 4;
int e[2] = {e0, e1};
float v[4] = {x, y, x, y};
for (int i = 0; i < 2; ++i) {
rect* b = bxs[i];
int ei = e[i];
slit* sh = slit_hit (b, ei, v[ei]);
if (sh) return sh;
}
return 0;
}
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;
cons << console::yellow << "Slits on edge = " << nse << eol;
}
}
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
delete s; // free mem
}
void slit_lip_t::edit () {
float delta = *cur - *prev;
*lip += delta;
clamp (low, *lip, high);
slitt->calc_mid ();
}
void slit_lip_t::set_high (float h) {
high = h;
edit ();
}
void slit_lip_t::set_low (float l) {
low = l;
edit ();
}
void slit_lip_t::clear () {
slitt = 0;
lip = prev = cur = 0;
low = high = 0;
}
int mondrian::try_slitting () {
if (slitting) {
add_remove_slit (win.mousex, win.mousey);
return 1;
}
return 0;
}
int mondrian::stop_adding_balls () {
if (adding_balls) {
stringstream ss; ss << "Stopped adding " << ball::types_str [added_ball_type] << "s.";
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");
return 1;
}
return 0;
}
int mondrian::stop_slitting () {
if (slitting) {
toggle_flag (slitting, "", "Stopped slitting");
return 1;
}
return 0;
}
int mondrian::stop_editing_slit () {
if (editing_slit) {
toggle_flag (editing_slit, "", "Stopped editing slit.");
if (slit_lip.slitt->is_too_small()) {
cons << console::red << "Closed slit because it was too small" << eol;
remove_slit (slit_lip.slitt);
}
return 1;
}
return 0;
}
int mondrian::stop_editing_edge () {
if (editing_edge) {
hit = 0;
edge = edge::NONE;
toggle_flag (editing_edge, "", "Stopped editing edge.");
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) {
clear_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.push_back (b);
++num_selected;
}
}
}
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)
set_ball_type (b, ball::HEALER);
else
set_ball_type (b, ball::WRECKER);
}
}
}
void mondrian::start_slitting () {
cons.cmd_line = mesg ("Click on edge of a box to add or remove slit. ESC to stop.", console::green);
slitting = 1;
}
void mondrian::change_slit_size (float d) {
slit::HALF_SIZE += d;
if (slit::HALF_SIZE < slit::MIN_HALF_SIZE) slit::HALF_SIZE = slit::MIN_HALF_SIZE;
cons << console::green << "Default slit size = " << slit::HALF_SIZE << eol;
}
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);
}
cons << console::green << "Removed all slits on this edge" << eol;
}
}
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 << console::red << "Please get on the edge of this box to remove its slits" << eol;
return;
}
}
}
cons << console::red << "Please get on an edge of a box that has slits" << eol;
}
void mondrian::remove_slits_on_current_box () { // ie box under cursor
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);
cons << console::green << "Removed all slits in this box" << eol;
return;
}
}
}
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);
}
cons << console::green << "Removed all slits" << eol;
}
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;
}
void mondrian::delete_all_rects () {
// put mouse in center of box
float midx = root->extents.midx, midy = root->extents.midy;
float wx, wy; obj2win (midx, midy, wx, wy);
extern int mousex, mousey; win2view (wx, wy, mousex, mousey, win, view);
SDL_WarpMouse (mousex, mousey);
win.update_mouse ();
while (delete_rect ()); // delete box under cursor until only root is left
}
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 j = scaleinfo.num_notes;
for (int i = 0; i < j; ++i) {
const string& name = scaleinfo.notes[i];
float value = INTERVALS [name];
if (value >= intervals.first) {
note_ids.first = i + 1;
break;
}
}
for (int k = j - 1; k > -1; --k) {
const string& name = scaleinfo.notes[k];
float value = INTERVALS [name];
if (value <= intervals.second) {
note_ids.second = k;
break;
}
}
}
void mondrian::make_splits (int n, int type, rect* B, list<rect*> *pr) {
if (B == 0) {
B = find_box_to_split ();
if (B == 0) {
cons << console::red << "Cant split as you are outside all boxes :(" << eol;
return;
}
}
float num;
if (type == split::VERTICAL) num = B->extents.width; else num = B->extents.height;
float sz = num / n;
if (sz < min_box_size) cons << console::red << "Cannot split as the boxes will be too small [" << " < " << min_box_size << " ] :(" << eol;
float pos;
for (int i = 0, j = n - 1; i < j; ++i) {
if (type == split::VERTICAL) {
pos = B->extents.left + sz;
split_rect (split::VERTICAL, pos, B);
} else {
pos = B->extents.bottom + sz;
split_rect (split::HORIZONTAL, pos, B);
}
if (pr) pr->push_back (B->child1);
B = B->child2;
}
if (pr) pr->push_back (B);
}
void mondrian::make_splits (int type, rect* B, list<rect*> *pr) {
if (B == 0) B = find_box_to_split ();
if (B) {
pair<float, float> intervals;
pair<int, int> note_ids;
if (type == split::VERTICAL)
B->get_vertical_interval (intervals, root);
else
B->get_horizontal_interval (intervals, root);
get_note_ids_from_intervals (note_ids, intervals);
int nsplits = 0;
for (int i = note_ids.first, j = note_ids.second; i < j; ++i) {
const string& name = scaleinfo.notes[i];
float value = INTERVALS [name];
float delta = value - 1.0f;
float pos;
if (type == split::VERTICAL)
pos = root->extents.left + delta * root->extents.width;
else
pos = root->extents.bottom + delta * root->extents.height;
split_rect (type, pos, B);
++nsplits;
if (pr) pr->push_back (B->child1);
B = B->child2;
}
if (pr) pr->push_back (B);
if (nsplits)
cons << console::green << "Created " << nsplits << " boxes on the notes of this scale" << eol;
else
cons << console::red << "No notes found to make splits :(" << eol;
} else {
cons << console::red << "Cant split as you are outside all boxes :(" << eol;
}
}
void mondrian::make_note_grid () {
if (root->child1 == 0 && root->child2 == 0) { // we only want to make note grid if there is just root
rect* B = root;
list<rect*> columns;
make_splits (split::VERTICAL, B, &columns); // split on notes along horizontal axis
// split each column along vertical axis
for (box_iterator s = columns.begin (), t = columns.end (); s != t; ++s) {
rect* C = *s;
make_splits (split::HORIZONTAL, C);
}
} else {
cons << console::red << "Please delete all boxes, cannot create note grid :(" << eol;
}
}
void mondrian::make_grid () {
if (root->child1 == 0 && root->child2 == 0) { // we only want to make box grid if there is just root
rect* B = root;
list<rect*> columns;
make_splits (num_boxes, split::VERTICAL, B, &columns); // split on notes along horizontal axis
// split each column along vertical axis
for (box_iterator s = columns.begin (), t = columns.end (); s != t; ++s) {
rect* C = *s;
make_splits (num_boxes, split::HORIZONTAL, C);
}
} else {
cons << console::red << "Please delete all boxes, cannot create box grid :(" << eol;
}
}
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 set_ball_type (ball* pb, int T) {
pb->type = T;
switch (T) {
case ball::BOUNCER:
color_ball_using_modulation (pb);
cons << console::yellow << "Ball " << "is a bouncer" << eol;
break;
case ball::WRECKER:
pb->r = 1.0f; pb->g = 0.25f; pb->b = 0.25f;
cons << console::red << "Ball " << "is a wrecker" << eol;
break;
case ball::HEALER:
pb->r = 0.0f; pb->g = 1.0f; pb->b = 1.0f;
cons << console::cyan << "Ball " << "is a healer" << eol;
}
}
void color_ball_using_modulation (ball* pb) {
if (pb->type != ball::BOUNCER) return;
if (pb->mod < 0) {
pb->r = pb->g = pb->b = 0.0f;
} else if (pb->mod > 0) {
pb->r = pb->g = pb->b = 1.0f;
} else {
pb->r = pb->g = pb->b = 0.75f;
}
}