8

I am trying to apply Tikz-dimline label position (Dimension lines) to a 3-dimensional drawing. This has worked well for me in 2D, so thought it would do so in 3D.

Thus, when in a 2D drawing I would use something like

\draw [postaction={indicate dimensions={$2$}}] (C) -- (G);

it should work in 3D when used as

\begin{scope}[canvas is xy plane at z=\Height]
  \draw [postaction={indicate dimensions={$2$}}] (C) -- (G);
\end{scope}

However, the above two produce exactly the same results (black dimension lines in the figure). An approximation of the desired result is shown in red.

enter image description here

What is the simplest way to modify either the dimensionsing code from Tikz-dimline label position (Dimension lines), or how I am using it to work in 3D?

Notes:

  • I would highly prefer changing how I am invoking the code (instead of changing the existing dimline code), as the existing code works well for the 2D case.

  • The desired result in red is an approximation so as not clutter the figure posted here (The dimension arrow line was shifted away from the end of the dimension markers).

  • The code seems longer due mostly to the packaging of the grid and axis drawings. The relevant parts are really at the top (the dimension code) and at the end in the canvas is xy plane at z=\Height scope.

Code:

\documentclass{article}

\usepackage{xparse} \usepackage{tikz} \usetikzlibrary{3d}

\pagecolor{white}

%% --------------------------------------------------------------------- %% https://tex.stackexchange.com/a/468144/4301 \usetikzlibrary{calc,decorations.pathreplacing,arrows.meta} \newif\ifdrawdimlineleft \newif\ifdrawdimlineright \tikzset{dimlabel distance/.initial=5mm, vertical lines extend/.initial=5mm, vertical dim line/.style={gray,thin}, dim arrow line/.style={latex-latex,thin}, dim label/.style={}, left dimline/.is if=drawdimlineleft, left dimline=true, right dimline/.is if=drawdimlineright, right dimline=true, indicate dimensions/.style={decorate,decoration={ show path construction, lineto code={ \draw[dim arrow line] ($ (\tikzinputsegmentfirst)!\pgfkeysvalueof{/tikz/dimlabel distance}!-90:(\tikzinputsegmentlast) $) -- ($ (\tikzinputsegmentlast)!\pgfkeysvalueof{/tikz/dimlabel distance}!90:(\tikzinputsegmentfirst) $) \ifx#1\empty \else node[midway,sloped,fill=white,dim label]{#1} \fi; \ifdrawdimlineleft \draw[vertical dim line] (\tikzinputsegmentfirst) -- ($ (\tikzinputsegmentfirst)!\pgfkeysvalueof{/tikz/vertical lines extend}!-90:(\tikzinputsegmentlast) $); \fi \ifdrawdimlineright \draw[vertical dim line] (\tikzinputsegmentlast) -- ($ (\tikzinputsegmentlast)!\pgfkeysvalueof{/tikz/vertical lines extend}!90:(\tikzinputsegmentfirst) $); \fi }}}}

%% ---------------------------------------------------------------------

