1use crate::*;
80
81pub use extendr_ffi::{cetype_t, graphics::*, R_NilValue, Rf_NoDevices, Rf_NumDevices};
83
84pub mod color;
85pub mod device_descriptor;
86pub mod device_driver;
87
88use color::Color;
89pub use device_descriptor::*;
90pub use device_driver::*;
91
92pub struct Context {
93 context: R_GE_gcontext,
94 xscale: (f64, f64),
95 yscale: (f64, f64),
96 offset: (f64, f64),
97 scalar: f64,
98}
99
100#[derive(Clone, Debug, PartialEq)]
101pub struct Device {
102 inner: pGEDevDesc,
103}
104
105#[derive(Clone, Debug, PartialEq)]
106pub struct Pattern {
107 inner: Robj,
108}
109
110#[derive(Clone, Debug, PartialEq)]
111pub struct TextMetric {
112 pub ascent: f64,
113 pub descent: f64,
114 pub width: f64,
115}
116
117#[derive(Clone, Debug, PartialEq)]
120pub struct Raster<P: AsRef<[u32]>> {
121 pub pixels: P,
122 pub width: usize,
123}
124
125impl Device {
126 pub(crate) fn inner(&self) -> pGEDevDesc {
127 self.inner
128 }
129
130 }
138
139#[derive(PartialEq, Debug, Clone)]
140pub enum LineEnd {
141 Round,
142 Butt,
143 Square,
144}
145
146#[derive(PartialEq, Debug, Clone)]
147pub enum LineJoin {
148 Round,
149 Mitre,
150 Bevel,
151}
152
153#[derive(PartialEq, Debug, Clone)]
154pub enum LineType {
155 Blank,
156 Solid,
157 Dashed,
158 Dotted,
159 DotDash,
160 LongDash,
161 TwoDash,
162}
163
164#[derive(PartialEq, Debug, Clone)]
165pub enum Unit {
166 Device,
167 Normalized,
168 Inches,
169 CM,
170}
171
172#[derive(PartialEq, Debug, Clone)]
173pub enum FontFace {
174 Plain,
175 Bold,
176 Italic,
177 BoldItalic,
178 Symbol,
179}
180
181impl From<LineEnd> for R_GE_lineend {
182 fn from(value: LineEnd) -> Self {
183 match value {
184 LineEnd::Round => Self::GE_ROUND_CAP,
185 LineEnd::Butt => Self::GE_BUTT_CAP,
186 LineEnd::Square => Self::GE_SQUARE_CAP,
187 }
188 }
189}
190
191impl From<LineJoin> for R_GE_linejoin {
192 fn from(value: LineJoin) -> Self {
193 match value {
194 LineJoin::Round => Self::GE_ROUND_JOIN,
195 LineJoin::Mitre => Self::GE_MITRE_JOIN,
196 LineJoin::Bevel => Self::GE_BEVEL_JOIN,
197 }
198 }
199}
200
201impl LineType {
202 fn to_i32(&self) -> i32 {
203 match self {
204 Self::Blank => LTY_BLANK as _,
205 Self::Solid => LTY_SOLID as _,
206 Self::Dashed => LTY_DASHED as _,
207 Self::Dotted => LTY_DOTTED as _,
208 Self::DotDash => LTY_DOTDASH as _,
209 Self::LongDash => LTY_LONGDASH as _,
210 Self::TwoDash => LTY_TWODASH as _,
211 }
212 }
213}
214
215impl FontFace {
216 fn to_i32(&self) -> i32 {
217 match self {
218 Self::Plain => 1,
219 Self::Bold => 2,
220 Self::Italic => 3,
221 Self::BoldItalic => 4,
222 Self::Symbol => 5,
223 }
224 }
225}
226
227fn unit_to_ge(unit: Unit) -> GEUnit {
228 match unit {
229 Unit::Device => GEUnit::GE_DEVICE,
230 Unit::Normalized => GEUnit::GE_NDC,
231 Unit::Inches => GEUnit::GE_INCHES,
232 Unit::CM => GEUnit::GE_CM,
233 }
234}
235
236impl Context {
237 pub fn from_device(dev: &Device, unit: Unit) -> Self {
238 #[allow(unused_unsafe)]
239 unsafe {
240 let offset = dev.to_device_coords((0., 0.), unit.clone());
241 let mut xscale = dev.to_device_coords((1., 0.), unit.clone());
242 let mut yscale = dev.to_device_coords((0., 1.), unit);
243 xscale.0 -= offset.0;
244 xscale.1 -= offset.1;
245 yscale.0 -= offset.0;
246 yscale.1 -= offset.1;
247
248 let scalar = (xscale.0 * yscale.1 - xscale.1 * yscale.0).abs().sqrt();
250
251 let mut context = R_GE_gcontext {
252 col: Color::rgb(0xff, 0xff, 0xff).to_i32(),
253 fill: Color::rgb(0xc0, 0xc0, 0xc0).to_i32(),
254 gamma: 1.0,
255 lwd: 1.0,
256 lty: 0,
257 lend: R_GE_lineend::GE_ROUND_CAP,
258 ljoin: R_GE_linejoin::GE_ROUND_JOIN,
259 lmitre: 10.0,
260 cex: 1.0,
261 ps: 14.0,
262 lineheight: 1.0,
263 fontface: 1,
264 fontfamily: [0; 201],
265 patternFill: R_NilValue,
266 };
267
268 context
269 .fontfamily
270 .iter_mut()
271 .zip(b"Helvetica".iter())
272 .for_each(|(d, s)| *d = *s as i8);
273
274 Self {
275 context,
276 xscale,
277 yscale,
278 offset,
279 scalar,
280 }
281 }
282 }
283
284 pub fn color(&mut self, col: Color) -> &mut Self {
286 self.context.col = col.to_i32();
287 self
288 }
289
290 pub fn fill(&mut self, fill: Color) -> &mut Self {
292 self.context.fill = fill.to_i32();
293 self
294 }
295
296 pub fn gamma(&mut self, gamma: f64) -> &mut Self {
298 self.context.gamma = gamma;
299 self
300 }
301
302 pub fn line_width(&mut self, lwd: f64) -> &mut Self {
304 self.context.lwd = (lwd * self.scalar).max(1.0);
305 self
306 }
307
308 pub fn line_type(&mut self, lty: LineType) -> &mut Self {
319 self.context.lty = lty.to_i32();
320 self
321 }
322
323 pub fn line_end(&mut self, lend: LineEnd) -> &mut Self {
330 self.context.lend = lend.into();
331 self
332 }
333
334 pub fn line_join(&mut self, ljoin: LineJoin) -> &mut Self {
341 self.context.ljoin = ljoin.into();
342 self
343 }
344
345 pub fn point_size(&mut self, ps: f64) -> &mut Self {
346 self.context.ps = ps;
347 self
348 }
349
350 pub fn line_mitre(&mut self, lmitre: f64) -> &mut Self {
352 self.context.lmitre = lmitre * self.scalar;
353 self
354 }
355
356 pub fn line_height(&mut self, lineheight: f64) -> &mut Self {
358 self.context.lineheight = lineheight;
359 self
360 }
361
362 pub fn font_face(&mut self, fontface: FontFace) -> &mut Self {
376 self.context.fontface = fontface.to_i32();
377 self
378 }
379
380 pub fn font_family(&mut self, fontfamily: &str) -> &mut Self {
382 let maxlen = self.context.fontfamily.len() - 1;
383
384 for c in self.context.fontfamily.iter_mut() {
385 *c = 0;
386 }
387
388 for (i, b) in fontfamily.bytes().enumerate().take(maxlen) {
389 self.context.fontfamily[i] = b as std::os::raw::c_char;
390 }
391 self
392 }
393
394 pub fn transform(
396 &mut self,
397 xscale: (f64, f64),
398 yscale: (f64, f64),
399 offset: (f64, f64),
400 ) -> &mut Self {
401 self.xscale = xscale;
402 self.yscale = yscale;
403 self.offset = offset;
404 self
405 }
406
407 pub(crate) fn context(&self) -> pGEcontext {
408 &self.context as *const extendr_ffi::R_GE_gcontext as *mut extendr_ffi::R_GE_gcontext
409 }
410
411 pub(crate) fn t(&self, xy: (f64, f64)) -> (f64, f64) {
413 (
414 self.offset.0 + xy.0 * self.xscale.0 + xy.1 * self.yscale.0,
415 self.offset.1 + xy.0 * self.xscale.1 + xy.1 * self.yscale.1,
416 )
417 }
418
419 pub(crate) fn trel(&self, wh: (f64, f64)) -> (f64, f64) {
421 (
422 wh.0 * self.xscale.0 + wh.1 * self.yscale.0,
423 wh.0 * self.xscale.1 + wh.1 * self.yscale.1,
424 )
425 }
426
427 pub(crate) fn ts(&self, value: f64) -> f64 {
429 value * self.scalar
430 }
431
432 pub(crate) fn its(&self, value: f64) -> f64 {
434 value / self.scalar
435 }
436
437 pub(crate) fn tmetric(&self, tm: TextMetric) -> TextMetric {
438 TextMetric {
439 ascent: tm.ascent / self.scalar,
440 descent: tm.descent / self.scalar,
441 width: tm.width / self.scalar,
442 }
443 }
444}
445
446#[allow(non_snake_case)]
447impl Device {
448 pub fn current() -> Result<Device> {
450 unsafe {
453 Ok(Device {
454 inner: GEcurrentDevice(),
455 })
456 }
457 }
458
459 pub fn mode_on(&self) -> Result<()> {
461 unsafe {
462 if Rf_NoDevices() != 0 {
463 Err(Error::NoGraphicsDevices(Robj::from(())))
464 } else {
465 GEMode(1, self.inner());
466 Ok(())
467 }
468 }
469 }
470
471 pub fn mode_off(&self) -> Result<()> {
473 unsafe {
474 if Rf_NoDevices() != 0 {
475 Err(Error::NoGraphicsDevices(Robj::from(())))
476 } else {
477 GEMode(0, self.inner());
478 Ok(())
479 }
480 }
481 }
482
483 pub fn device_number(&self) -> i32 {
485 unsafe { GEdeviceNumber(self.inner()) }
486 }
487
488 pub fn get_device(number: i32) -> Result<Device> {
490 unsafe {
491 if number < 0 || number >= Rf_NumDevices() {
492 Err(Error::NoGraphicsDevices(Robj::from(())))
493 } else {
494 Ok(Device {
495 inner: GEgetDevice(number),
496 })
497 }
498 }
499 }
500
501 pub fn from_device_coords(&self, value: (f64, f64), from: Unit) -> (f64, f64) {
504 let from = unit_to_ge(from);
505 unsafe {
506 (
507 GEfromDeviceX(value.0, from, self.inner()),
508 GEfromDeviceY(value.1, from, self.inner()),
509 )
510 }
511 }
512
513 pub fn to_device_coords(&self, value: (f64, f64), to: Unit) -> (f64, f64) {
516 if to == Unit::Device {
517 value
518 } else {
519 let to = unit_to_ge(to);
520 unsafe {
521 (
522 GEtoDeviceX(value.0, to, self.inner()),
523 GEtoDeviceY(value.1, to, self.inner()),
524 )
525 }
526 }
527 }
528
529 pub fn from_device_wh(&self, value: (f64, f64), from: Unit) -> (f64, f64) {
532 let from = unit_to_ge(from);
533 unsafe {
534 (
535 GEfromDeviceWidth(value.0, from, self.inner()),
536 GEfromDeviceHeight(value.1, from, self.inner()),
537 )
538 }
539 }
540
541 pub fn to_device_wh(&self, value: (f64, f64), to: Unit) -> (f64, f64) {
544 let to = unit_to_ge(to);
545 unsafe {
546 (
547 GEtoDeviceWidth(value.0, to, self.inner()),
548 GEtoDeviceHeight(value.1, to, self.inner()),
549 )
550 }
551 }
552
553 pub fn new_page(&self, gc: &Context) {
555 unsafe { GENewPage(gc.context(), self.inner()) }
556 }
557
558 pub fn clip(&self, from: (f64, f64), to: (f64, f64), gc: &Context) {
560 let from = gc.t(from);
561 let to = gc.t(to);
562 unsafe { GESetClip(from.0, from.1, to.0, to.1, self.inner()) }
563 }
564
565 pub fn line(&self, from: (f64, f64), to: (f64, f64), gc: &Context) {
567 let from = gc.t(from);
568 let to = gc.t(to);
569 unsafe { GELine(from.0, from.1, to.0, to.1, gc.context(), self.inner()) }
570 }
571
572 pub fn polyline<T: IntoIterator<Item = (f64, f64)>>(&self, coords: T, gc: &Context) {
576 let (mut x, mut y): (Vec<_>, Vec<_>) = coords.into_iter().map(|xy| gc.t(xy)).unzip();
577 let xptr = x.as_mut_slice().as_mut_ptr();
578 let yptr = y.as_mut_slice().as_mut_ptr();
579 unsafe {
580 GEPolyline(
581 x.len() as std::os::raw::c_int,
582 xptr,
583 yptr,
584 gc.context(),
585 self.inner(),
586 )
587 }
588 }
589
590 pub fn polygon<T: IntoIterator<Item = (f64, f64)>>(&self, coords: T, gc: &Context) {
594 let (mut x, mut y): (Vec<_>, Vec<_>) = coords.into_iter().map(|xy| gc.t(xy)).unzip();
595 let xptr = x.as_mut_slice().as_mut_ptr();
596 let yptr = y.as_mut_slice().as_mut_ptr();
597 unsafe {
598 GEPolygon(
599 x.len() as std::os::raw::c_int,
600 xptr,
601 yptr,
602 gc.context(),
603 self.inner(),
604 )
605 }
606 }
607
608 pub fn circle(&self, center: (f64, f64), radius: f64, gc: &Context) {
645 let center = gc.t(center);
646 let radius = gc.ts(radius);
647 unsafe { GECircle(center.0, center.1, radius, gc.context(), self.inner()) }
648 }
649
650 pub fn rect(&self, from: (f64, f64), to: (f64, f64), gc: &Context) {
654 let from = gc.t(from);
655 let to = gc.t(to);
656 unsafe { GERect(from.0, from.1, to.0, to.1, gc.context(), self.inner()) }
657 }
658
659 pub fn path<T: IntoIterator<Item = impl IntoIterator<Item = (f64, f64)>>>(
664 &self,
665 coords: T,
666 winding: bool,
667 gc: &Context,
668 ) {
669 let mut x = Vec::new();
670 let mut y = Vec::new();
671 let mut nper: Vec<std::os::raw::c_int> = Vec::new();
672 let coords = coords.into_iter();
673 for segment in coords {
674 let mut n = 0;
675 for xy in segment {
676 let xy = gc.t(xy);
677 x.push(xy.0);
678 y.push(xy.1);
679 n += 1;
680 }
681 nper.push(n);
682 }
683
684 let xptr = x.as_mut_slice().as_mut_ptr();
685 let yptr = y.as_mut_slice().as_mut_ptr();
686 let nperptr = nper.as_mut_slice().as_mut_ptr();
687 unsafe {
688 GEPath(
689 xptr,
690 yptr,
691 nper.len() as std::os::raw::c_int,
692 nperptr,
693 winding.into(),
694 gc.context(),
695 self.inner(),
696 )
697 }
698 }
699
700 pub fn capture(&self) -> Robj {
702 unsafe { Robj::from_sexp(GECap(self.inner())) }
703 }
704
705 pub fn raster<T: AsRef<[u32]>>(
707 &self,
708 raster: Raster<T>,
709 pos: (f64, f64),
710 size: (f64, f64),
711 angle: f64,
712 interpolate: bool,
713 gc: &Context,
714 ) {
715 let (x, y) = gc.t(pos);
716 let (width, height) = gc.trel(size);
717 let w = raster.width;
718 let pixels = raster.pixels.as_ref();
719 let h = pixels.len() / w;
720 unsafe {
721 let raster = pixels.as_ptr() as *mut u32;
722 let w = w as i32;
723 let h = h as i32;
724 let interpolate = interpolate.into();
725 GERaster(
726 raster,
727 w,
728 h,
729 x,
730 y,
731 width,
732 height,
733 angle,
734 interpolate,
735 gc.context(),
736 self.inner(),
737 )
738 };
739 }
740
741 pub fn text<T: AsRef<str>>(
744 &self,
745 pos: (f64, f64),
746 text: T,
747 center: (f64, f64),
748 rot: f64,
749 gc: &Context,
750 ) {
751 unsafe {
752 let (x, y) = gc.t(pos);
753 let (xc, yc) = gc.trel(center);
754 let text = std::ffi::CString::new(text.as_ref()).unwrap();
755 let enc = cetype_t::CE_UTF8;
756 GEText(
757 x,
758 y,
759 text.as_ptr(),
760 enc,
761 xc,
762 yc,
763 rot,
764 gc.context(),
765 self.inner(),
766 );
767 }
768 }
769
770 pub fn symbol(&self, pos: (f64, f64), symbol: i32, size: f64, gc: &Context) {
773 unsafe {
774 let (x, y) = gc.t(pos);
775 GESymbol(x, y, symbol, gc.ts(size), gc.context(), self.inner());
776 }
777 }
778
779 pub fn char_metric(&self, c: char, gc: &Context) -> TextMetric {
781 unsafe {
782 let mut res = TextMetric {
783 ascent: 0.0,
784 descent: 0.0,
785 width: 0.0,
786 };
787 GEMetricInfo(
788 c as i32,
789 gc.context(),
790 &mut res.ascent as *mut f64,
791 &mut res.descent as *mut f64,
792 &mut res.width as *mut f64,
793 self.inner(),
794 );
795 gc.tmetric(res)
796 }
797 }
798
799 pub fn text_width<T: AsRef<str>>(&self, text: T, gc: &Context) -> f64 {
801 let text = std::ffi::CString::new(text.as_ref()).unwrap();
802 let enc = cetype_t::CE_UTF8;
803 unsafe { gc.its(GEStrWidth(text.as_ptr(), enc, gc.context(), self.inner())) }
804 }
805
806 pub fn text_height<T: AsRef<str>>(&self, text: T, gc: &Context) -> f64 {
808 let text = std::ffi::CString::new(text.as_ref()).unwrap();
809 let enc = cetype_t::CE_UTF8;
810 unsafe { gc.its(GEStrHeight(text.as_ptr(), enc, gc.context(), self.inner())) }
811 }
812
813 pub fn text_metric<T: AsRef<str>>(&self, text: T, gc: &Context) -> TextMetric {
815 let text = std::ffi::CString::new(text.as_ref()).unwrap();
816 let enc = cetype_t::CE_UTF8;
817 unsafe {
818 let mut res = TextMetric {
819 ascent: 0.0,
820 descent: 0.0,
821 width: 0.0,
822 };
823 GEStrMetric(
824 text.as_ptr(),
825 enc,
826 gc.context(),
827 &mut res.ascent as *mut f64,
828 &mut res.descent as *mut f64,
829 &mut res.width as *mut f64,
830 self.inner(),
831 );
832 gc.tmetric(res)
833 }
834 }
835
836 pub fn math_text_width(&self, expr: &Robj, gc: &Context) -> f64 {
838 unsafe { gc.its(GEExpressionWidth(expr.get(), gc.context(), self.inner())) }
839 }
840
841 pub fn math_text_height(&self, expr: &Robj, gc: &Context) -> f64 {
843 unsafe { gc.its(GEExpressionHeight(expr.get(), gc.context(), self.inner())) }
844 }
845
846 pub fn math_text_metric(&self, expr: &Robj, gc: &Context) -> TextMetric {
848 unsafe {
849 let mut res = TextMetric {
850 ascent: 0.0,
851 descent: 0.0,
852 width: 0.0,
853 };
854 GEExpressionMetric(
855 expr.get(),
856 gc.context(),
857 &mut res.ascent as *mut f64,
858 &mut res.descent as *mut f64,
859 &mut res.width as *mut f64,
860 self.inner(),
861 );
862 gc.tmetric(res)
863 }
864 }
865
866 pub fn math_text(
868 &self,
869 expr: &Robj,
870 pos: (f64, f64),
871 center: (f64, f64),
872 rot: f64,
873 gc: &Context,
874 ) {
875 unsafe {
876 let (x, y) = gc.t(pos);
877 let (xc, yc) = gc.trel(center);
878 GEMathText(x, y, expr.get(), xc, yc, rot, gc.context(), self.inner());
879 }
880 }
881}