/*************************************************
*    The PMW Music Typesetter - 3rd incarnation  *
*************************************************/

/* Copyright (c) Philip Hazel, 1991 - 2008 */

/* Written by Philip Hazel, starting November 1991 */
/* This file last modified: December 2019 */


/* This file contains code for outputting hairpins */

#include "pmwhdr.h"
#include "outhdr.h"
#include "pagehdr.h"




/*************************************************
*          Deal with start of hairpin            *
*************************************************/

/* This remembers the hairpin parameters for later use.

Arguments:
  h           the hairpin data for the start of the hairpin
  x           the x coordinate of the start of the hairpin

Returns:      nothing
*/

void
out_setstarthairpin(b_hairpinstr *h, int x)
{
hairpinstr *hh = store_Xget(sizeof(hairpinstr));
hh->hairpin = h;
hh->x = x;
hh->maxy = -BIGNUMBER;
hh->miny =  BIGNUMBER;
bar_cont->hairpin = hh;

if ((h->flags & hp_bar) != 0)
  hh->x = out_startlinebar?
    (out_sysblock->timexposition + out_sysblock->xjustify) : out_lastbarlinex;

/* The /bar option overrides /h */

else if ((h->flags & hp_halfway) != 0) out_hairpinhalf = TRUE;

/* And /h overrides /lc and /rc. Adjust the x coordinate by looking for the
relevant musical offset. If x == 0 it means we are setting up for a
continuation hairpin while paginating, so don't do anything. */

else if (h->offset != 0 && x != 0)
  {
  int offset = mac_muldiv(len_crotchet, h->offset, 1000);
  hh->x = out_barx + out_findAoffset(out_moff + offset);
  }
}


/*************************************************
*               Draw a hairpin                   *
*************************************************/

/* This actually draws the hairpin.

Arguments:
  h1           the data for the end of the hairpin
  x1           the x coordinate of the end of the hairpin

Returns:       nothing
*/

void
out_drawhairpin(b_hairpinstr *h1, int x1)
{
hairpinstr *hh = bar_cont->hairpin;
b_hairpinstr *h0 = hh->hairpin;
BOOL skip = FALSE;

int flags = h0->flags;
int abs = (flags & hp_abs) != 0;
int cwidth = h0->width/2;
int dwidth = cwidth;
int thickness = curmovt->hairpinlinewidth;
int x0 = hh->x;
int offset, y0, y1, y0hole, y1hole;

/* Compute basic offset from stave base */

if ((flags & hp_below) == 0)
  {
  offset = abs? 16000 : (((hh->maxy > 16000)? hh->maxy + 6000 : 22000) +
    (h0->width - 7000)/2);
  }

else
  {
  offset = abs? 0 : (((hh->miny < 0)? hh->miny - 6000 : -6000) -
    (h0->width - 7000)/2);

  if ((flags & hp_middle) != 0 && out_stave < out_laststave)
    {
    int gap = out_sysblock->stavespacing[out_stave]/2;
    int st = out_stave + 1;

    while (gap == 0 && st < out_laststave)
      if (mac_teststave(out_sysblock->notsuspend, st))
        gap = out_sysblock->stavespacing[st++]/2;

    gap -= 8 * main_stavemagn;
    if (-gap < offset) offset = -gap;
    }
  }

/* At start of line, start just before first note; also set small gap at start
of hairpin. If continued decrescendo, start at smaller width. */

if (x0 == 0)
  {
  x0 = out_sysblock->firstnoteposition - 4*main_stavemagn;
  y0 = h0->y + ((h1 == NULL)? 0 : h1->su);
  y0hole = main_stavemagn;
  dwidth = (80*dwidth)/100;
  }
else  /* not start of line */
  {
  x0 += h0->x;
  y0 = h0->y;
  y0hole = 0;
  }

/* Add manual right-hand adjustment; at end of line we use the left-hand value.
Set small gap in decrescendo at end of line. */

if (h1 != NULL)
  {
  if ((h1->flags & hp_bar) != 0)      /* /bar overrides /h */
    {
    x1 = out_barlinex;
    }
  else if ((h1->flags & hp_halfway) != 0)
    {
    x1 += mac_muldiv(out_barx + out_findXoffset(out_moff) - x1 -
      6*main_stavemagn, h1->h, 1000);
    }
  else if (h1->offset != 0)
    {
    int xoffset = mac_muldiv(len_crotchet, h1->offset, 1000);
    x1 = out_barx + out_findAoffset(out_moff + xoffset);
    }
  x1 += h1->x;
  y1 = h0->y + h1->y;
  y1hole = 0;
  }
else  /* end of line; reduce crescendo width a bit */
  {
  y1 = h0->y + h0->su;
  y1hole = main_stavemagn;
  cwidth = (cwidth*80)/100;
  }

/* Final y values */

y0 = ((y0 + offset)*main_stavemagn)/1000;
y1 = ((y1 + offset)*main_stavemagn)/1000;

/* Draw the hairpin, enforcing a minimum length of 10 points, except at the end
of line where we can suppress a decrescendo. We can't suppress a crescendo -
the user will have to fix. */

if (x1 - x0 < 10*main_stavemagn)
  {
  if (h1 != NULL || h0->opt == '<') x1 = x0 + 10*main_stavemagn;
    else skip = TRUE;
  }

if (!skip)
  {
  if (h0->opt == '>')
    {
    ps_line(x0, y0 + dwidth, x1, y1 + y1hole, thickness, 0);
    ps_line(x0, y0 - dwidth, x1, y1 - y1hole, thickness, 0);
    }
  else
    {
    ps_line(x0, y0 + y0hole, x1, y1 + cwidth, thickness, 0);
    ps_line(x0, y0 - y0hole, x1, y1 - cwidth, thickness, 0);
    }
  }

/* Free the dynamic store and clear its pointer. */

store_free(hh);
bar_cont->hairpin = NULL;
}


/* End of sethairpin.c */
