6

I have to create graph of function 3x^4 − 11x^3 + 12x^2 − 4x + 1 ; interval <0,1;1> .

I have just func.mp

 input gjktc.mp;
u:=1mm;

path p[];
beginfig(1);

draw (0,0)--(10u,10u);
draw (0,10u)--(10u,20u);

for i=1 upto 72:
draw (0u,0u+i*2u)--(10u,10u+i*2u);
endfor;

for i=1 upto 72:
draw (10u,10u+i*2u)--(20u,0u+i*2u);
endfor;

%penc 0.2pt;
%p2=(0,0) for i=1 upto 72:
%..(i*0.087*u,-sind(i*5)*u)
%endfor;

%draw p2 rotated 15 withcolor red;

endfig;

end;

I am sure, that is not well and not enought. Please help me. Graph has to look like this: enter image description here

1
  • I can't process this because I don't have any file named gjktc.mp. If I delete that input command the result looks nothing like your figure. If I uncomment your commented code, mpost complains at penc 0.2pt. Try to write a MWE that does not require gjktc.mp.
    – Dan
    Commented Nov 4, 2013 at 19:18

2 Answers 2

8

As I couldn't run you code, here are some general thoughts on drawing the graph of a function like 3x^4 − 11x^3 + 12x^2 − 4x + 1.

I would start by defining a macro that returns the value of this function:

vardef func (expr X) = 
  3X*X*X*X - 11X*X*X + 12X*X - 4X + 1
enddef;

Since MP can natively draw only cubics in one step, one needs to choose a subdivision of the interval into parts. Normally about 10 subintervals is plenty. Then, I prefer to compute the points on the graph and store them in an array. Taking the interval from 0 to 1 and 10 subintervals:

pair Z[]; numeric N;
N=10;
for j=0 upto N:
  Z[j] := ( j/N, func (j/N) );
endfor

Now create and draw the path:

path P;
P := Z0 for j=1 upto N: ..Z[j] endfor;
u := 10cm;
beginfig(1)
  draw P scaled u;
endfig

You will find that for some functions, the resulting path, though it passes through the required points, can get a little wobbly in between. That doesn't happen in this case, but when it does, it helps a lot if the derivative is used to supply direction information.

If you want shaded rectangles that match the graph as in your figure, the following will produce them. Create these before drawing the curve of the function, so it is visible on top of them. It is useful to first define a command that returns a rectangle path when the corners are given

vardef rect (expr LL, UR) = 
  LL -- (xpart UR, ypart LL) -- UR 
     -- (xpart LL, ypart UR) -- cycle
enddef;
beginfig(2)
  for j=1 upto N: 
    fill rect ((xpart Z[j-1],0), Z[j]) scaled u withcolor .8white;
    draw rect ((xpart Z[j-1],0), Z[j]) scaled u withcolor black;
  endfor
  draw P scaled u;
endfig
end

If you insist on shading with slanted lines, ask another question about that.

6

The graph of an arbitrary function cannot be rendered accurately using Bézier curves. For MetaPost, there is a bpolynomial package for accurately rendering polynomials of up to order 3. But since the polynomial considered here is of order 4, that package is not of much help. When drawing graphs of higher-order polynomials or non-polynomial functions, some kind of piecewise interpolation is required.

The naive approach is to construct a path by connecting some sample points using MetaPost's .. operator. This can lead to sufficient results. But even when interpolating well-behaving functions, the shape of such a path often looks strange due to arbitrary slopes and curvatures at the end-points.

Daniel Luecking's splines package provides a few macros for piecewise spline interpolation. When called with a false first parameter, the fcnspline macro computes a Bézier path such that there is no curvature at the end-points (second derivative equates to zero). This leads to a bit more ‘regular’ shapes. The package has some more features, e.g., sample points don't need to be distributed evenly or you can add your own equations to influence path shape. But the manual is really terse on all that ...

The following example shows two paths, calculated using the naive approach (violet) and calculated via fcnspline(false) (black). Note, how parentheses have been used in macro f, since * and ** operators have the same precedence in MetaPost. Drawing the hatched bars is left as an exercise. (There should be more than one hatching related MetaPost package.)

outputtemplate := "%j-%c.mps";
prologues := 2;

input splines

% Evaluate a function.
def f(expr x) =
  3(x**4) - 11(x**3) + 12(x**2) - 4x + 1
enddef;

% Construct naive Bézier path.
% Parameters are left and right interval boundaries and the number
% of sample points.
def naive_path(expr l,r,k)=
    (l, f(l))
    for i = 1 upto k-1:
      .. ((i/(k-1))[l,r], f((i/(k-1))[l,r]))
    endfor
enddef;

% Construct list of sample points for use with splines package.
% Parameters are left and right interval boundaries and the number
% of sample points.
def list(expr l,r,k)=
    (l, f(l))
    for i = 1 upto k-1:
      , ((i/(k-1))[l,r], f((i/(k-1))[l,r]))
    endfor
enddef;

% Show sample points.  Argument is a list of points.
def dodots (text t) =
  for _loc = t:
    drawdot (u*_loc) withcolor red;
  endfor
