/*****************************************************************************
 *
 * MODULE:       d.rast.arrow
 * AUTHOR(S):    Chris Rewerts, Agricultural Engineering, Purdue University
 * PURPOSE:      Draw arrows on slope/aspect maps.
 * COPYRIGHT:    (C) 2000, 2010, 2023 by the GRASS Development Team
 *
 *              This program is free software under the GNU General Public
 *              License (>=v2). Read the file COPYING that comes with GRASS
 *              for details.
 *
 *****************************************************************************/

/* some minor cleanup done by Andreas Lange, andreas.lange@rhein-main.de
 * Update to handle NULLs and floating point aspect maps: Hamish Bowman, Aug
 * 2004; Update for 360 degree arrows and magnitude scaling:  Hamish Bowman, Oct
 * 2005; Align grids with raster cells: Huidae Cho, Apr 2009; Drainage aspect
 * type: Huidae Cho, Sep 2015; Terraflow type: Huidae Cho, May 2023
 */

/*
 *   Chris Rewerts, Agricultural Engineering, Purdue University
 *   rewerts@ecn.purdue.edu  March 1991
 *
 *   d.rast.arrow
 *
 *   Usage:  d.rast.arrow
 *
 *   This program used Dgrid's sources as a beginning. Purpose of Darrow
 *   is to read an aspect layer produced by slope.aspect or by the
 *   programs created for the ANSWERS or AGNPS Hydrology Toolbox
 *   endeavors.  d.rast.arrow draws an arrow on the graphic display
 *   of each cell, so that the flow pattern computed as an aspect
 *   layer can be easily seen. Other symbols ("?", "X") may be drawn
 *   as needed.
 */

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/display.h>
#include <grass/colors.h>
#include <grass/glocale.h>

#define RpD    ((2 * M_PI) / 360.) /* radians/degree */
#define D2R(d) (double)(d * RpD)   /* degrees->radians */

static void arrow_mag(double, double);
static void arrow_360(double);
static void arrow_se(void);
static void arrow_ne(void);
static void arrow_nw(void);
static void arrow_sw(void);
static void arrow_e(void);
static void arrow_w(void);
static void arrow_s(void);
static void arrow_n(void);
static void draw_x(void);
static void unknown_(void);

static char *layer_name;
static int map_type, arrow_color, grid_color, x_color, unknown_color;
static int row, col;

