0
\usepackage{tikz}
\usetikzlibrary{patterns, arrows}

\begin{document}
\begin{tikzpicture}
  % Set the width and height of the rectangles
  \pgfmathsetmacro{\rectHeight}{1} % Height of all rectangles
  \pgfmathsetmacro{\totalWidth}{8} % Total width of the bottom rectangle
  \pgfmathsetmacro{\toprecPercentage}{0.8} % Width of the top rectangle as a percentage of the bottom rectangle
  
  % Define the number of rectangles
  \def\numRectangles{9}
  
  % Define an array of descriptions for the arrows
  \def\descriptions{{"Desc 0", "Desc 1", "Desc 2", "Desc 3", "Desc 4", "Desc 5", "Desc 6", "Desc 7", "Desc 8"}}
  
  % Define an array of segment counts
  \def\segmentCounts{{3, 9, 1, 1, 1, 1, 1, 1, 1}} % Number of segments for each rectangle

  % Define segmentFillPercentages for each rectangle
  \def\segmentFillPercentages{
    {70, 30, 50},  % Customize these percentages for rectangle 0
    {20, 40, 60, 80, 10, 30, 50, 70, 90},  % Customize these percentages for rectangle 1
    {10},         % Customize these percentages for rectangle 2
    {40},         % Customize these percentages for rectangle 3
    {20},         % Customize these percentages for rectangle 4
    {10},         % Customize these percentages for rectangle 5
    {40},         % Customize these percentages for rectangle 6
    {20},         % Customize these percentages for rectangle 7
    {100}         % Customize these percentages for rectangle 8
  }
  
  % Calculate the width of the top rectangle
  \pgfmathsetmacro{\toprecWidth}{\toprecPercentage*\totalWidth}
  
  % Draw the rectangles and fill them with different patterns based on patterns based on fill percentages and segment counts
  \foreach \i in {0,...,\numexpr\numRectangles-1} {
    \pgfmathsetmacro{\currentWidth}{\totalWidth - (\i/\numRectangles)*\toprecWidth}
    \pgfmathsetmacro{\startingPoint}{0.5*(\totalWidth-\currentWidth)} % Calculate the starting point
    
    % Draw segments within each rectangle
    \pgfmathsetmacro{\segmentCount}{\segmentCounts[\i]}
    \pgfmathsetmacro{\segmentWidth}{\currentWidth/\segmentCount}
    
    % Get the fill percentages for the current rectangle
    \pgfmathparse{\segmentFillPercentages[\i]}
    \let\currentSegmentFillPercentages\pgfmathresult
    
    % Draw and fill each segment with different fill percentages
    \foreach \j in {1,...,\segmentCount} {
      \pgfmathparse{\currentSegmentFillPercentages[\j-1]}
      \let\segmentFillPercentage\pgfmathresult
      \pgfmathsetmacro{\segmentStartingPoint}{\startingPoint + (\j-1)*\segmentWidth}
      
      \draw (\segmentStartingPoint,\rectHeight*\i) rectangle (\segmentStartingPoint + \segmentWidth,\rectHeight*\i+\rectHeight);
      \fill[pattern=grid,pattern color=redpattern=grid,pattern color=red] (\segmentStartingPoint,\rectHeight*\i) rectangle (\segmentStartingPoint + \segmentWidth,\rectHeight*\i+(\segmentFillPercentage/100)*\rectHeight);
    }
    
    % Add arrows and descriptions within the loop
    \draw[->] (\startingPoint + \currentWidth + 0.2, \rectHeight*\i + 0.5*\rectHeight) -- (\startingPoint + \currentWidth + 1.0, \rectHeight*\i + 0.5*\rectHeight) node[right,font=\footnotesize,text width=2cm] {\pgfmathparse{\descriptions[\i]}\pgfmathresult};
  }
\end{tikzpicture}
\end{document}

Trying different methods to get the fillcolor for the segments but unable to get it correct. Any help on solving this issue is much appericiated.

1
  • 2
    Welcome. Surely pattern=grid,pattern color=redpattern=grid,pattern color=red is a typo/copy-paste error. You've got some troubles accessing the arrays because of the number of braces needed for the <array>[<index>] syntax to work. Are you set on using these arrays? I always find them cumbersome to use. Commented Sep 28, 2023 at 19:01

2 Answers 2

1

The \fill in the \j loop has a bad option (missing comma):

\fill[pattern=grid,pattern color=redpattern=grid,pattern color=red] …;

Let's fix that:

\fill[pattern=grid,pattern color=red] …;

The same path has a calculation using parentheses inside a coordinate. You will have to protect those from the TikZ parser with another set of braces:

 (\segmentStartingPoint + \segmentWidth,
   {\rectHeight*\i+(\segmentFillPercentage/100)*\rectHeight})