enddef;

% Output scale.
numeric u; u:=1cm;
% Adjust arrow length.
ahlength := 2bp;

path xaxis, yaxis;
xaxis := (-.1u,0)--(1.1u,0);
yaxis := (0,-.1u)--(0,1.1u);

% interval and sample points
numeric a,b,k;
a := 0.1;
b := 1;
k := 9;

beginfig(1);
  % Draw coordinate system.
  drawarrow xaxis;
  drawarrow yaxis;
  % Draw graph by naive spline construction.
  draw (naive_path(a,b,k)) scaled u withcolor .75(red+blue);
  % Draw graph using splines package.
  draw fcnspline(false)(list(a,b,k)) scaled u;
  % Draw sample points.
  dodots(list(a,b,k));
endfig;

end

function graphs of first code example

Edit - To elaborate a bit on the example

For the function considered here, the angles of gradient at x = 0.1 and x = 1 are ca. -62 and -45 degrees, resp. With what I called the naive approach above we get a path with angles of -85 and -52 degrees, with macro fcnspline(false) we get angles of -53 and -39 degrees. While both solutions are quite poor at resembling the behaviour at the end points, the splines package's solution is clearly better at x = 0.1.

That doesn't mean the fcnspline macro always generates a path with superior shape. But the zero curvature condition tends to avoid surprising shapes. Other advantages of the splines package are, it provides ready-made solutions for spline interpolation, and with the fcnspline macro there's no need to refer to the derivative of a function. Only the actual function needs to be evaluated - with acceptable results.

One trick to improve path shape (there are many) - again without referring to the derivative - is to construct a path that covers a larger interval than needed, then cutting it to the considered interval via the subpath of operator. That doesn't work for all functions, e.g., near pole points, but you get the idea.

In the example below, I've increased the interval by Δx on both sides, adding two sample points, and finally cut off the first and last path segments via subpath (1,k) of <path>. For both approaches, paths have been drawn over those from the last example in a lighter shade. Angles of gradient for all four paths are written to the .log file.

outputtemplate := "%j-%c.mps";
prologues := 2;

input splines

% Evaluate a function.
def f(expr x) =
  3(x**4) - 11(x**3) + 12(x**2) - 4x + 1
enddef;

% Construct naive Bézier path.
% Parameters are left and right interval boundaries and the number
% of sample points.
def naive_path(expr l,r,k)=
    (l, f(l))
    for i = 1 upto k-1:
      .. ((i/(k-1))[l,r], f((i/(k-1))[l,r]))
    endfor
enddef;

% Construct list of sample points for use with splines package.
% Parameters are left and right interval boundaries and the number
% of sample points.
def list(expr l,r,k)=
    (l, f(l))
    for i = 1 upto k-1:
      , ((i/(k-1))[l,r], f((i/(k-1))[l,r]))
    endfor
enddef;

% Show sample points.  Argument is a list of points.
def dodots (text t) =
  for _loc = t:
    drawdot (u*_loc) withcolor .75(red+green);
  endfor
enddef;

% Return a string containing angles of gradient at end points
% for a given path.
def anglesofgradient(expr p) =
  decimal angle((postcontrol 0 of p) - (point 0 of p))
  & " and "
  & decimal angle((point length p of p) - (precontrol length p of p))
enddef;


% Output scale.
numeric u; u := 1cm;
% Adjust arrow length.
ahlength := 2bp;

% interval and sample points
numeric a,b,k,delta;
a := 0.1;
b := 1;
k := 9;
delta := (b-a)/(k-1);

beginfig(1);
  path pn, pntwo, ps, pstwo;
  % Graph using naive spline construction.
  pn := naive_path(a, b, k);
  pntwo := subpath (1,k) of (naive_path(a-delta, b+delta, k+2));
  % Graph using splines package.
  ps := fcnspline(false)(list(a, b, k));
  pstwo := subpath (1,k) of fcnspline(false)(list(a-delta, b+delta, k+2));
  % Draw graphs.
  draw pn scaled u withcolor .75(red+blue) withpen pencircle scaled 1bp;
  draw ps scaled u withcolor black withpen pencircle scaled .75bp;
  draw pntwo scaled u withcolor .5[.75(red+blue),white] withpen pencircle scaled .4bp;
  draw pstwo scaled u withcolor .5[black,white] withpen pencircle scaled .2bp;
  % Draw sample points.
  dodots(list(a,b,k));
  % Show angles.
  show "angles at end points";
  show "pn    (dark violet) : " & anglesofgradient(pn);
  show "ps    (black)       : " & anglesofgradient(ps);
  show "pntwo (light violet): " & anglesofgradient(pntwo);
  show "pstwo (gray)        : " & anglesofgradient(pstwo);
endfig;

end

function graphs of second code example

These are the angles of gradient at the end points:

>> "pn    (dark violet) : -84.5836 and -52.30644"
>> "ps    (black)       : -53.27785 and -38.72513"
>> "pntwo (light violet): -64.68489 and -44.6875"
>> "pstwo (gray)        : -64.88264 and -46.19849"

You must log in to answer this question.

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