int main(int argc, char **argv)
{
    struct Cell_head window;
    RASTER_MAP_TYPE raster_type, mag_raster_type = -1;
    int layer_fd;
    void *raster_row, *ptr;
    int nrows, ncols;
    int aspect_c = -1;
    float aspect_f = -1.0;

    double scale;
    int skip, no_arrow;
    char *mag_map = NULL;
    void *mag_raster_row = NULL, *mag_ptr = NULL;
    double length = -1;
    int mag_fd = -1;
    struct FPRange range;
    double mag_min, mag_max;

    struct GModule *module;
    struct Option *opt1, *opt2, *opt3, *opt4, *opt5, *opt6, *opt7, *opt8, *opt9;
    struct Flag *align;

    double t, b, l, r;

    G_gisinit(argv[0]);

    module = G_define_module();
    G_add_keyword(_("display"));
    G_add_keyword(_("map annotations"));
    G_add_keyword(_("raster"));
    G_add_keyword(_("arrow"));
    module->description = _("Draws arrows representing cell aspect direction "
                            "for a raster map containing aspect data.");

    opt1 = G_define_standard_option(G_OPT_R_MAP);
    opt1->description = _("Name of raster aspect map to be displayed");

    opt2 = G_define_option();
    opt2->key = "type";
    opt2->type = TYPE_STRING;
    opt2->required = NO;
    opt2->answer = "grass";
    opt2->options = "grass,compass,drainage,agnps,answers,terraflow";
    opt2->description = _("Type of existing raster aspect map");

    opt3 = G_define_standard_option(G_OPT_C);
    opt3->key = "color";
    opt3->answer = "green";
    opt3->label = _("Color for drawing arrows");
    opt3->guisection = _("Colors");

    opt4 = G_define_standard_option(G_OPT_CN);
    opt4->key = "grid_color";
    opt4->answer = "gray";
    opt4->label = _("Color for drawing drawing grid");
    opt4->guisection = _("Colors");

    opt5 = G_define_standard_option(G_OPT_CN);
    opt5->key = "null_color";
    opt5->answer = DEFAULT_FG_COLOR;
    opt5->label = _("Color for drawing null values (X symbol)");
    opt5->guisection = _("Colors");

    opt6 = G_define_standard_option(G_OPT_CN);
    opt6->key = "unknown_color";
    opt6->answer = "red";
    opt6->label = _("Color for showing unknown information (? symbol)");
    opt6->guisection = _("Colors");

    opt9 = G_define_option();
    opt9->key = "skip";
    opt9->type = TYPE_INTEGER;
    opt9->required = NO;
    opt9->answer = "1";
    opt9->description = _("Draw arrow every Nth grid cell");

    opt7 = G_define_option();
    opt7->key = "magnitude_map";
    opt7->type = TYPE_STRING;
    opt7->required = NO;
    opt7->multiple = NO;
    opt7->gisprompt = "old,cell,raster";
    opt7->description = _("Raster map containing values used for arrow length");

    opt8 = G_define_option();
    opt8->key = "scale";
    opt8->type = TYPE_DOUBLE;
    opt8->required = NO;
    opt8->answer = "1.0";
    opt8->description = _("Scale factor for arrows (magnitude map)");

    align = G_define_flag();
    align->key = 'a';
    align->description = _("Align grids with raster cells");

    /* Check command line */
    if (G_parser(argc, argv))
        exit(EXIT_FAILURE);

    layer_name = opt1->answer;

    arrow_color = D_translate_color(opt3->answer);

    /* Convert none (transparent) to -1 which in this module means
       that we will not draw things having this color (-1).
       We don't do that for arrow because we always want them.
       (This is specified by the gisprompt ('type') of the options.)
     */
    if (strcmp("none", opt4->answer) == 0)
        grid_color = -1;
    else
        grid_color = D_translate_color(opt4->answer);

    if (strcmp("none", opt5->answer) == 0)
        x_color = -1;
    else
        x_color = D_translate_color(opt5->answer);

    if (strcmp("none", opt6->answer) == 0)
        unknown_color = -1;
    else
        unknown_color = D_translate_color(opt6->answer);

    if (strcmp("grass", opt2->answer) == 0)
        map_type = 1;
    else if (strcmp("agnps", opt2->answer) == 0)
        map_type = 2;
    else if (strcmp("answers", opt2->answer) == 0)
        map_type = 3;
    else if (strcmp("compass", opt2->answer) == 0)
        map_type = 4;
    else if (strcmp("drainage", opt2->answer) == 0)
        map_type = 5;
    else if (strcmp("terraflow", opt2->answer) == 0)
        map_type = 6;

    scale = atof(opt8->answer);
    if (scale <= 0.0)
        G_fatal_error(_("Illegal value for scale factor"));

    skip = atoi(opt9->answer);
    if (skip <= 0)
        G_fatal_error(_("Illegal value for skip factor"));

    if (opt7->answer) {
        if (map_type != 1 && map_type != 4)
            G_fatal_error(_("Magnitude is only supported for GRASS and compass "
                            "aspect maps."));

        mag_map = opt7->answer;
    }
    else if (scale != 1.0)
        G_warning(_("Scale option requires magnitude_map"));

    /* Setup driver and check important information */
    D_open_driver();

    D_setup(0);

    /* Read in the map window associated with window */
    G_get_window(&window);

    if (align->answer) {
        struct Cell_head wind;

        Rast_get_cellhd(layer_name, "", &wind);

        /* expand window extent by one wind resolution */
        wind.west +=
            wind.ew_res * ((int)((window.west - wind.west) / wind.ew_res) -
                           (window.west < wind.west));
        wind.east +=
            wind.ew_res * ((int)((window.east - wind.east) / wind.ew_res) +
                           (window.east > wind.east));
        wind.south +=
            wind.ns_res * ((int)((window.south - wind.south) / wind.ns_res) -
                           (window.south < wind.south));
        wind.north +=
            wind.ns_res * ((int)((window.north - wind.north) / wind.ns_res) +
                           (window.north > wind.north));

        wind.rows = (wind.north - wind.south) / wind.ns_res;
        wind.cols = (wind.east - wind.west) / wind.ew_res;

        Rast_set_window(&wind);

        nrows = wind.rows;
        ncols = wind.cols;

        t = (wind.north - window.north) * nrows / (wind.north - wind.south);
        b = t +
            (window.north - window.south) * nrows / (wind.north - wind.south);
        l = (window.west - wind.west) * ncols / (wind.east - wind.west);
        r = l + (window.east - window.west) * ncols / (wind.east - wind.west);
    }
    else {
        nrows = window.rows;
        ncols = window.cols;

        t = 0;
        b = nrows;
        l = 0;
        r = ncols;
    }

    D_set_src(t, b, l, r);
    D_update_conversions();

    /* figure out arrow scaling if using a magnitude map */
    if (opt7->answer) {
        Rast_init_fp_range(&range); /* really needed? */
        if (Rast_read_fp_range(mag_map, "", &range) != 1)
            G_fatal_error(_("Problem reading range file"));
        Rast_get_fp_range_min_max(&range, &mag_min, &mag_max);

        scale *= 1.5 / fabs(mag_max);
        G_debug(3, "scaling=%.2f  rast_max=%.2f", scale, mag_max);
    }

    if (grid_color > 0) { /* ie not "none" */
        /* Set color */
        D_use_color(grid_color);

        /* Draw vertical grids */
        for (col = 0; col < ncols; col++)
            D_line_abs(col, 0, col, nrows);

        /* Draw horizontal grids */
        for (row = 0; row < nrows; row++)
            D_line_abs(0, row, ncols, row);
    }

    /* open the raster map */
    layer_fd = Rast_open_old(layer_name, "");

    raster_type = Rast_get_map_type(layer_fd);

    /* allocate the cell array */
    raster_row = Rast_allocate_buf(raster_type);

    if (opt7->answer) {
        /* open the magnitude raster map */
        mag_fd = Rast_open_old(mag_map, "");

        mag_raster_type = Rast_get_map_type(mag_fd);

        /* allocate the cell array */
        mag_raster_row = Rast_allocate_buf(mag_raster_type);
    }

    /* loop through cells, find value, determine direction
       (n,s,e,w,ne,se,sw,nw), and call appropriate function to draw an arrow on
       the cell */

    for (row = 0; row < nrows; row++) {
        Rast_get_row(layer_fd, raster_row, row, raster_type);
        ptr = raster_row;

        if (opt7->answer) {
            Rast_get_row(mag_fd, mag_raster_row, row, mag_raster_type);
            mag_ptr = mag_raster_row;
        }

        for (col = 0; col < ncols; col++) {

            if (row % skip != 0)
                no_arrow = TRUE;
            else
                no_arrow = FALSE;

            if (col % skip != 0)
                no_arrow = TRUE;

            /* find aspect direction based on cell value */
            if (raster_type == CELL_TYPE)
                aspect_f = *((CELL *)ptr);
            else if (raster_type == FCELL_TYPE)
                aspect_f = *((FCELL *)ptr);
            else if (raster_type == DCELL_TYPE)
                aspect_f = *((DCELL *)ptr);

            if (opt7->answer) {

                if (mag_raster_type == CELL_TYPE)
                    length = *((CELL *)mag_ptr);
                else if (mag_raster_type == FCELL_TYPE)
                    length = *((FCELL *)mag_ptr);
                else if (mag_raster_type == DCELL_TYPE)
                    length = *((DCELL *)mag_ptr);

                length *= scale;

                if (Rast_is_null_value(mag_ptr, mag_raster_type)) {
                    G_debug(5, "Invalid arrow length [NULL]. Skipping.");
                    no_arrow = TRUE;
                }
                else if (length <= 0.0) { /* use fabs() or theta+=180? */
                    G_debug(5, "Illegal arrow length [%.3f]. Skipping.",
                            length);
                    no_arrow = TRUE;
                }
            }

            if (no_arrow) {
                ptr = G_incr_void_ptr(ptr, Rast_cell_size(raster_type));
                if (opt7->answer)
                    mag_ptr = G_incr_void_ptr(mag_ptr,
                                              Rast_cell_size(mag_raster_type));
                no_arrow = FALSE;
                continue;
            }

            /* treat AGNPS and ANSWERS data like old zero-as-null CELL */
            /*   TODO: update models */
            if (map_type == 2 || map_type == 3 || map_type == 5 ||
                map_type == 6) {
                if (Rast_is_null_value(ptr, raster_type))
                    aspect_c = 0;
                else if (map_type == 5 && aspect_f < 0)
                    aspect_c = (int)(0.5 - aspect_f);
                else
                    aspect_c = (int)(aspect_f + 0.5);
            }

            /** Now draw the arrows **/

            /* case switch for standard GRASS aspect map
               measured in degrees counter-clockwise from east */
            if (map_type == 1) {
                D_use_color(arrow_color);

                if (Rast_is_null_value(ptr, raster_type)) {
                    /* don't draw anything if x_color is none (transparent) */
                    if (x_color > 0) {
                        D_use_color(x_color);
                        draw_x();
                        D_use_color(arrow_color);
                    }
                }
                else if (aspect_f >= 0.0 && aspect_f <= 360.0) {
                    if (opt7->answer)
                        arrow_mag(aspect_f, length);
                    else
                        arrow_360(aspect_f);
                }
                else if (unknown_color > 0) {
                    /* don't draw if unknown_color is none (transparent) */
                    D_use_color(unknown_color);
                    unknown_();
                    D_use_color(arrow_color);
                }
            }

            /* case switch for AGNPS type aspect map */
            else if (map_type == 2) {
                D_use_color(arrow_color);
                switch (aspect_c) {
                case 0:
                    /* only draw if x_color is not none (transparent) */
                    if (x_color > 0) {
                        D_use_color(x_color);
                        draw_x();
                        D_use_color(arrow_color);
                    }
                    break;
                case 1:
                    arrow_n();
                    break;
                case 2:
                    arrow_ne();
                    break;
                case 3:
                    arrow_e();
                    break;
                case 4:
                    arrow_se();
                    break;
                case 5:
                    arrow_s();
                    break;
                case 6:
                    arrow_sw();
                    break;
                case 7:
                    arrow_w();
                    break;
                case 8:
                    arrow_nw();
                    break;
                default:
                    /* only draw if unknown_color is not none */
                    if (unknown_color > 0) {
                        D_use_color(unknown_color);
                        unknown_();
                        D_use_color(arrow_color);
                    }
                    break;
                }
            }

            /* case switch for ANSWERS type aspect map */
            else if (map_type == 3) {
                D_use_color(arrow_color);
                if (aspect_c >= 15 && aspect_c <= 360) /* start at zero? */
                    arrow_360((double)aspect_c);
                else if (aspect_c == 400) {
                    if (unknown_color > 0) {
                        /* only draw if unknown_color is not none */
                        D_use_color(unknown_color);
                        unknown_();
                        D_use_color(arrow_color);
                    }
                }
                else if (x_color > 0) {
                    /* only draw if x_color is not none (transparent) */
                    D_use_color(x_color);
                    draw_x();
                    D_use_color(arrow_color);
                }
            }

            /* case switch for compass type aspect map
               measured in degrees clockwise from north */
            else if (map_type == 4) {
                D_use_color(arrow_color);

                if (Rast_is_null_value(ptr, raster_type)) {
                    if (x_color > 0) {
                        /* only draw if x_color is not none */
                        D_use_color(x_color);
                        draw_x();
                        D_use_color(arrow_color);
                    }
                }
                else if (aspect_f >= 0.0 && aspect_f <= 360.0) {
                    if (opt7->answer)
                        arrow_mag(90 - aspect_f, length);
                    else
                        arrow_360(90 - aspect_f);
                }
                else if (unknown_color > 0) {
                    /* only draw if unknown_color is not none */
                    D_use_color(unknown_color);
                    unknown_();
                    D_use_color(arrow_color);
                }
            }

            /* case switch for r.watershed drainage type aspect map */
            else if (map_type == 5) {
                D_use_color(arrow_color);
                switch (aspect_c) {
                case 0:
                    /* only draw if x_color is not none (transparent) */
                    if (x_color > 0) {
                        D_use_color(x_color);
                        draw_x();
                        D_use_color(arrow_color);
                    }
                    break;
                case 1:
                    arrow_ne();
                    break;
                case 2:
                    arrow_n();
                    break;
                case 3:
                    arrow_nw();
                    break;
                case 4:
                    arrow_w();
                    break;
                case 5:
                    arrow_sw();
                    break;
                case 6:
                    arrow_s();
                    break;
                case 7:
                    arrow_se();
                    break;
                case 8:
                    arrow_e();
                    break;
                default:
                    /* only draw if unknown_color is not none */
                    if (unknown_color > 0) {
                        D_use_color(unknown_color);
                        unknown_();
                        D_use_color(arrow_color);
                    }
                    break;
                }
            }

            /* case switch for r.terraflow direction type aspect map */
            else if (map_type == 6) {
                D_use_color(arrow_color);
                switch (aspect_c) {
                case 0:
                    /* only draw if x_color is not none (transparent) */
                    if (x_color > 0) {
                        D_use_color(x_color);
                        draw_x();
                        D_use_color(arrow_color);
                    }
                    break;
                case 1:
                    arrow_e();
                    break;
                case 2:
                    arrow_se();
                    break;
                case 4:
                    arrow_s();
                    break;
                case 8:
                    arrow_sw();
                    break;
                case 16:
                    arrow_w();
                    break;
                case 32:
                    arrow_nw();
                    break;
                case 64:
                    arrow_n();
                    break;
                case 128:
                    arrow_ne();
                    break;
                default:
                    /* only draw if unknown_color is not none */
                    if (unknown_color > 0) {
                        D_use_color(unknown_color);
                        unknown_();
                        D_use_color(arrow_color);
                    }
                    break;
                }
            }

            ptr = G_incr_void_ptr(ptr, Rast_cell_size(raster_type));
            if (opt7->answer)
                mag_ptr =
                    G_incr_void_ptr(mag_ptr, Rast_cell_size(mag_raster_type));
        }
    }

    Rast_close(layer_fd);
    if (opt7->answer)
        Rast_close(mag_fd);

    D_save_command(G_recreate_command());
    D_close_driver();

    exit(EXIT_SUCCESS);
}

