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
266 #[cfg(use_r_ge_version_14)]
267 patternFill: R_NilValue,
268 };
269
270 context
271 .fontfamily
272 .iter_mut()
273 .zip(b"Helvetica".iter())
274 .for_each(|(d, s)| *d = *s as i8);
275
276 Self {
277 context,
278 xscale,
279 yscale,
280 offset,
281 scalar,
282 }
283 }
284 }
285
286 pub fn color(&mut self, col: Color) -> &mut Self {
288 self.context.col = col.to_i32();
289 self
290 }
291
292 pub fn fill(&mut self, fill: Color) -> &mut Self {
294 self.context.fill = fill.to_i32();
295 self
296 }
297
298 pub fn gamma(&mut self, gamma: f64) -> &mut Self {
300 self.context.gamma = gamma;
301 self
302 }
303
304 pub fn line_width(&mut self, lwd: f64) -> &mut Self {
306 self.context.lwd = (lwd * self.scalar).max(1.0);
307 self
308 }
309
310 pub fn line_type(&mut self, lty: LineType) -> &mut Self {
321 self.context.lty = lty.to_i32();
322 self
323 }
324
325 pub fn line_end(&mut self, lend: LineEnd) -> &mut Self {
332 self.context.lend = lend.into();
333 self
334 }
335
336 pub fn line_join(&mut self, ljoin: LineJoin) -> &mut Self {
343 self.context.ljoin = ljoin.into();
344 self
345 }
346
347 pub fn point_size(&mut self, ps: f64) -> &mut Self {
348 self.context.ps = ps;
349 self
350 }
351
352 pub fn line_mitre(&mut self, lmitre: f64) -> &mut Self {
354 self.context.lmitre = lmitre * self.scalar;
355 self
356 }
357
358 pub fn line_height(&mut self, lineheight: f64) -> &mut Self {
360 self.context.lineheight = lineheight;
361 self
362 }
363
364 pub fn font_face(&mut self, fontface: FontFace) -> &mut Self {
378 self.context.fontface = fontface.to_i32();
379 self
380 }
381
382 pub fn font_family(&mut self, fontfamily: &str) -> &mut Self {
384 let maxlen = self.context.fontfamily.len() - 1;
385
386 for c in self.context.fontfamily.iter_mut() {
387 *c = 0;
388 }
389
390 for (i, b) in fontfamily.bytes().enumerate().take(maxlen) {
391 self.context.fontfamily[i] = b as std::os::raw::c_char;
392 }
393 self
394 }
395
396 pub fn transform(
398 &mut self,
399 xscale: (f64, f64),
400 yscale: (f64, f64),
401 offset: (f64, f64),
402 ) -> &mut Self {
403 self.xscale = xscale;
404 self.yscale = yscale;
405 self.offset = offset;
406 self
407 }
408
409 pub(crate) fn context(&self) -> pGEcontext {
410 unsafe { std::mem::transmute(&self.context) }
411 }
412
413 pub(crate) fn t(&self, xy: (f64, f64)) -> (f64, f64) {
415 (
416 self.offset.0 + xy.0 * self.xscale.0 + xy.1 * self.yscale.0,
417 self.offset.1 + xy.0 * self.xscale.1 + xy.1 * self.yscale.1,
418 )
419 }
420
421 pub(crate) fn trel(&self, wh: (f64, f64)) -> (f64, f64) {
423 (
424 wh.0 * self.xscale.0 + wh.1 * self.yscale.0,
425 wh.0 * self.xscale.1 + wh.1 * self.yscale.1,
426 )
427 }
428
429 pub(crate) fn ts(&self, value: f64) -> f64 {
431 value * self.scalar
432 }
433
434 pub(crate) fn its(&self, value: f64) -> f64 {
436 value / self.scalar
437 }
438
439 pub(crate) fn tmetric(&self, tm: TextMetric) -> TextMetric {
440 TextMetric {
441 ascent: tm.ascent / self.scalar,
442 descent: tm.descent / self.scalar,
443 width: tm.width / self.scalar,
444 }
445 }
446}
447
448#[allow(non_snake_case)]
449impl Device {
450 pub fn current() -> Result<Device> {
452 unsafe {
455 Ok(Device {
456 inner: GEcurrentDevice(),
457 })
458 }
459 }
460
461 pub fn mode_on(&self) -> Result<()> {
463 unsafe {
464 if Rf_NoDevices() != 0 {
465 Err(Error::NoGraphicsDevices(Robj::from(())))
466 } else {
467 GEMode(1, self.inner());
468 Ok(())
469 }
470 }
471 }
472
473 pub fn mode_off(&self) -> Result<()> {
475 unsafe {
476 if Rf_NoDevices() != 0 {
477 Err(Error::NoGraphicsDevices(Robj::from(())))
478 } else {
479 GEMode(0, self.inner());
480 Ok(())
481 }
482 }
483 }
484
485 pub fn device_number(&self) -> i32 {
487 unsafe { GEdeviceNumber(self.inner()) }
488 }
489
490 pub fn get_device(number: i32) -> Result<Device> {
492 unsafe {
493 if number < 0 || number >= Rf_NumDevices() {
494 Err(Error::NoGraphicsDevices(Robj::from(())))
495 } else {
496 Ok(Device {
497 inner: GEgetDevice(number),
498 })
499 }
500 }
501 }
502
503 pub fn from_device_coords(&self, value: (f64, f64), from: Unit) -> (f64, f64) {
506 let from = unit_to_ge(from);
507 unsafe {
508 (
509 GEfromDeviceX(value.0, from, self.inner()),
510 GEfromDeviceY(value.1, from, self.inner()),
511 )
512 }
513 }
514
515 pub fn to_device_coords(&self, value: (f64, f64), to: Unit) -> (f64, f64) {
518 if to == Unit::Device {
519 value
520 } else {
521 let to = unit_to_ge(to);
522 unsafe {
523 (
524 GEtoDeviceX(value.0, to, self.inner()),
525 GEtoDeviceY(value.1, to, self.inner()),
526 )
527 }
528 }
529 }
530
531 pub fn from_device_wh(&self, value: (f64, f64), from: Unit) -> (f64, f64) {
534 let from = unit_to_ge(from);
535 unsafe {
536 (
537 GEfromDeviceWidth(value.0, from, self.inner()),
538 GEfromDeviceHeight(value.1, from, self.inner()),
539 )
540 }
541 }
542
543 pub fn to_device_wh(&self, value: (f64, f64), to: Unit) -> (f64, f64) {
546 let to = unit_to_ge(to);
547 unsafe {
548 (
549 GEtoDeviceWidth(value.0, to, self.inner()),
550 GEtoDeviceHeight(value.1, to, self.inner()),
551 )
552 }
553 }
554
555 pub fn new_page(&self, gc: &Context) {
557 unsafe { GENewPage(gc.context(), self.inner()) }
558 }
559
560 pub fn clip(&self, from: (f64, f64), to: (f64, f64), gc: &Context) {
562 let from = gc.t(from);
563 let to = gc.t(to);
564 unsafe { GESetClip(from.0, from.1, to.0, to.1, self.inner()) }
565 }
566
567 pub fn line(&self, from: (f64, f64), to: (f64, f64), gc: &Context) {
569 let from = gc.t(from);
570 let to = gc.t(to);
571 unsafe { GELine(from.0, from.1, to.0, to.1, gc.context(), self.inner()) }
572 }
573
574 pub fn polyline<T: IntoIterator<Item = (f64, f64)>>(&self, coords: T, gc: &Context) {
578 let (mut x, mut y): (Vec<_>, Vec<_>) = coords.into_iter().map(|xy| gc.t(xy)).unzip();
579 let xptr = x.as_mut_slice().as_mut_ptr();
580 let yptr = y.as_mut_slice().as_mut_ptr();
581 unsafe {
582 GEPolyline(
583 x.len() as std::os::raw::c_int,
584 xptr,
585 yptr,
586 gc.context(),
587 self.inner(),
588 )
589 }
590 }
591
592 pub fn polygon<T: IntoIterator<Item = (f64, f64)>>(&self, coords: T, gc: &Context) {
596 let (mut x, mut y): (Vec<_>, Vec<_>) = coords.into_iter().map(|xy| gc.t(xy)).unzip();
597 let xptr = x.as_mut_slice().as_mut_ptr();
598 let yptr = y.as_mut_slice().as_mut_ptr();
599 unsafe {
600 GEPolygon(
601 x.len() as std::os::raw::c_int,
602 xptr,
603 yptr,
604 gc.context(),
605 self.inner(),
606 )
607 }
608 }
609
610 pub fn circle(&self, center: (f64, f64), radius: f64, gc: &Context) {
647 let center = gc.t(center);
648 let radius = gc.ts(radius);
649 unsafe { GECircle(center.0, center.1, radius, gc.context(), self.inner()) }
650 }
651
652 pub fn rect(&self, from: (f64, f64), to: (f64, f64), gc: &Context) {
656 let from = gc.t(from);
657 let to = gc.t(to);
658 unsafe { GERect(from.0, from.1, to.0, to.1, gc.context(), self.inner()) }
659 }
660
661 pub fn path<T: IntoIterator<Item = impl IntoIterator<Item = (f64, f64)>>>(
666 &self,
667 coords: T,
668 winding: bool,
669 gc: &Context,
670 ) {
671 let mut x = Vec::new();
672 let mut y = Vec::new();
673 let mut nper: Vec<std::os::raw::c_int> = Vec::new();
674 let coords = coords.into_iter();
675 for segment in coords {
676 let mut n = 0;
677 for xy in segment {
678 let xy = gc.t(xy);
679 x.push(xy.0);
680 y.push(xy.1);
681 n += 1;
682 }
683 nper.push(n);
684 }
685
686 let xptr = x.as_mut_slice().as_mut_ptr();
687 let yptr = y.as_mut_slice().as_mut_ptr();
688 let nperptr = nper.as_mut_slice().as_mut_ptr();
689 unsafe {
690 GEPath(
691 xptr,
692 yptr,
693 nper.len() as std::os::raw::c_int,
694 nperptr,
695 winding.into(),
696 gc.context(),
697 self.inner(),
698 )
699 }
700 }
701
702 pub fn capture(&self) -> Robj {
704 unsafe { Robj::from_sexp(GECap(self.inner())) }
705 }
706
707 pub fn raster<T: AsRef<[u32]>>(
709 &self,
710 raster: Raster<T>,
711 pos: (f64, f64),
712 size: (f64, f64),
713 angle: f64,
714 interpolate: bool,
715 gc: &Context,
716 ) {
717 let (x, y) = gc.t(pos);
718 let (width, height) = gc.trel(size);
719 let w = raster.width;
720 let pixels = raster.pixels.as_ref();
721 let h = pixels.len() / w;
722 unsafe {
723 let raster = pixels.as_ptr() as *mut u32;
724 let w = w as i32;
725 let h = h as i32;
726 let interpolate = interpolate.into();
727 GERaster(
728 raster,
729 w,
730 h,
731 x,
732 y,
733 width,
734 height,
735 angle,
736 interpolate,
737 gc.context(),
738 self.inner(),
739 )
740 };
741 }
742
743 pub fn text<T: AsRef<str>>(
746 &self,
747 pos: (f64, f64),
748 text: T,
749 center: (f64, f64),
750 rot: f64,
751 gc: &Context,
752 ) {
753 unsafe {
754 let (x, y) = gc.t(pos);
755 let (xc, yc) = gc.trel(center);
756 let text = std::ffi::CString::new(text.as_ref()).unwrap();
757 let enc = cetype_t::CE_UTF8;
758 GEText(
759 x,
760 y,
761 text.as_ptr(),
762 enc,
763 xc,
764 yc,
765 rot,
766 gc.context(),
767 self.inner(),
768 );
769 }
770 }
771
772 pub fn symbol(&self, pos: (f64, f64), symbol: i32, size: f64, gc: &Context) {
775 unsafe {
776 let (x, y) = gc.t(pos);
777 GESymbol(x, y, symbol, gc.ts(size), gc.context(), self.inner());
778 }
779 }
780
781 pub fn char_metric(&self, c: char, gc: &Context) -> TextMetric {
783 unsafe {
784 let mut res = TextMetric {
785 ascent: 0.0,
786 descent: 0.0,
787 width: 0.0,
788 };
789 GEMetricInfo(
790 c as i32,
791 gc.context(),
792 &mut res.ascent as *mut f64,
793 &mut res.descent as *mut f64,
794 &mut res.width as *mut f64,
795 self.inner(),
796 );
797 gc.tmetric(res)
798 }
799 }
800
801 pub fn text_width<T: AsRef<str>>(&self, text: T, gc: &Context) -> f64 {
803 let text = std::ffi::CString::new(text.as_ref()).unwrap();
804 let enc = cetype_t::CE_UTF8;
805 unsafe { gc.its(GEStrWidth(text.as_ptr(), enc, gc.context(), self.inner())) }
806 }
807
808 pub fn text_height<T: AsRef<str>>(&self, text: T, gc: &Context) -> f64 {
810 let text = std::ffi::CString::new(text.as_ref()).unwrap();
811 let enc = cetype_t::CE_UTF8;
812 unsafe { gc.its(GEStrHeight(text.as_ptr(), enc, gc.context(), self.inner())) }
813 }
814
815 pub fn text_metric<T: AsRef<str>>(&self, text: T, gc: &Context) -> TextMetric {
817 let text = std::ffi::CString::new(text.as_ref()).unwrap();
818 let enc = cetype_t::CE_UTF8;
819 unsafe {
820 let mut res = TextMetric {
821 ascent: 0.0,
822 descent: 0.0,
823 width: 0.0,
824 };
825 GEStrMetric(
826 text.as_ptr(),
827 enc,
828 gc.context(),
829 &mut res.ascent as *mut f64,
830 &mut res.descent as *mut f64,
831 &mut res.width as *mut f64,
832 self.inner(),
833 );
834 gc.tmetric(res)
835 }
836 }
837
838 pub fn math_text_width(&self, expr: &Robj, gc: &Context) -> f64 {
840 unsafe { gc.its(GEExpressionWidth(expr.get(), gc.context(), self.inner())) }
841 }
842
843 pub fn math_text_height(&self, expr: &Robj, gc: &Context) -> f64 {
845 unsafe { gc.its(GEExpressionHeight(expr.get(), gc.context(), self.inner())) }
846 }
847
848 pub fn math_text_metric(&self, expr: &Robj, gc: &Context) -> TextMetric {
850 unsafe {
851 let mut res = TextMetric {
852 ascent: 0.0,
853 descent: 0.0,
854 width: 0.0,
855 };
856 GEExpressionMetric(
857 expr.get(),
858 gc.context(),
859 &mut res.ascent as *mut f64,
860 &mut res.descent as *mut f64,
861 &mut res.width as *mut f64,
862 self.inner(),
863 );
864 gc.tmetric(res)
865 }
866 }
867
868 pub fn math_text(
870 &self,
871 expr: &Robj,
872 pos: (f64, f64),
873 center: (f64, f64),
874 rot: f64,
875 gc: &Context,
876 ) {
877 unsafe {
878 let (x, y) = gc.t(pos);
879 let (xc, yc) = gc.trel(center);
880 GEMathText(x, y, expr.get(), xc, yc, rot, gc.context(), self.inner());
881 }
882 }
883}