PageRenderTime 379ms CodeModel.GetById 50ms RepoModel.GetById 0ms app.codeStats 0ms

/src/MultiPoly.cpp

https://github.com/fodrek/heekscad
C++ | 407 lines | 289 code | 58 blank | 60 comment | 54 complexity | 8380a183e28e70287c2c609960c417c6 MD5 | raw file
  1. // MultiPoly.cpp
  2. // Copyright (c) 2009, Dan Heeks
  3. // This program is released under the BSD license. See the file COPYING for details.
  4. #include "stdafx.h"
  5. #include "Sketch.h"
  6. #include "EndedObject.h"
  7. #include "BentleyOttmann.h"
  8. #include "SimpleIntersector.h"
  9. #include "MultiPoly.h"
  10. #include "HArc.h"
  11. //This algorithm takes an array of complex sketches (CSketch* constaining multiple closed paths)
  12. //And creates a new set of paths that are no longer self intersecting
  13. //these lists are returns an array of trees of objects
  14. //each level of the trees corresponds to whether or not the objects should be added or removed
  15. //
  16. //all sketches must only contain closed shapes
  17. std::vector<FastCurve*> shapes;
  18. extern double tol;
  19. std::vector<TopoDS_Face> MultiPoly(std::list<CSketch*> sketches)
  20. {
  21. tol = wxGetApp().m_geom_tol;
  22. shapes.clear();
  23. //first pass: build lists of closed shapes
  24. std::list<CSketch*>::iterator it;
  25. for(it = sketches.begin(); it!= sketches.end(); ++it)
  26. {
  27. CSketch* sketch = *it;
  28. //Copy this sketches objects into a new list
  29. std::list<EndedObject*> sketchobjs;
  30. HeeksObj* obj = sketch->GetFirstChild();
  31. while(obj)
  32. {
  33. //TODO: for now we only handle EndedObject
  34. EndedObject* eobj = dynamic_cast<EndedObject*>(obj);
  35. if(eobj)
  36. {
  37. HArc* arc = dynamic_cast<HArc*>(obj);
  38. if(arc)
  39. {
  40. shapes.push_back(new FastArc(arc->A->m_p,arc->B->m_p,arc->C->m_p,arc->m_axis.Direction().Z() > 0, arc->GetCircle()));
  41. }
  42. else
  43. shapes.push_back(new FastLine(eobj->A->m_p,eobj->B->m_p));
  44. }
  45. obj = sketch->GetNextChild();
  46. }
  47. }
  48. //Get a list of all the intersection points
  49. Intersector *m_int = new SimpleIntersector();
  50. std::map<FastCurve*, std::vector<Intersection> > intersections = m_int->Intersect(shapes);
  51. //Make our flashy double hashmap
  52. TwoDNearMap bcurves(tol);
  53. //Create a new list of bounded segment objects. Whose endpoints are locatable via hash
  54. //with the exception that the hash returns values within tolerance of the specified point
  55. std::map<FastCurve*, std::vector<Intersection> >::iterator it2;
  56. for(it2 = intersections.begin(); it2 != intersections.end(); ++it2)
  57. {
  58. FastCurve *tline = (*it2).first;
  59. std::vector<Intersection> inter = (*it2).second;
  60. for(unsigned i=0; i < inter.size()-1; i++)
  61. {
  62. double startu=tline->GetU(inter[i].X,inter[i].Y);
  63. double newu=tline->GetU(inter[i+1].X,inter[i+1].Y);
  64. CompoundSegment* segment;
  65. if(startu < newu)
  66. segment = new CompoundSegment(tline,tol,startu,newu);
  67. else
  68. segment = new CompoundSegment(tline,tol,newu,startu);
  69. bcurves.insert(inter[i].X,inter[i].Y,segment);
  70. bcurves.insert(inter[i+1].X,inter[i+1].Y,segment);
  71. startu = newu;
  72. }
  73. }
  74. //This gets the hashtable working
  75. bcurves.sort();
  76. AnalyzeNearMap(bcurves);
  77. std::vector<CompoundSegment*> closed_shapes;
  78. //Create a new tree of boundedcurves, that is much smaller. follow all chains and attempt to remove
  79. //segments that are connected to only 2 other curves. This will yield a non-orientable graph
  80. //so our definition of polygons better be very graph theoretical
  81. std::vector<void*> returnvec;
  82. for(int i=0; i < bcurves.GetVecCount(); i++)
  83. {
  84. OneDNearMap* ptMap = bcurves.GetElement(i);
  85. double x_coord = bcurves.GetCoord(i);
  86. for(int j=0; j < ptMap->GetVecCount(); j++)
  87. {
  88. double y_coord = ptMap->GetCoord(j);
  89. if(!ptMap->IsValid(j))
  90. continue;
  91. returnvec.clear();
  92. bcurves.find(x_coord,y_coord,returnvec);
  93. if(returnvec.size() == 1)
  94. {
  95. //TODO: this means the current segment is part of an unclosed shape. However it is not clear that
  96. //this shape is fully concatenated or does not intersect a closed shape. these should probably be removed
  97. //prior to this loop
  98. continue;
  99. }
  100. if(returnvec.size() != 2)
  101. continue;
  102. //Concatenate the 2 groups and remove *it4 from the map
  103. CompoundSegment* seg1 = (CompoundSegment*)returnvec[0];
  104. CompoundSegment* seg2 = (CompoundSegment*)returnvec[1];
  105. if(seg1 == seg2)
  106. {
  107. //this means we have found a closed shape. Remove it from the bcurves and add it to a list of closed shapes
  108. //remove from the map
  109. bcurves.remove(x_coord,y_coord,seg1);
  110. bcurves.remove(x_coord,y_coord,seg2);
  111. closed_shapes.push_back(seg1);
  112. continue;
  113. }
  114. ConcatSegments(x_coord,y_coord,seg1,seg2,bcurves);
  115. }
  116. }
  117. //Now we have a graph of CompoundSegment*. These should be fast to traverse.
  118. //Non self intersecting shapes are already in closed_shapes
  119. //We could speed this up by regenerating near_map to get rid of removed references
  120. bool done=false;
  121. while(!done)
  122. {
  123. bool found=false;
  124. for(int i=0; i < bcurves.GetVecCount(); i++)
  125. {
  126. OneDNearMap* ptMap = bcurves.GetElement(i);
  127. double x_coord = bcurves.GetCoord(i);
  128. for(int j=0; j < ptMap->GetVecCount(); j++)
  129. {
  130. double y_coord = ptMap->GetCoord(j);
  131. if(!ptMap->IsValid(j))
  132. continue;
  133. returnvec.clear();
  134. bcurves.find(x_coord,y_coord,returnvec);
  135. //for most cases, returnvec should have 4 elements. more complicated cases have an even number > 4
  136. //check for elements that are the same, which mean this polygon could terminate here, so terminate it
  137. //and merge the other CompoundSegments if there are only two remaining
  138. int nfound=0;
  139. for(size_t k=0; k < returnvec.size(); k++)
  140. {
  141. for(size_t l=k+1; l < returnvec.size(); l++)
  142. {
  143. if(returnvec[k] == returnvec[l])
  144. {
  145. //this means we have found a closed shape. Remove it from the bcurves and add it to a list of closed shapes
  146. //remove from the map
  147. bcurves.remove(x_coord,y_coord,returnvec[k]);
  148. bcurves.remove(x_coord,y_coord,returnvec[l]);
  149. closed_shapes.push_back((CompoundSegment*)returnvec[k]);
  150. nfound+=2;
  151. found=true;
  152. }
  153. }
  154. }
  155. //now we know that no 2 elements in returnvec are equal.
  156. //make sure there are the right number of elements for our next op
  157. if(returnvec.size() - nfound != 2)
  158. continue;
  159. //Quick and dirty way to get the pointers
  160. returnvec.clear();
  161. bcurves.find(x_coord,y_coord,returnvec);
  162. //Merge the segments and get them out of this coordinate
  163. ConcatSegments(x_coord,y_coord,(CompoundSegment*)returnvec[0],(CompoundSegment*)returnvec[1],bcurves);
  164. found=true;
  165. }
  166. }
  167. if(!found)
  168. done = true;
  169. }
  170. //Now that we have all closed shapes, we need to define the relationships. Since we know that they are not intersecting
  171. //3 kinds of things can happen. A shape is either inside, enclosing, adjacent, or unrelated to another.
  172. std::vector<std::vector<CompoundSegment*> > inside_of;
  173. inside_of.resize(closed_shapes.size());
  174. for(unsigned i=0; i < closed_shapes.size(); i++)
  175. {
  176. for(unsigned j=0; j < closed_shapes.size(); j++)
  177. {
  178. //We can determine if a shape is inside or outside by finding the winding number of just 1 point with the
  179. //entire other polygon
  180. if(i==j)
  181. continue;
  182. int rays = closed_shapes[i]->GetRayIntersectionCount(closed_shapes[j]->Begin());
  183. if(rays%2)
  184. {
  185. //Polygon J is inside of polygon I
  186. inside_of[j].push_back(closed_shapes[i]);
  187. int x=0;
  188. x++;
  189. }
  190. }
  191. }
  192. //This is used to reverse the ordering of a closed shape. Getting it to be CW or CCW
  193. //OCC is picky about the ordering when the polygon has holes
  194. for(unsigned i=0; i < closed_shapes.size(); i++)
  195. {
  196. closed_shapes[i]->Order();
  197. #ifdef FORCEPOLYGONORDERING
  198. bool cw = closed_shapes[i]->GetCW();
  199. if(!cw)
  200. {
  201. closed_shapes[i]->Reverse();
  202. closed_shapes[i]->Order();
  203. }
  204. #endif
  205. }
  206. //Sort these lists for easy comparison
  207. for(unsigned i=0; i < inside_of.size(); i++)
  208. {
  209. std::sort(inside_of[i].begin(),inside_of[i].end());
  210. }
  211. //Now we want to descend into this thing. First find all shapes that are inside of nothing else
  212. std::vector<std::pair<CompoundSegment*,std::vector<CompoundSegment*> > > gold;
  213. find_level(true,gold,closed_shapes,inside_of,std::vector<CompoundSegment*>());
  214. return TopoDSFaceAdaptor(gold);
  215. }
  216. //This is a recursive function that will analyze the graph for islands and such
  217. //It could be sped up by removing elements from closed_shapes and inside_of as the get consumed
  218. std::vector<CompoundSegment*> find_level(bool odd,
  219. std::vector<std::pair<CompoundSegment*,std::vector<CompoundSegment*> > > &pRet,
  220. std::vector<CompoundSegment*>& closed_shapes,
  221. std::vector<std::vector<CompoundSegment*> >& inside_of,
  222. std::vector<CompoundSegment*> parents)
  223. {
  224. std::vector<CompoundSegment*> retValue;
  225. for(unsigned i=0; i < closed_shapes.size(); i++)
  226. {
  227. if(inside_of[i].size() != parents.size())
  228. continue;
  229. bool no_match = false;
  230. for(unsigned j=0; j < inside_of[i].size(); j++)
  231. {
  232. if(inside_of[i][j] != parents[j])
  233. {
  234. no_match = true;
  235. break;
  236. }
  237. }
  238. if(no_match)
  239. continue;
  240. //this closed shape is good to go
  241. std::vector<CompoundSegment*> nparents = parents;
  242. nparents.push_back(closed_shapes[i]);
  243. std::sort(nparents.begin(), nparents.end());
  244. std::vector<CompoundSegment*> deletions = find_level(!odd,pRet,closed_shapes,inside_of,nparents);
  245. if(odd)
  246. {
  247. pRet.push_back(std::pair<CompoundSegment*,std::vector<CompoundSegment*> >(closed_shapes[i],deletions));
  248. }
  249. else
  250. retValue.push_back(closed_shapes[i]);
  251. }
  252. return retValue;
  253. }
  254. void ConcatSegments(double x_coord, double y_coord, CompoundSegment* seg1, CompoundSegment* seg2, TwoDNearMap &bcurves)
  255. {
  256. seg1->Add(seg2,x_coord,y_coord);
  257. //Must find the pointer at the end of seg2 and change it
  258. gp_Pnt begin = seg2->Begin();
  259. gp_Pnt end = seg2->End();
  260. if(MyIsEqual(begin.X(),x_coord) && MyIsEqual(begin.Y(),y_coord))
  261. bcurves.remap(end.X(),end.Y(),seg2,seg1);
  262. else
  263. {
  264. if(MyIsEqual(end.X(),x_coord) && MyIsEqual(end.Y(),y_coord))
  265. bcurves.remap(begin.X(),begin.Y(),seg2,seg1);
  266. else
  267. {
  268. //kaboom
  269. int x=0;
  270. x++;
  271. }
  272. }
  273. //remove from the map
  274. bcurves.remove(x_coord,y_coord,seg1);
  275. bcurves.remove(x_coord,y_coord,seg2);
  276. //delete seg2;
  277. #ifdef TESTORDERING
  278. seg1->Order();
  279. #endif
  280. }
  281. TopoDS_Wire TopoDSWireAdaptor(CompoundSegment* poly, bool inside)
  282. {
  283. std::list<TopoDS_Edge> edges;
  284. //if(inside)
  285. //poly->();
  286. poly->GetEdges(edges);
  287. BRepLib_MakeWire wire_maker;
  288. std::list<TopoDS_Edge>::iterator It;
  289. for(It = edges.begin(); It != edges.end(); It++)
  290. {
  291. TopoDS_Edge &edge = *It;
  292. // BRepLib_BuildCurve3d( edge );
  293. wire_maker.Add(edge);
  294. }
  295. TopoDS_Wire wire = wire_maker.Wire();
  296. // if(inside)
  297. // wire = TopoDS::Wire(wire.Oriented(TopAbs_REVERSED));
  298. // else
  299. wire = TopoDS::Wire(wire.Oriented(TopAbs_FORWARD));
  300. return wire;
  301. }
  302. std::vector<std::pair<TopoDS_Wire,std::vector<TopoDS_Wire> > > TopoDSWireAdaptor(
  303. std::vector<std::pair<CompoundSegment*,std::vector<CompoundSegment*> > > &data)
  304. {
  305. std::vector<std::pair<TopoDS_Wire,std::vector<TopoDS_Wire> > > ret;
  306. for(unsigned i=0; i < data.size(); i++)
  307. {
  308. TopoDS_Wire outside = TopoDSWireAdaptor(data[i].first,false);
  309. std::vector<TopoDS_Wire> insides;
  310. for(unsigned j=0; j < data[i].second.size(); j++)
  311. insides.push_back(TopoDSWireAdaptor(data[i].second[j],true));
  312. ret.push_back(std::pair<TopoDS_Wire,std::vector<TopoDS_Wire> >(outside,insides));
  313. }
  314. return ret;
  315. }
  316. std::vector<TopoDS_Face> TopoDSFaceAdaptor(
  317. std::vector<std::pair<CompoundSegment*,std::vector<CompoundSegment*> > > &data)
  318. {
  319. std::vector<TopoDS_Face> faces;
  320. std::vector<std::pair<TopoDS_Wire,std::vector<TopoDS_Wire> > > wires;
  321. wires = TopoDSWireAdaptor(data);
  322. for(unsigned i=0; i < wires.size(); i++)
  323. {
  324. BRepBuilderAPI_MakeFace makeFace(wires[i].first);
  325. for(unsigned j=0; j < wires[i].second.size(); j++)
  326. makeFace.Add(wires[i].second[j]);
  327. faces.push_back(makeFace.Face());
  328. }
  329. return faces;
  330. }
  331. void AnalyzeNearMap(TwoDNearMap &bcurves)
  332. {
  333. std::vector<void*> returnvec;
  334. for(int i=0; i < bcurves.GetVecCount(); i++)
  335. {
  336. OneDNearMap* ptMap = bcurves.GetElement(i);
  337. double x_coord = bcurves.GetCoord(i);
  338. for(int j=0; j < ptMap->GetVecCount(); j++)
  339. {
  340. double y_coord = ptMap->GetCoord(j);
  341. if(!ptMap->IsValid(j))
  342. continue;
  343. returnvec.clear();
  344. bcurves.find(x_coord,y_coord,returnvec);
  345. int x=0;
  346. x++;
  347. }
  348. }
  349. }