/*
Copyright (c) 2004-2005, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  its contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/

#define DKFIGTOO_C 1
#include "dkfig.h"

#line 40 "dkfigtoo.ctr"




/* {{{ alphabet, used for string encoding of numbers */
static char alphabet[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ" };
/* }}} */




#line 51 "dkfigtoo.ctr"
/* {{{ trace_bezier_point

  Debug output for a Bezier point.

*/
#if TRACE_DEBUG
static void
trace_bezier_point DK_P1(dk_fig_bezier_point *,bp)
{
  FILE *f;
  f = dktrace_file();
  if(f) {
    fprintf(f, "value:    x=%lg y=%lg\n", (bp->value).x, (bp->value).y);
    fprintf(f, "lcontrol: x=%lg y=%lg\n", (bp->lcontrol).x, (bp->lcontrol).y);
    fprintf(f, "rcontrol: x=%lg y=%lg\n", (bp->rcontrol).x, (bp->rcontrol).y);
  }
}
#endif
/* }}} */



/* {{{ dkfig_tool_invert_y

  Check whether y is inverted (positive numbers downward).

*/
int
dkfig_tool_invert_y DK_P1(dk_fig_drawing *,d)
{
  int back = 1;
  if(d) {
    if(d->cstype == 1) {
      if((d->opt1) & DKFIG_OPT_USE_CS_SETTING) {
        back = 0;
      }
    }
  }
  return back;
}
/* }}} */



/* {{{ null_point

  Initialize point.

*/
static void
null_point DK_P1(dk_fig_point *,p)
{
  p->x = p->y = 0.0;
}
/* }}} */



/* {{{ dkfig_tool_null_arrow

  Initialize arrowhead structure.

*/
void
dkfig_tool_null_arrow DK_P1(dk_fig_arrow *,a)
{
  if(a) {
    a->type = 0;
    a->style = 0;
    a->thickness = 0.0;
    a->w = 0.0;
    a->h = 0.0;
    a->alpha = 0.0;
    null_point(&(a->p1)); null_point(&(a->p2));
    null_point(&(a->p3)); null_point(&(a->p4));
    a->numpoints = 0;
    a->decrease  = 0.0;
  }
}
/* }}} */



/* {{{ dkfig_tool_calc_arrow

  Calculate arrowhead points.

*/
int
dkfig_tool_calc_arrow DK_P6(dk_fig_arrow *,a, dk_fig_point *,p,\
  double,phi, dk_fig_fpd *,f, dk_fig_drawing *,d, dk_fig_conversion *,c)
{
  int back = 0;
  int matherr = 0;
  double lw, dist, deltal, radius, w, h, sf, pointdec;
  
  if((a) && (p) && (f) && (d)) {		
    back = 1;
    lw = dist = a->alpha = deltal = 0.0; sf = 1.0;
    a->numpoints = 3;
    /* lw = (dkma_l_to_double(f->lt) * d->fres) / 80.0; */
    lw = dkma_div_double_ok(
      dkma_mul_double_ok(dkma_l_to_double(f->lt), d->fres, &matherr),
      (((d->opt1) & DKFIG_OPT_ENLIGHTEN_LOOK) ? 160.0 : 80.0),
      &matherr
    ); 
    a->alpha = dkma_atan2((0.5 * a->w), a->h);
    
    /*
      For beveled and rounded arrowheads, the length correction
      deltal is half of the linewidth.
    */
    deltal = 0.5 * lw;	
    /*
      For mitered arrowhead we need a trigonometric
      calculation.
    */
    if(d->ahlj == 0) {
      deltal = dkma_div_double_ok(deltal, sin(a->alpha), &matherr);
      
    }
    /*
      pointdec is the "top" corner of the arrowhead,
      a->decrease is the amount to decrease the line.
    */
    pointdec = a->decrease = deltal;
    
    /*
      Find optimum decrease if no metapost arrowheads are in use
      and the arrowhead is mitered.
    */ 
    if(d->ahlj == 0) {
      if((!((d->opt1) & DKFIG_OPT_METAPOST_ARROWHEADS)) || (c->bdnum > 1)) {
        double cmin, cmax;
	cmin = dkma_div_double_ok((0.5 * lw), tan(a->alpha), &matherr);
        cmax = dkma_mul_double_ok(2.0, pointdec, &matherr);
	a->decrease = 0.5 * dkma_add_double_ok(cmin, cmax, &matherr);
	
      }
    }
    
    w = a->w;
    h = a->h;
    /* radius = sqrt(0.25*w*w+h*h); */
    radius = sqrt(
      dkma_add_double_ok(
        (0.25 * dkma_mul_double_ok(w, w, &matherr)),
	dkma_mul_double_ok(h, h, &matherr),
        &matherr
      )
    );
    /* (a->p2).x = p->x - pointdec * cos(phi); */
    (a->p2).x = dkma_sub_double_ok(p->x, (pointdec * cos(phi)), &matherr);
    /* (a->p2).y = p->y - pointdec * sin(phi); */
    if(dkfig_tool_invert_y(d)) {
      (a->p2).y = dkma_add_double_ok(p->y, (pointdec * sin(phi)), &matherr);
    } else {
      (a->p2).y = dkma_sub_double_ok(p->y, (pointdec * sin(phi)), &matherr);
    }
    /* p1->x = p2->x + radius * cos(phi + M_PI - a->alpha); */
    (a->p1).x = dkma_add_double_ok(
      (a->p2).x, (radius * cos(phi + M_PI - a->alpha)), &matherr
    );
    if(dkfig_tool_invert_y(d)) {
      (a->p1).y = dkma_sub_double_ok(
        (a->p2).y, (radius * sin(phi + M_PI - a->alpha)), &matherr
      );
    } else {
      (a->p1).y = dkma_add_double_ok(
        (a->p2).y, (radius * sin(phi + M_PI - a->alpha)), &matherr
      );
    }
    (a->p3).x = dkma_add_double_ok(
      (a->p2).x, (radius * cos(phi + M_PI + a->alpha)), &matherr
    );
    if(dkfig_tool_invert_y(d)) {
      (a->p3).y = dkma_sub_double_ok(
        (a->p2).y, (radius * sin(phi + M_PI + a->alpha)), &matherr
      );
    } else {
      (a->p3).y = dkma_add_double_ok(
        (a->p2).y, (radius * sin(phi + M_PI + a->alpha)), &matherr
      );
    }
    switch(a->type) {
      case 2: sf = 0.75; a->numpoints = 4; break;
      case 3: sf = 1.25; a->numpoints = 4; break;
    }
    (a->p4).x = dkma_sub_double_ok(
      (a->p2).x,
      (dkma_mul_double_ok(sf, h, &matherr) * cos(phi)),
      &matherr
    );
    if(dkfig_tool_invert_y(d)) {
      (a->p4).y = dkma_add_double_ok(
        (a->p2).y,
	(dkma_mul_double_ok(sf, h, &matherr) * sin(phi)),
	&matherr
      );
    } else {
      (a->p4).y = dkma_sub_double_ok(
        (a->p2).y,
        (dkma_mul_double_ok(sf, h, &matherr) * sin(phi)),
        &matherr
      );
    }
  }
  
  if(matherr) { back = 0; }
  
  return back;
}
/* }}} */



/* {{{ dkfig_tool_null_fpd

  Initialize a pattern-fill-and-draw style info

*/
void
dkfig_tool_null_fpd DK_P1(dk_fig_fpd *,p)
{
  p->cl = 0;	/* not closed */
  p->ot = -1;	/* unknown sub type */
  p->ls = 0;	/* line style contigous */
  p->lt = 0UL;	/* no line thickness */
  p->pc = -1;	/* unknown pen color */
  p->fc = -1;	/* unknown fill color */
  p->af = -1;	/* unknown fill style */
  p->js = 0;	/* linjoin=0 */
  p->cs = 0;	/* linecap=0 */
  p->ar = 0;	/* no arrowheads */
  p->sv = 0.0;	/* style value 0.0 */
  p->ps = 0;	/* no pen style */
  dkfig_tool_null_arrow(&(p->ahf));	/* emtpy arrowhead structures */
  dkfig_tool_null_arrow(&(p->ahb));
}
/* }}} */



/* {{{ correct_fonth_for_plain_tex

  Plain TeX does not provide all the font handling instructions
  we want to use with LaTeX. So we need to apply corrections.

*/
static void
correct_fonth_for_plain_tex DK_P1(dk_fig_fonth_t *,fhptr)
{
	
	if(fhptr->handling == 4) {
	  
	  fhptr->handling = 3;
	  if((fhptr->oflags) & 4) {		
	    fhptr->fontno = fhptr->ofontno;
	    if(fhptr->fontno < 0) fhptr->fontno = 0;
	    if(fhptr->fontno > 34) fhptr->fontno = 34;
	  } else {				
	    switch(fhptr->ofontno) {
	      case 2: {				
	        fhptr->fontno = 2;
              } break;
	      case 3: {				
	        fhptr->fontno = 1;
	      } break;
	      case 4: {				
	        fhptr->fontno = 16;
	      } break;
	      case 5: {				
	        fhptr->fontno = 12;
	      } break;
	      default: {
	        fhptr->fontno = 0;		
	      } break;
	    }
	  }
	}
	if(fhptr->handling == 5) {
	  fhptr->handling = 2;
	}
	
}
/* }}} */



/* {{{ dkfig_tool_correct_for_plain_tex

  Correct all font handling structures if plain TeX is used.

*/
void
dkfig_tool_correct_for_plain_tex DK_P1(dk_fig_conversion *,c)
{
  dk_fig_drawing *d = NULL;
  dk_fig_fonth_t *fhptr = NULL;
  dk_fig_object  *o = NULL;
  
  if(c) {					
    if((c->opt1) & DKFIG_OPT_OLD_TEX) {		
      o = c->drwng;
      if(o) {
        d = (dk_fig_drawing *)(o->data);
        if(d) {					
          if(d->fonthi) {				
	    dksto_it_reset(d->fonthi);
            while((fhptr=(dk_fig_fonth_t *)dksto_it_next(d->fonthi)) != NULL) {
	      
	      correct_fonth_for_plain_tex(fhptr);
	    }
	  } else {				
	  }
        }
      }
    } else {					
    }
  }
  
}
/* }}} */



/* {{{ dkfig_tool_bb_reset

  Reset a bounding box information.

*/
void
dkfig_tool_bb_reset DK_P1(dk_fig_bb *,bb)
{		
  if(bb) {
    bb->xmin  = bb->xmax = bb->ymin = bb->ymax = 0.0;
    bb->flags = 0x00;
  }		
}
/* }}} */



/* {{{ dkfig_tool_bb_add_x/y

  Add co-ordinates.

*/
void
dkfig_tool_bb_add_x DK_P2(dk_fig_bb *,bb, double,x)
{				
  if(bb) {
    if(bb->flags & 0x01) {
      if(x < bb->xmin) bb->xmin = x;
      if(x > bb->xmax) bb->xmax = x;
    } else {
      bb->xmin = bb->xmax = x;
      bb->flags |= 0x01;
    }
  }				
}

void
dkfig_tool_bb_add_y DK_P2(dk_fig_bb *,bb, double,y)
{		
  if(bb) {
    if(bb->flags & 0x02) {
      if(y < bb->ymin) bb->ymin = y;
      if(y > bb->ymax) bb->ymax = y;
    } else {
      bb->ymin = bb->ymax = y;
      bb->flags |= 0x02;
    }
  }		
}
/* }}} */



/* {{{ dkfig_tool_bb_get_xmin/xmax/ymin/ymax

  Get the minimum and maximum values.

*/
double
dkfig_tool_bb_get_xmin DK_P1(dk_fig_bb *,bb)
{
  double back = 0.0;	
  if(bb) {
    if(bb->flags & 0x01) {
      back = bb->xmin;
    }
  }			
  return back;
}

double
dkfig_tool_bb_get_xmax DK_P1(dk_fig_bb *,bb)
{
  double back = 0.0;	
  if(bb) {
    if(bb->flags & 0x01) {
      back = bb->xmax;
    }
  }			
  return back;
}

double
dkfig_tool_bb_get_ymin DK_P1(dk_fig_bb *,bb)
{
  double back = 0.0;	
  if(bb) {
    if(bb->flags & 0x02) {
      back = bb->ymin;
    }
  }			
  return back;
}

double
dkfig_tool_bb_get_ymax DK_P1(dk_fig_bb *,bb)
{
  double back = 0.0;	
  if(bb) {
    if(bb->flags & 0x02) {
      back = bb->ymax;
    }
  }			
  return back;
}
/* }}} */



/* {{{ dkfig_tool_bb_have_x/dkfig_tool_bb_have_y

  Check whether the bounding box data contains information
  about x- or y-co-ordinates.

*/
int
dkfig_tool_bb_have_x DK_P1(dk_fig_bb *,bb)
{
  int back = 0;	
  if(bb) {
    if(bb->flags & 0x01) back = 1;
  }		
  return back;
}

int
dkfig_tool_bb_have_y DK_P1(dk_fig_bb *,bb)
{
  int back = 0;
  
  if(bb) {
    if(bb->flags & 0x02) back = 1;
  } 
  return back;
}
/* }}} */



/* {{{ dkfig_tool_bb_add

  Add a primitives bounding box src to the drawing's
  bounding box dest.

*/
void
dkfig_tool_bb_add DK_P2(dk_fig_bb *,dest, dk_fig_bb *,src)
{
  
  if(dest && src) {
    if(src->flags & 0x01) {
      dkfig_tool_bb_add_x(dest, src->xmin);
      dkfig_tool_bb_add_x(dest, src->xmax);
    }
    if(src->flags & 0x02) {
      dkfig_tool_bb_add_y(dest, src->ymin);
      dkfig_tool_bb_add_y(dest, src->ymax);
    }
  } 
}
/* }}} */



/* {{{ in_range

  Check whether the value "val" is in the given range.

*/
static int
in_range DK_P3(double,left, double,right, double,val)
{
  int back = 0;
  double min, max;
  
  if(left < right) {
    min = left; max = right;
  } else {
    min = right; max = left;
  }
  if((min <= val) && (val <= max)) {
    back = 1;
  }
  
  return back;
}
/* }}} */



/* {{{ add_point_to_bb

  Add one point to a bounding box.

*/
static void
add_point_to_bb DK_P3(dk_fig_bb *,bb, dk_fig_point *,p, double,lwh)
{
  double v;
  int matherr;
  
  matherr = 0;
  v = dkma_add_double_ok(p->x, lwh, &matherr);
  if(!matherr) { dkfig_tool_bb_add_x(bb, v); }
  matherr = 0;
  v = dkma_sub_double_ok(p->x, lwh, &matherr);
  if(!matherr) { dkfig_tool_bb_add_x(bb, v); }
  matherr = 0;
  v = dkma_add_double_ok(p->y, lwh, &matherr);
  if(!matherr) { dkfig_tool_bb_add_y(bb, v); }
  matherr = 0;
  v = dkma_sub_double_ok(p->y, lwh, &matherr);
  if(!matherr) { dkfig_tool_bb_add_y(bb, v); }
  
}
/* }}} */



/* {{{ add_arrow_to_bb

  Add arrowhead data to bounding box.

*/
static void
add_arrow_to_bb DK_P3(dk_fig_bb *,bb, dk_fig_arrow *,a, double,lwh)
{
  
  add_point_to_bb(bb, &(a->p1), lwh);
  add_point_to_bb(bb, &(a->p2), lwh);
  add_point_to_bb(bb, &(a->p3), lwh);
  if(a->numpoints > 3) {
    add_point_to_bb(bb, &(a->p4), lwh);
  }
  
}
/* }}} */



/* {{{ Several functions to find arc extrema */
static void
top_arc_inside_extrema DK_P1(dk_fig_arc_calc *,arc)
{
  
  if(in_range(arc->aa, arc->ac, 0.0)) {		
    arc->xright = dkma_add_double_ok(arc->xm, arc->ra, &(arc->mathok));
  }
  if(in_range(arc->aa, arc->ac, M_PI_2)) {	
    arc->ytop = dkma_add_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
  if(in_range(arc->aa, arc->ac, M_PI)) {	
    arc->xleft = dkma_sub_double_ok(arc->xm, arc->ra, &(arc->mathok));
  }
  
}



static void
top_arc_outside_extrema DK_P1(dk_fig_arc_calc *,arc)
{
  double min, max;
  
  if(arc->aa < arc->ac) {
    min = arc->aa; max = arc->ac;
  } else {
    min = arc->ac; max = arc->aa;
  }
  min += 2.0 * M_PI;
  if(in_range(min, max, 0.0)) {		
    arc->xright = dkma_add_double_ok(arc->xm, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, M_PI_2)) {	
    arc->ytop = dkma_add_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, M_PI)) {	
    arc->xleft = dkma_sub_double_ok(arc->xm, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, (M_PI + M_PI_2))) {	
    arc->ybottom = dkma_sub_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, (2.0 * M_PI))) {	
    arc->xright = dkma_add_double_ok(arc->xm, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, (2.0 * M_PI + M_PI_2))) {
    arc->ytop = dkma_add_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, (3.0 * M_PI))) {
    arc->xleft = dkma_sub_double_ok(arc->xm, arc->ra, &(arc->mathok));
  }
  
}



