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;