daqana / tikzDevice

A R package for producing graphics output as PGF/TikZ code for use in TeX documents.
https://daqana.github.io/tikzDevice
132 stars 26 forks source link

Unwanted cropping of ggplot2 objects #147

Open karseneau opened 8 years ago

karseneau commented 8 years ago

There is visible cropping of .tex output from tikzDevice caused by \path[use as bounding box, ... ] and \path[clip] lines generated by the package. When these lines are removed, the tikzpicture renders correctly when compiled with knitr.

This may be an issue caused by an "artifact left from rather old PSTricks habits" according to this older answer from StackExchange.

krlmlr commented 7 years ago

Thanks. Can you please submit a reproducible example where the cropping affects the output?

karseneau commented 7 years ago

Thanks Kirill (@krlmlr) for getting to look at this, please see reprex below.

library(lubridate)
#> 
#> Attaching package: 'lubridate'
#> The following object is masked from 'package:base':
#> 
#>     date
library(ggplot2)
library(tikzDevice)

df <- dplyr::tibble(
  x = as.Date("2017-07-01") - months(0:11),
  y = sample(0:9, 12, replace = TRUE)
)

out_file <- capture.output(
  tikz(console = TRUE, width = 2.5, height = 1),
  plot <- ggplot(df, aes(x, y)) +
    geom_line(size = 3) +
    geom_point() +
    theme_void() +
    theme(legend.position = "none"),
  print(plot),
  dev.off()
)

# tikzDevice output
writeLines(out_file)
#> % Created by tikzDevice version 0.10.1 on 2017-08-15 11:48:28
#> % !TEX encoding = UTF-8 Unicode
#> \relax
#> \begin{tikzpicture}[x=1pt,y=1pt]
#> \definecolor{fillColor}{RGB}{255,255,255}
#> \path[use as bounding box,fill=fillColor,fill opacity=0.00] (0,0) rectangle (180.67, 72.27);
#> \begin{scope}
#> \path[clip] (  2.75,  2.75) rectangle (180.67, 72.27);
#> \definecolor{drawColor}{RGB}{0,0,0}
#> 
#> \path[draw=drawColor,line width= 3.4pt,line join=round] ( 10.84, 21.71) --
#>  ( 25.85, 37.51) --
#>  ( 40.38, 53.31) --
#>  ( 55.39, 69.11) --
#>  ( 69.92, 13.81) --
#>  ( 84.93, 29.61) --
#>  ( 99.95, 69.11) --
#>  (113.51, 61.21) --
#>  (128.52,  5.91) --
#>  (143.05, 21.71) --
#>  (158.06, 53.31) --
#>  (172.59, 69.11);
#> \definecolor{fillColor}{RGB}{0,0,0}
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (172.59, 69.11) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (158.06, 53.31) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (143.05, 21.71) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (128.52,  5.91) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (113.51, 61.21) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 99.95, 69.11) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 84.93, 29.61) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 69.92, 13.81) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 55.39, 69.11) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 40.38, 53.31) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 25.85, 37.51) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 10.84, 21.71) circle (  1.96);
#> \end{scope}
#> \end{tikzpicture}
#> png 
#>   2

# manually trimmed
writeLines(
  out_file[-(c(1:3, 6, 8, (length(out_file) - 1):length(out_file)))]
)
#> \begin{tikzpicture}[x=1pt,y=1pt]
#> \definecolor{fillColor}{RGB}{255,255,255}
#> \begin{scope}
#> \definecolor{drawColor}{RGB}{0,0,0}
#> 
#> \path[draw=drawColor,line width= 3.4pt,line join=round] ( 10.84, 21.71) --
#>  ( 25.85, 37.51) --
#>  ( 40.38, 53.31) --
#>  ( 55.39, 69.11) --
#>  ( 69.92, 13.81) --
#>  ( 84.93, 29.61) --
#>  ( 99.95, 69.11) --
#>  (113.51, 61.21) --
#>  (128.52,  5.91) --
#>  (143.05, 21.71) --
#>  (158.06, 53.31) --
#>  (172.59, 69.11);
#> \definecolor{fillColor}{RGB}{0,0,0}
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (172.59, 69.11) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (158.06, 53.31) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (143.05, 21.71) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (128.52,  5.91) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (113.51, 61.21) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 99.95, 69.11) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 84.93, 29.61) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 69.92, 13.81) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 55.39, 69.11) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 40.38, 53.31) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 25.85, 37.51) circle (  1.96);
#> 
#> \path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 10.84, 21.71) circle (  1.96);
#> \end{scope}
#> \end{tikzpicture}

When rendering a minimal tex with both outputs and adding a frame, we can see the difference in borders.

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{backgrounds}

\begin{document}

