1use crate::sys;
4use dear_imgui_rs::{with_scratch_txt, with_scratch_txt_two};
5use std::borrow::Cow;
6use std::marker::PhantomData;
7use std::os::raw::c_char;
8use std::rc::Rc;
9
10use crate::Colormap;
11
12#[repr(i32)]
14#[derive(Copy, Clone, Debug, PartialEq, Eq)]
15pub enum StyleVar {
16 PlotDefaultSize = sys::ImPlotStyleVar_PlotDefaultSize as i32,
17 PlotMinSize = sys::ImPlotStyleVar_PlotMinSize as i32,
18 PlotBorderSize = sys::ImPlotStyleVar_PlotBorderSize as i32,
19 MinorAlpha = sys::ImPlotStyleVar_MinorAlpha as i32,
20 MajorTickLen = sys::ImPlotStyleVar_MajorTickLen as i32,
21 MinorTickLen = sys::ImPlotStyleVar_MinorTickLen as i32,
22 MajorTickSize = sys::ImPlotStyleVar_MajorTickSize as i32,
23 MinorTickSize = sys::ImPlotStyleVar_MinorTickSize as i32,
24 MajorGridSize = sys::ImPlotStyleVar_MajorGridSize as i32,
25 MinorGridSize = sys::ImPlotStyleVar_MinorGridSize as i32,
26 PlotPadding = sys::ImPlotStyleVar_PlotPadding as i32,
27 LabelPadding = sys::ImPlotStyleVar_LabelPadding as i32,
28 LegendPadding = sys::ImPlotStyleVar_LegendPadding as i32,
29 LegendInnerPadding = sys::ImPlotStyleVar_LegendInnerPadding as i32,
30 LegendSpacing = sys::ImPlotStyleVar_LegendSpacing as i32,
31 MousePosPadding = sys::ImPlotStyleVar_MousePosPadding as i32,
32 AnnotationPadding = sys::ImPlotStyleVar_AnnotationPadding as i32,
33 FitPadding = sys::ImPlotStyleVar_FitPadding as i32,
34 DigitalPadding = sys::ImPlotStyleVar_DigitalPadding as i32,
35 DigitalSpacing = sys::ImPlotStyleVar_DigitalSpacing as i32,
36}
37
38pub struct StyleVarToken {
40 was_popped: bool,
41 _not_send_or_sync: PhantomData<Rc<()>>,
42}
43
44impl StyleVarToken {
45 pub fn pop(mut self) {
47 if self.was_popped {
48 panic!("Attempted to pop a style var token twice.");
49 }
50 self.was_popped = true;
51 unsafe {
52 sys::ImPlot_PopStyleVar(1);
53 }
54 }
55}
56
57impl Drop for StyleVarToken {
58 fn drop(&mut self) {
59 if !self.was_popped {
60 unsafe {
61 sys::ImPlot_PopStyleVar(1);
62 }
63 }
64 }
65}
66
67pub struct StyleColorToken {
69 was_popped: bool,
70 _not_send_or_sync: PhantomData<Rc<()>>,
71}
72
73impl StyleColorToken {
74 pub fn pop(mut self) {
76 if self.was_popped {
77 panic!("Attempted to pop a style color token twice.");
78 }
79 self.was_popped = true;
80 unsafe {
81 sys::ImPlot_PopStyleColor(1);
82 }
83 }
84}
85
86impl Drop for StyleColorToken {
87 fn drop(&mut self) {
88 if !self.was_popped {
89 unsafe {
90 sys::ImPlot_PopStyleColor(1);
91 }
92 }
93 }
94}
95
96#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
98#[repr(transparent)]
99pub struct ColormapIndex(pub(crate) i32);
100
101impl ColormapIndex {
102 #[inline]
103 pub const fn new(index: usize) -> Option<Self> {
104 if index <= i32::MAX as usize {
105 Some(Self(index as i32))
106 } else {
107 None
108 }
109 }
110
111 #[inline]
112 pub const fn get(self) -> usize {
113 self.0 as usize
114 }
115
116 #[inline]
117 pub const fn raw(self) -> i32 {
118 self.0
119 }
120
121 #[inline]
122 pub(crate) const fn from_raw(raw: i32) -> Option<Self> {
123 if raw >= 0 { Some(Self(raw)) } else { None }
124 }
125}
126
127impl From<Colormap> for ColormapIndex {
128 #[inline]
129 fn from(value: Colormap) -> Self {
130 value.index()
131 }
132}
133
134impl From<usize> for ColormapIndex {
135 #[inline]
136 fn from(value: usize) -> Self {
137 Self::new(value).expect("colormap index exceeded ImPlot's i32 range")
138 }
139}
140
141#[derive(Copy, Clone, Debug, PartialEq, Eq)]
143pub enum ColormapSelection {
144 Current,
145 Index(ColormapIndex),
146}
147
148impl ColormapSelection {
149 #[inline]
150 pub(crate) const fn raw(self) -> i32 {
151 match self {
152 Self::Current => crate::IMPLOT_AUTO,
153 Self::Index(index) => index.raw(),
154 }
155 }
156}
157
158impl From<Colormap> for ColormapSelection {
159 #[inline]
160 fn from(value: Colormap) -> Self {
161 Self::Index(value.index())
162 }
163}
164
165impl From<ColormapIndex> for ColormapSelection {
166 #[inline]
167 fn from(value: ColormapIndex) -> Self {
168 Self::Index(value)
169 }
170}
171
172impl From<Option<ColormapIndex>> for ColormapSelection {
173 #[inline]
174 fn from(value: Option<ColormapIndex>) -> Self {
175 value.map_or(Self::Current, Self::Index)
176 }
177}
178
179#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
181#[repr(transparent)]
182pub struct ColormapColorIndex(i32);
183
184impl ColormapColorIndex {
185 #[inline]
186 pub const fn new(index: usize) -> Option<Self> {
187 Self::from_usize(index)
188 }
189
190 #[inline]
191 pub const fn from_usize(index: usize) -> Option<Self> {
192 if index <= i32::MAX as usize {
193 Some(Self(index as i32))
194 } else {
195 None
196 }
197 }
198
199 #[inline]
200 pub const fn get(self) -> usize {
201 self.0 as usize
202 }
203
204 #[inline]
205 pub const fn raw(self) -> i32 {
206 self.0
207 }
208}
209
210impl From<usize> for ColormapColorIndex {
211 #[inline]
212 fn from(value: usize) -> Self {
213 Self::new(value).expect("colormap color index exceeded ImPlot's i32 range")
214 }
215}
216
217#[must_use]
219pub struct ColormapToken {
220 was_popped: bool,
221 _not_send_or_sync: PhantomData<Rc<()>>,
222}
223
224impl ColormapToken {
225 pub fn pop(mut self) {
227 if self.was_popped {
228 panic!("Attempted to pop an ImPlot colormap token twice.");
229 }
230 self.was_popped = true;
231 unsafe {
232 sys::ImPlot_PopColormap(1);
233 }
234 }
235}
236
237impl Drop for ColormapToken {
238 fn drop(&mut self) {
239 if !self.was_popped {
240 unsafe {
241 sys::ImPlot_PopColormap(1);
242 }
243 }
244 }
245}
246
247#[derive(Debug, Clone, Default, PartialEq)]
252pub struct PlotItemArrayStyle<'a> {
253 line_colors: Option<Cow<'a, [u32]>>,
254 fill_colors: Option<Cow<'a, [u32]>>,
255 marker_sizes: Option<Cow<'a, [f32]>>,
256 marker_line_colors: Option<Cow<'a, [u32]>>,
257 marker_fill_colors: Option<Cow<'a, [u32]>>,
258}
259
260impl<'a> PlotItemArrayStyle<'a> {
261 pub fn new() -> Self {
263 Self::default()
264 }
265
266 pub fn with_line_colors(mut self, colors: &'a [u32]) -> Self {
268 self.line_colors = Some(Cow::Borrowed(colors));
269 self
270 }
271
272 pub fn with_fill_colors(mut self, colors: &'a [u32]) -> Self {
274 self.fill_colors = Some(Cow::Borrowed(colors));
275 self
276 }
277
278 pub fn with_marker_sizes(mut self, sizes: &'a [f32]) -> Self {
280 self.marker_sizes = Some(Cow::Borrowed(sizes));
281 self
282 }
283
284 pub fn with_marker_line_colors(mut self, colors: &'a [u32]) -> Self {
286 self.marker_line_colors = Some(Cow::Borrowed(colors));
287 self
288 }
289
290 pub fn with_marker_fill_colors(mut self, colors: &'a [u32]) -> Self {
292 self.marker_fill_colors = Some(Cow::Borrowed(colors));
293 self
294 }
295
296 fn apply_to_spec(&self, spec: &mut sys::ImPlotSpec_c) {
297 spec.LineColors = self
298 .line_colors
299 .as_ref()
300 .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
301 spec.FillColors = self
302 .fill_colors
303 .as_ref()
304 .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
305 spec.MarkerSizes = self
306 .marker_sizes
307 .as_ref()
308 .map_or(std::ptr::null_mut(), |sizes| sizes.as_ptr() as *mut _);
309 spec.MarkerLineColors = self
310 .marker_line_colors
311 .as_ref()
312 .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
313 spec.MarkerFillColors = self
314 .marker_fill_colors
315 .as_ref()
316 .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
317 }
318}
319
320pub fn with_next_plot_item_array_style<'a, R>(
325 style: PlotItemArrayStyle<'a>,
326 f: impl FnOnce() -> R,
327) -> R {
328 let previous = crate::plots::take_next_plot_spec();
329 let mut spec = previous.unwrap_or_else(crate::plots::default_plot_spec);
330 style.apply_to_spec(&mut spec);
331 crate::plots::set_next_plot_spec(Some(spec));
332
333 let out = f();
334
335 if crate::plots::take_next_plot_spec().is_some() {
336 crate::plots::set_next_plot_spec(previous);
337 }
338
339 out
340}
341
342pub fn push_style_var_f32(var: StyleVar, value: f32) -> StyleVarToken {
344 unsafe {
345 sys::ImPlot_PushStyleVar_Float(var as sys::ImPlotStyleVar, value);
346 }
347 StyleVarToken {
348 was_popped: false,
349 _not_send_or_sync: PhantomData,
350 }
351}
352
353pub fn push_style_var_vec2(var: StyleVar, value: [f32; 2]) -> StyleVarToken {
355 unsafe {
356 sys::ImPlot_PushStyleVar_Vec2(
357 var as sys::ImPlotStyleVar,
358 sys::ImVec2_c {
359 x: value[0],
360 y: value[1],
361 },
362 );
363 }
364 StyleVarToken {
365 was_popped: false,
366 _not_send_or_sync: PhantomData,
367 }
368}
369
370pub fn push_style_color(element: crate::PlotColorElement, color: [f32; 4]) -> StyleColorToken {
372 unsafe {
373 let r = (color[0] * 255.0) as u32;
375 let g = (color[1] * 255.0) as u32;
376 let b = (color[2] * 255.0) as u32;
377 let a = (color[3] * 255.0) as u32;
378 let color_u32 = (a << 24) | (b << 16) | (g << 8) | r;
379
380 sys::ImPlot_PushStyleColor_U32(element as sys::ImPlotCol, color_u32);
381 }
382 StyleColorToken {
383 was_popped: false,
384 _not_send_or_sync: PhantomData,
385 }
386}
387
388pub fn push_colormap(cmap: impl Into<ColormapIndex>) -> ColormapToken {
390 unsafe {
391 sys::ImPlot_PushColormap_PlotColormap(cmap.into().raw());
392 }
393 ColormapToken {
394 was_popped: false,
395 _not_send_or_sync: PhantomData,
396 }
397}
398
399pub fn push_colormap_name(name: &str) -> ColormapToken {
401 assert!(!name.contains('\0'), "colormap name contained NUL");
402 with_scratch_txt(name, |ptr| unsafe { sys::ImPlot_PushColormap_Str(ptr) });
403 ColormapToken {
404 was_popped: false,
405 _not_send_or_sync: PhantomData,
406 }
407}
408
409pub fn add_colormap(name: &str, colors: &[[f32; 4]], qualitative: bool) -> ColormapIndex {
411 assert!(!name.contains('\0'), "colormap name contained NUL");
412 assert!(
413 colors.len() > 1,
414 "colormap must contain at least two colors"
415 );
416 assert!(
417 colors
418 .iter()
419 .flatten()
420 .all(|component| component.is_finite()),
421 "colormap colors must be finite"
422 );
423 let count = i32::try_from(colors.len()).expect("colormap contained too many colors");
424 let colors: Vec<sys::ImVec4> = colors
425 .iter()
426 .map(|color| sys::ImVec4 {
427 x: color[0],
428 y: color[1],
429 z: color[2],
430 w: color[3],
431 })
432 .collect();
433 let index = with_scratch_txt(name, |ptr| unsafe {
434 sys::ImPlot_AddColormap_Vec4Ptr(ptr, colors.as_ptr(), count, qualitative)
435 });
436 ColormapIndex::from_raw(index).expect("ImPlot returned a negative colormap index")
437}
438
439fn colormap_count_from_i32(raw: i32, caller: &str) -> usize {
440 assert!(raw >= 0, "{caller} returned a negative colormap count");
441 usize::try_from(raw).expect("non-negative colormap count must fit usize")
442}
443
444pub fn colormap_count() -> usize {
446 colormap_count_from_i32(
447 unsafe { sys::ImPlot_GetColormapCount() },
448 "colormap_count()",
449 )
450}
451
452pub fn colormap_name(index: impl Into<ColormapIndex>) -> String {
454 unsafe {
455 let p = sys::ImPlot_GetColormapName(index.into().raw());
456 if p.is_null() {
457 return String::new();
458 }
459 std::ffi::CStr::from_ptr(p).to_string_lossy().into_owned()
460 }
461}
462
463pub fn colormap_index_by_name(name: &str) -> Option<ColormapIndex> {
465 if name.contains('\0') {
466 return None;
467 }
468 let index = with_scratch_txt(name, |ptr| unsafe { sys::ImPlot_GetColormapIndex(ptr) });
469 ColormapIndex::from_raw(index)
470}
471
472pub fn colormap_size(index: impl Into<ColormapIndex>) -> usize {
474 colormap_count_from_i32(
475 unsafe { sys::ImPlot_GetColormapSize(index.into().raw()) },
476 "colormap_size()",
477 )
478}
479
480pub fn get_style_colormap_index() -> Option<ColormapIndex> {
482 unsafe {
483 let style = sys::ImPlot_GetStyle();
484 if style.is_null() {
485 return None;
486 }
487 ColormapIndex::from_raw((*style).Colormap)
488 }
489}
490
491pub fn get_style_colormap_name() -> Option<String> {
493 let idx = get_style_colormap_index()?;
494 let count = colormap_count();
495 if idx.get() >= count {
496 return None;
497 }
498 Some(colormap_name(idx))
499}
500
501pub fn set_style_colormap(index: impl Into<ColormapIndex>) {
503 unsafe {
504 let style = sys::ImPlot_GetStyle();
505 if !style.is_null() {
506 let count = colormap_count();
507 if count > 0 {
508 let index = index.into().get();
509 let idx = index.min(count - 1);
510 (*style).Colormap = ColormapIndex::from(idx).raw();
511 }
512 }
513 }
514}
515
516pub fn set_style_colormap_by_name(name: &str) {
518 if let Some(idx) = colormap_index_by_name(name) {
519 set_style_colormap(idx);
520 }
521}
522
523pub fn get_colormap_color(index: ColormapColorIndex) -> [f32; 4] {
525 unsafe {
526 let out = sys::ImPlot_GetColormapColor(index.raw(), crate::IMPLOT_AUTO);
527 [out.x, out.y, out.z, out.w]
528 }
529}
530
531pub fn get_colormap_color_from(
533 index: ColormapColorIndex,
534 cmap: impl Into<ColormapIndex>,
535) -> [f32; 4] {
536 unsafe {
537 let out = sys::ImPlot_GetColormapColor(index.raw(), cmap.into().raw());
538 [out.x, out.y, out.z, out.w]
539 }
540}
541
542pub fn sample_colormap(t: f32) -> [f32; 4] {
544 assert!(
545 (0.0..=1.0).contains(&t),
546 "sample_colormap t must be between 0 and 1"
547 );
548 unsafe {
549 let out = sys::ImPlot_SampleColormap(t, crate::IMPLOT_AUTO);
550 [out.x, out.y, out.z, out.w]
551 }
552}
553
554pub fn sample_colormap_from(t: f32, cmap: impl Into<ColormapSelection>) -> [f32; 4] {
556 assert!(
557 (0.0..=1.0).contains(&t),
558 "sample_colormap t must be between 0 and 1"
559 );
560 unsafe {
561 let out = sys::ImPlot_SampleColormap(t, cmap.into().raw());
562 [out.x, out.y, out.z, out.w]
563 }
564}
565
566pub fn next_colormap_color() -> [f32; 4] {
568 unsafe {
569 let out = sys::ImPlot_NextColormapColor();
570 [out.x, out.y, out.z, out.w]
571 }
572}
573
574pub fn show_style_editor() {
578 unsafe { sys::ImPlot_ShowStyleEditor(std::ptr::null_mut()) }
579}
580
581pub fn show_style_selector(label: &str) -> bool {
583 let label = if label.contains('\0') { "" } else { label };
584 with_scratch_txt(label, |ptr| unsafe { sys::ImPlot_ShowStyleSelector(ptr) })
585}
586
587pub fn show_colormap_selector(label: &str) -> bool {
589 let label = if label.contains('\0') { "" } else { label };
590 with_scratch_txt(label, |ptr| unsafe {
591 sys::ImPlot_ShowColormapSelector(ptr)
592 })
593}
594
595pub fn show_input_map_selector(label: &str) -> bool {
597 let label = if label.contains('\0') { "" } else { label };
598 with_scratch_txt(label, |ptr| unsafe {
599 sys::ImPlot_ShowInputMapSelector(ptr)
600 })
601}
602
603pub fn map_input_default() {
605 unsafe { sys::ImPlot_MapInputDefault(sys::ImPlot_GetInputMap()) }
606}
607
608pub fn map_input_reverse() {
610 unsafe { sys::ImPlot_MapInputReverse(sys::ImPlot_GetInputMap()) }
611}
612
613pub fn colormap_scale(
617 label: &str,
618 scale_min: f64,
619 scale_max: f64,
620 height: f32,
621 cmap: impl Into<ColormapSelection>,
622) {
623 assert!(
624 scale_min.is_finite(),
625 "colormap_scale scale_min must be finite"
626 );
627 assert!(
628 scale_max.is_finite(),
629 "colormap_scale scale_max must be finite"
630 );
631 assert!(height.is_finite(), "colormap_scale height must be finite");
632 let label = if label.contains('\0') { "" } else { label };
633 let size = sys::ImVec2_c { x: 0.0, y: height };
634 let fmt_ptr: *const c_char = std::ptr::null();
635 let flags = sys::ImPlotColormapScaleFlags_None as sys::ImPlotColormapScaleFlags;
636 let cmap = cmap.into().raw();
637 with_scratch_txt(label, |ptr| unsafe {
638 sys::ImPlot_ColormapScale(ptr, scale_min, scale_max, size, fmt_ptr, flags, cmap)
639 })
640}
641
642pub fn colormap_slider(
644 label: &str,
645 t: &mut f32,
646 out_color: Option<&mut [f32; 4]>,
647 format: Option<&str>,
648 cmap: impl Into<ColormapSelection>,
649) -> bool {
650 assert!(t.is_finite(), "colormap_slider t must be finite");
651 let label = if label.contains('\0') { "" } else { label };
652 let format = format.filter(|s| !s.contains('\0'));
653 let cmap = cmap.into().raw();
654 let mut out = sys::ImVec4 {
655 x: 0.0,
656 y: 0.0,
657 z: 0.0,
658 w: 0.0,
659 };
660 let out_ptr = if out_color.is_some() {
661 &mut out as *mut sys::ImVec4
662 } else {
663 std::ptr::null_mut()
664 };
665
666 let changed = match format {
667 Some(fmt) => with_scratch_txt_two(label, fmt, |label_ptr, fmt_ptr| unsafe {
668 sys::ImPlot_ColormapSlider(label_ptr, t as *mut f32, out_ptr, fmt_ptr, cmap)
669 }),
670 None => with_scratch_txt(label, |label_ptr| unsafe {
671 sys::ImPlot_ColormapSlider(label_ptr, t as *mut f32, out_ptr, std::ptr::null(), cmap)
672 }),
673 };
674
675 if let Some(out_color) = out_color {
676 *out_color = [out.x, out.y, out.z, out.w];
677 }
678 changed
679}
680
681pub fn colormap_button(label: &str, size: [f32; 2], cmap: impl Into<ColormapSelection>) -> bool {
683 assert!(
684 size[0].is_finite() && size[1].is_finite(),
685 "colormap_button size must be finite"
686 );
687 let label = if label.contains('\0') { "" } else { label };
688 let sz = sys::ImVec2_c {
689 x: size[0],
690 y: size[1],
691 };
692 let cmap = cmap.into().raw();
693 with_scratch_txt(label, |ptr| unsafe {
694 sys::ImPlot_ColormapButton(ptr, sz, cmap)
695 })
696}
697
698#[cfg(test)]
699mod tests {
700 use super::{
701 Colormap, ColormapColorIndex, ColormapIndex, ColormapSelection, PlotItemArrayStyle,
702 with_next_plot_item_array_style,
703 };
704 use crate::plots::{PlotDataLayout, PlotDataOffset, PlotDataStride};
705
706 #[test]
707 fn colormap_indices_reject_negative_values() {
708 assert_eq!(ColormapIndex::from_raw(-1), None);
709 assert_eq!(ColormapIndex::new(0).map(ColormapIndex::raw), Some(0));
710 assert_eq!(ColormapIndex::new(0).map(ColormapIndex::get), Some(0));
711 assert_eq!(ColormapIndex::new(i32::MAX as usize + 1), None);
712 assert_eq!(
713 ColormapIndex::from(Colormap::Viridis).raw(),
714 crate::sys::ImPlotColormap_Viridis
715 );
716 assert_eq!(ColormapSelection::Current.raw(), crate::IMPLOT_AUTO);
717 assert_eq!(
718 ColormapSelection::from(Colormap::Viridis).raw(),
719 crate::sys::ImPlotColormap_Viridis
720 );
721
722 assert_eq!(
723 ColormapColorIndex::new(0).map(ColormapColorIndex::get),
724 Some(0)
725 );
726 assert_eq!(
727 ColormapColorIndex::from_usize(i32::MAX as usize).map(ColormapColorIndex::raw),
728 Some(i32::MAX)
729 );
730 assert_eq!(ColormapColorIndex::from_usize(i32::MAX as usize + 1), None);
731 }
732
733 #[test]
734 #[should_panic(expected = "test returned a negative colormap count")]
735 fn colormap_count_conversion_rejects_negative_ffi_values() {
736 let _ = super::colormap_count_from_i32(-1, "test");
737 }
738
739 #[test]
740 #[should_panic(expected = "sample_colormap t must be between 0 and 1")]
741 fn sample_colormap_rejects_out_of_range_t_before_ffi() {
742 let _ = super::sample_colormap(-0.1);
743 }
744
745 #[test]
746 fn next_plot_item_array_style_is_consumed_by_next_spec() {
747 let line_colors = [0x01020304u32, 0x05060708];
748 let fill_colors = [0x11121314u32];
749 let marker_sizes = [2.0f32, 4.0, 8.0];
750
751 with_next_plot_item_array_style(
752 PlotItemArrayStyle::new()
753 .with_line_colors(&line_colors)
754 .with_fill_colors(&fill_colors)
755 .with_marker_sizes(&marker_sizes),
756 || {
757 let layout =
758 PlotDataLayout::new(PlotDataOffset::samples(3), PlotDataStride::bytes(16));
759 let spec = crate::plots::plot_spec_from(7, layout);
760 assert_eq!(spec.Flags, 7);
761 assert_eq!(spec.Offset, 3);
762 assert_eq!(spec.Stride, 16);
763 assert_eq!(spec.LineColors, line_colors.as_ptr() as *mut _);
764 assert_eq!(spec.FillColors, fill_colors.as_ptr() as *mut _);
765 assert_eq!(spec.MarkerSizes, marker_sizes.as_ptr() as *mut _);
766 },
767 );
768
769 let spec = crate::plots::plot_spec_from(0, PlotDataLayout::DEFAULT);
770 assert!(spec.LineColors.is_null());
771 assert!(spec.FillColors.is_null());
772 assert!(spec.MarkerSizes.is_null());
773 }
774
775 #[test]
776 fn next_plot_item_array_style_is_restored_if_unused() {
777 let line_colors = [0xAABBCCDDu32];
778
779 with_next_plot_item_array_style(
780 PlotItemArrayStyle::new().with_line_colors(&line_colors),
781 || {},
782 );
783
784 let spec = crate::plots::plot_spec_from(0, PlotDataLayout::DEFAULT);
785 assert!(spec.LineColors.is_null());
786 }
787}