I64 box_x_min, box_x_max, box_y_min, box_y_max;

class Arrow
{
        Arrow   *next, *last;
        F64              x, y, dx, dy;

} head;

         <1>/* Graphics Not Rendered in HTML */

Bool bow_drawn;
F64  bow_x, bow_y, bow_theta;

U0 DrawIt(CTask *task, CDC *dc)
{
        F64              theta, x, y, dx, dy, str_w, str_h, draw_len;
        Arrow   *tmpa;
        CD3I32   ctrl[5];

        dc->color = RED;
        GrBorder(dc, box_x_min, box_y_min, box_x_max, box_y_max);

        x = ClampI64(mouse.pos.x - task->pix_left - task->scroll_x, box_x_min, box_x_max);
        y = ClampI64(mouse.pos.y - task->pix_top  - task->scroll_y, box_y_min, box_y_max);
        dx = bow_x - x;
        dy = bow_y - y;

        if (bow_drawn && (dx | dy))
                bow_theta = Arg(dx, dy);
        else
        {
                bow_x = x;
                bow_y = y;
        }

        draw_len = Sqrt(dx * dx + dy * dy);
        str_w = draw_len / 3;
        str_h = Sqrt(60 * 60 - str_w * str_w);

        dc->color = BLACK;
        GrLine(dc,      x - str_h / 2 * Cos(bow_theta + pi / 2) + str_w * Cos(bow_theta),
                                y - str_h / 2 * Sin(bow_theta + pi / 2) + str_w * Sin(bow_theta), x, y);
        GrLine(dc,      x + str_h / 2 * Cos(bow_theta + pi / 2) + str_w * Cos(bow_theta), 
                                y + str_h / 2 * Sin(bow_theta + pi / 2) + str_w * Sin(bow_theta), x, y);

        MemSet(ctrl, 0, sizeof(ctrl));
        ctrl[0].x = x - str_h / 2 * Cos(bow_theta + pi / 2) + str_w * Cos(bow_theta);
        ctrl[0].y = y - str_h / 2 * Sin(bow_theta + pi / 2) + str_w * Sin(bow_theta);
        ctrl[1].x = x - 0.75 * str_h / 2 * Cos(bow_theta + pi / 2) + draw_len / 2 * Cos(bow_theta) + str_w * Cos(bow_theta);
        ctrl[1].y = y - 0.75 * str_h / 2 * Sin(bow_theta + pi / 2) + draw_len / 2 * Sin(bow_theta) + str_w * Sin(bow_theta);
        ctrl[2].x = x + draw_len / 2 * Cos(bow_theta) + str_w * Cos(bow_theta);
        ctrl[2].y = y + draw_len / 2 * Sin(bow_theta) + str_w * Sin(bow_theta);
        ctrl[3].x = x + 0.75 * str_h / 2 * Cos(bow_theta + pi / 2) + draw_len / 2 * Cos(bow_theta) + str_w * Cos(bow_theta);
        ctrl[3].y = y + 0.75 * str_h / 2 * Sin(bow_theta + pi / 2) + draw_len / 2 * Sin(bow_theta) + str_w * Sin(bow_theta);
        ctrl[4].x = x + str_h / 2 * Cos(bow_theta + pi / 2) + str_w * Cos(bow_theta);
        ctrl[4].y = y + str_h / 2 * Sin(bow_theta + pi / 2) + str_w * Sin(bow_theta);

        dc->color = BROWN;
        dc->thick = 2;
        Gr2BSpline3(dc, ctrl, 5);
        dc->thick = 1;

        if (bow_drawn)
                Sprite3ZB(dc, x, y, 0, <1>, bow_theta);

        tmpa = head.next;
        while (tmpa != &head)
        {
                theta = Arg(tmpa->dx, tmpa->dy);
                Sprite3ZB(dc, tmpa->x, tmpa->y, 0, <1>, theta);
                tmpa = tmpa->next;
        }
}

#define ANIMATE_SLEEP_MS                                10

