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: Radu Serban
13 // =============================================================================
14 //
15 // =============================================================================
16 
17 #include "chrono/assets/ChBoxShape.h"
18 #include "chrono/assets/ChCapsuleShape.h"
19 #include "chrono/assets/ChColorAsset.h"
20 #include "chrono/assets/ChConeShape.h"
21 #include "chrono/assets/ChCylinderShape.h"
22 #include "chrono/assets/ChEllipsoidShape.h"
23 #include "chrono/assets/ChLineShape.h"
24 #include "chrono/assets/ChPointPointDrawing.h"
25 #include "chrono/assets/ChRoundedBoxShape.h"
26 #include "chrono/assets/ChRoundedCylinderShape.h"
27 #include "chrono/assets/ChSphereShape.h"
28 #include "chrono/assets/ChTriangleMeshShape.h"
29 #include "chrono/geometry/ChLineBezier.h"
30 #include "chrono/utils/ChUtilsInputOutput.h"
31 
32 namespace chrono {
33 namespace utils {
34 
35 // -----------------------------------------------------------------------------
36 // WriteBodies
37 //
38 // Write to a CSV file pody position, orientation, and (optionally) linear and
39 // angular velocity. Optionally, only active bodies are processed.
40 // -----------------------------------------------------------------------------
WriteBodies(ChSystem * system,const std::string & filename,bool active_only,bool dump_vel,const std::string & delim)41 void WriteBodies(ChSystem* system,
42                  const std::string& filename,
43                  bool active_only,
44                  bool dump_vel,
45                  const std::string& delim) {
46     CSV_writer csv(delim);
47 
48     for (auto body : system->Get_bodylist()) {
49         if (active_only && !body->IsActive())
50             continue;
51         csv << body->GetPos() << body->GetRot();
52         if (dump_vel)
53             csv << body->GetPos_dt() << body->GetWvel_loc();
54         csv << std::endl;
55     }
56 
57     csv.write_to_file(filename);
58 }
59 
60 // -----------------------------------------------------------------------------
61 // WriteCheckpoint
62 //
63 // Create a CSV file with a checkpoint ...
64 //
65 // -----------------------------------------------------------------------------
WriteCheckpoint(ChSystem * system,const std::string & filename)66 bool WriteCheckpoint(ChSystem* system, const std::string& filename) {
67     // Create the CSV stream.
68     CSV_writer csv(" ");
69     std::string tab("    ");
70 
71     // Write contact method type (0: NSC, 1: SMC)
72     int ctype = (system->GetContactMethod() == ChContactMethod::NSC) ? 0 : 1;
73     csv << ctype << std::endl;
74 
75     for (auto body : system->Get_bodylist()) {
76         // Write body identifier, the body fixed flag, and the collide flag
77         csv << body->GetIdentifier() << body->GetBodyFixed() << body->GetCollide() << tab;
78 
79         // Write collision family information.
80         csv << body->GetCollisionModel()->GetFamilyGroup() << body->GetCollisionModel()->GetFamilyMask() << tab;
81 
82         // Write body mass and inertia
83         csv << body->GetMass() << body->GetInertiaXX() << tab;
84 
85         // Write body position, orientation, and their time derivatives
86         csv << body->GetPos() << body->GetRot() << tab;
87         csv << body->GetPos_dt() << body->GetRot_dt() << tab;
88 
89         csv << std::endl;
90 
91         // Write number of collision shapes
92         int n_shapes = body->GetCollisionModel()->GetNumShapes();
93         csv << n_shapes << std::endl;
94 
95         // Loop over each shape and write its data on a separate line.
96         // If we encounter an unsupported type, return false.
97         for (int index = 0; index < n_shapes; index++) {
98             auto shape = body->GetCollisionModel()->GetShape(index);
99 
100             // Write relative position and rotation
101             ChCoordsys<> csys = body->GetCollisionModel()->GetShapePos(index);
102             csv << csys.pos << csys.rot << tab;
103 
104             // Write shape material information
105             if (ctype == 0) {
106                 auto mat = std::static_pointer_cast<ChMaterialSurfaceNSC>(shape->GetMaterial());
107                 csv << mat->static_friction << mat->sliding_friction << mat->rolling_friction << mat->spinning_friction;
108                 csv << mat->restitution << mat->cohesion << mat->dampingf;
109                 csv << mat->compliance << mat->complianceT << mat->complianceRoll << mat->complianceSpin;
110                 csv << tab;
111             } else {
112                 auto mat = std::static_pointer_cast<ChMaterialSurfaceSMC>(shape->GetMaterial());
113                 csv << mat->young_modulus << mat->poisson_ratio;
114                 csv << mat->static_friction << mat->sliding_friction;
115                 csv << mat->restitution << mat->constant_adhesion << mat->adhesionMultDMT;
116                 csv << mat->kn << mat->gn << mat->kt << mat->gt;
117                 csv << tab;
118             }
119 
120             // Write shape type and characteristic dimensions
121             std::vector<double> dims = body->GetCollisionModel()->GetShapeDimensions(index);
122             if (dims.empty()) {
123                 std::cout << "utils::WriteCheckpoint ERROR: unknown or not supported collision shape\n";
124                 return false;
125             }
126 
127             csv << shape->GetType() << dims;
128 
129             csv << std::endl;
130         }
131     }
132 
133     csv.write_to_file(filename);
134 
135     return true;
136 }
137 
138 // -----------------------------------------------------------------------------
139 // ReadCheckpoint
140 //
141 // Read a CSV file with checkpoint data and create the bodies.
142 //
143 // -----------------------------------------------------------------------------
ReadCheckpoint(ChSystem * system,const std::string & filename)144 void ReadCheckpoint(ChSystem* system, const std::string& filename) {
145     // Open input file stream
146     std::ifstream ifile(filename.c_str());
147     std::string line;
148 
149     // Read the contact method type
150     std::getline(ifile, line);
151     std::istringstream iss0(line);
152     int ctype;
153     iss0 >> ctype;
154     auto contact_method = (ctype == 0) ? ChContactMethod::NSC : ChContactMethod::SMC;
155 
156     // Check consistency with the current system
157     if (contact_method != system->GetContactMethod()) {
158         std::cout << "utils::ReadCheckpoint ERROR: checkpoint data file inconsistent with the Chrono system\n";
159         std::cout << "    Contact method in data file: " << (ctype == 0 ? "NSC" : "SMC") << "\n";
160         return;
161     }
162 
163     while (std::getline(ifile, line)) {
164         std::istringstream iss1(line);
165 
166         // Read body Id and flags
167         int bid, bfixed, bcollide;
168         short family_group, family_mask;
169         iss1 >> bid >> bfixed >> bcollide >> family_group >> family_mask;
170 
171         // Read body mass and inertia
172         double mass;
173         ChVector<> inertiaXX;
174         iss1 >> mass >> inertiaXX.x() >> inertiaXX.y() >> inertiaXX.z();
175 
176         // Read body position, orientation, and their time derivatives
177         ChVector<> bpos, bpos_dt;
178         ChQuaternion<> brot, brot_dt;
179         iss1 >> bpos.x() >> bpos.y() >> bpos.z();
180         iss1 >> brot.e0() >> brot.e1() >> brot.e2() >> brot.e3();
181         iss1 >> bpos_dt.x() >> bpos_dt.y() >> bpos_dt.z();
182         iss1 >> brot_dt.e0() >> brot_dt.e1() >> brot_dt.e2() >> brot_dt.e3();
183 
184         // Create a body of the appropriate type
185         auto body = std::shared_ptr<ChBody>(system->NewBody());
186         system->AddBody(body);
187 
188         // Set body properties and state
189         body->SetPos(bpos);
190         body->SetRot(brot);
191         body->SetPos_dt(bpos_dt);
192         body->SetRot_dt(brot_dt);
193 
194         body->SetIdentifier(bid);
195         body->SetBodyFixed(bfixed != 0);
196         body->SetCollide(bcollide != 0);
197 
198         body->SetMass(mass);
199         body->SetInertiaXX(inertiaXX);
200 
201         // Get next line in the file (number of collision shapes)
202         std::getline(ifile, line);
203         std::istringstream iss3(line);
204 
205         int n_shapes;
206         iss3 >> n_shapes;
207 
208         // In a loop, read information about each shape and add geometry to the body
209         body->GetCollisionModel()->ClearModel();
210 
211         for (int j = 0; j < n_shapes; j++) {
212             std::getline(ifile, line);
213             std::istringstream iss(line);
214 
215             // Get shape relative position and rotation
216             ChVector<> spos;
217             ChQuaternion<> srot;
218             iss >> spos.x() >> spos.y() >> spos.z() >> srot.e0() >> srot.e1() >> srot.e2() >> srot.e3();
219 
220             // Get material information and create the material
221             std::shared_ptr<ChMaterialSurface> mat;
222             if (ctype == 0) {
223                 auto matNSC = chrono_types::make_shared<ChMaterialSurfaceNSC>();
224                 iss >> matNSC->static_friction >> matNSC->sliding_friction >> matNSC->rolling_friction >>
225                     matNSC->spinning_friction;
226                 iss >> matNSC->restitution >> matNSC->cohesion >> matNSC->dampingf;
227                 iss >> matNSC->compliance >> matNSC->complianceT >> matNSC->complianceRoll >> matNSC->complianceSpin;
228                 mat = matNSC;
229             } else {
230                 auto matSMC = chrono_types::make_shared<ChMaterialSurfaceSMC>();
231                 iss >> matSMC->young_modulus >> matSMC->poisson_ratio;
232                 iss >> matSMC->static_friction >> matSMC->sliding_friction;
233                 iss >> matSMC->restitution >> matSMC->constant_adhesion >> matSMC->adhesionMultDMT;
234                 iss >> matSMC->kn >> matSMC->gn >> matSMC->kt >> matSMC->gt;
235                 mat = matSMC;
236             }
237 
238             // Get shape type and geometry data and create the shape (both visualization and contact).
239             int stype;
240             iss >> stype;
241 
242             switch (collision::ChCollisionShape::Type(stype)) {
243                 case collision::ChCollisionShape::Type::SPHERE: {
244                     double radius;
245                     iss >> radius;
246                     AddSphereGeometry(body.get(), mat, radius, spos, srot);
247                 } break;
248                 case collision::ChCollisionShape::Type::ELLIPSOID: {
249                     ChVector<> size;
250                     iss >> size.x() >> size.y() >> size.z();
251                     AddEllipsoidGeometry(body.get(), mat, size, spos, srot);
252                 } break;
253                 case collision::ChCollisionShape::Type::BOX: {
254                     ChVector<> size;
255                     iss >> size.x() >> size.y() >> size.z();
256                     AddBoxGeometry(body.get(), mat, size, spos, srot);
257                 } break;
258                 case collision::ChCollisionShape::Type::CAPSULE: {
259                     double radius, hlen;
260                     iss >> radius >> hlen;
261                     AddCapsuleGeometry(body.get(), mat, radius, hlen, spos, srot);
262                 } break;
263                 case collision::ChCollisionShape::Type::CYLINDER: {
264                     double radius, hlen, dummy;
265                     iss >> radius >> dummy >> hlen;
266                     AddCylinderGeometry(body.get(), mat, radius, hlen, spos, srot);
267                 } break;
268                 case collision::ChCollisionShape::Type::CONE: {
269                     double radius, height, dummy;
270                     iss >> radius >> dummy >> height;
271                     AddConeGeometry(body.get(), mat, radius, height, spos, srot);
272                 } break;
273                 case collision::ChCollisionShape::Type::ROUNDEDBOX: {
274                     ChVector<> size;
275                     double srad;
276                     iss >> size.x() >> size.y() >> size.z() >> srad;
277                     AddRoundedBoxGeometry(body.get(), mat, size, srad, spos, srot);
278                 } break;
279                 case collision::ChCollisionShape::Type::ROUNDEDCYL: {
280                     double radius, hlen, srad, dummy;
281                     iss >> radius >> dummy >> hlen >> srad;
282                     AddRoundedCylinderGeometry(body.get(), mat, radius, hlen, srad, spos, srot);
283                 } break;
284                 default:
285                     break;
286             }
287         }
288 
289         // Set the collision family group and the collision family mask.
290         body->GetCollisionModel()->SetFamilyGroup(family_group);
291         body->GetCollisionModel()->SetFamilyMask(family_mask);
292 
293         // Complete construction of the collision model.
294         body->GetCollisionModel()->BuildModel();
295     }
296 }
297 
298 // -----------------------------------------------------------------------------
299 // Write CSV output file with current camera information
300 // -----------------------------------------------------------------------------
WriteCamera(const std::string & filename,const ChVector<> & cam_location,const ChVector<> & cam_target,const ChVector<> & camera_upvec,const std::string & delim)301 void WriteCamera(const std::string& filename,
302                  const ChVector<>& cam_location,
303                  const ChVector<>& cam_target,
304                  const ChVector<>& camera_upvec,
305                  const std::string& delim) {
306     CSV_writer csv(delim);
307     csv << cam_location << std::endl;
308     csv << cam_target << std::endl;
309     csv << camera_upvec << std::endl;
310     csv.write_to_file(filename);
311 }
312 
313 // -----------------------------------------------------------------------------
314 // Write CSV output file for PovRay.
315 // A line with information about a visualization asset contains:
316 //    bodyId, bodyActive, x, y, z, e0, e1, e2, e3, shapeType, [shape Data]
317 // A line with information about a link contains:
318 //    linkType, [linkData]
319 //
320 // NOTE: we do not account for any transform specified for the ChGeometry of
321 // a visual asset (except for cylinders where that is implicit)!
322 // -----------------------------------------------------------------------------
323 enum POVRayShapeType {
324     SPHERE = 0,
325     ELLIPSOID = 1,
326     BOX = 2,
327     CYLINDER = 3,
328     CONVEXHULL = 4,
329     TRIANGLEMESH = 5,
330     BARREL = 6,
331     CAPSULE = 7,
332     CONE = 8,
333     ROUNDEDBOX = 9,
334     ROUNDEDCYL = 10,
335     ROUNDEDCONE = 11,
336     BEZIER = 12
337 };
338 
339 enum POVRayLinkType {
340     REVOLUTE = 0,
341     SPHERICAL = 1,
342     PRISMATIC = 2,
343     UNIVERSAL = 3,
344     DISTANCE = 4,
345     ENGINE = 5,
346     SPRING = 6,
347     TSDA = 7,
348     CYLINDRICAL = 8,
349     REV_SPH = 9
350 };
351 
352 enum POVRayLineType { SEGMENT = 0, COIL = 1 };
353 
WriteVisualizationAssets(ChSystem * system,const std::string & filename,bool body_info,const std::string & delim)354 void WriteVisualizationAssets(ChSystem* system, const std::string& filename, bool body_info, const std::string& delim) {
355     WriteVisualizationAssets(
356         system,                                        //
357         filename,                                      //
358         [](const ChBody& b) -> bool { return true; },  //
359         body_info,                                     //
360         delim);
361 }
362 
WriteVisualizationAssets(ChSystem * system,const std::string & filename,std::function<bool (const ChBody &)> selector,bool body_info,const std::string & delim)363 void WriteVisualizationAssets(ChSystem* system,
364                               const std::string& filename,
365                               std::function<bool(const ChBody&)> selector,
366                               bool body_info,
367                               const std::string& delim) {
368     CSV_writer csv(delim);
369 
370     // If requested, Loop over all bodies and write out their position and
371     // orientation.  Otherwise, body count is left at 0.
372     int b_count = 0;
373 
374     if (body_info) {
375         for (auto body : system->Get_bodylist()) {
376             if (!selector(*body))
377                 continue;
378 
379             const ChVector<>& body_pos = body->GetFrame_REF_to_abs().GetPos();
380             const ChQuaternion<>& body_rot = body->GetFrame_REF_to_abs().GetRot();
381 
382             csv << body->GetIdentifier() << body->IsActive() << body_pos << body_rot << std::endl;
383 
384             b_count++;
385         }
386     }
387 
388     // Loop over all bodies and over all their assets.
389     int a_count = 0;
390     for (auto body : system->Get_bodylist()) {
391         if (!selector(*body))
392             continue;
393 
394         const ChVector<>& body_pos = body->GetFrame_REF_to_abs().GetPos();
395         const ChQuaternion<>& body_rot = body->GetFrame_REF_to_abs().GetRot();
396 
397         ChColor color(0.8f, 0.8f, 0.8f);
398 
399         // First loop over assets -- search for a color asset
400         for (auto asset : body->GetAssets()) {
401             if (auto color_asset = std::dynamic_pointer_cast<ChColorAsset>(asset))
402                 color = color_asset->GetColor();
403         }
404 
405         // Loop over assets once again -- write information for supported types.
406         for (auto asset : body->GetAssets()) {
407             auto visual_asset = std::dynamic_pointer_cast<ChVisualization>(asset);
408             if (!visual_asset)
409                 continue;
410 
411             const Vector& asset_pos = visual_asset->Pos;
412             Quaternion asset_rot = visual_asset->Rot.Get_A_quaternion();
413 
414             Vector pos = body_pos + body_rot.Rotate(asset_pos);
415             Quaternion rot = body_rot % asset_rot;
416 
417             bool supported = true;
418             std::stringstream gss;
419 
420             if (auto sphere = std::dynamic_pointer_cast<ChSphereShape>(visual_asset)) {
421                 gss << SPHERE << delim << sphere->GetSphereGeometry().rad;
422                 a_count++;
423             } else if (auto ellipsoid = std::dynamic_pointer_cast<ChEllipsoidShape>(visual_asset)) {
424                 const Vector& size = ellipsoid->GetEllipsoidGeometry().rad;
425                 gss << ELLIPSOID << delim << size.x() << delim << size.y() << delim << size.z();
426                 a_count++;
427             } else if (auto box = std::dynamic_pointer_cast<ChBoxShape>(visual_asset)) {
428                 const Vector& size = box->GetBoxGeometry().Size;
429                 gss << BOX << delim << size.x() << delim << size.y() << delim << size.z();
430                 a_count++;
431             } else if (auto capsule = std::dynamic_pointer_cast<ChCapsuleShape>(visual_asset)) {
432                 const geometry::ChCapsule& geom = capsule->GetCapsuleGeometry();
433                 gss << CAPSULE << delim << geom.rad << delim << geom.hlen;
434                 a_count++;
435             } else if (auto cylinder = std::dynamic_pointer_cast<ChCylinderShape>(visual_asset)) {
436                 const geometry::ChCylinder& geom = cylinder->GetCylinderGeometry();
437                 gss << CYLINDER << delim << geom.rad << delim << geom.p1.x() << delim << geom.p1.y() << delim
438                     << geom.p1.z() << delim << geom.p2.x() << delim << geom.p2.y() << delim << geom.p2.z();
439                 a_count++;
440             } else if (auto cone = std::dynamic_pointer_cast<ChConeShape>(visual_asset)) {
441                 const geometry::ChCone& geom = cone->GetConeGeometry();
442                 gss << CONE << delim << geom.rad.x() << delim << geom.rad.y();
443                 a_count++;
444             } else if (auto rbox = std::dynamic_pointer_cast<ChRoundedBoxShape>(visual_asset)) {
445                 const geometry::ChRoundedBox& geom = rbox->GetRoundedBoxGeometry();
446                 gss << ROUNDEDBOX << delim << geom.Size.x() << delim << geom.Size.y() << delim << geom.Size.z() << delim
447                     << geom.radsphere;
448                 a_count++;
449             } else if (auto rcyl = std::dynamic_pointer_cast<ChRoundedCylinderShape>(visual_asset)) {
450                 const geometry::ChRoundedCylinder& geom = rcyl->GetRoundedCylinderGeometry();
451                 gss << ROUNDEDCYL << delim << geom.rad << delim << geom.hlen << delim << geom.radsphere;
452                 a_count++;
453             } else if (auto mesh = std::dynamic_pointer_cast<ChTriangleMeshShape>(visual_asset)) {
454                 gss << TRIANGLEMESH << delim << "\"" << mesh->GetName() << "\"";
455                 a_count++;
456             } else if (auto line = std::dynamic_pointer_cast<ChLineShape>(visual_asset)) {
457                 std::shared_ptr<geometry::ChLine> geom = line->GetLineGeometry();
458                 if (auto bezier = std::dynamic_pointer_cast<geometry::ChLineBezier>(geom)) {
459                     gss << BEZIER << delim << "\"" << line->GetName() << "\"";
460                     a_count++;
461                 } else {
462                     supported = false;
463                 }
464             } else {
465                 supported = false;
466             }
467 
468             if (supported) {
469                 csv << body->GetIdentifier() << body->IsActive() << pos << rot << color << gss.str() << std::endl;
470             }
471         }
472     }
473 
474     // Loop over all links.  Write information on selected types of links.
475     int l_count = 0;
476     for (auto ilink : system->Get_linklist()) {
477         if (auto linkR = std::dynamic_pointer_cast<ChLinkLockRevolute>(ilink)) {
478             chrono::ChFrame<> frA_abs = *(linkR->GetMarker1()) >> *(linkR->GetBody1());
479             chrono::ChFrame<> frB_abs = *(linkR->GetMarker2()) >> *(linkR->GetBody2());
480 
481             csv << REVOLUTE << frA_abs.GetPos() << frA_abs.GetA().Get_A_Zaxis() << std::endl;
482             l_count++;
483         } else if (auto linkS = std::dynamic_pointer_cast<ChLinkLockSpherical>(ilink)) {
484             chrono::ChFrame<> frA_abs = *(linkS->GetMarker1()) >> *(linkS->GetBody1());
485             chrono::ChFrame<> frB_abs = *(linkS->GetMarker2()) >> *(linkS->GetBody2());
486 
487             csv << SPHERICAL << frA_abs.GetPos() << std::endl;
488             l_count++;
489         } else if (auto linkP = std::dynamic_pointer_cast<ChLinkLockPrismatic>(ilink)) {
490             chrono::ChFrame<> frA_abs = *(linkP->GetMarker1()) >> *(linkP->GetBody1());
491             chrono::ChFrame<> frB_abs = *(linkP->GetMarker2()) >> *(linkP->GetBody2());
492 
493             csv << PRISMATIC << frA_abs.GetPos() << frA_abs.GetA().Get_A_Zaxis() << std::endl;
494             l_count++;
495         } else if (auto linkC = std::dynamic_pointer_cast<ChLinkLockCylindrical>(ilink)) {
496             chrono::ChFrame<> frA_abs = *(linkC->GetMarker1()) >> *(linkC->GetBody1());
497             chrono::ChFrame<> frB_abs = *(linkC->GetMarker2()) >> *(linkC->GetBody2());
498 
499             csv << CYLINDRICAL << frA_abs.GetPos() << frA_abs.GetA().Get_A_Zaxis() << std::endl;
500             l_count++;
501         } else if (auto linkU = std::dynamic_pointer_cast<ChLinkUniversal>(ilink)) {
502             chrono::ChFrame<> frA_abs = linkU->GetFrame1Abs();
503             chrono::ChFrame<> frB_abs = linkU->GetFrame2Abs();
504 
505             csv << UNIVERSAL << frA_abs.GetPos() << frA_abs.GetA().Get_A_Xaxis() << frB_abs.GetA().Get_A_Yaxis()
506                 << std::endl;
507             l_count++;
508         } else if (auto linkT = std::dynamic_pointer_cast<ChLinkTSDA>(ilink)) {
509             csv << TSDA << linkT->GetPoint1Abs() << linkT->GetPoint2Abs() << std::endl;
510             l_count++;
511         } else if (auto linkD = std::dynamic_pointer_cast<ChLinkDistance>(ilink)) {
512             csv << DISTANCE << linkD->GetEndPoint1Abs() << linkD->GetEndPoint2Abs() << std::endl;
513             l_count++;
514         } else if (auto linkRS = std::dynamic_pointer_cast<ChLinkRevoluteSpherical>(ilink)) {
515             csv << REV_SPH << linkRS->GetPoint1Abs() << linkRS->GetPoint2Abs() << std::endl;
516             l_count++;
517         }
518     }
519 
520     // Loop over links and write assets associated with spring-dampers.
521     int la_count = 0;
522     for (auto ilink : system->Get_linklist()) {
523         auto link = std::dynamic_pointer_cast<ChLinkTSDA>(ilink);
524         if (!link)
525             continue;
526         for (auto asset : link->GetAssets()) {
527             auto visual_asset = std::dynamic_pointer_cast<ChVisualization>(asset);
528             if (!visual_asset)
529                 continue;
530             if (std::dynamic_pointer_cast<ChPointPointSegment>(visual_asset)) {
531                 csv << SEGMENT << link->GetPoint1Abs() << link->GetPoint2Abs() << std::endl;
532                 la_count++;
533             } else if (std::dynamic_pointer_cast<ChPointPointSpring>(visual_asset)) {
534                 csv << COIL << link->GetPoint1Abs() << link->GetPoint2Abs() << std::endl;
535                 la_count++;
536             }
537         }
538     }
539 
540     // Write the output file, including a first line with number of bodies, visual
541     // assets, links, and TSDA assets.
542     std::stringstream header;
543     header << b_count << delim << a_count << delim << l_count << delim << la_count << delim << std::endl;
544 
545     csv.write_to_file(filename, header.str());
546 }
547 
548 // -----------------------------------------------------------------------------
549 // WriteMeshPovray
550 //
551 // Write the triangular mesh from the specified OBJ file as a macro in a PovRay
552 // include file.
553 // -----------------------------------------------------------------------------
WriteMeshPovray(geometry::ChTriangleMeshConnected & trimesh,const std::string & mesh_name,const std::string & out_dir,const ChColor & col,const ChVector<> & pos,const ChQuaternion<> & rot,bool smoothed)554 void WriteMeshPovray(geometry::ChTriangleMeshConnected& trimesh,
555                      const std::string& mesh_name,
556                      const std::string& out_dir,
557                      const ChColor& col,
558                      const ChVector<>& pos,
559                      const ChQuaternion<>& rot,
560                      bool smoothed) {
561     // Transform vertices.
562     for (unsigned int i = 0; i < trimesh.m_vertices.size(); i++)
563         trimesh.m_vertices[i] = pos + rot.Rotate(trimesh.m_vertices[i]);
564 
565     // Transform normals
566     if (smoothed) {
567         for (unsigned int i = 0; i < trimesh.m_normals.size(); i++)
568             trimesh.m_normals[i] = rot.Rotate(trimesh.m_normals[i]);
569     }
570 
571     // Open output file.
572     std::string pov_filename = out_dir + "/" + mesh_name + ".inc";
573     std::ofstream ofile(pov_filename.c_str());
574 
575     ofile << "#declare " << mesh_name << "_mesh = mesh2 {" << std::endl;
576 
577     // Write vertices.
578     ofile << "vertex_vectors {" << std::endl;
579     ofile << trimesh.m_vertices.size();
580     for (unsigned int i = 0; i < trimesh.m_vertices.size(); i++) {
581         ChVector<> v = trimesh.m_vertices[i];
582         ofile << ",\n<" << v.x() << ", " << v.z() << ", " << v.y() << ">";
583     }
584     ofile << "\n}" << std::endl;
585 
586     // Write normals.
587     if (smoothed) {
588         ofile << "normal_vectors {" << std::endl;
589         ofile << trimesh.m_normals.size();
590         for (unsigned int i = 0; i < trimesh.m_normals.size(); i++) {
591             ChVector<> n = trimesh.m_normals[i];
592             ofile << ",\n<" << n.x() << ", " << n.z() << ", " << n.y() << ">";
593         }
594         ofile << "\n}" << std::endl;
595     }
596 
597     // Write face connectivity.
598     ofile << "face_indices {" << std::endl;
599     ofile << trimesh.m_face_v_indices.size();
600     for (int i = 0; i < trimesh.m_face_v_indices.size(); i++) {
601         ChVector<int> face = trimesh.m_face_v_indices[i];
602         ofile << ",\n<" << face.x() << ", " << face.y() << ", " << face.z() << ">";
603     }
604     ofile << "\n}" << std::endl;
605 
606     ofile << "\n}" << std::endl;
607 
608     // Write the object
609     ofile << "#declare " << mesh_name << " = object {" << std::endl;
610 
611     ofile << "   " << mesh_name << "_mesh" << std::endl;
612     ofile << "   texture {" << std::endl;
613     ofile << "      pigment {color rgb<" << col.R << ", " << col.G << ", " << col.B << ">}" << std::endl;
614     ofile << "      finish  {phong 0.2  diffuse 0.6}" << std::endl;
615     ofile << "    }" << std::endl;
616     ofile << "}" << std::endl;
617 
618     // Close the output file.
619     ofile.close();
620 }
621 
WriteMeshPovray(const std::string & obj_filename,const std::string & mesh_name,const std::string & out_dir,const ChColor & col,const ChVector<> & pos,const ChQuaternion<> & rot)622 bool WriteMeshPovray(const std::string& obj_filename,
623                      const std::string& mesh_name,
624                      const std::string& out_dir,
625                      const ChColor& col,
626                      const ChVector<>& pos,
627                      const ChQuaternion<>& rot) {
628     // Read trimesh from OBJ file
629     geometry::ChTriangleMeshConnected trimesh;
630     if (!trimesh.LoadWavefrontMesh(obj_filename, false, false))
631         return false;
632 
633     // Generate output
634     WriteMeshPovray(trimesh, mesh_name, out_dir, col, pos, rot);
635 
636     return true;
637 }
638 
639 // -----------------------------------------------------------------------------
640 // WriteCurvePovray
641 //
642 // Write the specified Bezier curve as a macro in a PovRay include file.
643 // -----------------------------------------------------------------------------
WriteCurvePovray(const ChBezierCurve & curve,const std::string & curve_name,const std::string & out_dir,double radius,const ChColor & col)644 void WriteCurvePovray(const ChBezierCurve& curve,
645                       const std::string& curve_name,
646                       const std::string& out_dir,
647                       double radius,
648                       const ChColor& col) {
649     int nP = 20;
650     double dt = 1.0 / nP;
651     size_t nS = curve.getNumPoints() - 1;
652 
653     // Open output file.
654     std::string pov_filename = out_dir + "/" + curve_name + ".inc";
655     std::ofstream ofile(pov_filename.c_str());
656 
657     ofile << "#declare " << curve_name << " = object {" << std::endl;
658     ofile << "  sphere_sweep {" << std::endl;
659     ofile << "    linear_spline " << nP * nS + 1 << "," << std::endl;
660 
661     ChVector<> v = curve.eval(0, 0.0);
662     ofile << "        <" << v.x() << ", " << v.z() << ", " << v.x() << "> ," << radius << std::endl;
663 
664     for (int iS = 0; iS < nS; iS++) {
665         for (int iP = 1; iP <= nP; iP++) {
666             v = curve.eval(iS, iP * dt);
667             ofile << "        <" << v.x() << ", " << v.z() << ", " << v.y() << "> ," << radius << std::endl;
668         }
669     }
670 
671     ofile << "    texture {" << std::endl;
672     ofile << "      pigment {color rgb<" << col.R << ", " << col.G << ", " << col.B << ">}" << std::endl;
673     ofile << "      finish  {phong 0.2  diffuse 0.6}" << std::endl;
674     ofile << "     }" << std::endl;
675 
676     ofile << "  }" << std::endl;
677     ofile << "}" << std::endl;
678 
679     // Close the output file.
680     ofile.close();
681 }
682 
683 }  // namespace utils
684 }  // namespace chrono
685