/* --- end of main --- */

/*---------------------------------------------------------------*/

static void arrow_mag(double theta, double length)
{ /* angle is measured in degrees counter-clockwise from east */
    double x, y, dx, dy, mid_x, mid_y;
    double theta_offset;

    theta *= -1; /* display coords use inverse y */

    /* find the display coordinates of the middle of the cell */
    mid_x = col + (.5);
    mid_y = row + (.5);

    D_begin();

    /* tail */
    D_move_abs(mid_x, mid_y);

    /* head */
    x = mid_x + (length * cos(D2R(theta)));
    y = mid_y + (length * sin(D2R(theta)));
    D_cont_abs(x, y);

    /* fin 1 */
    theta_offset = theta + 20;
    dx = mid_x + (0.6 * length * cos(D2R(theta_offset)));
    dy = mid_y + (0.6 * length * sin(D2R(theta_offset)));
    D_cont_abs(dx, dy);

    /* fin 2 */
    D_move_abs(x, y);
    theta_offset = theta - 20;
    dx = mid_x + (0.6 * length * cos(D2R(theta_offset)));
    dy = mid_y + (0.6 * length * sin(D2R(theta_offset)));
    D_cont_abs(dx, dy);

    D_end();
    D_stroke();
}

static void arrow_360(double theta)
{ /* angle is measured in degrees counter-clockwise from east */
    double x, y, dx, dy, mid_x, mid_y;
    double max_radius, theta_offset;

    theta *= -1; /* display coords use inverse y */
    max_radius = 0.8 / 2;

    /* find the display coordinates of the middle of the cell */
    mid_x = col + (0.5);
    mid_y = row + (0.5);

    D_begin();

    /* head */
    x = mid_x + (max_radius * cos(D2R(theta)));
    y = mid_y + (max_radius * sin(D2R(theta)));
    D_move_abs(x, y);

    /* tail */
    dx = -2 * (max_radius * cos(D2R(theta)));
    dy = -2 * (max_radius * sin(D2R(theta)));
    D_cont_rel(dx, dy);

    /* fin 1 */
    D_move_abs(x, y);
    theta_offset = theta + 90;
    dx = mid_x + (0.5 * max_radius * cos(D2R(theta_offset)));
    dy = mid_y + (0.5 * max_radius * sin(D2R(theta_offset)));
    D_cont_abs(dx, dy);

    /* fin 2 */
    D_move_abs(x, y);
    theta_offset = theta - 90;
    dx = mid_x + (0.5 * max_radius * cos(D2R(theta_offset)));
    dy = mid_y + (0.5 * max_radius * sin(D2R(theta_offset)));
    D_cont_abs(dx, dy);

    D_end();
    D_stroke();
}

