## pyx-checkins

 [PyX-checkins] commit 3546: trunk/pyx/design: add invalid parametrisation and self-intersections sections contributed by Michael Schindler From: - 2013-10-24 21:21:24 Revision: 3546 https://sourceforge.net/p/pyx/code/3546/ Author: wobsta Date: 2013-10-24 21:21:21 +0000 (Thu, 24 Oct 2013) Log Message: ----------- add invalid parametrisation and self-intersections sections contributed by Michael Schindler Modified Paths: -------------- trunk/pyx/design/beziers.tex Added Paths: ----------- trunk/pyx/design/invalid1.py trunk/pyx/design/invalid2.py trunk/pyx/design/invalid3.py trunk/pyx/design/invalid4.py trunk/pyx/design/selfinter1.py trunk/pyx/design/selfinter2.py Modified: trunk/pyx/design/beziers.tex =================================================================== --- trunk/pyx/design/beziers.tex 2013-10-22 12:49:43 UTC (rev 3545) +++ trunk/pyx/design/beziers.tex 2013-10-24 21:21:21 UTC (rev 3546) @@ -322,6 +322,7 @@ \section{Relative coordinates} +% <<< A B\'ezier curve is given by \vec b(t) = (1-t)^3\vec p_0 + 3t(1-t)^2\vec p_1 + 3t^2(1-t)\vec p_2 + t^3\vec p_3 @@ -385,7 +386,257 @@ The pre-removal of such problems would introduce major defects to the B\'ezier curve (like changing the direction of the tangential vector at the beginning and the end point). +% >>> +\section{Invalid parametrisation} + +% <<< +If the derivative of the parametrisation vanishes (both coordinates) at a +parameter~$t\in[0,1]$, the parametrisation is considered to be invalid at this +point. Under which circumstances can this occur? We search for solutions~$t$ for +the quadratic equations +% + + \label{zeroderiv} + \begin{aligned} + 0 = x(t)/3 &= t^2\bigl[(x_3-x_0) - 3(x_2-x_1)\bigr] + 2t\bigl[(x_2-x_1)-(x_1-x_0)\bigr] + (x_1-x_0) \\ + 0 = y(t)/3 &= t^2\bigl[(y_3-y_0) - 3(y_2-y_1)\bigr] + 2t\bigl[(y_2-y_1)-(y_1-y_0)\bigr] + (y_1-y_0) + \end{aligned} + +% +Using the usual formula for a quadratic equation $0=at^2 + bt + c$, we get the +same parameter for $x$- and $y$-coordinates, and the two inequalities from +$0\leq t\leq1$, +% +$$+ \label{samet} + t_{1,2} = \frac{1}{2a_x} \Bigl(-b_x \pm^1 \sqrt{b_x^2 - 4a_xc_x}\Bigr) + = \frac{1}{2a_y} \Bigl(-b_y \pm^2 \sqrt{b_y^2 - 4a_yc_y}\Bigr) +$$ +% +The two sign choices are independent of each other. + +For the moment, we put aside the special cases $p_3=p_0$ and $p_1=p_0$. As +affine transformations cannot change the degeneracy of the parametrisation, we +can fix three points without loss of generality: +% ++ \begin{aligned} + (x_0, y_0) = (0, 0),\quad + (x_1, y_1) = (0, 1),\quad + (x_3, y_3) = (1, 0). + \end{aligned} + +% +Thus, the only remaining true parameters are $(x_2, y_2)$. We will use $\Delta x += x_2$ and $\Delta y = y_2 - 1$. Equation~\eqref{samet} now becomes +% +$$+% a_y \Bigl(-b_x/2 \pm^1 \sqrt{(b_x/2)^2 - a_xc_x}\Bigr) +% = a_x \Bigl(-b_y/2 \pm^2 \sqrt{(b_y/2)^2 - a_yc_y}\Bigr) +% +% ( -3\Delta y) \Bigl(-(\Delta x ) \pm^1 \sqrt{(\Delta x )^2 }\Bigr) +% = (1-3\Delta x) \Bigl(-(\Delta y-1) \pm^2 \sqrt{(\Delta y-1)^2 + 3\Delta y}\Bigr) +% + \label{samet2} + ( -3\Delta y) \Bigl(-\Delta x \pm^1 \sqrt{(\Delta x )^2 }\Bigr) + = (1-3\Delta x) \Bigl(1-\Delta y \pm^2 \sqrt{1 + \Delta y + \Delta y^2}\Bigr) +$$ +% +Special care has to be taken if $a_x=0$ or $a_y=0$. For the inequalities, we +have to consider the signs of $a_x$ and $a_y$ explicitly: +\arraycolsep=0pt +% ++ \begin{aligned} + 0 \leq -\Delta x \pm^1 \sqrt{\Delta x^2} \leq 1-3\Delta x &\quad\text{if \Delta x < 1/3 (or a_x > 0)} \\ + 0 \leq \Delta x \mp^1 \sqrt{\Delta x^2} \leq 3\Delta x-1 &\quad\text{if 1/3 < \Delta x (or a_x < 0)} + \end{aligned} + +% +which can be reduced to +% ++ \label{constr1} + \begin{aligned} + \text{no constraint} &\quad\text{if \Delta x \leq 0} \\ + \text{only the upper sign of \pm^1 is allowed} &\quad\text{if 0 < \Delta x < 1} \\ + \text{no constraint} &\quad\text{if \Delta x \geq 1} + \end{aligned} + +% +Between $0$ and $1$ the upper sign makes the inequality to be trivially +satisfied, because $-\Delta x + |\Delta x| = 0$. + +The inequality constraint can equivalently be formulated for $\Delta y$, +% ++ \begin{aligned} + 0 \leq 1-\Delta y \pm^2 \sqrt{1+\Delta y+\Delta y^2} \leq -3\Delta y &\quad\text{if \Delta y < 0 (or a_y > 0)} \\ + 0 \leq \Delta y-1 \mp^2 \sqrt{1+\Delta y+\Delta y^2} \leq 3\Delta y &\quad\text{if \Delta y > 0 (or a_y < 0)} + \end{aligned} + +% +In the first line, the comparison with~$0$ is always true. In the right +comparison, the lower sign is always possible and does not yield a constraint, +whereas the upper sign is only possible for $\Delta y \leq -1$. In the second +line, the upper sign is incompatible with the left comparison, and the lower +sign is always possible. We can reduce the conditions to +% ++ \label{constr2} + \begin{aligned} + \text{no constraint} &\quad\text{if \Delta y \leq -1} \\ + \text{only the lower sign of \pm^2 is allowed} &\quad\text{if \Delta y > -1} \\ + \end{aligned} + +% + +Equation~\eqref{samet2} with the two constraints \eqref{constr1} and +\eqref{constr2} is the system of equations that gives us the values $\Delta x, +\Delta y$ for which the parametrisation could be invalid. + +First, let us check that the classic case for a cusp is well described. It has +$\Delta x=-1, \Delta y=0$. We have no constraint on $x$, and we have to take the +lower sign on $y$. Indeed, the left-hand side of eq.~\eqref{samet2} vanishes due +for the lower sign, and the right one vanishes also for the lower sign. This +case is shown among the examples in figure~\ref{fig:invalid3}. + +% +\begin{figure}[ht] + \centering + \includegraphics{invalid2}% + \caption{Absolute value of the difference between left- and right-hand side of + eq.~\eqref{samet2}. The dotted lines are given by eqs.~\eqref{leftcurve} and + \eqref{rightcurve}.}% + \label{fig:invalid2}% +\end{figure}% +% +\begin{figure}[ht] + \centering + \includegraphics{invalid3}% + \caption{Some examples of invalid parametrisations from the general + (non-degenerate) case described by eqs.~\eqref{samet2}, \eqref{constr1}, + \eqref{constr2}.}% + \label{fig:invalid3}% +\end{figure}% +% +Figure~\ref{fig:invalid2} shows the difference between left- and right-hand side +of Eq.~\eqref{samet2} for all valid signs, respecting the constraints. The +horizontal and vertical lines are those special cases where $a_x=0$ or $a_y=0$ +and need further considerations. The zero-level curve at $\Delta x < 0$ (dotted +curve in the figure) is well described by the equation +% +$$+ \label{leftcurve} + \Delta x = \frac{1}{3}\Bigl(-1-2\Delta y - \sqrt{(2\Delta y+1)^2 + 3}\Bigr) +$$ +% +which is obtained by taking the upper sign of $\pm^1$ in eq.~\eqref{samet2}: +With negative values of $\Delta x$ the left-hand side becomes $6\Delta x\Delta +y$. Isolating the square-root and then taking the square leads to a quadratic +equation for $\Delta x$, of which eq.~\eqref{leftcurve} is the negative +solution. A numerical check shows that this solution corresponds to the lower +sign of $\pm^2$ and has thus no restriction in $\Delta y$. The second solution +(dotted curve in the figure for $\Delta x>0$) of the mentioned quadratic +equation, +% +$$+ \label{rightcurve} + \Delta x = \frac{1}{3}\Bigl(-1-2\Delta y + \sqrt{(2\Delta y+1)^2 + 3}\Bigr) +$$ +% +corresponds to the upper sign of $\pm^2$ and is thus subject to the constraint +\hbox{$\Delta y \leq -1$}. + +Apart from the general cases on the two curves, we must treat some special cases +separately: +% +\begin{itemize} +\item $a_y=0$: This appears as a zero-level line in figure~\ref{fig:invalid2}, + but this is an artefact coming from both sides of eq.~\eqref{samet2} being + zero. We have to go back to the original set of + equations~\eqref{zeroderiv}: We have in the lower line $\Delta y=0$, thus + $t=1/2$. Going back to the upper line with this value, we obtain $\Delta + x=-1$. This parameter point is already covered by the general treatment. +\item $a_x=0$: This implies $\Delta x=1/3$, and the upper line gives $t=0$. + This, however, contradicts the lower line. We do not get an invalid + parametrisation point from $a_x=0$. +\item $p_1=p_0$: This is either an even more degenerate case (see below), or it + can be mapped to the reverse situation of $\Delta x=1, \Delta y=-1$ which + was $p_2=p_3$. +\item $p_1=p_0$ and $p_2=p_3$: This is a line. +\item $p_3=p_0$: There are invalid parametrisation points if all four + $p_0,p_1,p_2,p_3$ are collinear. Depending on the sign of + $(p_1-p_0)\cdot(p_3-p_2)$ there are one or two invalid points. +\item $p_1=p_0=p_2=p_3$: This is a point. +\end{itemize} +% +% >>> + +\section{Self-intersections} + +% <<< +In order to determine the control points which lead to self-intersections, we +could analyse the set of equations +% +$$+ x(t_1) = x(t_2)\quad\text{and}\quad + y(t_1) = y(t_2) \quad\text{for}\quad t_1\neq t_2. +$$ +% +To solve the problem, we could express the solutions $t(\Delta x, \bar x)$ of +the cubic equation $\bar x = x(t)$, test their number via Cardano's equation, +and, if they are more than one, use a pair of them in the $y$-equation to see +whether we get two times the same $y$-value. This would be a formidable task. + +We here take a different route. As for the invalid parametrisation above, we +exclude some degenerate cases and use affine transformations, such that only the +parameters $\Delta x=x_2$ and $\Delta y=y_2-1$ are left. It is geometrically +evident that a little left and right of the two relevant curves in +figure~\ref{fig:invalid2} we must have different situations as far as +self-intersections are concerned. Either we find one a little left or a little +right of the curves, and none on the respective other side. Further, a +self-intersection can clear away when parameters change such that the curve +passes through one of its endpoints. These two conditions boil down to the phase +diagram in figure~\ref{fig:selfinter2}. In the gray shaded areas, +self-intersections are possible. +% +\begin{figure}[t] + \includegraphics{selfinter2}% + \caption{Self-intersections are possible in the gray-shaded areas. The black, + red, green, blue curves are from eqs.~\eqref{leftcurve}, \eqref{rightcurve}, + \eqref{pass00}, \eqref{pass10}, respectively.}% + \label{fig:selfinter2}% +\end{figure} +% + +We first calculate the parameters $0>> + \end{document} % vim:foldmethod=marker:foldmarker=<<<,>>> Added: trunk/pyx/design/invalid1.py =================================================================== --- trunk/pyx/design/invalid1.py (rev 0) +++ trunk/pyx/design/invalid1.py 2013-10-24 21:21:21 UTC (rev 3546) @@ -0,0 +1,60 @@ +#!/usr/bin/env python +import sys;sys.path.insert(0, "..") +from math import * +from pyx import * +# File for invalid parametrisations of Bezier curves +# visualise the constraints + +def fx(x, sign1): + if x < 1/3.0: # a_x > 0 + if sign1 == "upper": + return -x + abs(x) + else: + return -x - abs(x) + else: + if sign1 == "upper": + return x - abs(x) + else: + return x + abs(x) + +def boundx(x): + if x < 1/3.0: + return 1-3*x + else: + return 3*x-1 + +def fy(y, sign2): + if y < 0: # a_y > 0 + if sign2 == "upper": + return 1-y + sqrt(1+y+y*y) + else: + return 1-y - sqrt(1+y+y*y) + else: + if sign2 == "upper": + return y-1 - sqrt(1+y+y*y) + else: + return y-1 + sqrt(1+y+y*y) + +def boundy(y): + if y < 0: + return -3*y + else: + return 3*y + +def doplot(xtitle, con): + g = graph.graphxy(width=10, + x=graph.axis.lin(title=xtitle, min=-5, max=5), + y=graph.axis.lin(), + key=graph.key.key(pos="tc"), + ) + g.plot(graph.data.function("y(x)=0", points=2, title=None), [graph.style.line()]) + g.plot(graph.data.function("y(x)=bound(x)", title=None, context=con, points=101), [graph.style.line()]) + g.plot(graph.data.function("y(x)=f(x, 'upper')", title="upper sign", context=con, points=101), [graph.style.line([color.rgb.red])]) + g.plot(graph.data.function("y(x)=f(x, 'lower')", title="lower sign", context=con, points=101), [graph.style.line([color.rgb.green])]) + return g + + +c1 = doplot(r"$\Delta x$", {"f":fx, "bound":boundx}) +c2 = doplot(r"$\Delta y$", {"f":fy, "bound":boundy}) +c1.insert(c2, [trafo.translate(0, c1.bbox().bottom() - c2.bbox().top() - 0.5)]) +c1.writePDFfile() Added: trunk/pyx/design/invalid2.py =================================================================== --- trunk/pyx/design/invalid2.py (rev 0) +++ trunk/pyx/design/invalid2.py 2013-10-24 21:21:21 UTC (rev 3546) @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +import sys;sys.path.insert(0, "..") +from math import * +from pyx import * +# File for invalid parametrisations of Bezier curves +# Draws a sketch of the areas where invalid params are to be expected + +def lhs(x, y, sign1): + if sign1 > 0: + return -3*y*(-x + abs(x)) + else: + return -3*y*(-x - abs(x)) + +def rhs(x, y, sign2): + if sign2 > 0: + return (1-3*x) * (1-y + sqrt(1+y+y*y)) + else: + return (1-3*x) * (1-y - sqrt(1+y+y*y)) + +def f(x, y): + xsigns = [-1, 1] + ysigns = [-1, 1] + if 0 < x < 1: + xsigns = [1] + if y > -1: + ysigns = [-1] + + val = float("inf") + for sign1 in xsigns: + for sign2 in ysigns: + val = min(val, abs(lhs(x,y,sign1) - rhs(x,y,sign2))) + return val + +xmin, xmax, xn = -2, 3, 250 +xvalues = [xmin + (xmax-xmin)*i/(xn-1) for i in range(xn)] +ymin, ymax, yn = -3, 2, 250 +yvalues = [ymin + (ymax-ymin)*i/(yn-1) for i in range(yn)] + +d = [] +for x in xvalues: + for y in yvalues: + d.append((x, y, log(f(x, y)))) + +g = graph.graphxy(width=10, + x=graph.axis.lin(title=r"$\Delta x$", min=xmin, max=xmax), + y=graph.axis.lin(title=r"$\Delta y$", min=ymin, max=ymax), +) +g.plot(graph.data.points(d, x=1, y=2, color=3, title=None), + [graph.style.density(gradient=color.rgbgradient.Rainbow)]) +g.dolayout() +g.plot(graph.data.function("x(y)=(-1-2*y-sqrt((1+2*y)**2+3))/3.0"), [graph.style.line([style.linestyle.dotted])]) +g.plot(graph.data.function("x(y)=(-1-2*y+sqrt((1+2*y)**2+3))/3.0", max=-1), [graph.style.line([style.linestyle.dotted])]) +g.writePDFfile() + Added: trunk/pyx/design/invalid3.py =================================================================== --- trunk/pyx/design/invalid3.py (rev 0) +++ trunk/pyx/design/invalid3.py 2013-10-24 21:21:21 UTC (rev 3546) @@ -0,0 +1,33 @@ +#!/usr/bin/env python +import sys;sys.path.insert(0, "..") +from math import * +from pyx import * +# File for invalid parametrisations of Bezier curves +# Some examples + +def Dx_m(Dy): + return (-1-2*Dy-sqrt((1+2*Dy)**2+3))/3.0 + +def Dx_p(Dy): + return (-1-2*Dy+sqrt((1+2*Dy)**2+3))/3.0 + +def acurve(Dx_fct, Dy, Dx=None): + if Dx is None: + Dx = Dx_fct(Dy) + p = path.curve(0,0, 0,1, Dx,1+Dy, 1,0) + c = canvas.canvas() + c.stroke(p, [deco.shownormpath()]) + c.text(-0.2, -10*unit.x_pt, r"\noindent$\Delta x=%g $\par\noindent$\Delta y=%g$"%(Dx,Dy), + [text.parbox(4), text.size.footnotesize]) + return c + +dx = 3 +dy = -3 +cc = acurve(Dx_m, 0) +c = acurve(Dx_m, 1); cc.insert(c, [trafo.translate(dx, 0)]) +c = acurve(Dx_m, -1); cc.insert(c, [trafo.translate(2*dx, 0)]) +c = acurve(Dx_m, -2); cc.insert(c, [trafo.translate(0, dy)]) +c = acurve(Dx_p, -1); cc.insert(c, [trafo.translate(dx, dy)]) +c = acurve(Dx_p, -3); cc.insert(c, [trafo.translate(2*dx, dy)]) +cc.writePDFfile() + Added: trunk/pyx/design/invalid4.py =================================================================== --- trunk/pyx/design/invalid4.py (rev 0) +++ trunk/pyx/design/invalid4.py 2013-10-24 21:21:21 UTC (rev 3546) @@ -0,0 +1,27 @@ +#!/usr/bin/env python +import sys;sys.path.insert(0, "..") +from math import * +from pyx import * +# File for invalid parametrisations of Bezier curves +# Check a-posteriori the sign of Delta y for the square-root solution of Delta x + +def Dx_m(y): + return (-1-2*y-sqrt((1+2*y)**2+3))/3.0 +def Dx_p(y): + return (-1-2*y+sqrt((1+2*y)**2+3))/3.0 + +con = {"Dx_m":Dx_m, "Dx_p":Dx_p} + +g = graph.graphxy(width=10, + x=graph.axis.lin(), + y=graph.axis.lin(min=-2, max=2), + key=graph.key.key(pos="bl"), +) +g.plot(graph.data.function("x(y)=6*y*Dx_m(y)", title="minus-sol, lhs", context=con), [graph.style.line([color.rgb.red])]) +g.plot(graph.data.function("x(y)=(1-3*Dx_m(y))*(1-y-sqrt(1+y+y*y))", title="minus-sol, minus-rhs", context=con), [graph.style.line([color.rgb.blue, style.linestyle.dashed])]) +g.plot(graph.data.function("x(y)=6*y*Dx_p(y)", title="plus-sol, lhs", context=con), [graph.style.line([color.rgb.green])]) +g.plot(graph.data.function("x(y)=(1-3*Dx_p(y))*(1-y+sqrt(1+y+y*y))", title="plus-sol, plus-rhs", context=con), [graph.style.line([style.linestyle.dashed])]) +g.plot(graph.data.function("x(y)=(1-3*Dx_p(y))*(1-y-sqrt(1+y+y*y))", title="plus-sol, minus-rhs", context=con), [graph.style.line([style.linestyle.dotted])]) +g.plot(graph.data.function("x(y)=(1-3*Dx_m(y))*(1-y+sqrt(1+y+y*y))", title="minus-sol, plus-rhs", context=con), [graph.style.line([style.linestyle.dotted])]) +g.writePDFfile() + Added: trunk/pyx/design/selfinter1.py =================================================================== --- trunk/pyx/design/selfinter1.py (rev 0) +++ trunk/pyx/design/selfinter1.py 2013-10-24 21:21:21 UTC (rev 3546) @@ -0,0 +1,55 @@ +#!/usr/bin/env python +import sys;sys.path.insert(0, "..") +from math import * +#sys.path.insert(0, os.path.expanduser("~/python/pyx-trunk")) +from pyx import * +# File for invalid parametrisations of Bezier curves +# Some examples + +def Dx_m(Dy): + return (-1-2*Dy-sqrt((1+2*Dy)**2+3))/3.0 + +def Dx_p(Dy): + return (-1-2*Dy+sqrt((1+2*Dy)**2+3))/3.0 + +def pass00(Dy): + return 1.0/(3.0*(Dy+1)) + +def pass10(Dy): + return (1.0+Dy**3)/(3.0*(Dy+1)) + +def acurve(Dx, Dy, trafo_reverse=False): + p = path.curve(0,0, 0,1, Dx,1+Dy, 1,0) + if trafo_reverse: + # calculate the trafo to put p2=(1,-1): + # (for the right branch only) + x2, y2 = Dx, Dy+1 + tr1 = trafo.scale(1, -1.0/y2) + pp = tr1.apply_pt(x2, y2) + tr2 = trafo.trafo(matrix=((1, (pp[0]-1)), (0, 1))) + p = p.transformed(tr2*tr1) + c = canvas.canvas() + c.stroke(p, [deco.shownormpath()]) + #c.text(-0.2, -10*unit.x_pt, r"\noindent$\Delta x=%g $\par\noindent$\Delta y=%g$"%(Dx,Dy), + # [text.parbox(4), text.size.footnotesize]) + return c + +dx = 3 +dy = -3 + +Dy = -2.5 +e = 0.1 +# test the left branch: +cc = acurve(pass00(Dy), Dy) # passes through startpoint +#cc = acurve(Dx_m(Dy)-e, Dy) +c = acurve(Dx_m(Dy) , Dy); cc.insert(c, [trafo.translate(1*dx, 0)]) +c = acurve(Dx_m(Dy)+e, Dy); cc.insert(c, [trafo.translate(2*dx, 0)]) + +# test the right branch: +#cc = acurve(Dx_p(Dy)-e, Dy, True) +#c = acurve(Dx_p(Dy) , Dy, True); cc.insert(c, [trafo.translate(1*dx, 0)]) +##c = acurve(Dx_p(Dy)+e, Dy, True); cc.insert(c, [trafo.translate(2*dx, 0)]) +#c = acurve(pass10(Dy), Dy, True); cc.insert(c, [trafo.translate(2*dx, 0)]) # passes through endpoint + +cc.writePDFfile() + Added: trunk/pyx/design/selfinter2.py =================================================================== --- trunk/pyx/design/selfinter2.py (rev 0) +++ trunk/pyx/design/selfinter2.py 2013-10-24 21:21:21 UTC (rev 3546) @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +import sys;sys.path.insert(0, "..") +from math import * +from pyx import * +# File for invalid parametrisations of Bezier curves +# Draws a sketch of the areas where invalid params are to be expected + +xmin, xmax = -2, 5 +ymin, ymax = -xmax, 2 + +g = graph.graphxy(width=10, + x=graph.axis.lin(title=r"$\Delta x$", min=xmin, max=xmax), + y=graph.axis.lin(title=r"$\Delta y$", min=ymin, max=ymax), + key=graph.key.key(pos="tr"), +) +g.plot(graph.data.function("y(x)=-1", title=None), [graph.style.line([style.linestyle.dotted])]) +d1 = g.plot(graph.data.function("x(y)=(-1-2*y-sqrt((1+2*y)**2+3))/3.0", title=r"cusp ($-$)"), [graph.style.line()]) +d2 = g.plot(graph.data.function("x(y)=(-1-2*y+sqrt((1+2*y)**2+3))/3.0", title=r"cusp ($+$)", max=-1), [graph.style.line([color.rgb.red])]) +d3 = g.plot(graph.data.function("x(y)=1.0/(3.0*(y+1))", title=r"passes through$(0,0)$", max=-1), [graph.style.line([color.rgb.green])]) +d4 = g.plot(graph.data.function("x(y)=(1+y**3)/(3.0*(1+y))", title=r"passes through$(1,0)\$", max=-1), [graph.style.line([color.rgb.blue])]) +g.doplot() + +p = d1.path << d3.path.reversed() +p.append(path.closepath()) +g.layers["filldata"].fill(p, [color.gray(0.8)]) + +p = d2.path << d4.path.reversed() +p.append(path.closepath()) +g.layers["filldata"].fill(p, [color.gray(0.8)]) + +g.writePDFfile() + + +