static void
top_arc_extrema DK_P1(dk_fig_arc_calc *,arc)
{
  int inside;
  
  inside = in_range(arc->aa, arc->ac, arc->ab);
  if(inside) {
    top_arc_inside_extrema(arc);
    if(arc->aa <= arc->ac) {
      arc->astart = arc->aa; arc->alength = arc->ac - arc->aa;
    } else {
      arc->astart = arc->ac; arc->alength = arc->aa - arc->ac;
      arc->revert = 1;
    }
  } else {
    top_arc_outside_extrema(arc);
    if(arc->ac <= arc->aa) {
      arc->astart = arc->aa;
      arc->alength = 2 * M_PI + arc->ac - arc->aa;
    } else {
      arc->astart = arc->ac;
      arc->alength = 2 * M_PI + arc->aa - arc->ac;
      arc->revert = 1;
    }
  }
  
}



static void
bottom_arc_inside_extrema DK_P1(dk_fig_arc_calc *,arc)
{
  if(in_range(arc->aa, arc->ac, (0.0 - M_PI_2))) {	
    arc->ybottom = dkma_sub_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
}



static void
bottom_arc_outside_extrema DK_P1(dk_fig_arc_calc *,arc)
{
  double min, max;
  if(arc->aa < arc->ac) {
    min = arc->aa; max = arc->ac;
  } else {
    min = arc->ac; max = arc->aa;
  }
  min += 2.0 * M_PI;
  if(in_range(min, max, (0.0 - M_PI))) {
    arc->xleft = dkma_sub_double_ok(arc->xm, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, (0.0 - M_PI_2))) {
    arc->ybottom = dkma_sub_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, 0.0)) {
    arc->xright = dkma_add_double_ok(arc->xm, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, M_PI_2)) {
    arc->ytop = dkma_add_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, M_PI)) {
    arc->xleft = dkma_sub_double_ok(arc->xm, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, (M_PI + M_PI_2))) {
    arc->ybottom = dkma_sub_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
  if(in_range(min, max, (2.0 * M_PI))) {
    arc->xright = dkma_add_double_ok(arc->xm, arc->ra, &(arc->mathok));
  }
}



static void
bottom_arc_extrema DK_P1(dk_fig_arc_calc *,arc)
{
  int inside;
  
  inside = in_range(arc->aa, arc->ac, arc->ab);
  if(inside) {
    bottom_arc_inside_extrema(arc);
    if(arc->aa <= arc->ac) {
      arc->astart = arc->aa; arc->alength = arc->ac - arc->aa;
    } else {
      arc->astart = arc->ac; arc->alength = arc->aa - arc->ac;
      arc->revert = 1;
    }
  } else {
    bottom_arc_outside_extrema(arc);
    if(arc->ac <= arc->aa) {
      arc->astart = arc->aa;
      arc->alength = 2.0 * M_PI + arc->ac - arc->aa;
    } else {
      arc->astart = arc->ac;
      arc->alength = 2.0 * M_PI + arc->aa - arc->ac;
      arc->revert = 1;
    }
  }
  
}



