15

I am looking for a tikz shape similar to the rectangle shape, but with the possibility of drawing individual borders selectively. Also when used with rounded corners, the corners missing a border should not be rounded.

The following image illustrates what I want when the bottom border is not drawn.

shape with missing border

Any clues on how to get this shape?

2
  • You need an algorithm that takes a list of sides that should be drawn a sequence of \pgfpathlineto and \pgfpathmoveto commands (where to start, where to end, etc). Do you have an idea on how to do that? There are 2^4 possibilities (including no border at all and a full border). (I started something like this but I cannot find it anymore.) Oct 26, 2013 at 21:39
  • 1
    Meanwhile: Cut one side of a rectangle node in TikZ Oct 26, 2013 at 21:52

4 Answers 4

16

A brute-force attempt.

The versions where only one or two opposite sides are drawn wouldn’t have a correct area to fill. One will need to use the special \behindbackgroundpath for this. The same is true for the other versions with two adjacent sides: they create a triangular area.

But you then need to use an extra key: /pgf/open rectangle fill. You can disable the filling with open rectangle fill=, i.e. an empty input. If you want to use TikZ options here (like shading, fading or such, this will get a little bit more complicated but would be doable, too.

As requested, I put the code for this shape in its own library. The files

The TikZ library only contains an \usepgflibrary to the PGF library which contains the actual code.


For the option open rectangle sides=full the usual background path of a rectangle is used. The same is true, if the value of open rectangle sides is empty. (If you want no border drawn, don’t use draw.) This also shows how to create aliases:

\let\pgf@sh@openrectangle@<new name>\pgf@sh@openrectangle@<old name>

say, for example

\let\pgf@sh@openrectangle@tab\pgf@sh@openrectangle@enw

The naming scheme is pretty easy: all sides starting from the eastern (counter-clockwise) side are specified:

  • e draws the eastern side.
  • ew draws the eastern and western side.
  • ews draws the the western, the southern and the eastern side (a cup).

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{chains} \usetikzlibrary{qrr.shapes.openrectangle}
\tikzset{nodes={rounded corners, text height=\heightof{f}, text width=\widthof{aaaa}, ultra thick, draw, open rectangle fill=blue!40, shape=open rectangle,
  append after command={(\tikzlastnode.south west) edge[to path={rectangle (\tikztotarget)}, very thin] (\tikzlastnode.north east)}}}
\begin{document}
\begin{tikzpicture}[x=1.1cm, Start chain/.style={start chain=ch#1 going below}, Start chain/.list={1,...,4}, node distance=+2pt]\ttfamily
\foreach \ch/\si in {1/{e, n, w, s},
                     2/{nws, ews, ens, enw},
                     3/{en, nw, ws, es},
                     4/{, full, ew, ns}}
  \foreach \Si in \si
    \node[at=(right:\ch), on chain=ch\ch, open rectangle sides=\Si] {\Si};
\end{tikzpicture}
\end{document}

Output

enter image description here

3
  • I would like to be able to fill the full area of the shape, independently of which borders are drawn. Your solution does that only for the cases with 3 or 4 borders. Could you please complete this feature for 0, 1 and 2 borders? Unfortunately I do not have the skill to program with tikz at this level yet. You mentioned the need to use a special \behindbackgroundpath for this.
    – Romildo
    Oct 29, 2013 at 20:10
  • I would like also to suggest to separate the code into two pieces: a tikz library (let's say tikzlibraryopenrectangle.code.tex) and a demonstration document.
    – Romildo
    Oct 29, 2013 at 20:13
  • @Romildo See my updated answer. Nov 8, 2013 at 23:28
15

I tried an approach with Tikz, by drawing a line around an existing node and turning that into a newcommand. You can change almost everything according to your needs.

The only exception is relative positioning. I'm still having a problem with it because I can't seem to make it work. I've tried different tweaks but I haven't found the solution yet. (If someone sees what I'm missing, please let me know.)

The only workaround for now is that, if you leave a blank line between the two commands, it will draw the node below the other one, if you leave no lines, they will be positioned one next to the other one.

rectangle image

\documentclass{article}
\usepackage{tikz}

\usetikzlibrary{positioning,shapes.misc,calc}

\tikzset{newnode/.style={inner sep=2mm, text depth=.5mm},
}

\newcommand{\newtab}[4]{
\begin{tikzpicture}
    \node[newnode, #2] (#1) {#4};
        \draw[draw=black, rounded corners, thick, #3]  ($(#1.south west)+(0mm,0mm)$) 
                -- ($(#1.north west)+(0mm,0mm)$)
                -- ($(#1.north east)+(0mm,0mm)$)
                -- ($(#1.south east)+(0mm,0mm)$);
    \node[newnode, #2] (#1) {#4};
\end{tikzpicture}}

\begin{document}

% node name, positiong (doesn't work yet), line properties, node text

\newtab{test}{}{fill=red!20}{This is a test}
\newtab{test2}{}{fill=blue!20}{This is another test} 
\newtab{test3}{}{fill=green!20}{This is yet another test}

\end{document}
8

Given the popularity of tikz on this site, this is a good example to show that tikz also works nicely with ConTeXt (though, the interface is not as pretty).

\usemodule[tikz]

\define\tikztopframe
  {\starttikzpicture
    \draw[
           \overlaylinecolor, 
           line width=\overlaylinewidth,
           fill=\overlaycolor,
           rounded corners,
         ]
      (0,0)--  (0,\overlayheight) -- (\overlaywidth,\overlayheight) -- (\overlaywidth, 0) ;
  \stoptikzpicture}

\defineoverlay[topframe][\tikztopframe]

\defineframed
  [topframe]
  [
    frame=off,
    location=low,
    background=topframe,
    backgroundcolor=yellow,
    framecolor=red,
  ]

\starttext
  Test
  \topframe[rulethickness=2pt]{This is a test}
  \topframe[backgroundcolor=green]{Another test}
  Test
\stoptext

which gives

enter image description here

8

Next solution is inspired in Qrrbrbirlbel's answer to Fill a node shape, although in this case we need two different orders to fill and drawthe shape.

\documentclass[tikz,border=3mm]{standalone}

\usetikzlibrary{positioning}

\begin{document}
\begin{tikzpicture}[
    bottomflat/.style={
        append after command={%
            \pgfextra
                \fill[fill=#1] (\tikzlastnode.south west) [rounded corners] |- (\tikzlastnode.north) -| (\tikzlastnode.east) [sharp corners] |- cycle;
                \draw[rounded corners] (\tikzlastnode.south west) |- (\tikzlastnode.north) -| (\tikzlastnode.south east);
            \endpgfextra}},
    topflat/.style={
        append after command={%
            \pgfextra
                \fill[fill=#1] (\tikzlastnode.north east) [rounded corners] |- (\tikzlastnode.south) -| (\tikzlastnode.west) [sharp corners] |- cycle;
                \draw[rounded corners] (\tikzlastnode.north east) |- (\tikzlastnode.south) -| (\tikzlastnode.north west);
            \endpgfextra}},
    leftflat/.style={
        append after command={%
            \pgfextra
                \fill[fill=#1] (\tikzlastnode.north west) [rounded corners] -| (\tikzlastnode.east) |- (\tikzlastnode.south) [sharp corners] -| cycle;
                \draw[rounded corners] (\tikzlastnode.north west) -| (\tikzlastnode.east) |- (\tikzlastnode.south west);
            \endpgfextra}},
    rightflat/.style={
        append after command={%
            \pgfextra
                \fill[fill=#1] (\tikzlastnode.south east) [rounded corners] -| (\tikzlastnode.west) |- (\tikzlastnode.north) [sharp corners] -| cycle;
                \draw[rounded corners] (\tikzlastnode.south east) -| (\tikzlastnode.west) |- (\tikzlastnode.north east);
            \endpgfextra}}]

\path node[bottomflat=blue!20] (A) {This is a test};
\path node[topflat=green!20, below=3mm of A] (B) {This is a test};
\path node[leftflat=red!20, below=3mm of B] (C) {This is a test};
\path node[rightflat=purple!20, below=3mm of C] (D) {This is a test};
\end{tikzpicture}
\end{document}

enter image description here

Update

Previous code works with TikZ 3.0.0 but it fails with TiKZ 2.10. In this case you need to use

\documentclass[tikz,border=3mm]{standalone}

\usetikzlibrary{positioning}

\begin{document}
\begin{tikzpicture}[
    bottomflat/.style={
        append after command={%
            \pgfextra
                \fill[fill=#1] (\tikzlastnode.south west) [rounded corners] |- (\tikzlastnode.north) -| (\tikzlastnode.east) [sharp corners] |- (\tikzlastnode.south)-- cycle;
                \draw[rounded corners] (\tikzlastnode.south west) |- (\tikzlastnode.north) -| (\tikzlastnode.south east);
            \endpgfextra}},
    topflat/.style={
        append after command={%
            \pgfextra
                \fill[fill=#1] (\tikzlastnode.north east) [rounded corners] |- (\tikzlastnode.south) -| (\tikzlastnode.west) [sharp corners] |- (\tikzlastnode.north west)--cycle;
                \draw[rounded corners] (\tikzlastnode.north east) |- (\tikzlastnode.south) -| (\tikzlastnode.north west);
            \endpgfextra}},
    leftflat/.style={
        append after command={%
            \pgfextra
                \fill[fill=#1] (\tikzlastnode.north west) [rounded corners] -| (\tikzlastnode.east) |- (\tikzlastnode.south) [sharp corners] -| (\tikzlastnode.south west)--cycle;
                \draw[rounded corners] (\tikzlastnode.north west) -| (\tikzlastnode.east) |- (\tikzlastnode.south west);
            \endpgfextra}},
    rightflat/.style={
        append after command={%
            \pgfextra
                \fill[fill=#1] (\tikzlastnode.south east) [rounded corners] -| (\tikzlastnode.west) |- (\tikzlastnode.north) [sharp corners] -| (\tikzlastnode.north east)--cycle;
                \draw[rounded corners] (\tikzlastnode.south east) -| (\tikzlastnode.west) |- (\tikzlastnode.north east);
            \endpgfextra}}]

\path node[bottomflat=blue!20] (A) {This is a test};
\path node[topflat=green!20, below=3mm of A] (B) {This is a test};
\path node[leftflat=red!20, below=3mm of B] (C) {This is a test};
\path node[rightflat=purple!20, below=3mm of C] (D) {This is a test};
\end{tikzpicture}
\end{document}
5
  • Your code does not compile here: ! Argument of \tikz@collect@coordinate@onpath has an extra }. <inserted text> \par l.32 ...[bottomflat=blue!20] (A) {This is a test};
    – Romildo
    Oct 29, 2013 at 20:48
  • The \draw part is not correct. For topflat, use \fill[fill=#1] (\tikzlastnode.north east) [rounded corners] |- (\tikzlastnode.south) -| (\tikzlastnode.west) [sharp corners] |- (\tikzlastnode.north) -- cycle;
    – mort
    Jan 15, 2014 at 17:43
  • @mort I don't understand where is the problem. There is no need to include (\tikzlastnode.north) between (\tikzlastnode.west) [sharp corners] and cycle if you use |- instead of --.
    – Ignasi
    Jan 15, 2014 at 17:58
  • @Ignasi: Like Romildo said, your code does not compile
    – mort
    Jan 15, 2014 at 18:00
  • @mort. You're right. I'm always using cvs version and sometimes forget to test solutions with 2.10. I've updated my answer.
    – Ignasi
    Jan 15, 2014 at 18:12

You must log in to answer this question.

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