How to properly smuggle (with or even without TikZ)?
I am seeking to find something that allows me to "broadcast" macros outside a group. Concrete examples include paths and scopes in tizpictures
. Here is an M(N)WE.
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{calc}
makeatletter
letsmuggleoutonepgfmath@smuggleone
makeatother
begin{document}
begin{tikzpicture}[globalize/.code n args={2}{xdef#2{#1}},
localize/.code n args={2}{pgfmathsetmacro{#2}{#1}typeout{#2}
%smuggleoutone#1
}]
begin{scope}[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
pgfextra{xdefmyangle{n1}};
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (extra.north) {using verb|pgfextra|};
%
begin{scope}[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[globalize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (globalize.north) {using texttt{globalize}};
%
xdefmyangle{7}
begin{scope}[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[localize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (localize.north) {attempt to smuggle};
%
end{tikzpicture}
end{document}
The two options on the left do partly what I am seeking to do, namely broadcast the macro myangle
outside the path. However, they do it at the expense of making myangle
global. TikZ has some internal commands that may allow one to avoid this, and to just smuggle the macro outside the path. Specifically, @DavidCarlisle suggested in the chat to use pgfmath@smuggleone
. However, my above attempts failed, i.e. if I uncomment
%smuggleoutone#1
the code produces errors.
QUESTION: Can one smuggle the macro outside the group without making it global?
"BONUS": Of course it would be great if there was an explanation what all the smuggle commands do.
"BONUUUUS": Conceivably these methods may be useful independently of TikZ, so if there is a way not to make them depend on TikZ being loaded, this would be great, but is certainly not a requirement.
tikz-pgf tex-core
add a comment |
I am seeking to find something that allows me to "broadcast" macros outside a group. Concrete examples include paths and scopes in tizpictures
. Here is an M(N)WE.
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{calc}
makeatletter
letsmuggleoutonepgfmath@smuggleone
makeatother
begin{document}
begin{tikzpicture}[globalize/.code n args={2}{xdef#2{#1}},
localize/.code n args={2}{pgfmathsetmacro{#2}{#1}typeout{#2}
%smuggleoutone#1
}]
begin{scope}[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
pgfextra{xdefmyangle{n1}};
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (extra.north) {using verb|pgfextra|};
%
begin{scope}[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[globalize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (globalize.north) {using texttt{globalize}};
%
xdefmyangle{7}
begin{scope}[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[localize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (localize.north) {attempt to smuggle};
%
end{tikzpicture}
end{document}
The two options on the left do partly what I am seeking to do, namely broadcast the macro myangle
outside the path. However, they do it at the expense of making myangle
global. TikZ has some internal commands that may allow one to avoid this, and to just smuggle the macro outside the path. Specifically, @DavidCarlisle suggested in the chat to use pgfmath@smuggleone
. However, my above attempts failed, i.e. if I uncomment
%smuggleoutone#1
the code produces errors.
QUESTION: Can one smuggle the macro outside the group without making it global?
"BONUS": Of course it would be great if there was an explanation what all the smuggle commands do.
"BONUUUUS": Conceivably these methods may be useful independently of TikZ, so if there is a way not to make them depend on TikZ being loaded, this would be great, but is certainly not a requirement.
tikz-pgf tex-core
@Circumscribe Good idea, let's clean up. (I wait till I get a sign from David before I accept your nice answer.)
– marmot
1 hour ago
Of course, that's fair. He'll know a lot more about this than I do. And he'll probably have a nice solution that avoids all global assignments and instead implements a loop inserting however manyaftergroup
s are necessary :D.
– Circumscribe
1 hour ago
add a comment |
I am seeking to find something that allows me to "broadcast" macros outside a group. Concrete examples include paths and scopes in tizpictures
. Here is an M(N)WE.
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{calc}
makeatletter
letsmuggleoutonepgfmath@smuggleone
makeatother
begin{document}
begin{tikzpicture}[globalize/.code n args={2}{xdef#2{#1}},
localize/.code n args={2}{pgfmathsetmacro{#2}{#1}typeout{#2}
%smuggleoutone#1
}]
begin{scope}[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
pgfextra{xdefmyangle{n1}};
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (extra.north) {using verb|pgfextra|};
%
begin{scope}[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[globalize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (globalize.north) {using texttt{globalize}};
%
xdefmyangle{7}
begin{scope}[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[localize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (localize.north) {attempt to smuggle};
%
end{tikzpicture}
end{document}
The two options on the left do partly what I am seeking to do, namely broadcast the macro myangle
outside the path. However, they do it at the expense of making myangle
global. TikZ has some internal commands that may allow one to avoid this, and to just smuggle the macro outside the path. Specifically, @DavidCarlisle suggested in the chat to use pgfmath@smuggleone
. However, my above attempts failed, i.e. if I uncomment
%smuggleoutone#1
the code produces errors.
QUESTION: Can one smuggle the macro outside the group without making it global?
"BONUS": Of course it would be great if there was an explanation what all the smuggle commands do.
"BONUUUUS": Conceivably these methods may be useful independently of TikZ, so if there is a way not to make them depend on TikZ being loaded, this would be great, but is certainly not a requirement.
tikz-pgf tex-core
I am seeking to find something that allows me to "broadcast" macros outside a group. Concrete examples include paths and scopes in tizpictures
. Here is an M(N)WE.
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{calc}
makeatletter
letsmuggleoutonepgfmath@smuggleone
makeatother
begin{document}
begin{tikzpicture}[globalize/.code n args={2}{xdef#2{#1}},
localize/.code n args={2}{pgfmathsetmacro{#2}{#1}typeout{#2}
%smuggleoutone#1
}]
begin{scope}[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
pgfextra{xdefmyangle{n1}};
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (extra.north) {using verb|pgfextra|};
%
begin{scope}[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[globalize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (globalize.north) {using texttt{globalize}};
%
xdefmyangle{7}
begin{scope}[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[localize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (localize.north) {attempt to smuggle};
%
end{tikzpicture}
end{document}
The two options on the left do partly what I am seeking to do, namely broadcast the macro myangle
outside the path. However, they do it at the expense of making myangle
global. TikZ has some internal commands that may allow one to avoid this, and to just smuggle the macro outside the path. Specifically, @DavidCarlisle suggested in the chat to use pgfmath@smuggleone
. However, my above attempts failed, i.e. if I uncomment
%smuggleoutone#1
the code produces errors.
QUESTION: Can one smuggle the macro outside the group without making it global?
"BONUS": Of course it would be great if there was an explanation what all the smuggle commands do.
"BONUUUUS": Conceivably these methods may be useful independently of TikZ, so if there is a way not to make them depend on TikZ being loaded, this would be great, but is certainly not a requirement.
tikz-pgf tex-core
tikz-pgf tex-core
edited 4 hours ago
marmot
asked 5 hours ago
marmotmarmot
92.8k4109203
92.8k4109203
@Circumscribe Good idea, let's clean up. (I wait till I get a sign from David before I accept your nice answer.)
– marmot
1 hour ago
Of course, that's fair. He'll know a lot more about this than I do. And he'll probably have a nice solution that avoids all global assignments and instead implements a loop inserting however manyaftergroup
s are necessary :D.
– Circumscribe
1 hour ago
add a comment |
@Circumscribe Good idea, let's clean up. (I wait till I get a sign from David before I accept your nice answer.)
– marmot
1 hour ago
Of course, that's fair. He'll know a lot more about this than I do. And he'll probably have a nice solution that avoids all global assignments and instead implements a loop inserting however manyaftergroup
s are necessary :D.
– Circumscribe
1 hour ago
@Circumscribe Good idea, let's clean up. (I wait till I get a sign from David before I accept your nice answer.)
– marmot
1 hour ago
@Circumscribe Good idea, let's clean up. (I wait till I get a sign from David before I accept your nice answer.)
– marmot
1 hour ago
Of course, that's fair. He'll know a lot more about this than I do. And he'll probably have a nice solution that avoids all global assignments and instead implements a loop inserting however many
aftergroup
s are necessary :D.– Circumscribe
1 hour ago
Of course, that's fair. He'll know a lot more about this than I do. And he'll probably have a nice solution that avoids all global assignments and instead implements a loop inserting however many
aftergroup
s are necessary :D.– Circumscribe
1 hour ago
add a comment |
1 Answer
1
active
oldest
votes
You can "smuggle" definitions out of their group with the TeX primitive aftergroup
. I'll first explain what aftergroup
does, then give one possible definition of smuggleoutone
and finally mention why you might want to use AfterGroup
(from etextools
) instead.
About aftergroup
:
It is possible to insert a single token right after the end of the current group using aftergroup<token>
.
You can only smuggle out one token at a time, so if you want to move out something consisting of multiple tokens (like a definition) you'll need to aftergroup
each of these tokens separately. This includes things like braces ({}
), so for instance
{aftergroupdefaftergroupabcaftergroup{aftergroup Aaftergroup Baftergroup Caftergroup}}
is equivalent to {}defabc{ABC}
.
This is quite a hassle, so the following may be more practical:
{gdefsomethingunique{defabc{ABC}}aftergroupsomethingunique}
This works by globally assigning defabc{ABC}
to somethingunique
and inserting that after the end of the group.
If ABC is replaced by some macro, say ABC
, that is only defined within the current group and that you want to be fully expanded then you'll want to use xdef
instead:
{%
newcommand*ABC{ABC}%
xdefsomethingunique{defnoexpandabc{ABC}}%
aftergroupsomethingunique
}
I've inserted noexpand
in front of abc
because we don't want abc
to be expanded.
If you only want ABC
to be expanded once you can instead use the slightly more complicated
{
newcommand*ABC{somethingthatshouldntbeexpanded}%
xdefsomethingunique{defnoexpandabc{unexpandedexpandafter{ABC}}}%
aftergroupsomethingunique
}
The primitives noexpand
, unexpanded
and expandafter
are all explained in this this answer.
Defining smuggleoutone
:
To smuggle the definition of abc
out of a group you can do what I did above with ABC
replaced by abc
itself.
This way abc
is defined as itself (expanded once) immediately after the end of the group.
You could thus define your smuggleoutone
command as follows:
newcommand*smuggleoutone[1]{%
xdefsomethingunique{defnoexpand#1{unexpandedexpandafter{#1}}}%
aftergroupsomethingunique
}
(You meant to use smuggleoutone#2
instead of smuggleoutone#1
by the way.)
There is one tiny problem with this definition though, and that is the fact that somethingunique
is no longer unique if you use the same name every time.
It would thus be better to increment a counter every time this command is used and to store the thing you want to smuggle out in a macro whose names contains the value of this counter.
This is how the AfterGroup
command, which I describe below, works (except that it also cleans up after itself).
Using AfterGroup
instead:
The command AfterGroup
is defined in the etextools
package.
It acts like aftergroup
, but it takes an argument that can consist of any number of tokens.
So, for instance, Aftergroup{defabc{ABC}}
inserts defabc{ABC}
after the current group without all of the aforementioned hassle.
There's also a starred version, Aftergroup*
, that does the same thing but first expands its arguments fully.
An alternative definition would thus be:
usepackage{etextools}
newcommand*smuggleoutone[1]{%
AfterGroup*{defnoexpand#1{unexpandedexpandafter{#1}}}%
}
This definition shouldn't suffer from the aforementioned problem.
All of this applied to your MWE:
Here is your MWE with two changes: (1) I've redefined smuggleoutone
and (2) I've uncommented smuggleoutone#1
and replaced #1
by #2
(I expect this was likely a typo).
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{calc}
usepackage{etextools}
newcommand*smuggleoutone[1]{%
AfterGroup*{defnoexpand#1{unexpandedexpandafter{#1}}}%
}
%% Alternative:
% newcommand*smuggleoutone[1]{%
% xdefsomethingunique{defnoexpand#1{unexpandedexpandafter{#1}}}%
% aftergroupsomethingunique
% }
begin{document}
begin{tikzpicture}[globalize/.code n args={2}{xdef#2{#1}},
localize/.code n args={2}{pgfmathsetmacro{#2}{#1}typeout{#2}
smuggleoutone#2
}]
begin{scope}[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
pgfextra{xdefmyangle{n1}};
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (extra.north) {using verb|pgfextra|};
%
begin{scope}[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[globalize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (globalize.north) {using texttt{globalize}};
%
xdefmyangle{7}
begin{scope}[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[localize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (localize.north) {attempt to smuggle};
%
end{tikzpicture}
end{document}
Thanks for the beautiful answer and the useful explanations!!!
– marmot
1 hour ago
You're welcome.
– Circumscribe
1 hour ago
I guess the only additional thing that might be interesting is whether you can jump also two or more levels that way. (But it is perfectly fine if you do not answer that.)
– marmot
23 mins ago
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "85"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f470961%2fhow-to-properly-smuggle-with-or-even-without-tikz%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
You can "smuggle" definitions out of their group with the TeX primitive aftergroup
. I'll first explain what aftergroup
does, then give one possible definition of smuggleoutone
and finally mention why you might want to use AfterGroup
(from etextools
) instead.
About aftergroup
:
It is possible to insert a single token right after the end of the current group using aftergroup<token>
.
You can only smuggle out one token at a time, so if you want to move out something consisting of multiple tokens (like a definition) you'll need to aftergroup
each of these tokens separately. This includes things like braces ({}
), so for instance
{aftergroupdefaftergroupabcaftergroup{aftergroup Aaftergroup Baftergroup Caftergroup}}
is equivalent to {}defabc{ABC}
.
This is quite a hassle, so the following may be more practical:
{gdefsomethingunique{defabc{ABC}}aftergroupsomethingunique}
This works by globally assigning defabc{ABC}
to somethingunique
and inserting that after the end of the group.
If ABC is replaced by some macro, say ABC
, that is only defined within the current group and that you want to be fully expanded then you'll want to use xdef
instead:
{%
newcommand*ABC{ABC}%
xdefsomethingunique{defnoexpandabc{ABC}}%
aftergroupsomethingunique
}
I've inserted noexpand
in front of abc
because we don't want abc
to be expanded.
If you only want ABC
to be expanded once you can instead use the slightly more complicated
{
newcommand*ABC{somethingthatshouldntbeexpanded}%
xdefsomethingunique{defnoexpandabc{unexpandedexpandafter{ABC}}}%
aftergroupsomethingunique
}
The primitives noexpand
, unexpanded
and expandafter
are all explained in this this answer.
Defining smuggleoutone
:
To smuggle the definition of abc
out of a group you can do what I did above with ABC
replaced by abc
itself.
This way abc
is defined as itself (expanded once) immediately after the end of the group.
You could thus define your smuggleoutone
command as follows:
newcommand*smuggleoutone[1]{%
xdefsomethingunique{defnoexpand#1{unexpandedexpandafter{#1}}}%
aftergroupsomethingunique
}
(You meant to use smuggleoutone#2
instead of smuggleoutone#1
by the way.)
There is one tiny problem with this definition though, and that is the fact that somethingunique
is no longer unique if you use the same name every time.
It would thus be better to increment a counter every time this command is used and to store the thing you want to smuggle out in a macro whose names contains the value of this counter.
This is how the AfterGroup
command, which I describe below, works (except that it also cleans up after itself).
Using AfterGroup
instead:
The command AfterGroup
is defined in the etextools
package.
It acts like aftergroup
, but it takes an argument that can consist of any number of tokens.
So, for instance, Aftergroup{defabc{ABC}}
inserts defabc{ABC}
after the current group without all of the aforementioned hassle.
There's also a starred version, Aftergroup*
, that does the same thing but first expands its arguments fully.
An alternative definition would thus be:
usepackage{etextools}
newcommand*smuggleoutone[1]{%
AfterGroup*{defnoexpand#1{unexpandedexpandafter{#1}}}%
}
This definition shouldn't suffer from the aforementioned problem.
All of this applied to your MWE:
Here is your MWE with two changes: (1) I've redefined smuggleoutone
and (2) I've uncommented smuggleoutone#1
and replaced #1
by #2
(I expect this was likely a typo).
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{calc}
usepackage{etextools}
newcommand*smuggleoutone[1]{%
AfterGroup*{defnoexpand#1{unexpandedexpandafter{#1}}}%
}
%% Alternative:
% newcommand*smuggleoutone[1]{%
% xdefsomethingunique{defnoexpand#1{unexpandedexpandafter{#1}}}%
% aftergroupsomethingunique
% }
begin{document}
begin{tikzpicture}[globalize/.code n args={2}{xdef#2{#1}},
localize/.code n args={2}{pgfmathsetmacro{#2}{#1}typeout{#2}
smuggleoutone#2
}]
begin{scope}[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
pgfextra{xdefmyangle{n1}};
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (extra.north) {using verb|pgfextra|};
%
begin{scope}[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[globalize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (globalize.north) {using texttt{globalize}};
%
xdefmyangle{7}
begin{scope}[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[localize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (localize.north) {attempt to smuggle};
%
end{tikzpicture}
end{document}
Thanks for the beautiful answer and the useful explanations!!!
– marmot
1 hour ago
You're welcome.
– Circumscribe
1 hour ago
I guess the only additional thing that might be interesting is whether you can jump also two or more levels that way. (But it is perfectly fine if you do not answer that.)
– marmot
23 mins ago
add a comment |
You can "smuggle" definitions out of their group with the TeX primitive aftergroup
. I'll first explain what aftergroup
does, then give one possible definition of smuggleoutone
and finally mention why you might want to use AfterGroup
(from etextools
) instead.
About aftergroup
:
It is possible to insert a single token right after the end of the current group using aftergroup<token>
.
You can only smuggle out one token at a time, so if you want to move out something consisting of multiple tokens (like a definition) you'll need to aftergroup
each of these tokens separately. This includes things like braces ({}
), so for instance
{aftergroupdefaftergroupabcaftergroup{aftergroup Aaftergroup Baftergroup Caftergroup}}
is equivalent to {}defabc{ABC}
.
This is quite a hassle, so the following may be more practical:
{gdefsomethingunique{defabc{ABC}}aftergroupsomethingunique}
This works by globally assigning defabc{ABC}
to somethingunique
and inserting that after the end of the group.
If ABC is replaced by some macro, say ABC
, that is only defined within the current group and that you want to be fully expanded then you'll want to use xdef
instead:
{%
newcommand*ABC{ABC}%
xdefsomethingunique{defnoexpandabc{ABC}}%
aftergroupsomethingunique
}
I've inserted noexpand
in front of abc
because we don't want abc
to be expanded.
If you only want ABC
to be expanded once you can instead use the slightly more complicated
{
newcommand*ABC{somethingthatshouldntbeexpanded}%
xdefsomethingunique{defnoexpandabc{unexpandedexpandafter{ABC}}}%
aftergroupsomethingunique
}
The primitives noexpand
, unexpanded
and expandafter
are all explained in this this answer.
Defining smuggleoutone
:
To smuggle the definition of abc
out of a group you can do what I did above with ABC
replaced by abc
itself.
This way abc
is defined as itself (expanded once) immediately after the end of the group.
You could thus define your smuggleoutone
command as follows:
newcommand*smuggleoutone[1]{%
xdefsomethingunique{defnoexpand#1{unexpandedexpandafter{#1}}}%
aftergroupsomethingunique
}
(You meant to use smuggleoutone#2
instead of smuggleoutone#1
by the way.)
There is one tiny problem with this definition though, and that is the fact that somethingunique
is no longer unique if you use the same name every time.
It would thus be better to increment a counter every time this command is used and to store the thing you want to smuggle out in a macro whose names contains the value of this counter.
This is how the AfterGroup
command, which I describe below, works (except that it also cleans up after itself).
Using AfterGroup
instead:
The command AfterGroup
is defined in the etextools
package.
It acts like aftergroup
, but it takes an argument that can consist of any number of tokens.
So, for instance, Aftergroup{defabc{ABC}}
inserts defabc{ABC}
after the current group without all of the aforementioned hassle.
There's also a starred version, Aftergroup*
, that does the same thing but first expands its arguments fully.
An alternative definition would thus be:
usepackage{etextools}
newcommand*smuggleoutone[1]{%
AfterGroup*{defnoexpand#1{unexpandedexpandafter{#1}}}%
}
This definition shouldn't suffer from the aforementioned problem.
All of this applied to your MWE:
Here is your MWE with two changes: (1) I've redefined smuggleoutone
and (2) I've uncommented smuggleoutone#1
and replaced #1
by #2
(I expect this was likely a typo).
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{calc}
usepackage{etextools}
newcommand*smuggleoutone[1]{%
AfterGroup*{defnoexpand#1{unexpandedexpandafter{#1}}}%
}
%% Alternative:
% newcommand*smuggleoutone[1]{%
% xdefsomethingunique{defnoexpand#1{unexpandedexpandafter{#1}}}%
% aftergroupsomethingunique
% }
begin{document}
begin{tikzpicture}[globalize/.code n args={2}{xdef#2{#1}},
localize/.code n args={2}{pgfmathsetmacro{#2}{#1}typeout{#2}
smuggleoutone#2
}]
begin{scope}[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
pgfextra{xdefmyangle{n1}};
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (extra.north) {using verb|pgfextra|};
%
begin{scope}[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[globalize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (globalize.north) {using texttt{globalize}};
%
xdefmyangle{7}
begin{scope}[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[localize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (localize.north) {attempt to smuggle};
%
end{tikzpicture}
end{document}
Thanks for the beautiful answer and the useful explanations!!!
– marmot
1 hour ago
You're welcome.
– Circumscribe
1 hour ago
I guess the only additional thing that might be interesting is whether you can jump also two or more levels that way. (But it is perfectly fine if you do not answer that.)
– marmot
23 mins ago
add a comment |
You can "smuggle" definitions out of their group with the TeX primitive aftergroup
. I'll first explain what aftergroup
does, then give one possible definition of smuggleoutone
and finally mention why you might want to use AfterGroup
(from etextools
) instead.
About aftergroup
:
It is possible to insert a single token right after the end of the current group using aftergroup<token>
.
You can only smuggle out one token at a time, so if you want to move out something consisting of multiple tokens (like a definition) you'll need to aftergroup
each of these tokens separately. This includes things like braces ({}
), so for instance
{aftergroupdefaftergroupabcaftergroup{aftergroup Aaftergroup Baftergroup Caftergroup}}
is equivalent to {}defabc{ABC}
.
This is quite a hassle, so the following may be more practical:
{gdefsomethingunique{defabc{ABC}}aftergroupsomethingunique}
This works by globally assigning defabc{ABC}
to somethingunique
and inserting that after the end of the group.
If ABC is replaced by some macro, say ABC
, that is only defined within the current group and that you want to be fully expanded then you'll want to use xdef
instead:
{%
newcommand*ABC{ABC}%
xdefsomethingunique{defnoexpandabc{ABC}}%
aftergroupsomethingunique
}
I've inserted noexpand
in front of abc
because we don't want abc
to be expanded.
If you only want ABC
to be expanded once you can instead use the slightly more complicated
{
newcommand*ABC{somethingthatshouldntbeexpanded}%
xdefsomethingunique{defnoexpandabc{unexpandedexpandafter{ABC}}}%
aftergroupsomethingunique
}
The primitives noexpand
, unexpanded
and expandafter
are all explained in this this answer.
Defining smuggleoutone
:
To smuggle the definition of abc
out of a group you can do what I did above with ABC
replaced by abc
itself.
This way abc
is defined as itself (expanded once) immediately after the end of the group.
You could thus define your smuggleoutone
command as follows:
newcommand*smuggleoutone[1]{%
xdefsomethingunique{defnoexpand#1{unexpandedexpandafter{#1}}}%
aftergroupsomethingunique
}
(You meant to use smuggleoutone#2
instead of smuggleoutone#1
by the way.)
There is one tiny problem with this definition though, and that is the fact that somethingunique
is no longer unique if you use the same name every time.
It would thus be better to increment a counter every time this command is used and to store the thing you want to smuggle out in a macro whose names contains the value of this counter.
This is how the AfterGroup
command, which I describe below, works (except that it also cleans up after itself).
Using AfterGroup
instead:
The command AfterGroup
is defined in the etextools
package.
It acts like aftergroup
, but it takes an argument that can consist of any number of tokens.
So, for instance, Aftergroup{defabc{ABC}}
inserts defabc{ABC}
after the current group without all of the aforementioned hassle.
There's also a starred version, Aftergroup*
, that does the same thing but first expands its arguments fully.
An alternative definition would thus be:
usepackage{etextools}
newcommand*smuggleoutone[1]{%
AfterGroup*{defnoexpand#1{unexpandedexpandafter{#1}}}%
}
This definition shouldn't suffer from the aforementioned problem.
All of this applied to your MWE:
Here is your MWE with two changes: (1) I've redefined smuggleoutone
and (2) I've uncommented smuggleoutone#1
and replaced #1
by #2
(I expect this was likely a typo).
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{calc}
usepackage{etextools}
newcommand*smuggleoutone[1]{%
AfterGroup*{defnoexpand#1{unexpandedexpandafter{#1}}}%
}
%% Alternative:
% newcommand*smuggleoutone[1]{%
% xdefsomethingunique{defnoexpand#1{unexpandedexpandafter{#1}}}%
% aftergroupsomethingunique
% }
begin{document}
begin{tikzpicture}[globalize/.code n args={2}{xdef#2{#1}},
localize/.code n args={2}{pgfmathsetmacro{#2}{#1}typeout{#2}
smuggleoutone#2
}]
begin{scope}[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
pgfextra{xdefmyangle{n1}};
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (extra.north) {using verb|pgfextra|};
%
begin{scope}[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[globalize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (globalize.north) {using texttt{globalize}};
%
xdefmyangle{7}
begin{scope}[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[localize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (localize.north) {attempt to smuggle};
%
end{tikzpicture}
end{document}
You can "smuggle" definitions out of their group with the TeX primitive aftergroup
. I'll first explain what aftergroup
does, then give one possible definition of smuggleoutone
and finally mention why you might want to use AfterGroup
(from etextools
) instead.
About aftergroup
:
It is possible to insert a single token right after the end of the current group using aftergroup<token>
.
You can only smuggle out one token at a time, so if you want to move out something consisting of multiple tokens (like a definition) you'll need to aftergroup
each of these tokens separately. This includes things like braces ({}
), so for instance
{aftergroupdefaftergroupabcaftergroup{aftergroup Aaftergroup Baftergroup Caftergroup}}
is equivalent to {}defabc{ABC}
.
This is quite a hassle, so the following may be more practical:
{gdefsomethingunique{defabc{ABC}}aftergroupsomethingunique}
This works by globally assigning defabc{ABC}
to somethingunique
and inserting that after the end of the group.
If ABC is replaced by some macro, say ABC
, that is only defined within the current group and that you want to be fully expanded then you'll want to use xdef
instead:
{%
newcommand*ABC{ABC}%
xdefsomethingunique{defnoexpandabc{ABC}}%
aftergroupsomethingunique
}
I've inserted noexpand
in front of abc
because we don't want abc
to be expanded.
If you only want ABC
to be expanded once you can instead use the slightly more complicated
{
newcommand*ABC{somethingthatshouldntbeexpanded}%
xdefsomethingunique{defnoexpandabc{unexpandedexpandafter{ABC}}}%
aftergroupsomethingunique
}
The primitives noexpand
, unexpanded
and expandafter
are all explained in this this answer.
Defining smuggleoutone
:
To smuggle the definition of abc
out of a group you can do what I did above with ABC
replaced by abc
itself.
This way abc
is defined as itself (expanded once) immediately after the end of the group.
You could thus define your smuggleoutone
command as follows:
newcommand*smuggleoutone[1]{%
xdefsomethingunique{defnoexpand#1{unexpandedexpandafter{#1}}}%
aftergroupsomethingunique
}
(You meant to use smuggleoutone#2
instead of smuggleoutone#1
by the way.)
There is one tiny problem with this definition though, and that is the fact that somethingunique
is no longer unique if you use the same name every time.
It would thus be better to increment a counter every time this command is used and to store the thing you want to smuggle out in a macro whose names contains the value of this counter.
This is how the AfterGroup
command, which I describe below, works (except that it also cleans up after itself).
Using AfterGroup
instead:
The command AfterGroup
is defined in the etextools
package.
It acts like aftergroup
, but it takes an argument that can consist of any number of tokens.
So, for instance, Aftergroup{defabc{ABC}}
inserts defabc{ABC}
after the current group without all of the aforementioned hassle.
There's also a starred version, Aftergroup*
, that does the same thing but first expands its arguments fully.
An alternative definition would thus be:
usepackage{etextools}
newcommand*smuggleoutone[1]{%
AfterGroup*{defnoexpand#1{unexpandedexpandafter{#1}}}%
}
This definition shouldn't suffer from the aforementioned problem.
All of this applied to your MWE:
Here is your MWE with two changes: (1) I've redefined smuggleoutone
and (2) I've uncommented smuggleoutone#1
and replaced #1
by #2
(I expect this was likely a typo).
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{calc}
usepackage{etextools}
newcommand*smuggleoutone[1]{%
AfterGroup*{defnoexpand#1{unexpandedexpandafter{#1}}}%
}
%% Alternative:
% newcommand*smuggleoutone[1]{%
% xdefsomethingunique{defnoexpand#1{unexpandedexpandafter{#1}}}%
% aftergroupsomethingunique
% }
begin{document}
begin{tikzpicture}[globalize/.code n args={2}{xdef#2{#1}},
localize/.code n args={2}{pgfmathsetmacro{#2}{#1}typeout{#2}
smuggleoutone#2
}]
begin{scope}[local bounding box=extra]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
pgfextra{xdefmyangle{n1}};
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (extra.north) {using verb|pgfextra|};
%
begin{scope}[local bounding box=globalize,xshift=3cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[globalize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (globalize.north) {using texttt{globalize}};
%
xdefmyangle{7}
begin{scope}[local bounding box=localize,xshift=6cm]
path let p1=($(2,1)-(0,0)$),n1={atan2(y1,x1)} in
[localize={n1}{myangle}];
node at (1,0) {myangle};
end{scope}
node[anchor=south] at (localize.north) {attempt to smuggle};
%
end{tikzpicture}
end{document}
edited 46 mins ago
answered 1 hour ago
CircumscribeCircumscribe
5,4611634
5,4611634
Thanks for the beautiful answer and the useful explanations!!!
– marmot
1 hour ago
You're welcome.
– Circumscribe
1 hour ago
I guess the only additional thing that might be interesting is whether you can jump also two or more levels that way. (But it is perfectly fine if you do not answer that.)
– marmot
23 mins ago
add a comment |
Thanks for the beautiful answer and the useful explanations!!!
– marmot
1 hour ago
You're welcome.
– Circumscribe
1 hour ago
I guess the only additional thing that might be interesting is whether you can jump also two or more levels that way. (But it is perfectly fine if you do not answer that.)
– marmot
23 mins ago
Thanks for the beautiful answer and the useful explanations!!!
– marmot
1 hour ago
Thanks for the beautiful answer and the useful explanations!!!
– marmot
1 hour ago
You're welcome.
– Circumscribe
1 hour ago
You're welcome.
– Circumscribe
1 hour ago
I guess the only additional thing that might be interesting is whether you can jump also two or more levels that way. (But it is perfectly fine if you do not answer that.)
– marmot
23 mins ago
I guess the only additional thing that might be interesting is whether you can jump also two or more levels that way. (But it is perfectly fine if you do not answer that.)
– marmot
23 mins ago
add a comment |
Thanks for contributing an answer to TeX - LaTeX Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f470961%2fhow-to-properly-smuggle-with-or-even-without-tikz%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
@Circumscribe Good idea, let's clean up. (I wait till I get a sign from David before I accept your nice answer.)
– marmot
1 hour ago
Of course, that's fair. He'll know a lot more about this than I do. And he'll probably have a nice solution that avoids all global assignments and instead implements a loop inserting however many
aftergroup
s are necessary :D.– Circumscribe
1 hour ago