Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Issue 2116] - New chunk option to add separators to subfloat environment #2140

Merged
merged 19 commits into from
Jun 29, 2022
Merged

[Issue 2116] - New chunk option to add separators to subfloat environment #2140

merged 19 commits into from
Jun 29, 2022

Conversation

pedropark99
Copy link
Contributor

@pedropark99 pedropark99 commented Jun 12, 2022

This PR contains a small change to the hook_plot_tex() function. This change adds a new chunk option called fig.subfigsep. This new chunk option expects to receive a Latex command as a character string (e.g. "\hfill"):

```{r}
#| echo = FALSE,
#| fig.cap='Many plots',
#| fig.subcap=c('First plot', 'Second plot', 'Third plot'),
#| out.width='.49\\linewidth',
#| fig.subfigsep = '\\hfill'
plot(1:10)
plot(rnorm(10), pch=19)
plot(1:5)
```

The separator will be added to all the plots in the chunk, except the first in the set.

@CLAassistant
Copy link

CLAassistant commented Jun 12, 2022

CLA assistant check
All committers have signed the CLA.

@cderv cderv linked an issue Jun 13, 2022 that may be closed by this pull request
3 tasks
Copy link
Owner

@yihui yihui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reverted some unnecessary changes. The main thing left for this PR is to allow fig.subsep to be a vector as requested/suggested in #2116 (comment). Are you able to do that? If not, I can ask someone else to do it. BTW, this new feature needs to be mentioned in NEWS.md, too. Thanks!

@pedropark99
Copy link
Contributor Author

Hey! Thank you for the comments and fixes! I read the issue again, and, I will think about this code again to come up with a solution. And how should I fill the NEWS.md ? I thought that "news" should come only from the "main" developers.

@pedropark99
Copy link
Contributor Author

I added new changes to the code. Now, the option fig.subsep accepts a single character value, or, a character vector of three elements. If the user supply a vector of two (or more than three) elements, hook_plot_tex() will thrown an error to the user with a stop() call.

As an example, the following chunk:

#| echo = FALSE,
#| fig.cap='Many plots',
#| fig.subcap=c('First plot', 'Second plot', 'Third plot'),
#| out.width='.49\\linewidth',
#| fig.subsep = '\\hfill'
plot(1:10)
plot(rnorm(10), pch=19)
plot(1:5)

will produce the same output as the first version of this PR. As a result, this Latex code will be produced:

\begin{knitrout}
\definecolor{shadecolor}{rgb}{0.969, 0.969, 0.969}\color{fgcolor}\begin{figure}
\subfloat[First plot\label{fig:pressure-1}]{\includegraphics[width=.49\linewidth]{figure/pressure-1} }
\hfill
\subfloat[Second plot\label{fig:pressure-2}]{\includegraphics[width=.49\linewidth]{figure/pressure-2} }
\hfill
\subfloat[Third plot\label{fig:pressure-3}]{\includegraphics[width=.49\linewidth]{figure/pressure-3} }\caption[Many plots]{Many plots}\label{fig:pressure}
\end{figure}
\end{knitrout}

Now, if fig.subsep is a three elements vector like this:

#| echo = FALSE,
#| fig.cap='Many plots',
#| fig.subcap=c('First plot', 'Second plot', 'Third plot'),
#| out.width='.49\\linewidth',
#| fig.subsep = c('\\prefix', '\\hfill', '\\postfix')
plot(1:10)
plot(rnorm(10), pch=19)
plot(1:5)

Then, the first element will be interpreted as the prefix of all subfloats, and the third element, as the postfix of all subfloats. The second element will be "filler", or, the separator between each "inner" subfloat. To be more precise, the above chunk will produce this Latex code:

\begin{knitrout}
\definecolor{shadecolor}{rgb}{0.969, 0.969, 0.969}\color{fgcolor}\begin{figure}
\prefix
\subfloat[First plot\label{fig:pressure-1}]{\includegraphics[width=.49\linewidth]{figure/pressure-1} }
\hfill
\subfloat[Second plot\label{fig:pressure-2}]{\includegraphics[width=.49\linewidth]{figure/pressure-2} }
\hfill
\subfloat[Third plot\label{fig:pressure-3}]{\includegraphics[width=.49\linewidth]{figure/pressure-3} }
\postfix
\caption[Many plots]{Many plots}\label{fig:pressure}
\end{figure}
\end{knitrout}

