1 /*
2 * mondrian.cc
3 * inspired by the works of Piet Mondrian
4 * DIN Is Noise is released under GNU Public License 2.0
5 * DIN Is Noise is copyright (c) 2006-2021 Jagannathan Sampath
6 * For more information, please visit https://dinisnoise.org/
7 */
8 
9 #include <fstream>
10 #include <iostream>
11 #include <sstream>
12 #include "ui_list.h"
13 #include "main.h"
14 #include "font.h"
15 #include "console.h"
16 #include "mondrian.h"
17 #include "solver.h"
18 #include "random.h"
19 #include "utils.h"
20 #include "vector2d.h"
21 #include "font.h"
22 #include "audio.h"
23 #include "curve_library.h"
24 #include "container.h"
25 #include "rect.h"
26 #include "ball.h"
27 #include "slit.h"
28 #include "oscilloscope.h"
29 
30 #include <algorithm>
31 #include <math.h>
32 
33 using namespace std;
34 
35 extern std::string user_data_dir;
36 extern viewport view;
37 extern font fnt;
38 extern mondrian mondrian0;
39 extern audio_out aout;
40 extern curve_library wav_lib, attack_lib, decay_lib;
41 extern gotog _gotomax;
42 extern int quit;
43 extern beat2value octave_shift;
44 extern float GOLDEN_RATIO;
45 extern const float PI_BY_180;
46 extern int IPS;
47 extern std::map <string, float> INTERVALS;
48 extern oscilloscope scope;
49 extern char BUFFER [];
50 extern float MIN_TIME;
51 extern int line_height;
52 
mondrian()53 mondrian::mondrian () :
54 wave ("mondrian_waveform.crv"), waved ("mondrian_waveform.ed"), wavlis (wave_listener::MONDRIAN),
55 attack ("mondrian_attack.crv"), decay ("mondrian_decay.crv"), attacked ("mondrian_attack.ed"),
56 decayed ("mondrian_decay.ed"), fdr (slit::INITIAL_OPEN_CLOSE_TIME), _help ("mondrian.hlp") {
57 
58   pan = zoom = 1;
59   root = 0;
60   hit = 0;
61   edge = edge::NONE;
62   num_balls = 0;
63   adding_balls = 0;
64   moving_balls = 0;
65   editing_edge = 0;
66   editing_slit = 0;
67   slitting = NOTHING;
68   started_making_ball = 0;
69   new_ball = 0;
70   n_pts = 0;
71   pts = 0;
72 	pts_d = 0;
73   pts_clr = 0;
74   n_mrk = 0;
75   mrk = 0;
76   cursor = 0;
77   num_triggered_notes = 0;
78   note_volume = 0;
79   voices = 0;
80 	min_voices = 0;
81 	auto_adjust_voices = 0;
82 	delta_attack_time = delta_decay_time = MIN_TIME;
83 	lmb_clicked = 0;
84 	num_boxes = 8;
85 	label_notes = 1;
86 	label_hz_vol = 0;
87 	fill_boxes = 1;
88 	draw__boxes = 1;
89 	draw__notes = 1;
90 	draw_slit_cutter = 0;
91 	num_selected_slits = 0;
92 	num_selected_balls = 0;
93 	sel_tar = SELECT_BALLS;
94   nleaves = 0;
95 
96   // box auto splitting
97   splitting_rects = 0;
98   auto_split_orient = split::BOTH;
99   auto_split_at = split::NOTES;
100 
101   // both [auto]split, delete
102 	split_leaf = rect::BIGGEST;
103 	delete_leaf = rect::RANDOM;
104 
105   delete_all_rects = 0;
106 
107   inst = 1;
108 
109 }
110 
~mondrian()111 mondrian::~mondrian () {
112   wave.save ("mondrian_waveform.crv");
113   attack.save ("mondrian_attack.crv");
114   decay.save ("mondrian_decay.crv");
115   ofstream edf ((user_data_dir + "mondrian.ed").c_str (), ios::out); save_settings (edf);
116   ofstream dataf ((user_data_dir + "mondrian.data").c_str(), ios::out);
117   string R("R");
118 	save_boxes (dataf, root, R);
119 	save_balls (dataf);
120 	dataf << "poly radius " << poly.radius << " points " << poly.points << endl;
121 	save_slits (dataf);
122   if (pts) delete[] pts;
123 	if (pts) delete[] pts_d;
124   if (pts_clr) delete[] pts_clr;
125   if (mrk) delete[] mrk;
126   for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) delete *p;
127   if (root) delete_children (root);
128   if (new_ball) delete new_ball;
129   dlog << "--- destroyed Mondrian --" << endl;
130 }
131 
enter()132 void mondrian::enter () {}
133 
leave()134 void mondrian::leave () {
135 	stop_doing_stuff ();
136   ui::leave ();
137 }
138 
139 
launch_note(ball * _ball,float t,float t0,float dt,const pair<float,float> & invl)140 void mondrian::launch_note (ball* _ball, float t, float t0, float dt, const pair<float, float>& invl) {
141   if (quit != DONT || (lmb && _ball == new_ball)) return;
142   float mid = (invl.first + invl.second) / 2.0f;
143 	if (t < mid) t = invl.first; else t = invl.second;
144   float interval = 1 + (t - t0) * dt;
145   float frequency = get_tonic (this) * interval * _ball->pitch_mult;
146   N.set_freq (frequency);
147 	++num_triggered_notes;
148 	if (auto_adjust_voices)
149 		voices = max (min_voices, min_voices + num_triggered_notes);
150 	else
151 		voices = min_voices;
152   note_volume = 1.0f / voices * _ball->vol_mult;
153 
154   triggered_notes.push_back (triggered_note (N, note_volume, _ball->x, _ball->y, _ball->trig_what));
155 	triggering_balls.push_back (_ball);
156 	++_ball->num_notes;
157 	triggered_note& last_triggered_note = triggered_notes.back ();
158 	last_triggered_note.setup (&wave, &attack, &decay);
159   last_triggered_note.r *= _ball->pitch_mult;
160   last_triggered_note.g *= _ball->pitch_mult;
161   last_triggered_note.b *= _ball->pitch_mult;
162 	print_num_triggered_notes ();
163 }
164 
print_num_triggered_notes()165 void mondrian::print_num_triggered_notes () {
166 	stringstream ss; ss << "Voices: " << num_triggered_notes << "/" << voices;
167 	uis.l_mondrian_voices.set_text (ss.str());
168 }
169 
randomise_box_color()170 void mondrian::randomise_box_color () {
171 	finding f; find (root, win.mousex, win.mousey, f);
172   rect* found = f.found;
173   if (found) found->make_random_color ();
174 }
175 
get_box_balls()176 list<ball*>& mondrian::get_box_balls () { // get balls in box or all balls
177 	finding f; find (root, win.mousex, win.mousey, f);
178 	rect* found = f.found;
179 	if (found) return found->balls; else return balls;
180 }
181 
get_balls()182 list<ball*>& mondrian::get_balls () { // get selected or balls in box or all balls
183   if (num_selected_balls) return selected_balls; else return get_box_balls ();
184 }
185 
get_balls(int)186 list<ball*>& mondrian::get_balls (int) { // get selected or all balls
187 	if (num_selected_balls) return selected_balls; else return balls;
188 }
189 
freeze_thaw_balls(list<ball * > & _balls)190 void mondrian::freeze_thaw_balls (list<ball*>& _balls) {
191   for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
192     ball* b = *p;
193     b->frozen = !b->frozen;
194 		if (b->frozen == 0 && b->V == 0) cons << YELLOW << "Defrosted ball [" << (uintptr_t) b << "] cant move [0 speed]" << eol;
195   }
196 }
197 
freeze_balls(list<ball * > & _balls)198 void mondrian::freeze_balls (list<ball*>& _balls) {
199   for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
200     ball* b = *p;
201     b->frozen = 1;
202 		if (b->V == 0) cons << YELLOW << "Ball [" << (uintptr_t) b << "] cant move already [0 speed]" << eol;
203   }
204 }
205 
thaw_balls(list<ball * > & _balls)206 void mondrian::thaw_balls (list<ball*>& _balls) {
207   for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
208     ball* b = *p;
209     b->frozen = 0;
210 		if (b->V == 0) cons << YELLOW << "Defrosted ball [" << (uintptr_t) b << "] cant move [0 speed]" << eol;
211   }
212 }
213 
clear_modulations(list<ball * > & _balls)214 void mondrian::clear_modulations (list<ball*>& _balls) {
215 	if (num_selected_balls == 0) {
216 		cons << YELLOW << "Please select some balls!" << eol;
217 		return;
218 	}
219   for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
220     ball* b = *p;
221 		b->pitch_mult = 1;
222 		b->mod = 0;
223 		b->color_using_modulation ();
224   }
225 	cons << GREEN << "Cleared modulation on " << num_selected_balls << " balls" << eol;
226 }
227 
update_parent(rect * C)228 void mondrian::update_parent (rect* C) {
229   rect* P = C->parent;
230   if (P) {
231     rect* child1 = P->child1, *child2 = P->child2;
232     box<float>& el = child1->extents;
233     box<float>& er = child2->extents;
234     box<float>& e = P->extents;
235     box<float> e0 (e);
236     if (P->split == split::VERTICAL) {
237       if (C == child1) {
238         er.left = el.right;
239         er.bottom = el.bottom;
240         er.top = el.top;
241         er.calc ();
242       } else {
243         el.right = er.left;
244         el.bottom = er.bottom;
245         el.top = er.top;
246         el.calc ();
247       }
248     } else {
249       if (C == child1) {
250         er.left = el.left;
251         er.right = el.right;
252         er.bottom = el.top;
253         er.calc ();
254       } else {
255         el.left = er.left;
256         el.right = er.right;
257         el.top = er.bottom;
258         el.calc ();
259       }
260     }
261     e.left = el.left;
262     e.right = er.right;
263     e.bottom = el.bottom;
264     e.top = er.top;
265     e.calc ();
266     if (e == e0) {
267       update_children (P->child1);
268       update_children (P->child2);
269     } else update_parent (P);
270   } else {
271     update_children (C);
272     calc_visual_params ();
273   }
274 }
275 
set_edge(rect * R,int e,float x,float y)276 void mondrian::set_edge (rect* R, int e, float x, float y) {
277   R->extents.set_edge (e, x, y);
278   update_parent (R);
279 }
280 
obj2win(const point<float> & v,float & wx,float & wy)281 void mondrian::obj2win (const point<float>& v, float& wx, float& wy) {
282   wx = win_per_obj.x * v.x;
283   wy = win_per_obj.y * v.y;
284 }
285 
obj2win(const float & ox,const float & oy,float & wx,float & wy)286 void mondrian::obj2win (const float& ox, const float& oy, float& wx, float& wy) {
287   wx = win_per_obj.x * ox;
288   wy = win_per_obj.y * oy;
289 }
290 
win2obj(const float & wx,const float & wy,float & ox,float & oy)291 void mondrian::win2obj (const float& wx, const float& wy, float& ox, float& oy) {
292   ox = obj_per_win.x * wx;
293   oy = obj_per_win.y * wy;
294 }
295 
load_settings()296 void mondrian::load_settings () {
297   ifstream file ((user_data_dir + "mondrian.ed").c_str (), ios::in);
298   if (!file) return;
299   load_settings (file);
300 }
301 
302 
save_settings(ofstream & file)303 void mondrian::save_settings (ofstream& file) {
304   file << "window " << win.left << spc << win.bottom << spc << win.right << spc << win.top << endl;
305   file << "win_chunk " << win_chunk.x << spc << win_chunk.y << endl;
306   file << "obj_chunk " << obj_chunk.x << spc << obj_chunk.y << endl;
307   file << "win_resolution " << win_resolution << endl;
308 	file << "label_hz_vol " << label_hz_vol << endl;
309 	file << "min_voices " << min_voices << endl;
310 	file << "auto_delete_rect " << auto_del_rect.active << endl;
311 	file << "auto_delete_rect_time " << auto_del_rect.triggert << endl;
312 	file << "auto_split_rect " << auto_split_rect.active << endl;
313 	file << "auto_split_time " << auto_split_rect.triggert << endl;
314 	file << "auto_split_orient " << auto_split_orient << endl;
315 	file << "auto_split_at " << auto_split_at << endl;
316 	file << "pick_box_split " << split_leaf << endl;
317 	file << "pick_box_delete " << delete_leaf << endl;
318 	file << "min_split_size " << mondrian::min_split_size << endl;
319 	file << "draw_boxes " << draw__boxes << endl;
320 	file << "draw_ball_pos " << draw_ball.position << endl;
321 	file << "draw_ball_heading " << draw_ball.heading << endl;
322 	file << "draw_notes " << draw__notes << endl;
323 	file << "label_notes " << label_notes << endl;
324 	file << "fill_boxes " << fill_boxes << endl;
325 	file << "num_boxes " << num_boxes << endl;
326 	file << "added_ball_type " << added_ball_type << endl;
327 	file << "cursor " << cursor << endl;
328 	file << "auto_adjust_voices " << auto_adjust_voices << endl;
329 	file << "half_slit_size " << slit::HALF_SIZE << endl;
330 	file << "turn_sync " << MENU.cb_turn_sync.state << endl;
331 	file << "speed_sync " << MENU.cb_speed_sync.state << endl;
332 }
333 
calc_win_mouse()334 void mondrian::calc_win_mouse () {
335   if (MENU.show == 0) win.update_mouse ();
336 }
337 
338 
toggle_triggered_sound()339 void mondrian::toggle_triggered_sound () {
340 	list<ball*>& _balls = get_balls ();
341 	int n = 0;
342 	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
343 		ball* b = *p;
344 		int& tw = b->trig_what;
345 		tw = !tw;
346 		cons << "Ball " << ++n;
347 		if (tw) cons << " triggers noise"; else cons << " triggers note";
348 		cons << eol;
349 	}
350 }
351 
clear_selected_targets()352 void mondrian::clear_selected_targets () {
353 	if (sel_tar == SELECT_BALLS) {
354 		browse.clear ();
355 		clear_selected<ball> (selected_balls, num_selected_balls);
356   }
357 	else
358 		clear_selected<slit> (selected_slits, num_selected_slits);
359 }
360 
select_all_targets()361 void mondrian::select_all_targets () {
362 	if (sel_tar == SELECT_BALLS) {
363 		browse.clear ();
364 		select_all<ball> (balls, selected_balls, num_selected_balls);
365   }
366 	else
367 		select_all<slit> (slits, selected_slits, num_selected_slits);
368 }
369 
invert_selected_targets()370 void mondrian::invert_selected_targets () {
371   if (sel_tar == SELECT_BALLS) {
372 		browse.clear ();
373     invert_selection<ball> (balls, selected_balls, num_selected_balls);
374   }
375   else
376     invert_selection<slit> (slits, selected_slits, num_selected_slits);
377 }
378 
select_box_targets()379 void mondrian::select_box_targets () {
380 	if (sel_tar == SELECT_BALLS) {
381 		select_box_balls ();
382   }
383 	else
384 		select_box_slits ();
385 }
386 
browse_ball(int i)387 void mondrian::browse_ball (int i) {
388   if (browse.n) {
389 		clear_selected<ball> (selected_balls, num_selected_balls, 0);
390     browse.which += i;
391 		wrap (0, browse.which, browse.last);
392 		ball* bb = browse.balls [browse.which];
393 		bb->select = 1;
394 		selected_balls.push_back (bb);
395 		++num_selected_balls;
396     after_selection ();
397   }
398 }
399 
delete_selected_targets()400 void mondrian::delete_selected_targets () {
401 	if (sel_tar == SELECT_BALLS)
402 		delete_selected_balls ();
403 	else
404 		remove_selected_slits ();
405 }
406 
delete_all_targets()407 void mondrian::delete_all_targets () {
408 	if (sel_tar == SELECT_BALLS)
409 		delete_all_balls ();
410 	else
411 		remove_all_slits ();
412 }
413 
update_attack()414 void mondrian::update_attack () {
415 	float x, y; attack.get_vertex (attack.last_vertex, x, y); _gotomax.set (x);
416   for (note_iterator i = triggered_notes.begin (), j = triggered_notes.end (); i != j; ++i) (*i).attack.update ();
417 }
418 
update_decay()419 void mondrian::update_decay () {
420   for (note_iterator i = triggered_notes.begin (), j = triggered_notes.end (); i != j; ++i) {
421 		triggered_note& ti = *i;
422 		solver& sol = ti.decay;
423 		sol.update ();
424 		float y; sol.mcrv->get_vertex (sol.mcrv->last_vertex, ti.decay_lastx, y);
425 	}
426 }
427 
update_waveform(multi_curve & crv)428 void mondrian::update_waveform (multi_curve& crv) {
429   static const string nam ("mondrian-waveform");
430   for (note_iterator i = triggered_notes.begin (), j = triggered_notes.end (); i != j; ++i)
431     (*i).update_solver (crv, nam);
432 }
433 
draw_rect(rect * what)434 void mondrian::draw_rect (rect* what) {
435 	box<float>& extents = what->extents;
436 
437 	if (fill_boxes) {
438 		glEnable (GL_BLEND);
439 		glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
440 		glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
441 		glColor4f (what->r, what->g, what->b, 0.5);
442 		glRectf (extents.left, extents.bottom, extents.right, extents.top);
443 		glDisable (GL_BLEND);
444 	}
445 
446 	// draw slits
447 	glColor3f (1, 1, 1);
448 	if (what->total_slits == 0) { // no slits
449 		glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
450 			glRectf (extents.left, extents.bottom, extents.right, extents.top); // outline rect
451 		glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
452 	} else {
453 		float startt [] = {extents.left, extents.bottom, extents.left, extents.bottom};
454 		float endd [] = {extents.right, extents.top, extents.right, extents.top};
455 		float levell [] = {extents.bottom, extents.right, extents.top, extents.left};
456 		int typp [] = {slit::HORIZONTAL, slit::VERTICAL, slit::HORIZONTAL, slit::VERTICAL};
457 		for (int i = 0; i < rect::nedges; ++i) {
458 			if (what->nslits[i]) {
459 				draw_slits (startt[i], endd[i], levell[i], typp[i], what->slits[i], slit_drawerr);
460 			} else {
461 				float li = levell[i];
462 				float si = startt[i], ei = endd[i];
463 				if (typp[i] == slit::HORIZONTAL) {
464 					slit_drawerr.add (si, li);
465 					slit_drawerr.add (ei, li);
466 				} else {
467 					slit_drawerr.add (li, si);
468 					slit_drawerr.add (li, ei);
469 				}
470 			}
471 		}
472 		slit_drawerr.draw ();
473 	}
474 }
475 
draw_leaves()476 void mondrian::draw_leaves () {
477 	for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
478 		rect* bi = *i;
479 		draw_rect (bi);
480 	}
481 }
482 
draw()483 void mondrian::draw  () {
484 
485 	glMatrixMode (GL_PROJECTION);
486 	glLoadIdentity ();
487 	glOrtho (win.left, win.right, win.bottom, win.top, -1, 1);
488 	glMatrixMode (GL_MODELVIEW);
489 	glLoadIdentity ();
490 
491 	draw_notes ();
492 
493 	if (label_notes) {
494 		tb.draw (); // note labels
495 		glColor3f (0.9, 0.9, 1);
496 		glVertexPointer (2, GL_FLOAT, 0, mrk);
497 		glDrawArrays (GL_LINES, 0, n_mrk); // note markers
498 	}
499 
500 	// draw cursor
501 	box<float>& rex = root->extents;
502 	if (inbox (rex, win.mousex, win.mousey)) {
503 		static const float cc = 0.5f;
504 		glColor3f (cc, cc, cc);
505 		crsr[0]=rex.left; crsr[1]=win.mousey;
506 		crsr[2]=rex.right; crsr[3]=win.mousey;
507 		crsr[4]=win.mousex; crsr[5]=rex.bottom;
508 		crsr[6]=win.mousex;crsr[7]=rex.top;
509 		glVertexPointer (2, GL_FLOAT, 0, crsr);
510 		glDrawArrays (GL_LINES, 0, 4);
511 	}
512 
513 	if (draw_slit_cutter) {
514 		glColor3f (1, 1, 0);
515 		glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
516 			glRectf (win.mousex - slit::HALF_SIZE, win.mousey - slit::HALF_SIZE, win.mousex + slit::HALF_SIZE, win.mousey + slit::HALF_SIZE);
517 		glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
518 	}
519 
520 	if (draw__boxes) draw_leaves ();
521 	draw_balls ();
522 
523 	mon_selector.draw (rgn);
524 
525 	//selector.draw ();
526 
527 	mark_selected_slits ();
528 
529 	/*glPointSize (5);
530 	glColor3f (0.0, 1.0, 1.0);
531 	for (int i = 0, j = marks.size (); i < j; ++i) {
532 		point<float>& pi = marks[i];
533 		glBegin (GL_POINTS);
534 		glVertex2f (pi.x, pi.y);
535 		glEnd ();
536 	}
537 	glPointSize (1);*/
538 
539 }
540 
calc_visual_params()541 void mondrian::calc_visual_params () {
542   win.calc ();
543   make_notes ();
544   tb.refresh (this);
545 }
546 
do_panx(int dir)547 void mondrian::do_panx (int dir) {
548   win.panx (dir);
549   calc_visual_params ();
550 }
551 
do_pany(int dir)552 void mondrian::do_pany (int dir) {
553   win.pany (dir);
554   calc_visual_params ();
555 }
556 
do_zoom(int dir)557 void mondrian::do_zoom (int dir) {
558   win.zoom (dir);
559   calc_visual_params ();
560 }
561 
setup()562 void mondrian::setup () {
563 
564 	dlog << "*** setting up Mondrian ***" << endl;
565 
566   load_settings ();
567 	load_boxes_and_balls ();
568 
569 	if (root == 0) {
570   	root = new rect;
571 		const int xsize = 800;
572   	root->extents (0, 0, xsize, xsize / GOLDEN_RATIO);
573 		root->calc_intervals ();
574 		poly.radius = root->extents.height / 4.0f;
575 		set_note_poly_points (24); cons.clear ();
576     add_leaf (root);
577 	}
578 
579 	poly.delta_radius = 1;
580 
581   // setup waveform
582   waved.add (&wave, &wavlis);
583   waved.attach_library (&wav_lib);
584 
585   // setup attack curve
586   attacked.add (&attack, &attacklis);
587   attacklis.inst = this;
588   attacked.attach_library (&attack_lib);
589 
590   // setup decay curve
591   decayed.add (&decay, &decaylis);
592   decaylis.inst = this;
593 	decayed.attach_library (&decay_lib);
594 
595 	scaleinfo.scl = this;
596 
597   dlog << "+++ Mondrian setup complete +++" << endl;
598 
599 }
600 
get_sibling(rect * R)601 rect* mondrian::get_sibling (rect* R) {
602   rect* P = R->parent;
603   if (P != 0) {
604     if (P->child1 == R)
605       return P->child2;
606     else
607       return P->child1;
608   }
609   return 0;
610 }
611 
612 
find(rect * R,float x,float y,finding & fnd)613 void mondrian::find (rect* R, float x, float y, finding& fnd) {
614   if (inbox (R->extents, x, y)) {
615     if (R->child1 != 0) {
616 			find (R->child1, x, y, fnd);
617       if (fnd.found == 0) find (R->child2, x, y, fnd);
618     } else {
619       fnd.found = R;
620       fnd.sibling = get_sibling (R);
621     }
622   }
623 }
624 
make_finding(rect * R)625 finding mondrian::make_finding (rect* R) {
626   finding result;
627   result.found = R;
628   result.sibling = get_sibling (R);
629   return result;
630 }
631 
box_under_cursor()632 rect* mondrian::box_under_cursor () {
633   finding result; find (root, win.mousex, win.mousey, result);
634   rect* F = result.found;
635   if (F == 0) {
636 		cons << YELLOW << "Sorry cant split because you are outside all boxes!" << eol;
637     return 0;
638   } else return F;
639 }
640 
641 
find_hit_slit(float x,float y)642 int mondrian::find_hit_slit (float x, float y) {
643 	rect* R = 0;
644 	for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) { // slit can only be on leaves
645 		rect* li = *i;
646 		box<float> exts = li->extents;
647 		exts.resize (gutter, gutter);
648 		if (inbox(exts, x, y)) { // finding 1 box is enough
649 			R = li;
650 			break;
651 		}
652 	}
653 	if (R) {
654 		int e = R->extents.get_edge_hit (x, y, gutter2);
655 		if (e != edge::NONE) {
656 			float v[] = {x, y, x, y};
657 			slit* hs = slit_hit (R, e, v[e]); // hit slit
658 			if (hs) {
659 				push_back (selected_slits, hs);
660 				++num_selected_slits;
661 				return 1;
662 			}
663 		}
664 	}
665 	return 0;
666 }
667 
add_remove_slit(float x,float y,fader * fdr,float sz)668 int mondrian::add_remove_slit (float x, float y, fader* fdr, float sz)  {
669 
670 
671 	int result = 0;
672 	int boxes_hit = 0;
673 	int k = 0;
674 
675 	// slit can only be on boxes that are leaves
676 	for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
677 		rect* li = *i;
678 		box<float> lie = li->extents;
679 		lie.resize (gutter, gutter);
680 		if (inbox(lie, x, y)) {
681 			if (++boxes_hit > 2) break; // too many!
682 			bxs[k++] = li;
683 		}
684 	}
685 
686 	if (boxes_hit == 2) { // slit can only be on 2 leaves
687 		slit* hs = slit_hit (bxs, x, y); // did we hit a slit?
688 		if (hs == 0) { // no slit hit
689 			slit* s = new slit (bxs, x, y, sz, fdr); // so make slit
690 			rect** boxes = s->boxes;
691 			int* edges = s->edges;
692 			if (edge_on_root (root, boxes, edges) == 0) { // balls wont escape
693 				// add slit to edges of the 2 leaves
694 				int result0 = boxes[0]->add_slit (s, edges[0]);
695 				int result1 = boxes[1]->add_slit (s, edges[1]);
696 				if (result0 && result1) { // added successfully
697 					++num_slits;
698 					slits.push_back (s); // add to global slits
699 				} else {
700 					// not added so discard
701 					remove_slit (s);
702 				}
703 			} else {
704 				// balls will escape playing area so discard
705 				remove_slit (s);
706 			}
707 		} else {
708 			remove_slit (hs);
709 		}
710 		result = 1;
711 	}
712 	return result;
713 }
714 
split_rect(int type,float t,rect * B)715 rect* mondrian::split_rect (int type, float t, rect* B) {
716 
717 	if (B == 0) {
718 		B = box_under_cursor ();
719 		if (B == 0) return 0;
720 	}
721 
722   stop_editing_slit ();
723 	stop_editing_edge ();
724 
725   box<float>& extents = B->extents;
726   B->split = type;
727   rect* v1 = new rect;
728   rect* v2 = new rect;
729   box<float>& v1e = v1->extents;
730   box<float>& v2e = v2->extents;
731 	if (type == split::VERTICAL) {
732   	v1e (extents.left, extents.bottom, t, extents.top);
733   	v2e (t, extents.bottom, extents.right, extents.top);
734 	} else {
735 		v1e (extents.left, extents.bottom, extents.right, t);
736 		v2e (extents.left, t, extents.right, extents.top);
737 	}
738   v1->parent = B;
739   v2->parent = B;
740   B->child1 = v1;
741   B->child2 = v2;
742   v1->calc_intervals ();
743   v2->calc_intervals ();
744   split_balls (B, v1, v2);
745 	remove_leaf (B);
746 	add_leaf (v1);
747 	add_leaf (v2);
748 	recreate_slits (B);
749 	return B;
750 }
751 
752 
recreate_slits(rect * R)753 void mondrian::recreate_slits (rect* R) {
754 	box<float>& e = R->extents;
755 	float fixed_val [rect::nedges] = {e.bottom, e.right, e.top, e.left};
756 	list<slit_info> reslits;
757 	for (int i = 0; i < rect::nedges; ++i) {
758 		list<slit*>& slits = R->slits[i];
759 		int ns = R->nslits [i];
760 		if (ns) {
761 			slit_info sf;
762 			float* moving [rect::nedges] = {&sf.x, &sf.y, &sf.x, &sf.y};
763 			float* fixed [rect::nedges] = {&sf.y, &sf.x, &sf.y, &sf.x};
764 			float* movingi = moving [i];
765 			float* fixedi = fixed [i];
766 			*fixedi = fixed_val [i];
767 			for (slit_iterator si = slits.begin (), sj = slits.end (); si != sj;) {
768 				slit* s = *si;
769 				if (s->fdr) {
770 					s->start = s->anim_start;
771 					s->end = s->anim_end;
772 					s->calc_mid ();
773 					sf.anim = 1;
774 					sf.fdr.copy (s->fdr);
775 				} else sf.anim = 0;
776 				*movingi = s->mid;
777 				sf.half_size = s->mid - s->start;
778 				reslits.push_back (sf);
779 				remove_slit (s);
780 				si = slits.begin ();
781 				sj = slits.end ();
782 			}
783 		}
784 	}
785 
786 	for (list<slit_info>::iterator i = reslits.begin (), j = reslits.end(); i != j; ++i) {
787 		slit_info& si = *i;
788 		if (si.anim)
789 			add_remove_slit (si.x, si.y, &si.fdr, si.half_size);
790 		else
791 			add_remove_slit (si.x, si.y, 0, si.half_size);
792 	}
793 }
794 
795 
add_leaf(rect * L)796 void mondrian::add_leaf (rect* L) {
797 	leaves.push_back (L);
798   ++nleaves;
799 }
800 
801 
remove_leaf(rect * L)802 void mondrian::remove_leaf (rect* L) {
803 	erase (leaves, L);
804   --nleaves;
805 }
806 
earliest_leaf()807 rect* mondrian::earliest_leaf () {
808   return *(leaves.begin());
809 }
810 
latest_leaf()811 rect* mondrian::latest_leaf () {
812   return *(--leaves.end());
813 }
814 
balled_leaf()815 rect* mondrian::balled_leaf () {
816   list<ball*>& balls = get_balls (0);
817   int sz = balls.size ();
818   if (sz) {
819     rnd<float> rd (0, balls.size()-1);
820     int r = rd() + 0.5;
821     ball* b = get <list<ball*>, ball*> (balls, r);
822     return b->R;
823   } else return rnd_leaf();
824 }
825 
rnd_leaf()826 rect* mondrian::rnd_leaf () {
827   rnd<float> rd (0, nleaves-1);
828   return  get< list<rect*>, rect*> (leaves, int(rd()+0.5));
829 }
830 
biggest_leaf()831 rect* mondrian::biggest_leaf () {
832   rect* mal = earliest_leaf ();
833   float max_area = mal->get_area ();
834 	for (box_iterator i = ++leaves.begin (), j = leaves.end (); i != j; ++i) {
835     rect* li = *i;
836     float li_area = li->get_area ();
837     if (li_area > max_area) {
838       max_area = li_area;
839       mal = li;
840     }
841   }
842   return mal;
843 }
844 
delete_rect(const finding & f)845 int mondrian::delete_rect (const finding& f) { // delete found box
846 
847 	rect* found = f.found;
848 	rect* sibling = f.sibling;
849 
850   // turn sibling into parent, kill parent
851   rect* parent = found->parent;
852   rect* grand_parent = parent->parent;
853   if (grand_parent) {
854     if (grand_parent->child1 == parent)
855       grand_parent->child1 = sibling;
856     else
857       grand_parent->child2 = sibling;
858   } else root = sibling;
859 
860   sibling->extents = parent->extents;
861   sibling->parent = grand_parent;
862   update_children (sibling);
863 
864 	finding fnd;
865   for (balls_iterator p = found->balls.begin (), q = found->balls.end (); p != q; ++p) { // find new home for balls of found
866     ball* b = *p;
867 		find (sibling, b->x, b->y, fnd);
868 		b->R = fnd.found;
869     if (b->R == 0) b->R = sibling;
870     b->R->balls.push_back (b);
871 		fnd.clear ();
872   }
873 
874   delete parent;
875 
876   remove_leaf (found);
877   recreate_slits (found);
878   delete found;
879 
880   return 1;
881 }
882 
delete_current_rect()883 void mondrian::delete_current_rect () { // delete box under cursor
884   finding f; find (root, win.mousex, win.mousey, f);
885   if (bad_rect (f.found)) return;
886 	delete_rect (f);
887 }
888 
delete_children(rect * _root)889 void mondrian::delete_children (rect* _root) {
890   rect* left = _root->child1;
891   rect* right = _root->child2;
892   if (left) {
893     delete_children (left);
894     delete_children (right);
895   }
896   delete _root;
897 }
898 
update_children(rect * R)899 void mondrian::update_children (rect* R) {
900   box<float>& extents = R->extents;
901   R->calc_intervals ();
902   if (R->child1 && R->child2) {
903     box<float>& c1e = R->child1->extents;
904     if (R->split == split::HORIZONTAL) {
905       R->child1->extents (extents.left, extents.bottom, extents.right, c1e.top);
906       R->child2->extents (extents.left, c1e.top, extents.right, extents.top);
907     } else if (R->split == split::VERTICAL) {
908       R->child1->extents (extents.left, extents.bottom, c1e.right, extents.top);
909       R->child2->extents (c1e.right, extents.bottom, extents.right, extents.top);
910     }
911     update_children (R->child1);
912     update_children (R->child2);
913   } else {
914 		if (edge != edge::NONE) {
915 			if (edge == edge::LEFT || edge == edge::RIGHT) {
916 				R->update_slits (edge::BOTTOM, R->extents.left, R->extents.right);
917 				R->update_slits (edge::TOP, R->extents.left, R->extents.right);
918 			} else {
919 				R->update_slits (edge::LEFT, R->extents.bottom, R->extents.top);
920 				R->update_slits (edge::RIGHT, R->extents.bottom, R->extents.top);
921 			}
922 		}
923 	}
924 }
925 
pick_leaf(int & what)926 rect* mondrian::pick_leaf (int& what) {
927   rect* asr = 0;
928   if (what == rect::RANDOM)
929     asr = rnd_leaf ();
930   else if (what == rect::BIGGEST)
931     asr = biggest_leaf ();
932   else if (what == rect::LATEST)
933     asr = latest_leaf ();
934 	else if (what == rect::EARLIEST)
935 		asr = earliest_leaf ();
936 	else {
937 		asr = balled_leaf ();
938 	}
939   return asr;
940 }
941 
942 
bg()943 void mondrian::bg () {
944 
945 	remove_finished_notes ();
946 
947 	if (quit) return;
948 
949   if (splitting_rects) {
950     for (split_iterator m = lsd.begin (), n = lsd.end (); m != n;) {
951       if (multi_split_rect (*m)) {
952         m = lsd.erase (m);
953         n = lsd.end ();
954         splitting_rects = !(m == n);
955       } else ++m;
956     }
957   } else {
958 
959 		if (delete_all_rects) {
960 			if (nleaves > 1) {
961 				rect* pl = pick_leaf (delete_leaf);
962 				int good_rect = !bad_rect (pl);
963 				if (good_rect) delete_rect ( make_finding (pl) );
964 			} else {
965 				delete_all_rects = 0;
966 			}
967 		} else {
968 			if (auto_del_rect.active ) {
969 				if (auto_del_rect (ui_clk())) {
970 					if (!editing_edge && !editing_slit) {
971 						rect* adr = pick_leaf (delete_leaf);
972 						if (!bad_rect (adr)) delete_rect (make_finding (adr));
973 					}
974 				}
975 			}
976 		}
977 
978     if (auto_split_rect.active) { // auto split box?
979 
980       if (auto_split_rect (ui_clk())) { // reached time to auto split
981 
982         // pick box to split
983         rect* asr = pick_leaf (split_leaf);
984 
985         int split_orient;
986         if (auto_split_orient == split::BOTH) {
987           rnd<float> rd (split::HORIZONTAL, split::VERTICAL); // horizontal or vertical at random
988           split_orient = rd () + 0.5;
989         } else
990           split_orient = auto_split_orient;
991 
992         if (auto_split_at == split::NOTES) { // split at notes of the scale
993           pair<int, int> note_ids;
994           if (get_note_ids (note_ids, asr, split_orient)) {
995             rnd<float> rd (note_ids.first, note_ids.second - 1);
996             int i = rd() + 0.5;
997             split_data sd (i, i + 1, auto_split_at, split_orient, asr);
998             lsd.push_back (sd); // split in bg!
999             splitting_rects = 1;
1000           }
1001         } else { // split at any microtone
1002           float delta;
1003           float low, high;
1004           if (split_orient == split::HORIZONTAL) {
1005             low = asr->extents.bottom;
1006             high = asr->extents.top;
1007             delta = asr->extents.height;
1008           } else {
1009             low = asr->extents.left;
1010             high = asr->extents.right;
1011             delta = asr->extents.width;
1012           }
1013           float _2_min_split_size = 2 * min_split_size;
1014           if (delta > _2_min_split_size) {
1015             rnd<float> rd (min_split_size, max (min_split_size, delta - min_split_size));
1016             float sz = rd ();
1017             int cl = 0;
1018             float pos = low + sz;
1019             cl = clamp (low, pos, high);
1020             if (!cl) {
1021               split_data sd (0, 1, auto_split_at, split_orient, asr);
1022               sd.sz = sz;
1023               lsd.push_back (sd); // split in bg!
1024               splitting_rects = 1;
1025             }
1026           }
1027         }
1028       }
1029     }
1030 
1031   }
1032 
1033 	for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i) (*i)->eval_anim ();
1034 
1035   for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
1036 		ball* b = *p;
1037 		b->eval_ops ();
1038 		b->update ();
1039 	}
1040 
1041 }
1042 
render_audio(float * L,float * R)1043 int mondrian::render_audio (float* L, float* R) {
1044 	int ret = 0;
1045 	balls_iterator bter = triggering_balls.begin ();
1046   for (note_iterator i = triggered_notes.begin (), j = triggered_notes.end (); i != j; ++i, ++bter) {
1047     triggered_note& ti = *i;
1048 		ball* bi = *bter;
1049     if (ti.state != triggered_note::FINISHED) {
1050       ti.eval (L, R, aout.result, aout.vol, aout.samples_per_channel, bi->attack_time, bi->decay_time, _gotomax);
1051       ti.eval (bi->attack_time);
1052 			ret += ti.player.mixer.active;
1053     }
1054   }
1055 	return ret;
1056 }
1057 
remove_finished_notes()1058 void mondrian::remove_finished_notes () {
1059 	balls_iterator bter = triggering_balls.begin ();
1060   for (note_iterator i = triggered_notes.begin (); i != triggered_notes.end();) {
1061     triggered_note& ti = *i;
1062     if (ti.state == triggered_note::FINISHED) {
1063       i = triggered_notes.erase (i);
1064 			ball* b = *bter; if (--b->num_notes <= 0 && b->del) delete b;
1065 			bter = triggering_balls.erase (bter);
1066       --num_triggered_notes;
1067 			print_num_triggered_notes ();
1068     } else {
1069 			++i;
1070 			++bter;
1071 		}
1072   }
1073 }
1074 
get_one_selected_ball()1075 ball* mondrian::get_one_selected_ball () {
1076 	if (num_selected_balls == 1) return *selected_balls.begin (); else return 0;
1077 }
1078 
change_param(float & param,float value,float & recent)1079 int mondrian::change_param (float& param, float value, float& recent) {
1080 	static const float minval = 0.0f;
1081 	int atminval = (param == minval);
1082 	param += value;
1083 	int ret = 1;
1084 	if (param < minval) {
1085 		param = minval;
1086 		if (atminval) ret = 0;
1087 	}
1088 	recent = param;
1089 	return ret;
1090 }
1091 
change_attack_time_kb(spinner<float> & s)1092 void mondrian::change_attack_time_kb (spinner<float>& s) {
1093 	int n = 0;
1094 	list<ball*>& _balls = get_balls ();
1095 	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
1096 		ball* b = *p;
1097 		change_param (b->attack_time, s(), ball::recent_attack_time);
1098 		sprintf (BUFFER, "Ball %d, attack time = %0.3f secs", ++n, b->attack_time);
1099 		cons << BUFFER << eol;
1100 	}
1101 
1102 }
1103 
change_decay_time_kb(spinner<float> & s)1104 void mondrian::change_decay_time_kb (spinner<float>& s) {
1105 	list<ball*>& _balls = get_balls ();
1106 	int n = 0;
1107 	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
1108 		ball* b = *p;
1109 		change_param (b->decay_time, s(), ball::recent_decay_time);
1110 		sprintf (BUFFER, "Ball %d, decay time = %0.3f secs", ++n, b->decay_time);
1111 		cons << BUFFER << eol;
1112 	}
1113 }
1114 
draw_balls()1115 void mondrian::draw_balls () {
1116 
1117 	int num_balls_ = num_balls;
1118 	if (new_ball) ++num_balls_;
1119   if (num_balls_ == 0) return;
1120   if (num_balls_ > n_pts) {
1121     if (pts) delete[] pts;
1122 		if (pts_d) delete[] pts_d;
1123     if (pts_clr) delete[] pts_clr;
1124     pts = new float [2 * num_balls_];
1125 		pts_d = new float [4 * num_balls_];
1126     pts_clr = new float [3 * num_balls_];
1127     n_pts = num_balls_;
1128   }
1129 
1130   int i = 0, j = 0, k = 0;
1131   float r, g, cb;
1132 
1133 	static const int S = 25;
1134   for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
1135     ball* b = *p;
1136     if (b->select) {r = 0; g = 1; cb = 0;} else {r = b->r; g = b->g; cb = b->b;}
1137 		if (draw_ball.trails) {
1138 			glColor3f (r, g, cb);
1139 			b->trail.draw ();
1140 		}
1141 
1142 		// position
1143     pts[i++] = b->x;
1144     pts[i++] = b->y;
1145 
1146 		// color
1147     pts_clr[j++]=r;
1148     pts_clr[j++]=g;
1149     pts_clr[j++]=cb;
1150 
1151 		// direction
1152 		pts_d[k++]=b->x+S*b->vx;
1153 		pts_d[k++]=b->y+S*b->vy;
1154 		pts_d[k++]=b->x;
1155 		pts_d[k++]=b->y;
1156 
1157   }
1158 
1159 	if (new_ball) {
1160 		pts[i++]=new_ball->x;
1161 		pts[i++]=new_ball->y;
1162 		pts_clr[j++]=new_ball->r;
1163 		pts_clr[j++]=new_ball->g;
1164 		pts_clr[j++]=new_ball->b;
1165 	}
1166 
1167 	if (draw_ball.heading) {
1168 		glColor3f (1, 1, 1);
1169 		glVertexPointer (2, GL_FLOAT, 0, pts_d);
1170 		glDrawArrays (GL_LINES, 0, 2 * num_balls);
1171 	}
1172 
1173 	if (draw_ball.position) {
1174 		glPointSize (5);
1175 		glEnableClientState (GL_COLOR_ARRAY);
1176 		glColorPointer (3, GL_FLOAT, 0, pts_clr);
1177 		glVertexPointer (2, GL_FLOAT, 0, pts);
1178 		glDrawArrays (GL_POINTS, 0, num_balls_);
1179 		glDisableClientState (GL_COLOR_ARRAY);
1180 		glPointSize (1);
1181 	}
1182 
1183 }
1184 
draw_notes()1185 void mondrian::draw_notes () {
1186 
1187 	int n = poly.points;
1188 	float xy [2*n];
1189 
1190 	if (label_hz_vol) tb_hz_vol.clear ();
1191 
1192   for (note_iterator i = triggered_notes.begin (), j = triggered_notes.end (); i != j; ++i) {
1193 		triggered_note& ti = *i;
1194 		float r = poly.radius * ti.volume.now / ti.volume.max;
1195 		if (draw__notes) {
1196 			for (int i = 0, j = 0; i < n; ++i) {
1197 				xy[j++]=ti.x+r*poly.coss[i];
1198 				xy[j++]=ti.y+r*poly.sinn[i];
1199 			}
1200 			if (scope.limit) glColor3f (1.0, 0.1, 0.1); else glColor3f (ti.r, ti.g, ti.b);
1201 			glVertexPointer (2, GL_FLOAT, 0, xy);
1202 			glDrawArrays (GL_LINE_LOOP, 0, n);
1203 		}
1204 
1205 		if (label_hz_vol) {
1206 			xy[0]=ti.x+r*poly.coss[0];
1207 			xy[1]=ti.y+r*poly.sinn[0];
1208 			float wtx = xy[0] + gutter, wty = xy[1] + gutter;
1209 			float clr = ti.volume.now * 1.0 / ti.volume.max;
1210 			sprintf (BUFFER, "%0.3f @ %03d%%", ti.start_hz, int(clr * 100.0 + 0.5));
1211 			tb_hz_vol.add (text (BUFFER, wtx, wty, ti.r, ti.g, ti.b));
1212 		}
1213 
1214 	}
1215 
1216 	if (label_hz_vol) {
1217 		tb_hz_vol.refresh (this);
1218 		tb_hz_vol.draw ();
1219 	}
1220 }
1221 
split_balls(rect * P,rect * C1,rect * C2)1222 void mondrian::split_balls (rect* P, rect* C1, rect* C2) {
1223   list<ball*>& P_balls = P->balls;
1224   for (balls_iterator p = P_balls.begin (); p != P_balls.end ();) {
1225     ball* b = *p;
1226     if (inbox (C1->extents, b->x, b->y)) {
1227       C1->balls.push_back (b);
1228       b->R = C1;
1229     } else {
1230       C2->balls.push_back (b);
1231       b->R = C2;
1232     }
1233     p = P_balls.erase (p);
1234   }
1235 }
1236 
change_speed(spinner<float> & s,float d)1237 void mondrian::change_speed (spinner<float>& s, float d) {
1238   list<ball*>& _balls = get_balls ();
1239 	int n = 0;
1240   for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
1241     ball* b = *p;
1242 
1243 		float& bv = b->V;
1244 		bv += s(d);
1245 
1246     if (bv <= 0) bv = 0.0f;
1247 
1248 		/*float& maxx = b->op_speed.max;
1249 		float omaxx = maxx;
1250 		maxx = max (maxx, bv);
1251 		if ((num_selected_balls == 1) && (maxx != omaxx)) MENU.sp_max_speed.set_value (maxx);*/
1252 
1253     sprintf (BUFFER, "Ball %d, speed = %0.3f", ++n, bv);
1254 
1255     cons << YELLOW << BUFFER << eol;
1256 
1257   }
1258 }
1259 
toggle_slit_anim()1260 void mondrian::toggle_slit_anim () {
1261 	int hs = 0;
1262 	if (num_selected_slits == 0) hs = find_hit_slit (win.mousex, win.mousey);
1263   for (slit_iterator s = selected_slits.begin (), t = selected_slits.end (); s != t; ++s) {
1264 		slit* si = *s;
1265 		si->toggle_anim ();
1266 	}
1267 	if (hs) clear_selected<slit> (selected_slits, num_selected_slits, 0);
1268 }
1269 
select_balls(const box<float> & rgn)1270 void mondrian::select_balls (const box<float>& rgn) {
1271 	browse.clear ();
1272   if (SHIFT || CTRL) ; else clear_selected<ball> (selected_balls, num_selected_balls);
1273   for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
1274     ball* b = *p;
1275     if (inbox (rgn, b->x, b->y)) select_using_modifiers<ball> (b, CTRL, selected_balls, num_selected_balls);
1276   }
1277 	after_selection ();
1278 }
1279 
after_selection()1280 void mondrian::after_selection () {
1281 	if (sel_tar == SELECT_BALLS) {
1282 		if (num_selected_balls) {
1283       cons << GREEN << "Selected " << num_selected_balls << " balls" << eol;
1284 			if (num_selected_balls == 1) {
1285 				ball* bsel = *selected_balls.begin();
1286         bsel->print ();
1287 				MENU.set_ball_ops (bsel);
1288 				if (browse.n) {
1289 					sprintf (BUFFER, " Ball %d of %d", browse.which+1, browse.n);
1290 					MENU.ol_browse_balls.set_text (BUFFER);
1291 				}
1292 			} else {
1293 				if (browse.n == 0) {
1294 					browse.balls.resize (num_selected_balls);
1295 					copy (selected_balls.begin (), selected_balls.end (), browse.balls.begin ());
1296 					browse.n = browse.balls.size ();
1297 					browse.last = browse.n - 1;
1298 					browse.which = -1;
1299 				}
1300 				MENU.ol_browse_balls.set_text (" Click arrow to load ball");
1301 			}
1302 		} else {
1303 			cons << RED << "No balls selected" << eol;
1304 			MENU.clear_ball_ops ();
1305 		}
1306 	} else {
1307 		if (num_selected_slits)
1308 			cons << GREEN << "Selected " << num_selected_slits << " slits" << eol;
1309 		else
1310 			cons << RED << "No slits selected" << eol;
1311 	}
1312 }
1313 
1314 
select_box_balls()1315 void mondrian::select_box_balls () {
1316   finding f; find (root, win.mousex, win.mousey, f);
1317 	rect* R = f.found;
1318   if (R) {
1319     list<ball*>& b_balls = R->balls;
1320 		browse.clear ();
1321     if (SHIFT || CTRL); else clear_selected<ball> (selected_balls, num_selected_balls);
1322     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);
1323 		after_selection ();
1324   }
1325 }
1326 
select_box_slits()1327 void mondrian::select_box_slits () {
1328 	finding f; find (root, win.mousex, win.mousey, f);
1329 	rect* R = f.found;
1330   if (R) {
1331 		if (SHIFT || CTRL); else clear_selected<slit> (selected_slits, num_selected_slits);
1332 		for (int i = 0; i < 4; ++i) {
1333 			list<slit*>& slits = R->slits[i];
1334 			for (slit_iterator s = slits.begin (), t = slits.end (); s != t; ++s) select_using_modifiers<slit> (*s, CTRL, selected_slits, num_selected_slits);
1335 		}
1336 		after_selection ();
1337 	}
1338 }
1339 
delete_ball(ball * b)1340 void mondrian::delete_ball (ball* b) {
1341 	if (::erase (balls, b)) --num_balls;
1342 	if (::erase (selected_balls, b)) --num_selected_balls;
1343 	if (::erase (browse.balls, b)) {
1344 		--browse.n;
1345 		if (browse.n) {
1346 			browse.last = browse.n - 1;
1347 			MENU.ol_browse_balls.set_text (" Click arrow to load ball");
1348 		} else {
1349 			browse.last = 0;
1350 		}
1351 	}
1352 
1353 	b->R->erase (b);
1354 	if (b->num_notes == 0) delete b; else b->del = 1;
1355 }
1356 
delete_selected_balls()1357 void mondrian::delete_selected_balls () {
1358   int nb = num_balls;
1359 	while (num_selected_balls) delete_ball (*selected_balls.begin());
1360   cons << YELLOW << "Deleted " << (nb - num_balls) << " balls" << eol;
1361 }
1362 
1363 
locate_ball(ball * b)1364 void mondrian::locate_ball (ball* b) {
1365 	if (!inbox (b->R->extents, b->x, b->y)) {
1366 		locator:
1367 		finding fnd; find (root, b->x, b->y, fnd);
1368 		rect* nbr = fnd.found;
1369 		if (nbr) {
1370 			b->R->erase (b);
1371 			b->R = nbr;
1372 			b->R->balls.push_back (b);
1373 		} else {
1374 			clamp<float>(root->extents.left, b->x, root->extents.right);
1375 			clamp<float>(root->extents.bottom, b->y, root->extents.top);
1376 			goto locator;
1377 		}
1378 	}
1379 }
1380 
move_balls(float dx,float dy)1381 void mondrian::move_balls (float dx, float dy) {
1382   for (balls_iterator p = selected_balls.begin (), q = selected_balls.end (); p != q; ++p) {
1383     ball* b = *p;
1384     b->x += dx;
1385     b->y += dy;
1386 		locate_ball (b);
1387   }
1388 }
1389 
toggle_flag(int & flag,const string & poz,const string & neg)1390 void mondrian::toggle_flag (int& flag, const string& poz, const string& neg) {
1391   flag = !flag;
1392   if (flag)
1393 		cons.set_cmd_line (poz, GREEN);
1394   else {
1395 		cons.set_cmd_line ("");
1396 		cons << YELLOW << neg << eol;
1397 	}
1398 }
1399 
do_add_balls(int type)1400 void mondrian::do_add_balls (int type) {
1401 	adding_balls = 1;
1402 	added_ball_type = type;
1403 	if (new_ball) new_ball->set_type (added_ball_type);
1404 	string bt (ball::types_str[added_ball_type]);
1405 	stringstream ss; ss << "Click, drag and release to launch " << bt << ". ESC to stop.";
1406 	cons.set_cmd_line (ss.str(), GREEN);
1407 	MENU.ol_ball_types.set_text (bt);
1408 }
1409 
do_move_balls()1410 void mondrian::do_move_balls () {
1411 	if (stop_moving_balls()) return;
1412   if (num_selected_balls) {
1413     stop_adding_balls ();
1414 		toggle_flag (moving_balls, "Just move your mouse to move the balls. ESC to stop.");
1415   }
1416   else
1417     cons << YELLOW << "Please select some balls!" << eol;
1418 }
1419 
delete_all_balls()1420 void mondrian::delete_all_balls () {
1421 	select_all<ball> (balls, selected_balls, num_selected_balls);
1422   delete_selected_balls ();
1423 }
1424 
make_notes()1425 void mondrian::make_notes () {
1426   vector<string> notes;
1427 	extern int NOTATION;
1428   extern int NUM_INTERVALS;
1429   extern const char* WESTERN_FLAT [];
1430   extern vector<string> INTERVAL_NAMES;
1431   extern vector<float> INTERVAL_VALUES;
1432   extern map<string,string> INT2IND;
1433 	int western = scaleinfo.western;
1434   if (NOTATION == WESTERN) {
1435     for (int i = 0; i < NUM_INTERVALS; ++i) notes.push_back (WESTERN_FLAT[(western + i) % 12]);
1436   } else if (NOTATION == NUMERIC) {
1437     for (int i = 0; i < NUM_INTERVALS; ++i) notes.push_back (INTERVAL_NAMES[i]);
1438   } else {
1439     for (int i = 0; i < NUM_INTERVALS; ++i) notes.push_back (INT2IND[INTERVAL_NAMES[i]]);
1440   }
1441   int lh = line_height, lh1 = 2 * lh, mcw = fnt.charwidth.max;
1442   box<float>& br = root->extents;
1443   float x = br.left, y1 = br.bottom - lh1, y2 = br.top + lh;
1444   int ks = 10, ksx = ks + 7;
1445   float y = br.bottom, x1 = br.left - ksx - 2 * mcw, x2 = br.right + ks + mcw;
1446 
1447 	static const int nsides = 4, verts_per_mark = 2;
1448   int _n_mrk = NUM_INTERVALS * nsides * verts_per_mark;
1449   if (_n_mrk > n_mrk) {
1450     if (mrk) delete[] mrk;
1451     n_mrk = _n_mrk;
1452     mrk = new float [2 * n_mrk];
1453   }
1454   int k = 0;
1455   tb.clear ();
1456   for (int i = 0; i < NUM_INTERVALS; ++i) {
1457     float iv = INTERVAL_VALUES[i] - 1;
1458     float xn = x + iv * br.width;
1459     float yn = y + iv * br.height;
1460     const string& ni = notes[i];
1461     mrk[k++]=xn;
1462     mrk[k++]=br.bottom;
1463     mrk[k++]=xn;
1464     mrk[k++]=br.bottom-ks;
1465     mrk[k++]=br.right;
1466     mrk[k++]=yn;
1467     mrk[k++]=br.right+ks;
1468     mrk[k++]=yn;
1469     mrk[k++]=xn;
1470     mrk[k++]=br.top;
1471     mrk[k++]=xn;
1472     mrk[k++]=br.top+ks;
1473     mrk[k++]=br.left;
1474     mrk[k++]=yn;
1475     mrk[k++]=br.left-ks;
1476     mrk[k++]=yn;
1477     tb.add (text (ni, xn, y1));
1478     yn -= fnt.lift;
1479     tb.add (text (ni, x1, yn));
1480     tb.add (text (ni, x2, yn));
1481     tb.add (text (ni, xn, y1));
1482     tb.add (text (ni, xn, y2));
1483   }
1484 }
1485 
1486 
save_boxes(ofstream & f,rect * R,string & id)1487 void mondrian::save_boxes (ofstream& f, rect* R, string& id) {
1488 	R->id = id;
1489 	rect* child1 = R->child1;
1490 	rect* child2 = R->child2;
1491 	box<float>& e = R->extents;
1492 	rect* P = R->parent;
1493 	string c1 ("-"), c2 ("-");
1494 	if (child1 != 0) {
1495 		string L (id + 'L'), R(id + 'R');
1496 		save_boxes (f, child1, L);
1497 		save_boxes (f, child2, R);
1498 		c1 = child1->id;
1499 		c2 = child2->id;
1500 	}
1501 	f << "box ";
1502 	if (P) f << P->id; else f << "0";
1503 	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;
1504 	int leaf = R->is_leaf ();
1505 	f << " leaf " << leaf << spc;
1506 	if (leaf) { // only leaf can have slits
1507 		int total_slits = R->total_slits;
1508 		f << "slits " << total_slits << spc;
1509 		// box can be leaf and not have any slits
1510 		if (total_slits) { // this leaf has slits
1511 			for (int i = 0; i < rect::nedges; ++i) {
1512 				int& nse = R->nslits[i];
1513 				f << nse << spc;
1514 				// not all of the leaf's edges need have slits
1515 				if (nse) { // this edge has slits
1516 					list<slit*>& si = R->slits[i];
1517 					for (slit_iterator p = si.begin (), q = si.end (); p != q; ++p) {
1518 						f << get_index_of_slit (*p) << spc;
1519 					}
1520 				}
1521 			}
1522 		}
1523 	}
1524 	f << endl;
1525 }
1526 
1527 
get_box_from_disk(list<box_from_disk_t> & boxes,const string & id)1528 box_from_disk_t mondrian::get_box_from_disk (list<box_from_disk_t>& boxes, const string& id) {
1529 	for (box_from_disk_iterator p = boxes.begin (), q = boxes.end (); p != q; ++p) {
1530 		box_from_disk_t& rp = *p;
1531 		if (rp.R->id == id) {
1532 			return rp;
1533 		}
1534 	}
1535 	return box_from_disk_t ();
1536 }
1537 
load_boxes_and_balls()1538 void mondrian::load_boxes_and_balls () {
1539 	list<box_from_disk_t> boxes;
1540 	box_from_disk_t box_from_disk;
1541 	string fname ("mondrian.data");
1542   ifstream f ((user_data_dir + fname).c_str (), ios::in);
1543 	if (!f) {
1544 		dlog << "!!! Failed loading boxes & balls from file: " << fname << " !!!" << endl;
1545 		return;
1546 	}
1547 	while (!f.eof()) {
1548 		string what, ignore;
1549 		f >> what;
1550 		if (what == "box") { // load box
1551 			rect *R = new rect;
1552 			box_from_disk.R = R;
1553 			f >> box_from_disk.parent;
1554 			f >> R->id;
1555 			float l, b, r, t;
1556 			f >> l >> b >> r >> t;
1557 			R->extents (l, b, r, t);
1558 			f >> R->split;
1559 			f >> R->r >> R->g >> R->b;
1560 			f >> box_from_disk.child1 >> box_from_disk.child2;
1561 			int is_leaf; f >> ignore >> is_leaf;
1562 			if (is_leaf) {
1563         add_leaf (R);
1564 				f >> ignore >> R->total_slits;
1565 				if (R->total_slits) { // can have slits
1566 					for (int i = 0; i < rect::nedges; ++i) {
1567 						list<slit*>& ls = R->slits [i]; // slits on edge
1568 						int& nse = R->nslits [i]; // number of slits on edge
1569 						f >> nse;
1570 						if (nse) {
1571 							for (int p = 0; p < nse; ++p) {
1572 								int ix; f >> ix; // get slit index
1573 								ls.push_back ((slit *) ix); // will resolve to slit* after slits are loaded
1574 							}
1575 						}
1576 					}
1577 				}
1578 			}
1579 			boxes.push_back (box_from_disk);
1580 			if (box_from_disk.parent == "0") root = R;
1581 		} else if (what == "ball") {
1582 
1583 			ball* b = new ball;
1584 			f >> b->x >> b->y >> b->V >> b->vx >> b->vy; b->set_velocity (b->V * b->vx, b->V * b->vy);
1585 			string rid; f >> rid >> b->frozen >> b->pitch_mult >> b->mod >> b->r >> b->g >> b->b >> b->attack_time >> b->decay_time;
1586 			int t; f >> t; b->trail.total = t; trail_t::alloc (t);
1587 			f >> b->auto_rotate >> b->dtheta >> b->type >> b->vol_mult >> b->trig_what;
1588 
1589 			float minn, maxx;
1590 			f >> b->op_turn.alarm.active >> b->op_turn.alarm.triggert;
1591 			f >> minn >> maxx; b->op_turn.rd.set (minn, maxx);
1592 
1593 			f >> b->op_turn.vx >> b->op_turn.vy >> b->op_turn.angle;
1594 			float turn_alpha; f >> turn_alpha;
1595 
1596 			f >> b->op_speed.alarm.active >> b->op_speed.alarm.triggert;
1597 			f >> minn >> maxx; b->op_speed.rd.set (minn, maxx);
1598 			f >> b->op_speed.max;
1599 			f >> b->op_speed.start >> b->op_speed.delta;
1600 			float speed_alpha; f >> speed_alpha;
1601 
1602 
1603 			f >> b->op_teleport.alarm.active >> b->op_teleport.alarm.triggert;
1604 			f >> b->op_teleport.radius;
1605 
1606 			f >> b->op_clone.alarm.active >> b->op_clone.alarm.triggert;
1607 			f >> b->op_clone.n >> b->op_clone.max >> b->op_clone.offset >> b->op_clone.clone_can_clone;
1608 
1609 			f >> b->op_transform.alarm.active >> b->op_transform.alarm.triggert;
1610 
1611 			ball_op* bops [] = {&b->op_turn, &b->op_speed, &b->op_teleport, &b->op_clone, &b->op_transform};
1612 			for (int i = 0; i < ball_op::NUM_OPS; ++i) {
1613 				ball_op* bopi = bops[i];
1614 				if (bopi->alarm.active) bopi->alarm.start ();
1615 			}
1616 
1617 			b->op_turn.alarm.startt += (turn_alpha * b->op_turn.alarm.triggert);
1618 			b->op_speed.alarm.startt += (speed_alpha * b->op_speed.alarm.triggert);
1619 
1620 			rect* R = get_box_from_disk (boxes, rid).R;
1621 			b->R = R;
1622 			R->balls.push_back (b);
1623 			balls.push_back (b);
1624 			++num_balls;
1625 		} else if (what == "rules") {
1626 			for (int i = 0; i < 3; ++i) f >> Transform::rules[i];
1627 		} else if (what == "poly") {
1628 			f >> ignore >> poly.radius >> ignore >> poly.points;
1629 			set_note_poly_points (poly.points); cons.clear ();
1630 		} else if (what == "slits") {
1631 			load_slits (f, boxes);
1632 		} else if (what == "max_balls") {
1633 			f >> Clone::max_balls;
1634 		} else break;
1635 	}
1636 
1637 	for (box_from_disk_iterator i = boxes.begin (), j = boxes.end (); i != j; ++i) {
1638 		box_from_disk_t& ri = *i;
1639 		rect* R = ri.R;
1640 		rect* P = get_box_from_disk (boxes, ri.parent).R;
1641 		rect* C1 = get_box_from_disk (boxes, ri.child1).R;
1642 		rect* C2 = get_box_from_disk (boxes, ri.child2).R;
1643 		R->parent = P;
1644 		R->child1 = C1;
1645 		R->child2 = C2;
1646 		R->calc_intervals ();
1647 	}
1648 
1649 	for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
1650 		rect* R = *i;
1651 		if (R->total_slits) {
1652 			for (int p = 0; p < rect::nedges; ++p) {
1653 				list<slit*>& ls = R->slits[p];
1654 				for (slit_iterator r = ls.begin (), s = ls.end (); r != s; ++r) {
1655 					int ix = (uintptr_t) *r;
1656 					*r = get_slit_from_index (ix);
1657 				}
1658 			}
1659 		}
1660 	}
1661 
1662 }
1663 
save_balls(ofstream & f)1664 void mondrian::save_balls (ofstream& f) {
1665 	f << "max_balls " << Clone::max_balls << endl;
1666 	for (balls_iterator i = balls.begin (), j = balls.end (); i != j; ++i) {
1667 		ball* pb = *i;
1668 		ball& b = *pb;
1669 		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;
1670 	}
1671 
1672 	f << "rules ";
1673 	for (int i = 0; i < 3; ++i) f << Transform::rules[i] << spc;
1674 	f << endl;
1675 
1676 }
1677 
save_slits(ofstream & f)1678 void mondrian::save_slits (ofstream& f) {
1679 	f << "slits " << num_slits << endl;
1680 	for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i) {
1681 		slit* si = *i;
1682 		for (int i = 0; i < 2; ++i) {
1683 			rect* ri = si->boxes[i];
1684 			int ei = si->edges[i];
1685 			f << ri->id << spc << ei << spc; // nb: call after save_boxes for proper rect id
1686 		}
1687 		int _fdr = 0;
1688 		if (si->fdr) {
1689 			si->start = si->anim_start;
1690 			si->end = si->anim_end;
1691 			_fdr = 1;
1692 		}
1693 		f << si->type << spc << si->start << spc << si->end << spc << si->mid << spc;
1694 
1695 		if (_fdr) {
1696 			f << _fdr << spc;
1697 			si->fdr->save (f);
1698 		} else {
1699 			f << _fdr;
1700 		}
1701 		f << endl;
1702 	}
1703 	for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i) delete *i; // free mem of all slits
1704 }
1705 
load_slits(ifstream & f,list<box_from_disk_t> & boxes)1706 void mondrian::load_slits (ifstream& f, list<box_from_disk_t>& boxes) {
1707 	f >> num_slits;
1708 	string r0, r1;
1709 	for (int i = 0; i < num_slits; ++i) {
1710 		slit* s = new slit;
1711 		f >> r0 >> s->edges[0] >> r1 >> s->edges[1];
1712 		s->boxes[0] = get_box_from_disk (boxes, r0).R;
1713 		s->boxes[1] = get_box_from_disk (boxes, r1).R;
1714 		f >> s->type >> s->start >> s->end >> s->mid;
1715 		int fdr_exists; f >> fdr_exists;
1716 		if (fdr_exists) {
1717 			s->toggle_anim ();
1718 			s->fdr->load (f);
1719 		}
1720 		slits.push_back (s);
1721 	}
1722 }
1723 
toggle_balls_type(int T)1724 void mondrian::toggle_balls_type (int T) {
1725 	static const char* names [] = {" bouncers ", " wreckers ", " healers "};
1726 	if (num_selected_balls) {
1727   	list<ball*>& _balls = get_balls ();
1728 		int t = 0, b = 0;
1729 		int Tt;
1730   	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
1731 			ball* pb = *p;
1732 			if (pb->type == T) {
1733 				Tt = ball::BOUNCER;
1734 				++b;
1735 			}
1736 			else {
1737 				Tt = T;
1738 				++t;
1739 			}
1740 			pb->set_type (Tt);
1741 		}
1742 		cons << GREEN << "Made " << t << names[T] << "and " << b << names[0] << eol;
1743 	} else cons << YELLOW << "Please select some balls!" << eol;
1744 }
1745 
modulate_balls(int w)1746 int mondrian::modulate_balls (int w) {
1747 	if (num_selected_balls) {
1748   	list<ball*>& _balls = get_balls ();
1749 		int b = 0;
1750   	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
1751 			ball* pb = *p;
1752 			pb->pitch_mult *= pow (octave_shift.sol.lasty, w);
1753       ball::recent_pitch_mult = pb->pitch_mult;
1754       sprintf (BUFFER, "Ball %d, pitch_multiplier = %0.3f", ++b, pb->pitch_mult);
1755       cons << BUFFER << eol;
1756 			pb->mod += w;
1757 			pb->color_using_modulation ();
1758 		}
1759 	}
1760 	return num_selected_balls;
1761 }
1762 
1763 /*void mondrian::rotate_velocity (spinner<int>& s) {
1764 	list<ball*>& _balls = get_balls ();
1765 	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
1766 		ball* pb = *p;
1767 		pb->rotate_velocity (dir);
1768 	}
1769 }*/
1770 
rotate_velocity(int dir)1771 void mondrian::rotate_velocity (int dir) {
1772  	list<ball*>& _balls = get_balls ();
1773  	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
1774 		ball* pb = *p;
1775 		pb->rotate_velocity (dir);
1776 	}
1777 }
1778 
change_ball_dtheta(int dir)1779 void mondrian::change_ball_dtheta (int dir) {
1780 	int n = 0;
1781 	list<ball*>& _balls = get_balls ();
1782 	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
1783 		ball* pb = *p;
1784 		pb->dtheta += (dir * PI_BY_180);
1785 		if (pb->dtheta <= 0) pb->dtheta = 0;
1786     sprintf (BUFFER, "Ball %d, delta rotate velocity = %0.3f", ++n, pb->dtheta);
1787     cons << BUFFER << eol;
1788 	}
1789 }
1790 
change_trail_size(spinner<int> & s)1791 void mondrian::change_trail_size (spinner<int>& s) {
1792 	int n = 0;
1793  	list<ball*>& _balls = get_balls ();
1794  	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
1795 		ball* pb = *p;
1796 		pb->trail.change (s());
1797 		cons << YELLOW << "Ball " << ++n << ", trail points = " << pb->trail.total << eol;
1798 	}
1799 }
1800 
set_note_poly_points(int p)1801 int mondrian::set_note_poly_points (int p) {
1802 	int ret = 1;
1803 	poly.points = p;
1804 	if (poly.points < 2) {
1805 		poly.points = 2;
1806 		ret = 0;
1807 	}
1808 	poly.coss.resize (poly.points);
1809 	poly.sinn.resize (poly.points);
1810 	int n = poly.points;
1811 	extern const float TWO_PI;
1812 	float dtheta = TWO_PI / n;
1813 	float theta = TWO_PI / 4.0f;
1814 	for (int i = 0; i < n; ++i) {
1815 		poly.coss[i] = cos(theta);
1816 		poly.sinn[i] = sin(theta);
1817 		theta += dtheta;
1818 	}
1819 	cons << YELLOW << "Note Polygon points = " << poly.points << eol;
1820 	return ret;
1821 }
1822 
set_note_poly_radius(float r)1823 int mondrian::set_note_poly_radius (float r) {
1824 	int ret = 1;
1825 	poly.radius = r;
1826 	if (poly.radius < 0) {
1827 		poly.radius = 0;
1828 		ret = 0;
1829 	}
1830   sprintf (BUFFER, "Note polygon radius = %0.3f", poly.radius);
1831   cons << YELLOW << BUFFER << eol;
1832 	return ret;
1833 }
1834 
flip_velocity()1835 void mondrian::flip_velocity () {
1836  	list<ball*>& _balls = get_balls ();
1837  	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
1838 		ball* pb = *p;
1839 		pb->vx = -pb->vx;
1840 		pb->vy = -pb->vy;
1841 		pb->calc_velocity_slope ();
1842 		if (pb->auto_rotate) {
1843 			pb->auto_rotate = -pb->auto_rotate;
1844 			rotate_velocity (pb->auto_rotate);
1845 		}
1846 	}
1847 }
1848 
set_auto_rotate(int ar)1849 void mondrian::set_auto_rotate (int ar) {
1850  	list<ball*>& _balls = get_balls ();
1851  	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) (*p)->auto_rotate = ar;
1852 }
1853 
toggle_auto_rotate(int ar)1854 void mondrian::toggle_auto_rotate (int ar) {
1855  	list<ball*>& _balls = get_balls ();
1856  	for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
1857 		ball* pb = *p;
1858 		if (pb->auto_rotate) pb->auto_rotate = 0; else pb->auto_rotate = ar;
1859 	}
1860 }
1861 
tonic_changed()1862 void mondrian::tonic_changed () {calc_visual_params ();}
scale_loaded()1863 void mondrian::scale_loaded () {}
scale_changed()1864 void mondrian::scale_changed () {}
1865 
remove_slit(slit * s)1866 void mondrian::remove_slit (slit* s) {
1867 
1868 	for (int i = 0; i < 2; ++i) { // slit is in 2 boxes
1869 		rect* ri = s->boxes[i]; // box
1870 		int ei = s->edges[i]; // edge
1871 		int& nse = ri->nslits[ei]; // num slits on edge
1872 		list<slit*>& si = ri->slits[ei]; // slits of edge
1873 		if (erase (si, s)) { // erase slit
1874 			--nse;
1875 			--ri->total_slits;
1876 		}
1877 	}
1878 
1879 	if (s == slit_lip.slitt) {
1880 		if (editing_slit) toggle_flag (editing_slit, "", "Stopped editing deleted slit");
1881 		slit_lip.clear ();
1882 	}
1883 
1884 	if (erase (slits, s)) --num_slits; // erase from global slits list
1885 	if (erase (selected_slits, s)) --num_selected_slits; // erase from selected slits list
1886 
1887 	delete s; // free mem
1888 	s = 0;
1889 }
1890 
try_slitting()1891 int mondrian::try_slitting () {
1892 	if (slitting == NOTHING) return 0;
1893   else {
1894 		add_remove_slit (win.mousex, win.mousey);
1895 		return mon_selector.abort ();
1896 	}
1897 	return 0;
1898 }
1899 
stop_adding_balls()1900 int mondrian::stop_adding_balls () {
1901   if (adding_balls) {
1902 		stringstream ss; ss << "Stopped adding " << ball::types_str [added_ball_type];
1903 		toggle_flag (adding_balls, "", ss.str());
1904 		started_making_ball = 0;
1905 		if (new_ball) delete new_ball;
1906 		new_ball = 0;
1907 		return 1;
1908 	}
1909 	return 0;
1910 }
1911 
stop_moving_balls()1912 int mondrian::stop_moving_balls () {
1913   if (moving_balls) {
1914 		toggle_flag (moving_balls, "", "Stopped moving balls");
1915 		mon_selector.abort ();
1916 		return 1;
1917 	}
1918 	return 0;
1919 }
1920 
1921 
stop_slitting()1922 int mondrian::stop_slitting () {
1923 	int ret = draw_slit_cutter;
1924 	if (slitting) {
1925 		toggle_flag (slitting, "", "Stopped slitting");
1926 		ret = 1;
1927 	}
1928 	draw_slit_cutter = 0;
1929 	return ret;
1930 }
1931 
stop_editing_slit()1932 int mondrian::stop_editing_slit () {
1933 	if (editing_slit) {
1934 		toggle_flag (editing_slit, "", "Stopped editing slit.");
1935 		if (slit_lip.slitt->is_too_small()) {
1936 			cons << YELLOW << "Closed slit because it was too small" << eol;
1937 			remove_slit (slit_lip.slitt);
1938 		}
1939 		mon_selector.abort();
1940 		return 1;
1941 	}
1942 	return 0;
1943 }
1944 
stop_editing_edge()1945 int mondrian::stop_editing_edge () {
1946 	if (editing_edge) {
1947 		hit = 0;
1948 		edge = edge::NONE;
1949 		toggle_flag (editing_edge, "", "Stopped editing edge.");
1950 		mon_selector.abort();
1951 		return 1;
1952 	}
1953 	return 0;
1954 }
1955 
stop_doing_stuff()1956 int mondrian::stop_doing_stuff () {
1957 	int result = 0;
1958   result |= stop_adding_balls ();
1959 	result |= stop_moving_balls ();
1960 	result |= stop_slitting ();
1961 	result |= stop_editing_slit ();
1962 	result |= stop_editing_edge ();
1963 	return result;
1964 }
1965 
select_type(int t)1966 void mondrian::select_type (int t) {
1967 	browse.clear ();
1968 	clear_selected<ball> (selected_balls, num_selected_balls);
1969 	for (balls_iterator p = balls.begin (), q = balls.end(); p != q; ++p) {
1970 		ball* b = *p;
1971 		if (b->type == t) {
1972 			b->select = 1;
1973 			selected_balls.push_back (b);
1974 			++num_selected_balls;
1975 		}
1976 	}
1977 	after_selection ();
1978 }
1979 
switch_balls_type()1980 void mondrian::switch_balls_type () {
1981 	// all balls: wreckers < > healers
1982 	for (balls_iterator p = balls.begin (), q = balls.end(); p != q; ++p) {
1983 		ball* b = *p;
1984 		if (b->type != ball::BOUNCER) {
1985 			if (b->type == ball::WRECKER)
1986 				b->set_type (ball::HEALER);
1987 			else
1988 				b->set_type (ball::WRECKER);
1989 		}
1990 	}
1991 }
1992 
start_slitting()1993 void mondrian::start_slitting () {
1994 	if (slitting) {
1995 		stop_slitting ();
1996 		return;
1997 	}
1998 	cons.set_cmd_line ("Click on edge of a box to add or remove slit. ESC to stop.", GREEN);
1999 	draw_slit_cutter = slitting = JUST_SLIT;
2000 }
2001 
change_slit_size(spinner<float> & s)2002 void mondrian::change_slit_size (spinner<float>& s) {
2003 	slit::HALF_SIZE += s();
2004 	if (slit::HALF_SIZE < slit::MIN_HALF_SIZE) slit::HALF_SIZE = slit::MIN_HALF_SIZE;
2005 	draw_slit_cutter = 1;
2006 	cons << GREEN << "Default slit size = " << slit::HALF_SIZE << eol;
2007 }
2008 
change_slit_anim_time(spinner<float> & sp)2009 void mondrian::change_slit_anim_time (spinner<float>& sp) {
2010 	int hs = 0;
2011 	if (num_selected_slits == 0) hs = find_hit_slit (win.mousex, win.mousey);
2012 	int i = 0;
2013 	for (slit_iterator s = selected_slits.begin (), t = selected_slits.end (); s != t; ++s) {
2014 		slit* S = *s;
2015 		if (S->fdr) {
2016 			double& dt = S->fdr->delta_time;
2017 			dt += sp();
2018 			if (dt < 0) dt = 0;
2019 			S->animt = dt;
2020       sprintf (BUFFER, "Slit %d, open|close time = %0.3f", ++i, dt);
2021       cons << GREEN << BUFFER << eol;
2022 		} else S->toggle_anim ();
2023 	}
2024 	if (hs) clear_selected<slit> (selected_slits, num_selected_slits, 0);
2025 }
2026 
remove_slits(rect * R)2027 void mondrian::remove_slits (rect* R) {
2028 	if (R->total_slits) {
2029 		for (int e = 0; e < rect::nedges; ++e) remove_slits_on_edge (R, e);
2030 	}
2031 }
2032 
remove_slits_on_edge(rect * R,int e)2033 void mondrian::remove_slits_on_edge (rect* R, int e) {
2034 	int nse = R->nslits [e];
2035 	if (nse) {
2036 		list<slit*>& slits = R->slits[e];
2037 		while (slits.begin () != slits.end ()) {
2038 			slit* sf = slits.front ();
2039 			remove_slit (sf);
2040 		}
2041 	}
2042 }
2043 
remove_slits_on_current_edge()2044 void mondrian::remove_slits_on_current_edge () {
2045 	rect* hit = 0;
2046 	for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
2047 		rect* li = *i;
2048 		if (li->total_slits && inbox (li->extents, win.mousex, win.mousey)) {
2049 			hit = li;
2050 			box<float>& bf = hit->extents;
2051 			edge = bf.get_edge_hit (win.mousex, win.mousey, gutter2); // find edge hit
2052 			if (edge != edge::NONE) {
2053 				remove_slits_on_edge (hit, edge); // remove slits
2054 				return;
2055 			} else {
2056 				cons << YELLOW << "Please get on an edge of this box to remove its slits" << eol;
2057 				return;
2058 			}
2059 		}
2060 	}
2061 	cons << YELLOW << "Please get on an edge of a box that has slits" << eol;
2062 }
2063 
remove_slits_on_current_box()2064 void mondrian::remove_slits_on_current_box () {
2065 	for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
2066 		rect* li = *i;
2067 		if (li->total_slits && inbox (li->extents, win.mousex, win.mousey)) {
2068 			remove_slits (li);
2069 			return;
2070 		}
2071 	}
2072 }
2073 
remove_slits_on_boxes_with_balls()2074 void mondrian::remove_slits_on_boxes_with_balls () {
2075 	list<ball*>& balls = get_balls (0);
2076 	map<rect*, int> m;
2077 	for (balls_iterator p = balls.begin (), q = balls.end (); p != q; ++p) {
2078 		ball* b = *p;
2079 		m[b->R] = 1;
2080 	}
2081 	for (map<rect*, int>::iterator i = m.begin (), j = m.end (); i != j; ++i) remove_slits ((*i).first);
2082 }
2083 
remove_all_slits()2084 void mondrian::remove_all_slits () { // in all boxes
2085 	for (box_iterator i = leaves.begin (), j = leaves.end (); i != j; ++i) {
2086 		rect* li = *i;
2087 		if (li->total_slits) remove_slits (li);
2088 	}
2089 }
2090 
remove_selected_slits()2091 void mondrian::remove_selected_slits () {
2092 	while (1) {
2093 		slit_iterator i = selected_slits.begin ();
2094 		if (i != selected_slits.end ()) remove_slit (*i); else break;
2095 	}
2096 }
2097 
get_index_of_slit(slit * s)2098 int mondrian::get_index_of_slit (slit* s) {
2099 	int k = 0;
2100 	for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i, ++k) {
2101 		if (*i == s) return k;
2102 	}
2103 	return -1;
2104 }
2105 
get_slit_from_index(int q)2106 slit* mondrian::get_slit_from_index (int q) {
2107 	int p = 0;
2108 	for (slit_iterator i = slits.begin (), j = slits.end (); i != j; ++i, ++p) {
2109 		if (p == q) return *i;
2110 	}
2111 	return 0;
2112 }
2113 
get_note_ids(pair<int,int> & nids,rect * R,int type)2114 int mondrian::get_note_ids (pair<int, int>& nids, rect* R, int type) {
2115 
2116   pair<float, float> intervals;
2117 
2118   if (type == split::VERTICAL)
2119 		R->get_vertical_interval (intervals, root);
2120   else
2121 		R->get_horizontal_interval (intervals, root);
2122 
2123   get_note_ids_from_intervals (nids, intervals);
2124 
2125 	return ((nids.first != -1) && (nids.second != -1));
2126 
2127 }
2128 
get_note_ids_from_intervals(pair<int,int> & note_ids,const pair<float,float> & intervals)2129 void mondrian::get_note_ids_from_intervals (pair<int, int>& note_ids, const pair<float, float>& intervals) {
2130 
2131 	note_ids.first = note_ids.second = -1;
2132 	int n = scaleinfo.num_notes;
2133 
2134 	static const float E = 0.0001;
2135 
2136 	for (int i = 0; i < n; ++i) {
2137 		const string& name = scaleinfo.notes[i];
2138 		float value = INTERVALS [name];
2139 		float delta = value - intervals.first;
2140 		if (delta > E) {
2141 			note_ids.first = i;
2142 			break;
2143 		}
2144 	}
2145 
2146 	if (note_ids.first != -1) {
2147 		for (int j = note_ids.first; j < n; ++j) {
2148 			const string& name = scaleinfo.notes [j];
2149 			float value = INTERVALS [name];
2150 			float delta = intervals.second - value;
2151 			if (delta > E) {
2152 				note_ids.second = j + 1;
2153 			} else break;
2154 		}
2155 	}
2156 
2157 }
2158 
multi_split_rect(split_data & sd)2159 int mondrian::multi_split_rect (split_data& sd) {
2160 	if (sd.start < sd.end) {
2161     float pos;
2162     if (sd.split_at == split::NOTES) {
2163       int i = sd.start;
2164       const string& name = scaleinfo.notes[i];
2165       float value = INTERVALS [name];
2166       float delta = value - 1.0f;
2167       if (sd.split_type == split::VERTICAL) pos = root->extents.left + delta * root->extents.width;
2168       else pos = root->extents.bottom + delta * root->extents.height;
2169     } else {
2170       if (sd.split_type == split::VERTICAL)
2171         pos = sd.R->extents.left + sd.sz;
2172       else
2173         pos = sd.R->extents.bottom + sd.sz;
2174     }
2175 		split_rect (sd.split_type, pos, sd.R);
2176 		if (sd.lr) sd.lr->push_back (sd.R->child1);
2177 		sd.R = sd.R->child2;
2178 		++sd.start;
2179     ++sd.nsplits;
2180 		return 0;
2181 	} else {
2182     if (sd.lr && sd.R && sd.nsplits) sd.lr->push_back (sd.R);
2183 		if (sd.lis) sd.lis->split_over (sd);
2184 		return 1;
2185 	}
2186 }
2187 
multi_split_rect(int n,int type,rect * B,list<rect * > * lr,split_listener * sl)2188 void mondrian::multi_split_rect (int n, int type, rect* B, list<rect*> *lr, split_listener* sl) { // split into n boxes
2189 	if (B == 0) {
2190 		B = box_under_cursor ();
2191 		if (B == 0) {
2192 			cons << YELLOW << "Cant split as you are outside all boxes :(" << eol;
2193 			return;
2194 		}
2195 	}
2196 	float amt;
2197 	if (type == split::VERTICAL) amt = B->extents.width; else amt = B->extents.height;
2198 	float sz = amt / n;
2199 	if (sz < min_split_size) {
2200 		cons << YELLOW << "Cannot split as the boxes will be too small [" << " < " << min_split_size << " ] :(" << eol;
2201     return;
2202   }
2203 
2204   // 1 split every frame
2205   split_data sd (0, n - 1, split::ANYWHERE, type, B, lr, sl, sz, n);
2206   lsd.push_back (sd);
2207   splitting_rects = 1;
2208 }
2209 
multi_split_rect(int type,rect * B,list<rect * > * lr,split_listener * sl)2210 void mondrian::multi_split_rect (int type, rect* B, list<rect*> *lr, split_listener* sl) { // split at notes
2211 	if (B == 0) B = box_under_cursor ();
2212 	if (B) {
2213     pair<int, int> note_ids;
2214     if (get_note_ids (note_ids, B, type)) {
2215 			// 1 split every frame
2216 			split_data sd (note_ids.first, note_ids.second, split::NOTES, type, B, lr, sl);
2217 			lsd.push_back (sd);
2218 			splitting_rects = 1;
2219 		}
2220 	} else {
2221 		cons << YELLOW << "Cant split as you are outside all boxes :(" << eol;
2222 	}
2223 }
2224 
make_note_grid()2225 void mondrian::make_note_grid () {
2226 	if (root->child1 == 0 && root->child2 == 0) {
2227 		rect* B = root;
2228     columns.clear ();
2229 		multi_split_rect (split::VERTICAL, B, &columns, this); // split on notes along horizontal axis
2230 	} else {
2231 		cons << YELLOW << "Please delete all boxes, cannot create note grid :(" << eol;
2232 	}
2233 }
2234 
make_nxn_grid()2235 void mondrian::make_nxn_grid () {
2236 	if (root->child1 == 0 && root->child2 == 0) {
2237 		rect* B = root;
2238     columns.clear ();
2239 		multi_split_rect (num_boxes, split::VERTICAL, B, &columns, this);
2240 	} else {
2241 		cons << YELLOW << "Please delete all boxes, cannot create box grid :(" << eol;
2242 	}
2243 }
2244 
split_over(split_data & sd)2245 void mondrian::split_over (split_data& sd) {
2246   if (sd.split_at == split::NOTES) {
2247     for (box_iterator s = sd.lr->begin (), t = sd.lr->end (); s != t; ++s) {
2248       multi_split_rect (split::HORIZONTAL, *s); // at notes
2249     }
2250   } else {
2251     for (box_iterator s = sd.lr->begin (), t = sd.lr->end (); s != t; ++s) {
2252       multi_split_rect (sd.n, split::HORIZONTAL, *s); // n x n
2253     }
2254   }
2255 }
2256 
set_ball_param(int what,float v)2257 void mondrian::set_ball_param (int what, float v) {
2258 	list<ball*>& _balls = get_balls ();
2259   for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
2260 		ball* b = *p;
2261 		switch (what) {
2262 			case ball::SET_DECAY_TIME:
2263 				b->decay_time = v;
2264 				break;
2265 			case ball::SET_ATTACK_TIME:
2266 				b->attack_time = v;
2267 				break;
2268 			case ball::SET_VOL_MULT:
2269 				b->vol_mult = v;
2270 		}
2271 	}
2272 }
2273 
2274 
change_ball_vol_mult(spinner<float> & s)2275 void mondrian::change_ball_vol_mult (spinner<float>& s) {
2276 	int n = 0;
2277 	list<ball*>& _balls = get_balls ();
2278   for (balls_iterator p = _balls.begin (), q = _balls.end (); p != q; ++p) {
2279 		ball* b = *p;
2280 		b->vol_mult += s();
2281 		// if (b->vol_mult < 0) b->vol_mult = 0.0f;
2282     sprintf (BUFFER, "Ball %d, volume multiplier = %0.3f", ++n, b->vol_mult);
2283     cons << GREEN << BUFFER << eol;
2284 	}
2285 }
2286 
change_min_voices(int d)2287 void mondrian::change_min_voices (int d) {
2288 	min_voices += d;
2289 	if (min_voices < 1) min_voices = 1;
2290 	cons << YELLOW << "Min Voices = " << min_voices << eol;
2291 	MENU.sp_mondrian_min_voices.set_value (min_voices);
2292 }
2293 
edge_on_root(rect * root,rect ** boxes,int * edges)2294 int edge_on_root (rect* root, rect** boxes, int* edges) {
2295 	float edge_vals_root [rect::nedges] = {root->extents.bottom, root->extents.right, root->extents.top, root->extents.left};
2296 	for (int i = 0; i < 2; ++i) {
2297 		rect* ri = boxes[i];
2298 		int ei = edges[i];
2299 		float edge_vals_ri [rect::nedges] = {ri->extents.bottom, ri->extents.right, ri->extents.top, ri->extents.left};
2300 		if (edge_vals_ri [ei] == edge_vals_root [ei]) return 1;
2301 	}
2302 	return 0;
2303 }
2304 
mark_selected_slits()2305 void mondrian::mark_selected_slits () {
2306 	if (num_selected_slits) {
2307 		glEnable (GL_LINE_STIPPLE);
2308 		glLineStipple (1, 0xF0F0);
2309 		glColor3f (0, 1, 0);
2310 		for (slit_iterator si = selected_slits.begin (), sj = selected_slits.end (); si != sj; ++si) {
2311 			slit* s = *si;
2312 			rect* r = s->boxes[0];
2313 			int e = s->edges[0];
2314 			box<float>& x = r->extents;
2315 			float l [] = {x.bottom, x.right, x.top, x.left};
2316 			int t [] =  {slit::HORIZONTAL, slit::VERTICAL, slit::HORIZONTAL, slit::VERTICAL};
2317 			glBegin (GL_LINES);
2318 			float le = l[e];
2319 			if (t[e] == slit::HORIZONTAL) {
2320 				glVertex2f (s->start, le);
2321 				glVertex2f (s->end, le);
2322 			} else {
2323 				glVertex2f (le, s->start);
2324 				glVertex2f (le, s->end);
2325 			}
2326 			glEnd ();
2327 		}
2328 	}
2329 	glDisable (GL_LINE_STIPPLE);
2330 }
2331 
region_begin()2332 void mondrian::region_begin () {
2333 	rgn.left = rgn.right = win.mousex;
2334 	rgn.bottom = rgn.top = win.mousey;
2335 }
2336 
region_end()2337 void mondrian::region_end () {
2338 	rgn.calc ();
2339 	select_balls (rgn);
2340 }
2341 
region_update()2342 const box<float>& mondrian::region_update () {
2343 	rgn.right = win.mousex;
2344 	rgn.top = win.mousey;
2345 	return rgn;
2346 }
2347 
draw_slit_cutter(int yesno)2348 void draw_slit_cutter (int yesno) {
2349 	mondrian0.draw_slit_cutter = yesno;
2350 }
2351 
2352 
browse_t()2353 mondrian::browse_t::browse_t () {
2354 	n = which = last = 0;
2355 }
2356 
clear()2357 void mondrian::browse_t::clear () {
2358 	n = which = last = 0;
2359 	balls.clear ();
2360 	MENU.ol_browse_balls.set_text ("");
2361 }
2362