/*This highlights the 4th dimension
of the transformation matrix which
is used for storing translations (shifts)
in matricies so they can be combined
with a matrix multiplication.

Scroll bar works.

The multiplication takes place in the
Sprite3ZB routine.
*/

#define SLIDER_SPACING  20
#define SLIDER_RANGE    30
#define SLIDER_BORDER   2

class CSliderState
{
    I64 s1, s2, s3;
    F64 arg1, arg2, scale;
} s;

U0 DrawCtrlSlider(CDC *dc, CCtrl *c)
{
    CSliderState *s = c->state;

    dc->color = LTGREEN;
    GrRect(dc,  c->left, c->top, SLIDER_SPACING * 4 + 3, SLIDER_SPACING * 2 + SLIDER_RANGE);
    dc->color = BROWN;
    GrRect(dc,  c->left + SLIDER_BORDER, c->top + SLIDER_BORDER, 
                SLIDER_SPACING * 4 + 3 - 2 * SLIDER_BORDER, 
                SLIDER_SPACING * 2 + SLIDER_RANGE - 2 * SLIDER_BORDER);
    dc->color = BLACK;
    GrLine(dc,  c->left + SLIDER_SPACING, c->top + SLIDER_SPACING, 
                c->left + SLIDER_SPACING, c->top + SLIDER_SPACING + SLIDER_RANGE - 1);

    GrLine(dc,  c->left + 2 * SLIDER_SPACING + 1, c->top + SLIDER_SPACING, 
                c->left + 2 * SLIDER_SPACING + 1, c->top + SLIDER_SPACING + SLIDER_RANGE - 1);

    GrLine(dc,  c->left + 3 * SLIDER_SPACING + 2, c->top + SLIDER_SPACING, 
                c->left + 3 * SLIDER_SPACING + 2, c->top + SLIDER_SPACING + SLIDER_RANGE - 1);

    dc->color = LTGREEN;
    GrPrint(dc, c->left + SLIDER_SPACING - FONT_WIDTH / 2, 
                c->top + SLIDER_SPACING + SLIDER_RANGE + 3, 
                "%d", s->s1 * 10 / SLIDER_RANGE);
    GrPrint(dc, c->left + 2 * SLIDER_SPACING + 1 - FONT_WIDTH / 2, 
                c->top + SLIDER_SPACING + SLIDER_RANGE + 3, 
                "%d", s->s2 * 10 / SLIDER_RANGE);
    GrPrint(dc, c->left + 3 * SLIDER_SPACING + 2 - FONT_WIDTH / 2, 
                c->top + SLIDER_SPACING + SLIDER_RANGE + 3, 
                "%d", s->s3 * 10 / SLIDER_RANGE);
    GrRect(dc,  c->left + SLIDER_SPACING - 3, 
                c->top + SLIDER_SPACING + SLIDER_RANGE - 1 - s->s1 - 2 , 7, 5);
    GrRect(dc,  c->left + 2 * SLIDER_SPACING + 1 - 3, 
                c->top + SLIDER_SPACING + SLIDER_RANGE - 1 - s->s2 - 2, 7, 5);
    GrRect(dc,  c->left + 3 * SLIDER_SPACING + 2 - 3, 
                c->top + SLIDER_SPACING + SLIDER_RANGE - 1 - s->s3 - 2, 7, 5);
    dc->color = GREEN;
    GrRect(dc,  c->left + SLIDER_SPACING - 2, 
                c->top + SLIDER_SPACING + SLIDER_RANGE - 1 - s->s1 - 1 , 5, 3);
    GrRect(dc,  c->left + 2 * SLIDER_SPACING + 1 - 2, 
                c->top + SLIDER_SPACING + SLIDER_RANGE - 1 - s->s2 - 1, 5, 3);
    GrRect(dc,  c->left + 3 * SLIDER_SPACING + 2 - 2, 
                c->top + SLIDER_SPACING + SLIDER_RANGE - 1 - s->s3 - 1, 5, 3);
}

U0 UpdateDerivedCtrlSlider(CCtrl *c)
{
    CSliderState *s = c->state;

    c->left     = c->win_task->pix_width / 2 - (SLIDER_SPACING * 4 + 3) / 2;
    c->right    = c->left + SLIDER_SPACING * 4 + 3;
    c->top      = c->win_task->pix_height / 2 - (SLIDER_SPACING * 2 + SLIDER_RANGE) / 2;
    c->bottom   = c->top + SLIDER_SPACING * 2 + SLIDER_RANGE;
    s->s1       = ClampI64(s->s1, 0, SLIDER_RANGE - 1);
    s->s2       = ClampI64(s->s2, 0, SLIDER_RANGE - 1);
    s->s3       = ClampI64(s->s3, 1, SLIDER_RANGE - 1);
    s->arg1     = pi / 2.0 * s->s1 / SLIDER_RANGE;
    s->arg2     = 1.0 * (s->s2 - SLIDER_RANGE / 2) / SLIDER_RANGE;
    s->scale    = 2.0 * s->s3 / SLIDER_RANGE;
}

