class MyMass:CMass
{
        F64 radius;
};

class MySpring:CSpring
{
};

CMathODE *ode=NULL;

U0 DrawIt(CTask *, CDC *dc)
{
        MyMass   *tmpm;
        MySpring *tmps;

        dc->color = RED;
        tmps = ode->next_spring;
        while (tmps != &ode->next_spring)
        {
                GrLine(dc, tmps->end1->x, tmps->end1->y, tmps->end2->x, tmps->end2->y);
                tmps = tmps->next;
        }

        dc->color = BLACK;
        tmpm = ode->next_mass;
        while (tmpm != &ode->next_mass)
        {
                GrCircle(dc, tmpm->x, tmpm->y, tmpm->radius);
                tmpm = tmpm->next;
        }
}

U0 MyDerivative(CMathODE *ode, F64, COrder2D3 *, COrder2D3 *)
{//The forces due to springs and drag are
//automatically handled by the ode code.
        //We can add new forces here.
        F64              d, dd;
        CD3              p;
        MyMass  *tmpm1, *tmpm2;

        tmpm1 = ode->next_mass;
        while (tmpm1 != &ode->next_mass)
        {
                tmpm2 = tmpm1->next;
                while (tmpm2 != &ode->next_mass)
                {
                        D3Sub(&p, &tmpm2->state->x, &tmpm1->state->x);
                        dd = D3NormSqr(&p);
                        if (dd <= Sqr(tmpm1->radius + tmpm2->radius))
                        {
                                d = Sqrt(dd) + 0.0001;
                                dd = 10.0 * Sqr(Sqr(Sqr(tmpm1->radius + tmpm2->radius) - dd));
                                D3MulEqu(&p, dd / d);
                                D3AddEqu(&tmpm2->DstateDt->DxDt, &p);
                                D3SubEqu(&tmpm1->DstateDt->DxDt, &p);
                        }
                        tmpm2 = tmpm2->next;
                }
                tmpm1 = tmpm1->next;
        }
}

U0 PlaceMass(I64 x, I64 y)
{
        MyMass *tmpm = CAlloc(sizeof(MyMass));

        tmpm->mass                                      = 1.0;
        tmpm->drag_profile_factor       = 100.0;
        tmpm->x                                         = x;
        tmpm->y                                         = y;
        tmpm->radius                            = 10 * (Rand + 0.25);
        QueueInsert(tmpm, ode->last_mass);
}

U0 PlaceSpring(MyMass *tmpm1, MyMass *tmpm2)
{
        MySpring *tmps = CAlloc(sizeof(MySpring));

        tmps->end1              = tmpm1;
        tmps->end2              = tmpm2;
        tmps->const             = 10000;
        tmps->rest_len  = 100;
        QueueInsert(tmps, ode->last_spring);
}

U0 Init()
{
        ode = ODENew(0, 1e-4, ODEF_HAS_MASSES);
        ode->derive                             = &MyDerivative;
        ode->drag_v2                    = 0.002;
        ode->drag_v3                    = 0.00001;
        ode->acceleration_limit = 5e3;

        QueueInsert(ode, Fs->last_ode);
}

U0 CleanUp()
{
        QueueRemove(ode);
        QueueDel(&ode->next_mass, TRUE);
        QueueDel(&ode->next_spring, TRUE);
        ODEDel(ode);
}

U0 MassSpringDemo()
{
        I64              message_code, arg1, arg2;
        MyMass  *tmpm1 = NULL, *tmpm2 = NULL;

        PopUpOk("Left-Click to place mas\n"
                        "Right-Click and drag to\n"
                        "connect with spring.\n\n"
                        "Springs are 100 pixs long.\n");
        SettingsPush; //See SettingsPush
        AutoComplete;
        WinBorder;
        WinMax;
        DocCursor;
        DocClear;

        Fs->win_inhibit |= WIG_DBL_CLICK;

        MenuPush(       "File {"
                                "  Abort(,CH_SHIFT_ESC);"
                                "  Exit(,CH_ESC);"
                                "}"
                                "Play {"
                                "  Restart(,'\n');"
                                "}"
                                );

        Init;
        Fs->draw_it = &DrawIt;

        try
        {
                while (TRUE)
                {
                        message_code = MessageGet(&arg1, &arg2, 
                                                        1 << MESSAGE_MS_L_DOWN | 1 << MESSAGE_MS_R_DOWN | 1 << MESSAGE_MS_R_UP | 1 << MESSAGE_KEY_DOWN);
                        switch (message_code)
                        {
                                case MESSAGE_MS_L_DOWN:
                                        PlaceMass(arg1, arg2);
                                        break;

                                case MESSAGE_MS_R_DOWN:
                                        tmpm1 = MassFind(ode, arg1, arg2);
                                        tmpm2 = NULL;
                                        break;

                                case MESSAGE_MS_R_UP:
                                        if (tmpm1 && (tmpm2=MassFind(ode, arg1, arg2)) && tmpm1 != tmpm2)
                                                PlaceSpring(tmpm1, tmpm2);
                                        tmpm1 = tmpm2 = NULL;
                                        break;

                                case MESSAGE_KEY_DOWN:
                                        switch (arg1)
                                        {
                                                case '\n':
                                                        CleanUp;
                                                        Init;
                                                        break;

                                                case CH_SHIFT_ESC:
                                                case CH_ESC:
                                                        goto mouse_done;
                                        }
                                        break;
                        }
                }
mouse_done: //Don't goto out of try
                MessageGet(,, 1 << MESSAGE_KEY_UP);
        }
        catch
                PutExcept;
        SettingsPop;
        CleanUp;
        MenuPop;
}

MassSpringDemo;