float AssignEModulus::sampleField( const OBLelement3D* FEelement, 
								  const McDArray<McVec3f>& vertices,
								  HxUniformScalarField3* field )
{
	float sum = 0;

	if ( !FEelement ) { return 0; }

	// this parameter controls the number of samples.
	// there are *roughly* STEPS^3 samples.
	const int STEPS = 10;
	const float PARAM_STEP = 1.0/STEPS;

	HxLocation3* location = field->createLocation();
	float value;
	float volume = 0.0;
	for ( float tt = 0.0; tt <= 1.0; tt += PARAM_STEP )
	{
		for ( float ss = 0.0; ss <= 1.0-tt; ss += PARAM_STEP )
		{
			for ( float rr = 0.0; rr <= 1.0-ss-tt; rr += PARAM_STEP )
			{
				// compute the coordinates of the point corresponding to this parameter triplet.
				// this is a convex sum of the element's vertices.
				McVec3f pcoords( rr, ss, tt );
				McVec3f point = FEelement->getLocation( vertices, pcoords );

				//McMat3f J;
				//FEelement->getJacobian( vertices, pcoords, J );
				//float detJ = J.det();
				float detJ = 1.0;	// ignore scaling by volume
					
				if ( location->move(point.x, point.y, point.z) == 1 )
				{
					// field is valid for new location
					field->eval( location, &value );
					sum += value*detJ;
					volume += detJ;
				}
				// else - this point is in the tetrahedron but is outside of the scalar field.
			}
		}
	}
	delete location;

	float result = (volume > 0) ? (sum / volume) : 0;

	return result;
}
void HxMovingLeastSquaresWarp::compute() {
    if (!portAction.wasHit())
        return;

    HxUniformScalarField3* fromImage =
        (HxUniformScalarField3*)portFromImage.getSource();

    HxLandmarkSet* pointSet = hxconnection_cast<HxLandmarkSet>(portData);

    if (!fromImage)
        return;

    /* It is ok to warp without landmarks, if method is rigid and
       input has a transformation */
    if (!pointSet) {
        return;
    } else if (pointSet->getNumSets() < 2) {
        theMsg->printf(
            "Error: LandmarkWarp data has to contain at least 2 sets.");
        return;
    }

    HxUniformScalarField3* outputImage;

    HxUniformVectorField3* outputVectorField = 0;

    outputImage = createOutputDataSet();
    outputVectorField = createOutputVectorDataSet();

    float* outputImageData = (float*)outputImage->lattice().dataPtr();

    McDArray<McVec2d> landmarks1, landmarks2;

    prepareLandmarks(landmarks1, landmarks2);

    MovingLeastSquares mls;
    mls.setAlpha(portAlpha.getValue());
    mls.setLandmarks(landmarks2, landmarks1);

    const McDim3l& dims = outputImage->lattice().getDims();

    McVec3f voxelSizeInOutputImage = outputImage->getVoxelSize();
    const McBox3f& bboxOfOutputImage = outputImage->getBoundingBox();
    const McBox3f& bboxOfFromImage = fromImage->getBoundingBox();
#ifdef _OPENMP
#pragma omp parallel for
#endif
    for (int i = 0; i < dims[0]; i++) {
        HxLocation3* locationInFromImage = fromImage->createLocation();
        std::cout << "\n" << i << " of " << dims[0];
        for (int j = 0; j < dims[1]; j++) {
            McVec2d currentPositionInOutput = McVec2d(
                bboxOfOutputImage[0] + (float)(i)*voxelSizeInOutputImage.x,
                bboxOfOutputImage[2] + (float)(j)*voxelSizeInOutputImage.y);
            McVec2d warpedCoordInFromImage =
                mls.interpolate(currentPositionInOutput);
            McVec3f displacement = McVec3f(
                warpedCoordInFromImage.x - currentPositionInOutput.x,
                warpedCoordInFromImage.y - currentPositionInOutput.y, 0);

            displacement = displacement * -1;
            locationInFromImage->move(McVec3f(warpedCoordInFromImage.x,
                                              warpedCoordInFromImage.y,
                                              bboxOfFromImage[4]));
            float resultValue[1];
            fromImage->eval(*locationInFromImage, resultValue);
            unsigned long pos = latticePos(i, j, 0, dims);
            outputImageData[pos] = resultValue[0];
            outputVectorField->lattice().set(i, j, 0, displacement.getValue());
        }
        delete locationInFromImage;
    }

    outputImage->touch();
    outputImage->fire();
}
void AssignEModulus::assignEModulus( GridType* grid )
{
	if ( !grid ) { return; }

	HxUniformScalarField3* image = dynamic_cast<HxUniformScalarField3*>( portImage.source() );
	if ( !image ) { return; }

	theWorkArea->startWorking( "Assigning Young's moduli to elements..." );
	theWorkArea->setProgressValue( 0.0 );

	OBLelement3D* FEelement = NULL;
	if ( dynamic_cast<HxTetraGrid*>(grid) )
	{
		FEelement = new OBLtetra;
	}
	if ( dynamic_cast<HxHexaGrid*>(grid) )
	{
		FEelement = new OBLhexa;
	}
	if ( !FEelement ) { return; }

	mculong totalElems = AmiraGrid::getNumberOfElements( grid );
	float* moduli = new float[totalElems];

	if ( portAveraging.getValue() == 0 )
	{
		HxLocation3* loc = image->createLocation();
		for ( mculong i = 0; i < totalElems; i++ )
		{
			theWorkArea->setProgressValue( (1.0*i)/totalElems );

			float intensity = 0;
			McDArray<McVec3f> vertices = AmiraGrid::getElementVertices( grid, i );
			McVec3f p = FEelement->getCenter( vertices );
			if ( loc->move( p[0], p[1], p[2] ) == 1 )
			{
				// image is valid for this location
				image->eval( loc, &intensity );
			}

			moduli[i] = this->getElasticityFromHU( intensity );
		}
		delete loc;
	}
	else if ( portAveraging.getValue() == 1 )	// Bonemat V1
	{
		// Create an instance of HxLoc3Regular instead of HxLocation3,
		// because we know we are dealing with a uniform scalar field and
		// we want access to the voxelindices
		//HxLoc3Regular* location = image->coords()->createLocation();
		HxLoc3Regular* location = (HxLoc3Regular*)image->createLocation();
		for ( mculong e = 0; e < totalElems; e++ )
		{
			theWorkArea->setProgressValue( (1.0*e)/totalElems );

			//float sum = 0.0;
			//int count = 0;
			//McVec3f pcoords;
			McDArray<McVec3f> vertices = AmiraGrid::getElementVertices( grid, e );

			//// determine bounding box of voxel indices within element
			//location->set( vertices[0][0], vertices[0][1], vertices[0][2] );
			//int indexbbox[6] = { location->ix, location->ix,
			//					location->iy, location->iy,
			//					location->iz, location->iz };
			//for ( int el = 1; el < 8; el++ )
			//{
			//	location->move( vertices[el][0], vertices[el][1], vertices[el][2] );
			//	if ( location->ix < indexbbox[0] ) { indexbbox[0] = location->ix; }
			//	if ( location->ix > indexbbox[1] ) { indexbbox[1] = location->ix; }
			//	if ( location->iy < indexbbox[2] ) { indexbbox[2] = location->iy; }
			//	if ( location->iy > indexbbox[3] ) { indexbbox[3] = location->iy; }
			//	if ( location->iz < indexbbox[4] ) { indexbbox[4] = location->iz; }
			//	if ( location->iz > indexbbox[5] ) { indexbbox[5] = location->iz; }
			//}

			//for ( int k = indexbbox[4]; k <= indexbbox[5]; k++ )
			//{
			//	for ( int j = indexbbox[2]; j <= indexbbox[3]; j++ )
			//	{
			//		for ( int i = indexbbox[0]; i <= indexbbox[1]; i++ )
			//		{
			//			float point[3];
			//			image->lattice.coords()->pos( i, j, k, point );
			//			int inside = FEelement->getIsoParam( vertices,
			//				McVec3f(point[0],point[1], point[2]), pcoords );
			//			if ( inside == 1 )
			//			{
			//				location->move( point );
			//				float value;
			//				image->eval( location, &value );
			//				sum += value;
			//				count++;
			//			}
			//		}
			//	}
			//}
			//float aveinten = 0.0;
			//if ( count > 0 )
			//{
			//	aveinten = sum/count;
			//}
			float aveinten = this->averageVoxels( FEelement, vertices, image );
			if ( aveinten == 0 )
			{
				McVec3f p = FEelement->getCenter( vertices );
				if ( location->move( p[0], p[1], p[2] ) == 1 )
				{
					// image is valid for this location
					image->eval( location, &aveinten );
				}
			}

			// ... then convert to elasticity
			moduli[e] = this->getElasticityFromHU( aveinten );
		}
		//theMsg->printf("indices range: %d %d %d %d %d %d", xmin, xmax, ymin, ymax, zmin, zmax );
		delete location;
	}
	else if ( portAveraging.getValue() == 2 )	// Bonemat V2
	{
		// determine boundary elements if requested
		McDArray<mclong> surface;
		if ( portBoundary.getValue() == 1 )
		{
			AmiraGrid::getSurfaceElements( grid, &surface );
		}

		for ( mclong i = 0; i < totalElems; i++ )
		{
			theWorkArea->setProgressValue( (1.0*i)/totalElems );

			// first determine average hounsfield unit for element...
			McDArray<McVec3f> vertices = AmiraGrid::getElementVertices( grid, i );
			float intensity = 0.0;
			if ( index( surface, i ) != -1 )
			{
				intensity = this->averageVoxels( FEelement, vertices, image );
			}
			else
			{
				intensity = this->sampleField( FEelement, vertices, image );
			}

			// ... then convert to elasticity
			moduli[i] = this->getElasticityFromHU( intensity );
		}
	}
	else if ( portAveraging.getValue() == 3 )	// Bonemat V3
	{
		// determine boundary elements if requested
		McDArray<mclong> surface;
		if ( portBoundary.getValue() == 1 )
		{
			AmiraGrid::getSurfaceElements( grid, &surface );
		}

		// first create temporarily image with hounsfield units converted into elasticity...
		//int size[3];
		//image->lattice.getSize( size[0], size[1], size[2] );
		//float bbox[6];
		//image->coords()->getBoundingBox( bbox );
		//mculong buffersize = size[0]*size[1]*size[2];
		//float* elasbuffer = new float[buffersize];
		//float HU = 0.0;
		//for ( int i = 0; i < buffersize; i++ )
		//{
		//	theWorkArea->setProgressValue( (0.3*i)/buffersize );

		//	image->lattice.eval( i, &HU );
		//	elasbuffer[i] = myParams[0] * pow((myParams[1]*(HU + myParams[2])
		//		+ myParams[3]), myParams[4]) + myParams[5];
		//}
		//HxUniformScalarField3* elasfield = new HxUniformScalarField3( size, McPrimType::mc_float, elasbuffer );
		//elasfield->lattice.setBoundingBox( bbox );

		HxUniformScalarField3* elasfield = this->convertHUtoElasField( image );

		// ... then determine the average of the field for each element
		int count = 0;
		for ( mclong i = 0; i < totalElems; i++ )
		{
			theWorkArea->setProgressValue( 0.3 + (0.7*i)/totalElems );

			McDArray<McVec3f> vertices = AmiraGrid::getElementVertices( grid, i );
			float intensity = 0.0;
			if ( index( surface, i ) != -1 )
			{
				moduli[i] = this->averageVoxels( FEelement, vertices, elasfield );
				if (moduli[i]==0)
				{
					//theMsg->printf("averageVoxels is zero for %d", i);
					count++;
				}
			}
			else
			{
				moduli[i] = this->sampleField( FEelement, vertices, elasfield );
				if (moduli[i]==0)
				{
					theMsg->printf("sampleField is zero for %d", i);
				}
			}
		}

		// create elasticity image if requested
		if ( portElasImage.getValue() == 1 )
		{
			setResult( numResults++, elasfield );
			elasfield->composeLabel( grid->getName(), "_elasticity" );
		}
		else
		{
			delete elasfield;
		}
	}

	if ( !FEelement ) { delete FEelement; FEelement = NULL; }

	this->createBins( portBins.getValue( 0 ), moduli, grid );

	// create data field if requested
	if ( portDataField.getValue() == 1 )
	{
		if ( HxTetraGrid* tetgrid = dynamic_cast<HxTetraGrid*>(grid) )
		{
			HxTetraScalarField3* scalardata = new HxTetraScalarField3( tetgrid,HxTetraData::PER_TETRA, moduli );
			setResult( numResults++, scalardata );
			scalardata->composeLabel( grid->getName(), "_youngs" );
		}
		if ( HxHexaGrid* hexgrid = dynamic_cast<HxHexaGrid*>(grid) )
		{
			HxHexaScalarField3* scalardata = new HxHexaScalarField3( hexgrid, HxHexaData::PER_HEXA, moduli );
			setResult( numResults++, scalardata );
			scalardata->composeLabel( grid->getName(), "_youngs" );
		}
	}
	else
	{
		delete [] moduli;
	}
}