好的,我的代码相当长。我尝试尽可能地缩短它,但它非常复杂,而且我仍然不擅长使用 pgfkeys 或 xparse。
问题:如果tikzpicture
旋转了非 90° 的倍数,线条就会偏离。如果tikzpicture
旋转了 90° 的倍数,线条就会正常。
为什么会这样?我该如何预防?
%%%%%%%%%%%%%% Common file
\documentclass{standalone}
\usepackage{etoolbox}
%Declare Macros for each floor to be defined by the building files. They are of the form: \ESKFPfloorEG, \ESKFPernEG, \ESKFPlabelsEG
\newcommand{\ESKFPcreatefloor}[1]{%
\expandafter\def\csname ESKFPfloor#1\endcsname{nothing defined}
}
\ESKFPcreatefloor{EG}
% Define Macros for the current floor to be used by the room files.
\newcommand{\ESKFPcurrentfloorcommand}{none}
\newcommand{\ESKFPsetfloor}[1]{%
\renewcommand{\ESKFPcurrentfloorcommand}{\csname ESKFPfloor#1\endcsname}
}
\newcommand{\ESKFPcurrentroom}{0}
\newcommand{\ESKFPsetroom}[1]{
\renewcommand{\ESKFPcurrentroom}{#1}
}
\newcommand{\ESKFPcurrentadditionaltext}{}
\newcommand{\ESKFPdoorata}{0}
\newcommand{\ESKFPdooratb}{0}
\newcommand{\ESKFPdooratc}{0}
\newcommand{\ESKFPdooratd}{0}
\newcommand{\ESKFPdoorapos}{\ifnumequal{\ESKFPdoorata}{1}{0.75}{0.25}}
\newcommand{\ESKFPdoorbpos}{\ifnumequal{\ESKFPdooratb}{1}{0.75}{0.25}}
\newcommand{\ESKFPdoorcpos}{\ifnumequal{\ESKFPdooratc}{1}{0.75}{0.25}}
\newcommand{\ESKFPdoordpos}{\ifnumequal{\ESKFPdooratd}{1}{0.75}{0.25}}
\newcommand{\ESKFPmisceins}{1}
\newcommand{\ESKFPmisczwei}{2}
\newcommand{\ESKFPmiscnull}{0}
\newcommand{\ESKFPcurrentexitdoorat}{0}
\newcounter{ESKFPcurrentdoor}
\setcounter{ESKFPcurrentdoor}{0}
\makeatletter
\long\def\ESKFPifnodedefined#1#2#3{%
\@ifundefined{pgf@sh@ns@#1}{#3}{#2}%
}
\makeatother
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{shapes.geometric}
\tikzset{%
every node/.style={outer sep=0pt},
ESKFPdoorcommonstyle/.style={%
transform shape,
text width=,
circular sector,
sloped,
allow upside down,
anchor=sector center,
circular sector angle=90,
draw,
minimum size=200pt,
},
ESKFPdoorstyle1/.style={%
pos=#1,
fill=yellow,
ESKFPdoorcommonstyle,
yscale=-1,
rotate=45,
},ESKFPdoorstyle1/.default=0.75,
ESKFPdoorstyle2/.style={%
pos=#1,
fill=blue,
ESKFPdoorcommonstyle,
% xscale=-1,
rotate=225
},ESKFPdoorstyle2/.default=0.25,
door1/.style={ESKFPdoorstyle1},
door2/.style={ESKFPdoorstyle2},
ESKFPnothighlightedstyle/.style={fill=white},
ESKFPhighlightedstyle/.style={fill=red},
ESKFPexitdoorstyle/.style={%
alias=ESKFPcurrentexitdoor
},
exitdoor/.style={ESKFPexitdoorstyle},
ESKFProomstyle/.style={ultra thick,fill=white,ESKFPcommonroomstyle},
ESKFPemptyroomstyle/.style={fill=none,ESKFPcommonroomstyle},
ESKFPcommonroomstyle/.style={draw},
ESKFPstairsstyle/.style={ultra thick,fill=green!80!black,ESKFPcommonroomstyle},
ESKFPelsestyle/.style={ESKFPcommonroomstyle},
stairs/.style={ESKFPelsestyle/.style={ESKFPstairsstyle}},
ESKFProomtextstyle/.style={inner sep=0pt,outer sep=0pt,font={\bfseries}},
ESKFPbackgroundstyle/.style={fill=green!50!white},
set door at 1/.code={\renewcommand{\ESKFPdoorata}{#1}},
set door at 2/.code={\renewcommand{\ESKFPdooratb}{#1}},
set door at 3/.code={\renewcommand{\ESKFPdooratc}{#1}},
set door at 4/.code={\renewcommand{\ESKFPdooratd}{#1}},
set door at 1/.default=1,
set door at 2/.default=1,
set door at 3/.default=1,
set door at 4/.default=1,
set exit door/.code={\renewcommand{\ESKFPcurrentexitdoorat}{#1}},
set exit door/.default=0,
set door 1 pos/.code={\renewcommand{\ESKFPdoorapos}{#1}},
set door 2 pos/.code={\renewcommand{\ESKFPdoorbpos}{#1}},
set door 3 pos/.code={\renewcommand{\ESKFPdoorcpos}{#1}},
set door 4 pos/.code={\renewcommand{\ESKFPdoordpos}{#1}},
set door 1 pos/.default=0.5,
set door 2 pos/.default=0.5,
set door 3 pos/.default=0.5,
set door 4 pos/.default=0.5,
}
%Layers of the Graphic
\pgfdeclarelayer{background}
\pgfdeclarelayer{rooms}
\pgfdeclarelayer{text}
\pgfdeclarelayer{test}
\pgfsetlayers{background,main,rooms,text,test}
%Highlightingstyle
\newcommand{\ESKFPdoifhighlighted}{%
\tikzset{ESKFPcurrentroomstyle/.style={ESKFPhighlightedstyle}}
}
\newcommand{\ESKFPdoifnothighlighted}{%
\tikzset{ESKFPcurrentroomstyle/.style={ESKFPnothighlightedstyle}}
}
\newcommand{\ESKFPcreateroom}[5][]{%#1=local tikz options,#2=name,#3=room number,#4=path with doors,#5=text coordinate
%The path has to be defined counter clockwise. Otherwise, the doors would be outside the room.
%To force the doors to be outside the room, use the option "shape border rotate=90" or "shape border rotate=270"
%If #5 is empty, than it is assumed, that the room has corners at the coordinates ESKFPc#2lu,ESKFPc#2ru, ESKFPc#2lo and ESKFPc#2ro and the text will be positioned at the barycentre of these 4 points.
%Find out, if room has to be highlighted or not
\ifdefmacro{#2}%
{\ifdefstrequal{\ESKFPcurrentroom}{#2}{\ESKFPdoifhighlighted}{\ESKFPdoifnothighlighted}}
{\ifdefstring{\ESKFPcurrentroom}{#2}{\ESKFPdoifhighlighted}{\ESKFPdoifnothighlighted}}
%Apply given options in a local scope
\begin{scope}[#1]
\ifstrempty{#4}
{%
\setcounter{ESKFPcurrentdoor}{0}
\draw[ESKFProomstyle,ESKFPcurrentroomstyle,%
to path=%
{% the following adds the doors, if specified. This definition resides in the macros \ESKFPdoorata, \ESKFPdooratb, \ESKFPdooratc, \ESKFPdooratd and can have the values 1,2 or anything else
\pgfextra{\stepcounter{ESKFPcurrentdoor}}
\ifcsequal{ESKFPdoorat\alph{ESKFPcurrentdoor}}{ESKFPmisceins}
{--node(ESKFProom#2door\the\value{ESKFPcurrentdoor})[door1,pos=\csname ESKFPdoor\alph{ESKFPcurrentdoor}pos\endcsname]{}(\tikztotarget)\tikztonodes}
{%
\ifcsequal{ESKFPdoorat\alph{ESKFPcurrentdoor}}{ESKFPmisczwei}
{--node(ESKFProom#2door\the\value{ESKFPcurrentdoor})[door2,pos=\csname ESKFPdoor\alph{ESKFPcurrentdoor}pos\endcsname]{}(\tikztotarget)\tikztonodes}
{--(\tikztotarget)\tikztonodes}
}
}
]%
(ESKFPc#2lu)to(ESKFPc#2ru)to(ESKFPc#2ro)to(ESKFPc#2lo)to(ESKFPc#2lu)--cycle;
\ifnumgreater{\ESKFPcurrentexitdoorat}{0}
{\coordinate(ESKFProom#2exitdoor) at ($(ESKFProom#2door\ESKFPcurrentexitdoorat.sector center)!0.5!(ESKFProom#2door\ESKFPcurrentexitdoorat.arc start)$);}%Exit door is defined, that door will be used. It is assumed, that this door is indeed defined.
{%
\renewcommand{\ESKFPcurrentadditionaltext}{Exit door guessed}
\ESKFPifnodedefined{ESKFProom#2door1}{\coordinate(ESKFProom#2exitdoor) at ($(ESKFProom#2door1.sector center)!0.5!(ESKFProom#2door1.arc start)$);}
{
\ESKFPifnodedefined{ESKFProom#2door2}{\coordinate(ESKFProom#2exitdoor) at ($(ESKFProom#2door2.sector center)!0.5!(ESKFProom#2door2.arc start)$);}
{
\ESKFPifnodedefined{ESKFProom#2door3}{\coordinate(ESKFProom#2exitdoor) at ($(ESKFProom#2door3.sector center)!0.5!(ESKFProom#2door3.arc start)$);}
{
\ESKFPifnodedefined{ESKFProom#2door4}{\coordinate(ESKFProom#2exitdoor) at ($(ESKFProom#2door4.sector center)!0.5!(ESKFProom#2door4.arc start)$);}
{
\coordinate(ESKFProom#2exitdoor) at ($(ESKFPc#2lu)!0.5!(ESKFPc#2ro)$);
\renewcommand{\ESKFPcurrentadditionaltext}{No exit door}
}
}
}
}
}
}
{%Path is supplied, especially a door with the style ESKFPexitdoorstyle must be given
%Also the coordinates ESKFPc[ROOMNAME]lu, ESKFPc[ROOMNAME]ru, ESKFPc[ROOMNAME]lo and ESKFPc[ROOMNAME]ro should be defined
\draw[ESKFProomstyle,ESKFPcurrentroomstyle]{#4}--cycle;
\coordinate(ESKFProom#2exitdoor) at (ESKFPcurrentexitdoor);
}
\ifstrempty{#5}
{\coordinate(ESKFProom#2textcenter) at ($0.25*(ESKFPc#2lu)+0.25*(ESKFPc#2ru)+0.25*(ESKFPc#2lo)+0.25*(ESKFPc#2ro)$);}
{\coordinate(ESKFProom#2textcenter) at #5;}
%Text (room number is set), the coordinate ESKFProom[ROOMNAME]textcenter is calculated before. This is #5 or is calculated from ESKFPc[ROOMNAME]lu, ESKFPc[ROOMNAME]ru, ESKFPc[ROOMNAME]lo and ESKFPc[ROOMNAME]ro if not given
\begin{pgfonlayer}{text}
\node[ESKFProomtextstyle](ESKFProom#2text) at (ESKFProom#2textcenter) {#3\ESKFPcurrentadditionaltext};
\end{pgfonlayer}
\end{scope}
}
\newcommand{\ESKFPcreateelse}[5][]{%#1=local tikz options,#2=name,#3=room number,#4=path with doors,#5=text coordinate
%The path has to be defined counter clockwise. Otherwise, the doors would be outside the room.
%To force the doors to be outside the room, use the option "shape border rotate=90" or "shape border rotate=270"
%If #5 is empty, than it is assumed, that the room has corners at the coordinates ESKFPc#2lu,ESKFPc#2ru, ESKFPc#2lo and ESKFPc#2ro and the text will be positioned at the barycentre of these 4 points.
%Apply given options in a local scope
\begin{scope}[#1]
%Test if path is given
\ifstrempty{#4}
{%If Path is NOT given
\setcounter{ESKFPcurrentdoor}{0}
\draw[ESKFPelsestyle,%
to path=%
{% the following adds the doors, if specified. This definition resides in the macros \ESKFPdoorata, \ESKFPdooratb, \ESKFPdooratc, \ESKFPdooratd and can have the values 1,2 or anything else
\pgfextra{\stepcounter{ESKFPcurrentdoor}}
\ifcsequal{ESKFPdoorat\alph{ESKFPcurrentdoor}}{ESKFPmisceins}
{--node(ESKFPstairs#2door\the\value{ESKFPcurrentdoor})[door1,pos=\csname ESKFPdoor\alph{ESKFPcurrentdoor}pos\endcsname]{}(\tikztotarget)\tikztonodes}
{%
\ifcsequal{ESKFPdoorat\alph{ESKFPcurrentdoor}}{ESKFPmisczwei}
{--node(ESKFPstairs#2door\the\value{ESKFPcurrentdoor})[door2,pos=\csname ESKFPdoor\alph{ESKFPcurrentdoor}pos\endcsname]{}(\tikztotarget)\tikztonodes}
{--(\tikztotarget)\tikztonodes}
}
}
]%
(ESKFPc#2lu)to(ESKFPc#2ru)to(ESKFPc#2ro)to(ESKFPc#2lo)to(ESKFPc#2lu)--cycle;
}
{%If Path IS given. A door with the style ESKFPexitdoorstyle must be given
%Also the coordinates ESKFPc[ROOMNAME]lu, ESKFPc[ROOMNAME]ru, ESKFPc[ROOMNAME]lo and ESKFPc[ROOMNAME]ro should be defined
\draw[ESKFPelsestyle]{#4}--cycle;
}
\ifstrempty{#5}%Test if coordinate for Text is given
{\coordinate(ESKFPstairs#2textcenter) at ($0.25*(ESKFPc#2lu)+0.25*(ESKFPc#2ru)+0.25*(ESKFPc#2lo)+0.25*(ESKFPc#2ro)$);}%If textcoordinate is NOT given
{\coordinate(ESKFPstairs#2textcenter) at #5;}%If textcoordinate is given
%Text (room number is set), the coordinate ESKFProom[ROOMNAME]textcenter is calculated before. This is #5 or is calculated from ESKFPc[ROOMNAME]lu, ESKFPc[ROOMNAME]ru, ESKFPc[ROOMNAME]lo and ESKFPc[ROOMNAME]ro if not given
\begin{pgfonlayer}{text}
\node[ESKFProomtextstyle](ESKFPstairs#2text) at (ESKFPstairs#2textcenter) {#3\ESKFPcurrentadditionaltext};
\end{pgfonlayer}
\end{scope}
}
%%%%%% Floor file
\newcommand{\ESKFPfloorGSEG}{%
\begin{scope}[scale=0.1]
%A101
\coordinate(ESKFPcA101lu) at (0,0);
\coordinate(ESKFPcA101ru) at (136,0);
\coordinate(ESKFPcA101ro) at (136,127);
\coordinate(ESKFPcA101lo) at (0,127);
%A102
\coordinate(ESKFPcA102lu) at (ESKFPcA101lo);
\coordinate(ESKFPcA102ru) at (ESKFPcA101ro);
\coordinate(ESKFPcA102lo) at ($(ESKFPcA101lo)+(0,90)$);
\coordinate(ESKFPcA102ro) at ($(ESKFPcA101ro)+(0,90)$);
%A103
\coordinate(ESKFPcA103lu) at (ESKFPcA102lo);
\coordinate(ESKFPcA103ru) at (ESKFPcA102ro);
\coordinate(ESKFPcA103lo) at ($(ESKFPcA102lo)+(0,90)$);
\coordinate(ESKFPcA103ro) at ($(ESKFPcA102ro)+(0,90)$);
%A104
\coordinate(ESKFPcA104lu) at (ESKFPcA103lo);
\coordinate(ESKFPcA104ru) at (ESKFPcA103ro);
\coordinate(ESKFPcA104lo) at ($(ESKFPcA103lo)+(0,120)$);
\coordinate(ESKFPcA104ro) at ($(ESKFPcA103ro)+(0,120)$);
%STA2CAFE
\coordinate(ESKFPcSTA2CAFElu) at ($(ESKFPcA104ro)+(0,-30)$);
\coordinate(ESKFPcSTA2CAFElo) at ($(ESKFPcA104ro)+(0,30)$);
\coordinate(ESKFPcSTA2CAFEru) at ($(ESKFPcSTA2CAFElu)+(120,0)$);
\coordinate(ESKFPcSTA2CAFEro) at ($(ESKFPcSTA2CAFElo)+(120,0)$);
%Rooms
\begin{pgfonlayer}{rooms}
%Left side
\ESKFPcreateroom[set door at 2,set exit door=2]{A101}{A101}{}{}
\ESKFPcreateroom[set door at 1,set door at 2=2,set exit door=2]{A102}{A102}{}{}
\ESKFPcreateroom[set door at 1,set door at 2,set exit door=2]{A103}{A103}{}{}
\ESKFPcreateroom[set door at 1,set door at 2=2,set exit door=2]{A104}{A104}{}{}
%Top side
\ESKFPcreateelse[stairs]{STA2CAFE}{To Cafeteria}{}{}
%
\ESKFPcreateelse{EMPTYA2CAFE}{}{(ESKFPcSTA2CAFElu)coordinate(ESKFPcEMPTYA2CAFElo)--++(0,-30)coordinate(ESKFPcEMPTYA2CAFElu)--node(ESKFPdoorEMPTYA2CAFEleft)[door2]{}node(ESKFPdoorEMPTYA2CAFEright)[door1]{}++(120,0)coordinate(ESKFPcEMPTYA2CAFEru)--++(0,30)coordinate(ESKFPcEMPTYA2CAFEro)}{(0,0)}%No Text to be set
%
\ESKFPcreateelse[stairs]{STAEG2AOGINEARCAFE}{Stairs}{($(ESKFPcSTA2CAFEro)+(0,-30)$)coordinate(ESKFPcSTAEG2AOGINEARCAFElo)--++(0,-120)coordinate(ESKFPcSTAEG2AOGINEARCAFElu)--node(ESKFPdoorSTAEG2AOGINEARCAFEleft)[door2]{}node(ESKFPdoorSTAEG2AOGINEARCAFEright)[door1]{}++(80,0)coordinate(ESKFPcSTAEG2AOGINEARCAFEru)--++(0,120)coordinate(ESKFPcSTAEG2AOGINEARCAFEro)--node(ESKFPdoorSTAEG2AOGINEARCAFEout)[door1,pos=0.25,rotate=180]{}(ESKFPcSTAEG2AOGINEARCAFElo)}{}
\end{pgfonlayer}
\end{scope}
%Backgroundlayer
\begin{pgfonlayer}{background}
\fill[ESKFPbackgroundstyle](ESKFPcA101lu)rectangle(ESKFPcSTAEG2AOGINEARCAFEro);
\end{pgfonlayer}
}
\let\ESKFPfloorEG\ESKFPfloorGSEG
%%%%%%%%%%%Actual Room file
\ESKFPsetfloor{EG}
\ESKFPsetroom{A102}
\begin{document}
\begin{tikzpicture}[mystyle/.style={ESKFPcurrentfloorstyle},rotate=45]
\ESKFPcurrentfloorcommand
\begin{pgfonlayer}{test}
\draw[red](ESKFProomA101exitdoor)to[in=0,out=0](ESKFProomA102exitdoor);
\end{pgfonlayer}
\end{tikzpicture}
\end{document}
答案1
问题似乎是执行计算时的浮点舍入问题 --- 如果将整个图片作为一个单元旋转,而不是单独旋转每张图片,则似乎可以很好地对齐。将第 294 行更改为以下内容:
\rotatebox{13}{\begin{tikzpicture}[mystyle/.style={ESKFPcurrentfloorstyle}]
并在第 299 行末尾关闭该组:
\end{tikzpicture}}
并且您可以任意旋转它并且仍能使物体排列整齐。