static void
left_side_extrema DK_P3(dk_fig_arc_calc *,arc, double,pos, double,neg)
{
  if(in_range(pos, (neg + 2.0 * M_PI), M_PI_2)) {
    arc->ytop = dkma_add_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
  if(in_range(pos, (neg + 2.0 * M_PI), (0.0 - M_PI_2))) {
    arc->ybottom = dkma_sub_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
  if(in_range(pos, (neg + 2.0 * M_PI), M_PI)) {
    arc->xleft = dkma_sub_double_ok(arc->xm, arc->ra, &(arc->mathok)); 
  }
}



static void
right_side_extrema DK_P3(dk_fig_arc_calc *,arc, double,pos, double,neg)
{
  if(in_range(neg,pos,0.0)) {
    arc->xright = dkma_add_double_ok(arc->xm, arc->ra, &(arc->mathok));
  }
  if(in_range(neg, pos, (0.0 - M_PI_2))) {
    arc->ybottom = dkma_sub_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
  if(in_range(neg, pos, M_PI_2)) {
    arc->ytop = dkma_add_double_ok(arc->ym, arc->ra, &(arc->mathok));
  }
}



static void
side_arc_extrema DK_P1(dk_fig_arc_calc *,arc)
{
  double pos, neg;
  if(arc->aa < 0.0) {
    neg = arc->aa; pos = arc->ac;
  } else {
    neg = arc->ac; pos = arc->aa;
  }
  if(arc->ab >= 0.0) {
    if(arc->ab >= pos) {
      left_side_extrema(arc, pos, neg);
    } else {
      right_side_extrema(arc, pos, neg);
    }
  } else {
    if(arc->ab <= neg) {
      left_side_extrema(arc, pos, neg);
    } else {
      right_side_extrema(arc, pos, neg);
    }
  }
}
/* }}} */



/* {{{ arc_extrema

  Search for minimum and maximum x and y.

*/
static void
arc_extrema DK_P1(dk_fig_arc_calc *,arc)
{
  if(arc->aa >= 0.0) {
    if(arc->ac >= 0.0) {
      top_arc_extrema(arc);
    } else {
      side_arc_extrema(arc);
      if((arc->ab >= arc->aa) || (arc->ab <= arc->ac)) {
        arc->astart = arc->aa;
	arc->alength = 2.0 * M_PI + arc->ac - arc->aa;
      } else {
        arc->astart = arc->ac;
	arc->alength = arc->aa - arc->ac;
	arc->revert = 1;
      }
    }
  } else {
    if(arc->ac >= 0.0) {	
      side_arc_extrema(arc);
      if((arc->ab >= arc->ac) || (arc->ab <= arc->aa)) {
        arc->astart = arc->ac;
	arc->alength = 2.0 * M_PI + arc->aa - arc->ac;
	arc->revert = 1;	
      } else {
        arc->astart = arc->aa;	
	arc->alength = arc->ac - arc->aa;
      }
    } else {
      bottom_arc_extrema(arc);
    }
  }
}
/* }}} */



/* {{{ calculate_arc_center

  Calculate the center point for three given points.
  On success the arc->mathok component is unchanged.
  On errors (2 or three equal points or three points
  on on line) the component is set.

*/
static void
calculate_arc_center DK_P1(dk_fig_arc_calc *,arc)
{
  double t, zaehler, nenner;
  
#if NO_MATH_CHECK
  zaehler = 0.5 * ((arc->xb - arc->xc) * (arc->xc - arc->xa) - (arc->yc - arc->yb) * (arc->yc - arc->ya));
  
  nenner = (arc->yb - arc->ya) * (arc->xb - arc->xc) - (arc->yc - arc->yb) * (arc->xa - arc->xb);
  
  t = zaehler / nenner; 
  arc->xm = 0.5 * (arc->xa + arc->xb) + t * (arc->yb - arc->ya);
  arc->ym = 0.5 * (arc->ya + arc->yb) + t * (arc->xa - arc->xb);
  
#endif
  zaehler = 0.5 * dkma_sub_double_ok(
    dkma_mul_double_ok(
      dkma_sub_double_ok(arc->xb, arc->xc, &(arc->mathok)),
      dkma_sub_double_ok(arc->xc, arc->xa, &(arc->mathok)),
      &(arc->mathok)
    ),
    dkma_mul_double_ok(
      dkma_sub_double_ok(arc->yc, arc->yb, &(arc->mathok)),
      dkma_sub_double_ok(arc->yc, arc->ya, &(arc->mathok)),
      &(arc->mathok)
    ),
    &(arc->mathok)
  ); 
  nenner = dkma_sub_double_ok(
    dkma_mul_double_ok(
      dkma_sub_double_ok(arc->yb, arc->ya, &(arc->mathok)),
      dkma_sub_double_ok(arc->xb, arc->xc, &(arc->mathok)),
      &(arc->mathok)
    ),
    dkma_mul_double_ok(
      dkma_sub_double_ok(arc->yc, arc->yb, &(arc->mathok)),
      dkma_sub_double_ok(arc->xa, arc->xb, &(arc->mathok)),
      &(arc->mathok)
    ),
    &(arc->mathok)
  ); 
  t = dkma_div_double_ok(zaehler, nenner, &(arc->mathok));
  
  arc->xm = dkma_add_double_ok(
    (0.5 * dkma_add_double_ok(arc->xa, arc->xb, &(arc->mathok))),
    dkma_mul_double_ok(
      t,
      dkma_sub_double_ok(arc->yb, arc->ya, &(arc->mathok)),
      &(arc->mathok)
    ),
    &(arc->mathok)
  );
  arc->ym = dkma_add_double_ok(
    (0.5 * dkma_add_double_ok(arc->ya, arc->yb, &(arc->mathok))),
    dkma_mul_double_ok(
      t,
      dkma_sub_double_ok(arc->xa, arc->xb, &(arc->mathok)),
      &(arc->mathok)
    ),
    &(arc->mathok)
  );
  
  
}
/* }}} */



/* {{{ dkfig_tool_arc_calc

  Calculate xleft, xright, ytop and ybottom for an arc
  in mathematical coordinates (positive y's go upwards).

*/
void
dkfig_tool_arc_calc DK_P1(dk_fig_arc_calc *,arc)
{
  double detx, dety;
  
  arc->xm = arc->ym = arc->ra = 0.0;
  arc->aa = arc->ab = arc->ac = 0.0;
  arc->astart = arc->alength = 0.0;
  arc->revert = 0;
  arc->xleft = arc->xright = arc->ytop = arc->ybottom = 0.0;
  arc->mathok = 0;
  calculate_arc_center(arc);
  if(!(arc->mathok)) {
    
    detx = dkma_sub_double_ok(arc->xm, arc->xa, &(arc->mathok));
    dety = dkma_sub_double_ok(arc->ym, arc->ya, &(arc->mathok));
    arc->ra = sqrt(
      dkma_add_double_ok(
        dkma_mul_double_ok(detx, detx, &(arc->mathok)),
	dkma_mul_double_ok(dety, dety, &(arc->mathok)),
        &(arc->mathok)
      )
    ); 
    arc->aa = dkma_atan2(
      dkma_sub_double_ok(arc->ya, arc->ym, &(arc->mathok)),
      dkma_sub_double_ok(arc->xa, arc->xm, &(arc->mathok))
    ); 
    arc->ab = dkma_atan2(
      dkma_sub_double_ok(arc->yb, arc->ym, &(arc->mathok)),
      dkma_sub_double_ok(arc->xb, arc->xm, &(arc->mathok))
    ); 
    arc->ac = dkma_atan2(
      dkma_sub_double_ok(arc->yc, arc->ym, &(arc->mathok)),
      dkma_sub_double_ok(arc->xc, arc->xm, &(arc->mathok))
    ); 
    arc->xleft = arc->xright = arc->xa;
    if(arc->xb < arc->xleft) arc->xleft = arc->xb;
    if(arc->xb > arc->xright) arc->xright = arc->xb;
    if(arc->xc < arc->xleft) arc->xleft = arc->xc;
    if(arc->xc > arc->xright) arc->xright = arc->xc;
    arc->ytop  = arc->ybottom = arc->ya;
    if(arc->yb < arc->ybottom) arc->ybottom = arc->yb;
    if(arc->yb > arc->ytop)    arc->ytop    = arc->yb;
    if(arc->yc < arc->ybottom) arc->ybottom = arc->yc;
    if(arc->yc > arc->ytop)    arc->ytop    = arc->yc;
    arc_extrema(arc);
  } 
}
/* }}} */



/* {{{ make_lwh

  Get half of the linewidth.

*/
static double
make_lwh DK_P2(long,th, dk_fig_drawing *,d)
{
  double back = 0.0;
  /* 2004/04/12 bug fixed: multiplication fres * was missing */
  back = d->fres * dkma_l_to_double(th) / 160.0;
  if((d->opt1) & DKFIG_OPT_ENLIGHTEN_LOOK) {
    back *= 0.5;
  }
  return back;
}
/* }}} */



/* {{{ dkfig_tool_bb_arc

  Calculate bounding box information for an arc.

*/
void
dkfig_tool_bb_arc DK_P2(dk_fig_object *,o, dk_fig_drawing *,d)
{
  dk_fig_arc_calc arc;
  dk_fig_arc *p;
  double lwh = 0.0, value = 0.0;
  int mathok = 0; 
  
  if(o) { dkfig_tool_bb_reset(&(o->dbb)); }
  if(o && d) {
    p = (dk_fig_arc *)(o->data);
    if(p) {
      lwh = make_lwh((o->fpd).lt, d);
      arc.xa = arc.ya = arc.xb = arc.yb = arc.xc = arc.yc = 0.0;
      arc.xa = dkma_l_to_double(p->x1);
      arc.ya = 0.0 - dkma_l_to_double(p->y1);
      arc.xb = dkma_l_to_double(p->x2);
      arc.yb = 0.0 - dkma_l_to_double(p->y2);
      arc.xc = dkma_l_to_double(p->x3);
      arc.yc = 0.0 - dkma_l_to_double(p->y3);
      dkfig_tool_arc_calc(&arc);
      if(!(arc.mathok)) {
	dkfig_tool_bb_add_x(&(o->dbb), arc.xleft);
	dkfig_tool_bb_add_x(&(o->dbb), arc.xright);
	dkfig_tool_bb_add_y(&(o->dbb), (0.0 - arc.ytop));
	dkfig_tool_bb_add_y(&(o->dbb), (0.0 - arc.ybottom));
	mathok = 0;
	value = dkma_add_double_ok(arc.xright, lwh, &mathok);
	if(!mathok) {
	  dkfig_tool_bb_add_x(&(o->dbb), value);
	}
	mathok = 0;
	value = dkma_sub_double_ok(arc.xleft, lwh, &mathok);
	if(!mathok) {
	  dkfig_tool_bb_add_x(&(o->dbb), value);
	}
	mathok = 0;
	value = dkma_add_double_ok((0.0 - arc.ytop), lwh, &mathok);
	if(!mathok) {
	  dkfig_tool_bb_add_y(&(o->dbb), value);
	}
	mathok = 0;
	value = dkma_sub_double_ok((0.0 - arc.ytop), lwh, &mathok);
	if(!mathok) {
	  dkfig_tool_bb_add_y(&(o->dbb), value);
	}
	mathok = 0;
	value = dkma_add_double_ok((0.0 - arc.ybottom), lwh, &mathok);
	if(!mathok) {
	  dkfig_tool_bb_add_y(&(o->dbb), value);
	}
	mathok = 0;
	value = dkma_sub_double_ok((0.0 - arc.ybottom), lwh, &mathok);
	if(!mathok) {
	  dkfig_tool_bb_add_y(&(o->dbb), value);
	}
      }
      if((o->fpd).ar & 1) {
        add_arrow_to_bb(&(o->dbb), &((o->fpd).ahf), lwh);
      }
      if((o->fpd).ar & 2) {
        add_arrow_to_bb(&(o->dbb), &((o->fpd).ahb), lwh);
      }
    } 
  } 
}
/* }}} */



/* {{{ dkfig_tool_bb_polyline

  Calculate bounding box data for a polyline.

*/
void
dkfig_tool_bb_polyline DK_P2(dk_fig_object *,o, dk_fig_drawing *,d)
{
  dk_fig_polyline *p;
  double lwh = 0.0;
  
  long *lptr = NULL;
  size_t i = 0;
  double value;
  
  if(o) { dkfig_tool_bb_reset(&(o->dbb)); }
  if(o && d) {
    p = (dk_fig_polyline *)(o->data);
    if(p) {
      lwh = make_lwh((o->fpd).lt, d);
      lptr = p->xvalues;
      if(lptr) {
        for(i = 0; i < p->npoints; i++) {
	  value = dkma_l_to_double(*(lptr++));
	  dkfig_tool_bb_add_x(&(o->dbb), value);
	  dkfig_tool_bb_add_x(&(o->dbb), (value-lwh));
	  dkfig_tool_bb_add_x(&(o->dbb), (value+lwh));
	}
      }
      lptr = p->yvalues;
      if(lptr) {
        for(i = 0; i < p->npoints; i++) {
	  value = dkma_l_to_double(*(lptr++));
	  dkfig_tool_bb_add_y(&(o->dbb), value);
	  dkfig_tool_bb_add_y(&(o->dbb), (value-lwh));
	  dkfig_tool_bb_add_y(&(o->dbb), (value+lwh));
	}
      }
      if((o->fpd).ar & 1) {
        add_arrow_to_bb(&(o->dbb), &((o->fpd).ahf), lwh);
      }
      if((o->fpd).ar & 2) {
        add_arrow_to_bb(&(o->dbb), &((o->fpd).ahb), lwh);
      }
    } 
  } 
}
/* }}} */



/* {{{ dkfig_tool_bb_ellipse

  Calculate bounding box data for an ellipse.

*/
void
dkfig_tool_bb_ellipse DK_P2(dk_fig_object *,o, dk_fig_drawing *,d)
{
  dk_fig_rot_ellipse e;
  dk_fig_ellipse *p;
  double lwh;
  
  if(o) { dkfig_tool_bb_reset(&(o->dbb)); }
  if(o && d) {
    p = (dk_fig_ellipse *)(o->data);
    if(p) {
      lwh    = make_lwh((o->fpd).lt, d);
      e.rx   = dkma_l_to_double(p->radiusx);
      e.ry   = dkma_l_to_double(p->radiusy);
      e.phi0 = p->angle;
      dkfig_tool_rotated_ellipse(&e);
      /* centerx, centery */
      dkfig_tool_bb_add_x(&(o->dbb), dkma_l_to_double(p->centerx));
      dkfig_tool_bb_add_y(&(o->dbb), dkma_l_to_double(p->centery));
      dkfig_tool_bb_add_x(&(o->dbb), (dkma_l_to_double(p->centerx) - e.w - lwh));
      dkfig_tool_bb_add_y(&(o->dbb), (dkma_l_to_double(p->centery) - e.h - lwh));
      dkfig_tool_bb_add_x(&(o->dbb), (dkma_l_to_double(p->centerx) + e.w + lwh));
      dkfig_tool_bb_add_y(&(o->dbb), (dkma_l_to_double(p->centery) + e.h + lwh));
    }
  }
  
  
}
/* }}} */



/* {{{ dkfig_tool_bb_spline

  Calculate bounding box data for a spline.

*/
void
dkfig_tool_bb_spline DK_P2(dk_fig_object *,o, dk_fig_drawing *,d)
{
  int matherr;
  dk_fig_spline *sptr;	dk_fig_bezier_point *bptr;
  size_t bsegs, cbseg, lindex, rindex;
  double lwh, value, d0, d1;
  dk_bspline_t bsp;
  
  lwh = 0.0;
  if(o) {	
  dkfig_tool_bb_reset(&(o->dbb));
  if(d) {	
  sptr = (dk_fig_spline *)(o->data);
  if(sptr) {
  bptr = sptr->bpoints;
  if(bptr) {	
    lwh = make_lwh((o->fpd).lt, d);
    /* 2004/05/24 bug fixed: npoints was used instead of nbpoints */
    bsegs = sptr->nbpoints - 1;
    switch((o->fpd).st) {
      case 1: case 3: case 5: bsegs = sptr->nbpoints; break;
    }
    for(cbseg = 0; cbseg < bsegs; cbseg++) {
      lindex = cbseg;
      rindex = lindex + 1;
      if(lindex >= sptr->nbpoints) { lindex = lindex - sptr->nbpoints; }
      if(rindex >= sptr->nbpoints) { rindex = rindex - sptr->nbpoints; }
      matherr = 0;
      value = dkma_add_double_ok((bptr[lindex]).value.x, lwh, &matherr);
      if(!matherr) { dkfig_tool_bb_add_x(&(o->dbb), value); }
      matherr = 0;
      value = dkma_sub_double_ok((bptr[lindex]).value.x, lwh, &matherr);
      if(!matherr) { dkfig_tool_bb_add_x(&(o->dbb), value); }
      matherr = 0;
      value = dkma_add_double_ok((bptr[lindex]).value.y, lwh, &matherr);
      if(!matherr) { dkfig_tool_bb_add_y(&(o->dbb), value); }
      matherr = 0;
      value = dkma_sub_double_ok((bptr[lindex]).value.y, lwh, &matherr);
      if(!matherr) { dkfig_tool_bb_add_y(&(o->dbb), value); }
      matherr = 0;
      value = dkma_add_double_ok((bptr[rindex]).value.x, lwh, &matherr);
      if(!matherr) { dkfig_tool_bb_add_x(&(o->dbb), value); }
      matherr = 0;
      value = dkma_sub_double_ok((bptr[rindex]).value.x, lwh, &matherr);
      if(!matherr) { dkfig_tool_bb_add_x(&(o->dbb), value); }
      matherr = 0;
      value = dkma_add_double_ok((bptr[rindex]).value.y, lwh, &matherr);
      if(!matherr) { dkfig_tool_bb_add_y(&(o->dbb), value); }
      matherr = 0;
      value = dkma_sub_double_ok((bptr[rindex]).value.y, lwh, &matherr); 
      if(!matherr) { dkfig_tool_bb_add_y(&(o->dbb), value); }
      
      
      d0 = ((bptr[lindex]).rcontrol.x - (bptr[lindex]).value.x) * 3.0;
      d1 = ((bptr[rindex]).value.x - (bptr[rindex]).lcontrol.x) * 3.0;
      if(dkbsp_minmax(&bsp, (bptr[lindex]).value.x, d0, (bptr[rindex]).value.x, d1)) {
        matherr = 0;
	value = dkma_add_double_ok(bsp.min, lwh, &matherr);
	if(!matherr) { dkfig_tool_bb_add_x(&(o->dbb), value); }
	matherr = 0;
	value = dkma_sub_double_ok(bsp.min, lwh, &matherr);
	if(!matherr) { dkfig_tool_bb_add_x(&(o->dbb), value); }
        matherr = 0;
	value = dkma_add_double_ok(bsp.max, lwh, &matherr);
	if(!matherr) { dkfig_tool_bb_add_x(&(o->dbb), value); }
	matherr = 0;
	value = dkma_sub_double_ok(bsp.max, lwh, &matherr);
	if(!matherr) { dkfig_tool_bb_add_x(&(o->dbb), value); }
      }
      
      
      d0 = ((bptr[lindex]).rcontrol.y - (bptr[lindex]).value.y) * 3.0;
      d1 = ((bptr[rindex]).value.y - (bptr[rindex]).lcontrol.y) * 3.0;
      if(dkbsp_minmax(&bsp, (bptr[lindex]).value.y, d0, (bptr[rindex]).value.y, d1)) {
        matherr = 0;
	value = dkma_add_double_ok(bsp.min, lwh, &matherr);
	if(!matherr) { dkfig_tool_bb_add_y(&(o->dbb), value); }
	matherr = 0;
	value = dkma_sub_double_ok(bsp.min, lwh, &matherr);
	if(!matherr) { dkfig_tool_bb_add_y(&(o->dbb), value); }
        matherr = 0;
	value = dkma_add_double_ok(bsp.max, lwh, &matherr);
	if(!matherr) { dkfig_tool_bb_add_y(&(o->dbb), value); }
	matherr = 0;
	value = dkma_sub_double_ok(bsp.max, lwh, &matherr);
	if(!matherr) { dkfig_tool_bb_add_y(&(o->dbb), value); }
      }
    }
  }
  if((o->fpd).ar & 1) {
    add_arrow_to_bb(&(o->dbb), &((o->fpd).ahf), lwh);
  }
  if((o->fpd).ar & 2) {
    add_arrow_to_bb(&(o->dbb), &((o->fpd).ahb), lwh);
  }
  }
  }
  }
  
  
}
/* }}} */



/* {{{

  Distance of point (xp,yp) to a line through
  (0,0)--(x0,y0).

*/
double
dkfig_tool_dist_pt_to_line0 DK_P4(double,xp,double,yp,double,x0,double,y0)
{
  double back = 0.0;
  int    mathok = 0;
  double tF, xF, yF, res, deltax, deltay;
  
  tF = dkma_div_double_ok(
    dkma_add_double_ok(
      dkma_mul_double_ok(yp,y0,&mathok),
      dkma_mul_double_ok(xp,x0,&mathok),
      &mathok
    ),
    dkma_add_double_ok(
      dkma_mul_double_ok(y0,y0,&mathok),
      dkma_mul_double_ok(x0,x0,&mathok),
      &mathok
    ),
    &mathok
  );
  xF = dkma_mul_double_ok(tF,x0,&mathok);
  yF = dkma_mul_double_ok(tF,y0,&mathok);
  deltax = dkma_sub_double_ok(xF,xp,&mathok);
  deltay = dkma_sub_double_ok(yF,yp,&mathok);
  res = sqrt(
    dkma_add_double_ok(
      dkma_mul_double_ok(deltax,deltax,&mathok),
      dkma_mul_double_ok(deltay,deltay,&mathok),
      &mathok
    )
  );
  if(!mathok) {
    back = res;
  } 
  return back;
}
/* }}} */



/* {{{ dkfig_tool_rotated_ellipse

  Calculate width and height of a rotated ellipse.
  Inputs are rx, ry and phi0 (in radians).
  Output is w and h (single width and height from
  center to top and from center to right).

*/
void
dkfig_tool_rotated_ellipse DK_P1(dk_fig_rot_ellipse *,e)
{
  double th, tw, w, h, xp, yp;
  int mathok;
  
  if(e) { 
    mathok = 0; e->h = 0.0; e->w = 0.0;
    th = dkma_atan2(
      dkma_mul_double_ok(e->ry, cos(e->phi0), &mathok),
      dkma_mul_double_ok(e->rx, sin(e->phi0), &mathok)
    ); 
    tw = dkma_atan2(
      (0.0 - dkma_mul_double_ok(e->ry, sin(e->phi0), &mathok)),
      dkma_mul_double_ok(e->rx, cos(e->phi0), &mathok)
    ); 
    if(!mathok) {
      xp = dkma_mul_double_ok(e->rx, cos(th), &mathok);
      yp = dkma_mul_double_ok(e->ry, sin(th), &mathok);
      h = dkfig_tool_dist_pt_to_line0(xp,yp,cos(e->phi0),(0.0-sin(e->phi0)));
      xp = dkma_mul_double_ok(e->rx, cos(tw), &mathok);
      yp = dkma_mul_double_ok(e->ry, sin(tw), &mathok);
      w = dkfig_tool_dist_pt_to_line0(
        xp,yp,cos(M_PI_2 - e->phi0),sin(M_PI_2 - e->phi0)
      ); 
      if(!mathok) {
        e->h = h; e->w = w;
      }
    } 
  } 
}
/* }}} */



/* {{{ null_fig_point

  Initialize data structure for one Fig point.

*/
static void
null_fig_point DK_P1(dk_fig_point *,pptr)
{
  pptr->x = pptr->y = 0.0;
}
/* }}} */



/* {{{ null_bezier_point

  Initialize data structure for one Bezier point.

*/
static void
null_bezier_point DK_P1(dk_fig_bezier_point *,pptr)
{
  null_fig_point(&(pptr->value));
  null_fig_point(&(pptr->lcontrol));
  null_fig_point(&(pptr->rcontrol));
}
/* }}} */



/* {{{ null_all_bezier_points

  Initialize all Bezier points in a spline object.

*/
static void
null_all_bezier_points DK_P2(dk_fig_bezier_point *,points, size_t,nelem)
{
  size_t i; dk_fig_bezier_point *ptr;
  i = nelem; ptr = points;
  while(i--) {
    null_bezier_point(ptr++);
  }
}
/* }}} */



/* {{{ spline_iteration_t

  Used for Newton iteration scheme.

*/
typedef struct {
  dk_fig_object *o;
  dk_fig_drawing*d;
  dk_fig_spline *s;
  unsigned long min;
  unsigned long max;
  int		 end;		/* in */
  double	 t;		/* in,out */
  double	 value;		/* out */
  double	 dvdt;		/* out */
  double	 dist;		/* in */
  double	 ox;		/* in */
  double	 oy;		/* in */
  /* internals */
  double	 xl;		/* in,out */
  double	 xr;		/* in,out */
  double	 xlp;		/* in,out */
  double	 xrm;		/* in,out */
  double	 yl;		/* in,out */
  double	 yr;		/* in,out */
  double	 ylp;		/* in,out */
  double	 yrm;		/* in,out */
  double	 tcur;		/* in,out */
  unsigned long	 currentstep;	/* in,out */
  size_t	 lindex;	/* in */
  size_t	 rindex;	/* in */
  double	 xcurrent;	/* in, out */
  double	 ycurrent;	/* in, out */
  double	 dxdt;		/* in */
  double	 dydt;		/* in */
} spline_iteration_t;
/* }}} */



/* {{{ do_calculate

  For a current value t calculate f(t), df/dt at t
  and t_next for Newton iteration scheme.

*/
static int
do_calculate DK_P1(spline_iteration_t *,si)
{
  int back = 0;
  int me   = 0;
  dk_bspline_t bsp;
  double x, y, dxdt, dydt, sqrv, dx, dy;
  
  if(dkbsp_for_t(&bsp,si->xl,si->xlp,si->xr,si->xrm,si->tcur)) {
    x = si->xcurrent = bsp.xvalue; dxdt = si->dxdt = bsp.dxdt;
    if(dkbsp_for_t(&bsp,si->yl,si->ylp,si->yr,si->yrm,si->tcur)) {
      y = si->ycurrent = bsp.xvalue; dydt = si->dydt = bsp.dxdt;
      back = 1;
      dx = dkma_sub_double_ok(x, si->ox, &me);
      dy = dkma_sub_double_ok(y, si->oy, &me);	
      sqrv = sqrt(
        dkma_add_double_ok(
	  dkma_mul_double_ok(dx, dx, &me),
	  dkma_mul_double_ok(dy, dy, &me),
	  &me
	)
      );					
      si->value = dkma_sub_double_ok(sqrv, si->dist, &me);
      si->dvdt  = dkma_div_double_ok(
        dkma_add_double_ok(
	  dkma_mul_double_ok(dx, dxdt, &me),
	  dkma_mul_double_ok(dy, dydt, &me),
	  &me
	),
	sqrv,
	&me
      );					
    }
  }
  if(me) { back = 0; }
  
  return back;
}
/* }}} */



/* {{{ do_iteration

  Run an iteration to shorten a spline for arrowheads.
  The Newton iteration scheme is used.

*/
static int
do_iteration DK_P1(spline_iteration_t *,si)
{
  int back = 0;
  int cc = 0;
  int me = 0;	/* math error */
  double oldt, oldvalue;
  
  oldt = oldvalue = 0.0;
  si->currentstep = 0UL; cc = 1;
  while(cc && (!back)) {
    
    /* set lindex, rindex */
    if(si->t <= dkma_l_to_double((long)((si->s)->nbpoints) - 1L)) {
      if(si->t >= 0.0) {
	si->lindex = (size_t)dkma_double_to_l_ok(floor(si->t), &me);
	si->rindex = si->lindex + 1;
	if(si->rindex >= (si->s)->nbpoints) {
	  si->rindex = (si->s)->nbpoints - 1;
	  si->lindex = si->rindex - 1;
	}
      } else {
	cc = 0;	
      }
    } else {
      cc = 0;	
    }
    if(me) { cc = 0; }
    if(cc) {
      si->xl   = ((si->s)->bpoints)[si->lindex].value.x;
      si->yl   = ((si->s)->bpoints)[si->lindex].value.y;
      si->xr   = ((si->s)->bpoints)[si->rindex].value.x;
      si->yr   = ((si->s)->bpoints)[si->rindex].value.y;
      si->xlp  = ((si->s)->bpoints)[si->lindex].rcontrol.x;
      si->ylp  = ((si->s)->bpoints)[si->lindex].rcontrol.y;
      si->xrm  = ((si->s)->bpoints)[si->rindex].lcontrol.x;
      si->yrm  = ((si->s)->bpoints)[si->rindex].lcontrol.y;
      si->tcur = si->t - dkma_l_to_double((long)(si->lindex));
      
      
      
      if(do_calculate(si)) {
        
	
        if(si->currentstep) {			
	  if(si->currentstep >= si->min) {	
	  if(fabs(si->value) < DKFIG_EPSILON) {	
	  if(fabs(dkma_sub_double_ok(si->t, oldt, &me)) < DKFIG_EPSILON) {
	  if(!me) {				
	    back = 1;	
	  }
	  }
	  }
	  }
	}
	if(!back) {		
	  oldvalue = si->value;
	  oldt     = si->t;
	}
      } else {		
        cc = 0;
      }
    }
    if(si->currentstep >= si->max) {
      cc = 0;	
    }
    if((cc) && (!back)) {
      si->currentstep += 1UL;
      si->t = dkma_sub_double_ok(
        si->t,
	dkma_div_double_ok(si->value, si->dvdt, &me),
	&me
      );
      if(me) { cc = 0; }
    }
    
  }
  if(me) {
    back = 0;
  }
  
  return back;
}
/* }}} */



/* {{{ distance

  Calculate distance between two points.

*/
static double
distance DK_P5(double,x0, double,x1, double,y0, double,y1, int *,me)
{
  double back;
  double dx, dy;
  dx = dkma_sub_double_ok(x1,x0,me);
  dy = dkma_sub_double_ok(y1,y0,me);
  back = dkma_add_double_ok(
    dkma_mul_double_ok(dx,dx,me),
    dkma_mul_double_ok(dy,dy,me),
    me
  );
  back = sqrt(back);
  return back;
}
/* }}} */



/* {{{ correct_bezier_point

  Setup partial bezier segments on the ends
  for arrowheads.

*/

static void
correct_bezier_point DK_P4(dk_fig_bezier_point *,bp,\
  double,width, int,side, int *,me)
{
  
  
#line 1662 "dkfigtoo.ctr"
  if(side) {	
    (bp->lcontrol).x = dkma_sub_double_ok(
      (bp->value).x,
      dkma_mul_double_ok(
        width,
	dkma_sub_double_ok(
	  (bp->value).x,
	  (bp->lcontrol).x,
	  me
	),
        me
      ),
      me
    );
    (bp->lcontrol).y = dkma_sub_double_ok(
      (bp->value).y,
      dkma_mul_double_ok(
        width,
	dkma_sub_double_ok(
	  (bp->value).y,
	  (bp->lcontrol).y,
	  me
	),
        me
      ),
      me
    );
  } else {	
    (bp->rcontrol).x = dkma_add_double_ok(
      (bp->value).x,
      dkma_mul_double_ok(
        width,
	dkma_sub_double_ok(
	  (bp->rcontrol).x,
	  (bp->value).x,
	  me
	),
	me
      ),
      me
    );
    (bp->rcontrol).y = dkma_add_double_ok(
      (bp->value).y,
      dkma_mul_double_ok(
        width,
	dkma_sub_double_ok(
	  (bp->rcontrol).y,
	  (bp->value).y,
	  me
	),
	me
      ),
      me
    );
  }
  
#line 1718 "dkfigtoo.ctr"
  
}
/* }}} */



/* {{{ setup_forward_partial

  Correct Bezier spline segment at the and
  to apply forward arrowhead.

*/
static int
setup_forward_partial DK_P3(dk_fig_object *,o,\
  dk_fig_drawing *,d, dk_fig_spline *,sptr)
{
  int back = 1;
  int me   = 0;
  double width;
  
  width = dkma_sub_double_ok(
    sptr->te,
    dkma_l_to_double((long)(sptr->normale)),
    &me
  ); 
DK_MEMCPY(&(sptr->pe2),&((sptr->bpoints)[sptr->normale]),sizeof(dk_fig_bezier_point));
  correct_bezier_point(&(sptr->pe2), width, 0, &me);
  correct_bezier_point(&(sptr->pe), width, 1, &me);
  if(me) { back = 0; }
  
  return back;
}
/* }}} */



/* {{{ setup_backward_partial

  Correct Bezier spline at beginning to apply
  arrowhead.

*/
static int
setup_backward_partial DK_P3(dk_fig_object *,o,\
  dk_fig_drawing *,d, dk_fig_spline *,sptr)
{
  int back = 1;
  int me   = 0;
  double width;
  
  width = dkma_sub_double_ok(
    dkma_l_to_double((long)(sptr->normals)),
    sptr->ta,
    &me
  ); 
DK_MEMCPY(&(sptr->pa2),&((sptr->bpoints)[sptr->normals]),sizeof(dk_fig_bezier_point));
  correct_bezier_point(&(sptr->pa), width, 0, &me);
  correct_bezier_point(&(sptr->pa2), width, 1, &me);
  if(me) { back = 0; }
  
  return back;
}
/* }}} */



/* {{{ setup_one_partial

  Correct Bezier spline element.
  This is the very unlikely case that we have
  only one Bezier spline element which needs
  correction on both ends.

*/
static int
setup_one_partial DK_P3(dk_fig_object *,o,\
  dk_fig_drawing *,d, dk_fig_spline *,sptr)
{
  int back = 1;
  int me   = 0;
  double width;
  
  width = dkma_sub_double_ok(sptr->te, sptr->ta, &me);
  DK_MEMCPY(&(sptr->pe2),&(sptr->pa),sizeof(dk_fig_bezier_point));
  DK_MEMCPY(&(sptr->pa2),&(sptr->pe),sizeof(dk_fig_bezier_point));
  correct_bezier_point(&(sptr->pe2), width, 0, &me);
  correct_bezier_point(&(sptr->pe), width, 1, &me);
  correct_bezier_point(&(sptr->pa), width, 0, &me);
  correct_bezier_point(&(sptr->pa2), width, 1, &me);
  sptr->flags = sptr->flags | DKFIG_SPLINE_FLAG_ONE_PARTIAL;
  
  if(me) { back = 0; }
  
  return back;
}
/* }}} */



/* {{{ null_si

  Initialize data structure for spline end shortening
  interpolation.

*/
static void
null_si DK_P1(spline_iteration_t *,si)
{
  
  si->xl = 0.0; si->xr = 0.0; si->xlp = 0.0; si->xrm = 0.0;
  si->yl = 0.0; si->yr = 0.0; si->ylp = 0.0; si->yrm = 0.0;
  si->tcur = 0.0; si->currentstep = 0UL;
  si->lindex = 0; si->rindex = 0;
  si->xcurrent = 0.0; si->ycurrent = 0.0;
  
}
/* }}} */



/* {{{ shorten_spline_for_arrowheads

  Shorten spline ends for arrowheads.

*/
static int
shorten_spline_for_arrowheads DK_P4(dk_fig_object *,o,\
  dk_fig_drawing *,d, dk_fig_spline *,sptr, dk_fig_conversion *,c)
{
  int back = 1;
  int me   = 0;
  int rm   = 0;
  int suc  = 0;
  double   dist;
  spline_iteration_t si;
  
  si.o = o; si.d = d; si.s = sptr;
  si.min = d->minitsteps; si.max = d->maxitsteps;
  if(((o->fpd).ar) & 1) {	/* forward arrow */
    rm = suc = 0;
    if((sptr->segs > 0) && (sptr->nbpoints > (sptr->segs + 1))) {
      null_si(&si);
      si.end  = 1;
      si.t    = dkma_l_to_double(((long)(sptr->nbpoints)) - 1L);
      si.dist = (o->fpd).ahf.decrease;
      si.ox   = (sptr->bpoints)[sptr->nbpoints - 1].value.x;
      si.oy   = (sptr->bpoints)[sptr->nbpoints - 1].value.y;
      me      = 0;
      dist    = distance(
        (sptr->bpoints)[sptr->nbpoints - 1 - sptr->segs].value.x,
        (sptr->bpoints)[sptr->nbpoints - 1].value.x,
        (sptr->bpoints)[sptr->nbpoints - 1 - sptr->segs].value.y,
        (sptr->bpoints)[sptr->nbpoints - 1].value.y,
        &me
      );
      si.t    = si.t - dkma_mul_double_ok(
	dkma_l_to_double((long)(sptr->segs)),
	dkma_div_double_ok(si.dist, dist, &me),
	&me
      );
      
      if(do_iteration(&si)) {
	
	suc = 1;
        sptr->te = si.t;
        (sptr->pe).value.x = si.xcurrent;
        (sptr->pe).value.y = si.ycurrent;
        (sptr->pe).lcontrol.x = dkma_sub_double_ok(si.xcurrent, (si.dxdt / 3.0), &me);
        (sptr->pe).lcontrol.y = dkma_sub_double_ok(si.ycurrent, (si.dydt / 3.0), &me);
        sptr->normale = (size_t)dkma_double_to_l(floor(si.t));
        
        
#line 1890 "dkfigtoo.ctr"
        if(sptr->normale >= sptr->nbpoints) {
          rm = 1;
        }
        if(me) { rm = 1; }
      } else {
	
	/* rm = 1; */
      }
    }
    if(!suc) {
      null_si(&si);
      si.end  = 1;
      si.t    = dkma_l_to_double(((long)(sptr->nbpoints)) - 1L);
      si.dist = (o->fpd).ahf.decrease;
      si.ox   = (sptr->bpoints)[sptr->nbpoints - 1].value.x;
      si.oy   = (sptr->bpoints)[sptr->nbpoints - 1].value.y;
      me      = 0;
      dist    = distance(
        (sptr->bpoints)[sptr->nbpoints - 2].value.x,
        (sptr->bpoints)[sptr->nbpoints - 1].value.x,
        (sptr->bpoints)[sptr->nbpoints - 2].value.y,
        (sptr->bpoints)[sptr->nbpoints - 1].value.y,
        &me
      );
      si.t    = si.t - dkma_div_double_ok(si.dist, dist, &me);
      
      if(do_iteration(&si)) {
        suc = 1;
        
        sptr->te = si.t;
        (sptr->pe).value.x = si.xcurrent;
        (sptr->pe).value.y = si.ycurrent;
        (sptr->pe).lcontrol.x = dkma_sub_double_ok(si.xcurrent, (si.dxdt / 3.0), &me);
        (sptr->pe).lcontrol.y = dkma_sub_double_ok(si.ycurrent, (si.dydt / 3.0), &me);
        sptr->normale = (size_t)dkma_double_to_l(floor(si.t));
        
        
#line 1927 "dkfigtoo.ctr"
        if(sptr->normale >= sptr->nbpoints) {
          rm = 1;
        }
        if(me) { rm = 1; }
      } else {
        
	rm = 1;
      }
    }
    if(rm || (!suc)) {
      (o->fpd).ar = ((o->fpd).ar & (~1));
      
      /* Warning: forward arrow removed */
      if(c) {
        dkfig_tool2_msg1(c, DK_LOG_LEVEL_WARNING, 51);
      }
    }
  }
  if(((o->fpd).ar) & 2) {	/* backward arrow */
    rm = suc = 0;
    if((sptr->segs > 0) && (sptr->nbpoints > sptr->segs)) {
      null_si(&si);
      si.end  = 2;
      si.t    = 0.0;
      si.dist = (o->fpd).ahb.decrease;
      si.ox   = (sptr->bpoints)[0].value.x;
      si.oy   = (sptr->bpoints)[0].value.y;
      me = 0;
      dist    = distance(
        (sptr->bpoints)[0].value.x,
        (sptr->bpoints)[sptr->segs].value.x,
        (sptr->bpoints)[0].value.y,
        (sptr->bpoints)[sptr->segs].value.y,
        &me
      );
      si.t    = dkma_mul_double_ok(
	dkma_l_to_double((long)(sptr->segs)),
	dkma_div_double_ok(si.dist, dist, &me),
	&me
      );
      
      if(do_iteration(&si)) {
        suc = 1;
	
        sptr->ta = si.t;
        (sptr->pa).value.x = si.xcurrent; (sptr->pa).value.y = si.ycurrent;
        (sptr->pa).rcontrol.x = dkma_add_double_ok(si.xcurrent, (si.dxdt / 3.0), &me);
        (sptr->pa).rcontrol.y = dkma_add_double_ok(si.ycurrent, (si.dydt / 3.0), &me);
        
        
#line 1977 "dkfigtoo.ctr"
        sptr->normals = (size_t)dkma_double_to_l(floor(si.t)) + 1;
        if(me) { rm = 1; }
        if(((o->fpd).ar) & 1) {		
          if(sptr->te <= sptr->ta) {
	    rm = 1;
	  }
        }
      } else {
	
        /* rm = 1; */
      }
    }
    if(!suc) {
      null_si(&si);
      si.end  = 2;
      si.t    = 0.0;
      si.dist = (o->fpd).ahb.decrease;
      si.ox   = (sptr->bpoints)[0].value.x;
      si.oy   = (sptr->bpoints)[0].value.y;
      me      = 0;
      dist    = distance(
        (sptr->bpoints)[0].value.x,
        (sptr->bpoints)[1].value.x,
        (sptr->bpoints)[0].value.y,
        (sptr->bpoints)[1].value.y,
        &me
      );
      si.t    = dkma_div_double_ok(si.dist, dist, &me);
      
      if(do_iteration(&si)) {
        suc = 1;
        
        sptr->ta = si.t;
        (sptr->pa).value.x = si.xcurrent; (sptr->pa).value.y = si.ycurrent;
        (sptr->pa).rcontrol.x = dkma_add_double_ok(si.xcurrent, (si.dxdt / 3.0), &me);
        (sptr->pa).rcontrol.y = dkma_add_double_ok(si.ycurrent, (si.dydt / 3.0), &me);
        
        
#line 2015 "dkfigtoo.ctr"
        sptr->normals = (size_t)dkma_double_to_l(floor(si.t)) + 1;
        if(me) { rm = 1; }
        if(((o->fpd).ar) & 1) {		
          if(sptr->te <= sptr->ta) {
	    rm = 1;
	  }
        }
      } else {
        rm = 1;
        
      }
    }
    if(rm || (!suc)) {
      (o->fpd).ar = ((o->fpd).ar & (~2));
      
      /* Warning: backward arrow removed */
      if(c) {
        dkfig_tool2_msg1(c, DK_LOG_LEVEL_WARNING, 52);
      }
    }
  }
  /* 2004/05/19 end of new code */
  switch(((o->fpd).ar) & 3) {
    case 1: {
      if(!setup_forward_partial(o,d,sptr)) {
        back = 0;
      }
    } break;
    case 2: {
      if(!setup_backward_partial(o,d,sptr)) {
        back = 0;
      }
    } break;
    case 3: {
      if(sptr->normals > sptr->normale) {
        if(!setup_one_partial(o,d,sptr)) {
	  back = 0;
	}
      } else {
        if(!setup_forward_partial(o,d,sptr)) {
          back = 0;
        }
        if(!setup_backward_partial(o,d,sptr)) {
          back = 0;
        }
      }
    } break;
  }
  
  return back;
}
/* }}} */



/* {{{ ddxdtt

  Get the second derivative for a Bezier segment
  at t=0 (fl=0). The intention was t=1 for fl!=0
  but this does not work.

*/
static double
ddxdtt DK_P6(double,x0, double,xp, double,xm, double,x1, int,fl, int *,me)
{
  double back = 0.0;
  
#if NO_MATH_CHECK
  back = xm + x0 - 2.0 * xp;
  if(fl) {
    back = back + x1 - x0 + 3.0 * (xp - xm);
  }
  back = 6.0 * back;
#else
  back = dkma_sub_double_ok(
    dkma_add_double_ok(xm, x0, me),
    dkma_mul_double_ok(2.0, xp, me),
    me
  );
  if(fl) {
    back = dkma_add_double_ok(
      back,
      dkma_add_double_ok(
        dkma_sub_double_ok(x1, x0, me),
	dkma_mul_double_ok(
	  3.0,
	  dkma_sub_double_ok(xp, xm, me),
	  me
	),
	me
      ),
      me
    );
  }
  back = dkma_mul_double_ok(6.0, back, me);
#endif
  
  return back;
}
/* }}} */


/* {{{ dkfig_tool_build_one_spline

  Final processing for a spline.
  Shorten spline if necessary for arrowheads.

*/
int
dkfig_tool_build_one_spline DK_P3(dk_fig_object *,o,\
  dk_fig_drawing *,d, dk_fig_conversion *,c)
{
  int back = 0;
  int	 matherr = 0;	/* mathematical error */
  size_t noxs;		/* number of bezier segments */
  size_t cxseg;		/* current X-spline segment */
  size_t subsegs;	/* number of bezier segments per X-spline segment */
  size_t lxindex, rxindex, j;	/* X-spline point indices */
  size_t lbindex, rbindex;	/* Bezier point indices */
  double deltat;		/* scale factor for derivations */
  dk_xspline_t xs;	/* X-spline calculation structure */
  dk_fig_spline *sptr;
  
  if(o) {		
  if(o->data) {		
    back = 1; 
    sptr = (dk_fig_spline *)(o->data);
    sptr->ta = 0.0;
    sptr->te = dkma_l_to_double((long)(sptr->nbpoints) - 1L);
    null_bezier_point(&(sptr->pa));
    null_bezier_point(&(sptr->pe));
    null_bezier_point(&(sptr->pa2));
    null_bezier_point(&(sptr->pe2));
    null_all_bezier_points(sptr->bpoints, sptr->nbpoints);
    deltat  = 1.0;
    subsegs = sptr->segs;
    if(subsegs < 1) { subsegs = 1; }
    if(subsegs > 1) {
      deltat = 1.0 / dkma_l_to_double((long)subsegs);
    }
    noxs = sptr->npoints;
    if(!((o->fpd).cl)) { noxs--; }
    for(cxseg = 0; cxseg < noxs; cxseg++) {	/* for all X-spline segments */
      lxindex = cxseg;
      rxindex = lxindex + 1;
      lbindex = lxindex * subsegs;
      rbindex = rxindex * subsegs;
      if(rxindex >= (size_t)(sptr->npoints)) {
        rxindex = rxindex - (size_t)(sptr->npoints);
      }
      if(rbindex >= (size_t)(sptr->nbpoints)) {
        rbindex = rbindex - (size_t)(sptr->nbpoints);
      }
      /* initialize structures */
      dkxsp_reset(&xs);
      dkxsp_set_pos(&xs, cxseg, sptr->npoints, (o->fpd).cl);
      /* set A */
      if((cxseg > 0) || (o->fpd).cl) {
        if(cxseg > 0) { j = cxseg - 1; }
	else { j = sptr->npoints - 1; }
        dkxsp_setA(
	  &xs, (sptr->svalues)[j],
	  dkma_l_to_double((sptr->xvalues)[j]),
	  dkma_l_to_double((sptr->yvalues)[j])
	);
      }
      /* set B */
      dkxsp_setB(
        &xs, (sptr->svalues)[cxseg],
	dkma_l_to_double((sptr->xvalues)[cxseg]),
	dkma_l_to_double((sptr->yvalues)[cxseg])
      );
      /* set C */
      j = cxseg + 1;
      if((j < (size_t)(sptr->npoints)) || (o->fpd).cl) {
        if(j >= (size_t)(sptr->npoints)) { j = j - (size_t)(sptr->npoints); }
	dkxsp_setC(
	  &xs, (sptr->svalues)[j],
	  dkma_l_to_double((sptr->xvalues)[j]),
	  dkma_l_to_double((sptr->yvalues)[j])
	);
      }
      /* set D */
      j = cxseg + 2;
      if((j < (size_t)(sptr->npoints)) || (o->fpd).cl) {
        if(j >= (size_t)(sptr->npoints)) { j = j - (size_t)(sptr->npoints); }
	dkxsp_setD(
	  &xs, (sptr->svalues)[j],
	  dkma_l_to_double((sptr->xvalues)[j]),
	  dkma_l_to_double((sptr->yvalues)[j])
	);
      }
      /* prepare calculation */
      dkxsp_step1(&xs);
      /* calculate "left" point */
      if(dkxsp_step2(&xs, 0.0)) {
        ((sptr->bpoints)[lbindex]).value.x = xs.x;
	((sptr->bpoints)[lbindex]).value.y = xs.y;
	((sptr->bpoints)[lbindex]).rcontrol.x
	= dkma_add_double_ok(xs.x, (deltat * xs.ddtx / 3.0), &matherr);
	((sptr->bpoints)[lbindex]).rcontrol.y
	= dkma_add_double_ok(xs.y, (deltat * xs.ddty / 3.0), &matherr);
	
	
      } else {
        matherr = 1;
      }
      /* calculate "right" point */
      if(dkxsp_step2(&xs, 1.0)) {
        ((sptr->bpoints)[rbindex]).value.x = xs.x;
	((sptr->bpoints)[rbindex]).value.y = xs.y;
	((sptr->bpoints)[rbindex]).lcontrol.x
	= dkma_sub_double_ok(xs.x, (deltat * xs.ddtx / 3.0), &matherr);
	((sptr->bpoints)[rbindex]).lcontrol.y
	= dkma_sub_double_ok(xs.y, (deltat * xs.ddty / 3.0), &matherr);
	
	
      } else {
        matherr = 1;
      }
      /* caluculate intermediate points */
      for(j = 1; j < subsegs; j++) {
        if(dkxsp_step2(&xs, (deltat*dkma_l_to_double((long)j)))) {
	  ((sptr->bpoints)[lbindex+j]).value.x = xs.x;
	  ((sptr->bpoints)[lbindex+j]).value.y = xs.y;
	  ((sptr->bpoints)[lbindex+j]).lcontrol.x
	  = dkma_sub_double_ok(xs.x, (deltat * xs.ddtx / 3.0), &matherr);
	  ((sptr->bpoints)[lbindex+j]).lcontrol.y
	  = dkma_sub_double_ok(xs.y, (deltat * xs.ddty / 3.0), &matherr);
	  ((sptr->bpoints)[lbindex+j]).rcontrol.x
	  = dkma_add_double_ok(xs.x, (deltat * xs.ddtx / 3.0), &matherr);
	  ((sptr->bpoints)[lbindex+j]).rcontrol.y
	  = dkma_add_double_ok(xs.y, (deltat * xs.ddty / 3.0), &matherr);
	  
	  
	  
	} else {
	  matherr = 1;
	}
      }
    }
    if((!((o->fpd).cl)) && ((sptr->nbpoints) >= 2)) {
      double phi, deltax, deltay;
      if(((o->fpd).ar) & 1) {
        
	deltay = ((sptr->bpoints)[sptr->nbpoints - 1]).value.y
	         - ((sptr->bpoints)[sptr->nbpoints - 1]).lcontrol.y;
        deltax = ((sptr->bpoints)[sptr->nbpoints - 1]).value.x
	         - ((sptr->bpoints)[sptr->nbpoints - 1]).lcontrol.x;

        phi = dkma_atan2(deltay, deltax);
	
	if((fabs(deltay) < DKFIG_EPSILON) && (fabs(deltax) < DKFIG_EPSILON)) {
	  /*
	  deltay = ddxdtt(
	    ((sptr->bpoints)[sptr->nbpoints - 2]).value.y,
	    ((sptr->bpoints)[sptr->nbpoints - 2]).rcontrol.y,
	    ((sptr->bpoints)[sptr->nbpoints - 1]).lcontrol.y,
	    ((sptr->bpoints)[sptr->nbpoints - 1]).value.y,
	    1,
	    &matherr
	  );
	  deltax = ddxdtt(
	    ((sptr->bpoints)[sptr->nbpoints - 2]).value.x,
	    ((sptr->bpoints)[sptr->nbpoints - 2]).rcontrol.x,
	    ((sptr->bpoints)[sptr->nbpoints - 1]).lcontrol.x,
	    ((sptr->bpoints)[sptr->nbpoints - 1]).value.x,
	    1,
	    &matherr
	  );
	  */
	  deltay = ddxdtt(
	    ((sptr->bpoints)[sptr->nbpoints - 1]).value.y,
	    ((sptr->bpoints)[sptr->nbpoints - 1]).lcontrol.y,
	    ((sptr->bpoints)[sptr->nbpoints - 2]).rcontrol.y,
	    ((sptr->bpoints)[sptr->nbpoints - 2]).value.y,
	    0,
	    &matherr
	  ); deltay = 0.0 - deltay;
	  deltax = ddxdtt(
	    ((sptr->bpoints)[sptr->nbpoints - 1]).value.x,
	    ((sptr->bpoints)[sptr->nbpoints - 1]).lcontrol.x,
	    ((sptr->bpoints)[sptr->nbpoints - 2]).rcontrol.x,
	    ((sptr->bpoints)[sptr->nbpoints - 2]).value.x,
	    0,
	    &matherr
	  ); deltax = 0.0 - deltax;
	  phi = dkma_atan2(deltay, deltax);
	  
	}
	if(dkfig_tool_invert_y(d)) {
	  phi = 0.0 - phi;
	}
	if(!dkfig_tool_calc_arrow(
	  (&(o->fpd).ahf), &(((sptr->bpoints)[sptr->nbpoints - 1]).value),
	  phi, &(o->fpd), d, c
	)
	)
	{
	  back = 0;
	}
      }
      if(((o->fpd).ar) & 2) {
        
	deltay = ((sptr->bpoints)[0]).value.y 
	         - ((sptr->bpoints)[0]).rcontrol.y;
	deltax = ((sptr->bpoints)[0]).value.x 
	         - ((sptr->bpoints)[0]).rcontrol.x;
        phi = dkma_atan2(deltay, deltax);
	
	if((fabs(deltay) < DKFIG_EPSILON) && (fabs(deltax) < DKFIG_EPSILON)) {
	  deltay = ddxdtt(
	    ((sptr->bpoints)[0]).value.y,
	    ((sptr->bpoints)[0]).rcontrol.y,
	    ((sptr->bpoints)[1]).lcontrol.y,
	    ((sptr->bpoints)[1]).value.y,
	    0,
	    &matherr
	  );
	  deltax = ddxdtt(
	    ((sptr->bpoints)[0]).value.x,
	    ((sptr->bpoints)[0]).rcontrol.x,
	    ((sptr->bpoints)[1]).lcontrol.x,
	    ((sptr->bpoints)[1]).value.x,
	    0,
	    &matherr
	  );
	  phi = dkma_atan2(deltay, deltax);
	  
	}
	if(dkfig_tool_invert_y(d)) {
	  phi = 0.0 - phi;
	}
	phi = phi + M_PI;
	if(phi > (2.0 * M_PI)) {
	  phi = phi - 2.0 * M_PI;
	}
	if(!dkfig_tool_calc_arrow(
	  (&(o->fpd).ahb), &(((sptr->bpoints)[0]).value),
	  phi, &(o->fpd), d, c
	)
	)
	{
	  back = 0;
	}
      }
    }
    if((o->fpd).ar & 3) {
    if(!shorten_spline_for_arrowheads(o,d,sptr,c)) {
      back = 0;
    }
    }
  }
  }
  if(matherr) {
    back = 0;		
  }
  
  return back;
}
/* }}} */



/* {{{ dkfig_tool_null_arc_calc

  Initialize data structure for arc calculation.

*/
void
dkfig_tool_null_arc_calc DK_P1(dk_fig_arc_calc *,c)
{
  if(c) {
    DK_MEMRES(c,sizeof(dk_fig_arc_calc));
    c->xa = c->ya = c->xb = c->yb = c->xc = c->yc = 0.0;
    c->xm = c->ym = c->ra = 0.0;
    c->aa = c->ab = c->ac = 0.0;
    c->xleft = c->xright = c->ybottom = c->ytop = 0.0;
    c->astart = c->alength = 0.0;
    c->revert = 0; c->mathok = 0;
  }
}
/* }}} */



/* {{{ dkfig_tool_invert_arc_calc

  If the Fig co-ordinates system is not swapped
  (positive y-values downward) we need to apply
  changes.

*/
void
dkfig_tool_invert_arc_calc DK_P1(dk_fig_arc_calc *,c)
{
  if(c) {
    c->ya = 0.0 - c->ya;
    c->yb = 0.0 - c->yb;
    c->yc = 0.0 - c->yc;
    c->ym = 0.0 - c->ym;
    c->ybottom = 0.0 - c->ybottom;
    c->ytop = 0.0 - c->ytop;
  }
}
/* }}} */



/* {{{ dkfig_tool_swap_arrows

  Swap forward an backward arrow.
  MetaPost forces us to draw arcs in pos. direction.
  For arcs specified in clockwise direction in
  the Fig file we need to swap the arrows to use
  "fill arrowhead p withcolor..."

*/
void
dkfig_tool_swap_arrows DK_P1(dk_fig_fpd *,fpd)
{
  dk_fig_arrow arrow;
  int newar;
  if(fpd) {
    if((fpd->ar) & 3) {
      newar = 0;
      if((fpd->ar) & 1) { newar |= 2; }
      if((fpd->ar) & 2) { newar |= 1; }
      DK_MEMCPY(&arrow, &(fpd->ahf), sizeof(dk_fig_arrow));
      DK_MEMCPY(&(fpd->ahf), &(fpd->ahb), sizeof(dk_fig_arrow));
      DK_MEMCPY(&(fpd->ahb), &arrow, sizeof(dk_fig_arrow));
      fpd->ar = newar;
    }
  }
}
/* }}} */



/* {{{ dkfig_tool_arc_complete

  Final processing for an arc.
  Corrections for arrowhead if necessary.

*/
int
dkfig_tool_arc_complete DK_P3(dk_fig_object *,o, dk_fig_drawing *,d,\
  dk_fig_conversion *,c)
{
  int back = 0;
  int me   = 0;
  int rm   = 0;
  dk_fig_arc *a;
  double buf, v;
  dk_fig_point pnt;
  
  if(o && d) {
    a = (dk_fig_arc *)(o->data);
    if(a) {
      a->lba = a->rba = 0.0;
      (a->calc).xa = dkma_l_to_double(a->x1);
      (a->calc).ya = dkma_l_to_double(a->y1);
      (a->calc).xb = dkma_l_to_double(a->x2);
      (a->calc).yb = dkma_l_to_double(a->y2);
      (a->calc).xc = dkma_l_to_double(a->x3);
      (a->calc).yc = dkma_l_to_double(a->y3);
      if(dkfig_tool_invert_y(d)) {	
        dkfig_tool_invert_arc_calc(&(a->calc));
      }
      dkfig_tool_arc_calc(&(a->calc));
      if(dkfig_tool_invert_y(d)) {	
        dkfig_tool_invert_arc_calc(&(a->calc));
      }
      if((a->calc).revert) {		
        dkfig_tool_swap_arrows(&(o->fpd));
	buf = (a->calc).xa; (a->calc).xa = (a->calc).xc; (a->calc).xc = buf;
	buf = (a->calc).ya; (a->calc).ya = (a->calc).yc; (a->calc).yc = buf;
	buf = (a->calc).aa; (a->calc).aa = (a->calc).ac; (a->calc).ac = buf;
      }
      if(!((a->calc).mathok)) {		
        back = 1;
	pnt.x = (a->calc).xc; pnt.y = (a->calc).yc;
	if(((o->fpd).ar) & 1) {
if(dkfig_tool_calc_arrow(&((o->fpd).ahf),&pnt,((a->calc).ac + M_PI_2),&(o->fpd),d,c)) {
          rm = 0;
          v = dkma_sub_double_ok(
	    1.0,
	    dkma_div_double_ok(
	      dkma_mul_double_ok((o->fpd).ahf.decrease,(o->fpd).ahf.decrease,&rm),
	      dkma_mul_double_ok(
	        2.0,
		dkma_mul_double_ok((a->calc).ra,(a->calc).ra,&rm),
		&rm
	      ),
	      &rm
	    ),
	    &rm
	  );
	  if(fabs(v) <= 1.0) {
	    a->rba = acos(v);
	    a->rba = fabs(a->rba);
	  } else {
	    rm = 1;
	  }
	  if(rm) {
            (o->fpd).ar = ((o->fpd).ar & (~1));
	    /* WARNING: FORWARD ARROWHEAD REMOVED */
	    if(c) {
	      dkfig_tool2_msg1(c, DK_LOG_LEVEL_WARNING, 51);
	    }
	  }
} else {
	  back = 0;
}
	}
	pnt.x = (a->calc).xa; pnt.y = (a->calc).ya;
	if(((o->fpd).ar) & 2) {
if(dkfig_tool_calc_arrow(&((o->fpd).ahb),&pnt,((a->calc).aa - M_PI_2),&(o->fpd),d,c)) {
          rm = 0;
          v = dkma_sub_double_ok(
	    1.0,
	    dkma_div_double_ok(
	      dkma_mul_double_ok((o->fpd).ahb.decrease,(o->fpd).ahb.decrease,&rm),
	      dkma_mul_double_ok(
	        2.0,
		dkma_mul_double_ok((a->calc).ra,(a->calc).ra,&rm),
		&rm
	      ),
	      &rm
	    ),
	    &rm
	  );
	  if(fabs(v) <= 1.0) {
	    a->lba = acos(v);
	    a->lba = fabs(a->lba);
	  } else {
	    rm = 1;
	  }
	  if(rm) {
            (o->fpd).ar = ((o->fpd).ar & (~2));
	    /* WARNING: ARROWHEAD BACKWARD REMOVED */
	    if(c) {
	      dkfig_tool2_msg1(c, DK_LOG_LEVEL_WARNING, 52);
	    }
	  }
} else {
	  back = 0;
}
        }
      }
    }
  }
  if(me) { back = 0; }
  
  return back;
}
/* }}} */



/* {{{ dkfig_tool_polyline_complete

  Final processing for a polyline.
  Line end correction for arrowheads if
  necessary.

*/
int
dkfig_tool_polyline_complete DK_P3(dk_fig_object *,o,\
  dk_fig_drawing *,d, dk_fig_conversion *,c)
{
  int back = 0;
  int matherr = 0;
  int rm = 0;
  double phi = 0.0;
  double seglength;
  dk_fig_polyline *p;
  dk_fig_point pnt;
  
  if(o && d) {
    p = (dk_fig_polyline *)(o->data);
    if(p) {
      back = 1;
      null_point(&(p->pa));
      null_point(&(p->pe));
      if((!((o->fpd).cl)) && ((p->npoints) >= 2)) {
        if((p->xvalues) && (p->yvalues)) {
	  if(((o->fpd).ar) & 1) {		
	    pnt.x = dkma_l_to_double((p->xvalues)[p->npoints - 1]);
	    pnt.y = dkma_l_to_double((p->yvalues)[p->npoints - 1]);
	    phi = dkma_atan2(
	      dkma_sub_double_ok(
	        dkma_l_to_double((p->yvalues)[p->npoints - 1]),
		dkma_l_to_double((p->yvalues)[p->npoints - 2]),
		&matherr
	      ),
	      dkma_sub_double_ok(
	        dkma_l_to_double((p->xvalues)[p->npoints - 1]),
		dkma_l_to_double((p->xvalues)[p->npoints - 2]),
		&matherr
	      )
	    );
	    if(dkfig_tool_invert_y(d)) {
	      phi = 0.0 - phi;
	    }	
	    if(dkfig_tool_calc_arrow(&((o->fpd).ahf),&pnt,phi,&(o->fpd),d,c))
	    {
	      rm = 0;
	      seglength = distance(
	        dkma_l_to_double((p->xvalues)[p->npoints - 2]),
		dkma_l_to_double((p->xvalues)[p->npoints - 1]),
		dkma_l_to_double((p->yvalues)[p->npoints - 2]),
		dkma_l_to_double((p->yvalues)[p->npoints - 1]),
	        &matherr
	      );
	      (p->pe).x = dkma_l_to_double((p->xvalues)[p->npoints - 1]);
	      (p->pe).y = dkma_l_to_double((p->yvalues)[p->npoints - 1]);
	      if(seglength >= (o->fpd).ahf.decrease) {
	        (p->pe).x = dkma_add_double_ok(
		  dkma_l_to_double((p->xvalues)[p->npoints - 2]),
		  dkma_mul_double_ok(
		    dkma_sub_double_ok(
		      dkma_l_to_double((p->xvalues)[p->npoints - 1]),
		      dkma_l_to_double((p->xvalues)[p->npoints - 2]),
		      &matherr
		    ),
		    dkma_sub_double_ok(
		      1.0,
		      dkma_div_double_ok(
		        (o->fpd).ahf.decrease,
			seglength,
			&matherr
		      ),
		      &matherr
		    ),
		    &matherr
		  ),
		  &matherr
		);
		(p->pe).y = dkma_add_double_ok(
		  dkma_l_to_double((p->yvalues)[p->npoints - 2]),
		  dkma_mul_double_ok(
		    dkma_sub_double_ok(
		      dkma_l_to_double((p->yvalues)[p->npoints - 1]),
		      dkma_l_to_double((p->yvalues)[p->npoints - 2]),
		      &matherr
		    ),
		    dkma_sub_double_ok(
		      1.0,
		      dkma_div_double_ok(
		        (o->fpd).ahf.decrease,
			seglength,
			&matherr
		      ),
		      &matherr
		    ),
		    &matherr
		  ),
		  &matherr
		);
		
		
		
	      } else {
	        rm = 1;
	      }
	      if(rm) {
                (o->fpd).ar = ((o->fpd).ar & (~1));
		/* WARNING: ARROWHEAD FORWARD REMOVED */
		if(c) {
		  dkfig_tool2_msg1(c, DK_LOG_LEVEL_WARNING, 51);
		}
	      }
	    }
	    else
	    {
	      back = 0;
	    }
	  }
	  if(((o->fpd).ar) & 2) {		
	    pnt.x = dkma_l_to_double((p->xvalues)[0]);
	    pnt.y = dkma_l_to_double((p->yvalues)[0]);
	    phi = dkma_atan2(
	      dkma_sub_double_ok(
	        dkma_l_to_double((p->yvalues)[0]),
		dkma_l_to_double((p->yvalues)[1]),
		&matherr
	      ),
	      dkma_sub_double_ok(
	        dkma_l_to_double((p->xvalues)[0]),
		dkma_l_to_double((p->xvalues)[1]),
		&matherr
	      )
	    );
	    if(dkfig_tool_invert_y(d)) {
	      phi = 0.0 - phi;
	    }	
	    if(dkfig_tool_calc_arrow(&((o->fpd).ahb),&pnt,phi,&(o->fpd),d,c))
	    {
	      rm = 0;
	      seglength = distance(
	        dkma_l_to_double((p->xvalues)[0]),
		dkma_l_to_double((p->xvalues)[1]),
		dkma_l_to_double((p->yvalues)[0]),
		dkma_l_to_double((p->yvalues)[1]),
	        &matherr
	      );
	      (p->pa).x = dkma_l_to_double((p->xvalues)[0]);
	      (p->pa).y = dkma_l_to_double((p->yvalues)[0]);
	      if(seglength > (o->fpd).ahb.decrease) {
	        (p->pa).x = dkma_add_double_ok(
		   dkma_l_to_double((p->xvalues)[0]),
		   dkma_mul_double_ok(
		     dkma_sub_double_ok(
		       dkma_l_to_double((p->xvalues)[1]),
		       dkma_l_to_double((p->xvalues)[0]),
		       &matherr
		     ),
		     dkma_div_double_ok(
		       (o->fpd).ahb.decrease,
		       seglength,
		       &matherr
		     ),
		     &matherr
		   ),
		  &matherr
		);
	        (p->pa).y = dkma_add_double_ok(
		   dkma_l_to_double((p->yvalues)[0]),
		   dkma_mul_double_ok(
		     dkma_sub_double_ok(
		       dkma_l_to_double((p->yvalues)[1]),
		       dkma_l_to_double((p->yvalues)[0]),
		       &matherr
		     ),
		     dkma_div_double_ok(
		       (o->fpd).ahb.decrease,
		       seglength,
		       &matherr
		     ),
		     &matherr
		   ),
		  &matherr
		);
		
		
		
	      } else {
	        rm = 1;
	      }
	      if(rm) {
                (o->fpd).ar = ((o->fpd).ar & (~2));
		/* WARNING: ARROWHEAD BACKWARD REMOVED */
		if(c) {
		  dkfig_tool2_msg1(c, DK_LOG_LEVEL_WARNING, 52);
		}
	      }
	    }
	    else
	    {
	      back = 0;
	    }
	  }
	} else {
	  back = 0;	
	}
      }
    }
  }
  if(matherr) { back = 0; }
  
  return back;
}
/* }}} */



/* {{{ dkfig_tool_fill_dcc

  Fill a dcc data by searching the color cell information
  in the drawing data.

*/
void
dkfig_tool_fill_dcc DK_P3(dk_fig_drawing *,d, dk_fig_dcc *,dcc, int,n)
{
  dk_fig_colorcell *cc;
  if(dcc) {
    dcc->red = dcc->green = dcc->blue = 0.0;
    dcc->ired = dcc->igreen = dcc->iblue = 0;
  }
  if(d && dcc) {
    if(d->ccit) {
      cc = (dk_fig_colorcell *)dksto_it_find_like(d->ccit, &n, 1);
      if(cc) {
        dcc->red    = dkma_l_to_double(((long)(cc->r)) & 255L) / 255.0;
        dcc->green  = dkma_l_to_double(((long)(cc->g)) & 255L) / 255.0;
        dcc->blue   = dkma_l_to_double(((long)(cc->b)) & 255L) / 255.0;
	dcc->ired   = (int)((cc->r) & 255L);
	dcc->igreen = (int)((cc->g) & 255L);
	dcc->iblue  = (int)((cc->b) & 255L);
      }
    }
  }
}
/* }}} */



/* {{{ dkfig_tool_correct_dcc

  Correct a dcc color data set using the color number
  and the area fill.

*/
void
dkfig_tool_correct_dcc DK_P3(dk_fig_dcc *,dcc, int,colno, int,af)
{
  double r, g, b, d;
  if(dcc) {
    r = dcc->red; g = dcc->green; b = dcc->blue;
    if(colno >= 1) {
      if(af > 0) {
        if(af <= 19) {
          d = dkma_l_to_double((long)af);
	  dcc->red   = (d * r)/20.0;
	  dcc->green = (d * g)/20.0;
	  dcc->blue  = (d * b)/20.0;
        } else {
          if(af > 20) {
	    if(af < 40) {
	      d =  dkma_l_to_double((long)af) - 20.0;
	      dcc->red   = r + ((1.0 - r) * d)/20.0;
	      dcc->green = g + ((1.0 - g) * d)/20.0;
	      dcc->blue  = b + ((1.0 - b) * d)/20.0;
	    } else {
	      if(af == 40) {
	        dcc->red = dcc->green = dcc->blue = 1.0;
	      }
	    }
	  }
        }
      } else {
        dcc->red = dcc->green = dcc->blue = 0.0;
      }
    } else {
      if((af>= 0) && (af <= 19)) {
        d = dkma_l_to_double((long)af);
	dcc->red = dcc->green = dcc->blue = 1.0 - d/20.0;
      } else {
        dcc->red = dcc->green = dcc->blue = 0.0;
      }
    }
    if(dcc->red > 1.0) dcc->red = 1.0;
    if(dcc->red < 0.0) dcc->red = 0.0;
    if(dcc->green > 1.0) dcc->green = 1.0;
    if(dcc->green < 0.0) dcc->green = 0.0;
    if(dcc->blue > 1.0) dcc->blue = 1.0;
    if(dcc->blue < 0.0) dcc->blue = 0.0;
    dcc->ired   = (int)dkma_double_to_l(255.0 * dcc->red);
    dcc->igreen = (int)dkma_double_to_l(255.0 * dcc->green);
    dcc->iblue  = (int)dkma_double_to_l(255.0 * dcc->blue);
  }
}
/* }}} */



/* {{{ dkfig_tool_must_fill

  Check whether or not an object is filled.

*/
int
dkfig_tool_must_fill DK_P2(int, area_fill, unsigned long,opt)
{
  int back = 0;
  
  if(area_fill >= 0) {
    if((area_fill < 41) || (area_fill > 56)) {
      back = 1;
    } else {
      if((area_fill >= 41) && (area_fill <= 62)) {
        if(!(opt & DKFIG_OPT_FILL_PATTERNS)) {
	  back = 1;
	}
      }
    }
  }
  
  return back;
}
/* }}} */



/* {{{ dkfig_tool_must_pattern

  Check whether or not we need to draw fill
  patterns.

*/
int
dkfig_tool_must_pattern DK_P2(int, area_fill, unsigned long,opt)
{
  int back = 0;
  
  if((area_fill >= 41) && (area_fill <= 62)) {
    if(opt & DKFIG_OPT_FILL_PATTERNS) {
      back = 1;
    }
  } 
  return back;
}
/* }}} */




/* {{{ dkfig_tool_num_as_string

  Encode one number as string and write it to
  output stream. The lgt parameter is the number
  of characters to produce.

*/
int
dkfig_tool_num_as_string DK_P3(dk_stream_t *,os, unsigned long,no, size_t,lgt)
{
  int back = 0;
  char buffer[128];
  unsigned long num, rest; size_t i, r;
  
  if((os) && lgt) {
    if((lgt < sizeof(buffer)) && (lgt > 0)) {
      back = 1;
      DK_MEMRES(buffer,sizeof(buffer)) ;
      num = no;
      for(i = 0; i < lgt; i++) {	
        rest = num % 26UL;		
	num  = num / 26UL;		
	r    = (size_t)rest;		
	buffer[lgt - 1 - i] = alphabet[r];
      }
      buffer[lgt] = '\0';
      back = dkstream_puts(os, buffer);
    }
  } 
  return back;
}
/* }}} */



/* {{{ dkfig_tool_delete_tex_files

   Remove files used for special text processing.

*/
void
dkfig_tool_delete_tex_files DK_P1(dk_fig_conversion *,c)
{
  if(c) {
    if(c->texn) {
      dksf_remove_file(c->texn);
    }
    if(c->dvin) {
      dksf_remove_file(c->dvin);
    }
    if(c->psn) {
      dksf_remove_file(c->psn);
    }
    if(c->logn) {
      dksf_remove_file(c->logn);
    }
    if(c->auxn) {
      dksf_remove_file(c->auxn);
    }
  }
}
/* }}} */



/* {{{ dkfig_tool_init_netpbm

   Initialize NetPBM library.

*/
static char program_name[] = { DKFIG_PROGRAM_NAME };
void
dkfig_tool_init_netpbm DK_P1(dk_fig_conversion *,c)
{
#if HAVE_PNM_H
  int argc; char *argv[2];
  
  if(!((c->opt1) & DKFIG_OPT_NETPBM_INITIALIZED)) {
    
    c->opt1 |= DKFIG_OPT_NETPBM_INITIALIZED;
    argc = 1; argv[0] = program_name; argv[1] = NULL;
    pnm_init(&argc, argv);
  }
  
#endif
}
/* }}} */




/* {{{ SCCS ID */
#ifndef LINT
static char sccs_id[] = {
"@(#)dkfigtoo.ctr 1.93 05/29/07\t(krause) - fig2vect"
};
#endif
/* }}} */