static void arrow_se(void)
{
    double x = col + (.8);
    double y = row + (.8);

    D_begin();
    D_move_abs(x, y);
    D_cont_rel(((-.6)), (((-.6))));
    D_move_abs(x, y);
    D_cont_rel(0, ((-.4)));
    D_move_abs(x, y);
    D_cont_rel(((-.4)), 0);
    D_end();
    D_stroke();
}

static void arrow_ne(void)
{
    double x = col + (.8);
    double y = row + (.2);

    D_begin();
    D_move_abs(x, y);
    D_cont_rel(((-.6)), (((.6))));
    D_move_abs(x, y);
    D_cont_rel(0, ((.4)));
    D_move_abs(x, y);
    D_cont_rel(((-.4)), 0);
    D_end();
    D_stroke();
}

static void arrow_nw(void)
{
    double x = col + (.2);
    double y = row + (.2);

    D_begin();
    D_move_abs(x, y);
    D_cont_rel(((.6)), (((.6))));
    D_move_abs(x, y);
    D_cont_rel(0, ((.4)));
    D_move_abs(x, y);
    D_cont_rel(((.4)), 0);
    D_end();
    D_stroke();
}

static void arrow_sw(void)
{
    double x = col + (.2);
    double y = row + (.8);

    D_begin();
    D_move_abs(x, y);
    D_cont_rel(((.6)), (((-.6))));
    D_move_abs(x, y);
    D_cont_rel(0, ((-.4)));
    D_move_abs(x, y);
    D_cont_rel(((.4)), 0);
    D_end();
    D_stroke();
}

