private bool IsZoomEnable(DataRect rect) { bool res = true; if (IsHorizontalNavigationEnabled) { double e_max = Math.Log(Math.Max(Math.Abs(rect.XMax), Math.Abs(rect.XMin)), 2); double log = Math.Log(rect.Width, 2); res = log > e_max - 40; } if (IsVerticalNavigationEnabled) { double e_max = Math.Log(Math.Max(Math.Abs(rect.YMax), Math.Abs(rect.YMin)), 2); double log = Math.Log(rect.Height, 2); res = res && log > e_max - 40; } return(res); }
/// <summary> /// Sets plot rectangle for current instance of <see cref="PlotBase"/> /// </summary> /// <param name="plotRect">plot rectangle value that would be set for current instance of <see cref="PlotBase"/></param> /// <param name="fromAutoFit">Identifies that it is internal call</param> protected void SetPlotRect(DataRect plotRect, bool fromAutoFit) { IsInternalChange = true; EnumAll(p => { p.PlotOriginX = plotRect.XMin; p.PlotOriginY = plotRect.YMin; p.PlotWidth = plotRect.Width; p.PlotHeight = plotRect.Height; if (!fromAutoFit) { p.IsAutoFitEnabled = false; p.InvalidateMeasure(); } }); IsInternalChange = false; }
/// <summary> /// Positions child elements and determines a size for a <see cref="Figure"/>. /// </summary> /// <param name="finalSize">The final area within the parent that Figure should use to arrange itself and its children.</param> /// <returns>The actual size used.</returns> protected override Size ArrangeOverride(Size finalSize) { finalSize.Width = Math.Min(finalSize.Width, DesiredSize.Width); finalSize.Height = Math.Min(finalSize.Height, DesiredSize.Height); // Arranging child elements foreach (UIElement elt in Children) { Image img = elt as Image; if (img != null) { Batch batch = img.Tag as Batch; if (batch != null && batch.Image == elt) // Special algorithm for snapshot image { DataRect plotRect = batch.PlotRect; var newLT = new Point(LeftFromX(plotRect.XMin), TopFromY(plotRect.YMax)); var newRB = new Point(LeftFromX(plotRect.XMax), TopFromY(plotRect.YMin)); batch.Image.Arrange(new Rect(newLT.X, newLT.Y, newRB.X - newLT.X, newRB.Y - newLT.Y)); } else { elt.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height)); } } else { elt.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height)); } } if (ClipToBounds) { Clip = new RectangleGeometry { Rect = new Rect(new Point(0, 0), finalSize) } } ; else { Clip = null; } return(finalSize); }
private void OnTaskCompleted(RenderResult r, RenderTaskState state) { if (r != null && !state.IsCanceled) { WriteableBitmap wr = new WriteableBitmap((int)r.Output.Width, (int)r.Output.Height, 96, 96, PixelFormats.Bgra32, null); // Calculate the number of bytes per pixel. int bytesPerPixel = (wr.Format.BitsPerPixel + 7) / 8; // Stride is bytes per pixel times the number of pixels. // Stride is the byte width of a single rectangle row. int stride = wr.PixelWidth * bytesPerPixel; wr.WritePixels(new Int32Rect(0, 0, wr.PixelWidth, wr.PixelHeight), r.Image, stride, 0); outputImage.Source = wr; Canvas.SetLeft(outputImage, r.Output.Left); Canvas.SetTop(outputImage, r.Output.Top); imageCartesianRect = r.Visible; imageSize = new Size(r.Output.Width, r.Output.Height); outputImage.RenderTransform = null; } RaiseTaskCompletion(state.Id); runningTasks.Remove(state); while (tasks.Count > 1) { long id = tasks.Dequeue(); RaiseTaskCompletion(id); } if (tasks.Count > 0 && runningTasks.Count < maxTasks) { EnqueueTask(tasks.Dequeue()); } InvalidateMeasure(); }
/// <summary>Performs array resize and subset with linear interpolation</summary> /// <param name="width">Result width</param> /// <param name="height">Result height</param> /// <param name="rect">Range of coordinates for entire data</param> /// <param name="x">Coordinates of x points (length of array must be <paramref name="width"/></param> /// <param name="y">Coordinates of y points (length of array must be <paramref name="height"/></param> /// <param name="data">Source data array</param> /// <param name="missingValue">Missing value or NaN if no missing value</param> /// <returns>Resulting array with size <paramref name="width"/> * <paramref name="height"/></returns> public static double[,] Filter(int width, int height, DataRect rect, double[] x, double[] y, double[,] data, double missingValue) { if (data == null) { throw new ArgumentNullException("data"); } if (x == null) { throw new ArgumentNullException("x"); } if (y == null) { throw new ArgumentNullException("y"); } // Check preconditions if (x.Length != data.GetLength(0)) { throw new ArgumentException("Size of x and data arrays does not match"); } if (y.Length != data.GetLength(1)) { throw new ArgumentException("Size of y and data arrays does not match"); } // Prepare filters int[] hp, vp, hi, vi; double[] ha, va, hw, vw; PrepareFilterTables(width, rect.XMin, rect.XMax, x, out hp, out hi, out ha, out hw); PrepareFilterTables(height, rect.YMin, rect.YMax, y, out vp, out vi, out va, out vw); // Prepare arrays double[,] r = new double[width, height]; bool hasMissingValue = !Double.IsNaN(missingValue); double offset = 0; if (hasMissingValue && missingValue == 0) { offset = -1; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { r[i, j] = offset; } } } // Do filtering int hpLen = hp.Length; int vpLen = vp.Length; for (int i = 0; i < hpLen; i++) { int px = hp[i]; int i0 = hi[i]; for (int j = 0; j < vpLen; j++) { int py = vp[j]; if (hasMissingValue && r[px, py] == missingValue) { continue; } int j0 = vi[j]; if (hasMissingValue && (data[i0, j0] == missingValue || data[i0 + 1, j0] == missingValue || data[i0, j0 + 1] == missingValue || data[i0 + 1, j0 + 1] == missingValue)) { r[px, py] = missingValue; } else { double v0 = ha[i] * data[i0, j0] + (1 - ha[i]) * data[i0 + 1, j0]; double v1 = ha[i] * data[i0, j0 + 1] + (1 - ha[i]) * data[i0 + 1, j0 + 1]; double v = va[j] * v0 + (1 - va[j]) * v1; r[px, py] += v * hw[i] * vw[j]; } } } // Offset data if needed if (offset != 0) { for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { if (r[i, j] != missingValue) { r[i, j] -= offset; } } } } return(r); }
/// <summary> /// Builds heatmap from specified data, grid, missing value, visibleRect and palette. /// </summary> /// <param name="data">2D array with data to plot</param> /// <param name="missingValue">Missing value. Heatmap will have transparent regions at missing value.</param> /// <param name="palette">Palette to translate numeric values to colors</param> /// <param name="range">Min and max values in <paramref name="data"/> array</param> /// <param name="rect">Heatmap rectangle screen coordinates</param> /// <param name="screen">Plot rectaneg screen coordinates</param> static public int[] BuildHeatMap(Rect screen, DataRect rect, double[] x, double[] y, double[,] data, double missingValue, IPalette palette, Range range) { if (data == null) { throw new ArgumentNullException("data"); } if (x == null) { throw new ArgumentNullException("x"); } if (y == null) { throw new ArgumentNullException("y"); } if (palette == null) { throw new ArgumentNullException("palette"); } int pixelWidth = (int)screen.Width; int pixelHeight = (int)screen.Height; int[] pixels = new int[pixelWidth * pixelHeight]; double doubleMissingValue = missingValue; UInt32[] paletteColors = new uint[512]; if (!palette.IsNormalized) { range = palette.Range; } for (int i = 0; i < 512; i++) { Color c; if (palette.IsNormalized) { c = palette.GetColor((double)i / 511.0); } else { c = palette.GetColor(range.Min + i * (range.Max - range.Min) / 511.0); } paletteColors[i] = (((uint)(c.A)) << 24) | (((uint)c.R) << 16) | (((uint)c.G) << 8) | c.B; } int xdimRank = x.Length; int ydimRank = y.Length; int xdataRank = data.GetLength(0); int ydataRank = data.GetLength(1); double factor = (range.Max != range.Min) ? (1.0 / (range.Max - range.Min)) : 0.0; if (xdataRank == xdimRank && ydataRank == ydimRank) { double[,] v = null; v = Filter(pixelWidth, pixelHeight, rect, x, y, data, doubleMissingValue); if (palette.IsNormalized) { if (Double.IsNaN(doubleMissingValue)) { for (int j = 0, k = 0; j < pixelHeight; j++) { for (int i = 0; i < pixelWidth; i++, k++) { double vv = v[i, pixelHeight - j - 1]; if (!Double.IsNaN(vv)) { double v01 = (vv - range.Min) * factor; pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF]; } } } } else { for (int j = 0, k = 0; j < pixelHeight; j++) { for (int i = 0; i < pixelWidth; i++, k++) { double vv = v[i, pixelHeight - j - 1]; if (vv != doubleMissingValue) { double v01 = (vv - range.Min) * factor; pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF]; } } } } } else // Palette is absolute { if (Double.IsNaN(doubleMissingValue)) { for (int j = 0, k = 0; j < pixelHeight; j++) { for (int i = 0; i < pixelWidth; i++, k++) { double vv = v[i, pixelHeight - j - 1]; if (!Double.IsNaN(vv)) { double v01 = (Math.Max(range.Min, Math.Min(range.Max, vv)) - range.Min) * factor; pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF]; } } } } else { for (int j = 0, k = 0; j < pixelHeight; j++) { for (int i = 0; i < pixelWidth; i++, k++) { double vv = v[i, pixelHeight - j - 1]; if (vv != doubleMissingValue) { double v01 = (Math.Max(range.Min, Math.Min(range.Max, vv)) - range.Min) * factor; pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF]; } } } } } } else if ((xdataRank + 1) == xdimRank && (ydataRank + 1) == ydimRank) { // Prepare arrays int[] xPixelDistrib; int[] yPixelDistrib; xPixelDistrib = CreatePixelToDataMap(pixelWidth, Math.Max(x[0], rect.XMin), Math.Min(x[x.Length - 1], rect.XMax), x); yPixelDistrib = CreatePixelToDataMap(pixelHeight, Math.Max(y[0], rect.YMin), Math.Min(y[y.Length - 1], rect.YMax), y); if (palette.IsNormalized) { if (Double.IsNaN(doubleMissingValue)) { for (int j = 0, k = 0; j < pixelHeight; j++) { for (int i = 0; i < pixelWidth; i++, k++) { double vv = data[xPixelDistrib[i], yPixelDistrib[pixelHeight - j - 1]]; if (!Double.IsNaN(vv)) { double v01 = (vv - range.Min) * factor; pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF]; } } } } else { for (int j = 0, k = 0; j < pixelHeight; j++) { for (int i = 0; i < pixelWidth; i++, k++) { double vv = data[xPixelDistrib[i], yPixelDistrib[pixelHeight - j - 1]]; if (vv != doubleMissingValue) { double v01 = (vv - range.Min) * factor; pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF]; } } } } } else // Palette is absolute { if (Double.IsNaN(doubleMissingValue)) { for (int j = 0, k = 0; j < pixelHeight; j++) { for (int i = 0; i < pixelWidth; i++, k++) { double vv = data[xPixelDistrib[i], yPixelDistrib[pixelHeight - j - 1]]; if (!Double.IsNaN(vv)) { double v01 = (Math.Max(range.Min, Math.Min(range.Max, vv)) - range.Min) * factor; pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF]; } } } } else { for (int j = 0, k = 0; j < pixelHeight; j++) { for (int i = 0; i < pixelWidth; i++, k++) { double vv = data[xPixelDistrib[i], yPixelDistrib[pixelHeight - j - 1]]; if (vv != doubleMissingValue) { double v01 = (Math.Max(range.Min, Math.Min(range.Max, vv)) - range.Min) * factor; pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF]; } } } } } } else { throw new ArgumentException("Size of x,y and data arrays does not match conditions"); } return(pixels); }
/// <summary> /// Initializes new instance of RenderTaskState class from given coordinate tranform. /// </summary> public RenderTaskState(DataRect actualPlotRect, Size screenSize) { ScreenSize = screenSize; ActualPlotRect = actualPlotRect; }
/// <summary> /// Initializes new instance of RenderResult class form given parameters. /// </summary> /// <param name="image">Array of image pixels.</param> /// <param name="visible">Visible rect for graph.</param> /// <param name="offset">Image start offset.</param> /// <param name="width">Image width.</param> /// <param name="height">image height.</param> public RenderResult(int[] image, DataRect visible, Point offset, double width, double height) { this.visible = visible; this.output = new Rect(offset, new Size(width, height)); this.image = image; }
/// <summary> /// Measures the size in layout required for child elements and determines a size for a <see cref="Figure"/>. /// </summary> /// <param name="availableSize">The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.</param> /// <returns>The size that this element determines it needs during layout, based on its calculations of child element sizes.</returns> protected override Size MeasureOverride(Size availableSize) { // Update number of batches SyncMarkerViewModels(); int batchCount = (int)Math.Ceiling(models.Count / (double)MarkersBatchSize); while (batches.Count < batchCount) { var b = new Batch(); batches.Add(b); Children.Add(b.Image); Children.Add(b.Panel); // Ensure inner plots have the same data transforms b.Panel.SetBinding(PlotBase.XDataTransformProperty, new Binding("XDataTransform") { Source = this }); b.Panel.SetBinding(PlotBase.YDataTransformProperty, new Binding("YDataTransform") { Source = this }); } availableSize = PerformAsMaster(availableSize); // Request redraw of all batches if transform is changed if (ScaleX != prevScaleX || ScaleY != prevScaleY || OffsetX != prevOffsetX || OffsetY != prevOffsetY) { prevScaleX = ScaleX; prevScaleY = ScaleY; prevOffsetX = OffsetX; prevOffsetY = OffsetY; idleTask.Start(); plotVersion++; foreach (var b in batches) { b.ChangedProperties = null; // Update all properties b.Panel.Visibility = System.Windows.Visibility.Collapsed; b.Image.Visibility = System.Windows.Visibility.Visible; } } foreach (UIElement elt in Children) { Image img = elt as Image; if (img != null) { Batch batch = img.Tag as Batch; if (batch != null && batch.Image == elt) // Special algorithm for snapshot image { DataRect plotRect = batch.PlotRect; var newLT = new Point(LeftFromX(plotRect.XMin), TopFromY(plotRect.YMax)); var newRB = new Point(LeftFromX(plotRect.XMax), TopFromY(plotRect.YMin)); batch.Image.Measure(new Size(newRB.X - newLT.X, newRB.Y - newLT.Y)); } else { elt.Measure(availableSize); } } else { elt.Measure(availableSize); } } return(availableSize); }
/// <summary> /// Sets plot rectangle for current instance of <see cref="PlotBase"/> /// </summary> /// <param name="plotRect">plot rectangle value that would be set for current instance of <see cref="PlotBase"/></param> public void SetPlotRect(DataRect plotRect) { SetPlotRect(plotRect, false); }
/// <summary> /// Measures the size in layout required for child elements and determines a size for the Figure. /// </summary> /// <param name="availableSize">The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.</param> /// <returns>The size that this element determines it needs during layout, based on its calculations of child element sizes.</returns> protected override Size MeasureOverride(Size availableSize) { // Assuming we are master panel var topElts = Children.Cast <UIElement>().Where(elt => GetPlacement(elt) == Placement.Top); var bottomElts = Children.Cast <UIElement>().Where(elt => GetPlacement(elt) == Placement.Bottom); var centerElts = Children.Cast <UIElement>().Where(elt => GetPlacement(elt) == Placement.Center); var rightElts = Children.Cast <UIElement>().Where(elt => GetPlacement(elt) == Placement.Right); var leftElts = Children.Cast <UIElement>().Where(elt => GetPlacement(elt) == Placement.Left); DataRect desiredRect; if (IsAutoFitEnabled) { desiredRect = AggregateBounds(); if (desiredRect.IsEmpty) { desiredRect = new DataRect(0, 0, 1, 1); } SetPlotRect(desiredRect, true); } else // resize { desiredRect = PlotRect; } //First Iteration: Measuring top and bottom slots, //then meassuring left and right with top and bottom output values // Create transform for first iteration if (double.IsNaN(availableSize.Width) || double.IsNaN(availableSize.Height) || double.IsInfinity(availableSize.Width) || double.IsInfinity(availableSize.Height)) { availableSize = new Size(100, 100); } Fit(desiredRect, availableSize); // Measure top and bottom slots double topBottomWidth = 0; double topBottomHeight = 0; topHeight = 0; bottomHeight = 0; foreach (var elt in topElts) { elt.Measure(availableSize); var ds = elt.DesiredSize; topBottomWidth = Math.Max(topBottomWidth, ds.Width); topHeight += ds.Height; } topBottomHeight += topHeight; foreach (var elt in bottomElts) { elt.Measure(availableSize); var ds = elt.DesiredSize; topBottomWidth = Math.Max(topBottomWidth, ds.Width); bottomHeight += ds.Height; } topBottomHeight += bottomHeight; // Measure left and right slots double leftRightWidth = 0; double leftRightHeight = 0; leftWidth = 0; rightWidth = 0; foreach (var elt in leftElts) { elt.Measure(availableSize); var ds = elt.DesiredSize; leftRightHeight = Math.Max(leftRightHeight, ds.Height); leftWidth += ds.Width; } leftRightWidth += leftWidth; foreach (var elt in rightElts) { elt.Measure(availableSize); var ds = elt.DesiredSize; leftRightHeight = Math.Max(leftRightHeight, ds.Height); rightWidth += ds.Width; } leftRightWidth += rightWidth; //Measure center elements Size availCenterSize = new Size(Math.Max(0, availableSize.Width - leftRightWidth), Math.Max(0, availableSize.Height - topBottomHeight)); Fit(desiredRect, availCenterSize); foreach (var elt in centerElts) { elt.Measure(availCenterSize); } // Remeasure top and bottom slots double topBottomWidth2 = 0; double topBottomHeight2 = 0; topHeight2 = 0; bottomHeight2 = 0; foreach (var elt in topElts) { elt.Measure(new Size(availCenterSize.Width, elt.DesiredSize.Height)); var ds = elt.DesiredSize; topBottomWidth2 = Math.Max(topBottomWidth2, ds.Width); topHeight2 += ds.Height; } topBottomHeight2 += topHeight2; foreach (var elt in bottomElts) { elt.Measure(new Size(availCenterSize.Width, elt.DesiredSize.Height)); var ds = elt.DesiredSize; topBottomWidth2 = Math.Max(topBottomWidth2, ds.Width); bottomHeight2 += ds.Height; } topBottomHeight2 += bottomHeight2; //Scaling elements of their new meassured height it not equal to first if (bottomHeight2 > bottomHeight) { ScaleTransform transform = new ScaleTransform { ScaleY = bottomHeight / bottomHeight2 }; foreach (var elt in bottomElts) { elt.RenderTransform = transform; } } if (topHeight2 > topHeight) { ScaleTransform transform = new ScaleTransform { ScaleY = topHeight / topHeight2 }; foreach (var elt in topElts) { elt.RenderTransform = transform; } } // ReMeasure left and right slots double leftRightWidth2 = 0; double leftRightHeight2 = 0; leftWidth2 = 0; rightWidth2 = 0; foreach (var elt in leftElts) { elt.Measure(new Size(elt.DesiredSize.Width, availCenterSize.Height)); var ds = elt.DesiredSize; leftRightHeight2 = Math.Max(leftRightHeight2, ds.Height); leftWidth2 += ds.Width; } leftRightWidth2 += leftWidth2; foreach (var elt in rightElts) { elt.Measure(new Size(elt.DesiredSize.Width, availCenterSize.Height)); var ds = elt.DesiredSize; leftRightHeight2 = Math.Max(leftRightHeight2, ds.Height); rightWidth2 += ds.Width; } leftRightWidth2 += rightWidth2; //Scaling elements of their new meassured height it not equal to first if (leftWidth2 > leftWidth) { ScaleTransform transform = new ScaleTransform { ScaleX = leftWidth / leftWidth2 }; foreach (var elt in leftElts) { elt.RenderTransform = transform; } } if (rightWidth2 > rightWidth) { ScaleTransform transform = new ScaleTransform { ScaleX = rightWidth / rightWidth2 }; foreach (var elt in rightElts) { elt.RenderTransform = transform; } } centerSize = availCenterSize; return(new Size(availCenterSize.Width + leftRightWidth, availCenterSize.Height + topBottomHeight)); }
/// <summary> /// Updates current instance of <see cref="DataRect"/> with minimal DataRect which will contain current DataRect and specified DataRect /// </summary> /// <param name="rect">DataRect, which will be used for surrond of current instance of <see cref="DataRect"/></param> public void Surround(DataRect rect) { this.x.Surround(rect.X); this.y.Surround(rect.Y); }
/// <summary> /// Renders frame and returns it as a render result. /// </summary> /// <param name="state">Render task state for rendering frame.</param> /// <returns>Render result of rendered frame.</returns> protected override RenderResult RenderFrame(RenderTaskState state) { if (state == null) { throw new ArgumentNullException("state"); } if (!state.Bounds.IsEmpty && !state.IsCanceled && data != null) { //DataRect dataRect = new DataRect(state.Transform.Visible); //Rect output = state.Transform.Screen; DataRect dataRect = state.ActualPlotRect; DataRect output = new DataRect(0, 0, state.ScreenSize.Width, state.ScreenSize.Height); DataRect bounds = state.Bounds; if (dataRect.XMin >= bounds.XMax || dataRect.XMax <= bounds.XMin || dataRect.YMin >= bounds.YMax || dataRect.YMax <= bounds.YMin) { return(null); } double left = 0; double xmin = dataRect.XMin; double scale = output.Width / dataRect.Width; if (xmin < bounds.XMin) { left = (bounds.XMin - dataRect.XMin) * scale; xmin = bounds.XMin; } double width = output.Width - left; double xmax = dataRect.XMax; if (xmax > bounds.XMax) { width -= (dataRect.XMax - bounds.XMax) * scale; xmax = bounds.XMax; } scale = output.Height / dataRect.Height; double top = 0; double ymax = dataRect.YMax; if (ymax > bounds.YMax) { top = (dataRect.YMax - bounds.YMax) * scale; ymax = bounds.YMax; } double height = output.Height - top; double ymin = dataRect.YMin; if (ymin < bounds.YMin) { height -= (bounds.YMin - dataRect.YMin) * scale; ymin = bounds.YMin; } if (xmin < bounds.XMin) { xmin = bounds.XMin; } if (xmax > bounds.XMax) { xmax = bounds.XMax; } if (ymin < bounds.YMin) { ymin = bounds.YMin; } if (ymax > bounds.YMax) { ymax = bounds.YMax; } DataRect visibleData = new DataRect(xmin, ymin, xmax, ymax); // Capture data to local variable double[,] localData; double[] localX, localY; long localDataVersion; IPalette localPalette; double localMV; Range localDataRange; bool getMaxMin = false; lock (locker) { localData = data; localX = xArr; localY = yArr; localDataVersion = dataVersion; localPalette = palette; localMV = missingValue; localDataRange = dataRange; if (palette.IsNormalized && dataVersion != dataRangeVersion) { getMaxMin = true; } } if (getMaxMin) { localDataRange = Double.IsNaN(missingValue) ? HeatmapBuilder.GetMaxMin(data) : HeatmapBuilder.GetMaxMin(data, missingValue); lock (locker) { if (dataVersion == localDataVersion) { dataRangeVersion = dataVersion; dataRange = localDataRange; } else { return(null); // New data was passed to Plot method so this render task is obsolete } } } if (paletteRangeUpdateRequired) { Dispatcher.BeginInvoke(new Action <long>(UpdatePaletteRange), localDataVersion); } return(new RenderResult(HeatmapBuilder.BuildHeatMap(new Rect(0, 0, width, height), visibleData, localX, localY, localData, localMV, localPalette, localDataRange), visibleData, new Point(left, top), width, height)); } else { return(null); } }