//----------------------------------------------------------- // Subroutine to set block on element end //----------------------------------------------------------- short CommonReadHandler::EndBMPInput(char *xName,int exitBlock) { if(strcmp(xName,"BMP")==0) { // make last intensity hole material if(currentLevel==NULL) throw SAXException(BMPError("No intensity levels for materials were defined for <BMP> file",bmpFileName)); // add level to grab left over intensities BMPLevel *newLevel=new BMPLevel(0,0,255); currentLevel->SetNextObject(newLevel); TranslateBMPFiles(); // scan file for material points or elements block=exitBlock; // Must have been in MATERIALPOINTS // delete defined levels while(firstLevel!=NULL) { currentLevel=(BMPLevel *)firstLevel->GetNextObject(); delete firstLevel; firstLevel=currentLevel; } currentLevel=NULL; } else if(strcmp(xName,"Intensity")==0) { if(currentLevel->concentration<0. || currentLevel->concentration>1.) throw SAXException(BMPError("Particle concentration potential for an intensity level must be from 0 to 1",bmpFileName)); block=BMPBLOCK; } else if(strcmp(xName,"Origin")==0 || strcmp(xName,"Thickness")==0 || strcmp(xName,"Angle")==0 || strcmp(xName,"Concentration")==0 || strcmp(xName,"Temperature")==0) { // nothing to do } // not a BMP block element else return FALSE; return TRUE; }
//----------------------------------------------------------- // Subroutine to translate BMP file into material point //----------------------------------------------------------- void MPMReadHandler::TranslateBMPFiles(void) { unsigned char **rows,**angleRows; BMPInfoHeader info,angleInfo; bool setAngles=FALSE; int numRotations=strlen(rotationAxes); // read image file char *bmpFullPath=archiver->ExpandOutputPath(bmpFileName); ReadBMPFile(bmpFullPath,info,&rows); delete [] bmpFullPath; // angle file name (overrides other angle settings) if(bmpAngleFileName[0]>0) { setAngles=TRUE; char *bmpFullAnglePath=archiver->ExpandOutputPath(bmpAngleFileName); ReadBMPFile(bmpFullAnglePath,angleInfo,&angleRows); if(info.height!=angleInfo.height || info.width!=angleInfo.width) throw SAXException(BMPError("The image file and angle file sizes do not match.",bmpFileName)); delete [] bmpFullAnglePath; } else if(numRotations>0) { int i; for(i=0;i<numRotations;i++) { char *expr=new char[strlen(angleExpr[i])+1]; strcpy(expr,angleExpr[i]); if(!CreateFunction(expr,i+1)) throw SAXException("Invalid angle expression was provided in <RotateX(YZ)> command."); delete [] expr; } } // bheight and bwidth provided in bmp file // <-1.e8 means not provided // negative means provided mm per pixel // positive is a specified size double xpw=-1.,ypw=-1.; if(bwidth<-1.e8 && bheight<-1.e8) { if(!info.knowsCellSize) throw SAXException(BMPError("<BMP> must specify width and/or height as size or pixels per mm.",bmpFileName)); bwidth = info.width*info.xcell; bheight = info.height*info.ycell; xpw = info.xcell; ypw = info.ycell; } else { // provided mm per pixel if(bheight<0. && bheight>-1.e8) { ypw = -bheight; bheight = ypw*(double)info.height; } if(bwidth<0. && bwidth>-1.e8) { xpw = -bwidth; bwidth = xpw*(double)info.width; } } // total dimensions (if only one is known, find the other, never have both unknown) if(bheight<0) bheight=bwidth*(double)info.height/(double)info.width; if(bwidth<0) bwidth=bheight*(double)info.width/(double)info.height; // final mm per pixel (if needed) if(xpw<0.) xpw=bwidth/(double)info.width; if(ypw<0.) ypw=bheight/(double)info.height; // variables for scanning BMP file int matID; MPMBase *newMpt; int ii,k; Vector mpos[MaxElParticles]; int ptFlag; BMPLevel *nextLevel; double deltax,deltay,deltaz,semiscale; double rmin,rmax,wtr1,wtr2,rweight; double cmin,cmax,wtc1,wtc2,weight; int r1,r2,c1,c2; BMPLevel *maxLevel; int row,col; ElementBase *elem; // Length/semiscale is half particle with // (semiscale=4 for 2D w 4 pts per element or 3D with 8 pts per element, // or 2 if 1 particle in the element) if(fmobj->IsThreeD()) semiscale=2.*pow((double)fmobj->ptsPerElement,1./3.); else semiscale=2.*sqrt((double)fmobj->ptsPerElement); /* Parallelizing the following loop will speed up check meshes on Mac 1. Will need copy of levels for each block (or pass copy of weight to BMPLevel methods) see BMPLevel methods: ClearWeight(),Material(double,double), MaximumWeight(double) 2. mpCtrl->AddMaterialPoint() will need critical block 3. FunctionValue() when setting angles uses globals they are problem for parallel */ // scan mesh and assign material points or angles try { for(ii=1;ii<=nelems;ii++) { // skip if image not in extent of element box elem=theElements[ii-1]; if(!elem->IntersectsBox(xorig,yorig,bwidth,bheight,zslice)) continue; // load point coordinates elem->MPMPoints(fmobj->ptsPerElement,mpos); // half the extent of the volume of a particle deltax=(elem->GetDeltaX())/semiscale; deltay=(elem->GetDeltaY())/semiscale; if(fmobj->IsThreeD()) deltaz=elem->GetDeltaZ()/semiscale; else deltaz=1.; if(deltaz<0.) deltaz=1.; for(k=0;k<fmobj->ptsPerElement;k++) { ptFlag=1<<k; // skip if already filled if(elem->filled&ptFlag) continue; // if point in the view area, then check it if((mpos[k].x>=xorig && mpos[k].x<xorig+bwidth) && (mpos[k].y>=yorig && mpos[k].y<yorig+bheight) && (mpos[k].z>=zslice-deltaz && mpos[k].z<zslice+deltaz)) { // find range of rows and cols for pixels over this material point if(yflipped) { rmin=(yorig+bheight-mpos[k].y-deltay)/ypw; rmax=(yorig+bheight-mpos[k].y-deltay)/ypw; } else { rmin=(mpos[k].y-deltay-yorig)/ypw; rmax=(mpos[k].y+deltay-yorig)/ypw; } r1=BMPIndex(rmin,info.height); r2=BMPIndex(rmax,info.height); if(r2==r1) { wtr1=wtr2=1.; } else { // fractional weight for first and last row in case not all within the extent wtr1=(double)(r1+1)-rmin; wtr2=rmax-(double)r2; } cmin=(mpos[k].x-deltax-xorig)/xpw; cmax=(mpos[k].x+deltax-xorig)/xpw; c1=BMPIndex(cmin,info.width); c2=BMPIndex(cmax,info.width); if(c2==c1) { wtc1=wtc2=1.; } else { // fractional weight for first and last col in case not all within the extent wtc1=(double)(c1+1)-cmin; wtc2=cmax-(double)c2; } // find material ID or none (a hole) // clear material weights nextLevel=firstLevel; while(nextLevel!=NULL) nextLevel=nextLevel->ClearWeight(); // add weights for(row=r1;row<=r2;row++) { if(row==r1) rweight=wtr1; else if(row==r2) rweight=wtr2; else rweight=1.; for(col=c1;col<=c2;col++) { if(col==c1) weight=rweight*wtc1; else if(col==c2) weight=rweight*wtc2; else weight=rweight; // find material at this level // (note: last level with matID=0 catches empty space) nextLevel=firstLevel; while(nextLevel!=NULL) { matID=nextLevel->Material(rows[row][col],weight); if(matID>=0) break; nextLevel=(BMPLevel *)nextLevel->GetNextObject(); } } } // find maximum weight (matID=0 means max is empty space) weight=0.; maxLevel=NULL; nextLevel=firstLevel; while(nextLevel!=NULL) nextLevel=nextLevel->MaximumWeight(weight,&maxLevel); if(maxLevel!=NULL) { matID=maxLevel->mat; nextLevel=maxLevel; } else matID=-1; // create a material point if one at this spot using matID and nextLevel // Note that empty spaced is not marked as filled which allows superposition of // images with different materials. If want to forcefully create a hole that // cannot be filled by subsequent image, will need to define a new material // type that can have matID for a hole. It will not create a point, but will mark // the location as filled if(matID>0) { if(fmobj->IsThreeD()) newMpt=new MatPoint3D(ii,matID,nextLevel->angle); else if(fmobj->IsAxisymmetric()) newMpt=new MatPointAS(ii,matID,nextLevel->angle,mpos[k].x); else newMpt=new MatPoint2D(ii,matID,nextLevel->angle,nextLevel->thickness); newMpt->SetPosition(&mpos[k]); newMpt->SetOrigin(&mpos[k]); newMpt->SetVelocity(&nextLevel->vel); mpCtrl->AddMaterialPoint(newMpt,nextLevel->concentration,nextLevel->temperature); // is there an angle image? if(setAngles) { // weight average of scanned pixels double totalWeight=0.; double totalIntensity=0.; for(row=r1;row<=r2;row++) { if(row==r1) rweight=wtr1; else if(row==r2) rweight=wtr2; else rweight=1.; for(col=c1;col<=c2;col++) { if(col==c1) weight=rweight*wtc1; else if(col==c2) weight=rweight*wtc2; else weight=rweight; totalIntensity+=weight*angleRows[row][col]; totalWeight+=weight; } } totalIntensity/=totalWeight; double matAngle=minAngle+(totalIntensity-minIntensity)*angleScale; newMpt->SetAnglez0InDegrees(matAngle); } else { // If had Rotate commands then use them SetMptAnglesFromFunctions(numRotations,&mpos[k],newMpt); } elem->filled|=ptFlag; } } } } } catch(const char *msg) { throw SAXException(msg); } // clean up for(row=0;row<info.height;row++) { delete [] rows[row]; if(setAngles) delete [] angleRows[row]; } delete [] rows; if(setAngles) delete [] angleRows; // angles if allocated for(ii=0;ii<numRotations;ii++) { delete [] angleExpr[ii]; } DeleteFunction(-1); }
//----------------------------------------------------------- // Subroutine to translate BMP file into material point // throws std::bad_alloc, SAXException() //----------------------------------------------------------- void FEAReadHandler::TranslateBMPFiles(void) { // file info and data unsigned char **rows,**angleRows=NULL; XYInfoHeader info,angleInfo; // read image file char *bmpFullPath=archiver->ExpandOutputPath(bmpFileName); ReadBMPFile(bmpFullPath,info,&rows); delete [] bmpFullPath; // angle file name bool setAngles = false; if(bmpAngleFileName[0]>0) { setAngles=true; char *bmpFullAnglePath=archiver->ExpandOutputPath(bmpAngleFileName); ReadBMPFile(bmpFullAnglePath,angleInfo,&angleRows); if(info.height!=angleInfo.height || info.width!=angleInfo.width) throw SAXException(BMPError("The image file and angle file sizes do not match.",bmpFileName)); delete [] bmpFullAnglePath; } // get final image width, height, and size per pixel Vector pw; const char *msg = CommonReadHandler::DecodeBMPWidthAndHeight(info,bwidth,bheight,orig.z,pw,false); if(msg != NULL) throw SAXException(BMPError(msg,bmpFileName)); pw.z = yflipped ? -1. : 1. ; // scan all elements - fill those that are in the area and have no material yet DomainMap map; ElementBase *elem; Vector center,del; for(int i=0;i<nelems;i++) { elem=theElements[i]; // skip if element already has a material if(elem->material!=NO_MATERIAL) continue; // get center (will skip unless the center of extent of the element is in the image) elem->GetXYZCentroid(¢er); // half the extent of the element (z means 2D) del = MakeVector((elem->GetDeltaX())/2.,(elem->GetDeltaY())/2.,-1.); // Get range of rows and columns and their weights (or skip if not in the image) if(!MapDomainToImage(info,center,orig,del,pw,bwidth,bheight,map)) continue; // find maximum level and its material ID or none (a hole) int matID=-1; BMPLevel *nextLevel = FindBMPLevel(firstLevel,map,rows); if(nextLevel!=NULL) matID = nextLevel->Material(); // set material ID if found a match if(matID>0) { // set it elem->material=matID; // is there an angle image too? if(setAngles) { double totalIntensity = FindAverageValue(map,rows); if(totalIntensity>0.) { double matAngle=minAngle+(totalIntensity-minIntensity)*angleScale; elem->SetAngleInDegrees(matAngle); } } } } // clean up for(int row=0;row<info.height;row++) { delete [] rows[row]; if(setAngles) delete [] angleRows[row]; } delete [] rows; if(setAngles) delete [] angleRows; }
//----------------------------------------------------------- // Subroutine to read BMP file //----------------------------------------------------------- void CommonReadHandler::ReadBMPFile(char *bmpFullPath,BMPInfoHeader &info,unsigned char ***theData) { short mustReverse=FALSE,status=TRUE; BMPHeader header; FILE *fp; unsigned int i,ncolors; unsigned char blueByte,greenByte,redByte,junkByte,dataByte,mask; int row,col,colByte; // decide what computer I am on // byte order marker int test=1; char *testPtr=(char *)&test; if(*testPtr==0) mustReverse=TRUE; // open the file if((fp=fopen(bmpFullPath,"r"))==NULL) throw SAXException(BMPError("The <BMP> file could not be opened.",bmpFullPath)); // read and check the header (individual reads due to Windows alignment issues) if(fread(header.type,2,1,fp)<1) status=FALSE; if(fread(&header.size,4,1,fp)<1) status=FALSE; if(fread(&header.reserved1,2,1,fp)<1) status=FALSE; if(fread(&header.reserved2,2,1,fp)<1) status=FALSE; if(fread(&header.offset,4,1,fp)<1) status=FALSE; if(!status) { fclose(fp); throw SAXException(BMPError("Error reading the specified <BMP> file.",bmpFileName)); } if(mustReverse) { Reverse((char *)&header.size,sizeof(int)); Reverse((char *)&header.offset,sizeof(int)); } // precalculate number of colors from header data ncolors=(header.offset-14-40)>>2; // exit if does not look like BMP file if(header.type[0]!='B' || header.type[1]!='M') { fclose(fp); throw SAXException(BMPError("<BMP> file is not a valid bit map file.",bmpFileName)); } // read information if(fread(&info.size,4,1,fp)<1) status=FALSE; if(fread(&info.width,4,1,fp)<1) status=FALSE; if(fread(&info.height,4,1,fp)<1) status=FALSE; if(fread(&info.planes,2,1,fp)<1) status=FALSE; if(fread(&info.bits,2,1,fp)<1) status=FALSE; if(fread(&info.compression,4,1,fp)<1) status=FALSE; if(fread(&info.imagesize,4,1,fp)<1) status=FALSE; if(fread(&info.xresolution,4,1,fp)<1) status=FALSE; if(fread(&info.yresolution,4,1,fp)<1) status=FALSE; if(fread(&info.ncolors,4,1,fp)<1) status=FALSE; if(fread(&info.importantcolors,4,1,fp)<1) status=FALSE; if(!status) { fclose(fp); throw SAXException(BMPError("Error reading the specified <BMP> file.",bmpFileName)); } if(mustReverse) { Reverse((char *)&info.size,sizeof(int)); Reverse((char *)&info.width,sizeof(int)); Reverse((char *)&info.height,sizeof(int)); Reverse((char *)&info.planes,2); Reverse((char *)&info.bits,2); Reverse((char *)&info.compression,sizeof(int)); Reverse((char *)&info.imagesize,sizeof(int)); Reverse((char *)&info.xresolution,sizeof(int)); Reverse((char *)&info.yresolution,sizeof(int)); Reverse((char *)&info.ncolors,sizeof(int)); Reverse((char *)&info.importantcolors,sizeof(int)); } // correct for Photoshop lack of information if(info.imagesize==0) info.imagesize=header.size-header.offset; if(info.ncolors==0) info.ncolors=ncolors; // can I read this BMP file if(info.planes!=1) { fclose(fp); throw SAXException(BMPError("Cannot read <BMP> files with more than 1 color plane.",bmpFileName)); } if(info.compression!=0) { fclose(fp); throw SAXException(BMPError("Cannot read compressed <BMP> files.",bmpFileName)); } if(info.ncolors!=ncolors || (ncolors!=2 && ncolors!=16 && ncolors!=256)) { fclose(fp); throw SAXException(BMPError("<BMP> files does not appear to be 1, 4, or 8 bit grayscale image.",bmpFileName)); } // read the 2, 16, or 256 colors for(i=0;i<ncolors;i++) { if(fread(&blueByte,1,1,fp)<1) status=FALSE; if(fread(&greenByte,1,1,fp)<1) status=FALSE; if(fread(&redByte,1,1,fp)<1) status=FALSE; if(fread(&junkByte,1,1,fp)<1) status=FALSE; if(!status) { fclose(fp); throw SAXException(BMPError("<BMP> color table is corrupted.",bmpFileName)); } intensity[i]=((int)blueByte+(int)greenByte+(int)blueByte)/3; } // buffer to read the file unsigned char **rows=new unsigned char *[info.height]; *theData=rows; if(rows==NULL) { fclose(fp); throw SAXException(BMPError("Out of memory reading <BMP> file.",bmpFileName)); } for(row=0;row<info.height;row++) { rows[row]=new unsigned char[info.width]; if(rows[row]==NULL) { fclose(fp); throw SAXException(BMPError("Out of memory reading <BMP> file.",bmpFileName)); } } // read one byte at a time (which might have multiple points) but each // row is multiple of 4 bytes int colorsPerByte=1; if(ncolors==16) colorsPerByte=2; else if(ncolors==2) colorsPerByte=8; int rowBytes=info.width/colorsPerByte; if(rowBytes%4!=0) rowBytes+=4-(rowBytes % 4); // check size unsigned int expectSize=(unsigned int)(info.height*rowBytes); if(info.imagesize<expectSize) { fclose(fp); throw SAXException(BMPError("<BMP> file appears to be missing some data.",bmpFileName)); } // scan each row for(row=0;row<info.height;row++) { col=0; for(colByte=0;colByte<rowBytes;colByte++) { // read next byte if(fread(&dataByte,1,1,fp)<1) { fclose(fp); throw SAXException(BMPError("Error reading image date of <BMP> file.",bmpFileName)); } if(col>=info.width) continue; switch(ncolors) { case 256: // each byte is an index rows[row][col]=intensity[dataByte]; col++; break; case 16: // each byte has two indices rows[row][col]=intensity[(dataByte&0xF0)>>4]; col++; if(col>=info.width) break; rows[row][col]=intensity[dataByte&0x0F]; col++; break; case 2: mask=0x80; for(i=1;i<=8;i++) { rows[row][col]=intensity[(dataByte&mask)>>(8-i)]; col++; if(col>=info.width) break; mask>>=1; } break; default: break; } } } fclose(fp); }
//----------------------------------------------------------- // Check for bmp element, return false if not //----------------------------------------------------------- short CommonReadHandler::BMPFileCommonInput(char *xName,const Attributes& attrs,int expectedBlock) { char *aName,*value; int i,numAttr; double aScaling; //----------------------------------------------------------- // Read BMP file name and resolution //----------------------------------------------------------- if(strcmp(xName,"BMP")==0) { ValidateCommand(xName,expectedBlock,ANY_DIM); block=BMPBLOCK; bwidth=bheight=-1.e9; // < -1.e8 means dimension was not specified bmpFileName[0]=0; bmpAngleFileName[0]=0; xorig=yorig=0.; yflipped=FALSE; zslice=0.; aScaling=ReadUnits(attrs,LENGTH_UNITS); numAttr=attrs.getLength(); #ifdef MPM_CODE rotationAxes[0]=0; // no rotations yet #endif for(i=0;i<numAttr;i++) { aName=XMLString::transcode(attrs.getLocalName(i)); value=XMLString::transcode(attrs.getValue(i)); if(strcmp(aName,"width")==0) sscanf(value,"%lf",&bwidth); else if(strcmp(aName,"height")==0) sscanf(value,"%lf",&bheight); else if(strcmp(aName,"name")==0) strcpy(bmpFileName,value); else if(strcmp(aName,"angles")==0) strcpy(bmpAngleFileName,value); delete [] aName; delete [] value; } if(bwidth<-1.e8 && bheight<-1.e8) throw SAXException(BMPError("<BMP> must specify width and/or height as size or pixels per mm.",bmpFileName)); bwidth*=aScaling; bheight*=aScaling; if(bmpFileName[0]==0) throw SAXException("<BMP> must specify the file in a name attribute."); } //----------------------------------------------------------- // Set origin on input data //----------------------------------------------------------- else if(strcmp(xName,"Origin")==0) { ValidateCommand(xName,BMPBLOCK,ANY_DIM); aScaling=ReadUnits(attrs,LENGTH_UNITS); numAttr=attrs.getLength(); for(i=0;i<numAttr;i++) { aName=XMLString::transcode(attrs.getLocalName(i)); value=XMLString::transcode(attrs.getValue(i)); if(strcmp(aName,"x")==0) { sscanf(value,"%lf",&xorig); xorig*=aScaling; } else if(strcmp(aName,"y")==0) { sscanf(value,"%lf",&yorig); yorig*=aScaling; } else if(strcmp(aName,"z")==0) { sscanf(value,"%lf",&zslice); zslice*=aScaling; } else if(strcmp(aName,"flipped")==0) { if(strcmp(value,"yes")==0 || strcmp(value,"Yes")==0 || strcmp(value,"YES")==0 || strcmp(value,"1")==0 ) yflipped=TRUE; else yflipped=FALSE; } delete [] aName; delete [] value; } } //----------------------------------------------------------- // Assign intensity to some material properties //----------------------------------------------------------- else if(strcmp(xName,"Intensity")==0) { ValidateCommand(xName,BMPBLOCK,ANY_DIM); numAttr=attrs.getLength(); int mat=-1; int imin=-1; int imax=-1; minAngle=0.; double maxAngle=0.; char matname[200]; matname[0]=0; for(i=0;i<numAttr;i++) { aName=XMLString::transcode(attrs.getLocalName(i)); value=XMLString::transcode(attrs.getValue(i)); if(strcmp(aName,"mat")==0) sscanf(value,"%d",&mat); else if(strcmp(aName,"matname")==0) { if(strlen(value)>199) value[200]=0; strcpy(matname,value); } else if(strcmp(aName,"imin")==0) sscanf(value,"%d",&imin); else if(strcmp(aName,"imax")==0) sscanf(value,"%d",&imax); else if(strcmp(aName,"minAngle")==0) sscanf(value,"%lf",&minAngle); else if(strcmp(aName,"maxAngle")==0) sscanf(value,"%lf",&maxAngle); delete [] aName; delete [] value; } // if gave a matname, it takes precedence over mat number if(strlen(matname)>0) mat = matCtrl->GetIDFromNewName(matname); if(imin<0 || imax<0) throw SAXException(BMPError("<Intensity> has incomplete set of attributes.",bmpFileName)); if(imin>=imax) throw SAXException(BMPError("<Intensity> range is not valid.",bmpFileName)); if(mat>=0) { BMPLevel *newLevel=new BMPLevel(mat,imin,imax); if(newLevel==NULL) throw SAXException(BMPError("<Intensity> failed due to memory error.",bmpFileName)); if(currentLevel==NULL) firstLevel=newLevel; else currentLevel->SetNextObject(newLevel); currentLevel=newLevel; block=INTENSITYBLOCK; } else { angleScale=(maxAngle-minAngle)/((double)imax-(double)imin); minIntensity=(double)imin; } } //----------------------------------------------------------- // Intensity properties for both MPM and FEA //----------------------------------------------------------- // thickness else if(strcmp(xName,"Thickness")==0) { ValidateCommand(xName,INTENSITYBLOCK,MUST_BE_2D); input=DOUBLE_NUM; inputPtr=(char *)¤tLevel->thickness; gScaling=ReadUnits(attrs,LENGTH_UNITS); } // angle else if(strcmp(xName,"Angle")==0) { ValidateCommand(xName,INTENSITYBLOCK,ANY_DIM); input=DOUBLE_NUM; inputPtr=(char *)¤tLevel->angle; } // temperature else if(strcmp(xName,"Temperature")==0) { #ifdef FEA_CODE if(block==THERMAL) return FALSE; #endif ValidateCommand(xName,INTENSITYBLOCK,ANY_DIM); input=DOUBLE_NUM; inputPtr=(char *)¤tLevel->temperature; } else return FALSE; return TRUE; }