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