U0 LeftClickSlider(CCtrl *c, I64 x, I64 y, Bool)
{
    CSliderState *s = c->state;

    if (x < c->left + (c->right - c->left) / 3)
        s->s1 = SLIDER_RANGE - 1 - (y - (c->top + SLIDER_SPACING));
    else if (x < c->left + 2 * (c->right - c->left) / 3)
        s->s2 = SLIDER_RANGE - 1 - (y - (c->top + SLIDER_SPACING));
    else
        s->s3 = SLIDER_RANGE - 1 - (y - (c->top + SLIDER_SPACING));
    if (c->update_derived_vals)
        (*c->update_derived_vals)(c);
}

CCtrl *SliderNew()
{
    CCtrl *c = CAlloc(sizeof(CCtrl));

    c->win_task = Fs;
    c->flags    = CTRLF_SHOW | CTRLF_CAPTURE_LEFT_MS;
    c->type     = CTRLT_GENERIC;
    c->state    = &s;
    MemSet(&s, 0, sizeof(s));
    c->draw_it              = &DrawCtrlSlider;
    c->left_click           = &LeftClickSlider;
    c->update_derived_vals  = &UpdateDerivedCtrlSlider;
    QueueInsert(c, Fs->last_ctrl);
    TaskDerivedValsUpdate;

    return c;
}

U0 SliderDel(CCtrl *c)
{
    QueueRemove(c);
    Free(c);
}





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

#define MAP_HEIGHT          2048
#define MAP_WIDTH           2048

#define TREES_NUM           256

I64 tree_x[TREES_NUM], tree_y[TREES_NUM];

class MPCtrl
{
    I64 mp_count;
    I64 r[16];
    F64 a;

} mp;

I64 mp_not_done_flags;

U0 MPDrawIt(CTask *task)
{
    CDC *dc = DCAlias(gr.dc2, task);
    I64  i, lo = Gs->num * TREES_NUM / mp.mp_count, hi = (Gs->num + 1) * TREES_NUM / mp.mp_count;

    MemCopy(dc->r, mp.r, sizeof(I64) * 16);
    dc->flags |= DCF_TRANSFORMATION;
    for (i = lo; i < hi; i++)
        Sprite3ZB(dc, tree_x[i], tree_y[i], 0, <tree>, mp.a);
    DCDel(dc);
    LBtr(&mp_not_done_flags, Gs->num);
}

U0 DrawIt(CTask *task, CDC *dc)
{
    I64 i, h, v;

    task->horz_scroll.min = -s.scale * MAP_HEIGHT * Sin(s.arg1);
    task->horz_scroll.max =  s.scale * MAP_WIDTH  * Cos(s.arg1) - task->pix_width;
    task->vert_scroll.min = 0;
    task->vert_scroll.max = s.scale * (MAP_HEIGHT * Cos(s.arg1) + MAP_WIDTH * Sin(s.arg1))
    -task->pix_height;
    TaskDerivedValsUpdate(task);

    h = task->horz_scroll.pos;
    v = task->vert_scroll.pos;

    Mat4x4RotZ(dc->r, s.arg1);
    Mat4x4Scale(dc->r, s.scale);
    DCMat4x4Set(dc, dc->r);
    Mat4x4TranslationEqu(dc->r, -h, -v, 0);
    MemCopy(mp.r, dc->r, sizeof(I64) * 16);
    mp.a = s.arg2;

    mp_not_done_flags = 1 << mp_count - 1;
    for (i = 0; i < mp.mp_count; i++)
        JobQueue(&MPDrawIt, task, i);
    while (mp_not_done_flags)
        Yield;

    dc->flags |= DCF_TRANSFORMATION;
    dc->color = BROWN;
    dc->thick = 4;
    GrLine3(dc, 2, 2, 0, MAP_WIDTH - 3, 2, 0);
    GrLine3(dc, 2, MAP_HEIGHT - 3, 0, MAP_WIDTH - 3, MAP_HEIGHT - 3, 0);
    GrLine3(dc, 2, 2, 0, 2, MAP_HEIGHT - 3, 0);
    GrLine3(dc, MAP_WIDTH - 3, 2, 0, MAP_WIDTH - 3, MAP_HEIGHT - 3, 0);
}

U0 Init()
{
    I64 i;

    for (i = 0; i < TREES_NUM; i++)
    {
        tree_x[i] = RandU16 % MAP_WIDTH;
        tree_y[i] = RandU16 % MAP_HEIGHT;
    }
}

U0 TransformDemo(I64 _mp_count=mp_count)
{
    SettingsPush; //See SettingsPush
    Init;
    Fs->win_inhibit = WIG_TASK_DEFAULT -
                        WIF_SELF_FOCUS - WIF_SELF_BORDER - WIF_SELF_GRAB_SCROLL - WIF_FOCUS_TASK_MENU - WIF_SELF_CTRLS;
    Fs->draw_it = &DrawIt;
    WinBorder(ON);
    DocCursor;
    DocClear;
    DocScroll;

    Fs->horz_scroll.pos = 0;
    Fs->vert_scroll.pos = 0;

    CCtrl *c = SliderNew;
    s.s1 = 0;
    s.s2 = SLIDER_RANGE / 2;
    s.s3 = SLIDER_RANGE / 2;

    MemSet(&mp, 0, sizeof(MPCtrl));
    mp.mp_count = _mp_count;

    View;
    SliderDel(c);
    SettingsPop;
//You might want to save the original state of the scroll bars.
}

TransformDemo;