TexGen
MeshDomainPlane.cpp
Go to the documentation of this file.
1/*=============================================================================
2TexGen: Geometric textile modeller.
3Copyright (C) 2019 Louise Brown
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 "TexGen.h"
22#include "MeshDomainPlane.h"
23
24extern "C"
25{
26#include "../Triangle/triangle.h"
27#include "../Triangle/triangle_api.h"
28}
29
30using namespace TexGen;
31using namespace std;
32
33CMeshDomainPlane::CMeshDomainPlane(double Seed, bool bFillEnds)
34{
35 m_Seed = Seed;
36 m_bFillEnds = bFillEnds;
37}
38
40{
41}
42
44{
45 // Add facets for domain planes
46 vector<CMesh>::iterator itDomainMeshes;
47
48 vector<CMesh>::iterator itTriangulatedMeshes;
49 int NumEdgeTris = 0;
50 int iNumNodes = CMesh::GetNumNodes(CMesh::TRI);
51 vector<XYZ> DomainMeshNormals;
52 int i;
53 vector<PLANEPARAMS> PlaneParams;
54
55 for (itDomainMeshes = m_DomainMeshes.begin(), i = 0; itDomainMeshes != m_DomainMeshes.end(); itDomainMeshes++, ++i)
56 {
57 const list<int> &QuadIndices = itDomainMeshes->GetIndices(CMesh::QUAD);
58 const list<int> &PolygonIndices = itDomainMeshes->GetIndices(CMesh::POLYGON);
59 list<int>::const_iterator itQuadIndices;
60 list<int>::const_iterator itPolyIndices;
61 vector<int> NumVertices;
62 vector<XY> HolePoints;
63 bool bIsQuad = QuadIndices.size() > 0;
64
65 // Save number of indices in each polygon (each polygon represents intersection of yarn with domain plane)
66 // If no quad element then first polygon is outline of plane in a prism domain
67 if (PolygonIndices.size() > 0)
68 {
69 for (itPolyIndices = PolygonIndices.begin(); itPolyIndices != PolygonIndices.end(); )
70 {
71 int Num = 0;
72 int Start = *itPolyIndices;
73 do
74 {
75 ++Num;
76 ++itPolyIndices;
77 } while ((itPolyIndices != PolygonIndices.end()) && ((*itPolyIndices) != Start));
78
79 if ((*itPolyIndices) != Start) // Reached end and not found complete polygon
80 {
81 TGERROR("Error creating intersection of yarn with domain");
82 return;
83 }
84 ++Num;
85 NumVertices.push_back(Num);
86 ++itPolyIndices;
87 }
88 m_PolygonNumVertices.push_back(NumVertices);
89 }
90
91 CMesh TriangleMesh;
92
93 // Convert domain edge points to 2D points. ConvertRef contains information to restore 2D points back to 3D on correct plane
94 // Will be a quad element or a polygon if one end of prism domain
95 vector< vector<XY> > ArrayPoints2D;
96 vector<XY> Points2D;
97 PLANEPARAMS ConvertRef;
98 if ( bIsQuad )
99 ConvertDomainPointsTo2D(QuadIndices, *itDomainMeshes, CMesh::GetNumNodes(CMesh::QUAD), Points2D, ConvertRef);
100 else
101 ConvertDomainPointsTo2D(PolygonIndices, *itDomainMeshes, NumVertices[0]-1, Points2D, ConvertRef);
102 PlaneParams.push_back(ConvertRef);
103 ArrayPoints2D.push_back(Points2D);
104
105
106 // Add yarn end polygons to 2D point array
107 int Poly = 0;
108 //for (itPolyIndices = PolygonIndices.begin(); itPolyIndices != PolygonIndices.end(); )
109 itPolyIndices = PolygonIndices.begin();
110 if (!bIsQuad)
111 {
112 advance(itPolyIndices, NumVertices[0]); // point to start of second polygon in list
113 Poly = 1;
114 }
115
116 for ( itPolyIndices; itPolyIndices != PolygonIndices.end(); )
117 {
118 vector<XYZ> Points3D;
119 for (int iNode = 0; iNode < NumVertices[Poly] - 1; ++iNode)
120 {
121 XYZ Point = itDomainMeshes->GetNode(*(itPolyIndices++));
122 Points3D.push_back(Point);
123 }
124 Points2D.clear();
125 Convert3DTo2DCoordinates(Points3D, ConvertRef, Points2D);
126 ArrayPoints2D.push_back(Points2D);
127
128 // Set up point inside polygon to be seed point if hole
129 XY HolePoint = (Points2D[0] + Points2D[(int)Points2D.size()/2])/2.0;
130 HolePoints.push_back(HolePoint);
131
132 itPolyIndices++;
133 Poly++;
134 }
135
136 int j;
137 if (bPeriodic) // At the moment the assumption is that prism domains will be treated as non-periodic
138 {
139 for (j = 0; j < i; ++j)
140 {
141 // If planes normals are equal and opposite (ie opposite faces of box) replicate mesh on second plane
142 if (GetLength((ConvertRef.Normal + PlaneParams[j].Normal)) < 0.000000001)
143 {
144 double dDist = DotProduct(PlaneParams[j].Normal, (PlaneParams[j].RefPoint - ConvertRef.RefPoint));
145 TriangleMesh = m_TriangulatedMeshes[j];
146 OffsetMeshPoints(TriangleMesh, ConvertRef.Normal, dDist);
147 break;
148 }
149 }
150 if (j == i) // If not already triangulated plane, do so now
151 {
152 vector<XY> SeededSides;
153 SeedSides(ArrayPoints2D[0]);
154 Triangulate(ArrayPoints2D, HolePoints, TriangleMesh, ConvertRef);
155 }
156 NumEdgeTris += (int)TriangleMesh.GetIndices(CMesh::TRI).size() / iNumNodes;
157 //m_TriangulatedMeshes.push_back(TriangleMesh);
158 }
159 else
160 {
161 vector<XY> SeededSides;
162 SeedSides(ArrayPoints2D[0]);
163 Triangulate(ArrayPoints2D, HolePoints, TriangleMesh, ConvertRef);
164 }
165 m_TriangulatedMeshes.push_back(TriangleMesh);
166 }
167
168}
169
170
171
172bool CMeshDomainPlane::ConvertDomainPointsTo2D(const list<int> &Indices, CMesh& DomainMesh, int numNodes, vector<XY>& Points2D, PLANEPARAMS& ConvertRef)
173{
174 list<int>::const_iterator itIndices;
175 itIndices = Indices.begin();
176 //for (itIndices = Indices.begin(); itIndices != Indices.end(); )
177 //{
178 vector<XYZ> Points3D;
179 for (int iNode = 0; iNode < numNodes; ++iNode)
180 {
181 XYZ Point = DomainMesh.GetNode(*(itIndices++));
182 Points3D.push_back(Point);
183 }
184 if (Points3D.size() < 3)
185 return false;
186 ConvertRef.RefPoint = Points3D[0];
187 ConvertRef.XAxis = Points3D[1] - ConvertRef.RefPoint;
188 ConvertRef.Normal = CrossProduct(ConvertRef.XAxis, (Points3D[2] - ConvertRef.RefPoint));
189 ConvertRef.YAxis = CrossProduct(ConvertRef.Normal, ConvertRef.XAxis);
190 ConvertRef.XAxis = Normalise(ConvertRef.XAxis);
191 ConvertRef.YAxis = Normalise(ConvertRef.YAxis);
192 ConvertRef.Normal = Normalise(ConvertRef.Normal);
193
194 Convert3DTo2DCoordinates(Points3D, ConvertRef, Points2D);
195 //}
196 return true;
197}
198
199void CMeshDomainPlane::Convert3DTo2DCoordinates(vector<XYZ>& Points3D, PLANEPARAMS& ConvertRef, vector<XY>& Points2D)
200{
201 vector<XYZ>::iterator itPoints3D;
202
203 for (itPoints3D = Points3D.begin(); itPoints3D != Points3D.end(); ++itPoints3D)
204 {
205 XYZ Relative = *itPoints3D - ConvertRef.RefPoint;
206 XY Point2D;
207 Point2D.x = DotProduct(ConvertRef.XAxis, Relative);
208 Point2D.y = DotProduct(ConvertRef.YAxis, Relative);
209 Points2D.push_back(Point2D);
210 }
211}
212
213void CMeshDomainPlane::OffsetMeshPoints(CMesh& Mesh, XYZ& Normal, double dDist)
214{
215 int iNumNodes = Mesh.GetNumNodes();
216 for (int i = 0; i < iNumNodes; ++i)
217 {
218 XYZ Point = Mesh.GetNode(i);
219 Point += Normal * dDist;
220 Mesh.SetNode(i, Point);
221 }
222}
223
224void CMeshDomainPlane::SeedSides(vector<XY>& Points)
225{
226 vector<XY> SeededSides;
227 vector<XY>::iterator itPoints;
228
229 XY StartPoint;
230 for (itPoints = Points.begin(); itPoints != Points.end(); )
231 {
232
233 if (itPoints == Points.begin())
234 {
235 StartPoint = *itPoints;
236 }
237 XY P1 = *(itPoints++);
238 XY P2;
239 if (itPoints != Points.end())
240 {
241 P2 = *itPoints;
242 }
243 else
244 {
245 P2 = StartPoint;
246 }
247 SeededSides.push_back(P1);
248 double dLength = GetLength(P1, P2);
249 int iNumSplits = int(floor(dLength / m_Seed));
250 for (int i = 1; i < iNumSplits; ++i)
251 {
252 double u = double(i) / double(iNumSplits);
253 XY Point = P1 + (P2 - P1)*u;
254 SeededSides.push_back(Point);
255 }
256 }
257 Points.clear();
258 Points.insert(Points.begin(), SeededSides.begin(), SeededSides.end());
259}
260
261bool CMeshDomainPlane::Triangulate(vector<vector<XY> > &PolygonPoints, vector<XY> &HolePoints, CMesh& OutputMesh, PLANEPARAMS& ConvertRef)
262{
263 // char szSwitches[128];
264 stringstream Switches;
265
266 double dMaxArea = 0.5*m_Seed*m_Seed;
267 double dMinAngle = 20;
268
269 // These are the switches to be used with triangle
270 // http://www-2.cs.cmu.edu/~quake/triangle.switch.html
271 // -p Triangulates a Planar Straight Line Graph (.poly file).
272 // -z Numbers all items starting from zero (rather than one).
273 // -n Outputs (to a .neigh file) a list of triangles neighboring each triangle.
274 // -P Suppresses the output .poly file.
275 // -B Suppresses boundary markers in the output .node, .poly, and .edge output files.
276 // -A Assigns a regional attribute to each triangle that identifies what segment-bounded region it belongs to.
277 // -Q Quiet: Suppresses all explanation of what Triangle is doing, unless an error occurs.
278 // -q Quality mesh generation with no angles smaller than 20 degrees. An alternate minimum angle may be specified after the `q'.
279 // -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.
280 // -Y Prohibits the insertion of Steiner points on the mesh boundary
281
282 /*#ifdef _DEBUG
283 sprintf(szSwitches, "pzAPBq%fa%f", dMinAngle, dMaxArea);
284 #else // _DEBUG
285 sprintf(szSwitches, "pzAQPBq%fa%f", dMinAngle, dMaxArea);
286 #endif // _DEBUG*/
287#ifndef _DEBUG
288 Switches << "Q";
289#endif
290 // Triangle has trouble parsing values given in scientific format so use fixed format with a
291 // rediculously high precision to get around the problem
292 Switches << "pzAPBq" << setiosflags(ios::fixed) << setprecision(20) << dMinAngle << "a" << dMaxArea;
293 Switches << "YY";
294
295 triangleio TriangleInput;
296 triangleio TriangleOutput;
297
298 context *ctx;
299 ctx = triangle_context_create();
300
301 triangle_context_options(ctx, (char*)Switches.str().c_str());
302
303 memset(&TriangleInput, 0, sizeof(TriangleInput));
304 memset(&TriangleOutput, 0, sizeof(TriangleOutput));
305
306 // Input Nodes
307 vector<vector<XY> >::iterator itArrayPolygonPoints;
308 int iNumPoints = 0;
309 for (itArrayPolygonPoints = PolygonPoints.begin(); itArrayPolygonPoints != PolygonPoints.end(); ++itArrayPolygonPoints)
310 {
311 iNumPoints += (int)(*itArrayPolygonPoints).size();
312 }
313 TriangleInput.pointlist = new REAL[iNumPoints * 2];
314 TriangleInput.numberofpoints = iNumPoints;
315
316 TriangleInput.segmentlist = new int[iNumPoints * 2];
317 TriangleInput.numberofsegments = iNumPoints;
318
319 int i = 0;
320 for (itArrayPolygonPoints = PolygonPoints.begin(); itArrayPolygonPoints != PolygonPoints.end(); ++itArrayPolygonPoints)
321 {
322 vector<XY>::iterator itPolyPoints;
323 int j = 0;
324 int iNumPolyPoints = (int)(*itArrayPolygonPoints).size();
325 int StartIndex = i;
326 for (itPolyPoints = (*itArrayPolygonPoints).begin(); itPolyPoints != (*itArrayPolygonPoints).end(); ++itPolyPoints)
327 {
328 // Input nodes
329 TriangleInput.pointlist[i * 2] = itPolyPoints->x;
330 TriangleInput.pointlist[i * 2 + 1] = itPolyPoints->y;
331
332 // Input Segments
333 TriangleInput.segmentlist[i * 2] = i;
334 if (j < iNumPolyPoints - 1)
335 {
336 TriangleInput.segmentlist[i * 2 + 1] = i + 1;
337 }
338 else
339 {
340 TriangleInput.segmentlist[i * 2 + 1] = StartIndex;
341 }
342
343 ++i;
344 ++j;
345 }
346 }
347
348 if (!m_bFillEnds)
349 {
350 // Input hole points
351 vector<XY>::iterator itHolePoints;
352 int iNumHoles = (int)HolePoints.size();
353 TriangleInput.holelist = new REAL[iNumHoles * 2];
354 TriangleInput.numberofholes = iNumHoles;
355 for (itHolePoints = HolePoints.begin(), i = 0; itHolePoints != HolePoints.end(); ++itHolePoints, ++i)
356 {
357 TriangleInput.holelist[i * 2] = itHolePoints->x;
358 TriangleInput.holelist[i * 2 + 1] = itHolePoints->y;
359 }
360 }
361
362 triangle_mesh_create(ctx, &TriangleInput);
363
364
365 delete[] TriangleInput.pointlist;
366 delete[] TriangleInput.segmentlist;
367 if (!m_bFillEnds)
368 delete[] TriangleInput.holelist;
369
370 triangle_mesh_copy(ctx, &TriangleOutput, 1, 1);
371
372 vector<XY> Points2D;
373 for (int i = 0; i<TriangleOutput.numberofpoints; ++i)
374 {
375 XY Point;
376 Point.x = TriangleOutput.pointlist[i * 2];
377 Point.y = TriangleOutput.pointlist[i * 2 + 1];
378 Points2D.push_back(Point);
379 }
380 vector<XYZ> Points3D;
381 vector<XYZ>::iterator itPoints3D;
382 Convert2DTo3DCoordinates(Points2D, Points3D, ConvertRef);
383
384 for (itPoints3D = Points3D.begin(); itPoints3D != Points3D.end(); ++itPoints3D)
385 {
386 OutputMesh.AddNode(*itPoints3D);
387 }
388
389 for (int i = 0; i<TriangleOutput.numberoftriangles; ++i)
390 {
391 OutputMesh.GetIndices(CMesh::TRI).push_back(TriangleOutput.trianglelist[i * 3]);
392 OutputMesh.GetIndices(CMesh::TRI).push_back(TriangleOutput.trianglelist[i * 3 + 1]);
393 OutputMesh.GetIndices(CMesh::TRI).push_back(TriangleOutput.trianglelist[i * 3 + 2]);
394 }
395
396 triangle_free(TriangleOutput.pointlist);
397 triangle_free(TriangleOutput.trianglelist);
398
399 triangle_context_destroy(ctx);
400 return true;
401}
402
403void CMeshDomainPlane::Convert2DTo3DCoordinates(vector<XY>& Points2D, vector<XYZ>& Points3D, PLANEPARAMS& ConvertRef)
404{
405 // Convert the 2D points back to the global 3d coordinate system
406 vector<XY>::iterator itPoints2D;
407
408 for (itPoints2D = Points2D.begin(); itPoints2D != Points2D.end(); ++itPoints2D)
409 {
410 XYZ Point3D;
411 Point3D = ConvertRef.XAxis * itPoints2D->x;
412 Point3D += ConvertRef.YAxis * itPoints2D->y;
413 // Translate the point to its global position
414 Point3D += ConvertRef.RefPoint;
415 // Add the new 3d point to the list
416 Points3D.push_back(Point3D);
417 }
418}
#define TGERROR(MESSAGE)
Macros used to report the file name and line number to the TexGenError and TexGenLog functions.
Definition: Logger.h:29
void Convert2DTo3DCoordinates(vector< XY > &Points2D, vector< XYZ > &Points3D, PLANEPARAMS &ConvertRef)
Convert local 2D coordinates to global 3D coordinates.
bool Triangulate(vector< vector< XY > > &PolygonPoints, vector< XY > &HolePoints, CMesh &OutputMesh, PLANEPARAMS &ConvertRef)
Triangulate the domain faces.
void Convert3DTo2DCoordinates(vector< XYZ > &Points3D, PLANEPARAMS &ConvertRef, vector< XY > &Points2D)
Convert global 3D coordinates to local 2D coordinates.
double m_Seed
Seed used for calculating boundary edge points.
void MeshDomainPlanes(bool bPeriodic)
void OffsetMeshPoints(CMesh &Mesh, XYZ &Normal, double dDist)
Offsets points in mesh by given distance in direction of normal.
vector< vector< int > > m_PolygonNumVertices
Number of polygon vertices on each face. Number of outer vector members = number of faces.
bool ConvertDomainPointsTo2D(const list< int > &Indices, CMesh &DomainMesh, int numNodes, vector< XY > &Points2D, PLANEPARAMS &ConvertRef)
Convert points on one domain surface to local 2D points.
void SeedSides(vector< XY > &Points)
Calculates seed points along domain edge.
bool m_bFillEnds
True if yarn areas are to be removed from domain.
vector< CMesh > m_DomainMeshes
Vector of meshes used to store domain plane meshes.
vector< CMesh > m_TriangulatedMeshes
Vector of triangulated domain plane meshes.
Defines the nodes and elements of a surface or volume mesh.
Definition: Mesh.h:58
int GetNumNodes() const
Return the number of nodes.
Definition: Mesh.cpp:2646
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
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
@ POLYGON
Definition: Mesh.h:76
void SetNode(int iIndex, XYZ Node)
Set the node at given index.
Definition: Mesh.cpp:2630
Namespace containing a series of customised math operations not found in the standard c++ library.
double GetLength(const XYZ &Point1, const XYZ &Point2)
Get the length between two points.
Definition: mymath.h:540
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
Structure which contains information for transformation from 3D to 2D plane.
Struct for representing points in 2D space.
Definition: mymath.h:103
double x
Definition: mymath.h:104
double y
Definition: mymath.h:104
Struct for representing points in 3D space.
Definition: mymath.h:56
double x
Definition: mymath.h:57
double y
Definition: mymath.h:57