16

I'm looking for a more automated way to draw an area around a number of PStricks nodes. The area should be offset by a specific amount so that it will completely contain the vertices inside of it.

Here is a minimal example of a polygon that just traces the vertices/nodes:

enter image description here

\documentclass{article}
\usepackage{pst-node}% http://ctan.org/pkg/pst-node
\pagestyle{empty}
\begin{document}

\begin{pspicture}(0,0)(10,10)%
  \SpecialCoor
  % Identify some vertices
  \pnode(1;0){c}
  \pnode(1;300){g}
  \rput{0}([nodesep=1]c){
    \pnode(1;60){d}
    \pnode(1;0){e}
    \pnode(1;-60){f}
  }

  % Mark area
  \pspolygon[fillstyle=solid,fillcolor=black!15,linecolor=black!35,dimen=outer]
    (c)(d)(e)(f)(g)

  % Mark vertices
  \qdisk(c){3pt}
  \qdisk(d){3pt}
  \qdisk(e){3pt}
  \qdisk(f){3pt}
  \qdisk(g){3pt}
\end{pspicture}

\end{document}

The dimen=outer has no effect (and is used with \psframe), but it gives the idea that I'm after, just in a more general setting. From the pstricks documentation showing the effect of the dimen key-value (section 13 Line styles p 25):

enter image description here

My ideal solution would be something like offsetsep=5pt, akin to the offset key-value for regular lines (section 31 Node connections, p 68):

enter image description here

For example offsetsep=15pt will draw a polygon that is offset (outward) by 15pt, while offsetsep=-20pt will draw a polygon that is offset (inward) by 20pt.

2 Answers 2

12

How about this? This part uses a semi-automatic method to calculate the centroid.

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-eucl}
\psset{PointName=none}
\begin{document}
\begin{pspicture}[showgrid=false](-5,-5)(5,5)
\pnodes{A}(-2,3)(3,4)(2,-1)(-2,-4)(-4,0)
%
\def\points{(0,0)}
\def\Points{A0}% a dirty trick here
\foreach \x in {0,1,...,4}{%
    \pscircle*[linecolor=red](A\x){3pt}%
    \xdef\points{\points +0.2(A\x)}%
    \xdef\Points{\Points,A\x}%
}
\expandafter\nodexn\expandafter{\points}{O}
\pstHomO
[
    HomCoef=1.1,
    PointSymbol=none,
    CurveType=polygon,
    fillstyle=solid,
    fillcolor=cyan,
    opacity=0.25,
]{O}{\Points}
\end{pspicture}
\end{document}

enter image description here

Summary on dimen

I believe that you feel uncomfortable to remember the following notes as they are illogically defined so they waste your memory (in your brain).

For closed curves:

  1. psframe, pscircle, psellipse, pswedge, psellipticwedge have dimen=outer by default.
  2. the radial sides of pswedge and psellipticwedge always have dimen=middle, they cannot be changed.
  3. pspolygon, psccurve, and \pscustom always have dimen=middle, it cannot be changed.

For open curves:

  1. psline, pscurve, psbezier, psarc always have dimen=middle, they cannot be changed.
  2. psellipticarc has dimen=outer by default.

Animation

This part still uses the old method where the centroid is manually calculated.

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-eucl}
\psset{PointName=none,dotscale=2}
\begin{document}
\multido{\r=.6+.1}{8}{%
\begin{pspicture}[showgrid=false](-5,-5)(5,5)
\pnode(-.6,.4){O}
\pstGeonode
    (-2,3){A}
    (3,4){B}
    (2,-1){C}
    (-2,-4){D}
    (-4,0){E}
%
\pstHomO
[
    HomCoef=\r,
    PointSymbol=none,
    CurveType=polygon,
    fillstyle=solid,
    fillcolor=cyan,
    opacity=0.25,
    linearc=.2,
]{O}{A,B,C,D,E}
\end{pspicture}}
\end{document}

enter image description here

