1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2014 projectchrono.org
5 // All rights reserved.
6 //
7 // Use of this source code is governed by a BSD-style license that can be found
8 // in the LICENSE file at the top level of the distribution and at
9 // http://projectchrono.org/license-chrono.txt.
10 //
11 // =============================================================================
12 // Authors: Alessandro Tasora
13 // =============================================================================
14 //
15 // Demo code about
16 // - using IRRLICHT as a realtime 3D viewer of a four-bar mechanism
17 // - using the IRRLICHT graphical user interface (GUI) to handle user-input
18 //   via mouse.
19 //
20 // =============================================================================
21 
22 #include "chrono/physics/ChLinkMotorRotationSpeed.h"
23 #include "chrono/physics/ChSystemNSC.h"
24 
25 #include "chrono_irrlicht/ChIrrApp.h"
26 
27 // Use the namespaces of Chrono
28 using namespace chrono;
29 using namespace chrono::irrlicht;
30 
31 // Use the main namespaces of Irrlicht
32 using namespace irr;
33 using namespace irr::core;
34 using namespace irr::scene;
35 using namespace irr::video;
36 using namespace irr::io;
37 using namespace irr::gui;
38 
39 // Some static data (this is a simple application, we can do this ;) just to allow easy GUI manipulation
40 IGUIStaticText* text_motorspeed = 0;
41 
42 // The MyEventReceiver class will be used to manage input
43 // from the GUI graphical user interface (the interface will
44 // be created with the basic -yet flexible- platform
45 // independent toolset of Irrlicht).
46 
47 class MyEventReceiver : public IEventReceiver {
48   public:
MyEventReceiver(ChSystemNSC * system,IrrlichtDevice * device,std::shared_ptr<ChLinkMotorRotationSpeed> motor)49     MyEventReceiver(ChSystemNSC* system, IrrlichtDevice* device, std::shared_ptr<ChLinkMotorRotationSpeed> motor) {
50         // store pointer to physical system & other stuff so we can tweak them by user keyboard
51         msystem = system;
52         mdevice = device;
53         mmotor = motor;
54     }
55 
OnEvent(const SEvent & event)56     bool OnEvent(const SEvent& event) {
57         // check if user moved the sliders with mouse..
58         if (event.EventType == EET_GUI_EVENT) {
59             s32 id = event.GUIEvent.Caller->getID();
60 
61             switch (event.GUIEvent.EventType) {
62                 case EGET_SCROLL_BAR_CHANGED:
63                     if (id == 101)  // id of 'motor speed' slider..
64                     {
65                         s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
66                         double newspeed = 10 * (double)pos / 100.0;
67                         // set the speed into motor object
68                         if (auto mfun = std::dynamic_pointer_cast<ChFunction_Const>(mmotor->GetSpeedFunction()))
69                             mfun->Set_yconst(newspeed);
70                         // show speed as formatted text in interface screen
71                         char message[50];
72                         sprintf(message, "Motor speed: %g [rad/s]", newspeed);
73                         text_motorspeed->setText(core::stringw(message).c_str());
74                     }
75                     break;
76                 default:
77                     break;
78             }
79         }
80 
81         return false;
82     }
83 
84   private:
85     ChSystemNSC* msystem;
86     IrrlichtDevice* mdevice;
87     std::shared_ptr<ChLinkMotorRotationSpeed> mmotor;
88 };
89 
main(int argc,char * argv[])90 int main(int argc, char* argv[]) {
91     GetLog() << "Copyright (c) 2017 projectchrono.org\nChrono version: " << CHRONO_VERSION << "\n\n";
92 
93     // 1- Create a Chrono physical system
94     ChSystemNSC my_system;
95 
96     // Create the Irrlicht visualization (open the Irrlicht device,
97     // bind a simple user interface, etc. etc.)
98     ChIrrApp application(&my_system, L"Example of integration of Chrono and Irrlicht, with GUI",
99                          core::dimension2d<u32>(800, 600));
100 
101     // Easy shortcuts to add camera, lights, logo and sky in Irrlicht scene:
102     application.AddTypicalLogo();
103     application.AddTypicalSky();
104     application.AddTypicalLights();
105     application.AddTypicalCamera();
106 
107     // 2- Create the rigid bodies of the four-bar mechanical system
108     //   (a flywheel, a rod, a rocker, a truss), maybe setting
109     //   position/mass/inertias of their center of mass (COG) etc.
110 
111     // ..the truss
112     auto my_body_A = chrono_types::make_shared<ChBody>();
113     my_system.AddBody(my_body_A);
114     my_body_A->SetBodyFixed(true);  // truss does not move!
115 
116     // ..the flywheel
117     auto my_body_B = chrono_types::make_shared<ChBody>();
118     my_system.AddBody(my_body_B);
119     my_body_B->SetPos(ChVector<>(0, 0, 0));  // position of COG of flywheel
120 
121     // ..the rod
122     auto my_body_C = chrono_types::make_shared<ChBody>();
123     my_system.AddBody(my_body_C);
124     my_body_C->SetPos(ChVector<>(4, 0, 0));  // position of COG of rod
125 
126     // ..the rocker
127     auto my_body_D = chrono_types::make_shared<ChBody>();
128     my_system.AddBody(my_body_D);
129     my_body_D->SetPos(ChVector<>(8, -4, 0));  // position of COG of rod
130 
131     // 3- Create constraints: the mechanical joints between the
132     //    rigid bodies. Doesn't matter if some constraints are redundant.
133 
134     // .. a motor between flywheel and truss
135     auto my_link_AB = chrono_types::make_shared<ChLinkMotorRotationSpeed>();
136     my_link_AB->Initialize(my_body_A, my_body_B, ChFrame<>(ChVector<>(0, 0, 0)));
137     my_system.AddLink(my_link_AB);
138     auto my_speed_function = chrono_types::make_shared<ChFunction_Const>(CH_C_PI);  // speed w=3.145 rad/sec
139     my_link_AB->SetSpeedFunction(my_speed_function);
140 
141     // .. a revolute joint between flywheel and rod
142     auto my_link_BC = chrono_types::make_shared<ChLinkLockRevolute>();
143     my_link_BC->Initialize(my_body_B, my_body_C, ChCoordsys<>(ChVector<>(2, 0, 0)));
144     my_system.AddLink(my_link_BC);
145 
146     // .. a revolute joint between rod and rocker
147     auto my_link_CD = chrono_types::make_shared<ChLinkLockRevolute>();
148     my_link_CD->Initialize(my_body_C, my_body_D, ChCoordsys<>(ChVector<>(8, 0, 0)));
149     my_system.AddLink(my_link_CD);
150 
151     // .. a revolute joint between rocker and truss
152     auto my_link_DA = chrono_types::make_shared<ChLinkLockRevolute>();
153     my_link_DA->Initialize(my_body_D, my_body_A, ChCoordsys<>(ChVector<>(8, -8, 0)));
154     my_system.AddLink(my_link_DA);
155 
156     //
157     // Prepare some graphical-user-interface (GUI) items to show
158     // on the screen
159     //
160 
161     // ..add a GUI text and GUI slider to control motor of mechanism via mouse
162     text_motorspeed =
163         application.GetIGUIEnvironment()->addStaticText(L"Motor speed:", rect<s32>(300, 85, 400, 100), false);
164     IGUIScrollBar* scrollbar =
165         application.GetIGUIEnvironment()->addScrollBar(true, rect<s32>(300, 105, 450, 120), 0, 101);
166     scrollbar->setMax(100);
167 
168     // ..Finally create the event receiver, for handling all the GUI (user will use
169     //   buttons/sliders to modify parameters)
170     MyEventReceiver receiver(&my_system, application.GetDevice(), my_link_AB);
171     // note how to add the custom event receiver to the default interface:
172     application.SetUserEventReceiver(&receiver);
173 
174     //
175     // Configure the solver with non-default settings
176     //
177 
178     // By default, the solver uses the EULER_IMPLICIT_LINEARIZED stepper, that is very fast,
179     // but may allow some geometric error in constraints (because it is based on constraint
180     // stabilization). Alternatively, the timestepper EULER_IMPLICIT_PROJECTED is slower,
181     // but it is based on constraint projection, so gaps in constraints are less noticeable
182     // (hence avoids the 'spongy' behaviour of the default stepper, which operates only
183     // on speed-impulse level and keeps constraints'closed' by a continuous stabilization).
184     my_system.SetTimestepperType(ChTimestepper::Type::EULER_IMPLICIT_LINEARIZED);
185 
186     //
187     // THE SOFT-REAL-TIME CYCLE, SHOWING THE SIMULATION
188     //
189 
190     // use this array of points to store trajectory of a rod-point
191     std::vector<chrono::ChVector<> > mtrajectory;
192 
193     application.SetTimestep(0.001);
194 
195     while (application.GetDevice()->run()) {
196         application.BeginScene();
197 
198         // This will draw 3D assets, but in this basic case we will draw some lines later..
199         application.DrawAll();
200 
201         // .. draw a grid
202         tools::drawGrid(application.GetVideoDriver(), 0.5, 0.5);
203         // .. draw a circle representing flywheel
204         tools::drawCircle(application.GetVideoDriver(), 2.1, ChCoordsys<>(ChVector<>(0, 0, 0), QUNIT));
205         // .. draw a small circle representing joint BC
206         tools::drawCircle(application.GetVideoDriver(), 0.06,
207                                ChCoordsys<>(my_link_BC->GetMarker1()->GetAbsCoord().pos, QUNIT));
208         // .. draw a small circle representing joint CD
209         tools::drawCircle(application.GetVideoDriver(), 0.06,
210                                ChCoordsys<>(my_link_CD->GetMarker1()->GetAbsCoord().pos, QUNIT));
211         // .. draw a small circle representing joint DA
212         tools::drawCircle(application.GetVideoDriver(), 0.06,
213                                ChCoordsys<>(my_link_DA->GetMarker1()->GetAbsCoord().pos, QUNIT));
214         // .. draw the rod (from joint BC to joint CD)
215         tools::drawSegment(application.GetVideoDriver(), my_link_BC->GetMarker1()->GetAbsCoord().pos,
216                                 my_link_CD->GetMarker1()->GetAbsCoord().pos, video::SColor(255, 0, 255, 0));
217         // .. draw the rocker (from joint CD to joint DA)
218         tools::drawSegment(application.GetVideoDriver(), my_link_CD->GetMarker1()->GetAbsCoord().pos,
219                                 my_link_DA->GetMarker1()->GetAbsCoord().pos, video::SColor(255, 255, 0, 0));
220         // .. draw the trajectory of the rod-point
221         tools::drawPolyline(application.GetVideoDriver(), mtrajectory, video::SColor(255, 0, 150, 0));
222 
223         // HERE CHRONO INTEGRATION IS PERFORMED: THE
224         // TIME OF THE SIMULATION ADVANCES FOR A SINGLE
225         // STEP:
226         // my_system.DoStepDynamics(0.01);
227 
228         // We need to add another point to the array of 3d
229         // points describing the trajectory to be drawn..
230         mtrajectory.push_back(my_body_C->Point_Body2World(ChVector<>(1, 1, 0)));
231         // keep only last 150 points..
232         if (mtrajectory.size() > 150)
233             mtrajectory.erase(mtrajectory.begin());
234 
235         // THIS PERFORMS THE TIMESTEP INTEGRATION!!!
236         application.DoStep();
237 
238         application.EndScene();
239     }
240 
241     return 0;
242 }
243