static void arrow_e(void)
{
    double x = col + (.9);
    double y = row + (.5);

    D_begin();
    D_move_abs(x, y);
    D_cont_rel(((-.8)), 0);
    D_move_abs(x, y);
    D_cont_rel(((-.3)), ((-.3)));
    D_move_abs(x, y);
    D_cont_rel(((-.3)), ((.3)));
    D_end();
    D_stroke();
}

static void arrow_w(void)
{
    double x = col + (.1);
    double y = row + (.5);

    D_begin();
    D_move_abs(x, y);
    D_cont_rel(((.8)), 0);
    D_move_abs(x, y);
    D_cont_rel(((.3)), ((-.3)));
    D_move_abs(x, y);
    D_cont_rel(((.3)), ((.3)));
    D_end();
    D_stroke();
}

static void arrow_s(void)
{
    double x = col + (.5);
    double y = row + (.9);

    D_begin();
    D_move_abs(x, y);
    D_cont_rel(0, ((-.8)));
    D_move_abs(x, y);
    D_cont_rel(((.3)), ((-.3)));
    D_move_abs(x, y);
    D_cont_rel(((-.3)), ((-.3)));
    D_end();
    D_stroke();
}

static void arrow_n(void)
{
    double x = col + (.5);
    double y = row + (.1);

    D_begin();
    D_move_abs(x, y);
    D_cont_rel(0, ((.8)));
    D_move_abs(x, y);
    D_cont_rel(((.3)), ((.3)));
    D_move_abs(x, y);
    D_cont_rel(((-.3)), ((.3)));
    D_end();
    D_stroke();
}

static void draw_x(void)
{
    double x = col;
    double y = row;

    D_begin();
    D_move_abs(x, y);
    D_cont_rel(1, 1);
    y = row + 1;
    D_move_abs(x, y);
    D_cont_rel(1, (-1));
    D_end();
    D_stroke();
}

static void unknown_(void)
{
    double x = col + (.3);
    double y = row + (.4);

    D_begin();
    D_move_abs(x, y);
    D_cont_rel(0, ((-.15)));
    D_cont_rel(((.1)), ((-.1)));
    D_cont_rel(((.2)), 0);
    D_cont_rel(((.1)), ((.1)));
    D_cont_rel(0, ((.2)));
    D_cont_rel(((-.1)), ((.1)));
    D_cont_rel(((-.1)), 0);
    D_cont_rel(0, ((.25)));
    D_move_rel(0, ((.1)));
    D_cont_rel(0, ((.1)));
    D_end();
    D_stroke();
}
