How can I give this perspective with Tikz?
I am trying to complete the image of the figure: I know how to perform the dashed circle and the legs of the table. The problem is to draw the upper part of the table. Can you give me a hint of how to do it?
Thank you!
tikz-pgf
add a comment |
I am trying to complete the image of the figure: I know how to perform the dashed circle and the legs of the table. The problem is to draw the upper part of the table. Can you give me a hint of how to do it?
Thank you!
tikz-pgf
Can you show us the code you already have?
– Sigur
13 hours ago
As far as I know, the most straightforward way will be to employ this great answer.
– marmot
13 hours ago
I am just starting... marmot, that seems really difficult!!
– Eduardo
13 hours ago
1
Yes, unfortunately these cool macros are not yet part of a package or library. So for the time being you would still copy the preamble. Notice that once you copied it, the rest will not be difficult.
– marmot
13 hours ago
add a comment |
I am trying to complete the image of the figure: I know how to perform the dashed circle and the legs of the table. The problem is to draw the upper part of the table. Can you give me a hint of how to do it?
Thank you!
tikz-pgf
I am trying to complete the image of the figure: I know how to perform the dashed circle and the legs of the table. The problem is to draw the upper part of the table. Can you give me a hint of how to do it?
Thank you!
tikz-pgf
tikz-pgf
edited 13 hours ago
Paul Stanley
14.4k42848
14.4k42848
asked 13 hours ago
EduardoEduardo
825
825
Can you show us the code you already have?
– Sigur
13 hours ago
As far as I know, the most straightforward way will be to employ this great answer.
– marmot
13 hours ago
I am just starting... marmot, that seems really difficult!!
– Eduardo
13 hours ago
1
Yes, unfortunately these cool macros are not yet part of a package or library. So for the time being you would still copy the preamble. Notice that once you copied it, the rest will not be difficult.
– marmot
13 hours ago
add a comment |
Can you show us the code you already have?
– Sigur
13 hours ago
As far as I know, the most straightforward way will be to employ this great answer.
– marmot
13 hours ago
I am just starting... marmot, that seems really difficult!!
– Eduardo
13 hours ago
1
Yes, unfortunately these cool macros are not yet part of a package or library. So for the time being you would still copy the preamble. Notice that once you copied it, the rest will not be difficult.
– marmot
13 hours ago
Can you show us the code you already have?
– Sigur
13 hours ago
Can you show us the code you already have?
– Sigur
13 hours ago
As far as I know, the most straightforward way will be to employ this great answer.
– marmot
13 hours ago
As far as I know, the most straightforward way will be to employ this great answer.
– marmot
13 hours ago
I am just starting... marmot, that seems really difficult!!
– Eduardo
13 hours ago
I am just starting... marmot, that seems really difficult!!
– Eduardo
13 hours ago
1
1
Yes, unfortunately these cool macros are not yet part of a package or library. So for the time being you would still copy the preamble. Notice that once you copied it, the rest will not be difficult.
– marmot
13 hours ago
Yes, unfortunately these cool macros are not yet part of a package or library. So for the time being you would still copy the preamble. Notice that once you copied it, the rest will not be difficult.
– marmot
13 hours ago
add a comment |
1 Answer
1
active
oldest
votes
All credits go to Max' answer. All I do is to truncate his general projection to a simpler case, which may help to understand better what's going on here. Max' picture shows very nicely what his code does: it transforms the objects in such a way that the edges that are parallel to the x axis meet in p
, the ones parallel to the y axis in q
and the ones parallel to the z axis in r
. (Yes, that's just a sloppy definition of "vanishing points".) However, in order to reproduce something like your screenshot, we only need to play with q
, which is what the following animation does.
documentclass[tikz,border=3.14mm]{standalone}
usepackage{tikz-3dplot}
usetikzlibrary{shapes.geometric,intersections}
usepgfmodule{nonlineartransformations}
% Max magic
makeatletter
% the first part is not in use here
deftikz@scan@transform@one@point#1{%
tikz@scan@one@pointpgf@process#1%
pgf@pos@transform{pgf@x}{pgf@y}}
tikzset{%
grid source opposite corners/.code args={#1and#2}{%
pgfextract@processtikz@transform@source@southwest{%
tikz@scan@transform@one@point{#1}}%
pgfextract@processtikz@transform@source@northeast{%
tikz@scan@transform@one@point{#2}}%
},
grid target corners/.code args={#1--#2--#3--#4}{%
pgfextract@processtikz@transform@target@southwest{%
tikz@scan@transform@one@point{#1}}%
pgfextract@processtikz@transform@target@southeast{%
tikz@scan@transform@one@point{#2}}%
pgfextract@processtikz@transform@target@northeast{%
tikz@scan@transform@one@point{#3}}%
pgfextract@processtikz@transform@target@northwest{%
tikz@scan@transform@one@point{#4}}%
}
}
deftikzgridtransform{%
pgfextract@processtikz@current@point{}%
pgf@process{%
pgfpointdiff{tikz@transform@source@southwest}%
{tikz@transform@source@northeast}%
}%
pgf@xc=pgf@xpgf@yc=pgf@y%
pgf@process{%
pgfpointdiff{tikz@transform@source@southwest}{tikz@current@point}%
}%
pgfmathparse{pgf@x/pgf@xc}lettikz@tx=pgfmathresult%
pgfmathparse{pgf@y/pgf@yc}lettikz@ty=pgfmathresult%
%
pgfpointlineattime{tikz@ty}{%
pgfpointlineattime{tikz@tx}{tikz@transform@target@southwest}%
{tikz@transform@target@southeast}}{%
pgfpointlineattime{tikz@tx}{tikz@transform@target@northwest}%
{tikz@transform@target@northeast}}%
}
% Initialize H matrix for perspective view
pgfmathsetmacroH@tpp@aa{1}pgfmathsetmacroH@tpp@ab{0}pgfmathsetmacroH@tpp@ac{0}%pgfmathsetmacroH@tpp@ad{0}
pgfmathsetmacroH@tpp@ba{0}pgfmathsetmacroH@tpp@bb{1}pgfmathsetmacroH@tpp@bc{0}%pgfmathsetmacroH@tpp@bd{0}
pgfmathsetmacroH@tpp@ca{0}pgfmathsetmacroH@tpp@cb{0}pgfmathsetmacroH@tpp@cc{1}%pgfmathsetmacroH@tpp@cd{0}
pgfmathsetmacroH@tpp@da{0}pgfmathsetmacroH@tpp@db{0}pgfmathsetmacroH@tpp@dc{0}%pgfmathsetmacroH@tpp@dd{1}
%Initialize H matrix for main rotation
pgfmathsetmacroH@rot@aa{1}pgfmathsetmacroH@rot@ab{0}pgfmathsetmacroH@rot@ac{0}%pgfmathsetmacroH@rot@ad{0}
pgfmathsetmacroH@rot@ba{0}pgfmathsetmacroH@rot@bb{1}pgfmathsetmacroH@rot@bc{0}%pgfmathsetmacroH@rot@bd{0}
pgfmathsetmacroH@rot@ca{0}pgfmathsetmacroH@rot@cb{0}pgfmathsetmacroH@rot@cc{1}%pgfmathsetmacroH@rot@cd{0}
%pgfmathsetmacroH@rot@da{0}pgfmathsetmacroH@rot@db{0}pgfmathsetmacroH@rot@dc{0}pgfmathsetmacroH@rot@dd{1}
pgfkeys{
/three point perspective/.cd,
p/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#1))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ba{#2/#1}
pgfmathsetmacroH@tpp@ca{#3/#1}
pgfmathsetmacroH@tpp@da{ 1/#1}
coordinate (vp-p) at (#1,#2,#3);
fi
},
q/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#2))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ab{#1/#2}
pgfmathsetmacroH@tpp@cb{#3/#2}
pgfmathsetmacroH@tpp@db{ 1/#2}
coordinate (vp-q) at (#1,#2,#3);
fi
},
r/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#3))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ac{#1/#3}
pgfmathsetmacroH@tpp@bc{#2/#3}
pgfmathsetmacroH@tpp@dc{ 1/#3}
coordinate (vp-r) at (#1,#2,#3);
fi
},
coordinate/.code args={#1,#2,#3}{
pgfmathsetmacrotpp@x{#1} %<- Max' fix
pgfmathsetmacrotpp@y{#2}
pgfmathsetmacrotpp@z{#3}
},
}
tikzset{
view/.code 2 args={
pgfmathsetmacrorot@main@theta{#1}
pgfmathsetmacrorot@main@phi{#2}
% Row 1
pgfmathsetmacroH@rot@aa{cos(rot@main@phi)}
pgfmathsetmacroH@rot@ab{sin(rot@main@phi)}
pgfmathsetmacroH@rot@ac{0}
% Row 2
pgfmathsetmacroH@rot@ba{-cos(rot@main@theta)*sin(rot@main@phi)}
pgfmathsetmacroH@rot@bb{cos(rot@main@phi)*cos(rot@main@theta)}
pgfmathsetmacroH@rot@bc{sin(rot@main@theta)}
% Row 3
pgfmathsetmacroH@m@ca{sin(rot@main@phi)*sin(rot@main@theta)}
pgfmathsetmacroH@m@cb{-cos(rot@main@phi)*sin(rot@main@theta)}
pgfmathsetmacroH@m@cc{cos(rot@main@theta)}
% Set vector values
pgfmathsetmacrovec@x@x{H@rot@aa}
pgfmathsetmacrovec@y@x{H@rot@ab}
pgfmathsetmacrovec@z@x{H@rot@ac}
pgfmathsetmacrovec@x@y{H@rot@ba}
pgfmathsetmacrovec@y@y{H@rot@bb}
pgfmathsetmacrovec@z@y{H@rot@bc}
% Set pgf vectors
pgfsetxvec{pgfpoint{vec@x@x cm}{vec@x@y cm}}
pgfsetyvec{pgfpoint{vec@y@x cm}{vec@y@y cm}}
pgfsetzvec{pgfpoint{vec@z@x cm}{vec@z@y cm}}
},
}
tikzset{
perspective/.code={pgfkeys{/three point perspective/.cd,#1}},
perspective/.default={p={(15,0,0)},q={(0,15,0)},r={(0,0,50)}},
}
tikzdeclarecoordinatesystem{three point perspective}{
pgfkeys{/three point perspective/.cd,coordinate={#1}}
pgfmathsetmacrotemp@p@w{H@tpp@da*tpp@x + H@tpp@db*tpp@y + H@tpp@dc*tpp@z + 1}
pgfmathsetmacrotemp@p@x{(H@tpp@aa*tpp@x + H@tpp@ab*tpp@y + H@tpp@ac*tpp@z)/temp@p@w}
pgfmathsetmacrotemp@p@y{(H@tpp@ba*tpp@x + H@tpp@bb*tpp@y + H@tpp@bc*tpp@z)/temp@p@w}
pgfmathsetmacrotemp@p@z{(H@tpp@ca*tpp@x + H@tpp@cb*tpp@y + H@tpp@cc*tpp@z)/temp@p@w}
pgfpointxyz{temp@p@x}{temp@p@y}{temp@p@z}
}
tikzaliascoordinatesystem{tpp}{three point perspective}
makeatother
begin{document}
tdplotsetmaincoords{70}{0}
foreach X [evaluate=X as vq using {X*X}]in {2,2.1,...,4,3.9,3.8,...,2.1}{
begin{tikzpicture}[scale=pi,%tdplot_main_coords
view={tdplotmaintheta}{tdplotmainphi},
perspective={
p = {(0,0,10)},
q = {(0,vq,1.25)},
}
]
path[tdplot_screen_coords] (-2,-1) rectangle (2,2);
foreach Y in {-1,1}
{foreach X in {1,-1}
{shade[top color=gray!50,bottom color=gray!60,middle color=gray!20,
shading angle=90] (tpp cs:X*0.9,Y*0.9,1) -- (tpp cs:X*0.89,Y*0.9,0)
to[bend left=X*12]
(tpp cs:X*0.81,Y*0.9,0) -- (tpp cs:X*0.8,Y*0.8,1);}}
node[cylinder,draw,minimum width=4mm,minimum height=5mm,aspect=0.5,inner
sep=3pt,rotate=90,cylinder uses custom fill,cylinder end fill=gray!50!black,
cylinder body fill=black,label={[font=sffamily]below left:2}] (c2) at
(tpp cs:0,0,0.1){};
draw[name path=line] (c2.top|-c2.before top) -- (tpp cs:0,0,1);
draw[gray!50,fill=gray!50]
(tpp cs:-1,-1,1) -- (tpp cs:1,-1,1) -- (tpp cs:1,1,1) -- (tpp cs:-1,1,1) -- cycle;
draw[gray!50,fill=white,thick]
(tpp cs:-1,-1,1) -- (tpp cs:1,-1,1)
-- (tpp cs:1,-1,0.9) -- (tpp cs:-1,-1,0.9) -- cycle;
draw[dashed,fill=gray!25,name path=circle] plot[variable=x,smooth,domain=0:360]
(tpp cs:{0.8*cos(x)},{0.8*sin(x)},1);
node[cylinder,draw,minimum width=4mm,minimum height=2mm,aspect=0.5,inner
sep=3pt,rotate=85,cylinder uses custom fill,cylinder end fill=gray!50!black,
cylinder body fill=black] (c1) at
(tpp cs:0.4,0.1,1.2){};
node[anchor=north,font=sffamily] at ([yshift=-1mm]c1){1};
draw[dashed,name intersections={of=circle and line}] (intersection-1)
-- (tpp cs:0,0,1);
draw (tpp cs:0,0,1) -- (c1.west);
end{tikzpicture}}
end{document}
And if you replace the loop by
foreach X [evaluate=X as vq using {X*X}]in {3.5}{
say, you'll get.
Of course, you may find that another choice of parameters reproduces your screen shot more closely. Apart from the entries of q
you can also play with the view angles.
Incredible :-) simply fantastic your work.
– Sebastiano
3 hours ago
This looks fantastic. Does this kind of transformation also work for arbitrarily oriented lines, not just for lines that are parallel to the axes?
– AlexG
3 hours ago
Really good!!! Thanks a lot!!!
– Eduardo
2 hours 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');
});
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%2f470460%2fhow-can-i-give-this-perspective-with-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
All credits go to Max' answer. All I do is to truncate his general projection to a simpler case, which may help to understand better what's going on here. Max' picture shows very nicely what his code does: it transforms the objects in such a way that the edges that are parallel to the x axis meet in p
, the ones parallel to the y axis in q
and the ones parallel to the z axis in r
. (Yes, that's just a sloppy definition of "vanishing points".) However, in order to reproduce something like your screenshot, we only need to play with q
, which is what the following animation does.
documentclass[tikz,border=3.14mm]{standalone}
usepackage{tikz-3dplot}
usetikzlibrary{shapes.geometric,intersections}
usepgfmodule{nonlineartransformations}
% Max magic
makeatletter
% the first part is not in use here
deftikz@scan@transform@one@point#1{%
tikz@scan@one@pointpgf@process#1%
pgf@pos@transform{pgf@x}{pgf@y}}
tikzset{%
grid source opposite corners/.code args={#1and#2}{%
pgfextract@processtikz@transform@source@southwest{%
tikz@scan@transform@one@point{#1}}%
pgfextract@processtikz@transform@source@northeast{%
tikz@scan@transform@one@point{#2}}%
},
grid target corners/.code args={#1--#2--#3--#4}{%
pgfextract@processtikz@transform@target@southwest{%
tikz@scan@transform@one@point{#1}}%
pgfextract@processtikz@transform@target@southeast{%
tikz@scan@transform@one@point{#2}}%
pgfextract@processtikz@transform@target@northeast{%
tikz@scan@transform@one@point{#3}}%
pgfextract@processtikz@transform@target@northwest{%
tikz@scan@transform@one@point{#4}}%
}
}
deftikzgridtransform{%
pgfextract@processtikz@current@point{}%
pgf@process{%
pgfpointdiff{tikz@transform@source@southwest}%
{tikz@transform@source@northeast}%
}%
pgf@xc=pgf@xpgf@yc=pgf@y%
pgf@process{%
pgfpointdiff{tikz@transform@source@southwest}{tikz@current@point}%
}%
pgfmathparse{pgf@x/pgf@xc}lettikz@tx=pgfmathresult%
pgfmathparse{pgf@y/pgf@yc}lettikz@ty=pgfmathresult%
%
pgfpointlineattime{tikz@ty}{%
pgfpointlineattime{tikz@tx}{tikz@transform@target@southwest}%
{tikz@transform@target@southeast}}{%
pgfpointlineattime{tikz@tx}{tikz@transform@target@northwest}%
{tikz@transform@target@northeast}}%
}
% Initialize H matrix for perspective view
pgfmathsetmacroH@tpp@aa{1}pgfmathsetmacroH@tpp@ab{0}pgfmathsetmacroH@tpp@ac{0}%pgfmathsetmacroH@tpp@ad{0}
pgfmathsetmacroH@tpp@ba{0}pgfmathsetmacroH@tpp@bb{1}pgfmathsetmacroH@tpp@bc{0}%pgfmathsetmacroH@tpp@bd{0}
pgfmathsetmacroH@tpp@ca{0}pgfmathsetmacroH@tpp@cb{0}pgfmathsetmacroH@tpp@cc{1}%pgfmathsetmacroH@tpp@cd{0}
pgfmathsetmacroH@tpp@da{0}pgfmathsetmacroH@tpp@db{0}pgfmathsetmacroH@tpp@dc{0}%pgfmathsetmacroH@tpp@dd{1}
%Initialize H matrix for main rotation
pgfmathsetmacroH@rot@aa{1}pgfmathsetmacroH@rot@ab{0}pgfmathsetmacroH@rot@ac{0}%pgfmathsetmacroH@rot@ad{0}
pgfmathsetmacroH@rot@ba{0}pgfmathsetmacroH@rot@bb{1}pgfmathsetmacroH@rot@bc{0}%pgfmathsetmacroH@rot@bd{0}
pgfmathsetmacroH@rot@ca{0}pgfmathsetmacroH@rot@cb{0}pgfmathsetmacroH@rot@cc{1}%pgfmathsetmacroH@rot@cd{0}
%pgfmathsetmacroH@rot@da{0}pgfmathsetmacroH@rot@db{0}pgfmathsetmacroH@rot@dc{0}pgfmathsetmacroH@rot@dd{1}
pgfkeys{
/three point perspective/.cd,
p/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#1))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ba{#2/#1}
pgfmathsetmacroH@tpp@ca{#3/#1}
pgfmathsetmacroH@tpp@da{ 1/#1}
coordinate (vp-p) at (#1,#2,#3);
fi
},
q/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#2))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ab{#1/#2}
pgfmathsetmacroH@tpp@cb{#3/#2}
pgfmathsetmacroH@tpp@db{ 1/#2}
coordinate (vp-q) at (#1,#2,#3);
fi
},
r/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#3))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ac{#1/#3}
pgfmathsetmacroH@tpp@bc{#2/#3}
pgfmathsetmacroH@tpp@dc{ 1/#3}
coordinate (vp-r) at (#1,#2,#3);
fi
},
coordinate/.code args={#1,#2,#3}{
pgfmathsetmacrotpp@x{#1} %<- Max' fix
pgfmathsetmacrotpp@y{#2}
pgfmathsetmacrotpp@z{#3}
},
}
tikzset{
view/.code 2 args={
pgfmathsetmacrorot@main@theta{#1}
pgfmathsetmacrorot@main@phi{#2}
% Row 1
pgfmathsetmacroH@rot@aa{cos(rot@main@phi)}
pgfmathsetmacroH@rot@ab{sin(rot@main@phi)}
pgfmathsetmacroH@rot@ac{0}
% Row 2
pgfmathsetmacroH@rot@ba{-cos(rot@main@theta)*sin(rot@main@phi)}
pgfmathsetmacroH@rot@bb{cos(rot@main@phi)*cos(rot@main@theta)}
pgfmathsetmacroH@rot@bc{sin(rot@main@theta)}
% Row 3
pgfmathsetmacroH@m@ca{sin(rot@main@phi)*sin(rot@main@theta)}
pgfmathsetmacroH@m@cb{-cos(rot@main@phi)*sin(rot@main@theta)}
pgfmathsetmacroH@m@cc{cos(rot@main@theta)}
% Set vector values
pgfmathsetmacrovec@x@x{H@rot@aa}
pgfmathsetmacrovec@y@x{H@rot@ab}
pgfmathsetmacrovec@z@x{H@rot@ac}
pgfmathsetmacrovec@x@y{H@rot@ba}
pgfmathsetmacrovec@y@y{H@rot@bb}
pgfmathsetmacrovec@z@y{H@rot@bc}
% Set pgf vectors
pgfsetxvec{pgfpoint{vec@x@x cm}{vec@x@y cm}}
pgfsetyvec{pgfpoint{vec@y@x cm}{vec@y@y cm}}
pgfsetzvec{pgfpoint{vec@z@x cm}{vec@z@y cm}}
},
}
tikzset{
perspective/.code={pgfkeys{/three point perspective/.cd,#1}},
perspective/.default={p={(15,0,0)},q={(0,15,0)},r={(0,0,50)}},
}
tikzdeclarecoordinatesystem{three point perspective}{
pgfkeys{/three point perspective/.cd,coordinate={#1}}
pgfmathsetmacrotemp@p@w{H@tpp@da*tpp@x + H@tpp@db*tpp@y + H@tpp@dc*tpp@z + 1}
pgfmathsetmacrotemp@p@x{(H@tpp@aa*tpp@x + H@tpp@ab*tpp@y + H@tpp@ac*tpp@z)/temp@p@w}
pgfmathsetmacrotemp@p@y{(H@tpp@ba*tpp@x + H@tpp@bb*tpp@y + H@tpp@bc*tpp@z)/temp@p@w}
pgfmathsetmacrotemp@p@z{(H@tpp@ca*tpp@x + H@tpp@cb*tpp@y + H@tpp@cc*tpp@z)/temp@p@w}
pgfpointxyz{temp@p@x}{temp@p@y}{temp@p@z}
}
tikzaliascoordinatesystem{tpp}{three point perspective}
makeatother
begin{document}
tdplotsetmaincoords{70}{0}
foreach X [evaluate=X as vq using {X*X}]in {2,2.1,...,4,3.9,3.8,...,2.1}{
begin{tikzpicture}[scale=pi,%tdplot_main_coords
view={tdplotmaintheta}{tdplotmainphi},
perspective={
p = {(0,0,10)},
q = {(0,vq,1.25)},
}
]
path[tdplot_screen_coords] (-2,-1) rectangle (2,2);
foreach Y in {-1,1}
{foreach X in {1,-1}
{shade[top color=gray!50,bottom color=gray!60,middle color=gray!20,
shading angle=90] (tpp cs:X*0.9,Y*0.9,1) -- (tpp cs:X*0.89,Y*0.9,0)
to[bend left=X*12]
(tpp cs:X*0.81,Y*0.9,0) -- (tpp cs:X*0.8,Y*0.8,1);}}
node[cylinder,draw,minimum width=4mm,minimum height=5mm,aspect=0.5,inner
sep=3pt,rotate=90,cylinder uses custom fill,cylinder end fill=gray!50!black,
cylinder body fill=black,label={[font=sffamily]below left:2}] (c2) at
(tpp cs:0,0,0.1){};
draw[name path=line] (c2.top|-c2.before top) -- (tpp cs:0,0,1);
draw[gray!50,fill=gray!50]
(tpp cs:-1,-1,1) -- (tpp cs:1,-1,1) -- (tpp cs:1,1,1) -- (tpp cs:-1,1,1) -- cycle;
draw[gray!50,fill=white,thick]
(tpp cs:-1,-1,1) -- (tpp cs:1,-1,1)
-- (tpp cs:1,-1,0.9) -- (tpp cs:-1,-1,0.9) -- cycle;
draw[dashed,fill=gray!25,name path=circle] plot[variable=x,smooth,domain=0:360]
(tpp cs:{0.8*cos(x)},{0.8*sin(x)},1);
node[cylinder,draw,minimum width=4mm,minimum height=2mm,aspect=0.5,inner
sep=3pt,rotate=85,cylinder uses custom fill,cylinder end fill=gray!50!black,
cylinder body fill=black] (c1) at
(tpp cs:0.4,0.1,1.2){};
node[anchor=north,font=sffamily] at ([yshift=-1mm]c1){1};
draw[dashed,name intersections={of=circle and line}] (intersection-1)
-- (tpp cs:0,0,1);
draw (tpp cs:0,0,1) -- (c1.west);
end{tikzpicture}}
end{document}
And if you replace the loop by
foreach X [evaluate=X as vq using {X*X}]in {3.5}{
say, you'll get.
Of course, you may find that another choice of parameters reproduces your screen shot more closely. Apart from the entries of q
you can also play with the view angles.
Incredible :-) simply fantastic your work.
– Sebastiano
3 hours ago
This looks fantastic. Does this kind of transformation also work for arbitrarily oriented lines, not just for lines that are parallel to the axes?
– AlexG
3 hours ago
Really good!!! Thanks a lot!!!
– Eduardo
2 hours ago
add a comment |
All credits go to Max' answer. All I do is to truncate his general projection to a simpler case, which may help to understand better what's going on here. Max' picture shows very nicely what his code does: it transforms the objects in such a way that the edges that are parallel to the x axis meet in p
, the ones parallel to the y axis in q
and the ones parallel to the z axis in r
. (Yes, that's just a sloppy definition of "vanishing points".) However, in order to reproduce something like your screenshot, we only need to play with q
, which is what the following animation does.
documentclass[tikz,border=3.14mm]{standalone}
usepackage{tikz-3dplot}
usetikzlibrary{shapes.geometric,intersections}
usepgfmodule{nonlineartransformations}
% Max magic
makeatletter
% the first part is not in use here
deftikz@scan@transform@one@point#1{%
tikz@scan@one@pointpgf@process#1%
pgf@pos@transform{pgf@x}{pgf@y}}
tikzset{%
grid source opposite corners/.code args={#1and#2}{%
pgfextract@processtikz@transform@source@southwest{%
tikz@scan@transform@one@point{#1}}%
pgfextract@processtikz@transform@source@northeast{%
tikz@scan@transform@one@point{#2}}%
},
grid target corners/.code args={#1--#2--#3--#4}{%
pgfextract@processtikz@transform@target@southwest{%
tikz@scan@transform@one@point{#1}}%
pgfextract@processtikz@transform@target@southeast{%
tikz@scan@transform@one@point{#2}}%
pgfextract@processtikz@transform@target@northeast{%
tikz@scan@transform@one@point{#3}}%
pgfextract@processtikz@transform@target@northwest{%
tikz@scan@transform@one@point{#4}}%
}
}
deftikzgridtransform{%
pgfextract@processtikz@current@point{}%
pgf@process{%
pgfpointdiff{tikz@transform@source@southwest}%
{tikz@transform@source@northeast}%
}%
pgf@xc=pgf@xpgf@yc=pgf@y%
pgf@process{%
pgfpointdiff{tikz@transform@source@southwest}{tikz@current@point}%
}%
pgfmathparse{pgf@x/pgf@xc}lettikz@tx=pgfmathresult%
pgfmathparse{pgf@y/pgf@yc}lettikz@ty=pgfmathresult%
%
pgfpointlineattime{tikz@ty}{%
pgfpointlineattime{tikz@tx}{tikz@transform@target@southwest}%
{tikz@transform@target@southeast}}{%
pgfpointlineattime{tikz@tx}{tikz@transform@target@northwest}%
{tikz@transform@target@northeast}}%
}
% Initialize H matrix for perspective view
pgfmathsetmacroH@tpp@aa{1}pgfmathsetmacroH@tpp@ab{0}pgfmathsetmacroH@tpp@ac{0}%pgfmathsetmacroH@tpp@ad{0}
pgfmathsetmacroH@tpp@ba{0}pgfmathsetmacroH@tpp@bb{1}pgfmathsetmacroH@tpp@bc{0}%pgfmathsetmacroH@tpp@bd{0}
pgfmathsetmacroH@tpp@ca{0}pgfmathsetmacroH@tpp@cb{0}pgfmathsetmacroH@tpp@cc{1}%pgfmathsetmacroH@tpp@cd{0}
pgfmathsetmacroH@tpp@da{0}pgfmathsetmacroH@tpp@db{0}pgfmathsetmacroH@tpp@dc{0}%pgfmathsetmacroH@tpp@dd{1}
%Initialize H matrix for main rotation
pgfmathsetmacroH@rot@aa{1}pgfmathsetmacroH@rot@ab{0}pgfmathsetmacroH@rot@ac{0}%pgfmathsetmacroH@rot@ad{0}
pgfmathsetmacroH@rot@ba{0}pgfmathsetmacroH@rot@bb{1}pgfmathsetmacroH@rot@bc{0}%pgfmathsetmacroH@rot@bd{0}
pgfmathsetmacroH@rot@ca{0}pgfmathsetmacroH@rot@cb{0}pgfmathsetmacroH@rot@cc{1}%pgfmathsetmacroH@rot@cd{0}
%pgfmathsetmacroH@rot@da{0}pgfmathsetmacroH@rot@db{0}pgfmathsetmacroH@rot@dc{0}pgfmathsetmacroH@rot@dd{1}
pgfkeys{
/three point perspective/.cd,
p/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#1))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ba{#2/#1}
pgfmathsetmacroH@tpp@ca{#3/#1}
pgfmathsetmacroH@tpp@da{ 1/#1}
coordinate (vp-p) at (#1,#2,#3);
fi
},
q/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#2))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ab{#1/#2}
pgfmathsetmacroH@tpp@cb{#3/#2}
pgfmathsetmacroH@tpp@db{ 1/#2}
coordinate (vp-q) at (#1,#2,#3);
fi
},
r/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#3))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ac{#1/#3}
pgfmathsetmacroH@tpp@bc{#2/#3}
pgfmathsetmacroH@tpp@dc{ 1/#3}
coordinate (vp-r) at (#1,#2,#3);
fi
},
coordinate/.code args={#1,#2,#3}{
pgfmathsetmacrotpp@x{#1} %<- Max' fix
pgfmathsetmacrotpp@y{#2}
pgfmathsetmacrotpp@z{#3}
},
}
tikzset{
view/.code 2 args={
pgfmathsetmacrorot@main@theta{#1}
pgfmathsetmacrorot@main@phi{#2}
% Row 1
pgfmathsetmacroH@rot@aa{cos(rot@main@phi)}
pgfmathsetmacroH@rot@ab{sin(rot@main@phi)}
pgfmathsetmacroH@rot@ac{0}
% Row 2
pgfmathsetmacroH@rot@ba{-cos(rot@main@theta)*sin(rot@main@phi)}
pgfmathsetmacroH@rot@bb{cos(rot@main@phi)*cos(rot@main@theta)}
pgfmathsetmacroH@rot@bc{sin(rot@main@theta)}
% Row 3
pgfmathsetmacroH@m@ca{sin(rot@main@phi)*sin(rot@main@theta)}
pgfmathsetmacroH@m@cb{-cos(rot@main@phi)*sin(rot@main@theta)}
pgfmathsetmacroH@m@cc{cos(rot@main@theta)}
% Set vector values
pgfmathsetmacrovec@x@x{H@rot@aa}
pgfmathsetmacrovec@y@x{H@rot@ab}
pgfmathsetmacrovec@z@x{H@rot@ac}
pgfmathsetmacrovec@x@y{H@rot@ba}
pgfmathsetmacrovec@y@y{H@rot@bb}
pgfmathsetmacrovec@z@y{H@rot@bc}
% Set pgf vectors
pgfsetxvec{pgfpoint{vec@x@x cm}{vec@x@y cm}}
pgfsetyvec{pgfpoint{vec@y@x cm}{vec@y@y cm}}
pgfsetzvec{pgfpoint{vec@z@x cm}{vec@z@y cm}}
},
}
tikzset{
perspective/.code={pgfkeys{/three point perspective/.cd,#1}},
perspective/.default={p={(15,0,0)},q={(0,15,0)},r={(0,0,50)}},
}
tikzdeclarecoordinatesystem{three point perspective}{
pgfkeys{/three point perspective/.cd,coordinate={#1}}
pgfmathsetmacrotemp@p@w{H@tpp@da*tpp@x + H@tpp@db*tpp@y + H@tpp@dc*tpp@z + 1}
pgfmathsetmacrotemp@p@x{(H@tpp@aa*tpp@x + H@tpp@ab*tpp@y + H@tpp@ac*tpp@z)/temp@p@w}
pgfmathsetmacrotemp@p@y{(H@tpp@ba*tpp@x + H@tpp@bb*tpp@y + H@tpp@bc*tpp@z)/temp@p@w}
pgfmathsetmacrotemp@p@z{(H@tpp@ca*tpp@x + H@tpp@cb*tpp@y + H@tpp@cc*tpp@z)/temp@p@w}
pgfpointxyz{temp@p@x}{temp@p@y}{temp@p@z}
}
tikzaliascoordinatesystem{tpp}{three point perspective}
makeatother
begin{document}
tdplotsetmaincoords{70}{0}
foreach X [evaluate=X as vq using {X*X}]in {2,2.1,...,4,3.9,3.8,...,2.1}{
begin{tikzpicture}[scale=pi,%tdplot_main_coords
view={tdplotmaintheta}{tdplotmainphi},
perspective={
p = {(0,0,10)},
q = {(0,vq,1.25)},
}
]
path[tdplot_screen_coords] (-2,-1) rectangle (2,2);
foreach Y in {-1,1}
{foreach X in {1,-1}
{shade[top color=gray!50,bottom color=gray!60,middle color=gray!20,
shading angle=90] (tpp cs:X*0.9,Y*0.9,1) -- (tpp cs:X*0.89,Y*0.9,0)
to[bend left=X*12]
(tpp cs:X*0.81,Y*0.9,0) -- (tpp cs:X*0.8,Y*0.8,1);}}
node[cylinder,draw,minimum width=4mm,minimum height=5mm,aspect=0.5,inner
sep=3pt,rotate=90,cylinder uses custom fill,cylinder end fill=gray!50!black,
cylinder body fill=black,label={[font=sffamily]below left:2}] (c2) at
(tpp cs:0,0,0.1){};
draw[name path=line] (c2.top|-c2.before top) -- (tpp cs:0,0,1);
draw[gray!50,fill=gray!50]
(tpp cs:-1,-1,1) -- (tpp cs:1,-1,1) -- (tpp cs:1,1,1) -- (tpp cs:-1,1,1) -- cycle;
draw[gray!50,fill=white,thick]
(tpp cs:-1,-1,1) -- (tpp cs:1,-1,1)
-- (tpp cs:1,-1,0.9) -- (tpp cs:-1,-1,0.9) -- cycle;
draw[dashed,fill=gray!25,name path=circle] plot[variable=x,smooth,domain=0:360]
(tpp cs:{0.8*cos(x)},{0.8*sin(x)},1);
node[cylinder,draw,minimum width=4mm,minimum height=2mm,aspect=0.5,inner
sep=3pt,rotate=85,cylinder uses custom fill,cylinder end fill=gray!50!black,
cylinder body fill=black] (c1) at
(tpp cs:0.4,0.1,1.2){};
node[anchor=north,font=sffamily] at ([yshift=-1mm]c1){1};
draw[dashed,name intersections={of=circle and line}] (intersection-1)
-- (tpp cs:0,0,1);
draw (tpp cs:0,0,1) -- (c1.west);
end{tikzpicture}}
end{document}
And if you replace the loop by
foreach X [evaluate=X as vq using {X*X}]in {3.5}{
say, you'll get.
Of course, you may find that another choice of parameters reproduces your screen shot more closely. Apart from the entries of q
you can also play with the view angles.
Incredible :-) simply fantastic your work.
– Sebastiano
3 hours ago
This looks fantastic. Does this kind of transformation also work for arbitrarily oriented lines, not just for lines that are parallel to the axes?
– AlexG
3 hours ago
Really good!!! Thanks a lot!!!
– Eduardo
2 hours ago
add a comment |
All credits go to Max' answer. All I do is to truncate his general projection to a simpler case, which may help to understand better what's going on here. Max' picture shows very nicely what his code does: it transforms the objects in such a way that the edges that are parallel to the x axis meet in p
, the ones parallel to the y axis in q
and the ones parallel to the z axis in r
. (Yes, that's just a sloppy definition of "vanishing points".) However, in order to reproduce something like your screenshot, we only need to play with q
, which is what the following animation does.
documentclass[tikz,border=3.14mm]{standalone}
usepackage{tikz-3dplot}
usetikzlibrary{shapes.geometric,intersections}
usepgfmodule{nonlineartransformations}
% Max magic
makeatletter
% the first part is not in use here
deftikz@scan@transform@one@point#1{%
tikz@scan@one@pointpgf@process#1%
pgf@pos@transform{pgf@x}{pgf@y}}
tikzset{%
grid source opposite corners/.code args={#1and#2}{%
pgfextract@processtikz@transform@source@southwest{%
tikz@scan@transform@one@point{#1}}%
pgfextract@processtikz@transform@source@northeast{%
tikz@scan@transform@one@point{#2}}%
},
grid target corners/.code args={#1--#2--#3--#4}{%
pgfextract@processtikz@transform@target@southwest{%
tikz@scan@transform@one@point{#1}}%
pgfextract@processtikz@transform@target@southeast{%
tikz@scan@transform@one@point{#2}}%
pgfextract@processtikz@transform@target@northeast{%
tikz@scan@transform@one@point{#3}}%
pgfextract@processtikz@transform@target@northwest{%
tikz@scan@transform@one@point{#4}}%
}
}
deftikzgridtransform{%
pgfextract@processtikz@current@point{}%
pgf@process{%
pgfpointdiff{tikz@transform@source@southwest}%
{tikz@transform@source@northeast}%
}%
pgf@xc=pgf@xpgf@yc=pgf@y%
pgf@process{%
pgfpointdiff{tikz@transform@source@southwest}{tikz@current@point}%
}%
pgfmathparse{pgf@x/pgf@xc}lettikz@tx=pgfmathresult%
pgfmathparse{pgf@y/pgf@yc}lettikz@ty=pgfmathresult%
%
pgfpointlineattime{tikz@ty}{%
pgfpointlineattime{tikz@tx}{tikz@transform@target@southwest}%
{tikz@transform@target@southeast}}{%
pgfpointlineattime{tikz@tx}{tikz@transform@target@northwest}%
{tikz@transform@target@northeast}}%
}
% Initialize H matrix for perspective view
pgfmathsetmacroH@tpp@aa{1}pgfmathsetmacroH@tpp@ab{0}pgfmathsetmacroH@tpp@ac{0}%pgfmathsetmacroH@tpp@ad{0}
pgfmathsetmacroH@tpp@ba{0}pgfmathsetmacroH@tpp@bb{1}pgfmathsetmacroH@tpp@bc{0}%pgfmathsetmacroH@tpp@bd{0}
pgfmathsetmacroH@tpp@ca{0}pgfmathsetmacroH@tpp@cb{0}pgfmathsetmacroH@tpp@cc{1}%pgfmathsetmacroH@tpp@cd{0}
pgfmathsetmacroH@tpp@da{0}pgfmathsetmacroH@tpp@db{0}pgfmathsetmacroH@tpp@dc{0}%pgfmathsetmacroH@tpp@dd{1}
%Initialize H matrix for main rotation
pgfmathsetmacroH@rot@aa{1}pgfmathsetmacroH@rot@ab{0}pgfmathsetmacroH@rot@ac{0}%pgfmathsetmacroH@rot@ad{0}
pgfmathsetmacroH@rot@ba{0}pgfmathsetmacroH@rot@bb{1}pgfmathsetmacroH@rot@bc{0}%pgfmathsetmacroH@rot@bd{0}
pgfmathsetmacroH@rot@ca{0}pgfmathsetmacroH@rot@cb{0}pgfmathsetmacroH@rot@cc{1}%pgfmathsetmacroH@rot@cd{0}
%pgfmathsetmacroH@rot@da{0}pgfmathsetmacroH@rot@db{0}pgfmathsetmacroH@rot@dc{0}pgfmathsetmacroH@rot@dd{1}
pgfkeys{
/three point perspective/.cd,
p/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#1))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ba{#2/#1}
pgfmathsetmacroH@tpp@ca{#3/#1}
pgfmathsetmacroH@tpp@da{ 1/#1}
coordinate (vp-p) at (#1,#2,#3);
fi
},
q/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#2))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ab{#1/#2}
pgfmathsetmacroH@tpp@cb{#3/#2}
pgfmathsetmacroH@tpp@db{ 1/#2}
coordinate (vp-q) at (#1,#2,#3);
fi
},
r/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#3))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ac{#1/#3}
pgfmathsetmacroH@tpp@bc{#2/#3}
pgfmathsetmacroH@tpp@dc{ 1/#3}
coordinate (vp-r) at (#1,#2,#3);
fi
},
coordinate/.code args={#1,#2,#3}{
pgfmathsetmacrotpp@x{#1} %<- Max' fix
pgfmathsetmacrotpp@y{#2}
pgfmathsetmacrotpp@z{#3}
},
}
tikzset{
view/.code 2 args={
pgfmathsetmacrorot@main@theta{#1}
pgfmathsetmacrorot@main@phi{#2}
% Row 1
pgfmathsetmacroH@rot@aa{cos(rot@main@phi)}
pgfmathsetmacroH@rot@ab{sin(rot@main@phi)}
pgfmathsetmacroH@rot@ac{0}
% Row 2
pgfmathsetmacroH@rot@ba{-cos(rot@main@theta)*sin(rot@main@phi)}
pgfmathsetmacroH@rot@bb{cos(rot@main@phi)*cos(rot@main@theta)}
pgfmathsetmacroH@rot@bc{sin(rot@main@theta)}
% Row 3
pgfmathsetmacroH@m@ca{sin(rot@main@phi)*sin(rot@main@theta)}
pgfmathsetmacroH@m@cb{-cos(rot@main@phi)*sin(rot@main@theta)}
pgfmathsetmacroH@m@cc{cos(rot@main@theta)}
% Set vector values
pgfmathsetmacrovec@x@x{H@rot@aa}
pgfmathsetmacrovec@y@x{H@rot@ab}
pgfmathsetmacrovec@z@x{H@rot@ac}
pgfmathsetmacrovec@x@y{H@rot@ba}
pgfmathsetmacrovec@y@y{H@rot@bb}
pgfmathsetmacrovec@z@y{H@rot@bc}
% Set pgf vectors
pgfsetxvec{pgfpoint{vec@x@x cm}{vec@x@y cm}}
pgfsetyvec{pgfpoint{vec@y@x cm}{vec@y@y cm}}
pgfsetzvec{pgfpoint{vec@z@x cm}{vec@z@y cm}}
},
}
tikzset{
perspective/.code={pgfkeys{/three point perspective/.cd,#1}},
perspective/.default={p={(15,0,0)},q={(0,15,0)},r={(0,0,50)}},
}
tikzdeclarecoordinatesystem{three point perspective}{
pgfkeys{/three point perspective/.cd,coordinate={#1}}
pgfmathsetmacrotemp@p@w{H@tpp@da*tpp@x + H@tpp@db*tpp@y + H@tpp@dc*tpp@z + 1}
pgfmathsetmacrotemp@p@x{(H@tpp@aa*tpp@x + H@tpp@ab*tpp@y + H@tpp@ac*tpp@z)/temp@p@w}
pgfmathsetmacrotemp@p@y{(H@tpp@ba*tpp@x + H@tpp@bb*tpp@y + H@tpp@bc*tpp@z)/temp@p@w}
pgfmathsetmacrotemp@p@z{(H@tpp@ca*tpp@x + H@tpp@cb*tpp@y + H@tpp@cc*tpp@z)/temp@p@w}
pgfpointxyz{temp@p@x}{temp@p@y}{temp@p@z}
}
tikzaliascoordinatesystem{tpp}{three point perspective}
makeatother
begin{document}
tdplotsetmaincoords{70}{0}
foreach X [evaluate=X as vq using {X*X}]in {2,2.1,...,4,3.9,3.8,...,2.1}{
begin{tikzpicture}[scale=pi,%tdplot_main_coords
view={tdplotmaintheta}{tdplotmainphi},
perspective={
p = {(0,0,10)},
q = {(0,vq,1.25)},
}
]
path[tdplot_screen_coords] (-2,-1) rectangle (2,2);
foreach Y in {-1,1}
{foreach X in {1,-1}
{shade[top color=gray!50,bottom color=gray!60,middle color=gray!20,
shading angle=90] (tpp cs:X*0.9,Y*0.9,1) -- (tpp cs:X*0.89,Y*0.9,0)
to[bend left=X*12]
(tpp cs:X*0.81,Y*0.9,0) -- (tpp cs:X*0.8,Y*0.8,1);}}
node[cylinder,draw,minimum width=4mm,minimum height=5mm,aspect=0.5,inner
sep=3pt,rotate=90,cylinder uses custom fill,cylinder end fill=gray!50!black,
cylinder body fill=black,label={[font=sffamily]below left:2}] (c2) at
(tpp cs:0,0,0.1){};
draw[name path=line] (c2.top|-c2.before top) -- (tpp cs:0,0,1);
draw[gray!50,fill=gray!50]
(tpp cs:-1,-1,1) -- (tpp cs:1,-1,1) -- (tpp cs:1,1,1) -- (tpp cs:-1,1,1) -- cycle;
draw[gray!50,fill=white,thick]
(tpp cs:-1,-1,1) -- (tpp cs:1,-1,1)
-- (tpp cs:1,-1,0.9) -- (tpp cs:-1,-1,0.9) -- cycle;
draw[dashed,fill=gray!25,name path=circle] plot[variable=x,smooth,domain=0:360]
(tpp cs:{0.8*cos(x)},{0.8*sin(x)},1);
node[cylinder,draw,minimum width=4mm,minimum height=2mm,aspect=0.5,inner
sep=3pt,rotate=85,cylinder uses custom fill,cylinder end fill=gray!50!black,
cylinder body fill=black] (c1) at
(tpp cs:0.4,0.1,1.2){};
node[anchor=north,font=sffamily] at ([yshift=-1mm]c1){1};
draw[dashed,name intersections={of=circle and line}] (intersection-1)
-- (tpp cs:0,0,1);
draw (tpp cs:0,0,1) -- (c1.west);
end{tikzpicture}}
end{document}
And if you replace the loop by
foreach X [evaluate=X as vq using {X*X}]in {3.5}{
say, you'll get.
Of course, you may find that another choice of parameters reproduces your screen shot more closely. Apart from the entries of q
you can also play with the view angles.
All credits go to Max' answer. All I do is to truncate his general projection to a simpler case, which may help to understand better what's going on here. Max' picture shows very nicely what his code does: it transforms the objects in such a way that the edges that are parallel to the x axis meet in p
, the ones parallel to the y axis in q
and the ones parallel to the z axis in r
. (Yes, that's just a sloppy definition of "vanishing points".) However, in order to reproduce something like your screenshot, we only need to play with q
, which is what the following animation does.
documentclass[tikz,border=3.14mm]{standalone}
usepackage{tikz-3dplot}
usetikzlibrary{shapes.geometric,intersections}
usepgfmodule{nonlineartransformations}
% Max magic
makeatletter
% the first part is not in use here
deftikz@scan@transform@one@point#1{%
tikz@scan@one@pointpgf@process#1%
pgf@pos@transform{pgf@x}{pgf@y}}
tikzset{%
grid source opposite corners/.code args={#1and#2}{%
pgfextract@processtikz@transform@source@southwest{%
tikz@scan@transform@one@point{#1}}%
pgfextract@processtikz@transform@source@northeast{%
tikz@scan@transform@one@point{#2}}%
},
grid target corners/.code args={#1--#2--#3--#4}{%
pgfextract@processtikz@transform@target@southwest{%
tikz@scan@transform@one@point{#1}}%
pgfextract@processtikz@transform@target@southeast{%
tikz@scan@transform@one@point{#2}}%
pgfextract@processtikz@transform@target@northeast{%
tikz@scan@transform@one@point{#3}}%
pgfextract@processtikz@transform@target@northwest{%
tikz@scan@transform@one@point{#4}}%
}
}
deftikzgridtransform{%
pgfextract@processtikz@current@point{}%
pgf@process{%
pgfpointdiff{tikz@transform@source@southwest}%
{tikz@transform@source@northeast}%
}%
pgf@xc=pgf@xpgf@yc=pgf@y%
pgf@process{%
pgfpointdiff{tikz@transform@source@southwest}{tikz@current@point}%
}%
pgfmathparse{pgf@x/pgf@xc}lettikz@tx=pgfmathresult%
pgfmathparse{pgf@y/pgf@yc}lettikz@ty=pgfmathresult%
%
pgfpointlineattime{tikz@ty}{%
pgfpointlineattime{tikz@tx}{tikz@transform@target@southwest}%
{tikz@transform@target@southeast}}{%
pgfpointlineattime{tikz@tx}{tikz@transform@target@northwest}%
{tikz@transform@target@northeast}}%
}
% Initialize H matrix for perspective view
pgfmathsetmacroH@tpp@aa{1}pgfmathsetmacroH@tpp@ab{0}pgfmathsetmacroH@tpp@ac{0}%pgfmathsetmacroH@tpp@ad{0}
pgfmathsetmacroH@tpp@ba{0}pgfmathsetmacroH@tpp@bb{1}pgfmathsetmacroH@tpp@bc{0}%pgfmathsetmacroH@tpp@bd{0}
pgfmathsetmacroH@tpp@ca{0}pgfmathsetmacroH@tpp@cb{0}pgfmathsetmacroH@tpp@cc{1}%pgfmathsetmacroH@tpp@cd{0}
pgfmathsetmacroH@tpp@da{0}pgfmathsetmacroH@tpp@db{0}pgfmathsetmacroH@tpp@dc{0}%pgfmathsetmacroH@tpp@dd{1}
%Initialize H matrix for main rotation
pgfmathsetmacroH@rot@aa{1}pgfmathsetmacroH@rot@ab{0}pgfmathsetmacroH@rot@ac{0}%pgfmathsetmacroH@rot@ad{0}
pgfmathsetmacroH@rot@ba{0}pgfmathsetmacroH@rot@bb{1}pgfmathsetmacroH@rot@bc{0}%pgfmathsetmacroH@rot@bd{0}
pgfmathsetmacroH@rot@ca{0}pgfmathsetmacroH@rot@cb{0}pgfmathsetmacroH@rot@cc{1}%pgfmathsetmacroH@rot@cd{0}
%pgfmathsetmacroH@rot@da{0}pgfmathsetmacroH@rot@db{0}pgfmathsetmacroH@rot@dc{0}pgfmathsetmacroH@rot@dd{1}
pgfkeys{
/three point perspective/.cd,
p/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#1))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ba{#2/#1}
pgfmathsetmacroH@tpp@ca{#3/#1}
pgfmathsetmacroH@tpp@da{ 1/#1}
coordinate (vp-p) at (#1,#2,#3);
fi
},
q/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#2))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ab{#1/#2}
pgfmathsetmacroH@tpp@cb{#3/#2}
pgfmathsetmacroH@tpp@db{ 1/#2}
coordinate (vp-q) at (#1,#2,#3);
fi
},
r/.code args={(#1,#2,#3)}{
pgfmathparse{int(round(#3))}
ifnumpgfmathresult=0else
pgfmathsetmacroH@tpp@ac{#1/#3}
pgfmathsetmacroH@tpp@bc{#2/#3}
pgfmathsetmacroH@tpp@dc{ 1/#3}
coordinate (vp-r) at (#1,#2,#3);
fi
},
coordinate/.code args={#1,#2,#3}{
pgfmathsetmacrotpp@x{#1} %<- Max' fix
pgfmathsetmacrotpp@y{#2}
pgfmathsetmacrotpp@z{#3}
},
}
tikzset{
view/.code 2 args={
pgfmathsetmacrorot@main@theta{#1}
pgfmathsetmacrorot@main@phi{#2}
% Row 1
pgfmathsetmacroH@rot@aa{cos(rot@main@phi)}
pgfmathsetmacroH@rot@ab{sin(rot@main@phi)}
pgfmathsetmacroH@rot@ac{0}
% Row 2
pgfmathsetmacroH@rot@ba{-cos(rot@main@theta)*sin(rot@main@phi)}
pgfmathsetmacroH@rot@bb{cos(rot@main@phi)*cos(rot@main@theta)}
pgfmathsetmacroH@rot@bc{sin(rot@main@theta)}
% Row 3
pgfmathsetmacroH@m@ca{sin(rot@main@phi)*sin(rot@main@theta)}
pgfmathsetmacroH@m@cb{-cos(rot@main@phi)*sin(rot@main@theta)}
pgfmathsetmacroH@m@cc{cos(rot@main@theta)}
% Set vector values
pgfmathsetmacrovec@x@x{H@rot@aa}
pgfmathsetmacrovec@y@x{H@rot@ab}
pgfmathsetmacrovec@z@x{H@rot@ac}
pgfmathsetmacrovec@x@y{H@rot@ba}
pgfmathsetmacrovec@y@y{H@rot@bb}
pgfmathsetmacrovec@z@y{H@rot@bc}
% Set pgf vectors
pgfsetxvec{pgfpoint{vec@x@x cm}{vec@x@y cm}}
pgfsetyvec{pgfpoint{vec@y@x cm}{vec@y@y cm}}
pgfsetzvec{pgfpoint{vec@z@x cm}{vec@z@y cm}}
},
}
tikzset{
perspective/.code={pgfkeys{/three point perspective/.cd,#1}},
perspective/.default={p={(15,0,0)},q={(0,15,0)},r={(0,0,50)}},
}
tikzdeclarecoordinatesystem{three point perspective}{
pgfkeys{/three point perspective/.cd,coordinate={#1}}
pgfmathsetmacrotemp@p@w{H@tpp@da*tpp@x + H@tpp@db*tpp@y + H@tpp@dc*tpp@z + 1}
pgfmathsetmacrotemp@p@x{(H@tpp@aa*tpp@x + H@tpp@ab*tpp@y + H@tpp@ac*tpp@z)/temp@p@w}
pgfmathsetmacrotemp@p@y{(H@tpp@ba*tpp@x + H@tpp@bb*tpp@y + H@tpp@bc*tpp@z)/temp@p@w}
pgfmathsetmacrotemp@p@z{(H@tpp@ca*tpp@x + H@tpp@cb*tpp@y + H@tpp@cc*tpp@z)/temp@p@w}
pgfpointxyz{temp@p@x}{temp@p@y}{temp@p@z}
}
tikzaliascoordinatesystem{tpp}{three point perspective}
makeatother
begin{document}
tdplotsetmaincoords{70}{0}
foreach X [evaluate=X as vq using {X*X}]in {2,2.1,...,4,3.9,3.8,...,2.1}{
begin{tikzpicture}[scale=pi,%tdplot_main_coords
view={tdplotmaintheta}{tdplotmainphi},
perspective={
p = {(0,0,10)},
q = {(0,vq,1.25)},
}
]
path[tdplot_screen_coords] (-2,-1) rectangle (2,2);
foreach Y in {-1,1}
{foreach X in {1,-1}
{shade[top color=gray!50,bottom color=gray!60,middle color=gray!20,
shading angle=90] (tpp cs:X*0.9,Y*0.9,1) -- (tpp cs:X*0.89,Y*0.9,0)
to[bend left=X*12]
(tpp cs:X*0.81,Y*0.9,0) -- (tpp cs:X*0.8,Y*0.8,1);}}
node[cylinder,draw,minimum width=4mm,minimum height=5mm,aspect=0.5,inner
sep=3pt,rotate=90,cylinder uses custom fill,cylinder end fill=gray!50!black,
cylinder body fill=black,label={[font=sffamily]below left:2}] (c2) at
(tpp cs:0,0,0.1){};
draw[name path=line] (c2.top|-c2.before top) -- (tpp cs:0,0,1);
draw[gray!50,fill=gray!50]
(tpp cs:-1,-1,1) -- (tpp cs:1,-1,1) -- (tpp cs:1,1,1) -- (tpp cs:-1,1,1) -- cycle;
draw[gray!50,fill=white,thick]
(tpp cs:-1,-1,1) -- (tpp cs:1,-1,1)
-- (tpp cs:1,-1,0.9) -- (tpp cs:-1,-1,0.9) -- cycle;
draw[dashed,fill=gray!25,name path=circle] plot[variable=x,smooth,domain=0:360]
(tpp cs:{0.8*cos(x)},{0.8*sin(x)},1);
node[cylinder,draw,minimum width=4mm,minimum height=2mm,aspect=0.5,inner
sep=3pt,rotate=85,cylinder uses custom fill,cylinder end fill=gray!50!black,
cylinder body fill=black] (c1) at
(tpp cs:0.4,0.1,1.2){};
node[anchor=north,font=sffamily] at ([yshift=-1mm]c1){1};
draw[dashed,name intersections={of=circle and line}] (intersection-1)
-- (tpp cs:0,0,1);
draw (tpp cs:0,0,1) -- (c1.west);
end{tikzpicture}}
end{document}
And if you replace the loop by
foreach X [evaluate=X as vq using {X*X}]in {3.5}{
say, you'll get.
Of course, you may find that another choice of parameters reproduces your screen shot more closely. Apart from the entries of q
you can also play with the view angles.
edited 9 hours ago
answered 11 hours ago
marmotmarmot
92k4107201
92k4107201
Incredible :-) simply fantastic your work.
– Sebastiano
3 hours ago
This looks fantastic. Does this kind of transformation also work for arbitrarily oriented lines, not just for lines that are parallel to the axes?
– AlexG
3 hours ago
Really good!!! Thanks a lot!!!
– Eduardo
2 hours ago
add a comment |
Incredible :-) simply fantastic your work.
– Sebastiano
3 hours ago
This looks fantastic. Does this kind of transformation also work for arbitrarily oriented lines, not just for lines that are parallel to the axes?
– AlexG
3 hours ago
Really good!!! Thanks a lot!!!
– Eduardo
2 hours ago
Incredible :-) simply fantastic your work.
– Sebastiano
3 hours ago
Incredible :-) simply fantastic your work.
– Sebastiano
3 hours ago
This looks fantastic. Does this kind of transformation also work for arbitrarily oriented lines, not just for lines that are parallel to the axes?
– AlexG
3 hours ago
This looks fantastic. Does this kind of transformation also work for arbitrarily oriented lines, not just for lines that are parallel to the axes?
– AlexG
3 hours ago
Really good!!! Thanks a lot!!!
– Eduardo
2 hours ago
Really good!!! Thanks a lot!!!
– Eduardo
2 hours 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');
});
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%2f470460%2fhow-can-i-give-this-perspective-with-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');
});
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');
});
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');
});
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
Can you show us the code you already have?
– Sigur
13 hours ago
As far as I know, the most straightforward way will be to employ this great answer.
– marmot
13 hours ago
I am just starting... marmot, that seems really difficult!!
– Eduardo
13 hours ago
1
Yes, unfortunately these cool macros are not yet part of a package or library. So for the time being you would still copy the preamble. Notice that once you copied it, the rest will not be difficult.
– marmot
13 hours ago