public unsafe VTKUnityStructuredGrid(VTKDataset vtkDataset, UInt32 desiredDensity, IDataProvider dataProvider) { m_dataset = vtkDataset; m_desiredDensity = desiredDensity; m_dataProvider = dataProvider; VTKParser parser = m_dataset.Parser; if (parser.GetDatasetType() != VTKDatasetType.VTK_STRUCTURED_POINTS) { Debug.Log("Error: The dataset should be a structured points dataset"); return; } //Get the points and modify the points / normals buffer m_ptsDesc = parser.GetStructuredPointsDescriptor(); //Determine the new dimension and spacing m_dimensions = GetDisplayableSize(); float maxAxis = (float)Math.Max(m_ptsDesc.Spacing[0] * m_ptsDesc.Size[0], Math.Max(m_ptsDesc.Spacing[1] * m_ptsDesc.Size[1], m_ptsDesc.Spacing[2] * m_ptsDesc.Size[2])); for (int i = 0; i < 3; i++) { m_spacing[i] = (float)(m_ptsDesc.Size[i] * m_ptsDesc.Spacing[i] / m_dimensions[i] / maxAxis); } //Small multiples array m_smallMultiples = new List <VTKUnitySmallMultiple>(); }
/// <summary> /// Initialize the small multiple /// </summary> /// <param name="parser">The VTK Parser</param> /// <param name="subDataset">The SubDataset bound to this VTK view</param> /// <param name="dimensions">The dimensions in use</param> /// <param name="dataProvider">The application data provider (a.k.a, Main)</param> /// <returns></returns> public unsafe bool Init(VTKParser parser, SubDataset subDataset, Vector3Int dimensions, IDataProvider dataProvider) { subDataset.AddListener(this); m_subDataset = subDataset; m_dataProvider = dataProvider; //Copy the variables m_dimensions = dimensions; VTKStructuredPoints descPts = parser.GetStructuredPointsDescriptor(); m_descPts = descPts; float maxAxis = (float)Math.Max(descPts.Spacing[0] * m_dimensions[0], Math.Max(descPts.Spacing[1] * m_dimensions[1], descPts.Spacing[2] * m_dimensions[2])); m_spacing = new Vector3(); for (int i = 0; i < 3; i++) { m_spacing[i] = (float)(descPts.Spacing[i] / maxAxis); } UpdateTF(); return(true); }
/// <summary> /// get the displayable size of the vector field. Indeed, due to hardware limitation, we cannot display all the vector field at once /// </summary> /// <returns>The vector field size displayable</returns> private Vector3Int GetDisplayableSize() { VTKStructuredPoints m_ptsDesc = m_dataset.Parser.GetStructuredPointsDescriptor(); if (m_ptsDesc.Size[0] == 0 || m_ptsDesc.Size[1] == 0 || m_ptsDesc.Size[2] == 0) { return(new Vector3Int(0, 0, 0)); } int maxRatio = (int)GetFieldSizeDiviser(); return(new Vector3Int((int)m_ptsDesc.Size[0] / maxRatio, (int)m_ptsDesc.Size[1] / maxRatio, (int)m_ptsDesc.Size[2] / maxRatio)); }
/// <summary> /// Get the size diviser used for the displayability of the dataset (structured grid) /// </summary> /// <returns>The field diviser applied along all axis</returns> public UInt32 GetFieldSizeDiviser() { VTKStructuredPoints m_ptsDesc = m_dataset.Parser.GetStructuredPointsDescriptor(); if (DesiredDensity == 0) { return(1); } UInt32 x = (m_ptsDesc.Size[0] + DesiredDensity - 1) / DesiredDensity; UInt32 y = (m_ptsDesc.Size[1] + DesiredDensity - 1) / DesiredDensity; UInt32 z = (m_ptsDesc.Size[2] + DesiredDensity - 1) / DesiredDensity; return(Math.Max(1, Math.Max(Math.Max(x, y), z))); }
static void Main(String[] argv) { if (argv.Length < 1) { Console.WriteLine("Needs the path to the dataset"); return; } VTKParser parser = new VTKParser(argv[0]); if (!parser.Parse()) { Console.WriteLine($"Fail to parse the file {argv[0]}"); return; } if (parser.GetDatasetType() == VTKDatasetType.VTK_UNSTRUCTURED_GRID) { Console.WriteLine("Unstructured grid"); return; } else if (parser.GetDatasetType() == VTKDatasetType.VTK_STRUCTURED_POINTS) { Console.WriteLine("Structured points"); VTKStructuredPoints pts = parser.GetStructuredPointsDescriptor(); Console.WriteLine($"Structured points : dimensions {pts.Size[0]}, {pts.Size[1]}, {pts.Size[2]}"); Console.WriteLine($"Structured points : spacing {pts.Spacing[0]:F4}, {pts.Spacing[1]:F4}, {pts.Spacing[2]:F4}"); Console.WriteLine($"Structured points : origin {pts.Origin[0]:F4}, {pts.Origin[1]:F4}, {pts.Origin[2]:F4}"); List <VTKFieldValue> fieldDesc = parser.GetPointFieldValueDescriptors(); if (fieldDesc.Count > 0) { foreach (var f in fieldDesc) { Console.WriteLine($"Found {f.Name} with {f.NbTuples} values"); } } else { Console.WriteLine("No value found..."); } return; } }
public override uint GetNbSpatialValues() { VTKStructuredPoints p = Parser.GetStructuredPointsDescriptor(); return(p.Size[0] * p.Size[1] * p.Size[2]); }
protected override Gradient ComputeGradient(int[] indices) { if (indices.Count() == 0) { return(null); } Gradient gradient = new Gradient(indices); for (int t = 0; t < m_nbTimesteps; t++) { VTKStructuredPoints ptsDesc = Parser.GetStructuredPointsDescriptor(); float[] grad = new float[ptsDesc.Size[0] * ptsDesc.Size[1] * ptsDesc.Size[2]]; float maxVal = 0; object lockObject = new object(); /////////////////////////////// ///Compute "Inside" Gradient/// /////////////////////////////// //Multi dimensionnal gradient computation, based on //Joe Kniss, Gordon Kindlmann, and Charles Hansen. 2002. Multidimensional Transfer Functions for Interactive Volume Rendering. IEEE Transactions on Visualization and Computer Graphics 8, 3 (July 2002), 270-285. DOI: https://doi.org/10.1109/TVCG.2002.1021579 if (indices.Count() > 1) { Parallel.For(1, ptsDesc.Size[2] - 1, () => new { maxGrad = new float[1] { float.MinValue }, df = new float[3 * indices.Count()], localGrad = new float[3], g = new float[9] }, (k, loopState, partialRes) => { for (UInt32 j = 1; j < ptsDesc.Size[1] - 1; j++) { for (UInt32 i = 1; i < ptsDesc.Size[0] - 1; i++) { //Indices UInt64 ind = (UInt64)(i + j * ptsDesc.Size[0] + k * ptsDesc.Size[1] * ptsDesc.Size[0]); //Read mask unsafe { if (m_mask != null && ((byte *)m_mask.Value)[ind] == 0) { grad[ind] = 0; continue; } } UInt64 indX1 = ind - 1; UInt64 indX2 = ind + 1; UInt64 indY1 = ind - (UInt64)(ptsDesc.Size[0]); UInt64 indY2 = ind + (UInt64)(ptsDesc.Size[0]); UInt64 indZ1 = ind - (UInt64)(ptsDesc.Size[1] * ptsDesc.Size[0]); UInt64 indZ2 = ind + (UInt64)(ptsDesc.Size[1] * ptsDesc.Size[0]); //Start computing the Df matrix. for (int l = 0; l < indices.Count(); l++) { int ids = indices[l]; if (m_ptFieldDescs[ids].NbValuesPerTuple == 1) { partialRes.localGrad[0] = (m_ptFieldDescs[ids].Value[t].ReadAsFloat(indX2) - m_ptFieldDescs[ids].Value[t].ReadAsFloat(indX1)) / (2.0f * (float)ptsDesc.Spacing[0]); partialRes.localGrad[1] = (m_ptFieldDescs[ids].Value[t].ReadAsFloat(indY2) - m_ptFieldDescs[ids].Value[t].ReadAsFloat(indY1)) / (2.0f * (float)ptsDesc.Spacing[1]); partialRes.localGrad[2] = (m_ptFieldDescs[ids].Value[t].ReadAsFloat(indZ2) - m_ptFieldDescs[ids].Value[t].ReadAsFloat(indZ1)) / (2.0f * (float)ptsDesc.Spacing[2]); partialRes.localGrad[2] = 0.0f; } else { partialRes.localGrad[0] = (m_ptFieldDescs[ids].ReadMagnitude(indX2, t) - m_ptFieldDescs[ids].ReadMagnitude(indX1, t)) / (2.0f * (float)ptsDesc.Spacing[0]); partialRes.localGrad[1] = (m_ptFieldDescs[ids].ReadMagnitude(indY2, t) - m_ptFieldDescs[ids].ReadMagnitude(indY1, t)) / (2.0f * (float)ptsDesc.Spacing[1]); partialRes.localGrad[2] = (m_ptFieldDescs[ids].ReadMagnitude(indZ2, t) - m_ptFieldDescs[ids].ReadMagnitude(indZ1, t)) / (2.0f * (float)ptsDesc.Spacing[2]); partialRes.localGrad[2] = 0.0f; } //Fill df for (int ii = 0; ii < 3; ii++) { partialRes.df[3 * l + ii] = partialRes.localGrad[ii] / (m_ptFieldDescs[ids].MaxVal - m_ptFieldDescs[ids].MinVal); } } //Compute g = Df^T * Df for (UInt32 l = 0; l < 9; l++) { partialRes.g[l] = 0; } for (UInt32 n = 0; n < indices.Count(); n++) { for (UInt32 l = 0; l < 3; l++) { for (UInt32 m = 0; m < 3; m++) { partialRes.g[3 * l + m] += partialRes.df[3 * n + l] * partialRes.df[3 * n + m]; } } } //Grad == L2 norm of g. //Taking sqrt(sum_{k=0}^9 g[k]*g[k]) is a good approximation based on Kniss et al. paper float gradMag = 0; for (UInt32 l = 0; l < 9; l++) { gradMag += partialRes.g[l] * partialRes.g[l]; } if (!float.IsNaN(gradMag)) { gradMag = (float)Math.Sqrt(gradMag); grad[ind] = gradMag; partialRes.maxGrad[0] = Math.Max(partialRes.maxGrad[0], gradMag); } else { grad[ind] = 0; } } } return(partialRes); }, (partialRes) => { lock (lockObject) { maxVal = Math.Max(maxVal, partialRes.maxGrad[0]); } }); } //1D gradient computation else if (m_ptFieldDescs[indices[0]].NbValuesPerTuple == 1) { int ids = indices[0]; Parallel.For(1, ptsDesc.Size[2] - 1, () => new { maxGrad = new float[1] { 0 }, localGrad = new float[3] }, (k, loopState, partialRes) => { for (UInt32 j = 1; j < ptsDesc.Size[1] - 1; j++) { for (UInt32 i = 1; i < ptsDesc.Size[0] - 1; i++) { //Indices UInt64 ind = (UInt64)(i + j * ptsDesc.Size[0] + k * ptsDesc.Size[1] * ptsDesc.Size[0]); //Read mask unsafe { if (m_mask != null && ((byte *)m_mask.Value)[ind] == 0) { grad[ind] = 0; continue; } } UInt64 indX1 = ind - 1; UInt64 indX2 = ind + 1; UInt64 indY1 = ind - (UInt64)(ptsDesc.Size[0]); UInt64 indY2 = ind + (UInt64)(ptsDesc.Size[0]); UInt64 indZ1 = ind - (UInt64)(ptsDesc.Size[1] * ptsDesc.Size[0]); UInt64 indZ2 = ind + (UInt64)(ptsDesc.Size[1] * ptsDesc.Size[0]); partialRes.localGrad[0] = (m_ptFieldDescs[ids].Value[t].ReadAsFloat(indX2) - m_ptFieldDescs[ids].Value[t].ReadAsFloat(indX1)) / (2.0f * (float)ptsDesc.Spacing[0]); partialRes.localGrad[1] = (m_ptFieldDescs[ids].Value[t].ReadAsFloat(indY2) - m_ptFieldDescs[ids].Value[t].ReadAsFloat(indY1)) / (2.0f * (float)ptsDesc.Spacing[1]); partialRes.localGrad[2] = (m_ptFieldDescs[ids].Value[t].ReadAsFloat(indZ2) - m_ptFieldDescs[ids].Value[t].ReadAsFloat(indZ1)) / (2.0f * (float)ptsDesc.Spacing[2]); partialRes.localGrad[2] = 0.0f; float gradMag = 0; for (UInt32 l = 0; l < 3; l++) { partialRes.localGrad[l] /= (m_ptFieldDescs[ids].MaxVal - m_ptFieldDescs[ids].MinVal); gradMag += partialRes.localGrad[l] * partialRes.localGrad[l]; } if (!float.IsNaN(gradMag)) { gradMag = (float)Math.Sqrt(gradMag); grad[ind] = gradMag; partialRes.maxGrad[0] = Math.Max(partialRes.maxGrad[0], gradMag); } else { grad[ind] = 0; } } } return(partialRes); }, (partialRes) => { lock (lockObject) { maxVal = Math.Max(maxVal, partialRes.maxGrad[0]); } }); } else { int ids = indices[0]; Parallel.For(1, ptsDesc.Size[2] - 1, () => new { maxGrad = new float[1] { 0 }, localGrad = new float[3] }, (k, loopState, partialRes) => { for (UInt32 j = 1; j < ptsDesc.Size[1] - 1; j++) { for (UInt32 i = 1; i < ptsDesc.Size[0] - 1; i++) { //Indices UInt64 ind = (UInt64)(i + j * ptsDesc.Size[0] + k * ptsDesc.Size[1] * ptsDesc.Size[0]); //Read mask unsafe { if (m_mask != null && ((byte *)m_mask.Value)[ind] == 0) { grad[ind] = 0; continue; } } UInt64 indX1 = ind - 1; UInt64 indX2 = ind + 1; UInt64 indY1 = ind - (UInt64)(ptsDesc.Size[0]); UInt64 indY2 = ind + (UInt64)(ptsDesc.Size[0]); UInt64 indZ1 = ind - (UInt64)(ptsDesc.Size[1] * ptsDesc.Size[0]); UInt64 indZ2 = ind + (UInt64)(ptsDesc.Size[1] * ptsDesc.Size[0]); partialRes.localGrad[0] = (m_ptFieldDescs[ids].ReadMagnitude(indX2, t) - m_ptFieldDescs[ids].ReadMagnitude(indX1, t)) / (2.0f * (float)ptsDesc.Spacing[0]); partialRes.localGrad[1] = (m_ptFieldDescs[ids].ReadMagnitude(indY2, t) - m_ptFieldDescs[ids].ReadMagnitude(indY1, t)) / (2.0f * (float)ptsDesc.Spacing[1]); partialRes.localGrad[2] = (m_ptFieldDescs[ids].ReadMagnitude(indZ2, t) - m_ptFieldDescs[ids].ReadMagnitude(indZ1, t)) / (2.0f * (float)ptsDesc.Spacing[2]); partialRes.localGrad[2] = 0.0f; float gradMag = 0; for (UInt32 l = 0; l < 3; l++) { partialRes.localGrad[l] /= (m_ptFieldDescs[ids].MaxVal - m_ptFieldDescs[ids].MinVal); gradMag += partialRes.localGrad[l] * partialRes.localGrad[l]; } if (!float.IsNaN(gradMag)) { gradMag = (float)Math.Sqrt(gradMag); grad[ind] = gradMag; partialRes.maxGrad[0] = Math.Max(partialRes.maxGrad[0], gradMag); } else { grad[ind] = 0; } } } return(partialRes); }, (partialRes) => { lock (lockObject) { maxVal = Math.Max(maxVal, partialRes.maxGrad[0]); } }); } //////////////////////////////// //////Set boundaries to 0/////// //////////////////////////////// //When gradient is not computable - default 0.0f Parallel.For(0, ptsDesc.Size[1], j => { for (UInt32 i = 0; i < ptsDesc.Size[0]; i++) { UInt64 colorValueOff1 = (UInt64)(i + ptsDesc.Size[0] * j); UInt64 colorValueOff2 = (UInt64)(i + ptsDesc.Size[0] * j + ptsDesc.Size[0] * ptsDesc.Size[1] * (ptsDesc.Size[2] - 1)); grad[colorValueOff1] = grad[colorValueOff2] = 0.0f; } }); Parallel.For(0, ptsDesc.Size[2], k => { for (UInt32 i = 0; i < ptsDesc.Size[0]; i++) { UInt64 colorValueOff1 = (UInt64)(i + ptsDesc.Size[0] * ptsDesc.Size[1] * k); UInt64 colorValueOff2 = (UInt64)(i + ptsDesc.Size[0] * (ptsDesc.Size[1] - 1) + ptsDesc.Size[0] * ptsDesc.Size[1] * k); grad[colorValueOff1] = grad[colorValueOff2] = 0.0f; } }); Parallel.For(0, ptsDesc.Size[2], k => { for (UInt32 j = 0; j < ptsDesc.Size[1]; j++) { UInt64 colorValueOff1 = (UInt64)(ptsDesc.Size[0] * j + ptsDesc.Size[0] * ptsDesc.Size[1] * k); UInt64 colorValueOff2 = (UInt64)(ptsDesc.Size[0] - 1 + ptsDesc.Size[0] * j + ptsDesc.Size[0] * ptsDesc.Size[1] * k); grad[colorValueOff1] = grad[colorValueOff2] = 0.0f; } }); gradient.Values.Add(grad); } return(gradient); }