Though, the parentheses aren't necessary and you can use relative coordinates here:

\fill[pattern=grid,pattern color=red]
  (\segmentStartingPoint, \rectHeight*\i)
  rectangle +(\segmentWidth, \segmentFillPercentage/100*\rectHeight);

On to the arrays, the array \segmentFillPercentages needs another set of braces around it as the manual states:

{x}  (array operators)

[…] The ⟨array specification⟩ consists of comma separated elements, for example, {1, 2, 3, 4, 5}. […] In addition, an element of an array can be an array itself, allowing multiple dimension arrays to be simulated: {1, {2,3}, {4,5}, 6}. When storing an array in a macro, do not forget the surrounding braces: \def\myarray{{1,2,3}} not \def\myarray{1,2,3}.

Here, I've opted to insert these braces in the PGFMath formula instead:

\pgfmathsetmacro\currentSegmentFillPercentages{{\segmentFillPercentages}[\i]}

Unfortunately, at this point the list inside the list will be converted into an internal representation for an array which means the later access on \currentSegmentFillPercentages won't work anymore. You could use two array access operators instead:

\pgfmathsetmacro\segmentFillPercentage{{\segmentFillPercentages}[\i][\j]}

or

\pgfmathArraysetmacro{\segmentFillPercentage}{\currentSegmentFillPercentages}{\j}

so that PGFMath doesn't have to do the [\i] access multiple times.

However both these approaches don't work correctly for rectangles with only one segment because the internal representation is not compatible anymore.

For this we just take the \currentSegmentFillPercentages directly.

