RandomGridFiller.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. //
  2. // RandomGridFiller.cpp
  3. // auto_fill_jewel_v3
  4. //
  5. // Created by Red on 2024/11/26.
  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. results.clear() ;
  20. if( jewelsArray.size()==0 ) return ;
  21. float width = topRightX - bottomLeftX + 1;
  22. float height = topRightY - bottomLeftY+ 1;
  23. float minJewWid = jewelsArray[0]._width ;
  24. float minJewHei = jewelsArray[0]._height;
  25. for(auto it = jewelsArray.begin()+1;it!=jewelsArray.end();++it ) {
  26. minJewWid = fmin( minJewWid, it->_width ) ;
  27. minJewHei = fmin( minJewHei, it->_height) ;
  28. }
  29. float gridWid = width/20;
  30. float gridHei = height/20;
  31. int nrows = width / gridWid ;
  32. int ncols = height/ gridHei ;
  33. vector<ContourData::Point> potPositions ;
  34. for(int irow = 0 ; irow < nrows; ++ irow )
  35. {
  36. for(int icol=0;icol<ncols;++icol)
  37. {
  38. ContourData::Point cp ;
  39. cp.x = (icol+0.5) * gridWid ;
  40. cp.y = (irow+0.5) * gridHei ;
  41. potPositions.push_back(cp) ;
  42. }
  43. }
  44. const int TRY_CNT=5 ;
  45. bool chosen = false ;
  46. int chosenTotCoverage = 9999999 ;
  47. float chosenTotDist = 999999 ;
  48. for(int itry = 0 ;itry < TRY_CNT; ++ itry ) {
  49. //cout<<"debug fill randomFillOneRound "<<itry<<endl;
  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(bottomLeftX+(topRightX-bottomLeftX)/2, bottomLeftY+(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. float plateCenterX = bottomLeftX+(topRightX-bottomLeftX)/2 ;
  123. float plateCenterY = bottomLeftY+(topRightY-bottomLeftY)/2 ;
  124. vector<FillResult> tempFillResults ;
  125. vector<int> sumOfTakenCountArray ;//保留占位数求和
  126. vector<float> dist2CenterArray ;
  127. const int NUM_TRY = distPositions.size() ;//最大尝试数量
  128. int itry = 0 ;//尝试数量
  129. //将潜在点位按照距离盘子中心的距离进行排序,宝石按照这个距离进行填入盘子
  130. vector<int> positionIndices ;
  131. //逐个尝试,尝试NUM_TRY,保留占位数求和最少的。
  132. for(int ip = 0 ; ip < distPositions.size();++ ip ) {
  133. if( distPositions[ip]._isTaken ) continue ;//位置占用跳过
  134. ContourData::Point posi = distPositions[ip]._point ;
  135. BoostGeometryTools::BoostPolygon jpoly2 = BoostGeometryTools::translatePolygon(jewPoly, posi.x, posi.y) ;
  136. //是否落在盘子内部
  137. bool isJewWithinPlate = BoostGeometryTools::isAWithinB(jpoly2, plateBox) ;
  138. if( isJewWithinPlate ) {
  139. float dist1 = sqrt( (posi.x-plateCenterX)*(posi.x-plateCenterX)+(posi.y-plateCenterY)*(posi.y-plateCenterY) );
  140. itry ++ ;
  141. FillResult fr ;
  142. fr._jewelTypeId = jewBox._jewelTypeId ;
  143. fr._rotdeg = rotdeg ;
  144. fr._x = posi.x ;
  145. fr._y = posi.y ;
  146. fr._width = jewBox._width ;
  147. fr._height = jewBox._height ;
  148. tempFillResults.push_back(fr) ;
  149. int tsum = totalCoverageGridCount(jpoly2, jewelGridDataArray) ;
  150. //cout<<"ij-"<<ij<<" itry-"<<itry<<" tsum "<<tsum<<endl;
  151. sumOfTakenCountArray.push_back(tsum) ;
  152. dist2CenterArray.push_back(dist1);
  153. positionIndices.push_back(ip); //positionIndices.push_back(ip) ;
  154. if( itry == NUM_TRY ) break ;
  155. }
  156. }
  157. //保留占位数最少的。
  158. if( tempFillResults.size()>0 ) {
  159. FillResult fr = tempFillResults[0] ;
  160. int tsum = sumOfTakenCountArray[0] ;
  161. int posiIndex =positionIndices[0] ;
  162. float tdist = dist2CenterArray[0] ;
  163. for(int itry1 = 0 ; itry1 < tempFillResults.size();++ itry1 ) {
  164. if( tsum > sumOfTakenCountArray[itry1]) {
  165. tsum = sumOfTakenCountArray[itry1] ;
  166. tdist = dist2CenterArray[itry1];
  167. fr = tempFillResults[itry1] ;
  168. posiIndex =positionIndices[itry1] ;
  169. }else if( tsum == sumOfTakenCountArray[itry1] && tdist < dist2CenterArray[itry1] ) {
  170. tsum = sumOfTakenCountArray[itry1] ;
  171. tdist = dist2CenterArray[itry1];
  172. fr = tempFillResults[itry1] ;
  173. posiIndex =positionIndices[itry1] ;
  174. }
  175. }
  176. distPositions[posiIndex]._isTaken = true ;//记录位置已经占用
  177. num2fill-- ;
  178. BoostGeometryTools::BoostPolygon jpoly2 = BoostGeometryTools::translatePolygon(jewPoly, fr._x, fr._y) ;
  179. setGridCoverageByBox(jpoly2, jewelGridDataArray[randJewIndex]);//写入子网格数据
  180. results.push_back(fr);//记录最优结果
  181. resultsJewIndices.push_back(randJewIndex) ;
  182. }
  183. }
  184. //重新计算覆盖度(这个看上去好像解决不了什么问题)后面可以删掉这部分代码
  185. vector<int> coverageArray(results.size(),0 ) ;
  186. resultTotCov = 0;
  187. for(int ij = 0 ; ij < results.size();++ij ) {
  188. FillResult& fr = results[ij] ;
  189. int jewIndex = resultsJewIndices[ij] ;
  190. BoostGeometryTools::BoostPolygon jpoly = BoostGeometryTools::makeRotatedBox(-fr._width/2,-fr._height/2,
  191. fr._width,fr._height ,
  192. fr._rotdeg) ;
  193. BoostGeometryTools::BoostPolygon jpoly2 = BoostGeometryTools::translatePolygon(jpoly, fr._x, fr._y) ;
  194. coverageArray[jewIndex] = totalCoverageGridCount(jpoly2, jewelGridDataArray, jewIndex ) ;
  195. //cout<<"ji-"<<jewIndex<<" cov "<<coverageArray[jewIndex]<<" x,y "<< fr._x<<","<<fr._y <<endl;
  196. resultTotCov += coverageArray[jewIndex] ;
  197. }
  198. //计算宝石间距离求和
  199. jewDistanceSum = 0 ;
  200. for(int ifr = 0 ; ifr < results.size();++ ifr ) {
  201. for(int jfr = ifr+1; jfr < results.size();++jfr ) {
  202. float dx = results[ifr]._x - results[jfr]._x ;
  203. float dy = results[ifr]._y - results[jfr]._y ;
  204. jewDistanceSum += sqrtf( dx*dx+dy*dy ) ;
  205. }
  206. }
  207. return num2fill ;
  208. }
  209. //随机排序数组索引值
  210. vector<int> RandomGridFiller::randIndices(const int count0 ) {
  211. int count = count0 ;
  212. vector<int> indices ;
  213. unordered_map<int, bool> dict ;
  214. while( count > 0 ) {
  215. int r = rand()%count0;
  216. if( dict.find(r) == dict.end() ) {
  217. dict[r] = true ;
  218. indices.push_back(r) ;
  219. count-- ;
  220. }
  221. }
  222. return indices;
  223. }
  224. int RandomGridFiller::sumOfSubGridTakenCounts(const int x,const int y,const int radius,
  225. const float gridwid,const float gridhei,const vector<vector<int>>& rowColSubGrid)
  226. {
  227. int sum = 0 ;
  228. int gridXNum = rowColSubGrid[0].size() ;
  229. int gridYNum = rowColSubGrid.size() ;
  230. int centerGridX = x*1.0 / gridwid + 0.5 ;
  231. int centerGridY = y*1.0 / gridhei + 0.5 ;
  232. int gridRadiusX = radius/gridwid ;
  233. int gridRadiusY = radius/gridhei ;
  234. for( int igridy = -gridRadiusY; igridy <= gridRadiusY; ++ igridy ) {
  235. for(int igridx = -gridRadiusX; igridx <= gridRadiusX; ++ igridx ) {
  236. int gridx = centerGridX + igridx ;
  237. int gridy = centerGridY + igridy ;
  238. if( gridx>=0 && gridx < gridXNum && gridy>=0 && gridy < gridYNum ) {
  239. sum += rowColSubGrid[gridy][gridx] ;
  240. }
  241. }
  242. }
  243. return sum ;
  244. }
  245. void RandomGridFiller::subGridTakenCountsAddOne(const int x,const int y,const int radius,
  246. const float gridwid,const float gridhei,vector<vector<int>>& rowColSubGrid)
  247. {
  248. int gridXNum = rowColSubGrid[0].size() ;
  249. int gridYNum = rowColSubGrid.size() ;
  250. int centerGridX = x*1.0 / gridwid + 0.5 ;
  251. int centerGridY = y*1.0 / gridhei + 0.5 ;
  252. int gridRadiusX = radius/gridwid ;
  253. int gridRadiusY = radius/gridhei ;
  254. for( int igridy = -gridRadiusY; igridy <= gridRadiusY; ++ igridy ) {
  255. for(int igridx = -gridRadiusX; igridx <= gridRadiusX; ++ igridx ) {
  256. int gridx = centerGridX + igridx ;
  257. int gridy = centerGridY + igridy ;
  258. if( gridx>=0 && gridx < gridXNum && gridy>=0 && gridy < gridYNum ) {
  259. rowColSubGrid[gridy][gridx]+=1 ;
  260. }
  261. }
  262. }
  263. }
  264. //多边形盖住的格点数量
  265. int RandomGridFiller::coverageGridCount( BoostGeometryTools::BoostPolygon& box, const GridData& grid )
  266. {
  267. int sum = 0 ;
  268. int nrows = grid._rows.size();
  269. int ncols = 0;
  270. if( nrows>0 ) ncols = grid._rows[0].size();
  271. for(int irow = 0 ; irow < grid._rows.size();++ irow ) {
  272. for(int icol=0;icol<ncols;++icol ) {
  273. bool taken = grid._rows[irow][icol] ;
  274. if( taken ) {
  275. BoostGeometryTools::BoostPoint bp ;
  276. float x = grid._bottomLeftX + icol * grid._stepX ;
  277. float y = grid._bottomLeftY + irow * grid._stepY ;
  278. bp.set<0>(x);
  279. bp.set<1>(y) ;
  280. sum += BoostGeometryTools::pointIntersetsBox(bp, box)?1:0 ;
  281. }
  282. }
  283. }
  284. return sum ;
  285. }
  286. int RandomGridFiller::totalCoverageGridCount(BoostGeometryTools::BoostPolygon& box, const vector<GridData>& grids ,int excludeIndex)
  287. {
  288. int sum = 0 ;
  289. for(int ij=0;ij<grids.size();++ij ) {
  290. sum += (ij!=excludeIndex)?coverageGridCount(box, grids[ij]):0 ;
  291. }
  292. return sum ;
  293. }
  294. //将覆盖的格点数据赋值
  295. void RandomGridFiller::setGridCoverageByBox( BoostGeometryTools::BoostPolygon& box, GridData& grid )
  296. {
  297. int nrows = grid._rows.size();
  298. int ncols = 0;
  299. if( nrows>0 ) ncols = grid._rows[0].size();
  300. for(int irow = 0 ; irow < grid._rows.size();++ irow ) {
  301. for(int icol=0;icol<ncols;++icol ) {
  302. BoostGeometryTools::BoostPoint bp ;
  303. float x = grid._bottomLeftX + icol * grid._stepX ;
  304. float y = grid._bottomLeftY + irow * grid._stepY ;
  305. bp.set<0>(x);
  306. bp.set<1>(y) ;
  307. grid._rows[irow][icol] = BoostGeometryTools::pointIntersetsBox(bp, box) ;
  308. }
  309. }
  310. }
  311. void RandomGridFiller::generateDistanceRandomSlotPositions(const float centerx,const float centery,const vector<ContourData::Point>& points, vector<RandomGridFiller::DistancePosition>& results )
  312. {
  313. results.clear() ;
  314. vector<int> randindices = randIndices( points.size() ) ;
  315. for(int ir = 0 ; ir <randindices.size();++ir ) {
  316. ContourData::Point pt = points[randindices[ir]] ;
  317. float dx = pt.x - centerx ;
  318. float dy = pt.y - centery ;
  319. float dist = sqrtf(dx*dx + dy*dy) ;
  320. DistancePosition dp ;
  321. dp._distance = dist ;
  322. dp._isTaken = false ;
  323. dp._point = pt ;
  324. results.push_back(dp) ;
  325. }
  326. //排序
  327. std::sort(results.begin(), results.end(), [](DistancePosition& p1,DistancePosition& p2)->bool{return p1._distance>p2._distance; } );
  328. }