提问于:
浏览数:
1769
最近在写一个画立方体的包,基础的形状和锚点设置都已经写好,然后想实现多个立方体之间的对齐,因为使用 positioning 包只能实现二维图形的对齐,三维图形还需要额外的偏移,目前针对这个功能写了个 demo,但是对写的代码不是很满意,总觉得有更好的写法
`dnnplot.sty`
```tex
\ProvidesPackage{dnnplot}
\RequirePackage{pdftexcmds}
\usetikzlibrary{math, calc}
\makeatletter
\let\pdfstrcmp=\pdf@strcmp
\pgfkeys{/block plot/.cd,
width/.store in=\block@width, width=1cm,
height/.store in=\block@height, height=1cm,
channel/.store in=\block@channel, channel=1cm,
scale/.store in=\block@scale, scale=.1,
pre/.store in=\block@pre, pre=,
align/.store in=\block@align, align=,
}
\pgfdeclareshape{base block}
{
\inheritsavedanchors[from=rectangle] % this is nearly a rectangle
\inheritanchorborder[from=rectangle]
\inheritanchor[from=rectangle]{north}
\inheritanchor[from=rectangle]{north west}
\inheritanchor[from=rectangle]{north east}
\inheritanchor[from=rectangle]{center}
\inheritanchor[from=rectangle]{west}
\inheritanchor[from=rectangle]{east}
\inheritanchor[from=rectangle]{mid}
\inheritanchor[from=rectangle]{mid west}
\inheritanchor[from=rectangle]{mid east}
\inheritanchor[from=rectangle]{base}
\inheritanchor[from=rectangle]{base west}
\inheritanchor[from=rectangle]{base east}
\inheritanchor[from=rectangle]{south}
\inheritanchor[from=rectangle]{south west}
\inheritanchor[from=rectangle]{south east}
\saveddimen{\offset}{\pgfmathsetlength\pgf@x{\block@offset}}
\anchor{back north west}{
\southwest \pgf@xa=\pgf@x
\northeast
\pgfmathsetlength\pgf@x{\pgf@xa + \offset}
\pgfmathsetlength\pgf@y{\pgf@y + \offset}
}
\anchor{back north east}{
\northeast
\pgfmathsetlength\pgf@x{\pgf@x + \offset}
\pgfmathsetlength\pgf@y{\pgf@y + \offset}
}
\anchor{back south east}{
\northeast \pgf@xa=\pgf@x
\southwest
\pgfmathsetlength\pgf@x{\pgf@xa + \offset}
\pgfmathsetlength\pgf@y{\pgf@y + \offset}
}
\backgroundpath{
% store lower right in xa/ya and upper right in xb/yb
\southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
\northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
\def\block@offset{\pgfqpoint{\offset}{\offset}}
% main path
\pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@ya}}
\pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@ya}}
\pgfpathlineto{\pgfpointadd{\pgfpoint{\pgf@xb}{\pgf@ya}}{\block@offset}}
\pgfpathlineto{\pgfpointadd{\pgfpoint{\pgf@xb}{\pgf@yb}}{\block@offset}}
\pgfpathlineto{\pgfpointadd{\pgfpoint{\pgf@xa}{\pgf@yb}}{\block@offset}}
\pgfpathlineto{\pgfqpoint{\pgf@xa}{\pgf@yb}}
\pgfpathclose
% other edge
\pgfpathmoveto{\pgfqpoint{\pgf@xb}{\pgf@ya}}
\pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@yb}}
\pgfpathlineto{\pgfqpoint{\pgf@xa}{\pgf@yb}}
\pgfpathmoveto{\pgfqpoint{\pgf@xb}{\pgf@yb}}
\pgfpathlineto{\pgfpointadd{\pgfpoint{\pgf@xb}{\pgf@yb}}{\block@offset}}
}
}
\tikzset{
block/.code={\tikzset{/block plot/.cd, #1}
% scale
\pgfmathsetlengthmacro\scaled@width{\block@scale*\block@width}
\pgfmathsetlengthmacro\scaled@height{\block@scale*\block@height}
\pgfmathsetlengthmacro\scaled@channel{\block@scale*\block@channel}
\pgfmathsetlengthmacro\block@offset{sqrt(2)/4*\scaled@width}
% shift
\ifx\block@pre\pgfutil@empty
\else\ifx\block@align\pgfutil@empty
\else
\tikzmath{
coordinate \pre@offset, \cur@offset, \block@shift;
\pre@offset = (\block@pre.back north west) - (\block@pre.north west);
\cur@offset = (\block@offset, \block@offset);
\block@shift = (0, 0);
if \pdf@strcmp{\block@align}{center}==0 then{
\block@shift = ($0.5*(\pre@offset) - 0.5*(\cur@offset)$);
} else{};
}
\tikzset{shift={(\block@shift)}}
\fi\fi
% general
\tikzset{
base block, draw, opacity=.2,
minimum height=\scaled@height,
minimum width=\scaled@channel,
}
}
}
\newcommand{\blocklabel}[3]{
\ifnum\pdf@strcmp{#2}{back east}=0
\path (#1.back south east) -- node[right] {#3} (#1.back north east);
\fi
}
\makeatother
```
`tikz.tex`
```tex
\documentclass[tikz, border=1cm]{standalone}
\usepackage{xcolor}
\usetikzlibrary{calc, math, positioning}
\usepackage{dnnplot}
\begin{document}
\begin{tikzpicture}
\node[fill=red, block={scale=1.5, width=50mm, height=50mm, channel=20mm}](a){};
\fill[blue] (a.center) circle (1pt);
\fill[fill=red] (a.north east) node[anchor=west] {north east} circle (1pt);
\fill[fill=red] (a.north west) circle (1pt);
\fill[fill=red] (a.south east) circle (1pt);
\fill[fill=red] (a.south west) circle (1pt);
\fill[fill=red] (a.back north east) circle (1pt);
\fill[fill=red] (a.back north west) circle (1pt);
\fill[fill=red] (a.back south east) circle (1pt);
\blocklabel{a}{back east}{back east label}
\node[fill=green, right=4cm of a, block={scale=1.5, width=10mm, height=10mm,
channel=10mm}](b){};
\node[fill=green, right=4cm of a, block={scale=1.5, width=10mm, height=10mm,
channel=10mm, pre=a, align=center}](c){};
\draw (a.center) --node[above]{shift in x-axis} (b.center);
\draw[draw=red, ->] (b.center) -- node[right]{shift in z-axis}(c.center);
\end{tikzpicture}
\end{document}
```
运行结果如下图,经过两次偏移后的立方体实现了 x 轴上的中心对齐:
![](https://pics.latexstudio.net/data/images/201911/00ec5d11765b2db.png)