I think this is a useful design for this chunk option, because the user can (if he wants to) use the three element vector, and, still, supress some of the separators, by using an empty string. For example, if I want to just prefix all the subfloats, and, not include any other Latex command, I can do this:

#| echo = FALSE,
#| fig.cap='Many plots',
#| fig.subcap=c('First plot', 'Second plot', 'Third plot'),
#| out.width='.49\\linewidth',
#| fig.subsep = c('\\prefix', '', '')
plot(1:10)
plot(rnorm(10), pch=19)
plot(1:5)

This would produce this Latex code:

\begin{knitrout}
\definecolor{shadecolor}{rgb}{0.969, 0.969, 0.969}\color{fgcolor}\begin{figure}
\prefix
\subfloat[First plot\label{fig:pressure-1}]{\includegraphics[width=.49\linewidth]{figure/pressure-1} }

\subfloat[Second plot\label{fig:pressure-2}]{\includegraphics[width=.49\linewidth]{figure/pressure-2} }

\subfloat[Third plot\label{fig:pressure-3}]{\includegraphics[width=.49\linewidth]{figure/pressure-3} }

\caption[Many plots]{Many plots}\label{fig:pressure}
\end{figure}

\end{knitrout}

@pedropark99 pedropark99 requested a review from yihui June 15, 2022 18:12
Copy link
Owner

@yihui yihui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't look at the code carefully, but I thought we should recycle subsep by subsep = rep(subsep, length.out = fig.num - 1) instead of limiting its length to 1 or 3?

@pedropark99
Copy link
Contributor Author

pedropark99 commented Jun 20, 2022

I didn't look at the code carefully, but I thought we should recycle subsep by subsep = rep(subsep, length.out = fig.num - 1) instead of limiting its length to 1 or 3?

Hey @yihui ! Sorry for the delay, I was on vacation. About this question of yours, I am not sure why you would use rep() on this situation. Could you elaborate why you would use it?

I did limit subsep to a 1 or 3 elements vector, to comply with the requests of @jakubkaczor at #2116; jakubkaczor wanted a way to add separators outside the subfloat environments (in my implementation, these are the first and third element of subsep vector), and, to add separators between each subfloat, as the "inner" separators (would be the second element of subsep vector);

Thank you!

Copy link
Owner

@yihui yihui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @yihui ! Sorry for the delay, I was on vacation.

No worries! I think that was more responsive than me :)

About this question of yours, I am not sure why you would use rep() on this situation. Could you elaborate why you would use it?

Sorry I didn't read the original request carefully. Now I understand why the length can be 1 or 3.

I was thinking of making it possible to specify different separators between subfloats, e.g., \hfill between the first and second plot, and something else between the second and third plot. Perhaps we should use different chunk options to specify the separators before and after all plots (fig.subsep1 and fig.subsep2?), and use fig.subsep only in-between plots.

@pedropark99
Copy link
Contributor Author

Uhmm! I see what you meant now. I think we can add some changes for this, and, I think the code would be actually simpler than this 1 or 3 length style. I will look into it tomorrow.

Bye ✌

@jakubkaczor
Copy link

jakubkaczor commented Jun 24, 2022

From the user perspective, I would like something like the following.

  1. If fig.subsep is a string or one-element vector, then the string/element is recycled between all subfigures.
  2. If fig.subsep is a two-element vector, then the first element is added before everything, the last after.
  3. If fig.subsep's length is not less than 3, then as in the point 2, but elements between the first and the last are recycled between subfigures.

The alternative is to have fig.subsep.inner as a recycled vector of inner separators and fig.subsep.outer as a string or one or two-element vector of outer separators. If it is a string or a vector of length one, the element is added before and after all the subfigures.

The first option is more comfortable to use, the other seems to be easier to explain and to understand for the new users. I think knitr's usage and its users may be more suitable for the first option.

@pedropark99
Copy link
Contributor Author

I added a new strategy in the code for fig.subsep. If fig.subsep is a single character value, this value will still be recycled between each subfloat environment. As a result, a chunk like this:

