TexGen
BasicVolumes.cpp
Go to the documentation of this file.
1/*=============================================================================
2TexGen: Geometric textile modeller.
3Copyright (C) 2006 Martin Sherburn
4
5This program is free software; you can redistribute it and/or
6modify it under the terms of the GNU General Public License
7as published by the Free Software Foundation; either version 2
8of the License, or (at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18=============================================================================*/
19
20#include "PrecompiledHeaders.h"
21#include "BasicVolumes.h"
22#include "TexGen.h"
23//#include "Yarn.h"
24extern "C"
25{
26#include "../Triangle/triangle.h"
27#include "../Triangle/triangle_api.h"
28}
29
30using namespace TexGen;
31CBasicVolumes::CBasicVolumes(void)
32: m_dTolerance(1e-6)
33, m_pTextile(NULL)
34, m_dSeed(1.0)
35, m_bCreatePeriodic(false)
36, m_bDebug(false)
37{
38}
39
41{
42}
43
44bool CBasicVolumes::CreateBasicVolumes(string TextileName)
45{
46 CTextile* pTextile = TEXGEN.GetTextile(TextileName);
47 if (pTextile)
48 return CreateBasicVolumes(*pTextile);
49 else
50 return false;
51}
52
54{
55 m_pTextile = &Textile;
58 m_YarnMeshes.clear();
59 m_ProjectedRegions.clear();
60 m_TriangleRegions.clear();
61
62 const CDomain* pDomain = Textile.GetDomain();
63 if (!pDomain)
64 return false;
65 vector<CYarn> &Yarns = Textile.GetYarns();
66 m_DomainMesh = pDomain->GetMesh();
67 m_YarnMeshes.clear();
68 m_YarnMeshes.resize(Yarns.size());
69 vector<CYarn>::iterator itYarn;
70 vector<CMesh>::iterator itMesh;
71 TGLOG("Projecting volume outlines onto the x/y plane");
72 for (itYarn = Yarns.begin(), itMesh = m_YarnMeshes.begin(); itYarn != Yarns.end() && itMesh != m_YarnMeshes.end(); ++itYarn, ++itMesh)
73 {
74 itYarn->AddSurfaceToMesh(*itMesh, *pDomain);
75 itMesh->ConvertQuadstoTriangles();
77 }
80 //m_bDebug = true;
81 if (m_bDebug)
82 m_ProjectedMesh.SaveToVTK("EdgesProject");
83
84 // This should have a higher tolerance than everything else
86
87 // Note the order of this sequence is very important, don't mess with this
88 // without thinking hard about it
89 TGLOG("Removing degenerate segments");
91 TGLOG("Splitting segments at the nodes");
93 TGLOG("Removing duplicate segments");
95 TGLOG("Merging straight segments");
97 // Need to run this again because the merge may have
98 // Unsplit some of the lines that we wanted to keep split
99 TGLOG("Splitting segments at the nodes");
101 TGLOG("Splitting segments by other segments");
103
104// m_ProjectedMesh.SaveToVTK("Projected");
105
106 // Check that the projected mesh is valid
107 if (!ValidProjectedMesh())
108 return false;
109
110 TGLOG("Identifying projected regions");
111 // Determine the different areas
113 return false;
114
116 {
117 TGLOG("Manually seeding boundary segments");
118 // Seed the outer boundary
119 if (!SeedOuterBoundary())
120 return false;
121 }
122
123 if (!RemoveOuterBoundary())
124 return false;
125
126 if (m_bDebug)
127 SaveProjectedContoursToVTK("Contours");
128
129 TGLOG("Identifying region centers");
130 // Get the centers of each area
132 return false;
133
134 TGLOG("Identifying list of yarns contained within each region");
135 // Figure out which yarns are in which areas
137
138 TGLOG("Meshing projected regions in 2D");
139 // Mesh the areas
140 if (!MeshProjectedAreas())
141 return false;
142
143 if (m_bDebug)
144 SaveProjectedAreasToVTK("Projected");
145 return true;
146}
147
149{
150 CMesh Mesh;
151// Mesh.m_Nodes = m_ProjectedMesh.m_Nodes;
152
153 CMeshData<unsigned char> AreaIndex("AreaIndex", CMeshDataBase::ELEMENT);
156
157 ostringstream ProjectedAreaIndexData;
158 vector<PROJECTED_REGION>::iterator itRegion;
159 int i = 0, j, k;
160 int iSegment, i1, i2;
161 list<int>::iterator itIndex;
162 list<int> &Indices = m_ProjectedMesh.GetIndices(CMesh::LINE);
163 for (itRegion = m_ProjectedRegions.begin(), j=0; itRegion != m_ProjectedRegions.end(); ++itRegion, ++j)
164 {
165 for (k=0; k<(int)itRegion->SegmentIndices.size(); ++k)
166 {
167 iSegment = itRegion->SegmentIndices[k];
168 itIndex = Indices.begin();
169 advance(itIndex, iSegment*2);
170 i1 = *(itIndex++);
171 i2 = *(itIndex++);
172 Mesh.AddNode(m_ProjectedMesh.GetNode(i1) + XYZ(0,0,0.01*j));
173 Mesh.AddNode(m_ProjectedMesh.GetNode(i2) + XYZ(0,0,0.01*j));
174 Mesh.GetIndices(CMesh::LINE).push_back(i++);
175 Mesh.GetIndices(CMesh::LINE).push_back(i++);
176 AreaIndex.m_Data.push_back(j);
177 Area.m_Data.push_back(itRegion->dArea);
178 NumYarns.m_Data.push_back(itRegion->YarnIndices.size());
179 }
180 }
181
182 vector<CMeshDataBase*> MeshData;
183
184 MeshData.push_back(&AreaIndex);
185 MeshData.push_back(&Area);
186 MeshData.push_back(&NumYarns);
187
188 Mesh.SaveToVTK(Filename, &MeshData);
189}
190
192{
193 vector<TiXmlElement> AdditionalData;
194
195 CMeshData<unsigned char> AreaIndex("AreaIndex", CMeshDataBase::ELEMENT);
198
199 ostringstream ProjectedAreaIndexData;
200 vector<int>::iterator itRegion;
201 for (itRegion = m_TriangleRegions.begin(); itRegion != m_TriangleRegions.end(); ++itRegion)
202 {
203 AreaIndex.m_Data.push_back(*itRegion);
204 Area.m_Data.push_back(m_ProjectedRegions[*itRegion].dArea);
205 NumYarns.m_Data.push_back(m_ProjectedRegions[*itRegion].YarnIndices.size());
206 }
207
208 vector<CMeshDataBase*> MeshData;
209
210 MeshData.push_back(&AreaIndex);
211 MeshData.push_back(&Area);
212 MeshData.push_back(&NumYarns);
213
214 m_ProjectedMesh.SaveToVTK(Filename, &MeshData);
215}
216
217bool CBasicVolumes::GetCommonEdgeIndices(int Indices1[3], int Indices2[3], int Common[2])
218{
219 int i, j, k=0;
220 for (i=0; i<3; ++i)
221 {
222 for (j=0; j<3; ++j)
223 {
224 if (Indices1[i] == Indices2[j])
225 {
226 Common[k++] = Indices1[i]; // == Indices2[j]
227 if (k == 2)
228 return true;
229 }
230 }
231 }
232 return false;
233}
234
236{
237 int iMergeCount = 0;
238 list<int>::iterator itIndex, itStart;
239 list<int>::iterator itCompareIndex, itCompareStart;
240 list<int> &Indices = Mesh.GetIndices(CMesh::LINE);
241 int a, b;
242 int i[2];
243 int j[2];
244 int iCommon;
245 int iEnd1, iEnd2;
246 XYZ P, P1, P2, V1, V2;
247 for (itIndex = Indices.begin(); itIndex != Indices.end(); )
248 {
249 itStart = itIndex;
250 i[0] = *(itIndex++);
251 i[1] = *(itIndex++);
252 for (itCompareIndex = itIndex; itCompareIndex != Indices.end(); )
253 {
254 itCompareStart = itCompareIndex;
255 j[0] = *(itCompareIndex++);
256 j[1] = *(itCompareIndex++);
257 iCommon = -1;
258 for (a=0; a<2; ++a)
259 {
260 for (b=0; b<2; ++b)
261 {
262 if (i[a] == j[b])
263 {
264 iCommon = i[a]; // == j[b]
265 iEnd1 = i[(a+1)%2];
266 iEnd2 = j[(b+1)%2];
267 break;
268 }
269 }
270 }
271 if (iCommon != -1)
272 {
273 P = Mesh.GetNode(iCommon);
274 P1 = Mesh.GetNode(iEnd1);
275 P2 = Mesh.GetNode(iEnd2);
276 V1 = P1-P;
277 V2 = P2-P;
278 Normalise(V1);
279 Normalise(V2);
280 if (DotProduct(V1, V2)+1 <= m_dTolerance)
281 {
282 // Ok merge them
283 Indices.erase(itStart, itIndex);
284 if (itIndex == itCompareStart)
285 itIndex = Indices.erase(itCompareStart, itCompareIndex);
286 else
287 Indices.erase(itCompareStart, itCompareIndex);
288
289 Indices.push_back(iEnd1);
290 Indices.push_back(iEnd2);
291
292 ++iMergeCount;
293 break;
294 }
295 }
296 }
297 }
298
300
301 return iMergeCount;
302}
303
305{
306 list<int>::iterator itIndex, itStart;
307 list<int>::iterator itCompareIndex;
308 list<int> &Indices = Mesh.GetIndices(CMesh::LINE);
309 int i1, i2;
310 int j1, j2;
311 int iDuplicateCount = 0;
312 for (itIndex = Indices.begin(); itIndex != Indices.end(); )
313 {
314 itStart = itIndex;
315 i1 = *(itIndex++);
316 i2 = *(itIndex++);
317 for (itCompareIndex = itIndex; itCompareIndex != Indices.end(); )
318 {
319 j1 = *(itCompareIndex++);
320 j2 = *(itCompareIndex++);
321 if ((i1 == j1 && i2 == j2) || (i1 == j2 && i2 == j1))
322 {
323 Indices.erase(itStart, itIndex);
324 ++iDuplicateCount;
325 break;
326 }
327 }
328 }
329 return iDuplicateCount;
330}
331
333{
334 list<int>::iterator itIndex, itStart;
335 list<int> &Indices = Mesh.GetIndices(CMesh::LINE);
336 int i1, i2;
337 int iDegenerateCount = 0;
338 for (itIndex = Indices.begin(); itIndex != Indices.end(); )
339 {
340 itStart = itIndex;
341 i1 = *(itIndex++);
342 i2 = *(itIndex++);
343 if (i1 == i2)
344 {
345 Indices.erase(itStart, itIndex);
346 ++iDegenerateCount;
347 }
348 }
349 return iDegenerateCount;
350}
351
353{
354 int iSplitCount = 0;
355 list<int>::iterator itIndex, itStart;
356 vector<XYZ>::const_iterator itNode;
357 list<int> &Indices = Mesh.GetIndices(CMesh::LINE);
358 int j, i1, i2;
359 XYZ P, L1, L2;
360 double dU, dUMin;
361 double dDistanceSquared, dToleranceSquared = m_dTolerance*m_dTolerance;
362 double dLengthSquared;
363 for (itIndex = Indices.begin(); itIndex != Indices.end(); )
364 {
365 itStart = itIndex;
366 i1 = *(itIndex++);
367 i2 = *(itIndex++);
368 L1 = Mesh.GetNode(i1);
369 L2 = Mesh.GetNode(i2);
370 dLengthSquared = GetLengthSquared(L1, L2);
371 for (itNode = Mesh.NodesBegin(), j=0; itNode != Mesh.NodesEnd(); ++itNode, ++j)
372 {
373 // Skip this node if it is one of the segment's ends
374 if (j == i1 || j == i2)
375 continue;
376 P = ShortestDistPointLine(*itNode, L1, L2, dU);
377 // Check dU is within the range 0 to 1 and also that the distances
378 // between the points P - L1 and P - L2 are greater than the tolerance
379 // (this is done using squared lengths for performance reasons)
380 // EDIT: This may be unnecessary, since nodes should already be merged
381 // at this point
382 dUMin = min(dU, 1-dU);
383 if (dU > 0 && dU < 1 && dUMin*dUMin*dLengthSquared > dToleranceSquared)
384 {
385 // Check the point is close to the line
386 dDistanceSquared = GetLengthSquared(P, *itNode);
387 if (dDistanceSquared <= dToleranceSquared)
388 {
389 // OK! Let's split this sucker...
390 // Remove the current segment
391 Indices.erase(itStart, itIndex);
392
393 // Add the new split segments
394 Indices.push_back(i1);
395 // If the line segment was the last in the list, some trickery is needed to
396 // ensure the newly inserted line is checked for splits
397 if (itIndex == Indices.end())
398 --itIndex;
399 Indices.push_back(j);
400
401 Indices.push_back(j);
402 Indices.push_back(i2);
403
404 ++iSplitCount;
405
406 break;
407 }
408 }
409 }
410 }
411 return iSplitCount;
412}
413
415{
416 int iSplitCount = 0;
417
418 list<int>::iterator itIndex, itStart;
419 list<int>::iterator itCompareIndex, itCompareStart;
420 list<int> &Indices = Mesh.GetIndices(CMesh::LINE);
421 int i1, i2;
422 int j1, j2;
423 int iNewNodeIndex;
424 XYZ P1, P2, P3, P4, P;
425 double dU1, dU2;
426 double dUMin1, dUMin2;
427 double dClosestDistSquared;
428 double dToleranceSquared = m_dTolerance*m_dTolerance;
429 for (itIndex = Indices.begin(); itIndex != Indices.end(); )
430 {
431 itStart = itIndex;
432 i1 = *(itIndex++);
433 i2 = *(itIndex++);
434 P1 = Mesh.GetNode(i1);
435 P2 = Mesh.GetNode(i2);
436 for (itCompareIndex = itIndex; itCompareIndex != Indices.end(); )
437 {
438 itCompareStart = itCompareIndex;
439 j1 = *(itCompareIndex++);
440 j2 = *(itCompareIndex++);
441 if (i1 != j1 && i1 != j2 && i2 != j1 && i2 != j2)
442 {
443 P3 = Mesh.GetNode(j1);
444 P4 = Mesh.GetNode(j2);
445 if (LineLineIntersect2D(XY(P1.x, P1.y), XY(P2.x, P2.y), XY(P3.x, P3.y), XY(P4.x, P4.y), dU1, dU2))
446 {
447 dUMin1 = min(dU1, 1-dU1);
448 dUMin2 = min(dU2, 1-dU2);
449 dClosestDistSquared = min(GetLengthSquared(P1, P2)*dUMin1*dUMin1,
450 GetLengthSquared(P1, P2)*dUMin2*dUMin2);
451 if (dClosestDistSquared > dToleranceSquared)
452 {
453 P = P1 + (P2-P1)*dU1;
454 Indices.erase(itStart, itIndex);
455 if (itIndex == itCompareStart)
456 itIndex = Indices.erase(itCompareStart, itCompareIndex);
457 else
458 Indices.erase(itCompareStart, itCompareIndex);
459
460 iNewNodeIndex = Mesh.AddNode(P); //Mesh.m_Nodes.size()-1;
461
462 Indices.push_back(i1);
463 Indices.push_back(iNewNodeIndex);
464
465 Indices.push_back(iNewNodeIndex);
466 Indices.push_back(i2);
467
468 Indices.push_back(j1);
469 Indices.push_back(iNewNodeIndex);
470
471 Indices.push_back(iNewNodeIndex);
472 Indices.push_back(j2);
473
474 ++iSplitCount;
475 break;
476 }
477 }
478 }
479 }
480 }
481
482 return iSplitCount;
483}
484
486{
487 // For the mesh to be valid, each line segment should
488 // at least be connected at both ends
489 list<int>::iterator itIndex;
490 list<int>::iterator itCompareIndex;
491 list<int> &Indices = m_ProjectedMesh.GetIndices(CMesh::LINE);
492 int i1, i2;
493 int j1, j2;
494 bool bEnd1, bEnd2;
495 for (itIndex = Indices.begin(); itIndex != Indices.end(); )
496 {
497 bEnd1 = false;
498 bEnd2 = false;
499 i1 = *(itIndex++);
500 i2 = *(itIndex++);
501 for (itCompareIndex = Indices.begin(); itCompareIndex != Indices.end(); )
502 {
503 j1 = *(itCompareIndex++);
504 j2 = *(itCompareIndex++);
505 if (i1 == j1 || i1 == j2)
506 bEnd1 = true;
507 if (i2 == j1 || i2 == j2)
508 bEnd2 = true;
509 }
510 if (!bEnd1 || !bEnd2)
511 return false;
512 }
513
514 return true;
515}
516
518{
519// int iIterationCount = 0;
520 // Start at a random segment, then follow it round either from origin to end
521 // or end to origin (depending on i==0 or i==1)
522 // Follow segments always following the one with the greatest clockwise angle
523 vector<int> LineState;
524 LineState.resize(m_ProjectedMesh.GetIndices(CMesh::LINE).size()/2, 0);
525 list<int>::iterator itIndex, itStartIndex;
526 list<int>::iterator itFollowIndex;
527 list<int>::iterator itCompareIndex, itStartCompareIndex;
528 list<int>::iterator itBestFind;
529 list<int> &Indices = m_ProjectedMesh.GetIndices(CMesh::LINE);
530 int iDir, iCurrentDir;
531 int i1, i2;
532 int j1, j2;
533 int iBestj1, iBestj2;
534 int iLineCount, iInitialLineCount;
535 int iEndIndex;
536 int iBestFindDir, iBestLineCount;
537 double dMaxAngle, dAngle;
538 XYZ V1, V2;
539 m_ProjectedRegions.clear();
540 for (iDir=FORWARD; iDir<=REVERSE; ++iDir)
541 {
542 for (itIndex = Indices.begin(), iInitialLineCount = 0; itIndex != Indices.end(); ++iInitialLineCount)
543 {
544 itFollowIndex = itIndex;
545 itStartIndex = itIndex;
546 iCurrentDir = iDir;
547 i1 = *(itIndex++);
548 i2 = *(itIndex++);
549 if (LineState[iInitialLineCount] & iDir)
550 continue;
552 do
553 {
554 if (iCurrentDir == FORWARD)
555 iEndIndex = i2;
556 else
557 iEndIndex = i1;
559 if (iCurrentDir == REVERSE)
560 V1 *= -1;
561 itBestFind = Indices.end();
562 for (itCompareIndex = Indices.begin(), iLineCount=0; itCompareIndex != Indices.end(); ++iLineCount)
563 {
564 itStartCompareIndex = itCompareIndex;
565 j1 = *(itCompareIndex++);
566 j2 = *(itCompareIndex++);
567 if (itStartCompareIndex == itFollowIndex)
568 continue;
569 if (iEndIndex == j1)
570 {
571 if (LineState[iLineCount] & FORWARD)
572 continue;
574 // Normalisation not needed
575 dAngle = atan2(CrossProduct(V1, V2).z, DotProduct(V1, V2));
576 if (itBestFind == Indices.end() || dAngle > dMaxAngle)
577 {
578 dMaxAngle = dAngle;
579 iBestFindDir = FORWARD;
580 itBestFind = itStartCompareIndex;
581 iBestLineCount = iLineCount;
582 iBestj1 = j1;
583 iBestj2 = j2;
584 }
585 }
586 else if (iEndIndex == j2)
587 {
588 if (LineState[iLineCount] & REVERSE)
589 continue;
591 // Normalisation not needed
592 dAngle = atan2(CrossProduct(V1, V2).z, DotProduct(V1, V2));
593 if (itBestFind == Indices.end() || dAngle > dMaxAngle)
594 {
595 dMaxAngle = dAngle;
596 iBestFindDir = REVERSE;
597 itBestFind = itStartCompareIndex;
598 iBestLineCount = iLineCount;
599 iBestj1 = j1;
600 iBestj2 = j2;
601 }
602 }
603 }
604 if (itBestFind == Indices.end())
605 return false;
606 LineState[iBestLineCount] |= iBestFindDir;
607 iCurrentDir = iBestFindDir;
608 itFollowIndex = itBestFind;
609 i1 = iBestj1;
610 i2 = iBestj2;
611 m_ProjectedRegions.back().SegmentIndices.push_back(iBestLineCount);
612 if (iBestFindDir == FORWARD)
613 m_ProjectedRegions.back().ContourNodes.push_back(iBestj2);
614 else
615 m_ProjectedRegions.back().ContourNodes.push_back(iBestj1);
616 } while(itFollowIndex != itStartIndex);
618 }
619 }
620
621 // The total area of all the regions should be 0, the large region encapsulating all the rest should
622 // be negative and equal in magnitude to the sum of all the interior regions. If not there may have been
623 // some problem creating the regions.
624 double dAreaSum = 0;
625 vector<PROJECTED_REGION>::iterator itRegion;
626 for (itRegion = m_ProjectedRegions.begin(); itRegion != m_ProjectedRegions.end(); ++itRegion)
627 {
628 dAreaSum += itRegion->dArea;
629 }
630
631 if (abs(dAreaSum) > 1e-3)
632 {
633 TGERROR("Error creating projected areas, the total area is non-zero: " << dAreaSum);
634 return false;
635 }
636
637 return true;
638}
639
641{
642 if (m_ProjectedRegions.empty())
643 return false;
644 // The outer region has its area inverted, so it will be the minimum of all areas
645 vector<PROJECTED_REGION>::iterator itOuterRegion =
646 min_element(m_ProjectedRegions.begin(), m_ProjectedRegions.end(), ProjectedRegionArea());
647
648 // The area should be negative
649 assert(itOuterRegion->dArea <= 0);
650
651 list<int> &Indices = m_ProjectedMesh.GetIndices(CMesh::LINE);
652 XYZ P;
653 double u;
654 int i, i1, i2, iPrevIndex, iNewIndex;
655 list<int>::iterator itIndex, itStart;
656 vector<int>::iterator itSegment;
657 double dLength;
658 // Apply a small distorition to the seed to prevent the likelyhood of the segment length being equal
659 // or very close to the seed length. It is important because opposite faces should be seeded in exactly
660 // the same manner.
661 const double dAdjustedSeed = m_dSeed + 1.2345e-3;
662 set<int> DeleteSegments;
663 // Split any segments up that are greater than the seed length
664 for (itSegment=itOuterRegion->SegmentIndices.begin(); itSegment!=itOuterRegion->SegmentIndices.end(); ++itSegment)
665 {
666 itIndex = Indices.begin();
667 advance(itIndex, (*itSegment)*2);
668 i1 = *(itIndex++);
669 i2 = *(itIndex++);
670 const XYZ &N1 = m_ProjectedMesh.GetNode(i1);
671 const XYZ &N2 = m_ProjectedMesh.GetNode(i2);
672 dLength = GetLength(N1, N2);
673 if (dLength > dAdjustedSeed)
674 {
675 int iNumSplits = int(floor(dLength / (dAdjustedSeed)));
676
677 // Add the segment to the list of segements to be deleted
678 // they will be deleted in the next loop so as not to invalidate
679 // segment indices for this loop
680 DeleteSegments.insert(*itSegment);
681
682 // Create some new segments
683 iPrevIndex = i1;
684 for (i=0; i<iNumSplits; ++i)
685 {
686 u = double(i+1)/double(iNumSplits+1);
687 P = N1 + (N2-N1)*u;
688 iNewIndex = m_ProjectedMesh.AddNode(P);
689 Indices.push_back(iPrevIndex);
690 Indices.push_back(iNewIndex);
691 iPrevIndex = iNewIndex;
692 }
693 Indices.push_back(iPrevIndex);
694 Indices.push_back(i2);
695 }
696 }
697
698 // Erase the segments that were split
699 for (itIndex=Indices.begin(), i=0; itIndex!=Indices.end(); ++i)
700 {
701 if (DeleteSegments.count(i))
702 {
703 // Needs erasing
704 itStart = itIndex;
705 ++itIndex;
706 ++itIndex;
707 itIndex = Indices.erase(itStart, itIndex);
708 }
709 else
710 {
711 // Increment indices
712 ++itIndex;
713 ++itIndex;
714 }
715 }
716
717 // The projected areas will have been invalidated by the splitting so calculate them again
718 return CreateProjectedAreas();
719}
720
722{
723 if (m_ProjectedRegions.empty())
724 return false;
725 // The outer region has its area inverted, so it will be the minimum of all areas
726 vector<PROJECTED_REGION>::iterator itOuterRegion =
727 min_element(m_ProjectedRegions.begin(), m_ProjectedRegions.end(), ProjectedRegionArea());
728
729 m_ProjectedRegions.erase(itOuterRegion);
730 return true;
731}
732
734{
735 vector<int>::const_iterator itIndex;
736 list<int> &Indices = m_ProjectedMesh.GetIndices(CMesh::LINE);
737 vector<XYZ> Nodes;
738 for (itIndex = Region.ContourNodes.begin(); itIndex != Region.ContourNodes.end(); ++itIndex)
739 {
740 Nodes.push_back(m_ProjectedMesh.GetNode(*itIndex));
741 }
742 return GetArea(&Nodes.front(), Nodes.size());
743}
744
746{
747 vector<PROJECTED_REGION>::iterator itRegion;
748 vector<int>::iterator itSegment;
749 list<int>::iterator itIndex;
750 list<int> &Indices = m_ProjectedMesh.GetIndices(CMesh::LINE);
751 int i, i1, i2;
752 XYZ Min, Max, P1, P2;
753 for (itRegion = m_ProjectedRegions.begin(), i=0; itRegion != m_ProjectedRegions.end(); ++itRegion, ++i)
754 {
755 XYZ Point;
756 for (itSegment=itRegion->SegmentIndices.begin(); itSegment!=itRegion->SegmentIndices.end(); ++itSegment)
757 {
758 itIndex = Indices.begin();
759 advance(itIndex, (*itSegment)*2);
760 i1 = *(itIndex++);
761 i2 = *(itIndex++);
762 P1 = m_ProjectedMesh.GetNode(i1);
763 P2 = m_ProjectedMesh.GetNode(i2);
764 if (itSegment == itRegion->SegmentIndices.begin())
765 {
766 Min = Max = P1;
767 }
768 Min = ::Min(Min, P1);
769 Min = ::Min(Min, P2);
770 Max = ::Max(Max, P1);
771 Max = ::Max(Max, P2);
772 Point += P1 + P2;
773 }
774 Point /= 2*itRegion->SegmentIndices.size();
775 // The center may not be within the region, so if its not just generate
776 // random points within the bounding box until one is found
777 while (!PointInsideRegion(Point, i))
778 {
779 Point.x = RandomNumber(Min.x, Max.x);
780 Point.y = RandomNumber(Min.y, Max.y);
781 }
782 itRegion->Center = Point;
783 }
784 return true;
785}
786
787// Algorithm borrowed from http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/
789{
790 PROJECTED_REGION &Region = m_ProjectedRegions[iRegion];
791 vector<int> &ContourNodes = Region.ContourNodes;
792 vector<int>::iterator itNodeIndex;
793
794 int counter = 0;
795 int i, N = (int)ContourNodes.size();
796 double xinters;
797 XYZ p1, p2;
798 p1 = m_ProjectedMesh.GetNode(ContourNodes[0]);
799 for (i=1;i<=N;i++)
800 {
801 p2 = m_ProjectedMesh.GetNode(ContourNodes[i % N]);
802 if (p.y > min(p1.y,p2.y))
803 {
804 if (p.y <= max(p1.y,p2.y))
805 {
806 if (p.x <= max(p1.x,p2.x))
807 {
808 if (p1.y != p2.y)
809 {
810 xinters = (p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x;
811 if (p1.x == p2.x || p.x <= xinters)
812 counter++;
813 }
814 }
815 }
816 }
817 p1 = p2;
818 }
819
820 if (counter % 2 == 1)
821 return true;
822 else
823 return false;
824}
825
827{
828 int i, iNumRegions = (int)m_ProjectedRegions.size();
829 int j, iNumYarns = (int)m_YarnMeshes.size();
830 XYZ Point;
831 vector<pair<double, int> > YarnHeightIndex;
832 for (i=0; i<iNumRegions; ++i)
833 {
834 Point = m_ProjectedRegions[i].Center;
835 YarnHeightIndex.clear();
836 for (j=0; j<iNumYarns; ++j)
837 {
838 double dMinZ = 0, dMaxZ = 0;
839 if (GetMeshVerticalBounds(m_YarnMeshes[j], Point, dMinZ, dMaxZ))
840 {
841 YarnHeightIndex.push_back(make_pair((dMinZ+dMaxZ)/2, j));
842 }
843 }
844 sort(YarnHeightIndex.begin(), YarnHeightIndex.end(), LessPairDoubleInt());
845 for (j=0; j<(int)YarnHeightIndex.size(); ++j)
846 {
847 m_ProjectedRegions[i].YarnIndices.push_back(YarnHeightIndex[j].second);
848 }
849 }
850}
851
853{
854 stringstream Switches;
855
856 double dMaxArea = 0.5*m_dSeed*m_dSeed;
857 double dMinAngle = 20;
858
859 // These are the switches to be used with triangle
860 // http://www-2.cs.cmu.edu/~quake/triangle.switch.html
861 // -p Triangulates a Planar Straight Line Graph (.poly file).
862 // -z Numbers all items starting from zero (rather than one).
863 // -n Outputs (to a .neigh file) a list of triangles neighboring each triangle.
864 // -P Suppresses the output .poly file.
865 // -B Suppresses boundary markers in the output .node, .poly, and .edge output files.
866 // -A Assigns a regional attribute to each triangle that identifies what segment-bounded region it belongs to.
867 // -Q Quiet: Suppresses all explanation of what Triangle is doing, unless an error occurs.
868 // -q Quality mesh generation with no angles smaller than 20 degrees. An alternate minimum angle may be specified after the `q'.
869 // -a Imposes a maximum triangle area constraint. A fixed area constraint (that applies to every triangle) may be specified after the `a', or varying area constraints may be read from a .poly file or .area file.
870 // -Y Prohibits the insertion of Steiner points on the mesh boundary
871
872#ifndef _DEBUG
873 Switches << "Q";
874#endif
875 // Triangle has trouble parsing values given in scientific format so use fixed format with a
876 // redicilously high precision to get around the problem
877 Switches << "pzAPBq" << setiosflags(ios::fixed) << setprecision(20) << dMinAngle << "a" << dMaxArea;
878
880 Switches << "Y";
881
882 triangleio TriangleInput, TriangleOutput;
883
884 context *ctx;
885 ctx = triangle_context_create();
886
887 triangle_context_options(ctx, (char*)Switches.str().c_str());
888
889 memset(&TriangleInput, 0, sizeof(TriangleInput));
890 memset(&TriangleOutput, 0, sizeof(TriangleOutput));
891
892 // Input Nodes
893 TriangleInput.pointlist = new REAL [m_ProjectedMesh.GetNumNodes()*2];
894 TriangleInput.numberofpoints = (int)m_ProjectedMesh.GetNumNodes();
895
896 int i;
897 for (i=0; i<TriangleInput.numberofpoints; ++i)
898 {
899 TriangleInput.pointlist[i*2] = m_ProjectedMesh.GetNode(i).x;
900 TriangleInput.pointlist[i*2+1] = m_ProjectedMesh.GetNode(i).y;
901 }
902
903 // Input Segments
904 list<int>::iterator itIndex;
905 list<int> &Indices = m_ProjectedMesh.GetIndices(CMesh::LINE);
906
907 TriangleInput.segmentlist = new int [Indices.size()];
908 TriangleInput.numberofsegments = (int)Indices.size()/2;
909
910 for (itIndex = Indices.begin(), i = 0; itIndex != Indices.end(); ++itIndex, ++i)
911 {
912 TriangleInput.segmentlist[i] = *itIndex;
913 }
914
915 // Input regions
916 TriangleInput.regionlist = new REAL [m_ProjectedRegions.size()*4];
917 TriangleInput.numberofregions = m_ProjectedRegions.size();
918
919 for (i=0; i<TriangleInput.numberofregions; ++i)
920 {
921 TriangleInput.regionlist[i*4] = m_ProjectedRegions[i].Center.x;
922 TriangleInput.regionlist[i*4+1] = m_ProjectedRegions[i].Center.y;
923 TriangleInput.regionlist[i*4+2] = i;
924 TriangleInput.regionlist[i*4+3] = 0; // this is unused
925 }
926
927 triangle_mesh_create(ctx, &TriangleInput);
928
929
930 delete [] TriangleInput.pointlist;
931 delete [] TriangleInput.segmentlist;
932 delete [] TriangleInput.regionlist;
933
935
936 triangle_mesh_copy(ctx, &TriangleOutput, 1, 1);
937
938 XYZ Point;
939 for (i=0; i<TriangleOutput.numberofpoints; ++i)
940 {
941 Point.x = TriangleOutput.pointlist[i*2];
942 Point.y = TriangleOutput.pointlist[i*2+1];
944 }
945
946 m_TriangleRegions.clear();
947 for (i=0; i<TriangleOutput.numberoftriangles; ++i)
948 {
949/* // Lets do a quick check to see if the elements are ordered consistently
950 int i1, i2, i3;
951 i1 = TriangleOutput.trianglelist[i*3];
952 i2 = TriangleOutput.trianglelist[i*3+1];
953 i3 = TriangleOutput.trianglelist[i*3+2];
954 XYZ P1, P2, P3;
955 P1 = m_ProjectedMesh.m_Nodes[i1];
956 P2 = m_ProjectedMesh.m_Nodes[i2];
957 P3 = m_ProjectedMesh.m_Nodes[i3];
958 assert(CrossProduct(P2-P1, P3-P1).z >= 0);*/
959
960 m_ProjectedMesh.GetIndices(CMesh::TRI).push_back(TriangleOutput.trianglelist[i*3]);
961 m_ProjectedMesh.GetIndices(CMesh::TRI).push_back(TriangleOutput.trianglelist[i*3+1]);
962 m_ProjectedMesh.GetIndices(CMesh::TRI).push_back(TriangleOutput.trianglelist[i*3+2]);
963 m_TriangleRegions.push_back((int)TriangleOutput.triangleattributelist[i]);
964 }
965
966 triangle_free(TriangleOutput.pointlist);
967 triangle_free(TriangleOutput.trianglelist);
968 triangle_free(TriangleOutput.triangleattributelist);
969
970 triangle_context_destroy(ctx);
971 return true;
972}
973
974
975bool CBasicVolumes::GetMeshVerticalBounds(const CMesh &Mesh, XYZ Point, double &dMinZ, double &dMaxZ, bool bForceFind)
976{
977 vector< pair<double, XYZ> > IntersectionPoints;
978 vector< pair<double, XYZ> >::iterator itIntersectionPt;
979 XYZ P1, P2;
980 P1 = P2 = Point;
981 P1.z = 0;
982 P2.z = 1;
983 double dZ;
984 Mesh.IntersectLine(P1, P2, IntersectionPoints, make_pair(false, false), bForceFind);
985 for (itIntersectionPt = IntersectionPoints.begin(); itIntersectionPt != IntersectionPoints.end(); )
986 {
987 // Ignore intersections with vertical wall faces
988 if (abs(itIntersectionPt->second.z) < m_dTolerance &&
989 (!IntersectionPoints.empty() || !bForceFind)) // Make sure you don't erase the last find if force find is on
990 itIntersectionPt = IntersectionPoints.erase(itIntersectionPt);
991 else
992 ++itIntersectionPt;
993 }
994 if (IntersectionPoints.empty())
995 return false;
996 for (itIntersectionPt = IntersectionPoints.begin(); itIntersectionPt != IntersectionPoints.end(); ++itIntersectionPt)
997 {
998 dZ = itIntersectionPt->first;
999 if (itIntersectionPt == IntersectionPoints.begin() || dZ < dMinZ)
1000 dMinZ = dZ;
1001 if (itIntersectionPt == IntersectionPoints.begin() || dZ > dMaxZ)
1002 dMaxZ = dZ;
1003 }
1004 return true;
1005}
1006
1008{
1009 CMesh ProjectedMesh = Mesh;
1010// ProjectedMesh.ConvertQuadstoTriangles();
1011 list<int>::iterator itIndex;
1012 list<int>::iterator itCompareIndex;
1013 list<int> &Indices = ProjectedMesh.GetIndices(CMesh::TRI);
1014 int i[3];
1015 int j[3];
1016 int CommonIndices[2];
1017 bool bDirection, bCompareDirection;
1018 XYZ V1, V2;
1019
1020 for (itIndex = Indices.begin(); itIndex != Indices.end(); )
1021 {
1022 i[0] = *(itIndex++);
1023 i[1] = *(itIndex++);
1024 i[2] = *(itIndex++);
1025 V1 = ProjectedMesh.GetNode(i[1])-ProjectedMesh.GetNode(i[0]);
1026 V2 = ProjectedMesh.GetNode(i[2])-ProjectedMesh.GetNode(i[0]);
1027
1028 bDirection = V1.x*V2.y-V2.x*V1.y>0?true:false;
1029 for (itCompareIndex = itIndex; itCompareIndex != Indices.end(); )
1030 {
1031 j[0] = *(itCompareIndex++);
1032 j[1] = *(itCompareIndex++);
1033 j[2] = *(itCompareIndex++);
1034 if (GetCommonEdgeIndices(i, j, CommonIndices))
1035 {
1036 V1 = ProjectedMesh.GetNode(j[1])-ProjectedMesh.GetNode(j[0]);
1037 V2 = ProjectedMesh.GetNode(j[2])-ProjectedMesh.GetNode(j[0]);
1038 bCompareDirection = V1.x*V2.y-V2.x*V1.y>0?true:false;
1039 if (bDirection != bCompareDirection)
1040 {
1041 ProjectedMesh.GetIndices(CMesh::LINE).push_back(CommonIndices[0]);
1042 ProjectedMesh.GetIndices(CMesh::LINE).push_back(CommonIndices[1]);
1043 }
1044 }
1045 }
1046 }
1047
1048 ProjectedMesh.RemoveAllElementsExcept(CMesh::LINE);
1049 ProjectedMesh.RemoveUnreferencedNodes();
1050
1051 vector<XYZ>::iterator itNode;
1052 vector<XYZ> &ProjectedMeshNodes = ProjectedMesh.GetNodes();
1053 for (itNode = ProjectedMeshNodes.begin(); itNode != ProjectedMeshNodes.end(); ++itNode)
1054 {
1055 itNode->z = 0;
1056 }
1057
1058 return ProjectedMesh;
1059}
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
#define TGERROR(MESSAGE)
Macros used to report the file name and line number to the TexGenError and TexGenLog functions.
Definition: Logger.h:29
#define TGLOG(MESSAGE)
Definition: Logger.h:36
#define NULL
Definition: ShinyConfig.h:50
#define TEXGEN
Helper macro to get the texgen instance.
Definition: TexGen.h:76
int RemoveDegenerateSegments(CMesh &Mesh)
bool GetCommonEdgeIndices(int Indices1[3], int Indices2[3], int Common[2])
vector< CMesh > m_YarnMeshes
Definition: BasicVolumes.h:114
bool PointInsideRegion(XYZ Point, int iRegion)
CMesh GetProjectedMesh(const CMesh &Mesh)
void SaveProjectedContoursToVTK(string Filename)
vector< PROJECTED_REGION > m_ProjectedRegions
Definition: BasicVolumes.h:115
vector< int > m_TriangleRegions
Definition: BasicVolumes.h:119
int SplitLinesByLines(CMesh &Mesh)
bool CreateBasicVolumes(CTextile &Textile)
double GetRegionArea(const PROJECTED_REGION &Region)
void SaveProjectedAreasToVTK(string Filename)
int MergeStraightLines(CMesh &Mesh)
int SplitLinesByNodes(CMesh &Mesh)
int RemoveDuplicateSegments(CMesh &Mesh)
bool GetMeshVerticalBounds(const CMesh &Mesh, XYZ Point, double &dMinZ, double &dMaxZ, bool bForceFind=false)
Abstract base class representing the domain in which a textile cell may lie.
Definition: Domain.h:34
const CMesh & GetMesh() const
Get the mesh representing the domain as a surface mesh.
Definition: Domain.h:73
vector< T > m_Data
Definition: MeshData.h:87
Defines the nodes and elements of a surface or volume mesh.
Definition: Mesh.h:58
void RemoveAllElementsExcept(ELEMENT_TYPE Type)
Remove all elements except those of given type.
Definition: Mesh.cpp:711
int MergeNodes(const double Tolerance=1e-8)
If any nodes share the same coordinates merge the nodes together and adjust indices accordingly.
Definition: Mesh.cpp:382
void InsertMesh(const CMesh &Mesh, XYZ Offset=XYZ(0, 0, 0))
Add the contents of Mesh to this mesh.
Definition: Mesh.cpp:93
void ConvertQuadstoTriangles(bool bQuality=true)
Convert the quad elements to triangles.
Definition: Mesh.cpp:1088
const vector< XYZ > & GetNodes() const
Get a const reference to the nodes.
Definition: Mesh.cpp:2656
const int AddNode(XYZ Node)
Append a node to the list of nodes, the integer returns the index of the node
Definition: Mesh.cpp:2624
const XYZ & GetNode(int iIndex) const
Get the node with given ID.
Definition: Mesh.cpp:2636
int RemoveUnreferencedNodes()
Remove nodes that are not referenced by any elements.
Definition: Mesh.cpp:687
bool SaveToVTK(string Filename, const vector< CMeshDataBase * > *pMeshData=NULL) const
Save the mesh to VTK unstructured grid file format (.vtu)
Definition: Mesh.cpp:2348
static int GetNumNodes(ELEMENT_TYPE Type)
Get the number of nodes a particular element type contains.
Definition: Mesh.h:81
const list< int > & GetIndices(ELEMENT_TYPE ElemType) const
Get the element indices of a given element type.
Definition: Mesh.cpp:2671
void Clear()
Empty mesh nodes and indices.
Definition: Mesh.cpp:1448
vector< XYZ >::const_iterator NodesBegin() const
Definition: Mesh.cpp:2604
vector< XYZ >::const_iterator NodesEnd() const
Definition: Mesh.cpp:2609
int IntersectLine(const XYZ &P1, const XYZ &P2, vector< pair< double, XYZ > > &IntersectionPoints, pair< bool, bool > TrimResults=make_pair(false, false), bool bForceFind=false) const
Find the points where a line intersects the mesh.
Definition: Mesh.cpp:1633
Represents a textile cell containing yarns.
Definition: Textile.h:39
const CDomain * GetDomain() const
Definition: Textile.h:287
const vector< CYarn > & GetYarns() const
Definition: Textile.cpp:676
Namespace containing a series of customised math operations not found in the standard c++ library.
double GetLengthSquared(const XYZ &Point1, const XYZ &Point2)
Get the length squared between two points.
Definition: mymath.h:548
XYZ ShortestDistPointLine(const XYZ &Point, const XYZ &LineStart, const XYZ &LineEnd, double &dU)
Definition: mymath.h:749
bool LineLineIntersect2D(const XY &p1, const XY &p2, const XY &p3, const XY &p4, double &dU1, double &dU2)
Definition: mymath.h:797
double RandomNumber(double dMin, double dMax)
Generate a random number between the limits given.
Definition: mymath.h:534
double Max(XYZ &Vector)
Get maximum element of vector and return it.
Definition: mymath.h:642
double GetLength(const XYZ &Point1, const XYZ &Point2)
Get the length between two points.
Definition: mymath.h:540
XYZ Min(const XYZ &P1, const XYZ &P2)
Given two points, return a new point who's coordinates are the smaller of the two.
Definition: mymath.h:1142
WXYZ & Normalise(WXYZ &Quaternion)
Normalise the quaternion and return it.
Definition: mymath.h:609
double DotProduct(const XYZ &left, const XYZ &right)
Get the dot product of two vectors.
Definition: mymath.h:512
XYZ CrossProduct(const XYZ &left, const XYZ &right)
Get the cross product of two vectors.
Definition: mymath.h:524
double GetArea(XYZ Points[], int iNumPoints)
Definition: mymath.h:1245
Struct representing a region projected onto the XY plane.
Definition: BasicVolumes.h:59
Functor defining the < operator based on the projected region's area.
Definition: BasicVolumes.h:73
Used to sort double-int pairs.
Definition: Misc.h:86
Struct for representing points in 2D space.
Definition: mymath.h:103
Struct for representing points in 3D space.
Definition: mymath.h:56
double z
Definition: mymath.h:57
double x
Definition: mymath.h:57
double y
Definition: mymath.h:57