6
  • 1
    No animation? :) Just kidding, +1
    – cmhughes
    Sep 16, 2013 at 20:08
  • @cmhughes: Thanks for upvoting. Animation has been added. Sep 16, 2013 at 20:16
  • Only for convex closed curves. Sep 16, 2013 at 20:35
  • Does this require you to know the (x,y) centre of the object (I haven't experimented)? That is, can I draw some points willy-nilly for a polygon and expect it to scale proportionately without problem? The pst-eucl documentation just references it as the centre. Did you figure (-.6,.4) out manually?
    – Werner
    Sep 16, 2013 at 20:59
  • @Werner: For convex polygons, the their centroids can be used as the center of homotheti transformation. I calculated the centroid manually with simple formula (sigma x over number of points, sigma y over number of points). Sep 17, 2013 at 4:47
13

Drawing parallel paths with the nfold library

For TikZ, nowadays the nfold library by Jonathan can be used. It automatically deals with curves as well. The math isn't trivial for Bézier curves (see the appendix of the package's manual) but it works very well.

I'm using the same paths as in the old answer below but added two diagrams with a new curved path to it where you can see the shortcomings of this mathematical challange. The parallel curves aren't perfectly parallel but you notice this only with offset=±\pgflinewidth. Interestingly, for just two lines with offset=±.5\pgflinewidth it looks perfect.

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{nfold}
\makeatletter
\tikzset{offset/.code=\tikz@addmode{\pgfgetpath\tikz@temp\pgfsetpath\pgfutil@empty\pgfoffsetpath\tikz@temp{#1}}}
\makeatother
\begin{document}
\tikz[nodes={circle, fill, inner sep=+1.5pt}]
  \draw[preaction={rounded corners, offset=10pt, fill=gray}]
    (-2, 3) node (A) {}
    ( 3, 4) node (B) {}
    ( 2,-1) node (C) {}
    (-2,-4) node (D) {}
    (-4, 0) node (E) {}
    plot[sharp cycle, samples at={A, ..., E}] (\x);

\begin{tikzpicture}[line width=.25cm, opacity=.75]
\draw[help lines] (-.5, -.5) grid (7.5, 3.9);
\draw                                           (0,2) rectangle (2,3);
\draw[offset=.5\pgflinewidth]  (0,0) rectangle (2,1);
\draw[offset=-.5\pgflinewidth] (3,0) rectangle (4,3);
\path[xshift=5cm, offset=.5\pgflinewidth, draw, line join=round]
  (0,0) -| (1,3) -- (2,1) -- (0,1) -- cycle;
\end{tikzpicture}

\tikz[line width=.2cm]
  \draw[
    postaction={draw=blue,                     offset=+ \pgflinewidth},
    postaction={draw=red, fill=green!50!black, offset=+-\pgflinewidth},
    bend left] (0,0) to ++(30:2) to ++ (-60:2) to ++(-150:2) to cycle;

\tikz[line width=.2cm]
  \path[
    postaction={draw=blue,                     offset=+ .5\pgflinewidth},
    postaction={draw=red, fill=green!50!black, offset=+-.5\pgflinewidth},
    bend left] (0,0) to ++(30:2) to ++ (-60:2) to ++(-150:2) to cycle;
\end{document}

Output (only curves)

enter image description here enter image description here

Drawing parallel paths with a (decoration)

Just for fun (cough) with TikZ (and PGF decorations).

The biggest part of the decoration code has already been provided by Mark Wilbrow in his answer to Draw additional parallel paths in TikZ. His decoration contour lineto works very fine but doesn’t deal with closed paths.

To work around this issue, the angle of the first segment (here (A) -- (B) is saved in \pgf@decorate@firstsegmentangle and later used if the input segment is a closepath/-- cycle.

If the contour line is drawn on the outer (longer) side of the polygon his code for the start state

\pgfpathmoveto{\pgfpoint{0pt}{\pgfdecoratedcontourdistance}}%

would have been fine but this point would lie on the outer side of a contour polygon that lies inside the original path. I don’t know a way to access the last segment’s angle to correct this in the very first state for the first segment. As a work-around the actual first segment of the contour line starts halfway between the first points (here, halfway between (A) and (B)).

This is sub-optimal. It is advised that the -- cycle operator actually travels a certain distance (so that an angle can be calculated). A path like

\path (0,0) -| (1,1) -| (0,0) -- cycle;

cannot be decorated successfully.

If the option rounded corners shall be used on the decorated contour line, the contour option (and rounded corners) has to be included in a pre- or postaction.

Code

\documentclass[tikz]{standalone}
\makeatletter
\usetikzlibrary{decorations,backgrounds}
\def\pgfdecoratedcontourdistance{0pt}
\pgfset{
  decoration/contour distance/.code=%
    \pgfmathsetlengthmacro\pgfdecoratedcontourdistance{#1}}
\pgfdeclaredecoration{contour lineto closed}{start}{%
  \state{start}[
    next state=draw,
    width=0pt,
    persistent precomputation=\let\pgf@decorate@firstsegmentangle\pgfdecoratedangle]{%
    \pgfpathmoveto{\pgfpointlineattime{.5}
      {\pgfqpoint{0pt}{\pgfdecoratedcontourdistance}}
      {\pgfqpoint{\pgfdecoratedinputsegmentlength}{\pgfdecoratedcontourdistance}}}%
  }%
  \state{draw}[next state=draw, width=\pgfdecoratedinputsegmentlength]{%
    \ifpgf@decorate@is@closepath@%
      \pgfmathsetmacro\pgfdecoratedangletonextinputsegment{%
        -\pgfdecoratedangle+\pgf@decorate@firstsegmentangle}%
    \fi
    \pgfmathsetlengthmacro\pgf@decoration@contour@shorten{%
      -\pgfdecoratedcontourdistance*cot(-\pgfdecoratedangletonextinputsegment/2+90)}%
    \pgfpathlineto
      {\pgfpoint{\pgfdecoratedinputsegmentlength+\pgf@decoration@contour@shorten}
      {\pgfdecoratedcontourdistance}}%
    \ifpgf@decorate@is@closepath@%
      \pgfpathclose
    \fi
  }%
  \state{final}{}%
}
\makeatother
\tikzset{
  contour/.style={
    decoration={
      name=contour lineto closed,
      contour distance=#1
    },
    decorate}}
\begin{document}
\begin{tikzpicture}
\path
    (-2, 3) coordinate (A)
    ( 3, 4) coordinate (B)
    ( 2,-1) coordinate (C)
    (-2,-4) coordinate (D)
    (-4, 0) coordinate (E);

\draw[preaction={contour=10pt, rounded corners, fill=gray}]
  (A) -- (B) -- (C) -- (D) -- (E) -- cycle;

\foreach \coord in {A,...,E} \fill (\coord) circle [radius=2pt];
\end{tikzpicture}

\begin{tikzpicture}[gridded, line width=.25cm, opacity=.75]
\draw                                           (0,2) rectangle (2,3);
\draw[contour=.5\pgflinewidth]  (0,0) rectangle (2,1);
\draw[contour=-.5\pgflinewidth] (3,0) rectangle (4,3);
\path[xshift=5cm,contour=.5\pgflinewidth,draw,line join=round]
  (0,0) -| (1,3) -- (2,1) -- (0,1) -- cycle;
\end{tikzpicture}
\end{document}

Output

enter image description here enter image description here

3
  • looks nice. Could you not also save the angle of the preceding segment? Then you could test if the current segment is a closepath and if it is and has length zero, use the previous angle? Sep 20, 2013 at 7:26
  • Thank you for this wonderful solution! Unfortunately, it doesn't work with Bezier curves. Do you also have a solution for that?
    – ValenStein
    Oct 25 at 7:13
  • 1
    @ValenStein Nowadays, we can let nfold do the work. See my updated answer. Oct 25 at 9:00

You must log in to answer this question.

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