#| echo = FALSE,
#| fig.cap='Many plots',
#| fig.subcap=c('First plot', 'Second plot', 'Third plot'),
#| out.width='.49\\linewidth',
#| fig.subsep = '\\hfill'
plot(1:10)
plot(rnorm(10), pch=19)
plot(1:5)

Would result in the following Latex code:

\begin{knitrout}
\definecolor{shadecolor}{rgb}{0.969, 0.969, 0.969}\color{fgcolor}\begin{figure}
\subfloat[First plot\label{fig:pressure-1}]{\includegraphics[width=.49\linewidth]{figure/pressure-1} }
\hfill
\subfloat[Second plot\label{fig:pressure-2}]{\includegraphics[width=.49\linewidth]{figure/pressure-2} }
\hfill
\subfloat[Third plot\label{fig:pressure-3}]{\includegraphics[width=.49\linewidth]{figure/pressure-3} }\caption[Many plots]{Many plots}\label{fig:pressure}
\end{figure}

\end{knitrout}

In the other hand, now, with the new version of the code, fig.subsep can be a vector with $n$ elements, where $n$ is between fig.num - 1 and fig.num + 1. If fig.subsep is a vector of fig.num - 1 or fig.num elements, then, hook_plot_tex() will prefix each subfloat with the current element of fig.subsep. In other words, if I had a chunk like this:

#| fig.subcap=c('First plot', 'Second plot', 'Third plot'),
#| fig.subsep = c('\\sep1', '\\sep2', '\\sep3')
plot(1:10)  #plotA
plot(1:10)  #plotB
plot(1:10)  #plotC

This would result in a Latex code similar to this:

\sep1
\subfloat{plotA}
\sep2
\subfloat{plotB}
\sep3
\subfloat{plotC}

Now, if fig.subsep is a vector with fig.num + 1 elements, then, hook_plot_tex() will follow the same strategy as above, with the difference that, it will add the last element of fig.subsep after the last subfloat envir. As an example, the following chunk:

#| echo = FALSE,
#| fig.cap='Many plots',
#| fig.subcap=c('First plot', 'Second plot', 'Third plot'),
#| out.width='.49\\linewidth',
#| fig.subsep = c('\\firstsep', '\\secondsep', '\\thirdsep', '\\fourthsep')
plot(1:10)
plot(rnorm(10), pch=19)
plot(1:5)

Would result in this Latex code:

\begin{knitrout}
\definecolor{shadecolor}{rgb}{0.969, 0.969, 0.969}\color{fgcolor}\begin{figure}
\firstsep
\subfloat[First plot\label{fig:pressure-1}]{\includegraphics[width=.49\linewidth]{figure/pressure-1} }
\secondsep
\subfloat[Second plot\label{fig:pressure-2}]{\includegraphics[width=.49\linewidth]{figure/pressure-2} }
\thirdsep
\subfloat[Third plot\label{fig:pressure-3}]{\includegraphics[width=.49\linewidth]{figure/pressure-3} }
\fourthsep\caption[Many plots]{Many plots}\label{fig:pressure}
\end{figure}

\end{knitrout}

What do you think about this solution?

@pedropark99 pedropark99 requested a review from yihui June 24, 2022 21:01
Copy link
Owner

@yihui yihui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solution sounds good to me. Thank you!

R/hooks-latex.R Outdated

# If user provides a vector with `fig.num - 1` or `fig.num` elements, use this case:
if (n_subsep %in% (fig.num + -1:0)) {
sub1 = paste(subsep[fig.cur], sub1, sep = '\n')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is buggy when n_subsep == fig.num - 1, because fig.cur ranges from 1 to fig.num (when fig.cur == fig.num, subsep[fig.cur] will be out of bound). For this case, we should conditionally add subsep to sub1 when !plot1. I've fixed the problem in my changes.

Copy link
Owner

@yihui yihui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a few tweaks to your code. I hope they make sense and actually work as expected. I'll merge this PR for now. If you have time, I'd truly appreciate it if you could help test the dev version. Thank you very much!

@yihui yihui merged commit 09ab146 into yihui:master Jun 29, 2022
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add an option for adding subfloat separators
4 participants