% Created by tikzDevice version 0.10.1 on 2017-08-15 11:47:27
% !TEX encoding = UTF-8 Unicode
\relax
\begin{tikzpicture}[framed,x=1pt,y=1pt]
\definecolor{fillColor}{RGB}{255,255,255}
\path[use as bounding box,fill=fillColor,fill opacity=0.00] (0,0) rectangle (180.67, 72.27);
\begin{scope}
\path[clip] (  2.75,  2.75) rectangle (180.67, 72.27);
\definecolor{drawColor}{RGB}{0,0,0}

\path[draw=drawColor,line width= 3.4pt,line join=round] ( 10.84, 37.51) --
    ( 25.85,  5.91) --
    ( 40.38, 61.21) --
    ( 55.39, 29.61) --
    ( 69.92, 61.21) --
    ( 84.93, 13.81) --
    ( 99.95, 13.81) --
    (113.51, 69.11) --
    (128.52, 37.51) --
    (143.05,  5.91) --
    (158.06, 21.71) --
    (172.59, 61.21);
\definecolor{fillColor}{RGB}{0,0,0}

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (172.59, 61.21) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (158.06, 21.71) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (143.05,  5.91) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (128.52, 37.51) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (113.51, 69.11) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 99.95, 13.81) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 84.93, 13.81) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 69.92, 61.21) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 55.39, 29.61) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 40.38, 61.21) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 25.85,  5.91) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 10.84, 37.51) circle (  1.96);
\end{scope}
\end{tikzpicture}
null device
          1

\begin{tikzpicture}[framed,x=1pt,y=1pt]
\definecolor{fillColor}{RGB}{255,255,255}
\begin{scope}
\definecolor{drawColor}{RGB}{0,0,0}

\path[draw=drawColor,line width= 3.4pt,line join=round] ( 10.84, 37.51) --
    ( 25.85,  5.91) --
    ( 40.38, 61.21) --
    ( 55.39, 29.61) --
    ( 69.92, 61.21) --
    ( 84.93, 13.81) --
    ( 99.95, 13.81) --
    (113.51, 69.11) --
    (128.52, 37.51) --
    (143.05,  5.91) --
    (158.06, 21.71) --
    (172.59, 61.21);
\definecolor{fillColor}{RGB}{0,0,0}

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (172.59, 61.21) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (158.06, 21.71) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (143.05,  5.91) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (128.52, 37.51) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] (113.51, 69.11) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 99.95, 13.81) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 84.93, 13.81) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 69.92, 61.21) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 55.39, 29.61) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 40.38, 61.21) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 25.85,  5.91) circle (  1.96);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round,fill=fillColor] ( 10.84, 37.51) circle (  1.96);
\end{scope}
\end{tikzpicture}

\end{document}
krlmlr commented 6 years ago

Thanks. I was able to compile your .tex file, after adding to \section{} commands the results look like this:

screenshot from 2018-01-23 13-12-34

I don't see what's wrong with the first plot (that uses use as bounding box), how do the results look like on your system?

karseneau commented 6 years ago

Yes, I produce the same output. The added margin/white-space in the first (1) result is not desirable, the second (2) is what is required.

krlmlr commented 6 years ago

We have a test suite that contains different plots with different techniques, how would a change affect the output there?

If the plots look "much better" without the use as bounding box, we may add another argument to tikz(), maybe bbox = TRUE. I'd rather not implement this change unconditionally, just to avoid the situation where plots start to look worse. I'm happy to review a pull request, but I won't have the time to implement the change myself.

rstub commented 5 years ago

I do not think this is an issue with tikzDevize. You are creating a drawing canvas of a certain size and ask ggplot2 to draw onto this canvas. The result produced by tikzDevice has precisely the requested size. However, ggplot2 left more space to the left and right than you would like. It might work out to remove the use as bounding box option, producing a tighter cropping via Tikz. However, the resulting image would no longer have the size you originally requested. That is something I am very hesitant to implement.

Instead, I would suggest to instruct ggplot2 to use the provided space more generously:

library(ggplot2)

df <- dplyr::tibble(
  x = as.Date("2017-07-01") - lubridate::month(0:11),
  y = sample(0:9, 12, replace = TRUE)
)

ggplot(df, aes(x, y)) +
  geom_line(size = 3) +
  geom_point() +
  theme_void() +
  theme(legend.position = "none") +
  theme(axis.title = element_blank())
original

ggplot(df, aes(x, y)) +
  geom_line(size = 3) +
  geom_point() +
  theme_void() +
  theme(legend.position = "none") +
  theme(axis.title = element_blank()) + 
  scale_x_date(expand = c(0.01,0))    # <- new
new

The graph looks different since you did not set the RNG's seed.