RandomGridFiller.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. //
  2. // RandomGridFiller.cpp
  3. // Test
  4. //
  5. // Created by 高慕白 on 2024/12/3.
  6. //
  7. #include "RandomGridFiller.hpp"
  8. #include "BoostGeometryTools.hpp"
  9. #include <iostream>
  10. #include <algorithm>
  11. using std::cout;
  12. using std::endl;
  13. void RandomGridFiller::fill( vector<JewelBox>& jewelsArray , //需要填充的宝石
  14. float bottomLeftX,float bottomLeftY,//盘子有效区域
  15. float topRightX,float topRightY, //盘子有效区域
  16. vector<FillResult>& results //填充结果
  17. )
  18. {
  19. srand(_seed) ;
  20. results.clear() ;
  21. if( jewelsArray.size()==0 ) return ;
  22. float width = topRightX - bottomLeftX + 1;
  23. float height = topRightY - bottomLeftY+ 1;
  24. float minJewWid = jewelsArray[0]._width ;
  25. float minJewHei = jewelsArray[0]._height;
  26. for(auto it = jewelsArray.begin()+1;it!=jewelsArray.end();++it ) {
  27. minJewWid = fmin( minJewWid, it->_width ) ;
  28. minJewHei = fmin( minJewHei, it->_height) ;
  29. }
  30. float gridWid = width/20;
  31. float gridHei = height/20;
  32. int nrows = width / gridWid ;
  33. int ncols = height/ gridHei ;
  34. vector<ContourData::Point> potPositions ;
  35. for(int irow = 0 ; irow < nrows; ++ irow )
  36. {
  37. for(int icol=0;icol<ncols;++icol)
  38. {
  39. ContourData::Point cp ;
  40. cp.x = (icol+0.5) * gridWid ;
  41. cp.y = (irow+0.5) * gridHei ;
  42. potPositions.push_back(cp) ;
  43. }
  44. }
  45. const int TRY_CNT=5 ;
  46. bool chosen = false ;
  47. int chosenTotCoverage = 9999999 ;
  48. float chosenTotDist = 999999 ;
  49. for(int itry = 0 ;itry < TRY_CNT; ++ itry ) {
  50. vector<FillResult> fillResults ;
  51. float jewDistSum=0;
  52. int cov = 0 ;
  53. int badcnt = randomFillOneRound(jewelsArray, bottomLeftX, bottomLeftY, topRightX, topRightY, potPositions, fillResults, jewDistSum,gridWid,gridHei,cov);
  54. cout<<"itry-"<<itry<<" badcnt "<<badcnt<<" jewDistSum "<<jewDistSum<<" cov "<<cov <<endl;
  55. if( chosen==false ) {
  56. chosen = true ;
  57. chosenTotCoverage = cov ;
  58. chosenTotDist = jewDistSum ;
  59. results = fillResults ;
  60. }else{
  61. if( cov < chosenTotCoverage ) {
  62. chosenTotCoverage = cov ;
  63. chosenTotDist = jewDistSum ;
  64. results = fillResults ;
  65. }else if( cov == chosenTotCoverage ) {
  66. if( chosenTotDist < jewDistSum ) {
  67. chosenTotCoverage = cov ;
  68. chosenTotDist = jewDistSum ;
  69. results = fillResults ;
  70. }
  71. }
  72. }
  73. }
  74. }
  75. int RandomGridFiller::randomFillOneRound( vector<JewelBox>& jewelsArray,
  76. float bottomLeftX,float bottomLeftY,//盘子有效区域
  77. float topRightX,float topRightY, //盘子有效区域
  78. vector<ContourData::Point> potPositions, //潜在的点位
  79. vector<FillResult>& results,
  80. float& jewDistanceSum
  81. ,const float gridWid
  82. ,const float gridHei
  83. ,int& resultTotCov
  84. )
  85. {
  86. results.clear() ;
  87. vector<int> jewIndices ;
  88. for(int ij=0;ij<jewelsArray.size();++ij ) {
  89. for(int ic=0 ; ic < jewelsArray[ij]._cnt ; ++ ic ) {
  90. jewIndices.push_back(ij) ;
  91. }
  92. }
  93. //需要填充的数量
  94. int num2fill = jewIndices.size() ;
  95. //盘子的有效区域矩形边界
  96. BoostGeometryTools::BoostPolygon plateBox = BoostGeometryTools::makeRotatedBox(bottomLeftX, bottomLeftY, (topRightX-bottomLeftX+1), (topRightY-bottomLeftY), 0);
  97. //点位从外到内
  98. vector<DistancePosition> distPositions ;
  99. generateDistanceRandomSlotPositions((topRightX-bottomLeftX)/2, (topRightY-bottomLeftY)/2, potPositions, distPositions) ;
  100. //使用网格描述盘子点位占用情况
  101. const int GRID_X_NUM = (topRightX - bottomLeftX+1)/gridWid ;
  102. const int GRID_Y_NUM = (topRightY - bottomLeftY+1)/gridHei ;
  103. //每个宝石分配一个占用格点数据,用于标记该宝石在盘子中的痕迹
  104. vector< GridData > jewelGridDataArray(jewIndices.size()) ;
  105. for(int ij=0;ij<jewIndices.size();++ij ) {
  106. jewelGridDataArray[ij].reset(bottomLeftX+gridWid/2, bottomLeftY+gridHei/2, gridWid, gridHei, GRID_X_NUM, GRID_Y_NUM) ;
  107. }
  108. //随机打散宝石顺序
  109. vector<int> resultsJewIndices ;
  110. vector<int> randIndicesOfJewIndices = randIndices(jewIndices.size()) ;
  111. for(int ij = 0 ; ij < randIndicesOfJewIndices.size() ; ++ij ) {
  112. int randJewIndex = randIndicesOfJewIndices[ij] ;
  113. int jewTypeIndex = jewIndices[randIndicesOfJewIndices[ij]] ;
  114. JewelBox& jewBox = jewelsArray[jewTypeIndex] ;
  115. int rotdeg = rand()%360 ;
  116. BoostGeometryTools::BoostPolygon jewPoly = BoostGeometryTools::makeRotatedBox(
  117. -jewBox._width/2,
  118. -jewBox._height/2,
  119. jewBox._width,
  120. jewBox._height ,
  121. rotdeg) ;
  122. vector<FillResult> tempFillResults ;
  123. vector<int> sumOfTakenCountArray ;//保留占位数求和
  124. const int NUM_TRY = distPositions.size() ;//最大尝试数量
  125. int itry = 0 ;//尝试数量
  126. //将潜在点位按照距离盘子中心的距离进行排序,宝石按照这个距离进行填入盘子
  127. vector<int> positionIndices ;
  128. //逐个尝试,尝试NUM_TRY,保留占位数求和最少的。
  129. for(int ip = 0 ; ip < distPositions.size();++ ip ) {
  130. if( distPositions[ip]._isTaken ) continue ;//位置占用跳过
  131. ContourData::Point posi = distPositions[ip]._point ;
  132. BoostGeometryTools::BoostPolygon jpoly2 = BoostGeometryTools::translatePolygon(jewPoly, posi.x, posi.y) ;
  133. //是否落在盘子内部
  134. bool isJewWithinPlate = BoostGeometryTools::isAWithinB(jpoly2, plateBox) ;
  135. if( isJewWithinPlate ) {
  136. itry ++ ;
  137. FillResult fr ;
  138. fr._jewelTypeId = jewBox._jewelTypeId ;
  139. fr._rotdeg = rotdeg ;
  140. fr._x = posi.x ;
  141. fr._y = posi.y ;
  142. fr._width = jewBox._width ;
  143. fr._height = jewBox._height ;
  144. tempFillResults.push_back(fr) ;
  145. int tsum = totalCoverageGridCount(jpoly2, jewelGridDataArray) ;
  146. //cout<<"ij-"<<ij<<" itry-"<<itry<<" tsum "<<tsum<<endl;
  147. sumOfTakenCountArray.push_back(tsum) ;
  148. positionIndices.push_back(ip); //positionIndices.push_back(ip) ;
  149. if( itry == NUM_TRY ) break ;
  150. }
  151. }
  152. //保留占位数最少的。
  153. if( tempFillResults.size()>0 ) {
  154. FillResult fr = tempFillResults[0] ;
  155. int tsum = sumOfTakenCountArray[0] ;
  156. int posiIndex =positionIndices[0] ;
  157. for(int itry = 1 ; itry < tempFillResults.size();++ itry ) {
  158. if( tsum > sumOfTakenCountArray[itry]) {
  159. tsum = sumOfTakenCountArray[itry] ;
  160. fr = tempFillResults[itry] ;
  161. posiIndex =positionIndices[itry] ;
  162. }
  163. }
  164. distPositions[posiIndex]._isTaken = true ;//记录位置已经占用
  165. num2fill-- ;
  166. BoostGeometryTools::BoostPolygon jpoly2 = BoostGeometryTools::translatePolygon(jewPoly, fr._x, fr._y) ;
  167. setGridCoverageByBox(jpoly2, jewelGridDataArray[randJewIndex]);//写入子网格数据
  168. results.push_back(fr);//记录最优结果
  169. resultsJewIndices.push_back(randJewIndex) ;
  170. //debug
  171. //cout<<"ij-"<<ij<<" pos "<<fr._x<<","<<fr._y<< " cov " << tsum <<endl;
  172. }
  173. }
  174. //重新计算覆盖度(这个看上去好像解决不了什么问题)后面可以删掉这部分代码
  175. vector<int> coverageArray(results.size(),0 ) ;
  176. resultTotCov = 0;
  177. for(int ij = 0 ; ij < results.size();++ij ) {
  178. FillResult& fr = results[ij] ;
  179. int jewIndex = resultsJewIndices[ij] ;
  180. BoostGeometryTools::BoostPolygon jpoly = BoostGeometryTools::makeRotatedBox(-fr._width/2,-fr._height/2,
  181. fr._width,fr._height ,
  182. fr._rotdeg) ;
  183. BoostGeometryTools::BoostPolygon jpoly2 = BoostGeometryTools::translatePolygon(jpoly, fr._x, fr._y) ;
  184. coverageArray[jewIndex] = totalCoverageGridCount(jpoly2, jewelGridDataArray, jewIndex ) ;
  185. //cout<<"ji-"<<jewIndex<<" cov "<<coverageArray[jewIndex]<<" x,y "<< fr._x<<","<<fr._y <<endl;
  186. resultTotCov += coverageArray[jewIndex] ;
  187. }
  188. //计算宝石间距离求和
  189. jewDistanceSum = 0 ;
  190. for(int ifr = 0 ; ifr < results.size();++ ifr ) {
  191. for(int jfr = ifr+1; jfr < results.size();++jfr ) {
  192. float dx = results[ifr]._x - results[jfr]._x ;
  193. float dy = results[ifr]._y - results[jfr]._y ;
  194. jewDistanceSum += sqrtf( dx*dx+dy*dy ) ;
  195. }
  196. }
  197. return num2fill ;
  198. }
  199. //随机排序数组索引值
  200. vector<int> RandomGridFiller::randIndices(const int count0 ) {
  201. int count = count0 ;
  202. vector<int> indices ;
  203. unordered_map<int, bool> dict ;
  204. while( count > 0 ) {
  205. int r = rand()%count0;
  206. if( dict.find(r) == dict.end() ) {
  207. dict[r] = true ;
  208. indices.push_back(r) ;
  209. count-- ;
  210. }
  211. }
  212. return indices;
  213. }
  214. int RandomGridFiller::sumOfSubGridTakenCounts(const int x,const int y,const int radius,
  215. const float gridwid,const float gridhei,const vector<vector<int>>& rowColSubGrid)
  216. {
  217. int sum = 0 ;
  218. int gridXNum = rowColSubGrid[0].size() ;
  219. int gridYNum = rowColSubGrid.size() ;
  220. int centerGridX = x*1.0 / gridwid + 0.5 ;
  221. int centerGridY = y*1.0 / gridhei + 0.5 ;
  222. int gridRadiusX = radius/gridwid ;
  223. int gridRadiusY = radius/gridhei ;
  224. for( int igridy = -gridRadiusY; igridy <= gridRadiusY; ++ igridy ) {
  225. for(int igridx = -gridRadiusX; igridx <= gridRadiusX; ++ igridx ) {
  226. int gridx = centerGridX + igridx ;
  227. int gridy = centerGridY + igridy ;
  228. if( gridx>=0 && gridx < gridXNum && gridy>=0 && gridy < gridYNum ) {
  229. sum += rowColSubGrid[gridy][gridx] ;
  230. }
  231. }
  232. }
  233. return sum ;
  234. }
  235. void RandomGridFiller::subGridTakenCountsAddOne(const int x,const int y,const int radius,
  236. const float gridwid,const float gridhei,vector<vector<int>>& rowColSubGrid)
  237. {
  238. int gridXNum = rowColSubGrid[0].size() ;
  239. int gridYNum = rowColSubGrid.size() ;
  240. int centerGridX = x*1.0 / gridwid + 0.5 ;
  241. int centerGridY = y*1.0 / gridhei + 0.5 ;
  242. int gridRadiusX = radius/gridwid ;
  243. int gridRadiusY = radius/gridhei ;
  244. for( int igridy = -gridRadiusY; igridy <= gridRadiusY; ++ igridy ) {
  245. for(int igridx = -gridRadiusX; igridx <= gridRadiusX; ++ igridx ) {
  246. int gridx = centerGridX + igridx ;
  247. int gridy = centerGridY + igridy ;
  248. if( gridx>=0 && gridx < gridXNum && gridy>=0 && gridy < gridYNum ) {
  249. rowColSubGrid[gridy][gridx]+=1 ;
  250. }
  251. }
  252. }
  253. }
  254. //多边形盖住的格点数量
  255. int RandomGridFiller::coverageGridCount( BoostGeometryTools::BoostPolygon& box, const GridData& grid )
  256. {
  257. int sum = 0 ;
  258. int nrows = grid._rows.size();
  259. int ncols = 0;
  260. if( nrows>0 ) ncols = grid._rows[0].size();
  261. for(int irow = 0 ; irow < grid._rows.size();++ irow ) {
  262. for(int icol=0;icol<ncols;++icol ) {
  263. bool taken = grid._rows[irow][icol] ;
  264. if( taken ) {
  265. BoostGeometryTools::BoostPoint bp ;
  266. float x = grid._bottomLeftX + icol * grid._stepX ;
  267. float y = grid._bottomLeftY + irow * grid._stepY ;
  268. bp.set<0>(x);
  269. bp.set<1>(y) ;
  270. sum += BoostGeometryTools::pointIntersetsBox(bp, box)?1:0 ;
  271. }
  272. }
  273. }
  274. return sum ;
  275. }
  276. int RandomGridFiller::totalCoverageGridCount(BoostGeometryTools::BoostPolygon& box, const vector<GridData>& grids ,int excludeIndex)
  277. {
  278. int sum = 0 ;
  279. for(int ij=0;ij<grids.size();++ij ) {
  280. sum += (ij!=excludeIndex)?coverageGridCount(box, grids[ij]):0 ;
  281. }
  282. return sum ;
  283. }
  284. //将覆盖的格点数据赋值
  285. void RandomGridFiller::setGridCoverageByBox( BoostGeometryTools::BoostPolygon& box, GridData& grid )
  286. {
  287. int nrows = grid._rows.size();
  288. int ncols = 0;
  289. if( nrows>0 ) ncols = grid._rows[0].size();
  290. for(int irow = 0 ; irow < grid._rows.size();++ irow ) {
  291. for(int icol=0;icol<ncols;++icol ) {
  292. BoostGeometryTools::BoostPoint bp ;
  293. float x = grid._bottomLeftX + icol * grid._stepX ;
  294. float y = grid._bottomLeftY + irow * grid._stepY ;
  295. bp.set<0>(x);
  296. bp.set<1>(y) ;
  297. grid._rows[irow][icol] = BoostGeometryTools::pointIntersetsBox(bp, box) ;
  298. }
  299. }
  300. }
  301. void RandomGridFiller::generateDistanceRandomSlotPositions(const float centerx,const float centery,const vector<ContourData::Point>& points, vector<RandomGridFiller::DistancePosition>& results )
  302. {
  303. results.clear() ;
  304. vector<int> randindices = randIndices( points.size() ) ;
  305. for(int ir = 0 ; ir <randindices.size();++ir ) {
  306. ContourData::Point pt = points[randindices[ir]] ;
  307. float dx = pt.x - centerx ;
  308. float dy = pt.y - centery ;
  309. float dist = sqrtf(dx*dx + dy*dy) ;
  310. DistancePosition dp ;
  311. dp._distance = dist ;
  312. dp._isTaken = false ;
  313. dp._point = pt ;
  314. results.push_back(dp) ;
  315. }
  316. //排序
  317. std::sort(results.begin(), results.end(), [](DistancePosition& p1,DistancePosition& p2)->bool{return p1._distance>p2._distance; } );
  318. }