\NewDocumentCommand{\DrawCoordinateGrid}{O{} m m m m m m}{% \def\XGridMin{#2} \def\XGridMax{#3} \def\YGridMin{#4} \def\YGridMax{#5} \def\ZGridMin{#6} \def\ZGridMax{#7} % \begin{scope}[canvas is xy plane at z=0, ultra thin, gray!75] \draw [#1] (\XGridMin,\YGridMin) grid (\XGridMax,\YGridMax); \end{scope} \begin{scope}[canvas is yz plane at x=0, ultra thin, gray!75] \draw [#1] (\YGridMin,\ZGridMin) grid (\YGridMax,\ZGridMax); \end{scope} \begin{scope}[canvas is xz plane at y=0, ultra thin, gray!75] \draw [#1] (\XGridMin,\ZGridMin) grid (\XGridMax,\ZGridMax); \end{scope} }%

\NewDocumentCommand{\DrawCoordinateAxis}{O{} m m m m m m}{% \def\XAxisMin{#2} \def\XAxisMax{#3} \def\YAxisMin{#4} \def\YAxisMax{#5} \def\ZAxisMin{#6} \def\ZAxisMax{#7} % \begin{scope}[thin, gray, -latex] \draw [#1] (\XAxisMin,0,0) -- (\XAxisMax,0,0) node [below left] {$x$}; \draw [#1] (0,\YAxisMin,0) -- (0,\YAxisMax,0) node [right] {$y$}; \draw [#1] (0,0,\ZAxisMin) -- (0,0,\ZAxisMax) node [above] {$z$}; \end{scope} }%

\newcommand\Width{2.5}% \newcommand\Depth{4.5}% \newcommand*\Height{2.0}%

\tikzset{Line Style/.style={draw=brown, ultra thick, join=round}}

\begin{document} \begin{tikzpicture}[ x={(-0.5cm,-0.5cm)}, y={(0.9659cm,-0.25882cm)}, z={(0cm,1cm)}, ]

\coordinate (O) at (0,0,0);
%% ---------------------------------------------- yz plane at x=0
\coordinate (A) at (0,\Width,0);
\coordinate (B) at (0,\Width,\Height);
\coordinate (C) at (0,0,\Height);
%% ---------------------------------------------- yz plane at x=\Depth
\coordinate (D) at (\Depth,0,0);
\coordinate (E) at (\Depth,\Width,0);
\coordinate (F) at (\Depth,\Width,\Height);
\coordinate (G) at (\Depth,0,\Height);

\DrawCoordinateGrid{0}{6}{0}{4}{0}{3}
\DrawCoordinateAxis[thick, black]{0}{6.5}{0}{4.5}{0}{3.5}

%% ------------------------------------ Draw the rectangular prism
\draw [Line Style] (O) -- (A) -- (B) -- (C) -- cycle;% yz plane at x=0
\draw [Line Style] (D) -- (E) -- (F) -- (G) -- cycle;% yz plane at x=\Depth
\draw [Line Style] (O) -- (D) -- (G) -- (C) -- cycle;% xz plane at y=0
\draw [Line Style] (A) -- (E) -- (F) -- (B) -- cycle;% xz plane at y=\Width

%% This produces the same if it is within a "canvas is xy plane at z=\Height" scope
%\draw [postaction={indicate dimensions={$2$}}] (C) -- (G);
\begin{scope}[canvas is xy plane at z=\Height, inner sep=0pt]
  \draw [postaction={indicate dimensions={$2$}}] (C) -- (G);
\end{scope}

%% This is what I expected from the above scope:
\begin{scope}[canvas is xy plane at z=\Height, transform shape, inner sep=2pt, red]
  \draw (C) -- ([yshift=-5mm]C);
  \draw (G) -- ([yshift=-5mm]G);
  \draw [latex-latex] ([yshift=-3mm]C) -- ([yshift=-3mm]G)
      node [pos=0.5, xscale=-1, yscale=-1, fill=white] {$2$};
\end{scope}

%% Following is for debugging purposes so you can see where the points are %% These are last so that they show up on top \foreach \xy in {O, A, B, C, D, E, F, G}{ \node at (\xy) {\tiny\xy}; }

\end{tikzpicture} \end{document}

Peter Grill
  • 223,288
  • I vote up the user @Peter Grill for the nice answers and for the question....I like your drawing. – Sebastiano Mar 15 '21 at 21:44
  • 1
    @Sebastiano: Thanks. I try hard to make my posts so that they might be useful to others (but don't always succeed). – Peter Grill Mar 15 '21 at 21:45
  • The problem is that the perpendicular directions that one gets from calc are perpendicular on the screen but not in 3D. So, in order to get what you want, you cannot rely on the calc methods. In 3d there are always two linearly independent directions that are perpendicular to a given direction. You want the result to be perpendicular in the ambient plane, right? –  Mar 16 '21 at 00:27
  • @user237299: Hmm. perhaps I need to create a separate set of macros with a similar interface to process 3D drawings. – Peter Grill Mar 16 '21 at 05:31
  • You can possibly have one style for all situations but maybe not with the show path construction decoration. –  Mar 16 '21 at 05:51
  • marmot said to me that "Yes. I think that you cannot really solve the problem in the way the question suggests one". See comment here – minhthien_2016 Mar 27 '21 at 05:49
  • Is my answer below useful for you? Is it near what you were looking for? – Daniel N Apr 25 '21 at 14:26
  • @DanielN: Appologies, haven't had time to look into it in detail, but sure looks promising. Hope to get back to this soon. – Peter Grill Apr 26 '21 at 08:05

1 Answers1

3

I start with your question, What is the simplest way to modify either the dimensioning code or how I am using it to work in 3D? I propose to do both changes to achieve your goals:

  • the use of the same code for 2D drawings
  • the dimensioning elements, in a 3D drawing, live in the desired plane.

The initial code for indicate dimensions performs a clockwise 90 degrees turn with respect to the direction of the segment it is supposed to measure. This is fine in 2D drawings, but not in 3D (since the pgf computations work with 2D points, so the angle will always be considered as a 90 degrees on the screen).

The answer. To overcome this, I introduce a pgf-key, angleshift, with the default value 0. When drawing in 3D, it is sufficient to modify its value (for example in the scope command that prepares the call of indicate dimensions). The angleshift performs a counter-clockwise rotation.

enter image description here

Now, concerning the modifications in the code: angleshift is used to modify

  • the turning angle
  • the orientation of the text that prints the argument (to include it in the desired plane)
  • the tips of the arrows (to make them belong to the desired plane like 3 in the example above).

The parallelepiped drawing enter image description here

The code. It is mainly your code with the small modifications I've described. They are marked with % modification <something>.

\documentclass[11pt, margin=1cm]{standalone}
\usepackage{xparse}
\usepackage{tikz}
\usetikzlibrary{3d}

%% --------------------------------------------------------------------- %% https://tex.stackexchange.com/a/468144/4301 \usetikzlibrary{calc, decorations.pathreplacing, arrows.meta} \newif\ifdrawdimlineleft \newif\ifdrawdimlineright

\pgfkeys{/tikz/.cd, % modification angleshift definition angleshift/.store in=\angleshift, angleshift=0 } \tikzset{% dimlabel distance/.initial=5mm, vertical lines extend/.initial=5mm, vertical dim line/.style={gray, thin}, dim arrow line/.style={% % modification tip arrows={latex[slant={-sin(\angleshift)}]-latex[slant={-sin(\angleshift)}]}, thin }, dim label/.style={}, left dimline/.is if=drawdimlineleft, left dimline=true, right dimline/.is if=drawdimlineright, right dimline=true, indicate dimensions/.style={% decorate, decoration={% show path construction, lineto code={% \draw[dim arrow line] % modification arrow line ($(\tikzinputsegmentfirst)!\pgfkeysvalueof{/tikz/dimlabel distance}! {-90+\angleshift}:(\tikzinputsegmentlast) $) -- ($ (\tikzinputsegmentlast)!\pgfkeysvalueof{/tikz/dimlabel distance}! {90+\angleshift}:(\tikzinputsegmentfirst) $) \ifx#1\empty \else % modification text node[midway, fill=white, dim label, sloped, xslant={-sin(\angleshift)}, scale=.8]{#1} \fi; \ifdrawdimlineleft % modification vertical dim line \draw[vertical dim line] (\tikzinputsegmentfirst) -- ($ (\tikzinputsegmentfirst)!\pgfkeysvalueof{/tikz/vertical lines extend}!{-90+\angleshift}:(\tikzinputsegmentlast) $); \fi \ifdrawdimlineright \draw[vertical dim line] (\tikzinputsegmentlast) -- ($ (\tikzinputsegmentlast)!\pgfkeysvalueof{/tikz/vertical lines extend}!{90+\angleshift}:(\tikzinputsegmentfirst) $); \fi } } } }

%% ---------------------------------------------------------------------

\NewDocumentCommand{\DrawCoordinateGrid}{O{} m m m m m m}{% \def\XGridMin{#2} \def\XGridMax{#3} \def\YGridMin{#4} \def\YGridMax{#5} \def\ZGridMin{#6} \def\ZGridMax{#7} % \begin{scope}[canvas is xy plane at z=0, ultra thin, gray!75] \draw [#1] (\XGridMin,\YGridMin) grid (\XGridMax,\YGridMax); \end{scope} \begin{scope}[canvas is yz plane at x=0, ultra thin, gray!75] \draw [#1] (\YGridMin,\ZGridMin) grid (\YGridMax,\ZGridMax); \end{scope} \begin{scope}[canvas is xz plane at y=0, ultra thin, gray!75] \draw [#1] (\XGridMin,\ZGridMin) grid (\XGridMax,\ZGridMax); \end{scope} }

\NewDocumentCommand{\DrawCoordinateAxis}{O{} m m m m m m}{% \def\XAxisMin{#2} \def\XAxisMax{#3} \def\YAxisMin{#4} \def\YAxisMax{#5} \def\ZAxisMin{#6} \def\ZAxisMax{#7} % \begin{scope}[thin, gray, -latex] \draw [#1] (\XAxisMin,0,0) -- (\XAxisMax,0,0) node [below left] {$x$}; \draw [#1] (0,\YAxisMin,0) -- (0,\YAxisMax,0) node [right] {$y$}; \draw [#1] (0,0,\ZAxisMin) -- (0,0,\ZAxisMax) node [above] {$z$}; \end{scope} }%

\newcommand\Width{2.5}% \newcommand\Depth{4.5}% \newcommand*\Height{2.0}% \tikzset{Line Style/.style={draw=brown, ultra thick, join=round}} \begin{document}

\begin{tikzpicture}[ x={(-0.5cm,-0.5cm)}, y={(0.9659cm,-0.25882cm)}, z={(0cm,1cm)}]

\coordinate (O) at (0,0,0); %% ---------------------------------------------- yz plane at x=0 \coordinate (A) at (0,\Width,0); \coordinate (B) at (0,\Width,\Height); \coordinate (C) at (0,0,\Height); %% ---------------------------------------------- yz plane at x=\Depth \coordinate (D) at (\Depth,0,0); \coordinate (E) at (\Depth,\Width,0); \coordinate (F) at (\Depth,\Width,\Height); \coordinate (G) at (\Depth,0,\Height);

\DrawCoordinateGrid{0}{6}{0}{4}{0}{3} \DrawCoordinateAxis[thick, black]{0}{6.5}{0}{4.5}{0}{3.5}

%% ------------------------------------ Draw the rectangular prism \draw [Line Style] (O) -- (A) -- (B) -- (C) -- cycle;% yz plane at x=0 \draw [Line Style] (D) -- (E) -- (F) -- (G) -- cycle;% yz plane at x=\Depth \draw [Line Style] (O) -- (D) -- (G) -- (C) -- cycle;% xz plane at y=0 \draw [Line Style] (A) -- (E) -- (F) -- (B) -- cycle;% xz plane at y=\Width

% modification angleshift \path [angleshift=30, blue, indicate dimensions={$2$}, inner sep=2pt] (C) -- (G);

%% Following is for debugging purposes so you can see where the points are %% These are last so that they show up on top \foreach \xy in {O, A, B, C, D, E, F, G}{ \node at (\xy) {\tiny\xy}; }
\end{tikzpicture} \end{document}

Daniel N
  • 5,687