U0 AnimateTask(I64)
{
        I64              x, y;
        Arrow   *tmpa, *tmpa1;
        F64              dt, t0 = tS;

        while (TRUE)
        {
                dt = tS - t0;
                t0 = tS;

                x = ClampI64(mouse.pos.x - Fs->parent_task->pix_left - Fs->parent_task->scroll_x,
                                         box_x_min, box_x_max) + Fs->parent_task->pix_left + Fs->parent_task->scroll_x;
                y = ClampI64(mouse.pos.y - Fs->parent_task->pix_top - Fs->parent_task->scroll_y, 
                                         box_y_min, box_y_max) + Fs->parent_task->pix_top + Fs->parent_task->scroll_y;
                if (mouse.pos.x != x || mouse.pos.y != y)
                        MouseSet(x, y);

                tmpa = head.next;
                while (tmpa != &head)
                {
                        tmpa1 = tmpa->next;
                        tmpa->x += tmpa->dx * dt;
                        tmpa->y += tmpa->dy * dt;
                        if (!(-Fs->parent_task->scroll_x <= tmpa->x < Fs->parent_task->pix_width  - Fs->parent_task->scroll_x) ||
                                !(-Fs->parent_task->scroll_y <= tmpa->y < Fs->parent_task->pix_height - Fs->parent_task->scroll_y))
                        {
                                QueueRemove(tmpa);
                                Free(tmpa);
                        }
                        tmpa = tmpa1;
                }
                Refresh;
        }
}

U0 Init()
{
        I64 w = Fs->pix_width, h = Fs->pix_height;

        QueueInit(&head);
        bow_drawn = FALSE;
        box_x_min = 7 * w / 16;
        box_y_min = 6 * h / 8;
        box_x_max = 9 * w / 16;
        box_y_max = 7 * h / 8;
        bow_theta = -pi / 2;
        bow_x = (box_x_min + box_x_max) / 2;
        bow_y = (box_y_min + box_y_max) / 2;
        MouseSet(bow_x + Fs->pix_left + Fs->scroll_x, bow_y + Fs->pix_top + Fs->scroll_y);
}

U0 CleanUp()
{
        QueueDel(&head, TRUE);
}

U0 Zing()
{
        I64              arg1, arg2;
        Arrow   *tmpa;

        MenuPush(       "File {"
                                "  Abort(,CH_SHIFT_ESC);"
                                "  Exit(,CH_ESC);"
                                "}"
                                "Play {"
                                "  Restart(,'\n');"
                                "}"
                                );
        SettingsPush; //See SettingsPush
        AutoComplete;
        WinBorder;
        WinMax;
        DocCursor;
        DocClear;

        Init;
        Fs->animate_task = Spawn(&AnimateTask, NULL, "Animate",, Fs);
        Fs->draw_it              = &DrawIt;
        Fs->win_inhibit  = WIG_TASK_DEFAULT - WIF_SELF_FOCUS - WIF_SELF_GRAB_SCROLL;
        try
        {
                while (TRUE)
                        switch (MessageGet(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN | 1 << MESSAGE_MS_L_DOWN | 1 << MESSAGE_MS_L_UP))
                        {
                                case MESSAGE_KEY_DOWN:
                                        switch (arg1)
                                        {
                                                case '\n':
                                                        CleanUp;
                                                        Init;
                                                        break;

                                                case CH_ESC:
                                                case CH_SHIFT_ESC:
                                                        goto zi_done;
                                        }
                                        break;

                                case MESSAGE_MS_L_DOWN:
                                        bow_x = arg1;
                                        bow_y = arg2;
                                        bow_drawn = TRUE;
                                        break;

                                case MESSAGE_MS_L_UP:
                                        if (arg1-bow_x || arg2-bow_y)
                                        {
                                                tmpa = MAlloc(sizeof(Arrow));
                                                tmpa->dx = 10.0 * (bow_x - arg1);
                                                tmpa->dy = 10.0 * (bow_y - arg2);
                                                tmpa->x = arg1;
                                                tmpa->y = arg2;
                                                QueueInsert(tmpa, head.last);
                                                Noise(50, 110, 114);
                                        }
                                        bow_drawn = FALSE;
                                        break;
                        }
zi_done:
                MessageGet(,, 1 << MESSAGE_KEY_UP);
        }
        catch
                PutExcept;
        SettingsPop;
        CleanUp;
        MenuPop;
}

Zing;