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