1use crate::basics::{
13 is_stop, VertexSource, PATH_CMD_LINE_TO, PATH_CMD_MOVE_TO, PATH_CMD_STOP,
14};
15use crate::bspline::Bspline;
16use crate::color::Rgba8;
17use crate::conv_stroke::ConvStroke;
18use crate::ellipse::Ellipse;
19use crate::gsv_text::GsvText;
20use crate::math_stroke::{LineCap, LineJoin};
21use crate::path_storage::PathStorage;
22use crate::pixfmt_rgba::PixfmtRgba32;
23use crate::rasterizer_scanline_aa::RasterizerScanlineAa;
24use crate::renderer_base::RendererBase;
25use crate::renderer_scanline::render_scanlines_aa_solid;
26use crate::scanline_u::ScanlineU8;
27
28pub fn render_ctrl(
36 ras: &mut RasterizerScanlineAa,
37 sl: &mut ScanlineU8,
38 ren: &mut RendererBase<PixfmtRgba32>,
39 ctrl: &mut dyn Ctrl,
40) {
41 for i in 0..ctrl.num_paths() {
42 ras.reset();
43 ras.add_path(ctrl, i);
44 render_scanlines_aa_solid(ras, sl, ren, &ctrl.color(i));
45 }
46}
47
48pub trait Ctrl: VertexSource {
50 fn num_paths(&self) -> u32;
51 fn color(&self, path_id: u32) -> Rgba8;
52}
53
54fn sprintf_format(fmt: &str, value: f64) -> String {
63 let mut result = String::with_capacity(fmt.len() + 16);
64 let bytes = fmt.as_bytes();
65 let mut i = 0;
66 let mut formatted = false;
67
68 while i < bytes.len() {
69 if !formatted && bytes[i] == b'%' {
70 let start = i;
72 i += 1; if i < bytes.len() && bytes[i] == b'%' {
74 result.push('%');
76 i += 1;
77 continue;
78 }
79 while i < bytes.len() && matches!(bytes[i], b'-' | b'+' | b'0' | b' ') {
81 i += 1;
82 }
83 while i < bytes.len() && bytes[i].is_ascii_digit() {
85 i += 1;
86 }
87 let mut precision: Option<usize> = None;
89 if i < bytes.len() && bytes[i] == b'.' {
90 i += 1;
91 let prec_start = i;
92 while i < bytes.len() && bytes[i].is_ascii_digit() {
93 i += 1;
94 }
95 if i > prec_start {
96 precision = fmt[prec_start..i].parse().ok();
97 } else {
98 precision = Some(0);
99 }
100 }
101 if i < bytes.len() {
103 match bytes[i] {
104 b'f' | b'F' => {
105 let p = precision.unwrap_or(6);
106 result.push_str(&format!("{:.prec$}", value, prec = p));
107 i += 1;
108 formatted = true;
109 }
110 b'd' | b'i' => {
111 result.push_str(&format!("{}", value as i64));
112 i += 1;
113 formatted = true;
114 }
115 b'e' | b'E' => {
116 let p = precision.unwrap_or(6);
117 result.push_str(&format!("{:.prec$e}", value, prec = p));
118 i += 1;
119 formatted = true;
120 }
121 b'g' | b'G' => {
122 let p = precision.unwrap_or(6);
123 let s = format!("{:.prec$}", value, prec = p);
125 let e = format!("{:.prec$e}", value, prec = p);
126 result.push_str(if s.len() <= e.len() { &s } else { &e });
127 i += 1;
128 formatted = true;
129 }
130 _ => {
131 result.push_str(&fmt[start..=i]);
133 i += 1;
134 }
135 }
136 } else {
137 result.push('%');
139 }
140 } else {
141 result.push(bytes[i] as char);
142 i += 1;
143 }
144 }
145 result
146}
147
148pub struct SliderCtrl {
159 x1: f64,
161 y1: f64,
162 x2: f64,
163 y2: f64,
164 xs1: f64,
166 ys1: f64,
167 xs2: f64,
168 ys2: f64,
169 value: f64,
171 preview_value: f64,
172 min: f64,
173 max: f64,
174 label: String,
176 border_width: f64,
178 border_extra: f64,
179 text_thickness: f64,
180 num_steps: u32,
181 descending: bool,
182 colors: [Rgba8; 6],
184 vertices: Vec<(f64, f64, u32)>,
186 vertex_idx: usize,
187}
188
189impl SliderCtrl {
190 pub fn new(x1: f64, y1: f64, x2: f64, y2: f64) -> Self {
191 let border_extra = (y2 - y1) / 2.0;
192 let mut s = Self {
193 x1,
194 y1,
195 x2,
196 y2,
197 xs1: 0.0,
198 ys1: 0.0,
199 xs2: 0.0,
200 ys2: 0.0,
201 value: 0.5,
202 preview_value: 0.5,
203 min: 0.0,
204 max: 1.0,
205 label: String::new(),
206 border_width: 1.0,
207 border_extra,
208 text_thickness: 1.0,
209 num_steps: 0,
210 descending: false,
211 colors: [
212 Rgba8::new(255, 230, 204, 255), Rgba8::new(179, 153, 153, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(153, 102, 102, 102), Rgba8::new(204, 0, 0, 153), Rgba8::new(0, 0, 0, 255), ],
219 vertices: Vec::new(),
220 vertex_idx: 0,
221 };
222 s.calc_box();
223 s
224 }
225
226 fn calc_box(&mut self) {
227 self.xs1 = self.x1 + self.border_width;
228 self.ys1 = self.y1 + self.border_width;
229 self.xs2 = self.x2 - self.border_width;
230 self.ys2 = self.y2 - self.border_width;
231 }
232
233 pub fn label(&mut self, fmt: &str) {
235 self.label = fmt.to_string();
236 }
237
238 pub fn range(&mut self, min: f64, max: f64) {
240 self.min = min;
241 self.max = max;
242 }
243
244 pub fn value(&self) -> f64 {
246 self.value * (self.max - self.min) + self.min
247 }
248
249 pub fn set_value(&mut self, v: f64) {
251 self.preview_value = ((v - self.min) / (self.max - self.min)).clamp(0.0, 1.0);
252 self.normalize_value(true);
253 }
254
255 pub fn num_steps(&mut self, n: u32) {
257 self.num_steps = n;
258 }
259
260 pub fn set_descending(&mut self, d: bool) {
261 self.descending = d;
262 }
263
264 pub fn border_width(&mut self, t: f64, extra: f64) {
265 self.border_width = t;
266 self.border_extra = extra;
267 self.calc_box();
268 }
269
270 pub fn text_thickness(&mut self, t: f64) {
271 self.text_thickness = t;
272 }
273
274 fn normalize_value(&mut self, preview_value_flag: bool) {
275 if self.num_steps > 0 {
276 let step = (self.preview_value * self.num_steps as f64 + 0.5) as i32;
277 self.value = step as f64 / self.num_steps as f64;
278 } else {
279 self.value = self.preview_value;
280 }
281 if preview_value_flag {
282 self.preview_value = self.value;
283 }
284 }
285
286 fn calc_background(&mut self) {
288 let (x1, y1, x2, y2) = (self.x1, self.y1, self.x2, self.y2);
289 let be = self.border_extra;
290 self.vertices.clear();
291 self.vertices.push((x1 - be, y1 - be, PATH_CMD_MOVE_TO));
292 self.vertices.push((x2 + be, y1 - be, PATH_CMD_LINE_TO));
293 self.vertices.push((x2 + be, y2 + be, PATH_CMD_LINE_TO));
294 self.vertices.push((x1 - be, y2 + be, PATH_CMD_LINE_TO));
295 }
296
297 fn calc_triangle(&mut self) {
299 self.vertices.clear();
300 if self.descending {
301 self.vertices.push((self.x1, self.y1, PATH_CMD_MOVE_TO));
302 self.vertices.push((self.x2, self.y1, PATH_CMD_LINE_TO));
303 self.vertices.push((self.x1, self.y2, PATH_CMD_LINE_TO));
304 self.vertices.push((self.x1, self.y1, PATH_CMD_LINE_TO));
305 } else {
306 self.vertices.push((self.x1, self.y1, PATH_CMD_MOVE_TO));
307 self.vertices.push((self.x2, self.y1, PATH_CMD_LINE_TO));
308 self.vertices.push((self.x2, self.y2, PATH_CMD_LINE_TO));
309 self.vertices.push((self.x1, self.y1, PATH_CMD_LINE_TO));
310 }
311 }
312
313 fn calc_text(&mut self) {
315 self.vertices.clear();
316
317 let text = if self.label.contains('%') {
318 sprintf_format(&self.label, self.value())
321 } else if self.label.is_empty() {
322 return;
323 } else {
324 self.label.clone()
325 };
326
327 let text_height = self.y2 - self.y1;
328 let mut txt = GsvText::new();
329 txt.start_point(self.x1, self.y1);
330 txt.size(text_height * 1.2, text_height);
331 txt.text(&text);
332
333 let mut stroke = ConvStroke::new(txt);
334 stroke.set_width(self.text_thickness);
335 stroke.set_line_join(LineJoin::Round);
336 stroke.set_line_cap(LineCap::Round);
337
338 stroke.rewind(0);
339 loop {
340 let (mut x, mut y) = (0.0, 0.0);
341 let cmd = stroke.vertex(&mut x, &mut y);
342 if is_stop(cmd) {
343 break;
344 }
345 self.vertices.push((x, y, cmd));
346 }
347 }
348
349 fn calc_pointer_preview(&mut self) {
351 self.vertices.clear();
352 let cx = self.xs1 + (self.xs2 - self.xs1) * self.preview_value;
353 let cy = (self.ys1 + self.ys2) / 2.0;
354 let r = self.y2 - self.y1;
355
356 let mut ell = Ellipse::new(cx, cy, r, r, 32, false);
357 ell.rewind(0);
358 loop {
359 let (mut x, mut y) = (0.0, 0.0);
360 let cmd = ell.vertex(&mut x, &mut y);
361 if is_stop(cmd) {
362 break;
363 }
364 self.vertices.push((x, y, cmd));
365 }
366 }
367
368 fn calc_pointer(&mut self) {
370 self.normalize_value(false);
371 self.vertices.clear();
372 let cx = self.xs1 + (self.xs2 - self.xs1) * self.value;
373 let cy = (self.ys1 + self.ys2) / 2.0;
374 let r = self.y2 - self.y1;
375
376 let mut ell = Ellipse::new(cx, cy, r, r, 32, false);
377 ell.rewind(0);
378 loop {
379 let (mut x, mut y) = (0.0, 0.0);
380 let cmd = ell.vertex(&mut x, &mut y);
381 if is_stop(cmd) {
382 break;
383 }
384 self.vertices.push((x, y, cmd));
385 }
386 }
387
388 fn calc_steps(&mut self) {
390 self.vertices.clear();
391 if self.num_steps == 0 {
392 return;
393 }
394 let mut d = (self.xs2 - self.xs1) / self.num_steps as f64;
395 if d > 0.004 {
396 d = 0.004;
397 }
398 for i in 0..=self.num_steps {
399 let x = self.xs1 + (self.xs2 - self.xs1) * i as f64 / self.num_steps as f64;
400 self.vertices.push((x, self.y1, PATH_CMD_MOVE_TO));
401 self.vertices
402 .push((x - d * (self.x2 - self.x1), self.y1 - self.border_extra, PATH_CMD_LINE_TO));
403 self.vertices
404 .push((x + d * (self.x2 - self.x1), self.y1 - self.border_extra, PATH_CMD_LINE_TO));
405 }
406 }
407}
408
409impl Ctrl for SliderCtrl {
410 fn num_paths(&self) -> u32 {
411 6
412 }
413
414 fn color(&self, path_id: u32) -> Rgba8 {
415 self.colors[path_id.min(5) as usize]
416 }
417}
418
419impl VertexSource for SliderCtrl {
420 fn rewind(&mut self, path_id: u32) {
421 self.vertex_idx = 0;
422 match path_id {
423 0 => self.calc_background(),
424 1 => self.calc_triangle(),
425 2 => self.calc_text(),
426 3 => self.calc_pointer_preview(),
427 4 => self.calc_pointer(),
428 5 => self.calc_steps(),
429 _ => {
430 self.vertices.clear();
431 }
432 }
433 }
434
435 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
436 if self.vertex_idx < self.vertices.len() {
437 let (vx, vy, cmd) = self.vertices[self.vertex_idx];
438 *x = vx;
439 *y = vy;
440 self.vertex_idx += 1;
441 cmd
442 } else {
443 PATH_CMD_STOP
444 }
445 }
446}
447
448pub struct ScaleCtrl {
456 x1: f64,
457 y1: f64,
458 x2: f64,
459 y2: f64,
460 xs1: f64,
461 ys1: f64,
462 xs2: f64,
463 ys2: f64,
464 border_width: f64,
465 border_extra: f64,
466 value1: f64,
467 value2: f64,
468 min_delta: f64,
469 colors: [Rgba8; 5],
470 vertices: Vec<(f64, f64, u32)>,
471 vertex_idx: usize,
472}
473
474impl ScaleCtrl {
475 pub fn new(x1: f64, y1: f64, x2: f64, y2: f64) -> Self {
476 let mut s = Self {
477 x1,
478 y1,
479 x2,
480 y2,
481 xs1: 0.0,
482 ys1: 0.0,
483 xs2: 0.0,
484 ys2: 0.0,
485 border_width: 1.0,
486 border_extra: (y2 - y1) * 0.5,
487 value1: 0.3,
488 value2: 0.7,
489 min_delta: 0.01,
490 colors: [
491 Rgba8::new(255, 230, 204, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(180, 120, 120, 180), Rgba8::new(204, 0, 0, 180), Rgba8::new(204, 0, 0, 180), ],
497 vertices: Vec::new(),
498 vertex_idx: 0,
499 };
500 s.calc_box();
501 s
502 }
503
504 fn calc_box(&mut self) {
505 self.xs1 = self.x1 + self.border_width;
506 self.ys1 = self.y1 + self.border_width;
507 self.xs2 = self.x2 - self.border_width;
508 self.ys2 = self.y2 - self.border_width;
509 }
510
511 pub fn set_min_delta(&mut self, d: f64) {
512 self.min_delta = d.clamp(0.0, 1.0);
513 self.set_value1(self.value1);
514 self.set_value2(self.value2);
515 }
516
517 pub fn value1(&self) -> f64 {
518 self.value1
519 }
520
521 pub fn value2(&self) -> f64 {
522 self.value2
523 }
524
525 pub fn set_value1(&mut self, v: f64) {
526 let max_v1 = (self.value2 - self.min_delta).clamp(0.0, 1.0);
527 self.value1 = v.clamp(0.0, max_v1);
528 }
529
530 pub fn set_value2(&mut self, v: f64) {
531 let min_v2 = (self.value1 + self.min_delta).clamp(0.0, 1.0);
532 self.value2 = v.clamp(min_v2, 1.0);
533 }
534
535 fn calc_background(&mut self) {
536 self.vertices.clear();
537 let be = self.border_extra;
538 self.vertices
539 .push((self.x1 - be, self.y1 - be, PATH_CMD_MOVE_TO));
540 self.vertices
541 .push((self.x2 + be, self.y1 - be, PATH_CMD_LINE_TO));
542 self.vertices
543 .push((self.x2 + be, self.y2 + be, PATH_CMD_LINE_TO));
544 self.vertices
545 .push((self.x1 - be, self.y2 + be, PATH_CMD_LINE_TO));
546 }
547
548 fn calc_border(&mut self) {
549 self.vertices.clear();
550 self.vertices.push((self.x1, self.y1, PATH_CMD_MOVE_TO));
551 self.vertices.push((self.x2, self.y1, PATH_CMD_LINE_TO));
552 self.vertices.push((self.x2, self.y2, PATH_CMD_LINE_TO));
553 self.vertices.push((self.x1, self.y2, PATH_CMD_LINE_TO));
554 self.vertices.push((
555 self.x1 + self.border_width,
556 self.y1 + self.border_width,
557 PATH_CMD_MOVE_TO,
558 ));
559 self.vertices.push((
560 self.x1 + self.border_width,
561 self.y2 - self.border_width,
562 PATH_CMD_LINE_TO,
563 ));
564 self.vertices.push((
565 self.x2 - self.border_width,
566 self.y2 - self.border_width,
567 PATH_CMD_LINE_TO,
568 ));
569 self.vertices.push((
570 self.x2 - self.border_width,
571 self.y1 + self.border_width,
572 PATH_CMD_LINE_TO,
573 ));
574 }
575
576 fn calc_selected_range(&mut self) {
577 self.vertices.clear();
578 let x1 = self.xs1 + (self.xs2 - self.xs1) * self.value1;
579 let x2 = self.xs1 + (self.xs2 - self.xs1) * self.value2;
580 self.vertices.push((x1, self.ys1, PATH_CMD_MOVE_TO));
581 self.vertices.push((x2, self.ys1, PATH_CMD_LINE_TO));
582 self.vertices.push((x2, self.ys2, PATH_CMD_LINE_TO));
583 self.vertices.push((x1, self.ys2, PATH_CMD_LINE_TO));
584 }
585
586 fn calc_handle(&mut self, v: f64) {
587 self.vertices.clear();
588 let cx = self.xs1 + (self.xs2 - self.xs1) * v;
589 let cy = (self.ys1 + self.ys2) * 0.5;
590 let r = (self.y2 - self.y1).max(1.0);
591 let mut ell = Ellipse::new(cx, cy, r, r, 32, false);
592 ell.rewind(0);
593 loop {
594 let (mut x, mut y) = (0.0, 0.0);
595 let cmd = ell.vertex(&mut x, &mut y);
596 if is_stop(cmd) {
597 break;
598 }
599 self.vertices.push((x, y, cmd));
600 }
601 }
602}
603
604impl Ctrl for ScaleCtrl {
605 fn num_paths(&self) -> u32 {
606 5
607 }
608
609 fn color(&self, path_id: u32) -> Rgba8 {
610 self.colors[path_id.min(4) as usize]
611 }
612}
613
614impl VertexSource for ScaleCtrl {
615 fn rewind(&mut self, path_id: u32) {
616 self.vertex_idx = 0;
617 match path_id {
618 0 => self.calc_background(),
619 1 => self.calc_border(),
620 2 => self.calc_selected_range(),
621 3 => self.calc_handle(self.value1),
622 4 => self.calc_handle(self.value2),
623 _ => self.vertices.clear(),
624 }
625 }
626
627 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
628 if self.vertex_idx < self.vertices.len() {
629 let (vx, vy, cmd) = self.vertices[self.vertex_idx];
630 *x = vx;
631 *y = vy;
632 self.vertex_idx += 1;
633 cmd
634 } else {
635 PATH_CMD_STOP
636 }
637 }
638}
639
640pub struct CboxCtrl {
650 x1: f64,
651 y1: f64,
652 x2: f64,
653 y2: f64,
654 text_thickness: f64,
655 text_height: f64,
656 text_width: f64,
657 label: String,
658 status: bool,
659 colors: [Rgba8; 3],
660 vertices: Vec<(f64, f64, u32)>,
661 vertex_idx: usize,
662}
663
664impl CboxCtrl {
665 pub fn new(x: f64, y: f64, label: &str) -> Self {
666 let text_height = 9.0;
667 Self {
668 x1: x,
669 y1: y,
670 x2: x + text_height * 1.5,
671 y2: y + text_height * 1.5,
672 text_thickness: 1.5,
673 text_height,
674 text_width: 0.0,
675 label: label.to_string(),
676 status: false,
677 colors: [
678 Rgba8::new(0, 0, 0, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(102, 0, 0, 255), ],
682 vertices: Vec::new(),
683 vertex_idx: 0,
684 }
685 }
686
687 pub fn set_status(&mut self, s: bool) {
688 self.status = s;
689 }
690
691 pub fn status(&self) -> bool {
692 self.status
693 }
694
695 pub fn text_size(&mut self, h: f64, w: f64) {
696 self.text_height = h;
697 self.text_width = w;
698 }
699
700 fn calc_border(&mut self) {
702 self.vertices.clear();
703 let t = self.text_thickness;
704 self.vertices.push((self.x1, self.y1, PATH_CMD_MOVE_TO));
706 self.vertices.push((self.x2, self.y1, PATH_CMD_LINE_TO));
707 self.vertices.push((self.x2, self.y2, PATH_CMD_LINE_TO));
708 self.vertices.push((self.x1, self.y2, PATH_CMD_LINE_TO));
709 self.vertices.push((self.x1 + t, self.y1 + t, PATH_CMD_MOVE_TO));
711 self.vertices.push((self.x1 + t, self.y2 - t, PATH_CMD_LINE_TO));
712 self.vertices.push((self.x2 - t, self.y2 - t, PATH_CMD_LINE_TO));
713 self.vertices.push((self.x2 - t, self.y1 + t, PATH_CMD_LINE_TO));
714 }
715
716 fn calc_text(&mut self) {
718 self.vertices.clear();
719 let mut txt = GsvText::new();
720 txt.start_point(
721 self.x1 + self.text_height * 2.0,
722 self.y1 + self.text_height / 5.0,
723 );
724 txt.size(self.text_height, self.text_width);
725 txt.text(&self.label);
726
727 let mut stroke = ConvStroke::new(txt);
728 stroke.set_width(self.text_thickness);
729 stroke.set_line_join(LineJoin::Round);
730 stroke.set_line_cap(LineCap::Round);
731
732 stroke.rewind(0);
733 loop {
734 let (mut x, mut y) = (0.0, 0.0);
735 let cmd = stroke.vertex(&mut x, &mut y);
736 if is_stop(cmd) {
737 break;
738 }
739 self.vertices.push((x, y, cmd));
740 }
741 }
742
743 fn calc_checkmark(&mut self) {
745 self.vertices.clear();
746 if !self.status {
747 return;
748 }
749 let d2 = (self.y2 - self.y1) / 2.0;
750 let t = self.text_thickness * 1.5;
751 self.vertices.push((self.x1 + self.text_thickness, self.y1 + self.text_thickness, PATH_CMD_MOVE_TO));
753 self.vertices.push((self.x1 + d2, self.y1 + d2 - t, PATH_CMD_LINE_TO));
754 self.vertices.push((self.x2 - self.text_thickness, self.y1 + self.text_thickness, PATH_CMD_LINE_TO));
755 self.vertices.push((self.x1 + d2 + t, self.y1 + d2, PATH_CMD_LINE_TO));
756 self.vertices.push((self.x2 - self.text_thickness, self.y2 - self.text_thickness, PATH_CMD_LINE_TO));
757 self.vertices.push((self.x1 + d2, self.y1 + d2 + t, PATH_CMD_LINE_TO));
758 self.vertices.push((self.x1 + self.text_thickness, self.y2 - self.text_thickness, PATH_CMD_LINE_TO));
759 self.vertices.push((self.x1 + d2 - t, self.y1 + d2, PATH_CMD_LINE_TO));
760 }
761}
762
763impl Ctrl for CboxCtrl {
764 fn num_paths(&self) -> u32 {
765 3
766 }
767
768 fn color(&self, path_id: u32) -> Rgba8 {
769 self.colors[path_id.min(2) as usize]
770 }
771}
772
773impl VertexSource for CboxCtrl {
774 fn rewind(&mut self, path_id: u32) {
775 self.vertex_idx = 0;
776 match path_id {
777 0 => self.calc_border(),
778 1 => self.calc_text(),
779 2 => self.calc_checkmark(),
780 _ => {
781 self.vertices.clear();
782 }
783 }
784 }
785
786 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
787 if self.vertex_idx < self.vertices.len() {
788 let (vx, vy, cmd) = self.vertices[self.vertex_idx];
789 *x = vx;
790 *y = vy;
791 self.vertex_idx += 1;
792 cmd
793 } else {
794 PATH_CMD_STOP
795 }
796 }
797}
798
799pub struct RboxCtrl {
810 x1: f64,
811 y1: f64,
812 x2: f64,
813 y2: f64,
814 xs1: f64,
815 ys1: f64,
816 xs2: f64,
817 ys2: f64,
818 border_width: f64,
819 border_extra: f64,
820 text_thickness: f64,
821 text_height: f64,
822 text_width: f64,
823 items: Vec<String>,
824 cur_item: i32,
825 dy: f64,
826 colors: [Rgba8; 5],
827 vertices: Vec<(f64, f64, u32)>,
828 vertex_idx: usize,
829}
830
831impl RboxCtrl {
832 pub fn new(x1: f64, y1: f64, x2: f64, y2: f64) -> Self {
833 let mut r = Self {
834 x1,
835 y1,
836 x2,
837 y2,
838 xs1: 0.0,
839 ys1: 0.0,
840 xs2: 0.0,
841 ys2: 0.0,
842 border_width: 1.0,
843 border_extra: 0.0,
844 text_thickness: 1.5,
845 text_height: 9.0,
846 text_width: 0.0,
847 items: Vec::new(),
848 cur_item: -1,
849 dy: 18.0,
850 colors: [
851 Rgba8::new(255, 255, 230, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(102, 0, 0, 255), ],
857 vertices: Vec::new(),
858 vertex_idx: 0,
859 };
860 r.calc_rbox();
861 r
862 }
863
864 fn calc_rbox(&mut self) {
865 self.xs1 = self.x1 + self.border_width;
866 self.ys1 = self.y1 + self.border_width;
867 self.xs2 = self.x2 - self.border_width;
868 self.ys2 = self.y2 - self.border_width;
869 }
870
871 pub fn add_item(&mut self, text: &str) {
872 self.items.push(text.to_string());
873 }
874
875 pub fn cur_item(&self) -> i32 {
876 self.cur_item
877 }
878
879 pub fn set_cur_item(&mut self, i: i32) {
880 self.cur_item = i;
881 }
882
883 pub fn text_size(&mut self, h: f64, w: f64) {
884 self.text_height = h;
885 self.text_width = w;
886 }
887
888 pub fn border_width(&mut self, t: f64, extra: f64) {
889 self.border_width = t;
890 self.border_extra = extra;
891 self.calc_rbox();
892 }
893
894 pub fn text_thickness(&mut self, t: f64) {
895 self.text_thickness = t;
896 }
897
898 pub fn background_color(&mut self, c: Rgba8) {
899 self.colors[0] = c;
900 }
901
902 fn calc_background(&mut self) {
904 self.vertices.clear();
905 let be = self.border_extra;
906 self.vertices.push((self.x1 - be, self.y1 - be, PATH_CMD_MOVE_TO));
907 self.vertices.push((self.x2 + be, self.y1 - be, PATH_CMD_LINE_TO));
908 self.vertices.push((self.x2 + be, self.y2 + be, PATH_CMD_LINE_TO));
909 self.vertices.push((self.x1 - be, self.y2 + be, PATH_CMD_LINE_TO));
910 }
911
912 fn calc_border(&mut self) {
914 self.vertices.clear();
915 let bw = self.border_width;
916 self.vertices.push((self.x1, self.y1, PATH_CMD_MOVE_TO));
918 self.vertices.push((self.x2, self.y1, PATH_CMD_LINE_TO));
919 self.vertices.push((self.x2, self.y2, PATH_CMD_LINE_TO));
920 self.vertices.push((self.x1, self.y2, PATH_CMD_LINE_TO));
921 self.vertices.push((self.x1 + bw, self.y1 + bw, PATH_CMD_MOVE_TO));
923 self.vertices.push((self.x1 + bw, self.y2 - bw, PATH_CMD_LINE_TO));
924 self.vertices.push((self.x2 - bw, self.y2 - bw, PATH_CMD_LINE_TO));
925 self.vertices.push((self.x2 - bw, self.y1 + bw, PATH_CMD_LINE_TO));
926 }
927
928 fn calc_text(&mut self) {
930 self.vertices.clear();
931 let dy = self.text_height * 2.0;
932
933 for (i, item) in self.items.iter().enumerate() {
934 let mut txt = GsvText::new();
935 txt.start_point(self.xs1 + dy * 1.5, self.ys1 + dy * (i as f64 + 1.0) - dy / 2.0);
936 txt.size(self.text_height, self.text_width);
937 txt.text(item);
938
939 let mut stroke = ConvStroke::new(txt);
940 stroke.set_width(self.text_thickness);
941 stroke.set_line_join(LineJoin::Round);
942 stroke.set_line_cap(LineCap::Round);
943
944 stroke.rewind(0);
945 loop {
946 let (mut x, mut y) = (0.0, 0.0);
947 let cmd = stroke.vertex(&mut x, &mut y);
948 if is_stop(cmd) {
949 break;
950 }
951 self.vertices.push((x, y, cmd));
952 }
953 }
954 }
955
956 fn calc_inactive_circles(&mut self) {
958 self.vertices.clear();
959 let dy = self.text_height * 2.0;
960 let r = self.text_height / 1.5;
961
962 for i in 0..self.items.len() {
963 let cx = self.xs1 + dy / 1.3;
964 let cy = self.ys1 + dy * i as f64 + dy / 1.3;
965
966 let mut ell = Ellipse::new(cx, cy, r, r, 32, false);
967 let mut stroke = ConvStroke::new(&mut ell);
968 stroke.set_width(self.text_thickness);
969
970 stroke.rewind(0);
971 loop {
972 let (mut x, mut y) = (0.0, 0.0);
973 let cmd = stroke.vertex(&mut x, &mut y);
974 if is_stop(cmd) {
975 break;
976 }
977 self.vertices.push((x, y, cmd));
978 }
979 }
980 }
981
982 fn calc_active_circle(&mut self) {
984 self.vertices.clear();
985 if self.cur_item < 0 {
986 return;
987 }
988 let dy = self.text_height * 2.0;
989 let cx = self.xs1 + dy / 1.3;
990 let cy = self.ys1 + dy * self.cur_item as f64 + dy / 1.3;
991 let r = self.text_height / 2.0;
992
993 let mut ell = Ellipse::new(cx, cy, r, r, 32, false);
994 ell.rewind(0);
995 loop {
996 let (mut x, mut y) = (0.0, 0.0);
997 let cmd = ell.vertex(&mut x, &mut y);
998 if is_stop(cmd) {
999 break;
1000 }
1001 self.vertices.push((x, y, cmd));
1002 }
1003 }
1004}
1005
1006impl Ctrl for RboxCtrl {
1007 fn num_paths(&self) -> u32 {
1008 5
1009 }
1010
1011 fn color(&self, path_id: u32) -> Rgba8 {
1012 self.colors[path_id.min(4) as usize]
1013 }
1014}
1015
1016impl VertexSource for RboxCtrl {
1017 fn rewind(&mut self, path_id: u32) {
1018 self.vertex_idx = 0;
1019 self.dy = self.text_height * 2.0;
1020 match path_id {
1021 0 => self.calc_background(),
1022 1 => self.calc_border(),
1023 2 => self.calc_text(),
1024 3 => self.calc_inactive_circles(),
1025 4 => self.calc_active_circle(),
1026 _ => {
1027 self.vertices.clear();
1028 }
1029 }
1030 }
1031
1032 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
1033 if self.vertex_idx < self.vertices.len() {
1034 let (vx, vy, cmd) = self.vertices[self.vertex_idx];
1035 *x = vx;
1036 *y = vy;
1037 self.vertex_idx += 1;
1038 cmd
1039 } else {
1040 PATH_CMD_STOP
1041 }
1042 }
1043}
1044
1045pub struct SplineCtrl {
1055 x1: f64,
1056 y1: f64,
1057 x2: f64,
1058 y2: f64,
1059 num_pnt: usize,
1060 xp: [f64; 32],
1061 yp: [f64; 32],
1062 spline_values: [f64; 256],
1063 spline_values8: [u8; 256],
1064 border_width: f64,
1065 border_extra: f64,
1066 curve_width: f64,
1067 point_size: f64,
1068 xs1: f64,
1069 ys1: f64,
1070 xs2: f64,
1071 ys2: f64,
1072 active_pnt: i32,
1073 colors: [Rgba8; 5],
1074 vertices: Vec<(f64, f64, u32)>,
1075 vertex_idx: usize,
1076}
1077
1078impl SplineCtrl {
1079 pub fn new(x1: f64, y1: f64, x2: f64, y2: f64, num_pnt: usize) -> Self {
1080 let n = num_pnt.clamp(4, 32);
1081 let mut s = Self {
1082 x1,
1083 y1,
1084 x2,
1085 y2,
1086 num_pnt: n,
1087 xp: [0.0; 32],
1088 yp: [0.0; 32],
1089 spline_values: [0.0; 256],
1090 spline_values8: [0; 256],
1091 border_width: 1.0,
1092 border_extra: 0.0,
1093 curve_width: 1.0,
1094 point_size: 3.0,
1095 xs1: 0.0,
1096 ys1: 0.0,
1097 xs2: 0.0,
1098 ys2: 0.0,
1099 active_pnt: -1,
1100 colors: [
1101 Rgba8::new(255, 255, 230, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(255, 0, 0, 255), ],
1107 vertices: Vec::new(),
1108 vertex_idx: 0,
1109 };
1110 for i in 0..s.num_pnt {
1111 s.xp[i] = i as f64 / (s.num_pnt - 1) as f64;
1112 s.yp[i] = 0.5;
1113 }
1114 s.calc_spline_box();
1115 s.update_spline();
1116 s
1117 }
1118
1119 pub fn border_width(&mut self, t: f64, extra: f64) {
1120 self.border_width = t;
1121 self.border_extra = extra;
1122 self.calc_spline_box();
1123 }
1124
1125 pub fn curve_width(&mut self, t: f64) {
1126 self.curve_width = t;
1127 }
1128
1129 pub fn point_size(&mut self, s: f64) {
1130 self.point_size = s;
1131 }
1132
1133 pub fn active_point(&mut self, i: i32) {
1134 self.active_pnt = i;
1135 }
1136
1137 pub fn background_color(&mut self, c: Rgba8) {
1138 self.colors[0] = c;
1139 }
1140
1141 pub fn border_color(&mut self, c: Rgba8) {
1142 self.colors[1] = c;
1143 }
1144
1145 pub fn curve_color(&mut self, c: Rgba8) {
1146 self.colors[2] = c;
1147 }
1148
1149 pub fn inactive_pnt_color(&mut self, c: Rgba8) {
1150 self.colors[3] = c;
1151 }
1152
1153 pub fn active_pnt_color(&mut self, c: Rgba8) {
1154 self.colors[4] = c;
1155 }
1156
1157 fn calc_spline_box(&mut self) {
1158 self.xs1 = self.x1 + self.border_width;
1159 self.ys1 = self.y1 + self.border_width;
1160 self.xs2 = self.x2 - self.border_width;
1161 self.ys2 = self.y2 - self.border_width;
1162 }
1163
1164 fn set_xp(&mut self, idx: usize, mut val: f64) {
1165 val = val.clamp(0.0, 1.0);
1166 if idx == 0 {
1167 val = 0.0;
1168 } else if idx == self.num_pnt - 1 {
1169 val = 1.0;
1170 } else {
1171 if val < self.xp[idx - 1] + 0.001 {
1172 val = self.xp[idx - 1] + 0.001;
1173 }
1174 if val > self.xp[idx + 1] - 0.001 {
1175 val = self.xp[idx + 1] - 0.001;
1176 }
1177 }
1178 self.xp[idx] = val;
1179 }
1180
1181 fn set_yp(&mut self, idx: usize, val: f64) {
1182 self.yp[idx] = val.clamp(0.0, 1.0);
1183 }
1184
1185 pub fn point(&mut self, idx: usize, x: f64, y: f64) {
1186 if idx < self.num_pnt {
1187 self.set_xp(idx, x);
1188 self.set_yp(idx, y);
1189 }
1190 }
1191
1192 pub fn value(&self, x: f64) -> f64 {
1193 let mut s = Bspline::new();
1194 s.init(&self.xp[..self.num_pnt], &self.yp[..self.num_pnt]);
1195 s.get(x).clamp(0.0, 1.0)
1196 }
1197
1198 pub fn value_at_point(&mut self, idx: usize, y: f64) {
1199 if idx < self.num_pnt {
1200 self.set_yp(idx, y);
1201 }
1202 }
1203
1204 pub fn x(&self, idx: usize) -> f64 {
1205 self.xp[idx]
1206 }
1207
1208 pub fn y(&self, idx: usize) -> f64 {
1209 self.yp[idx]
1210 }
1211
1212 pub fn update_spline(&mut self) {
1213 let mut spline = Bspline::new();
1214 spline.init(&self.xp[..self.num_pnt], &self.yp[..self.num_pnt]);
1215 for i in 0..256 {
1216 let v = spline.get(i as f64 / 255.0).clamp(0.0, 1.0);
1217 self.spline_values[i] = v;
1218 self.spline_values8[i] = (v * 255.0) as u8;
1219 }
1220 }
1221
1222 pub fn spline(&self) -> &[f64; 256] {
1223 &self.spline_values
1224 }
1225
1226 pub fn spline8(&self) -> &[u8; 256] {
1227 &self.spline_values8
1228 }
1229
1230 fn calc_xp(&self, idx: usize) -> f64 {
1231 self.xs1 + (self.xs2 - self.xs1) * self.xp[idx]
1232 }
1233
1234 fn calc_yp(&self, idx: usize) -> f64 {
1235 self.ys1 + (self.ys2 - self.ys1) * self.yp[idx]
1236 }
1237
1238 fn calc_background(&mut self) {
1239 self.vertices.clear();
1240 let be = self.border_extra;
1241 self.vertices
1242 .push((self.x1 - be, self.y1 - be, PATH_CMD_MOVE_TO));
1243 self.vertices
1244 .push((self.x2 + be, self.y1 - be, PATH_CMD_LINE_TO));
1245 self.vertices
1246 .push((self.x2 + be, self.y2 + be, PATH_CMD_LINE_TO));
1247 self.vertices
1248 .push((self.x1 - be, self.y2 + be, PATH_CMD_LINE_TO));
1249 }
1250
1251 fn calc_border(&mut self) {
1252 self.vertices.clear();
1253 self.vertices.push((self.x1, self.y1, PATH_CMD_MOVE_TO));
1254 self.vertices.push((self.x2, self.y1, PATH_CMD_LINE_TO));
1255 self.vertices.push((self.x2, self.y2, PATH_CMD_LINE_TO));
1256 self.vertices.push((self.x1, self.y2, PATH_CMD_LINE_TO));
1257 self.vertices.push((
1258 self.x1 + self.border_width,
1259 self.y1 + self.border_width,
1260 PATH_CMD_MOVE_TO,
1261 ));
1262 self.vertices.push((
1263 self.x1 + self.border_width,
1264 self.y2 - self.border_width,
1265 PATH_CMD_LINE_TO,
1266 ));
1267 self.vertices.push((
1268 self.x2 - self.border_width,
1269 self.y2 - self.border_width,
1270 PATH_CMD_LINE_TO,
1271 ));
1272 self.vertices.push((
1273 self.x2 - self.border_width,
1274 self.y1 + self.border_width,
1275 PATH_CMD_LINE_TO,
1276 ));
1277 }
1278
1279 fn calc_curve(&mut self) {
1280 self.vertices.clear();
1281 let mut path = PathStorage::new();
1282 path.move_to(self.xs1, self.ys1 + (self.ys2 - self.ys1) * self.spline_values[0]);
1283 for i in 1..256 {
1284 path.line_to(
1285 self.xs1 + (self.xs2 - self.xs1) * i as f64 / 255.0,
1286 self.ys1 + (self.ys2 - self.ys1) * self.spline_values[i],
1287 );
1288 }
1289 let mut stroke = ConvStroke::new(&mut path);
1290 stroke.set_width(self.curve_width);
1291 stroke.rewind(0);
1292 loop {
1293 let (mut x, mut y) = (0.0, 0.0);
1294 let cmd = stroke.vertex(&mut x, &mut y);
1295 if is_stop(cmd) {
1296 break;
1297 }
1298 self.vertices.push((x, y, cmd));
1299 }
1300 }
1301
1302 fn calc_inactive_points(&mut self) {
1303 self.vertices.clear();
1304 for i in 0..self.num_pnt {
1305 if i as i32 == self.active_pnt {
1306 continue;
1307 }
1308 let mut ell = Ellipse::new(
1309 self.calc_xp(i),
1310 self.calc_yp(i),
1311 self.point_size,
1312 self.point_size,
1313 32,
1314 false,
1315 );
1316 ell.rewind(0);
1317 loop {
1318 let (mut x, mut y) = (0.0, 0.0);
1319 let cmd = ell.vertex(&mut x, &mut y);
1320 if is_stop(cmd) {
1321 break;
1322 }
1323 self.vertices.push((x, y, cmd));
1324 }
1325 }
1326 }
1327
1328 fn calc_active_point(&mut self) {
1329 self.vertices.clear();
1330 if self.active_pnt < 0 || self.active_pnt as usize >= self.num_pnt {
1331 return;
1332 }
1333 let i = self.active_pnt as usize;
1334 let mut ell = Ellipse::new(
1335 self.calc_xp(i),
1336 self.calc_yp(i),
1337 self.point_size,
1338 self.point_size,
1339 32,
1340 false,
1341 );
1342 ell.rewind(0);
1343 loop {
1344 let (mut x, mut y) = (0.0, 0.0);
1345 let cmd = ell.vertex(&mut x, &mut y);
1346 if is_stop(cmd) {
1347 break;
1348 }
1349 self.vertices.push((x, y, cmd));
1350 }
1351 }
1352}
1353
1354impl Ctrl for SplineCtrl {
1355 fn num_paths(&self) -> u32 {
1356 5
1357 }
1358
1359 fn color(&self, path_id: u32) -> Rgba8 {
1360 self.colors[path_id.min(4) as usize]
1361 }
1362}
1363
1364impl VertexSource for SplineCtrl {
1365 fn rewind(&mut self, path_id: u32) {
1366 self.vertex_idx = 0;
1367 match path_id {
1368 0 => self.calc_background(),
1369 1 => self.calc_border(),
1370 2 => self.calc_curve(),
1371 3 => self.calc_inactive_points(),
1372 4 => self.calc_active_point(),
1373 _ => self.vertices.clear(),
1374 }
1375 }
1376
1377 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
1378 if self.vertex_idx < self.vertices.len() {
1379 let (vx, vy, cmd) = self.vertices[self.vertex_idx];
1380 *x = vx;
1381 *y = vy;
1382 self.vertex_idx += 1;
1383 cmd
1384 } else {
1385 PATH_CMD_STOP
1386 }
1387 }
1388}
1389
1390pub struct GammaCtrl {
1401 x1: f64,
1403 y1: f64,
1404 x2: f64,
1405 y2: f64,
1406 gamma_spline: crate::gamma::GammaSpline,
1408 border_width: f64,
1410 border_extra: f64,
1411 curve_width: f64,
1412 grid_width: f64,
1413 text_thickness: f64,
1414 point_size: f64,
1415 text_height: f64,
1416 text_width: f64,
1417 xc1: f64,
1419 yc1: f64,
1420 xc2: f64,
1421 yc2: f64,
1422 xs1: f64,
1424 ys1: f64,
1425 xs2: f64,
1426 ys2: f64,
1427 xt1: f64,
1429 yt1: f64,
1430 #[allow(dead_code)]
1431 xt2: f64,
1432 #[allow(dead_code)]
1433 yt2: f64,
1434 xp1: f64,
1436 yp1: f64,
1437 xp2: f64,
1438 yp2: f64,
1439 p1_active: bool,
1441 mouse_point: u32,
1442 pdx: f64,
1443 pdy: f64,
1444 colors: [Rgba8; 7],
1446 vertices: Vec<(f64, f64, u32)>,
1448 vertex_idx: usize,
1449}
1450
1451impl GammaCtrl {
1452 pub fn new(x1: f64, y1: f64, x2: f64, y2: f64) -> Self {
1453 let text_height = 9.0;
1454 let border_width = 2.0;
1455 let yc2 = y2 - text_height * 2.0;
1456
1457 let mut gc = Self {
1458 x1,
1459 y1,
1460 x2,
1461 y2,
1462 gamma_spline: crate::gamma::GammaSpline::new(),
1463 border_width,
1464 border_extra: 0.0,
1465 curve_width: 2.0,
1466 grid_width: 0.2,
1467 text_thickness: 1.5,
1468 point_size: 5.0,
1469 text_height,
1470 text_width: 0.0,
1471 xc1: x1,
1472 yc1: y1,
1473 xc2: x2,
1474 yc2,
1475 xs1: 0.0,
1476 ys1: 0.0,
1477 xs2: 0.0,
1478 ys2: 0.0,
1479 xt1: x1,
1480 yt1: yc2,
1481 xt2: x2,
1482 yt2: y2,
1483 xp1: 0.0,
1484 yp1: 0.0,
1485 xp2: 0.0,
1486 yp2: 0.0,
1487 p1_active: true,
1488 mouse_point: 0,
1489 pdx: 0.0,
1490 pdy: 0.0,
1491 colors: [
1492 Rgba8::new(255, 255, 230, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(51, 51, 0, 255), Rgba8::new(0, 0, 0, 255), Rgba8::new(255, 0, 0, 255), Rgba8::new(0, 0, 0, 255), ],
1500 vertices: Vec::new(),
1501 vertex_idx: 0,
1502 };
1503 gc.calc_spline_box();
1504 gc
1505 }
1506
1507 fn calc_spline_box(&mut self) {
1508 self.xs1 = self.xc1 + self.border_width;
1509 self.ys1 = self.yc1 + self.border_width;
1510 self.xs2 = self.xc2 - self.border_width;
1511 self.ys2 = self.yc2 - self.border_width * 0.5;
1512 }
1513
1514 fn calc_points(&mut self) {
1515 let (kx1, ky1, kx2, ky2) = self.gamma_spline.get_values();
1516 self.xp1 = self.xs1 + (self.xs2 - self.xs1) * kx1 * 0.25;
1517 self.yp1 = self.ys1 + (self.ys2 - self.ys1) * ky1 * 0.25;
1518 self.xp2 = self.xs2 - (self.xs2 - self.xs1) * kx2 * 0.25;
1519 self.yp2 = self.ys2 - (self.ys2 - self.ys1) * ky2 * 0.25;
1520 }
1521
1522 fn calc_values(&mut self) {
1523 let kx1 = (self.xp1 - self.xs1) * 4.0 / (self.xs2 - self.xs1);
1524 let ky1 = (self.yp1 - self.ys1) * 4.0 / (self.ys2 - self.ys1);
1525 let kx2 = (self.xs2 - self.xp2) * 4.0 / (self.xs2 - self.xs1);
1526 let ky2 = (self.ys2 - self.yp2) * 4.0 / (self.ys2 - self.ys1);
1527 self.gamma_spline.set_values(kx1, ky1, kx2, ky2);
1528 }
1529
1530 pub fn border_width(&mut self, t: f64, extra: f64) {
1533 self.border_width = t;
1534 self.border_extra = extra;
1535 self.calc_spline_box();
1536 }
1537
1538 pub fn curve_width(&mut self, t: f64) {
1539 self.curve_width = t;
1540 }
1541
1542 pub fn grid_width(&mut self, t: f64) {
1543 self.grid_width = t;
1544 }
1545
1546 pub fn text_thickness(&mut self, t: f64) {
1547 self.text_thickness = t;
1548 }
1549
1550 pub fn text_size(&mut self, h: f64, w: f64) {
1551 self.text_width = w;
1552 self.text_height = h;
1553 self.yc2 = self.y2 - self.text_height * 2.0;
1554 self.yt1 = self.y2 - self.text_height * 2.0;
1555 self.calc_spline_box();
1556 }
1557
1558 pub fn point_size(&mut self, s: f64) {
1559 self.point_size = s;
1560 }
1561
1562 pub fn set_values(&mut self, kx1: f64, ky1: f64, kx2: f64, ky2: f64) {
1565 self.gamma_spline.set_values(kx1, ky1, kx2, ky2);
1566 }
1567
1568 pub fn get_values(&self) -> (f64, f64, f64, f64) {
1569 self.gamma_spline.get_values()
1570 }
1571
1572 pub fn gamma(&self) -> &[u8; 256] {
1573 self.gamma_spline.gamma()
1574 }
1575
1576 pub fn y(&self, x: f64) -> f64 {
1577 self.gamma_spline.y(x)
1578 }
1579
1580 pub fn get_gamma_spline(&self) -> &crate::gamma::GammaSpline {
1581 &self.gamma_spline
1582 }
1583
1584 pub fn change_active_point(&mut self) {
1585 self.p1_active = !self.p1_active;
1586 }
1587
1588 pub fn background_color(&mut self, c: Rgba8) {
1591 self.colors[0] = c;
1592 }
1593 pub fn border_color(&mut self, c: Rgba8) {
1594 self.colors[1] = c;
1595 }
1596 pub fn curve_color(&mut self, c: Rgba8) {
1597 self.colors[2] = c;
1598 }
1599 pub fn grid_color(&mut self, c: Rgba8) {
1600 self.colors[3] = c;
1601 }
1602 pub fn inactive_pnt_color(&mut self, c: Rgba8) {
1603 self.colors[4] = c;
1604 }
1605 pub fn active_pnt_color(&mut self, c: Rgba8) {
1606 self.colors[5] = c;
1607 }
1608 pub fn text_color(&mut self, c: Rgba8) {
1609 self.colors[6] = c;
1610 }
1611
1612 pub fn in_rect(&self, x: f64, y: f64) -> bool {
1615 x >= self.x1 && x <= self.x2 && y >= self.y1 && y <= self.y2
1616 }
1617
1618 pub fn on_mouse_button_down(&mut self, x: f64, y: f64) -> bool {
1619 self.calc_points();
1620 let dist1 = ((x - self.xp1).powi(2) + (y - self.yp1).powi(2)).sqrt();
1621 if dist1 <= self.point_size + 1.0 {
1622 self.mouse_point = 1;
1623 self.pdx = self.xp1 - x;
1624 self.pdy = self.yp1 - y;
1625 self.p1_active = true;
1626 return true;
1627 }
1628 let dist2 = ((x - self.xp2).powi(2) + (y - self.yp2).powi(2)).sqrt();
1629 if dist2 <= self.point_size + 1.0 {
1630 self.mouse_point = 2;
1631 self.pdx = self.xp2 - x;
1632 self.pdy = self.yp2 - y;
1633 self.p1_active = false;
1634 return true;
1635 }
1636 false
1637 }
1638
1639 pub fn on_mouse_button_up(&mut self, _x: f64, _y: f64) -> bool {
1640 if self.mouse_point != 0 {
1641 self.mouse_point = 0;
1642 return true;
1643 }
1644 false
1645 }
1646
1647 pub fn on_mouse_move(&mut self, x: f64, y: f64, button_flag: bool) -> bool {
1648 if !button_flag {
1649 return self.on_mouse_button_up(x, y);
1650 }
1651 if self.mouse_point == 1 {
1652 self.xp1 = x + self.pdx;
1653 self.yp1 = y + self.pdy;
1654 self.calc_values();
1655 return true;
1656 }
1657 if self.mouse_point == 2 {
1658 self.xp2 = x + self.pdx;
1659 self.yp2 = y + self.pdy;
1660 self.calc_values();
1661 return true;
1662 }
1663 false
1664 }
1665
1666 pub fn on_arrow_keys(&mut self, left: bool, right: bool, down: bool, up: bool) -> bool {
1667 let (mut kx1, mut ky1, mut kx2, mut ky2) = self.gamma_spline.get_values();
1668 let mut ret = false;
1669 if self.p1_active {
1670 if left {
1671 kx1 -= 0.005;
1672 ret = true;
1673 }
1674 if right {
1675 kx1 += 0.005;
1676 ret = true;
1677 }
1678 if down {
1679 ky1 -= 0.005;
1680 ret = true;
1681 }
1682 if up {
1683 ky1 += 0.005;
1684 ret = true;
1685 }
1686 } else {
1687 if left {
1688 kx2 += 0.005;
1689 ret = true;
1690 }
1691 if right {
1692 kx2 -= 0.005;
1693 ret = true;
1694 }
1695 if down {
1696 ky2 += 0.005;
1697 ret = true;
1698 }
1699 if up {
1700 ky2 -= 0.005;
1701 ret = true;
1702 }
1703 }
1704 if ret {
1705 self.gamma_spline.set_values(kx1, ky1, kx2, ky2);
1706 }
1707 ret
1708 }
1709
1710 fn calc_background(&mut self) {
1714 self.vertices.clear();
1715 let be = self.border_extra;
1716 self.vertices
1717 .push((self.x1 - be, self.y1 - be, PATH_CMD_MOVE_TO));
1718 self.vertices
1719 .push((self.x2 + be, self.y1 - be, PATH_CMD_LINE_TO));
1720 self.vertices
1721 .push((self.x2 + be, self.y2 + be, PATH_CMD_LINE_TO));
1722 self.vertices
1723 .push((self.x1 - be, self.y2 + be, PATH_CMD_LINE_TO));
1724 }
1725
1726 fn calc_border(&mut self) {
1728 self.vertices.clear();
1729 let bw = self.border_width;
1730 self.vertices
1732 .push((self.x1, self.y1, PATH_CMD_MOVE_TO));
1733 self.vertices
1734 .push((self.x2, self.y1, PATH_CMD_LINE_TO));
1735 self.vertices
1736 .push((self.x2, self.y2, PATH_CMD_LINE_TO));
1737 self.vertices
1738 .push((self.x1, self.y2, PATH_CMD_LINE_TO));
1739 self.vertices
1741 .push((self.x1 + bw, self.y1 + bw, PATH_CMD_MOVE_TO));
1742 self.vertices
1743 .push((self.x1 + bw, self.y2 - bw, PATH_CMD_LINE_TO));
1744 self.vertices
1745 .push((self.x2 - bw, self.y2 - bw, PATH_CMD_LINE_TO));
1746 self.vertices
1747 .push((self.x2 - bw, self.y1 + bw, PATH_CMD_LINE_TO));
1748 self.vertices
1750 .push((self.xc1 + bw, self.yc2 - bw * 0.5, PATH_CMD_MOVE_TO));
1751 self.vertices
1752 .push((self.xc2 - bw, self.yc2 - bw * 0.5, PATH_CMD_LINE_TO));
1753 self.vertices
1754 .push((self.xc2 - bw, self.yc2 + bw * 0.5, PATH_CMD_LINE_TO));
1755 self.vertices
1756 .push((self.xc1 + bw, self.yc2 + bw * 0.5, PATH_CMD_LINE_TO));
1757 }
1758
1759 fn calc_curve(&mut self) {
1761 self.vertices.clear();
1762 self.gamma_spline
1763 .set_box(self.xs1, self.ys1, self.xs2, self.ys2);
1764
1765 let mut spline_copy = crate::gamma::GammaSpline::new();
1766 let (kx1, ky1, kx2, ky2) = self.gamma_spline.get_values();
1768 spline_copy.set_values(kx1, ky1, kx2, ky2);
1769 spline_copy.set_box(self.xs1, self.ys1, self.xs2, self.ys2);
1770
1771 let mut path_verts = Vec::new();
1773 spline_copy.rewind(0);
1774 loop {
1775 let (mut x, mut y) = (0.0, 0.0);
1776 let cmd = spline_copy.vertex(&mut x, &mut y);
1777 if is_stop(cmd) {
1778 break;
1779 }
1780 path_verts.push((x, y, cmd));
1781 }
1782
1783 let mut path = crate::path_storage::PathStorage::new();
1785 for (i, &(x, y, cmd)) in path_verts.iter().enumerate() {
1786 if i == 0 {
1787 path.move_to(x, y);
1788 } else {
1789 path.line_to(x, y);
1790 }
1791 let _ = cmd;
1792 }
1793
1794 let mut stroke = ConvStroke::new(&mut path);
1795 stroke.set_width(self.curve_width);
1796 stroke.rewind(0);
1797 loop {
1798 let (mut x, mut y) = (0.0, 0.0);
1799 let cmd = stroke.vertex(&mut x, &mut y);
1800 if is_stop(cmd) {
1801 break;
1802 }
1803 self.vertices.push((x, y, cmd));
1804 }
1805 }
1806
1807 fn calc_grid(&mut self) {
1809 self.vertices.clear();
1810 self.calc_points();
1811
1812 let gw = self.grid_width;
1813 let (xs1, ys1, xs2, ys2) = (self.xs1, self.ys1, self.xs2, self.ys2);
1814 let ymid = (ys1 + ys2) * 0.5;
1815 let xmid = (xs1 + xs2) * 0.5;
1816
1817 self.vertices.push((xs1, ymid - gw * 0.5, PATH_CMD_MOVE_TO));
1819 self.vertices.push((xs2, ymid - gw * 0.5, PATH_CMD_LINE_TO));
1820 self.vertices.push((xs2, ymid + gw * 0.5, PATH_CMD_LINE_TO));
1821 self.vertices.push((xs1, ymid + gw * 0.5, PATH_CMD_LINE_TO));
1822
1823 self.vertices.push((xmid - gw * 0.5, ys1, PATH_CMD_MOVE_TO));
1825 self.vertices
1826 .push((xmid - gw * 0.5, ys2, PATH_CMD_LINE_TO));
1827 self.vertices
1828 .push((xmid + gw * 0.5, ys2, PATH_CMD_LINE_TO));
1829 self.vertices
1830 .push((xmid + gw * 0.5, ys1, PATH_CMD_LINE_TO));
1831
1832 let (xp1, yp1) = (self.xp1, self.yp1);
1834 self.vertices
1835 .push((xs1, yp1 - gw * 0.5, PATH_CMD_MOVE_TO));
1836 self.vertices
1837 .push((xp1 - gw * 0.5, yp1 - gw * 0.5, PATH_CMD_LINE_TO));
1838 self.vertices
1839 .push((xp1 - gw * 0.5, ys1, PATH_CMD_LINE_TO));
1840 self.vertices
1841 .push((xp1 + gw * 0.5, ys1, PATH_CMD_LINE_TO));
1842 self.vertices
1843 .push((xp1 + gw * 0.5, yp1 + gw * 0.5, PATH_CMD_LINE_TO));
1844 self.vertices
1845 .push((xs1, yp1 + gw * 0.5, PATH_CMD_LINE_TO));
1846
1847 let (xp2, yp2) = (self.xp2, self.yp2);
1849 self.vertices
1850 .push((xs2, yp2 + gw * 0.5, PATH_CMD_MOVE_TO));
1851 self.vertices
1852 .push((xp2 + gw * 0.5, yp2 + gw * 0.5, PATH_CMD_LINE_TO));
1853 self.vertices
1854 .push((xp2 + gw * 0.5, ys2, PATH_CMD_LINE_TO));
1855 self.vertices
1856 .push((xp2 - gw * 0.5, ys2, PATH_CMD_LINE_TO));
1857 self.vertices
1858 .push((xp2 - gw * 0.5, yp2 - gw * 0.5, PATH_CMD_LINE_TO));
1859 self.vertices
1860 .push((xs2, yp2 - gw * 0.5, PATH_CMD_LINE_TO));
1861 }
1862
1863 fn calc_inactive_point(&mut self) {
1865 self.vertices.clear();
1866 self.calc_points();
1867 let (cx, cy) = if self.p1_active {
1868 (self.xp2, self.yp2)
1869 } else {
1870 (self.xp1, self.yp1)
1871 };
1872 let mut ell = Ellipse::new(cx, cy, self.point_size, self.point_size, 32, false);
1873 ell.rewind(0);
1874 loop {
1875 let (mut x, mut y) = (0.0, 0.0);
1876 let cmd = ell.vertex(&mut x, &mut y);
1877 if is_stop(cmd) {
1878 break;
1879 }
1880 self.vertices.push((x, y, cmd));
1881 }
1882 }
1883
1884 fn calc_active_point(&mut self) {
1886 self.vertices.clear();
1887 self.calc_points();
1888 let (cx, cy) = if self.p1_active {
1889 (self.xp1, self.yp1)
1890 } else {
1891 (self.xp2, self.yp2)
1892 };
1893 let mut ell = Ellipse::new(cx, cy, self.point_size, self.point_size, 32, false);
1894 ell.rewind(0);
1895 loop {
1896 let (mut x, mut y) = (0.0, 0.0);
1897 let cmd = ell.vertex(&mut x, &mut y);
1898 if is_stop(cmd) {
1899 break;
1900 }
1901 self.vertices.push((x, y, cmd));
1902 }
1903 }
1904
1905 fn calc_text_display(&mut self) {
1907 self.vertices.clear();
1908 let (kx1, ky1, kx2, ky2) = self.gamma_spline.get_values();
1909 let text = format!("{:.3} {:.3} {:.3} {:.3}", kx1, ky1, kx2, ky2);
1910
1911 let mut txt = GsvText::new();
1912 txt.text(&text);
1913 txt.size(self.text_height, self.text_width);
1914 txt.start_point(
1915 self.xt1 + self.border_width * 2.0,
1916 (self.yt1 + self.y2) * 0.5 - self.text_height * 0.5,
1917 );
1918
1919 let mut stroke = ConvStroke::new(txt);
1920 stroke.set_width(self.text_thickness);
1921 stroke.set_line_join(LineJoin::Round);
1922 stroke.set_line_cap(LineCap::Round);
1923
1924 stroke.rewind(0);
1925 loop {
1926 let (mut x, mut y) = (0.0, 0.0);
1927 let cmd = stroke.vertex(&mut x, &mut y);
1928 if is_stop(cmd) {
1929 break;
1930 }
1931 self.vertices.push((x, y, cmd));
1932 }
1933 }
1934}
1935
1936impl Ctrl for GammaCtrl {
1937 fn num_paths(&self) -> u32 {
1938 7
1939 }
1940
1941 fn color(&self, path_id: u32) -> Rgba8 {
1942 self.colors[path_id.min(6) as usize]
1943 }
1944}
1945
1946impl VertexSource for GammaCtrl {
1947 fn rewind(&mut self, path_id: u32) {
1948 self.vertex_idx = 0;
1949 match path_id {
1950 0 => self.calc_background(),
1951 1 => self.calc_border(),
1952 2 => self.calc_curve(),
1953 3 => self.calc_grid(),
1954 4 => self.calc_inactive_point(),
1955 5 => self.calc_active_point(),
1956 6 => self.calc_text_display(),
1957 _ => {
1958 self.vertices.clear();
1959 }
1960 }
1961 }
1962
1963 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
1964 if self.vertex_idx < self.vertices.len() {
1965 let (vx, vy, cmd) = self.vertices[self.vertex_idx];
1966 *x = vx;
1967 *y = vy;
1968 self.vertex_idx += 1;
1969 cmd
1970 } else {
1971 PATH_CMD_STOP
1972 }
1973 }
1974}
1975
1976#[cfg(test)]
1981mod tests {
1982 use super::*;
1983
1984 #[test]
1985 fn test_slider_ctrl_default_value() {
1986 let s = SliderCtrl::new(5.0, 5.0, 300.0, 12.0);
1987 assert!((s.value() - 0.5).abs() < 1e-10);
1989 }
1990
1991 #[test]
1992 fn test_slider_ctrl_set_value() {
1993 let mut s = SliderCtrl::new(5.0, 5.0, 300.0, 12.0);
1994 s.range(-180.0, 180.0);
1995 s.set_value(90.0);
1996 assert!((s.value() - 90.0).abs() < 1e-6);
1997 }
1998
1999 #[test]
2000 fn test_slider_ctrl_vertex_source() {
2001 let mut s = SliderCtrl::new(5.0, 5.0, 300.0, 12.0);
2002 s.rewind(0);
2004 let mut count = 0;
2005 loop {
2006 let (mut x, mut y) = (0.0, 0.0);
2007 let cmd = s.vertex(&mut x, &mut y);
2008 if is_stop(cmd) {
2009 break;
2010 }
2011 count += 1;
2012 }
2013 assert_eq!(count, 4);
2014 }
2015
2016 #[test]
2017 fn test_slider_ctrl_pointer_circle() {
2018 let mut s = SliderCtrl::new(5.0, 5.0, 300.0, 12.0);
2019 s.rewind(4);
2021 let mut count = 0;
2022 loop {
2023 let (mut x, mut y) = (0.0, 0.0);
2024 let cmd = s.vertex(&mut x, &mut y);
2025 if is_stop(cmd) {
2026 break;
2027 }
2028 count += 1;
2029 }
2030 assert!(count >= 32);
2031 }
2032
2033 #[test]
2034 fn test_slider_ctrl_text() {
2035 let mut s = SliderCtrl::new(5.0, 5.0, 300.0, 12.0);
2036 s.label("Angle=%3.2f");
2037 s.set_value(45.0);
2038 s.rewind(2);
2040 let mut count = 0;
2041 loop {
2042 let (mut x, mut y) = (0.0, 0.0);
2043 let cmd = s.vertex(&mut x, &mut y);
2044 if is_stop(cmd) {
2045 break;
2046 }
2047 count += 1;
2048 }
2049 assert!(count > 0);
2050 }
2051
2052 #[test]
2053 fn test_slider_ctrl_num_paths() {
2054 let s = SliderCtrl::new(5.0, 5.0, 300.0, 12.0);
2055 assert_eq!(s.num_paths(), 6);
2056 }
2057
2058 #[test]
2059 fn test_scale_ctrl_defaults() {
2060 let s = ScaleCtrl::new(5.0, 5.0, 395.0, 12.0);
2061 assert!((s.value1() - 0.3).abs() < 1e-10);
2062 assert!((s.value2() - 0.7).abs() < 1e-10);
2063 assert_eq!(s.num_paths(), 5);
2064 }
2065
2066 #[test]
2067 fn test_scale_ctrl_setters_clamp_and_gap() {
2068 let mut s = ScaleCtrl::new(5.0, 5.0, 395.0, 12.0);
2069 s.set_min_delta(0.1);
2070 s.set_value1(0.95);
2071 assert!(s.value1() <= s.value2() - 0.1 + 1e-10);
2072 s.set_value2(-1.0);
2073 assert!(s.value2() >= s.value1() + 0.1 - 1e-10);
2074 }
2075
2076 #[test]
2077 fn test_scale_ctrl_paths_emit_vertices() {
2078 let mut s = ScaleCtrl::new(5.0, 5.0, 395.0, 12.0);
2079 for path_id in 0..5 {
2080 s.rewind(path_id);
2081 let mut seen = 0;
2082 loop {
2083 let (mut x, mut y) = (0.0, 0.0);
2084 let cmd = s.vertex(&mut x, &mut y);
2085 if is_stop(cmd) {
2086 break;
2087 }
2088 seen += 1;
2089 }
2090 assert!(seen > 0, "expected vertices for path {}", path_id);
2091 }
2092 }
2093
2094 #[test]
2095 fn test_sprintf_format_various() {
2096 assert_eq!(sprintf_format("Some Value=%1.0f", 40.0), "Some Value=40");
2098 assert_eq!(sprintf_format("Some Value=%1.0f", 32.0), "Some Value=32");
2099
2100 assert_eq!(sprintf_format("Spiral=%.3f", 0.0), "Spiral=0.000");
2102 assert_eq!(sprintf_format("Spiral=%.3f", 0.05), "Spiral=0.050");
2103
2104 assert_eq!(sprintf_format("N=%.2f", 4.0), "N=4.00");
2106
2107 assert_eq!(sprintf_format("Angle=%3.2f", 45.0), "Angle=45.00");
2109
2110 assert_eq!(sprintf_format("Num Points=%.0f", 200.0), "Num Points=200");
2112
2113 assert_eq!(sprintf_format("Count=%d", 42.7), "Count=42");
2115
2116 assert_eq!(sprintf_format("radius=%4.3f", 1.5), "radius=1.500");
2118
2119 assert_eq!(sprintf_format("Hello", 42.0), "Hello");
2121
2122 assert_eq!(sprintf_format("Val=%f", 3.14), "Val=3.140000");
2124 }
2125
2126 #[test]
2127 fn test_cbox_ctrl_basic() {
2128 let mut c = CboxCtrl::new(10.0, 10.0, "Outline");
2129 assert!(!c.status());
2130 c.set_status(true);
2131 assert!(c.status());
2132 assert_eq!(c.num_paths(), 3);
2133 }
2134
2135 #[test]
2136 fn test_cbox_ctrl_checkmark_only_when_active() {
2137 let mut c = CboxCtrl::new(10.0, 10.0, "Test");
2138 c.set_status(false);
2140 c.rewind(2);
2141 let (mut x, mut y) = (0.0, 0.0);
2142 let cmd = c.vertex(&mut x, &mut y);
2143 assert_eq!(cmd, PATH_CMD_STOP);
2144
2145 c.set_status(true);
2147 c.rewind(2);
2148 let cmd = c.vertex(&mut x, &mut y);
2149 assert_eq!(cmd, PATH_CMD_MOVE_TO);
2150 }
2151
2152 #[test]
2153 fn test_rbox_ctrl_basic() {
2154 let mut r = RboxCtrl::new(10.0, 10.0, 150.0, 100.0);
2155 r.add_item("Option A");
2156 r.add_item("Option B");
2157 r.add_item("Option C");
2158 r.set_cur_item(1);
2159 assert_eq!(r.cur_item(), 1);
2160 assert_eq!(r.num_paths(), 5);
2161 }
2162
2163 #[test]
2164 fn test_rbox_ctrl_inactive_circles() {
2165 let mut r = RboxCtrl::new(10.0, 10.0, 150.0, 100.0);
2166 r.add_item("A");
2167 r.add_item("B");
2168 r.rewind(3);
2170 let mut count = 0;
2171 loop {
2172 let (mut x, mut y) = (0.0, 0.0);
2173 let cmd = r.vertex(&mut x, &mut y);
2174 if is_stop(cmd) {
2175 break;
2176 }
2177 count += 1;
2178 }
2179 assert!(count > 32); }
2181
2182 #[test]
2183 fn test_rbox_ctrl_no_active_when_negative() {
2184 let mut r = RboxCtrl::new(10.0, 10.0, 150.0, 100.0);
2185 r.add_item("X");
2186 r.rewind(4);
2188 let (mut x, mut y) = (0.0, 0.0);
2189 let cmd = r.vertex(&mut x, &mut y);
2190 assert_eq!(cmd, PATH_CMD_STOP);
2191 }
2192
2193 #[test]
2198 fn test_gamma_ctrl_basic() {
2199 let gc = GammaCtrl::new(10.0, 10.0, 200.0, 200.0);
2200 assert_eq!(gc.num_paths(), 7);
2201 }
2202
2203 #[test]
2204 fn test_gamma_ctrl_default_values() {
2205 let gc = GammaCtrl::new(10.0, 10.0, 200.0, 200.0);
2206 let (kx1, ky1, kx2, ky2) = gc.get_values();
2207 assert!((kx1 - 1.0).abs() < 0.01);
2208 assert!((ky1 - 1.0).abs() < 0.01);
2209 assert!((kx2 - 1.0).abs() < 0.01);
2210 assert!((ky2 - 1.0).abs() < 0.01);
2211 }
2212
2213 #[test]
2214 fn test_gamma_ctrl_set_values() {
2215 let mut gc = GammaCtrl::new(10.0, 10.0, 200.0, 200.0);
2216 gc.set_values(0.5, 1.5, 0.8, 1.2);
2217 let (kx1, ky1, kx2, ky2) = gc.get_values();
2218 assert!((kx1 - 0.5).abs() < 0.001);
2219 assert!((ky1 - 1.5).abs() < 0.001);
2220 assert!((kx2 - 0.8).abs() < 0.001);
2221 assert!((ky2 - 1.2).abs() < 0.001);
2222 }
2223
2224 #[test]
2225 fn test_gamma_ctrl_gamma_table() {
2226 let gc = GammaCtrl::new(10.0, 10.0, 200.0, 200.0);
2227 let gamma = gc.gamma();
2228 assert_eq!(gamma[0], 0);
2229 assert_eq!(gamma[255], 255);
2230 }
2231
2232 #[test]
2233 fn test_gamma_ctrl_background_path() {
2234 let mut gc = GammaCtrl::new(10.0, 10.0, 200.0, 200.0);
2235 gc.rewind(0);
2236 let mut count = 0;
2237 loop {
2238 let (mut x, mut y) = (0.0, 0.0);
2239 let cmd = gc.vertex(&mut x, &mut y);
2240 if is_stop(cmd) {
2241 break;
2242 }
2243 count += 1;
2244 }
2245 assert_eq!(count, 4); }
2247
2248 #[test]
2249 fn test_gamma_ctrl_border_path() {
2250 let mut gc = GammaCtrl::new(10.0, 10.0, 200.0, 200.0);
2251 gc.rewind(1);
2252 let mut count = 0;
2253 loop {
2254 let (mut x, mut y) = (0.0, 0.0);
2255 let cmd = gc.vertex(&mut x, &mut y);
2256 if is_stop(cmd) {
2257 break;
2258 }
2259 count += 1;
2260 }
2261 assert_eq!(count, 12); }
2263
2264 #[test]
2265 fn test_gamma_ctrl_curve_path() {
2266 let mut gc = GammaCtrl::new(10.0, 10.0, 200.0, 200.0);
2267 gc.rewind(2);
2268 let mut count = 0;
2269 loop {
2270 let (mut x, mut y) = (0.0, 0.0);
2271 let cmd = gc.vertex(&mut x, &mut y);
2272 if is_stop(cmd) {
2273 break;
2274 }
2275 count += 1;
2276 }
2277 assert!(count > 10, "Curve path should have many vertices, got {count}");
2278 }
2279
2280 #[test]
2281 fn test_gamma_ctrl_grid_path() {
2282 let mut gc = GammaCtrl::new(10.0, 10.0, 200.0, 200.0);
2283 gc.rewind(3);
2284 let mut count = 0;
2285 loop {
2286 let (mut x, mut y) = (0.0, 0.0);
2287 let cmd = gc.vertex(&mut x, &mut y);
2288 if is_stop(cmd) {
2289 break;
2290 }
2291 count += 1;
2292 }
2293 assert_eq!(count, 20); }
2295
2296 #[test]
2297 fn test_gamma_ctrl_points_path() {
2298 let mut gc = GammaCtrl::new(10.0, 10.0, 200.0, 200.0);
2299 gc.rewind(4);
2301 let mut count = 0;
2302 loop {
2303 let (mut x, mut y) = (0.0, 0.0);
2304 let cmd = gc.vertex(&mut x, &mut y);
2305 if is_stop(cmd) {
2306 break;
2307 }
2308 count += 1;
2309 }
2310 assert!(count >= 32);
2311
2312 gc.rewind(5);
2314 let mut count = 0;
2315 loop {
2316 let (mut x, mut y) = (0.0, 0.0);
2317 let cmd = gc.vertex(&mut x, &mut y);
2318 if is_stop(cmd) {
2319 break;
2320 }
2321 count += 1;
2322 }
2323 assert!(count >= 32);
2324 }
2325
2326 #[test]
2327 fn test_gamma_ctrl_text_path() {
2328 let mut gc = GammaCtrl::new(10.0, 10.0, 200.0, 200.0);
2329 gc.rewind(6);
2330 let mut count = 0;
2331 loop {
2332 let (mut x, mut y) = (0.0, 0.0);
2333 let cmd = gc.vertex(&mut x, &mut y);
2334 if is_stop(cmd) {
2335 break;
2336 }
2337 count += 1;
2338 }
2339 assert!(count > 0, "Text path should have vertices");
2340 }
2341
2342 #[test]
2343 fn test_gamma_ctrl_mouse_interaction() {
2344 let mut gc = GammaCtrl::new(0.0, 0.0, 200.0, 200.0);
2345 assert!(gc.in_rect(100.0, 100.0));
2347 assert!(!gc.in_rect(300.0, 300.0));
2348
2349 let changed = gc.on_arrow_keys(false, true, false, false);
2351 assert!(changed);
2352 let (kx1, _, _, _) = gc.get_values();
2353 assert!((kx1 - 1.005).abs() < 0.001);
2354 }
2355}