(I've also convertedthe \j loop to a 0, …, \segmentCount-1 loop.)

Code

\documentclass[tikz]{standalone}
\usepackage{tikz}
\usetikzlibrary{patterns, arrows}
\makeatletter
\newcommand*\pgfmathArraysetmacro[3]{%
  \pgfmathtruncatemacro\@temp{#3}%
  \expandafter\pgfmatharray@#2{\@temp}%
  \let#1\pgfmathresult}
\makeatother
\begin{document}
\begin{tikzpicture}
% Set the width and height of the rectangles
\pgfmathsetmacro{\rectHeight}{1} % Height of all rectangles
\pgfmathsetmacro{\totalWidth}{8} % Total width of the bottom rectangle
\pgfmathsetmacro{\toprecPercentage}{0.8} % Width of the top rectangle as a percentage of the bottom rectangle

% Define the number of rectangles
\def\numRectangles{9}

% Define an array of descriptions for the arrows
\def\descriptions{{"Desc 0", "Desc 1", "Desc 2", "Desc 3", "Desc 4", "Desc 5", "Desc 6", "Desc 7", "Desc 8"}}

% Define an array of segment counts
\def\segmentCounts{{3, 9, 1, 1, 1, 1, 1, 1, 1}} % Number of segments for each rectangle

% Define segmentFillPercentages for each rectangle
\def\segmentFillPercentages{
  {70, 30, 50},  % Customize these percentages for rectangle 0
  {20, 40, 60, 80, 10, 30, 50, 70, 90},  % Customize these percentages for rectangle 1
  {10},         % Customize these percentages for rectangle 2
  {40},         % Customize these percentages for rectangle 3
  {20},         % Customize these percentages for rectangle 4
  {10},         % Customize these percentages for rectangle 5
  {40},         % Customize these percentages for rectangle 6
  {20},         % Customize these percentages for rectangle 7
  {100}         % Customize these percentages for rectangle 8
}

% Calculate the width of the top rectangle
\pgfmathsetmacro{\toprecWidth}{\toprecPercentage*\totalWidth}

% Draw the rectangles and fill them with different patterns based on patterns based on fill percentages and segment counts
\foreach \i in {0,...,\inteval{\numRectangles-1}} {
  \pgfmathsetmacro{\currentWidth}{\totalWidth - (\i/\numRectangles)*\toprecWidth}
  \pgfmathsetmacro{\startingPoint}{0.5*(\totalWidth-\currentWidth)} % Calculate the starting point
  
  % Draw segments within each rectangle
  \pgfmathsetmacro{\segmentCount}{\segmentCounts[\i]}
  \pgfmathsetmacro{\segmentWidth}{\currentWidth/\segmentCount}
  
  % Get the fill percentages for the current rectangle
  \pgfmathsetmacro\currentSegmentFillPercentages{{\segmentFillPercentages}[\i]}
  \typeout{\currentSegmentFillPercentages}
  % Draw and fill each segment with different fill percentages
  \foreach \j in {0,...,\inteval{\segmentCount-1}} {
    \ifnum\segmentCount=1 % need to check special case of just one segment
      \let\segmentFillPercentage\currentSegmentFillPercentages
    \else
      \pgfmathsetmacro\segmentFillPercentage{{\segmentFillPercentages}[\i][\j]}  % Option 1
%      \pgfmathArraysetmacro{\segmentFillPercentage}{\currentSegmentFillPercentages}{\j} % Option 2
    \fi

    \pgfmathsetmacro{\segmentStartingPoint}{\startingPoint + \j*\segmentWidth}

    \draw (\segmentStartingPoint, \rectHeight*\i) rectangle +(\segmentWidth, \rectHeight);
    \fill[pattern=grid,pattern color=red] (\segmentStartingPoint,\rectHeight*\i) rectangle + (\segmentWidth, \segmentFillPercentage/100*\rectHeight);
  }
    
  % Add arrows and descriptions within the loop
  \draw[->] (\startingPoint + \currentWidth + 0.2, \rectHeight*\i + 0.5*\rectHeight) -- +(.8,0)
    node[right, font=\footnotesize, text width=2cm] {\pgfmathprint{\descriptions[\i]}};
}
\end{tikzpicture}
\end{document}

Output

enter image description here

3
  • thanks for the response, with this approach the segments in desc 0 and desc 1 are only getting filled. only the array of segments not the standalone segments.
    – IamK
    Commented Sep 29, 2023 at 3:22
  • @IamK You're right. I didn't even notice the difference when posting the other answers. The internal array representation os not consistent when the internal array only consists of one element. I've updated the answer that deals with this now properly. Commented Sep 29, 2023 at 8:52
  • thank you @qrrbrbirlbel
    – IamK
    Commented Oct 1, 2023 at 8:38
1

Here's another approach on drawing this.

You need three macros:

  • \botWidth describes the width of the bottom rectangle,
  • \topWidth describes the width of the top rectangle and
  • \LIST containts the list of descriptions and lists of percentages.

The macro \countList is used to count the elements of \LIST, we can't – unlike with \Percs later – use the dim function since \LIST not only contains math.

The rest of the diagram is one big \foreach that loops through the \LIST. With the evaluate key, the number of percentages will be evaluated and the \Widthof the rectangle will be calculated from \botWidth and \topWidth. (The rectangle will actually be double as wide as the numbers specified.)

The height of the rectangles is set to 1 but this can easily adjusted by setting a different y vector.

The rectangle will be drawn and a path picture will be used to draw the pattern:
For this a coordinate system is setup so that

  • (0, 0) is in the bottom-left corner,
  • (1, 0) is in the bottom-right corner,
  • (0, 1) is in the top-left corner and
  • (1, 1) is in the top-right corner.

(This only works because no transformation like rotation or slanting is used.) This means we can use 1/\numPercs for the width of the patterned rectangles and \perc/100 for the height.

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{arrows.meta, calc, patterns}
\newcommand*\countList[2]{% not the most efficient but oh well
  \setcounter{pgf@counta}{0}%
  \foreach~in#1{\stepcounter{pgf@counta}}%
  \edef#2{\the\value{pgf@counta}}}
\begin{document}
\begin{tikzpicture}[thick]
\newcommand*\botWidth{4}
\newcommand*\topWidth{1}
\newcommand*\LIST{%
  Desc 0/{70, 30, 50},
  Desc 1/{20, 40, 60, 80, 10, 30, 50, 70, 90},
  Desc 2/{10},
  Desc 3/{40},
  Desc 4/{20},
  Desc 5/{10},
  Desc 6/{40},
  Desc 7/{20},
  Desc 8/{100}}

\countList\LIST\n
\foreach[
  count=\i from 0,
  evaluate={
    \numPercs=dim({\Percs});
    \Width=\botWidth-(\botWidth-\topWidth)/(\n-1)*\i;
  }] \Title/\Percs in \LIST {
  \draw (-\Width,\i) rectangle +(2*\Width,1) coordinate (tr) [
    path picture={
      \tikzset{shift=(path picture bounding box.south west),
                   x=(path picture bounding box.south east),
                   y=(path picture bounding box.north west)}
      \coordinate (@) at (0, 0);
      \foreach \perc in \Percs
        \path[pattern=grid, pattern color=red]
          (@) edge (@|-0,1)
          (@) rectangle +(1/\numPercs,\perc/100)
          coordinate (@) at (current path bounding box.south east);
    },
  ];
  \draw[->] ([shift=(down:.5), xshift=5mm]tr) -- +(right:8mm)
    node[right] {\Title};
}
\end{tikzpicture}
\end{document}

Output

enter image description here

1
  • i have modified the code little but i get the idea
    – IamK
    Commented Sep 29, 2023 at 3:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .