Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
PeriodicGlidePlane.h
Go to the documentation of this file.
1/* This file is part of MoDELib, the Mechanics Of Defects Evolution Library.
2 *
3 *
4 * MoDELib is distributed without any warranty under the
5 * GNU General Public License (GPL) v2 <http://www.gnu.org/licenses/>.
6 */
7
8#ifndef model_PeriodicGlidePlane_H_
9#define model_PeriodicGlidePlane_H_
10
11
12#include <memory>
13#include <algorithm>
14#include <limits>
16#include <string>
17#include <list>
18
19#include <GlidePlane.h>
20#include <GlidePlaneFactory.h>
21//#include <PeriodicGlidePlaneFactory.h>
22#include <TerminalColors.h>
23#include <DiophantineSolver.h>
24#include <SharedPtrFactories.h>
25#include <GramSchmidt.h>
26
27
28namespace model
29{
30
31template<int dim>
33
34template<int dim>
36
37template<int dim>
39
40template<int dim>
42
43template<int dim>
45
46// template<int dim>
47// class PeriodicPlaneEdge;
48
49/**********************************************************************/
50/**********************************************************************/
51template<int dim>
53{
54
55 typedef Eigen::Matrix<double,dim,1> VectorDim;
57 const std::shared_ptr<PeriodicPlaneNode<dim>> source;
58 const std::shared_ptr<PeriodicPlaneNode<dim>> sink;
59 const std::shared_ptr<const MeshBoundarySegment<dim>> meshIntersection;
60 const short int edgeID;
62
66
67 //static VectorDim getDeltaShift(const PeriodicPlanePatch<dim>* const patch,const std::shared_ptr<const MeshBoundarySegment<dim>> meshIntersection);
68
69 /**********************************************************************/
70 PeriodicPlaneEdge(const PeriodicPlanePatch<dim>* const patch_in,
71 const std::shared_ptr<PeriodicPlaneNode<dim>>& source_in,
72 const std::shared_ptr<PeriodicPlaneNode<dim>>& sink_in,
73 const std::shared_ptr<const MeshBoundarySegment<dim>> meshIntersection_in,
74 const short int&)
75 ;
77
78 std::string tag() const;
79};
80
81/**********************************************************************/
82/**********************************************************************/
83template<int dim>
85{
87
90
92 /* init */ inEdge(nullptr)
93 /* init */,outEdge(nullptr)
94 {
95
96 }
97
98};
99
100/**********************************************************************/
101/**********************************************************************/
102template<int dim>
103class PeriodicPlaneNode : public StaticID<PeriodicPlaneNode<dim>>
104/* */,public Eigen::Matrix<double,dim-1,1>
105{
106
107public:
108
109 typedef Eigen::Matrix<double,dim-1,1> VectorLowerDim;
113 typedef std::map<size_t,NodalConnectivity<dim>> NodalConnectivityContainerType;
114 typedef std::set<PeriodicPlaneEdgeType*> InOutEdgeContainerType;
116 typedef CompareVectorsByComponent<double,dim-1,float> CompareType;
117
118private:
119
120 NodalConnectivityContainerType _patchConnectivities;
121 NodalConnectivityContainerType _neighborConnectivities;
122
123
124public:
125
127 void addLink(PeriodicPlaneEdgeType* const link);
128 void removeLink(PeriodicPlaneEdgeType* const link);
135};
136
137/**********************************************************************/
138/**********************************************************************/
139template<int dim>
140struct PeriodicPlanePatch : public StaticID<PeriodicPlanePatch<dim>>
141/* */, private std::vector<std::shared_ptr<PeriodicPlaneEdge<dim>>>
142{
143
144 typedef Eigen::Matrix<double,dim,1> VectorDim;
146 typedef Eigen::Matrix<double,dim-1,1> VectorLowerDim;
148 typedef std::vector<std::shared_ptr<PeriodicPlaneEdge<dim>>> PeriodicPlaneEdgeContainerType;
149
152 const std::shared_ptr<GlidePlane<dim>> glidePlane;
153
159 std::shared_ptr<PeriodicPlaneEdge<dim>> edgeOnFaces(const std::set<const Polyhedron<dim,dim-1>*>& faces) const;
160 int contains(const VectorLowerDim& test) const;
161};
162
163
164
165
166
167template<int dim>
168struct PeriodicPatchBoundary : public StaticID<PeriodicPatchBoundary<dim>>
169/* */,public KeyConstructableWeakPtrFactory<PeriodicPatchBoundary<dim>,PeriodicPlaneNode<dim>,typename PeriodicPlaneNode<dim>::CompareType>
170/* */,private std::set<const PeriodicPlaneEdge<dim>*>
171/* */,public KeyConstructableWeakPtrFactory<PeriodicPatchBoundary<dim>,PeriodicPlanePatch<dim>,typename PeriodicPlanePatch<dim>::CompareType>
172
173{
174 typedef Eigen::Matrix<double,dim,dim> MatrixDim;
175 typedef Eigen::Matrix<double,dim,1> VectorDim;
176 typedef Eigen::Matrix<double,dim-1,1> VectorLowerDim;
178 typedef std::set<const PeriodicPlaneEdge<dim>*> UntwinnedEdgeContainerType;
179 typedef std::vector<const PeriodicPlaneEdge<dim>*> BoundaryContainerType;
180 typedef std::vector<BoundaryContainerType> BoundariesContainerType;
182
186 const std::shared_ptr<GlidePlane<dim>> referencePlane;
187 // const MatrixDim L2G;
188
189
190 // static MatrixDim getL2G(VectorDim z);
191 PeriodicPatchBoundary(GlidePlaneFactory<dim>&, const std::shared_ptr<GlidePlane<dim>>& referencePlane_in);
192 // GlidePlaneKey<dim> getGlidePlaneKey(const VectorDim& shift);
193 // std::shared_ptr<GlidePlane<dim>> getGlidePlane(const VectorDim& shift);
194 void createNewBoundary(const PeriodicPlaneEdge<dim>* currentEdge,UntwinnedEdgeContainerType& untwinnedCopy);
195 void updateBoundaries();
205 const NodeCointainerType& nodes() const;
207 bool isCompact() const;
208 static VectorDim rightHandedNormal(const std::vector<const PeriodicPlaneEdge<dim>*>& bnd);
209 double rightHandedArea(const std::vector<const PeriodicPlaneEdge<dim>*>& bnd);
210 bool isRightHandedBoundary(const std::vector<const PeriodicPlaneEdge<dim>*>& bnd);
211 VectorLowerDim getLocalPosition(const VectorDim& point,const VectorDim& shift) const;
213 std::shared_ptr<PeriodicPlaneNode<dim> > getSharedNode(const VectorDim& pointDim,const VectorDim& shift);
214 std::shared_ptr<PeriodicPlanePatch<dim>> getPatch(const VectorDim& shift);
216 std::shared_ptr<GlidePlane<dim>> getGlidePlane(const VectorDim& shift);
217 const PatchContainerType& patches() const;
219
220
221};
222
223
224
225
226/**********************************************************************/
227/**********************************************************************/
228// ,
229template<int dim>
231// /* */,public KeyConstructableSharedPtrFactory<PeriodicGlidePlane<dim>,PeriodicPlanePatch<dim>> // container of patches
232{
233
234
235public:
236
237 typedef Eigen::Matrix<double,dim,dim> MatrixDim;
238 typedef Eigen::Matrix<double,dim,1> VectorDim;
239 typedef Eigen::Matrix<long int, dim, 1> VectorDimI;
240 typedef Eigen::Matrix<double,dim-1,1> VectorLowerDim;
241 // typedef KeyConstructableSharedPtrFactory<PeriodicGlidePlane<dim>,PeriodicPlanePatch<dim>> PatchContainerType;
242 typedef std::set<const PeriodicPlaneEdge<dim>*> UntwinnedEdgeContainerType;
243 typedef std::vector<const PeriodicPlaneEdge<dim>*> BoundaryContainerType;
244 typedef std::vector<BoundaryContainerType> BoundariesContainerType;
248
251 const std::shared_ptr<GlidePlane<dim>> referencePlane;
252
253
255 void print();
256 // VectorDim getGlidePlaneShiftfromReferencePlane(const GlidePlane<dim> *gp) const;
257
258 template<typename T>
259 std::vector<PolygonPatchIntersectionRecord<dim,T>> polygonPatchIntersection(const std::vector<std::pair<VectorDim,const T* const>>& polyPoints,const bool& intersectInternalNodes=false);
260 template<typename T>
261 std::vector<PolygonPatchIntersectionRecord<dim,T>> polygonPatchIntersection(const std::vector<std::pair<VectorLowerDim,const T* const>>& polyPoints,const bool& intersectInternalNodes=false);
262
263
264 std::pair<VectorDim,bool> findPatch(const VectorLowerDim&,const VectorDim&);
265
266 std::vector<std::shared_ptr<PeriodicPlanePatch<dim>>> filledPatches(const std::vector<VectorDim>& patchShifts);
267
268};
269
270
271
272template<int dim>
274{
275 typedef Eigen::Matrix<double,dim,1> VectorDim;
279
282 PeriodicPlanePatchIO(std::stringstream& ss);
283 // template <class T>
284 // friend T& operator << (T& os, const PeriodicPlanePatchIO<dim>& ds);
285
286};
287
288template <int dim,class T>
290{
291 os << ds.glidePlaneID<<"\t"
292 /**/<< ds.patchBoundaryID<<"\t"
293 /**/<< std::setprecision(15)<<std::scientific<<ds.shift.transpose();
294 return os;
295}
296
297template<int dim>
298template<typename T>
299std::vector<PolygonPatchIntersectionRecord<dim,T>> PeriodicGlidePlane<dim>::polygonPatchIntersection(const std::vector<std::pair<VectorDim,const T* const>>& polyPoints, const bool& intersectInternalNodes)
300{
301 std::vector<std::pair<VectorLowerDim,const T* const>> lowerPolyPoints;
302 for(const auto& point : polyPoints)
303 {
304 assert(this->referencePlane->contains(point.first) && "reference plane does not cointain point");
305 lowerPolyPoints.push_back(std::make_pair(referencePlane->localPosition(point.first-VectorDim::Zero()),point.second));
306 }
307 return polygonPatchIntersection(lowerPolyPoints,intersectInternalNodes);
308}
309
310template<int dim>
311template<typename T>
312std::vector<PolygonPatchIntersectionRecord<dim,T>> PeriodicGlidePlane<dim>::polygonPatchIntersection(const std::vector<std::pair<VectorLowerDim, const T* const>>& polyPoints, const bool& intersectInternalNodes)
313{
314 using Edge = PeriodicPlaneEdge<dim>;
315 using Patch = PeriodicPlanePatch<dim>;
316 using EdgeIDs = std::pair<short int, short int>;
317 using EdgeMap = std::map<VectorDim, std::set<EdgeIDs>, CompareVectorsByComponent<double,dim,float>>;
319 using Metadata = PolygonPatchMetadata<T>;
320 using Arc = PolygonPatchArcKind;
321 std::vector<Record> result;
322 if(polyPoints.size() < 3)
323 {
324 return result;
325 }
326 // Always intersect geometry: equal endpoint patches do not imply that the
327 // intervening polygon edge stays inside a non-convex periodic domain.
328 (void)intersectInternalNodes;
329 const auto cross = [](const VectorLowerDim& a, const VectorLowerDim& b)
330 {
331 return a[0]*b[1]-a[1]*b[0];
332 };
333 double scale = 1.0;
334 double signedArea = 0.0;
335 for(size_t i=0; i<polyPoints.size(); ++i)
336 {
337 const VectorLowerDim a = polyPoints[i].first-polyPoints.front().first;
338 const VectorLowerDim b = polyPoints[(i+1)%polyPoints.size()].first-polyPoints.front().first;
339 scale = std::max(scale,a.norm());
340 signedArea += cross(a,b);
341 }
342 const double tolerance = 1.e-9*scale;
343 // Junction contraction can leave distinct node identities at coincident
344 // positions. Use the same edge tolerance as the walk below: fewer than
345 // three retained edges cannot enclose a slipped area, even when roundoff
346 // leaves a nonzero signed area for the original nearly collapsed triangle.
347 size_t retainedEdges=0;
348 for(size_t i=0; i<polyPoints.size(); ++i)
349 {
350 if((polyPoints[(i+1)%polyPoints.size()].first-polyPoints[i].first).norm()>tolerance)
351 {
352 ++retainedEdges;
353 }
354 }
355 if(retainedEdges<3)
356 {
357 return result;
358 }
359 // Zero algebraic area is not a collapse proof (opposite lobes cancel).
360 // Only discard a contour confined to a line. For cancellation, a local
361 // oriented turn supplies the boundary orientation convention; successor
362 // selection still uses intersection incidence and signed winding layers.
363 VectorLowerDim longest=VectorLowerDim::Zero();
364 for(const auto& vertex : polyPoints)
365 {
366 const VectorLowerDim d=vertex.first-polyPoints.front().first;
367 if(d.squaredNorm()>longest.squaredNorm())
368 {
369 longest=d;
370 }
371 }
372 double orientation=0.0;
373 for(const auto& vertex : polyPoints)
374 {
375 const double turn=cross(longest,vertex.first-polyPoints.front().first);
376 if(std::abs(turn)>tolerance*longest.norm())
377 {
378 orientation=turn;
379 break;
380 }
381 }
382 if(orientation==0.0)
383 {
384 return result;
385 }
386 const bool forward = (std::abs(signedArea)>tolerance*tolerance ? signedArea : orientation)>0.0;
388 std::vector<std::shared_ptr<Patch>> heldPatches;
389 const auto getPatch = [&](const VectorDim& shift)
390 {
391 auto patch = boundary.getPatch(shift);
392 heldPatches.push_back(patch);
393 return patch;
394 };
395 const auto traversable = [](const Edge* edge)
396 {
397 for(const auto* face : edge->meshIntersection->faces)
398 {
399 if(!face->periodicFacePair.second ||
400 face->regionIDs != face->periodicFacePair.second->regionIDs)
401 {
402 return false;
403 }
404 }
405 return edge->deltaShift.squaredNorm() > 0.0;
406 };
407 const auto crossSeam = [&](const Edge* edge)
408 {
409 auto patch = getPatch(edge->patch->shift+edge->deltaShift);
410 if(!edge->twin)
411 {
412 throw std::runtime_error("Periodic polygon walk: same-grain edge has no twin after creating its neighbor.");
413 }
414 return patch;
415 };
416 const auto edgeIDs = [&](const Patch* patch, const VectorLowerDim& point)
417 {
418 EdgeIDs ids(-1,-1);
419 for(const auto& edge : patch->edges())
420 {
421 const VectorLowerDim d = *edge->sink-*edge->source;
422 const double t = d.dot(point-*edge->source)/d.squaredNorm();
423 if(t>=-tolerance/d.norm() && t<=1.0+tolerance/d.norm() &&
424 std::abs(cross(d,point-*edge->source))<=tolerance*d.norm())
425 {
426 if(ids.first<0)
427 {
428 ids.first=edge->edgeID;
429 }
430 else
431 {
432 ids.second=edge->edgeID;
433 }
434 }
435 }
436 return ids;
437 };
438 const auto originalSegment = [&](size_t index)
439 {
440 const size_t next=(index+1)%polyPoints.size();
441 return PolygonOriginalSegment<T>{index,next,polyPoints[index].second,polyPoints[next].second};
442 };
443 const auto metadata = [&](const VectorLowerDim& point, std::optional<size_t> segment,
444 std::optional<size_t> vertex, Arc incoming,
445 std::optional<size_t> incomingSegment, bool seam)
446 {
447 Metadata value;
448 value.originalVertex=vertex;
449 value.incomingArc=incoming;
450 value.crossesPeriodicSeam=seam;
451 if(segment)
452 {
453 const auto origin=originalSegment(*segment);
454 const VectorLowerDim d=polyPoints[origin.sinkIndex].first-polyPoints[origin.sourceIndex].first;
455 const double t=d.squaredNorm()>0.0 ? d.dot(point-polyPoints[origin.sourceIndex].first)/d.squaredNorm() : 0.0;
456 value.polygonLocation=PolygonSegmentLocation<T>{origin,std::clamp(t,0.0,1.0)};
457 }
458 if(incomingSegment)
459 {
460 value.incomingOriginalSegment=originalSegment(*incomingSegment);
461 }
462 return value;
463 };
464 const auto classify = [](Metadata& value)
465 {
466 if(value.originalVertex)
467 {
469 }
470 else if(value.onPhysicalBoundary)
471 {
472 value.kind=value.polygonLocation ? PolygonPatchNodeKind::PhysicalBoundaryIntersection
474 }
475 else
476 {
478 }
479 };
480 const auto append = [&](const VectorLowerDim& point, const Patch* patch, const T* original, Metadata value)
481 {
482 const EdgeIDs geometricIDs=edgeIDs(patch,point);
483 value.atPatchCorner=geometricIDs.second>=0;
484 for(short id : {geometricIDs.first,geometricIDs.second})
485 {
486 if(id>=0 && !traversable(patch->edges()[id].get()))
487 {
488 value.onPhysicalBoundary=true;
489 }
490 }
491 const EdgeIDs ids = original ? EdgeIDs(-1,-1) : geometricIDs;
492 const auto samePort=[&](const Metadata& previous)
493 {
494 if((!previous.originalVertex && !previous.polygonLocation) ||
495 (!value.originalVertex && !value.polygonLocation))
496 {
497 return true; // A corner annotation adds no distinct polygon port.
498 }
499 if(previous.originalVertex && value.originalVertex)
500 {
501 return previous.originalVertex==value.originalVertex;
502 }
503 return previous.polygonLocation && value.polygonLocation &&
504 previous.polygonLocation->segment.sourceIndex==value.polygonLocation->segment.sourceIndex &&
505 std::abs(previous.polygonLocation->parameter-value.polygonLocation->parameter)<=1.e-10;
506 };
507 if(!result.empty() && samePort(std::get<6>(result.back())) &&
508 (std::get<0>(result.back())-point).norm()<=tolerance &&
509 (std::get<1>(result.back())-patch->shift).norm()<=tolerance)
510 {
511 auto merged=std::get<6>(result.back());
512 merged.crossesPeriodicSeam=merged.crossesPeriodicSeam || value.crossesPeriodicSeam;
513 merged.onPhysicalBoundary=merged.onPhysicalBoundary || value.onPhysicalBoundary;
514 merged.atPatchCorner=merged.atPatchCorner || value.atPatchCorner;
515 if(value.polygonLocation)
516 {
517 merged.polygonLocation=value.polygonLocation;
518 }
519 if(value.originalVertex)
520 {
521 merged.originalVertex=value.originalVertex;
522 }
523 classify(merged);
524 if(original)
525 {
526 result.pop_back();
527 result.emplace_back(point,patch->shift,ids,EdgeMap(),0,original,merged);
528 }
529 else
530 {
531 std::get<6>(result.back())=merged;
532 }
533 return;
534 }
535 classify(value);
536 result.emplace_back(point,patch->shift,ids,EdgeMap(),0,original,value);
537 };
538 // Geometry stage: split the directed polygon at patch intersections. Each
539 // interval retains its original segment identity, including coincident arcs.
540 const auto central=getPatch(VectorDim::Zero());
541 VectorLowerDim lower=polyPoints.front().first;
542 VectorLowerDim upper=lower;
543 for(const auto& vertex : polyPoints)
544 {
545 lower=lower.cwiseMin(vertex.first);
546 upper=upper.cwiseMax(vertex.first);
547 }
548 for(const auto& edge : central->edges())
549 {
550 lower=lower.cwiseMin(*edge->source);
551 upper=upper.cwiseMax(*edge->source);
552 }
553 std::vector<std::shared_ptr<Patch>> patches{central};
554 std::set<const Patch*> discovered{central.get()};
555 for(size_t i=0; i<patches.size(); ++i)
556 {
557 for(const auto& edge : patches[i]->edges())
558 {
559 if(traversable(edge.get()))
560 {
561 const auto neighbor=crossSeam(edge.get());
562 if(discovered.insert(neighbor.get()).second)
563 {
564 VectorLowerDim lo=*neighbor->edges().front()->source;
565 VectorLowerDim hi=lo;
566 for(const auto& other : neighbor->edges())
567 {
568 lo=lo.cwiseMin(*other->source);
569 hi=hi.cwiseMax(*other->source);
570 }
571 if((lo.array()<=upper.array()+tolerance).all() &&
572 (hi.array()>=lower.array()-tolerance).all())
573 {
574 patches.push_back(neighbor);
575 }
576 }
577 }
578 }
579 if(patches.size()>100000)
580 {
581 throw std::runtime_error("Periodic polygon graph exceeded its patch limit.");
582 }
583 }
584 struct Piece
585 {
586 size_t segment;
587 double begin,end;
588 const Patch* patch;
589 VectorLowerDim source,sink;
590 };
591 std::vector<Piece> pieces;
592 for(size_t segment=0; segment<polyPoints.size(); ++segment)
593 {
594 const VectorLowerDim a=polyPoints[segment].first;
595 const VectorLowerDim d=polyPoints[(segment+1)%polyPoints.size()].first-a;
596 if(d.norm()<=tolerance)
597 {
598 continue;
599 }
600 const double paramTolerance=tolerance/d.norm();
601 std::vector<Piece> intervals;
602 for(const auto& patch : patches)
603 {
604 double enter=0.0;
605 double leave=1.0;
606 bool outside=false;
607 for(const auto& edge : patch->edges())
608 {
609 const VectorLowerDim direction=*edge->sink-*edge->source;
610 const double f0=cross(direction,a-*edge->source);
611 const double f1=cross(direction,a+d-*edge->source);
612 // Same-grain periodic faces partition adjacent patches. A
613 // geometric expansion here creates overlapping intervals at an
614 // oblique seam/physical corner, possibly assigning a cut to the
615 // wrong patch with no physical boundary port. Allow only
616 // roundoff at these seams. Physical faces retain the geometric
617 // contact tolerance so stationary boundary contours are stable.
618 const double coordinateScale=std::max({1.0,a.norm(),(a+d).norm(),
619 edge->source->norm(),edge->sink->norm()});
620 const double distanceTolerance=traversable(edge.get()) ?
621 64*std::numeric_limits<double>::epsilon()*coordinateScale : tolerance;
622 const double eps=distanceTolerance*direction.norm();
623 if(f0 < -eps && f1 < -eps)
624 {
625 outside=true;
626 break;
627 }
628 if(f0 < -eps)
629 {
630 enter=std::max(enter,f0/(f0-f1));
631 }
632 else if(f1 < -eps)
633 {
634 leave=std::min(leave,f0/(f0-f1));
635 }
636 }
637 if(!outside && leave-enter>paramTolerance)
638 {
639 intervals.push_back({segment,enter,leave,patch.get(),a+enter*d,a+leave*d});
640 }
641 }
642 std::stable_sort(intervals.begin(),intervals.end(),[](const Piece& a,const Piece& b)
643 {
644 return a.begin<b.begin;
645 });
646 // A segment on a periodic seam belongs to both closed patches. Retain
647 // one interval, preferring the incoming patch to avoid an artificial hop.
648 for(size_t i=0; i<intervals.size();)
649 {
650 size_t end=i+1;
651 while(end<intervals.size() && std::abs(intervals[end].begin-intervals[i].begin)<=paramTolerance &&
652 std::abs(intervals[end].end-intervals[i].end)<=paramTolerance)
653 {
654 ++end;
655 }
656 size_t chosen=i;
657 if(!pieces.empty())
658 {
659 for(size_t j=i; j<end; ++j)
660 {
661 if(intervals[j].patch==pieces.back().patch)
662 {
663 chosen=j;
664 break;
665 }
666 }
667 }
668 pieces.push_back(intervals[chosen]);
669 i=end;
670 }
671 }
672 const auto nextPhysicalInDirection=[&](const Edge* edge,bool direction)
673 {
674 const Edge* next=direction ? edge->next : edge->prev;
675 std::set<const Edge*> corners;
676 while(traversable(next))
677 {
678 if(!corners.insert(next).second)
679 {
680 throw std::runtime_error("Periodic polygon graph has a cyclic seam corner.");
681 }
682 crossSeam(next);
683 next=direction ? next->twin->next : next->twin->prev;
684 }
685 return next;
686 };
687 if(pieces.empty())
688 {
689 // No retained polygon arc does not imply an empty slipped area: the
690 // polygon may surround an entire bounded grain cross-section. With no
691 // crossing arcs, winding is constant in each convex patch interior.
692 for(const auto& patch : patches)
693 {
694 VectorLowerDim interior=VectorLowerDim::Zero();
695 for(const auto& edge : patch->edges())
696 {
697 interior+=*edge->source;
698 }
699 interior/=patch->edges().size();
700 int winding=0;
701 for(size_t i=0; i<polyPoints.size(); ++i)
702 {
703 const VectorLowerDim a=polyPoints[i].first-interior;
704 const VectorLowerDim b=polyPoints[(i+1)%polyPoints.size()].first-interior;
705 if(a.y()<=0 && b.y()>0 && cross(a,b)>0)
706 {
707 ++winding;
708 }
709 else if(a.y()>0 && b.y()<=0 && cross(a,b)<0)
710 {
711 --winding;
712 }
713 }
714 if(winding==0)
715 {
716 continue;
717 }
718 const Edge* seed=nullptr;
719 for(const auto& edge : patch->edges())
720 {
721 if(!traversable(edge.get()))
722 {
723 seed=edge.get();
724 break;
725 }
726 }
727 if(!seed)
728 {
729 throw std::runtime_error("Enclosed periodic patch has no physical boundary seed.");
730 }
731 const bool direction=winding>0;
732 for(int layer=0; layer<std::abs(winding); ++layer)
733 {
734 const Edge* edge=seed;
735 std::set<const Edge*> visited;
736 do
737 {
738 if(!visited.insert(edge).second || visited.size()>100000)
739 {
740 throw std::runtime_error("Enclosed physical boundary does not form a closed cycle.");
741 }
742 const VectorLowerDim corner=direction ? *edge->sink : *edge->source;
743 auto enclosed=metadata(corner,std::nullopt,std::nullopt,Arc::PhysicalBoundary,std::nullopt,false);
744 enclosed.enclosureLayer=layer;
745 append(corner,edge->patch,nullptr,enclosed);
746 const Edge* next=nextPhysicalInDirection(edge,direction);
747 if(next->patch!=edge->patch)
748 {
749 std::get<6>(result.back()).crossesPeriodicSeam=true;
750 enclosed.incomingArc=Arc::PeriodicSeam;
751 enclosed.crossesPeriodicSeam=true;
752 append(corner,next->patch,nullptr,enclosed);
753 }
754 edge=next;
755 }
756 while(edge!=seed);
757 }
758 // Retain one connected contour, as in the intersection-driven walk.
759 return result;
760 }
761 return result; // The polygon is disjoint from every discovered patch.
762 }
763 const size_t none=std::numeric_limits<size_t>::max();
764 const auto continuous=[&](size_t i,size_t j)
765 {
766 const auto& a=pieces[i];
767 const auto& b=pieces[j];
768 if((a.sink-b.source).norm()>tolerance)
769 {
770 return false;
771 }
772 if(a.segment==b.segment && b.begin>=a.end-1.e-10)
773 {
774 return true;
775 }
776 // Clipping can move an endpoint a roundoff-sized distance along its
777 // original segment. Use the same physical tolerance as the skipped
778 // short edges below, rather than requiring exact parameters 0 and 1.
779 // Connectivity still follows the original ordered polygon path.
780 if((a.sink-polyPoints[(a.segment+1)%polyPoints.size()].first).norm()>tolerance ||
781 (b.source-polyPoints[b.segment].first).norm()>tolerance)
782 {
783 return false;
784 }
785 for(size_t k=(a.segment+1)%polyPoints.size(); k!=b.segment; k=(k+1)%polyPoints.size())
786 {
787 if((polyPoints[(k+1)%polyPoints.size()].first-polyPoints[k].first).norm()>tolerance)
788 {
789 return false;
790 }
791 }
792 return true;
793 };
794 const auto nextPhysical=[&](const Edge* edge)
795 {
796 return nextPhysicalInDirection(edge,forward);
797 };
798 const auto boundaryPort=[&](const Patch* patch,const VectorLowerDim& point)
799 {
800 const EdgeIDs ids=edgeIDs(patch,point);
801 const Edge* found=nullptr;
802 for(short id : {ids.first,ids.second})
803 {
804 if(id>=0 && !traversable(patch->edges()[id].get()))
805 {
806 found=patch->edges()[id].get();
807 const VectorLowerDim end=forward ? *found->sink : *found->source;
808 if((point-end).norm()<=tolerance)
809 {
810 found=nextPhysical(found);
811 }
812 break;
813 }
814 }
815 if(!found)
816 {
817 throw std::runtime_error("Periodic polygon cut has no physical boundary port.");
818 }
819 return found;
820 };
821 struct Port
822 {
823 size_t piece;
824 bool exit;
825 const Edge* edge;
826 double parameter;
827 VectorLowerDim point;
828 size_t polygonNext;
829 };
830 std::vector<Port> ports;
831 std::vector<size_t> successor(pieces.size(),none);
832 std::vector<size_t> exitPort(pieces.size(),none);
833 std::vector<size_t> entryPort(pieces.size(),none);
834 std::vector<bool> boundaryWrap(pieces.size(),false);
835 std::vector<bool> boundaryReverse(pieces.size(),false);
836 // Original polygon links define the port permutation. An outside chain
837 // joins each exit to its original entry; it is never inferred by proximity.
838 for(size_t i=0; i<pieces.size(); ++i)
839 {
840 const size_t j=(i+1)%pieces.size();
841 if(continuous(i,j))
842 {
843 successor[i]=j;
844 }
845 else
846 {
847 const size_t out=ports.size();
848 for(bool exit : {true,false})
849 {
850 const size_t piece=exit ? i : j;
851 const VectorLowerDim point=exit ? pieces[piece].sink : pieces[piece].source;
852 const Edge* edge=boundaryPort(pieces[piece].patch,point);
853 const VectorLowerDim a=forward ? *edge->source : *edge->sink;
854 const VectorLowerDim d=forward ? VectorLowerDim(*edge->sink-*edge->source) : VectorLowerDim(*edge->source-*edge->sink);
855 ports.push_back({piece,exit,edge,d.dot(point-a)/d.squaredNorm(),point,ports.size()+1});
856 }
857 exitPort[i]=out;
858 entryPort[j]=out+1;
859 }
860 }
861 if(!ports.empty())
862 {
863 ports.back().polygonNext=0;
864 }
865 std::map<const Edge*,std::vector<size_t>> edgePorts;
866 std::set<const Edge*> physicalEdges;
867 for(const auto& patch : patches)
868 {
869 for(const auto& edge : patch->edges())
870 {
871 if(!traversable(edge.get()))
872 {
873 physicalEdges.insert(edge.get());
874 }
875 }
876 }
877 for(size_t i=0; i<ports.size(); ++i)
878 {
879 physicalEdges.insert(ports[i].edge);
880 edgePorts[ports[i].edge].push_back(i);
881 }
882 // Edge incidence is the only boundary connectivity: next/prev, crossing
883 // twins at periodic corners. Sorting is confined to intersection ordering.
884 std::map<const Edge*,const Edge*> boundaryNext;
885 std::map<const Edge*,const Edge*> boundaryPrevious;
886 std::set<const Edge*> hasPredecessor;
887 for(const auto* edge : physicalEdges)
888 {
889 const Edge* next=nextPhysical(edge);
890 if(physicalEdges.count(next))
891 {
892 boundaryNext[edge]=next;
893 boundaryPrevious[next]=edge;
894 hasPredecessor.insert(next);
895 }
896 auto& list=edgePorts[edge];
897 std::stable_sort(list.begin(),list.end(),[&](size_t a,size_t b)
898 {
899 return ports[a].parameter<ports[b].parameter;
900 });
901 }
902 std::vector<const Edge*> seeds;
903 for(const auto* edge : physicalEdges)
904 {
905 if(!hasPredecessor.count(edge))
906 {
907 seeds.push_back(edge);
908 }
909 }
910 seeds.insert(seeds.end(),physicalEdges.begin(),physicalEdges.end());
911 std::set<const Edge*> connected;
912 for(const auto* seed : seeds)
913 {
914 if(connected.count(seed))
915 {
916 continue;
917 }
918 std::vector<std::vector<size_t>> buckets;
919 const Edge* edge=seed;
920 while(edge && connected.insert(edge).second)
921 {
922 const auto& list=edgePorts[edge];
923 for(size_t i=0; i<list.size();)
924 {
925 size_t j=i+1;
926 const double eps=tolerance/(*edge->sink-*edge->source).norm();
927 while(j<list.size() && std::abs(ports[list[j]].parameter-ports[list[i]].parameter)<=eps)
928 {
929 ++j;
930 }
931 buckets.emplace_back(list.begin()+i,list.begin()+j);
932 i=j;
933 }
934 const auto next=boundaryNext.find(edge);
935 edge=next==boundaryNext.end() ? nullptr : next->second;
936 }
937 if(buckets.empty())
938 {
939 continue;
940 }
941 // Count signed winding layers in boundary order. Only a closed edge
942 // cycle may rotate its port list to a zero-winding starting point.
943 int winding=0;
944 int minimum=0;
945 size_t first=0;
946 for(size_t i=0; i<buckets.size(); ++i)
947 {
948 for(size_t port : buckets[i])
949 {
950 winding+=ports[port].exit ? 1 : -1;
951 }
952 if(winding<minimum)
953 {
954 minimum=winding;
955 first=i+1;
956 }
957 }
958 if(winding!=0)
959 {
960 throw std::runtime_error("Unbalanced physical-boundary intersection ports.");
961 }
962 bool zeroWindingCut=false;
963 if(edge==seed)
964 {
965 // A closed boundary may meet a negative-winding lobe of a folded
966 // polygon. Rotating to the minimum prefix count would add an
967 // artificial full-grain winding. Find an actual zero-winding gap
968 // using polygon intersections, then keep both signed layers.
969 for(size_t k=0;k<buckets.size() && !zeroWindingCut;++k)
970 {
971 const auto& port=ports[buckets[k].front()];
972 const auto& previous=ports[buckets[(k+buckets.size()-1)%buckets.size()].back()];
973 // The gap before a corner port can lie entirely on preceding
974 // edges. Follow boundary incidence to sample that gap; looking
975 // only on the port's outgoing edge misses every such gap.
976 std::vector<VectorLowerDim> samples;
977 const Edge* gapEdge=previous.edge;
978 VectorLowerDim gapStart=previous.point;
979 bool wrap=gapEdge==port.edge && previous.parameter>=port.parameter;
980 std::set<const Edge*> gapVisited;
981 while(gapVisited.insert(gapEdge).second)
982 {
983 const bool last=gapEdge==port.edge && !wrap;
984 const VectorLowerDim gapEnd=last ? port.point :
985 (forward ? *gapEdge->sink : *gapEdge->source);
986 if((gapEnd-gapStart).norm()>2*tolerance)
987 {
988 samples.push_back(0.5*(gapStart+gapEnd));
989 }
990 if(last)
991 {
992 break;
993 }
994 wrap=false;
995 gapEdge=boundaryNext.at(gapEdge);
996 gapStart=forward ? *gapEdge->source : *gapEdge->sink;
997 // Permit the final partial interval after wrapping once.
998 if(gapEdge==port.edge)
999 {
1000 if((port.point-gapStart).norm()>2*tolerance)
1001 {
1002 samples.push_back(0.5*(gapStart+port.point));
1003 }
1004 break;
1005 }
1006 }
1007 for(const auto& sample : samples)
1008 {
1009 int sampleWinding=0;
1010 bool onPolygon=false;
1011 for(size_t j=0;j<polyPoints.size();++j)
1012 {
1013 const VectorLowerDim p=polyPoints[j].first-sample;
1014 const VectorLowerDim q=polyPoints[(j+1)%polyPoints.size()].first-sample;
1015 if(std::abs(cross(p,q))<=tolerance*(q-p).norm() && p.dot(q)<=tolerance*tolerance)
1016 {
1017 onPolygon=true;
1018 break;
1019 }
1020 if(p.y()<=0 && q.y()>0 && cross(p,q)>0)
1021 {
1022 ++sampleWinding;
1023 }
1024 else if(p.y()>0 && q.y()<=0 && cross(p,q)<0)
1025 {
1026 --sampleWinding;
1027 }
1028 }
1029 if(!onPolygon && sampleWinding==0)
1030 {
1031 std::rotate(buckets.begin(),buckets.begin()+k,buckets.end());
1032 zeroWindingCut=true;
1033 break;
1034 }
1035 }
1036 }
1037 }
1038 if(edge!=seed || zeroWindingCut)
1039 {
1040 // An open chain has a fixed beginning: rotating its intersection
1041 // list would invent a connection between its two ends. Signed
1042 // winding layers may instead require a predecessor walk (an entry
1043 // followed by an exit), as in a self-intersecting moving loop.
1044 std::vector<size_t> exits;
1045 std::vector<std::set<size_t>> pendingEntries;
1046 const auto connectPorts=[&](size_t out,size_t in,bool reverse)
1047 {
1048 successor[ports[out].piece]=ports[in].piece;
1049 boundaryReverse[ports[out].piece]=reverse;
1050 if(edge==seed && ports[out].edge==ports[in].edge)
1051 {
1052 const double delta=ports[in].parameter-ports[out].parameter;
1053 const double eps=tolerance/(*ports[out].edge->sink-*ports[out].edge->source).norm();
1054 boundaryWrap[ports[out].piece]=reverse ? delta>eps : delta<-eps;
1055 }
1056 };
1057 const auto chooseEntry=[&](size_t out,const std::set<size_t>& entries)
1058 {
1059 size_t in=ports[out].polygonNext;
1060 while(!entries.count(in) && in!=out)
1061 {
1062 in=ports[in].polygonNext;
1063 }
1064 if(in==out)
1065 {
1066 throw std::runtime_error("Disconnected polygon intersection ports.");
1067 }
1068 return in;
1069 };
1070 for(const auto& bucket : buckets)
1071 {
1072 std::vector<size_t> localExits;
1073 std::set<size_t> entries;
1074 for(size_t port : bucket)
1075 {
1076 if(ports[port].exit)
1077 {
1078 localExits.push_back(port);
1079 }
1080 else
1081 {
1082 entries.insert(port);
1083 }
1084 }
1085 // Resolve coincident ports by their original polygon
1086 // permutation before extending either signed winding layer.
1087 while(!localExits.empty() && !entries.empty())
1088 {
1089 const size_t out=localExits.back();
1090 localExits.pop_back();
1091 const size_t in=chooseEntry(out,entries);
1092 connectPorts(out,in,false);
1093 entries.erase(in);
1094 }
1095 for(size_t out : localExits)
1096 {
1097 if(pendingEntries.empty())
1098 {
1099 exits.push_back(out);
1100 }
1101 else
1102 {
1103 auto& candidates=pendingEntries.back();
1104 const size_t in=chooseEntry(out,candidates);
1105 connectPorts(out,in,true);
1106 candidates.erase(in);
1107 if(candidates.empty())
1108 {
1109 pendingEntries.pop_back();
1110 }
1111 }
1112 }
1113 while(!entries.empty())
1114 {
1115 if(exits.empty())
1116 {
1117 pendingEntries.push_back(entries);
1118 entries.clear();
1119 }
1120 else
1121 {
1122 const size_t out=exits.back();
1123 exits.pop_back();
1124 const size_t in=chooseEntry(out,entries);
1125 connectPorts(out,in,false);
1126 entries.erase(in);
1127 }
1128 }
1129 }
1130 if(!exits.empty() || !pendingEntries.empty())
1131 {
1132 throw std::runtime_error("Unbalanced open-boundary intersection ports.");
1133 }
1134 continue;
1135 }
1136 std::vector<size_t> stack;
1137 for(size_t k=0; k<buckets.size(); ++k)
1138 {
1139 const auto& bucket=buckets[(first+k)%buckets.size()];
1140 std::set<size_t> entries;
1141 for(size_t port : bucket)
1142 {
1143 if(ports[port].exit)
1144 {
1145 stack.push_back(port);
1146 }
1147 else
1148 {
1149 entries.insert(port);
1150 }
1151 }
1152 while(!entries.empty())
1153 {
1154 if(stack.empty())
1155 {
1156 throw std::runtime_error("Physical-boundary entry has no exit port.");
1157 }
1158 const size_t out=stack.back();
1159 stack.pop_back();
1160 // Coincident ports remain distinct. Follow the original port
1161 // permutation to resolve their sheet incidence, independent of
1162 // coordinates, node IDs, or the starting polygon vertex.
1163 size_t in=ports[out].polygonNext;
1164 while(!entries.count(in) && in!=out)
1165 {
1166 in=ports[in].polygonNext;
1167 }
1168 if(in==out)
1169 {
1170 throw std::runtime_error("Disconnected polygon intersection ports.");
1171 }
1172 successor[ports[out].piece]=ports[in].piece;
1173 // Intersection ordering records whether this boundary link
1174 // passes around the entire edge cycle before returning here.
1175 boundaryWrap[ports[out].piece]=ports[out].edge==ports[in].edge &&
1176 ports[in].parameter<ports[out].parameter-tolerance/(*ports[out].edge->sink-*ports[out].edge->source).norm();
1177 entries.erase(in);
1178 }
1179 }
1180 if(!stack.empty())
1181 {
1182 throw std::runtime_error("Physical-boundary exit has no entry port.");
1183 }
1184 }
1185 // Walk the already connected graph. There is no geometric branch search
1186 // here: each polygon arc has exactly one successor, possibly via a face arc.
1187 size_t start=0;
1188 for(size_t i=0; i<pieces.size(); ++i)
1189 {
1190 if(pieces[i].begin==0.0)
1191 {
1192 start=i;
1193 break;
1194 }
1195 }
1196 const auto vertexAt=[&](const Piece& piece,bool sink)->std::optional<size_t>
1197 {
1198 if(sink && piece.end==1.0)
1199 {
1200 return (piece.segment+1)%polyPoints.size();
1201 }
1202 if(!sink && piece.begin==0.0)
1203 {
1204 return piece.segment;
1205 }
1206 return std::nullopt;
1207 };
1208 const auto appendPort=[&](const VectorLowerDim& point,const Patch* patch,size_t segment,
1209 std::optional<size_t> vertex,Arc arc,bool seam)
1210 {
1211 append(point,patch,vertex ? polyPoints[*vertex].second : nullptr,
1212 metadata(point,segment,vertex,arc,arc==Arc::OriginalSegment ? std::optional<size_t>(segment) : std::nullopt,seam));
1213 };
1214 std::set<size_t> walked;
1215 size_t current=start;
1216 do
1217 {
1218 if(!walked.insert(current).second || successor[current]==none)
1219 {
1220 throw std::runtime_error("Invalid polygon intersection successor cycle.");
1221 }
1222 const auto& piece=pieces[current];
1223 const size_t next=successor[current];
1224 const auto& target=pieces[next];
1225 const bool boundaryArc=exitPort[current]!=none;
1226 appendPort(piece.sink,piece.patch,piece.segment,vertexAt(piece,true),Arc::OriginalSegment,false);
1227 if(boundaryArc)
1228 {
1229 const Edge* edge=ports[exitPort[current]].edge;
1230 const Edge* destination=ports[entryPort[next]].edge;
1231 const Patch* active=piece.patch;
1232 if(edge->patch!=active)
1233 {
1234 std::get<6>(result.back()).crossesPeriodicSeam=true;
1235 active=edge->patch;
1236 appendPort(piece.sink,active,piece.segment,vertexAt(piece,true),Arc::PeriodicSeam,true);
1237 }
1238 std::set<const Edge*> route;
1239 bool wrap=boundaryWrap[current];
1240 while(edge!=destination || wrap)
1241 {
1242 wrap=false;
1243 if(!route.insert(edge).second)
1244 {
1245 throw std::runtime_error("Boundary arc cannot reach its assigned entry port.");
1246 }
1247 const VectorLowerDim corner=(forward!=boundaryReverse[current]) ? *edge->sink : *edge->source;
1248 append(corner,active,nullptr,metadata(corner,std::nullopt,std::nullopt,Arc::PhysicalBoundary,std::nullopt,false));
1249 const auto& adjacency=boundaryReverse[current] ? boundaryPrevious : boundaryNext;
1250 const auto following=adjacency.find(edge);
1251 if(following==adjacency.end())
1252 {
1253 throw std::runtime_error("Boundary arc has no stored successor.");
1254 }
1255 edge=following->second;
1256 if(edge->patch!=active)
1257 {
1258 std::get<6>(result.back()).crossesPeriodicSeam=true;
1259 active=edge->patch;
1260 append(corner,active,nullptr,metadata(corner,std::nullopt,std::nullopt,Arc::PeriodicSeam,std::nullopt,true));
1261 }
1262 }
1263 appendPort(target.source,active,target.segment,vertexAt(target,false),Arc::PhysicalBoundary,false);
1264 if(active!=target.patch)
1265 {
1266 std::get<6>(result.back()).crossesPeriodicSeam=true;
1267 appendPort(target.source,target.patch,target.segment,vertexAt(target,false),Arc::PeriodicSeam,true);
1268 }
1269 }
1270 else if(piece.patch!=target.patch)
1271 {
1272 std::get<6>(result.back()).crossesPeriodicSeam=true;
1273 appendPort(target.source,target.patch,target.segment,vertexAt(target,false),Arc::PeriodicSeam,true);
1274 }
1275 current=next;
1276 }
1277 while(current!=start);
1278 // Clipping can collapse a nondegenerate input polygon onto a physical
1279 // face. This is the COMPLETE walked contour, including every seam and
1280 // boundary bend, not merely its original anchors. With fewer than three
1281 // records its closed straight-edge path can only retrace a point or line.
1282 // Return the ordinary empty-contour result so reconciliation disconnects
1283 // only this loop's memberships, preserving shared network junctions.
1284 if(result.size()<3)
1285 {
1286 return {};
1287 }
1288 // Preserve the existing record contract: crossings between successive
1289 // original nodes carry the same edge map and a decreasing remaining count.
1290 size_t begin=0;
1291 for(size_t end=0; end<result.size(); ++end)
1292 {
1293 if(std::get<5>(result[end]) || end+1==result.size())
1294 {
1295 EdgeMap edges;
1296 size_t count=0;
1297 for(size_t i=begin; i<end; ++i)
1298 {
1299 edges[std::get<1>(result[i])].insert(std::get<2>(result[i]));
1300 ++count;
1301 }
1302 for(size_t i=begin; i<=end; ++i)
1303 {
1304 std::get<3>(result[i])=edges;
1305 if(i<end)
1306 {
1307 std::get<4>(result[i])=--count;
1308 }
1309 }
1310 begin=end+1;
1311 }
1312 }
1313 return result;
1314}
1315
1316template<typename T1,typename T2, typename T3,typename T4>
1317void printIntersections(const T1& ppi,const T2& usedPatches,const T3& lastPatch,int& removeMe, const T4& untwinnedEdges)
1318{
1319 std::ofstream pointsFile("points_"+std::to_string(removeMe)+".txt");
1320 for(const auto& tup : ppi)
1321 {
1322 // if(!node.second.expired())
1323 // {
1324 pointsFile<<" "<<std::get<0>(tup).transpose()<<std::endl;
1325 // }
1326 }
1327
1328 std::ofstream edgesFile("edges_"+std::to_string(removeMe)+".txt");
1329 for(const auto& patch : usedPatches)
1330 {
1331 for(const auto& edge : patch->edges())
1332 {
1333 if(untwinnedEdges.find(edge.get())!=untwinnedEdges.end())
1334 {
1335 edgesFile<<patch->sID<<" "<<edge->source->transpose()<<" "<<edge->sink->transpose()<<" 1"<<std::endl;
1336 }
1337 else
1338 {
1339 edgesFile<<patch->sID<<" "<<edge->source->transpose()<<" "<<edge->sink->transpose()<<" 0"<<std::endl;
1340 }
1341
1342 }
1343 }
1344
1345 std::ofstream lastPatchFile("lastPatch_"+std::to_string(removeMe)+".txt");
1346 for(const auto& edge : lastPatch->edges())
1347 {
1348 lastPatchFile<<lastPatch->sID<<" "<<edge->source->transpose()<<" "<<edge->sink->transpose()<<std::endl;
1349 }
1350 removeMe++;
1351}
1352
1353
1354}
1355#endif
Class template representing a straight line at the intersection of PlanarMeshFaces.
std::vector< const PeriodicPlaneEdge< dim > * > BoundaryContainerType
GlidePlaneFactory< dim > & glidePlaneFactory
std::pair< VectorDim, bool > findPatch(const VectorLowerDim &, const VectorDim &)
PeriodicGlidePlaneFactoryType & periodicGlidePlaneFactory
Eigen::Matrix< double, dim-1, 1 > VectorLowerDim
std::vector< PolygonPatchIntersectionRecord< dim, T > > polygonPatchIntersection(const std::vector< std::pair< VectorDim, const T *const > > &polyPoints, const bool &intersectInternalNodes=false)
Eigen::Matrix< long int, dim, 1 > VectorDimI
const std::shared_ptr< GlidePlane< dim > > referencePlane
std::vector< std::shared_ptr< PeriodicPlanePatch< dim > > > filledPatches(const std::vector< VectorDim > &patchShifts)
Eigen::Matrix< double, dim, 1 > VectorDim
PeriodicGlidePlane(PeriodicGlidePlaneFactoryType *const pgpf, const KeyType &key_in)
std::vector< BoundaryContainerType > BoundariesContainerType
std::set< const PeriodicPlaneEdge< dim > * > UntwinnedEdgeContainerType
GlidePlaneType::KeyType KeyType
Eigen::Matrix< double, dim, dim > MatrixDim
PeriodicGlidePlaneFactory< dim > PeriodicGlidePlaneFactoryType
const NodalConnectivityContainerType & neighborConnectivities() const
PeriodicPlaneEdge< dim > PeriodicPlaneEdgeType
CompareVectorsByComponent< double, dim-1, float > CompareType
const NodalConnectivityContainerType & patchConnectivities() const
Eigen::Matrix< double, dim-1, 1 > VectorLowerDim
void removeLink(PeriodicPlaneEdgeType *const link)
InOutEdgeContainerType outEdges() const
void addLink(PeriodicPlaneEdgeType *const link)
std::map< size_t, NodalConnectivity< dim > > NodalConnectivityContainerType
PeriodicPlaneNode(PeriodicPatchBoundary< dim > *const, const VectorLowerDim &pos)
InOutEdgeContainerType inEdges() const
NodalConnectivity< dim > NodalConnectivityType
std::set< PeriodicPlaneEdgeType * > InOutEdgeContainerType
PeriodicPlaneNode< dim > PeriodicPlaneNodeType
A class template that implements a counter of the number of instances of Derived type that are create...
Definition StaticID.h:29
the magnitude of the R vector q
final point of the source segment t
a
Definition int_R.m:14
b
Definition int_R.m:16
void printIntersections(const T1 &ppi, const T2 &usedPatches, const T3 &lastPatch, int &removeMe, const T4 &untwinnedEdges)
size_t InstanceCount< Derived >::count
T & operator<<(T &os, const PeriodicPlanePatchIO< dim > &ds)
std::tuple< Eigen::Matrix< double, dim-1, 1 >, Eigen::Matrix< double, dim, 1 >, std::pair< short int, short int >, std::map< Eigen::Matrix< double, dim, 1 >, std::set< std::pair< short int, short int > >, CompareVectorsByComponent< double, dim, float > >, size_t, const T *const, PolygonPatchMetadata< T > > PolygonPatchIntersectionRecord
LatticePlaneKey< dim > GlidePlaneKey
GlidePlaneKey< dim > KeyType
Definition GlidePlane.h:49
PeriodicPlaneEdgeType * outEdge
PeriodicPlaneEdge< dim > PeriodicPlaneEdgeType
PeriodicPlaneEdgeType * inEdge
static VectorDim rightHandedNormal(const std::vector< const PeriodicPlaneEdge< dim > * > &bnd)
void createNewBoundary(const PeriodicPlaneEdge< dim > *currentEdge, UntwinnedEdgeContainerType &untwinnedCopy)
PeriodicPatchBoundary(GlidePlaneFactory< dim > &, const std::shared_ptr< GlidePlane< dim > > &referencePlane_in)
KeyConstructableWeakPtrFactory< PeriodicPatchBoundary< dim >, PeriodicPlaneNode< dim >, typename PeriodicPlaneNode< dim >::CompareType > NodeCointainerType
Eigen::Matrix< double, dim, dim > MatrixDim
VectorLowerDim getLocalPosition(const VectorDim &point, const VectorDim &shift) const
std::vector< const PeriodicPlaneEdge< dim > * > BoundaryContainerType
const PatchContainerType & patches() const
VectorDim getGlobalPosition(const VectorLowerDim &point) const
BoundariesContainerType & innerBoundaries()
bool isRightHandedBoundary(const std::vector< const PeriodicPlaneEdge< dim > * > &bnd)
BoundariesContainerType _outerBoundaries
std::set< const PeriodicPlaneEdge< dim > * > UntwinnedEdgeContainerType
void addUntwinnedEdge(const PeriodicPlaneEdge< dim > *link)
std::shared_ptr< GlidePlane< dim > > getGlidePlane(const VectorDim &shift)
double rightHandedArea(const std::vector< const PeriodicPlaneEdge< dim > * > &bnd)
const std::shared_ptr< GlidePlane< dim > > referencePlane
int isInsideOuterBoundary(const VectorLowerDim &test)
BoundariesContainerType _innerBoundaries
Eigen::Matrix< double, dim-1, 1 > VectorLowerDim
KeyConstructableWeakPtrFactory< PeriodicPatchBoundary< dim >, PeriodicPlanePatch< dim >, typename PeriodicPlanePatch< dim >::CompareType > PatchContainerType
std::vector< BoundaryContainerType > BoundariesContainerType
Eigen::Matrix< double, dim, 1 > VectorDim
const UntwinnedEdgeContainerType & untwinnedEdges() const
std::shared_ptr< PeriodicPlanePatch< dim > > getPatch(const VectorDim &shift)
std::shared_ptr< PeriodicPlaneNode< dim > > getSharedNode(const VectorDim &pointDim, const VectorDim &shift)
BoundariesContainerType & outerBoundaries()
GlidePlaneFactory< dim > & glidePlaneFactory
GlidePlaneKey< dim > getGlidePlaneKey(const VectorDim &shift)
void removeUntwinnedEdge(const PeriodicPlaneEdge< dim > *link)
PeriodicPlaneEdge(const PeriodicPlanePatch< dim > *const patch_in, const std::shared_ptr< PeriodicPlaneNode< dim > > &source_in, const std::shared_ptr< PeriodicPlaneNode< dim > > &sink_in, const std::shared_ptr< const MeshBoundarySegment< dim > > meshIntersection_in, const short int &)
const std::shared_ptr< const MeshBoundarySegment< dim > > meshIntersection
PeriodicPlaneEdge< dim > * next
Eigen::Matrix< double, dim, 1 > VectorDim
const std::shared_ptr< PeriodicPlaneNode< dim > > sink
const std::shared_ptr< PeriodicPlaneNode< dim > > source
PeriodicPlaneEdge< dim > * twin
const PeriodicPlanePatch< dim > *const patch
PeriodicPlaneEdge< dim > * prev
std::shared_ptr< PeriodicPlaneEdge< dim > > edgeOnFaces(const std::set< const Polyhedron< dim, dim-1 > * > &faces) const
static bool isRightHandedBoundary(const BoundingMeshSegments< dim > &, const Plane< dim > &)
const PeriodicPlaneEdgeContainerType & edges() const
Eigen::Matrix< double, dim-1, 1 > VectorLowerDim
CompareVectorsByComponent< double, dim, float > CompareType
Eigen::Matrix< double, dim, 1 > VectorDim
const std::shared_ptr< GlidePlane< dim > > glidePlane
PeriodicPatchBoundary< dim > *const patchBoundary
PeriodicPlanePatch(PeriodicPatchBoundary< dim > *const, const VectorDim &)
int contains(const VectorLowerDim &test) const
void addMeshIntersections(const BoundingMeshSegments< dim > &)
std::vector< std::shared_ptr< PeriodicPlaneEdge< dim > > > PeriodicPlaneEdgeContainerType
Eigen::Matrix< double, dim, 1 > VectorDim
void parameter(std::string &text, const std::string &key, const std::string &value)
int end[2]
int corner[3]
m edges
int j
void out
struct otri neighbor
newsubseg ss[0]
void in
int index
int corners
m samples
vertex p[3]
int k
int i
REAL * vertex
Definition triangle.cpp:542
result
vertex first
int current
REAL longest