public void AddChunkDrawdataToMeshQueue()
    {
        int3 chunkSize = CONST.chunkSize;

        ChunkDrawData chunkDrawData = new ChunkDrawData(new List <Vector3>(), new List <int>(), new List <Vector2>(), this);

        int offset = 0;

        BlockDrawInfo drawData;

        for (int x = 0; x < chunkSize.x; x++)
        {
            for (int y = 0; y < chunkSize.y; y++)
            {
                for (int z = 0; z < chunkSize.z; z++)
                {
                    if (getBlock(x, y, z) != BlockType.Air)
                    {
                        drawData = getBlockDrawData(x, y, z, offset);

                        chunkDrawData.vertexList.AddRange(drawData.blockVertexList);
                        chunkDrawData.UVList.AddRange(drawData.blockUVList);

                        /*
                         * int j = 0;
                         * foreach (int val in drawData.blockTriangleList)
                         * {
                         *  drawData.blockTriangleList[j] = val + offset;
                         *  j++;
                         * }
                         */

                        offset += drawData.triangleCount;

                        chunkDrawData.triangleList.AddRange(drawData.blockTriangleList);
                    }
                }
            }
        }

        ChunkDrawDataArray cdda = new ChunkDrawDataArray(chunkDrawData.vertexList.ToArray(),
                                                         chunkDrawData.triangleList.ToArray(), chunkDrawData.UVList.ToArray(), this);

        MeshGenerationQueue.AddToQueue(cdda);
    }
    public void AddChunkDrawdataToMeshQueue(System.Object stateInfo)
    {
        int3 chunkSize = CONST.chunkSize;

        ChunkDrawData chunkDrawData = new ChunkDrawData(new List<Vector3>(), new List<int>(), new List<Vector2>(),this);

        int offset = 0;
        BlockDrawInfo drawData;
        for (int x = 0; x < chunkSize.x; x++)
        {
            for (int y = 0; y < chunkSize.y; y++)
            {
                for (int z = 0; z < chunkSize.z; z++)
                {
                    if (blocks[x, y, z].blockType != BlockType.Air)
                    {

                        drawData = getBlockDrawData(x, y, z);

                        chunkDrawData.vertexList.AddRange(drawData.blockVertexList);
                        chunkDrawData.UVList.AddRange(drawData.blockUVList);

                        int j = 0;
                        foreach (int val in drawData.blockTriangleList)
                        {
                            drawData.blockTriangleList[j] = val + offset;
                            j++;
                        }

                        offset += drawData.triangleCount;

                        chunkDrawData.triangleList.AddRange(drawData.blockTriangleList);
                    }
                }
            }
        }

        ChunkDrawDataArray cdda = new ChunkDrawDataArray(chunkDrawData.vertexList.ToArray(),
            chunkDrawData.triangleList.ToArray(), chunkDrawData.UVList.ToArray(), this);

        MeshGenerationQueue.AddToQueue(cdda);
    }
        void DrawLastDelayedAndForcedDrawChunks(DrawingContext drawingContext, ChunkDrawData drawData, List <IEventDataNode> forceDrawChunks)
        {
            if (drawData != null)
            {
                drawingContext.DrawRectangle(drawData.Fill, drawData.Pen, drawData.Rect);
            }

            foreach (var forcedChunk in forceDrawChunks)
            {
                Brush brush = this.SelectedFill;
                Pen   pen   = this.selectedPen;

                if (forcedChunk == this.hotChunk)
                {
                    brush = this.HighlightedFill;
                    pen   = this.highlightedPen;
                }

                DrawChunk(drawingContext, forcedChunk, brush, pen, null, forceDraw: true);
            }
        }
        ChunkDrawData DrawChunk(DrawingContext drawingContext, IEventDataNode chunk, Brush brush, Pen pen, ChunkDrawData previousChunkData, bool forceDraw)
        {
            var x1 = TimeAxis.TimeToScreen(chunk.StartTime);
            var x2 = Math.Max(x1, TimeAxis.TimeToScreen(chunk.StartTime + chunk.Duration));  // Max in case the duration is bogus

            if (x2 < 0 || x1 > this.ActualWidth)
            {
                if (previousChunkData != null)
                {
                    drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect);
                }

                return(null);
            }

            var r = new Rect(new Point(x1, verticalMargin), new Size(x2 - x1, this.ActualHeight - (verticalMargin * 2)));
            var halfHeightRect = new Rect(new Point(x1, (this.ActualHeight - halfHeight) / 2), new Size(x2 - x1, halfHeight));
            var style          = chunk.Style;

            if (pen == null)
            {
                // Since there is no stroke (pen == null), this is a "normal" chunk with no border
                // (the bar has already been filled w/ border color) so deflate 1/2 pixel to simulate
                // a border stroke.
                r.Inflate(-0.5, -0.5);
            }
            else
            {
                r.Inflate(0, -0.5);
            }

            const int minWidth = 3;

            // Points-in-time (markers) are always 3px wide
            if (style == EventRenderStyle.ParentedMarker)
            {
                r = new Rect(x1 - Math.Floor((double)minWidth / 2), verticalMargin, minWidth, this.ActualHeight - (verticalMargin * 2));
            }

            // Adjust rect height based on chunk nesting depth
            if (chunk.ZIndex > 0 && !r.IsEmpty && style != EventRenderStyle.HalfHeight)
            {
                double delta = chunk.ZIndex * 2;
                r.Inflate(0, -delta);
            }

            if (forceDraw)
            {
                // This is a special (hot or selected) chunk, so enforce a minimum width and always draw it.
                if (style == EventRenderStyle.HalfHeight)
                {
                    r = halfHeightRect;
                }

                var width = r.Width;
                if (width < minWidth)
                {
                    var halfGap = (minWidth - width) / 2;
                    r = new Rect(new Point(r.Left - halfGap, r.Top), new Point(r.Right + halfGap, r.Bottom));
                }
                drawingContext.DrawRectangle(brush, pen, r);
                return(null);
            }

            const double minDistance = 16;

            if (style == EventRenderStyle.HalfHeight)
            {
                r = halfHeightRect;
            }

            // Draw the chunk.  We may need to merge it with the previous rect if there is one.
            if (previousChunkData != null)
            {
                // We have a previous rect.  If it's far enough away from this one, then draw it.
                // OR, if this one is big enough BY ITSELF to be drawn, draw the previous one.
                // Otherwise, merge it with this one, and draw the result if it's wide enough.
                if (previousChunkData.Rect.Right < (r.Left - (minDistance / 4)) || (r.Width >= minDistance))
                {
                    drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect);
                }
                else
                {
                    previousChunkData.Rect = Rect.Union(previousChunkData.Rect, r);
                    previousChunkData.Fill = Brushes.Gray;

                    if (previousChunkData.Rect.Width >= minDistance)
                    {
                        drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect);
                        previousChunkData = null;
                    }

                    return(previousChunkData);
                }
            }

            if (r.Width >= minDistance)
            {
                drawingContext.DrawRectangle(brush, pen, r);
                previousChunkData = null;
            }
            else
            {
                previousChunkData = new ChunkDrawData {
                    Fill = brush, Rect = r, Pen = pen
                };
            }

            return(previousChunkData);
        }
        public override void Draw(DrawingContext drawingContext, ulong tickMinVis, ulong tickMaxVis)
        {
            Rect r = new Rect(new Point(0, 0), new Size(this.ActualWidth, this.ActualHeight));

            // Use a slightly oversize clip region.  Otherwise the user wouldn't see the left edge
            // of the outline on an event starting right at zero.
            Rect r2 = new Rect(new Point(-2, 0), new Size(this.ActualWidth + 4, this.ActualHeight));
            RectangleGeometry rg = new RectangleGeometry(r2);

            drawingContext.PushClip(rg);

            drawingContext.DrawRectangle(Brushes.Transparent, null, r);

            drawingContext.DrawLine(axisPen, new Point(0, this.ActualHeight / 2), new Point(this.ActualWidth, this.ActualHeight / 2));

            bool doOptimizedBorders = false;

            // Chunks are ordered by z-index, then by start time.  For each z-index, chunks should be in left-to-right order
            // and not overlap.  Track the maximum drawn X pixel for each z-index pass, so we can draw very small events
            // without drawing a crazy number of rectangles.  See DrawChunk() for the comparisons against maximumDrawnX.
            int                   currentZIndex     = -1;
            ChunkDrawData         previousChunkData = null;
            List <IEventDataNode> forceDrawChunks   = new List <IEventDataNode>();

            foreach (var chunk in this.sortedNodes)
            {
                if (chunk.ZIndex > currentZIndex)
                {
                    currentZIndex = chunk.ZIndex;

                    // Draw the previous chunk data (if any) and the forced-drawn (selected/hot) chunks at the previous level
                    DrawLastDelayedAndForcedDrawChunks(drawingContext, previousChunkData, forceDrawChunks);
                    previousChunkData = null;
                    forceDrawChunks.Clear();

                    // If dataSource.ContiguousEvents is true, then we are guaranteed that the events are
                    // always right next to each other -- but only at level 0.  At higher levels, they may
                    // not be contiguous due to the tree expansion state.  The "doOptimizedBorders" mode is to
                    // fill the entire region with the outline color, then draw the chunks with no borders
                    // (if they are big enough to be visible).
                    doOptimizedBorders = (dataSource.ContiguousEvents && currentZIndex == 0);

                    if (doOptimizedBorders)
                    {
                        // Draw the full bar in the standard pen brush color, as if it were full of 1-px wide chunks.
                        r = new Rect(new Point(0, verticalMargin), new Size(this.ActualWidth, this.ActualHeight - (verticalMargin * 2)));
                        if (currentZIndex > 0)
                        {
                            double delta = chunk.ZIndex * 2;
                            r.Inflate(0, -delta);
                        }
                        drawingContext.DrawRectangle(this.Stroke, null, r);
                    }
                }

                if (chunk.Visible)
                {
                    Brush brush;
                    Pen   pen;

                    if (chunk == hotChunk || this.selectedChunks.Contains(chunk))
                    {
                        // We need to draw these a second time.  If we skip them, it causes different
                        // merging behavior and you get visual artifacts as you select/hover over events.
                        forceDrawChunks.Add(chunk);
                    }

                    pen = doOptimizedBorders ? null : standardPen;

                    if (UseEventColor)
                    {
                        uint nodeColor = chunk.Color;
                        if (!customBrushes.TryGetValue(nodeColor, out brush))
                        {
                            brush = new SolidColorBrush(new EventColor(nodeColor));
                            customBrushes.Add(nodeColor, brush);
                        }
                    }
                    else
                    {
                        brush = this.Fill;
                    }

                    previousChunkData = DrawChunk(drawingContext, chunk, brush, pen, previousChunkData, forceDraw: false);
                }
            }

            DrawLastDelayedAndForcedDrawChunks(drawingContext, previousChunkData, forceDrawChunks);

            double?lastDrawnSlicingMarkerX = null;

            foreach (var marker in this.slicingMarkers)
            {
                lastDrawnSlicingMarkerX = DrawSlicingMarker(drawingContext, marker, lastDrawnSlicingMarkerX);
            }

            List <Rect>   labelRects = new List <Rect>();
            FormattedText ftLabel    = null;

            // Draw selected chunk arrows, and calculate label rect requirements
            foreach (var selectedChunk in this.selectedChunks)
            {
                if (selectedChunk != hotChunk)
                {
                    var    brush        = IsFocused ? this.SelectedFill : this.InactiveSelectedFill;
                    var    middle       = TimeAxis.TimeToScreen(selectedChunk.StartTime + (selectedChunk.Duration / 2));
                    double heightAdjust = 0.5;

                    if (selectedChunk.Style == EventRenderStyle.SlicingMarker)
                    {
                        heightAdjust = verticalMargin - 6;
                    }

                    var geom = selectionArrowGeometry.Clone();
                    geom.Transform = new TranslateTransform(middle, verticalMargin - heightAdjust);
                    drawingContext.DrawGeometry(brush, selectedPen, geom);

                    var x1 = TimeAxis.TimeToScreen(selectedChunk.StartTime);
                    var x2 = TimeAxis.TimeToScreen(selectedChunk.StartTime + selectedChunk.Duration);
                    if (x1 < this.ActualWidth && x2 > 0)
                    {
                        // Draw label, if selected chunk is visible
                        if (ftLabel == null)
                        {
                            // NOTE:  It is assumed that all selected chunks have the same name...
                            ftLabel = new FormattedText(selectedChunk.Name, CultureInfo.CurrentCulture,
                                                        FlowDirection.LeftToRight, labelTypeface, labelFontSize, Brushes.Black);
                        }

                        // Try to center the label under the event.  But adjust its position if it
                        // is clipped by the left or right edge of the timeline.
                        var left = middle - ftLabel.Width / 2;
                        if (left < 0)
                        {
                            left = 0;
                        }
                        else if (left + ftLabel.Width > this.ActualWidth)
                        {
                            left = this.ActualWidth - ftLabel.Width;
                        }

                        // Create a rect (top/height insignificant) for this label and remember it
                        labelRects.Add(new Rect(left, 0, ftLabel.Width, 10));
                    }
                }
            }

            if (ftLabel != null)
            {
                List <Rect> mergedRects = new List <Rect>(labelRects.Count);

                // Merge the overlapping label rects together
                while (labelRects.Count > 0)
                {
                    Rect rect = labelRects[0];

                    labelRects.RemoveAt(0);

                    for (int i = 0; i < labelRects.Count;)
                    {
                        if (rect.IntersectsWith(labelRects[i]))
                        {
                            rect.Union(labelRects[i]);
                            labelRects.RemoveAt(i);
                        }
                        else
                        {
                            i++;
                        }
                    }

                    mergedRects.Add(rect);
                }

                // Draw the text centered in each of the remaining label rects
                foreach (var rect in mergedRects)
                {
                    double centerX = rect.Left + (rect.Width / 2) - (ftLabel.Width / 2);
                    drawingContext.DrawText(ftLabel, new Point(centerX, this.ActualHeight - verticalMargin));
                }
            }

            // Draw hot chunk arrow
            if (hotChunk != null)
            {
                var    middle       = TimeAxis.TimeToScreen(hotChunk.StartTime + (hotChunk.Duration / 2));
                double heightAdjust = 0.5;

                if (hotChunk.Style == EventRenderStyle.SlicingMarker)
                {
                    heightAdjust = verticalMargin - 6;
                }

                hotArrowGeometry.Transform = new TranslateTransform(middle, verticalMargin - heightAdjust);
                drawingContext.DrawGeometry(this.HighlightedFill, this.highlightedPen, hotArrowGeometry);
            }

            drawingContext.Pop(); // pop clip

            UpdateToolTip();
        }
        ChunkDrawData DrawChunk(DrawingContext drawingContext, IEventDataNode chunk, Brush brush, Pen pen, ChunkDrawData previousChunkData, bool forceDraw)
        {
            var x1 = TimeAxis.TimeToScreen(chunk.StartTime);
            var x2 = Math.Max(x1, TimeAxis.TimeToScreen(chunk.StartTime + chunk.Duration));  // Max in case the duration is bogus

            if (x2 < 0 || x1 > this.ActualWidth)
            {
                if (previousChunkData != null)
                {
                    drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect);
                }

                return null;
            }

            var r = new Rect(new Point(x1, verticalMargin), new Size(x2 - x1, this.ActualHeight - (verticalMargin * 2)));
            var halfHeightRect = new Rect(new Point(x1, (this.ActualHeight - halfHeight) / 2), new Size(x2 - x1, halfHeight));
            var style = chunk.Style;

            if (pen == null)
            {
                // Since there is no stroke (pen == null), this is a "normal" chunk with no border
                // (the bar has already been filled w/ border color) so deflate 1/2 pixel to simulate
                // a border stroke.
                r.Inflate(-0.5, -0.5);
            }
            else
            {
                r.Inflate(0, -0.5);
            }

            const int minWidth = 3;

            // Points-in-time (markers) are always 3px wide
            if (style == EventRenderStyle.ParentedMarker)
            {
                r = new Rect(x1 - Math.Floor((double)minWidth / 2), verticalMargin, minWidth, this.ActualHeight - (verticalMargin * 2));
            }

            // Adjust rect height based on chunk nesting depth
            if (chunk.ZIndex > 0 && !r.IsEmpty && style != EventRenderStyle.HalfHeight)
            {
                double delta = chunk.ZIndex * 2;
                r.Inflate(0, -delta);
            }

            if (forceDraw)
            {
                // This is a special (hot or selected) chunk, so enforce a minimum width and always draw it.
                if (style == EventRenderStyle.HalfHeight)
                {
                    r = halfHeightRect;
                }

                var width = r.Width;
                if (width < minWidth)
                {
                    var halfGap = (minWidth - width) / 2;
                    r = new Rect(new Point(r.Left - halfGap, r.Top), new Point(r.Right + halfGap, r.Bottom));
                }
                drawingContext.DrawRectangle(brush, pen, r);
                return null;
            }

            const double minDistance = 16;

            if (style == EventRenderStyle.HalfHeight)
            {
                r = halfHeightRect;
            }

            // Draw the chunk.  We may need to merge it with the previous rect if there is one.
            if (previousChunkData != null)
            {
                // We have a previous rect.  If it's far enough away from this one, then draw it.
                // OR, if this one is big enough BY ITSELF to be drawn, draw the previous one.
                // Otherwise, merge it with this one, and draw the result if it's wide enough.
                if (previousChunkData.Rect.Right < (r.Left - (minDistance / 4)) || (r.Width >= minDistance))
                {
                    drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect);
                }
                else
                {
                    previousChunkData.Rect = Rect.Union(previousChunkData.Rect, r);
                    previousChunkData.Fill = Brushes.Gray;

                    if (previousChunkData.Rect.Width >= minDistance)
                    {
                        drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect);
                        previousChunkData = null;
                    }

                    return previousChunkData;
                }
            }

            if (r.Width >= minDistance)
            {
                drawingContext.DrawRectangle(brush, pen, r);
                previousChunkData = null;
            }
            else
            {
                previousChunkData = new ChunkDrawData { Fill = brush, Rect = r, Pen = pen };
            }

            return previousChunkData;
        }
        void DrawLastDelayedAndForcedDrawChunks(DrawingContext drawingContext, ChunkDrawData drawData, List<IEventDataNode> forceDrawChunks)
        {
            if (drawData != null)
            {
                drawingContext.DrawRectangle(drawData.Fill, drawData.Pen, drawData.Rect);
            }

            foreach (var forcedChunk in forceDrawChunks)
            {
                Brush brush = this.SelectedFill;
                Pen pen = this.selectedPen;

                if (forcedChunk == this.hotChunk)
                {
                    brush = this.HighlightedFill;
                    pen = this.highlightedPen;
                }

                DrawChunk(drawingContext, forcedChunk, brush, pen, null, forceDraw: true);
            }
        }