Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Suppose I have two ellipses with known locations, major and minor axes, and rotation angles. What I would like to do is define a custom path that primarily uses one ellipse but follows the other at their intersection. A picture is helpful here:

custom path for two ellipses

So I want my custom path to start at A, follow the black ellipse clockwise to B, then follow the pink ellipse back to A. I know I'll need to use TikZ to compute the intersections for me automatically (I have some experience with this). As for creating the path itself, this Batman post indicates I'd have to use \pgfpatharcto commands: Using TikZ, how to draw an elliptical arc starting from point A to point B with the origin as its center where both radii are given?

Once I get this path, I want to be able to use it for reverse clipping, as described here: How can I invert a 'clip' selection within TikZ?. I also have experience doing this, but I don't understand how I would use it with the lower level pgf commands used to define the path.

This isn't the exact problem I am working on. Generally, I will have a number of shapes and I want to use clipping and reverse clipping to draw isolated regions. So this question really boils down to: how do I create a custom path that can be used with reverse clipping?

share|improve this question
    
To do the specific action described in your last paragraph, you could do it in two steps: fill the path "square + ellipse" (ie as a single path command) with the even-odd rule. Then fill the ellipse but reverse clip against the second ellipse. I merely mention this to get it out of the way as I think that your more general question is very interesting and would like to see an answer to it. –  Loop Space Oct 8 '12 at 9:13
    
Oops, that would also get you the right-hand bit of the first ellipse. But you could adjust the clip to avoid that. It wouldn't be so elegant, though. –  Loop Space Oct 8 '12 at 9:14
    
To show where I am ultimately going: I intend on using this partial ellipse as a fundamental shape in a Venn-like diagram. Your solution here: tex.stackexchange.com/questions/67395/… is what I intend on doing, except using three custom ellipse shapes and four circles. –  Tom Oct 8 '12 at 9:18
    
That's useful to know. I can think of several approaches to this question, ranging from "Extremely elegant" down to "Quick and dirty" and which depend somewhat on how much calculation you are prepared to do beforehand (for example, you could compute the intersection points yourself and use them to decompose the paths yourself). I'd quite like to see all of the possible solutions! But that might not be helpful for you. From your comment, it seems safe to assume that all the paths will be ellipse segments and the parameters known in advance. Is that right? –  Loop Space Oct 8 '12 at 9:27
    
All parameters will be known. I mostly have the base drawn in TikZ: i.imgur.com/bc4qm.png . But I am having trouble removing the red lines. I drew ellipses, but I really need them to be those partial ellipses. After that, I will have 7 fundamental paths (4 circles, 3 partial ellipses) and I want to use clipping and reverse-clipping to independently display each atom in the diagram. –  Tom Oct 8 '12 at 9:33

3 Answers 3

up vote 21 down vote accepted
+100

well, this will not be a short answer. In fact, it will be a very long answer, but will solve the problems of this type in a generic way. Here are some examples of what you can do with this proposal.

Exemplo 1 Exemplo 2

Libraries and packages

We used a feature of tikz called decorations.markings to recreate the path with nodes. Below is the statement:

\tikzset{
    split/.style = {
        , name path = #1
        , /utils/exec={\setcounter{cnt}{0}}
        , postaction = {
            , decorate
            , decoration={
                markings
                , mark = between positions 0 and 1 step 2pt with {
                    \node [
                        circle
                        , minimum size = 0pt
                        , inner sep = .2pt
                        , /utils/exec={\stepcounter{cnt} \setpointsof{#1}{\thecnt}}
                    ] (#1-\thecnt) {};
                }
            }
        }
    }
}

We also use the library intersections to find the points of intersection between paths.

Furthermore, we use the package etoolbox that provides the means to store values ​​in a very specific and also features flow control (if, while, etc.).

Macros

First I will explain the macros.

The next two macros store and retrieve the number of points of intersection between two paths.

% #1: path 1
% #2: path 2
% #3: intersect points
\newcommand{\setintersectpointsof}[3]{\csxdef{intersectpointsof#1and#2}{#3}}

% #1: path 1
% #2: path 2
\newcommand{\getintersectpointsof}[2]{\csuse{intersectpointsof#1and#2}}

The next two macros store and retrieve the number of points of a path.

% #1: path
\newcommand{\pointsof}[1]{\csuse{pointsof#1}}

% #1: path
% #2: intersect points
\newcommand{\setpointsof}[2]{\csnumgdef{pointsof#1}{#2}}

The next two macros store and retrieve values ​​related to indexed variables. Something close to the vectors in programming.

% #1: name of path
% #2: name of the paths
% #3: index of the data
% #4: data
\newcommand\setstorageof[4]{\csnumgdef{#1in#2#3}{#4}}

% #1: name of the path
% #1: name of the paths
% #2: index of the data
\newcommand\getstorageof[3]{\csuse{#1in#2#3}}

The next macro identifies the intersection points in each path. Therefore, it is also used a margin of error for fine tuning.

% #1: first path
% #2: second path
% #3: margin of error
\newcommand{\getintersectionsof}[3]{

    \path [
        name intersections = {of = #1 and #2, name = j, total=\t, sort by = #1}
        , /utils/exec = {\xdef\total{\t}}
    ];

    \setintersectpointsof{#1}{#2}{\total}
    \pgfmathsetlengthmacro{\marginoferror}{#3}

    \foreach \q in {1, ..., \getintersectpointsof{#1}{#2}} {
        \pgfextractx{\xref}{\pgfpointanchor{j-\q}{center}}
        \pgfextracty{\yref}{\pgfpointanchor{j-\q}{center}}

        \foreach \p in {#1, #2} {

            \foreach \r in {1, ..., \pointsof{\p}}{
                \pgfextractx{\xone}{\pgfpointanchor{\p-\r}{center}}
                \pgfextracty{\yone}{\pgfpointanchor{\p-\r}{center}}

                \ifboolexpr{
                    test {\ifdimless{\xref - \marginoferror}{\xone}} and test {\ifdimless{\xone}{\xref + \marginoferror}}
                    and
                    test {\ifdimless{\yref - \marginoferror}{\yone}} and test {\ifdimless{\yone}{\yref + \marginoferror}}
                }{
                    \setstorageof{\p}{#1#2}{\q}{\r}
                }{
                }
            }
        }
    }
}

The next two macros build the paths taken between intersections. Can be made directly or reverse with respect to the recreation paths made by the markings.

% #1: working path
% #2: first element
% #3: last element
% #4: resulting path
% #5: next position of resulting path
\newcommand{\constructreversepath}[5]{
    \pgfmathtruncatemacro{\k}{#2}   
    \csedef{#4}{};
    \unlessboolexpr{test{\ifnumequal{\k}{#3}}}{
        \stepcounter{innercnt}
        \node (#4-\theinnercnt) at (#1-\k.center) {};
        \csxdef{#4}{\csuse{#4} (#4-\theinnercnt)};
        \pgfmathtruncatemacro{\k}{\k - 1}
        \ifnumless{\k}{1}{
            \pgfmathtruncatemacro{\k}{\pointsof{#1} - \k}
        }{
            \ifnumgreater{\k}{\pointsof{#1}}{
                \pgfmathtruncatemacro{\k}{\k - \pointsof{#1}}
            }{
            }
        }
    }
    \stepcounter{innercnt}
    \node (#4-\theinnercnt) at (#1-\k.center) {};
    \csedef{#4}{\csuse{#4} (#4-\theinnercnt)};
}

% #1: working path
% #2: first element
% #3: last element
% #4: resulting path
% #5: next position of resulting path
\newcommand{\constructdirectpath}[5]{
    \pgfmathtruncatemacro{\k}{#2}
    \csedef{#4}{};
    \unlessboolexpr{test{\ifnumequal{\k}{#3}}}{
        \stepcounter{innercnt}
        \node (#4-\theinnercnt) at (#1-\k.center) {};
        \csxdef{#4}{\csuse{#4} (#4-\theinnercnt)};
        \pgfmathtruncatemacro{\k}{\k + 1}
        \ifnumless{\k}{1}{
            \pgfmathtruncatemacro{\k}{\pointsof{#1} - \i}
        }{
            \ifnumgreater{\k}{\pointsof{#1}}{
                \pgfmathtruncatemacro{\k}{\k - \pointsof{#1}}
            }{
            }
        }
    }
    \stepcounter{innercnt}
    \node (#4-\theinnercnt) at (#1-\k.center){};
    \csedef{#4}{\csuse{#4} (#4-\theinnercnt)};
}

And the next macro creates macros that store the full path between two points of intersection using the two previous macros according to the input option (direct or reverse).

% #1: first path 
% #2: direct or reverse first path
% #3: second path
% #4: direct or reverse second path
% #5: index of first point
% #6: index of second point
% #7: name of resulting path
\newcommand{\constructpath}[7]{
    \setcounter{innercnt}{0}

    \pgfmathtruncatemacro{\indexone}{\getstorageof{#1}{#1#3}{#5}}
    \pgfmathtruncatemacro{\indextwo}{\getstorageof{#1}{#1#3}{#6}}

    \ifstrequal{#2}{direct}{
        \constructdirectpath{#1}{\indexone}{\indextwo}{#7}{\theinnercnt}
    }{
        \constructreversepath{#1}{\indexone}{\indextwo}{#7}{\theinnercnt}
    }

    \pgfmathtruncatemacro{\indexone}{\getstorageof{#3}{#1#3}{#5}}
    \pgfmathtruncatemacro{\indextwo}{\getstorageof{#3}{#1#3}{#6}}

    \ifstrequal{#4}{direct}{
        \constructdirectpath{#3}{\indextwo}{\indexone}{#7}{\theinnercnt}
    }{
        \constructreversepath{#3}{\indextwo}{\indexone}{#7}{\theinnercnt}
    }

    \setpointsof{#7}{\theinnercnt}
    \csxdef{#7}{}
    \foreach \k in {1, ..., \theinnercnt}{
        \csxdef{#7}{\csuse{#7} (#7-\k)}
    }
}

And that's it. Below I show, step by step, how to implement the proposed solution to the problem.

Structure

Let us begin making clear the basic structure of the document with packages, libraries, variables and counters. All subsequent changes will be made ​​within the environment tikzpicture.

\documentclass{standalone}

\usepackage{etoolbox}

\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\usetikzlibrary{intersections}
\usetikzlibrary{shapes.geometric}

\newcounter{cnt}
\newcounter{innercnt}

\newdimen\xone
\newdimen\yone
\newdimen\xref
\newdimen\yref

%
% here you must put the tikzset and macros.
%

\begin{document}
    \begin{tikzpicture}

        %
        % here you must put all subsequent code
        %

    \end{tikzpicture}
\end{document}

Step by step

First let's create the three ellipses that form the basis of our paths.

\path node [ellipse, split = path01, minimum width = 2cm, minimum height = 1cm] {};
\path node [ellipse, split = path02, minimum width = 2cm, minimum height = 1cm, rotate = 60] {};
\path node [ellipse, split = path03, minimum width = 2cm, minimum height = 1cm, rotate = 120] {};

And we will get:

Ellipses base

So we need the points of intersection between the ellipses. In this case, we are working with the first horizontal ellipse and the ellipse rotated 60 degrees. I put the indexes of each of the intersections to show how each can be referenced.

Intersections

And now we need to build the paths that we use between the points of intersection. In the image, the paths were painted to highlight the change. But the code below does not.

\constructpath{path01}{direct}{path02}{reverse}{1}{2}{path-1-2-1}
\constructpath{path01}{direct}{path02}{reverse}{2}{3}{path-1-2-2}
\constructpath{path01}{direct}{path02}{reverse}{3}{4}{path-1-2-3}
\constructpath{path01}{direct}{path02}{reverse}{4}{1}{path-1-2-4}

\path [name path = path-1-2-1] plot [smooth cycle] coordinates {\csuse{path-1-2-1}};
\path [name path = path-1-2-2] plot [smooth cycle] coordinates {\csuse{path-1-2-2}};
\path [name path = path-1-2-3] plot [smooth cycle] coordinates {\csuse{path-1-2-3}};
\path [name path = path-1-2-4] plot [smooth cycle] coordinates {\csuse{path-1-2-4}};

Paths between points of intersection

We do the same for the remaining ellipse.

\getintersectionsof{path01}{path03}{0.8pt}

\constructpath{path01}{direct}{path03}{reverse}{1}{2}{path-1-3-1}
\constructpath{path01}{direct}{path03}{reverse}{2}{3}{path-1-3-2}
\constructpath{path01}{direct}{path03}{reverse}{3}{4}{path-1-3-3}
\constructpath{path01}{direct}{path03}{reverse}{4}{1}{path-1-3-4}

\path [name path = path-1-3-1] plot [smooth cycle] coordinates {\csuse{path-1-3-1}};
\path [name path = path-1-3-2] plot [smooth cycle] coordinates {\csuse{path-1-3-2}};
\path [name path = path-1-3-3] plot [smooth cycle] coordinates {\csuse{path-1-3-3}};
\path [name path = path-1-3-4] plot [smooth cycle] coordinates {\csuse{path-1-3-4}};

Now we define the final paths using the paths already prepared earlier.

\getintersectionsof{path02}{path-1-3-1}{0.8pt}
\getintersectionsof{path02}{path-1-3-3}{0.8pt}
\constructpath{path02}{direct}{path-1-3-1}{direct}{1}{2}{path-1-2-3-3}
\constructpath{path02}{direct}{path-1-3-3}{direct}{1}{2}{path-1-2-3-4}

\getintersectionsof{path03}{path-1-2-2}{0.8pt}
\getintersectionsof{path03}{path-1-2-4}{0.8pt}
\constructpath{path03}{direct}{path-1-2-2}{reverse}{1}{2}{path-1-2-3-5}
\constructpath{path03}{direct}{path-1-2-4}{reverse}{1}{2}{path-1-2-3-6}

And now just paint the interest areas.

\fill [orange!80] plot [smooth cycle] coordinates {\csuse{path-1-2-3-1}};
\fill [orange!80] plot [smooth cycle] coordinates {\csuse{path-1-2-3-4}};
\fill [orange!80] plot [smooth cycle] coordinates {\csuse{path-1-2-3-5}};

And TAH-DAH!!!

Final result

share|improve this answer
1  
Absolutely #($#&!@) fantastic. This is the sort of thing I had in mind when I suggested that there could be a range of answers - I was really hoping someone would do something like this! –  Loop Space Oct 12 '12 at 21:57
    
Wow! This is really great. Much better, and very generic. –  Tom Oct 13 '12 at 23:40

I've used something similar to blend a node shape with background before but I don't know how suitable it is for the Venn diagram question.

Basically the solution motto is: When things get tough, switch to path picture. I've removed the rotated ellipse on top to show the clipping inside the node. But you can add it by uncommenting the line in the code.

\documentclass[tikz]{standalone}
\usetikzlibrary{shapes.geometric}
\begin{document}
\begin{tikzpicture}
\draw[ultra thick,blue,fill=red!70](-2,-2) rectangle (2,2);

\node[ellipse,draw,preaction={fill=red!70}, %<- First fill the node!
minimum width=2cm,minimum height=1cm,
path picture={ %<- Load the ellipse and the rectangle that partially fills the ellipse
    \begin{scope}[filler/.style={minimum width=2cm,minimum height=1cm,rotate=45,fill=yellow}]
    \node[ellipse,filler]{};
    \node[filler,anchor=north]{};
    \end{scope}
}] {};

% \node[ellipse,draw,rotate=45,minimum width=2cm,minimum height=1cm] {};
\end{tikzpicture} 
\end{document}

Here are the resulting images:

enter image description here enter image description here

share|improve this answer
    
This looks good, but I'm unsure of how to adapt it to Venn diagram I mentioned in the comments. Reverse clipping seems to be what I need if I want to isolate regions easily. –  Tom Oct 10 '12 at 6:51
    
@Tom I've suspected this. I'll try to understand what happens in that Venn diagram question. –  percusse Oct 10 '12 at 19:16
    
@Tom Should I delete this answer? In any case you can answer your own question and accept it. –  percusse Oct 10 '12 at 19:58
    
I think what you did is much cleaner. So it might be good to keep it on here, in case people want other solutions that also work. Once I clean mine up, I can put a more formal answer there---I'll make it pinpoint to what the question originally asked. –  Tom Oct 10 '12 at 20:01

I ended up working with \pgfpatharcto and the intersections library, along with reverse clipping. The allowed me to me isolate regions for a more complicated diagram I was trying to draw.

The only tricky part here is that you must manually choose 2 of the 4 possible intersections points. Then, you must make sure to construct your path in a CCW direction (since the reverseclip is defined with a CW direction). See: How is the interior of a path determined when reverse clipping?. Otherwise, there is some low level calling going on to load saved paths, but the rest follows the technique listed here: How can I invert a 'clip' selection within TikZ?

Here is the image:

Reverse cropped ellipse

If you generate the PDF, you can see that the filled area is slightly outside the stroked area. I'm not sure, but I think this might be related to the numerical inaccuracies of \pgfpatharcto. Its barely noticable. Here is the code:

\documentclass{article} % does not work with \documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{intersections}
\begin{document}
\pagestyle{empty}

\def\EllipseXRadius{3.5cm}
\def\EllipseYRadius{1.75cm}
\def\EllipseShape{ellipse [x radius=\EllipseXRadius, y radius=\EllipseYRadius]}

% This is a CW path, so the clipped path must be CCW in order for this to work.
% http://tex.stackexchange.com/questions/76212/how-is-the-interior-of-a-path-determined-when-reverse-clipping    
\tikzstyle{reverseclip}=[insert path={(current page.north east) --
  (current page.south east) --
  (current page.south west) --
  (current page.north west) --
  (current page.north east)}
]

\begin{tikzpicture}[remember picture] % two compilations are required

\path[name path=ellipse1] (0,0) \EllipseShape;
\path[name path=ellipse2, rotate=60] (0,0) \EllipseShape;    

% Use this to manually identify the desired intersection points.
%\fill [name intersections={of=ellipse1 and ellipse2, name=i, total=\t, sort by=ellipse1}]
%  \foreach \s in {1,...,\t}{(i-\s) circle (2pt) node[above] {\footnotesize\s}};

% Make the truncated ellipse using manually choosen points (i-2) and (i-3)
\path [name intersections={of=ellipse1 and ellipse2, name=i, total=\t, sort by=ellipse1}];
\pgfpathmoveto{\pgfpointanchor{i-2}{center}}
\pgfpatharcto{\EllipseXRadius}{\EllipseYRadius}{60}{0}{1}{\pgfpointanchor{i-3}{center}}
\pgfpatharcto{\EllipseXRadius}{\EllipseYRadius}{0}{1}{1}{\pgfpointanchor{i-2}{center}}
% Do not close the path...as the stroke endings are bad. instead, hack it:
\pgfpathcircle{\pgfpointanchor{i-2}{center}}{.05pt}

\makeatletter

\pgfsyssoftpath@getcurrentpath{\TruncatedEllipse}

% clear the current path
\pgfusepath{}

\begin{scope}
    \begin{pgfinterruptboundingbox}
        \pgfsyssoftpath@setcurrentpath{\TruncatedEllipse}
        \clip[reverseclip];
    \end{pgfinterruptboundingbox}
    \filldraw[fill=red!50, draw=blue, thick, fill opacity=.5] (-5,-5) rectangle (5,5);
\end{scope}

% Fill the truncated ellipse.
% Note: You can see the numerical inaccuracies.       
\pgfsyssoftpath@setcurrentpath{\TruncatedEllipse}
\fill[green!50!blue];  

\makeatother


\draw[thick] (0,0) \EllipseShape;
\draw[thick, rotate=60] (0,0) \EllipseShape;

\end{tikzpicture}
\end{document}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.