Sfoglia il codice sorgente

improve the mergebigboundaryparticles method. improve the compositiondistributionGrid module.

GSP 1 mese fa
parent
commit
a1d34b38cc

+ 284 - 280
OTSCPP/OTSImagePro/OTSImageProcess.cpp

@@ -11,6 +11,7 @@
 #include "FieldMgr.h"
 #include "BaseFunction.h"
 #include "GreyPeak.h"
+#include <unordered_map>
 
 
 namespace OTSIMGPROC
@@ -1337,6 +1338,8 @@ namespace OTSIMGPROC
 			{
 				Rect rectMax = Rect(x, y, w, h);
 
+
+
 				Mat  rectROI = labels(rectMax).clone();
 				Mat imageROI = Mat::zeros(rectMax.size(), nobgMat.type());
 
@@ -1555,6 +1558,7 @@ namespace OTSIMGPROC
 
 
 
+
 		CRect r = CRect(0, 0, nWidthImg, nHeightImg);
 
 		CBSEImgPtr imgNoBGBinary = CBSEImgPtr(new CBSEImg(r));
@@ -1664,7 +1668,7 @@ namespace OTSIMGPROC
 		return true;
 	}
 	
-	 BOOL COTSImageProcess::CalcuParticleImagePropertes(COTSParticlePtr a_pOTSPart, double a_PixelSize)
+	BOOL COTSImageProcess::CalcuParticleImagePropertes(COTSParticlePtr a_pOTSPart, double a_PixelSize)
 	{	
 		//--------- convert this particle data to image data,construct an image only with this particle.------
 		const int nExpand_Size = 4;
@@ -1987,368 +1991,368 @@ namespace OTSIMGPROC
 
 	BOOL COTSImageProcess::MergeBigBoundaryParticles(COTSFieldDataList allFields,double pixelSize,int scanFieldSize, CSize ResolutionSize, COTSParticleList& mergedParts)
 	{
-		class BorderPart
+		// Lightweight wrapper used only in this function (no global/static map).
+		struct BorderPart
+		{
+			explicit BorderPart(const COTSParticlePtr& p) : myPart(p) {}
+			COTSParticlePtr LockMyPart() const { return myPart; }
+			COTSParticlePtr LockHeaderParticle() const { return headerParticle.lock(); }
+
+			COTSParticlePtr myPart;
+			std::weak_ptr<COTSParticle> headerParticle; // points to representative particle if merged
+		};
+
+		// Map lives only for this invocation -> no leaked persistent state between calls.
+		std::unordered_map<COTSParticle*, std::shared_ptr<BorderPart>> allborderPart;
+		allborderPart.reserve(1024);
+
+			// Helper: convert COTSParticleList -> vector<BorderPart shared_ptr>
+	auto ConvertPartToBorderPart = [&](const COTSParticleList& parts) -> std::vector<std::shared_ptr<BorderPart>>
+	{
+		std::vector<std::shared_ptr<BorderPart>> borderParts;
+		borderParts.reserve(parts.size());
+		for (auto p : parts)
 		{
-			typedef std::shared_ptr<BorderPart>  CBorderPartPtr;
-			BorderPart(COTSParticlePtr p)
+			COTSParticle* raw = p.get();
+			auto it = allborderPart.find(raw);
+			if (it == allborderPart.end())
 			{
-				myPart = p;
-				headerParticle = NULL;
+				auto bp = std::make_shared<BorderPart>(p);
+				borderParts.push_back(bp);
+				allborderPart[raw] = bp;
 			}
-		public:
-			COTSParticlePtr myPart;
-			COTSParticle* headerParticle;//used to merge particles ,if this particle has been merged then this pointer will point to the first particle of these merged particles or else it's NULL.
-		
-			
-			static std::vector<CBorderPartPtr> ConvertPartToBorderPart(COTSParticleList parts)
+			else
 			{
-				static std::map<COTSParticle*, CBorderPartPtr> allborderPart;
-				std::vector<CBorderPartPtr> borderParts;
-
-				for (auto p : parts)
-				{
-					if (allborderPart.find(p.get()) == allborderPart.end())
-					{
-						auto borderp = CBorderPartPtr(new BorderPart(p));
-						borderParts.push_back(borderp);
-						allborderPart[p.get()] = borderp;
-					}
-					else
-					{
-						borderParts.push_back(allborderPart[p.get()]);
-					}
-					
-				}
-				return borderParts;
-			
+				borderParts.push_back(it->second);
 			}
+		}
+		return borderParts;
+	};
 
-		};
+	// Helper: get header particle pointer from raw COTSParticle*
+	auto GetHeaderParticlePtr = [&](COTSParticle* raw) -> COTSParticlePtr
+	{
+		if (!raw) return nullptr;
+		auto it = allborderPart.find(raw);
+		if (it == allborderPart.end()) return nullptr;
+		return it->second->LockHeaderParticle();
+	};
 
-		auto FldMgr = new CFieldMgr(scanFieldSize, ResolutionSize);
-	
+	// Use RAII to avoid leaks
+	std::unique_ptr<CFieldMgr> FldMgr(new CFieldMgr(scanFieldSize, ResolutionSize));
 
-		std::map<COTSParticle*, COTSParticleList> mapMergeParticles;//hold up all the boundary connected particles. the pair's first is also the member of these particles.
-		std::map<COTSParticle*, COTSSegmentsList> mapMergedSegments;//hold up all the segment's corresponding clone in the connected particles.
+	std::map<COTSParticle*, COTSParticleList> mapMergeParticles; // hold boundary-connected particles
+	std::map<COTSParticle*, COTSSegmentsList> mapMergedSegments;  // kept for compatibility with existing logic (unused below)
 
-		for (auto centerfld : allFields)
+	// Iterate fields and check 4 neighbor directions
+	for (auto centerfld : allFields)
+	{
+		// LEFT neighbor
+		if (auto leftFld = FldMgr->FindNeighborField(allFields, centerfld, SORTING_DIRECTION::LEFT))
 		{
-			// find neighbor field on the left.
-			auto leftFld = FldMgr->FindNeighborField(allFields, centerfld, SORTING_DIRECTION::LEFT);
-			if (leftFld != nullptr)
-			{
-				auto lParts = centerfld->GetLeftBorderedBigParticles();
-				auto rParts = leftFld->GetRightBorderedBigParticles();
-				auto leftParts = BorderPart::ConvertPartToBorderPart(lParts);
-				auto rightParts = BorderPart::ConvertPartToBorderPart(rParts);
+			auto lParts = centerfld->GetLeftBorderedBigParticles();
+			auto rParts = leftFld->GetRightBorderedBigParticles();
+			auto leftParts = ConvertPartToBorderPart(lParts);
+			auto rightParts = ConvertPartToBorderPart(rParts);
 
-				for (auto leftp : leftParts)
+			for (auto& leftp : leftParts)
+			{
+				for (auto& rightp : rightParts)
 				{
-					for (auto rightp : rightParts)
+					auto left_sp = leftp->LockMyPart();
+					auto right_sp = rightp->LockMyPart();
+					if (!left_sp || !right_sp) continue;
+
+					if (left_sp->IsConnected(right_sp.get(), centerfld->Width, centerfld->Height, (int)SORTING_DIRECTION::LEFT))
 					{
-						if (leftp->myPart->IsConnected(rightp->myPart.get(), centerfld->Width, centerfld->Height, (int)SORTING_DIRECTION::LEFT))
+						auto left_hdr = leftp->LockHeaderParticle();
+						auto right_hdr = rightp->LockHeaderParticle();
+						if (left_hdr)
 						{
-							if (leftp->headerParticle != NULL)
+							if (!right_hdr)
 							{
-								if (rightp->headerParticle == NULL)
-								{
-									rightp->headerParticle = leftp->headerParticle;
-									mapMergeParticles[leftp->headerParticle].push_back(rightp->myPart);
-								}
+								rightp->headerParticle = leftp->headerParticle;
+								mapMergeParticles[left_hdr.get()].push_back(right_sp);
+							}
+						}
+						else
+						{
+							if (right_hdr)
+							{
+								leftp->headerParticle = right_hdr;
+								mapMergeParticles[right_hdr.get()].push_back(left_sp);
 							}
 							else
 							{
-								if (rightp->headerParticle != NULL)
-								{
-									leftp->headerParticle = rightp->myPart.get();
-
-									mapMergeParticles[rightp->myPart.get()].push_back(leftp->myPart);
-								}
-								else
-								{
-									leftp->headerParticle = leftp->myPart.get();
-									rightp->headerParticle = leftp->myPart.get();
-									mapMergeParticles[leftp->myPart.get()].push_back(rightp->myPart);
-								}
-
+								leftp->headerParticle = left_sp;
+								rightp->headerParticle = left_sp;
+								mapMergeParticles[left_sp.get()].push_back(right_sp);
 							}
-
 						}
 					}
 				}
-
 			}
-			//find neighbor field on the upward
-			auto upFld = FldMgr->FindNeighborField(allFields, centerfld, SORTING_DIRECTION::UP);
-			if (upFld != nullptr)
-			{
-				auto topBorderParts = centerfld->GetTopBorderedBigParticles();
-				auto bottomBorderParts = upFld->GetBottomBorderedBigParticles();
-				auto upParts = BorderPart::ConvertPartToBorderPart(topBorderParts);
-				auto downParts = BorderPart::ConvertPartToBorderPart(bottomBorderParts);
+		}
+
+		// UP neighbor
+		if (auto upFld = FldMgr->FindNeighborField(allFields, centerfld, SORTING_DIRECTION::UP))
+		{
+			auto topBorderParts = centerfld->GetTopBorderedBigParticles();
+			auto bottomBorderParts = upFld->GetBottomBorderedBigParticles();
+			auto upParts = ConvertPartToBorderPart(topBorderParts);
+			auto downParts = ConvertPartToBorderPart(bottomBorderParts);
 
-				for (auto upprt : upParts)
+			for (auto& upprt : upParts)
+			{
+				for (auto& downprt : downParts)
 				{
-					for (auto downprt : downParts)
+					auto up_sp = upprt->LockMyPart();
+					auto down_sp = downprt->LockMyPart();
+					if (!up_sp || !down_sp) continue;
+
+					if (up_sp->IsConnected(down_sp.get(), centerfld->Width, centerfld->Height, (int)SORTING_DIRECTION::UP))
 					{
-						if (upprt->myPart->IsConnected(downprt->myPart.get(), centerfld->Width, centerfld->Height, (int)SORTING_DIRECTION::UP))
+						auto up_hdr = upprt->LockHeaderParticle();
+						auto down_hdr = downprt->LockHeaderParticle();
+						if (up_hdr)
 						{
-
-							if (upprt->headerParticle != NULL)
+							if (!down_hdr)
 							{
-								if (downprt->headerParticle == NULL)
-								{
-									downprt->headerParticle = upprt->headerParticle;
-									mapMergeParticles[upprt->headerParticle].push_back(downprt->myPart);
-								}
-
+								downprt->headerParticle = up_hdr;
+								mapMergeParticles[up_hdr.get()].push_back(down_sp);
+							}
+						}
+						else
+						{
+							if (down_hdr)
+							{
+								upprt->headerParticle = down_hdr;
+								mapMergeParticles[down_hdr.get()].push_back(up_sp);
 							}
 							else
 							{
-								if (downprt->headerParticle != NULL)
-								{
-									upprt->headerParticle = downprt->headerParticle;
-									mapMergeParticles[downprt->myPart.get()].push_back(upprt->myPart);
-								}
-								else
-								{
-									upprt->headerParticle = upprt->myPart.get();
-									downprt->headerParticle = upprt->myPart.get();
-									mapMergeParticles[upprt->myPart.get()].push_back(downprt->myPart);
-								}
-
+								upprt->headerParticle = up_sp;
+								downprt->headerParticle = up_sp;
+								mapMergeParticles[up_sp.get()].push_back(down_sp);
 							}
-
 						}
 					}
 				}
-
 			}
-			//find neighbor field on the downward.
-			auto downFld = FldMgr->FindNeighborField(allFields, centerfld,SORTING_DIRECTION::DOWN);
-			if (downFld != nullptr)
-			{
-				auto bottomParts = centerfld->GetBottomBorderedBigParticles();
-				auto topParts = downFld->GetTopBorderedBigParticles();
-				auto downParts = BorderPart::ConvertPartToBorderPart(bottomParts);
-				auto upParts= BorderPart::ConvertPartToBorderPart(topParts);
+		}
 
-				for (auto downprt : downParts)
+		// DOWN neighbor
+		if (auto downFld = FldMgr->FindNeighborField(allFields, centerfld, SORTING_DIRECTION::DOWN))
+		{
+			auto bottomParts = centerfld->GetBottomBorderedBigParticles();
+			auto topParts = downFld->GetTopBorderedBigParticles();
+			auto downParts = ConvertPartToBorderPart(bottomParts);
+			auto upParts = ConvertPartToBorderPart(topParts);
+
+			for (auto& downprt : downParts)
+			{
+				for (auto& upprt : upParts)
 				{
-					for (auto upprt : upParts)
+					auto down_sp = downprt->LockMyPart();
+					auto up_sp = upprt->LockMyPart();
+					if (!down_sp || !up_sp) continue;
+
+					if (down_sp->IsConnected(up_sp.get(), centerfld->Width, centerfld->Height, (int)SORTING_DIRECTION::DOWN))
 					{
-						if (downprt->myPart->IsConnected(upprt->myPart.get(), centerfld->Width, centerfld->Height, (int)SORTING_DIRECTION::DOWN))
+						auto down_hdr = downprt->LockHeaderParticle();
+						auto up_hdr = upprt->LockHeaderParticle();
+						if (down_hdr)
 						{
-
-							if (downprt->headerParticle != NULL)
+							if (!up_hdr)
 							{
-								if (upprt->headerParticle == NULL)
-								{
-									upprt->headerParticle = downprt->headerParticle;
-									mapMergeParticles[downprt->headerParticle].push_back(upprt->myPart);
-								}
-
+								upprt->headerParticle = down_hdr;
+								mapMergeParticles[down_hdr.get()].push_back(up_sp);
+							}
+						}
+						else
+						{
+							if (up_hdr)
+							{
+								downprt->headerParticle = up_hdr;
+								mapMergeParticles[up_hdr.get()].push_back(down_sp);
 							}
 							else
 							{
-								if (upprt->headerParticle != NULL)
-								{
-									downprt->headerParticle = upprt->headerParticle;
-									mapMergeParticles[upprt->headerParticle].push_back(downprt->myPart);
-								}
-								else
-								{
-									downprt->headerParticle = downprt->myPart.get();
-									upprt->headerParticle = downprt->myPart.get();
-									mapMergeParticles[downprt->myPart.get()].push_back(upprt->myPart);
-								}
-
+								downprt->headerParticle = down_sp;
+								upprt->headerParticle = down_sp;
+								mapMergeParticles[down_sp.get()].push_back(up_sp);
 							}
-
 						}
 					}
 				}
-
 			}
-			//find neighbor field on the right.
-			auto rightFld = FldMgr->FindNeighborField(allFields, centerfld, SORTING_DIRECTION::RIGHT);
-			if (rightFld != nullptr)
-			{
-				auto rParts = centerfld->GetRightBorderedBigParticles();
-				auto lParts = rightFld->GetLeftBorderedBigParticles();
-				auto rightParts = BorderPart::ConvertPartToBorderPart(rParts);
-				auto leftParts = BorderPart::ConvertPartToBorderPart(lParts);
+		}
+
+		// RIGHT neighbor
+		if (auto rightFld = FldMgr->FindNeighborField(allFields, centerfld, SORTING_DIRECTION::RIGHT))
+		{
+			auto rParts = centerfld->GetRightBorderedBigParticles();
+			auto lParts = rightFld->GetLeftBorderedBigParticles();
+			auto rightParts = ConvertPartToBorderPart(rParts);
+			auto leftParts = ConvertPartToBorderPart(lParts);
 
-				for (auto rightprt : rightParts)
+			for (auto& rightprt : rightParts)
+			{
+				for (auto& leftprt : leftParts)
 				{
-					for (auto leftprt : leftParts)
+					auto right_sp = rightprt->LockMyPart();
+					auto left_sp = leftprt->LockMyPart();
+					if (!right_sp || !left_sp) continue;
+
+					if (right_sp->IsConnected(left_sp.get(), centerfld->Width, centerfld->Height, (int)SORTING_DIRECTION::RIGHT))
 					{
-						if (rightprt->myPart->IsConnected(leftprt->myPart.get(), centerfld->Width, centerfld->Height, (int)SORTING_DIRECTION::RIGHT))
+						auto right_hdr = rightprt->LockHeaderParticle();
+						auto left_hdr = leftprt->LockHeaderParticle();
+						if (right_hdr)
 						{
-
-							if (rightprt->headerParticle != NULL)
+							if (!left_hdr)
 							{
-								if (leftprt->headerParticle == NULL)
-								{
-									leftprt->headerParticle = rightprt->headerParticle;
-									mapMergeParticles[rightprt->headerParticle].push_back(leftprt->myPart);
-								}
-
+								leftprt->headerParticle = right_hdr;
+								mapMergeParticles[right_hdr.get()].push_back(left_sp);
+							}
+						}
+						else
+						{
+							if (left_hdr)
+							{
+								rightprt->headerParticle = left_hdr;
+								mapMergeParticles[left_hdr.get()].push_back(right_sp);
 							}
 							else
 							{
-								if (leftprt->headerParticle != NULL)
-								{
-									rightprt->headerParticle = leftprt->headerParticle;
-									mapMergeParticles[leftprt->headerParticle].push_back(rightprt->myPart);
-								}
-								else
-								{
-									rightprt->headerParticle = rightprt->myPart.get();
-									leftprt->headerParticle = rightprt->myPart.get();
-									mapMergeParticles[rightprt->myPart.get()].push_back(leftprt->myPart);
-								}
-
+								rightprt->headerParticle = right_sp;
+								leftprt->headerParticle = right_sp;
+								mapMergeParticles[right_sp.get()].push_back(left_sp);
 							}
-
 						}
 					}
 				}
-
 			}
 		}
-		/*for (auto particle : mapMergeParticles)
-		{
-			
-		}*/
-		static int partTagId;
-		for (auto pair : mapMergeParticles)
+	}
+
+	// Merge groups into new particles
+	static int partTagId; // preserved behavior (was static previously). If you want thread-safety use std::atomic<int>.
+	for (auto& pair : mapMergeParticles)
+	{
+		struct EleAreaPercentage
 		{
-			struct EleAreaPercentage
-			{
-				EleAreaPercentage(double p, CElementChemistryPtr e)
-				{
-					areaPercentage = p;
-					eleData = e;
-				}
-				double areaPercentage;
-				CElementChemistryPtr eleData;
-			};
-
-			auto newPart = COTSParticlePtr(new COTSParticle());
-			COTSSegmentsList newSegs;
-			auto p = pair.first;
-			newPart->SetSEMPos(p->GetSEMPos());
-			
-			//firstly,we sum up all the merged particles's area and get the represent string.
-			std::string partsStr = std::to_string(p->GetFieldId()) + ":" + std::to_string(p->GetAnalysisId());
-			double allPartArea = p->GetActualArea();//Get the first particle's area.
+			EleAreaPercentage(double p, CElementChemistryPtr e) : areaPercentage(p), eleData(e) {}
+			double areaPercentage;
+			CElementChemistryPtr eleData;
+		};
 
+		auto newPart = COTSParticlePtr(new COTSParticle());
+		COTSSegmentsList newSegs;
+		auto p = pair.first;
+		newPart->SetSEMPos(p->GetSEMPos());
 
-			for (auto other : pair.second)// Get the total area of all these particles for  the use of ele calcu.
-			{
-				partsStr += "," + std::to_string(other->GetFieldId()) + ":" + std::to_string(other->GetAnalysisId());//Get the subparticles string such as "1:1,2:1" etc.
-				allPartArea += other->GetActualArea();//Get other particle's area
+		// build parts string and total area
+		std::string partsStr = std::to_string(p->GetFieldId()) + ":" + std::to_string(p->GetAnalysisId());
+		double allPartArea = p->GetActualArea();
 
-			}
-			// calculate all the new segment's position.
-			//std::vector <COTSParticle*> allSubParts;
-			COTSParticleList allSubParts;
-			allSubParts.push_back(COTSParticlePtr(p));
-			
-			for (auto other : pair.second)// Get the total area of all these particles for  the use of ele calcu.
-			{
-				allSubParts.push_back(other);
-			}
-			for (auto subp : allSubParts)
+		for (auto other : pair.second)
+		{
+			partsStr += "," + std::to_string(other->GetFieldId()) + ":" + std::to_string(other->GetAnalysisId());
+			allPartArea += other->GetActualArea();
+		}
+
+		// collect sub-parts (use header particle for the primary entry)
+		COTSParticleList allSubParts;
+		allSubParts.push_back(GetHeaderParticlePtr(p));
+		for (auto other : pair.second)
+		{
+			allSubParts.push_back(other);
+		}
+
+		// compute merged segments in global OTS coords
+		for (auto subp : allSubParts)
+		{
+			int fid = subp->GetFieldId();
+			CPoint myFldPos;
+			for (auto f : allFields)
 			{
-				int fid = subp->GetFieldId();
-				CPoint myFldPos;
-				for (auto f : allFields)//find  this particle's filed.
+				if (f->GetId() == fid)
 				{
-					if (f->GetId() == fid)
-					{
-						myFldPos = f->GetPosition();
-					}
-				}
-				int fldWidth = allFields[0]->Width;
-				int fldHeight = allFields[0]->Height;
-				CPoint fldLeftUpPos = CPoint(myFldPos.x + fldWidth / 2 , myFldPos.y + fldHeight / 2 );
-				for (auto s : subp->GetFeature()->GetSegmentsList())
-				{
-					COTSSegmentPtr newseg = COTSSegmentPtr(new COTSSegment());
-					newseg->SetStart(s->GetStart()  + fldLeftUpPos.x);
-					newseg->SetHeight((0 - s->GetHeight()) + fldLeftUpPos.y);//the coordinate system of segment in a field is different with the OTS coordinate system.OTS system's y axis is upward positive ,yet the field is downward positive.
-					newseg->SetLength(s->GetLength());
-					newSegs.push_back(newseg);
+					myFldPos = f->GetPosition();
+					break;
 				}
-				
 			}
+			int fldWidth = allFields[0]->Width;
+			int fldHeight = allFields[0]->Height;
+			CPoint fldLeftUpPos = CPoint(myFldPos.x + fldWidth / 2, myFldPos.y + fldHeight / 2);
+			for (auto s : subp->GetFeature()->GetSegmentsList())
+			{
+				COTSSegmentPtr newseg = COTSSegmentPtr(new COTSSegment());
+				newseg->SetStart(s->GetStart() + fldLeftUpPos.x);
+				newseg->SetHeight((0 - s->GetHeight()) + fldLeftUpPos.y);
+				newseg->SetLength(s->GetLength());
+				newSegs.push_back(newseg);
+			}
+		}
 
-			COTSFeaturePtr newFeature = COTSFeaturePtr(new COTSFeature());
-			newFeature->SetSegmentsList(newSegs);
-			newPart->SetFeature(newFeature);
-			newPart->CalCoverRectFromSegment();
-
-
-
-			//second, we get all the element data and their  area percentage .
-			std::map<std::string, std::vector<EleAreaPercentage>> mapEleData;
+		COTSFeaturePtr newFeature = COTSFeaturePtr(new COTSFeature());
+		newFeature->SetSegmentsList(newSegs);
+		newPart->SetFeature(newFeature);
+		newPart->CalCoverRectFromSegment();
 
-			CPosXrayPtr pXray1 = p->GetXrayInfo();
-			if (pXray1 != nullptr)
+		// merge element chemistry weighted by area
+		std::map<std::string, std::vector<EleAreaPercentage>> mapEleData;
+		CPosXrayPtr pXray1 = p->GetXrayInfo();
+		if (pXray1)
+		{
+			for (auto ele : pXray1->GetElementQuantifyData())
 			{
-				for (auto ele : pXray1->GetElementQuantifyData())
-				{
-					mapEleData[ele->GetName().GetBuffer()].push_back(EleAreaPercentage(p->GetActualArea() / allPartArea, ele));
-				}
+				mapEleData[ele->GetName().GetBuffer()].push_back(EleAreaPercentage(p->GetActualArea() / allPartArea, ele));
 			}
-			
-
-			for (auto other : pair.second)
+		}
+		for (auto other : pair.second)
+		{
+			auto otherXray = other->GetXrayInfo();
+			if (otherXray)
 			{
-				auto otherXray = other->GetXrayInfo();
-				if (otherXray != nullptr)
+				for (auto eledata : otherXray->GetElementQuantifyData())
 				{
-					for (auto eledata : otherXray->GetElementQuantifyData())
-					{
-						mapEleData[eledata->GetName().GetBuffer()].push_back(EleAreaPercentage(other->GetActualArea() / allPartArea, eledata));
-
-					}
+					mapEleData[eledata->GetName().GetBuffer()].push_back(EleAreaPercentage(other->GetActualArea() / allPartArea, eledata));
 				}
-				
 			}
-			// third,we calculate all the element's new percentage data and get a new element chemistry list.
+		}
 
-			CElementChemistriesList newCheList;
-			for (auto eledata : mapEleData)
+		CElementChemistriesList newCheList;
+		for (auto& eledata : mapEleData)
+		{
+			CElementChemistryPtr newEleche = CElementChemistryPtr(new CElementChemistry());
+			newEleche->SetName(CString(eledata.first.c_str()));
+			double newPercentage = 0;
+			for (auto& d : eledata.second)
 			{
-				CElementChemistryPtr newEleche = CElementChemistryPtr(new CElementChemistry());
-				newEleche->SetName(CString(eledata.first.c_str()));
-				double newPercentage = 0;
-				for (auto d : eledata.second)
-				{
-					newPercentage += d.areaPercentage * d.eleData->GetPercentage();
-				}
-				newEleche->SetPercentage(newPercentage);
-				newCheList.push_back(newEleche);
+				newPercentage += d.areaPercentage * d.eleData->GetPercentage();
 			}
+			newEleche->SetPercentage(newPercentage);
+			newCheList.push_back(newEleche);
+		}
+
+		CPosXrayPtr xray(new CPosXray());
+		xray->SetElementQuantifyData(newCheList);
+		newPart->SetXrayInfo(xray);
+		newPart->SetConnectedParticlesSequentialString(partsStr);
+		newPart->SetSubParticles(allSubParts);
+		newPart->SetActualArea(allPartArea);
+		partTagId++;
+		newPart->SetParticleId(partTagId);
+		newPart->SetAnalysisId(partTagId);
+		std::string name = p->GetClassifyName();
+		newPart->SetClassifyName(name);
+		newPart->SetColor(p->GetColor());
+		mergedParts.push_back(newPart);
+	}
 
-			CPosXrayPtr xray(new CPosXray());
-			xray->SetElementQuantifyData(newCheList);
-			newPart->SetXrayInfo(xray);
-			newPart->SetConnectedParticlesSequentialString(partsStr);
-			newPart->SetSubParticles(allSubParts);
-			newPart->SetActualArea(allPartArea);
-			partTagId++;
-			newPart->SetParticleId(partTagId);
-			newPart->SetAnalysisId(partTagId);
-			std::string name = p->GetClassifyName();
-			newPart->SetClassifyName(name);
-			newPart->SetColor(p->GetColor());
-			mergedParts.push_back(newPart);
-		}
-		return true;
+	return true;
 	}
 
 	BOOL COTSImageProcess::CalcuBrightPhaseDarkPhaseGrayByOtsu(CBSEImgPtr a_pImgIn, int& bright, int& dark)

+ 1 - 1
OTSIncAReportApp/1-UI/Control_DEVCharts/DEVChart_ParticlesComposition.cs

@@ -469,7 +469,7 @@ namespace OTSIncAReportApp.Control_ECharts
 
                     case "PComponent":
                         //创建新的图形数据源
-                        dt = particledata.GetParticleListByIncA("area");
+                        dt = particledata.GetParticleStatisticDataListByIncA("area");
 
                         DataTable dtPComponent = new DataTable();
                         dtPComponent.Columns.Add("name");

+ 392 - 527
OTSIncAReportApp/1-UI/Control_Grids/CompositionDistributionGrid.cs

@@ -12,6 +12,7 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Data;
+using System.Diagnostics;
 using System.Drawing;
 using System.Linq;
 using System.Windows.Forms;
@@ -23,603 +24,499 @@ namespace OTSIncAReportGrids
     /// </summary>
     public partial class CompositionDistributionGrid : UserControl
     {
-        #region 设置变量
-        //设置模拟数据表
-        public DataTable m_dt = new DataTable();
-        DataTable SelectParticlesListConvertToTable(List<Particle> selectParticles)
-        {
-            DataTable dataTable = new DataTable();
-
-
-
-
-            return dataTable;
-        }
-        //国际化
+        #region settings / fields
+        // Internationalization
         Language lan;
         Hashtable table;
 
-        //测量结果
+        // measurement results
         ResultFile result = null;
 
         OTSIncAReportApp.OTSSampleReportInfo.ReportCondition source;
         OTSIncAReportApp.frmReportApp m_ReportApp;
-
         frmReportConditionChoose m_condition;
 
-        //int gridheight = 0;
         List<int> list_gridheight = new List<int>();
+        List<DataTable> list_table_data = new List<DataTable>();
+        List<Point> list_table_size = new List<Point>();
+        List<Point> list_table_Point = new List<Point>();
+        List<string> list_fileName = new List<string>();
+        int DataNumber = 0; // current dataset index
 
-
-		List<DataTable> list_table_data = new List<DataTable>();
-		List<Point> list_table_size = new List<Point>();
-		List<Point> list_table_Point = new List<Point>();
-		List<string> list_fileName = new List<string>();
-        int DataNumber = 0;//当前数据;
-
-		List<ComboBox> list_ComboBox = new List<ComboBox>();
+        List<ComboBox> list_ComboBox = new List<ComboBox>();
         List<ChartControl> list_ChartControl = new List<ChartControl>();
 
-		int gridwidth = 0;
+        int gridwidth = 0;
         int GridPosition = 0;
         int chartposition = 0;
-		int indexGraphicType = 0;
+        int indexGraphicType = 0;
         string GraphicType = "柱状图";
-		#endregion
 
-		#region 多数据源
-		/// <summary>
-		/// 绑定datagridview数据
-		/// </summary>
-		public void BindDataGridView(string path, int num, string filename)
+        // constants for column names used repeatedly
+        private static readonly string ColRowId = "rowid";
+        private static readonly string ColTypeName = "TypeName";
+        private static readonly string ColTypeColor = "TypeColor";
+        private static readonly string ColCount = "con";
+        private static readonly string ColAreaRadio = "AreaRadio";
+        private static readonly string ColAvg = "av";
+        private static readonly string ColMax = "max";
+
+        // list of possible chart types for combo boxes
+        private static readonly string[] ChartTypes = new string[]
         {
-            List<int> FLID = new List<int>() { 0, 1, 2, 4, 6, 7, 8, 9 };
-            List<string> NameList = new List<string>() { table["partcletype0"].ToString(), table["partcletype1"].ToString(), table["partcletype2"].ToString(), table["partcletype4"].ToString(), table["partcletype6"].ToString(), table["partcletype7"].ToString(), table["partcletype8"].ToString(), table["partcletype9"].ToString() };
-            DataTable dt;
-            OTSGridView gridView = new OTSGridView();
-
-            OTSIncAReportApp.DataOperation.DataAccess.ParticleData particledata = new OTSIncAReportApp.DataOperation.DataAccess.ParticleData(path);
-            string str1 = table["str1"].ToString();
-            string str2 = table["str2"].ToString();
-            string str3 = table["str3"].ToString();
-            string str4 = table["str4"].ToString();
-            string str5 = table["str5"].ToString();
-            string str6 = table["str6"].ToString();
-            string str7 = table["str7"].ToString();
-            string str8 = table["str8"].ToString();
-            string str9 = table["str9"].ToString();
-            string str10 = table["str10"].ToString();
-            string str11 = table["str11"].ToString();
-            string str13 = table["str13"].ToString();
-            //创建Grid的列序号
-            gridView.Columns.Add("rowid", str1);
-            //创建Grid的列颗粒名
-            gridView.Columns.Add("TypeName", str8);
-            //创建Grid的列代表色
-            gridView.Columns.Add("TypeColor", str9);
-            //创建Grid的列颗粒数
-            gridView.Columns.Add("con", str10);
-            //创建Grid的列颗粒数
-            //gridView.Columns.Add("SmallParticle", str11);
-            //创建Grid的列面积占比
-            gridView.Columns.Add("AreaRadio", str13);
-            string con = m_ReportApp.m_conditionChoose.m_conditionData.GetPropItemDisplayValueByPropItemName(OTS_REPORT_PROP_GRID_ITEMS.SIZE_CAL_METHOD_TYPE).ToString();
-            string po = "";
-            switch (con)
+            "柱状图","折线图","饼图","圆环图","气泡图","簇状条形图","堆积条形图","瀑布图",
+            "3D柱状图","3D圆环图","3D饼图","3D折线图","3D百分比堆积条形图","3D漏斗图",
+            "3DManhattan 条形图","3D堆积条形图","3D阶梯线图"
+        };
+
+        #endregion
+
+        #region constructor / initialization
+
+        public CompositionDistributionGrid(OTSIncAReportApp.frmReportApp ReportApp)
+        {
+            m_ReportApp = ReportApp ?? throw new ArgumentNullException(nameof(ReportApp));
+            result = m_ReportApp.m_rstDataMgr.ResultFilesList[m_ReportApp.m_rstDataMgr.GetWorkingResultId()];
+            source = m_ReportApp.m_conditionChoose.m_conditionData;
+            m_condition = ReportApp.m_conditionChoose;
+            InitializeComponent();
+
+            // internationalization
+            lan = new Language(this);
+            table = lan.GetNameTable(this.Name);
+        }
+
+        #endregion
+
+       
+
+        #region grid/chart building
+
+        /// <summary>
+        /// Primary method to bind one data source into a grid and optionally create chart controls.
+        /// Refactored for clarity and reuse.
+        /// </summary>
+        public void BindDataGridView(string path, int num, string filename)
+        {
+            if (string.IsNullOrWhiteSpace(path)) return;
+
+            // load translation strings once
+            string str1 = table["str1"]?.ToString() ?? "No.";
+            string str2 = table["str2"]?.ToString() ?? string.Empty;
+            string str3 = table["str3"]?.ToString() ?? "Area";
+            string str4 = table["str4"]?.ToString() ?? "Avg";
+            string str5 = table["str5"]?.ToString() ?? string.Empty;
+            string str6 = table["str6"]?.ToString() ?? "Max";
+            string str7 = table["str7"]?.ToString() ?? "Max";
+            string str8 = table["str8"]?.ToString() ?? "Name";
+            string str9 = table["str9"]?.ToString() ?? "Color";
+            string str10 = table["str10"]?.ToString() ?? "Count";
+            string str11 = table["str11"]?.ToString() ?? string.Empty;
+            string str13 = table["str13"]?.ToString() ?? "Area%";
+
+            // prepare data access objects
+            var particledata = new ParticleData(path);
+            FieldData fielddata = new FieldData(path);
+
+            // Determine diameter column based on settings
+            string sizeMethod = m_ReportApp.m_conditionChoose.m_conditionData.GetPropItemDisplayValueByPropItemName(OTS_REPORT_PROP_GRID_ITEMS.SIZE_CAL_METHOD_TYPE).ToString();
+            string po;
+            switch (sizeMethod)
             {
                 case "DMAX":
                     po = "DMAX";
-                    //创建Grid的列平均直径
-                    gridView.Columns.Add("av", str4);
-                    //创建Grid的列最大直径
-                    gridView.Columns.Add("max", str7);
                     break;
                 case "DMIN":
                     po = "DMIN";
-                    //创建Grid的列平均直径
-                    gridView.Columns.Add("av", str4);
-                    //创建Grid的列最大直径
-                    gridView.Columns.Add("max", str7);
+                    break;
+                case "FERET":
+                    po = "DFERET";
                     break;
                 case "ECD":
                     po = "Area";
-                    //创建Grid的列平均直径
-                    gridView.Columns.Add("av", str3);
-                    //创建Grid的列最大直径
-                    gridView.Columns.Add("max", str6);
                     break;
-                case "FERET":
-                    po = "DFERET";
-                    //创建Grid的列平均直径
-                    gridView.Columns.Add("av", str4);
-                    //创建Grid的列最大直径
-                    gridView.Columns.Add("max", str7);
+                default:
+                    po = "Area";
                     break;
             }
-            double totalArea = 0;
 
-            
-            int sel = m_ReportApp.m_conditionChoose.m_conditionData.GetComboDownListIndexByItemName(OTS_REPORT_PROP_GRID_ITEMS.DATA_SOURCE_TYPE);
+            // compute total area used in percentage calculation
+            double totalArea = ComputeTotalArea(result, fielddata);
+
+            // fetch data table
+            DataTable dt = particledata.GetParticleStatisticDataListByIncA(po) ?? new DataTable();
+
+            // create and set up grid
+            var gridView = new OTSGridView();
+            CreateGridColumns(gridView, str1, str8, str9, str10, str13, sizeMethod, str3, str4, str6, str7);
+
             gridView.MergeColumnNames.Add(filename);
             gridView.AddSpanHeader(1, gridView.Columns.Count - 1, filename);
-            
-                FieldData fielddata = new FieldData(path);
-                double scanFieldSize = result.GetScanFieldSizeX();
-                double wide = result.GetImageWidth();
-                double high = result.GetImageHeight();
-                double pixelSize = scanFieldSize / wide;
-                int filedCount = fielddata.GetFiledCount();
-                totalArea = high * pixelSize*wide* pixelSize * filedCount;
-                dt = particledata.GetParticleListByIncA(po);
 
-            for (int i = 0; i < dt.Rows.Count; i++)
-            {
-                if (Convert.ToInt32(dt.Rows[i]["TypeId"]) < 100)
-                {
-                    continue;
-                }
-                int add_rowindex = gridView.Rows.Add();
-                gridView.Rows[add_rowindex].Tag= "Statistics";
-                gridView.Rows[add_rowindex].Cells[0].Value = add_rowindex + 1;
-                for (int k = 1; k < gridView.ColumnCount; k++)
-                {
+            // populate rows for TypeId >=100 (custom types) and for predefined types TypeId <=100
+            PopulateGridRows(gridView, dt, totalArea, sizeMethod, filename, true);
+            PopulateGridRows(gridView, dt, totalArea, sizeMethod, filename, false);
 
-                    if (gridView.Columns[k].Name == "TypeColor" && dt.Rows[i]["TypeColor"].ToString() != "")
-                    {
-                        string color = dt.Rows[i]["TypeColor"].ToString();
-                        if (!color.Contains("#"))
-                        {
-                            color = "#" + dt.Rows[i]["TypeColor"].ToString();
-                        }
-                        gridView.Rows[add_rowindex].Cells[k].Style.BackColor = DrawFunction.colorHx16toRGB(color);
-                    }
-                    else if (gridView.Columns[k].Name.ToLower() == "av" || gridView.Columns[k].Name.ToLower() == "max")
-                    {
-                        if (con == "ECD")
-                        {
-                            double Area = Convert.ToDouble(dt.Rows[i][gridView.Columns[k].Name]);
-                            gridView.Rows[add_rowindex].Cells[k].Value = Math.Round(Math.Sqrt(Area / Math.PI)* 2,2);
-                        }
-                        else
-                        {
-                            gridView.Rows[add_rowindex].Cells[k].Value = Math.Round( Convert.ToDouble(dt.Rows[i][gridView.Columns[k].Name])* 2,2);
-                        }
-                        gridView.Rows[add_rowindex].Cells[k].Style.BackColor = Color.Azure;
-                    }
-                    else if (gridView.Columns[k].Name.ToLower() == "arearadio")
-                    {
-                        if (totalArea == 0)
-                        {
-                            gridView.Rows[add_rowindex].Cells[k].Value = 0;
-                        }
-                        else
-                        { 
-                            gridView.Rows[add_rowindex].Cells[k].Value = Math.Round(Convert.ToDouble(dt.Rows[i]["Area"]) * 100 / totalArea, 3);
-                        }
-                        gridView.Rows[add_rowindex].Cells[k].Style.BackColor = Color.Azure;
+            gridView.Name = "gridView" + num.ToString();
+            list_table_Point.Add(SetDataGridViewStyle(gridView));
+            AddSumRow(gridView);
 
+            panel1.Controls.Add(gridView);
+            list_table_data.Add(dt);
+            list_fileName.Add(filename);
+        }
 
-                    }
-                    else
-                    {
-                        gridView.Rows[add_rowindex].Cells[k].Value = dt.Rows[i][gridView.Columns[k].Name];
-                        gridView.Rows[add_rowindex].Cells[k].Style.BackColor = Color.Azure;
-                    }
-                }
+        private double ComputeTotalArea(ResultFile resultFile, FieldData fielddata)
+        {
+            try
+            {
+                if (resultFile == null || fielddata == null) return 0.0;
+                double scanFieldSize = resultFile.GetScanFieldSizeX();
+                double wide = resultFile.GetImageWidth();
+                double high = resultFile.GetImageHeight();
+                int filedCount = fielddata.GetFiledCount();
+                double pixelSize = scanFieldSize / wide;
+                // total scanned area = per-field width*height * number of fields (in OTS coordinates)
+                // in original code totalArea = high * pixelSize * wide * pixelSize * filedCount;
+                return high * pixelSize * wide * pixelSize * filedCount;
+            }
+            catch (Exception ex)
+            {
+                Debug.WriteLine("ComputeTotalArea failed: " + ex);
+                return 0.0;
+            }
+        }
+
+        private void CreateGridColumns(OTSGridView gridView, string headerRowId, string headerTypeName, string headerTypeColor, string headerCount, string headerAreaPct, string sizeMethod,
+            string headerArea, string headerAvg, string headerMaxAlt, string headerMax)
+        {
+            gridView.Columns.Clear();
+            gridView.Columns.Add(ColRowId, headerRowId);
+            gridView.Columns.Add(ColTypeName, headerTypeName);
+            gridView.Columns.Add(ColTypeColor, headerTypeColor);
+            gridView.Columns.Add(ColCount, headerCount);
+            gridView.Columns.Add(ColAreaRadio, headerAreaPct);
+
+            if (sizeMethod == "ECD")
+            {
+                gridView.Columns.Add(ColAvg, headerArea);
+                gridView.Columns.Add(ColMax, headerMaxAlt);
+            }
+            else
+            {
+                gridView.Columns.Add(ColAvg, headerAvg);
+                gridView.Columns.Add(ColMax, headerMax);
             }
+        }
+
+        private void PopulateGridRows(OTSGridView gridView, DataTable dt, double totalArea, string sizeMethod, string filename, bool customTypesFirst)
+        {
+            if (dt == null || gridView == null) return;
 
+            // When customTypesFirst == true, process TypeId >= 100 (original code)
             for (int i = 0; i < dt.Rows.Count; i++)
             {
-                if (Convert.ToInt32(dt.Rows[i]["TypeId"]) > 100)
+                if (customTypesFirst)
                 {
-                    continue;
+                    if (Convert.ToInt32(dt.Rows[i]["TypeId"]) < 100) continue;
                 }
-                int add_rowindex = gridView.Rows.Add();
-                gridView.Rows[add_rowindex].Tag = "Statistics";
-                int index = FLID.IndexOf(Convert.ToInt32(dt.Rows[i]["TypeId"]));
-                if (index == -1)
+                else
                 {
-                    index = 7;
+                    if (Convert.ToInt32(dt.Rows[i]["TypeId"]) >= 100) continue;
                 }
-                string name = NameList[index];
 
-                gridView.Rows[add_rowindex].Cells[0].Value = add_rowindex + 1;
+                int addRowIndex = gridView.Rows.Add();
+                gridView.Rows[addRowIndex].Tag = "Statistics";
+                gridView.Rows[addRowIndex].Cells[0].Value = addRowIndex + 1;
+
                 for (int k = 1; k < gridView.ColumnCount; k++)
                 {
-
-                    if (gridView.Columns[k].Name == "TypeColor" && dt.Rows[i]["TypeColor"].ToString() != "")
+                    var colName = gridView.Columns[k].Name;
+                    if (string.Equals(colName, ColTypeColor, StringComparison.OrdinalIgnoreCase) && dt.Rows[i]["TypeColor"] != DBNull.Value && dt.Rows[i]["TypeColor"].ToString() != "")
                     {
-                        string color = "#000000";
-                        gridView.Rows[add_rowindex].Cells[k].Style.BackColor = DrawFunction.colorHx16toRGB(color);
+                        string color = dt.Rows[i]["TypeColor"].ToString();
+                        if (!color.Contains("#")) color = "#" + color;
+                        try
+                        {
+                            gridView.Rows[addRowIndex].Cells[k].Style.BackColor = DrawFunction.colorHx16toRGB(color);
+                        }
+                        catch
+                        {
+                            gridView.Rows[addRowIndex].Cells[k].Style.BackColor = Color.Black;
+                        }
                     }
-                    else if (gridView.Columns[k].Name.ToLower() == "av" || gridView.Columns[k].Name.ToLower() == "max")
+                    else if (string.Equals(colName, ColAvg, StringComparison.OrdinalIgnoreCase) || string.Equals(colName, ColMax, StringComparison.OrdinalIgnoreCase))
                     {
-                        if (con == "ECD")
+                        double value = 0;
+                        double.TryParse(dt.Rows[i][gridView.Columns[k].Name].ToString(), out value);
+                        if (sizeMethod == "ECD")
                         {
-                            double Area = Convert.ToDouble(dt.Rows[i][gridView.Columns[k].Name]);
-                            gridView.Rows[add_rowindex].Cells[k].Value = Math.Round(Math.Sqrt(Area / Math.PI) * 2, 2);
+                            // value interpreted as area -> convert to diameter
+                            double area = value;
+                            gridView.Rows[addRowIndex].Cells[k].Value = Math.Round(Math.Sqrt(area / Math.PI) * 2, 2);
                         }
                         else
                         {
-                            gridView.Rows[add_rowindex].Cells[k].Value = Math.Round(Convert.ToDouble(dt.Rows[i][gridView.Columns[k].Name]) * 2, 2);
+                            gridView.Rows[addRowIndex].Cells[k].Value = Math.Round(value , 2);
                         }
-                        gridView.Rows[add_rowindex].Cells[k].Style.BackColor = Color.Azure;
+                        gridView.Rows[addRowIndex].Cells[k].Style.BackColor = Color.Azure;
                     }
-                    else if (gridView.Columns[k].Name.ToLower() == "arearadio")
+                    else if (string.Equals(colName, ColAreaRadio, StringComparison.OrdinalIgnoreCase))
                     {
                         if (totalArea == 0)
                         {
-                            gridView.Rows[add_rowindex].Cells[k].Value = 0;
+                            gridView.Rows[addRowIndex].Cells[k].Value = 0;
                         }
                         else
                         {
-                            gridView.Rows[add_rowindex].Cells[k].Value = Math.Round(Convert.ToDouble(dt.Rows[i]["Area"]) * 100 / totalArea, 5);
+                            double areaVal = 0;
+                            double.TryParse(dt.Rows[i]["Area"].ToString(), out areaVal);
+                            gridView.Rows[addRowIndex].Cells[k].Value = Math.Round(areaVal * 100 / totalArea, customTypesFirst ? 3 : 5);
                         }
-                        gridView.Rows[add_rowindex].Cells[k].Style.BackColor = Color.Azure;
-
+                        gridView.Rows[addRowIndex].Cells[k].Style.BackColor = Color.Azure;
                     }
                     else
                     {
-                        gridView.Rows[add_rowindex].Cells[k].Value = dt.Rows[i][gridView.Columns[k].Name];
-                        gridView.Rows[add_rowindex].Cells[k].Style.BackColor = Color.Azure;
+                        gridView.Rows[addRowIndex].Cells[k].Value = dt.Rows[i][gridView.Columns[k].Name];
+                        gridView.Rows[addRowIndex].Cells[k].Style.BackColor = Color.Azure;
                     }
-
                 }
             }
-
-            gridView.Name = "gridView" + num.ToString();
-            list_table_Point.Add(SetDataGridViewStyle(gridView));
-            AddSumRow(gridView);
-
-            panel1.Controls.Add(gridView);
-
-            list_table_data.Add(dt);
         }
 
         void AddSumRow(OTSGridView gridView)
         {
-            if (gridView.Rows == null || gridView.Rows.Count == 0)
+            if (gridView == null || gridView.Rows == null || gridView.Rows.Count == 0)
                 return;
-            int a1 = gridView.Rows.Add();
-            gridView.Rows[a1].Tag = "Sum";
-            gridView.Rows[a1].Cells[1].Value = "Sum";
+
+            int sumRowIndex = gridView.Rows.Add();
+            gridView.Rows[sumRowIndex].Tag = "Sum";
+            gridView.Rows[sumRowIndex].Cells[1].Value = "Sum";
             gridView.Refresh();
+
+            long totalCount = 0;
+            double totalArea = 0.0;
+
             for (int j = 0; j < gridView.Rows.Count - 1; j++)
             {
-                if (gridView.Rows[a1].Cells[3].Value == null)
-                    gridView.Rows[a1].Cells[3].Value = 0;
-                gridView.Rows[a1].Cells[3].Value = Convert.ToInt64(gridView.Rows[a1].Cells[3].Value) + Convert.ToInt64(gridView.Rows[j].Cells[3].Value);
-                if (gridView.Rows[a1].Cells[4].Value == null)
-                    gridView.Rows[a1].Cells[4].Value = 0;
-                gridView.Rows[a1].Cells[4].Value = Convert.ToDouble(gridView.Rows[a1].Cells[4].Value) + Convert.ToDouble(gridView.Rows[j].Cells[4].Value);
-            }
-            gridView.Rows[a1].Height = gridView.Rows[a1-1].Height;
-            for (int j = 1; j < gridView.Columns.Count; j++)
-            {
-                gridView.Rows[a1].DefaultCellStyle.BackColor = Color.Azure;
+                if (gridView.Rows[j].Tag == null) continue;
+                try
+                {
+                    var cntObj = gridView.Rows[j].Cells[3].Value;
+                    if (cntObj != null && long.TryParse(cntObj.ToString(), out long cntVal))
+                        totalCount += cntVal;
+
+                    var areaObj = gridView.Rows[j].Cells[4].Value;
+                    if (areaObj != null && double.TryParse(areaObj.ToString(), out double areaVal))
+                        totalArea += areaVal;
+                }
+                catch { /* ignore malformed rows */ }
             }
-        }
 
-        private void Graphics(DataTable data,string filename,string comboBox1Text,int pos)
-        {
-            ChartControl chartControl1 = new ChartControl();
-            chartControl1.Series.Clear();
+            // assign sums
+            gridView.Rows[sumRowIndex].Cells[3].Value = totalCount;
+            gridView.Rows[sumRowIndex].Cells[4].Value = totalArea;
 
-            // 假设你已经有一个 ChartControl 实例,并且已经添加了一个饼图 Series
-            Series series = new Series(filename, ViewType.Bar);
-            for (int a = 0; a < data.Rows.Count; a++)
+            gridView.Rows[sumRowIndex].Height = gridView.Rows[Math.Max(0, sumRowIndex - 1)].Height;
+            for (int j = 1; j < gridView.Columns.Count; j++)
             {
-                string name = data.Rows[a]["TypeName"].ToString();
-                double Cunt = Convert.ToDouble(data.Rows[a]["con"]);
-                series.Points.Add(new SeriesPoint(name, Cunt));
+                gridView.Rows[sumRowIndex].DefaultCellStyle.BackColor = Color.Azure;
             }
-            series.LabelsVisibility = DefaultBoolean.True;
-            chartControl1.Series.Add(series);
-
-            //图例的位置定义
-            chartControl1.Legend.AlignmentVertical = LegendAlignmentVertical.Top;
-            chartControl1.Legend.AlignmentHorizontal = LegendAlignmentHorizontal.RightOutside;
-            chartControl1.Legend.Visibility = DevExpress.Utils.DefaultBoolean.False;
-            chartControl1.Legend.Direction = LegendDirection.TopToBottom;
-
-            //十字准线光标
-            chartControl1.CrosshairEnabled = DefaultBoolean.True;
-            chartControl1.CrosshairOptions.ShowValueLine = true;
-            chartControl1.CrosshairOptions.ShowArgumentLabels = true;
-            DevFunctions.ChangeView2(chartControl1, comboBox1Text);
-            chartControl1.Location = new Point(pos, list_gridheight.Max());
-            chartControl1.Size = new Size(GridPosition, GridPosition);
-			// 获取图表的坐标系(通常为 XYDiagram)
-            XYDiagram diagram = chartControl1.Diagram as XYDiagram;
-
-			if (diagram != null)
-			{
-				// 设置横坐标(X轴)名称
-				diagram.AxisX.Title.Text = "种类";
-				diagram.AxisX.Title.Visibility = DevExpress.Utils.DefaultBoolean.True; // 确保标题可见
-
-				// 设置纵坐标(Y轴)名称
-				diagram.AxisY.Title.Text = "数量";
-				diagram.AxisY.Title.Visibility = DevExpress.Utils.DefaultBoolean.True; // 确保标题可见
-			}
-			panel1.Controls.Add(chartControl1);
-
-			
-		}
+        }
 
-        /// <summary>
-        /// 设置DataGridView样式
-        /// </summary>
         private Point SetDataGridViewStyle(OTSGridView gridView)
         {
-            //用户不能调整标题的高度
+            // user can't change header height
             gridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
 
-            //用户不能调整 行高
+            // user can't change row height
             gridView.AllowUserToResizeRows = false;
 
-            //改变行的高度;
-            //gridView.RowTemplate.Height = 20;
-
-            //点击选择整行
             gridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
 
-            //居中显示
-            System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
-            dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
-            gridView.DefaultCellStyle = dataGridViewCellStyle1;
+            var centeredStyle = new DataGridViewCellStyle
+            {
+                Alignment = DataGridViewContentAlignment.MiddleCenter
+            };
+            gridView.DefaultCellStyle = centeredStyle;
             gridView.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
 
-            //再次重覆禁用拖动表头高度,居然有效果了
-            gridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
-
-            //设置grid可以复制
             gridView.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText;
 
-            //设置每列的宽度
-            gridView.Columns[0].Width = 40;//第一列序号的宽度设置一下吧,要不太丑
+            gridView.Columns[0].Width = 40;
             gridView.Columns[1].Width = 200;
 
-            //设置序号列不排序
-            //gridView.Columns[0].SortMode = DataGridViewColumnSortMode.NotSortable;
-
-            //设置序号列不可以设置宽度
             gridView.Columns[0].Resizable = DataGridViewTriState.False;
             gridView.AllowUserToAddRows = false;
             gridView.AllowUserToDeleteRows = false;
             gridView.AllowUserToResizeRows = false;
             gridView.BackgroundColor = System.Drawing.SystemColors.ButtonHighlight;
-            gridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
-            gridView.ContextMenuStrip = this.contextMenuStrip1;
-            gridView.BorderStyle = 0;
-
-            //gridView.Dock = System.Windows.Forms.DockStyle.Fill;
-            //gridView.Location = new System.Drawing.Point(0, 0);
-            //gridView.Location = new System.Drawing.Point(0, gridheight);
-            int gridheight = 0;
-            //chartposition = gridwidth;
-            int outpos = gridwidth;
-			gridView.Location = new System.Drawing.Point(gridwidth, 0);
-
-			gridView.Margin = new System.Windows.Forms.Padding(2);
-            gridView.MergeColumnHeaderBackColor = System.Drawing.SystemColors.ButtonHighlight;
-            //gridView.Name = "Gview_gz";
-            gridView.ReadOnly = true;
             gridView.RowHeadersVisible = false;
             gridView.RowHeadersWidth = 40;
             gridView.RowTemplate.Height = 30;
-            //gridView.Size = new System.Drawing.Size(667, 520);
             gridView.Size = new System.Drawing.Size(gridView.ColumnCount * 100 + 60, gridView.RowCount * 30);
-			//gridView.Size = new System.Drawing.Size(gridView.ColumnCount * 100 + 60, gridView.RowCount * 30 + 30);
-			//gridheight = gridheight + gridView.RowCount * 30 + 80;
-			gridheight = gridheight + (gridView.RowCount+1) * 30 + 5;
-			gridwidth = gridwidth + gridView.ColumnCount * 100 + 60;
+            int gridheight = (gridView.RowCount + 1) * 30 + 5;
+            gridwidth = gridwidth + gridView.ColumnCount * 100 + 60;
             chartposition = chartposition + gridView.ColumnCount * 100;
-			GridPosition = gridView.ColumnCount * 100 +40;
-			list_gridheight.Add(gridheight);
+            GridPosition = gridView.ColumnCount * 100 + 40;
+            list_gridheight.Add(gridheight);
 
-			gridView.TabIndex = 0;
-            
+            gridView.TabIndex = 0;
+            gridView.ContextMenuStrip = this.contextMenuStrip1;
+            gridView.BorderStyle = 0;
+            gridView.ReadOnly = true;
 
             gridView.ColumnHeaderMouseClick += new DataGridViewCellMouseEventHandler(this.Gview_ColumnHeaderMouseClick);
             gridView.Sorted += new System.EventHandler(this.Gview_gz_Sorted);
 
-            //先设置一下头的高度,否则会太矮不好看
+            // set header height a bit taller for aesthetics
             gridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing;
             gridView.ColumnHeadersHeight = 40;
 
-			list_table_size.Add( new Point(gridView.ColumnCount * 100+40, gridView.RowCount * 30));
-
+            list_table_size.Add(new Point(gridView.ColumnCount * 100 + 40, gridView.RowCount * 30));
+            return new Point(gridwidth - (gridView.ColumnCount * 100 + 60), gridheight + 30);
+        }
 
-			return new Point(outpos, gridheight+30);
-		}
         #endregion
 
-        #region 窗体加载及初始化
+        #region charts
 
+        public void SetGraphicType(ReportCondition sourceGridData)
+        {
+            if (sourceGridData == null) return;
 
+            string stdId_TYPE = "";
+            int stdIdIndex = 0;
+            var STDIdItm = sourceGridData.ConditionItemList.Find(s => s.iItemId == OTS_REPORT_PROP_GRID_ITEMS.GRAPHIC_TYPE);
+            if (STDIdItm != null)
+            {
+                stdId_TYPE = STDIdItm.itemDisplayVal.ToString();
+                stdIdIndex = STDIdItm.comboDownList.IndexOf(stdId_TYPE);
+            }
 
-        /// <summary>
-        /// 成份分布表,构造函数
-        /// </summary>
-        /// <param name="in_sec">传入,委托类对象</param>
-        /// <param name="in_Cotsreportprojfilemgrclr">传入,项目管理类对象</param>
-        public CompositionDistributionGrid(OTSIncAReportApp.frmReportApp ReportApp)
-        {
-            m_ReportApp = ReportApp;
-            result = m_ReportApp.m_rstDataMgr.ResultFilesList[m_ReportApp.m_rstDataMgr.GetWorkingResultId()];
-            source = m_ReportApp.m_conditionChoose.m_conditionData;
+            indexGraphicType = stdIdIndex;
+            GraphicType = stdId_TYPE;
 
-            m_condition= ReportApp.m_conditionChoose;
-            InitializeComponent();
-            //国际化
-            lan = new Language(this);
-            table = lan.GetNameTable(this.Name);
+            int high = 0;
+            for (int i = 0; i < list_table_Point.Count; i++)
+            {
+                if (high < list_table_Point[i].Y)
+                {
+                    high = list_table_Point[i].Y;
+                }
+            }
+
+            for (int i = 0; i < list_table_data.Count; i++)
+            {
+                DataNumber = i;
+                Graphics(list_table_data[i], list_fileName[i], stdId_TYPE, list_table_size[i], list_table_Point[i], high);
+            }
         }
 
-        public void SetGraphicType(ReportCondition sourceGridData)
+        private void Graphics(DataTable data, string filename, string comboBox1Text, Point a_size, Point a_Location, int location_hig)
         {
-			
-
-			string stdId_TYPE = "";
-			int stdIdIndex = 0;
-			//find the stdId condition
-			var STDIdItm = sourceGridData.ConditionItemList.Find(s => s.iItemId == OTS_REPORT_PROP_GRID_ITEMS.GRAPHIC_TYPE);
-			if (STDIdItm != null)
-			{
-				stdId_TYPE = STDIdItm.itemDisplayVal.ToString();
-				stdIdIndex = STDIdItm.comboDownList.IndexOf(stdId_TYPE);
-			}
+            var chartControl1 = new ChartControl();
+            chartControl1.Series.Clear();
 
-            indexGraphicType = stdIdIndex;
-            GraphicType = stdId_TYPE;
+            Series series = new Series(filename, ViewType.Bar);
+            if (data != null)
+            {
+                for (int a = 0; a < data.Rows.Count; a++)
+                {
+                    string name = data.Rows[a]["TypeName"].ToString();
+                    double cnt = 0;
+                    double.TryParse(data.Rows[a]["con"].ToString(), out cnt);
+                    series.Points.Add(new SeriesPoint(name, cnt));
+                }
+            }
 
+            series.LabelsVisibility = DefaultBoolean.True;
+            chartControl1.Series.Add(series);
 
-			int high = 0;
-			for (int i = 0; i < list_table_Point.Count; i++)
-			{
-				if (high < list_table_Point[i].Y)
-				{
-					high = list_table_Point[i].Y;
-				}
-			}
+            chartControl1.Legend.AlignmentVertical = LegendAlignmentVertical.Top;
+            chartControl1.Legend.AlignmentHorizontal = LegendAlignmentHorizontal.RightOutside;
+            chartControl1.Legend.Visibility = DevExpress.Utils.DefaultBoolean.False;
+            chartControl1.Legend.Direction = LegendDirection.TopToBottom;
 
-			for (int i = 0; i < list_table_data.Count; i++)
-			{
-                DataNumber = i;
-				Graphics(list_table_data[i], list_fileName[i], stdId_TYPE, list_table_size[i], list_table_Point[i], high);
-			}
-		}
-		/// <summary>
-		/// chart图创建
-		/// </summary>
-		/// <param name="dt"></param>
-		/// <param name="stdId"></param>
-		private void Graphics(DataTable data, string filename, string comboBox1Text, Point a_size, Point a_Location, int location_hig)
-		{
-    
-
-			ChartControl chartControl1 = new ChartControl();
-			chartControl1.Series.Clear();
-
-			// 假设你已经有一个 ChartControl 实例,并且已经添加了一个饼图 Series
-			Series series = new Series(filename, ViewType.Bar);
-			for (int a = 0; a < data.Rows.Count; a++)
-			{
-				string name = data.Rows[a]["TypeName"].ToString();
-				double Cunt = Convert.ToDouble(data.Rows[a]["con"]);
-				series.Points.Add(new SeriesPoint(name, Cunt));
-			}
-			series.LabelsVisibility = DefaultBoolean.True;
-			chartControl1.Series.Add(series);
-
-			//图例的位置定义
-			chartControl1.Legend.AlignmentVertical = LegendAlignmentVertical.Top;
-			chartControl1.Legend.AlignmentHorizontal = LegendAlignmentHorizontal.RightOutside;
-			chartControl1.Legend.Visibility = DevExpress.Utils.DefaultBoolean.False;
-			chartControl1.Legend.Direction = LegendDirection.TopToBottom;
-
-			//十字准线光标
-			chartControl1.CrosshairEnabled = DefaultBoolean.True;
-			chartControl1.CrosshairOptions.ShowValueLine = true;
-			chartControl1.CrosshairOptions.ShowArgumentLabels = true;
-			DevFunctions.ChangeView2(chartControl1, comboBox1Text);
-			chartControl1.Location = new Point(a_Location.X, location_hig+20);
-			chartControl1.Size = new Size(a_size.X, a_size.Y);
-			// 获取图表的坐标系(通常为 XYDiagram)
-			XYDiagram diagram = chartControl1.Diagram as XYDiagram;
-
-			if (diagram != null)
-			{
-				// 设置横坐标(X轴)名称
-				diagram.AxisX.Title.Text = "种类";
-				diagram.AxisX.Title.Visibility = DevExpress.Utils.DefaultBoolean.True; // 确保标题可见
-
-				// 设置纵坐标(Y轴)名称
-				diagram.AxisY.Title.Text = "数量";
-				diagram.AxisY.Title.Visibility = DevExpress.Utils.DefaultBoolean.True; // 确保标题可见
-			}
-            
-			ComboBox comboBox = new ComboBox();
-			list_ChartControl.Add(chartControl1);
+            chartControl1.CrosshairEnabled = DefaultBoolean.True;
+            chartControl1.CrosshairOptions.ShowValueLine = true;
+            chartControl1.CrosshairOptions.ShowArgumentLabels = true;
+            DevFunctions.ChangeView2(chartControl1, comboBox1Text);
+            chartControl1.Location = new Point(a_Location.X, location_hig + 20);
+            chartControl1.Size = new Size(a_size.X, a_size.Y);
+
+            var diagram = chartControl1.Diagram as XYDiagram;
+            if (diagram != null)
+            {
+                diagram.AxisX.Title.Text = "种类";
+                diagram.AxisX.Title.Visibility = DevExpress.Utils.DefaultBoolean.True;
+                diagram.AxisY.Title.Text = "数量";
+                diagram.AxisY.Title.Visibility = DevExpress.Utils.DefaultBoolean.True;
+            }
+
+            // prepare combo box for chart type selection (re-use ChartTypes constant)
+            var comboBox = new ComboBox();
+            list_ChartControl.Add(chartControl1);
             list_ComboBox.Add(comboBox);
-            list_ComboBox[list_ComboBox.Count - 1].Items.Add("柱状图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("折线图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("饼图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("圆环图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("气泡图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("簇状条形图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("堆积条形图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("瀑布图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("3D柱状图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("3D圆环图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("3D饼图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("3D折线图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("3D百分比堆积条形图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("3D漏斗图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("3DManhattan 条形图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("3D堆积条形图");
-			list_ComboBox[list_ComboBox.Count - 1].Items.Add("3D阶梯线图");
-            list_ComboBox[list_ComboBox.Count - 1].Name = Convert.ToString(list_ComboBox.Count - 1);
-			list_ComboBox[list_ComboBox.Count - 1].SelectedIndex = 0;
-			list_ComboBox[list_ComboBox.Count - 1].Location = new Point(a_Location.X+chartControl1.Size.Width-100, location_hig);
-			list_ComboBox[list_ComboBox.Count - 1].Size = new Size(100, 5);
-			list_ComboBox[list_ComboBox.Count - 1].SelectedIndexChanged += ComboBox_SelectedIndexChanged;
-			panel1.Controls.Add(chartControl1);
-            panel1.Controls.Add(list_ComboBox[list_ComboBox.Count - 1]);
-		}
+
+            comboBox.Items.AddRange(ChartTypes);
+            comboBox.Name = Convert.ToString(list_ComboBox.Count - 1);
+            comboBox.SelectedIndex = 0;
+            comboBox.Location = new Point(a_Location.X + chartControl1.Size.Width - 100, location_hig);
+            comboBox.Size = new Size(100, 25);
+            comboBox.SelectedIndexChanged += ComboBox_SelectedIndexChanged;
+
+            panel1.Controls.Add(chartControl1);
+            panel1.Controls.Add(comboBox);
+        }
 
         private void ComboBox_SelectedIndexChanged(object sender, EventArgs e)
         {
-			ComboBox comboBox = sender as ComboBox;
-            if (comboBox != null)
+            var comboBox = sender as ComboBox;
+            if (comboBox == null) return;
+
+            if (int.TryParse(comboBox.Name, out int idx) && idx >= 0 && idx < list_ChartControl.Count)
             {
-                DevFunctions.ChangeView2(list_ChartControl[Convert.ToInt32(comboBox.Name)], comboBox.Text);
+                DevFunctions.ChangeView2(list_ChartControl[idx], comboBox.Text);
+            }
+        }
+
+        #endregion
 
-			}
-				//DevFunctions.ChangeView2(list_ChartControl[DataNumber], comboBox.Text);
-		}
+        #region events / UI actions
 
-		private void CompositionDistributionGrid_Load(object sender, EventArgs e)
+        private void CompositionDistributionGrid_Load(object sender, EventArgs e)
         {
+            list_table_data.Clear();
+            list_table_size.Clear();
+            list_table_Point.Clear();
+            list_fileName.Clear();
 
-			list_table_data.Clear();
-			list_table_size.Clear();
-			list_table_Point.Clear();
-			list_fileName.Clear();
-			string sou = m_condition.m_CurrentConditions[OTS_REPORT_PROP_GRID_ITEMS.DATA_SOURCE].itemDisplayVal.ToString();
-
-			if (sou.Contains("+"))
-			{
-				for (int i = 0; i < sou.Split('+').Length; i++)
-				{
-					ResultFile resultFile = m_ReportApp.m_rstDataMgr.ResultFilesList.Find(s => s.anotherFileName == sou.Split('+')[i]);
-					if (resultFile != null)
-					{
-						BindDataGridView(resultFile.FilePath, i, resultFile.anotherFileName);
-						list_fileName.Add(resultFile.anotherFileName);
-					}
-				}
-			}
-			else
-			{
-				for (int i = 0; i < m_ReportApp.m_rstDataMgr.ResultFilesList.Count; i++)
-				{
-					if (sou == m_ReportApp.m_rstDataMgr.ResultFilesList[i].anotherFileName.ToString())
-					{
-						BindDataGridView(m_ReportApp.m_rstDataMgr.ResultFilesList[i].FilePath, 0, m_ReportApp.m_rstDataMgr.ResultFilesList[i].anotherFileName);
-						list_fileName.Add(m_ReportApp.m_rstDataMgr.ResultFilesList[i].anotherFileName);
-					}
-				}
-			}
-		}
-        #endregion
+            string sou = m_condition.m_CurrentConditions[OTS_REPORT_PROP_GRID_ITEMS.DATA_SOURCE].itemDisplayVal.ToString();
+            if (sou.Contains("+"))
+            {
+                foreach (var name in sou.Split('+'))
+                {
+                    var resultFile = m_ReportApp.m_rstDataMgr.ResultFilesList.Find(s => s.anotherFileName == name);
+                    if (resultFile != null)
+                    {
+                        BindDataGridView(resultFile.FilePath, list_fileName.Count, resultFile.anotherFileName);
+                    }
+                }
+            }
+            else
+            {
+                var match = m_ReportApp.m_rstDataMgr.ResultFilesList.FirstOrDefault(r => r.anotherFileName == sou);
+                if (match != null)
+                {
+                    BindDataGridView(match.FilePath, 0, match.anotherFileName);
+                }
+            }
+        }
 
-        #region 相关事件
         private void ToolStripMenuItem1_Click(object sender, EventArgs e)
         {
             OTSGridView otv = (OTSGridView)((ContextMenuStrip)(sender as ToolStripMenuItem).Owner).SourceControl;
-            //复制整个表
             CopyAll(otv);
         }
 
@@ -629,126 +526,94 @@ namespace OTSIncAReportGrids
             CopySelected(otv);
         }
 
-        /// <summary>
-        /// 复制选择区域
-        /// </summary>
         public void CopySelected(OTSGridView in_ogv)
         {
-            Clipboard.SetDataObject(((OTSGridView)in_ogv).GetClipboardContent());
+            if (in_ogv == null) return;
+            Clipboard.SetDataObject(in_ogv.GetClipboardContent());
         }
 
-        /// <summary>
-        /// 复制选择区域
-        /// </summary>
         public void CopySelected()
         {
-            DataGridView dgv = (DataGridView)panel1.Controls["Gview_gz"];
-            //复制选择区域
-            object oo = dgv.GetClipboardContent();
+            var dgv = panel1.Controls.OfType<DataGridView>().FirstOrDefault();
+            if (dgv == null) return;
             Clipboard.SetDataObject(dgv.GetClipboardContent());
         }
 
-
-        /// <summary>
-        /// 复制所有
-        /// </summary>
         public void CopyAll(OTSGridView in_ogv)
         {
+            if (in_ogv == null) return;
             in_ogv.SelectAll();
-            Clipboard.SetDataObject(((OTSGridView)in_ogv).GetClipboardContent());
+            Clipboard.SetDataObject(in_ogv.GetClipboardContent());
         }
 
-        /// <summary>
-        /// 复制所有
-        /// </summary>
         public void CopyAll()
         {
-            DataGridView dgv = (DataGridView)panel1.Controls["Gview_gz"];
+            var dgv = panel1.Controls.OfType<DataGridView>().FirstOrDefault();
+            if (dgv == null) return;
             dgv.SelectAll();
-            Clipboard.SetDataObject(((OTSGridView)dgv).GetClipboardContent());
+            Clipboard.SetDataObject(dgv.GetClipboardContent());
         }
 
-
         private void ToolStripMenuItem3_Click(object sender, EventArgs e)
         {
-            foreach (var item in panel1.Controls)
-            {
-                if (item.GetType().ToString() == "OTSGridView")
-                {
-                    panel1.Controls.Remove((OTSGridView)item);
-                }
-            }
-			list_table_data.Clear();
-			list_table_size.Clear();
-			list_table_Point.Clear();
-			list_fileName.Clear();
+            // remove only OTSGridView controls
+            var toRemove = panel1.Controls.OfType<OTSGridView>().ToArray();
+            foreach (var g in toRemove) panel1.Controls.Remove(g);
 
+            list_table_data.Clear();
+            list_table_size.Clear();
+            list_table_Point.Clear();
+            list_fileName.Clear();
 
-			string sou = source.GetPropItemDisplayValueByPropItemName(OTS_REPORT_PROP_GRID_ITEMS.DATA_SOURCE).ToString();
+            string sou = source.GetPropItemDisplayValueByPropItemName(OTS_REPORT_PROP_GRID_ITEMS.DATA_SOURCE).ToString();
             if (sou.Contains("+"))
             {
-                for (int i = 0; i < sou.Split('+').Length; i++)
+                foreach (var name in sou.Split('+'))
                 {
-                    ResultFile resultFile = m_ReportApp.m_rstDataMgr.ResultFilesList.Find(s => s.anotherFileName == sou.Split('+')[i]);
+                    var resultFile = m_ReportApp.m_rstDataMgr.ResultFilesList.Find(s => s.anotherFileName == name);
                     if (resultFile != null)
                     {
-                        BindDataGridView(resultFile.FilePath, i, resultFile.anotherFileName);
-						list_fileName.Add(resultFile.anotherFileName);
-					}
+                        BindDataGridView(resultFile.FilePath, list_fileName.Count, resultFile.anotherFileName);
+                    }
                 }
             }
             else
             {
-                BindDataGridView(m_ReportApp.m_rstDataMgr.ResultFilesList[m_ReportApp.m_rstDataMgr.GetWorkingResultId()].FilePath, 0, m_ReportApp.m_rstDataMgr.ResultFilesList[m_ReportApp.m_rstDataMgr.GetWorkingResultId()].anotherFileName);
-				list_fileName.Add(m_ReportApp.m_rstDataMgr.ResultFilesList[m_ReportApp.m_rstDataMgr.GetWorkingResultId()].anotherFileName);
-			}
+                var match = m_ReportApp.m_rstDataMgr.ResultFilesList[m_ReportApp.m_rstDataMgr.GetWorkingResultId()];
+                BindDataGridView(match.FilePath, 0, match.anotherFileName);
+            }
         }
 
-
         void Gview_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
         {
-            if (((OTSGridView)sender).Rows == null || ((OTSGridView)sender).Rows.Count == 0)
-                return;
-            AddSumRow((OTSGridView)sender);
+            var grid = sender as OTSGridView;
+            if (grid == null || grid.Rows == null || grid.Rows.Count == 0) return;
+            AddSumRow(grid);
         }
+
         private void Gview_gz_Sorted(object sender, EventArgs e)
         {
-            if (((OTSGridView)sender).Rows == null || ((OTSGridView)sender).Rows.Count == 0)
-                return;
-            for (int i = 0; i < ((OTSGridView)sender).Rows.Count; i++)
-            {
-                if (((OTSGridView)sender).Rows[i].Tag.ToString() == "Sum")
-                { ((OTSGridView)sender).Rows.RemoveAt(i); break; }
-            }
-            ((OTSGridView)sender).Refresh();
-            //排序完成,重新设置序号
-            for (int i = 0; i < ((OTSGridView)sender).Rows.Count; i++)
-            {
-                ((OTSGridView)sender).Rows[i].Cells[0].Value = (i+1).ToString();
-            }
-        }
-        #endregion
+            var grid = sender as OTSGridView;
+            if (grid == null || grid.Rows == null || grid.Rows.Count == 0) return;
 
-        #region 获取向导出模块提供的DataTable和GridView对象
-        /// <summary>
-        /// 获取到该模块输出后形成的DataTable,和GridView
-        /// </summary>
-        /// <param name="out_dt"></param>
-        /// <param name="out_dg"></param>
-        public void GetDataTableAndGridView(out DataTable out_dt, out DataGridView out_dg)
-        {
-            out_dt = m_dt;
-            out_dg = null;
-            foreach (var item in panel1.Controls)
+            for (int i = 0; i < grid.Rows.Count; i++)
             {
-                if (item.GetType().ToString() == "OTSGridView")
+                if (grid.Rows[i].Tag != null && grid.Rows[i].Tag.ToString() == "Sum")
                 {
-                    out_dg = ((OTSGridView)item);
+                    grid.Rows.RemoveAt(i);
+                    break;
                 }
             }
 
+            grid.Refresh();
+            for (int i = 0; i < grid.Rows.Count; i++)
+            {
+                grid.Rows[i].Cells[0].Value = (i + 1).ToString();
+            }
         }
+
         #endregion
 
+     
     }
 }

+ 3 - 1
OTSIncAReportApp/1-UI/frmMeasureRstMgr.cs

@@ -726,6 +726,8 @@ namespace OTSIncAReportApp
                     }
                     part.SetFieldId(p.FieldId);
                     part.SetAnalysisId(p.XrayId);
+                    part.SetParticleId(p.ParticleId);
+                    
                     part.SetXray(xray);
                     part.SetActualArea(p.Area);
                     part.SetSEMPos(new Point(p.PosX, p.PosY));
@@ -784,7 +786,7 @@ namespace OTSIncAReportApp
                 {
                     partdb.UpdateIsSubParticleState(subpart);
                 }
-                 xraydb.DeleteElementChemistryById(part.GetFieldId(), part.GetParticleId());
+                 //xraydb.DeleteElementChemistryById(part.GetFieldId(), part.GetParticleId());
             }
             List<CPosXrayClr> ches = new List<CPosXrayClr>();
             foreach (COTSParticleClr part in mergedParts)

+ 1 - 1
OTSIncAReportApp/3-ServiceCenter/DataOperation/DataAccess/ParticleData.cs

@@ -148,7 +148,7 @@ namespace OTSIncAReportApp.DataOperation.DataAccess
         /// </summary>
         /// <param name="model">Feature</param>
         /// <returns></returns>
-        public DataTable GetParticleListByIncA(string con)
+        public DataTable GetParticleStatisticDataListByIncA(string con)
         {
             string sqlp = @"select TypeId,TypeName,TypeColor,count(1) as con,sum(Area) as Area,avg(" + con
                 + ") as av,max(" + con